mirror of
https://github.com/oxc-project/oxc.git
synced 2026-09-14 19:36:11 +08:00
fix(packages/codegen): print matching quoted import names as identifiers (#26404)
Oxc codegen npm package emits invalid JavaScript when a quoted imported name matches its local binding. The printer compares the decoded names and omits the alias, but leaves the imported name quoted. A quoted import name requires an explicit `as` binding.
For this valid input:
```js
import { "foo" as foo } from "m";
```
The JavaScript printer previously emitted:
```js
import { "foo" } from "m";
```
It now prints the matching local binding as identifier shorthand:
```js
import { foo } from "m";
```
`printImportDeclaration` checks for a string-literal imported name whose decoded value equals the local binding name before printing the imported name. In that case, it prints the local identifier directly, including its source-map mapping. The local binding already supplies a valid identifier, so this requires neither a separate identifier-validity check nor an AST mutation.
Comparing the decoded value also handles escaped and Unicode import names:
```js
// Input
import { "a\u0062" as ab } from "m";
import { "π" as π } from "m";
// Output
import { ab } from "m";
import { π } from "m";
```
Declaration-level and inline TypeScript type imports use the same shorthand:
```ts
// Input
import type { "Foo" as Foo } from "m";
import { type "Bar" as Bar } from "m";
// Output
import type { Foo } from "m";
import { type Bar } from "m";
```
Quoted names that differ from their local bindings retain their quotes and aliases, including `"foo-bar" as foo`, `"" as foo`, and `"default" as foo`. Ordinary identifier imports keep their existing shorthand behavior. The correction applies to both the ordinary and source-map-enabled builds and brings the JavaScript printer in line with the Rust codegen fix in #26386.
This commit is contained in:
@@ -114,8 +114,13 @@ export function printImportDeclaration(node: ESTree.ImportDeclaration, state: St
|
||||
|
||||
if (TS && specifier.importKind === "type") write(state, "type ", CAT_OTHER);
|
||||
|
||||
const importedName = moduleExportName(specifier.imported, state);
|
||||
const { local } = specifier;
|
||||
const { imported, local } = specifier;
|
||||
if (imported.type === "Literal" && imported.value === local.name) {
|
||||
printSpaceBeforeIdentifier(state);
|
||||
writeWithMapNamed(state, local.name, local.start, local.end, local);
|
||||
break;
|
||||
}
|
||||
const importedName = moduleExportName(imported, state);
|
||||
if (importedName !== local.name) {
|
||||
write(state, " as ", CAT_OTHER);
|
||||
writeWithMapNamed(state, local.name, local.start, local.end, local);
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import { "foo" as foo } from "m";
|
||||
import { "a\u0062" as ab } from "m";
|
||||
import { "π" as π } from "m";
|
||||
import { "type" as type } from "m";
|
||||
import { "foo-bar" as dashed } from "m";
|
||||
import { "" as empty } from "m";
|
||||
import { "default" as defaultImport } from "m";
|
||||
import { "foo" as bar } from "m";
|
||||
import { "one" as one, "two" as two, three } from "m";
|
||||
import { four as four, five } from "m";
|
||||
@@ -0,0 +1,6 @@
|
||||
import type { "Foo" as Foo } from "m";
|
||||
import { type "Bar" as Bar } from "m";
|
||||
import type { "a\u0063" as ac } from "m";
|
||||
import { type "a\u0064" as ad } from "m";
|
||||
import type { Baz as Baz } from "m";
|
||||
import { type Qux as Qux } from "m";
|
||||
Reference in New Issue
Block a user