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