@atscript/typescript 0.1.25 → 0.1.27
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/README.md +50 -0
- package/dist/cli.cjs +140 -46
- package/dist/index.cjs +143 -51
- package/dist/index.d.ts +1 -1
- package/dist/index.mjs +141 -51
- package/dist/utils.cjs +163 -39
- package/dist/utils.d.ts +24 -2
- package/dist/utils.mjs +162 -40
- package/package.json +5 -5
- package/scripts/setup-skills.js +23 -13
- package/skills/atscript-typescript/SKILL.md +22 -14
- package/skills/atscript-typescript/annotations.md +51 -50
- package/skills/atscript-typescript/codegen.md +15 -10
- package/skills/atscript-typescript/core.md +23 -21
- package/skills/atscript-typescript/runtime.md +66 -52
- package/skills/atscript-typescript/syntax.md +31 -31
- package/skills/atscript-typescript/utilities.md +137 -72
- package/skills/atscript-typescript/validation.md +39 -39
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
2
3
|
//#region rolldown:runtime
|
|
3
4
|
var __create = Object.create;
|
|
4
5
|
var __defProp = Object.defineProperty;
|
|
@@ -968,6 +969,7 @@ else if (!newBase) throw new Error(`"${typeName}" is not annotated type`);
|
|
|
968
969
|
__is_atscript_annotated_type: true,
|
|
969
970
|
type: newBase.type,
|
|
970
971
|
metadata,
|
|
972
|
+
id: newBase.id,
|
|
971
973
|
validator(opts) {
|
|
972
974
|
return new Validator(this, opts);
|
|
973
975
|
}
|
|
@@ -978,6 +980,10 @@ else if (!newBase) throw new Error(`"${typeName}" is not annotated type`);
|
|
|
978
980
|
annotate(key, value, asArray) {
|
|
979
981
|
annotate(this.$metadata, key, value, asArray);
|
|
980
982
|
return this;
|
|
983
|
+
},
|
|
984
|
+
id(value) {
|
|
985
|
+
this.$type.id = value;
|
|
986
|
+
return this;
|
|
981
987
|
}
|
|
982
988
|
};
|
|
983
989
|
return handle;
|
|
@@ -1029,47 +1035,71 @@ function isPhantomType(def) {
|
|
|
1029
1035
|
return null;
|
|
1030
1036
|
}
|
|
1031
1037
|
function buildJsonSchema(type) {
|
|
1038
|
+
const defs = {};
|
|
1039
|
+
let isRoot = true;
|
|
1040
|
+
const buildObject = (d) => {
|
|
1041
|
+
const properties = {};
|
|
1042
|
+
const required = [];
|
|
1043
|
+
for (const [key, val] of d.type.props.entries()) {
|
|
1044
|
+
if (isPhantomType(val)) continue;
|
|
1045
|
+
properties[key] = build(val);
|
|
1046
|
+
if (!val.optional) required.push(key);
|
|
1047
|
+
}
|
|
1048
|
+
const schema$1 = {
|
|
1049
|
+
type: "object",
|
|
1050
|
+
properties
|
|
1051
|
+
};
|
|
1052
|
+
if (required.length > 0) schema$1.required = required;
|
|
1053
|
+
return schema$1;
|
|
1054
|
+
};
|
|
1032
1055
|
const build = (def) => {
|
|
1056
|
+
if (def.id && def.type.kind === "object" && !isRoot) {
|
|
1057
|
+
const name = def.id;
|
|
1058
|
+
if (!defs[name]) {
|
|
1059
|
+
defs[name] = {};
|
|
1060
|
+
defs[name] = buildObject(def);
|
|
1061
|
+
}
|
|
1062
|
+
return { $ref: `#/$defs/${name}` };
|
|
1063
|
+
}
|
|
1064
|
+
isRoot = false;
|
|
1033
1065
|
const meta = def.metadata;
|
|
1034
1066
|
return forAnnotatedType(def, {
|
|
1035
1067
|
phantom() {
|
|
1036
1068
|
return {};
|
|
1037
1069
|
},
|
|
1038
1070
|
object(d) {
|
|
1039
|
-
|
|
1040
|
-
const required = [];
|
|
1041
|
-
for (const [key, val] of d.type.props.entries()) {
|
|
1042
|
-
if (isPhantomType(val)) continue;
|
|
1043
|
-
properties[key] = build(val);
|
|
1044
|
-
if (!val.optional) required.push(key);
|
|
1045
|
-
}
|
|
1046
|
-
const schema = {
|
|
1047
|
-
type: "object",
|
|
1048
|
-
properties
|
|
1049
|
-
};
|
|
1050
|
-
if (required.length > 0) schema.required = required;
|
|
1051
|
-
return schema;
|
|
1071
|
+
return buildObject(d);
|
|
1052
1072
|
},
|
|
1053
1073
|
array(d) {
|
|
1054
|
-
const schema = {
|
|
1074
|
+
const schema$1 = {
|
|
1055
1075
|
type: "array",
|
|
1056
1076
|
items: build(d.type.of)
|
|
1057
1077
|
};
|
|
1058
1078
|
const minLength = meta.get("expect.minLength");
|
|
1059
|
-
if (minLength) schema.minItems = typeof minLength === "number" ? minLength : minLength.length;
|
|
1079
|
+
if (minLength) schema$1.minItems = typeof minLength === "number" ? minLength : minLength.length;
|
|
1060
1080
|
const maxLength = meta.get("expect.maxLength");
|
|
1061
|
-
if (maxLength) schema.maxItems = typeof maxLength === "number" ? maxLength : maxLength.length;
|
|
1062
|
-
return schema;
|
|
1081
|
+
if (maxLength) schema$1.maxItems = typeof maxLength === "number" ? maxLength : maxLength.length;
|
|
1082
|
+
return schema$1;
|
|
1063
1083
|
},
|
|
1064
1084
|
union(d) {
|
|
1065
1085
|
const disc = detectDiscriminator(d.type.items);
|
|
1066
|
-
if (disc)
|
|
1067
|
-
oneOf
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1086
|
+
if (disc) {
|
|
1087
|
+
const oneOf = d.type.items.map(build);
|
|
1088
|
+
const mapping = {};
|
|
1089
|
+
for (const [val, origPath] of Object.entries(disc.mapping)) {
|
|
1090
|
+
const idx = Number.parseInt(origPath.split("/").pop(), 10);
|
|
1091
|
+
const item = d.type.items[idx];
|
|
1092
|
+
if (item.id && defs[item.id]) mapping[val] = `#/$defs/${item.id}`;
|
|
1093
|
+
else mapping[val] = origPath;
|
|
1071
1094
|
}
|
|
1072
|
-
|
|
1095
|
+
return {
|
|
1096
|
+
oneOf,
|
|
1097
|
+
discriminator: {
|
|
1098
|
+
propertyName: disc.propertyName,
|
|
1099
|
+
mapping
|
|
1100
|
+
}
|
|
1101
|
+
};
|
|
1102
|
+
}
|
|
1073
1103
|
return { anyOf: d.type.items.map(build) };
|
|
1074
1104
|
},
|
|
1075
1105
|
intersection(d) {
|
|
@@ -1083,33 +1113,38 @@ function buildJsonSchema(type) {
|
|
|
1083
1113
|
};
|
|
1084
1114
|
},
|
|
1085
1115
|
final(d) {
|
|
1086
|
-
const schema = {};
|
|
1087
|
-
if (d.type.value !== undefined) schema.const = d.type.value;
|
|
1116
|
+
const schema$1 = {};
|
|
1117
|
+
if (d.type.value !== undefined) schema$1.const = d.type.value;
|
|
1088
1118
|
if (d.type.designType && d.type.designType !== "any") {
|
|
1089
|
-
schema.type = d.type.designType === "undefined" ? "null" : d.type.designType;
|
|
1090
|
-
if (schema.type === "number" && meta.get("expect.int")) schema.type = "integer";
|
|
1119
|
+
schema$1.type = d.type.designType === "undefined" ? "null" : d.type.designType;
|
|
1120
|
+
if (schema$1.type === "number" && meta.get("expect.int")) schema$1.type = "integer";
|
|
1091
1121
|
}
|
|
1092
|
-
if (schema.type === "string") {
|
|
1093
|
-
if (meta.get("meta.required")) schema.minLength = 1;
|
|
1122
|
+
if (schema$1.type === "string") {
|
|
1123
|
+
if (meta.get("meta.required")) schema$1.minLength = 1;
|
|
1094
1124
|
const minLength = meta.get("expect.minLength");
|
|
1095
|
-
if (minLength) schema.minLength = typeof minLength === "number" ? minLength : minLength.length;
|
|
1125
|
+
if (minLength) schema$1.minLength = typeof minLength === "number" ? minLength : minLength.length;
|
|
1096
1126
|
const maxLength = meta.get("expect.maxLength");
|
|
1097
|
-
if (maxLength) schema.maxLength = typeof maxLength === "number" ? maxLength : maxLength.length;
|
|
1127
|
+
if (maxLength) schema$1.maxLength = typeof maxLength === "number" ? maxLength : maxLength.length;
|
|
1098
1128
|
const patterns = meta.get("expect.pattern");
|
|
1099
|
-
if (patterns?.length) if (patterns.length === 1) schema.pattern = patterns[0].pattern;
|
|
1100
|
-
else schema.allOf = (schema.allOf || []).concat(patterns.map((p) => ({ pattern: p.pattern })));
|
|
1129
|
+
if (patterns?.length) if (patterns.length === 1) schema$1.pattern = patterns[0].pattern;
|
|
1130
|
+
else schema$1.allOf = (schema$1.allOf || []).concat(patterns.map((p) => ({ pattern: p.pattern })));
|
|
1101
1131
|
}
|
|
1102
|
-
if (schema.type === "number" || schema.type === "integer") {
|
|
1132
|
+
if (schema$1.type === "number" || schema$1.type === "integer") {
|
|
1103
1133
|
const min = meta.get("expect.min");
|
|
1104
|
-
if (min) schema.minimum = typeof min === "number" ? min : min.minValue;
|
|
1134
|
+
if (min) schema$1.minimum = typeof min === "number" ? min : min.minValue;
|
|
1105
1135
|
const max = meta.get("expect.max");
|
|
1106
|
-
if (max) schema.maximum = typeof max === "number" ? max : max.maxValue;
|
|
1136
|
+
if (max) schema$1.maximum = typeof max === "number" ? max : max.maxValue;
|
|
1107
1137
|
}
|
|
1108
|
-
return schema;
|
|
1138
|
+
return schema$1;
|
|
1109
1139
|
}
|
|
1110
1140
|
});
|
|
1111
1141
|
};
|
|
1112
|
-
|
|
1142
|
+
const schema = build(type);
|
|
1143
|
+
if (Object.keys(defs).length > 0) return {
|
|
1144
|
+
...schema,
|
|
1145
|
+
$defs: defs
|
|
1146
|
+
};
|
|
1147
|
+
return schema;
|
|
1113
1148
|
}
|
|
1114
1149
|
|
|
1115
1150
|
//#endregion
|
|
@@ -1130,11 +1165,23 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
1130
1165
|
this.writeln("/* eslint-disable */");
|
|
1131
1166
|
this.writeln("/* oxlint-disable */");
|
|
1132
1167
|
const imports = ["defineAnnotatedType as $", "annotate as $a"];
|
|
1168
|
+
const hasMutatingAnnotate = this.doc.nodes.some((n) => n.entity === "annotate" && n.isMutating);
|
|
1169
|
+
if (hasMutatingAnnotate) imports.push("cloneRefProp as $c");
|
|
1133
1170
|
const jsonSchemaMode = resolveJsonSchemaMode(this.opts);
|
|
1134
1171
|
if (jsonSchemaMode === "lazy") imports.push("buildJsonSchema as $$");
|
|
1135
1172
|
if (this.opts?.exampleData) imports.push("createDataFromAnnotatedType as $e");
|
|
1136
1173
|
if (jsonSchemaMode === false) imports.push("throwFeatureDisabled as $d");
|
|
1137
1174
|
this.writeln(`import { ${imports.join(", ")} } from "@atscript/typescript/utils"`);
|
|
1175
|
+
const nameCounts = new Map();
|
|
1176
|
+
const nodesByName = new Map();
|
|
1177
|
+
for (const node of this.doc.nodes) if (node.__typeId !== null && node.__typeId !== undefined && node.id) {
|
|
1178
|
+
const name = node.id;
|
|
1179
|
+
nameCounts.set(name, (nameCounts.get(name) || 0) + 1);
|
|
1180
|
+
if (!nodesByName.has(name)) nodesByName.set(name, []);
|
|
1181
|
+
nodesByName.get(name).push(node);
|
|
1182
|
+
}
|
|
1183
|
+
for (const [name, nodes] of nodesByName) if (nodes.length === 1) this.typeIds.set(nodes[0], name);
|
|
1184
|
+
else for (let i = 0; i < nodes.length; i++) this.typeIds.set(nodes[i], `${name}__${i + 1}`);
|
|
1138
1185
|
}
|
|
1139
1186
|
buildAdHocMap(annotateNodes) {
|
|
1140
1187
|
const map = new Map();
|
|
@@ -1148,6 +1195,15 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
1148
1195
|
}
|
|
1149
1196
|
return map.size > 0 ? map : null;
|
|
1150
1197
|
}
|
|
1198
|
+
/**
|
|
1199
|
+
* Checks if any ad-hoc annotation path extends beyond the current _propPath,
|
|
1200
|
+
* meaning annotations target properties inside a referenced type.
|
|
1201
|
+
*/ hasAdHocAnnotationsThroughRef() {
|
|
1202
|
+
if (!this._adHocAnnotations || this._propPath.length === 0) return false;
|
|
1203
|
+
const prefix = `${this._propPath.join(".")}.`;
|
|
1204
|
+
for (const key of this._adHocAnnotations.keys()) if (key.startsWith(prefix)) return true;
|
|
1205
|
+
return false;
|
|
1206
|
+
}
|
|
1151
1207
|
post() {
|
|
1152
1208
|
for (const node of this.postAnnotate) if (node.entity === "annotate") {
|
|
1153
1209
|
const annotateNode = node;
|
|
@@ -1178,6 +1234,8 @@ else {
|
|
|
1178
1234
|
this.writeln("static __is_atscript_annotated_type = true");
|
|
1179
1235
|
this.writeln("static type = {}");
|
|
1180
1236
|
this.writeln("static metadata = new Map()");
|
|
1237
|
+
const typeId = this.typeIds.get(node);
|
|
1238
|
+
if (typeId) this.writeln(`static id = "${typeId}"`);
|
|
1181
1239
|
this.renderJsonSchemaMethod(node);
|
|
1182
1240
|
this.renderExampleDataMethod(node);
|
|
1183
1241
|
}
|
|
@@ -1256,6 +1314,8 @@ else {
|
|
|
1256
1314
|
case "type": {
|
|
1257
1315
|
const def = node.getDefinition();
|
|
1258
1316
|
const handle = this.toAnnotatedHandle(def, true);
|
|
1317
|
+
const typeId = this.typeIds.get(node) ?? (node.__typeId !== null && node.__typeId !== undefined ? node.id : undefined);
|
|
1318
|
+
if (typeId) handle.id(typeId);
|
|
1259
1319
|
return skipAnnotations ? handle : this.applyExpectAnnotations(handle, this.doc.evalAnnotationsForNode(node));
|
|
1260
1320
|
}
|
|
1261
1321
|
case "prop": {
|
|
@@ -1382,6 +1442,14 @@ else handle.prop(prop.id, propHandle.$type);
|
|
|
1382
1442
|
return this;
|
|
1383
1443
|
}
|
|
1384
1444
|
}
|
|
1445
|
+
if (this._adHocAnnotations && this.hasAdHocAnnotationsThroughRef()) {
|
|
1446
|
+
let resolved = decl ? this.doc.mergeIntersection(decl) : undefined;
|
|
1447
|
+
if (resolved && (0, __atscript_core.isInterface)(resolved)) resolved = resolved.getDefinition() || resolved;
|
|
1448
|
+
if (resolved) {
|
|
1449
|
+
this.annotateType(resolved, name);
|
|
1450
|
+
return this;
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1385
1453
|
const chain = ref.hasChain ? `, [${ref.chain.map((c) => `"${escapeQuotes(c.text)}"`).join(", ")}]` : "";
|
|
1386
1454
|
this.writeln(`$(${name ? `"", ${name}` : ""})`).indent().writeln(`.refTo(${ref.id}${chain})`);
|
|
1387
1455
|
if (!ref.hasChain) {
|
|
@@ -1615,11 +1683,29 @@ else targetValue = "true";
|
|
|
1615
1683
|
const targetName = node.targetName;
|
|
1616
1684
|
const targetDef = this.resolveTargetDef(targetName);
|
|
1617
1685
|
this.writeln("// Ad-hoc annotations for ", targetName);
|
|
1686
|
+
const allClones = [];
|
|
1687
|
+
const entryAccessors = [];
|
|
1618
1688
|
for (const entry of node.entries) {
|
|
1619
1689
|
const anns = entry.annotations;
|
|
1620
1690
|
if (!anns || anns.length === 0) continue;
|
|
1621
1691
|
const parts = entry.hasChain ? [entry.id, ...entry.chain.map((c) => c.text)] : [entry.id];
|
|
1622
|
-
const accessors = this.buildMutatingAccessors(targetName, targetDef, parts);
|
|
1692
|
+
const { accessors, clones } = this.buildMutatingAccessors(targetName, targetDef, parts);
|
|
1693
|
+
allClones.push(...clones);
|
|
1694
|
+
entryAccessors.push({
|
|
1695
|
+
entry,
|
|
1696
|
+
accessors
|
|
1697
|
+
});
|
|
1698
|
+
}
|
|
1699
|
+
const cloneKeys = new Set();
|
|
1700
|
+
for (const clone of allClones) {
|
|
1701
|
+
const key = `${clone.parentPath}|${clone.propName}`;
|
|
1702
|
+
if (!cloneKeys.has(key)) {
|
|
1703
|
+
cloneKeys.add(key);
|
|
1704
|
+
this.writeln(`$c(${clone.parentPath}, "${escapeQuotes(clone.propName)}")`);
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
for (const { entry, accessors } of entryAccessors) {
|
|
1708
|
+
const anns = entry.annotations;
|
|
1623
1709
|
for (const accessor of accessors) {
|
|
1624
1710
|
const cleared = new Set();
|
|
1625
1711
|
for (const an of anns) {
|
|
@@ -1669,15 +1755,21 @@ else targetValue = "true";
|
|
|
1669
1755
|
prefix: `${targetName}.type`,
|
|
1670
1756
|
def: targetDef
|
|
1671
1757
|
}];
|
|
1758
|
+
const clones = [];
|
|
1672
1759
|
for (let i = 0; i < parts.length; i++) {
|
|
1673
1760
|
const nextAccessors = [];
|
|
1674
1761
|
for (const { prefix, def } of accessors) {
|
|
1675
1762
|
const results = this.buildPropPaths(def, parts[i]);
|
|
1676
|
-
if (results.length > 0) for (const result of results) if (i < parts.length - 1)
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1763
|
+
if (results.length > 0) for (const result of results) if (i < parts.length - 1) {
|
|
1764
|
+
if (result.propDef && (0, __atscript_core.isRef)(result.propDef)) clones.push({
|
|
1765
|
+
parentPath: prefix,
|
|
1766
|
+
propName: parts[i]
|
|
1767
|
+
});
|
|
1768
|
+
nextAccessors.push({
|
|
1769
|
+
prefix: `${prefix}${result.path}?.type`,
|
|
1770
|
+
def: result.propDef
|
|
1771
|
+
});
|
|
1772
|
+
} else nextAccessors.push({
|
|
1681
1773
|
prefix: `${prefix}${result.path}?`,
|
|
1682
1774
|
def: result.propDef
|
|
1683
1775
|
});
|
|
@@ -1691,7 +1783,10 @@ else {
|
|
|
1691
1783
|
}
|
|
1692
1784
|
accessors = nextAccessors;
|
|
1693
1785
|
}
|
|
1694
|
-
return
|
|
1786
|
+
return {
|
|
1787
|
+
accessors: accessors.map((a) => a.prefix),
|
|
1788
|
+
clones
|
|
1789
|
+
};
|
|
1695
1790
|
}
|
|
1696
1791
|
/**
|
|
1697
1792
|
* Finds a property in a type tree at compile time, returning all
|
|
@@ -1727,7 +1822,7 @@ else {
|
|
|
1727
1822
|
return [];
|
|
1728
1823
|
}
|
|
1729
1824
|
constructor(doc, opts) {
|
|
1730
|
-
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 = [];
|
|
1825
|
+
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), _define_property(this, "typeIds", void 0), this.opts = opts, this.postAnnotate = [], this._adHocAnnotations = null, this._propPath = [], this.typeIds = new Map();
|
|
1731
1826
|
}
|
|
1732
1827
|
};
|
|
1733
1828
|
|
|
@@ -1776,8 +1871,5 @@ else return t.optional ? `${t.type} | true` : t.type;
|
|
|
1776
1871
|
};
|
|
1777
1872
|
|
|
1778
1873
|
//#endregion
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
//#endregion
|
|
1783
|
-
module.exports = src_default;
|
|
1874
|
+
exports.default = tsPlugin
|
|
1875
|
+
exports.tsPlugin = tsPlugin
|
package/dist/index.d.ts
CHANGED
package/dist/index.mjs
CHANGED
|
@@ -944,6 +944,7 @@ else if (!newBase) throw new Error(`"${typeName}" is not annotated type`);
|
|
|
944
944
|
__is_atscript_annotated_type: true,
|
|
945
945
|
type: newBase.type,
|
|
946
946
|
metadata,
|
|
947
|
+
id: newBase.id,
|
|
947
948
|
validator(opts) {
|
|
948
949
|
return new Validator(this, opts);
|
|
949
950
|
}
|
|
@@ -954,6 +955,10 @@ else if (!newBase) throw new Error(`"${typeName}" is not annotated type`);
|
|
|
954
955
|
annotate(key, value, asArray) {
|
|
955
956
|
annotate(this.$metadata, key, value, asArray);
|
|
956
957
|
return this;
|
|
958
|
+
},
|
|
959
|
+
id(value) {
|
|
960
|
+
this.$type.id = value;
|
|
961
|
+
return this;
|
|
957
962
|
}
|
|
958
963
|
};
|
|
959
964
|
return handle;
|
|
@@ -1005,47 +1010,71 @@ function isPhantomType(def) {
|
|
|
1005
1010
|
return null;
|
|
1006
1011
|
}
|
|
1007
1012
|
function buildJsonSchema(type) {
|
|
1013
|
+
const defs = {};
|
|
1014
|
+
let isRoot = true;
|
|
1015
|
+
const buildObject = (d) => {
|
|
1016
|
+
const properties = {};
|
|
1017
|
+
const required = [];
|
|
1018
|
+
for (const [key, val] of d.type.props.entries()) {
|
|
1019
|
+
if (isPhantomType(val)) continue;
|
|
1020
|
+
properties[key] = build(val);
|
|
1021
|
+
if (!val.optional) required.push(key);
|
|
1022
|
+
}
|
|
1023
|
+
const schema$1 = {
|
|
1024
|
+
type: "object",
|
|
1025
|
+
properties
|
|
1026
|
+
};
|
|
1027
|
+
if (required.length > 0) schema$1.required = required;
|
|
1028
|
+
return schema$1;
|
|
1029
|
+
};
|
|
1008
1030
|
const build = (def) => {
|
|
1031
|
+
if (def.id && def.type.kind === "object" && !isRoot) {
|
|
1032
|
+
const name = def.id;
|
|
1033
|
+
if (!defs[name]) {
|
|
1034
|
+
defs[name] = {};
|
|
1035
|
+
defs[name] = buildObject(def);
|
|
1036
|
+
}
|
|
1037
|
+
return { $ref: `#/$defs/${name}` };
|
|
1038
|
+
}
|
|
1039
|
+
isRoot = false;
|
|
1009
1040
|
const meta = def.metadata;
|
|
1010
1041
|
return forAnnotatedType(def, {
|
|
1011
1042
|
phantom() {
|
|
1012
1043
|
return {};
|
|
1013
1044
|
},
|
|
1014
1045
|
object(d) {
|
|
1015
|
-
|
|
1016
|
-
const required = [];
|
|
1017
|
-
for (const [key, val] of d.type.props.entries()) {
|
|
1018
|
-
if (isPhantomType(val)) continue;
|
|
1019
|
-
properties[key] = build(val);
|
|
1020
|
-
if (!val.optional) required.push(key);
|
|
1021
|
-
}
|
|
1022
|
-
const schema = {
|
|
1023
|
-
type: "object",
|
|
1024
|
-
properties
|
|
1025
|
-
};
|
|
1026
|
-
if (required.length > 0) schema.required = required;
|
|
1027
|
-
return schema;
|
|
1046
|
+
return buildObject(d);
|
|
1028
1047
|
},
|
|
1029
1048
|
array(d) {
|
|
1030
|
-
const schema = {
|
|
1049
|
+
const schema$1 = {
|
|
1031
1050
|
type: "array",
|
|
1032
1051
|
items: build(d.type.of)
|
|
1033
1052
|
};
|
|
1034
1053
|
const minLength = meta.get("expect.minLength");
|
|
1035
|
-
if (minLength) schema.minItems = typeof minLength === "number" ? minLength : minLength.length;
|
|
1054
|
+
if (minLength) schema$1.minItems = typeof minLength === "number" ? minLength : minLength.length;
|
|
1036
1055
|
const maxLength = meta.get("expect.maxLength");
|
|
1037
|
-
if (maxLength) schema.maxItems = typeof maxLength === "number" ? maxLength : maxLength.length;
|
|
1038
|
-
return schema;
|
|
1056
|
+
if (maxLength) schema$1.maxItems = typeof maxLength === "number" ? maxLength : maxLength.length;
|
|
1057
|
+
return schema$1;
|
|
1039
1058
|
},
|
|
1040
1059
|
union(d) {
|
|
1041
1060
|
const disc = detectDiscriminator(d.type.items);
|
|
1042
|
-
if (disc)
|
|
1043
|
-
oneOf
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1061
|
+
if (disc) {
|
|
1062
|
+
const oneOf = d.type.items.map(build);
|
|
1063
|
+
const mapping = {};
|
|
1064
|
+
for (const [val, origPath] of Object.entries(disc.mapping)) {
|
|
1065
|
+
const idx = Number.parseInt(origPath.split("/").pop(), 10);
|
|
1066
|
+
const item = d.type.items[idx];
|
|
1067
|
+
if (item.id && defs[item.id]) mapping[val] = `#/$defs/${item.id}`;
|
|
1068
|
+
else mapping[val] = origPath;
|
|
1047
1069
|
}
|
|
1048
|
-
|
|
1070
|
+
return {
|
|
1071
|
+
oneOf,
|
|
1072
|
+
discriminator: {
|
|
1073
|
+
propertyName: disc.propertyName,
|
|
1074
|
+
mapping
|
|
1075
|
+
}
|
|
1076
|
+
};
|
|
1077
|
+
}
|
|
1049
1078
|
return { anyOf: d.type.items.map(build) };
|
|
1050
1079
|
},
|
|
1051
1080
|
intersection(d) {
|
|
@@ -1059,33 +1088,38 @@ function buildJsonSchema(type) {
|
|
|
1059
1088
|
};
|
|
1060
1089
|
},
|
|
1061
1090
|
final(d) {
|
|
1062
|
-
const schema = {};
|
|
1063
|
-
if (d.type.value !== undefined) schema.const = d.type.value;
|
|
1091
|
+
const schema$1 = {};
|
|
1092
|
+
if (d.type.value !== undefined) schema$1.const = d.type.value;
|
|
1064
1093
|
if (d.type.designType && d.type.designType !== "any") {
|
|
1065
|
-
schema.type = d.type.designType === "undefined" ? "null" : d.type.designType;
|
|
1066
|
-
if (schema.type === "number" && meta.get("expect.int")) schema.type = "integer";
|
|
1094
|
+
schema$1.type = d.type.designType === "undefined" ? "null" : d.type.designType;
|
|
1095
|
+
if (schema$1.type === "number" && meta.get("expect.int")) schema$1.type = "integer";
|
|
1067
1096
|
}
|
|
1068
|
-
if (schema.type === "string") {
|
|
1069
|
-
if (meta.get("meta.required")) schema.minLength = 1;
|
|
1097
|
+
if (schema$1.type === "string") {
|
|
1098
|
+
if (meta.get("meta.required")) schema$1.minLength = 1;
|
|
1070
1099
|
const minLength = meta.get("expect.minLength");
|
|
1071
|
-
if (minLength) schema.minLength = typeof minLength === "number" ? minLength : minLength.length;
|
|
1100
|
+
if (minLength) schema$1.minLength = typeof minLength === "number" ? minLength : minLength.length;
|
|
1072
1101
|
const maxLength = meta.get("expect.maxLength");
|
|
1073
|
-
if (maxLength) schema.maxLength = typeof maxLength === "number" ? maxLength : maxLength.length;
|
|
1102
|
+
if (maxLength) schema$1.maxLength = typeof maxLength === "number" ? maxLength : maxLength.length;
|
|
1074
1103
|
const patterns = meta.get("expect.pattern");
|
|
1075
|
-
if (patterns?.length) if (patterns.length === 1) schema.pattern = patterns[0].pattern;
|
|
1076
|
-
else schema.allOf = (schema.allOf || []).concat(patterns.map((p) => ({ pattern: p.pattern })));
|
|
1104
|
+
if (patterns?.length) if (patterns.length === 1) schema$1.pattern = patterns[0].pattern;
|
|
1105
|
+
else schema$1.allOf = (schema$1.allOf || []).concat(patterns.map((p) => ({ pattern: p.pattern })));
|
|
1077
1106
|
}
|
|
1078
|
-
if (schema.type === "number" || schema.type === "integer") {
|
|
1107
|
+
if (schema$1.type === "number" || schema$1.type === "integer") {
|
|
1079
1108
|
const min = meta.get("expect.min");
|
|
1080
|
-
if (min) schema.minimum = typeof min === "number" ? min : min.minValue;
|
|
1109
|
+
if (min) schema$1.minimum = typeof min === "number" ? min : min.minValue;
|
|
1081
1110
|
const max = meta.get("expect.max");
|
|
1082
|
-
if (max) schema.maximum = typeof max === "number" ? max : max.maxValue;
|
|
1111
|
+
if (max) schema$1.maximum = typeof max === "number" ? max : max.maxValue;
|
|
1083
1112
|
}
|
|
1084
|
-
return schema;
|
|
1113
|
+
return schema$1;
|
|
1085
1114
|
}
|
|
1086
1115
|
});
|
|
1087
1116
|
};
|
|
1088
|
-
|
|
1117
|
+
const schema = build(type);
|
|
1118
|
+
if (Object.keys(defs).length > 0) return {
|
|
1119
|
+
...schema,
|
|
1120
|
+
$defs: defs
|
|
1121
|
+
};
|
|
1122
|
+
return schema;
|
|
1089
1123
|
}
|
|
1090
1124
|
|
|
1091
1125
|
//#endregion
|
|
@@ -1106,11 +1140,23 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
1106
1140
|
this.writeln("/* eslint-disable */");
|
|
1107
1141
|
this.writeln("/* oxlint-disable */");
|
|
1108
1142
|
const imports = ["defineAnnotatedType as $", "annotate as $a"];
|
|
1143
|
+
const hasMutatingAnnotate = this.doc.nodes.some((n) => n.entity === "annotate" && n.isMutating);
|
|
1144
|
+
if (hasMutatingAnnotate) imports.push("cloneRefProp as $c");
|
|
1109
1145
|
const jsonSchemaMode = resolveJsonSchemaMode(this.opts);
|
|
1110
1146
|
if (jsonSchemaMode === "lazy") imports.push("buildJsonSchema as $$");
|
|
1111
1147
|
if (this.opts?.exampleData) imports.push("createDataFromAnnotatedType as $e");
|
|
1112
1148
|
if (jsonSchemaMode === false) imports.push("throwFeatureDisabled as $d");
|
|
1113
1149
|
this.writeln(`import { ${imports.join(", ")} } from "@atscript/typescript/utils"`);
|
|
1150
|
+
const nameCounts = new Map();
|
|
1151
|
+
const nodesByName = new Map();
|
|
1152
|
+
for (const node of this.doc.nodes) if (node.__typeId !== null && node.__typeId !== undefined && node.id) {
|
|
1153
|
+
const name = node.id;
|
|
1154
|
+
nameCounts.set(name, (nameCounts.get(name) || 0) + 1);
|
|
1155
|
+
if (!nodesByName.has(name)) nodesByName.set(name, []);
|
|
1156
|
+
nodesByName.get(name).push(node);
|
|
1157
|
+
}
|
|
1158
|
+
for (const [name, nodes] of nodesByName) if (nodes.length === 1) this.typeIds.set(nodes[0], name);
|
|
1159
|
+
else for (let i = 0; i < nodes.length; i++) this.typeIds.set(nodes[i], `${name}__${i + 1}`);
|
|
1114
1160
|
}
|
|
1115
1161
|
buildAdHocMap(annotateNodes) {
|
|
1116
1162
|
const map = new Map();
|
|
@@ -1124,6 +1170,15 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
1124
1170
|
}
|
|
1125
1171
|
return map.size > 0 ? map : null;
|
|
1126
1172
|
}
|
|
1173
|
+
/**
|
|
1174
|
+
* Checks if any ad-hoc annotation path extends beyond the current _propPath,
|
|
1175
|
+
* meaning annotations target properties inside a referenced type.
|
|
1176
|
+
*/ hasAdHocAnnotationsThroughRef() {
|
|
1177
|
+
if (!this._adHocAnnotations || this._propPath.length === 0) return false;
|
|
1178
|
+
const prefix = `${this._propPath.join(".")}.`;
|
|
1179
|
+
for (const key of this._adHocAnnotations.keys()) if (key.startsWith(prefix)) return true;
|
|
1180
|
+
return false;
|
|
1181
|
+
}
|
|
1127
1182
|
post() {
|
|
1128
1183
|
for (const node of this.postAnnotate) if (node.entity === "annotate") {
|
|
1129
1184
|
const annotateNode = node;
|
|
@@ -1154,6 +1209,8 @@ else {
|
|
|
1154
1209
|
this.writeln("static __is_atscript_annotated_type = true");
|
|
1155
1210
|
this.writeln("static type = {}");
|
|
1156
1211
|
this.writeln("static metadata = new Map()");
|
|
1212
|
+
const typeId = this.typeIds.get(node);
|
|
1213
|
+
if (typeId) this.writeln(`static id = "${typeId}"`);
|
|
1157
1214
|
this.renderJsonSchemaMethod(node);
|
|
1158
1215
|
this.renderExampleDataMethod(node);
|
|
1159
1216
|
}
|
|
@@ -1232,6 +1289,8 @@ else {
|
|
|
1232
1289
|
case "type": {
|
|
1233
1290
|
const def = node.getDefinition();
|
|
1234
1291
|
const handle = this.toAnnotatedHandle(def, true);
|
|
1292
|
+
const typeId = this.typeIds.get(node) ?? (node.__typeId !== null && node.__typeId !== undefined ? node.id : undefined);
|
|
1293
|
+
if (typeId) handle.id(typeId);
|
|
1235
1294
|
return skipAnnotations ? handle : this.applyExpectAnnotations(handle, this.doc.evalAnnotationsForNode(node));
|
|
1236
1295
|
}
|
|
1237
1296
|
case "prop": {
|
|
@@ -1358,6 +1417,14 @@ else handle.prop(prop.id, propHandle.$type);
|
|
|
1358
1417
|
return this;
|
|
1359
1418
|
}
|
|
1360
1419
|
}
|
|
1420
|
+
if (this._adHocAnnotations && this.hasAdHocAnnotationsThroughRef()) {
|
|
1421
|
+
let resolved = decl ? this.doc.mergeIntersection(decl) : undefined;
|
|
1422
|
+
if (resolved && isInterface(resolved)) resolved = resolved.getDefinition() || resolved;
|
|
1423
|
+
if (resolved) {
|
|
1424
|
+
this.annotateType(resolved, name);
|
|
1425
|
+
return this;
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1361
1428
|
const chain = ref.hasChain ? `, [${ref.chain.map((c) => `"${escapeQuotes(c.text)}"`).join(", ")}]` : "";
|
|
1362
1429
|
this.writeln(`$(${name ? `"", ${name}` : ""})`).indent().writeln(`.refTo(${ref.id}${chain})`);
|
|
1363
1430
|
if (!ref.hasChain) {
|
|
@@ -1591,11 +1658,29 @@ else targetValue = "true";
|
|
|
1591
1658
|
const targetName = node.targetName;
|
|
1592
1659
|
const targetDef = this.resolveTargetDef(targetName);
|
|
1593
1660
|
this.writeln("// Ad-hoc annotations for ", targetName);
|
|
1661
|
+
const allClones = [];
|
|
1662
|
+
const entryAccessors = [];
|
|
1594
1663
|
for (const entry of node.entries) {
|
|
1595
1664
|
const anns = entry.annotations;
|
|
1596
1665
|
if (!anns || anns.length === 0) continue;
|
|
1597
1666
|
const parts = entry.hasChain ? [entry.id, ...entry.chain.map((c) => c.text)] : [entry.id];
|
|
1598
|
-
const accessors = this.buildMutatingAccessors(targetName, targetDef, parts);
|
|
1667
|
+
const { accessors, clones } = this.buildMutatingAccessors(targetName, targetDef, parts);
|
|
1668
|
+
allClones.push(...clones);
|
|
1669
|
+
entryAccessors.push({
|
|
1670
|
+
entry,
|
|
1671
|
+
accessors
|
|
1672
|
+
});
|
|
1673
|
+
}
|
|
1674
|
+
const cloneKeys = new Set();
|
|
1675
|
+
for (const clone of allClones) {
|
|
1676
|
+
const key = `${clone.parentPath}|${clone.propName}`;
|
|
1677
|
+
if (!cloneKeys.has(key)) {
|
|
1678
|
+
cloneKeys.add(key);
|
|
1679
|
+
this.writeln(`$c(${clone.parentPath}, "${escapeQuotes(clone.propName)}")`);
|
|
1680
|
+
}
|
|
1681
|
+
}
|
|
1682
|
+
for (const { entry, accessors } of entryAccessors) {
|
|
1683
|
+
const anns = entry.annotations;
|
|
1599
1684
|
for (const accessor of accessors) {
|
|
1600
1685
|
const cleared = new Set();
|
|
1601
1686
|
for (const an of anns) {
|
|
@@ -1645,15 +1730,21 @@ else targetValue = "true";
|
|
|
1645
1730
|
prefix: `${targetName}.type`,
|
|
1646
1731
|
def: targetDef
|
|
1647
1732
|
}];
|
|
1733
|
+
const clones = [];
|
|
1648
1734
|
for (let i = 0; i < parts.length; i++) {
|
|
1649
1735
|
const nextAccessors = [];
|
|
1650
1736
|
for (const { prefix, def } of accessors) {
|
|
1651
1737
|
const results = this.buildPropPaths(def, parts[i]);
|
|
1652
|
-
if (results.length > 0) for (const result of results) if (i < parts.length - 1)
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1738
|
+
if (results.length > 0) for (const result of results) if (i < parts.length - 1) {
|
|
1739
|
+
if (result.propDef && isRef(result.propDef)) clones.push({
|
|
1740
|
+
parentPath: prefix,
|
|
1741
|
+
propName: parts[i]
|
|
1742
|
+
});
|
|
1743
|
+
nextAccessors.push({
|
|
1744
|
+
prefix: `${prefix}${result.path}?.type`,
|
|
1745
|
+
def: result.propDef
|
|
1746
|
+
});
|
|
1747
|
+
} else nextAccessors.push({
|
|
1657
1748
|
prefix: `${prefix}${result.path}?`,
|
|
1658
1749
|
def: result.propDef
|
|
1659
1750
|
});
|
|
@@ -1667,7 +1758,10 @@ else {
|
|
|
1667
1758
|
}
|
|
1668
1759
|
accessors = nextAccessors;
|
|
1669
1760
|
}
|
|
1670
|
-
return
|
|
1761
|
+
return {
|
|
1762
|
+
accessors: accessors.map((a) => a.prefix),
|
|
1763
|
+
clones
|
|
1764
|
+
};
|
|
1671
1765
|
}
|
|
1672
1766
|
/**
|
|
1673
1767
|
* Finds a property in a type tree at compile time, returning all
|
|
@@ -1703,7 +1797,7 @@ else {
|
|
|
1703
1797
|
return [];
|
|
1704
1798
|
}
|
|
1705
1799
|
constructor(doc, opts) {
|
|
1706
|
-
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 = [];
|
|
1800
|
+
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), _define_property(this, "typeIds", void 0), this.opts = opts, this.postAnnotate = [], this._adHocAnnotations = null, this._propPath = [], this.typeIds = new Map();
|
|
1707
1801
|
}
|
|
1708
1802
|
};
|
|
1709
1803
|
|
|
@@ -1752,8 +1846,4 @@ else return t.optional ? `${t.type} | true` : t.type;
|
|
|
1752
1846
|
};
|
|
1753
1847
|
|
|
1754
1848
|
//#endregion
|
|
1755
|
-
|
|
1756
|
-
var src_default = tsPlugin;
|
|
1757
|
-
|
|
1758
|
-
//#endregion
|
|
1759
|
-
export { src_default as default };
|
|
1849
|
+
export { tsPlugin as default, tsPlugin };
|