Merge branch 'master' into feature/module-build-error-with-loader-name

# Conflicts:
#	lib/ModuleBuildError.js
#	lib/ModuleError.js
#	lib/ModuleWarning.js
This commit is contained in:
Tobias Koppers 2018-06-05 10:23:00 +02:00
commit 9fbe914078
84 changed files with 1198 additions and 491 deletions

45
declarations.d.ts vendored
View File

@ -36,6 +36,7 @@ declare module "@webassemblyjs/ast" {
ModuleImport?: (p: NodePath<ModuleImport>) => void;
ModuleExport?: (p: NodePath<ModuleExport>) => void;
Start?: (p: NodePath<Start>) => void;
Global?: (p: NodePath<Global>) => void;
}
);
export class NodePath<T> {
@ -60,28 +61,46 @@ declare module "@webassemblyjs/ast" {
}
export class ModuleExport extends Node {
name: string;
descr: ModuleExportDescr;
}
type Index = Identifier | NumberLiteral;
export class ModuleExportDescr extends Node {
type: string;
exportType: string;
id: Index;
}
export class NumberLiteral extends Node {
value: number;
raw: string;
}
export class ModuleExportDescr extends Node {}
export class IndexLiteral extends Node {}
export class NumberLiteral extends Node {}
export class FloatLiteral extends Node {}
export class Global extends Node {}
export class GlobalType extends Node {
valtype: string;
}
export class Global extends Node {
init: Instruction[];
globalType: GlobalType;
}
export class FuncParam extends Node {
valtype: string;
}
export class Instruction extends Node {}
export class Instruction extends Node {
id: string;
args: NumberLiteral[];
}
export class CallInstruction extends Instruction {}
export class ObjectInstruction extends Instruction {}
export class Func extends Node {
signature: Signature;
}
export class Signature {
type: "Signature";
params: FuncParam[];
results: string[];
}
export class TypeInstruction extends Node {}
export class IndexInFuncSection extends Node {}
export function indexLiteral(index: number): IndexLiteral;
export function indexLiteral(index: number): Index;
export function numberLiteralFromRaw(num: number): NumberLiteral;
export function floatLiteral(
value: number,
@ -93,29 +112,33 @@ declare module "@webassemblyjs/ast" {
export function identifier(indentifier: string): Identifier;
export function funcParam(valType: string, id: Identifier): FuncParam;
export function instruction(inst: string, args: Node[]): Instruction;
export function callInstruction(funcIndex: IndexLiteral): CallInstruction;
export function callInstruction(funcIndex: Index): CallInstruction;
export function objectInstruction(
kind: string,
type: string,
init: Node[]
): ObjectInstruction;
export function signature(params: FuncParam[], results: string[]): Signature;
export function func(initFuncId, Signature, funcBody): Func;
export function func(initFuncId, signature: Signature, funcBody): Func;
export function typeInstruction(
id: Identifier,
functype: Signature
): TypeInstruction;
export function indexInFuncSection(index: IndexLiteral): IndexInFuncSection;
export function indexInFuncSection(index: Index): IndexInFuncSection;
export function moduleExport(
identifier: string,
descr: ModuleExportDescr
): ModuleExport;
export function moduleExportDescr(
type: string,
index: ModuleExportDescr
): ModuleExport;
index: Index
): ModuleExportDescr;
export function getSectionMetadata(ast: any, section: string);
export class FuncSignature {
args: string[];
result: string[];
}
}
/**

View File

@ -16,13 +16,13 @@ class AsyncDependencyToInitialChunkError extends WebpackError {
* @param {TODO} loc location of dependency
*/
constructor(chunkName, module, loc) {
super();
super(
`It's not allowed to load an initial chunk on demand. The chunk name "${chunkName}" is already used by an entrypoint.`
);
this.name = "AsyncDependencyToInitialChunkError";
this.message = `It's not allowed to load an initial chunk on demand. The chunk name "${chunkName}" is already used by an entrypoint.`;
this.module = module;
this.origin = module;
this.originLoc = loc;
this.loc = loc;
Error.captureStackTrace(this, this.constructor);
}

View File

@ -8,64 +8,60 @@ const WebpackError = require("./WebpackError");
/** @typedef {import("./Module")} Module */
/**
* @param {Module[]} modules the modules to be sorted
* @returns {Module[]} sorted version of original modules
*/
const sortModules = modules => {
return modules.slice().sort((a, b) => {
a = a.identifier();
b = b.identifier();
/* istanbul ignore next */
if (a < b) return -1;
/* istanbul ignore next */
if (a > b) return 1;
/* istanbul ignore next */
return 0;
});
};
/**
* @param {Module[]} modules each module from throw
* @returns {string} each message from provided moduels
*/
const createModulesListMessage = modules => {
return modules
.map(m => {
let message = `* ${m.identifier()}`;
const validReasons = m.reasons.filter(reason => reason.module);
if (validReasons.length > 0) {
message += `\n Used by ${validReasons.length} module(s), i. e.`;
message += `\n ${validReasons[0].module.identifier()}`;
}
return message;
})
.join("\n");
};
class CaseSensitiveModulesWarning extends WebpackError {
/**
* Creates an instance of CaseSensitiveModulesWarning.
* @param {Module[]} modules modules that were detected
*/
constructor(modules) {
super();
this.name = "CaseSensitiveModulesWarning";
const sortedModules = this._sort(modules);
const modulesList = this._moduleMessages(sortedModules);
this.message = `There are multiple modules with names that only differ in casing.
const sortedModules = sortModules(modules);
const modulesList = createModulesListMessage(sortedModules);
super(`There are multiple modules with names that only differ in casing.
This can lead to unexpected behavior when compiling on a filesystem with other case-semantic.
Use equal casing. Compare these module identifiers:
${modulesList}`;
${modulesList}`);
this.name = "CaseSensitiveModulesWarning";
this.origin = this.module = sortedModules[0];
Error.captureStackTrace(this, this.constructor);
}
/**
* @private
* @param {Module[]} modules the modules to be sorted
* @returns {Module[]} sorted version of original modules
*/
_sort(modules) {
return modules.slice().sort((a, b) => {
a = a.identifier();
b = b.identifier();
/* istanbul ignore next */
if (a < b) return -1;
/* istanbul ignore next */
if (a > b) return 1;
/* istanbul ignore next */
return 0;
});
}
/**
* @private
* @param {Module[]} modules each module from throw
* @returns {string} each message from provided moduels
*/
_moduleMessages(modules) {
return modules
.map(m => {
let message = `* ${m.identifier()}`;
const validReasons = m.reasons.filter(reason => reason.module);
if (validReasons.length > 0) {
message += `\n Used by ${validReasons.length} module(s), i. e.`;
message += `\n ${validReasons[0].module.identifier()}`;
}
return message;
})
.join("\n");
}
}
module.exports = CaseSensitiveModulesWarning;

View File

