Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
183 changes: 93 additions & 90 deletions packages/vue-language-core/src/generators/script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ export function generate(
emitsTypeNums: 0,
exposeRuntimeArg: undefined,
importSectionEndOffset: 0,
notOnTopTypeExports: [],
defineProps: undefined,
propsAssignName: undefined,
propsRuntimeArg: undefined,
Expand Down Expand Up @@ -274,27 +273,42 @@ export function generate(
FileRangeCapabilities.full,
]);
}
function generateExportDefault() {
// fix https://github.com/johnsoncodehk/volar/issues/1127
codes.push([
'',
'scriptSetup',
0,
{ diagnostic: true },
]);
codes.push('export default ');
}
function generateExportDefaultEndMapping() {
if (!sfc.scriptSetup) {
return;
}
// fix https://github.com/johnsoncodehk/volar/issues/1127
codes.push([
'',
'scriptSetup',
sfc.scriptSetup.content.length,
{ diagnostic: true },
]);
codes.push(`\n`);
}
function generateScriptSetupAndTemplate() {

if (!sfc.scriptSetup || !scriptSetupRanges) {
return;
}

if (!scriptRanges?.exportDefault) {
// fix https://github.com/johnsoncodehk/volar/issues/1127
codes.push([
'',
'scriptSetup',
0,
{ diagnostic: true },
]);
codes.push('export default ');
}

const definePropMirrors: Record<string, [number, number]> = {};
let scriptSetupGeneratedOffset: number | undefined;

if (sfc.scriptSetup.generic) {
if (!scriptRanges?.exportDefault) {
generateExportDefault();
}
codes.push(`(<`);
codes.push([
sfc.scriptSetup.generic,
Expand All @@ -307,16 +321,31 @@ export function generate(
}
codes.push(`>`);
codes.push('(\n');
codes.push(`__VLS_props: typeof __VLS_setup['props'] & import('vue').VNodeProps,\n`);
codes.push(`__VLS_ctx: Pick<typeof __VLS_setup, 'expose' | 'attrs' | 'emit' | 'slots'>,\n`);
codes.push('__VLS_setup = (() => {\n');
scriptSetupGeneratedOffset = generateSetupFunction(true, 'none', definePropMirrors);

//#region exposed
codes.push(`const __VLS_exposed = `);
if (scriptSetupRanges.exposeRuntimeArg) {
addVirtualCode('scriptSetup', scriptSetupRanges.exposeRuntimeArg.start, scriptSetupRanges.exposeRuntimeArg.end);
}
else {
codes.push(`{}`);
}
codes.push(';\n');
//#endregion

//#region props
if (
(scriptSetupRanges.propsRuntimeArg && scriptSetupRanges.defineProps)
|| scriptSetupRanges.defineProp.length
) {
codes.push(`__VLS_props = (() => {\n`);
if (scriptSetupRanges.propsRuntimeArg && scriptSetupRanges.defineProps) {
codes.push(`const __VLS_return = (await import('vue')).`);
addVirtualCode('scriptSetup', scriptSetupRanges.defineProps.start, scriptSetupRanges.defineProps.end);
codes.push(`const __VLS_props = `);
addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.defineProps.start, scriptSetupRanges.defineProps.end);
codes.push(`;\n`);
codes.push(`return {} as typeof __VLS_return`);
}
else if (scriptSetupRanges.defineProp.length) {
codes.push(`const __VLS_defaults = {\n`);
Expand All @@ -334,7 +363,7 @@ export function generate(
}
}
codes.push(`};\n`);
codes.push(`return {} as {\n`);
codes.push(`let __VLS_props!: {\n`);
for (const defineProp of scriptSetupRanges.defineProp) {
let propName = 'modelValue';
if (defineProp.name) {
Expand All @@ -356,70 +385,77 @@ export function generate(
}
codes.push(',\n');
}
codes.push(`}`);
codes.push(`};\n`);
}
codes.push(` & import('vue').VNodeProps`);;
if (scriptSetupRanges.slotsTypeArg) {
usedHelperTypes.ToTemplateSlots = true;
codes.push(` & { [K in keyof JSX.ElementChildrenAttribute]: __VLS_ToTemplateSlots<`);
addVirtualCode('scriptSetup', scriptSetupRanges.slotsTypeArg.start, scriptSetupRanges.slotsTypeArg.end);
addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.slotsTypeArg.start, scriptSetupRanges.slotsTypeArg.end);
codes.push(`>; }`);
}
codes.push(`;\n`);
codes.push(`})()`);
}
else {
codes.push(`__VLS_props: import('vue').VNodeProps`);
codes.push(`const __VLS_props: {}`);
if (scriptSetupRanges.slotsTypeArg) {
usedHelperTypes.ToTemplateSlots = true;
codes.push(` & { [K in keyof JSX.ElementChildrenAttribute]: __VLS_ToTemplateSlots<`);
addVirtualCode('scriptSetup', scriptSetupRanges.slotsTypeArg.start, scriptSetupRanges.slotsTypeArg.end);
addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.slotsTypeArg.start, scriptSetupRanges.slotsTypeArg.end);
codes.push(`>; }`);
}
if (scriptSetupRanges.propsTypeArg) {
codes.push(' & ');
addVirtualCode('scriptSetup', scriptSetupRanges.propsTypeArg.start, scriptSetupRanges.propsTypeArg.end);
addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.propsTypeArg.start, scriptSetupRanges.propsTypeArg.end);
}
codes.push(`;\n`);
}
codes.push(',\n');
codes.push('__VLS_ctx = (() => {\n');
scriptSetupGeneratedOffset = generateSetupFunction(true, definePropMirrors);
codes.push('return {\n');
codes.push('attrs: {} as any,\n');
codes.push('slots: {} as typeof __VLS_setup extends () => Promise<{ slots: infer T }> ? T : never,\n');
//#endregion

