diff options
author | 2022-02-07 22:02:54 -0800 | |
---|---|---|
committer | 2022-02-07 22:02:54 -0800 | |
commit | a571c56b4ca9266302483e36460b3f830e23c0d7 (patch) | |
tree | bb3ee5868d16de9a1c96828bd3ae092dacfb571b | |
parent | 7b5bf62ad581292395ebed37d705ddd892a9a812 (diff) | |
download | bun-a571c56b4ca9266302483e36460b3f830e23c0d7.tar.gz bun-a571c56b4ca9266302483e36460b3f830e23c0d7.tar.zst bun-a571c56b4ca9266302483e36460b3f830e23c0d7.zip |
[TS] Implement `import {type foo} from 'bar';` (type inside clause)
-rw-r--r-- | integration/bunjs-only-snippets/transpiler.test.js | 22 | ||||
-rw-r--r-- | src/js_parser/js_parser.zig | 110 |
2 files changed, 112 insertions, 20 deletions
diff --git a/integration/bunjs-only-snippets/transpiler.test.js b/integration/bunjs-only-snippets/transpiler.test.js index 310546e83..167032c9a 100644 --- a/integration/bunjs-only-snippets/transpiler.test.js +++ b/integration/bunjs-only-snippets/transpiler.test.js @@ -16,6 +16,18 @@ describe("Bun.Transpiler", () => { const code = `import { useParams } from "remix"; import type { LoaderFunction, ActionFunction } from "remix"; + import { type xx } from 'mod'; + import { type xx as yy } from 'mod'; + import { type 'xx' as yy } from 'mod'; + import { type as } from 'mod'; + import { type as as } from 'mod'; + import { type as as as } from 'mod'; + import { type xx } from 'mod'; + import { type xx as yy } from 'mod'; + import { type if as yy } from 'mod'; + import { type 'xx' as yy } from 'mod'; + import React, { type ReactNode } from 'react'; + export const loader: LoaderFunction = async ({ params @@ -40,6 +52,8 @@ describe("Bun.Transpiler", () => { it("reports import paths, excluding types", () => { const imports = transpiler.scanImports(code); expect(imports.filter(({ path }) => path === "remix")).toHaveLength(1); + expect(imports.filter(({ path }) => path === "mod")).toHaveLength(0); + expect(imports.filter(({ path }) => path === "react")).toHaveLength(1); }); }); @@ -123,12 +137,20 @@ describe("Bun.Transpiler", () => { }); it("removes types", () => { + expect(code.includes("mod")).toBe(true); + expect(code.includes("xx")).toBe(true); expect(code.includes("ActionFunction")).toBe(true); expect(code.includes("LoaderFunction")).toBe(true); + expect(code.includes("ReactNode")).toBe(true); + expect(code.includes("React")).toBe(true); const out = transpiler.transformSync(code); expect(out.includes("ActionFunction")).toBe(false); expect(out.includes("LoaderFunction")).toBe(false); + expect(out.includes("mod")).toBe(false); + expect(out.includes("xx")).toBe(false); + expect(out.includes("ReactNode")).toBe(false); + expect(out.includes("React")).toBe(true); const { exports } = transpiler.scan(out); expect(exports[0]).toBe("action"); diff --git a/src/js_parser/js_parser.zig b/src/js_parser/js_parser.zig index 58e7f5a2a..6552a69e8 100644 --- a/src/js_parser/js_parser.zig +++ b/src/js_parser/js_parser.zig @@ -6896,28 +6896,98 @@ pub fn NewParser( var original_name = alias; try p.lexer.next(); - if (p.lexer.isContextualKeyword("as")) { - try p.lexer.next(); - original_name = p.lexer.identifier; - name = LocRef{ .loc = alias_loc, .ref = try p.storeNameInRef(original_name) }; - try p.lexer.expect(.t_identifier); - } else if (!isIdentifier) { - // An import where the name is a keyword must have an alias - try p.lexer.expectedString("\"as\""); - } + // "import { type xx } from 'mod'" + // "import { type xx as yy } from 'mod'" + // "import { type 'xx' as yy } from 'mod'" + // "import { type as } from 'mod'" + // "import { type as as } from 'mod'" + // "import { type as as as } from 'mod'" + if (is_typescript_enabled and + strings.eqlComptime(alias, "type") and + p.lexer.token != .t_comma and + p.lexer.token != .t_close_brace) + { + if (p.lexer.isContextualKeyword("as")) { + try p.lexer.next(); + if (p.lexer.isContextualKeyword("as")) { + original_name = p.lexer.identifier; + name = LocRef{ .loc = p.lexer.loc(), .ref = try p.storeNameInRef(original_name) }; + try p.lexer.next(); - // Reject forbidden names - if (isEvalOrArguments(original_name)) { - const r = js_lexer.rangeOfIdentifier(p.source, name.loc); - try p.log.addRangeErrorFmt(p.source, r, p.allocator, "Cannot use \"{s}\" as an identifier here", .{original_name}); - } + if (p.lexer.token == .t_identifier) { + // "import { type as as as } from 'mod'" + // "import { type as as foo } from 'mod'" + try p.lexer.next(); + } else { + // "import { type as as } from 'mod'" + try items.append(.{ + .alias = alias, + .alias_loc = alias_loc, + .name = name, + .original_name = original_name, + }); + } + } else if (p.lexer.token == .t_identifier) { + // "import { type as xxx } from 'mod'" + original_name = p.lexer.identifier; + name = LocRef{ .loc = p.lexer.loc(), .ref = try p.storeNameInRef(original_name) }; + try p.lexer.expect(.t_identifier); - try items.append(js_ast.ClauseItem{ - .alias = alias, - .alias_loc = alias_loc, - .name = name, - .original_name = original_name, - }); + if (isEvalOrArguments(original_name)) { + const r = p.source.rangeOfString(name.loc); + try p.log.addRangeErrorFmt(p.source, r, p.allocator, "Cannot use {s} as an identifier here", .{original_name}); + } + + try items.append(.{ + .alias = alias, + .alias_loc = alias_loc, + .name = name, + .original_name = original_name, + }); + } + } else { + const is_identifier = p.lexer.token == .t_identifier; + + // "import { type xx } from 'mod'" + // "import { type xx as yy } from 'mod'" + // "import { type if as yy } from 'mod'" + // "import { type 'xx' as yy } from 'mod'" + _ = try p.parseClauseAlias("import"); + try p.lexer.next(); + + if (p.lexer.isContextualKeyword("as")) { + try p.lexer.next(); + + try p.lexer.expect(.t_identifier); + } else if (!is_identifier) { + // An import where the name is a keyword must have an alias + try p.lexer.expectedString("\"as\""); + } + } + } else { + if (p.lexer.isContextualKeyword("as")) { + try p.lexer.next(); + original_name = p.lexer.identifier; + name = LocRef{ .loc = alias_loc, .ref = try p.storeNameInRef(original_name) }; + try p.lexer.expect(.t_identifier); + } else if (!isIdentifier) { + // An import where the name is a keyword must have an alias + try p.lexer.expectedString("\"as\""); + } + + // Reject forbidden names + if (isEvalOrArguments(original_name)) { + const r = js_lexer.rangeOfIdentifier(p.source, name.loc); + try p.log.addRangeErrorFmt(p.source, r, p.allocator, "Cannot use \"{s}\" as an identifier here", .{original_name}); + } + + try items.append(js_ast.ClauseItem{ + .alias = alias, + .alias_loc = alias_loc, + .name = name, + .original_name = original_name, + }); + } if (p.lexer.token != .t_comma) { break; |