@formspec/build 0.1.0-alpha.1 → 0.1.0-alpha.11
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 +138 -0
- package/dist/__tests__/analyzer-edge-cases.test.d.ts +13 -0
- package/dist/__tests__/analyzer-edge-cases.test.d.ts.map +1 -0
- package/dist/__tests__/analyzer.test.d.ts +5 -0
- package/dist/__tests__/analyzer.test.d.ts.map +1 -0
- package/dist/__tests__/codegen.test.d.ts +5 -0
- package/dist/__tests__/codegen.test.d.ts.map +1 -0
- package/dist/__tests__/decorator-pipeline.test.d.ts +11 -0
- package/dist/__tests__/decorator-pipeline.test.d.ts.map +1 -0
- package/dist/__tests__/fixtures/edge-cases.d.ts +110 -0
- package/dist/__tests__/fixtures/edge-cases.d.ts.map +1 -0
- package/dist/__tests__/fixtures/example-a-builtins.d.ts +12 -0
- package/dist/__tests__/fixtures/example-a-builtins.d.ts.map +1 -0
- package/dist/__tests__/fixtures/example-b-decorators.d.ts +5 -0
- package/dist/__tests__/fixtures/example-b-decorators.d.ts.map +1 -0
- package/dist/__tests__/fixtures/example-b-extended.d.ts +5 -0
- package/dist/__tests__/fixtures/example-b-extended.d.ts.map +1 -0
- package/dist/__tests__/fixtures/example-c-custom.d.ts +5 -0
- package/dist/__tests__/fixtures/example-c-custom.d.ts.map +1 -0
- package/dist/__tests__/fixtures/example-c-decorators.d.ts +5 -0
- package/dist/__tests__/fixtures/example-c-decorators.d.ts.map +1 -0
- package/dist/__tests__/fixtures/example-d-mixed-decorators.d.ts +6 -0
- package/dist/__tests__/fixtures/example-d-mixed-decorators.d.ts.map +1 -0
- package/dist/__tests__/fixtures/example-e-decorators.d.ts +11 -0
- package/dist/__tests__/fixtures/example-e-decorators.d.ts.map +1 -0
- package/dist/__tests__/fixtures/example-e-no-namespace.d.ts +5 -0
- package/dist/__tests__/fixtures/example-e-no-namespace.d.ts.map +1 -0
- package/dist/__tests__/fixtures/example-interface-types.d.ts +102 -0
- package/dist/__tests__/fixtures/example-interface-types.d.ts.map +1 -0
- package/dist/__tests__/fixtures/example-jsdoc-constraints.d.ts +16 -0
- package/dist/__tests__/fixtures/example-jsdoc-constraints.d.ts.map +1 -0
- package/dist/__tests__/fixtures/example-nested-class.d.ts +45 -0
- package/dist/__tests__/fixtures/example-nested-class.d.ts.map +1 -0
- package/dist/__tests__/fixtures/sample-forms.d.ts +55 -0
- package/dist/__tests__/fixtures/sample-forms.d.ts.map +1 -0
- package/dist/__tests__/interface-types.test.d.ts +11 -0
- package/dist/__tests__/interface-types.test.d.ts.map +1 -0
- package/dist/__tests__/jsdoc-constraints.test.d.ts +10 -0
- package/dist/__tests__/jsdoc-constraints.test.d.ts.map +1 -0
- package/dist/analyzer/class-analyzer.d.ts +139 -0
- package/dist/analyzer/class-analyzer.d.ts.map +1 -0
- package/dist/analyzer/decorator-extractor.d.ts +78 -0
- package/dist/analyzer/decorator-extractor.d.ts.map +1 -0
- package/dist/analyzer/jsdoc-constraints.d.ts +47 -0
- package/dist/analyzer/jsdoc-constraints.d.ts.map +1 -0
- package/dist/analyzer/program.d.ts +53 -0
- package/dist/analyzer/program.d.ts.map +1 -0
- package/dist/analyzer/type-converter.d.ts +75 -0
- package/dist/analyzer/type-converter.d.ts.map +1 -0
- package/dist/browser.cjs +549 -0
- package/dist/browser.cjs.map +1 -0
- package/dist/browser.d.ts +56 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +501 -0
- package/dist/browser.js.map +1 -0
- package/dist/build.d.ts +890 -0
- package/dist/cli.cjs +2267 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.js +2204 -103
- package/dist/cli.js.map +1 -1
- package/dist/codegen/index.d.ts +75 -0
- package/dist/codegen/index.d.ts.map +1 -0
- package/dist/generators/class-schema.d.ts +114 -0
- package/dist/generators/class-schema.d.ts.map +1 -0
- package/dist/generators/method-schema.d.ts +67 -0
- package/dist/generators/method-schema.d.ts.map +1 -0
- package/dist/index.cjs +2093 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +11 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2024 -104
- package/dist/index.js.map +1 -1
- package/dist/internals.cjs +1345 -0
- package/dist/internals.cjs.map +1 -0
- package/dist/internals.d.ts +21 -0
- package/dist/internals.d.ts.map +1 -0
- package/dist/internals.js +1298 -0
- package/dist/internals.js.map +1 -0
- package/dist/json-schema/generator.d.ts.map +1 -1
- package/dist/json-schema/schema.d.ts +16 -0
- package/dist/json-schema/schema.d.ts.map +1 -0
- package/dist/json-schema/types.d.ts +28 -0
- package/dist/json-schema/types.d.ts.map +1 -1
- package/dist/ui-schema/generator.d.ts +15 -0
- package/dist/ui-schema/generator.d.ts.map +1 -1
- package/dist/ui-schema/schema.d.ts +357 -0
- package/dist/ui-schema/schema.d.ts.map +1 -0
- package/dist/ui-schema/types.d.ts +8 -73
- package/dist/ui-schema/types.d.ts.map +1 -1
- package/package.json +25 -7
- package/dist/__tests__/cli.test.js +0 -178
- package/dist/__tests__/cli.test.js.map +0 -1
- package/dist/__tests__/edge-cases.test.js +0 -209
- package/dist/__tests__/edge-cases.test.js.map +0 -1
- package/dist/__tests__/generator.test.js +0 -208
- package/dist/__tests__/generator.test.js.map +0 -1
- package/dist/__tests__/integration.test.js +0 -163
- package/dist/__tests__/integration.test.js.map +0 -1
- package/dist/__tests__/write-schemas.test.js +0 -196
- package/dist/__tests__/write-schemas.test.js.map +0 -1
- package/dist/json-schema/generator.js +0 -146
- package/dist/json-schema/generator.js.map +0 -1
- package/dist/json-schema/types.js +0 -7
- package/dist/json-schema/types.js.map +0 -1
- package/dist/ui-schema/generator.js +0 -150
- package/dist/ui-schema/generator.js.map +0 -1
- package/dist/ui-schema/types.js +0 -8
- package/dist/ui-schema/types.js.map +0 -1
|
@@ -0,0 +1,1345 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/internals.ts
|
|
31
|
+
var internals_exports = {};
|
|
32
|
+
__export(internals_exports, {
|
|
33
|
+
analyzeClass: () => analyzeClass,
|
|
34
|
+
analyzeInterface: () => analyzeInterface,
|
|
35
|
+
analyzeTypeAlias: () => analyzeTypeAlias,
|
|
36
|
+
collectFormSpecReferences: () => collectFormSpecReferences,
|
|
37
|
+
createProgramContext: () => createProgramContext,
|
|
38
|
+
findClassByName: () => findClassByName,
|
|
39
|
+
findInterfaceByName: () => findInterfaceByName,
|
|
40
|
+
findTypeAliasByName: () => findTypeAliasByName,
|
|
41
|
+
generateClassSchemas: () => generateClassSchemas,
|
|
42
|
+
generateMethodSchemas: () => generateMethodSchemas,
|
|
43
|
+
generateUiSchemaFromFields: () => generateUiSchemaFromFields
|
|
44
|
+
});
|
|
45
|
+
module.exports = __toCommonJS(internals_exports);
|
|
46
|
+
|
|
47
|
+
// src/analyzer/program.ts
|
|
48
|
+
var ts = __toESM(require("typescript"), 1);
|
|
49
|
+
var path = __toESM(require("path"), 1);
|
|
50
|
+
function createProgramContext(filePath) {
|
|
51
|
+
const absolutePath = path.resolve(filePath);
|
|
52
|
+
const fileDir = path.dirname(absolutePath);
|
|
53
|
+
const configPath = ts.findConfigFile(fileDir, ts.sys.fileExists.bind(ts.sys), "tsconfig.json");
|
|
54
|
+
let compilerOptions;
|
|
55
|
+
let fileNames;
|
|
56
|
+
if (configPath) {
|
|
57
|
+
const configFile = ts.readConfigFile(configPath, ts.sys.readFile.bind(ts.sys));
|
|
58
|
+
if (configFile.error) {
|
|
59
|
+
throw new Error(
|
|
60
|
+
`Error reading tsconfig.json: ${ts.flattenDiagnosticMessageText(configFile.error.messageText, "\n")}`
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
const parsed = ts.parseJsonConfigFileContent(
|
|
64
|
+
configFile.config,
|
|
65
|
+
ts.sys,
|
|
66
|
+
path.dirname(configPath)
|
|
67
|
+
);
|
|
68
|
+
if (parsed.errors.length > 0) {
|
|
69
|
+
const errorMessages = parsed.errors.map((e) => ts.flattenDiagnosticMessageText(e.messageText, "\n")).join("\n");
|
|
70
|
+
throw new Error(`Error parsing tsconfig.json: ${errorMessages}`);
|
|
71
|
+
}
|
|
72
|
+
compilerOptions = parsed.options;
|
|
73
|
+
fileNames = parsed.fileNames.includes(absolutePath) ? parsed.fileNames : [...parsed.fileNames, absolutePath];
|
|
74
|
+
} else {
|
|
75
|
+
compilerOptions = {
|
|
76
|
+
target: ts.ScriptTarget.ES2022,
|
|
77
|
+
module: ts.ModuleKind.NodeNext,
|
|
78
|
+
moduleResolution: ts.ModuleResolutionKind.NodeNext,
|
|
79
|
+
strict: true,
|
|
80
|
+
skipLibCheck: true,
|
|
81
|
+
declaration: true
|
|
82
|
+
};
|
|
83
|
+
fileNames = [absolutePath];
|
|
84
|
+
}
|
|
85
|
+
const program = ts.createProgram(fileNames, compilerOptions);
|
|
86
|
+
const sourceFile = program.getSourceFile(absolutePath);
|
|
87
|
+
if (!sourceFile) {
|
|
88
|
+
throw new Error(`Could not find source file: ${absolutePath}`);
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
program,
|
|
92
|
+
checker: program.getTypeChecker(),
|
|
93
|
+
sourceFile
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function findNodeByName(sourceFile, name, predicate, getName) {
|
|
97
|
+
let result = null;
|
|
98
|
+
function visit(node) {
|
|
99
|
+
if (result) return;
|
|
100
|
+
if (predicate(node) && getName(node) === name) {
|
|
101
|
+
result = node;
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
ts.forEachChild(node, visit);
|
|
105
|
+
}
|
|
106
|
+
visit(sourceFile);
|
|
107
|
+
return result;
|
|
108
|
+
}
|
|
109
|
+
function findClassByName(sourceFile, className) {
|
|
110
|
+
return findNodeByName(sourceFile, className, ts.isClassDeclaration, (n) => n.name?.text);
|
|
111
|
+
}
|
|
112
|
+
function findInterfaceByName(sourceFile, interfaceName) {
|
|
113
|
+
return findNodeByName(sourceFile, interfaceName, ts.isInterfaceDeclaration, (n) => n.name.text);
|
|
114
|
+
}
|
|
115
|
+
function findTypeAliasByName(sourceFile, aliasName) {
|
|
116
|
+
return findNodeByName(sourceFile, aliasName, ts.isTypeAliasDeclaration, (n) => n.name.text);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// src/analyzer/class-analyzer.ts
|
|
120
|
+
var ts4 = __toESM(require("typescript"), 1);
|
|
121
|
+
|
|
122
|
+
// src/analyzer/decorator-extractor.ts
|
|
123
|
+
var ts2 = __toESM(require("typescript"), 1);
|
|
124
|
+
var import_core = require("@formspec/core");
|
|
125
|
+
function extractDecorators(member) {
|
|
126
|
+
const decorators = [];
|
|
127
|
+
const modifiers = ts2.canHaveDecorators(member) ? ts2.getDecorators(member) : void 0;
|
|
128
|
+
if (!modifiers) return decorators;
|
|
129
|
+
for (const decorator of modifiers) {
|
|
130
|
+
const info = parseDecorator(decorator);
|
|
131
|
+
if (info) {
|
|
132
|
+
decorators.push(info);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return decorators;
|
|
136
|
+
}
|
|
137
|
+
function parseDecorator(decorator) {
|
|
138
|
+
const expr = decorator.expression;
|
|
139
|
+
if (ts2.isIdentifier(expr)) {
|
|
140
|
+
return {
|
|
141
|
+
name: expr.text,
|
|
142
|
+
args: [],
|
|
143
|
+
node: decorator
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
if (ts2.isCallExpression(expr)) {
|
|
147
|
+
const callee = expr.expression;
|
|
148
|
+
let name = null;
|
|
149
|
+
if (ts2.isIdentifier(callee)) {
|
|
150
|
+
name = callee.text;
|
|
151
|
+
} else if (ts2.isPropertyAccessExpression(callee)) {
|
|
152
|
+
name = callee.name.text;
|
|
153
|
+
}
|
|
154
|
+
if (!name) return null;
|
|
155
|
+
const args = expr.arguments.map(extractArgValue);
|
|
156
|
+
return {
|
|
157
|
+
name,
|
|
158
|
+
args,
|
|
159
|
+
node: decorator
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
function extractArgValue(node) {
|
|
165
|
+
if (ts2.isStringLiteral(node)) {
|
|
166
|
+
return node.text;
|
|
167
|
+
}
|
|
168
|
+
if (ts2.isNumericLiteral(node)) {
|
|
169
|
+
return Number(node.text);
|
|
170
|
+
}
|
|
171
|
+
if (node.kind === ts2.SyntaxKind.TrueKeyword) {
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
if (node.kind === ts2.SyntaxKind.FalseKeyword) {
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
if (node.kind === ts2.SyntaxKind.NullKeyword) {
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
if (ts2.isPrefixUnaryExpression(node)) {
|
|
181
|
+
if (node.operator === ts2.SyntaxKind.MinusToken && ts2.isNumericLiteral(node.operand)) {
|
|
182
|
+
return -Number(node.operand.text);
|
|
183
|
+
}
|
|
184
|
+
if (node.operator === ts2.SyntaxKind.PlusToken && ts2.isNumericLiteral(node.operand)) {
|
|
185
|
+
return Number(node.operand.text);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (ts2.isArrayLiteralExpression(node)) {
|
|
189
|
+
return node.elements.map((el) => {
|
|
190
|
+
if (ts2.isSpreadElement(el)) {
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
return extractArgValue(el);
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
if (ts2.isObjectLiteralExpression(node)) {
|
|
197
|
+
const obj = {};
|
|
198
|
+
for (const prop of node.properties) {
|
|
199
|
+
if (ts2.isPropertyAssignment(prop)) {
|
|
200
|
+
const key = getPropertyName(prop.name);
|
|
201
|
+
if (key) {
|
|
202
|
+
obj[key] = extractArgValue(prop.initializer);
|
|
203
|
+
}
|
|
204
|
+
} else if (ts2.isShorthandPropertyAssignment(prop)) {
|
|
205
|
+
const key = prop.name.text;
|
|
206
|
+
obj[key] = null;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return obj;
|
|
210
|
+
}
|
|
211
|
+
if (ts2.isNoSubstitutionTemplateLiteral(node)) {
|
|
212
|
+
return node.text;
|
|
213
|
+
}
|
|
214
|
+
if (ts2.isRegularExpressionLiteral(node)) {
|
|
215
|
+
const regexText = node.text;
|
|
216
|
+
const lastSlash = regexText.lastIndexOf("/");
|
|
217
|
+
if (lastSlash > 0) {
|
|
218
|
+
return regexText.substring(1, lastSlash);
|
|
219
|
+
}
|
|
220
|
+
return regexText;
|
|
221
|
+
}
|
|
222
|
+
if (ts2.isNewExpression(node)) {
|
|
223
|
+
if (ts2.isIdentifier(node.expression) && node.expression.text === "RegExp" && node.arguments && node.arguments.length > 0) {
|
|
224
|
+
const firstArg = node.arguments[0];
|
|
225
|
+
if (firstArg && ts2.isStringLiteral(firstArg)) {
|
|
226
|
+
return firstArg.text;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
if (ts2.isIdentifier(node)) {
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
function getPropertyName(name) {
|
|
236
|
+
if (ts2.isIdentifier(name)) {
|
|
237
|
+
return name.text;
|
|
238
|
+
}
|
|
239
|
+
if (ts2.isStringLiteral(name)) {
|
|
240
|
+
return name.text;
|
|
241
|
+
}
|
|
242
|
+
if (ts2.isNumericLiteral(name)) {
|
|
243
|
+
return name.text;
|
|
244
|
+
}
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
var FORMSPEC_DECORATORS = {
|
|
248
|
+
// Display metadata
|
|
249
|
+
Field: { argTypes: ["object"] },
|
|
250
|
+
// Grouping
|
|
251
|
+
Group: { argTypes: ["string"] },
|
|
252
|
+
// Conditional display
|
|
253
|
+
ShowWhen: { argTypes: ["object"] },
|
|
254
|
+
// Enum options
|
|
255
|
+
EnumOptions: { argTypes: ["array"] },
|
|
256
|
+
// Numeric constraints
|
|
257
|
+
Minimum: { argTypes: ["number"] },
|
|
258
|
+
Maximum: { argTypes: ["number"] },
|
|
259
|
+
ExclusiveMinimum: { argTypes: ["number"] },
|
|
260
|
+
ExclusiveMaximum: { argTypes: ["number"] },
|
|
261
|
+
// String constraints
|
|
262
|
+
MinLength: { argTypes: ["number"] },
|
|
263
|
+
MaxLength: { argTypes: ["number"] },
|
|
264
|
+
Pattern: { argTypes: ["string"] }
|
|
265
|
+
};
|
|
266
|
+
function isFormSpecDecoratorsPath(fileName) {
|
|
267
|
+
const normalized = fileName.replace(/\\/g, "/");
|
|
268
|
+
return normalized.includes("node_modules/@formspec/decorators") || normalized.includes("/packages/decorators/");
|
|
269
|
+
}
|
|
270
|
+
function resolveDecorator(decorator, checker) {
|
|
271
|
+
const expr = decorator.expression;
|
|
272
|
+
let targetNode;
|
|
273
|
+
let name;
|
|
274
|
+
if (ts2.isIdentifier(expr)) {
|
|
275
|
+
targetNode = expr;
|
|
276
|
+
name = expr.text;
|
|
277
|
+
} else if (ts2.isCallExpression(expr)) {
|
|
278
|
+
if (ts2.isIdentifier(expr.expression)) {
|
|
279
|
+
targetNode = expr.expression;
|
|
280
|
+
name = expr.expression.text;
|
|
281
|
+
} else {
|
|
282
|
+
return null;
|
|
283
|
+
}
|
|
284
|
+
} else {
|
|
285
|
+
return null;
|
|
286
|
+
}
|
|
287
|
+
if (name in FORMSPEC_DECORATORS) {
|
|
288
|
+
const symbol = checker.getSymbolAtLocation(targetNode);
|
|
289
|
+
if (symbol) {
|
|
290
|
+
const declarations = symbol.declarations;
|
|
291
|
+
if (declarations && declarations.length > 0) {
|
|
292
|
+
const decl = declarations[0];
|
|
293
|
+
if (decl) {
|
|
294
|
+
const sourceFile = decl.getSourceFile();
|
|
295
|
+
const fileName = sourceFile.fileName;
|
|
296
|
+
if (isFormSpecDecoratorsPath(fileName)) {
|
|
297
|
+
return {
|
|
298
|
+
name,
|
|
299
|
+
isFormSpec: true,
|
|
300
|
+
isMarker: !ts2.isCallExpression(expr)
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
const resolvedSymbol = checker.getSymbolAtLocation(targetNode);
|
|
308
|
+
if (!resolvedSymbol) return null;
|
|
309
|
+
const type = checker.getTypeOfSymbol(resolvedSymbol);
|
|
310
|
+
const props = type.getProperties();
|
|
311
|
+
let extendsBuiltin;
|
|
312
|
+
let extensionName;
|
|
313
|
+
let isMarker = false;
|
|
314
|
+
for (const prop of props) {
|
|
315
|
+
const escapedName = prop.getEscapedName();
|
|
316
|
+
if (escapedName.startsWith("__@") && (escapedName.includes("formspec.extends") || escapedName.includes("FORMSPEC_EXTENDS"))) {
|
|
317
|
+
const propType = checker.getTypeOfSymbol(prop);
|
|
318
|
+
if (propType.isStringLiteral()) {
|
|
319
|
+
extendsBuiltin = propType.value;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
if (escapedName.startsWith("__@") && (escapedName.includes("formspec.extension") || escapedName.includes("FORMSPEC_EXTENSION"))) {
|
|
323
|
+
const propType = checker.getTypeOfSymbol(prop);
|
|
324
|
+
if (propType.isStringLiteral()) {
|
|
325
|
+
extensionName = propType.value;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
if (escapedName.startsWith("__@") && (escapedName.includes("formspec.marker") || escapedName.includes("FORMSPEC_MARKER"))) {
|
|
329
|
+
isMarker = true;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
if (extendsBuiltin) {
|
|
333
|
+
return {
|
|
334
|
+
name,
|
|
335
|
+
extendsBuiltin,
|
|
336
|
+
isFormSpec: true,
|
|
337
|
+
isMarker: false
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
if (extensionName) {
|
|
341
|
+
return {
|
|
342
|
+
name,
|
|
343
|
+
extensionName,
|
|
344
|
+
isFormSpec: true,
|
|
345
|
+
isMarker
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
if (isMarker) {
|
|
349
|
+
return {
|
|
350
|
+
name,
|
|
351
|
+
isFormSpec: true,
|
|
352
|
+
isMarker: true
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
return null;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// src/analyzer/jsdoc-constraints.ts
|
|
359
|
+
var ts3 = __toESM(require("typescript"), 1);
|
|
360
|
+
var import_core2 = require("@formspec/core");
|
|
361
|
+
function extractJSDocConstraints(node) {
|
|
362
|
+
const results = [];
|
|
363
|
+
const jsDocTags = ts3.getJSDocTags(node);
|
|
364
|
+
for (const tag of jsDocTags) {
|
|
365
|
+
const tagName = tag.tagName.text;
|
|
366
|
+
if (!(tagName in import_core2.CONSTRAINT_TAG_DEFINITIONS)) {
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
369
|
+
const constraintName = tagName;
|
|
370
|
+
const expectedType = import_core2.CONSTRAINT_TAG_DEFINITIONS[constraintName];
|
|
371
|
+
const commentText = getTagCommentText(tag);
|
|
372
|
+
if (commentText === void 0 || commentText === "") {
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
const trimmed = commentText.trim();
|
|
376
|
+
if (trimmed === "") {
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
if (expectedType === "number") {
|
|
380
|
+
const value = Number(trimmed);
|
|
381
|
+
if (Number.isNaN(value)) {
|
|
382
|
+
continue;
|
|
383
|
+
}
|
|
384
|
+
results.push(createSyntheticDecorator(constraintName, value));
|
|
385
|
+
} else if (expectedType === "json") {
|
|
386
|
+
try {
|
|
387
|
+
const parsed = JSON.parse(trimmed);
|
|
388
|
+
if (!Array.isArray(parsed)) {
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
results.push(createSyntheticDecorator(constraintName, parsed));
|
|
392
|
+
} catch {
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
395
|
+
} else {
|
|
396
|
+
results.push(createSyntheticDecorator(constraintName, trimmed));
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
return results;
|
|
400
|
+
}
|
|
401
|
+
function extractJSDocFieldMetadata(node) {
|
|
402
|
+
const jsDocTags = ts3.getJSDocTags(node);
|
|
403
|
+
let displayName;
|
|
404
|
+
let description;
|
|
405
|
+
for (const tag of jsDocTags) {
|
|
406
|
+
const tagName = tag.tagName.text;
|
|
407
|
+
const commentText = getTagCommentText(tag);
|
|
408
|
+
if (commentText === void 0 || commentText.trim() === "") {
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
411
|
+
const trimmed = commentText.trim();
|
|
412
|
+
if (tagName === "Field_displayName") {
|
|
413
|
+
displayName = trimmed;
|
|
414
|
+
} else if (tagName === "Field_description") {
|
|
415
|
+
description = trimmed;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
if (displayName === void 0 && description === void 0) {
|
|
419
|
+
return null;
|
|
420
|
+
}
|
|
421
|
+
const fieldOpts = {
|
|
422
|
+
...displayName !== void 0 ? { displayName } : {},
|
|
423
|
+
...description !== void 0 ? { description } : {}
|
|
424
|
+
};
|
|
425
|
+
return createSyntheticDecorator("Field", fieldOpts);
|
|
426
|
+
}
|
|
427
|
+
function getTagCommentText(tag) {
|
|
428
|
+
if (tag.comment === void 0) {
|
|
429
|
+
return void 0;
|
|
430
|
+
}
|
|
431
|
+
if (typeof tag.comment === "string") {
|
|
432
|
+
return tag.comment;
|
|
433
|
+
}
|
|
434
|
+
return ts3.getTextOfJSDocComment(tag.comment);
|
|
435
|
+
}
|
|
436
|
+
function createSyntheticDecorator(name, value) {
|
|
437
|
+
return {
|
|
438
|
+
name,
|
|
439
|
+
args: [value],
|
|
440
|
+
node: void 0
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// src/analyzer/class-analyzer.ts
|
|
445
|
+
function analyzeClass(classDecl, checker) {
|
|
446
|
+
const name = classDecl.name?.text ?? "AnonymousClass";
|
|
447
|
+
const fields = [];
|
|
448
|
+
const instanceMethods = [];
|
|
449
|
+
const staticMethods = [];
|
|
450
|
+
for (const member of classDecl.members) {
|
|
451
|
+
if (ts4.isPropertyDeclaration(member)) {
|
|
452
|
+
const fieldInfo = analyzeField(member, checker);
|
|
453
|
+
if (fieldInfo) {
|
|
454
|
+
fields.push(fieldInfo);
|
|
455
|
+
}
|
|
456
|
+
} else if (ts4.isMethodDeclaration(member)) {
|
|
457
|
+
const methodInfo = analyzeMethod(member, checker);
|
|
458
|
+
if (methodInfo) {
|
|
459
|
+
const isStatic = member.modifiers?.some((m) => m.kind === ts4.SyntaxKind.StaticKeyword);
|
|
460
|
+
if (isStatic) {
|
|
461
|
+
staticMethods.push(methodInfo);
|
|
462
|
+
} else {
|
|
463
|
+
instanceMethods.push(methodInfo);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
return {
|
|
469
|
+
name,
|
|
470
|
+
fields,
|
|
471
|
+
instanceMethods,
|
|
472
|
+
staticMethods
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
function analyzeField(prop, checker) {
|
|
476
|
+
if (!ts4.isIdentifier(prop.name)) {
|
|
477
|
+
return null;
|
|
478
|
+
}
|
|
479
|
+
const name = prop.name.text;
|
|
480
|
+
const typeNode = prop.type;
|
|
481
|
+
const type = checker.getTypeAtLocation(prop);
|
|
482
|
+
const optional = prop.questionToken !== void 0;
|
|
483
|
+
const decorators = extractDecorators(prop);
|
|
484
|
+
for (const dec of decorators) {
|
|
485
|
+
if (dec.node) {
|
|
486
|
+
const resolved = resolveDecorator(dec.node, checker);
|
|
487
|
+
if (resolved) {
|
|
488
|
+
dec.resolved = resolved;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
if (prop.type) {
|
|
493
|
+
const aliasConstraints = extractTypeAliasConstraints(prop.type, checker);
|
|
494
|
+
decorators.push(...aliasConstraints);
|
|
495
|
+
}
|
|
496
|
+
const jsdocConstraints = extractJSDocConstraints(prop);
|
|
497
|
+
decorators.push(...jsdocConstraints);
|
|
498
|
+
const deprecated = hasDeprecatedTag(prop);
|
|
499
|
+
const defaultValue = extractDefaultValue(prop.initializer);
|
|
500
|
+
return {
|
|
501
|
+
name,
|
|
502
|
+
typeNode,
|
|
503
|
+
type,
|
|
504
|
+
optional,
|
|
505
|
+
decorators,
|
|
506
|
+
deprecated,
|
|
507
|
+
defaultValue
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
function extractTypeAliasConstraints(typeNode, checker) {
|
|
511
|
+
if (!ts4.isTypeReferenceNode(typeNode)) return [];
|
|
512
|
+
const symbol = checker.getSymbolAtLocation(typeNode.typeName);
|
|
513
|
+
if (!symbol?.declarations) return [];
|
|
514
|
+
const aliasDecl = symbol.declarations.find(ts4.isTypeAliasDeclaration);
|
|
515
|
+
if (!aliasDecl) return [];
|
|
516
|
+
if (ts4.isTypeLiteralNode(aliasDecl.type)) return [];
|
|
517
|
+
return extractJSDocConstraints(aliasDecl);
|
|
518
|
+
}
|
|
519
|
+
function hasDeprecatedTag(node) {
|
|
520
|
+
const jsDocTags = ts4.getJSDocTags(node);
|
|
521
|
+
return jsDocTags.some((tag) => tag.tagName.text === "deprecated");
|
|
522
|
+
}
|
|
523
|
+
function extractDefaultValue(initializer) {
|
|
524
|
+
if (!initializer) return void 0;
|
|
525
|
+
if (ts4.isStringLiteral(initializer)) {
|
|
526
|
+
return initializer.text;
|
|
527
|
+
}
|
|
528
|
+
if (ts4.isNumericLiteral(initializer)) {
|
|
529
|
+
return Number(initializer.text);
|
|
530
|
+
}
|
|
531
|
+
if (initializer.kind === ts4.SyntaxKind.TrueKeyword) {
|
|
532
|
+
return true;
|
|
533
|
+
}
|
|
534
|
+
if (initializer.kind === ts4.SyntaxKind.FalseKeyword) {
|
|
535
|
+
return false;
|
|
536
|
+
}
|
|
537
|
+
if (initializer.kind === ts4.SyntaxKind.NullKeyword) {
|
|
538
|
+
return null;
|
|
539
|
+
}
|
|
540
|
+
if (ts4.isPrefixUnaryExpression(initializer)) {
|
|
541
|
+
if (initializer.operator === ts4.SyntaxKind.MinusToken && ts4.isNumericLiteral(initializer.operand)) {
|
|
542
|
+
return -Number(initializer.operand.text);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
return void 0;
|
|
546
|
+
}
|
|
547
|
+
function analyzeMethod(method, checker) {
|
|
548
|
+
if (!ts4.isIdentifier(method.name)) {
|
|
549
|
+
return null;
|
|
550
|
+
}
|
|
551
|
+
const name = method.name.text;
|
|
552
|
+
const parameters = [];
|
|
553
|
+
for (const param of method.parameters) {
|
|
554
|
+
if (ts4.isIdentifier(param.name)) {
|
|
555
|
+
const paramInfo = analyzeParameter(param, checker);
|
|
556
|
+
parameters.push(paramInfo);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
const returnTypeNode = method.type;
|
|
560
|
+
const signature = checker.getSignatureFromDeclaration(method);
|
|
561
|
+
const returnType = signature ? checker.getReturnTypeOfSignature(signature) : checker.getTypeAtLocation(method);
|
|
562
|
+
return {
|
|
563
|
+
name,
|
|
564
|
+
parameters,
|
|
565
|
+
returnTypeNode,
|
|
566
|
+
returnType
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
function analyzeParameter(param, checker) {
|
|
570
|
+
const name = ts4.isIdentifier(param.name) ? param.name.text : "param";
|
|
571
|
+
const typeNode = param.type;
|
|
572
|
+
const type = checker.getTypeAtLocation(param);
|
|
573
|
+
const formSpecExportName = detectFormSpecReference(typeNode);
|
|
574
|
+
const optional = param.questionToken !== void 0 || param.initializer !== void 0;
|
|
575
|
+
return {
|
|
576
|
+
name,
|
|
577
|
+
typeNode,
|
|
578
|
+
type,
|
|
579
|
+
formSpecExportName,
|
|
580
|
+
optional
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
function detectFormSpecReference(typeNode) {
|
|
584
|
+
if (!typeNode) return null;
|
|
585
|
+
if (!ts4.isTypeReferenceNode(typeNode)) return null;
|
|
586
|
+
const typeName = ts4.isIdentifier(typeNode.typeName) ? typeNode.typeName.text : ts4.isQualifiedName(typeNode.typeName) ? typeNode.typeName.right.text : null;
|
|
587
|
+
if (typeName !== "InferSchema" && typeName !== "InferFormSchema") return null;
|
|
588
|
+
const typeArg = typeNode.typeArguments?.[0];
|
|
589
|
+
if (!typeArg || !ts4.isTypeQueryNode(typeArg)) return null;
|
|
590
|
+
if (ts4.isIdentifier(typeArg.exprName)) {
|
|
591
|
+
return typeArg.exprName.text;
|
|
592
|
+
}
|
|
593
|
+
if (ts4.isQualifiedName(typeArg.exprName)) {
|
|
594
|
+
return typeArg.exprName.right.text;
|
|
595
|
+
}
|
|
596
|
+
return null;
|
|
597
|
+
}
|
|
598
|
+
function analyzeInterface(interfaceDecl, checker) {
|
|
599
|
+
const name = interfaceDecl.name.text;
|
|
600
|
+
const fields = [];
|
|
601
|
+
for (const member of interfaceDecl.members) {
|
|
602
|
+
if (ts4.isPropertySignature(member)) {
|
|
603
|
+
const fieldInfo = analyzeInterfaceProperty(member, checker);
|
|
604
|
+
if (fieldInfo) {
|
|
605
|
+
fields.push(fieldInfo);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
return {
|
|
610
|
+
name,
|
|
611
|
+
fields,
|
|
612
|
+
instanceMethods: [],
|
|
613
|
+
staticMethods: []
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
function analyzeTypeAlias(typeAlias, checker) {
|
|
617
|
+
if (!ts4.isTypeLiteralNode(typeAlias.type)) {
|
|
618
|
+
const sourceFile = typeAlias.getSourceFile();
|
|
619
|
+
const { line } = sourceFile.getLineAndCharacterOfPosition(typeAlias.getStart());
|
|
620
|
+
const kindDesc = ts4.SyntaxKind[typeAlias.type.kind] ?? "unknown";
|
|
621
|
+
return {
|
|
622
|
+
ok: false,
|
|
623
|
+
error: `Type alias "${typeAlias.name.text}" at line ${String(line + 1)} is not an object type literal (found ${kindDesc})`
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
const name = typeAlias.name.text;
|
|
627
|
+
const fields = [];
|
|
628
|
+
for (const member of typeAlias.type.members) {
|
|
629
|
+
if (ts4.isPropertySignature(member)) {
|
|
630
|
+
const fieldInfo = analyzeInterfaceProperty(member, checker);
|
|
631
|
+
if (fieldInfo) {
|
|
632
|
+
fields.push(fieldInfo);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
return {
|
|
637
|
+
ok: true,
|
|
638
|
+
analysis: {
|
|
639
|
+
name,
|
|
640
|
+
fields,
|
|
641
|
+
instanceMethods: [],
|
|
642
|
+
staticMethods: []
|
|
643
|
+
}
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
function analyzeInterfaceProperty(prop, checker) {
|
|
647
|
+
if (!ts4.isIdentifier(prop.name)) {
|
|
648
|
+
return null;
|
|
649
|
+
}
|
|
650
|
+
const name = prop.name.text;
|
|
651
|
+
const typeNode = prop.type;
|
|
652
|
+
const type = checker.getTypeAtLocation(prop);
|
|
653
|
+
const optional = prop.questionToken !== void 0;
|
|
654
|
+
const decorators = [];
|
|
655
|
+
if (typeNode) {
|
|
656
|
+
const aliasConstraints = extractTypeAliasConstraints(typeNode, checker);
|
|
657
|
+
decorators.push(...aliasConstraints);
|
|
658
|
+
}
|
|
659
|
+
const fieldMetadata = extractJSDocFieldMetadata(prop);
|
|
660
|
+
if (fieldMetadata) {
|
|
661
|
+
decorators.push(fieldMetadata);
|
|
662
|
+
}
|
|
663
|
+
const jsdocConstraints = extractJSDocConstraints(prop);
|
|
664
|
+
decorators.push(...jsdocConstraints);
|
|
665
|
+
const deprecated = hasDeprecatedTag(prop);
|
|
666
|
+
return {
|
|
667
|
+
name,
|
|
668
|
+
typeNode,
|
|
669
|
+
type,
|
|
670
|
+
optional,
|
|
671
|
+
decorators,
|
|
672
|
+
deprecated,
|
|
673
|
+
defaultValue: void 0
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
// src/analyzer/type-converter.ts
|
|
678
|
+
var ts5 = __toESM(require("typescript"), 1);
|
|
679
|
+
|
|
680
|
+
// src/json-schema/types.ts
|
|
681
|
+
function setSchemaExtension(schema, key, value) {
|
|
682
|
+
schema[key] = value;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
// src/analyzer/type-converter.ts
|
|
686
|
+
function getNamedTypeFieldInfoMap(type, checker) {
|
|
687
|
+
const symbols = [type.getSymbol(), type.aliasSymbol].filter(
|
|
688
|
+
(s) => s?.declarations != null && s.declarations.length > 0
|
|
689
|
+
);
|
|
690
|
+
for (const symbol of symbols) {
|
|
691
|
+
const declarations = symbol.declarations;
|
|
692
|
+
if (!declarations) continue;
|
|
693
|
+
const classDecl = declarations.find(ts5.isClassDeclaration);
|
|
694
|
+
if (classDecl) {
|
|
695
|
+
const map = /* @__PURE__ */ new Map();
|
|
696
|
+
for (const member of classDecl.members) {
|
|
697
|
+
if (ts5.isPropertyDeclaration(member) && ts5.isIdentifier(member.name)) {
|
|
698
|
+
const fieldInfo = analyzeField(member, checker);
|
|
699
|
+
if (fieldInfo) map.set(fieldInfo.name, fieldInfo);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
return map;
|
|
703
|
+
}
|
|
704
|
+
const interfaceDecl = declarations.find(ts5.isInterfaceDeclaration);
|
|
705
|
+
if (interfaceDecl) {
|
|
706
|
+
return buildFieldInfoMapFromSignatures(interfaceDecl.members, checker);
|
|
707
|
+
}
|
|
708
|
+
const typeAliasDecl = declarations.find(ts5.isTypeAliasDeclaration);
|
|
709
|
+
if (typeAliasDecl && ts5.isTypeLiteralNode(typeAliasDecl.type)) {
|
|
710
|
+
return buildFieldInfoMapFromSignatures(typeAliasDecl.type.members, checker);
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
return null;
|
|
714
|
+
}
|
|
715
|
+
function buildFieldInfoMapFromSignatures(members, checker) {
|
|
716
|
+
const map = /* @__PURE__ */ new Map();
|
|
717
|
+
for (const member of members) {
|
|
718
|
+
if (ts5.isPropertySignature(member)) {
|
|
719
|
+
const fieldInfo = analyzeInterfaceProperty(member, checker);
|
|
720
|
+
if (fieldInfo) {
|
|
721
|
+
map.set(fieldInfo.name, fieldInfo);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
return map;
|
|
726
|
+
}
|
|
727
|
+
function getObjectPropertyInfos(type, checker) {
|
|
728
|
+
const fieldInfoMap = getNamedTypeFieldInfoMap(type, checker);
|
|
729
|
+
const result = [];
|
|
730
|
+
for (const prop of type.getProperties()) {
|
|
731
|
+
const declaration = prop.valueDeclaration ?? prop.declarations?.[0];
|
|
732
|
+
if (!declaration) continue;
|
|
733
|
+
const propType = checker.getTypeOfSymbolAtLocation(prop, declaration);
|
|
734
|
+
const optional = !!(prop.flags & ts5.SymbolFlags.Optional);
|
|
735
|
+
const fieldInfo = fieldInfoMap?.get(prop.name) ?? void 0;
|
|
736
|
+
result.push({ name: prop.name, type: propType, optional, fieldInfo });
|
|
737
|
+
}
|
|
738
|
+
return result;
|
|
739
|
+
}
|
|
740
|
+
function convertType(type, checker) {
|
|
741
|
+
return convertTypeInternal(type, checker, /* @__PURE__ */ new Set());
|
|
742
|
+
}
|
|
743
|
+
function convertTypeInternal(type, checker, visiting) {
|
|
744
|
+
if (type.flags & ts5.TypeFlags.String) {
|
|
745
|
+
return { jsonSchema: { type: "string" }, formSpecFieldType: "text" };
|
|
746
|
+
}
|
|
747
|
+
if (type.flags & ts5.TypeFlags.Number) {
|
|
748
|
+
return { jsonSchema: { type: "number" }, formSpecFieldType: "number" };
|
|
749
|
+
}
|
|
750
|
+
if (type.flags & ts5.TypeFlags.Boolean) {
|
|
751
|
+
return { jsonSchema: { type: "boolean" }, formSpecFieldType: "boolean" };
|
|
752
|
+
}
|
|
753
|
+
if (type.flags & ts5.TypeFlags.Null) {
|
|
754
|
+
return { jsonSchema: { type: "null" }, formSpecFieldType: "null" };
|
|
755
|
+
}
|
|
756
|
+
if (type.flags & ts5.TypeFlags.Undefined) {
|
|
757
|
+
return { jsonSchema: {}, formSpecFieldType: "undefined" };
|
|
758
|
+
}
|
|
759
|
+
if (type.isStringLiteral()) {
|
|
760
|
+
return {
|
|
761
|
+
jsonSchema: { const: type.value },
|
|
762
|
+
formSpecFieldType: "enum"
|
|
763
|
+
};
|
|
764
|
+
}
|
|
765
|
+
if (type.isNumberLiteral()) {
|
|
766
|
+
return {
|
|
767
|
+
jsonSchema: { const: type.value },
|
|
768
|
+
formSpecFieldType: "number"
|
|
769
|
+
};
|
|
770
|
+
}
|
|
771
|
+
if (type.isUnion()) {
|
|
772
|
+
return convertUnionType(type, checker, visiting);
|
|
773
|
+
}
|
|
774
|
+
if (checker.isArrayType(type)) {
|
|
775
|
+
return convertArrayType(type, checker, visiting);
|
|
776
|
+
}
|
|
777
|
+
if (type.flags & ts5.TypeFlags.Object) {
|
|
778
|
+
return convertObjectType(type, checker, visiting);
|
|
779
|
+
}
|
|
780
|
+
return { jsonSchema: {}, formSpecFieldType: "unknown" };
|
|
781
|
+
}
|
|
782
|
+
function convertUnionType(type, checker, visiting) {
|
|
783
|
+
const types = type.types;
|
|
784
|
+
const nonNullTypes = types.filter(
|
|
785
|
+
(t) => !(t.flags & (ts5.TypeFlags.Null | ts5.TypeFlags.Undefined))
|
|
786
|
+
);
|
|
787
|
+
const hasNull = types.some((t) => t.flags & ts5.TypeFlags.Null);
|
|
788
|
+
const isBooleanUnion = nonNullTypes.length === 2 && nonNullTypes.every((t) => t.flags & ts5.TypeFlags.BooleanLiteral);
|
|
789
|
+
if (isBooleanUnion) {
|
|
790
|
+
const result = {
|
|
791
|
+
jsonSchema: { type: "boolean" },
|
|
792
|
+
formSpecFieldType: "boolean"
|
|
793
|
+
};
|
|
794
|
+
if (hasNull) {
|
|
795
|
+
result.jsonSchema = { oneOf: [{ type: "boolean" }, { type: "null" }] };
|
|
796
|
+
}
|
|
797
|
+
return result;
|
|
798
|
+
}
|
|
799
|
+
const allStringLiterals = nonNullTypes.every((t) => t.isStringLiteral());
|
|
800
|
+
if (allStringLiterals && nonNullTypes.length > 0) {
|
|
801
|
+
const enumValues = nonNullTypes.map((t) => t.value);
|
|
802
|
+
const result = {
|
|
803
|
+
jsonSchema: { enum: enumValues },
|
|
804
|
+
formSpecFieldType: "enum"
|
|
805
|
+
};
|
|
806
|
+
if (hasNull) {
|
|
807
|
+
result.jsonSchema = { oneOf: [{ enum: enumValues }, { type: "null" }] };
|
|
808
|
+
}
|
|
809
|
+
return result;
|
|
810
|
+
}
|
|
811
|
+
const allNumberLiterals = nonNullTypes.every((t) => t.isNumberLiteral());
|
|
812
|
+
if (allNumberLiterals && nonNullTypes.length > 0) {
|
|
813
|
+
const enumValues = nonNullTypes.map((t) => t.value);
|
|
814
|
+
const result = {
|
|
815
|
+
jsonSchema: { enum: enumValues },
|
|
816
|
+
formSpecFieldType: "enum"
|
|
817
|
+
};
|
|
818
|
+
if (hasNull) {
|
|
819
|
+
result.jsonSchema = { oneOf: [{ enum: enumValues }, { type: "null" }] };
|
|
820
|
+
}
|
|
821
|
+
return result;
|
|
822
|
+
}
|
|
823
|
+
if (nonNullTypes.length === 1 && nonNullTypes[0]) {
|
|
824
|
+
const result = convertTypeInternal(nonNullTypes[0], checker, visiting);
|
|
825
|
+
if (hasNull) {
|
|
826
|
+
result.jsonSchema = { oneOf: [result.jsonSchema, { type: "null" }] };
|
|
827
|
+
}
|
|
828
|
+
return result;
|
|
829
|
+
}
|
|
830
|
+
const schemas = nonNullTypes.map((t) => convertTypeInternal(t, checker, visiting).jsonSchema);
|
|
831
|
+
if (hasNull) {
|
|
832
|
+
schemas.push({ type: "null" });
|
|
833
|
+
}
|
|
834
|
+
return {
|
|
835
|
+
jsonSchema: { oneOf: schemas },
|
|
836
|
+
formSpecFieldType: "union"
|
|
837
|
+
};
|
|
838
|
+
}
|
|
839
|
+
function convertArrayType(type, checker, visiting) {
|
|
840
|
+
const typeArgs = type.typeArguments;
|
|
841
|
+
const elementType = typeArgs?.[0];
|
|
842
|
+
const itemSchema = elementType ? convertTypeInternal(elementType, checker, visiting).jsonSchema : {};
|
|
843
|
+
return {
|
|
844
|
+
jsonSchema: {
|
|
845
|
+
type: "array",
|
|
846
|
+
items: itemSchema
|
|
847
|
+
},
|
|
848
|
+
formSpecFieldType: "array"
|
|
849
|
+
};
|
|
850
|
+
}
|
|
851
|
+
function convertObjectType(type, checker, visiting) {
|
|
852
|
+
if (visiting.has(type)) {
|
|
853
|
+
return { jsonSchema: { type: "object" }, formSpecFieldType: "object" };
|
|
854
|
+
}
|
|
855
|
+
visiting.add(type);
|
|
856
|
+
const properties = {};
|
|
857
|
+
const required = [];
|
|
858
|
+
for (const propInfo of getObjectPropertyInfos(type, checker)) {
|
|
859
|
+
const propSchema = convertTypeInternal(propInfo.type, checker, visiting).jsonSchema;
|
|
860
|
+
properties[propInfo.name] = propInfo.fieldInfo ? applyDecoratorsToSchema(propSchema, propInfo.fieldInfo.decorators, propInfo.fieldInfo) : propSchema;
|
|
861
|
+
if (!propInfo.optional) {
|
|
862
|
+
required.push(propInfo.name);
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
visiting.delete(type);
|
|
866
|
+
return {
|
|
867
|
+
jsonSchema: {
|
|
868
|
+
type: "object",
|
|
869
|
+
properties,
|
|
870
|
+
...required.length > 0 ? { required } : {}
|
|
871
|
+
},
|
|
872
|
+
formSpecFieldType: "object"
|
|
873
|
+
};
|
|
874
|
+
}
|
|
875
|
+
function createFormSpecField(fieldName, type, decorators, optional, checker, visitedTypes = /* @__PURE__ */ new Set()) {
|
|
876
|
+
const { formSpecFieldType } = convertType(type, checker);
|
|
877
|
+
const field = {
|
|
878
|
+
_field: formSpecFieldType,
|
|
879
|
+
id: fieldName
|
|
880
|
+
};
|
|
881
|
+
if (!optional) {
|
|
882
|
+
field.required = true;
|
|
883
|
+
}
|
|
884
|
+
if (formSpecFieldType === "object" && type.flags & ts5.TypeFlags.Object) {
|
|
885
|
+
if (!visitedTypes.has(type)) {
|
|
886
|
+
visitedTypes.add(type);
|
|
887
|
+
const nestedFields = [];
|
|
888
|
+
for (const propInfo of getObjectPropertyInfos(type, checker)) {
|
|
889
|
+
nestedFields.push(
|
|
890
|
+
createFormSpecField(
|
|
891
|
+
propInfo.name,
|
|
892
|
+
propInfo.type,
|
|
893
|
+
propInfo.fieldInfo?.decorators ?? [],
|
|
894
|
+
propInfo.optional,
|
|
895
|
+
checker,
|
|
896
|
+
visitedTypes
|
|
897
|
+
)
|
|
898
|
+
);
|
|
899
|
+
}
|
|
900
|
+
visitedTypes.delete(type);
|
|
901
|
+
if (nestedFields.length > 0) {
|
|
902
|
+
field.fields = nestedFields;
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
for (const dec of decorators) {
|
|
907
|
+
applyDecoratorToField(field, dec);
|
|
908
|
+
}
|
|
909
|
+
return field;
|
|
910
|
+
}
|
|
911
|
+
function applyDecoratorToField(field, decorator) {
|
|
912
|
+
const { args } = decorator;
|
|
913
|
+
const resolved = decorator.resolved;
|
|
914
|
+
const effectiveName = resolved?.extendsBuiltin ?? decorator.name;
|
|
915
|
+
switch (effectiveName) {
|
|
916
|
+
case "Field": {
|
|
917
|
+
const opts = args[0];
|
|
918
|
+
if (typeof opts === "object" && opts !== null && !Array.isArray(opts)) {
|
|
919
|
+
if (typeof opts["displayName"] === "string") {
|
|
920
|
+
field.label = opts["displayName"];
|
|
921
|
+
}
|
|
922
|
+
if (typeof opts["description"] === "string") {
|
|
923
|
+
field.description = opts["description"];
|
|
924
|
+
}
|
|
925
|
+
if (typeof opts["placeholder"] === "string") {
|
|
926
|
+
field.placeholder = opts["placeholder"];
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
break;
|
|
930
|
+
}
|
|
931
|
+
case "Minimum":
|
|
932
|
+
if (typeof args[0] === "number") {
|
|
933
|
+
field.min = args[0];
|
|
934
|
+
}
|
|
935
|
+
break;
|
|
936
|
+
case "Maximum":
|
|
937
|
+
if (typeof args[0] === "number") {
|
|
938
|
+
field.max = args[0];
|
|
939
|
+
}
|
|
940
|
+
break;
|
|
941
|
+
case "MinLength":
|
|
942
|
+
if (typeof args[0] === "number") {
|
|
943
|
+
field.minLength = args[0];
|
|
944
|
+
}
|
|
945
|
+
break;
|
|
946
|
+
case "MaxLength":
|
|
947
|
+
if (typeof args[0] === "number") {
|
|
948
|
+
field.maxLength = args[0];
|
|
949
|
+
}
|
|
950
|
+
break;
|
|
951
|
+
case "Pattern":
|
|
952
|
+
if (typeof args[0] === "string") {
|
|
953
|
+
field.pattern = args[0];
|
|
954
|
+
}
|
|
955
|
+
break;
|
|
956
|
+
case "EnumOptions":
|
|
957
|
+
if (Array.isArray(args[0])) {
|
|
958
|
+
field.options = args[0];
|
|
959
|
+
}
|
|
960
|
+
break;
|
|
961
|
+
case "ShowWhen":
|
|
962
|
+
if (typeof args[0] === "object" && args[0] !== null) {
|
|
963
|
+
field.showWhen = args[0];
|
|
964
|
+
}
|
|
965
|
+
break;
|
|
966
|
+
case "Group":
|
|
967
|
+
if (typeof args[0] === "string") {
|
|
968
|
+
field.group = args[0];
|
|
969
|
+
}
|
|
970
|
+
break;
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
function applyDecoratorsToSchema(schema, decorators, fieldInfo) {
|
|
974
|
+
const result = { ...schema };
|
|
975
|
+
for (const dec of decorators) {
|
|
976
|
+
const { args } = dec;
|
|
977
|
+
const resolved = dec.resolved;
|
|
978
|
+
const effectiveName = resolved?.extendsBuiltin ?? dec.name;
|
|
979
|
+
switch (effectiveName) {
|
|
980
|
+
case "Field": {
|
|
981
|
+
const opts = args[0];
|
|
982
|
+
if (typeof opts === "object" && opts !== null && !Array.isArray(opts)) {
|
|
983
|
+
if (typeof opts["displayName"] === "string") {
|
|
984
|
+
result.title = opts["displayName"];
|
|
985
|
+
}
|
|
986
|
+
if (typeof opts["description"] === "string") {
|
|
987
|
+
result.description = opts["description"];
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
break;
|
|
991
|
+
}
|
|
992
|
+
case "Minimum":
|
|
993
|
+
if (typeof args[0] === "number") {
|
|
994
|
+
result.minimum = args[0];
|
|
995
|
+
}
|
|
996
|
+
break;
|
|
997
|
+
case "Maximum":
|
|
998
|
+
if (typeof args[0] === "number") {
|
|
999
|
+
result.maximum = args[0];
|
|
1000
|
+
}
|
|
1001
|
+
break;
|
|
1002
|
+
case "ExclusiveMinimum":
|
|
1003
|
+
if (typeof args[0] === "number") {
|
|
1004
|
+
result.exclusiveMinimum = args[0];
|
|
1005
|
+
}
|
|
1006
|
+
break;
|
|
1007
|
+
case "ExclusiveMaximum":
|
|
1008
|
+
if (typeof args[0] === "number") {
|
|
1009
|
+
result.exclusiveMaximum = args[0];
|
|
1010
|
+
}
|
|
1011
|
+
break;
|
|
1012
|
+
case "MinLength":
|
|
1013
|
+
if (typeof args[0] === "number") {
|
|
1014
|
+
result.minLength = args[0];
|
|
1015
|
+
}
|
|
1016
|
+
break;
|
|
1017
|
+
case "MaxLength":
|
|
1018
|
+
if (typeof args[0] === "number") {
|
|
1019
|
+
result.maxLength = args[0];
|
|
1020
|
+
}
|
|
1021
|
+
break;
|
|
1022
|
+
case "Pattern":
|
|
1023
|
+
if (typeof args[0] === "string") {
|
|
1024
|
+
result.pattern = args[0];
|
|
1025
|
+
}
|
|
1026
|
+
break;
|
|
1027
|
+
}
|
|
1028
|
+
if (resolved?.extensionName && /^[a-z][a-z0-9-]*$/.test(resolved.extensionName)) {
|
|
1029
|
+
const key = `x-formspec-${resolved.extensionName}`;
|
|
1030
|
+
if (resolved.isMarker) {
|
|
1031
|
+
setSchemaExtension(result, key, true);
|
|
1032
|
+
} else {
|
|
1033
|
+
setSchemaExtension(result, key, args[0] ?? true);
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
if (fieldInfo) {
|
|
1038
|
+
if (fieldInfo.deprecated) {
|
|
1039
|
+
result.deprecated = true;
|
|
1040
|
+
}
|
|
1041
|
+
if (fieldInfo.defaultValue !== void 0) {
|
|
1042
|
+
result.default = fieldInfo.defaultValue;
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
return result;
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
// src/ui-schema/schema.ts
|
|
1049
|
+
var import_zod = require("zod");
|
|
1050
|
+
var jsonPointerSchema = import_zod.z.string();
|
|
1051
|
+
var ruleEffectSchema = import_zod.z.enum(["SHOW", "HIDE", "ENABLE", "DISABLE"]);
|
|
1052
|
+
var uiSchemaElementTypeSchema = import_zod.z.enum([
|
|
1053
|
+
"Control",
|
|
1054
|
+
"VerticalLayout",
|
|
1055
|
+
"HorizontalLayout",
|
|
1056
|
+
"Group",
|
|
1057
|
+
"Categorization",
|
|
1058
|
+
"Category",
|
|
1059
|
+
"Label"
|
|
1060
|
+
]);
|
|
1061
|
+
var ruleConditionSchema = import_zod.z.lazy(
|
|
1062
|
+
() => import_zod.z.object({
|
|
1063
|
+
const: import_zod.z.unknown().optional(),
|
|
1064
|
+
enum: import_zod.z.array(import_zod.z.unknown()).readonly().optional(),
|
|
1065
|
+
type: import_zod.z.string().optional(),
|
|
1066
|
+
not: ruleConditionSchema.optional(),
|
|
1067
|
+
minimum: import_zod.z.number().optional(),
|
|
1068
|
+
maximum: import_zod.z.number().optional(),
|
|
1069
|
+
exclusiveMinimum: import_zod.z.number().optional(),
|
|
1070
|
+
exclusiveMaximum: import_zod.z.number().optional(),
|
|
1071
|
+
minLength: import_zod.z.number().optional(),
|
|
1072
|
+
properties: import_zod.z.record(import_zod.z.string(), ruleConditionSchema).optional(),
|
|
1073
|
+
required: import_zod.z.array(import_zod.z.string()).optional(),
|
|
1074
|
+
allOf: import_zod.z.array(ruleConditionSchema).optional()
|
|
1075
|
+
}).strict()
|
|
1076
|
+
);
|
|
1077
|
+
var schemaBasedConditionSchema = import_zod.z.object({
|
|
1078
|
+
scope: jsonPointerSchema,
|
|
1079
|
+
schema: ruleConditionSchema
|
|
1080
|
+
}).strict();
|
|
1081
|
+
var ruleSchema = import_zod.z.object({
|
|
1082
|
+
effect: ruleEffectSchema,
|
|
1083
|
+
condition: schemaBasedConditionSchema
|
|
1084
|
+
}).strict();
|
|
1085
|
+
var uiSchemaElementSchema = import_zod.z.lazy(
|
|
1086
|
+
() => import_zod.z.union([
|
|
1087
|
+
controlSchema,
|
|
1088
|
+
verticalLayoutSchema,
|
|
1089
|
+
horizontalLayoutSchema,
|
|
1090
|
+
groupLayoutSchema,
|
|
1091
|
+
categorizationSchema,
|
|
1092
|
+
categorySchema,
|
|
1093
|
+
labelElementSchema
|
|
1094
|
+
])
|
|
1095
|
+
);
|
|
1096
|
+
var controlSchema = import_zod.z.object({
|
|
1097
|
+
type: import_zod.z.literal("Control"),
|
|
1098
|
+
scope: jsonPointerSchema,
|
|
1099
|
+
label: import_zod.z.union([import_zod.z.string(), import_zod.z.literal(false)]).optional(),
|
|
1100
|
+
rule: ruleSchema.optional(),
|
|
1101
|
+
options: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional()
|
|
1102
|
+
}).passthrough();
|
|
1103
|
+
var verticalLayoutSchema = import_zod.z.lazy(
|
|
1104
|
+
() => import_zod.z.object({
|
|
1105
|
+
type: import_zod.z.literal("VerticalLayout"),
|
|
1106
|
+
elements: import_zod.z.array(uiSchemaElementSchema),
|
|
1107
|
+
rule: ruleSchema.optional(),
|
|
1108
|
+
options: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional()
|
|
1109
|
+
}).passthrough()
|
|
1110
|
+
);
|
|
1111
|
+
var horizontalLayoutSchema = import_zod.z.lazy(
|
|
1112
|
+
() => import_zod.z.object({
|
|
1113
|
+
type: import_zod.z.literal("HorizontalLayout"),
|
|
1114
|
+
elements: import_zod.z.array(uiSchemaElementSchema),
|
|
1115
|
+
rule: ruleSchema.optional(),
|
|
1116
|
+
options: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional()
|
|
1117
|
+
}).passthrough()
|
|
1118
|
+
);
|
|
1119
|
+
var groupLayoutSchema = import_zod.z.lazy(
|
|
1120
|
+
() => import_zod.z.object({
|
|
1121
|
+
type: import_zod.z.literal("Group"),
|
|
1122
|
+
label: import_zod.z.string(),
|
|
1123
|
+
elements: import_zod.z.array(uiSchemaElementSchema),
|
|
1124
|
+
rule: ruleSchema.optional(),
|
|
1125
|
+
options: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional()
|
|
1126
|
+
}).passthrough()
|
|
1127
|
+
);
|
|
1128
|
+
var categorySchema = import_zod.z.lazy(
|
|
1129
|
+
() => import_zod.z.object({
|
|
1130
|
+
type: import_zod.z.literal("Category"),
|
|
1131
|
+
label: import_zod.z.string(),
|
|
1132
|
+
elements: import_zod.z.array(uiSchemaElementSchema),
|
|
1133
|
+
rule: ruleSchema.optional(),
|
|
1134
|
+
options: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional()
|
|
1135
|
+
}).passthrough()
|
|
1136
|
+
);
|
|
1137
|
+
var categorizationSchema = import_zod.z.lazy(
|
|
1138
|
+
() => import_zod.z.object({
|
|
1139
|
+
type: import_zod.z.literal("Categorization"),
|
|
1140
|
+
elements: import_zod.z.array(categorySchema),
|
|
1141
|
+
label: import_zod.z.string().optional(),
|
|
1142
|
+
rule: ruleSchema.optional(),
|
|
1143
|
+
options: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional()
|
|
1144
|
+
}).passthrough()
|
|
1145
|
+
);
|
|
1146
|
+
var labelElementSchema = import_zod.z.object({
|
|
1147
|
+
type: import_zod.z.literal("Label"),
|
|
1148
|
+
text: import_zod.z.string(),
|
|
1149
|
+
rule: ruleSchema.optional(),
|
|
1150
|
+
options: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional()
|
|
1151
|
+
}).passthrough();
|
|
1152
|
+
var uiSchema = import_zod.z.lazy(
|
|
1153
|
+
() => import_zod.z.union([verticalLayoutSchema, horizontalLayoutSchema, groupLayoutSchema, categorizationSchema])
|
|
1154
|
+
);
|
|
1155
|
+
|
|
1156
|
+
// src/ui-schema/generator.ts
|
|
1157
|
+
var import_zod2 = require("zod");
|
|
1158
|
+
function parseOrThrow(schema, value, label) {
|
|
1159
|
+
try {
|
|
1160
|
+
return schema.parse(value);
|
|
1161
|
+
} catch (error) {
|
|
1162
|
+
if (error instanceof import_zod2.z.ZodError) {
|
|
1163
|
+
throw new Error(
|
|
1164
|
+
`Generated ${label} failed validation:
|
|
1165
|
+
${error.issues.map((i) => ` ${i.path.join(".")}: ${i.message}`).join("\n")}`
|
|
1166
|
+
);
|
|
1167
|
+
}
|
|
1168
|
+
throw error;
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
function formSpecFieldToElement(field, scopePrefix = "#/properties") {
|
|
1172
|
+
const control = {
|
|
1173
|
+
type: "Control",
|
|
1174
|
+
scope: `${scopePrefix}/${field.id}`
|
|
1175
|
+
};
|
|
1176
|
+
if (field.label !== void 0) {
|
|
1177
|
+
control.label = field.label;
|
|
1178
|
+
}
|
|
1179
|
+
if (field.showWhen !== void 0 && typeof field.showWhen === "object" && "field" in field.showWhen && "value" in field.showWhen) {
|
|
1180
|
+
const sw = field.showWhen;
|
|
1181
|
+
control.rule = {
|
|
1182
|
+
effect: "SHOW",
|
|
1183
|
+
condition: {
|
|
1184
|
+
scope: `#/properties/${sw.field}`,
|
|
1185
|
+
schema: { const: sw.value }
|
|
1186
|
+
}
|
|
1187
|
+
};
|
|
1188
|
+
}
|
|
1189
|
+
return control;
|
|
1190
|
+
}
|
|
1191
|
+
function generateUiSchemaFromFields(fields) {
|
|
1192
|
+
const groupMap = /* @__PURE__ */ new Map();
|
|
1193
|
+
const orderedKeys = [];
|
|
1194
|
+
const ungrouped = [];
|
|
1195
|
+
for (const field of fields) {
|
|
1196
|
+
const element = formSpecFieldToElement(field);
|
|
1197
|
+
if (field.group !== void 0) {
|
|
1198
|
+
if (!groupMap.has(field.group)) {
|
|
1199
|
+
groupMap.set(field.group, []);
|
|
1200
|
+
orderedKeys.push(field.group);
|
|
1201
|
+
}
|
|
1202
|
+
groupMap.get(field.group).push(element);
|
|
1203
|
+
} else {
|
|
1204
|
+
orderedKeys.push(null);
|
|
1205
|
+
ungrouped.push(element);
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
const elements = [];
|
|
1209
|
+
let ungroupedIndex = 0;
|
|
1210
|
+
for (const key of orderedKeys) {
|
|
1211
|
+
if (key === null) {
|
|
1212
|
+
const el = ungrouped[ungroupedIndex++];
|
|
1213
|
+
if (el !== void 0) {
|
|
1214
|
+
elements.push(el);
|
|
1215
|
+
}
|
|
1216
|
+
} else {
|
|
1217
|
+
const groupElements = groupMap.get(key) ?? [];
|
|
1218
|
+
const groupLayout = {
|
|
1219
|
+
type: "Group",
|
|
1220
|
+
label: key,
|
|
1221
|
+
elements: groupElements
|
|
1222
|
+
};
|
|
1223
|
+
elements.push(groupLayout);
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
const result = {
|
|
1227
|
+
type: "VerticalLayout",
|
|
1228
|
+
elements
|
|
1229
|
+
};
|
|
1230
|
+
return parseOrThrow(uiSchema, result, "UI Schema");
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
// src/generators/class-schema.ts
|
|
1234
|
+
function generateClassSchemas(analysis, checker) {
|
|
1235
|
+
const properties = {};
|
|
1236
|
+
const required = [];
|
|
1237
|
+
const uiElements = [];
|
|
1238
|
+
for (const field of analysis.fields) {
|
|
1239
|
+
const { jsonSchema: baseSchema } = convertType(field.type, checker);
|
|
1240
|
+
const fieldSchema = applyDecoratorsToSchema(baseSchema, field.decorators, field);
|
|
1241
|
+
properties[field.name] = fieldSchema;
|
|
1242
|
+
if (!field.optional) {
|
|
1243
|
+
required.push(field.name);
|
|
1244
|
+
}
|
|
1245
|
+
const formSpecField = createFormSpecField(
|
|
1246
|
+
field.name,
|
|
1247
|
+
field.type,
|
|
1248
|
+
field.decorators,
|
|
1249
|
+
field.optional,
|
|
1250
|
+
checker
|
|
1251
|
+
);
|
|
1252
|
+
uiElements.push(formSpecField);
|
|
1253
|
+
}
|
|
1254
|
+
const jsonSchema = {
|
|
1255
|
+
type: "object",
|
|
1256
|
+
properties,
|
|
1257
|
+
...required.length > 0 ? { required } : {}
|
|
1258
|
+
};
|
|
1259
|
+
const uiSchema2 = generateUiSchemaFromFields(uiElements);
|
|
1260
|
+
return { jsonSchema, uiSchema: uiSchema2 };
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
// src/generators/method-schema.ts
|
|
1264
|
+
function generateMethodSchemas(method, checker, loadedFormSpecs) {
|
|
1265
|
+
const returnType = convertType(method.returnType, checker).jsonSchema;
|
|
1266
|
+
const params = generateParamsSchemas(method.parameters, checker, loadedFormSpecs);
|
|
1267
|
+
return {
|
|
1268
|
+
name: method.name,
|
|
1269
|
+
params,
|
|
1270
|
+
returnType
|
|
1271
|
+
};
|
|
1272
|
+
}
|
|
1273
|
+
function generateParamsSchemas(parameters, checker, loadedFormSpecs) {
|
|
1274
|
+
if (parameters.length === 0) {
|
|
1275
|
+
return null;
|
|
1276
|
+
}
|
|
1277
|
+
for (const param of parameters) {
|
|
1278
|
+
if (param.formSpecExportName) {
|
|
1279
|
+
const formSpec = loadedFormSpecs.get(param.formSpecExportName);
|
|
1280
|
+
if (formSpec) {
|
|
1281
|
+
return {
|
|
1282
|
+
jsonSchema: formSpec.jsonSchema,
|
|
1283
|
+
uiSchema: formSpec.uiSchema,
|
|
1284
|
+
formSpecExport: param.formSpecExportName
|
|
1285
|
+
};
|
|
1286
|
+
}
|
|
1287
|
+
console.warn(
|
|
1288
|
+
`Warning: FormSpec export "${param.formSpecExportName}" not found, using static analysis`
|
|
1289
|
+
);
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
if (parameters.length === 1 && parameters[0]) {
|
|
1293
|
+
const param = parameters[0];
|
|
1294
|
+
const jsonSchema = convertType(param.type, checker).jsonSchema;
|
|
1295
|
+
return {
|
|
1296
|
+
jsonSchema,
|
|
1297
|
+
uiSchema: null,
|
|
1298
|
+
formSpecExport: null
|
|
1299
|
+
};
|
|
1300
|
+
}
|
|
1301
|
+
const properties = {};
|
|
1302
|
+
const required = [];
|
|
1303
|
+
for (const param of parameters) {
|
|
1304
|
+
const paramSchema = convertType(param.type, checker).jsonSchema;
|
|
1305
|
+
properties[param.name] = paramSchema;
|
|
1306
|
+
if (!param.optional) {
|
|
1307
|
+
required.push(param.name);
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
return {
|
|
1311
|
+
jsonSchema: {
|
|
1312
|
+
type: "object",
|
|
1313
|
+
properties,
|
|
1314
|
+
...required.length > 0 ? { required } : {}
|
|
1315
|
+
},
|
|
1316
|
+
uiSchema: null,
|
|
1317
|
+
formSpecExport: null
|
|
1318
|
+
};
|
|
1319
|
+
}
|
|
1320
|
+
function collectFormSpecReferences(methods) {
|
|
1321
|
+
const references = /* @__PURE__ */ new Set();
|
|
1322
|
+
for (const method of methods) {
|
|
1323
|
+
for (const param of method.parameters) {
|
|
1324
|
+
if (param.formSpecExportName) {
|
|
1325
|
+
references.add(param.formSpecExportName);
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
return references;
|
|
1330
|
+
}
|
|
1331
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1332
|
+
0 && (module.exports = {
|
|
1333
|
+
analyzeClass,
|
|
1334
|
+
analyzeInterface,
|
|
1335
|
+
analyzeTypeAlias,
|
|
1336
|
+
collectFormSpecReferences,
|
|
1337
|
+
createProgramContext,
|
|
1338
|
+
findClassByName,
|
|
1339
|
+
findInterfaceByName,
|
|
1340
|
+
findTypeAliasByName,
|
|
1341
|
+
generateClassSchemas,
|
|
1342
|
+
generateMethodSchemas,
|
|
1343
|
+
generateUiSchemaFromFields
|
|
1344
|
+
});
|
|
1345
|
+
//# sourceMappingURL=internals.cjs.map
|