//#region emit
codes.push('emit: ');
//#region emits
codes.push(`const __VLS_emit = `);
if (scriptSetupRanges.emitsTypeArg) {
codes.push('{} as ');
addVirtualCode('scriptSetup', scriptSetupRanges.emitsTypeArg.start, scriptSetupRanges.emitsTypeArg.end);
codes.push(',\n');
addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.emitsTypeArg.start, scriptSetupRanges.emitsTypeArg.end);
codes.push(';\n');
}
else if (scriptSetupRanges.emitsRuntimeArg) {
codes.push(`(await import('vue')).defineEmits(`);
addVirtualCode('scriptSetup', scriptSetupRanges.emitsRuntimeArg.start, scriptSetupRanges.emitsRuntimeArg.end);
codes.push('),\n');
addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.emitsRuntimeArg.start, scriptSetupRanges.emitsRuntimeArg.end);
codes.push(');\n');
}
else {
codes.push('{} as any,\n');
codes.push('{} as any;\n');
}
//#endregion

//#region expose
codes.push('expose(__VLS_exposed: typeof __VLS_setup extends () => Promise<{ exposed: infer T }> ? T : never) { },\n');
//#endregion

codes.push('return {} as {\n');
codes.push(`props: typeof __VLS_props,\n`);
codes.push('expose(exposed: typeof __VLS_exposed): void,\n');
codes.push('attrs: any,\n');
codes.push('slots: ReturnType<typeof __VLS_template>,\n');
codes.push('emit: typeof __VLS_emit');
codes.push('};\n');
codes.push('})(),\n');
codes.push(') => ({} as JSX.Element & { __ctx?: typeof __VLS_ctx, __props?: typeof __VLS_props }))');
codes.push(') => ({} as any))');
}
else if (!sfc.script) {
// no script block, generate script setup code at root
scriptSetupGeneratedOffset = generateSetupFunction(false, 'export', definePropMirrors);
}
else {
if (!scriptRanges?.exportDefault) {
generateExportDefault();
}
codes.push('(() => {\n');
scriptSetupGeneratedOffset = generateSetupFunction(false, definePropMirrors);
codes.push(`return {} as typeof __VLS_setup extends () => Promise<infer T> ? T : never;\n`);
scriptSetupGeneratedOffset = generateSetupFunction(false, 'return', definePropMirrors);
codes.push(`})()`);
}

