mirror of
https://github.com/withastro/astro.git
synced 2025-04-07 23:41:43 -05:00
fix(vue): vue regular script block exports not being recognized inside editor (#8998)
Co-authored-by: Erika <3019731+Princesseuh@users.noreply.github.com>
This commit is contained in:
parent
d979b8f0a8
commit
14e586cc77
9 changed files with 105 additions and 14 deletions
.changeset
packages/integrations/vue
pnpm-lock.yaml
5
.changeset/two-oranges-knock.md
Normal file
5
.changeset/two-oranges-knock.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'@astrojs/vue': patch
|
||||
---
|
||||
|
||||
Adds editor support for Vue non setup script blocks and Vue 3.3 generics.
|
|
@ -51,6 +51,7 @@
|
|||
"astro-scripts": "workspace:*",
|
||||
"chai": "^4.3.7",
|
||||
"linkedom": "^0.15.1",
|
||||
"cheerio": "1.0.0-rc.12",
|
||||
"mocha": "^10.2.0",
|
||||
"vite": "^4.4.9",
|
||||
"vue": "^3.3.4"
|
||||
|
|
|
@ -6,7 +6,7 @@ export function toTSX(code: string, className: string): string {
|
|||
// NOTE: As you can expect, using regexes for this is not exactly the most reliable way of doing things
|
||||
// However, I couldn't figure out a way to do it using Vue's compiler, I tried looking at how Volar does it, but I
|
||||
// didn't really understand everything happening there and it seemed to be pretty Volar-specific. I do believe
|
||||
// someone more knowledgable on Vue's internals could figure it out, but since this solution is good enough for most
|
||||
// someone more knowledgeable on Vue's internals could figure it out, but since this solution is good enough for most
|
||||
// Vue components (and it's an improvement over, well, nothing), it's alright, I think
|
||||
try {
|
||||
const parsedResult = parse(code);
|
||||
|
@ -18,29 +18,39 @@ export function toTSX(code: string, className: string): string {
|
|||
`;
|
||||
}
|
||||
|
||||
if (parsedResult.descriptor.scriptSetup) {
|
||||
const definePropsType =
|
||||
parsedResult.descriptor.scriptSetup.content.match(/defineProps<([\s\S]+)>/m);
|
||||
// Vue supports 2 type of script blocks: setup and non-setup
|
||||
const regularScriptBlockContent = parsedResult.descriptor.script?.content ?? '';
|
||||
const { scriptSetup } = parsedResult.descriptor;
|
||||
|
||||
if (scriptSetup) {
|
||||
const definePropsType = scriptSetup.content.match(/defineProps<([\S\s]+?)>\s?\(\)/m);
|
||||
const propsGeneric = scriptSetup.attrs.generic;
|
||||
const propsGenericType = propsGeneric ? `<${propsGeneric}>` : '';
|
||||
|
||||
if (definePropsType) {
|
||||
result = `
|
||||
${parsedResult.descriptor.scriptSetup.content}
|
||||
${regularScriptBlockContent}
|
||||
${scriptSetup.content}
|
||||
|
||||
export default function ${className}__AstroComponent_(_props: ${definePropsType[1]}): any {
|
||||
export default function ${className}__AstroComponent_${propsGenericType}(_props: ${definePropsType[1]}): any {
|
||||
<div></div>
|
||||
}
|
||||
`;
|
||||
} else {
|
||||
const defineProps =
|
||||
parsedResult.descriptor.scriptSetup.content.match(/defineProps\([\s\S]+\)/m);
|
||||
// TODO. Find a way to support generics when using defineProps without passing explicit types.
|
||||
// Right now something like this `defineProps({ prop: { type: Array as PropType<T[]> } })`
|
||||
// won't be correctly typed in Astro.
|
||||
const defineProps = scriptSetup.content.match(/defineProps\([\s\S]+\)/m);
|
||||
|
||||
if (defineProps) {
|
||||
result = `
|
||||
import { defineProps } from '@vue/runtime-core';
|
||||
|
||||
${regularScriptBlockContent}
|
||||
|
||||
const Props = ${defineProps[0]}
|
||||
|
||||
export default function ${className}__AstroComponent_(_props: typeof Props): any {
|
||||
export default function ${className}__AstroComponent_${propsGenericType}(_props: typeof Props): any {
|
||||
<div></div>
|
||||
}
|
||||
`;
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import { loadFixture } from './test-utils.js';
|
||||
import { expect } from 'chai';
|
||||
import { load as cheerioLoad } from 'cheerio';
|
||||
import { parseHTML } from 'linkedom';
|
||||
|
||||
describe('App Entrypoint', () => {
|
||||
/** @type {import('./test-utils').Fixture} */
|
||||
let fixture;
|
||||
|
@ -13,11 +15,21 @@ describe('App Entrypoint', () => {
|
|||
});
|
||||
|
||||
it('loads during SSR', async () => {
|
||||
const data = await fixture.readFile('/index.html');
|
||||
const { document } = parseHTML(data);
|
||||
const bar = document.querySelector('#foo > #bar');
|
||||
expect(bar).not.to.be.undefined;
|
||||
expect(bar.textContent).to.eq('works');
|
||||
const html = await fixture.readFile('/index.html');
|
||||
const $ = cheerioLoad(html);
|
||||
|
||||
// test 1: basic component renders
|
||||
expect($('#foo > #bar').text()).to.eq('works');
|
||||
|
||||
// test 2: component with multiple script blocks renders and exports
|
||||
// values from non setup block correctly
|
||||
expect($('#multiple-script-blocks').text()).to.equal('2 4');
|
||||
|
||||
// test 3: component using generics renders
|
||||
expect($('#generics').text()).to.equal('generic');
|
||||
|
||||
// test 4: component using generics and multiple script blocks renders
|
||||
expect($('#generics-and-blocks').text()).to.equal('1 3!!!');
|
||||
});
|
||||
|
||||
it('setup included in renderer bundle', async () => {
|
||||
|
|
13
packages/integrations/vue/test/fixtures/app-entrypoint/src/components/Generics.vue
vendored
Normal file
13
packages/integrations/vue/test/fixtures/app-entrypoint/src/components/Generics.vue
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
<script lang="ts" setup generic="T extends 'generic' | 'not-generic'">
|
||||
interface GenericComponentProps {
|
||||
value: T
|
||||
}
|
||||
|
||||
defineProps<GenericComponentProps>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="generics">
|
||||
{{ value }}
|
||||
</div>
|
||||
</template>
|
18
packages/integrations/vue/test/fixtures/app-entrypoint/src/components/GenericsAndBlocks.vue
vendored
Normal file
18
packages/integrations/vue/test/fixtures/app-entrypoint/src/components/GenericsAndBlocks.vue
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
<script lang="ts">
|
||||
export const customFormatter = (num: number) => `${num * 3}!!!`
|
||||
|
||||
export type FormatNumber<T> = (num: T) => string;
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup generic="T extends number, Formatter extends FormatNumber<T>">
|
||||
const props = defineProps<{
|
||||
value: T,
|
||||
formatter: Formatter
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="generics-and-blocks">
|
||||
{{ value }} {{ props.formatter(props.value) }}
|
||||
</div>
|
||||
</template>
|
18
packages/integrations/vue/test/fixtures/app-entrypoint/src/components/MultipleScriptBlocks.vue
vendored
Normal file
18
packages/integrations/vue/test/fixtures/app-entrypoint/src/components/MultipleScriptBlocks.vue
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
<script lang="ts">
|
||||
export const doubleNumber = (num: number) => num * 2
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
defineProps({
|
||||
value: {
|
||||
type: Number,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="multiple-script-blocks">
|
||||
{{ doubleNumber(value) }} <slot />
|
||||
</div>
|
||||
</template>
|
|
@ -1,5 +1,8 @@
|
|||
---
|
||||
import Foo from '../components/Foo.vue';
|
||||
import MultipleScriptBlocks, { doubleNumber } from '../components/MultipleScriptBlocks.vue';
|
||||
import GenericComponent from '../components/Generics.vue';
|
||||
import GenericsAndBlocks, { customFormatter } from '../components/GenericsAndBlocks.vue';
|
||||
---
|
||||
|
||||
<html>
|
||||
|
@ -8,5 +11,13 @@ import Foo from '../components/Foo.vue';
|
|||
</head>
|
||||
<body>
|
||||
<Foo client:load />
|
||||
|
||||
<MultipleScriptBlocks value={1}>
|
||||
{doubleNumber(2)}
|
||||
</MultipleScriptBlocks>
|
||||
|
||||
<GenericComponent value={'generic'} />
|
||||
|
||||
<GenericsAndBlocks value={1} formatter={customFormatter} />
|
||||
</body>
|
||||
</html>
|
||||
|
|
3
pnpm-lock.yaml
generated
3
pnpm-lock.yaml
generated
|
@ -4803,6 +4803,9 @@ importers:
|
|||
chai:
|
||||
specifier: ^4.3.7
|
||||
version: 4.3.10
|
||||
cheerio:
|
||||
specifier: 1.0.0-rc.12
|
||||
version: 1.0.0-rc.12
|
||||
linkedom:
|
||||
specifier: ^0.15.1
|
||||
version: 0.15.6
|
||||
|
|
Loading…
Add table
Reference in a new issue