From 4d638746485007332666b2ed96e078193c2bc9ea Mon Sep 17 00:00:00 2001 From: Zak Burke Date: Wed, 4 Oct 2023 10:48:33 -0400 Subject: [PATCH 1/5] FOLIO-3627 bundle stripes-core's service-worker.js Include `service-worker.js`, pulled from `@folio/stripes-core/src/service-worker.js`, in the output bundle so it can be registered via `navigator.serviceWorker.register(...)`. Refs FOLIO-3627 --- webpack.config.base.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/webpack.config.base.js b/webpack.config.base.js index 5289c0e..0099205 100644 --- a/webpack.config.base.js +++ b/webpack.config.base.js @@ -50,6 +50,10 @@ const baseConfig = { dependOn: 'stripesConfig', import: '@folio/stripes-ui' }, + 'service-worker': { + import: '@folio/stripes-core/src/service-worker.js', + filename: 'service-worker.js', + } }, resolve: { alias: { From 03b7a1c9e1c191091ce5f704a1a722613f12a230 Mon Sep 17 00:00:00 2001 From: Zak Burke Date: Sat, 7 Oct 2023 21:14:19 -0400 Subject: [PATCH 2/5] clean up * use a dynamic output file name, i.e. allow the main bundle and the service-worker to use separate names. * ensure that only a single webpack runtime is in effect, which is necessary when hot-reloading is enabled. TBH, I don't entirely understand how this setting works, but the creator of webpack recommended it in a conversation with the creators of react itself and the hot-reload plugin, so I'm gonna stick with it. After adding the service worker, hot reload would fail without this setting in place. The change would be detected, but the app would bomb. --- webpack.config.cli.dev.js | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/webpack.config.cli.dev.js b/webpack.config.cli.dev.js index 804ea33..900b714 100644 --- a/webpack.config.cli.dev.js +++ b/webpack.config.cli.dev.js @@ -36,12 +36,34 @@ const buildConfig = (stripesConfig) => { }); // Override filename to remove the hash in development due to memory issues (STCOR-296) - devConfig.output.filename = 'bundle.js'; - devConfig.entry = [ - 'webpack-hot-middleware/client', - ...devConfig.entry.css, - '@folio/stripes-ui', - ]; + devConfig.output.filename = '[name].js'; + devConfig.entry = + { + css: devConfig.entry.css, + main: [ + 'webpack-hot-middleware/client', + '@folio/stripes-ui', + ], + 'service-worker': { + import: '@folio/stripes-core/src/service-worker.js', + filename: 'service-worker.js', + } + }; + + // in dev-mode when react-refresh-webpack-plugin (hot reload) is in play + // and there are multiple entry points on a single page (as there are now + // that we have a service-worker), we need to make sure there is only one + // runtime instance so that modules are only instantiated once. + // + // I don't entirely understand what that ^^^^ means, but it's the outcome + // of a conversation among the creators of react, pmmmwh, and webpack, + // so I ain't gonna argue. + // + // thanks SO: https://stackoverflow.com/questions/65640449/how-to-solve-chunkloaderror-loading-hot-update-chunk-second-app-failed-in-webpa + // and sokra: https://github.com/pmmmwh/react-refresh-webpack-plugin/issues/88#issuecomment-627558799 + devConfig.optimization = { + runtimeChunk: 'single' + }; devConfig.plugins = devConfig.plugins.concat([ new webpack.ProvidePlugin({ From 9b3668a2ff8ab2d1fa37a4e04c8d737c5af335bf Mon Sep 17 00:00:00 2001 From: Zak Burke Date: Sat, 7 Oct 2023 23:25:07 -0400 Subject: [PATCH 3/5] avoid optimzation.runtimeChunk: single This _does_ fix the crash-on-reload, which is SUPER nice, but it prevents the service-worker from running, which is a non-starter. Back to the drawing board. --- webpack.config.cli.dev.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/webpack.config.cli.dev.js b/webpack.config.cli.dev.js index 900b714..f1a8f3e 100644 --- a/webpack.config.cli.dev.js +++ b/webpack.config.cli.dev.js @@ -61,9 +61,9 @@ const buildConfig = (stripesConfig) => { // // thanks SO: https://stackoverflow.com/questions/65640449/how-to-solve-chunkloaderror-loading-hot-update-chunk-second-app-failed-in-webpa // and sokra: https://github.com/pmmmwh/react-refresh-webpack-plugin/issues/88#issuecomment-627558799 - devConfig.optimization = { - runtimeChunk: 'single' - }; + // devConfig.optimization = { + // runtimeChunk: 'single' + // }; devConfig.plugins = devConfig.plugins.concat([ new webpack.ProvidePlugin({ From c9b4d6cd5dc9945930bbae80643cdb13ada92fa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Kuklis?= Date: Wed, 18 Oct 2023 13:20:45 -0400 Subject: [PATCH 4/5] Add separated config for service-worker (#129) * Add separate config for service-worker --- webpack.config.cli.dev.js | 20 +++++++------------- webpack.config.service.worker.js | 31 +++++++++++++++++++++++++++++++ webpack/serve.js | 26 +++++++++++++++++--------- 3 files changed, 55 insertions(+), 22 deletions(-) create mode 100644 webpack.config.service.worker.js diff --git a/webpack.config.cli.dev.js b/webpack.config.cli.dev.js index f1a8f3e..d390941 100644 --- a/webpack.config.cli.dev.js +++ b/webpack.config.cli.dev.js @@ -22,6 +22,7 @@ const buildConfig = (stripesConfig) => { const base = buildBaseConfig(allModulePaths); const devConfig = Object.assign({}, base, cli, { + name: 'development', devtool: 'inline-source-map', mode: 'development', cache: { @@ -36,19 +37,12 @@ const buildConfig = (stripesConfig) => { }); // Override filename to remove the hash in development due to memory issues (STCOR-296) - devConfig.output.filename = '[name].js'; - devConfig.entry = - { - css: devConfig.entry.css, - main: [ - 'webpack-hot-middleware/client', - '@folio/stripes-ui', - ], - 'service-worker': { - import: '@folio/stripes-core/src/service-worker.js', - filename: 'service-worker.js', - } - }; + devConfig.output.filename = 'bundle.js'; + devConfig.entry = [ + 'webpack-hot-middleware/client', + ...devConfig.entry.css, + '@folio/stripes-ui', + ]; // in dev-mode when react-refresh-webpack-plugin (hot reload) is in play // and there are multiple entry points on a single page (as there are now diff --git a/webpack.config.service.worker.js b/webpack.config.service.worker.js new file mode 100644 index 0000000..9199e10 --- /dev/null +++ b/webpack.config.service.worker.js @@ -0,0 +1,31 @@ +const path = require('path'); + +const config = { + name: 'service-worker', + mode: 'development', + target: 'web', + entry: { + 'service-worker': { + import: '@folio/stripes-core/src/service-worker.js', + filename: 'service-worker.js', + } + }, + output: { + path: path.join(__dirname, 'dist'), + filename: 'service-worker.js', + publicPath: '/', + }, + module: { + rules: [ + { + test: /\.js$/, + loader: 'esbuild-loader', + options: { + target: 'es2015' + } + }, + ] + } +}; + +module.exports = config; diff --git a/webpack/serve.js b/webpack/serve.js index f3d60d6..0344cb9 100644 --- a/webpack/serve.js +++ b/webpack/serve.js @@ -9,6 +9,7 @@ const applyWebpackOverrides = require('./apply-webpack-overrides'); const logger = require('./logger')(); const buildConfig = require('../webpack.config.cli.dev'); const sharedStylesConfig = require('../webpack.config.cli.shared.styles'); +const serviceWorkerConfig = require('../webpack.config.service.worker'); const cwd = path.resolve(); const platformModulePath = path.join(cwd, 'node_modules'); @@ -39,8 +40,10 @@ module.exports = function serve(stripesConfig, options) { config = applyWebpackOverrides(options.webpackOverrides, config); logger.log('assign final webpack config', config); - const compiler = webpack(config); - compiler.hooks.done.tap('StripesCoreServe', stats => resolve(stats)); + const compiler = webpack([serviceWorkerConfig, config]); + const [swCompiler, stripesCompiler] = compiler.compilers; + + stripesCompiler.hooks.done.tap('StripesCoreServe', stats => resolve(stats)); const port = options.port || process.env.STRIPES_PORT || 3000; const host = options.host || process.env.STRIPES_HOST || 'localhost'; @@ -49,6 +52,14 @@ module.exports = function serve(stripesConfig, options) { app.use(staticFileMiddleware); + // To handle rewrites without the dot rule, we should include the static middleware twice + // https://github.com/bripkens/connect-history-api-fallback/blob/master/examples/static-files-and-index-rewrite + app.use(staticFileMiddleware); + + app.use(webpackDevMiddleware(swCompiler, { + publicPath: serviceWorkerConfig.output.publicPath, + })); + // Process index rewrite before webpack-dev-middleware // to respond with webpack's dist copy of index.html app.use(connectHistoryApiFallback({ @@ -56,16 +67,13 @@ module.exports = function serve(stripesConfig, options) { htmlAcceptHeaders: ['text/html', 'application/xhtml+xml'], })); - // To handle rewrites without the dot rule, we should include the static middleware twice - // https://github.com/bripkens/connect-history-api-fallback/blob/master/examples/static-files-and-index-rewrite - app.use(staticFileMiddleware); - - app.use(webpackDevMiddleware(compiler, { - stats: 'errors-only', + app.use(webpackDevMiddleware(stripesCompiler, { publicPath: config.output.publicPath, })); - app.use(webpackHotMiddleware(compiler)); + + + app.use(webpackHotMiddleware(stripesCompiler)); app.listen(port, host, (err) => { if (err) { From 1fc3257a766bed147e480d97d373179e29b0e91c Mon Sep 17 00:00:00 2001 From: Zak Burke Date: Wed, 18 Oct 2023 13:28:03 -0400 Subject: [PATCH 5/5] handle production build via multi-compile --- webpack.config.base.js | 4 ---- webpack.config.cli.dev.js | 15 --------------- webpack.config.cli.js | 1 - webpack/build.js | 18 +++++++++++++++--- 4 files changed, 15 insertions(+), 23 deletions(-) diff --git a/webpack.config.base.js b/webpack.config.base.js index 0099205..5289c0e 100644 --- a/webpack.config.base.js +++ b/webpack.config.base.js @@ -50,10 +50,6 @@ const baseConfig = { dependOn: 'stripesConfig', import: '@folio/stripes-ui' }, - 'service-worker': { - import: '@folio/stripes-core/src/service-worker.js', - filename: 'service-worker.js', - } }, resolve: { alias: { diff --git a/webpack.config.cli.dev.js b/webpack.config.cli.dev.js index d390941..baed059 100644 --- a/webpack.config.cli.dev.js +++ b/webpack.config.cli.dev.js @@ -44,21 +44,6 @@ const buildConfig = (stripesConfig) => { '@folio/stripes-ui', ]; - // in dev-mode when react-refresh-webpack-plugin (hot reload) is in play - // and there are multiple entry points on a single page (as there are now - // that we have a service-worker), we need to make sure there is only one - // runtime instance so that modules are only instantiated once. - // - // I don't entirely understand what that ^^^^ means, but it's the outcome - // of a conversation among the creators of react, pmmmwh, and webpack, - // so I ain't gonna argue. - // - // thanks SO: https://stackoverflow.com/questions/65640449/how-to-solve-chunkloaderror-loading-hot-update-chunk-second-app-failed-in-webpa - // and sokra: https://github.com/pmmmwh/react-refresh-webpack-plugin/issues/88#issuecomment-627558799 - // devConfig.optimization = { - // runtimeChunk: 'single' - // }; - devConfig.plugins = devConfig.plugins.concat([ new webpack.ProvidePlugin({ process: 'process/browser.js', diff --git a/webpack.config.cli.js b/webpack.config.cli.js index b2f6f2e..858aac9 100644 --- a/webpack.config.cli.js +++ b/webpack.config.cli.js @@ -9,6 +9,5 @@ module.exports = { filename: 'bundle.[name][contenthash].js', chunkFilename: 'chunk.[name][chunkhash].js', publicPath: '/', - clean: true }, }; diff --git a/webpack/build.js b/webpack/build.js index ac5cb8c..df74ef1 100644 --- a/webpack/build.js +++ b/webpack/build.js @@ -6,7 +6,7 @@ const applyWebpackOverrides = require('./apply-webpack-overrides'); const logger = require('./logger')(); const buildConfig = require('../webpack.config.cli.prod'); const sharedStylesConfig = require('../webpack.config.cli.shared.styles'); - +const serviceWorkerConfig = require('../webpack.config.service.worker'); const platformModulePath = path.join(path.resolve(), 'node_modules'); module.exports = function build(stripesConfig, options) { @@ -73,9 +73,21 @@ module.exports = function build(stripesConfig, options) { config = applyWebpackOverrides(options.webpackOverrides, config); logger.log('assign final webpack config', config); - const compiler = webpack(config); - compiler.run((err, stats) => { + + // repoint the service-worker's output.path value so it emits + // into options.outputPath + if (options.outputPath) { + serviceWorkerConfig.output.path = path.resolve(options.outputPath); + } + // override the default mode; given we are building, assume production + serviceWorkerConfig.mode = 'production'; + + webpack([config,serviceWorkerConfig], (err, stats) => { if (err) { + console.error(err.stack || err); + if (err.details) { + console.error(err.details); + } reject(err); } else { resolve(stats);