Skip to content

Commit

Permalink
handle load JS bundles from CDN mapped URLs
Browse files Browse the repository at this point in the history
  • Loading branch information
jchip committed May 28, 2019
1 parent 0544066 commit eb94895
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 20 deletions.
33 changes: 19 additions & 14 deletions packages/subapp-web/lib/init.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,30 +17,35 @@ module.exports = function setup(setupContext) {

const routeData = setupContext.routeOptions.__internals;

const vendorAssets = util.getVendorBundles(routeData.assets);
const bundleBase = util.getBundleBase(setupContext.routeOptions);
const bundleNames = vendorAssets.map(a => a.chunkNames[0]);
const cdnJsBundles = util.getCdnJsBundles(
routeData.assets.byChunkName,
setupContext.routeOptions
);

let vendorBundleLoadJs = "";
let commonScript = "";

const vendorAssets = util.getVendorBundles(routeData.assets);
const bundleNames = vendorAssets.map(a => a.chunkNames[0]);

if (vendorAssets.length > 0) {
vendorBundleLoadJs = `markBundlesLoaded(${JSON.stringify(bundleNames)});`;
commonScript = vendorAssets
.map(a => `<script src="${bundleBase}${a.name}" async></script>`)
.map(a => {
// map all chunk names that has a URL mapped and load them with script tags
return a.chunkNames
.map(x => cdnJsBundles[x] && `<script src="${cdnJsBundles[x]}" async></script>`)
.filter(x => x)
.join("");
})
.join("\n");
}

const jsBundleByChunkName = Object.entries(routeData.assets.byChunkName).reduce(
(a, [name, bundle]) => {
a[name] = Array.isArray(bundle) ? bundle.find(x => x.endsWith(".js")) : bundle;
return a;
},
{}
);

const bundleAssets = {
byChunkName: jsBundleByChunkName,
basePath: bundleBase
byChunkName: cdnJsBundles,
basePath: ""
};

const webSubAppJs = `\n<script id="bundleAssets" type="application/json">
${JSON.stringify(bundleAssets)}
</script>
Expand Down
15 changes: 11 additions & 4 deletions packages/subapp-web/lib/load.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,14 @@ module.exports = function setup(setupContext, token) {
const name = props.name;
const routeData = setupContext.routeOptions.__internals;
const bundleAsset = util.getSubAppBundle(name, routeData.assets);
const bundleJs = bundleAsset.name;
const async = props.async ? " async" : "";
const defer = props.defer ? " defer" : "";
const bundleBase = util.getBundleBase(setupContext.routeOptions);
const comment = process.env.NODE_ENV === "production" ? "" : `<!-- subapp load ${name} -->\n`;

const retrieveDevServerBundle = async () => {
return new Promise((resolve, reject) => {
request(`${bundleBase}${bundleJs}`, (err, resp, body) => {
request(`${bundleBase}${bundleAsset.name}`, (err, resp, body) => {
if (err) {
reject(err);
} else {
Expand All @@ -56,13 +55,21 @@ module.exports = function setup(setupContext, token) {
let bundleScript;
const webpackDev = process.env.WEBPACK_DEV === "true";

const cdnJsBundles = util.getCdnJsBundles(
routeData.assets.byChunkName,
setupContext.routeOptions
);

if (props.inlineScript === "always" || (props.inlineScript === true && !webpackDev)) {
if (!webpackDev) {
const src = Fs.readFileSync(Path.resolve("dist/js", bundleJs)).toString();
const src = Fs.readFileSync(Path.resolve("dist/js", bundleAsset.name)).toString();
bundleScript = `<script>${src}</script>`;
}
} else {
bundleScript = `<script src="${bundleBase}${bundleJs}"${async}${defer}></script>`;
bundleScript = bundleAsset.chunkNames
.map(x => cdnJsBundles[x] && `<script src="${cdnJsBundles[x]}"${async}${defer}></script>`)
.filter(x => x)
.join("");
}

let SubApp;
Expand Down
81 changes: 79 additions & 2 deletions packages/subapp-web/lib/util.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
"use strict";

/* eslint-disable global-require */
/* eslint-disable global-require, max-statements */

const assert = require("assert");
const Path = require("path");

module.exports = {
let CDN_ASSETS;
let CDN_JS_BUNDLES;

const utils = {
getVendorBundles: assets => {
const chunkNames = Object.keys(assets.byChunkName)
.filter(x => x.startsWith("vendors"))
Expand Down Expand Up @@ -34,5 +38,78 @@ module.exports = {
} else {
return routeData.devBundleBase;
}
},

/*
From an object of bundles:
{
bundleName: "bundle-file.js"
}
map with a CDN assets object:
{
"/dist/js/bundle-file.js": "/cdn.com/hash-12345.js"
}
to:
{
bundleName: "/cdn.com/hash-12345.js"
}
If a bundleName doesn't have a CDN map, then its URL is created by prepending basePath:
{
bundleName: "${basePath}bundle-file.js"
}
*/

mapCdnAssets(bundlesByName, basePath = "", assetsFile = "config/assets.json") {
if (!CDN_ASSETS) {
const assetsFp = Path.resolve(assetsFile);
try {
CDN_ASSETS = require(assetsFp);
} catch (err) {
CDN_ASSETS = {};
}
}

const cdnBundles = {};
const bundleNames = Object.keys(bundlesByName);

for (const name of bundleNames) {
const bundleFile = bundlesByName[name];
for (const mapName in CDN_ASSETS) {
if (mapName.endsWith(bundleFile)) {
cdnBundles[name] = CDN_ASSETS[mapName];
break;
}
}

if (!cdnBundles[name]) {
cdnBundles[name] = basePath.concat(bundlesByName[name]);
}
}

return cdnBundles;
},

getCdnJsBundles(byChunkNameAssets, routeOptions) {
if (CDN_JS_BUNDLES) {
return CDN_JS_BUNDLES;
}

const jsBundleByChunkName = Object.entries(byChunkNameAssets).reduce((a, [name, bundle]) => {
a[name] = Array.isArray(bundle) ? bundle.find(x => x.endsWith(".js")) : bundle;
return a;
}, {});

const bundleBase = utils.getBundleBase(routeOptions);

return (CDN_JS_BUNDLES = utils.mapCdnAssets(jsBundleByChunkName, bundleBase));
}
};

module.exports = utils;

0 comments on commit eb94895

Please sign in to comment.