@formspec/build 0.1.0-alpha.48 → 0.1.0-alpha.49
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/analyzer/class-analyzer.d.ts.map +1 -1
- package/dist/analyzer/tsdoc-parser.d.ts.map +1 -1
- package/dist/cli.cjs +113 -3
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +113 -3
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +112 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +112 -2
- package/dist/index.js.map +1 -1
- package/dist/internals.cjs +98 -1
- package/dist/internals.cjs.map +1 -1
- package/dist/internals.js +98 -1
- package/dist/internals.js.map +1 -1
- package/package.json +1 -1
package/dist/internals.js
CHANGED
|
@@ -999,6 +999,92 @@ function supportsConstraintCapability(type, checker, capability) {
|
|
|
999
999
|
}
|
|
1000
1000
|
return false;
|
|
1001
1001
|
}
|
|
1002
|
+
var MAX_HINT_CANDIDATES = 5;
|
|
1003
|
+
var MAX_HINT_DEPTH = 3;
|
|
1004
|
+
function stripHintNullishUnion(type) {
|
|
1005
|
+
if (!type.isUnion()) {
|
|
1006
|
+
return type;
|
|
1007
|
+
}
|
|
1008
|
+
const nonNullish = type.types.filter(
|
|
1009
|
+
(member) => (member.flags & (ts.TypeFlags.Null | ts.TypeFlags.Undefined)) === 0
|
|
1010
|
+
);
|
|
1011
|
+
if (nonNullish.length === 1 && nonNullish[0] !== void 0) {
|
|
1012
|
+
return nonNullish[0];
|
|
1013
|
+
}
|
|
1014
|
+
return type;
|
|
1015
|
+
}
|
|
1016
|
+
function isCallableType(type) {
|
|
1017
|
+
return type.getCallSignatures().length > 0 || type.getConstructSignatures().length > 0;
|
|
1018
|
+
}
|
|
1019
|
+
function isUserEmittableHintProperty(property, declaration) {
|
|
1020
|
+
if (property.name.startsWith("__")) {
|
|
1021
|
+
return false;
|
|
1022
|
+
}
|
|
1023
|
+
if ("name" in declaration && declaration.name !== void 0) {
|
|
1024
|
+
const name = declaration.name;
|
|
1025
|
+
if (ts.isComputedPropertyName(name) || ts.isPrivateIdentifier(name)) {
|
|
1026
|
+
return false;
|
|
1027
|
+
}
|
|
1028
|
+
if (!ts.isIdentifier(name) && !ts.isStringLiteral(name) && !ts.isNumericLiteral(name)) {
|
|
1029
|
+
return false;
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
return true;
|
|
1033
|
+
}
|
|
1034
|
+
function collectObjectSubfieldCandidates(type, checker, capability) {
|
|
1035
|
+
const out = [];
|
|
1036
|
+
const visit = (current, prefix, depth) => {
|
|
1037
|
+
if (depth > MAX_HINT_DEPTH) {
|
|
1038
|
+
return;
|
|
1039
|
+
}
|
|
1040
|
+
const stripped = stripHintNullishUnion(current);
|
|
1041
|
+
if (isCallableType(stripped)) {
|
|
1042
|
+
return;
|
|
1043
|
+
}
|
|
1044
|
+
if (!hasTypeSemanticCapability(stripped, checker, "object-like")) {
|
|
1045
|
+
return;
|
|
1046
|
+
}
|
|
1047
|
+
for (const property of stripped.getProperties()) {
|
|
1048
|
+
const declaration = property.valueDeclaration ?? property.declarations?.[0];
|
|
1049
|
+
if (declaration === void 0) {
|
|
1050
|
+
continue;
|
|
1051
|
+
}
|
|
1052
|
+
if (!isUserEmittableHintProperty(property, declaration)) {
|
|
1053
|
+
continue;
|
|
1054
|
+
}
|
|
1055
|
+
const propertyType = checker.getTypeOfSymbolAtLocation(property, declaration);
|
|
1056
|
+
const path3 = [...prefix, property.name];
|
|
1057
|
+
if (supportsConstraintCapability(propertyType, checker, capability)) {
|
|
1058
|
+
out.push(path3.join("."));
|
|
1059
|
+
continue;
|
|
1060
|
+
}
|
|
1061
|
+
const strippedPropertyType = stripHintNullishUnion(propertyType);
|
|
1062
|
+
if (!isCallableType(strippedPropertyType) && hasTypeSemanticCapability(strippedPropertyType, checker, "object-like")) {
|
|
1063
|
+
visit(strippedPropertyType, path3, depth + 1);
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
};
|
|
1067
|
+
visit(type, [], 0);
|
|
1068
|
+
return out;
|
|
1069
|
+
}
|
|
1070
|
+
function buildPathTargetHint(subjectType, checker, capability, tagName, argumentText) {
|
|
1071
|
+
if (!hasTypeSemanticCapability(subjectType, checker, "object-like")) {
|
|
1072
|
+
return null;
|
|
1073
|
+
}
|
|
1074
|
+
const candidates = collectObjectSubfieldCandidates(subjectType, checker, capability);
|
|
1075
|
+
const primary = candidates[0];
|
|
1076
|
+
if (primary === void 0) {
|
|
1077
|
+
return null;
|
|
1078
|
+
}
|
|
1079
|
+
const argText = argumentText?.trim() ?? "";
|
|
1080
|
+
const renderExample = (path3) => argText === "" ? `@${tagName} :${path3}` : `@${tagName} :${path3} ${argText}`;
|
|
1081
|
+
if (candidates.length === 1) {
|
|
1082
|
+
return `Hint: use a path target to constrain a subfield, e.g. ${renderExample(primary)}`;
|
|
1083
|
+
}
|
|
1084
|
+
const shown = candidates.slice(0, MAX_HINT_CANDIDATES);
|
|
1085
|
+
const overflow = candidates.length > MAX_HINT_CANDIDATES ? ", \u2026" : "";
|
|
1086
|
+
return `Hint: use a path target to constrain a subfield (candidates: ${shown.join(", ")}${overflow}), e.g. ${renderExample(primary)}`;
|
|
1087
|
+
}
|
|
1002
1088
|
function makeDiagnostic(code, message, provenance) {
|
|
1003
1089
|
return {
|
|
1004
1090
|
code,
|
|
@@ -1166,10 +1252,18 @@ function buildCompilerBackedConstraintDiagnostics(node, sourceFile, tagName, par
|
|
|
1166
1252
|
const requiredCapability = definition.capabilities[0];
|
|
1167
1253
|
if (requiredCapability !== void 0 && !supportsConstraintCapability(subjectType, checker, requiredCapability)) {
|
|
1168
1254
|
const actualType = checker.typeToString(subjectType, node, SYNTHETIC_TYPE_FORMAT_FLAGS);
|
|
1255
|
+
const baseMessage = `Target "${node.getText(sourceFile)}": constraint "${tagName}" is only valid on ${capabilityLabel(requiredCapability)} targets, but field type is "${actualType}"`;
|
|
1256
|
+
const hint = buildPathTargetHint(
|
|
1257
|
+
subjectType,
|
|
1258
|
+
checker,
|
|
1259
|
+
requiredCapability,
|
|
1260
|
+
tagName,
|
|
1261
|
+
parsedTag?.argumentText
|
|
1262
|
+
);
|
|
1169
1263
|
return [
|
|
1170
1264
|
makeDiagnostic(
|
|
1171
1265
|
"TYPE_MISMATCH",
|
|
1172
|
-
|
|
1266
|
+
hint === null ? baseMessage : `${baseMessage}. ${hint}`,
|
|
1173
1267
|
provenance
|
|
1174
1268
|
)
|
|
1175
1269
|
];
|
|
@@ -2819,6 +2913,9 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
|
|
|
2819
2913
|
if (type.flags & ts3.TypeFlags.Undefined) {
|
|
2820
2914
|
return { kind: "primitive", primitiveKind: "null" };
|
|
2821
2915
|
}
|
|
2916
|
+
if (type.flags & ts3.TypeFlags.Void) {
|
|
2917
|
+
return { kind: "primitive", primitiveKind: "null" };
|
|
2918
|
+
}
|
|
2822
2919
|
if (type.isStringLiteral()) {
|
|
2823
2920
|
return {
|
|
2824
2921
|
kind: "enum",
|