@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.
- package/dist/core/parseCSS.d.ts +0 -2
- package/dist/core/parseCSS.js +71 -49
- package/dist/core/parseJS.d.ts +1 -3
- package/dist/core/parseJS.js +111 -80
- package/package.json +1 -1
package/dist/core/parseCSS.d.ts
CHANGED
|
@@ -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[];
|
package/dist/core/parseCSS.js
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
|
103
|
-
|
|
104
|
-
.
|
|
105
|
-
|
|
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
|
|
136
|
+
return styleAttrs;
|
|
114
137
|
}
|
|
115
|
-
|
|
116
|
-
function
|
|
117
|
-
const
|
|
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
|
|
125
|
-
|
|
126
|
-
|
|
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
|
|
157
|
+
return classesAndIds;
|
|
136
158
|
}
|
|
137
|
-
exports.
|
|
159
|
+
exports.collectClassesAndIdsFromDocument = collectClassesAndIdsFromDocument;
|
|
138
160
|
//# sourceMappingURL=parseCSS.js.map
|
package/dist/core/parseJS.d.ts
CHANGED
|
@@ -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
|
|
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[];
|
package/dist/core/parseJS.js
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
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
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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) =>
|
|
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
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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
|
-
|
|
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
|
-
|
|
150
|
-
exports.htmlEventAttributes = [
|
|
181
|
+
const htmlEventAttributes = [
|
|
151
182
|
'onabort',
|
|
152
183
|
'onafterprint',
|
|
153
184
|
'onauxclick',
|