| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763 |
- import {
- ConstantTypes,
- type DirectiveArguments,
- type ForCodegenNode,
- type IfConditionalExpression,
- NodeTypes,
- type RootNode,
- type VNodeCall,
- createArrayExpression,
- createAssignmentExpression,
- createBlockStatement,
- createCacheExpression,
- createCallExpression,
- createCompoundExpression,
- createConditionalExpression,
- createIfStatement,
- createInterpolation,
- createObjectExpression,
- createObjectProperty,
- createSimpleExpression,
- createTemplateLiteral,
- createVNodeCall,
- generate,
- locStub,
- } from '../src'
- import {
- CREATE_COMMENT,
- CREATE_ELEMENT_VNODE,
- CREATE_VNODE,
- FRAGMENT,
- RENDER_LIST,
- RESOLVE_COMPONENT,
- RESOLVE_DIRECTIVE,
- TO_DISPLAY_STRING,
- helperNameMap,
- } from '../src/runtimeHelpers'
- import { createElementWithCodegen, genFlagText } from './testUtils'
- import { PatchFlags } from '@vue/shared'
- function createRoot(options: Partial<RootNode> = {}): RootNode {
- return {
- type: NodeTypes.ROOT,
- source: '',
- children: [],
- helpers: new Set(),
- components: [],
- directives: [],
- imports: [],
- hoists: [],
- cached: 0,
- temps: 0,
- codegenNode: createSimpleExpression(`null`, false),
- loc: locStub,
- ...options,
- }
- }
- describe('compiler: codegen', () => {
- test('module mode preamble', () => {
- const root = createRoot({
- helpers: new Set([CREATE_VNODE, RESOLVE_DIRECTIVE]),
- })
- const { code } = generate(root, { mode: 'module' })
- expect(code).toMatch(
- `import { ${helperNameMap[CREATE_VNODE]} as _${helperNameMap[CREATE_VNODE]}, ${helperNameMap[RESOLVE_DIRECTIVE]} as _${helperNameMap[RESOLVE_DIRECTIVE]} } from "vue"`,
- )
- expect(code).toMatchSnapshot()
- })
- test('module mode preamble w/ optimizeImports: true', () => {
- const root = createRoot({
- helpers: new Set([CREATE_VNODE, RESOLVE_DIRECTIVE]),
- })
- const { code } = generate(root, { mode: 'module', optimizeImports: true })
- expect(code).toMatch(
- `import { ${helperNameMap[CREATE_VNODE]}, ${helperNameMap[RESOLVE_DIRECTIVE]} } from "vue"`,
- )
- expect(code).toMatch(
- `const _${helperNameMap[CREATE_VNODE]} = ${helperNameMap[CREATE_VNODE]}, _${helperNameMap[RESOLVE_DIRECTIVE]} = ${helperNameMap[RESOLVE_DIRECTIVE]}`,
- )
- expect(code).toMatchSnapshot()
- })
- test('function mode preamble', () => {
- const root = createRoot({
- helpers: new Set([CREATE_VNODE, RESOLVE_DIRECTIVE]),
- })
- const { code } = generate(root, { mode: 'function' })
- expect(code).toMatch(`const _Vue = Vue`)
- expect(code).toMatch(
- `const { ${helperNameMap[CREATE_VNODE]}: _${helperNameMap[CREATE_VNODE]}, ${helperNameMap[RESOLVE_DIRECTIVE]}: _${helperNameMap[RESOLVE_DIRECTIVE]} } = _Vue`,
- )
- expect(code).toMatchSnapshot()
- })
- test('function mode preamble w/ prefixIdentifiers: true', () => {
- const root = createRoot({
- helpers: new Set([CREATE_VNODE, RESOLVE_DIRECTIVE]),
- })
- const { code } = generate(root, {
- mode: 'function',
- prefixIdentifiers: true,
- })
- expect(code).not.toMatch(`const _Vue = Vue`)
- expect(code).toMatch(
- `const { ${helperNameMap[CREATE_VNODE]}: _${helperNameMap[CREATE_VNODE]}, ${helperNameMap[RESOLVE_DIRECTIVE]}: _${helperNameMap[RESOLVE_DIRECTIVE]} } = Vue`,
- )
- expect(code).toMatchSnapshot()
- })
- test('assets + temps', () => {
- const root = createRoot({
- components: [`Foo`, `bar-baz`, `barbaz`, `Qux__self`],
- directives: [`my_dir_0`, `my_dir_1`],
- temps: 3,
- })
- const { code } = generate(root, { mode: 'function' })
- expect(code).toMatch(
- `const _component_Foo = _${helperNameMap[RESOLVE_COMPONENT]}("Foo")\n`,
- )
- expect(code).toMatch(
- `const _component_bar_baz = _${helperNameMap[RESOLVE_COMPONENT]}("bar-baz")\n`,
- )
- expect(code).toMatch(
- `const _component_barbaz = _${helperNameMap[RESOLVE_COMPONENT]}("barbaz")\n`,
- )
- // implicit self reference from SFC filename
- expect(code).toMatch(
- `const _component_Qux = _${helperNameMap[RESOLVE_COMPONENT]}("Qux", true)\n`,
- )
- expect(code).toMatch(
- `const _directive_my_dir_0 = _${helperNameMap[RESOLVE_DIRECTIVE]}("my_dir_0")\n`,
- )
- expect(code).toMatch(
- `const _directive_my_dir_1 = _${helperNameMap[RESOLVE_DIRECTIVE]}("my_dir_1")\n`,
- )
- expect(code).toMatch(`let _temp0, _temp1, _temp2`)
- expect(code).toMatchSnapshot()
- })
- test('hoists', () => {
- const root = createRoot({
- hoists: [
- createSimpleExpression(`hello`, false, locStub),
- createObjectExpression(
- [
- createObjectProperty(
- createSimpleExpression(`id`, true, locStub),
- createSimpleExpression(`foo`, true, locStub),
- ),
- ],
- locStub,
- ),
- ],
- })
- const { code } = generate(root)
- expect(code).toMatch(`const _hoisted_1 = hello`)
- expect(code).toMatch(`const _hoisted_2 = { id: "foo" }`)
- expect(code).toMatchSnapshot()
- })
- test('temps', () => {
- const root = createRoot({
- temps: 3,
- })
- const { code } = generate(root)
- expect(code).toMatch(`let _temp0, _temp1, _temp2`)
- expect(code).toMatchSnapshot()
- })
- test('static text', () => {
- const { code } = generate(
- createRoot({
- codegenNode: {
- type: NodeTypes.TEXT,
- content: 'hello',
- loc: locStub,
- },
- }),
- )
- expect(code).toMatch(`return "hello"`)
- expect(code).toMatchSnapshot()
- })
- test('interpolation', () => {
- const { code } = generate(
- createRoot({
- codegenNode: createInterpolation(`hello`, locStub),
- }),
- )
- expect(code).toMatch(`return _${helperNameMap[TO_DISPLAY_STRING]}(hello)`)
- expect(code).toMatchSnapshot()
- })
- test('comment', () => {
- const { code } = generate(
- createRoot({
- codegenNode: {
- type: NodeTypes.COMMENT,
- content: 'foo',
- loc: locStub,
- },
- }),
- )
- expect(code).toMatch(`return _${helperNameMap[CREATE_COMMENT]}("foo")`)
- expect(code).toMatchSnapshot()
- })
- test('compound expression', () => {
- const { code } = generate(
- createRoot({
- codegenNode: createCompoundExpression([
- `_ctx.`,
- createSimpleExpression(`foo`, false, locStub),
- ` + `,
- {
- type: NodeTypes.INTERPOLATION,
- loc: locStub,
- content: createSimpleExpression(`bar`, false, locStub),
- },
- // nested compound
- createCompoundExpression([` + `, `nested`]),
- ]),
- }),
- )
- expect(code).toMatch(
- `return _ctx.foo + _${helperNameMap[TO_DISPLAY_STRING]}(bar) + nested`,
- )
- expect(code).toMatchSnapshot()
- })
- test('ifNode', () => {
- const { code } = generate(
- createRoot({
- codegenNode: {
- type: NodeTypes.IF,
- loc: locStub,
- branches: [],
- codegenNode: createConditionalExpression(
- createSimpleExpression('foo', false),
- createSimpleExpression('bar', false),
- createSimpleExpression('baz', false),
- ) as IfConditionalExpression,
- },
- }),
- )
- expect(code).toMatch(/return foo\s+\? bar\s+: baz/)
- expect(code).toMatchSnapshot()
- })
- test('forNode', () => {
- const { code } = generate(
- createRoot({
- codegenNode: {
- type: NodeTypes.FOR,
- loc: locStub,
- source: createSimpleExpression('foo', false),
- valueAlias: undefined,
- keyAlias: undefined,
- objectIndexAlias: undefined,
- children: [],
- parseResult: {} as any,
- codegenNode: {
- type: NodeTypes.VNODE_CALL,
- tag: FRAGMENT,
- isBlock: true,
- disableTracking: true,
- props: undefined,
- children: createCallExpression(RENDER_LIST),
- patchFlag: PatchFlags.TEXT,
- dynamicProps: undefined,
- directives: undefined,
- loc: locStub,
- } as ForCodegenNode,
- },
- }),
- )
- expect(code).toMatch(`openBlock(true)`)
- expect(code).toMatchSnapshot()
- })
- test('forNode with constant expression', () => {
- const { code } = generate(
- createRoot({
- codegenNode: {
- type: NodeTypes.FOR,
- loc: locStub,
- source: createSimpleExpression(
- '1 + 2',
- false,
- locStub,
- ConstantTypes.CAN_STRINGIFY,
- ),
- valueAlias: undefined,
- keyAlias: undefined,
- objectIndexAlias: undefined,
- children: [],
- parseResult: {} as any,
- codegenNode: {
- type: NodeTypes.VNODE_CALL,
- tag: FRAGMENT,
- isBlock: true,
- disableTracking: false,
- props: undefined,
- children: createCallExpression(RENDER_LIST),
- patchFlag: PatchFlags.STABLE_FRAGMENT,
- dynamicProps: undefined,
- directives: undefined,
- loc: locStub,
- } as ForCodegenNode,
- },
- }),
- )
- expect(code).toMatch(`openBlock()`)
- expect(code).toMatchSnapshot()
- })
- test('Element (callExpression + objectExpression + TemplateChildNode[])', () => {
- const { code } = generate(
- createRoot({
- codegenNode: createElementWithCodegen(
- // string
- `"div"`,
- // ObjectExpression
- createObjectExpression(
- [
- createObjectProperty(
- createSimpleExpression(`id`, true, locStub),
- createSimpleExpression(`foo`, true, locStub),
- ),
- createObjectProperty(
- createSimpleExpression(`prop`, false, locStub),
- createSimpleExpression(`bar`, false, locStub),
- ),
- // compound expression as computed key
- createObjectProperty(
- {
- type: NodeTypes.COMPOUND_EXPRESSION,
- loc: locStub,
- children: [
- `foo + `,
- createSimpleExpression(`bar`, false, locStub),
- ],
- },
- createSimpleExpression(`bar`, false, locStub),
- ),
- ],
- locStub,
- ),
- // ChildNode[]
- [
- createElementWithCodegen(
- `"p"`,
- createObjectExpression(
- [
- createObjectProperty(
- // should quote the key!
- createSimpleExpression(`some-key`, true, locStub),
- createSimpleExpression(`foo`, true, locStub),
- ),
- ],
- locStub,
- ),
- ),
- ],
- // flag
- PatchFlags.FULL_PROPS,
- ),
- }),
- )
- expect(code).toMatch(`
- return _${helperNameMap[CREATE_ELEMENT_VNODE]}("div", {
- id: "foo",
- [prop]: bar,
- [foo + bar]: bar
- }, [
- _${helperNameMap[CREATE_ELEMENT_VNODE]}("p", { "some-key": "foo" })
- ], ${genFlagText(PatchFlags.FULL_PROPS)})`)
- expect(code).toMatchSnapshot()
- })
- test('ArrayExpression', () => {
- const { code } = generate(
- createRoot({
- codegenNode: createArrayExpression([
- createSimpleExpression(`foo`, false),
- createCallExpression(`bar`, [`baz`]),
- ]),
- }),
- )
- expect(code).toMatch(`return [
- foo,
- bar(baz)
- ]`)
- expect(code).toMatchSnapshot()
- })
- test('ConditionalExpression', () => {
- const { code } = generate(
- createRoot({
- codegenNode: createConditionalExpression(
- createSimpleExpression(`ok`, false),
- createCallExpression(`foo`),
- createConditionalExpression(
- createSimpleExpression(`orNot`, false),
- createCallExpression(`bar`),
- createCallExpression(`baz`),
- ),
- ),
- }),
- )
- expect(code).toMatch(
- `return ok
- ? foo()
- : orNot
- ? bar()
- : baz()`,
- )
- expect(code).toMatchSnapshot()
- })
- test('CacheExpression', () => {
- const { code } = generate(
- createRoot({
- cached: 1,
- codegenNode: createCacheExpression(
- 1,
- createSimpleExpression(`foo`, false),
- ),
- }),
- {
- mode: 'module',
- prefixIdentifiers: true,
- },
- )
- expect(code).toMatch(`_cache[1] || (_cache[1] = foo)`)
- expect(code).toMatchSnapshot()
- })
- test('CacheExpression w/ isVNode: true', () => {
- const { code } = generate(
- createRoot({
- cached: 1,
- codegenNode: createCacheExpression(
- 1,
- createSimpleExpression(`foo`, false),
- true,
- ),
- }),
- {
- mode: 'module',
- prefixIdentifiers: true,
- },
- )
- expect(code).toMatch(
- `
- _cache[1] || (
- _setBlockTracking(-1),
- _cache[1] = foo,
- _setBlockTracking(1),
- _cache[1]
- )
- `.trim(),
- )
- expect(code).toMatchSnapshot()
- })
- test('TemplateLiteral', () => {
- const { code } = generate(
- createRoot({
- codegenNode: createCallExpression(`_push`, [
- createTemplateLiteral([
- `foo`,
- createCallExpression(`_renderAttr`, ['id', 'foo']),
- `bar`,
- ]),
- ]),
- }),
- { ssr: true, mode: 'module' },
- )
- expect(code).toMatchInlineSnapshot(`
- "
- export function ssrRender(_ctx, _push, _parent, _attrs) {
- _push(\`foo\${_renderAttr(id, foo)}bar\`)
- }"
- `)
- })
- describe('IfStatement', () => {
- test('if', () => {
- const { code } = generate(
- createRoot({
- codegenNode: createBlockStatement([
- createIfStatement(
- createSimpleExpression('foo', false),
- createBlockStatement([createCallExpression(`ok`)]),
- ),
- ]),
- }),
- { ssr: true, mode: 'module' },
- )
- expect(code).toMatchInlineSnapshot(`
- "
- export function ssrRender(_ctx, _push, _parent, _attrs) {
- if (foo) {
- ok()
- }
- }"
- `)
- })
- test('if/else', () => {
- const { code } = generate(
- createRoot({
- codegenNode: createBlockStatement([
- createIfStatement(
- createSimpleExpression('foo', false),
- createBlockStatement([createCallExpression(`foo`)]),
- createBlockStatement([createCallExpression('bar')]),
- ),
- ]),
- }),
- { ssr: true, mode: 'module' },
- )
- expect(code).toMatchInlineSnapshot(`
- "
- export function ssrRender(_ctx, _push, _parent, _attrs) {
- if (foo) {
- foo()
- } else {
- bar()
- }
- }"
- `)
- })
- test('if/else-if', () => {
- const { code } = generate(
- createRoot({
- codegenNode: createBlockStatement([
- createIfStatement(
- createSimpleExpression('foo', false),
- createBlockStatement([createCallExpression(`foo`)]),
- createIfStatement(
- createSimpleExpression('bar', false),
- createBlockStatement([createCallExpression(`bar`)]),
- ),
- ),
- ]),
- }),
- { ssr: true, mode: 'module' },
- )
- expect(code).toMatchInlineSnapshot(`
- "
- export function ssrRender(_ctx, _push, _parent, _attrs) {
- if (foo) {
- foo()
- } else if (bar) {
- bar()
- }
- }"
- `)
- })
- test('if/else-if/else', () => {
- const { code } = generate(
- createRoot({
- codegenNode: createBlockStatement([
- createIfStatement(
- createSimpleExpression('foo', false),
- createBlockStatement([createCallExpression(`foo`)]),
- createIfStatement(
- createSimpleExpression('bar', false),
- createBlockStatement([createCallExpression(`bar`)]),
- createBlockStatement([createCallExpression('baz')]),
- ),
- ),
- ]),
- }),
- { ssr: true, mode: 'module' },
- )
- expect(code).toMatchInlineSnapshot(`
- "
- export function ssrRender(_ctx, _push, _parent, _attrs) {
- if (foo) {
- foo()
- } else if (bar) {
- bar()
- } else {
- baz()
- }
- }"
- `)
- })
- })
- test('AssignmentExpression', () => {
- const { code } = generate(
- createRoot({
- codegenNode: createAssignmentExpression(
- createSimpleExpression(`foo`, false),
- createSimpleExpression(`bar`, false),
- ),
- }),
- )
- expect(code).toMatchInlineSnapshot(`
- "
- return function render(_ctx, _cache) {
- with (_ctx) {
- return foo = bar
- }
- }"
- `)
- })
- describe('VNodeCall', () => {
- function genCode(node: VNodeCall) {
- return generate(
- createRoot({
- codegenNode: node,
- }),
- ).code.match(/with \(_ctx\) \{\s+([^]+)\s+\}\s+\}$/)![1]
- }
- const mockProps = createObjectExpression([
- createObjectProperty(`foo`, createSimpleExpression(`bar`, true)),
- ])
- const mockChildren = createCompoundExpression(['children'])
- const mockDirs = createArrayExpression([
- createArrayExpression([`foo`, createSimpleExpression(`bar`, false)]),
- ]) as DirectiveArguments
- test('tag only', () => {
- expect(genCode(createVNodeCall(null, `"div"`))).toMatchInlineSnapshot(`
- "return _createElementVNode("div")
- "
- `)
- expect(genCode(createVNodeCall(null, FRAGMENT))).toMatchInlineSnapshot(`
- "return _createElementVNode(_Fragment)
- "
- `)
- })
- test('with props', () => {
- expect(genCode(createVNodeCall(null, `"div"`, mockProps)))
- .toMatchInlineSnapshot(`
- "return _createElementVNode("div", { foo: "bar" })
- "
- `)
- })
- test('with children, no props', () => {
- expect(genCode(createVNodeCall(null, `"div"`, undefined, mockChildren)))
- .toMatchInlineSnapshot(`
- "return _createElementVNode("div", null, children)
- "
- `)
- })
- test('with children + props', () => {
- expect(genCode(createVNodeCall(null, `"div"`, mockProps, mockChildren)))
- .toMatchInlineSnapshot(`
- "return _createElementVNode("div", { foo: "bar" }, children)
- "
- `)
- })
- test('with patchFlag and no children/props', () => {
- expect(
- genCode(
- createVNodeCall(null, `"div"`, undefined, undefined, PatchFlags.TEXT),
- ),
- ).toMatchInlineSnapshot(`
- "return _createElementVNode("div", null, null, 1 /* TEXT */)
- "
- `)
- })
- test('as block', () => {
- expect(
- genCode(
- createVNodeCall(
- null,
- `"div"`,
- mockProps,
- mockChildren,
- undefined,
- undefined,
- undefined,
- true,
- ),
- ),
- ).toMatchInlineSnapshot(`
- "return (_openBlock(), _createElementBlock("div", { foo: "bar" }, children))
- "
- `)
- })
- test('as for block', () => {
- expect(
- genCode(
- createVNodeCall(
- null,
- `"div"`,
- mockProps,
- mockChildren,
- undefined,
- undefined,
- undefined,
- true,
- true,
- ),
- ),
- ).toMatchInlineSnapshot(`
- "return (_openBlock(true), _createElementBlock("div", { foo: "bar" }, children))
- "
- `)
- })
- test('with directives', () => {
- expect(
- genCode(
- createVNodeCall(
- null,
- `"div"`,
- mockProps,
- mockChildren,
- undefined,
- undefined,
- mockDirs,
- ),
- ),
- ).toMatchInlineSnapshot(`
- "return _withDirectives(_createElementVNode("div", { foo: "bar" }, children), [
- [foo, bar]
- ])
- "
- `)
- })
- test('block + directives', () => {
- expect(
- genCode(
- createVNodeCall(
- null,
- `"div"`,
- mockProps,
- mockChildren,
- undefined,
- undefined,
- mockDirs,
- true,
- ),
- ),
- ).toMatchInlineSnapshot(`
- "return _withDirectives((_openBlock(), _createElementBlock("div", { foo: "bar" }, children)), [
- [foo, bar]
- ])
- "
- `)
- })
- })
- })
|