@atscript/typescript 0.0.1
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/LICENSE +21 -0
- package/cli.cjs +3 -0
- package/dist/cli.cjs +726 -0
- package/dist/index.cjs +1019 -0
- package/dist/index.d.ts +98 -0
- package/dist/index.mjs +990 -0
- package/package.json +61 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,990 @@
|
|
|
1
|
+
import { isArray, isConst, isGroup, isInterface, isPrimitive, isRef, isStructure } from "@atscript/core";
|
|
2
|
+
import path from "path";
|
|
3
|
+
|
|
4
|
+
//#region packages/typescript/src/codegen/code-printer.ts
|
|
5
|
+
function _define_property$3(obj, key, value) {
|
|
6
|
+
if (key in obj) Object.defineProperty(obj, key, {
|
|
7
|
+
value,
|
|
8
|
+
enumerable: true,
|
|
9
|
+
configurable: true,
|
|
10
|
+
writable: true
|
|
11
|
+
});
|
|
12
|
+
else obj[key] = value;
|
|
13
|
+
return obj;
|
|
14
|
+
}
|
|
15
|
+
var CodePrinter = class {
|
|
16
|
+
/**
|
|
17
|
+
* Write text with NO automatic newline.
|
|
18
|
+
* If current line is empty, we prepend indentation.
|
|
19
|
+
*/ write(...text) {
|
|
20
|
+
if (!this.currentLine) this.currentLine = this.indentString();
|
|
21
|
+
this.currentLine += text.join("");
|
|
22
|
+
return this;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Write text and then finalize this line with a newline.
|
|
26
|
+
* Implemented by calling `write(...)` + push line + reset `currentLine`.
|
|
27
|
+
*/ writeln(...text) {
|
|
28
|
+
this.write(...text);
|
|
29
|
+
this.lines.push(this.currentLine);
|
|
30
|
+
this.currentLine = "";
|
|
31
|
+
return this;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Open a block with an opening/closing pair, e.g. "{}" or "()".
|
|
35
|
+
* By default, places the opening delimiter on the current line (no extra newline).
|
|
36
|
+
*/ block(pair) {
|
|
37
|
+
return this.doBlock(pair, false);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Same as `block()`, but adds a newline after writing the opening delimiter.
|
|
41
|
+
*/ blockln(pair) {
|
|
42
|
+
return this.doBlock(pair, true);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Close the most recent block. By default, closing delimiter goes on the current line.
|
|
46
|
+
*/ pop() {
|
|
47
|
+
return this.doPop(false);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Same as `pop()`, but puts the closing delimiter on a new line.
|
|
51
|
+
*/ popln() {
|
|
52
|
+
return this.doPop(true);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Increase indentation by one level.
|
|
56
|
+
*/ indent() {
|
|
57
|
+
this.indentLevel++;
|
|
58
|
+
return this;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Decrease indentation by one level (never below 0).
|
|
62
|
+
*/ unindent() {
|
|
63
|
+
this.indentLevel = Math.max(0, this.indentLevel - 1);
|
|
64
|
+
return this;
|
|
65
|
+
}
|
|
66
|
+
render() {
|
|
67
|
+
return this.toString();
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Return the entire code as a string. Removes any half-finished current line if empty.
|
|
71
|
+
*/ toString() {
|
|
72
|
+
if (this.currentLine.trim() !== "") this.lines.push(this.currentLine);
|
|
73
|
+
else if (this.currentLine.length > 0) this.lines.push(this.currentLine);
|
|
74
|
+
return this.lines.join("\n");
|
|
75
|
+
}
|
|
76
|
+
doBlock(pair, withNewLine) {
|
|
77
|
+
if (pair.length < 2) throw new Error(`Block pair must have at least 2 chars, e.g. "{}" or "()". Got "${pair}"`);
|
|
78
|
+
const opening = pair[0];
|
|
79
|
+
const closing = pair[pair.length - 1];
|
|
80
|
+
this.write(opening);
|
|
81
|
+
this.blockStack.push(closing);
|
|
82
|
+
if (withNewLine) this.writeln();
|
|
83
|
+
this.indent();
|
|
84
|
+
return this;
|
|
85
|
+
}
|
|
86
|
+
doPop(withNewLine) {
|
|
87
|
+
if (!this.blockStack.length) throw new Error("No block to pop (stack is empty).");
|
|
88
|
+
const closing = this.blockStack.pop();
|
|
89
|
+
this.unindent();
|
|
90
|
+
if (withNewLine) this.writeln(closing);
|
|
91
|
+
else this.write(closing);
|
|
92
|
+
return this;
|
|
93
|
+
}
|
|
94
|
+
indentString() {
|
|
95
|
+
return " ".repeat(this.indentLevel * this.indentSize);
|
|
96
|
+
}
|
|
97
|
+
constructor() {
|
|
98
|
+
_define_property$3(this, "lines", []);
|
|
99
|
+
_define_property$3(this, "currentLine", "");
|
|
100
|
+
_define_property$3(this, "indentLevel", 0);
|
|
101
|
+
_define_property$3(this, "indentSize", 2);
|
|
102
|
+
_define_property$3(this, "blockStack", []);
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
//#endregion
|
|
107
|
+
//#region packages/typescript/src/codegen/base-renderer.ts
|
|
108
|
+
function _define_property$2(obj, key, value) {
|
|
109
|
+
if (key in obj) Object.defineProperty(obj, key, {
|
|
110
|
+
value,
|
|
111
|
+
enumerable: true,
|
|
112
|
+
configurable: true,
|
|
113
|
+
writable: true
|
|
114
|
+
});
|
|
115
|
+
else obj[key] = value;
|
|
116
|
+
return obj;
|
|
117
|
+
}
|
|
118
|
+
var BaseRenderer = class extends CodePrinter {
|
|
119
|
+
pre() {}
|
|
120
|
+
post() {}
|
|
121
|
+
render() {
|
|
122
|
+
this.pre();
|
|
123
|
+
for (const node of this.doc.nodes) this.renderNode(node);
|
|
124
|
+
this.post();
|
|
125
|
+
return this.toString();
|
|
126
|
+
}
|
|
127
|
+
renderInterface(node) {}
|
|
128
|
+
renderType(node) {}
|
|
129
|
+
transformFromPath(path$1) {
|
|
130
|
+
return path$1 + ".as";
|
|
131
|
+
}
|
|
132
|
+
renderImport(node) {
|
|
133
|
+
const def = node.getDefinition();
|
|
134
|
+
const refs = [];
|
|
135
|
+
const from = this.transformFromPath(node.token("path").text);
|
|
136
|
+
let isUnusedImport = true;
|
|
137
|
+
if (isGroup(def)) {
|
|
138
|
+
for (const child of def.unwrap()) if (isRef(child) && !this.unused.has(child.id)) {
|
|
139
|
+
const node$1 = child;
|
|
140
|
+
refs.push(node$1.id);
|
|
141
|
+
isUnusedImport = false;
|
|
142
|
+
}
|
|
143
|
+
} else if (isRef(def) && !this.unused.has(def.id)) {
|
|
144
|
+
refs.push(def.id);
|
|
145
|
+
isUnusedImport = false;
|
|
146
|
+
}
|
|
147
|
+
if (!isUnusedImport) this.writeln(`import { ${refs.join(", ")} } from "${from}"`);
|
|
148
|
+
}
|
|
149
|
+
renderNode(node) {
|
|
150
|
+
switch (node.entity) {
|
|
151
|
+
case "interface": {
|
|
152
|
+
this.renderInterface(node);
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
case "type": {
|
|
156
|
+
this.renderType(node);
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
case "import": {
|
|
160
|
+
this.renderImport(node);
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
default: break;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
constructor(doc) {
|
|
167
|
+
super(), _define_property$2(this, "doc", void 0), _define_property$2(this, "unused", void 0), this.doc = doc;
|
|
168
|
+
this.unused = new Set(this.doc.getUnusedTokens().map((t) => t.text));
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
//#endregion
|
|
173
|
+
//#region packages/typescript/src/codegen/utils.ts
|
|
174
|
+
function wrapProp(name) {
|
|
175
|
+
const validIdentifier = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
|
|
176
|
+
if (!validIdentifier.test(name)) return `"${escapeQuotes(name)}"`;
|
|
177
|
+
return name;
|
|
178
|
+
}
|
|
179
|
+
function escapeQuotes(str) {
|
|
180
|
+
return str.replace(/"/g, "\\\"");
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
//#endregion
|
|
184
|
+
//#region packages/typescript/src/codegen/type-renderer.ts
|
|
185
|
+
var TypeRenderer = class extends BaseRenderer {
|
|
186
|
+
pre() {
|
|
187
|
+
this.writeln("// prettier-ignore-start");
|
|
188
|
+
this.writeln("/* eslint-disable */");
|
|
189
|
+
this.writeln(`/// <reference path="./${this.doc.name}" />`);
|
|
190
|
+
this.writeln("/**");
|
|
191
|
+
this.writeln(" * 🪄 This file was generated by Atscript");
|
|
192
|
+
this.writeln(" * Do not edit this file!");
|
|
193
|
+
this.writeln(" */");
|
|
194
|
+
this.writeln();
|
|
195
|
+
this.writeln("import type { TAtscriptTypeObject, TAtscriptTypeComplex, TAtscriptTypeFinal, TAtscriptTypeArray, TMetadataMap, Validator } from \"@atscript/typescript\"");
|
|
196
|
+
}
|
|
197
|
+
post() {
|
|
198
|
+
this.writeln("// prettier-ignore-end");
|
|
199
|
+
}
|
|
200
|
+
renderTypeDef(def) {
|
|
201
|
+
if (!def) {
|
|
202
|
+
this.write("unknown");
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
if (isStructure(def)) return this.renderStructure(def);
|
|
206
|
+
if (isGroup(def)) {
|
|
207
|
+
const tuple = def.entity === "tuple";
|
|
208
|
+
const operator = tuple ? ", " : ` ${def.op} `;
|
|
209
|
+
const children = def.unwrap();
|
|
210
|
+
const hasSubgroup = !tuple && children.some((c) => c.entity === "group");
|
|
211
|
+
this.write(tuple ? "[" : hasSubgroup ? "(" : "");
|
|
212
|
+
this.renderTypeDef(def.first);
|
|
213
|
+
for (const child of children.slice(1)) {
|
|
214
|
+
this.write(operator);
|
|
215
|
+
this.renderTypeDef(child);
|
|
216
|
+
}
|
|
217
|
+
return this.write(tuple ? "]" : hasSubgroup ? ")" : "");
|
|
218
|
+
}
|
|
219
|
+
if (isConst(def)) {
|
|
220
|
+
const name = def.token("identifier")?.type === "number" ? def.id : `"${escapeQuotes(def.id)}"`;
|
|
221
|
+
return this.write(name);
|
|
222
|
+
}
|
|
223
|
+
if (isRef(def)) {
|
|
224
|
+
const node = def;
|
|
225
|
+
const unwound = this.doc.unwindType(node.id, node.chain);
|
|
226
|
+
if (isPrimitive(unwound?.def)) {
|
|
227
|
+
this.write(renderPrimitiveTypeDef(unwound.def.config.type));
|
|
228
|
+
if (node.hasChain) this.write(` /* ${node.chain.map((c) => c.text).join(".")} */`);
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
let name = node.id;
|
|
232
|
+
for (const c of node.chain) name += `["${escapeQuotes(c.text)}"]`;
|
|
233
|
+
return this.write(name);
|
|
234
|
+
}
|
|
235
|
+
if (isArray(def)) {
|
|
236
|
+
const node = def;
|
|
237
|
+
const def2 = node.getDefinition();
|
|
238
|
+
const isGrp = def2?.entity === "group";
|
|
239
|
+
if (isGrp) this.write("(");
|
|
240
|
+
this.renderTypeDef(def2);
|
|
241
|
+
if (isGrp) this.write(")");
|
|
242
|
+
return this.write("[]");
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
renderStructure(struct, asClass) {
|
|
246
|
+
this.blockln("{}");
|
|
247
|
+
for (const prop of Array.from(struct.props.values())) {
|
|
248
|
+
const optional = !!prop.token("optional");
|
|
249
|
+
this.write(wrapProp(prop.id), optional ? "?" : "", ": ");
|
|
250
|
+
this.renderTypeDef(prop.getDefinition());
|
|
251
|
+
this.writeln();
|
|
252
|
+
}
|
|
253
|
+
if (asClass) {
|
|
254
|
+
this.writeln("static __is_anscript_annotated_type: true");
|
|
255
|
+
this.writeln(`static type: TAtscriptTypeObject<keyof ${asClass}>`);
|
|
256
|
+
this.writeln(`static metadata: TMetadataMap<AtscriptMetadata>`);
|
|
257
|
+
this.writeln(`static validator: () => Validator`);
|
|
258
|
+
}
|
|
259
|
+
this.pop();
|
|
260
|
+
}
|
|
261
|
+
renderInterface(node) {
|
|
262
|
+
this.writeln();
|
|
263
|
+
const exported = node.token("export")?.text === "export";
|
|
264
|
+
this.renderJsDoc(node);
|
|
265
|
+
this.write(exported ? "export declare " : "declare ");
|
|
266
|
+
this.write(`class ${node.id} `);
|
|
267
|
+
const struct = node.getDefinition();
|
|
268
|
+
if (struct?.entity === "structure") this.renderStructure(struct, node.id);
|
|
269
|
+
else this.writeln("{}");
|
|
270
|
+
this.writeln();
|
|
271
|
+
}
|
|
272
|
+
renderType(node) {
|
|
273
|
+
this.writeln();
|
|
274
|
+
const exported = node.token("export")?.text === "export";
|
|
275
|
+
this.renderJsDoc(node);
|
|
276
|
+
this.write(exported ? "export " : "declare ");
|
|
277
|
+
this.write(`type ${node.id} = `);
|
|
278
|
+
this.renderTypeDef(node.getDefinition());
|
|
279
|
+
this.writeln();
|
|
280
|
+
this.renderTypeNamespace(node);
|
|
281
|
+
}
|
|
282
|
+
renderTypeNamespace(node) {
|
|
283
|
+
this.write(`declare namespace ${node.id} `);
|
|
284
|
+
this.blockln("{}");
|
|
285
|
+
const def = node.getDefinition();
|
|
286
|
+
let typeDef = "TAtscriptTypeDef";
|
|
287
|
+
if (def) {
|
|
288
|
+
let realDef = def;
|
|
289
|
+
if (isRef(def)) realDef = this.doc.unwindType(def.id, def.chain)?.def || realDef;
|
|
290
|
+
realDef = this.doc.mergeIntersection(realDef);
|
|
291
|
+
if (isStructure(realDef) || isInterface(realDef)) typeDef = `TAtscriptTypeObject<keyof ${node.id}}>`;
|
|
292
|
+
else if (isGroup(realDef)) typeDef = "TAtscriptTypeComplex";
|
|
293
|
+
else if (isArray(realDef)) typeDef = "TAtscriptTypeArray";
|
|
294
|
+
else if (isPrimitive(realDef)) typeDef = "TAtscriptTypeFinal";
|
|
295
|
+
}
|
|
296
|
+
this.writeln(`const __is_anscript_annotated_type: true`);
|
|
297
|
+
this.writeln(`const type: ${typeDef}`);
|
|
298
|
+
this.writeln(`const metadata: TMetadataMap<AtscriptMetadata>`);
|
|
299
|
+
this.writeln(`const validator: () => Validator`);
|
|
300
|
+
this.popln();
|
|
301
|
+
}
|
|
302
|
+
renderJsDoc(node) {
|
|
303
|
+
const range = node.token("identifier")?.range;
|
|
304
|
+
const rangeStr = range ? `:${range.start.line + 1}:${range.start.character + 1}` : "";
|
|
305
|
+
this.writeln(`/**`);
|
|
306
|
+
this.writeln(` * Atscript ${node.entity} **${node.id}**`);
|
|
307
|
+
this.writeln(` * @see {@link ./${this.doc.name}${rangeStr}}`);
|
|
308
|
+
this.writeln(` */`);
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
function renderPrimitiveTypeDef(def) {
|
|
312
|
+
if (!def) return "unknown";
|
|
313
|
+
if (typeof def === "string") return def === "void" ? "undefined" : def;
|
|
314
|
+
switch (def.kind) {
|
|
315
|
+
case "final": return def.value === "void" ? "undefined" : def.value;
|
|
316
|
+
case "union": return def.items.map(renderPrimitiveTypeDef).join(" | ");
|
|
317
|
+
case "intersection": return def.items.map(renderPrimitiveTypeDef).join(" & ");
|
|
318
|
+
case "tuple": return `[${def.items.map(renderPrimitiveTypeDef).join(", ")}]`;
|
|
319
|
+
case "array": return `${renderPrimitiveTypeDef(def.of)}[]`;
|
|
320
|
+
case "object": {
|
|
321
|
+
const props = Object.entries(def.props).map(([key, val]) => `${wrapProp(key)}${typeof val === "object" && val.optional ? "?" : ""}: ${renderPrimitiveTypeDef(val)}`).join("; ");
|
|
322
|
+
return `{ ${props} }`;
|
|
323
|
+
}
|
|
324
|
+
default: return "unknown";
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
//#endregion
|
|
329
|
+
//#region packages/typescript/src/codegen/js-renderer.ts
|
|
330
|
+
function _define_property$1(obj, key, value) {
|
|
331
|
+
if (key in obj) Object.defineProperty(obj, key, {
|
|
332
|
+
value,
|
|
333
|
+
enumerable: true,
|
|
334
|
+
configurable: true,
|
|
335
|
+
writable: true
|
|
336
|
+
});
|
|
337
|
+
else obj[key] = value;
|
|
338
|
+
return obj;
|
|
339
|
+
}
|
|
340
|
+
var JsRenderer = class extends BaseRenderer {
|
|
341
|
+
pre() {
|
|
342
|
+
this.writeln("import { defineAnnotatedType as $ } from \"@atscript/typescript\"");
|
|
343
|
+
}
|
|
344
|
+
post() {
|
|
345
|
+
for (const node of this.postAnnotate) {
|
|
346
|
+
this.annotateType(node.getDefinition(), node.id);
|
|
347
|
+
this.indent().defineMetadata(node).unindent();
|
|
348
|
+
this.writeln();
|
|
349
|
+
}
|
|
350
|
+
super.post();
|
|
351
|
+
}
|
|
352
|
+
renderInterface(node) {
|
|
353
|
+
this.writeln();
|
|
354
|
+
const exported = node.token("export")?.text === "export";
|
|
355
|
+
this.write(exported ? "export " : "");
|
|
356
|
+
this.write(`class ${node.id} `);
|
|
357
|
+
this.blockln("{}");
|
|
358
|
+
this.writeln("static __is_anscript_annotated_type = true");
|
|
359
|
+
this.writeln("static type = {}");
|
|
360
|
+
this.writeln("static metadata = new Map()");
|
|
361
|
+
this.popln();
|
|
362
|
+
this.postAnnotate.push(node);
|
|
363
|
+
this.writeln();
|
|
364
|
+
}
|
|
365
|
+
renderType(node) {
|
|
366
|
+
this.writeln();
|
|
367
|
+
const exported = node.token("export")?.text === "export";
|
|
368
|
+
this.write(exported ? "export " : "");
|
|
369
|
+
this.write(`class ${node.id} `);
|
|
370
|
+
this.blockln("{}");
|
|
371
|
+
this.writeln("static __is_anscript_annotated_type = true");
|
|
372
|
+
this.writeln("static type = {}");
|
|
373
|
+
this.writeln("static metadata = new Map()");
|
|
374
|
+
this.popln();
|
|
375
|
+
this.postAnnotate.push(node);
|
|
376
|
+
this.writeln();
|
|
377
|
+
}
|
|
378
|
+
annotateType(_node, name) {
|
|
379
|
+
if (!_node) return this;
|
|
380
|
+
const node = this.doc.mergeIntersection(_node);
|
|
381
|
+
let kind = node.entity;
|
|
382
|
+
switch (node.entity) {
|
|
383
|
+
case "ref": {
|
|
384
|
+
const ref = node;
|
|
385
|
+
const decl = this.doc.unwindType(ref.id, ref.chain)?.def;
|
|
386
|
+
if (isPrimitive(decl)) {
|
|
387
|
+
this.annotateType(decl, name);
|
|
388
|
+
return this;
|
|
389
|
+
}
|
|
390
|
+
const chain = ref.hasChain ? `, [${ref.chain.map((c) => `"${escapeQuotes(c.text)}"`).join(", ")}]` : "";
|
|
391
|
+
this.writeln(`$(${name ? `"", ${name}` : ""})`).indent().writeln(`.refTo(${ref.id}${chain})`).unindent();
|
|
392
|
+
return this;
|
|
393
|
+
}
|
|
394
|
+
case "primitive": {
|
|
395
|
+
this.definePrimitive(node, name);
|
|
396
|
+
return this;
|
|
397
|
+
}
|
|
398
|
+
case "const": {
|
|
399
|
+
this.writeln(`$(${name ? `"", ${name}` : ""})`).indent().defineConst(node).unindent();
|
|
400
|
+
return this;
|
|
401
|
+
}
|
|
402
|
+
case "structure": {
|
|
403
|
+
this.writeln(`$("object"${name ? `, ${name}` : ""})`).indent().defineObject(node).unindent();
|
|
404
|
+
return this;
|
|
405
|
+
}
|
|
406
|
+
case "group": {
|
|
407
|
+
kind = node.op === "|" ? "union" : "intersection";
|
|
408
|
+
this.writeln(`$("${kind}"${name ? `, ${name}` : ""})`).indent().defineGroup(node).unindent();
|
|
409
|
+
return this;
|
|
410
|
+
}
|
|
411
|
+
case "tuple": {
|
|
412
|
+
this.writeln(`$("tuple"${name ? `, ${name}` : ""})`).indent().defineGroup(node).unindent();
|
|
413
|
+
return this;
|
|
414
|
+
}
|
|
415
|
+
case "array": {
|
|
416
|
+
this.writeln(`$("array"${name ? `, ${name}` : ""})`).indent().defineArray(node).unindent();
|
|
417
|
+
return this;
|
|
418
|
+
}
|
|
419
|
+
default: {
|
|
420
|
+
console.log("!!!!!!! UNKNOWN ", node.entity);
|
|
421
|
+
return this;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
defineConst(node) {
|
|
426
|
+
const t = node.token("identifier")?.type;
|
|
427
|
+
const designType = t === "text" ? "string" : t === "number" ? "number" : "unknown";
|
|
428
|
+
const type = t === "text" ? "String" : t === "number" ? "Number" : "undefined";
|
|
429
|
+
this.writeln(`.designType("${escapeQuotes(designType)}")`);
|
|
430
|
+
this.writeln(`.value(${t === "text" ? `"${escapeQuotes(node.id)}"` : node.id})`);
|
|
431
|
+
return this;
|
|
432
|
+
}
|
|
433
|
+
definePrimitive(node, name) {
|
|
434
|
+
this.renderPrimitiveDef(node.id === "never" ? "never" : node.config.type, name);
|
|
435
|
+
this.writeln(` .tags(${Array.from(node.tags).map((f) => `"${escapeQuotes(f)}"`).join(", ")})`);
|
|
436
|
+
return this;
|
|
437
|
+
}
|
|
438
|
+
renderPrimitiveDef(def, name) {
|
|
439
|
+
const d = (t) => [`"${t || ""}"`, name].filter(Boolean).join(", ").replace(/^""$/, "");
|
|
440
|
+
if (!def) return this.writeln(`$(${d()}).designType("any")`);
|
|
441
|
+
if (typeof def === "string") return this.writeln(`$(${d()}).designType("${def === "void" ? "undefined" : def}")`);
|
|
442
|
+
switch (def.kind) {
|
|
443
|
+
case "final": return this.writeln(`$(${d()}).designType("${def.value === "void" ? "undefined" : def.value}")`);
|
|
444
|
+
case "union":
|
|
445
|
+
case "intersection":
|
|
446
|
+
case "tuple":
|
|
447
|
+
this.writeln(`$(${d(def.kind)})`);
|
|
448
|
+
this.indent();
|
|
449
|
+
for (const itemDef of def.items) {
|
|
450
|
+
this.write(`.item(`);
|
|
451
|
+
this.indent();
|
|
452
|
+
this.renderPrimitiveDef(itemDef);
|
|
453
|
+
this.writeln(".$type");
|
|
454
|
+
this.unindent();
|
|
455
|
+
this.write(`)`);
|
|
456
|
+
}
|
|
457
|
+
this.unindent();
|
|
458
|
+
return;
|
|
459
|
+
case "array":
|
|
460
|
+
this.writeln(`$(${d("array")})`);
|
|
461
|
+
this.indent();
|
|
462
|
+
this.write(".of(");
|
|
463
|
+
this.indent();
|
|
464
|
+
this.renderPrimitiveDef(def.of);
|
|
465
|
+
this.writeln(`.$type`);
|
|
466
|
+
this.unindent();
|
|
467
|
+
this.writeln(`)`);
|
|
468
|
+
this.unindent();
|
|
469
|
+
return;
|
|
470
|
+
case "object":
|
|
471
|
+
this.writeln(`$(${d("object")})`);
|
|
472
|
+
this.indent();
|
|
473
|
+
for (const [key, propDef] of Object.entries(def.props)) {
|
|
474
|
+
const optional = typeof propDef === "object" && propDef.optional;
|
|
475
|
+
this.writeln(`.prop(`);
|
|
476
|
+
this.indent();
|
|
477
|
+
this.writeln(`"${escapeQuotes(key)}",`);
|
|
478
|
+
this.renderPrimitiveDef(propDef);
|
|
479
|
+
if (optional) this.writeln(".optional()");
|
|
480
|
+
this.writeln(".$type");
|
|
481
|
+
this.unindent();
|
|
482
|
+
this.write(`)`);
|
|
483
|
+
}
|
|
484
|
+
this.unindent();
|
|
485
|
+
return;
|
|
486
|
+
default: return this.writeln(`$(${d()}).designType("any")`);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
defineObject(node) {
|
|
490
|
+
const props = Array.from(node.props.values());
|
|
491
|
+
for (const prop of props) {
|
|
492
|
+
const optional = !!prop.token("optional");
|
|
493
|
+
this.writeln(`.prop(`);
|
|
494
|
+
this.indent();
|
|
495
|
+
this.writeln(`"${escapeQuotes(prop.id)}",`);
|
|
496
|
+
this.annotateType(prop.getDefinition());
|
|
497
|
+
this.indent().defineMetadata(prop).unindent();
|
|
498
|
+
if (optional) this.writeln(" .optional()");
|
|
499
|
+
this.writeln(" .$type");
|
|
500
|
+
this.unindent();
|
|
501
|
+
this.write(`)`);
|
|
502
|
+
}
|
|
503
|
+
this.writeln();
|
|
504
|
+
return this;
|
|
505
|
+
}
|
|
506
|
+
defineGroup(node) {
|
|
507
|
+
const items = node.unwrap();
|
|
508
|
+
for (const item of items) this.write(".item(").indent().annotateType(item).write(" .$type").writeln(`)`).unindent();
|
|
509
|
+
return this;
|
|
510
|
+
}
|
|
511
|
+
defineArray(node) {
|
|
512
|
+
this.write(".of(").indent().annotateType(node.getDefinition()).write(" .$type").writeln(`)`).unindent();
|
|
513
|
+
return this;
|
|
514
|
+
}
|
|
515
|
+
defineMetadata(node) {
|
|
516
|
+
const annotations = this.doc.evalAnnotationsForNode(node);
|
|
517
|
+
annotations?.forEach((an) => {
|
|
518
|
+
this.resolveAnnotationValue(node, an);
|
|
519
|
+
});
|
|
520
|
+
return this;
|
|
521
|
+
}
|
|
522
|
+
resolveAnnotationValue(node, an) {
|
|
523
|
+
const spec = this.doc.resolveAnnotation(an.name);
|
|
524
|
+
let targetValue = "true";
|
|
525
|
+
let multiple = false;
|
|
526
|
+
if (spec) {
|
|
527
|
+
multiple = spec.config.multiple;
|
|
528
|
+
const length = spec.arguments.length;
|
|
529
|
+
if (length !== 0) if (Array.isArray(spec.config.argument)) {
|
|
530
|
+
targetValue = "{ ";
|
|
531
|
+
let i = 0;
|
|
532
|
+
for (const aSpec of spec.arguments) {
|
|
533
|
+
if (an.args[i]) targetValue += `${wrapProp(aSpec.name)}: ${aSpec.type === "string" ? `"${escapeQuotes(an.args[i]?.text)}"` : an.args[i]?.text}${i === length - 1 ? "" : ", "} `;
|
|
534
|
+
else {}
|
|
535
|
+
i++;
|
|
536
|
+
}
|
|
537
|
+
targetValue += "}";
|
|
538
|
+
} else {
|
|
539
|
+
const aSpec = spec.arguments[0];
|
|
540
|
+
if (an.args[0]) targetValue = aSpec.type === "string" ? `"${escapeQuotes(an.args[0]?.text)}"` : an.args[0]?.text;
|
|
541
|
+
else targetValue = "true";
|
|
542
|
+
}
|
|
543
|
+
} else {
|
|
544
|
+
multiple = node.countAnnotations(an.name) > 1 || an.args.length > 1;
|
|
545
|
+
if (an.args.length) targetValue = an.args[0].type === "text" ? `"${escapeQuotes(an.args[0].text)}"` : an.args[0].text;
|
|
546
|
+
}
|
|
547
|
+
if (multiple) this.writeln(`.annotate("${escapeQuotes(an.name)}", ${targetValue}, true)`);
|
|
548
|
+
else this.writeln(`.annotate("${escapeQuotes(an.name)}", ${targetValue})`);
|
|
549
|
+
}
|
|
550
|
+
constructor(...args) {
|
|
551
|
+
super(...args), _define_property$1(this, "postAnnotate", []);
|
|
552
|
+
}
|
|
553
|
+
};
|
|
554
|
+
|
|
555
|
+
//#endregion
|
|
556
|
+
//#region packages/typescript/src/plugin.ts
|
|
557
|
+
const tsPlugin = () => {
|
|
558
|
+
return {
|
|
559
|
+
name: "typesccript",
|
|
560
|
+
render(doc, format) {
|
|
561
|
+
if (format === "dts") return [{
|
|
562
|
+
fileName: `${doc.name}.d.ts`,
|
|
563
|
+
content: new TypeRenderer(doc).render()
|
|
564
|
+
}];
|
|
565
|
+
if (format === "js") return [{
|
|
566
|
+
fileName: `${doc.name}.js`,
|
|
567
|
+
content: new JsRenderer(doc).render()
|
|
568
|
+
}];
|
|
569
|
+
},
|
|
570
|
+
async buildEnd(output, format, repo) {
|
|
571
|
+
if (format === "dts") {
|
|
572
|
+
const annotations = await repo.getUsedAnnotations();
|
|
573
|
+
const tags = await repo.getPrimitivesTags() || new Set();
|
|
574
|
+
let rendered = [];
|
|
575
|
+
for (const [key, val] of Object.entries(annotations)) {
|
|
576
|
+
const multiple = val.multiple;
|
|
577
|
+
let typeLine = Array.from(val.types).map((t) => {
|
|
578
|
+
if (t.type === "object") return `{ ${Object.entries(t.props).map(([k, v]) => `${wrapProp(k)}${v.optional ? "?" : ""}: ${v.type}`).join(", ")} }`;
|
|
579
|
+
else return t.optional ? `${t.type} | true` : t.type;
|
|
580
|
+
}).join(" | ");
|
|
581
|
+
rendered.push(`${wrapProp(key)}: ${multiple ? "(" : ""}${typeLine}${multiple ? ")[]" : ""}`);
|
|
582
|
+
}
|
|
583
|
+
let renderedTags = Array.from(tags).map((f) => `"${escapeQuotes(f)}"`).join(" | ");
|
|
584
|
+
output.push({
|
|
585
|
+
content: "/**\n * 🪄 This file was generated by Atscript\n * It is generated based on annotations used in this project\n * Do not edit this file!\n *\n * Use `npx asc -f dts` command to re-generate this file\n */\nexport {}\n\ndeclare global {\n interface AtscriptMetadata {\n " + rendered.join("\n ") + "\n }\n" + " type AtscriptPrimitiveTags = " + renderedTags + "\n" + "}\n",
|
|
586
|
+
fileName: "atscript.d.ts",
|
|
587
|
+
source: "",
|
|
588
|
+
target: path.join(repo.root, "atscript.d.ts")
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
};
|
|
593
|
+
};
|
|
594
|
+
|
|
595
|
+
//#endregion
|
|
596
|
+
//#region packages/typescript/src/validator.ts
|
|
597
|
+
function _define_property(obj, key, value) {
|
|
598
|
+
if (key in obj) Object.defineProperty(obj, key, {
|
|
599
|
+
value,
|
|
600
|
+
enumerable: true,
|
|
601
|
+
configurable: true,
|
|
602
|
+
writable: true
|
|
603
|
+
});
|
|
604
|
+
else obj[key] = value;
|
|
605
|
+
return obj;
|
|
606
|
+
}
|
|
607
|
+
const regexCache = new Map();
|
|
608
|
+
var Validator = class {
|
|
609
|
+
isLimitExceeded() {
|
|
610
|
+
if (this.stackErrors.length > 0) return this.stackErrors[this.stackErrors.length - 1].length >= this.opts.errorLimit;
|
|
611
|
+
return this.errors.length >= this.opts.errorLimit;
|
|
612
|
+
}
|
|
613
|
+
push(name) {
|
|
614
|
+
this.stackPath.push(name);
|
|
615
|
+
this.stackErrors.push([]);
|
|
616
|
+
}
|
|
617
|
+
pop(saveErrors) {
|
|
618
|
+
this.stackPath.pop();
|
|
619
|
+
const popped = this.stackErrors.pop();
|
|
620
|
+
if (saveErrors && popped?.length) popped.forEach((error) => {
|
|
621
|
+
this.error(error.message, error.path, error.details);
|
|
622
|
+
});
|
|
623
|
+
return popped;
|
|
624
|
+
}
|
|
625
|
+
clear() {
|
|
626
|
+
this.stackErrors[this.stackErrors.length - 1] = [];
|
|
627
|
+
}
|
|
628
|
+
error(message, path$1, details) {
|
|
629
|
+
const errors = this.stackErrors[this.stackErrors.length - 1] || this.errors;
|
|
630
|
+
const error = {
|
|
631
|
+
path: path$1 || this.stackPath.join(".").slice(1),
|
|
632
|
+
message
|
|
633
|
+
};
|
|
634
|
+
if (details?.length) error.details = details;
|
|
635
|
+
errors.push(error);
|
|
636
|
+
}
|
|
637
|
+
throw() {
|
|
638
|
+
throw new ValidatorError(this.errors);
|
|
639
|
+
}
|
|
640
|
+
validate(value, safe) {
|
|
641
|
+
this.push("");
|
|
642
|
+
this.errors = [];
|
|
643
|
+
this.stackErrors = [];
|
|
644
|
+
const passed = this._validate(this.def, value);
|
|
645
|
+
this.pop(!passed);
|
|
646
|
+
if (!passed) {
|
|
647
|
+
if (safe) return false;
|
|
648
|
+
this.throw();
|
|
649
|
+
}
|
|
650
|
+
return true;
|
|
651
|
+
}
|
|
652
|
+
_validate(def, value) {
|
|
653
|
+
if (this.isLimitExceeded()) return false;
|
|
654
|
+
if (!isAnnotatedType(def)) throw new Error("Can not validate not-annotated type");
|
|
655
|
+
if (def.optional && value === undefined) return true;
|
|
656
|
+
switch (def.type.kind) {
|
|
657
|
+
case "object": return this.validateObject(def, value);
|
|
658
|
+
case "union": return this.validateUnion(def, value);
|
|
659
|
+
case "intersection": return this.validateIntersection(def, value);
|
|
660
|
+
case "tuple": return this.validateTuple(def, value);
|
|
661
|
+
case "array": return this.validateArray(def, value);
|
|
662
|
+
case "": return this.validatePrimitive(def, value);
|
|
663
|
+
default: throw new Error(`Unknown type "${def.type.kind}"`);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
validateUnion(def, value) {
|
|
667
|
+
let i = 0;
|
|
668
|
+
const popped = [];
|
|
669
|
+
for (const item of def.type.items) {
|
|
670
|
+
this.push(`[${item.type.kind || item.type.designType}(${i})]`);
|
|
671
|
+
if (this._validate(item, value)) {
|
|
672
|
+
this.pop(false);
|
|
673
|
+
return true;
|
|
674
|
+
}
|
|
675
|
+
const errors = this.pop(false);
|
|
676
|
+
if (errors) popped.push(...errors);
|
|
677
|
+
i++;
|
|
678
|
+
}
|
|
679
|
+
this.clear();
|
|
680
|
+
const expected = def.type.items.map((item, i$1) => `[${item.type.kind || item.type.designType}(${i$1})]`).join(", ");
|
|
681
|
+
this.error(`Value does not match any of the allowed types: ${expected}`, undefined, popped);
|
|
682
|
+
return false;
|
|
683
|
+
}
|
|
684
|
+
validateIntersection(def, value) {
|
|
685
|
+
for (const item of def.type.items) if (!this._validate(item, value)) return false;
|
|
686
|
+
return true;
|
|
687
|
+
}
|
|
688
|
+
validateTuple(def, value) {
|
|
689
|
+
if (!Array.isArray(value) || value.length !== def.type.items.length) {
|
|
690
|
+
this.error("Expected array of length " + def.type.items.length);
|
|
691
|
+
return false;
|
|
692
|
+
}
|
|
693
|
+
let i = 0;
|
|
694
|
+
for (const item of def.type.items) {
|
|
695
|
+
this.push(`[${i}]`);
|
|
696
|
+
if (!this._validate(item, value[i])) {
|
|
697
|
+
this.pop(true);
|
|
698
|
+
return false;
|
|
699
|
+
}
|
|
700
|
+
this.pop(false);
|
|
701
|
+
i++;
|
|
702
|
+
}
|
|
703
|
+
return true;
|
|
704
|
+
}
|
|
705
|
+
validateArray(def, value) {
|
|
706
|
+
if (!Array.isArray(value)) {
|
|
707
|
+
this.error("Expected array");
|
|
708
|
+
return false;
|
|
709
|
+
}
|
|
710
|
+
const minLength = def.metadata.get("expect.minLength");
|
|
711
|
+
if (typeof minLength === "number" && value.length < minLength) {
|
|
712
|
+
this.error(`Expected minimum length of ${minLength} items, got ${value.length} items`);
|
|
713
|
+
return false;
|
|
714
|
+
}
|
|
715
|
+
const maxLength = def.metadata.get("expect.maxLength");
|
|
716
|
+
if (typeof maxLength === "number" && value.length > maxLength) {
|
|
717
|
+
this.error(`Expected maximum length of ${maxLength} items, got ${value.length} items`);
|
|
718
|
+
return false;
|
|
719
|
+
}
|
|
720
|
+
let i = 0;
|
|
721
|
+
let passed = true;
|
|
722
|
+
for (const item of value) {
|
|
723
|
+
this.push(`[${i}]`);
|
|
724
|
+
if (!this._validate(def.type.of, item)) {
|
|
725
|
+
passed = false;
|
|
726
|
+
this.pop(true);
|
|
727
|
+
if (this.isLimitExceeded()) return false;
|
|
728
|
+
} else this.pop(false);
|
|
729
|
+
i++;
|
|
730
|
+
}
|
|
731
|
+
return passed;
|
|
732
|
+
}
|
|
733
|
+
validateObject(def, value) {
|
|
734
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
735
|
+
this.error("Expected object");
|
|
736
|
+
return false;
|
|
737
|
+
}
|
|
738
|
+
let passed = true;
|
|
739
|
+
const valueKeys = new Set(Object.keys(value));
|
|
740
|
+
const typeKeys = new Set();
|
|
741
|
+
for (const [key, item] of def.type.props.entries()) {
|
|
742
|
+
typeKeys.add(key);
|
|
743
|
+
if (value[key] === undefined) {
|
|
744
|
+
if (this.opts.partial === "deep" || this.opts.partial === true && this.stackPath.length <= 1) continue;
|
|
745
|
+
}
|
|
746
|
+
this.push(key);
|
|
747
|
+
if (this._validate(item, value[key])) this.pop(false);
|
|
748
|
+
else {
|
|
749
|
+
passed = false;
|
|
750
|
+
this.pop(true);
|
|
751
|
+
if (this.isLimitExceeded()) return false;
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
for (const key of valueKeys) if (this.opts.unknwonProps !== "ignore") {
|
|
755
|
+
if (!typeKeys.has(key)) {
|
|
756
|
+
if (this.opts.unknwonProps === "error") {
|
|
757
|
+
this.push(key);
|
|
758
|
+
this.error(`Unexpected property`);
|
|
759
|
+
this.pop(true);
|
|
760
|
+
if (this.isLimitExceeded()) return false;
|
|
761
|
+
passed = false;
|
|
762
|
+
} else if (this.opts.unknwonProps === "strip") delete value[key];
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
return passed;
|
|
766
|
+
}
|
|
767
|
+
validatePrimitive(def, value) {
|
|
768
|
+
if (typeof def.type.value !== "undefined") {
|
|
769
|
+
if (value !== def.type.value) {
|
|
770
|
+
this.error(`Expected ${def.type.value}, got ${value}`);
|
|
771
|
+
return false;
|
|
772
|
+
}
|
|
773
|
+
return true;
|
|
774
|
+
}
|
|
775
|
+
const typeOfValue = Array.isArray(value) ? "array" : typeof value;
|
|
776
|
+
switch (def.type.designType) {
|
|
777
|
+
case "never":
|
|
778
|
+
this.error(`This type is impossible, must be an internal problem`);
|
|
779
|
+
return false;
|
|
780
|
+
case "any": return true;
|
|
781
|
+
case "string":
|
|
782
|
+
if (typeOfValue !== def.type.designType) {
|
|
783
|
+
this.error(`Expected ${def.type.designType}, got ${typeOfValue}`);
|
|
784
|
+
return false;
|
|
785
|
+
}
|
|
786
|
+
return this.validateString(def, value);
|
|
787
|
+
case "number":
|
|
788
|
+
if (typeOfValue !== def.type.designType) {
|
|
789
|
+
this.error(`Expected ${def.type.designType}, got ${typeOfValue}`);
|
|
790
|
+
return false;
|
|
791
|
+
}
|
|
792
|
+
return this.validateNumber(def, value);
|
|
793
|
+
case "boolean":
|
|
794
|
+
if (typeOfValue !== def.type.designType) {
|
|
795
|
+
this.error(`Expected ${def.type.designType}, got ${typeOfValue}`);
|
|
796
|
+
return false;
|
|
797
|
+
}
|
|
798
|
+
return true;
|
|
799
|
+
case "undefined":
|
|
800
|
+
if (value !== undefined) {
|
|
801
|
+
this.error(`Expected ${def.type.designType}, got ${typeOfValue}`);
|
|
802
|
+
return false;
|
|
803
|
+
}
|
|
804
|
+
return true;
|
|
805
|
+
case "null":
|
|
806
|
+
if (value !== null) {
|
|
807
|
+
this.error(`Expected ${def.type.designType}, got ${typeOfValue}`);
|
|
808
|
+
return false;
|
|
809
|
+
}
|
|
810
|
+
return true;
|
|
811
|
+
default: throw new Error(`Unknown type "${def.type.designType}"`);
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
validateString(def, value) {
|
|
815
|
+
const minLength = def.metadata.get("expect.minLength");
|
|
816
|
+
if (typeof minLength === "number" && value.length < minLength) {
|
|
817
|
+
this.error(`Expected minimum length of ${minLength} characters, got ${value.length} characters`);
|
|
818
|
+
return false;
|
|
819
|
+
}
|
|
820
|
+
const maxLength = def.metadata.get("expect.maxLength");
|
|
821
|
+
if (typeof maxLength === "number" && value.length > maxLength) {
|
|
822
|
+
this.error(`Expected maximum length of ${maxLength} characters, got ${value.length} characters`);
|
|
823
|
+
return false;
|
|
824
|
+
}
|
|
825
|
+
const patterns = def.metadata.get("expect.pattern");
|
|
826
|
+
for (const { pattern, flags, message } of patterns || []) {
|
|
827
|
+
if (!pattern) continue;
|
|
828
|
+
const cacheKey = `${pattern}//${flags || ""}`;
|
|
829
|
+
let regex = regexCache.get(cacheKey);
|
|
830
|
+
if (!regex) {
|
|
831
|
+
regex = new RegExp(pattern, flags);
|
|
832
|
+
regexCache.set(cacheKey, regex);
|
|
833
|
+
}
|
|
834
|
+
if (!regex.test(value)) {
|
|
835
|
+
this.error(message || `Value is expected to match pattern "${pattern}"`);
|
|
836
|
+
return false;
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
return true;
|
|
840
|
+
}
|
|
841
|
+
validateNumber(def, value) {
|
|
842
|
+
const int = def.metadata.get("expect.int");
|
|
843
|
+
if (typeof int === "boolean" && int && value % 1 !== 0) {
|
|
844
|
+
this.error(`Expected integer, got ${value}`);
|
|
845
|
+
return false;
|
|
846
|
+
}
|
|
847
|
+
const min = def.metadata.get("expect.min");
|
|
848
|
+
if (typeof min === "number" && value < min) {
|
|
849
|
+
this.error(`Expected minimum ${min}, got ${value}`);
|
|
850
|
+
return false;
|
|
851
|
+
}
|
|
852
|
+
const max = def.metadata.get("expect.max");
|
|
853
|
+
if (typeof max === "number" && value > max) {
|
|
854
|
+
this.error(`Expected maximum ${max}, got ${value}`);
|
|
855
|
+
return false;
|
|
856
|
+
}
|
|
857
|
+
return true;
|
|
858
|
+
}
|
|
859
|
+
constructor(def, opts) {
|
|
860
|
+
_define_property(this, "def", void 0);
|
|
861
|
+
_define_property(this, "opts", void 0);
|
|
862
|
+
_define_property(this, "errors", void 0);
|
|
863
|
+
_define_property(this, "stackErrors", void 0);
|
|
864
|
+
_define_property(this, "stackPath", void 0);
|
|
865
|
+
this.def = def;
|
|
866
|
+
this.errors = [];
|
|
867
|
+
this.stackErrors = [];
|
|
868
|
+
this.stackPath = [];
|
|
869
|
+
this.opts = {
|
|
870
|
+
partial: false,
|
|
871
|
+
unknwonProps: "error",
|
|
872
|
+
errorLimit: 10,
|
|
873
|
+
...opts
|
|
874
|
+
};
|
|
875
|
+
}
|
|
876
|
+
};
|
|
877
|
+
var ValidatorError = class extends Error {
|
|
878
|
+
constructor(errors) {
|
|
879
|
+
super(`${errors[0].path ? errors[0].path + ": " : ""}${errors[0].message}`), _define_property(this, "errors", void 0), _define_property(this, "name", void 0), this.errors = errors, this.name = "Validation Error";
|
|
880
|
+
}
|
|
881
|
+
};
|
|
882
|
+
|
|
883
|
+
//#endregion
|
|
884
|
+
//#region packages/typescript/src/annotated-type.ts
|
|
885
|
+
function isAnnotatedType(type) {
|
|
886
|
+
return type && type.__is_anscript_annotated_type;
|
|
887
|
+
}
|
|
888
|
+
function defineAnnotatedType(_kind, base) {
|
|
889
|
+
const kind = _kind || "";
|
|
890
|
+
const type = base?.type || {};
|
|
891
|
+
type.kind = kind;
|
|
892
|
+
if ([
|
|
893
|
+
"union",
|
|
894
|
+
"intersection",
|
|
895
|
+
"tuple"
|
|
896
|
+
].includes(kind)) type.items = [];
|
|
897
|
+
if (kind === "object") type.props = new Map();
|
|
898
|
+
type.tags = new Set();
|
|
899
|
+
const metadata = base?.metadata || new Map();
|
|
900
|
+
if (base) Object.assign(base, {
|
|
901
|
+
__is_anscript_annotated_type: true,
|
|
902
|
+
metadata,
|
|
903
|
+
type,
|
|
904
|
+
validator(opts) {
|
|
905
|
+
return new Validator(this, opts);
|
|
906
|
+
}
|
|
907
|
+
});
|
|
908
|
+
else base = {
|
|
909
|
+
__is_anscript_annotated_type: true,
|
|
910
|
+
metadata,
|
|
911
|
+
type,
|
|
912
|
+
validator(opts) {
|
|
913
|
+
return new Validator(this, opts);
|
|
914
|
+
}
|
|
915
|
+
};
|
|
916
|
+
const handle = {
|
|
917
|
+
$type: base,
|
|
918
|
+
$def: type,
|
|
919
|
+
$metadata: metadata,
|
|
920
|
+
_existingObject: undefined,
|
|
921
|
+
tags(...tags) {
|
|
922
|
+
for (const tag of tags) this.$def.tags.add(tag);
|
|
923
|
+
return this;
|
|
924
|
+
},
|
|
925
|
+
designType(value) {
|
|
926
|
+
this.$def.designType = value;
|
|
927
|
+
return this;
|
|
928
|
+
},
|
|
929
|
+
value(value) {
|
|
930
|
+
this.$def.value = value;
|
|
931
|
+
return this;
|
|
932
|
+
},
|
|
933
|
+
of(value) {
|
|
934
|
+
this.$def.of = value;
|
|
935
|
+
return this;
|
|
936
|
+
},
|
|
937
|
+
item(value) {
|
|
938
|
+
this.$def.items.push(value);
|
|
939
|
+
return this;
|
|
940
|
+
},
|
|
941
|
+
prop(name, value) {
|
|
942
|
+
this.$def.props.set(name, value);
|
|
943
|
+
return this;
|
|
944
|
+
},
|
|
945
|
+
optional() {
|
|
946
|
+
this.$type.optional = true;
|
|
947
|
+
return this;
|
|
948
|
+
},
|
|
949
|
+
refTo(type$1, chain) {
|
|
950
|
+
let newBase = type$1;
|
|
951
|
+
const typeName = type$1.name || "Unknown";
|
|
952
|
+
if (isAnnotatedType(newBase)) {
|
|
953
|
+
let keys = "";
|
|
954
|
+
for (const c of chain || []) {
|
|
955
|
+
keys += `["${c}"]`;
|
|
956
|
+
if (newBase.type.kind === "object") newBase = newBase.type.props.get(c);
|
|
957
|
+
else throw new Error(`Can't find prop ${typeName}${keys}`);
|
|
958
|
+
}
|
|
959
|
+
if (!newBase && keys) throw new Error(`Can't find prop ${typeName}${keys}`);
|
|
960
|
+
else if (!newBase) throw new Error(`"${typeName}" is not annotated type`);
|
|
961
|
+
this.$type = {
|
|
962
|
+
__is_anscript_annotated_type: true,
|
|
963
|
+
type: newBase.type,
|
|
964
|
+
metadata,
|
|
965
|
+
validator(opts) {
|
|
966
|
+
return new Validator(this, opts);
|
|
967
|
+
}
|
|
968
|
+
};
|
|
969
|
+
} else throw new Error(`${type$1} is not annotated type`);
|
|
970
|
+
return this;
|
|
971
|
+
},
|
|
972
|
+
annotate(key, value, asArray) {
|
|
973
|
+
if (asArray) if (this.$metadata.has(key)) {
|
|
974
|
+
const a = this.$metadata.get(key);
|
|
975
|
+
if (Array.isArray(a)) a.push(value);
|
|
976
|
+
else this.$metadata.set(key, [a, value]);
|
|
977
|
+
} else this.$metadata.set(key, [value]);
|
|
978
|
+
else this.$metadata.set(key, value);
|
|
979
|
+
return this;
|
|
980
|
+
}
|
|
981
|
+
};
|
|
982
|
+
return handle;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
//#endregion
|
|
986
|
+
//#region packages/typescript/src/index.ts
|
|
987
|
+
var src_default = tsPlugin;
|
|
988
|
+
|
|
989
|
+
//#endregion
|
|
990
|
+
export { Validator, ValidatorError, src_default as default, defineAnnotatedType, isAnnotatedType };
|