Merge tag 'v4.13.0' into next

4.13.0
This commit is contained in:
Tobias Koppers 2018-07-04 22:08:31 +02:00
commit f0ae54d478
56 changed files with 1026 additions and 507 deletions

View File

@ -51,6 +51,12 @@ or in watch mode
yarn test:unit --watch
```
### To update Jest snapshots use
```bash
yarn test:update-snapshots
```
### To run code formatter (prettier) run
```bash

View File

@ -47,6 +47,7 @@ const isInstalled = packageName => {
* @typedef {Object} CliOption
* @property {string} name display name
* @property {string} package npm package name
* @property {string} binName name of the executable file
* @property {string} alias shortcut for choice
* @property {boolean} installed currently installed?
* @property {string} url homepage
@ -58,6 +59,7 @@ const CLIs = [
{
name: "webpack-cli",
package: "webpack-cli",
binName: "webpack-cli",
alias: "cli",
installed: isInstalled("webpack-cli"),
url: "https://github.com/webpack/webpack-cli",
@ -66,6 +68,7 @@ const CLIs = [
{
name: "webpack-command",
package: "webpack-command",
binName: "webpack-command",
alias: "command",
installed: isInstalled("webpack-command"),
url: "https://github.com/webpack-contrib/webpack-command",
@ -154,7 +157,10 @@ if (installedClis.length === 0) {
});
});
} else if (installedClis.length === 1) {
require(installedClis[0].package); // eslint-disable-line
const path = require("path");
const pkgPath = require.resolve(`${installedClis[0].package}/package.json`);
const pkg = require(pkgPath); // eslint-disable-line
require(path.resolve(path.dirname(pkgPath), pkg.bin[installedClis[0].binName])); // eslint-disable-line
} else {
console.warn(
`You have installed ${installedClis

View File

@ -4,7 +4,7 @@ It's built separately from the app part. The vendors dll is only built when the
The DllPlugin in combination with the `output.library` option exposes the internal require function as global variable in the target environment.
A manifest is creates which includes mappings from module names to internal ids.
A manifest is created which includes mappings from module names to internal ids.
### webpack.config.js

View File

@ -4,7 +4,7 @@ It's built separately from the app part. The vendors dll is only built when the
The DllPlugin in combination with the `output.library` option exposes the internal require function as global variable in the target environment.
A manifest is creates which includes mappings from module names to internal ids.
A manifest is created which includes mappings from module names to internal ids.
### webpack.config.js

View File

@ -70,6 +70,12 @@ class ChunkGroup {
this.chunks = [];
/** @type {OriginRecord[]} */
this.origins = [];
/** Indicies in top-down order */
/** @private @type {Map<Module, number>} */
this._moduleIndicies = new Map();
/** Indicies in bottom-up order */
/** @private @type {Map<Module, number>} */
this._moduleIndicies2 = new Map();
}
/**
@ -440,6 +446,44 @@ class ChunkGroup {
return result;
}
/**
* Sets the top-down index of a module in this ChunkGroup
* @param {Module} module module for which the index should be set
* @param {number} index the index of the module
* @returns {void}
*/
setModuleIndex(module, index) {
this._moduleIndicies.set(module, index);
}
/**
* Gets the top-down index of a module in this ChunkGroup
* @param {Module} module the module
* @returns {number} index
*/
getModuleIndex(module) {
return this._moduleIndicies.get(module);
}
/**
* Sets the bottom-up index of a module in this ChunkGroup
* @param {Module} module module for which the index should be set
* @param {number} index the index of the module
* @returns {void}
*/
setModuleIndex2(module, index) {
this._moduleIndicies2.set(module, index);
}
/**
* Gets the bottom-up index of a module in this ChunkGroup
* @param {Module} module the module
* @returns {number} index
*/
getModuleIndex2(module) {
return this._moduleIndicies2.get(module);
}
checkConstraints() {
const chunk = this;
for (const child of chunk._children) {

View File

@ -34,6 +34,10 @@ const Queue = require("./util/Queue");
const SortableSet = require("./util/SortableSet");
const GraphHelpers = require("./GraphHelpers");
/** @typedef {import("./Module")} Module */
/** @typedef {import("./DependenciesBlock")} DependenciesBlock */
/** @typedef {import("./AsyncDependenciesBlock")} AsyncDependenciesBlock */
const byId = (a, b) => {
if (a.id < b.id) return -1;
if (a.id > b.id) return 1;
@ -234,8 +238,6 @@ class Compilation {
this._modules = new Map();
this.cache = null;
this.records = null;
this.nextFreeModuleIndex = undefined;
this.nextFreeModuleIndex2 = undefined;
this.additionalChunkAssets = [];
this.assets = {};
this.errors = [];
@ -845,8 +847,6 @@ class Compilation {
}
this.hooks.afterOptimizeDependencies.call(this.modules);
this.nextFreeModuleIndex = 0;
this.nextFreeModuleIndex2 = 0;
for (const preparedEntrypoint of this._preparedEntrypoints) {
const module = preparedEntrypoint.module;
const name = preparedEntrypoint.name;
@ -864,7 +864,6 @@ class Compilation {
chunk.entryModule = module;
chunk.name = name;
this.assignIndex(module);
this.assignDepth(module);
}
this.processDependenciesBlocksForChunkGroups(this.chunkGroups.slice());
@ -1013,6 +1012,13 @@ class Compilation {
}
}
/**
* @param {TODO} groupOptions options for the chunk group
* @param {Module} module the module the references the chunk group
* @param {TODO} loc the location from with the chunk group is reference (inside of module)
* @param {string} request the request from which the the chunk group is referenced
* @returns {ChunkGroup} the new or existing chunk group
*/
addChunkInGroup(groupOptions, module, loc, request) {
if (typeof groupOptions === "string") {
groupOptions = { name: groupOptions };
@ -1056,70 +1062,6 @@ class Compilation {
return chunk;
}
assignIndex(module) {
const assignIndexToModule = module => {
// enter module
if (typeof module.index !== "number") {
module.index = this.nextFreeModuleIndex++;
// leave module
queue.push(() => (module.index2 = this.nextFreeModuleIndex2++));
// enter it as block
assignIndexToDependencyBlock(module);
}
};
const assignIndexToDependency = dependency => {
if (dependency.module) {
queue.push(() => assignIndexToModule(dependency.module));
}
};
const assignIndexToDependencyBlock = block => {
let allDependencies = [];
const iteratorDependency = d => allDependencies.push(d);
const iteratorBlock = b =>
queue.push(() => assignIndexToDependencyBlock(b));
if (block.variables) {
iterationBlockVariable(block.variables, iteratorDependency);
}
if (block.dependencies) {
iterationOfArrayCallback(block.dependencies, iteratorDependency);
}
if (block.blocks) {
const blocks = block.blocks;
let indexBlock = blocks.length;
while (indexBlock--) {
iteratorBlock(blocks[indexBlock]);
}
}
let indexAll = allDependencies.length;
while (indexAll--) {
iteratorAllDependencies(allDependencies[indexAll]);
}
};
const queue = [
() => {
assignIndexToModule(module);
}
];
const iteratorAllDependencies = d => {
queue.push(() => assignIndexToDependency(d));
};
while (queue.length) {
queue.pop()();
}
}
assignDepth(module) {
const queue = new Set([module]);
let depth;
@ -1162,7 +1104,12 @@ class Compilation {
}
}
// This method creates the Chunk graph from the Module graph
/**
* This method creates the Chunk graph from the Module graph
* @private
* @param {TODO[]} inputChunkGroups chunk groups which are processed
* @returns {void}
*/
processDependenciesBlocksForChunkGroups(inputChunkGroups) {
// Process is splitting into two parts:
// Part one traverse the module graph and builds a very basic chunks graph
@ -1172,10 +1119,12 @@ class Compilation {
// eachother and Blocks with Chunks. It stops traversing when all modules
// for a chunk are already available. So it doesn't connect unneeded chunks.
const chunkDependencies = new Map(); // Map<Chunk, Array<{Module, Chunk}>>
/** @type {Map<Chunk, {block: DependenciesBlock, chunkGroup: ChunkGroup}[]>} */
const chunkDependencies = new Map();
const allCreatedChunkGroups = new Set();
// PREPARE
/** @type {Map<DependenciesBlock, { modules: Module[], blocks: AsyncDependenciesBlock[]}>} */
const blockInfoMap = new Map();
const iteratorDependency = d => {
@ -1202,7 +1151,15 @@ class Compilation {
blockQueue.push(b);
};
let block, blockQueue, blockInfoModules, blockInfoBlocks;
/** @type {DependenciesBlock} */
let block;
/** @type {TODO} */
let blockQueue;
/** @type {Set<TODO>} */
let blockInfoModules;
/** @type {TODO[]} */
let blockInfoBlocks;
for (const module of this.modules) {
blockQueue = [module];
while (blockQueue.length > 0) {
@ -1223,7 +1180,7 @@ class Compilation {
}
const blockInfo = {
modules: blockInfoModules,
modules: Array.from(blockInfoModules),
blocks: blockInfoBlocks
};
blockInfoMap.set(block, blockInfo);
@ -1232,15 +1189,49 @@ class Compilation {
// PART ONE
/** @type {Map<ChunkGroup, { index: number, index2: number }>} */
const chunkGroupCounters = new Map();
for (const chunkGroup of inputChunkGroups) {
chunkGroupCounters.set(chunkGroup, { index: 0, index2: 0 });
}
let nextFreeModuleIndex = 0;
let nextFreeModuleIndex2 = 0;
/** @type {Map<DependenciesBlock, ChunkGroup>} */
const blockChunkGroups = new Map();
// Start with the provided modules/chunks
const queue = inputChunkGroups.map(chunkGroup => ({
const ADD_AND_ENTER_MODULE = 0;
const ENTER_MODULE = 1;
const PROCESS_BLOCK = 2;
const LEAVE_MODULE = 3;
/**
* @typedef {Object} QueueItem
* @property {number} action
* @property {DependenciesBlock} block
* @property {Module} module
* @property {Chunk} chunk
* @property {ChunkGroup} chunkGroup
*/
/**
* @param {ChunkGroup} chunkGroup chunk group
* @returns {QueueItem} queue item
*/
const chunkGroupToQueueItem = chunkGroup => ({
action: ENTER_MODULE,
block: chunkGroup.chunks[0].entryModule,
module: chunkGroup.chunks[0].entryModule,
chunk: chunkGroup.chunks[0],
chunkGroup
}));
});
// Start with the provided modules/chunks
/** @type {QueueItem[]} */
let queue = inputChunkGroups.map(chunkGroupToQueueItem).reverse();
/** @type {QueueItem[]} */
let queueDelayed = [];
let module, chunk, chunkGroup;
@ -1263,6 +1254,7 @@ class Compilation {
b.loc,
b.request
);
chunkGroupCounters.set(c, { index: 0, index2: 0 });
blockChunkGroups.set(b, c);
allCreatedChunkGroups.add(c);
}
@ -1280,7 +1272,8 @@ class Compilation {
});
// 3. We enqueue the DependenciesBlock for traversal
queue.push({
queueDelayed.push({
action: PROCESS_BLOCK,
block: b,
module: module,
chunk: c.chunks[0],
@ -1291,33 +1284,95 @@ class Compilation {
// Iterative traversal of the Module graph
// Recursive would be simpler to write but could result in Stack Overflows
while (queue.length) {
const queueItem = queue.pop();
module = queueItem.module;
block = queueItem.block;
chunk = queueItem.chunk;
chunkGroup = queueItem.chunkGroup;
while (queue.length) {
const queueItem = queue.pop();
module = queueItem.module;
block = queueItem.block;
chunk = queueItem.chunk;
chunkGroup = queueItem.chunkGroup;
// get prepared block info
const blockInfo = blockInfoMap.get(block);
switch (queueItem.action) {
case ADD_AND_ENTER_MODULE: {
// We connect Module and Chunk when not already done
if (chunk.addModule(module)) {
module.addChunk(chunk);
} else {
// already connected, skip it
break;
}
}
// fallthrough
case ENTER_MODULE: {
if (chunkGroup !== undefined) {
const index = chunkGroup.getModuleIndex(module);
if (index === undefined) {
chunkGroup.setModuleIndex(
module,
chunkGroupCounters.get(chunkGroup).index++
);
}
}
// Traverse all referenced modules
for (const refModule of blockInfo.modules) {
// We connect Module and Chunk when not already done
if (chunk.addModule(refModule)) {
refModule.addChunk(chunk);
if (module.index === null) {
module.index = nextFreeModuleIndex++;
}
// And enqueue the Module for traversal
queue.push({
block: refModule,
module: refModule,
chunk,
chunkGroup
});
queue.push({
action: LEAVE_MODULE,
block,
module,
chunk,
chunkGroup
});
}
// fallthrough
case PROCESS_BLOCK: {
// get prepared block info
const blockInfo = blockInfoMap.get(block);
// Traverse all referenced modules
for (let i = blockInfo.modules.length - 1; i >= 0; i--) {
const refModule = blockInfo.modules[i];
if (chunk.containsModule(refModule)) {
// skip early if already connected
continue;
}
// enqueue the add and enter to enter in the correct order
// this is relevant with circular dependencies
queue.push({
action: ADD_AND_ENTER_MODULE,
block: refModule,
module: refModule,
chunk,
chunkGroup
});
}
// Traverse all Blocks
iterationOfArrayCallback(blockInfo.blocks, iteratorBlock);
break;
}
case LEAVE_MODULE: {
if (chunkGroup !== undefined) {
const index = chunkGroup.getModuleIndex2(module);
if (index === undefined) {
chunkGroup.setModuleIndex2(
module,
chunkGroupCounters.get(chunkGroup).index2++
);
}
}
if (module.index2 === null) {
module.index2 = nextFreeModuleIndex2++;
}
break;
}
}
}
// Traverse all Blocks
iterationOfArrayCallback(blockInfo.blocks, iteratorBlock);
const tempQueue = queue;
queue = queueDelayed.reverse();
queueDelayed = tempQueue;
}
// PART TWO

View File

@ -14,26 +14,44 @@ const {
} = require("./JavascriptParserHelpers");
const NullFactory = require("./NullFactory");
const stringifyObj = obj => {
class RuntimeValue {
constructor(fn, fileDependencies) {
this.fn = fn;
this.fileDependencies = fileDependencies || [];
}
exec(parser) {
for (const fileDependency of this.fileDependencies) {
parser.state.module.buildInfo.fileDependencies.add(fileDependency);
}
return this.fn();
}
}
const stringifyObj = (obj, parser) => {
return (
"Object({" +
Object.keys(obj)
.map(key => {
const code = obj[key];
return JSON.stringify(key) + ":" + toCode(code);
return JSON.stringify(key) + ":" + toCode(code, parser);
})
.join(",") +
"})"
);
};
const toCode = code => {
const toCode = (code, parser) => {
if (code === null) {
return "null";
}
if (code === undefined) {
return "undefined";
}
if (code instanceof RuntimeValue) {
return toCode(code.exec(parser), parser);
}
if (code instanceof RegExp && code.toString) {
return code.toString();
}
@ -41,7 +59,7 @@ const toCode = code => {
return "(" + code.toString() + ")";
}
if (typeof code === "object") {
return stringifyObj(code);
return stringifyObj(code, parser);
}
return code + "";
};
@ -51,6 +69,10 @@ class DefinePlugin {
this.definitions = definitions;
}
static runtimeValue(fn, fileDependencies) {
return new RuntimeValue(fn, fileDependencies);
}
apply(compiler) {
const definitions = this.definitions;
compiler.hooks.compilation.tap(
@ -69,6 +91,7 @@ class DefinePlugin {
if (
code &&
typeof code === "object" &&
!(code instanceof RuntimeValue) &&
!(code instanceof RegExp)
) {
walkDefinitions(code, prefix + key + ".");
@ -93,7 +116,6 @@ class DefinePlugin {
if (isTypeof) key = key.replace(/^typeof\s+/, "");
let recurse = false;
let recurseTypeof = false;
code = toCode(code);
if (!isTypeof) {
parser.hooks.canRename.for(key).tap("DefinePlugin", approve);
parser.hooks.evaluateIdentifier
@ -109,21 +131,23 @@ class DefinePlugin {
*/
if (recurse) return;
recurse = true;
const res = parser.evaluate(code);
const res = parser.evaluate(toCode(code, parser));
recurse = false;
res.setRange(expr.range);
return res;
});
parser.hooks.expression
.for(key)
.tap(
"DefinePlugin",
/__webpack_require__/.test(code)
? toConstantDependencyWithWebpackRequire(parser, code)
: toConstantDependency(parser, code)
);
parser.hooks.expression.for(key).tap("DefinePlugin", expr => {
const strCode = toCode(code, parser);
if (/__webpack_require__/.test(strCode)) {
return toConstantDependencyWithWebpackRequire(
parser,
strCode
)(expr);
} else {
return toConstantDependency(parser, strCode)(expr);
}
});
}
const typeofCode = isTypeof ? code : "typeof (" + code + ")";
parser.hooks.evaluateTypeof.for(key).tap("DefinePlugin", expr => {
/**
* this is needed in case there is a recursion in the DefinePlugin
@ -135,12 +159,18 @@ class DefinePlugin {
*/
if (recurseTypeof) return;
recurseTypeof = true;
const typeofCode = isTypeof
? toCode(code, parser)
: "typeof (" + toCode(code, parser) + ")";
const res = parser.evaluate(typeofCode);
recurseTypeof = false;
res.setRange(expr.range);
return res;
});
parser.hooks.typeof.for(key).tap("DefinePlugin", expr => {
const typeofCode = isTypeof
? toCode(code, parser)
: "typeof (" + toCode(code, parser) + ")";
const res = parser.evaluate(typeofCode);
if (!res.isString()) return;
return toConstantDependency(
@ -151,7 +181,6 @@ class DefinePlugin {
};
const applyObjectDefine = (key, obj) => {
const code = stringifyObj(obj);
parser.hooks.canRename.for(key).tap("DefinePlugin", approve);
parser.hooks.evaluateIdentifier
.for(key)
@ -161,14 +190,17 @@ class DefinePlugin {
parser.hooks.evaluateTypeof
.for(key)
.tap("DefinePlugin", evaluateToString("object"));
parser.hooks.expression
.for(key)
.tap(
"DefinePlugin",
/__webpack_require__/.test(code)
? toConstantDependencyWithWebpackRequire(parser, code)
: toConstantDependency(parser, code)
);
parser.hooks.expression.for(key).tap("DefinePlugin", expr => {
const strCode = stringifyObj(obj, parser);
if (/__webpack_require__/.test(strCode)) {
return toConstantDependencyWithWebpackRequire(parser, strCode)(
expr
);
} else {
return toConstantDependency(parser, strCode)(expr);
}
});
parser.hooks.typeof
.for(key)
.tap(

View File

@ -41,11 +41,9 @@ class DependenciesBlockVariable {
hasDependencies(filter) {
if (filter) {
if (this.dependencies.some(filter)) return true;
} else {
if (this.dependencies.length > 0) return true;
return this.dependencies.some(filter);
}
return false;
return this.dependencies.length > 0;
}
}

View File

@ -56,6 +56,7 @@ const RuntimeChunkPlugin = require("./optimize/RuntimeChunkPlugin");
const NoEmitOnErrorsPlugin = require("./NoEmitOnErrorsPlugin");
const NamedModulesPlugin = require("./NamedModulesPlugin");
const NamedChunksPlugin = require("./NamedChunksPlugin");
const HashedModuleIdsPlugin = require("./HashedModuleIdsPlugin");
const DefinePlugin = require("./DefinePlugin");
const SizeLimitsPlugin = require("./performance/SizeLimitsPlugin");
const WasmFinalizeExportsPlugin = require("./wasm/WasmFinalizeExportsPlugin");
@ -354,6 +355,9 @@ class WebpackOptionsApply extends OptionsApply {
if (options.optimization.namedModules) {
new NamedModulesPlugin().apply(compiler);
}
if (options.optimization.hashedModuleIds) {
new HashedModuleIdsPlugin().apply(compiler);
}
if (options.optimization.namedChunks) {
new NamedChunksPlugin().apply(compiler);
}

View File

@ -265,6 +265,7 @@ class WebpackOptionsDefaulter extends OptionsDefaulter {
"make",
options => options.mode === "development"
);
this.set("optimization.hashedModuleIds", false);
this.set(
"optimization.namedChunks",
"make",

View File

@ -5,7 +5,7 @@
*/
class Queue {
/**
* @param {IterableIterator<T>=} items The initial elements.
* @param {Iterable<T>=} items The initial elements.
*/
constructor(items) {
/** @private @type {Set<T>} */

View File

@ -154,15 +154,21 @@ class JsonpMainTemplatePlugin {
: "",
"script.charset = 'utf-8';",
`script.timeout = ${chunkLoadTimeout / 1000};`,
crossOriginLoading
? `script.crossOrigin = ${JSON.stringify(crossOriginLoading)};`
: "",
`if (${mainTemplate.requireFn}.nc) {`,
Template.indent(
`script.setAttribute("nonce", ${mainTemplate.requireFn}.nc);`
),
"}",
"script.src = jsonpScriptSrc(chunkId);",
crossOriginLoading
? Template.asString([
"if (script.src.indexOf(window.location.origin + '/') !== 0) {",
Template.indent(
`script.crossOrigin = ${JSON.stringify(crossOriginLoading)};`
),
"}"
])
: "",
"onScriptComplete = function (event) {",
Template.indent([
"// avoid mem leaks in IE.",
@ -208,9 +214,6 @@ class JsonpMainTemplatePlugin {
? `link.type = ${JSON.stringify(jsonpScriptType)};`
: "",
"link.charset = 'utf-8';",
crossOriginLoading
? `link.crossOrigin = ${JSON.stringify(crossOriginLoading)};`
: "",
`if (${mainTemplate.requireFn}.nc) {`,
Template.indent(
`link.setAttribute("nonce", ${mainTemplate.requireFn}.nc);`
@ -218,7 +221,16 @@ class JsonpMainTemplatePlugin {
"}",
'link.rel = "preload";',
'link.as = "script";',
"link.href = jsonpScriptSrc(chunkId);"
"link.href = jsonpScriptSrc(chunkId);",
crossOriginLoading
? Template.asString([
"if (link.href.indexOf(window.location.origin + '/') !== 0) {",
Template.indent(
`link.crossOrigin = ${JSON.stringify(crossOriginLoading)};`
),
"}"
])
: ""
]);
}
);

View File

@ -1,6 +1,6 @@
{
"name": "webpack",
"version": "4.12.2",
"version": "4.13.0",
"author": "Tobias Koppers @sokra",
"description": "Packs CommonJs/AMD modules for the browser. Allows to split your codebase into multiple bundles, which can be loaded on demand. Support loaders to preprocess files, i.e. json, jsx, es7, css, less, ... and your custom stuff.",
"license": "MIT",
@ -15,7 +15,7 @@
"ajv": "^6.1.0",
"ajv-keywords": "^3.1.0",
"chrome-trace-event": "^1.0.0",
"enhanced-resolve": "^4.0.0",
"enhanced-resolve": "^4.1.0",
"eslint-scope": "^3.7.1",
"json-parse-better-errors": "^1.0.2",
"loader-runner": "^2.3.0",
@ -103,6 +103,7 @@
"scripts": {
"setup": "node ./setup/setup.js",
"test": "node --max-old-space-size=4096 --trace-deprecation node_modules/jest-cli/bin/jest",
"test:update-snapshots": "yarn jest -u",
"test:integration": "node --max-old-space-size=4096 --trace-deprecation node_modules/jest-cli/bin/jest --testMatch \"<rootDir>/test/*.test.js\"",
"test:basic": "node --max-old-space-size=4096 --trace-deprecation node_modules/jest-cli/bin/jest --testMatch \"<rootDir>/test/{TestCasesNormal,StatsTestCases,ConfigTestCases}.test.js\"",
"test:unit": "node --max-old-space-size=4096 --trace-deprecation node_modules/jest-cli/bin/jest --testMatch \"<rootDir>/test/*.unittest.js\"",

View File

@ -1563,6 +1563,10 @@
"description": "Use readable module identifiers for better debugging",
"type": "boolean"
},
"hashedModuleIds": {
"description": "Use hashed module id instead module identifiers for better long term caching",
"type": "boolean"
},
"namedChunks": {
"description": "Use readable chunk identifiers for better debugging",
"type": "boolean"

View File

@ -178,7 +178,14 @@ describe("ConfigTestCases", () => {
expect: expect,
setTimeout: setTimeout,
clearTimeout: clearTimeout,
document: new FakeDocument()
document: new FakeDocument(),
location: {
href: "https://test.cases/path/index.html",
origin: "https://test.cases",
toString() {
return "https://test.cases/path/index.html";
}
}
};
function _require(currentDirectory, module) {

View File

@ -29,6 +29,7 @@ const DEFAULT_OPTIMIZATIONS = {
noEmitOnErrors: false,
concatenateModules: false,
namedModules: false,
hashedModuleIds: false,
minimizer: [uglifyJsForTesting]
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1 @@
import "./shared";

View File

@ -0,0 +1 @@
import "./shared";

View File

@ -0,0 +1,6 @@
import "./a";
import(/* webpackChunkName: "async" */ "./async");
import "./b";
import "./c";
it("should compile", () => {});

View File

@ -0,0 +1,6 @@
import "./c";
import(/* webpackChunkName: "async" */ "./async");
import "./b";
import "./a";
it("should compile", () => {});

View File

@ -0,0 +1,5 @@
module.exports = {
findBundle: function(i, options) {
return ["entry1.js", "entry2.js"];
}
};

View File

@ -0,0 +1,95 @@
/** @typedef {import("../../../../lib/Compilation")} Compilation */
/** @typedef {import("../../../../lib/Module")} Module */
module.exports = {
entry: {
entry1: "./entry1",
entry2: "./entry2"
},
output: {
filename: "[name].js"
},
plugins: [
function() {
/**
* @param {Compilation} compilation compilation
* @returns {void}
*/
const handler = compilation => {
compilation.hooks.afterSeal.tap("testcase", () => {
const data = {};
for (const [name, group] of compilation.namedChunkGroups) {
/** @type {Map<Module, number>} */
const modules = new Map();
const modules2 = new Map();
for (const chunk of group.chunks) {
for (const module of chunk.modulesIterable) {
modules.set(module, group.getModuleIndex(module));
modules2.set(module, group.getModuleIndex2(module));
}
}
const sortedModules = Array.from(modules).sort((a, b) => {
return a[1] - b[1];
});
const sortedModules2 = Array.from(modules2).sort((a, b) => {
return a[1] - b[1];
});
const text = sortedModules
.map(
([m, index]) =>
`${index}: ${m.readableIdentifier(
compilation.requestShortener
)}`
)
.join(", ");
const text2 = sortedModules2
.map(
([m, index]) =>
`${index}: ${m.readableIdentifier(
compilation.requestShortener
)}`
)
.join(", ");
data[name + "Index"] = text;
data[name + "Index2"] = text2;
}
expect(data).toEqual({
entry1Index:
"0: ./entry1.js, 1: ./a.js, 2: ./shared.js, 3: ./b.js, 4: ./c.js",
entry1Index2:
"0: ./shared.js, 1: ./a.js, 2: ./b.js, 3: ./c.js, 4: ./entry1.js",
entry2Index:
"0: ./entry2.js, 1: ./c.js, 2: ./b.js, 3: ./shared.js, 4: ./a.js",
entry2Index2:
"0: ./c.js, 1: ./shared.js, 2: ./b.js, 3: ./a.js, 4: ./entry2.js",
asyncIndex: "0: ./async.js",
asyncIndex2: "0: ./async.js"
});
const indicies = compilation.modules
.map(
m =>
`${m.index}: ${m.readableIdentifier(
compilation.requestShortener
)}`
)
.join(", ");
const indicies2 = compilation.modules
.map(
m =>
`${m.index2}: ${m.readableIdentifier(
compilation.requestShortener
)}`
)
.join(", ");
expect(indicies).toEqual(
"2: ./shared.js, 4: ./c.js, 3: ./b.js, 1: ./a.js, 6: ./async.js, 5: ./entry2.js, 0: ./entry1.js"
);
expect(indicies2).toEqual(
"0: ./shared.js, 3: ./c.js, 2: ./b.js, 1: ./a.js, 6: ./async.js, 5: ./entry2.js, 4: ./entry1.js"
);
});
};
this.hooks.compilation.tap("testcase", handler);
}
]
};

View File

@ -0,0 +1,67 @@
it("should load script without crossorigin attribute (default)", function() {
const promise = import("./empty?a" /* webpackChunkName: "crossorigin-default" */);
var script = document.head._children.pop();
__non_webpack_require__("./crossorigin-default.web.js");
expect(script.src).toBe("https://test.cases/path/crossorigin-default.web.js");
expect(script.crossOrigin).toBe(undefined);
return promise;
});
it("should load script without crossorigin attribute (relative)", function() {
var originalValue = __webpack_public_path__;
__webpack_public_path__ = "../";
const promise = import("./empty?b" /* webpackChunkName: "crossorigin-relative" */);
__webpack_public_path__ = originalValue;
var script = document.head._children.pop();
__non_webpack_require__("./crossorigin-relative.web.js");
expect(script.src).toBe("https://test.cases/crossorigin-relative.web.js");
expect(script.crossOrigin).toBe(undefined);
return promise;
});
it("should load script without crossorigin attribute (server relative)", function() {
var originalValue = __webpack_public_path__;
__webpack_public_path__ = "/server/";
const promise = import("./empty?c" /* webpackChunkName: "crossorigin-server-relative" */);
__webpack_public_path__ = originalValue;
var script = document.head._children.pop();
__non_webpack_require__("./crossorigin-server-relative.web.js");
expect(script.src).toBe("https://test.cases/server/crossorigin-server-relative.web.js");
expect(script.crossOrigin).toBe(undefined);
return promise;
});
it("should load script without crossorigin attribute (same origin)", function() {
var originalValue = __webpack_public_path__;
__webpack_public_path__ = "https://test.cases/";
const promise = import("./empty?d" /* webpackChunkName: "crossorigin-same-origin" */);
__webpack_public_path__ = originalValue;
var script = document.head._children.pop();
__non_webpack_require__("./crossorigin-same-origin.web.js");
expect(script.src).toBe("https://test.cases/crossorigin-same-origin.web.js");
expect(script.crossOrigin).toBe(undefined);
return promise;
});
it("should load script with crossorigin attribute anonymous (different origin)", function() {
var originalValue = __webpack_public_path__;
__webpack_public_path__ = "https://example.com/";
const promise = import("./empty?e" /* webpackChunkName: "crossorigin-different-origin" */);
__webpack_public_path__ = originalValue;
var script = document.head._children.pop();
__non_webpack_require__("./crossorigin-different-origin.web.js");
expect(script.src).toBe("https://example.com/crossorigin-different-origin.web.js");
expect(script.crossOrigin).toBe("anonymous");
return promise;
});

View File

@ -0,0 +1,13 @@
module.exports = {
target: "web",
output: {
chunkFilename: "[name].web.js",
crossOriginLoading: "anonymous"
},
performance: {
hints: false
},
optimization: {
minimize: false
}
};

View File

@ -0,0 +1 @@
module.exports = module.id;

View File

@ -0,0 +1 @@
module.exports = module.id;

View File

@ -0,0 +1 @@
module.exports = module.id;

View File

@ -0,0 +1 @@
module.exports = module.id;

View File

@ -0,0 +1 @@
module.exports = module.id;

View File

@ -0,0 +1,7 @@
it("should have named modules ids", function() {
for (var i = 1; i <= 5; i++) {
var moduleId = require("./files/file" + i + ".js");
expect(moduleId).toMatch(/^[/=a-zA-Z0-9]{4,5}$/);
}
});

View File

@ -0,0 +1,5 @@
module.exports = {
optimization: {
hashedModuleIds: true
}
};

View File

@ -0,0 +1 @@
module.exports = module.id;

View File

@ -0,0 +1 @@
module.exports = module.id;

View File

@ -0,0 +1 @@
module.exports = module.id;

View File

@ -0,0 +1 @@
module.exports = module.id;

View File

@ -0,0 +1 @@
module.exports = module.id;

View File

@ -0,0 +1,10 @@
var path = require("path");
it("should have named modules ids", function() {
for (var i = 1; i <= 5; i++) {
var expectedModuleId = "file" + i + ".js";
var moduleId = require("./files/file" + i + ".js");
expect(path.basename(moduleId)).toBe(expectedModuleId);
}
});

View File

@ -0,0 +1,5 @@
module.exports = {
optimization: {
namedModules: true
}
};

View File

@ -2,7 +2,7 @@ it("should be able to load the split chunk on demand", () => {
const promise = import(/* webpackChunkName: "shared" */ "./shared");
const script = document.head._children[0];
expect(script.src).toBe("dep~b~shared.js");
expect(script.src).toBe("https://test.cases/path/dep~b~shared.js");
__non_webpack_require__("./dep~b~shared.js");

View File

@ -10,11 +10,11 @@ beforeEach(() => {
afterEach(() => {
__webpack_nonce__ = oldNonce;
__webpack_public_path__ = oldPublicPath;
})
});
it("should prefetch and preload child chunks on chunk load", () => {
__webpack_nonce__ = "nonce";
__webpack_public_path__ = "/public/path/";
__webpack_public_path__ = "https://example.com/public/path/";
let link, script;

View File

@ -20,6 +20,8 @@ class FakeElement {
this._type = type;
this._children = [];
this._attributes = Object.create(null);
this._src = undefined;
this._href = undefined;
}
appendChild(node) {
@ -33,4 +35,40 @@ class FakeElement {
getAttribute(name) {
return this._attributes[name];
}
_toRealUrl(value) {
if (/^\//.test(value)) {
return `https://test.cases${value}`;
} else if (/^\.\.\//.test(value)) {
return `https://test.cases${value.substr(2)}`;
} else if (/^\.\//.test(value)) {
return `https://test.cases/path${value.substr(1)}`;
} else if (/^\w+:\/\//.test(value)) {
return value;
} else if (/^\/\//.test(value)) {
return `https:${value}`;
} else {
return `https://test.cases/path/${value}`;
}
}
set src(value) {
if (this._type === "script") {
this._src = this._toRealUrl(value);
}
}
get src() {
return this._src;
}
set href(value) {
if (this._type === "link") {
this._href = this._toRealUrl(value);
}
}
get href() {
return this._href;
}
}

View File

@ -0,0 +1 @@
123

View File

@ -0,0 +1 @@
321

View File

@ -1,4 +1,11 @@
var webpack = require("../../../");
var fs = require("fs");
var join = require("path").join;
function read(path) {
return JSON.stringify(fs.readFileSync(join(__dirname, path), "utf8"));
}
module.exports = [
{
mode: "production",
@ -18,5 +25,22 @@ module.exports = [
VALUE: "321"
})
]
},
{
mode: "production",
entry: "./index",
plugins: [
new webpack.DefinePlugin({
VALUE: webpack.DefinePlugin.runtimeValue(() => read("123.txt"), [
"./123.txt"
])
}),
new webpack.DefinePlugin({
VALUE: webpack.DefinePlugin.runtimeValue(() => read("321.txt"), [
"./321.txt"
])
})
]
}
];

