wip: tests for compileScriptSetup
This commit is contained in:
parent
e4df2d7749
commit
3e1cdba9db
@ -30,7 +30,6 @@ import {
|
|||||||
import { createCompilerError, ErrorCodes } from '../errors'
|
import { createCompilerError, ErrorCodes } from '../errors'
|
||||||
import { Node, Function, Identifier, ObjectProperty } from '@babel/types'
|
import { Node, Function, Identifier, ObjectProperty } from '@babel/types'
|
||||||
import { validateBrowserExpression } from '../validateExpression'
|
import { validateBrowserExpression } from '../validateExpression'
|
||||||
import { ParserPlugin } from '@babel/parser'
|
|
||||||
|
|
||||||
const isLiteralWhitelisted = /*#__PURE__*/ makeMap('true,false,null,this')
|
const isLiteralWhitelisted = /*#__PURE__*/ makeMap('true,false,null,this')
|
||||||
|
|
||||||
@ -130,10 +129,7 @@ export function processExpression(
|
|||||||
: `(${rawExp})${asParams ? `=>{}` : ``}`
|
: `(${rawExp})${asParams ? `=>{}` : ``}`
|
||||||
try {
|
try {
|
||||||
ast = parseJS(source, {
|
ast = parseJS(source, {
|
||||||
plugins: [
|
plugins: [...context.expressionPlugins, ...babelParserDefautPlugins]
|
||||||
...context.expressionPlugins,
|
|
||||||
...(babelParserDefautPlugins as ParserPlugin[])
|
|
||||||
]
|
|
||||||
}).program
|
}).program
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
context.onError(
|
context.onError(
|
||||||
|
@ -0,0 +1,248 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`SFC compile <script setup> <script setup lang="ts"> hoist type declarations 1`] = `
|
||||||
|
"import { defineComponent as __define__ } from 'vue'
|
||||||
|
import { Slots as __Slots__ } from 'vue'
|
||||||
|
export interface Foo {}
|
||||||
|
type Bar = {}
|
||||||
|
|
||||||
|
export function setup() {
|
||||||
|
|
||||||
|
const a = 1
|
||||||
|
|
||||||
|
return { a }
|
||||||
|
}
|
||||||
|
|
||||||
|
export default __define__({
|
||||||
|
setup
|
||||||
|
})"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`SFC compile <script setup> errors should allow export default referencing imported binding 1`] = `
|
||||||
|
"import { bar } from './bar'
|
||||||
|
|
||||||
|
export function setup() {
|
||||||
|
|
||||||
|
|
||||||
|
return { bar }
|
||||||
|
}
|
||||||
|
|
||||||
|
const __default__ = {
|
||||||
|
props: {
|
||||||
|
foo: {
|
||||||
|
default: () => bar
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
__default__.setup = setup
|
||||||
|
export default __default__"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`SFC compile <script setup> errors should allow export default referencing re-exported binding 1`] = `
|
||||||
|
"import { bar } from './bar'
|
||||||
|
|
||||||
|
export function setup() {
|
||||||
|
|
||||||
|
|
||||||
|
return { bar }
|
||||||
|
}
|
||||||
|
|
||||||
|
const __default__ = {
|
||||||
|
props: {
|
||||||
|
foo: {
|
||||||
|
default: () => bar
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
__default__.setup = setup
|
||||||
|
export default __default__"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`SFC compile <script setup> errors should allow export default referencing scope var 1`] = `
|
||||||
|
"export function setup() {
|
||||||
|
|
||||||
|
const bar = 1
|
||||||
|
|
||||||
|
return { }
|
||||||
|
}
|
||||||
|
|
||||||
|
const __default__ = {
|
||||||
|
props: {
|
||||||
|
foo: {
|
||||||
|
default: bar => bar + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
__default__.setup = setup
|
||||||
|
export default __default__"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`SFC compile <script setup> explicit setup signature 1`] = `
|
||||||
|
"export function setup(props, { emit }) {
|
||||||
|
emit('foo')
|
||||||
|
return { }
|
||||||
|
}
|
||||||
|
|
||||||
|
export default { setup }"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`SFC compile <script setup> exports export * from './x' 1`] = `
|
||||||
|
"import { toRefs as __toRefs__ } from 'vue'
|
||||||
|
import * as __export_all_0__ from './x'
|
||||||
|
|
||||||
|
export function setup() {
|
||||||
|
|
||||||
|
const y = 1
|
||||||
|
|
||||||
|
return Object.assign(
|
||||||
|
{ y },
|
||||||
|
__toRefs__(__export_all_0__)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default { setup }"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`SFC compile <script setup> exports export { x } 1`] = `
|
||||||
|
"export function setup() {
|
||||||
|
|
||||||
|
const x = 1
|
||||||
|
const y = 2
|
||||||
|
|
||||||
|
return { x, y }
|
||||||
|
}
|
||||||
|
|
||||||
|
export default { setup }"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`SFC compile <script setup> exports export { x } from './x' 1`] = `
|
||||||
|
"import { x, y } from './x'
|
||||||
|
|
||||||
|
export function setup() {
|
||||||
|
|
||||||
|
|
||||||
|
return { x, y }
|
||||||
|
}
|
||||||
|
|
||||||
|
export default { setup }"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`SFC compile <script setup> exports export { x as default } 1`] = `
|
||||||
|
"import x from './x'
|
||||||
|
|
||||||
|
export function setup() {
|
||||||
|
|
||||||
|
const y = 1
|
||||||
|
|
||||||
|
return { y }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const __default__ = x
|
||||||
|
__default__.setup = setup
|
||||||
|
export default __default__"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`SFC compile <script setup> exports export { x as default } from './x' 1`] = `
|
||||||
|
"import { x as __default__ } from './x'
|
||||||
|
import { y } from './x'
|
||||||
|
|
||||||
|
export function setup() {
|
||||||
|
|
||||||
|
|
||||||
|
return { y }
|
||||||
|
}
|
||||||
|
|
||||||
|
__default__.setup = setup
|
||||||
|
export default __default__"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`SFC compile <script setup> exports export class X() {} 1`] = `
|
||||||
|
"export function setup() {
|
||||||
|
class X {}
|
||||||
|
return { X }
|
||||||
|
}
|
||||||
|
|
||||||
|
export default { setup }"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`SFC compile <script setup> exports export const { x } = ... (destructuring) 1`] = `
|
||||||
|
"export function setup() {
|
||||||
|
|
||||||
|
const [a = 1, { b } = { b: 123 }, ...c] = useFoo()
|
||||||
|
const { d = 2, _: [e], ...f } = useBar()
|
||||||
|
|
||||||
|
return { a, b, c, d, e, f }
|
||||||
|
}
|
||||||
|
|
||||||
|
export default { setup }"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`SFC compile <script setup> exports export const x = ... 1`] = `
|
||||||
|
"export function setup() {
|
||||||
|
const x = 1
|
||||||
|
return { x }
|
||||||
|
}
|
||||||
|
|
||||||
|
export default { setup }"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`SFC compile <script setup> exports export default from './x' 1`] = `
|
||||||
|
"import __default__ from './x'
|
||||||
|
|
||||||
|
export function setup() {
|
||||||
|
|
||||||
|
|
||||||
|
return { }
|
||||||
|
}
|
||||||
|
|
||||||
|
__default__.setup = setup
|
||||||
|
export default __default__"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`SFC compile <script setup> exports export default in <script setup> 1`] = `
|
||||||
|
"export function setup() {
|
||||||
|
|
||||||
|
const y = 1
|
||||||
|
|
||||||
|
return { y }
|
||||||
|
}
|
||||||
|
|
||||||
|
const __default__ = {
|
||||||
|
props: ['foo']
|
||||||
|
}
|
||||||
|
__default__.setup = setup
|
||||||
|
export default __default__"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`SFC compile <script setup> exports export function x() {} 1`] = `
|
||||||
|
"export function setup() {
|
||||||
|
function x(){}
|
||||||
|
return { x }
|
||||||
|
}
|
||||||
|
|
||||||
|
export default { setup }"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`SFC compile <script setup> import dedupe between <script> and <script setup> 1`] = `
|
||||||
|
"import { x } from './x'
|
||||||
|
|
||||||
|
export function setup() {
|
||||||
|
|
||||||
|
x()
|
||||||
|
|
||||||
|
return { }
|
||||||
|
}
|
||||||
|
|
||||||
|
export default { setup }"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`SFC compile <script setup> should hoist imports 1`] = `
|
||||||
|
"import { ref } from 'vue'
|
||||||
|
export function setup() {
|
||||||
|
|
||||||
|
return { }
|
||||||
|
}
|
||||||
|
|
||||||
|
export default { setup }"
|
||||||
|
`;
|
366
packages/compiler-sfc/__tests__/compileScript.spec.ts
Normal file
366
packages/compiler-sfc/__tests__/compileScript.spec.ts
Normal file
@ -0,0 +1,366 @@
|
|||||||
|
import { parse, compileScriptSetup, SFCScriptCompileOptions } from '../src'
|
||||||
|
import { parse as babelParse } from '@babel/parser'
|
||||||
|
import { babelParserDefautPlugins } from '@vue/shared'
|
||||||
|
|
||||||
|
function compile(src: string, options?: SFCScriptCompileOptions) {
|
||||||
|
const { descriptor } = parse(src)
|
||||||
|
return compileScriptSetup(descriptor, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertCode(code: string) {
|
||||||
|
// parse the generated code to make sure it is valid
|
||||||
|
try {
|
||||||
|
babelParse(code, {
|
||||||
|
sourceType: 'module',
|
||||||
|
plugins: [...babelParserDefautPlugins, 'typescript']
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
console.log(code)
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
expect(code).toMatchSnapshot()
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('SFC compile <script setup>', () => {
|
||||||
|
test('should hoist imports', () => {
|
||||||
|
assertCode(compile(`<script setup>import { ref } from 'vue'</script>`).code)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('explicit setup signature', () => {
|
||||||
|
assertCode(
|
||||||
|
compile(`<script setup="props, { emit }">emit('foo')</script>`).code
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('import dedupe between <script> and <script setup>', () => {
|
||||||
|
const code = compile(`
|
||||||
|
<script>
|
||||||
|
import { x } from './x'
|
||||||
|
</script>
|
||||||
|
<script setup>
|
||||||
|
import { x } from './x'
|
||||||
|
x()
|
||||||
|
</script>
|
||||||
|
`).code
|
||||||
|
assertCode(code)
|
||||||
|
expect(code.indexOf(`import { x }`)).toEqual(
|
||||||
|
code.lastIndexOf(`import { x }`)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('exports', () => {
|
||||||
|
test('export const x = ...', () => {
|
||||||
|
const { code, bindings } = compile(
|
||||||
|
`<script setup>export const x = 1</script>`
|
||||||
|
)
|
||||||
|
assertCode(code)
|
||||||
|
expect(bindings).toStrictEqual({
|
||||||
|
x: 'setup'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('export const { x } = ... (destructuring)', () => {
|
||||||
|
const { code, bindings } = compile(`<script setup>
|
||||||
|
export const [a = 1, { b } = { b: 123 }, ...c] = useFoo()
|
||||||
|
export const { d = 2, _: [e], ...f } = useBar()
|
||||||
|
</script>`)
|
||||||
|
assertCode(code)
|
||||||
|
expect(bindings).toStrictEqual({
|
||||||
|
a: 'setup',
|
||||||
|
b: 'setup',
|
||||||
|
c: 'setup',
|
||||||
|
d: 'setup',
|
||||||
|
e: 'setup',
|
||||||
|
f: 'setup'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('export function x() {}', () => {
|
||||||
|
const { code, bindings } = compile(
|
||||||
|
`<script setup>export function x(){}</script>`
|
||||||
|
)
|
||||||
|
assertCode(code)
|
||||||
|
expect(bindings).toStrictEqual({
|
||||||
|
x: 'setup'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('export class X() {}', () => {
|
||||||
|
const { code, bindings } = compile(
|
||||||
|
`<script setup>export class X {}</script>`
|
||||||
|
)
|
||||||
|
assertCode(code)
|
||||||
|
expect(bindings).toStrictEqual({
|
||||||
|
X: 'setup'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('export { x }', () => {
|
||||||
|
const { code, bindings } = compile(
|
||||||
|
`<script setup>
|
||||||
|
const x = 1
|
||||||
|
const y = 2
|
||||||
|
export { x, y }
|
||||||
|
</script>`
|
||||||
|
)
|
||||||
|
assertCode(code)
|
||||||
|
expect(bindings).toStrictEqual({
|
||||||
|
x: 'setup',
|
||||||
|
y: 'setup'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`export { x } from './x'`, () => {
|
||||||
|
const { code, bindings } = compile(
|
||||||
|
`<script setup>
|
||||||
|
export { x, y } from './x'
|
||||||
|
</script>`
|
||||||
|
)
|
||||||
|
assertCode(code)
|
||||||
|
expect(bindings).toStrictEqual({
|
||||||
|
x: 'setup',
|
||||||
|
y: 'setup'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`export default from './x'`, () => {
|
||||||
|
const { code, bindings } = compile(
|
||||||
|
`<script setup>
|
||||||
|
export default from './x'
|
||||||
|
</script>`,
|
||||||
|
{
|
||||||
|
parserPlugins: ['exportDefaultFrom']
|
||||||
|
}
|
||||||
|
)
|
||||||
|
assertCode(code)
|
||||||
|
expect(bindings).toStrictEqual({})
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`export { x as default }`, () => {
|
||||||
|
const { code, bindings } = compile(
|
||||||
|
`<script setup>
|
||||||
|
import x from './x'
|
||||||
|
const y = 1
|
||||||
|
export { x as default, y }
|
||||||
|
</script>`
|
||||||
|
)
|
||||||
|
assertCode(code)
|
||||||
|
expect(bindings).toStrictEqual({
|
||||||
|
y: 'setup'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`export { x as default } from './x'`, () => {
|
||||||
|
const { code, bindings } = compile(
|
||||||
|
`<script setup>
|
||||||
|
export { x as default, y } from './x'
|
||||||
|
</script>`
|
||||||
|
)
|
||||||
|
assertCode(code)
|
||||||
|
expect(bindings).toStrictEqual({
|
||||||
|
y: 'setup'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`export * from './x'`, () => {
|
||||||
|
const { code, bindings } = compile(
|
||||||
|
`<script setup>
|
||||||
|
export * from './x'
|
||||||
|
export const y = 1
|
||||||
|
</script>`
|
||||||
|
)
|
||||||
|
assertCode(code)
|
||||||
|
expect(bindings).toStrictEqual({
|
||||||
|
y: 'setup'
|
||||||
|
// in this case we cannot extract bindings from ./x so it falls back
|
||||||
|
// to runtime proxy dispatching
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('export default in <script setup>', () => {
|
||||||
|
const { code, bindings } = compile(
|
||||||
|
`<script setup>
|
||||||
|
export default {
|
||||||
|
props: ['foo']
|
||||||
|
}
|
||||||
|
export const y = 1
|
||||||
|
</script>`
|
||||||
|
)
|
||||||
|
assertCode(code)
|
||||||
|
expect(bindings).toStrictEqual({
|
||||||
|
y: 'setup'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('<script setup lang="ts">', () => {
|
||||||
|
test('hoist type declarations', () => {
|
||||||
|
const { code, bindings } = compile(`
|
||||||
|
<script setup lang="ts">
|
||||||
|
export interface Foo {}
|
||||||
|
type Bar = {}
|
||||||
|
export const a = 1
|
||||||
|
</script>`)
|
||||||
|
assertCode(code)
|
||||||
|
expect(bindings).toStrictEqual({ a: 'setup' })
|
||||||
|
})
|
||||||
|
|
||||||
|
test('extract props', () => {})
|
||||||
|
|
||||||
|
test('extract emits', () => {})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('errors', () => {
|
||||||
|
test('must have <script setup>', () => {
|
||||||
|
expect(() => compile(`<script>foo()</script>`)).toThrow(
|
||||||
|
`SFC has no <script setup>`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('<script> and <script setup> must have same lang', () => {
|
||||||
|
expect(() =>
|
||||||
|
compile(`<script>foo()</script><script setup lang="ts">bar()</script>`)
|
||||||
|
).toThrow(`<script> and <script setup> must have the same language type`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('export local as default', () => {
|
||||||
|
expect(() =>
|
||||||
|
compile(`<script setup>
|
||||||
|
const bar = 1
|
||||||
|
export { bar as default }
|
||||||
|
</script>`)
|
||||||
|
).toThrow(`Cannot export locally defined variable as default`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('export default referencing local var', () => {
|
||||||
|
expect(() =>
|
||||||
|
compile(`<script setup>
|
||||||
|
const bar = 1
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
foo: {
|
||||||
|
default: () => bar
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>`)
|
||||||
|
).toThrow(`cannot reference locally declared variables`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('export default referencing exports', () => {
|
||||||
|
expect(() =>
|
||||||
|
compile(`<script setup>
|
||||||
|
export const bar = 1
|
||||||
|
export default {
|
||||||
|
props: bar
|
||||||
|
}
|
||||||
|
</script>`)
|
||||||
|
).toThrow(`cannot reference locally declared variables`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should allow export default referencing scope var', () => {
|
||||||
|
assertCode(
|
||||||
|
compile(`<script setup>
|
||||||
|
const bar = 1
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
foo: {
|
||||||
|
default: bar => bar + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>`).code
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should allow export default referencing imported binding', () => {
|
||||||
|
assertCode(
|
||||||
|
compile(`<script setup>
|
||||||
|
import { bar } from './bar'
|
||||||
|
export { bar }
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
foo: {
|
||||||
|
default: () => bar
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>`).code
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should allow export default referencing re-exported binding', () => {
|
||||||
|
assertCode(
|
||||||
|
compile(`<script setup>
|
||||||
|
export { bar } from './bar'
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
foo: {
|
||||||
|
default: () => bar
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>`).code
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('error on duplicated defalut export', () => {
|
||||||
|
expect(() =>
|
||||||
|
compile(`
|
||||||
|
<script>
|
||||||
|
export default {}
|
||||||
|
</script>
|
||||||
|
<script setup>
|
||||||
|
export default {}
|
||||||
|
</script>
|
||||||
|
`)
|
||||||
|
).toThrow(`Default export is already declared`)
|
||||||
|
|
||||||
|
expect(() =>
|
||||||
|
compile(`
|
||||||
|
<script>
|
||||||
|
export default {}
|
||||||
|
</script>
|
||||||
|
<script setup>
|
||||||
|
const x = {}
|
||||||
|
export { x as default }
|
||||||
|
</script>
|
||||||
|
`)
|
||||||
|
).toThrow(`Default export is already declared`)
|
||||||
|
|
||||||
|
expect(() =>
|
||||||
|
compile(`
|
||||||
|
<script>
|
||||||
|
export default {}
|
||||||
|
</script>
|
||||||
|
<script setup>
|
||||||
|
export { x as default } from './y'
|
||||||
|
</script>
|
||||||
|
`)
|
||||||
|
).toThrow(`Default export is already declared`)
|
||||||
|
|
||||||
|
expect(() =>
|
||||||
|
compile(`
|
||||||
|
<script>
|
||||||
|
export { x as default } from './y'
|
||||||
|
</script>
|
||||||
|
<script setup>
|
||||||
|
export default {}
|
||||||
|
</script>
|
||||||
|
`)
|
||||||
|
).toThrow(`Default export is already declared`)
|
||||||
|
|
||||||
|
expect(() =>
|
||||||
|
compile(`
|
||||||
|
<script>
|
||||||
|
const x = {}
|
||||||
|
export { x as default }
|
||||||
|
</script>
|
||||||
|
<script setup>
|
||||||
|
export default {}
|
||||||
|
</script>
|
||||||
|
`)
|
||||||
|
).toThrow(`Default export is already declared`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
@ -51,7 +51,21 @@ export function compileScriptSetup(
|
|||||||
const setupExports: Record<string, boolean> = {}
|
const setupExports: Record<string, boolean> = {}
|
||||||
let exportAllIndex = 0
|
let exportAllIndex = 0
|
||||||
let defaultExport: Node | undefined
|
let defaultExport: Node | undefined
|
||||||
let needDefaultExportCheck: boolean = false
|
let needDefaultExportRefCheck: boolean = false
|
||||||
|
|
||||||
|
const checkDuplicateDefaultExport = (node: Node) => {
|
||||||
|
if (defaultExport) {
|
||||||
|
// <script> already has export default
|
||||||
|
throw new Error(
|
||||||
|
`Default export is already declared in normal <script>.\n\n` +
|
||||||
|
generateCodeFrame(
|
||||||
|
source,
|
||||||
|
node.start! + startOffset,
|
||||||
|
node.start! + startOffset + `export default`.length
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const s = new MagicString(source)
|
const s = new MagicString(source)
|
||||||
const startOffset = scriptSetup.loc.start.offset
|
const startOffset = scriptSetup.loc.start.offset
|
||||||
@ -62,7 +76,7 @@ export function compileScriptSetup(
|
|||||||
const isTS = scriptSetup.lang === 'ts'
|
const isTS = scriptSetup.lang === 'ts'
|
||||||
const plugins: ParserPlugin[] = [
|
const plugins: ParserPlugin[] = [
|
||||||
...(options.parserPlugins || []),
|
...(options.parserPlugins || []),
|
||||||
...(babelParserDefautPlugins as ParserPlugin[]),
|
...babelParserDefautPlugins,
|
||||||
...(isTS ? (['typescript'] as const) : [])
|
...(isTS ? (['typescript'] as const) : [])
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -130,10 +144,11 @@ export function compileScriptSetup(
|
|||||||
|
|
||||||
// 2. check <script setup="xxx"> function signature
|
// 2. check <script setup="xxx"> function signature
|
||||||
const hasExplicitSignature = typeof scriptSetup.setup === 'string'
|
const hasExplicitSignature = typeof scriptSetup.setup === 'string'
|
||||||
let propsVar = `$props`
|
|
||||||
let emitVar = `$emit`
|
let propsVar: string | undefined
|
||||||
let slotsVar = `$slots`
|
let emitVar: string | undefined
|
||||||
let attrsVar = `$attrs`
|
let slotsVar: string | undefined
|
||||||
|
let attrsVar: string | undefined
|
||||||
|
|
||||||
let propsType = `{}`
|
let propsType = `{}`
|
||||||
let emitType = `(e: string, ...args: any[]) => void`
|
let emitType = `(e: string, ...args: any[]) => void`
|
||||||
@ -239,24 +254,32 @@ export function compileScriptSetup(
|
|||||||
s.remove(start, end)
|
s.remove(start, end)
|
||||||
}
|
}
|
||||||
for (const specifier of node.specifiers) {
|
for (const specifier of node.specifiers) {
|
||||||
if (specifier.type == 'ExportDefaultSpecifier') {
|
if (specifier.type === 'ExportDefaultSpecifier') {
|
||||||
// export default from './x'
|
// export default from './x'
|
||||||
// rewrite to `import __default__ from './x'`
|
// rewrite to `import __default__ from './x'`
|
||||||
|
checkDuplicateDefaultExport(node)
|
||||||
defaultExport = node
|
defaultExport = node
|
||||||
s.overwrite(
|
s.overwrite(
|
||||||
specifier.exported.start! + startOffset,
|
specifier.exported.start! + startOffset,
|
||||||
specifier.exported.start! + startOffset + 7,
|
specifier.exported.start! + startOffset + 7,
|
||||||
'__default__'
|
'__default__'
|
||||||
)
|
)
|
||||||
} else if (specifier.type == 'ExportSpecifier') {
|
} else if (specifier.type === 'ExportSpecifier') {
|
||||||
if (specifier.exported.name === 'default') {
|
if (specifier.exported.name === 'default') {
|
||||||
|
checkDuplicateDefaultExport(node)
|
||||||
defaultExport = node
|
defaultExport = node
|
||||||
// 1. remove specifier
|
// 1. remove specifier
|
||||||
if (node.specifiers.length > 1) {
|
if (node.specifiers.length > 1) {
|
||||||
s.remove(
|
// removing the default specifier from a list of specifiers.
|
||||||
specifier.start! + startOffset,
|
// look ahead until we reach the first non , or whitespace char.
|
||||||
specifier.end! + startOffset
|
let end = specifier.end! + startOffset
|
||||||
)
|
while (end < source.length) {
|
||||||
|
if (/[^,\s]/.test(source.charAt(end))) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
end++
|
||||||
|
}
|
||||||
|
s.remove(specifier.start! + startOffset, end)
|
||||||
} else {
|
} else {
|
||||||
s.remove(node.start! + startOffset!, node.end! + startOffset!)
|
s.remove(node.start! + startOffset!, node.end! + startOffset!)
|
||||||
}
|
}
|
||||||
@ -288,6 +311,9 @@ export function compileScriptSetup(
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setupExports[specifier.exported.name] = true
|
setupExports[specifier.exported.name] = true
|
||||||
|
if (node.source) {
|
||||||
|
imports[specifier.exported.name] = node.source.value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -305,30 +331,15 @@ export function compileScriptSetup(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (node.type === 'ExportDefaultDeclaration') {
|
if (node.type === 'ExportDefaultDeclaration') {
|
||||||
if (defaultExport) {
|
checkDuplicateDefaultExport(node)
|
||||||
// <script> already has export default
|
// export default {} inside <script setup>
|
||||||
throw new Error(
|
// this should be kept in module scope - move it to the end
|
||||||
`Default export is already declared in normal <script>.\n\n` +
|
s.move(start, end, source.length)
|
||||||
generateCodeFrame(
|
s.overwrite(start, start + `export default`.length, `const __default__ =`)
|
||||||
source,
|
// save it for analysis when all imports and variable declarations have
|
||||||
node.start! + startOffset,
|
// been recorded
|
||||||
node.start! + startOffset + `export default`.length
|
defaultExport = node
|
||||||
)
|
needDefaultExportRefCheck = true
|
||||||
)
|
|
||||||
} else {
|
|
||||||
// export default {} inside <script setup>
|
|
||||||
// this should be kept in module scope - move it to the end
|
|
||||||
s.move(start, end, source.length)
|
|
||||||
s.overwrite(
|
|
||||||
start,
|
|
||||||
start + `export default`.length,
|
|
||||||
`const __default__ =`
|
|
||||||
)
|
|
||||||
// save it for analysis when all imports and variable declarations have
|
|
||||||
// been recorded
|
|
||||||
defaultExport = node
|
|
||||||
needDefaultExportCheck = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@ -397,7 +408,7 @@ export function compileScriptSetup(
|
|||||||
|
|
||||||
// check default export to make sure it doesn't reference setup scope
|
// check default export to make sure it doesn't reference setup scope
|
||||||
// variables
|
// variables
|
||||||
if (needDefaultExportCheck) {
|
if (needDefaultExportRefCheck) {
|
||||||
checkDefaultExport(
|
checkDefaultExport(
|
||||||
defaultExport!,
|
defaultExport!,
|
||||||
setupScopeVars,
|
setupScopeVars,
|
||||||
@ -428,7 +439,7 @@ export function compileScriptSetup(
|
|||||||
|
|
||||||
// wrap setup code with function
|
// wrap setup code with function
|
||||||
// finalize the argument signature.
|
// finalize the argument signature.
|
||||||
let args
|
let args = ``
|
||||||
if (isTS) {
|
if (isTS) {
|
||||||
if (slotsType === '__Slots__') {
|
if (slotsType === '__Slots__') {
|
||||||
s.prepend(`import { Slots as __Slots__ } from 'vue'\n`)
|
s.prepend(`import { Slots as __Slots__ } from 'vue'\n`)
|
||||||
@ -450,13 +461,9 @@ export function compileScriptSetup(
|
|||||||
ss.appendRight(setupCtxASTNode.end! - 1!, `: ${ctxType}`)
|
ss.appendRight(setupCtxASTNode.end! - 1!, `: ${ctxType}`)
|
||||||
}
|
}
|
||||||
args = ss.toString()
|
args = ss.toString()
|
||||||
} else {
|
|
||||||
args = `$props: ${propsType}, { emit: $emit, slots: $slots, attrs: $attrs }: ${ctxType}`
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
args = hasExplicitSignature
|
args = hasExplicitSignature ? (scriptSetup.setup as string) : ``
|
||||||
? scriptSetup.setup
|
|
||||||
: `$props, { emit: $emit, slots: $slots, attrs: $attrs }`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// export the content of <script setup> as a named export, `setup`.
|
// export the content of <script setup> as a named export, `setup`.
|
||||||
@ -602,6 +609,7 @@ function walkPattern(node: Node, bindings: Record<string, boolean>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function extractProps(node: TSTypeLiteral, props: Set<string>) {
|
function extractProps(node: TSTypeLiteral, props: Set<string>) {
|
||||||
|
// TODO generate type/required checks in dev
|
||||||
for (const m of node.members) {
|
for (const m of node.members) {
|
||||||
if (m.type === 'TSPropertySignature' && m.key.type === 'Identifier') {
|
if (m.type === 'TSPropertySignature' && m.key.type === 'Identifier') {
|
||||||
props.add(m.key.name)
|
props.add(m.key.name)
|
||||||
|
@ -23,6 +23,7 @@ export {
|
|||||||
SFCAsyncStyleCompileOptions,
|
SFCAsyncStyleCompileOptions,
|
||||||
SFCStyleCompileResults
|
SFCStyleCompileResults
|
||||||
} from './compileStyle'
|
} from './compileStyle'
|
||||||
|
export { SFCScriptCompileOptions } from './compileScript'
|
||||||
export {
|
export {
|
||||||
CompilerOptions,
|
CompilerOptions,
|
||||||
CompilerError,
|
CompilerError,
|
||||||
|
@ -23,7 +23,7 @@ export const babelParserDefautPlugins = [
|
|||||||
'bigInt',
|
'bigInt',
|
||||||
'optionalChaining',
|
'optionalChaining',
|
||||||
'nullishCoalescingOperator'
|
'nullishCoalescingOperator'
|
||||||
]
|
] as const
|
||||||
|
|
||||||
export const EMPTY_OBJ: { readonly [key: string]: any } = __DEV__
|
export const EMPTY_OBJ: { readonly [key: string]: any } = __DEV__
|
||||||
? Object.freeze({})
|
? Object.freeze({})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user