diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/bun.js/module_loader.zig | 2 | ||||
-rw-r--r-- | src/bundler.zig | 16 | ||||
-rw-r--r-- | src/bundler/bundle_v2.zig | 3 | ||||
-rw-r--r-- | src/js_ast.zig | 7 | ||||
-rw-r--r-- | src/js_parser.zig | 890 | ||||
-rw-r--r-- | src/options.zig | 1 | ||||
-rw-r--r-- | src/resolver/resolver.zig | 4 | ||||
-rw-r--r-- | src/resolver/tsconfig_json.zig | 9 | ||||
-rw-r--r-- | src/runtime.footer.bun.js | 5 | ||||
-rw-r--r-- | src/runtime.footer.js | 5 | ||||
-rw-r--r-- | src/runtime.footer.node.js | 5 | ||||
-rw-r--r-- | src/runtime.footer.with-refresh.js | 5 | ||||
-rw-r--r-- | src/runtime.js | 22 | ||||
-rw-r--r-- | src/runtime.zig | 18 |
14 files changed, 882 insertions, 110 deletions
diff --git a/src/bun.js/module_loader.zig b/src/bun.js/module_loader.zig index db8df00c2..135bd5e94 100644 --- a/src/bun.js/module_loader.zig +++ b/src/bun.js/module_loader.zig @@ -402,6 +402,7 @@ pub const RuntimeTranspilerStore = struct { .file_hash = hash, .macro_remappings = macro_remappings, .jsx = bundler.options.jsx, + .emit_decorator_metadata = bundler.options.emit_decorator_metadata, .virtual_source = null, .dont_bundle_twice = true, .allow_commonjs = true, @@ -1427,6 +1428,7 @@ pub const ModuleLoader = struct { .file_hash = hash, .macro_remappings = macro_remappings, .jsx = jsc_vm.bundler.options.jsx, + .emit_decorator_metadata = jsc_vm.bundler.options.emit_decorator_metadata, .virtual_source = virtual_source, .dont_bundle_twice = true, .allow_commonjs = true, diff --git a/src/bundler.zig b/src/bundler.zig index 76fbc02ee..15089e241 100644 --- a/src/bundler.zig +++ b/src/bundler.zig @@ -435,13 +435,14 @@ pub const Bundler = struct { ); if (auto_jsx) { - // If we don't explicitly pass JSX, try to get it from the root tsconfig - if (bundler.options.transform_options.jsx == null) { - // Most of the time, this will already be cached - if (bundler.resolver.readDirInfo(bundler.fs.top_level_dir) catch null) |root_dir| { - if (root_dir.tsconfig_json) |tsconfig| { + // Most of the time, this will already be cached + if (bundler.resolver.readDirInfo(bundler.fs.top_level_dir) catch null) |root_dir| { + if (root_dir.tsconfig_json) |tsconfig| { + // If we don't explicitly pass JSX, try to get it from the root tsconfig + if (bundler.options.transform_options.jsx == null) { bundler.options.jsx = tsconfig.jsx; } + bundler.options.emit_decorator_metadata = tsconfig.emit_decorator_metadata; } } } @@ -780,6 +781,7 @@ pub const Bundler = struct { .file_descriptor = file_descriptor, .file_hash = filepath_hash, .macro_remappings = bundler.options.macro_remap, + .emit_decorator_metadata = resolve_result.emit_decorator_metadata, .jsx = resolve_result.jsx, }, client_entry_point, @@ -899,6 +901,7 @@ pub const Bundler = struct { .file_hash = null, .macro_remappings = bundler.options.macro_remap, .jsx = resolve_result.jsx, + .emit_decorator_metadata = resolve_result.emit_decorator_metadata, }, client_entry_point_, ) orelse { @@ -1188,6 +1191,7 @@ pub const Bundler = struct { replace_exports: runtime.Runtime.Features.ReplaceableExport.Map = .{}, inject_jest_globals: bool = false, set_breakpoint_on_first_line: bool = false, + emit_decorator_metadata: bool = false, dont_bundle_twice: bool = false, allow_commonjs: bool = false, @@ -1300,7 +1304,9 @@ pub const Bundler = struct { jsx.parse = loader.isJSX(); var opts = js_parser.Parser.Options.init(jsx, loader); + opts.legacy_transform_require_to_import = bundler.options.allow_runtime and !bundler.options.target.isBun(); + opts.features.emit_decorator_metadata = this_parse.emit_decorator_metadata; opts.features.allow_runtime = bundler.options.allow_runtime; opts.features.set_breakpoint_on_first_line = this_parse.set_breakpoint_on_first_line; opts.features.trim_unused_imports = bundler.options.trim_unused_imports orelse loader.isTypeScript(); diff --git a/src/bundler/bundle_v2.zig b/src/bundler/bundle_v2.zig index 359272e5f..e5b73814c 100644 --- a/src/bundler/bundle_v2.zig +++ b/src/bundler/bundle_v2.zig @@ -2276,6 +2276,7 @@ pub const ParseTask = struct { tree_shaking: bool = false, known_target: ?options.Target = null, module_type: options.ModuleType = .unknown, + emit_decorator_metadata: bool = false, ctx: *BundleV2, package_version: string = "", @@ -2298,6 +2299,7 @@ pub const ParseTask = struct { .jsx = resolve_result.jsx, .source_index = source_index orelse Index.invalid, .module_type = resolve_result.module_type, + .emit_decorator_metadata = resolve_result.emit_decorator_metadata, .package_version = if (resolve_result.package_json) |package_json| package_json.version else "", }; } @@ -2600,6 +2602,7 @@ pub const ParseTask = struct { opts.features.minify_syntax = bundler.options.minify_syntax; opts.features.minify_identifiers = bundler.options.minify_identifiers; opts.features.should_fold_typescript_constant_expressions = opts.features.inlining or loader.isTypeScript(); + opts.features.emit_decorator_metadata = bundler.options.emit_decorator_metadata; opts.tree_shaking = if (source.index.isRuntime()) true else bundler.options.tree_shaking; opts.module_type = task.module_type; diff --git a/src/js_ast.zig b/src/js_ast.zig index 7811541f4..b9e34d279 100644 --- a/src/js_ast.zig +++ b/src/js_ast.zig @@ -26,6 +26,7 @@ const is_bindgen = std.meta.globalOption("bindgen", bool) orelse false; const ComptimeStringMap = bun.ComptimeStringMap; const JSPrinter = @import("./js_printer.zig"); const js_lexer = @import("./js_lexer.zig"); +const TypeScript = @import("./js_parser.zig").TypeScript; const ThreadlocalArena = @import("./mimalloc_arena.zig").Arena; /// This is the index to the automatically-generated part containing code that @@ -834,6 +835,8 @@ pub const G = struct { // This is omitted for class fields value: ?ExprNodeIndex = null, + ts_metadata: TypeScript.Metadata = .m_none, + pub const List = BabyList(Property); pub const Kind = enum(u3) { @@ -865,6 +868,8 @@ pub const G = struct { arguments_ref: ?Ref = null, flags: Flags.Function.Set = Flags.Function.None, + + return_ts_metadata: TypeScript.Metadata = .m_none, }; pub const Arg = struct { ts_decorators: ExprNodeList = ExprNodeList{}, @@ -873,6 +878,8 @@ pub const G = struct { // "constructor(public x: boolean) {}" is_typescript_ctor_field: bool = false, + + ts_metadata: TypeScript.Metadata = .m_none, }; }; diff --git a/src/js_parser.zig b/src/js_parser.zig index a61657128..a9f346caf 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"); @@ -2437,6 +2600,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 +2737,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 +4469,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, @@ -6737,7 +6902,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; @@ -6765,6 +6930,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 +6970,11 @@ fn NewParser_( // "function foo(a: any) {}" if (p.lexer.token == .t_colon) { try p.lexer.next(); - try p.skipTypeScriptType(.lowest); + 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); + } } } @@ -6825,6 +6995,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) { @@ -6864,9 +7035,18 @@ 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 (func.flags.contains(.is_async) and p.options.features.emit_decorator_metadata and opts.allow_ts_decorators and (opts.has_argument_decorators or opts.has_decorators)) { + func.return_ts_metadata = .m_promise; + } } // "function foo(): any;" @@ -6881,10 +7061,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 +7097,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 +7250,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 +7334,10 @@ fn NewParser_( try p.skipTypeScriptType(.lowest); return; } + + if (comptime get_metadata) { + result.* = .m_object; + } }, .t_minus => { // "-123" @@ -7113,8 +7346,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 +7366,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 +7394,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 +7416,7 @@ fn NewParser_( var check_type_parameters = true; switch (kind) { - .prefix => { + .prefix_keyof => { try p.lexer.next(); // Valid: @@ -7188,10 +7427,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 +7458,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 +7489,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 +7593,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 +7633,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 +7659,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 +7675,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 +7707,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 +7730,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 +7762,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 +7791,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 +7870,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 +11541,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 +11599,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 +12039,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 +12058,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 +12116,7 @@ fn NewParser_( }), .key = key, .initializer = initializer, + .ts_metadata = ts_metadata, }; } @@ -11739,6 +12163,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 +12241,7 @@ fn NewParser_( }), .key = key, .value = value, + .ts_metadata = .m_function, }; } @@ -11895,6 +12321,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{}; @@ -18123,7 +18550,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; @@ -18765,7 +19192,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 +19219,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 +19234,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 +19395,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 +19443,220 @@ fn NewParser_( } } + fn serializeMetadata(p: *P, ts_metadata: TypeScript.Metadata) !Expr { + return switch (ts_metadata) { + .m_none => p.newExpr( + E.Undefined{}, + logger.Loc.Empty, + ), + + .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); + 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 = ¤t_expr.data.e_dot.target; + i -= 1; + } + + 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 = ¤t_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| { diff --git a/src/options.zig b/src/options.zig index aaeb1fce8..73ca9a64b 100644 --- a/src/options.zig +++ b/src/options.zig @@ -1364,6 +1364,7 @@ pub const BundleOptions = struct { loaders: Loader.HashTable, resolve_dir: string = "/", jsx: JSX.Pragma = JSX.Pragma{}, + emit_decorator_metadata: bool = false, auto_import_jsx: bool = true, allow_runtime: bool = true, diff --git a/src/resolver/resolver.zig b/src/resolver/resolver.zig index b6f571e23..4ddd97dca 100644 --- a/src/resolver/resolver.zig +++ b/src/resolver/resolver.zig @@ -187,6 +187,8 @@ pub const Result = struct { // This is the "type" field from "package.json" module_type: options.ModuleType = options.ModuleType.unknown, + emit_decorator_metadata: bool = false, + debug_meta: ?DebugMeta = null, dirname_fd: StoredFileDescriptorType = 0, @@ -965,6 +967,7 @@ pub const Resolver = struct { if (dir.enclosing_tsconfig_json) |tsconfig| { result.jsx = tsconfig.mergeJSX(result.jsx); + result.emit_decorator_metadata = result.emit_decorator_metadata or tsconfig.emit_decorator_metadata; } // If you use mjs or mts, then you're using esm @@ -3904,6 +3907,7 @@ pub const Resolver = struct { // starting from the base config (end of the list) // successively apply the inheritable attributes to the next config while (parent_configs.popOrNull()) |parent_config| { + merged_config.emit_decorator_metadata = merged_config.emit_decorator_metadata or parent_config.emit_decorator_metadata; if (parent_config.base_url.len > 0) { merged_config.base_url = parent_config.base_url; merged_config.base_url_for_paths = parent_config.base_url_for_paths; diff --git a/src/resolver/tsconfig_json.zig b/src/resolver/tsconfig_json.zig index 3f7be00ba..ba85fc965 100644 --- a/src/resolver/tsconfig_json.zig +++ b/src/resolver/tsconfig_json.zig @@ -56,6 +56,8 @@ pub const TSConfigJSON = struct { preserve_imports_not_used_as_values: ?bool = false, + emit_decorator_metadata: bool = false, + pub fn hasBaseURL(tsconfig: *const TSConfigJSON) bool { return tsconfig.base_url.len > 0; } @@ -137,6 +139,13 @@ pub const TSConfigJSON = struct { } } + // Parse "emitDecoratorMetadata" + if (compiler_opts.expr.asProperty("emitDecoratorMetadata")) |emit_decorator_metadata_prop| { + if (emit_decorator_metadata_prop.expr.asBool()) |val| { + result.emit_decorator_metadata = val; + } + } + // Parse "jsxFactory" if (compiler_opts.expr.asProperty("jsxFactory")) |jsx_prop| { if (jsx_prop.expr.asString(allocator)) |str| { diff --git a/src/runtime.footer.bun.js b/src/runtime.footer.bun.js index fdf7cdefa..810525af7 100644 --- a/src/runtime.footer.bun.js +++ b/src/runtime.footer.bun.js @@ -11,8 +11,9 @@ export var regeneratorRuntime = BUN_RUNTIME.regeneratorRuntime; export var __exportValue = BUN_RUNTIME.__exportValue; export var __exportDefault = BUN_RUNTIME.__exportDefault; export var __merge = BUN_RUNTIME.__merge; -export var __decorateClass = BUN_RUNTIME.__decorateClass; -export var __decorateParam = BUN_RUNTIME.__decorateParam; +export var __legacyDecorateClassTS = BUN_RUNTIME.__legacyDecorateClassTS; +export var __legacyDecorateParamTS = BUN_RUNTIME.__legacyDecorateParamTS; +export var __legacyMetadataTS = BUN_RUNTIME.__legacyMetadataTS; export var $$bun_runtime_json_parse = JSON.parse; export var __internalIsCommonJSNamespace = BUN_RUNTIME.__internalIsCommonJSNamespace; export var $$typeof = BUN_RUNTIME.$$typeof; diff --git a/src/runtime.footer.js b/src/runtime.footer.js index ceeab055d..48c86f47b 100644 --- a/src/runtime.footer.js +++ b/src/runtime.footer.js @@ -18,8 +18,9 @@ export var regeneratorRuntime = BUN_RUNTIME.regeneratorRuntime; export var __exportValue = BUN_RUNTIME.__exportValue; export var __exportDefault = BUN_RUNTIME.__exportDefault; export var __merge = BUN_RUNTIME.__merge; -export var __decorateClass = BUN_RUNTIME.__decorateClass; -export var __decorateParam = BUN_RUNTIME.__decorateParam; +export var __legacyDecorateClassTS = BUN_RUNTIME.__legacyDecorateClassTS; +export var __legacyDecorateParamTS = BUN_RUNTIME.__legacyDecorateParamTS; +export var __legacyMetadataTS = BUN_RUNTIME.__legacyMetadataTS; export var $$bun_runtime_json_parse = JSON.parse; export var __internalIsCommonJSNamespace = BUN_RUNTIME.__internalIsCommonJSNamespace; diff --git a/src/runtime.footer.node.js b/src/runtime.footer.node.js index ef28d3b31..4318195c5 100644 --- a/src/runtime.footer.node.js +++ b/src/runtime.footer.node.js @@ -12,8 +12,9 @@ export var __cJS2eSM = BUN_RUNTIME.__cJS2eSM; export var regeneratorRuntime = BUN_RUNTIME.regeneratorRuntime; export var __exportValue = BUN_RUNTIME.__exportValue; export var __exportDefault = BUN_RUNTIME.__exportDefault; -export var __decorateClass = BUN_RUNTIME.__decorateClass; -export var __decorateParam = BUN_RUNTIME.__decorateParam; +export var __legacyDecorateClassTS = BUN_RUNTIME.__legacyDecorateClassTS; +export var __legacyDecorateParamTS = BUN_RUNTIME.__legacyDecorateParamTS; +export var __legacyMetadataTS = BUN_RUNTIME.__legacyMetadataTS; export var $$bun_runtime_json_parse = JSON.parse; export var __internalIsCommonJSNamespace = BUN_RUNTIME.__internalIsCommonJSNamespace; var require = __$module.createRequire(import.meta.url); diff --git a/src/runtime.footer.with-refresh.js b/src/runtime.footer.with-refresh.js index 784f5f8fa..9aa9472e9 100644 --- a/src/runtime.footer.with-refresh.js +++ b/src/runtime.footer.with-refresh.js @@ -17,8 +17,9 @@ export var __cJS2eSM = BUN_RUNTIME.__cJS2eSM; export var regeneratorRuntime = BUN_RUNTIME.regeneratorRuntime; export var __exportValue = BUN_RUNTIME.__exportValue; export var __exportDefault = BUN_RUNTIME.__exportDefault; -export var __decorateClass = BUN_RUNTIME.__decorateClass; -export var __decorateParam = BUN_RUNTIME.__decorateParam; +export var __legacyDecorateClassTS = BUN_RUNTIME.__legacyDecorateClassTS; +export var __legacyDecorateParamTS = BUN_RUNTIME.__legacyDecorateParamTS; +export var __legacyMetadataTS = BUN_RUNTIME.__legacyMetadataTS; export var $$bun_runtime_json_parse = JSON.parse; export var __FastRefreshRuntime = BUN_RUNTIME.__FastRefreshRuntime; export var __internalIsCommonJSNamespace = BUN_RUNTIME.__internalIsCommonJSNamespace; diff --git a/src/runtime.js b/src/runtime.js index 41242637b..fb1d5e70e 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -250,15 +250,23 @@ export var __merge = (props, defaultProps) => { : mergeDefaultProps(props, defaultProps); }; -export var __decorateClass = (decorators, target, key, kind) => { - var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target; - for (var i = decorators.length - 1, decorator; i >= 0; i--) - if ((decorator = decorators[i])) result = (kind ? decorator(target, key, result) : decorator(result)) || result; - if (kind && result) __defProp(target, key, result); - return result; +export var __legacyDecorateClassTS = function (decorators, target, key, desc) { + var c = arguments.length, + r = c < 3 ? target : desc === null ? (desc = Object.getOwnPropertyDescriptor(target, key)) : desc, + d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") + r = Reflect.decorate(decorators, target, key, desc); + else + for (var i = decorators.length - 1; i >= 0; i--) + if ((d = decorators[i])) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; }; -export var __decorateParam = (index, decorator) => (target, key) => decorator(target, key, index); +export var __legacyDecorateParamTS = (index, decorator) => (target, key) => decorator(target, key, index); + +export var __legacyMetadataTS = (k, v) => { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); +}; export var __esm = (fn, res) => () => (fn && (res = fn((fn = 0))), res); diff --git a/src/runtime.zig b/src/runtime.zig index cf9f1d208..a9a88f979 100644 --- a/src/runtime.zig +++ b/src/runtime.zig @@ -328,6 +328,8 @@ pub const Runtime = struct { commonjs_at_runtime: bool = false, + emit_decorator_metadata: bool = false, + pub fn shouldUnwrapRequire(this: *const Features, package_name: string) bool { return package_name.len > 0 and strings.indexEqualAny(this.unwrap_commonjs_packages, package_name) != null; } @@ -372,8 +374,9 @@ pub const Runtime = struct { __exportDefault: ?GeneratedSymbol = null, __FastRefreshRuntime: ?GeneratedSymbol = null, __merge: ?GeneratedSymbol = null, - __decorateClass: ?GeneratedSymbol = null, - __decorateParam: ?GeneratedSymbol = null, + __legacyDecorateClassTS: ?GeneratedSymbol = null, + __legacyDecorateParamTS: ?GeneratedSymbol = null, + __legacyMetadataTS: ?GeneratedSymbol = null, @"$$typeof": ?GeneratedSymbol = null, pub const all = [_][]const u8{ @@ -395,8 +398,9 @@ pub const Runtime = struct { "__exportDefault", "__FastRefreshRuntime", "__merge", - "__decorateClass", - "__decorateParam", + "__legacyDecorateClassTS", + "__legacyDecorateParamTS", + "__legacyMetadataTS", "$$typeof", }; const all_sorted: [all.len]string = brk: { @@ -540,6 +544,11 @@ pub const Runtime = struct { return Entry{ .key = 18, .value = val.ref }; } }, + 19 => { + if (@field(this.runtime_imports, all[19])) |val| { + return Entry{ .key = 19, .value = val.ref }; + } + }, else => { return null; }, @@ -603,6 +612,7 @@ pub const Runtime = struct { 16 => (@field(imports, all[16]) orelse return null).ref, 17 => (@field(imports, all[17]) orelse return null).ref, 18 => (@field(imports, all[18]) orelse return null).ref, + 19 => (@field(imports, all[19]) orelse return null).ref, else => null, }; } |