]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
refactor(compiler-vapor): symbol for newline (#104)
author三咲智子 Kevin Deng <sxzz@sxzz.moe>
Wed, 31 Jan 2024 09:26:07 +0000 (17:26 +0800)
committerGitHub <noreply@github.com>
Wed, 31 Jan 2024 09:26:07 +0000 (17:26 +0800)
16 files changed:
packages/compiler-vapor/src/generate.ts
packages/compiler-vapor/src/generators/block.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/for.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/operation.ts
packages/compiler-vapor/src/generators/prop.ts
packages/compiler-vapor/src/generators/ref.ts
packages/compiler-vapor/src/generators/template.ts
packages/compiler-vapor/src/generators/text.ts
packages/compiler-vapor/src/ir.ts
packages/runtime-vapor/src/render.ts

index 5cb27aff367f579d9f2fbeb013cd4996ad227476..134d988aef439cf508f366bd3045482fa7d8dd1a 100644 (file)
@@ -19,6 +19,9 @@ interface CodegenOptions extends BaseCodegenOptions {
 }
 
 export type CodeFragment =
+  | typeof NEWLINE
+  | typeof INDENT_START
+  | typeof INDENT_END
   | string
   | [code: string, newlineIndex?: number, loc?: SourceLocation, name?: string]
   | undefined
@@ -28,13 +31,9 @@ export class CodegenContext {
 
   source: string
   code: CodeFragment[]
-  indentLevel: number = 0
   map?: SourceMapGenerator
 
   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[]>
@@ -58,12 +57,6 @@ export class CodegenContext {
   ): CodeFragment[] => {
     return [name, ...this.multi(['(', ')', ', '], ...args)]
   }
-  withIndent = <T>(fn: () => T): T => {
-    ++this.indentLevel
-    const ret = fn()
-    --this.indentLevel
-    return ret
-  }
 
   helpers = new Set<string>([])
   vaporHelpers = new Set<string>([])
@@ -136,13 +129,17 @@ export interface VaporCodegenResult extends BaseCodegenResult {
   vaporHelpers: Set<string>
 }
 
+export const NEWLINE = Symbol(__DEV__ ? `newline` : ``)
+export const INDENT_START = Symbol(__DEV__ ? `indent start` : ``)
+export const INDENT_END = Symbol(__DEV__ ? `indent end` : ``)
+
 // IR -> JS codegen
 export function generate(
   ir: RootIRNode,
   options: CodegenOptions = {},
 ): VaporCodegenResult {
   const ctx = new CodegenContext(ir, options)
-  const { push, withIndent, newline, helpers, vaporHelpers } = ctx
+  const { push, helpers, vaporHelpers } = ctx
 
   const functionName = 'render'
   const isSetupInlined = !!options.inline
@@ -151,18 +148,17 @@ export function generate(
   } else {
     push(
       // placeholder for preamble
-      newline(),
-      newline(),
+      NEWLINE,
+      NEWLINE,
       `export function ${functionName}(_ctx) {`,
     )
   }
 
-  withIndent(() => {
-    ir.template.forEach((template, i) => push(...genTemplate(template, i, ctx)))
-    push(...genBlockFunctionContent(ir, ctx))
-  })
+  push(INDENT_START)
+  ir.template.forEach((template, i) => push(...genTemplate(template, i, ctx)))
+  push(...genBlockFunctionContent(ir, ctx))
+  push(INDENT_END, NEWLINE)
 
-  push(newline())
   if (isSetupInlined) {
     push('})()')
   } else {
@@ -198,9 +194,21 @@ export function generate(
 function genCodeFragment(context: CodegenContext) {
   let codegen = ''
   const pos = { line: 1, column: 1, offset: 0 }
+  let indentLevel = 0
 
   for (let frag of context.code) {
     if (!frag) continue
+
+    if (frag === NEWLINE) {
+      frag = [`\n${`  `.repeat(indentLevel)}`, NewlineType.Start]
+    } else if (frag === INDENT_START) {
+      indentLevel++
+      continue
+    } else if (frag === INDENT_END) {
+      indentLevel--
+      continue
+    }
+
     if (isString(frag)) frag = [frag]
 
     let [code, newlineIndex = NewlineType.None, loc, name] = frag
@@ -269,8 +277,7 @@ function genCodeFragment(context: CodegenContext) {
   }
 }
 
-export function buildCodeFragment() {
-  const frag: CodeFragment[] = []
+export function buildCodeFragment(...frag: CodeFragment[]) {
   const push = frag.push.bind(frag)
   return [frag, push] as const
 }
index 5316b1ad288c273e3e70f126f01a3a1d6bbdb6ac..9c57ca98d9a2de99af6b10d5b49057cbd862f82d 100644 (file)
@@ -9,6 +9,9 @@ import {
 import {
   type CodeFragment,
   type CodegenContext,
+  INDENT_END,
+  INDENT_START,
+  NEWLINE,
   buildCodeFragment,
 } from '../generate'
 import { genWithDirective } from './directive'
@@ -20,13 +23,14 @@ export function genBlockFunction(
   args: CodeFragment[] = [],
   returnValue?: () => CodeFragment[],
 ): CodeFragment[] {
-  const { newline, withIndent } = context
   return [
     '(',
     ...args,
     ') => {',
-    ...withIndent(() => genBlockFunctionContent(oper, context, returnValue)),
-    newline(),
+    INDENT_START,
+    ...genBlockFunctionContent(oper, context, returnValue),
+    INDENT_END,
+    NEWLINE,
     '}',
   ]
 }
@@ -36,15 +40,16 @@ export function genBlockFunctionContent(
   ctx: CodegenContext,
   returnValue?: () => CodeFragment[],
 ): CodeFragment[] {
-  const { newline, vaporHelper } = ctx
-  const [frag, push] = buildCodeFragment()
-
-  push(newline(), `const n${ir.dynamic.id} = t${ir.templateIndex}()`)
+  const { vaporHelper } = ctx
+  const [frag, push] = buildCodeFragment(
+    NEWLINE,
+    `const n${ir.dynamic.id} = t${ir.templateIndex}()`,
+  )
 
   const children = genChildren(ir.dynamic.children)
   if (children) {
     push(
-      newline(),
+      NEWLINE,
       `const ${children} = ${vaporHelper('children')}(n${ir.dynamic.id})`,
     )
   }
@@ -61,7 +66,7 @@ export function genBlockFunctionContent(
   push(...genEffects(ir.effect, ctx))
 
   push(
-    newline(),
+    NEWLINE,
     'return ',
     ...(returnValue ? returnValue() : [`n${ir.dynamic.id}`]),
   )
index b9cc8cf019082bd2c52005a262fa5b357c14aec2..e5d4211aaf61216c62bd254f742ea85ff686dd18 100644 (file)
@@ -1,23 +1,20 @@
 import { createSimpleExpression, isSimpleIdentifier } from '@vue/compiler-dom'
 import { camelize } from '@vue/shared'
 import { genExpression } from './expression'
-import type { CodeFragment, CodegenContext } from '../generate'
+import { type CodeFragment, type CodegenContext, NEWLINE } from '../generate'
 import type { WithDirectiveIRNode } from '../ir'
 
 export function genWithDirective(
   opers: WithDirectiveIRNode[],
   context: CodegenContext,
-) {
-  const { newline, call, multi, vaporHelper } = context
+): CodeFragment[] {
+  const { call, multi, vaporHelper } = context
 
   const element = `n${opers[0].element}`
   const directiveItems = opers.map(genDirective)
   const directives = multi(['[', ']', ', '], ...directiveItems)
 
-  return [
-    newline(),
-    ...call(vaporHelper('withDirectives'), element, directives),
-  ]
+  return [NEWLINE, ...call(vaporHelper('withDirectives'), element, directives)]
 
   function genDirective({ dir, builtin }: WithDirectiveIRNode): CodeFragment[] {
     const NULL = 'void 0'
index c4e859bd4409b99aac27e6995243cb4e043f850f..a8f755c56933c2369c547b0be6e9d6bbb408d2c2 100644 (file)
@@ -1,4 +1,4 @@
-import type { CodeFragment, CodegenContext } from '../generate'
+import { type CodeFragment, type CodegenContext, NEWLINE } from '../generate'
 import type {
   AppendNodeIRNode,
   InsertNodeIRNode,
@@ -7,13 +7,13 @@ import type {
 
 export function genInsertNode(
   oper: InsertNodeIRNode,
-  { newline, call, vaporHelper }: CodegenContext,
+  { call, vaporHelper }: CodegenContext,
 ): CodeFragment[] {
   const elements = ([] as number[]).concat(oper.element)
   let element = elements.map(el => `n${el}`).join(', ')
   if (elements.length > 1) element = `[${element}]`
   return [
-    newline(),
+    NEWLINE,
     ...call(
       vaporHelper('insert'),
       element,
@@ -25,10 +25,10 @@ export function genInsertNode(
 
 export function genPrependNode(
   oper: PrependNodeIRNode,
-  { newline, call, vaporHelper }: CodegenContext,
+  { call, vaporHelper }: CodegenContext,
 ): CodeFragment[] {
   return [
-    newline(),
+    NEWLINE,
     ...call(
       vaporHelper('prepend'),
       `n${oper.parent}`,
@@ -39,11 +39,10 @@ export function genPrependNode(
 
 export function genAppendNode(
   oper: AppendNodeIRNode,
-  { newline, call, vaporHelper }: CodegenContext,
+  { call, vaporHelper }: CodegenContext,
 ): CodeFragment[] {
-  newline()
   return [
-    newline(),
+    NEWLINE,
     ...call(
       vaporHelper('append'),
       `n${oper.parent}`,
index e6695f0a4ece0502bf91d1717f22f90ec6fee9ee..77a68433eba5004891ce584090e9a2e405f034e2 100644 (file)
@@ -1,5 +1,5 @@
 import { isMemberExpression } from '@vue/compiler-dom'
-import type { CodeFragment, CodegenContext } from '../generate'
+import { type CodeFragment, type CodegenContext, NEWLINE } from '../generate'
 import type { SetEventIRNode } from '../ir'
 import { genExpression } from './expression'
 
@@ -11,7 +11,7 @@ export function genSetEvent(
   oper: SetEventIRNode,
   context: CodegenContext,
 ): CodeFragment[] {
-  const { vaporHelper, newline, call, options: ctxOptions } = context
+  const { vaporHelper, call, options: ctxOptions } = context
   const { keys, nonKeys, options } = oper.modifiers
 
   const name = genName()
@@ -20,7 +20,7 @@ export function genSetEvent(
     !!options.length && `{ ${options.map(v => `${v}: true`).join(', ')} }`
 
   return [
-    newline(),
+    NEWLINE,
     ...call(vaporHelper('on'), `n${oper.element}`, name, handler, opt),
   ]
 
index 6514686abe51cf728e08c0e04ecf190a400d3e70..9640553e83958c57b76544e6e84227df0d0eef4d 100644 (file)
@@ -3,6 +3,9 @@ import { genExpression } from './expression'
 import {
   type CodeFragment,
   type CodegenContext,
+  INDENT_END,
+  INDENT_START,
+  NEWLINE,
   buildCodeFragment,
 } from '../generate'
 import type { ForIRNode, IREffect } from '../ir'
@@ -13,7 +16,7 @@ export function genFor(
   oper: ForIRNode,
   context: CodegenContext,
 ): CodeFragment[] {
-  const { newline, call, vaporHelper } = context
+  const { call, vaporHelper } = context
   const { source, value, key, render } = oper
 
   const rawValue = value && value.content
@@ -39,50 +42,49 @@ export function genFor(
   context.genEffect = undefined
 
   return [
-    newline(),
+    NEWLINE,
     `const n${oper.id} = `,
     ...call(vaporHelper('createFor'), sourceExpr, blockFn),
   ]
 
-  function genEffectInFor(effects: IREffect[]) {
+  function genEffectInFor(effects: IREffect[]): CodeFragment[] {
     if (!effects.length) {
       updateFn = '() => {}'
       return []
     }
 
-    const [frag, push] = buildCodeFragment()
-
-    context.withIndent(() => {
-      if (rawValue || rawKey) {
-        push(
-          newline(),
-          'const ',
-          '[',
-          rawValue && [rawValue, NewlineType.None, value.loc],
-          rawKey && ', ',
-          rawKey && [rawKey, NewlineType.None, key.loc],
-          '] = _block.s',
-        )
-      }
+    const [frag, push] = buildCodeFragment(INDENT_START)
+    // const [value, key] = _block.s
+    if (rawValue || rawKey) {
+      push(
+        NEWLINE,
+        'const ',
+        '[',
+        rawValue && [rawValue, NewlineType.None, value.loc],
+        rawKey && ', ',
+        rawKey && [rawKey, NewlineType.None, key.loc],
+        '] = _block.s',
+      )
+    }
 
-      const idMap: Record<string, string | null> = {}
-      if (value) idMap[value.content] = null
-      if (key) idMap[key.content] = null
+    const idMap: Record<string, string | null> = {}
+    if (value) idMap[value.content] = null
+    if (key) idMap[key.content] = null
+    context.withId(() => {
+      effects.forEach(effect =>
+        push(...genOperations(effect.operations, context)),
+      )
+    }, idMap)
 
-      context.withId(() => {
-        effects.forEach(effect =>
-          push(...genOperations(effect.operations, context)),
-        )
-      }, idMap)
-    })
+    push(INDENT_END)
 
     return [
-      newline(),
+      NEWLINE,
       `const ${updateFn} = () => {`,
       ...frag,
-      newline(),
+      NEWLINE,
       '}',
-      newline(),
+      NEWLINE,
       `${vaporHelper('renderEffect')}(${updateFn})`,
     ]
   }
index 803bab3a14de1abcc42e540714292b7ff4a57528..00cecc909bc3a7757d2aa5920f7753ba7c67533c 100644 (file)
@@ -1,4 +1,4 @@
-import type { CodeFragment, CodegenContext } from '../generate'
+import { type CodeFragment, type CodegenContext, NEWLINE } from '../generate'
 import type { SetHtmlIRNode } from '../ir'
 import { genExpression } from './expression'
 
@@ -6,9 +6,9 @@ export function genSetHtml(
   oper: SetHtmlIRNode,
   context: CodegenContext,
 ): CodeFragment[] {
-  const { newline, call, vaporHelper } = context
+  const { call, vaporHelper } = context
   return [
-    newline(),
+    NEWLINE,
     ...call(
       vaporHelper('setHtml'),
       `n${oper.element}`,
index 7c3eeb6c64b5c9a0e2e3b6f9d1593d591d67e3a1..df585b449ef0d50a8d1aaf002c0f5efefad16cf3 100644 (file)
@@ -1,6 +1,7 @@
 import {
   type CodeFragment,
   type CodegenContext,
+  NEWLINE,
   buildCodeFragment,
 } from '../generate'
 import { IRNodeTypes, type IfIRNode } from '../ir'
@@ -12,7 +13,7 @@ export function genIf(
   context: CodegenContext,
   isNested = false,
 ): CodeFragment[] {
-  const { call, vaporHelper, newline } = context
+  const { call, vaporHelper } = context
   const { condition, positive, negative } = oper
   const [frag, push] = buildCodeFragment()
 
@@ -33,7 +34,7 @@ export function genIf(
     }
   }
 
-  if (!isNested) push(newline(), `const n${oper.id} = `)
+  if (!isNested) push(NEWLINE, `const n${oper.id} = `)
   push(
     ...call(vaporHelper('createIf'), conditionExpr, positiveArg, negativeArg),
   )
index 4295f74778ce18d06cf773544b97227ff602389a..7912195f46c029702fda210f52b6fc8f6d1511b0 100644 (file)
@@ -1,7 +1,7 @@
 import { camelize, isString } from '@vue/shared'
 import { genExpression } from './expression'
 import type { SetModelValueIRNode } from '../ir'
-import type { CodeFragment, CodegenContext } from '../generate'
+import { type CodeFragment, type CodegenContext, NEWLINE } from '../generate'
 
 export function genSetModelValue(
   oper: SetModelValueIRNode,
@@ -9,33 +9,22 @@ export function genSetModelValue(
 ): CodeFragment[] {
   const {
     vaporHelper,
-    newline,
     call,
     options: { isTS },
   } = context
 
-  const name = genName()
-  const handler = genHandler()
+  const name = isString(oper.key)
+    ? [JSON.stringify(`update:${camelize(oper.key)}`)]
+    : ['`update:${', ...genExpression(oper.key, context), '}`']
+  const handler = [
+    (isTS ? `($event: any)` : `$event`) + ' => ((',
+    // TODO handle not a ref
+    ...genExpression(oper.value, context),
+    ') = $event)',
+  ]
 
   return [
-    newline(),
+    NEWLINE,
     ...call(vaporHelper('on'), `n${oper.element}`, name, handler),
   ]
-
-  function genName(): CodeFragment[] {
-    if (isString(oper.key)) {
-      return [JSON.stringify(`update:${camelize(oper.key)}`)]
-    } else {
-      return ['`update:${', ...genExpression(oper.key, context), '}`']
-    }
-  }
-
-  function genHandler(): CodeFragment[] {
-    return [
-      (isTS ? `($event: any)` : `$event`) + ' => ((',
-      // TODO handle not a ref
-      ...genExpression(oper.value, context),
-      ') = $event)',
-    ]
-  }
 }
index 3e414095f3a5bc6f3b13a2a9936d7decc42d2ae9..0f5117bde468b48ff0569b0641b0dd4b9a4c37e2 100644 (file)
@@ -2,6 +2,9 @@ import { type IREffect, IRNodeTypes, type OperationNode } from '../ir'
 import {
   type CodeFragment,
   type CodegenContext,
+  INDENT_END,
+  INDENT_START,
+  NEWLINE,
   buildCodeFragment,
 } from '../generate'
 import { genAppendNode, genInsertNode, genPrependNode } from './dom'
@@ -74,13 +77,14 @@ export function genEffects(effects: IREffect[], context: CodegenContext) {
 }
 
 function genEffect({ operations }: IREffect, context: CodegenContext) {
-  const { newline, withIndent, vaporHelper } = context
-  const [frag, push] = buildCodeFragment()
-  push(newline(), `${vaporHelper('renderEffect')}(() => {`)
-  withIndent(() => {
-    operations.forEach(op => push(...genOperation(op, context)))
-  })
-  push(newline(), '})')
+  const { vaporHelper } = context
+  const [frag, push] = buildCodeFragment(
+    NEWLINE,
+    `${vaporHelper('renderEffect')}(() => {`,
+    INDENT_START,
+  )
+  operations.forEach(op => push(...genOperation(op, context)))
+  push(INDENT_END, NEWLINE, '})')
   return frag
 }
 
index 6c326da350305a53ca5bec05f4135a135ab79bed..43b5510984bcad60e9b393765cdd8b86d373adfd 100644 (file)
@@ -1,4 +1,4 @@
-import type { CodeFragment, CodegenContext } from '../generate'
+import { type CodeFragment, type CodegenContext, NEWLINE } from '../generate'
 import type { SetPropIRNode, VaporHelper } from '../ir'
 import { genExpression } from './expression'
 import { isString } from '@vue/shared'
@@ -7,7 +7,7 @@ export function genSetProp(
   oper: SetPropIRNode,
   context: CodegenContext,
 ): CodeFragment[] {
-  const { call, newline, vaporHelper, helper } = context
+  const { call, vaporHelper, helper } = context
 
   const element = `n${oper.element}`
   const key = genExpression(oper.key, context)
@@ -31,14 +31,14 @@ export function genSetProp(
 
     if (helperName) {
       return [
-        newline(),
+        NEWLINE,
         ...call(vaporHelper(helperName), element, omitKey ? false : key, value),
       ]
     }
   }
 
   return [
-    newline(),
+    NEWLINE,
     ...call(vaporHelper('setDynamicProp'), element, genDynamicKey(), value),
   ]
 
index 896443037fc65257910265a653f07c5bc7acdedf..6037133b0781ea1b220bf27aa6789fa2ddcea883 100644 (file)
@@ -1,14 +1,14 @@
 import { genExpression } from './expression'
-import type { CodeFragment, CodegenContext } from '../generate'
+import { type CodeFragment, type CodegenContext, NEWLINE } from '../generate'
 import type { SetRefIRNode } from '../ir'
 
 export function genSetRef(
   oper: SetRefIRNode,
   context: CodegenContext,
 ): CodeFragment[] {
-  const { newline, call, vaporHelper } = context
+  const { call, vaporHelper } = context
   return [
-    newline(),
+    NEWLINE,
     ...call(
       vaporHelper('setRef'),
       [`n${oper.element}`],
index bbf79c061c6ecf37327bc21ba2f0b1184647e897..15945a1cde7550fe47946c95c65735f3389d61ee 100644 (file)
@@ -1,4 +1,4 @@
-import type { CodeFragment, CodegenContext } from '../generate'
+import { type CodeFragment, type CodegenContext, NEWLINE } from '../generate'
 import {
   type FragmentFactoryIRNode,
   IRNodeTypes,
@@ -8,18 +8,18 @@ import {
 export function genTemplate(
   node: TemplateFactoryIRNode | FragmentFactoryIRNode,
   index: number,
-  { newline, vaporHelper }: CodegenContext,
+  { vaporHelper }: CodegenContext,
 ): CodeFragment[] {
   if (node.type === IRNodeTypes.TEMPLATE_FACTORY) {
     // TODO source map?
     return [
-      newline(),
+      NEWLINE,
       `const t${index} = ${vaporHelper('template')}(${JSON.stringify(
         node.template,
       )})`,
     ]
   } else {
     // fragment
-    return [newline(), `const t${index} = ${vaporHelper('fragment')}()`]
+    return [NEWLINE, `const t${index} = ${vaporHelper('fragment')}()`]
   }
 }
index a5bc02c7b4cabbcdceb89f7213aa962e76f42719..8d9eeac90497ac35334338c8d0b2ac6da4346ebc 100644 (file)
@@ -1,4 +1,4 @@
-import type { CodeFragment, CodegenContext } from '../generate'
+import { type CodeFragment, type CodegenContext, NEWLINE } from '../generate'
 import type { CreateTextNodeIRNode, SetTextIRNode } from '../ir'
 import { genExpression } from './expression'
 
@@ -6,9 +6,9 @@ export function genSetText(
   oper: SetTextIRNode,
   context: CodegenContext,
 ): CodeFragment[] {
-  const { call, newline, vaporHelper } = context
+  const { call, vaporHelper } = context
   return [
-    newline(),
+    NEWLINE,
     ...call(
       vaporHelper('setText'),
       `n${oper.element}`,
@@ -21,9 +21,9 @@ export function genCreateTextNode(
   oper: CreateTextNodeIRNode,
   context: CodegenContext,
 ): CodeFragment[] {
-  const { newline, call, vaporHelper } = context
+  const { call, vaporHelper } = context
   return [
-    newline(),
+    NEWLINE,
     `const n${oper.id} = `,
     ...call(vaporHelper('createTextNode')),
   ]
index 1c9596f35a0aaf33a2f4cb0bc0de5c35c2c21b64..6962a1fff99ab0fafd7b7266902334a290f07b76 100644 (file)
@@ -40,8 +40,7 @@ export interface BaseIRNode {
   loc: SourceLocation
 }
 
-// TODO refactor
-export type VaporHelper = keyof typeof import('../../runtime-vapor/src')
+export type VaporHelper = keyof typeof import('@vue/runtime-vapor')
 
 export interface BlockFunctionIRNode extends BaseIRNode {
   type: IRNodeTypes.BLOCK_FUNCTION
index 8bae599530fcf087b28a9b763627534d83f3fdc3..9beb919f2562c3971a3ca9c30c4e4a92947c87b8 100644 (file)
@@ -12,7 +12,7 @@ import { invokeDirectiveHook } from './directive'
 import { insert, querySelector, remove } from './dom'
 import { queuePostRenderEffect } from './scheduler'
 
-export const fragmentKey = Symbol('fragment')
+export const fragmentKey = Symbol(__DEV__ ? `fragmentKey` : ``)
 
 export type Block = Node | Fragment | Block[]
 export type ParentBlock = ParentNode | Block[]