if (scriptRanges?.exportDefault && scriptRanges.exportDefault.expression.end !== scriptRanges.exportDefault.end) {
addVirtualCode('script', scriptRanges.exportDefault.expression.end, scriptRanges.exportDefault.end);
}
generateExportDefaultEndMapping();

if (scriptSetupGeneratedOffset !== undefined) {
for (const defineProp of scriptSetupRanges.defineProp) {
if (!defineProp.name) {
Expand All @@ -439,27 +475,13 @@ export function generate(
}
}
}

if (scriptRanges?.exportDefault && scriptRanges.exportDefault.expression.end !== scriptRanges.exportDefault.end) {
addVirtualCode('script', scriptRanges.exportDefault.expression.end, scriptRanges.exportDefault.end);
}
codes.push(`;`);
// fix https://github.com/johnsoncodehk/volar/issues/1127
codes.push([
'',
'scriptSetup',
sfc.scriptSetup.content.length,
{ diagnostic: true },
]);
codes.push(`\n`);
}
function generateSetupFunction(functional: boolean, definePropMirrors: Record<string, [number, number]>) {
function generateSetupFunction(functional: boolean, mode: 'return' | 'export' | 'none', definePropMirrors: Record<string, [number, number]>) {

if (!scriptSetupRanges || !sfc.scriptSetup) {
return;
}


const definePropProposalA = sfc.scriptSetup.content.trimStart().startsWith('// @experimentalDefinePropProposal=kevinEdition') || vueCompilerOptions.experimentalDefinePropProposal === 'kevinEdition';
const definePropProposalB = sfc.scriptSetup.content.trimStart().startsWith('// @experimentalDefinePropProposal=johnsonEdition') || vueCompilerOptions.experimentalDefinePropProposal === 'johnsonEdition';

Expand All @@ -481,23 +503,9 @@ declare function defineProp<T>(value?: T | (() => T), required?: boolean, rest?:
`.trim() + '\n');
}

codes.push('const __VLS_setup = async () => {\n');

const scriptSetupGeneratedOffset = muggle.getLength(codes) - scriptSetupRanges.importSectionEndOffset;

if (sfc.scriptSetup.generic && scriptSetupRanges.propsRuntimeArg && scriptSetupRanges.defineProps) {
addVirtualCode('scriptSetup', scriptSetupRanges.importSectionEndOffset, scriptSetupRanges.defineProps.start);
codes.push('__VLS_props');
addVirtualCode('scriptSetup', scriptSetupRanges.defineProps.end);
}
else if (sfc.scriptSetup.generic && scriptSetupRanges.propsTypeArg) {
addVirtualCode('scriptSetup', scriptSetupRanges.importSectionEndOffset, scriptSetupRanges.propsTypeArg.start);
codes.push('typeof __VLS_props');
addVirtualCode('scriptSetup', scriptSetupRanges.propsTypeArg.end);
}
else {
addVirtualCode('scriptSetup', scriptSetupRanges.importSectionEndOffset);
}
addVirtualCode('scriptSetup', scriptSetupRanges.importSectionEndOffset);

if (scriptSetupRanges.propsTypeArg && scriptSetupRanges.withDefaultsArg) {
// fix https://github.com/johnsoncodehk/volar/issues/1187
Expand Down Expand Up @@ -642,29 +650,24 @@ declare function defineProp<T>(value?: T | (() => T), required?: boolean, rest?:

generateTemplate();

if (functional) {
codes.push('return {\n');
codes.push('slots: __VLS_template(),\n');
codes.push('exposed: ');
if (scriptSetupRanges.exposeRuntimeArg) {
addVirtualCode('scriptSetup', scriptSetupRanges.exposeRuntimeArg.start, scriptSetupRanges.exposeRuntimeArg.end);
}
else {
codes.push(`{}`);
}
codes.push(',\n');
codes.push('};\n');
if (mode === 'return') {
codes.push(`return `);
}
else {
else if (mode === 'export') {
generateExportDefault();
}
if (mode === 'return' || mode === 'export') {
if (!vueCompilerOptions.skipTemplateCodegen && (htmlGen?.hasSlot || scriptSetupRanges?.slotsTypeArg)) {
usedHelperTypes.WithTemplateSlots = true;
codes.push(`return {} as __VLS_WithTemplateSlots<typeof __VLS_publicComponent, ReturnType<typeof __VLS_template>>;\n`);
codes.push(`{} as __VLS_WithTemplateSlots<typeof __VLS_publicComponent, ReturnType<typeof __VLS_template>>;`);
}
else {
codes.push(`return {} as typeof __VLS_publicComponent;\n`);
codes.push(`{} as typeof __VLS_publicComponent;`);
}
}
codes.push(`};\n`);
if (mode === 'export') {
generateExportDefaultEndMapping();
}

return scriptSetupGeneratedOffset;
}
Expand Down
5 changes: 0 additions & 5 deletions packages/vue-language-core/src/parsers/scriptSetupRanges.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ export function parseScriptSetupRanges(
) {

let foundNonImportExportNode = false;
let notOnTopTypeExports: TextRange[] = [];
let importSectionEndOffset = 0;
let withDefaultsArg: TextRange | undefined;
let propsAssignName: string | undefined;
Expand Down Expand Up @@ -48,15 +47,11 @@ export function parseScriptSetupRanges(
importSectionEndOffset = node.getStart(ast, true);
foundNonImportExportNode = true;
}
else if (isTypeExport && foundNonImportExportNode) {
notOnTopTypeExports.push(_getStartEnd(node));
}
});
ast.forEachChild(child => visitNode(child, ast));

return {
importSectionEndOffset,
notOnTopTypeExports,
bindings,
withDefaultsArg,
defineProps,
Expand Down
2 changes: 1 addition & 1 deletion packages/vue-language-service/src/languageService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ function resolvePlugins(
isSupportedDocument: (document) => document.languageId === 'jade',
vueCompilerOptions,
});
plugins.vue ??= createVuePlugin(vueCompilerOptions);
plugins.vue ??= createVuePlugin();
plugins.css ??= createCssPlugin();
plugins['pug-beautify'] ??= createPugFormatPlugin();
plugins.json ??= createJsonPlugin(settings?.json);
Expand Down
21 changes: 1 addition & 20 deletions packages/vue-language-service/src/plugins/vue.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import { parseScriptSetupRanges } from '@volar/vue-language-core';
import { LanguageServicePlugin } from '@volar/language-service';
import * as html from 'vscode-html-languageservice';
import * as vscode from 'vscode-languageserver-protocol';
import { TextDocument } from 'vscode-languageserver-textdocument';
import createHtmlPlugin from '@volar-plugins/html';
import * as vue from '@volar/vue-language-core';
import { VueCompilerOptions } from '../types';
import { loadLanguageBlocks } from './data';

let sfcDataProvider: html.IHTMLDataProvider | undefined;

export default (vueCompilerOptions: VueCompilerOptions): LanguageServicePlugin => (context) => {
export default (): LanguageServicePlugin => (context) => {

const htmlPlugin = createHtmlPlugin({ validLang: 'vue', disableCustomData: true })(context);

Expand Down Expand Up @@ -46,23 +44,6 @@ export default (vueCompilerOptions: VueCompilerOptions): LanguageServicePlugin =

const result: vscode.Diagnostic[] = [];
const sfc = vueSourceFile.sfc;

if (sfc.scriptSetup && sfc.scriptSetupAst) {
const scriptSetupRanges = parseScriptSetupRanges(_ts.module, sfc.scriptSetupAst, vueCompilerOptions);
for (const range of scriptSetupRanges.notOnTopTypeExports) {
result.push(vscode.Diagnostic.create(
{
start: document.positionAt(range.start + sfc.scriptSetup.startTagEnd),
end: document.positionAt(range.end + sfc.scriptSetup.startTagEnd),
},
'type and interface export statements must be on the top in <script setup>',
vscode.DiagnosticSeverity.Warning,
undefined,
'volar',
));
}
}

const program = _ts.languageService.getProgram();

if (program && !program.getSourceFile(vueSourceFile.mainScriptName)) {
Expand Down
Loading