@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.mjs
CHANGED
|
@@ -116,6 +116,9 @@ else obj[key] = value;
|
|
|
116
116
|
return obj;
|
|
117
117
|
}
|
|
118
118
|
var BaseRenderer = class extends CodePrinter {
|
|
119
|
+
get unused() {
|
|
120
|
+
return this._unused ?? (this._unused = new Set(this.doc.getUnusedTokens().map((t) => t.text)));
|
|
121
|
+
}
|
|
119
122
|
pre() {}
|
|
120
123
|
post() {}
|
|
121
124
|
render() {
|
|
@@ -169,15 +172,14 @@ var BaseRenderer = class extends CodePrinter {
|
|
|
169
172
|
}
|
|
170
173
|
}
|
|
171
174
|
constructor(doc) {
|
|
172
|
-
super(), _define_property$3(this, "doc", void 0), _define_property$3(this, "
|
|
173
|
-
this.unused = new Set(this.doc.getUnusedTokens().map((t) => t.text));
|
|
175
|
+
super(), _define_property$3(this, "doc", void 0), _define_property$3(this, "_unused", void 0), this.doc = doc;
|
|
174
176
|
}
|
|
175
177
|
};
|
|
176
178
|
|
|
177
179
|
//#endregion
|
|
178
180
|
//#region packages/typescript/src/codegen/utils.ts
|
|
181
|
+
const validIdentifier = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
|
|
179
182
|
function wrapProp(name) {
|
|
180
|
-
const validIdentifier = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
|
|
181
183
|
if (!validIdentifier.test(name)) return `"${escapeQuotes(name)}"`;
|
|
182
184
|
return name;
|
|
183
185
|
}
|
|
@@ -264,13 +266,14 @@ var TypeRenderer = class TypeRenderer extends BaseRenderer {
|
|
|
264
266
|
}
|
|
265
267
|
if (isPrimitive(def)) return this.write(renderPrimitiveTypeDef(def.config.type));
|
|
266
268
|
}
|
|
267
|
-
renderStructure(struct, asClass, interfaceNode) {
|
|
269
|
+
renderStructure(struct, asClass, interfaceNode, filterProps) {
|
|
268
270
|
this.blockln("{}");
|
|
269
271
|
const patterns = [];
|
|
270
272
|
const propsDefs = new Set();
|
|
271
|
-
for (const prop of
|
|
273
|
+
for (const prop of struct.props.values()) {
|
|
274
|
+
if (filterProps?.has(prop.id)) continue;
|
|
272
275
|
if (prop.token("identifier")?.pattern) {
|
|
273
|
-
patterns.push(prop);
|
|
276
|
+
if (!filterProps) patterns.push(prop);
|
|
274
277
|
continue;
|
|
275
278
|
}
|
|
276
279
|
const phantomType = this.phantomPropType(prop.getDefinition());
|
|
@@ -286,34 +289,36 @@ var TypeRenderer = class TypeRenderer extends BaseRenderer {
|
|
|
286
289
|
}
|
|
287
290
|
if (patterns.length > 0) {
|
|
288
291
|
this.write(`[key: string]: `);
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
for (const def of defs) {
|
|
295
|
-
this.writeln();
|
|
296
|
-
this.write("| ");
|
|
297
|
-
def.split("\n").forEach((l) => this.write(l.trim()));
|
|
298
|
-
}
|
|
299
|
-
this.unindent();
|
|
292
|
+
for (const prop of patterns) propsDefs.add(this.renderTypeDefString(prop.getDefinition()));
|
|
293
|
+
const defs = Array.from(propsDefs);
|
|
294
|
+
if (defs.length > 1) {
|
|
295
|
+
this.indent();
|
|
296
|
+
for (const def of defs) {
|
|
300
297
|
this.writeln();
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
this.writeln(
|
|
307
|
-
this.writeln(`static metadata: TMetadataMap<AtscriptMetadata>`);
|
|
308
|
-
this.writeln(`static validator: (opts?: Partial<TValidatorOptions>) => Validator<typeof ${asClass}>`);
|
|
309
|
-
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. */");
|
|
310
|
-
this.writeln("static toJsonSchema: () => any");
|
|
311
|
-
if (!this.opts?.exampleData) this.writeln("/** @deprecated Example Data support is disabled. To enable, set `exampleData: true` in tsPlugin options. */");
|
|
312
|
-
this.writeln("static toExampleData?: () => any");
|
|
313
|
-
if (interfaceNode && this.hasDbTable(interfaceNode)) this.renderFlat(interfaceNode);
|
|
298
|
+
this.write("| ");
|
|
299
|
+
def.split("\n").forEach((l) => this.write(l.trim()));
|
|
300
|
+
}
|
|
301
|
+
this.unindent();
|
|
302
|
+
this.writeln();
|
|
303
|
+
} else defs[0].split("\n").forEach((l) => this.writeln(l));
|
|
314
304
|
}
|
|
305
|
+
if (asClass) this.renderStaticDeclarations(asClass, interfaceNode);
|
|
315
306
|
this.pop();
|
|
316
307
|
}
|
|
308
|
+
renderStaticDeclarations(asClass, interfaceNode) {
|
|
309
|
+
this.writeln("static __is_atscript_annotated_type: true");
|
|
310
|
+
this.writeln(`static type: TAtscriptTypeObject<keyof ${asClass}, ${asClass}>`);
|
|
311
|
+
this.writeln(`static metadata: TMetadataMap<AtscriptMetadata>`);
|
|
312
|
+
this.writeln(`static validator: (opts?: Partial<TValidatorOptions>) => Validator<typeof ${asClass}>`);
|
|
313
|
+
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. */");
|
|
314
|
+
this.writeln("static toJsonSchema: () => any");
|
|
315
|
+
if (!this.opts?.exampleData) this.writeln("/** @deprecated Example Data support is disabled. To enable, set `exampleData: true` in tsPlugin options. */");
|
|
316
|
+
this.writeln("static toExampleData?: () => any");
|
|
317
|
+
if (interfaceNode && this.hasDbTable(interfaceNode)) {
|
|
318
|
+
this.renderFlat(interfaceNode);
|
|
319
|
+
this.renderPk(interfaceNode);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
317
322
|
renderInterface(node) {
|
|
318
323
|
this.writeln();
|
|
319
324
|
const exported = node.token("export")?.text === "export";
|
|
@@ -337,7 +342,7 @@ var TypeRenderer = class TypeRenderer extends BaseRenderer {
|
|
|
337
342
|
}
|
|
338
343
|
if (!firstParentProps && isStructure(fpDef)) firstParentProps = fpDef.props;
|
|
339
344
|
}
|
|
340
|
-
this.
|
|
345
|
+
this.renderStructure(resolved, node.id, node, firstParentProps);
|
|
341
346
|
} else this.writeln("{}");
|
|
342
347
|
} else {
|
|
343
348
|
this.write(`class ${node.id} `);
|
|
@@ -347,35 +352,6 @@ else this.writeln("{}");
|
|
|
347
352
|
}
|
|
348
353
|
this.writeln();
|
|
349
354
|
}
|
|
350
|
-
/**
|
|
351
|
-
* Renders a structure block, optionally filtering out props that exist in a parent.
|
|
352
|
-
*/ renderStructureFiltered(struct, asClass, filterProps, interfaceNode) {
|
|
353
|
-
if (!filterProps) return this.renderStructure(struct, asClass, interfaceNode);
|
|
354
|
-
this.blockln("{}");
|
|
355
|
-
for (const prop of Array.from(struct.props.values())) {
|
|
356
|
-
if (filterProps.has(prop.id)) continue;
|
|
357
|
-
if (prop.token("identifier")?.pattern) continue;
|
|
358
|
-
const phantomType = this.phantomPropType(prop.getDefinition());
|
|
359
|
-
if (phantomType) {
|
|
360
|
-
this.writeln(`// ${prop.id}: ${phantomType}`);
|
|
361
|
-
continue;
|
|
362
|
-
}
|
|
363
|
-
const optional = !!prop.token("optional");
|
|
364
|
-
this.write(wrapProp(prop.id), optional ? "?" : "", ": ");
|
|
365
|
-
const renderedDef = this.renderTypeDefString(prop.getDefinition());
|
|
366
|
-
renderedDef.split("\n").forEach((l) => this.writeln(l));
|
|
367
|
-
}
|
|
368
|
-
this.writeln("static __is_atscript_annotated_type: true");
|
|
369
|
-
this.writeln(`static type: TAtscriptTypeObject<keyof ${asClass}, ${asClass}>`);
|
|
370
|
-
this.writeln(`static metadata: TMetadataMap<AtscriptMetadata>`);
|
|
371
|
-
this.writeln(`static validator: (opts?: Partial<TValidatorOptions>) => Validator<typeof ${asClass}>`);
|
|
372
|
-
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. */");
|
|
373
|
-
this.writeln("static toJsonSchema: () => any");
|
|
374
|
-
if (!this.opts?.exampleData) this.writeln("/** @deprecated Example Data support is disabled. To enable, set `exampleData: true` in tsPlugin options. */");
|
|
375
|
-
this.writeln("static toExampleData?: () => any");
|
|
376
|
-
if (interfaceNode && this.hasDbTable(interfaceNode)) this.renderFlat(interfaceNode);
|
|
377
|
-
this.pop();
|
|
378
|
-
}
|
|
379
355
|
renderType(node) {
|
|
380
356
|
this.writeln();
|
|
381
357
|
const exported = node.token("export")?.text === "export";
|
|
@@ -473,6 +449,43 @@ else {
|
|
|
473
449
|
}
|
|
474
450
|
this.pop();
|
|
475
451
|
}
|
|
452
|
+
/**
|
|
453
|
+
* Renders the `static __pk` property — the primary key type for type-safe
|
|
454
|
+
* `deleteOne`/`findById` signatures on `AtscriptDbTable`.
|
|
455
|
+
*
|
|
456
|
+
* - **Single PK** (one `@meta.id`) → `static __pk: <scalar type>`
|
|
457
|
+
* - **Compound PK** (multiple `@meta.id`) → `static __pk: { field1: Type1; field2: Type2 }`
|
|
458
|
+
* - **No PK** → no `__pk` emitted
|
|
459
|
+
*/ renderPk(node) {
|
|
460
|
+
let struct;
|
|
461
|
+
if (node.hasExtends) struct = this.doc.resolveInterfaceExtends(node);
|
|
462
|
+
if (!struct) struct = node.getDefinition();
|
|
463
|
+
if (!struct || !isStructure(struct)) return;
|
|
464
|
+
const pkProps = [];
|
|
465
|
+
for (const [name, prop] of struct.props) {
|
|
466
|
+
if (prop.token("identifier")?.pattern) continue;
|
|
467
|
+
if (prop.countAnnotations("meta.id") > 0) pkProps.push({
|
|
468
|
+
name,
|
|
469
|
+
prop
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
if (pkProps.length === 0) return;
|
|
473
|
+
this.writeln();
|
|
474
|
+
if (pkProps.length === 1) {
|
|
475
|
+
this.write("static __pk: ");
|
|
476
|
+
const renderedDef = this.renderTypeDefString(pkProps[0].prop.getDefinition());
|
|
477
|
+
renderedDef.split("\n").forEach((l) => this.writeln(l));
|
|
478
|
+
} else {
|
|
479
|
+
this.write("static __pk: ");
|
|
480
|
+
this.blockln("{}");
|
|
481
|
+
for (const { name, prop } of pkProps) {
|
|
482
|
+
this.write(wrapProp(name), ": ");
|
|
483
|
+
const renderedDef = this.renderTypeDefString(prop.getDefinition());
|
|
484
|
+
renderedDef.split("\n").forEach((l) => this.writeln(l));
|
|
485
|
+
}
|
|
486
|
+
this.pop();
|
|
487
|
+
}
|
|
488
|
+
}
|
|
476
489
|
phantomPropType(def) {
|
|
477
490
|
if (!def) return undefined;
|
|
478
491
|
if (isPrimitive(def) && def.config.type === "phantom") return def.id;
|
|
@@ -521,24 +534,6 @@ function renderPrimitiveTypeDef(def) {
|
|
|
521
534
|
}
|
|
522
535
|
}
|
|
523
536
|
|
|
524
|
-
//#endregion
|
|
525
|
-
//#region packages/typescript/src/traverse.ts
|
|
526
|
-
function forAnnotatedType(def, handlers) {
|
|
527
|
-
switch (def.type.kind) {
|
|
528
|
-
case "": {
|
|
529
|
-
const typed = def;
|
|
530
|
-
if (handlers.phantom && typed.type.designType === "phantom") return handlers.phantom(typed);
|
|
531
|
-
return handlers.final(typed);
|
|
532
|
-
}
|
|
533
|
-
case "object": return handlers.object(def);
|
|
534
|
-
case "array": return handlers.array(def);
|
|
535
|
-
case "union": return handlers.union(def);
|
|
536
|
-
case "intersection": return handlers.intersection(def);
|
|
537
|
-
case "tuple": return handlers.tuple(def);
|
|
538
|
-
default: throw new Error(`Unknown type kind "${def.type.kind}"`);
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
|
|
542
537
|
//#endregion
|
|
543
538
|
//#region packages/typescript/src/validator.ts
|
|
544
539
|
function _define_property$1(obj, key, value) {
|
|
@@ -554,28 +549,35 @@ else obj[key] = value;
|
|
|
554
549
|
const regexCache = new Map();
|
|
555
550
|
var Validator = class {
|
|
556
551
|
isLimitExceeded() {
|
|
557
|
-
if (this.stackErrors.length > 0)
|
|
552
|
+
if (this.stackErrors.length > 0) {
|
|
553
|
+
const top = this.stackErrors[this.stackErrors.length - 1];
|
|
554
|
+
return top !== null && top.length >= this.opts.errorLimit;
|
|
555
|
+
}
|
|
558
556
|
return this.errors.length >= this.opts.errorLimit;
|
|
559
557
|
}
|
|
560
558
|
push(name) {
|
|
561
559
|
this.stackPath.push(name);
|
|
562
|
-
this.stackErrors.push(
|
|
560
|
+
this.stackErrors.push(null);
|
|
561
|
+
this.cachedPath = this.stackPath.length <= 1 ? "" : this.stackPath[1] + (this.stackPath.length > 2 ? "." + this.stackPath.slice(2).join(".") : "");
|
|
563
562
|
}
|
|
564
563
|
pop(saveErrors) {
|
|
565
564
|
this.stackPath.pop();
|
|
566
565
|
const popped = this.stackErrors.pop();
|
|
567
|
-
if (saveErrors && popped
|
|
568
|
-
|
|
569
|
-
});
|
|
566
|
+
if (saveErrors && popped !== null && popped !== undefined && popped.length > 0) for (const err of popped) this.error(err.message, err.path, err.details);
|
|
567
|
+
this.cachedPath = this.stackPath.length <= 1 ? "" : this.stackPath[1] + (this.stackPath.length > 2 ? "." + this.stackPath.slice(2).join(".") : "");
|
|
570
568
|
return popped;
|
|
571
569
|
}
|
|
572
570
|
clear() {
|
|
573
|
-
this.stackErrors[this.stackErrors.length - 1] =
|
|
571
|
+
this.stackErrors[this.stackErrors.length - 1] = null;
|
|
574
572
|
}
|
|
575
573
|
error(message, path$1, details) {
|
|
576
|
-
|
|
574
|
+
let errors = this.stackErrors[this.stackErrors.length - 1];
|
|
575
|
+
if (!errors) if (this.stackErrors.length > 0) {
|
|
576
|
+
errors = [];
|
|
577
|
+
this.stackErrors[this.stackErrors.length - 1] = errors;
|
|
578
|
+
} else errors = this.errors;
|
|
577
579
|
const error = {
|
|
578
|
-
path: path$1 || this.
|
|
580
|
+
path: path$1 || this.cachedPath,
|
|
579
581
|
message
|
|
580
582
|
};
|
|
581
583
|
if (details?.length) error.details = details;
|
|
@@ -595,9 +597,10 @@ var Validator = class {
|
|
|
595
597
|
* @returns `true` if the value matches the type definition.
|
|
596
598
|
* @throws {ValidatorError} When validation fails and `safe` is not `true`.
|
|
597
599
|
*/ validate(value, safe, context) {
|
|
598
|
-
this.push("");
|
|
599
600
|
this.errors = [];
|
|
600
601
|
this.stackErrors = [];
|
|
602
|
+
this.stackPath = [""];
|
|
603
|
+
this.cachedPath = "";
|
|
601
604
|
this.context = context;
|
|
602
605
|
const passed = this.validateSafe(this.def, value);
|
|
603
606
|
this.pop(!passed);
|
|
@@ -611,7 +614,7 @@ var Validator = class {
|
|
|
611
614
|
validateSafe(def, value) {
|
|
612
615
|
if (this.isLimitExceeded()) return false;
|
|
613
616
|
if (!isAnnotatedType(def)) throw new Error("Can not validate not-annotated type");
|
|
614
|
-
if (typeof this.opts.replace === "function") def = this.opts.replace(def, this.
|
|
617
|
+
if (typeof this.opts.replace === "function") def = this.opts.replace(def, this.cachedPath);
|
|
615
618
|
if (def.optional && value === undefined) return true;
|
|
616
619
|
for (const plugin of this.opts.plugins) {
|
|
617
620
|
const result = plugin(this, def, value);
|
|
@@ -620,18 +623,21 @@ var Validator = class {
|
|
|
620
623
|
return this.validateAnnotatedType(def, value);
|
|
621
624
|
}
|
|
622
625
|
get path() {
|
|
623
|
-
return this.
|
|
626
|
+
return this.cachedPath;
|
|
624
627
|
}
|
|
625
628
|
validateAnnotatedType(def, value) {
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
629
|
+
switch (def.type.kind) {
|
|
630
|
+
case "": {
|
|
631
|
+
if (def.type.designType === "phantom") return true;
|
|
632
|
+
return this.validatePrimitive(def, value);
|
|
633
|
+
}
|
|
634
|
+
case "object": return this.validateObject(def, value);
|
|
635
|
+
case "array": return this.validateArray(def, value);
|
|
636
|
+
case "union": return this.validateUnion(def, value);
|
|
637
|
+
case "intersection": return this.validateIntersection(def, value);
|
|
638
|
+
case "tuple": return this.validateTuple(def, value);
|
|
639
|
+
default: throw new Error(`Unknown type kind "${def.type.kind}"`);
|
|
640
|
+
}
|
|
635
641
|
}
|
|
636
642
|
validateUnion(def, value) {
|
|
637
643
|
let i = 0;
|
|
@@ -695,6 +701,30 @@ var Validator = class {
|
|
|
695
701
|
return false;
|
|
696
702
|
}
|
|
697
703
|
}
|
|
704
|
+
const uniqueItems = def.metadata.get("expect.array.uniqueItems");
|
|
705
|
+
if (uniqueItems) {
|
|
706
|
+
const separator = "▼↩";
|
|
707
|
+
const seen = new Set();
|
|
708
|
+
const keyProps = new Set();
|
|
709
|
+
if (def.type.of.type.kind === "object") {
|
|
710
|
+
for (const [key, val] of def.type.of.type.props.entries()) if (val.metadata.get("expect.array.key")) keyProps.add(key);
|
|
711
|
+
}
|
|
712
|
+
for (let idx = 0; idx < value.length; idx++) {
|
|
713
|
+
const item = value[idx];
|
|
714
|
+
let key;
|
|
715
|
+
if (keyProps.size > 0) {
|
|
716
|
+
key = "";
|
|
717
|
+
for (const prop of keyProps) key += JSON.stringify(item[prop]) + separator;
|
|
718
|
+
} else key = JSON.stringify(item);
|
|
719
|
+
if (seen.has(key)) {
|
|
720
|
+
this.push(String(idx));
|
|
721
|
+
this.error(uniqueItems.message || "Duplicate items are not allowed");
|
|
722
|
+
this.pop(true);
|
|
723
|
+
return false;
|
|
724
|
+
}
|
|
725
|
+
seen.add(key);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
698
728
|
let i = 0;
|
|
699
729
|
let passed = true;
|
|
700
730
|
for (const item of value) {
|
|
@@ -716,21 +746,20 @@ var Validator = class {
|
|
|
716
746
|
let passed = true;
|
|
717
747
|
const valueKeys = new Set(Object.keys(value));
|
|
718
748
|
const typeKeys = new Set();
|
|
719
|
-
|
|
749
|
+
let skipList;
|
|
720
750
|
if (this.opts.skipList) {
|
|
721
|
-
const path$1 = this.stackPath.length > 1 ? `${this.
|
|
722
|
-
this.opts.skipList.
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
});
|
|
751
|
+
const path$1 = this.stackPath.length > 1 ? `${this.cachedPath}.` : "";
|
|
752
|
+
for (const item of this.opts.skipList) if (item.startsWith(path$1)) {
|
|
753
|
+
const key = item.slice(path$1.length);
|
|
754
|
+
if (!skipList) skipList = new Set();
|
|
755
|
+
skipList.add(key);
|
|
756
|
+
valueKeys.delete(key);
|
|
757
|
+
}
|
|
729
758
|
}
|
|
730
759
|
let partialFunctionMatched = false;
|
|
731
|
-
if (typeof this.opts.partial === "function") partialFunctionMatched = this.opts.partial(def, this.
|
|
760
|
+
if (typeof this.opts.partial === "function") partialFunctionMatched = this.opts.partial(def, this.cachedPath);
|
|
732
761
|
for (const [key, item] of def.type.props.entries()) {
|
|
733
|
-
if (skipList.has(key) || isPhantomType(item)) continue;
|
|
762
|
+
if (skipList && skipList.has(key) || isPhantomType(item)) continue;
|
|
734
763
|
typeKeys.add(key);
|
|
735
764
|
if (value[key] === undefined) {
|
|
736
765
|
if (partialFunctionMatched || this.opts.partial === "deep" || this.opts.partial === true && this.stackPath.length <= 1) continue;
|
|
@@ -751,19 +780,21 @@ else {
|
|
|
751
780
|
def: propDef
|
|
752
781
|
});
|
|
753
782
|
if (matched.length > 0) {
|
|
783
|
+
this.push(key);
|
|
754
784
|
let keyPassed = false;
|
|
755
|
-
for (const { def:
|
|
756
|
-
this.
|
|
757
|
-
|
|
758
|
-
|
|
785
|
+
for (const { def: propDef } of matched) {
|
|
786
|
+
if (this.validateSafe(propDef, value[key])) {
|
|
787
|
+
keyPassed = true;
|
|
788
|
+
break;
|
|
789
|
+
}
|
|
790
|
+
this.clear();
|
|
759
791
|
}
|
|
760
792
|
if (!keyPassed) {
|
|
761
|
-
this.push(key);
|
|
762
793
|
this.validateSafe(matched[0].def, value[key]);
|
|
763
794
|
this.pop(true);
|
|
764
795
|
passed = false;
|
|
765
796
|
if (this.isLimitExceeded()) return false;
|
|
766
|
-
}
|
|
797
|
+
} else this.pop(false);
|
|
767
798
|
} else if (this.opts.unknownProps !== "ignore") {
|
|
768
799
|
if (this.opts.unknownProps === "error") {
|
|
769
800
|
this.push(key);
|
|
@@ -916,11 +947,13 @@ else {
|
|
|
916
947
|
/** Validation errors collected during the last {@link validate} call. */ _define_property$1(this, "errors", void 0);
|
|
917
948
|
_define_property$1(this, "stackErrors", void 0);
|
|
918
949
|
_define_property$1(this, "stackPath", void 0);
|
|
950
|
+
_define_property$1(this, "cachedPath", void 0);
|
|
919
951
|
_define_property$1(this, "context", void 0);
|
|
920
952
|
this.def = def;
|
|
921
953
|
this.errors = [];
|
|
922
954
|
this.stackErrors = [];
|
|
923
955
|
this.stackPath = [];
|
|
956
|
+
this.cachedPath = "";
|
|
924
957
|
this.opts = {
|
|
925
958
|
partial: false,
|
|
926
959
|
unknownProps: "error",
|
|
@@ -1064,6 +1097,24 @@ function isPhantomType(def) {
|
|
|
1064
1097
|
return def.type.kind === "" && def.type.designType === "phantom";
|
|
1065
1098
|
}
|
|
1066
1099
|
|
|
1100
|
+
//#endregion
|
|
1101
|
+
//#region packages/typescript/src/traverse.ts
|
|
1102
|
+
function forAnnotatedType(def, handlers) {
|
|
1103
|
+
switch (def.type.kind) {
|
|
1104
|
+
case "": {
|
|
1105
|
+
const typed = def;
|
|
1106
|
+
if (handlers.phantom && typed.type.designType === "phantom") return handlers.phantom(typed);
|
|
1107
|
+
return handlers.final(typed);
|
|
1108
|
+
}
|
|
1109
|
+
case "object": return handlers.object(def);
|
|
1110
|
+
case "array": return handlers.array(def);
|
|
1111
|
+
case "union": return handlers.union(def);
|
|
1112
|
+
case "intersection": return handlers.intersection(def);
|
|
1113
|
+
case "tuple": return handlers.tuple(def);
|
|
1114
|
+
default: throw new Error(`Unknown type kind "${def.type.kind}"`);
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1067
1118
|
//#endregion
|
|
1068
1119
|
//#region packages/typescript/src/json-schema.ts
|
|
1069
1120
|
/**
|
|
@@ -1236,24 +1287,25 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
1236
1287
|
this.writeln("// prettier-ignore-start");
|
|
1237
1288
|
this.writeln("/* eslint-disable */");
|
|
1238
1289
|
this.writeln("/* oxlint-disable */");
|
|
1290
|
+
let hasMutatingAnnotate = false;
|
|
1291
|
+
const nodesByName = new Map();
|
|
1292
|
+
for (const node of this.doc.nodes) {
|
|
1293
|
+
if (node.entity === "annotate" && node.isMutating) hasMutatingAnnotate = true;
|
|
1294
|
+
if (node.__typeId !== null && node.__typeId !== undefined && node.id) {
|
|
1295
|
+
const name = node.id;
|
|
1296
|
+
if (!nodesByName.has(name)) nodesByName.set(name, []);
|
|
1297
|
+
nodesByName.get(name).push(node);
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
for (const [name, nodes] of nodesByName) if (nodes.length === 1) this.typeIds.set(nodes[0], name);
|
|
1301
|
+
else for (let i = 0; i < nodes.length; i++) this.typeIds.set(nodes[i], `${name}__${i + 1}`);
|
|
1239
1302
|
const imports = ["defineAnnotatedType as $", "annotate as $a"];
|
|
1240
|
-
const hasMutatingAnnotate = this.doc.nodes.some((n) => n.entity === "annotate" && n.isMutating);
|
|
1241
1303
|
if (hasMutatingAnnotate) imports.push("cloneRefProp as $c");
|
|
1242
1304
|
const jsonSchemaMode = resolveJsonSchemaMode(this.opts);
|
|
1243
1305
|
if (jsonSchemaMode === "lazy") imports.push("buildJsonSchema as $$");
|
|
1244
1306
|
if (this.opts?.exampleData) imports.push("createDataFromAnnotatedType as $e");
|
|
1245
1307
|
if (jsonSchemaMode === false) imports.push("throwFeatureDisabled as $d");
|
|
1246
1308
|
this.writeln(`import { ${imports.join(", ")} } from "@atscript/typescript/utils"`);
|
|
1247
|
-
const nameCounts = new Map();
|
|
1248
|
-
const nodesByName = new Map();
|
|
1249
|
-
for (const node of this.doc.nodes) if (node.__typeId !== null && node.__typeId !== undefined && node.id) {
|
|
1250
|
-
const name = node.id;
|
|
1251
|
-
nameCounts.set(name, (nameCounts.get(name) || 0) + 1);
|
|
1252
|
-
if (!nodesByName.has(name)) nodesByName.set(name, []);
|
|
1253
|
-
nodesByName.get(name).push(node);
|
|
1254
|
-
}
|
|
1255
|
-
for (const [name, nodes] of nodesByName) if (nodes.length === 1) this.typeIds.set(nodes[0], name);
|
|
1256
|
-
else for (let i = 0; i < nodes.length; i++) this.typeIds.set(nodes[i], `${name}__${i + 1}`);
|
|
1257
1309
|
}
|
|
1258
1310
|
buildAdHocMap(annotateNodes) {
|
|
1259
1311
|
const map = new Map();
|
|
@@ -1262,7 +1314,8 @@ else for (let i = 0; i < nodes.length; i++) this.typeIds.set(nodes[i], `${name}_
|
|
|
1262
1314
|
const anns = entry.annotations || [];
|
|
1263
1315
|
if (anns.length > 0) {
|
|
1264
1316
|
const existing = map.get(path$1);
|
|
1265
|
-
|
|
1317
|
+
if (existing) existing.push(...anns);
|
|
1318
|
+
else map.set(path$1, [...anns]);
|
|
1266
1319
|
}
|
|
1267
1320
|
}
|
|
1268
1321
|
return map.size > 0 ? map : null;
|
|
@@ -1321,35 +1374,20 @@ else def = def.getDefinition() || def;
|
|
|
1321
1374
|
this.renderExampleDataMethod(node);
|
|
1322
1375
|
}
|
|
1323
1376
|
renderInterface(node) {
|
|
1324
|
-
this.
|
|
1325
|
-
const exported = node.token("export")?.text === "export";
|
|
1326
|
-
this.write(exported ? "export " : "");
|
|
1327
|
-
this.write(`class ${node.id} `);
|
|
1328
|
-
this.blockln("{}");
|
|
1329
|
-
this.renderClassStatics(node);
|
|
1330
|
-
this.popln();
|
|
1331
|
-
this.postAnnotate.push(node);
|
|
1332
|
-
this.writeln();
|
|
1377
|
+
this.renderDefinitionClass(node);
|
|
1333
1378
|
}
|
|
1334
1379
|
renderType(node) {
|
|
1335
|
-
this.
|
|
1336
|
-
const exported = node.token("export")?.text === "export";
|
|
1337
|
-
this.write(exported ? "export " : "");
|
|
1338
|
-
this.write(`class ${node.id} `);
|
|
1339
|
-
this.blockln("{}");
|
|
1340
|
-
this.renderClassStatics(node);
|
|
1341
|
-
this.popln();
|
|
1342
|
-
this.postAnnotate.push(node);
|
|
1343
|
-
this.writeln();
|
|
1380
|
+
this.renderDefinitionClass(node);
|
|
1344
1381
|
}
|
|
1345
1382
|
renderAnnotate(node) {
|
|
1346
1383
|
if (node.isMutating) {
|
|
1347
1384
|
this.postAnnotate.push(node);
|
|
1348
1385
|
return;
|
|
1349
1386
|
}
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1387
|
+
if (!this.doc.unwindType(node.targetName)?.def) return;
|
|
1388
|
+
this.renderDefinitionClass(node);
|
|
1389
|
+
}
|
|
1390
|
+
renderDefinitionClass(node) {
|
|
1353
1391
|
this.writeln();
|
|
1354
1392
|
const exported = node.token("export")?.text === "export";
|
|
1355
1393
|
this.write(exported ? "export " : "");
|
|
@@ -1507,6 +1545,11 @@ else handle.prop(prop.id, propHandle.$type);
|
|
|
1507
1545
|
handle.annotate(a.name, true);
|
|
1508
1546
|
break;
|
|
1509
1547
|
}
|
|
1548
|
+
case "expect.array.uniqueItems":
|
|
1549
|
+
case "expect.array.key": {
|
|
1550
|
+
handle.annotate(a.name, { message: a.args[0]?.text });
|
|
1551
|
+
break;
|
|
1552
|
+
}
|
|
1510
1553
|
default:
|
|
1511
1554
|
}
|
|
1512
1555
|
});
|
|
@@ -1574,10 +1617,7 @@ else handle.prop(prop.id, propHandle.$type);
|
|
|
1574
1617
|
this.writeln(`$("array"${name ? `, ${name}` : ""})`).indent().defineArray(node).unindent();
|
|
1575
1618
|
return this;
|
|
1576
1619
|
}
|
|
1577
|
-
default:
|
|
1578
|
-
console.log("!!!!!!! UNKNOWN", node.entity);
|
|
1579
|
-
return this;
|
|
1580
|
-
}
|
|
1620
|
+
default: return this;
|
|
1581
1621
|
}
|
|
1582
1622
|
}
|
|
1583
1623
|
defineConst(node) {
|
|
@@ -1789,40 +1829,25 @@ else targetValue = "true";
|
|
|
1789
1829
|
this.writeln(`$c(${clone.parentPath}, "${escapeQuotes(clone.propName)}")`);
|
|
1790
1830
|
}
|
|
1791
1831
|
}
|
|
1792
|
-
for (const { entry, accessors } of entryAccessors)
|
|
1793
|
-
const anns = entry.annotations;
|
|
1794
|
-
for (const accessor of accessors) {
|
|
1795
|
-
const cleared = new Set();
|
|
1796
|
-
for (const an of anns) {
|
|
1797
|
-
const { value, multiple } = this.computeAnnotationValue(entry, an);
|
|
1798
|
-
if (multiple) {
|
|
1799
|
-
if (!cleared.has(an.name)) {
|
|
1800
|
-
const spec = this.doc.resolveAnnotation(an.name);
|
|
1801
|
-
if (!spec || spec.config.mergeStrategy !== "append") this.writeln(`${accessor}.metadata.delete("${escapeQuotes(an.name)}")`);
|
|
1802
|
-
cleared.add(an.name);
|
|
1803
|
-
}
|
|
1804
|
-
this.writeln(`$a(${accessor}.metadata, "${escapeQuotes(an.name)}", ${value}, true)`);
|
|
1805
|
-
} else this.writeln(`$a(${accessor}.metadata, "${escapeQuotes(an.name)}", ${value})`);
|
|
1806
|
-
}
|
|
1807
|
-
}
|
|
1808
|
-
}
|
|
1832
|
+
for (const { entry, accessors } of entryAccessors) for (const accessor of accessors) this.emitMutatingAnnotations(entry, entry.annotations, accessor);
|
|
1809
1833
|
const topAnnotations = node.annotations;
|
|
1810
|
-
if (topAnnotations && topAnnotations.length > 0)
|
|
1811
|
-
const cleared = new Set();
|
|
1812
|
-
for (const an of topAnnotations) {
|
|
1813
|
-
const { value, multiple } = this.computeAnnotationValue(node, an);
|
|
1814
|
-
if (multiple) {
|
|
1815
|
-
if (!cleared.has(an.name)) {
|
|
1816
|
-
const spec = this.doc.resolveAnnotation(an.name);
|
|
1817
|
-
if (!spec || spec.config.mergeStrategy !== "append") this.writeln(`${targetName}.metadata.delete("${escapeQuotes(an.name)}")`);
|
|
1818
|
-
cleared.add(an.name);
|
|
1819
|
-
}
|
|
1820
|
-
this.writeln(`$a(${targetName}.metadata, "${escapeQuotes(an.name)}", ${value}, true)`);
|
|
1821
|
-
} else this.writeln(`$a(${targetName}.metadata, "${escapeQuotes(an.name)}", ${value})`);
|
|
1822
|
-
}
|
|
1823
|
-
}
|
|
1834
|
+
if (topAnnotations && topAnnotations.length > 0) this.emitMutatingAnnotations(node, topAnnotations, targetName);
|
|
1824
1835
|
this.writeln();
|
|
1825
1836
|
}
|
|
1837
|
+
emitMutatingAnnotations(node, annotations, accessor) {
|
|
1838
|
+
const cleared = new Set();
|
|
1839
|
+
for (const an of annotations) {
|
|
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(`${accessor}.metadata.delete("${escapeQuotes(an.name)}")`);
|
|
1845
|
+
cleared.add(an.name);
|
|
1846
|
+
}
|
|
1847
|
+
this.writeln(`$a(${accessor}.metadata, "${escapeQuotes(an.name)}", ${value}, true)`);
|
|
1848
|
+
} else this.writeln(`$a(${accessor}.metadata, "${escapeQuotes(an.name)}", ${value})`);
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1826
1851
|
resolveTargetDef(targetName) {
|
|
1827
1852
|
const unwound = this.doc.unwindType(targetName);
|
|
1828
1853
|
if (!unwound?.def) return undefined;
|