diff --git a/.gitignore b/.gitignore index 7ab24b8..5c4368c 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ logs/ # Directories node_modules/ lib/ +public/ # Configs .babelrc diff --git a/package.json b/package.json index cf9f50b..02302d5 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,8 @@ "setup": "tsc --build && beemo create-config eslint prettier", "build": "beemo typescript --build --reference-workspaces", "clean": "rm -rf packages/*/{lib,*.tsbuildinfo}", - "test": "yarn run build && yarn run jest && yarn run lint", + "test": "yarn run build && yarn run jest && yarn run lint && yarn run test:webpack", + "test:webpack": "NODE_ENV=production webpack --config ./packages/config-webpack/tests/webpack.test.config.js", "lint": "beemo eslint .", "jest": "beemo jest", "prettier": "beemo prettier", @@ -29,7 +30,8 @@ "eslint", "jest", "prettier", - "typescript" + "typescript", + "webpack" ], "settings": { "node": true diff --git a/packages/config-webpack/package.json b/packages/config-webpack/package.json index 2fa54ba..9ddec0c 100644 --- a/packages/config-webpack/package.json +++ b/packages/config-webpack/package.json @@ -33,7 +33,6 @@ "fast-glob": "^3.2.2", "file-loader": "^6.0.0", "html-webpack-plugin": "^4.0.1", - "inline-manifest-webpack-plugin": "^4.0.2", "is-docker": "^2.0.0", "terser-webpack-plugin": "^2.3.5", "url-loader": "^4.0.0", diff --git a/packages/config-webpack/src/index.ts b/packages/config-webpack/src/index.ts index 057f3d9..5ba6afc 100644 --- a/packages/config-webpack/src/index.ts +++ b/packages/config-webpack/src/index.ts @@ -3,7 +3,6 @@ import path from 'path'; import webpack from 'webpack'; import HtmlWebpackPlugin from 'html-webpack-plugin'; -import InlineManifestWebpackPlugin from 'inline-manifest-webpack-plugin'; import TerserPlugin from 'terser-webpack-plugin'; import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer'; import { WebpackConfig } from '@beemo/driver-webpack'; @@ -14,6 +13,7 @@ import { GQL_EXT_PATTERN, TJSX_EXT_PATTERN, } from '@airbnb/nimbus-common'; +import InlineManifestPlugin from './plugins/InlineManifestPlugin'; import { PORT, ROOT, PROD, getESMAliases, getFavIcon, getParallelValue } from './helpers'; export interface WebpackOptions { @@ -22,6 +22,7 @@ export interface WebpackOptions { parallel?: boolean | string | number; port?: string | number; react?: boolean; + root?: string; sourceMaps?: boolean; srcFolder: string; } @@ -32,11 +33,12 @@ export function getConfig({ parallel = true, port = PORT, react = false, + root = ROOT, sourceMaps = false, srcFolder, }: WebpackOptions): WebpackConfig { - const srcPath = path.join(ROOT, srcFolder); - const publicPath = path.join(ROOT, buildFolder); + const srcPath = path.join(root, srcFolder); + const publicPath = path.join(root, buildFolder); const entry = [srcPath]; const plugins = [ new webpack.NamedChunksPlugin(), @@ -65,7 +67,7 @@ export function getConfig({ if (PROD) { plugins.push( // Inline the runtime chunk to enable long-term caching - new InlineManifestWebpackPlugin(), + new InlineManifestPlugin(), ); } else if (react) { plugins.push( @@ -79,6 +81,8 @@ export function getConfig({ bail: PROD, + context: root, + entry: { core: entry, }, diff --git a/packages/config-webpack/src/plugins/InlineManifestPlugin.ts b/packages/config-webpack/src/plugins/InlineManifestPlugin.ts new file mode 100644 index 0000000..074d31c --- /dev/null +++ b/packages/config-webpack/src/plugins/InlineManifestPlugin.ts @@ -0,0 +1,97 @@ +/* eslint-disable no-param-reassign, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access, import/no-extraneous-dependencies */ + +import webpack from 'webpack'; +import HtmlWebpackPlugin from 'html-webpack-plugin'; +import sourceMappingURL from 'source-map-url'; + +function getAssetName(chunks: webpack.compilation.Chunk[], chunkName: string) { + return chunks.filter((chunk) => chunk.name === chunkName)?.[0]?.files[0]; +} + +function inlineWhenMatched( + compilation: webpack.compilation.Compilation, + scripts: HtmlWebpackPlugin.HtmlTagObject[], + manifestAssetName: string, +) { + return scripts.map((script) => { + const isManifestScript = + script.tagName === 'script' && + typeof script.attributes.src === 'string' && + script.attributes.src?.includes(manifestAssetName); + + if (isManifestScript) { + return { + tagName: 'script', + voidTag: true, + attributes: { + type: 'text/javascript', + }, + innerHTML: sourceMappingURL.removeFrom(compilation.assets[manifestAssetName].source()), + }; + } + + return script; + }); +} + +export default class InlineManifestPlugin implements webpack.Plugin { + name: string; + + constructor(name: string = 'runtime') { + this.name = name; + } + + apply(compiler: webpack.Compiler) { + const { name } = this; + + compiler.hooks.emit.tap('InlineManifestWebpackPlugin', (compilation) => { + const assetName = getAssetName(compilation.chunks, name); + + if (assetName) { + delete compilation.assets[assetName]; + } + }); + + compiler.hooks.compilation.tap('InlineManifestWebpackPlugin', (compilation) => { + const hooks = HtmlWebpackPlugin.getHooks(compilation); + + hooks.alterAssetTags.tapAsync('InlineManifestWebpackPlugin', (data, cb) => { + const assetName = getAssetName(compilation.chunks, name); + + if (assetName) { + data.assetTags.scripts = inlineWhenMatched( + compilation, + data.assetTags.scripts, + assetName, + ); + } + + cb(null, data); + }); + + hooks.beforeAssetTagGeneration.tapAsync( + 'InlineManifestWebpackPlugin', + (htmlPluginData, cb) => { + const assetName = getAssetName(compilation.chunks, name); + + // @ts-ignore Option exists + if (assetName && htmlPluginData.plugin.options.inject === false) { + const { assets } = htmlPluginData; + const runtime = ``; + const runtimeIndex = assets.js.indexOf(assets.publicPath + assetName); + + if (runtimeIndex >= 0) { + assets.js.splice(runtimeIndex, 1); + } + + assets.js.push(runtime); + } + + cb(null, htmlPluginData); + }, + ); + }); + } +} diff --git a/packages/config-webpack/tests/src/index.html b/packages/config-webpack/tests/src/index.html new file mode 100644 index 0000000..d40c7db --- /dev/null +++ b/packages/config-webpack/tests/src/index.html @@ -0,0 +1,11 @@ + + +
+