@formspec/build 0.1.0-alpha.36 → 0.1.0-alpha.37
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 +1 -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/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 +190 -5
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +190 -5
- 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 +190 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +190 -5
- package/dist/index.js.map +1 -1
- package/dist/internals.cjs +196 -3
- package/dist/internals.cjs.map +1 -1
- package/dist/internals.js +196 -3
- package/dist/internals.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -2906,6 +2906,21 @@ function isObjectType(type) {
|
|
|
2906
2906
|
function isIntersectionType(type) {
|
|
2907
2907
|
return !!(type.flags & ts3.TypeFlags.Intersection);
|
|
2908
2908
|
}
|
|
2909
|
+
function isResolvableObjectLikeAliasTypeNode(typeNode) {
|
|
2910
|
+
if (ts3.isParenthesizedTypeNode(typeNode)) {
|
|
2911
|
+
return isResolvableObjectLikeAliasTypeNode(typeNode.type);
|
|
2912
|
+
}
|
|
2913
|
+
if (ts3.isTypeLiteralNode(typeNode) || ts3.isTypeReferenceNode(typeNode)) {
|
|
2914
|
+
return true;
|
|
2915
|
+
}
|
|
2916
|
+
return ts3.isIntersectionTypeNode(typeNode) && typeNode.types.length > 0 && typeNode.types.every((member) => isResolvableObjectLikeAliasTypeNode(member));
|
|
2917
|
+
}
|
|
2918
|
+
function isSemanticallyPlainObjectLikeType(type, checker) {
|
|
2919
|
+
if (isIntersectionType(type)) {
|
|
2920
|
+
return type.types.length > 0 && type.types.every((member) => isSemanticallyPlainObjectLikeType(member, checker));
|
|
2921
|
+
}
|
|
2922
|
+
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);
|
|
2923
|
+
}
|
|
2909
2924
|
function isTypeReference(type) {
|
|
2910
2925
|
return !!(type.flags & ts3.TypeFlags.Object) && !!(type.objectFlags & ts3.ObjectFlags.Reference);
|
|
2911
2926
|
}
|
|
@@ -3163,6 +3178,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry,
|
|
|
3163
3178
|
const kindDesc = ts3.SyntaxKind[typeAlias.type.kind] ?? "unknown";
|
|
3164
3179
|
return {
|
|
3165
3180
|
ok: false,
|
|
3181
|
+
kind: "not-object-like",
|
|
3166
3182
|
error: `Type alias "${typeAlias.name.text}" at line ${String(line + 1)} is not an object-like type alias (found ${kindDesc})`
|
|
3167
3183
|
};
|
|
3168
3184
|
}
|
|
@@ -3177,6 +3193,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry,
|
|
|
3177
3193
|
const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
|
|
3178
3194
|
return {
|
|
3179
3195
|
ok: false,
|
|
3196
|
+
kind: "duplicate-properties",
|
|
3180
3197
|
error: `Type alias "${name}" at line ${String(line + 1)} contains duplicate property names across object-like members: ${duplicatePropertyNames.join(", ")}`
|
|
3181
3198
|
};
|
|
3182
3199
|
}
|
|
@@ -3873,10 +3890,10 @@ function findDuplicateObjectLikeTypeAliasPropertyNames(members) {
|
|
|
3873
3890
|
return [...duplicates].sort();
|
|
3874
3891
|
}
|
|
3875
3892
|
function getAnalyzableObjectLikePropertyName(name) {
|
|
3876
|
-
if (
|
|
3877
|
-
return
|
|
3893
|
+
if (ts3.isIdentifier(name) || ts3.isStringLiteral(name) || ts3.isNumericLiteral(name)) {
|
|
3894
|
+
return name.text;
|
|
3878
3895
|
}
|
|
3879
|
-
return
|
|
3896
|
+
return null;
|
|
3880
3897
|
}
|
|
3881
3898
|
function applyEnumMemberDisplayNames(type, annotations) {
|
|
3882
3899
|
if (!annotations.some(
|
|
@@ -4086,6 +4103,19 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
|
|
|
4086
4103
|
diagnostics
|
|
4087
4104
|
);
|
|
4088
4105
|
}
|
|
4106
|
+
if (resolvedSourceTypeNode !== void 0 && isResolvableObjectLikeAliasTypeNode(resolvedSourceTypeNode) && isSemanticallyPlainObjectLikeType(type, checker)) {
|
|
4107
|
+
return resolveObjectType(
|
|
4108
|
+
type,
|
|
4109
|
+
checker,
|
|
4110
|
+
file,
|
|
4111
|
+
typeRegistry,
|
|
4112
|
+
visiting,
|
|
4113
|
+
sourceNode,
|
|
4114
|
+
metadataPolicy,
|
|
4115
|
+
extensionRegistry,
|
|
4116
|
+
diagnostics
|
|
4117
|
+
);
|
|
4118
|
+
}
|
|
4089
4119
|
}
|
|
4090
4120
|
if (isObjectType(type)) {
|
|
4091
4121
|
return resolveObjectType(
|
|
@@ -4993,6 +5023,95 @@ function findInterfaceByName(sourceFile, interfaceName) {
|
|
|
4993
5023
|
function findTypeAliasByName(sourceFile, aliasName) {
|
|
4994
5024
|
return findNodeByName(sourceFile, aliasName, ts4.isTypeAliasDeclaration, (n) => n.name.text);
|
|
4995
5025
|
}
|
|
5026
|
+
function getResolvedObjectRootType(rootType, typeRegistry) {
|
|
5027
|
+
if (rootType.kind === "object") {
|
|
5028
|
+
return rootType;
|
|
5029
|
+
}
|
|
5030
|
+
if (rootType.kind !== "reference") {
|
|
5031
|
+
return null;
|
|
5032
|
+
}
|
|
5033
|
+
const definition = typeRegistry[rootType.name];
|
|
5034
|
+
return definition?.type.kind === "object" ? definition.type : null;
|
|
5035
|
+
}
|
|
5036
|
+
function createResolvedObjectAliasAnalysis(name, rootType, typeRegistry, rootInfo, diagnostics) {
|
|
5037
|
+
const resolvedRootType = getResolvedObjectRootType(rootType, typeRegistry);
|
|
5038
|
+
if (resolvedRootType === null) {
|
|
5039
|
+
return null;
|
|
5040
|
+
}
|
|
5041
|
+
const fields = resolvedRootType.properties.map((property) => ({
|
|
5042
|
+
kind: "field",
|
|
5043
|
+
name: property.name,
|
|
5044
|
+
...property.metadata !== void 0 && { metadata: property.metadata },
|
|
5045
|
+
type: property.type,
|
|
5046
|
+
required: !property.optional,
|
|
5047
|
+
constraints: property.constraints,
|
|
5048
|
+
annotations: property.annotations,
|
|
5049
|
+
provenance: property.provenance
|
|
5050
|
+
}));
|
|
5051
|
+
return {
|
|
5052
|
+
name,
|
|
5053
|
+
...rootInfo.metadata !== void 0 && { metadata: rootInfo.metadata },
|
|
5054
|
+
fields,
|
|
5055
|
+
fieldLayouts: fields.map(() => ({})),
|
|
5056
|
+
typeRegistry,
|
|
5057
|
+
...rootInfo.annotations.length > 0 && { annotations: [...rootInfo.annotations] },
|
|
5058
|
+
...diagnostics.length > 0 && { diagnostics: [...diagnostics] },
|
|
5059
|
+
instanceMethods: [],
|
|
5060
|
+
staticMethods: []
|
|
5061
|
+
};
|
|
5062
|
+
}
|
|
5063
|
+
function containsTypeReferenceInObjectLikeAlias(typeNode) {
|
|
5064
|
+
if (ts4.isParenthesizedTypeNode(typeNode)) {
|
|
5065
|
+
return containsTypeReferenceInObjectLikeAlias(typeNode.type);
|
|
5066
|
+
}
|
|
5067
|
+
if (ts4.isTypeReferenceNode(typeNode)) {
|
|
5068
|
+
return true;
|
|
5069
|
+
}
|
|
5070
|
+
return ts4.isIntersectionTypeNode(typeNode) && typeNode.types.some((member) => containsTypeReferenceInObjectLikeAlias(member));
|
|
5071
|
+
}
|
|
5072
|
+
function collectFallbackAliasMemberPropertyNames(typeNode, checker) {
|
|
5073
|
+
if (ts4.isParenthesizedTypeNode(typeNode)) {
|
|
5074
|
+
return collectFallbackAliasMemberPropertyNames(typeNode.type, checker);
|
|
5075
|
+
}
|
|
5076
|
+
if (ts4.isTypeLiteralNode(typeNode)) {
|
|
5077
|
+
const propertyNames = [];
|
|
5078
|
+
for (const member of typeNode.members) {
|
|
5079
|
+
if (!ts4.isPropertySignature(member)) {
|
|
5080
|
+
continue;
|
|
5081
|
+
}
|
|
5082
|
+
const propertyName = getAnalyzableObjectLikePropertyName(member.name);
|
|
5083
|
+
if (propertyName !== null) {
|
|
5084
|
+
propertyNames.push(propertyName);
|
|
5085
|
+
}
|
|
5086
|
+
}
|
|
5087
|
+
return propertyNames;
|
|
5088
|
+
}
|
|
5089
|
+
if (ts4.isTypeReferenceNode(typeNode)) {
|
|
5090
|
+
return checker.getTypeFromTypeNode(typeNode).getProperties().map((property) => property.getName());
|
|
5091
|
+
}
|
|
5092
|
+
return null;
|
|
5093
|
+
}
|
|
5094
|
+
function findFallbackAliasDuplicatePropertyNames(typeNode, checker) {
|
|
5095
|
+
if (!ts4.isIntersectionTypeNode(typeNode)) {
|
|
5096
|
+
return [];
|
|
5097
|
+
}
|
|
5098
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5099
|
+
const duplicates = /* @__PURE__ */ new Set();
|
|
5100
|
+
for (const member of typeNode.types) {
|
|
5101
|
+
const propertyNames = collectFallbackAliasMemberPropertyNames(member, checker);
|
|
5102
|
+
if (propertyNames === null) {
|
|
5103
|
+
continue;
|
|
5104
|
+
}
|
|
5105
|
+
for (const propertyName of propertyNames) {
|
|
5106
|
+
if (seen.has(propertyName)) {
|
|
5107
|
+
duplicates.add(propertyName);
|
|
5108
|
+
} else {
|
|
5109
|
+
seen.add(propertyName);
|
|
5110
|
+
}
|
|
5111
|
+
}
|
|
5112
|
+
}
|
|
5113
|
+
return [...duplicates].sort();
|
|
5114
|
+
}
|
|
4996
5115
|
function analyzeNamedTypeToIR(filePath, typeName, extensionRegistry, metadataPolicy, discriminatorOptions) {
|
|
4997
5116
|
const ctx = createProgramContext(filePath);
|
|
4998
5117
|
return analyzeNamedTypeToIRFromProgramContext(
|
|
@@ -5041,6 +5160,51 @@ function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensi
|
|
|
5041
5160
|
if (result.ok) {
|
|
5042
5161
|
return result.analysis;
|
|
5043
5162
|
}
|
|
5163
|
+
const fallbackEligible = result.kind === "not-object-like" && isResolvableObjectLikeAliasTypeNode(typeAlias.type) && containsTypeReferenceInObjectLikeAlias(typeAlias.type);
|
|
5164
|
+
if (!fallbackEligible) {
|
|
5165
|
+
throw new Error(result.error);
|
|
5166
|
+
}
|
|
5167
|
+
const duplicatePropertyNames = findFallbackAliasDuplicatePropertyNames(
|
|
5168
|
+
typeAlias.type,
|
|
5169
|
+
ctx.checker
|
|
5170
|
+
);
|
|
5171
|
+
if (duplicatePropertyNames.length > 0) {
|
|
5172
|
+
const sourceFile = typeAlias.getSourceFile();
|
|
5173
|
+
const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
|
|
5174
|
+
throw new Error(
|
|
5175
|
+
`Type alias "${typeAlias.name.text}" at line ${String(line + 1)} contains duplicate property names across object-like members: ${duplicatePropertyNames.join(", ")}`
|
|
5176
|
+
);
|
|
5177
|
+
}
|
|
5178
|
+
const rootInfo = analyzeDeclarationRootInfo(
|
|
5179
|
+
typeAlias,
|
|
5180
|
+
ctx.checker,
|
|
5181
|
+
analysisFilePath,
|
|
5182
|
+
extensionRegistry,
|
|
5183
|
+
metadataPolicy
|
|
5184
|
+
);
|
|
5185
|
+
const diagnostics = [...rootInfo.diagnostics];
|
|
5186
|
+
const typeRegistry = {};
|
|
5187
|
+
const rootType = resolveTypeNode(
|
|
5188
|
+
ctx.checker.getTypeAtLocation(typeAlias),
|
|
5189
|
+
ctx.checker,
|
|
5190
|
+
analysisFilePath,
|
|
5191
|
+
typeRegistry,
|
|
5192
|
+
/* @__PURE__ */ new Set(),
|
|
5193
|
+
typeAlias,
|
|
5194
|
+
createAnalyzerMetadataPolicy(metadataPolicy, discriminatorOptions),
|
|
5195
|
+
extensionRegistry,
|
|
5196
|
+
diagnostics
|
|
5197
|
+
);
|
|
5198
|
+
const fallbackAnalysis = createResolvedObjectAliasAnalysis(
|
|
5199
|
+
typeAlias.name.text,
|
|
5200
|
+
rootType,
|
|
5201
|
+
typeRegistry,
|
|
5202
|
+
rootInfo,
|
|
5203
|
+
diagnostics
|
|
5204
|
+
);
|
|
5205
|
+
if (fallbackAnalysis !== null) {
|
|
5206
|
+
return fallbackAnalysis;
|
|
5207
|
+
}
|
|
5044
5208
|
throw new Error(result.error);
|
|
5045
5209
|
}
|
|
5046
5210
|
throw new Error(
|
|
@@ -5546,15 +5710,36 @@ function generateSchemasFromParameter(options) {
|
|
|
5546
5710
|
}
|
|
5547
5711
|
function generateSchemasFromReturnType(options) {
|
|
5548
5712
|
const signature = options.context.checker.getSignatureFromDeclaration(options.declaration);
|
|
5549
|
-
const
|
|
5713
|
+
const returnType = signature !== void 0 ? options.context.checker.getReturnTypeOfSignature(signature) : options.context.checker.getTypeAtLocation(options.declaration);
|
|
5714
|
+
const type = unwrapPromiseType(options.context.checker, returnType);
|
|
5715
|
+
const sourceNode = type !== returnType ? unwrapPromiseTypeNode(options.declaration.type) ?? options.declaration.type ?? options.declaration : options.declaration.type ?? options.declaration;
|
|
5550
5716
|
const fallbackName = options.declaration.name !== void 0 && ts7.isIdentifier(options.declaration.name) ? `${options.declaration.name.text}ReturnType` : "ReturnType";
|
|
5551
5717
|
return generateSchemasFromResolvedType({
|
|
5552
5718
|
...options,
|
|
5553
5719
|
type,
|
|
5554
|
-
sourceNode
|
|
5720
|
+
sourceNode,
|
|
5555
5721
|
name: fallbackName
|
|
5556
5722
|
});
|
|
5557
5723
|
}
|
|
5724
|
+
function unwrapPromiseType(checker, type) {
|
|
5725
|
+
if (!("getAwaitedType" in checker) || typeof checker.getAwaitedType !== "function") {
|
|
5726
|
+
return type;
|
|
5727
|
+
}
|
|
5728
|
+
return checker.getAwaitedType(type) ?? type;
|
|
5729
|
+
}
|
|
5730
|
+
function unwrapPromiseTypeNode(typeNode) {
|
|
5731
|
+
if (typeNode === void 0) {
|
|
5732
|
+
return void 0;
|
|
5733
|
+
}
|
|
5734
|
+
if (ts7.isParenthesizedTypeNode(typeNode)) {
|
|
5735
|
+
const unwrapped = unwrapPromiseTypeNode(typeNode.type);
|
|
5736
|
+
return unwrapped ?? typeNode;
|
|
5737
|
+
}
|
|
5738
|
+
return isPromiseTypeReferenceNode(typeNode) ? typeNode.typeArguments[0] : typeNode;
|
|
5739
|
+
}
|
|
5740
|
+
function isPromiseTypeReferenceNode(typeNode) {
|
|
5741
|
+
return ts7.isTypeReferenceNode(typeNode) && ts7.isIdentifier(typeNode.typeName) && typeNode.typeName.text === "Promise" && typeNode.typeArguments !== void 0 && typeNode.typeArguments.length > 0;
|
|
5742
|
+
}
|
|
5558
5743
|
|
|
5559
5744
|
// src/generators/mixed-authoring.ts
|
|
5560
5745
|
function buildMixedAuthoringSchemas(options) {
|