]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
refactor(compiler-vapor): simplify codegen context
author三咲智子 Kevin Deng <sxzz@sxzz.moe>
Tue, 30 Jan 2024 11:35:29 +0000 (19:35 +0800)
committer三咲智子 Kevin Deng <sxzz@sxzz.moe>
Tue, 30 Jan 2024 11:35:29 +0000 (19:35 +0800)
packages/compiler-vapor/src/generate.ts
packages/compiler-vapor/src/generators/directive.ts
packages/compiler-vapor/src/generators/dom.ts
packages/compiler-vapor/src/generators/event.ts
packages/compiler-vapor/src/generators/expression.ts
packages/compiler-vapor/src/generators/html.ts
packages/compiler-vapor/src/generators/if.ts
packages/compiler-vapor/src/generators/modelValue.ts
packages/compiler-vapor/src/generators/prop.ts
packages/compiler-vapor/src/generators/ref.ts
packages/compiler-vapor/src/generators/text.ts

index e629138617396c0a37335d68a10e6e25f810643f..240cc73474f3646d1d3d4dba0ce2ee9f0c6f9184 100644 (file)
@@ -18,7 +18,7 @@ import {
   type WithDirectiveIRNode,
 } from './ir'
 import { SourceMapGenerator } from 'source-map-js'
-import { isString } from '@vue/shared'
+import { extend, isString } from '@vue/shared'
 import type { ParserPlugin } from '@babel/parser'
 import { genSetProp } from './generators/prop'
 import { genCreateTextNode, genSetText } from './generators/text'
