aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Dylan Conway <35280289+dylan-conway@users.noreply.github.com> 2023-09-20 08:10:03 -0700
committerGravatar GitHub <noreply@github.com> 2023-09-20 08:10:03 -0700
commit689b28455c943897a3d286271e6f182afb17848f (patch)
tree470132b70476e7ead623b4945ebaa72c27dfe839
parent4439f1615571f3558704a7b48cfa84273be4221d (diff)
downloadbun-689b28455c943897a3d286271e6f182afb17848f.tar.gz
bun-689b28455c943897a3d286271e6f182afb17848f.tar.zst
bun-689b28455c943897a3d286271e6f182afb17848f.zip
add `emitDecoratorMetadata` (#5777)
* some progess * needs more tests * make tests easier to debug * get metadata for constructor arg decorators * fix some things * merge `emitDecoratorMetadata` option * remove `^` * bundler tests and get option from tsconfig earlier * remove spaces * fix tests
-rw-r--r--src/bun.js/module_loader.zig2
-rw-r--r--src/bundler.zig16
-rw-r--r--src/bundler/bundle_v2.zig3
-rw-r--r--src/js_ast.zig7
-rw-r--r--src/js_parser.zig890
-rw-r--r--src/options.zig1
-rw-r--r--src/resolver/resolver.zig4
-rw-r--r--src/resolver/tsconfig_json.zig9
-rw-r--r--src/runtime.footer.bun.js5
-rw-r--r--src/runtime.footer.js5
-rw-r--r--src/runtime.footer.node.js5
-rw-r--r--src/runtime.footer.with-refresh.js5
-rw-r--r--src/runtime.js22
-rw-r--r--src/runtime.zig18
-rw-r--r--test/bundler/bundler_decorator_metadata.test.ts505
-rw-r--r--test/package.json1
-rw-r--r--test/transpiler/decorator-metadata.test.ts494
-rw-r--r--test/tsconfig.json1
-rw-r--r--tsconfig.json1
19 files changed, 1884 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 = &current_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 = &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| {
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,
};
}
diff --git a/test/bundler/bundler_decorator_metadata.test.ts b/test/bundler/bundler_decorator_metadata.test.ts
new file mode 100644
index 000000000..54bbdd6a5
--- /dev/null
+++ b/test/bundler/bundler_decorator_metadata.test.ts
@@ -0,0 +1,505 @@
+import assert from "assert";
+import { itBundled, testForFile } from "./expectBundled";
+var { describe, test, expect } = testForFile(import.meta.path);
+
+itBundled("decorator_metadata/TypeSerialization", {
+ files: {
+ "/entry.ts": /* ts */ `
+ import "reflect-metadata";
+ function d1() {}
+ class Known {}
+ class Swag {}
+ class A_1 {}
+
+ // @ts-ignore
+ @d1
+ class Yolo {
+ constructor(
+ p0: any,
+ p1: unknown,
+ p2: never,
+ p3: void,
+ p4: null,
+ p5: undefined,
+ p6: number,
+ p7: string,
+ p8: boolean,
+ p9: symbol,
+ p10: bigint,
+ p11: object,
+ p12: () => {},
+ p13: [],
+ p14: {},
+ p15: 123,
+ p16: 123n,
+ p17: "123",
+ p18: \`123\`,
+ p19: true,
+ p20: false,
+ // @ts-ignore
+ p21: Map,
+ // @ts-ignore
+ p22: Set,
+ p23: Known,
+ // @ts-ignore
+ p24: Unknown,
+ p25: never & string,
+ p26: string & never,
+ p27: null & string,
+ p28: string & null,
+ p29: undefined & string,
+ p30: string & undefined,
+ p31: void & string,
+ p32: string & void,
+ p33: unknown & string,
+ p34: string & unknown,
+ p35: any & string,
+ p36: string & any,
+ p37: never | string,
+ p38: string | never,
+ p39: null | string,
+ p40: string | null,
+ p41: undefined | string,
+ p42: string | undefined,
+ p43: void | string,
+ p44: string | void,
+ p45: unknown | string,
+ p46: string | unknown,
+ p47: any | string,
+ p48: string | any,
+ p49: string | string,
+ p50: string & string,
+ p51: Known | Swag,
+ p52: Swag | Known,
+ p53: Known & Swag,
+ p54: Swag & Known,
+ p55: never | Swag,
+ p56: Swag | never,
+ p57: null | Swag,
+ p58: Swag | null,
+ p59: undefined | Swag,
+ p60: Swag | undefined,
+ p61: void | Swag,
+ p62: Swag | void,
+ p63: unknown | Swag,
+ p64: Swag | unknown,
+ p65: any | Swag,
+ p66: Swag | any,
+ p67: never & Swag,
+ p68: Swag & never,
+ p69: null & Swag,
+ p70: Swag & null,
+ p71: undefined & Swag,
+ p72: Swag & undefined,
+ p73: void & Swag,
+ p74: Swag & void,
+ p75: unknown & Swag,
+ p76: Swag & unknown,
+ p77: any & Swag,
+ p78: Swag & any,
+ p79: Swag | Swag,
+ p80: Swag & Swag,
+ // @ts-ignore
+ p81: Unknown | Known,
+ // @ts-ignore
+ p82: Known | Unknown,
+ // @ts-ignore
+ p83: Unknown & Known,
+ // @ts-ignore
+ p84: Known & Unknown,
+ // @ts-ignore
+ p85: Unknown | Unknown,
+ // @ts-ignore
+ p86: Unknown & Unknown,
+ p87: never | never,
+ p88: never & never,
+ p89: null | null,
+ p90: null & null,
+ p91: undefined | undefined,
+ p92: undefined & undefined,
+ p93: void | void,
+ p94: void & void,
+ p95: unknown | unknown,
+ p96: unknown & unknown,
+ p97: any | any,
+ p98: any & any,
+ p99: never | void,
+ p100: void | never,
+ p101: null | void,
+ p102: void | null,
+ p103: undefined | void,
+ p104: void | undefined,
+ p105: void | void,
+ p106: void & void,
+ p107: unknown | void,
+ p108: void | unknown,
+ p109: any | void,
+ p110: void | any,
+ p111: never | unknown,
+ p112: unknown | never,
+ p113: null | unknown,
+ p114: unknown | null,
+ p115: undefined | unknown,
+ p116: unknown | undefined,
+ p117: void | unknown,
+ p118: unknown | void,
+ p119: unknown | unknown,
+ p120: unknown & unknown,
+ p121: any | unknown,
+ p122: unknown | any,
+ p123: never | any,
+ p124: any | never,
+ p125: null | any,
+ p126: any | null,
+ p127: undefined | any,
+ p128: any | undefined,
+ p129: void | any,
+ p130: any | void,
+ p131: unknown | any,
+ p132: any | unknown,
+ p133: any | any,
+ p134: never & void,
+ p135: void & never,
+ p136: null & void,
+ p137: void & null,
+ p138: undefined & void,
+ p139: void & undefined,
+ p140: void & void,
+ p141: void | void,
+ p142: unknown & void,
+ p143: void & unknown,
+ p144: any & void,
+ p145: void & any,
+ p146: never & unknown,
+ p147: unknown & never,
+ p148: null & unknown,
+ p149: unknown & null,
+ p150: undefined & unknown,
+ p151: unknown & undefined,
+ p152: void & unknown,
+ p153: unknown & void,
+ p154: unknown & unknown,
+ p155: unknown | unknown,
+ p156: any & unknown,
+ p157: unknown & any,
+ p158: never & any,
+ p159: any & never,
+ p160: null & any,
+ p161: any & null,
+ p162: undefined & any,
+ p163: any & undefined,
+ p164: void & any,
+ p165: any & void,
+ p166: unknown & any,
+ p167: any & unknown,
+ p168: any & any,
+ p169: string & number & boolean & never & symbol,
+ p170: "foo" | A_1,
+ p171: true | boolean,
+ p172: "foo" | boolean,
+ p173: A_1 | "foo",
+ ){}
+ }
+
+ const received = Reflect.getMetadata("design:paramtypes", Yolo);
+ console.log(received.length === 174);
+ console.log(received[0] === Object);
+ console.log(received[1] === Object);
+ console.log(received[2] === void 0);
+ console.log(received[3] === void 0);
+ console.log(received[4] === void 0);
+ console.log(received[5] === void 0);
+ console.log(received[6] === Number);
+ console.log(received[7] === String);
+ console.log(received[8] === Boolean);
+ console.log(received[9] === (typeof Symbol === "function" ? Symbol : Object));
+ console.log(received[10] === (typeof BigInt === "function" ? BigInt : Object));
+ console.log(received[11] === Object);
+ console.log(received[12] === Function);
+ console.log(received[13] === Array);
+ console.log(received[14] === Object);
+ console.log(received[15] === Number);
+ console.log(received[16] === (typeof BigInt === "function" ? BigInt : Object));
+ console.log(received[17] === String);
+ console.log(received[18] === String);
+ console.log(received[19] === Boolean);
+ console.log(received[20] === Boolean);
+ console.log(received[21] === Map);
+ console.log(received[22] === Set);
+ console.log(received[23] === Known);
+ console.log(received[24] === Object);
+ console.log(received[25] === void 0);
+ console.log(received[26] === void 0);
+ console.log(received[27] === String);
+ console.log(received[28] === String);
+ console.log(received[29] === String);
+ console.log(received[30] === String);
+ console.log(received[31] === Object);
+ console.log(received[32] === Object);
+ console.log(received[33] === String);
+ console.log(received[34] === String);
+ console.log(received[35] === Object);
+ console.log(received[36] === Object);
+ console.log(received[37] === String);
+ console.log(received[38] === String);
+ console.log(received[39] === String);
+ console.log(received[40] === String);
+ console.log(received[41] === String);
+ console.log(received[42] === String);
+ console.log(received[43] === Object);
+ console.log(received[44] === Object);
+ console.log(received[45] === Object);
+ console.log(received[46] === Object);
+ console.log(received[47] === Object);
+ console.log(received[48] === Object);
+ console.log(received[49] === String);
+ console.log(received[50] === String);
+ console.log(received[51] === Object);
+ console.log(received[52] === Object);
+ console.log(received[53] === Object);
+ console.log(received[54] === Object);
+ console.log(received[55] === Swag);
+ console.log(received[56] === Swag);
+ console.log(received[57] === Swag);
+ console.log(received[58] === Swag);
+ console.log(received[59] === Swag);
+ console.log(received[60] === Swag);
+ console.log(received[61] === Object);
+ console.log(received[62] === Object);
+ console.log(received[63] === Object);
+ console.log(received[64] === Object);
+ console.log(received[65] === Object);
+ console.log(received[66] === Object);
+ console.log(received[67] === void 0);
+ console.log(received[68] === void 0);
+ console.log(received[69] === Swag);
+ console.log(received[70] === Swag);
+ console.log(received[71] === Swag);
+ console.log(received[72] === Swag);
+ console.log(received[73] === Object);
+ console.log(received[74] === Object);
+ console.log(received[75] === Swag);
+ console.log(received[76] === Swag);
+ console.log(received[77] === Object);
+ console.log(received[78] === Object);
+ console.log(received[79] === Swag);
+ console.log(received[80] === Swag);
+ console.log(received[81] === Object);
+ console.log(received[82] === Object);
+ console.log(received[83] === Object);
+ console.log(received[84] === Object);
+ console.log(received[85] === Object);
+ console.log(received[86] === Object);
+ console.log(received[87] === void 0);
+ console.log(received[88] === void 0);
+ console.log(received[89] === void 0);
+ console.log(received[90] === void 0);
+ console.log(received[91] === void 0);
+ console.log(received[92] === void 0);
+ console.log(received[93] === void 0);
+ console.log(received[94] === void 0);
+ console.log(received[95] === Object);
+ console.log(received[96] === void 0);
+ console.log(received[97] === Object);
+ console.log(received[98] === Object);
+ console.log(received[99] === void 0);
+ console.log(received[100] === void 0);
+ console.log(received[101] === void 0);
+ console.log(received[102] === void 0);
+ console.log(received[103] === void 0);
+ console.log(received[104] === void 0);
+ console.log(received[105] === void 0);
+ console.log(received[106] === void 0);
+ console.log(received[107] === Object);
+ console.log(received[108] === Object);
+ console.log(received[109] === Object);
+ console.log(received[110] === Object);
+ console.log(received[111] === Object);
+ console.log(received[112] === Object);
+ console.log(received[113] === Object);
+ console.log(received[114] === Object);
+ console.log(received[115] === Object);
+ console.log(received[116] === Object);
+ console.log(received[117] === Object);
+ console.log(received[118] === Object);
+ console.log(received[119] === Object);
+ console.log(received[120] === void 0);
+ console.log(received[121] === Object);
+ console.log(received[122] === Object);
+ console.log(received[123] === Object);
+ console.log(received[124] === Object);
+ console.log(received[125] === Object);
+ console.log(received[126] === Object);
+ console.log(received[127] === Object);
+ console.log(received[128] === Object);
+ console.log(received[129] === Object);
+ console.log(received[130] === Object);
+ console.log(received[131] === Object);
+ console.log(received[132] === Object);
+ console.log(received[133] === Object);
+ console.log(received[134] === void 0);
+ console.log(received[135] === void 0);
+ console.log(received[136] === void 0);
+ console.log(received[137] === void 0);
+ console.log(received[138] === void 0);
+ console.log(received[139] === void 0);
+ console.log(received[140] === void 0);
+ console.log(received[141] === void 0);
+ console.log(received[142] === void 0);
+ console.log(received[143] === void 0);
+ console.log(received[144] === Object);
+ console.log(received[145] === Object);
+ console.log(received[146] === void 0);
+ console.log(received[147] === void 0);
+ console.log(received[148] === void 0);
+ console.log(received[149] === void 0);
+ console.log(received[150] === void 0);
+ console.log(received[151] === void 0);
+ console.log(received[152] === void 0);
+ console.log(received[153] === void 0);
+ console.log(received[154] === void 0);
+ console.log(received[155] === Object);
+ console.log(received[156] === Object);
+ console.log(received[157] === Object);
+ console.log(received[158] === void 0);
+ console.log(received[159] === Object);
+ console.log(received[160] === Object);
+ console.log(received[161] === Object);
+ console.log(received[162] === Object);
+ console.log(received[163] === Object);
+ console.log(received[164] === Object);
+ console.log(received[165] === Object);
+ console.log(received[166] === Object);
+ console.log(received[167] === Object);
+ console.log(received[168] === Object);
+ console.log(received[169] === Object);
+ console.log(received[170] === Object);
+ console.log(received[171] === Boolean);
+ console.log(received[172] === Object);
+ console.log(received[173] === Object);
+
+ // @ts-ignore
+ @d1
+ class A {
+ // @ts-ignore
+ constructor(@d1 arg1: string) {}
+ // @ts-ignore
+ @d1
+ // @ts-ignore
+ method1(@d1 arg1: number): boolean {
+ return true;
+ }
+ // @ts-ignore
+ @d1
+ prop1: () => {};
+ // @ts-ignore
+ @d1
+ prop2: "foo" = "foo";
+ // @ts-ignore
+ @d1
+ prop3: symbol;
+ }
+
+ console.log(Reflect.getMetadata("design:type", A) === undefined);
+ console.log(Reflect.getMetadata("design:paramtypes", A)[0] === String);
+ console.log(Reflect.getMetadata("design:returntype", A) === undefined);
+
+ console.log(Reflect.getMetadata("design:type", A.prototype) === undefined);
+ console.log(Reflect.getMetadata("design:paramtypes", A.prototype) === undefined);
+ console.log(Reflect.getMetadata("design:returntype", A.prototype) === undefined);
+
+ console.log(Reflect.getMetadata("design:type", A.prototype.method1) === undefined);
+ console.log(Reflect.getMetadata("design:paramtypes", A.prototype.method1) === undefined);
+ console.log(Reflect.getMetadata("design:returntype", A.prototype.method1) === undefined);
+
+ console.log(Reflect.getMetadata("design:type", A.prototype, "method1") === Function);
+ console.log(Reflect.getMetadata("design:paramtypes", A.prototype, "method1")[0] === Number);
+ console.log(Reflect.getMetadata("design:returntype", A.prototype, "method1") === Boolean);
+
+ console.log(Reflect.getMetadata("design:type", A.prototype, "prop1") === Function);
+ console.log(Reflect.getMetadata("design:paramtypes", A.prototype, "prop1") === undefined);
+ console.log(Reflect.getMetadata("design:returntype", A.prototype, "prop1") === undefined);
+
+ console.log(Reflect.getMetadata("design:type", A.prototype, "prop2") === String);
+ console.log(Reflect.getMetadata("design:paramtypes", A.prototype, "prop2") === undefined);
+ console.log(Reflect.getMetadata("design:returntype", A.prototype, "prop2") === undefined);
+
+ console.log(Reflect.getMetadata("design:type", A.prototype, "prop3") === Symbol);
+ console.log(Reflect.getMetadata("design:paramtypes", A.prototype, "prop3") === undefined);
+ console.log(Reflect.getMetadata("design:returntype", A.prototype, "prop3") === undefined);
+
+ class HelloWorld {
+ // @ts-ignore
+ constructor(@d1 arg1: string) {}
+ }
+
+ console.log(Reflect.getMetadata("design:type", HelloWorld) === undefined);
+ console.log(Reflect.getMetadata("design:paramtypes", HelloWorld)[0] === String);
+ console.log(Reflect.getMetadata("design:returntype", HelloWorld) === undefined);
+
+ type B = "hello" | "world";
+ const b = 2;
+ const c = ["hello", "world"] as const;
+ type Loser = \`hello \${B}\`; // "hello hello" | "hello world"
+ function d1() {}
+
+ class AClass {
+ constructor(
+ // @ts-ignore
+ @d1 p0: \`hello \${B}\`,
+ // @ts-ignore
+ p1: keyof Something,
+ p2: typeof b,
+ p3: readonly ["hello", "world"],
+ p4: typeof c,
+ p5: readonly [number, string],
+ // prettier-ignore
+ p6: (string | string),
+ // prettier-ignore
+ p7: (string & string),
+ p8: boolean extends true ? "a" : "b",
+ // @ts-ignore
+ p9: Loser extends Loser ? string : Foo,
+ p10: { [keyof in string]: number },
+ // @ts-ignore
+ p11: blah extends blahblah ? number : void,
+ ) {}
+
+ // @ts-ignore
+ @d1
+ async method1() {
+ return true;
+ }
+ }
+
+ const paramtypes = Reflect.getMetadata("design:paramtypes", AClass);
+ console.log(paramtypes[0] === String);
+ console.log(paramtypes[1] === Object);
+ console.log(paramtypes[2] === Object);
+ console.log(paramtypes[3] === Array);
+ console.log(paramtypes[4] === Object);
+ console.log(paramtypes[5] === Array);
+ console.log(paramtypes[6] === String);
+ console.log(paramtypes[7] === String);
+ console.log(paramtypes[8] === String);
+ console.log(paramtypes[9] === Object);
+ console.log(paramtypes[10] === Object);
+ console.log(paramtypes[11] === Object);
+
+ console.log(Reflect.getMetadata("design:returntype", AClass.prototype, "method1") === Promise);
+ `,
+ "/tsconfig.json": /* json */ `
+ {
+ "compilerOptions": {
+ "experimentalDecorators": true,
+ "emitDecoratorMetadata": true,
+ }
+ }
+ `,
+ },
+ install: ["reflect-metadata"],
+ bundling: true,
+ run: {
+ stdout: "true\n".repeat(212),
+ },
+});
diff --git a/test/package.json b/test/package.json
index ad28f114a..9e55cc543 100644
--- a/test/package.json
+++ b/test/package.json
@@ -9,6 +9,7 @@
},
"dependencies": {
"@prisma/client": "5.1.1",
+ "reflect-metadata": "0.1.13",
"@resvg/resvg-js": "2.4.1",
"@swc/core": "1.3.38",
"body-parser": "1.20.2",
diff --git a/test/transpiler/decorator-metadata.test.ts b/test/transpiler/decorator-metadata.test.ts
new file mode 100644
index 000000000..861604430
--- /dev/null
+++ b/test/transpiler/decorator-metadata.test.ts
@@ -0,0 +1,494 @@
+import "reflect-metadata";
+
+describe("decorator metadata", () => {
+ test("type serialization", () => {
+ function d1() {}
+ class Known {}
+ class Swag {}
+ class A_1 {}
+
+ // @ts-ignore
+ @d1
+ class A {
+ constructor(
+ p0: any,
+ p1: unknown,
+ p2: never,
+ p3: void,
+ p4: null,
+ p5: undefined,
+ p6: number,
+ p7: string,
+ p8: boolean,
+ p9: symbol,
+ p10: bigint,
+ p11: object,
+ p12: () => {},
+ p13: [],
+ p14: {},
+ p15: 123,
+ p16: 123n,
+ p17: "123",
+ p18: `123`,
+ p19: true,
+ p20: false,
+ // @ts-ignore
+ p21: Map,
+ // @ts-ignore
+ p22: Set,
+ p23: Known,
+ // @ts-ignore
+ p24: Unknown,
+ p25: never & string,
+ p26: string & never,
+ p27: null & string,
+ p28: string & null,
+ p29: undefined & string,
+ p30: string & undefined,
+ p31: void & string,
+ p32: string & void,
+ p33: unknown & string,
+ p34: string & unknown,
+ p35: any & string,
+ p36: string & any,
+ p37: never | string,
+ p38: string | never,
+ p39: null | string,
+ p40: string | null,
+ p41: undefined | string,
+ p42: string | undefined,
+ p43: void | string,
+ p44: string | void,
+ p45: unknown | string,
+ p46: string | unknown,
+ p47: any | string,
+ p48: string | any,
+ p49: string | string,
+ p50: string & string,
+ p51: Known | Swag,
+ p52: Swag | Known,
+ p53: Known & Swag,
+ p54: Swag & Known,
+ p55: never | Swag,
+ p56: Swag | never,
+ p57: null | Swag,
+ p58: Swag | null,
+ p59: undefined | Swag,
+ p60: Swag | undefined,
+ p61: void | Swag,
+ p62: Swag | void,
+ p63: unknown | Swag,
+ p64: Swag | unknown,
+ p65: any | Swag,
+ p66: Swag | any,
+ p67: never & Swag,
+ p68: Swag & never,
+ p69: null & Swag,
+ p70: Swag & null,
+ p71: undefined & Swag,
+ p72: Swag & undefined,
+ p73: void & Swag,
+ p74: Swag & void,
+ p75: unknown & Swag,
+ p76: Swag & unknown,
+ p77: any & Swag,
+ p78: Swag & any,
+ p79: Swag | Swag,
+ p80: Swag & Swag,
+ // @ts-ignore
+ p81: Unknown | Known,
+ // @ts-ignore
+ p82: Known | Unknown,
+ // @ts-ignore
+ p83: Unknown & Known,
+ // @ts-ignore
+ p84: Known & Unknown,
+ // @ts-ignore
+ p85: Unknown | Unknown,
+ // @ts-ignore
+ p86: Unknown & Unknown,
+ p87: never | never,
+ p88: never & never,
+ p89: null | null,
+ p90: null & null,
+ p91: undefined | undefined,
+ p92: undefined & undefined,
+ p93: void | void,
+ p94: void & void,
+ p95: unknown | unknown,
+ p96: unknown & unknown,
+ p97: any | any,
+ p98: any & any,
+ p99: never | void,
+ p100: void | never,
+ p101: null | void,
+ p102: void | null,
+ p103: undefined | void,
+ p104: void | undefined,
+ p105: void | void,
+ p106: void & void,
+ p107: unknown | void,
+ p108: void | unknown,
+ p109: any | void,
+ p110: void | any,
+ p111: never | unknown,
+ p112: unknown | never,
+ p113: null | unknown,
+ p114: unknown | null,
+ p115: undefined | unknown,
+ p116: unknown | undefined,
+ p117: void | unknown,
+ p118: unknown | void,
+ p119: unknown | unknown,
+ p120: unknown & unknown,
+ p121: any | unknown,
+ p122: unknown | any,
+ p123: never | any,
+ p124: any | never,
+ p125: null | any,
+ p126: any | null,
+ p127: undefined | any,
+ p128: any | undefined,
+ p129: void | any,
+ p130: any | void,
+ p131: unknown | any,
+ p132: any | unknown,
+ p133: any | any,
+ p134: never & void,
+ p135: void & never,
+ p136: null & void,
+ p137: void & null,
+ p138: undefined & void,
+ p139: void & undefined,
+ p140: void & void,
+ p141: void | void,
+ p142: unknown & void,
+ p143: void & unknown,
+ p144: any & void,
+ p145: void & any,
+ p146: never & unknown,
+ p147: unknown & never,
+ p148: null & unknown,
+ p149: unknown & null,
+ p150: undefined & unknown,
+ p151: unknown & undefined,
+ p152: void & unknown,
+ p153: unknown & void,
+ p154: unknown & unknown,
+ p155: unknown | unknown,
+ p156: any & unknown,
+ p157: unknown & any,
+ p158: never & any,
+ p159: any & never,
+ p160: null & any,
+ p161: any & null,
+ p162: undefined & any,
+ p163: any & undefined,
+ p164: void & any,
+ p165: any & void,
+ p166: unknown & any,
+ p167: any & unknown,
+ p168: any & any,
+ p169: string & number & boolean & never & symbol,
+ p170: "foo" | A_1,
+ p171: true | boolean,
+ p172: "foo" | boolean,
+ p173: A_1 | "foo",
+ ) {}
+ }
+
+ const received = Reflect.getMetadata("design:paramtypes", A);
+ expect(received.length).toBe(174);
+ expect(received[0]).toBe(Object);
+ expect(received[1]).toBe(Object);
+ expect(received[2]).toBe(void 0);
+ expect(received[3]).toBe(void 0);
+ expect(received[4]).toBe(void 0);
+ expect(received[5]).toBe(void 0);
+ expect(received[6]).toBe(Number);
+ expect(received[7]).toBe(String);
+ expect(received[8]).toBe(Boolean);
+ expect(received[9]).toBe(typeof Symbol === "function" ? Symbol : Object);
+ expect(received[10]).toBe(typeof BigInt === "function" ? BigInt : Object);
+ expect(received[11]).toBe(Object);
+ expect(received[12]).toBe(Function);
+ expect(received[13]).toBe(Array);
+ expect(received[14]).toBe(Object);
+ expect(received[15]).toBe(Number);
+ expect(received[16]).toBe(typeof BigInt === "function" ? BigInt : Object);
+ expect(received[17]).toBe(String);
+ expect(received[18]).toBe(String);
+ expect(received[19]).toBe(Boolean);
+ expect(received[20]).toBe(Boolean);
+ expect(received[21]).toBe(Map);
+ expect(received[22]).toBe(Set);
+ expect(received[23]).toBe(Known);
+ expect(received[24]).toBe(Object);
+ expect(received[25]).toBe(void 0);
+ expect(received[26]).toBe(void 0);
+ expect(received[27]).toBe(String);
+ expect(received[28]).toBe(String);
+ expect(received[29]).toBe(String);
+ expect(received[30]).toBe(String);
+ expect(received[31]).toBe(Object);
+ expect(received[32]).toBe(Object);
+ expect(received[33]).toBe(String);
+ expect(received[34]).toBe(String);
+ expect(received[35]).toBe(Object);
+ expect(received[36]).toBe(Object);
+ expect(received[37]).toBe(String);
+ expect(received[38]).toBe(String);
+ expect(received[39]).toBe(String);
+ expect(received[40]).toBe(String);
+ expect(received[41]).toBe(String);
+ expect(received[42]).toBe(String);
+ expect(received[43]).toBe(Object);
+ expect(received[44]).toBe(Object);
+ expect(received[45]).toBe(Object);
+ expect(received[46]).toBe(Object);
+ expect(received[47]).toBe(Object);
+ expect(received[48]).toBe(Object);
+ expect(received[49]).toBe(String);
+ expect(received[50]).toBe(String);
+ expect(received[51]).toBe(Object);
+ expect(received[52]).toBe(Object);
+ expect(received[53]).toBe(Object);
+ expect(received[54]).toBe(Object);
+ expect(received[55]).toBe(Swag);
+ expect(received[56]).toBe(Swag);
+ expect(received[57]).toBe(Swag);
+ expect(received[58]).toBe(Swag);
+ expect(received[59]).toBe(Swag);
+ expect(received[60]).toBe(Swag);
+ expect(received[61]).toBe(Object);
+ expect(received[62]).toBe(Object);
+ expect(received[63]).toBe(Object);
+ expect(received[64]).toBe(Object);
+ expect(received[65]).toBe(Object);
+ expect(received[66]).toBe(Object);
+ expect(received[67]).toBe(void 0);
+ expect(received[68]).toBe(void 0);
+ expect(received[69]).toBe(Swag);
+ expect(received[70]).toBe(Swag);
+ expect(received[71]).toBe(Swag);
+ expect(received[72]).toBe(Swag);
+ expect(received[73]).toBe(Object);
+ expect(received[74]).toBe(Object);
+ expect(received[75]).toBe(Swag);
+ expect(received[76]).toBe(Swag);
+ expect(received[77]).toBe(Object);
+ expect(received[78]).toBe(Object);
+ expect(received[79]).toBe(Swag);
+ expect(received[80]).toBe(Swag);
+ expect(received[81]).toBe(Object);
+ expect(received[82]).toBe(Object);
+ expect(received[83]).toBe(Object);
+ expect(received[84]).toBe(Object);
+ expect(received[85]).toBe(Object);
+ expect(received[86]).toBe(Object);
+ expect(received[87]).toBe(void 0);
+ expect(received[88]).toBe(void 0);
+ expect(received[89]).toBe(void 0);
+ expect(received[90]).toBe(void 0);
+ expect(received[91]).toBe(void 0);
+ expect(received[92]).toBe(void 0);
+ expect(received[93]).toBe(void 0);
+ expect(received[94]).toBe(void 0);
+ expect(received[95]).toBe(Object);
+ expect(received[96]).toBe(void 0);
+ expect(received[97]).toBe(Object);
+ expect(received[98]).toBe(Object);
+ expect(received[99]).toBe(void 0);
+ expect(received[100]).toBe(void 0);
+ expect(received[101]).toBe(void 0);
+ expect(received[102]).toBe(void 0);
+ expect(received[103]).toBe(void 0);
+ expect(received[104]).toBe(void 0);
+ expect(received[105]).toBe(void 0);
+ expect(received[106]).toBe(void 0);
+ expect(received[107]).toBe(Object);
+ expect(received[108]).toBe(Object);
+ expect(received[109]).toBe(Object);
+ expect(received[110]).toBe(Object);
+ expect(received[111]).toBe(Object);
+ expect(received[112]).toBe(Object);
+ expect(received[113]).toBe(Object);
+ expect(received[114]).toBe(Object);
+ expect(received[115]).toBe(Object);
+ expect(received[116]).toBe(Object);
+ expect(received[117]).toBe(Object);
+ expect(received[118]).toBe(Object);
+ expect(received[119]).toBe(Object);
+ expect(received[120]).toBe(void 0);
+ expect(received[121]).toBe(Object);
+ expect(received[122]).toBe(Object);
+ expect(received[123]).toBe(Object);
+ expect(received[124]).toBe(Object);
+ expect(received[125]).toBe(Object);
+ expect(received[126]).toBe(Object);
+ expect(received[127]).toBe(Object);
+ expect(received[128]).toBe(Object);
+ expect(received[129]).toBe(Object);
+ expect(received[130]).toBe(Object);
+ expect(received[131]).toBe(Object);
+ expect(received[132]).toBe(Object);
+ expect(received[133]).toBe(Object);
+ expect(received[134]).toBe(void 0);
+ expect(received[135]).toBe(void 0);
+ expect(received[136]).toBe(void 0);
+ expect(received[137]).toBe(void 0);
+ expect(received[138]).toBe(void 0);
+ expect(received[139]).toBe(void 0);
+ expect(received[140]).toBe(void 0);
+ expect(received[141]).toBe(void 0);
+ expect(received[142]).toBe(void 0);
+ expect(received[143]).toBe(void 0);
+ expect(received[144]).toBe(Object);
+ expect(received[145]).toBe(Object);
+ expect(received[146]).toBe(void 0);
+ expect(received[147]).toBe(void 0);
+ expect(received[148]).toBe(void 0);
+ expect(received[149]).toBe(void 0);
+ expect(received[150]).toBe(void 0);
+ expect(received[151]).toBe(void 0);
+ expect(received[152]).toBe(void 0);
+ expect(received[153]).toBe(void 0);
+ expect(received[154]).toBe(void 0);
+ expect(received[155]).toBe(Object);
+ expect(received[156]).toBe(Object);
+ expect(received[157]).toBe(Object);
+ expect(received[158]).toBe(void 0);
+ expect(received[159]).toBe(Object);
+ expect(received[160]).toBe(Object);
+ expect(received[161]).toBe(Object);
+ expect(received[162]).toBe(Object);
+ expect(received[163]).toBe(Object);
+ expect(received[164]).toBe(Object);
+ expect(received[165]).toBe(Object);
+ expect(received[166]).toBe(Object);
+ expect(received[167]).toBe(Object);
+ expect(received[168]).toBe(Object);
+ expect(received[169]).toBe(Object);
+ expect(received[170]).toBe(Object);
+ expect(received[171]).toBe(Boolean);
+ expect(received[172]).toBe(Object);
+ expect(received[173]).toBe(Object);
+ });
+ test("design: type, paramtypes, returntype", () => {
+ function d1() {}
+ // @ts-ignore
+ @d1
+ class A {
+ // @ts-ignore
+ constructor(@d1 arg1: string) {}
+ // @ts-ignore
+ @d1
+ // @ts-ignore
+ method1(@d1 arg1: number): boolean {
+ return true;
+ }
+ // @ts-ignore
+ @d1
+ prop1: () => {};
+ // @ts-ignore
+ @d1
+ prop2: "foo" = "foo";
+ // @ts-ignore
+ @d1
+ prop3: symbol;
+ }
+
+ expect(Reflect.getMetadata("design:type", A)).toBeUndefined();
+ expect(Reflect.getMetadata("design:paramtypes", A)[0]).toBe(String);
+ expect(Reflect.getMetadata("design:returntype", A)).toBeUndefined();
+
+ expect(Reflect.getMetadata("design:type", A.prototype)).toBeUndefined();
+ expect(Reflect.getMetadata("design:paramtypes", A.prototype)).toBeUndefined();
+ expect(Reflect.getMetadata("design:returntype", A.prototype)).toBeUndefined();
+
+ expect(Reflect.getMetadata("design:type", A.prototype.method1)).toBeUndefined();
+ expect(Reflect.getMetadata("design:paramtypes", A.prototype.method1)).toBeUndefined();
+ expect(Reflect.getMetadata("design:returntype", A.prototype.method1)).toBeUndefined();
+
+ expect(Reflect.getMetadata("design:type", A.prototype, "method1")).toBe(Function);
+ expect(Reflect.getMetadata("design:paramtypes", A.prototype, "method1")[0]).toBe(Number);
+ expect(Reflect.getMetadata("design:returntype", A.prototype, "method1")).toBe(Boolean);
+
+ expect(Reflect.getMetadata("design:type", A.prototype, "prop1")).toBe(Function);
+ expect(Reflect.getMetadata("design:paramtypes", A.prototype, "prop1")).toBeUndefined();
+ expect(Reflect.getMetadata("design:returntype", A.prototype, "prop1")).toBeUndefined();
+
+ expect(Reflect.getMetadata("design:type", A.prototype, "prop2")).toBe(String);
+ expect(Reflect.getMetadata("design:paramtypes", A.prototype, "prop2")).toBeUndefined();
+ expect(Reflect.getMetadata("design:returntype", A.prototype, "prop2")).toBeUndefined();
+
+ expect(Reflect.getMetadata("design:type", A.prototype, "prop3")).toBe(Symbol);
+ expect(Reflect.getMetadata("design:paramtypes", A.prototype, "prop3")).toBeUndefined();
+ expect(Reflect.getMetadata("design:returntype", A.prototype, "prop3")).toBeUndefined();
+ });
+
+ test("class with only constructor argument decorators", () => {
+ function d1() {}
+ class A {
+ // @ts-ignore
+ constructor(@d1 arg1: string) {}
+ }
+
+ expect(Reflect.getMetadata("design:type", A)).toBeUndefined();
+ expect(Reflect.getMetadata("design:paramtypes", A)[0]).toBe(String);
+ expect(Reflect.getMetadata("design:returntype", A)).toBeUndefined();
+ });
+
+ test("more types", () => {
+ type B = "hello" | "world";
+ const b = 2;
+ const c = ["hello", "world"] as const;
+ type Loser = `hello ${B}`; // "hello hello" | "hello world"
+ function d1() {}
+
+ class A {
+ constructor(
+ // @ts-ignore
+ @d1 p0: `hello ${B}`,
+ // @ts-ignore
+ p1: keyof Something,
+ p2: typeof b,
+ p3: readonly ["hello", "world"],
+ p4: typeof c,
+ p5: readonly [number, string],
+ // prettier-ignore
+ p6: (string | string),
+ // prettier-ignore
+ p7: (string & string),
+ p8: boolean extends true ? "a" : "b",
+ // @ts-ignore
+ p9: Loser extends Loser ? string : Foo,
+ p10: { [keyof in string]: number },
+ // @ts-ignore
+ p11: blah extends blahblah ? number : void,
+ ) {}
+
+ // @ts-ignore
+ @d1
+ async method1() {
+ return true;
+ }
+ }
+
+ const paramtypes = Reflect.getMetadata("design:paramtypes", A);
+ expect(paramtypes[0]).toBe(String);
+ expect(paramtypes[1]).toBe(Object);
+ expect(paramtypes[2]).toBe(Object);
+ expect(paramtypes[3]).toBe(Array);
+ expect(paramtypes[4]).toBe(Object);
+ expect(paramtypes[5]).toBe(Array);
+ expect(paramtypes[6]).toBe(String);
+ expect(paramtypes[7]).toBe(String);
+ expect(paramtypes[8]).toBe(String);
+ expect(paramtypes[9]).toBe(Object);
+ expect(paramtypes[10]).toBe(Object);
+ expect(paramtypes[11]).toBe(Object);
+
+ expect(Reflect.getMetadata("design:returntype", A.prototype, "method1")).toBe(Promise);
+ });
+});
diff --git a/test/tsconfig.json b/test/tsconfig.json
index af2af2bb5..d308b40b7 100644
--- a/test/tsconfig.json
+++ b/test/tsconfig.json
@@ -7,6 +7,7 @@
"moduleResolution": "bundler",
"moduleDetection": "force",
"allowImportingTsExtensions": true,
+ "experimentalDecorators": true,
"noEmit": true,
"composite": true,
"strict": true,
diff --git a/tsconfig.json b/tsconfig.json
index e8b7322b4..b19b46d1f 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -2,6 +2,7 @@
"extends": "./tsconfig.base.json",
"compilerOptions": {
"experimentalDecorators": true,
+ "emitDecoratorMetadata": true,
// "skipLibCheck": true,
"allowJs": true
},