@atscript/typescript 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +90 -51
- package/dist/index.cjs +90 -51
- package/dist/index.d.ts +8 -2
- package/dist/index.mjs +90 -51
- package/dist/utils.cjs +306 -41
- package/dist/utils.d.ts +273 -11
- package/dist/utils.mjs +301 -41
- package/package.json +2 -2
package/dist/cli.cjs
CHANGED
|
@@ -234,7 +234,7 @@ var TypeRenderer = class TypeRenderer extends BaseRenderer {
|
|
|
234
234
|
this.writeln(" * Do not edit this file!");
|
|
235
235
|
this.writeln(" */");
|
|
236
236
|
this.writeln();
|
|
237
|
-
this.writeln("import type { TAtscriptTypeObject, TAtscriptTypeComplex, TAtscriptTypeFinal, TAtscriptTypeArray, TMetadataMap, Validator,
|
|
237
|
+
this.writeln("import type { TAtscriptTypeObject, TAtscriptTypeComplex, TAtscriptTypeFinal, TAtscriptTypeArray, TAtscriptAnnotatedType, TMetadataMap, Validator, TValidatorOptions } from \"@atscript/typescript/utils\"");
|
|
238
238
|
}
|
|
239
239
|
post() {
|
|
240
240
|
this.writeln("// prettier-ignore-end");
|
|
@@ -323,9 +323,10 @@ var TypeRenderer = class TypeRenderer extends BaseRenderer {
|
|
|
323
323
|
}
|
|
324
324
|
if (asClass) {
|
|
325
325
|
this.writeln("static __is_atscript_annotated_type: true");
|
|
326
|
-
this.writeln(`static type: TAtscriptTypeObject<keyof ${asClass}>`);
|
|
326
|
+
this.writeln(`static type: TAtscriptTypeObject<keyof ${asClass}, ${asClass}>`);
|
|
327
327
|
this.writeln(`static metadata: TMetadataMap<AtscriptMetadata>`);
|
|
328
|
-
this.writeln(`static validator:
|
|
328
|
+
this.writeln(`static validator: (opts?: Partial<TValidatorOptions>) => Validator<typeof ${asClass}>`);
|
|
329
|
+
if (resolveJsonSchemaMode(this.opts) === false) this.writeln("/** @deprecated JSON Schema support is disabled. Calling this method will throw a runtime error. To enable, set `jsonSchema: 'lazy'` or `jsonSchema: 'bundle'` in tsPlugin options, or add `@emit.jsonSchema` annotation to individual interfaces. */");
|
|
329
330
|
this.writeln("static toJsonSchema: () => any");
|
|
330
331
|
}
|
|
331
332
|
this.pop();
|
|
@@ -340,6 +341,12 @@ var TypeRenderer = class TypeRenderer extends BaseRenderer {
|
|
|
340
341
|
if (struct?.entity === "structure") this.renderStructure(struct, node.id);
|
|
341
342
|
else this.writeln("{}");
|
|
342
343
|
this.writeln();
|
|
344
|
+
const nsPrefix = exported ? "export declare" : "declare";
|
|
345
|
+
this.write(`${nsPrefix} namespace ${node.id} `);
|
|
346
|
+
this.blockln("{}");
|
|
347
|
+
this.writeln(`type DataType = ${node.id}`);
|
|
348
|
+
this.popln();
|
|
349
|
+
this.writeln();
|
|
343
350
|
}
|
|
344
351
|
renderType(node) {
|
|
345
352
|
this.writeln();
|
|
@@ -366,6 +373,11 @@ else this.writeln("{}");
|
|
|
366
373
|
} else {
|
|
367
374
|
this.write(exported ? "export declare " : "declare ");
|
|
368
375
|
this.writeln(`class ${node.id} extends ${targetName} {}`);
|
|
376
|
+
const nsPrefix = exported ? "export declare" : "declare";
|
|
377
|
+
this.write(`${nsPrefix} namespace ${node.id} `);
|
|
378
|
+
this.blockln("{}");
|
|
379
|
+
this.writeln(`type DataType = ${node.id}`);
|
|
380
|
+
this.popln();
|
|
369
381
|
this.writeln();
|
|
370
382
|
}
|
|
371
383
|
}
|
|
@@ -380,15 +392,17 @@ else this.writeln("{}");
|
|
|
380
392
|
let realDef = inputDef;
|
|
381
393
|
if ((0, __atscript_core.isRef)(inputDef)) realDef = this.doc.unwindType(inputDef.id, inputDef.chain)?.def || realDef;
|
|
382
394
|
realDef = this.doc.mergeIntersection(realDef);
|
|
383
|
-
if ((0, __atscript_core.isStructure)(realDef) || (0, __atscript_core.isInterface)(realDef)) typeDef = `TAtscriptTypeObject<keyof ${name}>`;
|
|
384
|
-
else if ((0, __atscript_core.isGroup)(realDef)) typeDef =
|
|
385
|
-
else if ((0, __atscript_core.isArray)(realDef)) typeDef =
|
|
386
|
-
else if ((0, __atscript_core.isPrimitive)(realDef)) typeDef =
|
|
395
|
+
if ((0, __atscript_core.isStructure)(realDef) || (0, __atscript_core.isInterface)(realDef)) typeDef = `TAtscriptTypeObject<keyof ${name}, ${name}>`;
|
|
396
|
+
else if ((0, __atscript_core.isGroup)(realDef)) typeDef = `TAtscriptTypeComplex<${name}>`;
|
|
397
|
+
else if ((0, __atscript_core.isArray)(realDef)) typeDef = `TAtscriptTypeArray<${name}>`;
|
|
398
|
+
else if ((0, __atscript_core.isPrimitive)(realDef)) typeDef = `TAtscriptTypeFinal<${name}>`;
|
|
387
399
|
}
|
|
400
|
+
this.writeln(`type DataType = ${name}`);
|
|
388
401
|
this.writeln(`const __is_atscript_annotated_type: true`);
|
|
389
402
|
this.writeln(`const type: ${typeDef}`);
|
|
390
403
|
this.writeln(`const metadata: TMetadataMap<AtscriptMetadata>`);
|
|
391
|
-
this.writeln(`const validator: (opts?: Partial<TValidatorOptions>) => Validator<
|
|
404
|
+
this.writeln(`const validator: (opts?: Partial<TValidatorOptions>) => Validator<TAtscriptAnnotatedType, ${name}>`);
|
|
405
|
+
if (resolveJsonSchemaMode(this.opts) === false) this.writeln("/** @deprecated JSON Schema support is disabled. Calling this method will throw a runtime error. To enable, set `jsonSchema: 'lazy'` or `jsonSchema: 'bundle'` in tsPlugin options, or add `@emit.jsonSchema` annotation to individual interfaces. */");
|
|
392
406
|
this.writeln("const toJsonSchema: () => any");
|
|
393
407
|
this.popln();
|
|
394
408
|
}
|
|
@@ -430,6 +444,20 @@ function renderPrimitiveTypeDef(def) {
|
|
|
430
444
|
}
|
|
431
445
|
}
|
|
432
446
|
|
|
447
|
+
//#endregion
|
|
448
|
+
//#region packages/typescript/src/traverse.ts
|
|
449
|
+
function forAnnotatedType(def, handlers) {
|
|
450
|
+
switch (def.type.kind) {
|
|
451
|
+
case "": return handlers.final(def);
|
|
452
|
+
case "object": return handlers.object(def);
|
|
453
|
+
case "array": return handlers.array(def);
|
|
454
|
+
case "union": return handlers.union(def);
|
|
455
|
+
case "intersection": return handlers.intersection(def);
|
|
456
|
+
case "tuple": return handlers.tuple(def);
|
|
457
|
+
default: throw new Error(`Unknown type kind "${def.type.kind}"`);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
433
461
|
//#endregion
|
|
434
462
|
//#region packages/typescript/src/validator.ts
|
|
435
463
|
function _define_property$2(obj, key, value) {
|
|
@@ -475,7 +503,17 @@ var Validator = class {
|
|
|
475
503
|
throw() {
|
|
476
504
|
throw new ValidatorError(this.errors);
|
|
477
505
|
}
|
|
478
|
-
|
|
506
|
+
/**
|
|
507
|
+
* Validates a value against the type definition.
|
|
508
|
+
*
|
|
509
|
+
* Acts as a TypeScript type guard — when it returns `true`, the value
|
|
510
|
+
* is narrowed to `DataType`.
|
|
511
|
+
*
|
|
512
|
+
* @param value - The value to validate.
|
|
513
|
+
* @param safe - If `true`, returns `false` on failure instead of throwing.
|
|
514
|
+
* @returns `true` if the value matches the type definition.
|
|
515
|
+
* @throws {ValidatorError} When validation fails and `safe` is not `true`.
|
|
516
|
+
*/ validate(value, safe) {
|
|
479
517
|
this.push("");
|
|
480
518
|
this.errors = [];
|
|
481
519
|
this.stackErrors = [];
|
|
@@ -502,15 +540,14 @@ var Validator = class {
|
|
|
502
540
|
return this.stackPath.slice(1).join(".");
|
|
503
541
|
}
|
|
504
542
|
validateAnnotatedType(def, value) {
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
}
|
|
543
|
+
return forAnnotatedType(def, {
|
|
544
|
+
final: (d) => this.validatePrimitive(d, value),
|
|
545
|
+
object: (d) => this.validateObject(d, value),
|
|
546
|
+
array: (d) => this.validateArray(d, value),
|
|
547
|
+
union: (d) => this.validateUnion(d, value),
|
|
548
|
+
intersection: (d) => this.validateIntersection(d, value),
|
|
549
|
+
tuple: (d) => this.validateTuple(d, value)
|
|
550
|
+
});
|
|
514
551
|
}
|
|
515
552
|
validateUnion(def, value) {
|
|
516
553
|
let i = 0;
|
|
@@ -742,7 +779,7 @@ else {
|
|
|
742
779
|
constructor(def, opts) {
|
|
743
780
|
_define_property$2(this, "def", void 0);
|
|
744
781
|
_define_property$2(this, "opts", void 0);
|
|
745
|
-
_define_property$2(this, "errors", void 0);
|
|
782
|
+
/** Validation errors collected during the last {@link validate} call. */ _define_property$2(this, "errors", void 0);
|
|
746
783
|
_define_property$2(this, "stackErrors", void 0);
|
|
747
784
|
_define_property$2(this, "stackPath", void 0);
|
|
748
785
|
this.def = def;
|
|
@@ -888,14 +925,12 @@ else if (!newBase) throw new Error(`"${typeName}" is not annotated type`);
|
|
|
888
925
|
//#region packages/typescript/src/json-schema.ts
|
|
889
926
|
function buildJsonSchema(type) {
|
|
890
927
|
const build$1 = (def) => {
|
|
891
|
-
const t = def.type;
|
|
892
928
|
const meta = def.metadata;
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
const obj = t;
|
|
929
|
+
return forAnnotatedType(def, {
|
|
930
|
+
object(d) {
|
|
896
931
|
const properties = {};
|
|
897
932
|
const required = [];
|
|
898
|
-
for (const [key, val] of
|
|
933
|
+
for (const [key, val] of d.type.props.entries()) {
|
|
899
934
|
properties[key] = build$1(val);
|
|
900
935
|
if (!val.optional) required.push(key);
|
|
901
936
|
}
|
|
@@ -905,41 +940,36 @@ function buildJsonSchema(type) {
|
|
|
905
940
|
};
|
|
906
941
|
if (required.length) schema.required = required;
|
|
907
942
|
return schema;
|
|
908
|
-
}
|
|
909
|
-
|
|
910
|
-
const arr = t;
|
|
943
|
+
},
|
|
944
|
+
array(d) {
|
|
911
945
|
const schema = {
|
|
912
946
|
type: "array",
|
|
913
|
-
items: build$1(
|
|
947
|
+
items: build$1(d.type.of)
|
|
914
948
|
};
|
|
915
949
|
const minLength = meta.get("expect.minLength");
|
|
916
950
|
if (typeof minLength === "number") schema.minItems = minLength;
|
|
917
951
|
const maxLength = meta.get("expect.maxLength");
|
|
918
952
|
if (typeof maxLength === "number") schema.maxItems = maxLength;
|
|
919
953
|
return schema;
|
|
920
|
-
}
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
}
|
|
929
|
-
case "tuple": {
|
|
930
|
-
const grp = t;
|
|
954
|
+
},
|
|
955
|
+
union(d) {
|
|
956
|
+
return { anyOf: d.type.items.map(build$1) };
|
|
957
|
+
},
|
|
958
|
+
intersection(d) {
|
|
959
|
+
return { allOf: d.type.items.map(build$1) };
|
|
960
|
+
},
|
|
961
|
+
tuple(d) {
|
|
931
962
|
return {
|
|
932
963
|
type: "array",
|
|
933
|
-
items:
|
|
964
|
+
items: d.type.items.map(build$1),
|
|
934
965
|
additionalItems: false
|
|
935
966
|
};
|
|
936
|
-
}
|
|
937
|
-
|
|
938
|
-
const fin = t;
|
|
967
|
+
},
|
|
968
|
+
final(d) {
|
|
939
969
|
const schema = {};
|
|
940
|
-
if (
|
|
941
|
-
if (
|
|
942
|
-
schema.type =
|
|
970
|
+
if (d.type.value !== undefined) schema.const = d.type.value;
|
|
971
|
+
if (d.type.designType && d.type.designType !== "any") {
|
|
972
|
+
schema.type = d.type.designType === "undefined" ? "null" : d.type.designType;
|
|
943
973
|
if (schema.type === "number" && meta.get("expect.int")) schema.type = "integer";
|
|
944
974
|
}
|
|
945
975
|
if (schema.type === "string") {
|
|
@@ -959,8 +989,7 @@ else schema.allOf = (schema.allOf || []).concat(patterns.map((p) => ({ pattern:
|
|
|
959
989
|
}
|
|
960
990
|
return schema;
|
|
961
991
|
}
|
|
962
|
-
|
|
963
|
-
}
|
|
992
|
+
});
|
|
964
993
|
};
|
|
965
994
|
return build$1(type);
|
|
966
995
|
}
|
|
@@ -982,7 +1011,7 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
982
1011
|
this.writeln("// prettier-ignore-start");
|
|
983
1012
|
this.writeln("/* eslint-disable */");
|
|
984
1013
|
const imports = ["defineAnnotatedType as $", "annotate as $a"];
|
|
985
|
-
if (
|
|
1014
|
+
if (resolveJsonSchemaMode(this.opts) === "lazy") imports.push("buildJsonSchema as $$");
|
|
986
1015
|
this.writeln(`import { ${imports.join(", ")} } from "@atscript/typescript/utils"`);
|
|
987
1016
|
}
|
|
988
1017
|
buildAdHocMap(annotateNodes) {
|
|
@@ -1070,15 +1099,21 @@ else {
|
|
|
1070
1099
|
this.writeln();
|
|
1071
1100
|
}
|
|
1072
1101
|
renderJsonSchemaMethod(node) {
|
|
1073
|
-
|
|
1102
|
+
const mode = resolveJsonSchemaMode(this.opts);
|
|
1103
|
+
const hasAnnotation = node.countAnnotations("emit.jsonSchema") > 0;
|
|
1104
|
+
if (hasAnnotation || mode === "bundle") {
|
|
1074
1105
|
const schema = JSON.stringify(buildJsonSchema(this.toAnnotatedType(node)));
|
|
1075
1106
|
this.writeln("static toJsonSchema() {");
|
|
1076
1107
|
this.indent().writeln(`return ${schema}`).unindent();
|
|
1077
1108
|
this.writeln("}");
|
|
1078
|
-
} else {
|
|
1109
|
+
} else if (mode === "lazy") {
|
|
1079
1110
|
this.writeln("static toJsonSchema() {");
|
|
1080
1111
|
this.indent().writeln("return this._jsonSchema ?? (this._jsonSchema = $$(this))").unindent();
|
|
1081
1112
|
this.writeln("}");
|
|
1113
|
+
} else {
|
|
1114
|
+
this.writeln("static toJsonSchema() {");
|
|
1115
|
+
this.indent().writeln("throw new Error(\"JSON Schema support is disabled. To enable, set `jsonSchema: 'lazy'` or `jsonSchema: 'bundle'` in tsPlugin options, or add @emit.jsonSchema annotation to individual interfaces.\")").unindent();
|
|
1116
|
+
this.writeln("}");
|
|
1082
1117
|
}
|
|
1083
1118
|
}
|
|
1084
1119
|
toAnnotatedType(node) {
|
|
@@ -1521,6 +1556,10 @@ else {
|
|
|
1521
1556
|
|
|
1522
1557
|
//#endregion
|
|
1523
1558
|
//#region packages/typescript/src/plugin.ts
|
|
1559
|
+
function resolveJsonSchemaMode(opts) {
|
|
1560
|
+
if (opts?.jsonSchema !== undefined) return opts.jsonSchema;
|
|
1561
|
+
return false;
|
|
1562
|
+
}
|
|
1524
1563
|
const tsPlugin = (opts) => {
|
|
1525
1564
|
return {
|
|
1526
1565
|
name: "typesccript",
|
package/dist/index.cjs
CHANGED
|
@@ -231,7 +231,7 @@ var TypeRenderer = class TypeRenderer extends BaseRenderer {
|
|
|
231
231
|
this.writeln(" * Do not edit this file!");
|
|
232
232
|
this.writeln(" */");
|
|
233
233
|
this.writeln();
|
|
234
|
-
this.writeln("import type { TAtscriptTypeObject, TAtscriptTypeComplex, TAtscriptTypeFinal, TAtscriptTypeArray, TMetadataMap, Validator,
|
|
234
|
+
this.writeln("import type { TAtscriptTypeObject, TAtscriptTypeComplex, TAtscriptTypeFinal, TAtscriptTypeArray, TAtscriptAnnotatedType, TMetadataMap, Validator, TValidatorOptions } from \"@atscript/typescript/utils\"");
|
|
235
235
|
}
|
|
236
236
|
post() {
|
|
237
237
|
this.writeln("// prettier-ignore-end");
|
|
@@ -320,9 +320,10 @@ var TypeRenderer = class TypeRenderer extends BaseRenderer {
|
|
|
320
320
|
}
|
|
321
321
|
if (asClass) {
|
|
322
322
|
this.writeln("static __is_atscript_annotated_type: true");
|
|
323
|
-
this.writeln(`static type: TAtscriptTypeObject<keyof ${asClass}>`);
|
|
323
|
+
this.writeln(`static type: TAtscriptTypeObject<keyof ${asClass}, ${asClass}>`);
|
|
324
324
|
this.writeln(`static metadata: TMetadataMap<AtscriptMetadata>`);
|
|
325
|
-
this.writeln(`static validator:
|
|
325
|
+
this.writeln(`static validator: (opts?: Partial<TValidatorOptions>) => Validator<typeof ${asClass}>`);
|
|
326
|
+
if (resolveJsonSchemaMode(this.opts) === false) this.writeln("/** @deprecated JSON Schema support is disabled. Calling this method will throw a runtime error. To enable, set `jsonSchema: 'lazy'` or `jsonSchema: 'bundle'` in tsPlugin options, or add `@emit.jsonSchema` annotation to individual interfaces. */");
|
|
326
327
|
this.writeln("static toJsonSchema: () => any");
|
|
327
328
|
}
|
|
328
329
|
this.pop();
|
|
@@ -337,6 +338,12 @@ var TypeRenderer = class TypeRenderer extends BaseRenderer {
|
|
|
337
338
|
if (struct?.entity === "structure") this.renderStructure(struct, node.id);
|
|
338
339
|
else this.writeln("{}");
|
|
339
340
|
this.writeln();
|
|
341
|
+
const nsPrefix = exported ? "export declare" : "declare";
|
|
342
|
+
this.write(`${nsPrefix} namespace ${node.id} `);
|
|
343
|
+
this.blockln("{}");
|
|
344
|
+
this.writeln(`type DataType = ${node.id}`);
|
|
345
|
+
this.popln();
|
|
346
|
+
this.writeln();
|
|
340
347
|
}
|
|
341
348
|
renderType(node) {
|
|
342
349
|
this.writeln();
|
|
@@ -363,6 +370,11 @@ else this.writeln("{}");
|
|
|
363
370
|
} else {
|
|
364
371
|
this.write(exported ? "export declare " : "declare ");
|
|
365
372
|
this.writeln(`class ${node.id} extends ${targetName} {}`);
|
|
373
|
+
const nsPrefix = exported ? "export declare" : "declare";
|
|
374
|
+
this.write(`${nsPrefix} namespace ${node.id} `);
|
|
375
|
+
this.blockln("{}");
|
|
376
|
+
this.writeln(`type DataType = ${node.id}`);
|
|
377
|
+
this.popln();
|
|
366
378
|
this.writeln();
|
|
367
379
|
}
|
|
368
380
|
}
|
|
@@ -377,15 +389,17 @@ else this.writeln("{}");
|
|
|
377
389
|
let realDef = inputDef;
|
|
378
390
|
if ((0, __atscript_core.isRef)(inputDef)) realDef = this.doc.unwindType(inputDef.id, inputDef.chain)?.def || realDef;
|
|
379
391
|
realDef = this.doc.mergeIntersection(realDef);
|
|
380
|
-
if ((0, __atscript_core.isStructure)(realDef) || (0, __atscript_core.isInterface)(realDef)) typeDef = `TAtscriptTypeObject<keyof ${name}>`;
|
|
381
|
-
else if ((0, __atscript_core.isGroup)(realDef)) typeDef =
|
|
382
|
-
else if ((0, __atscript_core.isArray)(realDef)) typeDef =
|
|
383
|
-
else if ((0, __atscript_core.isPrimitive)(realDef)) typeDef =
|
|
392
|
+
if ((0, __atscript_core.isStructure)(realDef) || (0, __atscript_core.isInterface)(realDef)) typeDef = `TAtscriptTypeObject<keyof ${name}, ${name}>`;
|
|
393
|
+
else if ((0, __atscript_core.isGroup)(realDef)) typeDef = `TAtscriptTypeComplex<${name}>`;
|
|
394
|
+
else if ((0, __atscript_core.isArray)(realDef)) typeDef = `TAtscriptTypeArray<${name}>`;
|
|
395
|
+
else if ((0, __atscript_core.isPrimitive)(realDef)) typeDef = `TAtscriptTypeFinal<${name}>`;
|
|
384
396
|
}
|
|
397
|
+
this.writeln(`type DataType = ${name}`);
|
|
385
398
|
this.writeln(`const __is_atscript_annotated_type: true`);
|
|
386
399
|
this.writeln(`const type: ${typeDef}`);
|
|
387
400
|
this.writeln(`const metadata: TMetadataMap<AtscriptMetadata>`);
|
|
388
|
-
this.writeln(`const validator: (opts?: Partial<TValidatorOptions>) => Validator<
|
|
401
|
+
this.writeln(`const validator: (opts?: Partial<TValidatorOptions>) => Validator<TAtscriptAnnotatedType, ${name}>`);
|
|
402
|
+
if (resolveJsonSchemaMode(this.opts) === false) this.writeln("/** @deprecated JSON Schema support is disabled. Calling this method will throw a runtime error. To enable, set `jsonSchema: 'lazy'` or `jsonSchema: 'bundle'` in tsPlugin options, or add `@emit.jsonSchema` annotation to individual interfaces. */");
|
|
389
403
|
this.writeln("const toJsonSchema: () => any");
|
|
390
404
|
this.popln();
|
|
391
405
|
}
|
|
@@ -427,6 +441,20 @@ function renderPrimitiveTypeDef(def) {
|
|
|
427
441
|
}
|
|
428
442
|
}
|
|
429
443
|
|
|
444
|
+
//#endregion
|
|
445
|
+
//#region packages/typescript/src/traverse.ts
|
|
446
|
+
function forAnnotatedType(def, handlers) {
|
|
447
|
+
switch (def.type.kind) {
|
|
448
|
+
case "": return handlers.final(def);
|
|
449
|
+
case "object": return handlers.object(def);
|
|
450
|
+
case "array": return handlers.array(def);
|
|
451
|
+
case "union": return handlers.union(def);
|
|
452
|
+
case "intersection": return handlers.intersection(def);
|
|
453
|
+
case "tuple": return handlers.tuple(def);
|
|
454
|
+
default: throw new Error(`Unknown type kind "${def.type.kind}"`);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
430
458
|
//#endregion
|
|
431
459
|
//#region packages/typescript/src/validator.ts
|
|
432
460
|
function _define_property$1(obj, key, value) {
|
|
@@ -472,7 +500,17 @@ var Validator = class {
|
|
|
472
500
|
throw() {
|
|
473
501
|
throw new ValidatorError(this.errors);
|
|
474
502
|
}
|
|
475
|
-
|
|
503
|
+
/**
|
|
504
|
+
* Validates a value against the type definition.
|
|
505
|
+
*
|
|
506
|
+
* Acts as a TypeScript type guard — when it returns `true`, the value
|
|
507
|
+
* is narrowed to `DataType`.
|
|
508
|
+
*
|
|
509
|
+
* @param value - The value to validate.
|
|
510
|
+
* @param safe - If `true`, returns `false` on failure instead of throwing.
|
|
511
|
+
* @returns `true` if the value matches the type definition.
|
|
512
|
+
* @throws {ValidatorError} When validation fails and `safe` is not `true`.
|
|
513
|
+
*/ validate(value, safe) {
|
|
476
514
|
this.push("");
|
|
477
515
|
this.errors = [];
|
|
478
516
|
this.stackErrors = [];
|
|
@@ -499,15 +537,14 @@ var Validator = class {
|
|
|
499
537
|
return this.stackPath.slice(1).join(".");
|
|
500
538
|
}
|
|
501
539
|
validateAnnotatedType(def, value) {
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
}
|
|
540
|
+
return forAnnotatedType(def, {
|
|
541
|
+
final: (d) => this.validatePrimitive(d, value),
|
|
542
|
+
object: (d) => this.validateObject(d, value),
|
|
543
|
+
array: (d) => this.validateArray(d, value),
|
|
544
|
+
union: (d) => this.validateUnion(d, value),
|
|
545
|
+
intersection: (d) => this.validateIntersection(d, value),
|
|
546
|
+
tuple: (d) => this.validateTuple(d, value)
|
|
547
|
+
});
|
|
511
548
|
}
|
|
512
549
|
validateUnion(def, value) {
|
|
513
550
|
let i = 0;
|
|
@@ -739,7 +776,7 @@ else {
|
|
|
739
776
|
constructor(def, opts) {
|
|
740
777
|
_define_property$1(this, "def", void 0);
|
|
741
778
|
_define_property$1(this, "opts", void 0);
|
|
742
|
-
_define_property$1(this, "errors", void 0);
|
|
779
|
+
/** Validation errors collected during the last {@link validate} call. */ _define_property$1(this, "errors", void 0);
|
|
743
780
|
_define_property$1(this, "stackErrors", void 0);
|
|
744
781
|
_define_property$1(this, "stackPath", void 0);
|
|
745
782
|
this.def = def;
|
|
@@ -885,14 +922,12 @@ else if (!newBase) throw new Error(`"${typeName}" is not annotated type`);
|
|
|
885
922
|
//#region packages/typescript/src/json-schema.ts
|
|
886
923
|
function buildJsonSchema(type) {
|
|
887
924
|
const build = (def) => {
|
|
888
|
-
const t = def.type;
|
|
889
925
|
const meta = def.metadata;
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
const obj = t;
|
|
926
|
+
return forAnnotatedType(def, {
|
|
927
|
+
object(d) {
|
|
893
928
|
const properties = {};
|
|
894
929
|
const required = [];
|
|
895
|
-
for (const [key, val] of
|
|
930
|
+
for (const [key, val] of d.type.props.entries()) {
|
|
896
931
|
properties[key] = build(val);
|
|
897
932
|
if (!val.optional) required.push(key);
|
|
898
933
|
}
|
|
@@ -902,41 +937,36 @@ function buildJsonSchema(type) {
|
|
|
902
937
|
};
|
|
903
938
|
if (required.length) schema.required = required;
|
|
904
939
|
return schema;
|
|
905
|
-
}
|
|
906
|
-
|
|
907
|
-
const arr = t;
|
|
940
|
+
},
|
|
941
|
+
array(d) {
|
|
908
942
|
const schema = {
|
|
909
943
|
type: "array",
|
|
910
|
-
items: build(
|
|
944
|
+
items: build(d.type.of)
|
|
911
945
|
};
|
|
912
946
|
const minLength = meta.get("expect.minLength");
|
|
913
947
|
if (typeof minLength === "number") schema.minItems = minLength;
|
|
914
948
|
const maxLength = meta.get("expect.maxLength");
|
|
915
949
|
if (typeof maxLength === "number") schema.maxItems = maxLength;
|
|
916
950
|
return schema;
|
|
917
|
-
}
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
}
|
|
926
|
-
case "tuple": {
|
|
927
|
-
const grp = t;
|
|
951
|
+
},
|
|
952
|
+
union(d) {
|
|
953
|
+
return { anyOf: d.type.items.map(build) };
|
|
954
|
+
},
|
|
955
|
+
intersection(d) {
|
|
956
|
+
return { allOf: d.type.items.map(build) };
|
|
957
|
+
},
|
|
958
|
+
tuple(d) {
|
|
928
959
|
return {
|
|
929
960
|
type: "array",
|
|
930
|
-
items:
|
|
961
|
+
items: d.type.items.map(build),
|
|
931
962
|
additionalItems: false
|
|
932
963
|
};
|
|
933
|
-
}
|
|
934
|
-
|
|
935
|
-
const fin = t;
|
|
964
|
+
},
|
|
965
|
+
final(d) {
|
|
936
966
|
const schema = {};
|
|
937
|
-
if (
|
|
938
|
-
if (
|
|
939
|
-
schema.type =
|
|
967
|
+
if (d.type.value !== undefined) schema.const = d.type.value;
|
|
968
|
+
if (d.type.designType && d.type.designType !== "any") {
|
|
969
|
+
schema.type = d.type.designType === "undefined" ? "null" : d.type.designType;
|
|
940
970
|
if (schema.type === "number" && meta.get("expect.int")) schema.type = "integer";
|
|
941
971
|
}
|
|
942
972
|
if (schema.type === "string") {
|
|
@@ -956,8 +986,7 @@ else schema.allOf = (schema.allOf || []).concat(patterns.map((p) => ({ pattern:
|
|
|
956
986
|
}
|
|
957
987
|
return schema;
|
|
958
988
|
}
|
|
959
|
-
|
|
960
|
-
}
|
|
989
|
+
});
|
|
961
990
|
};
|
|
962
991
|
return build(type);
|
|
963
992
|
}
|
|
@@ -979,7 +1008,7 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
979
1008
|
this.writeln("// prettier-ignore-start");
|
|
980
1009
|
this.writeln("/* eslint-disable */");
|
|
981
1010
|
const imports = ["defineAnnotatedType as $", "annotate as $a"];
|
|
982
|
-
if (
|
|
1011
|
+
if (resolveJsonSchemaMode(this.opts) === "lazy") imports.push("buildJsonSchema as $$");
|
|
983
1012
|
this.writeln(`import { ${imports.join(", ")} } from "@atscript/typescript/utils"`);
|
|
984
1013
|
}
|
|
985
1014
|
buildAdHocMap(annotateNodes) {
|
|
@@ -1067,15 +1096,21 @@ else {
|
|
|
1067
1096
|
this.writeln();
|
|
1068
1097
|
}
|
|
1069
1098
|
renderJsonSchemaMethod(node) {
|
|
1070
|
-
|
|
1099
|
+
const mode = resolveJsonSchemaMode(this.opts);
|
|
1100
|
+
const hasAnnotation = node.countAnnotations("emit.jsonSchema") > 0;
|
|
1101
|
+
if (hasAnnotation || mode === "bundle") {
|
|
1071
1102
|
const schema = JSON.stringify(buildJsonSchema(this.toAnnotatedType(node)));
|
|
1072
1103
|
this.writeln("static toJsonSchema() {");
|
|
1073
1104
|
this.indent().writeln(`return ${schema}`).unindent();
|
|
1074
1105
|
this.writeln("}");
|
|
1075
|
-
} else {
|
|
1106
|
+
} else if (mode === "lazy") {
|
|
1076
1107
|
this.writeln("static toJsonSchema() {");
|
|
1077
1108
|
this.indent().writeln("return this._jsonSchema ?? (this._jsonSchema = $$(this))").unindent();
|
|
1078
1109
|
this.writeln("}");
|
|
1110
|
+
} else {
|
|
1111
|
+
this.writeln("static toJsonSchema() {");
|
|
1112
|
+
this.indent().writeln("throw new Error(\"JSON Schema support is disabled. To enable, set `jsonSchema: 'lazy'` or `jsonSchema: 'bundle'` in tsPlugin options, or add @emit.jsonSchema annotation to individual interfaces.\")").unindent();
|
|
1113
|
+
this.writeln("}");
|
|
1079
1114
|
}
|
|
1080
1115
|
}
|
|
1081
1116
|
toAnnotatedType(node) {
|
|
@@ -1518,6 +1553,10 @@ else {
|
|
|
1518
1553
|
|
|
1519
1554
|
//#endregion
|
|
1520
1555
|
//#region packages/typescript/src/plugin.ts
|
|
1556
|
+
function resolveJsonSchemaMode(opts) {
|
|
1557
|
+
if (opts?.jsonSchema !== undefined) return opts.jsonSchema;
|
|
1558
|
+
return false;
|
|
1559
|
+
}
|
|
1521
1560
|
const tsPlugin = (opts) => {
|
|
1522
1561
|
return {
|
|
1523
1562
|
name: "typesccript",
|
package/dist/index.d.ts
CHANGED
|
@@ -2,9 +2,15 @@ import { TAtscriptPlugin } from '@atscript/core';
|
|
|
2
2
|
|
|
3
3
|
interface TTsPluginOptions {
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* JSON Schema support mode:
|
|
6
|
+
* - `false` — No support. `toJsonSchema()` throws at runtime. No `buildJsonSchema` import. *(default)*
|
|
7
|
+
* - `'lazy'` — Import `buildJsonSchema`, compute on demand, cache in type object.
|
|
8
|
+
* - `'bundle'` — Pre-compute at build time, embed static JSON in output. No import.
|
|
9
|
+
*
|
|
10
|
+
* Individual interfaces can override this with `@emit.jsonSchema` annotation
|
|
11
|
+
* to force build-time embedding regardless of this setting.
|
|
6
12
|
*/
|
|
7
|
-
|
|
13
|
+
jsonSchema?: false | 'lazy' | 'bundle';
|
|
8
14
|
}
|
|
9
15
|
declare const tsPlugin: (opts?: TTsPluginOptions) => TAtscriptPlugin;
|
|
10
16
|
|