fix(compiler-sfc): fix template usage check false positives on types
fix #5414
This commit is contained in:
parent
ba17792b72
commit
ccf92564d3
@ -535,6 +535,23 @@ 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, Bar, Baz } from './x'
|
||||||
|
|
||||||
|
export default /*#__PURE__*/_defineComponent({
|
||||||
|
setup(__props, { expose }) {
|
||||||
|
expose();
|
||||||
|
|
||||||
|
const a = 1
|
||||||
|
function b() {}
|
||||||
|
|
||||||
|
return { a, b, Baz }
|
||||||
|
}
|
||||||
|
|
||||||
|
})"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`SFC compile <script setup> dev mode import usage check attribute expressions 1`] = `
|
exports[`SFC compile <script setup> dev mode import usage check attribute expressions 1`] = `
|
||||||
"import { defineComponent as _defineComponent } from 'vue'
|
"import { defineComponent as _defineComponent } from 'vue'
|
||||||
import { bar, baz } from './x'
|
import { bar, baz } from './x'
|
||||||
|
@ -432,6 +432,23 @@ defineExpose({ foo: 123 })
|
|||||||
expect(content).toMatch(`return { FooBaz, Last }`)
|
expect(content).toMatch(`return { FooBaz, Last }`)
|
||||||
assertCode(content)
|
assertCode(content)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('TS annotations', () => {
|
||||||
|
const { content } = compile(`
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Foo, Bar, Baz } from './x'
|
||||||
|
const a = 1
|
||||||
|
function b() {}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
{{ a as Foo }}
|
||||||
|
{{ b<Bar>() }}
|
||||||
|
{{ Baz }}
|
||||||
|
</template>
|
||||||
|
`)
|
||||||
|
expect(content).toMatch(`return { a, b, Baz }`)
|
||||||
|
assertCode(content)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('inlineTemplate mode', () => {
|
describe('inlineTemplate mode', () => {
|
||||||
|
@ -12,7 +12,12 @@ import {
|
|||||||
walkIdentifiers
|
walkIdentifiers
|
||||||
} from '@vue/compiler-dom'
|
} from '@vue/compiler-dom'
|
||||||
import { DEFAULT_FILENAME, SFCDescriptor, SFCScriptBlock } from './parse'
|
import { DEFAULT_FILENAME, SFCDescriptor, SFCScriptBlock } from './parse'
|
||||||
import { parse as _parse, ParserOptions, ParserPlugin } from '@babel/parser'
|
import {
|
||||||
|
parse as _parse,
|
||||||
|
parseExpression,
|
||||||
|
ParserOptions,
|
||||||
|
ParserPlugin
|
||||||
|
} from '@babel/parser'
|
||||||
import { camelize, capitalize, generateCodeFrame, makeMap } from '@vue/shared'
|
import { camelize, capitalize, generateCodeFrame, makeMap } from '@vue/shared'
|
||||||
import {
|
import {
|
||||||
Node,
|
Node,
|
||||||
@ -348,14 +353,23 @@ export function compileScript(
|
|||||||
local: string,
|
local: string,
|
||||||
imported: string | false,
|
imported: string | false,
|
||||||
isType: boolean,
|
isType: boolean,
|
||||||
isFromSetup: boolean
|
isFromSetup: boolean,
|
||||||
|
needTemplateUsageCheck: boolean
|
||||||
) {
|
) {
|
||||||
if (source === 'vue' && imported) {
|
if (source === 'vue' && imported) {
|
||||||
userImportAlias[imported] = local
|
userImportAlias[imported] = local
|
||||||
}
|
}
|
||||||
|
|
||||||
let isUsedInTemplate = true
|
// template usage check is only needed in non-inline mode, so we can skip
|
||||||
if (isTS && sfc.template && !sfc.template.src && !sfc.template.lang) {
|
// the work if inlineTemplate is true.
|
||||||
|
let isUsedInTemplate = needTemplateUsageCheck
|
||||||
|
if (
|
||||||
|
needTemplateUsageCheck &&
|
||||||
|
isTS &&
|
||||||
|
sfc.template &&
|
||||||
|
!sfc.template.src &&
|
||||||
|
!sfc.template.lang
|
||||||
|
) {
|
||||||
isUsedInTemplate = isImportUsed(local, sfc)
|
isUsedInTemplate = isImportUsed(local, sfc)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -813,7 +827,8 @@ export function compileScript(
|
|||||||
node.importKind === 'type' ||
|
node.importKind === 'type' ||
|
||||||
(specifier.type === 'ImportSpecifier' &&
|
(specifier.type === 'ImportSpecifier' &&
|
||||||
specifier.importKind === 'type'),
|
specifier.importKind === 'type'),
|
||||||
false
|
false,
|
||||||
|
!options.inlineTemplate
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else if (node.type === 'ExportDefaultDeclaration') {
|
} else if (node.type === 'ExportDefaultDeclaration') {
|
||||||
@ -1027,7 +1042,8 @@ export function compileScript(
|
|||||||
node.importKind === 'type' ||
|
node.importKind === 'type' ||
|
||||||
(specifier.type === 'ImportSpecifier' &&
|
(specifier.type === 'ImportSpecifier' &&
|
||||||
specifier.importKind === 'type'),
|
specifier.importKind === 'type'),
|
||||||
true
|
true,
|
||||||
|
!options.inlineTemplate
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2051,14 +2067,14 @@ function resolveTemplateUsageCheckString(sfc: SFCDescriptor) {
|
|||||||
code += `,v${capitalize(camelize(prop.name))}`
|
code += `,v${capitalize(camelize(prop.name))}`
|
||||||
}
|
}
|
||||||
if (prop.exp) {
|
if (prop.exp) {
|
||||||
code += `,${stripStrings(
|
code += `,${processExp(
|
||||||
(prop.exp as SimpleExpressionNode).content
|
(prop.exp as SimpleExpressionNode).content
|
||||||
)}`
|
)}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (node.type === NodeTypes.INTERPOLATION) {
|
} else if (node.type === NodeTypes.INTERPOLATION) {
|
||||||
code += `,${stripStrings(
|
code += `,${processExp(
|
||||||
(node.content as SimpleExpressionNode).content
|
(node.content as SimpleExpressionNode).content
|
||||||
)}`
|
)}`
|
||||||
}
|
}
|
||||||
@ -2071,6 +2087,19 @@ function resolveTemplateUsageCheckString(sfc: SFCDescriptor) {
|
|||||||
return code
|
return code
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function processExp(exp: string) {
|
||||||
|
if (/ as \w|<.*>/.test(exp)) {
|
||||||
|
let ret = ''
|
||||||
|
// has potential type cast or generic arguments that uses types
|
||||||
|
const ast = parseExpression(exp, { plugins: ['typescript'] })
|
||||||
|
walkIdentifiers(ast, node => {
|
||||||
|
ret += `,` + node.name
|
||||||
|
})
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
return stripStrings(exp)
|
||||||
|
}
|
||||||
|
|
||||||
function stripStrings(exp: string) {
|
function stripStrings(exp: string) {
|
||||||
return exp
|
return exp
|
||||||
.replace(/'[^']*'|"[^"]*"/g, '')
|
.replace(/'[^']*'|"[^"]*"/g, '')
|
||||||
|
Loading…
x
Reference in New Issue
Block a user