Skip to content

Commit

Permalink
feat(compiler-sfc): compileScript inline render function mode
Browse files Browse the repository at this point in the history
  • Loading branch information
yyx990803 committed Nov 10, 2020
1 parent 3f99e23 commit 886ed76
Show file tree
Hide file tree
Showing 8 changed files with 192 additions and 77 deletions.
38 changes: 30 additions & 8 deletions packages/compiler-core/src/codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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(`)
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -356,7 +374,8 @@ function genFunctionPreamble(ast: RootNode, context: CodegenContext) {
function genModulePreamble(
ast: RootNode,
context: CodegenContext,
genScopeId: boolean
genScopeId: boolean,
inline?: boolean
) {
const {
push,
Expand Down Expand Up @@ -423,7 +442,10 @@ function genModulePreamble(

genHoists(ast.hoists, context)
newline()
push(`export `)

if (!inline) {
push(`export `)
}
}

function genAssets(
Expand Down
52 changes: 34 additions & 18 deletions packages/compiler-core/src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
4 changes: 3 additions & 1 deletion packages/compiler-core/src/runtimeHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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!
Expand Down Expand Up @@ -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) {
Expand Down
4 changes: 4 additions & 0 deletions packages/compiler-core/src/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ export function createTransformContext(
ssr = false,
ssrCssVars = ``,
bindingMetadata = EMPTY_OBJ,
inline = false,
inlinePropsIdentifier = `$props`,
onError = defaultOnError
}: TransformOptions
): TransformContext {
Expand All @@ -142,6 +144,8 @@ export function createTransformContext(
ssr,
ssrCssVars,
bindingMetadata,
inline,
inlinePropsIdentifier,
onError,

// state
Expand Down
20 changes: 15 additions & 5 deletions packages/compiler-core/src/transforms/transformExpression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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')

Expand Down Expand Up @@ -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.
Expand Down
10 changes: 8 additions & 2 deletions packages/compiler-sfc/__tests__/compileScript.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -464,14 +464,20 @@ describe('SFC compile <script setup>', () => {
compile(`<script setup>
export const a = 1
</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(() =>
compile(`<script setup>
const bar = 1
export { bar as default }
</script>`)
).toThrow(`cannot contain non-type named exports`)
).toThrow(`cannot contain non-type named or * exports`)
})

test('ref: non-assignment expressions', () => {
Expand Down
Loading

0 comments on commit 886ed76

Please sign in to comment.