Skip to content

Commit

Permalink
feat: supports full module interop
Browse files Browse the repository at this point in the history
  • Loading branch information
yejimeiming committed Sep 22, 2023
1 parent 242aeaa commit de0a56e
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 25 deletions.
4 changes: 2 additions & 2 deletions src/dynamic-require.ts
Expand Up @@ -7,7 +7,7 @@ import {
} from 'vite-plugin-dynamic-import'
import { normalizePath, COLOURS } from 'vite-plugin-utils/function'
import {
type Options,
type CommonjsOptions,
TAG,
} from '.'
import type { Analyzed } from './analyze'
Expand All @@ -27,7 +27,7 @@ export class DynaimcRequire {

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

Expand Down
28 changes: 15 additions & 13 deletions src/generate-import.ts
@@ -1,13 +1,14 @@
import type { ImportType } from 'src'
import type { CommonjsOptions, ImportInteropType } from 'src'
import type { Analyzed } from './analyze'

export interface ImportRecord {
node: AcornNode
importExpression?: string
importedName?: string
importInterop?: string
}

export function generateImport(analyzed: Analyzed, id: string, rules?: ImportType | ((id: string) => ImportType)) {
export function generateImport(analyzed: Analyzed, id: string, options: CommonjsOptions) {
const { importRules } = options.advanced ?? {}
const imports: ImportRecord[] = []
let count = 0

Expand Down Expand Up @@ -36,26 +37,27 @@ export function generateImport(analyzed: Analyzed, id: string, rules?: ImportTyp
}

// This is probably less accurate, but is much cheaper than a full AST parse.
let importType: ImportType = 'defaultFirst'
if (typeof rules === 'string') {
importType = rules
} else if (typeof rules === 'function') {
importType = rules(id) || 'defaultFirst'
let importInterop: ImportInteropType | string = 'defaultFirst'
if (typeof importRules === 'string') {
importInterop = importRules
} else if (typeof importRules === 'function') {
importInterop = importRules(id)
}

impt.importExpression = `import * as ${importName} from "${requireId}"`
switch (importType) {
switch (importInterop) {
case 'defaultFirst':
impt.importedName = `${importName}.default || ${importName}`
impt.importInterop = `${importName}.default || ${importName}`
break
case 'namedFirst':
impt.importedName = `Object.keys(${importName}).join('') !== 'default' ? ${importName} : ${importName}.default`
impt.importInterop = `Object.keys(${importName}).join('') !== "default" ? ${importName} : ${importName}.default`
break
case 'merge':
impt.importedName = `${importName}.default ? Object.assign(${importName}.default, ${importName}) : ${importName}`
impt.importInterop = `${importName}.default ? Object.assign(${importName}.default, ${importName}) : ${importName}`
break
default:
throw new Error(`Unknown import type: ${importType} for ${id}`)
// User-defined module interop.
impt.importInterop = importInterop // string | undefined
}

imports.push(impt)
Expand Down
25 changes: 15 additions & 10 deletions src/index.ts
Expand Up @@ -20,9 +20,9 @@ import { DynaimcRequire } from './dynamic-require'

export const TAG = '[vite-plugin-commonjs]'

export type ImportType = 'defaultFirst' | 'namedFirst' | 'merge'
export type ImportInteropType = 'defaultFirst' | 'namedFirst' | 'merge'

export interface Options {
export interface CommonjsOptions {
filter?: (id: string) => boolean | undefined
dynamic?: {
/**
Expand All @@ -45,12 +45,17 @@ export interface Options {
onFiles?: (files: string[], id: string) => typeof files | undefined
}
advanced?: {
/** Custom import behavior */
importRules?: ImportType | ((id: string) => ImportType)
/**
* Custom import module interop behavior.
*
* If you want to fully customize the interop behavior,
* you can pass a function and return the interop code snippet.
*/
importRules?: ImportInteropType | ((id: string) => ImportInteropType | string)
}
}

export default function commonjs(options: Options = {}): Plugin {
export default function commonjs(options: CommonjsOptions = {}): Plugin {
let config: ResolvedConfig
let extensions = DEFAULT_EXTENSIONS
let dynaimcRequire: DynaimcRequire
Expand Down Expand Up @@ -122,7 +127,7 @@ async function transformCommonjs({
extensions,
dynaimcRequire,
}: {
options: Options,
options: CommonjsOptions,
code: string,
id: string,
extensions: string[],
Expand All @@ -146,7 +151,7 @@ async function transformCommonjs({
}

const analyzed = analyzer(ast, code, id)
const imports = generateImport(analyzed, id, options.advanced?.importRules)
const imports = generateImport(analyzed, id, options)
const exportRuntime = id.includes('node_modules/.vite')
// Bypass Pre-build
? null
Expand All @@ -161,12 +166,12 @@ async function transformCommonjs({
const {
node,
importExpression,
importedName,
importInterop,
} = impt
if (importExpression && importedName) {
if (importExpression != null && importInterop != null) {
// TODO: Merge duplicated require id
hoistImports.push(importExpression + ';')
ms.overwrite(node.start, node.end, importedName)
ms.overwrite(node.start, node.end, importInterop)
}
}

Expand Down

0 comments on commit de0a56e

Please sign in to comment.