@astrojs/language-server 2.5.5 → 2.6.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/dist/check.d.ts CHANGED
@@ -21,7 +21,6 @@ export declare class AstroCheck {
21
21
  private ts;
22
22
  project: ReturnType<typeof kit.createProject>;
23
23
  private linter;
24
- private skipJSX;
25
24
  constructor(workspacePath: string, typescriptPath: string | undefined, tsconfigPath: string | undefined);
26
25
  /**
27
26
  * Lint a list of files or the entire project and optionally log the errors found
package/dist/check.js CHANGED
@@ -45,8 +45,6 @@ class AstroCheck {
45
45
  this.workspacePath = workspacePath;
46
46
  this.typescriptPath = typescriptPath;
47
47
  this.tsconfigPath = tsconfigPath;
48
- // TODO: Remove this when the JSX typing issue is fixed
49
- this.skipJSX = false;
50
48
  this.initialize();
51
49
  }
52
50
  /**
@@ -57,9 +55,6 @@ class AstroCheck {
57
55
  */
58
56
  async lint({ fileNames = undefined, cancel = () => false, logErrors = undefined, }) {
59
57
  let files = fileNames !== undefined ? fileNames : this.project.languageHost.getScriptFileNames();
60
- if (this.skipJSX) {
61
- files = files.filter((file) => !file.endsWith('.jsx') && !file.endsWith('.tsx'));
62
- }
63
58
  const result = {
64
59
  status: undefined,
65
60
  fileChecked: 0,
@@ -139,11 +134,6 @@ class AstroCheck {
139
134
  });
140
135
  });
141
136
  }
142
- const files = this.project.languageHost.getScriptFileNames();
143
- if (files.some((file) => file.endsWith('.jsx') || file.endsWith('.tsx'))) {
144
- console.warn('\x1b[33;1mWARNING:\x1b[0m Checking `.jsx` and `.tsx` files is temporarily disabled due to an issue in the Astro language server and TypeScript. See https://github.com/withastro/language-tools/issues/727 for more details. In the meantime, such files can be checked using `tsc --noEmit`.');
145
- this.skipJSX = true;
146
- }
147
137
  this.linter = kit.createLinter(config, this.project.languageHost);
148
138
  }
