@formspec/build 0.1.0-alpha.36 → 0.1.0-alpha.38
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/README.md +36 -1
- package/dist/analyzer/class-analyzer.d.ts +4 -0
- package/dist/analyzer/class-analyzer.d.ts.map +1 -1
- package/dist/analyzer/program.d.ts.map +1 -1
- package/dist/analyzer/tsdoc-parser.d.ts.map +1 -1
- package/dist/build-alpha.d.ts +2 -0
- package/dist/build-beta.d.ts +2 -0
- package/dist/build-internal.d.ts +2 -0
- package/dist/build.d.ts +2 -0
- package/dist/cli.cjs +213 -8
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +213 -8
- package/dist/cli.js.map +1 -1
- package/dist/generators/discovered-schema.d.ts +2 -0
- package/dist/generators/discovered-schema.d.ts.map +1 -1
- package/dist/index.cjs +213 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +213 -8
- package/dist/index.js.map +1 -1
- package/dist/internals.cjs +219 -6
- package/dist/internals.cjs.map +1 -1
- package/dist/internals.js +219 -6
- package/dist/internals.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -2147,6 +2147,16 @@ function buildSupportingDeclarations(sourceFile, extensionTypeNames) {
|
|
|
2147
2147
|
return true;
|
|
2148
2148
|
}).map((statement) => statement.getText(sourceFile));
|
|
2149
2149
|
}
|
|
2150
|
+
function pushUniqueCompilerDiagnostics(target, additions) {
|
|
2151
|
+
for (const diagnostic of additions) {
|
|
2152
|
+
if ((diagnostic.code === "UNSUPPORTED_CUSTOM_TYPE_OVERRIDE" || diagnostic.code === "SYNTHETIC_SETUP_FAILURE") && target.some(
|
|
2153
|
+
(existing) => existing.code === diagnostic.code && existing.message === diagnostic.message
|
|
2154
|
+
)) {
|
|
2155
|
+
continue;
|
|
2156
|
+
}
|
|
2157
|
+
target.push(diagnostic);
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
2150
2160
|
function renderSyntheticArgumentExpression(valueKind, argumentText) {
|
|
2151
2161
|
const trimmed = argumentText.trim();
|
|
2152
2162
|
if (trimmed === "") {
|
|
@@ -2413,6 +2423,16 @@ function buildCompilerBackedConstraintDiagnostics(node, sourceFile, tagName, par
|
|
|
2413
2423
|
if (result.diagnostics.length === 0) {
|
|
2414
2424
|
return [];
|
|
2415
2425
|
}
|
|
2426
|
+
const setupDiagnostic = result.diagnostics.find((diagnostic) => diagnostic.kind !== "typescript");
|
|
2427
|
+
if (setupDiagnostic !== void 0) {
|
|
2428
|
+
return [
|
|
2429
|
+
makeDiagnostic(
|
|
2430
|
+
setupDiagnostic.kind === "unsupported-custom-type-override" ? "UNSUPPORTED_CUSTOM_TYPE_OVERRIDE" : "SYNTHETIC_SETUP_FAILURE",
|
|
2431
|
+
setupDiagnostic.message,
|
|
2432
|
+
provenance
|
|
2433
|
+
)
|
|
2434
|
+
];
|
|
2435
|
+
}
|
|
2416
2436
|
const expectedLabel = definition.valueKind === null ? "compatible argument" : capabilityLabel(definition.valueKind);
|
|
2417
2437
|
return [
|
|
2418
2438
|
makeDiagnostic(
|
|
@@ -2578,7 +2598,7 @@ function parseTSDocTags(node, file = "", options) {
|
|
|
2578
2598
|
options
|
|
2579
2599
|
);
|
|
2580
2600
|
if (compilerDiagnostics.length > 0) {
|
|
2581
|
-
diagnostics
|
|
2601
|
+
pushUniqueCompilerDiagnostics(diagnostics, compilerDiagnostics);
|
|
2582
2602
|
continue;
|
|
2583
2603
|
}
|
|
2584
2604
|
const constraintNode = parseConstraintTagValue(
|
|
@@ -2665,7 +2685,7 @@ function parseTSDocTags(node, file = "", options) {
|
|
|
2665
2685
|
options
|
|
2666
2686
|
);
|
|
2667
2687
|
if (compilerDiagnostics.length > 0) {
|
|
2668
|
-
diagnostics
|
|
2688
|
+
pushUniqueCompilerDiagnostics(diagnostics, compilerDiagnostics);
|
|
2669
2689
|
continue;
|
|
2670
2690
|
}
|
|
2671
2691
|
const constraintNode = parseConstraintTagValue(
|
|
@@ -2699,7 +2719,7 @@ function parseTSDocTags(node, file = "", options) {
|
|
|
2699
2719
|
options
|
|
2700
2720
|
);
|
|
2701
2721
|
if (compilerDiagnostics.length > 0) {
|
|
2702
|
-
diagnostics
|
|
2722
|
+
pushUniqueCompilerDiagnostics(diagnostics, compilerDiagnostics);
|
|
2703
2723
|
continue;
|
|
2704
2724
|
}
|
|
2705
2725
|
const constraintNode = parseConstraintTagValue(
|
|
@@ -2906,6 +2926,21 @@ function isObjectType(type) {
|
|
|
2906
2926
|
function isIntersectionType(type) {
|
|
2907
2927
|
return !!(type.flags & ts3.TypeFlags.Intersection);
|
|
2908
2928
|
}
|
|
2929
|
+
function isResolvableObjectLikeAliasTypeNode(typeNode) {
|
|
2930
|
+
if (ts3.isParenthesizedTypeNode(typeNode)) {
|
|
2931
|
+
return isResolvableObjectLikeAliasTypeNode(typeNode.type);
|
|
2932
|
+
}
|
|
2933
|
+
if (ts3.isTypeLiteralNode(typeNode) || ts3.isTypeReferenceNode(typeNode)) {
|
|
2934
|
+
return true;
|
|
2935
|
+
}
|
|
2936
|
+
return ts3.isIntersectionTypeNode(typeNode) && typeNode.types.length > 0 && typeNode.types.every((member) => isResolvableObjectLikeAliasTypeNode(member));
|
|
2937
|
+
}
|
|
2938
|
+
function isSemanticallyPlainObjectLikeType(type, checker) {
|
|
2939
|
+
if (isIntersectionType(type)) {
|
|
2940
|
+
return type.types.length > 0 && type.types.every((member) => isSemanticallyPlainObjectLikeType(member, checker));
|
|
2941
|
+
}
|
|
2942
|
+
return isObjectType(type) && checker.getSignaturesOfType(type, ts3.SignatureKind.Call).length === 0 && checker.getSignaturesOfType(type, ts3.SignatureKind.Construct).length === 0 && !checker.isArrayType(type) && !checker.isTupleType(type);
|
|
2943
|
+
}
|
|
2909
2944
|
function isTypeReference(type) {
|
|
2910
2945
|
return !!(type.flags & ts3.TypeFlags.Object) && !!(type.objectFlags & ts3.ObjectFlags.Reference);
|
|
2911
2946
|
}
|
|
@@ -3163,6 +3198,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry,
|
|
|
3163
3198
|
const kindDesc = ts3.SyntaxKind[typeAlias.type.kind] ?? "unknown";
|
|
3164
3199
|
return {
|
|
3165
3200
|
ok: false,
|
|
3201
|
+
kind: "not-object-like",
|
|
3166
3202
|
error: `Type alias "${typeAlias.name.text}" at line ${String(line + 1)} is not an object-like type alias (found ${kindDesc})`
|
|
3167
3203
|
};
|
|
3168
3204
|
}
|
|
@@ -3177,6 +3213,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry,
|
|
|
3177
3213
|
const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
|
|
3178
3214
|
return {
|
|
3179
3215
|
ok: false,
|
|
3216
|
+
kind: "duplicate-properties",
|
|
3180
3217
|
error: `Type alias "${name}" at line ${String(line + 1)} contains duplicate property names across object-like members: ${duplicatePropertyNames.join(", ")}`
|
|
3181
3218
|
};
|
|
3182
3219
|
}
|
|
@@ -3873,10 +3910,10 @@ function findDuplicateObjectLikeTypeAliasPropertyNames(members) {
|
|
|
3873
3910
|
return [...duplicates].sort();
|
|
3874
3911
|
}
|
|
3875
3912
|
function getAnalyzableObjectLikePropertyName(name) {
|
|
3876
|
-
if (
|
|
3877
|
-
return
|
|
3913
|
+
if (ts3.isIdentifier(name) || ts3.isStringLiteral(name) || ts3.isNumericLiteral(name)) {
|
|
3914
|
+
return name.text;
|
|
3878
3915
|
}
|
|
3879
|
-
return
|
|
3916
|
+
return null;
|
|
3880
3917
|
}
|
|
3881
3918
|
function applyEnumMemberDisplayNames(type, annotations) {
|
|
3882
3919
|
if (!annotations.some(
|
|
@@ -4086,6 +4123,19 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
|
|
|
4086
4123
|
diagnostics
|
|
4087
4124
|
);
|
|
4088
4125
|
}
|
|
4126
|
+
if (resolvedSourceTypeNode !== void 0 && isResolvableObjectLikeAliasTypeNode(resolvedSourceTypeNode) && isSemanticallyPlainObjectLikeType(type, checker)) {
|
|
4127
|
+
return resolveObjectType(
|
|
4128
|
+
type,
|
|
4129
|
+
checker,
|
|
4130
|
+
file,
|
|
4131
|
+
typeRegistry,
|
|
4132
|
+
visiting,
|
|
4133
|
+
sourceNode,
|
|
4134
|
+
metadataPolicy,
|
|
4135
|
+
extensionRegistry,
|
|
4136
|
+
diagnostics
|
|
4137
|
+
);
|
|
4138
|
+
}
|
|
4089
4139
|
}
|
|
4090
4140
|
if (isObjectType(type)) {
|
|
4091
4141
|
return resolveObjectType(
|
|
@@ -4993,6 +5043,95 @@ function findInterfaceByName(sourceFile, interfaceName) {
|
|
|
4993
5043
|
function findTypeAliasByName(sourceFile, aliasName) {
|
|
4994
5044
|
return findNodeByName(sourceFile, aliasName, ts4.isTypeAliasDeclaration, (n) => n.name.text);
|
|
4995
5045
|
}
|
|
5046
|
+
function getResolvedObjectRootType(rootType, typeRegistry) {
|
|
5047
|
+
if (rootType.kind === "object") {
|
|
5048
|
+
return rootType;
|
|
5049
|
+
}
|
|
5050
|
+
if (rootType.kind !== "reference") {
|
|
5051
|
+
return null;
|
|
5052
|
+
}
|
|
5053
|
+
const definition = typeRegistry[rootType.name];
|
|
5054
|
+
return definition?.type.kind === "object" ? definition.type : null;
|
|
5055
|
+
}
|
|
5056
|
+
function createResolvedObjectAliasAnalysis(name, rootType, typeRegistry, rootInfo, diagnostics) {
|
|
5057
|
+
const resolvedRootType = getResolvedObjectRootType(rootType, typeRegistry);
|
|
5058
|
+
if (resolvedRootType === null) {
|
|
5059
|
+
return null;
|
|
5060
|
+
}
|
|
5061
|
+
const fields = resolvedRootType.properties.map((property) => ({
|
|
5062
|
+
kind: "field",
|
|
5063
|
+
name: property.name,
|
|
5064
|
+
...property.metadata !== void 0 && { metadata: property.metadata },
|
|
5065
|
+
type: property.type,
|
|
5066
|
+
required: !property.optional,
|
|
5067
|
+
constraints: property.constraints,
|
|
5068
|
+
annotations: property.annotations,
|
|
5069
|
+
provenance: property.provenance
|
|
5070
|
+
}));
|
|
5071
|
+
return {
|
|
5072
|
+
name,
|
|
5073
|
+
...rootInfo.metadata !== void 0 && { metadata: rootInfo.metadata },
|
|
5074
|
+
fields,
|
|
5075
|
+
fieldLayouts: fields.map(() => ({})),
|
|
5076
|
+
typeRegistry,
|
|
5077
|
+
...rootInfo.annotations.length > 0 && { annotations: [...rootInfo.annotations] },
|
|
5078
|
+
...diagnostics.length > 0 && { diagnostics: [...diagnostics] },
|
|
5079
|
+
instanceMethods: [],
|
|
5080
|
+
staticMethods: []
|
|
5081
|
+
};
|
|
5082
|
+
}
|
|
5083
|
+
function containsTypeReferenceInObjectLikeAlias(typeNode) {
|
|
5084
|
+
if (ts4.isParenthesizedTypeNode(typeNode)) {
|
|
5085
|
+
return containsTypeReferenceInObjectLikeAlias(typeNode.type);
|
|
5086
|
+
}
|
|
5087
|
+
if (ts4.isTypeReferenceNode(typeNode)) {
|
|
5088
|
+
return true;
|
|
5089
|
+
}
|
|
5090
|
+
return ts4.isIntersectionTypeNode(typeNode) && typeNode.types.some((member) => containsTypeReferenceInObjectLikeAlias(member));
|
|
5091
|
+
}
|
|
5092
|
+
function collectFallbackAliasMemberPropertyNames(typeNode, checker) {
|
|
5093
|
+
if (ts4.isParenthesizedTypeNode(typeNode)) {
|
|
5094
|
+
return collectFallbackAliasMemberPropertyNames(typeNode.type, checker);
|
|
5095
|
+
}
|
|
5096
|
+
if (ts4.isTypeLiteralNode(typeNode)) {
|
|
5097
|
+
const propertyNames = [];
|
|
5098
|
+
for (const member of typeNode.members) {
|
|
5099
|
+
if (!ts4.isPropertySignature(member)) {
|
|
5100
|
+
continue;
|
|
5101
|
+
}
|
|
5102
|
+
const propertyName = getAnalyzableObjectLikePropertyName(member.name);
|
|
5103
|
+
if (propertyName !== null) {
|
|
5104
|
+
propertyNames.push(propertyName);
|
|
5105
|
+
}
|
|
5106
|
+
}
|
|
5107
|
+
return propertyNames;
|
|
5108
|
+
}
|
|
5109
|
+
if (ts4.isTypeReferenceNode(typeNode)) {
|
|
5110
|
+
return checker.getTypeFromTypeNode(typeNode).getProperties().map((property) => property.getName());
|
|
5111
|
+
}
|
|
5112
|
+
return null;
|
|
5113
|
+
}
|
|
5114
|
+
function findFallbackAliasDuplicatePropertyNames(typeNode, checker) {
|
|
5115
|
+
if (!ts4.isIntersectionTypeNode(typeNode)) {
|
|
5116
|
+
return [];
|
|
5117
|
+
}
|
|
5118
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5119
|
+
const duplicates = /* @__PURE__ */ new Set();
|
|
5120
|
+
for (const member of typeNode.types) {
|
|
5121
|
+
const propertyNames = collectFallbackAliasMemberPropertyNames(member, checker);
|
|
5122
|
+
if (propertyNames === null) {
|
|
5123
|
+
continue;
|
|
5124
|
+
}
|
|
5125
|
+
for (const propertyName of propertyNames) {
|
|
5126
|
+
if (seen.has(propertyName)) {
|
|
5127
|
+
duplicates.add(propertyName);
|
|
5128
|
+
} else {
|
|
5129
|
+
seen.add(propertyName);
|
|
5130
|
+
}
|
|
5131
|
+
}
|
|
5132
|
+
}
|
|
5133
|
+
return [...duplicates].sort();
|
|
5134
|
+
}
|
|
4996
5135
|
function analyzeNamedTypeToIR(filePath, typeName, extensionRegistry, metadataPolicy, discriminatorOptions) {
|
|
4997
5136
|
const ctx = createProgramContext(filePath);
|
|
4998
5137
|
return analyzeNamedTypeToIRFromProgramContext(
|
|
@@ -5041,6 +5180,51 @@ function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensi
|
|
|
5041
5180
|
if (result.ok) {
|
|
5042
5181
|
return result.analysis;
|
|
5043
5182
|
}
|
|
5183
|
+
const fallbackEligible = result.kind === "not-object-like" && isResolvableObjectLikeAliasTypeNode(typeAlias.type) && containsTypeReferenceInObjectLikeAlias(typeAlias.type);
|
|
5184
|
+
if (!fallbackEligible) {
|
|
5185
|
+
throw new Error(result.error);
|
|
5186
|
+
}
|
|
5187
|
+
const duplicatePropertyNames = findFallbackAliasDuplicatePropertyNames(
|
|
5188
|
+
typeAlias.type,
|
|
5189
|
+
ctx.checker
|
|
5190
|
+
);
|
|
5191
|
+
if (duplicatePropertyNames.length > 0) {
|
|
5192
|
+
const sourceFile = typeAlias.getSourceFile();
|
|
5193
|
+
const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
|
|
5194
|
+
throw new Error(
|
|
5195
|
+
`Type alias "${typeAlias.name.text}" at line ${String(line + 1)} contains duplicate property names across object-like members: ${duplicatePropertyNames.join(", ")}`
|
|
5196
|
+
);
|
|
5197
|
+
}
|
|
5198
|
+
const rootInfo = analyzeDeclarationRootInfo(
|
|
5199
|
+
typeAlias,
|
|
5200
|
+
ctx.checker,
|
|
5201
|
+
analysisFilePath,
|
|
5202
|
+
extensionRegistry,
|
|
5203
|
+
metadataPolicy
|
|
5204
|
+
);
|
|
5205
|
+
const diagnostics = [...rootInfo.diagnostics];
|
|
5206
|
+
const typeRegistry = {};
|
|
5207
|
+
const rootType = resolveTypeNode(
|
|
5208
|
+
ctx.checker.getTypeAtLocation(typeAlias),
|
|
5209
|
+
ctx.checker,
|
|
5210
|
+
analysisFilePath,
|
|
5211
|
+
typeRegistry,
|
|
5212
|
+
/* @__PURE__ */ new Set(),
|
|
5213
|
+
typeAlias,
|
|
5214
|
+
createAnalyzerMetadataPolicy(metadataPolicy, discriminatorOptions),
|
|
5215
|
+
extensionRegistry,
|
|
5216
|
+
diagnostics
|
|
5217
|
+
);
|
|
5218
|
+
const fallbackAnalysis = createResolvedObjectAliasAnalysis(
|
|
5219
|
+
typeAlias.name.text,
|
|
5220
|
+
rootType,
|
|
5221
|
+
typeRegistry,
|
|
5222
|
+
rootInfo,
|
|
5223
|
+
diagnostics
|
|
5224
|
+
);
|
|
5225
|
+
if (fallbackAnalysis !== null) {
|
|
5226
|
+
return fallbackAnalysis;
|
|
5227
|
+
}
|
|
5044
5228
|
throw new Error(result.error);
|
|
5045
5229
|
}
|
|
5046
5230
|
throw new Error(
|
|
@@ -5546,15 +5730,36 @@ function generateSchemasFromParameter(options) {
|
|
|
5546
5730
|
}
|
|
5547
5731
|
function generateSchemasFromReturnType(options) {
|
|
5548
5732
|
const signature = options.context.checker.getSignatureFromDeclaration(options.declaration);
|
|
5549
|
-
const
|
|
5733
|
+
const returnType = signature !== void 0 ? options.context.checker.getReturnTypeOfSignature(signature) : options.context.checker.getTypeAtLocation(options.declaration);
|
|
5734
|
+
const type = unwrapPromiseType(options.context.checker, returnType);
|
|
5735
|
+
const sourceNode = type !== returnType ? unwrapPromiseTypeNode(options.declaration.type) ?? options.declaration.type ?? options.declaration : options.declaration.type ?? options.declaration;
|
|
5550
5736
|
const fallbackName = options.declaration.name !== void 0 && ts7.isIdentifier(options.declaration.name) ? `${options.declaration.name.text}ReturnType` : "ReturnType";
|
|
5551
5737
|
return generateSchemasFromResolvedType({
|
|
5552
5738
|
...options,
|
|
5553
5739
|
type,
|
|
5554
|
-
sourceNode
|
|
5740
|
+
sourceNode,
|
|
5555
5741
|
name: fallbackName
|
|
5556
5742
|
});
|
|
5557
5743
|
}
|
|
5744
|
+
function unwrapPromiseType(checker, type) {
|
|
5745
|
+
if (!("getAwaitedType" in checker) || typeof checker.getAwaitedType !== "function") {
|
|
5746
|
+
return type;
|
|
5747
|
+
}
|
|
5748
|
+
return checker.getAwaitedType(type) ?? type;
|
|
5749
|
+
}
|
|
5750
|
+
function unwrapPromiseTypeNode(typeNode) {
|
|
5751
|
+
if (typeNode === void 0) {
|
|
5752
|
+
return void 0;
|
|
5753
|
+
}
|
|
5754
|
+
if (ts7.isParenthesizedTypeNode(typeNode)) {
|
|
5755
|
+
const unwrapped = unwrapPromiseTypeNode(typeNode.type);
|
|
5756
|
+
return unwrapped ?? typeNode;
|
|
5757
|
+
}
|
|
5758
|
+
return isPromiseTypeReferenceNode(typeNode) ? typeNode.typeArguments[0] : typeNode;
|
|
5759
|
+
}
|
|
5760
|
+
function isPromiseTypeReferenceNode(typeNode) {
|
|
5761
|
+
return ts7.isTypeReferenceNode(typeNode) && ts7.isIdentifier(typeNode.typeName) && typeNode.typeName.text === "Promise" && typeNode.typeArguments !== void 0 && typeNode.typeArguments.length > 0;
|
|
5762
|
+
}
|
|
5558
5763
|
|
|
5559
5764
|
// src/generators/mixed-authoring.ts
|
|
5560
5765
|
function buildMixedAuthoringSchemas(options) {
|