Skip to content
This repository was archived by the owner on Dec 25, 2024. It is now read-only.

Commit d29eeab

Browse files
authored
fix: avoid native tag as component (#159)
fix: avoid HTML elements as component
1 parent e82dcbe commit d29eeab

File tree

11 files changed

+270
-5
lines changed

11 files changed

+270
-5
lines changed

playground/src/App.vue

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<script setup lang="ts">
22
import { defineAsyncComponent } from '@vue/composition-api'
3-
3+
import ButtonTest from './ButtonTest.vue';
44
import HelloWorld from './HelloWorld.vue'
55
66
const AsyncComponent = defineAsyncComponent(() => import('./Async.vue'))
@@ -12,6 +12,8 @@ function onUpdate(e: any) {
1212
</script>
1313
<template>
1414
<div>
15+
<ButtonTest />
16+
1517
<HelloWorld name="Vue 2" @update="onUpdate" />
1618

1719
<AsyncComponent />

playground/src/ButtonTest.vue

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<script setup lang="ts">
2+
import Button from './Foo.vue';
3+
import button from './Foo.vue';
4+
</script>
5+
6+
<template>
7+
<div>
8+
<button>{{ Button }}</button>
9+
<Button>{{ button }}</Button>
10+
</div>
11+
</template>

playground/src/Foo.vue

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
<script setup lang="ts">
2+
3+
</script>
14
<template>
2-
<div>Foo</div>
5+
<div>
6+
Button Component: <slot></slot>
7+
</div>
38
</template>

src/core/parseSFC.ts

+1
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,7 @@ export async function parseSFC(
383383
return {
384384
id,
385385
template: {
386+
tags: new Set(result?.components),
386387
components: new Set(result?.components.map(pascalize)),
387388
directives: new Set(
388389
result?.directives

src/core/transformScriptSetup.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { capitalize } from '@vue/shared'
22
import type { Node, ObjectExpression, Statement } from '@babel/types'
33
import { notNullish, partition, uniq } from '@antfu/utils'
4+
import { parserOptions } from '@vue/compiler-dom'
45
import type { ParsedSFC, ScriptSetupTransformOptions } from '../types'
56
import { applyMacros } from './macros'
67
import { getIdentifierDeclarations } from './identifiers'
@@ -54,7 +55,13 @@ export function transformScriptSetup(
5455
return t.objectProperty(id, id, false, true)
5556
})
5657

57-
const components = Array.from(template.components)
58+
const nonNativeTags = new Set(
59+
Array.from(template.tags)
60+
.filter(tag => !parserOptions.isNativeTag!(tag))
61+
.map(pascalize),
62+
)
63+
64+
const components = Array.from(nonNativeTags)
5865
.map(
5966
component =>
6067
declarationArray.find(declare => declare === component)

src/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export interface ParsedSFC {
1818
template: {
1919
/** foo-bar -> FooBar */
2020
components: Set<string>
21+
tags: Set<string>
2122
/** v-foo-bar -> fooBar */
2223
directives: Set<string>
2324
identifiers: Set<string>

test/__snapshots__/transform.test.ts.snap

+107-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
exports[`transform > fixtures > playground/src/App.vue 1`] = `
44
"<script lang=\\"ts\\">
55
import { defineAsyncComponent } from '@vue/composition-api';
6+
import ButtonTest from './ButtonTest.vue';
67
import HelloWorld from './HelloWorld.vue';
78
const AsyncComponent = defineAsyncComponent(() => import('./Async.vue'));
89
const __sfc_main = {};
@@ -16,13 +17,16 @@ __sfc_main.setup = (__props, __ctx) => {
1617
};
1718
};
1819
__sfc_main.components = Object.assign({
20+
ButtonTest,
1921
HelloWorld,
2022
AsyncComponent
2123
}, __sfc_main.components);
2224
export default __sfc_main;
2325
</script>
2426
<template>
2527
<div>
28+
<ButtonTest />
29+
2630
<HelloWorld name=\\"Vue 2\\" @update=\\"onUpdate\\" />
2731
2832
<AsyncComponent />
@@ -45,9 +49,41 @@ exports[`transform > fixtures > playground/src/Bar.vue 1`] = `
4549
"
4650
`;
4751

52+
exports[`transform > fixtures > playground/src/ButtonTest.vue 1`] = `
53+
"<script lang=\\"ts\\">
54+
import Button from './Foo.vue';
55+
import button from './Foo.vue';
56+
const __sfc_main = {};
57+
__sfc_main.setup = (__props, __ctx) => {
58+
return {
59+
Button,
60+
button
61+
};
62+
};
63+
__sfc_main.components = Object.assign({
64+
Button
65+
}, __sfc_main.components);
66+
export default __sfc_main;
67+
</script>
68+
69+
<template>
70+
<div>
71+
<button>{{ Button }}</button>
72+
<Button>{{ button }}</Button>
73+
</div>
74+
</template>
75+
"
76+
`;
77+
4878
exports[`transform > fixtures > playground/src/Foo.vue 1`] = `
49-
"<template>
50-
<div>Foo</div>
79+
"<script lang=\\"ts\\">
80+
const __sfc_main = {};
81+
export default __sfc_main;
82+
</script>
83+
<template>
84+
<div>
85+
Button Component: <slot></slot>
86+
</div>
5187
</template>
5288
"
5389
`;
@@ -330,6 +366,75 @@ export default __sfc_main;
330366
"
331367
`;
332368
369+
exports[`transform > fixtures > test/fixtures/HtmlTag.vue 1`] = `
370+
"<script lang=\\"ts\\">
371+
import Enum from './Enum.vue';
372+
import { ref } from '@vue/composition-api';
373+
const __sfc_main = {};
374+
__sfc_main.setup = (__props, __ctx) => {
375+
let p = \\"hello word\\";
376+
let Div = ref(\\"hello word\\");
377+
let h3 = 'test';
378+
let H3 = '';
379+
return {
380+
p,
381+
Div,
382+
h3,
383+
H3
384+
};
385+
};
386+
__sfc_main.components = Object.assign({
387+
Enum
388+
}, __sfc_main.components);
389+
export default __sfc_main;
390+
</script>
391+
392+
<template>
393+
<div>
394+
<Enum />
395+
<h3></h3>
396+
{{ H3 }}
397+
{{ h3 }}
398+
{{ Div }}
399+
<p>{{ p }}</p>
400+
</div>
401+
</template>
402+
"
403+
`;
404+
405+
exports[`transform > fixtures > test/fixtures/HtmlTag2.vue 1`] = `
406+
"<script lang=\\"ts\\">
407+
import Button from './DynamicStyle.vue';
408+
import button from './DynamicStyle.vue';
409+
import { defineAsyncComponent } from '@vue/composition-api';
410+
const footer = defineAsyncComponent(() => import('./ScriptOnly.vue'));
411+
import { ref } from '@vue/composition-api';
412+
const __sfc_main = {};
413+
__sfc_main.setup = (__props, __ctx) => {
414+
const p = ref(\\"hello word\\");
415+
return {
416+
Button,
417+
button,
418+
p
419+
};
420+
};
421+
__sfc_main.components = Object.assign({
422+
Button
423+
}, __sfc_main.components);
424+
export default __sfc_main;
425+
</script>
426+
427+
<template>
428+
<div>
429+
<p>{{ p }}</p>
430+
<button>{{ Button }}</button>
431+
<Button>{{ button }}</Button>
432+
<footer>FOOTER</footer>
433+
</div>
434+
</template>
435+
"
436+
`;
437+
333438
exports[`transform > fixtures > test/fixtures/JSLongComment.vue 1`] = `
334439
"<script lang=\\"ts\\">
335440
const __sfc_main = {};

test/fixtures/HtmlTag.vue

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<script setup lang="ts">
2+
import Enum from './Enum.vue'
3+
import { ref } from '@vue/composition-api';
4+
5+
let p = "hello word";
6+
let Div = ref("hello word");
7+
let h3 = 'test'
8+
let H3 = ''
9+
</script>
10+
11+
<template>
12+
<div>
13+
<Enum />
14+
<h3></h3>
15+
{{ H3 }}
16+
{{ h3 }}
17+
{{ Div }}
18+
<p>{{ p }}</p>
19+
</div>
20+
</template>

test/fixtures/HtmlTag2.vue

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<script setup lang="ts">
2+
import Button from './DynamicStyle.vue';
3+
import button from './DynamicStyle.vue';
4+
import { defineAsyncComponent } from '@vue/composition-api'
5+
const footer = defineAsyncComponent(() => import('./ScriptOnly.vue'))
6+
7+
import { ref } from '@vue/composition-api';
8+
9+
const p = ref("hello word");
10+
11+
</script>
12+
13+
<template>
14+
<div>
15+
<p>{{ p }}</p>
16+
<button>{{ Button }}</button>
17+
<Button>{{ button }}</Button>
18+
<footer>FOOTER</footer>
19+
</div>
20+
</template>

test/identifiers.test.ts

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ describe('identifiers', () => {
99
['var a = 1', ['a']],
1010
['import { foo, t as bar } from "z"', ['foo', 'bar']],
1111
['import foo from "z"', ['foo']],
12+
['import Button from \'./DynamicStyle.vue\'', ['Button']],
13+
['import button from \'./DynamicStyle.vue\'', ['button']],
1214
['import * as foo from "z"', ['foo']],
1315
['function foo(bar) {const a = z}', ['foo']],
1416
['console.log(foo)', []],

test/nativeTag.test.ts

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import { describe, expect, it } from 'vitest'
2+
import { transform } from '../src'
3+
4+
describe('filter native tags as vue components', () => {
5+
describe('no components output', () => {
6+
const cases: string[] = [
7+
`
8+
<script setup lang="ts">
9+
import button from './DynamicStyle.vue';
10+
</script>
11+
12+
<template>
13+
<button>{{ Button }}</button>
14+
</template>
15+
`,
16+
`
17+
<script setup lang="ts">
18+
let p='hello'
19+
let Div='hello'
20+
</script>
21+
22+
<template>
23+
<div>
24+
<p>{{ p }}</p>
25+
<Button>{{ Div }}</Button>
26+
</div>
27+
</template>
28+
`,
29+
`
30+
<script setup lang="ts">
31+
let svg='hello'
32+
</script>
33+
34+
<template>
35+
<div>
36+
<p>{{ svg }}</p>
37+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 283.46 283.46">
38+
<defs>
39+
<style>
40+
.cls-1{fill:#231815;}
41+
@media (prefers-color-scheme: dark) { .cls-1{fill:#ffffff;} }
42+
</style>
43+
</defs>
44+
<path class="cls-1" d="M144.89,89.86c-33.46,0-54.44,14.56-66.14,26.76a86,86,0,0,0-23.69,58.94c0,22.64,8.81,43.48,24.8,58.67,15.7,14.92,36.9,23.14,59.68,23.14,23.81,0,46-8.49,62.49-23.91,17-15.9,26.37-37.93,26.37-62C228.4,120.37,185.94,89.86,144.89,89.86Zm.49,153.67a61.49,61.49,0,0,1-46.45-20.4c-12.33-13.76-18.85-32.64-18.85-54.62,0-20.7,6.07-37.67,17.57-49.07,10.11-10,24.39-15.62,40.19-15.74,19,0,35.22,6.56,46.76,19,12.6,13.58,19.27,34,19.27,58.95C203.87,224.39,174.49,243.53,145.38,243.53Z"/>
45+
<polygon class="cls-1" points="198.75 74.96 179.45 74.96 142.09 37.83 104.51 74.96 86.14 74.96 138.09 24.25 146.81 24.25 198.75 74.96"/>
46+
</svg>
47+
</div>
48+
</template>
49+
`,
50+
]
51+
52+
for (const input of cases) {
53+
it(input, async () => {
54+
const result = await transform(input, 'Lang.vue', { reactivityTransform: true })
55+
expect(result?.code.includes('__sfc_main.components')).toEqual(false)
56+
})
57+
}
58+
})
59+
60+
it('capitalized native tags as components', async () => {
61+
const input = `
62+
<script setup lang="ts">
63+
import Button from './DynamicStyle.vue';
64+
</script>
65+
66+
<template>
67+
<Button>{{ Button }}</Button>
68+
</template>
69+
`
70+
const result = await transform(input, 'Lang.vue', { reactivityTransform: true })
71+
expect(result?.code.includes('__sfc_main.components = Object.assign({\n Button\n}, __sfc_main.components);')).toEqual(true)
72+
})
73+
74+
it('keep non-native components', async () => {
75+
const input = `
76+
<script setup lang="ts">
77+
import Button from './DynamicStyle.vue';
78+
import DynamicStyle from './DynamicStyle.vue';
79+
let p='hello'
80+
</script>
81+
82+
<template>
83+
<dynamic-style/>
84+
<p>{{ p }}</p>
85+
<button>{{ Button }}</button>
86+
</template>
87+
`
88+
const result = await transform(input, 'Lang.vue', { reactivityTransform: true })
89+
expect(result?.code.includes('__sfc_main.components = Object.assign({\n DynamicStyle\n}, __sfc_main.components);')).toEqual(true)
90+
})
91+
})

0 commit comments

Comments
 (0)