@astrojs/language-server 0.19.6 → 0.20.3

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,28 @@
1
1
  # @astrojs/language-server
2
2
 
3
+ ## 0.20.3
4
+
5
+ ### Patch Changes
6
+
7
+ - 081cf24: Fix completions not working inside script tags, fix duplicate completions in some cases, added completions for the slot element
8
+
9
+ ## 0.20.1
10
+
11
+ ### Patch Changes
12
+
13
+ - e6996f5: Fixed many situations where the language server would warn abusively about not being able to find Astro
14
+ - 4589c2b: Fix the language server not warning properly when a package is implicitely any due to missing types
15
+
16
+ ## 0.20.0
17
+
18
+ ### Minor Changes
19
+
20
+ - ba0fab1: Load language integrations from the user's project instead of bundling them in the language server
21
+
22
+ ### Patch Changes
23
+
24
+ - fa3f0f7: Updated exports for `astro check`
25
+
3
26
  ## 0.19.6
4
27
 
5
28
  ### Patch Changes
package/dist/check.d.ts CHANGED
@@ -1,6 +1,8 @@
1
- import type { Diagnostic } from 'vscode-languageserver';
2
- export { DiagnosticSeverity } from 'vscode-languageserver-protocol';
3
- interface GetDiagnosticsResult {
1
+ import type { Diagnostic } from 'vscode-languageserver-types';
2
+ import { LSConfig } from './core/config';
3
+ export { DiagnosticSeverity } from 'vscode-languageserver-types';
4
+ export { Diagnostic };
5
+ export interface GetDiagnosticsResult {
4
6
  filePath: string;
5
7
  text: string;
6
8
  diagnostics: Diagnostic[];
@@ -9,7 +11,7 @@ export declare class AstroCheck {
9
11
  private docManager;
10
12
  private configManager;
11
13
  private pluginHost;
12
- constructor(workspacePath: string);
14
+ constructor(workspacePath: string, options?: LSConfig);
13
15
  upsertDocument(doc: {
14
16
  text: string;
15
17
  uri: string;
package/dist/check.js CHANGED
@@ -1,17 +1,20 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.AstroCheck = exports.DiagnosticSeverity = void 0;
4
- const documents_1 = require("./core/documents");
5
4
  const config_1 = require("./core/config");
5
+ const documents_1 = require("./core/documents");
6
6
  const plugins_1 = require("./plugins");
7
- var vscode_languageserver_protocol_1 = require("vscode-languageserver-protocol");
8
- Object.defineProperty(exports, "DiagnosticSeverity", { enumerable: true, get: function () { return vscode_languageserver_protocol_1.DiagnosticSeverity; } });
7
+ var vscode_languageserver_types_1 = require("vscode-languageserver-types");
8
+ Object.defineProperty(exports, "DiagnosticSeverity", { enumerable: true, get: function () { return vscode_languageserver_types_1.DiagnosticSeverity; } });
9
9
  class AstroCheck {
10
- constructor(workspacePath) {
10
+ constructor(workspacePath, options) {
11
11
  this.docManager = documents_1.DocumentManager.newInstance();
12
12
  this.configManager = new config_1.ConfigManager();
13
13
  this.pluginHost = new plugins_1.PluginHost(this.docManager);
14
14
  this.initialize(workspacePath);
15
+ if (options) {
16
+ this.configManager.updateGlobalConfig(options);
17
+ }
15
18
  }
16
19
  upsertDocument(doc) {
17
20
  this.docManager.openDocument({
@@ -21,6 +21,10 @@ export declare function getLineAtPosition(position: Position, text: string): str
21
21
  * Return if a given offset is inside the start tag of a component
22
22
  */
23
23
  export declare function isInComponentStartTag(html: HTMLDocument, offset: number): boolean;
24
+ /**
25
+ * Return if a given offset is inside the name of a tag
26
+ */
27
+ export declare function isInTagName(html: HTMLDocument, offset: number): boolean;
24
28
  /**
25
29
  * Return true if a specific node could be a component.
26
30
  * This is not a 100% sure test as it'll return false for any component that does not match the standard format for a component
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getFirstNonWhitespaceIndex = exports.getLineOffsets = exports.offsetAt = exports.positionAt = exports.isInsideFrontmatter = exports.isInsideExpression = exports.isInTag = exports.isPossibleComponent = exports.isInComponentStartTag = exports.getLineAtPosition = exports.extractScriptTags = exports.extractStyleTags = exports.walk = void 0;
3
+ exports.getFirstNonWhitespaceIndex = exports.getLineOffsets = exports.offsetAt = exports.positionAt = exports.isInsideFrontmatter = exports.isInsideExpression = exports.isInTag = exports.isPossibleComponent = exports.isInTagName = exports.isInComponentStartTag = exports.getLineAtPosition = exports.extractScriptTags = exports.extractStyleTags = exports.walk = void 0;
4
4
  const vscode_languageserver_1 = require("vscode-languageserver");
5
5
  const utils_1 = require("../../utils");
6
6
  const parseHtml_1 = require("./parseHtml");
@@ -105,6 +105,14 @@ function isInComponentStartTag(html, offset) {
105
105
  return isPossibleComponent(node) && (!node.startTagEnd || offset < node.startTagEnd);
106
106
  }
107
107
  exports.isInComponentStartTag = isInComponentStartTag;
108
+ /**
109
+ * Return if a given offset is inside the name of a tag
110
+ */
111
+ function isInTagName(html, offset) {
112
+ const node = html.findNodeAt(offset);
113
+ return offset > node.start && offset < node.start + (node.tag?.length ?? 0);
114
+ }
115
+ exports.isInTagName = isInTagName;
108
116
  /**
109
117
  * Return true if a specific node could be a component.
110
118
  * This is not a 100% sure test as it'll return false for any component that does not match the standard format for a component
@@ -0,0 +1,6 @@
1
+ import type * as svelte from '@astrojs/svelte/dist/editor.cjs';
2
+ import type * as vue from '@astrojs/svelte/dist/editor.cjs';
3
+ export declare function setIsTrusted(_isTrusted: boolean): void;
4
+ export declare function getPackagePath(packageName: string, fromPath: string): string | undefined;
5
+ export declare function importSvelteIntegration(fromPath: string): typeof svelte | undefined;
6
+ export declare function importVueIntegration(fromPath: string): typeof vue | undefined;
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.importVueIntegration = exports.importSvelteIntegration = exports.getPackagePath = exports.setIsTrusted = void 0;
4
+ const path_1 = require("path");
5
+ let isTrusted = true;
6
+ function setIsTrusted(_isTrusted) {
7
+ isTrusted = _isTrusted;
8
+ }
9
+ exports.setIsTrusted = setIsTrusted;
10
+ function getPackagePath(packageName, fromPath) {
11
+ const paths = [];
12
+ if (isTrusted) {
13
+ paths.unshift(fromPath);
14
+ }
15
+ try {
16
+ const packageJSONPath = require.resolve(`${packageName}/package.json`, {
17
+ paths,
18
+ });
19
+ return (0, path_1.dirname)(packageJSONPath);
20
+ }
21
+ catch (e) {
22
+ return undefined;
23
+ }
24
+ }
25
+ exports.getPackagePath = getPackagePath;
26
+ function importEditorIntegration(packageName, fromPath) {
27
+ const pkgPath = getPackagePath(packageName, fromPath);
28
+ if (pkgPath) {
29
+ const main = (0, path_1.resolve)(pkgPath, 'dist', 'editor.cjs');
30
+ return require(main);
31
+ }
32
+ return undefined;
33
+ }
34
+ function importSvelteIntegration(fromPath) {
35
+ return importEditorIntegration('@astrojs/svelte', fromPath);
36
+ }
37
+ exports.importSvelteIntegration = importSvelteIntegration;
38
+ function importVueIntegration(fromPath) {
39
+ return importEditorIntegration('@astrojs/vue', fromPath);
40
+ }
41
+ exports.importVueIntegration = importVueIntegration;
package/dist/index.d.ts CHANGED
@@ -1 +1,2 @@
1
- export { AstroCheck, DiagnosticSeverity } from './check';
1
+ export { AstroCheck, Diagnostic, DiagnosticSeverity, GetDiagnosticsResult } from './check';
2
+ export { offsetAt } from './core/documents';
package/dist/index.js CHANGED
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DiagnosticSeverity = exports.AstroCheck = void 0;
3
+ exports.offsetAt = exports.DiagnosticSeverity = exports.AstroCheck = void 0;
4
4
  var check_1 = require("./check");
5
5
  Object.defineProperty(exports, "AstroCheck", { enumerable: true, get: function () { return check_1.AstroCheck; } });
6
6
  Object.defineProperty(exports, "DiagnosticSeverity", { enumerable: true, get: function () { return check_1.DiagnosticSeverity; } });
7
+ var documents_1 = require("./core/documents");
8
+ Object.defineProperty(exports, "offsetAt", { enumerable: true, get: function () { return documents_1.offsetAt; } });
@@ -38,19 +38,14 @@ class PluginHost {
38
38
  const astro = completions.find((completion) => completion.plugin === 'astro');
39
39
  if (html && ts) {
40
40
  const inComponentStartTag = (0, documents_1.isInComponentStartTag)(document.html, document.offsetAt(position));
41
- if (!inComponentStartTag) {
41
+ // If the HTML plugin returned completions, it's highly likely that TS ones are duplicate
42
+ if (html.result.items.length > 0) {
42
43
  ts.result.items = [];
43
44
  }
44
- // If the Astro plugin has completions for us, don't show TypeScript's as they're most likely duplicates
45
+ // Inside components, if the Astro plugin has completions we don't want the TS ones are they're duplicates
45
46
  if (astro && astro.result.items.length > 0 && inComponentStartTag) {
46
47
  ts.result.items = [];
47
48
  }
48
- ts.result.items = ts.result.items.map((item) => {
49
- if (item.sortText != '-1') {
50
- item.sortText = 'Z' + (item.sortText || '');
51
- }
52
- return item;
53
- });
54
49
  }
55
50
  let flattenedCompletions = completions.flatMap((completion) => completion.result.items);
56
51
  const isIncomplete = completions.reduce((incomplete, completion) => incomplete || completion.result.isIncomplete, false);
@@ -11,7 +11,7 @@ class HTMLPlugin {
11
11
  constructor(configManager) {
12
12
  this.__name = 'html';
13
13
  this.lang = (0, vscode_html_languageservice_1.getLanguageService)({
14
- customDataProviders: [astro_attributes_1.astroAttributes, astro_attributes_1.classListAttribute],
14
+ customDataProviders: [astro_attributes_1.astroAttributes, astro_attributes_1.astroElements, astro_attributes_1.classListAttribute],
15
15
  });
16
16
  this.attributeOnlyLang = (0, vscode_html_languageservice_1.getLanguageService)({
17
17
  customDataProviders: [astro_attributes_1.astroAttributes],
@@ -72,10 +72,14 @@ class HTMLPlugin {
72
72
  }
73
73
  // If we're in a component starting tag, we do not want HTML language completions
74
74
  // as HTML attributes are not valid for components
75
- const results = (0, utils_1.isInComponentStartTag)(html, document.offsetAt(position))
75
+ const inComponentTag = (0, utils_1.isInComponentStartTag)(html, offset);
76
+ const inTagName = (0, utils_1.isInTagName)(html, offset);
77
+ const results = inComponentTag && !inTagName
76
78
  ? (0, utils_2.removeDataAttrCompletion)(this.attributeOnlyLang.doComplete(document, position, html).items)
77
- : this.lang.doComplete(document, position, html).items;
78
- return vscode_languageserver_1.CompletionList.create([...results, ...this.getLangCompletions(results), ...emmetResults.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);
81
+ const langCompletions = inComponentTag ? [] : this.getLangCompletions(results);
82
+ return vscode_languageserver_1.CompletionList.create([...results, ...langCompletions, ...emmetResults.items],
79
83
  // Emmet completions change on every keystroke, so they are never complete
80
84
  emmetResults.items.length > 0);
81
85
  }
@@ -1,3 +1,4 @@
1
1
  export declare const classListAttribute: import("vscode-html-languageservice").IHTMLDataProvider;
2
+ export declare const astroElements: import("vscode-html-languageservice").IHTMLDataProvider;
2
3
  export declare const astroAttributes: import("vscode-html-languageservice").IHTMLDataProvider;
3
4
  export declare const astroDirectives: import("vscode-html-languageservice").IHTMLDataProvider;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.astroDirectives = exports.astroAttributes = exports.classListAttribute = void 0;
3
+ exports.astroDirectives = exports.astroAttributes = exports.astroElements = exports.classListAttribute = void 0;
4
4
  const vscode_html_languageservice_1 = require("vscode-html-languageservice");
5
5
  exports.classListAttribute = (0, vscode_html_languageservice_1.newHTMLDataProvider)('class-list', {
6
6
  version: 1,
@@ -17,42 +17,31 @@ exports.classListAttribute = (0, vscode_html_languageservice_1.newHTMLDataProvid
17
17
  },
18
18
  ],
19
19
  });
20
- exports.astroAttributes = (0, vscode_html_languageservice_1.newHTMLDataProvider)('astro-attributes', {
20
+ exports.astroElements = (0, vscode_html_languageservice_1.newHTMLDataProvider)('astro-elements', {
21
21
  version: 1,
22
- globalAttributes: [
23
- {
24
- name: 'set:html',
25
- description: 'Inject unescaped HTML into this tag',
26
- references: [
27
- {
28
- name: 'Astro reference',
29
- url: 'https://docs.astro.build/en/reference/directives-reference/#sethtml',
30
- },
31
- ],
32
- },
22
+ tags: [
33
23
  {
34
- name: 'set:text',
35
- description: 'Inject escaped text into this tag',
24
+ name: 'slot',
25
+ description: 'The slot element is a placeholder for external HTML content, allowing you to inject (or “slot”) child elements from other files into your component template.',
36
26
  references: [
37
27
  {
38
28
  name: 'Astro reference',
39
- url: 'https://docs.astro.build/en/reference/directives-reference/#settext',
29
+ url: 'https://docs.astro.build/en/core-concepts/astro-components/#slots',
40
30
  },
41
31
  ],
42
- },
43
- {
44
- name: 'is:raw',
45
- description: 'Instructs the Astro compiler to treat any children of this element as text',
46
- valueSet: 'v',
47
- references: [
32
+ attributes: [
48
33
  {
49
- name: 'Astro reference',
50
- url: 'https://docs.astro.build/en/reference/directives-reference/#israw',
34
+ name: 'name',
35
+ description: 'The name attribute allows you to pass only HTML elements with the corresponding slot name into a slot’s location.',
36
+ references: [
37
+ {
38
+ name: 'Astro reference',
39
+ url: 'https://docs.astro.build/en/core-concepts/astro-components/#named-slots',
40
+ },
41
+ ],
51
42
  },
52
43
  ],
53
44
  },
54
- ],
55
- tags: [
56
45
  {
57
46
  name: 'script',
58
47
  attributes: [
@@ -146,6 +135,42 @@ exports.astroAttributes = (0, vscode_html_languageservice_1.newHTMLDataProvider)
146
135
  },
147
136
  ],
148
137
  });
138
+ exports.astroAttributes = (0, vscode_html_languageservice_1.newHTMLDataProvider)('astro-attributes', {
139
+ version: 1,
140
+ globalAttributes: [
141
+ {
142
+ name: 'set:html',
143
+ description: 'Inject unescaped HTML into this tag',
144
+ references: [
145
+ {
146
+ name: 'Astro reference',
147
+ url: 'https://docs.astro.build/en/reference/directives-reference/#sethtml',
148
+ },
149
+ ],
150
+ },
151
+ {
152
+ name: 'set:text',
153
+ description: 'Inject escaped text into this tag',
154
+ references: [
155
+ {
156
+ name: 'Astro reference',
157
+ url: 'https://docs.astro.build/en/reference/directives-reference/#settext',
158
+ },
159
+ ],
160
+ },
161
+ {
162
+ name: 'is:raw',
163
+ description: 'Instructs the Astro compiler to treat any children of this element as text',
164
+ valueSet: 'v',
165
+ references: [
166
+ {
167
+ name: 'Astro reference',
168
+ url: 'https://docs.astro.build/en/reference/directives-reference/#israw',
169
+ },
170
+ ],
171
+ },
172
+ ],
173
+ });
149
174
  exports.astroDirectives = (0, vscode_html_languageservice_1.newHTMLDataProvider)('astro-directives', {
150
175
  version: 1,
151
176
  globalAttributes: [
@@ -21,7 +21,6 @@ export declare class CompletionsProviderImpl implements CompletionsProvider<Comp
21
21
  getCompletions(document: AstroDocument, position: Position, completionContext?: CompletionContext, cancellationToken?: CancellationToken): Promise<AppCompletionList<CompletionItemData> | null>;
22
22
  resolveCompletion(document: AstroDocument, item: AppCompletionItem<CompletionItemData>, cancellationToken?: CancellationToken): Promise<AppCompletionItem<CompletionItemData>>;
23
23
  private toCompletionItem;
24
- private isValidCompletion;
25
24
  private getCompletionDocument;
26
25
  /**
27
26
  * If the textEdit is out of the word range of the triggered position
@@ -78,26 +78,8 @@ class CompletionsProviderImpl {
78
78
  scriptTagIndex = scriptIndex;
79
79
  completions = lang.getCompletionsAtPosition(scriptFilePath, scriptOffset, {
80
80
  ...tsPreferences,
81
- // File extensions are required inside script tags, however TypeScript can't return completions with the `ts`
82
- // extension, so what we'll do instead is force `minimal` (aka, no extension) and manually add the extensions
83
- importModuleSpecifierEnding: 'minimal',
84
81
  triggerCharacter: validTriggerCharacter,
85
82
  }, formatOptions);
86
- if (completions) {
87
- // Manually adds file extensions to js and ts files
88
- completions.entries = completions?.entries.map((comp) => {
89
- if (comp.kind === typescript_1.ScriptElementKind.scriptElement &&
90
- (comp.kindModifiers === '.js' || comp.kindModifiers === '.ts')) {
91
- return {
92
- ...comp,
93
- name: comp.name + comp.kindModifiers,
94
- };
95
- }
96
- else {
97
- return comp;
98
- }
99
- });
100
- }
101
83
  }
102
84
  else {
103
85
  // PERF: Getting TS completions is fairly slow and I am currently not sure how to speed it up
@@ -132,7 +114,7 @@ class CompletionsProviderImpl {
132
114
  const fragment = await tsDoc.createFragment();
133
115
  const existingImports = this.getExistingImports(document);
134
116
  const completionItems = completions.entries
135
- .filter(this.isValidCompletion)
117
+ .filter(isValidCompletion)
136
118
  .map((entry) => this.toCompletionItem(fragment, entry, filePath, offset, isCompletionInsideFrontmatter, scriptTagIndex, existingImports))
137
119
  .filter(utils_3.isNotNullOrUndefined)
138
120
  .map((comp) => this.fixTextEditRange(wordRangeStartPosition, comp));
@@ -250,13 +232,6 @@ class CompletionsProviderImpl {
250
232
  },
251
233
  };
252
234
  }
253
- isValidCompletion(completion) {
254
- // Remove completion for default exported function
255
- if (completion.name === 'default' && completion.kindModifiers == typescript_1.ScriptElementKindModifier.exportedModifier) {
256
- return false;
257
- }
258
- return true;
259
- }
260
235
  getCompletionDocument(compDetail) {
261
236
  const { sourceDisplay, documentation: tsDocumentation, displayParts } = compDetail;
262
237
  let detail = (0, utils_2.removeAstroComponentSuffix)(typescript_1.default.displayPartsToString(displayParts));
@@ -355,3 +330,31 @@ function codeActionChangeToTextEdit(document, fragment, isInsideScriptTag, chang
355
330
  return vscode_languageserver_1.TextEdit.replace(range, change.newText);
356
331
  }
357
332
  exports.codeActionChangeToTextEdit = codeActionChangeToTextEdit;
333
+ // When Svelte components are imported, we have to reference the svelte2tsx's types to properly type the component
334
+ // An unfortunate downside of this is that it polutes completions, so let's filter those internal types manually
335
+ const svelte2tsxTypes = new Set([
336
+ 'Svelte2TsxComponent',
337
+ 'Svelte2TsxComponentConstructorParameters',
338
+ 'SvelteComponentConstructor',
339
+ 'SvelteActionReturnType',
340
+ 'SvelteTransitionConfig',
341
+ 'SvelteTransitionReturnType',
342
+ 'SvelteAnimationReturnType',
343
+ 'SvelteWithOptionalProps',
344
+ 'SvelteAllProps',
345
+ 'SveltePropsAnyFallback',
346
+ 'SvelteSlotsAnyFallback',
347
+ 'SvelteRestProps',
348
+ 'SvelteSlots',
349
+ 'SvelteStore',
350
+ ]);
351
+ function isValidCompletion(completion) {
352
+ // Remove completion for default exported function
353
+ const isDefaultExport = completion.name === 'default' && completion.kindModifiers == typescript_1.ScriptElementKindModifier.exportedModifier;
354
+ // Remove completion for svelte2tsx internal types
355
+ const isSvelte2tsxCompletion = completion.name.startsWith('__sveltets_') || svelte2tsxTypes.has(completion.name);
356
+ if (isDefaultExport || isSvelte2tsxCompletion) {
357
+ return false;
358
+ }
359
+ return true;
360
+ }
@@ -5,13 +5,13 @@ import { DiagnosticsProvider } from '../../interfaces';
5
5
  import { LanguageServiceManager } from '../LanguageServiceManager';
6
6
  export declare enum DiagnosticCodes {
7
7
  SPREAD_EXPECTED = 1005,
8
+ IS_NOT_A_MODULE = 2306,
8
9
  DUPLICATED_JSX_ATTRIBUTES = 17001,
9
10
  MUST_HAVE_PARENT_ELEMENT = 2657,
10
11
  CANT_RETURN_OUTSIDE_FUNC = 1108,
11
12
  ISOLATED_MODULE_COMPILE_ERR = 1208,
12
13
  TYPE_NOT_ASSIGNABLE = 2322,
13
14
  JSX_NO_CLOSING_TAG = 17008,
14
- NO_DECL_IMPLICIT_ANY_TYPE = 7016,
15
15
  JSX_ELEMENT_NO_CALL = 2604
16
16
  }
17
17
  export declare class DiagnosticsProviderImpl implements DiagnosticsProvider {
@@ -13,13 +13,13 @@ const utils_1 = require("../utils");
13
13
  var DiagnosticCodes;
14
14
  (function (DiagnosticCodes) {
15
15
  DiagnosticCodes[DiagnosticCodes["SPREAD_EXPECTED"] = 1005] = "SPREAD_EXPECTED";
16
+ DiagnosticCodes[DiagnosticCodes["IS_NOT_A_MODULE"] = 2306] = "IS_NOT_A_MODULE";
16
17
  DiagnosticCodes[DiagnosticCodes["DUPLICATED_JSX_ATTRIBUTES"] = 17001] = "DUPLICATED_JSX_ATTRIBUTES";
17
18
  DiagnosticCodes[DiagnosticCodes["MUST_HAVE_PARENT_ELEMENT"] = 2657] = "MUST_HAVE_PARENT_ELEMENT";
18
19
  DiagnosticCodes[DiagnosticCodes["CANT_RETURN_OUTSIDE_FUNC"] = 1108] = "CANT_RETURN_OUTSIDE_FUNC";
19
20
  DiagnosticCodes[DiagnosticCodes["ISOLATED_MODULE_COMPILE_ERR"] = 1208] = "ISOLATED_MODULE_COMPILE_ERR";
20
21
  DiagnosticCodes[DiagnosticCodes["TYPE_NOT_ASSIGNABLE"] = 2322] = "TYPE_NOT_ASSIGNABLE";
21
22
  DiagnosticCodes[DiagnosticCodes["JSX_NO_CLOSING_TAG"] = 17008] = "JSX_NO_CLOSING_TAG";
22
- DiagnosticCodes[DiagnosticCodes["NO_DECL_IMPLICIT_ANY_TYPE"] = 7016] = "NO_DECL_IMPLICIT_ANY_TYPE";
23
23
  DiagnosticCodes[DiagnosticCodes["JSX_ELEMENT_NO_CALL"] = 2604] = "JSX_ELEMENT_NO_CALL";
24
24
  })(DiagnosticCodes = exports.DiagnosticCodes || (exports.DiagnosticCodes = {}));
25
25
  class DiagnosticsProviderImpl {
@@ -86,7 +86,6 @@ class DiagnosticsProviderImpl {
86
86
  isNoSpreadExpected(diag, document) &&
87
87
  isNoCantReturnOutsideFunction(diag) &&
88
88
  isNoIsolatedModuleError(diag) &&
89
- isNoImportImplicitAnyType(diag) &&
90
89
  isNoJsxCannotHaveMultipleAttrsError(diag));
91
90
  })
92
91
  .map(enhanceIfNecessary);
@@ -172,9 +171,6 @@ function isNoJsxCannotHaveMultipleAttrsError(diagnostic) {
172
171
  function isNoJSXMustHaveOneParent(diagnostic) {
173
172
  return diagnostic.code !== DiagnosticCodes.MUST_HAVE_PARENT_ELEMENT;
174
173
  }
175
- function isNoImportImplicitAnyType(diagnostic) {
176
- return diagnostic.code !== DiagnosticCodes.NO_DECL_IMPLICIT_ANY_TYPE;
177
- }
178
174
  /**
179
175
  * When using the shorthand syntax for props TSX expects you to use the spread operator
180
176
  * Since the shorthand syntax works differently in Astro and this is not required, hide this message
@@ -190,7 +186,7 @@ function isNoSpreadExpected(diagnostic, document) {
190
186
  }
191
187
  /**
192
188
  * Ignore "Can't return outside of function body"
193
- * Since the frontmatter is at the top level, users trying to return a Response for SSR mode run into this
189
+ * Since the frontmatter is at the top level, users trying to return a Response for SSR mode run into this
194
190
  */
195
191
  function isNoCantReturnOutsideFunction(diagnostic) {
196
192
  return diagnostic.code !== DiagnosticCodes.CANT_RETURN_OUTSIDE_FUNC;
@@ -206,6 +202,19 @@ function isNoIsolatedModuleError(diagnostic) {
206
202
  * Some diagnostics have JSX-specific nomenclature or unclear description. Enhance them for more clarity.
207
203
  */
208
204
  function enhanceIfNecessary(diagnostic) {
205
+ // When the language integrations are not installed, the content of the imported snapshot is empty
206
+ // As such, it triggers the "is not a module error", which we can enhance with a more helpful message for the related framework
207
+ if (diagnostic.code === DiagnosticCodes.IS_NOT_A_MODULE) {
208
+ if (diagnostic.message.includes('.svelte')) {
209
+ diagnostic.message +=
210
+ '\n\nIs the `@astrojs/svelte` package installed? You can add it to your project by running the following command: `astro add svelte`. If already installed, restarting the language server might be necessary in order for the change to take effect';
211
+ }
212
+ if (diagnostic.message.includes('.vue')) {
213
+ diagnostic.message +=
214
+ '\n\nIs the `@astrojs/vue` package installed? You can add it to your project by running the following command: `astro add vue`. If already installed, restarting the language server might be necessary in order for the change to take effect';
215
+ }
216
+ return diagnostic;
217
+ }
209
218
  // JSX element has no closing tag. JSX -> HTML
210
219
  if (diagnostic.code === DiagnosticCodes.JSX_NO_CLOSING_TAG) {
211
220
  return {
@@ -224,7 +233,7 @@ function enhanceIfNecessary(diagnostic) {
224
233
  }
225
234
  // For the rare case where an user might try to put a client directive on something that is not a component
226
235
  if (diagnostic.code === DiagnosticCodes.TYPE_NOT_ASSIGNABLE) {
227
- if (diagnostic.message.includes("Property 'client:") && diagnostic.message.includes("to type 'HTMLProps")) {
236
+ if (diagnostic.message.includes("Property 'client:") && diagnostic.message.includes("to type 'HTMLAttributes")) {
228
237
  return {
229
238
  ...diagnostic,
230
239
  message: 'Client directives are only available on framework components',
@@ -90,7 +90,9 @@ async function createLanguageService(tsconfigPath, docContext, workspaceUris) {
90
90
  const scriptFileNames = [];
91
91
  // Before Astro 1.0, JSX definitions were inside of the language-server instead of inside Astro
92
92
  // TODO: Remove this and astro-jsx.d.ts in types when we consider having support for Astro < 1.0 unnecessary
93
- if (astroVersion.major === 0 || astroVersion.full === '1.0.0-beta.0') {
93
+ if ((astroVersion.major === 0 || astroVersion.full === '1.0.0-beta.0') &&
94
+ !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
95
+ ) {
94
96
  const astroTSXFile = typescript_1.default.sys.resolvePath((0, path_1.resolve)(languageServerDirectory, '../types/astro-jsx.d.ts'));
95
97
  scriptFileNames.push(astroTSXFile);
96
98
  console.warn("Version lower than 1.0 detected, using internal types instead of Astro's");
@@ -219,19 +221,23 @@ async function createLanguageService(tsconfigPath, docContext, workspaceUris) {
219
221
  });
220
222
  }
221
223
  function getParsedTSConfig() {
222
- let configJson = (tsconfigPath && typescript_1.default.readConfigFile(tsconfigPath, typescript_1.default.sys.readFile).config) || {};
224
+ let configJson = (tsconfigPath && typescript_1.default.readConfigFile(tsconfigPath, typescript_1.default.sys.readFile).config) || {
225
+ compilerOptions: getDefaultCompilerOptions(astroVersion),
226
+ };
223
227
  // If our user has types in their config but it doesn't include the types needed for Astro, add them to the config
224
228
  if (configJson.compilerOptions?.types) {
225
229
  if (!configJson.compilerOptions?.types.includes('astro/env')) {
226
230
  configJson.compilerOptions.types.push('astro/env');
227
231
  }
228
- if (astroVersion.major >= 1 &&
232
+ if ((astroVersion.major >= 1 || astroVersion.full.startsWith('0.0.0-rc-')) &&
229
233
  astroVersion.full !== '1.0.0-beta.0' &&
230
234
  !configJson.compilerOptions?.types.includes('astro/astro-jsx')) {
231
235
  configJson.compilerOptions.types.push('astro/astro-jsx');
232
236
  }
233
237
  }
234
- configJson.compilerOptions = Object.assign(getDefaultCompilerOptions(astroVersion), configJson.compilerOptions);
238
+ else {
239
+ configJson.compilerOptions = Object.assign(getDefaultCompilerOptions(astroVersion), configJson.compilerOptions);
240
+ }
235
241
  // Delete include so that .astro files don't get mistakenly excluded by the user
236
242
  delete configJson.include;
237
243
  // If the user supplied exclude, let's use theirs otherwise, use ours
@@ -241,6 +247,7 @@ async function createLanguageService(tsconfigPath, docContext, workspaceUris) {
241
247
  noEmit: true,
242
248
  declaration: false,
243
249
  resolveJsonModule: true,
250
+ allowSyntheticDefaultImports: true,
244
251
  allowNonTsExtensions: true,
245
252
  allowJs: true,
246
253
  jsx: typescript_1.default.JsxEmit.Preserve,
@@ -267,16 +274,14 @@ async function createLanguageService(tsconfigPath, docContext, workspaceUris) {
267
274
  }
268
275
  }
269
276
  /**
270
- * Default configuration used as a base and when the user doesn't have any
277
+ * Default compiler configuration used when the user doesn't have any
271
278
  */
272
279
  function getDefaultCompilerOptions(astroVersion) {
273
280
  const types = ['astro/env'];
274
- if (astroVersion.major >= 1 && astroVersion.full !== '1.0.0-beta.0') {
281
+ if ((astroVersion.major >= 1 && astroVersion.full !== '1.0.0-beta.0') || astroVersion.full.startsWith('0.0.0-rc-')) {
275
282
  types.push('astro/astro-jsx');
276
283
  }
277
284
  return {
278
- maxNodeModuleJsDepth: 2,
279
- allowSyntheticDefaultImports: true,
280
285
  types: types,
281
286
  };
282
287
  }
@@ -9,9 +9,8 @@ const astro2tsx_1 = __importDefault(require("../astro2tsx"));
9
9
  const vscode_uri_1 = require("vscode-uri");
10
10
  const utils_1 = require("../utils");
11
11
  const DocumentSnapshot_1 = require("./DocumentSnapshot");
12
- const svelte_language_integration_1 = require("@astrojs/svelte-language-integration");
13
- const vue_language_integration_1 = require("@astrojs/vue-language-integration");
14
12
  const utils_2 = require("../../../utils");
13
+ const importPackage_1 = require("../../../importPackage");
15
14
  // Utilities to create Snapshots from different contexts
16
15
  function createFromDocument(document) {
17
16
  const { code } = (0, astro2tsx_1.default)(document.getText(), classNameFromFilename(document.getURL()));
@@ -75,10 +74,16 @@ function createFromFrameworkFilePath(filePath, framework) {
75
74
  const originalText = typescript_1.default.sys.readFile(filePath) ?? '';
76
75
  let code = '';
77
76
  if (framework === 'svelte') {
78
- code = (0, svelte_language_integration_1.toTSX)(originalText, className);
77
+ const svelteIntegration = (0, importPackage_1.importSvelteIntegration)(filePath);
78
+ if (svelteIntegration) {
79
+ code = svelteIntegration.toTSX(originalText, className);
80
+ }
79
81
  }
80
82
  else if (framework === 'vue') {
81
- code = (0, vue_language_integration_1.toTSX)(originalText, className);
83
+ const vueIntegration = (0, importPackage_1.importVueIntegration)(filePath);
84
+ if (vueIntegration) {
85
+ code = vueIntegration.toTSX(originalText, className);
86
+ }
82
87
  }
83
88
  return new DocumentSnapshot_1.TypeScriptDocumentSnapshot(0, filePath, code, typescript_1.default.ScriptKind.TSX);
84
89
  }
package/dist/server.js CHANGED
@@ -49,6 +49,11 @@ function startLanguageServer(connection) {
49
49
  const workspaceUris = params.workspaceFolders?.map((folder) => folder.uri.toString()) ?? [params.rootUri ?? ''];
50
50
  workspaceUris.forEach((uri) => {
51
51
  uri = (0, utils_1.urlToPath)(uri);
52
+ // If the workspace is not an Astro project, we shouldn't warn about not finding Astro
53
+ // Unless the extension enabled itself in an untitled workspace, in which case the warning is valid
54
+ if (!(0, utils_1.isAstroWorkspace)(uri) && uri !== '/' && uri !== '') {
55
+ return;
56
+ }
52
57
  const astroVersion = (0, utils_1.getUserAstroVersion)(uri);
53
58
  if (astroVersion.exist === false) {
54
59
  connection.sendNotification(vscode_languageserver_1.ShowMessageNotification.type, {
@@ -56,12 +61,6 @@ function startLanguageServer(connection) {
56
61
  type: vscode_languageserver_1.MessageType.Warning,
57
62
  });
58
63
  }
59
- if (astroVersion.exist && astroVersion.major === 0 && astroVersion.minor < 24 && astroVersion.patch < 5) {
60
- connection.sendNotification(vscode_languageserver_1.ShowMessageNotification.type, {
61
- message: `The version of Astro you're using (${astroVersion.full}) is not supported by this version of the Astro language server. Please upgrade Astro to any version higher than 0.23.4 or if using the VS Code extension, downgrade the extension to 0.8.10`,
62
- type: vscode_languageserver_1.MessageType.Error,
63
- });
64
- }
65
64
  });
66
65
  hasConfigurationCapability = !!(params.capabilities.workspace && !!params.capabilities.workspace.configuration);
67
66
  pluginHost.initialize({
package/dist/utils.d.ts CHANGED
@@ -65,6 +65,10 @@ export declare function debounceSameArg<T>(fn: (arg: T) => void, shouldCancelPre
65
65
  * @param milliseconds Number of milliseconds to debounce/throttle
66
66
  */
67
67
  export declare function debounceThrottle<T extends (...args: any) => void>(fn: T, milliseconds: number): T;
68
+ /**
69
+ * Try to determine if a workspace could be an Astro project based on the content of `package.json`
70
+ */
71
+ export declare function isAstroWorkspace(workspacePath: string): boolean;
68
72
  export interface AstroVersion {
69
73
  full: string;
70
74
  major: number;
package/dist/utils.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getUserAstroVersion = exports.debounceThrottle = exports.debounceSameArg = exports.getRegExpMatches = exports.regexLastIndexOf = exports.isBeforeOrEqualToPosition = exports.isInRange = exports.isNotNullOrUndefined = exports.clamp = exports.modifyLines = exports.toPascalCase = exports.mergeDeep = exports.get = exports.getLastPartOfPath = exports.pathToUrl = exports.urlToPath = exports.normalizePath = exports.normalizeUri = void 0;
3
+ exports.getUserAstroVersion = exports.isAstroWorkspace = exports.debounceThrottle = exports.debounceSameArg = exports.getRegExpMatches = exports.regexLastIndexOf = exports.isBeforeOrEqualToPosition = exports.isInRange = exports.isNotNullOrUndefined = exports.clamp = exports.modifyLines = exports.toPascalCase = exports.mergeDeep = exports.get = exports.getLastPartOfPath = exports.pathToUrl = exports.urlToPath = exports.normalizePath = exports.normalizeUri = void 0;
4
4
  const vscode_uri_1 = require("vscode-uri");
5
5
  /** Normalizes a document URI */
6
6
  function normalizeUri(uri) {
@@ -197,6 +197,23 @@ function debounceThrottle(fn, milliseconds) {
197
197
  return maybeCall;
198
198
  }
199
199
  exports.debounceThrottle = debounceThrottle;
200
+ /**
201
+ * Try to determine if a workspace could be an Astro project based on the content of `package.json`
202
+ */
203
+ function isAstroWorkspace(workspacePath) {
204
+ try {
205
+ const astroPackageJson = require.resolve('./package.json', { paths: [workspacePath] });
206
+ const deps = Object.assign(require(astroPackageJson).dependencies ?? {}, require(astroPackageJson).devDependencies ?? {});
207
+ if (Object.keys(deps).includes('astro')) {
208
+ return true;
209
+ }
210
+ }
211
+ catch (e) {
212
+ return false;
213
+ }
214
+ return false;
215
+ }
216
+ exports.isAstroWorkspace = isAstroWorkspace;
200
217
  function getUserAstroVersion(basePath) {
201
218
  let version = '0.0.0';
202
219
  let exist = true;
@@ -213,7 +230,7 @@ function getUserAstroVersion(basePath) {
213
230
  catch (e) {
214
231
  // If we still couldn't find it, it probably just doesn't exist
215
232
  exist = false;
216
- console.error(e);
233
+ console.error(`${basePath} seems to be an Astro project, but we couldn't find Astro or Astro is not installed`);
217
234
  }
218
235
  }
219
236
  let [major, minor, patch] = version.split('.');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrojs/language-server",
3
- "version": "0.19.6",
3
+ "version": "0.20.3",
4
4
  "author": "withastro",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",
@@ -19,8 +19,6 @@
19
19
  "test": "cross-env TS_NODE_TRANSPILE_ONLY=true mocha --timeout 20000 --require ts-node/register \"test/**/*.ts\" --exclude \"test/**/*.d.ts\""
20
20
  },
21
21
  "dependencies": {
22
- "@astrojs/vue-language-integration": "^0.1.1",
23
- "@astrojs/svelte-language-integration": "^0.1.6",
24
22
  "@vscode/emmet-helper": "^2.8.4",
25
23
  "source-map": "^0.7.3",
26
24
  "typescript": "~4.6.4",
@@ -33,15 +31,19 @@
33
31
  "vscode-uri": "^3.0.3"
34
32
  },
35
33
  "devDependencies": {
34
+ "@astrojs/svelte": "^0.5.0",
35
+ "@astrojs/vue": "^0.5.0",
36
36
  "@types/chai": "^4.3.0",
37
37
  "@types/mocha": "^9.1.0",
38
38
  "@types/sinon": "^10.0.11",
39
- "astro": "^1.0.0-beta.1",
39
+ "astro": "^1.0.0-beta.72",
40
40
  "astro-scripts": "0.0.1",
41
41
  "chai": "^4.3.6",
42
42
  "cross-env": "^7.0.3",
43
43
  "mocha": "^9.2.2",
44
44
  "sinon": "^13.0.1",
45
- "ts-node": "^10.7.0"
45
+ "svelte": "^3.49.0",
46
+ "ts-node": "^10.7.0",
47
+ "vue": "^3.2.37"
46
48
  }
47
49
  }