@vue/typescript-plugin 2.0.0 → 2.0.2

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/index.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import type * as ts from 'typescript';
2
+ declare const _default: ts.server.PluginModuleFactory;
3
+ export = _default;
package/index.js ADDED
@@ -0,0 +1,196 @@
1
+ "use strict";
2
+ const decorateLanguageService_1 = require("@volar/typescript/lib/node/decorateLanguageService");
3
+ const decorateLanguageServiceHost_1 = require("@volar/typescript/lib/node/decorateLanguageServiceHost");
4
+ const language_core_1 = require("@vue/language-core");
5
+ const utils_1 = require("./lib/utils");
6
+ const vue = require("@vue/language-core");
7
+ const server_1 = require("./lib/server");
8
+ const componentInfos_1 = require("./lib/requests/componentInfos");
9
+ const shared_1 = require("@vue/shared");
10
+ const windowsPathReg = /\\/g;
11
+ const externalFiles = new WeakMap();
12
+ const projectExternalFileExtensions = new WeakMap();
13
+ const decoratedLanguageServices = new WeakSet();
14
+ const decoratedLanguageServiceHosts = new WeakSet();
15
+ function createLanguageServicePlugin() {
16
+ return modules => {
17
+ const { typescript: ts } = modules;
18
+ const pluginModule = {
19
+ create(info) {
20
+ if (!decoratedLanguageServices.has(info.languageService)
21
+ && !decoratedLanguageServiceHosts.has(info.languageServiceHost)) {
22
+ decoratedLanguageServices.add(info.languageService);
23
+ decoratedLanguageServiceHosts.add(info.languageServiceHost);
24
+ const vueOptions = vue.resolveVueCompilerOptions(getVueCompilerOptions());
25
+ const languagePlugin = vue.createVueLanguagePlugin(ts, id => id, info.languageServiceHost.getCompilationSettings(), vueOptions);
26
+ const extensions = languagePlugin.typescript?.extraFileExtensions.map(ext => '.' + ext.extension) ?? [];
27
+ const getScriptSnapshot = info.languageServiceHost.getScriptSnapshot.bind(info.languageServiceHost);
28
+ const files = (0, language_core_1.createFileRegistry)([languagePlugin], ts.sys.useCaseSensitiveFileNames, fileName => {
29
+ const snapshot = getScriptSnapshot(fileName);
30
+ if (snapshot) {
31
+ files.set(fileName, (0, language_core_1.resolveCommonLanguageId)(fileName), snapshot);
32
+ }
33
+ else {
34
+ files.delete(fileName);
35
+ }
36
+ });
37
+ projectExternalFileExtensions.set(info.project, extensions);
38
+ utils_1.projects.set(info.project, {
39
+ info,
40
+ files,
41
+ ts,
42
+ vueOptions,
43
+ });
44
+ (0, decorateLanguageService_1.decorateLanguageService)(files, info.languageService);
45
+ (0, decorateLanguageServiceHost_1.decorateLanguageServiceHost)(files, info.languageServiceHost, ts);
46
+ (0, server_1.startNamedPipeServer)(info.project.projectKind);
47
+ const getCompletionsAtPosition = info.languageService.getCompletionsAtPosition;
48
+ const getCompletionEntryDetails = info.languageService.getCompletionEntryDetails;
49
+ const getCodeFixesAtPosition = info.languageService.getCodeFixesAtPosition;
50
+ const getEncodedSemanticClassifications = info.languageService.getEncodedSemanticClassifications;
51
+ info.languageService.getCompletionsAtPosition = (fileName, position, options) => {
52
+ const result = getCompletionsAtPosition(fileName, position, options);
53
+ if (result) {
54
+ // filter __VLS_
55
+ result.entries = result.entries.filter(entry => entry.name.indexOf('__VLS_') === -1
56
+ && (!entry.labelDetails?.description || entry.labelDetails.description.indexOf('__VLS_') === -1));
57
+ // modify label
58
+ for (const item of result.entries) {
59
+ if (item.source) {
60
+ const originalName = item.name;
61
+ for (const ext of vueOptions.extensions) {
62
+ const suffix = (0, shared_1.capitalize)(ext.substring('.'.length)); // .vue -> Vue
63
+ if (item.source.endsWith(ext) && item.name.endsWith(suffix)) {
64
+ item.name = item.name.slice(0, -suffix.length);
65
+ if (item.insertText) {
66
+ // #2286
67
+ item.insertText = item.insertText.replace(`${suffix}$1`, '$1');
68
+ }
69
+ if (item.data) {
70
+ // @ts-expect-error
71
+ item.data.__isComponentAutoImport = {
72
+ ext,
73
+ suffix,
74
+ originalName,
75
+ newName: item.insertText,
76
+ };
77
+ }
78
+ break;
79
+ }
80
+ }
81
+ }
82
+ }
83
+ }
84
+ return result;
85
+ };
86
+ info.languageService.getCompletionEntryDetails = (...args) => {
87
+ const details = getCompletionEntryDetails(...args);
88
+ // modify import statement
89
+ // @ts-expect-error
90
+ if (args[6]?.__isComponentAutoImport) {
91
+ // @ts-expect-error
92
+ const { ext, suffix, originalName, newName } = args[6]?.__isComponentAutoImport;
93
+ for (const codeAction of details?.codeActions ?? []) {
94
+ for (const change of codeAction.changes) {
95
+ for (const textChange of change.textChanges) {
96
+ textChange.newText = textChange.newText.replace('import ' + originalName + ' from ', 'import ' + newName + ' from ');
97
+ }
98
+ }
99
+ }
100
+ }
101
+ return details;
102
+ };
103
+ info.languageService.getCodeFixesAtPosition = (...args) => {
104
+ let result = getCodeFixesAtPosition(...args);
105
+ // filter __VLS_
106
+ result = result.filter(entry => entry.description.indexOf('__VLS_') === -1);
107
+ return result;
108
+ };
109
+ info.languageService.getEncodedSemanticClassifications = (fileName, span, format) => {
110
+ const result = getEncodedSemanticClassifications(fileName, span, format);
111
+ const file = files.get(fileName);
112
+ if (file?.generated?.code instanceof vue.VueGeneratedCode
113
+ && file.generated.code.sfc.template) {
114
+ const validComponentNames = (0, componentInfos_1._getComponentNames)(ts, info.languageService, file.generated.code, vueOptions);
115
+ const components = new Set([
116
+ ...validComponentNames,
117
+ ...validComponentNames.map(vue.hyphenateTag),
118
+ ]);
119
+ const { template } = file.generated.code.sfc;
120
+ const spanTemplateRange = [
121
+ span.start - template.startTagEnd,
122
+ span.start + span.length - template.startTagEnd,
123
+ ];
124
+ template.ast?.children.forEach(function visit(node) {
125
+ if (node.loc.end.offset <= spanTemplateRange[0] || node.loc.start.offset >= spanTemplateRange[1]) {
126
+ return;
127
+ }
128
+ if (node.type === 1) {
129
+ if (components.has(node.tag)) {
130
+ result.spans.push(node.loc.start.offset + node.loc.source.indexOf(node.tag) + template.startTagEnd, node.tag.length, 256);
131
+ if (template.lang === 'html' && !node.isSelfClosing) {
132
+ result.spans.push(node.loc.start.offset + node.loc.source.lastIndexOf(node.tag) + template.startTagEnd, node.tag.length, 256);
133
+ }
134
+ }
135
+ for (const child of node.children) {
136
+ visit(child);
137
+ }
138
+ }
139
+ else if (node.type === 9) {
140
+ for (const branch of node.branches) {
141
+ for (const child of branch.children) {
142
+ visit(child);
143
+ }
144
+ }
145
+ }
146
+ else if (node.type === 11) {
147
+ for (const child of node.children) {
148
+ visit(child);
149
+ }
150
+ }
151
+ });
152
+ }
153
+ return result;
154
+ };
155
+ }
156
+ return info.languageService;
157
+ function getVueCompilerOptions() {
158
+ if (info.project.projectKind === ts.server.ProjectKind.Configured) {
159
+ const tsconfig = info.project.getProjectName();
160
+ return vue.createParsedCommandLine(ts, ts.sys, tsconfig.replace(windowsPathReg, '/')).vueOptions;
161
+ }
162
+ else {
163
+ return vue.createParsedCommandLineByJson(ts, ts.sys, info.languageServiceHost.getCurrentDirectory(), {}).vueOptions;
164
+ }
165
+ }
166
+ },
167
+ getExternalFiles(project, updateLevel = 0) {
168
+ if (updateLevel >= (1)
169
+ || !externalFiles.has(project)) {
170
+ const oldFiles = externalFiles.get(project);
171
+ const newFiles = (0, decorateLanguageServiceHost_1.searchExternalFiles)(ts, project, projectExternalFileExtensions.get(project));
172
+ externalFiles.set(project, newFiles);
173
+ if (oldFiles && !arrayItemsEqual(oldFiles, newFiles)) {
174
+ project.refreshDiagnostics();
175
+ }
176
+ }
177
+ return externalFiles.get(project);
178
+ },
179
+ };
180
+ return pluginModule;
181
+ };
182
+ }
183
+ function arrayItemsEqual(a, b) {
184
+ if (a.length !== b.length) {
185
+ return false;
186
+ }
187
+ const set = new Set(a);
188
+ for (const file of b) {
189
+ if (!set.has(file)) {
190
+ return false;
191
+ }
192
+ }
193
+ return true;
194
+ }
195
+ module.exports = createLanguageServicePlugin();
196
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,13 @@
1
+ import type * as ts from 'typescript';
2
+ export declare function collectExtractProps(...args: Parameters<typeof import('./requests/collectExtractProps.js')['collectExtractProps']>): Promise<{
3
+ name: string;
4
+ type: string;
5
+ model: boolean;
6
+ }[] | null | undefined>;
7
+ export declare function getPropertiesAtLocation(...args: Parameters<typeof import('./requests/getPropertiesAtLocation.js')['getPropertiesAtLocation']>): Promise<string[] | null | undefined>;
8
+ export declare function getQuickInfoAtPosition(...args: Parameters<typeof import('./requests/getQuickInfoAtPosition.js')['getQuickInfoAtPosition']>): Promise<ts.QuickInfo | null | undefined>;
9
+ export declare function getComponentProps(...args: Parameters<typeof import('./requests/componentInfos.js')['getComponentProps']>): Promise<string[] | null | undefined>;
10
+ export declare function getComponentEvents(...args: Parameters<typeof import('./requests/componentInfos.js')['getComponentEvents']>): Promise<string[] | null | undefined>;
11
+ export declare function getTemplateContextProps(...args: Parameters<typeof import('./requests/componentInfos.js')['getTemplateContextProps']>): Promise<string[] | null | undefined>;
12
+ export declare function getComponentNames(...args: Parameters<typeof import('./requests/componentInfos.js')['getComponentNames']>): Promise<string[] | null | undefined>;
13
+ export declare function getElementAttrs(...args: Parameters<typeof import('./requests/componentInfos.js')['getElementAttrs']>): Promise<string[] | null | undefined>;
package/lib/client.js ADDED
@@ -0,0 +1,119 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getElementAttrs = exports.getComponentNames = exports.getTemplateContextProps = exports.getComponentEvents = exports.getComponentProps = exports.getQuickInfoAtPosition = exports.getPropertiesAtLocation = exports.collectExtractProps = void 0;
4
+ const net = require("net");
5
+ const fs = require("fs");
6
+ const utils_1 = require("./utils");
7
+ function collectExtractProps(...args) {
8
+ return sendRequest({
9
+ type: 'collectExtractProps',
10
+ args,
11
+ });
12
+ }
13
+ exports.collectExtractProps = collectExtractProps;
14
+ async function getPropertiesAtLocation(...args) {
15
+ return await sendRequest({
16
+ type: 'getPropertiesAtLocation',
17
+ args,
18
+ });
19
+ }
20
+ exports.getPropertiesAtLocation = getPropertiesAtLocation;
21
+ function getQuickInfoAtPosition(...args) {
22
+ return sendRequest({
23
+ type: 'getQuickInfoAtPosition',
24
+ args,
25
+ });
26
+ }
27
+ exports.getQuickInfoAtPosition = getQuickInfoAtPosition;
28
+ // Component Infos
29
+ function getComponentProps(...args) {
30
+ return sendRequest({
31
+ type: 'getComponentProps',
32
+ args,
33
+ });
34
+ }
35
+ exports.getComponentProps = getComponentProps;
36
+ function getComponentEvents(...args) {
37
+ return sendRequest({
38
+ type: 'getComponentEvents',
39
+ args,
40
+ });
41
+ }
42
+ exports.getComponentEvents = getComponentEvents;
43
+ function getTemplateContextProps(...args) {
44
+ return sendRequest({
45
+ type: 'getTemplateContextProps',
46
+ args,
47
+ });
48
+ }
49
+ exports.getTemplateContextProps = getTemplateContextProps;
50
+ function getComponentNames(...args) {
51
+ return sendRequest({
52
+ type: 'getComponentNames',
53
+ args,
54
+ });
55
+ }
56
+ exports.getComponentNames = getComponentNames;
57
+ function getElementAttrs(...args) {
58
+ return sendRequest({
59
+ type: 'getElementAttrs',
60
+ args,
61
+ });
62
+ }
63
+ exports.getElementAttrs = getElementAttrs;
64
+ async function sendRequest(request) {
65
+ const pipeFile = await getPipeFile(request.args[0]);
66
+ if (!pipeFile) {
67
+ console.error('[Vue Named Pipe Client] pipeFile not found');
68
+ return;
69
+ }
70
+ return await _sendRequest(request, pipeFile);
71
+ }
72
+ async function getPipeFile(fileName) {
73
+ if (fs.existsSync(utils_1.pipeTable)) {
74
+ const table = JSON.parse(fs.readFileSync(utils_1.pipeTable, 'utf8'));
75
+ const all = Object.values(table);
76
+ const configuredServers = all
77
+ .filter(item => item.serverKind === 1)
78
+ .sort((a, b) => Math.abs(process.pid - a.pid) - Math.abs(process.pid - b.pid));
79
+ const inferredServers = all
80
+ .filter(item => item.serverKind === 0)
81
+ .sort((a, b) => Math.abs(process.pid - a.pid) - Math.abs(process.pid - b.pid));
82
+ for (const server of configuredServers) {
83
+ const response = await _sendRequest({ type: 'containsFile', args: [fileName] }, server.pipeFile);
84
+ if (response) {
85
+ return server.pipeFile;
86
+ }
87
+ }
88
+ for (const server of inferredServers) {
89
+ const response = await _sendRequest({ type: 'containsFile', args: [fileName] }, server.pipeFile);
90
+ if (typeof response === 'boolean') {
91
+ return server.pipeFile;
92
+ }
93
+ }
94
+ }
95
+ }
96
+ function _sendRequest(request, pipeFile) {
97
+ return new Promise(resolve => {
98
+ try {
99
+ const client = net.connect(pipeFile);
100
+ client.on('connect', () => {
101
+ client.write(JSON.stringify(request));
102
+ });
103
+ client.on('data', data => {
104
+ const text = data.toString();
105
+ resolve(JSON.parse(text));
106
+ client.end();
107
+ });
108
+ client.on('error', err => {
109
+ console.error('[Vue Named Pipe Client]', err);
110
+ return resolve(undefined);
111
+ });
112
+ }
113
+ catch (e) {
114
+ console.error('[Vue Named Pipe Client]', e);
115
+ return resolve(undefined);
116
+ }
117
+ });
118
+ }
119
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1,5 @@
1
+ export declare function collectExtractProps(fileName: string, templateCodeRange: [number, number], isTsPlugin?: boolean): {
2
+ name: string;
3
+ type: string;
4
+ model: boolean;
5
+ }[] | undefined;
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.collectExtractProps = void 0;
4
+ const language_core_1 = require("@vue/language-core");
5
+ const utils_1 = require("../utils");
6
+ function collectExtractProps(fileName, templateCodeRange, isTsPlugin = true) {
7
+ const match = (0, utils_1.getProject)(fileName);
8
+ if (!match) {
9
+ return;
10
+ }
11
+ const { info, files, ts } = match;
12
+ const volarFile = files.get(fileName);
13
+ if (!(volarFile?.generated?.code instanceof language_core_1.VueGeneratedCode)) {
14
+ return;
15
+ }
16
+ const result = new Map();
17
+ const languageService = info.languageService;
18
+ const program = languageService.getCurrentProgram();
19
+ if (!program) {
20
+ return;
21
+ }
22
+ const sourceFile = program.getSourceFile(fileName);
23
+ const checker = program.getTypeChecker();
24
+ const script = volarFile.generated?.languagePlugin.typescript?.getScript(volarFile.generated.code);
25
+ const maps = script ? [...files.getMaps(script.code).values()] : [];
26
+ const sfc = volarFile.generated.code.sfc;
27
+ sourceFile.forEachChild(function visit(node) {
28
+ if (ts.isPropertyAccessExpression(node)
29
+ && ts.isIdentifier(node.expression)
30
+ && node.expression.text === '__VLS_ctx'
31
+ && ts.isIdentifier(node.name)) {
32
+ const { name } = node;
33
+ for (const [_, map] of maps) {
34
+ const source = map.getSourceOffset(name.getEnd() - (isTsPlugin ? volarFile.snapshot.getLength() : 0));
35
+ if (source
36
+ && source[0] >= sfc.template.startTagEnd + templateCodeRange[0]
37
+ && source[0] <= sfc.template.startTagEnd + templateCodeRange[1]
38
+ && (0, language_core_1.isSemanticTokensEnabled)(source[1].data)) {
39
+ if (!result.has(name.text)) {
40
+ const type = checker.getTypeAtLocation(node);
41
+ const typeString = checker.typeToString(type, node, ts.TypeFormatFlags.NoTruncation);
42
+ result.set(name.text, {
43
+ name: name.text,
44
+ type: typeString.includes('__VLS_') ? 'any' : typeString,
45
+ model: false,
46
+ });
47
+ }
48
+ const isModel = ts.isPostfixUnaryExpression(node.parent) || ts.isBinaryExpression(node.parent);
49
+ if (isModel) {
50
+ result.get(name.text).model = true;
51
+ }
52
+ break;
53
+ }
54
+ }
55
+ }
56
+ node.forEachChild(visit);
57
+ });
58
+ return [...result.values()];
59
+ }
60
+ exports.collectExtractProps = collectExtractProps;
61
+ //# sourceMappingURL=collectExtractProps.js.map
@@ -0,0 +1,8 @@
1
+ import * as vue from '@vue/language-core';
2
+ import type * as ts from 'typescript';
3
+ export declare function getComponentProps(fileName: string, tag: string, requiredOnly?: boolean): string[] | undefined;
4
+ export declare function getComponentEvents(fileName: string, tag: string): string[] | undefined;
5
+ export declare function getTemplateContextProps(fileName: string): string[] | undefined;
6
+ export declare function getComponentNames(fileName: string): string[] | undefined;
7
+ export declare function _getComponentNames(ts: typeof import('typescript'), tsLs: ts.LanguageService, vueCode: vue.VueGeneratedCode, vueOptions: vue.VueCompilerOptions): string[];
8
+ export declare function getElementAttrs(fileName: string, tagName: string): string[] | undefined;
@@ -0,0 +1,254 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getElementAttrs = exports._getComponentNames = exports.getComponentNames = exports.getTemplateContextProps = exports.getComponentEvents = exports.getComponentProps = void 0;
4
+ const vue = require("@vue/language-core");
5
+ const shared_1 = require("@vue/shared");
6
+ const utils_1 = require("../utils");
7
+ function getComponentProps(fileName, tag, requiredOnly = false) {
8
+ const match = (0, utils_1.getProject)(fileName);
9
+ if (!match) {
10
+ return;
11
+ }
12
+ const { ts, files, vueOptions } = match;
13
+ const volarFile = files.get(fileName);
14
+ if (!(volarFile?.generated?.code instanceof vue.VueGeneratedCode)) {
15
+ return;
16
+ }
17
+ const vueCode = volarFile.generated.code;
18
+ const tsLs = match.info.languageService;
19
+ const program = tsLs.getCurrentProgram();
20
+ if (!program) {
21
+ return;
22
+ }
23
+ const checker = program.getTypeChecker();
24
+ const components = getVariableType(ts, tsLs, vueCode, '__VLS_components');
25
+ if (!components)
26
+ return [];
27
+ const name = tag.split('.');
28
+ let componentSymbol = components.type.getProperty(name[0]);
29
+ if (!componentSymbol && !vueOptions.nativeTags.includes(name[0])) {
30
+ componentSymbol = components.type.getProperty((0, shared_1.camelize)(name[0]))
31
+ ?? components.type.getProperty((0, shared_1.capitalize)((0, shared_1.camelize)(name[0])));
32
+ }
33
+ if (!componentSymbol)
34
+ return [];
35
+ let componentType = checker.getTypeOfSymbolAtLocation(componentSymbol, components.node);
36
+ for (let i = 1; i < name.length; i++) {
37
+ componentSymbol = componentType.getProperty(name[i]);
38
+ if (componentSymbol) {
39
+ componentType = checker.getTypeOfSymbolAtLocation(componentSymbol, components.node);
40
+ }
41
+ else {
42
+ return [];
43
+ }
44
+ }
45
+ const result = new Set();
46
+ for (const sig of componentType.getCallSignatures()) {
47
+ const propParam = sig.parameters[0];
48
+ if (propParam) {
49
+ const propsType = checker.getTypeOfSymbolAtLocation(propParam, components.node);
50
+ const props = propsType.getProperties();
51
+ for (const prop of props) {
52
+ if (!requiredOnly || !(prop.flags & ts.SymbolFlags.Optional)) {
53
+ result.add(prop.name);
54
+ }
55
+ }
56
+ }
57
+ }
58
+ for (const sig of componentType.getConstructSignatures()) {
59
+ const instanceType = sig.getReturnType();
60
+ const propsSymbol = instanceType.getProperty('$props');
61
+ if (propsSymbol) {
62
+ const propsType = checker.getTypeOfSymbolAtLocation(propsSymbol, components.node);
63
+ const props = propsType.getProperties();
64
+ for (const prop of props) {
65
+ if (prop.flags & ts.SymbolFlags.Method) { // #2443
66
+ continue;
67
+ }
68
+ if (!requiredOnly || !(prop.flags & ts.SymbolFlags.Optional)) {
69
+ result.add(prop.name);
70
+ }
71
+ }
72
+ }
73
+ }
74
+ return [...result];
75
+ }
76
+ exports.getComponentProps = getComponentProps;
77
+ function getComponentEvents(fileName, tag) {
78
+ const match = (0, utils_1.getProject)(fileName);
79
+ if (!match) {
80
+ return;
81
+ }
82
+ const { ts, files, vueOptions } = match;
83
+ const volarFile = files.get(fileName);
84
+ if (!(volarFile?.generated?.code instanceof vue.VueGeneratedCode)) {
85
+ return;
86
+ }
87
+ const tsLs = match.info.languageService;
88
+ const vueCode = volarFile.generated.code;
89
+ const program = tsLs.getCurrentProgram();
90
+ if (!program) {
91
+ return;
92
+ }
93
+ const checker = program.getTypeChecker();
94
+ const components = getVariableType(ts, tsLs, vueCode, '__VLS_components');
95
+ if (!components)
96
+ return [];
97
+ const name = tag.split('.');
98
+ let componentSymbol = components.type.getProperty(name[0]);
99
+ if (!componentSymbol && !vueOptions.nativeTags.includes(name[0])) {
100
+ componentSymbol = components.type.getProperty((0, shared_1.camelize)(name[0]))
101
+ ?? components.type.getProperty((0, shared_1.capitalize)((0, shared_1.camelize)(name[0])));
102
+ }
103
+ if (!componentSymbol)
104
+ return [];
105
+ let componentType = checker.getTypeOfSymbolAtLocation(componentSymbol, components.node);
106
+ for (let i = 1; i < name.length; i++) {
107
+ componentSymbol = componentType.getProperty(name[i]);
108
+ if (componentSymbol) {
109
+ componentType = checker.getTypeOfSymbolAtLocation(componentSymbol, components.node);
110
+ }
111
+ else {
112
+ return [];
113
+ }
114
+ }
115
+ const result = new Set();
116
+ // for (const sig of componentType.getCallSignatures()) {
117
+ // const emitParam = sig.parameters[1];
118
+ // if (emitParam) {
119
+ // // TODO
120
+ // }
121
+ // }
122
+ for (const sig of componentType.getConstructSignatures()) {
123
+ const instanceType = sig.getReturnType();
124
+ const emitSymbol = instanceType.getProperty('$emit');
125
+ if (emitSymbol) {
126
+ const emitType = checker.getTypeOfSymbolAtLocation(emitSymbol, components.node);
127
+ for (const call of emitType.getCallSignatures()) {
128
+ const eventNameParamSymbol = call.parameters[0];
129
+ if (eventNameParamSymbol) {
130
+ const eventNameParamType = checker.getTypeOfSymbolAtLocation(eventNameParamSymbol, components.node);
131
+ if (eventNameParamType.isStringLiteral()) {
132
+ result.add(eventNameParamType.value);
133
+ }
134
+ }
135
+ }
136
+ }
137
+ }
138
+ return [...result];
139
+ }
140
+ exports.getComponentEvents = getComponentEvents;
141
+ function getTemplateContextProps(fileName) {
142
+ const match = (0, utils_1.getProject)(fileName);
143
+ if (!match) {
144
+ return;
145
+ }
146
+ const { ts, files } = match;
147
+ const volarFile = files.get(fileName);
148
+ if (!(volarFile?.generated?.code instanceof vue.VueGeneratedCode)) {
149
+ return;
150
+ }
151
+ const tsLs = match.info.languageService;
152
+ const vueCode = volarFile.generated.code;
153
+ return getVariableType(ts, tsLs, vueCode, '__VLS_ctx')
154
+ ?.type
155
+ ?.getProperties()
156
+ .map(c => c.name);
157
+ }
158
+ exports.getTemplateContextProps = getTemplateContextProps;
159
+ function getComponentNames(fileName) {
160
+ const match = (0, utils_1.getProject)(fileName);
161
+ if (!match) {
162
+ return;
163
+ }
164
+ const { ts, files, vueOptions } = match;
165
+ const volarFile = files.get(fileName);
166
+ if (!(volarFile?.generated?.code instanceof vue.VueGeneratedCode)) {
167
+ return;
168
+ }
169
+ const tsLs = match.info.languageService;
170
+ const vueCode = volarFile.generated.code;
171
+ return getVariableType(ts, tsLs, vueCode, '__VLS_components')
172
+ ?.type
173
+ ?.getProperties()
174
+ .map(c => c.name)
175
+ .filter(entry => entry.indexOf('$') === -1 && !entry.startsWith('_'))
176
+ .filter(entry => !vueOptions.nativeTags.includes(entry))
177
+ ?? [];
178
+ }
179
+ exports.getComponentNames = getComponentNames;
180
+ function _getComponentNames(ts, tsLs, vueCode, vueOptions) {
181
+ return getVariableType(ts, tsLs, vueCode, '__VLS_components')
182
+ ?.type
183
+ ?.getProperties()
184
+ .map(c => c.name)
185
+ .filter(entry => entry.indexOf('$') === -1 && !entry.startsWith('_'))
186
+ .filter(entry => !vueOptions.nativeTags.includes(entry))
187
+ ?? [];
188
+ }
189
+ exports._getComponentNames = _getComponentNames;
190
+ function getElementAttrs(fileName, tagName) {
191
+ const match = (0, utils_1.getProject)(fileName);
192
+ if (!match) {
193
+ return;
194
+ }
195
+ const { ts, files } = match;
196
+ const volarFile = files.get(fileName);
197
+ if (!(volarFile?.generated?.code instanceof vue.VueGeneratedCode)) {
198
+ return;
199
+ }
200
+ const tsLs = match.info.languageService;
201
+ const program = tsLs.getCurrentProgram();
202
+ if (!program) {
203
+ return;
204
+ }
205
+ let tsSourceFile;
206
+ if (tsSourceFile = program.getSourceFile(fileName)) {
207
+ const typeNode = tsSourceFile.statements.find((node) => ts.isTypeAliasDeclaration(node) && node.name.getText() === '__VLS_IntrinsicElementsCompletion');
208
+ const checker = program.getTypeChecker();
209
+ if (checker && typeNode) {
210
+ const type = checker.getTypeFromTypeNode(typeNode.type);
211
+ const el = type.getProperty(tagName);
212
+ if (el) {
213
+ const attrs = checker.getTypeOfSymbolAtLocation(el, typeNode).getProperties();
214
+ return attrs.map(c => c.name);
215
+ }
216
+ }
217
+ }
218
+ return [];
219
+ }
220
+ exports.getElementAttrs = getElementAttrs;
221
+ function getVariableType(ts, tsLs, vueCode, name) {
222
+ const program = tsLs.getCurrentProgram();
223
+ if (!program) {
224
+ return;
225
+ }
226
+ let tsSourceFile;
227
+ if (tsSourceFile = program.getSourceFile(vueCode.fileName)) {
228
+ const node = searchVariableDeclarationNode(ts, tsSourceFile, name);
229
+ const checker = program.getTypeChecker();
230
+ if (checker && node) {
231
+ return {
232
+ node: node,
233
+ type: checker.getTypeAtLocation(node),
234
+ };
235
+ }
236
+ }
237
+ }
238
+ function searchVariableDeclarationNode(ts, sourceFile, name) {
239
+ let componentsNode;
240
+ walk(sourceFile);
241
+ return componentsNode;
242
+ function walk(node) {
243
+ if (componentsNode) {
244
+ return;
245
+ }
246
+ else if (ts.isVariableDeclaration(node) && node.name.getText() === name) {
247
+ componentsNode = node;
248
+ }
249
+ else {
250
+ node.forEachChild(walk);
251
+ }
252
+ }
253
+ }
254
+ //# sourceMappingURL=componentInfos.js.map
@@ -0,0 +1 @@
1
+ export declare function containsFile(fileName: string): boolean;
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.containsFile = void 0;
4
+ const utils_1 = require("../utils");
5
+ function containsFile(fileName) {
6
+ return !!(0, utils_1.getProject)(fileName);
7
+ }
8
+ exports.containsFile = containsFile;
9
+ //# sourceMappingURL=containsFile.js.map
@@ -0,0 +1 @@
1
+ export declare function getPropertiesAtLocation(fileName: string, position: number, isTsPlugin?: boolean): string[] | undefined;
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getPropertiesAtLocation = void 0;
4
+ const language_core_1 = require("@vue/language-core");
5
+ const utils_1 = require("../utils");
6
+ function getPropertiesAtLocation(fileName, position, isTsPlugin = true) {
7
+ const match = (0, utils_1.getProject)(fileName);
8
+ if (!match) {
9
+ return;
10
+ }
11
+ const { info, files, ts } = match;
12
+ const languageService = info.languageService;
13
+ // mapping
14
+ const file = files.get(fileName);
15
+ if (file?.generated) {
16
+ const virtualScript = file.generated.languagePlugin.typescript?.getScript(file.generated.code);
17
+ if (!virtualScript) {
18
+ return;
19
+ }
20
+ let mapped = false;
21
+ for (const [_1, [_2, map]] of files.getMaps(virtualScript.code)) {
22
+ for (const [position2, mapping] of map.getGeneratedOffsets(position)) {
23
+ if ((0, language_core_1.isCompletionEnabled)(mapping.data)) {
24
+ position = position2;
25
+ mapped = true;
26
+ break;
27
+ }
28
+ }
29
+ if (mapped) {
30
+ break;
31
+ }
32
+ }
33
+ if (!mapped) {
34
+ return;
35
+ }
36
+ if (isTsPlugin) {
37
+ position += file.snapshot.getLength();
38
+ }
39
+ }
40
+ const program = languageService.getCurrentProgram();
41
+ if (!program) {
42
+ return;
43
+ }
44
+ const sourceFile = program.getSourceFile(fileName);
45
+ if (!sourceFile) {
46
+ return;
47
+ }
48
+ const node = findPositionIdentifier(sourceFile, sourceFile, position);
49
+ if (!node) {
50
+ return;
51
+ }
52
+ const checker = program.getTypeChecker();
53
+ const type = checker.getTypeAtLocation(node);
54
+ const props = type.getProperties();
55
+ return props.map(prop => prop.name);
56
+ function findPositionIdentifier(sourceFile, node, offset) {
57
+ let result;
58
+ node.forEachChild(child => {
59
+ if (!result) {
60
+ if (child.end === offset && ts.isIdentifier(child)) {
61
+ result = child;
62
+ }
63
+ else if (child.end >= offset && child.getStart(sourceFile) < offset) {
64
+ result = findPositionIdentifier(sourceFile, child, offset);
65
+ }
66
+ }
67
+ });
68
+ return result;
69
+ }
70
+ }
71
+ exports.getPropertiesAtLocation = getPropertiesAtLocation;
72
+ //# sourceMappingURL=getPropertiesAtLocation.js.map
@@ -0,0 +1 @@
1
+ export declare function getQuickInfoAtPosition(fileName: string, position: number): import("typescript").QuickInfo | undefined;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getQuickInfoAtPosition = void 0;
4
+ const utils_1 = require("../utils");
5
+ function getQuickInfoAtPosition(fileName, position) {
6
+ const match = (0, utils_1.getProject)(fileName);
7
+ if (!match) {
8
+ return;
9
+ }
10
+ const { info } = match;
11
+ const languageService = info.languageService;
12
+ return languageService.getQuickInfoAtPosition(fileName, position);
13
+ }
14
+ exports.getQuickInfoAtPosition = getQuickInfoAtPosition;
15
+ //# sourceMappingURL=getQuickInfoAtPosition.js.map
@@ -0,0 +1,6 @@
1
+ import type * as ts from 'typescript';
2
+ export interface Request {
3
+ type: 'containsFile' | 'collectExtractProps' | 'getPropertiesAtLocation' | 'getQuickInfoAtPosition' | 'getComponentProps' | 'getComponentEvents' | 'getTemplateContextProps' | 'getComponentNames' | 'getElementAttrs';
4
+ args: any;
5
+ }
6
+ export declare function startNamedPipeServer(serverKind: ts.server.ProjectKind): void;
package/lib/server.js ADDED
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.startNamedPipeServer = void 0;
4
+ const fs = require("fs");
5
+ const net = require("net");
6
+ const collectExtractProps_1 = require("./requests/collectExtractProps");
7
+ const componentInfos_1 = require("./requests/componentInfos");
8
+ const containsFile_1 = require("./requests/containsFile");
9
+ const getPropertiesAtLocation_1 = require("./requests/getPropertiesAtLocation");
10
+ const getQuickInfoAtPosition_1 = require("./requests/getQuickInfoAtPosition");
11
+ const utils_1 = require("./utils");
12
+ let started = false;
13
+ function startNamedPipeServer(serverKind) {
14
+ if (started)
15
+ return;
16
+ started = true;
17
+ const pipeFile = process.platform === 'win32'
18
+ ? `\\\\.\\pipe\\vue-tsp-${process.pid}`
19
+ : `/tmp/vue-tsp-${process.pid}`;
20
+ const server = net.createServer(connection => {
21
+ connection.on('data', data => {
22
+ const text = data.toString();
23
+ const request = JSON.parse(text);
24
+ if (request.type === 'containsFile') {
25
+ const result = containsFile_1.containsFile.apply(null, request.args);
26
+ connection.write(JSON.stringify(result ?? null));
27
+ }
28
+ else if (request.type === 'collectExtractProps') {
29
+ const result = collectExtractProps_1.collectExtractProps.apply(null, request.args);
30
+ connection.write(JSON.stringify(result ?? null));
31
+ }
32
+ else if (request.type === 'getPropertiesAtLocation') {
33
+ const result = getPropertiesAtLocation_1.getPropertiesAtLocation.apply(null, request.args);
34
+ connection.write(JSON.stringify(result ?? null));
35
+ }
36
+ else if (request.type === 'getQuickInfoAtPosition') {
37
+ const result = getQuickInfoAtPosition_1.getQuickInfoAtPosition.apply(null, request.args);
38
+ connection.write(JSON.stringify(result ?? null));
39
+ }
40
+ // Component Infos
41
+ else if (request.type === 'getComponentProps') {
42
+ const result = componentInfos_1.getComponentProps.apply(null, request.args);
43
+ connection.write(JSON.stringify(result ?? null));
44
+ }
45
+ else if (request.type === 'getComponentEvents') {
46
+ const result = componentInfos_1.getComponentEvents.apply(null, request.args);
47
+ connection.write(JSON.stringify(result ?? null));
48
+ }
49
+ else if (request.type === 'getTemplateContextProps') {
50
+ const result = componentInfos_1.getTemplateContextProps.apply(null, request.args);
51
+ connection.write(JSON.stringify(result ?? null));
52
+ }
53
+ else if (request.type === 'getComponentNames') {
54
+ const result = componentInfos_1.getComponentNames.apply(null, request.args);
55
+ connection.write(JSON.stringify(result ?? null));
56
+ }
57
+ else if (request.type === 'getElementAttrs') {
58
+ const result = componentInfos_1.getElementAttrs.apply(null, request.args);
59
+ connection.write(JSON.stringify(result ?? null));
60
+ }
61
+ else {
62
+ console.warn('[Vue Named Pipe Server] Unknown request type:', request.type);
63
+ connection.write(JSON.stringify(null));
64
+ }
65
+ });
66
+ connection.on('error', err => console.error('[Vue Named Pipe Server]', err.message));
67
+ });
68
+ clearupPipeTable();
69
+ if (!fs.existsSync(utils_1.pipeTable)) {
70
+ fs.writeFileSync(utils_1.pipeTable, JSON.stringify({}));
71
+ }
72
+ const table = JSON.parse(fs.readFileSync(utils_1.pipeTable, 'utf8'));
73
+ table[process.pid] = {
74
+ pid: process.pid,
75
+ pipeFile,
76
+ serverKind,
77
+ };
78
+ fs.writeFileSync(utils_1.pipeTable, JSON.stringify(table, undefined, 2));
79
+ try {
80
+ fs.unlinkSync(pipeFile);
81
+ }
82
+ catch { }
83
+ server.listen(pipeFile);
84
+ }
85
+ exports.startNamedPipeServer = startNamedPipeServer;
86
+ function clearupPipeTable() {
87
+ if (fs.existsSync(utils_1.pipeTable)) {
88
+ const table = JSON.parse(fs.readFileSync(utils_1.pipeTable, 'utf8'));
89
+ for (const pid in table) {
90
+ const { pipeFile } = table[pid];
91
+ try {
92
+ const client = net.connect(pipeFile);
93
+ client.on('connect', () => {
94
+ client.end();
95
+ });
96
+ client.on('error', () => {
97
+ const table = JSON.parse(fs.readFileSync(utils_1.pipeTable, 'utf8'));
98
+ delete table[pid];
99
+ fs.writeFileSync(utils_1.pipeTable, JSON.stringify(table, undefined, 2));
100
+ });
101
+ }
102
+ catch { }
103
+ }
104
+ }
105
+ }
106
+ //# sourceMappingURL=server.js.map
package/lib/utils.d.ts ADDED
@@ -0,0 +1,31 @@
1
+ import type { FileRegistry, VueCompilerOptions } from '@vue/language-core';
2
+ import type * as ts from 'typescript';
3
+ export interface PipeTable {
4
+ [pid: string]: {
5
+ pid: number;
6
+ pipeFile: string;
7
+ serverKind: ts.server.ProjectKind;
8
+ };
9
+ }
10
+ export declare const pipeTable: string;
11
+ export declare const projects: Map<ts.server.Project, {
12
+ info: ts.server.PluginCreateInfo;
13
+ files: FileRegistry;
14
+ ts: typeof ts;
15
+ vueOptions: VueCompilerOptions;
16
+ }>;
17
+ export declare function getProject(fileName: string): {
18
+ info: ts.server.PluginCreateInfo;
19
+ files: {
20
+ languagePlugins: import("@vue/language-core").LanguagePlugin<import("@vue/language-core").VirtualCode>[];
21
+ set(id: string, languageId: string, snapshot: ts.IScriptSnapshot, plugins?: import("@vue/language-core").LanguagePlugin<import("@vue/language-core").VirtualCode>[] | undefined): import("@vue/language-core").SourceFile;
22
+ delete(id: string): void;
23
+ get(id: string): import("@vue/language-core").SourceFile | undefined;
24
+ getByVirtualCode(virtualCode: import("@vue/language-core").VirtualCode): import("@vue/language-core").SourceFile;
25
+ getLinkedCodeMap(virtualCode: import("@vue/language-core").VirtualCode): import("@vue/language-core").LinkedCodeMap | undefined;
26
+ getMaps(virtualCode: import("@vue/language-core").VirtualCode): Map<string, [ts.IScriptSnapshot, import("@vue/language-core").SourceMap<import("@vue/language-core").CodeInformation>]>;
27
+ getVirtualCode(sourceFileId: string, virtualCodeId: string): readonly [import("@vue/language-core").VirtualCode, import("@vue/language-core").SourceFile] | readonly [undefined, undefined];
28
+ };
29
+ ts: typeof ts;
30
+ vueOptions: VueCompilerOptions;
31
+ } | undefined;
package/lib/utils.js ADDED
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getProject = exports.projects = exports.pipeTable = void 0;
4
+ const os = require("os");
5
+ const path = require("path");
6
+ exports.pipeTable = path.join(os.tmpdir(), 'vue-tsp-table.json');
7
+ exports.projects = new Map();
8
+ function getProject(fileName) {
9
+ for (const [project, data] of exports.projects) {
10
+ if (project.containsFile(fileName)) {
11
+ return data;
12
+ }
13
+ }
14
+ }
15
+ exports.getProject = getProject;
16
+ //# sourceMappingURL=utils.js.map
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@vue/typescript-plugin",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
4
  "license": "MIT",
5
5
  "files": [
6
- "out/**/*.js",
7
- "out/**/*.d.ts"
6
+ "**/*.js",
7
+ "**/*.d.ts"
8
8
  ],
9
9
  "repository": {
10
10
  "type": "git",
@@ -13,11 +13,11 @@
13
13
  },
14
14
  "dependencies": {
15
15
  "@volar/typescript": "~2.1.0",
16
- "@vue/language-core": "2.0.0",
16
+ "@vue/language-core": "2.0.2",
17
17
  "@vue/shared": "^3.4.0"
18
18
  },
19
19
  "devDependencies": {
20
20
  "@types/node": "latest"
21
21
  },
22
- "gitHead": "aa47e5a7d8a6dae62cc80dbdb5db6a9bfa4f8715"
22
+ "gitHead": "b377d5f990ffe7ef44f0d1871fcb8b5c2deafad1"
23
23
  }