Skip to content

Commit

Permalink
feat: babel runtime scan
Browse files Browse the repository at this point in the history
  • Loading branch information
pionxzh committed Sep 30, 2023
1 parent 376fd18 commit a2587de
Show file tree
Hide file tree
Showing 27 changed files with 586 additions and 68 deletions.
252 changes: 250 additions & 2 deletions packages/ast-utils/src/exports.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { isString } from './isPrimitive'
import type { ASTNode, Collection, JSCodeshift } from 'jscodeshift'
import { isTopLevel } from './isTopLevel'
import type { ASTNode, AssignmentExpression, Collection, Identifier, JSCodeshift, MemberExpression, VariableDeclarator } from 'jscodeshift'

type Exported = string
type Local = string
Expand All @@ -21,7 +22,7 @@ export class ExportManager {
this.exportsFrom.set(local, source)
}

collect(j: JSCodeshift, root: Collection) {
collectEsModuleExport(j: JSCodeshift, root: Collection) {
root
.find(j.ExportDefaultDeclaration)
.forEach((path) => {
Expand Down Expand Up @@ -75,6 +76,253 @@ export class ExportManager {
})
}

collectCommonJsExport(j: JSCodeshift, root: Collection) {
/**
* Default export
*
* Note: `exports = { ... }` is not valid
* So we won't handle it
*
* @example
* module.exports = 1
* ->
* export default 1
*
* @example
* module.exports = { foo: 1 }
* ->
* export default { foo: 1 }
*/
root
.find(j.ExpressionStatement, {
expression: {
type: 'AssignmentExpression',
operator: '=',
left: {
type: 'MemberExpression',
object: {
type: 'Identifier',
name: 'module',
},
property: {
type: 'Identifier',
name: 'exports',
},
},
},
})
.forEach((path) => {
if (!isTopLevel(j, path)) return

const expression = path.node.expression as AssignmentExpression
if (j.Identifier.check(expression.right)) {
this.addDefaultExport(expression.right.name)
}
else if (j.ObjectExpression.check(expression.right)) {
const object = expression.right
const properties = object.properties
properties.forEach((property) => {
if (j.ObjectProperty.check(property)) {
const key = property.key
const value = property.value
if (j.Identifier.check(key) && j.Identifier.check(value)) {
this.addNamedExport(key.name, value.name)
}
}
})
}
})

/**
* Individual exports
*
* @example
* module.exports.foo = 1
* ->
* export const foo = 1
*
* @example
* module.exports.foo = foo
* ->
* export { foo }
*/
root
.find(j.ExpressionStatement, {
expression: {
type: 'AssignmentExpression',
operator: '=',
left: {
type: 'MemberExpression',
object: {
type: 'MemberExpression',
object: {
type: 'Identifier',
name: 'module',
},
property: {
type: 'Identifier',
name: 'exports',
},
},
property: {
type: 'Identifier',
},
},
},
})
.forEach((path) => {
if (!isTopLevel(j, path)) return

const expression = path.node.expression as AssignmentExpression
const left = expression.left as MemberExpression
const name = (left.property as Identifier).name
this.addNamedExport(name, name)
})

/**
* Individual exports
*
* @example
* exports.foo = 2
* ->
* export const foo = 2
*/
root
.find(j.ExpressionStatement, {
expression: {
type: 'AssignmentExpression',
operator: '=',
left: {
type: 'MemberExpression',
object: {
type: 'Identifier',
name: 'exports',
},
property: {
type: 'Identifier',
},
},
},
})
.forEach((path) => {
if (!isTopLevel(j, path)) return

const expression = path.node.expression as AssignmentExpression
const left = expression.left as MemberExpression
const name = (left.property as Identifier).name
this.addNamedExport(name, name)
})

/**
* Special case:
*
* @example
* var foo = exports.foo = 1
*/
root
.find(j.VariableDeclaration, {
declarations: [
{
type: 'VariableDeclarator',
id: {
type: 'Identifier',
},
init: {
type: 'AssignmentExpression',
operator: '=',
left: {
type: 'MemberExpression',
object: {
type: 'Identifier',
name: 'exports',
},
property: {
type: 'Identifier',
},
},
},
},
],
})
.forEach((path) => {
if (!isTopLevel(j, path)) return

const declaration = path.node.declarations[0] as VariableDeclarator
const init = declaration.init as AssignmentExpression
const left = init.left as MemberExpression
const right = init.right

const name = (left.property as Identifier).name

if (j.Identifier.check(right)) {
if (name === 'default') {
this.addDefaultExport(right.name)
}
else {
this.addNamedExport(name, right.name)
}
}
})

/**
* Special case:
*
* @example
* var bar = module.exports.baz = 2
*/
root
.find(j.VariableDeclaration, {
declarations: [
{
type: 'VariableDeclarator',
id: {
type: 'Identifier',
},
init: {
type: 'AssignmentExpression',
operator: '=',
left: {
type: 'MemberExpression',
object: {
type: 'MemberExpression',
object: {
type: 'Identifier',
name: 'module',
},
property: {
type: 'Identifier',
name: 'exports',
},
},
property: {
type: 'Identifier',
},
},
},
},
],
})
.forEach((path) => {
if (!isTopLevel(j, path)) return

const declaration = path.node.declarations[0] as VariableDeclarator
const init = declaration.init as AssignmentExpression
const left = init.left as MemberExpression
const right = init.right

const name = (left.property as Identifier).name

if (j.Identifier.check(right)) {
if (name === 'default') {
this.addDefaultExport(right.name)
}
else {
this.addNamedExport(name, right.name)
}
}
})
}

toJSON(): Record<Exported, Local> {
return Object.fromEntries(this.exports)
}
Expand Down
69 changes: 67 additions & 2 deletions packages/ast-utils/src/imports.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { isNumber, isString } from './isPrimitive'
import { isTopLevel } from './isTopLevel'
import type { NodePath } from 'ast-types/lib/node-path'
import type { Collection, ImportDeclaration, JSCodeshift, VariableDeclaration } from 'jscodeshift'
import type { CallExpression, Collection, ImportDeclaration, JSCodeshift, Literal, VariableDeclaration, VariableDeclarator } from 'jscodeshift'

type Source = string
type Imported = string
Expand Down Expand Up @@ -159,7 +161,7 @@ export class ImportManager {
]
}

collectImportsFromRoot(j: JSCodeshift, root: Collection) {
collectEsModuleImport(j: JSCodeshift, root: Collection) {
root
.find(j.ImportDeclaration)
.forEach((path) => {
Expand Down Expand Up @@ -204,6 +206,69 @@ export class ImportManager {
})
}

collectCommonJsImport(j: JSCodeshift, root: Collection) {
/**
* Basic require and require with destructuring
*
* @example
* var foo = require('foo')
* var { bar } = require('bar')
*/
root
.find(j.VariableDeclaration, {
declarations: [
{
type: 'VariableDeclarator',
init: {
type: 'CallExpression',
callee: {
type: 'Identifier',
name: 'require',
},
arguments: [{
type: 'Literal' as const,
value: (value: unknown) => isString(value) || isNumber(value),
}],
},
},
],
})
.forEach((path) => {
if (!isTopLevel(j, path)) return

const firstDeclaration = path.node.declarations[0] as VariableDeclarator
const id = firstDeclaration.id
const init = firstDeclaration.init as CallExpression

const sourceLiteral = init.arguments[0] as Literal
const source = sourceLiteral.value as string

if (j.Identifier.check(id)) {
const local = id.name
this.addDefaultImport(source, local)
return
}

/**
* var { bar } = require('bar')
*/
if (j.ObjectPattern.check(id)) {
id.properties.forEach((property) => {
if (j.Property.check(property)
&& j.Identifier.check(property.key)
&& j.Identifier.check(property.value)
) {
const imported = property.key.name
const local = property.value.name
this.addNamedImport(source, imported, local)
}
})
// eslint-disable-next-line no-useless-return
return
}
})
}

/**
* Remove all collected import declarations
*/
Expand Down
1 change: 1 addition & 0 deletions packages/ast-utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ export { wrapAstTransformation } from './wrapAstTransformation'
export * from './imports'
export * from './exports'
export * from './isPrimitive'
export * from './types'
11 changes: 11 additions & 0 deletions packages/ast-utils/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { ImportInfo } from './imports'

export type ModuleMapping = Record<number | string, string>

export interface ModuleMeta {
[moduleId: string]: {
import: ImportInfo[]
export: Record<string, string>
tags: Record<string, string[]>
}
}
1 change: 1 addition & 0 deletions packages/playground/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"@fortawesome/vue-fontawesome": "^3.0.3",
"@headlessui/vue": "^1.7.16",
"@heroicons/vue": "^2.0.18",
"@unminify-kit/ast-utils": "workspace:*",
"@unminify-kit/unminify": "workspace:*",
"@unminify-kit/unpacker": "workspace:*",
"@vueuse/core": "^10.4.1",
Expand Down
2 changes: 1 addition & 1 deletion packages/playground/src/codemod.worker.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { runTransformations, transformationMap } from '@unminify-kit/unminify'
import type { ModuleMeta } from './composables/useModuleMeta'
import type { TransformedModule } from './types'
import type { ModuleMapping } from '@unminify-kit/unpacker'
import type { ModuleMapping } from '@unminify-kit/ast-utils'

onmessage = (
msg: MessageEvent<{
Expand Down
Loading

0 comments on commit a2587de

Please sign in to comment.