From 46b204e158678714ae80ae1b39fab3af588c579e Mon Sep 17 00:00:00 2001 From: Dylan Piercey Date: Fri, 21 Feb 2020 21:06:20 -0800 Subject: [PATCH] feat: dynamic builds now use $global.buildName BREAKING CHANGE: The getClientCompilerName API has been removed. --- README.md | 24 ++++--- .../__snapshots__/server--main.js | 11 +-- .../__snapshots__/server--main.js | 16 ++--- .../__snapshots__/server--main.js | 20 +++--- .../server.js | 2 +- .../webpack.config.ts | 6 +- .../__snapshots__/server--main.js | 11 +-- src/loader/get-asset-code.ts | 6 +- src/plugin/index.ts | 70 ++++++++----------- 9 files changed, 80 insertions(+), 86 deletions(-) diff --git a/README.md b/README.md index a67ecb7..4404808 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,8 @@ export default [ Sometimes you need to have multiple compilers for your client side bundles. For example with [`i18n`](https://github.com/webpack/webpack/tree/master/examples/i18n) or [even shipping dynamic runtime bundles to the browser](https://github.com/eBay/arc/tree/master/packages/arc-webpack). -The Marko webpack plugin allows you to pass a function which is inlined into the server bundle and can respond with the name of the compiler whose assets should be sent to the browser. +The Marko webpack browser plugin can be passed to multiple webpack compilers. At runtime you can provide a `$global.buildName` when rendering which will cause assets from the webpack compiler with that name to be included in the page. + For example with the webpack i18n plugin you might have a config like the following: ```js @@ -114,13 +115,7 @@ const languages = { de: require("./de.json") }; -const markoPlugin = new MarkoPlugin({ - // $global here is the `out.global` from Marko. - getClientCompilerName($global) { - // You must return the name of one of the browser compilers below. - return `Browser-${$global.language}`; - } -}); +const markoPlugin = new MarkoPlugin(); export default [ { @@ -154,13 +149,20 @@ export default [ ]; ``` -With the above config you can render your top level Marko template server side with a `language` global, like so: +With the above config you can render your top level Marko template server side with a `$global.bundleName`, like so: -``` -template.render({ $global: { language: "de" } }); +```javascript +template.render({ $global: { bundleName: "Browser-de" } }); ``` This will automatically send assets for the German language. +Of course in this case you'll want to conditionally send the appropriate assets given a users locale. This can be some simply, like so: + +```javascript +template.render({ $global: { bundleName: `Browser-${req.language}` } }); +``` + +Note: If a bundle with the provided name does not exist an error will be thrown. ## Dynamic public paths diff --git a/src/__tests__/fixtures/basic-template-plugin/__snapshots__/server--main.js b/src/__tests__/fixtures/basic-template-plugin/__snapshots__/server--main.js index c29bfbc..9a661cd 100644 --- a/src/__tests__/fixtures/basic-template-plugin/__snapshots__/server--main.js +++ b/src/__tests__/fixtures/basic-template-plugin/__snapshots__/server--main.js @@ -8,8 +8,10 @@ /***/ (function(module, exports) { module.exports = { - getBundleName: function(){return "browser"}, - entries: {"test_uYWJ":{"browser":{"js":["test_uYWJ.js"]}}} + getAssets(entry) { + return this.build[entry]; + }, + build: {"test_uYWJ":{"js":["test_uYWJ.js"]}} } /***/ }), @@ -84,8 +86,7 @@ var marko_template = module.exports = __webpack_require__(/*! marko/dist/html */ template = __webpack_require__(/*! ./test.marko */ "./src/__tests__/fixtures/basic-template-plugin/test.marko"), module_MARKOWEBPACKMANIFEST_module = __webpack_require__(/*! ./../../../../__MARKO_WEBPACK__MANIFEST.js */ "./__MARKO_WEBPACK__MANIFEST.js"), MARKOWEBPACKMANIFEST_module = module_MARKOWEBPACKMANIFEST_module.default || module_MARKOWEBPACKMANIFEST_module, - getBundleName = module_MARKOWEBPACKMANIFEST_module.getBundleName, - entries = module_MARKOWEBPACKMANIFEST_module.entries, + getAssets = module_MARKOWEBPACKMANIFEST_module.getAssets, marko_dynamicTag = __webpack_require__(/*! marko/dist/runtime/helpers/dynamic-tag */ "marko/dist/runtime/helpers/dynamic-tag"), marko_loadTag = __webpack_require__(/*! marko/dist/runtime/helpers/load-tag */ "marko/dist/runtime/helpers/load-tag"), init_components_tag = marko_loadTag(__webpack_require__(/*! marko/dist/core-tags/components/init-components-tag */ "marko/dist/core-tags/components/init-components-tag")); @@ -138,7 +139,7 @@ function render(input, out, __component, component, state) { out.___renderAssets = renderAssets; - out.___assets = entries["test_uYWJ"][getBundleName(out.global)]; + out.___assets = getAssets("test_uYWJ", out.global.buildName); out.flush = outFlushOverride; diff --git a/src/__tests__/fixtures/multiple-entries-plugin/__snapshots__/server--main.js b/src/__tests__/fixtures/multiple-entries-plugin/__snapshots__/server--main.js index 4df3fb2..e6a5fe8 100644 --- a/src/__tests__/fixtures/multiple-entries-plugin/__snapshots__/server--main.js +++ b/src/__tests__/fixtures/multiple-entries-plugin/__snapshots__/server--main.js @@ -8,8 +8,10 @@ /***/ (function(module, exports) { module.exports = { - getBundleName: function(){return "browser"}, - entries: {"bar_aSxt":{"browser":{"js":["bar_aSxt~foo_3XPO.js","bar_aSxt.js"]}},"foo_3XPO":{"browser":{"js":["bar_aSxt~foo_3XPO.js","foo_3XPO.js"]}}} + getAssets(entry) { + return this.build[entry]; + }, + build: {"bar_aSxt":{"js":["bar_aSxt~foo_3XPO.js","bar_aSxt.js"]},"foo_3XPO":{"js":["bar_aSxt~foo_3XPO.js","foo_3XPO.js"]}} } /***/ }), @@ -73,8 +75,7 @@ var marko_template = module.exports = __webpack_require__(/*! marko/dist/html */ template = __webpack_require__(/*! ./bar.marko */ "./src/__tests__/fixtures/multiple-entries-plugin/bar.marko"), module_MARKOWEBPACKMANIFEST_module = __webpack_require__(/*! ./../../../../__MARKO_WEBPACK__MANIFEST.js */ "./__MARKO_WEBPACK__MANIFEST.js"), MARKOWEBPACKMANIFEST_module = module_MARKOWEBPACKMANIFEST_module.default || module_MARKOWEBPACKMANIFEST_module, - getBundleName = module_MARKOWEBPACKMANIFEST_module.getBundleName, - entries = module_MARKOWEBPACKMANIFEST_module.entries, + getAssets = module_MARKOWEBPACKMANIFEST_module.getAssets, marko_dynamicTag = __webpack_require__(/*! marko/dist/runtime/helpers/dynamic-tag */ "marko/dist/runtime/helpers/dynamic-tag"), marko_loadTag = __webpack_require__(/*! marko/dist/runtime/helpers/load-tag */ "marko/dist/runtime/helpers/load-tag"), init_components_tag = marko_loadTag(__webpack_require__(/*! marko/dist/core-tags/components/init-components-tag */ "marko/dist/core-tags/components/init-components-tag")); @@ -127,7 +128,7 @@ function render(input, out, __component, component, state) { out.___renderAssets = renderAssets; - out.___assets = entries["bar_aSxt"][getBundleName(out.global)]; + out.___assets = getAssets("bar_aSxt", out.global.buildName); out.flush = outFlushOverride; @@ -250,8 +251,7 @@ var marko_template = module.exports = __webpack_require__(/*! marko/dist/html */ template = __webpack_require__(/*! ./foo.marko */ "./src/__tests__/fixtures/multiple-entries-plugin/foo.marko"), module_MARKOWEBPACKMANIFEST_module = __webpack_require__(/*! ./../../../../__MARKO_WEBPACK__MANIFEST.js */ "./__MARKO_WEBPACK__MANIFEST.js"), MARKOWEBPACKMANIFEST_module = module_MARKOWEBPACKMANIFEST_module.default || module_MARKOWEBPACKMANIFEST_module, - getBundleName = module_MARKOWEBPACKMANIFEST_module.getBundleName, - entries = module_MARKOWEBPACKMANIFEST_module.entries, + getAssets = module_MARKOWEBPACKMANIFEST_module.getAssets, marko_dynamicTag = __webpack_require__(/*! marko/dist/runtime/helpers/dynamic-tag */ "marko/dist/runtime/helpers/dynamic-tag"), marko_loadTag = __webpack_require__(/*! marko/dist/runtime/helpers/load-tag */ "marko/dist/runtime/helpers/load-tag"), init_components_tag = marko_loadTag(__webpack_require__(/*! marko/dist/core-tags/components/init-components-tag */ "marko/dist/core-tags/components/init-components-tag")); @@ -304,7 +304,7 @@ function render(input, out, __component, component, state) { out.___renderAssets = renderAssets; - out.___assets = entries["foo_3XPO"][getBundleName(out.global)]; + out.___assets = getAssets("foo_3XPO", out.global.buildName); out.flush = outFlushOverride; diff --git a/src/__tests__/fixtures/with-class-component-plugin-dynamic-bundle/__snapshots__/server--main.js b/src/__tests__/fixtures/with-class-component-plugin-dynamic-bundle/__snapshots__/server--main.js index 160b351..6085dff 100644 --- a/src/__tests__/fixtures/with-class-component-plugin-dynamic-bundle/__snapshots__/server--main.js +++ b/src/__tests__/fixtures/with-class-component-plugin-dynamic-bundle/__snapshots__/server--main.js @@ -8,10 +8,15 @@ /***/ (function(module, exports) { module.exports = { - getBundleName: function getClientCompilerName($global) { - return $global.bundle; - }, - entries: {"test_YDNP":{"browser-A":{"css":["test_YDNP.A.css"],"js":["test_YDNP.A.js"]},"browser-B":{"css":["test_YDNP.B.css"],"js":["test_YDNP.B.js"]},"browser-C":{"css":["test_YDNP.C.css"],"js":["test_YDNP.C.js"]}}} + getAssets(entry, buildName) { + const buildAssets = this.builds[buildName]; + if (!buildAssets) { + throw new Error("Unable to load assets for build with a '$global.buildName' of '" + buildName + "'."); + } + + return buildAssets[entry]; + }, + builds: {"browser-A":{"test_YDNP":{"css":["test_YDNP.A.css"],"js":["test_YDNP.A.js"]}},"browser-B":{"test_YDNP":{"css":["test_YDNP.B.css"],"js":["test_YDNP.B.js"]}},"browser-C":{"test_YDNP":{"css":["test_YDNP.C.css"],"js":["test_YDNP.C.js"]}}} } /***/ }), @@ -70,7 +75,7 @@ const test = __webpack_require__(/*! ./test.marko */ "./src/__tests__/fixtures/w http .createServer((req, res) => { - test.render({}, res); + test.render({ $global: { buildName: "A" } }, res); }) .listen(0); @@ -144,8 +149,7 @@ var marko_template = module.exports = __webpack_require__(/*! marko/dist/html */ template = __webpack_require__(/*! ./test.marko */ "./src/__tests__/fixtures/with-class-component-plugin-dynamic-bundle/test.marko"), module_MARKOWEBPACKMANIFEST_module = __webpack_require__(/*! ./../../../../__MARKO_WEBPACK__MANIFEST.js */ "./__MARKO_WEBPACK__MANIFEST.js"), MARKOWEBPACKMANIFEST_module = module_MARKOWEBPACKMANIFEST_module.default || module_MARKOWEBPACKMANIFEST_module, - getBundleName = module_MARKOWEBPACKMANIFEST_module.getBundleName, - entries = module_MARKOWEBPACKMANIFEST_module.entries, + getAssets = module_MARKOWEBPACKMANIFEST_module.getAssets, marko_dynamicTag = __webpack_require__(/*! marko/dist/runtime/helpers/dynamic-tag */ "marko/dist/runtime/helpers/dynamic-tag"), marko_loadTag = __webpack_require__(/*! marko/dist/runtime/helpers/load-tag */ "marko/dist/runtime/helpers/load-tag"), init_components_tag = marko_loadTag(__webpack_require__(/*! marko/dist/core-tags/components/init-components-tag */ "marko/dist/core-tags/components/init-components-tag")); @@ -198,7 +202,7 @@ function render(input, out, __component, component, state) { out.___renderAssets = renderAssets; - out.___assets = entries["test_YDNP"][getBundleName(out.global)]; + out.___assets = getAssets("test_YDNP", out.global.buildName); out.flush = outFlushOverride; diff --git a/src/__tests__/fixtures/with-class-component-plugin-dynamic-bundle/server.js b/src/__tests__/fixtures/with-class-component-plugin-dynamic-bundle/server.js index 6dc9ebc..5199b48 100644 --- a/src/__tests__/fixtures/with-class-component-plugin-dynamic-bundle/server.js +++ b/src/__tests__/fixtures/with-class-component-plugin-dynamic-bundle/server.js @@ -3,6 +3,6 @@ const test = require("./test.marko"); http .createServer((req, res) => { - test.render({}, res); + test.render({ $global: { buildName: "A" } }, res); }) .listen(0); diff --git a/src/__tests__/fixtures/with-class-component-plugin-dynamic-bundle/webpack.config.ts b/src/__tests__/fixtures/with-class-component-plugin-dynamic-bundle/webpack.config.ts index bc72eea..1ddc97c 100644 --- a/src/__tests__/fixtures/with-class-component-plugin-dynamic-bundle/webpack.config.ts +++ b/src/__tests__/fixtures/with-class-component-plugin-dynamic-bundle/webpack.config.ts @@ -3,11 +3,7 @@ import * as webpack from "webpack"; import MarkoPlugin from "../../../plugin"; import ExtractCSSPlugin from "mini-css-extract-plugin"; -const markoPlugin = new MarkoPlugin({ - getClientCompilerName($global) { - return $global.bundle; - } -}); +const markoPlugin = new MarkoPlugin(); export default [ { diff --git a/src/__tests__/fixtures/with-class-component-plugin/__snapshots__/server--main.js b/src/__tests__/fixtures/with-class-component-plugin/__snapshots__/server--main.js index 3ca870f..8585152 100644 --- a/src/__tests__/fixtures/with-class-component-plugin/__snapshots__/server--main.js +++ b/src/__tests__/fixtures/with-class-component-plugin/__snapshots__/server--main.js @@ -8,8 +8,10 @@ /***/ (function(module, exports) { module.exports = { - getBundleName: function(){return "browser"}, - entries: {"test_nzzJ":{"browser":{"css":["test_nzzJ.css"],"js":["test_nzzJ.js"]}}} + getAssets(entry) { + return this.build[entry]; + }, + build: {"test_nzzJ":{"css":["test_nzzJ.css"],"js":["test_nzzJ.js"]}} } /***/ }), @@ -140,8 +142,7 @@ var marko_template = module.exports = __webpack_require__(/*! marko/dist/html */ template = __webpack_require__(/*! ./test.marko */ "./src/__tests__/fixtures/with-class-component-plugin/test.marko"), module_MARKOWEBPACKMANIFEST_module = __webpack_require__(/*! ./../../../../__MARKO_WEBPACK__MANIFEST.js */ "./__MARKO_WEBPACK__MANIFEST.js"), MARKOWEBPACKMANIFEST_module = module_MARKOWEBPACKMANIFEST_module.default || module_MARKOWEBPACKMANIFEST_module, - getBundleName = module_MARKOWEBPACKMANIFEST_module.getBundleName, - entries = module_MARKOWEBPACKMANIFEST_module.entries, + getAssets = module_MARKOWEBPACKMANIFEST_module.getAssets, marko_dynamicTag = __webpack_require__(/*! marko/dist/runtime/helpers/dynamic-tag */ "marko/dist/runtime/helpers/dynamic-tag"), marko_loadTag = __webpack_require__(/*! marko/dist/runtime/helpers/load-tag */ "marko/dist/runtime/helpers/load-tag"), init_components_tag = marko_loadTag(__webpack_require__(/*! marko/dist/core-tags/components/init-components-tag */ "marko/dist/core-tags/components/init-components-tag")); @@ -194,7 +195,7 @@ function render(input, out, __component, component, state) { out.___renderAssets = renderAssets; - out.___assets = entries["test_nzzJ"][getBundleName(out.global)]; + out.___assets = getAssets("test_nzzJ", out.global.buildName); out.flush = outFlushOverride; diff --git a/src/loader/get-asset-code.ts b/src/loader/get-asset-code.ts index 93531ae..a52d154 100644 --- a/src/loader/get-asset-code.ts +++ b/src/loader/get-asset-code.ts @@ -4,7 +4,7 @@ import { VIRTUAL_SERVER_MANIFEST_PATH } from "../shared/virtual"; export default (resourcePath: string): string => ` import template from ${JSON.stringify(`./${path.basename(resourcePath)}`)}; -import { getBundleName, entries } from ${JSON.stringify( +import { getAssets } from ${JSON.stringify( `./${path.relative(path.dirname(resourcePath), VIRTUAL_SERVER_MANIFEST_PATH)}` )}; @@ -50,9 +50,9 @@ static function outEndOverride(data, encoding, callback) { $ out.___flush = out.flush; $ out.___end = out.end; $ out.___renderAssets = renderAssets; -$ out.___assets = entries[${JSON.stringify( +$ out.___assets = getAssets(${JSON.stringify( moduleName(resourcePath) -)}][getBundleName(out.global)]; +)}, out.global.buildName); $ out.flush = outFlushOverride; $ out.end = outEndOverride; diff --git a/src/plugin/index.ts b/src/plugin/index.ts index 5d8f692..809965b 100644 --- a/src/plugin/index.ts +++ b/src/plugin/index.ts @@ -18,37 +18,20 @@ interface ResolvablePromise extends Promise { resolve(value: T): void; } -interface Options { - getClientCompilerName?($global): string; -} - export default class MarkoWebpackPlugin { private serverIsBuilding = true; - private totalBrowserCompilers = 0; + private browserCompilerNames: string[] = []; private pendingBrowserBuilds: Array> = []; private clientEntries = createDeferredPromise(); private clientAssets: { - [entryName: string]: { - [bundleName: string]: { [assetType: string]: string[] }; + [buildName: string]: { + [entryName: string]: { [assetType: string]: string[] }; }; } = {}; - private getClientCompilerNameSource: string; private virtualServerModules = new VirtualModulesPlugin({ [VIRTUAL_SERVER_MANIFEST_PATH]: MANIFEST_CONTENT }); - constructor(options?: Options) { - if (options && options.getClientCompilerName) { - this.getClientCompilerNameSource = options.getClientCompilerName.toString(); - - if ( - /^getClientCompilerName\s*\(/.test(this.getClientCompilerNameSource) - ) { - this.getClientCompilerNameSource = `function ${this.getClientCompilerNameSource}`; - } - } - } - // Overwritten by each compiler. // eslint-disable-next-line @typescript-eslint/no-empty-function private invalidateBrowserBuild(): void {} @@ -148,11 +131,30 @@ export default class MarkoWebpackPlugin { placeholder ); if (placeholderPosition > -1) { + const hasMultipleBuilds = + this.browserCompilerNames.length > 1; + const content = escapeIfEval( - `{\n getBundleName: ${ - this.getClientCompilerNameSource - },\n entries: ${JSON.stringify(clientAssets)}\n}` + hasMultipleBuilds + ? `{ + getAssets(entry, buildName) { + const buildAssets = this.builds[buildName]; + if (!buildAssets) { + throw new Error("Unable to load assets for build with a '$global.buildName' of '" + buildName + "'."); + } + + return buildAssets[entry]; + }, + builds: ${JSON.stringify(clientAssets)} +}` + : `{ + getAssets(entry) { + return this.build[entry]; + }, + build: ${JSON.stringify(clientAssets[this.browserCompilerNames[0]])} +}` ); + const newSource = new ReplaceSource( compilation.assets[filename], filename @@ -174,28 +176,16 @@ export default class MarkoWebpackPlugin { }; } get browser() { - this.totalBrowserCompilers++; - return (compiler: Compiler): void => { - if (!this.getClientCompilerNameSource) { - if (this.totalBrowserCompilers > 1) { - throw new Error( - "@marko/webpack requires the 'getClientCompilerName' option when using multiple browser compilers." - ); - } - - this.getClientCompilerNameSource = `function(){return ${JSON.stringify( - compiler.options.name - )}}`; - } - let isWatchMode = false; let pendingBuild = createDeferredPromise(); + const compilerName = compiler.options.name; const virtualModules = new VirtualModulesPlugin({ [VIRTUAL_BROWSER_INVALIDATE_PATH]: "" }); registerVirtualModules(compiler, virtualModules); + this.browserCompilerNames.push(compilerName); this.pendingBrowserBuilds.push(pendingBuild); compiler.hooks.watchRun.tap("MarkoWebpackBrowser:watch", () => { @@ -229,9 +219,9 @@ export default class MarkoWebpackPlugin { type.push(asset); } - const entryAssets = (this.clientAssets[entryName] = - this.clientAssets[entryName] || {}); - entryAssets[compiler.options.name] = assetsByType; + const buildAssets = (this.clientAssets[compilerName] = + this.clientAssets[compilerName] || {}); + buildAssets[entryName] = assetsByType; } pendingBuild.resolve();