Merge pull request #11117 from vankop/add-export-as-from

add export * as namespace from "module"
This commit is contained in:
Tobias Koppers 2020-07-06 22:31:07 +02:00 committed by GitHub
commit e8ed3d8d3b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 143 additions and 60 deletions

View File

@ -33,16 +33,10 @@ const HarmonyImportDependency = require("./HarmonyImportDependency");
const idsSymbol = Symbol("HarmonyExportImportedSpecifierDependency.ids");
/** @type {Map<string, string[]>} */
const EMPTY_MAP = new Map();
/** @type {Set<string>} */
const EMPTY_SET = new Set();
/**
* @param {string[][]} referencedExports list of referenced exports, will be added to
* @param {string[]} prefix export prefix
* @param {ExportInfo} exportInfo the export info
* @param {ExportInfo=} exportInfo the export info
* @param {boolean} defaultPointsToSelf when true, using default will reference itself
* @param {Set<ExportInfo>} alreadyVisited already visited export info (to handle circular reexports)
*/
@ -82,6 +76,21 @@ const processExportInfo = (
}
};
class NormalReexportItem {
/**
* @param {string} name export name
* @param {string[]} ids reexported ids from other module
* @param {ExportInfo=} exportInfo export info from other module
* @param {boolean} checked true, if it should be checked at runtime if this export exists
*/
constructor(name, ids, exportInfo, checked) {
this.name = name;
this.ids = ids;
this.exportInfo = exportInfo;
this.checked = checked;
}
}
class ExportMode {
/**
* @param {ExportModeType} type type of the mode
@ -89,18 +98,26 @@ class ExportMode {
constructor(type) {
/** @type {ExportModeType} */
this.type = type;
// for "normal-reexport":
/** @type {NormalReexportItem[] | null} */
this.items = null;
// for "reexport-named-default" | "reexport-fake-namespace-object" | "reexport-namespace-object"
/** @type {string|null} */
this.name = null;
/** @type {Map<string, string[]>} */
this.map = EMPTY_MAP;
/** @type {ExportInfo} */
this.partialNamespaceExportInfo = undefined;
/** @type {Set<string>|null} */
/** @type {ExportInfo | null} */
this.partialNamespaceExportInfo = null;
// for "dynamic-reexport":
/** @type {Set<string> | null} */
this.ignored = null;
/** @type {Set<string>|null} */
this.checked = null;
/** @type {string|null} */
// for "missing":
/** @type {string | null} */
this.userRequest = null;
// for "reexport-fake-namespace-object":
/** @type {number} */
this.fakeType = 0;
}
@ -245,12 +262,11 @@ class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
break;
default:
mode = new ExportMode("normal-reexport");
mode.map = new Map([[name, ids]]);
mode.checked = EMPTY_SET;
mode.items = [new NormalReexportItem(name, ids, exportInfo, false)];
break;
}
} else {
// export { * as name }
// export * as name
switch (importedExportsType) {
case "default-only":
mode = new ExportMode("reexport-fake-namespace-object");
@ -303,16 +319,18 @@ class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
return mode;
}
/** @type {Map<string, string[]>} */
const map = new Map();
for (const exportName of exports) {
map.set(exportName, [exportName]);
}
const mode = new ExportMode("normal-reexport");
mode.map = map;
mode.checked = checked;
mode.items = Array.from(
exports,
exportName =>
new NormalReexportItem(
exportName,
[exportName],
exportsInfo.getReadOnlyExportInfo(exportName),
checked.has(exportName)
)
);
return mode;
}
@ -419,7 +437,7 @@ class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
processExportInfo(
referencedExports,
[],
mode.partialNamespaceExportInfo
/** @type {ExportInfo} */ (mode.partialNamespaceExportInfo)
);
return referencedExports;
}
@ -433,7 +451,7 @@ class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
processExportInfo(
referencedExports,
[],
mode.partialNamespaceExportInfo,
/** @type {ExportInfo} */ (mode.partialNamespaceExportInfo),
mode.type === "reexport-fake-namespace-object"
);
return referencedExports;
@ -442,8 +460,13 @@ class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
case "dynamic-reexport":
return Dependency.EXPORTS_OBJECT_REFERENCED;
case "normal-reexport":
return Array.from(mode.map.values());
case "normal-reexport": {
const referencedExports = [];
for (const { ids, exportInfo } of mode.items) {
processExportInfo(referencedExports, ids, exportInfo, false);
}
return referencedExports;
}
default:
throw new Error(`Unknown mode ${mode.type}`);
@ -502,15 +525,17 @@ class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
exports: [],
dependencies: [moduleGraph.getModule(this)]
};
case "normal-reexport":
case "normal-reexport": {
const from = moduleGraph.getModule(this);
return {
exports: Array.from(mode.map.keys()).map(name => ({
name,
from: moduleGraph.getModule(this),
export: mode.map.get(name)
exports: Array.from(mode.items, item => ({
name: item.name,
from,
export: item.ids
})),
dependencies: [moduleGraph.getModule(this)]
dependencies: [from]
};
}
case "reexport-dynamic-default":
case "reexport-undefined":
return {
@ -620,9 +645,12 @@ class HarmonyExportImportedSpecifierDependency extends HarmonyImportDependency {
const mode = this.getMode(chunkGraph.moduleGraph);
hash.update(mode.type);
for (const [k, v] of mode.map) {
hash.update(k);
hash.update(v.join());
if (mode.items) {
for (const item of mode.items) {
hash.update(item.name);
hash.update(item.ids.join());
if (item.checked) hash.update("checked");
}
}
if (mode.ignored) {
hash.update("ignored");
@ -803,16 +831,16 @@ HarmonyExportImportedSpecifierDependency.Template = class HarmonyExportImportedS
break;
case "normal-reexport":
for (const [key, id] of mode.map) {
if (mode.checked.has(key)) {
for (const { name, ids, checked } of mode.items) {
if (checked) {
initFragments.push(
new InitFragment(
"/* harmony reexport (checked) */ " +
this.getConditionalReexportStatement(
module,
key,
name,
importVar,
id,
ids,
runtimeRequirements
),
InitFragment.STAGE_HARMONY_IMPORTS,
@ -824,9 +852,9 @@ HarmonyExportImportedSpecifierDependency.Template = class HarmonyExportImportedS
this.getReexportFragment(
module,
"reexport safe",
module.getUsedName(moduleGraph, key),
module.getUsedName(moduleGraph, name),
importVar,
importedModule.getUsedName(moduleGraph, id),
importedModule.getUsedName(moduleGraph, ids),
runtimeRequirements
)
);

View File

@ -1729,8 +1729,9 @@ class JavascriptParser extends Parser {
blockPreWalkExportAllDeclaration(statement) {
const source = statement.source.value;
const name = statement.exported ? statement.exported.name : null;
this.hooks.exportImport.call(statement, source);
this.hooks.exportImportSpecifier.call(statement, source, null, null, 0);
this.hooks.exportImportSpecifier.call(statement, source, null, name, 0);
}
preWalkVariableDeclaration(statement) {

View File

@ -214,15 +214,15 @@ class SideEffectsFlagPlugin {
const targetModule = moduleGraph.getModule(dep);
switch (mode.type) {
case "normal-reexport":
for (const [key, ids] of mode.map) {
for (const { name, ids, checked } of mode.items) {
// TODO Support reexporting namespace object
if (ids.length > 0) {
addStaticReexport(
info,
key,
name,
targetModule,
ids[0],
mode.checked.has(key)
checked
);
}
}

View File

@ -5,12 +5,12 @@
"description": "Packs CommonJs/AMD modules for the browser. Allows to split your codebase into multiple bundles, which can be loaded on demand. Support loaders to preprocess files, i.e. json, jsx, es7, css, less, ... and your custom stuff.",
"license": "MIT",
"dependencies": {
"@types/estree": "0.0.42",
"@types/estree": "^0.0.45",
"@webassemblyjs/ast": "1.9.0",
"@webassemblyjs/helper-module-context": "1.9.0",
"@webassemblyjs/wasm-edit": "1.9.0",
"@webassemblyjs/wasm-parser": "1.9.0",
"acorn": "^7.0.0",
"acorn": "^7.3.0",
"chrome-trace-event": "^1.0.2",
"enhanced-resolve": "5.0.0-beta.7",
"eslint-scope": "^5.0.0",

View File

@ -2738,7 +2738,7 @@ Entrypoint main = main.js
`;
exports[`StatsTestCases should print correct stats for side-effects-optimization 1`] = `
"Hash: d7d1b4f45cd90d4abdb64369e7ea517dce8ffb09
"Hash: d7d1b4f45cd90d4abdb6d61ad7aea717eb8e1363
Child
Hash: d7d1b4f45cd90d4abdb6
Time: X ms
@ -2762,7 +2762,7 @@ Child
ModuleConcatenation bailout: Module is not an ECMAScript module
+ 2 hidden modules
Child
Hash: 4369e7ea517dce8ffb09
Hash: d61ad7aea717eb8e1363
Time: X ms
Built at: 1970-04-20 12:42:42
Asset Size
@ -3843,7 +3843,7 @@ chunk default/async-a.js (async-a) 134 bytes <{179}> [rendered]
`;
exports[`StatsTestCases should print correct stats for tree-shaking 1`] = `
"Hash: 0653813b0ee99f2a94bd
"Hash: cd2fbd230872570f58af
Time: X ms
Built at: 1970-04-20 12:42:42
Asset Size

View File

@ -0,0 +1,9 @@
import {c} from "./c.js";
const b2 = 3;
const b3 = c;
export {c as b1, c as b4, b2, b3}
export const usedB1 = __webpack_exports_info__.b1.used;
export const usedB2 = __webpack_exports_info__.b2.used;
export const usedB3 = __webpack_exports_info__.b3.used;
export const usedB4 = __webpack_exports_info__.b4.used;

View File

@ -0,0 +1 @@
export * as c from "./d.js";

View File

@ -0,0 +1,4 @@
export const d1 = 1;
export const d2 = 2;
export const usedD1 = __webpack_exports_info__.d1.used;
export const usedD2 = __webpack_exports_info__.d2.used;

View File

@ -0,0 +1,4 @@
export const e1 = 10;
export const e2 = 20;
export const usedE1 = __webpack_exports_info__.e1.used;
export const usedE2 = __webpack_exports_info__.e2.used;

View File

@ -0,0 +1 @@
export * as e from "./e.js";

View File

@ -0,0 +1,2 @@
export * as f1 from "./e1.js";
export * as f2 from "./e.js";

View File

@ -0,0 +1,3 @@
import {f1, f2} from "./f.js";
export {f1, f2 as g1};

View File

@ -0,0 +1 @@
export * as h from "./g.js";

View File

@ -1,8 +1,31 @@
"use strict";
import { x, y } from "./a";
import {d2, usedD1, usedD2} from "./d.js";
import {b1, usedB1, usedB2, usedB3, usedB4} from "./b.js";
import {usedE1, usedE2} from "./e.js";
import {h} from "./h.js";
it("namespace export as from commonjs should override named export", function() {
expect(x).toBe(1);
expect(y).toBe(3);
});
it("named namespace export should work correctly", function () {
expect(d2).toBe(2);
expect(usedD1).toBe(false);
expect(usedD2).toBe(true);
expect(b1.d2).toBe(2);
expect(usedB1).toBe(true);
expect(usedB2).toBe(false);
expect(usedB3).toBe(false);
expect(usedB4).toBe(false);
});
it("complex case should work correctly", () => {
expect(h.f1.e.e1).toBe(10);
expect(h.g1.e1).toBe(10);
expect(usedE1).toBe(true);
expect(usedE2).toBe(false);
});

8
types.d.ts vendored
View File

@ -12,6 +12,7 @@ import {
BinaryExpression,
BlockStatement,
BreakStatement,
ChainExpression,
ClassDeclaration,
ClassExpression,
Comment,
@ -32,6 +33,7 @@ import {
Identifier,
IfStatement,
ImportDeclaration,
ImportExpression,
LabeledStatement,
LogicalExpression,
MemberExpression,
@ -2521,7 +2523,9 @@ type Expression =
| ClassExpression
| MetaProperty
| Identifier
| AwaitExpression;
| AwaitExpression
| ImportExpression
| ChainExpression;
type ExternalItem =
| string
| RegExp
@ -3412,6 +3416,8 @@ declare abstract class JavascriptParser extends Parser {
| MetaProperty
| Identifier
| AwaitExpression
| ImportExpression
| ChainExpression
| Super;
};
getFreeInfoFromVariable(

View File

@ -622,10 +622,10 @@
resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==
"@types/estree@0.0.42":
version "0.0.42"
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.42.tgz#8d0c1f480339efedb3e46070e22dd63e0430dd11"
integrity sha512-K1DPVvnBCPxzD+G51/cxVIoc2X8uUVl1zpJeE6iKcgHMj4+tbat5Xu4TjV7v2QSDbIeAfLi2hIk+u2+s0MlpUQ==
"@types/estree@^0.0.45":
version "0.0.45"
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.45.tgz#e9387572998e5ecdac221950dab3e8c3b16af884"
integrity sha512-jnqIUKDUqJbDIUxm0Uj7bnlMnRm1T/eZ9N+AVMqhPgzrba2GhGG5o/jCTwmdPK709nEZsGoMzXEDUjcXHa3W0g==
"@types/graceful-fs@^4.1.2":
version "4.1.3"
@ -965,7 +965,7 @@ acorn@^6.0.1:
resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.3.0.tgz#0087509119ffa4fc0a0041d1e93a417e68cb856e"
integrity sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==
acorn@^7.0.0, acorn@^7.1.0:
acorn@^7.1.0, acorn@^7.3.0:
version "7.3.1"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.3.1.tgz#85010754db53c3fbaf3b9ea3e083aa5c5d147ffd"
integrity sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==