Refactor how exportsType work

add flagged exportsType for __esModule flagged
add getExportsType to merge strict with exportsType
move CommonJsStuffPlugin into CommonJsPlugin
split CommonJsPlugin into imports and exports part
enable minimal tree shaking for CommonJs imports and exports
This commit is contained in:
Tobias Koppers 2019-12-04 22:54:26 +01:00
parent 32ec03f2b2
commit 782e4535ac
49 changed files with 2382 additions and 1079 deletions

View File

@ -0,0 +1,282 @@
# example.js
```javascript
const inc = require("./increment").increment;
var a = 1;
inc(a); // 2
```
# increment.js
```javascript
const add = require("./math").add;
exports.increment = function increment(val) {
return add(val, 1);
};
exports.incrementBy2 = function incrementBy2(val) {
return add(val, 2);
};
exports.decrement = function decrement(val) {
return add(val, 1);
};
```
# math.js
```javascript
exports.add = function add() {
var sum = 0,
i = 0,
args = arguments,
l = args.length;
while (i < l) {
sum += args[i++];
}
return sum;
};
exports.multiply = function multiply() {
var product = 0,
i = 0,
args = arguments,
l = args.length;
while (i < l) {
sum *= args[i++];
}
return sum;
};
```
# dist/output.js
```javascript
/******/ (() => { // webpackBootstrap
/******/ var __webpack_modules__ = ([
/* 0 */,
/* 1 */
/*!**********************!*\
!*** ./increment.js ***!
\**********************/
/*! exports type: default defaultObject: redirect */
/*! export decrement [provided] [unused] [renamed to C] */
/*! export default [provided] [unused] [no name, virtual] */
/*! export decrement [provided] [unused] [renamed to C] */
/*! export increment [provided] [used] [renamed to pD] */
/*! export incrementBy2 [provided] [unused] [renamed to ju] */
/*! other exports [not provided] [unused] */
/*! export increment [provided] [used] [renamed to pD] */
/*! export incrementBy2 [provided] [unused] [renamed to ju] */
/*! other exports [not provided] [unused] */
/*! runtime requirements: __webpack_require__, __webpack_exports__ */
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
var __webpack_unused_export__;
const add = __webpack_require__(/*! ./math */ 2)/* .add */ .K;
exports.pD = function increment(val) {
return add(val, 1);
};
__webpack_unused_export__ = function incrementBy2(val) {
return add(val, 2);
};
__webpack_unused_export__ = function decrement(val) {
return add(val, 1);
};
/***/ }),
/* 2 */
/*!*****************!*\
!*** ./math.js ***!
\*****************/
/*! exports type: default defaultObject: redirect */
/*! export add [provided] [used] [renamed to K] */
/*! export default [provided] [unused] [no name, virtual] */
/*! export add [provided] [used] [renamed to K] */
/*! export multiply [provided] [unused] [renamed to j] */
/*! other exports [not provided] [unused] */
/*! export multiply [provided] [unused] [renamed to j] */
/*! other exports [not provided] [unused] */
/*! runtime requirements: __webpack_exports__ */
/***/ ((__unused_webpack_module, exports) => {
var __webpack_unused_export__;
exports.K = function add() {
var sum = 0,
i = 0,
args = arguments,
l = args.length;
while (i < l) {
sum += args[i++];
}
return sum;
};
__webpack_unused_export__ = function multiply() {
var product = 0,
i = 0,
args = arguments,
l = args.length;
while (i < l) {
sum *= args[i++];
}
return sum;
};
/***/ })
/******/ ]);
```
<details><summary><code>/* webpack runtime code */</code></summary>
``` js
/************************************************************************/
/******/ // The module cache
/******/ var __webpack_module_cache__ = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ if(__webpack_module_cache__[moduleId]) {
/******/ return __webpack_module_cache__[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = __webpack_module_cache__[moduleId] = {
/******/ // no module.id needed
/******/ // no module.loaded needed
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/************************************************************************/
```
</details>
``` js
(() => {
/*!********************!*\
!*** ./example.js ***!
\********************/
/*! dynamic exports type */
/*! exports [maybe provided (runtime-defined)] [unused] */
/*! runtime requirements: __webpack_require__ */
const inc = __webpack_require__(/*! ./increment */ 1)/* .increment */ .pD;
var a = 1;
inc(a); // 2
})();
/******/ })()
;
```
# dist/output.js (production)
```javascript
/*! For license information please see output.js.LICENSE */
(()=>{var r=[,(r,t,n)=>{const e=n(2).K;t.pD=function(r){return e(r,1)}},(r,t)=>{t.K=function(){for(var r=0,t=0,n=arguments,e=n.length;t<e;)r+=n[t++];return r}}],t={};function n(e){if(t[e])return t[e].exports;var o=t[e]={exports:{}};return r[e](o,o.exports,n),o.exports}(0,n(1).pD)(1)})();
```
# dist/without.js (same without tree shaking)
```javascript
/*! For license information please see without.js.LICENSE */
(()=>{var n=[,(n,r,t)=>{const e=t(2).add;r.increment=function(n){return e(n,1)},r.incrementBy2=function(n){return e(n,2)},r.decrement=function(n){return e(n,1)}},(n,r)=>{r.add=function(){for(var n=0,r=0,t=arguments,e=t.length;r<e;)n+=t[r++];return n},r.multiply=function(){for(var n=0,r=arguments,t=r.length;n<t;)sum*=r[n++];return sum}}],r={};function t(e){if(r[e])return r[e].exports;var u=r[e]={exports:{}};return n[e](u,u.exports,t),u.exports}(0,t(1).increment)(1)})();
```
# Info
## Unoptimized
```
Hash: 0a1b2c3d4e5f6a7b8c9d
Version: webpack 5.0.0-beta.7
Child
Hash: 0a1b2c3d4e5f6a7b8c9d
Asset Size
output.js 3.44 KiB [emitted] [name: main]
Entrypoint main = output.js
chunk output.js (main) 634 bytes [entry] [rendered]
> ./example.js main
./example.js 70 bytes [built]
[no exports used]
entry ./example.js main
./increment.js 251 bytes [built]
[exports: default, decrement, increment, incrementBy2]
[only some exports used: increment]
cjs full require ./increment ./example.js 1:12-44
./math.js 313 bytes [built]
[exports: default, add, multiply]
[only some exports used: add]
cjs full require ./math ./increment.js 1:12-33
Child
Hash: 0a1b2c3d4e5f6a7b8c9d
Asset Size
without.js 3.7 KiB [emitted] [name: main]
Entrypoint main = without.js
chunk without.js (main) 634 bytes [entry] [rendered]
> ./example.js main
./example.js 70 bytes [built]
[used exports unknown]
entry ./example.js main
./increment.js 251 bytes [built]
[exports: default, decrement, increment, incrementBy2]
[used exports unknown]
cjs full require ./increment ./example.js 1:12-44
./math.js 313 bytes [built]
[exports: default, add, multiply]
[used exports unknown]
cjs full require ./math ./increment.js 1:12-33
```
## Production mode
```
Hash: 0a1b2c3d4e5f6a7b8c9d
Version: webpack 5.0.0-beta.7
Child
Hash: 0a1b2c3d4e5f6a7b8c9d
Asset Size
output.js 348 bytes [emitted] [name: main]
output.js.LICENSE 1.16 KiB [emitted]
Entrypoint main = output.js
chunk output.js (main) 634 bytes [entry] [rendered]
> ./example.js main
./example.js 70 bytes [built]
[no exports used]
entry ./example.js main
./increment.js 251 bytes [built]
[exports: default, decrement, increment, incrementBy2]
[only some exports used: increment]
cjs full require ./increment ./example.js 1:12-44
./math.js 313 bytes [built]
[exports: default, add, multiply]
[only some exports used: add]
cjs full require ./math ./increment.js 1:12-33
Child
Hash: 0a1b2c3d4e5f6a7b8c9d
Asset Size
without.js 534 bytes [emitted] [name: main]
without.js.LICENSE 1.49 KiB [emitted]
Entrypoint main = without.js
chunk without.js (main) 634 bytes [entry] [rendered]
> ./example.js main
./example.js 70 bytes [built]
[used exports unknown]
entry ./example.js main
./increment.js 251 bytes [built]
[exports: default, decrement, increment, incrementBy2]
[used exports unknown]
cjs full require ./increment ./example.js 1:12-44
./math.js 313 bytes [built]
[exports: default, add, multiply]
[used exports unknown]
cjs full require ./math ./increment.js 1:12-33
```

View File

@ -0,0 +1,2 @@
global.NO_TARGET_ARGS = true;
require("../build-common");

View File

@ -0,0 +1,58 @@
BAD:
module.exports = abc; module.exports.xxx = abc; abc.xxx;
exports = abc;
module.exports
exports
this
function f() { return this; } module.exports = { f }; module.exports.xxx = abc;
EXPORTS:
exports.xxx = abc;
module.exports.xxx = abc;
this.xxx = abc
Object.defineProperty(exports, "xxx", { ... })
Object.defineProperty(module.exports, "xxx", { ... })
Object.defineProperty(this, "xxx", { ... })
module.exports.xxx
exports.xxx
this.xxx
module.exports = function() {}; module.exports.xxx = abc;
module.exports = { ... }; module.exports.xxx = abc;
OBJECTS:
module.exports = { xxx: abc };
IMPORT:
require(x).xxx
var { xxx } = require(x);
var x = require(x); x.xxx;
REEXPORT:
module.exports.xxx = require(x);
module.exports.xxx = require(x).xxx;
exports.xxx = require(x);
exports.xxx = require(x).xxx;
module.exports = { xxx2: require(x) };
module.exports = { xxx2: require(x).xxx };
var xxx = require(x); exports.xxx = xxx;
var xxx = require(x); exports.xxx = xxx.xxx;
var xxx = require(x); module.exports = { xxx };
var xxx = require(x); module.exports = { xxx: xxx.xxx };
TRANSPILED:
TypeScript:
function __export(m) { for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; }
__export(require(x));
Babel:
var xxx = _interopRequireDefault(require(x));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
xxx.xxx;

View File

@ -0,0 +1,3 @@
const inc = require("./increment").increment;
var a = 1;
inc(a); // 2

View File

@ -0,0 +1,10 @@
const add = require("./math").add;
exports.increment = function increment(val) {
return add(val, 1);
};
exports.incrementBy2 = function incrementBy2(val) {
return add(val, 2);
};
exports.decrement = function decrement(val) {
return add(val, 1);
};

View File

@ -0,0 +1,21 @@
exports.add = function add() {
var sum = 0,
i = 0,
args = arguments,
l = args.length;
while (i < l) {
sum += args[i++];
}
return sum;
};
exports.multiply = function multiply() {
var product = 0,
i = 0,
args = arguments,
l = args.length;
while (i < l) {
sum *= args[i++];
}
return sum;
};

View File

@ -0,0 +1,49 @@
# example.js
```javascript
_{{example.js}}_
```
# increment.js
```javascript
_{{increment.js}}_
```
# math.js
```javascript
_{{math.js}}_
```
# dist/output.js
```javascript
_{{dist/output.js}}_
```
# dist/output.js (production)
```javascript
_{{production:dist/output.js}}_
```
# dist/without.js (same without tree shaking)
```javascript
_{{production:dist/without.js}}_
```
# Info
## Unoptimized
```
_{{stdout}}_
```
## Production mode
```
_{{production:stdout}}_
```

View File

@ -0,0 +1,26 @@
module.exports = [
{
entry: "./example.js",
output: {
pathinfo: true,
filename: "output.js"
},
optimization: {
moduleIds: "size",
usedExports: true,
mangleExports: true
}
},
{
entry: "./example.js",
output: {
pathinfo: true,
filename: "without.js"
},
optimization: {
moduleIds: "size",
usedExports: false,
mangleExports: false
}
}
];

View File

