mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 04:08:41 +00:00
fix(transformer/typescript): remove type-only import = when only_remove_type_imports is true (#8275)
close: https://github.com/oxc-project/oxc/issues/8230 close: https://github.com/rolldown/rolldown/issues/3287 Related PR in Babel: https://github.com/oxc-project/oxc/issues/8230 I have compared our output with TypeScript, and it is the same as `TypeScript`, Babel's implementation currently hasn't removed imports referenced by type-only `TSImportEqualsDeclaration`
This commit is contained in:
parent
d56020b84c
commit
9a03bd23b9
13 changed files with 367 additions and 340 deletions
|
|
@ -143,9 +143,12 @@ impl<'a> Traverse<'a> for TypeScriptAnnotations<'a, '_> {
|
|||
true
|
||||
}
|
||||
}
|
||||
Statement::TSExportAssignment(_) | Statement::TSNamespaceExportDeclaration(_) => {
|
||||
false
|
||||
}
|
||||
// `import Binding = X.Y.Z`
|
||||
// `Binding` can be referenced as a value or a type, but here we already know it only as a type
|
||||
// See `TypeScriptModule::transform_ts_import_equals`
|
||||
Statement::TSTypeAliasDeclaration(_)
|
||||
| Statement::TSExportAssignment(_)
|
||||
| Statement::TSNamespaceExportDeclaration(_) => false,
|
||||
_ => return true,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ impl<'a, 'ctx> TypeScript<'a, 'ctx> {
|
|||
annotations: TypeScriptAnnotations::new(options, ctx),
|
||||
r#enum: TypeScriptEnum::new(),
|
||||
namespace: TypeScriptNamespace::new(options, ctx),
|
||||
module: TypeScriptModule::new(ctx),
|
||||
module: TypeScriptModule::new(options.only_remove_type_imports, ctx),
|
||||
rewrite_extensions: TypeScriptRewriteExtensions::new(options),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use oxc_ast::{ast::*, NONE};
|
||||
use oxc_semantic::Reference;
|
||||
use oxc_span::SPAN;
|
||||
use oxc_syntax::reference::ReferenceFlags;
|
||||
use oxc_traverse::{Traverse, TraverseCtx};
|
||||
|
|
@ -7,12 +8,14 @@ use super::diagnostics;
|
|||
use crate::TransformCtx;
|
||||
|
||||
pub struct TypeScriptModule<'a, 'ctx> {
|
||||
/// <https://babeljs.io/docs/babel-plugin-transform-typescript#onlyremovetypeimports>
|
||||
only_remove_type_imports: bool,
|
||||
ctx: &'ctx TransformCtx<'a>,
|
||||
}
|
||||
|
||||
impl<'a, 'ctx> TypeScriptModule<'a, 'ctx> {
|
||||
pub fn new(ctx: &'ctx TransformCtx<'a>) -> Self {
|
||||
Self { ctx }
|
||||
pub fn new(only_remove_type_imports: bool, ctx: &'ctx TransformCtx<'a>) -> Self {
|
||||
Self { only_remove_type_imports, ctx }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -37,7 +40,9 @@ impl<'a> Traverse<'a> for TypeScriptModule<'a, '_> {
|
|||
fn enter_declaration(&mut self, decl: &mut Declaration<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
if let Declaration::TSImportEqualsDeclaration(import_equals) = decl {
|
||||
if import_equals.import_kind.is_value() {
|
||||
*decl = self.transform_ts_import_equals(import_equals, ctx);
|
||||
if let Some(new_decl) = self.transform_ts_import_equals(import_equals, ctx) {
|
||||
*decl = new_decl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -88,7 +93,29 @@ impl<'a> TypeScriptModule<'a, '_> {
|
|||
&self,
|
||||
decl: &mut TSImportEqualsDeclaration<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) -> Declaration<'a> {
|
||||
) -> Option<Declaration<'a>> {
|
||||
if !self.only_remove_type_imports
|
||||
&& !ctx.parent().is_export_named_declaration()
|
||||
&& ctx.symbols().get_resolved_references(decl.id.symbol_id()).all(Reference::is_type)
|
||||
{
|
||||
// No value reference, we will remove this declaration in `TypeScriptAnnotations`
|
||||
match &mut decl.module_reference {
|
||||
module_reference @ match_ts_type_name!(TSModuleReference) => {
|
||||
let ident = module_reference.to_ts_type_name().get_identifier_reference();
|
||||
let reference = ctx.symbols_mut().get_reference_mut(ident.reference_id());
|
||||
// The binding of TSImportEqualsDeclaration has treated as a type reference,
|
||||
// so an identifier reference that it referenced also should be treated as a type reference.
|
||||
// `import TypeBinding = X.Y.Z`
|
||||
// ^ `X` should be treated as a type reference.
|
||||
let flags = reference.flags_mut();
|
||||
debug_assert_eq!(*flags, ReferenceFlags::Read);
|
||||
*flags = ReferenceFlags::Type;
|
||||
}
|
||||
TSModuleReference::ExternalModuleReference(_) => {}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
let binding_pattern_kind =
|
||||
ctx.ast.binding_pattern_kind_binding_identifier(SPAN, &decl.id.name);
|
||||
let binding = ctx.ast.binding_pattern(binding_pattern_kind, NONE, false);
|
||||
|
|
@ -123,7 +150,7 @@ impl<'a> TypeScriptModule<'a, '_> {
|
|||
let decls =
|
||||
ctx.ast.vec1(ctx.ast.variable_declarator(SPAN, kind, binding, Some(init), false));
|
||||
|
||||
ctx.ast.declaration_variable(SPAN, kind, decls, false)
|
||||
Some(ctx.ast.declaration_variable(SPAN, kind, decls, false))
|
||||
}
|
||||
|
||||
#[allow(clippy::only_used_in_recursion)]
|
||||
|
|
|
|||
|
|
@ -138,13 +138,19 @@ describe('modules', () => {
|
|||
const code = `
|
||||
export = function foo (): void {}
|
||||
import bar = require('bar')
|
||||
console.log(bar)
|
||||
`;
|
||||
const ret = transform('test.ts', code, {
|
||||
typescript: {
|
||||
declaration: {},
|
||||
},
|
||||
});
|
||||
expect(ret.code).toEqual('module.exports = function foo() {};\nconst bar = require("bar");\n');
|
||||
expect(ret.code).toMatchInlineSnapshot(`
|
||||
"module.exports = function foo() {};
|
||||
const bar = require("bar");
|
||||
console.log(bar);
|
||||
"
|
||||
`);
|
||||
expect(ret.declaration).toEqual('declare const _default: () => void;\nexport = _default;\n');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -130,10 +130,9 @@ after transform: ["Y", "foo"]
|
|||
rebuilt : []
|
||||
|
||||
tasks/coverage/babel/packages/babel-parser/test/fixtures/estree/typescript/import-require/input.js
|
||||
semantic error: Missing SymbolId: "x"
|
||||
Binding symbols mismatch:
|
||||
after transform: ScopeId(0): [SymbolId(0)]
|
||||
rebuilt : ScopeId(0): [SymbolId(0)]
|
||||
semantic error: Bindings mismatch:
|
||||
after transform: ScopeId(0): ["x"]
|
||||
rebuilt : ScopeId(0): []
|
||||
|
||||
tasks/coverage/babel/packages/babel-parser/test/fixtures/estree/typescript/literals/input.js
|
||||
semantic error: Scope children mismatch:
|
||||
|
|
@ -723,28 +722,30 @@ after transform: ScopeId(0): [ScopeId(1), ScopeId(2)]
|
|||
rebuilt : ScopeId(0): []
|
||||
|
||||
tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/import/equals/input.ts
|
||||
semantic error: Missing SymbolId: "A"
|
||||
Binding symbols mismatch:
|
||||
after transform: ScopeId(0): [SymbolId(0)]
|
||||
rebuilt : ScopeId(0): [SymbolId(0)]
|
||||
semantic error: Bindings mismatch:
|
||||
after transform: ScopeId(0): ["A"]
|
||||
rebuilt : ScopeId(0): []
|
||||
Unresolved references mismatch:
|
||||
after transform: ["B"]
|
||||
rebuilt : []
|
||||
|
||||
tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/import/equals-in-unambiguous/input.ts
|
||||
semantic error: Missing SymbolId: "A"
|
||||
Binding symbols mismatch:
|
||||
after transform: ScopeId(0): [SymbolId(0)]
|
||||
rebuilt : ScopeId(0): [SymbolId(0)]
|
||||
semantic error: Bindings mismatch:
|
||||
after transform: ScopeId(0): ["A"]
|
||||
rebuilt : ScopeId(0): []
|
||||
Unresolved references mismatch:
|
||||
after transform: ["B"]
|
||||
rebuilt : []
|
||||
|
||||
tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/import/equals-require/input.ts
|
||||
semantic error: Missing SymbolId: "a"
|
||||
Binding symbols mismatch:
|
||||
after transform: ScopeId(0): [SymbolId(0)]
|
||||
rebuilt : ScopeId(0): [SymbolId(0)]
|
||||
semantic error: Bindings mismatch:
|
||||
after transform: ScopeId(0): ["a"]
|
||||
rebuilt : ScopeId(0): []
|
||||
|
||||
tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/import/equals-require-in-unambiguous/input.ts
|
||||
semantic error: Missing SymbolId: "a"
|
||||
Binding symbols mismatch:
|
||||
after transform: ScopeId(0): [SymbolId(0)]
|
||||
rebuilt : ScopeId(0): [SymbolId(0)]
|
||||
semantic error: Bindings mismatch:
|
||||
after transform: ScopeId(0): ["a"]
|
||||
rebuilt : ScopeId(0): []
|
||||
|
||||
tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/import/export-import/input.ts
|
||||
semantic error: Missing SymbolId: "A"
|
||||
|
|
@ -799,10 +800,9 @@ after transform: ScopeId(0): ["a"]
|
|||
rebuilt : ScopeId(0): []
|
||||
|
||||
tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/import/import-type-as-identifier/input.ts
|
||||
semantic error: Missing SymbolId: "type"
|
||||
Binding symbols mismatch:
|
||||
after transform: ScopeId(0): [SymbolId(0)]
|
||||
rebuilt : ScopeId(0): [SymbolId(0)]
|
||||
semantic error: Bindings mismatch:
|
||||
after transform: ScopeId(0): ["type"]
|
||||
rebuilt : ScopeId(0): []
|
||||
|
||||
tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/import/internal-comments/input.ts
|
||||
semantic error: Bindings mismatch:
|
||||
|
|
@ -1431,8 +1431,7 @@ after transform: ScopeId(0): [ScopeId(1)]
|
|||
rebuilt : ScopeId(0): []
|
||||
|
||||
tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/scope/redeclaration-import-equals-var/input.ts
|
||||
semantic error: Missing SymbolId: "a"
|
||||
Bindings mismatch:
|
||||
semantic error: Bindings mismatch:
|
||||
after transform: ScopeId(0): ["M", "a"]
|
||||
rebuilt : ScopeId(0): ["a"]
|
||||
Scope children mismatch:
|
||||
|
|
@ -1443,7 +1442,13 @@ after transform: SymbolId(1): SymbolFlags(FunctionScopedVariable | Import)
|
|||
rebuilt : SymbolId(0): SymbolFlags(FunctionScopedVariable)
|
||||
Symbol span mismatch for "a":
|
||||
after transform: SymbolId(1): Span { start: 20, end: 21 }
|
||||
rebuilt : SymbolId(0): Span { start: 0, end: 0 }
|
||||
rebuilt : SymbolId(0): Span { start: 31, end: 32 }
|
||||
Symbol redeclarations mismatch for "a":
|
||||
after transform: SymbolId(1): [Span { start: 31, end: 32 }]
|
||||
rebuilt : SymbolId(0): []
|
||||
Unresolved references mismatch:
|
||||
after transform: ["M"]
|
||||
rebuilt : []
|
||||
|
||||
tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/scope/redeclaration-import-let/input.ts
|
||||
semantic error: Symbol flags mismatch for "Context":
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1 @@
|
|||
export {};
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
var AliasModule = LongNameModule;
|
||||
const some = 3;
|
||||
const bar = AliasModule.foo;
|
||||
const baz = AliasModule.Inner.bar;
|
||||
let str;
|
||||
let node;
|
||||
console.log(some);
|
||||
export {};
|
||||
|
|
@ -2060,11 +2060,9 @@ after transform: ScopeId(0): ["React", "x"]
|
|||
rebuilt : ScopeId(0): ["x"]
|
||||
|
||||
* imports/elide-type-referenced-in-imports-equal-no/input.ts
|
||||
Missing SymbolId: "foo"
|
||||
Missing SymbolId: "bar"
|
||||
Binding symbols mismatch:
|
||||
after transform: ScopeId(0): [SymbolId(0), SymbolId(1), SymbolId(2), SymbolId(3)]
|
||||
rebuilt : ScopeId(0): [SymbolId(0), SymbolId(1), SymbolId(2), SymbolId(3)]
|
||||
Bindings mismatch:
|
||||
after transform: ScopeId(0): ["bar", "foo", "nsa", "nsb"]
|
||||
rebuilt : ScopeId(0): []
|
||||
|
||||
* imports/elide-typeof/input.ts
|
||||
Bindings mismatch:
|
||||
|
|
@ -2215,23 +2213,22 @@ after transform: SymbolId(2): [ReferenceId(4), ReferenceId(6)]
|
|||
rebuilt : SymbolId(1): [ReferenceId(1)]
|
||||
|
||||
* namespace/alias/input.ts
|
||||
Missing SymbolId: "b"
|
||||
Missing SymbolId: "AliasModule"
|
||||
Bindings mismatch:
|
||||
after transform: ScopeId(0): ["AliasModule", "LongNameModule", "b", "babel", "bar", "baz", "node", "some", "str"]
|
||||
rebuilt : ScopeId(0): ["AliasModule", "b", "babel", "bar", "baz", "node", "some", "str"]
|
||||
rebuilt : ScopeId(0): ["AliasModule", "bar", "baz", "node", "some", "str"]
|
||||
Scope children mismatch:
|
||||
after transform: ScopeId(0): [ScopeId(1)]
|
||||
rebuilt : ScopeId(0): []
|
||||
Reference symbol mismatch for "AliasModule":
|
||||
after transform: SymbolId(8) "AliasModule"
|
||||
rebuilt : SymbolId(2) "AliasModule"
|
||||
rebuilt : SymbolId(0) "AliasModule"
|
||||
Reference symbol mismatch for "AliasModule":
|
||||
after transform: SymbolId(8) "AliasModule"
|
||||
rebuilt : SymbolId(2) "AliasModule"
|
||||
rebuilt : SymbolId(0) "AliasModule"
|
||||
Unresolved reference IDs mismatch for "LongNameModule":
|
||||
after transform: [ReferenceId(1), ReferenceId(5)]
|
||||
rebuilt : [ReferenceId(1)]
|
||||
rebuilt : [ReferenceId(0)]
|
||||
|
||||
* namespace/clobber-class/input.ts
|
||||
Missing SymbolId: "_A"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
commit: 54a8389f
|
||||
|
||||
Passed: 126/144
|
||||
Passed: 126/145
|
||||
|
||||
# All Passed:
|
||||
* babel-plugin-transform-class-static-block
|
||||
|
|
@ -47,7 +47,7 @@ after transform: SymbolId(0): [ReferenceId(0), ReferenceId(2), ReferenceId(6), R
|
|||
rebuilt : SymbolId(0): [ReferenceId(0), ReferenceId(2), ReferenceId(6), ReferenceId(10)]
|
||||
|
||||
|
||||
# babel-plugin-transform-typescript (2/10)
|
||||
# babel-plugin-transform-typescript (2/11)
|
||||
* class-property-definition/input.ts
|
||||
Unresolved references mismatch:
|
||||
after transform: ["const"]
|
||||
|
|
@ -159,6 +159,12 @@ Scope children mismatch:
|
|||
after transform: ScopeId(0): [ScopeId(1)]
|
||||
rebuilt : ScopeId(0): []
|
||||
|
||||
* preserve-import-=/input.js
|
||||
Missing SymbolId: "Foo"
|
||||
Binding symbols mismatch:
|
||||
after transform: ScopeId(0): [SymbolId(0), SymbolId(1), SymbolId(2)]
|
||||
rebuilt : ScopeId(0): [SymbolId(0), SymbolId(1), SymbolId(2)]
|
||||
|
||||
* redeclarations/input.ts
|
||||
Bindings mismatch:
|
||||
after transform: ScopeId(0): ["A"]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
import nsa from "./module-a";
|
||||
import Foo = nsa.bar;
|
||||
|
||||
const foo: Foo = 0;
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"plugins": [
|
||||
[
|
||||
"transform-typescript",
|
||||
{
|
||||
"onlyRemoveTypeImports": true
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
import nsa from "./module-a";
|
||||
var Foo = nsa.bar;
|
||||
const foo = 0;
|
||||
Loading…
Reference in a new issue