Skip to content

Commit 3b9955e

Browse files
Brooooooklynclaude
andcommitted
fix(angular): use module source type and add fast path for TS stripping
- Use SourceType::ts().with_module(true) instead of script-mode ts() so import.meta and ESM-only syntax parse correctly in RawSource fallback expressions. - Add fast path: try parsing as .mjs first. If the expression is already valid JavaScript (no type annotations), return it as-is without running the heavier semantic→transform→codegen pipeline. Only expressions with actual TypeScript syntax pay the full cost. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent d70418d commit 3b9955e

File tree

1 file changed

+23
-9
lines changed

1 file changed

+23
-9
lines changed

crates/oxc_angular_compiler/src/output/oxc_converter.rs

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -698,18 +698,33 @@ fn make_raw_source<'a>(
698698

699699
/// Strip TypeScript type annotations from an expression source string.
700700
///
701-
/// Wraps the expression in a minimal program (`0,(expr)`), parses as TypeScript,
702-
/// runs the TypeScript transformer to strip types, and codegens to JavaScript.
703-
/// Returns the original source if stripping fails.
701+
/// Fast path: tries parsing as ESM JavaScript first. If the expression is
702+
/// already valid JS (no type annotations), returns it as-is without running
703+
/// the heavier semantic/transform/codegen pipeline.
704+
///
705+
/// Slow path: if JS parsing fails (likely due to TS syntax), wraps the
706+
/// expression, parses as TypeScript module, strips types via transformer,
707+
/// and codegens to JavaScript.
704708
fn strip_expression_types(expr_source: &str) -> String {
705709
use std::path::Path;
706710

711+
// Fast path: try parsing as JS module. If it succeeds, the expression
712+
// is already valid JavaScript — return as-is without transformation.
713+
{
714+
let allocator = oxc_allocator::Allocator::default();
715+
let wrapped = format!("0,({expr_source})");
716+
let source_type = oxc_span::SourceType::mjs();
717+
let parser_ret = oxc_parser::Parser::new(&allocator, &wrapped, source_type).parse();
718+
if !parser_ret.panicked && parser_ret.errors.is_empty() {
719+
return expr_source.to_string();
720+
}
721+
}
722+
723+
// Slow path: expression contains TypeScript syntax — run full pipeline.
707724
let allocator = oxc_allocator::Allocator::default();
708-
// Use comma operator + parens to create a valid single expression statement.
709-
// This handles expressions that would be ambiguous at statement level
710-
// (e.g., arrow functions, object literals).
711725
let wrapped = format!("0,({expr_source})");
712-
let source_type = oxc_span::SourceType::ts();
726+
// Use module TypeScript so import.meta and ESM syntax are valid.
727+
let source_type = oxc_span::SourceType::ts().with_module(true);
713728
let parser_ret = oxc_parser::Parser::new(&allocator, &wrapped, source_type).parse();
714729

715730
if parser_ret.panicked {
@@ -725,14 +740,13 @@ fn strip_expression_types(expr_source: &str) -> String {
725740
..Default::default()
726741
};
727742
let transformer =
728-
oxc_transformer::Transformer::new(&allocator, Path::new("_.ts"), &transform_options);
743+
oxc_transformer::Transformer::new(&allocator, Path::new("_.mts"), &transform_options);
729744
transformer.build_with_scoping(semantic_ret.semantic.into_scoping(), &mut program);
730745

731746
let codegen_ret = oxc_codegen::Codegen::new().with_source_text(&wrapped).build(&program);
732747

733748
// Strip the wrapper: codegen produces "0, (expr);\n" → extract "expr"
734749
let code = codegen_ret.code.trim_end();
735-
// The codegen may format as "0, (expr);" or "0, (expr);\n"
736750
if let Some(rest) = code.strip_prefix("0, (").or_else(|| code.strip_prefix("0,(")) {
737751
if let Some(inner) = rest.strip_suffix(");") {
738752
return inner.to_string();

0 commit comments

Comments
 (0)