Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor the assets resolution of the script setup #3546

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
17 changes: 15 additions & 2 deletions packages/compiler-core/__tests__/codegen.spec.ts
Expand Up @@ -35,6 +35,7 @@ import {
} from '../src/runtimeHelpers'
import { createElementWithCodegen, genFlagText } from './testUtils'
import { PatchFlags } from '@vue/shared'
import { AssetData } from '../src/transform'

function createRoot(options: Partial<RootNode> = {}): RootNode {
return {
Expand All @@ -53,6 +54,13 @@ function createRoot(options: Partial<RootNode> = {}): RootNode {
}
}

function createAsset(name: string): AssetData {
return {
name,
warnMissing: true
}
}

describe('compiler: codegen', () => {
test('module mode preamble', () => {
const root = createRoot({
Expand Down Expand Up @@ -126,8 +134,13 @@ describe('compiler: codegen', () => {

test('assets + temps', () => {
const root = createRoot({
components: [`Foo`, `bar-baz`, `barbaz`, `Qux__self`],
directives: [`my_dir_0`, `my_dir_1`],
components: [
createAsset(`Foo`),
createAsset(`bar-baz`),
createAsset(`barbaz`),
createAsset(`Qux__self`)
],
directives: [createAsset(`my_dir_0`), createAsset(`my_dir_1`)],
temps: 3
})
const { code } = generate(root, { mode: 'function' })
Expand Down
Expand Up @@ -69,15 +69,17 @@ describe('compiler: element transform', () => {
test('import + resolve component', () => {
const { root } = parseWithElementTransform(`<Foo/>`)
expect(root.helpers).toContain(RESOLVE_COMPONENT)
expect(root.components).toContain(`Foo`)
expect(root.components).toEqual([{ name: 'Foo', warnMissing: true }])
})

test('resolve implcitly self-referencing component', () => {
const { root } = parseWithElementTransform(`<Example/>`, {
filename: `/foo/bar/Example.vue?vue&type=template`
})
expect(root.helpers).toContain(RESOLVE_COMPONENT)
expect(root.components).toContain(`Example__self`)
expect(root.components).toEqual([
{ name: 'Example__self', warnMissing: true }
])
})

test('resolve component from setup bindings', () => {
Expand All @@ -86,8 +88,8 @@ describe('compiler: element transform', () => {
Example: BindingTypes.SETUP_MAYBE_REF
}
})
expect(root.helpers).not.toContain(RESOLVE_COMPONENT)
expect(node.tag).toBe(`$setup["Example"]`)
expect(root.helpers).toContain(RESOLVE_COMPONENT)
expect(node.tag).toBe(`_component_Example`)
})

test('do not resolve component from non-script-setup bindings', () => {
Expand All @@ -99,7 +101,7 @@ describe('compiler: element transform', () => {
bindingMetadata
})
expect(root.helpers).toContain(RESOLVE_COMPONENT)
expect(root.components).toContain(`Example`)
expect(root.components).toEqual([{ name: 'Example', warnMissing: true }])
})

test('static props', () => {
Expand Down Expand Up @@ -476,7 +478,7 @@ describe('compiler: element transform', () => {
}
)
expect(root.helpers).toContain(RESOLVE_DIRECTIVE)
expect(root.directives).toContain(`foo`)
expect(root.directives).toEqual([{ name: 'foo', warnMissing: true }])
expect(node).toMatchObject({
tag: `"div"`,
props: undefined,
Expand Down Expand Up @@ -536,9 +538,11 @@ describe('compiler: element transform', () => {
`<div v-foo v-bar="x" v-baz:[arg].mod.mad="y" />`
)
expect(root.helpers).toContain(RESOLVE_DIRECTIVE)
expect(root.directives).toContain(`foo`)
expect(root.directives).toContain(`bar`)
expect(root.directives).toContain(`baz`)
expect(root.directives).toEqual([
{ name: 'foo', warnMissing: true },
{ name: 'bar', warnMissing: true },
{ name: 'baz', warnMissing: true }
])

expect(node).toMatchObject({
directives: {
Expand Down
8 changes: 4 additions & 4 deletions packages/compiler-core/src/ast.ts
Expand Up @@ -11,7 +11,7 @@ import {
WITH_DIRECTIVES
} from './runtimeHelpers'
import { PropsExpression } from './transforms/transformElement'
import { ImportItem, TransformContext } from './transform'
import { ImportItem, TransformContext, AssetData } from './transform'

// Vue template is a platform-agnostic superset of HTML (syntax only).
// More namespaces like SVG and MathML are declared by platform specific
Expand Down Expand Up @@ -101,8 +101,8 @@ export interface RootNode extends Node {
type: NodeTypes.ROOT
children: TemplateChildNode[]
helpers: symbol[]
components: string[]
directives: string[]
components: AssetData[]
directives: AssetData[]
hoists: (JSChildNode | null)[]
imports: ImportItem[]
cached: number
Expand All @@ -111,7 +111,7 @@ export interface RootNode extends Node {
codegenNode?: TemplateChildNode | JSChildNode | BlockStatement

// v2 compat only
filters?: string[]
filters?: AssetData[]
}

export type ElementNode =
Expand Down
15 changes: 11 additions & 4 deletions packages/compiler-core/src/codegen.ts
Expand Up @@ -53,7 +53,7 @@ import {
WITH_CTX,
RESOLVE_FILTER
} from './runtimeHelpers'
import { ImportItem } from './transform'
import { AssetData, ImportItem } from './transform'

const PURE_ANNOTATION = `/*#__PURE__*/`
const WITH_ID = `_withId`
Expand Down Expand Up @@ -464,7 +464,7 @@ function genModulePreamble(
}

function genAssets(
assets: string[],
assets: AssetData[],
type: 'component' | 'directive' | 'filter',
{ helper, push, newline }: CodegenContext
) {
Expand All @@ -476,15 +476,22 @@ function genAssets(
: RESOLVE_DIRECTIVE
)
for (let i = 0; i < assets.length; i++) {
let id = assets[i]
const data = assets[i]
let id = data.name
// potential component implicit self-reference inferred from SFC filename
const maybeSelfReference = id.endsWith('__self')
if (maybeSelfReference) {
id = id.slice(0, -6)
}
const needFallbackArg = !!data.fallback
const needWarnArg = needFallbackArg || !data.warnMissing
const needMaybeSelfReferenceArg =
type === 'component' && (needWarnArg || maybeSelfReference)
push(
`const ${toValidAssetId(id, type)} = ${resolver}(${JSON.stringify(id)}${
maybeSelfReference ? `, true` : ``
needMaybeSelfReferenceArg ? `, ${JSON.stringify(data.warnMissing)}` : ``
}${needWarnArg ? `, ${JSON.stringify(data.warnMissing)}` : ``}${
needFallbackArg ? `, ${data.fallback}` : ``
})`
)
if (i < assets.length - 1) {
Expand Down
10 changes: 8 additions & 2 deletions packages/compiler-core/src/compat/transformFilter.ts
Expand Up @@ -180,12 +180,18 @@ function wrapFilter(
context.helper(RESOLVE_FILTER)
const i = filter.indexOf('(')
if (i < 0) {
context.filters!.add(filter)
context.filters!.set(filter, {
name: filter,
warnMissing: true
})
return `${toValidAssetId(filter, 'filter')}(${exp})`
} else {
const name = filter.slice(0, i)
const args = filter.slice(i + 1)
context.filters!.add(name)
context.filters!.set(name, {
name,
warnMissing: true
})
return `${toValidAssetId(name, 'filter')}(${exp}${
args !== ')' ? ',' + args : args
}`
Expand Down
24 changes: 15 additions & 9 deletions packages/compiler-core/src/transform.ts
Expand Up @@ -83,6 +83,12 @@ export interface ImportItem {
path: string
}

export interface AssetData {
name: string
warnMissing: boolean
fallback?: string
}

export interface TransformContext
extends Required<
Omit<TransformOptions, 'filename' | keyof CompilerCompatOptions>
Expand All @@ -91,8 +97,8 @@ export interface TransformContext
selfName: string | null
root: RootNode
helpers: Map<symbol, number>
components: Set<string>
directives: Set<string>
components: Map<string, AssetData>
directives: Map<string, AssetData>
hoists: (JSChildNode | null)[]
imports: ImportItem[]
temps: number
Expand Down Expand Up @@ -120,7 +126,7 @@ export interface TransformContext
constantCache: Map<TemplateChildNode, ConstantTypes>

// 2.x Compat only
filters?: Set<string>
filters?: Map<string, AssetData>
}

export function createTransformContext(
Expand Down Expand Up @@ -175,8 +181,8 @@ export function createTransformContext(
// state
root,
helpers: new Map(),
components: new Set(),
directives: new Set(),
components: new Map(),
directives: new Map(),
hoists: [],
imports: [],
constantCache: new Map(),
Expand Down Expand Up @@ -293,7 +299,7 @@ export function createTransformContext(
}

if (__COMPAT__) {
context.filters = new Set()
context.filters = new Map()
}

function addId(id: string) {
Expand Down Expand Up @@ -322,15 +328,15 @@ export function transform(root: RootNode, options: TransformOptions) {
}
// finalize meta information
root.helpers = [...context.helpers.keys()]
root.components = [...context.components]
root.directives = [...context.directives]
root.components = [...context.components.values()]
root.directives = [...context.directives.values()]
root.imports = context.imports
root.hoists = context.hoists
root.temps = context.temps
root.cached = context.cached

if (__COMPAT__) {
root.filters = [...context.filters!]
root.filters = [...context.filters!.values()]
}
}

Expand Down
33 changes: 26 additions & 7 deletions packages/compiler-core/src/transforms/transformElement.ts
Expand Up @@ -277,7 +277,13 @@ export function resolveComponentType(
if (!__BROWSER__) {
const fromSetup = resolveSetupReference(tag, context)
if (fromSetup) {
return fromSetup
context.helper(RESOLVE_COMPONENT)
context.components.set(tag, {
name: tag,
warnMissing: false,
fallback: fromSetup
})
return `${toValidAssetId(tag, `component`)}`
}
}

Expand All @@ -291,13 +297,19 @@ export function resolveComponentType(
// codegen.ts has special check for __self postfix when generating
// component imports, which will pass additional `maybeSelfReference` flag
// to `resolveComponent`.
context.components.add(tag + `__self`)
context.components.set(tag, {
name: tag + `__self`,
warnMissing: true
})
return toValidAssetId(tag, `component`)
}

// 5. user component (resolve)
context.helper(RESOLVE_COMPONENT)
context.components.add(tag)
context.components.set(tag, {
name: tag,
warnMissing: true
})
return toValidAssetId(tag, `component`)
}

Expand Down Expand Up @@ -708,16 +720,23 @@ function buildDirectiveArgs(
dirArgs.push(context.helperString(runtime))
} else {
// user directive.
context.helper(RESOLVE_DIRECTIVE)
// see if we have directives exposed via <script setup>
const fromSetup = !__BROWSER__ && resolveSetupReference(dir.name, context)
if (fromSetup) {
dirArgs.push(fromSetup)
context.directives.set(dir.name, {
name: dir.name,
warnMissing: false,
fallback: fromSetup
})
} else {
// inject statement for resolving directive
context.helper(RESOLVE_DIRECTIVE)
context.directives.add(dir.name)
dirArgs.push(toValidAssetId(dir.name, `directive`))
context.directives.set(dir.name, {
name: dir.name,
warnMissing: true
})
}
dirArgs.push(toValidAssetId(dir.name, `directive`))
}
const { loc } = dir
if (dir.exp) dirArgs.push(dir.exp)
Expand Down
Expand Up @@ -214,7 +214,7 @@ return { ref }
`;

exports[`SFC compile <script setup> inlineTemplate mode avoid unref() when necessary 1`] = `
"import { unref as _unref, toDisplayString as _toDisplayString, createTextVNode as _createTextVNode, withCtx as _withCtx, createVNode as _createVNode, Fragment as _Fragment, openBlock as _openBlock, createBlock as _createBlock } from \\"vue\\"
"import { unref as _unref, toDisplayString as _toDisplayString, createTextVNode as _createTextVNode, resolveComponent as _resolveComponent, withCtx as _withCtx, createVNode as _createVNode, Fragment as _Fragment, openBlock as _openBlock, createBlock as _createBlock } from \\"vue\\"

import { ref } from 'vue'
import Foo, { bar } from './Foo.vue'
Expand All @@ -231,8 +231,10 @@ export default {
function fn() {}

return (_ctx, _cache) => {
const _component_Foo = _resolveComponent(\\"Foo\\", false, false, Foo)

return (_openBlock(), _createBlock(_Fragment, null, [
_createVNode(Foo, null, {
_createVNode(_component_Foo, null, {
default: _withCtx(() => [
_createTextVNode(_toDisplayString(_unref(bar)), 1 /* TEXT */)
]),
Expand All @@ -247,7 +249,7 @@ return (_ctx, _cache) => {
`;

exports[`SFC compile <script setup> inlineTemplate mode referencing scope components and directives 1`] = `
"import { unref as _unref, createVNode as _createVNode, withDirectives as _withDirectives, Fragment as _Fragment, openBlock as _openBlock, createBlock as _createBlock } from \\"vue\\"
"import { resolveDirective as _resolveDirective, unref as _unref, createVNode as _createVNode, withDirectives as _withDirectives, resolveComponent as _resolveComponent, Fragment as _Fragment, openBlock as _openBlock, createBlock as _createBlock } from \\"vue\\"

import ChildComp from './Child.vue'
import SomeOtherComp from './Other.vue'
Expand All @@ -259,12 +261,16 @@ export default {


return (_ctx, _cache) => {
const _component_ChildComp = _resolveComponent(\\"ChildComp\\", false, false, ChildComp)
const _component_some_other_comp = _resolveComponent(\\"some-other-comp\\", false, false, SomeOtherComp)
const _directive_my_dir = _resolveDirective(\\"my-dir\\", false, _unref(myDir))

return (_openBlock(), _createBlock(_Fragment, null, [
_withDirectives(_createVNode(\\"div\\", null, null, 512 /* NEED_PATCH */), [
[_unref(myDir)]
[_directive_my_dir]
]),
_createVNode(ChildComp),
_createVNode(SomeOtherComp)
_createVNode(_component_ChildComp),
_createVNode(_component_some_other_comp)
], 64 /* STABLE_FRAGMENT */))
}
}
Expand Down