diff --git a/CHANGELOG.md b/CHANGELOG.md index c389156d010..1c0c3bcda73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +* Fix asset references with the `--line-limit` flag ([#3286](https://github.com/evanw/esbuild/issues/3286)) + + The recently-released `--line-limit` flag tells esbuild to terminate long lines after they pass this length limit. This includes automatically wrapping long strings across multiple lines using escaped newline syntax. However, using this could cause esbuild to generate incorrect code for references from generated output files to assets in the bundle (i.e. files loaded with the `file` or `copy` loaders). This is because esbuild implements asset references internally using find-and-replace with a randomly-generated string, but the find operation fails if the string is split by an escaped newline due to line wrapping. This release fixes the problem by not wrapping these strings. This issue affected asset references in both JS and CSS files. + * Support local names in CSS for `@keyframe`, `@counter-style`, and `@container` ([#20](https://github.com/evanw/esbuild/issues/20)) This release extends support for local names in CSS files loaded with the `local-css` loader to cover the `@keyframe`, `@counter-style`, and `@container` rules (and also `animation`, `list-style`, and `container` declarations). Here's an example: diff --git a/internal/ast/ast.go b/internal/ast/ast.go index ce470f6231d..38e0218bc81 100644 --- a/internal/ast/ast.go +++ b/internal/ast/ast.go @@ -124,6 +124,10 @@ const ( // CSS "@import" of an empty file should be removed WasLoadedWithEmptyLoader + + // Unique keys are randomly-generated strings that are used to replace paths + // in the source code after it's printed. These must not ever be split apart. + ContainsUniqueKey ) func (flags ImportRecordFlags) Has(flag ImportRecordFlags) bool { diff --git a/internal/bundler/bundler.go b/internal/bundler/bundler.go index f702c119d0c..3e9db7cb469 100644 --- a/internal/bundler/bundler.go +++ b/internal/bundler/bundler.go @@ -310,7 +310,10 @@ func parseFile(args parseArgs) { case config.LoaderFile: uniqueKey := fmt.Sprintf("%sA%08d", args.uniqueKeyPrefix, args.sourceIndex) uniqueKeyPath := uniqueKey + source.KeyPath.IgnoredSuffix - expr := js_ast.Expr{Data: &js_ast.EString{Value: helpers.StringToUTF16(uniqueKeyPath)}} + expr := js_ast.Expr{Data: &js_ast.EString{ + Value: helpers.StringToUTF16(uniqueKeyPath), + ContainsUniqueKey: true, + }} ast := js_parser.LazyExportAST(args.log, source, js_parser.OptionsFromConfig(&args.options), expr, "") ast.URLForCSS = uniqueKeyPath if pluginName != "" { diff --git a/internal/bundler_tests/bundler_default_test.go b/internal/bundler_tests/bundler_default_test.go index 2eb614a7a19..d750a8d6032 100644 --- a/internal/bundler_tests/bundler_default_test.go +++ b/internal/bundler_tests/bundler_default_test.go @@ -8321,6 +8321,9 @@ func TestLineLimitNotMinified(t *testing.T) { default_suite.expectBundled(t, bundled{ files: map[string]string{ "/script.jsx": ` + import fileURL from './x.file' + import copyURL from './x.copy' + import dataURL from './x.data' export const SignUpForm = (props) => { return

@@ -8329,6 +8332,9 @@ func TestLineLimitNotMinified(t *testing.T) { {props.buttonText} By signing up, you are agreeing to our terms of service. + + +

} `, @@ -8343,16 +8349,30 @@ func TestLineLimitNotMinified(t *testing.T) { `CIgZmlsbD0iI0ZGQ0YwMCIvPgogIDxwYXRoIGQ9Ik00Ny41IDUyLjVMOTUgMTAwbC00Ny41IDQ3LjVtNjAtOTVMM` + `TU1IDEwMGwtNDcuNSA0Ny41IiBmaWxsPSJub25lIiBzdHJva2U9IiMxOTE5MTkiIHN0cm9rZS13aWR0aD0iMjQiL` + `z4KPC9zdmc+Cg==); + cursor: url(x.file); + cursor: url(x.copy); + cursor: url(x.data); } `, + "/x.file": `...file...`, + "/x.copy": `...copy...`, + "/x.data": `...lots of long data...lots of long data...`, }, entryPaths: []string{ "/script.jsx", "/style.css", }, options: config.Options{ + Mode: config.ModeBundle, AbsOutputDir: "/out", LineLimit: 32, + ExtensionToLoader: map[string]config.Loader{ + ".jsx": config.LoaderJSX, + ".css": config.LoaderCSS, + ".file": config.LoaderFile, + ".copy": config.LoaderCopy, + ".data": config.LoaderDataURL, + }, }, }) } diff --git a/internal/bundler_tests/snapshots/snapshots_default.txt b/internal/bundler_tests/snapshots/snapshots_default.txt index be5ce957ea6..c71533ba2af 100644 --- a/internal/bundler_tests/snapshots/snapshots_default.txt +++ b/internal/bundler_tests/snapshots/snapshots_default.txt @@ -2979,8 +2979,24 @@ BzdHJva2U9IiMxOTE5MTkiIHN0cm9rZS\ ================================================================================ TestLineLimitNotMinified +---------- /out/x-TZ25B4WH.file ---------- +...file... +---------- /out/x-UF3O47Y3.copy ---------- +...copy... ---------- /out/script.js ---------- -export const SignUpForm = (props) => { +// x.file +var x_default = "./x-TZ25B4WH.file"; + +// script.jsx +import copyURL from "./x-UF3O47Y3.copy"; + +// x.data +var x_default2 = "data:text/plai\ +n;charset=utf-8,...lots of long \ +data...lots of long data..."; + +// script.jsx +var SignUpForm = (props) => { return /* @__PURE__ */ React.createElement( "p", { class: "signup" }, /* @__PURE__ */ React. createElement("label", null, "\ @@ -2998,10 +3014,19 @@ led" }, props.buttonText), /* @__PURE__ */ React. By signing up, you are agreeing \ to our ", /* @__PURE__ */ React. createElement("a", { href: "/t\ -os/" }, "terms of service"), ".")); +os/" }, "terms of service"), "."), + /* @__PURE__ */ React.createElement( + "img", { src: x_default }), /* @__PURE__ */ React. + createElement("img", { src: copyURL }), + /* @__PURE__ */ React.createElement( + "img", { src: x_default2 })); +}; +export { + SignUpForm }; ---------- /out/style.css ---------- +/* style.css */ body.light-mode.new-user-segment:not(.logged-in) .signup, body.light-mode.new-user-segment:not(.logged-in) @@ -3027,6 +3052,11 @@ tNjAtOTVMMTU1IDEwMGwtNDcuNSA0Ny4\ 1IiBmaWxsPSJub25lIiBzdHJva2U9IiM\ xOTE5MTkiIHN0cm9rZS13aWR0aD0iMjQ\ iLz4KPC9zdmc+Cg=="); + cursor: url(./x-TZ25B4WH.file); + cursor: url(./x-UF3O47Y3.copy); + cursor: url("data:text/plain;c\ +harset=utf-8,...lots of long dat\ +a...lots of long data..."); } ================================================================================ diff --git a/internal/css_printer/css_printer.go b/internal/css_printer/css_printer.go index 194cf93cd32..caeaf2e19d5 100644 --- a/internal/css_printer/css_printer.go +++ b/internal/css_printer/css_printer.go @@ -154,7 +154,7 @@ func (p *printer) printRule(rule css_ast.Rule, indent int32, omitTrailingSemicol p.print("@charset ") // It's not valid to print the string with single quotes - p.printQuotedWithQuote(r.Encoding, '"') + p.printQuotedWithQuote(r.Encoding, '"', 0) p.print(";") case *css_ast.RAtImport: @@ -163,7 +163,12 @@ func (p *printer) printRule(rule css_ast.Rule, indent int32, omitTrailingSemicol } else { p.print("@import ") } - p.printQuoted(p.importRecords[r.ImportRecordIndex].Path.Text) + record := p.importRecords[r.ImportRecordIndex] + var flags printQuotedFlags + if record.Flags.Has(ast.ContainsUniqueKey) { + flags |= printQuotedNoWrap + } + p.printQuoted(record.Path.Text, flags) p.recordImportPathForMetafile(r.ImportRecordIndex) p.printTokens(r.ImportConditions, printTokensOpts{}) p.print(";") @@ -485,7 +490,7 @@ func (p *printer) printCompoundSelector(sel css_ast.CompoundSelector, isFirst bo if printAsIdent { p.printIdent(s.MatcherValue, identNormal, canDiscardWhitespaceAfter) } else { - p.printQuoted(s.MatcherValue) + p.printQuoted(s.MatcherValue, 0) } } if s.MatcherModifier != 0 { @@ -633,8 +638,14 @@ func bestQuoteCharForString(text string, forURL bool) byte { return '"' } -func (p *printer) printQuoted(text string) { - p.printQuotedWithQuote(text, bestQuoteCharForString(text, false)) +type printQuotedFlags uint8 + +const ( + printQuotedNoWrap printQuotedFlags = 1 << iota +) + +func (p *printer) printQuoted(text string, flags printQuotedFlags) { + p.printQuotedWithQuote(text, bestQuoteCharForString(text, false), flags) } type escapeKind uint8 @@ -685,7 +696,7 @@ func (p *printer) printWithEscape(c rune, escape escapeKind, remainingText strin } // Note: This function is hot in profiles -func (p *printer) printQuotedWithQuote(text string, quote byte) { +func (p *printer) printQuotedWithQuote(text string, quote byte, flags printQuotedFlags) { if quote != quoteForURL { p.css = append(p.css, quote) } @@ -697,7 +708,7 @@ func (p *printer) printQuotedWithQuote(text string, quote byte) { // Only compute the line length if necessary var startLineLength int wrapLongLines := false - if p.options.LineLimit > 0 && quote != quoteForURL { + if p.options.LineLimit > 0 && quote != quoteForURL && (flags&printQuotedNoWrap) == 0 { startLineLength = p.currentLineLength() if startLineLength > p.options.LineLimit { startLineLength = p.options.LineLimit @@ -983,16 +994,20 @@ func (p *printer) printTokens(tokens []css_ast.Token, opts printTokensOpts) bool p.printIdent(t.Text, identHash, whitespace) case css_lexer.TString: - p.printQuoted(t.Text) + p.printQuoted(t.Text, 0) case css_lexer.TURL: - text := p.importRecords[t.PayloadIndex].Path.Text + record := p.importRecords[t.PayloadIndex] + text := record.Path.Text tryToAvoidQuote := true - if p.options.LineLimit > 0 && p.currentLineLength()+len(text) >= p.options.LineLimit { + var flags printQuotedFlags + if record.Flags.Has(ast.ContainsUniqueKey) { + flags |= printQuotedNoWrap + } else if p.options.LineLimit > 0 && p.currentLineLength()+len(text) >= p.options.LineLimit { tryToAvoidQuote = false } p.print("url(") - p.printQuotedWithQuote(text, bestQuoteCharForString(text, tryToAvoidQuote)) + p.printQuotedWithQuote(text, bestQuoteCharForString(text, tryToAvoidQuote), flags) p.print(")") p.recordImportPathForMetafile(t.PayloadIndex) diff --git a/internal/css_printer/css_printer_test.go b/internal/css_printer/css_printer_test.go index 87dc64e820e..76b3695503d 100644 --- a/internal/css_printer/css_printer_test.go +++ b/internal/css_printer/css_printer_test.go @@ -57,7 +57,7 @@ func expectPrintedString(t *testing.T, stringValue string, expected string) { t.Run(stringValue, func(t *testing.T) { t.Helper() p := printer{} - p.printQuoted(stringValue) + p.printQuoted(stringValue, 0) test.AssertEqualWithDiff(t, string(p.css), expected) }) } diff --git a/internal/js_ast/js_ast.go b/internal/js_ast/js_ast.go index da51000e79c..1b9adac56fc 100644 --- a/internal/js_ast/js_ast.go +++ b/internal/js_ast/js_ast.go @@ -779,6 +779,7 @@ type EString struct { LegacyOctalLoc logger.Loc PreferTemplate bool HasPropertyKeyComment bool // If true, a preceding comment contains "@__KEY__" + ContainsUniqueKey bool // If true, this string must not be wrapped } type TemplatePart struct { diff --git a/internal/js_printer/js_printer.go b/internal/js_printer/js_printer.go index c0764b31183..a8ed7db03bc 100644 --- a/internal/js_printer/js_printer.go +++ b/internal/js_printer/js_printer.go @@ -61,7 +61,7 @@ func QuoteIdentifier(js []byte, name string, unsupportedFeatures compat.JSFeatur return js } -func (p *printer) printUnquotedUTF16(text []uint16, quote rune) { +func (p *printer) printUnquotedUTF16(text []uint16, quote rune, flags printQuotedFlags) { temp := make([]byte, utf8.UTFMax) js := p.js i := 0 @@ -70,7 +70,7 @@ func (p *printer) printUnquotedUTF16(text []uint16, quote rune) { // Only compute the line length if necessary var startLineLength int wrapLongLines := false - if p.options.LineLimit > 0 { + if p.options.LineLimit > 0 && (flags&printQuotedNoWrap) == 0 { startLineLength = p.currentLineLength() if startLineLength > p.options.LineLimit { startLineLength = p.options.LineLimit @@ -389,8 +389,15 @@ func (p *printer) printBytes(bytes []byte) { p.js = append(p.js, bytes...) } -func (p *printer) printQuotedUTF8(text string, allowBacktick bool) { - p.printQuotedUTF16(helpers.StringToUTF16(text), allowBacktick) +type printQuotedFlags uint8 + +const ( + printQuotedAllowBacktick printQuotedFlags = 1 << iota + printQuotedNoWrap +) + +func (p *printer) printQuotedUTF8(text string, flags printQuotedFlags) { + p.printQuotedUTF16(helpers.StringToUTF16(text), flags) } func (p *printer) addSourceMapping(loc logger.Loc) { @@ -463,7 +470,7 @@ func (p *printer) printClauseAlias(loc logger.Loc, alias string) { p.printIdentifier(alias) } else { p.addSourceMapping(loc) - p.printQuotedUTF8(alias, false /* allowBacktick */) + p.printQuotedUTF8(alias, 0) } } @@ -776,7 +783,7 @@ func (p *printer) printBinding(binding js_ast.Binding) { } } else { p.addSourceMapping(property.Key.Loc) - p.printQuotedUTF8(name, false /* allowBacktick */) + p.printQuotedUTF8(name, 0) } } else { p.printExpr(property.Key, js_ast.LLowest, 0) @@ -1234,7 +1241,7 @@ func (p *printer) printProperty(property js_ast.Property) { } } else { p.addSourceMapping(property.Key.Loc) - p.printQuotedUTF8(name, false /* allowBacktick */) + p.printQuotedUTF8(name, 0) } case *js_ast.EString: @@ -1283,7 +1290,7 @@ func (p *printer) printProperty(property js_ast.Property) { p.printIdentifierUTF16(key.Value) } else { p.addSourceMapping(property.Key.Loc) - p.printQuotedUTF16(key.Value, false /* allowBacktick */) + p.printQuotedUTF16(key.Value, 0) } default: @@ -1317,9 +1324,9 @@ func (p *printer) printProperty(property js_ast.Property) { } } -func (p *printer) printQuotedUTF16(data []uint16, allowBacktick bool) { +func (p *printer) printQuotedUTF16(data []uint16, flags printQuotedFlags) { if p.options.UnsupportedFeatures.Has(compat.TemplateLiteral) { - allowBacktick = false + flags &= ^printQuotedAllowBacktick } singleCost := 0 @@ -1351,15 +1358,15 @@ func (p *printer) printQuotedUTF16(data []uint16, allowBacktick bool) { c := "\"" if doubleCost > singleCost { c = "'" - if singleCost > backtickCost && allowBacktick { + if singleCost > backtickCost && (flags&printQuotedAllowBacktick) != 0 { c = "`" } - } else if doubleCost > backtickCost && allowBacktick { + } else if doubleCost > backtickCost && (flags&printQuotedAllowBacktick) != 0 { c = "`" } p.print(c) - p.printUnquotedUTF16(data, rune(c[0])) + p.printUnquotedUTF16(data, rune(c[0]), flags) p.print(c) } @@ -2007,7 +2014,7 @@ func (p *printer) printExpr(expr js_ast.Expr, level js_ast.L, flags printExprFla p.print("/* @__KEY__ */ ") } - p.printQuotedUTF8(name, true) + p.printQuotedUTF8(name, printQuotedAllowBacktick) case *js_ast.EJSXElement: // Start the opening tag @@ -2463,7 +2470,7 @@ func (p *printer) printExpr(expr js_ast.Expr, level js_ast.L, flags printExprFla // Inline cross-module TypeScript enum references here if value, ok := p.tryToGetImportedEnumValue(e.Target, e.Name); ok { if value.String != nil { - p.printQuotedUTF16(value.String, true /* allowBacktick */) + p.printQuotedUTF16(value.String, printQuotedAllowBacktick) } else { p.printNumber(value.Number, level) } @@ -2503,7 +2510,7 @@ func (p *printer) printExpr(expr js_ast.Expr, level js_ast.L, flags printExprFla } p.print("[") p.addSourceMapping(e.NameLoc) - p.printQuotedUTF8(e.Name, true /* allowBacktick */) + p.printQuotedUTF8(e.Name, printQuotedAllowBacktick) p.print("]") } if wrap { @@ -2518,7 +2525,7 @@ func (p *printer) printExpr(expr js_ast.Expr, level js_ast.L, flags printExprFla if index, ok := e.Index.Data.(*js_ast.EString); ok { if value, name, ok := p.tryToGetImportedEnumValueUTF16(e.Target, index.Value); ok { if value.String != nil { - p.printQuotedUTF16(value.String, true /* allowBacktick */) + p.printQuotedUTF16(value.String, printQuotedAllowBacktick) } else { p.printNumber(value.Number, level) } @@ -2845,6 +2852,10 @@ func (p *printer) printExpr(expr js_ast.Expr, level js_ast.L, flags printExprFla } case *js_ast.EString: + var flags printQuotedFlags + if e.ContainsUniqueKey { + flags = printQuotedNoWrap + } p.addSourceMapping(expr.Loc) if !p.options.MinifyWhitespace && e.HasPropertyKeyComment { @@ -2854,12 +2865,12 @@ func (p *printer) printExpr(expr js_ast.Expr, level js_ast.L, flags printExprFla // If this was originally a template literal, print it as one as long as we're not minifying if e.PreferTemplate && !p.options.MinifySyntax && !p.options.UnsupportedFeatures.Has(compat.TemplateLiteral) { p.print("`") - p.printUnquotedUTF16(e.Value, '`') + p.printUnquotedUTF16(e.Value, '`', flags) p.print("`") return } - p.printQuotedUTF16(e.Value, true /* allowBacktick */) + p.printQuotedUTF16(e.Value, flags|printQuotedAllowBacktick) case *js_ast.ETemplate: if p.options.MinifySyntax && e.TagOrNil.Data == nil { @@ -2895,7 +2906,7 @@ func (p *printer) printExpr(expr js_ast.Expr, level js_ast.L, flags printExprFla copy.Parts = replaced switch e2 := js_ast.InlineStringsAndNumbersIntoTemplate(logger.Loc{}, ©).Data.(type) { case *js_ast.EString: - p.printQuotedUTF16(e2.Value, true /* allowBacktick */) + p.printQuotedUTF16(e2.Value, printQuotedAllowBacktick) return case *js_ast.ETemplate: e = e2 @@ -2905,7 +2916,7 @@ func (p *printer) printExpr(expr js_ast.Expr, level js_ast.L, flags printExprFla // Convert no-substitution template literals into strings if it's smaller if len(e.Parts) == 0 { p.addSourceMapping(expr.Loc) - p.printQuotedUTF16(e.HeadCooked, true /* allowBacktick */) + p.printQuotedUTF16(e.HeadCooked, printQuotedAllowBacktick) return } } @@ -2937,7 +2948,7 @@ func (p *printer) printExpr(expr js_ast.Expr, level js_ast.L, flags printExprFla if e.TagOrNil.Data != nil { p.print(e.HeadRaw) } else { - p.printUnquotedUTF16(e.HeadCooked, '`') + p.printUnquotedUTF16(e.HeadCooked, '`', 0) } for _, part := range e.Parts { p.print("${") @@ -2947,7 +2958,7 @@ func (p *printer) printExpr(expr js_ast.Expr, level js_ast.L, flags printExprFla if e.TagOrNil.Data != nil { p.print(part.TailRaw) } else { - p.printUnquotedUTF16(part.TailCooked, '`') + p.printUnquotedUTF16(part.TailCooked, '`', 0) } } p.print("`") @@ -3032,7 +3043,7 @@ func (p *printer) printExpr(expr js_ast.Expr, level js_ast.L, flags printExprFla } else { p.print("[") p.addSourceMappingForName(expr.Loc, alias, ref) - p.printQuotedUTF8(alias, true /* allowBacktick */) + p.printQuotedUTF8(alias, printQuotedAllowBacktick) p.print("]") } if wrap { @@ -3781,7 +3792,7 @@ func (p *printer) printIndentedComment(text string) { func (p *printer) printPath(importRecordIndex uint32, importKind ast.ImportKind) { record := p.importRecords[importRecordIndex] p.addSourceMapping(record.Range.Loc) - p.printQuotedUTF8(record.Path.Text, false /* allowBacktick */) + p.printQuotedUTF8(record.Path.Text, printQuotedNoWrap) if p.options.NeedsMetafile { external := "" @@ -3904,7 +3915,7 @@ func (p *printer) printImportAssertionsClause(assertions ast.ImportAssertions) { p.printSpaceBeforeIdentifier() p.printIdentifierUTF16(entry.Key) } else { - p.printQuotedUTF16(entry.Key, false /* allowBacktick */) + p.printQuotedUTF16(entry.Key, 0) } p.print(":") @@ -3915,12 +3926,12 @@ func (p *printer) printImportAssertionsClause(assertions ast.ImportAssertions) { p.printIndent() p.printExprCommentsAtLoc(entry.ValueLoc) p.addSourceMapping(entry.ValueLoc) - p.printQuotedUTF16(entry.Value, false /* allowBacktick */) + p.printQuotedUTF16(entry.Value, 0) p.options.Indent-- } else { p.printSpace() p.addSourceMapping(entry.ValueLoc) - p.printQuotedUTF16(entry.Value, false /* allowBacktick */) + p.printQuotedUTF16(entry.Value, 0) } } @@ -4652,7 +4663,7 @@ func (p *printer) printStmt(stmt js_ast.Stmt, flags printStmtFlags) { p.addSourceMapping(stmt.Loc) p.printIndent() p.printSpaceBeforeIdentifier() - p.printQuotedUTF16(s.Value, false /* allowBacktick */) + p.printQuotedUTF16(s.Value, 0) p.printSemicolonAfterStatement() case *js_ast.SBreak: @@ -4828,7 +4839,7 @@ func Print(tree js_ast.AST, symbols ast.SymbolMap, r renamer.Renamer, options Op // Add the top-level directive if present for _, directive := range tree.Directives { p.printIndent() - p.printQuotedUTF8(directive, options.ASCIIOnly) + p.printQuotedUTF8(directive, 0) p.print(";") p.printNewline() } diff --git a/internal/linker/linker.go b/internal/linker/linker.go index 0759650106f..a3902374ac7 100644 --- a/internal/linker/linker.go +++ b/internal/linker/linker.go @@ -942,7 +942,7 @@ func (c *linkerContext) computeCrossChunkDependencies() { otherChunkIndex := c.graph.Files[record.SourceIndex.GetIndex()].EntryPointChunkIndex record.Path.Text = c.chunks[otherChunkIndex].uniqueKey record.SourceIndex = ast.Index32{} - record.Flags |= ast.ShouldNotBeExternalInMetafile + record.Flags |= ast.ShouldNotBeExternalInMetafile | ast.ContainsUniqueKey // Track this cross-chunk dynamic import so we make sure to // include its hash when we're calculating the hashes of all @@ -1302,6 +1302,9 @@ func (c *linkerContext) scanImportsAndExports() { } else { record.Flags |= ast.ShouldNotBeExternalInMetafile } + if strings.Contains(otherRepr.AST.URLForCSS, c.uniqueKeyPrefix) { + record.Flags |= ast.ContainsUniqueKey + } // Copy the additional files to the output directory additionalFiles = append(additionalFiles, otherFile.InputFile.AdditionalFiles...) @@ -1312,7 +1315,7 @@ func (c *linkerContext) scanImportsAndExports() { record.Path.Text = otherRepr.URLForCode record.Path.Namespace = "" record.CopySourceIndex = ast.Index32{} - record.Flags |= ast.ShouldNotBeExternalInMetafile + record.Flags |= ast.ShouldNotBeExternalInMetafile | ast.ContainsUniqueKey // Copy the additional files to the output directory additionalFiles = append(additionalFiles, otherFile.InputFile.AdditionalFiles...) @@ -1330,7 +1333,7 @@ func (c *linkerContext) scanImportsAndExports() { record.Path.Text = otherRepr.URLForCode record.Path.Namespace = "" record.CopySourceIndex = ast.Index32{} - record.Flags |= ast.ShouldNotBeExternalInMetafile + record.Flags |= ast.ShouldNotBeExternalInMetafile | ast.ContainsUniqueKey // Copy the additional files to the output directory additionalFiles = append(additionalFiles, otherFile.InputFile.AdditionalFiles...) @@ -4973,7 +4976,7 @@ func (c *linkerContext) generateChunkJS(chunkIndex int, chunkWaitGroup *sync.Wai crossChunkImportRecords[i] = ast.ImportRecord{ Kind: chunkImport.importKind, Path: logger.Path{Text: c.chunks[chunkImport.chunkIndex].uniqueKey}, - Flags: ast.ShouldNotBeExternalInMetafile, + Flags: ast.ShouldNotBeExternalInMetafile | ast.ContainsUniqueKey, } } crossChunkResult := js_printer.Print(js_ast.AST{