ts-gem-plugin 0.0.1 → 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.
Files changed (2) hide show
  1. package/dist/index.js +103 -34
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -299,9 +299,13 @@ var HTMLDataProvider = class {
299
299
  const ts = this.#ts;
300
300
  const typeChecker = this.#getProgram().getTypeChecker();
301
301
  const node = this.#elements.get(tag);
302
- if (!node) return [];
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;
303
308
  const isDep = isDepElement(node);
304
- const result = [];
305
309
  const props = typeChecker.getTypeAtLocation(node).getApparentProperties();
306
310
  props.forEach((e) => {
307
311
  const declaration = e.getDeclarations()?.at(0);
@@ -322,7 +326,7 @@ var HTMLDataProvider = class {
322
326
  result.push({ name: `?${e.name}`, description });
323
327
  break;
324
328
  }
325
- if (getBasicUnionValues(declaration)) {
329
+ if (type && getUnionValues(type)) {
326
330
  result.push({ name: e.name, description });
327
331
  }
328
332
  if (typeText?.startsWith("Emitter")) {
@@ -345,31 +349,18 @@ var HTMLDataProvider = class {
345
349
  const node = this.#elements.get(tag);
346
350
  if (!node) return [];
347
351
  const prop = typeChecker.getTypeAtLocation(node).getProperty(getAttrName(attr).attr);
348
- const declaration = prop?.getDeclarations()?.at(0);
349
- const result = getBasicUnionValues(declaration);
352
+ const result = prop && getUnionValues(typeChecker.getTypeOfSymbol(prop));
350
353
  return result?.map((name) => ({ name })) || [];
351
354
  }
352
355
  };
353
- var STRING_REG = /("|')(?<str>.*)\1/;
354
- function getBasicUnionValues(node) {
355
- const typeText = node?.type?.getText();
356
- if (!typeText) return;
356
+ function getUnionValues(type) {
357
+ if (!type.isUnion()) return;
357
358
  const result = [];
358
- for (const text of typeText.split("|")) {
359
- const t = text.trim();
360
- if (!t) continue;
361
- const number = Number(t);
362
- if (!Number.isNaN(number)) {
363
- result.push(t);
364
- continue;
365
- }
366
- const match = t.match(STRING_REG);
367
- if (!match) {
368
- return;
369
- }
370
- result.push(match.groups.str);
371
- }
372
- if (result.length) return result;
359
+ type.types.forEach((e) => {
360
+ if (!e.isLiteral()) return;
361
+ result.push(String(e.value));
362
+ });
363
+ return result;
373
364
  }
374
365
  var COMMENT_LINE_CONTENT = /^(\/?[ *\t]*)?(?<str>.*?)(\**\/)?$/gm;
375
366
  function getDocComment(typescript, declaration) {
@@ -438,7 +429,7 @@ var Cache = class {
438
429
  // src/cache.ts
439
430
  var LRUCache = class {
440
431
  #bucket;
441
- constructor(...args) {
432
+ constructor(args) {
442
433
  this.#bucket = new Cache({ max: 25, renewal: true, ...args });
443
434
  }
444
435
  #genKey(context, position) {
@@ -508,6 +499,11 @@ var Context = class {
508
499
  return this.#virtualHtmlCache.get({ text, fileName: "" }, void 0, () => {
509
500
  const vDoc = createVirtualDocument("html", text);
510
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
+ });
511
507
  return { vDoc, vHtml };
512
508
  });
513
509
  }
@@ -549,12 +545,34 @@ var Context = class {
549
545
  const name = symbol.escapedName.toString();
550
546
  const match = name.match(/^(SVG|HTML)(\w*)Element$/);
551
547
  const declaration = symbol.declarations?.find((e) => this.ts.isInterfaceDeclaration(e));
552
- if (match && declaration) {
553
- this.builtInElements.set(camelToKebabCase(match[2]), declaration);
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);
554
553
  }
555
554
  });
556
555
  }
557
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
+ };
558
576
  function createVirtualDocument(languageId, content) {
559
577
  return import_vscode_html_languageservice2.TextDocument.create(`embedded://document.${languageId}`, languageId, 1, content);
560
578
  }
@@ -755,6 +773,21 @@ function genAttrDefinitionInfo(context, { start, length }, propDeclaration) {
755
773
  ]
756
774
  };
757
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
+ }
758
791
 
759
792
  // src/decorate-css.ts
