@astrojs/language-server 2.3.3 → 2.3.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.
@@ -2,7 +2,5 @@ import type { ParseResult } from '@astrojs/compiler/types';
2
2
  import { VirtualFile } from '@volar/language-core';
3
3
  import type ts from 'typescript/lib/tsserverlibrary';
4
4
  import type { HTMLDocument } from 'vscode-html-languageservice';
5
- import type { AttributeNodeWithPosition } from './compilerUtils.js';
6
5
  export declare function extractStylesheets(fileName: string, snapshot: ts.IScriptSnapshot, htmlDocument: HTMLDocument, ast: ParseResult['ast']): VirtualFile[];
7
6
  export declare function collectClassesAndIdsFromDocument(ast: ParseResult['ast']): string[];
8
- export declare function findInlineStyles(ast: ParseResult['ast']): AttributeNodeWithPosition[];
@@ -23,42 +23,13 @@ var __importStar = (this && this.__importStar) || function (mod) {
23
23
  return result;
24
24
  };
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.findInlineStyles = exports.collectClassesAndIdsFromDocument = exports.extractStylesheets = void 0;
26
+ exports.collectClassesAndIdsFromDocument = exports.extractStylesheets = void 0;
27
27
  const utils_1 = require("@astrojs/compiler/utils");
28
28
  const language_core_1 = require("@volar/language-core");
29
29
  const SourceMap = __importStar(require("@volar/source-map"));
30
30
  const muggle = __importStar(require("muggle-string"));
