@vue/language-service 1.8.6 → 1.8.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/helpers.d.ts +4 -3
- package/out/helpers.js +51 -44
- package/out/ideFeatures/nameCasing.js +4 -4
- package/out/languageService.js +28 -74
- package/out/plugins/vue-extract-file.d.ts +5 -0
- package/out/plugins/vue-extract-file.js +86 -20
- package/out/plugins/vue-template.js +47 -34
- package/package.json +15 -15
package/out/helpers.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import * as vue from '@vue/language-core';
|
|
2
2
|
import * as embedded from '@volar/language-core';
|
|
3
3
|
import type * as ts from 'typescript/lib/tsserverlibrary';
|
|
4
|
-
export declare function
|
|
5
|
-
export declare function
|
|
6
|
-
export declare function
|
|
4
|
+
export declare function getPropsByTag(ts: typeof import('typescript/lib/tsserverlibrary'), tsLs: ts.LanguageService, sourceFile: embedded.VirtualFile, tag: string, vueCompilerOptions: vue.VueCompilerOptions, requiredOnly?: boolean): string[];
|
|
5
|
+
export declare function getEventsOfTag(ts: typeof import('typescript/lib/tsserverlibrary'), tsLs: ts.LanguageService, sourceFile: embedded.VirtualFile, tag: string, vueCompilerOptions: vue.VueCompilerOptions): string[];
|
|
6
|
+
export declare function getTemplateCtx(ts: typeof import('typescript/lib/tsserverlibrary'), tsLs: ts.LanguageService, sourceFile: embedded.VirtualFile): string[] | undefined;
|
|
7
|
+
export declare function getComponentNames(ts: typeof import('typescript/lib/tsserverlibrary'), tsLs: ts.LanguageService, sourceFile: embedded.VirtualFile, vueCompilerOptions: vue.VueCompilerOptions): string[];
|
|
7
8
|
export declare function getElementAttrs(ts: typeof import('typescript/lib/tsserverlibrary'), tsLs: ts.LanguageService, tsLsHost: ts.LanguageServiceHost, tagName: string): string[];
|
|
8
9
|
type Tags = Map<string, {
|
|
9
10
|
offsets: number[];
|
package/out/helpers.js
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getTemplateTagsAndAttrs = exports.getElementAttrs = exports.
|
|
3
|
+
exports.getTemplateTagsAndAttrs = exports.getElementAttrs = exports.getComponentNames = exports.getTemplateCtx = exports.getEventsOfTag = exports.getPropsByTag = void 0;
|
|
4
4
|
const vue = require("@vue/language-core");
|
|
5
5
|
const embedded = require("@volar/language-core");
|
|
6
6
|
const reactivity_1 = require("@vue/reactivity");
|
|
7
7
|
const language_core_1 = require("@vue/language-core");
|
|
8
8
|
const shared_1 = require("@vue/shared");
|
|
9
|
-
function
|
|
9
|
+
function getPropsByTag(ts, tsLs, sourceFile, tag, vueCompilerOptions, requiredOnly = false) {
|
|
10
10
|
const checker = tsLs.getProgram().getTypeChecker();
|
|
11
|
-
const components =
|
|
11
|
+
const components = getVariableType(ts, tsLs, sourceFile, '__VLS_components');
|
|
12
12
|
if (!components)
|
|
13
13
|
return [];
|
|
14
14
|
const name = tag.split('.');
|
|
15
|
-
let componentSymbol = components.
|
|
15
|
+
let componentSymbol = components.type.getProperty(name[0]);
|
|
16
16
|
if (!componentSymbol && !vueCompilerOptions.nativeTags.includes(name[0])) {
|
|
17
|
-
componentSymbol = components.
|
|
18
|
-
?? components.
|
|
17
|
+
componentSymbol = components.type.getProperty((0, shared_1.camelize)(name[0]))
|
|
18
|
+
?? components.type.getProperty((0, shared_1.capitalize)((0, shared_1.camelize)(name[0])));
|
|
19
19
|
}
|
|
20
20
|
if (!componentSymbol)
|
|
21
21
|
return [];
|
|
22
|
-
let componentType = checker.getTypeOfSymbolAtLocation(componentSymbol, components.
|
|
22
|
+
let componentType = checker.getTypeOfSymbolAtLocation(componentSymbol, components.node);
|
|
23
23
|
for (let i = 1; i < name.length; i++) {
|
|
24
24
|
componentSymbol = componentType.getProperty(name[i]);
|
|
25
25
|
if (componentSymbol) {
|
|
26
|
-
componentType = checker.getTypeOfSymbolAtLocation(componentSymbol, components.
|
|
26
|
+
componentType = checker.getTypeOfSymbolAtLocation(componentSymbol, components.node);
|
|
27
27
|
}
|
|
28
28
|
else {
|
|
29
29
|
return [];
|
|
@@ -33,7 +33,7 @@ function checkPropsOfTag(ts, tsLs, sourceFile, tag, vueCompilerOptions, required
|
|
|
33
33
|
for (const sig of componentType.getCallSignatures()) {
|
|
34
34
|
const propParam = sig.parameters[0];
|
|
35
35
|
if (propParam) {
|
|
36
|
-
const propsType = checker.getTypeOfSymbolAtLocation(propParam, components.
|
|
36
|
+
const propsType = checker.getTypeOfSymbolAtLocation(propParam, components.node);
|
|
37
37
|
const props = propsType.getProperties();
|
|
38
38
|
for (const prop of props) {
|
|
39
39
|
if (!requiredOnly || !(prop.flags & ts.SymbolFlags.Optional)) {
|
|
@@ -46,7 +46,7 @@ function checkPropsOfTag(ts, tsLs, sourceFile, tag, vueCompilerOptions, required
|
|
|
46
46
|
const instanceType = sig.getReturnType();
|
|
47
47
|
const propsSymbol = instanceType.getProperty('$props');
|
|
48
48
|
if (propsSymbol) {
|
|
49
|
-
const propsType = checker.getTypeOfSymbolAtLocation(propsSymbol, components.
|
|
49
|
+
const propsType = checker.getTypeOfSymbolAtLocation(propsSymbol, components.node);
|
|
50
50
|
const props = propsType.getProperties();
|
|
51
51
|
for (const prop of props) {
|
|
52
52
|
if (prop.flags & ts.SymbolFlags.Method) { // #2443
|
|
@@ -60,25 +60,25 @@ function checkPropsOfTag(ts, tsLs, sourceFile, tag, vueCompilerOptions, required
|
|
|
60
60
|
}
|
|
61
61
|
return [...result];
|
|
62
62
|
}
|
|
63
|
-
exports.
|
|
64
|
-
function
|
|
63
|
+
exports.getPropsByTag = getPropsByTag;
|
|
64
|
+
function getEventsOfTag(ts, tsLs, sourceFile, tag, vueCompilerOptions) {
|
|
65
65
|
const checker = tsLs.getProgram().getTypeChecker();
|
|
66
|
-
const components =
|
|
66
|
+
const components = getVariableType(ts, tsLs, sourceFile, '__VLS_components');
|
|
67
67
|
if (!components)
|
|
68
68
|
return [];
|
|
69
69
|
const name = tag.split('.');
|
|
70
|
-
let componentSymbol = components.
|
|
70
|
+
let componentSymbol = components.type.getProperty(name[0]);
|
|
71
71
|
if (!componentSymbol && !vueCompilerOptions.nativeTags.includes(name[0])) {
|
|
72
|
-
componentSymbol = components.
|
|
73
|
-
?? components.
|
|
72
|
+
componentSymbol = components.type.getProperty((0, shared_1.camelize)(name[0]))
|
|
73
|
+
?? components.type.getProperty((0, shared_1.capitalize)((0, shared_1.camelize)(name[0])));
|
|
74
74
|
}
|
|
75
75
|
if (!componentSymbol)
|
|
76
76
|
return [];
|
|
77
|
-
let componentType = checker.getTypeOfSymbolAtLocation(componentSymbol, components.
|
|
77
|
+
let componentType = checker.getTypeOfSymbolAtLocation(componentSymbol, components.node);
|
|
78
78
|
for (let i = 1; i < name.length; i++) {
|
|
79
79
|
componentSymbol = componentType.getProperty(name[i]);
|
|
80
80
|
if (componentSymbol) {
|
|
81
|
-
componentType = checker.getTypeOfSymbolAtLocation(componentSymbol, components.
|
|
81
|
+
componentType = checker.getTypeOfSymbolAtLocation(componentSymbol, components.node);
|
|
82
82
|
}
|
|
83
83
|
else {
|
|
84
84
|
return [];
|
|
@@ -95,11 +95,11 @@ function checkEventsOfTag(ts, tsLs, sourceFile, tag, vueCompilerOptions) {
|
|
|
95
95
|
const instanceType = sig.getReturnType();
|
|
96
96
|
const emitSymbol = instanceType.getProperty('$emit');
|
|
97
97
|
if (emitSymbol) {
|
|
98
|
-
const emitType = checker.getTypeOfSymbolAtLocation(emitSymbol, components.
|
|
98
|
+
const emitType = checker.getTypeOfSymbolAtLocation(emitSymbol, components.node);
|
|
99
99
|
for (const call of emitType.getCallSignatures()) {
|
|
100
100
|
const eventNameParamSymbol = call.parameters[0];
|
|
101
101
|
if (eventNameParamSymbol) {
|
|
102
|
-
const eventNameParamType = checker.getTypeOfSymbolAtLocation(eventNameParamSymbol, components.
|
|
102
|
+
const eventNameParamType = checker.getTypeOfSymbolAtLocation(eventNameParamSymbol, components.node);
|
|
103
103
|
if (eventNameParamType.isStringLiteral()) {
|
|
104
104
|
result.add(eventNameParamType.value);
|
|
105
105
|
}
|
|
@@ -109,17 +109,24 @@ function checkEventsOfTag(ts, tsLs, sourceFile, tag, vueCompilerOptions) {
|
|
|
109
109
|
}
|
|
110
110
|
return [...result];
|
|
111
111
|
}
|
|
112
|
-
exports.
|
|
113
|
-
function
|
|
114
|
-
return
|
|
115
|
-
?.
|
|
112
|
+
exports.getEventsOfTag = getEventsOfTag;
|
|
113
|
+
function getTemplateCtx(ts, tsLs, sourceFile) {
|
|
114
|
+
return getVariableType(ts, tsLs, sourceFile, '__VLS_ctx')
|
|
115
|
+
?.type
|
|
116
|
+
?.getProperties()
|
|
117
|
+
.map(c => c.name);
|
|
118
|
+
}
|
|
119
|
+
exports.getTemplateCtx = getTemplateCtx;
|
|
120
|
+
function getComponentNames(ts, tsLs, sourceFile, vueCompilerOptions) {
|
|
121
|
+
return getVariableType(ts, tsLs, sourceFile, '__VLS_components')
|
|
122
|
+
?.type
|
|
116
123
|
?.getProperties()
|
|
117
124
|
.map(c => c.name)
|
|
118
125
|
.filter(entry => entry.indexOf('$') === -1 && !entry.startsWith('_'))
|
|
119
126
|
.filter(entry => !vueCompilerOptions.nativeTags.includes(entry))
|
|
120
127
|
?? [];
|
|
121
128
|
}
|
|
122
|
-
exports.
|
|
129
|
+
exports.getComponentNames = getComponentNames;
|
|
123
130
|
function getElementAttrs(ts, tsLs, tsLsHost, tagName) {
|
|
124
131
|
const sharedTypesFileName = tsLsHost.getCurrentDirectory() + '/' + language_core_1.sharedTypes.baseName;
|
|
125
132
|
let tsSourceFile;
|
|
@@ -138,7 +145,7 @@ function getElementAttrs(ts, tsLs, tsLsHost, tagName) {
|
|
|
138
145
|
return [];
|
|
139
146
|
}
|
|
140
147
|
exports.getElementAttrs = getElementAttrs;
|
|
141
|
-
function
|
|
148
|
+
function getVariableType(ts, tsLs, sourceFile, name) {
|
|
142
149
|
if (!(sourceFile instanceof vue.VueFile)) {
|
|
143
150
|
return;
|
|
144
151
|
}
|
|
@@ -150,29 +157,29 @@ function getComponentsType(ts, tsLs, sourceFile) {
|
|
|
150
157
|
}
|
|
151
158
|
});
|
|
152
159
|
if (file && (tsSourceFile = tsLs.getProgram()?.getSourceFile(file.fileName))) {
|
|
153
|
-
const
|
|
160
|
+
const node = searchVariableDeclarationNode(ts, tsSourceFile, name);
|
|
154
161
|
const checker = tsLs.getProgram()?.getTypeChecker();
|
|
155
|
-
if (checker &&
|
|
162
|
+
if (checker && node) {
|
|
156
163
|
return {
|
|
157
|
-
|
|
158
|
-
|
|
164
|
+
node: node,
|
|
165
|
+
type: checker.getTypeAtLocation(node),
|
|
159
166
|
};
|
|
160
167
|
}
|
|
161
168
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
169
|
+
}
|
|
170
|
+
function searchVariableDeclarationNode(ts, sourceFile, name) {
|
|
171
|
+
let componentsNode;
|
|
172
|
+
walk(sourceFile);
|
|
173
|
+
return componentsNode;
|
|
174
|
+
function walk(node) {
|
|
175
|
+
if (componentsNode) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
else if (ts.isVariableDeclaration(node) && node.name.getText() === name) {
|
|
179
|
+
componentsNode = node;
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
node.forEachChild(walk);
|
|
176
183
|
}
|
|
177
184
|
}
|
|
178
185
|
}
|
|
@@ -16,7 +16,7 @@ async function convertTagName(ts, context, uri, casing, vueCompilerOptions) {
|
|
|
16
16
|
const template = desc.template;
|
|
17
17
|
const document = context.documents.getDocumentByFileName(rootFile.snapshot, rootFile.fileName);
|
|
18
18
|
const edits = [];
|
|
19
|
-
const components = (0, helpers_1.
|
|
19
|
+
const components = (0, helpers_1.getComponentNames)(ts, languageService, rootFile, vueCompilerOptions);
|
|
20
20
|
const tags = (0, helpers_1.getTemplateTagsAndAttrs)(rootFile);
|
|
21
21
|
for (const [tagName, { offsets }] of tags) {
|
|
22
22
|
const componentName = components.find(component => component === tagName || (0, shared_1.hyphenate)(component) === tagName);
|
|
@@ -48,12 +48,12 @@ async function convertAttrName(ts, context, uri, casing, vueCompilerOptions) {
|
|
|
48
48
|
const template = desc.template;
|
|
49
49
|
const document = context.documents.getDocumentByFileName(rootFile.snapshot, rootFile.fileName);
|
|
50
50
|
const edits = [];
|
|
51
|
-
const components = (0, helpers_1.
|
|
51
|
+
const components = (0, helpers_1.getComponentNames)(ts, languageService, rootFile, vueCompilerOptions);
|
|
52
52
|
const tags = (0, helpers_1.getTemplateTagsAndAttrs)(rootFile);
|
|
53
53
|
for (const [tagName, { attrs }] of tags) {
|
|
54
54
|
const componentName = components.find(component => component === tagName || (0, shared_1.hyphenate)(component) === tagName);
|
|
55
55
|
if (componentName) {
|
|
56
|
-
const props = (0, helpers_1.
|
|
56
|
+
const props = (0, helpers_1.getPropsByTag)(ts, languageService, rootFile, componentName, vueCompilerOptions);
|
|
57
57
|
for (const [attrName, { offsets }] of attrs) {
|
|
58
58
|
const propName = props.find(prop => prop === attrName || (0, shared_1.hyphenate)(prop) === attrName);
|
|
59
59
|
if (propName) {
|
|
@@ -124,7 +124,7 @@ function detect(ts, context, uri, vueCompilerOptions) {
|
|
|
124
124
|
return result;
|
|
125
125
|
}
|
|
126
126
|
function getTagNameCase(file) {
|
|
127
|
-
const components = (0, helpers_1.
|
|
127
|
+
const components = (0, helpers_1.getComponentNames)(ts, languageService, file, vueCompilerOptions);
|
|
128
128
|
const tagNames = (0, helpers_1.getTemplateTagsAndAttrs)(file);
|
|
129
129
|
const result = [];
|
|
130
130
|
let anyComponentUsed = false;
|
package/out/languageService.js
CHANGED
|
@@ -32,23 +32,16 @@ function resolveConfig(config, compilerOptions = {}, vueCompilerOptions = {}, ts
|
|
|
32
32
|
return config;
|
|
33
33
|
}
|
|
34
34
|
exports.resolveConfig = resolveConfig;
|
|
35
|
-
const unicodeReg = /\\u/g;
|
|
36
35
|
function resolvePlugins(services, vueCompilerOptions) {
|
|
37
36
|
const originalTsPlugin = services?.typescript ?? (0, volar_service_typescript_1.default)();
|
|
38
37
|
services ??= {};
|
|
39
|
-
services.typescript = (
|
|
40
|
-
const base = typeof originalTsPlugin === 'function' ? originalTsPlugin(
|
|
41
|
-
if (!
|
|
38
|
+
services.typescript = (ctx, modules) => {
|
|
39
|
+
const base = typeof originalTsPlugin === 'function' ? originalTsPlugin(ctx, modules) : originalTsPlugin;
|
|
40
|
+
if (!ctx || !modules?.typescript)
|
|
42
41
|
return base;
|
|
43
42
|
const ts = modules.typescript;
|
|
44
|
-
const transformedItem = new WeakSet();
|
|
45
43
|
return {
|
|
46
44
|
...base,
|
|
47
|
-
transformCompletionItem(item) {
|
|
48
|
-
if (transformedItem.has(item)) {
|
|
49
|
-
return item;
|
|
50
|
-
}
|
|
51
|
-
},
|
|
52
45
|
async provideCompletionItems(document, position, context, item) {
|
|
53
46
|
const result = await base.provideCompletionItems?.(document, position, context, item);
|
|
54
47
|
if (result) {
|
|
@@ -57,18 +50,18 @@ function resolvePlugins(services, vueCompilerOptions) {
|
|
|
57
50
|
&& (!item.labelDetails?.description || item.labelDetails.description.indexOf('__VLS_') === -1));
|
|
58
51
|
// handle component auto-import patch
|
|
59
52
|
let casing;
|
|
60
|
-
for (const [_, map] of
|
|
61
|
-
const virtualFile =
|
|
53
|
+
for (const [_, map] of ctx.documents.getMapsByVirtualFileUri(document.uri)) {
|
|
54
|
+
const virtualFile = ctx.documents.getSourceByUri(map.sourceFileDocument.uri)?.root;
|
|
62
55
|
if (virtualFile instanceof vue.VueFile) {
|
|
63
56
|
const isAutoImport = !!map.toSourcePosition(position, data => typeof data.completion === 'object' && !!data.completion.autoImportOnly);
|
|
64
57
|
if (isAutoImport) {
|
|
65
|
-
const source =
|
|
58
|
+
const source = ctx.documents.getVirtualFileByUri(document.uri)[1];
|
|
66
59
|
for (const item of result.items) {
|
|
67
60
|
item.data.__isComponentAutoImport = true;
|
|
68
61
|
}
|
|
69
62
|
// fix #2458
|
|
70
63
|
if (source) {
|
|
71
|
-
casing ??= await (0, nameCasing_1.getNameCasing)(ts,
|
|
64
|
+
casing ??= await (0, nameCasing_1.getNameCasing)(ts, ctx, ctx.env.fileNameToUri(source.fileName), vueCompilerOptions);
|
|
72
65
|
if (casing.tag === types_1.TagNameCasing.Kebab) {
|
|
73
66
|
for (const item of result.items) {
|
|
74
67
|
item.filterText = (0, shared_1.hyphenate)(item.filterText ?? item.label);
|
|
@@ -93,7 +86,7 @@ function resolvePlugins(services, vueCompilerOptions) {
|
|
|
93
86
|
if (itemData?.uri
|
|
94
87
|
&& item.textEdit?.newText.endsWith(suffix)
|
|
95
88
|
&& item.additionalTextEdits?.length === 1 && item.additionalTextEdits[0].newText.indexOf('import ' + item.textEdit.newText + ' from ') >= 0
|
|
96
|
-
&& (await
|
|
89
|
+
&& (await ctx.env.getConfiguration?.('vue.complete.normalizeComponentImportName') ?? true)) {
|
|
97
90
|
newName = item.textEdit.newText.slice(0, -suffix.length);
|
|
98
91
|
newName = newName[0].toUpperCase() + newName.substring(1);
|
|
99
92
|
if (newName === 'Index') {
|
|
@@ -108,9 +101,9 @@ function resolvePlugins(services, vueCompilerOptions) {
|
|
|
108
101
|
}
|
|
109
102
|
item.additionalTextEdits[0].newText = item.additionalTextEdits[0].newText.replace('import ' + item.textEdit.newText + ' from ', 'import ' + newName + ' from ');
|
|
110
103
|
item.textEdit.newText = newName;
|
|
111
|
-
const source =
|
|
104
|
+
const source = ctx.documents.getVirtualFileByUri(itemData.uri)[1];
|
|
112
105
|
if (source) {
|
|
113
|
-
const casing = await (0, nameCasing_1.getNameCasing)(ts,
|
|
106
|
+
const casing = await (0, nameCasing_1.getNameCasing)(ts, ctx, ctx.env.fileNameToUri(source.fileName), vueCompilerOptions);
|
|
114
107
|
if (casing.tag === types_1.TagNameCasing.Kebab) {
|
|
115
108
|
item.textEdit.newText = (0, shared_1.hyphenate)(item.textEdit.newText);
|
|
116
109
|
}
|
|
@@ -122,65 +115,26 @@ function resolvePlugins(services, vueCompilerOptions) {
|
|
|
122
115
|
}
|
|
123
116
|
}
|
|
124
117
|
const data = item.data;
|
|
125
|
-
if (item.data?.__isComponentAutoImport && data && item.additionalTextEdits?.length && item.textEdit) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
...exportDefault.componentsOptionNode.properties,
|
|
144
|
-
ts.factory.createShorthandPropertyAssignment(componentName),
|
|
145
|
-
],
|
|
146
|
-
};
|
|
147
|
-
const printText = printer.printNode(ts.EmitHint.Expression, newNode, sfc.scriptAst);
|
|
148
|
-
const editRange = {
|
|
149
|
-
start: textDoc.positionAt(sfc.script.startTagEnd + exportDefault.componentsOption.start),
|
|
150
|
-
end: textDoc.positionAt(sfc.script.startTagEnd + exportDefault.componentsOption.end),
|
|
151
|
-
};
|
|
152
|
-
transformed = true;
|
|
153
|
-
item.additionalTextEdits.push({
|
|
154
|
-
range: editRange,
|
|
155
|
-
newText: unescape(printText.replace(unicodeReg, '%u')),
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
else if (exportDefault.args && exportDefault.argsNode) {
|
|
159
|
-
const newNode = {
|
|
160
|
-
...exportDefault.argsNode,
|
|
161
|
-
properties: [
|
|
162
|
-
...exportDefault.argsNode.properties,
|
|
163
|
-
ts.factory.createShorthandPropertyAssignment(`components: { ${componentName} }`),
|
|
164
|
-
],
|
|
165
|
-
};
|
|
166
|
-
const printText = printer.printNode(ts.EmitHint.Expression, newNode, sfc.scriptAst);
|
|
167
|
-
const editRange = {
|
|
168
|
-
start: textDoc.positionAt(sfc.script.startTagEnd + exportDefault.args.start),
|
|
169
|
-
end: textDoc.positionAt(sfc.script.startTagEnd + exportDefault.args.end),
|
|
170
|
-
};
|
|
171
|
-
transformed = true;
|
|
172
|
-
item.additionalTextEdits.push({
|
|
173
|
-
range: editRange,
|
|
174
|
-
newText: unescape(printText.replace(unicodeReg, '%u')),
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
}
|
|
118
|
+
if (item.data?.__isComponentAutoImport && data && item.additionalTextEdits?.length && item.textEdit && itemData?.uri) {
|
|
119
|
+
const fileName = ctx.env.uriToFileName(itemData.uri);
|
|
120
|
+
const langaugeService = ctx.inject('typescript/languageService');
|
|
121
|
+
const [virtualFile] = ctx.virtualFiles.getVirtualFile(fileName);
|
|
122
|
+
const ast = langaugeService.getProgram()?.getSourceFile(fileName);
|
|
123
|
+
const exportDefault = ast ? vue.scriptRanges.parseScriptRanges(ts, ast, false, true).exportDefault : undefined;
|
|
124
|
+
if (virtualFile && ast && exportDefault) {
|
|
125
|
+
const componentName = newName ?? item.textEdit.newText;
|
|
126
|
+
const optionEdit = (0, vue_extract_file_1.createAddComponentToOptionEdit)(ts, ast, componentName);
|
|
127
|
+
if (optionEdit) {
|
|
128
|
+
const textDoc = ctx.documents.getDocumentByFileName(virtualFile.snapshot, virtualFile.fileName);
|
|
129
|
+
item.additionalTextEdits.push({
|
|
130
|
+
range: {
|
|
131
|
+
start: textDoc.positionAt(optionEdit.range.start),
|
|
132
|
+
end: textDoc.positionAt(optionEdit.range.end),
|
|
133
|
+
},
|
|
134
|
+
newText: optionEdit.newText,
|
|
135
|
+
});
|
|
179
136
|
}
|
|
180
137
|
}
|
|
181
|
-
if (transformed) {
|
|
182
|
-
transformedItem.add(item);
|
|
183
|
-
}
|
|
184
138
|
}
|
|
185
139
|
return item;
|
|
186
140
|
},
|
|
@@ -1,2 +1,7 @@
|
|
|
1
1
|
import { Service } from '@volar/language-service';
|
|
2
|
+
import type * as ts from 'typescript/lib/tsserverlibrary';
|
|
2
3
|
export default function (): Service;
|
|
4
|
+
export declare function createAddComponentToOptionEdit(ts: typeof import('typescript/lib/tsserverlibrary'), ast: ts.SourceFile, componentName: string): {
|
|
5
|
+
range: import("@vue/language-core").TextRange;
|
|
6
|
+
newText: string;
|
|
7
|
+
} | undefined;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createAddComponentToOptionEdit = void 0;
|
|
3
4
|
const language_core_1 = require("@vue/language-core");
|
|
5
|
+
const unicodeReg = /\\u/g;
|
|
4
6
|
function default_1() {
|
|
5
7
|
return (ctx, modules) => {
|
|
6
8
|
if (!modules?.typescript)
|
|
@@ -65,6 +67,37 @@ function default_1() {
|
|
|
65
67
|
if (sfc.template.startTagEnd > script.startTagEnd) {
|
|
66
68
|
newFileTags = newFileTags.reverse();
|
|
67
69
|
}
|
|
70
|
+
const currentFileEdits = [
|
|
71
|
+
{
|
|
72
|
+
range: {
|
|
73
|
+
start: document.positionAt(sfc.template.startTagEnd + templateCodeRange[0]),
|
|
74
|
+
end: document.positionAt(sfc.template.startTagEnd + templateCodeRange[1]),
|
|
75
|
+
},
|
|
76
|
+
newText: generateReplaceTemplate(),
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
range: lastImportNode ? {
|
|
80
|
+
start: document.positionAt(script.startTagEnd + lastImportNode.end),
|
|
81
|
+
end: document.positionAt(script.startTagEnd + lastImportNode.end),
|
|
82
|
+
} : {
|
|
83
|
+
start: document.positionAt(script.startTagEnd),
|
|
84
|
+
end: document.positionAt(script.startTagEnd),
|
|
85
|
+
},
|
|
86
|
+
newText: `\nimport ${newName} from './${newName}.vue'`,
|
|
87
|
+
},
|
|
88
|
+
];
|
|
89
|
+
if (sfc.script && sfc.scriptAst) {
|
|
90
|
+
const edit = createAddComponentToOptionEdit(ts, sfc.scriptAst, newName);
|
|
91
|
+
if (edit) {
|
|
92
|
+
currentFileEdits.push({
|
|
93
|
+
range: {
|
|
94
|
+
start: document.positionAt(sfc.script.startTagEnd + edit.range.start),
|
|
95
|
+
end: document.positionAt(sfc.script.startTagEnd + edit.range.end),
|
|
96
|
+
},
|
|
97
|
+
newText: edit.newText,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
68
101
|
return {
|
|
69
102
|
...codeAction,
|
|
70
103
|
edit: {
|
|
@@ -75,25 +108,7 @@ function default_1() {
|
|
|
75
108
|
uri: document.uri,
|
|
76
109
|
version: null,
|
|
77
110
|
},
|
|
78
|
-
edits:
|
|
79
|
-
{
|
|
80
|
-
range: {
|
|
81
|
-
start: document.positionAt(sfc.template.startTagEnd + templateCodeRange[0]),
|
|
82
|
-
end: document.positionAt(sfc.template.startTagEnd + templateCodeRange[1]),
|
|
83
|
-
},
|
|
84
|
-
newText: generateReplaceTemplate(),
|
|
85
|
-
},
|
|
86
|
-
{
|
|
87
|
-
range: lastImportNode ? {
|
|
88
|
-
start: document.positionAt(script.startTagEnd + lastImportNode.end),
|
|
89
|
-
end: document.positionAt(script.startTagEnd + lastImportNode.end),
|
|
90
|
-
} : {
|
|
91
|
-
start: document.positionAt(script.startTagEnd),
|
|
92
|
-
end: document.positionAt(script.startTagEnd),
|
|
93
|
-
},
|
|
94
|
-
newText: `\nimport ${newName} from './${newName}.vue'`,
|
|
95
|
-
},
|
|
96
|
-
],
|
|
111
|
+
edits: currentFileEdits,
|
|
97
112
|
},
|
|
98
113
|
// creating new file with content
|
|
99
114
|
{
|
|
@@ -198,11 +213,26 @@ function selectTemplateCode(startOffset, endOffset, templateBlock, templateAst)
|
|
|
198
213
|
if (startOffset < templateBlock.startTagEnd || endOffset > templateBlock.endTagStart)
|
|
199
214
|
return;
|
|
200
215
|
const insideNodes = [];
|
|
201
|
-
|
|
216
|
+
templateAst.children.forEach(function visit(node) {
|
|
202
217
|
if (node.loc.start.offset + templateBlock.startTagEnd >= startOffset
|
|
203
218
|
&& node.loc.end.offset + templateBlock.startTagEnd <= endOffset) {
|
|
204
219
|
insideNodes.push(node);
|
|
205
220
|
}
|
|
221
|
+
if ('children' in node) {
|
|
222
|
+
node.children.forEach(node => {
|
|
223
|
+
if (typeof node === 'object') {
|
|
224
|
+
visit(node);
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
else if ('branches' in node) {
|
|
229
|
+
node.branches.forEach(visit);
|
|
230
|
+
}
|
|
231
|
+
else if ('content' in node) {
|
|
232
|
+
if (typeof node.content === 'object') {
|
|
233
|
+
visit(node.content);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
206
236
|
});
|
|
207
237
|
if (insideNodes.length) {
|
|
208
238
|
const first = insideNodes.sort((a, b) => a.loc.start.offset - b.loc.start.offset)[0];
|
|
@@ -225,4 +255,40 @@ function isInitialIndentNeeded(ts, languageKind, initialIndentSetting) {
|
|
|
225
255
|
};
|
|
226
256
|
return initialIndentSetting[languageKindIdMap[languageKind]] ?? false;
|
|
227
257
|
}
|
|
258
|
+
function createAddComponentToOptionEdit(ts, ast, componentName) {
|
|
259
|
+
const exportDefault = language_core_1.scriptRanges.parseScriptRanges(ts, ast, false, true).exportDefault;
|
|
260
|
+
if (!exportDefault)
|
|
261
|
+
return;
|
|
262
|
+
// https://github.com/microsoft/TypeScript/issues/36174
|
|
263
|
+
const printer = ts.createPrinter();
|
|
264
|
+
if (exportDefault.componentsOption && exportDefault.componentsOptionNode) {
|
|
265
|
+
const newNode = {
|
|
266
|
+
...exportDefault.componentsOptionNode,
|
|
267
|
+
properties: [
|
|
268
|
+
...exportDefault.componentsOptionNode.properties,
|
|
269
|
+
ts.factory.createShorthandPropertyAssignment(componentName),
|
|
270
|
+
],
|
|
271
|
+
};
|
|
272
|
+
const printText = printer.printNode(ts.EmitHint.Expression, newNode, ast);
|
|
273
|
+
return {
|
|
274
|
+
range: exportDefault.componentsOption,
|
|
275
|
+
newText: unescape(printText.replace(unicodeReg, '%u')),
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
else if (exportDefault.args && exportDefault.argsNode) {
|
|
279
|
+
const newNode = {
|
|
280
|
+
...exportDefault.argsNode,
|
|
281
|
+
properties: [
|
|
282
|
+
...exportDefault.argsNode.properties,
|
|
283
|
+
ts.factory.createShorthandPropertyAssignment(`components: { ${componentName} }`),
|
|
284
|
+
],
|
|
285
|
+
};
|
|
286
|
+
const printText = printer.printNode(ts.EmitHint.Expression, newNode, ast);
|
|
287
|
+
return {
|
|
288
|
+
range: exportDefault.args,
|
|
289
|
+
newText: unescape(printText.replace(unicodeReg, '%u')),
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
exports.createAddComponentToOptionEdit = createAddComponentToOptionEdit;
|
|
228
294
|
//# sourceMappingURL=vue-extract-file.js.map
|
|
@@ -85,7 +85,7 @@ exports.default = (options) => (_context, modules) => {
|
|
|
85
85
|
if (virtualFile && virtualFile instanceof vue.VueFile && scanner) {
|
|
86
86
|
// visualize missing required props
|
|
87
87
|
const casing = await (0, nameCasing_1.getNameCasing)(ts, _context, map.sourceFileDocument.uri, options.vueCompilerOptions);
|
|
88
|
-
const components = (0, helpers_1.
|
|
88
|
+
const components = (0, helpers_1.getComponentNames)(ts, languageService, virtualFile, options.vueCompilerOptions);
|
|
89
89
|
const componentProps = {};
|
|
90
90
|
let token;
|
|
91
91
|
let current;
|
|
@@ -97,7 +97,7 @@ exports.default = (options) => (_context, modules) => {
|
|
|
97
97
|
: components.find(component => component === tagName || (0, shared_1.hyphenate)(component) === tagName);
|
|
98
98
|
const checkTag = tagName.indexOf('.') >= 0 ? tagName : component;
|
|
99
99
|
if (checkTag) {
|
|
100
|
-
componentProps[checkTag] ??= (0, helpers_1.
|
|
100
|
+
componentProps[checkTag] ??= (0, helpers_1.getPropsByTag)(ts, languageService, virtualFile, checkTag, options.vueCompilerOptions, true);
|
|
101
101
|
current = {
|
|
102
102
|
unburnedRequiredProps: [...componentProps[checkTag]],
|
|
103
103
|
labelOffset: scanner.getTokenOffset() + scanner.getTokenLength(),
|
|
@@ -229,7 +229,7 @@ exports.default = (options) => (_context, modules) => {
|
|
|
229
229
|
const virtualFile = _context.documents.getSourceByUri(map.sourceFileDocument.uri)?.root;
|
|
230
230
|
if (!virtualFile || !(virtualFile instanceof vue.VueFile))
|
|
231
231
|
continue;
|
|
232
|
-
const templateScriptData = (0, helpers_1.
|
|
232
|
+
const templateScriptData = (0, helpers_1.getComponentNames)(ts, languageService, virtualFile, options.vueCompilerOptions);
|
|
233
233
|
const components = new Set([
|
|
234
234
|
...templateScriptData,
|
|
235
235
|
...templateScriptData.map(shared_1.hyphenate),
|
|
@@ -290,7 +290,7 @@ exports.default = (options) => (_context, modules) => {
|
|
|
290
290
|
getId: () => 'vue-template',
|
|
291
291
|
isApplicable: () => true,
|
|
292
292
|
provideTags: () => {
|
|
293
|
-
const components = (0, helpers_1.
|
|
293
|
+
const components = (0, helpers_1.getComponentNames)(ts, languageService, vueSourceFile, options.vueCompilerOptions)
|
|
294
294
|
.filter(name => name !== 'Transition'
|
|
295
295
|
&& name !== 'TransitionGroup'
|
|
296
296
|
&& name !== 'KeepAlive'
|
|
@@ -326,9 +326,24 @@ exports.default = (options) => (_context, modules) => {
|
|
|
326
326
|
},
|
|
327
327
|
provideAttributes: (tag) => {
|
|
328
328
|
const attrs = (0, helpers_1.getElementAttrs)(ts, languageService, languageServiceHost, tag);
|
|
329
|
-
const props = new Set((0, helpers_1.
|
|
330
|
-
const events = (0, helpers_1.
|
|
329
|
+
const props = new Set((0, helpers_1.getPropsByTag)(ts, languageService, vueSourceFile, tag, options.vueCompilerOptions));
|
|
330
|
+
const events = (0, helpers_1.getEventsOfTag)(ts, languageService, vueSourceFile, tag, options.vueCompilerOptions);
|
|
331
331
|
const attributes = [];
|
|
332
|
+
const tsCodegen = vue.tsCodegen.get(vueSourceFile.sfc);
|
|
333
|
+
if (tsCodegen) {
|
|
334
|
+
let ctxVars = [
|
|
335
|
+
...tsCodegen.scriptRanges.value?.bindings.map(binding => vueSourceFile.sfc.script.content.substring(binding.start, binding.end)) ?? [],
|
|
336
|
+
...tsCodegen.scriptSetupRanges.value?.bindings.map(binding => vueSourceFile.sfc.scriptSetup.content.substring(binding.start, binding.end)) ?? [],
|
|
337
|
+
...(0, helpers_1.getTemplateCtx)(ts, languageService, vueSourceFile) ?? [],
|
|
338
|
+
];
|
|
339
|
+
ctxVars = [...new Set(ctxVars)];
|
|
340
|
+
const dirs = ctxVars.map(shared_1.hyphenate).filter(v => v.startsWith('v-'));
|
|
341
|
+
for (const dir of dirs) {
|
|
342
|
+
attributes.push({
|
|
343
|
+
name: dir,
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
}
|
|
332
347
|
for (const prop of [...props, ...attrs]) {
|
|
333
348
|
const isGlobal = !props.has(prop);
|
|
334
349
|
const name = casing.attr === types_1.AttrNameCasing.Camel ? prop : (0, shared_1.hyphenate)(prop);
|
|
@@ -407,7 +422,7 @@ exports.default = (options) => (_context, modules) => {
|
|
|
407
422
|
function afterHtmlCompletion(completionList, map, vueSourceFile) {
|
|
408
423
|
const languageService = _context.inject('typescript/languageService');
|
|
409
424
|
const replacement = getReplacement(completionList, map.sourceFileDocument);
|
|
410
|
-
const componentNames = new Set((0, helpers_1.
|
|
425
|
+
const componentNames = new Set((0, helpers_1.getComponentNames)(ts, languageService, vueSourceFile, options.vueCompilerOptions).map(shared_1.hyphenate));
|
|
411
426
|
if (replacement) {
|
|
412
427
|
const isEvent = replacement.text.startsWith('v-on:') || replacement.text.startsWith('@');
|
|
413
428
|
const isProp = replacement.text.startsWith('v-bind:') || replacement.text.startsWith(':');
|
|
@@ -469,39 +484,37 @@ exports.default = (options) => (_context, modules) => {
|
|
|
469
484
|
if (itemId) {
|
|
470
485
|
item.documentation = undefined;
|
|
471
486
|
}
|
|
472
|
-
if (
|
|
473
|
-
|
|
474
|
-
|
|
487
|
+
if (item.kind === 10 && componentNames.has((0, shared_1.hyphenate)(item.label))) {
|
|
488
|
+
item.kind = 6;
|
|
489
|
+
item.sortText = '\u0000' + (item.sortText ?? item.label);
|
|
490
|
+
}
|
|
491
|
+
else if (itemId && (itemId.type === 'componentProp' || itemId.type === 'componentEvent')) {
|
|
492
|
+
const [componentName] = itemId.args;
|
|
493
|
+
if (componentName !== '*') {
|
|
494
|
+
item.sortText = '\u0000' + (item.sortText ?? item.label);
|
|
495
|
+
}
|
|
496
|
+
if (itemId.type === 'componentProp') {
|
|
475
497
|
if (componentName !== '*') {
|
|
476
|
-
item.
|
|
498
|
+
item.kind = 5;
|
|
477
499
|
}
|
|
478
|
-
if (itemId.type === 'componentProp') {
|
|
479
|
-
if (componentName !== '*') {
|
|
480
|
-
item.kind = 5;
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
else {
|
|
484
|
-
item.kind = componentName !== '*' ? 3 : 23;
|
|
485
|
-
}
|
|
486
|
-
}
|
|
487
|
-
else if (item.label === 'v-if'
|
|
488
|
-
|| item.label === 'v-else-if'
|
|
489
|
-
|| item.label === 'v-else'
|
|
490
|
-
|| item.label === 'v-for') {
|
|
491
|
-
item.kind = 2;
|
|
492
|
-
item.sortText = '\u0003' + (item.sortText ?? item.label);
|
|
493
|
-
}
|
|
494
|
-
else if (item.label.startsWith('v-')) {
|
|
495
|
-
item.kind = 3;
|
|
496
|
-
item.sortText = '\u0002' + (item.sortText ?? item.label);
|
|
497
500
|
}
|
|
498
501
|
else {
|
|
499
|
-
item.
|
|
502
|
+
item.kind = componentName !== '*' ? 3 : 23;
|
|
500
503
|
}
|
|
501
504
|
}
|
|
502
|
-
else if (item.
|
|
503
|
-
item.
|
|
504
|
-
item.
|
|
505
|
+
else if (item.label === 'v-if'
|
|
506
|
+
|| item.label === 'v-else-if'
|
|
507
|
+
|| item.label === 'v-else'
|
|
508
|
+
|| item.label === 'v-for') {
|
|
509
|
+
item.kind = 14;
|
|
510
|
+
item.sortText = '\u0003' + (item.sortText ?? item.label);
|
|
511
|
+
}
|
|
512
|
+
else if (item.label.startsWith('v-')) {
|
|
513
|
+
item.kind = 3;
|
|
514
|
+
item.sortText = '\u0002' + (item.sortText ?? item.label);
|
|
515
|
+
}
|
|
516
|
+
else {
|
|
517
|
+
item.sortText = '\u0001' + (item.sortText ?? item.label);
|
|
505
518
|
}
|
|
506
519
|
}
|
|
507
520
|
options.updateCustomData(htmlOrPugService, []);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vue/language-service",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.7",
|
|
4
4
|
"main": "out/index.js",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"files": [
|
|
@@ -17,28 +17,28 @@
|
|
|
17
17
|
"update-html-data": "node ./scripts/update-html-data.js"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@volar/language-core": "~1.
|
|
21
|
-
"@volar/language-service": "~1.
|
|
22
|
-
"@volar/typescript": "~1.
|
|
20
|
+
"@volar/language-core": "~1.10.0",
|
|
21
|
+
"@volar/language-service": "~1.10.0",
|
|
22
|
+
"@volar/typescript": "~1.10.0",
|
|
23
23
|
"@vue/compiler-dom": "^3.3.0",
|
|
24
|
-
"@vue/language-core": "1.8.
|
|
24
|
+
"@vue/language-core": "1.8.7",
|
|
25
25
|
"@vue/reactivity": "^3.3.0",
|
|
26
26
|
"@vue/shared": "^3.3.0",
|
|
27
|
-
"volar-service-css": "0.0.
|
|
28
|
-
"volar-service-emmet": "0.0.
|
|
29
|
-
"volar-service-html": "0.0.
|
|
30
|
-
"volar-service-json": "0.0.
|
|
31
|
-
"volar-service-pug": "0.0.
|
|
32
|
-
"volar-service-pug-beautify": "0.0.
|
|
33
|
-
"volar-service-typescript": "0.0.
|
|
34
|
-
"volar-service-typescript-twoslash-queries": "0.0.
|
|
27
|
+
"volar-service-css": "0.0.11",
|
|
28
|
+
"volar-service-emmet": "0.0.11",
|
|
29
|
+
"volar-service-html": "0.0.11",
|
|
30
|
+
"volar-service-json": "0.0.11",
|
|
31
|
+
"volar-service-pug": "0.0.11",
|
|
32
|
+
"volar-service-pug-beautify": "0.0.11",
|
|
33
|
+
"volar-service-typescript": "0.0.11",
|
|
34
|
+
"volar-service-typescript-twoslash-queries": "0.0.11",
|
|
35
35
|
"vscode-html-languageservice": "^5.0.4",
|
|
36
36
|
"vscode-languageserver-textdocument": "^1.0.8"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
|
-
"@volar/kit": "~1.
|
|
39
|
+
"@volar/kit": "~1.10.0",
|
|
40
40
|
"vscode-languageserver-protocol": "^3.17.3",
|
|
41
41
|
"vscode-uri": "^3.0.7"
|
|
42
42
|
},
|
|
43
|
-
"gitHead": "
|
|
43
|
+
"gitHead": "2b888901897fc9155bc12eb30ddae41afed5d942"
|
|
44
44
|
}
|