aboutsummaryrefslogtreecommitdiff
path: root/src/js_parser.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/js_parser.zig')
-rw-r--r--src/js_parser.zig1247
1 files changed, 985 insertions, 262 deletions
diff --git a/src/js_parser.zig b/src/js_parser.zig
index a61657128..fce928a6e 100644
--- a/src/js_parser.zig
+++ b/src/js_parser.zig
@@ -70,7 +70,7 @@ const RefHashCtx = @import("./ast/base.zig").RefHashCtx;
pub const StringHashMap = bun.StringHashMap;
pub const AutoHashMap = std.AutoHashMap;
-const StringHashMapUnamanged = bun.StringHashMapUnmanaged;
+const StringHashMapUnmanaged = bun.StringHashMapUnmanaged;
const ObjectPool = @import("./pool.zig").ObjectPool;
const NodeFallbackModules = @import("./node_fallbacks.zig");
@@ -439,6 +439,155 @@ pub const TypeScript = struct {
};
}
+ pub const Metadata = union(enum) {
+ m_none: void,
+
+ m_never: void,
+ m_unknown: void,
+ m_any: void,
+ m_void: void,
+ m_null: void,
+ m_undefined: void,
+ m_function: void,
+ m_array: void,
+ m_boolean: void,
+ m_string: void,
+ m_object: void,
+ m_number: void,
+ m_bigint: void,
+ m_symbol: void,
+ m_promise: void,
+ m_identifier: Ref,
+ m_dot: List(Ref),
+
+ pub const default: @This() = .m_none;
+
+ // the logic in finishUnion, mergeUnion, finishIntersection and mergeIntersection is
+ // translated from:
+ // https://github.com/microsoft/TypeScript/blob/e0a324b0503be479f2b33fd2e17c6e86c94d1297/src/compiler/transformers/typeSerializer.ts#L402
+
+ /// Return the final union type if possible, or return null to continue merging.
+ ///
+ /// If the current type is m_never, m_null, or m_undefined assign the current type
+ /// to m_none and return null to ensure it's always replaced by the next type.
+ pub fn finishUnion(current: *@This(), p: anytype) ?@This() {
+ return switch (current.*) {
+ .m_identifier => |ref| {
+ if (strings.eqlComptime(p.loadNameFromRef(ref), "Object")) {
+ return .m_object;
+ }
+ return null;
+ },
+
+ .m_unknown,
+ .m_any,
+ .m_object,
+ => .m_object,
+
+ .m_never,
+ .m_null,
+ .m_undefined,
+ => {
+ current.* = .m_none;
+ return null;
+ },
+
+ else => null,
+ };
+ }
+
+ pub fn mergeUnion(result: *@This(), left: @This()) void {
+ if (left != .m_none) {
+ if (std.meta.activeTag(result.*) != std.meta.activeTag(left)) {
+ result.* = switch (result.*) {
+ .m_never,
+ .m_undefined,
+ .m_null,
+ => left,
+
+ else => .m_object,
+ };
+ } else {
+ switch (result.*) {
+ .m_identifier => |ref| {
+ if (!ref.eql(left.m_identifier)) {
+ result.* = .m_object;
+ }
+ },
+ else => {},
+ }
+ }
+ } else {
+ // always take the next value if left is m_none
+ }
+ }
+
+ /// Return the final intersection type if possible, or return null to continue merging.
+ ///
+ /// If the current type is m_unknown, m_null, or m_undefined assign the current type
+ /// to m_none and return null to ensure it's always replaced by the next type.
+ pub fn finishIntersection(current: *@This(), p: anytype) ?@This() {
+ return switch (current.*) {
+ .m_identifier => |ref| {
+ if (strings.eqlComptime(p.loadNameFromRef(ref), "Object")) {
+ return .m_object;
+ }
+ return null;
+ },
+
+ // ensure m_never is the final type
+ .m_never => .m_never,
+
+ .m_any,
+ .m_object,
+ => .m_object,
+
+ .m_unknown,
+ .m_null,
+ .m_undefined,
+ => {
+ current.* = .m_none;
+ return null;
+ },
+
+ else => null,
+ };
+ }
+
+ pub fn mergeIntersection(result: *@This(), left: @This()) void {
+ if (left != .m_none) {
+ if (std.meta.activeTag(result.*) != std.meta.activeTag(left)) {
+ result.* = switch (result.*) {
+ .m_unknown,
+ .m_undefined,
+ .m_null,
+ => left,
+
+ // ensure m_never is the final type
+ .m_never => .m_never,
+
+ else => .m_object,
+ };
+ } else {
+ switch (result.*) {
+ .m_identifier => |ref| {
+ if (!ref.eql(left.m_identifier)) {
+ result.* = .m_object;
+ }
+ },
+ else => {},
+ }
+ }
+ } else {
+ // make sure intersection of only m_unknown serializes to "undefined"
+ // instead of "Object"
+ if (result.* == .m_unknown) {
+ result.* = .m_undefined;
+ }
+ }
+ }
+ };
+
pub fn isTSArrowFnJSX(p: anytype) !bool {
var oldLexer = std.mem.toBytes(p.lexer);
@@ -664,19 +813,19 @@ pub const TypeScript = struct {
.{ "abstract", .abstract },
.{ "asserts", .asserts },
- .{ "keyof", .prefix },
- .{ "readonly", .prefix },
+ .{ "keyof", .prefix_keyof },
+ .{ "readonly", .prefix_readonly },
- .{ "any", .primitive },
- .{ "never", .primitive },
- .{ "unknown", .primitive },
- .{ "undefined", .primitive },
- .{ "object", .primitive },
- .{ "number", .primitive },
- .{ "string", .primitive },
- .{ "boolean", .primitive },
- .{ "bigint", .primitive },
- .{ "symbol", .primitive },
+ .{ "any", .primitive_any },
+ .{ "never", .primitive_never },
+ .{ "unknown", .primitive_unknown },
+ .{ "undefined", .primitive_undefined },
+ .{ "object", .primitive_object },
+ .{ "number", .primitive_number },
+ .{ "string", .primitive_string },
+ .{ "boolean", .primitive_boolean },
+ .{ "bigint", .primitive_bigint },
+ .{ "symbol", .primitive_symbol },
.{ "infer", .infer },
});
@@ -685,17 +834,30 @@ pub const TypeScript = struct {
unique,
abstract,
asserts,
- prefix,
- primitive,
+ prefix_keyof,
+ prefix_readonly,
+ primitive_any,
+ primitive_never,
+ primitive_unknown,
+ primitive_undefined,
+ primitive_object,
+ primitive_number,
+ primitive_string,
+ primitive_boolean,
+ primitive_bigint,
+ primitive_symbol,
infer,
};
};
- pub const SkipTypeOptions = struct {
- is_return_type: bool = false,
- is_index_signature: bool = false,
- allow_tuple_labels: bool = false,
- disallow_conditional_types: bool = false,
+ pub const SkipTypeOptions = enum {
+ is_return_type,
+ is_index_signature,
+ allow_tuple_labels,
+ disallow_conditional_types,
+
+ pub const Bitset = std.enums.EnumSet(@This());
+ pub const empty = Bitset.initEmpty();
};
};
@@ -1398,8 +1560,9 @@ const StaticSymbolName = struct {
pub const __HMRClient = NewStaticSymbol("Bun");
pub const __FastRefreshModule = NewStaticSymbol("FastHMR");
pub const __FastRefreshRuntime = NewStaticSymbol("FastRefresh");
- pub const __decorateClass = NewStaticSymbol("__decorateClass");
- pub const __decorateParam = NewStaticSymbol("__decorateParam");
+ pub const __legacyDecorateClassTS = NewStaticSymbol("__legacyDecorateClassTS");
+ pub const __legacyDecorateParamTS = NewStaticSymbol("__legacyDecorateParamTS");
+ pub const __legacyMetadataTS = NewStaticSymbol("__legacyMetadataTS");
pub const @"$$typeof" = NewStaticSymbol("$$typeof");
pub const @"$$m" = NewStaticSymbol("$$m");
@@ -1437,6 +1600,7 @@ pub const SideEffects = enum(u1) {
}
pub fn simplifyBoolean(p: anytype, expr: Expr) Expr {
+ if (!p.options.features.dead_code_elimination) return expr;
switch (expr.data) {
.e_unary => |e| {
if (e.op == .un_not) {
@@ -1451,14 +1615,14 @@ pub const SideEffects = enum(u1) {
.e_binary => |e| {
switch (e.op) {
.bin_logical_and => {
- const effects = SideEffects.toBoolean(e.right.data);
+ const effects = SideEffects.toBoolean(p, e.right.data);
if (effects.ok and effects.value and effects.side_effects == .no_side_effects) {
// "if (anything && truthyNoSideEffects)" => "if (anything)"
return e.left;
}
},
.bin_logical_or => {
- const effects = SideEffects.toBoolean(e.right.data);
+ const effects = SideEffects.toBoolean(p, e.right.data);
if (effects.ok and !effects.value and effects.side_effects == .no_side_effects) {
// "if (anything || falsyNoSideEffects)" => "if (anything)"
return e.left;
@@ -1484,6 +1648,7 @@ pub const SideEffects = enum(u1) {
}
pub fn simpifyUnusedExpr(p: anytype, expr: Expr) ?Expr {
+ if (!p.options.features.dead_code_elimination) return expr;
switch (expr.data) {
.e_null, .e_undefined, .e_missing, .e_boolean, .e_number, .e_big_int, .e_string, .e_this, .e_reg_exp, .e_function, .e_arrow, .e_import_meta => {
return null;
@@ -1746,7 +1911,8 @@ pub const SideEffects = enum(u1) {
//
// We can't trim the entire branch as dead or calling foo() will incorrectly
// assign to a global variable instead.
- pub fn shouldKeepStmtInDeadControlFlow(stmt: Stmt, allocator: Allocator) bool {
+ pub fn shouldKeepStmtInDeadControlFlow(p: anytype, stmt: Stmt, allocator: Allocator) bool {
+ if (!p.options.features.dead_code_elimination) return true;
switch (stmt.data) {
// Omit these statements entirely
.s_empty, .s_expr, .s_throw, .s_return, .s_break, .s_continue, .s_class, .s_debugger => return false,
@@ -1777,7 +1943,7 @@ pub const SideEffects = enum(u1) {
.s_block => |block| {
for (block.stmts) |child| {
- if (shouldKeepStmtInDeadControlFlow(child, allocator)) {
+ if (shouldKeepStmtInDeadControlFlow(p, child, allocator)) {
return true;
}
}
@@ -1786,43 +1952,43 @@ pub const SideEffects = enum(u1) {
},
.s_if => |_if_| {
- if (shouldKeepStmtInDeadControlFlow(_if_.yes, allocator)) {
+ if (shouldKeepStmtInDeadControlFlow(p, _if_.yes, allocator)) {
return true;
}
const no = _if_.no orelse return false;
- return shouldKeepStmtInDeadControlFlow(no, allocator);
+ return shouldKeepStmtInDeadControlFlow(p, no, allocator);
},
.s_while => {
- return shouldKeepStmtInDeadControlFlow(stmt.data.s_while.body, allocator);
+ return shouldKeepStmtInDeadControlFlow(p, stmt.data.s_while.body, allocator);
},
.s_do_while => {
- return shouldKeepStmtInDeadControlFlow(stmt.data.s_do_while.body, allocator);
+ return shouldKeepStmtInDeadControlFlow(p, stmt.data.s_do_while.body, allocator);
},
.s_for => |__for__| {
if (__for__.init) |init_| {
- if (shouldKeepStmtInDeadControlFlow(init_, allocator)) {
+ if (shouldKeepStmtInDeadControlFlow(p, init_, allocator)) {
return true;
}
}
- return shouldKeepStmtInDeadControlFlow(__for__.body, allocator);
+ return shouldKeepStmtInDeadControlFlow(p, __for__.body, allocator);
},
.s_for_in => |__for__| {
- return shouldKeepStmtInDeadControlFlow(__for__.init, allocator) or shouldKeepStmtInDeadControlFlow(__for__.body, allocator);
+ return shouldKeepStmtInDeadControlFlow(p, __for__.init, allocator) or shouldKeepStmtInDeadControlFlow(p, __for__.body, allocator);
},
.s_for_of => |__for__| {
- return shouldKeepStmtInDeadControlFlow(__for__.init, allocator) or shouldKeepStmtInDeadControlFlow(__for__.body, allocator);
+ return shouldKeepStmtInDeadControlFlow(p, __for__.init, allocator) or shouldKeepStmtInDeadControlFlow(p, __for__.body, allocator);
},
.s_label => |label| {
- return shouldKeepStmtInDeadControlFlow(label.stmt, allocator);
+ return shouldKeepStmtInDeadControlFlow(p, label.stmt, allocator);
},
else => return true,
}
@@ -1928,11 +2094,15 @@ pub const SideEffects = enum(u1) {
pub const toTypeOf = Expr.Data.typeof;
- pub fn toNullOrUndefined(exp: Expr.Data) Result {
+ pub fn toNullOrUndefined(p: anytype, exp: Expr.Data) Result {
+ if (!p.options.features.dead_code_elimination) {
+ // value should not be read if ok is false, all existing calls to this function already adhere to this
+ return Result{ .ok = false, .value = undefined, .side_effects = .could_have_side_effects };
+ }
switch (exp) {
// Never null or undefined
.e_boolean, .e_number, .e_string, .e_reg_exp, .e_function, .e_arrow, .e_big_int => {
- return Result{ .value = false, .side_effects = SideEffects.no_side_effects, .ok = true };
+ return Result{ .value = false, .side_effects = .no_side_effects, .ok = true };
},
.e_object, .e_array, .e_class => {
@@ -2016,7 +2186,7 @@ pub const SideEffects = enum(u1) {
},
.bin_comma => {
- const res = toNullOrUndefined(e.right.data);
+ const res = toNullOrUndefined(p, e.right.data);
if (res.ok) {
return Result{ .ok = true, .value = res.value, .side_effects = SideEffects.could_have_side_effects };
}
@@ -2030,7 +2200,11 @@ pub const SideEffects = enum(u1) {
return Result{ .ok = false, .value = false, .side_effects = SideEffects.could_have_side_effects };
}
- pub fn toBoolean(exp: Expr.Data) Result {
+ pub fn toBoolean(p: anytype, exp: Expr.Data) Result {
+ if (!p.options.features.dead_code_elimination) {
+ // value should not be read if ok is false, all existing calls to this function already adhere to this
+ return Result{ .ok = false, .value = undefined, .side_effects = .could_have_side_effects };
+ }
switch (exp) {
.e_null, .e_undefined => {
return Result{ .ok = true, .value = false, .side_effects = .no_side_effects };
@@ -2064,7 +2238,7 @@ pub const SideEffects = enum(u1) {
return Result{ .ok = true, .value = true, .side_effects = .could_have_side_effects };
},
.un_not => {
- var result = toBoolean(e_.value.data);
+ var result = toBoolean(p, e_.value.data);
if (result.ok) {
result.value = !result.value;
return result;
@@ -2077,21 +2251,21 @@ pub const SideEffects = enum(u1) {
switch (e_.op) {
.bin_logical_or => {
// "anything || truthy" is truthy
- const result = toBoolean(e_.right.data);
+ const result = toBoolean(p, e_.right.data);
if (result.value and result.ok) {
return Result{ .ok = true, .value = true, .side_effects = .could_have_side_effects };
}
},
.bin_logical_and => {
// "anything && falsy" is falsy
- const result = toBoolean(e_.right.data);
+ const result = toBoolean(p, e_.right.data);
if (!result.value and result.ok) {
return Result{ .ok = true, .value = false, .side_effects = .could_have_side_effects };
}
},
.bin_comma => {
// "anything, truthy/falsy" is truthy/falsy
- var result = toBoolean(e_.right.data);
+ var result = toBoolean(p, e_.right.data);
if (result.ok) {
result.side_effects = .could_have_side_effects;
return result;
@@ -2437,6 +2611,7 @@ const FnOrArrowDataParse = struct {
is_typescript_declare: bool = false,
has_argument_decorators: bool = false,
+ has_decorators: bool = false,
is_return_disallowed: bool = false,
is_this_disallowed: bool = false,
@@ -2573,6 +2748,7 @@ const PropertyOpts = struct {
is_ts_abstract: bool = false,
ts_decorators: []Expr = &[_]Expr{},
has_argument_decorators: bool = false,
+ has_class_decorators: bool = false,
};
pub const ScanPassResult = struct {
@@ -4304,7 +4480,7 @@ fn NewParser_(
emitted_namespace_vars: RefMap = RefMap{},
is_exported_inside_namespace: RefRefMap = .{},
- known_enum_values: Map(Ref, StringHashMapUnamanged(f64)) = .{},
+ known_enum_values: Map(Ref, StringHashMapUnmanaged(f64)) = .{},
local_type_names: StringBoolMap = StringBoolMap{},
// This is the reference to the generated function argument for the namespace,
@@ -4787,20 +4963,20 @@ fn NewParser_(
}
},
.s_if => |if_statement| {
- const result = SideEffects.toBoolean(if_statement.test_.data);
+ const result = SideEffects.toBoolean(p, if_statement.test_.data);
if (!(result.ok and result.side_effects == .no_side_effects and !result.value)) {
break :can_remove_part false;
}
},
.s_while => |while_statement| {
- const result = SideEffects.toBoolean(while_statement.test_.data);
+ const result = SideEffects.toBoolean(p, while_statement.test_.data);
if (!(result.ok and result.side_effects == .no_side_effects and !result.value)) {
break :can_remove_part false;
}
},
.s_for => |for_statement| {
if (for_statement.test_) |expr| {
- const result = SideEffects.toBoolean(expr.data);
+ const result = SideEffects.toBoolean(p, expr.data);
if (!(result.ok and result.side_effects == .no_side_effects and !result.value)) {
break :can_remove_part false;
}
@@ -6729,6 +6905,7 @@ fn NewParser_(
p.fn_or_arrow_data_parse.allow_super_call = opts.allow_super_call;
p.fn_or_arrow_data_parse.allow_super_property = opts.allow_super_property;
+ var rest_arg: bool = false;
var arg_has_decorators: bool = false;
var args = List(G.Arg){};
while (p.lexer.token != T.t_close_paren) {
@@ -6737,7 +6914,7 @@ fn NewParser_(
try p.lexer.next();
if (p.lexer.token == T.t_colon) {
try p.lexer.next();
- try p.skipTypeScriptType(js_ast.Op.Level.lowest);
+ try p.skipTypeScriptType(.lowest);
}
if (p.lexer.token != T.t_comma) {
break;
@@ -6758,6 +6935,7 @@ fn NewParser_(
if (!func.flags.contains(.has_rest_arg) and p.lexer.token == T.t_dot_dot_dot) {
// p.markSyntaxFeature
try p.lexer.next();
+ rest_arg = true;
func.flags.insert(.has_rest_arg);
}
@@ -6765,6 +6943,7 @@ fn NewParser_(
var is_identifier = p.lexer.token == T.t_identifier;
var text = p.lexer.identifier;
var arg = try p.parseBinding();
+ var ts_metadata = TypeScript.Metadata.default;
if (comptime is_typescript_enabled) {
if (is_identifier and opts.is_constructor) {
@@ -6804,7 +6983,19 @@ fn NewParser_(
// "function foo(a: any) {}"
if (p.lexer.token == .t_colon) {
try p.lexer.next();
- try p.skipTypeScriptType(.lowest);
+ if (!rest_arg) {
+ if (p.options.features.emit_decorator_metadata and
+ opts.allow_ts_decorators and
+ (opts.has_argument_decorators or opts.has_decorators or arg_has_decorators))
+ {
+ ts_metadata = try p.skipTypeScriptTypeWithMetadata(.lowest);
+ } else {
+ try p.skipTypeScriptType(.lowest);
+ }
+ } else {
+ // rest parameter is always object, leave metadata as m_none
+ try p.skipTypeScriptType(.lowest);
+ }
}
}
@@ -6825,6 +7016,7 @@ fn NewParser_(
// We need to track this because it affects code generation
.is_typescript_ctor_field = is_typescript_ctor_field,
+ .ts_metadata = ts_metadata,
}) catch unreachable;
if (p.lexer.token != .t_comma) {
@@ -6844,6 +7036,7 @@ fn NewParser_(
}
try p.lexer.next();
+ rest_arg = false;
}
if (args.items.len > 0) {
func.args = args.items;
@@ -6864,9 +7057,22 @@ fn NewParser_(
p.fn_or_arrow_data_parse.has_argument_decorators = arg_has_decorators;
// "function foo(): any {}"
- if (is_typescript_enabled and p.lexer.token == .t_colon) {
- try p.lexer.next();
- try p.skipTypescriptReturnType();
+ if (is_typescript_enabled) {
+ if (p.lexer.token == .t_colon) {
+ try p.lexer.next();
+
+ if (p.options.features.emit_decorator_metadata and opts.allow_ts_decorators and (opts.has_argument_decorators or opts.has_decorators)) {
+ func.return_ts_metadata = try p.skipTypescriptReturnTypeWithMetadata();
+ } else {
+ try p.skipTypescriptReturnType();
+ }
+ } else if (p.options.features.emit_decorator_metadata and opts.allow_ts_decorators and (opts.has_argument_decorators or opts.has_decorators)) {
+ if (func.flags.contains(.is_async)) {
+ func.return_ts_metadata = .m_promise;
+ } else {
+ func.return_ts_metadata = .m_undefined;
+ }
+ }
}
// "function foo(): any;"
@@ -6881,10 +7087,15 @@ fn NewParser_(
return func;
}
- // pub fn parseBinding(p: *P)
-
pub inline fn skipTypescriptReturnType(p: *P) anyerror!void {
- try p.skipTypeScriptTypeWithOpts(.lowest, .{ .is_return_type = true });
+ var result = TypeScript.Metadata.default;
+ try p.skipTypeScriptTypeWithOpts(.lowest, TypeScript.SkipTypeOptions.Bitset.initOne(.is_return_type), false, &result);
+ }
+
+ pub inline fn skipTypescriptReturnTypeWithMetadata(p: *P) anyerror!TypeScript.Metadata {
+ var result = TypeScript.Metadata.default;
+ try p.skipTypeScriptTypeWithOpts(.lowest, TypeScript.SkipTypeOptions.Bitset.initOne(.is_return_type), true, &result);
+ return result;
}
pub fn parseTypeScriptDecorators(p: *P) ![]ExprNodeIndex {
@@ -6912,7 +7123,15 @@ fn NewParser_(
inline fn skipTypeScriptType(p: *P, level: js_ast.Op.Level) anyerror!void {
p.markTypeScriptOnly();
- try p.skipTypeScriptTypeWithOpts(level, .{});
+ var result = TypeScript.Metadata.default;
+ try p.skipTypeScriptTypeWithOpts(level, TypeScript.SkipTypeOptions.empty, false, &result);
+ }
+
+ inline fn skipTypeScriptTypeWithMetadata(p: *P, level: js_ast.Op.Level) anyerror!TypeScript.Metadata {
+ p.markTypeScriptOnly();
+ var result = TypeScript.Metadata.default;
+ try p.skipTypeScriptTypeWithOpts(level, TypeScript.SkipTypeOptions.empty, true, &result);
+ return result;
}
fn skipTypeScriptBinding(p: *P) anyerror!void {
@@ -7057,41 +7276,77 @@ fn NewParser_(
// let x = (y: any): (y) => {return 0};
// let x = (y: any): asserts y is (y) => {};
//
- fn skipTypeScriptParenOrFnType(p: *P) anyerror!void {
+ fn skipTypeScriptParenOrFnType(p: *P, comptime get_metadata: bool, result: *TypeScript.Metadata) anyerror!void {
p.markTypeScriptOnly();
if (p.trySkipTypeScriptArrowArgsWithBacktracking()) {
try p.skipTypescriptReturnType();
+ if (comptime get_metadata)
+ result.* = .m_function;
} else {
try p.lexer.expect(.t_open_paren);
- try p.skipTypeScriptType(.lowest);
+ if (comptime get_metadata) {
+ result.* = try p.skipTypeScriptTypeWithMetadata(.lowest);
+ } else {
+ try p.skipTypeScriptType(.lowest);
+ }
try p.lexer.expect(.t_close_paren);
}
}
- fn skipTypeScriptTypeWithOpts(p: *P, level: js_ast.Op.Level, opts: TypeScript.SkipTypeOptions) anyerror!void {
+ fn skipTypeScriptTypeWithOpts(
+ p: *P,
+ level: js_ast.Op.Level,
+ opts: TypeScript.SkipTypeOptions.Bitset,
+ comptime get_metadata: bool,
+ result: *TypeScript.Metadata,
+ ) anyerror!void {
p.markTypeScriptOnly();
while (true) {
switch (p.lexer.token) {
- .t_numeric_literal,
- .t_big_integer_literal,
- .t_string_literal,
- .t_no_substitution_template_literal,
- .t_true,
- .t_false,
- .t_null,
- .t_void,
- => {
+ .t_numeric_literal => {
try p.lexer.next();
+ if (comptime get_metadata) {
+ result.* = .m_number;
+ }
+ },
+ .t_big_integer_literal => {
+ try p.lexer.next();
+ if (comptime get_metadata) {
+ result.* = .m_bigint;
+ }
+ },
+ .t_string_literal, .t_no_substitution_template_literal => {
+ try p.lexer.next();
+ if (comptime get_metadata) {
+ result.* = .m_string;
+ }
+ },
+ .t_true, .t_false => {
+ try p.lexer.next();
+ if (comptime get_metadata) {
+ result.* = .m_boolean;
+ }
+ },
+ .t_null => {
+ try p.lexer.next();
+ if (comptime get_metadata) {
+ result.* = .m_null;
+ }
+ },
+ .t_void => {
+ try p.lexer.next();
+ if (comptime get_metadata) {
+ result.* = .m_void;
+ }
},
-
.t_const => {
const r = p.lexer.range();
try p.lexer.next();
// ["const: number]"
- if (opts.allow_tuple_labels and p.lexer.token == .t_colon) {
+ if (opts.contains(.allow_tuple_labels) and p.lexer.token == .t_colon) {
try p.log.addRangeError(p.source, r, "Unexpected \"const\"");
}
},
@@ -7105,6 +7360,10 @@ fn NewParser_(
try p.skipTypeScriptType(.lowest);
return;
}
+
+ if (comptime get_metadata) {
+ result.* = .m_object;
+ }
},
.t_minus => {
// "-123"
@@ -7113,8 +7372,14 @@ fn NewParser_(
if (p.lexer.token == .t_big_integer_literal) {
try p.lexer.next();
+ if (comptime get_metadata) {
+ result.* = .m_bigint;
+ }
} else {
try p.lexer.expect(.t_numeric_literal);
+ if (comptime get_metadata) {
+ result.* = .m_number;
+ }
}
},
.t_ampersand, .t_bar => {
@@ -7127,7 +7392,7 @@ fn NewParser_(
try p.lexer.next();
// "[import: number]"
- if (opts.allow_tuple_labels and p.lexer.token == .t_colon) {
+ if (opts.contains(.allow_tuple_labels) and p.lexer.token == .t_colon) {
return;
}
@@ -7155,21 +7420,21 @@ fn NewParser_(
try p.lexer.next();
// "[new: number]"
- if (opts.allow_tuple_labels and p.lexer.token == .t_colon) {
+ if (opts.contains(.allow_tuple_labels) and p.lexer.token == .t_colon) {
return;
}
_ = try p.skipTypeScriptTypeParameters(.{ .allow_const_modifier = true });
- try p.skipTypeScriptParenOrFnType();
+ try p.skipTypeScriptParenOrFnType(get_metadata, result);
},
.t_less_than => {
// "<T>() => Foo<T>"
_ = try p.skipTypeScriptTypeParameters(.{ .allow_const_modifier = true });
- try p.skipTypeScriptParenOrFnType();
+ try p.skipTypeScriptParenOrFnType(get_metadata, result);
},
.t_open_paren => {
// "(number | string)"
- try p.skipTypeScriptParenOrFnType();
+ try p.skipTypeScriptParenOrFnType(get_metadata, result);
},
.t_identifier => {
const kind = TypeScript.Identifier.IMap.get(p.lexer.identifier) orelse .normal;
@@ -7177,7 +7442,7 @@ fn NewParser_(
var check_type_parameters = true;
switch (kind) {
- .prefix => {
+ .prefix_keyof => {
try p.lexer.next();
// Valid:
@@ -7188,10 +7453,28 @@ fn NewParser_(
// Invalid:
// "A extends B ? keyof : string"
//
- if ((p.lexer.token != .t_colon and p.lexer.token != .t_in) or (!opts.is_index_signature and !opts.allow_tuple_labels)) {
+ if ((p.lexer.token != .t_colon and p.lexer.token != .t_in) or (!opts.contains(.is_index_signature) and !opts.contains(.allow_tuple_labels))) {
try p.skipTypeScriptType(.prefix);
}
+ if (comptime get_metadata) {
+ result.* = .m_object;
+ }
+
+ break;
+ },
+ .prefix_readonly => {
+ try p.lexer.next();
+
+ if ((p.lexer.token != .t_colon and p.lexer.token != .t_in) or (!opts.contains(.is_index_signature) and !opts.contains(.allow_tuple_labels))) {
+ try p.skipTypeScriptType(.prefix);
+ }
+
+ // assume array or tuple literal
+ if (comptime get_metadata) {
+ result.* = .m_array;
+ }
+
break;
},
.infer => {
@@ -7201,7 +7484,7 @@ fn NewParser_(
// "type Foo = Bar extends [infer T extends string] ? T : null"
// "type Foo = Bar extends [infer T extends string ? infer T : never] ? T : null"
// "type Foo = { [infer in Bar]: number }"
- if ((p.lexer.token != .t_colon and p.lexer.token != .t_in) or (!opts.is_index_signature and !opts.allow_tuple_labels)) {
+ if ((p.lexer.token != .t_colon and p.lexer.token != .t_in) or (!opts.contains(.is_index_signature) and !opts.contains(.allow_tuple_labels))) {
try p.lexer.expect(.t_identifier);
if (p.lexer.token == .t_extends) {
_ = p.trySkipTypeScriptConstraintOfInferTypeWithBacktracking(opts);
@@ -7232,15 +7515,90 @@ fn NewParser_(
// "function assert(x: boolean): asserts x"
// "function assert(x: boolean): asserts x is boolean"
- if (opts.is_return_type and !p.lexer.has_newline_before and (p.lexer.token == .t_identifier or p.lexer.token == .t_this)) {
+ if (opts.contains(.is_return_type) and !p.lexer.has_newline_before and (p.lexer.token == .t_identifier or p.lexer.token == .t_this)) {
try p.lexer.next();
}
},
- .primitive => {
+ .primitive_any => {
+ try p.lexer.next();
+ check_type_parameters = false;
+ if (comptime get_metadata) {
+ result.* = .m_any;
+ }
+ },
+ .primitive_never => {
+ try p.lexer.next();
+ check_type_parameters = false;
+ if (comptime get_metadata) {
+ result.* = .m_never;
+ }
+ },
+ .primitive_unknown => {
+ try p.lexer.next();
+ check_type_parameters = false;
+ if (comptime get_metadata) {
+ result.* = .m_unknown;
+ }
+ },
+ .primitive_undefined => {
+ try p.lexer.next();
+ check_type_parameters = false;
+ if (comptime get_metadata) {
+ result.* = .m_undefined;
+ }
+ },
+ .primitive_object => {
+ try p.lexer.next();
+ check_type_parameters = false;
+ if (comptime get_metadata) {
+ result.* = .m_object;
+ }
+ },
+ .primitive_number => {
+ try p.lexer.next();
+ check_type_parameters = false;
+ if (comptime get_metadata) {
+ result.* = .m_number;
+ }
+ },
+ .primitive_string => {
try p.lexer.next();
check_type_parameters = false;
+ if (comptime get_metadata) {
+ result.* = .m_string;
+ }
+ },
+ .primitive_boolean => {
+ try p.lexer.next();
+ check_type_parameters = false;
+ if (comptime get_metadata) {
+ result.* = .m_boolean;
+ }
+ },
+ .primitive_bigint => {
+ try p.lexer.next();
+ check_type_parameters = false;
+ if (comptime get_metadata) {
+ result.* = .m_bigint;
+ }
+ },
+ .primitive_symbol => {
+ try p.lexer.next();
+ check_type_parameters = false;
+ if (comptime get_metadata) {
+ result.* = .m_symbol;
+ }
},
else => {
+ if (comptime get_metadata) {
+ const find_result = p.findSymbol(logger.Loc.Empty, p.lexer.identifier) catch unreachable;
+ if (p.known_enum_values.contains(find_result.ref)) {
+ result.* = .m_number;
+ } else {
+ result.* = .{ .m_identifier = find_result.ref };
+ }
+ }
+
try p.lexer.next();
},
}
@@ -7261,10 +7619,15 @@ fn NewParser_(
try p.lexer.next();
// "[typeof: number]"
- if (opts.allow_tuple_labels and p.lexer.token == .t_colon) {
+ if (opts.contains(.allow_tuple_labels) and p.lexer.token == .t_colon) {
return;
}
+ // always `Object`
+ if (comptime get_metadata) {
+ result.* = .m_object;
+ }
+
if (p.lexer.token == .t_import) {
// "typeof import('fs')"
continue;
@@ -7296,13 +7659,16 @@ fn NewParser_(
// "[first: number, second: string]"
try p.lexer.next();
+ if (comptime get_metadata) {
+ result.* = .m_array;
+ }
+
while (p.lexer.token != .t_close_bracket) {
if (p.lexer.token == .t_dot_dot_dot) {
try p.lexer.next();
}
- try p.skipTypeScriptTypeWithOpts(.lowest, TypeScript.SkipTypeOptions{
- .allow_tuple_labels = true,
- });
+ var dummy_result = TypeScript.Metadata.default;
+ try p.skipTypeScriptTypeWithOpts(.lowest, TypeScript.SkipTypeOptions.Bitset.initOne(.allow_tuple_labels), false, &dummy_result);
if (p.lexer.token == .t_question) {
try p.lexer.next();
}
@@ -7319,10 +7685,12 @@ fn NewParser_(
},
.t_open_brace => {
try p.skipTypeScriptObjectType();
+ if (comptime get_metadata) {
+ result.* = .m_object;
+ }
},
.t_template_head => {
// "`${'a' | 'b'}-${'c' | 'd'}`"
-
while (true) {
try p.lexer.next();
try p.skipTypeScriptType(.lowest);
@@ -7333,11 +7701,14 @@ fn NewParser_(
break;
}
}
+ if (comptime get_metadata) {
+ result.* = .m_string;
+ }
},
else => {
// "[function: number]"
- if (opts.allow_tuple_labels and p.lexer.isIdentifierOrKeyword()) {
+ if (opts.contains(.allow_tuple_labels) and p.lexer.isIdentifierOrKeyword()) {
if (p.lexer.token != .t_function) {
try p.lexer.unexpected();
}
@@ -7362,8 +7733,22 @@ fn NewParser_(
if (level.gte(.bitwise_or)) {
return;
}
+
try p.lexer.next();
- try p.skipTypeScriptType(.bitwise_or);
+
+ if (comptime get_metadata) {
+ var left = result.*;
+ if (left.finishUnion(p)) |final| {
+ // finish skipping the rest of the type without collecting type metadata.
+ result.* = final;
+ try p.skipTypeScriptType(.bitwise_or);
+ } else {
+ try p.skipTypeScriptTypeWithOpts(.bitwise_or, TypeScript.SkipTypeOptions.empty, get_metadata, result);
+ result.mergeUnion(left);
+ }
+ } else {
+ try p.skipTypeScriptType(.bitwise_or);
+ }
},
.t_ampersand => {
if (level.gte(.bitwise_and)) {
@@ -7371,7 +7756,20 @@ fn NewParser_(
}
try p.lexer.next();
- try p.skipTypeScriptType(.bitwise_and);
+
+ if (comptime get_metadata) {
+ var left = result.*;
+ if (left.finishIntersection(p)) |final| {
+ // finish skipping the rest of the type without collecting type metadata.
+ result.* = final;
+ try p.skipTypeScriptType(.bitwise_and);
+ } else {
+ try p.skipTypeScriptTypeWithOpts(.bitwise_and, TypeScript.SkipTypeOptions.empty, get_metadata, result);
+ result.mergeIntersection(left);
+ }
+ } else {
+ try p.skipTypeScriptType(.bitwise_and);
+ }
},
.t_exclamation => {
// A postfix "!" is allowed in JSDoc types in TypeScript, which are only
@@ -7390,6 +7788,22 @@ fn NewParser_(
if (!p.lexer.isIdentifierOrKeyword()) {
try p.lexer.expect(.t_identifier);
}
+
+ if (comptime get_metadata) {
+ if (result.* == .m_identifier) {
+ var dot = List(Ref).initCapacity(p.allocator, 2) catch unreachable;
+ dot.appendAssumeCapacity(result.m_identifier);
+ const find_result = p.findSymbol(logger.Loc.Empty, p.lexer.identifier) catch unreachable;
+ dot.appendAssumeCapacity(find_result.ref);
+ result.* = .{ .m_dot = dot };
+ } else if (result.* == .m_dot) {
+ if (p.lexer.isIdentifierOrKeyword()) {
+ const find_result = p.findSymbol(logger.Loc.Empty, p.lexer.identifier) catch unreachable;
+ result.m_dot.append(p.allocator, find_result.ref) catch unreachable;
+ }
+ }
+ }
+
try p.lexer.next();
// "{ <A extends B>(): c.d \n <E extends F>(): g.h }" must not become a single type
@@ -7403,26 +7817,56 @@ fn NewParser_(
return;
}
try p.lexer.next();
+ var skipped = false;
if (p.lexer.token != .t_close_bracket) {
+ skipped = true;
try p.skipTypeScriptType(.lowest);
}
try p.lexer.expect(.t_close_bracket);
+
+ if (comptime get_metadata) {
+ if (result.* == .m_none) {
+ result.* = .m_array;
+ } else {
+ // if something was skipped, it is object type
+ if (skipped) {
+ result.* = .m_object;
+ } else {
+ result.* = .m_array;
+ }
+ }
+ }
},
.t_extends => {
// "{ x: number \n extends: boolean }" must not become a single type
- if (p.lexer.has_newline_before or opts.disallow_conditional_types) {
+ if (p.lexer.has_newline_before or opts.contains(.disallow_conditional_types)) {
return;
}
try p.lexer.next();
// The type following "extends" is not permitted to be another conditional type
- try p.skipTypeScriptTypeWithOpts(.lowest, .{ .disallow_conditional_types = true });
+ var extends_type = TypeScript.Metadata.default;
+ try p.skipTypeScriptTypeWithOpts(.lowest, TypeScript.SkipTypeOptions.Bitset.initOne(.disallow_conditional_types), get_metadata, &extends_type);
- try p.lexer.expect(.t_question);
- try p.skipTypeScriptType(.lowest);
- try p.lexer.expect(.t_colon);
- try p.skipTypeScriptType(.lowest);
+ if (comptime get_metadata) {
+ // intersection
+ try p.lexer.expect(.t_question);
+ var left = try p.skipTypeScriptTypeWithMetadata(.lowest);
+ try p.lexer.expect(.t_colon);
+ if (left.finishIntersection(p)) |final| {
+ result.* = final;
+ try p.skipTypeScriptType(.lowest);
+ } else {
+ try p.skipTypeScriptTypeWithOpts(.bitwise_and, TypeScript.SkipTypeOptions.empty, get_metadata, result);
+ result.mergeIntersection(left);
+ }
+ } else {
+ try p.lexer.expect(.t_question);
+ try p.skipTypeScriptType(.lowest);
+ try p.lexer.expect(.t_colon);
+ try p.skipTypeScriptType(.lowest);
+ }
},
else => {
return;
@@ -7452,7 +7896,8 @@ fn NewParser_(
if (p.lexer.token == .t_open_bracket) {
// Index signature or computed property
try p.lexer.next();
- try p.skipTypeScriptTypeWithOpts(.lowest, .{ .is_index_signature = true });
+ var metadata = TypeScript.Metadata.default;
+ try p.skipTypeScriptTypeWithOpts(.lowest, TypeScript.SkipTypeOptions.Bitset.initOne(.is_index_signature), false, &metadata);
// "{ [key: string]: number }"
// "{ readonly [K in keyof T]: T[K] }"
@@ -11122,13 +11567,12 @@ fn NewParser_(
return result;
}
- pub fn skipTypeScriptConstraintOfInferTypeWithBacktracking(p: *P, flags: TypeScript.SkipTypeOptions) anyerror!bool {
+ pub fn skipTypeScriptConstraintOfInferTypeWithBacktracking(p: *P, flags: TypeScript.SkipTypeOptions.Bitset) anyerror!bool {
try p.lexer.expect(.t_extends);
- try p.skipTypeScriptTypeWithOpts(.prefix, TypeScript.SkipTypeOptions{
- .disallow_conditional_types = true,
- });
+ var metadata = TypeScript.Metadata.default;
+ try p.skipTypeScriptTypeWithOpts(.prefix, TypeScript.SkipTypeOptions.Bitset.initOne(.disallow_conditional_types), false, &metadata);
- if (!flags.disallow_conditional_types and p.lexer.token == .t_question) {
+ if (!flags.contains(.disallow_conditional_types) and p.lexer.token == .t_question) {
return error.Backtrack;
}
@@ -11181,7 +11625,7 @@ fn NewParser_(
return Backtracking.lexerBacktracker(p, Backtracking.skipTypeScriptArrowArgsWithBacktracking, bool);
}
- pub fn trySkipTypeScriptConstraintOfInferTypeWithBacktracking(p: *P, flags: TypeScript.SkipTypeOptions) bool {
+ pub fn trySkipTypeScriptConstraintOfInferTypeWithBacktracking(p: *P, flags: TypeScript.SkipTypeOptions.Bitset) bool {
return Backtracking.lexerBacktrackerWithArgs(p, Backtracking.skipTypeScriptConstraintOfInferTypeWithBacktracking, .{ p, flags }, bool);
}
@@ -11621,6 +12065,7 @@ fn NewParser_(
(p.lexer.token != .t_open_paren or has_definite_assignment_assertion_operator))
{
var initializer: ?Expr = null;
+ var ts_metadata = TypeScript.Metadata.default;
// Forbid the names "constructor" and "prototype" in some cases
if (!is_computed) {
@@ -11639,7 +12084,11 @@ fn NewParser_(
// Skip over types
if (p.lexer.token == .t_colon) {
try p.lexer.next();
- try p.skipTypeScriptType(.lowest);
+ if (p.options.features.emit_decorator_metadata and opts.is_class and opts.ts_decorators.len > 0) {
+ ts_metadata = try p.skipTypeScriptTypeWithMetadata(.lowest);
+ } else {
+ try p.skipTypeScriptType(.lowest);
+ }
}
}
@@ -11693,6 +12142,7 @@ fn NewParser_(
}),
.key = key,
.initializer = initializer,
+ .ts_metadata = ts_metadata,
};
}
@@ -11739,6 +12189,7 @@ fn NewParser_(
.allow_super_property = true,
.allow_ts_decorators = opts.allow_ts_decorators,
.is_constructor = is_constructor,
+ .has_decorators = opts.ts_decorators.len > 0 or (opts.has_class_decorators and is_constructor),
// Only allow omitting the body if we're parsing TypeScript class
.allow_missing_body_for_type_script = is_typescript_enabled and opts.is_class,
@@ -11816,6 +12267,7 @@ fn NewParser_(
}),
.key = key,
.value = value,
+ .ts_metadata = .m_function,
};
}
@@ -11895,6 +12347,7 @@ fn NewParser_(
const first_decorator_loc = p.lexer.loc();
if (opts.allow_ts_decorators) {
opts.ts_decorators = try p.parseTypeScriptDecorators();
+ opts.has_class_decorators = class_opts.ts_decorators.len > 0;
has_decorators = has_decorators or opts.ts_decorators.len > 0;
} else {
opts.ts_decorators = &[_]Expr{};
@@ -12131,7 +12584,7 @@ fn NewParser_(
// Remove unnecessary optional chains
if (p.options.features.minify_syntax) {
- const result = SideEffects.toNullOrUndefined(left.data);
+ const result = SideEffects.toNullOrUndefined(p, left.data);
if (result.ok and !result.value) {
optional_start = null;
}
@@ -13568,7 +14021,7 @@ fn NewParser_(
.name = part,
.name_loc = loc,
- .can_be_removed_if_unused = true,
+ .can_be_removed_if_unused = p.options.features.dead_code_elimination,
},
loc,
);
@@ -14031,6 +14484,7 @@ fn NewParser_(
}
fn bindingCanBeRemovedIfUnused(p: *P, binding: Binding) bool {
+ if (!p.options.features.dead_code_elimination) return false;
switch (binding.data) {
.b_array => |bi| {
for (bi.items) |*item| {
@@ -14069,6 +14523,7 @@ fn NewParser_(
}
fn stmtsCanBeRemovedIfUnused(p: *P, stmts: []Stmt) bool {
+ if (!p.options.features.dead_code_elimination) return false;
for (stmts) |stmt| {
switch (stmt.data) {
// These never have side effects
@@ -14902,7 +15357,7 @@ fn NewParser_(
// Mark the control flow as dead if the branch is never taken
switch (e_.op) {
.bin_logical_or => {
- const side_effects = SideEffects.toBoolean(e_.left.data);
+ const side_effects = SideEffects.toBoolean(p, e_.left.data);
if (side_effects.ok and side_effects.value) {
// "true || dead"
const old = p.is_control_flow_dead;
@@ -14914,7 +15369,7 @@ fn NewParser_(
}
},
.bin_logical_and => {
- const side_effects = SideEffects.toBoolean(e_.left.data);
+ const side_effects = SideEffects.toBoolean(p, e_.left.data);
if (side_effects.ok and !side_effects.value) {
// "false && dead"
const old = p.is_control_flow_dead;
@@ -14926,7 +15381,7 @@ fn NewParser_(
}
},
.bin_nullish_coalescing => {
- const side_effects = SideEffects.toNullOrUndefined(e_.left.data);
+ const side_effects = SideEffects.toNullOrUndefined(p, e_.left.data);
if (side_effects.ok and !side_effects.value) {
// "notNullOrUndefined ?? dead"
const old = p.is_control_flow_dead;
@@ -15006,7 +15461,7 @@ fn NewParser_(
}
},
.bin_nullish_coalescing => {
- const nullorUndefined = SideEffects.toNullOrUndefined(e_.left.data);
+ const nullorUndefined = SideEffects.toNullOrUndefined(p, e_.left.data);
if (nullorUndefined.ok) {
if (!nullorUndefined.value) {
return e_.left;
@@ -15023,7 +15478,7 @@ fn NewParser_(
}
},
.bin_logical_or => {
- const side_effects = SideEffects.toBoolean(e_.left.data);
+ const side_effects = SideEffects.toBoolean(p, e_.left.data);
if (side_effects.ok and side_effects.value) {
return e_.left;
} else if (side_effects.ok and side_effects.side_effects == .no_side_effects) {
@@ -15038,7 +15493,7 @@ fn NewParser_(
}
},
.bin_logical_and => {
- const side_effects = SideEffects.toBoolean(e_.left.data);
+ const side_effects = SideEffects.toBoolean(p, e_.left.data);
if (side_effects.ok) {
if (!side_effects.value) {
return e_.left;
@@ -15404,7 +15859,7 @@ fn NewParser_(
if (p.options.features.minify_syntax)
e_.value = SideEffects.simplifyBoolean(p, e_.value);
- const side_effects = SideEffects.toBoolean(e_.value.data);
+ const side_effects = SideEffects.toBoolean(p, e_.value.data);
if (side_effects.ok) {
return p.newExpr(E.Boolean{ .value = !side_effects.value }, expr.loc);
}
@@ -15556,7 +16011,7 @@ fn NewParser_(
e_.test_ = SideEffects.simplifyBoolean(p, e_.test_);
- const side_effects = SideEffects.toBoolean(e_.test_.data);
+ const side_effects = SideEffects.toBoolean(p, e_.test_.data);
if (!side_effects.ok) {
e_.yes = p.visitExpr(e_.yes);
@@ -16129,6 +16584,7 @@ fn NewParser_(
}
pub fn classCanBeRemovedIfUnused(p: *P, class: *G.Class) bool {
+ if (!p.options.features.dead_code_elimination) return false;
if (class.extends) |*extends| {
if (!p.exprCanBeRemovedIfUnused(extends)) {
return false;
@@ -16167,6 +16623,7 @@ fn NewParser_(
// When React Fast Refresh is enabled, anything that's a JSX component should not be removable
// This is to improve the reliability of fast refresh between page loads.
pub fn exprCanBeRemovedIfUnused(p: *P, expr: *const Expr) bool {
+ if (!p.options.features.dead_code_elimination) return false;
switch (expr.data) {
.e_null,
.e_undefined,
@@ -17301,7 +17758,7 @@ fn NewParser_(
const orig_dead = p.is_control_flow_dead;
if (p.options.features.replace_exports.count() > 0) {
if (p.options.features.replace_exports.getPtr("default")) |entry| {
- p.is_control_flow_dead = entry.* != .replace;
+ p.is_control_flow_dead = p.options.features.dead_code_elimination and (entry.* != .replace);
mark_for_replace = true;
}
}
@@ -17555,7 +18012,8 @@ fn NewParser_(
}
},
.s_expr => |data| {
- const should_trim_primitive = !p.options.features.minify_syntax and !data.value.isPrimitiveLiteral();
+ const should_trim_primitive = p.options.features.dead_code_elimination and
+ (p.options.features.minify_syntax and data.value.isPrimitiveLiteral());
p.stmt_expr_value = data.value.data;
defer p.stmt_expr_value = .{ .e_missing = .{} };
@@ -17726,7 +18184,7 @@ fn NewParser_(
data.body = p.visitLoopBody(data.body);
data.test_ = SideEffects.simplifyBoolean(p, data.test_);
- const result = SideEffects.toBoolean(data.test_.data);
+ const result = SideEffects.toBoolean(p, data.test_.data);
if (result.ok and result.side_effects == .no_side_effects) {
data.test_ = p.newExpr(E.Boolean{ .value = result.value }, data.test_.loc);
}
@@ -17744,7 +18202,7 @@ fn NewParser_(
data.test_ = SideEffects.simplifyBoolean(p, data.test_);
}
- const effects = SideEffects.toBoolean(data.test_.data);
+ const effects = SideEffects.toBoolean(p, data.test_.data);
if (effects.ok and !effects.value) {
const old = p.is_control_flow_dead;
p.is_control_flow_dead = true;
@@ -17776,7 +18234,7 @@ fn NewParser_(
if (p.options.features.minify_syntax) {
if (effects.ok) {
if (effects.value) {
- if (data.no == null or !SideEffects.shouldKeepStmtInDeadControlFlow(data.no.?, p.allocator)) {
+ if (data.no == null or !SideEffects.shouldKeepStmtInDeadControlFlow(p, data.no.?, p.allocator)) {
if (effects.side_effects == .could_have_side_effects) {
// Keep the condition if it could have side effects (but is still known to be truthy)
if (SideEffects.simpifyUnusedExpr(p, data.test_)) |test_| {
@@ -17790,7 +18248,7 @@ fn NewParser_(
}
} else {
// The test is falsy
- if (!SideEffects.shouldKeepStmtInDeadControlFlow(data.yes, p.allocator)) {
+ if (!SideEffects.shouldKeepStmtInDeadControlFlow(p, data.yes, p.allocator)) {
if (effects.side_effects == .could_have_side_effects) {
// Keep the condition if it could have side effects (but is still known to be truthy)
if (SideEffects.simpifyUnusedExpr(p, data.test_)) |test_| {
@@ -17845,7 +18303,7 @@ fn NewParser_(
if (data.test_) |test_| {
data.test_ = SideEffects.simplifyBoolean(p, p.visitExpr(test_));
- const result = SideEffects.toBoolean(data.test_.?.data);
+ const result = SideEffects.toBoolean(p, data.test_.?.data);
if (result.ok and result.value and result.side_effects == .no_side_effects) {
data.test_ = null;
}
@@ -17986,7 +18444,8 @@ fn NewParser_(
.s_function => |data| {
// We mark it as dead, but the value may not actually be dead
// We just want to be sure to not increment the usage counts for anything in the function
- const mark_as_dead = data.func.flags.contains(.is_export) and p.options.features.replace_exports.count() > 0 and p.isExportToEliminate(data.func.name.?.ref.?);
+ const mark_as_dead = p.options.features.dead_code_elimination and data.func.flags.contains(.is_export) and
+ p.options.features.replace_exports.count() > 0 and p.isExportToEliminate(data.func.name.?.ref.?);
const original_is_dead = p.is_control_flow_dead;
if (mark_as_dead) {
@@ -18036,7 +18495,8 @@ fn NewParser_(
return;
},
.s_class => |data| {
- const mark_as_dead = data.is_export and p.options.features.replace_exports.count() > 0 and p.isExportToEliminate(data.class.class_name.?.ref.?);
+ const mark_as_dead = p.options.features.dead_code_elimination and data.is_export and
+ p.options.features.replace_exports.count() > 0 and p.isExportToEliminate(data.class.class_name.?.ref.?);
const original_is_dead = p.is_control_flow_dead;
if (mark_as_dead) {
@@ -18123,7 +18583,7 @@ fn NewParser_(
// Track values so they can be used by constant folding. We need to follow
// links here in case the enum was merged with a preceding namespace
- var values_so_far = StringHashMapUnamanged(f64){};
+ var values_so_far = StringHashMapUnmanaged(f64){};
p.known_enum_values.put(allocator, data.name.ref orelse p.panic("Expected data.name.ref", .{}), values_so_far) catch unreachable;
p.known_enum_values.put(allocator, data.arg, values_so_far) catch unreachable;
@@ -18288,7 +18748,7 @@ fn NewParser_(
if (decl.binding.data == .b_identifier) {
if (p.options.features.replace_exports.getPtr(p.loadNameFromRef(decl.binding.data.b_identifier.ref))) |replacer| {
replacement = replacer;
- if (replacer.* != .replace) {
+ if (p.options.features.dead_code_elimination and (replacer.* != .replace)) {
p.is_control_flow_dead = true;
}
}
@@ -18765,7 +19225,7 @@ fn NewParser_(
const args = p.allocator.alloc(Expr, 2) catch unreachable;
args[0] = p.newExpr(E.Number{ .value = @as(f64, @floatFromInt(i)) }, arg_decorator.loc);
args[1] = arg_decorator;
- decorators.append(p.callRuntime(arg_decorator.loc, "__decorateParam", args)) catch unreachable;
+ decorators.append(p.callRuntime(arg_decorator.loc, "__legacyDecorateParamTS", args)) catch unreachable;
if (is_constructor) {
class.ts_decorators.update(decorators);
} else {
@@ -18792,7 +19252,12 @@ fn NewParser_(
else => bun.unreachablePanic("Unexpected AST node type {any}", .{prop.key.?}),
};
- const descriptor_kind: f64 = if (!prop.flags.contains(.is_method)) 2 else 1;
+ // TODO: when we have the `accessor` modifier, add `and !prop.flags.contains(.has_accessor_modifier)` to
+ // the if statement.
+ const descriptor_kind: Expr = if (!prop.flags.contains(.is_method))
+ p.newExpr(E.Undefined{}, loc)
+ else
+ p.newExpr(E.Null{}, loc);
var target: Expr = undefined;
if (prop.flags.contains(.is_static)) {
@@ -18802,13 +19267,60 @@ fn NewParser_(
target = p.newExpr(E.Dot{ .target = p.newExpr(E.Identifier{ .ref = class.class_name.?.ref.? }, class.class_name.?.loc), .name = "prototype", .name_loc = loc }, loc);
}
+ var array = prop.ts_decorators.listManaged(p.allocator);
+
+ if (p.options.features.emit_decorator_metadata) {
+ {
+ // design:type
+ var args = p.allocator.alloc(Expr, 2) catch unreachable;
+ args[0] = p.newExpr(E.String{ .data = "design:type" }, logger.Loc.Empty);
+ args[1] = p.serializeMetadata(prop.ts_metadata) catch unreachable;
+ array.append(p.callRuntime(loc, "__legacyMetadataTS", args)) catch unreachable;
+ }
+ {
+ // design:paramtypes and design:returntype if method
+ if (prop.flags.contains(.is_method)) {
+ if (prop.value) |prop_value| {
+ {
+ var args = p.allocator.alloc(Expr, 2) catch unreachable;
+ args[0] = p.newExpr(E.String{ .data = "design:paramtypes" }, logger.Loc.Empty);
+
+ const method_args = prop_value.data.e_function.func.args;
+
+ if (method_args.len > 0) {
+ var args_array = p.allocator.alloc(Expr, method_args.len) catch unreachable;
+
+ for (method_args, 0..) |method_arg, i| {
+ args_array[i] = p.serializeMetadata(method_arg.ts_metadata) catch unreachable;
+ }
+
+ args[1] = p.newExpr(E.Array{ .items = ExprNodeList.init(args_array) }, logger.Loc.Empty);
+ } else {
+ args[1] = p.newExpr(E.Array{ .items = ExprNodeList.init(&[_]Expr{}) }, logger.Loc.Empty);
+ }
+
+ array.append(p.callRuntime(loc, "__legacyMetadataTS", args)) catch unreachable;
+ }
+ {
+ var args = p.allocator.alloc(Expr, 2) catch unreachable;
+ args[0] = p.newExpr(E.String{ .data = "design:returntype" }, logger.Loc.Empty);
+
+ args[1] = p.serializeMetadata(prop_value.data.e_function.func.return_ts_metadata) catch unreachable;
+
+ array.append(p.callRuntime(loc, "__legacyMetadataTS", args)) catch unreachable;
+ }
+ }
+ }
+ }
+ }
+
const args = p.allocator.alloc(Expr, 4) catch unreachable;
- args[0] = p.newExpr(E.Array{ .items = prop.ts_decorators }, loc);
+ args[0] = p.newExpr(E.Array{ .items = ExprNodeList.init(array.items) }, loc);
args[1] = target;
args[2] = descriptor_key;
- args[3] = p.newExpr(E.Number{ .value = descriptor_kind }, loc);
+ args[3] = descriptor_kind;
- const decorator = p.callRuntime(prop.key.?.loc, "__decorateClass", args);
+ const decorator = p.callRuntime(prop.key.?.loc, "__legacyDecorateClassTS", args);
const decorator_stmt = p.s(S.SExpr{ .value = decorator }, decorator.loc);
if (prop.flags.contains(.is_static)) {
@@ -18916,13 +19428,38 @@ fn NewParser_(
stmts.appendSliceAssumeCapacity(instance_decorators.items);
stmts.appendSliceAssumeCapacity(static_decorators.items);
if (class.ts_decorators.len > 0) {
+ var array = class.ts_decorators.listManaged(p.allocator);
+
+ if (p.options.features.emit_decorator_metadata) {
+ if (constructor_function != null) {
+ // design:paramtypes
+ var args = p.allocator.alloc(Expr, 2) catch unreachable;
+ args[0] = p.newExpr(E.String{ .data = "design:paramtypes" }, logger.Loc.Empty);
+
+ const constructor_args = constructor_function.?.func.args;
+ if (constructor_args.len > 0) {
+ var param_array = p.allocator.alloc(Expr, constructor_args.len) catch unreachable;
+
+ for (constructor_args, 0..) |constructor_arg, i| {
+ param_array[i] = p.serializeMetadata(constructor_arg.ts_metadata) catch unreachable;
+ }
+
+ args[1] = p.newExpr(E.Array{ .items = ExprNodeList.init(param_array) }, logger.Loc.Empty);
+ } else {
+ args[1] = p.newExpr(E.Array{ .items = ExprNodeList.init(&[_]Expr{}) }, logger.Loc.Empty);
+ }
+
+ array.append(p.callRuntime(stmt.loc, "__legacyMetadataTS", args)) catch unreachable;
+ }
+ }
+
const args = p.allocator.alloc(Expr, 2) catch unreachable;
- args[0] = p.newExpr(E.Array{ .items = class.ts_decorators }, stmt.loc);
+ args[0] = p.newExpr(E.Array{ .items = ExprNodeList.init(array.items) }, stmt.loc);
args[1] = p.newExpr(E.Identifier{ .ref = class.class_name.?.ref.? }, class.class_name.?.loc);
stmts.appendAssumeCapacity(Stmt.assign(
p.newExpr(E.Identifier{ .ref = class.class_name.?.ref.? }, class.class_name.?.loc),
- p.callRuntime(stmt.loc, "__decorateClass", args),
+ p.callRuntime(stmt.loc, "__legacyDecorateClassTS", args),
p.allocator,
));
@@ -18939,6 +19476,234 @@ fn NewParser_(
}
}
+ fn serializeMetadata(p: *P, ts_metadata: TypeScript.Metadata) !Expr {
+ return switch (ts_metadata) {
+ .m_none,
+ .m_any,
+ .m_unknown,
+ .m_object,
+ => p.newExpr(
+ E.Identifier{
+ .ref = (p.findSymbol(logger.Loc.Empty, "Object") catch unreachable).ref,
+ },
+ logger.Loc.Empty,
+ ),
+
+ .m_never,
+ .m_undefined,
+ .m_null,
+ .m_void,
+ => p.newExpr(
+ E.Undefined{},
+ logger.Loc.Empty,
+ ),
+
+ .m_string => p.newExpr(
+ E.Identifier{
+ .ref = (p.findSymbol(logger.Loc.Empty, "String") catch unreachable).ref,
+ },
+ logger.Loc.Empty,
+ ),
+ .m_number => p.newExpr(
+ E.Identifier{
+ .ref = (p.findSymbol(logger.Loc.Empty, "Number") catch unreachable).ref,
+ },
+ logger.Loc.Empty,
+ ),
+ .m_function => p.newExpr(
+ E.Identifier{
+ .ref = (p.findSymbol(logger.Loc.Empty, "Function") catch unreachable).ref,
+ },
+ logger.Loc.Empty,
+ ),
+ .m_boolean => p.newExpr(
+ E.Identifier{
+ .ref = (p.findSymbol(logger.Loc.Empty, "Boolean") catch unreachable).ref,
+ },
+ logger.Loc.Empty,
+ ),
+ .m_array => p.newExpr(
+ E.Identifier{
+ .ref = (p.findSymbol(logger.Loc.Empty, "Array") catch unreachable).ref,
+ },
+ logger.Loc.Empty,
+ ),
+
+ .m_bigint => p.maybeDefinedHelper(
+ p.newExpr(
+ E.Identifier{
+ .ref = (p.findSymbol(logger.Loc.Empty, "BigInt") catch unreachable).ref,
+ },
+ logger.Loc.Empty,
+ ),
+ ),
+
+ .m_symbol => p.maybeDefinedHelper(
+ p.newExpr(
+ E.Identifier{
+ .ref = (p.findSymbol(logger.Loc.Empty, "Symbol") catch unreachable).ref,
+ },
+ logger.Loc.Empty,
+ ),
+ ),
+
+ .m_promise => p.newExpr(
+ E.Identifier{
+ .ref = (p.findSymbol(logger.Loc.Empty, "Promise") catch unreachable).ref,
+ },
+ logger.Loc.Empty,
+ ),
+
+ .m_identifier => |ref| {
+ p.recordUsage(ref);
+ if (p.is_import_item.contains(ref)) {
+ return p.maybeDefinedHelper(p.newExpr(
+ E.ImportIdentifier{
+ .ref = ref,
+ },
+ logger.Loc.Empty,
+ ));
+ }
+
+ return p.maybeDefinedHelper(p.newExpr(
+ E.Identifier{ .ref = ref },
+ logger.Loc.Empty,
+ ));
+ },
+
+ .m_dot => |_refs| {
+ var refs = _refs;
+ std.debug.assert(refs.items.len >= 2);
+ defer refs.deinit(p.allocator);
+
+ var dots = p.newExpr(
+ E.Dot{
+ .name = p.loadNameFromRef(refs.items[refs.items.len - 1]),
+ .name_loc = logger.Loc.Empty,
+ .target = undefined,
+ },
+ logger.Loc.Empty,
+ );
+
+ var current_expr = &dots.data.e_dot.target;
+ var i: usize = refs.items.len - 2;
+ while (i > 0) {
+ current_expr.* = p.newExpr(E.Dot{
+ .name = p.loadNameFromRef(refs.items[i]),
+ .name_loc = logger.Loc.Empty,
+ .target = undefined,
+ }, logger.Loc.Empty);
+ current_expr = &current_expr.data.e_dot.target;
+ i -= 1;
+ }
+
+ if (p.is_import_item.contains(refs.items[0])) {
+ current_expr.* = p.newExpr(
+ E.ImportIdentifier{
+ .ref = refs.items[0],
+ },
+ logger.Loc.Empty,
+ );
+ } else {
+ current_expr.* = p.newExpr(
+ E.Identifier{
+ .ref = refs.items[0],
+ },
+ logger.Loc.Empty,
+ );
+ }
+
+ const dot_identifier = current_expr.*;
+ var current_dot = dots;
+
+ var maybe_defined_dots = p.newExpr(
+ E.Binary{
+ .op = .bin_logical_or,
+ .right = try p.checkIfDefinedHelper(current_dot),
+ .left = undefined,
+ },
+ logger.Loc.Empty,
+ );
+
+ if (i < refs.items.len - 2) {
+ current_dot = current_dot.data.e_dot.target;
+ }
+ current_expr = &maybe_defined_dots.data.e_binary.left;
+
+ while (i < refs.items.len - 2) {
+ current_expr.* = p.newExpr(
+ E.Binary{
+ .op = .bin_logical_or,
+ .right = try p.checkIfDefinedHelper(current_dot),
+ .left = undefined,
+ },
+ logger.Loc.Empty,
+ );
+
+ current_expr = &current_expr.data.e_binary.left;
+ i += 1;
+ if (i < refs.items.len - 2) {
+ current_dot = current_dot.data.e_dot.target;
+ }
+ }
+
+ current_expr.* = try p.checkIfDefinedHelper(dot_identifier);
+
+ var root = p.newExpr(
+ E.If{
+ .yes = p.newExpr(
+ E.Identifier{
+ .ref = (p.findSymbol(logger.Loc.Empty, "Object") catch unreachable).ref,
+ },
+ logger.Loc.Empty,
+ ),
+ .no = dots,
+ .test_ = maybe_defined_dots,
+ },
+ logger.Loc.Empty,
+ );
+
+ return root;
+ },
+ };
+ }
+
+ fn checkIfDefinedHelper(p: *P, expr: Expr) !Expr {
+ return p.newExpr(
+ E.Binary{
+ .op = .bin_strict_eq,
+ .left = p.newExpr(
+ E.Unary{
+ .op = .un_typeof,
+ .value = expr,
+ },
+ logger.Loc.Empty,
+ ),
+ .right = p.newExpr(
+ E.String{ .data = "undefined" },
+ logger.Loc.Empty,
+ ),
+ },
+ logger.Loc.Empty,
+ );
+ }
+
+ fn maybeDefinedHelper(p: *P, identifier_expr: Expr) !Expr {
+ return p.newExpr(
+ E.If{
+ .test_ = try p.checkIfDefinedHelper(identifier_expr),
+ .yes = p.newExpr(
+ E.Identifier{
+ .ref = (p.findSymbol(logger.Loc.Empty, "Object") catch unreachable).ref,
+ },
+ logger.Loc.Empty,
+ ),
+ .no = identifier_expr,
+ },
+ logger.Loc.Empty,
+ );
+ }
+
fn visitForLoopInit(p: *P, stmt: Stmt, is_in_or_of: bool) Stmt {
switch (stmt.data) {
.s_expr => |st| {
@@ -19713,7 +20478,7 @@ fn NewParser_(
if (p.is_control_flow_dead) {
var end: usize = 0;
for (visited.items) |item| {
- if (!SideEffects.shouldKeepStmtInDeadControlFlow(item, p.allocator)) {
+ if (!SideEffects.shouldKeepStmtInDeadControlFlow(p, item, p.allocator)) {
continue;
}
@@ -19802,7 +20567,9 @@ fn NewParser_(
var output = ListManaged(Stmt).initCapacity(p.allocator, stmts.items.len) catch unreachable;
for (stmts.items) |stmt| {
- if (is_control_flow_dead and !SideEffects.shouldKeepStmtInDeadControlFlow(stmt, p.allocator)) {
+ if (is_control_flow_dead and p.options.features.dead_code_elimination and
+ !SideEffects.shouldKeepStmtInDeadControlFlow(p, stmt, p.allocator))
+ {
// Strip unnecessary statements if the control flow is dead here
continue;
}
@@ -20474,42 +21241,50 @@ fn NewParser_(
parts[parts.len - 1].stmts = new_stmts_list;
},
- // This becomes
+ // This transforms the user's code into.
//
- // (function (module, exports, require) {
+ // (function (exports, require, module, __filename, __dirname) {
+ // ...
+ // }).call(
+ // this.module.exports,
+ // this.module.exports,
+ // this.require,
+ // this.module,
+ // this.__filename,
+ // this.__dirname,
+ // );
//
- // })(module, exports, require);
+ // `this` is a `CommonJSFunctionArgumentsStructure`
+ // which is initialized in `evaluateCommonJSModuleOnce`
.bun_js => {
var args = allocator.alloc(Arg, 5) catch unreachable;
args[0..5].* = .{
- Arg{
- .binding = p.b(B.Identifier{ .ref = p.module_ref }, logger.Loc.Empty),
- },
- Arg{
- .binding = p.b(B.Identifier{ .ref = p.exports_ref }, logger.Loc.Empty),
- },
- Arg{
- .binding = p.b(B.Identifier{ .ref = p.require_ref }, logger.Loc.Empty),
- },
- Arg{
- .binding = p.b(B.Identifier{ .ref = p.dirname_ref }, logger.Loc.Empty),
- },
- Arg{
- .binding = p.b(B.Identifier{ .ref = p.filename_ref }, logger.Loc.Empty),
- },
+ Arg{ .binding = p.b(B.Identifier{ .ref = p.exports_ref }, logger.Loc.Empty) },
+ Arg{ .binding = p.b(B.Identifier{ .ref = p.require_ref }, logger.Loc.Empty) },
+ Arg{ .binding = p.b(B.Identifier{ .ref = p.module_ref }, logger.Loc.Empty) },
+ Arg{ .binding = p.b(B.Identifier{ .ref = p.filename_ref }, logger.Loc.Empty) },
+ Arg{ .binding = p.b(B.Identifier{ .ref = p.dirname_ref }, logger.Loc.Empty) },
+ };
+
+ const cjsArguments = Expr{
+ .data = .{ .e_this = .{} },
+ .loc = logger.Loc.Empty,
};
+
var total_stmts_count: usize = 0;
for (parts) |part| {
total_stmts_count += part.stmts.len;
}
var stmts_to_copy = allocator.alloc(Stmt, total_stmts_count) catch unreachable;
- var remaining_stmts = stmts_to_copy;
- for (parts) |part| {
- for (part.stmts, remaining_stmts[0..part.stmts.len]) |src, *dest| {
- dest.* = src;
+ {
+ var remaining_stmts = stmts_to_copy;
+ for (parts) |part| {
+ for (part.stmts, remaining_stmts[0..part.stmts.len]) |src, *dest| {
+ dest.* = src;
+ }
+ remaining_stmts = remaining_stmts[part.stmts.len..];
}
- remaining_stmts = remaining_stmts[part.stmts.len..];
}
const wrapper = p.newExpr(
@@ -20517,147 +21292,57 @@ fn NewParser_(
.func = G.Fn{
.name = null,
.open_parens_loc = logger.Loc.Empty,
- .args = args,
+ .args = args[0..5],
.body = .{ .loc = logger.Loc.Empty, .stmts = stmts_to_copy },
.flags = Flags.Function.init(.{ .is_export = false }),
},
},
logger.Loc.Empty,
);
- const cjsGlobal = p.newSymbol(.unbound, "$_BunCommonJSModule_$") catch unreachable;
- var all_call_args = allocator.alloc(Expr, 8) catch unreachable;
+
const this_module = p.newExpr(
E.Dot{
.name = "module",
- .target = p.newExpr(E.Identifier{ .ref = cjsGlobal }, logger.Loc.Empty),
+ .target = cjsArguments,
.name_loc = logger.Loc.Empty,
},
logger.Loc.Empty,
);
- var bind_args = all_call_args[0..1];
- bind_args[0] = this_module;
- var bind_resolve_args = all_call_args[1..2];
- var call_args = all_call_args[2..];
-
- const module_id = p.newExpr(E.Dot{
- .name = "id",
- .target = this_module,
- .name_loc = logger.Loc.Empty,
- }, logger.Loc.Empty);
-
- bind_resolve_args[0] = module_id;
- const get_require = p.newExpr(
+ const module_exports = p.newExpr(
E.Dot{
- .name = "require",
+ .name = "exports",
.target = this_module,
.name_loc = logger.Loc.Empty,
},
logger.Loc.Empty,
);
- const create_binding = p.newExpr(
- E.Call{
- .target = p.newExpr(E.Dot{
- .name = "bind",
- .name_loc = logger.Loc.Empty,
- .target = get_require,
- }, logger.Loc.Empty),
- .args = bun.BabyList(Expr).init(bind_args),
- },
- logger.Loc.Empty,
- );
-
- const get_resolve = p.newExpr(E.Dot{
- .name = "resolve",
- .name_loc = logger.Loc.Empty,
- .target = get_require,
- }, logger.Loc.Empty);
-
- const create_resolve_binding = p.newExpr(
- E.Call{
- .target = p.newExpr(E.Dot{
- .name = "bind",
- .name_loc = logger.Loc.Empty,
- .target = get_resolve,
- }, logger.Loc.Empty),
- .args = bun.BabyList(Expr).init(bind_resolve_args),
- },
- logger.Loc.Empty,
- );
-
- const require_path = p.newExpr(
- E.Dot{
- .name = "path",
- .target = get_require,
- .name_loc = logger.Loc.Empty,
- },
- logger.Loc.Empty,
- );
- const assign_binding = p.newExpr(
- E.Binary{
- .left = get_require,
- .right = create_binding,
- .op = .bin_assign,
- },
- logger.Loc.Empty,
- );
-
- const assign_resolve_binding = p.newExpr(
- E.Binary{
- .left = get_resolve,
- .right = create_resolve_binding,
- .op = .bin_assign,
- },
- logger.Loc.Empty,
- );
-
- const assign_id = p.newExpr(E.Binary{
- .left = require_path,
- .right = module_id,
- .op = .bin_assign,
- }, logger.Loc.Empty);
-
- var create_require = [4]Expr{
- assign_binding,
- assign_id,
- assign_resolve_binding,
- get_require,
- };
-
- //
- // (function(module, exports, require, __dirname, __filename) {}).call(this.exports, this.module, this.exports, this.module.require = this.module.require.bind(module), (this.module.require.id = this.module.id, this.module.require), __dirname, __filename)
+ var call_args = allocator.alloc(Expr, 6) catch unreachable;
call_args[0..6].* = .{
+ module_exports, // this.module.exports (this value inside fn)
+ module_exports, // this.module.exports (arg 1)
p.newExpr(
E.Dot{
- .name = "exports",
- .target = this_module,
+ .name = "require",
+ .target = cjsArguments,
.name_loc = logger.Loc.Empty,
},
logger.Loc.Empty,
),
- this_module,
+ this_module, // this.module
p.newExpr(
E.Dot{
- .name = "exports",
- .target = this_module,
+ .name = "__filename",
+ .target = cjsArguments,
.name_loc = logger.Loc.Empty,
},
logger.Loc.Empty,
),
- Expr.joinAllWithComma(&create_require, p.allocator),
p.newExpr(
E.Dot{
.name = "__dirname",
- .target = p.newExpr(E.Identifier{ .ref = cjsGlobal }, logger.Loc.Empty),
- .name_loc = logger.Loc.Empty,
- },
- logger.Loc.Empty,
- ),
- p.newExpr(
- E.Dot{
- .name = "__filename",
- .target = p.newExpr(E.Identifier{ .ref = cjsGlobal }, logger.Loc.Empty),
+ .target = cjsArguments,
.name_loc = logger.Loc.Empty,
},
logger.Loc.Empty,
@@ -20679,14 +21364,50 @@ fn NewParser_(
logger.Loc.Empty,
);
- var only_stmt = try p.allocator.alloc(Stmt, 1);
- only_stmt[0] = p.s(
+ var top_level_stmts = p.allocator.alloc(Stmt, 1 + @as(usize, @intFromBool(p.has_import_meta))) catch unreachable;
+ parts[0].stmts = top_level_stmts;
+
+ // var $Bun_import_meta = this.createImportMeta(this.filename);
+ if (p.has_import_meta) {
+ p.import_meta_ref = p.newSymbol(.other, "$Bun_import_meta") catch unreachable;
+ var decl = allocator.alloc(Decl, 1) catch unreachable;
+ decl[0] = Decl{
+ .binding = Binding.alloc(
+ p.allocator,
+ B.Identifier{
+ .ref = p.import_meta_ref,
+ },
+ logger.Loc.Empty,
+ ),
+ .value = p.newExpr(
+ E.Call{
+ .target = p.newExpr(E.Dot{
+ .target = cjsArguments,
+ .name = "createImportMeta",
+ .name_loc = logger.Loc.Empty,
+ }, logger.Loc.Empty),
+ // reuse the `this.__filename` argument
+ .args = ExprNodeList.init(call_args[5..6]),
+ },
+ logger.Loc.Empty,
+ ),
+ };
+
+ top_level_stmts[0] = p.s(
+ S.Local{
+ .decls = G.Decl.List.init(decl),
+ .kind = .k_var,
+ },
+ logger.Loc.Empty,
+ );
+ top_level_stmts = top_level_stmts[1..];
+ }
+ top_level_stmts[0] = p.s(
S.SExpr{
.value = call,
},
logger.Loc.Empty,
);
- parts[0].stmts = only_stmt;
parts.len = 1;
},
@@ -21217,6 +21938,8 @@ fn NewParser_(
// TODO:
// .const_values = p.const_values,
+
+ .import_meta_ref = p.import_meta_ref,
};
}