[TS] Fix bug with import Foo = require("bar")

Closes https://github.com/oven-sh/bun/issues/1045
This commit is contained in:
Jarred Sumner
2022-08-10 15:08:54 -07:00
parent 67cdf1c9b3
commit 2644b9d792
2 changed files with 109 additions and 74 deletions

View File

@@ -739,10 +739,10 @@ pub const ImportScanner = struct {
// Skip to the underlying reference
var value = decl.value;
if (decl.value) |val| {
if (decl.value != null) {
while (true) {
if (@as(Expr.Tag, val.data) == .e_dot) {
value = val.data.e_dot.target;
if (@as(Expr.Tag, value.?.data) == .e_dot) {
value = value.?.data.e_dot.target;
} else {
break;
}
@@ -8097,7 +8097,8 @@ fn NewParser_(
const kind = S.Local.Kind.k_const;
const name = p.lexer.identifier;
var value = p.e(E.Identifier{ .ref = p.storeNameInRef(name) catch unreachable }, p.lexer.loc());
const target = p.e(E.Identifier{ .ref = p.storeNameInRef(name) catch unreachable }, p.lexer.loc());
var value = target;
try p.lexer.expect(.t_identifier);
if (strings.eqlComptime(name, "require") and p.lexer.token == .t_open_paren) {
@@ -8106,17 +8107,17 @@ fn NewParser_(
const path = p.e(p.lexer.toEString(), p.lexer.loc());
try p.lexer.expect(.t_string_literal);
try p.lexer.expect(.t_close_paren);
const args = try ExprNodeList.one(p.allocator, path);
value.data = .{ .e_call = Expr.Data.Store.All.append(E.Call, E.Call{ .target = value, .close_paren_loc = p.lexer.loc(), .args = args }) };
if (!opts.is_typescript_declare) {
const args = try ExprNodeList.one(p.allocator, path);
value = p.e(E.Call{ .target = target, .close_paren_loc = p.lexer.loc(), .args = args }, loc);
}
} else {
// "import Foo = Bar"
// "import Foo = Bar.Baz"
while (p.lexer.token == .t_dot) {
var prev_value = value;
while (p.lexer.token == .t_dot) : (prev_value = value) {
try p.lexer.next();
value.data = .{ .e_dot = Expr.Data.Store.All.append(
E.Dot,
E.Dot{ .target = value, .name = p.lexer.identifier, .name_loc = p.lexer.loc() },
) };
value = p.e(E.Dot{ .target = prev_value, .name = p.lexer.identifier, .name_loc = p.lexer.loc() }, loc);
try p.lexer.expect(.t_identifier);
}
}
@@ -15717,8 +15718,9 @@ fn NewParser_(
pub fn ignoreUsage(p: *P, ref: Ref) void {
if (!p.is_control_flow_dead) {
assert(@as(usize, ref.innerIndex()) < p.symbols.items.len);
p.symbols.items[ref.innerIndex()].use_count_estimate -|= 1;
var use = p.symbol_uses.get(ref) orelse p.panic("Expected symbol_uses to exist {s}\n{s}", .{ ref, p.symbol_uses });
var use = p.symbol_uses.get(ref) orelse return;
use.count_estimate -|= 1;
if (use.count_estimate == 0) {
_ = p.symbol_uses.swapRemove(ref);

View File

@@ -1,6 +1,101 @@
import { expect, it, describe } from "bun:test";
describe("Bun.Transpiler", () => {
const transpiler = new Bun.Transpiler({
loader: "tsx",
define: {
"process.env.NODE_ENV": JSON.stringify("development"),
user_undefined: "undefined",
},
macro: {
react: {
bacon: `${import.meta.dir}/macro-check.js`,
},
},
platform: "browser",
});
const ts = {
parsed: (code, trim = true, autoExport = false) => {
if (autoExport) {
code = "export default (" + code + ")";
}
var out = transpiler.transformSync(code, "ts");
if (autoExport && out.startsWith("export default ")) {
out = out.substring("export default ".length);
}
if (trim) {
out = out.trim();
if (out.endsWith(";")) {
out = out.substring(0, out.length - 1);
}
return out.trim();
}
return out;
},
expectPrinted: (code, out) => {
expect(ts.parsed(code, true, true)).toBe(out);
},
expectPrinted_: (code, out) => {
expect(ts.parsed(code, !out.endsWith(";\n"), false)).toBe(out);
},
expectParseError: (code, message) => {
try {
ts.parsed(code, false, false);
} catch (er) {
var err = er;
if (er instanceof AggregateError) {
err = err.errors[0];
}
expect(er.message).toBe(message);
return;
}
throw new Error("Expected parse error for code\n\t" + code);
},
};
describe("TypeScript", () => {
it("import Foo = Baz.Bar", () => {
ts.expectPrinted_(
"import Foo = Baz.Bar;\nexport default Foo;",
"const Foo = Baz.Bar;\nexport default Foo"
);
});
it("import Foo = require('bar')", () => {
ts.expectPrinted_(
"import React = require('react')",
'const React = require("react")'
);
});
it("import type Foo = require('bar')", () => {
ts.expectPrinted_("import type Foo = require('bar')", "");
});
it("unused import = gets removed", () => {
ts.expectPrinted_("import Foo = Baz.Bar;", "");
});
it("export import Foo = Baz.Bar", () => {
ts.expectPrinted_(
"export import Foo = Baz.Bar;",
"export const Foo = Baz.Bar"
);
});
});
describe("exports.replace", () => {
const transpiler = new Bun.Transpiler({
exports: {
@@ -111,19 +206,6 @@ describe("Bun.Transpiler", () => {
});
});
const transpiler = new Bun.Transpiler({
loader: "tsx",
define: {
"process.env.NODE_ENV": JSON.stringify("development"),
user_undefined: "undefined",
},
macro: {
react: {
bacon: `${import.meta.dir}/macro-check.js`,
},
},
platform: "browser",
});
const bunTranspiler = new Bun.Transpiler({
loader: "tsx",
define: {
@@ -496,55 +578,6 @@ export var ComponentThatHasSpreadCausesDeopt = jsx(Hello, {
throw new Error("Expected parse error for code\n\t" + code);
};
const ts = {
parsed: (code, trim = true, autoExport = false) => {
if (autoExport) {
code = "export default (" + code + ")";
}
var out = transpiler.transformSync(code, "ts");
if (autoExport && out.startsWith("export default ")) {
out = out.substring("export default ".length);
}
if (trim) {
out = out.trim();
if (out.endsWith(";")) {
out = out.substring(0, out.length - 1);
}
return out.trim();
}
return out;
},
expectPrinted: (code, out) => {
expect(ts.parsed(code, true, true)).toBe(out);
},
expectPrinted_: (code, out) => {
expect(ts.parsed(code, !out.endsWith(";\n"), false)).toBe(out);
},
expectParseError: (code, message) => {
try {
ts.parsed(code, false, false);
} catch (er) {
var err = er;
if (er instanceof AggregateError) {
err = err.errors[0];
}
expect(er.message).toBe(message);
return;
}
throw new Error("Expected parse error for code\n\t" + code);
},
};
describe("parser", () => {
it("arrays", () => {