@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.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");
|
|
@@ -296,9 +296,10 @@ var TypeRenderer = class TypeRenderer extends BaseRenderer {
|
|
|
296
296
|
}
|
|
297
297
|
if (asClass) {
|
|
298
298
|
this.writeln("static __is_atscript_annotated_type: true");
|
|
299
|
-
this.writeln(`static type: TAtscriptTypeObject<keyof ${asClass}>`);
|
|
299
|
+
this.writeln(`static type: TAtscriptTypeObject<keyof ${asClass}, ${asClass}>`);
|
|
300
300
|
this.writeln(`static metadata: TMetadataMap<AtscriptMetadata>`);
|
|
301
|
-
this.writeln(`static validator:
|
|
301
|
+
this.writeln(`static validator: (opts?: Partial<TValidatorOptions>) => Validator<typeof ${asClass}>`);
|
|
302
|
+
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
303
|
this.writeln("static toJsonSchema: () => any");
|
|
303
304
|
}
|
|
304
305
|
this.pop();
|
|
@@ -313,6 +314,12 @@ var TypeRenderer = class TypeRenderer extends BaseRenderer {
|
|
|
313
314
|
if (struct?.entity === "structure") this.renderStructure(struct, node.id);
|
|
314
315
|
else this.writeln("{}");
|
|
315
316
|
this.writeln();
|
|
317
|
+
const nsPrefix = exported ? "export declare" : "declare";
|
|
318
|
+
this.write(`${nsPrefix} namespace ${node.id} `);
|
|
319
|
+
this.blockln("{}");
|
|
320
|
+
this.writeln(`type DataType = ${node.id}`);
|
|
321
|
+
this.popln();
|
|
322
|
+
this.writeln();
|
|
316
323
|
}
|
|
317
324
|
renderType(node) {
|
|
318
325
|
this.writeln();
|
|
@@ -327,44 +334,60 @@ else this.writeln("{}");
|
|
|
327
334
|
renderAnnotate(node) {
|
|
328
335
|
if (node.isMutating) return;
|
|
329
336
|
const targetName = node.targetName;
|
|
330
|
-
const unwound = this.doc.unwindType(targetName);
|
|
331
|
-
if (!unwound?.def) return;
|
|
332
|
-
const def = this.doc.mergeIntersection(unwound.def);
|
|
333
337
|
this.writeln();
|
|
334
338
|
const exported = node.token("export")?.text === "export";
|
|
335
339
|
this.renderJsDoc(node);
|
|
336
|
-
if (
|
|
337
|
-
this.write(exported ? "export declare " : "declare ");
|
|
338
|
-
this.write(`class ${node.id} `);
|
|
339
|
-
this.renderStructure(def, node.id);
|
|
340
|
-
} else {
|
|
340
|
+
if (this.isTypeTarget(targetName)) {
|
|
341
341
|
this.write(exported ? "export " : "declare ");
|
|
342
|
-
this.write(`type ${node.id} = `);
|
|
343
|
-
this.
|
|
342
|
+
this.write(`type ${node.id} = ${targetName}`);
|
|
343
|
+
this.writeln();
|
|
344
|
+
const unwound = this.doc.unwindType(targetName);
|
|
345
|
+
this.renderTypeNamespaceFor(node.id, unwound?.def);
|
|
346
|
+
} else {
|
|
347
|
+
this.write(exported ? "export declare " : "declare ");
|
|
348
|
+
this.writeln(`class ${node.id} extends ${targetName} {}`);
|
|
349
|
+
const nsPrefix = exported ? "export declare" : "declare";
|
|
350
|
+
this.write(`${nsPrefix} namespace ${node.id} `);
|
|
351
|
+
this.blockln("{}");
|
|
352
|
+
this.writeln(`type DataType = ${node.id}`);
|
|
353
|
+
this.popln();
|
|
354
|
+
this.writeln();
|
|
344
355
|
}
|
|
345
|
-
this.writeln();
|
|
346
356
|
}
|
|
347
357
|
renderTypeNamespace(node) {
|
|
348
|
-
this.
|
|
358
|
+
this.renderTypeNamespaceFor(node.id, node.getDefinition());
|
|
359
|
+
}
|
|
360
|
+
renderTypeNamespaceFor(name, inputDef) {
|
|
361
|
+
this.write(`declare namespace ${name} `);
|
|
349
362
|
this.blockln("{}");
|
|
350
|
-
const def = node.getDefinition();
|
|
351
363
|
let typeDef = "TAtscriptTypeDef";
|
|
352
|
-
if (
|
|
353
|
-
let realDef =
|
|
354
|
-
if (isRef(
|
|
364
|
+
if (inputDef) {
|
|
365
|
+
let realDef = inputDef;
|
|
366
|
+
if (isRef(inputDef)) realDef = this.doc.unwindType(inputDef.id, inputDef.chain)?.def || realDef;
|
|
355
367
|
realDef = this.doc.mergeIntersection(realDef);
|
|
356
|
-
if (isStructure(realDef) || isInterface(realDef)) typeDef = `TAtscriptTypeObject<keyof ${
|
|
357
|
-
else if (isGroup(realDef)) typeDef =
|
|
358
|
-
else if (isArray(realDef)) typeDef =
|
|
359
|
-
else if (isPrimitive(realDef)) typeDef =
|
|
368
|
+
if (isStructure(realDef) || isInterface(realDef)) typeDef = `TAtscriptTypeObject<keyof ${name}, ${name}>`;
|
|
369
|
+
else if (isGroup(realDef)) typeDef = `TAtscriptTypeComplex<${name}>`;
|
|
370
|
+
else if (isArray(realDef)) typeDef = `TAtscriptTypeArray<${name}>`;
|
|
371
|
+
else if (isPrimitive(realDef)) typeDef = `TAtscriptTypeFinal<${name}>`;
|
|
360
372
|
}
|
|
373
|
+
this.writeln(`type DataType = ${name}`);
|
|
361
374
|
this.writeln(`const __is_atscript_annotated_type: true`);
|
|
362
375
|
this.writeln(`const type: ${typeDef}`);
|
|
363
376
|
this.writeln(`const metadata: TMetadataMap<AtscriptMetadata>`);
|
|
364
|
-
this.writeln(`const validator:
|
|
377
|
+
this.writeln(`const validator: (opts?: Partial<TValidatorOptions>) => Validator<TAtscriptAnnotatedType, ${name}>`);
|
|
378
|
+
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
379
|
this.writeln("const toJsonSchema: () => any");
|
|
366
380
|
this.popln();
|
|
367
381
|
}
|
|
382
|
+
isTypeTarget(name, doc) {
|
|
383
|
+
const d = doc || this.doc;
|
|
384
|
+
const decl = d.getDeclarationOwnerNode(name);
|
|
385
|
+
if (!decl?.node) return false;
|
|
386
|
+
if (decl.node.entity === "type") return true;
|
|
387
|
+
if (decl.node.entity === "interface") return false;
|
|
388
|
+
if (decl.node.entity === "annotate") return this.isTypeTarget(decl.node.targetName, decl.doc);
|
|
389
|
+
return false;
|
|
390
|
+
}
|
|
368
391
|
renderJsDoc(node) {
|
|
369
392
|
const range = node.token("identifier")?.range;
|
|
370
393
|
const rangeStr = range ? `:${range.start.line + 1}:${range.start.character + 1}` : "";
|
|
@@ -394,6 +417,20 @@ function renderPrimitiveTypeDef(def) {
|
|
|
394
417
|
}
|
|
395
418
|
}
|
|
396
419
|
|
|
420
|
+
//#endregion
|
|
421
|
+
//#region packages/typescript/src/traverse.ts
|
|
422
|
+
function forAnnotatedType(def, handlers) {
|
|
423
|
+
switch (def.type.kind) {
|
|
424
|
+
case "": return handlers.final(def);
|
|
425
|
+
case "object": return handlers.object(def);
|
|
426
|
+
case "array": return handlers.array(def);
|
|
427
|
+
case "union": return handlers.union(def);
|
|
428
|
+
case "intersection": return handlers.intersection(def);
|
|
429
|
+
case "tuple": return handlers.tuple(def);
|
|
430
|
+
default: throw new Error(`Unknown type kind "${def.type.kind}"`);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
397
434
|
//#endregion
|
|
398
435
|
//#region packages/typescript/src/validator.ts
|
|
399
436
|
function _define_property$1(obj, key, value) {
|
|
@@ -439,7 +476,17 @@ var Validator = class {
|
|
|
439
476
|
throw() {
|
|
440
477
|
throw new ValidatorError(this.errors);
|
|
441
478
|
}
|
|
442
|
-
|
|
479
|
+
/**
|
|
480
|
+
* Validates a value against the type definition.
|
|
481
|
+
*
|
|
482
|
+
* Acts as a TypeScript type guard — when it returns `true`, the value
|
|
483
|
+
* is narrowed to `DataType`.
|
|
484
|
+
*
|
|
485
|
+
* @param value - The value to validate.
|
|
486
|
+
* @param safe - If `true`, returns `false` on failure instead of throwing.
|
|
487
|
+
* @returns `true` if the value matches the type definition.
|
|
488
|
+
* @throws {ValidatorError} When validation fails and `safe` is not `true`.
|
|
489
|
+
*/ validate(value, safe) {
|
|
443
490
|
this.push("");
|
|
444
491
|
this.errors = [];
|
|
445
492
|
this.stackErrors = [];
|
|
@@ -466,15 +513,14 @@ var Validator = class {
|
|
|
466
513
|
return this.stackPath.slice(1).join(".");
|
|
467
514
|
}
|
|
468
515
|
validateAnnotatedType(def, value) {
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
}
|
|
516
|
+
return forAnnotatedType(def, {
|
|
517
|
+
final: (d) => this.validatePrimitive(d, value),
|
|
518
|
+
object: (d) => this.validateObject(d, value),
|
|
519
|
+
array: (d) => this.validateArray(d, value),
|
|
520
|
+
union: (d) => this.validateUnion(d, value),
|
|
521
|
+
intersection: (d) => this.validateIntersection(d, value),
|
|
522
|
+
tuple: (d) => this.validateTuple(d, value)
|
|
523
|
+
});
|
|
478
524
|
}
|
|
479
525
|
validateUnion(def, value) {
|
|
480
526
|
let i = 0;
|
|
@@ -706,7 +752,7 @@ else {
|
|
|
706
752
|
constructor(def, opts) {
|
|
707
753
|
_define_property$1(this, "def", void 0);
|
|
708
754
|
_define_property$1(this, "opts", void 0);
|
|
709
|
-
_define_property$1(this, "errors", void 0);
|
|
755
|
+
/** Validation errors collected during the last {@link validate} call. */ _define_property$1(this, "errors", void 0);
|
|
710
756
|
_define_property$1(this, "stackErrors", void 0);
|
|
711
757
|
_define_property$1(this, "stackPath", void 0);
|
|
712
758
|
this.def = def;
|
|
@@ -733,6 +779,15 @@ var ValidatorError = class extends Error {
|
|
|
733
779
|
function isAnnotatedType(type) {
|
|
734
780
|
return type && type.__is_atscript_annotated_type;
|
|
735
781
|
}
|
|
782
|
+
function annotate(metadata, key, value, asArray) {
|
|
783
|
+
if (!metadata) return;
|
|
784
|
+
if (asArray) if (metadata.has(key)) {
|
|
785
|
+
const a = metadata.get(key);
|
|
786
|
+
if (Array.isArray(a)) a.push(value);
|
|
787
|
+
else metadata.set(key, [a, value]);
|
|
788
|
+
} else metadata.set(key, [value]);
|
|
789
|
+
else metadata.set(key, value);
|
|
790
|
+
}
|
|
736
791
|
function defineAnnotatedType(_kind, base) {
|
|
737
792
|
const kind = _kind || "";
|
|
738
793
|
const type = base?.type || {};
|
|
@@ -832,12 +887,7 @@ else if (!newBase) throw new Error(`"${typeName}" is not annotated type`);
|
|
|
832
887
|
return this;
|
|
833
888
|
},
|
|
834
889
|
annotate(key, value, asArray) {
|
|
835
|
-
|
|
836
|
-
const a = this.$metadata.get(key);
|
|
837
|
-
if (Array.isArray(a)) a.push(value);
|
|
838
|
-
else this.$metadata.set(key, [a, value]);
|
|
839
|
-
} else this.$metadata.set(key, [value]);
|
|
840
|
-
else this.$metadata.set(key, value);
|
|
890
|
+
annotate(this.$metadata, key, value, asArray);
|
|
841
891
|
return this;
|
|
842
892
|
}
|
|
843
893
|
};
|
|
@@ -848,14 +898,12 @@ else this.$metadata.set(key, value);
|
|
|
848
898
|
//#region packages/typescript/src/json-schema.ts
|
|
849
899
|
function buildJsonSchema(type) {
|
|
850
900
|
const build = (def) => {
|
|
851
|
-
const t = def.type;
|
|
852
901
|
const meta = def.metadata;
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
const obj = t;
|
|
902
|
+
return forAnnotatedType(def, {
|
|
903
|
+
object(d) {
|
|
856
904
|
const properties = {};
|
|
857
905
|
const required = [];
|
|
858
|
-
for (const [key, val] of
|
|
906
|
+
for (const [key, val] of d.type.props.entries()) {
|
|
859
907
|
properties[key] = build(val);
|
|
860
908
|
if (!val.optional) required.push(key);
|
|
861
909
|
}
|
|
@@ -865,41 +913,36 @@ function buildJsonSchema(type) {
|
|
|
865
913
|
};
|
|
866
914
|
if (required.length) schema.required = required;
|
|
867
915
|
return schema;
|
|
868
|
-
}
|
|
869
|
-
|
|
870
|
-
const arr = t;
|
|
916
|
+
},
|
|
917
|
+
array(d) {
|
|
871
918
|
const schema = {
|
|
872
919
|
type: "array",
|
|
873
|
-
items: build(
|
|
920
|
+
items: build(d.type.of)
|
|
874
921
|
};
|
|
875
922
|
const minLength = meta.get("expect.minLength");
|
|
876
923
|
if (typeof minLength === "number") schema.minItems = minLength;
|
|
877
924
|
const maxLength = meta.get("expect.maxLength");
|
|
878
925
|
if (typeof maxLength === "number") schema.maxItems = maxLength;
|
|
879
926
|
return schema;
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
}
|
|
889
|
-
case "tuple": {
|
|
890
|
-
const grp = t;
|
|
927
|
+
},
|
|
928
|
+
union(d) {
|
|
929
|
+
return { anyOf: d.type.items.map(build) };
|
|
930
|
+
},
|
|
931
|
+
intersection(d) {
|
|
932
|
+
return { allOf: d.type.items.map(build) };
|
|
933
|
+
},
|
|
934
|
+
tuple(d) {
|
|
891
935
|
return {
|
|
892
936
|
type: "array",
|
|
893
|
-
items:
|
|
937
|
+
items: d.type.items.map(build),
|
|
894
938
|
additionalItems: false
|
|
895
939
|
};
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
const fin = t;
|
|
940
|
+
},
|
|
941
|
+
final(d) {
|
|
899
942
|
const schema = {};
|
|
900
|
-
if (
|
|
901
|
-
if (
|
|
902
|
-
schema.type =
|
|
943
|
+
if (d.type.value !== undefined) schema.const = d.type.value;
|
|
944
|
+
if (d.type.designType && d.type.designType !== "any") {
|
|
945
|
+
schema.type = d.type.designType === "undefined" ? "null" : d.type.designType;
|
|
903
946
|
if (schema.type === "number" && meta.get("expect.int")) schema.type = "integer";
|
|
904
947
|
}
|
|
905
948
|
if (schema.type === "string") {
|
|
@@ -919,8 +962,7 @@ else schema.allOf = (schema.allOf || []).concat(patterns.map((p) => ({ pattern:
|
|
|
919
962
|
}
|
|
920
963
|
return schema;
|
|
921
964
|
}
|
|
922
|
-
|
|
923
|
-
}
|
|
965
|
+
});
|
|
924
966
|
};
|
|
925
967
|
return build(type);
|
|
926
968
|
}
|
|
@@ -941,8 +983,8 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
941
983
|
pre() {
|
|
942
984
|
this.writeln("// prettier-ignore-start");
|
|
943
985
|
this.writeln("/* eslint-disable */");
|
|
944
|
-
const imports = ["defineAnnotatedType as $"];
|
|
945
|
-
if (
|
|
986
|
+
const imports = ["defineAnnotatedType as $", "annotate as $a"];
|
|
987
|
+
if (resolveJsonSchemaMode(this.opts) === "lazy") imports.push("buildJsonSchema as $$");
|
|
946
988
|
this.writeln(`import { ${imports.join(", ")} } from "@atscript/typescript/utils"`);
|
|
947
989
|
}
|
|
948
990
|
buildAdHocMap(annotateNodes) {
|
|
@@ -960,27 +1002,26 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
960
1002
|
post() {
|
|
961
1003
|
for (const node of this.postAnnotate) if (node.entity === "annotate") {
|
|
962
1004
|
const annotateNode = node;
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
if (
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
1005
|
+
if (annotateNode.isMutating) this.renderMutatingAnnotateNode(annotateNode);
|
|
1006
|
+
else {
|
|
1007
|
+
const unwound = this.doc.unwindType(annotateNode.targetName);
|
|
1008
|
+
if (unwound?.def) {
|
|
1009
|
+
let def = this.doc.mergeIntersection(unwound.def);
|
|
1010
|
+
if (isInterface(def)) def = def.getDefinition() || def;
|
|
1011
|
+
this._adHocAnnotations = this.buildAdHocMap([annotateNode]);
|
|
1012
|
+
this.annotateType(def, node.id);
|
|
1013
|
+
this._adHocAnnotations = null;
|
|
1014
|
+
this.indent();
|
|
1015
|
+
this.defineMetadataForAnnotateAlias(annotateNode);
|
|
1016
|
+
this.unindent();
|
|
1017
|
+
this.writeln();
|
|
1018
|
+
}
|
|
974
1019
|
}
|
|
975
1020
|
} else {
|
|
976
|
-
const mutatingNodes = this.doc.getAnnotateNodesFor(node.id).filter((n) => n.isMutating);
|
|
977
|
-
this._adHocAnnotations = this.buildAdHocMap(mutatingNodes);
|
|
978
1021
|
this.annotateType(node.getDefinition(), node.id);
|
|
979
|
-
this._adHocAnnotations = null;
|
|
980
1022
|
this.indent().defineMetadata(node).unindent();
|
|
981
1023
|
this.writeln();
|
|
982
1024
|
}
|
|
983
|
-
this.renderMutatingAnnotates();
|
|
984
1025
|
this.writeln("// prettier-ignore-end");
|
|
985
1026
|
super.post();
|
|
986
1027
|
}
|
|
@@ -1014,7 +1055,7 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
1014
1055
|
}
|
|
1015
1056
|
renderAnnotate(node) {
|
|
1016
1057
|
if (node.isMutating) {
|
|
1017
|
-
this.
|
|
1058
|
+
this.postAnnotate.push(node);
|
|
1018
1059
|
return;
|
|
1019
1060
|
}
|
|
1020
1061
|
const targetName = node.targetName;
|
|
@@ -1031,15 +1072,21 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
1031
1072
|
this.writeln();
|
|
1032
1073
|
}
|
|
1033
1074
|
renderJsonSchemaMethod(node) {
|
|
1034
|
-
|
|
1075
|
+
const mode = resolveJsonSchemaMode(this.opts);
|
|
1076
|
+
const hasAnnotation = node.countAnnotations("emit.jsonSchema") > 0;
|
|
1077
|
+
if (hasAnnotation || mode === "bundle") {
|
|
1035
1078
|
const schema = JSON.stringify(buildJsonSchema(this.toAnnotatedType(node)));
|
|
1036
1079
|
this.writeln("static toJsonSchema() {");
|
|
1037
1080
|
this.indent().writeln(`return ${schema}`).unindent();
|
|
1038
1081
|
this.writeln("}");
|
|
1039
|
-
} else {
|
|
1082
|
+
} else if (mode === "lazy") {
|
|
1040
1083
|
this.writeln("static toJsonSchema() {");
|
|
1041
1084
|
this.indent().writeln("return this._jsonSchema ?? (this._jsonSchema = $$(this))").unindent();
|
|
1042
1085
|
this.writeln("}");
|
|
1086
|
+
} else {
|
|
1087
|
+
this.writeln("static toJsonSchema() {");
|
|
1088
|
+
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();
|
|
1089
|
+
this.writeln("}");
|
|
1043
1090
|
}
|
|
1044
1091
|
}
|
|
1045
1092
|
toAnnotatedType(node) {
|
|
@@ -1302,18 +1349,13 @@ else handle.prop(prop.id, propHandle.$type);
|
|
|
1302
1349
|
return this;
|
|
1303
1350
|
}
|
|
1304
1351
|
defineMetadata(node) {
|
|
1305
|
-
|
|
1306
|
-
let adHocNames;
|
|
1307
|
-
let adHoc;
|
|
1352
|
+
let annotations = this.doc.evalAnnotationsForNode(node);
|
|
1308
1353
|
if (this._adHocAnnotations && this._propPath.length > 0) {
|
|
1309
1354
|
const path$1 = this._propPath.join(".");
|
|
1310
|
-
adHoc = this._adHocAnnotations.get(path$1);
|
|
1311
|
-
if (adHoc)
|
|
1355
|
+
const adHoc = this._adHocAnnotations.get(path$1);
|
|
1356
|
+
if (adHoc) annotations = this.doc.mergeNodesAnnotations(annotations, adHoc);
|
|
1312
1357
|
}
|
|
1313
1358
|
annotations?.forEach((an) => {
|
|
1314
|
-
if (!adHocNames || !adHocNames.has(an.name)) this.resolveAnnotationValue(node, an);
|
|
1315
|
-
});
|
|
1316
|
-
adHoc?.forEach((an) => {
|
|
1317
1359
|
this.resolveAnnotationValue(node, an);
|
|
1318
1360
|
});
|
|
1319
1361
|
return this;
|
|
@@ -1325,11 +1367,8 @@ else handle.prop(prop.id, propHandle.$type);
|
|
|
1325
1367
|
const annotateAnnotations = this.doc.evalAnnotationsForNode(annotateNode);
|
|
1326
1368
|
const targetDecl = this.doc.getDeclarationOwnerNode(annotateNode.targetName);
|
|
1327
1369
|
const targetAnnotations = targetDecl?.node ? targetDecl.doc.evalAnnotationsForNode(targetDecl.node) : undefined;
|
|
1328
|
-
const
|
|
1329
|
-
|
|
1330
|
-
if (!overriddenNames.has(an.name)) this.resolveAnnotationValue(annotateNode, an);
|
|
1331
|
-
});
|
|
1332
|
-
annotateAnnotations?.forEach((an) => {
|
|
1370
|
+
const merged = this.doc.mergeNodesAnnotations(targetAnnotations, annotateAnnotations);
|
|
1371
|
+
merged.forEach((an) => {
|
|
1333
1372
|
this.resolveAnnotationValue(annotateNode, an);
|
|
1334
1373
|
});
|
|
1335
1374
|
return this;
|
|
@@ -1368,52 +1407,132 @@ else targetValue = "true";
|
|
|
1368
1407
|
multiple: !!multiple
|
|
1369
1408
|
};
|
|
1370
1409
|
}
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1410
|
+
renderMutatingAnnotateNode(node) {
|
|
1411
|
+
const targetName = node.targetName;
|
|
1412
|
+
const targetDef = this.resolveTargetDef(targetName);
|
|
1413
|
+
this.writeln("// Ad-hoc annotations for ", targetName);
|
|
1414
|
+
for (const entry of node.entries) {
|
|
1415
|
+
const anns = entry.annotations;
|
|
1416
|
+
if (!anns || anns.length === 0) continue;
|
|
1417
|
+
const parts = entry.hasChain ? [entry.id, ...entry.chain.map((c) => c.text)] : [entry.id];
|
|
1418
|
+
const accessors = this.buildMutatingAccessors(targetName, targetDef, parts);
|
|
1419
|
+
for (const accessor of accessors) {
|
|
1420
|
+
const cleared = new Set();
|
|
1380
1421
|
for (const an of anns) {
|
|
1381
1422
|
const { value, multiple } = this.computeAnnotationValue(entry, an);
|
|
1382
1423
|
if (multiple) {
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
this.writeln(
|
|
1389
|
-
|
|
1390
|
-
this.writeln(`}`);
|
|
1391
|
-
} else this.writeln(`${accessor}.metadata.set("${escapeQuotes(an.name)}", ${value})`);
|
|
1424
|
+
if (!cleared.has(an.name)) {
|
|
1425
|
+
const spec = this.doc.resolveAnnotation(an.name);
|
|
1426
|
+
if (!spec || spec.config.mergeStrategy !== "append") this.writeln(`${accessor}.metadata.delete("${escapeQuotes(an.name)}")`);
|
|
1427
|
+
cleared.add(an.name);
|
|
1428
|
+
}
|
|
1429
|
+
this.writeln(`$a(${accessor}.metadata, "${escapeQuotes(an.name)}", ${value}, true)`);
|
|
1430
|
+
} else this.writeln(`$a(${accessor}.metadata, "${escapeQuotes(an.name)}", ${value})`);
|
|
1392
1431
|
}
|
|
1393
1432
|
}
|
|
1394
|
-
|
|
1395
|
-
|
|
1433
|
+
}
|
|
1434
|
+
const topAnnotations = node.annotations;
|
|
1435
|
+
if (topAnnotations && topAnnotations.length > 0) {
|
|
1436
|
+
const cleared = new Set();
|
|
1437
|
+
for (const an of topAnnotations) {
|
|
1396
1438
|
const { value, multiple } = this.computeAnnotationValue(node, an);
|
|
1397
1439
|
if (multiple) {
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
this.writeln(
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1440
|
+
if (!cleared.has(an.name)) {
|
|
1441
|
+
const spec = this.doc.resolveAnnotation(an.name);
|
|
1442
|
+
if (!spec || spec.config.mergeStrategy !== "append") this.writeln(`${targetName}.metadata.delete("${escapeQuotes(an.name)}")`);
|
|
1443
|
+
cleared.add(an.name);
|
|
1444
|
+
}
|
|
1445
|
+
this.writeln(`$a(${targetName}.metadata, "${escapeQuotes(an.name)}", ${value}, true)`);
|
|
1446
|
+
} else this.writeln(`$a(${targetName}.metadata, "${escapeQuotes(an.name)}", ${value})`);
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
this.writeln();
|
|
1450
|
+
}
|
|
1451
|
+
resolveTargetDef(targetName) {
|
|
1452
|
+
const unwound = this.doc.unwindType(targetName);
|
|
1453
|
+
if (!unwound?.def) return undefined;
|
|
1454
|
+
let def = unwound.def;
|
|
1455
|
+
if (isInterface(def)) def = def.getDefinition() || def;
|
|
1456
|
+
return def;
|
|
1457
|
+
}
|
|
1458
|
+
/**
|
|
1459
|
+
* Builds the runtime accessor paths for mutating annotate entries.
|
|
1460
|
+
* Computes exact paths at compile time by walking the AST,
|
|
1461
|
+
* so the generated JS accesses props directly without runtime search.
|
|
1462
|
+
* Returns multiple paths when a property appears in multiple union branches.
|
|
1463
|
+
*/ buildMutatingAccessors(targetName, targetDef, parts) {
|
|
1464
|
+
let accessors = [{
|
|
1465
|
+
prefix: targetName + ".type",
|
|
1466
|
+
def: targetDef
|
|
1467
|
+
}];
|
|
1468
|
+
for (let i = 0; i < parts.length; i++) {
|
|
1469
|
+
const nextAccessors = [];
|
|
1470
|
+
for (const { prefix, def } of accessors) {
|
|
1471
|
+
const results = this.buildPropPaths(def, parts[i]);
|
|
1472
|
+
if (results.length > 0) for (const result of results) if (i < parts.length - 1) nextAccessors.push({
|
|
1473
|
+
prefix: prefix + result.path + "?.type",
|
|
1474
|
+
def: result.propDef
|
|
1475
|
+
});
|
|
1476
|
+
else nextAccessors.push({
|
|
1477
|
+
prefix: prefix + result.path + "?",
|
|
1478
|
+
def: result.propDef
|
|
1479
|
+
});
|
|
1480
|
+
else {
|
|
1481
|
+
const suffix = `.props.get("${escapeQuotes(parts[i])}")` + (i < parts.length - 1 ? "?.type" : "?");
|
|
1482
|
+
nextAccessors.push({
|
|
1483
|
+
prefix: prefix + suffix,
|
|
1484
|
+
def: undefined
|
|
1485
|
+
});
|
|
1486
|
+
}
|
|
1407
1487
|
}
|
|
1488
|
+
accessors = nextAccessors;
|
|
1489
|
+
}
|
|
1490
|
+
return accessors.map((a) => a.prefix);
|
|
1491
|
+
}
|
|
1492
|
+
/**
|
|
1493
|
+
* Finds a property in a type tree at compile time, returning all
|
|
1494
|
+
* matching runtime path strings and prop definitions for further chaining.
|
|
1495
|
+
* Returns multiple results when the same property appears in different union branches.
|
|
1496
|
+
*/ buildPropPaths(def, propName) {
|
|
1497
|
+
if (!def) return [];
|
|
1498
|
+
def = this.doc.mergeIntersection(def);
|
|
1499
|
+
if (isRef(def)) {
|
|
1500
|
+
const ref = def;
|
|
1501
|
+
const unwound = this.doc.unwindType(ref.id, ref.chain)?.def;
|
|
1502
|
+
return this.buildPropPaths(unwound, propName);
|
|
1503
|
+
}
|
|
1504
|
+
if (isInterface(def)) return this.buildPropPaths(def.getDefinition(), propName);
|
|
1505
|
+
if (isStructure(def)) {
|
|
1506
|
+
const prop = def.props.get(propName);
|
|
1507
|
+
if (prop) return [{
|
|
1508
|
+
path: `.props.get("${escapeQuotes(propName)}")`,
|
|
1509
|
+
propDef: prop.getDefinition()
|
|
1510
|
+
}];
|
|
1511
|
+
return [];
|
|
1408
1512
|
}
|
|
1513
|
+
if (isGroup(def)) {
|
|
1514
|
+
const group = def;
|
|
1515
|
+
const items = group.unwrap();
|
|
1516
|
+
const results = [];
|
|
1517
|
+
for (let i = 0; i < items.length; i++) for (const result of this.buildPropPaths(items[i], propName)) results.push({
|
|
1518
|
+
path: `.items[${i}].type${result.path}`,
|
|
1519
|
+
propDef: result.propDef
|
|
1520
|
+
});
|
|
1521
|
+
return results;
|
|
1522
|
+
}
|
|
1523
|
+
return [];
|
|
1409
1524
|
}
|
|
1410
1525
|
constructor(doc, opts) {
|
|
1411
|
-
super(doc), _define_property(this, "opts", void 0), _define_property(this, "postAnnotate", void 0), _define_property(this, "
|
|
1526
|
+
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 = [];
|
|
1412
1527
|
}
|
|
1413
1528
|
};
|
|
1414
1529
|
|
|
1415
1530
|
//#endregion
|
|
1416
1531
|
//#region packages/typescript/src/plugin.ts
|
|
1532
|
+
function resolveJsonSchemaMode(opts) {
|
|
1533
|
+
if (opts?.jsonSchema !== undefined) return opts.jsonSchema;
|
|
1534
|
+
return false;
|
|
1535
|
+
}
|
|
1417
1536
|
const tsPlugin = (opts) => {
|
|
1418
1537
|
return {
|
|
1419
1538
|
name: "typesccript",
|