Skip to content

Commit 6fbbaf8

Browse files
committed
more
1 parent 013b2c3 commit 6fbbaf8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+5269
-52
lines changed

Cargo.lock

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/swc_ecma_compiler/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,18 @@ unexpected_cfgs = { level = "warn", check-cfg = ['cfg(swc_ast_unknown)'] }
1515
[dependencies]
1616
bitflags = { workspace = true }
1717
indexmap = { workspace = true }
18+
itoa = "1"
19+
memchr = { workspace = true }
1820
rustc-hash = { workspace = true }
1921
serde = { workspace = true }
2022
tracing = { workspace = true }
2123

2224
swc_atoms = { version = "9.0.0", path = "../swc_atoms" }
2325
swc_common = { version = "17.0.0", path = "../swc_common" }
2426
swc_ecma_ast = { version = "18.0.0", path = "../swc_ecma_ast" }
27+
swc_ecma_regexp = { version = "0.6.0", path = "../swc_ecma_regexp" }
28+
swc_ecma_regexp_ast = { version = "0.6.0", path = "../swc_ecma_regexp_ast" }
29+
swc_ecma_regexp_visit = { version = "0.6.0", path = "../swc_ecma_regexp_visit" }
2530
swc_ecma_transforms_base = { version = "30.0.0", path = "../swc_ecma_transforms_base" }
2631
swc_ecma_utils = { version = "24.0.0", path = "../swc_ecma_utils" }
2732
swc_ecma_visit = { version = "18.0.0", path = "../swc_ecma_visit" }

crates/swc_ecma_compiler/src/compat/decorator.rs

