@atscript/typescript 0.1.2 → 0.1.4
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 +118 -54
- package/dist/index.cjs +118 -54
- package/dist/index.d.ts +8 -2
- package/dist/index.mjs +118 -54
- package/dist/utils.cjs +330 -42
- package/dist/utils.d.ts +286 -12
- package/dist/utils.mjs +324 -42
- package/package.json +2 -2
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
|
|
package/dist/index.mjs
CHANGED
|
@@ -207,7 +207,7 @@ var TypeRenderer = class TypeRenderer extends BaseRenderer {
|
|
|
207
207
|
this.writeln(" * Do not edit this file!");
|
|
208
208
|
this.writeln(" */");
|
|
209
209
|
this.writeln();
|
|
210
|
-
this.writeln("import type { TAtscriptTypeObject, TAtscriptTypeComplex, TAtscriptTypeFinal, TAtscriptTypeArray, TMetadataMap, Validator,
|
|
210
|
+
this.writeln("import type { TAtscriptTypeObject, TAtscriptTypeComplex, TAtscriptTypeFinal, TAtscriptTypeArray, TAtscriptAnnotatedType, TMetadataMap, Validator, TValidatorOptions } from \"@atscript/typescript/utils\"");
|
|
211
211
|
}
|
|
212
212
|
post() {
|
|
213
213
|
this.writeln("// prettier-ignore-end");
|
|
@@ -271,6 +271,10 @@ var TypeRenderer = class TypeRenderer extends BaseRenderer {
|
|
|
271
271
|
patterns.push(prop);
|
|
272
272
|
continue;
|
|
273
273
|
}
|
|
274
|
+
if (this.isPhantomProp(prop.getDefinition())) {
|
|
275
|
+
this.writeln(`// ${prop.id}: phantom`);
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
274
278
|
const optional = !!prop.token("optional");
|
|
275
279
|
this.write(wrapProp(prop.id), optional ? "?" : "", ": ");
|
|
276
280
|
const renderedDef = this.renderTypeDefString(prop.getDefinition());
|
|
@@ -296,9 +300,10 @@ var TypeRenderer = class TypeRenderer extends BaseRenderer {
|
|
|
296
300
|
}
|
|
297
301
|
if (asClass) {
|
|
298
302
|
this.writeln("static __is_atscript_annotated_type: true");
|
|
299
|
-
this.writeln(`static type: TAtscriptTypeObject<keyof ${asClass}>`);
|
|
303
|
+
this.writeln(`static type: TAtscriptTypeObject<keyof ${asClass}, ${asClass}>`);
|
|
300
304
|
this.writeln(`static metadata: TMetadataMap<AtscriptMetadata>`);
|
|
301
|
-
this.writeln(`static validator:
|
|
305
|
+
this.writeln(`static validator: (opts?: Partial<TValidatorOptions>) => Validator<typeof ${asClass}>`);
|
|
306
|
+
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. */");
|
|
302
307
|
this.writeln("static toJsonSchema: () => any");
|
|
303
308
|
}
|
|
304
309
|
this.pop();
|
|
@@ -313,6 +318,12 @@ var TypeRenderer = class TypeRenderer extends BaseRenderer {
|
|
|
313
318
|
if (struct?.entity === "structure") this.renderStructure(struct, node.id);
|
|
314
319
|
else this.writeln("{}");
|
|
315
320
|
this.writeln();
|
|
321
|
+
const nsPrefix = exported ? "export declare" : "declare";
|
|
322
|
+
this.write(`${nsPrefix} namespace ${node.id} `);
|
|
323
|
+
this.blockln("{}");
|
|
324
|
+
this.writeln(`type DataType = ${node.id}`);
|
|
325
|
+
this.popln();
|
|
326
|
+
this.writeln();
|
|
316
327
|
}
|
|
317
328
|
renderType(node) {
|
|
318
329
|
this.writeln();
|
|
@@ -339,6 +350,11 @@ else this.writeln("{}");
|
|
|
339
350
|
} else {
|
|
340
351
|
this.write(exported ? "export declare " : "declare ");
|
|
341
352
|
this.writeln(`class ${node.id} extends ${targetName} {}`);
|
|
353
|
+
const nsPrefix = exported ? "export declare" : "declare";
|
|
354
|
+
this.write(`${nsPrefix} namespace ${node.id} `);
|
|
355
|
+
this.blockln("{}");
|
|
356
|
+
this.writeln(`type DataType = ${node.id}`);
|
|
357
|
+
this.popln();
|
|
342
358
|
this.writeln();
|
|
343
359
|
}
|
|
344
360
|
}
|
|
@@ -353,18 +369,29 @@ else this.writeln("{}");
|
|
|
353
369
|
let realDef = inputDef;
|
|
354
370
|
if (isRef(inputDef)) realDef = this.doc.unwindType(inputDef.id, inputDef.chain)?.def || realDef;
|
|
355
371
|
realDef = this.doc.mergeIntersection(realDef);
|
|
356
|
-
if (isStructure(realDef) || isInterface(realDef)) typeDef = `TAtscriptTypeObject<keyof ${name}>`;
|
|
357
|
-
else if (isGroup(realDef)) typeDef =
|
|
358
|
-
else if (isArray(realDef)) typeDef =
|
|
359
|
-
else if (isPrimitive(realDef)) typeDef =
|
|
372
|
+
if (isStructure(realDef) || isInterface(realDef)) typeDef = `TAtscriptTypeObject<keyof ${name}, ${name}>`;
|
|
373
|
+
else if (isGroup(realDef)) typeDef = `TAtscriptTypeComplex<${name}>`;
|
|
374
|
+
else if (isArray(realDef)) typeDef = `TAtscriptTypeArray<${name}>`;
|
|
375
|
+
else if (isPrimitive(realDef)) typeDef = `TAtscriptTypeFinal<${name}>`;
|
|
360
376
|
}
|
|
377
|
+
this.writeln(`type DataType = ${name}`);
|
|
361
378
|
this.writeln(`const __is_atscript_annotated_type: true`);
|
|
362
379
|
this.writeln(`const type: ${typeDef}`);
|
|
363
380
|
this.writeln(`const metadata: TMetadataMap<AtscriptMetadata>`);
|
|
364
|
-
this.writeln(`const validator: (opts?: Partial<TValidatorOptions>) => Validator<
|
|
381
|
+
this.writeln(`const validator: (opts?: Partial<TValidatorOptions>) => Validator<typeof ${name}, ${name}>`);
|
|
382
|
+
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. */");
|
|
365
383
|
this.writeln("const toJsonSchema: () => any");
|
|
366
384
|
this.popln();
|
|
367
385
|
}
|
|
386
|
+
isPhantomProp(def) {
|
|
387
|
+
if (!def) return false;
|
|
388
|
+
if (isPrimitive(def) && def.id === "phantom") return true;
|
|
389
|
+
if (isRef(def)) {
|
|
390
|
+
const unwound = this.doc.unwindType(def.id, def.chain)?.def;
|
|
391
|
+
return isPrimitive(unwound) && unwound.id === "phantom";
|
|
392
|
+
}
|
|
393
|
+
return false;
|
|
394
|
+
}
|
|
368
395
|
isTypeTarget(name, doc) {
|
|
369
396
|
const d = doc || this.doc;
|
|
370
397
|
const decl = d.getDeclarationOwnerNode(name);
|
|
@@ -403,6 +430,24 @@ function renderPrimitiveTypeDef(def) {
|
|
|
403
430
|
}
|
|
404
431
|
}
|
|
405
432
|
|
|
433
|
+
//#endregion
|
|
434
|
+
//#region packages/typescript/src/traverse.ts
|
|
435
|
+
function forAnnotatedType(def, handlers) {
|
|
436
|
+
switch (def.type.kind) {
|
|
437
|
+
case "": {
|
|
438
|
+
const typed = def;
|
|
439
|
+
if (handlers.phantom && typed.type.designType === "phantom") return handlers.phantom(typed);
|
|
440
|
+
return handlers.final(typed);
|
|
441
|
+
}
|
|
442
|
+
case "object": return handlers.object(def);
|
|
443
|
+
case "array": return handlers.array(def);
|
|
444
|
+
case "union": return handlers.union(def);
|
|
445
|
+
case "intersection": return handlers.intersection(def);
|
|
446
|
+
case "tuple": return handlers.tuple(def);
|
|
447
|
+
default: throw new Error(`Unknown type kind "${def.type.kind}"`);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
406
451
|
//#endregion
|
|
407
452
|
//#region packages/typescript/src/validator.ts
|
|
408
453
|
function _define_property$1(obj, key, value) {
|
|
@@ -448,7 +493,17 @@ var Validator = class {
|
|
|
448
493
|
throw() {
|
|
449
494
|
throw new ValidatorError(this.errors);
|
|
450
495
|
}
|
|
451
|
-
|
|
496
|
+
/**
|
|
497
|
+
* Validates a value against the type definition.
|
|
498
|
+
*
|
|
499
|
+
* Acts as a TypeScript type guard — when it returns `true`, the value
|
|
500
|
+
* is narrowed to `DataType`.
|
|
501
|
+
*
|
|
502
|
+
* @param value - The value to validate.
|
|
503
|
+
* @param safe - If `true`, returns `false` on failure instead of throwing.
|
|
504
|
+
* @returns `true` if the value matches the type definition.
|
|
505
|
+
* @throws {ValidatorError} When validation fails and `safe` is not `true`.
|
|
506
|
+
*/ validate(value, safe) {
|
|
452
507
|
this.push("");
|
|
453
508
|
this.errors = [];
|
|
454
509
|
this.stackErrors = [];
|
|
@@ -475,15 +530,15 @@ var Validator = class {
|
|
|
475
530
|
return this.stackPath.slice(1).join(".");
|
|
476
531
|
}
|
|
477
532
|
validateAnnotatedType(def, value) {
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
}
|
|
533
|
+
return forAnnotatedType(def, {
|
|
534
|
+
final: (d) => this.validatePrimitive(d, value),
|
|
535
|
+
phantom: () => true,
|
|
536
|
+
object: (d) => this.validateObject(d, value),
|
|
537
|
+
array: (d) => this.validateArray(d, value),
|
|
538
|
+
union: (d) => this.validateUnion(d, value),
|
|
539
|
+
intersection: (d) => this.validateIntersection(d, value),
|
|
540
|
+
tuple: (d) => this.validateTuple(d, value)
|
|
541
|
+
});
|
|
487
542
|
}
|
|
488
543
|
validateUnion(def, value) {
|
|
489
544
|
let i = 0;
|
|
@@ -574,7 +629,7 @@ var Validator = class {
|
|
|
574
629
|
let partialFunctionMatched = false;
|
|
575
630
|
if (typeof this.opts.partial === "function") partialFunctionMatched = this.opts.partial(def, this.path);
|
|
576
631
|
for (const [key, item] of def.type.props.entries()) {
|
|
577
|
-
if (skipList.has(key)) continue;
|
|
632
|
+
if (skipList.has(key) || isPhantomType(item)) continue;
|
|
578
633
|
typeKeys.add(key);
|
|
579
634
|
if (value[key] === undefined) {
|
|
580
635
|
if (partialFunctionMatched || this.opts.partial === "deep" || this.opts.partial === true && this.stackPath.length <= 1) continue;
|
|
@@ -715,7 +770,7 @@ else {
|
|
|
715
770
|
constructor(def, opts) {
|
|
716
771
|
_define_property$1(this, "def", void 0);
|
|
717
772
|
_define_property$1(this, "opts", void 0);
|
|
718
|
-
_define_property$1(this, "errors", void 0);
|
|
773
|
+
/** Validation errors collected during the last {@link validate} call. */ _define_property$1(this, "errors", void 0);
|
|
719
774
|
_define_property$1(this, "stackErrors", void 0);
|
|
720
775
|
_define_property$1(this, "stackPath", void 0);
|
|
721
776
|
this.def = def;
|
|
@@ -856,19 +911,24 @@ else if (!newBase) throw new Error(`"${typeName}" is not annotated type`);
|
|
|
856
911
|
};
|
|
857
912
|
return handle;
|
|
858
913
|
}
|
|
914
|
+
function isPhantomType(def) {
|
|
915
|
+
return def.type.kind === "" && def.type.designType === "phantom";
|
|
916
|
+
}
|
|
859
917
|
|
|
860
918
|
//#endregion
|
|
861
919
|
//#region packages/typescript/src/json-schema.ts
|
|
862
920
|
function buildJsonSchema(type) {
|
|
863
921
|
const build = (def) => {
|
|
864
|
-
const t = def.type;
|
|
865
922
|
const meta = def.metadata;
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
923
|
+
return forAnnotatedType(def, {
|
|
924
|
+
phantom() {
|
|
925
|
+
return {};
|
|
926
|
+
},
|
|
927
|
+
object(d) {
|
|
869
928
|
const properties = {};
|
|
870
929
|
const required = [];
|
|
871
|
-
for (const [key, val] of
|
|
930
|
+
for (const [key, val] of d.type.props.entries()) {
|
|
931
|
+
if (isPhantomType(val)) continue;
|
|
872
932
|
properties[key] = build(val);
|
|
873
933
|
if (!val.optional) required.push(key);
|
|
874
934
|
}
|
|
@@ -878,41 +938,36 @@ function buildJsonSchema(type) {
|
|
|
878
938
|
};
|
|
879
939
|
if (required.length) schema.required = required;
|
|
880
940
|
return schema;
|
|
881
|
-
}
|
|
882
|
-
|
|
883
|
-
const arr = t;
|
|
941
|
+
},
|
|
942
|
+
array(d) {
|
|
884
943
|
const schema = {
|
|
885
944
|
type: "array",
|
|
886
|
-
items: build(
|
|
945
|
+
items: build(d.type.of)
|
|
887
946
|
};
|
|
888
947
|
const minLength = meta.get("expect.minLength");
|
|
889
948
|
if (typeof minLength === "number") schema.minItems = minLength;
|
|
890
949
|
const maxLength = meta.get("expect.maxLength");
|
|
891
950
|
if (typeof maxLength === "number") schema.maxItems = maxLength;
|
|
892
951
|
return schema;
|
|
893
|
-
}
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
}
|
|
902
|
-
case "tuple": {
|
|
903
|
-
const grp = t;
|
|
952
|
+
},
|
|
953
|
+
union(d) {
|
|
954
|
+
return { anyOf: d.type.items.map(build) };
|
|
955
|
+
},
|
|
956
|
+
intersection(d) {
|
|
957
|
+
return { allOf: d.type.items.map(build) };
|
|
958
|
+
},
|
|
959
|
+
tuple(d) {
|
|
904
960
|
return {
|
|
905
961
|
type: "array",
|
|
906
|
-
items:
|
|
962
|
+
items: d.type.items.map(build),
|
|
907
963
|
additionalItems: false
|
|
908
964
|
};
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
const fin = t;
|
|
965
|
+
},
|
|
966
|
+
final(d) {
|
|
912
967
|
const schema = {};
|
|
913
|
-
if (
|
|
914
|
-
if (
|
|
915
|
-
schema.type =
|
|
968
|
+
if (d.type.value !== undefined) schema.const = d.type.value;
|
|
969
|
+
if (d.type.designType && d.type.designType !== "any") {
|
|
970
|
+
schema.type = d.type.designType === "undefined" ? "null" : d.type.designType;
|
|
916
971
|
if (schema.type === "number" && meta.get("expect.int")) schema.type = "integer";
|
|
917
972
|
}
|
|
918
973
|
if (schema.type === "string") {
|
|
@@ -932,8 +987,7 @@ else schema.allOf = (schema.allOf || []).concat(patterns.map((p) => ({ pattern:
|
|
|
932
987
|
}
|
|
933
988
|
return schema;
|
|
934
989
|
}
|
|
935
|
-
|
|
936
|
-
}
|
|
990
|
+
});
|
|
937
991
|
};
|
|
938
992
|
return build(type);
|
|
939
993
|
}
|
|
@@ -955,7 +1009,7 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
955
1009
|
this.writeln("// prettier-ignore-start");
|
|
956
1010
|
this.writeln("/* eslint-disable */");
|
|
957
1011
|
const imports = ["defineAnnotatedType as $", "annotate as $a"];
|
|
958
|
-
if (
|
|
1012
|
+
if (resolveJsonSchemaMode(this.opts) === "lazy") imports.push("buildJsonSchema as $$");
|
|
959
1013
|
this.writeln(`import { ${imports.join(", ")} } from "@atscript/typescript/utils"`);
|
|
960
1014
|
}
|
|
961
1015
|
buildAdHocMap(annotateNodes) {
|
|
@@ -1043,15 +1097,21 @@ else {
|
|
|
1043
1097
|
this.writeln();
|
|
1044
1098
|
}
|
|
1045
1099
|
renderJsonSchemaMethod(node) {
|
|
1046
|
-
|
|
1100
|
+
const mode = resolveJsonSchemaMode(this.opts);
|
|
1101
|
+
const hasAnnotation = node.countAnnotations("emit.jsonSchema") > 0;
|
|
1102
|
+
if (hasAnnotation || mode === "bundle") {
|
|
1047
1103
|
const schema = JSON.stringify(buildJsonSchema(this.toAnnotatedType(node)));
|
|
1048
1104
|
this.writeln("static toJsonSchema() {");
|
|
1049
1105
|
this.indent().writeln(`return ${schema}`).unindent();
|
|
1050
1106
|
this.writeln("}");
|
|
1051
|
-
} else {
|
|
1107
|
+
} else if (mode === "lazy") {
|
|
1052
1108
|
this.writeln("static toJsonSchema() {");
|
|
1053
1109
|
this.indent().writeln("return this._jsonSchema ?? (this._jsonSchema = $$(this))").unindent();
|
|
1054
1110
|
this.writeln("}");
|
|
1111
|
+
} else {
|
|
1112
|
+
this.writeln("static toJsonSchema() {");
|
|
1113
|
+
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();
|
|
1114
|
+
this.writeln("}");
|
|
1055
1115
|
}
|
|
1056
1116
|
}
|
|
1057
1117
|
toAnnotatedType(node) {
|
|
@@ -1085,7 +1145,7 @@ else {
|
|
|
1085
1145
|
case "primitive": {
|
|
1086
1146
|
const prim = node;
|
|
1087
1147
|
const handle = defineAnnotatedType();
|
|
1088
|
-
handle.designType(prim.id === "never" ? "never" : prim.config.type);
|
|
1148
|
+
handle.designType(prim.id === "never" ? "never" : prim.id === "phantom" ? "phantom" : prim.config.type);
|
|
1089
1149
|
if (!skipAnnotations) this.applyExpectAnnotations(handle, this.doc.evalAnnotationsForNode(node));
|
|
1090
1150
|
return handle;
|
|
1091
1151
|
}
|
|
@@ -1212,7 +1272,7 @@ else handle.prop(prop.id, propHandle.$type);
|
|
|
1212
1272
|
return this;
|
|
1213
1273
|
}
|
|
1214
1274
|
definePrimitive(node, name) {
|
|
1215
|
-
this.renderPrimitiveDef(node.id === "never" ? "never" : node.config.type, name);
|
|
1275
|
+
this.renderPrimitiveDef(node.id === "never" ? "never" : node.id === "phantom" ? "phantom" : node.config.type, name);
|
|
1216
1276
|
this.writeln(` .tags(${Array.from(node.tags).map((f) => `"${escapeQuotes(f)}"`).join(", ")})`);
|
|
1217
1277
|
return this;
|
|
1218
1278
|
}
|
|
@@ -1494,6 +1554,10 @@ else {
|
|
|
1494
1554
|
|
|
1495
1555
|
//#endregion
|
|
1496
1556
|
//#region packages/typescript/src/plugin.ts
|
|
1557
|
+
function resolveJsonSchemaMode(opts) {
|
|
1558
|
+
if (opts?.jsonSchema !== undefined) return opts.jsonSchema;
|
|
1559
|
+
return false;
|
|
1560
|
+
}
|
|
1497
1561
|
const tsPlugin = (opts) => {
|
|
1498
1562
|
return {
|
|
1499
1563
|
name: "typesccript",
|