@@ -38,7 +38,9 @@ interface CodegenOptions extends BaseCodegenOptions {
 // @ts-expect-error
 function checkNever(x: never): never {}
 
-export interface CodegenContext extends Required<CodegenOptions> {
+export interface CodegenContext {
+  options: Required<CodegenOptions>
+
   source: string
   code: string
   line: number
@@ -53,8 +55,8 @@ export interface CodegenContext extends Required<CodegenOptions> {
     loc?: SourceLocation,
     name?: string,
   ): void
-  pushNewline(
-    code: string,
+  newline(
+    code?: string,
     newlineIndex?: number,
     loc?: SourceLocation,
     name?: string,
@@ -63,9 +65,8 @@ export interface CodegenContext extends Required<CodegenOptions> {
     codes: [left: string, right: string, segment?: string],
     ...fn: Array<false | string | (() => void)>
   ): void
-  pushFnCall(name: string, ...args: Array<false | string | (() => void)>): void
+  pushCall(name: string, ...args: Array<false | string | (() => void)>): void
   withIndent(fn: () => void): void
-  newline(): void
 
   helpers: Set<string>
   vaporHelpers: Set<string>
@@ -73,47 +74,33 @@ export interface CodegenContext extends Required<CodegenOptions> {
   vaporHelper(name: string): string
 }
 
-function createCodegenContext(
-  ir: RootIRNode,
-  {
-    mode = 'function',
-    prefixIdentifiers = 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 = [],
-  }: CodegenOptions,
-) {
+function createCodegenContext(ir: RootIRNode, options: CodegenOptions) {
   const helpers = new Set<string>([])
   const vaporHelpers = new Set<string>([])
   const context: CodegenContext = {
-    mode,
-    prefixIdentifiers,
-    sourceMap,
-    filename,
-    scopeId,
-    optimizeImports,
-    runtimeGlobalName,
-    runtimeModuleName,
-    ssrRuntimeModuleName,
-    ssr,
-    isTS,
-    inSSR,
-    bindingMetadata,
-    expressionPlugins,
-    inline,
+    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: ``,
+    code: '',
     column: 1,
     line: 1,
     offset: 0,
@@ -129,6 +116,7 @@ function createCodegenContext(
       vaporHelpers.add(name)
       return `_${name}`
     },
+
     push(code, newlineIndex = NewlineType.None, loc, name) {
       context.code += code
       if (!__BROWSER__ && context.map) {
@@ -174,23 +162,21 @@ function createCodegenContext(
         }
       }
     },
-    pushNewline(code, newlineIndex, node) {
-      context.newline()
-      context.push(code, newlineIndex, node)
+    newline(code, newlineIndex, node) {
+      context.push(`\n${`  `.repeat(context.indentLevel)}`, NewlineType.Start)
+      code && context.push(code, newlineIndex, node)
     },
     pushMulti([left, right, seg], ...fns) {
       fns = fns.filter(Boolean)
       context.push(left)
-      for (let i = 0; i < fns.length; i++) {
-        const fn = fns[i] as string | (() => void)
-
+      for (const [i, fn] of fns.entries()) {
         if (isString(fn)) context.push(fn)
-        else fn()
+        else (fn as () => void)()
         if (seg && i < fns.length - 1) context.push(seg)
       }
       context.push(right)
     },
-    pushFnCall(name, ...args) {
+    pushCall(name, ...args) {
       context.push(name)
       context.pushMulti(['(', ')', ', '], ...args)
     },
@@ -199,11 +185,10 @@ function createCodegenContext(
       fn()
       --context.indentLevel
     },
-    newline() {
-      context.push(`\n${`  `.repeat(context.indentLevel)}`, NewlineType.Start)
-    },
   }
 
+  const filename = context.options.filename
+
   function addMapping(loc: Position, name: string | null = null) {
     // we use the private property to directly add the mapping
     // because the addMapping() implementation in source-map-js has a bunch of
@@ -221,7 +206,7 @@ function createCodegenContext(
     })
   }
 
-  if (!__BROWSER__ && sourceMap) {
+  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)
@@ -243,15 +228,7 @@ export function generate(
   options: CodegenOptions = {},
 ): VaporCodegenResult {
   const ctx = createCodegenContext(ir, options)
-  const {
-    push,
-    pushNewline,
-    withIndent,
-    newline,
-    helpers,
-    vaporHelper,
-    vaporHelpers,
-  } = ctx
+  const { push, withIndent, newline, helpers, vaporHelper, vaporHelpers } = ctx
 
   const functionName = 'render'
   const isSetupInlined = !!options.inline
@@ -260,21 +237,21 @@ export function generate(
   } else {
     // placeholder for preamble
     newline()
-    pushNewline(`export function ${functionName}(_ctx) {`)
+    newline(`export function ${functionName}(_ctx) {`)
   }
 
   withIndent(() => {
     ir.template.forEach((template, i) => {
       if (template.type === IRNodeTypes.TEMPLATE_FACTORY) {
         // TODO source map?
-        pushNewline(
+        newline(
           `const t${i} = ${vaporHelper('template')}(${JSON.stringify(
             template.template,
           )})`,
         )
       } else {
         // fragment
-        pushNewline(`const t${i} = ${vaporHelper('fragment')}()`)
+        newline(`const t${i} = ${vaporHelper('fragment')}()`)
       }
     })
 
@@ -380,14 +357,12 @@ export function genBlockFunctionContent(
   ir: BlockFunctionIRNode | RootIRNode,
   ctx: CodegenContext,
 ) {
-  const { pushNewline, withIndent, vaporHelper } = ctx
-  pushNewline(`const n${ir.dynamic.id} = t${ir.templateIndex}()`)
+  const { newline, withIndent, vaporHelper } = ctx
+  newline(`const n${ir.dynamic.id} = t${ir.templateIndex}()`)
 
   const children = genChildren(ir.dynamic.children)
   if (children) {
-    pushNewline(
-      `const ${children} = ${vaporHelper('children')}(n${ir.dynamic.id})`,
-    )
+    newline(`const ${children} = ${vaporHelper('children')}(n${ir.dynamic.id})`)
   }
 
   const directiveOps = ir.operation.filter(
@@ -403,16 +378,16 @@ export function genBlockFunctionContent(
   }
 
   for (const { operations } of ir.effect) {
-    pushNewline(`${vaporHelper('renderEffect')}(() => {`)
+    newline(`${vaporHelper('renderEffect')}(() => {`)
     withIndent(() => {
       for (const operation of operations) {
         genOperation(operation, ctx)
       }
     })
-    pushNewline('})')
+    newline('})')
   }
 
-  pushNewline(`return n${ir.dynamic.id}`)
+  newline(`return n${ir.dynamic.id}`)
 }
 
 function groupDirective(ops: WithDirectiveIRNode[]): WithDirectiveIRNode[][] {
index f3a1af67a9acec27ae8919cd8d2eff1945ab8704..2c9d9143c7dec7b3ed2c05d602a404800eb945c7 100644 (file)
@@ -8,11 +8,17 @@ export function genWithDirective(
   opers: WithDirectiveIRNode[],
   context: CodegenContext,
 ) {
-  const { push, newline, pushFnCall, pushMulti, vaporHelper, bindingMetadata } =
-    context
+  const {
+    push,
+    newline,
+    pushCall,
+    pushMulti,
+    vaporHelper,
+    options: { bindingMetadata },
+  } = context
 
   newline()
-  pushFnCall(
+  pushCall(
     vaporHelper('withDirectives'),
     // 1st arg: node
     `n${opers[0].element}`,
index dcc9324060c208ab0e5363721725f388d6c6f200..17a3e27cc742cf3265ed3ca8caa4d3aba9a717c7 100644 (file)
@@ -5,37 +5,35 @@ import type {
   PrependNodeIRNode,
 } from '../ir'
 
-export function genInsertNode(oper: InsertNodeIRNode, context: CodegenContext) {
-  const { newline, pushFnCall, vaporHelper } = context
+export function genInsertNode(
+  oper: InsertNodeIRNode,
+  { newline, pushCall, vaporHelper }: CodegenContext,
+) {
   const elements = ([] as number[]).concat(oper.element)
   let element = elements.map(el => `n${el}`).join(', ')
   if (elements.length > 1) element = `[${element}]`
   newline()
-  pushFnCall(
-    vaporHelper('insert'),
-    element,
-    `n${oper.parent}`,
-    `n${oper.anchor}`,
-  )
+  pushCall(vaporHelper('insert'), element, `n${oper.parent}`, `n${oper.anchor}`)
 }
 
 export function genPrependNode(
   oper: PrependNodeIRNode,
-  context: CodegenContext,
+  { newline, pushCall, vaporHelper }: CodegenContext,
 ) {
-  const { newline, pushFnCall, vaporHelper } = context
   newline()
-  pushFnCall(
+  pushCall(
     vaporHelper('prepend'),
     `n${oper.parent}`,
     oper.elements.map(el => `n${el}`).join(', '),
   )
 }
 
-export function genAppendNode(oper: AppendNodeIRNode, context: CodegenContext) {
-  const { newline, pushFnCall, vaporHelper } = context
+export function genAppendNode(
+  oper: AppendNodeIRNode,
+  { newline, pushCall, vaporHelper }: CodegenContext,
+) {
   newline()
-  pushFnCall(
+  pushCall(
     vaporHelper('append'),
     `n${oper.parent}`,
     oper.elements.map(el => `n${el}`).join(', '),
index bec6528e945d0b8a1f07a3024db569e8803795ee..f369315d9982e1a2e5c0c8d6546c6af44381dbe0 100644 (file)
@@ -8,11 +8,18 @@ const fnExpRE =
   /^\s*([\w$_]+|(async\s*)?\([^)]*?\))\s*(:[^=]+)?=>|^\s*(async\s+)?function(?:\s+[\w$]+)?\s*\(/
 
 export function genSetEvent(oper: SetEventIRNode, context: CodegenContext) {
-  const { vaporHelper, push, newline, pushMulti, pushFnCall } = context
+  const {
+    vaporHelper,
+    push,
+    newline,
+    pushMulti,
+    pushCall,
+    options: ctxOptions,
+  } = context
   const { keys, nonKeys, options } = oper.modifiers
 
   newline()
-  pushFnCall(
+  pushCall(
     vaporHelper('on'),
     // 1st arg: event name
     () => push(`n${oper.element}`),
@@ -44,7 +51,7 @@ export function genSetEvent(oper: SetEventIRNode, context: CodegenContext) {
 
       ;(keys.length ? pushWithKeys : pushNoop)(() =>
         (nonKeys.length ? pushWithModifiers : pushNoop)(() => {
-          genEventHandler(context)
+          genEventHandler()
         }),
       )
     },
@@ -53,10 +60,10 @@ export function genSetEvent(oper: SetEventIRNode, context: CodegenContext) {
       (() => push(`{ ${options.map(v => `${v}: true`).join(', ')} }`)),
   )
 
-  function genEventHandler(context: CodegenContext) {
+  function genEventHandler() {
     const exp = oper.value
     if (exp && exp.content.trim()) {
-      const isMemberExp = isMemberExpression(exp.content, context)
+      const isMemberExp = isMemberExpression(exp.content, ctxOptions)
       const isInlineStatement = !(isMemberExp || fnExpRE.test(exp.content))
       const hasMultipleStatements = exp.content.includes(`;`)
 
index d6ae21f5187f4426802ff4a2b5a22de827a61edd..90019290ec842b86a4f7d62281a49d7dc8a34870 100644 (file)
@@ -15,7 +15,10 @@ export function genExpression(
   context: CodegenContext,
   knownIds: Record<string, number> = Object.create(null),
 ): void {
-  const { push } = context
+  const {
+    push,
+    options: { prefixIdentifiers },
+  } = context
   if (isString(node)) return push(node)
 
   const { content: rawExpr, ast, isStatic, loc } = node
@@ -24,7 +27,7 @@ export function genExpression(
   }
   if (
     __BROWSER__ ||
-    !context.prefixIdentifiers ||
+    !prefixIdentifiers ||
     !node.content.trim() ||
     // there was a parsing error
     ast === false ||
@@ -81,9 +84,10 @@ const isLiteralWhitelisted = /*#__PURE__*/ makeMap('true,false,null,this')
 
 function genIdentifier(
   id: string,
-  { inline, bindingMetadata, vaporHelper, push }: CodegenContext,
+  { options, vaporHelper, push }: CodegenContext,
   loc?: SourceLocation,
 ): void {
+  const { inline, bindingMetadata } = options
   let name: string | undefined = id
   if (inline) {
     switch (bindingMetadata[id]) {
index 1a26f4bf2d3f60d2a0c2e7f110add180bb61afd3..3ca7c569562dd00f65afab6279012558664afcbd 100644 (file)
@@ -3,9 +3,9 @@ import type { SetHtmlIRNode } from '../ir'
 import { genExpression } from './expression'
 
 export function genSetHtml(oper: SetHtmlIRNode, context: CodegenContext) {
-  const { newline, pushFnCall, vaporHelper } = context
+  const { newline, pushCall, vaporHelper } = context
   newline()
-  pushFnCall(vaporHelper('setHtml'), `n${oper.element}`, () =>
+  pushCall(vaporHelper('setHtml'), `n${oper.element}`, () =>
     genExpression(oper.value, context),
   )
 }
index 0019273f8277fc6dcfdfcad195aa4d92e9ea722c..72b2b099199771ccbe7b6a557f29d6146a8eb270 100644 (file)
@@ -7,7 +7,7 @@ export function genIf(
   context: CodegenContext,
   isNested = false,
 ) {
-  const { pushFnCall, vaporHelper, pushNewline, push } = context
+  const { pushCall, vaporHelper, newline, push } = context
   const { condition, positive, negative } = oper
 
   let positiveArg = () => genBlockFunction(positive, context)
@@ -24,8 +24,8 @@ export function genIf(
     }
   }
 
-  if (!isNested) pushNewline(`const n${oper.id} = `)
-  pushFnCall(
+  if (!isNested) newline(`const n${oper.id} = `)
+  pushCall(
     vaporHelper('createIf'),
     () => {
       push('() => (')
@@ -38,11 +38,11 @@ export function genIf(
 }
 
 function genBlockFunction(oper: BlockFunctionIRNode, context: CodegenContext) {
-  const { pushNewline, push, withIndent } = context
+  const { newline, push, withIndent } = context
 
   push('() => {')
   withIndent(() => {
     genBlockFunctionContent(oper, context)
   })
-  pushNewline('}')
+  newline('}')
 }
index 1cc76246d60cd8b7f672b80a5eec72092a61aa74..f596cb2c9aa250676e69529c2e93912ef8294035 100644 (file)
@@ -7,10 +7,16 @@ export function genSetModelValue(
   oper: SetModelValueIRNode,
   context: CodegenContext,
 ) {
-  const { vaporHelper, push, newline, pushFnCall } = context
+  const {
+    vaporHelper,
+    push,
+    newline,
+    pushCall,
+    options: { isTS },
+  } = context
 
   newline()
-  pushFnCall(
+  pushCall(
     vaporHelper('on'),
     // 1st arg: event name
     () => push(`n${oper.element}`),
@@ -26,7 +32,7 @@ export function genSetModelValue(
     },
     // 3rd arg: event handler
     () => {
-      push((context.isTS ? `($event: any)` : `$event`) + ' => ((')
+      push((isTS ? `($event: any)` : `$event`) + ' => ((')
       // TODO handle not a ref
       genExpression(oper.value, context)
       push(') = $event)')
index 7fb40fedf76e3e6840ff142a9a5b34d6eafd32e2..204c2f2c5b64a6aa8ee5e7968e170851e509f7d3 100644 (file)
@@ -4,7 +4,7 @@ import { genExpression } from './expression'
 import { isString } from '@vue/shared'
 
 export function genSetProp(oper: SetPropIRNode, context: CodegenContext) {
-  const { pushFnCall, pushMulti, newline, vaporHelper, helper } = context
+  const { pushCall, pushMulti, newline, vaporHelper, helper } = context
 
   newline()
 
@@ -27,7 +27,7 @@ export function genSetProp(oper: SetPropIRNode, context: CodegenContext) {
     }
 
     if (helperName) {
-      pushFnCall(
+      pushCall(
         vaporHelper(helperName),
         element,
         omitKey
@@ -35,7 +35,7 @@ export function genSetProp(oper: SetPropIRNode, context: CodegenContext) {
           : () => {
               const expr = () => genExpression(oper.key, context)
               if (oper.runtimeCamelize) {
-                pushFnCall(helper('camelize'), expr)
+                pushCall(helper('camelize'), expr)
               } else {
                 expr()
               }
@@ -46,13 +46,13 @@ export function genSetProp(oper: SetPropIRNode, context: CodegenContext) {
     }
   }
 
-  pushFnCall(
+  pushCall(
     vaporHelper('setDynamicProp'),
     element,
     // 2. key name
     () => {
       if (oper.runtimeCamelize) {
-        pushFnCall(helper('camelize'), () => genExpression(oper.key, context))
+        pushCall(helper('camelize'), () => genExpression(oper.key, context))
       } else if (oper.modifier) {
         pushMulti([`\`${oper.modifier}\${`, `}\``], () =>
           genExpression(oper.key, context),
index 78b8024d78f722a42e255b0ad37a4a338ab4826d..bcd0c0880d1e8f0a15729c8bc56d867609c89ca3 100644 (file)
@@ -3,9 +3,9 @@ import type { SetRefIRNode } from '../ir'
 import { genExpression } from './expression'
 
 export function genSetRef(oper: SetRefIRNode, context: CodegenContext) {
-  const { newline, pushFnCall, vaporHelper } = context
+  const { newline, pushCall, vaporHelper } = context
   newline()
-  pushFnCall(vaporHelper('setRef'), `n${oper.element}`, () =>
+  pushCall(vaporHelper('setRef'), `n${oper.element}`, () =>
     genExpression(oper.value, context),
   )
 }
index 4c6cc02cf29e2a093c6fe0a1549a71620e891df6..7752acd97110809fc76058a63bd0108307e160d7 100644 (file)
@@ -3,9 +3,9 @@ import type { CreateTextNodeIRNode, SetTextIRNode } from '../ir'
 import { genExpression } from './expression'
 
 export function genSetText(oper: SetTextIRNode, context: CodegenContext) {
-  const { pushFnCall, newline, vaporHelper } = context
+  const { pushCall, newline, vaporHelper } = context
   newline()
-  pushFnCall(vaporHelper('setText'), `n${oper.element}`, () =>
+  pushCall(vaporHelper('setText'), `n${oper.element}`, () =>
     genExpression(oper.value, context),
   )
 }
@@ -14,9 +14,9 @@ export function genCreateTextNode(
   oper: CreateTextNodeIRNode,
   context: CodegenContext,
 ) {
-  const { pushNewline, pushFnCall, vaporHelper } = context
-  pushNewline(`const n${oper.id} = `)
-  pushFnCall(vaporHelper('createTextNode'), () =>
+  const { newline, pushCall, vaporHelper } = context
+  newline(`const n${oper.id} = `)
+  pushCall(vaporHelper('createTextNode'), () =>
     genExpression(oper.value, context),
   )
 }