]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat(compiler): element codegen
authorEvan You <yyx990803@gmail.com>
Sun, 22 Sep 2019 20:50:57 +0000 (16:50 -0400)
committerEvan You <yyx990803@gmail.com>
Sun, 22 Sep 2019 20:50:57 +0000 (16:50 -0400)
15 files changed:
packages/compiler-core/__tests__/codegen.spec.ts
packages/compiler-core/src/ast.ts
packages/compiler-core/src/codegen.ts
packages/compiler-core/src/index.ts
packages/compiler-core/src/transforms/element.ts
packages/compiler-core/src/transforms/expression.ts [new file with mode: 0644]
packages/compiler-core/src/transforms/vFor.ts
packages/compiler-core/src/utils.ts
packages/compiler-dom/src/index.ts
packages/runtime-core/src/componentOptions.ts
packages/runtime-core/src/componentPublicInstanceProxy.ts
packages/runtime-core/src/helpers/renderList.ts [new file with mode: 0644]
packages/runtime-core/src/helpers/resolveAssets.ts [new file with mode: 0644]
packages/runtime-core/src/index.ts
packages/vue/src/index.ts

index 73e4a5a593fbf2febcc80b75aeebfd639571d554..d5f17f175e5d10622fdd6cda59fe3a582f4f5446 100644 (file)
@@ -8,15 +8,24 @@ describe('compiler: codegen', () => {
     const { code, map } = generate(ast, {
       filename: `foo.vue`
     })
-    expect(code).toBe(`["hello ", world]`)
+    expect(code).toBe(
+      `return function render() {
+  with (this) {
+    return [
+      "hello ",
+      world
+    ]
+  }
+}`
+    )
 
     expect(map!.sources).toEqual([`foo.vue`])
     expect(map!.sourcesContent).toEqual([source])
 
     const consumer = await new SourceMapConsumer(map as RawSourceMap)
     const pos = consumer.originalPositionFor({
-      line: 1,
-      column: 11
+      line: 5,
+      column: 6
     })
     expect(pos).toMatchObject({
       line: 1,
index d97b976ac65ad084abdb0eb1bc6f1b2c47fec18e..75d07527e7001e083de59533955f568b07b615be 100644 (file)
@@ -20,10 +20,10 @@ export const enum NodeTypes {
   IF_BRANCH,
   FOR,
   // codegen
-  CALL_EXPRESSION,
-  OBJECT_EXPRESSION,
-  PROPERTY,
-  ARRAY_EXPRESSION
+  JS_CALL_EXPRESSION,
+  JS_OBJECT_EXPRESSION,
+  JS_PROPERTY,
+  JS_ARRAY_EXPRESSION
 }
 
 export const enum ElementTypes {
@@ -129,36 +129,35 @@ export interface ForNode extends Node {
   children: ChildNode[]
 }
 
-// We also include a subset of JavaScript AST for code generation
-// purposes. The AST is intentioanlly minimal just to meet the exact needs of
+// We also include a number of JavaScript AST nodes for code generation.
+// The AST is an intentioanlly minimal subset just to meet the exact needs of
 // Vue render function generation.
-type CodegenNode =
-  | string
+export type JSChildNode =
   | CallExpression
   | ObjectExpression
   | ArrayExpression
   | ExpressionNode
 
 export interface CallExpression extends Node {
-  type: NodeTypes.CALL_EXPRESSION
+  type: NodeTypes.JS_CALL_EXPRESSION
   callee: string // can only be imported runtime helpers, so no source location
-  arguments: Array<CodegenNode | ChildNode[]>
+  arguments: Array<string | JSChildNode | ChildNode[]>
 }
 
 export interface ObjectExpression extends Node {
-  type: NodeTypes.OBJECT_EXPRESSION
+  type: NodeTypes.JS_OBJECT_EXPRESSION
   properties: Array<Property>
 }
 
 export interface Property extends Node {
-  type: NodeTypes.PROPERTY
+  type: NodeTypes.JS_PROPERTY
   key: ExpressionNode
   value: ExpressionNode
 }
 
 export interface ArrayExpression extends Node {
-  type: NodeTypes.ARRAY_EXPRESSION
-  elements: Array<CodegenNode>
+  type: NodeTypes.JS_ARRAY_EXPRESSION
+  elements: Array<string | JSChildNode>
 }
 
 export function createArrayExpression(
@@ -166,7 +165,7 @@ export function createArrayExpression(
   loc: SourceLocation
 ): ArrayExpression {
   return {
-    type: NodeTypes.ARRAY_EXPRESSION,
+    type: NodeTypes.JS_ARRAY_EXPRESSION,
     loc,
     elements
   }
@@ -177,7 +176,7 @@ export function createObjectExpression(
   loc: SourceLocation
 ): ObjectExpression {
   return {
-    type: NodeTypes.OBJECT_EXPRESSION,
+    type: NodeTypes.JS_OBJECT_EXPRESSION,
     loc,
     properties
   }
@@ -189,7 +188,7 @@ export function createObjectProperty(
   loc: SourceLocation
 ): Property {
   return {
-    type: NodeTypes.PROPERTY,
+    type: NodeTypes.JS_PROPERTY,
     loc,
     key,
     value
@@ -215,7 +214,7 @@ export function createCallExpression(
   loc: SourceLocation
 ): CallExpression {
   return {
-    type: NodeTypes.CALL_EXPRESSION,
+    type: NodeTypes.JS_CALL_EXPRESSION,
     loc,
     callee,
     arguments: args
index fadef39b732b2f7471d1c038f845cfb4220cfce6..ed2b6f050930a63b0d649d584c755ceca33e394d 100644 (file)
@@ -7,16 +7,26 @@ import {
   TextNode,
   CommentNode,
   ExpressionNode,
-  NodeTypes
+  NodeTypes,
+  JSChildNode,
+  CallExpression,
+  ArrayExpression,
+  ObjectExpression,
+  IfBranchNode
 } from './ast'
 import { SourceMapGenerator, RawSourceMap } from 'source-map'
-import { advancePositionWithMutation } from './utils'
+import { advancePositionWithMutation, assert } from './utils'
+import { isString, isArray } from '@vue/shared'
+import { RENDER_LIST_HELPER } from './transforms/vFor'
+
+type CodegenNode = ChildNode | JSChildNode
 
 export interface CodegenOptions {
-  // Assume ES module environment. If true, will generate import statements for
+  //  will generate import statements for
   // runtime helpers; otherwise will grab the helpers from global `Vue`.
   // default: false
-  module?: boolean
+  mode?: 'module' | 'function'
+  useWith?: boolean
   // Filename for source map generation.
   filename?: string
 }
@@ -32,52 +42,34 @@ export interface CodegenContext extends Required<CodegenOptions> {
   line: number
   column: number
   offset: number
-  indent: number
+  indentLevel: number
   imports: Set<string>
   knownIdentifiers: Set<string>
   map?: SourceMapGenerator
-  push(generatedCode: string, astNode?: ChildNode): void
-}
-
-export function generate(
-  ast: RootNode,
-  options: CodegenOptions = {}
-): CodegenResult {
-  const context = createCodegenContext(ast, options)
-  if (context.module) {
-    // TODO inject import statements on RootNode
-    context.push(`export function render() {\n`)
-    context.indent++
-    context.push(`  return `)
-  }
-  if (ast.children.length === 1) {
-    genNode(ast.children[0], context)
-  } else {
-    genChildren(ast.children, context)
-  }
-  if (context.module) {
-    context.indent--
-    context.push(`\n}`)
-  }
-  return {
-    code: context.code,
-    map: context.map ? context.map.toJSON() : undefined
-  }
+  push(code: string, node?: CodegenNode): void
+  indent(): void
+  deindent(): void
+  newline(): void
 }
 
 function createCodegenContext(
   ast: RootNode,
-  { module = false, filename = `template.vue.html` }: CodegenOptions
+  {
+    mode = 'function',
+    useWith = true,
+    filename = `template.vue.html`
+  }: CodegenOptions
 ): CodegenContext {
   const context: CodegenContext = {
-    module,
+    mode,
+    useWith,
     filename,
     source: ast.loc.source,
     code: ``,
     column: 1,
     line: 1,
     offset: 0,
-    indent: 0,
+    indentLevel: 0,
     imports: new Set(),
     knownIdentifiers: new Set(),
 
@@ -86,9 +78,8 @@ function createCodegenContext(
       ? undefined
       : new (require('source-map')).SourceMapGenerator(),
 
-    push(generatedCode, node) {
-      // TODO handle indent
-      context.code += generatedCode
+    push(code, node?: CodegenNode) {
+      context.code += code
       if (context.map) {
         if (node) {
           context.map.addMapping({
@@ -103,30 +94,113 @@ function createCodegenContext(
             }
           })
         }
-        advancePositionWithMutation(
-          context,
-          generatedCode,
-          generatedCode.length
-        )
+        advancePositionWithMutation(context, code, code.length)
       }
+    },
+    indent() {
+      newline(++context.indentLevel)
+    },
+    deindent() {
+      newline(--context.indentLevel)
+    },
+    newline() {
+      newline(context.indentLevel)
     }
   }
+  const newline = (n: number) => context.push('\n' + `  `.repeat(n))
   if (!__BROWSER__) {
     context.map!.setSourceContent(filename, context.source)
   }
   return context
 }
 
+export function generate(
+  ast: RootNode,
+  options: CodegenOptions = {}
+): CodegenResult {
+  const context = createCodegenContext(ast, options)
+  // TODO handle different output for module mode and IIFE mode
+  const { mode, push, useWith, indent, deindent } = context
+  if (mode === 'function') {
+    // TODO generate const declarations for helpers
+    push(`return `)
+  } else {
+    // TODO generate import statements for helpers
+    push(`export default `)
+  }
+  push(`function render() {`)
+  if (useWith) {
+    indent()
+    push(`with (this) {`)
+  }
+  indent()
+  push(`return `)
+  genChildren(ast.children, context)
+  if (useWith) {
+    deindent()
+    push(`}`)
+  }
+  deindent()
+  push(`}`)
+  return {
+    code: context.code,
+    map: context.map ? context.map.toJSON() : undefined
+  }
+}
+
+// This will generate a single vnode call if the list has length === 1.
 function genChildren(children: ChildNode[], context: CodegenContext) {
-  context.push(`[`)
-  for (let i = 0; i < children.length; i++) {
-    genNode(children[i], context)
-    if (i < children.length - 1) context.push(', ')
+  if (children.length === 1) {
+    genNode(children[0], context)
+  } else {
+    genNodeListAsArray(children, context)
   }
+}
+
+function genNodeListAsArray(
+  nodes: (string | CodegenNode | ChildNode[])[],
+  context: CodegenContext
+) {
+  const multilines = nodes.length > 1
+  context.push(`[`)
+  multilines && context.indent()
+  genNodeList(nodes, context, multilines)
+  multilines && context.deindent()
   context.push(`]`)
 }
 
-function genNode(node: ChildNode, context: CodegenContext) {
+function genNodeList(
+  nodes: (string | CodegenNode | ChildNode[])[],
+  context: CodegenContext,
+  multilines: boolean = false
+) {
+  const { push, newline } = context
+  for (let i = 0; i < nodes.length; i++) {
+    const node = nodes[i]
+    if (isString(node)) {
+      // plain code string
+      // note not adding quotes here because this can be any code,
+      // not just plain strings.
+      push(node)
+    } else if (isArray(node)) {
+      // child VNodes in a h() call
+      // not using genChildren here because we want them to always be an array
+      genNodeListAsArray(node, context)
+    } else {
+      genNode(node, context)
+    }
+    if (i < nodes.length - 1) {
+      if (multilines) {
+        push(',')
+        newline()
+      } else {
+        push(', ')
+      }
+    }
+  }
+}
+
+function genNode(node: CodegenNode, context: CodegenContext) {
   switch (node.type) {
     case NodeTypes.ELEMENT:
       genElement(node, context)
@@ -146,20 +220,55 @@ function genNode(node: ChildNode, context: CodegenContext) {
     case NodeTypes.FOR:
       genFor(node, context)
       break
+    case NodeTypes.JS_CALL_EXPRESSION:
+      genCallExpression(node, context)
+      break
+    case NodeTypes.JS_OBJECT_EXPRESSION:
+      genObjectExpression(node, context)
+      break
+    case NodeTypes.JS_ARRAY_EXPRESSION:
+      genArrayExpression(node, context)
   }
 }
 
-function genElement(node: ElementNode, context: CodegenContext) {}
+function genElement(node: ElementNode, context: CodegenContext) {
+  __DEV__ &&
+    assert(
+      node.codegenNode != null,
+      `AST is not transformed for codegen. ` +
+        `Apply appropriate transforms first.`
+    )
+  genCallExpression(node.codegenNode!, context, false)
+}
 
 function genText(node: TextNode | ExpressionNode, context: CodegenContext) {
   context.push(JSON.stringify(node.content), node)
 }
 
 function genExpression(node: ExpressionNode, context: CodegenContext) {
-  if (!__BROWSER__) {
-    // TODO parse expression content and rewrite identifiers
+  // if (node.codegenNode) {
+  //   TODO handle transformed expression
+  // }
+  const text = node.isStatic ? JSON.stringify(node.content) : node.content
+  context.push(text, node)
+}
+
+function genExpressionAsPropertyKey(
+  node: ExpressionNode,
+  context: CodegenContext
+) {
+  // if (node.codegenNode) {
+  //   TODO handle transformed expression
+  // }
+  if (node.isStatic) {
+    // only quote keys if necessary
+    const text = /^\d|[^\w]/.test(node.content)
+      ? JSON.stringify(node.content)
+      : node.content
+    context.push(text, node)
+  } else {
+    context.push(`[${node.content}]`, node)
   }
-  context.push(node.content, node)
 }
 
 function genComment(node: CommentNode, context: CodegenContext) {
@@ -167,6 +276,107 @@ function genComment(node: CommentNode, context: CodegenContext) {
 }
 
 // control flow
-function genIf(node: IfNode, context: CodegenContext) {}
+function genIf(node: IfNode, context: CodegenContext) {
+  genIfBranch(node.branches[0], node.branches, 1, context)
+}
 
-function genFor(node: ForNode, context: CodegenContext) {}
+function genIfBranch(
+  { condition, children }: IfBranchNode,
+  branches: IfBranchNode[],
+  nextIndex: number,
+  context: CodegenContext
+) {
+  if (condition) {
+    // v-if or v-else-if
+    context.push(`(${condition.content})`, condition)
+    context.push(`?`)
+    genChildren(children, context)
+    context.push(`:`)
+    if (nextIndex < branches.length) {
+      genIfBranch(branches[nextIndex], branches, nextIndex + 1, context)
+    } else {
+      context.push(`null`)
+    }
+  } else {
+    // v-else
+    __DEV__ && assert(nextIndex === branches.length)
+    genChildren(children, context)
+  }
+}
+
+function genFor(node: ForNode, context: CodegenContext) {
+  const { push } = context
+  const { source, keyAlias, valueAlias, objectIndexAlias, children } = node
+  push(`${RENDER_LIST_HELPER}(`, node)
+  genExpression(source, context)
+  context.push(`(`)
+  if (valueAlias) {
+    // not using genExpression here because these aliases can only be code
+    // that is valid in the function argument position, so the parse rule can
+    // be off and they don't need identifier prefixing anyway.
+    push(valueAlias.content, valueAlias)
+    push(`, `)
+  }
+  if (keyAlias) {
+    if (!valueAlias) {
+      push(`_, `)
+    }
+    push(keyAlias.content, keyAlias)
+    push(`, `)
+  }
+  if (objectIndexAlias) {
+    if (!keyAlias) {
+      if (!valueAlias) {
+        push(`_, `)
+      }
+      push(`_, `)
+    }
+    push(objectIndexAlias.content, objectIndexAlias)
+  }
+  context.push(`) => `)
+  genChildren(children, context)
+  context.push(`)`)
+}
+
+// JavaScript
+function genCallExpression(
+  node: CallExpression,
+  context: CodegenContext,
+  multilines = node.arguments.length > 1
+) {
+  context.push(node.callee + `(`, node)
+  multilines && context.indent()
+  genNodeList(node.arguments, context, multilines)
+  multilines && context.deindent()
+  context.push(`)`)
+}
+
+function genObjectExpression(node: ObjectExpression, context: CodegenContext) {
+  const { push, indent, deindent, newline } = context
+  const { properties } = node
+  const multilines = properties.length > 1
+  push(`{`, node)
+  multilines && indent()
+  for (let i = 0; i < properties.length; i++) {
+    const { key, value } = properties[i]
+    // key
+    genExpressionAsPropertyKey(key, context)
+    push(`: `)
+    // value
+    genExpression(value, context)
+    if (i < properties.length - 1) {
+      if (multilines) {
+        push(`,`)
+        newline()
+      } else {
+        push(`, `)
+      }
+    }
+  }
+  multilines && deindent()
+  push(`}`)
+}
+
+function genArrayExpression(node: ArrayExpression, context: CodegenContext) {
+  genNodeListAsArray(node.elements, context)
+}
index 62fc7317c02909ba578808371e6a7d2bdd629505..fb91b33228eadf20d67950af76f76ed3e34adab4 100644 (file)
@@ -3,6 +3,9 @@ import { transform, TransformOptions } from './transform'
 import { generate, CodegenOptions, CodegenResult } from './codegen'
 import { RootNode } from './ast'
 import { isString } from '@vue/shared'
+import { transformIf } from './transforms/vIf'
+import { transformFor } from './transforms/vFor'
+import { prepareElementForCodegen } from './transforms/element'
 
 export type CompilerOptions = ParserOptions & TransformOptions & CodegenOptions
 
@@ -15,7 +18,9 @@ export function compile(
   transform(ast, {
     ...options,
     nodeTransforms: [
-      // TODO include built-in core transforms
+      transformIf,
+      transformFor,
+      prepareElementForCodegen,
       ...(options.nodeTransforms || []) // user transforms
     ],
     directiveTransforms: {
index 780b7729f39f02601bc6fd493af5495789c14e9c..066874c3e519077cc9bdc6f7033cb6ec9259ad76 100644 (file)
@@ -24,11 +24,16 @@ export const prepareElementForCodegen: NodeTransform = (node, context) => {
       node.tagType === ElementTypes.ELEMENT ||
       node.tagType === ElementTypes.COMPONENT
     ) {
-      const isComponent = node.tagType === ElementTypes.ELEMENT
+      const isComponent = node.tagType === ElementTypes.COMPONENT
       const hasProps = node.props.length > 0
       const hasChildren = node.children.length > 0
       let runtimeDirectives: DirectiveNode[] | undefined
 
+      if (isComponent) {
+        // TODO inject import for `resolveComponent`
+        // TODO inject statement for resolving component
+      }
+
       const args: CallExpression['arguments'] = [
         // TODO inject resolveComponent dep to root
         isComponent ? node.tag : `"${node.tag}"`
@@ -49,9 +54,11 @@ export const prepareElementForCodegen: NodeTransform = (node, context) => {
       }
 
       const { loc } = node
+      // TODO inject import for `h`
       const vnode = createCallExpression(`h`, args, loc)
 
-      if (runtimeDirectives) {
+      if (runtimeDirectives && runtimeDirectives.length) {
+        // TODO inject import for `applyDirectives`
         node.codegenNode = createCallExpression(
           `applyDirectives`,
           [
@@ -170,7 +177,8 @@ function createDirectiveArgs(
   dir: DirectiveNode,
   context: TransformContext
 ): ArrayExpression {
-  // TODO inject resolveDirective dep to root
+  // TODO inject import for `resolveDirective`
+  // TODO inject statement for resolving directive
   const dirArgs: ArrayExpression['elements'] = [dir.name]
   const { loc } = dir
   if (dir.exp) dirArgs.push(dir.exp)
diff --git a/packages/compiler-core/src/transforms/expression.ts b/packages/compiler-core/src/transforms/expression.ts
new file mode 100644 (file)
index 0000000..ba13f11
--- /dev/null
@@ -0,0 +1,9 @@
+// - Parse expressions in templates into more detailed JavaScript ASTs so that
+//   source-maps are more accurate
+//
+// - Prefix identifiers with `_ctx.` so that they are accessed from the render
+//   context
+//
+// - This transform is only applied in non-browser builds because it relies on
+//   an additional JavaScript parser. In the browser, there is no source-map
+//   support and the code is wrapped in `with (this) { ... }`.
index 8b8e79e2d1b50adab09c974ceff8b5536a9d7921..8e25ab09227da67176850649fc22681824cd9f39 100644 (file)
@@ -7,13 +7,17 @@ const forAliasRE = /([\s\S]*?)(?:(?<=\))|\s+)(?:in|of)\s+([\s\S]*)/
 const forIteratorRE = /,([^,\}\]]*)(?:,([^,\}\]]*))?$/
 const stripParensRE = /^\(|\)$/g
 
+export const RENDER_LIST_HELPER = `renderList`
+
 export const transformFor = createStructuralDirectiveTransform(
   'for',
   (node, dir, context) => {
     if (dir.exp) {
+      // TODO inject helper import
       const aliases = parseAliasExpressions(dir.exp.content)
 
       if (aliases) {
+        // TODO inject identifiers to context
         context.replaceNode({
           type: NodeTypes.FOR,
           loc: node.loc,
index 49609ac1198796c165ef1f5cd2fd61723bc432cc..2a93612bd888e4ece81741e572304a1a913a49ab 100644 (file)
@@ -61,6 +61,6 @@ export function advancePositionWithMutation(
 
 export function assert(condition: boolean, msg?: string) {
   if (!condition) {
-    throw new Error(msg || `unexpected parser condition`)
+    throw new Error(msg || `unexpected compiler condition`)
   }
 }
index 6c09e0bcf6730e30b234d31664d13acbada5ebdb..3ead2be7f3d7c210b602e1a6fdae03ed78af2c75 100644 (file)
@@ -6,8 +6,6 @@ import {
 import { parserOptionsMinimal } from './parserOptionsMinimal'
 import { parserOptionsStandard } from './parserOptionsStandard'
 
-export * from '@vue/compiler-core'
-
 export function compile(
   template: string,
   options: CompilerOptions = {}
@@ -21,3 +19,5 @@ export function compile(
     }
   })
 }
+
+export * from '@vue/compiler-core'
index 60c2033abd87d980f4707838f55eecd8ead1b6e4..f972808839388d256092396060d3e7ce8d701cb1 100644 (file)
@@ -1,7 +1,6 @@
 import {
   ComponentInternalInstance,
   Data,
-  currentInstance,
   Component,
   SetupContext
 } from './component'
@@ -11,9 +10,7 @@ import {
   isString,
   isObject,
   isArray,
-  EMPTY_OBJ,
-  capitalize,
-  camelize
+  EMPTY_OBJ
 } from '@vue/shared'
 import { computed } from './apiReactivity'
 import { watch, WatchOptions, CleanupRegistrator } from './apiWatch'
@@ -29,12 +26,10 @@ import {
   onUnmounted
 } from './apiLifecycle'
 import { DebuggerEvent, reactive } from '@vue/reactivity'
-import { warn } from './warning'
 import { ComponentPropsOptions, ExtractPropTypes } from './componentProps'
 import { Directive } from './directives'
 import { VNodeChild } from './vnode'
 import { ComponentPublicInstance } from './componentPublicInstanceProxy'
-import { currentRenderingInstance } from './componentRenderUtils'
 
 interface ComponentOptionsBase<
   Props,
@@ -387,32 +382,3 @@ function applyMixins(
     applyOptions(instance, mixins[i], true)
   }
 }
-
-export function resolveComponent(name: string): Component | undefined {
-  return resolveAsset('components', name) as any
-}
-
-export function resolveDirective(name: string): Directive | undefined {
-  return resolveAsset('directives', name) as any
-}
-
-function resolveAsset(type: 'components' | 'directives', name: string) {
-  const instance = currentRenderingInstance || currentInstance
-  if (instance) {
-    let camelized
-    const registry = instance[type]
-    const res =
-      registry[name] ||
-      registry[(camelized = camelize(name))] ||
-      registry[capitalize(camelized)]
-    if (__DEV__ && !res) {
-      warn(`Failed to resolve ${type.slice(0, -1)}: ${name}`)
-    }
-    return res
-  } else if (__DEV__) {
-    warn(
-      `resolve${capitalize(type.slice(0, -1))} ` +
-        `can only be used in render() or setup().`
-    )
-  }
-}
index 6494028e63f698c278b5b608834e9c2434623136..e0555e5dedbebfc762d4f1c23073474af12fa4a8 100644 (file)
@@ -40,6 +40,7 @@ export const PublicInstanceProxyHandlers = {
       // return the value from propsProxy for ref unwrapping and readonly
       return (propsProxy as any)[key]
     } else {
+      // TODO simplify this?
       switch (key) {
         case '$data':
           return data
@@ -79,6 +80,7 @@ export const PublicInstanceProxyHandlers = {
   },
   has(target: ComponentInternalInstance, key: string): boolean {
     const { renderContext, data, props } = target
+    // TODO handle $xxx properties
     return (
       (data !== EMPTY_OBJ && hasOwn(data, key)) ||
       hasOwn(renderContext, key) ||
diff --git a/packages/runtime-core/src/helpers/renderList.ts b/packages/runtime-core/src/helpers/renderList.ts
new file mode 100644 (file)
index 0000000..7a715be
--- /dev/null
@@ -0,0 +1,2 @@
+// TODO
+export function renderList() {}
diff --git a/packages/runtime-core/src/helpers/resolveAssets.ts b/packages/runtime-core/src/helpers/resolveAssets.ts
new file mode 100644 (file)
index 0000000..fd6fdd5
--- /dev/null
@@ -0,0 +1,34 @@
+import { currentRenderingInstance } from '../componentRenderUtils'
+import { currentInstance, Component } from '../component'
+import { Directive } from '../directives'
+import { camelize, capitalize } from '@vue/shared'
+import { warn } from '../warning'
+
+export function resolveComponent(name: string): Component | undefined {
+  return resolveAsset('components', name) as any
+}
+
+export function resolveDirective(name: string): Directive | undefined {
+  return resolveAsset('directives', name) as any
+}
+
+function resolveAsset(type: 'components' | 'directives', name: string) {
+  const instance = currentRenderingInstance || currentInstance
+  if (instance) {
+    let camelized
+    const registry = instance[type]
+    const res =
+      registry[name] ||
+      registry[(camelized = camelize(name))] ||
+      registry[capitalize(camelized)]
+    if (__DEV__ && !res) {
+      warn(`Failed to resolve ${type.slice(0, -1)}: ${name}`)
+    }
+    return res
+  } else if (__DEV__) {
+    warn(
+      `resolve${capitalize(type.slice(0, -1))} ` +
+        `can only be used in render() or setup().`
+    )
+  }
+}
index 006ecf647d478f5f1021584a384c00badb3bb959..15c66f854d3384fa81d2475369f71cec2beadcfc 100644 (file)
@@ -37,7 +37,8 @@ export {
 
 // Internal, for compiler generated code
 export { applyDirectives } from './directives'
-export { resolveComponent, resolveDirective } from './componentOptions'
+export { resolveComponent, resolveDirective } from './helpers/resolveAssets'
+export { renderList } from './helpers/renderList'
 
 // Internal, for integration with runtime compiler
 export { registerRuntimeCompiler } from './component'
index 1c89caba337d12be1e3a56df65a8924c182f0a87..e2baaaf6f58666aafc8d551c1c73814fd8286e6d 100644 (file)
@@ -8,7 +8,7 @@ function compileToFunction(
   options?: CompilerOptions
 ): RenderFunction {
   const { code } = compile(template, options)
-  return new Function(`with(this){return ${code}}`) as RenderFunction
+  return new Function(code)() as RenderFunction
 }
 
 registerRuntimeCompiler(compileToFunction)