Skip to content

Commit 9c25669

Browse files
authored
Merge pull request webpack#6012 from webpack/feature/mjs-import-non-esm
process imports from mjs to non-esm correctly
2 parents 23e96e9 + 21cdc84 commit 9c25669

File tree

11 files changed

+300
-52
lines changed

11 files changed

+300
-52
lines changed

lib/dependencies/HarmonyDetectionParserPlugin.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,10 @@ module.exports = class HarmonyDetectionParserPlugin {
4646
module.meta.harmonyModule = true;
4747
module.strict = true;
4848
module.exportsArgument = "__webpack_exports__";
49-
if(isStrictHarmony)
49+
if(isStrictHarmony) {
50+
module.meta.strictHarmonyModule = true;
5051
module.moduleArgument = "__webpack_module__";
52+
}
5153
}
5254
});
5355

lib/dependencies/HarmonyExportDependencyParserPlugin.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ const HarmonyExportImportedSpecifierDependency = require("./HarmonyExportImporte
1212
const ConstDependency = require("./ConstDependency");
1313

1414
module.exports = class HarmonyExportDependencyParserPlugin {
15+
constructor(moduleOptions) {
16+
this.strictExportPresence = moduleOptions.strictExportPresence;
17+
}
18+
1519
apply(parser) {
1620
parser.plugin("export", statement => {
1721
const dep = new HarmonyExportHeaderDependency(statement.declaration && statement.declaration.range, statement.range);
@@ -47,7 +51,7 @@ module.exports = class HarmonyExportDependencyParserPlugin {
4751
harmonyNamedExports.add(name);
4852
if(rename === "imported var") {
4953
const settings = parser.state.harmonySpecifier.get(id);
50-
dep = new HarmonyExportImportedSpecifierDependency(settings.source, parser.state.module, settings.sourceOrder, parser.state.harmonyParserScope, settings.id, name, harmonyNamedExports, null);
54+
dep = new HarmonyExportImportedSpecifierDependency(settings.source, parser.state.module, settings.sourceOrder, parser.state.harmonyParserScope, settings.id, name, harmonyNamedExports, null, this.strictExportPresence);
5155
} else {
5256
dep = new HarmonyExportSpecifierDependency(parser.state.module, id, name);
5357
}
@@ -64,7 +68,7 @@ module.exports = class HarmonyExportDependencyParserPlugin {
6468
} else {
6569
harmonyStarExports = parser.state.harmonyStarExports = parser.state.harmonyStarExports || [];
6670
}
67-
const dep = new HarmonyExportImportedSpecifierDependency(source, parser.state.module, parser.state.lastHarmonyImportOrder, parser.state.harmonyParserScope, id, name, harmonyNamedExports, harmonyStarExports && harmonyStarExports.slice());
71+
const dep = new HarmonyExportImportedSpecifierDependency(source, parser.state.module, parser.state.lastHarmonyImportOrder, parser.state.harmonyParserScope, id, name, harmonyNamedExports, harmonyStarExports && harmonyStarExports.slice(), this.strictExportPresence);
6872
if(harmonyStarExports) {
6973
harmonyStarExports.push(dep);
7074
}

lib/dependencies/HarmonyExportImportedSpecifierDependency.js

Lines changed: 109 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@ const HarmonyImportDependency = require("./HarmonyImportDependency");
77
const Template = require("../Template");
88

99
class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
10-
constructor(request, originModule, sourceOrder, parserScope, id, name, activeExports, otherStarExports) {
10+
constructor(request, originModule, sourceOrder, parserScope, id, name, activeExports, otherStarExports, strictExportPresence) {
1111
super(request, originModule, sourceOrder, parserScope);
1212
this.id = id;
1313
this.name = name;
1414
this.activeExports = activeExports;
1515
this.otherStarExports = otherStarExports;
16+
this.strictExportPresence = strictExportPresence;
1617
}
1718

1819
get type() {
@@ -40,32 +41,57 @@ class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
4041
}
4142

4243
const isNotAHarmonyModule = importedModule.meta && !importedModule.meta.harmonyModule;
44+
const strictHarmonyModule = this.originModule.meta.strictHarmonyModule;
4345
if(name && id === "default" && isNotAHarmonyModule) {
44-
return {
45-
type: "reexport-non-harmony-default",
46-
module: importedModule,
47-
name
48-
};
46+
if(strictHarmonyModule) {
47+
return {
48+
type: "reexport-non-harmony-default-strict",
49+
module: importedModule,
50+
name
51+
};
52+
} else {
53+
return {
54+
type: "reexport-non-harmony-default",
55+
module: importedModule,
56+
name
57+
};
58+
}
4959
}
5060

5161
if(name) {
5262
// export { name as name }
5363
if(id) {
64+
if(isNotAHarmonyModule && strictHarmonyModule) {
65+
return {
66+
type: "rexport-non-harmony-undefined",
67+
module: importedModule,
68+
name
69+
};
70+
} else {
71+
return {
72+
type: "safe-reexport",
73+
module: importedModule,
74+
map: new Map([
75+
[name, id]
76+
])
77+
};
78+
}
79+
}
80+
81+
// export { * as name }
82+
if(isNotAHarmonyModule && strictHarmonyModule) {
5483
return {
55-
type: "safe-reexport",
84+
type: "reexport-fake-namespace-object",
5685
module: importedModule,
57-
map: new Map([
58-
[name, id]
59-
])
86+
name
87+
};
88+
} else {
89+
return {
90+
type: "reexport-namespace-object",
91+
module: importedModule,
92+
name
6093
};
6194
}
62-
63-
// export { * as name }
64-
return {
65-
type: "reexport-namespace-object",
66-
module: importedModule,
67-
name
68-
};
6995
}
7096

7197
const hasUsedExports = Array.isArray(this.originModule.usedExports);
@@ -166,6 +192,9 @@ class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
166192
};
167193

168194
case "reexport-namespace-object":
195+
case "reexport-non-harmony-default-strict":
196+
case "reexport-fake-namespace-object":
197+
case "rexport-non-harmony-undefined":
169198
return {
170199
module: mode.module,
171200
importedNames: true
@@ -239,6 +268,55 @@ class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
239268
};
240269
}
241270

271+
getWarnings() {
272+
if(this.strictExportPresence || this.originModule.meta.strictHarmonyModule) {
273+
return [];
274+
}
275+
return this._getErrors();
276+
}
277+
278+
getErrors() {
279+
if(this.strictExportPresence || this.originModule.meta.strictHarmonyModule) {
280+
return this._getErrors();
281+
}
282+
return [];
283+
}
284+
285+
_getErrors() {
286+
const importedModule = this.module;
287+
if(!importedModule) {
288+
return;
289+
}
290+
291+
if(!importedModule.meta || !importedModule.meta.harmonyModule) {
292+
// It's not an harmony module
293+
if(this.originModule.meta.strictHarmonyModule && this.id !== "default") {
294+
// In strict harmony modules we only support the default export
295+
const exportName = this.id ? `the named export '${this.id}'` : "the namespace object";
296+
const err = new Error(`Can't reexport ${exportName} from non EcmaScript module (only default export is available)`);
297+
err.hideStack = true;
298+
return [err];
299+
}
300+
return;
301+
}
302+
303+
if(!this.id) {
304+
return;
305+
}
306+
307+
if(importedModule.isProvided(this.id) !== false) {
308+
// It's provided or we are not sure
309+
return;
310+
}
311+
312+
// We are sure that it's not provided
313+
const idIsNotNameMessage = this.id !== this.name ? ` (reexported as '${this.name}')` : "";
314+
const errorMessage = `"export '${this.id}'${idIsNotNameMessage} was not found in '${this.userRequest}'`;
315+
const err = new Error(errorMessage);
316+
err.hideStack = true;
317+
return [err];
318+
}
319+
242320
updateHash(hash) {
243321
super.updateHash(hash);
244322
const hashValue = this.getHashValue(this.module);
@@ -317,6 +395,15 @@ HarmonyExportImportedSpecifierDependency.Template = class HarmonyExportImportedS
317395
case "reexport-non-harmony-default":
318396
return "/* harmony reexport (default from non-hamory) */ " + this.getReexportStatement(module, module.isUsed(mode.name), importVar, null);
319397

398+
case "reexport-fake-namespace-object":
399+
return "/* harmony reexport (fake namespace object from non-hamory) */ " + this.getReexportFakeNamespaceObjectStatement(module, module.isUsed(mode.name), importVar);
400+
401+
case "rexport-non-harmony-undefined":
402+
return "/* harmony reexport (non default export from non-hamory) */ " + this.getReexportStatement(module, module.isUsed(mode.name), "undefined", "");
403+
404+
case "reexport-non-harmony-default-strict":
405+
return "/* harmony reexport (default from non-hamory) */ " + this.getReexportStatement(module, module.isUsed(mode.name), importVar, "");
406+
320407
case "reexport-namespace-object":
321408
return "/* harmony reexport (module object) */ " + this.getReexportStatement(module, module.isUsed(mode.name), importVar, "");
322409

@@ -359,6 +446,11 @@ HarmonyExportImportedSpecifierDependency.Template = class HarmonyExportImportedS
359446
return `__webpack_require__.d(${exportsName}, ${JSON.stringify(key)}, function() { return ${name}${returnValue}; });\n`;
360447
}
361448

449+
getReexportFakeNamespaceObjectStatement(module, key, name) {
450+
const exportsName = module.exportsArgument;
451+
return `__webpack_require__.d(${exportsName}, ${JSON.stringify(key)}, function() { return { "default": ${name} }; });\n`;
452+
}
453+
362454
getConditionalReexportStatement(module, key, name, valueKey) {
363455
const exportsName = module.exportsArgument;
364456
const returnValue = this.getReturnValue(valueKey);

lib/dependencies/HarmonyImportDependency.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ class HarmonyImportDependency extends ModuleDependency {
5050
if(importVar) {
5151
const isHarmonyModule = module.meta && module.meta.harmonyModule;
5252
const content = `/* harmony import */ ${optDeclaration}${importVar} = __webpack_require__(${comment}${JSON.stringify(module.id)});${optNewline}`;
53-
if(isHarmonyModule) {
53+
if(isHarmonyModule || this.originModule.meta.strictHarmonyModule) {
5454
return content;
5555
}
5656
return `${content}/* harmony import */ ${optDeclaration}${importVar}_default = /*#__PURE__*/__webpack_require__.n(${importVar});${optNewline}`;

lib/dependencies/HarmonyImportSpecifierDependency.js

Lines changed: 48 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -31,22 +31,34 @@ class HarmonyImportSpecifierDependency extends HarmonyImportDependency {
3131
}
3232

3333
getWarnings() {
34-
if(this.strictExportPresence) {
34+
if(this.strictExportPresence || this.originModule.meta.strictHarmonyModule) {
3535
return [];
3636
}
3737
return this._getErrors();
3838
}
3939

4040
getErrors() {
41-
if(this.strictExportPresence) {
41+
if(this.strictExportPresence || this.originModule.meta.strictHarmonyModule) {
4242
return this._getErrors();
4343
}
4444
return [];
4545
}
4646

4747
_getErrors() {
4848
const importedModule = this.module;
49-
if(!importedModule || !importedModule.meta || !importedModule.meta.harmonyModule) {
49+
if(!importedModule) {
50+
return;
51+
}
52+
53+
if(!importedModule.meta || !importedModule.meta.harmonyModule) {
54+
// It's not an harmony module
55+
if(this.originModule.meta.strictHarmonyModule && this.id !== "default") {
56+
// In strict harmony modules we only support the default export
57+
const exportName = this.id ? `the named export '${this.id}'` : "the namespace object";
58+
const err = new Error(`Can't import ${exportName} from non EcmaScript module (only default export is available)`);
59+
err.hideStack = true;
60+
return [err];
61+
}
5062
return;
5163
}
5264

@@ -55,9 +67,11 @@ class HarmonyImportSpecifierDependency extends HarmonyImportDependency {
5567
}
5668

5769
if(importedModule.isProvided(this.id) !== false) {
70+
// It's provided or we are not sure
5871
return;
5972
}
6073

74+
// We are sure that it's not provided
6175
const idIsNotNameMessage = this.id !== this.name ? ` (imported as '${this.name}')` : "";
6276
const errorMessage = `"export '${this.id}'${idIsNotNameMessage} was not found in '${this.userRequest}'`;
6377
const err = new Error(errorMessage);
@@ -90,26 +104,46 @@ HarmonyImportSpecifierDependency.Template = class HarmonyImportSpecifierDependen
90104

91105
getContent(dep, importedVar) {
92106
const importedModule = dep.module;
93-
const nonHarmonyDefaultImport = dep.directImport && dep.id === "default" && !(importedModule && (!importedModule.meta || importedModule.meta.harmonyModule));
107+
const nonHarmonyImport = !(importedModule && (!importedModule.meta || importedModule.meta.harmonyModule));
108+
const importedVarSuffix = this.getImportVarSuffix(dep.id, importedModule);
94109
const shortHandPrefix = dep.shorthand ? `${dep.name}: ` : "";
95-
const importedVarSuffix = this.getImportVarSuffix(dep.id, nonHarmonyDefaultImport, importedModule);
96110

97-
if(dep.call && nonHarmonyDefaultImport) {
98-
return `${shortHandPrefix}${importedVar}_default()`;
111+
// Note: dep.call and dep.shorthand are exclusive
112+
113+
if(nonHarmonyImport) {
114+
const defaultExport = dep.id === "default";
115+
if(dep.originModule.meta.strictHarmonyModule) {
116+
if(defaultExport) {
117+
return `${shortHandPrefix}${importedVar}`;
118+
}
119+
120+
if(!dep.id) {
121+
if(shortHandPrefix)
122+
return `${shortHandPrefix}/* fake namespace object for non-esm import */ { default: ${importedVar} }`;
123+
else
124+
return `Object(/* fake namespace object for non-esm import */{ "default": ${importedVar} })`;
125+
}
126+
127+
return `${shortHandPrefix}/* non-default import from non-esm module */undefined`;
128+
} else {
129+
if(dep.call && defaultExport) {
130+
return `${shortHandPrefix}${importedVar}_default()`;
131+
}
132+
133+
if(defaultExport) {
134+
return `${shortHandPrefix}${importedVar}_default.a`;
135+
}
136+
}
99137
}
100138

101-
if(dep.call && dep.id) {
102-
return `${shortHandPrefix}Object(${importedVar}${importedVarSuffix})`;
139+
if(dep.call && dep.id && dep.directImport) {
140+
return `Object(${importedVar}${importedVarSuffix})`;
103141
}
104142

105143
return `${shortHandPrefix}${importedVar}${importedVarSuffix}`;
106144
}
107145

108-
getImportVarSuffix(id, nonHarmonyDefaultImport, importedModule) {
109-
if(nonHarmonyDefaultImport) {
110-
return "_default.a";
111-
}
112-
146+
getImportVarSuffix(id, importedModule) {
113147
if(id) {
114148
const used = importedModule ? importedModule.isUsed(id) : id;
115149
const optionalComment = id !== used ? " /* " + id + " */" : "";

lib/dependencies/HarmonyModulesPlugin.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ class HarmonyModulesPlugin {
6767
parser.apply(
6868
new HarmonyDetectionParserPlugin(),
6969
new HarmonyImportDependencyParserPlugin(this.options),
70-
new HarmonyExportDependencyParserPlugin()
70+
new HarmonyExportDependencyParserPlugin(this.options)
7171
);
7272
});
7373
});

0 commit comments

Comments
 (0)