31
31
  function extractStylesheets(fileName, snapshot, htmlDocument, ast) {
32
- const embeddedCSSFiles = [];
33
- for (const [index, root] of htmlDocument.roots.entries()) {
34
- if (root.tag === 'style' && root.startTagEnd !== undefined && root.endTagStart !== undefined) {
35
- const styleText = snapshot.getText(root.startTagEnd, root.endTagStart);
36
- embeddedCSSFiles.push({
37
- fileName: fileName + `.${index}.css`,
38
- kind: language_core_1.FileKind.TextFile,
39
- snapshot: {
40
- getText: (start, end) => styleText.substring(start, end),
41
- getLength: () => styleText.length,
42
- getChangeRange: () => undefined,
43
- },
44
- codegenStacks: [],
45
- mappings: [
46
- {
47
- sourceRange: [root.startTagEnd, root.endTagStart],
48
- generatedRange: [0, styleText.length],
49
- data: language_core_1.FileRangeCapabilities.full,
50
- },
51
- ],
52
- capabilities: {
53
- diagnostic: false,
54
- documentSymbol: true,
55
- foldingRange: true,
56
- documentFormatting: false,
57
- },
58
- embeddedFiles: [],
59
- });
60
- }
61
- }
32
+ const embeddedCSSFiles = findEmbeddedStyles(fileName, snapshot, htmlDocument.roots);
62
33
  const inlineStyles = findInlineStyles(ast);
63
34
  if (inlineStyles.length > 0) {
64
35
  const codes = [];
@@ -91,18 +62,70 @@ function extractStylesheets(fileName, snapshot, htmlDocument, ast) {
91
62
  return embeddedCSSFiles;
92
63
  }
93
64
  exports.extractStylesheets = extractStylesheets;
94
- // TODO: Provide completion for classes and IDs
95
- function collectClassesAndIdsFromDocument(ast) {
96
- const classesAndIds = [];
65
+ /**
66
+ * Find all embedded styles in a document.
67
+ * Embedded styles are styles that are defined in `<style>` tags.
68
+ */
69
+ function findEmbeddedStyles(fileName, snapshot, roots) {
70
+ const embeddedCSSFiles = [];
71
+ let cssIndex = 0;
72
+ getEmbeddedCSSInNodes(roots);
73
+ function getEmbeddedCSSInNodes(nodes) {
74
+ for (const [_, node] of nodes.entries()) {
75
+ if (node.tag === 'style' &&
76
+ node.startTagEnd !== undefined &&
77
+ node.endTagStart !== undefined) {
78
+ const styleText = snapshot.getText(node.startTagEnd, node.endTagStart);
79
+ embeddedCSSFiles.push({
80
+ fileName: fileName + `.${cssIndex}.css`,
81
+ kind: language_core_1.FileKind.TextFile,
82
+ snapshot: {
83
+ getText: (start, end) => styleText.substring(start, end),
84
+ getLength: () => styleText.length,
85
+ getChangeRange: () => undefined,
86
+ },
87
+ codegenStacks: [],
88
+ mappings: [
89
+ {
90
+ sourceRange: [node.startTagEnd, node.endTagStart],
91
+ generatedRange: [0, styleText.length],
92
+ data: language_core_1.FileRangeCapabilities.full,
93
+ },
94
+ ],
95
+ capabilities: {
96
+ diagnostic: false,
97
+ documentSymbol: true,
98
+ foldingRange: true,
99
+ documentFormatting: false,
100
+ },
101
+ embeddedFiles: [],
102
+ });
103
+ cssIndex++;
104
+ }
105
+ if (node.children)
106
+ getEmbeddedCSSInNodes(node.children);
107
+ }
108
+ }
109
+ return embeddedCSSFiles;
110
+ }
111
+ /**
112
+ * Find all inline styles using the Astro AST
113
+ * Inline styles are styles that are defined in the `style` attribute of an element.
114
+ * TODO: Merge this with `findEmbeddedCSS`? Unlike scripts, there's no scoping being done here, so merging all of it in
115
+ * the same virtual file is possible, though it might make mapping more tricky.
116
+ */
117
+ function findInlineStyles(ast) {
118
+ const styleAttrs = [];
119
+ // `@astrojs/compiler`'s `walk` method is async, so we can't use it here. Arf
97
120
  function walkDown(parent) {
98
121
  if (!parent.children)
99
122
  return;
100
123
  parent.children.forEach((child) => {
101
124
  if (utils_1.is.element(child)) {
102
- const classOrIDAttributes = child.attributes
103
- .filter((attr) => attr.kind === 'quoted' && (attr.name === 'class' || attr.name === 'id'))
104
- .flatMap((attr) => attr.value.split(' '));
105
- classesAndIds.push(...classOrIDAttributes);
125
+ const styleAttribute = child.attributes.find((attr) => attr.name === 'style' && attr.kind === 'quoted');
126
+ if (styleAttribute && styleAttribute.position) {
127
+ styleAttrs.push(styleAttribute);
128
+ }
106
129
  }
107
130
  if (utils_1.is.parent(child)) {
108
131
  walkDown(child);
@@ -110,21 +133,20 @@ function collectClassesAndIdsFromDocument(ast) {
110
133
  });
111
134
  }
112
135
  walkDown(ast);
113
- return classesAndIds;
136
+ return styleAttrs;
114
137
  }
115
- exports.collectClassesAndIdsFromDocument = collectClassesAndIdsFromDocument;
116
- function findInlineStyles(ast) {
117
- const styleAttrs = [];
118
- // `@astrojs/compiler`'s `walk` method is async, so we can't use it here. Arf
138
+ // TODO: Provide completion for classes and IDs
139
+ function collectClassesAndIdsFromDocument(ast) {
140
+ const classesAndIds = [];
119
141
  function walkDown(parent) {
120
142
  if (!parent.children)
121
143
  return;
122
144
  parent.children.forEach((child) => {
123
145
  if (utils_1.is.element(child)) {
124
- const styleAttribute = child.attributes.find((attr) => attr.name === 'style' && attr.kind === 'quoted');
125
- if (styleAttribute && styleAttribute.position) {
126
- styleAttrs.push(styleAttribute);
127
- }
146
+ const classOrIDAttributes = child.attributes
147
+ .filter((attr) => attr.kind === 'quoted' && (attr.name === 'class' || attr.name === 'id'))
148
+ .flatMap((attr) => attr.value.split(' '));
149
+ classesAndIds.push(...classOrIDAttributes);
128
150
  }
129
151
  if (utils_1.is.parent(child)) {
130
152
  walkDown(child);
@@ -132,7 +154,7 @@ function findInlineStyles(ast) {
132
154
  });
133
155
  }
134
156
  walkDown(ast);
135
- return styleAttrs;
157
+ return classesAndIds;
136
158
  }
137
- exports.findInlineStyles = findInlineStyles;
159
+ exports.collectClassesAndIdsFromDocument = collectClassesAndIdsFromDocument;
138
160
  //# sourceMappingURL=parseCSS.js.map
@@ -1,7 +1,5 @@
1
1
  import type { ParseResult } from '@astrojs/compiler/types';
2
2
  import { VirtualFile } from '@volar/language-core';
3
3
  import type ts from 'typescript/lib/tsserverlibrary';
4
- import type { HTMLDocument, Node } from 'vscode-html-languageservice';
4
+ import type { HTMLDocument } from 'vscode-html-languageservice';
5
5
  export declare function extractScriptTags(fileName: string, snapshot: ts.IScriptSnapshot, htmlDocument: HTMLDocument, ast: ParseResult['ast']): VirtualFile[];
6
- export declare function isIsolatedScriptTag(scriptTag: Node): boolean;
7
- export declare const htmlEventAttributes: string[];
@@ -23,92 +23,104 @@ var __importStar = (this && this.__importStar) || function (mod) {
23
23
  return result;
24
24
  };
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.htmlEventAttributes = exports.isIsolatedScriptTag = exports.extractScriptTags = void 0;
26
+ exports.extractScriptTags = void 0;
27
27
  const utils_1 = require("@astrojs/compiler/utils");
28
28
  const language_core_1 = require("@volar/language-core");
29
29
  const SourceMap = __importStar(require("@volar/source-map"));
30
30
  const muggle = __importStar(require("muggle-string"));
31
31
  function extractScriptTags(fileName, snapshot, htmlDocument, ast) {
32
- const embeddedJSFiles = [];
33
- for (const [index, root] of htmlDocument.roots.entries()) {
34
- if (root.tag === 'script' &&
35
- root.startTagEnd !== undefined &&
36
- root.endTagStart !== undefined &&
37
- isIsolatedScriptTag(root)) {
38
- const scriptText = snapshot.getText(root.startTagEnd, root.endTagStart);
39
- embeddedJSFiles.push({
40
- fileName: fileName + `.${index}.mts`,
41
- kind: language_core_1.FileKind.TypeScriptHostFile,
42
- snapshot: {
43
- getText: (start, end) => scriptText.substring(start, end),
44
- getLength: () => scriptText.length,
45
- getChangeRange: () => undefined,
46
- },
47
- codegenStacks: [],
48
- mappings: [
49
- {
50
- sourceRange: [root.startTagEnd, root.endTagStart],
51
- generatedRange: [0, scriptText.length],
52
- data: language_core_1.FileRangeCapabilities.full,
53
- },
54
- ],
55
- capabilities: {
56
- diagnostic: true,
57
- codeAction: true,
58
- inlayHint: true,
59
- documentSymbol: true,
60
- foldingRange: true,
61
- documentFormatting: false,
62
- },
63
- embeddedFiles: [],
64
- });
65
- }
66
- }
32
+ const embeddedJSFiles = findIsolatedScripts(fileName, snapshot, htmlDocument.roots);
67
33
  const javascriptContexts = [
68
34
  ...findInlineScripts(htmlDocument, snapshot),
69
35
  ...findEventAttributes(ast),
70
36
  ].sort((a, b) => a.startOffset - b.startOffset);
71
37
  if (javascriptContexts.length > 0) {
72
- const codes = [];
73
- for (const javascriptContext of javascriptContexts) {
74
- codes.push([
75
- javascriptContext.content,
76
- undefined,
77
- javascriptContext.startOffset,
78
- language_core_1.FileRangeCapabilities.full,
79
- ]);
80
- }
81
- const mappings = SourceMap.buildMappings(codes);
82
- const text = muggle.toString(codes);
83
- embeddedJSFiles.push({
84
- fileName: fileName + '.inline.mjs',
85
- codegenStacks: [],
86
- snapshot: {
87
- getText: (start, end) => text.substring(start, end),
88
- getLength: () => text.length,
89
- getChangeRange: () => undefined,
90
- },
91
- capabilities: language_core_1.FileCapabilities.full,
92
- embeddedFiles: [],
93
- kind: language_core_1.FileKind.TypeScriptHostFile,
94
- mappings,
95
- });
38
+ embeddedJSFiles.push(mergeJSContexts(fileName, javascriptContexts));
96
39
  }
97
40
  return embeddedJSFiles;
98
41
  }
99
42
  exports.extractScriptTags = extractScriptTags;
43
+ function isIsolatedScriptTag(scriptTag) {
44
+ // Using any kind of attributes on the script tag will disable hoisting
45
+ if (!scriptTag.attributes ||
46
+ (scriptTag.attributes && Object.entries(scriptTag.attributes).length === 0) ||
47
+ scriptTag.attributes['type']?.includes('module')) {
48
+ return true;
49
+ }
50
+ return false;
51
+ }
52
+ /**
53
+ * Get all the isolated scripts in the HTML document
54
+ * Isolated scripts are scripts that are hoisted by Astro and as such, are isolated from the rest of the code because of the implicit `type="module"`
55
+ * All the isolated scripts are passed to the TypeScript language server as separate `.mts` files.
56
+ */
57
+ function findIsolatedScripts(fileName, snapshot, roots) {
58
+ const embeddedScripts = [];
59
+ let scriptIndex = 0;
60
+ getEmbeddedScriptsInNodes(roots);
61
+ function getEmbeddedScriptsInNodes(nodes) {
62
+ for (const [_, node] of nodes.entries()) {
63
+ if (node.tag === 'script' &&
64
+ node.startTagEnd !== undefined &&
65
+ node.endTagStart !== undefined &&
66
+ isIsolatedScriptTag(node)) {
67
+ const scriptText = snapshot.getText(node.startTagEnd, node.endTagStart);
68
+ embeddedScripts.push({
69
+ fileName: fileName + `.${scriptIndex}.mts`,
70
+ kind: language_core_1.FileKind.TypeScriptHostFile,
71
+ snapshot: {
72
+ getText: (start, end) => scriptText.substring(start, end),
73
+ getLength: () => scriptText.length,
74
+ getChangeRange: () => undefined,
75
+ },
76
+ codegenStacks: [],
77
+ mappings: [
78
+ {
79
+ sourceRange: [node.startTagEnd, node.endTagStart],
80
+ generatedRange: [0, scriptText.length],
81
+ data: language_core_1.FileRangeCapabilities.full,
82
+ },
83
+ ],
84
+ capabilities: {
85
+ diagnostic: true,
86
+ codeAction: true,
87
+ inlayHint: true,
88
+ documentSymbol: true,
89
+ foldingRange: true,
90
+ documentFormatting: false,
91
+ },
92
+ embeddedFiles: [],
93
+ });
94
+ scriptIndex++;
95
+ }
96
+ if (node.children)
97
+ getEmbeddedScriptsInNodes(node.children);
98
+ }
99
+ }
100
+ return embeddedScripts;
101
+ }
102
+ /**
103
+ * Get all the inline scripts in the HTML document
104
+ * Inline scripts are scripts that are not hoisted by Astro and as such, are isolated from the rest of the code.
105
+ * All the inline scripts are concatenated into a single `.mjs` file and passed to the TypeScript language server.
106
+ */
100
107
  function findInlineScripts(htmlDocument, snapshot) {
101
108
  const inlineScripts = [];
102
- for (const [_, root] of htmlDocument.roots.entries()) {
103
- if (root.tag === 'script' &&
104
- root.startTagEnd !== undefined &&
105
- root.endTagStart !== undefined &&
106
- !isIsolatedScriptTag(root)) {
107
- const scriptText = snapshot.getText(root.startTagEnd, root.endTagStart);
108
- inlineScripts.push({
109
- startOffset: root.startTagEnd,
110
- content: scriptText,
111
- });
109
+ getInlineScriptsInNodes(htmlDocument.roots);
110
+ function getInlineScriptsInNodes(nodes) {
111
+ for (const [_, node] of nodes.entries()) {
112
+ if (node.tag === 'script' &&
113
+ node.startTagEnd !== undefined &&
114
+ node.endTagStart !== undefined &&
115
+ !isIsolatedScriptTag(node)) {
116
+ const scriptText = snapshot.getText(node.startTagEnd, node.endTagStart);
117
+ inlineScripts.push({
118
+ startOffset: node.startTagEnd,
119
+ content: scriptText,
120
+ });
121
+ }
122
+ if (node.children)
123
+ getInlineScriptsInNodes(node.children);
112
124
  }
113
125
  }
114
126
  return inlineScripts;
@@ -121,7 +133,7 @@ function findEventAttributes(ast) {
121
133
  return;
122
134
  parent.children.forEach((child) => {
123
135
  if (utils_1.is.element(child)) {
124
- const eventAttribute = child.attributes.find((attr) => exports.htmlEventAttributes.includes(attr.name) && attr.kind === 'quoted');
136
+ const eventAttribute = child.attributes.find((attr) => htmlEventAttributes.includes(attr.name) && attr.kind === 'quoted');
125
137
  if (eventAttribute && eventAttribute.position) {
126
138
  eventAttrs.push({
127
139
  content: eventAttribute.value,
@@ -137,17 +149,36 @@ function findEventAttributes(ast) {
137
149
  walkDown(ast);
138
150
  return eventAttrs;
139
151
  }
140
- function isIsolatedScriptTag(scriptTag) {
141
- // Using any kind of attributes on the script tag will disable hoisting
142
- if (!scriptTag.attributes ||
143
- (scriptTag.attributes && Object.entries(scriptTag.attributes).length === 0) ||
144
- scriptTag.attributes['type']?.includes('module')) {
145
- return true;
152
+ /**
153
+ * Merge all the inline and non-hoisted scripts into a single `.mjs` file
154
+ */
155
+ function mergeJSContexts(fileName, javascriptContexts) {
156
+ const codes = [];
157
+ for (const javascriptContext of javascriptContexts) {
158
+ codes.push([
159
+ javascriptContext.content,
160
+ undefined,
161
+ javascriptContext.startOffset,
162
+ language_core_1.FileRangeCapabilities.full,
163
+ ]);
146
164
  }
147
- return false;
165
+ const mappings = SourceMap.buildMappings(codes);
166
+ const text = muggle.toString(codes);
167
+ return {
168
+ fileName: fileName + '.inline.mjs',
169
+ codegenStacks: [],
170
+ snapshot: {
171
+ getText: (start, end) => text.substring(start, end),
172
+ getLength: () => text.length,
173
+ getChangeRange: () => undefined,
174
+ },
175
+ capabilities: language_core_1.FileCapabilities.full,
176
+ embeddedFiles: [],
177
+ kind: language_core_1.FileKind.TypeScriptHostFile,
178
+ mappings,
179
+ };
148
180
  }
149
- exports.isIsolatedScriptTag = isIsolatedScriptTag;
150
- exports.htmlEventAttributes = [
181
+ const htmlEventAttributes = [
151
182
  'onabort',
152
183
  'onafterprint',
153
184
  'onauxclick',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrojs/language-server",
3
- "version": "2.3.3",
3
+ "version": "2.3.4",
4
4
  "author": "withastro",
5
5
  "license": "MIT",
6
6
  "repository": {