From: 三咲智子 Kevin Deng Date: Wed, 31 Jan 2024 09:26:07 +0000 (+0800) Subject: refactor(compiler-vapor): symbol for newline (#104) X-Git-Tag: v3.6.0-alpha.1~16^2~621 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=46e83e968112c21e34e2a9b13657149e9bef51b5;p=thirdparty%2Fvuejs%2Fcore.git refactor(compiler-vapor): symbol for newline (#104) --- diff --git a/packages/compiler-vapor/src/generate.ts b/packages/compiler-vapor/src/generate.ts index 5cb27aff36..134d988aef 100644 --- a/packages/compiler-vapor/src/generate.ts +++ b/packages/compiler-vapor/src/generate.ts @@ -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 @@ -58,12 +57,6 @@ export class CodegenContext { ): CodeFragment[] => { return [name, ...this.multi(['(', ')', ', '], ...args)] } - withIndent = (fn: () => T): T => { - ++this.indentLevel - const ret = fn() - --this.indentLevel - return ret - } helpers = new Set([]) vaporHelpers = new Set([]) @@ -136,13 +129,17 @@ export interface VaporCodegenResult extends BaseCodegenResult { vaporHelpers: Set } +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 } diff --git a/packages/compiler-vapor/src/generators/block.ts b/packages/compiler-vapor/src/generators/block.ts index 5316b1ad28..9c57ca98d9 100644 --- a/packages/compiler-vapor/src/generators/block.ts +++ b/packages/compiler-vapor/src/generators/block.ts @@ -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}`]), ) diff --git a/packages/compiler-vapor/src/generators/directive.ts b/packages/compiler-vapor/src/generators/directive.ts index b9cc8cf019..e5d4211aaf 100644 --- a/packages/compiler-vapor/src/generators/directive.ts +++ b/packages/compiler-vapor/src/generators/directive.ts @@ -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' diff --git a/packages/compiler-vapor/src/generators/dom.ts b/packages/compiler-vapor/src/generators/dom.ts index c4e859bd44..a8f755c569 100644 --- a/packages/compiler-vapor/src/generators/dom.ts +++ b/packages/compiler-vapor/src/generators/dom.ts @@ -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}`, diff --git a/packages/compiler-vapor/src/generators/event.ts b/packages/compiler-vapor/src/generators/event.ts index e6695f0a4e..77a68433eb 100644 --- a/packages/compiler-vapor/src/generators/event.ts +++ b/packages/compiler-vapor/src/generators/event.ts @@ -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), ] diff --git a/packages/compiler-vapor/src/generators/for.ts b/packages/compiler-vapor/src/generators/for.ts index 6514686abe..9640553e83 100644 --- a/packages/compiler-vapor/src/generators/for.ts +++ b/packages/compiler-vapor/src/generators/for.ts @@ -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 = {} - if (value) idMap[value.content] = null - if (key) idMap[key.content] = null + const idMap: Record = {} + 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})`, ] } diff --git a/packages/compiler-vapor/src/generators/html.ts b/packages/compiler-vapor/src/generators/html.ts index 803bab3a14..00cecc909b 100644 --- a/packages/compiler-vapor/src/generators/html.ts +++ b/packages/compiler-vapor/src/generators/html.ts @@ -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}`, diff --git a/packages/compiler-vapor/src/generators/if.ts b/packages/compiler-vapor/src/generators/if.ts index 7c3eeb6c64..df585b449e 100644 --- a/packages/compiler-vapor/src/generators/if.ts +++ b/packages/compiler-vapor/src/generators/if.ts @@ -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), ) diff --git a/packages/compiler-vapor/src/generators/modelValue.ts b/packages/compiler-vapor/src/generators/modelValue.ts index 4295f74778..7912195f46 100644 --- a/packages/compiler-vapor/src/generators/modelValue.ts +++ b/packages/compiler-vapor/src/generators/modelValue.ts @@ -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)', - ] - } } diff --git a/packages/compiler-vapor/src/generators/operation.ts b/packages/compiler-vapor/src/generators/operation.ts index 3e414095f3..0f5117bde4 100644 --- a/packages/compiler-vapor/src/generators/operation.ts +++ b/packages/compiler-vapor/src/generators/operation.ts @@ -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 } diff --git a/packages/compiler-vapor/src/generators/prop.ts b/packages/compiler-vapor/src/generators/prop.ts index 6c326da350..43b5510984 100644 --- a/packages/compiler-vapor/src/generators/prop.ts +++ b/packages/compiler-vapor/src/generators/prop.ts @@ -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), ] diff --git a/packages/compiler-vapor/src/generators/ref.ts b/packages/compiler-vapor/src/generators/ref.ts index 896443037f..6037133b07 100644 --- a/packages/compiler-vapor/src/generators/ref.ts +++ b/packages/compiler-vapor/src/generators/ref.ts @@ -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}`], diff --git a/packages/compiler-vapor/src/generators/template.ts b/packages/compiler-vapor/src/generators/template.ts index bbf79c061c..15945a1cde 100644 --- a/packages/compiler-vapor/src/generators/template.ts +++ b/packages/compiler-vapor/src/generators/template.ts @@ -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')}()`] } } diff --git a/packages/compiler-vapor/src/generators/text.ts b/packages/compiler-vapor/src/generators/text.ts index a5bc02c7b4..8d9eeac904 100644 --- a/packages/compiler-vapor/src/generators/text.ts +++ b/packages/compiler-vapor/src/generators/text.ts @@ -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')), ] diff --git a/packages/compiler-vapor/src/ir.ts b/packages/compiler-vapor/src/ir.ts index 1c9596f35a..6962a1fff9 100644 --- a/packages/compiler-vapor/src/ir.ts +++ b/packages/compiler-vapor/src/ir.ts @@ -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 diff --git a/packages/runtime-vapor/src/render.ts b/packages/runtime-vapor/src/render.ts index 8bae599530..9beb919f25 100644 --- a/packages/runtime-vapor/src/render.ts +++ b/packages/runtime-vapor/src/render.ts @@ -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[]