@fluixi/ts-plugin 0.1.0-alpha.1 → 0.1.0-alpha.2
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 +332 -96
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -423,26 +423,54 @@ function templatePieces(ts, tpl, sf) {
|
|
|
423
423
|
});
|
|
424
424
|
return pieces;
|
|
425
425
|
}
|
|
426
|
-
|
|
427
|
-
|
|
426
|
+
|
|
427
|
+
// src/cache.ts
|
|
428
|
+
var parsed = /* @__PURE__ */ new WeakMap();
|
|
429
|
+
function parseCached(ts, sf, tpl) {
|
|
430
|
+
let byTemplate = parsed.get(sf);
|
|
431
|
+
if (!byTemplate) {
|
|
432
|
+
byTemplate = /* @__PURE__ */ new Map();
|
|
433
|
+
parsed.set(sf, byTemplate);
|
|
434
|
+
}
|
|
435
|
+
const key = tpl.getStart(sf);
|
|
436
|
+
const hit = byTemplate.get(key);
|
|
437
|
+
if (hit) return hit;
|
|
438
|
+
const result = k(templatePieces(ts, tpl, sf));
|
|
439
|
+
byTemplate.set(key, result);
|
|
440
|
+
return result;
|
|
441
|
+
}
|
|
442
|
+
var templates = /* @__PURE__ */ new WeakMap();
|
|
443
|
+
function templatesIn(ts, sf) {
|
|
444
|
+
const hit = templates.get(sf);
|
|
445
|
+
if (hit) return hit;
|
|
446
|
+
const found = [];
|
|
428
447
|
const visit = (node) => {
|
|
429
|
-
if (
|
|
430
|
-
|
|
431
|
-
found = node;
|
|
432
|
-
return;
|
|
448
|
+
if (ts.isTaggedTemplateExpression(node) && ts.isIdentifier(node.tag) && (node.tag.text === "html" || node.tag.text === "svg")) {
|
|
449
|
+
found.push(node);
|
|
433
450
|
}
|
|
434
451
|
ts.forEachChild(node, visit);
|
|
435
452
|
};
|
|
436
453
|
visit(sf);
|
|
454
|
+
found.sort((a, b2) => a.getStart(sf) - b2.getStart(sf));
|
|
455
|
+
templates.set(sf, found);
|
|
437
456
|
return found;
|
|
438
457
|
}
|
|
458
|
+
function templateAtCached(ts, sf, pos) {
|
|
459
|
+
let match;
|
|
460
|
+
for (const tpl of templatesIn(ts, sf)) {
|
|
461
|
+
const start = tpl.template.getStart(sf);
|
|
462
|
+
if (start > pos) break;
|
|
463
|
+
if (pos <= tpl.template.getEnd()) match = tpl;
|
|
464
|
+
}
|
|
465
|
+
return match;
|
|
466
|
+
}
|
|
439
467
|
|
|
440
468
|
// src/hover.ts
|
|
441
469
|
function componentQuickInfo(ts, ls, fileName, position) {
|
|
442
470
|
const program = ls.getProgram();
|
|
443
471
|
const sf = program?.getSourceFile(fileName);
|
|
444
472
|
if (!program || !sf) return void 0;
|
|
445
|
-
const template =
|
|
473
|
+
const template = templateAtCached(ts, sf, position);
|
|
446
474
|
if (!template) return void 0;
|
|
447
475
|
const hit = componentTagAt(template, sf, position, ts);
|
|
448
476
|
if (!hit) return void 0;
|
|
@@ -457,7 +485,7 @@ function componentQuickInfo(ts, ls, fileName, position) {
|
|
|
457
485
|
return { ...info, textSpan: { start: hit.start, length: hit.name.length } };
|
|
458
486
|
}
|
|
459
487
|
function componentTagAt(template, sf, position, ts) {
|
|
460
|
-
const { root } =
|
|
488
|
+
const { root } = parseCached(ts, sf, template.template);
|
|
461
489
|
let hit;
|
|
462
490
|
const walk = (nodes) => {
|
|
463
491
|
for (const node of nodes) {
|
|
@@ -621,14 +649,14 @@ function resolveElementType(ts, checker, location, tag) {
|
|
|
621
649
|
return t ?? resolveGlobalType(ts, checker, location, "HTMLElement");
|
|
622
650
|
}
|
|
623
651
|
function inferParamAt(ts, program, sf, position) {
|
|
624
|
-
const template =
|
|
652
|
+
const template = templateAtCached(ts, sf, position);
|
|
625
653
|
if (!template || ts.isNoSubstitutionTemplateLiteral(template.template)) return void 0;
|
|
626
654
|
const spans = template.template.templateSpans;
|
|
627
655
|
const holeExprs = spans.map((s) => s.expression);
|
|
628
656
|
const holeIndex = holeExprs.findIndex((e) => position >= e.getStart(sf) && position <= e.getEnd());
|
|
629
657
|
if (holeIndex === -1) return void 0;
|
|
630
658
|
const roles = /* @__PURE__ */ new Map();
|
|
631
|
-
holeRoles(
|
|
659
|
+
holeRoles(parseCached(ts, sf, template.template).root.children, roles);
|
|
632
660
|
const role = roles.get(holeIndex);
|
|
633
661
|
if (!role) return void 0;
|
|
634
662
|
const fn = holeExprs[holeIndex];
|
|
@@ -706,7 +734,7 @@ function componentPropCompletions(ts, ls, fileName, position) {
|
|
|
706
734
|
const program = ls.getProgram();
|
|
707
735
|
const sf = program?.getSourceFile(fileName);
|
|
708
736
|
if (!program || !sf) return void 0;
|
|
709
|
-
const template =
|
|
737
|
+
const template = templateAtCached(ts, sf, position);
|
|
710
738
|
if (!template) return void 0;
|
|
711
739
|
const comp = componentTagAtAttrArea(ts, sf, template.template, position);
|
|
712
740
|
if (!comp || !comp.tag) return void 0;
|
|
@@ -735,7 +763,7 @@ function componentPropHover(ts, ls, fileName, position) {
|
|
|
735
763
|
const program = ls.getProgram();
|
|
736
764
|
const sf = program?.getSourceFile(fileName);
|
|
737
765
|
if (!program || !sf) return void 0;
|
|
738
|
-
const template =
|
|
766
|
+
const template = templateAtCached(ts, sf, position);
|
|
739
767
|
if (!template) return void 0;
|
|
740
768
|
const hit = propNameAt(ts, sf, template.template, position);
|
|
741
769
|
if (!hit) return void 0;
|
|
@@ -771,7 +799,7 @@ function componentPropHover(ts, ls, fileName, position) {
|
|
|
771
799
|
};
|
|
772
800
|
}
|
|
773
801
|
function propNameAt(ts, sf, tpl, position) {
|
|
774
|
-
const { root } =
|
|
802
|
+
const { root } = parseCached(ts, sf, tpl);
|
|
775
803
|
let hit;
|
|
776
804
|
const walk = (nodes) => {
|
|
777
805
|
for (const node of nodes) {
|
|
@@ -846,7 +874,7 @@ function firstParamType(ts, checker, type) {
|
|
|
846
874
|
return void 0;
|
|
847
875
|
}
|
|
848
876
|
function componentTagAtAttrArea(ts, sf, tpl, position) {
|
|
849
|
-
const { root } =
|
|
877
|
+
const { root } = parseCached(ts, sf, tpl);
|
|
850
878
|
let hit;
|
|
851
879
|
const walk = (nodes) => {
|
|
852
880
|
for (const node of nodes) {
|
|
@@ -921,6 +949,175 @@ function propertyAccessAt(ts, sf, position) {
|
|
|
921
949
|
return found;
|
|
922
950
|
}
|
|
923
951
|
|
|
952
|
+
// src/elements.ts
|
|
953
|
+
var ATTRIBUTES_MODULE = "@fluixi/dom/attributes";
|
|
954
|
+
var perProgram = /* @__PURE__ */ new WeakMap();
|
|
955
|
+
function intrinsics(ts, program, host, sf) {
|
|
956
|
+
const hit = perProgram.get(program);
|
|
957
|
+
if (hit) return hit;
|
|
958
|
+
let type;
|
|
959
|
+
try {
|
|
960
|
+
const checker = program.getTypeChecker();
|
|
961
|
+
const declared = (file) => {
|
|
962
|
+
if (!file) return void 0;
|
|
963
|
+
const moduleSymbol = checker.getSymbolAtLocation(file);
|
|
964
|
+
const symbol = moduleSymbol?.exports?.get(ts.escapeLeadingUnderscores("IntrinsicElements"));
|
|
965
|
+
return symbol ? checker.getDeclaredTypeOfSymbol(symbol) : void 0;
|
|
966
|
+
};
|
|
967
|
+
const resolved = ts.resolveModuleName(
|
|
968
|
+
ATTRIBUTES_MODULE,
|
|
969
|
+
sf.fileName,
|
|
970
|
+
program.getCompilerOptions(),
|
|
971
|
+
host
|
|
972
|
+
);
|
|
973
|
+
type = declared(
|
|
974
|
+
resolved.resolvedModule && program.getSourceFile(resolved.resolvedModule.resolvedFileName)
|
|
975
|
+
);
|
|
976
|
+
if (!type) {
|
|
977
|
+
for (const file of program.getSourceFiles()) {
|
|
978
|
+
if (!file.isDeclarationFile && file.fileName.indexOf("@fluixi/dom") === -1) continue;
|
|
979
|
+
if (file.fileName.indexOf("@fluixi/dom") === -1) continue;
|
|
980
|
+
type = declared(file);
|
|
981
|
+
if (type) break;
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
} catch {
|
|
985
|
+
type = void 0;
|
|
986
|
+
}
|
|
987
|
+
const entry = { type, byTag: /* @__PURE__ */ new Map() };
|
|
988
|
+
perProgram.set(program, entry);
|
|
989
|
+
return entry;
|
|
990
|
+
}
|
|
991
|
+
function attributesFor(ts, program, host, sf, tag) {
|
|
992
|
+
const state = intrinsics(ts, program, host, sf);
|
|
993
|
+
if (!state.type) return void 0;
|
|
994
|
+
if (state.byTag.has(tag)) return state.byTag.get(tag);
|
|
995
|
+
let props;
|
|
996
|
+
const checker = program.getTypeChecker();
|
|
997
|
+
const tagSymbol = state.type.getProperty(tag);
|
|
998
|
+
if (tagSymbol) {
|
|
999
|
+
const decl = tagSymbol.valueDeclaration ?? tagSymbol.declarations?.[0];
|
|
1000
|
+
if (decl) props = checker.getTypeOfSymbolAtLocation(tagSymbol, decl).getProperties();
|
|
1001
|
+
}
|
|
1002
|
+
state.byTag.set(tag, props);
|
|
1003
|
+
return props;
|
|
1004
|
+
}
|
|
1005
|
+
function elementAtAttributeArea(ts, sf, tpl, position) {
|
|
1006
|
+
const { root } = parseCached(ts, sf, tpl);
|
|
1007
|
+
let hit;
|
|
1008
|
+
const walk = (nodes) => {
|
|
1009
|
+
for (const node of nodes) {
|
|
1010
|
+
if (node.kind === "Element") {
|
|
1011
|
+
const el = node;
|
|
1012
|
+
const openStart = el.loc.start + 1 + el.tag.length;
|
|
1013
|
+
const openEnd = firstAttributeEnd(el);
|
|
1014
|
+
if (position > openStart && position <= openEnd) {
|
|
1015
|
+
const present = /* @__PURE__ */ new Set();
|
|
1016
|
+
for (const a of el.attributes) {
|
|
1017
|
+
if ("loc" in a && a.loc) present.add(writtenName(sf.text, a.loc.start));
|
|
1018
|
+
}
|
|
1019
|
+
hit = { tag: el.tag, present };
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
if (!hit && "children" in node && node.children) walk(node.children);
|
|
1023
|
+
if (hit) return;
|
|
1024
|
+
}
|
|
1025
|
+
};
|
|
1026
|
+
walk(root.children);
|
|
1027
|
+
return hit;
|
|
1028
|
+
}
|
|
1029
|
+
function writtenName(text, start) {
|
|
1030
|
+
let end = start;
|
|
1031
|
+
while (end < text.length && !/[\s=/>]/.test(text[end])) end++;
|
|
1032
|
+
return text.slice(start, end);
|
|
1033
|
+
}
|
|
1034
|
+
function firstAttributeEnd(el) {
|
|
1035
|
+
let end = el.loc.start + 1 + el.tag.length;
|
|
1036
|
+
for (const a of el.attributes) {
|
|
1037
|
+
if ("loc" in a && a.loc) end = Math.max(end, a.loc.end);
|
|
1038
|
+
}
|
|
1039
|
+
return end + 1;
|
|
1040
|
+
}
|
|
1041
|
+
function elementAttributeCompletions(ts, ls, host, fileName, position) {
|
|
1042
|
+
const program = ls.getProgram();
|
|
1043
|
+
const sf = program?.getSourceFile(fileName);
|
|
1044
|
+
if (!program || !sf) return void 0;
|
|
1045
|
+
const tpl = templateAtCached(ts, sf, position);
|
|
1046
|
+
if (!tpl) return void 0;
|
|
1047
|
+
const hit = elementAtAttributeArea(ts, sf, tpl.template, position);
|
|
1048
|
+
if (!hit) return void 0;
|
|
1049
|
+
const props = attributesFor(ts, program, host, sf, hit.tag);
|
|
1050
|
+
if (!props) return void 0;
|
|
1051
|
+
const entries = [];
|
|
1052
|
+
for (const p2 of props) {
|
|
1053
|
+
const name = p2.getName();
|
|
1054
|
+
if (name === "children" || hit.present.has(name)) continue;
|
|
1055
|
+
entries.push({
|
|
1056
|
+
name,
|
|
1057
|
+
kind: ts.ScriptElementKind.memberVariableElement,
|
|
1058
|
+
kindModifiers: "",
|
|
1059
|
+
sortText: name.startsWith("on") ? "1" : "0"
|
|
1060
|
+
});
|
|
1061
|
+
}
|
|
1062
|
+
return entries.length ? entries : void 0;
|
|
1063
|
+
}
|
|
1064
|
+
function attributeNameAt(ts, sf, tpl, position) {
|
|
1065
|
+
const { root } = parseCached(ts, sf, tpl);
|
|
1066
|
+
let hit;
|
|
1067
|
+
const walk = (nodes) => {
|
|
1068
|
+
for (const node of nodes) {
|
|
1069
|
+
if (node.kind === "Element") {
|
|
1070
|
+
const el = node;
|
|
1071
|
+
for (const a of el.attributes) {
|
|
1072
|
+
if (!("loc" in a) || !a.loc) continue;
|
|
1073
|
+
const start = a.loc.start;
|
|
1074
|
+
const name = writtenName(sf.text, start);
|
|
1075
|
+
if (name && position >= start && position <= start + name.length) {
|
|
1076
|
+
hit = { tag: el.tag, name, start };
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
if (!hit && "children" in node && node.children) walk(node.children);
|
|
1081
|
+
if (hit) return;
|
|
1082
|
+
}
|
|
1083
|
+
};
|
|
1084
|
+
walk(root.children);
|
|
1085
|
+
return hit;
|
|
1086
|
+
}
|
|
1087
|
+
function elementAttributeHover(ts, ls, host, fileName, position) {
|
|
1088
|
+
const program = ls.getProgram();
|
|
1089
|
+
const sf = program?.getSourceFile(fileName);
|
|
1090
|
+
if (!program || !sf) return void 0;
|
|
1091
|
+
const tpl = templateAtCached(ts, sf, position);
|
|
1092
|
+
if (!tpl) return void 0;
|
|
1093
|
+
const hit = attributeNameAt(ts, sf, tpl.template, position);
|
|
1094
|
+
if (!hit) return void 0;
|
|
1095
|
+
const props = attributesFor(ts, program, host, sf, hit.tag);
|
|
1096
|
+
const symbol = props?.find((p2) => p2.getName() === hit.name);
|
|
1097
|
+
if (!symbol) return void 0;
|
|
1098
|
+
const decl = symbol.valueDeclaration ?? symbol.declarations?.[0];
|
|
1099
|
+
if (!decl) return void 0;
|
|
1100
|
+
const checker = program.getTypeChecker();
|
|
1101
|
+
const typeText = checker.typeToString(checker.getTypeOfSymbolAtLocation(symbol, decl));
|
|
1102
|
+
return {
|
|
1103
|
+
kind: ts.ScriptElementKind.memberVariableElement,
|
|
1104
|
+
kindModifiers: "",
|
|
1105
|
+
textSpan: { start: hit.start, length: hit.name.length },
|
|
1106
|
+
displayParts: [
|
|
1107
|
+
{ text: "(", kind: "punctuation" },
|
|
1108
|
+
{ text: "attribute", kind: "text" },
|
|
1109
|
+
{ text: ")", kind: "punctuation" },
|
|
1110
|
+
{ text: " ", kind: "space" },
|
|
1111
|
+
{ text: `${hit.tag}.`, kind: "text" },
|
|
1112
|
+
{ text: hit.name, kind: "propertyName" },
|
|
1113
|
+
{ text: ":", kind: "punctuation" },
|
|
1114
|
+
{ text: " ", kind: "space" },
|
|
1115
|
+
{ text: typeText, kind: "text" }
|
|
1116
|
+
],
|
|
1117
|
+
documentation: symbol.getDocumentationComment(checker)
|
|
1118
|
+
};
|
|
1119
|
+
}
|
|
1120
|
+
|
|
924
1121
|
// src/virtual.ts
|
|
925
1122
|
var EVENT_MAP_KEYS = new Set(Object.keys({
|
|
926
1123
|
click: 0,
|
|
@@ -985,12 +1182,6 @@ function inHole(segments, pos) {
|
|
|
985
1182
|
function inKey(segments, pos) {
|
|
986
1183
|
return segments.some((s) => s.key && pos >= s.srcStart && pos <= s.srcEnd);
|
|
987
1184
|
}
|
|
988
|
-
function toGenerated(segments, pos) {
|
|
989
|
-
for (const s of segments) {
|
|
990
|
-
if (pos >= s.srcStart && pos <= s.srcEnd) return s.genStart + (pos - s.srcStart);
|
|
991
|
-
}
|
|
992
|
-
return -1;
|
|
993
|
-
}
|
|
994
1185
|
function toSource(segments, pos) {
|
|
995
1186
|
for (const s of segments) {
|
|
996
1187
|
if (pos >= s.genStart && pos <= s.genEnd) return s.srcStart + (pos - s.genStart);
|
|
@@ -998,8 +1189,8 @@ function toSource(segments, pos) {
|
|
|
998
1189
|
return -1;
|
|
999
1190
|
}
|
|
1000
1191
|
function buildVirtual(ts, sf) {
|
|
1001
|
-
const
|
|
1002
|
-
if (
|
|
1192
|
+
const templates2 = collectTemplates(ts, sf);
|
|
1193
|
+
if (templates2.length === 0) return void 0;
|
|
1003
1194
|
const srcText = sf.text;
|
|
1004
1195
|
const segments = [];
|
|
1005
1196
|
let gen = "";
|
|
@@ -1012,7 +1203,7 @@ function buildVirtual(ts, sf) {
|
|
|
1012
1203
|
src = to;
|
|
1013
1204
|
};
|
|
1014
1205
|
gen += PRELUDE;
|
|
1015
|
-
for (const tpl of
|
|
1206
|
+
for (const tpl of templates2) {
|
|
1016
1207
|
copy(tpl.node.getStart(sf));
|
|
1017
1208
|
gen = emitTemplate(ts, sf, tpl, gen, segments, srcText);
|
|
1018
1209
|
src = tpl.node.getEnd();
|
|
@@ -1207,81 +1398,105 @@ function emitTemplate(ts, sf, tpl, gen, segments, srcText) {
|
|
|
1207
1398
|
return gen;
|
|
1208
1399
|
}
|
|
1209
1400
|
|
|
1210
|
-
// src/
|
|
1211
|
-
var
|
|
1401
|
+
// src/shadow.ts
|
|
1402
|
+
var SUFFIX = ".__fx.ts";
|
|
1403
|
+
function isShadowPath(fileName) {
|
|
1404
|
+
return fileName.endsWith(SUFFIX);
|
|
1405
|
+
}
|
|
1406
|
+
function shadowPathFor(fileName) {
|
|
1407
|
+
return fileName + SUFFIX;
|
|
1408
|
+
}
|
|
1409
|
+
function sourcePathFor(shadow) {
|
|
1410
|
+
return shadow.slice(0, -SUFFIX.length);
|
|
1411
|
+
}
|
|
1412
|
+
var ShadowFiles = class {
|
|
1212
1413
|
constructor(ts, host) {
|
|
1213
1414
|
this.ts = ts;
|
|
1214
1415
|
this.host = host;
|
|
1416
|
+
this.patchHost();
|
|
1417
|
+
}
|
|
1418
|
+
built = /* @__PURE__ */ new Map();
|
|
1419
|
+
/** Source files known to contain no templates, so we skip building a shadow. */
|
|
1420
|
+
barren = /* @__PURE__ */ new Map();
|
|
1421
|
+
/**
|
|
1422
|
+
* Teach the existing host about shadow paths. The language service holds this
|
|
1423
|
+
* host object, so overriding its methods in place is enough — no second service.
|
|
1424
|
+
*/
|
|
1425
|
+
patchHost() {
|
|
1426
|
+
const ts = this.ts;
|
|
1427
|
+
const host = this.host;
|
|
1215
1428
|
const self = this;
|
|
1216
|
-
const
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
};
|
|
1223
|
-
}
|
|
1224
|
-
const value = Reflect.get(target, prop, receiver);
|
|
1225
|
-
return typeof value === "function" ? value.bind(target) : value;
|
|
1429
|
+
const originalNames = host.getScriptFileNames.bind(host);
|
|
1430
|
+
host.getScriptFileNames = () => {
|
|
1431
|
+
const names = originalNames();
|
|
1432
|
+
const out = names.slice();
|
|
1433
|
+
for (const name of names) {
|
|
1434
|
+
if (!isShadowPath(name) && self.build(name)) out.push(shadowPathFor(name));
|
|
1226
1435
|
}
|
|
1227
|
-
|
|
1228
|
-
|
|
1436
|
+
return out;
|
|
1437
|
+
};
|
|
1438
|
+
const originalSnapshot = host.getScriptSnapshot.bind(host);
|
|
1439
|
+
host.getScriptSnapshot = (fileName) => {
|
|
1440
|
+
if (!isShadowPath(fileName)) return originalSnapshot(fileName);
|
|
1441
|
+
const built = self.build(sourcePathFor(fileName));
|
|
1442
|
+
return built ? ts.ScriptSnapshot.fromString(built.text) : void 0;
|
|
1443
|
+
};
|
|
1444
|
+
const originalVersion = host.getScriptVersion.bind(host);
|
|
1445
|
+
host.getScriptVersion = (fileName) => isShadowPath(fileName) ? originalVersion(sourcePathFor(fileName)) : originalVersion(fileName);
|
|
1446
|
+
if (typeof host.fileExists === "function") {
|
|
1447
|
+
const originalExists = host.fileExists.bind(host);
|
|
1448
|
+
host.fileExists = (fileName) => isShadowPath(fileName) ? !!self.build(sourcePathFor(fileName)) : originalExists(fileName);
|
|
1449
|
+
}
|
|
1229
1450
|
}
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
cache = /* @__PURE__ */ new Map();
|
|
1233
|
-
/** The virtual doc for a file (built + cached per source version), if it has templates. */
|
|
1234
|
-
virtual(fileName) {
|
|
1451
|
+
/** Build (and cache per source version) the shadow text for a file. */
|
|
1452
|
+
build(fileName) {
|
|
1235
1453
|
const version = this.host.getScriptVersion(fileName);
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
const
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1454
|
+
if (this.barren.get(fileName) === version) return void 0;
|
|
1455
|
+
const hit = this.built.get(fileName);
|
|
1456
|
+
if (hit && hit.version === version) return hit;
|
|
1457
|
+
const snapshot = this.host.getScriptSnapshot(fileName);
|
|
1458
|
+
if (!snapshot) return void 0;
|
|
1459
|
+
const text = snapshot.getText(0, snapshot.getLength());
|
|
1460
|
+
if (text.indexOf("html`") === -1 && text.indexOf("svg`") === -1) {
|
|
1461
|
+
this.barren.set(fileName, version);
|
|
1462
|
+
this.built.delete(fileName);
|
|
1463
|
+
return void 0;
|
|
1464
|
+
}
|
|
1465
|
+
const sf = this.ts.createSourceFile(fileName, text, this.ts.ScriptTarget.Latest, true);
|
|
1466
|
+
const virtual = buildVirtual(this.ts, sf);
|
|
1467
|
+
if (!virtual) {
|
|
1468
|
+
this.barren.set(fileName, version);
|
|
1469
|
+
this.built.delete(fileName);
|
|
1470
|
+
return void 0;
|
|
1471
|
+
}
|
|
1472
|
+
const entry = {
|
|
1473
|
+
version,
|
|
1474
|
+
text: virtual.text + "\nexport {};\n",
|
|
1475
|
+
segments: virtual.segments
|
|
1476
|
+
};
|
|
1477
|
+
this.built.set(fileName, entry);
|
|
1478
|
+
return entry;
|
|
1248
1479
|
}
|
|
1249
|
-
|
|
1250
|
-
|
|
1480
|
+
/** The shadow path for a source file, when it has one. */
|
|
1481
|
+
shadowFor(fileName) {
|
|
1482
|
+
return this.build(fileName) ? shadowPathFor(fileName) : void 0;
|
|
1251
1483
|
}
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
return segs ? toGenerated(segs, pos) : -1;
|
|
1484
|
+
segmentsFor(fileName) {
|
|
1485
|
+
return this.build(fileName)?.segments;
|
|
1255
1486
|
}
|
|
1256
|
-
toSrc(fileName,
|
|
1257
|
-
const
|
|
1258
|
-
return
|
|
1487
|
+
toSrc(fileName, position) {
|
|
1488
|
+
const segments = this.segmentsFor(fileName);
|
|
1489
|
+
return segments ? toSource(segments, position) : -1;
|
|
1259
1490
|
}
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
return segs ? inHole(segs, pos) : false;
|
|
1491
|
+
inHole(fileName, position) {
|
|
1492
|
+
const segments = this.segmentsFor(fileName);
|
|
1493
|
+
return !!segments && inHole(segments, position);
|
|
1264
1494
|
}
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
return segs ? inKey(segs, pos) : false;
|
|
1495
|
+
inKey(fileName, position) {
|
|
1496
|
+
const segments = this.segmentsFor(fileName);
|
|
1497
|
+
return !!segments && inKey(segments, position);
|
|
1269
1498
|
}
|
|
1270
1499
|
};
|
|
1271
|
-
function scriptKind(ts, fileName) {
|
|
1272
|
-
if (fileName.endsWith(".tsx")) return ts.ScriptKind.TSX;
|
|
1273
|
-
if (fileName.endsWith(".jsx")) return ts.ScriptKind.JSX;
|
|
1274
|
-
if (fileName.endsWith(".js") || fileName.endsWith(".mjs") || fileName.endsWith(".cjs")) return ts.ScriptKind.JS;
|
|
1275
|
-
return ts.ScriptKind.TS;
|
|
1276
|
-
}
|
|
1277
|
-
|
|
1278
|
-
// src/bridge.ts
|
|
1279
|
-
function mapDiagnostic(vs, fileName, d) {
|
|
1280
|
-
if (d.start === void 0) return d;
|
|
1281
|
-
const start = vs.toSrc(fileName, d.start);
|
|
1282
|
-
if (start === -1) return void 0;
|
|
1283
|
-
return { ...d, start };
|
|
1284
|
-
}
|
|
1285
1500
|
|
|
1286
1501
|
// src/index.ts
|
|
1287
1502
|
function init(modules) {
|
|
@@ -1289,11 +1504,12 @@ function init(modules) {
|
|
|
1289
1504
|
return {
|
|
1290
1505
|
create(info) {
|
|
1291
1506
|
const ls = info.languageService;
|
|
1292
|
-
|
|
1507
|
+
const host = info.languageServiceHost;
|
|
1508
|
+
let shadows;
|
|
1293
1509
|
try {
|
|
1294
|
-
|
|
1510
|
+
shadows = new ShadowFiles(ts, info.languageServiceHost);
|
|
1295
1511
|
} catch {
|
|
1296
|
-
|
|
1512
|
+
shadows = void 0;
|
|
1297
1513
|
}
|
|
1298
1514
|
const proxy = /* @__PURE__ */ Object.create(null);
|
|
1299
1515
|
for (const key of Object.keys(ls)) {
|
|
@@ -1318,17 +1534,18 @@ function init(modules) {
|
|
|
1318
1534
|
return true;
|
|
1319
1535
|
});
|
|
1320
1536
|
};
|
|
1321
|
-
const holeDiagnostics = (fileName
|
|
1322
|
-
|
|
1537
|
+
const holeDiagnostics = (fileName) => {
|
|
1538
|
+
const shadow = shadows?.shadowFor(fileName);
|
|
1539
|
+
if (!shadow) return [];
|
|
1323
1540
|
try {
|
|
1324
1541
|
const sourceFile = ls.getProgram()?.getSourceFile(fileName);
|
|
1325
1542
|
const out = [];
|
|
1326
|
-
for (const d of
|
|
1543
|
+
for (const d of ls.getSemanticDiagnostics(shadow)) {
|
|
1327
1544
|
if (d.start === void 0) continue;
|
|
1328
|
-
const
|
|
1329
|
-
if (
|
|
1330
|
-
|
|
1331
|
-
}
|
|
1545
|
+
const start = shadows.toSrc(fileName, d.start);
|
|
1546
|
+
if (start === -1) continue;
|
|
1547
|
+
if (!shadows.inHole(fileName, start) && !shadows.inKey(fileName, start)) continue;
|
|
1548
|
+
out.push({ ...d, start, file: sourceFile });
|
|
1332
1549
|
}
|
|
1333
1550
|
return out;
|
|
1334
1551
|
} catch {
|
|
@@ -1336,16 +1553,30 @@ function init(modules) {
|
|
|
1336
1553
|
}
|
|
1337
1554
|
};
|
|
1338
1555
|
proxy.getSemanticDiagnostics = (fileName) => {
|
|
1556
|
+
if (isShadowPath(fileName)) return [];
|
|
1339
1557
|
const base = reliableFilter(fileName, ls.getSemanticDiagnostics(fileName));
|
|
1340
|
-
return base.concat(holeDiagnostics(fileName
|
|
1558
|
+
return base.concat(holeDiagnostics(fileName));
|
|
1341
1559
|
};
|
|
1342
|
-
proxy.getSuggestionDiagnostics = (fileName) => reliableFilter(fileName, ls.getSuggestionDiagnostics(fileName));
|
|
1560
|
+
proxy.getSuggestionDiagnostics = (fileName) => isShadowPath(fileName) ? [] : reliableFilter(fileName, ls.getSuggestionDiagnostics(fileName));
|
|
1561
|
+
proxy.getSyntacticDiagnostics = (fileName) => isShadowPath(fileName) ? [] : ls.getSyntacticDiagnostics(fileName);
|
|
1562
|
+
const notShadow = (items) => items?.filter((i) => !isShadowPath(i.fileName));
|
|
1563
|
+
proxy.findRenameLocations = (fileName, position, findInStrings, findInComments, prefs) => notShadow(ls.findRenameLocations(fileName, position, findInStrings, findInComments, prefs));
|
|
1564
|
+
proxy.getReferencesAtPosition = (fileName, position) => notShadow(ls.getReferencesAtPosition(fileName, position));
|
|
1565
|
+
proxy.getDefinitionAtPosition = (fileName, position) => notShadow(ls.getDefinitionAtPosition(fileName, position));
|
|
1566
|
+
proxy.getImplementationAtPosition = (fileName, position) => notShadow(ls.getImplementationAtPosition(fileName, position));
|
|
1567
|
+
proxy.getDocumentHighlights = (fileName, position, filesToSearch) => ls.getDocumentHighlights(fileName, position, filesToSearch)?.filter((h) => !isShadowPath(h.fileName));
|
|
1568
|
+
proxy.findReferences = (fileName, position) => ls.findReferences(fileName, position)?.filter((r) => !isShadowPath(r.definition.fileName)).map((r) => ({ ...r, references: r.references.filter((x2) => !isShadowPath(x2.fileName)) }));
|
|
1343
1569
|
proxy.getQuickInfoAtPosition = (fileName, position) => {
|
|
1344
1570
|
try {
|
|
1345
1571
|
const prop = componentPropHover(ts, ls, fileName, position);
|
|
1346
1572
|
if (prop) return prop;
|
|
1347
1573
|
} catch {
|
|
1348
1574
|
}
|
|
1575
|
+
try {
|
|
1576
|
+
const attr = elementAttributeHover(ts, ls, host, fileName, position);
|
|
1577
|
+
if (attr) return attr;
|
|
1578
|
+
} catch {
|
|
1579
|
+
}
|
|
1349
1580
|
try {
|
|
1350
1581
|
const inferred = paramQuickInfo(ts, ls, fileName, position);
|
|
1351
1582
|
if (inferred) return inferred;
|
|
@@ -1359,6 +1590,11 @@ function init(modules) {
|
|
|
1359
1590
|
if (props) return completionList(props);
|
|
1360
1591
|
} catch {
|
|
1361
1592
|
}
|
|
1593
|
+
try {
|
|
1594
|
+
const attrs = elementAttributeCompletions(ts, ls, host, fileName, position);
|
|
1595
|
+
if (attrs) return completionList(attrs);
|
|
1596
|
+
} catch {
|
|
1597
|
+
}
|
|
1362
1598
|
try {
|
|
1363
1599
|
const members = paramMemberCompletions(ts, ls, fileName, position);
|
|
1364
1600
|
if (members) return completionList(members);
|