149
139
  getTsconfig() {
@@ -1,8 +1,27 @@
1
+ import type { ConvertToTSXOptions, DiagnosticMessage, TSXResult } from '@astrojs/compiler/types';
1
2
  import { VirtualFile } from '@volar/language-core';
3
+ import { Range } from '@volar/language-server';
2
4
  import { HTMLDocument } from 'vscode-html-languageservice';
3
- export declare function astro2tsx(input: string, fileName: string, ts: typeof import('typescript/lib/tsserverlibrary.js'), htmlDocument: HTMLDocument): {
5
+ export interface LSPTSXRanges {
6
+ frontmatter: Range;
7
+ body: Range;
8
+ }
9
+ interface Astro2TSXResult {
4
10
  virtualFile: VirtualFile;
5
- diagnostics: import("@astrojs/compiler").DiagnosticMessage[] | {
11
+ diagnostics: DiagnosticMessage[];
12
+ ranges: LSPTSXRanges;
13
+ }
14
+ export declare function safeConvertToTSX(content: string, options: ConvertToTSXOptions): TSXResult | {
15
+ code: string;
16
+ map: {
17
+ file: string;
18
+ sources: never[];
19
+ sourcesContent: never[];
20
+ names: never[];
21
+ mappings: string;
22
+ version: number;
23
+ };
24
+ diagnostics: {
6
25
  code: 1000;
7
26
  location: {
8
27
  file: string;
@@ -13,4 +32,17 @@ export declare function astro2tsx(input: string, fileName: string, ts: typeof im
13
32
  severity: 1;
14
33
  text: string;
15
34
  }[];
35
+ metaRanges: {
36
+ frontmatter: {
37
+ start: number;
38
+ end: number;
39
+ };
40
+ body: {
41
+ start: number;
42
+ end: number;
43
+ };
44
+ };
16
45
  };
46
+ export declare function getTSXRangesAsLSPRanges(tsx: TSXResult): LSPTSXRanges;
47
+ export declare function astro2tsx(input: string, fileName: string, ts: typeof import('typescript/lib/tsserverlibrary.js'), htmlDocument: HTMLDocument): Astro2TSXResult;
48
+ export {};
@@ -1,9 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.astro2tsx = void 0;
3
+ exports.astro2tsx = exports.getTSXRangesAsLSPRanges = exports.safeConvertToTSX = void 0;
4
4
  const sync_1 = require("@astrojs/compiler/sync");
5
5
  const sourcemap_codec_1 = require("@jridgewell/sourcemap-codec");
6
6
  const language_core_1 = require("@volar/language-core");
7
+ const language_server_1 = require("@volar/language-server");
7
8
  const vscode_html_languageservice_1 = require("vscode-html-languageservice");
8
9
  const utils_js_1 = require("./utils.js");
9
10
  function safeConvertToTSX(content, options) {
@@ -31,14 +32,34 @@ function safeConvertToTSX(content, options) {
31
32
  text: `The Astro compiler encountered an unknown error while transform this file to TSX. Please create an issue with your code and the error shown in the server's logs: https://github.com/withastro/language-tools/issues`,
32
33
  },
33
34
  ],
35
+ metaRanges: {
36
+ frontmatter: {
37
+ start: 0,
38
+ end: 0,
39
+ },
40
+ body: {
41
+ start: 0,
42
+ end: 0,
43
+ },
44
+ },
34
45
  };
35
46
  }
36
47
  }
48
+ exports.safeConvertToTSX = safeConvertToTSX;
49
+ function getTSXRangesAsLSPRanges(tsx) {
50
+ const textDocument = vscode_html_languageservice_1.TextDocument.create('', 'typescriptreact', 0, tsx.code);
51
+ return {
52
+ frontmatter: language_server_1.Range.create(textDocument.positionAt(tsx.metaRanges.frontmatter.start), textDocument.positionAt(tsx.metaRanges.frontmatter.end)),
53
+ body: language_server_1.Range.create(textDocument.positionAt(tsx.metaRanges.body.start), textDocument.positionAt(tsx.metaRanges.body.end)),
54
+ };
55
+ }
56
+ exports.getTSXRangesAsLSPRanges = getTSXRangesAsLSPRanges;
37
57
  function astro2tsx(input, fileName, ts, htmlDocument) {
38
58
  const tsx = safeConvertToTSX(input, { filename: fileName });
39
59
  return {
40
60
  virtualFile: getVirtualFileTSX(input, tsx, fileName, ts, htmlDocument),
41
61
  diagnostics: tsx.diagnostics,
62
+ ranges: getTSXRangesAsLSPRanges(tsx),
42
63
  };
43
64
  }
44
65
  exports.astro2tsx = astro2tsx;
@@ -1,9 +1,9 @@
1
- import type { DiagnosticMessage, ParseResult } from '@astrojs/compiler/types';
1
+ import type { DiagnosticMessage } from '@astrojs/compiler/types';
2
2
  import { FileCapabilities, FileKind, type Language, type VirtualFile } from '@volar/language-core';
3
3
  import type ts from 'typescript/lib/tsserverlibrary';
4
4
  import type { HTMLDocument } from 'vscode-html-languageservice';
5
5
  import type { AstroInstall } from '../utils.js';
6
- import { FrontmatterStatus } from './parseAstro';
6
+ import { AstroMetadata } from './parseAstro';
7
7
  export declare function getLanguageModule(astroInstall: AstroInstall | undefined, ts: typeof import('typescript/lib/tsserverlibrary.js')): Language<AstroFile>;
8
8
  export declare class AstroFile implements VirtualFile {
9
9
  sourceFileName: string;
@@ -14,9 +14,7 @@ export declare class AstroFile implements VirtualFile {
14
14
  fileName: string;
15
15
  mappings: VirtualFile['mappings'];
16
16
  embeddedFiles: VirtualFile['embeddedFiles'];
17
- astroMeta: ParseResult & {
18
- frontmatter: FrontmatterStatus;
19
- };
17
+ astroMeta: AstroMetadata;
20
18
  compilerDiagnostics: DiagnosticMessage[];
21
19
  htmlDocument: HTMLDocument;
22
20
  scriptFiles: string[];
@@ -54,13 +54,34 @@ function getLanguageModule(astroInstall, ts) {
54
54
  return host.resolveModuleName?.(moduleName, impliedNodeFormat) ?? moduleName;
55
55
  },
56
56
  getScriptFileNames() {
57
+ let languageServerDirectory;
58
+ try {
59
+ languageServerDirectory = path.dirname(require.resolve('@astrojs/language-server'));
60
+ }
61
+ catch (e) {
62
+ languageServerDirectory = __dirname;
63
+ }
57
64
  const fileNames = host.getScriptFileNames();
58
- return [
59
- ...fileNames,
60
- ...(astroInstall
61
- ? ['./env.d.ts', './astro-jsx.d.ts'].map((filePath) => ts.sys.resolvePath(path.resolve(astroInstall.path, filePath)))
62
- : []),
63
- ];
65
+ const addedFileNames = [];
66
+ if (astroInstall) {
67
+ addedFileNames.push(...['./env.d.ts', './astro-jsx.d.ts'].map((filePath) => ts.sys.resolvePath(path.resolve(astroInstall.path, filePath))));
68
+ // If Astro version is < 4.0.8, add jsx-runtime-augment.d.ts to the files to fake `JSX` being available from "astro/jsx-runtime".
69
+ // TODO: Remove this once a majority of users are on Astro 4.0.8+, erika - 2023-12-28
70
+ if (astroInstall.version.major >= 4 &&
71
+ (astroInstall.version.minor > 0 || astroInstall.version.patch >= 8)) {
72
+ addedFileNames.push(...['../jsx-runtime-augment.d.ts'].map((filePath) => ts.sys.resolvePath(path.resolve(languageServerDirectory, filePath))));
73
+ }
74
+ }
75
+ else {
76
+ // If we don't have an Astro installation, add the fallback types from the language server.
77
+ // See the README in packages/language-server/types for more information.
78
+ addedFileNames.push(...[
79
+ '../types/env.d.ts',
80
+ '../types/astro-jsx.d.ts',
81
+ '../types/jsx-runtime-fallback.d.ts',
82
+ ].map((f) => ts.sys.resolvePath(path.resolve(languageServerDirectory, f))));
83
+ }
84
+ return [...fileNames, ...addedFileNames];
64
85
  },
65
86
  getCompilationSettings() {
66
87
  const baseCompilationSettings = host.getCompilationSettings();
@@ -69,8 +90,6 @@ function getLanguageModule(astroInstall, ts) {
69
90
  module: ts.ModuleKind.ESNext ?? 99,
70
91
  target: ts.ScriptTarget.ESNext ?? 99,
71
92
  jsx: ts.JsxEmit.Preserve ?? 1,
72
- jsxImportSource: undefined,
73
- jsxFactory: 'astroHTML',
74
93
  resolveJsonModule: true,
75
94
  allowJs: true,
76
95
  isolatedModules: true,
@@ -112,20 +131,21 @@ class AstroFile {
112
131
  },
113
132
  ];
114
133
  this.compilerDiagnostics = [];
115
- this.astroMeta = (0, parseAstro_1.getAstroMetadata)(this.fileName, this.snapshot.getText(0, this.snapshot.getLength()));
116
- if (this.astroMeta.diagnostics.length > 0) {
117
- this.compilerDiagnostics.push(...this.astroMeta.diagnostics);
134
+ const astroMetadata = (0, parseAstro_1.getAstroMetadata)(this.fileName, this.snapshot.getText(0, this.snapshot.getLength()));
135
+ if (astroMetadata.diagnostics.length > 0) {
136
+ this.compilerDiagnostics.push(...astroMetadata.diagnostics);
118
137
  }
119
- const { htmlDocument, virtualFile: htmlVirtualFile } = (0, parseHTML_1.parseHTML)(this.fileName, this.snapshot, this.astroMeta.frontmatter.status === 'closed'
120
- ? this.astroMeta.frontmatter.position.end.offset
138
+ const { htmlDocument, virtualFile: htmlVirtualFile } = (0, parseHTML_1.parseHTML)(this.fileName, this.snapshot, astroMetadata.frontmatter.status === 'closed'
139
+ ? astroMetadata.frontmatter.position.end.offset
121
140
  : 0);
122
141
  this.htmlDocument = htmlDocument;
123
- const scriptTags = (0, parseJS_js_1.extractScriptTags)(this.fileName, this.snapshot, htmlDocument, this.astroMeta.ast);
142
+ const scriptTags = (0, parseJS_js_1.extractScriptTags)(this.fileName, this.snapshot, htmlDocument, astroMetadata.ast);
124
143
  this.scriptFiles = scriptTags.map((scriptTag) => scriptTag.fileName);
125
- htmlVirtualFile.embeddedFiles.push(...(0, parseCSS_1.extractStylesheets)(this.fileName, this.snapshot, htmlDocument, this.astroMeta.ast), ...scriptTags);
144
+ htmlVirtualFile.embeddedFiles.push(...(0, parseCSS_1.extractStylesheets)(this.fileName, this.snapshot, htmlDocument, astroMetadata.ast), ...scriptTags);
126
145
  this.embeddedFiles = [];
127
146
  this.embeddedFiles.push(htmlVirtualFile);
128
147
  const tsx = (0, astro2tsx_1.astro2tsx)(this.snapshot.getText(0, this.snapshot.getLength()), this.fileName, this.ts, htmlDocument);
148
+ this.astroMeta = { ...astroMetadata, tsxRanges: tsx.ranges };
129
149
  this.compilerDiagnostics.push(...tsx.diagnostics);
130
150
  this.embeddedFiles.push(tsx.virtualFile);
131
151
  }
@@ -1,8 +1,10 @@
1
1
  import type { ParseOptions, ParseResult, Point } from '@astrojs/compiler/types';
2
- type AstroMetadata = ParseResult & {
2
+ import type { LSPTSXRanges } from './astro2tsx.js';
3
+ export type AstroMetadata = ParseResult & {
3
4
  frontmatter: FrontmatterStatus;
5
+ tsxRanges: LSPTSXRanges;
4
6
  };
5
- export declare function getAstroMetadata(fileName: string, input: string, options?: ParseOptions): AstroMetadata;
7
+ export declare function getAstroMetadata(fileName: string, input: string, options?: ParseOptions): Omit<AstroMetadata, 'tsxRanges'>;
6
8
  interface FrontmatterOpen {
7
9
  status: 'open';
8
10
  position: {
@@ -0,0 +1,3 @@
1
+ import type { CodeAction, ServiceContext } from '@volar/language-service';
2
+ export declare function enhancedProvideCodeActions(codeActions: CodeAction[], context: ServiceContext): CodeAction[];
3
+ export declare function enhancedResolveCodeAction(codeAction: CodeAction, context: ServiceContext): CodeAction;
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.enhancedResolveCodeAction = exports.enhancedProvideCodeActions = void 0;
4
+ const language_server_1 = require("@volar/language-server");
5
+ const index_js_1 = require("../../core/index.js");
6
+ const utils_js_1 = require("../utils.js");
7
+ function enhancedProvideCodeActions(codeActions, context) {
8
+ return codeActions.map((codeAction) => mapCodeAction(codeAction, context));
9
+ }
10
+ exports.enhancedProvideCodeActions = enhancedProvideCodeActions;
11
+ function enhancedResolveCodeAction(codeAction, context) {
12
+ /**
13
+ * TypeScript code actions don't come through here, as they're considered to be already fully resolved
14
+ * A lot of the code actions we'll encounter here are more tricky ones, such as fixAll or refactor
15
+ * For now, it seems like we don't need to do anything special here, but we'll keep this function around
16
+ */
17
+ return mapCodeAction(codeAction, context);
18
+ }
19
+ exports.enhancedResolveCodeAction = enhancedResolveCodeAction;
20
+ function mapCodeAction(codeAction, context) {
21
+ if (!codeAction.edit || !codeAction.edit.documentChanges)
22
+ return codeAction;
23
+ codeAction.edit.documentChanges = codeAction.edit.documentChanges.map((change) => {
24
+ if (language_server_1.TextDocumentEdit.is(change)) {
25
+ const [virtualFile, source] = context.documents.getVirtualFileByUri(change.textDocument.uri);
26
+ const file = source?.root;
27
+ if (!virtualFile || !(file instanceof index_js_1.AstroFile) || !context.host)
28
+ return change;
29
+ change.edits = change.edits.map((edit) => {
30
+ const shouldModifyEdit = (0, utils_js_1.editShouldBeInFrontmatter)(edit.range, file.astroMeta);
31
+ if (shouldModifyEdit.itShould) {
32
+ edit = (0, utils_js_1.ensureProperEditForFrontmatter)(edit, file.astroMeta, '\n');
33
+ }
34
+ return edit;
35
+ });
36
+ }
37
+ return change;
38
+ });
39
+ return codeAction;
40
+ }
41
+ //# sourceMappingURL=codeActions.js.map
@@ -1,3 +1,3 @@
1
- import { CompletionItem, CompletionList } from '@volar/language-server';
1
+ import { CompletionItem, CompletionList, ServiceContext } from '@volar/language-server';
2
2
  export declare function enhancedProvideCompletionItems(completions: CompletionList): CompletionList;
3
- export declare function enhancedResolveCompletionItem(resolvedCompletion: CompletionItem): CompletionItem;
3
+ export declare function enhancedResolveCompletionItem(resolvedCompletion: CompletionItem, context: ServiceContext): CompletionItem;
@@ -2,6 +2,8 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.enhancedResolveCompletionItem = exports.enhancedProvideCompletionItems = void 0;
4
4
  const language_server_1 = require("@volar/language-server");
5
+ const index_js_1 = require("../../core/index.js");
6
+ const utils_js_1 = require("../utils.js");
5
7
  function enhancedProvideCompletionItems(completions) {
6
8
  completions.items = completions.items.filter(isValidCompletion).map((completion) => {
7
9
  const source = completion?.data?.originalItem?.source;
@@ -23,11 +25,23 @@ function enhancedProvideCompletionItems(completions) {
23
25
  return completions;
24
26
  }
25
27
  exports.enhancedProvideCompletionItems = enhancedProvideCompletionItems;
26
- function enhancedResolveCompletionItem(resolvedCompletion) {
28
+ function enhancedResolveCompletionItem(resolvedCompletion, context) {
27
29
  // Make sure we keep our icons even when the completion is resolved
28
30
  if (resolvedCompletion.data.isComponent) {
29
31
  resolvedCompletion.detail = getDetailForFileCompletion(resolvedCompletion.detail ?? '', resolvedCompletion.data.originalItem.source);
30
32
  }
33
+ if (resolvedCompletion.additionalTextEdits) {
34
+ const [virtualFile, source] = context.documents.getVirtualFileByUri(resolvedCompletion.data.uri);
35
+ const file = source?.root;
36
+ if (!virtualFile || !(file instanceof index_js_1.AstroFile) || !context.host)
37
+ return resolvedCompletion;
38
+ resolvedCompletion.additionalTextEdits = resolvedCompletion.additionalTextEdits.map((edit) => {
39
+ if ((0, utils_js_1.editShouldBeInFrontmatter)(edit.range, file.astroMeta).itShould) {
40
+ edit = (0, utils_js_1.ensureProperEditForFrontmatter)(edit, file.astroMeta, '\n');
41
+ }
42
+ return edit;
43
+ });
44
+ }
31
45
  return resolvedCompletion;
32
46
  }
33
47
  exports.enhancedResolveCompletionItem = enhancedResolveCompletionItem;
@@ -9,4 +9,4 @@ export declare enum DiagnosticCodes {
9
9
  JSX_NO_CLOSING_TAG = 17008,
10
10
  JSX_ELEMENT_NO_CALL = 2604
11
11
  }
12
- export declare function enhancedProvideSemanticDiagnostics(originalDiagnostics: Diagnostic[], astroLineCount?: number | undefined): Diagnostic[];
12
+ export declare function enhancedProvideSemanticDiagnostics(originalDiagnostics: Diagnostic[], tsxLineCount?: number | undefined): Diagnostic[];
@@ -14,13 +14,13 @@ var DiagnosticCodes;
14
14
  DiagnosticCodes[DiagnosticCodes["JSX_NO_CLOSING_TAG"] = 17008] = "JSX_NO_CLOSING_TAG";
15
15
  DiagnosticCodes[DiagnosticCodes["JSX_ELEMENT_NO_CALL"] = 2604] = "JSX_ELEMENT_NO_CALL";
16
16
  })(DiagnosticCodes || (exports.DiagnosticCodes = DiagnosticCodes = {}));
17
- function enhancedProvideSemanticDiagnostics(originalDiagnostics, astroLineCount) {
17
+ function enhancedProvideSemanticDiagnostics(originalDiagnostics, tsxLineCount) {
18
18
  const diagnostics = originalDiagnostics
19
- .filter((diagnostic) => (astroLineCount ? diagnostic.range.start.line <= astroLineCount : true) &&
19
+ .filter((diagnostic) => (tsxLineCount ? diagnostic.range.start.line <= tsxLineCount : true) &&
20
20
  isNoCantReturnOutsideFunction(diagnostic) &&
21
21
  isNoIsolatedModuleError(diagnostic) &&
22
22
  isNoJsxCannotHaveMultipleAttrsError(diagnostic))
23
- .map((diag) => astroLineCount ? generalEnhancements(astroEnhancements(diag)) : generalEnhancements(diag));
23
+ .map((diag) => tsxLineCount ? generalEnhancements(astroEnhancements(diag)) : generalEnhancements(diag));
24
24
  return diagnostics;
25
25
  }
26
26
  exports.enhancedProvideSemanticDiagnostics = enhancedProvideSemanticDiagnostics;
@@ -1,2 +1,2 @@
1
- import { Service } from '@volar/language-server';
1
+ import type { Service } from '@volar/language-server';
2
2
  export declare const create: () => Service;
@@ -4,10 +4,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.create = void 0;
7
- const language_server_1 = require("@volar/language-server");
8
7
  const volar_service_typescript_1 = __importDefault(require("volar-service-typescript"));
9
8
  const index_js_1 = require("../../core/index.js");
10
- const utils_js_1 = require("../utils.js");
9
+ const codeActions_js_1 = require("./codeActions.js");
11
10
  const completions_js_1 = require("./completions.js");
12
11
  const diagnostics_js_1 = require("./diagnostics.js");
13
12
  const create = () => (context, modules) => {
@@ -21,82 +20,6 @@ const create = () => (context, modules) => {
21
20
  }
22
21
  return {
23
22
  ...typeScriptPlugin,
24
- transformCompletionItem(item) {
25
- const [_, source] = context.documents.getVirtualFileByUri(item.data.uri);
26
- const file = source?.root;
27
- if (!(file instanceof index_js_1.AstroFile) || !context.host)
28
- return undefined;
29
- if (file.scriptFiles.includes(item.data.fileName))
30
- return undefined;
31
- const newLine = context.host.getCompilationSettings().newLine?.toString() ?? '\n';
32
- if (item.additionalTextEdits) {
33
- item.additionalTextEdits = item.additionalTextEdits.map((edit) => {
34
- // HACK: There's a weird situation sometimes where some components (especially Svelte) will get imported as type imports
35
- // for some unknown reason. This work around the problem by always ensuring a normal import for components
36
- if (item.data.isComponent && edit.newText.includes('import type')) {
37
- edit.newText.replace('import type', 'import');
38
- }
39
- if ((0, utils_js_1.editShouldBeInFrontmatter)(edit.range)) {
40
- return (0, utils_js_1.ensureProperEditForFrontmatter)(edit, file.astroMeta.frontmatter, newLine);
41
- }
42
- return edit;
43
- });
44
- }
45
- return item;
46
- },
47
- transformCodeAction(item) {
48
- if (item.kind !== 'quickfix')
49
- return undefined;
50
- const originalFileName = item.data.uri.replace('.tsx', '');
51
- const [_, source] = context.documents.getVirtualFileByUri(originalFileName);
52
- const file = source?.root;
53
- if (!(file instanceof index_js_1.AstroFile) || !context.host)
54
- return undefined;
55
- if (file.scriptFiles.includes(item.diagnostics?.[0].data.documentUri.replace('file://', '')))
56
- return undefined;
57
- const document = context.getTextDocument(originalFileName);
58
- if (!document)
59
- return undefined;
60
- const newLine = context.host.getCompilationSettings().newLine?.toString() ?? '\n';
61
- if (!item.edit?.documentChanges)
62
- return undefined;
63
- item.edit.documentChanges = item.edit.documentChanges.map((change) => {
64
- if (language_server_1.TextDocumentEdit.is(change)) {
65
- change.textDocument.uri = originalFileName;
66
- if (change.edits.length === 1) {
67
- change.edits = change.edits.map((edit) => {
68
- const editInFrontmatter = (0, utils_js_1.editShouldBeInFrontmatter)(edit.range, document);
69
- if (editInFrontmatter.itShould) {
70
- return (0, utils_js_1.ensureProperEditForFrontmatter)(edit, file.astroMeta.frontmatter, newLine, editInFrontmatter.position);
71
- }
72
- return edit;
73
- });
74
- }
75
- else {
76
- if (file.astroMeta.frontmatter.status === 'closed') {
77
- change.edits = change.edits.map((edit) => {
78
- const editInFrontmatter = (0, utils_js_1.editShouldBeInFrontmatter)(edit.range, document);
79
- if (editInFrontmatter.itShould) {
80
- edit.range = (0, utils_js_1.ensureRangeIsInFrontmatter)(edit.range, file.astroMeta.frontmatter, editInFrontmatter.position);
81
- }
82
- return edit;
83
- });
84
- }
85
- else {
86
- // TODO: Handle when there's multiple edits and a new frontmatter is potentially needed
87
- if (change.edits.some((edit) => {
88
- return (0, utils_js_1.editShouldBeInFrontmatter)(edit.range, document).itShould;
89
- })) {
90
- console.error('Code actions with multiple edits that require potentially creating a frontmatter are currently not implemented. In the meantime, please manually insert a frontmatter in your file before using this code action.');
91
- change.edits = [];
92
- }
93
- }
94
- }
95
- }
96
- return change;
97
- });
98
- return item;
99
- },
100
23
  async provideCompletionItems(document, position, completionContext, token) {
101
24
  const originalCompletions = await typeScriptPlugin.provideCompletionItems(document, position, completionContext, token);
102
25
  if (!originalCompletions)
@@ -107,22 +30,35 @@ const create = () => (context, modules) => {
107
30
  const resolvedCompletionItem = await typeScriptPlugin.resolveCompletionItem(item, token);
108
31
  if (!resolvedCompletionItem)
109
32
  return item;
110
- return (0, completions_js_1.enhancedResolveCompletionItem)(resolvedCompletionItem);
33
+ return (0, completions_js_1.enhancedResolveCompletionItem)(resolvedCompletionItem, context);
34
+ },
35
+ async provideCodeActions(document, range, codeActionContext, token) {
36
+ const originalCodeActions = await typeScriptPlugin.provideCodeActions(document, range, codeActionContext, token);
37
+ if (!originalCodeActions)
38
+ return null;
39
+ return (0, codeActions_js_1.enhancedProvideCodeActions)(originalCodeActions, context);
40
+ },
41
+ async resolveCodeAction(codeAction, token) {
42
+ const resolvedCodeAction = await typeScriptPlugin.resolveCodeAction(codeAction, token);
43
+ if (!resolvedCodeAction)
44
+ return codeAction;
45
+ return (0, codeActions_js_1.enhancedResolveCodeAction)(resolvedCodeAction, context);
111
46
  },
112
47
  async provideSemanticDiagnostics(document, token) {
113
48
  const [_, source] = context.documents.getVirtualFileByUri(document.uri);
114
49
  const file = source?.root;
115
- let astroDocument = undefined;
50
+ let tsxLineCount = undefined;
116
51
  if (file instanceof index_js_1.AstroFile) {
117
52
  // If we have compiler errors, our TSX isn't valid so don't bother showing TS errors
118
53
  if (file.hasCompilationErrors)
119
54
  return null;
120
- astroDocument = context.documents.getDocumentByFileName(file.snapshot, file.sourceFileName);
55
+ // We'll use this to filter out diagnostics that are outside the mapped range of the TSX
56
+ tsxLineCount = file.astroMeta.tsxRanges.body.end.line;
121
57
  }
122
58
  const diagnostics = await typeScriptPlugin.provideSemanticDiagnostics(document, token);
123
59
  if (!diagnostics)
124
60
  return null;
125
- return (0, diagnostics_js_1.enhancedProvideSemanticDiagnostics)(diagnostics, astroDocument?.lineCount);
61
+ return (0, diagnostics_js_1.enhancedProvideSemanticDiagnostics)(diagnostics, tsxLineCount);
126
62
  },
127
63
  };
128
64
  };
@@ -1,5 +1,5 @@
1
- import { HTMLDocument, Node, Range, TextDocument, TextEdit } from 'vscode-html-languageservice';
2
- import type { FrontmatterStatus } from '../core/parseAstro.js';
1
+ import { HTMLDocument, Node, Range, TextEdit } from 'vscode-html-languageservice';
2
+ import type { AstroMetadata, FrontmatterStatus } from '../core/parseAstro.js';
3
3
  export declare function isJSDocument(languageId: string): boolean;
4
4
  /**
5
5
  * Return true if a specific node could be a component.
@@ -19,13 +19,13 @@ export declare function isInsideExpression(html: string, tagStart: number, posit
19
19
  */
20
20
  export declare function isInsideFrontmatter(offset: number, frontmatter: FrontmatterStatus): boolean;
21
21
  type FrontmatterEditPosition = 'top' | 'bottom';
22
- export declare function ensureProperEditForFrontmatter(edit: TextEdit, frontmatter: FrontmatterStatus, newLine: string, position?: FrontmatterEditPosition): TextEdit;
22
+ export declare function ensureProperEditForFrontmatter(edit: TextEdit, metadata: AstroMetadata, newLine: string, position?: FrontmatterEditPosition): TextEdit;
23
23
  /**
24
24
  * Force a range to be at the start of the frontmatter if it is outside
25
25
  */
26
- export declare function ensureRangeIsInFrontmatter(range: Range, frontmatter: FrontmatterStatus, position?: FrontmatterEditPosition): Range;
27
- export declare function getNewFrontmatterEdit(edit: TextEdit, newLine: string): TextEdit;
28
- export declare function getOpenFrontmatterEdit(edit: TextEdit, newLine: string): TextEdit;
26
+ export declare function ensureRangeIsInFrontmatter(range: Range, metadata: AstroMetadata, position?: FrontmatterEditPosition): Range;
27
+ export declare function getNewFrontmatterEdit(edit: TextEdit, astroMetadata: AstroMetadata, newLine: string): TextEdit;
28
+ export declare function getOpenFrontmatterEdit(edit: TextEdit, astroMetadata: AstroMetadata, newLine: string): TextEdit;
29
29
  type FrontmatterEditValidity = {
30
30
  itShould: false;
31
31
  position: undefined;
@@ -33,5 +33,5 @@ type FrontmatterEditValidity = {
33
33
  itShould: true;
34
34
  position: FrontmatterEditPosition;
35
35
  };
36
- export declare function editShouldBeInFrontmatter(range: Range, astroDocument?: TextDocument): FrontmatterEditValidity;
36
+ export declare function editShouldBeInFrontmatter(range: Range, astroMetadata: AstroMetadata): FrontmatterEditValidity;
37
37
  export {};
@@ -47,66 +47,64 @@ function isInsideFrontmatter(offset, frontmatter) {
47
47
  }
48
48
  }
49
49
  exports.isInsideFrontmatter = isInsideFrontmatter;
50
- function ensureProperEditForFrontmatter(edit, frontmatter, newLine, position = 'top') {
51
- switch (frontmatter.status) {
50
+ function ensureProperEditForFrontmatter(edit, metadata, newLine, position = 'top') {
51
+ switch (metadata.frontmatter.status) {
52
52
  case 'open':
53
- return getOpenFrontmatterEdit(edit, newLine);
53
+ return getOpenFrontmatterEdit(edit, metadata, newLine);
54
54
  case 'closed':
55
+ const newRange = ensureRangeIsInFrontmatter(edit.range, metadata, position);
55
56
  return {
56
- newText: edit.newText,
57
- range: ensureRangeIsInFrontmatter(edit.range, frontmatter, position),
57
+ newText: newRange.start.line === metadata.frontmatter.position.start.line &&
58
+ edit.newText.startsWith(newLine)
59
+ ? edit.newText.trimStart()
60
+ : edit.newText,
61
+ range: newRange,
58
62
  };
59
63
  case 'doesnt-exist':
60
- return getNewFrontmatterEdit(edit, newLine);
64
+ return getNewFrontmatterEdit(edit, metadata, newLine);
61
65
  }
62
66
  }
63
67
  exports.ensureProperEditForFrontmatter = ensureProperEditForFrontmatter;
64
68
  /**
65
69
  * Force a range to be at the start of the frontmatter if it is outside
66
70
  */
67
- function ensureRangeIsInFrontmatter(range, frontmatter, position = 'top') {
68
- if (frontmatter.status === 'open' || frontmatter.status === 'closed') {
69
- // Q: Why not use PointToPosition?
70
- // A: The Astro compiler returns positions at the exact line where the frontmatter is, which is not adequate for mapping
71
- // edits as we want edits *inside* the frontmatter and not on the same line, or you would end up with things like `---import ...`
72
- const frontmatterStartPosition = {
73
- line: frontmatter.position.start.line,
74
- character: frontmatter.position.start.column - 1,
75
- };
76
- const frontmatterEndPosition = frontmatter.position.end
77
- ? { line: frontmatter.position.end.line - 1, character: 0 }
71
+ function ensureRangeIsInFrontmatter(range, metadata, position = 'top') {
72
+ if (metadata.frontmatter.status === 'open' || metadata.frontmatter.status === 'closed') {
73
+ const frontmatterEndPosition = metadata.frontmatter.position.end
74
+ ? metadata.tsxRanges.frontmatter.end
78
75
  : undefined;
79
76
  // If the range start is outside the frontmatter, return a range at the start of the frontmatter
80
- if (range.start.line < frontmatterStartPosition.line ||
77
+ if (range.start.line < metadata.tsxRanges.frontmatter.start.line ||
81
78
  (frontmatterEndPosition && range.start.line > frontmatterEndPosition.line)) {
82
79
  if (frontmatterEndPosition && position === 'bottom') {
83
80
  return vscode_html_languageservice_1.Range.create(frontmatterEndPosition, frontmatterEndPosition);
84
81
  }
85
- return vscode_html_languageservice_1.Range.create(frontmatterStartPosition, frontmatterStartPosition);
82
+ return vscode_html_languageservice_1.Range.create(metadata.tsxRanges.frontmatter.start, metadata.tsxRanges.frontmatter.start);
86
83
  }
87
84
  return range;
88
85
  }
89
86
  return range;
90
87
  }
91
88
  exports.ensureRangeIsInFrontmatter = ensureRangeIsInFrontmatter;
92
- function getNewFrontmatterEdit(edit, newLine) {
93
- edit.newText = `---${newLine}${edit.newText}---${newLine}${newLine}`;
94
- edit.range = vscode_html_languageservice_1.Range.create(0, 0, 0, 0);
89
+ function getNewFrontmatterEdit(edit, astroMetadata, newLine) {
90
+ edit.newText = `---${edit.newText.startsWith(newLine) ? '' : newLine}${edit.newText}---${newLine}${newLine}`;
91
+ edit.range = vscode_html_languageservice_1.Range.create(astroMetadata.tsxRanges.frontmatter.start, astroMetadata.tsxRanges.frontmatter.start);
95
92
  return edit;
96
93
  }
97
94
  exports.getNewFrontmatterEdit = getNewFrontmatterEdit;
98
- function getOpenFrontmatterEdit(edit, newLine) {
95
+ function getOpenFrontmatterEdit(edit, astroMetadata, newLine) {
99
96
  edit.newText = edit.newText.startsWith(newLine)
100
97
  ? `${edit.newText}---`
101
98
  : `${newLine}${edit.newText}---`;
99
+ edit.range = vscode_html_languageservice_1.Range.create(astroMetadata.tsxRanges.frontmatter.start, astroMetadata.tsxRanges.frontmatter.start);
102
100
  return edit;
103
101
  }
104
102
  exports.getOpenFrontmatterEdit = getOpenFrontmatterEdit;
105
- // Most edits that are at 0:0, or outside the document are intended for the frontmatter
106
- function editShouldBeInFrontmatter(range, astroDocument) {
107
- const isAtZeroZero = range.start.line === 0 && range.start.character === 0;
108
- const isPastFile = astroDocument && range.start.line > astroDocument.lineCount;
109
- const shouldIt = isAtZeroZero || isPastFile;
103
+ // Most edits that are at the beginning of the TSX, or outside the document are intended for the frontmatter
104
+ function editShouldBeInFrontmatter(range, astroMetadata) {
105
+ const isAtTSXStart = range.start.line < astroMetadata.tsxRanges.frontmatter.start.line;
106
+ const isPastFile = range.start.line > astroMetadata.tsxRanges.body.end.line;
107
+ const shouldIt = isAtTSXStart || isPastFile;
110
108
  return shouldIt
111
109
  ? { itShould: true, position: isPastFile ? 'bottom' : 'top' }
112
110
  : { itShould: false, position: undefined };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrojs/language-server",
3
- "version": "2.5.5",
3
+ "version": "2.6.0",
4
4
  "author": "withastro",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -20,22 +20,22 @@
20
20
  "astro-ls": "./bin/nodeServer.js"
21
21
  },
22
22
  "dependencies": {
23
- "@astrojs/compiler": "^2.2.2",
23
+ "@astrojs/compiler": "^2.4.0",
24
24
  "@jridgewell/sourcemap-codec": "^1.4.15",
25
- "@volar/kit": "~1.10.9",
26
- "@volar/language-core": "~1.10.9",
27
- "@volar/language-server": "~1.10.9",
28
- "@volar/language-service": "~1.10.9",
29
- "@volar/source-map": "~1.10.9",
30
- "@volar/typescript": "~1.10.9",
25
+ "@volar/kit": "~1.11.1",
26
+ "@volar/language-core": "~1.11.1",
27
+ "@volar/language-server": "~1.11.1",
28
+ "@volar/language-service": "~1.11.1",
29
+ "@volar/source-map": "~1.11.1",
30
+ "@volar/typescript": "~1.11.1",
31
31
  "fast-glob": "^3.2.12",
32
32
  "muggle-string": "^0.3.1",
33
- "volar-service-css": "0.0.16",
34
- "volar-service-emmet": "0.0.16",
35
- "volar-service-html": "0.0.16",
36
- "volar-service-prettier": "0.0.16",
37
- "volar-service-typescript": "0.0.16",
38
- "volar-service-typescript-twoslash-queries": "0.0.16",
33
+ "volar-service-css": "0.0.17",
34
+ "volar-service-emmet": "0.0.17",
35
+ "volar-service-html": "0.0.17",
36
+ "volar-service-prettier": "0.0.17",
37
+ "volar-service-typescript": "0.0.17",
38
+ "volar-service-typescript-twoslash-queries": "0.0.17",
39
39
  "vscode-html-languageservice": "^5.1.0",
40
40
  "vscode-uri": "^3.0.8"
41
41
  },
@@ -45,13 +45,13 @@
45
45
  "@types/chai": "^4.3.5",
46
46
  "@types/mocha": "^10.0.1",
47
47
  "@types/node": "^18.17.8",
48
- "astro": "^3.3.0",
48
+ "astro": "^4.1.0",
49
49
  "chai": "^4.3.7",
50
50
  "mocha": "^10.2.0",
51
51
  "tsx": "^3.12.7",
52
+ "typescript": "^5.2.2",
52
53
  "vscode-languageserver-protocol": "^3.17.5",
53
- "vscode-languageserver-textdocument": "^1.0.11",
54
- "typescript": "^5.2.2"
54
+ "vscode-languageserver-textdocument": "^1.0.11"
55
55
  },
56
56
  "peerDependencies": {
57
57
  "prettier": "^3.0.0",
@@ -0,0 +1,5 @@
1
+ /// <reference types="astro/astro-jsx" />
2
+
3
+ declare module 'astro/jsx-runtime' {
4
+ export import JSX = astroHTML.JSX;
5
+ }
@@ -0,0 +1,5 @@
1
+ /// <reference path="astro-jsx.d.ts" />
2
+
3
+ declare module 'astro/jsx-runtime' {
4
+ export import JSX = astroHTML.JSX;
5
+ }
@@ -1,5 +0,0 @@
1
- declare namespace astroHTML.JSX {
2
- interface HTMLAttributes {
3
- [name: string]: any;
4
- }
5
- }