vue3-yuanma/packages/compiler-sfc/src/rewriteDefault.ts

70 lines
2.1 KiB
TypeScript

import { parse, ParserPlugin } from '@babel/parser'
import MagicString from 'magic-string'
const defaultExportRE = /((?:^|\n|;)\s*)export(\s*)default/
const namedDefaultExportRE = /((?:^|\n|;)\s*)export(.+)as(\s*)default/
const exportDefaultClassRE = /((?:^|\n|;)\s*)export\s+default\s+class\s+([\w$]+)/
/**
* Utility for rewriting `export default` in a script block into a variable
* declaration so that we can inject things into it
*/
export function rewriteDefault(
input: string,
as: string,
parserPlugins?: ParserPlugin[]
): string {
if (!hasDefaultExport(input)) {
return input + `\nconst ${as} = {}`
}
let replaced: string | undefined
const classMatch = input.match(exportDefaultClassRE)
if (classMatch) {
replaced =
input.replace(exportDefaultClassRE, '$1class $2') +
`\nconst ${as} = ${classMatch[2]}`
} else {
replaced = input.replace(defaultExportRE, `$1const ${as} =`)
}
if (!hasDefaultExport(replaced)) {
return replaced
}
// if the script somehow still contains `default export`, it probably has
// multi-line comments or template strings. fallback to a full parse.
const s = new MagicString(input)
const ast = parse(input, {
sourceType: 'module',
plugins: parserPlugins
}).program.body
ast.forEach(node => {
if (node.type === 'ExportDefaultDeclaration') {
s.overwrite(node.start!, node.declaration.start!, `const ${as} = `)
}
if (node.type === 'ExportNamedDeclaration') {
node.specifiers.forEach(specifier => {
if (
specifier.type === 'ExportSpecifier' &&
specifier.exported.type === 'Identifier' &&
specifier.exported.name === 'default'
) {
const end = specifier.end!
s.overwrite(
specifier.start!,
input.charAt(end) === ',' ? end + 1 : end,
``
)
s.append(`\nconst ${as} = ${specifier.local.name}`)
}
})
}
})
return s.toString()
}
export function hasDefaultExport(input: string): boolean {
return defaultExportRE.test(input) || namedDefaultExportRE.test(input)
}