@astrojs/language-server 0.21.0 → 0.23.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 CHANGED
@@ -1,5 +1,33 @@
1
1
  # @astrojs/language-server
2
2
 
3
+ ## 0.23.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 1dcef68: Automatically type `Astro.props` using the Props interface when available
8
+
9
+ ### Patch Changes
10
+
11
+ - b6c95f2: Fix completions for HTML attributes not working anymore since 0.20.3
12
+
13
+ ## 0.22.0
14
+
15
+ ### Minor Changes
16
+
17
+ - d5aafc0: Formatting is now powered by Prettier and our Prettier plugin. Going forward, this should result in a more stable and complete way of formatting Astro files
18
+
19
+ ### Patch Changes
20
+
21
+ - 61620f1: Add support for Go To Type Definition
22
+ - 9337f00: Fix language server not working when no initlizationOptions were passed
23
+
24
+ ## 0.21.1
25
+
26
+ ### Patch Changes
27
+
28
+ - 0e9d7d0: Improve error handling in cases where we can't load types from the user's project and when the project isn't at the root of the folder
29
+ - 3f79dbf: Fix `tsconfig.json` not loading properly in certain contexts on Windows
30
+
3
31
  ## 0.21.0
4
32
 
5
33
  ### Minor Changes
package/README.md CHANGED
@@ -11,6 +11,5 @@ The Astro language server, implement the [language server protocol](https://micr
11
11
  │ ├── core # Core code such as .astro file parsing, configuration manager, document definition etc
12
12
  │ └── plugins # Modules for the different languages supported in .astro files
13
13
  ├── test # Tests
14
- ├── types # Types
15
- └── astro.d.ts # Types injected into .astro files by the language server
14
+ └── types # Types injected into Astro files by the language server under certain conditions
16
15
  ```
package/dist/check.js CHANGED
@@ -4,6 +4,8 @@ exports.AstroCheck = exports.DiagnosticSeverity = void 0;
4
4
  const config_1 = require("./core/config");
5
5
  const documents_1 = require("./core/documents");
6
6
  const plugins_1 = require("./plugins");
7
+ const LanguageServiceManager_1 = require("./plugins/typescript/LanguageServiceManager");
8
+ const utils_1 = require("./utils");
7
9
  var vscode_languageserver_types_1 = require("vscode-languageserver-types");
8
10
  Object.defineProperty(exports, "DiagnosticSeverity", { enumerable: true, get: function () { return vscode_languageserver_types_1.DiagnosticSeverity; } });
9
11
  class AstroCheck {
@@ -37,7 +39,8 @@ class AstroCheck {
37
39
  }));
38
40
  }
39
41
  initialize(workspacePath) {
40
- this.pluginHost.registerPlugin(new plugins_1.TypeScriptPlugin(this.docManager, this.configManager, [workspacePath]));
42
+ const languageServiceManager = new LanguageServiceManager_1.LanguageServiceManager(this.docManager, [(0, utils_1.normalizeUri)(workspacePath)], this.configManager);
43
+ this.pluginHost.registerPlugin(new plugins_1.TypeScriptPlugin(this.configManager, languageServiceManager));
41
44
  }
42
45
  async getDiagnosticsForFile(uri) {
43
46
  const diagnostics = await this.pluginHost.getDiagnostics({ uri });
@@ -1,5 +1,5 @@
1
1
  import { VSCodeEmmetConfig } from '@vscode/emmet-helper';
2
- import { LSConfig, LSCSSConfig, LSFormatConfig, LSHTMLConfig, LSTypescriptConfig } from './interfaces';
2
+ import { LSConfig, LSCSSConfig, LSHTMLConfig, LSTypescriptConfig } from './interfaces';
3
3
  import { Connection, FormattingOptions } from 'vscode-languageserver';
4
4
  import { TextDocument } from 'vscode-languageserver-textdocument';
5
5
  import { FormatCodeSettings, InlayHintsOptions, UserPreferences } from 'typescript';
@@ -23,7 +23,6 @@ export declare class ConfigManager {
23
23
  removeDocument(scopeUri: string): void;
24
24
  getConfig<T>(section: string, scopeUri: string): Promise<T | Record<string, any>>;
25
25
  getEmmetConfig(document: TextDocument): Promise<VSCodeEmmetConfig>;
26
- getAstroFormatConfig(document: TextDocument): Promise<LSFormatConfig>;
27
26
  getTSFormatConfig(document: TextDocument, vscodeOptions?: FormattingOptions): Promise<FormatCodeSettings>;
28
27
  getTSPreferences(document: TextDocument): Promise<UserPreferences>;
29
28
  getTSInlayHintsPreferences(document: TextDocument): Promise<InlayHintsOptions>;
@@ -90,13 +90,6 @@ class ConfigManager {
90
90
  showSuggestionsAsSnippets: emmetConfig.showSuggestionsAsSnippets ?? false,
91
91
  };
92
92
  }
93
- async getAstroFormatConfig(document) {
94
- const astroFormatConfig = (await this.getConfig('astro.format', document.uri)) ?? {};
95
- return {
96
- indentFrontmatter: astroFormatConfig.indentFrontmatter ?? exports.defaultLSConfig.format.indentFrontmatter,
97
- newLineAfterFrontmatter: astroFormatConfig.newLineAfterFrontmatter ?? exports.defaultLSConfig.format.newLineAfterFrontmatter,
98
- };
99
- }
100
93
  async getTSFormatConfig(document, vscodeOptions) {
101
94
  const formatConfig = (await this.getConfig('typescript.format', document.uri)) ?? {};
102
95
  return {
@@ -1,6 +1,9 @@
1
1
  import type * as svelte from '@astrojs/svelte/dist/editor.cjs';
2
2
  import type * as vue from '@astrojs/svelte/dist/editor.cjs';
3
+ import type * as prettier from 'prettier';
3
4
  export declare function setIsTrusted(_isTrusted: boolean): void;
4
- export declare function getPackagePath(packageName: string, fromPath: string): string | undefined;
5
+ export declare function getPackagePath(packageName: string, fromPath: string[]): string | undefined;
5
6
  export declare function importSvelteIntegration(fromPath: string): typeof svelte | undefined;
6
7
  export declare function importVueIntegration(fromPath: string): typeof vue | undefined;
8
+ export declare function importPrettier(fromPath: string): typeof prettier;
9
+ export declare function getPrettierPluginPath(fromPath: string): string;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.importVueIntegration = exports.importSvelteIntegration = exports.getPackagePath = exports.setIsTrusted = void 0;
3
+ exports.getPrettierPluginPath = exports.importPrettier = exports.importVueIntegration = exports.importSvelteIntegration = exports.getPackagePath = exports.setIsTrusted = void 0;
4
4
  const path_1 = require("path");
5
5
  let isTrusted = true;
6
6
  function setIsTrusted(_isTrusted) {
@@ -10,13 +10,10 @@ exports.setIsTrusted = setIsTrusted;
10
10
  function getPackagePath(packageName, fromPath) {
11
11
  const paths = [];
12
12
  if (isTrusted) {
13
- paths.unshift(fromPath);
13
+ paths.unshift(...fromPath);
14
14
  }
15
15
  try {
16
- const packageJSONPath = require.resolve(`${packageName}/package.json`, {
17
- paths,
18
- });
19
- return (0, path_1.dirname)(packageJSONPath);
16
+ return (0, path_1.dirname)(require.resolve(packageName + '/package.json', { paths }));
20
17
  }
21
18
  catch (e) {
22
19
  return undefined;
@@ -24,10 +21,16 @@ function getPackagePath(packageName, fromPath) {
24
21
  }
25
22
  exports.getPackagePath = getPackagePath;
26
23
  function importEditorIntegration(packageName, fromPath) {
27
- const pkgPath = getPackagePath(packageName, fromPath);
24
+ const pkgPath = getPackagePath(packageName, [fromPath]);
28
25
  if (pkgPath) {
29
- const main = (0, path_1.resolve)(pkgPath, 'dist', 'editor.cjs');
30
- return require(main);
26
+ try {
27
+ const main = (0, path_1.resolve)(pkgPath, 'dist', 'editor.cjs');
28
+ return require(main);
29
+ }
30
+ catch (e) {
31
+ console.error(`Couldn't load editor module from ${pkgPath}. Make sure you're using at least version v0.2.1 of the corresponding integration`);
32
+ return undefined;
33
+ }
31
34
  }
32
35
  return undefined;
33
36
  }
@@ -39,3 +42,13 @@ function importVueIntegration(fromPath) {
39
42
  return importEditorIntegration('@astrojs/vue', fromPath);
40
43
  }
41
44
  exports.importVueIntegration = importVueIntegration;
45
+ function importPrettier(fromPath) {
46
+ // This shouldn't ever fail, because we bundle Prettier in the extension itself
47
+ const prettierPkg = getPackagePath('prettier', [fromPath, __dirname]);
48
+ return require(prettierPkg);
49
+ }
50
+ exports.importPrettier = importPrettier;
51
+ function getPrettierPluginPath(fromPath) {
52
+ return getPackagePath('prettier-plugin-astro', [fromPath, __dirname]);
53
+ }
54
+ exports.getPrettierPluginPath = getPrettierPluginPath;
@@ -24,6 +24,7 @@ export declare class PluginHost {
24
24
  getSemanticTokens(textDocument: TextDocumentIdentifier, range?: Range, cancellationToken?: CancellationToken): Promise<SemanticTokens | null>;
25
25
  getLinkedEditingRanges(textDocument: TextDocumentIdentifier, position: Position): Promise<LinkedEditingRanges | null>;
26
26
  getDefinitions(textDocument: TextDocumentIdentifier, position: Position): Promise<DefinitionLink[] | Location[]>;
27
+ getTypeDefinition(textDocument: TextDocumentIdentifier, position: Position): Promise<Location[] | null>;
27
28
  rename(textDocument: TextDocumentIdentifier, position: Position, newName: string): Promise<WorkspaceEdit | null>;
28
29
  getDocumentColors(textDocument: TextDocumentIdentifier): Promise<ColorInformation[]>;
29
30
  getColorPresentations(textDocument: TextDocumentIdentifier, range: Range, color: Color): Promise<ColorPresentation[]>;
@@ -115,6 +115,10 @@ class PluginHost {
115
115
  return definitions.map((def) => ({ range: def.targetSelectionRange, uri: def.targetUri }));
116
116
  }
117
117
  }
118
+ getTypeDefinition(textDocument, position) {
119
+ const document = this.getDocument(textDocument.uri);
120
+ return this.execute('getTypeDefinitions', [document, position], ExecuteMode.FirstNonNull);
121
+ }
118
122
  async rename(textDocument, position, newName) {
119
123
  const document = this.getDocument(textDocument.uri);
120
124
  return this.execute('rename', [document, position, newName], ExecuteMode.FirstNonNull);
@@ -1,13 +1,15 @@
1
- import { CompletionContext, FoldingRange, Position } from 'vscode-languageserver';
1
+ import { CompletionContext, FoldingRange, Position, TextEdit, FormattingOptions } from 'vscode-languageserver';
2
2
  import { ConfigManager } from '../../core/config';
3
- import { AstroDocument, DocumentManager } from '../../core/documents';
3
+ import { AstroDocument } from '../../core/documents';
4
4
  import { AppCompletionList, Plugin } from '../interfaces';
5
+ import { LanguageServiceManager } from '../typescript/LanguageServiceManager';
5
6
  export declare class AstroPlugin implements Plugin {
6
7
  __name: string;
7
8
  private configManager;
8
9
  private readonly languageServiceManager;
9
10
  private readonly completionProvider;
10
- constructor(docManager: DocumentManager, configManager: ConfigManager, workspaceUris: string[]);
11
+ constructor(configManager: ConfigManager, languageServiceManager: LanguageServiceManager);
11
12
  getCompletions(document: AstroDocument, position: Position, completionContext?: CompletionContext): Promise<AppCompletionList | null>;
13
+ formatDocument(document: AstroDocument, options: FormattingOptions): Promise<TextEdit[]>;
12
14
  getFoldingRanges(document: AstroDocument): FoldingRange[];
13
15
  }
@@ -2,19 +2,51 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.AstroPlugin = void 0;
4
4
  const vscode_languageserver_1 = require("vscode-languageserver");
5
- const LanguageServiceManager_1 = require("../typescript/LanguageServiceManager");
5
+ const importPackage_1 = require("../../importPackage");
6
+ const utils_1 = require("../../utils");
6
7
  const CompletionsProvider_1 = require("./features/CompletionsProvider");
7
8
  class AstroPlugin {
8
- constructor(docManager, configManager, workspaceUris) {
9
+ constructor(configManager, languageServiceManager) {
9
10
  this.__name = 'astro';
10
11
  this.configManager = configManager;
11
- this.languageServiceManager = new LanguageServiceManager_1.LanguageServiceManager(docManager, workspaceUris, configManager);
12
+ this.languageServiceManager = languageServiceManager;
12
13
  this.completionProvider = new CompletionsProvider_1.CompletionsProviderImpl(this.languageServiceManager);
13
14
  }
14
15
  async getCompletions(document, position, completionContext) {
15
16
  const completions = this.completionProvider.getCompletions(document, position, completionContext);
16
17
  return completions;
17
18
  }
19
+ async formatDocument(document, options) {
20
+ const filePath = document.getFilePath();
21
+ if (!filePath) {
22
+ return [];
23
+ }
24
+ const prettier = (0, importPackage_1.importPrettier)(filePath);
25
+ const prettierConfig = await prettier.resolveConfig(filePath, { editorconfig: true, useCache: false });
26
+ const editorFormatConfig = options !== undefined
27
+ ? {
28
+ tabWidth: prettierConfig?.tabWidth ?? options.tabSize,
29
+ useTabs: prettierConfig?.useTabs ?? !options.insertSpaces,
30
+ }
31
+ : {};
32
+ const resultConfig = (0, utils_1.mergeDeep)(prettierConfig ?? {}, editorFormatConfig);
33
+ const fileInfo = await prettier.getFileInfo(filePath, { ignorePath: '.prettierignore' });
34
+ if (fileInfo.ignored) {
35
+ return [];
36
+ }
37
+ const result = prettier.format(document.getText(), {
38
+ ...resultConfig,
39
+ plugins: getAstroPrettierPlugin(),
40
+ parser: 'astro',
41
+ });
42
+ return document.getText() === result
43
+ ? []
44
+ : [vscode_languageserver_1.TextEdit.replace(vscode_languageserver_1.Range.create(document.positionAt(0), document.positionAt(document.getTextLength())), result)];
45
+ function getAstroPrettierPlugin() {
46
+ const hasPluginLoadedAlready = prettier.getSupportInfo().languages.some((l) => l.name === 'astro');
47
+ return hasPluginLoadedAlready ? [] : [(0, importPackage_1.getPrettierPluginPath)(filePath)];
48
+ }
49
+ }
18
50
  getFoldingRanges(document) {
19
51
  const foldingRanges = [];
20
52
  const { frontmatter } = document.astroMeta;
@@ -1,3 +1,3 @@
1
1
  import { LanguageService } from 'vscode-css-languageservice';
2
- export declare function getLanguage(kind?: string): "css" | "scss" | "less";
2
+ export declare function getLanguage(kind?: string): "css" | "less" | "scss";
3
3
  export declare function getLanguageService(kind?: string): LanguageService;
@@ -1,4 +1,4 @@
1
- import { CompletionList, Position, TextEdit, FoldingRange, Hover, SymbolInformation, FormattingOptions, LinkedEditingRanges } from 'vscode-languageserver';
1
+ import { CompletionList, Position, FoldingRange, Hover, SymbolInformation, LinkedEditingRanges } from 'vscode-languageserver';
2
2
  import type { Plugin } from '../interfaces';
3
3
  import { ConfigManager } from '../../core/config/ConfigManager';
4
4
  import { AstroDocument } from '../../core/documents/AstroDocument';
@@ -15,7 +15,6 @@ export declare class HTMLPlugin implements Plugin {
15
15
  * Get HTML completions
16
16
  */
17
17
  getCompletions(document: AstroDocument, position: Position): Promise<CompletionList | null>;
18
- formatDocument(document: AstroDocument, options: FormattingOptions): Promise<TextEdit[]>;
19
18
  getFoldingRanges(document: AstroDocument): FoldingRange[] | null;
20
19
  getLinkedEditingRanges(document: AstroDocument, position: Position): LinkedEditingRanges | null;
21
20
  doTagComplete(document: AstroDocument, position: Position): Promise<string | null>;
@@ -76,30 +76,16 @@ class HTMLPlugin {
76
76
  const inTagName = (0, utils_1.isInTagName)(html, offset);
77
77
  const results = inComponentTag && !inTagName
78
78
  ? (0, utils_2.removeDataAttrCompletion)(this.attributeOnlyLang.doComplete(document, position, html).items)
79
- : // We filter items with no documentation to prevent duplicates with our own defined script and style tags
80
- this.lang.doComplete(document, position, html).items.filter((item) => item.documentation !== undefined);
79
+ : this.lang.doComplete(document, position, html).items.filter(isNoAddedTagWithNoDocumentation);
81
80
  const langCompletions = inComponentTag ? [] : this.getLangCompletions(results);
82
81
  return vscode_languageserver_1.CompletionList.create([...results, ...langCompletions, ...emmetResults.items],
83
82
  // Emmet completions change on every keystroke, so they are never complete
84
83
  emmetResults.items.length > 0);
85
- }
86
- async formatDocument(document, options) {
87
- const start = document.positionAt(document.astroMeta.frontmatter.state === 'closed' ? document.astroMeta.frontmatter.endOffset + 3 : 0);
88
- if (document.astroMeta.frontmatter.state === 'closed') {
89
- start.line += 1;
90
- start.character = 0;
91
- }
92
- const end = document.positionAt(document.getTextLength());
93
- const htmlFormatConfig = (await this.configManager.getConfig('html.format', document.uri)) ?? {};
94
- // The HTML plugin can't format script tags properly, we'll handle those inside the TypeScript plugin
95
- if (htmlFormatConfig.contentUnformatted) {
96
- htmlFormatConfig.contentUnformatted = htmlFormatConfig.contentUnformatted + ',script';
97
- }
98
- else {
99
- htmlFormatConfig.contentUnformatted = 'script';
100
- }
101
- const edits = this.lang.format(document, vscode_languageserver_1.Range.create(start, end), { ...htmlFormatConfig, ...options });
102
- return edits;
84
+ // Filter script and style completions with no documentation to prevent duplicates
85
+ // due to our added definitions for those tags
86
+ function isNoAddedTagWithNoDocumentation(item) {
87
+ return !(['script', 'style'].includes(item.label) && item.documentation === undefined);
88
+ }
103
89
  }
104
90
  getFoldingRanges(document) {
105
91
  const html = document.html;
@@ -1,4 +1,4 @@
1
- import { CodeAction, CodeActionContext, Color, ColorInformation, ColorPresentation, CompletionContext, CompletionItem, CompletionList, DefinitionLink, Diagnostic, FileChangeType, FoldingRange, FormattingOptions, Hover, InlayHint, LinkedEditingRanges, Position, Range, ReferenceContext, SelectionRange, SemanticTokens, SignatureHelp, SignatureHelpContext, SymbolInformation, TextDocumentContentChangeEvent, TextDocumentIdentifier, TextEdit, WorkspaceEdit } from 'vscode-languageserver';
1
+ import { CodeAction, CodeActionContext, Color, Location, ColorInformation, ColorPresentation, CompletionContext, CompletionItem, CompletionList, DefinitionLink, Diagnostic, FileChangeType, FoldingRange, FormattingOptions, Hover, InlayHint, LinkedEditingRanges, Position, Range, ReferenceContext, SelectionRange, SemanticTokens, SignatureHelp, SignatureHelpContext, SymbolInformation, TextDocumentContentChangeEvent, TextDocumentIdentifier, TextEdit, WorkspaceEdit } from 'vscode-languageserver';
2
2
  import { TextDocument } from 'vscode-languageserver-textdocument';
3
3
  export declare type Resolvable<T> = T | Promise<T>;
4
4
  export interface AppCompletionItem<T extends TextDocumentIdentifier = any> extends CompletionItem {
@@ -74,6 +74,9 @@ export interface SemanticTokensProvider {
74
74
  export interface LinkedEditingRangesProvider {
75
75
  getLinkedEditingRanges(document: TextDocument, position: Position): Resolvable<LinkedEditingRanges | null>;
76
76
  }
77
+ export interface TypeDefinitionProvider {
78
+ getTypeDefinitions(document: TextDocument, position: Position): Resolvable<Location[] | null>;
79
+ }
77
80
  export interface OnWatchFileChangesParam {
78
81
  fileName: string;
79
82
  changeType: FileChangeType;
@@ -84,7 +87,7 @@ export interface OnWatchFileChangesProvider {
84
87
  export interface UpdateNonAstroFile {
85
88
  updateNonAstroFile(fileName: string, changes: TextDocumentContentChangeEvent[]): void;
86
89
  }
87
- declare type ProviderBase = DiagnosticsProvider & HoverProvider & CompletionsProvider & DefinitionsProvider & FormattingProvider & FoldingRangesProvider & TagCompleteProvider & DocumentColorsProvider & ColorPresentationsProvider & DocumentSymbolsProvider & UpdateImportsProvider & CodeActionsProvider & FindReferencesProvider & RenameProvider & SignatureHelpProvider & SemanticTokensProvider & SelectionRangeProvider & OnWatchFileChangesProvider & LinkedEditingRangesProvider & InlayHintsProvider & UpdateNonAstroFile;
90
+ declare type ProviderBase = DiagnosticsProvider & HoverProvider & CompletionsProvider & DefinitionsProvider & TypeDefinitionProvider & FormattingProvider & FoldingRangesProvider & TagCompleteProvider & DocumentColorsProvider & ColorPresentationsProvider & DocumentSymbolsProvider & UpdateImportsProvider & CodeActionsProvider & FindReferencesProvider & RenameProvider & SignatureHelpProvider & SemanticTokensProvider & SelectionRangeProvider & OnWatchFileChangesProvider & LinkedEditingRangesProvider & InlayHintsProvider & UpdateNonAstroFile;
88
91
  export declare type LSProvider = ProviderBase;
89
92
  export declare type Plugin = Partial<ProviderBase> & {
90
93
  __name: string;
@@ -1,8 +1,9 @@
1
- import { CancellationToken, CodeAction, CodeActionContext, CompletionContext, DefinitionLink, Diagnostic, FoldingRange, FormattingOptions, Hover, InlayHint, Position, Range, SemanticTokens, SignatureHelp, SignatureHelpContext, SymbolInformation, TextDocumentContentChangeEvent, TextEdit, WorkspaceEdit } from 'vscode-languageserver';
1
+ import { CancellationToken, CodeAction, CodeActionContext, CompletionContext, DefinitionLink, Location, Diagnostic, FoldingRange, Hover, InlayHint, Position, Range, SemanticTokens, SignatureHelp, SignatureHelpContext, SymbolInformation, TextDocumentContentChangeEvent, WorkspaceEdit } from 'vscode-languageserver';
2
2
  import { ConfigManager } from '../../core/config';
3
- import { AstroDocument, DocumentManager } from '../../core/documents';
3
+ import { AstroDocument } from '../../core/documents';
4
4
  import { AppCompletionItem, AppCompletionList, OnWatchFileChangesParam, Plugin } from '../interfaces';
5
5
  import { CompletionItemData } from './features/CompletionsProvider';
6
+ import { LanguageServiceManager } from './LanguageServiceManager';
6
7
  import { Astro2TSXResult } from './astro2tsx';
7
8
  export declare class TypeScriptPlugin implements Plugin {
8
9
  __name: string;
@@ -12,17 +13,16 @@ export declare class TypeScriptPlugin implements Plugin {
12
13
  private readonly completionProvider;
13
14
  private readonly hoverProvider;
14
15
  private readonly definitionsProvider;
16
+ private readonly typeDefinitionsProvider;
15
17
  private readonly signatureHelpProvider;
16
18
  private readonly diagnosticsProvider;
17
19
  private readonly documentSymbolsProvider;
18
20
  private readonly inlayHintsProvider;
19
21
  private readonly semanticTokensProvider;
20
22
  private readonly foldingRangesProvider;
21
- private readonly formattingProvider;
22
- constructor(docManager: DocumentManager, configManager: ConfigManager, workspaceUris: string[]);
23
+ constructor(configManager: ConfigManager, languageServiceManager: LanguageServiceManager);
23
24
  doHover(document: AstroDocument, position: Position): Promise<Hover | null>;
24
25
  rename(document: AstroDocument, position: Position, newName: string): Promise<WorkspaceEdit | null>;
25
- formatDocument(document: AstroDocument, options: FormattingOptions): Promise<TextEdit[]>;
26
26
  getFoldingRanges(document: AstroDocument): Promise<FoldingRange[] | null>;
27
27
  getSemanticTokens(document: AstroDocument, range?: Range, cancellationToken?: CancellationToken): Promise<SemanticTokens | null>;
28
28
  getDocumentSymbols(document: AstroDocument): Promise<SymbolInformation[]>;
@@ -31,6 +31,7 @@ export declare class TypeScriptPlugin implements Plugin {
31
31
  resolveCompletion(document: AstroDocument, completionItem: AppCompletionItem<CompletionItemData>, cancellationToken?: CancellationToken): Promise<AppCompletionItem<CompletionItemData>>;
32
32
  getInlayHints(document: AstroDocument, range: Range): Promise<InlayHint[]>;
33
33
  getDefinitions(document: AstroDocument, position: Position): Promise<DefinitionLink[]>;
34
+ getTypeDefinition(document: AstroDocument, position: Position): Promise<Location[] | null>;
34
35
  getDiagnostics(document: AstroDocument, cancellationToken?: CancellationToken): Promise<Diagnostic[]>;
35
36
  onWatchFileChanges(onWatchFileChangesParas: OnWatchFileChangesParam[]): Promise<void>;
36
37
  updateNonAstroFile(fileName: string, changes: TextDocumentContentChangeEvent[]): Promise<void>;
@@ -10,7 +10,6 @@ const CompletionsProvider_1 = require("./features/CompletionsProvider");
10
10
  const DiagnosticsProvider_1 = require("./features/DiagnosticsProvider");
11
11
  const HoverProvider_1 = require("./features/HoverProvider");
12
12
  const SignatureHelpProvider_1 = require("./features/SignatureHelpProvider");
13
- const LanguageServiceManager_1 = require("./LanguageServiceManager");
14
13
  const utils_1 = require("./utils");
15
14
  const DocumentSymbolsProvider_1 = require("./features/DocumentSymbolsProvider");
16
15
  const SemanticTokenProvider_1 = require("./features/SemanticTokenProvider");
@@ -18,25 +17,25 @@ const FoldingRangesProvider_1 = require("./features/FoldingRangesProvider");
18
17
  const CodeActionsProvider_1 = require("./features/CodeActionsProvider");
19
18
  const DefinitionsProvider_1 = require("./features/DefinitionsProvider");
20
19
  const InlayHintsProvider_1 = require("./features/InlayHintsProvider");
21
- const FormattingProvider_1 = require("./features/FormattingProvider");
22
20
  const astro2tsx_1 = __importDefault(require("./astro2tsx"));
23
21
  const utils_2 = require("./snapshots/utils");
22
+ const TypeDefinitionsProvider_1 = require("./features/TypeDefinitionsProvider");
24
23
  class TypeScriptPlugin {
25
- constructor(docManager, configManager, workspaceUris) {
24
+ constructor(configManager, languageServiceManager) {
26
25
  this.__name = 'typescript';
27
26
  this.configManager = configManager;
28
- this.languageServiceManager = new LanguageServiceManager_1.LanguageServiceManager(docManager, workspaceUris, configManager);
27
+ this.languageServiceManager = languageServiceManager;
29
28
  this.codeActionsProvider = new CodeActionsProvider_1.CodeActionsProviderImpl(this.languageServiceManager, this.configManager);
30
29
  this.completionProvider = new CompletionsProvider_1.CompletionsProviderImpl(this.languageServiceManager, this.configManager);
31
30
  this.hoverProvider = new HoverProvider_1.HoverProviderImpl(this.languageServiceManager);
32
31
  this.definitionsProvider = new DefinitionsProvider_1.DefinitionsProviderImpl(this.languageServiceManager);
32
+ this.typeDefinitionsProvider = new TypeDefinitionsProvider_1.TypeDefinitionsProviderImpl(this.languageServiceManager);
33
33
  this.signatureHelpProvider = new SignatureHelpProvider_1.SignatureHelpProviderImpl(this.languageServiceManager);
34
34
  this.diagnosticsProvider = new DiagnosticsProvider_1.DiagnosticsProviderImpl(this.languageServiceManager);
35
35
  this.documentSymbolsProvider = new DocumentSymbolsProvider_1.DocumentSymbolsProviderImpl(this.languageServiceManager);
36
36
  this.semanticTokensProvider = new SemanticTokenProvider_1.SemanticTokensProviderImpl(this.languageServiceManager);
37
37
  this.inlayHintsProvider = new InlayHintsProvider_1.InlayHintsProviderImpl(this.languageServiceManager, this.configManager);
38
38
  this.foldingRangesProvider = new FoldingRangesProvider_1.FoldingRangesProviderImpl(this.languageServiceManager);
39
- this.formattingProvider = new FormattingProvider_1.FormattingProviderImpl(this.languageServiceManager, this.configManager);
40
39
  }
41
40
  async doHover(document, position) {
42
41
  if (!(await this.featureEnabled(document, 'hover'))) {
@@ -67,9 +66,6 @@ class TypeScriptPlugin {
67
66
  });
68
67
  return edit;
69
68
  }
70
- async formatDocument(document, options) {
71
- return this.formattingProvider.formatDocument(document, options);
72
- }
73
69
  async getFoldingRanges(document) {
74
70
  return this.foldingRangesProvider.getFoldingRanges(document);
75
71
  }
@@ -108,6 +104,9 @@ class TypeScriptPlugin {
108
104
  async getDefinitions(document, position) {
109
105
  return this.definitionsProvider.getDefinitions(document, position);
110
106
  }
107
+ async getTypeDefinition(document, position) {
108
+ return this.typeDefinitionsProvider.getTypeDefinitions(document, position);
109
+ }
111
110
  async getDiagnostics(document, cancellationToken) {
112
111
  if (!(await this.featureEnabled(document, 'diagnostics'))) {
113
112
  return [];
@@ -4,10 +4,24 @@ const os_1 = require("os");
4
4
  const parseAstro_1 = require("../../core/documents/parseAstro");
5
5
  function addProps(content, className) {
6
6
  let defaultExportType = 'Record<string, any>';
7
+ let shouldAddGlobal = false;
8
+ let astroGlobal = "type AstroGlobal = import('astro').AstroGlobal";
9
+ const astroGlobalConstDef = `
10
+ /**
11
+ * Astro global available in all contexts in .astro files
12
+ *
13
+ * [Astro documentation](https://docs.astro.build/reference/api-reference/#astro-global)
14
+ */
15
+ declare const Astro: Readonly<AstroGlobal>;
16
+ `;
7
17
  if (/(interface|type) Props/.test(content)) {
8
18
  defaultExportType = 'Props';
19
+ shouldAddGlobal = true;
20
+ astroGlobal += ' & { props: Props }';
9
21
  }
10
- return os_1.EOL + `export default function ${className}__AstroComponent_(_props: ${defaultExportType}): any {}`;
22
+ return (os_1.EOL +
23
+ (shouldAddGlobal ? astroGlobal + os_1.EOL + astroGlobalConstDef : '') +
24
+ `export default function ${className}__AstroComponent_(_props: ${defaultExportType}): any {}`);
11
25
  }
12
26
  function escapeTemplateLiteralContent(content) {
13
27
  return content.replace(/`/g, '\\`');
