Skip to content

Commit

Permalink
feat(config): add nocache option for file patterns
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
nmalaguti committed Jun 1, 2015
1 parent b8e1bf1 commit 6ef7e7b
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 17 deletions.
12 changes: 10 additions & 2 deletions docs/config/02-files.md
Expand Up @@ -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
Expand All @@ -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}
],
```

Expand All @@ -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}
],
```

Expand Down
16 changes: 11 additions & 5 deletions lib/config.js
Expand Up @@ -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);
};


Expand All @@ -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);
};


Expand Down
27 changes: 21 additions & 6 deletions lib/file_list.js
Expand Up @@ -32,6 +32,8 @@ var File = function(path, mtime) {

this.mtime = mtime;
this.isUrl = false;

this.doNotCache = false;
};

var Url = function(path) {
Expand Down Expand Up @@ -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;
}
});
Expand Down Expand Up @@ -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();
Expand Down
8 changes: 5 additions & 3 deletions lib/middleware/common.js
Expand Up @@ -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) {
Expand All @@ -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
Expand All @@ -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'));

Expand Down
2 changes: 1 addition & 1 deletion lib/middleware/source_files.js
Expand Up @@ -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();
}
Expand Down
14 changes: 14 additions & 0 deletions test/unit/file_list.spec.coffee
Expand Up @@ -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()
Expand Down
16 changes: 16 additions & 0 deletions test/unit/middleware/source_files.spec.coffee
Expand Up @@ -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'

0 comments on commit 6ef7e7b

Please sign in to comment.