@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/cli.js
CHANGED
|
@@ -3016,6 +3016,21 @@ function isObjectType(type) {
|
|
|
3016
3016
|
function isIntersectionType(type) {
|
|
3017
3017
|
return !!(type.flags & ts3.TypeFlags.Intersection);
|
|
3018
3018
|
}
|
|
3019
|
+
function isResolvableObjectLikeAliasTypeNode(typeNode) {
|
|
3020
|
+
if (ts3.isParenthesizedTypeNode(typeNode)) {
|
|
3021
|
+
return isResolvableObjectLikeAliasTypeNode(typeNode.type);
|
|
3022
|
+
}
|
|
3023
|
+
if (ts3.isTypeLiteralNode(typeNode) || ts3.isTypeReferenceNode(typeNode)) {
|
|
3024
|
+
return true;
|
|
3025
|
+
}
|
|
3026
|
+
return ts3.isIntersectionTypeNode(typeNode) && typeNode.types.length > 0 && typeNode.types.every((member) => isResolvableObjectLikeAliasTypeNode(member));
|
|
3027
|
+
}
|
|
3028
|
+
function isSemanticallyPlainObjectLikeType(type, checker) {
|
|
3029
|
+
if (isIntersectionType(type)) {
|
|
3030
|
+
return type.types.length > 0 && type.types.every((member) => isSemanticallyPlainObjectLikeType(member, checker));
|
|
3031
|
+
}
|
|
3032
|
+
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);
|
|
3033
|
+
}
|
|
3019
3034
|
function isTypeReference(type) {
|
|
3020
3035
|
return !!(type.flags & ts3.TypeFlags.Object) && !!(type.objectFlags & ts3.ObjectFlags.Reference);
|
|
3021
3036
|
}
|
|
@@ -3268,6 +3283,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry,
|
|
|
3268
3283
|
const kindDesc = ts3.SyntaxKind[typeAlias.type.kind] ?? "unknown";
|
|
3269
3284
|
return {
|
|
3270
3285
|
ok: false,
|
|
3286
|
+
kind: "not-object-like",
|
|
3271
3287
|
error: `Type alias "${typeAlias.name.text}" at line ${String(line + 1)} is not an object-like type alias (found ${kindDesc})`
|
|
3272
3288
|
};
|
|
3273
3289
|
}
|
|
@@ -3282,6 +3298,7 @@ function analyzeTypeAliasToIR(typeAlias, checker, file = "", extensionRegistry,
|
|
|
3282
3298
|
const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
|
|
3283
3299
|
return {
|
|
3284
3300
|
ok: false,
|
|
3301
|
+
kind: "duplicate-properties",
|
|
3285
3302
|
error: `Type alias "${name}" at line ${String(line + 1)} contains duplicate property names across object-like members: ${duplicatePropertyNames.join(", ")}`
|
|
3286
3303
|
};
|
|
3287
3304
|
}
|
|
@@ -3978,10 +3995,10 @@ function findDuplicateObjectLikeTypeAliasPropertyNames(members) {
|
|
|
3978
3995
|
return [...duplicates].sort();
|
|
3979
3996
|
}
|
|
3980
3997
|
function getAnalyzableObjectLikePropertyName(name) {
|
|
3981
|
-
if (
|
|
3982
|
-
return
|
|
3998
|
+
if (ts3.isIdentifier(name) || ts3.isStringLiteral(name) || ts3.isNumericLiteral(name)) {
|
|
3999
|
+
return name.text;
|
|
3983
4000
|
}
|
|
3984
|
-
return
|
|
4001
|
+
return null;
|
|
3985
4002
|
}
|
|
3986
4003
|
function applyEnumMemberDisplayNames(type, annotations) {
|
|
3987
4004
|
if (!annotations.some(
|
|
@@ -4191,6 +4208,19 @@ function resolveTypeNode(type, checker, file, typeRegistry, visiting, sourceNode
|
|
|
4191
4208
|
diagnostics
|
|
4192
4209
|
);
|
|
4193
4210
|
}
|
|
4211
|
+
if (resolvedSourceTypeNode !== void 0 && isResolvableObjectLikeAliasTypeNode(resolvedSourceTypeNode) && isSemanticallyPlainObjectLikeType(type, checker)) {
|
|
4212
|
+
return resolveObjectType(
|
|
4213
|
+
type,
|
|
4214
|
+
checker,
|
|
4215
|
+
file,
|
|
4216
|
+
typeRegistry,
|
|
4217
|
+
visiting,
|
|
4218
|
+
sourceNode,
|
|
4219
|
+
metadataPolicy,
|
|
4220
|
+
extensionRegistry,
|
|
4221
|
+
diagnostics
|
|
4222
|
+
);
|
|
4223
|
+
}
|
|
4194
4224
|
}
|
|
4195
4225
|
if (isObjectType(type)) {
|
|
4196
4226
|
return resolveObjectType(
|
|
@@ -5114,6 +5144,95 @@ function findInterfaceByName(sourceFile, interfaceName) {
|
|
|
5114
5144
|
function findTypeAliasByName(sourceFile, aliasName) {
|
|
5115
5145
|
return findNodeByName(sourceFile, aliasName, ts4.isTypeAliasDeclaration, (n) => n.name.text);
|
|
5116
5146
|
}
|
|
5147
|
+
function getResolvedObjectRootType(rootType, typeRegistry) {
|
|
5148
|
+
if (rootType.kind === "object") {
|
|
5149
|
+
return rootType;
|
|
5150
|
+
}
|
|
5151
|
+
if (rootType.kind !== "reference") {
|
|
5152
|
+
return null;
|
|
5153
|
+
}
|
|
5154
|
+
const definition = typeRegistry[rootType.name];
|
|
5155
|
+
return definition?.type.kind === "object" ? definition.type : null;
|
|
5156
|
+
}
|
|
5157
|
+
function createResolvedObjectAliasAnalysis(name, rootType, typeRegistry, rootInfo, diagnostics) {
|
|
5158
|
+
const resolvedRootType = getResolvedObjectRootType(rootType, typeRegistry);
|
|
5159
|
+
if (resolvedRootType === null) {
|
|
5160
|
+
return null;
|
|
5161
|
+
}
|
|
5162
|
+
const fields = resolvedRootType.properties.map((property) => ({
|
|
5163
|
+
kind: "field",
|
|
5164
|
+
name: property.name,
|
|
5165
|
+
...property.metadata !== void 0 && { metadata: property.metadata },
|
|
5166
|
+
type: property.type,
|
|
5167
|
+
required: !property.optional,
|
|
5168
|
+
constraints: property.constraints,
|
|
5169
|
+
annotations: property.annotations,
|
|
5170
|
+
provenance: property.provenance
|
|
5171
|
+
}));
|
|
5172
|
+
return {
|
|
5173
|
+
name,
|
|
5174
|
+
...rootInfo.metadata !== void 0 && { metadata: rootInfo.metadata },
|
|
5175
|
+
fields,
|
|
5176
|
+
fieldLayouts: fields.map(() => ({})),
|
|
5177
|
+
typeRegistry,
|
|
5178
|
+
...rootInfo.annotations.length > 0 && { annotations: [...rootInfo.annotations] },
|
|
5179
|
+
...diagnostics.length > 0 && { diagnostics: [...diagnostics] },
|
|
5180
|
+
instanceMethods: [],
|
|
5181
|
+
staticMethods: []
|
|
5182
|
+
};
|
|
5183
|
+
}
|
|
5184
|
+
function containsTypeReferenceInObjectLikeAlias(typeNode) {
|
|
5185
|
+
if (ts4.isParenthesizedTypeNode(typeNode)) {
|
|
5186
|
+
return containsTypeReferenceInObjectLikeAlias(typeNode.type);
|
|
5187
|
+
}
|
|
5188
|
+
if (ts4.isTypeReferenceNode(typeNode)) {
|
|
5189
|
+
return true;
|
|
5190
|
+
}
|
|
5191
|
+
return ts4.isIntersectionTypeNode(typeNode) && typeNode.types.some((member) => containsTypeReferenceInObjectLikeAlias(member));
|
|
5192
|
+
}
|
|
5193
|
+
function collectFallbackAliasMemberPropertyNames(typeNode, checker) {
|
|
5194
|
+
if (ts4.isParenthesizedTypeNode(typeNode)) {
|
|
5195
|
+
return collectFallbackAliasMemberPropertyNames(typeNode.type, checker);
|
|
5196
|
+
}
|
|
5197
|
+
if (ts4.isTypeLiteralNode(typeNode)) {
|
|
5198
|
+
const propertyNames = [];
|
|
5199
|
+
for (const member of typeNode.members) {
|
|
5200
|
+
if (!ts4.isPropertySignature(member)) {
|
|
5201
|
+
continue;
|
|
5202
|
+
}
|
|
5203
|
+
const propertyName = getAnalyzableObjectLikePropertyName(member.name);
|
|
5204
|
+
if (propertyName !== null) {
|
|
5205
|
+
propertyNames.push(propertyName);
|
|
5206
|
+
}
|
|
5207
|
+
}
|
|
5208
|
+
return propertyNames;
|
|
5209
|
+
}
|
|
5210
|
+
if (ts4.isTypeReferenceNode(typeNode)) {
|
|
5211
|
+
return checker.getTypeFromTypeNode(typeNode).getProperties().map((property) => property.getName());
|
|
5212
|
+
}
|
|
5213
|
+
return null;
|
|
5214
|
+
}
|
|
5215
|
+
function findFallbackAliasDuplicatePropertyNames(typeNode, checker) {
|
|
5216
|
+
if (!ts4.isIntersectionTypeNode(typeNode)) {
|
|
5217
|
+
return [];
|
|
5218
|
+
}
|
|
5219
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5220
|
+
const duplicates = /* @__PURE__ */ new Set();
|
|
5221
|
+
for (const member of typeNode.types) {
|
|
5222
|
+
const propertyNames = collectFallbackAliasMemberPropertyNames(member, checker);
|
|
5223
|
+
if (propertyNames === null) {
|
|
5224
|
+
continue;
|
|
5225
|
+
}
|
|
5226
|
+
for (const propertyName of propertyNames) {
|
|
5227
|
+
if (seen.has(propertyName)) {
|
|
5228
|
+
duplicates.add(propertyName);
|
|
5229
|
+
} else {
|
|
5230
|
+
seen.add(propertyName);
|
|
5231
|
+
}
|
|
5232
|
+
}
|
|
5233
|
+
}
|
|
5234
|
+
return [...duplicates].sort();
|
|
5235
|
+
}
|
|
5117
5236
|
function analyzeNamedTypeToIR(filePath, typeName, extensionRegistry, metadataPolicy, discriminatorOptions) {
|
|
5118
5237
|
const ctx = createProgramContext(filePath);
|
|
5119
5238
|
return analyzeNamedTypeToIRFromProgramContext(
|
|
@@ -5162,6 +5281,51 @@ function analyzeNamedTypeToIRFromProgramContext(ctx, filePath, typeName, extensi
|
|
|
5162
5281
|
if (result.ok) {
|
|
5163
5282
|
return result.analysis;
|
|
5164
5283
|
}
|
|
5284
|
+
const fallbackEligible = result.kind === "not-object-like" && isResolvableObjectLikeAliasTypeNode(typeAlias.type) && containsTypeReferenceInObjectLikeAlias(typeAlias.type);
|
|
5285
|
+
if (!fallbackEligible) {
|
|
5286
|
+
throw new Error(result.error);
|
|
5287
|
+
}
|
|
5288
|
+
const duplicatePropertyNames = findFallbackAliasDuplicatePropertyNames(
|
|
5289
|
+
typeAlias.type,
|
|
5290
|
+
ctx.checker
|
|
5291
|
+
);
|
|
5292
|
+
if (duplicatePropertyNames.length > 0) {
|
|
5293
|
+
const sourceFile = typeAlias.getSourceFile();
|
|
5294
|
+
const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
|
|
5295
|
+
throw new Error(
|
|
5296
|
+
`Type alias "${typeAlias.name.text}" at line ${String(line + 1)} contains duplicate property names across object-like members: ${duplicatePropertyNames.join(", ")}`
|
|
5297
|
+
);
|
|
5298
|
+
}
|
|
5299
|
+
const rootInfo = analyzeDeclarationRootInfo(
|
|
5300
|
+
typeAlias,
|
|
5301
|
+
ctx.checker,
|
|
5302
|
+
analysisFilePath,
|
|
5303
|
+
extensionRegistry,
|
|
5304
|
+
metadataPolicy
|
|
5305
|
+
);
|
|
5306
|
+
const diagnostics = [...rootInfo.diagnostics];
|
|
5307
|
+
const typeRegistry = {};
|
|
5308
|
+
const rootType = resolveTypeNode(
|
|
5309
|
+
ctx.checker.getTypeAtLocation(typeAlias),
|
|
5310
|
+
ctx.checker,
|
|
5311
|
+
analysisFilePath,
|
|
5312
|
+
typeRegistry,
|
|
5313
|
+
/* @__PURE__ */ new Set(),
|
|
5314
|
+
typeAlias,
|
|
5315
|
+
createAnalyzerMetadataPolicy(metadataPolicy, discriminatorOptions),
|
|
5316
|
+
extensionRegistry,
|
|
5317
|
+
diagnostics
|
|
5318
|
+
);
|
|
5319
|
+
const fallbackAnalysis = createResolvedObjectAliasAnalysis(
|
|
5320
|
+
typeAlias.name.text,
|
|
5321
|
+
rootType,
|
|
5322
|
+
typeRegistry,
|
|
5323
|
+
rootInfo,
|
|
5324
|
+
diagnostics
|
|
5325
|
+
);
|
|
5326
|
+
if (fallbackAnalysis !== null) {
|
|
5327
|
+
return fallbackAnalysis;
|
|
5328
|
+
}
|
|
5165
5329
|
throw new Error(result.error);
|
|
5166
5330
|
}
|
|
5167
5331
|
throw new Error(
|
|
@@ -5704,15 +5868,36 @@ function generateSchemasFromParameter(options) {
|
|
|
5704
5868
|
}
|
|
5705
5869
|
function generateSchemasFromReturnType(options) {
|
|
5706
5870
|
const signature = options.context.checker.getSignatureFromDeclaration(options.declaration);
|
|
5707
|
-
const
|
|
5871
|
+
const returnType = signature !== void 0 ? options.context.checker.getReturnTypeOfSignature(signature) : options.context.checker.getTypeAtLocation(options.declaration);
|
|
5872
|
+
const type = unwrapPromiseType(options.context.checker, returnType);
|
|
5873
|
+
const sourceNode = type !== returnType ? unwrapPromiseTypeNode(options.declaration.type) ?? options.declaration.type ?? options.declaration : options.declaration.type ?? options.declaration;
|
|
5708
5874
|
const fallbackName = options.declaration.name !== void 0 && ts7.isIdentifier(options.declaration.name) ? `${options.declaration.name.text}ReturnType` : "ReturnType";
|
|
5709
5875
|
return generateSchemasFromResolvedType({
|
|
5710
5876
|
...options,
|
|
5711
5877
|
type,
|
|
5712
|
-
sourceNode
|
|
5878
|
+
sourceNode,
|
|
5713
5879
|
name: fallbackName
|
|
5714
5880
|
});
|
|
5715
5881
|
}
|
|
5882
|
+
function unwrapPromiseType(checker, type) {
|
|
5883
|
+
if (!("getAwaitedType" in checker) || typeof checker.getAwaitedType !== "function") {
|
|
5884
|
+
return type;
|
|
5885
|
+
}
|
|
5886
|
+
return checker.getAwaitedType(type) ?? type;
|
|
5887
|
+
}
|
|
5888
|
+
function unwrapPromiseTypeNode(typeNode) {
|
|
5889
|
+
if (typeNode === void 0) {
|
|
5890
|
+
return void 0;
|
|
5891
|
+
}
|
|
5892
|
+
if (ts7.isParenthesizedTypeNode(typeNode)) {
|
|
5893
|
+
const unwrapped = unwrapPromiseTypeNode(typeNode.type);
|
|
5894
|
+
return unwrapped ?? typeNode;
|
|
5895
|
+
}
|
|
5896
|
+
return isPromiseTypeReferenceNode(typeNode) ? typeNode.typeArguments[0] : typeNode;
|
|
5897
|
+
}
|
|
5898
|
+
function isPromiseTypeReferenceNode(typeNode) {
|
|
5899
|
+
return ts7.isTypeReferenceNode(typeNode) && ts7.isIdentifier(typeNode.typeName) && typeNode.typeName.text === "Promise" && typeNode.typeArguments !== void 0 && typeNode.typeArguments.length > 0;
|
|
5900
|
+
}
|
|
5716
5901
|
var init_discovered_schema = __esm({
|
|
5717
5902
|
"src/generators/discovered-schema.ts"() {
|
|
5718
5903
|
"use strict";
|