-
-
Notifications
You must be signed in to change notification settings - Fork 375
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds brotli support for modern javascript (#674)
* WIP adding sw as a webpack dependency * adding sw-config * Brotli support for modern javascript * adding brotli plugin to webpack * fixing conditional plugins * lint fixes * fixing error * bug-fix * WIP fix watch mode * fix watch mode * add the capability to build sw from user land * fixing comments * fixing spacing * fixing in place mutation * no multi compilers * Update run-webpack.js * removing unwanted files * Update sw-plugin.js * Update sw-plugin.js * making changes for workbox v4 * fixes for regexp * addressing comments * precacing only index.html * Update run-webpack.js * Update run-webpack.js * fixing kluer dep * fixing package.json * fixing unhashed bundle bug * no json parse * fixing comments
- Loading branch information
Showing
7 changed files
with
1,735 additions
and
1,333 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) { | ||
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') | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
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 { info } = require('../../util'); | ||
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) { | ||
info( | ||
'⚛️ Detected custom sw.js: compiling instead of default Service Worker.' | ||
); | ||
} else { | ||
info('⚛️ 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 = Object.assign( | ||
{}, | ||
childCompiler.options.output, | ||
{ filename: '[name].js' } | ||
); | ||
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.