Lines changed: 0 additions & 6 deletions
This file was deleted.
Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
//! Emitting decorator metadata
2+
//!
3+
//! This plugin is used to emit decorator metadata for legacy decorators by
4+
//! the `__metadata` helper.
5+
//!
6+
//! ## Example
7+
//!
8+
//! Input:
9+
//! ```ts
10+
//! class Demo {
11+
//! @LogMethod
12+
//! public foo(bar: number) {}
13+
//!
14+
//! @Prop
15+
//! prop: string = "hello";
16+
//! }
17+
//! ```
18+
//!
19+
//! Output:
20+
//! ```js
21+
//! class Demo {
22+
//! foo(bar) {}
23+
//! prop = "hello";
24+
//! }
25+
//! babelHelpers.decorate([
26+
//! LogMethod,
27+
//! babelHelpers.decorateParam(0, babelHelpers.decorateMetadata("design:type", Function)),
28+
//! babelHelpers.decorateParam(0, babelHelpers.decorateMetadata("design:paramtypes", [Number])),
29+
//! babelHelpers.decorateParam(0, babelHelpers.decorateMetadata("design:returntype", void 0))
30+
//! ], Demo.prototype, "foo", null);
31+
//! babelHelpers.decorate([Prop, babelHelpers.decorateMetadata("design:type", String)], Demo.prototype, "prop", void 0);
32+
//! ```
33+
//!
34+
//! ## Implementation
35+
//!
36+
//! This is a port of the oxc implementation based on
37+
//! <https://github.com/microsoft/TypeScript/blob/d85767abfd83880cea17cea70f9913e9c4496dcc/src/compiler/transformers/ts.ts#L1119-L1136>
38+
//!
39+
//! ## Limitations
40+
//!
41+
//! ### Compared to TypeScript
42+
//!
43+
//! We lack the type inference ability that TypeScript has, so we cannot
44+
//! determine the exact type of type references. For example:
45+
//!
46+
//! Input:
47+
//! ```ts
48+
//! type Foo = string;
49+
//! class Cls {
50+
//! @dec
51+
//! p: Foo = ""
52+
//! }
53+
//! ```
54+
//!
55+
//! TypeScript Output:
56+
//! ```js
57+
//! class Cls {
58+
//! constructor() {
59+
//! this.p = "";
60+
//! }
61+
//! }
62+
//! __decorate([
63+
//! dec,
64+
//! __metadata("design:type", String) // Infers that Foo is String
65+
//! ], Cls.prototype, "p", void 0);
66+
//! ```
67+
//!
68+
//! Our Output:
69+
//! ```js
70+
//! var _ref;
71+
//! class Cls {
72+
//! p = "";
73+
//! }
74+
//! babelHelpers.decorate([
75+
//! dec,
76+
//! babelHelpers.decorateMetadata("design:type", typeof (_ref = typeof Foo === "undefined" && Foo) === "function" ? _ref : Object)
77+
//! ],
78+
//! Cls.prototype, "p", void 0);
79+
//! ```
80+
//!
81+
//! ### Compared to SWC
82+
//!
83+
//! SWC also has the above limitation. SWC provides additional support for
84+
//! inferring enum members, which we currently do not have. The limitation may
85+
//! not be a problem as SWC has been adopted in [NestJS](https://docs.nestjs.com/recipes/swc#jest--swc).
86+
//!
87+
//! ## Porting Status
88+
//!
89+
//! **TODO**: This is a stub implementation. The full porting requires:
90+
//!
91+
//! 1. Type serialization logic for all TypeScript types
92+
//! 2. Enum type inference and tracking
93+
//! 3. Metadata generation for:
94+
//! - `design:type` - Type of the member
95+
//! - `design:paramtypes` - Types of parameters
96+
//! - `design:returntype` - Return type (for methods)
97+
//! 4. Integration with helper loader for `_metadata` helper
98+
//! 5. Proper handling of type annotations and their serialization
99+
//!
100+
//! The original oxc implementation (~800 lines) includes:
101+
//! - Type node serialization (void, function, array, boolean, string, number,
102+
//! bigint, symbol, etc.)
103+
//! - Type reference resolution with fallback for unavailable types
104+
//! - Union/intersection type handling
105+
//! - Enum type inference (string, number, mixed)
106+
//! - Entity name serialization with runtime checks
107+
//! - Metadata stacks for methods and constructors
108+
//!
109+
//! ## References
110+
//! * TypeScript's [emitDecoratorMetadata](https://www.typescriptlang.org/tsconfig#emitDecoratorMetadata)
111+
112+
use swc_ecma_ast::*;
113+
use swc_ecma_visit::{VisitMut, VisitMutWith};
114+
115+
/// Type of an enum inferred from its members
116+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
117+
enum EnumType {
118+
/// All members are string literals or template literals with string-only
119+
/// expressions
120+
String,
121+
/// All members are numeric, bigint, unary numeric, or auto-incremented
122+
Number,
123+
/// Mixed types or computed values
124+
Object,
125+
}
126+
127+
/// Metadata for decorated methods
128+
#[allow(dead_code)]
129+
pub(super) struct MethodMetadata {
130+
/// The `design:type` metadata expression
131+
pub r#type: Box<Expr>,
132+
/// The `design:paramtypes` metadata expression
133+
pub param_types: Box<Expr>,
134+
/// The `design:returntype` metadata expression (optional, omitted for
135+
/// getters/setters)
136+
pub return_type: Option<Box<Expr>>,
137+
}
138+
139+
/// Legacy decorator metadata transformer
140+
///
141+
/// Emits decorator metadata for TypeScript decorators
142+
pub struct LegacyDecoratorMetadata {
143+
// TODO: Add fields for tracking metadata state
144+
// - method_metadata_stack: Stack for method metadata
145+
// - constructor_metadata_stack: Stack for constructor metadata
146+
// - enum_types: Map of enum symbol IDs to their inferred types
147+
}
148+
149+
impl LegacyDecoratorMetadata {
150+
/// Create a new metadata transformer
151+
pub fn new() -> Self {
152+
Self {
153+
// TODO: Initialize metadata tracking structures
154+
}
155+
}
156+
157+
/// Infer the type of an enum based on its members
158+
#[allow(dead_code)]
159+
fn infer_enum_type(_members: &[TsEnumMember]) -> EnumType {
160+
// TODO: Implement enum type inference
161+
// Analyze members to determine if enum is String, Number, or Object type
162+
EnumType::Object
163+
}
164+
165+
/// Serialize a TypeScript type node for use with decorator metadata
166+
///
167+
/// Types are serialized as follows:
168+
/// - Void types -> "undefined" (e.g. "void 0")
169+
/// - Function and Constructor types -> global "Function"
170+
/// - Array and Tuple types -> global "Array"
171+
/// - Boolean types and type predicates -> global "Boolean"
172+
/// - String literal types and strings -> global "String"
173+
/// - Enum and number types -> global "Number"
174+
/// - Symbol types -> global "Symbol"
175+
/// - Type references to classes -> constructor for the class
176+
/// - Everything else -> global "Object"
177+
#[allow(dead_code)]
178+
fn serialize_type_node(&mut self, _node: &TsType) -> Box<Expr> {
179+
// TODO: Implement type serialization logic
180+
// This is the core of metadata emission
181+
Box::new(Expr::Ident(Ident::new_no_ctxt(
182+
"Object".into(),
183+
Default::default(),
184+
)))
185+
}
186+
187+
/// Create a metadata decorator call expression
188+
#[allow(dead_code)]
189+
fn create_metadata(&self, _key: &str, _value: Box<Expr>) -> Decorator {
190+
// TODO: Generate `_metadata(key, value)` helper call
191+
Decorator {
192+
span: Default::default(),
193+
expr: Box::new(Expr::Ident(Ident::new_no_ctxt(
194+
"TODO".into(),
195+
Default::default(),
196+
))),
197+
}
198+
}
199+
}
200+
201+
impl VisitMut for LegacyDecoratorMetadata {
202+
fn visit_mut_module(&mut self, n: &mut Module) {
203+
// TODO: Finalize metadata emission
204+
n.visit_mut_children_with(self);
205+
}
206+
207+
fn visit_mut_script(&mut self, n: &mut Script) {
208+
// TODO: Finalize metadata emission
209+
n.visit_mut_children_with(self);
210+
}
211+
212+
fn visit_mut_ts_enum_decl(&mut self, n: &mut TsEnumDecl) {
213+
// TODO: Collect enum type information for metadata generation
214+
n.visit_mut_children_with(self);
215+
}
216+
217+
fn visit_mut_class(&mut self, n: &mut Class) {
218+
// TODO: Handle constructor metadata
219+
n.visit_mut_children_with(self);
220+
}
221+
222+
fn visit_mut_class_method(&mut self, n: &mut ClassMethod) {
223+
// TODO: Generate method metadata (design:type, design:paramtypes,
224+
// design:returntype)
225+
n.visit_mut_children_with(self);
226+
}
227+
228+
fn visit_mut_class_prop(&mut self, n: &mut ClassProp) {
229+
// TODO: Generate property metadata (design:type)
230+
if !n.decorators.is_empty() {
231+
// Should add design:type metadata decorator
232+
}
233+
n.visit_mut_children_with(self);
234+
}
235+
236+
fn visit_mut_private_prop(&mut self, n: &mut PrivateProp) {
237+
// TODO: Generate private property metadata
238+
n.visit_mut_children_with(self);
239+
}
240+
}

0 commit comments

Comments
 (0)