From 6ef7e7b1546ff3e80a0369ea69c33625858a1e7a Mon Sep 17 00:00:00 2001 From: Nick Malaguti Date: Mon, 20 Apr 2015 19:17:07 -0400 Subject: [PATCH] feat(config): add nocache option for file patterns Add nocache property for file patterns in the config file. This allows files that should be served by the webserver but that don't need to be preprocessed but that should to be served fresh (e.g. map files, pre-transpiled source built by other build process) without a large amount of watches. --- docs/config/02-files.md | 12 +++++++-- lib/config.js | 16 +++++++---- lib/file_list.js | 27 ++++++++++++++----- lib/middleware/common.js | 8 +++--- lib/middleware/source_files.js | 2 +- test/unit/file_list.spec.coffee | 14 ++++++++++ test/unit/middleware/source_files.spec.coffee | 16 +++++++++++ 7 files changed, 78 insertions(+), 17 deletions(-) diff --git a/docs/config/02-files.md b/docs/config/02-files.md index 81712c4ab..9b1021da2 100644 --- a/docs/config/02-files.md +++ b/docs/config/02-files.md @@ -41,6 +41,11 @@ Each pattern is either a simple string or an object with four properties: * **Default.** `true` * **Description.** Should the files be served by Karma's webserver? +### `nocache` +* **Type.** Boolean +* **Default.** `false` +* **Description.** Should the files be served from disk on each request by Karma's webserver? + ## Preprocessor transformations Depending on preprocessor configuration, be aware that files loaded may be transformed and no longer available in @@ -62,7 +67,10 @@ files: [ {pattern: 'compiled/index.html', watched: false}, // this file only gets watched and is otherwise ignored - {pattern: 'app/index.html', included: false, served: false} + {pattern: 'app/index.html', included: false, served: false}, + + // this file will be served on demand from disk and will be ignored by the watcher + {pattern: 'compiled/app.js.map', included: false, served: true, watched: false, nocache: true} ], ``` @@ -73,7 +81,7 @@ Example for loading images ```javascript files: [ - {pattern: "test/images/*.jpg", watched: false, included: false, served: true} + {pattern: 'test/images/*.jpg', watched: false, included: false, served: true, nocache: false} ], ``` diff --git a/lib/config.js b/lib/config.js index 02cd8ec36..5f9302dfa 100644 --- a/lib/config.js +++ b/lib/config.js @@ -22,15 +22,16 @@ try { LIVE_SCRIPT_AVAILABLE = true; } catch (e) {} -var Pattern = function(pattern, served, included, watched) { +var Pattern = function(pattern, served, included, watched, nocache) { this.pattern = pattern; this.served = helper.isDefined(served) ? served : true; this.included = helper.isDefined(included) ? included : true; this.watched = helper.isDefined(watched) ? watched : true; + this.nocache = helper.isDefined(nocache) ? nocache : false; }; var UrlPattern = function(url) { - Pattern.call(this, url, false, true, false); + Pattern.call(this, url, false, true, false, false); }; @@ -43,15 +44,20 @@ var createPatternObject = function(pattern) { if (pattern.pattern && helper.isString(pattern.pattern)) { return helper.isUrlAbsolute(pattern.pattern) ? new UrlPattern(pattern.pattern) : - new Pattern(pattern.pattern, pattern.served, pattern.included, pattern.watched); + new Pattern( + pattern.pattern, + pattern.served, + pattern.included, + pattern.watched, + pattern.nocache); } log.warn('Invalid pattern %s!\n\tObject is missing "pattern" property.', pattern); - return new Pattern(null, false, false, false); + return new Pattern(null, false, false, false, false); } log.warn('Invalid pattern %s!\n\tExpected string or object with "pattern" property.', pattern); - return new Pattern(null, false, false, false); + return new Pattern(null, false, false, false, false); }; diff --git a/lib/file_list.js b/lib/file_list.js index 9b33ddb56..9c10260be 100644 --- a/lib/file_list.js +++ b/lib/file_list.js @@ -32,6 +32,8 @@ var File = function(path, mtime) { this.mtime = mtime; this.isUrl = false; + + this.doNotCache = false; }; var Url = function(path) { @@ -102,6 +104,10 @@ var List = function(patterns, excludes, emitter, preprocess, batchInterval) { files.included.push(file); } + if (patterns[idx].nocache) { + file.doNotCache = true; + } + uniqueMap[file.path] = true; } }); @@ -216,15 +222,24 @@ var List = function(patterns, excludes, emitter, preprocess, batchInterval) { // TODO(vojta): reuse file objects var file = new File(path, stat.mtime); - preprocess(file, function(err) { - buckets[i].push(file); + if (patternObject.nocache) { + log.debug('Not preprocessing "%s" due to nocache', path); - if (err) { - addError(path); - } + file.doNotCache = true; + buckets[i].push(file); finish(); - }); + } else { + preprocess(file, function(err) { + buckets[i].push(file); + + if (err) { + addError(path); + } + + finish(); + }); + } } else { log.debug('Ignored directory "%s"', path); finish(); diff --git a/lib/middleware/common.js b/lib/middleware/common.js index 979efafee..6f0c40952 100644 --- a/lib/middleware/common.js +++ b/lib/middleware/common.js @@ -28,7 +28,7 @@ var serve404 = function(response, path) { var createServeFile = function(fs, directory) { var cache = Object.create(null); - return function(filepath, response, transform, content) { + return function(filepath, response, transform, content, doNotCache) { var responseData; if (directory) { @@ -40,7 +40,7 @@ var createServeFile = function(fs, directory) { } // serve from cache - if (content) { + if (content && !doNotCache) { response.setHeader('Content-Type', mime.lookup(filepath, 'text/plain')); // call custom transform fn to transform the data @@ -57,7 +57,9 @@ var createServeFile = function(fs, directory) { return serve404(response, filepath); } - cache[filepath] = data.toString(); + if (!doNotCache) { + cache[filepath] = data.toString(); + } response.setHeader('Content-Type', mime.lookup(filepath, 'text/plain')); diff --git a/lib/middleware/source_files.js b/lib/middleware/source_files.js index e20f4d16f..7ef6e1c4f 100644 --- a/lib/middleware/source_files.js +++ b/lib/middleware/source_files.js @@ -46,7 +46,7 @@ var createSourceFilesMiddleware = function(filesPromise, serveFile, // without timestamps - no cache (debug) common.setNoCacheHeaders(response); } - }, file.content); + }, file.content, file.doNotCache); } else { next(); } diff --git a/test/unit/file_list.spec.coffee b/test/unit/file_list.spec.coffee index 00ee4097c..10992455a 100644 --- a/test/unit/file_list.spec.coffee +++ b/test/unit/file_list.spec.coffee @@ -241,6 +241,20 @@ describe 'file_list', -> expect(pathsFrom files.served).to.deep.equal ['/a.txt'] done() + it 'should properly mark files that should not be cached', (done) -> + # /a.* => /a.txt [nocache FALSE] + # /some/*.js => /some/a.js, /some/b.js [nocache TRUE] + files = [new config.Pattern('/a.*'), new config.Pattern('/some/*.js', true, true, true, true)] + list = new m.List files, [], emitter, preprocessMock + + refreshListAndThen (files) -> + expect(pathsFrom files.served).to.deep.equal ['/a.txt', '/some/a.js', '/some/b.js'] + expect(preprocessMock.callCount).to.equal 1 + expect(files.served[0].doNotCache).to.be.false + expect(files.served[1].doNotCache).to.be.true + expect(files.served[2].doNotCache).to.be.true + done() + #============================================================================ # List.getIncludedFiles() diff --git a/test/unit/middleware/source_files.spec.coffee b/test/unit/middleware/source_files.spec.coffee index fdb40b33c..455eb3b64 100644 --- a/test/unit/middleware/source_files.spec.coffee +++ b/test/unit/middleware/source_files.spec.coffee @@ -174,3 +174,19 @@ describe 'middleware.source_files', -> done() callHandlerWith '/absolute/some/file.js' + + it 'should not use cached content if doNotCache is set', (done) -> + cachedFile = new File('/src/some.js') + cachedFile.content = 'cached-content' + cachedFile.doNotCache = true + + servedFiles [ + cachedFile + ] + + response.once 'end', -> + expect(nextSpy).not.to.have.been.called + expect(response).to.beServedAs 200, 'js-source' + done() + + callHandlerWith '/absolute/src/some.js'