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