-
-
Notifications
You must be signed in to change notification settings - Fork 375
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adds brotli support for modern javascript #674
Changes from 33 commits
0507885
c5d5246
5a37b9f
65a507c
02498fe
375c545
9c34521
e9e1cdc
8b84b4d
cc29725
1500698
86f0f78
3d1ac3a
1369f2c
3135b3b
c1fd332
8aec475
8bc9521
dcc8f41
434a813
9ed7049
fb28138
81bb193
dc64998
9a04494
95d3ccb
400fdb2
7f21e24
fdf81b4
37d9ec7
9054e9b
126cd49
c95b6dc
4302de4
8935426
b12603b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
self.__precacheManifest = [].concat(self.__precacheManifest || []); | ||
|
||
/* global workbox */ | ||
/** We are sure brotli is enabled for browsers supporting script type=module | ||
* so we do brotli support only for them. | ||
* We can do brolti support for other browsers but there is no good way of | ||
* feature detect the same at the time of pre-caching. | ||
*/ | ||
if (process.env.ENABLE_BROTLI && process.env.ES_BUILD) { | ||
// Alter the precache manifest to precache brotli files instead of gzip files. | ||
self.__precacheManifest = self.__precacheManifest.map(asset => { | ||
if (/.*.js$/.test(asset.url)) { | ||
asset.url = asset.url.replace(/.esm.js$/, '.esm.js.br'); | ||
} | ||
return asset; | ||
}); | ||
|
||
class BrotliRedirectPlugin { | ||
// Before saving the response in cache, we need to treat the headers. | ||
async cacheWillUpdate({ response }) { | ||
const clonedResponse = response.clone(); | ||
if (/.js.br(\?.*)?$/.test(clonedResponse.url)) { | ||
const headers = new Headers(clonedResponse.headers); | ||
headers.set('content-type', 'application/javascript'); | ||
return new Response(await clonedResponse.text(), { headers }); | ||
} | ||
return response; | ||
} | ||
} | ||
workbox.precaching.addPlugins([new BrotliRedirectPlugin()]); | ||
} | ||
|
||
const precacheOptions = {}; | ||
if (process.env.ENABLE_BROTLI) { | ||
developit marked this conversation as resolved.
Show resolved
Hide resolved
|
||
precacheOptions['urlManipulation'] = ({ url }) => { | ||
if (/.esm.js$/.test(url.href)) { | ||
url.href = url.href + '.br'; | ||
} | ||
return [url]; | ||
}; | ||
} | ||
|
||
workbox.precaching.precacheAndRoute(self.__precacheManifest, precacheOptions); | ||
workbox.routing.registerNavigationRoute( | ||
workbox.precaching.getCacheKeyForURL('/index.html') | ||
); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
const SingleEntryPlugin = require('webpack/lib/SingleEntryPlugin'); | ||
const BabelEsmPlugin = require('babel-esm-plugin'); | ||
const { DefinePlugin } = require('webpack'); | ||
const fs = require('fs'); | ||
const { resolve } = require('path'); | ||
const { blue } = require('chalk'); | ||
|
||
class SWBuilderPlugin { | ||
constructor(config) { | ||
const { src, brotli, esm } = config; | ||
this.brotli_ = brotli; | ||
this.esm_ = esm; | ||
this.src_ = src; | ||
} | ||
apply(compiler) { | ||
let swSrc = resolve(__dirname, '../sw.js'); | ||
const exists = fs.existsSync(resolve(`${this.src_}/sw.js`)); | ||
if (exists) { | ||
if (exists) { | ||
console.log( | ||
blue( | ||
'⚛️ Detected custom sw.js: compiling instead of default Service Worker.' | ||
) | ||
); | ||
} else { | ||
console.log( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same thing here? |
||
blue('⚛️ No custom sw.js detected: compiling default Service Worker.') | ||
); | ||
} | ||
} | ||
compiler.hooks.make.tapAsync( | ||
this.constructor.name, | ||
(compilation, callback) => { | ||
const outputOptions = compiler.options; | ||
const plugins = [ | ||
new BabelEsmPlugin({ | ||
filename: '[name]-esm.js', | ||
excludedPlugins: ['BabelEsmPlugin', this.constructor.name], | ||
beforeStartExecution: plugins => { | ||
plugins.forEach(plugin => { | ||
if (plugin.constructor.name === 'DefinePlugin') { | ||
if (!plugin.definitions) | ||
throw Error( | ||
'ESM Error: DefinePlugin found without definitions.' | ||
); | ||
plugin.definitions['process.env.ES_BUILD'] = true; | ||
} | ||
}); | ||
}, | ||
}), | ||
new DefinePlugin({ | ||
'process.env.ENABLE_BROTLI': this.brotli_, | ||
'process.env.ES_BUILD': false, | ||
'process.env.NODE_ENV': 'production', | ||
}), | ||
]; | ||
|
||
/** | ||
* We are deliberatly not passing plugins in createChildCompiler. | ||
* All webpack does with plugins is to call `apply` method on them | ||
* with the childCompiler. | ||
* But by then we haven't given childCompiler a fileSystem or other options | ||
* which a few plugins might expect while execution the apply method. | ||
* We do call the `apply` method of all plugins by ourselves later in the code | ||
*/ | ||
const childCompiler = compilation.createChildCompiler( | ||
this.constructor.name | ||
); | ||
|
||
childCompiler.context = compiler.context; | ||
childCompiler.options = Object.assign({}, outputOptions); | ||
childCompiler.options.entry = { | ||
sw: swSrc, | ||
}; | ||
childCompiler.options.target = 'webworker'; | ||
childCompiler.options.output = JSON.parse( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @developit @ForsakenHarmony do we have any dep already which can help me do this deepcopy? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nvm fixed it |
||
JSON.stringify(childCompiler.options.output) | ||
); | ||
childCompiler.options.output.filename = '[name].js'; | ||
|
||
// Call the `apply` method of all plugins by ourselves. | ||
if (Array.isArray(plugins)) { | ||
for (const plugin of plugins) { | ||
plugin.apply(childCompiler); | ||
} | ||
} | ||
|
||
childCompiler.apply( | ||
new SingleEntryPlugin(compiler.context, swSrc, 'sw') | ||
); | ||
|
||
compilation.hooks.additionalAssets.tapAsync( | ||
this.constructor.name, | ||
childProcessDone => { | ||
childCompiler.runAsChild((err, entries, childCompilation) => { | ||
if (!err) { | ||
compilation.assets = Object.assign( | ||
childCompilation.assets, | ||
compilation.assets | ||
); | ||
} | ||
err && compilation.errors.push(err); | ||
childProcessDone(); | ||
}); | ||
} | ||
); | ||
callback(); | ||
} | ||
); | ||
} | ||
} | ||
|
||
module.exports = SWBuilderPlugin; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
guess this should use the util log fn