Skip to content
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
14 changes: 9 additions & 5 deletions src/webpack/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ const createHash = require("webpack/lib/util/createHash");
const fs = require("fs");
const { mergeDeep } = require("./utils");

const addInterleaveRequire = require("./requireExtentions");
const {
addInterleaveExtension,
addInterleaveRequire
} = require("./requireExtentions");
const { addWebpackRegister } = require("./beforeStartup");
const {
interleaveStyleConfig,
Expand Down Expand Up @@ -383,10 +386,11 @@ class URLImportPlugin {
// this new method will allow a interleaved component to be required and automatically download its dependencies
// it returns a promise so the actual interleaved module is not executed until any missing dependencies are loaded
mainTemplate.hooks.requireExtensions.tap("URLImportPlugin", source => {
return addInterleaveRequire(
source,
mainTemplate.requireFn,
this.opts
return [addInterleaveExtension, addInterleaveRequire].reduce(
(sourceCode, extension) => {
return extension(sourceCode, mainTemplate.requireFn, this.opts);
},
source
);
});
// TODO add an option for this
Expand Down
124 changes: 119 additions & 5 deletions src/webpack/requireExtentions.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,128 @@
const Template = require("webpack/lib/Template");

const detachedPromise = (promiseVar, resolverVar) =>
Template.asString([
`var ${promiseVar} = new Promise(function (resolve, reject) {`,
Template.indent([resolverVar, " = [resolve, reject];"]),
"});"
]);

const chunkPromise = Template.asString([
"function chunkPromise(chunkName){",
Template.indent([
"var resolver;",
detachedPromise("promise", "resolver"),
// never overwrite existing promises
"if(!interleaveDeferred[chunkName])interleaveDeferred[chunkName] = {promise:promise, resolver:resolver};",
"return true"
]),
"}"
]);

/**
* addInterleaveExtension - adds a function to webpack runtime which helps register interleaved chunks requirements
* @param source - the _webpack_require_ source code, from webpack runtime
* @param options - the plugin options
* @returns {string}
*/
export const addInterleaveExtension = (source, { debug }) => {
return Template.asString([
// get the current source template that holds webpack require
source,
// add another function below webpack require, this function
// has access to any and all functions and variables within the webpack bootstrap
"// webpack chunk self registration",
// TODO: use the interleave map added via localVars already within the runtime

"var interleaveDeferred = {};",
"var interleaveDeferredCopy = {};",
"var registeredResolver;",
detachedPromise("allChunksRegistered", "registeredResolver"),
// this is called whenever registerLocals window.webpackRegister.push is executed
// chunkMap is what the chunk pushes into the registration, containing the chunks build hash, chunk names and modules ids it needs
"function registerLocals(chunkMap) {",
chunkPromise,
Template.indent([
"var options = chunkMap[0];",
"var chunkDependencyKeys = chunkMap[1];",
"var chunkModuleHashMap = chunkMap[2];",
debug
? Template.asString([
"console.log({",
Template.indent([
"chunkBelongsToThisBuild: compilationHash === options.hash",
"chunkCompileHash: options.hash,",
"chunkDependencyKeys: chunkDependencyKeys,",
"chunkModuleHashMap: chunkModuleHashMap,",
"webpackModules: modules"
]),
"});"
])
: "",
// check if compilationHash (from webpack runtime) matches the hash the chunk is reporting, if it does then do nothing
// because the chunk isnt being interleaved, its being consumed by the app it actually belongs to
"if(compilationHash !== options.hash) {",
// this still needs to be written, so its mostly logging at the moment - what goes here will be a mix of webpack functions instructing webpack to download urls
Template.indent([
debug ? "console.group('chunkDependencyKeys Loop');" : "",
"chunkDependencyKeys.forEach(function(chunkName){",
Template.indent([
// get css for chunk, if there is any.
"var cssChunks = chunkModuleHashMap[chunkName].css",
debug
? Template.asString([
"console.log({",
Template.indent([
"chunkName: chunkName,",
"css: chunkModuleHashMap[chunkName].css,",
"js: chunkModuleHashMap[chunkName].js,"
]),
"});"
])
: "",
// Loop over the modules a chunk depends on
"chunkModuleHashMap[chunkName].js.find(function(moduleId){",
Template.indent([
"if(!modules[moduleId]) {",
debug
? "console.log('Module', moduleId, 'is missing from host build. Needs', chunkName);"
: "",
// as soon as a missing module is found, get the chunk that contains it from the origin build
Template.indent("return chunkPromise(chunkName)"),
"}"
]),
"});",
// TODO: use CSS cache to determine if must re-download
"if(cssChunks && cssChunks.length) {",
Template.indent([
// loop over css chunks attached to a chunk
"cssChunks.forEach(function(styleChunk){",
debug
? "console.log('CSS', styleChunk, 'is missing from host build. Will download);"
: "",
Template.indent(["chunkPromise(styleChunk)"]),
"});"
]),
"}"
]),
"})",
debug ? "console.endGroup();" : "",
// resolve the promise
`registeredResolver[0]();`
]),
"}"
]),
"};"
]);
};

// setting up async require capabilities
// eslint-disable-next-line no-unused-vars
module.exports = (source, requireFn) => {
const template = Template.asString([
export const addInterleaveRequire = (source, requireFn) => {
return Template.asString([
source,
"",
Template.getFunctionContent(
require("./interleaveFn").requireInterleaveExtension
)
).replace("__webpack__require__", requireFn)
]);
return template.replace("__webpack_require__", requireFn);
};