Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: handle non JS/CSS assets for CDN #1614

Merged
merged 2 commits into from
Apr 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/subapp-web/lib/init.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const subappUtil = require("subapp-util");
module.exports = function setup(setupContext) {
const distDir = process.env.NODE_ENV === "production" ? "../dist/min" : "../dist/dev";
const clientJs = Fs.readFileSync(Path.join(__dirname, distDir, "subapp-web.js")).toString();
const cdnJs = Fs.readFileSync(Path.join(__dirname, distDir, "cdn-map.js")).toString();
const littleLoader = Fs.readFileSync(
require.resolve("little-loader/dist/little-loader.min.js"),
"utf8"
Expand All @@ -27,6 +28,8 @@ module.exports = function setup(setupContext) {

const bundleAssets = {
jsChunksById: cdnJsBundles,
// md === mapping data for other assets
md: util.getCdnOtherMappings(setupContext.routeOptions),
entryPoints: assets.entryPoints,
basePath: ""
};
Expand All @@ -36,6 +39,7 @@ ${JSON.stringify(bundleAssets)}
</script>
<script>/*LL*/${littleLoader}/*LL*/
${clientJs}
${cdnJs}
</script>
`;

Expand Down
48 changes: 42 additions & 6 deletions packages/subapp-web/lib/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const { tryThrowOriginalSubappRegisterError } = require("subapp-util");

let CDN_ASSETS;
let CDN_JS_BUNDLES;
let CDN_OTHER_MAPPINGS;
let FrameworkLib;

const utils = {
Expand Down Expand Up @@ -165,11 +166,7 @@ const utils = {
return cdnBundles;
},

getCdnJsBundles(assets, routeOptions, cdnAssetsFile = "config/assets.json") {
if (CDN_JS_BUNDLES) {
return CDN_JS_BUNDLES;
}

loadCdnAssets(routeOptions, cdnAssetsFile = "config/assets.json") {
if (routeOptions.cdn.enable === true && CDN_ASSETS === undefined) {
const env = process.env.NODE_ENV;
const prod = env === "production";
Expand Down Expand Up @@ -200,6 +197,42 @@ ${ignoreMsg}`
}
}

return CDN_ASSETS;
},

getCdnOtherMappings(routeOptions) {
if (CDN_OTHER_MAPPINGS) {
return CDN_OTHER_MAPPINGS;
}
utils.loadCdnAssets(routeOptions);

/*
* Send non js/css assets as CDN mapping data to the browser.
*
* For js and css assets, webpack is aware of them, and treat them as special chunks.
* IDs are assign to each "chunks". In dev mode, it's the chunk name, but in prod mode
* it's an integer.
*
* For any other assets that are managed by Electrode, after they are uploaded to CDN,
* there's just the CDN mapping info on them, and we need to send them to the browser
* and provide code to map them from original file name to the CDN URL.
*/
CDN_OTHER_MAPPINGS = Object.keys(CDN_ASSETS || {})
.filter(x => !x.endsWith(".js") && !x.endsWith(".css"))
.reduce((acc, k) => {
acc[Path.basename(k)] = CDN_ASSETS[k];
return acc;
}, {});

return CDN_OTHER_MAPPINGS;
},

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

utils.loadCdnAssets(routeOptions);
//
// pack up entrypoints data from stats
//
Expand All @@ -210,7 +243,9 @@ ${ignoreMsg}`

const bundleBase = utils.getBundleBase(routeOptions);
const allChunks = _.mergeWith({}, js, css, (a, b) => (a && b ? [].concat(a, b) : a || b));
return (CDN_JS_BUNDLES = utils.mapCdnAssets(allChunks, bundleBase, CDN_ASSETS));
CDN_JS_BUNDLES = utils.mapCdnAssets(allChunks, bundleBase, CDN_ASSETS);

return CDN_JS_BUNDLES;
},

getChunksById(stats) {
Expand Down Expand Up @@ -299,6 +334,7 @@ ${ignoreMsg}`
//

