diff --git a/CHANGELOG.md b/CHANGELOG.md index eab383e3ac4..0c1bc5f435f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,18 @@ document.onkeydown=o=>o.keyCode===65&&console.log("🧀"); ``` +* Parse an upcoming change to TypeScript type syntax ([#3490](https://github.com/evanw/esbuild/issues/3490), [#3491](https://github.com/evanw/esbuild/pull/3491)) + + With this release, you can now use `from` as the name of a default type-only import in TypeScript code: + + ```ts + import type from from 'from' + ``` + + This matches a similar [change in the TypeScript compiler](https://github.com/microsoft/TypeScript/issues/56376) which will start allowing this syntax in an upcoming version of TypeScript. + + This change was contributed by [@magic-akari](https://github.com/magic-akari). + ## 0.19.5 * Fix a regression in 0.19.0 regarding `paths` in `tsconfig.json` ([#3354](https://github.com/evanw/esbuild/issues/3354)) diff --git a/internal/js_parser/js_parser.go b/internal/js_parser/js_parser.go index 2caa0277156..c2a949d7891 100644 --- a/internal/js_parser/js_parser.go +++ b/internal/js_parser/js_parser.go @@ -7491,6 +7491,7 @@ func (p *parser) parseStmt(opts parseStmtOpts) js_ast.Stmt { p.lexer.Expected(js_lexer.TIdentifier) } + syntaxBeforePath: switch p.lexer.Token { case js_lexer.TOpenParen, js_lexer.TDot: // "import('path')" @@ -7553,22 +7554,23 @@ func (p *parser) parseStmt(opts parseStmtOpts) js_ast.Stmt { if defaultName.String == "type" { switch p.lexer.Token { case js_lexer.TIdentifier: - if p.lexer.Identifier.String != "from" { - defaultName = p.lexer.Identifier - stmt.DefaultName.Loc = p.lexer.Loc() - p.lexer.Next() - if p.lexer.Token == js_lexer.TEquals { - // "import type foo = require('bar');" - // "import type foo = bar.baz;" - opts.isTypeScriptDeclare = true - return p.parseTypeScriptImportEqualsStmt(loc, opts, stmt.DefaultName.Loc, defaultName.String) - } else { - // "import type foo from 'bar';" - p.lexer.ExpectContextualKeyword("from") - p.parsePath() - p.lexer.ExpectOrInsertSemicolon() - return js_ast.Stmt{Loc: loc, Data: js_ast.STypeScriptShared} - } + nameSubstring := p.lexer.Identifier + nameLoc := p.lexer.Loc() + p.lexer.Next() + if p.lexer.Token == js_lexer.TEquals { + // "import type foo = require('bar');" + // "import type foo = bar.baz;" + opts.isTypeScriptDeclare = true + return p.parseTypeScriptImportEqualsStmt(loc, opts, nameLoc, nameSubstring.String) + } else if p.lexer.Token == js_lexer.TStringLiteral && nameSubstring.String == "from" { + // "import type from 'bar';" + break syntaxBeforePath + } else { + // "import type foo from 'bar';" + p.lexer.ExpectContextualKeyword("from") + p.parsePath() + p.lexer.ExpectOrInsertSemicolon() + return js_ast.Stmt{Loc: loc, Data: js_ast.STypeScriptShared} } case js_lexer.TAsterisk: diff --git a/internal/js_parser/ts_parser_test.go b/internal/js_parser/ts_parser_test.go index 7df46476aca..de2b6f875d5 100644 --- a/internal/js_parser/ts_parser_test.go +++ b/internal/js_parser/ts_parser_test.go @@ -2540,12 +2540,14 @@ func TestTSImportEqualsInNamespace(t *testing.T) { func TestTSTypeOnlyImport(t *testing.T) { expectPrintedTS(t, "import type foo from 'bar'; x", "x;\n") expectPrintedTS(t, "import type foo from 'bar'\nx", "x;\n") + expectPrintedTS(t, "import type from from 'bar'; x", "x;\n") expectPrintedTS(t, "import type * as foo from 'bar'; x", "x;\n") expectPrintedTS(t, "import type * as foo from 'bar'\nx", "x;\n") expectPrintedTS(t, "import type {foo, bar as baz} from 'bar'; x", "x;\n") expectPrintedTS(t, "import type {'foo' as bar} from 'bar'\nx", "x;\n") expectPrintedTS(t, "import type foo = require('bar'); x", "x;\n") expectPrintedTS(t, "import type foo = bar.baz; x", "x;\n") + expectPrintedTS(t, "import type from = require('bar'); x", "x;\n") expectPrintedTS(t, "import type = bar; type", "const type = bar;\ntype;\n") expectPrintedTS(t, "import type = foo.bar; type", "const type = foo.bar;\ntype;\n") @@ -2578,6 +2580,8 @@ func TestTSTypeOnlyImport(t *testing.T) { expectParseErrorTS(t, "import type foo, * as foo from 'bar'", ": ERROR: Expected \"from\" but found \",\"\n") expectParseErrorTS(t, "import type foo, {foo} from 'bar'", ": ERROR: Expected \"from\" but found \",\"\n") + expectParseErrorTS(t, "import type from, * as foo from 'bar'", ": ERROR: Expected \"from\" but found \",\"\n") + expectParseErrorTS(t, "import type from, {foo} from 'bar'", ": ERROR: Expected \"from\" but found \",\"\n") expectParseErrorTS(t, "import type * as foo = require('bar')", ": ERROR: Expected \"from\" but found \"=\"\n") expectParseErrorTS(t, "import type {foo} = require('bar')", ": ERROR: Expected \"from\" but found \"=\"\n")