diff --git a/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap b/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap index 6c358654..792de768 100644 --- a/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap +++ b/packages/compiler-sfc/__tests__/__snapshots__/compileScript.spec.ts.snap @@ -554,212 +554,6 @@ return () => {} }" `; -exports[`SFC compile - `) + `, + { refSugar: true } + ) assertCode(content) expect(content).toMatch(`import { ref } from 'vue'`) }) @@ -829,7 +832,9 @@ const emit = defineEmits(['a', 'b']) expected: string | ((content: string) => boolean), shouldAsync = true ) { - const { content } = compile(``) + const { content } = compile(``, { + refSugar: true + }) expect(content).toMatch(`${shouldAsync ? `async ` : ``}setup(`) if (typeof expected === 'string') { expect(content).toMatch(expected) @@ -893,238 +898,6 @@ const emit = defineEmits(['a', 'b']) }) }) - describe('ref: syntax sugar', () => { - test('convert ref declarations', () => { - const { content, bindings } = compile(``) - expect(content).toMatch(`import { ref as _ref } from 'vue'`) - expect(content).not.toMatch(`ref: foo`) - expect(content).not.toMatch(`ref: a`) - expect(content).not.toMatch(`ref: b`) - expect(content).toMatch(`const foo = _ref()`) - expect(content).toMatch(`const a = _ref(1)`) - expect(content).toMatch(` - const b = _ref({ - count: 0 - }) - `) - // normal declarations left untouched - expect(content).toMatch(`let c = () => {}`) - expect(content).toMatch(`let d`) - assertCode(content) - expect(bindings).toStrictEqual({ - foo: BindingTypes.SETUP_REF, - a: BindingTypes.SETUP_REF, - b: BindingTypes.SETUP_REF, - c: BindingTypes.SETUP_LET, - d: BindingTypes.SETUP_LET - }) - }) - - test('multi ref declarations', () => { - const { content, bindings } = compile(``) - expect(content).toMatch(` - const a = _ref(1), b = _ref(2), c = _ref({ - count: 0 - }) - `) - expect(content).toMatch(`return { a, b, c }`) - assertCode(content) - expect(bindings).toStrictEqual({ - a: BindingTypes.SETUP_REF, - b: BindingTypes.SETUP_REF, - c: BindingTypes.SETUP_REF - }) - }) - - test('should not convert non ref labels', () => { - const { content } = compile(``) - expect(content).toMatch(`foo: a = 1, b = 2`) - assertCode(content) - }) - - test('accessing ref binding', () => { - const { content } = compile(``) - expect(content).toMatch(`console.log(a.value)`) - expect(content).toMatch(`return a.value + 1`) - assertCode(content) - }) - - test('cases that should not append .value', () => { - const { content } = compile(``) - expect(content).not.toMatch(`a.value`) - }) - - test('mutating ref binding', () => { - const { content } = compile(``) - expect(content).toMatch(`a.value++`) - expect(content).toMatch(`a.value = a.value + 1`) - expect(content).toMatch(`b.value.count++`) - expect(content).toMatch(`b.value.count = b.value.count + 1`) - expect(content).toMatch(`;({ a: a.value } = { a: 2 })`) - expect(content).toMatch(`;[a.value] = [1]`) - assertCode(content) - }) - - test('using ref binding in property shorthand', () => { - const { content } = compile(``) - expect(content).toMatch(`const b = { a: a.value }`) - // should not convert destructure - expect(content).toMatch(`const { a } = b`) - assertCode(content) - }) - - test('should not rewrite scope variable', () => { - const { content } = compile(` - `) - expect(content).toMatch('console.log(a)') - expect(content).toMatch('console.log(b.value)') - expect(content).toMatch('console.log(c)') - expect(content).toMatch('console.log($d)') - expect(content).toMatch('console.log(d.value)') - expect(content).toMatch('console.log(e)') - assertCode(content) - }) - - test('object destructure', () => { - const { content, bindings } = compile(``) - expect(content).toMatch( - `const n = _ref(1), { a: __a, b: __c, d: __d = 1, e: __f = 2, ...__g } = useFoo()` - ) - expect(content).toMatch(`const { foo: __foo } = useSomthing(() => 1)`) - expect(content).toMatch(`\nconst a = _ref(__a);`) - expect(content).not.toMatch(`\nconst b = _ref(__b);`) - expect(content).toMatch(`\nconst c = _ref(__c);`) - expect(content).toMatch(`\nconst d = _ref(__d);`) - expect(content).not.toMatch(`\nconst e = _ref(__e);`) - expect(content).toMatch(`\nconst f = _ref(__f);`) - expect(content).toMatch(`\nconst g = _ref(__g);`) - expect(content).toMatch(`\nconst foo = _ref(__foo);`) - expect(content).toMatch( - `console.log(n.value, a.value, c.value, d.value, f.value, g.value, foo.value)` - ) - expect(content).toMatch(`return { n, a, c, d, f, g, foo }`) - expect(bindings).toStrictEqual({ - n: BindingTypes.SETUP_REF, - a: BindingTypes.SETUP_REF, - c: BindingTypes.SETUP_REF, - d: BindingTypes.SETUP_REF, - f: BindingTypes.SETUP_REF, - g: BindingTypes.SETUP_REF, - foo: BindingTypes.SETUP_REF - }) - assertCode(content) - }) - - test('array destructure', () => { - const { content, bindings } = compile(``) - expect(content).toMatch( - `const n = _ref(1), [__a, __b = 1, ...__c] = useFoo()` - ) - expect(content).toMatch(`\nconst a = _ref(__a);`) - expect(content).toMatch(`\nconst b = _ref(__b);`) - expect(content).toMatch(`\nconst c = _ref(__c);`) - expect(content).toMatch(`console.log(n.value, a.value, b.value, c.value)`) - expect(content).toMatch(`return { n, a, b, c }`) - expect(bindings).toStrictEqual({ - n: BindingTypes.SETUP_REF, - a: BindingTypes.SETUP_REF, - b: BindingTypes.SETUP_REF, - c: BindingTypes.SETUP_REF - }) - assertCode(content) - }) - - test('nested destructure', () => { - const { content, bindings } = compile(``) - expect(content).toMatch(`const [{ a: { b: __b }}] = useFoo()`) - expect(content).toMatch(`const { c: [__d, __e] } = useBar()`) - expect(content).not.toMatch(`\nconst a = _ref(__a);`) - expect(content).not.toMatch(`\nconst c = _ref(__c);`) - expect(content).toMatch(`\nconst b = _ref(__b);`) - expect(content).toMatch(`\nconst d = _ref(__d);`) - expect(content).toMatch(`\nconst e = _ref(__e);`) - expect(content).toMatch(`return { b, d, e }`) - expect(bindings).toStrictEqual({ - b: BindingTypes.SETUP_REF, - d: BindingTypes.SETUP_REF, - e: BindingTypes.SETUP_REF - }) - assertCode(content) - }) - }) - describe('errors', () => { test('`) - ).toThrow(`ref: statements can only contain assignment expressions`) - }) - test('defineProps/Emit() w/ both type and non-type args', () => { expect(() => { compile(``) - ).toThrow(`cannot reference locally declared variables`) - - expect(() => - compile(``) - ).toThrow(`cannot reference locally declared variables`) - }) - test('should allow defineProps/Emit() referencing scope var', () => { assertCode( compile(``) + expect(content).toMatch(`import { ref as _ref } from 'vue'`) + expect(content).not.toMatch(`ref: foo`) + expect(content).not.toMatch(`ref: a`) + expect(content).not.toMatch(`ref: b`) + expect(content).toMatch(`const foo = _ref()`) + expect(content).toMatch(`const a = _ref(1)`) + expect(content).toMatch(` + const b = _ref({ + count: 0 + }) + `) + // normal declarations left untouched + expect(content).toMatch(`let c = () => {}`) + expect(content).toMatch(`let d`) + assertCode(content) + expect(bindings).toStrictEqual({ + foo: BindingTypes.SETUP_REF, + a: BindingTypes.SETUP_REF, + b: BindingTypes.SETUP_REF, + c: BindingTypes.SETUP_LET, + d: BindingTypes.SETUP_LET + }) + }) + + test('multi ref declarations', () => { + const { content, bindings } = compileWithRefSugar(``) + expect(content).toMatch(` + const a = _ref(1), b = _ref(2), c = _ref({ + count: 0 + }) + `) + expect(content).toMatch(`return { a, b, c }`) + assertCode(content) + expect(bindings).toStrictEqual({ + a: BindingTypes.SETUP_REF, + b: BindingTypes.SETUP_REF, + c: BindingTypes.SETUP_REF + }) + }) + + test('should not convert non ref labels', () => { + const { content } = compileWithRefSugar(``) + expect(content).toMatch(`foo: a = 1, b = 2`) + assertCode(content) + }) + + test('accessing ref binding', () => { + const { content } = compileWithRefSugar(``) + expect(content).toMatch(`console.log(a.value)`) + expect(content).toMatch(`return a.value + 1`) + assertCode(content) + }) + + test('cases that should not append .value', () => { + const { content } = compileWithRefSugar(``) + expect(content).not.toMatch(`a.value`) + }) + + test('mutating ref binding', () => { + const { content } = compileWithRefSugar(``) + expect(content).toMatch(`a.value++`) + expect(content).toMatch(`a.value = a.value + 1`) + expect(content).toMatch(`b.value.count++`) + expect(content).toMatch(`b.value.count = b.value.count + 1`) + expect(content).toMatch(`;({ a: a.value } = { a: 2 })`) + expect(content).toMatch(`;[a.value] = [1]`) + assertCode(content) + }) + + test('using ref binding in property shorthand', () => { + const { content } = compileWithRefSugar(``) + expect(content).toMatch(`const b = { a: a.value }`) + // should not convert destructure + expect(content).toMatch(`const { a } = b`) + assertCode(content) + }) + + test('should not rewrite scope variable', () => { + const { content } = compileWithRefSugar(` + `) + expect(content).toMatch('console.log(a)') + expect(content).toMatch('console.log(b.value)') + expect(content).toMatch('console.log(c)') + expect(content).toMatch('console.log($d)') + expect(content).toMatch('console.log(d.value)') + expect(content).toMatch('console.log(e)') + assertCode(content) + }) + + test('object destructure', () => { + const { content, bindings } = compileWithRefSugar(``) + expect(content).toMatch( + `const n = _ref(1), { a: __a, b: __c, d: __d = 1, e: __f = 2, ...__g } = useFoo()` + ) + expect(content).toMatch(`const { foo: __foo } = useSomthing(() => 1)`) + expect(content).toMatch(`\nconst a = _ref(__a);`) + expect(content).not.toMatch(`\nconst b = _ref(__b);`) + expect(content).toMatch(`\nconst c = _ref(__c);`) + expect(content).toMatch(`\nconst d = _ref(__d);`) + expect(content).not.toMatch(`\nconst e = _ref(__e);`) + expect(content).toMatch(`\nconst f = _ref(__f);`) + expect(content).toMatch(`\nconst g = _ref(__g);`) + expect(content).toMatch(`\nconst foo = _ref(__foo);`) + expect(content).toMatch( + `console.log(n.value, a.value, c.value, d.value, f.value, g.value, foo.value)` + ) + expect(content).toMatch(`return { n, a, c, d, f, g, foo }`) + expect(bindings).toStrictEqual({ + n: BindingTypes.SETUP_REF, + a: BindingTypes.SETUP_REF, + c: BindingTypes.SETUP_REF, + d: BindingTypes.SETUP_REF, + f: BindingTypes.SETUP_REF, + g: BindingTypes.SETUP_REF, + foo: BindingTypes.SETUP_REF + }) + assertCode(content) + }) + + test('array destructure', () => { + const { content, bindings } = compileWithRefSugar(``) + expect(content).toMatch( + `const n = _ref(1), [__a, __b = 1, ...__c] = useFoo()` + ) + expect(content).toMatch(`\nconst a = _ref(__a);`) + expect(content).toMatch(`\nconst b = _ref(__b);`) + expect(content).toMatch(`\nconst c = _ref(__c);`) + expect(content).toMatch(`console.log(n.value, a.value, b.value, c.value)`) + expect(content).toMatch(`return { n, a, b, c }`) + expect(bindings).toStrictEqual({ + n: BindingTypes.SETUP_REF, + a: BindingTypes.SETUP_REF, + b: BindingTypes.SETUP_REF, + c: BindingTypes.SETUP_REF + }) + assertCode(content) + }) + + test('nested destructure', () => { + const { content, bindings } = compileWithRefSugar(``) + expect(content).toMatch(`const [{ a: { b: __b }}] = useFoo()`) + expect(content).toMatch(`const { c: [__d, __e] } = useBar()`) + expect(content).not.toMatch(`\nconst a = _ref(__a);`) + expect(content).not.toMatch(`\nconst c = _ref(__c);`) + expect(content).toMatch(`\nconst b = _ref(__b);`) + expect(content).toMatch(`\nconst d = _ref(__d);`) + expect(content).toMatch(`\nconst e = _ref(__e);`) + expect(content).toMatch(`return { b, d, e }`) + expect(bindings).toStrictEqual({ + b: BindingTypes.SETUP_REF, + d: BindingTypes.SETUP_REF, + e: BindingTypes.SETUP_REF + }) + assertCode(content) + }) + + describe('errors', () => { + test('ref: non-assignment expressions', () => { + expect(() => + compile( + ``, + { refSugar: true } + ) + ).toThrow(`ref: statements can only contain assignment expressions`) + }) + + test('defineProps/Emit() referencing ref declarations', () => { + expect(() => + compile( + ``, + { refSugar: true } + ) + ).toThrow(`cannot reference locally declared variables`) + + expect(() => + compile( + ``, + { refSugar: true } + ) + ).toThrow(`cannot reference locally declared variables`) + }) + }) +}) diff --git a/packages/compiler-sfc/src/compileScript.ts b/packages/compiler-sfc/src/compileScript.ts index 9347e996..d8873610 100644 --- a/packages/compiler-sfc/src/compileScript.ts +++ b/packages/compiler-sfc/src/compileScript.ts @@ -189,7 +189,7 @@ export function compileScript( const setupBindings: Record = Object.create(null) const refBindings: Record = Object.create(null) const refIdentifiers: Set = new Set() - const enableRefSugar = options.refSugar !== false + const enableRefSugar = !!options.refSugar let defaultExport: Node | undefined let hasDefinePropsCall = false let hasDefineEmitCall = false @@ -732,10 +732,11 @@ export function compileScript( ) processRefExpression(node.body.expression, node) } else { - // TODO if we end up shipping ref: sugar as an opt-in feature, - // need to proxy the option in vite, vue-loader and rollup-plugin-vue. error( - `ref: sugar needs to be explicitly enabled via vite or vue-loader options.`, + `ref: sugar now needs to be explicitly enabled via @vitejs/plugin-vue ` + + `or vue-loader options:\n` + + `- @vitejs/plugin-vue: via \`script.refSugar\`\n` + + `- vue-loader: via \`refSugar\` (requires 16.3.0+)`, node ) }