Skip to content

Commit 947f99f

Browse files
committed
fix #4010, fix #4012: import.meta regression
1 parent de9598f commit 947f99f

File tree

4 files changed

+70
-13
lines changed

4 files changed

+70
-13
lines changed

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## Unreleased
4+
5+
* Fix regression with `--define` and `import.meta` ([#4010](https://github.com/evanw/esbuild/issues/4010), [#4012](https://github.com/evanw/esbuild/issues/4012))
6+
7+
The previous change in version 0.24.1 to use a more expression-like parser for `define` values to allow quoted property names introduced a regression that removed the ability to use `--define:import.meta=...`. Even though `import` is normally a keyword that can't be used as an identifier, ES modules special-case the `import.meta` expression to behave like an identifier anyway. This change fixes the regression.
8+
39
## 0.24.1
410

511
* Allow `es2024` as a target in `tsconfig.json` ([#4004](https://github.com/evanw/esbuild/issues/4004))

internal/js_parser/global_name_parser.go

+15-2
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,22 @@ func ParseGlobalName(log logger.Log, source logger.Source) (result []string, ok
1919

2020
lexer := js_lexer.NewLexerGlobalName(log, source)
2121

22-
// Start off with an identifier
22+
// Start off with an identifier or a keyword that results in an object
2323
result = append(result, lexer.Identifier.String)
24-
lexer.Expect(js_lexer.TIdentifier)
24+
switch lexer.Token {
25+
case js_lexer.TThis:
26+
lexer.Next()
27+
28+
case js_lexer.TImport:
29+
// Handle "import.meta"
30+
lexer.Next()
31+
lexer.Expect(js_lexer.TDot)
32+
result = append(result, lexer.Identifier.String)
33+
lexer.ExpectContextualKeyword("meta")
34+
35+
default:
36+
lexer.Expect(js_lexer.TIdentifier)
37+
}
2538

2639
// Follow with dot or index expressions
2740
for lexer.Token != js_lexer.TEndOfFile {

internal/linker/linker.go

+23-7
Original file line numberDiff line numberDiff line change
@@ -5973,7 +5973,7 @@ func (c *linkerContext) generateChunkJS(chunkIndex int, chunkWaitGroup *sync.Wai
59735973
func (c *linkerContext) generateGlobalNamePrefix() string {
59745974
var text string
59755975
globalName := c.options.GlobalName
5976-
prefix := globalName[0]
5976+
prefix, globalName := globalName[0], globalName[1:]
59775977
space := " "
59785978
join := ";\n"
59795979

@@ -5982,17 +5982,26 @@ func (c *linkerContext) generateGlobalNamePrefix() string {
59825982
join = ";"
59835983
}
59845984

5985+
// Assume the "this" and "import.meta" objects always exist
5986+
isExistingObject := prefix == "this"
5987+
if prefix == "import" && len(globalName) > 0 && globalName[0] == "meta" {
5988+
prefix, globalName = "import.meta", globalName[1:]
5989+
isExistingObject = true
5990+
}
5991+
59855992
// Use "||=" to make the code more compact when it's supported
5986-
if len(globalName) > 1 && !c.options.UnsupportedJSFeatures.Has(compat.LogicalAssignment) {
5987-
if js_printer.CanEscapeIdentifier(prefix, c.options.UnsupportedJSFeatures, c.options.ASCIIOnly) {
5993+
if len(globalName) > 0 && !c.options.UnsupportedJSFeatures.Has(compat.LogicalAssignment) {
5994+
if isExistingObject {
5995+
// Keep the prefix as it is
5996+
} else if js_printer.CanEscapeIdentifier(prefix, c.options.UnsupportedJSFeatures, c.options.ASCIIOnly) {
59885997
if c.options.ASCIIOnly {
59895998
prefix = string(js_printer.QuoteIdentifier(nil, prefix, c.options.UnsupportedJSFeatures))
59905999
}
59916000
text = fmt.Sprintf("var %s%s", prefix, join)
59926001
} else {
59936002
prefix = fmt.Sprintf("this[%s]", helpers.QuoteForJSON(prefix, c.options.ASCIIOnly))
59946003
}
5995-
for _, name := range globalName[1:] {
6004+
for _, name := range globalName {
59966005
var dotOrIndex string
59976006
if js_printer.CanEscapeIdentifier(name, c.options.UnsupportedJSFeatures, c.options.ASCIIOnly) {
59986007
if c.options.ASCIIOnly {
@@ -6002,12 +6011,19 @@ func (c *linkerContext) generateGlobalNamePrefix() string {
60026011
} else {
60036012
dotOrIndex = fmt.Sprintf("[%s]", helpers.QuoteForJSON(name, c.options.ASCIIOnly))
60046013
}
6005-
prefix = fmt.Sprintf("(%s%s||=%s{})%s", prefix, space, space, dotOrIndex)
6014+
if isExistingObject {
6015+
prefix = fmt.Sprintf("%s%s", prefix, dotOrIndex)
6016+
isExistingObject = false
6017+
} else {
6018+
prefix = fmt.Sprintf("(%s%s||=%s{})%s", prefix, space, space, dotOrIndex)
6019+
}
60066020
}
60076021
return fmt.Sprintf("%s%s%s=%s", text, prefix, space, space)
60086022
}
60096023

6010-
if js_printer.CanEscapeIdentifier(prefix, c.options.UnsupportedJSFeatures, c.options.ASCIIOnly) {
6024+
if isExistingObject {
6025+
text = fmt.Sprintf("%s%s=%s", prefix, space, space)
6026+
} else if js_printer.CanEscapeIdentifier(prefix, c.options.UnsupportedJSFeatures, c.options.ASCIIOnly) {
60116027
if c.options.ASCIIOnly {
60126028
prefix = string(js_printer.QuoteIdentifier(nil, prefix, c.options.UnsupportedJSFeatures))
60136029
}
@@ -6017,7 +6033,7 @@ func (c *linkerContext) generateGlobalNamePrefix() string {
60176033
text = fmt.Sprintf("%s%s=%s", prefix, space, space)
60186034
}
60196035

6020-
for _, name := range globalName[1:] {
6036+
for _, name := range globalName {
60216037
oldPrefix := prefix
60226038
if js_printer.CanEscapeIdentifier(name, c.options.UnsupportedJSFeatures, c.options.ASCIIOnly) {
60236039
if c.options.ASCIIOnly {

scripts/js-api-tests.js

+26-4
Original file line numberDiff line numberDiff line change
@@ -6009,6 +6009,22 @@ class Foo {
60096009
π["π 𐀀"]["𐀀"]["𐀀 π"] = `)
60106010
},
60116011

6012+
async iifeGlobalNameThis({ esbuild }) {
6013+
const { code } = await esbuild.transform(`export default 123`, { format: 'iife', globalName: 'this.foo.bar' })
6014+
const globals = {}
6015+
vm.createContext(globals)
6016+
vm.runInContext(code, globals)
6017+
assert.strictEqual(globals.foo.bar.default, 123)
6018+
assert.strictEqual(code.slice(0, code.indexOf('(() => {\n')), `(this.foo ||= {}).bar = `)
6019+
},
6020+
6021+
async iifeGlobalNameImportMeta({ esbuild }) {
6022+
const { code } = await esbuild.transform(`export default 123`, { format: 'iife', globalName: 'import.meta.foo.bar' })
6023+
const { default: import_meta } = await import('data:text/javascript,' + code + '\nexport default import.meta')
6024+
assert.strictEqual(import_meta.foo.bar.default, 123)
6025+
assert.strictEqual(code.slice(0, code.indexOf('(() => {\n')), `(import.meta.foo ||= {}).bar = `)
6026+
},
6027+
60126028
async jsx({ esbuild }) {
60136029
const { code } = await esbuild.transform(`console.log(<div/>)`, { loader: 'jsx' })
60146030
assert.strictEqual(code, `console.log(/* @__PURE__ */ React.createElement("div", null));\n`)
@@ -6123,13 +6139,19 @@ class Foo {
61236139
},
61246140

61256141
async defineThis({ esbuild }) {
6126-
const { code } = await esbuild.transform(`console.log(a, b); export {}`, { define: { a: 'this', b: 'this.foo' }, format: 'esm' })
6127-
assert.strictEqual(code, `console.log(void 0, (void 0).foo);\n`)
6142+
const { code: code1 } = await esbuild.transform(`console.log(a, b); export {}`, { define: { a: 'this', b: 'this.foo' }, format: 'esm' })
6143+
assert.strictEqual(code1, `console.log(void 0, (void 0).foo);\n`)
6144+
6145+
const { code: code2 } = await esbuild.transform(`console.log(this, this.x); export {}`, { define: { this: 'a', 'this.x': 'b' }, format: 'esm' })
6146+
assert.strictEqual(code2, `console.log(a, b);\n`)
61286147
},
61296148

61306149
async defineImportMetaESM({ esbuild }) {
6131-
const { code } = await esbuild.transform(`console.log(a, b); export {}`, { define: { a: 'import.meta', b: 'import.meta.foo' }, format: 'esm' })
6132-
assert.strictEqual(code, `console.log(import.meta, import.meta.foo);\n`)
6150+
const { code: code1 } = await esbuild.transform(`console.log(a, b); export {}`, { define: { a: 'import.meta', b: 'import.meta.foo' }, format: 'esm' })
6151+
assert.strictEqual(code1, `console.log(import.meta, import.meta.foo);\n`)
6152+
6153+
const { code: code2 } = await esbuild.transform(`console.log(import.meta, import.meta.x); export {}`, { define: { 'import.meta': 'a', 'import.meta.x': 'b' }, format: 'esm' })
6154+
assert.strictEqual(code2, `console.log(a, b);\n`)
61336155
},
61346156

61356157
async defineImportMetaIIFE({ esbuild }) {

0 commit comments

Comments
 (0)