@astrojs/language-server 0.15.0 → 0.17.0

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