import { vi } from 'vitest' import { BindingTypes } from '@vue/compiler-core' import { assertCode, compileSFCScript as compile, getPositionInCode, mockId, } from './utils' import { type RawSourceMap, SourceMapConsumer } from 'source-map-js' vi.mock('../src/warn', () => ({ warn: vi.fn(), warnOnce: vi.fn(), })) import { warnOnce } from '../src/warn' const warnOnceMock = vi.mocked(warnOnce) describe('SFC compile `) expect(content).toMatch(`return { a, b }`) assertCode(content) }) test('should expose top level declarations', () => { const { content, bindings } = compile(` `) expect(content).toMatch( `return { get aa() { return aa }, set aa(v) { aa = v }, ` + `bb, cc, dd, get a() { return a }, set a(v) { a = v }, b, c, d, ` + `get xx() { return xx }, get x() { return x } }`, ) expect(bindings).toStrictEqual({ x: BindingTypes.SETUP_MAYBE_REF, a: BindingTypes.SETUP_LET, b: BindingTypes.SETUP_CONST, c: BindingTypes.SETUP_CONST, d: BindingTypes.SETUP_CONST, xx: BindingTypes.SETUP_MAYBE_REF, aa: BindingTypes.SETUP_LET, bb: BindingTypes.LITERAL_CONST, cc: BindingTypes.SETUP_CONST, dd: BindingTypes.SETUP_CONST, }) assertCode(content) }) test('binding analysis for destructure', () => { const { content, bindings } = compile(` `) expect(content).toMatch('return { foo, bar, baz, y, z }') expect(bindings).toStrictEqual({ foo: BindingTypes.SETUP_MAYBE_REF, bar: BindingTypes.SETUP_MAYBE_REF, baz: BindingTypes.SETUP_MAYBE_REF, y: BindingTypes.SETUP_MAYBE_REF, z: BindingTypes.SETUP_MAYBE_REF, }) assertCode(content) }) test('demote const reactive binding to let when used in v-model', () => { warnOnceMock.mockClear() const { content, bindings } = compile(` `) expect(content).toMatch( `let name = reactive({ first: 'john', last: 'doe' })`, ) expect(bindings!.name).toBe(BindingTypes.SETUP_LET) expect(warnOnceMock).toHaveBeenCalledTimes(1) expect(warnOnceMock).toHaveBeenCalledWith( expect.stringContaining( '`v-model` cannot update a `const` reactive binding', ), ) assertCode(content) }) test('demote const reactive binding to let when used in v-model (inlineTemplate)', () => { warnOnceMock.mockClear() const { content, bindings } = compile( ` `, { inlineTemplate: true }, ) expect(content).toMatch( `let name = reactive({ first: 'john', last: 'doe' })`, ) expect(bindings!.name).toBe(BindingTypes.SETUP_LET) expect(warnOnceMock).toHaveBeenCalledTimes(1) expect(warnOnceMock).toHaveBeenCalledWith( expect.stringContaining( '`v-model` cannot update a `const` reactive binding', ), ) assertCode(content) }) test('v-model should error on literal const bindings', () => { expect(() => compile( ` `, { inlineTemplate: true }, ), ).toThrow('v-model cannot be used on a const binding') }) describe(' `) assertCode(content) }) test('script setup first', () => { const { content } = compile(` `) assertCode(content) }) // #7805 test('keep original semi style', () => { const { content } = compile(` `) assertCode(content) expect(content).toMatch(`console.log('test')`) expect(content).toMatch(`const props = __props;`) expect(content).toMatch(`const emit = __emit;`) expect(content).toMatch(`(function () {})()`) }) test('script setup first, named default export', () => { const { content } = compile(` `) assertCode(content) }) // #4395 test('script setup first, lang="ts", script block content export default', () => { const { content } = compile(` `) // ensure __default__ is declared before used expect(content).toMatch(/const __default__[\S\s]*\.\.\.__default__/m) assertCode(content) }) describe('spaces in ExportDefaultDeclaration node', () => { // #4371 test('with many spaces and newline', () => { // #4371 const { content } = compile(` `) assertCode(content) }) test('with minimal spaces', () => { const { content } = compile(` `) assertCode(content) }) }) test('export call expression as default', () => { const { content } = compile(` `) assertCode(content) }) }) describe('imports', () => { test('should hoist and expose imports', () => { assertCode( compile(``).content, ) }) test('should extract comment for import or type declarations', () => { assertCode( compile(` `).content, ) }) // #2740 test('should allow defineProps/Emit at the start of imports', () => { assertCode( compile(``).content, ) }) test('dedupe between user & helper', () => { const { content } = compile( ` `, ) assertCode(content) expect(content).toMatch( `import { useCssVars as _useCssVars, unref as _unref } from 'vue'`, ) expect(content).toMatch(`import { useCssVars, ref } from 'vue'`) }) test('import dedupe between `) assertCode(content) expect(content.indexOf(`import { x }`)).toEqual( content.lastIndexOf(`import { x }`), ) }) describe('import ref/reactive function from other place', () => { test('import directly', () => { const { bindings } = compile(` `) expect(bindings).toStrictEqual({ ref: BindingTypes.SETUP_MAYBE_REF, reactive: BindingTypes.SETUP_MAYBE_REF, foo: BindingTypes.SETUP_MAYBE_REF, bar: BindingTypes.SETUP_MAYBE_REF, }) }) test('import w/ alias', () => { const { bindings } = compile(` `) expect(bindings).toStrictEqual({ _reactive: BindingTypes.SETUP_MAYBE_REF, _ref: BindingTypes.SETUP_MAYBE_REF, foo: BindingTypes.SETUP_MAYBE_REF, bar: BindingTypes.SETUP_MAYBE_REF, }) }) test('aliased usage before import site', () => { const { bindings } = compile(` `) expect(bindings).toStrictEqual({ bar: BindingTypes.SETUP_REACTIVE_CONST, x: BindingTypes.SETUP_CONST, }) }) }) test('should support module string names syntax', () => { const { content, bindings } = compile(` `) assertCode(content) expect(bindings).toStrictEqual({ foo: BindingTypes.SETUP_MAYBE_REF, }) }) }) describe('inlineTemplate mode', () => { test('should work', () => { const { content } = compile( ` `, { inlineTemplate: true }, ) // check snapshot and make sure helper imports and // hoists are placed correctly. assertCode(content) // in inline mode, no need to call expose() since nothing is exposed // anyway! expect(content).not.toMatch(`expose()`) }) test('with defineExpose()', () => { const { content } = compile( ` `, { inlineTemplate: true }, ) assertCode(content) expect(content).toMatch(`setup(__props, { expose: __expose })`) expect(content).toMatch(`expose({ count })`) }) test('referencing scope components and directives', () => { const { content } = compile( ` `, { inlineTemplate: true }, ) expect(content).toMatch('[_unref(vMyDir)]') expect(content).toMatch('_createVNode(ChildComp)') // kebab-case component support expect(content).toMatch('_createVNode(SomeOtherComp)') assertCode(content) }) test('avoid unref() when necessary', () => { // function, const, component import const { content } = compile( ` `, { inlineTemplate: true }, ) // no need to unref vue component import expect(content).toMatch(`createVNode(Foo,`) // #2699 should unref named imports from .vue expect(content).toMatch(`unref(bar)`) // should unref other imports expect(content).toMatch(`unref(other)`) // no need to unref constant literals expect(content).not.toMatch(`unref(constant)`) // should directly use .value for known refs expect(content).toMatch(`count.value`) // should unref() on const bindings that may be refs expect(content).toMatch(`unref(maybe)`) // should unref() on let bindings expect(content).toMatch(`unref(lett)`) // no need to unref namespace import (this also preserves tree-shaking) expect(content).toMatch(`tree.foo()`) // no need to unref function declarations expect(content).toMatch(`{ onClick: fn }`) // no need to mark constant fns in patch flag expect(content).not.toMatch(`PROPS`) assertCode(content) }) test('v-model codegen', () => { const { content } = compile( ` `, { inlineTemplate: true }, ) // known const ref: set value expect(content).toMatch(`(count).value = $event`) // const but maybe ref: assign if ref, otherwise do nothing expect(content).toMatch(`_isRef(maybe) ? (maybe).value = $event : null`) // let: handle both cases expect(content).toMatch( `_isRef(lett) ? (lett).value = $event : lett = $event`, ) assertCode(content) }) test('v-model w/ newlines codegen', () => { const { content } = compile( ` `, { inlineTemplate: true }, ) expect(content).toMatch(`_isRef(count) ? (count).value = $event : null`) assertCode(content) }) test('v-model should not generate ref assignment code for non-setup bindings', () => { const { content } = compile( ` `, { inlineTemplate: true }, ) expect(content).not.toMatch(`_isRef(foo)`) }) test('template assignment expression codegen', () => { const { content } = compile( ` `, { inlineTemplate: true }, ) // known const ref: set value expect(content).toMatch(`count.value = 1`) // const but maybe ref: only assign after check expect(content).toMatch(`maybe.value = count.value`) // let: handle both cases expect(content).toMatch( `_isRef(lett) ? lett.value = count.value : lett = count.value`, ) expect(content).toMatch(`_isRef(v) ? v.value += 1 : v += 1`) expect(content).toMatch(`_isRef(v) ? v.value -= 1 : v -= 1`) expect(content).toMatch(`_isRef(v) ? v.value = a : v = a`) expect(content).toMatch(`_isRef(v) ? v.value = _ctx.a : v = _ctx.a`) assertCode(content) }) test('template update expression codegen', () => { const { content } = compile( ` `, { inlineTemplate: true }, ) // known const ref: set value expect(content).toMatch(`count.value++`) expect(content).toMatch(`--count.value`) // const but maybe ref (non-ref case ignored) expect(content).toMatch(`maybe.value++`) expect(content).toMatch(`--maybe.value`) // let: handle both cases expect(content).toMatch(`_isRef(lett) ? lett.value++ : lett++`) expect(content).toMatch(`_isRef(lett) ? --lett.value : --lett`) assertCode(content) }) test('template destructure assignment codegen', () => { const { content } = compile( ` `, { inlineTemplate: true }, ) // known const ref: set value expect(content).toMatch(`({ count: count.value } = val)`) // const but maybe ref (non-ref case ignored) expect(content).toMatch(`[maybe.value] = val`) // let: assumes non-ref expect(content).toMatch(`{ lett: lett } = val`) assertCode(content) }) test('ssr codegen', () => { const { content } = compile( ` `, { inlineTemplate: true, templateOptions: { ssr: true, }, }, ) expect(content).toMatch(`\n __ssrInlineRender: true,\n`) expect(content).toMatch(`return (_ctx, _push`) expect(content).toMatch(`ssrInterpolate`) expect(content).not.toMatch(`useCssVars`) expect(content).toMatch(`":--${mockId}-count": (count.value)`) expect(content).toMatch(`":--${mockId}-style\\\\.color": (style.color)`) expect(content).toMatch( `":--${mockId}-height\\\\ \\\\+\\\\ \\\\\\"px\\\\\\"": (height.value + "px")`, ) assertCode(content) }) test('the v-for wrapped in parentheses can be correctly parsed & inline is false', () => { expect(() => compile( ` `, { inlineTemplate: false, }, ), ).not.toThrowError() }) test('unref + new expression', () => { const { content } = compile( ` `, { inlineTemplate: true }, ) expect(content).toMatch(`new (_unref(Foo))()`) expect(content).toMatch(`new (_unref(Foo)).Bar()`) assertCode(content) }) // #12682 test('source map', () => { const source = ` ` const { content, map } = compile(source, { inlineTemplate: true }) expect(map).not.toBeUndefined() const consumer = new SourceMapConsumer(map as RawSourceMap) expect( consumer.originalPositionFor(getPositionInCode(content, 'count')), ).toMatchObject(getPositionInCode(source, `count`)) expect( consumer.originalPositionFor(getPositionInCode(content, 'Error')), ).toMatchObject(getPositionInCode(source, `Error`)) }) describe('destructure setup context for built-in properties', () => { const theCompile = (template: string, setup = '/* ... */') => compile( `\n`, { inlineTemplate: true }, ) test('should extract attrs when $attrs is used', () => { let { content } = theCompile('
') expect(content).toMatch('setup(__props, { attrs: $attrs })') expect(content).not.toMatch('slots: $slots') expect(content).not.toMatch('emit: $emit') expect(content).not.toMatch('const $props = __props') assertCode(content) }) test('should extract slots when $slots is used', () => { let { content } = theCompile('') expect(content).toMatch('setup(__props, { slots: $slots })') assertCode(content) }) test('should alias __props to $props when $props is used', () => { let { content } = theCompile('
{{ $props }}
') expect(content).toMatch('setup(__props)') expect(content).toMatch('const $props = __props') assertCode(content) }) test('should extract emit when $emit is used', () => { let { content } = theCompile(`
`) expect(content).toMatch('setup(__props, { emit: $emit })') expect(content).not.toMatch('const $emit = __emit') assertCode(content) }) test('should alias __emit to $emit when defineEmits is used', () => { let { content } = compile( ` `, { inlineTemplate: true }, ) expect(content).toMatch('setup(__props, { emit: __emit })') expect(content).toMatch('const $emit = __emit') expect(content).toMatch('const emit = __emit') assertCode(content) }) test('should extract all built-in properties when they are used', () => { let { content } = theCompile( '
{{ $props }}{{ $slots }}{{ $emit }}{{ $attrs }}
', ) expect(content).toMatch( 'setup(__props, { emit: $emit, attrs: $attrs, slots: $slots })', ) expect(content).toMatch('const $props = __props') assertCode(content) }) test('should not extract built-in properties when neither is used', () => { let { content } = theCompile('
{{ msg }}
') expect(content).toMatch('setup(__props)') expect(content).not.toMatch('attrs: $attrs') expect(content).not.toMatch('slots: $slots') expect(content).not.toMatch('emit: $emit') expect(content).not.toMatch('props: $props') assertCode(content) }) describe('user-defined properties override', () => { test('should not extract $attrs when user defines it', () => { let { content } = theCompile( '
', 'let $attrs', ) expect(content).toMatch('setup(__props)') expect(content).not.toMatch('attrs: $attrs') assertCode(content) }) test('should not extract $slots when user defines it', () => { let { content } = theCompile( '', 'let $slots', ) expect(content).toMatch('setup(__props)') expect(content).not.toMatch('slots: $slots') assertCode(content) }) test('should not extract $emit when user defines it', () => { let { content } = theCompile( `
click
`, 'let $emit', ) expect(content).toMatch('setup(__props)') expect(content).not.toMatch('emit: $emit') assertCode(content) }) test('should not generate $props alias when user defines it', () => { let { content } = theCompile( '
{{ $props.msg }}
', 'let $props', ) expect(content).toMatch('setup(__props)') expect(content).not.toMatch('const $props = __props') assertCode(content) }) test('should only extract non-user-defined properties', () => { let { content } = theCompile( '
{{ $attrs }}{{ $slots }}{{ $emit }}{{ $props }}
', 'let $attrs', ) expect(content).toMatch( 'setup(__props, { emit: $emit, slots: $slots })', ) expect(content).not.toMatch('attrs: $attrs') expect(content).toMatch('const $props = __props') assertCode(content) }) test('should handle mixed defineEmits and user-defined $emit', () => { let { content } = theCompile( `
click
`, ` const emit = defineEmits(['click']) let $emit `, ) expect(content).toMatch('setup(__props, { emit: __emit })') expect(content).toMatch('const emit = __emit') expect(content).not.toMatch('const $emit = __emit') assertCode(content) }) }) }) }) describe('with TypeScript', () => { test('hoist type declarations', () => { const { content } = compile(` `) assertCode(content) }) test('runtime Enum', () => { const { content, bindings } = compile( ``, ) assertCode(content) expect(bindings).toStrictEqual({ Foo: BindingTypes.LITERAL_CONST, }) }) test('runtime Enum in normal script', () => { const { content, bindings } = compile( ` `, ) assertCode(content) expect(bindings).toStrictEqual({ D: BindingTypes.LITERAL_CONST, C: BindingTypes.LITERAL_CONST, B: BindingTypes.LITERAL_CONST, Foo: BindingTypes.LITERAL_CONST, }) }) test('const Enum', () => { const { content, bindings } = compile( ``, { hoistStatic: true }, ) assertCode(content) expect(bindings).toStrictEqual({ Foo: BindingTypes.LITERAL_CONST, }) }) test('import type', () => { const { content } = compile( ``, ) expect(content).toMatch(`return { get Baz() { return Baz } }`) assertCode(content) }) test('with generic attribute', () => { const { content } = compile(` `) assertCode(content) }) }) describe('async/await detection', () => { function assertAwaitDetection(code: string, shouldAsync = true) { const { content } = compile(``) if (shouldAsync) { expect(content).toMatch(`let __temp, __restore`) } expect(content).toMatch(`${shouldAsync ? `async ` : ``}setup(`) assertCode(content) return content } test('expression statement', () => { assertAwaitDetection(`await foo`) }) test('variable', () => { assertAwaitDetection(`const a = 1 + (await foo)`) }) test('ref', () => { assertAwaitDetection(`let a = ref(1 + (await foo))`) }) // #4448 test('nested await', () => { assertAwaitDetection(`await (await foo)`) assertAwaitDetection(`await ((await foo))`) assertAwaitDetection(`await (await (await foo))`) }) // should prepend semicolon test('nested leading await in expression statement', () => { const code = assertAwaitDetection(`foo()\nawait 1 + await 2`) expect(code).toMatch(`foo()\n;(`) }) // #4596 should NOT prepend semicolon test('single line conditions', () => { const code = assertAwaitDetection(`if (false) await foo()`) expect(code).not.toMatch(`if (false) ;(`) }) test('nested statements', () => { assertAwaitDetection(`if (ok) { await foo } else { await bar }`) }) test('multiple `if` nested statements', () => { assertAwaitDetection(`if (ok) { let a = 'foo' await 0 + await 1 await 2 } else if (a) { await 10 if (b) { await 0 + await 1 } else { let a = 'foo' await 2 } if (b) { await 3 await 4 } } else { await 5 }`) }) test('multiple `if while` nested statements', () => { assertAwaitDetection(`if (ok) { while (d) { await 5 } while (d) { await 5 await 6 if (c) { let f = 10 10 + await 7 } else { await 8 await 9 } } }`) }) test('multiple `if for` nested statements', () => { assertAwaitDetection(`if (ok) { for (let a of [1,2,3]) { await a } for (let a of [1,2,3]) { await a await a } }`) }) test('should ignore await inside functions', () => { // function declaration assertAwaitDetection(`async function foo() { await bar }`, false) // function expression assertAwaitDetection(`const foo = async () => { await bar }`, false) // object method assertAwaitDetection(`const obj = { async method() { await bar }}`, false) // class method assertAwaitDetection( `const cls = class Foo { async method() { await bar }}`, false, ) }) }) describe('errors', () => { test('`), ).toThrow(``, ), ).toThrow(``), ).toThrow(moduleErrorMsg) expect(() => compile(``), ).toThrow(moduleErrorMsg) expect(() => compile(``), ).toThrow(moduleErrorMsg) }) test('defineProps/Emit() referencing local var', () => { expect(() => compile(``), ).toThrow(`cannot reference locally declared variables`) expect(() => compile(``), ).toThrow(`cannot reference locally declared variables`) // #4644 expect(() => compile(` `), ).not.toThrow(`cannot reference locally declared variables`) }) test('should allow defineProps/Emit() referencing scope var', () => { assertCode( compile(``).content, ) }) test('should allow defineProps/Emit() referencing imported binding', () => { assertCode( compile(``).content, ) }) test('defineModel() referencing local var', () => { expect(() => compile(``), ).toThrow(`cannot reference locally declared variables`) // allow const expect(() => compile(``), ).not.toThrow(`cannot reference locally declared variables`) // allow in get/set expect(() => compile(``), ).not.toThrow(`cannot reference locally declared variables`) }) }) }) describe('SFC analyze `) expect(scriptAst).toBeDefined() }) it('recognizes props array declaration', () => { const { bindings } = compile(` `) expect(bindings).toStrictEqual({ foo: BindingTypes.PROPS, bar: BindingTypes.PROPS, }) expect(bindings!.__isScriptSetup).toBe(false) }) it('recognizes props object declaration', () => { const { bindings } = compile(` `) expect(bindings).toStrictEqual({ foo: BindingTypes.PROPS, bar: BindingTypes.PROPS, baz: BindingTypes.PROPS, qux: BindingTypes.PROPS, }) expect(bindings!.__isScriptSetup).toBe(false) }) it('recognizes setup return', () => { const { bindings } = compile(` `) expect(bindings).toStrictEqual({ foo: BindingTypes.SETUP_MAYBE_REF, bar: BindingTypes.SETUP_MAYBE_REF, }) expect(bindings!.__isScriptSetup).toBe(false) }) it('recognizes exported vars', () => { const { bindings } = compile(` `) expect(bindings).toStrictEqual({ foo: BindingTypes.LITERAL_CONST, }) }) it('recognizes async setup return', () => { const { bindings } = compile(` `) expect(bindings).toStrictEqual({ foo: BindingTypes.SETUP_MAYBE_REF, bar: BindingTypes.SETUP_MAYBE_REF, }) expect(bindings!.__isScriptSetup).toBe(false) }) it('recognizes data return', () => { const { bindings } = compile(` `) expect(bindings).toStrictEqual({ foo: BindingTypes.DATA, bar: BindingTypes.DATA, }) }) it('recognizes methods', () => { const { bindings } = compile(` `) expect(bindings).toStrictEqual({ foo: BindingTypes.OPTIONS }) }) it('recognizes computeds', () => { const { bindings } = compile(` `) expect(bindings).toStrictEqual({ foo: BindingTypes.OPTIONS, bar: BindingTypes.OPTIONS, }) }) it('recognizes injections array declaration', () => { const { bindings } = compile(` `) expect(bindings).toStrictEqual({ foo: BindingTypes.OPTIONS, bar: BindingTypes.OPTIONS, }) }) it('recognizes injections object declaration', () => { const { bindings } = compile(` `) expect(bindings).toStrictEqual({ foo: BindingTypes.OPTIONS, bar: BindingTypes.OPTIONS, }) }) it('works for mixed bindings', () => { const { bindings } = compile(` `) expect(bindings).toStrictEqual({ foo: BindingTypes.OPTIONS, bar: BindingTypes.PROPS, baz: BindingTypes.SETUP_MAYBE_REF, qux: BindingTypes.DATA, quux: BindingTypes.OPTIONS, quuz: BindingTypes.OPTIONS, }) }) it('works for script setup', () => { const { bindings } = compile(` `) expect(bindings).toStrictEqual({ r: BindingTypes.SETUP_CONST, a: BindingTypes.SETUP_REF, b: BindingTypes.SETUP_LET, c: BindingTypes.LITERAL_CONST, d: BindingTypes.SETUP_MAYBE_REF, e: BindingTypes.SETUP_LET, foo: BindingTypes.PROPS, }) }) describe('auto name inference', () => { test('basic', () => { const { content } = compile( ` `, undefined, { filename: 'FooBar.vue', }, ) expect(content).toMatch(`export default { __name: 'FooBar'`) assertCode(content) }) test('do not overwrite manual name (object)', () => { const { content } = compile( ` `, undefined, { filename: 'FooBar.vue', }, ) expect(content).not.toMatch(`name: 'FooBar'`) expect(content).toMatch(`name: 'Baz'`) assertCode(content) }) test('do not overwrite manual name (call)', () => { const { content } = compile( ` `, undefined, { filename: 'FooBar.vue', }, ) expect(content).not.toMatch(`name: 'FooBar'`) expect(content).toMatch(`name: 'Baz'`) assertCode(content) }) }) }) describe('SFC genDefaultAs', () => { test('normal `, { genDefaultAs: '_sfc_', }, ) expect(content).not.toMatch('export default') expect(content).toMatch(`const _sfc_ = {}`) assertCode(content) }) test('normal `, { genDefaultAs: '_sfc_', }, ) expect(content).not.toMatch('export default') expect(content).not.toMatch('__default__') expect(content).toMatch(`const _sfc_ = {}`) assertCode(content) }) test(' `, { genDefaultAs: '_sfc_', }, ) expect(content).not.toMatch('export default') expect(content).toMatch( `const _sfc_ = /*@__PURE__*/Object.assign(__default__`, ) assertCode(content) }) test(' `, { genDefaultAs: '_sfc_', }, ) expect(content).not.toMatch('export default') expect(content).toMatch( `const _sfc_ = /*@__PURE__*/Object.assign(__default__`, ) assertCode(content) }) test('`, { genDefaultAs: '_sfc_', }, ) expect(content).not.toMatch('export default') expect(content).toMatch(`const _sfc_ = {\n setup`) assertCode(content) }) test('`, { genDefaultAs: '_sfc_', }, ) expect(content).not.toMatch('export default') expect(content).toMatch(`const _sfc_ = /*@__PURE__*/_defineComponent(`) assertCode(content) }) test(' `, { genDefaultAs: '_sfc_', }, ) expect(content).not.toMatch('export default') expect(content).toMatch( `const _sfc_ = /*@__PURE__*/_defineComponent({\n ...__default__`, ) assertCode(content) }) test('binding type for edge cases', () => { const { bindings } = compile( ``, ) expect(bindings).toStrictEqual({ toRef: BindingTypes.SETUP_CONST, props: BindingTypes.SETUP_REACTIVE_CONST, foo: BindingTypes.SETUP_REF, }) }) describe('parser plugins', () => { test('import attributes', () => { const { content } = compile(` `) assertCode(content) expect(() => compile(` `), ).toThrow() }) test('import attributes (user override for deprecated syntax)', () => { const { content } = compile( ` `, { babelParserPlugins: [ ['importAttributes', { deprecatedAssertSyntax: true }], ], }, ) assertCode(content) }) }) }) describe('compileScript', () => { test('should care about runtimeModuleName', () => { const { content } = compile( ` `, { templateOptions: { compilerOptions: { runtimeModuleName: 'npm:vue', }, }, }, ) expect(content).toMatch( `import { withAsyncContext as _withAsyncContext } from "npm:vue"\n`, ) assertCode(content) }) test('should not compile unrecognized language', () => { const { content, lang, scriptAst } = compile( ``, ) expect(content).toMatch(`export default data: -> myVal: 0`) expect(lang).toBe('coffee') expect(scriptAst).not.toBeDefined() }) }) describe('vapor mode + ssr', () => { test('rewrite defineVaporAsyncComponent import', () => { const { content } = compile( ` `, { templateOptions: { ssr: true, }, }, ) expect(content).toContain( `import { defineAsyncComponent as defineVaporAsyncComponent } from 'vue'`, ) }) test('rewrite defineVaporAsyncComponent import with local name', () => { const { content } = compile( ` `, { templateOptions: { ssr: true, }, }, ) expect(content).toContain( `import { defineAsyncComponent as def } from 'vue'`, ) }) })