refactor(compiler-vapor): class-based CodegenContext

This commit is contained in:
三咲智子 Kevin Deng 2024-01-30 22:46:56 +08:00
parent e02725665d
commit b8806f0954
No known key found for this signature in database
GPG Key ID: 69992F2250DFD93E
1 changed files with 77 additions and 94 deletions

View File

@ -44,111 +44,94 @@ export type CodeFragment =
| [code: string, newlineIndex?: number, loc?: SourceLocation, name?: string] | [code: string, newlineIndex?: number, loc?: SourceLocation, name?: string]
| undefined | undefined
export interface CodegenContext { export class CodegenContext {
options: Required<CodegenOptions> options: Required<CodegenOptions>
source: string source: string
code: CodeFragment[] code: CodeFragment[]
indentLevel: number indentLevel: number = 0
map?: SourceMapGenerator map?: SourceMapGenerator
push(...args: CodeFragment[]): void push: (...args: CodeFragment[]) => void
newline(): CodeFragment newline = (): CodeFragment => {
multi( return [`\n${` `.repeat(this.indentLevel)}`, NewlineType.Start]
codes: [left: string, right: string, segment: string], }
...fn: Array<false | string | CodeFragment[]> multi = (
): CodeFragment[] [left, right, seg]: [left: string, right: string, segment: string],
call( ...fns: Array<false | string | CodeFragment[]>
): CodeFragment[] => {
const frag: CodeFragment[] = []
fns = fns.filter(Boolean)
frag.push(left)
for (let [i, fn] of fns.entries()) {
if (fn) {
if (isString(fn)) fn = [fn]
frag.push(...fn)
if (i < fns.length - 1) frag.push(seg)
}
}
frag.push(right)
return frag
}
call = (
name: string, name: string,
...args: Array<false | string | CodeFragment[]> ...args: Array<false | string | CodeFragment[]>
): CodeFragment[] ): CodeFragment[] => {
withIndent<T>(fn: () => T): T return [name, ...this.multi(['(', ')', ', '], ...args)]
}
helpers: Set<string> withIndent = <T>(fn: () => T): T => {
vaporHelpers: Set<string> ++this.indentLevel
helper(name: string): string const ret = fn()
vaporHelper(name: VaporHelper): string --this.indentLevel
} return ret
function createCodegenContext(ir: RootIRNode, options: CodegenOptions) {
const helpers = new Set<string>([])
const vaporHelpers = new Set<string>([])
const [code, push] = buildCodeFragment()
const context: CodegenContext = {
options: extend(
{
mode: 'function',
prefixIdentifiers: options.mode === 'module',
sourceMap: false,
filename: `template.vue.html`,
scopeId: null,
optimizeImports: false,
runtimeGlobalName: `Vue`,
runtimeModuleName: `vue`,
ssrRuntimeModuleName: 'vue/server-renderer',
ssr: false,
isTS: false,
inSSR: false,
inline: false,
bindingMetadata: {},
expressionPlugins: [],
},
options,
),
source: ir.source,
code,
indentLevel: 0,
helpers,
vaporHelpers,
helper(name: string) {
helpers.add(name)
return `_${name}`
},
vaporHelper(name: VaporHelper) {
vaporHelpers.add(name)
return `_${name}`
},
push,
newline() {
return [`\n${` `.repeat(context.indentLevel)}`, NewlineType.Start]
},
multi([left, right, seg], ...fns) {
const frag: CodeFragment[] = []
fns = fns.filter(Boolean)
frag.push(left)
for (let [i, fn] of fns.entries()) {
if (fn) {
if (isString(fn)) fn = [fn]
frag.push(...fn)
if (i < fns.length - 1) frag.push(seg)
}
}
frag.push(right)
return frag
},
call(name, ...args) {
return [name, ...context.multi(['(', ')', ', '], ...args)]
},
withIndent(fn) {
++context.indentLevel
const ret = fn()
--context.indentLevel
return ret
},
} }
const filename = context.options.filename helpers = new Set<string>([])
if (!__BROWSER__ && context.options.sourceMap) { vaporHelpers = new Set<string>([])
// lazy require source-map implementation, only in non-browser builds helper = (name: string) => {
context.map = new SourceMapGenerator() this.helpers.add(name)
context.map.setSourceContent(filename, context.source) return `_${name}`
context.map._sources.add(filename) }
vaporHelper = (name: VaporHelper) => {
this.vaporHelpers.add(name)
return `_${name}`
} }
return context constructor(ir: RootIRNode, options: CodegenOptions) {
const defaultOptions = {
mode: 'function',
prefixIdentifiers: options.mode === 'module',
sourceMap: false,
filename: `template.vue.html`,
scopeId: null,
optimizeImports: false,
runtimeGlobalName: `Vue`,
runtimeModuleName: `vue`,
ssrRuntimeModuleName: 'vue/server-renderer',
ssr: false,
isTS: false,
inSSR: false,
inline: false,
bindingMetadata: {},
expressionPlugins: [],
}
this.options = extend(defaultOptions, options)
this.source = ir.source
const [code, push] = buildCodeFragment()
this.code = code
this.push = push
const {
options: { filename, sourceMap },
} = this
if (!__BROWSER__ && sourceMap) {
// lazy require source-map implementation, only in non-browser builds
this.map = new SourceMapGenerator()
this.map.setSourceContent(filename, this.source)
this.map._sources.add(filename)
}
}
} }
export interface VaporCodegenResult extends BaseCodegenResult { export interface VaporCodegenResult extends BaseCodegenResult {
@ -162,7 +145,7 @@ export function generate(
ir: RootIRNode, ir: RootIRNode,
options: CodegenOptions = {}, options: CodegenOptions = {},
): VaporCodegenResult { ): VaporCodegenResult {
const ctx = createCodegenContext(ir, options) const ctx = new CodegenContext(ir, options)
const { push, withIndent, newline, helpers, vaporHelpers } = ctx const { push, withIndent, newline, helpers, vaporHelpers } = ctx
const functionName = 'render' const functionName = 'render'