760
793
  var CSSLanguageService = class {
@@ -992,7 +1025,9 @@ var HTMLLanguageService = class {
992
1025
  if (!tagDeclaration) return;
993
1026
  for (const [attributeName, { value, start, end }] of node.attributesMap) {
994
1027
  if (attributeName.startsWith("_")) continue;
1028
+ const hasValueSpan = value?.startsWith("_");
995
1029
  const attrInfo = getAttrName(attributeName);
1030
+ const propType = getPropType(typeChecker, tagDeclaration, attrInfo);
996
1031
  const diagnostic = {
997
1032
  category: context.typescript.DiagnosticCategory.Warning,
998
1033
  file,
@@ -1000,9 +1035,28 @@ var HTMLLanguageService = class {
1000
1035
  length: end - start,
1001
1036
  source: NAME,
1002
1037
  code: 103 /* PropTypeError */,
1003
- messageText: `'${attributeName}' type error`
1038
+ messageText: !propType ? `'${attributeName}' type error` : `'${attributeName}' not satisfied '${typeChecker.typeToString(propType)}'`
1004
1039
  };
1005
- const propType = getPropType(typeChecker, tagDeclaration, attrInfo);
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
+ }
1006
1060
  if (!propType) {
1007
1061
  if (attrInfo.decorate !== "@") {
1008
1062
  diagnostics.push({
@@ -1025,10 +1079,8 @@ var HTMLLanguageService = class {
1025
1079
  }
1026
1080
  continue;
1027
1081
  }
1028
- if (value.startsWith("_")) {
1029
- const valueOffset = end + offset + 3;
1030
- const spanExp = getSpanExpression(this.#ctx.ts, file, valueOffset);
1031
- const spanType = typeChecker.getTypeAtLocation(spanExp);
1082
+ if (hasValueSpan) {
1083
+ const spanType = getSpanType(this.#ctx.ts, typeChecker, file, offset, end);
1032
1084
  switch (attrInfo.decorate) {
1033
1085
  case "?":
1034
1086
  const boolType = getUnionType(typeChecker, [
@@ -1085,7 +1137,7 @@ var HTMLLanguageService = class {
1085
1137
  const node = vHtml.findNodeAt(currentOffset);
1086
1138
  const { text, start, length, before } = getHTMLTextAtPosition(context.text, currentOffset);
1087
1139
  const empty = { textSpan: { start, length } };
1088
- if (node.tag === "style") {
1140
+ if (node.tag === "style" && currentOffset > node.startTagEnd) {
1089
1141
  const { style, vDoc, position: pos } = this.#getEmbeddedCss(context, position, vHtml);
1090
1142
  const cssNode = style.findChildAtOffset(vDoc.offsetAt(pos), true);
1091
1143
  if (!cssNode) return empty;
@@ -1105,6 +1157,15 @@ var HTMLLanguageService = class {
1105
1157
  }
1106
1158
  const { attr, offset } = getAttrName(text);
1107
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
+ );
1168
+ }
1108
1169
  const typeChecker = this.#ctx.getProgram().getTypeChecker();
1109
1170
  const propSymbol = typeChecker.getTypeAtLocation(definitionNode).getProperty(kebabToCamelCase(attr));
1110
1171
  const propDeclaration = propSymbol?.getDeclarations()?.at(0);
@@ -1120,8 +1181,16 @@ function getSpanExpression(typescript, file, pos) {
1120
1181
  }
1121
1182
  return node.expression;
1122
1183
  }
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
+ }
1123
1189
  function getPropType(typeChecker, tagClassDeclaration, attrInfo) {
1124
1190
  const classType = typeChecker.getTypeAtLocation(tagClassDeclaration);
1191
+ if (attrInfo.attr.startsWith("data-")) {
1192
+ return typeChecker.getStringType();
1193
+ }
1125
1194
  const propName = kebabToCamelCase(attrInfo.attr);
1126
1195
  switch (propName) {
1127
1196
  case "class":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-gem-plugin",
3
- "version": "0.0.1",
3
+ "version": "0.0.6",
4
4
  "description": "Typescript language service plugin for Gem",
5
5
  "keywords": [
6
6
  "gem",
@@ -18,7 +18,7 @@
18
18
  "@mantou/typescript-template-language-service-decorator": "^2.3.4",
19
19
  "@mantou/vscode-css-languageservice": "^6.3.3",
20
20
  "@mantou/vscode-emmet-helper": "^2.9.3",
21
- "@mantou/vscode-html-languageservice": "^5.3.3",
21
+ "@mantou/vscode-html-languageservice": "^5.3.4",
22
22
  "duoyun-ui": "^2.2.0",
23
23
  "vscode-languageserver-textdocument": "^1.0.12",
24
24
  "vscode-languageserver-types": "^3.17.5"