feat(compiler-sfc): compileScript inline render function mode
This commit is contained in:
parent
3f99e239e0
commit
886ed7681d
@ -60,12 +60,16 @@ type CodegenNode = TemplateChildNode | JSChildNode | SSRCodegenNode
|
|||||||
|
|
||||||
export interface CodegenResult {
|
export interface CodegenResult {
|
||||||
code: string
|
code: string
|
||||||
|
preamble: string
|
||||||
ast: RootNode
|
ast: RootNode
|
||||||
map?: RawSourceMap
|
map?: RawSourceMap
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CodegenContext
|
export interface CodegenContext
|
||||||
extends Omit<Required<CodegenOptions>, 'bindingMetadata'> {
|
extends Omit<
|
||||||
|
Required<CodegenOptions>,
|
||||||
|
'bindingMetadata' | 'inline' | 'inlinePropsIdentifier'
|
||||||
|
> {
|
||||||
source: string
|
source: string
|
||||||
code: string
|
code: string
|
||||||
line: number
|
line: number
|
||||||
@ -199,12 +203,18 @@ export function generate(
|
|||||||
const hasHelpers = ast.helpers.length > 0
|
const hasHelpers = ast.helpers.length > 0
|
||||||
const useWithBlock = !prefixIdentifiers && mode !== 'module'
|
const useWithBlock = !prefixIdentifiers && mode !== 'module'
|
||||||
const genScopeId = !__BROWSER__ && scopeId != null && mode === 'module'
|
const genScopeId = !__BROWSER__ && scopeId != null && mode === 'module'
|
||||||
|
const isSetupInlined = !!options.inline
|
||||||
|
|
||||||
// preambles
|
// 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') {
|
if (!__BROWSER__ && mode === 'module') {
|
||||||
genModulePreamble(ast, context, genScopeId)
|
genModulePreamble(ast, preambleContext, genScopeId, isSetupInlined)
|
||||||
} else {
|
} else {
|
||||||
genFunctionPreamble(ast, context)
|
genFunctionPreamble(ast, preambleContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
// binding optimizations
|
// binding optimizations
|
||||||
@ -213,10 +223,17 @@ export function generate(
|
|||||||
: ``
|
: ``
|
||||||
// enter render function
|
// enter render function
|
||||||
if (!ssr) {
|
if (!ssr) {
|
||||||
|
if (isSetupInlined) {
|
||||||
|
if (genScopeId) {
|
||||||
|
push(`${PURE_ANNOTATION}_withId(`)
|
||||||
|
}
|
||||||
|
push(`() => {`)
|
||||||
|
} else {
|
||||||
if (genScopeId) {
|
if (genScopeId) {
|
||||||
push(`const render = ${PURE_ANNOTATION}_withId(`)
|
push(`const render = ${PURE_ANNOTATION}_withId(`)
|
||||||
}
|
}
|
||||||
push(`function render(_ctx, _cache${optimizeSources}) {`)
|
push(`function render(_ctx, _cache${optimizeSources}) {`)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (genScopeId) {
|
if (genScopeId) {
|
||||||
push(`const ssrRender = ${PURE_ANNOTATION}_withId(`)
|
push(`const ssrRender = ${PURE_ANNOTATION}_withId(`)
|
||||||
@ -290,6 +307,7 @@ export function generate(
|
|||||||
return {
|
return {
|
||||||
ast,
|
ast,
|
||||||
code: context.code,
|
code: context.code,
|
||||||
|
preamble: isSetupInlined ? preambleContext.code : ``,
|
||||||
// SourceMapGenerator does have toJSON() method but it's not in the types
|
// SourceMapGenerator does have toJSON() method but it's not in the types
|
||||||
map: context.map ? (context.map as any).toJSON() : undefined
|
map: context.map ? (context.map as any).toJSON() : undefined
|
||||||
}
|
}
|
||||||
@ -356,7 +374,8 @@ function genFunctionPreamble(ast: RootNode, context: CodegenContext) {
|
|||||||
function genModulePreamble(
|
function genModulePreamble(
|
||||||
ast: RootNode,
|
ast: RootNode,
|
||||||
context: CodegenContext,
|
context: CodegenContext,
|
||||||
genScopeId: boolean
|
genScopeId: boolean,
|
||||||
|
inline?: boolean
|
||||||
) {
|
) {
|
||||||
const {
|
const {
|
||||||
push,
|
push,
|
||||||
@ -423,7 +442,10 @@ function genModulePreamble(
|
|||||||
|
|
||||||
genHoists(ast.hoists, context)
|
genHoists(ast.hoists, context)
|
||||||
newline()
|
newline()
|
||||||
|
|
||||||
|
if (!inline) {
|
||||||
push(`export `)
|
push(`export `)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function genAssets(
|
function genAssets(
|
||||||
|
@ -65,7 +65,39 @@ export interface BindingMetadata {
|
|||||||
[key: string]: 'data' | 'props' | 'setup' | 'options'
|
[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.
|
* An array of node transforms to be applied to every AST node.
|
||||||
*/
|
*/
|
||||||
@ -128,26 +160,15 @@ export interface TransformOptions {
|
|||||||
* SFC scoped styles ID
|
* SFC scoped styles ID
|
||||||
*/
|
*/
|
||||||
scopeId?: string | null
|
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
|
* SFC `<style vars>` injection string
|
||||||
* needed to render inline CSS variables on component root
|
* needed to render inline CSS variables on component root
|
||||||
*/
|
*/
|
||||||
ssrCssVars?: string
|
ssrCssVars?: string
|
||||||
/**
|
|
||||||
* Optional binding metadata analyzed from script - used to optimize
|
|
||||||
* binding access when `prefixIdentifiers` is enabled.
|
|
||||||
*/
|
|
||||||
bindingMetadata?: BindingMetadata
|
|
||||||
onError?: (error: CompilerError) => void
|
onError?: (error: CompilerError) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CodegenOptions {
|
export interface CodegenOptions extends SharedTransformCodegenOptions {
|
||||||
/**
|
/**
|
||||||
* - `module` mode will generate ES module import statements for helpers
|
* - `module` mode will generate ES module import statements for helpers
|
||||||
* and export the render function as the default export.
|
* and export the render function as the default export.
|
||||||
@ -189,11 +210,6 @@ export interface CodegenOptions {
|
|||||||
* @default 'Vue'
|
* @default 'Vue'
|
||||||
*/
|
*/
|
||||||
runtimeGlobalName?: string
|
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
|
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 POP_SCOPE_ID = Symbol(__DEV__ ? `popScopeId` : ``)
|
||||||
export const WITH_SCOPE_ID = Symbol(__DEV__ ? `withScopeId` : ``)
|
export const WITH_SCOPE_ID = Symbol(__DEV__ ? `withScopeId` : ``)
|
||||||
export const WITH_CTX = Symbol(__DEV__ ? `withCtx` : ``)
|
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
|
// Name mapping for runtime helpers that need to be imported from 'vue' in
|
||||||
// generated code. Make sure these are correctly exported in the runtime!
|
// generated code. Make sure these are correctly exported in the runtime!
|
||||||
@ -62,7 +63,8 @@ export const helperNameMap: any = {
|
|||||||
[PUSH_SCOPE_ID]: `pushScopeId`,
|
[PUSH_SCOPE_ID]: `pushScopeId`,
|
||||||
[POP_SCOPE_ID]: `popScopeId`,
|
[POP_SCOPE_ID]: `popScopeId`,
|
||||||
[WITH_SCOPE_ID]: `withScopeId`,
|
[WITH_SCOPE_ID]: `withScopeId`,
|
||||||
[WITH_CTX]: `withCtx`
|
[WITH_CTX]: `withCtx`,
|
||||||
|
[UNREF]: `unref`
|
||||||
}
|
}
|
||||||
|
|
||||||
export function registerRuntimeHelpers(helpers: any) {
|
export function registerRuntimeHelpers(helpers: any) {
|
||||||
|
@ -124,6 +124,8 @@ export function createTransformContext(
|
|||||||
ssr = false,
|
ssr = false,
|
||||||
ssrCssVars = ``,
|
ssrCssVars = ``,
|
||||||
bindingMetadata = EMPTY_OBJ,
|
bindingMetadata = EMPTY_OBJ,
|
||||||
|
inline = false,
|
||||||
|
inlinePropsIdentifier = `$props`,
|
||||||
onError = defaultOnError
|
onError = defaultOnError
|
||||||
}: TransformOptions
|
}: TransformOptions
|
||||||
): TransformContext {
|
): TransformContext {
|
||||||
@ -142,6 +144,8 @@ export function createTransformContext(
|
|||||||
ssr,
|
ssr,
|
||||||
ssrCssVars,
|
ssrCssVars,
|
||||||
bindingMetadata,
|
bindingMetadata,
|
||||||
|
inline,
|
||||||
|
inlinePropsIdentifier,
|
||||||
onError,
|
onError,
|
||||||
|
|
||||||
// state
|
// state
|
||||||
|
@ -28,6 +28,7 @@ import { Node, Function, Identifier, ObjectProperty } from '@babel/types'
|
|||||||
import { validateBrowserExpression } from '../validateExpression'
|
import { validateBrowserExpression } from '../validateExpression'
|
||||||
import { parse } from '@babel/parser'
|
import { parse } from '@babel/parser'
|
||||||
import { walk } from 'estree-walker'
|
import { walk } from 'estree-walker'
|
||||||
|
import { UNREF } from '../runtimeHelpers'
|
||||||
|
|
||||||
const isLiteralWhitelisted = /*#__PURE__*/ makeMap('true,false,null,this')
|
const isLiteralWhitelisted = /*#__PURE__*/ makeMap('true,false,null,this')
|
||||||
|
|
||||||
@ -97,13 +98,22 @@ export function processExpression(
|
|||||||
return node
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
const { bindingMetadata } = context
|
const { inline, inlinePropsIdentifier, bindingMetadata } = context
|
||||||
const prefix = (raw: string) => {
|
const prefix = (raw: string) => {
|
||||||
|
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)
|
const source = hasOwn(bindingMetadata, raw)
|
||||||
? `$` + bindingMetadata[raw]
|
? `$` + bindingMetadata[raw]
|
||||||
: `_ctx`
|
: `_ctx`
|
||||||
return `${source}.${raw}`
|
return `${source}.${raw}`
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// fast path if expression is a simple identifier.
|
// fast path if expression is a simple identifier.
|
||||||
const rawExp = node.content
|
const rawExp = node.content
|
||||||
|
@ -464,14 +464,20 @@ describe('SFC compile <script setup>', () => {
|
|||||||
compile(`<script setup>
|
compile(`<script setup>
|
||||||
export const a = 1
|
export const a = 1
|
||||||
</script>`)
|
</script>`)
|
||||||
).toThrow(`cannot contain non-type named exports`)
|
).toThrow(`cannot contain non-type named or * exports`)
|
||||||
|
|
||||||
|
expect(() =>
|
||||||
|
compile(`<script setup>
|
||||||
|
export * from './foo'
|
||||||
|
</script>`)
|
||||||
|
).toThrow(`cannot contain non-type named or * exports`)
|
||||||
|
|
||||||
expect(() =>
|
expect(() =>
|
||||||
compile(`<script setup>
|
compile(`<script setup>
|
||||||
const bar = 1
|
const bar = 1
|
||||||
export { bar as default }
|
export { bar as default }
|
||||||
</script>`)
|
</script>`)
|
||||||
).toThrow(`cannot contain non-type named exports`)
|
).toThrow(`cannot contain non-type named or * exports`)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('ref: non-assignment expressions', () => {
|
test('ref: non-assignment expressions', () => {
|
||||||
|
@ -27,13 +27,28 @@ import {
|
|||||||
import { walk } from 'estree-walker'
|
import { walk } from 'estree-walker'
|
||||||
import { RawSourceMap } from 'source-map'
|
import { RawSourceMap } from 'source-map'
|
||||||
import { genCssVarsCode, injectCssVarsCalls } from './genCssVars'
|
import { genCssVarsCode, injectCssVarsCalls } from './genCssVars'
|
||||||
|
import { compileTemplate, SFCTemplateCompileOptions } from './compileTemplate'
|
||||||
|
|
||||||
export interface SFCScriptCompileOptions {
|
export interface SFCScriptCompileOptions {
|
||||||
/**
|
/**
|
||||||
* https://babeljs.io/docs/en/babel-parser#plugins
|
* https://babeljs.io/docs/en/babel-parser#plugins
|
||||||
*/
|
*/
|
||||||
babelParserPlugins?: ParserPlugin[]
|
babelParserPlugins?: ParserPlugin[]
|
||||||
|
/**
|
||||||
|
* Enable ref: label sugar
|
||||||
|
* https://github.com/vuejs/rfcs/pull/228
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
refSugar?: boolean
|
refSugar?: boolean
|
||||||
|
/**
|
||||||
|
* Compile the template and inline the resulting render function
|
||||||
|
* directly inside setup().
|
||||||
|
* - Only affects <script setup>
|
||||||
|
* - This should only be used in production because it prevents the template
|
||||||
|
* from being hot-reloaded separately from component state.
|
||||||
|
*/
|
||||||
|
inlineTemplate?: boolean
|
||||||
|
templateOptions?: SFCTemplateCompileOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasWarned: Record<string, boolean> = {}
|
const hasWarned: Record<string, boolean> = {}
|
||||||
@ -356,10 +371,10 @@ export function compileScript(
|
|||||||
const setupValue = scriptSetup.setup
|
const setupValue = scriptSetup.setup
|
||||||
const hasExplicitSignature = typeof setupValue === 'string'
|
const hasExplicitSignature = typeof setupValue === 'string'
|
||||||
|
|
||||||
let propsVar: string | undefined
|
let propsIdentifier: string | undefined
|
||||||
let emitVar: string | undefined
|
let emitIdentifier: string | undefined
|
||||||
let slotsVar: string | undefined
|
let slotsIdentifier: string | undefined
|
||||||
let attrsVar: string | undefined
|
let attrsIdentifier: string | undefined
|
||||||
|
|
||||||
let propsType = `{}`
|
let propsType = `{}`
|
||||||
let emitType = `(e: string, ...args: any[]) => void`
|
let emitType = `(e: string, ...args: any[]) => void`
|
||||||
@ -390,16 +405,20 @@ export function compileScript(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isTS) {
|
// parse the signature to extract the identifiers users are assigning to
|
||||||
// <script setup="xxx" lang="ts">
|
// the arguments. props identifier is always needed for inline mode
|
||||||
// parse the signature to extract the props/emit variables the user wants
|
// template compilation
|
||||||
// we need them to find corresponding type declarations.
|
|
||||||
const params = ((signatureAST as ExpressionStatement)
|
const params = ((signatureAST as ExpressionStatement)
|
||||||
.expression as ArrowFunctionExpression).params
|
.expression as ArrowFunctionExpression).params
|
||||||
if (params[0] && params[0].type === 'Identifier') {
|
if (params[0] && params[0].type === 'Identifier') {
|
||||||
propsASTNode = params[0]
|
propsASTNode = params[0]
|
||||||
propsVar = propsASTNode.name
|
propsIdentifier = propsASTNode.name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isTS) {
|
||||||
|
// <script setup="xxx" lang="ts">
|
||||||
|
// additional identifiers are needed for TS in order to match declared
|
||||||
|
// types
|
||||||
if (params[1] && params[1].type === 'ObjectPattern') {
|
if (params[1] && params[1].type === 'ObjectPattern') {
|
||||||
setupCtxASTNode = params[1]
|
setupCtxASTNode = params[1]
|
||||||
for (const p of params[1].properties) {
|
for (const p of params[1].properties) {
|
||||||
@ -409,11 +428,11 @@ export function compileScript(
|
|||||||
p.value.type === 'Identifier'
|
p.value.type === 'Identifier'
|
||||||
) {
|
) {
|
||||||
if (p.key.name === 'emit') {
|
if (p.key.name === 'emit') {
|
||||||
emitVar = p.value.name
|
emitIdentifier = p.value.name
|
||||||
} else if (p.key.name === 'slots') {
|
} else if (p.key.name === 'slots') {
|
||||||
slotsVar = p.value.name
|
slotsIdentifier = p.value.name
|
||||||
} else if (p.key.name === 'attrs') {
|
} else if (p.key.name === 'attrs') {
|
||||||
attrsVar = p.value.name
|
attrsIdentifier = p.value.name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -560,16 +579,16 @@ export function compileScript(
|
|||||||
typeNode.end! + startOffset
|
typeNode.end! + startOffset
|
||||||
)
|
)
|
||||||
if (typeNode.type === 'TSTypeLiteral') {
|
if (typeNode.type === 'TSTypeLiteral') {
|
||||||
if (id.name === propsVar) {
|
if (id.name === propsIdentifier) {
|
||||||
propsType = typeString
|
propsType = typeString
|
||||||
extractRuntimeProps(typeNode, typeDeclaredProps, declaredTypes)
|
extractRuntimeProps(typeNode, typeDeclaredProps, declaredTypes)
|
||||||
} else if (id.name === slotsVar) {
|
} else if (id.name === slotsIdentifier) {
|
||||||
slotsType = typeString
|
slotsType = typeString
|
||||||
} else if (id.name === attrsVar) {
|
} else if (id.name === attrsIdentifier) {
|
||||||
attrsType = typeString
|
attrsType = typeString
|
||||||
}
|
}
|
||||||
} else if (
|
} else if (
|
||||||
id.name === emitVar &&
|
id.name === emitIdentifier &&
|
||||||
typeNode.type === 'TSFunctionType'
|
typeNode.type === 'TSFunctionType'
|
||||||
) {
|
) {
|
||||||
emitType = typeString
|
emitType = typeString
|
||||||
@ -583,10 +602,10 @@ export function compileScript(
|
|||||||
if (
|
if (
|
||||||
node.type === 'TSDeclareFunction' &&
|
node.type === 'TSDeclareFunction' &&
|
||||||
node.id &&
|
node.id &&
|
||||||
node.id.name === emitVar
|
node.id.name === emitIdentifier
|
||||||
) {
|
) {
|
||||||
const index = node.id.start! + startOffset
|
const index = node.id.start! + startOffset
|
||||||
s.overwrite(index, index + emitVar.length, '__emit__')
|
s.overwrite(index, index + emitIdentifier.length, '__emit__')
|
||||||
emitType = `typeof __emit__`
|
emitType = `typeof __emit__`
|
||||||
extractRuntimeEmits(node, typeDeclaredEmits)
|
extractRuntimeEmits(node, typeDeclaredEmits)
|
||||||
}
|
}
|
||||||
@ -681,7 +700,7 @@ export function compileScript(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 7. finalize setup argument signature.
|
// 7. finalize setup argument signature.
|
||||||
let args = ``
|
let args = options.inlineTemplate ? `$props` : ``
|
||||||
if (isTS) {
|
if (isTS) {
|
||||||
if (slotsType === 'Slots') {
|
if (slotsType === 'Slots') {
|
||||||
helperImports.add('Slots')
|
helperImports.add('Slots')
|
||||||
@ -704,8 +723,8 @@ export function compileScript(
|
|||||||
}
|
}
|
||||||
args = ss.toString()
|
args = ss.toString()
|
||||||
}
|
}
|
||||||
} else {
|
} else if (hasExplicitSignature) {
|
||||||
args = hasExplicitSignature ? (setupValue as string) : ``
|
args = setupValue as string
|
||||||
}
|
}
|
||||||
|
|
||||||
// 8. wrap setup code with function.
|
// 8. wrap setup code with function.
|
||||||
@ -716,11 +735,9 @@ export function compileScript(
|
|||||||
`\nexport ${hasAwait ? `async ` : ``}function setup(${args}) {\n`
|
`\nexport ${hasAwait ? `async ` : ``}function setup(${args}) {\n`
|
||||||
)
|
)
|
||||||
|
|
||||||
// generate return statement
|
|
||||||
const exposedBindings = { ...userImports, ...setupBindings }
|
const exposedBindings = { ...userImports, ...setupBindings }
|
||||||
let returned = `{ ${Object.keys(exposedBindings).join(', ')} }`
|
|
||||||
|
|
||||||
// inject `useCssVars` calls
|
// 9. inject `useCssVars` calls
|
||||||
if (hasCssVars) {
|
if (hasCssVars) {
|
||||||
helperImports.add(`useCssVars`)
|
helperImports.add(`useCssVars`)
|
||||||
for (const style of styles) {
|
for (const style of styles) {
|
||||||
@ -734,9 +751,58 @@ export function compileScript(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 10. analyze binding metadata
|
||||||
|
if (scriptAst) {
|
||||||
|
Object.assign(bindingMetadata, analyzeScriptBindings(scriptAst))
|
||||||
|
}
|
||||||
|
Object.keys(exposedBindings).forEach(key => {
|
||||||
|
bindingMetadata[key] = 'setup'
|
||||||
|
})
|
||||||
|
Object.keys(typeDeclaredProps).forEach(key => {
|
||||||
|
bindingMetadata[key] = 'props'
|
||||||
|
})
|
||||||
|
Object.assign(bindingMetadata, analyzeScriptBindings(scriptSetupAst))
|
||||||
|
|
||||||
|
// 11. generate return statement
|
||||||
|
let returned
|
||||||
|
if (options.inlineTemplate) {
|
||||||
|
if (sfc.template) {
|
||||||
|
// inline render function mode - we are going to compile the template and
|
||||||
|
// inline it right here
|
||||||
|
const { code, preamble, tips, errors } = compileTemplate({
|
||||||
|
...options.templateOptions,
|
||||||
|
filename,
|
||||||
|
source: sfc.template.content,
|
||||||
|
compilerOptions: {
|
||||||
|
inline: true,
|
||||||
|
inlinePropsIdentifier: propsIdentifier,
|
||||||
|
bindingMetadata
|
||||||
|
}
|
||||||
|
// TODO source map
|
||||||
|
})
|
||||||
|
if (tips.length) {
|
||||||
|
tips.forEach(warnOnce)
|
||||||
|
}
|
||||||
|
const err = errors[0]
|
||||||
|
if (typeof err === 'string') {
|
||||||
|
throw new Error(err)
|
||||||
|
} else if (err) {
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
if (preamble) {
|
||||||
|
s.prepend(preamble)
|
||||||
|
}
|
||||||
|
returned = code
|
||||||
|
} else {
|
||||||
|
returned = `() => {}`
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// return bindings from setup
|
||||||
|
returned = `{ ${Object.keys(exposedBindings).join(', ')} }`
|
||||||
|
}
|
||||||
s.appendRight(endOffset, `\nreturn ${returned}\n}\n\n`)
|
s.appendRight(endOffset, `\nreturn ${returned}\n}\n\n`)
|
||||||
|
|
||||||
// 9. finalize default export
|
// 12. finalize default export
|
||||||
if (isTS) {
|
if (isTS) {
|
||||||
// for TS, make sure the exported type is still valid type with
|
// for TS, make sure the exported type is still valid type with
|
||||||
// correct props information
|
// correct props information
|
||||||
@ -759,24 +825,12 @@ export function compileScript(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 10. finalize Vue helper imports
|
// 13. finalize Vue helper imports
|
||||||
const helpers = [...helperImports].filter(i => userImports[i] !== 'vue')
|
const helpers = [...helperImports].filter(i => userImports[i] !== 'vue')
|
||||||
if (helpers.length) {
|
if (helpers.length) {
|
||||||
s.prepend(`import { ${helpers.join(', ')} } from 'vue'\n`)
|
s.prepend(`import { ${helpers.join(', ')} } from 'vue'\n`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 11. expose bindings for template compiler optimization
|
|
||||||
if (scriptAst) {
|
|
||||||
Object.assign(bindingMetadata, analyzeScriptBindings(scriptAst))
|
|
||||||
}
|
|
||||||
Object.keys(exposedBindings).forEach(key => {
|
|
||||||
bindingMetadata[key] = 'setup'
|
|
||||||
})
|
|
||||||
Object.keys(typeDeclaredProps).forEach(key => {
|
|
||||||
bindingMetadata[key] = 'props'
|
|
||||||
})
|
|
||||||
Object.assign(bindingMetadata, analyzeScriptBindings(scriptSetupAst))
|
|
||||||
|
|
||||||
s.trim()
|
s.trim()
|
||||||
return {
|
return {
|
||||||
...scriptSetup,
|
...scriptSetup,
|
||||||
|
@ -30,6 +30,7 @@ export interface TemplateCompiler {
|
|||||||
|
|
||||||
export interface SFCTemplateCompileResults {
|
export interface SFCTemplateCompileResults {
|
||||||
code: string
|
code: string
|
||||||
|
preamble?: string
|
||||||
source: string
|
source: string
|
||||||
tips: string[]
|
tips: string[]
|
||||||
errors: (string | CompilerError)[]
|
errors: (string | CompilerError)[]
|
||||||
@ -168,7 +169,7 @@ function doCompileTemplate({
|
|||||||
nodeTransforms = [transformAssetUrl, transformSrcset]
|
nodeTransforms = [transformAssetUrl, transformSrcset]
|
||||||
}
|
}
|
||||||
|
|
||||||
let { code, map } = compiler.compile(source, {
|
let { code, preamble, map } = compiler.compile(source, {
|
||||||
mode: 'module',
|
mode: 'module',
|
||||||
prefixIdentifiers: true,
|
prefixIdentifiers: true,
|
||||||
hoistStatic: true,
|
hoistStatic: true,
|
||||||
@ -192,7 +193,7 @@ function doCompileTemplate({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { code, source, errors, tips: [], map }
|
return { code, preamble, source, errors, tips: [], map }
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapLines(oldMap: RawSourceMap, newMap: RawSourceMap): RawSourceMap {
|
function mapLines(oldMap: RawSourceMap, newMap: RawSourceMap): RawSourceMap {
|
||||||
|
Loading…
Reference in New Issue
Block a user