@@ -0,0 +1,9 @@
1
+ import { Position, Location } from 'vscode-languageserver-protocol';
2
+ import { AstroDocument } from '../../../core/documents';
3
+ import { TypeDefinitionProvider } from '../../interfaces';
4
+ import { LanguageServiceManager } from '../LanguageServiceManager';
5
+ export declare class TypeDefinitionsProviderImpl implements TypeDefinitionProvider {
6
+ private languageServiceManager;
7
+ constructor(languageServiceManager: LanguageServiceManager);
8
+ getTypeDefinitions(document: AstroDocument, position: Position): Promise<Location[]>;
9
+ }
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TypeDefinitionsProviderImpl = void 0;
4
+ const vscode_languageserver_protocol_1 = require("vscode-languageserver-protocol");
5
+ const documents_1 = require("../../../core/documents");
6
+ const utils_1 = require("../../../utils");
7
+ const utils_2 = require("../utils");
8
+ const utils_3 = require("./utils");
9
+ class TypeDefinitionsProviderImpl {
10
+ constructor(languageServiceManager) {
11
+ this.languageServiceManager = languageServiceManager;
12
+ }
13
+ async getTypeDefinitions(document, position) {
14
+ const { lang, tsDoc } = await this.languageServiceManager.getLSAndTSDoc(document);
15
+ const mainFragment = await tsDoc.createFragment();
16
+ const fragmentOffset = mainFragment.offsetAt(mainFragment.getGeneratedPosition(position));
17
+ const tsFilePath = (0, utils_2.toVirtualAstroFilePath)(tsDoc.filePath);
18
+ const html = document.html;
19
+ const offset = document.offsetAt(position);
20
+ const node = html.findNodeAt(offset);
21
+ let typeDefs;
22
+ if (node.tag === 'script') {
23
+ const { snapshot: scriptTagSnapshot, filePath: scriptFilePath, offset: scriptOffset, } = (0, utils_2.getScriptTagSnapshot)(tsDoc, document, node, position);
24
+ typeDefs = lang.getTypeDefinitionAtPosition(scriptFilePath, scriptOffset);
25
+ if (typeDefs) {
26
+ typeDefs = typeDefs.map((def) => {
27
+ const isInSameFile = def.fileName === scriptFilePath;
28
+ def.fileName = isInSameFile ? tsFilePath : def.fileName;
29
+ if (isInSameFile) {
30
+ def.textSpan.start = mainFragment.offsetAt(scriptTagSnapshot.getOriginalPosition(scriptTagSnapshot.positionAt(def.textSpan.start)));
31
+ }
32
+ return def;
33
+ });
34
+ }
35
+ }
36
+ else {
37
+ typeDefs = lang.getTypeDefinitionAtPosition(tsFilePath, fragmentOffset);
38
+ }
39
+ const docs = new utils_3.SnapshotFragmentMap(this.languageServiceManager);
40
+ docs.set(tsFilePath, { fragment: mainFragment, snapshot: tsDoc });
41
+ if (!typeDefs) {
42
+ return [];
43
+ }
44
+ const result = await Promise.all(typeDefs.map(async (typeDef) => {
45
+ const { fragment } = await docs.retrieve(typeDef.fileName);
46
+ const fileName = (0, utils_2.ensureRealFilePath)(typeDef.fileName);
47
+ const range = (0, documents_1.mapRangeToOriginal)(fragment, (0, utils_2.convertRange)(fragment, typeDef.textSpan));
48
+ if (range.start.line >= 0 && range.end.line >= 0) {
49
+ return vscode_languageserver_protocol_1.Location.create((0, utils_1.pathToUrl)(fileName), range);
50
+ }
51
+ }));
52
+ return result.filter(utils_1.isNotNullOrUndefined);
53
+ }
54
+ }
55
+ exports.TypeDefinitionsProviderImpl = TypeDefinitionsProviderImpl;
@@ -72,7 +72,7 @@ async function createLanguageService(tsconfigPath, docContext, workspaceUris) {
72
72
  const tsconfigRoot = tsconfigPath ? (0, path_1.dirname)(tsconfigPath) : process.cwd();
73
73
  const workspacePaths = workspaceUris.map((uri) => (0, utils_1.urlToPath)(uri));
74
74
  const workspacePath = workspacePaths.find((uri) => tsconfigRoot.startsWith(uri)) || workspacePaths[0];
75
- const astroVersion = (0, utils_1.getUserAstroVersion)(workspacePath);
75
+ const astroInstall = (0, utils_1.getAstroInstall)([tsconfigRoot, workspacePath]);
76
76
  const config = (await docContext.configManager.getConfig('astro.typescript', workspacePath)) ?? {};
77
77
  const allowArbitraryAttrs = config.allowArbitraryAttributes ?? false;
78
78
  // `raw` here represent the tsconfig merged with any extended config
@@ -81,9 +81,8 @@ async function createLanguageService(tsconfigPath, docContext, workspaceUris) {
81
81
  const snapshotManager = new SnapshotManager_1.SnapshotManager(docContext.globalSnapshotManager, files, fullConfig, tsconfigRoot || process.cwd());
82
82
  const astroModuleLoader = (0, module_loader_1.createAstroModuleLoader)(getScriptSnapshot, compilerOptions);
83
83
  const scriptFileNames = [];
84
- if (astroVersion.exist) {
85
- const astroDir = (0, path_1.dirname)(require.resolve('astro', { paths: [workspacePath] }));
86
- scriptFileNames.push(...['./env.d.ts', './astro-jsx.d.ts'].map((f) => typescript_1.default.sys.resolvePath((0, path_1.resolve)(astroDir, f))));
84
+ if (astroInstall) {
85
+ scriptFileNames.push(...['./env.d.ts', './astro-jsx.d.ts'].map((f) => typescript_1.default.sys.resolvePath((0, path_1.resolve)(astroInstall.path, f))));
87
86
  }
88
87
  let languageServerDirectory;
89
88
  try {
@@ -92,14 +91,13 @@ async function createLanguageService(tsconfigPath, docContext, workspaceUris) {
92
91
  catch (e) {
93
92
  languageServerDirectory = __dirname;
94
93
  }
95
- // Before Astro 1.0, JSX definitions were inside of the language-server instead of inside Astro
96
- // TODO: Remove this and astro-jsx.d.ts in types when we consider having support for Astro < 1.0 unnecessary
97
- if ((astroVersion.major === 0 || astroVersion.full === '1.0.0-beta.0') &&
98
- !astroVersion.full.startsWith('0.0.0-rc-') // 1.0.0's RC is considered to be 0.0.0, so we have to check for it
94
+ // Fallback to internal types when Astro is not installed or the Astro version is too old
95
+ if (!astroInstall ||
96
+ ((astroInstall.version.major === 0 || astroInstall.version.full === '1.0.0-beta.0') &&
97
+ !astroInstall.version.full.startsWith('0.0.0-rc-')) // 1.0.0's RC is considered to be 0.0.0, so we have to check for it
99
98
  ) {
100
- const astroTSXFile = typescript_1.default.sys.resolvePath((0, path_1.resolve)(languageServerDirectory, '../types/astro-jsx.d.ts'));
101
- scriptFileNames.push(astroTSXFile);
102
- console.warn("Version lower than 1.0 detected, using internal types instead of Astro's");
99
+ scriptFileNames.push(...['../types/astro-jsx.d.ts', '../types/env.d.ts'].map((f) => typescript_1.default.sys.resolvePath((0, path_1.resolve)(languageServerDirectory, f))));
100
+ console.warn("Couldn't load types from Astro, using internal types instead");
103
101
  }
104
102
  if (allowArbitraryAttrs) {
105
103
  const arbitraryAttrsDTS = typescript_1.default.sys.resolvePath((0, path_1.resolve)(languageServerDirectory, '../types/arbitrary-attrs.d.ts'));