Skip to content

Commit

Permalink
Update to Volar 2 stable (#762)
Browse files Browse the repository at this point in the history
* Revert "revert: Volar 2.0 Upgrade (#735)"

This reverts commit 0207697.

* Update to Volar 2 stable

* Fix TS support

* Update tests

* Upgrade Volar

* Implement `getExtraScripts()`

* Merge

* Format

* chore: bump volar

* fix: Small fixes related to formatting

* fix: intellisense not working in script tag

* chore: remove unneeded code

* test(completions): Add test for completions inside script tags

* test: add more tests

* chore: changeset

---------

Co-authored-by: Princesseuh <[email protected]>
  • Loading branch information
johnsoncodehk and Princesseuh authored Feb 8, 2024
1 parent ada26ac commit 15a5532
Show file tree
Hide file tree
Showing 42 changed files with 1,531 additions and 1,747 deletions.
10 changes: 10 additions & 0 deletions .changeset/brave-spiders-marry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@astrojs/language-server': minor
'@astrojs/check': minor
'@astrojs/ts-plugin': minor
'astro-vscode': minor
---

Upgrade to Volar 2.0. No regressions are currently expected, however as this is a fairly consequential backend change, please report any issues you encounter.

For reference, Volar is the underlying framework that powers the Astro language server, you can think of it as Vite for editor tooling.
14 changes: 5 additions & 9 deletions packages/astro-check/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,7 @@ export async function check(flags: Partial<Flags>): Promise<boolean | void>;
export async function check(flags: Partial<Flags>): Promise<boolean | void> {
const workspaceRoot = path.resolve(flags.root ?? process.cwd());
const require = createRequire(import.meta.url);
const checker = new AstroCheck(
workspaceRoot,
require.resolve('typescript/lib/tsserverlibrary.js'),
flags.tsconfig
);
const checker = new AstroCheck(workspaceRoot, require.resolve('typescript'), flags.tsconfig);

let req = 0;

Expand All @@ -44,20 +40,20 @@ export async function check(flags: Partial<Flags>): Promise<boolean | void> {
// Dynamically get the list of extensions to watch from the files already included in the project
const checkedExtensions = Array.from(
new Set(
checker.project.languageHost.getScriptFileNames().map((fileName) => path.extname(fileName))
checker.linter.languageHost.getScriptFileNames().map((fileName) => path.extname(fileName))
)
);
createWatcher(workspaceRoot, checkedExtensions)
.on('add', (fileName) => {
checker.project.fileCreated(fileName);
checker.linter.fileCreated(fileName);
update();
})
.on('unlink', (fileName) => {
checker.project.fileDeleted(fileName);
checker.linter.fileDeleted(fileName);
update();
})
.on('change', (fileName) => {
checker.project.fileUpdated(fileName);
checker.linter.fileUpdated(fileName);
update();
});
}
Expand Down
27 changes: 13 additions & 14 deletions packages/language-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,19 @@
"dependencies": {
"@astrojs/compiler": "^2.4.0",
"@jridgewell/sourcemap-codec": "^1.4.15",
"@volar/kit": "~1.11.1",
"@volar/language-core": "~1.11.1",
"@volar/language-server": "~1.11.1",
"@volar/language-service": "~1.11.1",
"@volar/source-map": "~1.11.1",
"@volar/typescript": "~1.11.1",
"@volar/kit": "~2.0.2",
"@volar/language-core": "~2.0.2",
"@volar/language-server": "~2.0.2",
"@volar/language-service": "~2.0.2",
"@volar/typescript": "~2.0.2",
"fast-glob": "^3.2.12",
"muggle-string": "^0.3.1",
"volar-service-css": "0.0.17",
"volar-service-emmet": "0.0.17",
"volar-service-html": "0.0.17",
"volar-service-prettier": "0.0.17",
"volar-service-typescript": "0.0.17",
"volar-service-typescript-twoslash-queries": "0.0.17",
"vscode-html-languageservice": "^5.1.0",
"volar-service-css": "0.0.28",
"volar-service-emmet": "0.0.28",
"volar-service-html": "0.0.28",
"volar-service-prettier": "0.0.28",
"volar-service-typescript": "0.0.28",
"volar-service-typescript-twoslash-queries": "0.0.28",
"vscode-html-languageservice": "^5.1.1",
"vscode-uri": "^3.0.8"
},
"devDependencies": {
Expand All @@ -51,6 +49,7 @@
"@types/chai": "^4.3.5",
"@types/mocha": "^10.0.1",
"@types/node": "^18.17.8",
"@volar/test-utils": "~2.0.2",
"astro": "^4.1.0",
"chai": "^4.3.7",
"mocha": "^10.2.0",
Expand Down
40 changes: 12 additions & 28 deletions packages/language-server/src/check.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,8 @@ export interface CheckResult {
}

export class AstroCheck {
private ts!: typeof import('typescript/lib/tsserverlibrary.js');
public project!: ReturnType<typeof kit.createProject>;
private linter!: ReturnType<typeof kit.createLinter>;
private ts!: typeof import('typescript');
public linter!: ReturnType<(typeof kit)['createTypeScriptChecker']>;

constructor(
private readonly workspacePath: string,
Expand Down Expand Up @@ -60,8 +59,7 @@ export class AstroCheck {
}
| undefined;
}): Promise<CheckResult> {
let files =
fileNames !== undefined ? fileNames : this.project.languageHost.getScriptFileNames();
let files = fileNames !== undefined ? fileNames : this.linter.languageHost.getScriptFileNames();

const result: CheckResult = {
status: undefined,
Expand Down Expand Up @@ -98,7 +96,7 @@ export class AstroCheck {
console.info(errorText);
}

const fileSnapshot = this.project.languageHost.getScriptSnapshot(file);
const fileSnapshot = this.linter.languageHost.getScriptSnapshot(file);
const fileContent = fileSnapshot?.getText(0, fileSnapshot.getLength());

result.fileResult.push({
Expand Down Expand Up @@ -131,38 +129,24 @@ export class AstroCheck {
const tsconfigPath = this.getTsconfig();

const astroInstall = getAstroInstall([this.workspacePath]);
const config: kit.Config = {
languages: {
astro: getLanguageModule(
typeof astroInstall === 'string' ? undefined : astroInstall,
this.ts
),
svelte: getSvelteLanguageModule(),
vue: getVueLanguageModule(),
},
services: {
typescript: createTypeScriptService(),
astro: createAstroService(),
},
};
const languages = [
getLanguageModule(typeof astroInstall === 'string' ? undefined : astroInstall, this.ts),
getSvelteLanguageModule(),
getVueLanguageModule(),
];
const services = [createTypeScriptService(this.ts), createAstroService(this.ts)];

if (tsconfigPath) {
this.project = kit.createProject(tsconfigPath, [
{ extension: 'astro', isMixedContent: true, scriptKind: 7 },
{ extension: 'vue', isMixedContent: true, scriptKind: 7 },
{ extension: 'svelte', isMixedContent: true, scriptKind: 7 },
]);
this.linter = kit.createTypeScriptChecker(languages, services, tsconfigPath);
} else {
this.project = kit.createInferredProject(this.workspacePath, () => {
this.linter = kit.createTypeScriptInferredChecker(languages, services, () => {
return fg.sync('**/*.astro', {
cwd: this.workspacePath,
ignore: ['node_modules'],
absolute: true,
});
});
}

this.linter = kit.createLinter(config, this.project.languageHost);
}

private getTsconfig() {
Expand Down
91 changes: 32 additions & 59 deletions packages/language-server/src/core/astro2tsx.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { convertToTSX } from '@astrojs/compiler/sync';
import type { ConvertToTSXOptions, DiagnosticMessage, TSXResult } from '@astrojs/compiler/types';
import type { ConvertToTSXOptions, TSXResult } from '@astrojs/compiler/types';
import { decode } from '@jridgewell/sourcemap-codec';
import { FileKind, FileRangeCapabilities, VirtualFile } from '@volar/language-core';
import type { CodeInformation, CodeMapping, VirtualCode } from '@volar/language-core';
import { Range } from '@volar/language-server';
import { HTMLDocument, TextDocument } from 'vscode-html-languageservice';
import { patchTSX } from './utils.js';
Expand All @@ -11,12 +11,6 @@ export interface LSPTSXRanges {
body: Range;
}

interface Astro2TSXResult {
virtualFile: VirtualFile;
diagnostics: DiagnosticMessage[];
ranges: LSPTSXRanges;
}

export function safeConvertToTSX(content: string, options: ConvertToTSXOptions) {
try {
const tsx = convertToTSX(content, { filename: options.filename });
Expand Down Expand Up @@ -73,34 +67,27 @@ export function getTSXRangesAsLSPRanges(tsx: TSXResult): LSPTSXRanges {
};
}

export function astro2tsx(
input: string,
fileName: string,
ts: typeof import('typescript/lib/tsserverlibrary.js'),
htmlDocument: HTMLDocument
): Astro2TSXResult {
export function astro2tsx(input: string, fileName: string, htmlDocument: HTMLDocument) {
const tsx = safeConvertToTSX(input, { filename: fileName });

return {
virtualFile: getVirtualFileTSX(input, tsx, fileName, ts, htmlDocument),
virtualCode: getVirtualCodeTSX(input, tsx, fileName, htmlDocument),
diagnostics: tsx.diagnostics,
ranges: getTSXRangesAsLSPRanges(tsx),
};
}

function getVirtualFileTSX(
function getVirtualCodeTSX(
input: string,
tsx: TSXResult,
fileName: string,
ts: typeof import('typescript/lib/tsserverlibrary.js'),
htmlDocument: HTMLDocument
): VirtualFile {
): VirtualCode {
tsx.code = patchTSX(tsx.code, fileName);
const v3Mappings = decode(tsx.map.mappings);
const sourcedDoc = TextDocument.create(fileName, 'astro', 0, input);
const genDoc = TextDocument.create(fileName + '.tsx', 'typescriptreact', 0, tsx.code);

const mappings: VirtualFile['mappings'] = [];
const sourcedDoc = TextDocument.create('', 'astro', 0, input);
const genDoc = TextDocument.create('', 'typescriptreact', 0, tsx.code);
const mappings: CodeMapping[] = [];

let current:
| {
Expand Down Expand Up @@ -131,33 +118,37 @@ function getVirtualFileTSX(
const lastMapping = mappings.length ? mappings[mappings.length - 1] : undefined;
if (
lastMapping &&
lastMapping.generatedRange[1] === current.genOffset &&
lastMapping.sourceRange[1] === current.sourceOffset
lastMapping.generatedOffsets[0] + lastMapping.lengths[0] === current.genOffset &&
lastMapping.sourceOffsets[0] + lastMapping.lengths[0] === current.sourceOffset
) {
lastMapping.generatedRange[1] = current.genOffset + length;
lastMapping.sourceRange[1] = current.sourceOffset + length;
lastMapping.lengths[0] += length;
} else {
// Disable features inside script tags. This is a bit annoying to do, I wonder if maybe leaving script tags
// unmapped would be better.
const node = htmlDocument.findNodeAt(current.sourceOffset);
const rangeCapabilities: FileRangeCapabilities =
const rangeCapabilities: CodeInformation =
node.tag !== 'script'
? FileRangeCapabilities.full
? {
verification: true,
completion: true,
semantic: true,
navigation: true,
structure: true,
format: false,
}
: {
verification: false,
completion: false,
definition: false,
diagnostic: false,
displayWithLink: false,
hover: false,
references: false,
referencesCodeLens: false,
rename: false,
semanticTokens: false,
semantic: false,
navigation: false,
structure: false,
format: false,
};

mappings.push({
sourceRange: [current.sourceOffset, current.sourceOffset + length],
generatedRange: [current.genOffset, current.genOffset + length],
sourceOffsets: [current.sourceOffset],
generatedOffsets: [current.genOffset],
lengths: [length],
data: rangeCapabilities,
});
}
Expand All @@ -174,33 +165,15 @@ function getVirtualFileTSX(
}
}

const ast = ts.createSourceFile('/a.tsx', tsx.code, ts.ScriptTarget.ESNext);
if (ast.statements[0]) {
mappings.push({
sourceRange: [0, input.length],
generatedRange: [ast.statements[0].getStart(ast), tsx.code.length],
data: {},
});
}

return {
fileName: fileName + '.tsx',
kind: FileKind.TypeScriptHostFile,
capabilities: {
codeAction: true,
documentFormatting: false,
diagnostic: true,
documentSymbol: true,
inlayHint: true,
foldingRange: true,
},
codegenStacks: [],
id: 'tsx',
languageId: 'typescriptreact',
snapshot: {
getText: (start, end) => tsx.code.substring(start, end),
getLength: () => tsx.code.length,
getChangeRange: () => undefined,
},
mappings: mappings,
embeddedFiles: [],
embeddedCodes: [],
};
}
Loading

0 comments on commit 15a5532

Please sign in to comment.