Skip to content

Commit 2bcd145

Browse files
committed
refactor(tsc): rework based on first-party TS API (#3795)
1 parent 519f768 commit 2bcd145

File tree

25 files changed

+287
-541
lines changed

25 files changed

+287
-541
lines changed

extensions/vscode/src/common.ts

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,6 @@ async function doActivate(context: vscode.ExtensionContext, createLc: CreateLang
7070
// doctor.register(context, client);
7171
// componentMeta.register(context, client);
7272

73-
const supportedLanguages: Record<string, boolean> = {
74-
vue: true,
75-
markdown: true,
76-
javascript: true,
77-
typescript: true,
78-
javascriptreact: true,
79-
typescriptreact: true,
80-
};
8173
const selectors: vscode.DocumentFilter[] = [{ language: 'vue' }];
8274

8375
if (config.server.petiteVue.supportHtmlFile) {
@@ -87,7 +79,7 @@ async function doActivate(context: vscode.ExtensionContext, createLc: CreateLang
8779
selectors.push({ language: 'markdown' });
8880
}
8981

90-
activateAutoInsertion([client], document => supportedLanguages[document.languageId]); // TODO: implement auto insert .value
82+
activateAutoInsertion(selectors, client); // TODO: implement auto insert .value
9183
activateDocumentDropEdit(selectors, client);
9284
activateWriteVirtualFiles('volar.action.writeVirtualFiles', client);
9385

extensions/vscode/src/nodeClientMain.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@ import * as fs from 'fs';
99

1010
export async function activate(context: vscode.ExtensionContext) {
1111

12-
const languageClients: lsp.LanguageClient[] = [];
13-
12+
let languageClient: lsp.LanguageClient;
1413
let serverPathStatusItem: vscode.StatusBarItem | undefined;
1514

1615
await commonActivate(context, (
@@ -110,7 +109,7 @@ export async function activate(context: vscode.ExtensionContext) {
110109
);
111110
client.start();
112111

113-
languageClients.push(client);
112+
languageClient = client;
114113

115114
updateProviders(client);
116115

@@ -149,7 +148,7 @@ export async function activate(context: vscode.ExtensionContext) {
149148
volarLabs: {
150149
version: supportLabsVersion,
151150
codegenStackSupport: true,
152-
languageClients,
151+
languageClient: languageClient!,
153152
languageServerProtocol: serverLib,
154153
},
155154
} satisfies ExportsInfoForLabs;

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
"devDependencies": {
2020
"@lerna-lite/cli": "latest",
2121
"@lerna-lite/publish": "latest",
22-
"@volar/language-service": "2.0.0-alpha.3",
22+
"@volar/language-service": "2.0.0-alpha.4",
2323
"typescript": "latest",
2424
"vite": "latest",
2525
"vitest": "latest"

packages/component-meta/src/base.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type * as ts from 'typescript/lib/tsserverlibrary';
33
import * as path from 'path-browserify';
44
import { code as typeHelpersCode } from 'vue-component-type-helpers';
55
import { code as vue2TypeHelpersCode } from 'vue-component-type-helpers/vue2';
6-
import { createLanguage, decorateLanguageService } from '@volar/typescript';
6+
import { createLanguage } from '@volar/typescript';
77

88
import type {
99
MetaCheckerOptions,
@@ -164,8 +164,6 @@ export function baseCreate(
164164
const { languageServiceHost } = language.typescript!;
165165
const tsLs = ts.createLanguageService(languageServiceHost);
166166

167-
decorateLanguageService(language.files, tsLs, false);
168-
169167
if (checkerOptions.forceUseTs) {
170168
const getScriptKind = languageServiceHost.getScriptKind?.bind(languageServiceHost);
171169
languageServiceHost.getScriptKind = (fileName) => {

packages/language-core/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"directory": "packages/language-core"
1414
},
1515
"dependencies": {
16-
"@volar/language-core": "2.0.0-alpha.3",
16+
"@volar/language-core": "2.0.0-alpha.4",
1717
"@vue/compiler-dom": "^3.3.0",
1818
"@vue/shared": "^3.3.0",
1919
"computeds": "^0.0.1",

packages/language-core/src/generators/script.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export function* generate(
4545
generic: undefined,
4646
genericOffset: 0,
4747
attrs: {},
48-
ast: ts.createSourceFile('', '', ts.ScriptTarget.Latest, false, ts.ScriptKind.TS),
48+
ast: ts.createSourceFile('', '', 99 satisfies ts.ScriptTarget.Latest, false, ts.ScriptKind.TS),
4949
};
5050
scriptSetupRanges = {
5151
bindings: [],
@@ -1013,7 +1013,7 @@ type __VLS_NormalizeEmits<T> = __VLS_Prettify<
10131013
for (const [segment, offset, onlyError] of eachInterpolationSegment(
10141014
ts,
10151015
cssBind.text,
1016-
ts.createSourceFile('/a.txt', cssBind.text, ts.ScriptTarget.ESNext),
1016+
ts.createSourceFile('/a.txt', cssBind.text, 99 satisfies ts.ScriptTarget.ESNext),
10171017
emptyLocalVars,
10181018
cssIds,
10191019
vueCompilerOptions,

packages/language-core/src/generators/template.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -575,7 +575,7 @@ export function* generate(
575575
if (leftExpressionRange && leftExpressionText) {
576576

577577
const collectAst = createTsAst(node.parseResult, `const [${leftExpressionText}]`);
578-
collectVars(ts, collectAst, forBlockVars);
578+
collectVars(ts, collectAst, collectAst, forBlockVars);
579579

580580
for (const varName of forBlockVars)
581581
localVars.set(varName, (localVars.get(varName) ?? 0) + 1);
@@ -871,7 +871,7 @@ export function* generate(
871871
);
872872

873873
const slotAst = createTsAst(slotDir, `(${slotDir.exp.content}) => {}`);
874-
collectVars(ts, slotAst, slotBlockVars);
874+
collectVars(ts, slotAst, slotAst, slotBlockVars);
875875
hasProps = true;
876876
if (slotDir.exp.content.indexOf(':') === -1) {
877877
yield _ts('const [');
@@ -1093,10 +1093,10 @@ export function* generate(
10931093
const ast = createTsAst(prop.exp, prop.exp.content);
10941094
let isCompoundExpression = true;
10951095

1096-
if (ast.getChildCount() === 2) { // with EOF
1097-
ast.forEachChild(child_1 => {
1096+
if (ast.statements.length === 1) {
1097+
ts.forEachChild(ast, child_1 => {
10981098
if (ts.isExpressionStatement(child_1)) {
1099-
child_1.forEachChild(child_2 => {
1099+
ts.forEachChild(child_1, child_2 => {
11001100
if (ts.isArrowFunction(child_2)) {
11011101
isCompoundExpression = false;
11021102
}
@@ -1902,7 +1902,7 @@ export function* generate(
19021902
function createTsAst(astHolder: any, text: string) {
19031903
if (astHolder.__volar_ast_text !== text) {
19041904
astHolder.__volar_ast_text = text;
1905-
astHolder.__volar_ast = ts.createSourceFile('/a.ts', text, ts.ScriptTarget.ESNext);
1905+
astHolder.__volar_ast = ts.createSourceFile('/a.ts', text, 99 satisfies ts.ScriptTarget.ESNext);
19061906
}
19071907
return astHolder.__volar_ast as ts.SourceFile;
19081908
}

packages/language-core/src/parsers/scriptRanges.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { TextRange } from '../types';
22
import type * as ts from 'typescript/lib/tsserverlibrary';
3-
import { getStartEnd, parseBindingRanges } from './scriptSetupRanges';
3+
import { getNodeText, getStartEnd, parseBindingRanges } from './scriptSetupRanges';
44

55
export interface ScriptRanges extends ReturnType<typeof parseScriptRanges> { }
66

@@ -17,12 +17,12 @@ export function parseScriptRanges(ts: typeof import('typescript/lib/tsserverlibr
1717

1818
const bindings = hasScriptSetup ? parseBindingRanges(ts, ast) : [];
1919

20-
ast.forEachChild(raw => {
20+
ts.forEachChild(ast, raw => {
2121

2222
if (ts.isExportAssignment(raw)) {
2323

2424
let node: ts.AsExpression | ts.ExportAssignment | ts.ParenthesizedExpression = raw;
25-
while (ts.isAsExpression(node.expression) || ts.isParenthesizedExpression(node.expression)) { // fix https://github.com/vuejs/language-tools/issues/1882
25+
while (isAsExpression(node.expression) || ts.isParenthesizedExpression(node.expression)) { // fix https://github.com/vuejs/language-tools/issues/1882
2626
node = node.expression;
2727
}
2828

@@ -39,12 +39,13 @@ export function parseScriptRanges(ts: typeof import('typescript/lib/tsserverlibr
3939
if (obj) {
4040
let componentsOptionNode: ts.ObjectLiteralExpression | undefined;
4141
let nameOptionNode: ts.Expression | undefined;
42-
obj.forEachChild(node => {
42+
ts.forEachChild(obj, node => {
4343
if (ts.isPropertyAssignment(node) && ts.isIdentifier(node.name)) {
44-
if (node.name.escapedText === 'components' && ts.isObjectLiteralExpression(node.initializer)) {
44+
const name = getNodeText(ts, node.name, ast);
45+
if (name === 'components' && ts.isObjectLiteralExpression(node.initializer)) {
4546
componentsOptionNode = node.initializer;
4647
}
47-
if (node.name.escapedText === 'name') {
48+
if (name === 'name') {
4849
nameOptionNode = node.initializer;
4950
}
5051
}
@@ -68,6 +69,11 @@ export function parseScriptRanges(ts: typeof import('typescript/lib/tsserverlibr
6869
};
6970

7071
function _getStartEnd(node: ts.Node) {
71-
return getStartEnd(node, ast);
72+
return getStartEnd(ts, node, ast);
73+
}
74+
75+
// isAsExpression is missing in tsc
76+
function isAsExpression(node: ts.Node): node is ts.AsExpression {
77+
return node.kind === ts.SyntaxKind.AsExpression;
7278
}
7379
}

packages/language-core/src/parsers/scriptSetupRanges.ts

Lines changed: 37 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ export function parseScriptSetupRanges(
3434
define?: ReturnType<typeof parseDefineFunction>;
3535
} = {};
3636

37-
const definePropProposalA = vueCompilerOptions.experimentalDefinePropProposal === 'kevinEdition' || ast.getFullText().trimStart().startsWith('// @experimentalDefinePropProposal=kevinEdition');
38-
const definePropProposalB = vueCompilerOptions.experimentalDefinePropProposal === 'johnsonEdition' || ast.getFullText().trimStart().startsWith('// @experimentalDefinePropProposal=johnsonEdition');
37+
const definePropProposalA = vueCompilerOptions.experimentalDefinePropProposal === 'kevinEdition' || ast.text.trimStart().startsWith('// @experimentalDefinePropProposal=kevinEdition');
38+
const definePropProposalB = vueCompilerOptions.experimentalDefinePropProposal === 'johnsonEdition' || ast.text.trimStart().startsWith('// @experimentalDefinePropProposal=johnsonEdition');
3939
const defineProp: {
4040
name: TextRange | undefined;
4141
nameIsString: boolean;
@@ -44,10 +44,10 @@ export function parseScriptSetupRanges(
4444
required: boolean;
4545
}[] = [];
4646
const bindings = parseBindingRanges(ts, ast);
47-
const text = ast.getFullText();
47+
const text = ast.text;
4848
const leadingCommentEndOffset = ts.getLeadingCommentRanges(text, 0)?.reverse()[0].end ?? 0;
4949

50-
ast.forEachChild(node => {
50+
ts.forEachChild(ast, node => {
5151
const isTypeExport = (ts.isTypeAliasDeclaration(node) || ts.isInterfaceDeclaration(node)) && node.modifiers?.some(mod => mod.kind === ts.SyntaxKind.ExportKeyword);
5252
if (
5353
!foundNonImportExportNode
@@ -57,18 +57,18 @@ export function parseScriptSetupRanges(
5757
// fix https://github.com/vuejs/language-tools/issues/1223
5858
&& !ts.isImportEqualsDeclaration(node)
5959
) {
60-
const commentRanges = ts.getLeadingCommentRanges(text, node.getFullStart());
60+
const commentRanges = ts.getLeadingCommentRanges(text, node.pos);
6161
if (commentRanges?.length) {
6262
const commentRange = commentRanges.sort((a, b) => a.pos - b.pos)[0];
6363
importSectionEndOffset = commentRange.pos;
6464
}
6565
else {
66-
importSectionEndOffset = node.getStart(ast);
66+
importSectionEndOffset = getStartEnd(ts, node, ast).start;
6767
}
6868
foundNonImportExportNode = true;
6969
}
7070
});
71-
ast.forEachChild(child => visitNode(child, [ast]));
71+
ts.forEachChild(ast, child => visitNode(child, [ast]));
7272

7373
return {
7474
leadingCommentEndOffset,
@@ -82,7 +82,7 @@ export function parseScriptSetupRanges(
8282
};
8383

8484
function _getStartEnd(node: ts.Node) {
85-
return getStartEnd(node, ast);
85+
return getStartEnd(ts, node, ast);
8686
}
8787

8888
function parseDefineFunction(node: ts.CallExpression): TextRange & {
@@ -102,7 +102,7 @@ export function parseScriptSetupRanges(
102102
ts.isCallExpression(node)
103103
&& ts.isIdentifier(node.expression)
104104
) {
105-
const callText = node.expression.getText(ast);
105+
const callText = getNodeText(ts, node.expression, ast);
106106
if (vueCompilerOptions.macros.defineModel.includes(callText)) {
107107
let name: TextRange | undefined;
108108
let options: ts.Node | undefined;
@@ -121,7 +121,7 @@ export function parseScriptSetupRanges(
121121
let required = false;
122122
if (options && ts.isObjectLiteralExpression(options)) {
123123
for (const property of options.properties) {
124-
if (ts.isPropertyAssignment(property) && ts.isIdentifier(property.name) && property.name.getText(ast) === 'required' && property.initializer.kind === ts.SyntaxKind.TrueKeyword) {
124+
if (ts.isPropertyAssignment(property) && ts.isIdentifier(property.name) && getNodeText(ts, property.name, ast) === 'required' && property.initializer.kind === ts.SyntaxKind.TrueKeyword) {
125125
required = true;
126126
break;
127127
}
@@ -142,7 +142,7 @@ export function parseScriptSetupRanges(
142142
const secondArg = node.arguments[1];
143143
if (ts.isObjectLiteralExpression(secondArg)) {
144144
for (const property of secondArg.properties) {
145-
if (ts.isPropertyAssignment(property) && ts.isIdentifier(property.name) && property.name.getText(ast) === 'required' && property.initializer.kind === ts.SyntaxKind.TrueKeyword) {
145+
if (ts.isPropertyAssignment(property) && ts.isIdentifier(property.name) && getNodeText(ts, property.name, ast) === 'required' && property.initializer.kind === ts.SyntaxKind.TrueKeyword) {
146146
required = true;
147147
break;
148148
}
@@ -181,13 +181,13 @@ export function parseScriptSetupRanges(
181181
else if (vueCompilerOptions.macros.defineSlots.includes(callText)) {
182182
slots.define = parseDefineFunction(node);
183183
if (ts.isVariableDeclaration(parent)) {
184-
slots.name = parent.name.getText(ast);
184+
slots.name = getNodeText(ts, parent.name, ast);
185185
}
186186
}
187187
else if (vueCompilerOptions.macros.defineEmits.includes(callText)) {
188188
emits.define = parseDefineFunction(node);
189189
if (ts.isVariableDeclaration(parent)) {
190-
emits.name = parent.name.getText(ast);
190+
emits.name = getNodeText(ts, parent.name, ast);
191191
}
192192
}
193193
else if (vueCompilerOptions.macros.defineExpose.includes(callText)) {
@@ -199,7 +199,7 @@ export function parseScriptSetupRanges(
199199
for (let i = parents.length - 1; i >= 0; i--) {
200200
if (ts.isStatement(parents[i])) {
201201
const statement = parents[i];
202-
statement.forEachChild(child => {
202+
ts.forEachChild(statement, child => {
203203
const range = _getStartEnd(child);
204204
statementRange ??= range;
205205
statementRange.end = range.end;
@@ -217,7 +217,7 @@ export function parseScriptSetupRanges(
217217
};
218218

219219
if (ts.isVariableDeclaration(parent)) {
220-
props.name = parent.name.getText(ast);
220+
props.name = getNodeText(ts, parent.name, ast);
221221
}
222222
if (node.arguments.length) {
223223
props.define.arg = _getStartEnd(node.arguments[0]);
@@ -233,11 +233,11 @@ export function parseScriptSetupRanges(
233233
props.withDefaults.arg = _getStartEnd(arg);
234234
}
235235
if (ts.isVariableDeclaration(parent)) {
236-
props.name = parent.name.getText(ast);
236+
props.name = getNodeText(ts, parent.name, ast);
237237
}
238238
}
239239
}
240-
node.forEachChild(child => {
240+
ts.forEachChild(node, child => {
241241
parents.push(node);
242242
visitNode(child, parents);
243243
parents.pop();
@@ -247,7 +247,7 @@ export function parseScriptSetupRanges(
247247

248248
export function parseBindingRanges(ts: typeof import('typescript/lib/tsserverlibrary'), sourceFile: ts.SourceFile) {
249249
const bindings: TextRange[] = [];
250-
sourceFile.forEachChild(node => {
250+
ts.forEachChild(sourceFile, node => {
251251
if (ts.isVariableStatement(node)) {
252252
for (const node_2 of node.declarationList.declarations) {
253253
const vars = _findBindingVars(node_2.name);
@@ -290,7 +290,7 @@ export function parseBindingRanges(ts: typeof import('typescript/lib/tsserverlib
290290
});
291291
return bindings;
292292
function _getStartEnd(node: ts.Node) {
293-
return getStartEnd(node, sourceFile);
293+
return getStartEnd(ts, node, sourceFile);
294294
}
295295
function _findBindingVars(left: ts.BindingName) {
296296
return findBindingVars(ts, left, sourceFile);
@@ -303,7 +303,7 @@ export function findBindingVars(ts: typeof import('typescript/lib/tsserverlibrar
303303
return vars;
304304
function worker(_node: ts.Node) {
305305
if (ts.isIdentifier(_node)) {
306-
vars.push(getStartEnd(_node, sourceFile));
306+
vars.push(getStartEnd(ts, _node, sourceFile));
307307
}
308308
// { ? } = ...
309309
// [ ? ] = ...
@@ -320,7 +320,7 @@ export function findBindingVars(ts: typeof import('typescript/lib/tsserverlibrar
320320
}
321321
// { foo } = ...
322322
else if (ts.isShorthandPropertyAssignment(_node)) {
323-
vars.push(getStartEnd(_node.name, sourceFile));
323+
vars.push(getStartEnd(ts, _node.name, sourceFile));
324324
}
325325
// { ...? } = ...
326326
// [ ...? ] = ...
@@ -330,9 +330,22 @@ export function findBindingVars(ts: typeof import('typescript/lib/tsserverlibrar
330330
}
331331
}
332332

333-
export function getStartEnd(node: ts.Node, sourceFile: ts.SourceFile) {
333+
export function getStartEnd(
334+
ts: typeof import('typescript/lib/tsserverlibrary'),
335+
node: ts.Node,
336+
sourceFile: ts.SourceFile
337+
) {
334338
return {
335-
start: node.getStart(sourceFile),
336-
end: node.getEnd(),
339+
start: (ts as any).getTokenPosOfNode(node, sourceFile) as number,
340+
end: node.end,
337341
};
338342
}
343+
344+
export function getNodeText(
345+
ts: typeof import('typescript/lib/tsserverlibrary'),
346+
node: ts.Node,
347+
sourceFile: ts.SourceFile
348+
) {
349+
const { start, end } = getStartEnd(ts, node, sourceFile);
350+
return sourceFile.text.substring(start, end);
351+
}

0 commit comments

Comments
 (0)