This commit is contained in:
2023-08-11 10:45:20 +08:00
commit 161ca982f3
31850 changed files with 2706500 additions and 0 deletions

View File

@ -0,0 +1,925 @@
// Vitest Snapshot v1
exports[`SFC analyze <script> bindings > auto name inference > basic 1`] = `
"export default {
__name: 'FooBar',
setup(__props) {
const a = 1
return { a }
}
}"
`;
exports[`SFC analyze <script> bindings > auto name inference > do not overwrite manual name (call) 1`] = `
"import { defineComponent } from 'vue'
const __default__ = defineComponent({
name: 'Baz'
})
export default /*#__PURE__*/Object.assign(__default__, {
setup(__props) {
const a = 1
return { a }
}
})"
`;
exports[`SFC analyze <script> bindings > auto name inference > do not overwrite manual name (object) 1`] = `
"const __default__ = {
name: 'Baz'
}
export default /*#__PURE__*/Object.assign(__default__, {
setup(__props) {
const a = 1
return { a }
}
})"
`;
exports[`SFC compile <script setup> > <script> after <script setup> the script content not end with \`\\n\` 1`] = `
"const n = 1
import { x } from './x'
export default {
setup(__props) {
return { n, x }
}
}"
`;
exports[`SFC compile <script setup> > <script> and <script setup> co-usage > script first 1`] = `
"import { x } from './x'
export const n = 1
const __default__ = {}
export default /*#__PURE__*/Object.assign(__default__, {
setup(__props) {
x()
return { n, x }
}
})"
`;
exports[`SFC compile <script setup> > <script> and <script setup> co-usage > script setup first 1`] = `
"export const n = 1
const __default__ = {}
import { x } from './x'
export default /*#__PURE__*/Object.assign(__default__, {
setup(__props) {
x()
return { n, x }
}
})"
`;
exports[`SFC compile <script setup> > <script> and <script setup> co-usage > script setup first, lang="ts", script block content export default 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
const __default__ = {
name: \\"test\\"
}
import { x } from './x'
export default /*#__PURE__*/_defineComponent({
...__default__,
setup(__props) {
x()
return { x }
}
})"
`;
exports[`SFC compile <script setup> > <script> and <script setup> co-usage > script setup first, named default export 1`] = `
"export const n = 1
const def = {}
const __default__ = def
import { x } from './x'
export default /*#__PURE__*/Object.assign(__default__, {
setup(__props) {
x()
return { n, def, x }
}
})"
`;
exports[`SFC compile <script setup> > <script> and <script setup> co-usage > spaces in ExportDefaultDeclaration node > with many spaces and newline 1`] = `
"import { x } from './x'
export const n = 1
const __default__ = {
some:'option'
}
export default /*#__PURE__*/Object.assign(__default__, {
setup(__props) {
x()
return { n, x }
}
})"
`;
exports[`SFC compile <script setup> > <script> and <script setup> co-usage > spaces in ExportDefaultDeclaration node > with minimal spaces 1`] = `
"import { x } from './x'
export const n = 1
const __default__ = {
some:'option'
}
export default /*#__PURE__*/Object.assign(__default__, {
setup(__props) {
x()
return { n, x }
}
})"
`;
exports[`SFC compile <script setup> > binding analysis for destructure 1`] = `
"export default {
setup(__props) {
const { foo, b: bar, ['x' + 'y']: baz, x: { y, zz: { z }}} = {}
return { foo, bar, baz, y, z }
}
}"
`;
exports[`SFC compile <script setup> > defineEmits() 1`] = `
"export default {
emits: ['foo', 'bar'],
setup(__props, { emit: myEmit }) {
return { myEmit }
}
}"
`;
exports[`SFC compile <script setup> > defineExpose() 1`] = `
"export default {
setup(__props, { expose }) {
expose({ foo: 123 })
return { }
}
}"
`;
exports[`SFC compile <script setup> > defineProps w/ external definition 1`] = `
"import { propsModel } from './props'
export default {
props: propsModel,
setup(__props) {
const props = __props
return { props, propsModel }
}
}"
`;
exports[`SFC compile <script setup> > defineProps w/ leading code 1`] = `
"import { x } from './x'
export default {
props: {},
setup(__props) {
const props = __props
return { props, x }
}
}"
`;
exports[`SFC compile <script setup> > defineProps() 1`] = `
"export default {
props: {
foo: String
},
setup(__props) {
const props = __props
const bar = 1
return { props, bar }
}
}"
`;
exports[`SFC compile <script setup> > defineProps/defineEmits in multi-variable declaration (full removal) 1`] = `
"export default {
props: ['item'],
emits: ['a'],
setup(__props, { emit }) {
const props = __props
return { props, emit }
}
}"
`;
exports[`SFC compile <script setup> > defineProps/defineEmits in multi-variable declaration 1`] = `
"export default {
props: ['item'],
emits: ['a'],
setup(__props, { emit }) {
const props = __props
const a = 1;
return { props, a, emit }
}
}"
`;
exports[`SFC compile <script setup> > dev mode import usage check > TS annotations 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
import { Foo, Baz, Qux, Fred } from './x'
export default /*#__PURE__*/_defineComponent({
setup(__props) {
const a = 1
function b() {}
return { a, b, Baz }
}
})"
`;
exports[`SFC compile <script setup> > dev mode import usage check > attribute expressions 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
import { bar, baz } from './x'
export default /*#__PURE__*/_defineComponent({
setup(__props) {
const cond = true
return { cond, bar, baz }
}
})"
`;
exports[`SFC compile <script setup> > dev mode import usage check > components 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
import { FooBar, FooBaz, FooQux, foo } from './x'
export default /*#__PURE__*/_defineComponent({
setup(__props) {
const fooBar: FooBar = 1
return { fooBar, FooBaz, FooQux, foo }
}
})"
`;
exports[`SFC compile <script setup> > dev mode import usage check > directive 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
import { vMyDir } from './x'
export default /*#__PURE__*/_defineComponent({
setup(__props) {
return { vMyDir }
}
})"
`;
exports[`SFC compile <script setup> > dev mode import usage check > js template string interpolations 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
import { VAR, VAR2, VAR3 } from './x'
export default /*#__PURE__*/_defineComponent({
setup(__props) {
return { VAR, VAR3 }
}
})"
`;
exports[`SFC compile <script setup> > dev mode import usage check > last tag 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
import { FooBaz, Last } from './x'
export default /*#__PURE__*/_defineComponent({
setup(__props) {
return { FooBaz, Last }
}
})"
`;
exports[`SFC compile <script setup> > dev mode import usage check > vue interpolations 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
import { x, y, z, x$y } from './x'
export default /*#__PURE__*/_defineComponent({
setup(__props) {
return { x, z, x$y }
}
})"
`;
exports[`SFC compile <script setup> > errors > should allow defineProps/Emit() referencing imported binding 1`] = `
"import { bar } from './bar'
export default {
props: {
foo: {
default: () => bar
}
},
emits: {
foo: () => bar > 1
},
setup(__props) {
return { bar }
}
}"
`;
exports[`SFC compile <script setup> > errors > should allow defineProps/Emit() referencing scope var 1`] = `
"export default {
props: {
foo: {
default: bar => bar + 1
}
},
emits: {
foo: bar => bar > 1
},
setup(__props) {
const bar = 1
return { bar }
}
}"
`;
exports[`SFC compile <script setup> > imports > import dedupe between <script> and <script setup> 1`] = `
"import { x } from './x'
export default {
setup(__props) {
x()
return { x }
}
}"
`;
exports[`SFC compile <script setup> > imports > should allow defineProps/Emit at the start of imports 1`] = `
"import { ref } from 'vue'
export default {
props: ['foo'],
emits: ['bar'],
setup(__props) {
const r = ref(0)
return { r, ref }
}
}"
`;
exports[`SFC compile <script setup> > imports > should extract comment for import or type declarations 1`] = `
"import a from 'a' // comment
import b from 'b'
export default {
setup(__props) {
return { a, b }
}
}"
`;
exports[`SFC compile <script setup> > imports > should hoist and expose imports 1`] = `
"import { ref } from 'vue'
import 'foo/css'
export default {
setup(__props) {
return { ref }
}
}"
`;
exports[`SFC compile <script setup> > should expose top level declarations 1`] = `
"import { xx } from './x'
let aa = 1
const bb = 2
function cc() {}
class dd {}
import { x } from './x'
export default {
setup(__props) {
let a = 1
const b = 2
function c() {}
class d {}
return { aa, bb, cc, dd, a, b, c, d, xx, x }
}
}"
`;
exports[`SFC compile <script setup> > with TypeScript > const Enum 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
const enum Foo { A = 123 }
export default /*#__PURE__*/_defineComponent({
setup(__props) {
return { Foo }
}
})"
`;
exports[`SFC compile <script setup> > with TypeScript > defineEmits w/ type (exported interface) 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
export interface Emits { (e: 'foo' | 'bar'): void }
export default /*#__PURE__*/_defineComponent({
emits: [\\"foo\\", \\"bar\\"],
setup(__props, { emit }: { emit: ({ (e: 'foo' | 'bar'): void }), expose: any, slots: any, attrs: any }) {
return { emit }
}
})"
`;
exports[`SFC compile <script setup> > with TypeScript > defineEmits w/ type (exported type alias) 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
export type Emits = { (e: 'foo' | 'bar'): void }
export default /*#__PURE__*/_defineComponent({
emits: [\\"foo\\", \\"bar\\"],
setup(__props, { emit }: { emit: ({ (e: 'foo' | 'bar'): void }), expose: any, slots: any, attrs: any }) {
return { emit }
}
})"
`;
exports[`SFC compile <script setup> > with TypeScript > defineEmits w/ type (interface) 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
interface Emits { (e: 'foo' | 'bar'): void }
export default /*#__PURE__*/_defineComponent({
emits: [\\"foo\\", \\"bar\\"],
setup(__props, { emit }: { emit: ({ (e: 'foo' | 'bar'): void }), expose: any, slots: any, attrs: any }) {
return { emit }
}
})"
`;
exports[`SFC compile <script setup> > with TypeScript > defineEmits w/ type (referenced exported function type) 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
export type Emits = (e: 'foo' | 'bar') => void
export default /*#__PURE__*/_defineComponent({
emits: [\\"foo\\", \\"bar\\"],
setup(__props, { emit }: { emit: ((e: 'foo' | 'bar') => void), expose: any, slots: any, attrs: any }) {
return { emit }
}
})"
`;
exports[`SFC compile <script setup> > with TypeScript > defineEmits w/ type (referenced function type) 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
type Emits = (e: 'foo' | 'bar') => void
export default /*#__PURE__*/_defineComponent({
emits: [\\"foo\\", \\"bar\\"],
setup(__props, { emit }: { emit: ((e: 'foo' | 'bar') => void), expose: any, slots: any, attrs: any }) {
return { emit }
}
})"
`;
exports[`SFC compile <script setup> > with TypeScript > defineEmits w/ type (type alias) 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
type Emits = { (e: 'foo' | 'bar'): void }
export default /*#__PURE__*/_defineComponent({
emits: [\\"foo\\", \\"bar\\"],
setup(__props, { emit }: { emit: ({ (e: 'foo' | 'bar'): void }), expose: any, slots: any, attrs: any }) {
return { emit }
}
})"
`;
exports[`SFC compile <script setup> > with TypeScript > defineEmits w/ type (type literal w/ call signatures) 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
export default /*#__PURE__*/_defineComponent({
emits: [\\"foo\\", \\"bar\\", \\"baz\\"],
setup(__props, { emit }: { emit: ({(e: 'foo' | 'bar'): void; (e: 'baz', id: number): void;}), expose: any, slots: any, attrs: any }) {
return { emit }
}
})"
`;
exports[`SFC compile <script setup> > with TypeScript > defineEmits w/ type 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
export default /*#__PURE__*/_defineComponent({
emits: [\\"foo\\", \\"bar\\"],
setup(__props, { emit }: { emit: ((e: 'foo' | 'bar') => void), expose: any, slots: any, attrs: any }) {
return { emit }
}
})"
`;
exports[`SFC compile <script setup> > with TypeScript > defineProps w/ exported interface 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
export interface Props { x?: number }
export default /*#__PURE__*/_defineComponent({
props: {
x: { type: Number, required: false }
},
setup(__props: any) {
return { }
}
})"
`;
exports[`SFC compile <script setup> > with TypeScript > defineProps w/ exported interface in normal script 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
export interface Props { x?: number }
export default /*#__PURE__*/_defineComponent({
props: {
x: { type: Number, required: false }
},
setup(__props: any) {
return { }
}
})"
`;
exports[`SFC compile <script setup> > with TypeScript > defineProps w/ exported type alias 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
export type Props = { x?: number }
export default /*#__PURE__*/_defineComponent({
props: {
x: { type: Number, required: false }
},
setup(__props: any) {
return { }
}
})"
`;
exports[`SFC compile <script setup> > with TypeScript > defineProps w/ interface 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
interface Props { x?: number }
export default /*#__PURE__*/_defineComponent({
props: {
x: { type: Number, required: false }
},
setup(__props: any) {
return { }
}
})"
`;
exports[`SFC compile <script setup> > with TypeScript > defineProps w/ type 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
interface Test {}
type Alias = number[]
export default /*#__PURE__*/_defineComponent({
props: {
string: { type: String, required: true },
number: { type: Number, required: true },
boolean: { type: Boolean, required: true },
object: { type: Object, required: true },
objectLiteral: { type: Object, required: true },
fn: { type: Function, required: true },
functionRef: { type: Function, required: true },
objectRef: { type: Object, required: true },
dateTime: { type: Date, required: true },
array: { type: Array, required: true },
arrayRef: { type: Array, required: true },
tuple: { type: Array, required: true },
set: { type: Set, required: true },
literal: { type: String, required: true },
optional: { type: null, required: false },
recordRef: { type: Object, required: true },
interface: { type: Object, required: true },
alias: { type: Array, required: true },
method: { type: Function, required: true },
symbol: { type: Symbol, required: true },
union: { type: [String, Number], required: true },
literalUnion: { type: String, required: true },
literalUnionNumber: { type: Number, required: true },
literalUnionMixed: { type: [String, Number, Boolean], required: true },
intersection: { type: Object, required: true },
foo: { type: [Function, null], required: true }
},
setup(__props: any) {
return { }
}
})"
`;
exports[`SFC compile <script setup> > with TypeScript > defineProps w/ type alias 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
type Props = { x?: number }
export default /*#__PURE__*/_defineComponent({
props: {
x: { type: Number, required: false }
},
setup(__props: any) {
return { }
}
})"
`;
exports[`SFC compile <script setup> > with TypeScript > defineProps/Emit w/ runtime options 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
export default /*#__PURE__*/_defineComponent({
props: { foo: String },
emits: ['a', 'b'],
setup(__props, { emit }) {
const props = __props
return { props, emit }
}
})"
`;
exports[`SFC compile <script setup> > with TypeScript > hoist type declarations 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
export interface Foo {}
type Bar = {}
export default /*#__PURE__*/_defineComponent({
setup(__props) {
return { }
}
})"
`;
exports[`SFC compile <script setup> > with TypeScript > import type 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
import type { Foo } from './main.ts'
import { type Bar, Baz } from './main.ts'
export default /*#__PURE__*/_defineComponent({
setup(__props) {
return { Baz }
}
})"
`;
exports[`SFC compile <script setup> > with TypeScript > runtime Enum 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
enum Foo { A = 123 }
export default /*#__PURE__*/_defineComponent({
setup(__props) {
return { Foo }
}
})"
`;
exports[`SFC compile <script setup> > with TypeScript > runtime Enum in normal script 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
enum Foo { A = 123 }
export enum D { D = \\"D\\" }
const enum C { C = \\"C\\" }
enum B { B = \\"B\\" }
export default /*#__PURE__*/_defineComponent({
setup(__props) {
return { D, C, B, Foo }
}
})"
`;
exports[`SFC compile <script setup> > with TypeScript > withDefaults (dynamic) 1`] = `
"import { mergeDefaults as _mergeDefaults, defineComponent as _defineComponent } from 'vue'
import { defaults } from './foo'
export default /*#__PURE__*/_defineComponent({
props: _mergeDefaults({
foo: { type: String, required: false },
bar: { type: Number, required: false },
baz: { type: Boolean, required: true }
}, { ...defaults }),
setup(__props: any) {
const props = __props as {
foo?: string
bar?: number
baz: boolean
}
return { props, defaults }
}
})"
`;
exports[`SFC compile <script setup> > with TypeScript > withDefaults (static) 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
export default /*#__PURE__*/_defineComponent({
props: {
foo: { type: String, required: false, default: 'hi' },
bar: { type: Number, required: false },
baz: { type: Boolean, required: true },
qux: { type: Function, required: false, default() { return 1 } }
},
setup(__props: any) {
const props = __props as { foo: string, bar?: number, baz: boolean, qux(): number }
return { props }
}
})"
`;

View File

@ -0,0 +1,189 @@
// Vitest Snapshot v1
exports[`CSS vars injection > codegen > <script> w/ default export 1`] = `
"const __default__ = { setup() {} }
import { useCssVars as _useCssVars } from 'vue'
const __injectCSSVars__ = () => {
_useCssVars((_vm, _setup) => ({
\\"xxxxxxxx-color\\": (_vm.color)
}))}
const __setup__ = __default__.setup
__default__.setup = __setup__
? (props, ctx) => { __injectCSSVars__();return __setup__(props, ctx) }
: __injectCSSVars__
export default __default__"
`;
exports[`CSS vars injection > codegen > <script> w/ default export in strings/comments 1`] = `
"
// export default {}
const __default__ = {}
import { useCssVars as _useCssVars } from 'vue'
const __injectCSSVars__ = () => {
_useCssVars((_vm, _setup) => ({
\\"xxxxxxxx-color\\": (_vm.color)
}))}
const __setup__ = __default__.setup
__default__.setup = __setup__
? (props, ctx) => { __injectCSSVars__();return __setup__(props, ctx) }
: __injectCSSVars__
export default __default__"
`;
exports[`CSS vars injection > codegen > <script> w/ no default export 1`] = `
"const a = 1
const __default__ = {}
import { useCssVars as _useCssVars } from 'vue'
const __injectCSSVars__ = () => {
_useCssVars((_vm, _setup) => ({
\\"xxxxxxxx-color\\": (_vm.color)
}))}
const __setup__ = __default__.setup
__default__.setup = __setup__
? (props, ctx) => { __injectCSSVars__();return __setup__(props, ctx) }
: __injectCSSVars__
export default __default__"
`;
exports[`CSS vars injection > codegen > should ignore comments 1`] = `
"import { useCssVars as _useCssVars } from 'vue'
export default {
setup(__props) {
_useCssVars((_vm, _setup) => ({
\\"xxxxxxxx-width\\": (_setup.width)
}))
const color = 'red';const width = 100
return { color, width }
}
}"
`;
exports[`CSS vars injection > codegen > should work with w/ complex expression 1`] = `
"import { useCssVars as _useCssVars } from 'vue'
export default {
setup(__props) {
_useCssVars((_vm, _setup) => ({
\\"xxxxxxxx-foo\\": (_setup.foo),
\\"xxxxxxxx-foo____px_\\": (_setup.foo + 'px'),
\\"xxxxxxxx-_a___b____2____px_\\": ((_setup.a + _setup.b) / 2 + 'px'),
\\"xxxxxxxx-__a___b______2___a_\\": (((_setup.a + _setup.b)) / (2 * _setup.a))
}))
let a = 100
let b = 200
let foo = 300
return { a, b, foo }
}
}"
`;
exports[`CSS vars injection > codegen > w/ <script setup> 1`] = `
"import { useCssVars as _useCssVars } from 'vue'
export default {
setup(__props) {
_useCssVars((_vm, _setup) => ({
\\"xxxxxxxx-color\\": (_setup.color)
}))
const color = 'red'
return { color }
}
}"
`;
exports[`CSS vars injection > codegen > w/ <script setup> using the same var multiple times 1`] = `
"import { useCssVars as _useCssVars } from 'vue'
export default {
setup(__props) {
_useCssVars((_vm, _setup) => ({
\\"xxxxxxxx-color\\": (_setup.color)
}))
const color = 'red'
return { color }
}
}"
`;
exports[`CSS vars injection > generating correct code for nested paths 1`] = `
"const a = 1
const __default__ = {}
import { useCssVars as _useCssVars } from 'vue'
const __injectCSSVars__ = () => {
_useCssVars((_vm, _setup) => ({
\\"xxxxxxxx-color\\": (_vm.color),
\\"xxxxxxxx-font_size\\": (_vm.font.size)
}))}
const __setup__ = __default__.setup
__default__.setup = __setup__
? (props, ctx) => { __injectCSSVars__();return __setup__(props, ctx) }
: __injectCSSVars__
export default __default__"
`;
exports[`CSS vars injection > w/ <script setup> binding analysis 1`] = `
"import { useCssVars as _useCssVars } from 'vue'
import { ref } from 'vue'
export default {
props: {
foo: String
},
setup(__props) {
_useCssVars((_vm, _setup) => ({
\\"xxxxxxxx-color\\": (_setup.color),
\\"xxxxxxxx-size\\": (_setup.size),
\\"xxxxxxxx-foo\\": (_vm.foo)
}))
const color = 'red'
const size = ref('10px')
return { color, size, ref }
}
}"
`;
exports[`CSS vars injection > w/ normal <script> binding analysis 1`] = `
"
const __default__ = {
setup() {
return {
size: ref('100px')
}
}
}
import { useCssVars as _useCssVars } from 'vue'
const __injectCSSVars__ = () => {
_useCssVars((_vm, _setup) => ({
\\"xxxxxxxx-size\\": (_vm.size)
}))}
const __setup__ = __default__.setup
__default__.setup = __setup__
? (props, ctx) => { __injectCSSVars__();return __setup__(props, ctx) }
: __injectCSSVars__
export default __default__"
`;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,203 @@
import { parse } from '../src/parse'
import { compileStyle, compileStyleAsync } from '../src/compileStyle'
test('preprocess less', () => {
const style = parse({
source:
'<style lang="less">\n' +
'@red: rgb(255, 0, 0);\n' +
'.color { color: @red; }\n' +
'</style>\n',
filename: 'example.vue',
sourceMap: true
}).styles[0]
const result = compileStyle({
id: 'v-scope-xxx',
filename: 'example.vue',
source: style.content,
map: style.map,
scoped: false,
preprocessLang: style.lang
})
expect(result.errors.length).toBe(0)
expect(result.code).toEqual(expect.stringContaining('color: #ff0000;'))
expect(result.map).toBeTruthy()
})
test('preprocess scss', () => {
const style = parse({
source:
'<style lang="scss">\n' +
'$red: red;\n' +
'.color { color: $red; }\n' +
'</style>\n',
filename: 'example.vue',
sourceMap: true
}).styles[0]
const result = compileStyle({
id: 'v-scope-xxx',
filename: 'example.vue',
source: style.content,
map: style.map,
scoped: false,
preprocessLang: style.lang
})
expect(result.errors.length).toBe(0)
expect(result.code).toMatch('color: red;')
expect(result.map).toBeTruthy()
})
test('preprocess sass', () => {
const style = parse({
source:
'<style lang="sass">\n' +
'$red: red\n' +
'.color\n' +
' color: $red\n' +
'</style>\n',
filename: 'example.vue',
sourceMap: true
}).styles[0]
const result = compileStyle({
id: 'v-scope-xxx',
filename: 'example.vue',
source: style.content,
map: style.map,
scoped: false,
preprocessLang: style.lang
})
expect(result.errors.length).toBe(0)
expect(result.code).toMatch('color: red;')
expect(result.map).toBeTruthy()
})
test('preprocess stylus', () => {
const style = parse({
source:
'<style lang="styl">\n' +
'red-color = rgb(255, 0, 0);\n' +
'.color\n' +
' color: red-color\n' +
'</style>\n',
filename: 'example.vue',
sourceMap: true
}).styles[0]
const result = compileStyle({
id: 'v-scope-xxx',
filename: 'example.vue',
source: style.content,
map: style.map,
scoped: false,
preprocessLang: style.lang
})
expect(result.errors.length).toBe(0)
expect(result.code).toEqual(expect.stringContaining('color: #f00;'))
expect(result.map).toBeTruthy()
})
test('custom postcss plugin', () => {
const spy = vi.fn()
compileStyle({
id: 'v-scope-xxx',
filename: 'example.vue',
source: '.foo { color: red }',
scoped: false,
postcssPlugins: [require('postcss').plugin('test-plugin', () => spy)()]
})
expect(spy).toHaveBeenCalled()
})
test('custom postcss options', () => {
const result = compileStyle({
id: 'v-scope-xxx',
filename: 'example.vue',
source: '.foo { color: red }',
scoped: false,
postcssOptions: { random: 'foo' }
})
expect((result.rawResult as any).opts.random).toBe('foo')
})
test('async postcss plugin in sync mode', () => {
const result = compileStyle({
id: 'v-scope-xxx',
filename: 'example.vue',
source: '.foo { color: red }',
scoped: false,
postcssPlugins: [
require('postcss').plugin(
'test-plugin',
() => async (result: any) => result
)
]
})
expect(result.errors).toHaveLength(1)
})
test('async postcss plugin', async () => {
const promise = compileStyleAsync({
id: 'v-scope-xxx',
filename: 'example.vue',
source: '.foo { color: red }',
scoped: false,
postcssPlugins: [
require('postcss').plugin(
'test-plugin',
() => async (result: any) => result
)
]
})
expect(promise instanceof Promise).toBe(true)
const result = await promise
expect(result.errors).toHaveLength(0)
expect(result.code).toEqual(expect.stringContaining('color: red'))
})
test('media query', () => {
const result = compileStyle({
id: 'v-scope-xxx',
scoped: true,
filename: 'example.vue',
source: `
@media print {
.foo {
color: #000;
}
}`
})
expect(result.errors).toHaveLength(0)
expect(result.code).toContain(
'@media print {\n.foo[v-scope-xxx] {\n color: #000;\n}\n}'
)
})
test('supports query', () => {
const result = compileStyle({
id: 'v-scope-xxx',
scoped: true,
filename: 'example.vue',
source: `
@supports ( color: #000 ) {
.foo {
color: #000;
}
}`
})
expect(result.errors).toHaveLength(0)
expect(result.code).toContain(
'@supports ( color: #000 ) {\n.foo[v-scope-xxx] {\n color: #000;\n}\n}'
)
})

View File

@ -0,0 +1,258 @@
import { parse } from '../src/parse'
import { SFCBlock } from '../src/parseComponent'
import { compileTemplate } from '../src/compileTemplate'
import Vue from 'vue'
function mockRender(code: string, mocks: Record<string, any> = {}) {
const fn = new Function(
`require`,
`${code}; return { render, staticRenderFns }`
)
const vm = new Vue(
Object.assign(
{},
fn((id: string) => mocks[id])
)
)
vm.$mount()
return (vm as any)._vnode
}
test('should work', () => {
const source = `<div><p>{{ render }}</p></div>`
const result = compileTemplate({
filename: 'example.vue',
source
})
expect(result.errors.length).toBe(0)
expect(result.source).toBe(source)
// should expose render fns
expect(result.code).toMatch(`var render = function`)
expect(result.code).toMatch(`var staticRenderFns = []`)
// should mark with stripped
expect(result.code).toMatch(`render._withStripped = true`)
// should prefix bindings
expect(result.code).toMatch(`_vm.render`)
expect(result.ast).not.toBeUndefined()
})
test('preprocess pug', () => {
const template = parse({
source:
'<template lang="pug">\n' +
'body\n' +
' h1 Pug Examples\n' +
' div.container\n' +
' p Cool Pug example!\n' +
'</template>\n',
filename: 'example.vue',
sourceMap: true
}).template as SFCBlock
const result = compileTemplate({
filename: 'example.vue',
source: template.content,
preprocessLang: template.lang
})
expect(result.errors.length).toBe(0)
})
/**
* vuejs/component-compiler-utils#22 Support uri fragment in transformed require
*/
test('supports uri fragment in transformed require', () => {
const source = '<svg>\
<use href="~@svg/file.svg#fragment"></use>\
</svg>' //
const result = compileTemplate({
filename: 'svgparticle.html',
source: source,
transformAssetUrls: {
use: 'href'
}
})
expect(result.errors.length).toBe(0)
expect(result.code).toMatch(
/href: require\("@svg\/file.svg"\) \+ "#fragment"/
)
})
/**
* vuejs/component-compiler-utils#22 Support uri fragment in transformed require
*/
test('when too short uri then empty require', () => {
const source = '<svg>\
<use href="~"></use>\
</svg>' //
const result = compileTemplate({
filename: 'svgparticle.html',
source: source,
transformAssetUrls: {
use: 'href'
}
})
expect(result.errors.length).toBe(0)
expect(result.code).toMatch(/href: require\(""\)/)
})
test('warn missing preprocessor', () => {
const template = parse({
source: '<template lang="unknownLang">\n' + '</template>\n',
filename: 'example.vue',
sourceMap: true
}).template as SFCBlock
const result = compileTemplate({
filename: 'example.vue',
source: template.content,
preprocessLang: template.lang
})
expect(result.errors.length).toBe(1)
})
test('transform assetUrls', () => {
const source = `
<div>
<img src="./logo.png">
<img src="~fixtures/logo.png">
<img src="~/fixtures/logo.png">
</div>
`
const result = compileTemplate({
filename: 'example.vue',
source,
transformAssetUrls: true
})
expect(result.errors.length).toBe(0)
const vnode = mockRender(result.code, {
'./logo.png': 'a',
'fixtures/logo.png': 'b'
})
expect(vnode.children[0].data.attrs.src).toBe('a')
expect(vnode.children[2].data.attrs.src).toBe('b')
expect(vnode.children[4].data.attrs.src).toBe('b')
})
test('transform srcset', () => {
const source = `
<div>
<img src="./logo.png">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink= "http://www.w3.org/1999/xlink">
<image xlink:href="./logo.png" />
</svg>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink= "http://www.w3.org/1999/xlink">
<use xlink:href="./logo.png"/>
</svg>
</svg>
<img src="./logo.png" srcset="./logo.png">
<img src="./logo.png" srcset="./logo.png 2x">
<img src="./logo.png" srcset="./logo.png, ./logo.png 2x">
<img src="./logo.png" srcset="./logo.png 2x, ./logo.png">
<img src="./logo.png" srcset="./logo.png 2x, ./logo.png 3x">
<img src="./logo.png" srcset="./logo.png, ./logo.png 2x, ./logo.png 3x">
<img
src="./logo.png"
srcset="
./logo.png 2x,
./logo.png 3x
">
</div>
`
const result = compileTemplate({
filename: 'example.vue',
source,
transformAssetUrls: true
})
expect(result.errors.length).toBe(0)
const vnode = mockRender(result.code, {
'./logo.png': 'test-url'
})
// img tag
expect(vnode.children[0].data.attrs.src).toBe('test-url')
// image tag (SVG)
expect(vnode.children[2].children[0].data.attrs['xlink:href']).toBe(
'test-url'
)
// use tag (SVG)
expect(vnode.children[4].children[0].data.attrs['xlink:href']).toBe(
'test-url'
)
// image tag with srcset
expect(vnode.children[6].data.attrs.srcset).toBe('test-url')
expect(vnode.children[8].data.attrs.srcset).toBe('test-url 2x')
// image tag with multiline srcset
expect(vnode.children[10].data.attrs.srcset).toBe('test-url, test-url 2x')
expect(vnode.children[12].data.attrs.srcset).toBe('test-url 2x, test-url')
expect(vnode.children[14].data.attrs.srcset).toBe('test-url 2x, test-url 3x')
expect(vnode.children[16].data.attrs.srcset).toBe(
'test-url, test-url 2x, test-url 3x'
)
expect(vnode.children[18].data.attrs.srcset).toBe('test-url 2x, test-url 3x')
})
test('transform assetUrls and srcset with base option', () => {
const source = `
<div>
<img src="./logo.png">
<img src="~fixtures/logo.png">
<img src="~/fixtures/logo.png">
<img src="./logo.png" srcset="./logo.png 2x, ./logo.png 3x">
<img src="@/fixtures/logo.png">
</div>
`
const result = compileTemplate({
filename: 'example.vue',
source,
transformAssetUrls: true,
transformAssetUrlsOptions: { base: '/base/' }
})
expect(result.errors.length).toBe(0)
const vnode = mockRender(result.code, {
'@/fixtures/logo.png': 'aliased'
})
expect(vnode.children[0].data.attrs.src).toBe('/base/logo.png')
expect(vnode.children[2].data.attrs.src).toBe('/base/fixtures/logo.png')
expect(vnode.children[4].data.attrs.src).toBe('/base/fixtures/logo.png')
expect(vnode.children[6].data.attrs.srcset).toBe(
'/base/logo.png 2x, /base/logo.png 3x'
)
expect(vnode.children[8].data.attrs.src).toBe('aliased')
})
test('transform with includeAbsolute', () => {
const source = `
<div>
<img src="./logo.png">
<img src="/logo.png">
<img src="https://foo.com/logo.png">
</div>
`
const result = compileTemplate({
filename: 'example.vue',
source,
transformAssetUrls: true,
transformAssetUrlsOptions: { includeAbsolute: true }
})
expect(result.errors.length).toBe(0)
const vnode = mockRender(result.code, {
'./logo.png': 'relative',
'/logo.png': 'absolute'
})
expect(vnode.children[0].data.attrs.src).toBe('relative')
expect(vnode.children[2].data.attrs.src).toBe('absolute')
expect(vnode.children[4].data.attrs.src).toBe('https://foo.com/logo.png')
})

View File

@ -0,0 +1,247 @@
import { compileStyle, parse } from '../src'
import { mockId, compile, assertCode } from './util'
describe('CSS vars injection', () => {
test('generating correct code for nested paths', () => {
const { content } = compile(
`<script>const a = 1</script>\n` +
`<style>div{
color: v-bind(color);
font-size: v-bind('font.size');
}</style>`
)
expect(content).toMatch(`_useCssVars((_vm, _setup) => ({
"${mockId}-color": (_vm.color),
"${mockId}-font_size": (_vm.font.size)
})`)
assertCode(content)
})
test('w/ normal <script> binding analysis', () => {
const { content } = compile(
`<script>
export default {
setup() {
return {
size: ref('100px')
}
}
}
</script>\n` +
`<style>
div {
font-size: v-bind(size);
}
</style>`
)
expect(content).toMatch(`_useCssVars((_vm, _setup) => ({
"${mockId}-size": (_vm.size)
})`)
expect(content).toMatch(`import { useCssVars as _useCssVars } from 'vue'`)
assertCode(content)
})
test('w/ <script setup> binding analysis', () => {
const { content } = compile(
`<script setup>
import { defineProps, ref } from 'vue'
const color = 'red'
const size = ref('10px')
defineProps({
foo: String
})
</script>\n` +
`<style>
div {
color: v-bind(color);
font-size: v-bind(size);
border: v-bind(foo);
}
</style>`
)
// should handle:
// 1. local const bindings
// 2. local potential ref bindings
// 3. props bindings (analyzed)
expect(content).toMatch(`_useCssVars((_vm, _setup) => ({
"${mockId}-color": (_setup.color),
"${mockId}-size": (_setup.size),
"${mockId}-foo": (_vm.foo)
})`)
expect(content).toMatch(`import { useCssVars as _useCssVars } from 'vue'`)
assertCode(content)
})
test('should rewrite CSS vars in compileStyle', () => {
const { code } = compileStyle({
source: `.foo {
color: v-bind(color);
font-size: v-bind('font.size');
}`,
filename: 'test.css',
id: 'data-v-test'
})
expect(code).toMatchInlineSnapshot(`
".foo[data-v-test] {
color: var(--test-color);
font-size: var(--test-font_size);
}"
`)
})
test('prod mode', () => {
const { content } = compile(
`<script>const a = 1</script>\n` +
`<style>div{
color: v-bind(color);
font-size: v-bind('font.size');
}</style>`,
{ isProd: true }
)
expect(content).toMatch(`_useCssVars((_vm, _setup) => ({
"4003f1a6": (_vm.color),
"41b6490a": (_vm.font.size)
}))}`)
const { code } = compileStyle({
source: `.foo {
color: v-bind(color);
font-size: v-bind('font.size');
}`,
filename: 'test.css',
id: mockId,
isProd: true
})
expect(code).toMatchInlineSnapshot(`
".foo[xxxxxxxx] {
color: var(--4003f1a6);
font-size: var(--41b6490a);
}"
`)
})
describe('codegen', () => {
test('<script> w/ no default export', () => {
assertCode(
compile(
`<script>const a = 1</script>\n` +
`<style>div{ color: v-bind(color); }</style>`
).content
)
})
test('<script> w/ default export', () => {
assertCode(
compile(
`<script>export default { setup() {} }</script>\n` +
`<style>div{ color: v-bind(color); }</style>`
).content
)
})
test('<script> w/ default export in strings/comments', () => {
assertCode(
compile(
`<script>
// export default {}
export default {}
</script>\n` + `<style>div{ color: v-bind(color); }</style>`
).content
)
})
test('w/ <script setup>', () => {
assertCode(
compile(
`<script setup>const color = 'red'</script>\n` +
`<style>div{ color: v-bind(color); }</style>`
).content
)
})
//#4185
test('should ignore comments', () => {
const { content } = compile(
`<script setup>const color = 'red';const width = 100</script>\n` +
`<style>
/* comment **/
div{ /* color: v-bind(color); */ width:20; }
div{ width: v-bind(width); }
/* comment */
</style>`
)
expect(content).not.toMatch(`"${mockId}-color": (_setup.color)`)
expect(content).toMatch(`"${mockId}-width": (_setup.width)`)
assertCode(content)
})
test('w/ <script setup> using the same var multiple times', () => {
const { content } = compile(
`<script setup>
const color = 'red'
</script>\n` +
`<style>
div {
color: v-bind(color);
}
p {
color: v-bind(color);
}
</style>`
)
// color should only be injected once, even if it is twice in style
expect(content).toMatch(`_useCssVars((_vm, _setup) => ({
"${mockId}-color": (_setup.color)
})`)
assertCode(content)
})
test('should work with w/ complex expression', () => {
const { content } = compile(
`<script setup>
let a = 100
let b = 200
let foo = 300
</script>\n` +
`<style>
p{
width: calc(v-bind(foo) - 3px);
height: calc(v-bind('foo') - 3px);
top: calc(v-bind(foo + 'px') - 3px);
}
div {
color: v-bind((a + b) / 2 + 'px' );
}
div {
color: v-bind ((a + b) / 2 + 'px' );
}
p {
color: v-bind(((a + b)) / (2 * a));
}
</style>`
)
expect(content).toMatch(`_useCssVars((_vm, _setup) => ({
"${mockId}-foo": (_setup.foo),
"${mockId}-foo____px_": (_setup.foo + 'px'),
"${mockId}-_a___b____2____px_": ((_setup.a + _setup.b) / 2 + 'px'),
"${mockId}-__a___b______2___a_": (((_setup.a + _setup.b)) / (2 * _setup.a))
})`)
assertCode(content)
})
// #6022
test('should be able to parse incomplete expressions', () => {
const { cssVars } = parse({
source: `<script setup>let xxx = 1</script>
<style scoped>
label {
font-weight: v-bind("count.toString(");
font-weight: v-bind(xxx);
}
</style>`
})
expect(cssVars).toMatchObject([`count.toString(`, `xxx`])
})
})
})

View File

@ -0,0 +1,269 @@
import { WarningMessage } from 'types/compiler'
import { parseComponent } from '../src/parseComponent'
describe('Single File Component parser', () => {
it('should parse', () => {
const res = parseComponent(
`
<template>
<div>hi</div>
</template>
<style src="./test.css"></style>
<style lang="stylus" scoped>
h1
color red
h2
color green
</style>
<style module>
h1 { font-weight: bold }
</style>
<style bool-attr val-attr="test"></style>
<script>
export default {}
</script>
<div>
<style>nested should be ignored</style>
</div>
`
)
expect(res.template!.content.trim()).toBe('<div>hi</div>')
expect(res.styles.length).toBe(4)
expect(res.styles[0].src).toBe('./test.css')
expect(res.styles[1].lang).toBe('stylus')
expect(res.styles[1].scoped).toBe(true)
expect(res.styles[1].content.trim()).toBe(
'h1\n color red\nh2\n color green'
)
expect(res.styles[2].module).toBe(true)
expect(res.styles[3].attrs['bool-attr']).toBe(true)
expect(res.styles[3].attrs['val-attr']).toBe('test')
expect(res.script!.content.trim()).toBe('export default {}')
})
it('should parse template with closed input', () => {
const res = parseComponent(`
<template>
<input type="text"/>
</template>
`)
expect(res.template!.content.trim()).toBe('<input type="text"/>')
})
it('should handle nested template', () => {
const res = parseComponent(`
<template>
<div><template v-if="ok">hi</template></div>
</template>
`)
expect(res.template!.content.trim()).toBe(
'<div><template v-if="ok">hi</template></div>'
)
})
it('deindent content', () => {
const content = `
<template>
<div></div>
</template>
<script>
export default {}
</script>
<style>
h1 { color: red }
</style>
`
const deindentDefault = parseComponent(content.trim(), {
pad: false
})
const deindentEnabled = parseComponent(content.trim(), {
pad: false,
deindent: true
})
const deindentDisabled = parseComponent(content.trim(), {
pad: false,
deindent: false
})
expect(deindentDefault.template!.content).toBe('\n<div></div>\n')
expect(deindentDefault.script!.content).toBe(
'\n export default {}\n '
)
expect(deindentDefault.styles[0].content).toBe('\nh1 { color: red }\n')
expect(deindentEnabled.template!.content).toBe('\n<div></div>\n')
expect(deindentEnabled.script!.content).toBe('\nexport default {}\n')
expect(deindentEnabled.styles[0].content).toBe('\nh1 { color: red }\n')
expect(deindentDisabled.template!.content).toBe(
'\n <div></div>\n '
)
expect(deindentDisabled.script!.content).toBe(
'\n export default {}\n '
)
expect(deindentDisabled.styles[0].content).toBe(
'\n h1 { color: red }\n '
)
})
it('pad content', () => {
const content = `
<template>
<div></div>
</template>
<script>
export default {}
</script>
<style>
h1 { color: red }
</style>
`
const padDefault = parseComponent(content.trim(), {
pad: true,
deindent: true
})
const padLine = parseComponent(content.trim(), {
pad: 'line',
deindent: true
})
const padSpace = parseComponent(content.trim(), {
pad: 'space',
deindent: true
})
expect(padDefault.script!.content).toBe(
Array(3 + 1).join('//\n') + '\nexport default {}\n'
)
expect(padDefault.styles[0].content).toBe(
Array(6 + 1).join('\n') + '\nh1 { color: red }\n'
)
expect(padLine.script!.content).toBe(
Array(3 + 1).join('//\n') + '\nexport default {}\n'
)
expect(padLine.styles[0].content).toBe(
Array(6 + 1).join('\n') + '\nh1 { color: red }\n'
)
expect(padSpace.script!.content).toBe(
`<template>
<div></div>
</template>
<script>`.replace(/./g, ' ') + '\nexport default {}\n'
)
expect(padSpace.styles[0].content).toBe(
`<template>
<div></div>
</template>
<script>
export default {}
</script>
<style>`.replace(/./g, ' ') + '\nh1 { color: red }\n'
)
})
it('should handle template blocks with lang as special text', () => {
const res = parseComponent(
`
<template lang="pug">
div
h1(v-if='1 < 2') hello
</template>
`,
{ deindent: true }
)
expect(res.template!.content.trim()).toBe(`div\n h1(v-if='1 < 2') hello`)
})
it('should handle component contains "<" only', () => {
const res = parseComponent(`
<template>
<span><</span>
</template>
`)
expect(res.template!.content.trim()).toBe(`<span><</span>`)
})
it('should handle custom blocks without parsing them', () => {
const res = parseComponent(
`
<template>
<div></div>
</template>
<example name="simple">
<my-button ref="button">Hello</my-button>
</example>
<example name="with props">
<my-button color="red">Hello</my-button>
</example>
<test name="simple" foo="bar">
export default function simple (vm) {
describe('Hello', () => {
it('should display Hello', () => {
this.vm.$refs.button.$el.innerText.should.equal('Hello')
}))
}))
}
</test>
<custom src="./x.json"></custom>
`
)
expect(res.customBlocks.length).toBe(4)
const simpleExample = res.customBlocks[0]
expect(simpleExample.type).toBe('example')
expect(simpleExample.content.trim()).toBe(
'<my-button ref="button">Hello</my-button>'
)
expect(simpleExample.attrs.name).toBe('simple')
const withProps = res.customBlocks[1]
expect(withProps.type).toBe('example')
expect(withProps.content.trim()).toBe(
'<my-button color="red">Hello</my-button>'
)
expect(withProps.attrs.name).toBe('with props')
const simpleTest = res.customBlocks[2]
expect(simpleTest.type).toBe('test')
expect(simpleTest.content.trim())
.toBe(`export default function simple (vm) {
describe('Hello', () => {
it('should display Hello', () => {
this.vm.$refs.button.$el.innerText.should.equal('Hello')
}))
}))
}`)
expect(simpleTest.attrs.name).toBe('simple')
expect(simpleTest.attrs.foo).toBe('bar')
const customWithSrc = res.customBlocks[3]
expect(customWithSrc.src).toBe('./x.json')
})
// Regression #4289
it('accepts nested template tag', () => {
const raw = `<div>
<template v-if="true === true">
<section class="section">
<div class="container">
Should be shown
</div>
</section>
</template>
<template v-else>
<p>Should not be shown</p>
</template>
</div>`
const res = parseComponent(`<template>${raw}</template>`)
expect(res.template!.content.trim()).toBe(raw)
})
it('should not hang on trailing text', () => {
const res = parseComponent(`<template>hi</`)
expect(res.template!.content).toBe('hi')
})
it('should collect errors with source range', () => {
const res = parseComponent(`<template>hi</`, { outputSourceRange: true })
expect(res.errors.length).toBe(1)
expect((res.errors[0] as WarningMessage).start).toBe(0)
})
})

View File

@ -0,0 +1,97 @@
import { prefixIdentifiers } from '../src/prefixIdentifiers'
import { compile } from 'web/entry-compiler'
import { format } from 'prettier'
import { BindingTypes } from '../src/types'
const toFn = (source: string) => `function render(){${source}\n}`
it('should work', () => {
const { render } = compile(`<div id="app">
<div :style="{ color }">{{ foo }}</div>
<p v-for="i in list">{{ i }}</p>
<foo inline-template>
<div>{{ bar }}</div>
</foo>
</div>`)
const result = format(prefixIdentifiers(toFn(render)), {
semi: false,
parser: 'babel'
})
expect(result).not.toMatch(`_vm._c`)
expect(result).toMatch(`_vm.foo`)
expect(result).toMatch(`_vm.list`)
expect(result).toMatch(`{ color: _vm.color }`)
expect(result).not.toMatch(`_vm.i`)
expect(result).not.toMatch(`with (this)`)
expect(result).toMatchInlineSnapshot(`
"function render() {
var _vm = this,
_c = _vm._self._c
return _c(
\\"div\\",
{ attrs: { id: \\"app\\" } },
[
_c(\\"div\\", { style: { color: _vm.color } }, [_vm._v(_vm._s(_vm.foo))]),
_vm._v(\\" \\"),
_vm._l(_vm.list, function (i) {
return _c(\\"p\\", [_vm._v(_vm._s(i))])
}),
_vm._v(\\" \\"),
_c(\\"foo\\", {
inlineTemplate: {
render: function () {
var _vm = this,
_c = _vm._self._c
return _c(\\"div\\", [_vm._v(_vm._s(_vm.bar))])
},
staticRenderFns: [],
},
}),
],
2
)
}
"
`)
})
it('setup bindings', () => {
const { render } = compile(`<div @click="count++">{{ count }}</div>`)
const result = format(
prefixIdentifiers(toFn(render), false, false, undefined, {
count: BindingTypes.SETUP_REF
}),
{
semi: false,
parser: 'babel'
}
)
expect(result).toMatch(`_setup = _vm._self._setupProxy`)
expect(result).toMatch(`_setup.count++`)
expect(result).toMatch(`_vm._s(_setup.count)`)
expect(result).toMatchInlineSnapshot(`
"function render() {
var _vm = this,
_c = _vm._self._c,
_setup = _vm._self._setupProxy
return _c(
\\"div\\",
{
on: {
click: function (\$event) {
_setup.count++
},
},
},
[_vm._v(_vm._s(_setup.count))]
)
}
"
`)
})

View File

@ -0,0 +1,245 @@
import { rewriteDefault } from '../src'
describe('compiler sfc: rewriteDefault', () => {
test('without export default', () => {
expect(rewriteDefault(`export a = {}`, 'script')).toMatchInlineSnapshot(`
"export a = {}
const script = {}"
`)
})
test('rewrite export default', () => {
expect(
rewriteDefault(`export default {}`, 'script')
).toMatchInlineSnapshot(`"const script = {}"`)
})
test('rewrite export named default', () => {
expect(
rewriteDefault(
`const a = 1 \n export { a as b, a as default, a as c}`,
'script'
)
).toMatchInlineSnapshot(`
"const a = 1
export { a as b, a as c}
const script = a"
`)
expect(
rewriteDefault(
`const a = 1 \n export { a as b, a as default , a as c}`,
'script'
)
).toMatchInlineSnapshot(`
"const a = 1
export { a as b, a as c}
const script = a"
`)
})
test('w/ comments', async () => {
expect(rewriteDefault(`// export default\nexport default {}`, 'script'))
.toMatchInlineSnapshot(`
"// export default
const script = {}"
`)
})
test('export named default multiline', () => {
expect(
rewriteDefault(`let App = {}\n export {\nApp as default\n}`, '_sfc_main')
).toMatchInlineSnapshot(`
"let App = {}
export {
}
const _sfc_main = App"
`)
})
test('export named default multiline /w comments', () => {
expect(
rewriteDefault(
`const a = 1 \n export {\n a as b,\n a as default,\n a as c}\n` +
`// export { myFunction as default }`,
'script'
)
).toMatchInlineSnapshot(`
"const a = 1
export {
a as b,
a as c}
// export { myFunction as default }
const script = a"
`)
expect(
rewriteDefault(
`const a = 1 \n export {\n a as b,\n a as default ,\n a as c}\n` +
`// export { myFunction as default }`,
'script'
)
).toMatchInlineSnapshot(`
"const a = 1
export {
a as b,
a as c}
// export { myFunction as default }
const script = a"
`)
})
test(`export { default } from '...'`, async () => {
expect(
rewriteDefault(`export { default, foo } from './index.js'`, 'script')
).toMatchInlineSnapshot(`
"import { default as __VUE_DEFAULT__ } from './index.js'
export { foo } from './index.js'
const script = __VUE_DEFAULT__"
`)
expect(
rewriteDefault(`export { default , foo } from './index.js'`, 'script')
).toMatchInlineSnapshot(`
"import { default as __VUE_DEFAULT__ } from './index.js'
export { foo } from './index.js'
const script = __VUE_DEFAULT__"
`)
expect(
rewriteDefault(`export { foo, default } from './index.js'`, 'script')
).toMatchInlineSnapshot(`
"import { default as __VUE_DEFAULT__ } from './index.js'
export { foo, } from './index.js'
const script = __VUE_DEFAULT__"
`)
expect(
rewriteDefault(
`export { foo as default, bar } from './index.js'`,
'script'
)
).toMatchInlineSnapshot(`
"import { foo } from './index.js'
export { bar } from './index.js'
const script = foo"
`)
expect(
rewriteDefault(
`export { foo as default , bar } from './index.js'`,
'script'
)
).toMatchInlineSnapshot(`
"import { foo } from './index.js'
export { bar } from './index.js'
const script = foo"
`)
expect(
rewriteDefault(
`export { bar, foo as default } from './index.js'`,
'script'
)
).toMatchInlineSnapshot(`
"import { foo } from './index.js'
export { bar, } from './index.js'
const script = foo"
`)
})
test('export default class', async () => {
expect(rewriteDefault(`export default class Foo {}`, 'script'))
.toMatchInlineSnapshot(`
"class Foo {}
const script = Foo"
`)
})
test('export default class w/ comments', async () => {
expect(
rewriteDefault(`// export default\nexport default class Foo {}`, 'script')
).toMatchInlineSnapshot(`
"// export default
class Foo {}
const script = Foo"
`)
})
test('export default class w/ comments 2', async () => {
expect(
rewriteDefault(
`export default {}\n` + `// export default class Foo {}`,
'script'
)
).toMatchInlineSnapshot(`
"const script = {}
// export default class Foo {}"
`)
})
test('export default class w/ comments 3', async () => {
expect(
rewriteDefault(
`/*\nexport default class Foo {}*/\n` + `export default class Bar {}`,
'script'
)
).toMatchInlineSnapshot(`
"/*
export default class Foo {}*/
class Bar {}
const script = Bar"
`)
})
test('@Component\nexport default class', async () => {
expect(rewriteDefault(`@Component\nexport default class Foo {}`, 'script'))
.toMatchInlineSnapshot(`
"@Component
class Foo {}
const script = Foo"
`)
})
test('@Component\nexport default class w/ comments', async () => {
expect(
rewriteDefault(`// export default\n@Component\nexport default class Foo {}`, 'script')
).toMatchInlineSnapshot(`
"// export default
@Component
class Foo {}
const script = Foo"
`)
})
test('@Component\nexport default class w/ comments 2', async () => {
expect(
rewriteDefault(
`export default {}\n` + `// @Component\n// export default class Foo {}`,
'script'
)
).toMatchInlineSnapshot(`
"const script = {}
// @Component
// export default class Foo {}"
`)
})
test('@Component\nexport default class w/ comments 3', async () => {
expect(
rewriteDefault(
`/*\n@Component\nexport default class Foo {}*/\n` + `export default class Bar {}`,
'script'
)
).toMatchInlineSnapshot(`
"/*
@Component
export default class Foo {}*/
class Bar {}
const script = Bar"
`)
})
})

View File

@ -0,0 +1,137 @@
import { compileStyle } from '../src/compileStyle'
// vue-loader/#1370
test('spaces after selector', () => {
const { code } = compileStyle({
source: `.foo , .bar { color: red; }`,
filename: 'test.css',
id: 'test'
})
expect(code).toMatch(`.foo[test], .bar[test] { color: red;`)
})
test('leading deep selector', () => {
const { code } = compileStyle({
source: `>>> .foo { color: red; }`,
filename: 'test.css',
id: 'test'
})
expect(code).toMatch(`[test] .foo { color: red;`)
})
test('scoped css', () => {
const { code: style } = compileStyle({
id: 'v-scope-xxx',
scoped: true,
filename: 'example.vue',
source: `
.test {
color: yellow;
}
.test:after {
content: 'bye!';
}
h1 {
color: green;
}
.anim {
animation: color 5s infinite, other 5s;
}
.anim-2 {
animation-name: color;
animation-duration: 5s;
}
.anim-3 {
animation: 5s color infinite, 5s other;
}
.anim-multiple {
animation: color 5s infinite, opacity 2s;
}
.anim-multiple-2 {
animation-name: color, opacity;
animation-duration: 5s, 2s;
}
@keyframes color {
from { color: red; }
to { color: green; }
}
@-webkit-keyframes color {
from { color: red; }
to { color: green; }
}
@keyframes opacity {
from { opacity: 0; }
to { opacity: 1; }
}
@-webkit-keyframes opacity {
from { opacity: 0; }
to { opacity: 1; }
}
.foo p >>> .bar {
color: red;
}
.foo div /deep/ .bar {
color: red;
}
.foo span ::v-deep .bar {
color: red;
}
`
})
expect(style).toContain(`.test[v-scope-xxx] {\n color: yellow;\n}`)
expect(style).toContain(`.test[v-scope-xxx]:after {\n content: \'bye!\';\n}`)
expect(style).toContain(`h1[v-scope-xxx] {\n color: green;\n}`)
// scoped keyframes
expect(style).toContain(
`.anim[v-scope-xxx] {\n animation: color-v-scope-xxx 5s infinite, other 5s;`
)
expect(style).toContain(
`.anim-2[v-scope-xxx] {\n animation-name: color-v-scope-xxx`
)
expect(style).toContain(
`.anim-3[v-scope-xxx] {\n animation: 5s color-v-scope-xxx infinite, 5s other;`
)
expect(style).toContain(`@keyframes color-v-scope-xxx {`)
expect(style).toContain(`@-webkit-keyframes color-v-scope-xxx {`)
expect(style).toContain(
`.anim-multiple[v-scope-xxx] {\n animation: color-v-scope-xxx 5s infinite,opacity-v-scope-xxx 2s;`
)
expect(style).toContain(
`.anim-multiple-2[v-scope-xxx] {\n animation-name: color-v-scope-xxx,opacity-v-scope-xxx;`
)
expect(style).toContain(`@keyframes opacity-v-scope-xxx {`)
expect(style).toContain(`@-webkit-keyframes opacity-v-scope-xxx {`)
// >>> combinator
expect(style).toContain(`.foo p[v-scope-xxx] .bar {\n color: red;\n}`)
// /deep/ alias for >>>
expect(style).toContain(`.foo div[v-scope-xxx] .bar {\n color: red;\n}`)
// ::-v-deep alias for >>>
expect(style).toContain(`.foo span[v-scope-xxx] .bar {\n color: red;\n}`)
})
test('pseudo element', () => {
const { code } = compileStyle({
source: '::selection { display: none; }',
filename: 'test.css',
id: 'test'
})
expect(code).toContain('[test]::selection {')
})
test('spaces before pseudo element', () => {
const { code } = compileStyle({
source: '.abc, ::selection { color: red; }',
filename: 'test.css',
id: 'test'
})
expect(code).toContain('.abc[test],')
expect(code).toContain('[test]::selection {')
})

View File

@ -0,0 +1,7 @@
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"types": ["node", "vitest/globals"]
},
"include": ["../src", "."]
}

35
node_modules/vue/packages/compiler-sfc/test/util.ts generated vendored Normal file
View File

@ -0,0 +1,35 @@
import {
parse,
compileScript,
type SFCParseOptions,
type SFCScriptCompileOptions
} from '../src'
import { parse as babelParse } from '@babel/parser'
export const mockId = 'xxxxxxxx'
export function compile(
source: string,
options?: Partial<SFCScriptCompileOptions>,
parseOptions?: Partial<SFCParseOptions>
) {
const sfc = parse({
...parseOptions,
source
})
return compileScript(sfc, { id: mockId, ...options })
}
export function assertCode(code: string) {
// parse the generated code to make sure it is valid
try {
babelParse(code, {
sourceType: 'module',
plugins: ['typescript']
})
} catch (e: any) {
console.log(code)
throw e
}
expect(code).toMatchSnapshot()
}