assets.chunksById = utils.getChunksById(stats);
assets.all = stats.assets;
assets.js = stats.assets.filter(asset => asset.name.endsWith(".js"));
assets.css = stats.assets.filter(asset => asset.name.endsWith(".css"));
assets.entryPoints = stats.entrypoints;
Expand Down
46 changes: 46 additions & 0 deletions packages/subapp-web/src/cdn-map.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//
// add CDN mapping support to xarc subapp client side lib
//
(function (w) {
const xv1 = w.xarcV1;
if (!xv1) return;

// cdnUpdate({md: {}}) - add mapping data to the CDN mapping resource
xv1.cdnUpdate = data => {
const md = data.md;
for (const k in md) {
if (xv1.rt.md[k]) {
console.error("CDN map already exist:", k);
} else {
xv1.rt.md[k] = md[k];
}
}
};

// cdnMap(key) - lookup CDN URL for key in the mapping
xv1.cdnMap = f => {
for (const k in xv1.rt.md) {
if (k.indexOf(f) >= 0) {
return xv1.rt.md[k];
}
}
console.error("CDN map not found:", f);
};

// compatible with window._wml.cdn
if (!w._wml) {
w._wml = {};
}

w._wml.cdn = {
map: xv1.cdnMap,
update: xv1.cdnUpdate
};

xv1.cdnInit = mapData => {
w._wml.cdn.md = xv1.rt.md = mapData.md || mapData;
};

// initialize bundle assets
xv1.getBundleAssets();
})(window);
25 changes: 19 additions & 6 deletions packages/subapp-web/src/subapp-web.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// xarc subapp client side lib version 1
// load into window.xarcV1 as a global
(function(w) {
(function (w) {
const version = 1000000; // ###.###.### major.minor.patch

if (w.xarcV1 && w.xarcV1.version >= version) return w.xarcV1;
Expand All @@ -24,6 +24,15 @@

rt: runtimeInfo,

//
// empty place holders for CDN mapping
//
cdnInit() {},
cdnUpdate() {},
cdnMap(x) {
return x;
},

defer() {
const defer = {};
defer.promise = new Promise((resolve, reject) => {
Expand Down Expand Up @@ -277,6 +286,14 @@
});
},

getBundleAssets() {
if (!runtimeInfo.bundleAssets) {
runtimeInfo.bundleAssets = xv1.dyn("bundleAssets");
xv1.cdnInit(runtimeInfo.bundleAssets);
}
return runtimeInfo.bundleAssets;
},

loadSubAppBundles(names, done) {
done = done || (() => {});

Expand All @@ -286,11 +303,7 @@
return done();
}

if (!runtimeInfo.bundleAssets) {
runtimeInfo.bundleAssets = xv1.dyn("bundleAssets");
}

const ba = runtimeInfo.bundleAssets;
const ba = xv1.getBundleAssets();
const loaded = [];
const assetsToLoad = toLoad
.reduce((a, name) => {
Expand Down
19 changes: 9 additions & 10 deletions packages/subapp-web/test/spec/util.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const {
getSubAppBundle,
getChunksById,
getBundleBase,
loadCdnAssets,
getCdnJsBundles,
getFramework,
setupFramework
Expand All @@ -31,7 +32,7 @@ describe("loadAssetsFromStats", () => {
});
});

describe("getChunksById", function() {
describe("getChunksById", function () {
const prodStats = JSON.parse(Fs.readFileSync(Path.join(__dirname, "../data/prod-stats.json")));
const devStats = JSON.parse(Fs.readFileSync(Path.join(__dirname, "../data/dev-stats.json")));

Expand All @@ -55,7 +56,7 @@ describe("getChunksById", function() {
});
});

describe("getSubAppBundle", function() {
describe("getSubAppBundle", function () {
it("should get bundles for a subapp by name", () => {
// load prod-stats.json
const { assets } = loadAssetsFromStats(Path.join(__dirname, "../data/prod-stats.json"));
Expand All @@ -67,7 +68,7 @@ describe("getSubAppBundle", function() {
});
});

describe("getBundleBase", function() {
describe("getBundleBase", function () {
it("should get base url for bundles in prod mode", () => {
expect(getBundleBase({ prodBundleBase: "http://prod", devBundleBase: "http://dev" })).to.equal(
"http://prod"
Expand All @@ -85,20 +86,18 @@ describe("getBundleBase", function() {
});
});

describe("getCdnJsBundles", function() {
describe("getCdnJsBundles", function () {
it("should generate mapping of chunk ID to CDN URLs", () => {
resetCdn();
const { assets } = loadAssetsFromStats(Path.join(__dirname, "../data/prod-stats.json"));
const cdnJsBundles = getCdnJsBundles(
assets,
{ cdn: { enable: true } },
"test/data/cdn-assets.json"
);
const options = { cdn: { enable: true } };
loadCdnAssets(options, "test/data/cdn-assets.json");
const cdnJsBundles = getCdnJsBundles(assets, options);
expect(cdnJsBundles[7]).contains("http://cdnasset.com/hash-123.js");
});
});

describe("get/set framework", function() {
describe("get/set framework", function () {
it("should allow set/get framework lib", () => {
function FL(ref) {
this.ref = ref;
Expand Down