@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/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,88 @@ 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 (unless unique indexes exist)
|
|
459
|
+
* - **Unique indexes** (`@db.index.unique`) → appended as union members
|
|
460
|
+
* - **Mongo collection** → always includes `string` (ObjectId) in the union;
|
|
461
|
+
* if no `@meta.id` fields, `__pk` is just `string`
|
|
462
|
+
*/ renderPk(node) {
|
|
463
|
+
const isMongoCollection = !!node.annotations?.some((a) => a.name === "db.mongo.collection");
|
|
464
|
+
let struct;
|
|
465
|
+
if (node.hasExtends) struct = this.doc.resolveInterfaceExtends(node);
|
|
466
|
+
if (!struct) struct = node.getDefinition();
|
|
467
|
+
if (!struct || !isStructure(struct)) return;
|
|
468
|
+
const structNode = struct;
|
|
469
|
+
const pkProps = [];
|
|
470
|
+
const uniqueByIndex = new Map();
|
|
471
|
+
for (const [name, prop] of structNode.props) {
|
|
472
|
+
if (prop.token("identifier")?.pattern) continue;
|
|
473
|
+
if (isMongoCollection && name === "_id") continue;
|
|
474
|
+
if (prop.countAnnotations("meta.id") > 0) pkProps.push({
|
|
475
|
+
name,
|
|
476
|
+
prop
|
|
477
|
+
});
|
|
478
|
+
if (prop.annotations) {
|
|
479
|
+
for (const ann of prop.annotations) if (ann.name === "db.index.unique") {
|
|
480
|
+
const indexName = ann.args[0]?.text ?? name;
|
|
481
|
+
let group = uniqueByIndex.get(indexName);
|
|
482
|
+
if (!group) {
|
|
483
|
+
group = [];
|
|
484
|
+
uniqueByIndex.set(indexName, group);
|
|
485
|
+
}
|
|
486
|
+
group.push({
|
|
487
|
+
name,
|
|
488
|
+
prop
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
const uniqueProps = [];
|
|
494
|
+
const pkNames = new Set(pkProps.map((p) => p.name));
|
|
495
|
+
for (const fields of uniqueByIndex.values()) if (fields.length === 1 && !pkNames.has(fields[0].name)) uniqueProps.push(fields[0]);
|
|
496
|
+
if (pkProps.length === 0 && uniqueProps.length === 0 && !isMongoCollection) return;
|
|
497
|
+
let mongoIdType;
|
|
498
|
+
if (isMongoCollection) {
|
|
499
|
+
const idProp = structNode.props.get("_id");
|
|
500
|
+
if (idProp) mongoIdType = this.renderTypeDefString(idProp.getDefinition()).trim();
|
|
501
|
+
mongoIdType ?? (mongoIdType = "string");
|
|
502
|
+
}
|
|
503
|
+
const uniqueTypes = [];
|
|
504
|
+
const seenTypes = new Set();
|
|
505
|
+
for (const { prop } of uniqueProps) {
|
|
506
|
+
const rendered = this.renderTypeDefString(prop.getDefinition()).trim();
|
|
507
|
+
if (!seenTypes.has(rendered)) {
|
|
508
|
+
seenTypes.add(rendered);
|
|
509
|
+
uniqueTypes.push(rendered);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
this.writeln();
|
|
513
|
+
const uniqueSuffix = uniqueTypes.length > 0 ? ` | ${uniqueTypes.join(" | ")}` : "";
|
|
514
|
+
if (pkProps.length === 0 && !isMongoCollection) this.writeln(`static __pk: ${uniqueTypes.join(" | ")}`);
|
|
515
|
+
else if (pkProps.length === 0) this.writeln(`static __pk: ${mongoIdType}${uniqueSuffix}`);
|
|
516
|
+
else if (pkProps.length === 1) {
|
|
517
|
+
this.write("static __pk: ");
|
|
518
|
+
if (isMongoCollection) this.write(`${mongoIdType} | `);
|
|
519
|
+
const renderedDef = this.renderTypeDefString(pkProps[0].prop.getDefinition()).trim();
|
|
520
|
+
this.writeln(`${renderedDef}${uniqueSuffix}`);
|
|
521
|
+
} else {
|
|
522
|
+
this.write("static __pk: ");
|
|
523
|
+
if (isMongoCollection) this.write(`${mongoIdType} | `);
|
|
524
|
+
this.blockln("{}");
|
|
525
|
+
for (const { name, prop } of pkProps) {
|
|
526
|
+
this.write(wrapProp(name), ": ");
|
|
527
|
+
const renderedDef = this.renderTypeDefString(prop.getDefinition());
|
|
528
|
+
renderedDef.split("\n").forEach((l) => this.writeln(l));
|
|
529
|
+
}
|
|
530
|
+
this.pop();
|
|
531
|
+
this.writeln(uniqueSuffix);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
476
534
|
phantomPropType(def) {
|
|
477
535
|
if (!def) return undefined;
|
|
478
536
|
if (isPrimitive(def) && def.config.type === "phantom") return def.id;
|
|
@@ -521,24 +579,6 @@ function renderPrimitiveTypeDef(def) {
|
|
|
521
579
|
}
|
|
522
580
|
}
|
|
523
581
|
|
|
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
582
|
//#endregion
|
|
543
583
|
//#region packages/typescript/src/validator.ts
|
|
544
584
|
function _define_property$1(obj, key, value) {
|
|
@@ -554,28 +594,35 @@ else obj[key] = value;
|
|
|
554
594
|
const regexCache = new Map();
|
|
555
595
|
var Validator = class {
|
|
556
596
|
isLimitExceeded() {
|
|
557
|
-
if (this.stackErrors.length > 0)
|
|
597
|
+
if (this.stackErrors.length > 0) {
|
|
598
|
+
const top = this.stackErrors[this.stackErrors.length - 1];
|
|
599
|
+
return top !== null && top.length >= this.opts.errorLimit;
|
|
600
|
+
}
|
|
558
601
|
return this.errors.length >= this.opts.errorLimit;
|
|
559
602
|
}
|
|
560
603
|
push(name) {
|
|
561
604
|
this.stackPath.push(name);
|
|
562
|
-
this.stackErrors.push(
|
|
605
|
+
this.stackErrors.push(null);
|
|
606
|
+
this.cachedPath = this.stackPath.length <= 1 ? "" : this.stackPath[1] + (this.stackPath.length > 2 ? "." + this.stackPath.slice(2).join(".") : "");
|
|
563
607
|
}
|
|
564
608
|
pop(saveErrors) {
|
|
565
609
|
this.stackPath.pop();
|
|
566
610
|
const popped = this.stackErrors.pop();
|
|
567
|
-
if (saveErrors && popped
|
|
568
|
-
|
|
569
|
-
});
|
|
611
|
+
if (saveErrors && popped !== null && popped !== undefined && popped.length > 0) for (const err of popped) this.error(err.message, err.path, err.details);
|
|
612
|
+
this.cachedPath = this.stackPath.length <= 1 ? "" : this.stackPath[1] + (this.stackPath.length > 2 ? "." + this.stackPath.slice(2).join(".") : "");
|
|
570
613
|
return popped;
|
|
571
614
|
}
|
|
572
615
|
clear() {
|
|
573
|
-
this.stackErrors[this.stackErrors.length - 1] =
|
|
616
|
+
this.stackErrors[this.stackErrors.length - 1] = null;
|
|
574
617
|
}
|
|
575
618
|
error(message, path$1, details) {
|
|
576
|
-
|
|
619
|
+
let errors = this.stackErrors[this.stackErrors.length - 1];
|
|
620
|
+
if (!errors) if (this.stackErrors.length > 0) {
|
|
621
|
+
errors = [];
|
|
622
|
+
this.stackErrors[this.stackErrors.length - 1] = errors;
|
|
623
|
+
} else errors = this.errors;
|
|
577
624
|
const error = {
|
|
578
|
-
path: path$1 || this.
|
|
625
|
+
path: path$1 || this.cachedPath,
|
|
579
626
|
message
|
|
580
627
|
};
|
|
581
628
|
if (details?.length) error.details = details;
|
|
@@ -595,9 +642,10 @@ var Validator = class {
|
|
|
595
642
|
* @returns `true` if the value matches the type definition.
|
|
596
643
|
* @throws {ValidatorError} When validation fails and `safe` is not `true`.
|
|
597
644
|
*/ validate(value, safe, context) {
|
|
598
|
-
this.push("");
|
|
599
645
|
this.errors = [];
|
|
600
646
|
this.stackErrors = [];
|
|
647
|
+
this.stackPath = [""];
|
|
648
|
+
this.cachedPath = "";
|
|
601
649
|
this.context = context;
|
|
602
650
|
const passed = this.validateSafe(this.def, value);
|
|
603
651
|
this.pop(!passed);
|
|
@@ -611,7 +659,7 @@ var Validator = class {
|
|
|
611
659
|
validateSafe(def, value) {
|
|
612
660
|
if (this.isLimitExceeded()) return false;
|
|
613
661
|
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.
|
|
662
|
+
if (typeof this.opts.replace === "function") def = this.opts.replace(def, this.cachedPath);
|
|
615
663
|
if (def.optional && value === undefined) return true;
|
|
616
664
|
for (const plugin of this.opts.plugins) {
|
|
617
665
|
const result = plugin(this, def, value);
|
|
@@ -620,18 +668,21 @@ var Validator = class {
|
|
|
620
668
|
return this.validateAnnotatedType(def, value);
|
|
621
669
|
}
|
|
622
670
|
get path() {
|
|
623
|
-
return this.
|
|
671
|
+
return this.cachedPath;
|
|
624
672
|
}
|
|
625
673
|
validateAnnotatedType(def, value) {
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
674
|
+
switch (def.type.kind) {
|
|
675
|
+
case "": {
|
|
676
|
+
if (def.type.designType === "phantom") return true;
|
|
677
|
+
return this.validatePrimitive(def, value);
|
|
678
|
+
}
|
|
679
|
+
case "object": return this.validateObject(def, value);
|
|
680
|
+
case "array": return this.validateArray(def, value);
|
|
681
|
+
case "union": return this.validateUnion(def, value);
|
|
682
|
+
case "intersection": return this.validateIntersection(def, value);
|
|
683
|
+
case "tuple": return this.validateTuple(def, value);
|
|
684
|
+
default: throw new Error(`Unknown type kind "${def.type.kind}"`);
|
|
685
|
+
}
|
|
635
686
|
}
|
|
636
687
|
validateUnion(def, value) {
|
|
637
688
|
let i = 0;
|
|
@@ -695,6 +746,30 @@ var Validator = class {
|
|
|
695
746
|
return false;
|
|
696
747
|
}
|
|
697
748
|
}
|
|
749
|
+
const uniqueItems = def.metadata.get("expect.array.uniqueItems");
|
|
750
|
+
if (uniqueItems) {
|
|
751
|
+
const separator = "▼↩";
|
|
752
|
+
const seen = new Set();
|
|
753
|
+
const keyProps = new Set();
|
|
754
|
+
if (def.type.of.type.kind === "object") {
|
|
755
|
+
for (const [key, val] of def.type.of.type.props.entries()) if (val.metadata.get("expect.array.key")) keyProps.add(key);
|
|
756
|
+
}
|
|
757
|
+
for (let idx = 0; idx < value.length; idx++) {
|
|
758
|
+
const item = value[idx];
|
|
759
|
+
let key;
|
|
760
|
+
if (keyProps.size > 0) {
|
|
761
|
+
key = "";
|
|
762
|
+
for (const prop of keyProps) key += JSON.stringify(item[prop]) + separator;
|
|
763
|
+
} else key = JSON.stringify(item);
|
|
764
|
+
if (seen.has(key)) {
|
|
765
|
+
this.push(String(idx));
|
|
766
|
+
this.error(uniqueItems.message || "Duplicate items are not allowed");
|
|
767
|
+
this.pop(true);
|
|
768
|
+
return false;
|
|
769
|
+
}
|
|
770
|
+
seen.add(key);
|
|
771
|
+
}
|
|
772
|
+
}
|
|
698
773
|
let i = 0;
|
|
699
774
|
let passed = true;
|
|
700
775
|
for (const item of value) {
|
|
@@ -716,21 +791,20 @@ var Validator = class {
|
|
|
716
791
|
let passed = true;
|
|
717
792
|
const valueKeys = new Set(Object.keys(value));
|
|
718
793
|
const typeKeys = new Set();
|
|
719
|
-
|
|
794
|
+
let skipList;
|
|
720
795
|
if (this.opts.skipList) {
|
|
721
|
-
const path$1 = this.stackPath.length > 1 ? `${this.
|
|
722
|
-
this.opts.skipList.
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
});
|
|
796
|
+
const path$1 = this.stackPath.length > 1 ? `${this.cachedPath}.` : "";
|
|
797
|
+
for (const item of this.opts.skipList) if (item.startsWith(path$1)) {
|
|
798
|
+
const key = item.slice(path$1.length);
|
|
799
|
+
if (!skipList) skipList = new Set();
|
|
800
|
+
skipList.add(key);
|
|
801
|
+
valueKeys.delete(key);
|
|
802
|
+
}
|
|
729
803
|
}
|
|
730
804
|
let partialFunctionMatched = false;
|
|
731
|
-
if (typeof this.opts.partial === "function") partialFunctionMatched = this.opts.partial(def, this.
|
|
805
|
+
if (typeof this.opts.partial === "function") partialFunctionMatched = this.opts.partial(def, this.cachedPath);
|
|
732
806
|
for (const [key, item] of def.type.props.entries()) {
|
|
733
|
-
if (skipList.has(key) || isPhantomType(item)) continue;
|
|
807
|
+
if (skipList && skipList.has(key) || isPhantomType(item)) continue;
|
|
734
808
|
typeKeys.add(key);
|
|
735
809
|
if (value[key] === undefined) {
|
|
736
810
|
if (partialFunctionMatched || this.opts.partial === "deep" || this.opts.partial === true && this.stackPath.length <= 1) continue;
|
|
@@ -751,19 +825,21 @@ else {
|
|
|
751
825
|
def: propDef
|
|
752
826
|
});
|
|
753
827
|
if (matched.length > 0) {
|
|
828
|
+
this.push(key);
|
|
754
829
|
let keyPassed = false;
|
|
755
|
-
for (const { def:
|
|
756
|
-
this.
|
|
757
|
-
|
|
758
|
-
|
|
830
|
+
for (const { def: propDef } of matched) {
|
|
831
|
+
if (this.validateSafe(propDef, value[key])) {
|
|
832
|
+
keyPassed = true;
|
|
833
|
+
break;
|
|
834
|
+
}
|
|
835
|
+
this.clear();
|
|
759
836
|
}
|
|
760
837
|
if (!keyPassed) {
|
|
761
|
-
this.push(key);
|
|
762
838
|
this.validateSafe(matched[0].def, value[key]);
|
|
763
839
|
this.pop(true);
|
|
764
840
|
passed = false;
|
|
765
841
|
if (this.isLimitExceeded()) return false;
|
|
766
|
-
}
|
|
842
|
+
} else this.pop(false);
|
|
767
843
|
} else if (this.opts.unknownProps !== "ignore") {
|
|
768
844
|
if (this.opts.unknownProps === "error") {
|
|
769
845
|
this.push(key);
|
|
@@ -916,11 +992,13 @@ else {
|
|
|
916
992
|
/** Validation errors collected during the last {@link validate} call. */ _define_property$1(this, "errors", void 0);
|
|
917
993
|
_define_property$1(this, "stackErrors", void 0);
|
|
918
994
|
_define_property$1(this, "stackPath", void 0);
|
|
995
|
+
_define_property$1(this, "cachedPath", void 0);
|
|
919
996
|
_define_property$1(this, "context", void 0);
|
|
920
997
|
this.def = def;
|
|
921
998
|
this.errors = [];
|
|
922
999
|
this.stackErrors = [];
|
|
923
1000
|
this.stackPath = [];
|
|
1001
|
+
this.cachedPath = "";
|
|
924
1002
|
this.opts = {
|
|
925
1003
|
partial: false,
|
|
926
1004
|
unknownProps: "error",
|
|
@@ -938,6 +1016,24 @@ var ValidatorError = class extends Error {
|
|
|
938
1016
|
|
|
939
1017
|
//#endregion
|
|
940
1018
|
//#region packages/typescript/src/annotated-type.ts
|
|
1019
|
+
const COMPLEX_KINDS = new Set([
|
|
1020
|
+
"union",
|
|
1021
|
+
"intersection",
|
|
1022
|
+
"tuple"
|
|
1023
|
+
]);
|
|
1024
|
+
/** Shared validator method reused by all annotated type nodes. */ function validatorMethod(opts) {
|
|
1025
|
+
return new Validator(this, opts);
|
|
1026
|
+
}
|
|
1027
|
+
function createAnnotatedTypeNode(type, metadata, opts) {
|
|
1028
|
+
return {
|
|
1029
|
+
__is_atscript_annotated_type: true,
|
|
1030
|
+
type,
|
|
1031
|
+
metadata,
|
|
1032
|
+
validator: validatorMethod,
|
|
1033
|
+
id: opts?.id,
|
|
1034
|
+
optional: opts?.optional
|
|
1035
|
+
};
|
|
1036
|
+
}
|
|
941
1037
|
function isAnnotatedType(type) {
|
|
942
1038
|
return type && type.__is_atscript_annotated_type;
|
|
943
1039
|
}
|
|
@@ -954,38 +1050,24 @@ function defineAnnotatedType(_kind, base) {
|
|
|
954
1050
|
const kind = _kind || "";
|
|
955
1051
|
const type = base?.type || {};
|
|
956
1052
|
type.kind = kind;
|
|
957
|
-
if ([
|
|
958
|
-
"union",
|
|
959
|
-
"intersection",
|
|
960
|
-
"tuple"
|
|
961
|
-
].includes(kind)) type.items = [];
|
|
1053
|
+
if (COMPLEX_KINDS.has(kind)) type.items = [];
|
|
962
1054
|
if (kind === "object") {
|
|
963
1055
|
type.props = new Map();
|
|
964
1056
|
type.propsPatterns = [];
|
|
965
1057
|
}
|
|
966
1058
|
type.tags = new Set();
|
|
967
1059
|
const metadata = base?.metadata || new Map();
|
|
968
|
-
|
|
969
|
-
__is_atscript_annotated_type: true,
|
|
970
|
-
metadata,
|
|
971
|
-
type,
|
|
972
|
-
validator(opts) {
|
|
973
|
-
return new Validator(this, opts);
|
|
974
|
-
}
|
|
975
|
-
});
|
|
976
|
-
else base = {
|
|
1060
|
+
const payload = {
|
|
977
1061
|
__is_atscript_annotated_type: true,
|
|
978
1062
|
metadata,
|
|
979
1063
|
type,
|
|
980
|
-
validator
|
|
981
|
-
return new Validator(this, opts);
|
|
982
|
-
}
|
|
1064
|
+
validator: validatorMethod
|
|
983
1065
|
};
|
|
1066
|
+
base = base ? Object.assign(base, payload) : payload;
|
|
984
1067
|
const handle = {
|
|
985
1068
|
$type: base,
|
|
986
1069
|
$def: type,
|
|
987
1070
|
$metadata: metadata,
|
|
988
|
-
_existingObject: undefined,
|
|
989
1071
|
tags(...tags) {
|
|
990
1072
|
for (const tag of tags) this.$def.tags.add(tag);
|
|
991
1073
|
return this;
|
|
@@ -1026,27 +1108,18 @@ else base = {
|
|
|
1026
1108
|
return this;
|
|
1027
1109
|
},
|
|
1028
1110
|
refTo(type$1, chain) {
|
|
1111
|
+
if (!isAnnotatedType(type$1)) throw new Error(`${type$1} is not annotated type`);
|
|
1029
1112
|
let newBase = type$1;
|
|
1030
1113
|
const typeName = type$1.name || "Unknown";
|
|
1031
|
-
if (
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1114
|
+
if (chain) for (let i = 0; i < chain.length; i++) {
|
|
1115
|
+
const c = chain[i];
|
|
1116
|
+
if (newBase.type.kind === "object" && newBase.type.props.has(c)) newBase = newBase.type.props.get(c);
|
|
1117
|
+
else {
|
|
1118
|
+
const keys = chain.slice(0, i + 1).map((k) => `["${k}"]`).join("");
|
|
1119
|
+
throw new Error(`Can't find prop ${typeName}${keys}`);
|
|
1037
1120
|
}
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
this.$type = {
|
|
1041
|
-
__is_atscript_annotated_type: true,
|
|
1042
|
-
type: newBase.type,
|
|
1043
|
-
metadata,
|
|
1044
|
-
id: newBase.id,
|
|
1045
|
-
validator(opts) {
|
|
1046
|
-
return new Validator(this, opts);
|
|
1047
|
-
}
|
|
1048
|
-
};
|
|
1049
|
-
} else throw new Error(`${type$1} is not annotated type`);
|
|
1121
|
+
}
|
|
1122
|
+
this.$type = createAnnotatedTypeNode(newBase.type, metadata, { id: newBase.id });
|
|
1050
1123
|
return this;
|
|
1051
1124
|
},
|
|
1052
1125
|
annotate(key, value, asArray) {
|
|
@@ -1064,6 +1137,24 @@ function isPhantomType(def) {
|
|
|
1064
1137
|
return def.type.kind === "" && def.type.designType === "phantom";
|
|
1065
1138
|
}
|
|
1066
1139
|
|
|
1140
|
+
//#endregion
|
|
1141
|
+
//#region packages/typescript/src/traverse.ts
|
|
1142
|
+
function forAnnotatedType(def, handlers) {
|
|
1143
|
+
switch (def.type.kind) {
|
|
1144
|
+
case "": {
|
|
1145
|
+
const typed = def;
|
|
1146
|
+
if (handlers.phantom && typed.type.designType === "phantom") return handlers.phantom(typed);
|
|
1147
|
+
return handlers.final(typed);
|
|
1148
|
+
}
|
|
1149
|
+
case "object": return handlers.object(def);
|
|
1150
|
+
case "array": return handlers.array(def);
|
|
1151
|
+
case "union": return handlers.union(def);
|
|
1152
|
+
case "intersection": return handlers.intersection(def);
|
|
1153
|
+
case "tuple": return handlers.tuple(def);
|
|
1154
|
+
default: throw new Error(`Unknown type kind "${def.type.kind}"`);
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1067
1158
|
//#endregion
|
|
1068
1159
|
//#region packages/typescript/src/json-schema.ts
|
|
1069
1160
|
/**
|
|
@@ -1078,10 +1169,10 @@ function isPhantomType(def) {
|
|
|
1078
1169
|
const firstObj = items[0].type;
|
|
1079
1170
|
const candidates = [];
|
|
1080
1171
|
for (const [propName, propType] of firstObj.props.entries()) if (propType.type.kind === "" && propType.type.value !== undefined) candidates.push(propName);
|
|
1081
|
-
|
|
1172
|
+
let result = null;
|
|
1082
1173
|
for (const candidate of candidates) {
|
|
1083
1174
|
const values = new Set();
|
|
1084
|
-
const
|
|
1175
|
+
const indexMapping = {};
|
|
1085
1176
|
let valid = true;
|
|
1086
1177
|
for (let i = 0; i < items.length; i++) {
|
|
1087
1178
|
const obj = items[i].type;
|
|
@@ -1096,19 +1187,21 @@ function isPhantomType(def) {
|
|
|
1096
1187
|
break;
|
|
1097
1188
|
}
|
|
1098
1189
|
values.add(val);
|
|
1099
|
-
|
|
1190
|
+
indexMapping[String(val)] = i;
|
|
1191
|
+
}
|
|
1192
|
+
if (valid) {
|
|
1193
|
+
if (result) return null;
|
|
1194
|
+
result = {
|
|
1195
|
+
propertyName: candidate,
|
|
1196
|
+
indexMapping
|
|
1197
|
+
};
|
|
1100
1198
|
}
|
|
1101
|
-
if (valid) validCandidates.push({
|
|
1102
|
-
propertyName: candidate,
|
|
1103
|
-
mapping
|
|
1104
|
-
});
|
|
1105
1199
|
}
|
|
1106
|
-
|
|
1107
|
-
return null;
|
|
1200
|
+
return result;
|
|
1108
1201
|
}
|
|
1109
1202
|
function buildJsonSchema(type) {
|
|
1110
1203
|
const defs = {};
|
|
1111
|
-
let
|
|
1204
|
+
let hasDefs = false;
|
|
1112
1205
|
const buildObject = (d) => {
|
|
1113
1206
|
const properties = {};
|
|
1114
1207
|
const required = [];
|
|
@@ -1125,15 +1218,15 @@ function buildJsonSchema(type) {
|
|
|
1125
1218
|
return schema$1;
|
|
1126
1219
|
};
|
|
1127
1220
|
const build = (def) => {
|
|
1128
|
-
if (def.id && def.type.kind === "object" &&
|
|
1221
|
+
if (def.id && def.type.kind === "object" && def !== type) {
|
|
1129
1222
|
const name = def.id;
|
|
1130
1223
|
if (!defs[name]) {
|
|
1224
|
+
hasDefs = true;
|
|
1131
1225
|
defs[name] = {};
|
|
1132
1226
|
defs[name] = buildObject(def);
|
|
1133
1227
|
}
|
|
1134
1228
|
return { $ref: `#/$defs/${name}` };
|
|
1135
1229
|
}
|
|
1136
|
-
isRoot = false;
|
|
1137
1230
|
const meta = def.metadata;
|
|
1138
1231
|
return forAnnotatedType(def, {
|
|
1139
1232
|
phantom() {
|
|
@@ -1158,11 +1251,9 @@ function buildJsonSchema(type) {
|
|
|
1158
1251
|
if (disc) {
|
|
1159
1252
|
const oneOf = d.type.items.map(build);
|
|
1160
1253
|
const mapping = {};
|
|
1161
|
-
for (const [val,
|
|
1162
|
-
const idx = Number.parseInt(origPath.split("/").pop(), 10);
|
|
1254
|
+
for (const [val, idx] of Object.entries(disc.indexMapping)) {
|
|
1163
1255
|
const item = d.type.items[idx];
|
|
1164
|
-
|
|
1165
|
-
else mapping[val] = origPath;
|
|
1256
|
+
mapping[val] = item.id && defs[item.id] ? `#/$defs/${item.id}` : `#/oneOf/${idx}`;
|
|
1166
1257
|
}
|
|
1167
1258
|
return {
|
|
1168
1259
|
oneOf,
|
|
@@ -1212,7 +1303,7 @@ else schema$1.allOf = (schema$1.allOf || []).concat(patterns.map((p) => ({ patte
|
|
|
1212
1303
|
});
|
|
1213
1304
|
};
|
|
1214
1305
|
const schema = build(type);
|
|
1215
|
-
if (
|
|
1306
|
+
if (hasDefs) return {
|
|
1216
1307
|
...schema,
|
|
1217
1308
|
$defs: defs
|
|
1218
1309
|
};
|
|
@@ -1236,24 +1327,25 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
1236
1327
|
this.writeln("// prettier-ignore-start");
|
|
1237
1328
|
this.writeln("/* eslint-disable */");
|
|
1238
1329
|
this.writeln("/* oxlint-disable */");
|
|
1330
|
+
let hasMutatingAnnotate = false;
|
|
1331
|
+
const nodesByName = new Map();
|
|
1332
|
+
for (const node of this.doc.nodes) {
|
|
1333
|
+
if (node.entity === "annotate" && node.isMutating) hasMutatingAnnotate = true;
|
|
1334
|
+
if (node.__typeId !== null && node.__typeId !== undefined && node.id) {
|
|
1335
|
+
const name = node.id;
|
|
1336
|
+
if (!nodesByName.has(name)) nodesByName.set(name, []);
|
|
1337
|
+
nodesByName.get(name).push(node);
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
for (const [name, nodes] of nodesByName) if (nodes.length === 1) this.typeIds.set(nodes[0], name);
|
|
1341
|
+
else for (let i = 0; i < nodes.length; i++) this.typeIds.set(nodes[i], `${name}__${i + 1}`);
|
|
1239
1342
|
const imports = ["defineAnnotatedType as $", "annotate as $a"];
|
|
1240
|
-
const hasMutatingAnnotate = this.doc.nodes.some((n) => n.entity === "annotate" && n.isMutating);
|
|
1241
1343
|
if (hasMutatingAnnotate) imports.push("cloneRefProp as $c");
|
|
1242
1344
|
const jsonSchemaMode = resolveJsonSchemaMode(this.opts);
|
|
1243
1345
|
if (jsonSchemaMode === "lazy") imports.push("buildJsonSchema as $$");
|
|
1244
1346
|
if (this.opts?.exampleData) imports.push("createDataFromAnnotatedType as $e");
|
|
1245
1347
|
if (jsonSchemaMode === false) imports.push("throwFeatureDisabled as $d");
|
|
1246
1348
|
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
1349
|
}
|
|
1258
1350
|
buildAdHocMap(annotateNodes) {
|
|
1259
1351
|
const map = new Map();
|
|
@@ -1262,7 +1354,8 @@ else for (let i = 0; i < nodes.length; i++) this.typeIds.set(nodes[i], `${name}_
|
|
|
1262
1354
|
const anns = entry.annotations || [];
|
|
1263
1355
|
if (anns.length > 0) {
|
|
1264
1356
|
const existing = map.get(path$1);
|
|
1265
|
-
|
|
1357
|
+
if (existing) existing.push(...anns);
|
|
1358
|
+
else map.set(path$1, [...anns]);
|
|
1266
1359
|
}
|
|
1267
1360
|
}
|
|
1268
1361
|
return map.size > 0 ? map : null;
|
|
@@ -1321,35 +1414,20 @@ else def = def.getDefinition() || def;
|
|
|
1321
1414
|
this.renderExampleDataMethod(node);
|
|
1322
1415
|
}
|
|
1323
1416
|
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();
|
|
1417
|
+
this.renderDefinitionClass(node);
|
|
1333
1418
|
}
|
|
1334
1419
|
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();
|
|
1420
|
+
this.renderDefinitionClass(node);
|
|
1344
1421
|
}
|
|
1345
1422
|
renderAnnotate(node) {
|
|
1346
1423
|
if (node.isMutating) {
|
|
1347
1424
|
this.postAnnotate.push(node);
|
|
1348
1425
|
return;
|
|
1349
1426
|
}
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1427
|
+
if (!this.doc.unwindType(node.targetName)?.def) return;
|
|
1428
|
+
this.renderDefinitionClass(node);
|
|
1429
|
+
}
|
|
1430
|
+
renderDefinitionClass(node) {
|
|
1353
1431
|
this.writeln();
|
|
1354
1432
|
const exported = node.token("export")?.text === "export";
|
|
1355
1433
|
this.write(exported ? "export " : "");
|
|
@@ -1507,6 +1585,11 @@ else handle.prop(prop.id, propHandle.$type);
|
|
|
1507
1585
|
handle.annotate(a.name, true);
|
|
1508
1586
|
break;
|
|
1509
1587
|
}
|
|
1588
|
+
case "expect.array.uniqueItems":
|
|
1589
|
+
case "expect.array.key": {
|
|
1590
|
+
handle.annotate(a.name, { message: a.args[0]?.text });
|
|
1591
|
+
break;
|
|
1592
|
+
}
|
|
1510
1593
|
default:
|
|
1511
1594
|
}
|
|
1512
1595
|
});
|
|
@@ -1574,10 +1657,7 @@ else handle.prop(prop.id, propHandle.$type);
|
|
|
1574
1657
|
this.writeln(`$("array"${name ? `, ${name}` : ""})`).indent().defineArray(node).unindent();
|
|
1575
1658
|
return this;
|
|
1576
1659
|
}
|
|
1577
|
-
default:
|
|
1578
|
-
console.log("!!!!!!! UNKNOWN", node.entity);
|
|
1579
|
-
return this;
|
|
1580
|
-
}
|
|
1660
|
+
default: return this;
|
|
1581
1661
|
}
|
|
1582
1662
|
}
|
|
1583
1663
|
defineConst(node) {
|
|
@@ -1789,40 +1869,25 @@ else targetValue = "true";
|
|
|
1789
1869
|
this.writeln(`$c(${clone.parentPath}, "${escapeQuotes(clone.propName)}")`);
|
|
1790
1870
|
}
|
|
1791
1871
|
}
|
|
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
|
-
}
|
|
1872
|
+
for (const { entry, accessors } of entryAccessors) for (const accessor of accessors) this.emitMutatingAnnotations(entry, entry.annotations, accessor);
|
|
1809
1873
|
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
|
-
}
|
|
1874
|
+
if (topAnnotations && topAnnotations.length > 0) this.emitMutatingAnnotations(node, topAnnotations, targetName);
|
|
1824
1875
|
this.writeln();
|
|
1825
1876
|
}
|
|
1877
|
+
emitMutatingAnnotations(node, annotations, accessor) {
|
|
1878
|
+
const cleared = new Set();
|
|
1879
|
+
for (const an of annotations) {
|
|
1880
|
+
const { value, multiple } = this.computeAnnotationValue(node, an);
|
|
1881
|
+
if (multiple) {
|
|
1882
|
+
if (!cleared.has(an.name)) {
|
|
1883
|
+
const spec = this.doc.resolveAnnotation(an.name);
|
|
1884
|
+
if (!spec || spec.config.mergeStrategy !== "append") this.writeln(`${accessor}.metadata.delete("${escapeQuotes(an.name)}")`);
|
|
1885
|
+
cleared.add(an.name);
|
|
1886
|
+
}
|
|
1887
|
+
this.writeln(`$a(${accessor}.metadata, "${escapeQuotes(an.name)}", ${value}, true)`);
|
|
1888
|
+
} else this.writeln(`$a(${accessor}.metadata, "${escapeQuotes(an.name)}", ${value})`);
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1826
1891
|
resolveTargetDef(targetName) {
|
|
1827
1892
|
const unwound = this.doc.unwindType(targetName);
|
|
1828
1893
|
if (!unwound?.def) return undefined;
|