@ -607,10 +607,10 @@ class Chunk {
return result;
}
getChildIdsByOrdersMap() {
getChildIdsByOrdersMap(includeDirectChildren) {
const chunkMaps = Object.create(null);
for (const chunk of this.getAllAsyncChunks()) {
const addChildIdsByOrdersToMap = chunk => {
const data = chunk.getChildIdsByOrders();
for (const key of Object.keys(data)) {
let chunkMap = chunkMaps[key];
@ -619,7 +619,16 @@ class Chunk {
}
chunkMap[chunk.id] = data[key];
}
};
if (includeDirectChildren) {
addChildIdsByOrdersToMap(this);
}
for (const chunk of this.getAllAsyncChunks()) {
addChildIdsByOrdersToMap(chunk);
}
return chunkMaps;
}

View File

@ -0,0 +1,22 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const WebpackError = require("./WebpackError");
class CommentCompilationWarning extends WebpackError {
constructor(message, module, loc) {
super(message);
this.name = "CommentCompilationWarning";
this.module = module;
this.loc = loc;
Error.captureStackTrace(this, this.constructor);
}
}
module.exports = CommentCompilationWarning;

View File

@ -487,6 +487,7 @@ class Compilation extends Tapable {
const errorAndCallback = err => {
err.origin = module;
err.dependencies = dependencies;
this.errors.push(err);
if (bail) {
callback(err);
@ -531,7 +532,7 @@ class Compilation extends Tapable {
if (err) {
semaphore.release();
return errorOrWarningAndCallback(
new ModuleNotFoundError(module, err, dependencies)
new ModuleNotFoundError(module, err)
);
}
if (!dependentModule) {

View File

@ -8,10 +8,9 @@ const WebpackError = require("./WebpackError");
class EntryModuleNotFoundError extends WebpackError {
constructor(err) {
super();
super("Entry module not found: " + err);
this.name = "EntryModuleNotFoundError";
this.message = "Entry module not found: " + err;
this.details = err.details;
this.error = err;

View File

@ -8,9 +8,8 @@ const WebpackError = require("./WebpackError");
module.exports = class HarmonyLinkingError extends WebpackError {
/** @param {string} message Error message */
constructor(message) {
super();
super(message);
this.name = "HarmonyLinkingError";
this.message = message;
this.hideStack = true;
Error.captureStackTrace(this, this.constructor);

View File

@ -9,41 +9,38 @@ const { cutOffLoaderExecution } = require("./ErrorHelpers");
class ModuleBuildError extends WebpackError {
constructor(module, err, { from = null } = {}) {
super();
this.name = "ModuleBuildError";
this.message = "Module build failed";
let message = "Module build failed";
let details = undefined;
if (from) {
this.message += ` (from ${from})`;
message += ` (from ${from})`;
}
let message;
message += ": ";
if (err !== null && typeof err === "object") {
if (typeof err.stack === "string" && err.stack) {
const stack = cutOffLoaderExecution(err.stack);
if (!err.hideStack) {
message = stack;
message += stack;
} else {
this.details = stack;
details = stack;
if (typeof err.message === "string" && err.message) {
message = err.message;
message += err.message;
} else {
message = err;
message += err;
}
}
} else if (typeof err.message === "string" && err.message) {
message = err.message;
message += err.message;
} else {
message = err;
message += err;
}
} else {
message = err;
}
if (message !== "") {
this.message += `:\n${message}`;
}
super(message);
this.name = "ModuleBuildError";
this.details = details;
this.module = module;
this.error = err;

View File

@ -5,7 +5,6 @@
"use strict";
const WebpackError = require("./WebpackError");
const formatLocation = require("./formatLocation");
/** @typedef {import("./Module")} Module */
@ -17,16 +16,17 @@ class ModuleDependencyError extends WebpackError {
* @param {TODO} loc location of dependency
*/
constructor(module, err, loc) {
super();
super(err.message);
this.name = "ModuleDependencyError";
this.message = `${formatLocation(loc)} ${err.message}`;
this.details = err.stack
.split("\n")
.slice(1)
.join("\n");
this.origin = this.module = module;
this.module = module;
this.loc = loc;
this.error = err;
this.origin = module.issuer;
Error.captureStackTrace(this, this.constructor);
}

View File

@ -5,20 +5,20 @@
"use strict";
const WebpackError = require("./WebpackError");
const formatLocation = require("./formatLocation");
module.exports = class ModuleDependencyWarning extends WebpackError {
constructor(module, err, loc) {
super();
super(err.message);
this.name = "ModuleDependencyWarning";
this.message = `${formatLocation(loc)} ${err.message}`;
this.details = err.stack
.split("\n")
.slice(1)
.join("\n");
this.origin = this.module = module;
this.module = module;
this.loc = loc;
this.error = err;
this.origin = module.issuer;
Error.captureStackTrace(this, this.constructor);
}

View File

@ -9,19 +9,18 @@ const { cleanUp } = require("./ErrorHelpers");
class ModuleError extends WebpackError {
constructor(module, err, { from = null } = {}) {
super();
this.name = "ModuleError";
this.module = module;
this.message = "Module Error";
let message = "Module Error";
if (from) {
this.message += ` (from ${from})`;
message += ` (from ${from})`;
}
if (err && typeof err === "object" && err.message) {
this.message += `:\n${err.message}`;
message += `:\n${err.message}`;
} else if (err) {
this.message += `:\n${err}`;
message += `:\n${err}`;
}
super(message);
this.name = "ModuleError";
this.module = module;
this.error = err;
this.details =
err && typeof err === "object" && err.stack

View File

@ -7,16 +7,13 @@
const WebpackError = require("./WebpackError");
class ModuleNotFoundError extends WebpackError {
constructor(module, err, dependencies) {
super();
constructor(module, err) {
super("Module not found: " + err);
this.name = "ModuleNotFoundError";
this.message = "Module not found: " + err;
this.details = err.details;
this.missing = err.missing;
this.module = module;
this.origin = module;
this.dependencies = dependencies;
this.error = err;
Error.captureStackTrace(this, this.constructor);

View File

@ -6,14 +6,18 @@
const WebpackError = require("./WebpackError");
class ModuleParseError extends WebpackError {
constructor(module, source, err) {
super();
/** @typedef {import("./Module")} Module */
this.name = "ModuleParseError";
this.message = "Module parse failed: " + err.message;
this.message +=
"\nYou may need an appropriate loader to handle this file type.";
class ModuleParseError extends WebpackError {
/**
* @param {Module} module the errored module
* @param {string} source source code
* @param {Error&any} err the parse error
*/
constructor(module, source, err) {
let message = "Module parse failed: " + err.message;
let loc = undefined;
message += "\nYou may need an appropriate loader to handle this file type.";
if (
err.loc &&
typeof err.loc === "object" &&
@ -22,19 +26,28 @@ class ModuleParseError extends WebpackError {
var lineNumber = err.loc.line;
if (/[\0\u0001\u0002\u0003\u0004\u0005\u0006\u0007]/.test(source)) {
// binary file
this.message += "\n(Source code omitted for this binary file)";
message += "\n(Source code omitted for this binary file)";
} else {
source = source.split("\n");
this.message +=
"\n| " +
source
.slice(Math.max(0, lineNumber - 3), lineNumber + 2)
.join("\n| ");
const sourceLines = source.split("\n");
const start = Math.max(0, lineNumber - 3);
const linesBefore = sourceLines.slice(start, lineNumber - 1);
const theLine = sourceLines[lineNumber - 1];
const linesAfter = sourceLines.slice(lineNumber, lineNumber + 2);
message +=
linesBefore.map(l => `\n| ${l}`).join("") +
`\n> ${theLine}` +
linesAfter.map(l => `\n| ${l}`).join("");
}
loc = err.loc;
} else {
this.message += "\n" + err.stack;
message += "\n" + err.stack;
}
super(message);
this.name = "ModuleParseError";
this.module = module;
this.loc = loc;
this.error = err;
Error.captureStackTrace(this, this.constructor);

View File

@ -9,19 +9,18 @@ const { cleanUp } = require("./ErrorHelpers");
class ModuleWarning extends WebpackError {
constructor(module, warning, { from = null } = {}) {
super();
this.name = "ModuleWarning";
this.module = module;
this.message = "Module Warning";
let message = "Module Warning";
if (from) {
this.message += ` (from ${from})`;
message += ` (from ${from})`;
}
if (warning && typeof warning === "object" && warning.message) {
this.message += `:\n${warning.message}`;
message += `:\n${warning.message}`;
} else if (warning) {
this.message += `:\n${warning}`;
message += `:\n${warning}`;
}
super(message);
this.name = "ModuleWarning";
this.module = module;
this.warning = warning;
this.details =
warning && typeof warning === "object" && warning.stack

View File

@ -43,9 +43,18 @@ const contextify = (context, request) => {
.split("!")
.map(r => {
const splitPath = r.split("?");
splitPath[0] = path.relative(context, splitPath[0]);
if (path.sep === "\\") splitPath[0] = splitPath[0].replace(/\\/g, "/");
if (splitPath[0].indexOf("../") !== 0) splitPath[0] = "./" + splitPath[0];
if (/^[a-zA-Z]:\\/.test(splitPath[0])) {
splitPath[0] = path.win32.relative(context, splitPath[0]);
if (!/^[a-zA-Z]:\\/.test(splitPath[0])) {
splitPath[0] = splitPath[0].replace(/\\/g, "/");
}
}
if (/^\//.test(splitPath[0])) {
splitPath[0] = path.posix.relative(context, splitPath[0]);
}
if (!/^(\.\.\/|\/|[a-zA-Z]:\\)/.test(splitPath[0])) {
splitPath[0] = "./" + splitPath[0];
}
return splitPath.join("?");
})
.join("!");
@ -76,6 +85,7 @@ class NormalModule extends Module {
rawRequest,
loaders,
resource,
matchResource,
parser,
generator,
resolveOptions
@ -90,6 +100,7 @@ class NormalModule extends Module {
this.parser = parser;
this.generator = generator;
this.resource = resource;
this.matchResource = matchResource;
this.loaders = loaders;
if (resolveOptions !== undefined) this.resolveOptions = resolveOptions;
@ -123,16 +134,21 @@ class NormalModule extends Module {
}
nameForCondition() {
const idx = this.resource.indexOf("?");
if (idx >= 0) return this.resource.substr(0, idx);
return this.resource;
const resource = this.matchResource || this.resource;
const idx = resource.indexOf("?");
if (idx >= 0) return resource.substr(0, idx);
return resource;
}
updateCacheModule(module) {
this.type = module.type;
this.request = module.request;
this.userRequest = module.userRequest;
this.rawRequest = module.rawRequest;
this.parser = module.parser;
this.generator = module.generator;
this.resource = module.resource;
this.matchResource = module.matchResource;
this.loaders = module.loaders;
this.resolveOptions = module.resolveOptions;
}

View File

@ -4,6 +4,7 @@
*/
"use strict";
const path = require("path");
const asyncLib = require("neo-async");
const {
Tapable,
@ -20,6 +21,8 @@ const cachedMerge = require("./util/cachedMerge");
const EMPTY_RESOLVE_OPTIONS = {};
const MATCH_RESOURCE_REGEX = /^([^!]+)!=!/;
const loaderToIdent = data => {
if (!data.options) {
return data.loader;
@ -158,19 +161,33 @@ class NormalModuleFactory extends Tapable {
const context = data.context;
const request = data.request;
const noPreAutoLoaders = request.startsWith("-!");
const noAutoLoaders = noPreAutoLoaders || request.startsWith("!");
const noPrePostAutoLoaders = request.startsWith("!!");
let elements = request
const loaderResolver = this.getResolver("loader");
const normalResolver = this.getResolver("normal", data.resolveOptions);
let matchResource = undefined;
let requestWithoutMatchResource = request;
const matchResourceMatch = MATCH_RESOURCE_REGEX.exec(request);
if (matchResourceMatch) {
matchResource = matchResourceMatch[1];
if (/^\.\.?\//.test(matchResource)) {
matchResource = path.join(context, matchResource);
}
requestWithoutMatchResource = request.substr(
matchResourceMatch[0].length
);
}
const noPreAutoLoaders = requestWithoutMatchResource.startsWith("-!");
const noAutoLoaders =
noPreAutoLoaders || requestWithoutMatchResource.startsWith("!");
const noPrePostAutoLoaders = requestWithoutMatchResource.startsWith("!!");
let elements = requestWithoutMatchResource
.replace(/^-?!+/, "")
.replace(/!!+/g, "!")
.split("!");
let resource = elements.pop();
elements = elements.map(identToLoaderRequest);
const loaderResolver = this.getResolver("loader");
const normalResolver = this.getResolver("normal", data.resolveOptions);
asyncLib.parallel(
[
callback =>
@ -234,12 +251,15 @@ class NormalModuleFactory extends Tapable {
);
}
const userRequest = loaders
.map(loaderToIdent)
.concat([resource])
.join("!");
const userRequest =
(matchResource !== undefined ? `${matchResource}!=!` : "") +
loaders
.map(loaderToIdent)
.concat([resource])
.join("!");
let resourcePath = resource;
let resourcePath =
matchResource !== undefined ? matchResource : resource;
let resourceQuery = "";
const queryIndex = resourcePath.indexOf("?");
if (queryIndex >= 0) {
@ -249,6 +269,10 @@ class NormalModuleFactory extends Tapable {
const result = this.ruleSet.exec({
resource: resourcePath,
realResource:
matchResource !== undefined
? resource.replace(/\?.*/, "")
: resourcePath,
resourceQuery,
issuer: contextInfo.issuer,
compiler: contextInfo.compiler
@ -326,6 +350,7 @@ class NormalModuleFactory extends Tapable {
rawRequest: request,
loaders,
resource,
matchResource,
resourceResolveData,
settings,
type,

View File

@ -7,8 +7,8 @@
// Syntax: https://developer.mozilla.org/en/SpiderMonkey/Parser_API
const acorn = require("acorn-dynamic-import").default;
const { Tapable, SyncBailHook } = require("tapable");
const HookMap = require("tapable/lib/HookMap");
const { Tapable, SyncBailHook, HookMap } = require("tapable");
const util = require("util");
const vm = require("vm");
const BasicEvaluatedExpression = require("./BasicEvaluatedExpression");
const StackedSetMap = require("./util/StackedSetMap");
@ -31,6 +31,14 @@ const defaultParserOptions = {
}
};
// regexp to match at lease one "magic comment"
const webpackCommentRegExp = new RegExp(/(^|\W)webpack[A-Z]{1,}[A-Za-z]{1,}:/);
const EMPTY_COMMENT_OPTIONS = {
options: null,
errors: null
};
class Parser extends Tapable {
constructor(options, sourceType = "auto") {
super();
@ -2060,20 +2068,27 @@ class Parser extends Tapable {
);
}
getCommentOptions(range) {
parseCommentOptions(range) {
const comments = this.getComments(range);
if (comments.length === 0) return null;
const options = comments.map(comment => {
try {
let val = vm.runInNewContext(
`(function(){return {${comment.value}};})()`
);
return val;
} catch (e) {
return {};
if (comments.length === 0) {
return EMPTY_COMMENT_OPTIONS;
}
let options = {};
let errors = [];
for (const comment of comments) {
const { value } = comment;
if (value && webpackCommentRegExp.test(value)) {
// try compile only if webpack options comment is present
try {
const val = vm.runInNewContext(`(function(){return {${value}};})()`);
Object.assign(options, val);
} catch (e) {
e.comment = comment;
errors.push(e);
}
}
});
return options.reduce((o, i) => Object.assign(o, i), {});
}
return { options, errors };
}
getNameForExpression(expression) {
@ -2161,4 +2176,12 @@ class Parser extends Tapable {
}
}
// TODO remove in webpack 5
Object.defineProperty(Parser.prototype, "getCommentOptions", {
configurable: false,
value: util.deprecate(function(range) {
return this.parseCommentOptions(range).options;
}, "Parser.getCommentOptions: Use Parser.parseCommentOptions(range) instead")
});
module.exports = Parser;

View File

@ -88,7 +88,7 @@ ParserHelpers.expressionIsUnsupported = (parser, message) => {
parser.state.current.addDependency(dep);
if (!parser.state.module) return;
parser.state.module.warnings.push(
new UnsupportedFeatureWarning(parser.state.module, message)
new UnsupportedFeatureWarning(parser.state.module, message, expr.loc)
);
return true;
};

View File

@ -4,9 +4,7 @@ const WebpackError = require("./WebpackError");
module.exports = class RemovedPluginError extends WebpackError {
constructor(message) {
super();
this.message = message;
super(message);
Error.captureStackTrace(this, this.constructor);
}

View File

@ -10,6 +10,7 @@ const PATH_CHARS_REGEXP = /[-[\]{}()*+?.,\\^$|#\s]/g;
const SEPARATOR_REGEXP = /[/\\]$/;
const FRONT_OR_BACK_BANG_REGEXP = /^!|!$/g;
const INDEX_JS_REGEXP = /\/index.js(!|\?|\(query\))/g;
const MATCH_RESOURCE_REGEXP = /!=!/;
const normalizeBackSlashDirection = request => {
return request.replace(NORMALIZE_SLASH_DIRECTION_REGEXP, "/");
@ -73,6 +74,7 @@ class RequestShortener {
}
result = result.replace(INDEX_JS_REGEXP, "$1");
result = result.replace(FRONT_OR_BACK_BANG_REGEXP, "");
result = result.replace(MATCH_RESOURCE_REGEXP, " = ");
this.cache.set(request, result);
return result;
}

View File

@ -203,6 +203,14 @@ module.exports = class RuleSet {
}
}
if (rule.realResource) {
try {
newRule.realResource = RuleSet.normalizeCondition(rule.realResource);
} catch (error) {
throw new Error(RuleSet.buildErrorMessage(rule.realResource, error));
}
}
if (rule.resourceQuery) {
try {
newRule.resourceQuery = RuleSet.normalizeCondition(rule.resourceQuery);
@ -477,10 +485,13 @@ module.exports = class RuleSet {
_run(data, rule, result) {
// test conditions
if (rule.resource && !data.resource) return false;
if (rule.realResource && !data.realResource) return false;
if (rule.resourceQuery && !data.resourceQuery) return false;
if (rule.compiler && !data.compiler) return false;
if (rule.issuer && !data.issuer) return false;
if (rule.resource && !rule.resource(data.resource)) return false;
if (rule.realResource && !rule.realResource(data.realResource))
return false;
if (data.issuer && rule.issuer && !rule.issuer(data.issuer)) return false;
if (
data.resourceQuery &&
@ -497,6 +508,7 @@ module.exports = class RuleSet {
const keys = Object.keys(rule).filter(key => {
return ![
"resource",
"realResource",
"resourceQuery",
"compiler",
"issuer",

View File

@ -56,8 +56,8 @@ class Stats {
formatFilePath(filePath) {
const OPTIONS_REGEXP = /^(\s|\S)*!/;
return filePath.includes("!")
? `${filePath.replace(OPTIONS_REGEXP, "")} (${filePath})\n`
: `${filePath}\n`;
? `${filePath.replace(OPTIONS_REGEXP, "")} (${filePath})`
: `${filePath}`;
}
hasWarnings() {
@ -288,6 +288,11 @@ class Stats {
text += this.formatFilePath(
e.module.readableIdentifier(requestShortener)
);
if (typeof e.loc === "object") {
const locInfo = formatLocation(e.loc);
if (locInfo) text += ` ${locInfo}`;
}
text += "\n";
}
text += e.message;
if (showErrorDetails && e.details) {
@ -297,7 +302,9 @@ class Stats {
text += e.missing.map(item => `\n[${item}]`).join("");
}
if (showModuleTrace && e.origin) {
text += `\n @ ${e.origin.readableIdentifier(requestShortener)}`;
text += `\n @ ${this.formatFilePath(
e.origin.readableIdentifier(requestShortener)
)}`;
if (typeof e.originLoc === "object") {
const locInfo = formatLocation(e.originLoc);
if (locInfo) text += ` ${locInfo}`;

View File

@ -7,12 +7,13 @@
const WebpackError = require("./WebpackError");
class UnsupportedFeatureWarning extends WebpackError {
constructor(module, message) {
super();
constructor(module, message, loc) {
super(message);
this.name = "UnsupportedFeatureWarning";
this.message = message;
this.origin = this.module = module;
this.module = module;
this.loc = loc;
this.hideStack = true;
Error.captureStackTrace(this, this.constructor);
}

View File

@ -58,6 +58,7 @@ const NamedModulesPlugin = require("./NamedModulesPlugin");
const NamedChunksPlugin = require("./NamedChunksPlugin");
const DefinePlugin = require("./DefinePlugin");
const SizeLimitsPlugin = require("./performance/SizeLimitsPlugin");
const WasmFinalizeExportsPlugin = require("./wasm/WasmFinalizeExportsPlugin");
class WebpackOptionsApply extends OptionsApply {
constructor() {
@ -339,6 +340,9 @@ class WebpackOptionsApply extends OptionsApply {
if (options.optimization.noEmitOnErrors) {
new NoEmitOnErrorsPlugin().apply(compiler);
}
if (options.optimization.checkWasmTypes) {
new WasmFinalizeExportsPlugin().apply(compiler);
}
if (options.optimization.namedModules) {
new NamedModulesPlugin().apply(compiler);
}

View File

@ -254,6 +254,9 @@ class WebpackOptionsDefaulter extends OptionsDefaulter {
this.set("optimization.noEmitOnErrors", "make", options =>
isProductionLikeMode(options)
);
this.set("optimization.checkWasmTypes", "make", options =>
isProductionLikeMode(options)
);
this.set(
"optimization.namedModules",
"make",
@ -294,11 +297,10 @@ class WebpackOptionsDefaulter extends OptionsDefaulter {
}
}
]);
this.set(
"optimization.nodeEnv",
"make",
options => options.mode || "production"
);
this.set("optimization.nodeEnv", "make", options => {
// TODO: In webpack 5, it should return `false` when mode is `none`
return options.mode || "production";
});
this.set("resolve", "call", value => Object.assign({}, value));
this.set("resolve.unsafeCache", true);

View File

@ -69,23 +69,23 @@ const indent = (str, prefix, firstLine) => {
class WebpackOptionsValidationError extends WebpackError {
constructor(validationErrors) {
super();
super(
"Invalid configuration object. " +
"Webpack has been initialised using a configuration object that does not match the API schema.\n" +
validationErrors
.map(
err =>
" - " +
indent(
WebpackOptionsValidationError.formatValidationError(err),
" ",
false
)
)
.join("\n")
);
this.name = "WebpackOptionsValidationError";
this.message =
"Invalid configuration object. " +
"Webpack has been initialised using a configuration object that does not match the API schema.\n" +
validationErrors
.map(
err =>
" - " +
indent(
WebpackOptionsValidationError.formatValidationError(err),
" ",
false
)
)
.join("\n");
this.validationErrors = validationErrors;
Error.captureStackTrace(this, this.constructor);

View File

@ -219,7 +219,8 @@ class AMDRequireDependenciesBlockParserPlugin {
new UnsupportedFeatureWarning(
parser.state.module,
"Cannot statically analyse 'require(…, …)' in line " +
expr.loc.start.line
expr.loc.start.line,
expr.loc
)
);
}

View File

@ -10,6 +10,7 @@ const ImportDependenciesBlock = require("./ImportDependenciesBlock");
const ImportEagerDependency = require("./ImportEagerDependency");
const ContextDependencyHelpers = require("./ContextDependencyHelpers");
const UnsupportedFeatureWarning = require("../UnsupportedFeatureWarning");
const CommentCompilationWarning = require("../CommentCompilationWarning");
class ImportParserPlugin {
constructor(options) {
@ -32,7 +33,26 @@ class ImportParserPlugin {
let exclude = null;
const groupOptions = {};
const importOptions = parser.getCommentOptions(expr.range);
const {
options: importOptions,
errors: commentErrors
} = parser.parseCommentOptions(expr.range);
if (commentErrors) {
for (const e of commentErrors) {
const { comment } = e;
parser.state.module.warnings.push(
new CommentCompilationWarning(
`Compilation error while processing magic comment(-s): /*${
comment.value
}*/: ${e.message}`,
parser.state.module,
comment.loc
)
);
}
}
if (importOptions) {
if (typeof importOptions.webpackIgnore !== "undefined") {
if (typeof importOptions.webpackIgnore !== "boolean") {
@ -41,7 +61,8 @@ class ImportParserPlugin {
parser.state.module,
`\`webpackIgnore\` expected a boolean, but received: ${
importOptions.webpackIgnore
}.`
}.`,
expr.loc
)
);
} else {
@ -58,7 +79,8 @@ class ImportParserPlugin {
parser.state.module,
`\`webpackChunkName\` expected a string, but received: ${
importOptions.webpackChunkName
}.`
}.`,
expr.loc
)
);
} else {
@ -72,7 +94,8 @@ class ImportParserPlugin {
parser.state.module,
`\`webpackMode\` expected a string, but received: ${
importOptions.webpackMode
}.`
}.`,
expr.loc
)
);
} else {
@ -90,7 +113,8 @@ class ImportParserPlugin {
parser.state.module,
`\`webpackPrefetch\` expected true or a number, but received: ${
importOptions.webpackPrefetch
}.`
}.`,
expr.loc
)
);
}
@ -106,7 +130,8 @@ class ImportParserPlugin {
parser.state.module,
`\`webpackPreload\` expected true or a number, but received: ${
importOptions.webpackPreload
}.`
}.`,
expr.loc
)
);
}
@ -121,7 +146,8 @@ class ImportParserPlugin {
parser.state.module,
`\`webpackInclude\` expected a regular expression, but received: ${
importOptions.webpackInclude
}.`
}.`,
expr.loc
)
);
} else {
@ -138,7 +164,8 @@ class ImportParserPlugin {
parser.state.module,
`\`webpackExclude\` expected a regular expression, but received: ${
importOptions.webpackExclude
}.`
}.`,
expr.loc
)
);
} else {
@ -152,7 +179,8 @@ class ImportParserPlugin {
parser.state.module.warnings.push(
new UnsupportedFeatureWarning(
parser.state.module,
`\`webpackMode\` expected 'lazy', 'eager' or 'weak', but received: ${mode}.`
`\`webpackMode\` expected 'lazy', 'eager' or 'weak', but received: ${mode}.`,
expr.loc
)
);
}
@ -195,7 +223,8 @@ class ImportParserPlugin {
parser.state.module.warnings.push(
new UnsupportedFeatureWarning(
parser.state.module,
`\`webpackMode\` expected 'lazy', 'lazy-once', 'eager' or 'weak', but received: ${mode}.`
`\`webpackMode\` expected 'lazy', 'lazy-once', 'eager' or 'weak', but received: ${mode}.`,
expr.loc
)
);
mode = "lazy";
@ -230,4 +259,5 @@ class ImportParserPlugin {
});
}
}
module.exports = ImportParserPlugin;

View File

@ -108,15 +108,15 @@ class SystemPlugin {
class SystemImportDeprecationWarning extends WebpackError {
constructor(module, loc) {
super();
super(
"System.import() is deprecated and will be removed soon. Use import() instead.\n" +
"For more info visit https://webpack.js.org/guides/code-splitting/"
);
this.name = "SystemImportDeprecationWarning";
this.message =
"System.import() is deprecated and will be removed soon. Use import() instead.\n" +
"For more info visit https://webpack.js.org/guides/code-splitting/";
this.origin = this.module = module;
this.originLoc = loc;
this.module = module;
this.loc = loc;
Error.captureStackTrace(this, this.constructor);
}

View File

@ -0,0 +1,29 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const DependencyReference = require("./DependencyReference");
const ModuleDependency = require("./ModuleDependency");
class WebAssemblyExportImportedDependency extends ModuleDependency {
constructor(exportName, request, name) {
super(request);
/** @type {string} */
this.exportName = exportName;
/** @type {string} */
this.name = name;
}
getReference() {
if (!this.module) return null;
return new DependencyReference(this.module, [this.name], false);
}
get type() {
return "wasm export import";
}
}
module.exports = WebAssemblyExportImportedDependency;

View File

@ -32,7 +32,7 @@ class WebAssemblyImportDependency extends ModuleDependency {
) {
return [
new UnsupportedWebAssemblyFeatureError(
`Import with ${
`Import "${this.name}" from "${this.request}" with ${
this.onlyDirectImport
} can only be used for direct wasm to wasm dependencies`
)

View File

@ -9,21 +9,21 @@ const SizeFormatHelpers = require("../SizeFormatHelpers");
module.exports = class AssetsOverSizeLimitWarning extends WebpackError {
constructor(assetsOverSizeLimit, assetLimit) {
super();
this.name = "AssetsOverSizeLimitWarning";
this.assets = assetsOverSizeLimit;
const assetLists = this.assets
const assetLists = assetsOverSizeLimit
.map(
asset =>
`\n ${asset.name} (${SizeFormatHelpers.formatSize(asset.size)})`
)
.join("");
this.message = `asset size limit: The following asset(s) exceed the recommended size limit (${SizeFormatHelpers.formatSize(
super(`asset size limit: The following asset(s) exceed the recommended size limit (${SizeFormatHelpers.formatSize(
assetLimit
)}).
This can impact web performance.
Assets: ${assetLists}`;
Assets: ${assetLists}`);
this.name = "AssetsOverSizeLimitWarning";
this.assets = assetsOverSizeLimit;
Error.captureStackTrace(this, this.constructor);
}

View File

@ -9,11 +9,7 @@ const SizeFormatHelpers = require("../SizeFormatHelpers");
module.exports = class EntrypointsOverSizeLimitWarning extends WebpackError {
constructor(entrypoints, entrypointLimit) {
super();
this.name = "EntrypointsOverSizeLimitWarning";
this.entrypoints = entrypoints;
const entrypointList = this.entrypoints
const entrypointList = entrypoints
.map(
entrypoint =>
`\n ${entrypoint.name} (${SizeFormatHelpers.formatSize(
@ -21,10 +17,13 @@ module.exports = class EntrypointsOverSizeLimitWarning extends WebpackError {
)})\n${entrypoint.files.map(asset => ` ${asset}`).join("\n")}`
)
.join("");
this.message = `entrypoint size limit: The following entrypoint(s) combined asset size exceeds the recommended limit (${SizeFormatHelpers.formatSize(
super(`entrypoint size limit: The following entrypoint(s) combined asset size exceeds the recommended limit (${SizeFormatHelpers.formatSize(
entrypointLimit
)}). This can impact web performance.
Entrypoints:${entrypointList}\n`;
Entrypoints:${entrypointList}\n`);
this.name = "EntrypointsOverSizeLimitWarning";
this.entrypoints = entrypoints;
Error.captureStackTrace(this, this.constructor);
}

View File

@ -8,13 +8,13 @@ const WebpackError = require("../WebpackError");
module.exports = class NoAsyncChunksWarning extends WebpackError {
constructor() {
super();
super(
"webpack performance recommendations: \n" +
"You can limit the size of your bundles by using import() or require.ensure to lazy load some parts of your application.\n" +
"For more info visit https://webpack.js.org/guides/code-splitting/"
);
this.name = "NoAsyncChunksWarning";
this.message =
"webpack performance recommendations: \n" +
"You can limit the size of your bundles by using import() or require.ensure to lazy load some parts of your application.\n" +
"For more info visit https://webpack.js.org/guides/code-splitting/";
Error.captureStackTrace(this, this.constructor);
}

View File

@ -8,9 +8,8 @@ const WebpackError = require("../WebpackError");
module.exports = class UnsupportedWebAssemblyFeatureError extends WebpackError {
/** @param {string} message Error message */
constructor(message) {
super();
super(message);
this.name = "UnsupportedWebAssemblyFeatureError";
this.message = message;
this.hideStack = true;
Error.captureStackTrace(this, this.constructor);

View File

@ -0,0 +1,66 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const UnsupportedWebAssemblyFeatureError = require("../wasm/UnsupportedWebAssemblyFeatureError");
class WasmFinalizeExportsPlugin {
apply(compiler) {
compiler.hooks.compilation.tap("WasmFinalizeExportsPlugin", compilation => {
compilation.hooks.finishModules.tap(
"WasmFinalizeExportsPlugin",
modules => {
for (const module of modules) {
// 1. if a WebAssembly module
if (module.type.startsWith("webassembly") === true) {
const jsIncompatibleExports =
module.buildMeta.jsIncompatibleExports;
if (jsIncompatibleExports === undefined) {
continue;
}
for (const reason of module.reasons) {
// 2. is referenced by a non-WebAssembly module
if (reason.module.type.startsWith("webassembly") === false) {
const ref = reason.dependency.getReference();
const importedNames = ref.importedNames;
if (Array.isArray(importedNames)) {
importedNames.forEach(name => {
// 3. and uses a func with an incompatible JS signature
if (
Object.prototype.hasOwnProperty.call(
jsIncompatibleExports,
name
)
) {
// 4. error
/** @type {any} */
const error = new UnsupportedWebAssemblyFeatureError(
`Export "${name}" with ${
jsIncompatibleExports[name]
} can only be used for direct wasm to wasm dependencies`
);
error.module = module;
error.origin = reason.module;
error.originLoc = reason.dependency.loc;
error.dependencies = [reason.dependency];
compilation.errors.push(error);
}
});
}
}
}
}
}
}
);
});
}
}
module.exports = WasmFinalizeExportsPlugin;

View File

@ -14,6 +14,8 @@ const { editWithAST, addWithAST } = require("@webassemblyjs/wasm-edit");
const { decode } = require("@webassemblyjs/wasm-parser");
const t = require("@webassemblyjs/ast");
const WebAssemblyExportImportedDependency = require("../dependencies/WebAssemblyExportImportedDependency");
/** @typedef {import("../Module")} Module */
/** @typedef {import("./WebAssemblyUtils").UsedWasmDependency} UsedWasmDependency */
@ -129,7 +131,7 @@ function getCountImportedFunc(ast) {
* Get next type index
*
* @param {Object} ast - Module's AST
* @returns {t.IndexLiteral} - index
* @returns {t.Index} - index
*/
function getNextTypeIndex(ast) {
const typeSectionMetadata = t.getSectionMetadata(ast, "type");
@ -150,7 +152,7 @@ function getNextTypeIndex(ast) {
*
* @param {Object} ast - Module's AST
* @param {Number} countImportedFunc - number of imported funcs
* @returns {t.IndexLiteral} - index
* @returns {t.Index} - index
*/
function getNextFuncIndex(ast, countImportedFunc) {
const funcSectionMetadata = t.getSectionMetadata(ast, "func");
@ -164,6 +166,27 @@ function getNextFuncIndex(ast, countImportedFunc) {
return t.indexLiteral(vectorOfSize + countImportedFunc);
}
/**
* Create a init instruction for a global
* @param {t.GlobalType} globalType the global type
* @returns {t.Instruction} init expression
*/
const createDefaultInitForGlobal = globalType => {
if (globalType.valtype[0] === "i") {
// create NumberLiteral global initializer
return t.objectInstruction("const", globalType.valtype, [
t.numberLiteralFromRaw(66)
]);
} else if (globalType.valtype[0] === "f") {
// create FloatLiteral global initializer
return t.objectInstruction("const", globalType.valtype, [
t.floatLiteral(66, false, false, "66")
]);
} else {
throw new Error("unknown type: " + globalType.valtype);
}
};
/**
* Rewrite the import globals:
* - removes the ModuleImport instruction
@ -188,21 +211,7 @@ const rewriteImportedGlobals = state => bin => {
globalType.mutability = "var";
let init;
if (globalType.valtype[0] === "i") {
// create NumberLiteral global initializer
init = t.objectInstruction("const", globalType.valtype, [
t.numberLiteralFromRaw(0)
]);
} else if (globalType.valtype[0] === "f") {
// create FloatLiteral global initializer
init = t.objectInstruction("const", globalType.valtype, [
t.floatLiteral(0, false, false, "0")
]);
} else {
throw new Error("unknown type: " + globalType.valtype);
}
const init = createDefaultInitForGlobal(globalType);
newGlobals.push(t.global(globalType, [init]));
@ -221,12 +230,7 @@ const rewriteImportedGlobals = state => bin => {
const initialGlobalidx = init.args[0];
const valtype = node.globalType.valtype;
node.init = [
// Poisong globals, they are meant to be rewritten
t.objectInstruction("const", valtype, [t.numberLiteralFromRaw(666)])
];
node.init = [createDefaultInitForGlobal(node.globalType)];
additionalInitCode.push(
/**
@ -253,18 +257,24 @@ const rewriteImportedGlobals = state => bin => {
* Rewrite the export names
* @param {Object} state state
* @param {Object} state.ast Module's ast
* @param {Object} state.module Module
* @param {Module} state.module Module
* @param {Set<string>} state.externalExports Module
* @returns {ArrayBufferTransform} transform
*/
const rewriteExportNames = ({ ast, module }) => bin => {
const rewriteExportNames = ({ ast, module, externalExports }) => bin => {
return editWithAST(ast, bin, {
ModuleExport(path) {
const usedName = module.isUsed(path.node.name);
if (usedName) {
path.node.name = usedName;
} else {
const isExternal = externalExports.has(path.node.name);
if (isExternal) {
path.remove();
return;
}
const usedName = module.isUsed(path.node.name);
if (!usedName) {
path.remove();
return;
}
path.node.name = usedName;
}
});
};
@ -282,9 +292,8 @@ const rewriteImports = ({ ast, usedDependencyMap }) => bin => {
const result = usedDependencyMap.get(
path.node.module + ":" + path.node.name
);
if (result === undefined) {
path.remove();
} else {
if (typeof result !== "undefined") {
path.node.module = WebAssemblyUtils.MANGLED_MODULE;
path.node.name = result.name;
}
@ -300,11 +309,11 @@ const rewriteImports = ({ ast, usedDependencyMap }) => bin => {
* @param {Object} state transformation state
* @param {Object} state.ast - Module's ast
* @param {t.Identifier} state.initFuncId identifier of the init function
* @param {t.IndexLiteral} state.startAtFuncIndex index of the start function
* @param {t.Index} state.startAtFuncIndex index of the start function
* @param {t.ModuleImport[]} state.importedGlobals list of imported globals
* @param {t.Instruction[]} state.additionalInitCode list of addition instructions for the init function
* @param {t.IndexLiteral} state.nextFuncIndex index of the next function
* @param {t.IndexLiteral} state.nextTypeIndex index of the next type
* @param {t.Index} state.nextFuncIndex index of the next function
* @param {t.Index} state.nextTypeIndex index of the next type
* @returns {ArrayBufferTransform} transform
*/
const addInitFunction = ({
@ -403,6 +412,11 @@ class WebAssemblyGenerator extends Generator {
const nextTypeIndex = getNextTypeIndex(ast);
const usedDependencyMap = getUsedDependencyMap(module);
const externalExports = new Set(
module.dependencies
.filter(d => d instanceof WebAssemblyExportImportedDependency)
.map(d => d.exportName)
);
/** @type {t.Instruction[]} */
const additionalInitCode = [];
@ -410,7 +424,8 @@ class WebAssemblyGenerator extends Generator {
const transform = compose(
rewriteExportNames({
ast,
module
module,
externalExports
}),
removeStartFunc({ ast }),

View File

@ -8,34 +8,7 @@ const Generator = require("../Generator");
const Template = require("../Template");
const { RawSource } = require("webpack-sources");
const WebAssemblyImportDependency = require("../dependencies/WebAssemblyImportDependency");
function generateInitParams(module) {
const list = [];
for (const dep of module.dependencies) {
if (dep instanceof WebAssemblyImportDependency) {
if (dep.description.type === "GlobalType") {
const exportName = dep.name;
const usedName = dep.module && dep.module.isUsed(exportName);
if (dep.module === null) {
// Dependency was not found, an error will be thrown later
continue;
}
if (usedName !== false) {
list.push(
`__webpack_require__(${JSON.stringify(
dep.module.id
)})[${JSON.stringify(usedName)}]`
);
}
}
}
}
return list;
}
const WebAssemblyExportImportedDependency = require("../dependencies/WebAssemblyExportImportedDependency");
class WebAssemblyJavascriptGenerator extends Generator {
generate(module, dependencyTemplates, runtimeTemplate) {
@ -43,24 +16,88 @@ class WebAssemblyJavascriptGenerator extends Generator {
? Template.numberToIdentifer(module.usedExports.length)
: "__webpack_init__";
const generateImports = () => {
const modules = new Map();
for (const dep of module.dependencies) {
if (dep.module) modules.set(dep.module, dep.userRequest);
}
return Template.asString(
Array.from(modules, ([m, r]) => {
return `${runtimeTemplate.moduleRaw({
module: m,
request: r
})};`;
})
);
};
let needExportsCopy = false;
const importedModules = new Map();
const initParams = [];
let index = 0;
for (const dep of module.dependencies) {
if (dep.module) {
let importData = importedModules.get(dep.module);
if (importData === undefined) {
importedModules.set(
dep.module,
(importData = {
importVar: `m${index}`,
index,
request: dep.userRequest,
names: new Set(),
reexports: []
})
);
index++;
}
if (dep instanceof WebAssemblyImportDependency) {
importData.names.add(dep.name);
if (dep.description.type === "GlobalType") {
const exportName = dep.name;
const usedName = dep.module && dep.module.isUsed(exportName);
// FIXME(sven): assert that the exports exists in the modules
// otherwise it will default to i32 0
const initParams = generateInitParams(module).join(",");
if (dep.module) {
if (usedName) {
initParams.push(
runtimeTemplate.exportFromImport({
module: dep.module,
request: dep.request,
importVar: importData.importVar,
originModule: module,
exportName: dep.name,
asiSafe: true,
isCall: false,
callContext: null
})
);
}
}
}
}
if (dep instanceof WebAssemblyExportImportedDependency) {
importData.names.add(dep.name);
const usedName = module.isUsed(dep.exportName);
if (usedName) {
const defineStatement = Template.asString([
`${module.exportsArgument}[${JSON.stringify(
usedName
)}] = ${runtimeTemplate.exportFromImport({
module: dep.module,
request: dep.request,
importVar: importData.importVar,
originModule: module,
exportName: dep.name,
asiSafe: true,
isCall: false,
callContext: null
})};`
]);
importData.reexports.push(defineStatement);
needExportsCopy = true;
}
}
}
}
const importsCode = Template.asString(
Array.from(
importedModules,
([module, { importVar, request, reexports }]) => {
const importStatement = runtimeTemplate.importStatement({
module,
request,
importVar,
originModule: module
});
return importStatement + reexports.join("\n");
}
)
);
// create source
const source = new RawSource(
@ -69,18 +106,24 @@ class WebAssemblyJavascriptGenerator extends Generator {
"// Instantiate WebAssembly module",
"var wasmExports = __webpack_require__.w[module.i];",
!Array.isArray(module.usedExports)
? `__webpack_require__.r(${module.exportsArgument});`
: "",
// this must be before import for circular dependencies
"// export exports from WebAssembly module",
Array.isArray(module.usedExports)
Array.isArray(module.usedExports) && !needExportsCopy
? `${module.moduleArgument}.exports = wasmExports;`
: "for(var name in wasmExports) " +
`if(name != ${JSON.stringify(initIdentifer)}) ` +
`${module.exportsArgument}[name] = wasmExports[name];`,
"// exec imports from WebAssembly module (for esm order)",
generateImports(),
importsCode,
"",
"// exec wasm module",
`wasmExports[${JSON.stringify(initIdentifer)}](${initParams})`
`wasmExports[${JSON.stringify(initIdentifer)}](${initParams.join(
", "
)})`
].join("\n")
);
return source;

View File

@ -9,6 +9,7 @@ const WebAssemblyParser = require("./WebAssemblyParser");
const WebAssemblyGenerator = require("./WebAssemblyGenerator");
const WebAssemblyJavascriptGenerator = require("./WebAssemblyJavascriptGenerator");
const WebAssemblyImportDependency = require("../dependencies/WebAssemblyImportDependency");
const WebAssemblyExportImportedDependency = require("../dependencies/WebAssemblyExportImportedDependency");
class WebAssemblyModulesPlugin {
apply(compiler) {
@ -20,6 +21,11 @@ class WebAssemblyModulesPlugin {
normalModuleFactory
);
compilation.dependencyFactories.set(
WebAssemblyExportImportedDependency,
normalModuleFactory
);
normalModuleFactory.hooks.createParser
.for("webassembly/experimental")
.tap("WebAssemblyModulesPlugin", () => {

View File

@ -6,9 +6,13 @@
const t = require("@webassemblyjs/ast");
const { decode } = require("@webassemblyjs/wasm-parser");
const {
moduleContextFromModuleAST
} = require("@webassemblyjs/helper-module-context");
const { Tapable } = require("tapable");
const WebAssemblyImportDependency = require("../dependencies/WebAssemblyImportDependency");
const WebAssemblyExportImportedDependency = require("../dependencies/WebAssemblyExportImportedDependency");
/** @typedef {import("../Module")} Module */
@ -24,18 +28,29 @@ const isMemoryImport = n => n.descr.type === "Memory";
*/
const isTableImport = n => n.descr.type === "Table";
/**
* @param {t.ModuleImport} n the import
* @returns {boolean} true, if a func was imported
*/
const isFuncImport = n => n.descr.type === "FuncImportDescr";
/**
* @param {t.ModuleImport} n the import
* @returns {boolean} true, if a global was imported
*/
const isGlobalImport = n => n.descr.type === "GlobalType";
const JS_COMPAT_TYPES = new Set(["i32", "f32", "f64"]);
/**
* @param {t.ModuleImport} moduleImport the import
* @param {t.Signature} signature the func signature
* @returns {null | string} the type incompatible with js types
*/
const getJsIncompatibleType = moduleImport => {
if (moduleImport.descr.type !== "FuncImportDescr") return null;
const signature = moduleImport.descr.signature;
const getJsIncompatibleType = signature => {
for (const param of signature.params) {
if (!JS_COMPAT_TYPES.has(param.valtype))
if (!JS_COMPAT_TYPES.has(param.valtype)) {
return `${param.valtype} as parameter`;
}
}
for (const type of signature.results) {
if (!JS_COMPAT_TYPES.has(type)) return `${type} as result`;
@ -43,9 +58,29 @@ const getJsIncompatibleType = moduleImport => {
return null;
};
/**
* TODO why are there two different Signature types?
* @param {t.FuncSignature} signature the func signature
* @returns {null | string} the type incompatible with js types
*/
const getJsIncompatibleTypeOfFuncSignature = signature => {
for (const param of signature.args) {
if (!JS_COMPAT_TYPES.has(param)) {
return `${param} as parameter`;
}
}
for (const type of signature.result) {
if (!JS_COMPAT_TYPES.has(type)) return `${type} as result`;
}
return null;
};
const decoderOpts = {
ignoreCodeSection: true,
ignoreDataSection: true
ignoreDataSection: true,
// this will avoid having to lookup with identifiers in the ModuleContext
ignoreCustomNameSection: true
};
class WebAssemblyParser extends Tapable {
@ -60,13 +95,65 @@ class WebAssemblyParser extends Tapable {
state.module.buildMeta.exportsType = "namespace";
// parse it
const ast = decode(binary, decoderOpts);
const program = decode(binary, decoderOpts);
const module = program.body[0];
const moduleContext = moduleContextFromModuleAST(module);
// extract imports and exports
const exports = (state.module.buildMeta.providedExports = []);
t.traverse(ast, {
const jsIncompatibleExports = (state.module.buildMeta.jsIncompatibleExports = []);
const importedGlobals = [];
t.traverse(module, {
ModuleExport({ node }) {
const descriptor = node.descr;
if (descriptor.exportType === "Func") {
const funcidx = descriptor.id.value;
/** @type {t.FuncSignature} */
const funcSignature = moduleContext.getFunction(funcidx);
const incompatibleType = getJsIncompatibleTypeOfFuncSignature(
funcSignature
);
if (incompatibleType) {
jsIncompatibleExports[node.name] = incompatibleType;
}
}
exports.push(node.name);
if (node.descr && node.descr.exportType === "Global") {
const refNode = importedGlobals[node.descr.id.value];
if (refNode) {
const dep = new WebAssemblyExportImportedDependency(
node.name,
refNode.module,
refNode.name
);
state.module.addDependency(dep);
}
}
},
Global({ node }) {
const init = node.init[0];
let importNode = null;
if (init.id === "get_global") {
const globalIdx = init.args[0].value;
if (globalIdx < importedGlobals.length) {
importNode = importedGlobals[globalIdx];
}
}
importedGlobals.push(importNode);
},
ModuleImport({ node }) {
@ -77,11 +164,16 @@ class WebAssemblyParser extends Tapable {
onlyDirectImport = "Memory";
} else if (isTableImport(node) === true) {
onlyDirectImport = "Table";
} else {
const incompatibleType = getJsIncompatibleType(node);
} else if (isFuncImport(node) === true) {
const incompatibleType = getJsIncompatibleType(node.descr.signature);
if (incompatibleType) {
onlyDirectImport = `Non-JS-compatible Func Sigurature (${incompatibleType})`;
}
} else if (isGlobalImport(node) === true) {
const type = node.descr.valtype;
if (!JS_COMPAT_TYPES.has(type)) {
onlyDirectImport = `Non-JS-compatible Global Type (${type})`;
}
}
const dep = new WebAssemblyImportDependency(
@ -92,6 +184,10 @@ class WebAssemblyParser extends Tapable {
);
state.module.addDependency(dep);
if (isGlobalImport(node)) {
importedGlobals.push(node);
}
}
});

View File

@ -14,6 +14,7 @@ class JsonpChunkTemplatePlugin {
const jsonpFunction = chunkTemplate.outputOptions.jsonpFunction;
const globalObject = chunkTemplate.outputOptions.globalObject;
const source = new ConcatSource();
const prefetchChunks = chunk.getChildIdsByOrders().prefetch;
source.add(
`(${globalObject}[${JSON.stringify(
jsonpFunction
@ -31,6 +32,12 @@ class JsonpChunkTemplatePlugin {
);
if (entries.length > 0) {
source.add(`,${JSON.stringify(entries)}`);
} else if (prefetchChunks && prefetchChunks.length) {
source.add(`,0`);
}
if (prefetchChunks && prefetchChunks.length) {
source.add(`,${JSON.stringify(prefetchChunks)}`);
}
source.add("])");
return source;

View File

@ -28,6 +28,10 @@ class JsonpMainTemplatePlugin {
}
return false;
};
const needPrefetchingCode = chunk => {
const allPrefetchChunks = chunk.getChildIdsByOrdersMap(true).prefetch;
return allPrefetchChunks && Object.keys(allPrefetchChunks).length;
};
// TODO refactor this
if (!mainTemplate.hooks.jsonpScript) {
mainTemplate.hooks.jsonpScript = new SyncWaterfallHook([
@ -232,9 +236,21 @@ class JsonpMainTemplatePlugin {
mainTemplate.hooks.linkPrefetch.tap(
"JsonpMainTemplatePlugin",
(_, chunk, hash) => {
const crossOriginLoading =
mainTemplate.outputOptions.crossOriginLoading;
return Template.asString([
"var link = document.createElement('link');",
crossOriginLoading
? `link.crossOrigin = ${JSON.stringify(crossOriginLoading)};`
: "",
`if (${mainTemplate.requireFn}.nc) {`,
Template.indent(
`link.setAttribute("nonce", ${mainTemplate.requireFn}.nc);`
),
"}",
'link.rel = "prefetch";',
'link.as = "script";',
"link.href = jsonpScriptSrc(chunkId);"
]);
}
@ -288,7 +304,7 @@ class JsonpMainTemplatePlugin {
"",
"// chunk preloadng for javascript",
"",
`var chunkPreloadMap = ${JSON.stringify(chunkMap, null, "\t")}`,
`var chunkPreloadMap = ${JSON.stringify(chunkMap, null, "\t")};`,
"",
"var chunkPreloadData = chunkPreloadMap[chunkId];",
"if(chunkPreloadData) {",
@ -310,45 +326,6 @@ class JsonpMainTemplatePlugin {
]);
}
);
mainTemplate.hooks.requireEnsure.tap(
{
name: "JsonpMainTemplatePlugin prefetch",
stage: 20
},
(source, chunk, hash) => {
const chunkMap = chunk.getChildIdsByOrdersMap().prefetch;
if (!chunkMap || Object.keys(chunkMap).length === 0) return source;
return Template.asString([
source,
"",
"// chunk prefetching for javascript",
"",
`var chunkPrefetchMap = ${JSON.stringify(chunkMap, null, "\t")}`,
"",
"var chunkPrefetchData = chunkPrefetchMap[chunkId];",
"if(chunkPrefetchData) {",
Template.indent([
"Promise.all(promises).then(function() {",
Template.indent([
"var head = document.getElementsByTagName('head')[0];",
"chunkPrefetchData.forEach(function(chunkId) {",
Template.indent([
"if(installedChunks[chunkId] === undefined) {",
Template.indent([
"installedChunks[chunkId] = null;",
mainTemplate.hooks.linkPrefetch.call("", chunk, hash),
"head.appendChild(link);"
]),
"}"
]),
"});"
]),
"})"
]),
"}"
]);
}
);
mainTemplate.hooks.requireExtensions.tap(
"JsonpMainTemplatePlugin",
(source, chunk) => {
@ -369,6 +346,7 @@ class JsonpMainTemplatePlugin {
(source, chunk, hash) => {
if (needChunkLoadingCode(chunk)) {
const withDefer = needEntryDeferringCode(chunk);
const withPrefetch = needPrefetchingCode(chunk);
return Template.asString([
source,
"",
@ -378,6 +356,7 @@ class JsonpMainTemplatePlugin {
"var chunkIds = data[0];",
"var moreModules = data[1];",
withDefer ? "var executeModules = data[2];" : "",
withPrefetch ? "var prefetchChunks = data[3] || [];" : "",
'// add "moreModules" to the modules object,',
'// then flag all "chunkIds" as loaded and fire callback',
"var moduleId, chunkId, i = 0, resolves = [];",
@ -405,6 +384,23 @@ class JsonpMainTemplatePlugin {
]),
"}",
"if(parentJsonpFunction) parentJsonpFunction(data);",
withPrefetch
? Template.asString([
"// chunk prefetching for javascript",
"var head = document.getElementsByTagName('head')[0];",
"prefetchChunks.forEach(function(chunkId) {",
Template.indent([
"if(installedChunks[chunkId] === undefined) {",
Template.indent([
"installedChunks[chunkId] = null;",
mainTemplate.hooks.linkPrefetch.call("", chunk, hash),
"head.appendChild(link);"
]),
"}"
]),
"});"
])
: "",
"while(resolves.length) {",
Template.indent("resolves.shift()();"),
"}",
@ -479,6 +475,25 @@ class JsonpMainTemplatePlugin {
return source;
}
);
mainTemplate.hooks.beforeStartup.tap(
"JsonpMainTemplatePlugin",
(source, chunk, hash) => {
const prefetchChunks = chunk.getChildIdsByOrders().prefetch;
if (
needChunkLoadingCode(chunk) &&
prefetchChunks &&
prefetchChunks.length
) {
return Template.asString([
source,
`webpackJsonpCallback([[], {}, 0, ${JSON.stringify(
prefetchChunks
)}]);`
]);
}
return source;
}
);
mainTemplate.hooks.startup.tap(
"JsonpMainTemplatePlugin",
(source, chunk, hash) => {

View File

@ -77,25 +77,32 @@ rules:
id: logResult
value: "{{{fetch}}}"
remove:
- ".\\[2K.\\[1G|.\\[999D.\\[K"
- "^[\\s\\S]+?\\$ yarn travis:\\$JOB_PART.*\n"
- "\\$ node --max-old-space-size=4096.*\n"
- "^yarn run.+\n"
- "\\(node:\\d+\\) \\[DEP0005\\].+\n"
- ".+rimraf coverage"
- "yarn run.+\n"
- "\\(node:\\d+\\) (\\[DEP0005\\]|DeprecationWarning).+\n"
- "\\$ yarn (cover|test):.+\n"
- "Ran all test suites.\n[\\s\\S]*"
- "PASS test/.*\n"
- "error Command failed with exit code \\d+.\n"
- "info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.\n"
- "Force exiting Jest\n\nHave you considered.+"
- "=============================== Coverage summary ===============================[\\s\\S]+?================================================================================"
- " *PASS *test/.*\n"
- "^\\s+\n|\\s+$"
string_cleanup_1:
id: firstError
value: "{{{logResult}}}"
remove:
- "\n\n ●[\\s\\S]*"
- "\n\n(| FAIL)[\\s\\S]*"
- "Test Suites:[\\s\\S]*"
- "\\s+$"
string_cleanup_2:
id: remainingErrors
value: "{{{logResult}}}"
remove:
- "^[\\s\\S]+?(?=\n\n ●|$)"
- "^[\\s\\S]+?(?=\n\n(| FAIL)|$)"
- "^\n+"
- "Test Suites:[\\s\\S]*"
- "\\s+$"
@ -155,25 +162,33 @@ rules:
id: logResult
value: "{{{fetch}}}"
remove:
- ".\\[2K.\\[1G|.\\[999D.\\[K"
- "^[\\s\\S]+?\\$ yarn travis:\\$JOB_PART.*\n"
- "\\$ node --max-old-space-size=4096.*\n"
- "^yarn run.+\n"
- "\\(node:\\d+\\) \\[DEP0005\\].+\n"
- ".+rimraf coverage"
- "yarn run.+\n"
- "\\(node:\\d+\\) (\\[DEP0005\\]|DeprecationWarning).+\n"
- "\\$ yarn (cover|test):.+\n"
- "The command \"yarn travis:\\$JOB_PART\" exited[\\s\\S]*"
- "PASS test/.*\n"
- "Ran all test suites.+\n"
- "error Command failed with exit code \\d+.\n"
- "info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.\n"
- "Force exiting Jest\n\nHave you considered.+"
- "=============================== Coverage summary ===============================[\\s\\S]+?================================================================================"
- " *PASS *test/.*\n"
- "^\\s+\n|\\s+$"
string_cleanup_1:
id: firstError
value: "{{{logResult}}}"
remove:
- "\n\n ●[\\s\\S]*"
- "\n\n(| FAIL)[\\s\\S]*"
- "Test Suites:[\\s\\S]*"
- "\\s+$"
string_cleanup_2:
id: remainingErrors
value: "{{{logResult}}}"
remove:
- "^[\\s\\S]+?(?=\n\n ●|$)"
- "^[\\s\\S]+?(?=\n\n(| FAIL)|$)"
- "^\n+"
- "Test Suites:[\\s\\S]*"
- "\\s+$"
@ -233,13 +248,21 @@ rules:
id: logResult
value: "{{{fetch}}}"
remove:
- ".\\[2K.\\[1G|.\\[999D.\\[K"
- "^[\\s\\S]+?\\$ yarn travis:\\$JOB_PART.*\n"
- "\\$ node --max-old-space-size=4096.*\n"
- "^yarn run.+\n"
- "\\(node:\\d+\\) \\[DEP0005\\].+\n"
- ".+rimraf coverage"
- "yarn run.+\n"
- "\\(node:\\d+\\) (\\[DEP0005\\]|DeprecationWarning).+\n"
- "\\$ yarn (unit|lint).+\n"
- "The command \"yarn travis:\\$JOB_PART\" exited[\\s\\S]*"
- "PASS test/.*\n"
- "Ran all test suites.+\n"
- "error Command failed with exit code \\d+.\n"
- "info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.\n"
- "Force exiting Jest\n\nHave you considered.+"
- "=============================== Coverage summary ===============================[\\s\\S]+?================================================================================"
- " *PASS *test/.*\n"
- "^\\s+\n|\\s+$"
actions:
comment:
identifier: "ci-result"

View File

@ -6,6 +6,7 @@
"license": "MIT",
"dependencies": {
"@webassemblyjs/ast": "1.5.10",
"@webassemblyjs/helper-module-context": "1.5.10",
"@webassemblyjs/wasm-edit": "1.5.10",
"@webassemblyjs/wasm-opt": "1.5.10",
"@webassemblyjs/wasm-parser": "1.5.10",

View File

@ -1542,6 +1542,10 @@
"description": "Avoid emitting assets when errors occur",
"type": "boolean"
},
"checkWasmTypes": {
"description": "Check for incompatible wasm types when importing/exporting from/to ESM",
"type": "boolean"
},
"namedModules": {
"description": "Use readable module identifiers for better debugging",
"type": "boolean"

View File

@ -7,6 +7,7 @@ const vm = require("vm");
const mkdirp = require("mkdirp");
const rimraf = require("rimraf");
const checkArrayExpectation = require("./checkArrayExpectation");
const FakeDocument = require("./helpers/FakeDocument");
const Stats = require("../lib/Stats");
const webpack = require("../lib/webpack");
@ -176,7 +177,8 @@ describe("ConfigTestCases", () => {
console: console,
expect: expect,
setTimeout: setTimeout,
clearTimeout: clearTimeout
clearTimeout: clearTimeout,
document: new FakeDocument()
};
function _require(currentDirectory, module) {

View File

@ -29,7 +29,11 @@ describe("ModuleDependencyError", () => {
});
it("has a message property", () => {
expect(env.moduleDependencyError.message).toBe("Location Error Message");
expect(env.moduleDependencyError.message).toBe("Error Message");
});
it("has a loc property", () => {
expect(env.moduleDependencyError.loc).toBe("Location");
});
it("has a details property", () => {
@ -38,8 +42,8 @@ describe("ModuleDependencyError", () => {
);
});
it("has an origin property", () => {
expect(env.moduleDependencyError.origin).toBe("myModule");
it("has an module property", () => {
expect(env.moduleDependencyError.module).toBe("myModule");
});
it("has an error property", () => {

View File

@ -16,11 +16,11 @@ describe("NormalModule", () => {
let resource;
let parser;
beforeEach(() => {
request = "some/request";
userRequest = "some/userRequest";
request = "/some/request";
userRequest = "/some/userRequest";
rawRequest = "some/rawRequest";
loaders = [];
resource = "some/resource";
resource = "/some/resource";
parser = {
parse() {}
};
@ -66,14 +66,14 @@ describe("NormalModule", () => {
it("contextifies the userRequest of the module", () => {
expect(
normalModule.libIdent({
context: "some/context"
context: "/some/context"
})
).toBe("../userRequest");
});
describe("given a userRequest containing loaders", () => {
beforeEach(() => {
userRequest =
"some/userRequest!some/other/userRequest!some/thing/is/off/here";
"/some/userRequest!/some/other/userRequest!/some/thing/is/off/here";
normalModule = new NormalModule({
type: "javascript/auto",
request,
@ -87,7 +87,7 @@ describe("NormalModule", () => {
it("contextifies every path in the userRequest", () => {
expect(
normalModule.libIdent({
context: "some/context"
context: "/some/context"
})
).toBe("../userRequest!../other/userRequest!../thing/is/off/here");
});
@ -95,7 +95,7 @@ describe("NormalModule", () => {
describe("given a userRequest containing query parameters", () => {
it("ignores paths in query parameters", () => {
userRequest =
"some/context/loader?query=foo\\bar&otherPath=testpath/other";
"F:\\some\\context\\loader?query=foo\\bar&otherPath=testpath/other";
normalModule = new NormalModule({
type: "javascript/auto",
request,
@ -107,7 +107,7 @@ describe("NormalModule", () => {
});
expect(
normalModule.libIdent({
context: "some/context"
context: "F:\\some\\context"
})
).toBe("./loader?query=foo\\bar&otherPath=testpath/other");
});

View File

@ -19,7 +19,7 @@ describe(
});
const inputPath =
"./node_modules/ts-loader!./node_modules/vue-loader/lib/selector.js?type=script&index=0!./src/app.vue";
const expectPath = `./src/app.vue (${inputPath})\n`;
const expectPath = `./src/app.vue (${inputPath})`;
expect(mockStats.formatFilePath(inputPath)).toBe(expectPath);
});

View File

@ -8,7 +8,7 @@ Child fitting:
Built at: Thu Jan 01 1970 00:00:00 GMT
Asset Size Chunks Chunk Names
9ac13fb7087e9ff1b93e.js 1.05 KiB 0 [emitted]
f2e891598128a57b072c.js 11 KiB 1 [emitted]
f2e891598128a57b072c.js 11.1 KiB 1 [emitted]
d1ba53816ff760e185b0.js 1.94 KiB 2 [emitted]
7b5b0a943e9362bc88c6.js 1.94 KiB 3 [emitted]
Entrypoint main = d1ba53816ff760e185b0.js 7b5b0a943e9362bc88c6.js f2e891598128a57b072c.js
@ -34,7 +34,7 @@ Child content-change:
Built at: Thu Jan 01 1970 00:00:00 GMT
Asset Size Chunks Chunk Names
9ac13fb7087e9ff1b93e.js 1.05 KiB 0 [emitted]
f2e891598128a57b072c.js 11 KiB 1 [emitted]
f2e891598128a57b072c.js 11.1 KiB 1 [emitted]
d1ba53816ff760e185b0.js 1.94 KiB 2 [emitted]
7b5b0a943e9362bc88c6.js 1.94 KiB 3 [emitted]
Entrypoint main = d1ba53816ff760e185b0.js 7b5b0a943e9362bc88c6.js f2e891598128a57b072c.js
@ -71,7 +71,7 @@ d6418937dfae4b3ee922.js 1 KiB 1 [emitted]
685acdc95ff4af957f47.js 1 KiB 7 [emitted]
606f48c13070850338b1.js 1.94 KiB 8 [emitted]
c5a8eae840969538f450.js 1.94 KiB 9 [emitted]
c69b2f79fdf6e98907c4.js 9.68 KiB 10 [emitted] main
c69b2f79fdf6e98907c4.js 9.7 KiB 10 [emitted] main
fcdf398c8972e4dcf788.js 1.94 KiB 11 [emitted]
Entrypoint main = c69b2f79fdf6e98907c4.js
chunk {0} fd868baa40dab4fc30fd.js 1.76 KiB <{10}> ={1}= ={2}= ={3}= ={7}= ={9}= [recorded] aggressive splitted
@ -498,7 +498,7 @@ Built at: Thu Jan 01 1970 00:00:00 GMT
Asset Size Chunks Chunk Names
0.bundle.js 152 bytes 0 [emitted]
1.bundle.js 289 bytes 1 [emitted]
bundle.js 8.27 KiB 2 [emitted] main
bundle.js 8.29 KiB 2 [emitted] main
3.bundle.js 227 bytes 3 [emitted]
Entrypoint main = bundle.js
chunk {0} 0.bundle.js 22 bytes <{2}> [rendered]
@ -537,7 +537,7 @@ Built at: Thu Jan 01 1970 00:00:00 GMT
0.bundle.js 433 bytes 0 [emitted]
1.bundle.js 297 bytes 1 [emitted]
2.bundle.js 588 bytes 2 [emitted]
bundle.js 8.65 KiB main [emitted] main
bundle.js 8.67 KiB main [emitted] main
Entrypoint main = bundle.js
chunk {main} bundle.js (main) 73 bytes >{0}< >{1}< [entry] [rendered]
> ./index main
@ -615,7 +615,7 @@ exports[`StatsTestCases should print correct stats for commons-chunk-min-size-0
Time: Xms
Built at: Thu Jan 01 1970 00:00:00 GMT
Asset Size Chunks Chunk Names
entry-1.js 6.58 KiB 0 [emitted] entry-1
entry-1.js 6.6 KiB 0 [emitted] entry-1
vendor-1~entry-1.js 314 bytes 1 [emitted] vendor-1~entry-1
Entrypoint entry-1 = vendor-1~entry-1.js entry-1.js
[0] ./entry-1.js 145 bytes {0} [built]
@ -632,7 +632,7 @@ exports[`StatsTestCases should print correct stats for commons-chunk-min-size-In
Time: Xms
Built at: Thu Jan 01 1970 00:00:00 GMT
Asset Size Chunks Chunk Names
entry-1.js 6.58 KiB 0 [emitted] entry-1
entry-1.js 6.6 KiB 0 [emitted] entry-1
vendor-1.js 314 bytes 1 [emitted] vendor-1
Entrypoint entry-1 = vendor-1.js entry-1.js
[0] ./entry-1.js 145 bytes {0} [built]
@ -651,7 +651,7 @@ Child
Time: Xms
Built at: Thu Jan 01 1970 00:00:00 GMT
Asset Size Chunks Chunk Names
app.js 6.67 KiB 0 [emitted] app
app.js 6.69 KiB 0 [emitted] app
vendor.6a3bdffda9f0de672978.js 619 bytes 1 [emitted] vendor
Entrypoint app = vendor.6a3bdffda9f0de672978.js app.js
[./constants.js] 87 bytes {1} [built]
@ -664,7 +664,7 @@ Child
Time: Xms
Built at: Thu Jan 01 1970 00:00:00 GMT
Asset Size Chunks Chunk Names
app.js 6.69 KiB 0 [emitted] app
app.js 6.7 KiB 0 [emitted] app
vendor.6a3bdffda9f0de672978.js 619 bytes 1 [emitted] vendor
Entrypoint app = vendor.6a3bdffda9f0de672978.js app.js
[./constants.js] 87 bytes {1} [built]
@ -985,7 +985,7 @@ Built at: Thu Jan 01 1970 00:00:00 GMT
0.js 305 bytes 0 [emitted]
1.js 314 bytes 1 [emitted]
2.js 308 bytes 2 [emitted]
entry.js 9.06 KiB 3 [emitted] entry
entry.js 9.08 KiB 3 [emitted] entry
Entrypoint entry = entry.js
[0] ./templates/bar.js 38 bytes {0} [optional] [built]
[1] ./templates/baz.js 38 bytes {1} [optional] [built]
@ -1000,13 +1000,35 @@ Time: Xms
Built at: Thu Jan 01 1970 00:00:00 GMT
Asset Size Chunks Chunk Names
0.js 149 bytes 0 [emitted]
entry.js 8.51 KiB 1 [emitted] entry
entry.js 8.53 KiB 1 [emitted] entry
Entrypoint entry = entry.js
[0] ./modules/b.js 22 bytes {0} [built]
[1] ./entry.js 120 bytes {1} [built]
[2] ./modules/a.js 37 bytes [built]"
`;
exports[`StatsTestCases should print correct stats for import-with-invalid-options-comments 1`] = `
"Built at: Thu Jan 01 1970 00:00:00 GMT
[0] ./chunk-a.js 27 bytes {2} [built]
[1] ./chunk-b.js 27 bytes {3} [built]
[2] ./chunk-c.js 27 bytes {4} [built]
[3] ./chunk-d.js 27 bytes {5} [built]
[4] ./chunk.js 401 bytes {0} [built] [3 warnings]
[5] ./index.js 50 bytes {1} [built]
WARNING in ./chunk.js 4:11-77
Compilation error while processing magic comment(-s): /* webpack Prefetch: 0, webpackChunkName: \\"notGoingToCompile-c\\" */: Unexpected identifier
@ ./index.js 1:0-49
WARNING in ./chunk.js 5:11-38
Compilation error while processing magic comment(-s): /* webpackPrefetch: nope */: nope is not defined
@ ./index.js 1:0-49
WARNING in ./chunk.js 2:11-84
Compilation error while processing magic comment(-s): /* webpackPrefetch: true, webpackChunkName: notGoingToCompileChunkName */: notGoingToCompileChunkName is not defined
@ ./index.js 1:0-49"
`;
exports[`StatsTestCases should print correct stats for limit-chunk-count-plugin 1`] = `
"Hash: 3c127dca42ce1c13b8eae6c0ceac1aa3be512946a5969fc94d792cce5364503bf42779f704747e2d
Child 1 chunks:
@ -1014,7 +1036,7 @@ Child 1 chunks:
Time: Xms
Built at: Thu Jan 01 1970 00:00:00 GMT
Asset Size Chunks Chunk Names
bundle.js 6.37 KiB 0 [emitted] main
bundle.js 6.39 KiB 0 [emitted] main
Entrypoint main = bundle.js
chunk {0} bundle.js (main) 191 bytes <{0}> >{0}< [entry] [rendered]
[0] ./index.js 73 bytes {0} [built]
@ -1029,7 +1051,7 @@ Child 2 chunks:
Built at: Thu Jan 01 1970 00:00:00 GMT
Asset Size Chunks Chunk Names
0.bundle.js 632 bytes 0 [emitted]
bundle.js 8.26 KiB 1 [emitted] main
bundle.js 8.28 KiB 1 [emitted] main
Entrypoint main = bundle.js
chunk {0} 0.bundle.js 118 bytes <{0}> <{1}> >{0}< [rendered]
[0] ./d.js 22 bytes {0} [built]
@ -1046,7 +1068,7 @@ Child 3 chunks:
Asset Size Chunks Chunk Names
0.bundle.js 494 bytes 0 [emitted]
1.bundle.js 245 bytes 1 [emitted]
bundle.js 8.26 KiB 2 [emitted] main
bundle.js 8.28 KiB 2 [emitted] main
Entrypoint main = bundle.js
chunk {0} 0.bundle.js 74 bytes <{0}> <{2}> >{0}< >{1}< [rendered]
[0] ./d.js 22 bytes {0} [built]
@ -1065,7 +1087,7 @@ Child 4 chunks:
0.bundle.js 236 bytes 0 [emitted]
1.bundle.js 245 bytes 1 [emitted]
2.bundle.js 323 bytes 2 [emitted]
bundle.js 8.26 KiB 3 [emitted] main
bundle.js 8.28 KiB 3 [emitted] main
Entrypoint main = bundle.js
chunk {0} 0.bundle.js 44 bytes <{2}> <{3}> [rendered]
[0] ./d.js 22 bytes {0} [built]
@ -1157,9 +1179,9 @@ exports[`StatsTestCases should print correct stats for module-deduplication 1`]
3.js 661 bytes 3 [emitted]
4.js 661 bytes 4 [emitted]
5.js 661 bytes 5 [emitted]
e1.js 9.4 KiB 6 [emitted] e1
e2.js 9.43 KiB 7 [emitted] e2
e3.js 9.45 KiB 8 [emitted] e3
e1.js 9.42 KiB 6 [emitted] e1
e2.js 9.44 KiB 7 [emitted] e2
e3.js 9.46 KiB 8 [emitted] e3
Entrypoint e1 = e1.js
Entrypoint e2 = e2.js
Entrypoint e3 = e3.js
@ -1203,9 +1225,9 @@ exports[`StatsTestCases should print correct stats for module-deduplication-name
async3.js 818 bytes 0 [emitted] async3
async1.js 818 bytes 1 [emitted] async1
async2.js 818 bytes 2 [emitted] async2
e1.js 9.29 KiB 3 [emitted] e1
e2.js 9.31 KiB 4 [emitted] e2
e3.js 9.33 KiB 5 [emitted] e3
e1.js 9.31 KiB 3 [emitted] e1
e2.js 9.33 KiB 4 [emitted] e2
e3.js 9.35 KiB 5 [emitted] e3
Entrypoint e1 = e1.js
Entrypoint e2 = e2.js
Entrypoint e3 = e3.js
@ -1321,7 +1343,7 @@ exports[`StatsTestCases should print correct stats for named-chunks-plugin 1`] =
Time: Xms
Built at: Thu Jan 01 1970 00:00:00 GMT
Asset Size Chunks Chunk Names
entry.js 6.43 KiB entry [emitted] entry
entry.js 6.45 KiB entry [emitted] entry
vendor.js 269 bytes vendor [emitted] vendor
Entrypoint entry = vendor.js entry.js
[./entry.js] 72 bytes {entry} [built]
@ -1337,7 +1359,7 @@ Built at: Thu Jan 01 1970 00:00:00 GMT
Asset Size Chunks Chunk Names
chunk-containing-__a_js.js 313 bytes chunk-containing-__a_js [emitted]
chunk-containing-__b_js.js 173 bytes chunk-containing-__b_js [emitted]
entry.js 8.17 KiB entry [emitted] entry
entry.js 8.18 KiB entry [emitted] entry
Entrypoint entry = entry.js
[0] ./modules/b.js 22 bytes {chunk-containing-__b_js} [built]
[1] ./modules/a.js 37 bytes {chunk-containing-__a_js} [built]
@ -1375,7 +1397,7 @@ Built at: Thu Jan 01 1970 00:00:00 GMT
ab.js 183 bytes 1 [emitted] ab
abd.js 277 bytes 2, 1 [emitted] abd
cir2.js 299 bytes 3 [emitted] cir2
main.js 9.07 KiB 4 [emitted] main
main.js 9.09 KiB 4 [emitted] main
cir2 from cir1.js 359 bytes 5, 3 [emitted] cir2 from cir1
chunk.js 190 bytes 6, 7 [emitted] chunk
ac in ab.js 130 bytes 7 [emitted] ac in ab
@ -1425,12 +1447,12 @@ Entrypoint main = main.js
| ./index.js 15 bytes [built]
| ./a.js 15 bytes [built]
ERROR in ./b.js
ERROR in ./b.js 6:7
Module parse failed: Unexpected token (6:7)
You may need an appropriate loader to handle this file type.
| includes
| a
| parser )
> parser )
| error
| in
@ ./a.js 2:0-13
@ -1668,11 +1690,11 @@ For more info visit https://webpack.js.org/guides/code-splitting/</CLR>"
exports[`StatsTestCases should print correct stats for prefetch 1`] = `
" Asset Size Chunks Chunk Names
prefetched.js 467 bytes 0 [emitted] prefetched
prefetched.js 475 bytes 0 [emitted] prefetched
normal.js 130 bytes 1 [emitted] normal
prefetched2.js 127 bytes 2 [emitted] prefetched2
prefetched3.js 130 bytes 3 [emitted] prefetched3
main.js 9.73 KiB 4 [emitted] main
main.js 9.66 KiB 4 [emitted] main
inner.js 136 bytes 5 [emitted] inner
inner2.js 201 bytes 6 [emitted] inner2
Entrypoint main = main.js (prefetch: prefetched2.js prefetched.js prefetched3.js)
@ -1705,7 +1727,7 @@ exports[`StatsTestCases should print correct stats for preload 1`] = `
normal.js 130 bytes 1 [emitted] normal
preloaded2.js 127 bytes 2 [emitted] preloaded2
preloaded3.js 130 bytes 3 [emitted] preloaded3
main.js 9.85 KiB 4 [emitted] main
main.js 9.87 KiB 4 [emitted] main
inner.js 136 bytes 5 [emitted] inner
inner2.js 201 bytes 6 [emitted] inner2
Entrypoint main = main.js (preload: preloaded2.js preloaded.js preloaded3.js)
@ -1725,7 +1747,7 @@ Built at: Thu Jan 01 1970 00:00:00 GMT
Asset Size Chunks Chunk Names
0.js 152 bytes 0 [emitted]
1.js 289 bytes 1 [emitted]
main.js 8.28 KiB 2 [emitted] main
main.js 8.29 KiB 2 [emitted] main
3.js 227 bytes 3 [emitted]
Entrypoint main = main.js
chunk {0} 0.js 22 bytes <{2}> [rendered]
@ -1784,7 +1806,7 @@ Built at: Thu Jan 01 1970 00:00:00 GMT
Asset Size Chunks Chunk Names
0.js 152 bytes 0 [emitted]
1.js 289 bytes 1 [emitted]
main.js 8.28 KiB 2 [emitted] main
main.js 8.29 KiB 2 [emitted] main
3.js 227 bytes 3 [emitted]
Entrypoint main = main.js
[0] ./d.js 22 bytes {3} [built]
@ -1862,7 +1884,7 @@ Built at: Thu Jan 01 1970 00:00:00 GMT
Asset Size Chunks Chunk Names
0.js 152 bytes 0 [emitted]
1.js 289 bytes 1 [emitted]
main.js 8.28 KiB 2 [emitted] main
main.js 8.29 KiB 2 [emitted] main
3.js 227 bytes 3 [emitted]
Entrypoint main = main.js
chunk {0} 0.js 22 bytes <{2}> [rendered]
@ -1953,7 +1975,7 @@ exports[`StatsTestCases should print correct stats for runtime-chunk-integration
Asset Size Chunks Chunk Names
0.js 719 bytes 0 [emitted]
main1.js 542 bytes 1 [emitted] main1
runtime.js 8.73 KiB 2 [emitted] runtime
runtime.js 8.75 KiB 2 [emitted] runtime
Entrypoint main1 = runtime.js main1.js
[0] ./b.js 20 bytes {0} [built]
[1] ./c.js 20 bytes {0} [built]
@ -1962,7 +1984,7 @@ exports[`StatsTestCases should print correct stats for runtime-chunk-integration
Child manifest is named entry:
Asset Size Chunks Chunk Names
0.js 719 bytes 0 [emitted]
manifest.js 9.04 KiB 1 [emitted] manifest
manifest.js 9.06 KiB 1 [emitted] manifest
main1.js 542 bytes 2 [emitted] main1
Entrypoint main1 = manifest.js main1.js
Entrypoint manifest = manifest.js
@ -2068,7 +2090,7 @@ Time: Xms
Built at: Thu Jan 01 1970 00:00:00 GMT
Asset Size Chunks Chunk Names
0.js 481 bytes 0 [emitted]
main.js 9.31 KiB 1 [emitted] main
main.js 9.32 KiB 1 [emitted] main
Entrypoint main = main.js
[0] ./components/src/CompAB/utils.js 97 bytes {1} [built]
harmony side effect evaluation ./utils [1] ./main.js + 1 modules 1:0-30

View File

@ -1,5 +1,5 @@
module.exports = [
[/System.get is not supported by webpack/],
[/System.register is not supported by webpack/],
[/System.get is not supported by webpack/],
[/System.set is not supported by webpack/],
];

View File

@ -0,0 +1,2 @@
export const n = 1;
export const m = 1.25

View File

@ -0,0 +1,8 @@
it("should export imported global", function() {
return import("./module").then(function({ v, w, x, test }) {
expect(v).toBe(1);
expect(w).toBe(1);
expect(x).toBe(1.25);
expect(test()).toBe(2);
});
});

View File

@ -0,0 +1 @@
export * from "./module.wat";

View File

@ -0,0 +1,17 @@
(module
(import "./env.js" "n" (global i32))
(import "./env.js" "m" (global $g2 f64))
(export "v" (global 0))
(global $g i32 (get_global 0))
(export "w" (global $g))
(export "x" (global $g2))
(func (export "test") (result i32)
get_global $g2
get_global $g2
f64.add
drop
get_global 0
get_global $g
i32.add
)
)

View File

@ -0,0 +1,5 @@
var supportsWebAssembly = require("../../../helpers/supportsWebAssembly");
module.exports = function(config) {
return supportsWebAssembly();
};

View File

@ -0,0 +1 @@
export const n = 1;

View File

@ -0,0 +1,17 @@
module.exports = [
[
/export-i64-param\.wat/,
/Export "a" with i64 as parameter can only be used for direct wasm to wasm dependencies/,
/export-i64-param\.js/
],
[
/export-i64-result\.wat/,
/Export "a" with i64 as result can only be used for direct wasm to wasm dependencies/,
/export-i64-result\.js/
],
[
/import-i64\.wat/,
/Import "n" from "\.\/env.js" with Non-JS-compatible Global Type \(i64\) can only be used for direct wasm to wasm dependencies/,
/index\.js/
]
]

View File

@ -0,0 +1 @@
export { a } from "./export-i64-param.wat";

View File

@ -0,0 +1,3 @@
(module
(func (export "a") (param i64) (nop))
)

View File

@ -0,0 +1 @@
export { a } from "./export-i64-result.wat";

View File

@ -0,0 +1,5 @@
(module
(func (export "a") (result i64)
(i64.const 1)
)
)

View File

@ -0,0 +1,3 @@
(module
(import "./env.js" "n" (global i64))
)

View File

@ -0,0 +1,17 @@
const errorRegex = /wasm function signature contains illegal type|invalid type/;
it("should disallow exporting a func signature with result i64", function() {
return import("./export-i64-result").then(({a}) => {
expect(() => a()).toThrow(errorRegex);
});
});
it("should disallow exporting a func signature with param i64", function() {
return import("./export-i64-param").then(({a}) => {
expect(() => a()).toThrow(errorRegex);
});
});
it("should disallow importing a value type of i64", function() {
return expect(import("./import-i64.wat")).rejects.toThrow(errorRegex);
});

View File

@ -0,0 +1,5 @@
var supportsWebAssembly = require("../../../helpers/supportsWebAssembly");
module.exports = function(config) {
return supportsWebAssembly() && config.mode === "production";
};

View File

@ -0,0 +1,11 @@
it("should be able to create two modules from loader", function() {
return import("./wrapper-loader!./src/wasm.dat").then(function(wasm) {
expect(wasm.getString()).toEqual("Hello World");
});
});
it("should be able to create two modules from loader with remaining request", function() {
return import("./wrapper-loader2!./src/wasm.dat?2").then(function(wasm) {
expect(wasm.getString()).toEqual("Hello World");
});
});

View File

@ -0,0 +1,8 @@
(module
(memory (export "memory") 1)
(data (i32.const 16) "Hello World\00")
(func (export "getString") (result i32)
(i32.const 16)
)
)

View File

@ -0,0 +1,5 @@
var supportsWebAssembly = require("../../../helpers/supportsWebAssembly");
module.exports = function(config) {
return supportsWebAssembly();
};

View File

@ -0,0 +1,17 @@
const stringifyRequest = require("loader-utils").stringifyRequest;
module.exports.pitch = function(remainingRequest) {
return `
import { getString as _getString, memory } from ${stringifyRequest(this,
`${this.resourcePath}.wat!=!${remainingRequest}`
)};
export function getString() {
const strBuf = new Uint8Array(memory.buffer, _getString());
const idx = strBuf.indexOf(0);
const strBuf2 = strBuf.slice(0, idx);
const str = Buffer.from(strBuf2).toString("utf-8");
return str;
};
`;
};

View File

@ -0,0 +1,18 @@
const stringifyRequest = require("loader-utils").stringifyRequest;
module.exports.pitch = function(remainingRequest) {
return `
import { getString as _getString, memory } from ${stringifyRequest(
this,
`${this.resourcePath}.wasm!=!wast-loader!${remainingRequest}`
)};
export function getString() {
const strBuf = new Uint8Array(memory.buffer, _getString());
const idx = strBuf.indexOf(0);
const strBuf2 = strBuf.slice(0, idx);
const str = Buffer.from(strBuf2).toString("utf-8");
return str;
};
`;
};

View File

@ -0,0 +1,3 @@
it("should set NODE_ENV according to mode", () => {
expect(process.env.NODE_ENV).toBe(__MODE__);
});

View File

@ -0,0 +1,21 @@
"use strict";
const DefinePlugin = require("../../../../lib/DefinePlugin");
module.exports = [
{
name: "development",
mode: "development",
plugins: [new DefinePlugin({ __MODE__: `"development"` })]
},
{
name: "production",
mode: "production",
plugins: [new DefinePlugin({ __MODE__: `"production"` })]
},
{
name: "none",
mode: "none",
plugins: [new DefinePlugin({ __MODE__: `"none"` })]
}
];

View File

@ -1,18 +1,10 @@
const FakeDocument = require("../../../helpers/FakeDocument");
beforeEach(() => {
global.document = new FakeDocument();
global.window = {};
});
afterEach(() => {
delete global.document;
delete global.window;
});
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");
__non_webpack_require__("./dep~b~shared.js");
return promise;
});

View File

@ -0,0 +1,4 @@
export default function() {
import(/* webpackPrefetch: true, webpackChunkName: "chunk1-a" */ "./chunk1-a");
import(/* webpackPreload: true, webpackChunkName: "chunk1-b" */ "./chunk1-b");
}

View File

@ -1,4 +1,3 @@
const FakeDocument = require("../../../helpers/FakeDocument");
let oldNonce;
let oldPublicPath;
@ -6,54 +5,95 @@ let oldPublicPath;
beforeEach(() => {
oldNonce = __webpack_nonce__;
oldPublicPath = __webpack_public_path__;
global.document = new FakeDocument(undefined, false);
global.window = {};
});
afterEach(() => {
delete global.document;
delete global.window;
__webpack_nonce__ = oldNonce;
__webpack_public_path__ = oldPublicPath;
})
it("should prefetch and preload child chunks on chunk load", (done) => {
it("should prefetch and preload child chunks on chunk load", () => {
__webpack_nonce__ = "nonce";
__webpack_public_path__ = "/public/path/";
const promise = import(/* webpackChunkName: "chunk1" */ "./chunk1");
expect(document.head._children).toHaveLength(2);
const script = document.head._children[0];
let link, script;
expect(document.head._children).toHaveLength(1);
// Test prefetch from entry chunk
link = document.head._children[0];
expect(link._type).toBe("link");
expect(link.rel).toBe("prefetch");
expect(link.href).toMatch(/chunk1\.js$/);
const promise = import(/* webpackChunkName: "chunk1", webpackPrefetch: true */ "./chunk1");
expect(document.head._children).toHaveLength(3);
// Test normal script loading
script = document.head._children[1];
expect(script._type).toBe("script");
expect(script.src).toBe("/public/path/chunk1.js")
expect(script.src).toMatch(/chunk1\.js$/);
expect(script.getAttribute("nonce")).toBe("nonce")
expect(script.crossOrigin).toBe("anonymous");
expect(script.onload).toBeTypeOf("function");
let link = document.head._children[1];
// Test preload of chunk1-b
link = document.head._children[2];
expect(link._type).toBe("link");
expect(link.rel).toBe("preload");
expect(link.as).toBe("script");
expect(link.href).toBe("/public/path/chunk1-b.js");
expect(link.href).toMatch(/chunk1-b\.js$/);
expect(link.charset).toBe("utf-8");
expect(link.getAttribute("nonce")).toBe("nonce");
expect(link.crossOrigin).toBe("anonymous");
// Run the script
__non_webpack_require__("./chunk1.js");
script.onload();
return promise.then((ex) => {
expect(document.head._children).toHaveLength(4);
let link = document.head._children[2];
expect(link._type).toBe("link");
expect(link.rel).toBe("prefetch");
expect(link.href).toBe("/public/path/chunk1-c.js");
return promise.then(() => {
expect(document.head._children).toHaveLength(5);
// Test prefetching for chunk1-c and chunk1-a in this order
link = document.head._children[3];
expect(link._type).toBe("link");
expect(link.rel).toBe("prefetch");
expect(link.href).toBe("/public/path/chunk1-a.js");
done();
}, done);
expect(link.href).toMatch(/chunk1-c\.js$/);
expect(link.crossOrigin).toBe("anonymous");
link = document.head._children[4];
expect(link._type).toBe("link");
expect(link.rel).toBe("prefetch");
expect(link.href).toMatch(/chunk1-a\.js$/);
expect(link.crossOrigin).toBe("anonymous");
const promise2 = import(/* webpackChunkName: "chunk1", webpackPrefetch: true */ "./chunk1");
// Loading chunk1 again should not trigger prefetch/preload
expect(document.head._children).toHaveLength(5);
const promise3 = import(/* webpackChunkName: "chunk2" */ "./chunk2");
expect(document.head._children).toHaveLength(6);
// Test normal script loading
script = document.head._children[5];
expect(script._type).toBe("script");
expect(script.src).toMatch(/chunk2\.js$/);
expect(script.getAttribute("nonce")).toBe("nonce")
expect(script.crossOrigin).toBe("anonymous");
expect(script.onload).toBeTypeOf("function");
// Run the script
__non_webpack_require__("./chunk2.js");
script.onload();
return promise3.then(() => {
// Loading chunk2 again should not trigger prefetch/preload as it's already prefetch/preloaded
expect(document.head._children).toHaveLength(6);
});
});
})

View File

@ -16,9 +16,8 @@ module.exports = class FakeDocument {
};
class FakeElement {
constructor(type, autoload = true) {
constructor(type) {
this._type = type;
this._autoload = autoload;
this._children = [];
this._attributes = Object.create(null);
}
@ -34,33 +33,4 @@ class FakeElement {
getAttribute(name) {
return this._attributes[name];
}
get onload() {
return this._onload;
}
set onload(script) {
if (this._autoload === true && typeof script === "function") {
script();
}
this._onload = script;
}
get src() {
return this._src;
}
set src(src) {
// eslint-disable-next-line no-undef
const publicPath = __webpack_public_path__;
eval(`
const path = require('path');
const fs = require('fs');
const content = fs.readFileSync(
path.join(__dirname, '${src}'.replace('${publicPath}', '')), "utf-8"
)
eval(content);
`);
this._src = src;
}
}

View File

@ -0,0 +1 @@
module.exports = "chunk-a";

View File

@ -0,0 +1 @@
module.exports = "chunk-b";

View File

@ -0,0 +1 @@
module.exports = "chunk-c";

View File

@ -0,0 +1 @@
module.exports = "chunk-d";

View File

@ -0,0 +1,6 @@
export default function() {
import(/* webpackPrefetch: true, webpackChunkName: notGoingToCompileChunkName */ "./chunk-a");
import(/* webpackPrefetch: 0, webpackChunkName: "goingToCompileChunkName-b" */ "./chunk-b");
import(/* webpack Prefetch: 0, webpackChunkName: "notGoingToCompile-c" */ "./chunk-c");
import(/* webpackPrefetch: nope */ /* webpackChunkName: "yep" */ "./chunk-d");
}

View File

@ -0,0 +1 @@
import(/* webpackChunkName: "chunk" */ "./chunk");

View File

@ -0,0 +1,15 @@
module.exports = {
mode: "production",
entry: "./index",
output: {
chunkFilename: "[name].js"
},
stats: {
timings: false,
hash: false,
entrypoints: false,
assets: false,
errorDetails: true,
moduleTrace: true
}
};