Include dynamic stubs in linked meteorInstall bundles.

This commit is contained in:
Ben Newman
2017-02-03 11:03:13 -05:00
parent 352f545be2
commit a2e950dfd3

View File

@@ -162,13 +162,15 @@ _.extend(Module.prototype, {
servePath: self.combinedServePath,
};
const results = [result];
// An array of strings and SourceNode objects.
let chunks = [];
let fileCount = 0;
// Emit each file
if (self.meteorInstallOptions) {
const tree = self._buildModuleTree();
const tree = self._buildModuleTree(results, sourceWidth);
fileCount = self._chunkifyModuleTree(tree, chunks, sourceWidth);
result.exportsName =
self._chunkifyEagerRequires(chunks, fileCount, sourceWidth);
@@ -216,19 +218,20 @@ _.extend(Module.prototype, {
}
);
return [result];
return results;
}),
// Builds a tree of nested objects where the properties are names of
// files or directories, and the values are either nested objects
// (representing directories) or File objects (representing modules).
// Bare files and lazy files that are never imported are ignored.
_buildModuleTree() {
_buildModuleTree(results, sourceWidth) {
assert.ok(this.meteorInstallOptions);
// Tree of File objects for all non-dynamic modules.
const tree = {};
_.each(this.files, function (file) {
_.each(this.files, file => {
if (file.bare) {
// Bare files will be added in between the synchronous require
// calls in _chunkifyEagerRequires.
@@ -242,22 +245,71 @@ _.extend(Module.prototype, {
return;
}
const parts = file.installPath.split("/");
let t = tree;
_.each(parts, function (part, i) {
const isLastPart = i === parts.length - 1;
t = _.has(t, part)
? t[part]
: t[part] = isLastPart ? file : {};
});
const dynamic = file.lazy && file.imported === "dynamic";
if (dynamic) {
const servePath = "dynamic/" + file.installPath;
const { code: source, map } =
file.getPrelinkedOutput({
sourceWidth: sourceWidth,
noLineNumbers: this.noLineNumbers
}).toStringWithSourceMap({
file: servePath,
});
results.push({
source,
servePath,
sourceMap: map && map.toJSON(),
dynamic: true,
});
const entry = {
version: file.sourceHash,
};
if (! _.isEmpty(file.deps)) {
entry.deps = file.deps;
}
if (file.installPath.endsWith("/package.json") &&
file.jsonData) {
const main = file.jsonData.main;
if (_.isString(main)) {
entry.main = main;
}
const browser = file.jsonData.browser;
if (_.isString(browser)) {
entry.browser = browser;
}
}
this._addToTree([entry], file.installPath, tree);
} else {
// If the file is not dynamic, then it should be included in the
// initial bundle, so we add it to the static tree.
this._addToTree(file, file.installPath, tree);
}
});
return tree;
},
// Takes the tree generated by _buildModuleTree and populates the chunks
_addToTree(obj, path, tree) {
const parts = path.split("/");
const lastIndex = parts.length - 1;
parts.forEach((part, i) => {
tree = _.has(tree, part)
? tree[part]
: tree[part] = i < lastIndex ? {} : obj;
});
},
// Take the tree generated in getPrelinkedFiles and populate the chunks
// array with strings and SourceNode objects that can be combined into a
// single SourceNode object. Returns the count of modules in the tree.
// single SourceNode object. Return the count of modules in the tree.
_chunkifyModuleTree(tree, chunks, sourceWidth) {
const self = this;
@@ -268,12 +320,18 @@ _.extend(Module.prototype, {
let moduleCount = 0;
function walk(t) {
if (t instanceof File) {
if (Array.isArray(t)) {
++moduleCount;
chunks.push(JSON.stringify(t, null, 2));
} else if (t instanceof File) {
++moduleCount;
chunks.push(t.getPrelinkedOutput({
sourceWidth,
noLineNumbers: self.noLineNumbers
}));
} else if (_.isObject(t)) {
chunks.push("{");
const keys = _.keys(t);
@@ -424,20 +482,16 @@ var File = function (inputFile, module) {
self.servePath = inputFile.servePath;
// Module identifiers imported or required by this module, if any.
if (Array.isArray(inputFile.deps)) {
self.deps = inputFile.deps;
} else if (inputFile.deps && typeof inputFile.deps === "object") {
self.deps = Object.keys(inputFile.deps);
} else {
self.deps = [];
}
// Excludes dynamically imported dependencies, and may exclude
// dependencies already included in the non-dynamic initial bundle.
self.deps = getNonDynamicDeps(inputFile.deps);
// True if the input file should not be evaluated eagerly.
self.lazy = inputFile.lazy; // could be `true`, `false` or `undefined` <sigh>
// True if the file is an eagerly evaluated entry point, or if some
// other file imports or requires it.
self.imported = !!inputFile.imported;
// True if the file is eagerly imported, "dynamic" if the file is
// dynamically imported.
self.imported = inputFile.imported;
// Boolean indicating whether this file is the main entry point module
// for its package.
@@ -450,10 +504,28 @@ var File = function (inputFile, module) {
// Is an Object, not a string.
self.sourceMap = inputFile.sourceMap;
// If inputFile is a JSON file, its parsed data will be exposed via the
// .jsonData property.
self.jsonData = inputFile.jsonData || null;
// The Module containing this file.
self.module = module;
};
function getNonDynamicDeps(inputFileDeps) {
const nonDynamicDeps = Object.create(null);
if (! _.isEmpty(inputFileDeps)) {
_.each(inputFileDeps, (info, id) => {
if (! info.dynamic) {
nonDynamicDeps[id] = info;
}
});
}
return Object.keys(nonDynamicDeps);
}
_.extend(File.prototype, {
// Return the globals in this file as an array of symbol names. For
// example: if the code references 'Foo.bar.baz' and 'Quux', and
@@ -513,19 +585,12 @@ _.extend(File.prototype, {
_getClosureHeader() {
if (this._useMeteorInstall()) {
var header = "";
if (this.deps.length > 0) {
header += "[";
_.each(this.deps, dep => {
header += JSON.stringify(dep) + ",";
});
}
const headerParts = [
header,
"function("
];
// The wrapper function is named "module" so that the UglifyJS
// minifier will parse it as a function declaration, because
// UglifyJS has trouble parsing single function expressions. If the
// module refers to `module`, however, it will be referring to the
// parameter of that name, rather than the function name.
const headerParts = ["function module("];
if (this.source.match(/\b__dirname\b/)) {
headerParts.push("require,exports,module,__filename,__dirname");
@@ -548,14 +613,9 @@ _.extend(File.prototype, {
},
_getClosureFooter() {
if (this._useMeteorInstall()) {
var footer = "}";
if (this.deps.length > 0) {
footer += "]";
}
return footer;
}
return "}).call(this);\n";
return this._useMeteorInstall()
? "}"
: "}).call(this);\n";
},
// Options:
@@ -1057,6 +1117,10 @@ export var fullLink = Profile("linker.fullLink", function (inputFiles, {
var headerContent = (new Array(headerLines + 1).join(';'));
return _.map(prelinkedFiles, function (file) {
if (file.dynamic) {
return file;
}
if (file.sourceMap) {
var sourceMap = file.sourceMap;
sourceMap.mappings = headerContent + sourceMap.mappings;