@ -1,233 +0,0 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const RuntimeGlobals = require("./RuntimeGlobals");
const RuntimeModule = require("./RuntimeModule");
const Template = require("./Template");
const ModuleDecoratorDependency = require("./dependencies/ModuleDecoratorDependency");
const RuntimeRequirementsDependency = require("./dependencies/RuntimeRequirementsDependency");
const {
evaluateToIdentifier,
expressionIsUnsupported,
toConstantDependency
} = require("./javascript/JavascriptParserHelpers");
class CommonJsStuffPlugin {
apply(compiler) {
compiler.hooks.compilation.tap(
"CommonJsStuffPlugin",
(compilation, { normalModuleFactory }) => {
compilation.dependencyFactories.set(
ModuleDecoratorDependency,
normalModuleFactory
);
compilation.dependencyTemplates.set(
ModuleDecoratorDependency,
new ModuleDecoratorDependency.Template()
);
compilation.hooks.runtimeRequirementInModule
.for(RuntimeGlobals.harmonyModuleDecorator)
.tap("CommonJsStuffPlugin", (module, set) => {
set.add(RuntimeGlobals.module);
set.add(RuntimeGlobals.requireScope);
});
compilation.hooks.runtimeRequirementInModule
.for(RuntimeGlobals.nodeModuleDecorator)
.tap("CommonJsStuffPlugin", (module, set) => {
set.add(RuntimeGlobals.module);
set.add(RuntimeGlobals.requireScope);
});
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.harmonyModuleDecorator)
.tap("CommonJsStuffPlugin", (chunk, set) => {
compilation.addRuntimeModule(
chunk,
new HarmonyModuleDecoratorRuntimeModule()
);
});
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.nodeModuleDecorator)
.tap("CommonJsStuffPlugin", (chunk, set) => {
compilation.addRuntimeModule(
chunk,
new NodeModuleDecoratorRuntimeModule()
);
});
const handler = (parser, parserOptions) => {
parser.hooks.expression
.for("require.main.require")
.tap(
"CommonJsStuffPlugin",
expressionIsUnsupported(
parser,
"require.main.require is not supported by webpack."
)
);
parser.hooks.expression
.for("module.parent.require")
.tap(
"CommonJsStuffPlugin",
expressionIsUnsupported(
parser,
"module.parent.require is not supported by webpack."
)
);
parser.hooks.expression
.for("require.main")
.tap(
"CommonJsStuffPlugin",
toConstantDependency(
parser,
`${RuntimeGlobals.moduleCache}[${RuntimeGlobals.entryModuleId}]`,
[RuntimeGlobals.moduleCache, RuntimeGlobals.entryModuleId]
)
);
parser.hooks.expression
.for("module.loaded")
.tap("CommonJsStuffPlugin", expr => {
parser.state.module.buildMeta.moduleConcatenationBailout =
"module.loaded";
const dep = new RuntimeRequirementsDependency([
RuntimeGlobals.moduleLoaded
]);
dep.loc = expr.loc;
parser.state.module.addPresentationalDependency(dep);
return true;
});
parser.hooks.expression
.for("module.id")
.tap("CommonJsStuffPlugin", expr => {
parser.state.module.buildMeta.moduleConcatenationBailout =
"module.id";
const dep = new RuntimeRequirementsDependency([
RuntimeGlobals.moduleId
]);
dep.loc = expr.loc;
parser.state.module.addPresentationalDependency(dep);
return true;
});
parser.hooks.expression
.for("module.exports")
.tap("CommonJsStuffPlugin", expr => {
const module = parser.state.module;
const isHarmony = parser.state.harmonyModule;
if (!isHarmony) {
if (module.moduleArgument === "module") {
// avoid rewriting module.exports for backward-compat
const dep = new RuntimeRequirementsDependency([
RuntimeGlobals.module
]);
dep.loc = expr.loc;
module.addPresentationalDependency(dep);
return true;
}
return toConstantDependency(
parser,
`${module.moduleArgument}.exports`,
[RuntimeGlobals.module]
)(expr);
}
});
parser.hooks.expression
.for("this")
.tap("CommonJsStuffPlugin", expr => {
if (!parser.scope.topLevelScope) return;
const isHarmony = parser.state.harmonyModule;
if (!isHarmony) {
return toConstantDependency(parser, "this", [
RuntimeGlobals.thisAsExports
])(expr);
}
});
parser.hooks.evaluateIdentifier.for("module.hot").tap(
"CommonJsStuffPlugin",
evaluateToIdentifier("module.hot", "module", () => ["hot"], false)
);
parser.hooks.expression
.for("module")
.tap("CommonJsStuffPlugin", expr => {
const isHarmony = parser.state.harmonyModule;
const dep = new ModuleDecoratorDependency(
isHarmony
? RuntimeGlobals.harmonyModuleDecorator
: RuntimeGlobals.nodeModuleDecorator
);
dep.loc = expr.loc;
parser.state.module.addPresentationalDependency(dep);
return true;
});
};
normalModuleFactory.hooks.parser
.for("javascript/auto")
.tap("CommonJsStuffPlugin", handler);
normalModuleFactory.hooks.parser
.for("javascript/dynamic")
.tap("CommonJsStuffPlugin", handler);
}
);
}
}
class HarmonyModuleDecoratorRuntimeModule extends RuntimeModule {
constructor() {
super("harmony module decorator");
}
/**
* @returns {string} runtime code
*/
generate() {
const { runtimeTemplate } = this.compilation;
return Template.asString([
`${
RuntimeGlobals.harmonyModuleDecorator
} = ${runtimeTemplate.basicFunction("module", [
"module = Object.create(module);",
"if (!module.children) module.children = [];",
"Object.defineProperty(module, 'exports', {",
Template.indent([
"enumerable: true,",
`set: ${runtimeTemplate.basicFunction("", [
"throw new Error('ES Modules may not assign module.exports or exports.*, Use ESM export syntax, instead: ' + module.id);"
])}`
]),
"});",
"return module;"
])};`
]);
}
}
class NodeModuleDecoratorRuntimeModule extends RuntimeModule {
constructor() {
super("node module decorator");
}
/**
* @returns {string} runtime code
*/
generate() {
const { runtimeTemplate } = this.compilation;
return Template.asString([
`${
RuntimeGlobals.nodeModuleDecorator
} = ${runtimeTemplate.basicFunction("module", [
"module.paths = [];",
"if (!module.children) module.children = [];",
"return module;"
])};`
]);
}
}
module.exports = CommonJsStuffPlugin;

View File

