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'