1
+use std::{io::stderr, num::NonZeroU32, sync::Arc};
2
+
3
+use forget_estree::{Binding, BindingId};
4
+use swc::Compiler;
5
+use swc_core::common::errors::Handler;
6
+use swc_core::common::source_map::Pos;
7
+use swc_core::common::{FileName, FilePathMapping, Mark, SourceMap, Span, SyntaxContext, GLOBALS};
8
+use swc_core::ecma::ast::{
9
+ AssignOp, BinaryOp, BlockStmt, Decl, EsVersion, Expr, Function, Ident, Lit, MemberExpr,
10
+ MemberProp, ModuleItem, Pat, PatOrExpr, Program, Stmt, UnaryOp, VarDecl, VarDeclKind,
11
+ VarDeclOrExpr,
12
+};
13
+use swc_core::ecma::parser::Syntax;
14
+use swc_core::ecma::transforms::base::resolver;
15
+use swc_core::ecma::visit::FoldWith;
16
+
17
+/// Parses source text into an forget_estree::Program via SWC, internally performing the parsing
18
+/// and SWC -> ESTree conversion.
19
+pub fn parse(
20
+ source: &str,
21
+ file: &str,
22
+) -> Result<forget_estree::Program, Box<dyn std::error::Error>> {
23
+ GLOBALS.set(&Default::default(), || {
24
+ let cm = Arc::new(SourceMap::new(FilePathMapping::empty()));
25
+ let c = Compiler::new(cm);
26
+ let fm =
27
+ c.cm.new_source_file(FileName::Real(file.into()), source.to_string());
28
+
29
+ let handler = Handler::with_emitter_writer(Box::new(stderr()), Some(c.cm.clone()));
30
+
31
+ let comments = c.comments().clone();
32
+ let module = c.parse_js(
33
+ fm.clone(),
34
+ &handler,
35
+ EsVersion::Es5,
36
+ Syntax::Typescript(Default::default()),
37
+ swc::config::IsModule::Bool(true),
38
+ Some(&comments),
39
+ )?;
40
+
41
+ let context = Context {
42
+ top_level_mark: Mark::new(),
43
+ unresolved_mark: Mark::new(),
44
+ };
45
+
46
+ let module = c.run_transform(&handler, false, || {
47
+ module.fold_with(&mut resolver(
48
+ context.unresolved_mark,
49
+ context.top_level_mark,
50
+ true,
51
+ ))
52
+ });
53
+
54
+ Ok(convert_program(&context, &module))
55
+ })
56
+}
57
+
58
+#[derive(Debug)]
59
+struct Context {
60
+ unresolved_mark: Mark,
61
+ top_level_mark: Mark,
62
+}
63
+
64
+fn convert_program(cx: &Context, program: &Program) -> forget_estree::Program {
65
+ let mut program_items: Vec<forget_estree::ModuleItem>;
66
+ match program {
67
+ Program::Module(program) => {
68
+ let body = &program.body;
69
+ program_items = Vec::with_capacity(body.len());
70
+ for item in body {
71
+ program_items.push(convert_module_item(cx, item));
72
+ }
73
+ }
74
+ Program::Script(program) => {
75
+ let body = &program.body;
76
+ program_items = Vec::with_capacity(body.len());
77
+ for item in body {
78
+ program_items.push(forget_estree::ModuleItem::Statement(convert_statement(
79
+ cx, item,
80
+ )));
81
+ }
82
+ }
83
+ };
84
+ forget_estree::Program {
85
+ source_type: if program.is_script() {
86
+ forget_estree::SourceType::Script
87
+ } else {
88
+ forget_estree::SourceType::Module
89
+ },
90
+ body: program_items,
91
+ // comments: None,
92
+ loc: None,
93
+ range: None,
94
+ }
95
+}
96
+
97
+fn convert_module_item(cx: &Context, item: &ModuleItem) -> forget_estree::ModuleItem {
98
+ match item {
99
+ ModuleItem::Stmt(item) => forget_estree::ModuleItem::Statement(convert_statement(cx, item)),
100
+ _ => todo!("translate module item {:#?}", item),
101
+ }
102
+}
103
+
104
+fn convert_span(span: &Span) -> Option<forget_estree::SourceRange> {
105
+ Some(forget_estree::SourceRange {
106
+ start: span.lo().to_u32(),
107
+ end: NonZeroU32::new(span.hi().to_u32())?,
108
+ })
109
+}
110
+
111
+fn convert_decl_kind(kind: &VarDeclKind) -> forget_estree::VariableDeclarationKind {
112
+ match kind {
113
+ VarDeclKind::Const => forget_estree::VariableDeclarationKind::Const,
114
+ VarDeclKind::Let => forget_estree::VariableDeclarationKind::Let,
115
+ VarDeclKind::Var => forget_estree::VariableDeclarationKind::Var,
116
+ }
117
+}
118
+
119
+fn convert_block_statement(cx: &Context, stmt: &BlockStmt) -> forget_estree::BlockStatement {
120
+ let mut body: Vec<forget_estree::Statement> = Vec::with_capacity(stmt.stmts.len());
121
+ for stmt in &stmt.stmts {
122
+ body.push(convert_statement(cx, stmt));
123
+ }
124
+ forget_estree::BlockStatement {
125
+ body,
126
+ loc: None,
127
+ range: convert_span(&stmt.span),
128
+ }
129
+}
130
+
131
+fn convert_function(cx: &Context, id: Option<&Ident>, fun: &Function) -> forget_estree::Function {
132
+ forget_estree::Function {
133
+ id: id.map(|id| forget_estree::Identifier {
134
+ name: id.sym.to_string(),
135
+ binding: convert_binding(cx, id.span.ctxt),
136
+ loc: None,
137
+ range: convert_span(&id.span),
138
+ }),
139
+ params: fun
140
+ .params
141
+ .iter()
142
+ .map(|param| convert_pattern(cx, ¶m.pat))
143
+ .collect(),
144
+ body: fun.body.as_ref().map(|body| {
145
+ forget_estree::FunctionBody::BlockStatement(Box::new(convert_block_statement(cx, body)))
146
+ }),
147
+ is_async: fun.is_async,
148
+ is_generator: fun.is_generator,
149
+ loc: None,
150
+ range: convert_span(&fun.span),
151
+ }
152
+}
153
+
154
+fn convert_statement(cx: &Context, stmt: &Stmt) -> forget_estree::Statement {
155
+ match stmt {
156
+ Stmt::Decl(Decl::Fn(item)) => forget_estree::Statement::FunctionDeclaration(Box::new(
157
+ forget_estree::FunctionDeclaration {
158
+ function: convert_function(cx, Some(&item.ident), &item.function),
159
+ loc: None,
160
+ range: convert_span(&item.function.span),
161
+ },
162
+ )),
163
+ Stmt::Decl(Decl::Var(item)) => forget_estree::Statement::VariableDeclaration(Box::new(
164
+ convert_variable_declaration(cx, item),
165
+ )),
166
+ Stmt::Block(item) => {
167
+ forget_estree::Statement::BlockStatement(Box::new(convert_block_statement(cx, item)))
168
+ }
169
+ Stmt::Break(item) => {
170
+ forget_estree::Statement::BreakStatement(Box::new(forget_estree::BreakStatement {
171
+ label: item
172
+ .label
173
+ .as_ref()
174
+ .map(|label| convert_identifier(cx, label)),
175
+ loc: None,
176
+ range: convert_span(&item.span),
177
+ }))
178
+ }
179
+ Stmt::Continue(item) => forget_estree::Statement::ContinueStatement(Box::new(
180
+ forget_estree::ContinueStatement {
181
+ label: item
182
+ .label
183
+ .as_ref()
184
+ .map(|label| convert_identifier(cx, label)),
185
+ loc: None,
186
+ range: convert_span(&item.span),
187
+ },
188
+ )),
189
+ Stmt::Debugger(item) => forget_estree::Statement::DebuggerStatement(Box::new(
190
+ forget_estree::DebuggerStatement {
191
+ loc: None,
192
+ range: convert_span(&item.span),
193
+ },
194
+ )),
195
+ Stmt::DoWhile(item) => {
196
+ forget_estree::Statement::DoWhileStatement(Box::new(forget_estree::DoWhileStatement {
197
+ body: convert_statement(cx, &item.body),
198
+ test: convert_expression(cx, &item.test),
199
+ loc: None,
200
+ range: convert_span(&item.span),
201
+ }))
202
+ }
203
+ Stmt::Empty(item) => {
204
+ forget_estree::Statement::EmptyStatement(Box::new(forget_estree::EmptyStatement {
205
+ loc: None,
206
+ range: convert_span(&item.span),
207
+ }))
208
+ }
209
+ Stmt::Expr(item) => {
210
+ let expression = convert_expression(cx, &item.expr);
211
+ forget_estree::Statement::ExpressionStatement(Box::new(
212
+ forget_estree::ExpressionStatement {
213
+ expression,
214
+ directive: None,
215
+ loc: None,
216
+ range: convert_span(&item.span),
217
+ },
218
+ ))
219
+ }
220
+ Stmt::For(item) => {
221
+ forget_estree::Statement::ForStatement(Box::new(forget_estree::ForStatement {
222
+ init: item.init.as_ref().map(|init| match init {
223
+ VarDeclOrExpr::Expr(init) => {
224
+ forget_estree::ForInit::Expression(convert_expression(cx, init))
225
+ }
226
+ VarDeclOrExpr::VarDecl(init) => {
227
+ assert_eq!(init.decls.len(), 1);
228
+ forget_estree::ForInit::VariableDeclaration(Box::new(
229
+ convert_variable_declaration(cx, init),
230
+ ))
231
+ }
232
+ }),
233
+ test: item.test.as_ref().map(|test| convert_expression(cx, test)),
234
+ update: item
235
+ .update
236
+ .as_ref()
237
+ .map(|update| convert_expression(cx, update)),
238
+ body: convert_statement(cx, &item.body),
239
+ loc: None,
240
+ range: convert_span(&item.span),
241
+ }))
242
+ }
243
+ Stmt::Return(item) => {
244
+ forget_estree::Statement::ReturnStatement(Box::new(forget_estree::ReturnStatement {
245
+ argument: item.arg.as_ref().map(|arg| convert_expression(cx, arg)),
246
+ loc: None,
247
+ range: convert_span(&item.span),
248
+ }))
249
+ }
250
+ Stmt::Throw(item) => {
251
+ forget_estree::Statement::ThrowStatement(Box::new(forget_estree::ThrowStatement {
252
+ argument: convert_expression(cx, &item.arg),
253
+ loc: None,
254
+ range: convert_span(&item.span),
255
+ }))
256
+ }
257
+ Stmt::If(item) => {
258
+ forget_estree::Statement::IfStatement(Box::new(forget_estree::IfStatement {
259
+ test: convert_expression(cx, &item.test),
260
+ consequent: convert_statement(cx, &item.cons),
261
+ alternate: item.alt.as_ref().map(|alt| convert_statement(cx, alt)),
262
+ loc: None,
263
+ range: convert_span(&item.span),
264
+ }))
265
+ }
266
+ _ => todo!("translate statement {:#?}", stmt),
267
+ }
268
+}
269
+
270
+fn convert_variable_declaration(
271
+ cx: &Context,
272
+ decl: &VarDecl,
273
+) -> forget_estree::VariableDeclaration {
274
+ forget_estree::VariableDeclaration {
275
+ kind: convert_decl_kind(&decl.kind),
276
+ declarations: decl
277
+ .decls
278
+ .iter()
279
+ .map(|declarator| forget_estree::VariableDeclarator {
280
+ id: convert_pattern(cx, &declarator.name),
281
+ init: declarator
282
+ .init
283
+ .as_ref()
284
+ .map(|init| convert_expression(cx, init)),
285
+ loc: None,
286
+ range: convert_span(&decl.span),
287
+ })
288
+ .collect(),
289
+ loc: None,
290
+ range: convert_span(&decl.span),
291
+ }
292
+}
293
+
294
+fn convert_expression(cx: &Context, expr: &Expr) -> forget_estree::Expression {
295
+ match expr {
296
+ Expr::Ident(expr) => {
297
+ forget_estree::Expression::Identifier(Box::new(convert_identifier(cx, expr)))
298
+ }
299
+ Expr::Array(expr) => {
300
+ forget_estree::Expression::ArrayExpression(Box::new(forget_estree::ArrayExpression {
301
+ elements: expr
302
+ .elems
303
+ .iter()
304
+ .map(|item| {
305
+ // TODO: represent holes in array expressions
306
+ let value = item.as_ref()?;
307
+ match value.spread {
308
+ Some(spread) => Some(forget_estree::ExpressionOrSpread::SpreadElement(
309
+ Box::new(forget_estree::SpreadElement {
310
+ argument: convert_expression(cx, &value.expr),
311
+ loc: None,
312
+ range: convert_span(&spread),
313
+ }),
314
+ )),
315
+ None => Some(forget_estree::ExpressionOrSpread::Expression(
316
+ convert_expression(cx, &value.expr),
317
+ )),
318
+ }
319
+ })
320
+ .collect(),
321
+ loc: None,
322
+ range: convert_span(&expr.span),
323
+ }))
324
+ }
325
+ Expr::Await(_expr) => {
326
+ // forget_estree::Expression::AwaitExpression(Box::new(forget_estree::AwaitExpression {
327
+ // argument: convert_expression(cx, &expr.arg),
328
+ // loc: None,
329
+ // range: convert_span(&expr.span),
330
+ // }))
331
+ todo!("await expression")
332
+ }
333
+ Expr::Unary(expr) => {
334
+ forget_estree::Expression::UnaryExpression(Box::new(forget_estree::UnaryExpression {
335
+ operator: convert_unary_operator(expr.op),
336
+ prefix: false,
337
+ argument: convert_expression(cx, &expr.arg),
338
+ loc: None,
339
+ range: convert_span(&expr.span),
340
+ }))
341
+ }
342
+ Expr::Bin(expr) => match convert_binary_operator(expr.op) {
343
+ Operator::Binary(op) => forget_estree::Expression::BinaryExpression(Box::new(
344
+ forget_estree::BinaryExpression {
345
+ operator: op,
346
+ left: convert_expression(cx, &expr.left),
347
+ right: convert_expression(cx, &expr.right),
348
+ loc: None,
349
+ range: convert_span(&expr.span),
350
+ },
351
+ )),
352
+ Operator::Logical(op) => forget_estree::Expression::LogicalExpression(Box::new(
353
+ forget_estree::LogicalExpression {
354
+ operator: op,
355
+ left: convert_expression(cx, &expr.left),
356
+ right: convert_expression(cx, &expr.right),
357
+ loc: None,
358
+ range: convert_span(&expr.span),
359
+ },
360
+ )),
361
+ },
362
+ Expr::Lit(expr) => {
363
+ let (value, range) = match expr {
364
+ Lit::Bool(expr) => (
365
+ forget_estree::JsValue::Bool(expr.value),
366
+ convert_span(&expr.span),
367
+ ),
368
+ Lit::Num(expr) => (
369
+ forget_estree::JsValue::Number(expr.value.into()),
370
+ convert_span(&expr.span),
371
+ ),
372
+ Lit::Str(expr) => (
373
+ forget_estree::JsValue::String(expr.value.to_string()),
374
+ convert_span(&expr.span),
375
+ ),
376
+ Lit::Null(expr) => (forget_estree::JsValue::Null, convert_span(&expr.span)),
377
+ _ => todo!(),
378
+ };
379
+ forget_estree::Expression::Literal(Box::new(forget_estree::Literal {
380
+ value,
381
+ raw: None,
382
+ loc: None,
383
+ regex: None,
384
+ range,
385
+ }))
386
+ }
387
+ Expr::Assign(expr) => forget_estree::Expression::AssignmentExpression(Box::new(
388
+ forget_estree::AssignmentExpression {
389
+ operator: convert_assignment_operator(expr.op),
390
+ left: convert_assignment_target(cx, &expr.left),
391
+ right: convert_expression(cx, &expr.right),
392
+ loc: None,
393
+ range: convert_span(&expr.span),
394
+ },
395
+ )),
396
+ Expr::Member(expr) => forget_estree::Expression::MemberExpression(Box::new(
397
+ convert_member_expression(cx, expr),
398
+ )),
399
+ Expr::Fn(expr) => forget_estree::Expression::FunctionExpression(Box::new(
400
+ forget_estree::FunctionExpression {
401
+ function: convert_function(cx, expr.ident.as_ref(), &expr.function),
402
+ loc: None,
403
+ range: convert_span(&expr.function.span),
404
+ },
405
+ )),
406
+ _ => todo!("translate expression {:#?}", expr),
407
+ }
408
+}
409
+
410
+fn convert_assignment_target(cx: &Context, target: &PatOrExpr) -> forget_estree::AssignmentTarget {
411
+ match target {
412
+ PatOrExpr::Pat(target) => {
413
+ forget_estree::AssignmentTarget::Pattern(convert_pattern(cx, target))
414
+ }
415
+ PatOrExpr::Expr(target) => {
416
+ match target.as_ref() {
417
+ Expr::Member(target) => forget_estree::AssignmentTarget::Expression(
418
+ forget_estree::Expression::MemberExpression(Box::new(
419
+ convert_member_expression(cx, target),
420
+ )),
421
+ ),
422
+ Expr::Ident(target) => forget_estree::AssignmentTarget::Pattern(
423
+ forget_estree::Pattern::Identifier(Box::new(convert_identifier(cx, target))),
424
+ ),
425
+ _ => {
426
+ panic!("Expected assignment target to be member expression or identifier, got {:#?}", target)
427
+ }
428
+ }
429
+ }
430
+ }
431
+}
432
+
433
+fn convert_member_expression(cx: &Context, expr: &MemberExpr) -> forget_estree::MemberExpression {
434
+ let (is_computed, property) = match &expr.prop {
435
+ MemberProp::Ident(prop) => (
436
+ false,
437
+ forget_estree::Expression::Identifier(Box::new(convert_identifier(cx, prop))),
438
+ ),
439
+ MemberProp::Computed(prop) => (true, convert_expression(cx, &prop.expr)),
440
+ _ => {
441
+ panic!("PrivateName member expression properties are not supported")
442
+ }
443
+ };
444
+ forget_estree::MemberExpression {
445
+ object: forget_estree::ExpressionOrSuper::Expression(convert_expression(cx, &expr.obj)),
446
+ property,
447
+ computed: is_computed,
448
+ // optional: false, // TODO
449
+ loc: None,
450
+ range: convert_span(&expr.span),
451
+ }
452
+}
453
+
454
+fn convert_unary_operator(op: UnaryOp) -> forget_estree::UnaryOperator {
455
+ match op {
456
+ UnaryOp::Bang => forget_estree::UnaryOperator::Negation,
457
+ UnaryOp::Delete => forget_estree::UnaryOperator::Delete,
458
+ UnaryOp::Minus => forget_estree::UnaryOperator::Minus,
459
+ UnaryOp::Plus => forget_estree::UnaryOperator::Plus,
460
+ UnaryOp::Tilde => forget_estree::UnaryOperator::Tilde,
461
+ UnaryOp::TypeOf => forget_estree::UnaryOperator::Typeof,
462
+ UnaryOp::Void => forget_estree::UnaryOperator::Void,
463
+ }
464
+}
465
+
466
+fn convert_assignment_operator(op: AssignOp) -> forget_estree::AssignmentOperator {
467
+ match op {
468
+ AssignOp::Assign => forget_estree::AssignmentOperator::Equals,
469
+ AssignOp::AddAssign => forget_estree::AssignmentOperator::PlusEquals,
470
+ _ => todo!("translate assignment operator"),
471
+ }
472
+}
473
+
474
+enum Operator {
475
+ Binary(forget_estree::BinaryOperator),
476
+ Logical(forget_estree::LogicalOperator),
477
+}
478
+
479
+fn convert_binary_operator(op: BinaryOp) -> Operator {
480
+ match op {
481
+ BinaryOp::Add => Operator::Binary(forget_estree::BinaryOperator::Add),
482
+ BinaryOp::BitAnd => Operator::Binary(forget_estree::BinaryOperator::BinaryAnd),
483
+ BinaryOp::BitOr => Operator::Binary(forget_estree::BinaryOperator::BinaryOr),
484
+ BinaryOp::BitXor => Operator::Binary(forget_estree::BinaryOperator::BinaryXor),
485
+ BinaryOp::Div => Operator::Binary(forget_estree::BinaryOperator::Divide),
486
+ BinaryOp::EqEq => Operator::Binary(forget_estree::BinaryOperator::Equals),
487
+ BinaryOp::EqEqEq => Operator::Binary(forget_estree::BinaryOperator::StrictEquals),
488
+ // BinaryOp::Exp => Operator::Binary(forget_estree::BinaryOperator::AsteriskAsterisk),
489
+ BinaryOp::Gt => Operator::Binary(forget_estree::BinaryOperator::GreaterThan),
490
+ BinaryOp::GtEq => Operator::Binary(forget_estree::BinaryOperator::GreaterThanOrEqual),
491
+ BinaryOp::In => Operator::Binary(forget_estree::BinaryOperator::In),
492
+ BinaryOp::InstanceOf => Operator::Binary(forget_estree::BinaryOperator::Instanceof),
493
+ BinaryOp::LShift => Operator::Binary(forget_estree::BinaryOperator::ShiftLeft),
494
+ BinaryOp::Lt => Operator::Binary(forget_estree::BinaryOperator::LessThan),
495
+ BinaryOp::LtEq => Operator::Binary(forget_estree::BinaryOperator::LessThanOrEqual),
496
+ BinaryOp::Mod => Operator::Binary(forget_estree::BinaryOperator::Modulo),
497
+ BinaryOp::Mul => Operator::Binary(forget_estree::BinaryOperator::Multiply),
498
+ BinaryOp::NotEq => Operator::Binary(forget_estree::BinaryOperator::NotEquals),
499
+ BinaryOp::NotEqEq => Operator::Binary(forget_estree::BinaryOperator::NotStrictEquals),
500
+ BinaryOp::RShift => Operator::Binary(forget_estree::BinaryOperator::ShiftRight),
501
+ BinaryOp::Sub => Operator::Binary(forget_estree::BinaryOperator::Subtract),
502
+ BinaryOp::ZeroFillRShift => {
503
+ Operator::Binary(forget_estree::BinaryOperator::UnsignedShiftRight)
504
+ }
505
+
506
+ BinaryOp::LogicalAnd => Operator::Logical(forget_estree::LogicalOperator::And),
507
+ BinaryOp::LogicalOr => Operator::Logical(forget_estree::LogicalOperator::Or),
508
+ BinaryOp::NullishCoalescing => {
509
+ Operator::Logical(forget_estree::LogicalOperator::NullCoalescing)
510
+ }
511
+
512
+ _ => panic!("Unsupported binary operator `{}`", op),
513
+ }
514
+}
515
+
516
+fn convert_pattern(cx: &Context, pat: &Pat) -> forget_estree::Pattern {
517
+ match pat {
518
+ Pat::Ident(pat) => {
519
+ forget_estree::Pattern::Identifier(Box::new(forget_estree::Identifier {
520
+ name: pat.id.sym.to_string(),
521
+ binding: convert_binding(cx, pat.id.span.ctxt),
522
+ loc: None,
523
+ range: convert_span(&pat.span),
524
+ }))
525
+ }
526
+ _ => todo!("translate pattern {:#?}", pat),
527
+ }
528
+}
529
+
530
+fn convert_binding(context: &Context, binding_cx: SyntaxContext) -> Option<Binding> {
531
+ let id = BindingId::new(binding_cx.as_u32());
532
+ if binding_cx.as_u32() == context.top_level_mark.as_u32() {
533
+ Some(Binding::Global)
534
+ } else if binding_cx.as_u32() == context.unresolved_mark.as_u32() {
535
+ Some(Binding::Module(id))
536
+ } else {
537
+ Some(Binding::Local(id))
538
+ }
539
+}
540
+
541
+fn convert_identifier(cx: &Context, identifier: &Ident) -> forget_estree::Identifier {
542
+ let name = identifier.sym.as_ref().to_string();
543
+ forget_estree::Identifier {
544
+ name,
545
+ binding: convert_binding(cx, identifier.span.ctxt),
546
+ loc: None,
547
+ range: convert_span(&identifier.span),
548
+ }
549
+}
550
+
551
+#[cfg(test)]
552
+mod tests {
553
+ #[test]
554
+ fn it_works() {}
555
+}