@atscript/typescript 0.1.0 → 0.1.2
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 +174 -88
- package/dist/index.cjs +174 -88
- package/dist/index.mjs +174 -88
- package/dist/utils.cjs +11 -6
- package/dist/utils.d.ts +11 -5
- package/dist/utils.mjs +11 -7
- package/package.json +2 -2
package/dist/cli.cjs
CHANGED
|
@@ -325,7 +325,7 @@ var TypeRenderer = class TypeRenderer extends BaseRenderer {
|
|
|
325
325
|
this.writeln("static __is_atscript_annotated_type: true");
|
|
326
326
|
this.writeln(`static type: TAtscriptTypeObject<keyof ${asClass}>`);
|
|
327
327
|
this.writeln(`static metadata: TMetadataMap<AtscriptMetadata>`);
|
|
328
|
-
this.writeln(`static validator: <TT extends TAtscriptAnnotatedTypeConstructor = ${asClass}>(opts?: Partial<TValidatorOptions>) => Validator<TT>`);
|
|
328
|
+
this.writeln(`static validator: <TT extends TAtscriptAnnotatedTypeConstructor = typeof ${asClass}>(opts?: Partial<TValidatorOptions>) => Validator<TT>`);
|
|
329
329
|
this.writeln("static toJsonSchema: () => any");
|
|
330
330
|
}
|
|
331
331
|
this.pop();
|
|
@@ -354,33 +354,33 @@ else this.writeln("{}");
|
|
|
354
354
|
renderAnnotate(node) {
|
|
355
355
|
if (node.isMutating) return;
|
|
356
356
|
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
357
|
this.writeln();
|
|
361
358
|
const exported = node.token("export")?.text === "export";
|
|
362
359
|
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 {
|
|
360
|
+
if (this.isTypeTarget(targetName)) {
|
|
368
361
|
this.write(exported ? "export " : "declare ");
|
|
369
|
-
this.write(`type ${node.id} = `);
|
|
370
|
-
this.
|
|
362
|
+
this.write(`type ${node.id} = ${targetName}`);
|
|
363
|
+
this.writeln();
|
|
364
|
+
const unwound = this.doc.unwindType(targetName);
|
|
365
|
+
this.renderTypeNamespaceFor(node.id, unwound?.def);
|
|
366
|
+
} else {
|
|
367
|
+
this.write(exported ? "export declare " : "declare ");
|
|
368
|
+
this.writeln(`class ${node.id} extends ${targetName} {}`);
|
|
369
|
+
this.writeln();
|
|
371
370
|
}
|
|
372
|
-
this.writeln();
|
|
373
371
|
}
|
|
374
372
|
renderTypeNamespace(node) {
|
|
375
|
-
this.
|
|
373
|
+
this.renderTypeNamespaceFor(node.id, node.getDefinition());
|
|
374
|
+
}
|
|
375
|
+
renderTypeNamespaceFor(name, inputDef) {
|
|
376
|
+
this.write(`declare namespace ${name} `);
|
|
376
377
|
this.blockln("{}");
|
|
377
|
-
const def = node.getDefinition();
|
|
378
378
|
let typeDef = "TAtscriptTypeDef";
|
|
379
|
-
if (
|
|
380
|
-
let realDef =
|
|
381
|
-
if ((0, __atscript_core.isRef)(
|
|
379
|
+
if (inputDef) {
|
|
380
|
+
let realDef = inputDef;
|
|
381
|
+
if ((0, __atscript_core.isRef)(inputDef)) realDef = this.doc.unwindType(inputDef.id, inputDef.chain)?.def || realDef;
|
|
382
382
|
realDef = this.doc.mergeIntersection(realDef);
|
|
383
|
-
if ((0, __atscript_core.isStructure)(realDef) || (0, __atscript_core.isInterface)(realDef)) typeDef = `TAtscriptTypeObject<keyof ${
|
|
383
|
+
if ((0, __atscript_core.isStructure)(realDef) || (0, __atscript_core.isInterface)(realDef)) typeDef = `TAtscriptTypeObject<keyof ${name}>`;
|
|
384
384
|
else if ((0, __atscript_core.isGroup)(realDef)) typeDef = "TAtscriptTypeComplex";
|
|
385
385
|
else if ((0, __atscript_core.isArray)(realDef)) typeDef = "TAtscriptTypeArray";
|
|
386
386
|
else if ((0, __atscript_core.isPrimitive)(realDef)) typeDef = "TAtscriptTypeFinal";
|
|
@@ -388,10 +388,19 @@ else if ((0, __atscript_core.isPrimitive)(realDef)) typeDef = "TAtscriptTypeFina
|
|
|
388
388
|
this.writeln(`const __is_atscript_annotated_type: true`);
|
|
389
389
|
this.writeln(`const type: ${typeDef}`);
|
|
390
390
|
this.writeln(`const metadata: TMetadataMap<AtscriptMetadata>`);
|
|
391
|
-
this.writeln(`const validator:
|
|
391
|
+
this.writeln(`const validator: (opts?: Partial<TValidatorOptions>) => Validator<TAtscriptAnnotatedTypeConstructor, ${name}>`);
|
|
392
392
|
this.writeln("const toJsonSchema: () => any");
|
|
393
393
|
this.popln();
|
|
394
394
|
}
|
|
395
|
+
isTypeTarget(name, doc) {
|
|
396
|
+
const d = doc || this.doc;
|
|
397
|
+
const decl = d.getDeclarationOwnerNode(name);
|
|
398
|
+
if (!decl?.node) return false;
|
|
399
|
+
if (decl.node.entity === "type") return true;
|
|
400
|
+
if (decl.node.entity === "interface") return false;
|
|
401
|
+
if (decl.node.entity === "annotate") return this.isTypeTarget(decl.node.targetName, decl.doc);
|
|
402
|
+
return false;
|
|
403
|
+
}
|
|
395
404
|
renderJsDoc(node) {
|
|
396
405
|
const range = node.token("identifier")?.range;
|
|
397
406
|
const rangeStr = range ? `:${range.start.line + 1}:${range.start.character + 1}` : "";
|
|
@@ -760,6 +769,15 @@ var ValidatorError = class extends Error {
|
|
|
760
769
|
function isAnnotatedType(type) {
|
|
761
770
|
return type && type.__is_atscript_annotated_type;
|
|
762
771
|
}
|
|
772
|
+
function annotate(metadata, key, value, asArray) {
|
|
773
|
+
if (!metadata) return;
|
|
774
|
+
if (asArray) if (metadata.has(key)) {
|
|
775
|
+
const a = metadata.get(key);
|
|
776
|
+
if (Array.isArray(a)) a.push(value);
|
|
777
|
+
else metadata.set(key, [a, value]);
|
|
778
|
+
} else metadata.set(key, [value]);
|
|
779
|
+
else metadata.set(key, value);
|
|
780
|
+
}
|
|
763
781
|
function defineAnnotatedType(_kind, base) {
|
|
764
782
|
const kind = _kind || "";
|
|
765
783
|
const type = base?.type || {};
|
|
@@ -859,12 +877,7 @@ else if (!newBase) throw new Error(`"${typeName}" is not annotated type`);
|
|
|
859
877
|
return this;
|
|
860
878
|
},
|
|
861
879
|
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);
|
|
880
|
+
annotate(this.$metadata, key, value, asArray);
|
|
868
881
|
return this;
|
|
869
882
|
}
|
|
870
883
|
};
|
|
@@ -968,7 +981,7 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
968
981
|
pre() {
|
|
969
982
|
this.writeln("// prettier-ignore-start");
|
|
970
983
|
this.writeln("/* eslint-disable */");
|
|
971
|
-
const imports = ["defineAnnotatedType as $"];
|
|
984
|
+
const imports = ["defineAnnotatedType as $", "annotate as $a"];
|
|
972
985
|
if (!this.opts?.preRenderJsonSchema) imports.push("buildJsonSchema as $$");
|
|
973
986
|
this.writeln(`import { ${imports.join(", ")} } from "@atscript/typescript/utils"`);
|
|
974
987
|
}
|
|
@@ -987,37 +1000,42 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
987
1000
|
post() {
|
|
988
1001
|
for (const node of this.postAnnotate) if (node.entity === "annotate") {
|
|
989
1002
|
const annotateNode = node;
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
if (
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1003
|
+
if (annotateNode.isMutating) this.renderMutatingAnnotateNode(annotateNode);
|
|
1004
|
+
else {
|
|
1005
|
+
const unwound = this.doc.unwindType(annotateNode.targetName);
|
|
1006
|
+
if (unwound?.def) {
|
|
1007
|
+
let def = this.doc.mergeIntersection(unwound.def);
|
|
1008
|
+
if ((0, __atscript_core.isInterface)(def)) def = def.getDefinition() || def;
|
|
1009
|
+
this._adHocAnnotations = this.buildAdHocMap([annotateNode]);
|
|
1010
|
+
this.annotateType(def, node.id);
|
|
1011
|
+
this._adHocAnnotations = null;
|
|
1012
|
+
this.indent();
|
|
1013
|
+
this.defineMetadataForAnnotateAlias(annotateNode);
|
|
1014
|
+
this.unindent();
|
|
1015
|
+
this.writeln();
|
|
1016
|
+
}
|
|
1001
1017
|
}
|
|
1002
1018
|
} else {
|
|
1003
|
-
const mutatingNodes = this.doc.getAnnotateNodesFor(node.id).filter((n) => n.isMutating);
|
|
1004
|
-
this._adHocAnnotations = this.buildAdHocMap(mutatingNodes);
|
|
1005
1019
|
this.annotateType(node.getDefinition(), node.id);
|
|
1006
|
-
this._adHocAnnotations = null;
|
|
1007
1020
|
this.indent().defineMetadata(node).unindent();
|
|
1008
1021
|
this.writeln();
|
|
1009
1022
|
}
|
|
1010
|
-
this.renderMutatingAnnotates();
|
|
1011
1023
|
this.writeln("// prettier-ignore-end");
|
|
1012
1024
|
super.post();
|
|
1013
1025
|
}
|
|
1026
|
+
renderClassStatics(node) {
|
|
1027
|
+
this.writeln("static __is_atscript_annotated_type = true");
|
|
1028
|
+
this.writeln("static type = {}");
|
|
1029
|
+
this.writeln("static metadata = new Map()");
|
|
1030
|
+
this.renderJsonSchemaMethod(node);
|
|
1031
|
+
}
|
|
1014
1032
|
renderInterface(node) {
|
|
1015
1033
|
this.writeln();
|
|
1016
1034
|
const exported = node.token("export")?.text === "export";
|
|
1017
1035
|
this.write(exported ? "export " : "");
|
|
1018
1036
|
this.write(`class ${node.id} `);
|
|
1019
1037
|
this.blockln("{}");
|
|
1020
|
-
this.
|
|
1038
|
+
this.renderClassStatics(node);
|
|
1021
1039
|
this.popln();
|
|
1022
1040
|
this.postAnnotate.push(node);
|
|
1023
1041
|
this.writeln();
|
|
@@ -1028,14 +1046,14 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
1028
1046
|
this.write(exported ? "export " : "");
|
|
1029
1047
|
this.write(`class ${node.id} `);
|
|
1030
1048
|
this.blockln("{}");
|
|
1031
|
-
this.
|
|
1049
|
+
this.renderClassStatics(node);
|
|
1032
1050
|
this.popln();
|
|
1033
1051
|
this.postAnnotate.push(node);
|
|
1034
1052
|
this.writeln();
|
|
1035
1053
|
}
|
|
1036
1054
|
renderAnnotate(node) {
|
|
1037
1055
|
if (node.isMutating) {
|
|
1038
|
-
this.
|
|
1056
|
+
this.postAnnotate.push(node);
|
|
1039
1057
|
return;
|
|
1040
1058
|
}
|
|
1041
1059
|
const targetName = node.targetName;
|
|
@@ -1046,7 +1064,7 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
1046
1064
|
this.write(exported ? "export " : "");
|
|
1047
1065
|
this.write(`class ${node.id} `);
|
|
1048
1066
|
this.blockln("{}");
|
|
1049
|
-
this.
|
|
1067
|
+
this.renderClassStatics(node);
|
|
1050
1068
|
this.popln();
|
|
1051
1069
|
this.postAnnotate.push(node);
|
|
1052
1070
|
this.writeln();
|
|
@@ -1323,18 +1341,13 @@ else handle.prop(prop.id, propHandle.$type);
|
|
|
1323
1341
|
return this;
|
|
1324
1342
|
}
|
|
1325
1343
|
defineMetadata(node) {
|
|
1326
|
-
|
|
1327
|
-
let adHocNames;
|
|
1328
|
-
let adHoc;
|
|
1344
|
+
let annotations = this.doc.evalAnnotationsForNode(node);
|
|
1329
1345
|
if (this._adHocAnnotations && this._propPath.length > 0) {
|
|
1330
1346
|
const path$3 = this._propPath.join(".");
|
|
1331
|
-
adHoc = this._adHocAnnotations.get(path$3);
|
|
1332
|
-
if (adHoc)
|
|
1347
|
+
const adHoc = this._adHocAnnotations.get(path$3);
|
|
1348
|
+
if (adHoc) annotations = this.doc.mergeNodesAnnotations(annotations, adHoc);
|
|
1333
1349
|
}
|
|
1334
1350
|
annotations?.forEach((an) => {
|
|
1335
|
-
if (!adHocNames || !adHocNames.has(an.name)) this.resolveAnnotationValue(node, an);
|
|
1336
|
-
});
|
|
1337
|
-
adHoc?.forEach((an) => {
|
|
1338
1351
|
this.resolveAnnotationValue(node, an);
|
|
1339
1352
|
});
|
|
1340
1353
|
return this;
|
|
@@ -1346,11 +1359,8 @@ else handle.prop(prop.id, propHandle.$type);
|
|
|
1346
1359
|
const annotateAnnotations = this.doc.evalAnnotationsForNode(annotateNode);
|
|
1347
1360
|
const targetDecl = this.doc.getDeclarationOwnerNode(annotateNode.targetName);
|
|
1348
1361
|
const targetAnnotations = targetDecl?.node ? targetDecl.doc.evalAnnotationsForNode(targetDecl.node) : undefined;
|
|
1349
|
-
const
|
|
1350
|
-
|
|
1351
|
-
if (!overriddenNames.has(an.name)) this.resolveAnnotationValue(annotateNode, an);
|
|
1352
|
-
});
|
|
1353
|
-
annotateAnnotations?.forEach((an) => {
|
|
1362
|
+
const merged = this.doc.mergeNodesAnnotations(targetAnnotations, annotateAnnotations);
|
|
1363
|
+
merged.forEach((an) => {
|
|
1354
1364
|
this.resolveAnnotationValue(annotateNode, an);
|
|
1355
1365
|
});
|
|
1356
1366
|
return this;
|
|
@@ -1389,47 +1399,123 @@ else targetValue = "true";
|
|
|
1389
1399
|
multiple: !!multiple
|
|
1390
1400
|
};
|
|
1391
1401
|
}
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1402
|
+
renderMutatingAnnotateNode(node) {
|
|
1403
|
+
const targetName = node.targetName;
|
|
1404
|
+
const targetDef = this.resolveTargetDef(targetName);
|
|
1405
|
+
this.writeln("// Ad-hoc annotations for ", targetName);
|
|
1406
|
+
for (const entry of node.entries) {
|
|
1407
|
+
const anns = entry.annotations;
|
|
1408
|
+
if (!anns || anns.length === 0) continue;
|
|
1409
|
+
const parts = entry.hasChain ? [entry.id, ...entry.chain.map((c) => c.text)] : [entry.id];
|
|
1410
|
+
const accessors = this.buildMutatingAccessors(targetName, targetDef, parts);
|
|
1411
|
+
for (const accessor of accessors) {
|
|
1412
|
+
const cleared = new Set();
|
|
1401
1413
|
for (const an of anns) {
|
|
1402
1414
|
const { value, multiple } = this.computeAnnotationValue(entry, an);
|
|
1403
1415
|
if (multiple) {
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
this.writeln(
|
|
1410
|
-
|
|
1411
|
-
this.writeln(`}`);
|
|
1412
|
-
} else this.writeln(`${accessor}.metadata.set("${escapeQuotes(an.name)}", ${value})`);
|
|
1416
|
+
if (!cleared.has(an.name)) {
|
|
1417
|
+
const spec = this.doc.resolveAnnotation(an.name);
|
|
1418
|
+
if (!spec || spec.config.mergeStrategy !== "append") this.writeln(`${accessor}.metadata.delete("${escapeQuotes(an.name)}")`);
|
|
1419
|
+
cleared.add(an.name);
|
|
1420
|
+
}
|
|
1421
|
+
this.writeln(`$a(${accessor}.metadata, "${escapeQuotes(an.name)}", ${value}, true)`);
|
|
1422
|
+
} else this.writeln(`$a(${accessor}.metadata, "${escapeQuotes(an.name)}", ${value})`);
|
|
1413
1423
|
}
|
|
1414
1424
|
}
|
|
1415
|
-
|
|
1416
|
-
|
|
1425
|
+
}
|
|
1426
|
+
const topAnnotations = node.annotations;
|
|
1427
|
+
if (topAnnotations && topAnnotations.length > 0) {
|
|
1428
|
+
const cleared = new Set();
|
|
1429
|
+
for (const an of topAnnotations) {
|
|
1417
1430
|
const { value, multiple } = this.computeAnnotationValue(node, an);
|
|
1418
1431
|
if (multiple) {
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
this.writeln(
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1432
|
+
if (!cleared.has(an.name)) {
|
|
1433
|
+
const spec = this.doc.resolveAnnotation(an.name);
|
|
1434
|
+
if (!spec || spec.config.mergeStrategy !== "append") this.writeln(`${targetName}.metadata.delete("${escapeQuotes(an.name)}")`);
|
|
1435
|
+
cleared.add(an.name);
|
|
1436
|
+
}
|
|
1437
|
+
this.writeln(`$a(${targetName}.metadata, "${escapeQuotes(an.name)}", ${value}, true)`);
|
|
1438
|
+
} else this.writeln(`$a(${targetName}.metadata, "${escapeQuotes(an.name)}", ${value})`);
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
this.writeln();
|
|
1442
|
+
}
|
|
1443
|
+
resolveTargetDef(targetName) {
|
|
1444
|
+
const unwound = this.doc.unwindType(targetName);
|
|
1445
|
+
if (!unwound?.def) return undefined;
|
|
1446
|
+
let def = unwound.def;
|
|
1447
|
+
if ((0, __atscript_core.isInterface)(def)) def = def.getDefinition() || def;
|
|
1448
|
+
return def;
|
|
1449
|
+
}
|
|
1450
|
+
/**
|
|
1451
|
+
* Builds the runtime accessor paths for mutating annotate entries.
|
|
1452
|
+
* Computes exact paths at compile time by walking the AST,
|
|
1453
|
+
* so the generated JS accesses props directly without runtime search.
|
|
1454
|
+
* Returns multiple paths when a property appears in multiple union branches.
|
|
1455
|
+
*/ buildMutatingAccessors(targetName, targetDef, parts) {
|
|
1456
|
+
let accessors = [{
|
|
1457
|
+
prefix: targetName + ".type",
|
|
1458
|
+
def: targetDef
|
|
1459
|
+
}];
|
|
1460
|
+
for (let i = 0; i < parts.length; i++) {
|
|
1461
|
+
const nextAccessors = [];
|
|
1462
|
+
for (const { prefix, def } of accessors) {
|
|
1463
|
+
const results = this.buildPropPaths(def, parts[i]);
|
|
1464
|
+
if (results.length > 0) for (const result of results) if (i < parts.length - 1) nextAccessors.push({
|
|
1465
|
+
prefix: prefix + result.path + "?.type",
|
|
1466
|
+
def: result.propDef
|
|
1467
|
+
});
|
|
1468
|
+
else nextAccessors.push({
|
|
1469
|
+
prefix: prefix + result.path + "?",
|
|
1470
|
+
def: result.propDef
|
|
1471
|
+
});
|
|
1472
|
+
else {
|
|
1473
|
+
const suffix = `.props.get("${escapeQuotes(parts[i])}")` + (i < parts.length - 1 ? "?.type" : "?");
|
|
1474
|
+
nextAccessors.push({
|
|
1475
|
+
prefix: prefix + suffix,
|
|
1476
|
+
def: undefined
|
|
1477
|
+
});
|
|
1478
|
+
}
|
|
1428
1479
|
}
|
|
1480
|
+
accessors = nextAccessors;
|
|
1481
|
+
}
|
|
1482
|
+
return accessors.map((a) => a.prefix);
|
|
1483
|
+
}
|
|
1484
|
+
/**
|
|
1485
|
+
* Finds a property in a type tree at compile time, returning all
|
|
1486
|
+
* matching runtime path strings and prop definitions for further chaining.
|
|
1487
|
+
* Returns multiple results when the same property appears in different union branches.
|
|
1488
|
+
*/ buildPropPaths(def, propName) {
|
|
1489
|
+
if (!def) return [];
|
|
1490
|
+
def = this.doc.mergeIntersection(def);
|
|
1491
|
+
if ((0, __atscript_core.isRef)(def)) {
|
|
1492
|
+
const ref = def;
|
|
1493
|
+
const unwound = this.doc.unwindType(ref.id, ref.chain)?.def;
|
|
1494
|
+
return this.buildPropPaths(unwound, propName);
|
|
1495
|
+
}
|
|
1496
|
+
if ((0, __atscript_core.isInterface)(def)) return this.buildPropPaths(def.getDefinition(), propName);
|
|
1497
|
+
if ((0, __atscript_core.isStructure)(def)) {
|
|
1498
|
+
const prop = def.props.get(propName);
|
|
1499
|
+
if (prop) return [{
|
|
1500
|
+
path: `.props.get("${escapeQuotes(propName)}")`,
|
|
1501
|
+
propDef: prop.getDefinition()
|
|
1502
|
+
}];
|
|
1503
|
+
return [];
|
|
1504
|
+
}
|
|
1505
|
+
if ((0, __atscript_core.isGroup)(def)) {
|
|
1506
|
+
const group = def;
|
|
1507
|
+
const items = group.unwrap();
|
|
1508
|
+
const results = [];
|
|
1509
|
+
for (let i = 0; i < items.length; i++) for (const result of this.buildPropPaths(items[i], propName)) results.push({
|
|
1510
|
+
path: `.items[${i}].type${result.path}`,
|
|
1511
|
+
propDef: result.propDef
|
|
1512
|
+
});
|
|
1513
|
+
return results;
|
|
1429
1514
|
}
|
|
1515
|
+
return [];
|
|
1430
1516
|
}
|
|
1431
1517
|
constructor(doc, opts) {
|
|
1432
|
-
super(doc), _define_property$1(this, "opts", void 0), _define_property$1(this, "postAnnotate", void 0), _define_property$1(this, "
|
|
1518
|
+
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 = [];
|
|
1433
1519
|
}
|
|
1434
1520
|
};
|
|
1435
1521
|
|
package/dist/index.cjs
CHANGED
|
@@ -322,7 +322,7 @@ var TypeRenderer = class TypeRenderer extends BaseRenderer {
|
|
|
322
322
|
this.writeln("static __is_atscript_annotated_type: true");
|
|
323
323
|
this.writeln(`static type: TAtscriptTypeObject<keyof ${asClass}>`);
|
|
324
324
|
this.writeln(`static metadata: TMetadataMap<AtscriptMetadata>`);
|
|
325
|
-
this.writeln(`static validator: <TT extends TAtscriptAnnotatedTypeConstructor = ${asClass}>(opts?: Partial<TValidatorOptions>) => Validator<TT>`);
|
|
325
|
+
this.writeln(`static validator: <TT extends TAtscriptAnnotatedTypeConstructor = typeof ${asClass}>(opts?: Partial<TValidatorOptions>) => Validator<TT>`);
|
|
326
326
|
this.writeln("static toJsonSchema: () => any");
|
|
327
327
|
}
|
|
328
328
|
this.pop();
|
|
@@ -351,33 +351,33 @@ else this.writeln("{}");
|
|
|
351
351
|
renderAnnotate(node) {
|
|
352
352
|
if (node.isMutating) return;
|
|
353
353
|
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
354
|
this.writeln();
|
|
358
355
|
const exported = node.token("export")?.text === "export";
|
|
359
356
|
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 {
|
|
357
|
+
if (this.isTypeTarget(targetName)) {
|
|
365
358
|
this.write(exported ? "export " : "declare ");
|
|
366
|
-
this.write(`type ${node.id} = `);
|
|
367
|
-
this.
|
|
359
|
+
this.write(`type ${node.id} = ${targetName}`);
|
|
360
|
+
this.writeln();
|
|
361
|
+
const unwound = this.doc.unwindType(targetName);
|
|
362
|
+
this.renderTypeNamespaceFor(node.id, unwound?.def);
|
|
363
|
+
} else {
|
|
364
|
+
this.write(exported ? "export declare " : "declare ");
|
|
365
|
+
this.writeln(`class ${node.id} extends ${targetName} {}`);
|
|
366
|
+
this.writeln();
|
|
368
367
|
}
|
|
369
|
-
this.writeln();
|
|
370
368
|
}
|
|
371
369
|
renderTypeNamespace(node) {
|
|
372
|
-
this.
|
|
370
|
+
this.renderTypeNamespaceFor(node.id, node.getDefinition());
|
|
371
|
+
}
|
|
372
|
+
renderTypeNamespaceFor(name, inputDef) {
|
|
373
|
+
this.write(`declare namespace ${name} `);
|
|
373
374
|
this.blockln("{}");
|
|
374
|
-
const def = node.getDefinition();
|
|
375
375
|
let typeDef = "TAtscriptTypeDef";
|
|
376
|
-
if (
|
|
377
|
-
let realDef =
|
|
378
|
-
if ((0, __atscript_core.isRef)(
|
|
376
|
+
if (inputDef) {
|
|
377
|
+
let realDef = inputDef;
|
|
378
|
+
if ((0, __atscript_core.isRef)(inputDef)) realDef = this.doc.unwindType(inputDef.id, inputDef.chain)?.def || realDef;
|
|
379
379
|
realDef = this.doc.mergeIntersection(realDef);
|
|
380
|
-
if ((0, __atscript_core.isStructure)(realDef) || (0, __atscript_core.isInterface)(realDef)) typeDef = `TAtscriptTypeObject<keyof ${
|
|
380
|
+
if ((0, __atscript_core.isStructure)(realDef) || (0, __atscript_core.isInterface)(realDef)) typeDef = `TAtscriptTypeObject<keyof ${name}>`;
|
|
381
381
|
else if ((0, __atscript_core.isGroup)(realDef)) typeDef = "TAtscriptTypeComplex";
|
|
382
382
|
else if ((0, __atscript_core.isArray)(realDef)) typeDef = "TAtscriptTypeArray";
|
|
383
383
|
else if ((0, __atscript_core.isPrimitive)(realDef)) typeDef = "TAtscriptTypeFinal";
|
|
@@ -385,10 +385,19 @@ else if ((0, __atscript_core.isPrimitive)(realDef)) typeDef = "TAtscriptTypeFina
|
|
|
385
385
|
this.writeln(`const __is_atscript_annotated_type: true`);
|
|
386
386
|
this.writeln(`const type: ${typeDef}`);
|
|
387
387
|
this.writeln(`const metadata: TMetadataMap<AtscriptMetadata>`);
|
|
388
|
-
this.writeln(`const validator:
|
|
388
|
+
this.writeln(`const validator: (opts?: Partial<TValidatorOptions>) => Validator<TAtscriptAnnotatedTypeConstructor, ${name}>`);
|
|
389
389
|
this.writeln("const toJsonSchema: () => any");
|
|
390
390
|
this.popln();
|
|
391
391
|
}
|
|
392
|
+
isTypeTarget(name, doc) {
|
|
393
|
+
const d = doc || this.doc;
|
|
394
|
+
const decl = d.getDeclarationOwnerNode(name);
|
|
395
|
+
if (!decl?.node) return false;
|
|
396
|
+
if (decl.node.entity === "type") return true;
|
|
397
|
+
if (decl.node.entity === "interface") return false;
|
|
398
|
+
if (decl.node.entity === "annotate") return this.isTypeTarget(decl.node.targetName, decl.doc);
|
|
399
|
+
return false;
|
|
400
|
+
}
|
|
392
401
|
renderJsDoc(node) {
|
|
393
402
|
const range = node.token("identifier")?.range;
|
|
394
403
|
const rangeStr = range ? `:${range.start.line + 1}:${range.start.character + 1}` : "";
|
|
@@ -757,6 +766,15 @@ var ValidatorError = class extends Error {
|
|
|
757
766
|
function isAnnotatedType(type) {
|
|
758
767
|
return type && type.__is_atscript_annotated_type;
|
|
759
768
|
}
|
|
769
|
+
function annotate(metadata, key, value, asArray) {
|
|
770
|
+
if (!metadata) return;
|
|
771
|
+
if (asArray) if (metadata.has(key)) {
|
|
772
|
+
const a = metadata.get(key);
|
|
773
|
+
if (Array.isArray(a)) a.push(value);
|
|
774
|
+
else metadata.set(key, [a, value]);
|
|
775
|
+
} else metadata.set(key, [value]);
|
|
776
|
+
else metadata.set(key, value);
|
|
777
|
+
}
|
|
760
778
|
function defineAnnotatedType(_kind, base) {
|
|
761
779
|
const kind = _kind || "";
|
|
762
780
|
const type = base?.type || {};
|
|
@@ -856,12 +874,7 @@ else if (!newBase) throw new Error(`"${typeName}" is not annotated type`);
|
|
|
856
874
|
return this;
|
|
857
875
|
},
|
|
858
876
|
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);
|
|
877
|
+
annotate(this.$metadata, key, value, asArray);
|
|
865
878
|
return this;
|
|
866
879
|
}
|
|
867
880
|
};
|
|
@@ -965,7 +978,7 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
965
978
|
pre() {
|
|
966
979
|
this.writeln("// prettier-ignore-start");
|
|
967
980
|
this.writeln("/* eslint-disable */");
|
|
968
|
-
const imports = ["defineAnnotatedType as $"];
|
|
981
|
+
const imports = ["defineAnnotatedType as $", "annotate as $a"];
|
|
969
982
|
if (!this.opts?.preRenderJsonSchema) imports.push("buildJsonSchema as $$");
|
|
970
983
|
this.writeln(`import { ${imports.join(", ")} } from "@atscript/typescript/utils"`);
|
|
971
984
|
}
|
|
@@ -984,37 +997,42 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
984
997
|
post() {
|
|
985
998
|
for (const node of this.postAnnotate) if (node.entity === "annotate") {
|
|
986
999
|
const annotateNode = node;
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
if (
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
1000
|
+
if (annotateNode.isMutating) this.renderMutatingAnnotateNode(annotateNode);
|
|
1001
|
+
else {
|
|
1002
|
+
const unwound = this.doc.unwindType(annotateNode.targetName);
|
|
1003
|
+
if (unwound?.def) {
|
|
1004
|
+
let def = this.doc.mergeIntersection(unwound.def);
|
|
1005
|
+
if ((0, __atscript_core.isInterface)(def)) def = def.getDefinition() || def;
|
|
1006
|
+
this._adHocAnnotations = this.buildAdHocMap([annotateNode]);
|
|
1007
|
+
this.annotateType(def, node.id);
|
|
1008
|
+
this._adHocAnnotations = null;
|
|
1009
|
+
this.indent();
|
|
1010
|
+
this.defineMetadataForAnnotateAlias(annotateNode);
|
|
1011
|
+
this.unindent();
|
|
1012
|
+
this.writeln();
|
|
1013
|
+
}
|
|
998
1014
|
}
|
|
999
1015
|
} else {
|
|
1000
|
-
const mutatingNodes = this.doc.getAnnotateNodesFor(node.id).filter((n) => n.isMutating);
|
|
1001
|
-
this._adHocAnnotations = this.buildAdHocMap(mutatingNodes);
|
|
1002
1016
|
this.annotateType(node.getDefinition(), node.id);
|
|
1003
|
-
this._adHocAnnotations = null;
|
|
1004
1017
|
this.indent().defineMetadata(node).unindent();
|
|
1005
1018
|
this.writeln();
|
|
1006
1019
|
}
|
|
1007
|
-
this.renderMutatingAnnotates();
|
|
1008
1020
|
this.writeln("// prettier-ignore-end");
|
|
1009
1021
|
super.post();
|
|
1010
1022
|
}
|
|
1023
|
+
renderClassStatics(node) {
|
|
1024
|
+
this.writeln("static __is_atscript_annotated_type = true");
|
|
1025
|
+
this.writeln("static type = {}");
|
|
1026
|
+
this.writeln("static metadata = new Map()");
|
|
1027
|
+
this.renderJsonSchemaMethod(node);
|
|
1028
|
+
}
|
|
1011
1029
|
renderInterface(node) {
|
|
1012
1030
|
this.writeln();
|
|
1013
1031
|
const exported = node.token("export")?.text === "export";
|
|
1014
1032
|
this.write(exported ? "export " : "");
|
|
1015
1033
|
this.write(`class ${node.id} `);
|
|
1016
1034
|
this.blockln("{}");
|
|
1017
|
-
this.
|
|
1035
|
+
this.renderClassStatics(node);
|
|
1018
1036
|
this.popln();
|
|
1019
1037
|
this.postAnnotate.push(node);
|
|
1020
1038
|
this.writeln();
|
|
@@ -1025,14 +1043,14 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
1025
1043
|
this.write(exported ? "export " : "");
|
|
1026
1044
|
this.write(`class ${node.id} `);
|
|
1027
1045
|
this.blockln("{}");
|
|
1028
|
-
this.
|
|
1046
|
+
this.renderClassStatics(node);
|
|
1029
1047
|
this.popln();
|
|
1030
1048
|
this.postAnnotate.push(node);
|
|
1031
1049
|
this.writeln();
|
|
1032
1050
|
}
|
|
1033
1051
|
renderAnnotate(node) {
|
|
1034
1052
|
if (node.isMutating) {
|
|
1035
|
-
this.
|
|
1053
|
+
this.postAnnotate.push(node);
|
|
1036
1054
|
return;
|
|
1037
1055
|
}
|
|
1038
1056
|
const targetName = node.targetName;
|
|
@@ -1043,7 +1061,7 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
1043
1061
|
this.write(exported ? "export " : "");
|
|
1044
1062
|
this.write(`class ${node.id} `);
|
|
1045
1063
|
this.blockln("{}");
|
|
1046
|
-
this.
|
|
1064
|
+
this.renderClassStatics(node);
|
|
1047
1065
|
this.popln();
|
|
1048
1066
|
this.postAnnotate.push(node);
|
|
1049
1067
|
this.writeln();
|
|
@@ -1320,18 +1338,13 @@ else handle.prop(prop.id, propHandle.$type);
|
|
|
1320
1338
|
return this;
|
|
1321
1339
|
}
|
|
1322
1340
|
defineMetadata(node) {
|
|
1323
|
-
|
|
1324
|
-
let adHocNames;
|
|
1325
|
-
let adHoc;
|
|
1341
|
+
let annotations = this.doc.evalAnnotationsForNode(node);
|
|
1326
1342
|
if (this._adHocAnnotations && this._propPath.length > 0) {
|
|
1327
1343
|
const path$2 = this._propPath.join(".");
|
|
1328
|
-
adHoc = this._adHocAnnotations.get(path$2);
|
|
1329
|
-
if (adHoc)
|
|
1344
|
+
const adHoc = this._adHocAnnotations.get(path$2);
|
|
1345
|
+
if (adHoc) annotations = this.doc.mergeNodesAnnotations(annotations, adHoc);
|
|
1330
1346
|
}
|
|
1331
1347
|
annotations?.forEach((an) => {
|
|
1332
|
-
if (!adHocNames || !adHocNames.has(an.name)) this.resolveAnnotationValue(node, an);
|
|
1333
|
-
});
|
|
1334
|
-
adHoc?.forEach((an) => {
|
|
1335
1348
|
this.resolveAnnotationValue(node, an);
|
|
1336
1349
|
});
|
|
1337
1350
|
return this;
|
|
@@ -1343,11 +1356,8 @@ else handle.prop(prop.id, propHandle.$type);
|
|
|
1343
1356
|
const annotateAnnotations = this.doc.evalAnnotationsForNode(annotateNode);
|
|
1344
1357
|
const targetDecl = this.doc.getDeclarationOwnerNode(annotateNode.targetName);
|
|
1345
1358
|
const targetAnnotations = targetDecl?.node ? targetDecl.doc.evalAnnotationsForNode(targetDecl.node) : undefined;
|
|
1346
|
-
const
|
|
1347
|
-
|
|
1348
|
-
if (!overriddenNames.has(an.name)) this.resolveAnnotationValue(annotateNode, an);
|
|
1349
|
-
});
|
|
1350
|
-
annotateAnnotations?.forEach((an) => {
|
|
1359
|
+
const merged = this.doc.mergeNodesAnnotations(targetAnnotations, annotateAnnotations);
|
|
1360
|
+
merged.forEach((an) => {
|
|
1351
1361
|
this.resolveAnnotationValue(annotateNode, an);
|
|
1352
1362
|
});
|
|
1353
1363
|
return this;
|
|
@@ -1386,47 +1396,123 @@ else targetValue = "true";
|
|
|
1386
1396
|
multiple: !!multiple
|
|
1387
1397
|
};
|
|
1388
1398
|
}
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1399
|
+
renderMutatingAnnotateNode(node) {
|
|
1400
|
+
const targetName = node.targetName;
|
|
1401
|
+
const targetDef = this.resolveTargetDef(targetName);
|
|
1402
|
+
this.writeln("// Ad-hoc annotations for ", targetName);
|
|
1403
|
+
for (const entry of node.entries) {
|
|
1404
|
+
const anns = entry.annotations;
|
|
1405
|
+
if (!anns || anns.length === 0) continue;
|
|
1406
|
+
const parts = entry.hasChain ? [entry.id, ...entry.chain.map((c) => c.text)] : [entry.id];
|
|
1407
|
+
const accessors = this.buildMutatingAccessors(targetName, targetDef, parts);
|
|
1408
|
+
for (const accessor of accessors) {
|
|
1409
|
+
const cleared = new Set();
|
|
1398
1410
|
for (const an of anns) {
|
|
1399
1411
|
const { value, multiple } = this.computeAnnotationValue(entry, an);
|
|
1400
1412
|
if (multiple) {
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
this.writeln(
|
|
1407
|
-
|
|
1408
|
-
this.writeln(`}`);
|
|
1409
|
-
} else this.writeln(`${accessor}.metadata.set("${escapeQuotes(an.name)}", ${value})`);
|
|
1413
|
+
if (!cleared.has(an.name)) {
|
|
1414
|
+
const spec = this.doc.resolveAnnotation(an.name);
|
|
1415
|
+
if (!spec || spec.config.mergeStrategy !== "append") this.writeln(`${accessor}.metadata.delete("${escapeQuotes(an.name)}")`);
|
|
1416
|
+
cleared.add(an.name);
|
|
1417
|
+
}
|
|
1418
|
+
this.writeln(`$a(${accessor}.metadata, "${escapeQuotes(an.name)}", ${value}, true)`);
|
|
1419
|
+
} else this.writeln(`$a(${accessor}.metadata, "${escapeQuotes(an.name)}", ${value})`);
|
|
1410
1420
|
}
|
|
1411
1421
|
}
|
|
1412
|
-
|
|
1413
|
-
|
|
1422
|
+
}
|
|
1423
|
+
const topAnnotations = node.annotations;
|
|
1424
|
+
if (topAnnotations && topAnnotations.length > 0) {
|
|
1425
|
+
const cleared = new Set();
|
|
1426
|
+
for (const an of topAnnotations) {
|
|
1414
1427
|
const { value, multiple } = this.computeAnnotationValue(node, an);
|
|
1415
1428
|
if (multiple) {
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
this.writeln(
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1429
|
+
if (!cleared.has(an.name)) {
|
|
1430
|
+
const spec = this.doc.resolveAnnotation(an.name);
|
|
1431
|
+
if (!spec || spec.config.mergeStrategy !== "append") this.writeln(`${targetName}.metadata.delete("${escapeQuotes(an.name)}")`);
|
|
1432
|
+
cleared.add(an.name);
|
|
1433
|
+
}
|
|
1434
|
+
this.writeln(`$a(${targetName}.metadata, "${escapeQuotes(an.name)}", ${value}, true)`);
|
|
1435
|
+
} else this.writeln(`$a(${targetName}.metadata, "${escapeQuotes(an.name)}", ${value})`);
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
this.writeln();
|
|
1439
|
+
}
|
|
1440
|
+
resolveTargetDef(targetName) {
|
|
1441
|
+
const unwound = this.doc.unwindType(targetName);
|
|
1442
|
+
if (!unwound?.def) return undefined;
|
|
1443
|
+
let def = unwound.def;
|
|
1444
|
+
if ((0, __atscript_core.isInterface)(def)) def = def.getDefinition() || def;
|
|
1445
|
+
return def;
|
|
1446
|
+
}
|
|
1447
|
+
/**
|
|
1448
|
+
* Builds the runtime accessor paths for mutating annotate entries.
|
|
1449
|
+
* Computes exact paths at compile time by walking the AST,
|
|
1450
|
+
* so the generated JS accesses props directly without runtime search.
|
|
1451
|
+
* Returns multiple paths when a property appears in multiple union branches.
|
|
1452
|
+
*/ buildMutatingAccessors(targetName, targetDef, parts) {
|
|
1453
|
+
let accessors = [{
|
|
1454
|
+
prefix: targetName + ".type",
|
|
1455
|
+
def: targetDef
|
|
1456
|
+
}];
|
|
1457
|
+
for (let i = 0; i < parts.length; i++) {
|
|
1458
|
+
const nextAccessors = [];
|
|
1459
|
+
for (const { prefix, def } of accessors) {
|
|
1460
|
+
const results = this.buildPropPaths(def, parts[i]);
|
|
1461
|
+
if (results.length > 0) for (const result of results) if (i < parts.length - 1) nextAccessors.push({
|
|
1462
|
+
prefix: prefix + result.path + "?.type",
|
|
1463
|
+
def: result.propDef
|
|
1464
|
+
});
|
|
1465
|
+
else nextAccessors.push({
|
|
1466
|
+
prefix: prefix + result.path + "?",
|
|
1467
|
+
def: result.propDef
|
|
1468
|
+
});
|
|
1469
|
+
else {
|
|
1470
|
+
const suffix = `.props.get("${escapeQuotes(parts[i])}")` + (i < parts.length - 1 ? "?.type" : "?");
|
|
1471
|
+
nextAccessors.push({
|
|
1472
|
+
prefix: prefix + suffix,
|
|
1473
|
+
def: undefined
|
|
1474
|
+
});
|
|
1475
|
+
}
|
|
1425
1476
|
}
|
|
1477
|
+
accessors = nextAccessors;
|
|
1478
|
+
}
|
|
1479
|
+
return accessors.map((a) => a.prefix);
|
|
1480
|
+
}
|
|
1481
|
+
/**
|
|
1482
|
+
* Finds a property in a type tree at compile time, returning all
|
|
1483
|
+
* matching runtime path strings and prop definitions for further chaining.
|
|
1484
|
+
* Returns multiple results when the same property appears in different union branches.
|
|
1485
|
+
*/ buildPropPaths(def, propName) {
|
|
1486
|
+
if (!def) return [];
|
|
1487
|
+
def = this.doc.mergeIntersection(def);
|
|
1488
|
+
if ((0, __atscript_core.isRef)(def)) {
|
|
1489
|
+
const ref = def;
|
|
1490
|
+
const unwound = this.doc.unwindType(ref.id, ref.chain)?.def;
|
|
1491
|
+
return this.buildPropPaths(unwound, propName);
|
|
1492
|
+
}
|
|
1493
|
+
if ((0, __atscript_core.isInterface)(def)) return this.buildPropPaths(def.getDefinition(), propName);
|
|
1494
|
+
if ((0, __atscript_core.isStructure)(def)) {
|
|
1495
|
+
const prop = def.props.get(propName);
|
|
1496
|
+
if (prop) return [{
|
|
1497
|
+
path: `.props.get("${escapeQuotes(propName)}")`,
|
|
1498
|
+
propDef: prop.getDefinition()
|
|
1499
|
+
}];
|
|
1500
|
+
return [];
|
|
1501
|
+
}
|
|
1502
|
+
if ((0, __atscript_core.isGroup)(def)) {
|
|
1503
|
+
const group = def;
|
|
1504
|
+
const items = group.unwrap();
|
|
1505
|
+
const results = [];
|
|
1506
|
+
for (let i = 0; i < items.length; i++) for (const result of this.buildPropPaths(items[i], propName)) results.push({
|
|
1507
|
+
path: `.items[${i}].type${result.path}`,
|
|
1508
|
+
propDef: result.propDef
|
|
1509
|
+
});
|
|
1510
|
+
return results;
|
|
1426
1511
|
}
|
|
1512
|
+
return [];
|
|
1427
1513
|
}
|
|
1428
1514
|
constructor(doc, opts) {
|
|
1429
|
-
super(doc), _define_property(this, "opts", void 0), _define_property(this, "postAnnotate", void 0), _define_property(this, "
|
|
1515
|
+
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 = [];
|
|
1430
1516
|
}
|
|
1431
1517
|
};
|
|
1432
1518
|
|
package/dist/index.mjs
CHANGED
|
@@ -298,7 +298,7 @@ var TypeRenderer = class TypeRenderer extends BaseRenderer {
|
|
|
298
298
|
this.writeln("static __is_atscript_annotated_type: true");
|
|
299
299
|
this.writeln(`static type: TAtscriptTypeObject<keyof ${asClass}>`);
|
|
300
300
|
this.writeln(`static metadata: TMetadataMap<AtscriptMetadata>`);
|
|
301
|
-
this.writeln(`static validator: <TT extends TAtscriptAnnotatedTypeConstructor = ${asClass}>(opts?: Partial<TValidatorOptions>) => Validator<TT>`);
|
|
301
|
+
this.writeln(`static validator: <TT extends TAtscriptAnnotatedTypeConstructor = typeof ${asClass}>(opts?: Partial<TValidatorOptions>) => Validator<TT>`);
|
|
302
302
|
this.writeln("static toJsonSchema: () => any");
|
|
303
303
|
}
|
|
304
304
|
this.pop();
|
|
@@ -327,33 +327,33 @@ else this.writeln("{}");
|
|
|
327
327
|
renderAnnotate(node) {
|
|
328
328
|
if (node.isMutating) return;
|
|
329
329
|
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
330
|
this.writeln();
|
|
334
331
|
const exported = node.token("export")?.text === "export";
|
|
335
332
|
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 {
|
|
333
|
+
if (this.isTypeTarget(targetName)) {
|
|
341
334
|
this.write(exported ? "export " : "declare ");
|
|
342
|
-
this.write(`type ${node.id} = `);
|
|
343
|
-
this.
|
|
335
|
+
this.write(`type ${node.id} = ${targetName}`);
|
|
336
|
+
this.writeln();
|
|
337
|
+
const unwound = this.doc.unwindType(targetName);
|
|
338
|
+
this.renderTypeNamespaceFor(node.id, unwound?.def);
|
|
339
|
+
} else {
|
|
340
|
+
this.write(exported ? "export declare " : "declare ");
|
|
341
|
+
this.writeln(`class ${node.id} extends ${targetName} {}`);
|
|
342
|
+
this.writeln();
|
|
344
343
|
}
|
|
345
|
-
this.writeln();
|
|
346
344
|
}
|
|
347
345
|
renderTypeNamespace(node) {
|
|
348
|
-
this.
|
|
346
|
+
this.renderTypeNamespaceFor(node.id, node.getDefinition());
|
|
347
|
+
}
|
|
348
|
+
renderTypeNamespaceFor(name, inputDef) {
|
|
349
|
+
this.write(`declare namespace ${name} `);
|
|
349
350
|
this.blockln("{}");
|
|
350
|
-
const def = node.getDefinition();
|
|
351
351
|
let typeDef = "TAtscriptTypeDef";
|
|
352
|
-
if (
|
|
353
|
-
let realDef =
|
|
354
|
-
if (isRef(
|
|
352
|
+
if (inputDef) {
|
|
353
|
+
let realDef = inputDef;
|
|
354
|
+
if (isRef(inputDef)) realDef = this.doc.unwindType(inputDef.id, inputDef.chain)?.def || realDef;
|
|
355
355
|
realDef = this.doc.mergeIntersection(realDef);
|
|
356
|
-
if (isStructure(realDef) || isInterface(realDef)) typeDef = `TAtscriptTypeObject<keyof ${
|
|
356
|
+
if (isStructure(realDef) || isInterface(realDef)) typeDef = `TAtscriptTypeObject<keyof ${name}>`;
|
|
357
357
|
else if (isGroup(realDef)) typeDef = "TAtscriptTypeComplex";
|
|
358
358
|
else if (isArray(realDef)) typeDef = "TAtscriptTypeArray";
|
|
359
359
|
else if (isPrimitive(realDef)) typeDef = "TAtscriptTypeFinal";
|
|
@@ -361,10 +361,19 @@ else if (isPrimitive(realDef)) typeDef = "TAtscriptTypeFinal";
|
|
|
361
361
|
this.writeln(`const __is_atscript_annotated_type: true`);
|
|
362
362
|
this.writeln(`const type: ${typeDef}`);
|
|
363
363
|
this.writeln(`const metadata: TMetadataMap<AtscriptMetadata>`);
|
|
364
|
-
this.writeln(`const validator:
|
|
364
|
+
this.writeln(`const validator: (opts?: Partial<TValidatorOptions>) => Validator<TAtscriptAnnotatedTypeConstructor, ${name}>`);
|
|
365
365
|
this.writeln("const toJsonSchema: () => any");
|
|
366
366
|
this.popln();
|
|
367
367
|
}
|
|
368
|
+
isTypeTarget(name, doc) {
|
|
369
|
+
const d = doc || this.doc;
|
|
370
|
+
const decl = d.getDeclarationOwnerNode(name);
|
|
371
|
+
if (!decl?.node) return false;
|
|
372
|
+
if (decl.node.entity === "type") return true;
|
|
373
|
+
if (decl.node.entity === "interface") return false;
|
|
374
|
+
if (decl.node.entity === "annotate") return this.isTypeTarget(decl.node.targetName, decl.doc);
|
|
375
|
+
return false;
|
|
376
|
+
}
|
|
368
377
|
renderJsDoc(node) {
|
|
369
378
|
const range = node.token("identifier")?.range;
|
|
370
379
|
const rangeStr = range ? `:${range.start.line + 1}:${range.start.character + 1}` : "";
|
|
@@ -733,6 +742,15 @@ var ValidatorError = class extends Error {
|
|
|
733
742
|
function isAnnotatedType(type) {
|
|
734
743
|
return type && type.__is_atscript_annotated_type;
|
|
735
744
|
}
|
|
745
|
+
function annotate(metadata, key, value, asArray) {
|
|
746
|
+
if (!metadata) return;
|
|
747
|
+
if (asArray) if (metadata.has(key)) {
|
|
748
|
+
const a = metadata.get(key);
|
|
749
|
+
if (Array.isArray(a)) a.push(value);
|
|
750
|
+
else metadata.set(key, [a, value]);
|
|
751
|
+
} else metadata.set(key, [value]);
|
|
752
|
+
else metadata.set(key, value);
|
|
753
|
+
}
|
|
736
754
|
function defineAnnotatedType(_kind, base) {
|
|
737
755
|
const kind = _kind || "";
|
|
738
756
|
const type = base?.type || {};
|
|
@@ -832,12 +850,7 @@ else if (!newBase) throw new Error(`"${typeName}" is not annotated type`);
|
|
|
832
850
|
return this;
|
|
833
851
|
},
|
|
834
852
|
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);
|
|
853
|
+
annotate(this.$metadata, key, value, asArray);
|
|
841
854
|
return this;
|
|
842
855
|
}
|
|
843
856
|
};
|
|
@@ -941,7 +954,7 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
941
954
|
pre() {
|
|
942
955
|
this.writeln("// prettier-ignore-start");
|
|
943
956
|
this.writeln("/* eslint-disable */");
|
|
944
|
-
const imports = ["defineAnnotatedType as $"];
|
|
957
|
+
const imports = ["defineAnnotatedType as $", "annotate as $a"];
|
|
945
958
|
if (!this.opts?.preRenderJsonSchema) imports.push("buildJsonSchema as $$");
|
|
946
959
|
this.writeln(`import { ${imports.join(", ")} } from "@atscript/typescript/utils"`);
|
|
947
960
|
}
|
|
@@ -960,37 +973,42 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
960
973
|
post() {
|
|
961
974
|
for (const node of this.postAnnotate) if (node.entity === "annotate") {
|
|
962
975
|
const annotateNode = node;
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
if (
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
976
|
+
if (annotateNode.isMutating) this.renderMutatingAnnotateNode(annotateNode);
|
|
977
|
+
else {
|
|
978
|
+
const unwound = this.doc.unwindType(annotateNode.targetName);
|
|
979
|
+
if (unwound?.def) {
|
|
980
|
+
let def = this.doc.mergeIntersection(unwound.def);
|
|
981
|
+
if (isInterface(def)) def = def.getDefinition() || def;
|
|
982
|
+
this._adHocAnnotations = this.buildAdHocMap([annotateNode]);
|
|
983
|
+
this.annotateType(def, node.id);
|
|
984
|
+
this._adHocAnnotations = null;
|
|
985
|
+
this.indent();
|
|
986
|
+
this.defineMetadataForAnnotateAlias(annotateNode);
|
|
987
|
+
this.unindent();
|
|
988
|
+
this.writeln();
|
|
989
|
+
}
|
|
974
990
|
}
|
|
975
991
|
} else {
|
|
976
|
-
const mutatingNodes = this.doc.getAnnotateNodesFor(node.id).filter((n) => n.isMutating);
|
|
977
|
-
this._adHocAnnotations = this.buildAdHocMap(mutatingNodes);
|
|
978
992
|
this.annotateType(node.getDefinition(), node.id);
|
|
979
|
-
this._adHocAnnotations = null;
|
|
980
993
|
this.indent().defineMetadata(node).unindent();
|
|
981
994
|
this.writeln();
|
|
982
995
|
}
|
|
983
|
-
this.renderMutatingAnnotates();
|
|
984
996
|
this.writeln("// prettier-ignore-end");
|
|
985
997
|
super.post();
|
|
986
998
|
}
|
|
999
|
+
renderClassStatics(node) {
|
|
1000
|
+
this.writeln("static __is_atscript_annotated_type = true");
|
|
1001
|
+
this.writeln("static type = {}");
|
|
1002
|
+
this.writeln("static metadata = new Map()");
|
|
1003
|
+
this.renderJsonSchemaMethod(node);
|
|
1004
|
+
}
|
|
987
1005
|
renderInterface(node) {
|
|
988
1006
|
this.writeln();
|
|
989
1007
|
const exported = node.token("export")?.text === "export";
|
|
990
1008
|
this.write(exported ? "export " : "");
|
|
991
1009
|
this.write(`class ${node.id} `);
|
|
992
1010
|
this.blockln("{}");
|
|
993
|
-
this.
|
|
1011
|
+
this.renderClassStatics(node);
|
|
994
1012
|
this.popln();
|
|
995
1013
|
this.postAnnotate.push(node);
|
|
996
1014
|
this.writeln();
|
|
@@ -1001,14 +1019,14 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
1001
1019
|
this.write(exported ? "export " : "");
|
|
1002
1020
|
this.write(`class ${node.id} `);
|
|
1003
1021
|
this.blockln("{}");
|
|
1004
|
-
this.
|
|
1022
|
+
this.renderClassStatics(node);
|
|
1005
1023
|
this.popln();
|
|
1006
1024
|
this.postAnnotate.push(node);
|
|
1007
1025
|
this.writeln();
|
|
1008
1026
|
}
|
|
1009
1027
|
renderAnnotate(node) {
|
|
1010
1028
|
if (node.isMutating) {
|
|
1011
|
-
this.
|
|
1029
|
+
this.postAnnotate.push(node);
|
|
1012
1030
|
return;
|
|
1013
1031
|
}
|
|
1014
1032
|
const targetName = node.targetName;
|
|
@@ -1019,7 +1037,7 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
1019
1037
|
this.write(exported ? "export " : "");
|
|
1020
1038
|
this.write(`class ${node.id} `);
|
|
1021
1039
|
this.blockln("{}");
|
|
1022
|
-
this.
|
|
1040
|
+
this.renderClassStatics(node);
|
|
1023
1041
|
this.popln();
|
|
1024
1042
|
this.postAnnotate.push(node);
|
|
1025
1043
|
this.writeln();
|
|
@@ -1296,18 +1314,13 @@ else handle.prop(prop.id, propHandle.$type);
|
|
|
1296
1314
|
return this;
|
|
1297
1315
|
}
|
|
1298
1316
|
defineMetadata(node) {
|
|
1299
|
-
|
|
1300
|
-
let adHocNames;
|
|
1301
|
-
let adHoc;
|
|
1317
|
+
let annotations = this.doc.evalAnnotationsForNode(node);
|
|
1302
1318
|
if (this._adHocAnnotations && this._propPath.length > 0) {
|
|
1303
1319
|
const path$1 = this._propPath.join(".");
|
|
1304
|
-
adHoc = this._adHocAnnotations.get(path$1);
|
|
1305
|
-
if (adHoc)
|
|
1320
|
+
const adHoc = this._adHocAnnotations.get(path$1);
|
|
1321
|
+
if (adHoc) annotations = this.doc.mergeNodesAnnotations(annotations, adHoc);
|
|
1306
1322
|
}
|
|
1307
1323
|
annotations?.forEach((an) => {
|
|
1308
|
-
if (!adHocNames || !adHocNames.has(an.name)) this.resolveAnnotationValue(node, an);
|
|
1309
|
-
});
|
|
1310
|
-
adHoc?.forEach((an) => {
|
|
1311
1324
|
this.resolveAnnotationValue(node, an);
|
|
1312
1325
|
});
|
|
1313
1326
|
return this;
|
|
@@ -1319,11 +1332,8 @@ else handle.prop(prop.id, propHandle.$type);
|
|
|
1319
1332
|
const annotateAnnotations = this.doc.evalAnnotationsForNode(annotateNode);
|
|
1320
1333
|
const targetDecl = this.doc.getDeclarationOwnerNode(annotateNode.targetName);
|
|
1321
1334
|
const targetAnnotations = targetDecl?.node ? targetDecl.doc.evalAnnotationsForNode(targetDecl.node) : undefined;
|
|
1322
|
-
const
|
|
1323
|
-
|
|
1324
|
-
if (!overriddenNames.has(an.name)) this.resolveAnnotationValue(annotateNode, an);
|
|
1325
|
-
});
|
|
1326
|
-
annotateAnnotations?.forEach((an) => {
|
|
1335
|
+
const merged = this.doc.mergeNodesAnnotations(targetAnnotations, annotateAnnotations);
|
|
1336
|
+
merged.forEach((an) => {
|
|
1327
1337
|
this.resolveAnnotationValue(annotateNode, an);
|
|
1328
1338
|
});
|
|
1329
1339
|
return this;
|
|
@@ -1362,47 +1372,123 @@ else targetValue = "true";
|
|
|
1362
1372
|
multiple: !!multiple
|
|
1363
1373
|
};
|
|
1364
1374
|
}
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1375
|
+
renderMutatingAnnotateNode(node) {
|
|
1376
|
+
const targetName = node.targetName;
|
|
1377
|
+
const targetDef = this.resolveTargetDef(targetName);
|
|
1378
|
+
this.writeln("// Ad-hoc annotations for ", targetName);
|
|
1379
|
+
for (const entry of node.entries) {
|
|
1380
|
+
const anns = entry.annotations;
|
|
1381
|
+
if (!anns || anns.length === 0) continue;
|
|
1382
|
+
const parts = entry.hasChain ? [entry.id, ...entry.chain.map((c) => c.text)] : [entry.id];
|
|
1383
|
+
const accessors = this.buildMutatingAccessors(targetName, targetDef, parts);
|
|
1384
|
+
for (const accessor of accessors) {
|
|
1385
|
+
const cleared = new Set();
|
|
1374
1386
|
for (const an of anns) {
|
|
1375
1387
|
const { value, multiple } = this.computeAnnotationValue(entry, an);
|
|
1376
1388
|
if (multiple) {
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
this.writeln(
|
|
1383
|
-
|
|
1384
|
-
this.writeln(`}`);
|
|
1385
|
-
} else this.writeln(`${accessor}.metadata.set("${escapeQuotes(an.name)}", ${value})`);
|
|
1389
|
+
if (!cleared.has(an.name)) {
|
|
1390
|
+
const spec = this.doc.resolveAnnotation(an.name);
|
|
1391
|
+
if (!spec || spec.config.mergeStrategy !== "append") this.writeln(`${accessor}.metadata.delete("${escapeQuotes(an.name)}")`);
|
|
1392
|
+
cleared.add(an.name);
|
|
1393
|
+
}
|
|
1394
|
+
this.writeln(`$a(${accessor}.metadata, "${escapeQuotes(an.name)}", ${value}, true)`);
|
|
1395
|
+
} else this.writeln(`$a(${accessor}.metadata, "${escapeQuotes(an.name)}", ${value})`);
|
|
1386
1396
|
}
|
|
1387
1397
|
}
|
|
1388
|
-
|
|
1389
|
-
|
|
1398
|
+
}
|
|
1399
|
+
const topAnnotations = node.annotations;
|
|
1400
|
+
if (topAnnotations && topAnnotations.length > 0) {
|
|
1401
|
+
const cleared = new Set();
|
|
1402
|
+
for (const an of topAnnotations) {
|
|
1390
1403
|
const { value, multiple } = this.computeAnnotationValue(node, an);
|
|
1391
1404
|
if (multiple) {
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
this.writeln(
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1405
|
+
if (!cleared.has(an.name)) {
|
|
1406
|
+
const spec = this.doc.resolveAnnotation(an.name);
|
|
1407
|
+
if (!spec || spec.config.mergeStrategy !== "append") this.writeln(`${targetName}.metadata.delete("${escapeQuotes(an.name)}")`);
|
|
1408
|
+
cleared.add(an.name);
|
|
1409
|
+
}
|
|
1410
|
+
this.writeln(`$a(${targetName}.metadata, "${escapeQuotes(an.name)}", ${value}, true)`);
|
|
1411
|
+
} else this.writeln(`$a(${targetName}.metadata, "${escapeQuotes(an.name)}", ${value})`);
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
this.writeln();
|
|
1415
|
+
}
|
|
1416
|
+
resolveTargetDef(targetName) {
|
|
1417
|
+
const unwound = this.doc.unwindType(targetName);
|
|
1418
|
+
if (!unwound?.def) return undefined;
|
|
1419
|
+
let def = unwound.def;
|
|
1420
|
+
if (isInterface(def)) def = def.getDefinition() || def;
|
|
1421
|
+
return def;
|
|
1422
|
+
}
|
|
1423
|
+
/**
|
|
1424
|
+
* Builds the runtime accessor paths for mutating annotate entries.
|
|
1425
|
+
* Computes exact paths at compile time by walking the AST,
|
|
1426
|
+
* so the generated JS accesses props directly without runtime search.
|
|
1427
|
+
* Returns multiple paths when a property appears in multiple union branches.
|
|
1428
|
+
*/ buildMutatingAccessors(targetName, targetDef, parts) {
|
|
1429
|
+
let accessors = [{
|
|
1430
|
+
prefix: targetName + ".type",
|
|
1431
|
+
def: targetDef
|
|
1432
|
+
}];
|
|
1433
|
+
for (let i = 0; i < parts.length; i++) {
|
|
1434
|
+
const nextAccessors = [];
|
|
1435
|
+
for (const { prefix, def } of accessors) {
|
|
1436
|
+
const results = this.buildPropPaths(def, parts[i]);
|
|
1437
|
+
if (results.length > 0) for (const result of results) if (i < parts.length - 1) nextAccessors.push({
|
|
1438
|
+
prefix: prefix + result.path + "?.type",
|
|
1439
|
+
def: result.propDef
|
|
1440
|
+
});
|
|
1441
|
+
else nextAccessors.push({
|
|
1442
|
+
prefix: prefix + result.path + "?",
|
|
1443
|
+
def: result.propDef
|
|
1444
|
+
});
|
|
1445
|
+
else {
|
|
1446
|
+
const suffix = `.props.get("${escapeQuotes(parts[i])}")` + (i < parts.length - 1 ? "?.type" : "?");
|
|
1447
|
+
nextAccessors.push({
|
|
1448
|
+
prefix: prefix + suffix,
|
|
1449
|
+
def: undefined
|
|
1450
|
+
});
|
|
1451
|
+
}
|
|
1401
1452
|
}
|
|
1453
|
+
accessors = nextAccessors;
|
|
1454
|
+
}
|
|
1455
|
+
return accessors.map((a) => a.prefix);
|
|
1456
|
+
}
|
|
1457
|
+
/**
|
|
1458
|
+
* Finds a property in a type tree at compile time, returning all
|
|
1459
|
+
* matching runtime path strings and prop definitions for further chaining.
|
|
1460
|
+
* Returns multiple results when the same property appears in different union branches.
|
|
1461
|
+
*/ buildPropPaths(def, propName) {
|
|
1462
|
+
if (!def) return [];
|
|
1463
|
+
def = this.doc.mergeIntersection(def);
|
|
1464
|
+
if (isRef(def)) {
|
|
1465
|
+
const ref = def;
|
|
1466
|
+
const unwound = this.doc.unwindType(ref.id, ref.chain)?.def;
|
|
1467
|
+
return this.buildPropPaths(unwound, propName);
|
|
1468
|
+
}
|
|
1469
|
+
if (isInterface(def)) return this.buildPropPaths(def.getDefinition(), propName);
|
|
1470
|
+
if (isStructure(def)) {
|
|
1471
|
+
const prop = def.props.get(propName);
|
|
1472
|
+
if (prop) return [{
|
|
1473
|
+
path: `.props.get("${escapeQuotes(propName)}")`,
|
|
1474
|
+
propDef: prop.getDefinition()
|
|
1475
|
+
}];
|
|
1476
|
+
return [];
|
|
1477
|
+
}
|
|
1478
|
+
if (isGroup(def)) {
|
|
1479
|
+
const group = def;
|
|
1480
|
+
const items = group.unwrap();
|
|
1481
|
+
const results = [];
|
|
1482
|
+
for (let i = 0; i < items.length; i++) for (const result of this.buildPropPaths(items[i], propName)) results.push({
|
|
1483
|
+
path: `.items[${i}].type${result.path}`,
|
|
1484
|
+
propDef: result.propDef
|
|
1485
|
+
});
|
|
1486
|
+
return results;
|
|
1402
1487
|
}
|
|
1488
|
+
return [];
|
|
1403
1489
|
}
|
|
1404
1490
|
constructor(doc, opts) {
|
|
1405
|
-
super(doc), _define_property(this, "opts", void 0), _define_property(this, "postAnnotate", void 0), _define_property(this, "
|
|
1491
|
+
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 = [];
|
|
1406
1492
|
}
|
|
1407
1493
|
};
|
|
1408
1494
|
|
package/dist/utils.cjs
CHANGED
|
@@ -338,6 +338,15 @@ var ValidatorError = class extends Error {
|
|
|
338
338
|
function isAnnotatedType(type) {
|
|
339
339
|
return type && type.__is_atscript_annotated_type;
|
|
340
340
|
}
|
|
341
|
+
function annotate(metadata, key, value, asArray) {
|
|
342
|
+
if (!metadata) return;
|
|
343
|
+
if (asArray) if (metadata.has(key)) {
|
|
344
|
+
const a = metadata.get(key);
|
|
345
|
+
if (Array.isArray(a)) a.push(value);
|
|
346
|
+
else metadata.set(key, [a, value]);
|
|
347
|
+
} else metadata.set(key, [value]);
|
|
348
|
+
else metadata.set(key, value);
|
|
349
|
+
}
|
|
341
350
|
function defineAnnotatedType(_kind, base) {
|
|
342
351
|
const kind = _kind || "";
|
|
343
352
|
const type = base?.type || {};
|
|
@@ -437,12 +446,7 @@ else if (!newBase) throw new Error(`"${typeName}" is not annotated type`);
|
|
|
437
446
|
return this;
|
|
438
447
|
},
|
|
439
448
|
annotate(key, value, asArray) {
|
|
440
|
-
|
|
441
|
-
const a = this.$metadata.get(key);
|
|
442
|
-
if (Array.isArray(a)) a.push(value);
|
|
443
|
-
else this.$metadata.set(key, [a, value]);
|
|
444
|
-
} else this.$metadata.set(key, [value]);
|
|
445
|
-
else this.$metadata.set(key, value);
|
|
449
|
+
annotate(this.$metadata, key, value, asArray);
|
|
446
450
|
return this;
|
|
447
451
|
}
|
|
448
452
|
};
|
|
@@ -546,6 +550,7 @@ else schema.allOf = (schema.allOf || []).concat(patterns.map((p) => ({ pattern:
|
|
|
546
550
|
//#endregion
|
|
547
551
|
exports.Validator = Validator
|
|
548
552
|
exports.ValidatorError = ValidatorError
|
|
553
|
+
exports.annotate = annotate
|
|
549
554
|
exports.buildJsonSchema = buildJsonSchema
|
|
550
555
|
exports.defineAnnotatedType = defineAnnotatedType
|
|
551
556
|
exports.isAnnotatedType = isAnnotatedType
|
package/dist/utils.d.ts
CHANGED
|
@@ -18,10 +18,10 @@ interface TValidatorPluginContext {
|
|
|
18
18
|
error: Validator<any>['error'];
|
|
19
19
|
path: Validator<any>['path'];
|
|
20
20
|
}
|
|
21
|
-
declare class Validator<T extends TAtscriptAnnotatedTypeConstructor
|
|
22
|
-
protected readonly def: T
|
|
21
|
+
declare class Validator<T extends TAtscriptAnnotatedTypeConstructor, IT = InstanceType<T>> {
|
|
22
|
+
protected readonly def: T;
|
|
23
23
|
protected opts: TValidatorOptions;
|
|
24
|
-
constructor(def: T
|
|
24
|
+
constructor(def: T, opts?: Partial<TValidatorOptions>);
|
|
25
25
|
errors: TError[];
|
|
26
26
|
protected stackErrors: TError[][];
|
|
27
27
|
protected stackPath: string[];
|
|
@@ -31,7 +31,7 @@ declare class Validator<T extends TAtscriptAnnotatedTypeConstructor> {
|
|
|
31
31
|
protected clear(): void;
|
|
32
32
|
protected error(message: string, path?: string, details?: TError[]): void;
|
|
33
33
|
protected throw(): void;
|
|
34
|
-
validate<TT =
|
|
34
|
+
validate<TT = IT>(value: any, safe?: boolean): value is TT;
|
|
35
35
|
protected validateSafe(def: TAtscriptAnnotatedType, value: any): boolean;
|
|
36
36
|
protected get path(): string;
|
|
37
37
|
protected validateAnnotatedType(def: TAtscriptAnnotatedType, value: any): boolean;
|
|
@@ -94,6 +94,11 @@ type TAtscriptAnnotatedTypeConstructor = TAtscriptAnnotatedType & (new (...args:
|
|
|
94
94
|
* Type Guard to check if a type is atscript-annotated
|
|
95
95
|
*/
|
|
96
96
|
declare function isAnnotatedType(type: any): type is TAtscriptAnnotatedType;
|
|
97
|
+
/**
|
|
98
|
+
* Standalone annotate function that handles both replace and append (array) strategies.
|
|
99
|
+
* Used by the handle's .annotate() method and by generated mutation statements.
|
|
100
|
+
*/
|
|
101
|
+
declare function annotate<K extends keyof AtscriptMetadata>(metadata: TMetadataMap<AtscriptMetadata> | undefined, key: K, value: AtscriptMetadata[K] extends (infer E)[] ? E : AtscriptMetadata[K], asArray?: boolean): void;
|
|
97
102
|
type TKind = '' | 'array' | 'object' | 'union' | 'intersection' | 'tuple';
|
|
98
103
|
declare function defineAnnotatedType(_kind?: TKind, base?: any): TAnnotatedTypeHandle;
|
|
99
104
|
/**
|
|
@@ -129,4 +134,5 @@ declare function isAnnotatedTypeOfPrimitive(t: TAtscriptAnnotatedType): boolean;
|
|
|
129
134
|
type TJsonSchema = Record<string, any>;
|
|
130
135
|
declare function buildJsonSchema(type: TAtscriptAnnotatedType): TJsonSchema;
|
|
131
136
|
|
|
132
|
-
export {
|
|
137
|
+
export { Validator, ValidatorError, annotate, buildJsonSchema, defineAnnotatedType, isAnnotatedType, isAnnotatedTypeOfPrimitive };
|
|
138
|
+
export type { TAnnotatedTypeHandle, TAtscriptAnnotatedType, TAtscriptAnnotatedTypeConstructor, TAtscriptTypeArray, TAtscriptTypeComplex, TAtscriptTypeDef, TAtscriptTypeFinal, TAtscriptTypeObject, TMetadataMap, TValidatorOptions, TValidatorPlugin, TValidatorPluginContext };
|
package/dist/utils.mjs
CHANGED
|
@@ -337,6 +337,15 @@ var ValidatorError = class extends Error {
|
|
|
337
337
|
function isAnnotatedType(type) {
|
|
338
338
|
return type && type.__is_atscript_annotated_type;
|
|
339
339
|
}
|
|
340
|
+
function annotate(metadata, key, value, asArray) {
|
|
341
|
+
if (!metadata) return;
|
|
342
|
+
if (asArray) if (metadata.has(key)) {
|
|
343
|
+
const a = metadata.get(key);
|
|
344
|
+
if (Array.isArray(a)) a.push(value);
|
|
345
|
+
else metadata.set(key, [a, value]);
|
|
346
|
+
} else metadata.set(key, [value]);
|
|
347
|
+
else metadata.set(key, value);
|
|
348
|
+
}
|
|
340
349
|
function defineAnnotatedType(_kind, base) {
|
|
341
350
|
const kind = _kind || "";
|
|
342
351
|
const type = base?.type || {};
|
|
@@ -436,12 +445,7 @@ else if (!newBase) throw new Error(`"${typeName}" is not annotated type`);
|
|
|
436
445
|
return this;
|
|
437
446
|
},
|
|
438
447
|
annotate(key, value, asArray) {
|
|
439
|
-
|
|
440
|
-
const a = this.$metadata.get(key);
|
|
441
|
-
if (Array.isArray(a)) a.push(value);
|
|
442
|
-
else this.$metadata.set(key, [a, value]);
|
|
443
|
-
} else this.$metadata.set(key, [value]);
|
|
444
|
-
else this.$metadata.set(key, value);
|
|
448
|
+
annotate(this.$metadata, key, value, asArray);
|
|
445
449
|
return this;
|
|
446
450
|
}
|
|
447
451
|
};
|
|
@@ -543,4 +547,4 @@ else schema.allOf = (schema.allOf || []).concat(patterns.map((p) => ({ pattern:
|
|
|
543
547
|
}
|
|
544
548
|
|
|
545
549
|
//#endregion
|
|
546
|
-
export { Validator, ValidatorError, buildJsonSchema, defineAnnotatedType, isAnnotatedType, isAnnotatedTypeOfPrimitive };
|
|
550
|
+
export { Validator, ValidatorError, annotate, buildJsonSchema, defineAnnotatedType, isAnnotatedType, isAnnotatedTypeOfPrimitive };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atscript/typescript",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Atscript: typescript-gen support.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.mjs",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"homepage": "https://github.com/moostjs/atscript/tree/main/packages/typescript#readme",
|
|
71
71
|
"license": "ISC",
|
|
72
72
|
"peerDependencies": {
|
|
73
|
-
"@atscript/core": "^0.1.
|
|
73
|
+
"@atscript/core": "^0.1.2"
|
|
74
74
|
},
|
|
75
75
|
"dependencies": {
|
|
76
76
|
"@moostjs/event-cli": "^0.5.32",
|