@atscript/typescript 0.1.31 → 0.1.32
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 +208 -183
- package/dist/index.cjs +208 -183
- package/dist/index.mjs +208 -183
- package/dist/utils.cjs +93 -55
- package/dist/utils.d.ts +19 -6
- package/dist/utils.mjs +93 -55
- package/package.json +2 -2
- package/skills/atscript-typescript/annotations.md +2 -1
package/dist/cli.cjs
CHANGED
|
@@ -143,6 +143,9 @@ else obj[key] = value;
|
|
|
143
143
|
return obj;
|
|
144
144
|
}
|
|
145
145
|
var BaseRenderer = class extends CodePrinter {
|
|
146
|
+
get unused() {
|
|
147
|
+
return this._unused ?? (this._unused = new Set(this.doc.getUnusedTokens().map((t) => t.text)));
|
|
148
|
+
}
|
|
146
149
|
pre() {}
|
|
147
150
|
post() {}
|
|
148
151
|
render() {
|
|
@@ -196,15 +199,14 @@ var BaseRenderer = class extends CodePrinter {
|
|
|
196
199
|
}
|
|
197
200
|
}
|
|
198
201
|
constructor(doc) {
|
|
199
|
-
super(), _define_property$4(this, "doc", void 0), _define_property$4(this, "
|
|
200
|
-
this.unused = new Set(this.doc.getUnusedTokens().map((t) => t.text));
|
|
202
|
+
super(), _define_property$4(this, "doc", void 0), _define_property$4(this, "_unused", void 0), this.doc = doc;
|
|
201
203
|
}
|
|
202
204
|
};
|
|
203
205
|
|
|
204
206
|
//#endregion
|
|
205
207
|
//#region packages/typescript/src/codegen/utils.ts
|
|
208
|
+
const validIdentifier = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
|
|
206
209
|
function wrapProp(name) {
|
|
207
|
-
const validIdentifier = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
|
|
208
210
|
if (!validIdentifier.test(name)) return `"${escapeQuotes(name)}"`;
|
|
209
211
|
return name;
|
|
210
212
|
}
|
|
@@ -291,13 +293,14 @@ var TypeRenderer = class TypeRenderer extends BaseRenderer {
|
|
|
291
293
|
}
|
|
292
294
|
if ((0, __atscript_core.isPrimitive)(def)) return this.write(renderPrimitiveTypeDef(def.config.type));
|
|
293
295
|
}
|
|
294
|
-
renderStructure(struct, asClass, interfaceNode) {
|
|
296
|
+
renderStructure(struct, asClass, interfaceNode, filterProps) {
|
|
295
297
|
this.blockln("{}");
|
|
296
298
|
const patterns = [];
|
|
297
299
|
const propsDefs = new Set();
|
|
298
|
-
for (const prop of
|
|
300
|
+
for (const prop of struct.props.values()) {
|
|
301
|
+
if (filterProps?.has(prop.id)) continue;
|
|
299
302
|
if (prop.token("identifier")?.pattern) {
|
|
300
|
-
patterns.push(prop);
|
|
303
|
+
if (!filterProps) patterns.push(prop);
|
|
301
304
|
continue;
|
|
302
305
|
}
|
|
303
306
|
const phantomType = this.phantomPropType(prop.getDefinition());
|
|
@@ -313,34 +316,36 @@ var TypeRenderer = class TypeRenderer extends BaseRenderer {
|
|
|
313
316
|
}
|
|
314
317
|
if (patterns.length > 0) {
|
|
315
318
|
this.write(`[key: string]: `);
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
for (const def of defs) {
|
|
322
|
-
this.writeln();
|
|
323
|
-
this.write("| ");
|
|
324
|
-
def.split("\n").forEach((l) => this.write(l.trim()));
|
|
325
|
-
}
|
|
326
|
-
this.unindent();
|
|
319
|
+
for (const prop of patterns) propsDefs.add(this.renderTypeDefString(prop.getDefinition()));
|
|
320
|
+
const defs = Array.from(propsDefs);
|
|
321
|
+
if (defs.length > 1) {
|
|
322
|
+
this.indent();
|
|
323
|
+
for (const def of defs) {
|
|
327
324
|
this.writeln();
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
this.writeln(
|
|
334
|
-
this.writeln(`static metadata: TMetadataMap<AtscriptMetadata>`);
|
|
335
|
-
this.writeln(`static validator: (opts?: Partial<TValidatorOptions>) => Validator<typeof ${asClass}>`);
|
|
336
|
-
if (resolveJsonSchemaMode(this.opts) === false) this.writeln("/** @deprecated JSON Schema support is disabled. Calling this method will throw a runtime error. To enable, set `jsonSchema: 'lazy'` or `jsonSchema: 'bundle'` in tsPlugin options, or add `@emit.jsonSchema` annotation to individual interfaces. */");
|
|
337
|
-
this.writeln("static toJsonSchema: () => any");
|
|
338
|
-
if (!this.opts?.exampleData) this.writeln("/** @deprecated Example Data support is disabled. To enable, set `exampleData: true` in tsPlugin options. */");
|
|
339
|
-
this.writeln("static toExampleData?: () => any");
|
|
340
|
-
if (interfaceNode && this.hasDbTable(interfaceNode)) this.renderFlat(interfaceNode);
|
|
325
|
+
this.write("| ");
|
|
326
|
+
def.split("\n").forEach((l) => this.write(l.trim()));
|
|
327
|
+
}
|
|
328
|
+
this.unindent();
|
|
329
|
+
this.writeln();
|
|
330
|
+
} else defs[0].split("\n").forEach((l) => this.writeln(l));
|
|
341
331
|
}
|
|
332
|
+
if (asClass) this.renderStaticDeclarations(asClass, interfaceNode);
|
|
342
333
|
this.pop();
|
|
343
334
|
}
|
|
335
|
+
renderStaticDeclarations(asClass, interfaceNode) {
|
|
336
|
+
this.writeln("static __is_atscript_annotated_type: true");
|
|
337
|
+
this.writeln(`static type: TAtscriptTypeObject<keyof ${asClass}, ${asClass}>`);
|
|
338
|
+
this.writeln(`static metadata: TMetadataMap<AtscriptMetadata>`);
|
|
339
|
+
this.writeln(`static validator: (opts?: Partial<TValidatorOptions>) => Validator<typeof ${asClass}>`);
|
|
340
|
+
if (resolveJsonSchemaMode(this.opts) === false) this.writeln("/** @deprecated JSON Schema support is disabled. Calling this method will throw a runtime error. To enable, set `jsonSchema: 'lazy'` or `jsonSchema: 'bundle'` in tsPlugin options, or add `@emit.jsonSchema` annotation to individual interfaces. */");
|
|
341
|
+
this.writeln("static toJsonSchema: () => any");
|
|
342
|
+
if (!this.opts?.exampleData) this.writeln("/** @deprecated Example Data support is disabled. To enable, set `exampleData: true` in tsPlugin options. */");
|
|
343
|
+
this.writeln("static toExampleData?: () => any");
|
|
344
|
+
if (interfaceNode && this.hasDbTable(interfaceNode)) {
|
|
345
|
+
this.renderFlat(interfaceNode);
|
|
346
|
+
this.renderPk(interfaceNode);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
344
349
|
renderInterface(node) {
|
|
345
350
|
this.writeln();
|
|
346
351
|
const exported = node.token("export")?.text === "export";
|
|
@@ -364,7 +369,7 @@ var TypeRenderer = class TypeRenderer extends BaseRenderer {
|
|
|
364
369
|
}
|
|
365
370
|
if (!firstParentProps && (0, __atscript_core.isStructure)(fpDef)) firstParentProps = fpDef.props;
|
|
366
371
|
}
|
|
367
|
-
this.
|
|
372
|
+
this.renderStructure(resolved, node.id, node, firstParentProps);
|
|
368
373
|
} else this.writeln("{}");
|
|
369
374
|
} else {
|
|
370
375
|
this.write(`class ${node.id} `);
|
|
@@ -374,35 +379,6 @@ else this.writeln("{}");
|
|
|
374
379
|
}
|
|
375
380
|
this.writeln();
|
|
376
381
|
}
|
|
377
|
-
/**
|
|
378
|
-
* Renders a structure block, optionally filtering out props that exist in a parent.
|
|
379
|
-
*/ renderStructureFiltered(struct, asClass, filterProps, interfaceNode) {
|
|
380
|
-
if (!filterProps) return this.renderStructure(struct, asClass, interfaceNode);
|
|
381
|
-
this.blockln("{}");
|
|
382
|
-
for (const prop of Array.from(struct.props.values())) {
|
|
383
|
-
if (filterProps.has(prop.id)) continue;
|
|
384
|
-
if (prop.token("identifier")?.pattern) continue;
|
|
385
|
-
const phantomType = this.phantomPropType(prop.getDefinition());
|
|
386
|
-
if (phantomType) {
|
|
387
|
-
this.writeln(`// ${prop.id}: ${phantomType}`);
|
|
388
|
-
continue;
|
|
389
|
-
}
|
|
390
|
-
const optional = !!prop.token("optional");
|
|
391
|
-
this.write(wrapProp(prop.id), optional ? "?" : "", ": ");
|
|
392
|
-
const renderedDef = this.renderTypeDefString(prop.getDefinition());
|
|
393
|
-
renderedDef.split("\n").forEach((l) => this.writeln(l));
|
|
394
|
-
}
|
|
395
|
-
this.writeln("static __is_atscript_annotated_type: true");
|
|
396
|
-
this.writeln(`static type: TAtscriptTypeObject<keyof ${asClass}, ${asClass}>`);
|
|
397
|
-
this.writeln(`static metadata: TMetadataMap<AtscriptMetadata>`);
|
|
398
|
-
this.writeln(`static validator: (opts?: Partial<TValidatorOptions>) => Validator<typeof ${asClass}>`);
|
|
399
|
-
if (resolveJsonSchemaMode(this.opts) === false) this.writeln("/** @deprecated JSON Schema support is disabled. Calling this method will throw a runtime error. To enable, set `jsonSchema: 'lazy'` or `jsonSchema: 'bundle'` in tsPlugin options, or add `@emit.jsonSchema` annotation to individual interfaces. */");
|
|
400
|
-
this.writeln("static toJsonSchema: () => any");
|
|
401
|
-
if (!this.opts?.exampleData) this.writeln("/** @deprecated Example Data support is disabled. To enable, set `exampleData: true` in tsPlugin options. */");
|
|
402
|
-
this.writeln("static toExampleData?: () => any");
|
|
403
|
-
if (interfaceNode && this.hasDbTable(interfaceNode)) this.renderFlat(interfaceNode);
|
|
404
|
-
this.pop();
|
|
405
|
-
}
|
|
406
382
|
renderType(node) {
|
|
407
383
|
this.writeln();
|
|
408
384
|
const exported = node.token("export")?.text === "export";
|
|
@@ -500,6 +476,43 @@ else {
|
|
|
500
476
|
}
|
|
501
477
|
this.pop();
|
|
502
478
|
}
|
|
479
|
+
/**
|
|
480
|
+
* Renders the `static __pk` property — the primary key type for type-safe
|
|
481
|
+
* `deleteOne`/`findById` signatures on `AtscriptDbTable`.
|
|
482
|
+
*
|
|
483
|
+
* - **Single PK** (one `@meta.id`) → `static __pk: <scalar type>`
|
|
484
|
+
* - **Compound PK** (multiple `@meta.id`) → `static __pk: { field1: Type1; field2: Type2 }`
|
|
485
|
+
* - **No PK** → no `__pk` emitted
|
|
486
|
+
*/ renderPk(node) {
|
|
487
|
+
let struct;
|
|
488
|
+
if (node.hasExtends) struct = this.doc.resolveInterfaceExtends(node);
|
|
489
|
+
if (!struct) struct = node.getDefinition();
|
|
490
|
+
if (!struct || !(0, __atscript_core.isStructure)(struct)) return;
|
|
491
|
+
const pkProps = [];
|
|
492
|
+
for (const [name, prop] of struct.props) {
|
|
493
|
+
if (prop.token("identifier")?.pattern) continue;
|
|
494
|
+
if (prop.countAnnotations("meta.id") > 0) pkProps.push({
|
|
495
|
+
name,
|
|
496
|
+
prop
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
if (pkProps.length === 0) return;
|
|
500
|
+
this.writeln();
|
|
501
|
+
if (pkProps.length === 1) {
|
|
502
|
+
this.write("static __pk: ");
|
|
503
|
+
const renderedDef = this.renderTypeDefString(pkProps[0].prop.getDefinition());
|
|
504
|
+
renderedDef.split("\n").forEach((l) => this.writeln(l));
|
|
505
|
+
} else {
|
|
506
|
+
this.write("static __pk: ");
|
|
507
|
+
this.blockln("{}");
|
|
508
|
+
for (const { name, prop } of pkProps) {
|
|
509
|
+
this.write(wrapProp(name), ": ");
|
|
510
|
+
const renderedDef = this.renderTypeDefString(prop.getDefinition());
|
|
511
|
+
renderedDef.split("\n").forEach((l) => this.writeln(l));
|
|
512
|
+
}
|
|
513
|
+
this.pop();
|
|
514
|
+
}
|
|
515
|
+
}
|
|
503
516
|
phantomPropType(def) {
|
|
504
517
|
if (!def) return undefined;
|
|
505
518
|
if ((0, __atscript_core.isPrimitive)(def) && def.config.type === "phantom") return def.id;
|
|
@@ -548,24 +561,6 @@ function renderPrimitiveTypeDef(def) {
|
|
|
548
561
|
}
|
|
549
562
|
}
|
|
550
563
|
|
|
551
|
-
//#endregion
|
|
552
|
-
//#region packages/typescript/src/traverse.ts
|
|
553
|
-
function forAnnotatedType(def, handlers) {
|
|
554
|
-
switch (def.type.kind) {
|
|
555
|
-
case "": {
|
|
556
|
-
const typed = def;
|
|
557
|
-
if (handlers.phantom && typed.type.designType === "phantom") return handlers.phantom(typed);
|
|
558
|
-
return handlers.final(typed);
|
|
559
|
-
}
|
|
560
|
-
case "object": return handlers.object(def);
|
|
561
|
-
case "array": return handlers.array(def);
|
|
562
|
-
case "union": return handlers.union(def);
|
|
563
|
-
case "intersection": return handlers.intersection(def);
|
|
564
|
-
case "tuple": return handlers.tuple(def);
|
|
565
|
-
default: throw new Error(`Unknown type kind "${def.type.kind}"`);
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
|
|
569
564
|
//#endregion
|
|
570
565
|
//#region packages/typescript/src/validator.ts
|
|
571
566
|
function _define_property$2(obj, key, value) {
|
|
@@ -581,28 +576,35 @@ else obj[key] = value;
|
|
|
581
576
|
const regexCache = new Map();
|
|
582
577
|
var Validator = class {
|
|
583
578
|
isLimitExceeded() {
|
|
584
|
-
if (this.stackErrors.length > 0)
|
|
579
|
+
if (this.stackErrors.length > 0) {
|
|
580
|
+
const top = this.stackErrors[this.stackErrors.length - 1];
|
|
581
|
+
return top !== null && top.length >= this.opts.errorLimit;
|
|
582
|
+
}
|
|
585
583
|
return this.errors.length >= this.opts.errorLimit;
|
|
586
584
|
}
|
|
587
585
|
push(name) {
|
|
588
586
|
this.stackPath.push(name);
|
|
589
|
-
this.stackErrors.push(
|
|
587
|
+
this.stackErrors.push(null);
|
|
588
|
+
this.cachedPath = this.stackPath.length <= 1 ? "" : this.stackPath[1] + (this.stackPath.length > 2 ? "." + this.stackPath.slice(2).join(".") : "");
|
|
590
589
|
}
|
|
591
590
|
pop(saveErrors) {
|
|
592
591
|
this.stackPath.pop();
|
|
593
592
|
const popped = this.stackErrors.pop();
|
|
594
|
-
if (saveErrors && popped
|
|
595
|
-
|
|
596
|
-
});
|
|
593
|
+
if (saveErrors && popped !== null && popped !== undefined && popped.length > 0) for (const err of popped) this.error(err.message, err.path, err.details);
|
|
594
|
+
this.cachedPath = this.stackPath.length <= 1 ? "" : this.stackPath[1] + (this.stackPath.length > 2 ? "." + this.stackPath.slice(2).join(".") : "");
|
|
597
595
|
return popped;
|
|
598
596
|
}
|
|
599
597
|
clear() {
|
|
600
|
-
this.stackErrors[this.stackErrors.length - 1] =
|
|
598
|
+
this.stackErrors[this.stackErrors.length - 1] = null;
|
|
601
599
|
}
|
|
602
600
|
error(message, path$3, details) {
|
|
603
|
-
|
|
601
|
+
let errors = this.stackErrors[this.stackErrors.length - 1];
|
|
602
|
+
if (!errors) if (this.stackErrors.length > 0) {
|
|
603
|
+
errors = [];
|
|
604
|
+
this.stackErrors[this.stackErrors.length - 1] = errors;
|
|
605
|
+
} else errors = this.errors;
|
|
604
606
|
const error = {
|
|
605
|
-
path: path$3 || this.
|
|
607
|
+
path: path$3 || this.cachedPath,
|
|
606
608
|
message
|
|
607
609
|
};
|
|
608
610
|
if (details?.length) error.details = details;
|
|
@@ -622,9 +624,10 @@ var Validator = class {
|
|
|
622
624
|
* @returns `true` if the value matches the type definition.
|
|
623
625
|
* @throws {ValidatorError} When validation fails and `safe` is not `true`.
|
|
624
626
|
*/ validate(value, safe, context) {
|
|
625
|
-
this.push("");
|
|
626
627
|
this.errors = [];
|
|
627
628
|
this.stackErrors = [];
|
|
629
|
+
this.stackPath = [""];
|
|
630
|
+
this.cachedPath = "";
|
|
628
631
|
this.context = context;
|
|
629
632
|
const passed = this.validateSafe(this.def, value);
|
|
630
633
|
this.pop(!passed);
|
|
@@ -638,7 +641,7 @@ var Validator = class {
|
|
|
638
641
|
validateSafe(def, value) {
|
|
639
642
|
if (this.isLimitExceeded()) return false;
|
|
640
643
|
if (!isAnnotatedType(def)) throw new Error("Can not validate not-annotated type");
|
|
641
|
-
if (typeof this.opts.replace === "function") def = this.opts.replace(def, this.
|
|
644
|
+
if (typeof this.opts.replace === "function") def = this.opts.replace(def, this.cachedPath);
|
|
642
645
|
if (def.optional && value === undefined) return true;
|
|
643
646
|
for (const plugin of this.opts.plugins) {
|
|
644
647
|
const result = plugin(this, def, value);
|
|
@@ -647,18 +650,21 @@ var Validator = class {
|
|
|
647
650
|
return this.validateAnnotatedType(def, value);
|
|
648
651
|
}
|
|
649
652
|
get path() {
|
|
650
|
-
return this.
|
|
653
|
+
return this.cachedPath;
|
|
651
654
|
}
|
|
652
655
|
validateAnnotatedType(def, value) {
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
656
|
+
switch (def.type.kind) {
|
|
657
|
+
case "": {
|
|
658
|
+
if (def.type.designType === "phantom") return true;
|
|
659
|
+
return this.validatePrimitive(def, value);
|
|
660
|
+
}
|
|
661
|
+
case "object": return this.validateObject(def, value);
|
|
662
|
+
case "array": return this.validateArray(def, value);
|
|
663
|
+
case "union": return this.validateUnion(def, value);
|
|
664
|
+
case "intersection": return this.validateIntersection(def, value);
|
|
665
|
+
case "tuple": return this.validateTuple(def, value);
|
|
666
|
+
default: throw new Error(`Unknown type kind "${def.type.kind}"`);
|
|
667
|
+
}
|
|
662
668
|
}
|
|
663
669
|
validateUnion(def, value) {
|
|
664
670
|
let i = 0;
|
|
@@ -722,6 +728,30 @@ var Validator = class {
|
|
|
722
728
|
return false;
|
|
723
729
|
}
|
|
724
730
|
}
|
|
731
|
+
const uniqueItems = def.metadata.get("expect.array.uniqueItems");
|
|
732
|
+
if (uniqueItems) {
|
|
733
|
+
const separator = "▼↩";
|
|
734
|
+
const seen = new Set();
|
|
735
|
+
const keyProps = new Set();
|
|
736
|
+
if (def.type.of.type.kind === "object") {
|
|
737
|
+
for (const [key, val] of def.type.of.type.props.entries()) if (val.metadata.get("expect.array.key")) keyProps.add(key);
|
|
738
|
+
}
|
|
739
|
+
for (let idx = 0; idx < value.length; idx++) {
|
|
740
|
+
const item = value[idx];
|
|
741
|
+
let key;
|
|
742
|
+
if (keyProps.size > 0) {
|
|
743
|
+
key = "";
|
|
744
|
+
for (const prop of keyProps) key += JSON.stringify(item[prop]) + separator;
|
|
745
|
+
} else key = JSON.stringify(item);
|
|
746
|
+
if (seen.has(key)) {
|
|
747
|
+
this.push(String(idx));
|
|
748
|
+
this.error(uniqueItems.message || "Duplicate items are not allowed");
|
|
749
|
+
this.pop(true);
|
|
750
|
+
return false;
|
|
751
|
+
}
|
|
752
|
+
seen.add(key);
|
|
753
|
+
}
|
|
754
|
+
}
|
|
725
755
|
let i = 0;
|
|
726
756
|
let passed = true;
|
|
727
757
|
for (const item of value) {
|
|
@@ -743,21 +773,20 @@ var Validator = class {
|
|
|
743
773
|
let passed = true;
|
|
744
774
|
const valueKeys = new Set(Object.keys(value));
|
|
745
775
|
const typeKeys = new Set();
|
|
746
|
-
|
|
776
|
+
let skipList;
|
|
747
777
|
if (this.opts.skipList) {
|
|
748
|
-
const path$3 = this.stackPath.length > 1 ? `${this.
|
|
749
|
-
this.opts.skipList.
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
});
|
|
778
|
+
const path$3 = this.stackPath.length > 1 ? `${this.cachedPath}.` : "";
|
|
779
|
+
for (const item of this.opts.skipList) if (item.startsWith(path$3)) {
|
|
780
|
+
const key = item.slice(path$3.length);
|
|
781
|
+
if (!skipList) skipList = new Set();
|
|
782
|
+
skipList.add(key);
|
|
783
|
+
valueKeys.delete(key);
|
|
784
|
+
}
|
|
756
785
|
}
|
|
757
786
|
let partialFunctionMatched = false;
|
|
758
|
-
if (typeof this.opts.partial === "function") partialFunctionMatched = this.opts.partial(def, this.
|
|
787
|
+
if (typeof this.opts.partial === "function") partialFunctionMatched = this.opts.partial(def, this.cachedPath);
|
|
759
788
|
for (const [key, item] of def.type.props.entries()) {
|
|
760
|
-
if (skipList.has(key) || isPhantomType(item)) continue;
|
|
789
|
+
if (skipList && skipList.has(key) || isPhantomType(item)) continue;
|
|
761
790
|
typeKeys.add(key);
|
|
762
791
|
if (value[key] === undefined) {
|
|
763
792
|
if (partialFunctionMatched || this.opts.partial === "deep" || this.opts.partial === true && this.stackPath.length <= 1) continue;
|
|
@@ -778,19 +807,21 @@ else {
|
|
|
778
807
|
def: propDef
|
|
779
808
|
});
|
|
780
809
|
if (matched.length > 0) {
|
|
810
|
+
this.push(key);
|
|
781
811
|
let keyPassed = false;
|
|
782
|
-
for (const { def:
|
|
783
|
-
this.
|
|
784
|
-
|
|
785
|
-
|
|
812
|
+
for (const { def: propDef } of matched) {
|
|
813
|
+
if (this.validateSafe(propDef, value[key])) {
|
|
814
|
+
keyPassed = true;
|
|
815
|
+
break;
|
|
816
|
+
}
|
|
817
|
+
this.clear();
|
|
786
818
|
}
|
|
787
819
|
if (!keyPassed) {
|
|
788
|
-
this.push(key);
|
|
789
820
|
this.validateSafe(matched[0].def, value[key]);
|
|
790
821
|
this.pop(true);
|
|
791
822
|
passed = false;
|
|
792
823
|
if (this.isLimitExceeded()) return false;
|
|
793
|
-
}
|
|
824
|
+
} else this.pop(false);
|
|
794
825
|
} else if (this.opts.unknownProps !== "ignore") {
|
|
795
826
|
if (this.opts.unknownProps === "error") {
|
|
796
827
|
this.push(key);
|
|
@@ -943,11 +974,13 @@ else {
|
|
|
943
974
|
/** Validation errors collected during the last {@link validate} call. */ _define_property$2(this, "errors", void 0);
|
|
944
975
|
_define_property$2(this, "stackErrors", void 0);
|
|
945
976
|
_define_property$2(this, "stackPath", void 0);
|
|
977
|
+
_define_property$2(this, "cachedPath", void 0);
|
|
946
978
|
_define_property$2(this, "context", void 0);
|
|
947
979
|
this.def = def;
|
|
948
980
|
this.errors = [];
|
|
949
981
|
this.stackErrors = [];
|
|
950
982
|
this.stackPath = [];
|
|
983
|
+
this.cachedPath = "";
|
|
951
984
|
this.opts = {
|
|
952
985
|
partial: false,
|
|
953
986
|
unknownProps: "error",
|
|
@@ -1091,6 +1124,24 @@ function isPhantomType(def) {
|
|
|
1091
1124
|
return def.type.kind === "" && def.type.designType === "phantom";
|
|
1092
1125
|
}
|
|
1093
1126
|
|
|
1127
|
+
//#endregion
|
|
1128
|
+
//#region packages/typescript/src/traverse.ts
|
|
1129
|
+
function forAnnotatedType(def, handlers) {
|
|
1130
|
+
switch (def.type.kind) {
|
|
1131
|
+
case "": {
|
|
1132
|
+
const typed = def;
|
|
1133
|
+
if (handlers.phantom && typed.type.designType === "phantom") return handlers.phantom(typed);
|
|
1134
|
+
return handlers.final(typed);
|
|
1135
|
+
}
|
|
1136
|
+
case "object": return handlers.object(def);
|
|
1137
|
+
case "array": return handlers.array(def);
|
|
1138
|
+
case "union": return handlers.union(def);
|
|
1139
|
+
case "intersection": return handlers.intersection(def);
|
|
1140
|
+
case "tuple": return handlers.tuple(def);
|
|
1141
|
+
default: throw new Error(`Unknown type kind "${def.type.kind}"`);
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1094
1145
|
//#endregion
|
|
1095
1146
|
//#region packages/typescript/src/json-schema.ts
|
|
1096
1147
|
/**
|
|
@@ -1263,24 +1314,25 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
1263
1314
|
this.writeln("// prettier-ignore-start");
|
|
1264
1315
|
this.writeln("/* eslint-disable */");
|
|
1265
1316
|
this.writeln("/* oxlint-disable */");
|
|
1317
|
+
let hasMutatingAnnotate = false;
|
|
1318
|
+
const nodesByName = new Map();
|
|
1319
|
+
for (const node of this.doc.nodes) {
|
|
1320
|
+
if (node.entity === "annotate" && node.isMutating) hasMutatingAnnotate = true;
|
|
1321
|
+
if (node.__typeId !== null && node.__typeId !== undefined && node.id) {
|
|
1322
|
+
const name = node.id;
|
|
1323
|
+
if (!nodesByName.has(name)) nodesByName.set(name, []);
|
|
1324
|
+
nodesByName.get(name).push(node);
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
for (const [name, nodes] of nodesByName) if (nodes.length === 1) this.typeIds.set(nodes[0], name);
|
|
1328
|
+
else for (let i = 0; i < nodes.length; i++) this.typeIds.set(nodes[i], `${name}__${i + 1}`);
|
|
1266
1329
|
const imports = ["defineAnnotatedType as $", "annotate as $a"];
|
|
1267
|
-
const hasMutatingAnnotate = this.doc.nodes.some((n) => n.entity === "annotate" && n.isMutating);
|
|
1268
1330
|
if (hasMutatingAnnotate) imports.push("cloneRefProp as $c");
|
|
1269
1331
|
const jsonSchemaMode = resolveJsonSchemaMode(this.opts);
|
|
1270
1332
|
if (jsonSchemaMode === "lazy") imports.push("buildJsonSchema as $$");
|
|
1271
1333
|
if (this.opts?.exampleData) imports.push("createDataFromAnnotatedType as $e");
|
|
1272
1334
|
if (jsonSchemaMode === false) imports.push("throwFeatureDisabled as $d");
|
|
1273
1335
|
this.writeln(`import { ${imports.join(", ")} } from "@atscript/typescript/utils"`);
|
|
1274
|
-
const nameCounts = new Map();
|
|
1275
|
-
const nodesByName = new Map();
|
|
1276
|
-
for (const node of this.doc.nodes) if (node.__typeId !== null && node.__typeId !== undefined && node.id) {
|
|
1277
|
-
const name = node.id;
|
|
1278
|
-
nameCounts.set(name, (nameCounts.get(name) || 0) + 1);
|
|
1279
|
-
if (!nodesByName.has(name)) nodesByName.set(name, []);
|
|
1280
|
-
nodesByName.get(name).push(node);
|
|
1281
|
-
}
|
|
1282
|
-
for (const [name, nodes] of nodesByName) if (nodes.length === 1) this.typeIds.set(nodes[0], name);
|
|
1283
|
-
else for (let i = 0; i < nodes.length; i++) this.typeIds.set(nodes[i], `${name}__${i + 1}`);
|
|
1284
1336
|
}
|
|
1285
1337
|
buildAdHocMap(annotateNodes) {
|
|
1286
1338
|
const map = new Map();
|
|
@@ -1289,7 +1341,8 @@ else for (let i = 0; i < nodes.length; i++) this.typeIds.set(nodes[i], `${name}_
|
|
|
1289
1341
|
const anns = entry.annotations || [];
|
|
1290
1342
|
if (anns.length > 0) {
|
|
1291
1343
|
const existing = map.get(path$3);
|
|
1292
|
-
|
|
1344
|
+
if (existing) existing.push(...anns);
|
|
1345
|
+
else map.set(path$3, [...anns]);
|
|
1293
1346
|
}
|
|
1294
1347
|
}
|
|
1295
1348
|
return map.size > 0 ? map : null;
|
|
@@ -1348,35 +1401,20 @@ else def = def.getDefinition() || def;
|
|
|
1348
1401
|
this.renderExampleDataMethod(node);
|
|
1349
1402
|
}
|
|
1350
1403
|
renderInterface(node) {
|
|
1351
|
-
this.
|
|
1352
|
-
const exported = node.token("export")?.text === "export";
|
|
1353
|
-
this.write(exported ? "export " : "");
|
|
1354
|
-
this.write(`class ${node.id} `);
|
|
1355
|
-
this.blockln("{}");
|
|
1356
|
-
this.renderClassStatics(node);
|
|
1357
|
-
this.popln();
|
|
1358
|
-
this.postAnnotate.push(node);
|
|
1359
|
-
this.writeln();
|
|
1404
|
+
this.renderDefinitionClass(node);
|
|
1360
1405
|
}
|
|
1361
1406
|
renderType(node) {
|
|
1362
|
-
this.
|
|
1363
|
-
const exported = node.token("export")?.text === "export";
|
|
1364
|
-
this.write(exported ? "export " : "");
|
|
1365
|
-
this.write(`class ${node.id} `);
|
|
1366
|
-
this.blockln("{}");
|
|
1367
|
-
this.renderClassStatics(node);
|
|
1368
|
-
this.popln();
|
|
1369
|
-
this.postAnnotate.push(node);
|
|
1370
|
-
this.writeln();
|
|
1407
|
+
this.renderDefinitionClass(node);
|
|
1371
1408
|
}
|
|
1372
1409
|
renderAnnotate(node) {
|
|
1373
1410
|
if (node.isMutating) {
|
|
1374
1411
|
this.postAnnotate.push(node);
|
|
1375
1412
|
return;
|
|
1376
1413
|
}
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1414
|
+
if (!this.doc.unwindType(node.targetName)?.def) return;
|
|
1415
|
+
this.renderDefinitionClass(node);
|
|
1416
|
+
}
|
|
1417
|
+
renderDefinitionClass(node) {
|
|
1380
1418
|
this.writeln();
|
|
1381
1419
|
const exported = node.token("export")?.text === "export";
|
|
1382
1420
|
this.write(exported ? "export " : "");
|
|
@@ -1534,6 +1572,11 @@ else handle.prop(prop.id, propHandle.$type);
|
|
|
1534
1572
|
handle.annotate(a.name, true);
|
|
1535
1573
|
break;
|
|
1536
1574
|
}
|
|
1575
|
+
case "expect.array.uniqueItems":
|
|
1576
|
+
case "expect.array.key": {
|
|
1577
|
+
handle.annotate(a.name, { message: a.args[0]?.text });
|
|
1578
|
+
break;
|
|
1579
|
+
}
|
|
1537
1580
|
default:
|
|
1538
1581
|
}
|
|
1539
1582
|
});
|
|
@@ -1601,10 +1644,7 @@ else handle.prop(prop.id, propHandle.$type);
|
|
|
1601
1644
|
this.writeln(`$("array"${name ? `, ${name}` : ""})`).indent().defineArray(node).unindent();
|
|
1602
1645
|
return this;
|
|
1603
1646
|
}
|
|
1604
|
-
default:
|
|
1605
|
-
console.log("!!!!!!! UNKNOWN", node.entity);
|
|
1606
|
-
return this;
|
|
1607
|
-
}
|
|
1647
|
+
default: return this;
|
|
1608
1648
|
}
|
|
1609
1649
|
}
|
|
1610
1650
|
defineConst(node) {
|
|
@@ -1816,40 +1856,25 @@ else targetValue = "true";
|
|
|
1816
1856
|
this.writeln(`$c(${clone.parentPath}, "${escapeQuotes(clone.propName)}")`);
|
|
1817
1857
|
}
|
|
1818
1858
|
}
|
|
1819
|
-
for (const { entry, accessors } of entryAccessors)
|
|
1820
|
-
const anns = entry.annotations;
|
|
1821
|
-
for (const accessor of accessors) {
|
|
1822
|
-
const cleared = new Set();
|
|
1823
|
-
for (const an of anns) {
|
|
1824
|
-
const { value, multiple } = this.computeAnnotationValue(entry, an);
|
|
1825
|
-
if (multiple) {
|
|
1826
|
-
if (!cleared.has(an.name)) {
|
|
1827
|
-
const spec = this.doc.resolveAnnotation(an.name);
|
|
1828
|
-
if (!spec || spec.config.mergeStrategy !== "append") this.writeln(`${accessor}.metadata.delete("${escapeQuotes(an.name)}")`);
|
|
1829
|
-
cleared.add(an.name);
|
|
1830
|
-
}
|
|
1831
|
-
this.writeln(`$a(${accessor}.metadata, "${escapeQuotes(an.name)}", ${value}, true)`);
|
|
1832
|
-
} else this.writeln(`$a(${accessor}.metadata, "${escapeQuotes(an.name)}", ${value})`);
|
|
1833
|
-
}
|
|
1834
|
-
}
|
|
1835
|
-
}
|
|
1859
|
+
for (const { entry, accessors } of entryAccessors) for (const accessor of accessors) this.emitMutatingAnnotations(entry, entry.annotations, accessor);
|
|
1836
1860
|
const topAnnotations = node.annotations;
|
|
1837
|
-
if (topAnnotations && topAnnotations.length > 0)
|
|
1838
|
-
const cleared = new Set();
|
|
1839
|
-
for (const an of topAnnotations) {
|
|
1840
|
-
const { value, multiple } = this.computeAnnotationValue(node, an);
|
|
1841
|
-
if (multiple) {
|
|
1842
|
-
if (!cleared.has(an.name)) {
|
|
1843
|
-
const spec = this.doc.resolveAnnotation(an.name);
|
|
1844
|
-
if (!spec || spec.config.mergeStrategy !== "append") this.writeln(`${targetName}.metadata.delete("${escapeQuotes(an.name)}")`);
|
|
1845
|
-
cleared.add(an.name);
|
|
1846
|
-
}
|
|
1847
|
-
this.writeln(`$a(${targetName}.metadata, "${escapeQuotes(an.name)}", ${value}, true)`);
|
|
1848
|
-
} else this.writeln(`$a(${targetName}.metadata, "${escapeQuotes(an.name)}", ${value})`);
|
|
1849
|
-
}
|
|
1850
|
-
}
|
|
1861
|
+
if (topAnnotations && topAnnotations.length > 0) this.emitMutatingAnnotations(node, topAnnotations, targetName);
|
|
1851
1862
|
this.writeln();
|
|
1852
1863
|
}
|
|
1864
|
+
emitMutatingAnnotations(node, annotations, accessor) {
|
|
1865
|
+
const cleared = new Set();
|
|
1866
|
+
for (const an of annotations) {
|
|
1867
|
+
const { value, multiple } = this.computeAnnotationValue(node, an);
|
|
1868
|
+
if (multiple) {
|
|
1869
|
+
if (!cleared.has(an.name)) {
|
|
1870
|
+
const spec = this.doc.resolveAnnotation(an.name);
|
|
1871
|
+
if (!spec || spec.config.mergeStrategy !== "append") this.writeln(`${accessor}.metadata.delete("${escapeQuotes(an.name)}")`);
|
|
1872
|
+
cleared.add(an.name);
|
|
1873
|
+
}
|
|
1874
|
+
this.writeln(`$a(${accessor}.metadata, "${escapeQuotes(an.name)}", ${value}, true)`);
|
|
1875
|
+
} else this.writeln(`$a(${accessor}.metadata, "${escapeQuotes(an.name)}", ${value})`);
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1853
1878
|
resolveTargetDef(targetName) {
|
|
1854
1879
|
const unwound = this.doc.unwindType(targetName);
|
|
1855
1880
|
if (!unwound?.def) return undefined;
|