feat(compiler-sfc): compileScript inline render function mode
This commit is contained in:
@@ -60,12 +60,16 @@ type CodegenNode = TemplateChildNode | JSChildNode | SSRCodegenNode
|
||||
|
||||
export interface CodegenResult {
|
||||
code: string
|
||||
preamble: string
|
||||
ast: RootNode
|
||||
map?: RawSourceMap
|
||||
}
|
||||
|
||||
export interface CodegenContext
|
||||
extends Omit<Required<CodegenOptions>, 'bindingMetadata'> {
|
||||
extends Omit<
|
||||
Required<CodegenOptions>,
|
||||
'bindingMetadata' | 'inline' | 'inlinePropsIdentifier'
|
||||
> {
|
||||
source: string
|
||||
code: string
|
||||
line: number
|
||||
@@ -199,12 +203,18 @@ export function generate(
|
||||
const hasHelpers = ast.helpers.length > 0
|
||||
const useWithBlock = !prefixIdentifiers && mode !== 'module'
|
||||
const genScopeId = !__BROWSER__ && scopeId != null && mode === 'module'
|
||||
const isSetupInlined = !!options.inline
|
||||
|
||||
// preambles
|
||||
// in setup() inline mode, the preamble is generated in a sub context
|
||||
// and returned separately.
|
||||
const preambleContext = isSetupInlined
|
||||
? createCodegenContext(ast, options)
|
||||
: context
|
||||
if (!__BROWSER__ && mode === 'module') {
|
||||
genModulePreamble(ast, context, genScopeId)
|
||||
genModulePreamble(ast, preambleContext, genScopeId, isSetupInlined)
|
||||
} else {
|
||||
genFunctionPreamble(ast, context)
|
||||
genFunctionPreamble(ast, preambleContext)
|
||||
}
|
||||
|
||||
// binding optimizations
|
||||
@@ -213,10 +223,17 @@ export function generate(
|
||||
: ``
|
||||
// enter render function
|
||||
if (!ssr) {
|
||||
if (genScopeId) {
|
||||
push(`const render = ${PURE_ANNOTATION}_withId(`)
|
||||
if (isSetupInlined) {
|
||||
if (genScopeId) {
|
||||
push(`${PURE_ANNOTATION}_withId(`)
|
||||
}
|
||||
push(`() => {`)
|
||||
} else {
|
||||
if (genScopeId) {
|
||||
push(`const render = ${PURE_ANNOTATION}_withId(`)
|
||||
}
|
||||
push(`function render(_ctx, _cache${optimizeSources}) {`)
|
||||
}
|
||||
push(`function render(_ctx, _cache${optimizeSources}) {`)
|
||||
} else {
|
||||
if (genScopeId) {
|
||||
push(`const ssrRender = ${PURE_ANNOTATION}_withId(`)
|
||||
@@ -290,6 +307,7 @@ export function generate(
|
||||
return {
|
||||
ast,
|
||||
code: context.code,
|
||||
preamble: isSetupInlined ? preambleContext.code : ``,
|
||||
// SourceMapGenerator does have toJSON() method but it's not in the types
|
||||
map: context.map ? (context.map as any).toJSON() : undefined
|
||||
}
|
||||
@@ -356,7 +374,8 @@ function genFunctionPreamble(ast: RootNode, context: CodegenContext) {
|
||||
function genModulePreamble(
|
||||
ast: RootNode,
|
||||
context: CodegenContext,
|
||||
genScopeId: boolean
|
||||
genScopeId: boolean,
|
||||
inline?: boolean
|
||||
) {
|
||||
const {
|
||||
push,
|
||||
@@ -423,7 +442,10 @@ function genModulePreamble(
|
||||
|
||||
genHoists(ast.hoists, context)
|
||||
newline()
|
||||
push(`export `)
|
||||
|
||||
if (!inline) {
|
||||
push(`export `)
|
||||
}
|
||||
}
|
||||
|
||||
function genAssets(
|
||||
|
||||
@@ -65,7 +65,39 @@ export interface BindingMetadata {
|
||||
[key: string]: 'data' | 'props' | 'setup' | 'options'
|
||||
}
|
||||
|
||||
export interface TransformOptions {
|
||||
interface SharedTransformCodegenOptions {
|
||||
/**
|
||||
* Transform expressions like {{ foo }} to `_ctx.foo`.
|
||||
* If this option is false, the generated code will be wrapped in a
|
||||
* `with (this) { ... }` block.
|
||||
* - This is force-enabled in module mode, since modules are by default strict
|
||||
* and cannot use `with`
|
||||
* @default mode === 'module'
|
||||
*/
|
||||
prefixIdentifiers?: boolean
|
||||
/**
|
||||
* Generate SSR-optimized render functions instead.
|
||||
* The resulting function must be attached to the component via the
|
||||
* `ssrRender` option instead of `render`.
|
||||
*/
|
||||
ssr?: boolean
|
||||
/**
|
||||
* Optional binding metadata analyzed from script - used to optimize
|
||||
* binding access when `prefixIdentifiers` is enabled.
|
||||
*/
|
||||
bindingMetadata?: BindingMetadata
|
||||
/**
|
||||
* Compile the function for inlining inside setup().
|
||||
* This allows the function to directly access setup() local bindings.
|
||||
*/
|
||||
inline?: boolean
|
||||
/**
|
||||
* Identifier for props in setup() inline mode.
|
||||
*/
|
||||
inlinePropsIdentifier?: string
|
||||
}
|
||||
|
||||
export interface TransformOptions extends SharedTransformCodegenOptions {
|
||||
/**
|
||||
* An array of node transforms to be applied to every AST node.
|
||||
*/
|
||||
@@ -128,26 +160,15 @@ export interface TransformOptions {
|
||||
* SFC scoped styles ID
|
||||
*/
|
||||
scopeId?: string | null
|
||||
/**
|
||||
* Generate SSR-optimized render functions instead.
|
||||
* The resulting function must be attached to the component via the
|
||||
* `ssrRender` option instead of `render`.
|
||||
*/
|
||||
ssr?: boolean
|
||||
/**
|
||||
* SFC `<style vars>` injection string
|
||||
* needed to render inline CSS variables on component root
|
||||
*/
|
||||
ssrCssVars?: string
|
||||
/**
|
||||
* Optional binding metadata analyzed from script - used to optimize
|
||||
* binding access when `prefixIdentifiers` is enabled.
|
||||
*/
|
||||
bindingMetadata?: BindingMetadata
|
||||
onError?: (error: CompilerError) => void
|
||||
}
|
||||
|
||||
export interface CodegenOptions {
|
||||
export interface CodegenOptions extends SharedTransformCodegenOptions {
|
||||
/**
|
||||
* - `module` mode will generate ES module import statements for helpers
|
||||
* and export the render function as the default export.
|
||||
@@ -189,11 +210,6 @@ export interface CodegenOptions {
|
||||
* @default 'Vue'
|
||||
*/
|
||||
runtimeGlobalName?: string
|
||||
// we need to know this during codegen to generate proper preambles
|
||||
prefixIdentifiers?: boolean
|
||||
bindingMetadata?: BindingMetadata
|
||||
// generate ssr-specific code?
|
||||
ssr?: boolean
|
||||
}
|
||||
|
||||
export type CompilerOptions = ParserOptions & TransformOptions & CodegenOptions
|
||||
|
||||
@@ -29,6 +29,7 @@ export const PUSH_SCOPE_ID = Symbol(__DEV__ ? `pushScopeId` : ``)
|
||||
export const POP_SCOPE_ID = Symbol(__DEV__ ? `popScopeId` : ``)
|
||||
export const WITH_SCOPE_ID = Symbol(__DEV__ ? `withScopeId` : ``)
|
||||
export const WITH_CTX = Symbol(__DEV__ ? `withCtx` : ``)
|
||||
export const UNREF = Symbol(__DEV__ ? `unref` : ``)
|
||||
|
||||
// Name mapping for runtime helpers that need to be imported from 'vue' in
|
||||
// generated code. Make sure these are correctly exported in the runtime!
|
||||
@@ -62,7 +63,8 @@ export const helperNameMap: any = {
|
||||
[PUSH_SCOPE_ID]: `pushScopeId`,
|
||||
[POP_SCOPE_ID]: `popScopeId`,
|
||||
[WITH_SCOPE_ID]: `withScopeId`,
|
||||
[WITH_CTX]: `withCtx`
|
||||
[WITH_CTX]: `withCtx`,
|
||||
[UNREF]: `unref`
|
||||
}
|
||||
|
||||
export function registerRuntimeHelpers(helpers: any) {
|
||||
|
||||
@@ -124,6 +124,8 @@ export function createTransformContext(
|
||||
ssr = false,
|
||||
ssrCssVars = ``,
|
||||
bindingMetadata = EMPTY_OBJ,
|
||||
inline = false,
|
||||
inlinePropsIdentifier = `$props`,
|
||||
onError = defaultOnError
|
||||
}: TransformOptions
|
||||
): TransformContext {
|
||||
@@ -142,6 +144,8 @@ export function createTransformContext(
|
||||
ssr,
|
||||
ssrCssVars,
|
||||
bindingMetadata,
|
||||
inline,
|
||||
inlinePropsIdentifier,
|
||||
onError,
|
||||
|
||||
// state
|
||||
|
||||
@@ -28,6 +28,7 @@ import { Node, Function, Identifier, ObjectProperty } from '@babel/types'
|
||||
import { validateBrowserExpression } from '../validateExpression'
|
||||
import { parse } from '@babel/parser'
|
||||
import { walk } from 'estree-walker'
|
||||
import { UNREF } from '../runtimeHelpers'
|
||||
|
||||
const isLiteralWhitelisted = /*#__PURE__*/ makeMap('true,false,null,this')
|
||||
|
||||
@@ -97,12 +98,21 @@ export function processExpression(
|
||||
return node
|
||||
}
|
||||
|
||||
const { bindingMetadata } = context
|
||||
const { inline, inlinePropsIdentifier, bindingMetadata } = context
|
||||
const prefix = (raw: string) => {
|
||||
const source = hasOwn(bindingMetadata, raw)
|
||||
? `$` + bindingMetadata[raw]
|
||||
: `_ctx`
|
||||
return `${source}.${raw}`
|
||||
if (inline) {
|
||||
// setup inline mode, it's either props or setup
|
||||
if (bindingMetadata[raw] !== 'setup') {
|
||||
return `${inlinePropsIdentifier}.${raw}`
|
||||
} else {
|
||||
return `${context.helperString(UNREF)}(${raw})`
|
||||
}
|
||||
} else {
|
||||
const source = hasOwn(bindingMetadata, raw)
|
||||
? `$` + bindingMetadata[raw]
|
||||
: `_ctx`
|
||||
return `${source}.${raw}`
|
||||
}
|
||||
}
|
||||
|
||||
// fast path if expression is a simple identifier.
|
||||
|
||||
Reference in New Issue
Block a user