ts-gem-plugin 0.0.5 → 0.0.6
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/index.js +1038 -519
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -25,19 +25,6 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
25
25
|
// src/index.ts
|
|
26
26
|
var import_typescript_template_language_service_decorator = require("@mantou/typescript-template-language-service-decorator");
|
|
27
27
|
|
|
28
|
-
// src/decorate-html.ts
|
|
29
|
-
var import_vscode_html_languageservice = require("vscode-html-languageservice");
|
|
30
|
-
var import_emmet_helper = require("@vscode/emmet-helper");
|
|
31
|
-
var import_vscode_css_languageservice = require("vscode-css-languageservice");
|
|
32
|
-
|
|
33
|
-
// ../duoyun-ui/lib/types.js
|
|
34
|
-
function isNullish(v) {
|
|
35
|
-
return v === null || v === void 0;
|
|
36
|
-
}
|
|
37
|
-
function isNotNullish(v) {
|
|
38
|
-
return !isNullish(v);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
28
|
// ../gem/lib/utils.js
|
|
42
29
|
var { assign, fromEntries, entries, keys } = Object;
|
|
43
30
|
var LinkedList = class extends EventTarget {
|
|
@@ -86,8 +73,10 @@ var LinkedList = class extends EventTarget {
|
|
|
86
73
|
find(value) {
|
|
87
74
|
return this.#map.get(value);
|
|
88
75
|
}
|
|
89
|
-
|
|
90
|
-
|
|
76
|
+
/**
|
|
77
|
+
* 添加到尾部,已存在时会删除老的项目
|
|
78
|
+
* 如果是添加第一个,start 事件会在添加前触发,避免处理事件重复的逻辑
|
|
79
|
+
*/
|
|
91
80
|
add(value) {
|
|
92
81
|
if (!this.#lastItem) {
|
|
93
82
|
this.dispatchEvent(new CustomEvent("start"));
|
|
@@ -104,7 +93,7 @@ var LinkedList = class extends EventTarget {
|
|
|
104
93
|
}
|
|
105
94
|
this.#map.set(value, item);
|
|
106
95
|
}
|
|
107
|
-
|
|
96
|
+
/** 删除这个元素后没有其他元素时立即出发 end 事件 */
|
|
108
97
|
delete(value) {
|
|
109
98
|
const deleteItem = this.#delete(value);
|
|
110
99
|
if (!this.#firstItem) {
|
|
@@ -112,8 +101,7 @@ var LinkedList = class extends EventTarget {
|
|
|
112
101
|
}
|
|
113
102
|
return deleteItem;
|
|
114
103
|
}
|
|
115
|
-
|
|
116
|
-
// 会从链表删除
|
|
104
|
+
/** 获取头部元素,会从链表删除 */
|
|
117
105
|
get() {
|
|
118
106
|
const firstItem = this.#firstItem;
|
|
119
107
|
if (!firstItem)
|
|
@@ -122,91 +110,152 @@ var LinkedList = class extends EventTarget {
|
|
|
122
110
|
return firstItem.value;
|
|
123
111
|
}
|
|
124
112
|
};
|
|
113
|
+
function camelToKebabCase(str) {
|
|
114
|
+
return str.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
|
|
115
|
+
}
|
|
125
116
|
function kebabToCamelCase(str) {
|
|
126
117
|
return str.replace(/-(.)/g, (_substr, $1) => $1.toUpperCase());
|
|
127
118
|
}
|
|
128
119
|
|
|
120
|
+
// src/configuration.ts
|
|
121
|
+
var defaultConfiguration = {
|
|
122
|
+
emmet: {},
|
|
123
|
+
elementDefineRules: {
|
|
124
|
+
"Duoyun*Element": "dy-*",
|
|
125
|
+
"*Element": "*"
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
var Rules = class {
|
|
129
|
+
#map = /* @__PURE__ */ new Map();
|
|
130
|
+
constructor(data) {
|
|
131
|
+
Object.entries(data).forEach(([classNamePattern, tagPattern]) => {
|
|
132
|
+
this.#map.set(new RegExp(classNamePattern.replace("*", "(.*)")), tagPattern.replace("*", "$1"));
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
findTag(className) {
|
|
136
|
+
for (const [reg, replaceStr] of this.#map) {
|
|
137
|
+
if (reg.exec(className)) {
|
|
138
|
+
return camelToKebabCase(className.replace(reg, replaceStr));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
var Configuration = class {
|
|
144
|
+
#emmet = defaultConfiguration.emmet;
|
|
145
|
+
#elementDefineRules = new Rules(defaultConfiguration.elementDefineRules);
|
|
146
|
+
update(config) {
|
|
147
|
+
this.#emmet = config.emmet || defaultConfiguration.emmet;
|
|
148
|
+
this.#elementDefineRules = new Rules({
|
|
149
|
+
...defaultConfiguration.elementDefineRules,
|
|
150
|
+
...config.elementDefineRules
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
get emmet() {
|
|
154
|
+
return this.#emmet;
|
|
155
|
+
}
|
|
156
|
+
get elementDefineRules() {
|
|
157
|
+
return this.#elementDefineRules;
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
// src/context.ts
|
|
162
|
+
var import_vscode_html_languageservice2 = require("@mantou/vscode-html-languageservice");
|
|
163
|
+
var import_standard_template_source_helper = __toESM(require("@mantou/typescript-template-language-service-decorator/lib/standard-template-source-helper"));
|
|
164
|
+
var import_standard_script_source_helper = __toESM(require("@mantou/typescript-template-language-service-decorator/lib/standard-script-source-helper"));
|
|
165
|
+
var import_vscode_css_languageservice = require("@mantou/vscode-css-languageservice");
|
|
166
|
+
|
|
167
|
+
// ../duoyun-ui/lib/map.js
|
|
168
|
+
var StringWeakMap = class {
|
|
169
|
+
#map = /* @__PURE__ */ new Map();
|
|
170
|
+
#weakMap = /* @__PURE__ */ new WeakMap();
|
|
171
|
+
#registry = new FinalizationRegistry((key) => this.#map.delete(key));
|
|
172
|
+
set(key, val) {
|
|
173
|
+
this.#map.set(key, new WeakRef(val));
|
|
174
|
+
this.#weakMap.set(val, key);
|
|
175
|
+
this.#registry.register(val, key);
|
|
176
|
+
}
|
|
177
|
+
get(key) {
|
|
178
|
+
return this.#map.get(key)?.deref();
|
|
179
|
+
}
|
|
180
|
+
findKey(val) {
|
|
181
|
+
return this.#weakMap.get(val);
|
|
182
|
+
}
|
|
183
|
+
*[Symbol.iterator]() {
|
|
184
|
+
const entries2 = this.#map.entries();
|
|
185
|
+
for (const [tag, ref] of entries2) {
|
|
186
|
+
yield [tag, ref.deref()];
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
|
|
129
191
|
// src/utils.ts
|
|
130
|
-
|
|
131
|
-
var import_vscode_languageserver_textdocument = require("vscode-languageserver-textdocument");
|
|
132
|
-
function isCustomElement(tag) {
|
|
192
|
+
function isCustomElementTag(tag) {
|
|
133
193
|
return tag.includes("-");
|
|
134
194
|
}
|
|
135
195
|
function isDepElement(node) {
|
|
136
196
|
return node.getSourceFile().fileName.includes("/node_modules/");
|
|
137
197
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
198
|
+
function bindMemberFunction(o, keys2 = Object.keys(o)) {
|
|
199
|
+
return Object.fromEntries(keys2.map((key) => [key, o[key].bind?.(o)]));
|
|
200
|
+
}
|
|
201
|
+
function forEachNode(roots, fn) {
|
|
202
|
+
const list = [...roots];
|
|
141
203
|
while (true) {
|
|
142
204
|
const currentNode = list.pop();
|
|
143
205
|
if (!currentNode) return;
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
206
|
+
fn(currentNode);
|
|
207
|
+
list.push(...currentNode.children);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
function getAstNodeAtPosition(typescript, node, pos) {
|
|
211
|
+
if (node.pos > pos || node.end <= pos) return;
|
|
212
|
+
while (node.kind >= typescript.SyntaxKind.FirstNode) {
|
|
213
|
+
const nested = typescript.forEachChild(node, (child) => child.pos <= pos && child.end > pos ? child : void 0);
|
|
214
|
+
if (nested === void 0) break;
|
|
215
|
+
node = nested;
|
|
154
216
|
}
|
|
217
|
+
return node;
|
|
155
218
|
}
|
|
219
|
+
var BEFORE_REG = /[^\s</>]+$/;
|
|
220
|
+
var AFTER_REG = /^[^\s</>]+/;
|
|
156
221
|
function getHTMLTextAtPosition(text, offset) {
|
|
157
|
-
const before = text.slice(0, offset).match(
|
|
158
|
-
const after = text.slice(offset).match(
|
|
222
|
+
const before = text.slice(0, offset).match(BEFORE_REG)?.at(0) || "";
|
|
223
|
+
const after = text.slice(offset).match(AFTER_REG)?.at(0) || "";
|
|
159
224
|
const str = before + after;
|
|
160
225
|
return {
|
|
226
|
+
before,
|
|
227
|
+
after,
|
|
161
228
|
text: str,
|
|
162
229
|
start: offset - before.length,
|
|
163
230
|
length: str.length
|
|
164
231
|
};
|
|
165
232
|
}
|
|
166
|
-
var attrMap = {
|
|
167
|
-
".": "property",
|
|
168
|
-
"?": "boolean",
|
|
169
|
-
"@": "event"
|
|
170
|
-
};
|
|
171
233
|
function getAttrName(text) {
|
|
172
234
|
const attr = text.split("=").at(0);
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
if (!typescript.isClassDeclaration(node)) return;
|
|
181
|
-
for (const modifier of node.modifiers || []) {
|
|
182
|
-
if (typescript.isDecorator(modifier) && typescript.isCallExpression(modifier.expression) && modifier.expression.expression.getText() === "customElement") {
|
|
183
|
-
const arg = modifier.expression.arguments.at(0);
|
|
184
|
-
if (arg && typescript.isStringLiteral(arg)) {
|
|
185
|
-
return arg.text;
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
if (isDep && node.name && typescript.isIdentifier(node.name)) {
|
|
190
|
-
const name = node.name.text;
|
|
191
|
-
if (name.endsWith("Element")) {
|
|
192
|
-
return name;
|
|
193
|
-
}
|
|
194
|
-
}
|
|
235
|
+
const isNotLetter = hasDecoratorAttr(attr);
|
|
236
|
+
const offset = isNotLetter ? 1 : 0;
|
|
237
|
+
return {
|
|
238
|
+
attr: attr.slice(offset),
|
|
239
|
+
offset,
|
|
240
|
+
decorate: isNotLetter ? attr.at(0) : ""
|
|
241
|
+
};
|
|
195
242
|
}
|
|
196
|
-
function
|
|
197
|
-
|
|
198
|
-
const commentRanges = typescript.getLeadingCommentRanges(fullText, declaration.getFullStart());
|
|
199
|
-
const commentStrings = commentRanges?.filter(({ kind }) => kind === typescript.SyntaxKind.MultiLineCommentTrivia).map(({ pos, end }) => fullText.slice(pos, end));
|
|
200
|
-
return commentStrings?.join("\n");
|
|
243
|
+
function hasDecoratorAttr(str) {
|
|
244
|
+
return str.charCodeAt(0) < 65;
|
|
201
245
|
}
|
|
202
|
-
function
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
246
|
+
function getTagInfo(node, offset) {
|
|
247
|
+
const tag = node.tag;
|
|
248
|
+
const openStart = node.start + 1 + offset;
|
|
249
|
+
return {
|
|
250
|
+
node,
|
|
251
|
+
tag,
|
|
252
|
+
offset,
|
|
253
|
+
open: { start: openStart, length: tag.length },
|
|
254
|
+
end: node.endTagStart && {
|
|
255
|
+
start: node.endTagStart + 2 + offset,
|
|
256
|
+
length: node.end - node.endTagStart - 3
|
|
257
|
+
}
|
|
258
|
+
};
|
|
210
259
|
}
|
|
211
260
|
var marker = Symbol();
|
|
212
261
|
function decorate(origin, cb) {
|
|
@@ -215,8 +264,317 @@ function decorate(origin, cb) {
|
|
|
215
264
|
result[marker] = true;
|
|
216
265
|
return result;
|
|
217
266
|
}
|
|
267
|
+
|
|
268
|
+
// src/data-provider.ts
|
|
269
|
+
var import_vscode_html_languageservice = require("@mantou/vscode-html-languageservice");
|
|
270
|
+
|
|
271
|
+
// src/constants.ts
|
|
272
|
+
var NAME = "gem-plugin";
|
|
273
|
+
|
|
274
|
+
// src/data-provider.ts
|
|
275
|
+
var dataProvider = (0, import_vscode_html_languageservice.getDefaultHTMLDataProvider)();
|
|
276
|
+
var HTMLDataProvider = class {
|
|
277
|
+
#ts;
|
|
278
|
+
#elements;
|
|
279
|
+
#getProgram;
|
|
280
|
+
constructor(typescript, elements, getProgram) {
|
|
281
|
+
this.#ts = typescript;
|
|
282
|
+
this.#elements = elements;
|
|
283
|
+
this.#getProgram = getProgram;
|
|
284
|
+
}
|
|
285
|
+
getId() {
|
|
286
|
+
return NAME;
|
|
287
|
+
}
|
|
288
|
+
isApplicable() {
|
|
289
|
+
return true;
|
|
290
|
+
}
|
|
291
|
+
provideTags() {
|
|
292
|
+
return [...this.#elements].map(([tag, node]) => ({
|
|
293
|
+
name: tag,
|
|
294
|
+
attributes: [],
|
|
295
|
+
description: getDocComment(this.#ts, node)
|
|
296
|
+
}));
|
|
297
|
+
}
|
|
298
|
+
provideAttributes(tag) {
|
|
299
|
+
const ts = this.#ts;
|
|
300
|
+
const typeChecker = this.#getProgram().getTypeChecker();
|
|
301
|
+
const node = this.#elements.get(tag);
|
|
302
|
+
const result = [
|
|
303
|
+
{ name: "v-if", description: "Similar to vue `v-if`" },
|
|
304
|
+
{ name: "v-else-if", description: "Similar to vue `v-else-if`" },
|
|
305
|
+
{ name: "v-else", description: "Similar to vue `v-else`", valueSet: "v" }
|
|
306
|
+
];
|
|
307
|
+
if (!node) return result;
|
|
308
|
+
const isDep = isDepElement(node);
|
|
309
|
+
const props = typeChecker.getTypeAtLocation(node).getApparentProperties();
|
|
310
|
+
props.forEach((e) => {
|
|
311
|
+
const declaration = e.getDeclarations()?.at(0);
|
|
312
|
+
const prop = declaration && ts.isPropertyDeclaration(declaration);
|
|
313
|
+
if (!prop) return;
|
|
314
|
+
const hasPropDecorator = declaration.modifiers?.some((m) => ts.isDecorator(m) && ts.isIdentifier(m.expression));
|
|
315
|
+
if (!hasPropDecorator && !isDep) return;
|
|
316
|
+
const type = declaration.type && typeChecker.getTypeFromTypeNode(declaration.type);
|
|
317
|
+
const typeText = declaration.type?.getText();
|
|
318
|
+
const description = getDocComment(ts, declaration);
|
|
319
|
+
switch (type) {
|
|
320
|
+
case typeChecker.getStringType():
|
|
321
|
+
case typeChecker.getNumberType():
|
|
322
|
+
result.push({ name: e.name, description });
|
|
323
|
+
break;
|
|
324
|
+
case typeChecker.getBooleanType():
|
|
325
|
+
result.push({ name: e.name, description, valueSet: "v" });
|
|
326
|
+
result.push({ name: `?${e.name}`, description });
|
|
327
|
+
break;
|
|
328
|
+
}
|
|
329
|
+
if (type && getUnionValues(type)) {
|
|
330
|
+
result.push({ name: e.name, description });
|
|
331
|
+
}
|
|
332
|
+
if (typeText?.startsWith("Emitter")) {
|
|
333
|
+
result.push({ name: `@${e.name}`, description });
|
|
334
|
+
} else {
|
|
335
|
+
result.push({ name: `.${e.name}`, description });
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
const oResult = dataProvider.provideAttributes(isCustomElementTag(tag) ? "div" : tag);
|
|
339
|
+
oResult.forEach((data) => {
|
|
340
|
+
const tryEvtName = data.name.replace(/^on/, "@");
|
|
341
|
+
if (tryEvtName !== data.name) {
|
|
342
|
+
result.push({ ...data, name: tryEvtName });
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
return result;
|
|
346
|
+
}
|
|
347
|
+
provideValues(tag, attr) {
|
|
348
|
+
const typeChecker = this.#getProgram().getTypeChecker();
|
|
349
|
+
const node = this.#elements.get(tag);
|
|
350
|
+
if (!node) return [];
|
|
351
|
+
const prop = typeChecker.getTypeAtLocation(node).getProperty(getAttrName(attr).attr);
|
|
352
|
+
const result = prop && getUnionValues(typeChecker.getTypeOfSymbol(prop));
|
|
353
|
+
return result?.map((name) => ({ name })) || [];
|
|
354
|
+
}
|
|
355
|
+
};
|
|
356
|
+
function getUnionValues(type) {
|
|
357
|
+
if (!type.isUnion()) return;
|
|
358
|
+
const result = [];
|
|
359
|
+
type.types.forEach((e) => {
|
|
360
|
+
if (!e.isLiteral()) return;
|
|
361
|
+
result.push(String(e.value));
|
|
362
|
+
});
|
|
363
|
+
return result;
|
|
364
|
+
}
|
|
365
|
+
var COMMENT_LINE_CONTENT = /^(\/?[ *\t]*)?(?<str>.*?)(\**\/)?$/gm;
|
|
366
|
+
function getDocComment(typescript, declaration) {
|
|
367
|
+
const fullText = declaration.getSourceFile().getFullText();
|
|
368
|
+
const commentRanges = typescript.getLeadingCommentRanges(fullText, declaration.getFullStart());
|
|
369
|
+
const commentStrings = commentRanges?.filter(({ kind }) => kind === typescript.SyntaxKind.MultiLineCommentTrivia).map(({ pos, end }) => {
|
|
370
|
+
const fullComment = [...fullText.slice(pos, end).matchAll(COMMENT_LINE_CONTENT)];
|
|
371
|
+
return fullComment.map((m) => m.groups.str).join("\n");
|
|
372
|
+
});
|
|
373
|
+
return commentStrings?.join("\n\n");
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// ../duoyun-ui/lib/cache.js
|
|
377
|
+
var Cache = class {
|
|
378
|
+
#max;
|
|
379
|
+
#maxAge;
|
|
380
|
+
#renewal;
|
|
381
|
+
#map = /* @__PURE__ */ new Map();
|
|
382
|
+
#reverseMap = /* @__PURE__ */ new Map();
|
|
383
|
+
#addedLinked = new LinkedList();
|
|
384
|
+
constructor({ max = Infinity, maxAge = Infinity, renewal = false } = {}) {
|
|
385
|
+
this.#max = max;
|
|
386
|
+
this.#maxAge = maxAge;
|
|
387
|
+
this.#renewal = renewal;
|
|
388
|
+
}
|
|
389
|
+
setOptions(options) {
|
|
390
|
+
this.#max = options.max ?? this.#max;
|
|
391
|
+
this.#maxAge = options.maxAge ?? this.#maxAge;
|
|
392
|
+
this.#renewal = options.renewal ?? this.#renewal;
|
|
393
|
+
}
|
|
394
|
+
#trim() {
|
|
395
|
+
for (let i = this.#addedLinked.size - this.#max; i > 0; i--) {
|
|
396
|
+
const value = this.#addedLinked.get();
|
|
397
|
+
const key = this.#reverseMap.get(value);
|
|
398
|
+
this.#reverseMap.delete(value);
|
|
399
|
+
this.#map.delete(key);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
set(key, value) {
|
|
403
|
+
this.#addedLinked.add(value);
|
|
404
|
+
this.#reverseMap.set(value, key);
|
|
405
|
+
this.#map.set(key, { value, timestamp: Date.now() });
|
|
406
|
+
this.#trim();
|
|
407
|
+
return value;
|
|
408
|
+
}
|
|
409
|
+
get(key, init) {
|
|
410
|
+
const cache = this.#map.get(key);
|
|
411
|
+
if (!cache) {
|
|
412
|
+
return init && this.set(key, init(key));
|
|
413
|
+
}
|
|
414
|
+
const { timestamp, value } = cache;
|
|
415
|
+
if (Date.now() - timestamp > this.#maxAge) {
|
|
416
|
+
this.#addedLinked.delete(value);
|
|
417
|
+
this.#reverseMap.delete(value);
|
|
418
|
+
this.#map.delete(key);
|
|
419
|
+
return init && this.set(key, init(key));
|
|
420
|
+
}
|
|
421
|
+
if (this.#renewal) {
|
|
422
|
+
cache.timestamp = Date.now();
|
|
423
|
+
}
|
|
424
|
+
this.#addedLinked.add(value);
|
|
425
|
+
return value;
|
|
426
|
+
}
|
|
427
|
+
};
|
|
428
|
+
|
|
429
|
+
// src/cache.ts
|
|
430
|
+
var LRUCache = class {
|
|
431
|
+
#bucket;
|
|
432
|
+
constructor(args) {
|
|
433
|
+
this.#bucket = new Cache({ max: 25, renewal: true, ...args });
|
|
434
|
+
}
|
|
435
|
+
#genKey(context, position) {
|
|
436
|
+
return [context.fileName, position?.line, position?.character, context.text].join(";");
|
|
437
|
+
}
|
|
438
|
+
get(context, position, init) {
|
|
439
|
+
return this.#bucket.get(this.#genKey(context, position), init);
|
|
440
|
+
}
|
|
441
|
+
};
|
|
442
|
+
|
|
443
|
+
// src/context.ts
|
|
444
|
+
var Context = class {
|
|
445
|
+
elements;
|
|
446
|
+
builtInElements;
|
|
447
|
+
ts;
|
|
448
|
+
config;
|
|
449
|
+
project;
|
|
450
|
+
logger;
|
|
451
|
+
dataProvider;
|
|
452
|
+
cssLanguageService;
|
|
453
|
+
htmlLanguageService;
|
|
454
|
+
htmlSourceHelper;
|
|
455
|
+
htmlTemplateStringSettings;
|
|
456
|
+
cssTemplateStringSettings;
|
|
457
|
+
getProgram;
|
|
458
|
+
constructor(typescript, config, info, logger) {
|
|
459
|
+
this.ts = typescript;
|
|
460
|
+
this.config = config;
|
|
461
|
+
this.getProgram = () => info.languageService.getProgram();
|
|
462
|
+
this.project = info.project;
|
|
463
|
+
this.logger = logger;
|
|
464
|
+
this.dataProvider = dataProvider;
|
|
465
|
+
this.elements = new StringWeakMap();
|
|
466
|
+
this.builtInElements = new StringWeakMap();
|
|
467
|
+
this.cssLanguageService = (0, import_vscode_css_languageservice.getCSSLanguageService)({});
|
|
468
|
+
this.htmlLanguageService = (0, import_vscode_html_languageservice2.getLanguageService)({
|
|
469
|
+
customDataProviders: [dataProvider, new HTMLDataProvider(typescript, this.elements, this.getProgram)]
|
|
470
|
+
});
|
|
471
|
+
this.htmlTemplateStringSettings = {
|
|
472
|
+
tags: ["html", "raw", "h"],
|
|
473
|
+
enableForStringWithSubstitutions: true,
|
|
474
|
+
getSubstitution
|
|
475
|
+
};
|
|
476
|
+
this.cssTemplateStringSettings = {
|
|
477
|
+
tags: ["styled", "css"],
|
|
478
|
+
enableForStringWithSubstitutions: true,
|
|
479
|
+
getSubstitution,
|
|
480
|
+
isValidTemplate: (node) => isValidCSSTemplate(typescript, node, "css")
|
|
481
|
+
};
|
|
482
|
+
this.htmlSourceHelper = new import_standard_template_source_helper.default(
|
|
483
|
+
typescript,
|
|
484
|
+
this.htmlTemplateStringSettings,
|
|
485
|
+
new import_standard_script_source_helper.default(typescript, info.project),
|
|
486
|
+
logger
|
|
487
|
+
);
|
|
488
|
+
}
|
|
489
|
+
#virtualHtmlCache = new LRUCache({ max: 1e3 });
|
|
490
|
+
#virtualCssCache = new LRUCache({ max: 1e3 });
|
|
491
|
+
getCssDoc(text) {
|
|
492
|
+
return this.#virtualCssCache.get({ text, fileName: "" }, void 0, () => {
|
|
493
|
+
const vDoc = createVirtualDocument("css", text);
|
|
494
|
+
const vCss = this.cssLanguageService.parseStylesheet(vDoc);
|
|
495
|
+
return { vDoc, vCss };
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
getHtmlDoc(text) {
|
|
499
|
+
return this.#virtualHtmlCache.get({ text, fileName: "" }, void 0, () => {
|
|
500
|
+
const vDoc = createVirtualDocument("html", text);
|
|
501
|
+
const vHtml = this.htmlLanguageService.parseHTMLDocument(vDoc);
|
|
502
|
+
vHtml.roots.forEach(function transform(e, index, arr) {
|
|
503
|
+
e.prev = arr[index - 1];
|
|
504
|
+
e.next = arr[index + 1];
|
|
505
|
+
e.children.forEach(transform);
|
|
506
|
+
});
|
|
507
|
+
return { vDoc, vHtml };
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
getTagFromNode(node, supportClassName = isDepElement(node)) {
|
|
511
|
+
if (!this.ts.isClassDeclaration(node)) return;
|
|
512
|
+
for (const modifier of node.modifiers || []) {
|
|
513
|
+
if (this.ts.isDecorator(modifier) && this.ts.isCallExpression(modifier.expression) && modifier.expression.expression.getText() === "customElement") {
|
|
514
|
+
const arg = modifier.expression.arguments.at(0);
|
|
515
|
+
if (arg && this.ts.isStringLiteral(arg)) {
|
|
516
|
+
return arg.text;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
if (supportClassName && node.name && this.ts.isIdentifier(node.name)) {
|
|
521
|
+
return this.config.elementDefineRules.findTag(node.name.text);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
updateElement(file) {
|
|
525
|
+
const isDep = isDepElement(file);
|
|
526
|
+
this.ts.forEachChild(file, (node) => {
|
|
527
|
+
const tag = this.getTagFromNode(node, isDep);
|
|
528
|
+
if (tag && this.ts.isClassDeclaration(node)) {
|
|
529
|
+
this.elements.set(tag, node);
|
|
530
|
+
}
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
#initElementsCache = /* @__PURE__ */ new WeakSet();
|
|
534
|
+
/**
|
|
535
|
+
* 当 project 准备好了执行
|
|
536
|
+
*/
|
|
537
|
+
initElements() {
|
|
538
|
+
const program = this.getProgram();
|
|
539
|
+
if (this.#initElementsCache.has(program)) return;
|
|
540
|
+
const files = program.getSourceFiles();
|
|
541
|
+
files.forEach((file) => this.updateElement(file));
|
|
542
|
+
const typeChecker = program.getTypeChecker();
|
|
543
|
+
const symbols = typeChecker.getSymbolsInScope(files.at(0), this.ts.SymbolFlags.Interface);
|
|
544
|
+
symbols.forEach((symbol) => {
|
|
545
|
+
const name = symbol.escapedName.toString();
|
|
546
|
+
const match = name.match(/^(SVG|HTML)(\w*)Element$/);
|
|
547
|
+
const declaration = symbol.declarations?.find((e) => this.ts.isInterfaceDeclaration(e));
|
|
548
|
+
if (!match || !declaration) return;
|
|
549
|
+
if (name in partialBuiltInElementMap) {
|
|
550
|
+
partialBuiltInElementMap[name].forEach((e) => this.builtInElements.set(e, declaration));
|
|
551
|
+
} else {
|
|
552
|
+
this.builtInElements.set(match[2].toLowerCase(), declaration);
|
|
553
|
+
}
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
};
|
|
557
|
+
var partialBuiltInElementMap = {
|
|
558
|
+
SVGAElement: [],
|
|
559
|
+
HTMLAnchorElement: ["a"],
|
|
560
|
+
SVGImageElement: [],
|
|
561
|
+
HTMLImageElement: ["img"],
|
|
562
|
+
SVGStyleElement: [],
|
|
563
|
+
HTMLStyleElement: ["style"],
|
|
564
|
+
HTMLDListElement: ["dl"],
|
|
565
|
+
HTMLOListElement: ["ol"],
|
|
566
|
+
HTMLUListElement: ["ul"],
|
|
567
|
+
HTMLHeadingElement: ["h1", "h2", "h3", "h4", "h5", "h6"],
|
|
568
|
+
HTMLModElement: ["del", "ins"],
|
|
569
|
+
HTMLQuoteElement: ["blockquote", "q", "cite"],
|
|
570
|
+
HTMLTableCaptionElement: ["caption"],
|
|
571
|
+
HTMLTableCellElement: ["th", "td"],
|
|
572
|
+
HTMLTableColElement: ["col"],
|
|
573
|
+
HTMLTableRowElement: ["tr"],
|
|
574
|
+
HTMLTableSectionElement: ["thead", "tfoot", "tbody"]
|
|
575
|
+
};
|
|
218
576
|
function createVirtualDocument(languageId, content) {
|
|
219
|
-
return
|
|
577
|
+
return import_vscode_html_languageservice2.TextDocument.create(`embedded://document.${languageId}`, languageId, 1, content);
|
|
220
578
|
}
|
|
221
579
|
function getSubstitution(templateString, start, end) {
|
|
222
580
|
return templateString.slice(start, end).replaceAll(/[^\n]/g, "_");
|
|
@@ -240,6 +598,13 @@ function isValidCSSTemplate(typescript, node, callName) {
|
|
|
240
598
|
return false;
|
|
241
599
|
}
|
|
242
600
|
}
|
|
601
|
+
|
|
602
|
+
// src/decorate-css.ts
|
|
603
|
+
var import_vscode_css_languageservice2 = require("@mantou/vscode-css-languageservice");
|
|
604
|
+
var import_vscode_emmet_helper = require("@mantou/vscode-emmet-helper");
|
|
605
|
+
|
|
606
|
+
// src/translates.ts
|
|
607
|
+
var vscode = __toESM(require("vscode-languageserver-types"));
|
|
243
608
|
function translateCompletionItemsToCompletionInfo(context, items) {
|
|
244
609
|
return {
|
|
245
610
|
defaultCommitCharacters: [],
|
|
@@ -370,126 +735,160 @@ function toDisplayParts(text, isDoc = false) {
|
|
|
370
735
|
}
|
|
371
736
|
];
|
|
372
737
|
}
|
|
738
|
+
function genElementDefinitionInfo(context, { start, length }, definitionNode) {
|
|
739
|
+
const htmlOffset = context.node.pos + 1;
|
|
740
|
+
return {
|
|
741
|
+
textSpan: { start, length },
|
|
742
|
+
definitions: [
|
|
743
|
+
{
|
|
744
|
+
containerName: "Custom Element",
|
|
745
|
+
containerKind: context.typescript.ScriptElementKind.unknown,
|
|
746
|
+
name: definitionNode.name.text,
|
|
747
|
+
kind: context.typescript.ScriptElementKind.classElement,
|
|
748
|
+
fileName: definitionNode.getSourceFile().fileName,
|
|
749
|
+
textSpan: {
|
|
750
|
+
start: definitionNode.name.getStart() - htmlOffset,
|
|
751
|
+
length: definitionNode.name.text.length
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
]
|
|
755
|
+
};
|
|
756
|
+
}
|
|
757
|
+
function genAttrDefinitionInfo(context, { start, length }, propDeclaration) {
|
|
758
|
+
const htmlOffset = context.node.pos + 1;
|
|
759
|
+
return {
|
|
760
|
+
textSpan: { start, length },
|
|
761
|
+
definitions: [
|
|
762
|
+
{
|
|
763
|
+
containerName: "Attribute",
|
|
764
|
+
containerKind: context.typescript.ScriptElementKind.unknown,
|
|
765
|
+
name: propDeclaration.getText(),
|
|
766
|
+
kind: context.typescript.ScriptElementKind.memberVariableElement,
|
|
767
|
+
fileName: propDeclaration.getSourceFile().fileName,
|
|
768
|
+
textSpan: {
|
|
769
|
+
start: propDeclaration.getStart() - htmlOffset,
|
|
770
|
+
length: propDeclaration.getText().length
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
]
|
|
774
|
+
};
|
|
775
|
+
}
|
|
776
|
+
function genCurrentCtxDefinitionInfo(context, { start, length }, definitionTextSpan) {
|
|
777
|
+
return {
|
|
778
|
+
textSpan: { start, length },
|
|
779
|
+
definitions: [
|
|
780
|
+
{
|
|
781
|
+
containerName: "Attribute",
|
|
782
|
+
containerKind: context.typescript.ScriptElementKind.unknown,
|
|
783
|
+
name: context.text.slice(start, start + length),
|
|
784
|
+
kind: context.typescript.ScriptElementKind.memberVariableElement,
|
|
785
|
+
fileName: context.fileName,
|
|
786
|
+
textSpan: definitionTextSpan
|
|
787
|
+
}
|
|
788
|
+
]
|
|
789
|
+
};
|
|
790
|
+
}
|
|
373
791
|
|
|
374
|
-
//
|
|
375
|
-
var
|
|
376
|
-
#max;
|
|
377
|
-
#
|
|
378
|
-
#
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
#linkedList = new LinkedList();
|
|
382
|
-
constructor({ max = Infinity, maxAge = Infinity, renewal = false } = {}) {
|
|
383
|
-
this.#max = max;
|
|
384
|
-
this.#maxAge = maxAge;
|
|
385
|
-
this.#renewal = renewal;
|
|
792
|
+
// src/decorate-css.ts
|
|
793
|
+
var CSSLanguageService = class {
|
|
794
|
+
#completionsCache = new LRUCache({ max: 1 });
|
|
795
|
+
#diagnosticsCache = new LRUCache();
|
|
796
|
+
#ctx;
|
|
797
|
+
constructor(ctx) {
|
|
798
|
+
this.#ctx = ctx;
|
|
386
799
|
}
|
|
387
|
-
#
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
}
|
|
800
|
+
#normalize(context, position) {
|
|
801
|
+
const parent = context.node.parent;
|
|
802
|
+
const tag = context.typescript.isTaggedTemplateExpression(parent) && parent.tag.getText();
|
|
803
|
+
const isStyle = context.typescript.isPropertyAssignment(parent) || tag === "styled";
|
|
804
|
+
if (!isStyle) return { offset: 0, text: context.text, pos: { ...position } };
|
|
805
|
+
const appendBefore = ".parent { ";
|
|
806
|
+
const appendAfter = " }";
|
|
807
|
+
const character = position.line === 0 ? position.character + appendBefore.length : position.character;
|
|
808
|
+
return {
|
|
809
|
+
offset: appendBefore.length,
|
|
810
|
+
text: `${appendBefore}${context.text}${appendAfter}`,
|
|
811
|
+
pos: { line: position.line, character }
|
|
812
|
+
};
|
|
394
813
|
}
|
|
395
|
-
|
|
396
|
-
this.#
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
814
|
+
#getCompletionsAtPosition(context, position) {
|
|
815
|
+
return this.#completionsCache.get(context, position, () => {
|
|
816
|
+
const { text, pos } = this.#normalize(context, position);
|
|
817
|
+
const { vDoc, vCss } = this.#ctx.getCssDoc(text);
|
|
818
|
+
let emmetResults;
|
|
819
|
+
const onCssProperty = () => emmetResults = (0, import_vscode_emmet_helper.doComplete)(vDoc, pos, "css", this.#ctx.config.emmet);
|
|
820
|
+
this.#ctx.cssLanguageService.setCompletionParticipants([{ onCssProperty }]);
|
|
821
|
+
(0, import_vscode_css_languageservice2.updateTags)([...this.#ctx.elements].map(([tag]) => tag));
|
|
822
|
+
const completions = this.#ctx.cssLanguageService.doComplete(vDoc, pos, vCss);
|
|
823
|
+
completions.items.push(...emmetResults?.items || []);
|
|
824
|
+
return completions;
|
|
825
|
+
});
|
|
401
826
|
}
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
if (!cache) {
|
|
405
|
-
return init && this.set(key, init(key));
|
|
406
|
-
}
|
|
407
|
-
const { timestamp, value } = cache;
|
|
408
|
-
if (Date.now() - timestamp > this.#maxAge) {
|
|
409
|
-
this.#linkedList.delete(value);
|
|
410
|
-
this.#reverseMap.delete(value);
|
|
411
|
-
this.#map.delete(key);
|
|
412
|
-
return init && this.set(key, init(key));
|
|
413
|
-
}
|
|
414
|
-
if (this.#renewal) {
|
|
415
|
-
cache.timestamp = Date.now();
|
|
416
|
-
}
|
|
417
|
-
this.#linkedList.get();
|
|
418
|
-
this.#linkedList.add(value);
|
|
419
|
-
return value;
|
|
827
|
+
getCompletionsAtPosition(context, position) {
|
|
828
|
+
return translateCompletionItemsToCompletionInfo(context, this.#getCompletionsAtPosition(context, position));
|
|
420
829
|
}
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
#genKey(context, position) {
|
|
427
|
-
return [context.fileName, position?.line, position?.character, context.text].join(";");
|
|
830
|
+
getCompletionEntryDetails(context, position, name) {
|
|
831
|
+
const completions = this.#getCompletionsAtPosition(context, position);
|
|
832
|
+
const item = completions.items.find((e) => e.label === name);
|
|
833
|
+
if (!item) return genDefaultCompletionEntryDetails(context, name);
|
|
834
|
+
return translateCompletionItemsToCompletionEntryDetails(context, item);
|
|
428
835
|
}
|
|
429
|
-
|
|
430
|
-
|
|
836
|
+
getQuickInfoAtPosition(context, position) {
|
|
837
|
+
const { text, pos } = this.#normalize(context, position);
|
|
838
|
+
const { vDoc, vCss } = this.#ctx.getCssDoc(text);
|
|
839
|
+
const hover = this.#ctx.cssLanguageService.doHover(vDoc, pos, vCss, {
|
|
840
|
+
documentation: true,
|
|
841
|
+
references: true
|
|
842
|
+
});
|
|
843
|
+
if (!hover) return;
|
|
844
|
+
return translateHover(context, hover, position, pos.character - position.character);
|
|
431
845
|
}
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
846
|
+
#getSyntacticDiagnostics(context) {
|
|
847
|
+
const { text, offset } = this.#normalize(context, { line: 0, character: 0 });
|
|
848
|
+
const { vDoc, vCss } = this.#ctx.getCssDoc(text);
|
|
849
|
+
const oDiagnostics = this.#ctx.cssLanguageService.doValidation(vDoc, vCss);
|
|
850
|
+
const file = this.#ctx.getProgram().getSourceFile(context.fileName);
|
|
851
|
+
return oDiagnostics.map(({ message, range }) => {
|
|
852
|
+
const start = context.toOffset(range.start);
|
|
853
|
+
return {
|
|
854
|
+
category: context.typescript.DiagnosticCategory.Warning,
|
|
855
|
+
code: 0,
|
|
856
|
+
file,
|
|
857
|
+
start: range.start.line === 0 ? start - offset : start,
|
|
858
|
+
length: context.toOffset(range.end) - start,
|
|
859
|
+
messageText: message,
|
|
860
|
+
source: NAME
|
|
861
|
+
};
|
|
862
|
+
});
|
|
863
|
+
}
|
|
864
|
+
getSyntacticDiagnostics(context) {
|
|
865
|
+
this.#ctx.initElements();
|
|
866
|
+
return this.#diagnosticsCache.get(context, void 0, () => this.#getSyntacticDiagnostics(context));
|
|
867
|
+
}
|
|
868
|
+
getDefinitionAndBoundSpan(context, position) {
|
|
869
|
+
const { text, offset } = this.#normalize(context, position);
|
|
870
|
+
const { vDoc, vCss } = this.#ctx.getCssDoc(text);
|
|
871
|
+
const empty = { textSpan: { start: 0, length: 0 } };
|
|
872
|
+
const node = vCss.findChildAtOffset(context.toOffset(position), true);
|
|
873
|
+
if (!node) return empty;
|
|
874
|
+
const ident = vDoc.getText().slice(node.offset, node.end);
|
|
875
|
+
const definitionNode = this.#ctx.elements.get(ident);
|
|
876
|
+
if (!definitionNode) return empty;
|
|
877
|
+
return genElementDefinitionInfo(context, { start: node.offset - offset, length: node.length }, definitionNode);
|
|
443
878
|
}
|
|
444
879
|
};
|
|
445
880
|
|
|
446
881
|
// src/decorate-html.ts
|
|
882
|
+
var import_vscode_css_languageservice3 = require("@mantou/vscode-css-languageservice");
|
|
883
|
+
var import_vscode_emmet_helper2 = require("@mantou/vscode-emmet-helper");
|
|
447
884
|
var HTMLLanguageService = class {
|
|
448
|
-
#completionsCache = new LRUCache();
|
|
885
|
+
#completionsCache = new LRUCache({ max: 1 });
|
|
449
886
|
#diagnosticsCache = new LRUCache();
|
|
450
|
-
#cssLanguageService;
|
|
451
|
-
#htmlLanguageService;
|
|
452
887
|
#ctx;
|
|
453
888
|
constructor(ctx) {
|
|
454
889
|
this.#ctx = ctx;
|
|
455
|
-
const ts = ctx.ts;
|
|
456
|
-
const htmlDataProvider = (0, import_vscode_html_languageservice.getDefaultHTMLDataProvider)();
|
|
457
|
-
const provideTags = htmlDataProvider.provideTags.bind(htmlDataProvider);
|
|
458
|
-
htmlDataProvider.provideTags = () => {
|
|
459
|
-
const result = [...ctx.elements].map(([tag, node]) => ({
|
|
460
|
-
name: tag,
|
|
461
|
-
attributes: [],
|
|
462
|
-
description: getDocComment(ts, node)
|
|
463
|
-
})).filter(isNotNullish);
|
|
464
|
-
return [...result, ...provideTags()];
|
|
465
|
-
};
|
|
466
|
-
const provideAttributes = htmlDataProvider.provideAttributes.bind(htmlDataProvider);
|
|
467
|
-
htmlDataProvider.provideAttributes = (tag) => {
|
|
468
|
-
const typeChecker = ctx.getProgram().getTypeChecker();
|
|
469
|
-
const node = ctx.elements.get(tag);
|
|
470
|
-
const isDep = node && isDepElement(node);
|
|
471
|
-
const props = node && typeChecker.getTypeAtLocation(node).getApparentProperties();
|
|
472
|
-
const result = (props || []).map((e) => {
|
|
473
|
-
const declaration = e.getDeclarations()?.at(0);
|
|
474
|
-
const prop = declaration && ts.isPropertyDeclaration(declaration);
|
|
475
|
-
if (!prop) return;
|
|
476
|
-
const hasPropDecorator = declaration.modifiers?.some(
|
|
477
|
-
(m) => ts.isDecorator(m) && ts.isIdentifier(m.expression)
|
|
478
|
-
);
|
|
479
|
-
if (!hasPropDecorator && !isDep) return;
|
|
480
|
-
const type = declaration.type && typeChecker.getTypeFromTypeNode(declaration.type);
|
|
481
|
-
return {
|
|
482
|
-
name: type === typeChecker.getBooleanType() ? e.name : `.${e.name}`,
|
|
483
|
-
description: getDocComment(ts, declaration),
|
|
484
|
-
valueSet: "v"
|
|
485
|
-
};
|
|
486
|
-
}).filter(isNotNullish);
|
|
487
|
-
return [...result, ...provideAttributes(isCustomElement(tag) ? "div" : tag)];
|
|
488
|
-
};
|
|
489
|
-
this.#htmlLanguageService = (0, import_vscode_html_languageservice.getLanguageService)({ customDataProviders: [htmlDataProvider] });
|
|
490
|
-
this.#cssLanguageService = (0, import_vscode_css_languageservice.getCSSLanguageService)({ useDefaultDataProvider: true });
|
|
491
890
|
}
|
|
492
|
-
#
|
|
891
|
+
#getAllStyleSheet(doc) {
|
|
493
892
|
const styles = [];
|
|
494
893
|
const nodes = [...doc.roots];
|
|
495
894
|
while (true) {
|
|
@@ -500,17 +899,17 @@ var HTMLLanguageService = class {
|
|
|
500
899
|
}
|
|
501
900
|
return styles;
|
|
502
901
|
}
|
|
503
|
-
#
|
|
902
|
+
#getEmbeddedCss(context, position, doc) {
|
|
504
903
|
const node = doc.findNodeAt(context.toOffset(position));
|
|
505
904
|
if (node.tag !== "style") return;
|
|
506
|
-
const
|
|
507
|
-
const
|
|
905
|
+
const text = context.text.slice(node.startTagEnd, node.endTagStart);
|
|
906
|
+
const { vDoc, vCss } = this.#ctx.getCssDoc(text);
|
|
508
907
|
const offset = context.toOffset(position) - node.startTagEnd;
|
|
509
|
-
const toPosition = (pos) => context.toPosition(
|
|
908
|
+
const toPosition = (pos) => context.toPosition(vDoc.offsetAt(pos) + node.startTagEnd);
|
|
510
909
|
return {
|
|
511
|
-
style,
|
|
512
|
-
|
|
513
|
-
position:
|
|
910
|
+
style: vCss,
|
|
911
|
+
vDoc,
|
|
912
|
+
position: vDoc.positionAt(offset),
|
|
514
913
|
updateRange: (range) => ({
|
|
515
914
|
start: toPosition(range.start),
|
|
516
915
|
end: toPosition(range.end)
|
|
@@ -518,43 +917,34 @@ var HTMLLanguageService = class {
|
|
|
518
917
|
};
|
|
519
918
|
}
|
|
520
919
|
#getCSSCompletionsAtPosition(context, position, doc) {
|
|
521
|
-
const css = this.#
|
|
920
|
+
const css = this.#getEmbeddedCss(context, position, doc);
|
|
522
921
|
if (!css) return [];
|
|
523
922
|
let emmetResults;
|
|
524
|
-
this.#
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
]);
|
|
531
|
-
const completions = this.#cssLanguageService.doComplete(css.virtualDocument, css.position, css.style);
|
|
923
|
+
const onCssProperty = () => emmetResults = (0, import_vscode_emmet_helper2.doComplete)(css.vDoc, css.position, "css", this.#ctx.config.emmet);
|
|
924
|
+
this.#ctx.cssLanguageService.setCompletionParticipants([{ onCssProperty }]);
|
|
925
|
+
(0, import_vscode_css_languageservice3.updateTags)([...this.#ctx.elements].map(([tag]) => tag));
|
|
926
|
+
const completions = this.#ctx.cssLanguageService.doComplete(css.vDoc, css.position, css.style);
|
|
532
927
|
completions.items.push(...emmetResults?.items || []);
|
|
533
|
-
return completions.items.map((e) =>
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
} : e.textEdit
|
|
539
|
-
}));
|
|
928
|
+
return completions.items.map((e) => {
|
|
929
|
+
const textEdit = e.textEdit && "range" in e.textEdit && e.textEdit;
|
|
930
|
+
const newTextEdit = textEdit && { newText: textEdit.newText, range: css.updateRange(textEdit.range) };
|
|
931
|
+
return { ...e, textEdit: newTextEdit || e.textEdit };
|
|
932
|
+
});
|
|
540
933
|
}
|
|
541
934
|
#getCompletionsAtPosition(context, position) {
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
completions.items.push(...emmetResults?.items || []);
|
|
556
|
-
completions.items.push(...this.#getCSSCompletionsAtPosition(context, position, vHtml));
|
|
557
|
-
return this.#completionsCache.updateCached(context, position, completions);
|
|
935
|
+
return this.#completionsCache.get(context, position, () => {
|
|
936
|
+
const { vDoc, vHtml } = this.#ctx.getHtmlDoc(context.text);
|
|
937
|
+
let emmetResults;
|
|
938
|
+
const onHtmlContent = () => {
|
|
939
|
+
(0, import_vscode_emmet_helper2.updateTags)([...this.#ctx.elements].map(([tag]) => tag));
|
|
940
|
+
emmetResults = (0, import_vscode_emmet_helper2.doComplete)(vDoc, position, "html", this.#ctx.config.emmet);
|
|
941
|
+
};
|
|
942
|
+
this.#ctx.htmlLanguageService.setCompletionParticipants([{ onHtmlContent }]);
|
|
943
|
+
const completions = this.#ctx.htmlLanguageService.doComplete(vDoc, position, vHtml);
|
|
944
|
+
completions.items.push(...emmetResults?.items || []);
|
|
945
|
+
completions.items.push(...this.#getCSSCompletionsAtPosition(context, position, vHtml));
|
|
946
|
+
return completions;
|
|
947
|
+
});
|
|
558
948
|
}
|
|
559
949
|
getCompletionsAtPosition(context, position) {
|
|
560
950
|
return translateCompletionItemsToCompletionInfo(context, this.#getCompletionsAtPosition(context, position));
|
|
@@ -566,163 +956,323 @@ var HTMLLanguageService = class {
|
|
|
566
956
|
return translateCompletionItemsToCompletionEntryDetails(context, item);
|
|
567
957
|
}
|
|
568
958
|
#getCSSQuickInfoAtPosition(context, position, doc) {
|
|
569
|
-
const css = this.#
|
|
570
|
-
|
|
959
|
+
const css = this.#getEmbeddedCss(context, position, doc);
|
|
960
|
+
if (!css) return;
|
|
961
|
+
const hover = this.#ctx.cssLanguageService.doHover(css.vDoc, css.position, css.style, {
|
|
571
962
|
documentation: true,
|
|
572
963
|
references: true
|
|
573
964
|
});
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
range: hover.range && css.updateRange(hover.range)
|
|
577
|
-
};
|
|
965
|
+
if (!hover) return;
|
|
966
|
+
return { ...hover, range: hover.range && css.updateRange(hover.range) };
|
|
578
967
|
}
|
|
579
968
|
getQuickInfoAtPosition(context, position) {
|
|
580
|
-
const
|
|
581
|
-
const
|
|
582
|
-
const hover = this.#htmlLanguageService.doHover(virtualDocument, position, vHtml, {
|
|
969
|
+
const { vDoc, vHtml } = this.#ctx.getHtmlDoc(context.text);
|
|
970
|
+
const htmlHover = this.#ctx.htmlLanguageService.doHover(vDoc, position, vHtml, {
|
|
583
971
|
documentation: true,
|
|
584
972
|
references: true
|
|
585
|
-
})
|
|
973
|
+
});
|
|
974
|
+
const hover = htmlHover || this.#getCSSQuickInfoAtPosition(context, position, vHtml);
|
|
586
975
|
if (!hover) return;
|
|
587
976
|
return translateHover(context, hover, position);
|
|
588
977
|
}
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
return oDiagnostics.map(({ message, range }) => {
|
|
603
|
-
const start = node.startTagEnd + textDocument.offsetAt(range.start);
|
|
604
|
-
const end = node.startTagEnd + textDocument.offsetAt(range.end);
|
|
605
|
-
return {
|
|
978
|
+
#getCssSyntacticDiagnostics(context) {
|
|
979
|
+
return this.#diagnosticsCache.get(context, void 0, () => {
|
|
980
|
+
const { vHtml } = this.#ctx.getHtmlDoc(context.text);
|
|
981
|
+
const file = this.#ctx.getProgram().getSourceFile(context.fileName);
|
|
982
|
+
const styles = this.#getAllStyleSheet(vHtml);
|
|
983
|
+
const diagnostics = [];
|
|
984
|
+
styles.forEach((node) => {
|
|
985
|
+
const text = context.text.slice(node.startTagEnd, node.endTagStart);
|
|
986
|
+
const { vDoc, vCss } = this.#ctx.getCssDoc(text);
|
|
987
|
+
this.#ctx.cssLanguageService.doValidation(vDoc, vCss).forEach(({ message, range }) => {
|
|
988
|
+
const start = node.startTagEnd + vDoc.offsetAt(range.start);
|
|
989
|
+
const end = node.startTagEnd + vDoc.offsetAt(range.end);
|
|
990
|
+
diagnostics.push({
|
|
606
991
|
category: context.typescript.DiagnosticCategory.Warning,
|
|
607
992
|
code: 0,
|
|
608
993
|
file,
|
|
609
994
|
start,
|
|
610
995
|
length: end - start,
|
|
611
|
-
messageText: message
|
|
612
|
-
|
|
996
|
+
messageText: message,
|
|
997
|
+
source: NAME
|
|
998
|
+
});
|
|
613
999
|
});
|
|
614
|
-
})
|
|
615
|
-
|
|
1000
|
+
});
|
|
1001
|
+
return diagnostics;
|
|
1002
|
+
});
|
|
1003
|
+
}
|
|
1004
|
+
#getHtmlSyntacticDiagnostics(context) {
|
|
1005
|
+
const offset = context.node.getStart() + 1;
|
|
1006
|
+
const { vHtml } = this.#ctx.getHtmlDoc(context.text);
|
|
1007
|
+
const program = this.#ctx.getProgram();
|
|
1008
|
+
const file = program.getSourceFile(context.fileName);
|
|
1009
|
+
const typeChecker = program.getTypeChecker();
|
|
1010
|
+
const diagnostics = [];
|
|
1011
|
+
forEachNode(vHtml.roots, (node) => {
|
|
1012
|
+
if (!node.tag) return;
|
|
1013
|
+
if (isCustomElementTag(node.tag) && !this.#ctx.elements.get(node.tag)) {
|
|
1014
|
+
diagnostics.push({
|
|
1015
|
+
category: context.typescript.DiagnosticCategory.Warning,
|
|
1016
|
+
code: 101 /* UnknownTag */,
|
|
1017
|
+
file,
|
|
1018
|
+
start: node.start + 1,
|
|
1019
|
+
length: node.tag.length,
|
|
1020
|
+
messageText: `Unknown element tag '${node.tag}'`,
|
|
1021
|
+
source: NAME
|
|
1022
|
+
});
|
|
1023
|
+
}
|
|
1024
|
+
const tagDeclaration = this.#ctx.elements.get(node.tag) || this.#ctx.builtInElements.get(node.tag);
|
|
1025
|
+
if (!tagDeclaration) return;
|
|
1026
|
+
for (const [attributeName, { value, start, end }] of node.attributesMap) {
|
|
1027
|
+
if (attributeName.startsWith("_")) continue;
|
|
1028
|
+
const hasValueSpan = value?.startsWith("_");
|
|
1029
|
+
const attrInfo = getAttrName(attributeName);
|
|
1030
|
+
const propType = getPropType(typeChecker, tagDeclaration, attrInfo);
|
|
1031
|
+
const diagnostic = {
|
|
1032
|
+
category: context.typescript.DiagnosticCategory.Warning,
|
|
1033
|
+
file,
|
|
1034
|
+
start,
|
|
1035
|
+
length: end - start,
|
|
1036
|
+
source: NAME,
|
|
1037
|
+
code: 103 /* PropTypeError */,
|
|
1038
|
+
messageText: !propType ? `'${attributeName}' type error` : `'${attributeName}' not satisfied '${typeChecker.typeToString(propType)}'`
|
|
1039
|
+
};
|
|
1040
|
+
if ((attributeName === "v-else-if" || attributeName === "v-else") && !node.prev?.attributesMap.has("v-if") && !node.prev?.attributesMap.has("v-else-if")) {
|
|
1041
|
+
diagnostics.push({
|
|
1042
|
+
...diagnostic,
|
|
1043
|
+
code: 104 /* PropSyntaxError */,
|
|
1044
|
+
messageText: `'${attrInfo.attr}' syntax error`
|
|
1045
|
+
});
|
|
1046
|
+
}
|
|
1047
|
+
if (attributeName === "v-if" || attributeName === "v-else-if") {
|
|
1048
|
+
const spanType = getSpanType(this.#ctx.ts, typeChecker, file, offset, end);
|
|
1049
|
+
if (!hasValueSpan || !typeChecker.isTypeAssignableTo(spanType, typeChecker.getBooleanType())) {
|
|
1050
|
+
diagnostics.push(diagnostic);
|
|
1051
|
+
}
|
|
1052
|
+
continue;
|
|
1053
|
+
}
|
|
1054
|
+
if (attributeName === "v-else") {
|
|
1055
|
+
if (value !== null) {
|
|
1056
|
+
diagnostics.push(diagnostic);
|
|
1057
|
+
}
|
|
1058
|
+
continue;
|
|
1059
|
+
}
|
|
1060
|
+
if (!propType) {
|
|
1061
|
+
if (attrInfo.decorate !== "@") {
|
|
1062
|
+
diagnostics.push({
|
|
1063
|
+
...diagnostic,
|
|
1064
|
+
code: 102 /* UnknownProp */,
|
|
1065
|
+
messageText: `Unknown property '${attrInfo.attr}'`
|
|
1066
|
+
});
|
|
1067
|
+
}
|
|
1068
|
+
continue;
|
|
1069
|
+
}
|
|
1070
|
+
if (value === null) {
|
|
1071
|
+
if (attrInfo.decorate) {
|
|
1072
|
+
diagnostics.push({
|
|
1073
|
+
...diagnostic,
|
|
1074
|
+
code: 104 /* PropSyntaxError */,
|
|
1075
|
+
messageText: `Consider using '${camelToKebabCase(attrInfo.attr)}'`
|
|
1076
|
+
});
|
|
1077
|
+
} else if (propType !== typeChecker.getBooleanType()) {
|
|
1078
|
+
diagnostics.push(diagnostic);
|
|
1079
|
+
}
|
|
1080
|
+
continue;
|
|
1081
|
+
}
|
|
1082
|
+
if (hasValueSpan) {
|
|
1083
|
+
const spanType = getSpanType(this.#ctx.ts, typeChecker, file, offset, end);
|
|
1084
|
+
switch (attrInfo.decorate) {
|
|
1085
|
+
case "?":
|
|
1086
|
+
const boolType = getUnionType(typeChecker, [
|
|
1087
|
+
typeChecker.getBooleanType(),
|
|
1088
|
+
typeChecker.getUndefinedType(),
|
|
1089
|
+
typeChecker.getNullType()
|
|
1090
|
+
]);
|
|
1091
|
+
if (!typeChecker.isTypeAssignableTo(spanType, boolType)) {
|
|
1092
|
+
diagnostics.push(diagnostic);
|
|
1093
|
+
}
|
|
1094
|
+
continue;
|
|
1095
|
+
case ".":
|
|
1096
|
+
case "@":
|
|
1097
|
+
if (!typeChecker.isTypeAssignableTo(spanType, propType)) {
|
|
1098
|
+
diagnostics.push(diagnostic);
|
|
1099
|
+
}
|
|
1100
|
+
continue;
|
|
1101
|
+
default:
|
|
1102
|
+
if (!typeChecker.isTypeAssignableTo(spanType, propType) && (!typeChecker.isTypeAssignableTo(propType, typeChecker.getStringType()) || !typeChecker.isTypeAssignableTo(spanType, typeChecker.getStringType()))) {
|
|
1103
|
+
diagnostics.push(diagnostic);
|
|
1104
|
+
}
|
|
1105
|
+
continue;
|
|
1106
|
+
}
|
|
1107
|
+
} else {
|
|
1108
|
+
const types = [typeChecker.getStringType(), typeChecker.getNumberType()];
|
|
1109
|
+
const valueLetter = value.startsWith('"') ? value.slice(1, -1) : value;
|
|
1110
|
+
types.push(typeChecker.getStringLiteralType(valueLetter));
|
|
1111
|
+
const numberValue = Number(valueLetter);
|
|
1112
|
+
if (!Number.isNaN(numberValue)) {
|
|
1113
|
+
types.push(typeChecker.getNumberLiteralType(numberValue));
|
|
1114
|
+
}
|
|
1115
|
+
if (attrInfo.decorate) {
|
|
1116
|
+
diagnostics.push({
|
|
1117
|
+
...diagnostic,
|
|
1118
|
+
code: 104 /* PropSyntaxError */,
|
|
1119
|
+
messageText: `Consider using '${camelToKebabCase(attrInfo.attr)}'`
|
|
1120
|
+
});
|
|
1121
|
+
} else if (types.every((t) => !typeChecker.isTypeAssignableTo(t, propType))) {
|
|
1122
|
+
diagnostics.push(diagnostic);
|
|
1123
|
+
}
|
|
1124
|
+
continue;
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
});
|
|
1128
|
+
return diagnostics;
|
|
1129
|
+
}
|
|
1130
|
+
getSyntacticDiagnostics(context) {
|
|
1131
|
+
this.#ctx.initElements();
|
|
1132
|
+
return [...this.#getCssSyntacticDiagnostics(context), ...this.#getHtmlSyntacticDiagnostics(context)];
|
|
616
1133
|
}
|
|
617
1134
|
getDefinitionAndBoundSpan(context, position) {
|
|
618
|
-
const
|
|
619
|
-
const
|
|
620
|
-
const
|
|
621
|
-
const
|
|
622
|
-
const
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
1135
|
+
const currentOffset = context.toOffset(position);
|
|
1136
|
+
const { vHtml } = this.#ctx.getHtmlDoc(context.text);
|
|
1137
|
+
const node = vHtml.findNodeAt(currentOffset);
|
|
1138
|
+
const { text, start, length, before } = getHTMLTextAtPosition(context.text, currentOffset);
|
|
1139
|
+
const empty = { textSpan: { start, length } };
|
|
1140
|
+
if (node.tag === "style" && currentOffset > node.startTagEnd) {
|
|
1141
|
+
const { style, vDoc, position: pos } = this.#getEmbeddedCss(context, position, vHtml);
|
|
1142
|
+
const cssNode = style.findChildAtOffset(vDoc.offsetAt(pos), true);
|
|
1143
|
+
if (!cssNode) return empty;
|
|
1144
|
+
const ident = vDoc.getText().slice(cssNode.offset, cssNode.end);
|
|
1145
|
+
const definitionNode2 = this.#ctx.elements.get(ident);
|
|
1146
|
+
if (!definitionNode2) return empty;
|
|
1147
|
+
return genElementDefinitionInfo(
|
|
1148
|
+
context,
|
|
1149
|
+
{ start: cssNode.offset + node.startTagEnd, length: cssNode.length },
|
|
1150
|
+
definitionNode2
|
|
1151
|
+
);
|
|
627
1152
|
}
|
|
1153
|
+
const definitionNode = this.#ctx.elements.get(node.tag) || this.#ctx.builtInElements.get(node.tag);
|
|
1154
|
+
if (!definitionNode || currentOffset > node.startTagEnd || !text) return empty;
|
|
628
1155
|
if (text === node.tag) {
|
|
629
|
-
return {
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
]
|
|
644
|
-
};
|
|
1156
|
+
return genElementDefinitionInfo(context, { start, length }, definitionNode);
|
|
1157
|
+
}
|
|
1158
|
+
const { attr, offset } = getAttrName(text);
|
|
1159
|
+
if (before.length > attr.length) return empty;
|
|
1160
|
+
if (attr === "v-else" || attr === "v-else-if") {
|
|
1161
|
+
const ifAttr = node.prev?.attributesMap.get("v-if") || node.prev?.attributesMap.get("v-else-if");
|
|
1162
|
+
if (!ifAttr) return empty;
|
|
1163
|
+
return genCurrentCtxDefinitionInfo(
|
|
1164
|
+
context,
|
|
1165
|
+
{ start: start + offset, length: attr.length },
|
|
1166
|
+
{ start: ifAttr.start, length: ifAttr.end - ifAttr.start }
|
|
1167
|
+
);
|
|
645
1168
|
}
|
|
646
|
-
const { attr, type } = getAttrName(text);
|
|
647
1169
|
const typeChecker = this.#ctx.getProgram().getTypeChecker();
|
|
648
1170
|
const propSymbol = typeChecker.getTypeAtLocation(definitionNode).getProperty(kebabToCamelCase(attr));
|
|
649
1171
|
const propDeclaration = propSymbol?.getDeclarations()?.at(0);
|
|
650
|
-
return
|
|
651
|
-
|
|
652
|
-
definitions: !propDeclaration ? void 0 : [
|
|
653
|
-
{
|
|
654
|
-
containerName: "Attribute",
|
|
655
|
-
containerKind: context.typescript.ScriptElementKind.unknown,
|
|
656
|
-
name: propDeclaration.getText(),
|
|
657
|
-
kind: context.typescript.ScriptElementKind.memberVariableElement,
|
|
658
|
-
fileName: propDeclaration.getSourceFile().fileName,
|
|
659
|
-
textSpan: {
|
|
660
|
-
start: propDeclaration.getStart() - htmlOffset,
|
|
661
|
-
length: propDeclaration.getText().length
|
|
662
|
-
}
|
|
663
|
-
}
|
|
664
|
-
]
|
|
665
|
-
};
|
|
1172
|
+
if (!propDeclaration) return empty;
|
|
1173
|
+
return genAttrDefinitionInfo(context, { start: start + offset, length: attr.length }, propDeclaration);
|
|
666
1174
|
}
|
|
667
1175
|
};
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
try {
|
|
676
|
-
const { declarations } = args.at(2);
|
|
677
|
-
if (!declarations) return true;
|
|
678
|
-
const isNever = declarations.every(
|
|
679
|
-
(node) => ctx.ts.isPropertySignature(node) && node.type?.getText() === "never"
|
|
680
|
-
);
|
|
681
|
-
return !isNever;
|
|
682
|
-
} catch {
|
|
683
|
-
return true;
|
|
684
|
-
}
|
|
685
|
-
};
|
|
686
|
-
return typeChecker;
|
|
1176
|
+
function getSpanExpression(typescript, file, pos) {
|
|
1177
|
+
let node = getAstNodeAtPosition(typescript, file, pos);
|
|
1178
|
+
while (!typescript.isTemplateSpan(node)) {
|
|
1179
|
+
node = node.parent;
|
|
1180
|
+
if (!node) return;
|
|
1181
|
+
}
|
|
1182
|
+
return node.expression;
|
|
687
1183
|
}
|
|
688
|
-
function
|
|
689
|
-
const
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
1184
|
+
function getSpanType(typescript, typeChecker, file, htmlOffset, attrNameEnd) {
|
|
1185
|
+
const valueOffset = attrNameEnd + htmlOffset + 3;
|
|
1186
|
+
const spanExp = getSpanExpression(typescript, file, valueOffset);
|
|
1187
|
+
return typeChecker.getTypeAtLocation(spanExp);
|
|
1188
|
+
}
|
|
1189
|
+
function getPropType(typeChecker, tagClassDeclaration, attrInfo) {
|
|
1190
|
+
const classType = typeChecker.getTypeAtLocation(tagClassDeclaration);
|
|
1191
|
+
if (attrInfo.attr.startsWith("data-")) {
|
|
1192
|
+
return typeChecker.getStringType();
|
|
1193
|
+
}
|
|
1194
|
+
const propName = kebabToCamelCase(attrInfo.attr);
|
|
1195
|
+
switch (propName) {
|
|
1196
|
+
case "class":
|
|
1197
|
+
case "style":
|
|
1198
|
+
case "part":
|
|
1199
|
+
case "exportparts":
|
|
1200
|
+
return typeChecker.getStringType();
|
|
1201
|
+
case "tabindex":
|
|
1202
|
+
return typeChecker.getNumberType();
|
|
1203
|
+
case "ariaDisabled":
|
|
1204
|
+
case "ariaChecked":
|
|
1205
|
+
case "ariaHidden":
|
|
1206
|
+
return getUnionType(typeChecker, [
|
|
1207
|
+
typeChecker.getStringType(),
|
|
1208
|
+
typeChecker.getBooleanType(),
|
|
1209
|
+
typeChecker.getUndefinedType()
|
|
1210
|
+
]);
|
|
1211
|
+
default:
|
|
1212
|
+
const isEvent = attrInfo.decorate === "@";
|
|
1213
|
+
const propSymbol = classType.getProperty(propName);
|
|
1214
|
+
const propType = propSymbol && typeChecker.getTypeOfSymbol(propSymbol);
|
|
1215
|
+
if (!isEvent) return propType;
|
|
1216
|
+
const eventHandleType = getEmitterHandleType(typeChecker, classType, propType);
|
|
1217
|
+
return getUnionType(typeChecker, [eventHandleType, typeChecker.getUndefinedType()]);
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
function getEmitterHandleType(typeChecker, classType, propType) {
|
|
1221
|
+
const handleSymbol = propType?.getProperty("handler");
|
|
1222
|
+
if (handleSymbol) return typeChecker.getTypeOfSymbol(handleSymbol);
|
|
1223
|
+
const addEventListenerSymbol = classType.getProperty("addEventListener");
|
|
1224
|
+
if (!addEventListenerSymbol) return typeChecker.getAnyType();
|
|
1225
|
+
const addEventListenerDecls = addEventListenerSymbol.declarations;
|
|
1226
|
+
const addEventListenerDecl = addEventListenerDecls.find((e) => !e.typeParameters);
|
|
1227
|
+
const listenerDecl = addEventListenerDecl.parameters.at(1);
|
|
1228
|
+
if (!listenerDecl) return typeChecker.getAnyType();
|
|
1229
|
+
return typeChecker.getTypeAtLocation(listenerDecl);
|
|
1230
|
+
}
|
|
1231
|
+
function getUnionType(typeChecker, types) {
|
|
1232
|
+
if ("getUnionType" in typeChecker) {
|
|
1233
|
+
return typeChecker.getUnionType(types);
|
|
1234
|
+
}
|
|
1235
|
+
return types.at(0);
|
|
696
1236
|
}
|
|
1237
|
+
|
|
1238
|
+
// src/decorate-ts.ts
|
|
697
1239
|
function decorateLanguageService(ctx, languageService) {
|
|
698
1240
|
const { ts, getProgram } = ctx;
|
|
699
|
-
const ls =
|
|
700
|
-
Object.entries(languageService).map(([key, value]) => [key, value.bind(languageService)])
|
|
701
|
-
);
|
|
1241
|
+
const ls = bindMemberFunction(languageService);
|
|
702
1242
|
languageService.getCompletionsAtPosition = (...args) => {
|
|
703
1243
|
const program = getProgram();
|
|
704
1244
|
const typeChecker = program.getTypeChecker();
|
|
705
1245
|
decorate(typeChecker, () => decorateTypeChecker(ctx, typeChecker));
|
|
706
1246
|
return ls.getCompletionsAtPosition(...args);
|
|
707
1247
|
};
|
|
1248
|
+
languageService.getSuggestionDiagnostics = (...args) => {
|
|
1249
|
+
const program = getProgram();
|
|
1250
|
+
const file = program.getSourceFile(args[0]);
|
|
1251
|
+
const result = ls.getSuggestionDiagnostics(...args);
|
|
1252
|
+
ctx.updateElement(file);
|
|
1253
|
+
return result.filter(({ start, reportsUnnecessary, category }) => {
|
|
1254
|
+
if (!reportsUnnecessary || category !== ts.DiagnosticCategory.Suggestion) return true;
|
|
1255
|
+
const node = getAstNodeAtPosition(ts, file, start);
|
|
1256
|
+
if (!node || !ts.isPrivateIdentifier(node)) return true;
|
|
1257
|
+
const declaration = node.parent;
|
|
1258
|
+
if (!ts.isMethodDeclaration(declaration) && !ts.isPropertyDeclaration(declaration)) return true;
|
|
1259
|
+
return !declaration.modifiers?.some((e) => e?.kind === ts.SyntaxKind.Decorator);
|
|
1260
|
+
});
|
|
1261
|
+
};
|
|
708
1262
|
languageService.findReferences = (...args) => {
|
|
1263
|
+
const oResult = ls.findReferences(...args) || [];
|
|
709
1264
|
const program = getProgram();
|
|
710
|
-
const result = [];
|
|
711
1265
|
const currentNode = getAstNodeAtPosition(ts, program.getSourceFile(args[0]), args[1]);
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
});
|
|
723
|
-
if (!references.length) continue;
|
|
724
|
-
result.push({
|
|
725
|
-
references,
|
|
1266
|
+
if (!currentNode) return oResult;
|
|
1267
|
+
const isIdent = ts.isIdentifier(currentNode);
|
|
1268
|
+
if (!isIdent) return oResult;
|
|
1269
|
+
const currentTag = ctx.getTagFromNode(currentNode.parent) || ctx.getTagFromNode(currentNode.parent.parent);
|
|
1270
|
+
const prop = ts.isClassDeclaration(currentNode.parent.parent) && currentNode;
|
|
1271
|
+
if (!currentTag) return oResult;
|
|
1272
|
+
const map = /* @__PURE__ */ new Map();
|
|
1273
|
+
forEachAllHtmlTemplateNode(ctx, currentTag, (file, tagInfo) => {
|
|
1274
|
+
const symbol = map.get(file.fileName) || {
|
|
1275
|
+
references: [],
|
|
726
1276
|
definition: {
|
|
727
1277
|
containerKind: ctx.ts.ScriptElementKind.unknown,
|
|
728
1278
|
containerName: "",
|
|
@@ -732,174 +1282,163 @@ function decorateLanguageService(ctx, languageService) {
|
|
|
732
1282
|
name: "test",
|
|
733
1283
|
kind: ctx.ts.ScriptElementKind.unknown
|
|
734
1284
|
}
|
|
735
|
-
}
|
|
1285
|
+
};
|
|
1286
|
+
map.set(file.fileName, symbol);
|
|
1287
|
+
if (prop) {
|
|
1288
|
+
const propNames = /* @__PURE__ */ new Set([`.${prop.text}`]);
|
|
1289
|
+
const kebabCaseName = camelToKebabCase(prop.text);
|
|
1290
|
+
["", "?", "@"].forEach((c) => propNames.add(`${c}${kebabCaseName}`));
|
|
1291
|
+
for (const propName of propNames) {
|
|
1292
|
+
const info = tagInfo.node.attributesMap.get(propName);
|
|
1293
|
+
if (!info) continue;
|
|
1294
|
+
const textSpan = { start: info.start + tagInfo.offset, length: info.end - info.start };
|
|
1295
|
+
symbol.references.push({ fileName: file.fileName, isWriteAccess: true, textSpan });
|
|
1296
|
+
}
|
|
1297
|
+
} else {
|
|
1298
|
+
symbol.references.push({
|
|
1299
|
+
fileName: file.fileName,
|
|
1300
|
+
isWriteAccess: true,
|
|
1301
|
+
textSpan: tagInfo.open
|
|
1302
|
+
});
|
|
1303
|
+
}
|
|
1304
|
+
});
|
|
1305
|
+
return [...map.values(), ...oResult];
|
|
1306
|
+
};
|
|
1307
|
+
languageService.getRenameInfo = (fileName, position, ...args) => {
|
|
1308
|
+
const result = ls.getRenameInfo(fileName, position, ...args);
|
|
1309
|
+
const tagInfo = findCurrentTagInfo(ctx, fileName, position);
|
|
1310
|
+
if (tagInfo) {
|
|
1311
|
+
return {
|
|
1312
|
+
canRename: true,
|
|
1313
|
+
displayName: tagInfo.tag,
|
|
1314
|
+
fullDisplayName: tagInfo.tag,
|
|
1315
|
+
kind: ts.ScriptElementKind.alias,
|
|
1316
|
+
kindModifiers: "tag",
|
|
1317
|
+
triggerSpan: tagInfo.open
|
|
1318
|
+
};
|
|
736
1319
|
}
|
|
737
|
-
|
|
1320
|
+
const tagDefinedInfo = findDefinedTagInfo(ctx, fileName, position);
|
|
1321
|
+
if (tagDefinedInfo) {
|
|
1322
|
+
return {
|
|
1323
|
+
canRename: true,
|
|
1324
|
+
displayName: tagDefinedInfo.tag,
|
|
1325
|
+
fullDisplayName: tagDefinedInfo.tag,
|
|
1326
|
+
kind: ts.ScriptElementKind.alias,
|
|
1327
|
+
kindModifiers: "tag",
|
|
1328
|
+
triggerSpan: tagDefinedInfo.textSpan
|
|
1329
|
+
};
|
|
1330
|
+
}
|
|
1331
|
+
return result;
|
|
738
1332
|
};
|
|
739
|
-
languageService.
|
|
740
|
-
const
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
const
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
1333
|
+
languageService.findRenameLocations = (fileName, position, ...args) => {
|
|
1334
|
+
const tagPairInfo = findCurrentTagInfo(ctx, fileName, position);
|
|
1335
|
+
if (tagPairInfo) {
|
|
1336
|
+
const result = [{ fileName, textSpan: tagPairInfo.open }];
|
|
1337
|
+
if (tagPairInfo.end) result.push({ fileName, textSpan: tagPairInfo.end });
|
|
1338
|
+
return result;
|
|
1339
|
+
}
|
|
1340
|
+
const tagDefinedInfo = findDefinedTagInfo(ctx, fileName, position);
|
|
1341
|
+
if (tagDefinedInfo) {
|
|
1342
|
+
const result = [{ fileName, textSpan: tagDefinedInfo.textSpan }];
|
|
1343
|
+
forEachAllHtmlTemplateNode(ctx, tagDefinedInfo.tag, (f, info) => {
|
|
1344
|
+
result.push({ fileName: f.fileName, textSpan: info.open });
|
|
1345
|
+
if (info.end) result.push({ fileName: f.fileName, textSpan: info.end });
|
|
1346
|
+
});
|
|
1347
|
+
return result;
|
|
1348
|
+
}
|
|
1349
|
+
const oResult = [...ls.findRenameLocations(fileName, position, ...args) || []];
|
|
1350
|
+
const file = ctx.getProgram().getSourceFile(fileName);
|
|
1351
|
+
const node = getAstNodeAtPosition(ctx.ts, file, position);
|
|
1352
|
+
const tag = node && ts.isPropertyDeclaration(node.parent) && ctx.getTagFromNode(node.parent.parent);
|
|
1353
|
+
if (!tag) return oResult;
|
|
1354
|
+
const propText = node.getText();
|
|
1355
|
+
const kebabCaseName = camelToKebabCase(propText);
|
|
1356
|
+
if (isPropType(ctx.ts, node.parent, ["emitter", "globalemitter"])) {
|
|
1357
|
+
forEachAllHtmlTemplateNode(ctx, tag, (f, tagInfo) => {
|
|
1358
|
+
const info = tagInfo.node.attributesMap.get(`@${kebabCaseName}`);
|
|
1359
|
+
if (!info) return;
|
|
1360
|
+
const textSpan = { start: info.start + tagInfo.offset, length: info.end - info.start };
|
|
1361
|
+
oResult.push({ fileName: f.fileName, prefixText: "@", textSpan });
|
|
1362
|
+
});
|
|
1363
|
+
}
|
|
1364
|
+
if (isPropType(ctx.ts, node.parent, ["attribute", "numattribute", "boolattribute", "property"])) {
|
|
1365
|
+
forEachAllHtmlTemplateNode(ctx, tag, (f, tagInfo) => {
|
|
1366
|
+
const propNames = ["", ".", "?"].map((c) => `${c}${kebabCaseName}`);
|
|
1367
|
+
propNames.map((propName) => {
|
|
1368
|
+
const info = tagInfo.node.attributesMap.get(propName);
|
|
1369
|
+
if (!info) return;
|
|
1370
|
+
const textSpan = { start: info.start + tagInfo.offset, length: info.end - info.start };
|
|
1371
|
+
oResult.push({ fileName: f.fileName, prefixText: ".", textSpan });
|
|
1372
|
+
});
|
|
1373
|
+
});
|
|
1374
|
+
}
|
|
1375
|
+
return oResult;
|
|
756
1376
|
};
|
|
757
1377
|
return languageService;
|
|
758
1378
|
}
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
};
|
|
768
|
-
|
|
769
|
-
#emmet = defaultConfiguration.emmet;
|
|
770
|
-
#format = defaultConfiguration.format;
|
|
771
|
-
#tags = defaultConfiguration.tags;
|
|
772
|
-
update(config) {
|
|
773
|
-
this.#format = Object.assign({}, defaultConfiguration.format, config.format || {});
|
|
774
|
-
this.#tags = config.tags || defaultConfiguration.tags;
|
|
775
|
-
}
|
|
776
|
-
get emmet() {
|
|
777
|
-
return this.#emmet;
|
|
778
|
-
}
|
|
779
|
-
get format() {
|
|
780
|
-
return this.#format;
|
|
781
|
-
}
|
|
782
|
-
get tags() {
|
|
783
|
-
return this.#tags;
|
|
784
|
-
}
|
|
785
|
-
};
|
|
786
|
-
var Store = class {
|
|
787
|
-
#map = /* @__PURE__ */ new Map();
|
|
788
|
-
#weakMap = /* @__PURE__ */ new WeakMap();
|
|
789
|
-
#registry = new FinalizationRegistry((key) => this.#map.delete(key));
|
|
790
|
-
set(key, val) {
|
|
791
|
-
this.#map.set(key, new WeakRef(val));
|
|
792
|
-
this.#weakMap.set(val, key);
|
|
793
|
-
this.#registry.register(val, key);
|
|
794
|
-
}
|
|
795
|
-
get(key) {
|
|
796
|
-
return this.#map.get(key)?.deref();
|
|
1379
|
+
function forEachAllHtmlTemplateNode(ctx, tag, fn) {
|
|
1380
|
+
for (const file of ctx.getProgram().getSourceFiles()) {
|
|
1381
|
+
if (file.fileName.endsWith(".d.ts")) continue;
|
|
1382
|
+
for (const templateContext of ctx.htmlSourceHelper.getAllTemplates(file.fileName)) {
|
|
1383
|
+
const { vHtml } = ctx.getHtmlDoc(templateContext.text);
|
|
1384
|
+
forEachNode(vHtml.roots, (node) => {
|
|
1385
|
+
if (node.tag !== tag) return;
|
|
1386
|
+
fn(file, getTagInfo(node, templateContext.node.getStart() + 1));
|
|
1387
|
+
});
|
|
1388
|
+
}
|
|
797
1389
|
}
|
|
798
|
-
|
|
799
|
-
|
|
1390
|
+
}
|
|
1391
|
+
function findCurrentTagInfo(ctx, fileName, position) {
|
|
1392
|
+
const templateContext = ctx.htmlSourceHelper.getTemplate(fileName, position);
|
|
1393
|
+
if (!templateContext) return;
|
|
1394
|
+
const htmlOffset = templateContext.node.pos + 1;
|
|
1395
|
+
const { vHtml } = ctx.getHtmlDoc(templateContext.text);
|
|
1396
|
+
const relativePosition = ctx.htmlSourceHelper.getRelativePosition(templateContext, position);
|
|
1397
|
+
const offset = templateContext.toOffset(relativePosition);
|
|
1398
|
+
const node = vHtml.findNodeAt(offset);
|
|
1399
|
+
const { text } = getHTMLTextAtPosition(templateContext.text, offset);
|
|
1400
|
+
const onTag = offset < node.startTagEnd && text === node.tag;
|
|
1401
|
+
if (!onTag || !node.tag) return;
|
|
1402
|
+
return getTagInfo(node, htmlOffset);
|
|
1403
|
+
}
|
|
1404
|
+
function findDefinedTagInfo(ctx, fileName, position) {
|
|
1405
|
+
const file = ctx.getProgram().getSourceFile(fileName);
|
|
1406
|
+
const node = getAstNodeAtPosition(ctx.ts, file, position);
|
|
1407
|
+
if (!node || !ctx.ts.isStringLiteral(node) || !ctx.ts.isCallExpression(node.parent) || node.parent.expression.getText() !== "customElement") {
|
|
1408
|
+
return;
|
|
800
1409
|
}
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
1410
|
+
const tag = node.text;
|
|
1411
|
+
return { tag, textSpan: { start: node.getStart() + 1, length: tag.length } };
|
|
1412
|
+
}
|
|
1413
|
+
function isPropType(typescript, node, types) {
|
|
1414
|
+
if (!typescript.isPropertyDeclaration(node)) return;
|
|
1415
|
+
for (const modifier of node.modifiers || []) {
|
|
1416
|
+
if (!typescript.isDecorator(modifier)) continue;
|
|
1417
|
+
const { expression } = modifier;
|
|
1418
|
+
if (typescript.isIdentifier(expression) && types.includes(expression.text)) {
|
|
1419
|
+
return true;
|
|
805
1420
|
}
|
|
806
1421
|
}
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
if (context.typescript.isPropertyAssignment(parent) || tag === "styled") {
|
|
824
|
-
const appendBefore = ".parent { ";
|
|
825
|
-
const appendAfter = " }";
|
|
826
|
-
return {
|
|
827
|
-
offset: appendBefore.length,
|
|
828
|
-
text: `${appendBefore}${context.text}${appendAfter}`,
|
|
829
|
-
pos: {
|
|
830
|
-
line: position.line,
|
|
831
|
-
character: position.line === 0 ? position.character + appendBefore.length : position.character
|
|
832
|
-
}
|
|
833
|
-
};
|
|
1422
|
+
}
|
|
1423
|
+
function decorateTypeChecker(ctx, typeChecker) {
|
|
1424
|
+
const internal = typeChecker;
|
|
1425
|
+
const checker = bindMemberFunction(internal, ["isValidPropertyAccessForCompletions"]);
|
|
1426
|
+
internal.isValidPropertyAccessForCompletions = (...args) => {
|
|
1427
|
+
const result = checker.isValidPropertyAccessForCompletions(...args);
|
|
1428
|
+
if (!result) return false;
|
|
1429
|
+
try {
|
|
1430
|
+
const { declarations } = args.at(2);
|
|
1431
|
+
if (!declarations) return true;
|
|
1432
|
+
const isNever = declarations.every(
|
|
1433
|
+
(node) => ctx.ts.isPropertySignature(node) && node.type?.getText() === "never"
|
|
1434
|
+
);
|
|
1435
|
+
return !isNever;
|
|
1436
|
+
} catch {
|
|
1437
|
+
return true;
|
|
834
1438
|
}
|
|
835
|
-
return {
|
|
836
|
-
offset: 0,
|
|
837
|
-
text: context.text,
|
|
838
|
-
pos: { ...position }
|
|
839
|
-
};
|
|
840
1439
|
};
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
if (cached) return cached;
|
|
844
|
-
const { text, pos } = this.#normalize(context, position);
|
|
845
|
-
const virtualDocument = createVirtualDocument("css", text);
|
|
846
|
-
const vCss = this.#cssLanguageService.parseStylesheet(virtualDocument);
|
|
847
|
-
let emmetResults;
|
|
848
|
-
this.#cssLanguageService.setCompletionParticipants([
|
|
849
|
-
{
|
|
850
|
-
onCssProperty: () => {
|
|
851
|
-
emmetResults = (0, import_emmet_helper2.doComplete)(virtualDocument, pos, "css", this.#ctx.config.emmet);
|
|
852
|
-
}
|
|
853
|
-
}
|
|
854
|
-
]);
|
|
855
|
-
const completions = this.#cssLanguageService.doComplete(virtualDocument, pos, vCss);
|
|
856
|
-
completions.items.push(...emmetResults?.items || []);
|
|
857
|
-
return this.#completionsCache.updateCached(context, position, completions);
|
|
858
|
-
}
|
|
859
|
-
getCompletionsAtPosition(context, position) {
|
|
860
|
-
return translateCompletionItemsToCompletionInfo(context, this.#getCompletionsAtPosition(context, position));
|
|
861
|
-
}
|
|
862
|
-
getCompletionEntryDetails(context, position, name) {
|
|
863
|
-
const completions = this.#getCompletionsAtPosition(context, position);
|
|
864
|
-
const item = completions.items.find((e) => e.label === name);
|
|
865
|
-
if (!item) return genDefaultCompletionEntryDetails(context, name);
|
|
866
|
-
return translateCompletionItemsToCompletionEntryDetails(context, item);
|
|
867
|
-
}
|
|
868
|
-
getQuickInfoAtPosition(context, position) {
|
|
869
|
-
const { text, pos } = this.#normalize(context, position);
|
|
870
|
-
const virtualDocument = createVirtualDocument("css", text);
|
|
871
|
-
const vCss = this.#cssLanguageService.parseStylesheet(virtualDocument);
|
|
872
|
-
const hover = this.#cssLanguageService.doHover(virtualDocument, pos, vCss, {
|
|
873
|
-
documentation: true,
|
|
874
|
-
references: true
|
|
875
|
-
});
|
|
876
|
-
if (!hover) return;
|
|
877
|
-
return translateHover(context, hover, position, pos.character - position.character);
|
|
878
|
-
}
|
|
879
|
-
getSyntacticDiagnostics(context) {
|
|
880
|
-
const cached = this.#diagnosticsCache.getCached(context);
|
|
881
|
-
if (cached) return cached;
|
|
882
|
-
const { text, offset } = this.#normalize(context, { line: 0, character: 0 });
|
|
883
|
-
const virtualDocument = createVirtualDocument("css", text);
|
|
884
|
-
const vCss = this.#cssLanguageService.parseStylesheet(virtualDocument);
|
|
885
|
-
const oDiagnostics = this.#cssLanguageService.doValidation(virtualDocument, vCss);
|
|
886
|
-
const file = this.#ctx.getProgram().getSourceFile(context.fileName);
|
|
887
|
-
return this.#diagnosticsCache.updateCached(
|
|
888
|
-
context,
|
|
889
|
-
oDiagnostics.map(({ message, range }) => {
|
|
890
|
-
const start = context.toOffset(range.start);
|
|
891
|
-
return {
|
|
892
|
-
category: context.typescript.DiagnosticCategory.Warning,
|
|
893
|
-
code: 0,
|
|
894
|
-
file,
|
|
895
|
-
start: range.start.line === 0 ? start - offset : start,
|
|
896
|
-
length: context.toOffset(range.end) - start,
|
|
897
|
-
messageText: message
|
|
898
|
-
};
|
|
899
|
-
})
|
|
900
|
-
);
|
|
901
|
-
}
|
|
902
|
-
};
|
|
1440
|
+
return typeChecker;
|
|
1441
|
+
}
|
|
903
1442
|
|
|
904
1443
|
// src/index.ts
|
|
905
1444
|
var LanguageServiceLogger = class {
|
|
@@ -908,7 +1447,7 @@ var LanguageServiceLogger = class {
|
|
|
908
1447
|
this.#info = info;
|
|
909
1448
|
}
|
|
910
1449
|
log(msg) {
|
|
911
|
-
this.#info.project.projectService.logger.info(`[
|
|
1450
|
+
this.#info.project.projectService.logger.info(`[${NAME}] ${msg}`);
|
|
912
1451
|
}
|
|
913
1452
|
};
|
|
914
1453
|
var HtmlPlugin = class {
|
|
@@ -920,32 +1459,16 @@ var HtmlPlugin = class {
|
|
|
920
1459
|
create(info) {
|
|
921
1460
|
return decorate(info.languageService, () => {
|
|
922
1461
|
const logger = new LanguageServiceLogger(info);
|
|
923
|
-
logger.log("Starting
|
|
1462
|
+
logger.log("Starting...");
|
|
924
1463
|
this.#config.update(info.config);
|
|
925
|
-
const context =
|
|
926
|
-
config: this.#config,
|
|
927
|
-
ts: this.#ts,
|
|
928
|
-
logger,
|
|
929
|
-
elements: new Store(),
|
|
930
|
-
getProgram: () => {
|
|
931
|
-
return info.languageService.getProgram();
|
|
932
|
-
},
|
|
933
|
-
getProject: () => {
|
|
934
|
-
return info.project;
|
|
935
|
-
}
|
|
936
|
-
};
|
|
1464
|
+
const context = new Context(this.#ts, this.#config, info, logger);
|
|
937
1465
|
const decoratedService = decorateLanguageService(context, info.languageService);
|
|
938
1466
|
const decoratedService1 = (0, import_typescript_template_language_service_decorator.decorateWithTemplateLanguageService)(
|
|
939
1467
|
this.#ts,
|
|
940
1468
|
decoratedService,
|
|
941
1469
|
info.project,
|
|
942
1470
|
new CSSLanguageService(context),
|
|
943
|
-
|
|
944
|
-
tags: ["styled", "css"],
|
|
945
|
-
enableForStringWithSubstitutions: true,
|
|
946
|
-
getSubstitution,
|
|
947
|
-
isValidTemplate: (node) => isValidCSSTemplate(this.#ts, node, "css")
|
|
948
|
-
},
|
|
1471
|
+
context.cssTemplateStringSettings,
|
|
949
1472
|
{ logger }
|
|
950
1473
|
);
|
|
951
1474
|
return (0, import_typescript_template_language_service_decorator.decorateWithTemplateLanguageService)(
|
|
@@ -953,11 +1476,7 @@ var HtmlPlugin = class {
|
|
|
953
1476
|
decoratedService1,
|
|
954
1477
|
info.project,
|
|
955
1478
|
new HTMLLanguageService(context),
|
|
956
|
-
|
|
957
|
-
tags: ["html", "raw", "h"],
|
|
958
|
-
enableForStringWithSubstitutions: true,
|
|
959
|
-
getSubstitution
|
|
960
|
-
},
|
|
1479
|
+
context.htmlTemplateStringSettings,
|
|
961
1480
|
{ logger }
|
|
962
1481
|
);
|
|
963
1482
|
});
|