@astrojs/language-server 0.15.0 → 0.17.0
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/CHANGELOG.md +32 -0
- package/dist/check.js +1 -2
- package/dist/core/config/ConfigManager.d.ts +20 -16
- package/dist/core/config/ConfigManager.js +112 -46
- package/dist/core/config/interfaces.d.ts +0 -52
- package/dist/core/documents/AstroDocument.d.ts +1 -0
- package/dist/core/documents/AstroDocument.js +1 -0
- package/dist/core/documents/DocumentMapper.d.ts +2 -0
- package/dist/core/documents/DocumentMapper.js +9 -9
- package/dist/core/documents/parseAstro.js +1 -1
- package/dist/core/documents/utils.d.ts +1 -0
- package/dist/core/documents/utils.js +19 -5
- package/dist/plugins/PluginHost.d.ts +2 -1
- package/dist/plugins/PluginHost.js +8 -6
- package/dist/plugins/astro/AstroPlugin.d.ts +1 -6
- package/dist/plugins/astro/AstroPlugin.js +1 -83
- package/dist/plugins/astro/features/CompletionsProvider.d.ts +4 -5
- package/dist/plugins/astro/features/CompletionsProvider.js +49 -59
- package/dist/plugins/css/CSSPlugin.d.ts +5 -5
- package/dist/plugins/css/CSSPlugin.js +41 -20
- package/dist/plugins/html/HTMLPlugin.d.ts +4 -4
- package/dist/plugins/html/HTMLPlugin.js +20 -16
- package/dist/plugins/html/features/astro-attributes.js +44 -27
- package/dist/plugins/typescript/LanguageServiceManager.js +1 -1
- package/dist/plugins/typescript/TypeScriptPlugin.d.ts +5 -4
- package/dist/plugins/typescript/TypeScriptPlugin.js +30 -108
- package/dist/plugins/typescript/astro-sys.js +3 -5
- package/dist/plugins/typescript/astro2tsx.js +1 -2
- package/dist/plugins/typescript/features/CodeActionsProvider.d.ts +16 -0
- package/dist/plugins/typescript/features/CodeActionsProvider.js +206 -0
- package/dist/plugins/typescript/features/CompletionsProvider.d.ts +5 -2
- package/dist/plugins/typescript/features/CompletionsProvider.js +116 -68
- package/dist/plugins/typescript/features/DefinitionsProvider.d.ts +9 -0
- package/dist/plugins/typescript/features/DefinitionsProvider.js +57 -0
- package/dist/plugins/typescript/features/DiagnosticsProvider.js +60 -18
- package/dist/plugins/typescript/features/DocumentSymbolsProvider.js +3 -4
- package/dist/plugins/typescript/features/FoldingRangesProvider.js +13 -6
- package/dist/plugins/typescript/features/HoverProvider.js +14 -1
- package/dist/plugins/typescript/features/SemanticTokenProvider.js +1 -1
- package/dist/plugins/typescript/features/SignatureHelpProvider.js +11 -3
- package/dist/plugins/typescript/features/utils.d.ts +2 -0
- package/dist/plugins/typescript/features/utils.js +19 -3
- package/dist/plugins/typescript/language-service.js +23 -6
- package/dist/plugins/typescript/module-loader.js +1 -1
- package/dist/plugins/typescript/previewer.js +1 -1
- package/dist/plugins/typescript/snapshots/DocumentSnapshot.d.ts +22 -2
- package/dist/plugins/typescript/snapshots/DocumentSnapshot.js +48 -1
- package/dist/plugins/typescript/snapshots/SnapshotManager.js +2 -1
- package/dist/plugins/typescript/snapshots/utils.js +3 -6
- package/dist/plugins/typescript/utils.d.ts +12 -1
- package/dist/plugins/typescript/utils.js +29 -1
- package/dist/server.js +43 -14
- package/dist/utils.d.ts +4 -0
- package/dist/utils.js +16 -3
- package/package.json +2 -2
|
@@ -13,32 +13,59 @@ class DiagnosticsProviderImpl {
|
|
|
13
13
|
this.languageServiceManager = languageServiceManager;
|
|
14
14
|
}
|
|
15
15
|
async getDiagnostics(document, _cancellationToken) {
|
|
16
|
-
var _a, _b;
|
|
17
16
|
// Don't return diagnostics for files inside node_modules. These are considered read-only
|
|
18
17
|
// and they would pollute the output for astro check
|
|
19
|
-
if (
|
|
18
|
+
if (document.getFilePath()?.includes('/node_modules/') || document.getFilePath()?.includes('\\node_modules\\')) {
|
|
20
19
|
return [];
|
|
21
20
|
}
|
|
22
21
|
const { lang, tsDoc } = await this.languageServiceManager.getLSAndTSDoc(document);
|
|
23
22
|
const filePath = (0, utils_1.toVirtualAstroFilePath)(tsDoc.filePath);
|
|
23
|
+
const fragment = await tsDoc.createFragment();
|
|
24
|
+
let scriptDiagnostics = [];
|
|
25
|
+
document.scriptTags.forEach((scriptTag) => {
|
|
26
|
+
const { filePath: scriptFilePath, snapshot: scriptTagSnapshot } = (0, utils_1.getScriptTagSnapshot)(tsDoc, document, scriptTag.container);
|
|
27
|
+
const scriptDiagnostic = [
|
|
28
|
+
...lang.getSyntacticDiagnostics(scriptFilePath),
|
|
29
|
+
...lang.getSuggestionDiagnostics(scriptFilePath),
|
|
30
|
+
...lang.getSemanticDiagnostics(scriptFilePath),
|
|
31
|
+
]
|
|
32
|
+
// We need to duplicate the diagnostic creation here because we can't map TS's diagnostics range to the original
|
|
33
|
+
// file due to some internal cache inside TS that would cause it to being mapped twice in some cases
|
|
34
|
+
.map((diagnostic) => ({
|
|
35
|
+
range: (0, utils_1.convertRange)(scriptTagSnapshot, diagnostic),
|
|
36
|
+
severity: (0, utils_1.mapSeverity)(diagnostic.category),
|
|
37
|
+
source: 'ts',
|
|
38
|
+
message: typescript_1.default.flattenDiagnosticMessageText(diagnostic.messageText, '\n'),
|
|
39
|
+
code: diagnostic.code,
|
|
40
|
+
tags: getDiagnosticTag(diagnostic),
|
|
41
|
+
}))
|
|
42
|
+
.map(mapRange(scriptTagSnapshot, document));
|
|
43
|
+
scriptDiagnostics.push(...scriptDiagnostic);
|
|
44
|
+
});
|
|
24
45
|
const { script: scriptBoundaries } = this.getTagBoundaries(lang, filePath);
|
|
25
46
|
const syntaxDiagnostics = lang.getSyntacticDiagnostics(filePath);
|
|
26
47
|
const suggestionDiagnostics = lang.getSuggestionDiagnostics(filePath);
|
|
27
|
-
const semanticDiagnostics = lang.getSemanticDiagnostics(filePath)
|
|
28
|
-
|
|
48
|
+
const semanticDiagnostics = lang.getSemanticDiagnostics(filePath);
|
|
49
|
+
const diagnostics = [
|
|
50
|
+
...syntaxDiagnostics,
|
|
51
|
+
...suggestionDiagnostics,
|
|
52
|
+
...semanticDiagnostics,
|
|
53
|
+
].filter((diag) => {
|
|
54
|
+
return isNoWithinBoundary(scriptBoundaries, diag);
|
|
29
55
|
});
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
56
|
+
return [
|
|
57
|
+
...diagnostics
|
|
58
|
+
.map((diagnostic) => ({
|
|
59
|
+
range: (0, utils_1.convertRange)(tsDoc, diagnostic),
|
|
60
|
+
severity: (0, utils_1.mapSeverity)(diagnostic.category),
|
|
61
|
+
source: 'ts',
|
|
62
|
+
message: typescript_1.default.flattenDiagnosticMessageText(diagnostic.messageText, '\n'),
|
|
63
|
+
code: diagnostic.code,
|
|
64
|
+
tags: getDiagnosticTag(diagnostic),
|
|
65
|
+
}))
|
|
66
|
+
.map(mapRange(fragment, document)),
|
|
67
|
+
...scriptDiagnostics,
|
|
68
|
+
]
|
|
42
69
|
.filter((diag) => {
|
|
43
70
|
return (hasNoNegativeLines(diag) &&
|
|
44
71
|
isNoJSXImplicitRuntimeWarning(diag) &&
|
|
@@ -47,13 +74,14 @@ class DiagnosticsProviderImpl {
|
|
|
47
74
|
isNoSpreadExpected(diag) &&
|
|
48
75
|
isNoCantResolveJSONModule(diag) &&
|
|
49
76
|
isNoCantReturnOutsideFunction(diag) &&
|
|
77
|
+
isNoIsolatedModuleError(diag) &&
|
|
50
78
|
isNoJsxCannotHaveMultipleAttrsError(diag));
|
|
51
79
|
})
|
|
52
80
|
.map(enhanceIfNecessary);
|
|
53
81
|
}
|
|
54
82
|
getTagBoundaries(lang, tsFilePath) {
|
|
55
83
|
const program = lang.getProgram();
|
|
56
|
-
const sourceFile = program
|
|
84
|
+
const sourceFile = program?.getSourceFile(tsFilePath);
|
|
57
85
|
const boundaries = {
|
|
58
86
|
script: [],
|
|
59
87
|
markdown: [],
|
|
@@ -147,12 +175,26 @@ function isNoCantReturnOutsideFunction(diagnostic) {
|
|
|
147
175
|
function isNoCantResolveJSONModule(diagnostic) {
|
|
148
176
|
return diagnostic.code !== 2732;
|
|
149
177
|
}
|
|
178
|
+
/**
|
|
179
|
+
* When the content of the file is invalid and can't be parsed properly for TSX generation, TS will show an error about
|
|
180
|
+
* how the current module can't be compiled under --isolatedModule, this is confusing to users so let's ignore this
|
|
181
|
+
*/
|
|
182
|
+
function isNoIsolatedModuleError(diagnostic) {
|
|
183
|
+
return diagnostic.code !== 1208;
|
|
184
|
+
}
|
|
150
185
|
/**
|
|
151
186
|
* Some diagnostics have JSX-specific nomenclature or unclear description. Enhance them for more clarity.
|
|
152
187
|
*/
|
|
153
188
|
function enhanceIfNecessary(diagnostic) {
|
|
189
|
+
// JSX element has no closing tag. JSX -> HTML
|
|
190
|
+
if (diagnostic.code === 17008) {
|
|
191
|
+
return {
|
|
192
|
+
...diagnostic,
|
|
193
|
+
message: diagnostic.message.replace('JSX', 'HTML'),
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
// For the rare case where an user might try to put a client directive on something that is not a component
|
|
154
197
|
if (diagnostic.code === 2322) {
|
|
155
|
-
// For the rare case where an user might try to put a client directive on something that is not a component
|
|
156
198
|
if (diagnostic.message.includes("Property 'client:") && diagnostic.message.includes("to type 'HTMLProps")) {
|
|
157
199
|
return {
|
|
158
200
|
...diagnostic,
|
|
@@ -11,7 +11,6 @@ class DocumentSymbolsProviderImpl {
|
|
|
11
11
|
this.languageServiceManager = languageServiceManager;
|
|
12
12
|
}
|
|
13
13
|
async getDocumentSymbols(document) {
|
|
14
|
-
var _a, _b, _c;
|
|
15
14
|
const { lang, tsDoc } = await this.languageServiceManager.getLSAndTSDoc(document);
|
|
16
15
|
const fragment = await tsDoc.createFragment();
|
|
17
16
|
const navTree = lang.getNavigationTree(tsDoc.filePath);
|
|
@@ -27,17 +26,17 @@ class DocumentSymbolsProviderImpl {
|
|
|
27
26
|
result.push(vscode_languageserver_types_1.SymbolInformation.create('Frontmatter', vscode_languageserver_types_1.SymbolKind.Namespace, vscode_languageserver_types_1.Range.create(document.positionAt(document.astroMeta.frontmatter.startOffset), document.positionAt(document.astroMeta.frontmatter.endOffset)), document.getURL()));
|
|
28
27
|
}
|
|
29
28
|
// Add a "Template" namespace for everything under the frontmatter
|
|
30
|
-
result.push(vscode_languageserver_types_1.SymbolInformation.create('Template', vscode_languageserver_types_1.SymbolKind.Namespace, vscode_languageserver_types_1.Range.create(document.positionAt(
|
|
29
|
+
result.push(vscode_languageserver_types_1.SymbolInformation.create('Template', vscode_languageserver_types_1.SymbolKind.Namespace, vscode_languageserver_types_1.Range.create(document.positionAt(document.astroMeta.frontmatter.endOffset ?? 0), document.positionAt(document.getTextLength())), document.getURL()));
|
|
31
30
|
for (let symbol of symbols.splice(1)) {
|
|
32
31
|
symbol = (0, documents_1.mapSymbolInformationToOriginal)(fragment, symbol);
|
|
33
|
-
if (document.offsetAt(symbol.location.range.end) >= (
|
|
32
|
+
if (document.offsetAt(symbol.location.range.end) >= (document.astroMeta.content.firstNonWhitespaceOffset ?? 0)) {
|
|
34
33
|
if (symbol.containerName === originalContainerName) {
|
|
35
34
|
symbol.containerName = 'Template';
|
|
36
35
|
}
|
|
37
36
|
// For some reason, it seems like TypeScript thinks that the "class" attribute is a real class, weird
|
|
38
37
|
if (symbol.kind === vscode_languageserver_types_1.SymbolKind.Class && symbol.name === '<class>') {
|
|
39
38
|
const node = document.html.findNodeAt(document.offsetAt(symbol.location.range.start));
|
|
40
|
-
if (
|
|
39
|
+
if (node.attributes?.class) {
|
|
41
40
|
continue;
|
|
42
41
|
}
|
|
43
42
|
}
|
|
@@ -15,15 +15,22 @@ class FoldingRangesProviderImpl {
|
|
|
15
15
|
const html = document.html;
|
|
16
16
|
const { lang, tsDoc } = await this.languageServiceManager.getLSAndTSDoc(document);
|
|
17
17
|
const filePath = (0, utils_1.toVirtualAstroFilePath)(tsDoc.filePath);
|
|
18
|
-
const outliningSpans = lang.getOutliningSpans(filePath)
|
|
19
|
-
const foldingRanges = [];
|
|
20
|
-
for (const span of outliningSpans) {
|
|
18
|
+
const outliningSpans = lang.getOutliningSpans(filePath).filter((span) => {
|
|
21
19
|
const node = html.findNodeAt(span.textSpan.start);
|
|
22
20
|
// Due to how our TSX output transform those tags into function calls or template literals
|
|
23
21
|
// TypeScript thinks of those as outlining spans, which is fine but we don't want folding ranges for those
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
return node.tag !== 'script' && node.tag !== 'style' && node.tag !== 'Markdown';
|
|
23
|
+
});
|
|
24
|
+
const scriptOutliningSpans = [];
|
|
25
|
+
document.scriptTags.forEach((scriptTag) => {
|
|
26
|
+
const { snapshot: scriptTagSnapshot, filePath: scriptFilePath } = (0, utils_1.getScriptTagSnapshot)(tsDoc, document, scriptTag.container);
|
|
27
|
+
scriptOutliningSpans.push(...lang.getOutliningSpans(scriptFilePath).map((span) => {
|
|
28
|
+
span.textSpan.start = document.offsetAt(scriptTagSnapshot.getOriginalPosition(scriptTagSnapshot.positionAt(span.textSpan.start)));
|
|
29
|
+
return span;
|
|
30
|
+
}));
|
|
31
|
+
});
|
|
32
|
+
const foldingRanges = [];
|
|
33
|
+
for (const span of [...outliningSpans, ...scriptOutliningSpans]) {
|
|
27
34
|
const start = document.positionAt(span.textSpan.start);
|
|
28
35
|
const end = adjustFoldingEnd(start, document.positionAt(span.textSpan.start + span.textSpan.length), document);
|
|
29
36
|
// When using this method for generating folding ranges, TypeScript tend to return some
|
|
@@ -18,7 +18,20 @@ class HoverProviderImpl {
|
|
|
18
18
|
const fragment = await tsDoc.createFragment();
|
|
19
19
|
const offset = fragment.offsetAt(fragment.getGeneratedPosition(position));
|
|
20
20
|
const filePath = (0, utils_1.toVirtualAstroFilePath)(tsDoc.filePath);
|
|
21
|
-
|
|
21
|
+
const html = document.html;
|
|
22
|
+
const documentOffset = document.offsetAt(position);
|
|
23
|
+
const node = html.findNodeAt(documentOffset);
|
|
24
|
+
let info;
|
|
25
|
+
if (node.tag === 'script') {
|
|
26
|
+
const { snapshot: scriptTagSnapshot, filePath: scriptFilePath, offset: scriptOffset, } = (0, utils_1.getScriptTagSnapshot)(tsDoc, document, node, position);
|
|
27
|
+
info = lang.getQuickInfoAtPosition(scriptFilePath, scriptOffset);
|
|
28
|
+
if (info) {
|
|
29
|
+
info.textSpan.start = fragment.offsetAt(scriptTagSnapshot.getOriginalPosition(scriptTagSnapshot.positionAt(info.textSpan.start)));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
info = lang.getQuickInfoAtPosition(filePath, offset);
|
|
34
|
+
}
|
|
22
35
|
if (!info) {
|
|
23
36
|
return null;
|
|
24
37
|
}
|
|
@@ -15,7 +15,7 @@ class SemanticTokensProviderImpl {
|
|
|
15
15
|
async getSemanticTokens(document, range, cancellationToken) {
|
|
16
16
|
const { lang, tsDoc } = await this.languageServiceManager.getLSAndTSDoc(document);
|
|
17
17
|
const fragment = (await tsDoc.createFragment());
|
|
18
|
-
if (cancellationToken
|
|
18
|
+
if (cancellationToken?.isCancellationRequested) {
|
|
19
19
|
return null;
|
|
20
20
|
}
|
|
21
21
|
const filePath = (0, utils_1.toVirtualAstroFilePath)(tsDoc.filePath);
|
|
@@ -15,13 +15,21 @@ class SignatureHelpProviderImpl {
|
|
|
15
15
|
async getSignatureHelp(document, position, context, cancellationToken) {
|
|
16
16
|
const { lang, tsDoc } = await this.languageServiceManager.getLSAndTSDoc(document);
|
|
17
17
|
const fragment = await tsDoc.createFragment();
|
|
18
|
-
if (cancellationToken
|
|
18
|
+
if (cancellationToken?.isCancellationRequested) {
|
|
19
19
|
return null;
|
|
20
20
|
}
|
|
21
21
|
const filePath = (0, utils_1.toVirtualAstroFilePath)(tsDoc.filePath);
|
|
22
22
|
const offset = fragment.offsetAt(fragment.getGeneratedPosition(position));
|
|
23
|
+
const node = document.html.findNodeAt(offset);
|
|
24
|
+
let info;
|
|
23
25
|
const triggerReason = this.toTsTriggerReason(context);
|
|
24
|
-
|
|
26
|
+
if (node.tag === 'script') {
|
|
27
|
+
const { filePath: scriptFilePath, offset: scriptOffset } = (0, utils_1.getScriptTagSnapshot)(tsDoc, document, node, position);
|
|
28
|
+
info = lang.getSignatureHelpItems(scriptFilePath, scriptOffset, triggerReason ? { triggerReason } : undefined);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
info = lang.getSignatureHelpItems(filePath, offset, triggerReason ? { triggerReason } : undefined);
|
|
32
|
+
}
|
|
25
33
|
if (!info) {
|
|
26
34
|
return null;
|
|
27
35
|
}
|
|
@@ -44,7 +52,7 @@ class SignatureHelpProviderImpl {
|
|
|
44
52
|
* adopted from https://github.com/microsoft/vscode/blob/265a2f6424dfbd3a9788652c7d376a7991d049a3/extensions/typescript-language-features/src/languageFeatures/signatureHelp.ts#L103
|
|
45
53
|
*/
|
|
46
54
|
toTsTriggerReason(context) {
|
|
47
|
-
switch (context
|
|
55
|
+
switch (context?.triggerKind) {
|
|
48
56
|
case vscode_languageserver_1.SignatureHelpTriggerKind.TriggerCharacter:
|
|
49
57
|
if (context.triggerCharacter) {
|
|
50
58
|
if (this.isReTrigger(context.isRetrigger, context.triggerCharacter)) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { SnapshotFragment, DocumentSnapshot } from '../snapshots/DocumentSnapshot';
|
|
2
2
|
import type { LanguageServiceManager } from '../LanguageServiceManager';
|
|
3
3
|
import { Position } from 'vscode-languageserver';
|
|
4
|
+
import ts from 'typescript';
|
|
4
5
|
export declare function isPartOfImportStatement(text: string, position: Position): boolean;
|
|
5
6
|
export declare class SnapshotFragmentMap {
|
|
6
7
|
private languageServiceManager;
|
|
@@ -21,3 +22,4 @@ export declare class SnapshotFragmentMap {
|
|
|
21
22
|
}>;
|
|
22
23
|
retrieveFragment(fileName: string): Promise<SnapshotFragment>;
|
|
23
24
|
}
|
|
25
|
+
export declare function findContainingNode<T extends ts.Node>(node: ts.Node, textSpan: ts.TextSpan, predicate: (node: ts.Node) => node is T): T | undefined;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SnapshotFragmentMap = exports.isPartOfImportStatement = void 0;
|
|
3
|
+
exports.findContainingNode = exports.SnapshotFragmentMap = exports.isPartOfImportStatement = void 0;
|
|
4
4
|
const documents_1 = require("../../../core/documents");
|
|
5
5
|
function isPartOfImportStatement(text, position) {
|
|
6
6
|
const line = (0, documents_1.getLineAtPosition)(position, text);
|
|
@@ -19,8 +19,7 @@ class SnapshotFragmentMap {
|
|
|
19
19
|
return this.map.get(fileName);
|
|
20
20
|
}
|
|
21
21
|
getFragment(fileName) {
|
|
22
|
-
|
|
23
|
-
return (_a = this.map.get(fileName)) === null || _a === void 0 ? void 0 : _a.fragment;
|
|
22
|
+
return this.map.get(fileName)?.fragment;
|
|
24
23
|
}
|
|
25
24
|
async retrieve(fileName) {
|
|
26
25
|
let snapshotFragment = this.get(fileName);
|
|
@@ -37,3 +36,20 @@ class SnapshotFragmentMap {
|
|
|
37
36
|
}
|
|
38
37
|
}
|
|
39
38
|
exports.SnapshotFragmentMap = SnapshotFragmentMap;
|
|
39
|
+
function findContainingNode(node, textSpan, predicate) {
|
|
40
|
+
const children = node.getChildren();
|
|
41
|
+
const end = textSpan.start + textSpan.length;
|
|
42
|
+
for (const child of children) {
|
|
43
|
+
if (!(child.getStart() <= textSpan.start && child.getEnd() >= end)) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (predicate(child)) {
|
|
47
|
+
return child;
|
|
48
|
+
}
|
|
49
|
+
const foundInChildren = findContainingNode(child, textSpan, predicate);
|
|
50
|
+
if (foundInChildren) {
|
|
51
|
+
return foundInChildren;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
exports.findContainingNode = findContainingNode;
|
|
@@ -33,6 +33,7 @@ const utils_1 = require("../../utils");
|
|
|
33
33
|
const module_loader_1 = require("./module-loader");
|
|
34
34
|
const SnapshotManager_1 = require("./snapshots/SnapshotManager");
|
|
35
35
|
const utils_2 = require("./utils");
|
|
36
|
+
const DocumentSnapshot_1 = require("./snapshots/DocumentSnapshot");
|
|
36
37
|
const DocumentSnapshotUtils = __importStar(require("./snapshots/utils"));
|
|
37
38
|
const services = new Map();
|
|
38
39
|
async function getLanguageService(path, workspaceUris, docContext) {
|
|
@@ -132,7 +133,7 @@ async function createLanguageService(tsconfigPath, docContext, workspaceUris) {
|
|
|
132
133
|
function updateSnapshotFromDocument(document) {
|
|
133
134
|
const filePath = document.getFilePath() || '';
|
|
134
135
|
const prevSnapshot = snapshotManager.get(filePath);
|
|
135
|
-
if (
|
|
136
|
+
if (prevSnapshot?.version === document.version) {
|
|
136
137
|
return prevSnapshot;
|
|
137
138
|
}
|
|
138
139
|
if (!prevSnapshot) {
|
|
@@ -140,6 +141,12 @@ async function createLanguageService(tsconfigPath, docContext, workspaceUris) {
|
|
|
140
141
|
}
|
|
141
142
|
const newSnapshot = DocumentSnapshotUtils.createFromDocument(document);
|
|
142
143
|
snapshotManager.set(filePath, newSnapshot);
|
|
144
|
+
document.scriptTags.forEach((scriptTag, index) => {
|
|
145
|
+
const scriptFilePath = filePath + `.__script${index}.js`;
|
|
146
|
+
const scriptSnapshot = new DocumentSnapshot_1.ScriptTagDocumentSnapshot(scriptTag, document, scriptFilePath);
|
|
147
|
+
snapshotManager.set(scriptFilePath, scriptSnapshot);
|
|
148
|
+
newSnapshot.scriptTagSnapshots?.push(scriptSnapshot);
|
|
149
|
+
});
|
|
143
150
|
if (prevSnapshot && prevSnapshot.scriptKind !== newSnapshot.scriptKind) {
|
|
144
151
|
// Restart language service as it doesn't handle script kind changes.
|
|
145
152
|
languageService.dispose();
|
|
@@ -166,6 +173,16 @@ async function createLanguageService(tsconfigPath, docContext, workspaceUris) {
|
|
|
166
173
|
astroModuleLoader.deleteUnresolvedResolutionsFromCache(fileName);
|
|
167
174
|
doc = DocumentSnapshotUtils.createFromFilePath(fileName, docContext.createDocument);
|
|
168
175
|
snapshotManager.set(fileName, doc);
|
|
176
|
+
// If we needed to create an Astro snapshot, also create its script tags snapshots
|
|
177
|
+
if ((0, utils_2.isAstroFilePath)(fileName)) {
|
|
178
|
+
const document = doc.parent;
|
|
179
|
+
document.scriptTags.forEach((scriptTag, index) => {
|
|
180
|
+
const scriptFilePath = fileName + `.__script${index}.js`;
|
|
181
|
+
const scriptSnapshot = new DocumentSnapshot_1.ScriptTagDocumentSnapshot(scriptTag, document, scriptFilePath);
|
|
182
|
+
snapshotManager.set(scriptFilePath, scriptSnapshot);
|
|
183
|
+
doc.scriptTagSnapshots?.push(scriptSnapshot);
|
|
184
|
+
});
|
|
185
|
+
}
|
|
169
186
|
return doc;
|
|
170
187
|
}
|
|
171
188
|
function updateProjectFiles() {
|
|
@@ -186,16 +203,15 @@ async function createLanguageService(tsconfigPath, docContext, workspaceUris) {
|
|
|
186
203
|
snapshotManager.updateNonAstroFile(fileName, changes);
|
|
187
204
|
}
|
|
188
205
|
function getParsedTSConfig() {
|
|
189
|
-
var _a, _b, _c, _d;
|
|
190
206
|
let configJson = (tsconfigPath && typescript_1.default.readConfigFile(tsconfigPath, typescript_1.default.sys.readFile).config) || {};
|
|
191
207
|
// If our user has types in their config but it doesn't include the types needed for Astro, add them to the config
|
|
192
|
-
if (
|
|
193
|
-
if (!
|
|
208
|
+
if (configJson.compilerOptions?.types) {
|
|
209
|
+
if (!configJson.compilerOptions?.types.includes('astro/env')) {
|
|
194
210
|
configJson.compilerOptions.types.push('astro/env');
|
|
195
211
|
}
|
|
196
212
|
if (astroVersion.major >= 1 &&
|
|
197
213
|
astroVersion.full !== '1.0.0-beta.0' &&
|
|
198
|
-
!
|
|
214
|
+
!configJson.compilerOptions?.types.includes('astro/astro-jsx')) {
|
|
199
215
|
configJson.compilerOptions.types.push('astro/astro-jsx');
|
|
200
216
|
}
|
|
201
217
|
}
|
|
@@ -203,7 +219,7 @@ async function createLanguageService(tsconfigPath, docContext, workspaceUris) {
|
|
|
203
219
|
// Delete include so that .astro files don't get mistakenly excluded by the user
|
|
204
220
|
delete configJson.include;
|
|
205
221
|
// If the user supplied exclude, let's use theirs otherwise, use ours
|
|
206
|
-
|
|
222
|
+
configJson.exclude ?? (configJson.exclude = getDefaultExclude());
|
|
207
223
|
// Everything here will always, unconditionally, be in the resulting config
|
|
208
224
|
const forcedCompilerOptions = {
|
|
209
225
|
noEmit: true,
|
|
@@ -215,6 +231,7 @@ async function createLanguageService(tsconfigPath, docContext, workspaceUris) {
|
|
|
215
231
|
jsxFactory: 'astroHTML',
|
|
216
232
|
module: typescript_1.default.ModuleKind.ESNext,
|
|
217
233
|
target: typescript_1.default.ScriptTarget.ESNext,
|
|
234
|
+
isolatedModules: true,
|
|
218
235
|
moduleResolution: typescript_1.default.ModuleResolutionKind.NodeJs,
|
|
219
236
|
};
|
|
220
237
|
const project = typescript_1.default.parseJsonConfigFileContent(configJson, typescript_1.default.sys, tsconfigRoot, forcedCompilerOptions, tsconfigPath, undefined, [
|
|
@@ -39,7 +39,7 @@ class ModuleResolutionCache {
|
|
|
39
39
|
*/
|
|
40
40
|
delete(resolvedModuleName) {
|
|
41
41
|
this.cache.forEach((val, key) => {
|
|
42
|
-
if (
|
|
42
|
+
if (val?.resolvedFileName === resolvedModuleName) {
|
|
43
43
|
this.cache.delete(key);
|
|
44
44
|
}
|
|
45
45
|
});
|
|
@@ -72,7 +72,7 @@ function getTagBodyText(tag) {
|
|
|
72
72
|
function getTagDocumentation(tag) {
|
|
73
73
|
function getWithType() {
|
|
74
74
|
const body = (typescript_1.default.displayPartsToString(tag.text) || '').split(/^(\S+)\s*-?\s*/);
|
|
75
|
-
if (
|
|
75
|
+
if (body?.length === 3) {
|
|
76
76
|
const param = body[1];
|
|
77
77
|
const doc = body[2];
|
|
78
78
|
const label = `*@${tag.name}* \`${param}\``;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import ts from 'typescript';
|
|
2
2
|
import { Position, TextDocumentContentChangeEvent } from 'vscode-languageserver';
|
|
3
|
-
import { AstroDocument, DocumentMapper, IdentityMapper } from '../../../core/documents';
|
|
3
|
+
import { AstroDocument, DocumentMapper, IdentityMapper, FragmentMapper, TagInformation } from '../../../core/documents';
|
|
4
4
|
import { FrameworkExt } from '../utils';
|
|
5
5
|
export interface DocumentSnapshot extends ts.IScriptSnapshot {
|
|
6
6
|
version: number;
|
|
@@ -36,11 +36,12 @@ export interface SnapshotFragment extends DocumentMapper {
|
|
|
36
36
|
* Snapshots used for Astro files
|
|
37
37
|
*/
|
|
38
38
|
export declare class AstroSnapshot implements DocumentSnapshot {
|
|
39
|
-
|
|
39
|
+
readonly parent: AstroDocument;
|
|
40
40
|
private readonly text;
|
|
41
41
|
readonly scriptKind: ts.ScriptKind;
|
|
42
42
|
private fragment?;
|
|
43
43
|
version: number;
|
|
44
|
+
scriptTagSnapshots: ScriptTagDocumentSnapshot[];
|
|
44
45
|
constructor(parent: AstroDocument, text: string, scriptKind: ts.ScriptKind);
|
|
45
46
|
createFragment(): Promise<AstroSnapshotFragment>;
|
|
46
47
|
destroyFragment(): null;
|
|
@@ -65,6 +66,25 @@ export declare class AstroSnapshotFragment implements SnapshotFragment {
|
|
|
65
66
|
isInGenerated(pos: Position): boolean;
|
|
66
67
|
getURL(): string;
|
|
67
68
|
}
|
|
69
|
+
export declare class ScriptTagDocumentSnapshot extends FragmentMapper implements DocumentSnapshot, SnapshotFragment {
|
|
70
|
+
scriptTag: TagInformation;
|
|
71
|
+
private readonly parent;
|
|
72
|
+
filePath: string;
|
|
73
|
+
readonly version: number;
|
|
74
|
+
private text;
|
|
75
|
+
scriptKind: ts.ScriptKind;
|
|
76
|
+
private lineOffsets?;
|
|
77
|
+
constructor(scriptTag: TagInformation, parent: AstroDocument, filePath: string);
|
|
78
|
+
positionAt(offset: number): Position;
|
|
79
|
+
offsetAt(position: Position): number;
|
|
80
|
+
createFragment(): Promise<SnapshotFragment>;
|
|
81
|
+
destroyFragment(): void;
|
|
82
|
+
getText(start: number, end: number): string;
|
|
83
|
+
getLength(): number;
|
|
84
|
+
getFullText(): string;
|
|
85
|
+
getChangeRange(): undefined;
|
|
86
|
+
private getLineOffsets;
|
|
87
|
+
}
|
|
68
88
|
/**
|
|
69
89
|
* Snapshot used for anything that is not an Astro file
|
|
70
90
|
* It's both used for .js(x)/.ts(x) files and .svelte/.vue files
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.TypeScriptDocumentSnapshot = exports.AstroSnapshotFragment = exports.AstroSnapshot = void 0;
|
|
6
|
+
exports.TypeScriptDocumentSnapshot = exports.ScriptTagDocumentSnapshot = exports.AstroSnapshotFragment = exports.AstroSnapshot = void 0;
|
|
7
|
+
const typescript_1 = __importDefault(require("typescript"));
|
|
4
8
|
const documents_1 = require("../../../core/documents");
|
|
5
9
|
const utils_1 = require("../../../utils");
|
|
6
10
|
const utils_2 = require("../utils");
|
|
@@ -13,6 +17,7 @@ class AstroSnapshot {
|
|
|
13
17
|
this.text = text;
|
|
14
18
|
this.scriptKind = scriptKind;
|
|
15
19
|
this.version = this.parent.version;
|
|
20
|
+
this.scriptTagSnapshots = [];
|
|
16
21
|
}
|
|
17
22
|
async createFragment() {
|
|
18
23
|
if (!this.fragment) {
|
|
@@ -72,6 +77,48 @@ class AstroSnapshotFragment {
|
|
|
72
77
|
}
|
|
73
78
|
}
|
|
74
79
|
exports.AstroSnapshotFragment = AstroSnapshotFragment;
|
|
80
|
+
class ScriptTagDocumentSnapshot extends documents_1.FragmentMapper {
|
|
81
|
+
constructor(scriptTag, parent, filePath) {
|
|
82
|
+
super(parent.getText(), scriptTag, filePath);
|
|
83
|
+
this.scriptTag = scriptTag;
|
|
84
|
+
this.parent = parent;
|
|
85
|
+
this.filePath = filePath;
|
|
86
|
+
this.version = this.parent.version;
|
|
87
|
+
this.text = this.parent.getText().slice(this.scriptTag.start, this.scriptTag.end) + '\nexport {}';
|
|
88
|
+
this.scriptKind = typescript_1.default.ScriptKind.JS;
|
|
89
|
+
}
|
|
90
|
+
positionAt(offset) {
|
|
91
|
+
return (0, documents_1.positionAt)(offset, this.text, this.getLineOffsets());
|
|
92
|
+
}
|
|
93
|
+
offsetAt(position) {
|
|
94
|
+
return (0, documents_1.offsetAt)(position, this.text, this.getLineOffsets());
|
|
95
|
+
}
|
|
96
|
+
async createFragment() {
|
|
97
|
+
return this;
|
|
98
|
+
}
|
|
99
|
+
destroyFragment() {
|
|
100
|
+
//
|
|
101
|
+
}
|
|
102
|
+
getText(start, end) {
|
|
103
|
+
return this.text.substring(start, end);
|
|
104
|
+
}
|
|
105
|
+
getLength() {
|
|
106
|
+
return this.text.length;
|
|
107
|
+
}
|
|
108
|
+
getFullText() {
|
|
109
|
+
return this.text;
|
|
110
|
+
}
|
|
111
|
+
getChangeRange() {
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
114
|
+
getLineOffsets() {
|
|
115
|
+
if (!this.lineOffsets) {
|
|
116
|
+
this.lineOffsets = (0, documents_1.getLineOffsets)(this.text);
|
|
117
|
+
}
|
|
118
|
+
return this.lineOffsets;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
exports.ScriptTagDocumentSnapshot = ScriptTagDocumentSnapshot;
|
|
75
122
|
/**
|
|
76
123
|
* Snapshot used for anything that is not an Astro file
|
|
77
124
|
* It's both used for .js(x)/.ts(x) files and .svelte/.vue files
|
|
@@ -129,7 +129,7 @@ class SnapshotManager {
|
|
|
129
129
|
const { include, exclude } = this.fileSpec;
|
|
130
130
|
// Since we default to not include anything,
|
|
131
131
|
// just don't waste time on this
|
|
132
|
-
if (
|
|
132
|
+
if (include?.length === 0) {
|
|
133
133
|
return;
|
|
134
134
|
}
|
|
135
135
|
const projectFiles = typescript_1.default.sys
|
|
@@ -187,6 +187,7 @@ class SnapshotManager {
|
|
|
187
187
|
const projectFiles = this.getProjectFileNames();
|
|
188
188
|
let allFiles = Array.from(new Set([...projectFiles, ...this.getFileNames()]));
|
|
189
189
|
allFiles = allFiles.map((file) => (0, utils_2.ensureRealFilePath)(file));
|
|
190
|
+
// eslint-disable-next-line no-console
|
|
190
191
|
console.log('SnapshotManager File Statistics:\n' +
|
|
191
192
|
`Project files: ${projectFiles.length}\n` +
|
|
192
193
|
`Astro files: ${allFiles.filter((name) => name.endsWith('.astro')).length}\n` +
|
|
@@ -55,8 +55,7 @@ exports.createFromNonAstroFilePath = createFromNonAstroFilePath;
|
|
|
55
55
|
* @param options options that apply in case it's a svelte file
|
|
56
56
|
*/
|
|
57
57
|
function createFromTSFilePath(filePath) {
|
|
58
|
-
|
|
59
|
-
const originalText = (_a = typescript_1.default.sys.readFile(filePath)) !== null && _a !== void 0 ? _a : '';
|
|
58
|
+
const originalText = typescript_1.default.sys.readFile(filePath) ?? '';
|
|
60
59
|
return new DocumentSnapshot_1.TypeScriptDocumentSnapshot(0, filePath, originalText);
|
|
61
60
|
}
|
|
62
61
|
exports.createFromTSFilePath = createFromTSFilePath;
|
|
@@ -66,15 +65,13 @@ exports.createFromTSFilePath = createFromTSFilePath;
|
|
|
66
65
|
* @param createDocument function that is used to create a document
|
|
67
66
|
*/
|
|
68
67
|
function createFromAstroFilePath(filePath, createDocument) {
|
|
69
|
-
|
|
70
|
-
const originalText = (_a = typescript_1.default.sys.readFile(filePath)) !== null && _a !== void 0 ? _a : '';
|
|
68
|
+
const originalText = typescript_1.default.sys.readFile(filePath) ?? '';
|
|
71
69
|
return createFromDocument(createDocument(filePath, originalText));
|
|
72
70
|
}
|
|
73
71
|
exports.createFromAstroFilePath = createFromAstroFilePath;
|
|
74
72
|
function createFromFrameworkFilePath(filePath, framework) {
|
|
75
|
-
var _a;
|
|
76
73
|
const className = classNameFromFilename(filePath);
|
|
77
|
-
const originalText =
|
|
74
|
+
const originalText = typescript_1.default.sys.readFile(filePath) ?? '';
|
|
78
75
|
let code = '';
|
|
79
76
|
if (framework === 'svelte') {
|
|
80
77
|
code = (0, svelte_language_integration_1.toTSX)(originalText, className);
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import ts from 'typescript';
|
|
2
2
|
import { CompletionItemKind, DiagnosticSeverity, Position, Range, SymbolKind, SemanticTokensLegend } from 'vscode-languageserver';
|
|
3
3
|
import { AstroDocument } from '../../core/documents';
|
|
4
|
-
import { SnapshotFragment } from './snapshots/DocumentSnapshot';
|
|
4
|
+
import { AstroSnapshot, ScriptTagDocumentSnapshot, SnapshotFragment } from './snapshots/DocumentSnapshot';
|
|
5
|
+
import { Node } from 'vscode-html-languageservice';
|
|
5
6
|
export declare const enum TokenType {
|
|
6
7
|
class = 0,
|
|
7
8
|
enum = 1,
|
|
@@ -41,6 +42,7 @@ export declare function convertRange(document: {
|
|
|
41
42
|
}): Range;
|
|
42
43
|
export declare function convertToLocationRange(defDoc: SnapshotFragment, textSpan: ts.TextSpan): Range;
|
|
43
44
|
export declare function ensureFrontmatterInsert(resultRange: Range, document: AstroDocument): Range;
|
|
45
|
+
export declare function checkEndOfFileCodeInsert(resultRange: Range, document: AstroDocument): Range;
|
|
44
46
|
export declare function removeAstroComponentSuffix(name: string): string;
|
|
45
47
|
export declare type FrameworkExt = 'astro' | 'vue' | 'jsx' | 'tsx' | 'svelte';
|
|
46
48
|
declare type FrameworkVirtualExt = 'ts' | 'tsx';
|
|
@@ -57,4 +59,13 @@ export declare function toVirtualFilePath(filePath: string): string;
|
|
|
57
59
|
export declare function toRealAstroFilePath(filePath: string): string;
|
|
58
60
|
export declare function ensureRealAstroFilePath(filePath: string): string;
|
|
59
61
|
export declare function ensureRealFilePath(filePath: string): string;
|
|
62
|
+
export declare function getScriptTagSnapshot(snapshot: AstroSnapshot, document: AstroDocument, tagInfo: Node | {
|
|
63
|
+
start: number;
|
|
64
|
+
end: number;
|
|
65
|
+
}, position?: Position): {
|
|
66
|
+
snapshot: ScriptTagDocumentSnapshot;
|
|
67
|
+
filePath: string;
|
|
68
|
+
index: number;
|
|
69
|
+
offset: number;
|
|
70
|
+
};
|
|
60
71
|
export {};
|