@ -462,13 +462,12 @@ class ContextModule extends Module {
return 9;
}
const moduleGraph = chunkGraph.moduleGraph;
// bitfield
let hasType = 0;
const comparator = compareModulesById(chunkGraph);
// if we filter first we get a new array
// therefor we dont need to create a clone of dependencies explicitly
// therefore the order of this is !important!
let hasNonHarmony = false;
let hasNamespace = false;
let hasNamed = false;
const comparator = compareModulesById(chunkGraph);
const fakeMap = dependencies
.map(dependency => ({
module: moduleGraph.getModule(dependency),
@ -477,30 +476,46 @@ class ContextModule extends Module {
.filter(item => item.module)
.sort((a, b) => comparator(a.module, b.module))
.reduce((map, { dependency: dep, module }) => {
const exportsType = module.buildMeta && module.buildMeta.exportsType;
const exportsType = module.getExportsType(
this.options.namespaceObject === "strict"
);
const id = chunkGraph.getModuleId(module);
if (!exportsType) {
map[id] = this.options.namespaceObject === "strict" ? 1 : 7;
hasNonHarmony = true;
} else if (exportsType === "namespace") {
map[id] = 9;
hasNamespace = true;
} else if (exportsType === "default") {
map[id] = this.options.namespaceObject === "strict" ? 1 : 3;
hasNamed = true;
switch (exportsType) {
case "namespace":
map[id] = 9;
hasType |= 1;
break;
case "dynamic":
map[id] = 7;
hasType |= 2;
break;
case "dynamic-default":
case "default-only":
map[id] = 1;
hasType |= 4;
break;
case "default-with-named":
map[id] = 3;
hasType |= 8;
break;
default:
throw new Error(`Unexpected exports type ${exportsType}`);
}
return map;
}, Object.create(null));
if (!hasNamespace && hasNonHarmony && !hasNamed) {
return this.options.namespaceObject === "strict" ? 1 : 7;
}
if (hasNamespace && !hasNonHarmony && !hasNamed) {
if (hasType === 1) {
return 9;
}
if (!hasNamespace && !hasNonHarmony && hasNamed) {
return this.options.namespaceObject === "strict" ? 1 : 3;
if (hasType === 2) {
return 7;
}
if (!hasNamespace && !hasNonHarmony && !hasNamed) {
if (hasType === 4) {
return 1;
}
if (hasType === 8) {
return 3;
}
if (hasType === 0) {
return 9;
}
return fakeMap;

View File

@ -65,8 +65,8 @@ const makeSerializable = require("./util/makeSerializable");
* @property {string=} exportsArgument
* @property {boolean=} strict
* @property {string=} moduleConcatenationBailout
* @property {("default" | "namespace")=} exportsType
* @property {(boolean | "redirect")=} defaultObject
* @property {("default" | "namespace" | "flagged")=} exportsType
* @property {(boolean | "redirect" | "redirect-warn")=} defaultObject
* @property {boolean=} strictHarmonyModule
* @property {boolean=} async
*/
@ -365,6 +365,29 @@ class Module extends DependenciesBlock {
return (this.buildInfo && this.buildInfo.moduleArgument) || "module";
}
/**
* @param {boolean} strict the importing module is strict
* @returns {"dynamic" | "dynamic-default" | "namespace" | "default-only" | "default-with-named"} export type
*/
getExportsType(strict) {
switch (this.buildMeta && this.buildMeta.exportsType) {
case "flagged":
return strict ? "dynamic-default" : "namespace";
case "namespace":
return "namespace";
case "default":
switch (this.buildMeta.defaultObject) {
case "redirect":
case "redirect-warn":
return strict ? "default-only" : "default-with-named";
default:
return "default-only";
}
default:
return strict ? "dynamic-default" : "dynamic";
}
}
/**
* @param {Dependency} presentationalDependency dependency being tied to module.
* This is a Dependency without edge in the module graph. It's only for presentation.

View File

@ -130,13 +130,8 @@ class ExportsInfo {
this._exportsAreOrdered = true;
}
setRedirectToDefaultObject() {
const defaultInfo = this.getExportInfo("default");
defaultInfo.canMangleProvide = false;
defaultInfo.provided = true;
defaultInfo.usedName = "";
const inner = defaultInfo.createNestedExportsInfo();
this._redirectTo = inner;
setRedirectNamedTo(exportsInfo) {
this._redirectTo = exportsInfo;
}
setHasProvideInfo() {
@ -958,8 +953,21 @@ class ModuleGraph {
* @returns {void}
*/
finishModule(module) {
if (module.buildMeta.defaultObject) {
this.getExportsInfo(module).setRedirectToDefaultObject();
if (module.buildMeta.exportsType === "default") {
const exportsInfo = this.getExportsInfo(module);
const defaultInfo = exportsInfo.getExportInfo("default");
defaultInfo.canMangleProvide = false;
defaultInfo.provided = true;
defaultInfo.usedName = "";
if (module.buildMeta.defaultObject) {
const innerObject = defaultInfo.createNestedExportsInfo();
if (
module.buildMeta.defaultObject === "redirect" ||
module.buildMeta.defaultObject === "redirect-warn"
) {
exportsInfo.setRedirectNamedTo(innerObject);
}
}
}
}

View File

@ -79,6 +79,18 @@ class ModuleInfoHeaderPlugin {
source.add("/*!****" + reqStrStar + "****!*\\\n");
source.add(" !*** " + reqStr + " ***!\n");
source.add(" \\****" + reqStrStar + "****/\n");
const exportsType = module.buildMeta.exportsType;
source.add(
Template.toComment(
exportsType
? `exports type: ${exportsType}${
exportsType === "default"
? ` defaultObject: ${module.buildMeta.defaultObject}`
: ""
}`
: "dynamic exports type"
) + "\n"
);
const exportsInfo = moduleGraph.getExportsInfo(module);
printExportsInfoToSource(source, "", exportsInfo);
source.add(

View File

@ -326,25 +326,26 @@ class RuntimeTemplate {
request,
weak
});
const exportsType = module.buildMeta && module.buildMeta.exportsType;
if (exportsType === "namespace") {
const rawModule = this.moduleRaw({
module,
chunkGraph,
request,
weak,
runtimeRequirements
});
return rawModule;
} else if (strict) {
runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
return `${RuntimeGlobals.createFakeNamespaceObject}(${moduleId}, 1)`;
} else if (exportsType === "default") {
runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
return `${RuntimeGlobals.createFakeNamespaceObject}(${moduleId}, 3)`;
} else {
runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
return `${RuntimeGlobals.createFakeNamespaceObject}(${moduleId}, 7)`;
const exportsType = module.getExportsType(strict);
switch (exportsType) {
case "namespace":
return this.moduleRaw({
module,
chunkGraph,
request,
weak,
runtimeRequirements
});
case "default-with-named":
runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
return `${RuntimeGlobals.createFakeNamespaceObject}(${moduleId}, 3)`;
case "default-only":
case "dynamic-default":
runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
return `${RuntimeGlobals.createFakeNamespaceObject}(${moduleId}, 1)`;
case "dynamic":
runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
return `${RuntimeGlobals.createFakeNamespaceObject}(${moduleId}, 7)`;
}
}
@ -427,54 +428,46 @@ class RuntimeTemplate {
request,
weak
});
const exportsType = module.buildMeta && module.buildMeta.exportsType;
if (exportsType === "namespace") {
if (header) {
const rawModule = this.moduleRaw({
module,
chunkGraph,
request,
weak,
runtimeRequirements
});
getModuleFunction = this.basicFunction(
"",
`${header}return ${rawModule};`
);
} else {
runtimeRequirements.add(RuntimeGlobals.require);
getModuleFunction = `__webpack_require__.bind(__webpack_require__, ${comment}${idExpr})`;
}
} else if (strict) {
runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
if (header) {
const returnExpression = `${RuntimeGlobals.createFakeNamespaceObject}(${moduleIdExpr}, 1)`;
getModuleFunction = header
? this.basicFunction("", `${header}return ${returnExpression};`)
: this.returningFunction(returnExpression);
} else {
getModuleFunction = `${RuntimeGlobals.createFakeNamespaceObject}.bind(__webpack_require__, ${comment}${idExpr}, 1)`;
}
} else if (exportsType === "default") {
runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
if (header) {
const returnExpression = `${RuntimeGlobals.createFakeNamespaceObject}(${moduleIdExpr}, 3)`;
getModuleFunction = header
? this.basicFunction("", `${header}return ${returnExpression};`)
: this.returningFunction(returnExpression);
} else {
getModuleFunction = `${RuntimeGlobals.createFakeNamespaceObject}.bind(__webpack_require__, ${comment}${idExpr}, 3)`;
}
} else {
runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
if (header) {
const returnExpression = `${RuntimeGlobals.createFakeNamespaceObject}(${moduleIdExpr}, 7)`;
getModuleFunction = header
? this.basicFunction("", `${header}return ${returnExpression};`)
: this.returningFunction(returnExpression);
} else {
getModuleFunction = `${RuntimeGlobals.createFakeNamespaceObject}.bind(__webpack_require__, ${comment}${idExpr}, 7)`;
}
const exportsType = module.getExportsType(strict);
let fakeType = 0;
switch (exportsType) {
case "namespace":
if (header) {
const rawModule = this.moduleRaw({
module,
chunkGraph,
request,
weak,
runtimeRequirements
});
getModuleFunction = this.basicFunction(
"",
`${header}return ${rawModule};`
);
} else {
runtimeRequirements.add(RuntimeGlobals.require);
getModuleFunction = `__webpack_require__.bind(__webpack_require__, ${comment}${idExpr})`;
}
break;
case "dynamic":
fakeType |= 7;
/* fall through */
case "default-with-named":
fakeType |= 3;
/* fall through */
case "default-only":
case "dynamic-default":
fakeType |= 1;
runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
if (header) {
const returnExpression = `${RuntimeGlobals.createFakeNamespaceObject}(${moduleIdExpr}, ${fakeType})`;
getModuleFunction = header
? this.basicFunction("", `${header}return ${returnExpression};`)
: this.returningFunction(returnExpression);
} else {
getModuleFunction = `${RuntimeGlobals.createFakeNamespaceObject}.bind(__webpack_require__, ${comment}${idExpr}, ${fakeType})`;
}
break;
}
return `${promise || "Promise.resolve()"}.then(${getModuleFunction})`;
@ -552,6 +545,7 @@ class RuntimeTemplate {
* @param {boolean} options.asiSafe true, if location is safe for ASI, a bracket can be emitted
* @param {boolean} options.isCall true, if expression will be called
* @param {boolean} options.callContext when false, call context will not be preserved
* @param {boolean} options.defaultInterop when true and accessing the default exports, interop code will be generated
* @param {string} options.importVar the identifier name of the import variable
* @param {InitFragment[]} options.initFragments init fragments will be added here
* @param {Set<string>} options.runtimeRequirements if set, will be filled with runtime requirements
@ -566,6 +560,7 @@ class RuntimeTemplate {
asiSafe,
isCall,
callContext,
defaultInterop,
importVar,
initFragments,
runtimeRequirements
@ -580,26 +575,43 @@ class RuntimeTemplate {
}
const exportsType = module.buildMeta && module.buildMeta.exportsType;
if (!exportsType) {
if (exportName.length > 0 && exportName[0] === "default") {
if (!originModule.buildMeta.strictHarmonyModule) {
if (isCall) {
return `${importVar}_default()${propertyAccess(exportName, 1)}`;
} else if (asiSafe) {
return `(${importVar}_default()${propertyAccess(exportName, 1)})`;
if (defaultInterop) {
if (!exportsType) {
if (exportName.length > 0 && exportName[0] === "default") {
if (!originModule.buildMeta.strictHarmonyModule) {
if (isCall) {
return `${importVar}_default()${propertyAccess(exportName, 1)}`;
} else if (asiSafe) {
return `(${importVar}_default()${propertyAccess(exportName, 1)})`;
} else {
return `${importVar}_default.a${propertyAccess(exportName, 1)}`;
}
} else {
return `${importVar}_default.a${propertyAccess(exportName, 1)}`;
return `${importVar}${propertyAccess(exportName, 1)}`;
}
} else if (originModule.buildMeta.strictHarmonyModule) {
if (exportName.length > 0) {
return (
"/* non-default import from non-esm module */undefined" +
propertyAccess(exportName, 1)
);
} else {
runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
initFragments.push(
new InitFragment(
`var ${importVar}_namespace_cache;\n`,
InitFragment.STAGE_CONSTANTS,
-1,
`${importVar}_namespace_cache`
)
);
return `/*#__PURE__*/ (${importVar}_namespace_cache || (${importVar}_namespace_cache = ${RuntimeGlobals.createFakeNamespaceObject}(${importVar})))`;
}
} else {
return `${importVar}${propertyAccess(exportName, 1)}`;
}
} else if (originModule.buildMeta.strictHarmonyModule) {
if (exportName.length > 0) {
return (
"/* non-default import from non-esm module */undefined" +
propertyAccess(exportName, 1)
);
} else {
}
if (exportsType === "default") {
if (exportName.length === 0) {
runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
initFragments.push(
new InitFragment(
@ -609,30 +621,20 @@ class RuntimeTemplate {
`${importVar}_namespace_cache`
)
);
return `/*#__PURE__*/ (${importVar}_namespace_cache || (${importVar}_namespace_cache = ${RuntimeGlobals.createFakeNamespaceObject}(${importVar})))`;
const content = `${importVar}_namespace_cache || (${importVar}_namespace_cache = ${
RuntimeGlobals.createFakeNamespaceObject
}(${importVar}, ${
originModule.buildMeta.strictHarmonyModule ? 0 : 2
}))`;
if (asiSafe) {
return `/*#__PURE__*/ (${content})`;
} else {
return `/*#__PURE__*/ Object(${content})`;
}
}
}
}
if (exportsType === "default") {
if (exportName.length === 0) {
runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
initFragments.push(
new InitFragment(
`var ${importVar}_namespace_cache;\n`,
InitFragment.STAGE_CONSTANTS,
-1,
`${importVar}_namespace_cache`
)
);
return `/*#__PURE__*/ (${importVar}_namespace_cache || (${importVar}_namespace_cache = ${
RuntimeGlobals.createFakeNamespaceObject
}(${importVar}, ${
originModule.buildMeta.strictHarmonyModule ? 0 : 2
})))`;
}
}
if (exportName.length > 0) {
const exportsInfo = moduleGraph.getExportsInfo(module);
const used = exportsInfo.getUsedName(exportName);

21
lib/SelfModuleFactory.js Normal file
View File

@ -0,0 +1,21 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
class SelfModuleFactory {
constructor(moduleGraph) {
this.moduleGraph = moduleGraph;
}
create(data, callback) {
const module = this.moduleGraph.getParentModule(data.dependencies[0]);
callback(null, {
module
});
}
}
module.exports = SelfModuleFactory;

View File

@ -18,7 +18,6 @@ const RecordIdsPlugin = require("./RecordIdsPlugin");
const RuntimePlugin = require("./RuntimePlugin");
const APIPlugin = require("./APIPlugin");
const CommonJsStuffPlugin = require("./CommonJsStuffPlugin");
const CompatibilityPlugin = require("./CompatibilityPlugin");
const ConstPlugin = require("./ConstPlugin");
const ExportsInfoApiPlugin = require("./ExportsInfoApiPlugin");
@ -341,7 +340,6 @@ class WebpackOptionsApply extends OptionsApply {
const NodeStuffPlugin = require("./NodeStuffPlugin");
new NodeStuffPlugin(options.node).apply(compiler);
}
new CommonJsStuffPlugin().apply(compiler);
new APIPlugin().apply(compiler);
new ExportsInfoApiPlugin().apply(compiler);
new ConstPlugin().apply(compiler);

View File

@ -0,0 +1,134 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const InitFragment = require("../InitFragment");
const { UsageState } = require("../ModuleGraph");
const RuntimeGlobals = require("../RuntimeGlobals");
const makeSerializable = require("../util/makeSerializable");
const propertyAccess = require("../util/propertyAccess");
const NullDependency = require("./NullDependency");
/** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */
/** @typedef {import("../Dependency")} Dependency */
/** @typedef {import("../Dependency").ExportsSpec} ExportsSpec */
/** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */
/** @typedef {import("../ModuleGraph")} ModuleGraph */
class CommonJsExportsDependency extends NullDependency {
constructor(range, base, names) {
super();
this.range = range;
this.base = base;
this.names = names;
}
get type() {
return "cjs exports";
}
/**
* Returns the exported names
* @param {ModuleGraph} moduleGraph module graph
* @returns {ExportsSpec | undefined} export names
*/
getExports(moduleGraph) {
return {
exports: [this.names[0]],
dependencies: undefined
};
}
serialize(context) {
const { write } = context;
write(this.range);
write(this.base);
write(this.names);
super.serialize(context);
}
deserialize(context) {
const { read } = context;
this.range = read();
this.base = read();
this.names = read();
super.deserialize(context);
}
}
makeSerializable(
CommonJsExportsDependency,
"webpack/lib/dependencies/CommonJsExportsDependency"
);
CommonJsExportsDependency.Template = class CommonJsExportsDependencyTemplate extends NullDependency.Template {
/**
* @param {Dependency} dependency the dependency for which the template should be applied
* @param {ReplaceSource} source the current replace source which can be modified
* @param {DependencyTemplateContext} templateContext the context object
* @returns {void}
*/
apply(
dependency,
source,
{ module, moduleGraph, initFragments, runtimeRequirements }
) {
const dep = /** @type {CommonJsExportsDependency} */ (dependency);
let used;
if (module.buildMeta.exportsType === "default") {
const defaultInfo = moduleGraph.getExportInfo(module, "default");
if (defaultInfo.used === UsageState.Used) {
used = dep.names;
} else {
used = defaultInfo.exportsInfo.getUsedName(dep.names);
}
} else {
used = moduleGraph.getExportsInfo(module).getUsedName(dep.names);
}
if (!used) {
initFragments.push(
new InitFragment(
"var __webpack_unused_export__;\n",
InitFragment.STAGE_CONSTANTS,
0,
"__webpack_unused_export__"
)
);
source.replace(
dep.range[0],
dep.range[1] - 1,
"__webpack_unused_export__"
);
return;
}
let base = undefined;
switch (dep.base) {
case "exports":
runtimeRequirements.add(RuntimeGlobals.exports);
base = module.exportsArgument;
break;
case "module.exports":
runtimeRequirements.add(RuntimeGlobals.module);
base = `${module.moduleArgument}.exports`;
break;
case "this":
runtimeRequirements.add(RuntimeGlobals.thisAsExports);
base = "this";
break;
default:
throw new Error(`Unsupported base ${dep.base}`);
}
source.replace(
dep.range[0],
dep.range[1] - 1,
`${base}${propertyAccess(used)}`
);
}
};
module.exports = CommonJsExportsDependency;

View File

@ -0,0 +1,161 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const RuntimeGlobals = require("../RuntimeGlobals");
const { evaluateToString } = require("../javascript/JavascriptParserHelpers");
const CommonJsExportsDependency = require("./CommonJsExportsDependency");
const CommonJsSelfReferenceDependency = require("./CommonJsSelfReferenceDependency");
const ModuleDecoratorDependency = require("./ModuleDecoratorDependency");
/** @typedef {import("../javascript/JavascriptParser")} JavascriptParser */
class CommonJsExportsDependencyParserPlugin {
/**
* @param {JavascriptParser} parser the parser
*/
apply(parser) {
const bailedOut = new WeakSet();
const enableModuleExports = () => {
if (!parser.state.module.buildMeta.exportsType) {
if (bailedOut.has(parser.state)) return;
parser.state.module.buildMeta.exportsType = "default";
parser.state.module.buildMeta.defaultObject = "redirect";
}
};
const checkNamespace = (members, valueExpr) => {
if (members.length > 0 && members[0] === "__esModule") {
if (
valueExpr &&
valueExpr.type === "Literal" &&
valueExpr.value === true
) {
parser.state.module.buildMeta.exportsType = "flagged";
} else {
bailoutModuleExports();
}
}
};
const bailoutModuleExports = () => {
bailedOut.add(parser.state);
parser.state.module.buildMeta.exportsType = undefined;
parser.state.module.buildMeta.defaultObject = false;
};
// metadata //
parser.hooks.evaluateTypeof
.for("module")
.tap("CommonJsExportsDependencyParserPlugin", evaluateToString("object"));
parser.hooks.evaluateTypeof
.for("exports")
.tap("CommonJsPlugin", evaluateToString("object"));
// exporting //
parser.hooks.assignMemberChain
.for("exports")
.tap("CommonJsExportsDependencyParserPlugin", (expr, members) => {
if (parser.state.harmonyModule) return;
enableModuleExports();
checkNamespace(members, expr.right);
const dep = new CommonJsExportsDependency(
expr.left.range,
"exports",
members
);
dep.loc = expr.loc;
parser.state.module.addDependency(dep);
return true;
});
parser.hooks.assignMemberChain
.for("this")
.tap("CommonJsExportsDependencyParserPlugin", (expr, members) => {
if (parser.state.harmonyModule) return;
if (!parser.scope.topLevelScope) return;
enableModuleExports();
checkNamespace(members, expr.right);
const dep = new CommonJsExportsDependency(
expr.left.range,
"this",
members
);
dep.loc = expr.loc;
parser.state.module.addDependency(dep);
return true;
});
parser.hooks.assignMemberChain
.for("module")
.tap("CommonJsExportsDependencyParserPlugin", (expr, members) => {
if (parser.state.harmonyModule) return;
if (members[0] !== "exports" || members.length <= 1) return;
enableModuleExports();
checkNamespace(members, expr.right);
const dep = new CommonJsExportsDependency(
expr.left.range,
"module.exports",
members.slice(1)
);
dep.loc = expr.loc;
parser.state.module.addDependency(dep);
return true;
});
// Self reference //
parser.hooks.expression
.for("exports")
.tap("CommonJsExportsDependencyParserPlugin", expr => {
if (parser.state.harmonyModule) return;
bailoutModuleExports();
const dep = new CommonJsSelfReferenceDependency(
expr.range,
"exports",
[]
);
dep.loc = expr.loc;
parser.state.module.addDependency(dep);
return true;
});
parser.hooks.expression
.for("module.exports")
.tap("CommonJsExportsDependencyParserPlugin", expr => {
if (parser.state.harmonyModule) return;
bailoutModuleExports();
const dep = new CommonJsSelfReferenceDependency(
expr.range,
"module.exports",
[]
);
dep.loc = expr.loc;
parser.state.module.addDependency(dep);
return true;
});
parser.hooks.expression
.for("this")
.tap("CommonJsExportsDependencyParserPlugin", expr => {
if (parser.state.harmonyModule) return;
if (!parser.scope.topLevelScope) return;
bailoutModuleExports();
const dep = new CommonJsSelfReferenceDependency(expr.range, "this", []);
dep.loc = expr.loc;
parser.state.module.addDependency(dep);
return true;
});
// Bailouts //
parser.hooks.expression.for("module").tap("CommonJsPlugin", expr => {
bailoutModuleExports();
const isHarmony = parser.state.harmonyModule;
const dep = new ModuleDecoratorDependency(
isHarmony
? RuntimeGlobals.harmonyModuleDecorator
: RuntimeGlobals.nodeModuleDecorator
);
dep.loc = expr.loc;
parser.state.module.addDependency(dep);
return true;
});
}
}
module.exports = CommonJsExportsDependencyParserPlugin;

View File

@ -0,0 +1,121 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const makeSerializable = require("../util/makeSerializable");
const ModuleDependency = require("./ModuleDependency");
/** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */
/** @typedef {import("../Dependency")} Dependency */
/** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */
/** @typedef {import("../ModuleGraph")} ModuleGraph */
class CommonJsFullRequireDependency extends ModuleDependency {
/**
* @param {string} request the request string
* @param {[number, number]} range location in source code
* @param {string[]} names accessed properties on module
*/
constructor(request, range, names) {
super(request);
this.range = range;
this.names = names;
this.call = false;
this.asiSafe = false;
}
/**
* Returns list of exports referenced by this dependency
* @param {ModuleGraph} moduleGraph module graph
* @returns {string[][]} referenced exports
*/
getReferencedExports(moduleGraph) {
if (this.call) {
const importedModule = moduleGraph.getModule(this);
if (
!importedModule ||
importedModule.getExportsType(false) !== "namespace"
) {
return [this.names.slice(0, -1)];
}
}
return [this.names];
}
serialize(context) {
const { write } = context;
write(this.names);
write(this.call);
write(this.asiSafe);
super.serialize(context);
}
deserialize(context) {
const { read } = context;
this.names = read();
this.call = read();
this.asiSafe = read();
super.deserialize(context);
}
get type() {
return "cjs full require";
}
}
CommonJsFullRequireDependency.Template = class CommonJsFullRequireDependencyTemplate extends ModuleDependency.Template {
/**
* @param {Dependency} dependency the dependency for which the template should be applied
* @param {ReplaceSource} source the current replace source which can be modified
* @param {DependencyTemplateContext} templateContext the context object
* @returns {void}
*/
apply(
dependency,
source,
{
module,
runtimeTemplate,
moduleGraph,
chunkGraph,
runtimeRequirements,
initFragments
}
) {
const dep = /** @type {CommonJsFullRequireDependency} */ (dependency);
if (!dep.range) return;
const importedModule = moduleGraph.getModule(dep);
const exports = runtimeTemplate.moduleExports({
module: importedModule,
chunkGraph,
request: dep.request,
weak: dep.weak,
runtimeRequirements
});
const exportExpr = runtimeTemplate.exportFromImport({
moduleGraph,
module: importedModule,
request: dep.request,
exportName: dep.names,
originModule: module,
asiSafe: dep.asiSafe,
isCall: dep.call,
callContext: undefined,
defaultInterop: false,
importVar: exports,
initFragments,
runtimeRequirements
});
source.replace(dep.range[0], dep.range[1] - 1, exportExpr);
}
};
makeSerializable(
CommonJsFullRequireDependency,
"webpack/lib/dependencies/CommonJsFullRequireDependency"
);
module.exports = CommonJsFullRequireDependency;

View File

@ -0,0 +1,322 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const RuntimeGlobals = require("../RuntimeGlobals");
const {
evaluateToIdentifier,
evaluateToString,
expressionIsUnsupported,
toConstantDependency
} = require("../javascript/JavascriptParserHelpers");
const CommonJsFullRequireDependency = require("./CommonJsFullRequireDependency");
const CommonJsRequireContextDependency = require("./CommonJsRequireContextDependency");
const CommonJsRequireDependency = require("./CommonJsRequireDependency");
const ConstDependency = require("./ConstDependency");
const ContextDependencyHelpers = require("./ContextDependencyHelpers");
const LocalModuleDependency = require("./LocalModuleDependency");
const { getLocalModule } = require("./LocalModulesHelpers");
const RequireHeaderDependency = require("./RequireHeaderDependency");
const RequireResolveContextDependency = require("./RequireResolveContextDependency");
const RequireResolveDependency = require("./RequireResolveDependency");
const RequireResolveHeaderDependency = require("./RequireResolveHeaderDependency");
class CommonJsImportsParserPlugin {
constructor(options) {
this.options = options;
}
apply(parser) {
const options = this.options;
// metadata //
const tapRequireExpression = (expression, getMembers) => {
parser.hooks.typeof
.for(expression)
.tap(
"CommonJsPlugin",
toConstantDependency(parser, JSON.stringify("function"))
);
parser.hooks.evaluateTypeof
.for(expression)
.tap("CommonJsPlugin", evaluateToString("function"));
parser.hooks.evaluateIdentifier
.for(expression)
.tap(
"CommonJsPlugin",
evaluateToIdentifier(expression, "require", getMembers, true)
);
};
tapRequireExpression("require", () => []);
tapRequireExpression("require.resolve", () => ["resolve"]);
tapRequireExpression("require.resolveWeak", () => ["resolveWeak"]);
// Weird stuff //
parser.hooks.assign.for("require").tap("CommonJsPlugin", expr => {
// to not leak to global "require", we need to define a local require here.
const dep = new ConstDependency("var require;", 0);
dep.loc = expr.loc;
parser.state.module.addPresentationalDependency(dep);
return true;
});
// Unsupported //
parser.hooks.expression
.for("require.main.require")
.tap(
"CommonJsPlugin",
expressionIsUnsupported(
parser,
"require.main.require is not supported by webpack."
)
);
parser.hooks.expression
.for("module.parent.require")
.tap(
"CommonJsPlugin",
expressionIsUnsupported(
parser,
"module.parent.require is not supported by webpack."
)
);
// renaming //
parser.hooks.canRename.for("require").tap("CommonJsPlugin", () => true);
parser.hooks.rename.for("require").tap("CommonJsPlugin", expr => {
// To avoid "not defined" error, replace the value with undefined
const dep = new ConstDependency("undefined", expr.range);
dep.loc = expr.loc;
parser.state.module.addPresentationalDependency(dep);
return false;
});
// inspection //
parser.hooks.expression
.for("require.cache")
.tap(
"CommonJsImportsParserPlugin",
toConstantDependency(parser, RuntimeGlobals.moduleCache, [
RuntimeGlobals.moduleCache,
RuntimeGlobals.moduleId,
RuntimeGlobals.moduleLoaded
])
);
// require as expression //
parser.hooks.expression
.for("require")
.tap("CommonJsImportsParserPlugin", expr => {
const dep = new CommonJsRequireContextDependency(
{
request: options.unknownContextRequest,
recursive: options.unknownContextRecursive,
regExp: options.unknownContextRegExp,
mode: "sync"
},
expr.range
);
dep.critical =
options.unknownContextCritical &&
"require function is used in a way in which dependencies cannot be statically extracted";
dep.loc = expr.loc;
dep.optional = !!parser.scope.inTry;
parser.state.current.addDependency(dep);
return true;
});
// require //
const processRequireItem = (expr, param) => {
if (param.isString()) {
const dep = new CommonJsRequireDependency(param.string, param.range);
dep.loc = expr.loc;
dep.optional = !!parser.scope.inTry;
parser.state.current.addDependency(dep);
return true;
}
};
const processRequireContext = (expr, param) => {
const dep = ContextDependencyHelpers.create(
CommonJsRequireContextDependency,
expr.range,
param,
expr,
options,
{},
parser
);
if (!dep) return;
dep.loc = expr.loc;
dep.optional = !!parser.scope.inTry;
parser.state.current.addDependency(dep);
return true;
};
const createRequireHandler = callNew => expr => {
if (expr.arguments.length !== 1) return;
let localModule;
const param = parser.evaluateExpression(expr.arguments[0]);
if (param.isConditional()) {
let isExpression = false;
for (const p of param.options) {
const result = processRequireItem(expr, p);
if (result === undefined) {
isExpression = true;
}
}
if (!isExpression) {
const dep = new RequireHeaderDependency(expr.callee.range);
dep.loc = expr.loc;
parser.state.module.addPresentationalDependency(dep);
return true;
}
}
if (
param.isString() &&
(localModule = getLocalModule(parser.state, param.string))
) {
localModule.flagUsed();
const dep = new LocalModuleDependency(localModule, expr.range, callNew);
dep.loc = expr.loc;
parser.state.module.addPresentationalDependency(dep);
return true;
} else {
const result = processRequireItem(expr, param);
if (result === undefined) {
processRequireContext(expr, param);
} else {
const dep = new RequireHeaderDependency(expr.callee.range);
dep.loc = expr.loc;
parser.state.module.addPresentationalDependency(dep);
}
return true;
}
};
parser.hooks.call
.for("require")
.tap("CommonJsImportsParserPlugin", createRequireHandler(false));
parser.hooks.new
.for("require")
.tap("CommonJsImportsParserPlugin", createRequireHandler(true));
parser.hooks.call
.for("module.require")
.tap("CommonJsImportsParserPlugin", createRequireHandler(false));
parser.hooks.new
.for("module.require")
.tap("CommonJsImportsParserPlugin", createRequireHandler(true));
// require with property access //
const chainHandler = (expr, calleeMembers, callExpr, members) => {
if (callExpr.arguments.length !== 1) return;
const param = parser.evaluateExpression(callExpr.arguments[0]);
if (param.isString() && !getLocalModule(parser.state, param.string)) {
const dep = new CommonJsFullRequireDependency(
param.string,
expr.range,
members
);
dep.asiSafe = !parser.isAsiPosition(expr.range[0]);
dep.loc = expr.loc;
parser.state.module.addDependency(dep);
return true;
}
};
const callChainHandler = (expr, calleeMembers, callExpr, members) => {
if (callExpr.arguments.length !== 1) return;
const param = parser.evaluateExpression(callExpr.arguments[0]);
if (param.isString() && !getLocalModule(parser.state, param.string)) {
const dep = new CommonJsFullRequireDependency(
param.string,
expr.callee.range,
members
);
dep.call = true;
dep.asiSafe = !parser.isAsiPosition(expr.range[0]);
dep.loc = expr.callee.loc;
parser.state.module.addDependency(dep);
return true;
}
};
parser.hooks.memberChainOfCallMemberChain
.for("require")
.tap("CommonJsImportsParserPlugin", chainHandler);
parser.hooks.memberChainOfCallMemberChain
.for("module.require")
.tap("CommonJsImportsParserPlugin", chainHandler);
parser.hooks.callMemberChainOfCallMemberChain
.for("require")
.tap("CommonJsImportsParserPlugin", callChainHandler);
parser.hooks.callMemberChainOfCallMemberChain
.for("module.require")
.tap("CommonJsImportsParserPlugin", callChainHandler);
// require.resolve //
const processResolve = (expr, weak) => {
if (expr.arguments.length !== 1) return;
const param = parser.evaluateExpression(expr.arguments[0]);
if (param.isConditional()) {
for (const option of param.options) {
const result = processResolveItem(expr, option, weak);
if (result === undefined) {
processResolveContext(expr, option, weak);
}
}
const dep = new RequireResolveHeaderDependency(expr.callee.range);
dep.loc = expr.loc;
parser.state.module.addPresentationalDependency(dep);
return true;
} else {
const result = processResolveItem(expr, param, weak);
if (result === undefined) {
processResolveContext(expr, param, weak);
}
const dep = new RequireResolveHeaderDependency(expr.callee.range);
dep.loc = expr.loc;
parser.state.module.addPresentationalDependency(dep);
return true;
}
};
const processResolveItem = (expr, param, weak) => {
if (param.isString()) {
const dep = new RequireResolveDependency(param.string, param.range);
dep.loc = expr.loc;
dep.optional = !!parser.scope.inTry;
dep.weak = weak;
parser.state.current.addDependency(dep);
return true;
}
};
const processResolveContext = (expr, param, weak) => {
const dep = ContextDependencyHelpers.create(
RequireResolveContextDependency,
param.range,
param,
expr,
options,
{
mode: weak ? "weak" : "sync"
},
parser
);
if (!dep) return;
dep.loc = expr.loc;
dep.optional = !!parser.scope.inTry;
parser.state.current.addDependency(dep);
return true;
};
parser.hooks.call
.for("require.resolve")
.tap("RequireResolveDependencyParserPlugin", expr => {
return processResolve(expr, false);
});
parser.hooks.call
.for("require.resolveWeak")
.tap("RequireResolveDependencyParserPlugin", expr => {
return processResolve(expr, true);
});
}
}
module.exports = CommonJsImportsParserPlugin;

View File

@ -5,22 +5,27 @@
"use strict";
const RuntimeGlobals = require("../RuntimeGlobals");
const RuntimeModule = require("../RuntimeModule");
const SelfModuleFactory = require("../SelfModuleFactory");
const Template = require("../Template");
const CommonJsExportsDependency = require("./CommonJsExportsDependency");
const CommonJsFullRequireDependency = require("./CommonJsFullRequireDependency");
const CommonJsRequireContextDependency = require("./CommonJsRequireContextDependency");
const CommonJsRequireDependency = require("./CommonJsRequireDependency");
const ConstDependency = require("./ConstDependency");
const CommonJsSelfReferenceDependency = require("./CommonJsSelfReferenceDependency");
const ModuleDecoratorDependency = require("./ModuleDecoratorDependency");
const RequireHeaderDependency = require("./RequireHeaderDependency");
const RequireResolveContextDependency = require("./RequireResolveContextDependency");
const RequireResolveDependency = require("./RequireResolveDependency");
const RequireResolveHeaderDependency = require("./RequireResolveHeaderDependency");
const RuntimeRequirementsDependency = require("./RuntimeRequirementsDependency");
const CommonJsRequireDependencyParserPlugin = require("./CommonJsRequireDependencyParserPlugin");
const RequireResolveDependencyParserPlugin = require("./RequireResolveDependencyParserPlugin");
const RuntimeGlobals = require("../RuntimeGlobals");
const CommonJsExportsParserPlugin = require("./CommonJsExportsParserPlugin");
const CommonJsImportsParserPlugin = require("./CommonJsImportsParserPlugin");
const {
evaluateToIdentifier,
evaluateToString,
toConstantDependency
} = require("../javascript/JavascriptParserHelpers");
@ -43,6 +48,15 @@ class CommonJsPlugin {
new CommonJsRequireDependency.Template()
);
compilation.dependencyFactories.set(
CommonJsFullRequireDependency,
normalModuleFactory
);
compilation.dependencyTemplates.set(
CommonJsFullRequireDependency,
new CommonJsFullRequireDependency.Template()
);
compilation.dependencyFactories.set(
CommonJsRequireContextDependency,
contextModuleFactory
@ -80,72 +94,116 @@ class CommonJsPlugin {
new RequireHeaderDependency.Template()
);
compilation.dependencyTemplates.set(
CommonJsExportsDependency,
new CommonJsExportsDependency.Template()
);
const selfFactory = new SelfModuleFactory(compilation.moduleGraph);
compilation.dependencyFactories.set(
CommonJsSelfReferenceDependency,
selfFactory
);
compilation.dependencyTemplates.set(
CommonJsSelfReferenceDependency,
new CommonJsSelfReferenceDependency.Template()
);
compilation.dependencyFactories.set(
ModuleDecoratorDependency,
selfFactory
);
compilation.dependencyTemplates.set(
ModuleDecoratorDependency,
new ModuleDecoratorDependency.Template()
);
compilation.hooks.runtimeRequirementInModule
.for(RuntimeGlobals.harmonyModuleDecorator)
.tap("CommonJsPlugin", (module, set) => {
set.add(RuntimeGlobals.module);
set.add(RuntimeGlobals.requireScope);
});
compilation.hooks.runtimeRequirementInModule
.for(RuntimeGlobals.nodeModuleDecorator)
.tap("CommonJsPlugin", (module, set) => {
set.add(RuntimeGlobals.module);
set.add(RuntimeGlobals.requireScope);
});
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.harmonyModuleDecorator)
.tap("CommonJsPlugin", (chunk, set) => {
compilation.addRuntimeModule(
chunk,
new HarmonyModuleDecoratorRuntimeModule()
);
});
compilation.hooks.runtimeRequirementInTree
.for(RuntimeGlobals.nodeModuleDecorator)
.tap("CommonJsPlugin", (chunk, set) => {
compilation.addRuntimeModule(
chunk,
new NodeModuleDecoratorRuntimeModule()
);
});
const handler = (parser, parserOptions) => {
if (parserOptions.commonjs !== undefined && !parserOptions.commonjs)
return;
const tapRequireExpression = (expression, getMembers) => {
parser.hooks.typeof
.for(expression)
.tap(
"CommonJsPlugin",
toConstantDependency(parser, JSON.stringify("function"))
);
parser.hooks.evaluateTypeof
.for(expression)
.tap("CommonJsPlugin", evaluateToString("function"));
parser.hooks.evaluateIdentifier
.for(expression)
.tap(
"CommonJsPlugin",
evaluateToIdentifier(expression, "require", getMembers, true)
);
};
tapRequireExpression("require", () => []);
tapRequireExpression("require.resolve", () => ["resolve"]);
tapRequireExpression("require.resolveWeak", () => ["resolveWeak"]);
parser.hooks.evaluateTypeof
.for("module")
.tap("CommonJsPlugin", evaluateToString("object"));
parser.hooks.expression.for("exports").tap("CommonJsPlugin", expr => {
const module = parser.state.module;
const isHarmony = parser.state.harmonyModule;
if (!isHarmony) {
return toConstantDependency(parser, module.exportsArgument, [
RuntimeGlobals.exports
])(expr);
}
});
parser.hooks.assign.for("require").tap("CommonJsPlugin", expr => {
// to not leak to global "require", we need to define a local require here.
const dep = new ConstDependency("var require;", 0);
dep.loc = expr.loc;
parser.state.module.addPresentationalDependency(dep);
return true;
});
parser.hooks.canRename
.for("require")
.tap("CommonJsPlugin", () => true);
parser.hooks.rename.for("require").tap("CommonJsPlugin", expr => {
// To avoid "not defined" error, replace the value with undefined
const dep = new ConstDependency("undefined", expr.range);
dep.loc = expr.loc;
parser.state.module.addPresentationalDependency(dep);
return false;
});
parser.hooks.typeof
.for("module")
.tap(
"CommonJsPlugin",
toConstantDependency(parser, JSON.stringify("object"))
);
parser.hooks.evaluateTypeof
.for("exports")
.tap("CommonJsPlugin", evaluateToString("object"));
new CommonJsRequireDependencyParserPlugin(options).apply(parser);
new RequireResolveDependencyParserPlugin(options).apply(parser);
parser.hooks.expression
.for("require.main")
.tap(
"CommonJsPlugin",
toConstantDependency(
parser,
`${RuntimeGlobals.moduleCache}[${RuntimeGlobals.entryModuleId}]`,
[RuntimeGlobals.moduleCache, RuntimeGlobals.entryModuleId]
)
);
parser.hooks.expression
.for("module.loaded")
.tap("CommonJsPlugin", expr => {
parser.state.module.buildMeta.moduleConcatenationBailout =
"module.loaded";
const dep = new RuntimeRequirementsDependency([
RuntimeGlobals.moduleLoaded
]);
dep.loc = expr.loc;
parser.state.module.addPresentationalDependency(dep);
return true;
});
parser.hooks.expression
.for("module.id")
.tap("CommonJsPlugin", expr => {
parser.state.module.buildMeta.moduleConcatenationBailout =
"module.id";
const dep = new RuntimeRequirementsDependency([
RuntimeGlobals.moduleId
]);
dep.loc = expr.loc;
parser.state.module.addPresentationalDependency(dep);
return true;
});
parser.hooks.evaluateIdentifier.for("module.hot").tap(
"CommonJsPlugin",
evaluateToIdentifier("module.hot", "module", () => ["hot"], false)
);
new CommonJsImportsParserPlugin(options).apply(parser);
new CommonJsExportsParserPlugin().apply(parser);
};
normalModuleFactory.hooks.parser
@ -158,4 +216,57 @@ class CommonJsPlugin {
);
}
}
class HarmonyModuleDecoratorRuntimeModule extends RuntimeModule {
constructor() {
super("harmony module decorator");
}
/**
* @returns {string} runtime code
*/
generate() {
const { runtimeTemplate } = this.compilation;
return Template.asString([
`${
RuntimeGlobals.harmonyModuleDecorator
} = ${runtimeTemplate.basicFunction("module", [
"module = Object.create(module);",
"if (!module.children) module.children = [];",
"Object.defineProperty(module, 'exports', {",
Template.indent([
"enumerable: true,",
`set: ${runtimeTemplate.basicFunction("", [
"throw new Error('ES Modules may not assign module.exports or exports.*, Use ESM export syntax, instead: ' + module.id);"
])}`
]),
"});",
"return module;"
])};`
]);
}
}
class NodeModuleDecoratorRuntimeModule extends RuntimeModule {
constructor() {
super("node module decorator");
}
/**
* @returns {string} runtime code
*/
generate() {
const { runtimeTemplate } = this.compilation;
return Template.asString([
`${
RuntimeGlobals.nodeModuleDecorator
} = ${runtimeTemplate.basicFunction("module", [
"module.paths = [];",
"if (!module.children) module.children = [];",
"return module;"
])};`
]);
}
}
module.exports = CommonJsPlugin;

View File

@ -1,136 +0,0 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const RuntimeGlobals = require("../RuntimeGlobals");
const {
toConstantDependency
} = require("../javascript/JavascriptParserHelpers");
const CommonJsRequireContextDependency = require("./CommonJsRequireContextDependency");
const CommonJsRequireDependency = require("./CommonJsRequireDependency");
const ContextDependencyHelpers = require("./ContextDependencyHelpers");
const LocalModuleDependency = require("./LocalModuleDependency");
const { getLocalModule } = require("./LocalModulesHelpers");
const RequireHeaderDependency = require("./RequireHeaderDependency");
class CommonJsRequireDependencyParserPlugin {
constructor(options) {
this.options = options;
}
apply(parser) {
const options = this.options;
const processItem = (expr, param) => {
if (param.isString()) {
const dep = new CommonJsRequireDependency(param.string, param.range);
dep.loc = expr.loc;
dep.optional = !!parser.scope.inTry;
parser.state.current.addDependency(dep);
return true;
}
};
const processContext = (expr, param) => {
const dep = ContextDependencyHelpers.create(
CommonJsRequireContextDependency,
expr.range,
param,
expr,
options,
{},
parser
);
if (!dep) return;
dep.loc = expr.loc;
dep.optional = !!parser.scope.inTry;
parser.state.current.addDependency(dep);
return true;
};
parser.hooks.expression
.for("require.cache")
.tap(
"CommonJsRequireDependencyParserPlugin",
toConstantDependency(parser, RuntimeGlobals.moduleCache, [
RuntimeGlobals.moduleCache
])
);
parser.hooks.expression
.for("require")
.tap("CommonJsRequireDependencyParserPlugin", expr => {
const dep = new CommonJsRequireContextDependency(
{
request: options.unknownContextRequest,
recursive: options.unknownContextRecursive,
regExp: options.unknownContextRegExp,
mode: "sync"
},
expr.range
);
dep.critical =
options.unknownContextCritical &&
"require function is used in a way in which dependencies cannot be statically extracted";
dep.loc = expr.loc;
dep.optional = !!parser.scope.inTry;
parser.state.current.addDependency(dep);
return true;
});
const createHandler = callNew => expr => {
if (expr.arguments.length !== 1) return;
let localModule;
const param = parser.evaluateExpression(expr.arguments[0]);
if (param.isConditional()) {
let isExpression = false;
for (const p of param.options) {
const result = processItem(expr, p);
if (result === undefined) {
isExpression = true;
}
}
if (!isExpression) {
const dep = new RequireHeaderDependency(expr.callee.range);
dep.loc = expr.loc;
parser.state.module.addPresentationalDependency(dep);
return true;
}
}
if (
param.isString() &&
(localModule = getLocalModule(parser.state, param.string))
) {
localModule.flagUsed();
const dep = new LocalModuleDependency(localModule, expr.range, callNew);
dep.loc = expr.loc;
parser.state.module.addPresentationalDependency(dep);
return true;
} else {
const result = processItem(expr, param);
if (result === undefined) {
processContext(expr, param);
} else {
const dep = new RequireHeaderDependency(expr.callee.range);
dep.loc = expr.loc;
parser.state.module.addPresentationalDependency(dep);
}
return true;
}
};
parser.hooks.call
.for("require")
.tap("CommonJsRequireDependencyParserPlugin", createHandler(false));
parser.hooks.new
.for("require")
.tap("CommonJsRequireDependencyParserPlugin", createHandler(true));
parser.hooks.call
.for("module.require")
.tap("CommonJsRequireDependencyParserPlugin", createHandler(false));
parser.hooks.new
.for("module.require")
.tap("CommonJsRequireDependencyParserPlugin", createHandler(true));
}
}
module.exports = CommonJsRequireDependencyParserPlugin;

View File

@ -0,0 +1,136 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const { UsageState } = require("../ModuleGraph");
const RuntimeGlobals = require("../RuntimeGlobals");
const makeSerializable = require("../util/makeSerializable");
const propertyAccess = require("../util/propertyAccess");
const NullDependency = require("./NullDependency");
/** @typedef {import("webpack-sources").ReplaceSource} ReplaceSource */
/** @typedef {import("../Dependency")} Dependency */
/** @typedef {import("../Dependency").ExportsSpec} ExportsSpec */
/** @typedef {import("../DependencyTemplate").DependencyTemplateContext} DependencyTemplateContext */
/** @typedef {import("../ModuleGraph")} ModuleGraph */
class CommonJsSelfReferenceDependency extends NullDependency {
constructor(range, base, names) {
super();
this.range = range;
this.base = base;
this.names = names;
}
get type() {
return "cjs self exports reference";
}
/**
* @returns {string | null} an identifier to merge equal requests
*/
getResourceIdentifier() {
return `self`;
}
/**
* Returns list of exports referenced by this dependency
* @param {ModuleGraph} moduleGraph module graph
* @returns {string[][]} referenced exports
*/
getReferencedExports(moduleGraph) {
return [this.names];
}
serialize(context) {
const { write } = context;
write(this.range);
write(this.base);
write(this.names);
super.serialize(context);
}
deserialize(context) {
const { read } = context;
this.range = read();
this.base = read();
this.names = read();
super.deserialize(context);
}
}
makeSerializable(
CommonJsSelfReferenceDependency,
"webpack/lib/dependencies/CommonJsSelfReferenceDependency"
);
CommonJsSelfReferenceDependency.Template = class CommonJsSelfReferenceDependencyTemplate extends NullDependency.Template {
/**
* @param {Dependency} dependency the dependency for which the template should be applied
* @param {ReplaceSource} source the current replace source which can be modified
* @param {DependencyTemplateContext} templateContext the context object
* @returns {void}
*/
apply(
dependency,
source,
{ module, moduleGraph, initFragments, runtimeRequirements }
) {
const dep = /** @type {CommonJsSelfReferenceDependency} */ (dependency);
let used;
if (dep.names.length === 0) {
used = dep.names;
} else if (module.buildMeta && module.buildMeta.exportsType === "default") {
const defaultInfo = moduleGraph.getExportInfo(module, "default");
if (defaultInfo.used === UsageState.Used) {
used = dep.names;
} else {
used = defaultInfo.exportsInfo.getUsedName(dep.names);
}
} else {
used = moduleGraph.getExportsInfo(module).getUsedName(dep.names);
}
if (!used) {
throw new Error(
"Self-reference dependency has unused export name: This should not happen"
);
}
let base = undefined;
switch (dep.base) {
case "exports":
runtimeRequirements.add(RuntimeGlobals.exports);
base = module.exportsArgument;
break;
case "module.exports":
runtimeRequirements.add(RuntimeGlobals.module);
base = `${module.moduleArgument}.exports`;
break;
case "this":
runtimeRequirements.add(RuntimeGlobals.thisAsExports);
base = "this";
break;
default:
throw new Error(`Unsupported base ${dep.base}`);
}
if (base === dep.base && used.join() === dep.names.join()) {
// Nothing has to be changed
// We don't use a replacement for compat reasons
// for plugins that update `module._source` which they
// shouldn't do!
return;
}
source.replace(
dep.range[0],
dep.range[1] - 1,
`/* self exports access */ ${base}${propertyAccess(used)}`
);
}
};
module.exports = CommonJsSelfReferenceDependency;

View File

@ -77,15 +77,13 @@ module.exports = class HarmonyDetectionParserPlugin {
});
const skipInHarmony = () => {
const module = parser.state.module;
if (module && module.buildMeta && module.buildMeta.exportsType) {
if (parser.state.harmonyModule) {
return true;
}
};
const nullInHarmony = () => {
const module = parser.state.module;
if (module && module.buildMeta && module.buildMeta.exportsType) {
if (parser.state.harmonyModule) {
return null;
}
};

View File

@ -29,7 +29,7 @@ const HarmonyImportDependency = require("./HarmonyImportDependency");
/** @typedef {import("../WebpackError")} WebpackError */
/** @typedef {import("../util/Hash")} Hash */
/** @typedef {"missing"|"unused"|"empty-star"|"reexport-non-harmony-default"|"reexport-named-default"|"reexport-namespace-object"|"reexport-fake-named-namespace-object"|"reexport-fake-namespace-object"|"reexport-non-harmony-undefined"|"normal-reexport"|"dynamic-reexport"} ExportModeType */
/** @typedef {"missing"|"unused"|"empty-star"|"reexport-dynamic-default"|"reexport-dynamic-default-default"|"reexport-named-default"|"reexport-namespace-object"|"reexport-fake-namespace-object"|"reexport-undefined"|"normal-reexport"|"dynamic-reexport"} ExportModeType */
const idsSymbol = Symbol("HarmonyExportImportedSpecifierDependency.ids");
@ -190,30 +190,35 @@ class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
return mode;
}
const strictHarmonyModule = parentModule.buildMeta.strictHarmonyModule;
const importedExportsType = importedModule.getExportsType(
parentModule.buildMeta.strictHarmonyModule
);
const isNotAHarmonyModule =
importedModule.buildMeta && !importedModule.buildMeta.exportsType;
const isNamedModule =
importedModule.buildMeta &&
importedModule.buildMeta.exportsType === "default";
const isDynamic = importedExportsType === "dynamic";
const isDynamicDefault = importedExportsType === "dynamic-default";
const isDefaultOnly = importedExportsType === "default-only";
const isDefaultWithNamed = importedExportsType === "default-with-named";
// Special handling for reexporting the default export
// from non-harmony modules
// from non-namespace modules
if (name && ids.length > 0 && ids[0] === "default") {
if (isNotAHarmonyModule) {
const mode = new ExportMode("reexport-non-harmony-default");
if (isDynamic) {
const mode = new ExportMode("reexport-dynamic-default");
mode.name = name;
return mode;
} else if (isNamedModule) {
} else if (isDefaultOnly || isDefaultWithNamed) {
const exportInfo = exportsInfo.getReadOnlyExportInfo(name);
const mode = new ExportMode("reexport-named-default");
mode.name = name;
mode.partialNamespaceExportInfo = exportInfo;
return mode;
} else if (isDynamicDefault) {
const mode = new ExportMode("reexport-dynamic-default-default");
mode.name = name;
return mode;
}
}
@ -225,8 +230,8 @@ class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
if (ids.length > 0) {
// export { name as name }
if ((isNotAHarmonyModule || isNamedModule) && strictHarmonyModule) {
mode = new ExportMode("reexport-non-harmony-undefined");
if (isDefaultOnly || isDynamicDefault) {
mode = new ExportMode("reexport-undefined");
mode.name = name;
} else {
mode = new ExportMode("normal-reexport");
@ -235,14 +240,16 @@ class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
}
} else {
// export { * as name }
if (isNotAHarmonyModule && strictHarmonyModule) {
if (isDefaultOnly || isDynamicDefault) {
mode = new ExportMode("reexport-fake-namespace-object");
mode.name = name;
} else if (isNamedModule) {
mode = new ExportMode("reexport-fake-named-namespace-object");
mode.partialNamespaceExportInfo = exportInfo;
mode.fakeType = 0;
} else if (isDefaultWithNamed) {
mode = new ExportMode("reexport-fake-namespace-object");
mode.name = name;
mode.partialNamespaceExportInfo = exportInfo;
mode.fakeType = strictHarmonyModule ? 0 : 2;
mode.fakeType = 2;
} else {
mode = new ExportMode("reexport-namespace-object");
mode.name = name;
@ -363,10 +370,10 @@ class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
case "missing":
case "unused":
case "empty-star":
case "reexport-non-harmony-undefined":
case "reexport-undefined":
return Dependency.NO_EXPORTS_REFERENCED;
case "reexport-non-harmony-default":
case "reexport-dynamic-default":
return Dependency.DEFAULT_EXPORT_REFERENCED;
case "reexport-named-default": {
@ -383,7 +390,7 @@ class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
}
case "reexport-namespace-object":
case "reexport-fake-named-namespace-object": {
case "reexport-fake-namespace-object": {
if (!mode.partialNamespaceExportInfo)
return Dependency.NS_OBJECT_REFERENCED;
/** @type {string[][]} */
@ -396,8 +403,8 @@ class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
return referencedExports;
}
case "reexport-fake-namespace-object":
case "dynamic-reexport":
case "reexport-dynamic-default-default":
return Dependency.NS_OBJECT_REFERENCED;
case "normal-reexport":
@ -468,14 +475,14 @@ class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
})),
dependencies: [moduleGraph.getModule(this)]
};
case "reexport-fake-namespace-object":
case "reexport-non-harmony-default":
case "reexport-non-harmony-undefined":
case "reexport-dynamic-default":
case "reexport-dynamic-default-default":
case "reexport-undefined":
return {
exports: [mode.name],
dependencies: [moduleGraph.getModule(this)]
};
case "reexport-fake-named-namespace-object":
case "reexport-fake-namespace-object":
case "reexport-namespace-object":
return {
exports: [
@ -678,23 +685,33 @@ HarmonyExportImportedSpecifierDependency.Template = class HarmonyExportImportedS
);
break;
case "reexport-non-harmony-default":
case "reexport-dynamic-default":
initFragments.push(
this.getReexportFragment(
module,
"reexport default from non-harmony",
"reexport default from dynamic",
module.getUsedName(moduleGraph, mode.name),
importVar,
module.buildMeta && module.buildMeta.strictHarmonyModule
? ""
: null,
null,
runtimeRequirements
)
);
break;
case "reexport-dynamic-default-default":
initFragments.push(
this.getReexportFragment(
module,
"reexport dynamic as default",
module.getUsedName(moduleGraph, mode.name),
importVar,
"",
runtimeRequirements
)
);
break;
case "reexport-fake-namespace-object":
case "reexport-fake-named-namespace-object":
initFragments.push(
...this.getReexportFakeNamespaceObjectFragments(
module,
@ -706,7 +723,7 @@ HarmonyExportImportedSpecifierDependency.Template = class HarmonyExportImportedS
);
break;
case "reexport-non-harmony-undefined":
case "reexport-undefined":
initFragments.push(
this.getReexportFragment(
module,

View File

@ -94,68 +94,68 @@ class HarmonyImportDependency extends ModuleDependency {
return;
}
const exportsType =
importedModule.buildMeta && importedModule.buildMeta.exportsType;
if (!exportsType) {
// It's not an harmony module
if (
ids.length > 0 &&
ids[0] !== "default" &&
moduleGraph.getParentModule(this).buildMeta.strictHarmonyModule
) {
// In strict harmony modules we only support the default export
const parentModule = moduleGraph.getParentModule(this);
const exportsType = importedModule.getExportsType(
parentModule.buildMeta.strictHarmonyModule
);
switch (exportsType) {
case "default-only":
case "dynamic-default":
// It's has only a default export
if (ids.length > 0 && ids[0] !== "default") {
// In strict harmony modules we only support the default export
return [
new HarmonyLinkingError(
`Can't import the named export ${ids
.map(id => `'${id}'`)
.join(
"."
)} ${additionalMessage} from default-exporting module (only default export is available)`
)
];
}
return;
case "default-with-named":
// It has a default export and named properties redirect
// In some cases we still want to warn here
if (
ids.length > 0 &&
ids[0] !== "default" &&
importedModule.buildMeta.defaultObject === "redirect-warn"
) {
// For these modules only the default export is supported
return [
new HarmonyLinkingError(
`Should not import the named export ${ids
.map(id => `'${id}'`)
.join(
"."
)} ${additionalMessage} from default-exporting module (only default export is available soon)`
)
];
}
return;
case "namespace":
if (ids.length === 0) {
return;
}
if (moduleGraph.isExportProvided(importedModule, ids) !== false) {
// It's provided or we are not sure
return;
}
// We are sure that it's not provided
return [
new HarmonyLinkingError(
`Can't import the named export ${ids
`export ${ids
.map(id => `'${id}'`)
.join(
"."
)} ${additionalMessage} from non EcmaScript module (only default export is available)`
.join(".")} ${additionalMessage} was not found in '${
this.userRequest
}'`
)
];
}
return;
} else if (exportsType === "default") {
if (
ids.length > 0 &&
ids[0] !== "default" &&
(moduleGraph.getParentModule(this).buildMeta.strictHarmonyModule
? importedModule.buildMeta.defaultObject !== "redirect"
: importedModule.buildMeta.defaultObject === false)
) {
// For these modules only the default export is supported
return [
new HarmonyLinkingError(
`Can't import the named export ${ids
.map(id => `'${id}'`)
.join(
"."
)} ${additionalMessage} from default-exporting module (only default export is available)`
)
];
}
return;
}
if (ids.length === 0) {
return;
}
if (moduleGraph.isExportProvided(importedModule, ids) !== false) {
// It's provided or we are not sure
return;
}
// We are sure that it's not provided
return [
new HarmonyLinkingError(
`export ${ids
.map(id => `'${id}'`)
.join(".")} ${additionalMessage} was not found in '${
this.userRequest
}'`
)
];
}
/**
@ -167,12 +167,15 @@ class HarmonyImportDependency extends ModuleDependency {
updateHash(hash, chunkGraph) {
super.updateHash(hash, chunkGraph);
const importedModule = chunkGraph.moduleGraph.getModule(this);
hash.update(
(importedModule &&
(!importedModule.buildMeta || importedModule.buildMeta.exportsType)) +
""
);
if (chunkGraph.moduleGraph.isAsync(importedModule)) hash.update("async");
if (importedModule) {
const parentModule = chunkGraph.moduleGraph.getParentModule(this);
hash.update(
importedModule.getExportsType(
parentModule.buildMeta && parentModule.buildMeta.strictHarmonyModule
)
);
if (chunkGraph.moduleGraph.isAsync(importedModule)) hash.update("async");
}
hash.update(`${this.sourceOrder}`);
if (this.await) hash.update("await");
}

View File

@ -171,9 +171,6 @@ class HarmonyImportSpecifierDependency extends HarmonyImportDependency {
if (importedModule) {
const exportsInfo = moduleGraph.getExportsInfo(importedModule);
hash.update(`${exportsInfo.getUsedName(ids)}`);
hash.update(
(!importedModule.buildMeta || importedModule.buildMeta.exportsType) + ""
);
}
}
@ -246,6 +243,7 @@ HarmonyImportSpecifierDependency.Template = class HarmonyImportSpecifierDependen
asiSafe: dep.asiSafe || dep.shorthand,
isCall: dep.call,
callContext: !dep.directImport,
defaultInterop: true,
importVar: dep.getImportVar(moduleGraph),
initFragments,
runtimeRequirements

View File

@ -18,6 +18,7 @@ class HarmonyTopLevelThisParserPlugin {
const dep = new ConstDependency("undefined", node.range, null);
dep.loc = node.loc;
parser.state.module.addPresentationalDependency(dep);
return this;
}
});
}

View File

@ -34,6 +34,22 @@ class ModuleDecoratorDependency extends NullDependency {
return "module decorator";
}
/**
* @returns {string | null} an identifier to merge equal requests
*/
getResourceIdentifier() {
return `self`;
}
/**
* Returns list of exports referenced by this dependency
* @param {ModuleGraph} moduleGraph module graph
* @returns {string[][]} referenced exports
*/
getReferencedExports(moduleGraph) {
return [[]];
}
/**
* Update the hash
* @param {Hash} hash hash to be updated

View File

@ -5,10 +5,13 @@
"use strict";
const Dependency = require("../Dependency");
const makeSerializable = require("../util/makeSerializable");
const ModuleDependency = require("./ModuleDependency");
const ModuleDependencyAsId = require("./ModuleDependencyTemplateAsId");
/** @typedef {import("../ModuleGraph")} ModuleGraph */
class RequireResolveDependency extends ModuleDependency {
constructor(request, range) {
super(request);
@ -20,6 +23,16 @@ class RequireResolveDependency extends ModuleDependency {
return "require.resolve";
}
/**
* Returns list of exports referenced by this dependency
* @param {ModuleGraph} moduleGraph module graph
* @returns {string[][]} referenced exports
*/
getReferencedExports(moduleGraph) {
// This doesn't use any export
return Dependency.NO_EXPORTS_REFERENCED;
}
serialize(context) {
const { write } = context;

View File

@ -1,87 +0,0 @@
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/
"use strict";
const ContextDependencyHelpers = require("./ContextDependencyHelpers");
const RequireResolveContextDependency = require("./RequireResolveContextDependency");
const RequireResolveDependency = require("./RequireResolveDependency");
const RequireResolveHeaderDependency = require("./RequireResolveHeaderDependency");
class RequireResolveDependencyParserPlugin {
constructor(options) {
this.options = options;
}
apply(parser) {
const options = this.options;
const process = (expr, weak) => {
if (expr.arguments.length !== 1) return;
const param = parser.evaluateExpression(expr.arguments[0]);
if (param.isConditional()) {
for (const option of param.options) {
const result = processItem(expr, option, weak);
if (result === undefined) {
processContext(expr, option, weak);
}
}
const dep = new RequireResolveHeaderDependency(expr.callee.range);
dep.loc = expr.loc;
parser.state.module.addPresentationalDependency(dep);
return true;
} else {
const result = processItem(expr, param, weak);
if (result === undefined) {
processContext(expr, param, weak);
}
const dep = new RequireResolveHeaderDependency(expr.callee.range);
dep.loc = expr.loc;
parser.state.module.addPresentationalDependency(dep);
return true;
}
};
const processItem = (expr, param, weak) => {
if (param.isString()) {
const dep = new RequireResolveDependency(param.string, param.range);
dep.loc = expr.loc;
dep.optional = !!parser.scope.inTry;
dep.weak = weak;
parser.state.current.addDependency(dep);
return true;
}
};
const processContext = (expr, param, weak) => {
const dep = ContextDependencyHelpers.create(
RequireResolveContextDependency,
param.range,
param,
expr,
options,
{
mode: weak ? "weak" : "sync"
},
parser
);
if (!dep) return;
dep.loc = expr.loc;
dep.optional = !!parser.scope.inTry;
parser.state.current.addDependency(dep);
return true;
};
parser.hooks.call
.for("require.resolve")
.tap("RequireResolveDependencyParserPlugin", expr => {
return process(expr, false);
});
parser.hooks.call
.for("require.resolveWeak")
.tap("RequireResolveDependencyParserPlugin", expr => {
return process(expr, true);
});
}
}
module.exports = RequireResolveDependencyParserPlugin;

View File

@ -10,6 +10,7 @@ const { SyncBailHook, HookMap } = require("tapable");
const vm = require("vm");
const Parser = require("../Parser");
const StackedMap = require("../util/StackedMap");
const memorize = require("../util/memorize");
const BasicEvaluatedExpression = require("./BasicEvaluatedExpression");
/** @typedef {import("acorn").Options} AcornOptions */
@ -41,6 +42,8 @@ const BasicEvaluatedExpression = require("./BasicEvaluatedExpression");
/** @typedef {import("../Parser").ParserState} ParserState */
/** @typedef {import("../Parser").PreparsedAst} PreparsedAst */
const EMPTY_ARRAY = [];
// Syntax: https://developer.mozilla.org/en/SpiderMonkey/Parser_API
const parser = AcornParser.extend(require("../parsing/importAwaitAcornPlugin"));
@ -84,6 +87,25 @@ const joinRanges = (startRange, endRange) => {
return [startRange[0], endRange[1]];
};
const objectAndMembersToName = (object, membersReversed) => {
let name = object;
for (let i = membersReversed.length - 1; i >= 0; i--) {
name = name + "." + membersReversed[i];
}
return name;
};
const getRootName = expression => {
switch (expression.type) {
case "Identifier":
return expression.name;
case "ThisExpression":
return "this";
default:
return undefined;
}
};
/** @type {AcornOptions} */
const defaultParserOptions = {
ranges: true,
@ -186,6 +208,10 @@ class JavascriptParser extends Parser {
rename: new HookMap(() => new SyncBailHook(["initExpression"])),
/** @type {HookMap<SyncBailHook<[import("estree").AssignmentExpression], boolean | void>>} */
assign: new HookMap(() => new SyncBailHook(["expression"])),
/** @type {HookMap<SyncBailHook<[import("estree").AssignmentExpression, string[]], boolean | void>>} */
assignMemberChain: new HookMap(
() => new SyncBailHook(["expression", "members"])
),
/** @type {HookMap<SyncBailHook<[ExpressionNode], boolean | void>>} */
typeof: new HookMap(() => new SyncBailHook(["expression"])),
/** @type {SyncBailHook<[ExpressionNode], boolean | void>} */
@ -194,15 +220,38 @@ class JavascriptParser extends Parser {
topLevelAwait: new SyncBailHook(["expression"]),
/** @type {HookMap<SyncBailHook<[ExpressionNode], boolean | void>>} */
call: new HookMap(() => new SyncBailHook(["expression"])),
/** @type {HookMap<SyncBailHook<[ExpressionNode, any[]], boolean | void>>} */
/** Something like "a.b()" */
/** @type {HookMap<SyncBailHook<[ExpressionNode, string[]], boolean | void>>} */
callMemberChain: new HookMap(
() => new SyncBailHook(["expression", "members"])
),
/** Something like "a.b().c.d" */
/** @type {HookMap<SyncBailHook<[ExpressionNode, string[], CallExpressionNode, string[]], boolean | void>>} */
memberChainOfCallMemberChain: new HookMap(
() =>
new SyncBailHook([
"expression",
"calleeMembers",
"callExpression",
"members"
])
),
/** Something like "a.b().c.d()"" */
/** @type {HookMap<SyncBailHook<[ExpressionNode, string[], CallExpressionNode, string[]], boolean | void>>} */
callMemberChainOfCallMemberChain: new HookMap(
() =>
new SyncBailHook([
"expression",
"calleeMembers",
"innerCallExpression",
"members"
])
),
/** @type {HookMap<SyncBailHook<[ExpressionNode], boolean | void>>} */
new: new HookMap(() => new SyncBailHook(["expression"])),
/** @type {HookMap<SyncBailHook<[ExpressionNode], boolean | void>>} */
expression: new HookMap(() => new SyncBailHook(["expression"])),
/** @type {HookMap<SyncBailHook<[ExpressionNode, any[]], boolean | void>>} */
/** @type {HookMap<SyncBailHook<[ExpressionNode, string[]], boolean | void>>} */
expressionMemberChain: new HookMap(
() => new SyncBailHook(["expression", "members"])
),
@ -219,6 +268,7 @@ class JavascriptParser extends Parser {
this.sourceType = sourceType;
/** @type {ScopeInfo} */
this.scope = undefined;
/** @type {ParserState} */
this.state = undefined;
this.comments = undefined;
this.semicolons = undefined;
@ -1996,6 +2046,23 @@ class JavascriptParser extends Parser {
}
});
this.walkPattern(expression.left);
} else if (expression.left.type === "MemberExpression") {
const exprName = this.getMemberExpressionInfo(expression.left, [
"expression"
]);
if (exprName) {
if (
this.callHooksForInfo(
this.hooks.assignMemberChain,
exprName.rootInfo,
expression,
exprName.getMembers()
)
) {
return;
}
}
this.walkExpression(expression.left);
} else {
this.walkExpression(expression.left);
}
@ -2140,6 +2207,22 @@ class JavascriptParser extends Parser {
// (function(…) { }(…))
this._walkIIFE(expression.callee, expression.arguments, null);
} else {
if (expression.callee.type === "MemberExpression") {
const exprInfo = this.getMemberExpressionInfo(expression.callee, [
"call"
]);
if (exprInfo && exprInfo.type === "call") {
const result = this.callHooksForInfo(
this.hooks.callMemberChainOfCallMemberChain,
exprInfo.rootInfo,
expression,
exprInfo.getCalleeMembers(),
exprInfo.call,
exprInfo.getMembers()
);
if (result === true) return;
}
}
const callee = this.evaluateExpression(expression.callee);
if (callee.isIdentifier()) {
const result1 = this.callHooksForInfo(
@ -2157,40 +2240,72 @@ class JavascriptParser extends Parser {
if (result2 === true) return;
}
if (expression.callee) this.walkExpression(expression.callee);
if (expression.callee) {
if (expression.callee.type === "MemberExpression") {
// because of call context we need to walk the call context as expression
this.walkExpression(expression.callee.object);
if (expression.callee.computed === true)
this.walkExpression(expression.callee.property);
} else {
this.walkExpression(expression.callee);
}
}
if (expression.arguments) this.walkExpressions(expression.arguments);
}
}
walkMemberExpression(expression) {
const exprName = this.getNameForExpression(expression);
if (exprName) {
this.walkMemberExpressionWithExpressionName(
expression,
exprName.name,
exprName.rootInfo,
exprName.getMembers()
);
return;
const exprInfo = this.getMemberExpressionInfo(expression, [
"call",
"expression"
]);
if (exprInfo) {
switch (exprInfo.type) {
case "expression": {
const members = exprInfo.getMembers();
const result = this.callHooksForInfo(
this.hooks.expressionMemberChain,
exprInfo.rootInfo,
expression,
members
);
if (result === true) return;
this.walkMemberExpressionWithExpressionName(
expression,
exprInfo.name,
exprInfo.rootInfo,
members
);
return;
}
case "call": {
const result = this.callHooksForInfo(
this.hooks.memberChainOfCallMemberChain,
exprInfo.rootInfo,
expression,
exprInfo.getCalleeMembers(),
exprInfo.call,
exprInfo.getMembers()
);
if (result === true) return;
// Fast skip over the member chain as we already called memberChainOfCallMemberChain
// and call computed property are literals anyway
this.walkExpression(exprInfo.call);
return;
}
}
}
this.walkExpression(expression.object);
if (expression.computed === true) this.walkExpression(expression.property);
}
walkMemberExpressionWithExpressionName(expression, name, rootInfo, members) {
const result1 = this.callHooksForInfo(
this.hooks.expressionMemberChain,
rootInfo,
expression,
members
);
if (result1 === true) return;
const result2 = this.callHooksForInfo(
const result = this.callHooksForInfo(
this.hooks.expression,
name,
expression
);
if (result2 === true) return;
if (result === true) return;
if (expression.object.type === "MemberExpression") {
// optimize case where expression.object is a MemberExpression too.
// we can keep info here when calling walkMemberExpression directly
@ -2219,7 +2334,7 @@ class JavascriptParser extends Parser {
}
callHooksForExpression(hookMap, expr, ...args) {
const exprName = this.getNameForExpression(expr);
const exprName = this.getMemberExpressionInfo(expr, ["expression"]);
if (exprName !== undefined) {
return this.callHooksForInfoWithFallback(
hookMap,
@ -2235,7 +2350,7 @@ class JavascriptParser extends Parser {
* @template T
* @template R
* @param {HookMap<SyncBailHook<T, R>>} hookMap hooks the should be called
* @param {ExpressionNode} expr expression info
* @param {MemberExpressionNode} expr expression info
* @param {function(string, string | ScopeInfo | VariableInfo, function(): string[]): any} fallback callback when variable in not handled by hooks
* @param {function(string): any} defined callback when variable is defined
* @param {AsArray<T>} args args for the hook
@ -2248,7 +2363,7 @@ class JavascriptParser extends Parser {
defined,
...args
) {
const exprName = this.getNameForExpression(expr);
const exprName = this.getMemberExpressionInfo(expr, ["expression"]);
if (exprName !== undefined) {
return this.callHooksForInfoWithFallback(
hookMap,
@ -2677,6 +2792,7 @@ class JavascriptParser extends Parser {
isStrict: false,
definitions: new StackedMap()
};
/** @type {ParserState} */
this.state = state;
this.comments = comments;
this.semicolons = semicolons;
@ -2691,6 +2807,7 @@ class JavascriptParser extends Parser {
}
this.hooks.finish.call(ast, comments);
this.scope = oldScope;
/** @type {ParserState} */
this.state = oldState;
this.comments = oldComments;
this.semicolons = oldSemicolons;
@ -2843,60 +2960,112 @@ class JavascriptParser extends Parser {
}
/**
* @param {ExpressionNode} expression an expression
* @returns {{ name: string, rootInfo: ExportedVariableInfo, getMembers: () => string[]}} name info
* @param {MemberExpressionNode} expression an member expression
* @returns {{ members: string[], object: ExpressionNode | SuperNode }} member names (reverse order) and remaining object
*/
getNameForExpression(expression) {
extractMemberExpressionChain(expression) {
/** @type {AnyNode} */
let expr = expression;
const exprName = [];
const members = [];
while (expr.type === "MemberExpression") {
if (expr.object.type === "Super") return undefined;
if (expr.computed) {
if (expr.property.type !== "Literal") break;
exprName.push(`${expr.property.value}`);
members.push(`${expr.property.value}`);
} else {
if (expr.property.type !== "Identifier") break;
exprName.push(expr.property.name);
members.push(expr.property.name);
}
expr = expr.object;
}
let rootName;
if (expr.type === "Identifier") {
rootName = expr.name;
} else if (expr.type === "ThisExpression") {
rootName = "this";
} else {
return undefined;
}
const rootInfo = this.getVariableInfo(rootName);
/** @type {string | ScopeInfo | true} */
let resolvedRoot;
if (rootInfo instanceof VariableInfo) {
resolvedRoot = rootInfo.freeName;
} else {
resolvedRoot = rootInfo;
}
if (typeof resolvedRoot !== "string") {
return undefined;
}
let name = resolvedRoot;
for (let i = exprName.length - 1; i >= 0; i--) {
name = name + "." + exprName[i];
}
let reversed = false;
return {
name,
rootInfo,
getMembers: () => {
if (!reversed) {
exprName.reverse();
reversed = true;
}
return exprName;
}
members,
object: expr
};
}
/**
* @param {string} varName variable name
* @returns {{name: string, info: VariableInfo | string}} name of the free variable and variable info for that
*/
getFreeInfoFromVariable(varName) {
const info = this.getVariableInfo(varName);
let name;
if (info instanceof VariableInfo) {
name = info.freeName;
if (typeof name !== "string") return undefined;
} else if (typeof info !== "string") {
return undefined;
} else {
name = info;
}
return { info, name };
}
/** @typedef {{ type: "call", call: CallExpressionNode, calleeName: string, rootInfo: string | VariableInfo, getCalleeMembers: () => string[], name: string, getMembers: () => string[]}} CallExpressionInfo */
/** @typedef {{ type: "expression", rootInfo: string | VariableInfo, name: string, getMembers: () => string[]}} ExpressionExpressionInfo */
/**
* @param {MemberExpressionNode} expression an member expression
* @param {("call"|"expression")[]} allowedTypes which types should be returned
* @returns {CallExpressionInfo | ExpressionExpressionInfo | undefined} expression info
*/
getMemberExpressionInfo(expression, allowedTypes) {
const possibleTypes = new Set(allowedTypes);
const { object, members } = this.extractMemberExpressionChain(expression);
switch (object.type) {
case "CallExpression": {
if (!possibleTypes.has("call")) return undefined;
let callee = object.callee;
let rootMembers = EMPTY_ARRAY;
if (callee.type === "MemberExpression") {
({
object: callee,
members: rootMembers
} = this.extractMemberExpressionChain(callee));
}
const rootName = getRootName(callee);
if (!rootName) return undefined;
const result = this.getFreeInfoFromVariable(rootName);
if (!result) return undefined;
const { info: rootInfo, name: resolvedRoot } = result;
const calleeName = objectAndMembersToName(resolvedRoot, rootMembers);
return {
type: "call",
call: object,
calleeName,
rootInfo,
getCalleeMembers: memorize(() => rootMembers.reverse()),
name: objectAndMembersToName(`${calleeName}()`, members),
getMembers: memorize(() => members.reverse())
};
}
case "Identifier":
case "ThisExpression": {
if (!possibleTypes.has("expression")) return undefined;
const rootName = getRootName(object);
if (!rootName) return undefined;
const result = this.getFreeInfoFromVariable(rootName);
if (!result) return undefined;
const { info: rootInfo, name: resolvedRoot } = result;
return {
type: "expression",
name: objectAndMembersToName(resolvedRoot, members),
rootInfo,
getMembers: memorize(() => members.reverse())
};
}
}
}
/**
* @param {MemberExpressionNode} expression an expression
* @returns {{ name: string, rootInfo: ExportedVariableInfo, getMembers: () => string[]}} name info
*/
getNameForExpression(expression) {
return this.getMemberExpressionInfo(expression, ["expression"]);
}
/**
* @param {string} code source code
* @param {ParseOptions} options parsing options

View File

@ -44,7 +44,8 @@ class JsonParser extends Parser {
state.module.buildInfo.jsonData = data;
state.module.buildInfo.strict = true;
state.module.buildMeta.exportsType = "default";
state.module.buildMeta.defaultObject = true;
state.module.buildMeta.defaultObject =
typeof data === "object" ? "redirect-warn" : false;
state.module.addDependency(
new JsonExportsDependency(JsonExportsDependency.getExportsFromData(data))
);

View File

@ -232,48 +232,50 @@ const getExternalImport = (
? ""
: Template.toNormalComment(`${exportName.join(".")}`);
let exprStart;
const exportsType = importedModule.getExportsType(strictHarmonyModule);
if (exportName.length === 0) {
switch (importedModule.buildMeta.exportsType) {
case "default":
switch (exportsType) {
case "dynamic-default":
case "default-only":
case "default-with-named":
info.interopNamespaceObjectUsed = true;
exprStart = info.interopNamespaceObjectName;
break;
case "namespace":
exprStart = info.name;
break;
case "dynamic":
exprStart = info.name;
break;
default:
if (strictHarmonyModule) {
info.interopNamespaceObjectUsed = true;
exprStart = info.interopNamespaceObjectName;
break;
} else {
exprStart = info.name;
break;
}
throw new Error(`Unexpected exportsType ${exportsType}`);
}
} else {
switch (importedModule.buildMeta.exportsType) {
case "default":
switch (exportsType) {
case "default-with-named":
case "namespace":
break;
default:
if (strictHarmonyModule) {
if (exportName[0] === "default") {
exprStart = info.name;
} else {
exprStart = "/* non-default import from non-esm module */undefined";
}
case "default-only":
case "dynamic-default":
if (exportName[0] === "default") {
exprStart = info.name;
} else {
if (exportName[0] === "default") {
info.interopDefaultAccessUsed = true;
exprStart = asCall
? `${info.interopDefaultAccessName}()`
: asiSafe
? `(${info.interopDefaultAccessName}())`
: `${info.interopDefaultAccessName}.a`;
}
exprStart =
"/* non-default import from default-exporting module */undefined";
}
break;
case "dynamic":
if (exportName[0] === "default") {
info.interopDefaultAccessUsed = true;
exprStart = asCall
? `${info.interopDefaultAccessName}()`
: asiSafe
? `(${info.interopDefaultAccessName}())`
: `${info.interopDefaultAccessName}.a`;
}
break;
default:
throw new Error(`Unexpected exportsType ${exportsType}`);
}
}
if (exprStart) {
@ -978,6 +980,7 @@ class ConcatenatedModule extends Module {
info.name = externalName;
if (
info.module.buildMeta.exportsType === "default" ||
info.module.buildMeta.exportsType === "flagged" ||
!info.module.buildMeta.exportsType
) {
const externalNameInterop = this.findNewName(
@ -1090,7 +1093,10 @@ class ConcatenatedModule extends Module {
result.add(
`var ${info.interopNamespaceObjectName} = /*#__PURE__*/${RuntimeGlobals.createFakeNamespaceObject}(${info.name}, 2);\n`
);
} else if (!info.module.buildMeta.exportsType) {
} else if (
info.module.buildMeta.exportsType === "flagged" ||
!info.module.buildMeta.exportsType
) {
runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
result.add(
`var ${info.interopNamespaceObjectName} = /*#__PURE__*/${RuntimeGlobals.createFakeNamespaceObject}(${info.name});\n`

View File

@ -32,8 +32,14 @@ module.exports = {
require("../dependencies/CachedConstDependency"),
"dependencies/CommonJsRequireContextDependency": () =>
require("../dependencies/CommonJsRequireContextDependency"),
"dependencies/CommonJsExportsDependency": () =>
require("../dependencies/CommonJsExportsDependency"),
"dependencies/CommonJsFullRequireDependency": () =>
require("../dependencies/CommonJsFullRequireDependency"),
"dependencies/CommonJsRequireDependency": () =>
require("../dependencies/CommonJsRequireDependency"),
"dependencies/CommonJsSelfReferenceDependency": () =>
require("../dependencies/CommonJsSelfReferenceDependency"),
"dependencies/ConstDependency": () =>
require("../dependencies/ConstDependency"),
"dependencies/ContextDependency": () =>

View File

@ -57,6 +57,7 @@ class AsyncWebAssemblyJavascriptGenerator extends Generator {
runtimeRequirements
} = generateContext;
runtimeRequirements.add(RuntimeGlobals.module);
runtimeRequirements.add(RuntimeGlobals.moduleId);
runtimeRequirements.add(RuntimeGlobals.exports);
runtimeRequirements.add(RuntimeGlobals.instantiateWasm);
/** @type {InitFragment[]} */
@ -120,6 +121,7 @@ class AsyncWebAssemblyJavascriptGenerator extends Generator {
asiSafe: true,
isCall: false,
callContext: false,
defaultInterop: true,
importVar,
initFragments,
runtimeRequirements
@ -143,7 +145,7 @@ class AsyncWebAssemblyJavascriptGenerator extends Generator {
: undefined;
const instantiateCall =
`${RuntimeGlobals.instantiateWasm}(${module.exportsArgument}, ${module.moduleArgument}.i` +
`${RuntimeGlobals.instantiateWasm}(${module.exportsArgument}, ${module.moduleArgument}.id` +
(importsObj ? `, ${importsObj})` : `)`);
const source = new RawSource(

View File

@ -100,6 +100,7 @@ class WebAssemblyJavascriptGenerator extends Generator {
asiSafe: true,
isCall: false,
callContext: null,
defaultInterop: true,
initFragments,
runtimeRequirements
})
@ -127,6 +128,7 @@ class WebAssemblyJavascriptGenerator extends Generator {
asiSafe: true,
isCall: false,
callContext: null,
defaultInterop: true,
initFragments,
runtimeRequirements
})};`,
@ -164,6 +166,7 @@ class WebAssemblyJavascriptGenerator extends Generator {
// need these globals
runtimeRequirements.add(RuntimeGlobals.module);
runtimeRequirements.add(RuntimeGlobals.moduleId);
runtimeRequirements.add(RuntimeGlobals.wasmInstances);
if (exportsInfo.otherExportsInfo.used !== UsageState.Unused) {
runtimeRequirements.add(RuntimeGlobals.makeNamespaceObject);
@ -178,7 +181,7 @@ class WebAssemblyJavascriptGenerator extends Generator {
[
'"use strict";',
"// Instantiate WebAssembly module",
`var wasmExports = ${RuntimeGlobals.wasmInstances}[${module.moduleArgument}.i];`,
`var wasmExports = ${RuntimeGlobals.wasmInstances}[${module.moduleArgument}.id];`,
exportsInfo.otherExportsInfo.used !== UsageState.Unused
? `${RuntimeGlobals.makeNamespaceObject}(${module.exportsArgument});`

View File

@ -202,10 +202,10 @@ describe("Stats", () => {
"comparedForEmit": false,
"emitted": true,
"info": Object {
"size": 198,
"size": 182,
},
"name": "entryA.js",
"size": 198,
"size": 182,
},
Object {
"auxiliaryChunkIdHints": Array [],
@ -217,10 +217,10 @@ describe("Stats", () => {
"comparedForEmit": false,
"emitted": true,
"info": Object {
"size": 1881,
"size": 1865,
},
"name": "entryB.js",
"size": 1881,
"size": 1865,
},
],
"assetsByChunkName": Object {

File diff suppressed because it is too large Load Diff

View File

@ -14,8 +14,8 @@ it("should bailout when reading whole exports object from module.exports", () =>
});
it("should reassigning exports (assign values)", () => {
expect(require("./assign-exports-assign").abc).toBe("abc");
expect(require("./assign-exports-assign").def).toBe(undefined);
expect(require("./assign-exports-assign?1").abc).toBe("abc");
expect(require("./assign-exports-assign?2").def).toBe(undefined);
});
it("should reassigning exports (define values)", () => {

View File

@ -1,17 +1,17 @@
module.exports = [
[
/Can't import the named export '2' \(imported as 'c'\) from default-exporting module \(only default export is available\)/
/Should not import the named export '2' \(imported as 'c'\) from default-exporting module \(only default export is available soon\)/
],
[
/Can't import the named export 'aa' \(imported as 'aa'\) from default-exporting module \(only default export is available\)/
/Should not import the named export 'aa' \(imported as 'aa'\) from default-exporting module \(only default export is available soon\)/
],
[
/Can't import the named export 'bb' \(imported as 'bb'\) from default-exporting module \(only default export is available\)/
/Should not import the named export 'bb' \(imported as 'bb'\) from default-exporting module \(only default export is available soon\)/
],
[
/Can't import the named export 'named' \(imported as 'named'\) from default-exporting module \(only default export is available\)/
/Should not import the named export 'named' \(imported as 'named'\) from default-exporting module \(only default export is available soon\)/
],
[
/Can't import the named export 'named' \(imported as 'gnamed'\) from default-exporting module \(only default export is available\)/
/Should not import the named export 'named' \(imported as 'gnamed'\) from default-exporting module \(only default export is available soon\)/
]
];

View File

@ -1,17 +1,17 @@
module.exports = [
[
/Can't import the named export '2' \(imported as 'c'\) from default-exporting module \(only default export is available\)/
/Should not import the named export '2' \(imported as 'c'\) from default-exporting module \(only default export is available soon\)/
],
[
/Can't import the named export 'aa' \(imported as 'aa'\) from default-exporting module \(only default export is available\)/
/Should not import the named export 'aa' \(imported as 'aa'\) from default-exporting module \(only default export is available soon\)/
],
[
/Can't import the named export 'bb' \(imported as 'bb'\) from default-exporting module \(only default export is available\)/
/Should not import the named export 'bb' \(imported as 'bb'\) from default-exporting module \(only default export is available soon\)/
],
[
/Can't import the named export 'named' \(imported as 'named'\) from default-exporting module \(only default export is available\)/
/Should not import the named export 'named' \(imported as 'named'\) from default-exporting module \(only default export is available soon\)/
],
[
/Can't import the named export 'named' \(imported as 'gnamed'\) from default-exporting module \(only default export is available\)/
/Should not import the named export 'named' \(imported as 'gnamed'\) from default-exporting module \(only default export is available soon\)/
]
];

View File

@ -1,5 +1,5 @@
module.exports = [
[
/Can't import the named export 'named' \(reexported as 'fNamed'\) from default-exporting module \(only default export is available\)/
/Should not import the named export 'named' \(reexported as 'fNamed'\) from default-exporting module \(only default export is available soon\)/
]
];

View File

@ -1,11 +1,11 @@
module.exports = [
[
/Can't import the named export 'data' \(imported as 'data'\) from non EcmaScript module \(only default export is available\)/
/Can't import the named export 'data' \(imported as 'data'\) from default-exporting module \(only default export is available\)/
],
[
/Can't import the named export 'data' \(imported as 'data'\) from non EcmaScript module \(only default export is available\)/
/Can't import the named export 'data' \(imported as 'data'\) from default-exporting module \(only default export is available\)/
],
[
/Can't import the named export 'data' \(reexported as 'data'\) from non EcmaScript module \(only default export is available\)/
/Can't import the named export 'data' \(reexported as 'data'\) from default-exporting module \(only default export is available\)/
]
];

View File

@ -53,8 +53,7 @@ function contextMixed(name) {
function promiseTest(promise, equalsTo) {
return promise.then(function(results) {
for(const result of results)
expect(result).toEqual(equalsTo);
expect(results).toEqual(results.map(() => equalsTo));
});
}

View File

@ -21,7 +21,7 @@ it("should not overwrite when using star export (known exports)", function() {
expect(x4).toBe("b");
expect(x5).toBe("c");
expect(x6).toBe("a");
expect(x7).toBe("b"); // Looks wrong, but is irrelevant as this should be an error anyway
expect(x7).toBe("d"); // Looks wrong, but is irrelevant as this should be an error anyway
});
it("should not overwrite when using star export (unknown exports)", function() {
@ -31,5 +31,5 @@ it("should not overwrite when using star export (unknown exports)", function() {
expect(y4).toBe("b");
expect(y5).toBe("c");
expect(y6).toBe("a");
expect(y7).toBe("b"); // Looks wrong, but is irrelevant as this should be an error anyway
expect(y7).toBe("d"); // Looks wrong, but is irrelevant as this should be an error anyway
});

View File

@ -1,8 +1,8 @@
module.exports = [
[
/Can't import the named export 'a' \(reexported as 'a'\) from default-exporting module \(only default export is available\)/
/Should not import the named export 'a' \(reexported as 'a'\) from default-exporting module \(only default export is available soon\)/
],
[
/Can't import the named export 'b' \(reexported as 'b'\) from default-exporting module \(only default export is available\)/
/Should not import the named export 'b' \(reexported as 'b'\) from default-exporting module \(only default export is available soon\)/
]
];

View File

@ -1 +1 @@
module.exports = [[/Can't import the named export/]];
module.exports = [[/Should not import the named export/]];

View File

@ -1,4 +1,4 @@
module.export = function someUsedFunction() {};
module.exports = function someUsedFunction() {};
function someRemoteUnUsedFunction1() {}
function someRemoteUnUsedFunction2() {}