From ff1e7b00a83ec1f66c8c275be9c1f03c4c52df4f Mon Sep 17 00:00:00 2001 From: Evan Wallace Date: Sun, 11 Apr 2021 02:44:25 -0700 Subject: [PATCH] encode entry point export constraints in the graph --- internal/bundler/linker.go | 83 ++++++++++++------- .../bundler/snapshots/snapshots_default.txt | 26 +++--- .../bundler/snapshots/snapshots_splitting.txt | 56 ++++++------- internal/graph/meta.go | 12 ++- scripts/js-api-tests.js | 20 ++--- 5 files changed, 112 insertions(+), 85 deletions(-) diff --git a/internal/bundler/linker.go b/internal/bundler/linker.go index 030c746c..4484980c 100644 --- a/internal/bundler/linker.go +++ b/internal/bundler/linker.go @@ -1379,6 +1379,57 @@ func (c *linkerContext) scanImportsAndExports() { repr.Meta.WrapperPartIndex = ast.MakeIndex32(partIndex) c.graph.GenerateSymbolImportAndUse(sourceIndex, partIndex, esmRef, 1, runtime.SourceIndex) } + + // If this is an entry point, depend on all exports so they are included + if file.IsEntryPoint() { + var dependencies []js_ast.Dependency + + for _, alias := range repr.Meta.SortedAndFilteredExportAliases { + export := repr.Meta.ResolvedExports[alias] + targetSourceIndex := export.SourceIndex + targetRef := export.Ref + + // If this is an import, then target what the import points to + targetRepr := c.graph.Files[targetSourceIndex].InputFile.Repr.(*graph.JSRepr) + if importData, ok := targetRepr.Meta.ImportsToBind[targetRef]; ok { + targetSourceIndex = importData.SourceIndex + targetRef = importData.Ref + targetRepr = c.graph.Files[targetSourceIndex].InputFile.Repr.(*graph.JSRepr) + dependencies = append(dependencies, importData.ReExports...) + } + + // Pull in all declarations of this symbol + for _, partIndex := range targetRepr.AST.TopLevelSymbolToParts[targetRef] { + dependencies = append(dependencies, js_ast.Dependency{ + SourceIndex: targetSourceIndex, + PartIndex: partIndex, + }) + } + } + + // Ensure "exports" is included if the current output format needs it + if repr.Meta.ForceIncludeExportsForEntryPoint { + dependencies = append(dependencies, js_ast.Dependency{ + SourceIndex: sourceIndex, + PartIndex: repr.Meta.NSExportPartIndex, + }) + } + + // Include the wrapper if present + if repr.Meta.Wrap != graph.WrapNone { + dependencies = append(dependencies, js_ast.Dependency{ + SourceIndex: sourceIndex, + PartIndex: repr.Meta.WrapperPartIndex.GetIndex(), + }) + } + + // Represent these constraints with a dummy part + entryPointPartIndex := c.graph.AddPartToFile(sourceIndex, js_ast.Part{ + Dependencies: dependencies, + CanBeRemovedIfUnused: false, + }) + repr.Meta.EntryPointPartIndex = ast.MakeIndex32(entryPointPartIndex) + } } } @@ -2334,38 +2385,6 @@ func (c *linkerContext) markFileAsLive(sourceIndex uint32) { } } - // If this is an entry point, include all exports - if file.IsEntryPoint() { - for _, alias := range repr.Meta.SortedAndFilteredExportAliases { - export := repr.Meta.ResolvedExports[alias] - targetSourceIndex := export.SourceIndex - targetRef := export.Ref - - // If this is an import, then target what the import points to - targetRepr := c.graph.Files[targetSourceIndex].InputFile.Repr.(*graph.JSRepr) - if importData, ok := targetRepr.Meta.ImportsToBind[targetRef]; ok { - targetSourceIndex = importData.SourceIndex - targetRef = importData.Ref - targetRepr = c.graph.Files[targetSourceIndex].InputFile.Repr.(*graph.JSRepr) - } - - // Pull in all declarations of this symbol - for _, partIndex := range targetRepr.AST.TopLevelSymbolToParts[targetRef] { - c.markPartAsLive(targetSourceIndex, partIndex) - } - } - - // Ensure "exports" is included if the current output format needs it - if repr.Meta.ForceIncludeExportsForEntryPoint { - c.markPartAsLive(sourceIndex, repr.Meta.NSExportPartIndex) - } - - // Include the wrapper if present - if repr.Meta.Wrap != graph.WrapNone { - c.markPartAsLive(sourceIndex, repr.Meta.WrapperPartIndex.GetIndex()) - } - } - case *graph.CSSRepr: // Include all "@import" rules for _, record := range repr.AST.ImportRecords { diff --git a/internal/bundler/snapshots/snapshots_default.txt b/internal/bundler/snapshots/snapshots_default.txt index e779da60..bcc87c3e 100644 --- a/internal/bundler/snapshots/snapshots_default.txt +++ b/internal/bundler/snapshots/snapshots_default.txt @@ -2847,26 +2847,26 @@ var import_es6_ns_export_abstract_class = __toModule(require_es6_ns_export_abstr TestTopLevelAwaitAllowedImportWithSplitting ---------- /out/entry.js ---------- // entry.js -import("./a-ZK7H2YZU.js"); -import("./b-MPIVDXIR.js"); -import("./c-F7O26T5D.js"); +import("./a-6SYMAPP6.js"); +import("./b-A7BBMSAV.js"); +import("./c-745DPZZ6.js"); require_entry(); await 0; ----------- /out/a-ZK7H2YZU.js ---------- -import "./chunk-BYXBJQAS.js"; -import "./chunk-34XDKUTO.js"; +---------- /out/a-6SYMAPP6.js ---------- +import "./chunk-QJYGFXJG.js"; +import "./chunk-G3F22LPE.js"; ----------- /out/b-MPIVDXIR.js ---------- -import "./chunk-BYXBJQAS.js"; -import "./chunk-34XDKUTO.js"; +---------- /out/b-A7BBMSAV.js ---------- +import "./chunk-QJYGFXJG.js"; +import "./chunk-G3F22LPE.js"; ----------- /out/chunk-BYXBJQAS.js ---------- +---------- /out/chunk-QJYGFXJG.js ---------- ----------- /out/c-F7O26T5D.js ---------- -import "./chunk-34XDKUTO.js"; +---------- /out/c-745DPZZ6.js ---------- +import "./chunk-G3F22LPE.js"; ----------- /out/chunk-34XDKUTO.js ---------- +---------- /out/chunk-G3F22LPE.js ---------- // c.js await 0; diff --git a/internal/bundler/snapshots/snapshots_splitting.txt b/internal/bundler/snapshots/snapshots_splitting.txt index 24a03c38..cd3b452f 100644 --- a/internal/bundler/snapshots/snapshots_splitting.txt +++ b/internal/bundler/snapshots/snapshots_splitting.txt @@ -41,7 +41,7 @@ TestSplittingCircularReferenceIssue251 import { p, q -} from "./chunk-G2XWJIBP.js"; +} from "./chunk-7MUWF4J7.js"; export { p, q @@ -51,13 +51,13 @@ export { import { p, q -} from "./chunk-G2XWJIBP.js"; +} from "./chunk-7MUWF4J7.js"; export { p, q }; ----------- /out/chunk-G2XWJIBP.js ---------- +---------- /out/chunk-7MUWF4J7.js ---------- // a.js var p = 5; @@ -205,19 +205,19 @@ TestSplittingDynamicAndNotDynamicCommonJSIntoES6 import { __toModule, require_foo -} from "./chunk-NAN7I22W.js"; +} from "./chunk-DOF77JFX.js"; // entry.js var import_foo = __toModule(require_foo()); -import("./foo-GCHTONSU.js").then(({default: {bar: b}}) => console.log(import_foo.bar, b)); +import("./foo-RHEBPILD.js").then(({default: {bar: b}}) => console.log(import_foo.bar, b)); ----------- /out/foo-GCHTONSU.js ---------- +---------- /out/foo-RHEBPILD.js ---------- import { require_foo -} from "./chunk-NAN7I22W.js"; +} from "./chunk-DOF77JFX.js"; export default require_foo(); ----------- /out/chunk-NAN7I22W.js ---------- +---------- /out/chunk-DOF77JFX.js ---------- // foo.js var require_foo = __commonJS((exports) => { exports.bar = 123; @@ -233,20 +233,20 @@ TestSplittingDynamicAndNotDynamicES6IntoES6 ---------- /out/entry.js ---------- import { bar -} from "./chunk-A3NXEA7F.js"; +} from "./chunk-FIX3TEVC.js"; // entry.js -import("./foo-ZFJDZWZM.js").then(({bar: b}) => console.log(bar, b)); +import("./foo-WSZEPKL7.js").then(({bar: b}) => console.log(bar, b)); ----------- /out/foo-ZFJDZWZM.js ---------- +---------- /out/foo-WSZEPKL7.js ---------- import { bar -} from "./chunk-A3NXEA7F.js"; +} from "./chunk-FIX3TEVC.js"; export { bar }; ----------- /out/chunk-A3NXEA7F.js ---------- +---------- /out/chunk-FIX3TEVC.js ---------- // foo.js var bar = 123; @@ -258,9 +258,9 @@ export { TestSplittingDynamicCommonJSIntoES6 ---------- /out/entry.js ---------- // entry.js -import("./foo-2YYMPLZI.js").then(({default: {bar}}) => console.log(bar)); +import("./foo-BQWGKGO4.js").then(({default: {bar}}) => console.log(bar)); ----------- /out/foo-2YYMPLZI.js ---------- +---------- /out/foo-BQWGKGO4.js ---------- // foo.js var require_foo = __commonJS((exports) => { exports.bar = 123; @@ -271,9 +271,9 @@ export default require_foo(); TestSplittingDynamicES6IntoES6 ---------- /out/entry.js ---------- // entry.js -import("./foo-F64I22OH.js").then(({bar}) => console.log(bar)); +import("./foo-HLOHNJMC.js").then(({bar}) => console.log(bar)); ----------- /out/foo-F64I22OH.js ---------- +---------- /out/foo-HLOHNJMC.js ---------- // foo.js var bar = 123; export { @@ -297,13 +297,13 @@ export { TestSplittingDynamicImportOutsideSourceTreeIssue264 ---------- /out/entry1.js ---------- // Users/user/project/src/entry1.js -import("./package-UW4GHB5S.js"); +import("./package-L6SN4V3F.js"); ---------- /out/entry2.js ---------- // Users/user/project/src/entry2.js -import("./package-UW4GHB5S.js"); +import("./package-L6SN4V3F.js"); ----------- /out/package-UW4GHB5S.js ---------- +---------- /out/package-L6SN4V3F.js ---------- // Users/user/project/node_modules/package/index.js console.log("imported"); @@ -313,7 +313,7 @@ TestSplittingHybridESMAndCJSIssue617 import { foo, init_a -} from "./chunk-UHVVO4ZE.js"; +} from "./chunk-XSW6IF3B.js"; init_a(); export { foo @@ -323,7 +323,7 @@ export { import { a_exports, init_a -} from "./chunk-UHVVO4ZE.js"; +} from "./chunk-XSW6IF3B.js"; // b.js var bar = (init_a(), a_exports); @@ -331,7 +331,7 @@ export { bar }; ----------- /out/chunk-UHVVO4ZE.js ---------- +---------- /out/chunk-XSW6IF3B.js ---------- // a.js var a_exports = {}; __export(a_exports, { @@ -442,9 +442,9 @@ export { TestSplittingPublicPathEntryName ---------- /out/a.js ---------- // a.js -import("/www/b-UNIUFLXI.js"); +import("/www/b-EDTTWD26.js"); ----------- /out/b-UNIUFLXI.js ---------- +---------- /out/b-EDTTWD26.js ---------- // b.js console.log("b"); @@ -453,7 +453,7 @@ TestSplittingReExportIssue273 ---------- /out/a.js ---------- import { a -} from "./chunk-OBLLZOHJ.js"; +} from "./chunk-WJGWDRHM.js"; export { a }; @@ -461,12 +461,12 @@ export { ---------- /out/b.js ---------- import { a -} from "./chunk-OBLLZOHJ.js"; +} from "./chunk-WJGWDRHM.js"; export { a }; ----------- /out/chunk-OBLLZOHJ.js ---------- +---------- /out/chunk-WJGWDRHM.js ---------- // a.js var a = 1; diff --git a/internal/graph/meta.go b/internal/graph/meta.go index 1370b306..a2fad975 100644 --- a/internal/graph/meta.go +++ b/internal/graph/meta.go @@ -106,16 +106,24 @@ type JSReprMeta struct { // This is the index to the automatically-generated part containing code that // calls "__export(exports, { ... getters ... })". This is used to generate // getters on an exports object for ES6 export statements, and is both for - // ES6 star imports and CommonJS-style modules. + // ES6 star imports and CommonJS-style modules. All files have one of these, + // although it may contain no statements if there is nothing to export. NSExportPartIndex uint32 // The index of the automatically-generated part used to represent the // CommonJS or ESM wrapper. This part is empty and is only useful for tree // shaking and code splitting. The wrapper can't be inserted into the part // because the wrapper contains other parts, which can't be represented by - // the current part system. + // the current part system. Only wrapped files have one of these. WrapperPartIndex ast.Index32 + // The index of the automatically-generated part used to handle entry point + // specific stuff. If a certain part is needed by the entry point, it's added + // as a dependency of this part. This is important for parts that are marked + // as removable when unused and that are not used by anything else. Only + // entry point files have one of these. + EntryPointPartIndex ast.Index32 + IsAsyncOrHasAsyncDependency bool DependsOnRuntimeSymbol bool Wrap WrapKind diff --git a/scripts/js-api-tests.js b/scripts/js-api-tests.js index d46e728a..de21a8b7 100644 --- a/scripts/js-api-tests.js +++ b/scripts/js-api-tests.js @@ -958,8 +958,8 @@ body { const inShared = makeInPath(shared); const chunk = 'chunk-M3UIZNA6.js'; const outEntry = makeOutPath(path.relative(testDir, entry)); - const outImport1 = makeOutPath('import1-RW4RT54M.js'); - const outImport2 = makeOutPath('import2-QZ6YEQ7N.js'); + const outImport1 = makeOutPath('import1-HDYVZ7NF.js'); + const outImport2 = makeOutPath('import2-NBRXROVG.js'); const outChunk = makeOutPath(chunk); assert.deepStrictEqual(json.inputs[inEntry], { @@ -1006,8 +1006,8 @@ body { assert.deepStrictEqual(json.outputs[outChunk].exports, []) assert.deepStrictEqual(json.outputs[outEntry].inputs, { [inEntry]: { bytesInOutput: 72 } }) - assert.deepStrictEqual(json.outputs[outImport1].inputs, {}) - assert.deepStrictEqual(json.outputs[outImport2].inputs, {}) + assert.deepStrictEqual(json.outputs[outImport1].inputs, { [inImport1]: { bytesInOutput: 0 } }) + assert.deepStrictEqual(json.outputs[outImport2].inputs, { [inImport2]: { bytesInOutput: 0 } }) assert.deepStrictEqual(json.outputs[outChunk].inputs, { [inShared]: { bytesInOutput: 28 } }) }, @@ -1509,8 +1509,8 @@ export { }; `) - const outputA = 'entry/name=demo/hash=QKCLTVYB.js' - const outputB = 'entry/name=demo/hash=7E3HRWSH.js' + const outputA = 'entry/name=demo/hash=XVIIHBIC.js' + const outputB = 'entry/name=demo/hash=VXSEQP3F.js' assert.strictEqual(value.outputFiles[0].path, path.join(outdir, outputA)) assert.strictEqual(value.outputFiles[1].path, path.join(outdir, outputB)) assert.strictEqual(value.outputFiles[2].path, path.join(outdir, chunk)) @@ -2320,8 +2320,8 @@ console.log("success"); write: false, }) assert.strictEqual(outputFiles.length, 2) - assert.strictEqual(outputFiles[0].path, path.join(testDir, 'entry', 'out', 'YMPAGTFY-1.cjs.js')) - assert.strictEqual(outputFiles[1].path, path.join(testDir, 'entry', 'out', 'MELA46K5-2.mjs.js')) + assert.strictEqual(outputFiles[0].path, path.join(testDir, 'entry', 'out', 'LDM7WUFR-1.cjs.js')) + assert.strictEqual(outputFiles[1].path, path.join(testDir, 'entry', 'out', '74SLWWSF-2.mjs.js')) }, async customEntryPointOutputPathsAbs({ esbuild, testDir }) { @@ -2341,8 +2341,8 @@ console.log("success"); write: false, }) assert.strictEqual(outputFiles.length, 2) - assert.strictEqual(outputFiles[0].path, path.join(testDir, 'entry', 'out', 'E5ZZI63T-1.js')) - assert.strictEqual(outputFiles[1].path, path.join(testDir, 'entry', 'out', 'FZK5DQMJ-2.js')) + assert.strictEqual(outputFiles[0].path, path.join(testDir, 'entry', 'out', 'MQUIWIER-1.js')) + assert.strictEqual(outputFiles[1].path, path.join(testDir, 'entry', 'out', 'ATGPBOSJ-2.js')) }, }