Skip to content

Commit

Permalink
refactor: use vite-plugin-utils
Browse files Browse the repository at this point in the history
  • Loading branch information
caoxiemeihao committed Oct 16, 2022
1 parent bb793a5 commit 236730a
Show file tree
Hide file tree
Showing 5 changed files with 31 additions and 161 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"typescript": "^4.7.4",
"vite": "^3.2.0-beta.2",
"vite-plugin-dynamic-import": "^1.2.3",
"vite-plugin-utils": "^0.3.1"
"vite-plugin-utils": "^0.3.3"
},
"keywords": [
"vite",
Expand Down
8 changes: 4 additions & 4 deletions src/analyze.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { AcornNode } from './types'
import { simpleWalk } from './utils'
import type { AcornNode } from './types'
import { walk } from 'vite-plugin-utils/function'

// ①(🎯): Top-level scope statement types, it also means statements that can be converted
// 顶级作用于语句类型,这种可以被无缝换成 import
Expand Down Expand Up @@ -56,7 +56,7 @@ export function analyzer(ast: AcornNode, code: string, id: string): Analyzed {
exports: [],
}

simpleWalk(ast, {
walk.sync(ast, {
CallExpression(node, ancestors) {
if (node.callee.name !== 'require') return

Expand All @@ -71,7 +71,7 @@ export function analyzer(ast: AcornNode, code: string, id: string): Analyzed {
dynamic: checkDynamicId(node),
})
},
AssignmentExpression(node, ancestors) {
AssignmentExpression(node) {
if (node.left.type !== 'MemberExpression') return
if (!(node.left.object.type === 'Identifier' && ['module', 'exports'].includes(node.left.object.name))) return

Expand Down
4 changes: 2 additions & 2 deletions src/dynamic-require.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export class DynaimcRequire {

constructor(
private config: ResolvedConfig,
private options: Options,
private options: Options & { extensions: string[] },
private resolve = new Resolve(config),
) { }

Expand All @@ -49,7 +49,7 @@ export class DynaimcRequire {
analyzed.code,
analyzed.id,
this.resolve,
options.extensions!,
this.options.extensions,
options.dynamic?.loose !== false,
)
if (!globResult) continue
Expand Down
46 changes: 23 additions & 23 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
import path from 'node:path'
import type { Plugin, ResolvedConfig } from 'vite'
import { analyzer, TopScopeType } from './analyze'
import { generateImport } from './generate-import'
import { generateExport } from './generate-export'
import {
cleanUrl,
isCommonjs,
JS_EXTENSIONS,
DEFAULT_EXTENSIONS,
KNOWN_SFC_EXTENSIONS,
KNOWN_ASSET_TYPES,
KNOWN_CSS_TYPES,
KNOWN_SFC_EXTENSIONS,
MagicString,
} from './utils'
} from 'vite-plugin-utils/constant'
import { MagicString } from 'vite-plugin-utils/function'
import { analyzer, TopScopeType } from './analyze'
import { generateImport } from './generate-import'
import { generateExport } from './generate-export'
import { isCommonjs } from './utils'
import { DynaimcRequire } from './dynamic-require'

export interface Options {
extensions?: string[]
filter?: (id: string) => false | undefined
dynamic?: {
/**
Expand All @@ -41,29 +39,31 @@ export interface Options {

export default function commonjs(options: Options = {}): Plugin {
let config: ResolvedConfig
const extensions = JS_EXTENSIONS
.concat(KNOWN_SFC_EXTENSIONS)
.concat(KNOWN_ASSET_TYPES)
.concat(KNOWN_CSS_TYPES)
let extensions = DEFAULT_EXTENSIONS
let dynaimcRequire: DynaimcRequire

return {
apply: 'serve',
name: 'vite-plugin-commonjs',
configResolved(_config) {
config = _config
options.extensions = [...new Set((config.resolve?.extensions || extensions).concat(options.extensions || []))]
dynaimcRequire = new DynaimcRequire(_config, options)
// https://github.com/vitejs/vite/blob/37ac91e5f680aea56ce5ca15ce1291adc3cbe05e/packages/vite/src/node/plugins/resolve.ts#L450
if (config.resolve?.extensions) extensions = config.resolve.extensions
dynaimcRequire = new DynaimcRequire(_config, {
...options,
extensions: [
...extensions,
...KNOWN_SFC_EXTENSIONS,
...KNOWN_ASSET_TYPES.map(type => '.' + type),
...KNOWN_CSS_TYPES.map(type => '.' + type),
],
})
},
async transform(code, id) {
const pureId = cleanUrl(id)
const extensions = JS_EXTENSIONS.concat(KNOWN_SFC_EXTENSIONS)
const { ext } = path.parse(pureId)

if (/node_modules\/(?!\.vite\/)/.test(pureId)) return
if (!extensions.includes(ext)) return
if (/node_modules\/(?!\.vite\/)/.test(id)) return
if (!extensions.includes(path.extname(id))) return
if (!isCommonjs(code)) return
if (options.filter?.(pureId) === false) return
if (options.filter?.(id) === false) return

const ast = this.parse(code)
const analyzed = analyzer(ast, code, id)
Expand Down
132 changes: 1 addition & 131 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,153 +1,23 @@
import { builtinModules } from 'node:module'
import { type AcornNode } from './types'
import { multilineCommentsRE, singlelineCommentsRE } from 'vite-plugin-utils/constant'

// ------------------------------------------------- RegExp

export const normallyImporteeRE = /^\.{1,2}\/[.-/\w]+(\.\w+)$/
export const multilineCommentsRE = /\/\*(.|[\r\n])*?\*\//gm
export const singlelineCommentsRE = /\/\/.*/g
export const queryRE = /\?.*$/s
export const hashRE = /#.*$/s

// ------------------------------------------------- const

export const JS_EXTENSIONS = [
'.mjs',
'.js',
'.ts',
'.jsx',
'.tsx',
'.cjs'
]
export const KNOWN_SFC_EXTENSIONS = [
'.vue',
'.svelte',
]
// https://github.com/vitejs/vite/blob/d6418605577319b2f92ea37081e34376bb47b286/packages/vite/src/node/constants.ts#L66
export const KNOWN_ASSET_TYPES = [
// images
'png',
'jpg',
'jpeg',
'gif',
'svg',
'ico',
'webp',
'avif',

// media
'mp4',
'webm',
'ogg',
'mp3',
'wav',
'flac',
'aac',

// fonts
'woff2?',
'eot',
'ttf',
'otf',

// other
'webmanifest',
'pdf',
'txt'
]
export const KNOWN_CSS_TYPES = [
'css',
'less',
'sass',
'scss',
'styl',
'stylus',
'pcss',
'postcss',
]
export const builtins = [
...builtinModules.map(m => !m.startsWith('_')),
...builtinModules.map(m => !m.startsWith('_')).map(m => `node:${m}`)
]

// ------------------------------------------------- function

export function cleanUrl(url: string): string {
return url.replace(hashRE, '').replace(queryRE, '')
}

export function isCommonjs(code: string) {
// Avoid matching the content of the comment
code = code
.replace(multilineCommentsRE, '')
.replace(singlelineCommentsRE, '')
return /\b(?:require|module|exports)\b/.test(code)
}

export function simpleWalk(
ast: AcornNode,
visitors: {
[type: string]: (node: AcornNode, ancestors: AcornNode[]) => void,
},
ancestors: AcornNode[] = [],
) {
if (!ast) return
if (Array.isArray(ast)) {
for (const element of ast as AcornNode[]) {
simpleWalk(element, visitors, ancestors)
}
} else {
ancestors = ancestors.concat(ast)
for (const key of Object.keys(ast)) {
(typeof ast[key] === 'object' &&
simpleWalk(ast[key], visitors, ancestors))
}
}
visitors[ast.type]?.(ast, ancestors)
}
// TODO
simpleWalk.async = function simpleWalkAsync() { }

export class MagicString {
private overwrites!: { loc: [number, number]; content: string }[]
private starts = ''
private ends = ''

constructor(
public str: string
) { }

public append(content: string) {
this.ends += content
return this
}

public prepend(content: string) {
this.starts = content + this.starts
return this
}

public overwrite(start: number, end: number, content: string) {
if (end < start) {
throw new Error(`"end" con't be less than "start".`)
}
if (!this.overwrites) {
this.overwrites = []
}

this.overwrites.push({ loc: [start, end], content })
return this
}

public toString() {
let str = this.str
if (this.overwrites) {
const arr = [...this.overwrites].sort((a, b) => b.loc[0] - a.loc[0])
for (const { loc: [start, end], content } of arr) {
// TODO: check start or end overlap
str = str.slice(0, start) + content + str.slice(end)
}
}
return this.starts + str + this.ends
}
}

0 comments on commit 236730a

Please sign in to comment.