Skip to content

Commit

Permalink
fix #208: pass "import.meta" through for esm
Browse files Browse the repository at this point in the history
  • Loading branch information
evanw committed Jul 2, 2020
1 parent 7b70448 commit ac97be7
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 3 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Changelog

## Unreleased

* Pass through `import.meta` syntax ([#208](https://github.com/evanw/esbuild/issues/208))

The `import.meta` syntax is a way for code in an ES6 module to access metadata about itself. For example, `import.meta.url` in the browser is the URL of the current module.

It's a new feature that doesn't work in older browsers, so esbuild converts it to a module-local variable to avoid generating code with a syntax error. However, this is only necessary when targeting older browsers or if the output format doesn't support `import.meta`.

The `import.meta` syntax is now passed through unmodified when the target is `es2020` or newer and the output format is `esm`. This lets you use features such as `import.meta.url` in those situations.

## 0.5.16

* Experimental code splitting with `--splitting` ([#16](https://github.com/evanw/esbuild/issues/16))
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ These syntax features are conditionally transformed for older browsers depending
| [Optional catch binding](https://github.com/tc39/proposal-optional-catch-binding) | `es2019` | `try {} catch {}` |
| [Optional chaining](https://github.com/tc39/proposal-optional-chaining) | `es2020` | `a?.b` |
| [Nullish coalescing](https://github.com/tc39/proposal-nullish-coalescing) | `es2020` | `a ?? b` |
| [`import.meta`](https://github.com/tc39/proposal-import-meta) | `es2020` | `import.meta` |
| [Class instance fields](https://github.com/tc39/proposal-class-fields) | `esnext` | `class { x }` |
| [Static class fields](https://github.com/tc39/proposal-static-class-features) | `esnext` | `class { static x }` |
| [Private instance methods](https://github.com/tc39/proposal-private-methods) | `esnext` | `class { #x() {} }` |
Expand Down
24 changes: 23 additions & 1 deletion internal/bundler/bundler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4986,7 +4986,7 @@ export {default as bar} from "./bar";
})
}

func TestImportMeta(t *testing.T) {
func TestImportMetaCommonJS(t *testing.T) {
expectBundled(t, bundled{
files: map[string]string{
"/entry.js": `
Expand All @@ -4996,6 +4996,7 @@ func TestImportMeta(t *testing.T) {
entryPaths: []string{"/entry.js"},
options: config.Options{
IsBundling: true,
OutputFormat: config.FormatCommonJS,
AbsOutputFile: "/out.js",
},
expected: map[string]string{
Expand All @@ -5007,6 +5008,27 @@ console.log(import_meta.url, import_meta.path);
})
}

func TestImportMetaES6(t *testing.T) {
expectBundled(t, bundled{
files: map[string]string{
"/entry.js": `
console.log(import.meta.url, import.meta.path)
`,
},
entryPaths: []string{"/entry.js"},
options: config.Options{
IsBundling: true,
OutputFormat: config.FormatESModule,
AbsOutputFile: "/out.js",
},
expected: map[string]string{
"/out.js": `// /entry.js
console.log(import.meta.url, import.meta.path);
`,
},
})
}

func TestImportMetaNoBundle(t *testing.T) {
expectBundled(t, bundled{
files: map[string]string{
Expand Down
11 changes: 9 additions & 2 deletions internal/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -2816,8 +2816,14 @@ func (p *parser) parseImportExpr(loc ast.Loc) ast.Expr {
if p.lexer.Token == lexer.TDot {
p.lexer.Next()
if p.lexer.IsContextualKeyword("meta") {
r := p.lexer.Range()
p.lexer.Next()
p.hasImportMeta = true
if p.Target < importMetaTarget {
r = ast.Range{Loc: loc, Len: r.End() - loc.Start}
p.log.AddRangeWarning(&p.source, r, fmt.Sprintf(
"\"import.meta\" is from ES2020 and will be empty when targeting %s", targetTable[p.Target]))
}
return ast.Expr{Loc: loc, Data: &ast.EImportMeta{}}
} else {
p.lexer.ExpectedString("\"meta\"")
Expand Down Expand Up @@ -8852,7 +8858,7 @@ func (p *parser) bindingCanBeRemovedIfUnused(binding ast.Binding) bool {
func (p *parser) exprCanBeRemovedIfUnused(expr ast.Expr) bool {
switch e := expr.Data.(type) {
case *ast.ENull, *ast.EUndefined, *ast.EBoolean, *ast.ENumber, *ast.EBigInt,
*ast.EString, *ast.EThis, *ast.ERegExp, *ast.EFunction, *ast.EArrow:
*ast.EString, *ast.EThis, *ast.ERegExp, *ast.EFunction, *ast.EArrow, *ast.EImportMeta:
return true

case *ast.EDot:
Expand Down Expand Up @@ -9169,7 +9175,8 @@ func (p *parser) prepareForVisitPass(options *config.Options) {
p.moduleRef = p.newSymbol(ast.SymbolHoisted, "module")
}

if options.IsBundling && p.hasImportMeta {
// Convert "import.meta" to a variable if it's not supported in the output format
if p.hasImportMeta && (p.Target < importMetaTarget || (options.IsBundling && !p.OutputFormat.KeepES6ImportExportSyntax())) {
p.importMetaRef = p.newSymbol(ast.SymbolOther, "import_meta")
p.moduleScope.Generated = append(p.moduleScope.Generated, p.importMetaRef)
} else {
Expand Down
1 change: 1 addition & 0 deletions internal/parser/parser_lower.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const asyncAwaitTarget = config.ES2017
const objectPropertyBindingTarget = config.ES2018
const nullishCoalescingTarget = config.ES2020
const privateNameTarget = config.ESNext
const importMetaTarget = config.ES2020

type futureSyntax uint8

Expand Down

0 comments on commit ac97be7

Please sign in to comment.