@atscript/typescript 0.1.31 → 0.1.33
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 +301 -236
- package/dist/index.cjs +301 -236
- package/dist/index.mjs +301 -236
- package/dist/utils.cjs +160 -143
- package/dist/utils.d.ts +28 -8
- package/dist/utils.mjs +160 -144
- 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,88 @@ 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 (unless unique indexes exist)
|
|
486
|
+
* - **Unique indexes** (`@db.index.unique`) → appended as union members
|
|
487
|
+
* - **Mongo collection** → always includes `string` (ObjectId) in the union;
|
|
488
|
+
* if no `@meta.id` fields, `__pk` is just `string`
|
|
489
|
+
*/ renderPk(node) {
|
|
490
|
+
const isMongoCollection = !!node.annotations?.some((a) => a.name === "db.mongo.collection");
|
|
491
|
+
let struct;
|
|
492
|
+
if (node.hasExtends) struct = this.doc.resolveInterfaceExtends(node);
|
|
493
|
+
if (!struct) struct = node.getDefinition();
|
|
494
|
+
if (!struct || !(0, __atscript_core.isStructure)(struct)) return;
|
|
495
|
+
const structNode = struct;
|
|
496
|
+
const pkProps = [];
|
|
497
|
+
const uniqueByIndex = new Map();
|
|
498
|
+
for (const [name, prop] of structNode.props) {
|
|
499
|
+
if (prop.token("identifier")?.pattern) continue;
|
|
500
|
+
if (isMongoCollection && name === "_id") continue;
|
|
501
|
+
if (prop.countAnnotations("meta.id") > 0) pkProps.push({
|
|
502
|
+
name,
|
|
503
|
+
prop
|
|
504
|
+
});
|
|
505
|
+
if (prop.annotations) {
|
|
506
|
+
for (const ann of prop.annotations) if (ann.name === "db.index.unique") {
|
|
507
|
+
const indexName = ann.args[0]?.text ?? name;
|
|
508
|
+
let group = uniqueByIndex.get(indexName);
|
|
509
|
+
if (!group) {
|
|
510
|
+
group = [];
|
|
511
|
+
uniqueByIndex.set(indexName, group);
|
|
512
|
+
}
|
|
513
|
+
group.push({
|
|
514
|
+
name,
|
|
515
|
+
prop
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
const uniqueProps = [];
|
|
521
|
+
const pkNames = new Set(pkProps.map((p) => p.name));
|
|
522
|
+
for (const fields of uniqueByIndex.values()) if (fields.length === 1 && !pkNames.has(fields[0].name)) uniqueProps.push(fields[0]);
|
|
523
|
+
if (pkProps.length === 0 && uniqueProps.length === 0 && !isMongoCollection) return;
|
|
524
|
+
let mongoIdType;
|
|
525
|
+
if (isMongoCollection) {
|
|
526
|
+
const idProp = structNode.props.get("_id");
|
|
527
|
+
if (idProp) mongoIdType = this.renderTypeDefString(idProp.getDefinition()).trim();
|
|
528
|
+
mongoIdType ?? (mongoIdType = "string");
|
|
529
|
+
}
|
|
530
|
+
const uniqueTypes = [];
|
|
531
|
+
const seenTypes = new Set();
|
|
532
|
+
for (const { prop } of uniqueProps) {
|
|
533
|
+
const rendered = this.renderTypeDefString(prop.getDefinition()).trim();
|
|
534
|
+
if (!seenTypes.has(rendered)) {
|
|
535
|
+
seenTypes.add(rendered);
|
|
536
|
+
uniqueTypes.push(rendered);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
this.writeln();
|
|
540
|
+
const uniqueSuffix = uniqueTypes.length > 0 ? ` | ${uniqueTypes.join(" | ")}` : "";
|
|
541
|
+
if (pkProps.length === 0 && !isMongoCollection) this.writeln(`static __pk: ${uniqueTypes.join(" | ")}`);
|
|
542
|
+
else if (pkProps.length === 0) this.writeln(`static __pk: ${mongoIdType}${uniqueSuffix}`);
|
|
543
|
+
else if (pkProps.length === 1) {
|
|
544
|
+
this.write("static __pk: ");
|
|
545
|
+
if (isMongoCollection) this.write(`${mongoIdType} | `);
|
|
546
|
+
const renderedDef = this.renderTypeDefString(pkProps[0].prop.getDefinition()).trim();
|
|
547
|
+
this.writeln(`${renderedDef}${uniqueSuffix}`);
|
|
548
|
+
} else {
|
|
549
|
+
this.write("static __pk: ");
|
|
550
|
+
if (isMongoCollection) this.write(`${mongoIdType} | `);
|
|
551
|
+
this.blockln("{}");
|
|
552
|
+
for (const { name, prop } of pkProps) {
|
|
553
|
+
this.write(wrapProp(name), ": ");
|
|
554
|
+
const renderedDef = this.renderTypeDefString(prop.getDefinition());
|
|
555
|
+
renderedDef.split("\n").forEach((l) => this.writeln(l));
|
|
556
|
+
}
|
|
557
|
+
this.pop();
|
|
558
|
+
this.writeln(uniqueSuffix);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
503
561
|
phantomPropType(def) {
|
|
504
562
|
if (!def) return undefined;
|
|
505
563
|
if ((0, __atscript_core.isPrimitive)(def) && def.config.type === "phantom") return def.id;
|
|
@@ -548,24 +606,6 @@ function renderPrimitiveTypeDef(def) {
|
|
|
548
606
|
}
|
|
549
607
|
}
|
|
550
608
|
|
|
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
609
|
//#endregion
|
|
570
610
|
//#region packages/typescript/src/validator.ts
|
|
571
611
|
function _define_property$2(obj, key, value) {
|
|
@@ -581,28 +621,35 @@ else obj[key] = value;
|
|
|
581
621
|
const regexCache = new Map();
|
|
582
622
|
var Validator = class {
|
|
583
623
|
isLimitExceeded() {
|
|
584
|
-
if (this.stackErrors.length > 0)
|
|
624
|
+
if (this.stackErrors.length > 0) {
|
|
625
|
+
const top = this.stackErrors[this.stackErrors.length - 1];
|
|
626
|
+
return top !== null && top.length >= this.opts.errorLimit;
|
|
627
|
+
}
|
|
585
628
|
return this.errors.length >= this.opts.errorLimit;
|
|
586
629
|
}
|
|
587
630
|
push(name) {
|
|
588
631
|
this.stackPath.push(name);
|
|
589
|
-
this.stackErrors.push(
|
|
632
|
+
this.stackErrors.push(null);
|
|
633
|
+
this.cachedPath = this.stackPath.length <= 1 ? "" : this.stackPath[1] + (this.stackPath.length > 2 ? "." + this.stackPath.slice(2).join(".") : "");
|
|
590
634
|
}
|
|
591
635
|
pop(saveErrors) {
|
|
592
636
|
this.stackPath.pop();
|
|
593
637
|
const popped = this.stackErrors.pop();
|
|
594
|
-
if (saveErrors && popped
|
|
595
|
-
|
|
596
|
-
});
|
|
638
|
+
if (saveErrors && popped !== null && popped !== undefined && popped.length > 0) for (const err of popped) this.error(err.message, err.path, err.details);
|
|
639
|
+
this.cachedPath = this.stackPath.length <= 1 ? "" : this.stackPath[1] + (this.stackPath.length > 2 ? "." + this.stackPath.slice(2).join(".") : "");
|
|
597
640
|
return popped;
|
|
598
641
|
}
|
|
599
642
|
clear() {
|
|
600
|
-
this.stackErrors[this.stackErrors.length - 1] =
|
|
643
|
+
this.stackErrors[this.stackErrors.length - 1] = null;
|
|
601
644
|
}
|
|
602
645
|
error(message, path$3, details) {
|
|
603
|
-
|
|
646
|
+
let errors = this.stackErrors[this.stackErrors.length - 1];
|
|
647
|
+
if (!errors) if (this.stackErrors.length > 0) {
|
|
648
|
+
errors = [];
|
|
649
|
+
this.stackErrors[this.stackErrors.length - 1] = errors;
|
|
650
|
+
} else errors = this.errors;
|
|
604
651
|
const error = {
|
|
605
|
-
path: path$3 || this.
|
|
652
|
+
path: path$3 || this.cachedPath,
|
|
606
653
|
message
|
|
607
654
|
};
|
|
608
655
|
if (details?.length) error.details = details;
|
|
@@ -622,9 +669,10 @@ var Validator = class {
|
|
|
622
669
|
* @returns `true` if the value matches the type definition.
|
|
623
670
|
* @throws {ValidatorError} When validation fails and `safe` is not `true`.
|
|
624
671
|
*/ validate(value, safe, context) {
|
|
625
|
-
this.push("");
|
|
626
672
|
this.errors = [];
|
|
627
673
|
this.stackErrors = [];
|
|
674
|
+
this.stackPath = [""];
|
|
675
|
+
this.cachedPath = "";
|
|
628
676
|
this.context = context;
|
|
629
677
|
const passed = this.validateSafe(this.def, value);
|
|
630
678
|
this.pop(!passed);
|
|
@@ -638,7 +686,7 @@ var Validator = class {
|
|
|
638
686
|
validateSafe(def, value) {
|
|
639
687
|
if (this.isLimitExceeded()) return false;
|
|
640
688
|
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.
|
|
689
|
+
if (typeof this.opts.replace === "function") def = this.opts.replace(def, this.cachedPath);
|
|
642
690
|
if (def.optional && value === undefined) return true;
|
|
643
691
|
for (const plugin of this.opts.plugins) {
|
|
644
692
|
const result = plugin(this, def, value);
|
|
@@ -647,18 +695,21 @@ var Validator = class {
|
|
|
647
695
|
return this.validateAnnotatedType(def, value);
|
|
648
696
|
}
|
|
649
697
|
get path() {
|
|
650
|
-
return this.
|
|
698
|
+
return this.cachedPath;
|
|
651
699
|
}
|
|
652
700
|
validateAnnotatedType(def, value) {
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
701
|
+
switch (def.type.kind) {
|
|
702
|
+
case "": {
|
|
703
|
+
if (def.type.designType === "phantom") return true;
|
|
704
|
+
return this.validatePrimitive(def, value);
|
|
705
|
+
}
|
|
706
|
+
case "object": return this.validateObject(def, value);
|
|
707
|
+
case "array": return this.validateArray(def, value);
|
|
708
|
+
case "union": return this.validateUnion(def, value);
|
|
709
|
+
case "intersection": return this.validateIntersection(def, value);
|
|
710
|
+
case "tuple": return this.validateTuple(def, value);
|
|
711
|
+
default: throw new Error(`Unknown type kind "${def.type.kind}"`);
|
|
712
|
+
}
|
|
662
713
|
}
|
|
663
714
|
validateUnion(def, value) {
|
|
664
715
|
let i = 0;
|
|
@@ -722,6 +773,30 @@ var Validator = class {
|
|
|
722
773
|
return false;
|
|
723
774
|
}
|
|
724
775
|
}
|
|
776
|
+
const uniqueItems = def.metadata.get("expect.array.uniqueItems");
|
|
777
|
+
if (uniqueItems) {
|
|
778
|
+
const separator = "▼↩";
|
|
779
|
+
const seen = new Set();
|
|
780
|
+
const keyProps = new Set();
|
|
781
|
+
if (def.type.of.type.kind === "object") {
|
|
782
|
+
for (const [key, val] of def.type.of.type.props.entries()) if (val.metadata.get("expect.array.key")) keyProps.add(key);
|
|
783
|
+
}
|
|
784
|
+
for (let idx = 0; idx < value.length; idx++) {
|
|
785
|
+
const item = value[idx];
|
|
786
|
+
let key;
|
|
787
|
+
if (keyProps.size > 0) {
|
|
788
|
+
key = "";
|
|
789
|
+
for (const prop of keyProps) key += JSON.stringify(item[prop]) + separator;
|
|
790
|
+
} else key = JSON.stringify(item);
|
|
791
|
+
if (seen.has(key)) {
|
|
792
|
+
this.push(String(idx));
|
|
793
|
+
this.error(uniqueItems.message || "Duplicate items are not allowed");
|
|
794
|
+
this.pop(true);
|
|
795
|
+
return false;
|
|
796
|
+
}
|
|
797
|
+
seen.add(key);
|
|
798
|
+
}
|
|
799
|
+
}
|
|
725
800
|
let i = 0;
|
|
726
801
|
let passed = true;
|
|
727
802
|
for (const item of value) {
|
|
@@ -743,21 +818,20 @@ var Validator = class {
|
|
|
743
818
|
let passed = true;
|
|
744
819
|
const valueKeys = new Set(Object.keys(value));
|
|
745
820
|
const typeKeys = new Set();
|
|
746
|
-
|
|
821
|
+
let skipList;
|
|
747
822
|
if (this.opts.skipList) {
|
|
748
|
-
const path$3 = this.stackPath.length > 1 ? `${this.
|
|
749
|
-
this.opts.skipList.
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
});
|
|
823
|
+
const path$3 = this.stackPath.length > 1 ? `${this.cachedPath}.` : "";
|
|
824
|
+
for (const item of this.opts.skipList) if (item.startsWith(path$3)) {
|
|
825
|
+
const key = item.slice(path$3.length);
|
|
826
|
+
if (!skipList) skipList = new Set();
|
|
827
|
+
skipList.add(key);
|
|
828
|
+
valueKeys.delete(key);
|
|
829
|
+
}
|
|
756
830
|
}
|
|
757
831
|
let partialFunctionMatched = false;
|
|
758
|
-
if (typeof this.opts.partial === "function") partialFunctionMatched = this.opts.partial(def, this.
|
|
832
|
+
if (typeof this.opts.partial === "function") partialFunctionMatched = this.opts.partial(def, this.cachedPath);
|
|
759
833
|
for (const [key, item] of def.type.props.entries()) {
|
|
760
|
-
if (skipList.has(key) || isPhantomType(item)) continue;
|
|
834
|
+
if (skipList && skipList.has(key) || isPhantomType(item)) continue;
|
|
761
835
|
typeKeys.add(key);
|
|
762
836
|
if (value[key] === undefined) {
|
|
763
837
|
if (partialFunctionMatched || this.opts.partial === "deep" || this.opts.partial === true && this.stackPath.length <= 1) continue;
|
|
@@ -778,19 +852,21 @@ else {
|
|
|
778
852
|
def: propDef
|
|
779
853
|
});
|
|
780
854
|
if (matched.length > 0) {
|
|
855
|
+
this.push(key);
|
|
781
856
|
let keyPassed = false;
|
|
782
|
-
for (const { def:
|
|
783
|
-
this.
|
|
784
|
-
|
|
785
|
-
|
|
857
|
+
for (const { def: propDef } of matched) {
|
|
858
|
+
if (this.validateSafe(propDef, value[key])) {
|
|
859
|
+
keyPassed = true;
|
|
860
|
+
break;
|
|
861
|
+
}
|
|
862
|
+
this.clear();
|
|
786
863
|
}
|
|
787
864
|
if (!keyPassed) {
|
|
788
|
-
this.push(key);
|
|
789
865
|
this.validateSafe(matched[0].def, value[key]);
|
|
790
866
|
this.pop(true);
|
|
791
867
|
passed = false;
|
|
792
868
|
if (this.isLimitExceeded()) return false;
|
|
793
|
-
}
|
|
869
|
+
} else this.pop(false);
|
|
794
870
|
} else if (this.opts.unknownProps !== "ignore") {
|
|
795
871
|
if (this.opts.unknownProps === "error") {
|
|
796
872
|
this.push(key);
|
|
@@ -943,11 +1019,13 @@ else {
|
|
|
943
1019
|
/** Validation errors collected during the last {@link validate} call. */ _define_property$2(this, "errors", void 0);
|
|
944
1020
|
_define_property$2(this, "stackErrors", void 0);
|
|
945
1021
|
_define_property$2(this, "stackPath", void 0);
|
|
1022
|
+
_define_property$2(this, "cachedPath", void 0);
|
|
946
1023
|
_define_property$2(this, "context", void 0);
|
|
947
1024
|
this.def = def;
|
|
948
1025
|
this.errors = [];
|
|
949
1026
|
this.stackErrors = [];
|
|
950
1027
|
this.stackPath = [];
|
|
1028
|
+
this.cachedPath = "";
|
|
951
1029
|
this.opts = {
|
|
952
1030
|
partial: false,
|
|
953
1031
|
unknownProps: "error",
|
|
@@ -965,6 +1043,24 @@ var ValidatorError = class extends Error {
|
|
|
965
1043
|
|
|
966
1044
|
//#endregion
|
|
967
1045
|
//#region packages/typescript/src/annotated-type.ts
|
|
1046
|
+
const COMPLEX_KINDS = new Set([
|
|
1047
|
+
"union",
|
|
1048
|
+
"intersection",
|
|
1049
|
+
"tuple"
|
|
1050
|
+
]);
|
|
1051
|
+
/** Shared validator method reused by all annotated type nodes. */ function validatorMethod(opts) {
|
|
1052
|
+
return new Validator(this, opts);
|
|
1053
|
+
}
|
|
1054
|
+
function createAnnotatedTypeNode(type, metadata, opts) {
|
|
1055
|
+
return {
|
|
1056
|
+
__is_atscript_annotated_type: true,
|
|
1057
|
+
type,
|
|
1058
|
+
metadata,
|
|
1059
|
+
validator: validatorMethod,
|
|
1060
|
+
id: opts?.id,
|
|
1061
|
+
optional: opts?.optional
|
|
1062
|
+
};
|
|
1063
|
+
}
|
|
968
1064
|
function isAnnotatedType(type) {
|
|
969
1065
|
return type && type.__is_atscript_annotated_type;
|
|
970
1066
|
}
|
|
@@ -981,38 +1077,24 @@ function defineAnnotatedType(_kind, base) {
|
|
|
981
1077
|
const kind = _kind || "";
|
|
982
1078
|
const type = base?.type || {};
|
|
983
1079
|
type.kind = kind;
|
|
984
|
-
if ([
|
|
985
|
-
"union",
|
|
986
|
-
"intersection",
|
|
987
|
-
"tuple"
|
|
988
|
-
].includes(kind)) type.items = [];
|
|
1080
|
+
if (COMPLEX_KINDS.has(kind)) type.items = [];
|
|
989
1081
|
if (kind === "object") {
|
|
990
1082
|
type.props = new Map();
|
|
991
1083
|
type.propsPatterns = [];
|
|
992
1084
|
}
|
|
993
1085
|
type.tags = new Set();
|
|
994
1086
|
const metadata = base?.metadata || new Map();
|
|
995
|
-
|
|
996
|
-
__is_atscript_annotated_type: true,
|
|
997
|
-
metadata,
|
|
998
|
-
type,
|
|
999
|
-
validator(opts) {
|
|
1000
|
-
return new Validator(this, opts);
|
|
1001
|
-
}
|
|
1002
|
-
});
|
|
1003
|
-
else base = {
|
|
1087
|
+
const payload = {
|
|
1004
1088
|
__is_atscript_annotated_type: true,
|
|
1005
1089
|
metadata,
|
|
1006
1090
|
type,
|
|
1007
|
-
validator
|
|
1008
|
-
return new Validator(this, opts);
|
|
1009
|
-
}
|
|
1091
|
+
validator: validatorMethod
|
|
1010
1092
|
};
|
|
1093
|
+
base = base ? Object.assign(base, payload) : payload;
|
|
1011
1094
|
const handle = {
|
|
1012
1095
|
$type: base,
|
|
1013
1096
|
$def: type,
|
|
1014
1097
|
$metadata: metadata,
|
|
1015
|
-
_existingObject: undefined,
|
|
1016
1098
|
tags(...tags) {
|
|
1017
1099
|
for (const tag of tags) this.$def.tags.add(tag);
|
|
1018
1100
|
return this;
|
|
@@ -1053,27 +1135,18 @@ else base = {
|
|
|
1053
1135
|
return this;
|
|
1054
1136
|
},
|
|
1055
1137
|
refTo(type$1, chain) {
|
|
1138
|
+
if (!isAnnotatedType(type$1)) throw new Error(`${type$1} is not annotated type`);
|
|
1056
1139
|
let newBase = type$1;
|
|
1057
1140
|
const typeName = type$1.name || "Unknown";
|
|
1058
|
-
if (
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1141
|
+
if (chain) for (let i = 0; i < chain.length; i++) {
|
|
1142
|
+
const c = chain[i];
|
|
1143
|
+
if (newBase.type.kind === "object" && newBase.type.props.has(c)) newBase = newBase.type.props.get(c);
|
|
1144
|
+
else {
|
|
1145
|
+
const keys = chain.slice(0, i + 1).map((k) => `["${k}"]`).join("");
|
|
1146
|
+
throw new Error(`Can't find prop ${typeName}${keys}`);
|
|
1064
1147
|
}
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
this.$type = {
|
|
1068
|
-
__is_atscript_annotated_type: true,
|
|
1069
|
-
type: newBase.type,
|
|
1070
|
-
metadata,
|
|
1071
|
-
id: newBase.id,
|
|
1072
|
-
validator(opts) {
|
|
1073
|
-
return new Validator(this, opts);
|
|
1074
|
-
}
|
|
1075
|
-
};
|
|
1076
|
-
} else throw new Error(`${type$1} is not annotated type`);
|
|
1148
|
+
}
|
|
1149
|
+
this.$type = createAnnotatedTypeNode(newBase.type, metadata, { id: newBase.id });
|
|
1077
1150
|
return this;
|
|
1078
1151
|
},
|
|
1079
1152
|
annotate(key, value, asArray) {
|
|
@@ -1091,6 +1164,24 @@ function isPhantomType(def) {
|
|
|
1091
1164
|
return def.type.kind === "" && def.type.designType === "phantom";
|
|
1092
1165
|
}
|
|
1093
1166
|
|
|
1167
|
+
//#endregion
|
|
1168
|
+
//#region packages/typescript/src/traverse.ts
|
|
1169
|
+
function forAnnotatedType(def, handlers) {
|
|
1170
|
+
switch (def.type.kind) {
|
|
1171
|
+
case "": {
|
|
1172
|
+
const typed = def;
|
|
1173
|
+
if (handlers.phantom && typed.type.designType === "phantom") return handlers.phantom(typed);
|
|
1174
|
+
return handlers.final(typed);
|
|
1175
|
+
}
|
|
1176
|
+
case "object": return handlers.object(def);
|
|
1177
|
+
case "array": return handlers.array(def);
|
|
1178
|
+
case "union": return handlers.union(def);
|
|
1179
|
+
case "intersection": return handlers.intersection(def);
|
|
1180
|
+
case "tuple": return handlers.tuple(def);
|
|
1181
|
+
default: throw new Error(`Unknown type kind "${def.type.kind}"`);
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1094
1185
|
//#endregion
|
|
1095
1186
|
//#region packages/typescript/src/json-schema.ts
|
|
1096
1187
|
/**
|
|
@@ -1105,10 +1196,10 @@ function isPhantomType(def) {
|
|
|
1105
1196
|
const firstObj = items[0].type;
|
|
1106
1197
|
const candidates = [];
|
|
1107
1198
|
for (const [propName, propType] of firstObj.props.entries()) if (propType.type.kind === "" && propType.type.value !== undefined) candidates.push(propName);
|
|
1108
|
-
|
|
1199
|
+
let result = null;
|
|
1109
1200
|
for (const candidate of candidates) {
|
|
1110
1201
|
const values = new Set();
|
|
1111
|
-
const
|
|
1202
|
+
const indexMapping = {};
|
|
1112
1203
|
let valid = true;
|
|
1113
1204
|
for (let i = 0; i < items.length; i++) {
|
|
1114
1205
|
const obj = items[i].type;
|
|
@@ -1123,19 +1214,21 @@ function isPhantomType(def) {
|
|
|
1123
1214
|
break;
|
|
1124
1215
|
}
|
|
1125
1216
|
values.add(val);
|
|
1126
|
-
|
|
1217
|
+
indexMapping[String(val)] = i;
|
|
1218
|
+
}
|
|
1219
|
+
if (valid) {
|
|
1220
|
+
if (result) return null;
|
|
1221
|
+
result = {
|
|
1222
|
+
propertyName: candidate,
|
|
1223
|
+
indexMapping
|
|
1224
|
+
};
|
|
1127
1225
|
}
|
|
1128
|
-
if (valid) validCandidates.push({
|
|
1129
|
-
propertyName: candidate,
|
|
1130
|
-
mapping
|
|
1131
|
-
});
|
|
1132
1226
|
}
|
|
1133
|
-
|
|
1134
|
-
return null;
|
|
1227
|
+
return result;
|
|
1135
1228
|
}
|
|
1136
1229
|
function buildJsonSchema(type) {
|
|
1137
1230
|
const defs = {};
|
|
1138
|
-
let
|
|
1231
|
+
let hasDefs = false;
|
|
1139
1232
|
const buildObject = (d) => {
|
|
1140
1233
|
const properties = {};
|
|
1141
1234
|
const required = [];
|
|
@@ -1152,15 +1245,15 @@ function buildJsonSchema(type) {
|
|
|
1152
1245
|
return schema$1;
|
|
1153
1246
|
};
|
|
1154
1247
|
const build$1 = (def) => {
|
|
1155
|
-
if (def.id && def.type.kind === "object" &&
|
|
1248
|
+
if (def.id && def.type.kind === "object" && def !== type) {
|
|
1156
1249
|
const name = def.id;
|
|
1157
1250
|
if (!defs[name]) {
|
|
1251
|
+
hasDefs = true;
|
|
1158
1252
|
defs[name] = {};
|
|
1159
1253
|
defs[name] = buildObject(def);
|
|
1160
1254
|
}
|
|
1161
1255
|
return { $ref: `#/$defs/${name}` };
|
|
1162
1256
|
}
|
|
1163
|
-
isRoot = false;
|
|
1164
1257
|
const meta = def.metadata;
|
|
1165
1258
|
return forAnnotatedType(def, {
|
|
1166
1259
|
phantom() {
|
|
@@ -1185,11 +1278,9 @@ function buildJsonSchema(type) {
|
|
|
1185
1278
|
if (disc) {
|
|
1186
1279
|
const oneOf = d.type.items.map(build$1);
|
|
1187
1280
|
const mapping = {};
|
|
1188
|
-
for (const [val,
|
|
1189
|
-
const idx = Number.parseInt(origPath.split("/").pop(), 10);
|
|
1281
|
+
for (const [val, idx] of Object.entries(disc.indexMapping)) {
|
|
1190
1282
|
const item = d.type.items[idx];
|
|
1191
|
-
|
|
1192
|
-
else mapping[val] = origPath;
|
|
1283
|
+
mapping[val] = item.id && defs[item.id] ? `#/$defs/${item.id}` : `#/oneOf/${idx}`;
|
|
1193
1284
|
}
|
|
1194
1285
|
return {
|
|
1195
1286
|
oneOf,
|
|
@@ -1239,7 +1330,7 @@ else schema$1.allOf = (schema$1.allOf || []).concat(patterns.map((p) => ({ patte
|
|
|
1239
1330
|
});
|
|
1240
1331
|
};
|
|
1241
1332
|
const schema = build$1(type);
|
|
1242
|
-
if (
|
|
1333
|
+
if (hasDefs) return {
|
|
1243
1334
|
...schema,
|
|
1244
1335
|
$defs: defs
|
|
1245
1336
|
};
|
|
@@ -1263,24 +1354,25 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
1263
1354
|
this.writeln("// prettier-ignore-start");
|
|
1264
1355
|
this.writeln("/* eslint-disable */");
|
|
1265
1356
|
this.writeln("/* oxlint-disable */");
|
|
1357
|
+
let hasMutatingAnnotate = false;
|
|
1358
|
+
const nodesByName = new Map();
|
|
1359
|
+
for (const node of this.doc.nodes) {
|
|
1360
|
+
if (node.entity === "annotate" && node.isMutating) hasMutatingAnnotate = true;
|
|
1361
|
+
if (node.__typeId !== null && node.__typeId !== undefined && node.id) {
|
|
1362
|
+
const name = node.id;
|
|
1363
|
+
if (!nodesByName.has(name)) nodesByName.set(name, []);
|
|
1364
|
+
nodesByName.get(name).push(node);
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
for (const [name, nodes] of nodesByName) if (nodes.length === 1) this.typeIds.set(nodes[0], name);
|
|
1368
|
+
else for (let i = 0; i < nodes.length; i++) this.typeIds.set(nodes[i], `${name}__${i + 1}`);
|
|
1266
1369
|
const imports = ["defineAnnotatedType as $", "annotate as $a"];
|
|
1267
|
-
const hasMutatingAnnotate = this.doc.nodes.some((n) => n.entity === "annotate" && n.isMutating);
|
|
1268
1370
|
if (hasMutatingAnnotate) imports.push("cloneRefProp as $c");
|
|
1269
1371
|
const jsonSchemaMode = resolveJsonSchemaMode(this.opts);
|
|
1270
1372
|
if (jsonSchemaMode === "lazy") imports.push("buildJsonSchema as $$");
|
|
1271
1373
|
if (this.opts?.exampleData) imports.push("createDataFromAnnotatedType as $e");
|
|
1272
1374
|
if (jsonSchemaMode === false) imports.push("throwFeatureDisabled as $d");
|
|
1273
1375
|
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
1376
|
}
|
|
1285
1377
|
buildAdHocMap(annotateNodes) {
|
|
1286
1378
|
const map = new Map();
|
|
@@ -1289,7 +1381,8 @@ else for (let i = 0; i < nodes.length; i++) this.typeIds.set(nodes[i], `${name}_
|
|
|
1289
1381
|
const anns = entry.annotations || [];
|
|
1290
1382
|
if (anns.length > 0) {
|
|
1291
1383
|
const existing = map.get(path$3);
|
|
1292
|
-
|
|
1384
|
+
if (existing) existing.push(...anns);
|
|
1385
|
+
else map.set(path$3, [...anns]);
|
|
1293
1386
|
}
|
|
1294
1387
|
}
|
|
1295
1388
|
return map.size > 0 ? map : null;
|
|
@@ -1348,35 +1441,20 @@ else def = def.getDefinition() || def;
|
|
|
1348
1441
|
this.renderExampleDataMethod(node);
|
|
1349
1442
|
}
|
|
1350
1443
|
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();
|
|
1444
|
+
this.renderDefinitionClass(node);
|
|
1360
1445
|
}
|
|
1361
1446
|
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();
|
|
1447
|
+
this.renderDefinitionClass(node);
|
|
1371
1448
|
}
|
|
1372
1449
|
renderAnnotate(node) {
|
|
1373
1450
|
if (node.isMutating) {
|
|
1374
1451
|
this.postAnnotate.push(node);
|
|
1375
1452
|
return;
|
|
1376
1453
|
}
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1454
|
+
if (!this.doc.unwindType(node.targetName)?.def) return;
|
|
1455
|
+
this.renderDefinitionClass(node);
|
|
1456
|
+
}
|
|
1457
|
+
renderDefinitionClass(node) {
|
|
1380
1458
|
this.writeln();
|
|
1381
1459
|
const exported = node.token("export")?.text === "export";
|
|
1382
1460
|
this.write(exported ? "export " : "");
|
|
@@ -1534,6 +1612,11 @@ else handle.prop(prop.id, propHandle.$type);
|
|
|
1534
1612
|
handle.annotate(a.name, true);
|
|
1535
1613
|
break;
|
|
1536
1614
|
}
|
|
1615
|
+
case "expect.array.uniqueItems":
|
|
1616
|
+
case "expect.array.key": {
|
|
1617
|
+
handle.annotate(a.name, { message: a.args[0]?.text });
|
|
1618
|
+
break;
|
|
1619
|
+
}
|
|
1537
1620
|
default:
|
|
1538
1621
|
}
|
|
1539
1622
|
});
|
|
@@ -1601,10 +1684,7 @@ else handle.prop(prop.id, propHandle.$type);
|
|
|
1601
1684
|
this.writeln(`$("array"${name ? `, ${name}` : ""})`).indent().defineArray(node).unindent();
|
|
1602
1685
|
return this;
|
|
1603
1686
|
}
|
|
1604
|
-
default:
|
|
1605
|
-
console.log("!!!!!!! UNKNOWN", node.entity);
|
|
1606
|
-
return this;
|
|
1607
|
-
}
|
|
1687
|
+
default: return this;
|
|
1608
1688
|
}
|
|
1609
1689
|
}
|
|
1610
1690
|
defineConst(node) {
|
|
@@ -1816,40 +1896,25 @@ else targetValue = "true";
|
|
|
1816
1896
|
this.writeln(`$c(${clone.parentPath}, "${escapeQuotes(clone.propName)}")`);
|
|
1817
1897
|
}
|
|
1818
1898
|
}
|
|
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
|
-
}
|
|
1899
|
+
for (const { entry, accessors } of entryAccessors) for (const accessor of accessors) this.emitMutatingAnnotations(entry, entry.annotations, accessor);
|
|
1836
1900
|
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
|
-
}
|
|
1901
|
+
if (topAnnotations && topAnnotations.length > 0) this.emitMutatingAnnotations(node, topAnnotations, targetName);
|
|
1851
1902
|
this.writeln();
|
|
1852
1903
|
}
|
|
1904
|
+
emitMutatingAnnotations(node, annotations, accessor) {
|
|
1905
|
+
const cleared = new Set();
|
|
1906
|
+
for (const an of annotations) {
|
|
1907
|
+
const { value, multiple } = this.computeAnnotationValue(node, an);
|
|
1908
|
+
if (multiple) {
|
|
1909
|
+
if (!cleared.has(an.name)) {
|
|
1910
|
+
const spec = this.doc.resolveAnnotation(an.name);
|
|
1911
|
+
if (!spec || spec.config.mergeStrategy !== "append") this.writeln(`${accessor}.metadata.delete("${escapeQuotes(an.name)}")`);
|
|
1912
|
+
cleared.add(an.name);
|
|
1913
|
+
}
|
|
1914
|
+
this.writeln(`$a(${accessor}.metadata, "${escapeQuotes(an.name)}", ${value}, true)`);
|
|
1915
|
+
} else this.writeln(`$a(${accessor}.metadata, "${escapeQuotes(an.name)}", ${value})`);
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1853
1918
|
resolveTargetDef(targetName) {
|
|
1854
1919
|
const unwound = this.doc.unwindType(targetName);
|
|
1855
1920
|
if (!unwound?.def) return undefined;
|