]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
refactor(compiler-vapor): class-based CodegenContext
author三咲智子 Kevin Deng <sxzz@sxzz.moe>
Tue, 30 Jan 2024 14:46:56 +0000 (22:46 +0800)
committer三咲智子 Kevin Deng <sxzz@sxzz.moe>
Tue, 30 Jan 2024 14:46:56 +0000 (22:46 +0800)
packages/compiler-vapor/src/generate.ts

index ca1b9fcbc7248dde79d3a26c6b34bdf21ad28e41..9eb561035c1587ac149a3c9fe8b2bff3881936eb 100644 (file)
@@ -44,111 +44,94 @@ export type CodeFragment =
   | [code: string, newlineIndex?: number, loc?: SourceLocation, name?: string]
   | undefined
 
-export interface CodegenContext {
+export class CodegenContext {
   options: Required<CodegenOptions>
 
   source: string
   code: CodeFragment[]
-  indentLevel: number
+  indentLevel: number = 0
   map?: SourceMapGenerator
 
-  push(...args: CodeFragment[]): void
-  newline(): CodeFragment
-  multi(
-    codes: [left: string, right: string, segment: string],
-    ...fn: Array<false | string | CodeFragment[]>
-  ): CodeFragment[]
-  call(
+  push: (...args: CodeFragment[]) => void
+  newline = (): CodeFragment => {
+    return [`\n${`  `.repeat(this.indentLevel)}`, NewlineType.Start]
+  }
+  multi = (
+    [left, right, seg]: [left: string, right: string, segment: string],
+    ...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,
     ...args: Array<false | string | CodeFragment[]>
-  ): CodeFragment[]
-  withIndent<T>(fn: () => T): T
-
-  helpers: Set<string>
-  vaporHelpers: Set<string>
-  helper(name: string): string
-  vaporHelper(name: VaporHelper): string
-}
-
-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
-    },
+  ): CodeFragment[] => {
+    return [name, ...this.multi(['(', ')', ', '], ...args)]
+  }
+  withIndent = <T>(fn: () => T): T => {
+    ++this.indentLevel
+    const ret = fn()
+    --this.indentLevel
+    return ret
   }
 
-  const filename = context.options.filename
-  if (!__BROWSER__ && context.options.sourceMap) {
-    // lazy require source-map implementation, only in non-browser builds
-    context.map = new SourceMapGenerator()
-    context.map.setSourceContent(filename, context.source)
-    context.map._sources.add(filename)
+  helpers = new Set<string>([])
+  vaporHelpers = new Set<string>([])
+  helper = (name: string) => {
+    this.helpers.add(name)
+    return `_${name}`
+  }
+  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 {
@@ -162,7 +145,7 @@ export function generate(
   ir: RootIRNode,
   options: CodegenOptions = {},
 ): VaporCodegenResult {
-  const ctx = createCodegenContext(ir, options)
+  const ctx = new CodegenContext(ir, options)
   const { push, withIndent, newline, helpers, vaporHelpers } = ctx
 
   const functionName = 'render'