View File

@ -0,0 +1,17 @@
it("should be able to use dynamic defines in watch mode", function() {
const module = require("./module");
expect(module).toEqual({
default: WATCH_STEP,
type: "string",
[Symbol.toStringTag]: "Module"
});
});
it("should not update a define when dependencies list is missing", function() {
const module2 = require("./module2");
expect(module2).toEqual({
default: "0",
type: "string",
[Symbol.toStringTag]: "Module"
});
});

View File

@ -0,0 +1,2 @@
export default TEST_VALUE;
export const type = typeof TEST_VALUE;

View File

@ -0,0 +1,2 @@
export default TEST_VALUE2;
export const type = typeof TEST_VALUE2;

View File

@ -0,0 +1 @@
0

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,22 @@
const path = require("path");
const fs = require("fs");
const webpack = require("../../../../");
const valueFile = path.resolve(
__dirname,
"../../../js/watch-src/plugins/define-plugin/value.txt"
);
module.exports = {
plugins: [
new webpack.DefinePlugin({
TEST_VALUE: webpack.DefinePlugin.runtimeValue(
() => {
return JSON.stringify(fs.readFileSync(valueFile, "utf-8").trim());
},
[valueFile]
),
TEST_VALUE2: webpack.DefinePlugin.runtimeValue(() => {
return JSON.stringify(fs.readFileSync(valueFile, "utf-8").trim());
}, [])
})
]
};

View File

@ -1750,9 +1750,9 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0:
dependencies:
once "^1.4.0"
enhanced-resolve@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.0.0.tgz#e34a6eaa790f62fccd71d93959f56b2b432db10a"
enhanced-resolve@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz#41c7e0bfdfe74ac1ffe1e57ad6a5c6c9f3742a7f"
dependencies:
graceful-fs "^4.1.2"
memory-fs "^0.4.0"