@atscript/typescript 0.1.1 → 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 +165 -85
- package/dist/index.cjs +165 -85
- package/dist/index.mjs +165 -85
- 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,27 +1000,26 @@ 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
|
}
|
|
@@ -1041,7 +1053,7 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
1041
1053
|
}
|
|
1042
1054
|
renderAnnotate(node) {
|
|
1043
1055
|
if (node.isMutating) {
|
|
1044
|
-
this.
|
|
1056
|
+
this.postAnnotate.push(node);
|
|
1045
1057
|
return;
|
|
1046
1058
|
}
|
|
1047
1059
|
const targetName = node.targetName;
|
|
@@ -1329,18 +1341,13 @@ else handle.prop(prop.id, propHandle.$type);
|
|
|
1329
1341
|
return this;
|
|
1330
1342
|
}
|
|
1331
1343
|
defineMetadata(node) {
|
|
1332
|
-
|
|
1333
|
-
let adHocNames;
|
|
1334
|
-
let adHoc;
|
|
1344
|
+
let annotations = this.doc.evalAnnotationsForNode(node);
|
|
1335
1345
|
if (this._adHocAnnotations && this._propPath.length > 0) {
|
|
1336
1346
|
const path$3 = this._propPath.join(".");
|
|
1337
|
-
adHoc = this._adHocAnnotations.get(path$3);
|
|
1338
|
-
if (adHoc)
|
|
1347
|
+
const adHoc = this._adHocAnnotations.get(path$3);
|
|
1348
|
+
if (adHoc) annotations = this.doc.mergeNodesAnnotations(annotations, adHoc);
|
|
1339
1349
|
}
|
|
1340
1350
|
annotations?.forEach((an) => {
|
|
1341
|
-
if (!adHocNames || !adHocNames.has(an.name)) this.resolveAnnotationValue(node, an);
|
|
1342
|
-
});
|
|
1343
|
-
adHoc?.forEach((an) => {
|
|
1344
1351
|
this.resolveAnnotationValue(node, an);
|
|
1345
1352
|
});
|
|
1346
1353
|
return this;
|
|
@@ -1352,11 +1359,8 @@ else handle.prop(prop.id, propHandle.$type);
|
|
|
1352
1359
|
const annotateAnnotations = this.doc.evalAnnotationsForNode(annotateNode);
|
|
1353
1360
|
const targetDecl = this.doc.getDeclarationOwnerNode(annotateNode.targetName);
|
|
1354
1361
|
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) => {
|
|
1362
|
+
const merged = this.doc.mergeNodesAnnotations(targetAnnotations, annotateAnnotations);
|
|
1363
|
+
merged.forEach((an) => {
|
|
1360
1364
|
this.resolveAnnotationValue(annotateNode, an);
|
|
1361
1365
|
});
|
|
1362
1366
|
return this;
|
|
@@ -1395,47 +1399,123 @@ else targetValue = "true";
|
|
|
1395
1399
|
multiple: !!multiple
|
|
1396
1400
|
};
|
|
1397
1401
|
}
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
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();
|
|
1407
1413
|
for (const an of anns) {
|
|
1408
1414
|
const { value, multiple } = this.computeAnnotationValue(entry, an);
|
|
1409
1415
|
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})`);
|
|
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})`);
|
|
1419
1423
|
}
|
|
1420
1424
|
}
|
|
1421
|
-
|
|
1422
|
-
|
|
1425
|
+
}
|
|
1426
|
+
const topAnnotations = node.annotations;
|
|
1427
|
+
if (topAnnotations && topAnnotations.length > 0) {
|
|
1428
|
+
const cleared = new Set();
|
|
1429
|
+
for (const an of topAnnotations) {
|
|
1423
1430
|
const { value, multiple } = this.computeAnnotationValue(node, an);
|
|
1424
1431
|
if (multiple) {
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
this.writeln(
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
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
|
+
}
|
|
1434
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;
|
|
1435
1514
|
}
|
|
1515
|
+
return [];
|
|
1436
1516
|
}
|
|
1437
1517
|
constructor(doc, opts) {
|
|
1438
|
-
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 = [];
|
|
1439
1519
|
}
|
|
1440
1520
|
};
|
|
1441
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,27 +997,26 @@ 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
|
}
|
|
@@ -1038,7 +1050,7 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
1038
1050
|
}
|
|
1039
1051
|
renderAnnotate(node) {
|
|
1040
1052
|
if (node.isMutating) {
|
|
1041
|
-
this.
|
|
1053
|
+
this.postAnnotate.push(node);
|
|
1042
1054
|
return;
|
|
1043
1055
|
}
|
|
1044
1056
|
const targetName = node.targetName;
|
|
@@ -1326,18 +1338,13 @@ else handle.prop(prop.id, propHandle.$type);
|
|
|
1326
1338
|
return this;
|
|
1327
1339
|
}
|
|
1328
1340
|
defineMetadata(node) {
|
|
1329
|
-
|
|
1330
|
-
let adHocNames;
|
|
1331
|
-
let adHoc;
|
|
1341
|
+
let annotations = this.doc.evalAnnotationsForNode(node);
|
|
1332
1342
|
if (this._adHocAnnotations && this._propPath.length > 0) {
|
|
1333
1343
|
const path$2 = this._propPath.join(".");
|
|
1334
|
-
adHoc = this._adHocAnnotations.get(path$2);
|
|
1335
|
-
if (adHoc)
|
|
1344
|
+
const adHoc = this._adHocAnnotations.get(path$2);
|
|
1345
|
+
if (adHoc) annotations = this.doc.mergeNodesAnnotations(annotations, adHoc);
|
|
1336
1346
|
}
|
|
1337
1347
|
annotations?.forEach((an) => {
|
|
1338
|
-
if (!adHocNames || !adHocNames.has(an.name)) this.resolveAnnotationValue(node, an);
|
|
1339
|
-
});
|
|
1340
|
-
adHoc?.forEach((an) => {
|
|
1341
1348
|
this.resolveAnnotationValue(node, an);
|
|
1342
1349
|
});
|
|
1343
1350
|
return this;
|
|
@@ -1349,11 +1356,8 @@ else handle.prop(prop.id, propHandle.$type);
|
|
|
1349
1356
|
const annotateAnnotations = this.doc.evalAnnotationsForNode(annotateNode);
|
|
1350
1357
|
const targetDecl = this.doc.getDeclarationOwnerNode(annotateNode.targetName);
|
|
1351
1358
|
const targetAnnotations = targetDecl?.node ? targetDecl.doc.evalAnnotationsForNode(targetDecl.node) : undefined;
|
|
1352
|
-
const
|
|
1353
|
-
|
|
1354
|
-
if (!overriddenNames.has(an.name)) this.resolveAnnotationValue(annotateNode, an);
|
|
1355
|
-
});
|
|
1356
|
-
annotateAnnotations?.forEach((an) => {
|
|
1359
|
+
const merged = this.doc.mergeNodesAnnotations(targetAnnotations, annotateAnnotations);
|
|
1360
|
+
merged.forEach((an) => {
|
|
1357
1361
|
this.resolveAnnotationValue(annotateNode, an);
|
|
1358
1362
|
});
|
|
1359
1363
|
return this;
|
|
@@ -1392,47 +1396,123 @@ else targetValue = "true";
|
|
|
1392
1396
|
multiple: !!multiple
|
|
1393
1397
|
};
|
|
1394
1398
|
}
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
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();
|
|
1404
1410
|
for (const an of anns) {
|
|
1405
1411
|
const { value, multiple } = this.computeAnnotationValue(entry, an);
|
|
1406
1412
|
if (multiple) {
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
this.writeln(
|
|
1413
|
-
|
|
1414
|
-
this.writeln(`}`);
|
|
1415
|
-
} else this.writeln(`${accessor}.metadata.set("${escapeQuotes(an.name)}", ${value})`);
|
|
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})`);
|
|
1416
1420
|
}
|
|
1417
1421
|
}
|
|
1418
|
-
|
|
1419
|
-
|
|
1422
|
+
}
|
|
1423
|
+
const topAnnotations = node.annotations;
|
|
1424
|
+
if (topAnnotations && topAnnotations.length > 0) {
|
|
1425
|
+
const cleared = new Set();
|
|
1426
|
+
for (const an of topAnnotations) {
|
|
1420
1427
|
const { value, multiple } = this.computeAnnotationValue(node, an);
|
|
1421
1428
|
if (multiple) {
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
this.writeln(
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
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
|
+
}
|
|
1431
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;
|
|
1432
1511
|
}
|
|
1512
|
+
return [];
|
|
1433
1513
|
}
|
|
1434
1514
|
constructor(doc, opts) {
|
|
1435
|
-
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 = [];
|
|
1436
1516
|
}
|
|
1437
1517
|
};
|
|
1438
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,27 +973,26 @@ 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
|
}
|
|
@@ -1014,7 +1026,7 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
1014
1026
|
}
|
|
1015
1027
|
renderAnnotate(node) {
|
|
1016
1028
|
if (node.isMutating) {
|
|
1017
|
-
this.
|
|
1029
|
+
this.postAnnotate.push(node);
|
|
1018
1030
|
return;
|
|
1019
1031
|
}
|
|
1020
1032
|
const targetName = node.targetName;
|
|
@@ -1302,18 +1314,13 @@ else handle.prop(prop.id, propHandle.$type);
|
|
|
1302
1314
|
return this;
|
|
1303
1315
|
}
|
|
1304
1316
|
defineMetadata(node) {
|
|
1305
|
-
|
|
1306
|
-
let adHocNames;
|
|
1307
|
-
let adHoc;
|
|
1317
|
+
let annotations = this.doc.evalAnnotationsForNode(node);
|
|
1308
1318
|
if (this._adHocAnnotations && this._propPath.length > 0) {
|
|
1309
1319
|
const path$1 = this._propPath.join(".");
|
|
1310
|
-
adHoc = this._adHocAnnotations.get(path$1);
|
|
1311
|
-
if (adHoc)
|
|
1320
|
+
const adHoc = this._adHocAnnotations.get(path$1);
|
|
1321
|
+
if (adHoc) annotations = this.doc.mergeNodesAnnotations(annotations, adHoc);
|
|
1312
1322
|
}
|
|
1313
1323
|
annotations?.forEach((an) => {
|
|
1314
|
-
if (!adHocNames || !adHocNames.has(an.name)) this.resolveAnnotationValue(node, an);
|
|
1315
|
-
});
|
|
1316
|
-
adHoc?.forEach((an) => {
|
|
1317
1324
|
this.resolveAnnotationValue(node, an);
|
|
1318
1325
|
});
|
|
1319
1326
|
return this;
|
|
@@ -1325,11 +1332,8 @@ else handle.prop(prop.id, propHandle.$type);
|
|
|
1325
1332
|
const annotateAnnotations = this.doc.evalAnnotationsForNode(annotateNode);
|
|
1326
1333
|
const targetDecl = this.doc.getDeclarationOwnerNode(annotateNode.targetName);
|
|
1327
1334
|
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) => {
|
|
1335
|
+
const merged = this.doc.mergeNodesAnnotations(targetAnnotations, annotateAnnotations);
|
|
1336
|
+
merged.forEach((an) => {
|
|
1333
1337
|
this.resolveAnnotationValue(annotateNode, an);
|
|
1334
1338
|
});
|
|
1335
1339
|
return this;
|
|
@@ -1368,47 +1372,123 @@ else targetValue = "true";
|
|
|
1368
1372
|
multiple: !!multiple
|
|
1369
1373
|
};
|
|
1370
1374
|
}
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
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();
|
|
1380
1386
|
for (const an of anns) {
|
|
1381
1387
|
const { value, multiple } = this.computeAnnotationValue(entry, an);
|
|
1382
1388
|
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})`);
|
|
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})`);
|
|
1392
1396
|
}
|
|
1393
1397
|
}
|
|
1394
|
-
|
|
1395
|
-
|
|
1398
|
+
}
|
|
1399
|
+
const topAnnotations = node.annotations;
|
|
1400
|
+
if (topAnnotations && topAnnotations.length > 0) {
|
|
1401
|
+
const cleared = new Set();
|
|
1402
|
+
for (const an of topAnnotations) {
|
|
1396
1403
|
const { value, multiple } = this.computeAnnotationValue(node, an);
|
|
1397
1404
|
if (multiple) {
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
this.writeln(
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
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
|
+
}
|
|
1407
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;
|
|
1408
1487
|
}
|
|
1488
|
+
return [];
|
|
1409
1489
|
}
|
|
1410
1490
|
constructor(doc, opts) {
|
|
1411
|
-
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 = [];
|
|
1412
1492
|
}
|
|
1413
1493
|
};
|
|
1414
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",
|