@astrojs/language-server 0.19.1 → 0.19.4

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,27 @@
1
1
  # @astrojs/language-server
2
2
 
3
+ ## 0.19.4
4
+
5
+ ### Patch Changes
6
+
7
+ - 1033856: Enable support for TypeScript inside hoisted script tags
8
+
9
+ ## 0.19.3
10
+
11
+ ### Patch Changes
12
+
13
+ - 49ff4ef: Fixed more bugs where nonexistent server settings would result in a crash
14
+ - 14cbf05: Fix frontmatter completion not working when three dashes were already present
15
+
16
+ ## 0.19.2
17
+
18
+ ### Patch Changes
19
+
20
+ - 7de4967: Add better error messages for Vue and Svelte components with syntax errors
21
+ - Updated dependencies [7de4967]
22
+ - @astrojs/svelte-language-integration@0.1.6
23
+ - @astrojs/vue-language-integration@0.1.1
24
+
3
25
  ## 0.19.1
4
26
 
5
27
  ### Patch Changes
@@ -142,8 +142,15 @@ class ConfigManager {
142
142
  * Return true if a plugin and an optional feature is enabled
143
143
  */
144
144
  async isEnabled(document, plugin, feature) {
145
- const config = await this.getConfig('astro', document.uri);
146
- return feature ? config[plugin].enabled && config[plugin][feature].enabled : config[plugin].enabled;
145
+ const config = (await this.getConfig('astro', document.uri)) ?? {};
146
+ if (config[plugin]) {
147
+ let res = config[plugin].enabled;
148
+ if (feature && config[plugin][feature]) {
149
+ res = res && config[plugin][feature].enabled;
150
+ }
151
+ return res;
152
+ }
153
+ return false;
147
154
  }
148
155
  /**
149
156
  * Updating the global config should only be done in cases where the client doesn't support `workspace/configuration`
@@ -89,6 +89,7 @@ class SourceMapDocumentMapper {
89
89
  return { line: -1, character: -1 };
90
90
  }
91
91
  if (mapped.line === 0) {
92
+ // eslint-disable-next-line no-console
92
93
  console.log('Got 0 mapped line from', generatedPosition, 'col was', mapped.column);
93
94
  }
94
95
  return {
@@ -23,15 +23,16 @@ class CompletionsProviderImpl {
23
23
  }
24
24
  async getCompletions(document, position, completionContext) {
25
25
  let items = [];
26
- if (completionContext?.triggerCharacter === '-') {
26
+ const html = document.html;
27
+ const offset = document.offsetAt(position);
28
+ const node = html.findNodeAt(offset);
29
+ const insideExpression = (0, utils_1.isInsideExpression)(document.getText(), node.start, offset);
30
+ if (completionContext?.triggerCharacter === '-' && node.parent === undefined && !insideExpression) {
27
31
  const frontmatter = this.getComponentScriptCompletion(document, position);
28
32
  if (frontmatter)
29
33
  items.push(frontmatter);
30
34
  }
31
- const html = document.html;
32
- const offset = document.offsetAt(position);
33
- const node = html.findNodeAt(offset);
34
- if ((0, utils_1.isInComponentStartTag)(html, offset) && !(0, utils_1.isInsideExpression)(document.getText(), node.start, offset)) {
35
+ if ((0, utils_1.isInComponentStartTag)(html, offset) && !insideExpression) {
35
36
  const { completions: props, componentFilePath } = await this.getPropCompletionsAndFilePath(document, position, completionContext);
36
37
  if (props.length) {
37
38
  items.push(...props);
@@ -50,7 +51,7 @@ class CompletionsProviderImpl {
50
51
  label: '---',
51
52
  sortText: '\0',
52
53
  preselect: true,
53
- detail: 'Component script',
54
+ detail: 'Create component script block',
54
55
  insertTextFormat: vscode_languageserver_1.InsertTextFormat.Snippet,
55
56
  commitCharacters: [],
56
57
  };
@@ -65,11 +66,18 @@ class CompletionsProviderImpl {
65
66
  };
66
67
  }
67
68
  if (document.astroMeta.frontmatter.state === 'open') {
69
+ let insertText = '---';
70
+ // If the current line is a full component script starter/ender, the user expects a full frontmatter
71
+ // completion and not just a completion for "---" on the same line (which result in, well, nothing)
72
+ if (prefix === '---') {
73
+ insertText = '---\n$0\n---';
74
+ }
68
75
  return {
69
76
  ...base,
70
- insertText: '---',
77
+ insertText,
78
+ detail: insertText === '---' ? 'Close component script block' : 'Create component script block',
71
79
  textEdit: prefix.match(/^\s*\-+/)
72
- ? vscode_languageserver_1.TextEdit.replace({ start: { ...position, character: 0 }, end: position }, '---')
80
+ ? vscode_languageserver_1.TextEdit.replace({ start: { ...position, character: 0 }, end: position }, insertText)
73
81
  : undefined,
74
82
  };
75
83
  }
@@ -63,8 +63,8 @@ class HTMLPlugin {
63
63
  items: [],
64
64
  };
65
65
  const emmetConfig = await this.configManager.getEmmetConfig(document);
66
- const extensionConfig = await this.configManager.getConfig('astro', document.uri);
67
- if (extensionConfig.html.completions.emmet) {
66
+ const extensionConfig = (await this.configManager.getConfig('astro', document.uri)) ?? {};
67
+ if (extensionConfig?.html?.completions?.emmet) {
68
68
  this.lang.setCompletionParticipants([
69
69
  {
70
70
  onHtmlContent: () => (emmetResults = (0, emmet_helper_1.doComplete)(document, position, 'html', emmetConfig) || emmetResults),
@@ -7,12 +7,12 @@ export declare enum DiagnosticCodes {
7
7
  SPREAD_EXPECTED = 1005,
8
8
  DUPLICATED_JSX_ATTRIBUTES = 17001,
9
9
  MUST_HAVE_PARENT_ELEMENT = 2657,
10
- CANNOT_IMPORT_TS_EXT = 2691,
11
10
  CANT_RETURN_OUTSIDE_FUNC = 1108,
12
11
  ISOLATED_MODULE_COMPILE_ERR = 1208,
13
12
  TYPE_NOT_ASSIGNABLE = 2322,
14
13
  JSX_NO_CLOSING_TAG = 17008,
15
- NO_DECL_IMPLICIT_ANY_TYPE = 7016
14
+ NO_DECL_IMPLICIT_ANY_TYPE = 7016,
15
+ JSX_ELEMENT_NO_CALL = 2604
16
16
  }
17
17
  export declare class DiagnosticsProviderImpl implements DiagnosticsProvider {
18
18
  private readonly languageServiceManager;
@@ -15,12 +15,12 @@ var DiagnosticCodes;
15
15
  DiagnosticCodes[DiagnosticCodes["SPREAD_EXPECTED"] = 1005] = "SPREAD_EXPECTED";
16
16
  DiagnosticCodes[DiagnosticCodes["DUPLICATED_JSX_ATTRIBUTES"] = 17001] = "DUPLICATED_JSX_ATTRIBUTES";
17
17
  DiagnosticCodes[DiagnosticCodes["MUST_HAVE_PARENT_ELEMENT"] = 2657] = "MUST_HAVE_PARENT_ELEMENT";
18
- DiagnosticCodes[DiagnosticCodes["CANNOT_IMPORT_TS_EXT"] = 2691] = "CANNOT_IMPORT_TS_EXT";
19
18
  DiagnosticCodes[DiagnosticCodes["CANT_RETURN_OUTSIDE_FUNC"] = 1108] = "CANT_RETURN_OUTSIDE_FUNC";
20
19
  DiagnosticCodes[DiagnosticCodes["ISOLATED_MODULE_COMPILE_ERR"] = 1208] = "ISOLATED_MODULE_COMPILE_ERR";
21
20
  DiagnosticCodes[DiagnosticCodes["TYPE_NOT_ASSIGNABLE"] = 2322] = "TYPE_NOT_ASSIGNABLE";
22
21
  DiagnosticCodes[DiagnosticCodes["JSX_NO_CLOSING_TAG"] = 17008] = "JSX_NO_CLOSING_TAG";
23
22
  DiagnosticCodes[DiagnosticCodes["NO_DECL_IMPLICIT_ANY_TYPE"] = 7016] = "NO_DECL_IMPLICIT_ANY_TYPE";
23
+ DiagnosticCodes[DiagnosticCodes["JSX_ELEMENT_NO_CALL"] = 2604] = "JSX_ELEMENT_NO_CALL";
24
24
  })(DiagnosticCodes = exports.DiagnosticCodes || (exports.DiagnosticCodes = {}));
25
25
  class DiagnosticsProviderImpl {
26
26
  constructor(languageServiceManager) {
@@ -53,7 +53,6 @@ class DiagnosticsProviderImpl {
53
53
  code: diagnostic.code,
54
54
  tags: getDiagnosticTag(diagnostic),
55
55
  }))
56
- .filter(isNoCantEndWithTS)
57
56
  .map(mapRange(scriptTagSnapshot, document));
58
57
  scriptDiagnostics.push(...scriptDiagnostic);
59
58
  });
@@ -189,10 +188,6 @@ function isNoSpreadExpected(diagnostic, document) {
189
188
  }
190
189
  return true;
191
190
  }
192
- /** Inside script tags, Astro currently require the `.ts` file extension for imports */
193
- function isNoCantEndWithTS(diagnostic) {
194
- return diagnostic.code !== DiagnosticCodes.CANNOT_IMPORT_TS_EXT;
195
- }
196
191
  /**
197
192
  * Ignore "Can't return outside of function body"
198
193
  * Since the frontmatter is at the top level, users trying to return a Response for SSR mode run into this
@@ -218,6 +213,15 @@ function enhanceIfNecessary(diagnostic) {
218
213
  message: diagnostic.message.replace('JSX', 'HTML'),
219
214
  };
220
215
  }
216
+ // JSX Element can't be constructed or called. This happens on syntax errors / invalid components
217
+ if (diagnostic.code === DiagnosticCodes.JSX_ELEMENT_NO_CALL) {
218
+ return {
219
+ ...diagnostic,
220
+ message: diagnostic.message
221
+ .replace('JSX element type', 'Component')
222
+ .replace('does not have any construct or call signatures.', 'is not a valid component.\n\nIf this is a Svelte or Vue component, it might have a syntax error that makes it impossible to parse.'),
223
+ };
224
+ }
221
225
  // For the rare case where an user might try to put a client directive on something that is not a component
222
226
  if (diagnostic.code === DiagnosticCodes.TYPE_NOT_ASSIGNABLE) {
223
227
  if (diagnostic.message.includes("Property 'client:") && diagnostic.message.includes("to type 'HTMLProps")) {
@@ -227,14 +231,6 @@ function enhanceIfNecessary(diagnostic) {
227
231
  };
228
232
  }
229
233
  }
230
- // An import path cannot end with '.ts(x)' consider importing with no extension
231
- // TODO: Remove this when https://github.com/withastro/astro/issues/3415 is fixed
232
- if (diagnostic.code === DiagnosticCodes.CANNOT_IMPORT_TS_EXT) {
233
- return {
234
- ...diagnostic,
235
- message: diagnostic.message.replace(/\.jsx?/, ''),
236
- };
237
- }
238
234
  return diagnostic;
239
235
  }
240
236
  function getDiagnosticTag(diagnostic) {
@@ -141,11 +141,10 @@ async function createLanguageService(tsconfigPath, docContext, workspaceUris) {
141
141
  }
142
142
  const newSnapshot = DocumentSnapshotUtils.createFromDocument(document);
143
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);
144
+ const scriptTagSnapshots = createScriptTagsSnapshots(filePath, document);
145
+ scriptTagSnapshots.forEach((snapshot) => {
146
+ snapshotManager.set(snapshot.filePath, snapshot);
147
+ newSnapshot.scriptTagSnapshots?.push(snapshot);
149
148
  });
150
149
  if (prevSnapshot && prevSnapshot.scriptKind !== newSnapshot.scriptKind) {
151
150
  // Restart language service as it doesn't handle script kind changes.
@@ -176,11 +175,10 @@ async function createLanguageService(tsconfigPath, docContext, workspaceUris) {
176
175
  // If we needed to create an Astro snapshot, also create its script tags snapshots
177
176
  if ((0, utils_2.isAstroFilePath)(fileName)) {
178
177
  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);
178
+ const scriptTagSnapshots = createScriptTagsSnapshots(fileName, document);
179
+ scriptTagSnapshots.forEach((snapshot) => {
180
+ snapshotManager.set(snapshot.filePath, snapshot);
181
+ doc.scriptTagSnapshots?.push(snapshot);
184
182
  });
185
183
  }
186
184
  return doc;
@@ -202,6 +200,14 @@ async function createLanguageService(tsconfigPath, docContext, workspaceUris) {
202
200
  }
203
201
  snapshotManager.updateNonAstroFile(fileName, changes);
204
202
  }
203
+ function createScriptTagsSnapshots(fileName, document) {
204
+ return document.scriptTags.map((scriptTag, index) => {
205
+ const scriptTagLanguage = (0, utils_2.getScriptTagLanguage)(scriptTag);
206
+ const scriptFilePath = fileName + `.__script${index}.${scriptTagLanguage}`;
207
+ const scriptSnapshot = new DocumentSnapshot_1.ScriptTagDocumentSnapshot(scriptTag, document, scriptFilePath);
208
+ return scriptSnapshot;
209
+ });
210
+ }
205
211
  function getParsedTSConfig() {
206
212
  let configJson = (tsconfigPath && typescript_1.default.readConfigFile(tsconfigPath, typescript_1.default.sys.readFile).config) || {};
207
213
  // If our user has types in their config but it doesn't include the types needed for Astro, add them to the config
@@ -16,12 +16,12 @@ const utils_1 = require("../../utils");
16
16
  function replaceLinks(text) {
17
17
  return (text
18
18
  // Http(s) links
19
- .replace(/\{@(link|linkplain|linkcode) (https?:\/\/[^ |}]+?)(?:[| ]([^{}\n]+?))?\}/gi, (_, tag, link, text) => {
19
+ .replace(/\{@(link|linkplain|linkcode) (https?:\/\/[^ |}]+?)(?:[| ]([^{}\n]+?))?\}/gi, (_, tag, link, label) => {
20
20
  switch (tag) {
21
21
  case 'linkcode':
22
- return `[\`${text ? text.trim() : link}\`](${link})`;
22
+ return `[\`${label ? label.trim() : link}\`](${link})`;
23
23
  default:
24
- return `[${text ? text.trim() : link}](${link})`;
24
+ return `[${label ? label.trim() : link}](${link})`;
25
25
  }
26
26
  }));
27
27
  }
@@ -1,6 +1,6 @@
1
1
  import ts from 'typescript';
2
2
  import { CompletionItemKind, DiagnosticSeverity, Position, Range, SymbolKind, SemanticTokensLegend } from 'vscode-languageserver';
3
- import { AstroDocument } from '../../core/documents';
3
+ import { AstroDocument, TagInformation } from '../../core/documents';
4
4
  import { AstroSnapshot, ScriptTagDocumentSnapshot, SnapshotFragment } from './snapshots/DocumentSnapshot';
5
5
  import { Node } from 'vscode-html-languageservice';
6
6
  export declare const enum TokenType {
@@ -59,6 +59,10 @@ export declare function toVirtualFilePath(filePath: string): string;
59
59
  export declare function toRealAstroFilePath(filePath: string): string;
60
60
  export declare function ensureRealAstroFilePath(filePath: string): string;
61
61
  export declare function ensureRealFilePath(filePath: string): string;
62
+ /**
63
+ * Return if a script tag is TypeScript or JavaScript
64
+ */
65
+ export declare function getScriptTagLanguage(scriptTag: TagInformation): 'js' | 'ts';
62
66
  export declare function getScriptTagSnapshot(snapshot: AstroSnapshot, document: AstroDocument, tagInfo: Node | {
63
67
  start: number;
64
68
  end: number;
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.getScriptTagSnapshot = exports.ensureRealFilePath = exports.ensureRealAstroFilePath = exports.toRealAstroFilePath = exports.toVirtualFilePath = exports.toVirtualAstroFilePath = exports.isVirtualFilePath = exports.isVirtualSvelteFilePath = exports.isVirtualVueFilePath = exports.isVirtualAstroFilePath = exports.isFrameworkFilePath = exports.isAstroFilePath = exports.isVirtualFrameworkFilePath = exports.getFrameworkFromFilePath = exports.removeAstroComponentSuffix = exports.checkEndOfFileCodeInsert = exports.ensureFrontmatterInsert = exports.convertToLocationRange = exports.convertRange = exports.mapSeverity = exports.getScriptKindFromFileName = exports.isSubPath = exports.findTsConfigPath = exports.getExtensionFromScriptKind = exports.getCommitCharactersForScriptElement = exports.scriptElementKindToCompletionItemKind = exports.symbolKindFromString = exports.getSemanticTokenLegend = void 0;
6
+ exports.getScriptTagSnapshot = exports.getScriptTagLanguage = exports.ensureRealFilePath = exports.ensureRealAstroFilePath = exports.toRealAstroFilePath = exports.toVirtualFilePath = exports.toVirtualAstroFilePath = exports.isVirtualFilePath = exports.isVirtualSvelteFilePath = exports.isVirtualVueFilePath = exports.isVirtualAstroFilePath = exports.isFrameworkFilePath = exports.isAstroFilePath = exports.isVirtualFrameworkFilePath = exports.getFrameworkFromFilePath = exports.removeAstroComponentSuffix = exports.checkEndOfFileCodeInsert = exports.ensureFrontmatterInsert = exports.convertToLocationRange = exports.convertRange = exports.mapSeverity = exports.getScriptKindFromFileName = exports.isSubPath = exports.findTsConfigPath = exports.getExtensionFromScriptKind = exports.getCommitCharactersForScriptElement = exports.scriptElementKindToCompletionItemKind = exports.symbolKindFromString = exports.getSemanticTokenLegend = void 0;
7
7
  const typescript_1 = __importDefault(require("typescript"));
8
8
  const path_1 = require("path");
9
9
  const utils_1 = require("../../utils");
@@ -346,9 +346,21 @@ function ensureRealFilePath(filePath) {
346
346
  }
347
347
  }
348
348
  exports.ensureRealFilePath = ensureRealFilePath;
349
+ /**
350
+ * Return if a script tag is TypeScript or JavaScript
351
+ */
352
+ function getScriptTagLanguage(scriptTag) {
353
+ // Using any kind of attributes on the script tag will disable hoisting, so we can just check if there's any
354
+ if (Object.entries(scriptTag.attributes).length === 0) {
355
+ return 'ts';
356
+ }
357
+ return 'js';
358
+ }
359
+ exports.getScriptTagLanguage = getScriptTagLanguage;
349
360
  function getScriptTagSnapshot(snapshot, document, tagInfo, position) {
350
361
  const index = document.scriptTags.findIndex((value) => value.container.start == tagInfo.start);
351
- const scriptFilePath = snapshot.filePath + `.__script${index}.js`;
362
+ const scriptTagLanguage = getScriptTagLanguage(document.scriptTags[index]);
363
+ const scriptFilePath = snapshot.filePath + `.__script${index}.${scriptTagLanguage}`;
352
364
  const scriptTagSnapshot = snapshot.scriptTagSnapshots[index];
353
365
  let offset = 0;
354
366
  if (position) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrojs/language-server",
3
- "version": "0.19.1",
3
+ "version": "0.19.4",
4
4
  "author": "withastro",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",
@@ -19,8 +19,8 @@
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.0",
23
- "@astrojs/svelte-language-integration": "^0.1.5",
22
+ "@astrojs/vue-language-integration": "^0.1.1",
23
+ "@astrojs/svelte-language-integration": "^0.1.6",
24
24
  "@vscode/emmet-helper": "^2.8.4",
25
25
  "lodash": "^4.17.21",
26
26
  "source-map": "^0.7.3",