refactor: simplify sfc script transform usage
This commit is contained in:
parent
9f706a9f5e
commit
b4f7ab45ea
@ -1,10 +1,9 @@
|
|||||||
import { parse, compileScriptSetup, SFCScriptCompileOptions } from '../src'
|
import { parse, SFCScriptCompileOptions } from '../src'
|
||||||
import { parse as babelParse } from '@babel/parser'
|
import { parse as babelParse } from '@babel/parser'
|
||||||
import { babelParserDefautPlugins } from '@vue/shared'
|
import { babelParserDefautPlugins } from '@vue/shared'
|
||||||
|
|
||||||
function compile(src: string, options?: SFCScriptCompileOptions) {
|
function compile(src: string, options?: SFCScriptCompileOptions) {
|
||||||
const { descriptor } = parse(src)
|
return parse(src, options).descriptor.scriptTransformed!
|
||||||
return compileScriptSetup(descriptor, options)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function assertCode(code: string) {
|
function assertCode(code: string) {
|
||||||
@ -23,17 +22,19 @@ function assertCode(code: string) {
|
|||||||
|
|
||||||
describe('SFC compile <script setup>', () => {
|
describe('SFC compile <script setup>', () => {
|
||||||
test('should hoist imports', () => {
|
test('should hoist imports', () => {
|
||||||
assertCode(compile(`<script setup>import { ref } from 'vue'</script>`).code)
|
assertCode(
|
||||||
|
compile(`<script setup>import { ref } from 'vue'</script>`).content
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('explicit setup signature', () => {
|
test('explicit setup signature', () => {
|
||||||
assertCode(
|
assertCode(
|
||||||
compile(`<script setup="props, { emit }">emit('foo')</script>`).code
|
compile(`<script setup="props, { emit }">emit('foo')</script>`).content
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('import dedupe between <script> and <script setup>', () => {
|
test('import dedupe between <script> and <script setup>', () => {
|
||||||
const code = compile(`
|
const { content } = compile(`
|
||||||
<script>
|
<script>
|
||||||
import { x } from './x'
|
import { x } from './x'
|
||||||
</script>
|
</script>
|
||||||
@ -41,30 +42,30 @@ describe('SFC compile <script setup>', () => {
|
|||||||
import { x } from './x'
|
import { x } from './x'
|
||||||
x()
|
x()
|
||||||
</script>
|
</script>
|
||||||
`).code
|
`)
|
||||||
assertCode(code)
|
assertCode(content)
|
||||||
expect(code.indexOf(`import { x }`)).toEqual(
|
expect(content.indexOf(`import { x }`)).toEqual(
|
||||||
code.lastIndexOf(`import { x }`)
|
content.lastIndexOf(`import { x }`)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('exports', () => {
|
describe('exports', () => {
|
||||||
test('export const x = ...', () => {
|
test('export const x = ...', () => {
|
||||||
const { code, bindings } = compile(
|
const { content, bindings } = compile(
|
||||||
`<script setup>export const x = 1</script>`
|
`<script setup>export const x = 1</script>`
|
||||||
)
|
)
|
||||||
assertCode(code)
|
assertCode(content)
|
||||||
expect(bindings).toStrictEqual({
|
expect(bindings).toStrictEqual({
|
||||||
x: 'setup'
|
x: 'setup'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test('export const { x } = ... (destructuring)', () => {
|
test('export const { x } = ... (destructuring)', () => {
|
||||||
const { code, bindings } = compile(`<script setup>
|
const { content, bindings } = compile(`<script setup>
|
||||||
export const [a = 1, { b } = { b: 123 }, ...c] = useFoo()
|
export const [a = 1, { b } = { b: 123 }, ...c] = useFoo()
|
||||||
export const { d = 2, _: [e], ...f } = useBar()
|
export const { d = 2, _: [e], ...f } = useBar()
|
||||||
</script>`)
|
</script>`)
|
||||||
assertCode(code)
|
assertCode(content)
|
||||||
expect(bindings).toStrictEqual({
|
expect(bindings).toStrictEqual({
|
||||||
a: 'setup',
|
a: 'setup',
|
||||||
b: 'setup',
|
b: 'setup',
|
||||||
@ -76,34 +77,34 @@ describe('SFC compile <script setup>', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test('export function x() {}', () => {
|
test('export function x() {}', () => {
|
||||||
const { code, bindings } = compile(
|
const { content, bindings } = compile(
|
||||||
`<script setup>export function x(){}</script>`
|
`<script setup>export function x(){}</script>`
|
||||||
)
|
)
|
||||||
assertCode(code)
|
assertCode(content)
|
||||||
expect(bindings).toStrictEqual({
|
expect(bindings).toStrictEqual({
|
||||||
x: 'setup'
|
x: 'setup'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test('export class X() {}', () => {
|
test('export class X() {}', () => {
|
||||||
const { code, bindings } = compile(
|
const { content, bindings } = compile(
|
||||||
`<script setup>export class X {}</script>`
|
`<script setup>export class X {}</script>`
|
||||||
)
|
)
|
||||||
assertCode(code)
|
assertCode(content)
|
||||||
expect(bindings).toStrictEqual({
|
expect(bindings).toStrictEqual({
|
||||||
X: 'setup'
|
X: 'setup'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test('export { x }', () => {
|
test('export { x }', () => {
|
||||||
const { code, bindings } = compile(
|
const { content, bindings } = compile(
|
||||||
`<script setup>
|
`<script setup>
|
||||||
const x = 1
|
const x = 1
|
||||||
const y = 2
|
const y = 2
|
||||||
export { x, y }
|
export { x, y }
|
||||||
</script>`
|
</script>`
|
||||||
)
|
)
|
||||||
assertCode(code)
|
assertCode(content)
|
||||||
expect(bindings).toStrictEqual({
|
expect(bindings).toStrictEqual({
|
||||||
x: 'setup',
|
x: 'setup',
|
||||||
y: 'setup'
|
y: 'setup'
|
||||||
@ -111,12 +112,12 @@ describe('SFC compile <script setup>', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test(`export { x } from './x'`, () => {
|
test(`export { x } from './x'`, () => {
|
||||||
const { code, bindings } = compile(
|
const { content, bindings } = compile(
|
||||||
`<script setup>
|
`<script setup>
|
||||||
export { x, y } from './x'
|
export { x, y } from './x'
|
||||||
</script>`
|
</script>`
|
||||||
)
|
)
|
||||||
assertCode(code)
|
assertCode(content)
|
||||||
expect(bindings).toStrictEqual({
|
expect(bindings).toStrictEqual({
|
||||||
x: 'setup',
|
x: 'setup',
|
||||||
y: 'setup'
|
y: 'setup'
|
||||||
@ -124,52 +125,52 @@ describe('SFC compile <script setup>', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test(`export default from './x'`, () => {
|
test(`export default from './x'`, () => {
|
||||||
const { code, bindings } = compile(
|
const { content, bindings } = compile(
|
||||||
`<script setup>
|
`<script setup>
|
||||||
export default from './x'
|
export default from './x'
|
||||||
</script>`,
|
</script>`,
|
||||||
{
|
{
|
||||||
parserPlugins: ['exportDefaultFrom']
|
babelParserPlugins: ['exportDefaultFrom']
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
assertCode(code)
|
assertCode(content)
|
||||||
expect(bindings).toStrictEqual({})
|
expect(bindings).toStrictEqual({})
|
||||||
})
|
})
|
||||||
|
|
||||||
test(`export { x as default }`, () => {
|
test(`export { x as default }`, () => {
|
||||||
const { code, bindings } = compile(
|
const { content, bindings } = compile(
|
||||||
`<script setup>
|
`<script setup>
|
||||||
import x from './x'
|
import x from './x'
|
||||||
const y = 1
|
const y = 1
|
||||||
export { x as default, y }
|
export { x as default, y }
|
||||||
</script>`
|
</script>`
|
||||||
)
|
)
|
||||||
assertCode(code)
|
assertCode(content)
|
||||||
expect(bindings).toStrictEqual({
|
expect(bindings).toStrictEqual({
|
||||||
y: 'setup'
|
y: 'setup'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test(`export { x as default } from './x'`, () => {
|
test(`export { x as default } from './x'`, () => {
|
||||||
const { code, bindings } = compile(
|
const { content, bindings } = compile(
|
||||||
`<script setup>
|
`<script setup>
|
||||||
export { x as default, y } from './x'
|
export { x as default, y } from './x'
|
||||||
</script>`
|
</script>`
|
||||||
)
|
)
|
||||||
assertCode(code)
|
assertCode(content)
|
||||||
expect(bindings).toStrictEqual({
|
expect(bindings).toStrictEqual({
|
||||||
y: 'setup'
|
y: 'setup'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test(`export * from './x'`, () => {
|
test(`export * from './x'`, () => {
|
||||||
const { code, bindings } = compile(
|
const { content, bindings } = compile(
|
||||||
`<script setup>
|
`<script setup>
|
||||||
export * from './x'
|
export * from './x'
|
||||||
export const y = 1
|
export const y = 1
|
||||||
</script>`
|
</script>`
|
||||||
)
|
)
|
||||||
assertCode(code)
|
assertCode(content)
|
||||||
expect(bindings).toStrictEqual({
|
expect(bindings).toStrictEqual({
|
||||||
y: 'setup'
|
y: 'setup'
|
||||||
// in this case we cannot extract bindings from ./x so it falls back
|
// in this case we cannot extract bindings from ./x so it falls back
|
||||||
@ -178,7 +179,7 @@ describe('SFC compile <script setup>', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
test('export default in <script setup>', () => {
|
test('export default in <script setup>', () => {
|
||||||
const { code, bindings } = compile(
|
const { content, bindings } = compile(
|
||||||
`<script setup>
|
`<script setup>
|
||||||
export default {
|
export default {
|
||||||
props: ['foo']
|
props: ['foo']
|
||||||
@ -186,7 +187,7 @@ describe('SFC compile <script setup>', () => {
|
|||||||
export const y = 1
|
export const y = 1
|
||||||
</script>`
|
</script>`
|
||||||
)
|
)
|
||||||
assertCode(code)
|
assertCode(content)
|
||||||
expect(bindings).toStrictEqual({
|
expect(bindings).toStrictEqual({
|
||||||
y: 'setup'
|
y: 'setup'
|
||||||
})
|
})
|
||||||
@ -195,18 +196,18 @@ describe('SFC compile <script setup>', () => {
|
|||||||
|
|
||||||
describe('<script setup lang="ts">', () => {
|
describe('<script setup lang="ts">', () => {
|
||||||
test('hoist type declarations', () => {
|
test('hoist type declarations', () => {
|
||||||
const { code, bindings } = compile(`
|
const { content, bindings } = compile(`
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
export interface Foo {}
|
export interface Foo {}
|
||||||
type Bar = {}
|
type Bar = {}
|
||||||
export const a = 1
|
export const a = 1
|
||||||
</script>`)
|
</script>`)
|
||||||
assertCode(code)
|
assertCode(content)
|
||||||
expect(bindings).toStrictEqual({ a: 'setup' })
|
expect(bindings).toStrictEqual({ a: 'setup' })
|
||||||
})
|
})
|
||||||
|
|
||||||
test('extract props', () => {
|
test('extract props', () => {
|
||||||
const { code } = compile(`
|
const { content } = compile(`
|
||||||
<script setup="myProps" lang="ts">
|
<script setup="myProps" lang="ts">
|
||||||
interface Test {}
|
interface Test {}
|
||||||
|
|
||||||
@ -237,59 +238,57 @@ describe('SFC compile <script setup>', () => {
|
|||||||
intersection: Test & {}
|
intersection: Test & {}
|
||||||
}
|
}
|
||||||
</script>`)
|
</script>`)
|
||||||
assertCode(code)
|
assertCode(content)
|
||||||
expect(code).toMatch(`string: { type: String, required: true }`)
|
expect(content).toMatch(`string: { type: String, required: true }`)
|
||||||
expect(code).toMatch(`number: { type: Number, required: true }`)
|
expect(content).toMatch(`number: { type: Number, required: true }`)
|
||||||
expect(code).toMatch(`boolean: { type: Boolean, required: true }`)
|
expect(content).toMatch(`boolean: { type: Boolean, required: true }`)
|
||||||
expect(code).toMatch(`object: { type: Object, required: true }`)
|
expect(content).toMatch(`object: { type: Object, required: true }`)
|
||||||
expect(code).toMatch(`objectLiteral: { type: Object, required: true }`)
|
expect(content).toMatch(`objectLiteral: { type: Object, required: true }`)
|
||||||
expect(code).toMatch(`fn: { type: Function, required: true }`)
|
expect(content).toMatch(`fn: { type: Function, required: true }`)
|
||||||
expect(code).toMatch(`functionRef: { type: Function, required: true }`)
|
expect(content).toMatch(`functionRef: { type: Function, required: true }`)
|
||||||
expect(code).toMatch(`objectRef: { type: Object, required: true }`)
|
expect(content).toMatch(`objectRef: { type: Object, required: true }`)
|
||||||
expect(code).toMatch(`array: { type: Array, required: true }`)
|
expect(content).toMatch(`array: { type: Array, required: true }`)
|
||||||
expect(code).toMatch(`arrayRef: { type: Array, required: true }`)
|
expect(content).toMatch(`arrayRef: { type: Array, required: true }`)
|
||||||
expect(code).toMatch(`tuple: { type: Array, required: true }`)
|
expect(content).toMatch(`tuple: { type: Array, required: true }`)
|
||||||
expect(code).toMatch(`set: { type: Set, required: true }`)
|
expect(content).toMatch(`set: { type: Set, required: true }`)
|
||||||
expect(code).toMatch(`literal: { type: String, required: true }`)
|
expect(content).toMatch(`literal: { type: String, required: true }`)
|
||||||
expect(code).toMatch(`optional: { type: null, required: false }`)
|
expect(content).toMatch(`optional: { type: null, required: false }`)
|
||||||
expect(code).toMatch(`recordRef: { type: Object, required: true }`)
|
expect(content).toMatch(`recordRef: { type: Object, required: true }`)
|
||||||
expect(code).toMatch(`interface: { type: Object, required: true }`)
|
expect(content).toMatch(`interface: { type: Object, required: true }`)
|
||||||
expect(code).toMatch(`alias: { type: Array, required: true }`)
|
expect(content).toMatch(`alias: { type: Array, required: true }`)
|
||||||
expect(code).toMatch(`union: { type: [String, Number], required: true }`)
|
expect(content).toMatch(
|
||||||
expect(code).toMatch(
|
`union: { type: [String, Number], required: true }`
|
||||||
|
)
|
||||||
|
expect(content).toMatch(
|
||||||
`literalUnion: { type: [String, String], required: true }`
|
`literalUnion: { type: [String, String], required: true }`
|
||||||
)
|
)
|
||||||
expect(code).toMatch(
|
expect(content).toMatch(
|
||||||
`literalUnionMixed: { type: [String, Number, Boolean], required: true }`
|
`literalUnionMixed: { type: [String, Number, Boolean], required: true }`
|
||||||
)
|
)
|
||||||
expect(code).toMatch(`intersection: { type: Object, required: true }`)
|
expect(content).toMatch(`intersection: { type: Object, required: true }`)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('extract emits', () => {
|
test('extract emits', () => {
|
||||||
const { code } = compile(`
|
const { content } = compile(`
|
||||||
<script setup="_, { emit: myEmit }" lang="ts">
|
<script setup="_, { emit: myEmit }" lang="ts">
|
||||||
declare function myEmit(e: 'foo' | 'bar'): void
|
declare function myEmit(e: 'foo' | 'bar'): void
|
||||||
declare function myEmit(e: 'baz', id: number): void
|
declare function myEmit(e: 'baz', id: number): void
|
||||||
</script>
|
</script>
|
||||||
`)
|
`)
|
||||||
assertCode(code)
|
assertCode(content)
|
||||||
expect(code).toMatch(`declare function __emit__(e: 'foo' | 'bar'): void`)
|
expect(content).toMatch(
|
||||||
expect(code).toMatch(
|
`declare function __emit__(e: 'foo' | 'bar'): void`
|
||||||
|
)
|
||||||
|
expect(content).toMatch(
|
||||||
`declare function __emit__(e: 'baz', id: number): void`
|
`declare function __emit__(e: 'baz', id: number): void`
|
||||||
)
|
)
|
||||||
expect(code).toMatch(
|
expect(content).toMatch(
|
||||||
`emits: ["foo", "bar", "baz"] as unknown as undefined`
|
`emits: ["foo", "bar", "baz"] as unknown as undefined`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('errors', () => {
|
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', () => {
|
test('<script> and <script setup> must have same lang', () => {
|
||||||
expect(() =>
|
expect(() =>
|
||||||
compile(`<script>foo()</script><script setup lang="ts">bar()</script>`)
|
compile(`<script>foo()</script><script setup lang="ts">bar()</script>`)
|
||||||
@ -342,7 +341,7 @@ describe('SFC compile <script setup>', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>`).code
|
</script>`).content
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -358,7 +357,7 @@ describe('SFC compile <script setup>', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>`).code
|
</script>`).content
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -373,7 +372,7 @@ describe('SFC compile <script setup>', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>`).code
|
</script>`).content
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import MagicString, { SourceMap } from 'magic-string'
|
import MagicString from 'magic-string'
|
||||||
import { SFCDescriptor, SFCScriptBlock } from './parse'
|
import { SFCDescriptor, SFCScriptBlock } from './parse'
|
||||||
import { parse, ParserPlugin } from '@babel/parser'
|
import { parse, ParserPlugin } from '@babel/parser'
|
||||||
import { babelParserDefautPlugins, generateCodeFrame } from '@vue/shared'
|
import { babelParserDefautPlugins, generateCodeFrame } from '@vue/shared'
|
||||||
@ -17,15 +17,16 @@ import {
|
|||||||
TSDeclareFunction
|
TSDeclareFunction
|
||||||
} from '@babel/types'
|
} from '@babel/types'
|
||||||
import { walk } from 'estree-walker'
|
import { walk } from 'estree-walker'
|
||||||
|
import { RawSourceMap } from 'source-map'
|
||||||
|
|
||||||
|
export interface SFCScriptCompileOptions {
|
||||||
|
babelParserPlugins?: ParserPlugin[]
|
||||||
|
}
|
||||||
|
|
||||||
export interface BindingMetadata {
|
export interface BindingMetadata {
|
||||||
[key: string]: 'data' | 'props' | 'setup' | 'ctx'
|
[key: string]: 'data' | 'props' | 'setup' | 'ctx'
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SFCScriptCompileOptions {
|
|
||||||
parserPlugins?: ParserPlugin[]
|
|
||||||
}
|
|
||||||
|
|
||||||
let hasWarned = false
|
let hasWarned = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -33,10 +34,10 @@ let hasWarned = false
|
|||||||
* It requires the whole SFC descriptor because we need to handle and merge
|
* It requires the whole SFC descriptor because we need to handle and merge
|
||||||
* normal `<script>` + `<script setup>` if both are present.
|
* normal `<script>` + `<script setup>` if both are present.
|
||||||
*/
|
*/
|
||||||
export function compileScriptSetup(
|
export function compileScript(
|
||||||
sfc: SFCDescriptor,
|
sfc: SFCDescriptor,
|
||||||
options: SFCScriptCompileOptions = {}
|
options: SFCScriptCompileOptions = {}
|
||||||
) {
|
): SFCScriptBlock {
|
||||||
if (__DEV__ && !__TEST__ && !hasWarned) {
|
if (__DEV__ && !__TEST__ && !hasWarned) {
|
||||||
hasWarned = true
|
hasWarned = true
|
||||||
console.log(
|
console.log(
|
||||||
@ -47,7 +48,13 @@ export function compileScriptSetup(
|
|||||||
|
|
||||||
const { script, scriptSetup, source, filename } = sfc
|
const { script, scriptSetup, source, filename } = sfc
|
||||||
if (!scriptSetup) {
|
if (!scriptSetup) {
|
||||||
throw new Error('SFC has no <script setup>.')
|
if (!script) {
|
||||||
|
throw new Error(`SFC contains no <script> tags.`)
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...script,
|
||||||
|
bindings: analyzeScriptBindings(script)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (script && script.lang !== scriptSetup.lang) {
|
if (script && script.lang !== scriptSetup.lang) {
|
||||||
@ -86,7 +93,7 @@ export function compileScriptSetup(
|
|||||||
|
|
||||||
const isTS = scriptSetup.lang === 'ts'
|
const isTS = scriptSetup.lang === 'ts'
|
||||||
const plugins: ParserPlugin[] = [
|
const plugins: ParserPlugin[] = [
|
||||||
...(options.parserPlugins || []),
|
...(options.babelParserPlugins || []),
|
||||||
...babelParserDefautPlugins,
|
...babelParserDefautPlugins,
|
||||||
...(isTS ? (['typescript'] as const) : [])
|
...(isTS ? (['typescript'] as const) : [])
|
||||||
]
|
]
|
||||||
@ -154,7 +161,8 @@ export function compileScriptSetup(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2. check <script setup="xxx"> function signature
|
// 2. check <script setup="xxx"> function signature
|
||||||
const hasExplicitSignature = typeof scriptSetup.setup === 'string'
|
const setupValue = scriptSetup.attrs.setup
|
||||||
|
const hasExplicitSignature = typeof setupValue === 'string'
|
||||||
|
|
||||||
let propsVar: string | undefined
|
let propsVar: string | undefined
|
||||||
let emitVar: string | undefined
|
let emitVar: string | undefined
|
||||||
@ -179,8 +187,8 @@ export function compileScriptSetup(
|
|||||||
// <script setup="xxx" lang="ts">
|
// <script setup="xxx" lang="ts">
|
||||||
// parse the signature to extract the props/emit variables the user wants
|
// parse the signature to extract the props/emit variables the user wants
|
||||||
// we need them to find corresponding type declarations.
|
// we need them to find corresponding type declarations.
|
||||||
const signatureAST = parse(`(${scriptSetup.setup})=>{}`, { plugins })
|
const signatureAST = parse(`(${setupValue})=>{}`, { plugins }).program
|
||||||
.program.body[0]
|
.body[0]
|
||||||
const params = ((signatureAST as ExpressionStatement)
|
const params = ((signatureAST as ExpressionStatement)
|
||||||
.expression as ArrowFunctionExpression).params
|
.expression as ArrowFunctionExpression).params
|
||||||
if (params[0] && params[0].type === 'Identifier') {
|
if (params[0] && params[0].type === 'Identifier') {
|
||||||
@ -464,7 +472,7 @@ export function compileScriptSetup(
|
|||||||
}`
|
}`
|
||||||
if (hasExplicitSignature) {
|
if (hasExplicitSignature) {
|
||||||
// inject types to user signature
|
// inject types to user signature
|
||||||
args = scriptSetup.setup as string
|
args = setupValue as string
|
||||||
const ss = new MagicString(args)
|
const ss = new MagicString(args)
|
||||||
if (propsASTNode) {
|
if (propsASTNode) {
|
||||||
// compensate for () wraper offset
|
// compensate for () wraper offset
|
||||||
@ -476,7 +484,7 @@ export function compileScriptSetup(
|
|||||||
args = ss.toString()
|
args = ss.toString()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
args = hasExplicitSignature ? (scriptSetup.setup as string) : ``
|
args = hasExplicitSignature ? (setupValue as string) : ``
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. wrap setup code with function.
|
// 6. wrap setup code with function.
|
||||||
@ -530,13 +538,14 @@ export function compileScriptSetup(
|
|||||||
|
|
||||||
s.trim()
|
s.trim()
|
||||||
return {
|
return {
|
||||||
|
...scriptSetup,
|
||||||
bindings,
|
bindings,
|
||||||
code: s.toString(),
|
content: s.toString(),
|
||||||
map: s.generateMap({
|
map: (s.generateMap({
|
||||||
source: filename,
|
source: filename,
|
||||||
hires: true,
|
hires: true,
|
||||||
includeContent: true
|
includeContent: true
|
||||||
}) as SourceMap
|
}) as unknown) as RawSourceMap
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
export { parse } from './parse'
|
export { parse } from './parse'
|
||||||
export { compileTemplate } from './compileTemplate'
|
export { compileTemplate } from './compileTemplate'
|
||||||
export { compileStyle, compileStyleAsync } from './compileStyle'
|
export { compileStyle, compileStyleAsync } from './compileStyle'
|
||||||
export { compileScriptSetup, analyzeScriptBindings } from './compileScript'
|
export { compileScript, analyzeScriptBindings } from './compileScript'
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
export {
|
export {
|
||||||
@ -23,7 +23,7 @@ export {
|
|||||||
SFCAsyncStyleCompileOptions,
|
SFCAsyncStyleCompileOptions,
|
||||||
SFCStyleCompileResults
|
SFCStyleCompileResults
|
||||||
} from './compileStyle'
|
} from './compileStyle'
|
||||||
export { SFCScriptCompileOptions } from './compileScript'
|
export { SFCScriptCompileOptions, BindingMetadata } from './compileScript'
|
||||||
export {
|
export {
|
||||||
CompilerOptions,
|
CompilerOptions,
|
||||||
CompilerError,
|
CompilerError,
|
||||||
|
@ -5,10 +5,12 @@ import {
|
|||||||
CompilerError,
|
CompilerError,
|
||||||
TextModes
|
TextModes
|
||||||
} from '@vue/compiler-core'
|
} from '@vue/compiler-core'
|
||||||
|
import * as CompilerDOM from '@vue/compiler-dom'
|
||||||
import { RawSourceMap, SourceMapGenerator } from 'source-map'
|
import { RawSourceMap, SourceMapGenerator } from 'source-map'
|
||||||
import { generateCodeFrame } from '@vue/shared'
|
import { generateCodeFrame } from '@vue/shared'
|
||||||
import { TemplateCompiler } from './compileTemplate'
|
import { TemplateCompiler } from './compileTemplate'
|
||||||
import * as CompilerDOM from '@vue/compiler-dom'
|
import { compileScript, BindingMetadata } from './compileScript'
|
||||||
|
import { ParserPlugin } from '@babel/parser'
|
||||||
|
|
||||||
export interface SFCParseOptions {
|
export interface SFCParseOptions {
|
||||||
filename?: string
|
filename?: string
|
||||||
@ -16,6 +18,7 @@ export interface SFCParseOptions {
|
|||||||
sourceRoot?: string
|
sourceRoot?: string
|
||||||
pad?: boolean | 'line' | 'space'
|
pad?: boolean | 'line' | 'space'
|
||||||
compiler?: TemplateCompiler
|
compiler?: TemplateCompiler
|
||||||
|
babelParserPlugins?: ParserPlugin[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SFCBlock {
|
export interface SFCBlock {
|
||||||
@ -35,7 +38,7 @@ export interface SFCTemplateBlock extends SFCBlock {
|
|||||||
|
|
||||||
export interface SFCScriptBlock extends SFCBlock {
|
export interface SFCScriptBlock extends SFCBlock {
|
||||||
type: 'script'
|
type: 'script'
|
||||||
setup?: boolean | string
|
bindings?: BindingMetadata
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SFCStyleBlock extends SFCBlock {
|
export interface SFCStyleBlock extends SFCBlock {
|
||||||
@ -50,6 +53,7 @@ export interface SFCDescriptor {
|
|||||||
template: SFCTemplateBlock | null
|
template: SFCTemplateBlock | null
|
||||||
script: SFCScriptBlock | null
|
script: SFCScriptBlock | null
|
||||||
scriptSetup: SFCScriptBlock | null
|
scriptSetup: SFCScriptBlock | null
|
||||||
|
scriptTransformed: SFCScriptBlock | null
|
||||||
styles: SFCStyleBlock[]
|
styles: SFCStyleBlock[]
|
||||||
customBlocks: SFCBlock[]
|
customBlocks: SFCBlock[]
|
||||||
}
|
}
|
||||||
@ -75,7 +79,8 @@ export function parse(
|
|||||||
filename = 'component.vue',
|
filename = 'component.vue',
|
||||||
sourceRoot = '',
|
sourceRoot = '',
|
||||||
pad = false,
|
pad = false,
|
||||||
compiler = CompilerDOM
|
compiler = CompilerDOM,
|
||||||
|
babelParserPlugins
|
||||||
}: SFCParseOptions = {}
|
}: SFCParseOptions = {}
|
||||||
): SFCParseResult {
|
): SFCParseResult {
|
||||||
const sourceKey =
|
const sourceKey =
|
||||||
@ -91,6 +96,7 @@ export function parse(
|
|||||||
template: null,
|
template: null,
|
||||||
script: null,
|
script: null,
|
||||||
scriptSetup: null,
|
scriptSetup: null,
|
||||||
|
scriptTransformed: null,
|
||||||
styles: [],
|
styles: [],
|
||||||
customBlocks: []
|
customBlocks: []
|
||||||
}
|
}
|
||||||
@ -146,15 +152,16 @@ export function parse(
|
|||||||
break
|
break
|
||||||
case 'script':
|
case 'script':
|
||||||
const block = createBlock(node, source, pad) as SFCScriptBlock
|
const block = createBlock(node, source, pad) as SFCScriptBlock
|
||||||
if (block.setup && !descriptor.scriptSetup) {
|
const isSetup = !!block.attrs.setup
|
||||||
|
if (isSetup && !descriptor.scriptSetup) {
|
||||||
descriptor.scriptSetup = block
|
descriptor.scriptSetup = block
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if (!block.setup && !descriptor.script) {
|
if (!isSetup && !descriptor.script) {
|
||||||
descriptor.script = block
|
descriptor.script = block
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
warnDuplicateBlock(source, filename, node, !!block.setup)
|
warnDuplicateBlock(source, filename, node, isSetup)
|
||||||
break
|
break
|
||||||
case 'style':
|
case 'style':
|
||||||
descriptor.styles.push(createBlock(node, source, pad) as SFCStyleBlock)
|
descriptor.styles.push(createBlock(node, source, pad) as SFCStyleBlock)
|
||||||
@ -182,6 +189,16 @@ export function parse(
|
|||||||
descriptor.styles.forEach(genMap)
|
descriptor.styles.forEach(genMap)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (descriptor.script || descriptor.scriptSetup) {
|
||||||
|
try {
|
||||||
|
descriptor.scriptTransformed = compileScript(descriptor, {
|
||||||
|
babelParserPlugins
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
errors.push(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const result = {
|
const result = {
|
||||||
descriptor,
|
descriptor,
|
||||||
errors
|
errors
|
||||||
@ -252,8 +269,6 @@ function createBlock(
|
|||||||
}
|
}
|
||||||
} else if (type === 'template' && p.name === 'functional') {
|
} else if (type === 'template' && p.name === 'functional') {
|
||||||
;(block as SFCTemplateBlock).functional = true
|
;(block as SFCTemplateBlock).functional = true
|
||||||
} else if (type === 'script' && p.name === 'setup') {
|
|
||||||
;(block as SFCScriptBlock).setup = attrs.setup || true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user