, required: true } };\n`);
- }
- }
- if (usedHelperTypes.MergePropDefaults) {
- codes.push(`type __VLS_WithDefaults = {
- // use 'keyof Pick
' instead of 'keyof P' to keep props jsdoc
- [K in keyof Pick
]: K extends keyof D ? __VLS_Prettify
: P[K]
- };\n`);
- codes.push(`type __VLS_Prettify = { [K in keyof T]: T[K]; } & {};\n`);
- }
- if (usedHelperTypes.WithTemplateSlots) {
- codes.push(
- `type __VLS_WithTemplateSlots = T & { new(): {\n`,
- `${getSlotsPropertyName(vueCompilerOptions.target)}: S;\n`,
- );
- if (vueCompilerOptions.jsxSlots) {
- usedHelperTypes.PropsChildren = true;
- codes.push(`$props: __VLS_PropsChildren;\n`);
- }
- codes.push(
- `} };\n`,
- );
- }
- if (usedHelperTypes.PropsChildren) {
- codes.push(`type __VLS_PropsChildren = { [K in keyof (boolean extends (JSX.ElementChildrenAttribute extends never ? true : false) ? never : JSX.ElementChildrenAttribute)]?: S; };\n`);
}
}
- function generateSrc() {
+ function* generateSrc(): Generator {
if (!script?.src)
return;
let src = script.src;
- if (src.endsWith('.d.ts')) src = src.substring(0, src.length - '.d.ts'.length);
- else if (src.endsWith('.ts')) src = src.substring(0, src.length - '.ts'.length);
- else if (src.endsWith('.tsx')) src = src.substring(0, src.length - '.tsx'.length) + '.jsx';
+ if (src.endsWith('.d.ts'))
+ src = src.substring(0, src.length - '.d.ts'.length);
+ else if (src.endsWith('.ts'))
+ src = src.substring(0, src.length - '.ts'.length);
+ else if (src.endsWith('.tsx'))
+ src = src.substring(0, src.length - '.tsx'.length) + '.jsx';
- if (!src.endsWith('.js') && !src.endsWith('.jsx')) src = src + '.js';
+ if (!src.endsWith('.js') && !src.endsWith('.jsx'))
+ src = src + '.js';
- codes.push(`export * from `);
- codes.push([
+ yield _(`export * from `);
+ yield _([
`'${src}'`,
'script',
- [script.srcOffset - 1, script.srcOffset + script.src.length + 1],
- {
- ...FileRangeCapabilities.full,
- rename: src === script.src ? true : {
- normalize: undefined,
- apply(newName) {
- if (
- newName.endsWith('.jsx')
- || newName.endsWith('.js')
- ) {
+ script.srcOffset - 1,
+ enableAllFeatures({
+ navigation: src === script.src ? true : {
+ shouldRename: () => false,
+ resolveRenameEditText(newName) {
+ if (newName.endsWith('.jsx') || newName.endsWith('.js')) {
newName = newName.split('.').slice(0, -1).join('.');
}
if (script?.src?.endsWith('.d.ts')) {
@@ -177,214 +362,196 @@ export function generate(
return newName;
},
},
- },
+ }),
]);
- codes.push(`;\n`);
- codes.push(`export { default } from '${src}';\n`);
+ yield _(`;\n`);
+ yield _(`export { default } from '${src}';\n`);
}
- function generateScriptContentBeforeExportDefault() {
+ function* generateScriptContentBeforeExportDefault(): Generator {
if (!script)
return;
- if (!!scriptSetup && scriptRanges?.exportDefault) {
- addVirtualCode('script', 0, scriptRanges.exportDefault.expression.start);
- }
- else {
- let isExportRawObject = false;
- if (scriptRanges?.exportDefault) {
- isExportRawObject = script.content.substring(scriptRanges.exportDefault.expression.start, scriptRanges.exportDefault.expression.end).startsWith('{');
- }
- if (isExportRawObject && vueCompilerOptions.optionsWrapper.length === 2 && scriptRanges?.exportDefault) {
- addVirtualCode('script', 0, scriptRanges.exportDefault.expression.start);
- codes.push(vueCompilerOptions.optionsWrapper[0]);
- {
- codes.push(['', 'script', scriptRanges.exportDefault.expression.start, {
- __hint: {
- setting: 'vue.inlayHints.optionsWrapper',
- label: vueCompilerOptions.optionsWrapper[0],
- tooltip: [
- 'This is virtual code that is automatically wrapped for type support, it does not affect your runtime behavior, you can customize it via `vueCompilerOptions.optionsWrapper` option in tsconfig / jsconfig.',
- 'To hide it, you can set `"vue.inlayHints.optionsWrapper": false` in IDE settings.',
- ].join('\n\n'),
- }
- } as any]);
- addVirtualCode('script', scriptRanges.exportDefault.expression.start, scriptRanges.exportDefault.expression.end);
- codes.push(['', 'script', scriptRanges.exportDefault.expression.end, {
- __hint: {
- setting: 'vue.inlayHints.optionsWrapper',
- label: vueCompilerOptions.optionsWrapper[1],
- tooltip: '',
- }
- } as any]);
- }
- codes.push(vueCompilerOptions.optionsWrapper[1]);
- addVirtualCode('script', scriptRanges.exportDefault.expression.end, script.content.length);
+ if (!!scriptSetup && scriptRanges?.exportDefault)
+ return yield _(generateSourceCode(script, 0, scriptRanges.exportDefault.expression.start));
+
+ let isExportRawObject = false;
+ if (scriptRanges?.exportDefault)
+ isExportRawObject = script.content.substring(scriptRanges.exportDefault.expression.start, scriptRanges.exportDefault.expression.end).startsWith('{');
+
+ if (!isExportRawObject || !vueCompilerOptions.optionsWrapper.length || !scriptRanges?.exportDefault)
+ return yield _(generateSourceCode(script, 0, script.content.length));
+
+ yield _(generateSourceCode(script, 0, scriptRanges.exportDefault.expression.start));
+ yield _(vueCompilerOptions.optionsWrapper[0]);
+ yield _(['', 'script', scriptRanges.exportDefault.expression.start, disableAllFeatures({
+ __hint: {
+ setting: 'vue.inlayHints.optionsWrapper',
+ label: vueCompilerOptions.optionsWrapper[0],
+ tooltip: [
+ 'This is virtual code that is automatically wrapped for type support, it does not affect your runtime behavior, you can customize it via `vueCompilerOptions.optionsWrapper` option in tsconfig / jsconfig.',
+ 'To hide it, you can set `"vue.inlayHints.optionsWrapper": false` in IDE settings.',
+ ].join('\n\n'),
}
- else {
- addVirtualCode('script', 0, script.content.length);
+ })]);
+ yield _(generateSourceCode(script, scriptRanges.exportDefault.expression.start, scriptRanges.exportDefault.expression.end));
+ yield _(['', 'script', scriptRanges.exportDefault.expression.end, disableAllFeatures({
+ __hint: {
+ setting: 'vue.inlayHints.optionsWrapper',
+ label: vueCompilerOptions.optionsWrapper[1],
+ tooltip: '',
}
- }
+ })]);
+ yield _(vueCompilerOptions.optionsWrapper[1]);
+ yield _(generateSourceCode(script, scriptRanges.exportDefault.expression.end, script.content.length));
}
- function generateScriptContentAfterExportDefault() {
+ function* generateScriptContentAfterExportDefault(): Generator {
if (!script)
return;
- if (!!scriptSetup && scriptRanges?.exportDefault) {
- addVirtualCode('script', scriptRanges.exportDefault.expression.end, script.content.length);
- }
+ if (!!scriptSetup && scriptRanges?.exportDefault)
+ yield _(generateSourceCode(script, scriptRanges.exportDefault.expression.end, script.content.length));
}
- function generateScriptSetupImports() {
-
- if (!scriptSetup)
+ function* generateScriptSetupImports(): Generator {
+ if (!scriptSetup || !scriptSetupRanges)
return;
- if (!scriptSetupRanges)
- return;
-
- codes.push([
+ yield _([
scriptSetup.content.substring(0, Math.max(scriptSetupRanges.importSectionEndOffset, scriptSetupRanges.leadingCommentEndOffset)) + '\n',
'scriptSetup',
0,
- FileRangeCapabilities.full,
+ enableAllFeatures({}),
]);
}
- function generateScriptSetupAndTemplate() {
-
- if (!scriptSetup || !scriptSetupRanges) {
+ function* generateScriptSetupAndTemplate(): Generator {
+ if (!scriptSetup || !scriptSetupRanges)
return;
- }
- const definePropMirrors: Record = {};
- let scriptSetupGeneratedOffset: number | undefined;
+ const definePropMirrors = new Map();
if (scriptSetup.generic) {
if (!scriptRanges?.exportDefault) {
- codes.push('export default ');
+ yield _(`export default `);
}
- codes.push(`(<`);
- codes.push([
+ yield _(`(<`);
+ yield _([
scriptSetup.generic,
scriptSetup.name,
scriptSetup.genericOffset,
- FileRangeCapabilities.full,
+ enableAllFeatures({}),
]);
- if (!scriptSetup.generic.endsWith(',')) {
- codes.push(`,`);
+ if (!scriptSetup.generic.endsWith(`,`)) {
+ yield _(`,`);
}
- codes.push(`>`);
- codes.push('(\n');
- codes.push(`__VLS_props: Awaited['props'],\n`);
- codes.push(`__VLS_ctx?: __VLS_Prettify, 'attrs' | 'emit' | 'slots'>>,\n`); // use __VLS_Prettify for less dts code
- codes.push(`__VLS_expose?: NonNullable>['expose'],\n`);
- codes.push('__VLS_setup = (async () => {\n');
- scriptSetupGeneratedOffset = generateSetupFunction(true, 'none', definePropMirrors);
+ yield _(`>`);
+ yield _(`(\n`
+ + ` __VLS_props: Awaited['props'],\n`
+ + ` __VLS_ctx?: ${helperTypes.Prettify.name}, 'attrs' | 'emit' | 'slots'>>,\n` // use __VLS_Prettify for less dts code
+ + ` __VLS_expose?: NonNullable>['expose'],\n`
+ + ` __VLS_setup = (async () => {\n`);
+
+ yield* generateSetupFunction(true, 'none', definePropMirrors);
//#region props
- codes.push(`const __VLS_fnComponent = `);
- codes.push(`(await import('${vueCompilerOptions.lib}')).defineComponent({\n`);
+ yield _(`const __VLS_fnComponent = `
+ + `(await import('${vueCompilerOptions.lib}')).defineComponent({\n`);
if (scriptSetupRanges.props.define?.arg) {
- codes.push(`props: `);
- addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.props.define.arg.start, scriptSetupRanges.props.define.arg.end);
- codes.push(`,\n`);
-
+ yield _(` props: `);
+ yield _(generateSourceCodeForExtraReference(scriptSetup, scriptSetupRanges.props.define.arg.start, scriptSetupRanges.props.define.arg.end));
+ yield _(`,\n`);
}
if (scriptSetupRanges.emits.define) {
- codes.push(
- `emits: ({} as __VLS_NormalizeEmits),\n`,
- );
+ yield _(` emits: ({} as __VLS_NormalizeEmits),\n`);
}
- codes.push(`});\n`);
+ yield _(`});\n`);
+
if (scriptSetupRanges.defineProp.length) {
- codes.push(`const __VLS_defaults = {\n`);
+ yield _(`const __VLS_defaults = {\n`);
for (const defineProp of scriptSetupRanges.defineProp) {
if (defineProp.defaultValue) {
if (defineProp.name) {
- codes.push(scriptSetup.content.substring(defineProp.name.start, defineProp.name.end));
+ yield _(scriptSetup.content.substring(defineProp.name.start, defineProp.name.end));
}
else {
- codes.push('modelValue');
+ yield _('modelValue');
}
- codes.push(`: `);
- codes.push(scriptSetup.content.substring(defineProp.defaultValue.start, defineProp.defaultValue.end));
- codes.push(`,\n`);
+ yield _(`: `);
+ yield _(scriptSetup.content.substring(defineProp.defaultValue.start, defineProp.defaultValue.end));
+ yield _(`,\n`);
}
}
- codes.push(`};\n`);
+ yield _(`};\n`);
}
- codes.push(`let __VLS_fnPropsTypeOnly!: {}`); // TODO: reuse __VLS_fnPropsTypeOnly even without generic, and remove __VLS_propsOption_defineProp
+
+ yield _(`let __VLS_fnPropsTypeOnly!: {}`); // TODO: reuse __VLS_fnPropsTypeOnly even without generic, and remove __VLS_propsOption_defineProp
if (scriptSetupRanges.props.define?.typeArg) {
- codes.push(` & `);
- addVirtualCode('scriptSetup', scriptSetupRanges.props.define.typeArg.start, scriptSetupRanges.props.define.typeArg.end);
+ yield _(` & `);
+ yield _(generateSourceCode(scriptSetup, scriptSetupRanges.props.define.typeArg.start, scriptSetupRanges.props.define.typeArg.end));
}
if (scriptSetupRanges.defineProp.length) {
- codes.push(` & {\n`);
+ yield _(` & {\n`);
for (const defineProp of scriptSetupRanges.defineProp) {
let propName = 'modelValue';
if (defineProp.name) {
propName = scriptSetup.content.substring(defineProp.name.start, defineProp.name.end);
- const propMirrorStart = muggle.getLength(codes);
- definePropMirrors[propName] = [propMirrorStart, propMirrorStart + propName.length];
+ definePropMirrors.set(propName, getGeneratedLength());
}
- codes.push(`${propName}${defineProp.required ? '' : '?'}: `);
+ yield _(`${propName}${defineProp.required ? '' : '?'}: `);
if (defineProp.type) {
- codes.push(scriptSetup.content.substring(defineProp.type.start, defineProp.type.end));
+ yield _(scriptSetup.content.substring(defineProp.type.start, defineProp.type.end));
}
else if (defineProp.defaultValue) {
- codes.push(`typeof __VLS_defaults['`);
- codes.push(propName);
- codes.push(`']`);
+ yield _(`typeof __VLS_defaults['`);
+ yield _(propName);
+ yield _(`']`);
}
else {
- codes.push(`any`);
+ yield _(`any`);
}
- codes.push(',\n');
+ yield _(',\n');
}
- codes.push(`}`);
+ yield _(`}`);
}
- codes.push(`;\n`);
- codes.push(`let __VLS_fnPropsDefineComponent!: InstanceType['$props']`);
- codes.push(`;\n`);
- codes.push(`let __VLS_fnPropsSlots!: `);
+ yield _(`;\n`);
+
+ yield _(`let __VLS_fnPropsDefineComponent!: InstanceType['$props'];\n`);
+ yield _(`let __VLS_fnPropsSlots!: `);
if (scriptSetupRanges.slots.define && vueCompilerOptions.jsxSlots) {
- usedHelperTypes.PropsChildren = true;
- codes.push(`__VLS_PropsChildren`);
+ yield _(`${helperTypes.PropsChildren.name}`);
}
else {
- codes.push(`{}`);
- }
- codes.push(`;\n`);
- codes.push(
- `let __VLS_defaultProps!: `,
- `import('${vueCompilerOptions.lib}').VNodeProps`,
- `& import('${vueCompilerOptions.lib}').AllowedComponentProps`,
- `& import('${vueCompilerOptions.lib}').ComponentCustomProps`,
- `;\n`,
- );
+ yield _(`{}`);
+ }
+ yield _(`;\n`);
+
+ yield _(`let __VLS_defaultProps!:\n`
+ + ` import('${vueCompilerOptions.lib}').VNodeProps\n`
+ + ` & import('${vueCompilerOptions.lib}').AllowedComponentProps\n`
+ + ` & import('${vueCompilerOptions.lib}').ComponentCustomProps;\n`);
//#endregion
- codes.push('return {} as {\n');
- codes.push(`props: __VLS_Prettify<__VLS_OmitKeepDiscriminatedUnion> & typeof __VLS_fnPropsSlots & typeof __VLS_defaultProps,\n`);
- codes.push(`expose(exposed: import('${vueCompilerOptions.lib}').ShallowUnwrapRef<${scriptSetupRanges.expose.define ? 'typeof __VLS_exposed' : '{}'}>): void,\n`);
- codes.push('attrs: any,\n');
- codes.push('slots: ReturnType,\n');
- codes.push(`emit: typeof ${scriptSetupRanges.emits.name ?? '__VLS_emit'},\n`);
- codes.push('};\n');
- codes.push('})(),\n');
- codes.push(`) => ({} as import('${vueCompilerOptions.lib}').VNode & { __ctx?: Awaited }))`);
+ yield _(` return {} as {\n`
+ + ` props: ${helperTypes.Prettify.name}<${helperTypes.OmitKeepDiscriminatedUnion.name}> & typeof __VLS_fnPropsSlots & typeof __VLS_defaultProps,\n`
+ + ` expose(exposed: import('${vueCompilerOptions.lib}').ShallowUnwrapRef<${scriptSetupRanges.expose.define ? 'typeof __VLS_exposed' : '{}'}>): void,\n`
+ + ` attrs: any,\n`
+ + ` slots: ReturnType,\n`
+ + ` emit: typeof ${scriptSetupRanges.emits.name ?? '__VLS_emit'},\n`
+ + ` };\n`);
+ yield _(` })(),\n`); // __VLS_setup = (async () => {
+ yield _(`) => ({} as import('${vueCompilerOptions.lib}').VNode & { __ctx?: Awaited }))`);
}
else if (!script) {
// no script block, generate script setup code at root
- scriptSetupGeneratedOffset = generateSetupFunction(false, 'export', definePropMirrors);
+ yield* generateSetupFunction(false, 'export', definePropMirrors);
}
else {
if (!scriptRanges?.exportDefault) {
- codes.push('export default ');
+ yield _(`export default `);
}
- codes.push('await (async () => {\n');
- scriptSetupGeneratedOffset = generateSetupFunction(false, 'return', definePropMirrors);
- codes.push(`})()`);
+ yield _(`await (async () => {\n`);
+ yield* generateSetupFunction(false, 'return', definePropMirrors);
+ yield _(`})()`);
}
if (scriptSetupGeneratedOffset !== undefined) {
@@ -393,142 +560,150 @@ export function generate(
continue;
}
const propName = scriptSetup.content.substring(defineProp.name.start, defineProp.name.end);
- const propMirror = definePropMirrors[propName];
- if (propMirror) {
- mirrorBehaviorMappings.push({
- sourceRange: [defineProp.name.start + scriptSetupGeneratedOffset, defineProp.name.end + scriptSetupGeneratedOffset],
- generatedRange: propMirror,
- data: [
- MirrorBehaviorCapabilities.full,
- MirrorBehaviorCapabilities.full,
- ],
+ const propMirror = definePropMirrors.get(propName);
+ if (propMirror !== undefined) {
+ linkedCodeMappings.push({
+ sourceOffsets: [defineProp.name.start + scriptSetupGeneratedOffset],
+ generatedOffsets: [propMirror],
+ lengths: [defineProp.name.end - defineProp.name.start],
+ data: undefined,
});
}
}
}
}
- function generateSetupFunction(functional: boolean, mode: 'return' | 'export' | 'none', definePropMirrors: Record) {
-
- if (!scriptSetupRanges || !scriptSetup) {
+ function* generateSetupFunction(functional: boolean, mode: 'return' | 'export' | 'none', definePropMirrors: Map): Generator {
+ if (!scriptSetupRanges || !scriptSetup)
return;
- }
const definePropProposalA = scriptSetup.content.trimStart().startsWith('// @experimentalDefinePropProposal=kevinEdition') || vueCompilerOptions.experimentalDefinePropProposal === 'kevinEdition';
const definePropProposalB = scriptSetup.content.trimStart().startsWith('// @experimentalDefinePropProposal=johnsonEdition') || vueCompilerOptions.experimentalDefinePropProposal === 'johnsonEdition';
if (vueCompilerOptions.target >= 3.3) {
- codes.push('const { ');
+ yield _(`const { `);
for (const macro of Object.keys(vueCompilerOptions.macros)) {
if (!bindingNames.has(macro)) {
- codes.push(macro, ', ');
+ yield _(macro + `, `);
}
}
- codes.push(`} = await import('${vueCompilerOptions.lib}');\n`);
+ yield _(`} = await import('${vueCompilerOptions.lib}');\n`);
}
if (definePropProposalA) {
- codes.push(`
-declare function defineProp(name: string, options: { required: true } & Record): import('${vueCompilerOptions.lib}').ComputedRef;
-declare function defineProp(name: string, options: { default: any } & Record): import('${vueCompilerOptions.lib}').ComputedRef;
-declare function defineProp(name?: string, options?: any): import('${vueCompilerOptions.lib}').ComputedRef;
-`.trim() + '\n');
+ yield _(`declare function defineProp(name: string, options: { required: true } & Record): import('${vueCompilerOptions.lib}').ComputedRef;\n`);
+ yield _(`declare function defineProp(name: string, options: { default: any } & Record): import('${vueCompilerOptions.lib}').ComputedRef;\n`);
+ yield _(`declare function defineProp(name?: string, options?: any): import('${vueCompilerOptions.lib}').ComputedRef;\n`);
}
if (definePropProposalB) {
- codes.push(`
-declare function defineProp(value: T | (() => T), required?: boolean, rest?: any): import('${vueCompilerOptions.lib}').ComputedRef;
-declare function defineProp(value: T | (() => T) | undefined, required: true, rest?: any): import('${vueCompilerOptions.lib}').ComputedRef;
-declare function defineProp(value?: T | (() => T), required?: boolean, rest?: any): import('${vueCompilerOptions.lib}').ComputedRef;
-`.trim() + '\n');
+ yield _(`declare function defineProp(value: T | (() => T), required?: boolean, rest?: any): import('${vueCompilerOptions.lib}').ComputedRef;\n`);
+ yield _(`declare function defineProp(value: T | (() => T) | undefined, required: true, rest?: any): import('${vueCompilerOptions.lib}').ComputedRef;\n`);
+ yield _(`declare function defineProp(value?: T | (() => T), required?: boolean, rest?: any): import('${vueCompilerOptions.lib}').ComputedRef;\n`);
}
- const scriptSetupGeneratedOffset = muggle.getLength(codes) - scriptSetupRanges.importSectionEndOffset;
+ scriptSetupGeneratedOffset = getGeneratedLength() - scriptSetupRanges.importSectionEndOffset;
- let setupCodeModifies: [() => void, number, number][] = [];
+ let setupCodeModifies: [Code[], number, number][] = [];
if (scriptSetupRanges.props.define && !scriptSetupRanges.props.name) {
const range = scriptSetupRanges.props.withDefaults ?? scriptSetupRanges.props.define;
const statement = scriptSetupRanges.props.define.statement;
if (statement.start === range.start && statement.end === range.end) {
- setupCodeModifies.push([() => codes.push(`const __VLS_props = `), range.start, range.start]);
+ setupCodeModifies.push([[`const __VLS_props = `], range.start, range.start]);
}
else {
- setupCodeModifies.push([() => {
- codes.push(`const __VLS_props = `);
- addVirtualCode('scriptSetup', range.start, range.end);
- codes.push(`;\n`);
- addVirtualCode('scriptSetup', statement.start, range.start);
- codes.push(`__VLS_props`);
- }, statement.start, range.end]);
+ setupCodeModifies.push([[
+ `const __VLS_props = `,
+ generateSourceCode(scriptSetup, range.start, range.end),
+ `;\n`,
+ generateSourceCode(scriptSetup, statement.start, range.start),
+ `__VLS_props`,
+ ], statement.start, range.end]);
}
}
if (scriptSetupRanges.slots.define && !scriptSetupRanges.slots.name) {
- setupCodeModifies.push([() => codes.push(`const __VLS_slots = `), scriptSetupRanges.slots.define.start, scriptSetupRanges.slots.define.start]);
+ setupCodeModifies.push([[`const __VLS_slots = `], scriptSetupRanges.slots.define.start, scriptSetupRanges.slots.define.start]);
}
if (scriptSetupRanges.emits.define && !scriptSetupRanges.emits.name) {
- setupCodeModifies.push([() => codes.push(`const __VLS_emit = `), scriptSetupRanges.emits.define.start, scriptSetupRanges.emits.define.start]);
+ setupCodeModifies.push([[`const __VLS_emit = `], scriptSetupRanges.emits.define.start, scriptSetupRanges.emits.define.start]);
}
if (scriptSetupRanges.expose.define) {
- setupCodeModifies.push([() => {
- if (scriptSetupRanges?.expose.define?.typeArg) {
- codes.push(`let __VLS_exposed!: `);
- addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.expose.define.typeArg.start, scriptSetupRanges.expose.define.typeArg.end);
- codes.push(`;\n`);
- }
- else if (scriptSetupRanges?.expose.define?.arg) {
- codes.push(`const __VLS_exposed = `);
- addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.expose.define.arg.start, scriptSetupRanges.expose.define.arg.end);
- codes.push(`;\n`);
- }
- else {
- codes.push(`const __VLS_exposed = {};\n`);
- }
- }, scriptSetupRanges.expose.define.start, scriptSetupRanges.expose.define.start]);
+ if (scriptSetupRanges.expose.define?.typeArg) {
+ setupCodeModifies.push([
+ [
+ `let __VLS_exposed!: `,
+ generateSourceCodeForExtraReference(scriptSetup, scriptSetupRanges.expose.define.typeArg.start, scriptSetupRanges.expose.define.typeArg.end),
+ `;\n`,
+ ],
+ scriptSetupRanges.expose.define.start,
+ scriptSetupRanges.expose.define.start,
+ ]);
+ }
+ else if (scriptSetupRanges.expose.define?.arg) {
+ setupCodeModifies.push([
+ [
+ `const __VLS_exposed = `,
+ generateSourceCodeForExtraReference(scriptSetup, scriptSetupRanges.expose.define.arg.start, scriptSetupRanges.expose.define.arg.end),
+ `;\n`,
+ ],
+ scriptSetupRanges.expose.define.start,
+ scriptSetupRanges.expose.define.start,
+ ]);
+ }
+ else {
+ setupCodeModifies.push([
+ [`const __VLS_exposed = {};\n`],
+ scriptSetupRanges.expose.define.start,
+ scriptSetupRanges.expose.define.start,
+ ]);
+ }
}
setupCodeModifies = setupCodeModifies.sort((a, b) => a[1] - b[1]);
if (setupCodeModifies.length) {
- addVirtualCode('scriptSetup', scriptSetupRanges.importSectionEndOffset, setupCodeModifies[0][1]);
+ yield _(generateSourceCode(scriptSetup, scriptSetupRanges.importSectionEndOffset, setupCodeModifies[0][1]));
while (setupCodeModifies.length) {
- const [generate, _, end] = setupCodeModifies.shift()!;
- generate();
+ const [codes, _start, end] = setupCodeModifies.shift()!;
+ for (const code of codes) {
+ yield _(code);
+ }
if (setupCodeModifies.length) {
const nextStart = setupCodeModifies[0][1];
- addVirtualCode('scriptSetup', end, nextStart);
+ yield _(generateSourceCode(scriptSetup, end, nextStart));
}
else {
- addVirtualCode('scriptSetup', end);
+ yield _(generateSourceCode(scriptSetup, end));
}
}
}
else {
- addVirtualCode('scriptSetup', scriptSetupRanges.importSectionEndOffset);
+ yield _(generateSourceCode(scriptSetup, scriptSetupRanges.importSectionEndOffset));
}
if (scriptSetupRanges.props.define?.typeArg && scriptSetupRanges.props.withDefaults?.arg) {
// fix https://github.com/vuejs/language-tools/issues/1187
- codes.push(`const __VLS_withDefaultsArg = (function (t: T) { return t })(`);
- addExtraReferenceVirtualCode('scriptSetup', scriptSetupRanges.props.withDefaults.arg.start, scriptSetupRanges.props.withDefaults.arg.end);
- codes.push(`);\n`);
+ yield _(`const __VLS_withDefaultsArg = (function (t: T) { return t })(`);
+ yield _(generateSourceCodeForExtraReference(scriptSetup, scriptSetupRanges.props.withDefaults.arg.start, scriptSetupRanges.props.withDefaults.arg.end));
+ yield _(`);\n`);
}
if (!functional && scriptSetupRanges.defineProp.length) {
- codes.push(`let __VLS_propsOption_defineProp!: {\n`);
+ yield _(`let __VLS_propsOption_defineProp!: {\n`);
for (const defineProp of scriptSetupRanges.defineProp) {
let propName = 'modelValue';
if (defineProp.name && defineProp.nameIsString) {
// renaming support
- addExtraReferenceVirtualCode('scriptSetup', defineProp.name.start, defineProp.name.end);
+ yield _(generateSourceCodeForExtraReference(scriptSetup, defineProp.name.start, defineProp.name.end));
}
else if (defineProp.name) {
propName = scriptSetup.content.substring(defineProp.name.start, defineProp.name.end);
- const start = muggle.getLength(codes);
- definePropMirrors[propName] = [start, start + propName.length];
- codes.push(propName);
+ const start = getGeneratedLength();
+ definePropMirrors.set(propName, start);
+ yield _(propName);
}
else {
- codes.push(propName);
+ yield _(propName);
}
- codes.push(`: `);
+ yield _(`: `);
let type = 'any';
if (!defineProp.nameIsString) {
@@ -539,182 +714,169 @@ declare function defineProp(value?: T | (() => T), required?: boolean, rest?:
}
if (defineProp.required) {
- codes.push(`{ required: true, type: import('${vueCompilerOptions.lib}').PropType<${type}> },\n`);
+ yield _(`{ required: true, type: import('${vueCompilerOptions.lib}').PropType<${type}> },\n`);
}
else {
- codes.push(`import('${vueCompilerOptions.lib}').PropType<${type}>,\n`);
+ yield _(`import('${vueCompilerOptions.lib}').PropType<${type}>,\n`);
}
}
- codes.push(`};\n`);
+ yield _(`};\n`);
}
- generateTemplate(functional);
+ yield* generateTemplate(functional);
if (mode === 'return' || mode === 'export') {
- if (!vueCompilerOptions.skipTemplateCodegen && (htmlGen?.hasSlot || scriptSetupRanges?.slots.define)) {
- usedHelperTypes.WithTemplateSlots = true;
- codes.push(`const __VLS_component = `);
- generateComponent(functional);
- codes.push(`;\n`);
- codes.push(mode === 'return' ? 'return ' : 'export default ');
- codes.push(`{} as __VLS_WithTemplateSlots>;\n`);
+ if (!vueCompilerOptions.skipTemplateCodegen && (templateCodegen?.hasSlot || scriptSetupRanges?.slots.define)) {
+ yield _(`const __VLS_component = `);
+ yield* generateComponent(functional);
+ yield _(`;\n`);
+ yield _(mode === 'return' ? 'return ' : 'export default ');
+ yield _(`{} as ${helperTypes.WithTemplateSlots.name}>;\n`);
}
else {
- codes.push(mode === 'return' ? 'return ' : 'export default ');
- generateComponent(functional);
- codes.push(`;\n`);
+ yield _(mode === 'return' ? 'return ' : 'export default ');
+ yield* generateComponent(functional);
+ yield _(`;\n`);
}
}
-
- return scriptSetupGeneratedOffset;
}
- function generateComponent(functional: boolean) {
-
+ function* generateComponent(functional: boolean): Generator {
if (!scriptSetupRanges)
return;
- if (scriptRanges?.exportDefault && scriptRanges.exportDefault.expression.start !== scriptRanges.exportDefault.args.start) {
+ if (script && scriptRanges?.exportDefault && scriptRanges.exportDefault.expression.start !== scriptRanges.exportDefault.args.start) {
// use defineComponent() from user space code if it exist
- addVirtualCode('script', scriptRanges.exportDefault.expression.start, scriptRanges.exportDefault.args.start);
- codes.push(`{\n`);
+ yield _(generateSourceCode(script, scriptRanges.exportDefault.expression.start, scriptRanges.exportDefault.args.start));
+ yield _(`{\n`);
}
else {
- codes.push(`(await import('${vueCompilerOptions.lib}')).defineComponent({\n`);
+ yield _(`(await import('${vueCompilerOptions.lib}')).defineComponent({\n`);
}
- codes.push(`setup() {\n`);
- codes.push(`return {\n`);
- generateSetupReturns();
+ yield _(`setup() {\n`);
+ yield _(`return {\n`);
+ yield* generateSetupReturns();
if (scriptSetupRanges.expose.define) {
- codes.push(`...__VLS_exposed,\n`);
+ yield _(`...__VLS_exposed,\n`);
}
- codes.push(`};\n`);
- codes.push(`},\n`);
- generateComponentOptions(functional);
- codes.push(`})`);
+ yield _(`};\n`);
+ yield _(`},\n`);
+ yield* generateComponentOptions(functional);
+ yield _(`})`);
}
- function generateComponentOptions(functional: boolean) {
- if (scriptSetupRanges && !bypassDefineComponent) {
+ function* generateComponentOptions(functional: boolean): Generator {
+ if (scriptSetup && scriptSetupRanges && !bypassDefineComponent) {
const ranges = scriptSetupRanges;
- const propsCodegens: (() => void)[] = [];
+ const propsCodegens: (() => Generator)[] = [];
if (ranges.props.define?.arg) {
const arg = ranges.props.define.arg;
- propsCodegens.push(() => {
- addExtraReferenceVirtualCode('scriptSetup', arg.start, arg.end);
+ propsCodegens.push(function* () {
+ yield _(generateSourceCodeForExtraReference(scriptSetup!, arg.start, arg.end));
});
}
if (ranges.props.define?.typeArg) {
const typeArg = ranges.props.define.typeArg;
- propsCodegens.push(() => {
-
- usedHelperTypes.DefinePropsToOptions = true;
+ propsCodegens.push(function* () {
- codes.push(`{} as `);
+ yield _(`{} as `);
if (ranges.props.withDefaults?.arg) {
- usedHelperTypes.MergePropDefaults = true;
- codes.push(`__VLS_WithDefaults<`);
+ yield _(`${helperTypes.WithDefaults.name}<`);
}
- codes.push(`__VLS_TypePropsToRuntimeProps<`);
+ yield _(`${helperTypes.TypePropsToOption.name}<`);
if (functional) {
- codes.push(`typeof __VLS_fnPropsTypeOnly`);
+ yield _(`typeof __VLS_fnPropsTypeOnly`);
}
else {
- addExtraReferenceVirtualCode('scriptSetup', typeArg.start, typeArg.end);
+ yield _(generateSourceCodeForExtraReference(scriptSetup!, typeArg.start, typeArg.end));
}
- codes.push(`>`);
+ yield _(`>`);
if (ranges.props.withDefaults?.arg) {
- codes.push(`, typeof __VLS_withDefaultsArg`);
- codes.push(`>`);
+ yield _(`, typeof __VLS_withDefaultsArg`);
+ yield _(`>`);
}
});
}
if (!functional && ranges.defineProp.length) {
- propsCodegens.push(() => {
- codes.push(`__VLS_propsOption_defineProp`);
+ propsCodegens.push(function* () {
+ yield _(`__VLS_propsOption_defineProp`);
});
}
if (propsCodegens.length === 1) {
- codes.push(`props: `);
+ yield _(`props: `);
for (const generate of propsCodegens) {
- generate();
+ yield* generate();
}
- codes.push(`,\n`);
+ yield _(`,\n`);
}
else if (propsCodegens.length >= 2) {
- codes.push(`props: {\n`);
+ yield _(`props: {\n`);
for (const generate of propsCodegens) {
- codes.push('...');
- generate();
- codes.push(',\n');
+ yield _(`...`);
+ yield* generate();
+ yield _(`,\n`);
}
- codes.push(`},\n`);
+ yield _(`},\n`);
}
if (ranges.emits.define) {
- codes.push(
- `emits: ({} as __VLS_NormalizeEmits),\n`,
- );
+ yield _(`emits: ({} as __VLS_NormalizeEmits),\n`);
}
}
- if (scriptRanges?.exportDefault?.args) {
- addVirtualCode('script', scriptRanges.exportDefault.args.start + 1, scriptRanges.exportDefault.args.end - 1);
+ if (script && scriptRanges?.exportDefault?.args) {
+ yield _(generateSourceCode(script, scriptRanges.exportDefault.args.start + 1, scriptRanges.exportDefault.args.end - 1));
}
}
- function generateSetupReturns() {
+ function* generateSetupReturns(): Generator {
if (scriptSetupRanges && bypassDefineComponent) {
// fill $props
if (scriptSetupRanges.props.define) {
// NOTE: defineProps is inaccurate for $props
- codes.push(`$props: __VLS_makeOptional(${scriptSetupRanges.props.name ?? `__VLS_props`}),\n`);
- codes.push(`...${scriptSetupRanges.props.name ?? `__VLS_props`},\n`);
+ yield _(`$props: __VLS_makeOptional(${scriptSetupRanges.props.name ?? `__VLS_props`}),\n`);
+ yield _(`...${scriptSetupRanges.props.name ?? `__VLS_props`},\n`);
}
// fill $emit
if (scriptSetupRanges.emits.define) {
- codes.push(`$emit: ${scriptSetupRanges.emits.name ?? '__VLS_emit'},\n`);
+ yield _(`$emit: ${scriptSetupRanges.emits.name ?? '__VLS_emit'},\n`);
}
}
}
- function generateTemplate(functional: boolean) {
+ function* generateTemplate(functional: boolean): Generator {
generatedTemplate = true;
if (!vueCompilerOptions.skipTemplateCodegen) {
-
- generateExportOptions();
- generateConstNameOption();
-
- codes.push(`function __VLS_template() {\n`);
-
- const templateGened = generateTemplateContext();
-
- codes.push(`}\n`);
-
- generateComponentForTemplateUsage(functional, templateGened.cssIds);
+ yield* generateExportOptions();
+ yield* generateConstNameOption();
+ yield _(`function __VLS_template() {\n`);
+ const cssIds = new Set();
+ yield* generateTemplateContext(cssIds);
+ yield _(`}\n`);
+ yield* generateComponentForTemplateUsage(functional, cssIds);
}
else {
- codes.push(`function __VLS_template() {\n`);
+ yield _(`function __VLS_template() {\n`);
const templateUsageVars = [...getTemplateUsageVars()];
- codes.push(`// @ts-ignore\n`);
- codes.push(`[${templateUsageVars.join(', ')}]\n`);
- codes.push(`return {};\n`);
- codes.push(`}\n`);
+ yield _(`// @ts-ignore\n`);
+ yield _(`[${templateUsageVars.join(', ')}]\n`);
+ yield _(`return {};\n`);
+ yield _(`}\n`);
}
}
- function generateComponentForTemplateUsage(functional: boolean, cssIds: Set) {
+ function* generateComponentForTemplateUsage(functional: boolean, cssIds: Set): Generator {
if (scriptSetup && scriptSetupRanges) {
- codes.push(`const __VLS_internalComponent = (await import('${vueCompilerOptions.lib}')).defineComponent({\n`);
- codes.push(`setup() {\n`);
- codes.push(`return {\n`);
- generateSetupReturns();
+ yield _(`const __VLS_internalComponent = (await import('${vueCompilerOptions.lib}')).defineComponent({\n`);
+ yield _(`setup() {\n`);
+ yield _(`return {\n`);
+ yield* generateSetupReturns();
// bindings
const templateUsageVars = getTemplateUsageVars();
for (const [content, bindings] of [
@@ -728,119 +890,120 @@ declare function defineProp(value?: T | (() => T), required?: boolean, rest?:
if (!templateUsageVars.has(varName) && !cssIds.has(varName)) {
continue;
}
- const templateStart = getLength(codes);
- codes.push(varName);
- const templateEnd = getLength(codes);
- codes.push(`: ${varName} as typeof `);
-
- const scriptStart = getLength(codes);
- codes.push(varName);
- const scriptEnd = getLength(codes);
- codes.push(',\n');
-
- mirrorBehaviorMappings.push({
- sourceRange: [scriptStart, scriptEnd],
- generatedRange: [templateStart, templateEnd],
- data: [
- MirrorBehaviorCapabilities.full,
- MirrorBehaviorCapabilities.full,
- ],
+ const templateOffset = getGeneratedLength();
+ yield _(varName);
+ yield _(`: ${varName} as typeof `);
+
+ const scriptOffset = getGeneratedLength();
+ yield _(varName);
+ yield _(`,\n`);
+
+ linkedCodeMappings.push({
+ sourceOffsets: [scriptOffset],
+ generatedOffsets: [templateOffset],
+ lengths: [varName.length],
+ data: undefined,
});
}
}
- codes.push(`};\n`); // return {
- codes.push(`},\n`); // setup() {
- generateComponentOptions(functional);
- codes.push(`});\n`); // defineComponent({
+ yield _(`};\n`); // return {
+ yield _(`},\n`); // setup() {
+ yield* generateComponentOptions(functional);
+ yield _(`});\n`); // defineComponent {
}
else if (script) {
- codes.push(`let __VLS_internalComponent!: typeof import('./${path.basename(fileName)}')['default'];\n`);
+ yield _(`let __VLS_internalComponent!: typeof import('./${path.basename(fileName)}')['default'];\n`);
}
else {
- codes.push(`const __VLS_internalComponent = (await import('${vueCompilerOptions.lib}')).defineComponent({});\n`);
+ yield _(`const __VLS_internalComponent = (await import('${vueCompilerOptions.lib}')).defineComponent({});\n`);
}
}
- function generateExportOptions() {
- codes.push(`\n`);
- codes.push(`const __VLS_componentsOption = `);
+ function* generateExportOptions(): Generator {
+ yield _(`\n`);
+ yield _(`const __VLS_componentsOption = `);
if (script && scriptRanges?.exportDefault?.componentsOption) {
const componentsOption = scriptRanges.exportDefault.componentsOption;
- codes.push([
+ yield _([
script.content.substring(componentsOption.start, componentsOption.end),
'script',
componentsOption.start,
- {
- references: true,
- rename: true,
- },
+ disableAllFeatures({
+ navigation: true,
+ }),
]);
}
else {
- codes.push('{}');
+ yield _(`{}`);
}
- codes.push(`;\n`);
+ yield _(`;\n`);
}
- function generateConstNameOption() {
- codes.push(`\n`);
+ function* generateConstNameOption(): Generator {
+ yield _(`\n`);
if (script && scriptRanges?.exportDefault?.nameOption) {
const nameOption = scriptRanges.exportDefault.nameOption;
- codes.push(`const __VLS_name = `);
- codes.push(`${script.content.substring(nameOption.start, nameOption.end)} as const`);
- codes.push(`;\n`);
+ yield _(`const __VLS_name = `);
+ yield _(`${script.content.substring(nameOption.start, nameOption.end)} as const`);
+ yield _(`;\n`);
}
else if (scriptSetup) {
- codes.push(`let __VLS_name!: '${path.basename(fileName.substring(0, fileName.lastIndexOf('.')))}';\n`);
+ yield _(`let __VLS_name!: '${path.basename(fileName.substring(0, fileName.lastIndexOf('.')))}';\n`);
}
else {
- codes.push(`const __VLS_name = undefined;\n`);
+ yield _(`const __VLS_name = undefined;\n`);
}
}
- function generateTemplateContext() {
+ function* generateTemplateContext(cssIds = new Set()): Generator {
const useGlobalThisTypeInCtx = fileName.endsWith('.html');
- codes.push(`let __VLS_ctx!: ${useGlobalThisTypeInCtx ? 'typeof globalThis &' : ''}`);
- codes.push(`InstanceType<__VLS_PickNotAny {}>> & {\n`);
+ yield _(`let __VLS_ctx!: ${useGlobalThisTypeInCtx ? 'typeof globalThis &' : ''}`);
+ yield _(`InstanceType<__VLS_PickNotAny {}>> & {\n`);
/* CSS Module */
for (let i = 0; i < styles.length; i++) {
const style = styles[i];
if (style.module) {
- codes.push(`${style.module}: Record & __VLS_Prettify<{}`);
+ yield _(`${style.module}: Record & ${helperTypes.Prettify.name}<{}`);
for (const className of style.classNames) {
- generateCssClassProperty(
+ yield* generateCssClassProperty(
i,
- className.text.substring(1),
- { start: className.offset, end: className.offset + className.text.length },
+ className.text,
+ className.offset,
'string',
false,
true,
);
}
- codes.push('>;\n');
+ yield _(`>;\n`);
}
}
- codes.push(`};\n`);
+ yield _(`}`);
+
+ if (bindingNames.size !== 0 && scriptSetupRanges?.props.define) {
+ yield _(` & Omit `'${name}'`).join(' | ')}>`);
+ }
+
+ yield _(`;\n`);
/* Components */
- codes.push('/* Components */\n');
- codes.push(`let __VLS_otherComponents!: NonNullable & typeof __VLS_componentsOption;\n`);
- codes.push(`let __VLS_own!: __VLS_SelfComponent { ${getSlotsPropertyName(vueCompilerOptions.target)}: typeof ${scriptSetupRanges?.slots?.name ?? '__VLS_slots'} })>;\n`);
- codes.push(`let __VLS_localComponents!: typeof __VLS_otherComponents & Omit;\n`);
- codes.push(`let __VLS_components!: typeof __VLS_localComponents & __VLS_GlobalComponents & typeof __VLS_ctx;\n`); // for html completion, TS references...
+ yield _(`/* Components */\n`);
+ yield _(`let __VLS_otherComponents!: NonNullable & typeof __VLS_componentsOption;\n`);
+ yield _(`let __VLS_own!: __VLS_SelfComponent { ${getSlotsPropertyName(vueCompilerOptions.target)}: typeof ${scriptSetupRanges?.slots?.name ?? '__VLS_slots'} })>;\n`);
+ yield _(`let __VLS_localComponents!: typeof __VLS_otherComponents & Omit;\n`);
+ yield _(`let __VLS_components!: typeof __VLS_localComponents & __VLS_GlobalComponents & typeof __VLS_ctx;\n`); // for html completion, TS references...
/* Style Scoped */
- codes.push('/* Style Scoped */\n');
- codes.push('type __VLS_StyleScopedClasses = {}');
+ yield _(`/* Style Scoped */\n`);
+ yield _(`type __VLS_StyleScopedClasses = {}`);
for (let i = 0; i < styles.length; i++) {
const style = styles[i];
const option = vueCompilerOptions.experimentalResolveStyleCssClasses;
if (option === 'always' || (option === 'scoped' && style.scoped)) {
for (const className of style.classNames) {
- generateCssClassProperty(
+ yield* generateCssClassProperty(
i,
- className.text.substring(1),
- { start: className.offset, end: className.offset + className.text.length },
+ className.text,
+ className.offset,
'boolean',
true,
!style.module,
@@ -848,152 +1011,147 @@ declare function defineProp(value?: T | (() => T), required?: boolean, rest?:
}
}
}
- codes.push(';\n');
- codes.push('let __VLS_styleScopedClasses!: __VLS_StyleScopedClasses | keyof __VLS_StyleScopedClasses | (keyof __VLS_StyleScopedClasses)[];\n');
-
- codes.push(`/* CSS variable injection */\n`);
- const cssIds = generateCssVars();
- codes.push(`/* CSS variable injection end */\n`);
-
- if (htmlGen) {
- muggle.setTracking(false);
- for (const s of htmlGen.codes) {
- codes.push(s);
- }
- muggle.setTracking(true);
- for (const s of htmlGen.codeStacks) {
- codeStacks.push(s);
+ yield _(`;\n`);
+ yield _('let __VLS_styleScopedClasses!: __VLS_StyleScopedClasses | keyof __VLS_StyleScopedClasses | (keyof __VLS_StyleScopedClasses)[];\n');
+
+ yield _(`/* CSS variable injection */\n`);
+ yield* generateCssVars(cssIds);
+ yield _(`/* CSS variable injection end */\n`);
+
+ if (templateCodegen) {
+ for (let i = 0; i < templateCodegen.tsCodes.length; i++) {
+ yield [
+ templateCodegen.tsCodes[i],
+ templateCodegen.tsCodegenStacks[i],
+ ];
}
}
-
- if (!htmlGen) {
- codes.push(`// no template\n`);
+ else {
+ yield _(`// no template\n`);
if (!scriptSetupRanges?.slots.define) {
- codes.push(`const __VLS_slots = {};\n`);
+ yield _(`const __VLS_slots = {};\n`);
}
}
- codes.push(`return ${scriptSetupRanges?.slots.name ?? '__VLS_slots'};\n`);
+ yield _(`return ${scriptSetupRanges?.slots.name ?? '__VLS_slots'};\n`);
- return { cssIds };
-
- function generateCssClassProperty(styleIndex: number, className: string, classRange: TextRange, propertyType: string, optional: boolean, referencesCodeLens: boolean) {
- codes.push(`\n & { `);
- codes.push([
- '',
- 'style_' + styleIndex,
- classRange.start,
- {
- references: true,
- referencesCodeLens,
- },
- ]);
- codes.push(`'`);
- codes.push([
- className,
- 'style_' + styleIndex,
- [classRange.start, classRange.end],
- {
- references: true,
- rename: {
- normalize: normalizeCssRename,
- apply: applyCssRename,
- },
+ }
+ function* generateCssClassProperty(
+ styleIndex: number,
+ classNameWithDot: string,
+ offset: number,
+ propertyType: string,
+ optional: boolean,
+ referencesCodeLens: boolean
+ ): Generator {
+ yield _(`\n & { `);
+ yield _([
+ '',
+ 'style_' + styleIndex,
+ offset,
+ disableAllFeatures({
+ navigation: true,
+ __referencesCodeLens: referencesCodeLens,
+ }),
+ ]);
+ yield _(`'`);
+ yield _([
+ '',
+ 'style_' + styleIndex,
+ offset,
+ disableAllFeatures({
+ navigation: {
+ resolveRenameNewName: normalizeCssRename,
+ resolveRenameEditText: applyCssRename,
},
- ]);
- codes.push(`'`);
- codes.push([
- '',
- 'style_' + styleIndex,
- classRange.end,
- {},
- ]);
- codes.push(`${optional ? '?' : ''}: ${propertyType}`);
- codes.push(` }`);
- }
- function generateCssVars() {
-
- const emptyLocalVars = new Map();
- const identifiers = new Set();
-
- for (const style of styles) {
- for (const cssBind of style.cssVars) {
- walkInterpolationFragment(
- ts,
- cssBind.text,
- ts.createSourceFile('/a.txt', cssBind.text, ts.ScriptTarget.ESNext),
- (frag, fragOffset, onlyForErrorMapping) => {
- if (fragOffset === undefined) {
- codes.push(frag);
- }
- else {
- codes.push([
- frag,
- style.name,
- cssBind.offset + fragOffset,
- onlyForErrorMapping
- ? { diagnostic: true }
- : FileRangeCapabilities.full,
- ]);
- }
- },
- emptyLocalVars,
- identifiers,
- vueCompilerOptions,
- );
- codes.push(';\n');
+ }),
+ ]);
+ yield _([
+ classNameWithDot.substring(1),
+ 'style_' + styleIndex,
+ offset + 1,
+ disableAllFeatures({ __combineLastMappping: true }),
+ ]);
+ yield _(`'`);
+ yield _([
+ '',
+ 'style_' + styleIndex,
+ offset + classNameWithDot.length,
+ disableAllFeatures({}),
+ ]);
+ yield _(`${optional ? '?' : ''}: ${propertyType}`);
+ yield _(` }`);
+ }
+ function* generateCssVars(cssIds: Set): Generator {
+
+ const emptyLocalVars = new Map();
+
+ for (const style of styles) {
+ for (const cssBind of style.cssVars) {
+ for (const [segment, offset, onlyError] of eachInterpolationSegment(
+ ts,
+ cssBind.text,
+ ts.createSourceFile('/a.txt', cssBind.text, 99 satisfies ts.ScriptTarget.ESNext),
+ emptyLocalVars,
+ cssIds,
+ vueCompilerOptions,
+ )) {
+ if (offset === undefined) {
+ yield _(segment);
+ }
+ else {
+ yield _([
+ segment,
+ style.name,
+ cssBind.offset + offset,
+ onlyError
+ ? disableAllFeatures({ verification: true })
+ : enableAllFeatures({}),
+ ]);
+ }
}
+ yield _(`;\n`);
}
-
- return identifiers;
}
}
function getTemplateUsageVars() {
const usageVars = new Set();
- if (htmlGen) {
+ if (templateCodegen) {
// fix import components unused report
for (const varName of bindingNames) {
- if (!!htmlGen.tagNames[varName] || !!htmlGen.tagNames[hyphenateTag(varName)]) {
+ if (templateCodegen.tagNames.has(varName) || templateCodegen.tagNames.has(hyphenateTag(varName))) {
usageVars.add(varName);
}
}
- for (const tag of Object.keys(htmlGen.tagNames)) {
+ for (const tag of Object.keys(templateCodegen.tagNames)) {
if (tag.indexOf('.') >= 0) {
usageVars.add(tag.split('.')[0]);
}
}
- for (const _id of htmlGen.accessedGlobalVariables) {
+ for (const _id of templateCodegen.accessedGlobalVariables) {
usageVars.add(_id);
}
}
return usageVars;
}
- function addVirtualCode(vueTag: 'script' | 'scriptSetup', start: number, end?: number) {
- muggle.offsetStack();
- codes.push([
- (vueTag === 'script' ? script : scriptSetup)!.content.substring(start, end),
- vueTag,
+ function generateSourceCode(block: SfcBlock, start: number, end?: number): Code {
+ return [
+ block.content.substring(start, end),
+ block.name,
start,
- FileRangeCapabilities.full, // diagnostic also working for setup() returns unused in template checking
- ]);
- muggle.resetOffsetStack();
+ enableAllFeatures({}), // diagnostic also working for setup() returns unused in template checking
+ ];
}
- function addExtraReferenceVirtualCode(vueTag: 'script' | 'scriptSetup', start: number, end: number) {
- muggle.offsetStack();
- codes.push([
- (vueTag === 'script' ? script : scriptSetup)!.content.substring(start, end),
- vueTag,
+ function generateSourceCodeForExtraReference(block: SfcBlock, start: number, end: number): Code {
+ return [
+ block.content.substring(start, end),
+ block.name,
start,
- {
- references: true,
- definition: true,
- rename: true,
- },
- ]);
- muggle.resetOffsetStack();
+ disableAllFeatures({ navigation: true }),
+ ];
}
}
diff --git a/packages/language-core/src/generators/template.ts b/packages/language-core/src/generators/template.ts
index f95543da24..26b88a2bf8 100644
--- a/packages/language-core/src/generators/template.ts
+++ b/packages/language-core/src/generators/template.ts
@@ -1,18 +1,18 @@
-import { FileRangeCapabilities } from '@volar/language-core';
-import { Segment } from '@volar/source-map';
+import { toString } from '@volar/language-core';
import * as CompilerDOM from '@vue/compiler-dom';
import { camelize, capitalize } from '@vue/shared';
import { minimatch } from 'minimatch';
-import * as muggle from 'muggle-string';
import type * as ts from 'typescript/lib/tsserverlibrary';
-import { Sfc, VueCompilerOptions } from '../types';
+import { Code, CodeAndStack, Sfc, VueCodeInformation, VueCompilerOptions } from '../types';
import { hyphenateAttr, hyphenateTag } from '../utils/shared';
-import { collectVars, walkInterpolationFragment } from '../utils/transform';
-
-const capabilitiesPresets = {
- all: FileRangeCapabilities.full,
- allWithHiddenParam: {
- ...FileRangeCapabilities.full, __hint: {
+import { collectVars, eachInterpolationSegment } from '../utils/transform';
+import { disableAllFeatures, enableAllFeatures, getStack, mergeFeatureSettings } from './utils';
+
+const presetInfos = {
+ disabledAll: disableAllFeatures({}),
+ all: enableAllFeatures({}),
+ allWithHiddenParam: enableAllFeatures({
+ __hint: {
setting: 'vue.inlayHints.inlineHandlerLeading',
label: '$event =>',
tooltip: [
@@ -21,20 +21,20 @@ const capabilitiesPresets = {
'[More info](https://github.com/vuejs/language-tools/issues/2445#issuecomment-1444771420)',
].join('\n\n'),
paddingRight: true,
- } /* TODO */
- } as FileRangeCapabilities,
- noDiagnostic: { ...FileRangeCapabilities.full, diagnostic: false } satisfies FileRangeCapabilities,
- diagnosticOnly: { diagnostic: true } satisfies FileRangeCapabilities,
- tagHover: { hover: true } satisfies FileRangeCapabilities,
- event: { hover: true, diagnostic: true } satisfies FileRangeCapabilities,
- tagReference: { references: true, definition: true, rename: { normalize: undefined, apply: noEditApply } } satisfies FileRangeCapabilities,
- attr: { hover: true, diagnostic: true, references: true, definition: true, rename: true } satisfies FileRangeCapabilities,
- attrReference: { references: true, definition: true, rename: true } satisfies FileRangeCapabilities,
- slotProp: { references: true, definition: true, rename: true, diagnostic: true } satisfies FileRangeCapabilities,
- scopedClassName: { references: true, definition: true, rename: true, completion: true } satisfies FileRangeCapabilities,
- slotName: { hover: true, diagnostic: true, references: true, definition: true, completion: true } satisfies FileRangeCapabilities,
- slotNameExport: { hover: true, diagnostic: true, references: true, definition: true, /* referencesCodeLens: true */ } satisfies FileRangeCapabilities,
- refAttr: { references: true, definition: true, rename: true } satisfies FileRangeCapabilities,
+ }
+ }),
+ noDiagnostics: enableAllFeatures({ verification: false }),
+ diagnosticOnly: disableAllFeatures({ verification: true }),
+ tagHover: disableAllFeatures({ semantic: { shouldHighlight: () => false } }),
+ event: disableAllFeatures({ semantic: { shouldHighlight: () => false }, verification: true }),
+ tagReference: disableAllFeatures({ navigation: { shouldRename: () => false } }),
+ attr: disableAllFeatures({ semantic: { shouldHighlight: () => false }, verification: true, navigation: true }),
+ attrReference: disableAllFeatures({ navigation: true }),
+ slotProp: disableAllFeatures({ navigation: true, verification: true }),
+ scopedClassName: disableAllFeatures({ navigation: true, completion: true }),
+ slotName: disableAllFeatures({ semantic: { shouldHighlight: () => false }, verification: true, navigation: true, completion: true }),
+ slotNameExport: disableAllFeatures({ semantic: { shouldHighlight: () => false }, verification: true, navigation: true, /* __navigationCodeLens: true */ }),
+ refAttr: disableAllFeatures({ navigation: true }),
};
const formatBrackets = {
normal: ['`${', '}`;'] as [string, string],
@@ -63,9 +63,9 @@ const transformContext: CompilerDOM.TransformContext = {
expressionPlugins: ['typescript'],
};
-type Code = Segment;
+type _CodeAndStack = [codeType: 'ts' | 'tsFormat' | 'inlineCss', ...codeAndStack: CodeAndStack];
-export function generate(
+export function* generate(
ts: typeof import('typescript/lib/tsserverlibrary'),
compilerOptions: ts.CompilerOptions,
vueCompilerOptions: VueCompilerOptions,
@@ -78,26 +78,69 @@ export function generate(
codegenStack: boolean,
) {
+ const processDirectiveComment = (code: Code) => {
+ if (ignoreError && typeof code !== 'string') {
+ const data = code[3];
+ if (data.verification) {
+ code[3] = {
+ ...data,
+ verification: false,
+ };
+ }
+ }
+ if (expectErrorToken && typeof code !== 'string') {
+ const token = expectErrorToken;
+ const data = code[3];
+ if (data.verification) {
+ code[3] = {
+ ...data,
+ verification: {
+ shouldReport: () => {
+ token.errors++;
+ return false;
+ },
+ },
+ };
+ }
+ }
+ return code;
+ };
+ const _ts = codegenStack
+ ? (code: Code): _CodeAndStack => ['ts', processDirectiveComment(code), getStack()]
+ : (code: Code): _CodeAndStack => ['ts', processDirectiveComment(code), ''];
+ const _tsFormat = codegenStack
+ ? (code: Code): _CodeAndStack => ['tsFormat', code, getStack()]
+ : (code: Code): _CodeAndStack => ['tsFormat', code, ''];
+ const _inlineCss = codegenStack
+ ? (code: Code): _CodeAndStack => ['inlineCss', code, getStack()]
+ : (code: Code): _CodeAndStack => ['inlineCss', code, ''];
const nativeTags = new Set(vueCompilerOptions.nativeTags);
- const [codes, codeStacks] = codegenStack ? muggle.track([] as Code[]) : [[], []];
- const [formatCodes, formatCodeStacks] = codegenStack ? muggle.track([] as Code[]) : [[], []];
- const [cssCodes, cssCodeStacks] = codegenStack ? muggle.track([] as Code[]) : [[], []];
- const slots = new Map();
+ const slots = new Map();
const slotExps = new Map();
- const tagNames = collectTagOffsets();
+ const tagOffsetsMap = collectTagOffsets();
const localVars = new Map();
- const tempVars: ReturnType[] = [];
+ const tempVars: {
+ text: string,
+ isShorthand: boolean,
+ offset: number,
+ }[][] = [];
const accessedGlobalVariables = new Set();
const scopedClasses: { className: string, offset: number; }[] = [];
const blockConditions: string[] = [];
const hasSlotElements = new Set();
- const componentCtxVar2EmitEventsVar = new Map();
+ const usedComponentCtxVars = new Set();
let hasSlot = false;
- let elementIndex = 0;
- let ignoreStart: undefined | number;
- let expectedErrorStart: undefined | number;
+ let ignoreError = false;
+ let expectErrorToken: { errors: number; } | undefined;
let expectedErrorNode: CompilerDOM.CommentNode | undefined;
+ let elementIndex = 0;
if (slotsAssignName) {
localVars.set(slotsAssignName, 1);
@@ -107,189 +150,37 @@ export function generate(
localVars.set(propsAssignName, 1);
}
- generatePreResolveComponents();
+ yield* generatePreResolveComponents();
if (template.ast) {
- visitNode(template.ast, undefined, undefined, undefined);
+ yield* generateAstNode(template.ast, undefined, undefined, undefined);
}
- generateStyleScopedClasses();
+ yield* generateStyleScopedClasses();
if (!hasScriptSetupSlots) {
- codes.push(
- 'var __VLS_slots!:',
- ...createSlotsTypeCode(),
- ';\n',
- );
+ yield _ts('var __VLS_slots!:');
+ yield* generateSlotsType();
+ yield _ts(';\n');
}
- generateAutoImportCompletionCode();
+ yield* generateExtraAutoImport();
return {
- codes,
- codeStacks,
- formatCodes,
- formatCodeStacks,
- cssCodes,
- cssCodeStacks,
- tagNames,
+ tagOffsetsMap,
accessedGlobalVariables,
hasSlot,
};
- function createSlotsTypeCode(): Code[] {
- const codes: Code[] = [];
- for (const [exp, slot] of slotExps) {
- hasSlot = true;
- codes.push(`Partial, (_: typeof ${slot.varName}) => any>> &\n`);
- }
- codes.push(`{\n`);
- for (const [name, slot] of slots) {
- hasSlot = true;
- codes.push(
- ...createObjectPropertyCode([
- name,
- 'template',
- slot.loc,
- {
- ...capabilitiesPresets.slotNameExport,
- referencesCodeLens: true,
- },
- ], slot.nodeLoc),
- );
- codes.push(`?(_: typeof ${slot.varName}): any,\n`);
- }
- codes.push(`}`);
- return codes;
- }
-
- function generateStyleScopedClasses() {
- codes.push(`if (typeof __VLS_styleScopedClasses === 'object' && !Array.isArray(__VLS_styleScopedClasses)) {\n`);
- for (const { className, offset } of scopedClasses) {
- codes.push(`__VLS_styleScopedClasses[`);
- codes.push(...createStringLiteralKeyCode([
- className,
- 'template',
- offset,
- {
- ...capabilitiesPresets.scopedClassName,
- displayWithLink: stylesScopedClasses.has(className),
- },
- ]));
- codes.push(`];\n`);
- }
- codes.push('}\n');
- }
-
- function toCanonicalComponentName(tagText: string) {
- return validTsVarReg.test(tagText) ? tagText : capitalize(camelize(tagText.replace(colonReg, '-')));
- }
-
- function getPossibleOriginalComponentName(tagText: string) {
- return [...new Set([
- // order is important: https://github.com/vuejs/language-tools/issues/2010
- capitalize(camelize(tagText)),
- camelize(tagText),
- tagText,
- ])];
- }
-
- function generatePreResolveComponents() {
-
- codes.push(`let __VLS_resolvedLocalAndGlobalComponents!: {}\n`);
-
- for (const tagName in tagNames) {
-
- if (nativeTags.has(tagName))
- continue;
-
- const isNamespacedTag = tagName.indexOf('.') >= 0;
- if (isNamespacedTag)
- continue;
-
- codes.push(
- `& __VLS_WithComponent<'${toCanonicalComponentName(tagName)}', typeof __VLS_localComponents, `,
- // order is important: https://github.com/vuejs/language-tools/issues/2010
- `"${capitalize(camelize(tagName))}", `,
- `"${camelize(tagName)}", `,
- `"${tagName}"`,
- '>\n',
- );
- }
-
- codes.push(`;\n`);
-
- for (const tagName in tagNames) {
-
- const tagOffsets = tagNames[tagName];
- const tagRanges: [number, number][] = tagOffsets.map(offset => [offset, offset + tagName.length]);
- const names = nativeTags.has(tagName) ? [tagName] : getPossibleOriginalComponentName(tagName);
-
- for (const name of names) {
- for (const tagRange of tagRanges) {
- codes.push(
- nativeTags.has(tagName) ? '__VLS_intrinsicElements' : '__VLS_components',
- ...createPropertyAccessCode([
- name,
- 'template',
- tagRange,
- {
- ...capabilitiesPresets.tagReference,
- rename: {
- normalize: tagName === name ? capabilitiesPresets.tagReference.rename.normalize : camelizeComponentName,
- apply: getTagRenameApply(tagName),
- },
- ...nativeTags.has(tagName) ? {
- ...capabilitiesPresets.tagHover,
- ...capabilitiesPresets.diagnosticOnly,
- } : {},
- },
- ]),
- ';',
- );
- }
- }
- codes.push('\n');
-
- if (nativeTags.has(tagName))
- continue;
-
- const isNamespacedTag = tagName.indexOf('.') >= 0;
- if (isNamespacedTag)
- continue;
-
- codes.push(
- '// @ts-ignore\n', // #2304
- '[',
- );
- const validName = toCanonicalComponentName(tagName);
- for (const tagRange of tagRanges) {
- codes.push([
- validName,
- 'template',
- tagRange,
- {
- completion: {
- additional: true,
- autoImportOnly: true,
- },
- },
- ]);
- codes.push(',');
- }
- codes.push(`];\n`);
- }
- }
-
function collectTagOffsets() {
- const tagOffsetsMap: Record = {};
+ const tagOffsetsMap = new Map();
if (!template.ast) {
return tagOffsetsMap;
}
- walkElementNodes(template.ast, node => {
+ for (const node of eachElementNode(template.ast)) {
if (node.tag === 'slot') {
// ignore
}
@@ -297,16 +188,20 @@ export function generate(
for (const prop of node.props) {
if (prop.type === CompilerDOM.NodeTypes.ATTRIBUTE && prop.name === 'is' && prop.value) {
const tag = prop.value.content;
- tagOffsetsMap[tag] ??= [];
- tagOffsetsMap[tag].push(prop.value.loc.start.offset + prop.value.loc.source.lastIndexOf(tag));
+ let offsets = tagOffsetsMap.get(tag);
+ if (!offsets) {
+ tagOffsetsMap.set(tag, offsets = []);
+ }
+ offsets.push(prop.value.loc.start.offset + prop.value.loc.source.lastIndexOf(tag));
break;
}
}
}
else {
- tagOffsetsMap[node.tag] ??= [];
-
- const offsets = tagOffsetsMap[node.tag];
+ let offsets = tagOffsetsMap.get(node.tag);
+ if (!offsets) {
+ tagOffsetsMap.set(node.tag, offsets = []);
+ }
const source = template.content.substring(node.loc.start.offset);
const startTagOffset = node.loc.start.offset + source.indexOf(node.tag);
@@ -318,75 +213,195 @@ export function generate(
}
}
}
- });
+ }
return tagOffsetsMap;
}
- function resolveComment() {
- if (ignoreStart !== undefined) {
- for (let i = ignoreStart; i < codes.length; i++) {
- const code = codes[i];
- if (typeof code === 'string') {
- continue;
+ function* generateExpectErrorComment(): Generator<_CodeAndStack> {
+
+ if (expectErrorToken && expectedErrorNode) {
+ const token = expectErrorToken;
+ yield _ts([
+ '',
+ 'template',
+ expectedErrorNode.loc.start.offset,
+ disableAllFeatures({
+ verification: {
+ shouldReport: () => token.errors === 0,
+ },
+ }),
+ ]);
+ yield _ts('// @ts-expect-error __VLS_TS_EXPECT_ERROR');
+ yield _ts([
+ '',
+ 'template',
+ expectedErrorNode.loc.end.offset,
+ disableAllFeatures({ __combineLastMappping: true }),
+ ]);
+ yield _ts('\n;\n');
+ }
+
+ ignoreError = false;
+ expectErrorToken = undefined;
+ expectedErrorNode = undefined;
+ }
+
+ function* generateCanonicalComponentName(tagText: string, offset: number, info: VueCodeInformation): Generator<_CodeAndStack> {
+ if (validTsVarReg.test(tagText)) {
+ yield _ts([tagText, 'template', offset, info]);
+ }
+ else {
+ yield* generateCamelized(
+ capitalize(tagText.replace(colonReg, '-')),
+ offset,
+ info
+ );
+ }
+ }
+
+ function* generateSlotsType(): Generator<_CodeAndStack> {
+ for (const [exp, slot] of slotExps) {
+ hasSlot = true;
+ yield _ts(`Partial, (_: typeof ${slot.varName}) => any>> &\n`);
+ }
+ yield _ts(`{\n`);
+ for (const [_, slot] of slots) {
+ hasSlot = true;
+ if (slot.name && slot.loc !== undefined) {
+ yield* generateObjectProperty(
+ slot.name,
+ slot.loc,
+ mergeFeatureSettings(presetInfos.slotNameExport, disableAllFeatures({ __referencesCodeLens: true })),
+ slot.nodeLoc
+ );
+ }
+ else {
+ yield _ts(['', 'template', slot.tagRange[0], mergeFeatureSettings(presetInfos.slotNameExport, disableAllFeatures({ __referencesCodeLens: true }))]);
+ yield _ts('default');
+ yield _ts(['', 'template', slot.tagRange[1], disableAllFeatures({ __combineLastMappping: true })]);
+ }
+ yield _ts(`?(_: typeof ${slot.varName}): any,\n`);
+ }
+ yield _ts(`}`);
+ }
+
+ function* generateStyleScopedClasses(): Generator<_CodeAndStack> {
+ yield _ts(`if (typeof __VLS_styleScopedClasses === 'object' && !Array.isArray(__VLS_styleScopedClasses)) {\n`);
+ for (const { className, offset } of scopedClasses) {
+ yield _ts(`__VLS_styleScopedClasses[`);
+ yield* generateStringLiteralKey(
+ className,
+ offset,
+ mergeFeatureSettings(
+ presetInfos.scopedClassName,
+ disableAllFeatures({ __displayWithLink: stylesScopedClasses.has(className) }),
+ ),
+ );
+ yield _ts(`];\n`);
+ }
+ yield _ts('}\n');
+ }
+
+ function* generatePreResolveComponents(): Generator<_CodeAndStack> {
+
+ yield _ts(`let __VLS_resolvedLocalAndGlobalComponents!: {}\n`);
+
+ for (const [tagName] of tagOffsetsMap) {
+
+ if (nativeTags.has(tagName))
+ continue;
+
+ const isNamespacedTag = tagName.indexOf('.') >= 0;
+ if (isNamespacedTag)
+ continue;
+
+ yield _ts(`& __VLS_WithComponent<'${getCanonicalComponentName(tagName)}', typeof __VLS_localComponents, `);
+ // order is important: https://github.com/vuejs/language-tools/issues/2010
+ yield _ts(`"${capitalize(camelize(tagName))}", `);
+ yield _ts(`"${camelize(tagName)}", `);
+ yield _ts(`"${tagName}"`);
+ yield _ts('>\n');
+ }
+
+ yield _ts(`;\n`);
+
+ for (const [tagName, tagOffsets] of tagOffsetsMap) {
+
+ for (const tagOffset of tagOffsets) {
+ if (nativeTags.has(tagName)) {
+ yield _ts(`__VLS_intrinsicElements`);
+ yield* generatePropertyAccess(
+ tagName,
+ tagOffset,
+ mergeFeatureSettings(
+ presetInfos.tagReference,
+ {
+ navigation: true
+ },
+ ...[
+ presetInfos.tagHover,
+ presetInfos.diagnosticOnly,
+ ],
+ ),
+ );
+ yield _ts(';');
}
- const cap = code[3];
- if (cap.diagnostic) {
- code[3] = {
- ...cap,
- diagnostic: false,
- };
+ else if (validTsVarReg.test(camelize(tagName))) {
+ for (const shouldCapitalize of tagName[0] === tagName.toUpperCase() ? [false] : [true, false]) {
+ const expectName = shouldCapitalize ? capitalize(camelize(tagName)) : camelize(tagName);
+ yield _ts('__VLS_components.');
+ yield* generateCamelized(
+ shouldCapitalize ? capitalize(tagName) : tagName,
+ tagOffset,
+ mergeFeatureSettings(
+ presetInfos.tagReference,
+ {
+ navigation: {
+ resolveRenameNewName: tagName !== expectName ? camelizeComponentName : undefined,
+ resolveRenameEditText: getTagRenameApply(tagName),
+ }
+ },
+ ),
+ );
+ yield _ts(';');
+ }
}
}
- ignoreStart = undefined;
- }
- if (expectedErrorStart !== undefined && expectedErrorStart !== codes.length && expectedErrorNode) {
- let errors = 0;
- const suppressError = () => {
- errors++;
- return false;
- };
- for (let i = expectedErrorStart; i < codes.length; i++) {
- const code = codes[i];
- if (typeof code === 'string') {
- continue;
- }
- const cap = code[3];
- if (cap.diagnostic) {
- code[3] = {
- ...cap,
- diagnostic: {
- shouldReport: suppressError,
- },
- };
+ yield _ts('\n');
+
+ if (
+ !nativeTags.has(tagName)
+ && validTsVarReg.test(camelize(tagName))
+ ) {
+ yield _ts('// @ts-ignore\n'); // #2304
+ yield _ts('[');
+ for (const tagOffset of tagOffsets) {
+ yield* generateCamelized(
+ capitalize(tagName),
+ tagOffset,
+ disableAllFeatures({
+ completion: {
+ isAdditional: true,
+ onlyImport: true,
+ },
+ }),
+ );
+ yield _ts(',');
}
+ yield _ts(`];\n`);
}
- codes.push(
- [
- '// @ts-expect-error __VLS_TS_EXPECT_ERROR',
- 'template',
- [expectedErrorNode.loc.start.offset, expectedErrorNode.loc.end.offset],
- {
- diagnostic: {
- shouldReport: () => errors === 0,
- },
- },
- ],
- '\n;\n',
- );
- expectedErrorStart = undefined;
- expectedErrorNode = undefined;
}
}
- function visitNode(
+ function* generateAstNode(
node: CompilerDOM.RootNode | CompilerDOM.TemplateChildNode | CompilerDOM.InterpolationNode | CompilerDOM.CompoundExpressionNode | CompilerDOM.TextNode | CompilerDOM.SimpleExpressionNode,
parentEl: CompilerDOM.ElementNode | undefined,
prevNode: CompilerDOM.TemplateChildNode | undefined,
componentCtxVar: string | undefined,
- ): void {
+ ): Generator<_CodeAndStack> {
- resolveComment();
+ yield* generateExpectErrorComment();
if (prevNode?.type === CompilerDOM.NodeTypes.COMMENT) {
const commentText = prevNode.content.trim().split(' ')[0];
@@ -394,10 +409,10 @@ export function generate(
return;
}
else if (commentText.match(/^@vue-ignore\b[\s\S]*/)) {
- ignoreStart = codes.length;
+ ignoreError = true;
}
else if (commentText.match(/^@vue-expect-error\b[\s\S]*/)) {
- expectedErrorStart = codes.length;
+ expectErrorToken = { errors: 0 };
expectedErrorNode = prevNode;
}
}
@@ -405,33 +420,33 @@ export function generate(
if (node.type === CompilerDOM.NodeTypes.ROOT) {
let prev: CompilerDOM.TemplateChildNode | undefined;
for (const childNode of node.children) {
- visitNode(childNode, parentEl, prev, componentCtxVar);
+ yield* generateAstNode(childNode, parentEl, prev, componentCtxVar);
prev = childNode;
}
- resolveComment();
+ yield* generateExpectErrorComment();
}
else if (node.type === CompilerDOM.NodeTypes.ELEMENT) {
const vForNode = getVForNode(node);
const vIfNode = getVIfNode(node);
if (vForNode) {
- visitVForNode(vForNode, parentEl, componentCtxVar);
+ yield* generateVFor(vForNode, parentEl, componentCtxVar);
}
else if (vIfNode) {
- visitVIfNode(vIfNode, parentEl, componentCtxVar);
+ yield* generateVIf(vIfNode, parentEl, componentCtxVar);
}
else {
- visitElementNode(node, parentEl, componentCtxVar);
+ yield* generateElement(node, parentEl, componentCtxVar);
}
}
else if (node.type === CompilerDOM.NodeTypes.TEXT_CALL) {
// {{ var }}
- visitNode(node.content, parentEl, undefined, componentCtxVar);
+ yield* generateAstNode(node.content, parentEl, undefined, componentCtxVar);
}
else if (node.type === CompilerDOM.NodeTypes.COMPOUND_EXPRESSION) {
// {{ ... }} {{ ... }}
for (const childNode of node.children) {
if (typeof childNode === 'object') {
- visitNode(childNode, parentEl, undefined, componentCtxVar);
+ yield* generateAstNode(childNode, parentEl, undefined, componentCtxVar);
}
}
}
@@ -452,42 +467,38 @@ export function generate(
content = content + rightCharacter;
}
- codes.push(
- ...createInterpolationCode(
- content,
- node.content.loc,
- start,
- capabilitiesPresets.all,
- '(',
- ');\n',
- ),
+ yield* generateInterpolation(
+ content,
+ node.content.loc,
+ start,
+ presetInfos.all,
+ '(',
+ ');\n',
);
const lines = content.split('\n');
- formatCodes.push(
- ...createFormatCode(
- content,
- start,
- lines.length <= 1 ? formatBrackets.curly : [
- formatBrackets.curly[0],
- lines[lines.length - 1].trim() === '' ? '' : formatBrackets.curly[1],
- ],
- ),
+ yield* generateTsFormat(
+ content,
+ start,
+ lines.length <= 1 ? formatBrackets.curly : [
+ formatBrackets.curly[0],
+ lines[lines.length - 1].trim() === '' ? '' : formatBrackets.curly[1],
+ ],
);
}
else if (node.type === CompilerDOM.NodeTypes.IF) {
// v-if / v-else-if / v-else
- visitVIfNode(node, parentEl, componentCtxVar);
+ yield* generateVIf(node, parentEl, componentCtxVar);
}
else if (node.type === CompilerDOM.NodeTypes.FOR) {
// v-for
- visitVForNode(node, parentEl, componentCtxVar);
+ yield* generateVFor(node, parentEl, componentCtxVar);
}
else if (node.type === CompilerDOM.NodeTypes.TEXT) {
// not needed progress
}
}
- function visitVIfNode(node: CompilerDOM.IfNode, parentEl: CompilerDOM.ElementNode | undefined, componentCtxVar: string | undefined) {
+ function* generateVIf(node: CompilerDOM.IfNode, parentEl: CompilerDOM.ElementNode | undefined, componentCtxVar: string | undefined): Generator<_CodeAndStack> {
let originalBlockConditionsLength = blockConditions.length;
@@ -496,52 +507,50 @@ export function generate(
const branch = node.branches[i];
if (i === 0)
- codes.push('if');
+ yield _ts('if');
else if (branch.condition)
- codes.push('else if');
+ yield _ts('else if');
else
- codes.push('else');
+ yield _ts('else');
let addedBlockCondition = false;
if (branch.condition?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) {
- codes.push(` `);
- const beforeCodeLength = codes.length;
- codes.push(
- ...createInterpolationCode(
- branch.condition.content,
- branch.condition.loc,
- branch.condition.loc.start.offset,
- capabilitiesPresets.all,
- '(',
- ')',
- ),
+ yield _ts(` `);
+ yield* generateInterpolation(
+ branch.condition.content,
+ branch.condition.loc,
+ branch.condition.loc.start.offset,
+ presetInfos.all,
+ '(',
+ ')',
);
- const afterCodeLength = codes.length;
-
- formatCodes.push(
- ...createFormatCode(
- branch.condition.content,
- branch.condition.loc.start.offset,
- formatBrackets.normal,
- ),
+ blockConditions.push(
+ toString(
+ [...generateInterpolation(branch.condition.content, branch.condition.loc, undefined, undefined, '(', ')')]
+ .map(code => code[1])
+ )
);
-
- blockConditions.push(muggle.toString(codes.slice(beforeCodeLength, afterCodeLength)));
addedBlockCondition = true;
+
+ yield* generateTsFormat(
+ branch.condition.content,
+ branch.condition.loc.start.offset,
+ formatBrackets.normal,
+ );
}
- codes.push(` {\n`);
+ yield _ts(` {\n`);
let prev: CompilerDOM.TemplateChildNode | undefined;
for (const childNode of branch.children) {
- visitNode(childNode, parentEl, prev, componentCtxVar);
+ yield* generateAstNode(childNode, parentEl, prev, componentCtxVar);
prev = childNode;
}
- resolveComment();
+ yield* generateExpectErrorComment();
- generateAutoImportCompletionCode();
- codes.push('}\n');
+ yield* generateExtraAutoImport();
+ yield _ts('}\n');
if (addedBlockCondition) {
blockConditions[blockConditions.length - 1] = `!(${blockConditions[blockConditions.length - 1]})`;
@@ -551,57 +560,53 @@ export function generate(
blockConditions.length = originalBlockConditionsLength;
}
- function visitVForNode(node: CompilerDOM.ForNode, parentEl: CompilerDOM.ElementNode | undefined, componentCtxVar: string | undefined) {
+ function* generateVFor(node: CompilerDOM.ForNode, parentEl: CompilerDOM.ElementNode | undefined, componentCtxVar: string | undefined): Generator<_CodeAndStack> {
const { source, value, key, index } = node.parseResult;
const leftExpressionRange = value ? { start: (value ?? key ?? index).loc.start.offset, end: (index ?? key ?? value).loc.end.offset } : undefined;
const leftExpressionText = leftExpressionRange ? node.loc.source.substring(leftExpressionRange.start - node.loc.start.offset, leftExpressionRange.end - node.loc.start.offset) : undefined;
const forBlockVars: string[] = [];
- codes.push(`for (const [`);
+ yield _ts(`for (const [`);
if (leftExpressionRange && leftExpressionText) {
const collectAst = createTsAst(node.parseResult, `const [${leftExpressionText}]`);
- collectVars(ts, collectAst, forBlockVars);
+ collectVars(ts, collectAst, collectAst, forBlockVars);
for (const varName of forBlockVars)
localVars.set(varName, (localVars.get(varName) ?? 0) + 1);
- codes.push([leftExpressionText, 'template', leftExpressionRange.start, capabilitiesPresets.all]);
- formatCodes.push(...createFormatCode(leftExpressionText, leftExpressionRange.start, formatBrackets.normal));
+ yield _ts([leftExpressionText, 'template', leftExpressionRange.start, presetInfos.all]);
+ yield* generateTsFormat(leftExpressionText, leftExpressionRange.start, formatBrackets.normal);
}
- codes.push(`] of __VLS_getVForSourceType`);
+ yield _ts(`] of __VLS_getVForSourceType`);
if (source.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) {
- codes.push(
+ yield _ts('(');
+ yield* generateInterpolation(
+ source.content,
+ source.loc,
+ source.loc.start.offset,
+ presetInfos.all,
'(',
- ...createInterpolationCode(
- source.content,
- source.loc,
- source.loc.start.offset,
- capabilitiesPresets.all,
- '(',
- ')',
- ),
- '!)', // #3102
- ') {\n',
+ ')',
);
+ yield _ts('!)'); // #3102
+ yield _ts(') {\n');
let prev: CompilerDOM.TemplateChildNode | undefined;
for (const childNode of node.children) {
- visitNode(childNode, parentEl, prev, componentCtxVar);
+ yield* generateAstNode(childNode, parentEl, prev, componentCtxVar);
prev = childNode;
}
- resolveComment();
+ yield* generateExpectErrorComment();
- generateAutoImportCompletionCode();
- codes.push('}\n');
+ yield* generateExtraAutoImport();
+ yield _ts('}\n');
- formatCodes.push(
- ...createFormatCode(
- source.content,
- source.loc.start.offset,
- formatBrackets.normal,
- ),
+ yield* generateTsFormat(
+ source.content,
+ source.loc.start.offset,
+ formatBrackets.normal,
);
}
@@ -609,9 +614,9 @@ export function generate(
localVars.set(varName, localVars.get(varName)! - 1);
}
- function visitElementNode(node: CompilerDOM.ElementNode, parentEl: CompilerDOM.ElementNode | undefined, componentCtxVar: string | undefined) {
+ function* generateElement(node: CompilerDOM.ElementNode, parentEl: CompilerDOM.ElementNode | undefined, componentCtxVar: string | undefined): Generator<_CodeAndStack> {
- codes.push(`{\n`);
+ yield _ts(`{\n`);
const startTagOffset = node.loc.start.offset + template.content.substring(node.loc.start.offset).indexOf(node.tag);
let endTagOffset = !node.isSelfClosing && template.lang === 'html' ? node.loc.start.offset + node.loc.source.lastIndexOf(node.tag) : undefined;
@@ -655,175 +660,158 @@ export function generate(
const isIntrinsicElement = nativeTags.has(tag) && tagOffsets.length;
if (isIntrinsicElement) {
- codes.push(
- 'const ',
- var_originalComponent,
- ` = __VLS_intrinsicElements[`,
- ...createStringLiteralKeyCode([
- tag,
- 'template',
- tagOffsets[0],
- capabilitiesPresets.diagnosticOnly,
- ]),
- '];\n',
+ yield _ts('const ');
+ yield _ts(var_originalComponent);
+ yield _ts(` = __VLS_intrinsicElements[`);
+ yield* generateStringLiteralKey(
+ tag,
+ tagOffsets[0],
+ presetInfos.diagnosticOnly,
);
+ yield _ts('];\n');
}
else if (isNamespacedTag) {
- codes.push(
- `const ${var_originalComponent} = `,
- ...createInterpolationCode(tag, node.loc, startTagOffset, capabilitiesPresets.all, '', ''),
- ';\n',
- );
+ yield _ts(`const ${var_originalComponent} = `);
+ yield* generateInterpolation(tag, node.loc, startTagOffset, presetInfos.all, '', '');
+ yield _ts(';\n');
}
else if (dynamicTagExp) {
- codes.push(
- `const ${var_originalComponent} = `,
- ...createInterpolationCode(dynamicTagExp.loc.source, dynamicTagExp.loc, dynamicTagExp.loc.start.offset, capabilitiesPresets.all, '(', ')'),
- ';\n',
- );
+ yield _ts(`const ${var_originalComponent} = `);
+ yield* generateInterpolation(dynamicTagExp.loc.source, dynamicTagExp.loc, dynamicTagExp.loc.start.offset, presetInfos.all, '(', ')');
+ yield _ts(';\n');
}
else {
- codes.push(
- `const ${var_originalComponent} = ({} as `,
- );
- for (const componentName of getPossibleOriginalComponentName(tag)) {
- codes.push(
- `'${componentName}' extends keyof typeof __VLS_ctx ? `,
- `{ '${toCanonicalComponentName(tag)}': typeof __VLS_ctx`,
- ...createPropertyAccessCode(componentName),
- ` }: `,
+ yield _ts(`const ${var_originalComponent} = ({} as `);
+ for (const componentName of getPossibleOriginalComponentNames(tag)) {
+ yield _ts(`'${componentName}' extends keyof typeof __VLS_ctx ? `);
+ yield _ts(`{ '${getCanonicalComponentName(tag)}': typeof __VLS_ctx`);
+ yield* generatePropertyAccess(componentName);
+ yield _ts(` }: `);
+ }
+ yield _ts(`typeof __VLS_resolvedLocalAndGlobalComponents)`);
+ if (tagOffsets.length) {
+ yield* generatePropertyAccess(
+ getCanonicalComponentName(tag),
+ tagOffsets[0],
+ presetInfos.diagnosticOnly,
);
}
- codes.push(
- `typeof __VLS_resolvedLocalAndGlobalComponents)`,
- ...(tagOffsets.length
- ? createPropertyAccessCode([
- toCanonicalComponentName(tag),
- 'template',
- [tagOffsets[0], tagOffsets[0] + tag.length],
- capabilitiesPresets.diagnosticOnly,
- ])
- : createPropertyAccessCode(toCanonicalComponentName(tag))
- ),
- ';\n',
- );
+ else {
+ yield* generatePropertyAccess(getCanonicalComponentName(tag));
+ }
+ yield _ts(';\n');
}
if (isIntrinsicElement) {
- codes.push(`const ${var_functionalComponent} = __VLS_elementAsFunctionalComponent(${var_originalComponent});\n`,);
+ yield _ts(`const ${var_functionalComponent} = __VLS_elementAsFunctionalComponent(${var_originalComponent});\n`);
}
else {
- codes.push(
- `const ${var_functionalComponent} = __VLS_asFunctionalComponent(`,
- `${var_originalComponent}, `,
- `new ${var_originalComponent}({`,
- ...createPropsCode(node, props, 'extraReferences'),
- '})',
- ');\n',
- );
+ yield _ts(`const ${var_functionalComponent} = __VLS_asFunctionalComponent(`);
+ yield _ts(`${var_originalComponent}, `);
+ yield _ts(`new ${var_originalComponent}({`);
+ yield* generateProps(node, props, 'extraReferences');
+ yield _ts('})');
+ yield _ts(');\n');
}
for (const offset of tagOffsets) {
if (isNamespacedTag || dynamicTagExp || isIntrinsicElement) {
continue;
}
- const key = toCanonicalComponentName(tag);
- codes.push(`({} as { ${key}: typeof ${var_originalComponent} }).`);
- codes.push(
- [
- key,
- 'template',
- [offset, offset + tag.length],
- {
- ...capabilitiesPresets.tagHover,
- ...capabilitiesPresets.diagnosticOnly,
- },
- ],
- ';\n',
+ yield _ts(`({} as { ${getCanonicalComponentName(tag)}: typeof ${var_originalComponent} }).`);
+ yield* generateCanonicalComponentName(
+ tag,
+ offset,
+ mergeFeatureSettings(
+ presetInfos.tagHover,
+ presetInfos.diagnosticOnly,
+ ),
);
+ yield _ts(';\n');
}
if (vueCompilerOptions.strictTemplates) {
// with strictTemplates, generate once for props type-checking + instance type
- codes.push(
- `const ${var_componentInstance} = ${var_functionalComponent}(`,
- // diagnostic start
- tagOffsets.length ? ['', 'template', tagOffsets[0], capabilitiesPresets.diagnosticOnly]
- : dynamicTagExp ? ['', 'template', startTagOffset, capabilitiesPresets.diagnosticOnly]
- : '',
- '{ ',
- ...createPropsCode(node, props, 'normal', propsFailedExps),
- '}',
- // diagnostic end
- tagOffsets.length ? ['', 'template', tagOffsets[0] + tag.length, capabilitiesPresets.diagnosticOnly]
- : dynamicTagExp ? ['', 'template', startTagOffset + tag.length, capabilitiesPresets.diagnosticOnly]
- : '',
- `, ...__VLS_functionalComponentArgsRest(${var_functionalComponent}));\n`,
+ yield _ts(`const ${var_componentInstance} = ${var_functionalComponent}(`);
+ // diagnostic start
+ yield _ts(
+ tagOffsets.length ? ['', 'template', tagOffsets[0], presetInfos.diagnosticOnly]
+ : dynamicTagExp ? ['', 'template', startTagOffset, presetInfos.diagnosticOnly]
+ : ''
+ );
+ yield _ts('{ ');
+ yield* generateProps(node, props, 'normal', propsFailedExps);
+ yield _ts('}');
+ // diagnostic end
+ yield _ts(
+ tagOffsets.length ? ['', 'template', tagOffsets[0] + tag.length, presetInfos.diagnosticOnly]
+ : dynamicTagExp ? ['', 'template', startTagOffset + tag.length, presetInfos.diagnosticOnly]
+ : ''
);
+ yield _ts(`, ...__VLS_functionalComponentArgsRest(${var_functionalComponent}));\n`);
}
else {
// without strictTemplates, this only for instacne type
- codes.push(
- `const ${var_componentInstance} = ${var_functionalComponent}(`,
- '{ ',
- ...createPropsCode(node, props, 'extraReferences'),
- '}',
- `, ...__VLS_functionalComponentArgsRest(${var_functionalComponent}));\n`,
- );
+ yield _ts(`const ${var_componentInstance} = ${var_functionalComponent}(`);
+ yield _ts('{ ');
+ yield* generateProps(node, props, 'extraReferences');
+ yield _ts('}');
+ yield _ts(`, ...__VLS_functionalComponentArgsRest(${var_functionalComponent}));\n`);
// and this for props type-checking
- codes.push(
- `({} as (props: __VLS_FunctionalComponentProps & Record) => void)(`,
- // diagnostic start
- tagOffsets.length ? ['', 'template', tagOffsets[0], capabilitiesPresets.diagnosticOnly]
- : dynamicTagExp ? ['', 'template', startTagOffset, capabilitiesPresets.diagnosticOnly]
- : '',
- '{ ',
- ...createPropsCode(node, props, 'normal', propsFailedExps),
- '}',
- // diagnostic end
- tagOffsets.length ? ['', 'template', tagOffsets[0] + tag.length, capabilitiesPresets.diagnosticOnly]
- : dynamicTagExp ? ['', 'template', startTagOffset + tag.length, capabilitiesPresets.diagnosticOnly]
- : '',
- `);\n`,
+ yield _ts(`({} as (props: __VLS_FunctionalComponentProps & Record) => void)(`);
+ // diagnostic start
+ yield _ts(
+ tagOffsets.length ? ['', 'template', tagOffsets[0], presetInfos.diagnosticOnly]
+ : dynamicTagExp ? ['', 'template', startTagOffset, presetInfos.diagnosticOnly]
+ : ''
+ );
+ yield _ts('{ ');
+ yield* generateProps(node, props, 'normal', propsFailedExps);
+ yield _ts('}');
+ // diagnostic end
+ yield _ts(
+ tagOffsets.length ? ['', 'template', tagOffsets[0] + tag.length, presetInfos.diagnosticOnly]
+ : dynamicTagExp ? ['', 'template', startTagOffset + tag.length, presetInfos.diagnosticOnly]
+ : ''
);
+ yield _ts(`);\n`);
}
+ let defineComponentCtxVar: string | undefined;
+
if (tag !== 'template' && tag !== 'slot') {
- componentCtxVar = `__VLS_${elementIndex++}`;
- const componentEventsVar = `__VLS_${elementIndex++}`;
- codes.push(`const ${componentCtxVar} = __VLS_pickFunctionalComponentCtx(${var_originalComponent}, ${var_componentInstance})!;\n`);
- codes.push(`let ${componentEventsVar}!: __VLS_NormalizeEmits;\n`);
- componentCtxVar2EmitEventsVar.set(componentCtxVar, componentEventsVar);
+ defineComponentCtxVar = `__VLS_${elementIndex++}`;
+ componentCtxVar = defineComponentCtxVar;
parentEl = node;
}
+ const componentEventsVar = `__VLS_${elementIndex++}`;
+
+ let usedComponentEventsVar = false;
+
//#region
// fix https://github.com/vuejs/language-tools/issues/1775
for (const failedExp of propsFailedExps) {
- codes.push(
- ...createInterpolationCode(
- failedExp.loc.source,
- failedExp.loc,
- failedExp.loc.start.offset,
- capabilitiesPresets.all,
- '(',
- ')',
- ),
- ';\n',
+ yield* generateInterpolation(
+ failedExp.loc.source,
+ failedExp.loc,
+ failedExp.loc.start.offset,
+ presetInfos.all,
+ '(',
+ ')',
);
+ yield _ts(';\n');
const fb = formatBrackets.normal;
if (fb) {
- formatCodes.push(
- ...createFormatCode(
- failedExp.loc.source,
- failedExp.loc.start.offset,
- fb,
- ),
+ yield* generateTsFormat(
+ failedExp.loc.source,
+ failedExp.loc.start.offset,
+ fb,
);
}
}
- generateInlineCss(props);
+ yield* generateInlineCss(props);
const vScope = props.find(prop => prop.type === CompilerDOM.NodeTypes.DIRECTIVE && (prop.name === 'scope' || prop.name === 'data'));
let inScope = false;
@@ -834,107 +822,100 @@ export function generate(
const scopeVar = `__VLS_${elementIndex++}`;
const condition = `__VLS_withScope(__VLS_ctx, ${scopeVar})`;
- codes.push(`const ${scopeVar} = `);
- codes.push([
+ yield _ts(`const ${scopeVar} = `);
+ yield _ts([
vScope.exp.loc.source,
'template',
vScope.exp.loc.start.offset,
- capabilitiesPresets.all,
+ presetInfos.all,
]);
- codes.push(';\n');
- codes.push(`if (${condition}) {\n`);
+ yield _ts(';\n');
+ yield _ts(`if (${condition}) {\n`);
blockConditions.push(condition);
inScope = true;
}
- generateDirectives(node);
- generateElReferences(node); //
+ yield* generateDirectives(node);
+ yield* generateReferencesForElements(node); //
if (shouldGenerateScopedClasses) {
- generateClassScoped(node);
+ yield* generateReferencesForScopedCssClasses(node);
}
if (componentCtxVar) {
- generateEvents(node, var_functionalComponent, var_componentInstance, componentCtxVar);
+ usedComponentCtxVars.add(componentCtxVar);
+ yield* generateEvents(node, var_functionalComponent, var_componentInstance, componentEventsVar, () => usedComponentEventsVar = true);
}
if (node.tag === 'slot') {
- generateSlot(node, startTagOffset);
+ yield* generateSlot(node, startTagOffset);
}
if (inScope) {
- codes.push('}\n');
+ yield _ts('}\n');
blockConditions.length = originalConditionsNum;
}
//#endregion
const slotDir = node.props.find(p => p.type === CompilerDOM.NodeTypes.DIRECTIVE && p.name === 'slot') as CompilerDOM.DirectiveNode;
if (slotDir && componentCtxVar) {
+ usedComponentCtxVars.add(componentCtxVar);
if (parentEl) {
hasSlotElements.add(parentEl);
}
const slotBlockVars: string[] = [];
- codes.push(`{\n`);
+ yield _ts(`{\n`);
let hasProps = false;
if (slotDir?.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) {
- formatCodes.push(
- ...createFormatCode(
- slotDir.exp.content,
- slotDir.exp.loc.start.offset,
- formatBrackets.params,
- ),
+ yield* generateTsFormat(
+ slotDir.exp.content,
+ slotDir.exp.loc.start.offset,
+ formatBrackets.params,
);
const slotAst = createTsAst(slotDir, `(${slotDir.exp.content}) => {}`);
- collectVars(ts, slotAst, slotBlockVars);
+ collectVars(ts, slotAst, slotAst, slotBlockVars);
hasProps = true;
if (slotDir.exp.content.indexOf(':') === -1) {
- codes.push(
- 'const [',
- [
- slotDir.exp.content,
- 'template',
- slotDir.exp.loc.start.offset,
- capabilitiesPresets.all,
- ],
- `] = __VLS_getSlotParams(`,
- );
+ yield _ts('const [');
+ yield _ts([
+ slotDir.exp.content,
+ 'template',
+ slotDir.exp.loc.start.offset,
+ presetInfos.all,
+ ]);
+ yield _ts(`] = __VLS_getSlotParams(`);
}
else {
- codes.push(
- 'const ',
- [
- slotDir.exp.content,
- 'template',
- slotDir.exp.loc.start.offset,
- capabilitiesPresets.all,
- ],
- ` = __VLS_getSlotParam(`,
- );
+ yield _ts('const ');
+ yield _ts([
+ slotDir.exp.content,
+ 'template',
+ slotDir.exp.loc.start.offset,
+ presetInfos.all,
+ ]);
+ yield _ts(` = __VLS_getSlotParam(`);
}
}
- codes.push(
- ['', 'template', (slotDir.arg ?? slotDir).loc.start.offset, capabilitiesPresets.diagnosticOnly],
- `(${componentCtxVar}.slots!)`,
- ...(
- (slotDir?.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION && slotDir.arg.content)
- ? createPropertyAccessCode([
- slotDir.arg.loc.source,
- 'template',
- slotDir.arg.loc.start.offset,
- slotDir.arg.isStatic ? capabilitiesPresets.slotName : capabilitiesPresets.all
- ], slotDir.arg.loc)
- : createPropertyAccessCode([
- 'default',
- 'template',
- [slotDir.loc.start.offset, slotDir.loc.start.offset + (slotDir.loc.source.startsWith('#') ? '#'.length : slotDir.loc.source.startsWith('v-slot:') ? 'v-slot:'.length : 0)],
- { ...capabilitiesPresets.slotName, completion: false },
- ])
- ),
- ['', 'template', (slotDir.arg ?? slotDir).loc.end.offset, capabilitiesPresets.diagnosticOnly],
- );
+ yield _ts(['', 'template', (slotDir.arg ?? slotDir).loc.start.offset, presetInfos.diagnosticOnly]);
+ yield _ts(`(${componentCtxVar}.slots!)`);
+ if (slotDir?.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION && slotDir.arg.content) {
+ yield* generatePropertyAccess(
+ slotDir.arg.loc.source,
+ slotDir.arg.loc.start.offset,
+ slotDir.arg.isStatic ? presetInfos.slotName : presetInfos.all,
+ slotDir.arg.loc
+ );
+ }
+ else {
+ yield _ts('.');
+ yield _ts(['', 'template', slotDir.loc.start.offset, { ...presetInfos.slotName, completion: false }] satisfies Code);
+ yield _ts('default');
+ yield _ts(['', 'template', slotDir.loc.start.offset + (slotDir.loc.source.startsWith('#') ? '#'.length : slotDir.loc.source.startsWith('v-slot:') ? 'v-slot:'.length : 0), disableAllFeatures({ __combineLastMappping: true })] satisfies Code);
+ }
+ yield _ts(['', 'template', (slotDir.arg ?? slotDir).loc.end.offset, presetInfos.diagnosticOnly]);
if (hasProps) {
- codes.push(')');
+ yield _ts(')');
}
- codes.push(';\n');
+ yield _ts(';\n');
slotBlockVars.forEach(varName => {
localVars.set(varName, (localVars.get(varName) ?? 0) + 1);
@@ -942,11 +923,11 @@ export function generate(
let prev: CompilerDOM.TemplateChildNode | undefined;
for (const childNode of node.children) {
- visitNode(childNode, parentEl, prev, componentCtxVar);
+ yield* generateAstNode(childNode, parentEl, prev, componentCtxVar);
prev = childNode;
}
- resolveComment();
- generateAutoImportCompletionCode();
+ yield* generateExpectErrorComment();
+ yield* generateExtraAutoImport();
slotBlockVars.forEach(varName => {
localVars.set(varName, localVars.get(varName)! - 1);
@@ -956,49 +937,51 @@ export function generate(
isStatic = slotDir.arg.isStatic;
}
if (isStatic && slotDir && !slotDir.arg) {
- codes.push(
- `${componentCtxVar}.slots!['`,
- [
- '',
- 'template',
- slotDir.loc.start.offset + (slotDir.loc.source.startsWith('#') ? '#'.length : slotDir.loc.source.startsWith('v-slot:') ? 'v-slot:'.length : 0),
- { completion: true },
- ],
- `'/* empty slot name completion */]\n`,
- );
+ yield _ts(`${componentCtxVar}.slots!['`);
+ yield _ts([
+ '',
+ 'template',
+ slotDir.loc.start.offset + (
+ slotDir.loc.source.startsWith('#')
+ ? '#'.length : slotDir.loc.source.startsWith('v-slot:')
+ ? 'v-slot:'.length
+ : 0
+ ),
+ disableAllFeatures({ completion: true }),
+ ]);
+ yield _ts(`'/* empty slot name completion */]\n`);
}
- codes.push(`}\n`);
+ yield _ts(`}\n`);
}
else {
let prev: CompilerDOM.TemplateChildNode | undefined;
for (const childNode of node.children) {
- visitNode(childNode, parentEl, prev, componentCtxVar);
+ yield* generateAstNode(childNode, parentEl, prev, componentCtxVar);
prev = childNode;
}
- resolveComment();
+ yield* generateExpectErrorComment();
// fix https://github.com/vuejs/language-tools/issues/932
if (!hasSlotElements.has(node) && node.children.length) {
- codes.push(
- `(${componentCtxVar}.slots!)`,
- ...createPropertyAccessCode([
- 'default',
- 'template',
- [
- node.children[0].loc.start.offset,
- node.children[node.children.length - 1].loc.end.offset,
- ],
- { references: true },
- ]),
- ';\n',
- );
+ yield _ts(`(${componentCtxVar}.slots!).`);
+ yield _ts(['', 'template', node.children[0].loc.start.offset, disableAllFeatures({ navigation: true })]);
+ yield _ts('default');
+ yield _ts(['', 'template', node.children[node.children.length - 1].loc.end.offset, disableAllFeatures({ __combineLastMappping: true })]);
+ yield _ts(';\n');
}
}
- codes.push(`}\n`);
+ if (defineComponentCtxVar && usedComponentCtxVars.has(defineComponentCtxVar)) {
+ yield _ts(`const ${componentCtxVar} = __VLS_pickFunctionalComponentCtx(${var_originalComponent}, ${var_componentInstance})!;\n`);
+ }
+ if (usedComponentEventsVar) {
+ yield _ts(`let ${componentEventsVar}!: __VLS_NormalizeEmits;\n`);
+ }
+
+ yield _ts(`}\n`);
}
- function generateEvents(node: CompilerDOM.ElementNode, componentVar: string, componentInstanceVar: string, componentCtxVar: string) {
+ function* generateEvents(node: CompilerDOM.ElementNode, componentVar: string, componentInstanceVar: string, eventsVar: string, used: () => void): Generator<_CodeAndStack> {
for (const prop of node.props) {
if (
@@ -1006,24 +989,26 @@ export function generate(
&& prop.name === 'on'
&& prop.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION
) {
- const eventsVar = componentCtxVar2EmitEventsVar.get(componentCtxVar);
+ used();
const eventVar = `__VLS_${elementIndex++}`;
- codes.push(
- `let ${eventVar} = { '${prop.arg.loc.source}': `,
- `__VLS_pickEvent(${eventsVar}['${prop.arg.loc.source}'], ({} as __VLS_FunctionalComponentProps)`,
- ...createPropertyAccessCode([
- camelize('on-' + prop.arg.loc.source), // onClickOutside
- 'template',
- [prop.arg.loc.start.offset, prop.arg.loc.end.offset],
+ yield _ts(`let ${eventVar} = { '${prop.arg.loc.source}': `);
+ yield _ts(`__VLS_pickEvent(`);
+ yield _ts(`${eventsVar}['${prop.arg.loc.source}'], `);
+ yield _ts(`({} as __VLS_FunctionalComponentProps)`);
+ const startCode: Code = [
+ '',
+ 'template',
+ prop.arg.loc.start.offset,
+ mergeFeatureSettings(
+ presetInfos.attrReference,
{
- ...capabilitiesPresets.attrReference,
- rename: {
+ navigation: {
// @click-outside -> onClickOutside
- normalize(newName) {
+ resolveRenameNewName(newName) {
return camelize('on-' + newName);
},
// onClickOutside -> @click-outside
- apply(newName) {
+ resolveRenameEditText(newName) {
const hName = hyphenateAttr(newName);
if (hyphenateAttr(newName).startsWith('on-')) {
return camelize(hName.slice('on-'.length));
@@ -1032,146 +1017,159 @@ export function generate(
},
},
},
- ]),
- `) };\n`,
- `${eventVar} = { `,
- );
- if (prop.arg.loc.source.startsWith('[') && prop.arg.loc.source.endsWith(']')) {
- codes.push(
- '[(',
- ...createInterpolationCode(
- prop.arg.loc.source.slice(1, -1),
- prop.arg.loc,
- prop.arg.loc.start.offset + 1,
- capabilitiesPresets.all,
- '',
- '',
- ),
- ')!]',
+ ),
+ ];
+ if (validTsVarReg.test(camelize(prop.arg.loc.source))) {
+ yield _ts(`.`);
+ yield _ts(startCode);
+ yield _ts(`on`);
+ yield* generateCamelized(
+ capitalize(prop.arg.loc.source),
+ prop.arg.loc.start.offset,
+ disableAllFeatures({ __combineLastMappping: true }),
);
}
else {
- codes.push(
- ...createObjectPropertyCode([
- prop.arg.loc.source,
- 'template',
- prop.arg.loc.start.offset,
- capabilitiesPresets.event,
- ], prop.arg.loc)
+ yield _ts(`[`);
+ yield _ts(startCode);
+ yield _ts(`'`);
+ yield _ts(['', 'template', prop.arg.loc.start.offset, disableAllFeatures({ __combineLastMappping: true })]);
+ yield _ts('on');
+ yield* generateCamelized(
+ capitalize(prop.arg.loc.source),
+ prop.arg.loc.start.offset,
+ disableAllFeatures({ __combineLastMappping: true }),
);
+ yield _ts(`'`);
+ yield _ts(['', 'template', prop.arg.loc.end.offset, disableAllFeatures({ __combineLastMappping: true })]);
+ yield _ts(`]`);
}
- codes.push(`: `);
- appendExpressionNode(prop);
- codes.push(` };\n`);
- }
- else if (
- prop.type === CompilerDOM.NodeTypes.DIRECTIVE
- && prop.name === 'on'
+ yield _ts(`) };\n`);
+ yield _ts(`${eventVar} = { `);
+ if (prop.arg.loc.source.startsWith('[') && prop.arg.loc.source.endsWith(']')) {
+ yield _ts('[(');
+ yield* generateInterpolation(
+ prop.arg.loc.source.slice(1, -1),
+ prop.arg.loc,
+ prop.arg.loc.start.offset + 1,
+ presetInfos.all,
+ '',
+ '',
+ );
+ yield _ts(')!]');
+ }
+ else {
+ yield* generateObjectProperty(
+ prop.arg.loc.source,
+ prop.arg.loc.start.offset,
+ presetInfos.event,
+ prop.arg.loc
+ );
+ }
+ yield _ts(`: `);
+ yield* appendExpressionNode(prop);
+ yield _ts(` };\n`);
+ }
+ else if (
+ prop.type === CompilerDOM.NodeTypes.DIRECTIVE
+ && prop.name === 'on'
&& prop.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION
) {
// for vue 2 nameless event
// https://github.com/johnsoncodehk/vue-tsc/issues/67
- codes.push(
- ...createInterpolationCode(
- prop.exp.content,
- prop.exp.loc,
- prop.exp.loc.start.offset,
- capabilitiesPresets.all,
- '$event => {(',
- ')}',
- ),
- ';\n',
+ yield* generateInterpolation(
+ prop.exp.content,
+ prop.exp.loc,
+ prop.exp.loc.start.offset,
+ presetInfos.all,
+ '$event => {(',
+ ')}',
);
- formatCodes.push(
- ...createFormatCode(
- prop.exp.content,
- prop.exp.loc.start.offset,
- formatBrackets.normal,
- ),
+ yield _ts(';\n');
+ yield* generateTsFormat(
+ prop.exp.content,
+ prop.exp.loc.start.offset,
+ formatBrackets.normal,
);
}
+ }
+ }
- function appendExpressionNode(prop: CompilerDOM.DirectiveNode) {
- if (prop.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) {
+ function* appendExpressionNode(prop: CompilerDOM.DirectiveNode): Generator<_CodeAndStack> {
+ if (prop.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) {
- const ast = createTsAst(prop.exp, prop.exp.content);
- let isCompoundExpression = true;
+ const ast = createTsAst(prop.exp, prop.exp.content);
+ let isCompoundExpression = true;
- if (ast.getChildCount() === 2) { // with EOF
- ast.forEachChild(child_1 => {
- if (ts.isExpressionStatement(child_1)) {
- child_1.forEachChild(child_2 => {
- if (ts.isArrowFunction(child_2)) {
- isCompoundExpression = false;
- }
- else if (ts.isIdentifier(child_2)) {
- isCompoundExpression = false;
- }
- });
+ if (ast.statements.length === 1) {
+ ts.forEachChild(ast, child_1 => {
+ if (ts.isExpressionStatement(child_1)) {
+ ts.forEachChild(child_1, child_2 => {
+ if (ts.isArrowFunction(child_2)) {
+ isCompoundExpression = false;
}
- else if (ts.isFunctionDeclaration(child_1)) {
+ else if (ts.isIdentifier(child_2)) {
isCompoundExpression = false;
}
});
}
+ else if (ts.isFunctionDeclaration(child_1)) {
+ isCompoundExpression = false;
+ }
+ });
+ }
- let prefix = '(';
- let suffix = ')';
- let isFirstMapping = true;
-
- if (isCompoundExpression) {
-
- codes.push('$event => {\n');
- localVars.set('$event', (localVars.get('$event') ?? 0) + 1);
+ let prefix = '(';
+ let suffix = ')';
+ let isFirstMapping = true;
- prefix = '';
- suffix = '';
- for (const blockCondition of blockConditions) {
- prefix += `if (!(${blockCondition})) return;\n`;
- }
- }
+ if (isCompoundExpression) {
- codes.push(
- ...createInterpolationCode(
- prop.exp.content,
- prop.exp.loc,
- prop.exp.loc.start.offset,
- () => {
- if (isCompoundExpression && isFirstMapping) {
- isFirstMapping = false;
- return capabilitiesPresets.allWithHiddenParam;
- }
- return capabilitiesPresets.all;
- },
- prefix,
- suffix,
- )
- );
+ yield _ts('$event => {\n');
+ localVars.set('$event', (localVars.get('$event') ?? 0) + 1);
- if (isCompoundExpression) {
- localVars.set('$event', localVars.get('$event')! - 1);
+ prefix = '';
+ suffix = '';
+ for (const blockCondition of blockConditions) {
+ prefix += `if (!(${blockCondition})) return;\n`;
+ }
+ }
- codes.push(';\n');
- generateAutoImportCompletionCode();
- codes.push('}\n');
+ yield* generateInterpolation(
+ prop.exp.content,
+ prop.exp.loc,
+ prop.exp.loc.start.offset,
+ () => {
+ if (isCompoundExpression && isFirstMapping) {
+ isFirstMapping = false;
+ return presetInfos.allWithHiddenParam;
}
+ return presetInfos.all;
+ },
+ prefix,
+ suffix,
+ );
- formatCodes.push(
- ...createFormatCode(
- prop.exp.content,
- prop.exp.loc.start.offset,
- isCompoundExpression ? formatBrackets.event : formatBrackets.normal,
- ),
- );
- }
- else {
- codes.push(`() => {}`);
- }
+ if (isCompoundExpression) {
+ localVars.set('$event', localVars.get('$event')! - 1);
+
+ yield _ts(';\n');
+ yield* generateExtraAutoImport();
+ yield _ts('}\n');
}
+
+ yield* generateTsFormat(
+ prop.exp.content,
+ prop.exp.loc.start.offset,
+ isCompoundExpression ? formatBrackets.event : formatBrackets.normal,
+ );
+ }
+ else {
+ yield _ts(`() => {}`);
}
}
- function createPropsCode(node: CompilerDOM.ElementNode, props: CompilerDOM.ElementNode['props'], mode: 'normal' | 'extraReferences', propsFailedExps?: CompilerDOM.SimpleExpressionNode[]): Code[] {
+ function* generateProps(node: CompilerDOM.ElementNode, props: CompilerDOM.ElementNode['props'], mode: 'normal' | 'extraReferences', propsFailedExps?: CompilerDOM.SimpleExpressionNode[]): Generator<_CodeAndStack> {
let styleAttrNum = 0;
let classAttrNum = 0;
@@ -1187,41 +1185,27 @@ export function generate(
classAttrNum++;
}
- const codes: Code[] = [];
-
- let caps_all: FileRangeCapabilities = capabilitiesPresets.all;
- let caps_diagnosticOnly: FileRangeCapabilities = capabilitiesPresets.diagnosticOnly;
- let caps_attr: FileRangeCapabilities = capabilitiesPresets.attr;
+ let caps_all: VueCodeInformation = presetInfos.all;
+ let caps_diagnosticOnly: VueCodeInformation = presetInfos.diagnosticOnly;
+ let caps_attr: VueCodeInformation = presetInfos.attr;
if (mode === 'extraReferences') {
- caps_all = {
- references: caps_all.references,
- rename: caps_all.rename,
- };
- caps_diagnosticOnly = {
- references: caps_diagnosticOnly.references,
- rename: caps_diagnosticOnly.rename,
- };
- caps_attr = {
- references: caps_attr.references,
- rename: caps_attr.rename,
- };
- }
-
- codes.push(`...{ `);
+ caps_all = disableAllFeatures({ navigation: caps_all.navigation });
+ caps_diagnosticOnly = disableAllFeatures({ navigation: caps_diagnosticOnly.navigation });
+ caps_attr = disableAllFeatures({ navigation: caps_attr.navigation });
+ }
+
+ yield _ts(`...{ `);
for (const prop of props) {
if (
prop.type === CompilerDOM.NodeTypes.DIRECTIVE
&& prop.name === 'on'
&& prop.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION
) {
- codes.push(
- ...createObjectPropertyCode(camelize('on-' + prop.arg.loc.source)),
- ': {} as any, ',
- );
+ yield _ts(`'${camelize('on-' + prop.arg.loc.source)}': {} as any, `);
}
}
- codes.push(`}, `);
+ yield _ts(`}, `);
const canCamelize = !nativeTags.has(node.tag) || node.tagType === CompilerDOM.ElementTypes.COMPONENT;
@@ -1233,7 +1217,7 @@ export function generate(
&& (!prop.exp || prop.exp.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION)
) {
- let attrNameText =
+ let propName =
prop.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION
? prop.arg.constType === CompilerDOM.ConstantTypes.CAN_STRINGIFY
? prop.arg.content
@@ -1241,15 +1225,15 @@ export function generate(
: getModelValuePropName(node, vueCompilerOptions.target, vueCompilerOptions);
if (prop.modifiers.some(m => m === 'prop' || m === 'attr')) {
- attrNameText = attrNameText?.substring(1);
+ propName = propName?.substring(1);
}
if (
- attrNameText === undefined
- || vueCompilerOptions.dataAttributes.some(pattern => minimatch(attrNameText!, pattern))
- || (attrNameText === 'style' && ++styleAttrNum >= 2)
- || (attrNameText === 'class' && ++classAttrNum >= 2)
- || (attrNameText === 'name' && node.tag === 'slot') // #2308
+ propName === undefined
+ || vueCompilerOptions.dataAttributes.some(pattern => minimatch(propName!, pattern))
+ || (propName === 'style' && ++styleAttrNum >= 2)
+ || (propName === 'class' && ++classAttrNum >= 2)
+ || (propName === 'name' && node.tag === 'slot') // #2308
) {
if (prop.exp && prop.exp.constType !== CompilerDOM.ConstantTypes.CAN_STRINGIFY) {
propsFailedExps?.push(prop.exp);
@@ -1257,161 +1241,99 @@ export function generate(
continue;
}
- let camelized = false;
-
- if (
- canCamelize
+ const shouldCamelize = canCamelize
&& (!prop.arg || (prop.arg.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION && prop.arg.isStatic)) // isStatic
- && hyphenateAttr(attrNameText) === attrNameText
- && !vueCompilerOptions.htmlAttributes.some(pattern => minimatch(attrNameText!, pattern))
- ) {
- attrNameText = camelize(attrNameText);
- camelized = true;
- }
-
- // camelize name
- codes.push([
- '',
- 'template',
- prop.loc.start.offset,
- caps_diagnosticOnly,
- ]);
- if (!prop.arg) {
- codes.push(
- ...createObjectPropertyCode([
- attrNameText,
- 'template',
- [prop.loc.start.offset, prop.loc.start.offset + prop.loc.source.indexOf('=')],
+ && hyphenateAttr(propName) === propName
+ && !vueCompilerOptions.htmlAttributes.some(pattern => minimatch(propName!, pattern));
+
+ yield _ts(['', 'template', prop.loc.start.offset, caps_diagnosticOnly]);
+ yield* generateObjectProperty(
+ propName,
+ prop.arg
+ ? prop.arg.loc.start.offset
+ : prop.loc.start.offset,
+ prop.arg
+ ? mergeFeatureSettings(
caps_attr,
- ], (prop.loc as any).name_1 ?? ((prop.loc as any).name_1 = {})),
- );
- }
- else if (prop.exp?.constType === CompilerDOM.ConstantTypes.CAN_STRINGIFY) {
- codes.push(
- ...createObjectPropertyCode([
- attrNameText,
- 'template',
- [prop.arg.loc.start.offset, prop.arg.loc.start.offset + attrNameText.length], // patch style attr,
{
- ...caps_attr,
- rename: {
- normalize: camelize,
- apply: camelized ? hyphenateAttr : noEditApply,
- },
+ navigation: caps_attr.navigation ? {
+ resolveRenameNewName: camelize,
+ resolveRenameEditText: shouldCamelize ? hyphenateAttr : undefined,
+ } : undefined,
},
- ], (prop.loc as any).name_2 ?? ((prop.loc as any).name_2 = {})),
- );
- }
- else {
- codes.push(
- ...createObjectPropertyCode([
- attrNameText,
- 'template',
- [prop.arg.loc.start.offset, prop.arg.loc.end.offset],
- {
- ...caps_attr,
- rename: {
- normalize: camelize,
- apply: camelized ? hyphenateAttr : noEditApply,
- },
- },
- ], (prop.loc as any).name_2 ?? ((prop.loc as any).name_2 = {})),
- );
- }
- codes.push(': (');
+ )
+ : caps_attr,
+ (prop.loc as any).name_2 ?? ((prop.loc as any).name_2 = {}),
+ shouldCamelize,
+ );
+ yield _ts(': (');
if (prop.exp && !(prop.exp.constType === CompilerDOM.ConstantTypes.CAN_STRINGIFY)) { // style='z-index: 2' will compile to {'z-index':'2'}
- codes.push(
- ...createInterpolationCode(
- prop.exp.loc.source,
- prop.exp.loc,
- prop.exp.loc.start.offset,
- caps_all,
- '(',
- ')',
- ),
+ yield* generateInterpolation(
+ prop.exp.loc.source,
+ prop.exp.loc,
+ prop.exp.loc.start.offset,
+ caps_all,
+ '(',
+ ')',
);
if (mode === 'normal') {
- formatCodes.push(
- ...createFormatCode(
- prop.exp.loc.source,
- prop.exp.loc.start.offset,
- formatBrackets.normal,
- ),
+ yield* generateTsFormat(
+ prop.exp.loc.source,
+ prop.exp.loc.start.offset,
+ formatBrackets.normal,
);
}
}
else {
- codes.push('{}');
+ yield _ts('{}');
}
- codes.push(')');
- codes.push([
+ yield _ts(')');
+ yield _ts([
'',
'template',
prop.loc.end.offset,
caps_diagnosticOnly,
]);
- codes.push(', ');
+ yield _ts(', ');
}
else if (prop.type === CompilerDOM.NodeTypes.ATTRIBUTE) {
- let attrNameText = prop.name;
-
if (
- vueCompilerOptions.dataAttributes.some(pattern => minimatch(attrNameText!, pattern))
- || (attrNameText === 'style' && ++styleAttrNum >= 2)
- || (attrNameText === 'class' && ++classAttrNum >= 2)
- || (attrNameText === 'name' && node.tag === 'slot') // #2308
- ) {
- continue;
- }
+ vueCompilerOptions.dataAttributes.some(pattern => minimatch(prop.name, pattern))
+ || (prop.name === 'style' && ++styleAttrNum >= 2)
+ || (prop.name === 'class' && ++classAttrNum >= 2)
+ || (prop.name === 'name' && node.tag === 'slot') // #2308
+ ) continue;
- let camelized = false;
-
- if (
- canCamelize
+ const shouldCamelize = canCamelize
&& hyphenateAttr(prop.name) === prop.name
- && !vueCompilerOptions.htmlAttributes.some(pattern => minimatch(attrNameText!, pattern))
- ) {
- attrNameText = camelize(prop.name);
- camelized = true;
- }
+ && !vueCompilerOptions.htmlAttributes.some(pattern => minimatch(prop.name, pattern));
- // camelize name
- codes.push([
- '',
- 'template',
+ yield _ts(['', 'template', prop.loc.start.offset, caps_diagnosticOnly]);
+ yield* generateObjectProperty(
+ prop.name,
prop.loc.start.offset,
- caps_diagnosticOnly,
- ]);
- codes.push(
- ...createObjectPropertyCode([
- attrNameText,
- 'template',
- [prop.loc.start.offset, prop.loc.start.offset + prop.name.length],
- {
- ...caps_attr,
- rename: {
- normalize: camelize,
- apply: camelized ? hyphenateAttr : noEditApply,
- },
- },
- ], (prop.loc as any).name_1 ?? ((prop.loc as any).name_1 = {}))
+ shouldCamelize
+ ? mergeFeatureSettings(caps_attr, {
+ navigation: caps_attr.navigation ? {
+ resolveRenameNewName: camelize,
+ resolveRenameEditText: hyphenateAttr,
+ } : undefined,
+ })
+ : caps_attr,
+ (prop.loc as any).name_1 ?? ((prop.loc as any).name_1 = {}),
+ shouldCamelize,
);
- codes.push(': (');
+ yield _ts(': (');
if (prop.value) {
- generateAttrValue(prop.value);
+ yield* generateAttrValue(prop.value, caps_all);
}
else {
- codes.push('true');
+ yield _ts('true');
}
- codes.push(')');
- codes.push([
- '',
- 'template',
- prop.loc.end.offset,
- caps_diagnosticOnly,
- ]);
- codes.push(', ');
+ yield _ts(')');
+ yield _ts(['', 'template', prop.loc.end.offset, caps_diagnosticOnly]);
+ yield _ts(', ');
}
else if (
prop.type === CompilerDOM.NodeTypes.DIRECTIVE
@@ -1419,27 +1341,23 @@ export function generate(
&& !prop.arg
&& prop.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION
) {
- codes.push(
- ['', 'template', prop.exp.loc.start.offset, capabilitiesPresets.diagnosticOnly],
- '...',
- ...createInterpolationCode(
- prop.exp.content,
- prop.exp.loc,
- prop.exp.loc.start.offset,
- caps_all,
- '(',
- ')',
- ),
- ['', 'template', prop.exp.loc.end.offset, capabilitiesPresets.diagnosticOnly],
- ', ',
+ yield _ts(['', 'template', prop.exp.loc.start.offset, presetInfos.diagnosticOnly]);
+ yield _ts('...');
+ yield* generateInterpolation(
+ prop.exp.content,
+ prop.exp.loc,
+ prop.exp.loc.start.offset,
+ caps_all,
+ '(',
+ ')',
);
+ yield _ts(['', 'template', prop.exp.loc.end.offset, presetInfos.diagnosticOnly]);
+ yield _ts(', ');
if (mode === 'normal') {
- formatCodes.push(
- ...createFormatCode(
- prop.exp.content,
- prop.exp.loc.start.offset,
- formatBrackets.normal,
- ),
+ yield* generateTsFormat(
+ prop.exp.content,
+ prop.exp.loc.start.offset,
+ formatBrackets.normal,
);
}
}
@@ -1448,34 +1366,9 @@ export function generate(
// tsCodeGen.addText("/* " + [prop.type, prop.name, prop.arg?.loc.source, prop.exp?.loc.source, prop.loc.source].join(", ") + " */ ");
}
}
-
- return codes;
-
- function generateAttrValue(attrNode: CompilerDOM.TextNode) {
- const char = attrNode.loc.source.startsWith("'") ? "'" : '"';
- codes.push(char);
- let start = attrNode.loc.start.offset;
- let end = attrNode.loc.end.offset;
- let content = attrNode.loc.source;
- if (
- (content.startsWith('"') && content.endsWith('"'))
- || (content.startsWith("'") && content.endsWith("'"))
- ) {
- start++;
- end--;
- content = content.slice(1, -1);
- }
- codes.push([
- toUnicodeIfNeed(content),
- 'template',
- [start, end],
- caps_all,
- ]);
- codes.push(char);
- }
}
- function generateInlineCss(props: CompilerDOM.ElementNode['props']) {
+ function* generateInlineCss(props: CompilerDOM.ElementNode['props']): Generator<_CodeAndStack> {
for (const prop of props) {
if (
prop.type === CompilerDOM.NodeTypes.DIRECTIVE
@@ -1490,19 +1383,22 @@ export function generate(
const end = prop.arg.loc.source.lastIndexOf(endCrt);
const content = prop.arg.loc.source.substring(start, end);
- cssCodes.push(`x { `);
- cssCodes.push([
+ yield _inlineCss(`x { `);
+ yield _inlineCss([
content,
'template',
prop.arg.loc.start.offset + start,
- capabilitiesPresets.all,
+ enableAllFeatures({
+ format: false,
+ structure: false,
+ }),
]);
- cssCodes.push(` }\n`);
+ yield _inlineCss(` }\n`);
}
}
}
- function generateDirectives(node: CompilerDOM.ElementNode) {
+ function* generateDirectives(node: CompilerDOM.ElementNode): Generator<_CodeAndStack> {
for (const prop of node.props) {
if (
prop.type === CompilerDOM.NodeTypes.DIRECTIVE
@@ -1516,120 +1412,101 @@ export function generate(
accessedGlobalVariables.add(camelize('v-' + prop.name));
if (prop.arg?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION && !prop.arg.isStatic) {
- codes.push(
- ...createInterpolationCode(
- prop.arg.content,
- prop.arg.loc,
- prop.arg.loc.start.offset + prop.arg.loc.source.indexOf(prop.arg.content),
- capabilitiesPresets.all,
- '(',
- ')',
- ),
- ';\n',
+ yield* generateInterpolation(
+ prop.arg.content,
+ prop.arg.loc,
+ prop.arg.loc.start.offset + prop.arg.loc.source.indexOf(prop.arg.content),
+ presetInfos.all,
+ '(',
+ ')',
);
- formatCodes.push(
- ...createFormatCode(
- prop.arg.content,
- prop.arg.loc.start.offset,
- formatBrackets.normal,
- ),
+ yield _ts(';\n');
+ yield* generateTsFormat(
+ prop.arg.content,
+ prop.arg.loc.start.offset,
+ formatBrackets.normal,
);
}
- codes.push(
- [
- '',
- 'template',
- prop.loc.start.offset,
- capabilitiesPresets.diagnosticOnly,
- ],
- `__VLS_directiveFunction(__VLS_ctx.`,
- [
- camelize('v-' + prop.name),
- 'template',
- [prop.loc.start.offset, prop.loc.start.offset + 'v-'.length + prop.name.length],
+ yield _ts(['', 'template', prop.loc.start.offset, presetInfos.diagnosticOnly]);
+ yield _ts(`__VLS_directiveFunction(__VLS_ctx.`);
+ yield* generateCamelized(
+ 'v-' + prop.name,
+ prop.loc.start.offset,
+ mergeFeatureSettings(
+ presetInfos.noDiagnostics,
{
- ...capabilitiesPresets.noDiagnostic,
completion: {
// fix https://github.com/vuejs/language-tools/issues/1905
- additional: true,
+ isAdditional: true,
},
- rename: {
- normalize: camelize,
- apply: getPropRenameApply(prop.name),
+ navigation: {
+ resolveRenameNewName: camelize,
+ resolveRenameEditText: getPropRenameApply(prop.name),
},
},
- ],
- ')',
- '(',
+ ),
);
+ yield _ts(')');
+ yield _ts('(');
+
if (prop.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION) {
- codes.push(
- ['', 'template', prop.exp.loc.start.offset, capabilitiesPresets.diagnosticOnly],
- ...createInterpolationCode(
- prop.exp.content,
- prop.exp.loc,
- prop.exp.loc.start.offset,
- capabilitiesPresets.all,
- '(',
- ')',
- ),
- ['', 'template', prop.exp.loc.end.offset, capabilitiesPresets.diagnosticOnly],
+ yield _ts(['', 'template', prop.exp.loc.start.offset, presetInfos.diagnosticOnly]);
+ yield* generateInterpolation(
+ prop.exp.content,
+ prop.exp.loc,
+ prop.exp.loc.start.offset,
+ presetInfos.all,
+ '(',
+ ')',
);
- formatCodes.push(
- ...createFormatCode(
- prop.exp.content,
- prop.exp.loc.start.offset,
- formatBrackets.normal,
- ),
+ yield _ts(['', 'template', prop.exp.loc.end.offset, presetInfos.diagnosticOnly]);
+ yield* generateTsFormat(
+ prop.exp.content,
+ prop.exp.loc.start.offset,
+ formatBrackets.normal,
);
}
else {
- codes.push('undefined');
+ yield _ts('undefined');
}
- codes.push(
- ')',
- ['', 'template', prop.loc.end.offset, capabilitiesPresets.diagnosticOnly],
- ';\n',
- );
+ yield _ts(')');
+ yield _ts(['', 'template', prop.loc.end.offset, presetInfos.diagnosticOnly]);
+ yield _ts(';\n');
}
}
}
- function generateElReferences(node: CompilerDOM.ElementNode) {
+ function* generateReferencesForElements(node: CompilerDOM.ElementNode): Generator<_CodeAndStack> {
for (const prop of node.props) {
if (
prop.type === CompilerDOM.NodeTypes.ATTRIBUTE
&& prop.name === 'ref'
&& prop.value
) {
- codes.push(
- '// @ts-ignore\n',
- ...createInterpolationCode(
- prop.value.content,
- prop.value.loc,
- prop.value.loc.start.offset + 1,
- capabilitiesPresets.refAttr,
- '(',
- ')',
- ),
- ';\n',
+ yield _ts('// @ts-ignore\n');
+ yield* generateInterpolation(
+ prop.value.content,
+ prop.value.loc,
+ prop.value.loc.start.offset + 1,
+ presetInfos.refAttr,
+ '(',
+ ')',
);
+ yield _ts(';\n');
}
}
}
- function generateClassScoped(node: CompilerDOM.ElementNode) {
+ function* generateReferencesForScopedCssClasses(node: CompilerDOM.ElementNode): Generator<_CodeAndStack> {
for (const prop of node.props) {
if (
prop.type === CompilerDOM.NodeTypes.ATTRIBUTE
&& prop.name === 'class'
&& prop.value
) {
-
let startOffset = prop.value.loc.start.offset;
let tempClassName = '';
-
for (const char of (prop.value.loc.source + ' ')) {
if (char.trim() === '' || char === '"' || char === "'") {
if (tempClassName !== '') {
@@ -1650,40 +1527,38 @@ export function generate(
&& prop.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION
&& prop.arg.content === 'class'
) {
- codes.push(`__VLS_styleScopedClasses = (`);
- codes.push([
+ yield _ts(`__VLS_styleScopedClasses = (`);
+ yield _ts([
prop.exp.content,
'template',
prop.exp.loc.start.offset,
- capabilitiesPresets.scopedClassName,
+ presetInfos.scopedClassName,
]);
- codes.push(`);\n`);
+ yield _ts(`);\n`);
}
}
}
- function generateSlot(node: CompilerDOM.ElementNode, startTagOffset: number) {
+ function* generateSlot(node: CompilerDOM.ElementNode, startTagOffset: number): Generator<_CodeAndStack> {
const varSlot = `__VLS_${elementIndex++}`;
const slotNameExpNode = getSlotNameExpNode();
if (hasScriptSetupSlots) {
- codes.push(
- '__VLS_normalizeSlot(',
- ['', 'template', node.loc.start.offset, capabilitiesPresets.diagnosticOnly],
- `${slotsAssignName ?? '__VLS_slots'}[`,
- ['', 'template', node.loc.start.offset, capabilitiesPresets.diagnosticOnly],
- slotNameExpNode?.content ?? `('${getSlotName()}' as const)`,
- ['', 'template', node.loc.end.offset, capabilitiesPresets.diagnosticOnly],
- ']',
- ['', 'template', node.loc.end.offset, capabilitiesPresets.diagnosticOnly],
- ')?.(',
- ['', 'template', startTagOffset, capabilitiesPresets.diagnosticOnly],
- '{\n',
- );
+ yield _ts('__VLS_normalizeSlot(');
+ yield _ts(['', 'template', node.loc.start.offset, presetInfos.diagnosticOnly]);
+ yield _ts(`${slotsAssignName ?? '__VLS_slots'}[`);
+ yield _ts(['', 'template', node.loc.start.offset, disableAllFeatures({ __combineLastMappping: true })]);
+ yield _ts(slotNameExpNode?.content ?? `('${getSlotName()?.[0] ?? 'default'}' as const)`);
+ yield _ts(['', 'template', node.loc.end.offset, disableAllFeatures({ __combineLastMappping: true })]);
+ yield _ts(']');
+ yield _ts(['', 'template', node.loc.end.offset, disableAllFeatures({ __combineLastMappping: true })]);
+ yield _ts(')?.(');
+ yield _ts(['', 'template', startTagOffset, disableAllFeatures({ __combineLastMappping: true })]);
+ yield _ts('{\n');
}
else {
- codes.push(`var ${varSlot} = {\n`);
+ yield _ts(`var ${varSlot} = {\n`);
}
for (const prop of node.props) {
if (
@@ -1691,18 +1566,16 @@ export function generate(
&& !prop.arg
&& prop.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION
) {
- codes.push(
- '...',
- ...createInterpolationCode(
- prop.exp.content,
- prop.exp.loc,
- prop.exp.loc.start.offset,
- capabilitiesPresets.attrReference,
- '(',
- ')',
- ),
- ',\n',
+ yield _ts('...');
+ yield* generateInterpolation(
+ prop.exp.content,
+ prop.exp.loc,
+ prop.exp.loc.start.offset,
+ presetInfos.attrReference,
+ '(',
+ ')',
);
+ yield _ts(',\n');
}
else if (
prop.type === CompilerDOM.NodeTypes.DIRECTIVE
@@ -1710,59 +1583,64 @@ export function generate(
&& prop.exp?.type === CompilerDOM.NodeTypes.SIMPLE_EXPRESSION
&& prop.arg.content !== 'name'
) {
- codes.push(
- ...createObjectPropertyCode([
- prop.arg.content,
- 'template',
- [prop.arg.loc.start.offset, prop.arg.loc.end.offset],
+ yield* generateObjectProperty(
+ prop.arg.content,
+ prop.arg.loc.start.offset,
+ mergeFeatureSettings(
+ presetInfos.slotProp,
{
- ...capabilitiesPresets.slotProp,
- rename: {
- normalize: camelize,
- apply: getPropRenameApply(prop.arg.content),
+ navigation: {
+ resolveRenameNewName: camelize,
+ resolveRenameEditText: getPropRenameApply(prop.arg.content),
},
},
- ], prop.arg.loc),
- ': ',
- ...createInterpolationCode(
- prop.exp.content,
- prop.exp.loc,
- prop.exp.loc.start.offset,
- capabilitiesPresets.attrReference,
- '(',
- ')',
),
- ',\n',
+ prop.arg.loc
);
+ yield _ts(': ');
+ yield* generateInterpolation(
+ prop.exp.content,
+ prop.exp.loc,
+ prop.exp.loc.start.offset,
+ presetInfos.attrReference,
+ '(',
+ ')',
+ );
+ yield _ts(',\n');
}
else if (
prop.type === CompilerDOM.NodeTypes.ATTRIBUTE
&& prop.name !== 'name' // slot name
) {
- codes.push(
- ...createObjectPropertyCode([
- prop.name,
- 'template',
- prop.loc.start.offset,
+ yield* generateObjectProperty(
+ prop.name,
+ prop.loc.start.offset,
+ mergeFeatureSettings(
+ presetInfos.attr,
{
- ...capabilitiesPresets.attr,
- rename: {
- normalize: camelize,
- apply: getPropRenameApply(prop.name),
+ navigation: {
+ resolveRenameNewName: camelize,
+ resolveRenameEditText: getPropRenameApply(prop.name),
},
},
- ], prop.loc),
- ': (',
- prop.value !== undefined ? `"${toUnicodeIfNeed(prop.value.content)}"` : 'true',
- '),\n',
+ ),
+ prop.loc
+ );
+ yield _ts(': (');
+ yield _ts(
+ prop.value !== undefined
+ ? `"${needToUnicode(prop.value.content) ? toUnicode(prop.value.content) : prop.value.content}"`
+ : 'true'
);
+ yield _ts('),\n');
}
}
- codes.push(
- '}',
- hasScriptSetupSlots ? ['', 'template', startTagOffset + node.tag.length, capabilitiesPresets.diagnosticOnly] : '',
- hasScriptSetupSlots ? `);\n` : `;\n`
- );
+ yield _ts('}');
+ if (hasScriptSetupSlots) {
+ yield _ts(['', 'template', startTagOffset + node.tag.length, presetInfos.diagnosticOnly]);
+ yield _ts(`)`);
+ }
+ yield _ts(`;\n`);
if (hasScriptSetupSlots) {
return;
@@ -1770,31 +1648,31 @@ export function generate(
if (slotNameExpNode) {
const varSlotExp = `__VLS_${elementIndex++}`;
- codes.push(`var ${varSlotExp} = `);
+ yield _ts(`var ${varSlotExp} = `);
if (typeof slotNameExpNode === 'string') {
- codes.push(slotNameExpNode);
+ yield _ts(slotNameExpNode);
}
else {
- codes.push(
- ...createInterpolationCode(
- slotNameExpNode.content,
- slotNameExpNode,
- undefined, undefined,
- '(',
- ')',
- ),
+ yield* generateInterpolation(
+ slotNameExpNode.content,
+ slotNameExpNode,
+ undefined, undefined,
+ '(',
+ ')',
);
}
- codes.push(` as const;\n`);
+ yield _ts(` as const;\n`);
slotExps.set(varSlotExp, {
varName: varSlot,
});
}
else {
const slotName = getSlotName();
- slots.set(slotName, {
+ slots.set(slotName?.[0] ?? 'default', {
+ name: slotName?.[0],
+ loc: slotName?.[1],
+ tagRange: [startTagOffset, startTagOffset + node.tag.length],
varName: varSlot,
- loc: [startTagOffset, startTagOffset + node.tag.length],
nodeLoc: node.loc,
});
}
@@ -1803,11 +1681,13 @@ export function generate(
for (const prop2 of node.props) {
if (prop2.name === 'name' && prop2.type === CompilerDOM.NodeTypes.ATTRIBUTE && prop2.value) {
if (prop2.value.content) {
- return prop2.value.content;
+ return [
+ prop2.value.content,
+ prop2.loc.start.offset + prop2.loc.source.indexOf(prop2.value.content, prop2.name.length),
+ ] as const;
}
}
}
- return 'default';
}
function getSlotNameExpNode() {
for (const prop2 of node.props) {
@@ -1820,100 +1700,173 @@ export function generate(
}
}
- function generateAutoImportCompletionCode() {
+ function* generateExtraAutoImport(): Generator<_CodeAndStack> {
if (!tempVars.length)
return;
- codes.push('// @ts-ignore\n'); // #2304
- codes.push('[');
+ yield _ts('// @ts-ignore\n'); // #2304
+ yield _ts('[');
for (const _vars of tempVars) {
for (const v of _vars) {
- codes.push([v.text, 'template', v.offset, { completion: { additional: true } }]);
- codes.push(',');
+ yield _ts([
+ v.text,
+ 'template',
+ v.offset,
+ disableAllFeatures({ completion: { isAdditional: true }, }),
+ ]);
+ yield _ts(',');
}
}
- codes.push('];\n');
+ yield _ts('];\n');
tempVars.length = 0;
}
- // functional like
+ function* generateAttrValue(attrNode: CompilerDOM.TextNode, info: VueCodeInformation): Generator<_CodeAndStack> {
+ const char = attrNode.loc.source.startsWith("'") ? "'" : '"';
+ yield _ts(char);
+ let start = attrNode.loc.start.offset;
+ let end = attrNode.loc.end.offset;
+ let content = attrNode.loc.source;
+ if (
+ (content.startsWith('"') && content.endsWith('"'))
+ || (content.startsWith("'") && content.endsWith("'"))
+ ) {
+ start++;
+ end--;
+ content = content.slice(1, -1);
+ }
+ if (needToUnicode(content)) {
+ yield _ts(['', 'template', start, info]);
+ yield _ts(toUnicode(content));
+ yield _ts(['', 'template', end, disableAllFeatures({ __combineLastMappping: true })]);
+ }
+ else {
+ yield _ts([content, 'template', start, info]);
+ }
+ yield _ts(char);
+ }
- function createFormatCode(mapCode: string, sourceOffset: number, formatWrapper: [string, string]): Code[] {
- return [
- formatWrapper[0],
- [mapCode, 'template', sourceOffset, { completion: true /* fix vue-autoinsert-parentheses not working */ }],
- formatWrapper[1],
- '\n',
- ];
+ function* generateCamelized(code: string, offset: number, info: VueCodeInformation): Generator<_CodeAndStack> {
+ const parts = code.split('-');
+ for (let i = 0; i < parts.length; i++) {
+ const part = parts[i];
+ if (part !== '') {
+ yield _ts([
+ i === 0
+ ? part
+ : capitalize(part),
+ 'template',
+ offset,
+ i === 0
+ ? info
+ : disableAllFeatures({ __combineLastMappping: true }),
+ ]);
+ }
+ offset += part.length + 1;
+ }
}
- function createObjectPropertyCode(a: Code, astHolder?: any): Code[] {
- const aStr = typeof a === 'string' ? a : a[0];
- if (validTsVarReg.test(aStr)) {
- return [a];
- }
- else if (aStr.startsWith('[') && aStr.endsWith(']') && astHolder) {
- const range = typeof a === 'object' ? a[2] : undefined;
- const data = typeof a === 'object' ? a[3] : undefined;
- return createInterpolationCode(
- aStr,
- astHolder,
- range && typeof range === 'object' ? range[0] : range,
- data,
- '',
- '',
- );
+ function* generateTsFormat(code: string, offset: number, formatWrapper: [string, string]): Generator<_CodeAndStack> {
+ yield _tsFormat(formatWrapper[0]);
+ yield _tsFormat([
+ code,
+ 'template',
+ offset,
+ mergeFeatureSettings(
+ presetInfos.disabledAll,
+ {
+ format: true,
+ // autoInserts: true, // TODO: support vue-autoinsert-parentheses
+ },
+ ),
+ ]);
+ yield _tsFormat(formatWrapper[1]);
+ yield _tsFormat('\n');
+ }
+
+ function* generateObjectProperty(code: string, offset: number, info: VueCodeInformation, astHolder?: any, shouldCamelize = false): Generator<_CodeAndStack> {
+ if (code.startsWith('[') && code.endsWith(']') && astHolder) {
+ yield* generateInterpolation(code, astHolder, offset, info, '', '');
+ }
+ else if (shouldCamelize) {
+ if (validTsVarReg.test(camelize(code))) {
+ yield* generateCamelized(code, offset, info);
+ }
+ else {
+ yield _ts(['', 'template', offset, info]);
+ yield _ts('"');
+ yield* generateCamelized(code, offset, disableAllFeatures({ __combineLastMappping: true }));
+ yield _ts('"');
+ yield _ts(['', 'template', offset + code.length, disableAllFeatures({ __combineLastMappping: true })]);
+ }
}
else {
- return createStringLiteralKeyCode(a);
+ if (validTsVarReg.test(code)) {
+ yield _ts([code, 'template', offset, info]);
+ }
+ else {
+ yield* generateStringLiteralKey(code, offset, info);
+ }
}
}
- function createInterpolationCode(
+ function* generateInterpolation(
_code: string,
astHolder: any,
start: number | undefined,
- data: FileRangeCapabilities | (() => FileRangeCapabilities) | undefined,
+ data: VueCodeInformation | (() => VueCodeInformation) | undefined,
prefix: string,
suffix: string,
- ): Code[] {
+ ): Generator<_CodeAndStack> {
const code = prefix + _code + suffix;
const ast = createTsAst(astHolder, code);
- const codes: Code[] = [];
- const vars = walkInterpolationFragment(ts, code, ast, (frag, fragOffset, isJustForErrorMapping) => {
- if (fragOffset === undefined) {
- codes.push(frag);
+ const vars: {
+ text: string,
+ isShorthand: boolean,
+ offset: number,
+ }[] = [];
+ for (let [section, offset, onlyError] of eachInterpolationSegment(
+ ts,
+ code,
+ ast,
+ localVars,
+ accessedGlobalVariables,
+ vueCompilerOptions,
+ vars,
+ )) {
+ if (offset === undefined) {
+ yield _ts(section);
}
else {
- fragOffset -= prefix.length;
+ offset -= prefix.length;
let addSuffix = '';
- const overLength = fragOffset + frag.length - _code.length;
+ const overLength = offset + section.length - _code.length;
if (overLength > 0) {
- addSuffix = frag.substring(frag.length - overLength);
- frag = frag.substring(0, frag.length - overLength);
+ addSuffix = section.substring(section.length - overLength);
+ section = section.substring(0, section.length - overLength);
}
- if (fragOffset < 0) {
- codes.push(frag.substring(0, -fragOffset));
- frag = frag.substring(-fragOffset);
- fragOffset = 0;
+ if (offset < 0) {
+ yield _ts(section.substring(0, -offset));
+ section = section.substring(-offset);
+ offset = 0;
}
if (start !== undefined && data !== undefined) {
- codes.push([
- frag,
+ yield _ts([
+ section,
'template',
- start + fragOffset,
- isJustForErrorMapping
- ? capabilitiesPresets.diagnosticOnly
+ start + offset,
+ onlyError
+ ? presetInfos.diagnosticOnly
: typeof data === 'function' ? data() : data,
]);
}
else {
- codes.push(frag);
+ yield _ts(section);
}
- codes.push(addSuffix);
+ yield _ts(addSuffix);
}
- }, localVars, accessedGlobalVariables, vueCompilerOptions);
+ }
if (start !== undefined) {
for (const v of vars) {
v.offset = start + v.offset - prefix.length;
@@ -1922,72 +1875,77 @@ export function generate(
tempVars.push(vars);
}
}
- return codes;
}
- function createTsAst(astHolder: any, text: string) {
- if (astHolder.__volar_ast_text !== text) {
- astHolder.__volar_ast_text = text;
- astHolder.__volar_ast = ts.createSourceFile('/a.ts', text, ts.ScriptTarget.ESNext);
+ function* generatePropertyAccess(code: string, offset?: number, info?: VueCodeInformation, astHolder?: any): Generator<_CodeAndStack> {
+ if (!compilerOptions.noPropertyAccessFromIndexSignature && validTsVarReg.test(code)) {
+ yield _ts('.');
+ yield _ts(offset !== undefined && info
+ ? [code, 'template', offset, info]
+ : code);
+ }
+ else if (code.startsWith('[') && code.endsWith(']')) {
+ yield* generateInterpolation(code, astHolder, offset, info, '', '');
+ }
+ else {
+ yield _ts('[');
+ yield* generateStringLiteralKey(code, offset, info);
+ yield _ts(']');
}
- return astHolder.__volar_ast as ts.SourceFile;
}
- function createPropertyAccessCode(a: Code, astHolder?: any): Code[] {
- const aStr = typeof a === 'string' ? a : a[0];
- if (!compilerOptions.noPropertyAccessFromIndexSignature && validTsVarReg.test(aStr)) {
- return ['.', a];
- }
- else if (aStr.startsWith('[') && aStr.endsWith(']')) {
- if (typeof a === 'string' || !astHolder) {
- return [a];
- }
- else {
- return createInterpolationCode(
- a[0],
- astHolder,
- typeof a[2] === 'number' ? a[2] : a[2][0],
- a[3],
- '',
- '',
- );
- }
+ function* generateStringLiteralKey(code: string, offset?: number, info?: VueCodeInformation): Generator<_CodeAndStack> {
+ if (offset === undefined || !info) {
+ yield _ts(`"${code}"`);
}
else {
- return ['[', ...createStringLiteralKeyCode(a), ']'];
+ yield _ts(['', 'template', offset, info]);
+ yield _ts('"');
+ yield _ts([code, 'template', offset, disableAllFeatures({ __combineLastMappping: true })]);
+ yield _ts('"');
+ yield _ts(['', 'template', offset + code.length, disableAllFeatures({ __combineLastMappping: true })]);
}
}
- function createStringLiteralKeyCode(a: Code): Code[] {
- let codes: Code[] = ['"', a, '"'];
- if (typeof a === 'object') {
- const start = typeof a[2] === 'number' ? a[2] : a[2][0];
- const end = typeof a[2] === 'number' ? a[2] : a[2][1];
- codes = [
- ['', 'template', start, a[3]],
- ...codes,
- ['', 'template', end, a[3]],
- ];
- }
- return codes;
+ function createTsAst(astHolder: any, text: string) {
+ if (astHolder.__volar_ast_text !== text) {
+ astHolder.__volar_ast_text = text;
+ astHolder.__volar_ast = ts.createSourceFile('/a.ts', text, 99 satisfies ts.ScriptTarget.ESNext);
+ }
+ return astHolder.__volar_ast as ts.SourceFile;
}
-};
+}
+
+function getCanonicalComponentName(tagText: string) {
+ return validTsVarReg.test(tagText)
+ ? tagText
+ : capitalize(camelize(tagText.replace(colonReg, '-')));
+}
+
+function getPossibleOriginalComponentNames(tagText: string) {
+ return [...new Set([
+ // order is important: https://github.com/vuejs/language-tools/issues/2010
+ capitalize(camelize(tagText)),
+ camelize(tagText),
+ tagText,
+ ])];
+}
-export function walkElementNodes(node: CompilerDOM.RootNode | CompilerDOM.TemplateChildNode, cb: (node: CompilerDOM.ElementNode) => void) {
+export function* eachElementNode(node: CompilerDOM.RootNode | CompilerDOM.TemplateChildNode): Generator {
if (node.type === CompilerDOM.NodeTypes.ROOT) {
for (const child of node.children) {
- walkElementNodes(child, cb);
+ yield* eachElementNode(child);
}
}
else if (node.type === CompilerDOM.NodeTypes.ELEMENT) {
const patchForNode = getVForNode(node);
if (patchForNode) {
- walkElementNodes(patchForNode, cb);
+ yield* eachElementNode(patchForNode);
}
else {
- cb(node);
+ yield node;
for (const child of node.children) {
- walkElementNodes(child, cb);
+ yield* eachElementNode(child);
}
}
}
@@ -1996,23 +1954,20 @@ export function walkElementNodes(node: CompilerDOM.RootNode | CompilerDOM.Templa
for (let i = 0; i < node.branches.length; i++) {
const branch = node.branches[i];
for (const childNode of branch.children) {
- walkElementNodes(childNode, cb);
+ yield* eachElementNode(childNode);
}
}
}
else if (node.type === CompilerDOM.NodeTypes.FOR) {
// v-for
for (const child of node.children) {
- walkElementNodes(child, cb);
+ yield* eachElementNode(child);
}
}
}
-function toUnicodeIfNeed(str: string) {
- if (str.indexOf('\\') === -1 && str.indexOf('\n') === -1) {
- return str;
- }
- return toUnicode(str);
+function needToUnicode(str: string) {
+ return str.indexOf('\\') >= 0 || str.indexOf('\n') >= 0;
}
function toUnicode(str: string) {
@@ -2030,15 +1985,11 @@ function camelizeComponentName(newName: string) {
}
function getTagRenameApply(oldName: string) {
- return oldName === hyphenateTag(oldName) ? hyphenateTag : noEditApply;
+ return oldName === hyphenateTag(oldName) ? hyphenateTag : undefined;
}
function getPropRenameApply(oldName: string) {
- return oldName === hyphenateAttr(oldName) ? hyphenateAttr : noEditApply;
-}
-
-function noEditApply(n: string) {
- return n;
+ return oldName === hyphenateAttr(oldName) ? hyphenateAttr : undefined;
}
function getModelValuePropName(node: CompilerDOM.ElementNode, vueVersion: number, vueCompilerOptions: VueCompilerOptions) {
diff --git a/packages/language-core/src/generators/utils.ts b/packages/language-core/src/generators/utils.ts
new file mode 100644
index 0000000000..3556496cbc
--- /dev/null
+++ b/packages/language-core/src/generators/utils.ts
@@ -0,0 +1,55 @@
+import { Code, CodeAndStack, VueCodeInformation } from '../types';
+
+export function withStack(code: Code): CodeAndStack {
+ return [code, getStack()];
+}
+
+// TODO: import from muggle-string
+export function getStack() {
+ const stack = new Error().stack!;
+ let source = stack.split('\n')[3].trim();
+ if (source.endsWith(')')) {
+ source = source.slice(source.lastIndexOf('(') + 1, -1);
+ }
+ else {
+ source = source.slice(source.lastIndexOf(' ') + 1);
+ }
+ return source;
+}
+
+export function disableAllFeatures(override: Partial): VueCodeInformation {
+ return {
+ verification: false,
+ completion: false,
+ semantic: false,
+ navigation: false,
+ structure: false,
+ format: false,
+ ...override,
+ };
+}
+
+export function enableAllFeatures(override: Partial): VueCodeInformation {
+ return {
+ verification: true,
+ completion: true,
+ semantic: true,
+ navigation: true,
+ structure: true,
+ format: true,
+ ...override,
+ };
+}
+
+export function mergeFeatureSettings(base: VueCodeInformation, ...others: Partial[]): VueCodeInformation {
+ const result: VueCodeInformation = { ...base };
+ for (const info of others) {
+ for (const key in info) {
+ const value = info[key as keyof VueCodeInformation];
+ if (value) {
+ result[key as keyof VueCodeInformation] = value as any;
+ }
+ }
+ }
+ return result;
+}
diff --git a/packages/language-core/src/index.ts b/packages/language-core/src/index.ts
index 30594dbcbd..0fe246adef 100644
--- a/packages/language-core/src/index.ts
+++ b/packages/language-core/src/index.ts
@@ -8,10 +8,8 @@ export * from './utils/ts';
export * from './utils/parseSfc';
export * as scriptRanges from './parsers/scriptRanges';
-export * as sharedTypes from './utils/globalTypes';
export * from './utils/shared';
export { tsCodegen } from './plugins/vue-tsx';
export * from '@volar/language-core';
-export * from '@volar/source-map';
export type * as CompilerDOM from '@vue/compiler-dom';
diff --git a/packages/language-core/src/languageModule.ts b/packages/language-core/src/languageModule.ts
index dcf477b78d..fd3ef93813 100644
--- a/packages/language-core/src/languageModule.ts
+++ b/packages/language-core/src/languageModule.ts
@@ -1,9 +1,8 @@
-import type { Language } from '@volar/language-core';
+import type { LanguagePlugin } from '@volar/language-core';
import * as path from 'path-browserify';
-import { getDefaultVueLanguagePlugins } from './plugins';
+import { getDefaultVueLanguagePlugins, createPluginContext } from './plugins';
import { VueFile } from './virtualFile/vueFile';
import { VueCompilerOptions, VueLanguagePlugin } from './types';
-import * as sharedTypes from './utils/globalTypes';
import type * as ts from 'typescript/lib/tsserverlibrary';
import { resolveVueCompilerOptions } from './utils/ts';
@@ -33,32 +32,43 @@ function getVueFileRegistry(key: string, plugins: VueLanguagePlugin[]) {
return fileRegistry;
}
+function getFileRegistryKey(
+ compilerOptions: ts.CompilerOptions,
+ vueCompilerOptions: VueCompilerOptions,
+ plugins: ReturnType[],
+ globalTypesHolder: string | undefined,
+) {
+ const values = [
+ globalTypesHolder,
+ ...Object.keys(vueCompilerOptions)
+ .sort()
+ .filter(key => key !== 'plugins')
+ .map(key => [key, vueCompilerOptions[key as keyof VueCompilerOptions]]),
+ [...new Set(plugins.map(plugin => plugin.requiredCompilerOptions ?? []).flat())]
+ .sort()
+ .map(key => [key, compilerOptions[key as keyof ts.CompilerOptions]]),
+ ];
+ return JSON.stringify(values);
+}
+
export function createVueLanguage(
ts: typeof import('typescript/lib/tsserverlibrary'),
compilerOptions: ts.CompilerOptions = {},
_vueCompilerOptions: Partial = {},
codegenStack: boolean = false,
-): Language {
+ globalTypesHolder?: string
+): LanguagePlugin {
const vueCompilerOptions = resolveVueCompilerOptions(_vueCompilerOptions);
- const plugins = getDefaultVueLanguagePlugins(
+ const allowLanguageIds = new Set(['vue']);
+ const pluginContext = createPluginContext(
ts,
compilerOptions,
vueCompilerOptions,
codegenStack,
+ globalTypesHolder,
);
- const keys = [
- ...Object.keys(vueCompilerOptions)
- .sort()
- .filter(key => key !== 'plugins')
- .map(key => [key, vueCompilerOptions[key as keyof VueCompilerOptions]]),
- [...new Set(plugins.map(plugin => plugin.requiredCompilerOptions ?? []).flat())]
- .sort()
- .map(key => [key, compilerOptions[key as keyof ts.CompilerOptions]]),
- ];
- const fileRegistry = getVueFileRegistry(JSON.stringify(keys), _vueCompilerOptions.plugins ?? []);
-
- const allowLanguageIds = new Set(['vue']);
+ const plugins = getDefaultVueLanguagePlugins(pluginContext);
if (vueCompilerOptions.extensions.includes('.md')) {
allowLanguageIds.add('markdown');
@@ -67,49 +77,79 @@ export function createVueLanguage(
allowLanguageIds.add('html');
}
+ let fileRegistry: Map | undefined;
+
return {
- createVirtualFile(fileName, snapshot, languageId) {
- if (
- (languageId && allowLanguageIds.has(languageId))
- || (!languageId && vueCompilerOptions.extensions.some(ext => fileName.endsWith(ext)))
- ) {
+ createVirtualFile(fileName, languageId, snapshot) {
+ if (allowLanguageIds.has(languageId)) {
+
+ if (!fileRegistry) {
+
+ pluginContext.globalTypesHolder ??= fileName;
+
+ fileRegistry = getVueFileRegistry(
+ getFileRegistryKey(compilerOptions, vueCompilerOptions, plugins, pluginContext.globalTypesHolder),
+ vueCompilerOptions.plugins,
+ );
+ }
+
if (fileRegistry.has(fileName)) {
const reusedVueFile = fileRegistry.get(fileName)!;
reusedVueFile.update(snapshot);
return reusedVueFile;
}
- const vueFile = new VueFile(fileName, snapshot, vueCompilerOptions, plugins, ts, codegenStack);
+ const vueFile = new VueFile(fileName, languageId, snapshot, vueCompilerOptions, plugins, ts, codegenStack);
fileRegistry.set(fileName, vueFile);
return vueFile;
}
},
- updateVirtualFile(sourceFile, snapshot) {
- sourceFile.update(snapshot);
+ updateVirtualFile(vueFile, snapshot) {
+ vueFile.update(snapshot);
},
- resolveHost(host) {
- const sharedTypesSnapshot = ts.ScriptSnapshot.fromString(sharedTypes.getTypesCode(vueCompilerOptions));
- const sharedTypesFileName = path.join(host.rootPath, sharedTypes.baseName);
- return {
- ...host,
- resolveModuleName(moduleName, impliedNodeFormat) {
- if (impliedNodeFormat === ts.ModuleKind.ESNext && vueCompilerOptions.extensions.some(ext => moduleName.endsWith(ext))) {
- return `${moduleName}.js`;
- }
- return host.resolveModuleName?.(moduleName, impliedNodeFormat) ?? moduleName;
- },
- getScriptFileNames() {
- return [
- sharedTypesFileName,
- ...host.getScriptFileNames(),
- ];
- },
- getScriptSnapshot(fileName) {
- if (fileName === sharedTypesFileName) {
- return sharedTypesSnapshot;
+ disposeVirtualFile(vueFile, files) {
+ fileRegistry?.delete(vueFile.fileName);
+ if (vueFile.fileName === pluginContext.globalTypesHolder) {
+ if (fileRegistry?.size) {
+ for (const [fileName, file] of fileRegistry!) {
+ pluginContext.globalTypesHolder = fileName;
+
+ fileRegistry = getVueFileRegistry(
+ getFileRegistryKey(compilerOptions, vueCompilerOptions, plugins, pluginContext.globalTypesHolder),
+ vueCompilerOptions.plugins,
+ );
+
+ files?.updateSourceFile(
+ file.fileName,
+ file.languageId,
+ // force dirty
+ { ...file.snapshot },
+ );
+ break;
}
- return host.getScriptSnapshot(fileName);
- },
- };
+ }
+ else {
+ fileRegistry = undefined;
+ pluginContext.globalTypesHolder = undefined;
+ }
+ }
+ },
+ typescript: {
+ extraFileExtensions: vueCompilerOptions.extensions.map(ext => ({
+ extension: ext,
+ isMixedContent: true,
+ scriptKind: 7 satisfies ts.ScriptKind.Deferred,
+ })),
+ resolveSourceFileName(tsFileName) {
+ const baseName = path.basename(tsFileName);
+ if (baseName.indexOf('.vue.') >= 0) { // .vue.ts .vue.d.ts .vue.js .vue.jsx .vue.tsx
+ return tsFileName.substring(0, tsFileName.lastIndexOf('.vue.') + '.vue'.length);
+ }
+ },
+ resolveModuleName(moduleName, impliedNodeFormat) {
+ if (impliedNodeFormat === 99 satisfies ts.ModuleKind.ESNext && vueCompilerOptions.extensions.some(ext => moduleName.endsWith(ext))) {
+ return `${moduleName}.js`;
+ }
+ },
},
};
}
@@ -122,9 +162,10 @@ export function createLanguages(
compilerOptions: ts.CompilerOptions = {},
vueCompilerOptions: Partial = {},
codegenStack: boolean = false,
-): Language[] {
+ globalTypesHolder?: string
+): LanguagePlugin[] {
return [
- createVueLanguage(ts, compilerOptions, vueCompilerOptions, codegenStack),
+ createVueLanguage(ts, compilerOptions, vueCompilerOptions, codegenStack, globalTypesHolder),
...vueCompilerOptions.experimentalAdditionalLanguageModules?.map(module => require(module)) ?? [],
];
}
diff --git a/packages/language-core/src/parsers/scriptRanges.ts b/packages/language-core/src/parsers/scriptRanges.ts
index e8331b2d13..1c8ba7a0e8 100644
--- a/packages/language-core/src/parsers/scriptRanges.ts
+++ b/packages/language-core/src/parsers/scriptRanges.ts
@@ -1,6 +1,6 @@
import type { TextRange } from '../types';
import type * as ts from 'typescript/lib/tsserverlibrary';
-import { getStartEnd, parseBindingRanges } from './scriptSetupRanges';
+import { getNodeText, getStartEnd, parseBindingRanges } from './scriptSetupRanges';
export interface ScriptRanges extends ReturnType { }
@@ -17,12 +17,12 @@ export function parseScriptRanges(ts: typeof import('typescript/lib/tsserverlibr
const bindings = hasScriptSetup ? parseBindingRanges(ts, ast) : [];
- ast.forEachChild(raw => {
+ ts.forEachChild(ast, raw => {
if (ts.isExportAssignment(raw)) {
let node: ts.AsExpression | ts.ExportAssignment | ts.ParenthesizedExpression = raw;
- while (ts.isAsExpression(node.expression) || ts.isParenthesizedExpression(node.expression)) { // fix https://github.com/vuejs/language-tools/issues/1882
+ while (isAsExpression(node.expression) || ts.isParenthesizedExpression(node.expression)) { // fix https://github.com/vuejs/language-tools/issues/1882
node = node.expression;
}
@@ -39,12 +39,13 @@ export function parseScriptRanges(ts: typeof import('typescript/lib/tsserverlibr
if (obj) {
let componentsOptionNode: ts.ObjectLiteralExpression | undefined;
let nameOptionNode: ts.Expression | undefined;
- obj.forEachChild(node => {
+ ts.forEachChild(obj, node => {
if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name)) {
- if (node.name.escapedText === 'components' && ts.isObjectLiteralExpression(node.initializer)) {
+ const name = getNodeText(ts, node.name, ast);
+ if (name === 'components' && ts.isObjectLiteralExpression(node.initializer)) {
componentsOptionNode = node.initializer;
}
- if (node.name.escapedText === 'name') {
+ if (name === 'name') {
nameOptionNode = node.initializer;
}
}
@@ -68,6 +69,11 @@ export function parseScriptRanges(ts: typeof import('typescript/lib/tsserverlibr
};
function _getStartEnd(node: ts.Node) {
- return getStartEnd(node, ast);
+ return getStartEnd(ts, node, ast);
+ }
+
+ // isAsExpression is missing in tsc
+ function isAsExpression(node: ts.Node): node is ts.AsExpression {
+ return node.kind === ts.SyntaxKind.AsExpression;
}
}
diff --git a/packages/language-core/src/parsers/scriptSetupRanges.ts b/packages/language-core/src/parsers/scriptSetupRanges.ts
index a5a1e97228..8aded9fd62 100644
--- a/packages/language-core/src/parsers/scriptSetupRanges.ts
+++ b/packages/language-core/src/parsers/scriptSetupRanges.ts
@@ -34,8 +34,8 @@ export function parseScriptSetupRanges(
define?: ReturnType;
} = {};
- const definePropProposalA = vueCompilerOptions.experimentalDefinePropProposal === 'kevinEdition' || ast.getFullText().trimStart().startsWith('// @experimentalDefinePropProposal=kevinEdition');
- const definePropProposalB = vueCompilerOptions.experimentalDefinePropProposal === 'johnsonEdition' || ast.getFullText().trimStart().startsWith('// @experimentalDefinePropProposal=johnsonEdition');
+ const definePropProposalA = vueCompilerOptions.experimentalDefinePropProposal === 'kevinEdition' || ast.text.trimStart().startsWith('// @experimentalDefinePropProposal=kevinEdition');
+ const definePropProposalB = vueCompilerOptions.experimentalDefinePropProposal === 'johnsonEdition' || ast.text.trimStart().startsWith('// @experimentalDefinePropProposal=johnsonEdition');
const defineProp: {
name: TextRange | undefined;
nameIsString: boolean;
@@ -44,10 +44,10 @@ export function parseScriptSetupRanges(
required: boolean;
}[] = [];
const bindings = parseBindingRanges(ts, ast);
- const text = ast.getFullText();
+ const text = ast.text;
const leadingCommentEndOffset = ts.getLeadingCommentRanges(text, 0)?.reverse()[0].end ?? 0;
- ast.forEachChild(node => {
+ ts.forEachChild(ast, node => {
const isTypeExport = (ts.isTypeAliasDeclaration(node) || ts.isInterfaceDeclaration(node)) && node.modifiers?.some(mod => mod.kind === ts.SyntaxKind.ExportKeyword);
if (
!foundNonImportExportNode
@@ -57,18 +57,18 @@ export function parseScriptSetupRanges(
// fix https://github.com/vuejs/language-tools/issues/1223
&& !ts.isImportEqualsDeclaration(node)
) {
- const commentRanges = ts.getLeadingCommentRanges(text, node.getFullStart());
+ const commentRanges = ts.getLeadingCommentRanges(text, node.pos);
if (commentRanges?.length) {
const commentRange = commentRanges.sort((a, b) => a.pos - b.pos)[0];
importSectionEndOffset = commentRange.pos;
}
else {
- importSectionEndOffset = node.getStart(ast);
+ importSectionEndOffset = getStartEnd(ts, node, ast).start;
}
foundNonImportExportNode = true;
}
});
- ast.forEachChild(child => visitNode(child, [ast]));
+ ts.forEachChild(ast, child => visitNode(child, [ast]));
return {
leadingCommentEndOffset,
@@ -82,7 +82,7 @@ export function parseScriptSetupRanges(
};
function _getStartEnd(node: ts.Node) {
- return getStartEnd(node, ast);
+ return getStartEnd(ts, node, ast);
}
function parseDefineFunction(node: ts.CallExpression): TextRange & {
@@ -102,7 +102,7 @@ export function parseScriptSetupRanges(
ts.isCallExpression(node)
&& ts.isIdentifier(node.expression)
) {
- const callText = node.expression.getText(ast);
+ const callText = getNodeText(ts, node.expression, ast);
if (vueCompilerOptions.macros.defineModel.includes(callText)) {
let name: TextRange | undefined;
let options: ts.Node | undefined;
@@ -121,7 +121,7 @@ export function parseScriptSetupRanges(
let required = false;
if (options && ts.isObjectLiteralExpression(options)) {
for (const property of options.properties) {
- if (ts.isPropertyAssignment(property) && ts.isIdentifier(property.name) && property.name.getText(ast) === 'required' && property.initializer.kind === ts.SyntaxKind.TrueKeyword) {
+ if (ts.isPropertyAssignment(property) && ts.isIdentifier(property.name) && getNodeText(ts, property.name, ast) === 'required' && property.initializer.kind === ts.SyntaxKind.TrueKeyword) {
required = true;
break;
}
@@ -142,7 +142,7 @@ export function parseScriptSetupRanges(
const secondArg = node.arguments[1];
if (ts.isObjectLiteralExpression(secondArg)) {
for (const property of secondArg.properties) {
- if (ts.isPropertyAssignment(property) && ts.isIdentifier(property.name) && property.name.getText(ast) === 'required' && property.initializer.kind === ts.SyntaxKind.TrueKeyword) {
+ if (ts.isPropertyAssignment(property) && ts.isIdentifier(property.name) && getNodeText(ts, property.name, ast) === 'required' && property.initializer.kind === ts.SyntaxKind.TrueKeyword) {
required = true;
break;
}
@@ -181,13 +181,13 @@ export function parseScriptSetupRanges(
else if (vueCompilerOptions.macros.defineSlots.includes(callText)) {
slots.define = parseDefineFunction(node);
if (ts.isVariableDeclaration(parent)) {
- slots.name = parent.name.getText(ast);
+ slots.name = getNodeText(ts, parent.name, ast);
}
}
else if (vueCompilerOptions.macros.defineEmits.includes(callText)) {
emits.define = parseDefineFunction(node);
if (ts.isVariableDeclaration(parent)) {
- emits.name = parent.name.getText(ast);
+ emits.name = getNodeText(ts, parent.name, ast);
}
}
else if (vueCompilerOptions.macros.defineExpose.includes(callText)) {
@@ -199,7 +199,7 @@ export function parseScriptSetupRanges(
for (let i = parents.length - 1; i >= 0; i--) {
if (ts.isStatement(parents[i])) {
const statement = parents[i];
- statement.forEachChild(child => {
+ ts.forEachChild(statement, child => {
const range = _getStartEnd(child);
statementRange ??= range;
statementRange.end = range.end;
@@ -217,7 +217,7 @@ export function parseScriptSetupRanges(
};
if (ts.isVariableDeclaration(parent)) {
- props.name = parent.name.getText(ast);
+ props.name = getNodeText(ts, parent.name, ast);
}
if (node.arguments.length) {
props.define.arg = _getStartEnd(node.arguments[0]);
@@ -233,11 +233,11 @@ export function parseScriptSetupRanges(
props.withDefaults.arg = _getStartEnd(arg);
}
if (ts.isVariableDeclaration(parent)) {
- props.name = parent.name.getText(ast);
+ props.name = getNodeText(ts, parent.name, ast);
}
}
}
- node.forEachChild(child => {
+ ts.forEachChild(node, child => {
parents.push(node);
visitNode(child, parents);
parents.pop();
@@ -247,7 +247,7 @@ export function parseScriptSetupRanges(
export function parseBindingRanges(ts: typeof import('typescript/lib/tsserverlibrary'), sourceFile: ts.SourceFile) {
const bindings: TextRange[] = [];
- sourceFile.forEachChild(node => {
+ ts.forEachChild(sourceFile, node => {
if (ts.isVariableStatement(node)) {
for (const node_2 of node.declarationList.declarations) {
const vars = _findBindingVars(node_2.name);
@@ -290,7 +290,7 @@ export function parseBindingRanges(ts: typeof import('typescript/lib/tsserverlib
});
return bindings;
function _getStartEnd(node: ts.Node) {
- return getStartEnd(node, sourceFile);
+ return getStartEnd(ts, node, sourceFile);
}
function _findBindingVars(left: ts.BindingName) {
return findBindingVars(ts, left, sourceFile);
@@ -303,7 +303,7 @@ export function findBindingVars(ts: typeof import('typescript/lib/tsserverlibrar
return vars;
function worker(_node: ts.Node) {
if (ts.isIdentifier(_node)) {
- vars.push(getStartEnd(_node, sourceFile));
+ vars.push(getStartEnd(ts, _node, sourceFile));
}
// { ? } = ...
// [ ? ] = ...
@@ -320,7 +320,7 @@ export function findBindingVars(ts: typeof import('typescript/lib/tsserverlibrar
}
// { foo } = ...
else if (ts.isShorthandPropertyAssignment(_node)) {
- vars.push(getStartEnd(_node.name, sourceFile));
+ vars.push(getStartEnd(ts, _node.name, sourceFile));
}
// { ...? } = ...
// [ ...? ] = ...
@@ -330,9 +330,22 @@ export function findBindingVars(ts: typeof import('typescript/lib/tsserverlibrar
}
}
-export function getStartEnd(node: ts.Node, sourceFile: ts.SourceFile) {
+export function getStartEnd(
+ ts: typeof import('typescript/lib/tsserverlibrary'),
+ node: ts.Node,
+ sourceFile: ts.SourceFile
+) {
return {
- start: node.getStart(sourceFile),
- end: node.getEnd(),
+ start: (ts as any).getTokenPosOfNode(node, sourceFile) as number,
+ end: node.end,
};
}
+
+export function getNodeText(
+ ts: typeof import('typescript/lib/tsserverlibrary'),
+ node: ts.Node,
+ sourceFile: ts.SourceFile
+) {
+ const { start, end } = getStartEnd(ts, node, sourceFile);
+ return sourceFile.text.substring(start, end);
+}
diff --git a/packages/language-core/src/plugins.ts b/packages/language-core/src/plugins.ts
index 775a1adfa2..027e4a13e1 100644
--- a/packages/language-core/src/plugins.ts
+++ b/packages/language-core/src/plugins.ts
@@ -12,25 +12,13 @@ import { VueCompilerOptions, VueLanguagePlugin } from './types';
import * as CompilerDOM from '@vue/compiler-dom';
import * as CompilerVue2 from './utils/vue2TemplateCompiler';
-export function getDefaultVueLanguagePlugins(
+export function createPluginContext(
ts: typeof import('typescript/lib/tsserverlibrary'),
compilerOptions: ts.CompilerOptions,
vueCompilerOptions: VueCompilerOptions,
codegenStack: boolean,
+ globalTypesHolder: string | undefined,
) {
-
- const plugins: VueLanguagePlugin[] = [
- useMdFilePlugin, // .md for VitePress
- useHtmlFilePlugin, // .html for PetiteVue
- useVueFilePlugin, // .vue and others for Vue
- useHtmlTemplatePlugin,
- useVueSfcStyles,
- useVueSfcCustomBlocks,
- useVueSfcScriptsFormat,
- useVueSfcTemplate,
- useVueTsx,
- ...vueCompilerOptions.plugins,
- ];
const pluginCtx: Parameters[0] = {
modules: {
'@vue/compiler-dom': vueCompilerOptions.target < 3
@@ -44,9 +32,28 @@ export function getDefaultVueLanguagePlugins(
compilerOptions,
vueCompilerOptions,
codegenStack,
+ globalTypesHolder,
};
+ return pluginCtx;
+}
+
+export function getDefaultVueLanguagePlugins(pluginContext: Parameters[0]) {
+
+ const plugins: VueLanguagePlugin[] = [
+ useMdFilePlugin, // .md for VitePress
+ useHtmlFilePlugin, // .html for PetiteVue
+ useVueFilePlugin, // .vue and others for Vue
+ useHtmlTemplatePlugin,
+ useVueSfcStyles,
+ useVueSfcCustomBlocks,
+ useVueSfcScriptsFormat,
+ useVueSfcTemplate,
+ useVueTsx,
+ ...pluginContext.vueCompilerOptions.plugins,
+ ];
+
const pluginInstances = plugins
- .map(plugin => plugin(pluginCtx))
+ .map(plugin => plugin(pluginContext))
.sort((a, b) => {
const aOrder = a.order ?? 0;
const bOrder = b.order ?? 0;
diff --git a/packages/language-core/src/plugins/file-md.ts b/packages/language-core/src/plugins/file-md.ts
index b4d37b339b..d8a22f8c6c 100644
--- a/packages/language-core/src/plugins/file-md.ts
+++ b/packages/language-core/src/plugins/file-md.ts
@@ -1,4 +1,4 @@
-import { buildMappings, Segment, SourceMap, toString } from '@volar/source-map';
+import { buildMappings, Segment, SourceMap, toString } from '@volar/language-core';
import type { SFCBlock } from '@vue/compiler-sfc';
import { VueLanguagePlugin } from '../types';
import { parse } from '../utils/parseSfc';
@@ -74,8 +74,8 @@ const plugin: VueLanguagePlugin = () => {
return sfc;
function transformRange(block: SFCBlock) {
- block.loc.start.offset = file2VueSourceMap.toSourceOffset(block.loc.start.offset)?.[0] ?? -1;
- block.loc.end.offset = file2VueSourceMap.toSourceOffset(block.loc.end.offset)?.[0] ?? -1;
+ block.loc.start.offset = file2VueSourceMap.getSourceOffset(block.loc.start.offset)?.[0] ?? -1;
+ block.loc.end.offset = file2VueSourceMap.getSourceOffset(block.loc.end.offset)?.[0] ?? -1;
}
};
}
diff --git a/packages/language-core/src/plugins/vue-sfc-customblocks.ts b/packages/language-core/src/plugins/vue-sfc-customblocks.ts
index b2bdf2e79e..7ec8d1d5d0 100644
--- a/packages/language-core/src/plugins/vue-sfc-customblocks.ts
+++ b/packages/language-core/src/plugins/vue-sfc-customblocks.ts
@@ -1,4 +1,4 @@
-import { FileCapabilities, FileRangeCapabilities } from '@volar/language-core';
+import { enableAllFeatures } from '../generators/utils';
import { VueLanguagePlugin } from '../types';
const customBlockReg = /^(.*)\.customBlock_([^_]+)_(\d+)\.([^.]+)$/;
@@ -24,12 +24,11 @@ const plugin: VueLanguagePlugin = () => {
const index = parseInt(match[3]);
const customBlock = sfc.customBlocks[index];
- embeddedFile.capabilities = FileCapabilities.full;
embeddedFile.content.push([
customBlock.content,
customBlock.name,
0,
- FileRangeCapabilities.full,
+ enableAllFeatures({}),
]);
}
},
diff --git a/packages/language-core/src/plugins/vue-sfc-scripts.ts b/packages/language-core/src/plugins/vue-sfc-scripts.ts
index e50695bf7c..df45f8cb0e 100644
--- a/packages/language-core/src/plugins/vue-sfc-scripts.ts
+++ b/packages/language-core/src/plugins/vue-sfc-scripts.ts
@@ -1,4 +1,4 @@
-import { FileCapabilities, FileKind } from '@volar/language-core';
+import { disableAllFeatures } from '../generators/utils';
import { VueLanguagePlugin } from '../types';
const scriptFormatReg = /^(.*)\.script_format\.([^.]+)$/;
@@ -26,18 +26,14 @@ const plugin: VueLanguagePlugin = () => {
const scriptSetupMatch = embeddedFile.fileName.match(scriptSetupFormatReg);
const script = scriptMatch ? sfc.script : scriptSetupMatch ? sfc.scriptSetup : undefined;
if (script) {
- embeddedFile.kind = FileKind.TextFile;
- embeddedFile.capabilities = {
- ...FileCapabilities.full,
- diagnostic: false,
- codeAction: false,
- inlayHint: false,
- };
embeddedFile.content.push([
script.content,
script.name,
0,
- {},
+ disableAllFeatures({
+ structure: true,
+ format: true,
+ }),
]);
}
},
diff --git a/packages/language-core/src/plugins/vue-sfc-styles.ts b/packages/language-core/src/plugins/vue-sfc-styles.ts
index b750349a1d..b661a8d88f 100644
--- a/packages/language-core/src/plugins/vue-sfc-styles.ts
+++ b/packages/language-core/src/plugins/vue-sfc-styles.ts
@@ -1,4 +1,4 @@
-import { FileCapabilities, FileRangeCapabilities } from '@volar/language-core';
+import { enableAllFeatures } from '../generators/utils';
import { VueLanguagePlugin } from '../types';
const styleReg = /^(.*)\.style_(\d+)\.([^.]+)$/;
@@ -24,12 +24,11 @@ const plugin: VueLanguagePlugin = () => {
const index = parseInt(match[2]);
const style = sfc.styles[index];
- embeddedFile.capabilities = FileCapabilities.full;
embeddedFile.content.push([
style.content,
style.name,
0,
- FileRangeCapabilities.full,
+ enableAllFeatures({}),
]);
}
},
diff --git a/packages/language-core/src/plugins/vue-sfc-template.ts b/packages/language-core/src/plugins/vue-sfc-template.ts
index b21e8e8cd4..1b366f235a 100644
--- a/packages/language-core/src/plugins/vue-sfc-template.ts
+++ b/packages/language-core/src/plugins/vue-sfc-template.ts
@@ -1,4 +1,4 @@
-import { FileCapabilities, FileRangeCapabilities } from '@volar/language-core';
+import { enableAllFeatures } from '../generators/utils';
import { VueLanguagePlugin } from '../types';
const templateReg = /^(.*)\.template\.([^.]+)$/;
@@ -19,12 +19,11 @@ const plugin: VueLanguagePlugin = () => {
resolveEmbeddedFile(_fileName, sfc, embeddedFile) {
const match = embeddedFile.fileName.match(templateReg);
if (match && sfc.template) {
- embeddedFile.capabilities = FileCapabilities.full;
embeddedFile.content.push([
sfc.template.content,
sfc.template.name,
0,
- FileRangeCapabilities.full,
+ enableAllFeatures({}),
]);
}
},
diff --git a/packages/language-core/src/plugins/vue-tsx.ts b/packages/language-core/src/plugins/vue-tsx.ts
index b3cb5e826b..1291312261 100644
--- a/packages/language-core/src/plugins/vue-tsx.ts
+++ b/packages/language-core/src/plugins/vue-tsx.ts
@@ -1,11 +1,11 @@
+import { CodeInformation, Mapping, Segment, StackNode, track } from '@volar/language-core';
import { computed, computedSet } from 'computeds';
import { generate as generateScript } from '../generators/script';
import { generate as generateTemplate } from '../generators/template';
import { parseScriptRanges } from '../parsers/scriptRanges';
import { parseScriptSetupRanges } from '../parsers/scriptSetupRanges';
-import { Sfc, VueLanguagePlugin } from '../types';
-import { FileCapabilities, FileKind } from '@volar/language-core';
-import * as muggle from 'muggle-string';
+import { Code, Sfc, VueLanguagePlugin } from '../types';
+import { enableAllFeatures } from '../generators/utils';
const templateFormatReg = /^\.template_format\.ts$/;
const templateStyleCssReg = /^\.template_style\.css$/;
@@ -43,41 +43,39 @@ const plugin: VueLanguagePlugin = (ctx) => {
resolveEmbeddedFile(fileName, sfc, embeddedFile) {
const _tsx = useTsx(fileName, sfc);
+ const lang = _tsx.lang();
const suffix = embeddedFile.fileName.replace(fileName, '');
- if (suffix === '.' + _tsx.lang()) {
- embeddedFile.kind = FileKind.TypeScriptHostFile;
- embeddedFile.capabilities = {
- ...FileCapabilities.full,
- foldingRange: false,
- documentFormatting: false,
- documentSymbol: false,
+ if (suffix === '.' + lang) {
+ embeddedFile.typescript = {
+ scriptKind: lang === 'js' ? ctx.modules.typescript.ScriptKind.JS
+ : lang === 'jsx' ? ctx.modules.typescript.ScriptKind.JSX
+ : lang === 'tsx' ? ctx.modules.typescript.ScriptKind.TSX
+ : ctx.modules.typescript.ScriptKind.TS
};
const tsx = _tsx.generatedScript();
if (tsx) {
- const [content, contentStacks] = ctx.codegenStack ? muggle.track([...tsx.codes], [...tsx.codeStacks]) : [[...tsx.codes], [...tsx.codeStacks]];
+ const [content, contentStacks] = ctx.codegenStack ? track([...tsx.codes], [...tsx.codeStacks]) : [[...tsx.codes], [...tsx.codeStacks]];
+ content.forEach(code => {
+ if (typeof code !== 'string') {
+ code[3].structure = false;
+ code[3].format = false;
+ }
+ });
embeddedFile.content = content;
embeddedFile.contentStacks = contentStacks;
- embeddedFile.mirrorBehaviorMappings = [...tsx.mirrorBehaviorMappings];
+ embeddedFile.linkedCodeMappings = [...tsx.linkedCodeMappings];
}
}
else if (suffix.match(templateFormatReg)) {
embeddedFile.parentFileName = fileName + '.template.' + sfc.template?.lang;
- embeddedFile.kind = FileKind.TextFile;
- embeddedFile.capabilities = {
- ...FileCapabilities.full,
- diagnostic: false,
- foldingRange: false,
- codeAction: false,
- inlayHint: false,
- };
const template = _tsx.generatedTemplate();
if (template) {
const [content, contentStacks] = ctx.codegenStack
- ? muggle.track([...template.formatCodes], [...template.formatCodeStacks])
- : [[...template.formatCodes], [...template.formatCodeStacks]];
+ ? track([...template.formatCodes], template.formatCodeStacks.map(stack => ({ stack, length: 1 })))
+ : [[...template.formatCodes], template.formatCodeStacks.map(stack => ({ stack, length: 1 }))];
embeddedFile.content = content;
embeddedFile.contentStacks = contentStacks;
}
@@ -90,7 +88,7 @@ const plugin: VueLanguagePlugin = (ctx) => {
cssVar.text,
style.name,
cssVar.offset,
- {},
+ enableAllFeatures({}),
]);
embeddedFile.content.push(');\n');
}
@@ -103,14 +101,11 @@ const plugin: VueLanguagePlugin = (ctx) => {
const template = _tsx.generatedTemplate();
if (template) {
const [content, contentStacks] = ctx.codegenStack
- ? muggle.track([...template.cssCodes], [...template.cssCodeStacks])
- : [[...template.cssCodes], [...template.cssCodeStacks]];
- embeddedFile.content = content;
+ ? track([...template.cssCodes], template.cssCodeStacks.map(stack => ({ stack, length: 1 })))
+ : [[...template.cssCodes], template.cssCodeStacks.map(stack => ({ stack, length: 1 }))];
+ embeddedFile.content = content as Segment[];
embeddedFile.contentStacks = contentStacks;
}
-
- // for color pickers support
- embeddedFile.capabilities.documentSymbol = true;
}
},
};
@@ -125,9 +120,13 @@ const plugin: VueLanguagePlugin = (ctx) => {
export default plugin;
-function createTsx(fileName: string, _sfc: Sfc, { vueCompilerOptions, compilerOptions, codegenStack, modules }: Parameters[0]) {
+function createTsx(
+ fileName: string,
+ _sfc: Sfc,
+ ctx: Parameters[0],
+) {
- const ts = modules.typescript;
+ const ts = ctx.modules.typescript;
const lang = computed(() => {
return !_sfc.script && !_sfc.scriptSetup ? 'ts'
: _sfc.scriptSetup && _sfc.scriptSetup.lang !== 'js' ? _sfc.scriptSetup.lang
@@ -141,11 +140,11 @@ function createTsx(fileName: string, _sfc: Sfc, { vueCompilerOptions, compilerOp
);
const scriptSetupRanges = computed(() =>
_sfc.scriptSetup
- ? parseScriptSetupRanges(ts, _sfc.scriptSetup.ast, vueCompilerOptions)
+ ? parseScriptSetupRanges(ts, _sfc.scriptSetup.ast, ctx.vueCompilerOptions)
: undefined
);
const shouldGenerateScopedClasses = computed(() => {
- const option = vueCompilerOptions.experimentalResolveStyleCssClasses;
+ const option = ctx.vueCompilerOptions.experimentalResolveStyleCssClasses;
return _sfc.styles.some(s => {
return option === 'always' || (option === 'scoped' && s.scoped);
});
@@ -159,7 +158,7 @@ function createTsx(fileName: string, _sfc: Sfc, { vueCompilerOptions, compilerOp
}
for (const style of _sfc.styles) {
- const option = vueCompilerOptions.experimentalResolveStyleCssClasses;
+ const option = ctx.vueCompilerOptions.experimentalResolveStyleCssClasses;
if (option === 'always' || (option === 'scoped' && style.scoped)) {
for (const className of style.classNames) {
classes.add(className.text.substring(1));
@@ -174,36 +173,108 @@ function createTsx(fileName: string, _sfc: Sfc, { vueCompilerOptions, compilerOp
if (!_sfc.template)
return;
- return generateTemplate(
+ const tsCodes: Code[] = [];
+ const tsFormatCodes: Code[] = [];
+ const inlineCssCodes: Code[] = [];
+ const tsCodegenStacks: string[] = [];
+ const tsFormatCodegenStacks: string[] = [];
+ const inlineCssCodegenStacks: string[] = [];
+ const codegen = generateTemplate(
ts,
- compilerOptions,
- vueCompilerOptions,
+ ctx.compilerOptions,
+ ctx.vueCompilerOptions,
_sfc.template,
shouldGenerateScopedClasses(),
stylesScopedClasses(),
hasScriptSetupSlots(),
slotsAssignName(),
propsAssignName(),
- codegenStack,
+ ctx.codegenStack,
);
+
+ let current = codegen.next();
+
+ while (!current.done) {
+ const [type, code, stack] = current.value;
+ if (type === 'ts') {
+ tsCodes.push(code);
+ }
+ else if (type === 'tsFormat') {
+ tsFormatCodes.push(code);
+ }
+ else if (type === 'inlineCss') {
+ inlineCssCodes.push(code);
+ }
+ if (ctx.codegenStack) {
+ if (type === 'ts') {
+ tsCodegenStacks.push(stack);
+ }
+ else if (type === 'tsFormat') {
+ tsFormatCodegenStacks.push(stack);
+ }
+ else if (type === 'inlineCss') {
+ inlineCssCodegenStacks.push(stack);
+ }
+ }
+ current = codegen.next();
+ }
+
+ return {
+ ...current.value,
+ codes: tsCodes,
+ codeStacks: tsCodegenStacks,
+ formatCodes: tsFormatCodes,
+ formatCodeStacks: tsFormatCodegenStacks,
+ cssCodes: inlineCssCodes,
+ cssCodeStacks: inlineCssCodegenStacks,
+ };
});
const hasScriptSetupSlots = computed(() => !!scriptSetupRanges()?.slots.define);
const slotsAssignName = computed(() => scriptSetupRanges()?.slots.name);
const propsAssignName = computed(() => scriptSetupRanges()?.props.name);
- const generatedScript = computed(() => generateScript(
- ts,
- fileName,
- _sfc.script,
- _sfc.scriptSetup,
- _sfc.styles,
- lang(),
- scriptRanges(),
- scriptSetupRanges(),
- generatedTemplate(),
- compilerOptions,
- vueCompilerOptions,
- codegenStack,
- ));
+ const generatedScript = computed(() => {
+ const codes: Code[] = [];
+ const codeStacks: StackNode[] = [];
+ const linkedCodeMappings: Mapping[] = [];
+ const _template = generatedTemplate();
+ let generatedLength = 0;
+ for (const [code, stack] of generateScript(
+ ts,
+ fileName,
+ _sfc.script,
+ _sfc.scriptSetup,
+ _sfc.styles,
+ lang(),
+ scriptRanges(),
+ scriptSetupRanges(),
+ _template ? {
+ tsCodes: _template.codes,
+ tsCodegenStacks: _template.codeStacks,
+ accessedGlobalVariables: _template.accessedGlobalVariables,
+ hasSlot: _template.hasSlot,
+ tagNames: new Set(_template.tagOffsetsMap.keys()),
+ } : undefined,
+ ctx.compilerOptions,
+ ctx.vueCompilerOptions,
+ ctx.globalTypesHolder,
+ () => generatedLength,
+ linkedCodeMappings,
+ ctx.codegenStack,
+ )) {
+ codes.push(code);
+ if (ctx.codegenStack) {
+ codeStacks.push({ stack, length: 1 });
+ }
+ generatedLength += typeof code === 'string'
+ ? code.length
+ : code[0].length;
+ };
+ return {
+ codes,
+ codeStacks,
+ linkedCodeMappings,
+ };
+ });
return {
scriptRanges,
diff --git a/packages/language-core/src/types.ts b/packages/language-core/src/types.ts
index 4096cadafd..cca3f9cfc0 100644
--- a/packages/language-core/src/types.ts
+++ b/packages/language-core/src/types.ts
@@ -2,6 +2,7 @@ import type * as CompilerDOM from '@vue/compiler-dom';
import type { SFCParseResult } from '@vue/compiler-sfc';
import type * as ts from 'typescript/lib/tsserverlibrary';
import type { VueEmbeddedFile } from './virtualFile/embeddedFile';
+import type { CodeInformation, Segment } from '@volar/language-core';
export type { SFCParseResult } from '@vue/compiler-sfc';
@@ -10,6 +11,23 @@ export type RawVueCompilerOptions = Partial;
+
export interface VueCompilerOptions {
target: number;
lib: string;
@@ -31,7 +49,6 @@ export interface VueCompilerOptions {
withDefaults: string[];
};
plugins: VueLanguagePlugin[];
- hooks: string[];
// experimental
experimentalDefinePropProposal: 'kevinEdition' | 'johnsonEdition' | false;
@@ -49,6 +66,7 @@ export type VueLanguagePlugin = (ctx: {
compilerOptions: ts.CompilerOptions;
vueCompilerOptions: VueCompilerOptions;
codegenStack: boolean;
+ globalTypesHolder: string | undefined;
}) => {
version: 1;
name?: string;
diff --git a/packages/language-core/src/utils/globalTypes.ts b/packages/language-core/src/utils/globalTypes.ts
deleted file mode 100644
index df049fca19..0000000000
--- a/packages/language-core/src/utils/globalTypes.ts
+++ /dev/null
@@ -1,133 +0,0 @@
-import { VueCompilerOptions } from '../types';
-import { getSlotsPropertyName } from './shared';
-
-export const baseName = '__VLS_types.d.ts';
-
-export function getTypesCode(vueCompilerOptions: VueCompilerOptions) {
- return `
-// @ts-nocheck
-
-type __VLS_IntrinsicElements = __VLS_PickNotAny>>;
-type __VLS_Element = __VLS_PickNotAny;
-
-type __VLS_IsAny = 0 extends 1 & T ? true : false;
-type __VLS_PickNotAny = __VLS_IsAny extends true ? B : A;
-
-type __VLS_Prettify = { [K in keyof T]: T[K]; } & {};
-
-type __VLS_OmitKeepDiscriminatedUnion =
- T extends any
- ? Pick>
- : never;
-
-type __VLS_GlobalComponents =
- __VLS_PickNotAny
- & __VLS_PickNotAny
- & __VLS_PickNotAny
- & Pick;
-
-declare const __VLS_intrinsicElements: __VLS_IntrinsicElements;
-
-// v-for
-declare function __VLS_getVForSourceType(source: number): [number, number, number][];
-declare function __VLS_getVForSourceType(source: string): [string, number, number][];
-declare function __VLS_getVForSourceType(source: T): [
- T[number], // item
- number, // key
- number, // index
-][];
-declare function __VLS_getVForSourceType }>(source: T): [
- T extends { [Symbol.iterator](): Iterator } ? T1 : never, // item
- number, // key
- undefined, // index
-][];
-declare function __VLS_getVForSourceType(source: T): [
- T[keyof T], // item
- keyof T, // key
- number, // index
-][];
-
-declare function __VLS_getSlotParams(slot: T): Parameters<__VLS_PickNotAny