@atscript/typescript 0.1.1 → 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 +252 -133
- package/dist/index.cjs +252 -133
- package/dist/index.d.ts +8 -2
- package/dist/index.mjs +252 -133
- package/dist/utils.cjs +317 -47
- package/dist/utils.d.ts +280 -12
- package/dist/utils.mjs +311 -47
- package/package.json +2 -2
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();
|
|
@@ -351,44 +358,60 @@ else this.writeln("{}");
|
|
|
351
358
|
renderAnnotate(node) {
|
|
352
359
|
if (node.isMutating) return;
|
|
353
360
|
const targetName = node.targetName;
|
|
354
|
-
const unwound = this.doc.unwindType(targetName);
|
|
355
|
-
if (!unwound?.def) return;
|
|
356
|
-
const def = this.doc.mergeIntersection(unwound.def);
|
|
357
361
|
this.writeln();
|
|
358
362
|
const exported = node.token("export")?.text === "export";
|
|
359
363
|
this.renderJsDoc(node);
|
|
360
|
-
if (
|
|
361
|
-
this.write(exported ? "export declare " : "declare ");
|
|
362
|
-
this.write(`class ${node.id} `);
|
|
363
|
-
this.renderStructure(def, node.id);
|
|
364
|
-
} else {
|
|
364
|
+
if (this.isTypeTarget(targetName)) {
|
|
365
365
|
this.write(exported ? "export " : "declare ");
|
|
366
|
-
this.write(`type ${node.id} = `);
|
|
367
|
-
this.
|
|
366
|
+
this.write(`type ${node.id} = ${targetName}`);
|
|
367
|
+
this.writeln();
|
|
368
|
+
const unwound = this.doc.unwindType(targetName);
|
|
369
|
+
this.renderTypeNamespaceFor(node.id, unwound?.def);
|
|
370
|
+
} else {
|
|
371
|
+
this.write(exported ? "export declare " : "declare ");
|
|
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();
|
|
378
|
+
this.writeln();
|
|
368
379
|
}
|
|
369
|
-
this.writeln();
|
|
370
380
|
}
|
|
371
381
|
renderTypeNamespace(node) {
|
|
372
|
-
this.
|
|
382
|
+
this.renderTypeNamespaceFor(node.id, node.getDefinition());
|
|
383
|
+
}
|
|
384
|
+
renderTypeNamespaceFor(name, inputDef) {
|
|
385
|
+
this.write(`declare namespace ${name} `);
|
|
373
386
|
this.blockln("{}");
|
|
374
|
-
const def = node.getDefinition();
|
|
375
387
|
let typeDef = "TAtscriptTypeDef";
|
|
376
|
-
if (
|
|
377
|
-
let realDef =
|
|
378
|
-
if ((0, __atscript_core.isRef)(
|
|
388
|
+
if (inputDef) {
|
|
389
|
+
let realDef = inputDef;
|
|
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 ${
|
|
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:
|
|
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
|
}
|
|
406
|
+
isTypeTarget(name, doc) {
|
|
407
|
+
const d = doc || this.doc;
|
|
408
|
+
const decl = d.getDeclarationOwnerNode(name);
|
|
409
|
+
if (!decl?.node) return false;
|
|
410
|
+
if (decl.node.entity === "type") return true;
|
|
411
|
+
if (decl.node.entity === "interface") return false;
|
|
412
|
+
if (decl.node.entity === "annotate") return this.isTypeTarget(decl.node.targetName, decl.doc);
|
|
413
|
+
return false;
|
|
414
|
+
}
|
|
392
415
|
renderJsDoc(node) {
|
|
393
416
|
const range = node.token("identifier")?.range;
|
|
394
417
|
const rangeStr = range ? `:${range.start.line + 1}:${range.start.character + 1}` : "";
|
|
@@ -418,6 +441,20 @@ function renderPrimitiveTypeDef(def) {
|
|
|
418
441
|
}
|
|
419
442
|
}
|
|
420
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
|
+
|
|
421
458
|
//#endregion
|
|
422
459
|
//#region packages/typescript/src/validator.ts
|
|
423
460
|
function _define_property$1(obj, key, value) {
|
|
@@ -463,7 +500,17 @@ var Validator = class {
|
|
|
463
500
|
throw() {
|
|
464
501
|
throw new ValidatorError(this.errors);
|
|
465
502
|
}
|
|
466
|
-
|
|
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) {
|
|
467
514
|
this.push("");
|
|
468
515
|
this.errors = [];
|
|
469
516
|
this.stackErrors = [];
|
|
@@ -490,15 +537,14 @@ var Validator = class {
|
|
|
490
537
|
return this.stackPath.slice(1).join(".");
|
|
491
538
|
}
|
|
492
539
|
validateAnnotatedType(def, value) {
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
}
|
|
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
|
+
});
|
|
502
548
|
}
|
|
503
549
|
validateUnion(def, value) {
|
|
504
550
|
let i = 0;
|
|
@@ -730,7 +776,7 @@ else {
|
|
|
730
776
|
constructor(def, opts) {
|
|
731
777
|
_define_property$1(this, "def", void 0);
|
|
732
778
|
_define_property$1(this, "opts", void 0);
|
|
733
|
-
_define_property$1(this, "errors", void 0);
|
|
779
|
+
/** Validation errors collected during the last {@link validate} call. */ _define_property$1(this, "errors", void 0);
|
|
734
780
|
_define_property$1(this, "stackErrors", void 0);
|
|
735
781
|
_define_property$1(this, "stackPath", void 0);
|
|
736
782
|
this.def = def;
|
|
@@ -757,6 +803,15 @@ var ValidatorError = class extends Error {
|
|
|
757
803
|
function isAnnotatedType(type) {
|
|
758
804
|
return type && type.__is_atscript_annotated_type;
|
|
759
805
|
}
|
|
806
|
+
function annotate(metadata, key, value, asArray) {
|
|
807
|
+
if (!metadata) return;
|
|
808
|
+
if (asArray) if (metadata.has(key)) {
|
|
809
|
+
const a = metadata.get(key);
|
|
810
|
+
if (Array.isArray(a)) a.push(value);
|
|
811
|
+
else metadata.set(key, [a, value]);
|
|
812
|
+
} else metadata.set(key, [value]);
|
|
813
|
+
else metadata.set(key, value);
|
|
814
|
+
}
|
|
760
815
|
function defineAnnotatedType(_kind, base) {
|
|
761
816
|
const kind = _kind || "";
|
|
762
817
|
const type = base?.type || {};
|
|
@@ -856,12 +911,7 @@ else if (!newBase) throw new Error(`"${typeName}" is not annotated type`);
|
|
|
856
911
|
return this;
|
|
857
912
|
},
|
|
858
913
|
annotate(key, value, asArray) {
|
|
859
|
-
|
|
860
|
-
const a = this.$metadata.get(key);
|
|
861
|
-
if (Array.isArray(a)) a.push(value);
|
|
862
|
-
else this.$metadata.set(key, [a, value]);
|
|
863
|
-
} else this.$metadata.set(key, [value]);
|
|
864
|
-
else this.$metadata.set(key, value);
|
|
914
|
+
annotate(this.$metadata, key, value, asArray);
|
|
865
915
|
return this;
|
|
866
916
|
}
|
|
867
917
|
};
|
|
@@ -872,14 +922,12 @@ else this.$metadata.set(key, value);
|
|
|
872
922
|
//#region packages/typescript/src/json-schema.ts
|
|
873
923
|
function buildJsonSchema(type) {
|
|
874
924
|
const build = (def) => {
|
|
875
|
-
const t = def.type;
|
|
876
925
|
const meta = def.metadata;
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
const obj = t;
|
|
926
|
+
return forAnnotatedType(def, {
|
|
927
|
+
object(d) {
|
|
880
928
|
const properties = {};
|
|
881
929
|
const required = [];
|
|
882
|
-
for (const [key, val] of
|
|
930
|
+
for (const [key, val] of d.type.props.entries()) {
|
|
883
931
|
properties[key] = build(val);
|
|
884
932
|
if (!val.optional) required.push(key);
|
|
885
933
|
}
|
|
@@ -889,41 +937,36 @@ function buildJsonSchema(type) {
|
|
|
889
937
|
};
|
|
890
938
|
if (required.length) schema.required = required;
|
|
891
939
|
return schema;
|
|
892
|
-
}
|
|
893
|
-
|
|
894
|
-
const arr = t;
|
|
940
|
+
},
|
|
941
|
+
array(d) {
|
|
895
942
|
const schema = {
|
|
896
943
|
type: "array",
|
|
897
|
-
items: build(
|
|
944
|
+
items: build(d.type.of)
|
|
898
945
|
};
|
|
899
946
|
const minLength = meta.get("expect.minLength");
|
|
900
947
|
if (typeof minLength === "number") schema.minItems = minLength;
|
|
901
948
|
const maxLength = meta.get("expect.maxLength");
|
|
902
949
|
if (typeof maxLength === "number") schema.maxItems = maxLength;
|
|
903
950
|
return schema;
|
|
904
|
-
}
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
}
|
|
913
|
-
case "tuple": {
|
|
914
|
-
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) {
|
|
915
959
|
return {
|
|
916
960
|
type: "array",
|
|
917
|
-
items:
|
|
961
|
+
items: d.type.items.map(build),
|
|
918
962
|
additionalItems: false
|
|
919
963
|
};
|
|
920
|
-
}
|
|
921
|
-
|
|
922
|
-
const fin = t;
|
|
964
|
+
},
|
|
965
|
+
final(d) {
|
|
923
966
|
const schema = {};
|
|
924
|
-
if (
|
|
925
|
-
if (
|
|
926
|
-
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;
|
|
927
970
|
if (schema.type === "number" && meta.get("expect.int")) schema.type = "integer";
|
|
928
971
|
}
|
|
929
972
|
if (schema.type === "string") {
|
|
@@ -943,8 +986,7 @@ else schema.allOf = (schema.allOf || []).concat(patterns.map((p) => ({ pattern:
|
|
|
943
986
|
}
|
|
944
987
|
return schema;
|
|
945
988
|
}
|
|
946
|
-
|
|
947
|
-
}
|
|
989
|
+
});
|
|
948
990
|
};
|
|
949
991
|
return build(type);
|
|
950
992
|
}
|
|
@@ -965,8 +1007,8 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
965
1007
|
pre() {
|
|
966
1008
|
this.writeln("// prettier-ignore-start");
|
|
967
1009
|
this.writeln("/* eslint-disable */");
|
|
968
|
-
const imports = ["defineAnnotatedType as $"];
|
|
969
|
-
if (
|
|
1010
|
+
const imports = ["defineAnnotatedType as $", "annotate as $a"];
|
|
1011
|
+
if (resolveJsonSchemaMode(this.opts) === "lazy") imports.push("buildJsonSchema as $$");
|
|
970
1012
|
this.writeln(`import { ${imports.join(", ")} } from "@atscript/typescript/utils"`);
|
|
971
1013
|
}
|
|
972
1014
|
buildAdHocMap(annotateNodes) {
|
|
@@ -984,27 +1026,26 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
984
1026
|
post() {
|
|
985
1027
|
for (const node of this.postAnnotate) if (node.entity === "annotate") {
|
|
986
1028
|
const annotateNode = node;
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
if (
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
1029
|
+
if (annotateNode.isMutating) this.renderMutatingAnnotateNode(annotateNode);
|
|
1030
|
+
else {
|
|
1031
|
+
const unwound = this.doc.unwindType(annotateNode.targetName);
|
|
1032
|
+
if (unwound?.def) {
|
|
1033
|
+
let def = this.doc.mergeIntersection(unwound.def);
|
|
1034
|
+
if ((0, __atscript_core.isInterface)(def)) def = def.getDefinition() || def;
|
|
1035
|
+
this._adHocAnnotations = this.buildAdHocMap([annotateNode]);
|
|
1036
|
+
this.annotateType(def, node.id);
|
|
1037
|
+
this._adHocAnnotations = null;
|
|
1038
|
+
this.indent();
|
|
1039
|
+
this.defineMetadataForAnnotateAlias(annotateNode);
|
|
1040
|
+
this.unindent();
|
|
1041
|
+
this.writeln();
|
|
1042
|
+
}
|
|
998
1043
|
}
|
|
999
1044
|
} else {
|
|
1000
|
-
const mutatingNodes = this.doc.getAnnotateNodesFor(node.id).filter((n) => n.isMutating);
|
|
1001
|
-
this._adHocAnnotations = this.buildAdHocMap(mutatingNodes);
|
|
1002
1045
|
this.annotateType(node.getDefinition(), node.id);
|
|
1003
|
-
this._adHocAnnotations = null;
|
|
1004
1046
|
this.indent().defineMetadata(node).unindent();
|
|
1005
1047
|
this.writeln();
|
|
1006
1048
|
}
|
|
1007
|
-
this.renderMutatingAnnotates();
|
|
1008
1049
|
this.writeln("// prettier-ignore-end");
|
|
1009
1050
|
super.post();
|
|
1010
1051
|
}
|
|
@@ -1038,7 +1079,7 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
1038
1079
|
}
|
|
1039
1080
|
renderAnnotate(node) {
|
|
1040
1081
|
if (node.isMutating) {
|
|
1041
|
-
this.
|
|
1082
|
+
this.postAnnotate.push(node);
|
|
1042
1083
|
return;
|
|
1043
1084
|
}
|
|
1044
1085
|
const targetName = node.targetName;
|
|
@@ -1055,15 +1096,21 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
1055
1096
|
this.writeln();
|
|
1056
1097
|
}
|
|
1057
1098
|
renderJsonSchemaMethod(node) {
|
|
1058
|
-
|
|
1099
|
+
const mode = resolveJsonSchemaMode(this.opts);
|
|
1100
|
+
const hasAnnotation = node.countAnnotations("emit.jsonSchema") > 0;
|
|
1101
|
+
if (hasAnnotation || mode === "bundle") {
|
|
1059
1102
|
const schema = JSON.stringify(buildJsonSchema(this.toAnnotatedType(node)));
|
|
1060
1103
|
this.writeln("static toJsonSchema() {");
|
|
1061
1104
|
this.indent().writeln(`return ${schema}`).unindent();
|
|
1062
1105
|
this.writeln("}");
|
|
1063
|
-
} else {
|
|
1106
|
+
} else if (mode === "lazy") {
|
|
1064
1107
|
this.writeln("static toJsonSchema() {");
|
|
1065
1108
|
this.indent().writeln("return this._jsonSchema ?? (this._jsonSchema = $$(this))").unindent();
|
|
1066
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("}");
|
|
1067
1114
|
}
|
|
1068
1115
|
}
|
|
1069
1116
|
toAnnotatedType(node) {
|
|
@@ -1326,18 +1373,13 @@ else handle.prop(prop.id, propHandle.$type);
|
|
|
1326
1373
|
return this;
|
|
1327
1374
|
}
|
|
1328
1375
|
defineMetadata(node) {
|
|
1329
|
-
|
|
1330
|
-
let adHocNames;
|
|
1331
|
-
let adHoc;
|
|
1376
|
+
let annotations = this.doc.evalAnnotationsForNode(node);
|
|
1332
1377
|
if (this._adHocAnnotations && this._propPath.length > 0) {
|
|
1333
1378
|
const path$2 = this._propPath.join(".");
|
|
1334
|
-
adHoc = this._adHocAnnotations.get(path$2);
|
|
1335
|
-
if (adHoc)
|
|
1379
|
+
const adHoc = this._adHocAnnotations.get(path$2);
|
|
1380
|
+
if (adHoc) annotations = this.doc.mergeNodesAnnotations(annotations, adHoc);
|
|
1336
1381
|
}
|
|
1337
1382
|
annotations?.forEach((an) => {
|
|
1338
|
-
if (!adHocNames || !adHocNames.has(an.name)) this.resolveAnnotationValue(node, an);
|
|
1339
|
-
});
|
|
1340
|
-
adHoc?.forEach((an) => {
|
|
1341
1383
|
this.resolveAnnotationValue(node, an);
|
|
1342
1384
|
});
|
|
1343
1385
|
return this;
|
|
@@ -1349,11 +1391,8 @@ else handle.prop(prop.id, propHandle.$type);
|
|
|
1349
1391
|
const annotateAnnotations = this.doc.evalAnnotationsForNode(annotateNode);
|
|
1350
1392
|
const targetDecl = this.doc.getDeclarationOwnerNode(annotateNode.targetName);
|
|
1351
1393
|
const targetAnnotations = targetDecl?.node ? targetDecl.doc.evalAnnotationsForNode(targetDecl.node) : undefined;
|
|
1352
|
-
const
|
|
1353
|
-
|
|
1354
|
-
if (!overriddenNames.has(an.name)) this.resolveAnnotationValue(annotateNode, an);
|
|
1355
|
-
});
|
|
1356
|
-
annotateAnnotations?.forEach((an) => {
|
|
1394
|
+
const merged = this.doc.mergeNodesAnnotations(targetAnnotations, annotateAnnotations);
|
|
1395
|
+
merged.forEach((an) => {
|
|
1357
1396
|
this.resolveAnnotationValue(annotateNode, an);
|
|
1358
1397
|
});
|
|
1359
1398
|
return this;
|
|
@@ -1392,52 +1431,132 @@ else targetValue = "true";
|
|
|
1392
1431
|
multiple: !!multiple
|
|
1393
1432
|
};
|
|
1394
1433
|
}
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1434
|
+
renderMutatingAnnotateNode(node) {
|
|
1435
|
+
const targetName = node.targetName;
|
|
1436
|
+
const targetDef = this.resolveTargetDef(targetName);
|
|
1437
|
+
this.writeln("// Ad-hoc annotations for ", targetName);
|
|
1438
|
+
for (const entry of node.entries) {
|
|
1439
|
+
const anns = entry.annotations;
|
|
1440
|
+
if (!anns || anns.length === 0) continue;
|
|
1441
|
+
const parts = entry.hasChain ? [entry.id, ...entry.chain.map((c) => c.text)] : [entry.id];
|
|
1442
|
+
const accessors = this.buildMutatingAccessors(targetName, targetDef, parts);
|
|
1443
|
+
for (const accessor of accessors) {
|
|
1444
|
+
const cleared = new Set();
|
|
1404
1445
|
for (const an of anns) {
|
|
1405
1446
|
const { value, multiple } = this.computeAnnotationValue(entry, an);
|
|
1406
1447
|
if (multiple) {
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
this.writeln(
|
|
1413
|
-
|
|
1414
|
-
this.writeln(`}`);
|
|
1415
|
-
} else this.writeln(`${accessor}.metadata.set("${escapeQuotes(an.name)}", ${value})`);
|
|
1448
|
+
if (!cleared.has(an.name)) {
|
|
1449
|
+
const spec = this.doc.resolveAnnotation(an.name);
|
|
1450
|
+
if (!spec || spec.config.mergeStrategy !== "append") this.writeln(`${accessor}.metadata.delete("${escapeQuotes(an.name)}")`);
|
|
1451
|
+
cleared.add(an.name);
|
|
1452
|
+
}
|
|
1453
|
+
this.writeln(`$a(${accessor}.metadata, "${escapeQuotes(an.name)}", ${value}, true)`);
|
|
1454
|
+
} else this.writeln(`$a(${accessor}.metadata, "${escapeQuotes(an.name)}", ${value})`);
|
|
1416
1455
|
}
|
|
1417
1456
|
}
|
|
1418
|
-
|
|
1419
|
-
|
|
1457
|
+
}
|
|
1458
|
+
const topAnnotations = node.annotations;
|
|
1459
|
+
if (topAnnotations && topAnnotations.length > 0) {
|
|
1460
|
+
const cleared = new Set();
|
|
1461
|
+
for (const an of topAnnotations) {
|
|
1420
1462
|
const { value, multiple } = this.computeAnnotationValue(node, an);
|
|
1421
1463
|
if (multiple) {
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
this.writeln(
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1464
|
+
if (!cleared.has(an.name)) {
|
|
1465
|
+
const spec = this.doc.resolveAnnotation(an.name);
|
|
1466
|
+
if (!spec || spec.config.mergeStrategy !== "append") this.writeln(`${targetName}.metadata.delete("${escapeQuotes(an.name)}")`);
|
|
1467
|
+
cleared.add(an.name);
|
|
1468
|
+
}
|
|
1469
|
+
this.writeln(`$a(${targetName}.metadata, "${escapeQuotes(an.name)}", ${value}, true)`);
|
|
1470
|
+
} else this.writeln(`$a(${targetName}.metadata, "${escapeQuotes(an.name)}", ${value})`);
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
this.writeln();
|
|
1474
|
+
}
|
|
1475
|
+
resolveTargetDef(targetName) {
|
|
1476
|
+
const unwound = this.doc.unwindType(targetName);
|
|
1477
|
+
if (!unwound?.def) return undefined;
|
|
1478
|
+
let def = unwound.def;
|
|
1479
|
+
if ((0, __atscript_core.isInterface)(def)) def = def.getDefinition() || def;
|
|
1480
|
+
return def;
|
|
1481
|
+
}
|
|
1482
|
+
/**
|
|
1483
|
+
* Builds the runtime accessor paths for mutating annotate entries.
|
|
1484
|
+
* Computes exact paths at compile time by walking the AST,
|
|
1485
|
+
* so the generated JS accesses props directly without runtime search.
|
|
1486
|
+
* Returns multiple paths when a property appears in multiple union branches.
|
|
1487
|
+
*/ buildMutatingAccessors(targetName, targetDef, parts) {
|
|
1488
|
+
let accessors = [{
|
|
1489
|
+
prefix: targetName + ".type",
|
|
1490
|
+
def: targetDef
|
|
1491
|
+
}];
|
|
1492
|
+
for (let i = 0; i < parts.length; i++) {
|
|
1493
|
+
const nextAccessors = [];
|
|
1494
|
+
for (const { prefix, def } of accessors) {
|
|
1495
|
+
const results = this.buildPropPaths(def, parts[i]);
|
|
1496
|
+
if (results.length > 0) for (const result of results) if (i < parts.length - 1) nextAccessors.push({
|
|
1497
|
+
prefix: prefix + result.path + "?.type",
|
|
1498
|
+
def: result.propDef
|
|
1499
|
+
});
|
|
1500
|
+
else nextAccessors.push({
|
|
1501
|
+
prefix: prefix + result.path + "?",
|
|
1502
|
+
def: result.propDef
|
|
1503
|
+
});
|
|
1504
|
+
else {
|
|
1505
|
+
const suffix = `.props.get("${escapeQuotes(parts[i])}")` + (i < parts.length - 1 ? "?.type" : "?");
|
|
1506
|
+
nextAccessors.push({
|
|
1507
|
+
prefix: prefix + suffix,
|
|
1508
|
+
def: undefined
|
|
1509
|
+
});
|
|
1510
|
+
}
|
|
1431
1511
|
}
|
|
1512
|
+
accessors = nextAccessors;
|
|
1513
|
+
}
|
|
1514
|
+
return accessors.map((a) => a.prefix);
|
|
1515
|
+
}
|
|
1516
|
+
/**
|
|
1517
|
+
* Finds a property in a type tree at compile time, returning all
|
|
1518
|
+
* matching runtime path strings and prop definitions for further chaining.
|
|
1519
|
+
* Returns multiple results when the same property appears in different union branches.
|
|
1520
|
+
*/ buildPropPaths(def, propName) {
|
|
1521
|
+
if (!def) return [];
|
|
1522
|
+
def = this.doc.mergeIntersection(def);
|
|
1523
|
+
if ((0, __atscript_core.isRef)(def)) {
|
|
1524
|
+
const ref = def;
|
|
1525
|
+
const unwound = this.doc.unwindType(ref.id, ref.chain)?.def;
|
|
1526
|
+
return this.buildPropPaths(unwound, propName);
|
|
1527
|
+
}
|
|
1528
|
+
if ((0, __atscript_core.isInterface)(def)) return this.buildPropPaths(def.getDefinition(), propName);
|
|
1529
|
+
if ((0, __atscript_core.isStructure)(def)) {
|
|
1530
|
+
const prop = def.props.get(propName);
|
|
1531
|
+
if (prop) return [{
|
|
1532
|
+
path: `.props.get("${escapeQuotes(propName)}")`,
|
|
1533
|
+
propDef: prop.getDefinition()
|
|
1534
|
+
}];
|
|
1535
|
+
return [];
|
|
1432
1536
|
}
|
|
1537
|
+
if ((0, __atscript_core.isGroup)(def)) {
|
|
1538
|
+
const group = def;
|
|
1539
|
+
const items = group.unwrap();
|
|
1540
|
+
const results = [];
|
|
1541
|
+
for (let i = 0; i < items.length; i++) for (const result of this.buildPropPaths(items[i], propName)) results.push({
|
|
1542
|
+
path: `.items[${i}].type${result.path}`,
|
|
1543
|
+
propDef: result.propDef
|
|
1544
|
+
});
|
|
1545
|
+
return results;
|
|
1546
|
+
}
|
|
1547
|
+
return [];
|
|
1433
1548
|
}
|
|
1434
1549
|
constructor(doc, opts) {
|
|
1435
|
-
super(doc), _define_property(this, "opts", void 0), _define_property(this, "postAnnotate", void 0), _define_property(this, "
|
|
1550
|
+
super(doc), _define_property(this, "opts", void 0), _define_property(this, "postAnnotate", void 0), _define_property(this, "_adHocAnnotations", void 0), _define_property(this, "_propPath", void 0), this.opts = opts, this.postAnnotate = [], this._adHocAnnotations = null, this._propPath = [];
|
|
1436
1551
|
}
|
|
1437
1552
|
};
|
|
1438
1553
|
|
|
1439
1554
|
//#endregion
|
|
1440
1555
|
//#region packages/typescript/src/plugin.ts
|
|
1556
|
+
function resolveJsonSchemaMode(opts) {
|
|
1557
|
+
if (opts?.jsonSchema !== undefined) return opts.jsonSchema;
|
|
1558
|
+
return false;
|
|
1559
|
+
}
|
|
1441
1560
|
const tsPlugin = (opts) => {
|
|
1442
1561
|
return {
|
|
1443
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
|
|