Skip to content

Commit

Permalink
feat(unminify): add un-import-rename rule
Browse files Browse the repository at this point in the history
  • Loading branch information
pionxzh committed Mar 16, 2024
1 parent 68e36c5 commit 95d3d00
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 0 deletions.
12 changes: 12 additions & 0 deletions packages/unminify/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ It covers most of the patterns that are used by the following tools:
- [`un-indirect-call`](#un-indirect-call)
- [`un-type-constructor` (Unsafe)](#un-type-constructor-unsafe)
- [`un-builtin-prototype`](#un-builtin-prototype)
- [`un-import-rename`](#un-import-rename)
- [`smart-rename`](#smart-rename)
- [`un-iife`](#un-iife)
- [Syntax Upgrade](#syntax-upgrade)
Expand Down Expand Up @@ -334,6 +335,17 @@ Convert function calls on instances of built-in objects to equivalent calls on t
+ String.prototype.indexOf.call(e, "bar");
```

### `un-import-rename`

Rename import specifier back to the original name

```diff
-import { foo as a } from 'bar';
- a();
+import { foo } from 'bar';
+ foo();
```

### `smart-rename`

Rename minified identifiers with heuristic rules.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { defineInlineTest } from '@wakaru/test-utils'
import transform from '../un-import-rename'

const inlineTest = defineInlineTest(transform)

inlineTest('import rename',
`
import { foo as a, bar as b, code } from '_';
console.log(a, b, code);
`,
`
import { foo, bar, code } from '_';
console.log(foo, bar, code);
`,
)

inlineTest('import rename with naming conflict',
`
import defaultExport, { foo as a, bar as b, code } from 'A';
import { foo, bar as c } from 'B';
console.log(a, b, code, foo, c);
`,
`
import defaultExport, { foo as foo_1, bar, code } from 'A';
import { foo, bar as bar_1 } from 'B';
console.log(foo_1, bar, code, foo, bar_1);
`,
)

inlineTest('multiple naming conflicts',
`
import { foo as a, bar as b } from 'A';
import { foo, bar } from 'B';
const foo_1 = 'local';
console.log(a, b, foo, bar, foo_1);
`,
`
import { foo as foo_2, bar as bar_1 } from 'A';
import { foo, bar } from 'B';
const foo_1 = 'local';
console.log(foo_2, bar_1, foo, bar, foo_1);
`,
)
2 changes: 2 additions & 0 deletions packages/unminify/src/transformations/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import unEsModuleFlag from './un-esmodule-flag'
import unExportRename from './un-export-rename'
import unFlipComparisons from './un-flip-comparisons'
import unIife from './un-iife'
import unImportRename from './un-import-rename'
import unIndirectCall from './un-indirect-call'
import unInfinity from './un-infinity'
import unJsx from './un-jsx'
Expand Down Expand Up @@ -73,6 +74,7 @@ export const transformationRules: TransformationRule[] = [

// advanced syntax upgrade
unIife,
unImportRename, // should run after `un-esm` to cover all the import statements
smartInline, // relies on `lebab`'s `let` to `const` and `un-sequence-expression`
smartRename, // partially relies on `un-indirect-call` to work
unOptionalChaining,
Expand Down
71 changes: 71 additions & 0 deletions packages/unminify/src/transformations/un-import-rename.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { assertScopeExists } from '@wakaru/ast-utils/assert'
import { generateName } from '@wakaru/ast-utils/identifier'
import { renameIdentifier } from '@wakaru/ast-utils/reference'
import { createJSCodeshiftTransformationRule } from '@wakaru/shared/rule'
import type { Identifier } from 'jscodeshift'

/**
* Rename import specifier back to the original name
*
* @example
* import { foo as ab } from 'bar';
* ab();
* ->
* import { foo } from 'bar'
* foo();
*/
export default createJSCodeshiftTransformationRule({
name: 'un-import-rename',
transform: (context) => {
const { root, j } = context

root
.find(j.ImportSpecifier, {
imported: {
type: 'Identifier',
},
local: {
type: 'Identifier',
},
})
.paths()
.forEach((path) => {
const specifier = path.node
const imported = specifier.imported as Identifier
const local = specifier.local as Identifier

if (imported.name === local.name) return

const scope = path.scope
assertScopeExists(scope)

const targetName = generateName(imported.name, scope)
renameIdentifier(j, scope, local.name, targetName)
})

/**
* fix all import { foo as foo } to import { foo }
*
* Doing this in a separate loop to avoid modifying the AST structure while iterating over it
* It will cause some weird behavior
*/
root
.find(j.ImportSpecifier, {
imported: {
type: 'Identifier',
},
local: {
type: 'Identifier',
},
})
.forEach((path) => {
const specifier = path.node
const imported = specifier.imported as Identifier
const local = specifier.local as Identifier

if (imported.name !== local.name) return

path.replace(j.importSpecifier(j.identifier(imported.name)))
})
},
})

0 comments on commit 95d3d00

Please sign in to comment.