diff --git a/crates/oxc_transformer/src/common/mod.rs b/crates/oxc_transformer/src/common/mod.rs index 3e008854ab13e..ed3465d0c57e2 100644 --- a/crates/oxc_transformer/src/common/mod.rs +++ b/crates/oxc_transformer/src/common/mod.rs @@ -6,13 +6,16 @@ use oxc_traverse::{Traverse, TraverseCtx}; use crate::TransformCtx; +pub mod module_imports; pub mod top_level_statements; pub mod var_declarations; +use module_imports::ModuleImports; use top_level_statements::TopLevelStatements; use var_declarations::VarDeclarations; pub struct Common<'a, 'ctx> { + module_imports: ModuleImports<'a, 'ctx>, var_declarations: VarDeclarations<'a, 'ctx>, top_level_statements: TopLevelStatements<'a, 'ctx>, } @@ -20,6 +23,7 @@ pub struct Common<'a, 'ctx> { impl<'a, 'ctx> Common<'a, 'ctx> { pub fn new(ctx: &'ctx TransformCtx<'a>) -> Self { Self { + module_imports: ModuleImports::new(ctx), var_declarations: VarDeclarations::new(ctx), top_level_statements: TopLevelStatements::new(ctx), } @@ -28,6 +32,7 @@ impl<'a, 'ctx> Common<'a, 'ctx> { impl<'a, 'ctx> Traverse<'a> for Common<'a, 'ctx> { fn exit_program(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) { + self.module_imports.exit_program(program, ctx); self.var_declarations.exit_program(program, ctx); self.top_level_statements.exit_program(program, ctx); } diff --git a/crates/oxc_transformer/src/helpers/module_imports.rs b/crates/oxc_transformer/src/common/module_imports.rs similarity index 52% rename from crates/oxc_transformer/src/helpers/module_imports.rs rename to crates/oxc_transformer/src/common/module_imports.rs index 5d6b17a566b1f..6ca5bd961d95e 100644 --- a/crates/oxc_transformer/src/helpers/module_imports.rs +++ b/crates/oxc_transformer/src/common/module_imports.rs @@ -1,12 +1,62 @@ +//! Utility transform to add `import` / `require` statements to top of program. +//! +//! `ModuleImportsStore` contains an `IndexMap, Vec>>`. +//! It is stored on `TransformCtx`. +//! +//! `ModuleImports` transform +//! +//! Other transforms can add `import`s / `require`s to the store by calling methods of `ModuleImportsStore`: +//! +//! ```rs +//! // import { jsx as _jsx } from 'react'; +//! self.ctx.module_imports.add_import( +//! Atom::from("react"), +//! NamedImport::new(Atom::from("jsx"), Some(Atom::from("_jsx")), symbol_id) +//! ); +//! +//! // var _react = require('react'); +//! self.ctx.module_imports.add_require( +//! Atom::from("react"), +//! NamedImport::new(Atom::from("_react"), None, symbol_id) +//! ); +//! ``` +//! +//! Based on `@babel/helper-module-imports` +//! + use std::cell::RefCell; use indexmap::IndexMap; -use oxc_allocator::Vec; + use oxc_ast::{ast::*, NONE}; use oxc_semantic::ReferenceFlags; use oxc_span::{Atom, SPAN}; use oxc_syntax::symbol::SymbolId; -use oxc_traverse::TraverseCtx; +use oxc_traverse::{Traverse, TraverseCtx}; + +use crate::TransformCtx; + +pub struct ModuleImports<'a, 'ctx> { + ctx: &'ctx TransformCtx<'a>, +} + +impl<'a, 'ctx> ModuleImports<'a, 'ctx> { + pub fn new(ctx: &'ctx TransformCtx<'a>) -> Self { + Self { ctx } + } +} + +impl<'a, 'ctx> Traverse<'a> for ModuleImports<'a, 'ctx> { + fn exit_program(&mut self, _program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) { + let mut imports = self.ctx.module_imports.imports.borrow_mut(); + let Some((first_import_type, _)) = imports.first() else { return }; + // Assume all imports are of the same kind + match first_import_type.kind { + ImportKind::Import => self.insert_import_statements(&mut imports, ctx), + ImportKind::Require => self.insert_require_statements(&mut imports, ctx), + } + } +} pub struct NamedImport<'a> { imported: Atom<'a>, @@ -20,7 +70,7 @@ impl<'a> NamedImport<'a> { } } -#[derive(Hash, Eq, PartialEq)] +#[derive(Clone, Copy, Hash, Eq, PartialEq)] pub enum ImportKind { Import, Require, @@ -38,51 +88,34 @@ impl<'a> ImportType<'a> { } } -/// Manage import statement globally -/// -pub struct ModuleImports<'a> { - imports: RefCell, std::vec::Vec>>>, -} - -impl<'a> ModuleImports<'a> { - pub fn new() -> ModuleImports<'a> { - Self { imports: RefCell::new(IndexMap::default()) } - } - - /// Add `import { named_import } from 'source'` - pub fn add_import(&self, source: Atom<'a>, import: NamedImport<'a>) { - self.imports - .borrow_mut() - .entry(ImportType::new(ImportKind::Import, source)) - .or_default() - .push(import); - } - - /// Add `var named_import from 'source'` - pub fn add_require(&self, source: Atom<'a>, import: NamedImport<'a>, front: bool) { - let len = self.imports.borrow().len(); - self.imports - .borrow_mut() - .entry(ImportType::new(ImportKind::Require, source)) - .or_default() - .push(import); - if front { - self.imports.borrow_mut().move_index(len, 0); - } +impl<'a, 'ctx> ModuleImports<'a, 'ctx> { + fn insert_import_statements( + &mut self, + imports: &mut IndexMap, Vec>>, + ctx: &mut TraverseCtx<'a>, + ) { + let stmts = imports.drain(..).map(|(import_type, names)| { + debug_assert!(import_type.kind == ImportKind::Import); + Self::get_named_import(import_type.source, names, ctx) + }); + self.ctx.top_level_statements.insert_statements(stmts); } - pub fn get_import_statements(&self, ctx: &mut TraverseCtx<'a>) -> Vec<'a, Statement<'a>> { - ctx.ast.vec_from_iter(self.imports.borrow_mut().drain(..).map(|(import_type, names)| { - match import_type.kind { - ImportKind::Import => Self::get_named_import(import_type.source, names, ctx), - ImportKind::Require => Self::get_require(import_type.source, names, ctx), - } - })) + fn insert_require_statements( + &mut self, + imports: &mut IndexMap, Vec>>, + ctx: &mut TraverseCtx<'a>, + ) { + let stmts = imports.drain(..).map(|(import_type, names)| { + debug_assert!(import_type.kind == ImportKind::Require); + Self::get_require(import_type.source, names, ctx) + }); + self.ctx.top_level_statements.insert_statements(stmts); } fn get_named_import( source: Atom<'a>, - names: std::vec::Vec>, + names: Vec>, ctx: &mut TraverseCtx<'a>, ) -> Statement<'a> { let specifiers = ctx.ast.vec_from_iter(names.into_iter().map(|name| { @@ -137,3 +170,44 @@ impl<'a> ModuleImports<'a> { ctx.ast.statement_declaration(var_decl) } } + +/// Store for `import` / `require` statements to be added at top of program +pub struct ModuleImportsStore<'a> { + imports: RefCell, Vec>>>, +} + +impl<'a> ModuleImportsStore<'a> { + pub fn new() -> ModuleImportsStore<'a> { + Self { imports: RefCell::new(IndexMap::default()) } + } + + /// Add `import { named_import } from 'source'` + pub fn add_import(&self, source: Atom<'a>, import: NamedImport<'a>) { + self.imports + .borrow_mut() + .entry(ImportType::new(ImportKind::Import, source)) + .or_default() + .push(import); + } + + /// Add `var named_import from 'source'`. + /// + /// If `front` is true, `require` is added to top of the `require`s. + /// TODO(improve-on-babel): `front` option is only required to pass one of Babel's tests. Output + /// without it is still valid. Remove this once our output doesn't need to match Babel exactly. + pub fn add_require(&self, source: Atom<'a>, import: NamedImport<'a>, front: bool) { + let len = self.imports.borrow().len(); + self.imports + .borrow_mut() + .entry(ImportType::new(ImportKind::Require, source)) + .or_default() + .push(import); + if front { + self.imports.borrow_mut().move_index(len, 0); + } + } + + pub fn is_empty(&self) -> bool { + self.imports.borrow().is_empty() + } +} diff --git a/crates/oxc_transformer/src/common/top_level_statements.rs b/crates/oxc_transformer/src/common/top_level_statements.rs index bf28330dfe64f..120d1e1c3a5af 100644 --- a/crates/oxc_transformer/src/common/top_level_statements.rs +++ b/crates/oxc_transformer/src/common/top_level_statements.rs @@ -67,4 +67,9 @@ impl<'a> TopLevelStatementsStore<'a> { pub fn insert_statement(&self, stmt: Statement<'a>) { self.stmts.borrow_mut().push(stmt); } + + /// Add statements to be inserted at top of program. + pub fn insert_statements>>(&self, stmts: I) { + self.stmts.borrow_mut().extend(stmts); + } } diff --git a/crates/oxc_transformer/src/context.rs b/crates/oxc_transformer/src/context.rs index 2dbfce8894e35..55b270a121e26 100644 --- a/crates/oxc_transformer/src/context.rs +++ b/crates/oxc_transformer/src/context.rs @@ -11,9 +11,9 @@ use oxc_span::SourceType; use crate::{ common::{ - top_level_statements::TopLevelStatementsStore, var_declarations::VarDeclarationsStore, + module_imports::ModuleImportsStore, top_level_statements::TopLevelStatementsStore, + var_declarations::VarDeclarationsStore, }, - helpers::module_imports::ModuleImports, TransformOptions, }; @@ -36,7 +36,7 @@ pub struct TransformCtx<'a> { // Helpers /// Manage import statement globally - pub module_imports: ModuleImports<'a>, + pub module_imports: ModuleImportsStore<'a>, /// Manage inserting `var` statements globally pub var_declarations: VarDeclarationsStore<'a>, /// Manage inserting statements at top of program globally @@ -68,7 +68,7 @@ impl<'a> TransformCtx<'a> { source_type, source_text, trivias, - module_imports: ModuleImports::new(), + module_imports: ModuleImportsStore::new(), var_declarations: VarDeclarationsStore::new(), top_level_statements: TopLevelStatementsStore::new(), } diff --git a/crates/oxc_transformer/src/lib.rs b/crates/oxc_transformer/src/lib.rs index c98189dcd885f..a6c8868c082f1 100644 --- a/crates/oxc_transformer/src/lib.rs +++ b/crates/oxc_transformer/src/lib.rs @@ -29,7 +29,6 @@ mod plugins; mod helpers { pub mod bindings; - pub mod module_imports; pub mod stack; } diff --git a/crates/oxc_transformer/src/react/jsx.rs b/crates/oxc_transformer/src/react/jsx.rs index 15531e910e7b5..9f6ed593a1e0d 100644 --- a/crates/oxc_transformer/src/react/jsx.rs +++ b/crates/oxc_transformer/src/react/jsx.rs @@ -106,8 +106,7 @@ pub use super::{ options::{JsxOptions, JsxRuntime}, }; use crate::{ - helpers::{bindings::BoundIdentifier, module_imports::NamedImport}, - TransformCtx, + common::module_imports::NamedImport, helpers::bindings::BoundIdentifier, TransformCtx, }; pub struct ReactJsx<'a, 'ctx> { @@ -170,6 +169,9 @@ impl<'a, 'ctx> AutomaticScriptBindings<'a, 'ctx> { if self.require_create_element.is_none() { let source = get_import_source(self.jsx_runtime_importer.as_str(), self.react_importer_len); + // We have to insert this `require` above `require("react/jsx-runtime")` + // just to pass one of Babel's tests, but the order doesn't actually matter. + // TODO(improve-on-babel): Remove this once we don't need our output to match Babel exactly. let id = self.add_require_statement("react", source, true, ctx); self.require_create_element = Some(id); } @@ -444,8 +446,8 @@ impl<'a, 'ctx> ReactJsx<'a, 'ctx> { } impl<'a, 'ctx> Traverse<'a> for ReactJsx<'a, 'ctx> { - fn exit_program(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) { - self.add_runtime_imports(program, ctx); + fn exit_program(&mut self, _program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) { + self.insert_var_file_name_statement(ctx); } fn exit_expression(&mut self, expr: &mut Expression<'a>, ctx: &mut TraverseCtx<'a>) { @@ -468,31 +470,26 @@ impl<'a, 'ctx> ReactJsx<'a, 'ctx> { self.ctx.ast } - fn add_runtime_imports(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) { - if self.bindings.is_classic() { - if let Some(stmt) = self.jsx_source.get_var_file_name_statement() { - program.body.insert(0, stmt); - } - return; - } - - let imports = self.ctx.module_imports.get_import_statements(ctx); - let mut index = program - .body - .iter() - .rposition(|stmt| matches!(stmt, Statement::ImportDeclaration(_))) - .map_or(0, |i| i + 1); - - if let Some(stmt) = self.jsx_source.get_var_file_name_statement() { - program.body.insert(index, stmt); - // If source type is module then we need to add the import statement after the var file name statement - // Follow the same behavior as babel - if !self.is_script() { - index += 1; - } + fn insert_var_file_name_statement(&mut self, ctx: &mut TraverseCtx<'a>) { + let Some(declarator) = self.jsx_source.get_var_file_name_declarator() else { return }; + + // If is a module, add filename statements before `import`s. If script, then after `require`s. + // This is the same behavior as Babel. + // If in classic mode, then there are no import statements, so it doesn't matter either way. + // TODO(improve-on-babel): Simplify this once we don't need to follow Babel exactly. + if self.bindings.is_classic() || !self.is_script() { + // Insert before imports - add to `top_level_statements` immediately + let stmt = Statement::VariableDeclaration(ctx.ast.alloc_variable_declaration( + SPAN, + VariableDeclarationKind::Var, + self.ctx.ast.vec1(declarator), + false, + )); + self.ctx.top_level_statements.insert_statement(stmt); + } else { + // Insert after imports - add to `var_declarations`, which are inserted after `require` statements + self.ctx.var_declarations.insert_declarator(declarator, ctx); } - - program.body.splice(index..index, imports); } fn transform_jsx<'b>( diff --git a/crates/oxc_transformer/src/react/jsx_source.rs b/crates/oxc_transformer/src/react/jsx_source.rs index 330f5086d25d9..e35c6dca85e90 100644 --- a/crates/oxc_transformer/src/react/jsx_source.rs +++ b/crates/oxc_transformer/src/react/jsx_source.rs @@ -59,9 +59,9 @@ impl<'a, 'ctx> ReactJsxSource<'a, 'ctx> { } impl<'a, 'ctx> Traverse<'a> for ReactJsxSource<'a, 'ctx> { - fn exit_program(&mut self, program: &mut Program<'a>, _ctx: &mut TraverseCtx<'a>) { + fn exit_program(&mut self, _program: &mut Program<'a>, _ctx: &mut TraverseCtx<'a>) { if let Some(stmt) = self.get_var_file_name_statement() { - program.body.insert(0, stmt); + self.ctx.top_level_statements.insert_statement(stmt); } } @@ -190,7 +190,19 @@ impl<'a, 'ctx> ReactJsxSource<'a, 'ctx> { self.ctx.ast.expression_object(SPAN, properties, None) } - pub fn get_var_file_name_statement(&mut self) -> Option> { + pub fn get_var_file_name_statement(&self) -> Option> { + let decl = self.get_var_file_name_declarator()?; + + let var_decl = Statement::VariableDeclaration(self.ctx.ast.alloc_variable_declaration( + SPAN, + VariableDeclarationKind::Var, + self.ctx.ast.vec1(decl), + false, + )); + Some(var_decl) + } + + pub fn get_var_file_name_declarator(&self) -> Option> { let filename_var = self.filename_var.as_ref()?; let var_kind = VariableDeclarationKind::Var; @@ -204,11 +216,9 @@ impl<'a, 'ctx> ReactJsxSource<'a, 'ctx> { .ctx .ast .expression_string_literal(SPAN, self.ctx.source_path.to_string_lossy()); - let decl = self.ctx.ast.variable_declarator(SPAN, var_kind, id, Some(init), false); - self.ctx.ast.vec1(decl) + self.ctx.ast.variable_declarator(SPAN, var_kind, id, Some(init), false) }; - let var_decl = self.ctx.ast.alloc_variable_declaration(SPAN, var_kind, decl, false); - Some(Statement::VariableDeclaration(var_decl)) + Some(decl) } fn get_filename_var(&mut self, ctx: &mut TraverseCtx<'a>) -> BoundIdentifier<'a> { diff --git a/crates/oxc_transformer/src/typescript/annotations.rs b/crates/oxc_transformer/src/typescript/annotations.rs index 6246086349c21..b6f608800e22f 100644 --- a/crates/oxc_transformer/src/typescript/annotations.rs +++ b/crates/oxc_transformer/src/typescript/annotations.rs @@ -151,7 +151,7 @@ impl<'a, 'ctx> Traverse<'a> for TypeScriptAnnotations<'a, 'ctx> { // Determine if we still have import/export statements, otherwise we // need to inject an empty statement (`export {}`) so that the file is // still considered a module - if no_modules_remaining && some_modules_deleted { + if no_modules_remaining && some_modules_deleted && self.ctx.module_imports.is_empty() { let export_decl = ModuleDeclaration::ExportNamedDeclaration( self.ctx.ast.plain_export_named_declaration(SPAN, self.ctx.ast.vec(), None), ); diff --git a/tasks/coverage/snapshots/semantic_typescript.snap b/tasks/coverage/snapshots/semantic_typescript.snap index 213b53397e26a..6cdcbd5f1da1c 100644 --- a/tasks/coverage/snapshots/semantic_typescript.snap +++ b/tasks/coverage/snapshots/semantic_typescript.snap @@ -1530,7 +1530,7 @@ after transform: ScopeId(20): [ScopeId(21), ScopeId(22)] rebuilt : ScopeId(13): [] Symbol reference IDs mismatch: after transform: SymbolId(0): [ReferenceId(28), ReferenceId(32), ReferenceId(33), ReferenceId(36), ReferenceId(38), ReferenceId(42), ReferenceId(44), ReferenceId(46)] -rebuilt : SymbolId(1): [ReferenceId(24), ReferenceId(28), ReferenceId(31)] +rebuilt : SymbolId(0): [ReferenceId(24), ReferenceId(28), ReferenceId(31)] tasks/coverage/typescript/tests/cases/compiler/capturedLetConstInLoop1.ts semantic error: Bindings mismatch: @@ -5488,7 +5488,7 @@ after transform: ScopeId(0): [ScopeId(1), ScopeId(8), ScopeId(9)] rebuilt : ScopeId(0): [ScopeId(1), ScopeId(2)] Symbol reference IDs mismatch: after transform: SymbolId(0): [ReferenceId(6), ReferenceId(15), ReferenceId(16), ReferenceId(18), ReferenceId(21), ReferenceId(22), ReferenceId(24)] -rebuilt : SymbolId(1): [ReferenceId(0), ReferenceId(3), ReferenceId(5), ReferenceId(8), ReferenceId(10), ReferenceId(12)] +rebuilt : SymbolId(0): [ReferenceId(0), ReferenceId(3), ReferenceId(5), ReferenceId(8), ReferenceId(10), ReferenceId(12)] Reference symbol mismatch: after transform: ReferenceId(8): Some("DropdownMenu") rebuilt : ReferenceId(1): None @@ -21018,7 +21018,7 @@ after transform: ScopeId(0): [ScopeId(1), ScopeId(2), ScopeId(3), ScopeId(4)] rebuilt : ScopeId(0): [ScopeId(1), ScopeId(2)] Symbol reference IDs mismatch: after transform: SymbolId(0): [ReferenceId(0), ReferenceId(3), ReferenceId(7), ReferenceId(9), ReferenceId(11), ReferenceId(13)] -rebuilt : SymbolId(1): [ReferenceId(0), ReferenceId(2), ReferenceId(3), ReferenceId(6), ReferenceId(8)] +rebuilt : SymbolId(0): [ReferenceId(0), ReferenceId(2), ReferenceId(3), ReferenceId(6), ReferenceId(8)] tasks/coverage/typescript/tests/cases/compiler/jsxComplexSignatureHasApplicabilityError.tsx semantic error: Bindings mismatch: @@ -21032,7 +21032,7 @@ after transform: ScopeId(6): ["WrappedComponent", "WrappedProps"] rebuilt : ScopeId(1): ["WrappedComponent"] Symbol reference IDs mismatch: after transform: SymbolId(0): [ReferenceId(17), ReferenceId(19), ReferenceId(44), ReferenceId(50), ReferenceId(54), ReferenceId(82), ReferenceId(85), ReferenceId(88), ReferenceId(102), ReferenceId(180), ReferenceId(195), ReferenceId(206), ReferenceId(209), ReferenceId(233), ReferenceId(236), ReferenceId(240)] -rebuilt : SymbolId(1): [ReferenceId(0)] +rebuilt : SymbolId(0): [ReferenceId(0)] Unresolved references mismatch: after transform: ["Array", "Exclude", "HTMLAnchorElement", "HTMLDivElement", "HTMLInputElement", "JSX", "Pick", "Promise", "ReactSelectClass", "undefined"] rebuilt : ["ReactSelectClass", "undefined"] @@ -21306,7 +21306,7 @@ after transform: ScopeId(0): [ScopeId(1)] rebuilt : ScopeId(0): [] Symbol reference IDs mismatch: after transform: SymbolId(0): [ReferenceId(0), ReferenceId(6)] -rebuilt : SymbolId(1): [ReferenceId(0)] +rebuilt : SymbolId(0): [ReferenceId(0)] Reference symbol mismatch: after transform: ReferenceId(4): Some("Comp") rebuilt : ReferenceId(1): None @@ -30410,7 +30410,7 @@ after transform: ScopeId(1): ["Inner", "P"] rebuilt : ScopeId(1): ["Inner"] Symbol reference IDs mismatch: after transform: SymbolId(0): [ReferenceId(0), ReferenceId(2), ReferenceId(4), ReferenceId(8)] -rebuilt : SymbolId(1): [ReferenceId(0), ReferenceId(1)] +rebuilt : SymbolId(0): [ReferenceId(0), ReferenceId(1)] tasks/coverage/typescript/tests/cases/compiler/reactSFCAndFunctionResolvable.tsx semantic error: Bindings mismatch: @@ -30418,7 +30418,7 @@ after transform: ScopeId(0): ["Checkbox", "OtherRadio", "Radio", "RandomComponen rebuilt : ScopeId(0): ["RandomComponent", "React", "_jsxFileName"] Symbol reference IDs mismatch: after transform: SymbolId(0): [ReferenceId(0), ReferenceId(1), ReferenceId(2), ReferenceId(3), ReferenceId(14), ReferenceId(16)] -rebuilt : SymbolId(1): [ReferenceId(7), ReferenceId(10)] +rebuilt : SymbolId(0): [ReferenceId(7), ReferenceId(10)] Reference symbol mismatch: after transform: ReferenceId(4): Some("condition1") rebuilt : ReferenceId(0): None @@ -30450,7 +30450,7 @@ after transform: ScopeId(0): ["React", "Tag", "_jsxFileName", "children", "class rebuilt : ScopeId(0): ["React", "_jsxFileName", "children", "classes", "rest"] Symbol reference IDs mismatch: after transform: SymbolId(0): [ReferenceId(0), ReferenceId(7)] -rebuilt : SymbolId(1): [ReferenceId(0)] +rebuilt : SymbolId(0): [ReferenceId(0)] Reference symbol mismatch: after transform: ReferenceId(1): Some("Tag") rebuilt : ReferenceId(1): None @@ -30464,7 +30464,7 @@ after transform: ScopeId(0): ["React", "Tag", "_jsxFileName", "children", "class rebuilt : ScopeId(0): ["React", "_jsxFileName", "children", "classes", "rest"] Symbol reference IDs mismatch: after transform: SymbolId(0): [ReferenceId(0), ReferenceId(1), ReferenceId(9)] -rebuilt : SymbolId(1): [ReferenceId(0)] +rebuilt : SymbolId(0): [ReferenceId(0)] Reference symbol mismatch: after transform: ReferenceId(3): Some("Tag") rebuilt : ReferenceId(1): None @@ -35341,7 +35341,7 @@ after transform: ScopeId(0): [ScopeId(1), ScopeId(2), ScopeId(3), ScopeId(4)] rebuilt : ScopeId(0): [ScopeId(1)] Symbol reference IDs mismatch: after transform: SymbolId(0): [ReferenceId(0), ReferenceId(3), ReferenceId(7), ReferenceId(12)] -rebuilt : SymbolId(1): [ReferenceId(0)] +rebuilt : SymbolId(0): [ReferenceId(0)] Unresolved references mismatch: after transform: ["Button", "HTMLButtonElement"] rebuilt : ["Button"]