Skip to content

Commit

Permalink
feat(server): Add support for encoded source files (#3123)
Browse files Browse the repository at this point in the history
  • Loading branch information
GreenGremlin authored and johnjbarton committed Sep 6, 2018
1 parent c91cb81 commit 68b37d3
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 1 deletion.
11 changes: 11 additions & 0 deletions docs/dev/05-plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,17 @@ Karma can be extended through plugins. A plugin is essentially an NPM module. Ty
- use NPM keywords `karma-plugin`, `karma-launcher`

## Preprocessors

A preprocessor is a function that accepts three arguments (`content`, `file`, and `next`), mutates the content in some way, and passes it on to the next preprocessor.

- arguments passed to preprocessor plugins:
- **`content`** of the file being processed
- **`file`** object describing the file being processed
- **path:** the current file, mutable file path. e. g. `some/file.coffee` -> `some/file.coffee.js` _This path is mutable and may not actually exist._
- **originalPath:** the original, unmutated path
- **encodings:** A mutable, keyed object where the keys are a valid encoding type ('gzip', 'compress', 'br', etc.) and the values are the encoded content. Encoded content should be stored here and not resolved using `next(null, encodedContent)`
- **type:** the pattern used to match the file
- **`next`** function to be called when preprocessing is complete, should be called as `next(null, processedContent)` or `next(error)`
- example plugins: [karma-coffee-preprocessor], [karma-ng-html2js-preprocessor]
- use naming convention is `karma-*-preprocessor`
- user NPM keywords `karma-plugin`, `karma-preprocessor`
Expand Down
4 changes: 4 additions & 0 deletions lib/file.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ class File {
// where the content is stored (processed)
this.contentPath = path

// encodings format {[encodingType]: encodedContent}
// example: {gzip: <Buffer 1f 8b 08...>}
this.encodings = Object.create(null)

this.mtime = mtime
this.isUrl = false

Expand Down
11 changes: 10 additions & 1 deletion lib/middleware/source_files.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,22 @@ function createSourceFilesMiddleware (filesPromise, serveFile, basePath, urlRoot
const rangeHeader = request.headers['range']

if (file) {
const acceptEncodingHeader = request.headers['accept-encoding']
const matchedEncoding = Object.keys(file.encodings).find(
(encoding) => new RegExp(`(^|.*, ?)${encoding}(,|$)`).test(acceptEncodingHeader)
)
const content = file.encodings[matchedEncoding] || file.content

serveFile(file.contentPath || file.path, rangeHeader, response, function () {
if (/\?\w+/.test(request.url)) {
common.setHeavyCacheHeaders(response) // files with timestamps - cache one year, rely on timestamps
} else {
common.setNoCacheHeaders(response) // without timestamps - no cache (debug)
}
}, file.content, file.doNotCache)
if (matchedEncoding) {
response.setHeader('Content-Encoding', matchedEncoding)
}
}, content, file.doNotCache)
} else {
next()
}
Expand Down
36 changes: 36 additions & 0 deletions test/unit/middleware/source_files.spec.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
var http = require('http')
var mocks = require('mocks')
var request = require('supertest')
var zlib = require('zlib')

var helper = require('../../../lib/helper')
var File = require('../../../lib/file')
Expand Down Expand Up @@ -109,6 +110,41 @@ describe('middleware.source_files', function () {
})
})

describe('file encoding', function () {
let file
beforeEach(function () {
file = new File('/src/some.js')
servedFiles([
file
])
})

it('serves encoded files', function () {
file.encodings.gzip = zlib.gzipSync('gzipped-js-source')
return request(server)
.get('/absolute/src/some.js')
.set('Accept-Encoding', 'gzip, deflate')
.expect(200, 'gzipped-js-source')
.expect('Content-Encoding', 'gzip')
.expect('Content-Type', 'application/javascript')
})

it('serves unencoded files when request does not accept available encodings', function (done) {
file.encodings.gzip = zlib.gzipSync('gzipped-js-source')
request(server)
.get('/absolute/src/some.js')
.set('Accept-Encoding', 'gzippy, deflate')
.expect(200, 'js-source')
.end((error, res) => {
if (error) {
return done(error)
}
expect(res.headers).to.not.have.property('content-encoding')
return done()
})
})
})

it('should serve absolute js source files ignoring timestamp', function () {
servedFiles([
new File('/src/some.js')
Expand Down

0 comments on commit 68b37d3

Please sign in to comment.