Skip to content

Commit 516ca31

Browse files
committed
fix #3343: allow bundle-internal string aliases
1 parent 9e2f304 commit 516ca31

7 files changed

Lines changed: 289 additions & 17 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,10 @@
110110
111111
Chrome shipped this new CSS at-rule in version 125 as part of the [CSS anchor positioning API](https://developer.chrome.com/blog/anchor-positioning-api). With this release, esbuild now knows to expect a declaration list inside of the `@position-try` body block and will format it appropriately.
112112
113+
* Always allow internal string import and export aliases ([#3343](https://github.com/evanw/esbuild/issues/3343))
114+
115+
Import and export names can be string literals in ES2022+. Previously esbuild forbid any usage of these aliases when the target was below ES2022. Starting with this release, esbuild will only forbid such usage when the alias would otherwise end up in output as a string literal. String literal aliases that are only used internally in the bundle and are "compiled away" are no longer errors. This makes it possible to use string literal aliases with esbuild's `inject` feature even when the target is earlier than ES2022.
116+
113117
## 0.21.3
114118
115119
* Implement the decorator metadata proposal ([#3760](https://github.com/evanw/esbuild/issues/3760))

internal/bundler_tests/bundler_default_test.go

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8894,3 +8894,204 @@ func TestObjectLiteralProtoSetterEdgeCasesMinifySyntax(t *testing.T) {
88948894
},
88958895
})
88968896
}
8897+
8898+
func TestForbidStringImportNamesNoBundle(t *testing.T) {
8899+
default_suite.expectBundled(t, bundled{
8900+
files: map[string]string{
8901+
"/entry.js": `
8902+
import { "an import" as anImport } from "./foo"
8903+
export { "another import" as "an export" } from "./foo"
8904+
anImport()
8905+
`,
8906+
},
8907+
entryPaths: []string{"/entry.js"},
8908+
options: config.Options{
8909+
Mode: config.ModePassThrough,
8910+
AbsOutputFile: "/out.js",
8911+
UnsupportedJSFeatures: compat.ArbitraryModuleNamespaceNames,
8912+
},
8913+
expectedCompileLog: `entry.js: ERROR: Using the string "an import" as an import name is not supported in the configured target environment
8914+
entry.js: ERROR: Using the string "another import" as an import name is not supported in the configured target environment
8915+
entry.js: ERROR: Using the string "an export" as an export name is not supported in the configured target environment
8916+
`,
8917+
})
8918+
}
8919+
8920+
func TestForbidStringExportNamesNoBundle(t *testing.T) {
8921+
default_suite.expectBundled(t, bundled{
8922+
files: map[string]string{
8923+
"/entry.js": `
8924+
let ok = true
8925+
export { ok as "ok", ok as "not ok" }
8926+
export { "same name" } from "./foo"
8927+
export { "name 1" as "name 2" } from "./foo"
8928+
export * as "name space" from "./foo"
8929+
`,
8930+
},
8931+
entryPaths: []string{"/entry.js"},
8932+
options: config.Options{
8933+
Mode: config.ModePassThrough,
8934+
AbsOutputFile: "/out.js",
8935+
UnsupportedJSFeatures: compat.ArbitraryModuleNamespaceNames,
8936+
},
8937+
expectedCompileLog: `entry.js: ERROR: Using the string "not ok" as an export name is not supported in the configured target environment
8938+
entry.js: ERROR: Using the string "same name" as an export name is not supported in the configured target environment
8939+
entry.js: ERROR: Using the string "name 1" as an import name is not supported in the configured target environment
8940+
entry.js: ERROR: Using the string "name 2" as an export name is not supported in the configured target environment
8941+
entry.js: ERROR: Using the string "name space" as an export name is not supported in the configured target environment
8942+
`,
8943+
})
8944+
}
8945+
8946+
func TestForbidStringImportNamesBundle(t *testing.T) {
8947+
default_suite.expectBundled(t, bundled{
8948+
files: map[string]string{
8949+
"/entry.js": `
8950+
import { "nest ed" as nested } from "./nested.js"
8951+
export { nested }
8952+
`,
8953+
"/nested.js": `
8954+
import { "some import" as nested } from "external"
8955+
export { nested as "nest ed" }
8956+
`,
8957+
},
8958+
entryPaths: []string{"/entry.js"},
8959+
options: config.Options{
8960+
Mode: config.ModeBundle,
8961+
AbsOutputFile: "/out.js",
8962+
UnsupportedJSFeatures: compat.ArbitraryModuleNamespaceNames,
8963+
ExternalSettings: config.ExternalSettings{
8964+
PreResolve: config.ExternalMatchers{Exact: map[string]bool{
8965+
"external": true,
8966+
}},
8967+
},
8968+
},
8969+
expectedCompileLog: `nested.js: ERROR: Using the string "some import" as an import name is not supported in the configured target environment
8970+
`,
8971+
})
8972+
}
8973+
8974+
func TestForbidStringExportNamesBundle(t *testing.T) {
8975+
default_suite.expectBundled(t, bundled{
8976+
files: map[string]string{
8977+
"/entry.js": `
8978+
import { "o.k." as ok } from "./internal.js"
8979+
export { ok as "ok", ok as "not ok" }
8980+
export * from "./nested.js"
8981+
export * as "name space" from "./nested.js"
8982+
`,
8983+
"/internal.js": `
8984+
let ok = true
8985+
export { ok as "o.k." }
8986+
`,
8987+
"/nested.js": `
8988+
export * from "./very-nested.js"
8989+
let nested = 1
8990+
export { nested as "nested name" }
8991+
`,
8992+
"/very-nested.js": `
8993+
let nested = 2
8994+
export { nested as "very nested name" }
8995+
`,
8996+
},
8997+
entryPaths: []string{"/entry.js"},
8998+
options: config.Options{
8999+
Mode: config.ModeBundle,
9000+
AbsOutputFile: "/out.js",
9001+
UnsupportedJSFeatures: compat.ArbitraryModuleNamespaceNames,
9002+
},
9003+
expectedCompileLog: `entry.js: ERROR: Using the string "not ok" as an export name is not supported in the configured target environment
9004+
entry.js: ERROR: Using the string "name space" as an export name is not supported in the configured target environment
9005+
nested.js: ERROR: Using the string "nested name" as an export name is not supported in the configured target environment
9006+
very-nested.js: ERROR: Using the string "very nested name" as an export name is not supported in the configured target environment
9007+
`,
9008+
})
9009+
}
9010+
9011+
func TestInjectWithStringExportNameNoBundle(t *testing.T) {
9012+
default_suite.expectBundled(t, bundled{
9013+
files: map[string]string{
9014+
"/entry.js": `
9015+
console.log(test)
9016+
`,
9017+
"/inject.js": `
9018+
const old = console.log
9019+
const fn = (...args) => old.apply(console, ['log:'].concat(args))
9020+
export { fn as "console.log" }
9021+
`,
9022+
},
9023+
entryPaths: []string{"/entry.js"},
9024+
options: config.Options{
9025+
Mode: config.ModePassThrough,
9026+
AbsOutputFile: "/out.js",
9027+
InjectPaths: []string{"/inject.js"},
9028+
UnsupportedJSFeatures: compat.ArbitraryModuleNamespaceNames,
9029+
},
9030+
})
9031+
}
9032+
9033+
func TestInjectWithStringExportNameBundle(t *testing.T) {
9034+
default_suite.expectBundled(t, bundled{
9035+
files: map[string]string{
9036+
"/entry.js": `
9037+
console.log(test)
9038+
console.info(test)
9039+
console.warn(test)
9040+
`,
9041+
"/inject.js": `
9042+
const old = console.log
9043+
const fn = (...args) => old.apply(console, ['log:'].concat(args))
9044+
export { fn as "console.log" }
9045+
export { "console.log" as "console.info" } from "./inject.js"
9046+
import { "console.info" as info } from "./inject.js"
9047+
export { info as "console.warn" }
9048+
`,
9049+
},
9050+
entryPaths: []string{"/entry.js"},
9051+
options: config.Options{
9052+
Mode: config.ModeBundle,
9053+
AbsOutputFile: "/out.js",
9054+
InjectPaths: []string{"/inject.js"},
9055+
UnsupportedJSFeatures: compat.ArbitraryModuleNamespaceNames,
9056+
},
9057+
})
9058+
}
9059+
9060+
func TestStringExportNamesCommonJS(t *testing.T) {
9061+
default_suite.expectBundled(t, bundled{
9062+
files: map[string]string{
9063+
"/entry.js": `
9064+
import { "some import" as someImport } from "./foo"
9065+
export { someImport as "some export" }
9066+
export * as "all the stuff" from "./foo"
9067+
`,
9068+
},
9069+
entryPaths: []string{"/entry.js"},
9070+
options: config.Options{
9071+
Mode: config.ModeConvertFormat,
9072+
AbsOutputFile: "/out.js",
9073+
OutputFormat: config.FormatCommonJS,
9074+
UnsupportedJSFeatures: compat.ArbitraryModuleNamespaceNames,
9075+
},
9076+
})
9077+
}
9078+
9079+
func TestStringExportNamesIIFE(t *testing.T) {
9080+
default_suite.expectBundled(t, bundled{
9081+
files: map[string]string{
9082+
"/entry.js": `
9083+
import { "some import" as someImport } from "./foo"
9084+
export { someImport as "some export" }
9085+
export * as "all the stuff" from "./foo"
9086+
`,
9087+
},
9088+
entryPaths: []string{"/entry.js"},
9089+
options: config.Options{
9090+
Mode: config.ModeConvertFormat,
9091+
AbsOutputFile: "/out.js",
9092+
OutputFormat: config.FormatIIFE,
9093+
UnsupportedJSFeatures: compat.ArbitraryModuleNamespaceNames,
9094+
GlobalName: []string{"global", "name"},
9095+
},
9096+
})
9097+
}

internal/bundler_tests/snapshots/snapshots_default.txt

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2197,6 +2197,25 @@ console.log(
21972197
second2 === "success (dot name)"
21982198
);
21992199

2200+
================================================================================
2201+
TestInjectWithStringExportNameBundle
2202+
---------- /out.js ----------
2203+
// inject.js
2204+
var old = console.log;
2205+
var fn = (...args) => old.apply(console, ["log:"].concat(args));
2206+
2207+
// entry.js
2208+
fn(test);
2209+
fn(test);
2210+
fn(test);
2211+
2212+
================================================================================
2213+
TestInjectWithStringExportNameNoBundle
2214+
---------- /out.js ----------
2215+
var old = console.log;
2216+
var fn = (...args) => old.apply(console, ["log:"].concat(args));
2217+
fn(test);
2218+
22002219
================================================================================
22012220
TestJSXAutomaticImportsCommonJS
22022221
---------- /out.js ----------
@@ -6171,6 +6190,33 @@ export function outer() {
61716190
}
61726191
__name(outer, "outer"), outer();
61736192

6193+
================================================================================
6194+
TestStringExportNamesCommonJS
6195+
---------- /out.js ----------
6196+
var entry_exports = {};
6197+
__export(entry_exports, {
6198+
"all the stuff": () => all_the_stuff,
6199+
"some export": () => import_foo["some import"]
6200+
});
6201+
module.exports = __toCommonJS(entry_exports);
6202+
var import_foo = require("./foo");
6203+
var all_the_stuff = __toESM(require("./foo"));
6204+
6205+
================================================================================
6206+
TestStringExportNamesIIFE
6207+
---------- /out.js ----------
6208+
var global;
6209+
(global ||= {}).name = (() => {
6210+
var entry_exports = {};
6211+
__export(entry_exports, {
6212+
"all the stuff": () => all_the_stuff,
6213+
"some export": () => import_foo["some import"]
6214+
});
6215+
var import_foo = require("./foo");
6216+
var all_the_stuff = __toESM(require("./foo"));
6217+
return __toCommonJS(entry_exports);
6218+
})();
6219+
61746220
================================================================================
61756221
TestSwitchScopeNoBundle
61766222
---------- /out.js ----------

internal/js_parser/js_parser.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5484,8 +5484,6 @@ func (p *parser) parseClauseAlias(kind string) js_lexer.MaybeSubstring {
54845484
if !ok {
54855485
p.log.AddError(&p.tracker, r,
54865486
fmt.Sprintf("This %s alias is invalid because it contains the unpaired Unicode surrogate U+%X", kind, problem))
5487-
} else {
5488-
p.markSyntaxFeature(compat.ArbitraryModuleNamespaceNames, r)
54895487
}
54905488
return js_lexer.MaybeSubstring{String: alias}
54915489
}

internal/js_parser/js_parser_lower.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -88,11 +88,6 @@ func (p *parser) markSyntaxFeature(feature compat.JSFeature, r logger.Range) (di
8888
"Top-level await is not available in %s", where))
8989
return
9090

91-
case compat.ArbitraryModuleNamespaceNames:
92-
p.log.AddError(&p.tracker, r, fmt.Sprintf(
93-
"Using a string as a module namespace identifier name is not supported in %s", where))
94-
return
95-
9691
case compat.Bigint:
9792
// Transforming these will never be supported
9893
p.log.AddError(&p.tracker, r, fmt.Sprintf(

internal/js_parser/js_parser_test.go

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3029,8 +3029,6 @@ func TestImport(t *testing.T) {
30293029
"<stdin>: ERROR: This import alias is invalid because it contains the unpaired Unicode surrogate U+D800\n")
30303030
expectParseError(t, "import {'\\uDC00' as x} from 'foo'",
30313031
"<stdin>: ERROR: This import alias is invalid because it contains the unpaired Unicode surrogate U+DC00\n")
3032-
expectParseErrorTarget(t, 2020, "import {'' as x} from 'foo'",
3033-
"<stdin>: ERROR: Using a string as a module namespace identifier name is not supported in the configured target environment\n")
30343032

30353033
// String import alias with "import * as"
30363034
expectParseError(t, "import * as '' from 'foo'", "<stdin>: ERROR: Expected identifier but found \"''\"\n")
@@ -3083,8 +3081,6 @@ func TestExport(t *testing.T) {
30833081
"<stdin>: ERROR: This export alias is invalid because it contains the unpaired Unicode surrogate U+D800\n")
30843082
expectParseError(t, "let x; export {x as '\\uDC00'}",
30853083
"<stdin>: ERROR: This export alias is invalid because it contains the unpaired Unicode surrogate U+DC00\n")
3086-
expectParseErrorTarget(t, 2020, "let x; export {x as ''}",
3087-
"<stdin>: ERROR: Using a string as a module namespace identifier name is not supported in the configured target environment\n")
30883084

30893085
// String import alias with "export {} from"
30903086
expectPrinted(t, "export {'' as x} from 'foo'", "export { \"\" as x } from \"foo\";\n")
@@ -3095,8 +3091,6 @@ func TestExport(t *testing.T) {
30953091
"<stdin>: ERROR: This export alias is invalid because it contains the unpaired Unicode surrogate U+D800\n")
30963092
expectParseError(t, "export {'\\uDC00' as x} from 'foo'",
30973093
"<stdin>: ERROR: This export alias is invalid because it contains the unpaired Unicode surrogate U+DC00\n")
3098-
expectParseErrorTarget(t, 2020, "export {'' as x} from 'foo'",
3099-
"<stdin>: ERROR: Using a string as a module namespace identifier name is not supported in the configured target environment\n")
31003094

31013095
// String export alias with "export {} from"
31023096
expectPrinted(t, "export {x as ''} from 'foo'", "export { x as \"\" } from \"foo\";\n")
@@ -3107,8 +3101,6 @@ func TestExport(t *testing.T) {
31073101
"<stdin>: ERROR: This export alias is invalid because it contains the unpaired Unicode surrogate U+D800\n")
31083102
expectParseError(t, "export {x as '\\uDC00'} from 'foo'",
31093103
"<stdin>: ERROR: This export alias is invalid because it contains the unpaired Unicode surrogate U+DC00\n")
3110-
expectParseErrorTarget(t, 2020, "export {x as ''} from 'foo'",
3111-
"<stdin>: ERROR: Using a string as a module namespace identifier name is not supported in the configured target environment\n")
31123104

31133105
// String import and export alias with "export {} from"
31143106
expectPrinted(t, "export {'x'} from 'foo'", "export { x } from \"foo\";\n")
@@ -3125,8 +3117,6 @@ func TestExport(t *testing.T) {
31253117
"<stdin>: ERROR: This export alias is invalid because it contains the unpaired Unicode surrogate U+D800\n")
31263118
expectParseError(t, "export * as '\\uDC00' from 'foo'",
31273119
"<stdin>: ERROR: This export alias is invalid because it contains the unpaired Unicode surrogate U+DC00\n")
3128-
expectParseErrorTarget(t, 2020, "export * as '' from 'foo'",
3129-
"<stdin>: ERROR: Using a string as a module namespace identifier name is not supported in the configured target environment\n")
31303120
}
31313121

31323122
func TestExportDuplicates(t *testing.T) {

0 commit comments

Comments
 (0)