Skip to content

Commit

Permalink
fix(compiler-sfc): fix import usage check for lowercase imported comp…
Browse files Browse the repository at this point in the history
…onents

fix #4358
  • Loading branch information
yyx990803 committed Aug 17, 2021
1 parent 03abc25 commit 57f1081
Show file tree
Hide file tree
Showing 3 changed files with 147 additions and 29 deletions.
Expand Up @@ -126,6 +126,82 @@ return { props, a, emit }
}"
`;
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 _defineComponent({
setup(__props, { expose }) {
expose()
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 _defineComponent({
setup(__props, { expose }) {
expose()
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 _defineComponent({
setup(__props, { expose }) {
expose()
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 _defineComponent({
setup(__props, { expose }) {
expose()
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 _defineComponent({
setup(__props, { expose }) {
expose()
return { x, z, x$y }
}
})"
`;
exports[`SFC compile <script setup> errors should allow defineProps/Emit() referencing imported binding 1`] = `
"import { bar } from './bar'
Expand Down Expand Up @@ -204,22 +280,6 @@ return { x }
}"
`;
exports[`SFC compile <script setup> imports imports not used in <template> should not be exposed 1`] = `
"import { defineComponent as _defineComponent } from 'vue'
import { FooBar, FooBaz, FooQux, vMyDir, x, y, z, x$y, VAR, VAR2, VAR3, Last } from './x'
export default _defineComponent({
setup(__props, { expose }) {
expose()
const fooBar: FooBar = 1
return { fooBar, FooBaz, FooQux, vMyDir, x, z, x$y, VAR, VAR3, Last }
}
})"
`;
exports[`SFC compile <script setup> imports should allow defineProps/Emit at the start of imports 1`] = `
"import { ref } from 'vue'
Expand Down
82 changes: 70 additions & 12 deletions packages/compiler-sfc/__tests__/compileScript.spec.ts
Expand Up @@ -209,32 +209,90 @@ defineExpose({ foo: 123 })
content.lastIndexOf(`import { x }`)
)
})
})

test('imports not used in <template> should not be exposed', () => {
// in dev mode, declared bindings are returned as an object from setup()
// when using TS, users may import types which should not be returned as
// values, so we need to check import usage in the template to determine
// what to be returned.
describe('dev mode import usage check', () => {
test('components', () => {
const { content } = compile(`
<script setup lang="ts">
import { FooBar, FooBaz, FooQux, vMyDir, x, y, z, x$y, VAR, VAR2, VAR3, Last } from './x'
import { FooBar, FooBaz, FooQux, foo } from './x'
const fooBar: FooBar = 1
</script>
<template>
<FooBaz v-my-dir>{{ x }} {{ yy }} {{ x$y }}</FooBaz>
<FooBaz></FooBaz>
<foo-qux/>
<div :id="z + 'y'">FooBar</div>
{{ \`\${VAR}VAR2\${VAR3}\` }}
<Last/>
<foo/>
FooBar
</template>
`)
// FooBar: should not be matched by plain text
// FooBar: should not be matched by plain text or incorrect case
// FooBaz: used as PascalCase component
// FooQux: used as kebab-case component
// vMyDir: used as directive v-my-dir
// foo: lowercase component
expect(content).toMatch(`return { fooBar, FooBaz, FooQux, foo }`)
assertCode(content)
})

test('directive', () => {
const { content } = compile(`
<script setup lang="ts">
import { vMyDir } from './x'
</script>
<template>
<div v-my-dir></div>
</template>
`)
expect(content).toMatch(`return { vMyDir }`)
assertCode(content)
})

test('vue interpolations', () => {
const { content } = compile(`
<script setup lang="ts">
import { x, y, z, x$y } from './x'
</script>
<template>
<div :id="z + 'y'">{{ x }} {{ yy }} {{ x$y }}</div>
</template>
`)
// x: used in interpolation
// y: should not be matched by {{ yy }} or 'y' in binding exps
// x$y: #4274 should escape special chars when creating Regex
// VAR & VAR3: #4340 interpolations in tempalte strings
expect(content).toMatch(
`return { fooBar, FooBaz, FooQux, vMyDir, x, z, x$y, VAR, VAR3, Last }`
)
expect(content).toMatch(`return { x, z, x$y }`)
assertCode(content)
})

// #4340 interpolations in tempalte strings
test('js template string interpolations', () => {
const { content } = compile(`
<script setup lang="ts">
import { VAR, VAR2, VAR3 } from './x'
</script>
<template>
{{ \`\${VAR}VAR2\${VAR3}\` }}
</template>
`)
// VAR2 should not be matched
expect(content).toMatch(`return { VAR, VAR3 }`)
assertCode(content)
})

// edge case: last tag in template
test('last tag', () => {
const { content } = compile(`
<script setup lang="ts">
import { FooBaz, Last } from './x'
</script>
<template>
<FooBaz></FooBaz>
<Last/>
</template>
`)
expect(content).toMatch(`return { FooBaz, Last }`)
assertCode(content)
})
})
Expand Down
2 changes: 1 addition & 1 deletion packages/compiler-sfc/src/compileScript.ts
Expand Up @@ -2212,7 +2212,7 @@ function resolveTemplateUsageCheckString(sfc: SFCDescriptor) {
!parserOptions.isNativeTag!(node.tag) &&
!parserOptions.isBuiltInComponent!(node.tag)
) {
code += `,${capitalize(camelize(node.tag))}`
code += `,${camelize(node.tag)},${capitalize(camelize(node.tag))}`
}
for (let i = 0; i < node.props.length; i++) {
const prop = node.props[i]
Expand Down

0 comments on commit 57f1081

Please sign in to comment.