Skip to content

Commit

Permalink
Merge pull request #23 from marko-js/dynamic-build-api
Browse files Browse the repository at this point in the history
feat: dynamic builds now use $global.buildName
  • Loading branch information
DylanPiercey authored Feb 24, 2020
2 parents ea77337 + 46b204e commit 0ab348e
Show file tree
Hide file tree
Showing 9 changed files with 80 additions and 86 deletions.
24 changes: 13 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 [
{
Expand Down Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"]}}
}

/***/ }),
Expand Down Expand Up @@ -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"));
Expand Down Expand Up @@ -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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"]}}
}

/***/ }),
Expand Down Expand Up @@ -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"));
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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"));
Expand Down Expand Up @@ -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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"]}}}
}

/***/ }),
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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"));
Expand Down Expand Up @@ -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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ const test = require("./test.marko");

http
.createServer((req, res) => {
test.render({}, res);
test.render({ $global: { buildName: "A" } }, res);
})
.listen(0);
Original file line number Diff line number Diff line change
Expand Up @@ -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 [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"]}}
}

/***/ }),
Expand Down Expand Up @@ -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"));
Expand Down Expand Up @@ -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;

Expand Down
6 changes: 3 additions & 3 deletions src/loader/get-asset-code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)}`
)};
Expand Down Expand Up @@ -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;
Expand Down
70 changes: 30 additions & 40 deletions src/plugin/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,37 +18,20 @@ interface ResolvablePromise<T> extends Promise<T> {
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<Promise<void>> = [];
private clientEntries = createDeferredPromise<Entry>();
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 {}
Expand Down Expand Up @@ -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
Expand All @@ -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<void>();
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", () => {
Expand Down Expand Up @@ -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();
Expand Down

0 comments on commit 0ab348e

Please sign in to comment.