]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
wip: element transforms
authorEvan You <yyx990803@gmail.com>
Sat, 21 Sep 2019 19:47:26 +0000 (15:47 -0400)
committerEvan You <yyx990803@gmail.com>
Sat, 21 Sep 2019 19:47:26 +0000 (15:47 -0400)
18 files changed:
packages/compiler-core/src/ast.ts
packages/compiler-core/src/codegen.ts
packages/compiler-core/src/index.ts
packages/compiler-core/src/transform.ts
packages/compiler-core/src/transforms/element.ts [new file with mode: 0644]
packages/compiler-core/src/transforms/optimizeBlocks.ts [moved from packages/compiler-core/src/directives/vBind.ts with 100% similarity]
packages/compiler-core/src/transforms/optimizeClass.ts [moved from packages/compiler-core/src/directives/vModel.ts with 100% similarity]
packages/compiler-core/src/transforms/optimizeInterpolations.ts [moved from packages/compiler-core/src/optimizations/mergeExpressions.ts with 100% similarity]
packages/compiler-core/src/transforms/optimizePatchFlags.ts [moved from packages/compiler-core/src/directives/vOn.ts with 100% similarity]
packages/compiler-core/src/transforms/optimizeStyle.ts [moved from packages/compiler-core/src/directives/vPre.ts with 100% similarity]
packages/compiler-core/src/transforms/vBind.ts [moved from packages/compiler-core/src/directives/vSlot.ts with 100% similarity]
packages/compiler-core/src/transforms/vFor.ts [moved from packages/compiler-core/src/directives/vFor.ts with 100% similarity]
packages/compiler-core/src/transforms/vIf.ts [moved from packages/compiler-core/src/directives/vIf.ts with 100% similarity]
packages/compiler-core/src/transforms/vModel.ts [moved from packages/compiler-core/src/directives/vText.ts with 100% similarity]
packages/compiler-core/src/transforms/vOn.ts [moved from packages/compiler-core/src/optimizations/blocks.ts with 100% similarity]
packages/compiler-core/src/transforms/vPre.ts [moved from packages/compiler-core/src/optimizations/class.ts with 100% similarity]
packages/compiler-core/src/transforms/vSlot.ts [moved from packages/compiler-core/src/optimizations/patchFlag.ts with 100% similarity]
packages/compiler-core/src/transforms/vText.ts [moved from packages/compiler-core/src/optimizations/style.ts with 100% similarity]

index 853a95304ebf29c4bb0167efea8a0176d3170d85..7c3b7a35e7a15f54b924addfc8d9841ad6abf337 100644 (file)
@@ -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<CodegenNode | ChildNode[]>
 }
 
-// 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<Property>
+}
+
+export interface Property extends Node {
+  type: NodeTypes.PROPERTY
+  key: ExpressionNode
+  value: ExpressionNode
+}
+
+export interface ArrayExpression extends Node {
+  type: NodeTypes.ARRAY_EXPRESSION
+  elements: Array<CodegenNode>
 }
index e85bb19376cc67ef3f9c223819ce5e1338fbcf64..fadef39b732b2f7471d1c038f845cfb4220cfce6 100644 (file)
@@ -26,14 +26,15 @@ export interface CodegenResult {
   map?: RawSourceMap
 }
 
-interface CodegenContext extends Required<CodegenOptions> {
+export interface CodegenContext extends Required<CodegenOptions> {
   source: string
   code: string
   line: number
   column: number
   offset: number
   indent: number
-  identifiers: Set<string>
+  imports: Set<string>
+  knownIdentifiers: Set<string>
   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)
index 495933aceca02a3bc461497a4b49628da5189915..3950c5cbfe6ad009ae074ae25019a9f2206fdae2 100644 (file)
@@ -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'
index 3f97359ff36eaaeb9041586770fff5e66119de68..e200e1270e10ef2dc1c5bbe4747b9a3443f2419d 100644 (file)
@@ -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 (file)
index 0000000..263af75
--- /dev/null
@@ -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
+}