From 93440bba97e1aa0ec6e3900b154a4fecd54acb7b Mon Sep 17 00:00:00 2001 From: Evan You Date: Sat, 21 Sep 2019 15:47:26 -0400 Subject: [PATCH] wip: element transforms --- packages/compiler-core/src/ast.ts | 62 ++++++++++--- packages/compiler-core/src/codegen.ts | 12 ++- packages/compiler-core/src/index.ts | 14 ++- packages/compiler-core/src/transform.ts | 2 +- .../compiler-core/src/transforms/element.ts | 87 +++++++++++++++++++ .../vBind.ts => transforms/optimizeBlocks.ts} | 0 .../vModel.ts => transforms/optimizeClass.ts} | 0 .../optimizeInterpolations.ts} | 0 .../optimizePatchFlags.ts} | 0 .../vPre.ts => transforms/optimizeStyle.ts} | 0 .../vSlot.ts => transforms/vBind.ts} | 0 .../src/{directives => transforms}/vFor.ts | 0 .../src/{directives => transforms}/vIf.ts | 0 .../vText.ts => transforms/vModel.ts} | 0 .../blocks.ts => transforms/vOn.ts} | 0 .../class.ts => transforms/vPre.ts} | 0 .../patchFlag.ts => transforms/vSlot.ts} | 0 .../style.ts => transforms/vText.ts} | 0 18 files changed, 158 insertions(+), 19 deletions(-) create mode 100644 packages/compiler-core/src/transforms/element.ts rename packages/compiler-core/src/{directives/vBind.ts => transforms/optimizeBlocks.ts} (100%) rename packages/compiler-core/src/{directives/vModel.ts => transforms/optimizeClass.ts} (100%) rename packages/compiler-core/src/{optimizations/mergeExpressions.ts => transforms/optimizeInterpolations.ts} (100%) rename packages/compiler-core/src/{directives/vOn.ts => transforms/optimizePatchFlags.ts} (100%) rename packages/compiler-core/src/{directives/vPre.ts => transforms/optimizeStyle.ts} (100%) rename packages/compiler-core/src/{directives/vSlot.ts => transforms/vBind.ts} (100%) rename packages/compiler-core/src/{directives => transforms}/vFor.ts (100%) rename packages/compiler-core/src/{directives => transforms}/vIf.ts (100%) rename packages/compiler-core/src/{directives/vText.ts => transforms/vModel.ts} (100%) rename packages/compiler-core/src/{optimizations/blocks.ts => transforms/vOn.ts} (100%) rename packages/compiler-core/src/{optimizations/class.ts => transforms/vPre.ts} (100%) rename packages/compiler-core/src/{optimizations/patchFlag.ts => transforms/vSlot.ts} (100%) rename packages/compiler-core/src/{optimizations/style.ts => transforms/vText.ts} (100%) diff --git a/packages/compiler-core/src/ast.ts b/packages/compiler-core/src/ast.ts index 853a95304e..7c3b7a35e7 100644 --- a/packages/compiler-core/src/ast.ts +++ b/packages/compiler-core/src/ast.ts @@ -15,9 +15,15 @@ export const enum NodeTypes { EXPRESSION, ATTRIBUTE, DIRECTIVE, + // containers IF, IF_BRANCH, - FOR + FOR, + // codegen + CALL_EXPRESSION, + OBJECT_EXPRESSION, + PROPERTY, + ARRAY_EXPRESSION } export const enum ElementTypes { @@ -32,7 +38,22 @@ export interface Node { loc: SourceLocation } +// The node's range. The `start` is inclusive and `end` is exclusive. +// [start, end) +export interface SourceLocation { + start: Position + end: Position + source: string +} + +export interface Position { + offset: number // from start of file + line: number + column: number +} + export type ParentNode = RootNode | ElementNode | IfBranchNode | ForNode + export type ChildNode = | ElementNode | ExpressionNode @@ -55,6 +76,7 @@ export interface ElementNode extends Node { attrs: AttributeNode[] directives: DirectiveNode[] children: ChildNode[] + codegenNode: CallExpression | undefined } export interface TextNode extends Node { @@ -108,16 +130,34 @@ export interface ForNode extends Node { children: ChildNode[] } -export interface Position { - offset: number // from start of file - line: number - column: number +// We also include a subset of JavaScript AST for code generation +// purposes. The AST is intentioanlly minimal just to meet the exact needs of +// Vue render function generation. +type CodegenNode = + | string + | CallExpression + | ObjectExpression + | ArrayExpression + | ExpressionNode + +export interface CallExpression extends Node { + type: NodeTypes.CALL_EXPRESSION + callee: string // can only be imported runtime helpers, so no source location + arguments: Array } -// The node's range. The `start` is inclusive and `end` is exclusive. -// [start, end) -export interface SourceLocation { - start: Position - end: Position - source: string +export interface ObjectExpression extends Node { + type: NodeTypes.OBJECT_EXPRESSION + properties: Array +} + +export interface Property extends Node { + type: NodeTypes.PROPERTY + key: ExpressionNode + value: ExpressionNode +} + +export interface ArrayExpression extends Node { + type: NodeTypes.ARRAY_EXPRESSION + elements: Array } diff --git a/packages/compiler-core/src/codegen.ts b/packages/compiler-core/src/codegen.ts index e85bb19376..fadef39b73 100644 --- a/packages/compiler-core/src/codegen.ts +++ b/packages/compiler-core/src/codegen.ts @@ -26,14 +26,15 @@ export interface CodegenResult { map?: RawSourceMap } -interface CodegenContext extends Required { +export interface CodegenContext extends Required { source: string code: string line: number column: number offset: number indent: number - identifiers: Set + imports: Set + knownIdentifiers: Set map?: SourceMapGenerator push(generatedCode: string, astNode?: ChildNode): void } @@ -77,11 +78,14 @@ function createCodegenContext( line: 1, offset: 0, indent: 0, - identifiers: new Set(), + imports: new Set(), + knownIdentifiers: new Set(), + // lazy require source-map implementation, only in non-browser builds! map: __BROWSER__ ? undefined : new (require('source-map')).SourceMapGenerator(), + push(generatedCode, node) { // TODO handle indent context.code += generatedCode @@ -145,7 +149,7 @@ function genNode(node: ChildNode, context: CodegenContext) { } } -function genElement(el: ElementNode, context: CodegenContext) {} +function genElement(node: ElementNode, context: CodegenContext) {} function genText(node: TextNode | ExpressionNode, context: CodegenContext) { context.push(JSON.stringify(node.content), node) diff --git a/packages/compiler-core/src/index.ts b/packages/compiler-core/src/index.ts index 495933acec..3950c5cbfe 100644 --- a/packages/compiler-core/src/index.ts +++ b/packages/compiler-core/src/index.ts @@ -1,14 +1,16 @@ import { parse, ParserOptions } from './parse' import { transform, TransformOptions } from './transform' import { generate, CodegenOptions, CodegenResult } from './codegen' +import { RootNode } from './ast' +import { isString } from '@vue/shared' export type CompilerOptions = ParserOptions & TransformOptions & CodegenOptions export function compile( - template: string, + template: string | RootNode, options: CompilerOptions = {} ): CodegenResult { - const ast = parse(template, options) + const ast = isString(template) ? parse(template, options) : template transform(ast, { ...options, @@ -27,9 +29,15 @@ export { transform, createDirectiveTransform, TransformOptions, + TransformContext, Transform, DirectiveTransform } from './transform' -export { generate, CodegenOptions, CodegenResult } from './codegen' +export { + generate, + CodegenOptions, + CodegenContext, + CodegenResult +} from './codegen' export { ErrorCodes, CompilerError, createCompilerError } from './errors' export * from './ast' diff --git a/packages/compiler-core/src/transform.ts b/packages/compiler-core/src/transform.ts index 3f97359ff3..e200e1270e 100644 --- a/packages/compiler-core/src/transform.ts +++ b/packages/compiler-core/src/transform.ts @@ -22,7 +22,7 @@ export interface TransformOptions { onError?: (error: CompilerError) => void } -interface TransformContext { +export interface TransformContext { transforms: Transform[] emitError: (error: CompilerError) => void parent: ParentNode diff --git a/packages/compiler-core/src/transforms/element.ts b/packages/compiler-core/src/transforms/element.ts new file mode 100644 index 0000000000..263af75fed --- /dev/null +++ b/packages/compiler-core/src/transforms/element.ts @@ -0,0 +1,87 @@ +import { Transform, TransformContext } from '../transform' +import { + NodeTypes, + ElementTypes, + CallExpression, + ObjectExpression, + ElementNode +} from '../ast' + +// generate a JavaScript AST for this element's codegen +export const prepareElementForCodegen: Transform = (node, context) => { + if (node.type === NodeTypes.ELEMENT) { + if ( + node.tagType === ElementTypes.ELEMENT || + node.tagType === ElementTypes.COMPONENT + ) { + const isComponent = node.tagType === ElementTypes.ELEMENT + const hasProps = node.attrs.length > 0 || node.directives.length > 0 + const hasChildren = node.children.length > 0 + + const args: CallExpression['arguments'] = [ + isComponent ? node.tag : `"${node.tag}"` + ] + // props + if (hasProps) { + args.push(buildProps(node)) + } + // children + if (hasChildren) { + if (!hasProps) { + // placeholder for null props, but use `0` for more condense code + args.push(`0`) + } + args.push(isComponent ? buildSlots(node, context) : node.children) + } + + node.codegenNode = { + type: NodeTypes.CALL_EXPRESSION, + loc: node.loc, + callee: `h`, + arguments: args + } + } + } +} + +function buildProps({ loc, attrs }: ElementNode): ObjectExpression { + return { + type: NodeTypes.OBJECT_EXPRESSION, + loc, + // At this stage we will only process static attrs. Directive bindings will + // be handled by their respective transforms which adds/modifies the props. + properties: attrs.map(({ name, value, loc }) => { + return { + type: NodeTypes.PROPERTY, + loc, + key: { + type: NodeTypes.EXPRESSION, + loc, + content: name, + isStatic: true + }, + value: { + type: NodeTypes.EXPRESSION, + loc: value ? value.loc : loc, + content: value ? value.content : '', + isStatic: true + } + } + }) + } +} + +function buildSlots( + { loc, children }: ElementNode, + context: TransformContext +): ObjectExpression { + const slots: ObjectExpression = { + type: NodeTypes.OBJECT_EXPRESSION, + loc, + properties: [] + } + + // TODO + + return slots +} diff --git a/packages/compiler-core/src/directives/vBind.ts b/packages/compiler-core/src/transforms/optimizeBlocks.ts similarity index 100% rename from packages/compiler-core/src/directives/vBind.ts rename to packages/compiler-core/src/transforms/optimizeBlocks.ts diff --git a/packages/compiler-core/src/directives/vModel.ts b/packages/compiler-core/src/transforms/optimizeClass.ts similarity index 100% rename from packages/compiler-core/src/directives/vModel.ts rename to packages/compiler-core/src/transforms/optimizeClass.ts diff --git a/packages/compiler-core/src/optimizations/mergeExpressions.ts b/packages/compiler-core/src/transforms/optimizeInterpolations.ts similarity index 100% rename from packages/compiler-core/src/optimizations/mergeExpressions.ts rename to packages/compiler-core/src/transforms/optimizeInterpolations.ts diff --git a/packages/compiler-core/src/directives/vOn.ts b/packages/compiler-core/src/transforms/optimizePatchFlags.ts similarity index 100% rename from packages/compiler-core/src/directives/vOn.ts rename to packages/compiler-core/src/transforms/optimizePatchFlags.ts diff --git a/packages/compiler-core/src/directives/vPre.ts b/packages/compiler-core/src/transforms/optimizeStyle.ts similarity index 100% rename from packages/compiler-core/src/directives/vPre.ts rename to packages/compiler-core/src/transforms/optimizeStyle.ts diff --git a/packages/compiler-core/src/directives/vSlot.ts b/packages/compiler-core/src/transforms/vBind.ts similarity index 100% rename from packages/compiler-core/src/directives/vSlot.ts rename to packages/compiler-core/src/transforms/vBind.ts diff --git a/packages/compiler-core/src/directives/vFor.ts b/packages/compiler-core/src/transforms/vFor.ts similarity index 100% rename from packages/compiler-core/src/directives/vFor.ts rename to packages/compiler-core/src/transforms/vFor.ts diff --git a/packages/compiler-core/src/directives/vIf.ts b/packages/compiler-core/src/transforms/vIf.ts similarity index 100% rename from packages/compiler-core/src/directives/vIf.ts rename to packages/compiler-core/src/transforms/vIf.ts diff --git a/packages/compiler-core/src/directives/vText.ts b/packages/compiler-core/src/transforms/vModel.ts similarity index 100% rename from packages/compiler-core/src/directives/vText.ts rename to packages/compiler-core/src/transforms/vModel.ts diff --git a/packages/compiler-core/src/optimizations/blocks.ts b/packages/compiler-core/src/transforms/vOn.ts similarity index 100% rename from packages/compiler-core/src/optimizations/blocks.ts rename to packages/compiler-core/src/transforms/vOn.ts diff --git a/packages/compiler-core/src/optimizations/class.ts b/packages/compiler-core/src/transforms/vPre.ts similarity index 100% rename from packages/compiler-core/src/optimizations/class.ts rename to packages/compiler-core/src/transforms/vPre.ts diff --git a/packages/compiler-core/src/optimizations/patchFlag.ts b/packages/compiler-core/src/transforms/vSlot.ts similarity index 100% rename from packages/compiler-core/src/optimizations/patchFlag.ts rename to packages/compiler-core/src/transforms/vSlot.ts diff --git a/packages/compiler-core/src/optimizations/style.ts b/packages/compiler-core/src/transforms/vText.ts similarity index 100% rename from packages/compiler-core/src/optimizations/style.ts rename to packages/compiler-core/src/transforms/vText.ts -- 2.47.3