@atscript/typescript 0.0.16 → 0.0.18
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 +53 -11
- package/dist/index.cjs +148 -32
- package/dist/index.d.ts +59 -25
- package/dist/index.mjs +147 -32
- package/package.json +7 -5
package/dist/cli.cjs
CHANGED
|
@@ -204,7 +204,7 @@ function wrapProp(name) {
|
|
|
204
204
|
return name;
|
|
205
205
|
}
|
|
206
206
|
function escapeQuotes(str) {
|
|
207
|
-
return str.replace(/"/g, "\\\"");
|
|
207
|
+
return str.replace(/"/g, "\\\"").replace(/\\/g, "\\\\");
|
|
208
208
|
}
|
|
209
209
|
|
|
210
210
|
//#endregion
|
|
@@ -219,7 +219,7 @@ var TypeRenderer = class extends BaseRenderer {
|
|
|
219
219
|
this.writeln(" * Do not edit this file!");
|
|
220
220
|
this.writeln(" */");
|
|
221
221
|
this.writeln();
|
|
222
|
-
this.writeln("import type { TAtscriptTypeObject, TAtscriptTypeComplex, TAtscriptTypeFinal, TAtscriptTypeArray, TMetadataMap, Validator } from \"@atscript/typescript\"");
|
|
222
|
+
this.writeln("import type { TAtscriptTypeObject, TAtscriptTypeComplex, TAtscriptTypeFinal, TAtscriptTypeArray, TMetadataMap, Validator, TAtscriptAnnotatedTypeConstructor, TValidatorOptions } from \"@atscript/typescript\"");
|
|
223
223
|
}
|
|
224
224
|
post() {
|
|
225
225
|
this.writeln("// prettier-ignore-end");
|
|
@@ -271,17 +271,41 @@ var TypeRenderer = class extends BaseRenderer {
|
|
|
271
271
|
}
|
|
272
272
|
renderStructure(struct, asClass) {
|
|
273
273
|
this.blockln("{}");
|
|
274
|
+
const patterns = [];
|
|
275
|
+
let hasProp = false;
|
|
274
276
|
for (const prop of Array.from(struct.props.values())) {
|
|
277
|
+
if (prop.token("identifier")?.pattern) {
|
|
278
|
+
patterns.push(prop);
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
hasProp = true;
|
|
275
282
|
const optional = !!prop.token("optional");
|
|
276
283
|
this.write(wrapProp(prop.id), optional ? "?" : "", ": ");
|
|
277
284
|
this.renderTypeDef(prop.getDefinition());
|
|
278
285
|
this.writeln();
|
|
279
286
|
}
|
|
287
|
+
if (patterns.length) {
|
|
288
|
+
this.write(`[key: string]: `);
|
|
289
|
+
if (hasProp) this.writeln("any");
|
|
290
|
+
else if (patterns.length === 1) {
|
|
291
|
+
this.renderTypeDef(patterns[0].getDefinition());
|
|
292
|
+
this.writeln();
|
|
293
|
+
} else {
|
|
294
|
+
this.indent();
|
|
295
|
+
for (const prop of patterns) {
|
|
296
|
+
this.writeln();
|
|
297
|
+
this.write("| ");
|
|
298
|
+
this.renderTypeDef(prop.getDefinition());
|
|
299
|
+
}
|
|
300
|
+
this.unindent();
|
|
301
|
+
this.writeln();
|
|
302
|
+
}
|
|
303
|
+
}
|
|
280
304
|
if (asClass) {
|
|
281
|
-
this.writeln("static
|
|
305
|
+
this.writeln("static __is_atscript_annotated_type: true");
|
|
282
306
|
this.writeln(`static type: TAtscriptTypeObject<keyof ${asClass}>`);
|
|
283
307
|
this.writeln(`static metadata: TMetadataMap<AtscriptMetadata>`);
|
|
284
|
-
this.writeln(`static validator: () => Validator
|
|
308
|
+
this.writeln(`static validator: <TT extends TAtscriptAnnotatedTypeConstructor = ${asClass}>(opts?: Partial<TValidatorOptions>) => Validator<TT>`);
|
|
285
309
|
}
|
|
286
310
|
this.pop();
|
|
287
311
|
}
|
|
@@ -320,10 +344,10 @@ else if ((0, __atscript_core.isGroup)(realDef)) typeDef = "TAtscriptTypeComplex"
|
|
|
320
344
|
else if ((0, __atscript_core.isArray)(realDef)) typeDef = "TAtscriptTypeArray";
|
|
321
345
|
else if ((0, __atscript_core.isPrimitive)(realDef)) typeDef = "TAtscriptTypeFinal";
|
|
322
346
|
}
|
|
323
|
-
this.writeln(`const
|
|
347
|
+
this.writeln(`const __is_atscript_annotated_type: true`);
|
|
324
348
|
this.writeln(`const type: ${typeDef}`);
|
|
325
349
|
this.writeln(`const metadata: TMetadataMap<AtscriptMetadata>`);
|
|
326
|
-
this.writeln(`const validator:
|
|
350
|
+
this.writeln(`const validator: <TT extends TAtscriptAnnotatedTypeConstructor = ${node.id}>(opts?: Partial<TValidatorOptions>) => Validator<TT>`);
|
|
327
351
|
this.popln();
|
|
328
352
|
}
|
|
329
353
|
renderJsDoc(node) {
|
|
@@ -382,7 +406,7 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
382
406
|
this.write(exported ? "export " : "");
|
|
383
407
|
this.write(`class ${node.id} `);
|
|
384
408
|
this.blockln("{}");
|
|
385
|
-
this.writeln("static
|
|
409
|
+
this.writeln("static __is_atscript_annotated_type = true");
|
|
386
410
|
this.writeln("static type = {}");
|
|
387
411
|
this.writeln("static metadata = new Map()");
|
|
388
412
|
this.popln();
|
|
@@ -395,7 +419,7 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
395
419
|
this.write(exported ? "export " : "");
|
|
396
420
|
this.write(`class ${node.id} `);
|
|
397
421
|
this.blockln("{}");
|
|
398
|
-
this.writeln("static
|
|
422
|
+
this.writeln("static __is_atscript_annotated_type = true");
|
|
399
423
|
this.writeln("static type = {}");
|
|
400
424
|
this.writeln("static metadata = new Map()");
|
|
401
425
|
this.popln();
|
|
@@ -508,6 +532,17 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
508
532
|
this.unindent();
|
|
509
533
|
this.write(`)`);
|
|
510
534
|
}
|
|
535
|
+
for (const [key, propDef] of Object.entries(def.propsPatterns)) {
|
|
536
|
+
const optional = typeof propDef === "object" && propDef.optional;
|
|
537
|
+
this.writeln(`.propPattern(`);
|
|
538
|
+
this.indent();
|
|
539
|
+
this.writeln(`${key},`);
|
|
540
|
+
this.renderPrimitiveDef(propDef);
|
|
541
|
+
if (optional) this.writeln(".optional()");
|
|
542
|
+
this.writeln(".$type");
|
|
543
|
+
this.unindent();
|
|
544
|
+
this.write(`)`);
|
|
545
|
+
}
|
|
511
546
|
this.unindent();
|
|
512
547
|
return;
|
|
513
548
|
default: return this.writeln(`$(${d()}).designType("any")`);
|
|
@@ -516,10 +551,17 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
516
551
|
defineObject(node) {
|
|
517
552
|
const props = Array.from(node.props.values());
|
|
518
553
|
for (const prop of props) {
|
|
554
|
+
const pattern = prop.token("identifier")?.pattern;
|
|
519
555
|
const optional = !!prop.token("optional");
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
556
|
+
if (pattern) {
|
|
557
|
+
this.writeln(`.propPattern(`);
|
|
558
|
+
this.indent();
|
|
559
|
+
this.writeln(`/${pattern.source}/${pattern.flags},`);
|
|
560
|
+
} else {
|
|
561
|
+
this.writeln(`.prop(`);
|
|
562
|
+
this.indent();
|
|
563
|
+
this.writeln(`"${escapeQuotes(prop.id)}",`);
|
|
564
|
+
}
|
|
523
565
|
this.annotateType(prop.getDefinition());
|
|
524
566
|
this.indent().defineMetadata(prop).unindent();
|
|
525
567
|
if (optional) this.writeln(" .optional()");
|
package/dist/index.cjs
CHANGED
|
@@ -202,7 +202,7 @@ function wrapProp(name) {
|
|
|
202
202
|
return name;
|
|
203
203
|
}
|
|
204
204
|
function escapeQuotes(str) {
|
|
205
|
-
return str.replace(/"/g, "\\\"");
|
|
205
|
+
return str.replace(/"/g, "\\\"").replace(/\\/g, "\\\\");
|
|
206
206
|
}
|
|
207
207
|
|
|
208
208
|
//#endregion
|
|
@@ -217,7 +217,7 @@ var TypeRenderer = class extends BaseRenderer {
|
|
|
217
217
|
this.writeln(" * Do not edit this file!");
|
|
218
218
|
this.writeln(" */");
|
|
219
219
|
this.writeln();
|
|
220
|
-
this.writeln("import type { TAtscriptTypeObject, TAtscriptTypeComplex, TAtscriptTypeFinal, TAtscriptTypeArray, TMetadataMap, Validator } from \"@atscript/typescript\"");
|
|
220
|
+
this.writeln("import type { TAtscriptTypeObject, TAtscriptTypeComplex, TAtscriptTypeFinal, TAtscriptTypeArray, TMetadataMap, Validator, TAtscriptAnnotatedTypeConstructor, TValidatorOptions } from \"@atscript/typescript\"");
|
|
221
221
|
}
|
|
222
222
|
post() {
|
|
223
223
|
this.writeln("// prettier-ignore-end");
|
|
@@ -269,17 +269,41 @@ var TypeRenderer = class extends BaseRenderer {
|
|
|
269
269
|
}
|
|
270
270
|
renderStructure(struct, asClass) {
|
|
271
271
|
this.blockln("{}");
|
|
272
|
+
const patterns = [];
|
|
273
|
+
let hasProp = false;
|
|
272
274
|
for (const prop of Array.from(struct.props.values())) {
|
|
275
|
+
if (prop.token("identifier")?.pattern) {
|
|
276
|
+
patterns.push(prop);
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
hasProp = true;
|
|
273
280
|
const optional = !!prop.token("optional");
|
|
274
281
|
this.write(wrapProp(prop.id), optional ? "?" : "", ": ");
|
|
275
282
|
this.renderTypeDef(prop.getDefinition());
|
|
276
283
|
this.writeln();
|
|
277
284
|
}
|
|
285
|
+
if (patterns.length) {
|
|
286
|
+
this.write(`[key: string]: `);
|
|
287
|
+
if (hasProp) this.writeln("any");
|
|
288
|
+
else if (patterns.length === 1) {
|
|
289
|
+
this.renderTypeDef(patterns[0].getDefinition());
|
|
290
|
+
this.writeln();
|
|
291
|
+
} else {
|
|
292
|
+
this.indent();
|
|
293
|
+
for (const prop of patterns) {
|
|
294
|
+
this.writeln();
|
|
295
|
+
this.write("| ");
|
|
296
|
+
this.renderTypeDef(prop.getDefinition());
|
|
297
|
+
}
|
|
298
|
+
this.unindent();
|
|
299
|
+
this.writeln();
|
|
300
|
+
}
|
|
301
|
+
}
|
|
278
302
|
if (asClass) {
|
|
279
|
-
this.writeln("static
|
|
303
|
+
this.writeln("static __is_atscript_annotated_type: true");
|
|
280
304
|
this.writeln(`static type: TAtscriptTypeObject<keyof ${asClass}>`);
|
|
281
305
|
this.writeln(`static metadata: TMetadataMap<AtscriptMetadata>`);
|
|
282
|
-
this.writeln(`static validator: () => Validator
|
|
306
|
+
this.writeln(`static validator: <TT extends TAtscriptAnnotatedTypeConstructor = ${asClass}>(opts?: Partial<TValidatorOptions>) => Validator<TT>`);
|
|
283
307
|
}
|
|
284
308
|
this.pop();
|
|
285
309
|
}
|
|
@@ -318,10 +342,10 @@ else if ((0, __atscript_core.isGroup)(realDef)) typeDef = "TAtscriptTypeComplex"
|
|
|
318
342
|
else if ((0, __atscript_core.isArray)(realDef)) typeDef = "TAtscriptTypeArray";
|
|
319
343
|
else if ((0, __atscript_core.isPrimitive)(realDef)) typeDef = "TAtscriptTypeFinal";
|
|
320
344
|
}
|
|
321
|
-
this.writeln(`const
|
|
345
|
+
this.writeln(`const __is_atscript_annotated_type: true`);
|
|
322
346
|
this.writeln(`const type: ${typeDef}`);
|
|
323
347
|
this.writeln(`const metadata: TMetadataMap<AtscriptMetadata>`);
|
|
324
|
-
this.writeln(`const validator:
|
|
348
|
+
this.writeln(`const validator: <TT extends TAtscriptAnnotatedTypeConstructor = ${node.id}>(opts?: Partial<TValidatorOptions>) => Validator<TT>`);
|
|
325
349
|
this.popln();
|
|
326
350
|
}
|
|
327
351
|
renderJsDoc(node) {
|
|
@@ -380,7 +404,7 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
380
404
|
this.write(exported ? "export " : "");
|
|
381
405
|
this.write(`class ${node.id} `);
|
|
382
406
|
this.blockln("{}");
|
|
383
|
-
this.writeln("static
|
|
407
|
+
this.writeln("static __is_atscript_annotated_type = true");
|
|
384
408
|
this.writeln("static type = {}");
|
|
385
409
|
this.writeln("static metadata = new Map()");
|
|
386
410
|
this.popln();
|
|
@@ -393,7 +417,7 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
393
417
|
this.write(exported ? "export " : "");
|
|
394
418
|
this.write(`class ${node.id} `);
|
|
395
419
|
this.blockln("{}");
|
|
396
|
-
this.writeln("static
|
|
420
|
+
this.writeln("static __is_atscript_annotated_type = true");
|
|
397
421
|
this.writeln("static type = {}");
|
|
398
422
|
this.writeln("static metadata = new Map()");
|
|
399
423
|
this.popln();
|
|
@@ -506,6 +530,17 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
506
530
|
this.unindent();
|
|
507
531
|
this.write(`)`);
|
|
508
532
|
}
|
|
533
|
+
for (const [key, propDef] of Object.entries(def.propsPatterns)) {
|
|
534
|
+
const optional = typeof propDef === "object" && propDef.optional;
|
|
535
|
+
this.writeln(`.propPattern(`);
|
|
536
|
+
this.indent();
|
|
537
|
+
this.writeln(`${key},`);
|
|
538
|
+
this.renderPrimitiveDef(propDef);
|
|
539
|
+
if (optional) this.writeln(".optional()");
|
|
540
|
+
this.writeln(".$type");
|
|
541
|
+
this.unindent();
|
|
542
|
+
this.write(`)`);
|
|
543
|
+
}
|
|
509
544
|
this.unindent();
|
|
510
545
|
return;
|
|
511
546
|
default: return this.writeln(`$(${d()}).designType("any")`);
|
|
@@ -514,10 +549,17 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
514
549
|
defineObject(node) {
|
|
515
550
|
const props = Array.from(node.props.values());
|
|
516
551
|
for (const prop of props) {
|
|
552
|
+
const pattern = prop.token("identifier")?.pattern;
|
|
517
553
|
const optional = !!prop.token("optional");
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
554
|
+
if (pattern) {
|
|
555
|
+
this.writeln(`.propPattern(`);
|
|
556
|
+
this.indent();
|
|
557
|
+
this.writeln(`/${pattern.source}/${pattern.flags},`);
|
|
558
|
+
} else {
|
|
559
|
+
this.writeln(`.prop(`);
|
|
560
|
+
this.indent();
|
|
561
|
+
this.writeln(`"${escapeQuotes(prop.id)}",`);
|
|
562
|
+
}
|
|
521
563
|
this.annotateType(prop.getDefinition());
|
|
522
564
|
this.indent().defineMetadata(prop).unindent();
|
|
523
565
|
if (optional) this.writeln(" .optional()");
|
|
@@ -653,7 +695,7 @@ var Validator = class {
|
|
|
653
695
|
error(message, path$2, details) {
|
|
654
696
|
const errors = this.stackErrors[this.stackErrors.length - 1] || this.errors;
|
|
655
697
|
const error = {
|
|
656
|
-
path: path$2 || this.
|
|
698
|
+
path: path$2 || this.path,
|
|
657
699
|
message
|
|
658
700
|
};
|
|
659
701
|
if (details?.length) error.details = details;
|
|
@@ -666,7 +708,7 @@ var Validator = class {
|
|
|
666
708
|
this.push("");
|
|
667
709
|
this.errors = [];
|
|
668
710
|
this.stackErrors = [];
|
|
669
|
-
const passed = this.
|
|
711
|
+
const passed = this.validateSafe(this.def, value);
|
|
670
712
|
this.pop(!passed);
|
|
671
713
|
if (!passed) {
|
|
672
714
|
if (safe) return false;
|
|
@@ -674,10 +716,21 @@ var Validator = class {
|
|
|
674
716
|
}
|
|
675
717
|
return true;
|
|
676
718
|
}
|
|
677
|
-
|
|
719
|
+
validateSafe(def, value) {
|
|
678
720
|
if (this.isLimitExceeded()) return false;
|
|
679
721
|
if (!isAnnotatedType(def)) throw new Error("Can not validate not-annotated type");
|
|
722
|
+
if (typeof this.opts.replace === "function") def = this.opts.replace(def, this.path);
|
|
680
723
|
if (def.optional && value === undefined) return true;
|
|
724
|
+
for (const plugin of this.opts.plugins) {
|
|
725
|
+
const result = plugin(this, def, value);
|
|
726
|
+
if (result === false || result === true) return result;
|
|
727
|
+
}
|
|
728
|
+
return this.validateAnnotatedType(def, value);
|
|
729
|
+
}
|
|
730
|
+
get path() {
|
|
731
|
+
return this.stackPath.slice(1).join(".");
|
|
732
|
+
}
|
|
733
|
+
validateAnnotatedType(def, value) {
|
|
681
734
|
switch (def.type.kind) {
|
|
682
735
|
case "object": return this.validateObject(def, value);
|
|
683
736
|
case "union": return this.validateUnion(def, value);
|
|
@@ -693,7 +746,7 @@ var Validator = class {
|
|
|
693
746
|
const popped = [];
|
|
694
747
|
for (const item of def.type.items) {
|
|
695
748
|
this.push(`[${item.type.kind || item.type.designType}(${i})]`);
|
|
696
|
-
if (this.
|
|
749
|
+
if (this.validateSafe(item, value)) {
|
|
697
750
|
this.pop(false);
|
|
698
751
|
return true;
|
|
699
752
|
}
|
|
@@ -707,7 +760,7 @@ var Validator = class {
|
|
|
707
760
|
return false;
|
|
708
761
|
}
|
|
709
762
|
validateIntersection(def, value) {
|
|
710
|
-
for (const item of def.type.items) if (!this.
|
|
763
|
+
for (const item of def.type.items) if (!this.validateSafe(item, value)) return false;
|
|
711
764
|
return true;
|
|
712
765
|
}
|
|
713
766
|
validateTuple(def, value) {
|
|
@@ -718,7 +771,7 @@ var Validator = class {
|
|
|
718
771
|
let i = 0;
|
|
719
772
|
for (const item of def.type.items) {
|
|
720
773
|
this.push(`[${i}]`);
|
|
721
|
-
if (!this.
|
|
774
|
+
if (!this.validateSafe(item, value[i])) {
|
|
722
775
|
this.pop(true);
|
|
723
776
|
return false;
|
|
724
777
|
}
|
|
@@ -746,7 +799,7 @@ var Validator = class {
|
|
|
746
799
|
let passed = true;
|
|
747
800
|
for (const item of value) {
|
|
748
801
|
this.push(`[${i}]`);
|
|
749
|
-
if (!this.
|
|
802
|
+
if (!this.validateSafe(def.type.of, item)) {
|
|
750
803
|
passed = false;
|
|
751
804
|
this.pop(true);
|
|
752
805
|
if (this.isLimitExceeded()) return false;
|
|
@@ -763,21 +816,55 @@ var Validator = class {
|
|
|
763
816
|
let passed = true;
|
|
764
817
|
const valueKeys = new Set(Object.keys(value));
|
|
765
818
|
const typeKeys = new Set();
|
|
819
|
+
const skipList = new Set();
|
|
820
|
+
if (this.opts.skipList) {
|
|
821
|
+
const path$2 = this.stackPath.length > 1 ? this.path + "." : "";
|
|
822
|
+
this.opts.skipList.forEach((item) => {
|
|
823
|
+
if (item.startsWith(path$2)) {
|
|
824
|
+
const key = item.slice(path$2.length);
|
|
825
|
+
skipList.add(key);
|
|
826
|
+
valueKeys.delete(key);
|
|
827
|
+
}
|
|
828
|
+
});
|
|
829
|
+
}
|
|
830
|
+
let partialFunctionMatched = false;
|
|
831
|
+
if (typeof this.opts.partial === "function") partialFunctionMatched = this.opts.partial(def, this.path);
|
|
766
832
|
for (const [key, item] of def.type.props.entries()) {
|
|
833
|
+
if (skipList.has(key)) continue;
|
|
767
834
|
typeKeys.add(key);
|
|
768
835
|
if (value[key] === undefined) {
|
|
769
|
-
if (this.opts.partial === "deep" || this.opts.partial === true && this.stackPath.length <= 1) continue;
|
|
836
|
+
if (partialFunctionMatched || this.opts.partial === "deep" || this.opts.partial === true && this.stackPath.length <= 1) continue;
|
|
770
837
|
}
|
|
771
838
|
this.push(key);
|
|
772
|
-
if (this.
|
|
839
|
+
if (this.validateSafe(item, value[key])) this.pop(false);
|
|
773
840
|
else {
|
|
774
841
|
passed = false;
|
|
775
842
|
this.pop(true);
|
|
776
843
|
if (this.isLimitExceeded()) return false;
|
|
777
844
|
}
|
|
778
845
|
}
|
|
779
|
-
for (const key of valueKeys)
|
|
780
|
-
|
|
846
|
+
for (const key of valueKeys)
|
|
847
|
+
/** matched patterns for unknown keys */ if (!typeKeys.has(key)) {
|
|
848
|
+
const matched = [];
|
|
849
|
+
for (const { pattern, def: propDef } of def.type.propsPatterns) if (pattern.test(key)) matched.push({
|
|
850
|
+
pattern,
|
|
851
|
+
def: propDef
|
|
852
|
+
});
|
|
853
|
+
if (matched.length) {
|
|
854
|
+
let keyPassed = false;
|
|
855
|
+
for (const { def: def$1 } of matched) if (this.validateSafe(def$1, value[key])) {
|
|
856
|
+
this.pop(false);
|
|
857
|
+
keyPassed = true;
|
|
858
|
+
break;
|
|
859
|
+
}
|
|
860
|
+
if (!keyPassed) {
|
|
861
|
+
this.push(key);
|
|
862
|
+
this.validateSafe(matched[0].def, value[key]);
|
|
863
|
+
this.pop(true);
|
|
864
|
+
passed = false;
|
|
865
|
+
if (this.isLimitExceeded()) return false;
|
|
866
|
+
}
|
|
867
|
+
} else if (this.opts.unknwonProps !== "ignore") {
|
|
781
868
|
if (this.opts.unknwonProps === "error") {
|
|
782
869
|
this.push(key);
|
|
783
870
|
this.error(`Unexpected property`);
|
|
@@ -895,7 +982,8 @@ else {
|
|
|
895
982
|
partial: false,
|
|
896
983
|
unknwonProps: "error",
|
|
897
984
|
errorLimit: 10,
|
|
898
|
-
...opts
|
|
985
|
+
...opts,
|
|
986
|
+
plugins: opts?.plugins || []
|
|
899
987
|
};
|
|
900
988
|
}
|
|
901
989
|
};
|
|
@@ -908,7 +996,7 @@ var ValidatorError = class extends Error {
|
|
|
908
996
|
//#endregion
|
|
909
997
|
//#region packages/typescript/src/annotated-type.ts
|
|
910
998
|
function isAnnotatedType(type) {
|
|
911
|
-
return type && type.
|
|
999
|
+
return type && type.__is_atscript_annotated_type;
|
|
912
1000
|
}
|
|
913
1001
|
function defineAnnotatedType(_kind, base) {
|
|
914
1002
|
const kind = _kind || "";
|
|
@@ -919,11 +1007,14 @@ function defineAnnotatedType(_kind, base) {
|
|
|
919
1007
|
"intersection",
|
|
920
1008
|
"tuple"
|
|
921
1009
|
].includes(kind)) type.items = [];
|
|
922
|
-
if (kind === "object")
|
|
1010
|
+
if (kind === "object") {
|
|
1011
|
+
type.props = new Map();
|
|
1012
|
+
type.propsPatterns = [];
|
|
1013
|
+
}
|
|
923
1014
|
type.tags = new Set();
|
|
924
1015
|
const metadata = base?.metadata || new Map();
|
|
925
1016
|
if (base) Object.assign(base, {
|
|
926
|
-
|
|
1017
|
+
__is_atscript_annotated_type: true,
|
|
927
1018
|
metadata,
|
|
928
1019
|
type,
|
|
929
1020
|
validator(opts) {
|
|
@@ -931,7 +1022,7 @@ function defineAnnotatedType(_kind, base) {
|
|
|
931
1022
|
}
|
|
932
1023
|
});
|
|
933
1024
|
else base = {
|
|
934
|
-
|
|
1025
|
+
__is_atscript_annotated_type: true,
|
|
935
1026
|
metadata,
|
|
936
1027
|
type,
|
|
937
1028
|
validator(opts) {
|
|
@@ -967,8 +1058,19 @@ else base = {
|
|
|
967
1058
|
this.$def.props.set(name, value);
|
|
968
1059
|
return this;
|
|
969
1060
|
},
|
|
970
|
-
|
|
971
|
-
this.$
|
|
1061
|
+
propPattern(pattern, def) {
|
|
1062
|
+
this.$def.propsPatterns.push({
|
|
1063
|
+
pattern,
|
|
1064
|
+
def
|
|
1065
|
+
});
|
|
1066
|
+
return this;
|
|
1067
|
+
},
|
|
1068
|
+
optional(value = true) {
|
|
1069
|
+
this.$type.optional = value;
|
|
1070
|
+
return this;
|
|
1071
|
+
},
|
|
1072
|
+
copyMetadata(fromMetadata, ignore) {
|
|
1073
|
+
for (const [key, value] of fromMetadata.entries()) if (!ignore || !ignore.has(key)) this.$metadata.set(key, value);
|
|
972
1074
|
return this;
|
|
973
1075
|
},
|
|
974
1076
|
refTo(type$1, chain) {
|
|
@@ -978,13 +1080,13 @@ else base = {
|
|
|
978
1080
|
let keys = "";
|
|
979
1081
|
for (const c of chain || []) {
|
|
980
1082
|
keys += `["${c}"]`;
|
|
981
|
-
if (newBase.type.kind === "object") newBase = newBase.type.props.get(c);
|
|
1083
|
+
if (newBase.type.kind === "object" && newBase.type.props.has(c)) newBase = newBase.type.props.get(c);
|
|
982
1084
|
else throw new Error(`Can't find prop ${typeName}${keys}`);
|
|
983
1085
|
}
|
|
984
1086
|
if (!newBase && keys) throw new Error(`Can't find prop ${typeName}${keys}`);
|
|
985
1087
|
else if (!newBase) throw new Error(`"${typeName}" is not annotated type`);
|
|
986
1088
|
this.$type = {
|
|
987
|
-
|
|
1089
|
+
__is_atscript_annotated_type: true,
|
|
988
1090
|
type: newBase.type,
|
|
989
1091
|
metadata,
|
|
990
1092
|
validator(opts) {
|
|
@@ -1006,6 +1108,19 @@ else this.$metadata.set(key, value);
|
|
|
1006
1108
|
};
|
|
1007
1109
|
return handle;
|
|
1008
1110
|
}
|
|
1111
|
+
function isAnnotatedTypeOfPrimitive(t) {
|
|
1112
|
+
if (["array", "object"].includes(t.type.kind)) return false;
|
|
1113
|
+
if (!t.type.kind) return true;
|
|
1114
|
+
if ([
|
|
1115
|
+
"union",
|
|
1116
|
+
"tuple",
|
|
1117
|
+
"intersection"
|
|
1118
|
+
].includes(t.type.kind)) {
|
|
1119
|
+
for (const item of t.type.items) if (!isAnnotatedTypeOfPrimitive(item)) return false;
|
|
1120
|
+
return true;
|
|
1121
|
+
}
|
|
1122
|
+
return false;
|
|
1123
|
+
}
|
|
1009
1124
|
|
|
1010
1125
|
//#endregion
|
|
1011
1126
|
//#region packages/typescript/src/index.ts
|
|
@@ -1016,4 +1131,5 @@ exports.Validator = Validator
|
|
|
1016
1131
|
exports.ValidatorError = ValidatorError
|
|
1017
1132
|
exports.default = src_default
|
|
1018
1133
|
exports.defineAnnotatedType = defineAnnotatedType
|
|
1019
|
-
exports.isAnnotatedType = isAnnotatedType
|
|
1134
|
+
exports.isAnnotatedType = isAnnotatedType
|
|
1135
|
+
exports.isAnnotatedTypeOfPrimitive = isAnnotatedTypeOfPrimitive
|
package/dist/index.d.ts
CHANGED
|
@@ -7,15 +7,25 @@ interface TError {
|
|
|
7
7
|
message: string;
|
|
8
8
|
details?: TError[];
|
|
9
9
|
}
|
|
10
|
+
type TValidatorPlugin = (ctx: TValidatorPluginContext, def: TAtscriptAnnotatedType, value: any) => boolean | undefined;
|
|
10
11
|
interface TValidatorOptions {
|
|
11
|
-
partial: boolean | 'deep';
|
|
12
|
+
partial: boolean | 'deep' | ((type: TAtscriptAnnotatedType<TAtscriptTypeObject>, path: string) => boolean);
|
|
13
|
+
replace?: (type: TAtscriptAnnotatedType, path: string) => TAtscriptAnnotatedType;
|
|
14
|
+
plugins: TValidatorPlugin[];
|
|
12
15
|
unknwonProps: 'strip' | 'ignore' | 'error';
|
|
13
16
|
errorLimit: number;
|
|
17
|
+
skipList?: Set<string>;
|
|
18
|
+
}
|
|
19
|
+
interface TValidatorPluginContext {
|
|
20
|
+
opts: Validator<any>['opts'];
|
|
21
|
+
validateAnnotatedType: Validator<any>['validateAnnotatedType'];
|
|
22
|
+
error: Validator<any>['error'];
|
|
23
|
+
path: Validator<any>['path'];
|
|
14
24
|
}
|
|
15
25
|
declare class Validator<T extends TAtscriptAnnotatedTypeConstructor> {
|
|
16
|
-
protected readonly def: T
|
|
26
|
+
protected readonly def: T | TAtscriptAnnotatedType<any>;
|
|
17
27
|
protected opts: TValidatorOptions;
|
|
18
|
-
constructor(def: T
|
|
28
|
+
constructor(def: T | TAtscriptAnnotatedType<any>, opts?: Partial<TValidatorOptions>);
|
|
19
29
|
errors: TError[];
|
|
20
30
|
protected stackErrors: TError[][];
|
|
21
31
|
protected stackPath: string[];
|
|
@@ -25,8 +35,10 @@ declare class Validator<T extends TAtscriptAnnotatedTypeConstructor> {
|
|
|
25
35
|
protected clear(): void;
|
|
26
36
|
protected error(message: string, path?: string, details?: TError[]): void;
|
|
27
37
|
protected throw(): void;
|
|
28
|
-
validate<TT =
|
|
29
|
-
protected
|
|
38
|
+
validate<TT = T>(value: any, safe?: boolean): value is TT;
|
|
39
|
+
protected validateSafe(def: TAtscriptAnnotatedType, value: any): boolean;
|
|
40
|
+
protected get path(): string;
|
|
41
|
+
protected validateAnnotatedType(def: TAtscriptAnnotatedType, value: any): boolean;
|
|
30
42
|
protected validateUnion(def: TAtscriptAnnotatedType<TAtscriptTypeComplex>, value: any): boolean;
|
|
31
43
|
protected validateIntersection(def: TAtscriptAnnotatedType<TAtscriptTypeComplex>, value: any): boolean;
|
|
32
44
|
protected validateTuple(def: TAtscriptAnnotatedType<TAtscriptTypeComplex>, value: any): boolean;
|
|
@@ -55,45 +67,67 @@ interface TAtscriptTypeArray {
|
|
|
55
67
|
interface TAtscriptTypeObject<K extends string = string> {
|
|
56
68
|
kind: 'object';
|
|
57
69
|
props: Map<K, TAtscriptAnnotatedType>;
|
|
70
|
+
propsPatterns: {
|
|
71
|
+
pattern: RegExp;
|
|
72
|
+
def: TAtscriptAnnotatedType;
|
|
73
|
+
}[];
|
|
58
74
|
tags: Set<AtscriptPrimitiveTags>;
|
|
59
75
|
}
|
|
60
76
|
interface TAtscriptTypeFinal {
|
|
61
77
|
kind: '';
|
|
78
|
+
/**
|
|
79
|
+
* design type
|
|
80
|
+
*/
|
|
62
81
|
designType: 'string' | 'number' | 'boolean' | 'undefined' | 'null' | 'object' | 'any' | 'never';
|
|
82
|
+
/**
|
|
83
|
+
* value for literals
|
|
84
|
+
*/
|
|
63
85
|
value?: string | number | boolean;
|
|
64
86
|
tags: Set<AtscriptPrimitiveTags>;
|
|
65
87
|
}
|
|
66
88
|
type TAtscriptTypeDef = TAtscriptTypeComplex | TAtscriptTypeFinal | TAtscriptTypeArray | TAtscriptTypeObject<string>;
|
|
67
89
|
interface TAtscriptAnnotatedType<T = TAtscriptTypeDef> {
|
|
68
|
-
|
|
90
|
+
__is_atscript_annotated_type: true;
|
|
69
91
|
type: T;
|
|
70
|
-
validator: (opts?: TValidatorOptions) => Validator<
|
|
92
|
+
validator: <TT extends TAtscriptAnnotatedTypeConstructor>(opts?: Partial<TValidatorOptions>) => Validator<TT>;
|
|
71
93
|
metadata: TMetadataMap<AtscriptMetadata>;
|
|
72
94
|
optional?: boolean;
|
|
73
95
|
}
|
|
74
96
|
type TAtscriptAnnotatedTypeConstructor = TAtscriptAnnotatedType & (new (...args: any[]) => any);
|
|
97
|
+
/**
|
|
98
|
+
* Type Guard to check if a type is atscript-annotated
|
|
99
|
+
*/
|
|
75
100
|
declare function isAnnotatedType(type: any): type is TAtscriptAnnotatedType;
|
|
76
101
|
type TKind = '' | 'array' | 'object' | 'union' | 'intersection' | 'tuple';
|
|
77
|
-
declare function defineAnnotatedType(_kind?: TKind, base?: any):
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
} & Omit<TAtscriptTypeComplex, "kind"> & Omit<TAtscriptTypeFinal, "kind"> & Omit<TAtscriptTypeArray, "kind"> & Omit<TAtscriptTypeObject<string>, "kind">;
|
|
82
|
-
$metadata: Map<string, unknown>;
|
|
83
|
-
_existingObject: TAtscriptAnnotatedType | undefined;
|
|
84
|
-
tags(...tags: string[]): any;
|
|
85
|
-
designType(value: TAtscriptTypeFinal["designType"]): any;
|
|
86
|
-
value(value: string | number | boolean): any;
|
|
87
|
-
of(value: TAtscriptAnnotatedType): any;
|
|
88
|
-
item(value: TAtscriptAnnotatedType): any;
|
|
89
|
-
prop(name: string, value: TAtscriptAnnotatedType): any;
|
|
90
|
-
optional(): any;
|
|
91
|
-
refTo(type: any, chain?: string[]): any;
|
|
92
|
-
annotate(key: string, value: any, asArray?: boolean): any;
|
|
93
|
-
};
|
|
102
|
+
declare function defineAnnotatedType(_kind?: TKind, base?: any): TAnnotatedTypeHandle;
|
|
103
|
+
/**
|
|
104
|
+
* Atscript Metadata Map with typed setters/getters
|
|
105
|
+
*/
|
|
94
106
|
interface TMetadataMap<O extends object> extends Map<keyof O, O[keyof O]> {
|
|
95
107
|
get<K extends keyof O>(key: K): O[K] | undefined;
|
|
96
108
|
set<K extends keyof O>(key: K, value: O[K]): this;
|
|
97
109
|
}
|
|
110
|
+
interface TAnnotatedTypeHandle {
|
|
111
|
+
$type: TAtscriptAnnotatedType;
|
|
112
|
+
$def: {
|
|
113
|
+
kind: TKind;
|
|
114
|
+
} & Omit<TAtscriptTypeComplex, 'kind'> & Omit<TAtscriptTypeFinal, 'kind'> & Omit<TAtscriptTypeArray, 'kind'> & Omit<TAtscriptTypeObject<string>, 'kind'>;
|
|
115
|
+
$metadata: TMetadataMap<AtscriptMetadata>;
|
|
116
|
+
_existingObject: TAtscriptAnnotatedType | undefined;
|
|
117
|
+
tags(...tags: string[]): TAnnotatedTypeHandle;
|
|
118
|
+
designType(value: TAtscriptTypeFinal['designType']): TAnnotatedTypeHandle;
|
|
119
|
+
value(value: string | number | boolean): TAnnotatedTypeHandle;
|
|
120
|
+
of(value: TAtscriptAnnotatedType): TAnnotatedTypeHandle;
|
|
121
|
+
item(value: TAtscriptAnnotatedType): TAnnotatedTypeHandle;
|
|
122
|
+
prop(name: string, value: TAtscriptAnnotatedType): TAnnotatedTypeHandle;
|
|
123
|
+
propPattern(pattern: RegExp, value: TAtscriptAnnotatedType): TAnnotatedTypeHandle;
|
|
124
|
+
optional(value?: boolean): TAnnotatedTypeHandle;
|
|
125
|
+
copyMetadata(fromMetadata: TMetadataMap<AtscriptMetadata>): TAnnotatedTypeHandle;
|
|
126
|
+
refTo(type: TAtscriptAnnotatedType & {
|
|
127
|
+
name?: string;
|
|
128
|
+
}, chain?: string[]): TAnnotatedTypeHandle;
|
|
129
|
+
annotate(key: keyof AtscriptMetadata, value: any, asArray?: boolean): TAnnotatedTypeHandle;
|
|
130
|
+
}
|
|
131
|
+
declare function isAnnotatedTypeOfPrimitive(t: TAtscriptAnnotatedType): boolean;
|
|
98
132
|
|
|
99
|
-
export { type TAtscriptAnnotatedType, type TAtscriptAnnotatedTypeConstructor, type TAtscriptTypeArray, type TAtscriptTypeComplex, type TAtscriptTypeDef, type TAtscriptTypeFinal, type TAtscriptTypeObject, type TMetadataMap, type TValidatorOptions, Validator, ValidatorError, tsPlugin as default, defineAnnotatedType, isAnnotatedType };
|
|
133
|
+
export { type TAnnotatedTypeHandle, type TAtscriptAnnotatedType, type TAtscriptAnnotatedTypeConstructor, type TAtscriptTypeArray, type TAtscriptTypeComplex, type TAtscriptTypeDef, type TAtscriptTypeFinal, type TAtscriptTypeObject, type TMetadataMap, type TValidatorOptions, type TValidatorPlugin, type TValidatorPluginContext, Validator, ValidatorError, tsPlugin as default, defineAnnotatedType, isAnnotatedType, isAnnotatedTypeOfPrimitive };
|
package/dist/index.mjs
CHANGED
|
@@ -177,7 +177,7 @@ function wrapProp(name) {
|
|
|
177
177
|
return name;
|
|
178
178
|
}
|
|
179
179
|
function escapeQuotes(str) {
|
|
180
|
-
return str.replace(/"/g, "\\\"");
|
|
180
|
+
return str.replace(/"/g, "\\\"").replace(/\\/g, "\\\\");
|
|
181
181
|
}
|
|
182
182
|
|
|
183
183
|
//#endregion
|
|
@@ -192,7 +192,7 @@ var TypeRenderer = class extends BaseRenderer {
|
|
|
192
192
|
this.writeln(" * Do not edit this file!");
|
|
193
193
|
this.writeln(" */");
|
|
194
194
|
this.writeln();
|
|
195
|
-
this.writeln("import type { TAtscriptTypeObject, TAtscriptTypeComplex, TAtscriptTypeFinal, TAtscriptTypeArray, TMetadataMap, Validator } from \"@atscript/typescript\"");
|
|
195
|
+
this.writeln("import type { TAtscriptTypeObject, TAtscriptTypeComplex, TAtscriptTypeFinal, TAtscriptTypeArray, TMetadataMap, Validator, TAtscriptAnnotatedTypeConstructor, TValidatorOptions } from \"@atscript/typescript\"");
|
|
196
196
|
}
|
|
197
197
|
post() {
|
|
198
198
|
this.writeln("// prettier-ignore-end");
|
|
@@ -244,17 +244,41 @@ var TypeRenderer = class extends BaseRenderer {
|
|
|
244
244
|
}
|
|
245
245
|
renderStructure(struct, asClass) {
|
|
246
246
|
this.blockln("{}");
|
|
247
|
+
const patterns = [];
|
|
248
|
+
let hasProp = false;
|
|
247
249
|
for (const prop of Array.from(struct.props.values())) {
|
|
250
|
+
if (prop.token("identifier")?.pattern) {
|
|
251
|
+
patterns.push(prop);
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
hasProp = true;
|
|
248
255
|
const optional = !!prop.token("optional");
|
|
249
256
|
this.write(wrapProp(prop.id), optional ? "?" : "", ": ");
|
|
250
257
|
this.renderTypeDef(prop.getDefinition());
|
|
251
258
|
this.writeln();
|
|
252
259
|
}
|
|
260
|
+
if (patterns.length) {
|
|
261
|
+
this.write(`[key: string]: `);
|
|
262
|
+
if (hasProp) this.writeln("any");
|
|
263
|
+
else if (patterns.length === 1) {
|
|
264
|
+
this.renderTypeDef(patterns[0].getDefinition());
|
|
265
|
+
this.writeln();
|
|
266
|
+
} else {
|
|
267
|
+
this.indent();
|
|
268
|
+
for (const prop of patterns) {
|
|
269
|
+
this.writeln();
|
|
270
|
+
this.write("| ");
|
|
271
|
+
this.renderTypeDef(prop.getDefinition());
|
|
272
|
+
}
|
|
273
|
+
this.unindent();
|
|
274
|
+
this.writeln();
|
|
275
|
+
}
|
|
276
|
+
}
|
|
253
277
|
if (asClass) {
|
|
254
|
-
this.writeln("static
|
|
278
|
+
this.writeln("static __is_atscript_annotated_type: true");
|
|
255
279
|
this.writeln(`static type: TAtscriptTypeObject<keyof ${asClass}>`);
|
|
256
280
|
this.writeln(`static metadata: TMetadataMap<AtscriptMetadata>`);
|
|
257
|
-
this.writeln(`static validator: () => Validator
|
|
281
|
+
this.writeln(`static validator: <TT extends TAtscriptAnnotatedTypeConstructor = ${asClass}>(opts?: Partial<TValidatorOptions>) => Validator<TT>`);
|
|
258
282
|
}
|
|
259
283
|
this.pop();
|
|
260
284
|
}
|
|
@@ -293,10 +317,10 @@ else if (isGroup(realDef)) typeDef = "TAtscriptTypeComplex";
|
|
|
293
317
|
else if (isArray(realDef)) typeDef = "TAtscriptTypeArray";
|
|
294
318
|
else if (isPrimitive(realDef)) typeDef = "TAtscriptTypeFinal";
|
|
295
319
|
}
|
|
296
|
-
this.writeln(`const
|
|
320
|
+
this.writeln(`const __is_atscript_annotated_type: true`);
|
|
297
321
|
this.writeln(`const type: ${typeDef}`);
|
|
298
322
|
this.writeln(`const metadata: TMetadataMap<AtscriptMetadata>`);
|
|
299
|
-
this.writeln(`const validator:
|
|
323
|
+
this.writeln(`const validator: <TT extends TAtscriptAnnotatedTypeConstructor = ${node.id}>(opts?: Partial<TValidatorOptions>) => Validator<TT>`);
|
|
300
324
|
this.popln();
|
|
301
325
|
}
|
|
302
326
|
renderJsDoc(node) {
|
|
@@ -355,7 +379,7 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
355
379
|
this.write(exported ? "export " : "");
|
|
356
380
|
this.write(`class ${node.id} `);
|
|
357
381
|
this.blockln("{}");
|
|
358
|
-
this.writeln("static
|
|
382
|
+
this.writeln("static __is_atscript_annotated_type = true");
|
|
359
383
|
this.writeln("static type = {}");
|
|
360
384
|
this.writeln("static metadata = new Map()");
|
|
361
385
|
this.popln();
|
|
@@ -368,7 +392,7 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
368
392
|
this.write(exported ? "export " : "");
|
|
369
393
|
this.write(`class ${node.id} `);
|
|
370
394
|
this.blockln("{}");
|
|
371
|
-
this.writeln("static
|
|
395
|
+
this.writeln("static __is_atscript_annotated_type = true");
|
|
372
396
|
this.writeln("static type = {}");
|
|
373
397
|
this.writeln("static metadata = new Map()");
|
|
374
398
|
this.popln();
|
|
@@ -481,6 +505,17 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
481
505
|
this.unindent();
|
|
482
506
|
this.write(`)`);
|
|
483
507
|
}
|
|
508
|
+
for (const [key, propDef] of Object.entries(def.propsPatterns)) {
|
|
509
|
+
const optional = typeof propDef === "object" && propDef.optional;
|
|
510
|
+
this.writeln(`.propPattern(`);
|
|
511
|
+
this.indent();
|
|
512
|
+
this.writeln(`${key},`);
|
|
513
|
+
this.renderPrimitiveDef(propDef);
|
|
514
|
+
if (optional) this.writeln(".optional()");
|
|
515
|
+
this.writeln(".$type");
|
|
516
|
+
this.unindent();
|
|
517
|
+
this.write(`)`);
|
|
518
|
+
}
|
|
484
519
|
this.unindent();
|
|
485
520
|
return;
|
|
486
521
|
default: return this.writeln(`$(${d()}).designType("any")`);
|
|
@@ -489,10 +524,17 @@ var JsRenderer = class extends BaseRenderer {
|
|
|
489
524
|
defineObject(node) {
|
|
490
525
|
const props = Array.from(node.props.values());
|
|
491
526
|
for (const prop of props) {
|
|
527
|
+
const pattern = prop.token("identifier")?.pattern;
|
|
492
528
|
const optional = !!prop.token("optional");
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
529
|
+
if (pattern) {
|
|
530
|
+
this.writeln(`.propPattern(`);
|
|
531
|
+
this.indent();
|
|
532
|
+
this.writeln(`/${pattern.source}/${pattern.flags},`);
|
|
533
|
+
} else {
|
|
534
|
+
this.writeln(`.prop(`);
|
|
535
|
+
this.indent();
|
|
536
|
+
this.writeln(`"${escapeQuotes(prop.id)}",`);
|
|
537
|
+
}
|
|
496
538
|
this.annotateType(prop.getDefinition());
|
|
497
539
|
this.indent().defineMetadata(prop).unindent();
|
|
498
540
|
if (optional) this.writeln(" .optional()");
|
|
@@ -628,7 +670,7 @@ var Validator = class {
|
|
|
628
670
|
error(message, path$1, details) {
|
|
629
671
|
const errors = this.stackErrors[this.stackErrors.length - 1] || this.errors;
|
|
630
672
|
const error = {
|
|
631
|
-
path: path$1 || this.
|
|
673
|
+
path: path$1 || this.path,
|
|
632
674
|
message
|
|
633
675
|
};
|
|
634
676
|
if (details?.length) error.details = details;
|
|
@@ -641,7 +683,7 @@ var Validator = class {
|
|
|
641
683
|
this.push("");
|
|
642
684
|
this.errors = [];
|
|
643
685
|
this.stackErrors = [];
|
|
644
|
-
const passed = this.
|
|
686
|
+
const passed = this.validateSafe(this.def, value);
|
|
645
687
|
this.pop(!passed);
|
|
646
688
|
if (!passed) {
|
|
647
689
|
if (safe) return false;
|
|
@@ -649,10 +691,21 @@ var Validator = class {
|
|
|
649
691
|
}
|
|
650
692
|
return true;
|
|
651
693
|
}
|
|
652
|
-
|
|
694
|
+
validateSafe(def, value) {
|
|
653
695
|
if (this.isLimitExceeded()) return false;
|
|
654
696
|
if (!isAnnotatedType(def)) throw new Error("Can not validate not-annotated type");
|
|
697
|
+
if (typeof this.opts.replace === "function") def = this.opts.replace(def, this.path);
|
|
655
698
|
if (def.optional && value === undefined) return true;
|
|
699
|
+
for (const plugin of this.opts.plugins) {
|
|
700
|
+
const result = plugin(this, def, value);
|
|
701
|
+
if (result === false || result === true) return result;
|
|
702
|
+
}
|
|
703
|
+
return this.validateAnnotatedType(def, value);
|
|
704
|
+
}
|
|
705
|
+
get path() {
|
|
706
|
+
return this.stackPath.slice(1).join(".");
|
|
707
|
+
}
|
|
708
|
+
validateAnnotatedType(def, value) {
|
|
656
709
|
switch (def.type.kind) {
|
|
657
710
|
case "object": return this.validateObject(def, value);
|
|
658
711
|
case "union": return this.validateUnion(def, value);
|
|
@@ -668,7 +721,7 @@ var Validator = class {
|
|
|
668
721
|
const popped = [];
|
|
669
722
|
for (const item of def.type.items) {
|
|
670
723
|
this.push(`[${item.type.kind || item.type.designType}(${i})]`);
|
|
671
|
-
if (this.
|
|
724
|
+
if (this.validateSafe(item, value)) {
|
|
672
725
|
this.pop(false);
|
|
673
726
|
return true;
|
|
674
727
|
}
|
|
@@ -682,7 +735,7 @@ var Validator = class {
|
|
|
682
735
|
return false;
|
|
683
736
|
}
|
|
684
737
|
validateIntersection(def, value) {
|
|
685
|
-
for (const item of def.type.items) if (!this.
|
|
738
|
+
for (const item of def.type.items) if (!this.validateSafe(item, value)) return false;
|
|
686
739
|
return true;
|
|
687
740
|
}
|
|
688
741
|
validateTuple(def, value) {
|
|
@@ -693,7 +746,7 @@ var Validator = class {
|
|
|
693
746
|
let i = 0;
|
|
694
747
|
for (const item of def.type.items) {
|
|
695
748
|
this.push(`[${i}]`);
|
|
696
|
-
if (!this.
|
|
749
|
+
if (!this.validateSafe(item, value[i])) {
|
|
697
750
|
this.pop(true);
|
|
698
751
|
return false;
|
|
699
752
|
}
|
|
@@ -721,7 +774,7 @@ var Validator = class {
|
|
|
721
774
|
let passed = true;
|
|
722
775
|
for (const item of value) {
|
|
723
776
|
this.push(`[${i}]`);
|
|
724
|
-
if (!this.
|
|
777
|
+
if (!this.validateSafe(def.type.of, item)) {
|
|
725
778
|
passed = false;
|
|
726
779
|
this.pop(true);
|
|
727
780
|
if (this.isLimitExceeded()) return false;
|
|
@@ -738,21 +791,55 @@ var Validator = class {
|
|
|
738
791
|
let passed = true;
|
|
739
792
|
const valueKeys = new Set(Object.keys(value));
|
|
740
793
|
const typeKeys = new Set();
|
|
794
|
+
const skipList = new Set();
|
|
795
|
+
if (this.opts.skipList) {
|
|
796
|
+
const path$1 = this.stackPath.length > 1 ? this.path + "." : "";
|
|
797
|
+
this.opts.skipList.forEach((item) => {
|
|
798
|
+
if (item.startsWith(path$1)) {
|
|
799
|
+
const key = item.slice(path$1.length);
|
|
800
|
+
skipList.add(key);
|
|
801
|
+
valueKeys.delete(key);
|
|
802
|
+
}
|
|
803
|
+
});
|
|
804
|
+
}
|
|
805
|
+
let partialFunctionMatched = false;
|
|
806
|
+
if (typeof this.opts.partial === "function") partialFunctionMatched = this.opts.partial(def, this.path);
|
|
741
807
|
for (const [key, item] of def.type.props.entries()) {
|
|
808
|
+
if (skipList.has(key)) continue;
|
|
742
809
|
typeKeys.add(key);
|
|
743
810
|
if (value[key] === undefined) {
|
|
744
|
-
if (this.opts.partial === "deep" || this.opts.partial === true && this.stackPath.length <= 1) continue;
|
|
811
|
+
if (partialFunctionMatched || this.opts.partial === "deep" || this.opts.partial === true && this.stackPath.length <= 1) continue;
|
|
745
812
|
}
|
|
746
813
|
this.push(key);
|
|
747
|
-
if (this.
|
|
814
|
+
if (this.validateSafe(item, value[key])) this.pop(false);
|
|
748
815
|
else {
|
|
749
816
|
passed = false;
|
|
750
817
|
this.pop(true);
|
|
751
818
|
if (this.isLimitExceeded()) return false;
|
|
752
819
|
}
|
|
753
820
|
}
|
|
754
|
-
for (const key of valueKeys)
|
|
755
|
-
|
|
821
|
+
for (const key of valueKeys)
|
|
822
|
+
/** matched patterns for unknown keys */ if (!typeKeys.has(key)) {
|
|
823
|
+
const matched = [];
|
|
824
|
+
for (const { pattern, def: propDef } of def.type.propsPatterns) if (pattern.test(key)) matched.push({
|
|
825
|
+
pattern,
|
|
826
|
+
def: propDef
|
|
827
|
+
});
|
|
828
|
+
if (matched.length) {
|
|
829
|
+
let keyPassed = false;
|
|
830
|
+
for (const { def: def$1 } of matched) if (this.validateSafe(def$1, value[key])) {
|
|
831
|
+
this.pop(false);
|
|
832
|
+
keyPassed = true;
|
|
833
|
+
break;
|
|
834
|
+
}
|
|
835
|
+
if (!keyPassed) {
|
|
836
|
+
this.push(key);
|
|
837
|
+
this.validateSafe(matched[0].def, value[key]);
|
|
838
|
+
this.pop(true);
|
|
839
|
+
passed = false;
|
|
840
|
+
if (this.isLimitExceeded()) return false;
|
|
841
|
+
}
|
|
842
|
+
} else if (this.opts.unknwonProps !== "ignore") {
|
|
756
843
|
if (this.opts.unknwonProps === "error") {
|
|
757
844
|
this.push(key);
|
|
758
845
|
this.error(`Unexpected property`);
|
|
@@ -870,7 +957,8 @@ else {
|
|
|
870
957
|
partial: false,
|
|
871
958
|
unknwonProps: "error",
|
|
872
959
|
errorLimit: 10,
|
|
873
|
-
...opts
|
|
960
|
+
...opts,
|
|
961
|
+
plugins: opts?.plugins || []
|
|
874
962
|
};
|
|
875
963
|
}
|
|
876
964
|
};
|
|
@@ -883,7 +971,7 @@ var ValidatorError = class extends Error {
|
|
|
883
971
|
//#endregion
|
|
884
972
|
//#region packages/typescript/src/annotated-type.ts
|
|
885
973
|
function isAnnotatedType(type) {
|
|
886
|
-
return type && type.
|
|
974
|
+
return type && type.__is_atscript_annotated_type;
|
|
887
975
|
}
|
|
888
976
|
function defineAnnotatedType(_kind, base) {
|
|
889
977
|
const kind = _kind || "";
|
|
@@ -894,11 +982,14 @@ function defineAnnotatedType(_kind, base) {
|
|
|
894
982
|
"intersection",
|
|
895
983
|
"tuple"
|
|
896
984
|
].includes(kind)) type.items = [];
|
|
897
|
-
if (kind === "object")
|
|
985
|
+
if (kind === "object") {
|
|
986
|
+
type.props = new Map();
|
|
987
|
+
type.propsPatterns = [];
|
|
988
|
+
}
|
|
898
989
|
type.tags = new Set();
|
|
899
990
|
const metadata = base?.metadata || new Map();
|
|
900
991
|
if (base) Object.assign(base, {
|
|
901
|
-
|
|
992
|
+
__is_atscript_annotated_type: true,
|
|
902
993
|
metadata,
|
|
903
994
|
type,
|
|
904
995
|
validator(opts) {
|
|
@@ -906,7 +997,7 @@ function defineAnnotatedType(_kind, base) {
|
|
|
906
997
|
}
|
|
907
998
|
});
|
|
908
999
|
else base = {
|
|
909
|
-
|
|
1000
|
+
__is_atscript_annotated_type: true,
|
|
910
1001
|
metadata,
|
|
911
1002
|
type,
|
|
912
1003
|
validator(opts) {
|
|
@@ -942,8 +1033,19 @@ else base = {
|
|
|
942
1033
|
this.$def.props.set(name, value);
|
|
943
1034
|
return this;
|
|
944
1035
|
},
|
|
945
|
-
|
|
946
|
-
this.$
|
|
1036
|
+
propPattern(pattern, def) {
|
|
1037
|
+
this.$def.propsPatterns.push({
|
|
1038
|
+
pattern,
|
|
1039
|
+
def
|
|
1040
|
+
});
|
|
1041
|
+
return this;
|
|
1042
|
+
},
|
|
1043
|
+
optional(value = true) {
|
|
1044
|
+
this.$type.optional = value;
|
|
1045
|
+
return this;
|
|
1046
|
+
},
|
|
1047
|
+
copyMetadata(fromMetadata, ignore) {
|
|
1048
|
+
for (const [key, value] of fromMetadata.entries()) if (!ignore || !ignore.has(key)) this.$metadata.set(key, value);
|
|
947
1049
|
return this;
|
|
948
1050
|
},
|
|
949
1051
|
refTo(type$1, chain) {
|
|
@@ -953,13 +1055,13 @@ else base = {
|
|
|
953
1055
|
let keys = "";
|
|
954
1056
|
for (const c of chain || []) {
|
|
955
1057
|
keys += `["${c}"]`;
|
|
956
|
-
if (newBase.type.kind === "object") newBase = newBase.type.props.get(c);
|
|
1058
|
+
if (newBase.type.kind === "object" && newBase.type.props.has(c)) newBase = newBase.type.props.get(c);
|
|
957
1059
|
else throw new Error(`Can't find prop ${typeName}${keys}`);
|
|
958
1060
|
}
|
|
959
1061
|
if (!newBase && keys) throw new Error(`Can't find prop ${typeName}${keys}`);
|
|
960
1062
|
else if (!newBase) throw new Error(`"${typeName}" is not annotated type`);
|
|
961
1063
|
this.$type = {
|
|
962
|
-
|
|
1064
|
+
__is_atscript_annotated_type: true,
|
|
963
1065
|
type: newBase.type,
|
|
964
1066
|
metadata,
|
|
965
1067
|
validator(opts) {
|
|
@@ -981,10 +1083,23 @@ else this.$metadata.set(key, value);
|
|
|
981
1083
|
};
|
|
982
1084
|
return handle;
|
|
983
1085
|
}
|
|
1086
|
+
function isAnnotatedTypeOfPrimitive(t) {
|
|
1087
|
+
if (["array", "object"].includes(t.type.kind)) return false;
|
|
1088
|
+
if (!t.type.kind) return true;
|
|
1089
|
+
if ([
|
|
1090
|
+
"union",
|
|
1091
|
+
"tuple",
|
|
1092
|
+
"intersection"
|
|
1093
|
+
].includes(t.type.kind)) {
|
|
1094
|
+
for (const item of t.type.items) if (!isAnnotatedTypeOfPrimitive(item)) return false;
|
|
1095
|
+
return true;
|
|
1096
|
+
}
|
|
1097
|
+
return false;
|
|
1098
|
+
}
|
|
984
1099
|
|
|
985
1100
|
//#endregion
|
|
986
1101
|
//#region packages/typescript/src/index.ts
|
|
987
1102
|
var src_default = tsPlugin;
|
|
988
1103
|
|
|
989
1104
|
//#endregion
|
|
990
|
-
export { Validator, ValidatorError, src_default as default, defineAnnotatedType, isAnnotatedType };
|
|
1105
|
+
export { Validator, ValidatorError, src_default as default, defineAnnotatedType, isAnnotatedType, isAnnotatedTypeOfPrimitive };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atscript/typescript",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.18",
|
|
4
4
|
"description": "Atscript: typescript-gen support.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.mjs",
|
|
@@ -46,13 +46,15 @@
|
|
|
46
46
|
},
|
|
47
47
|
"homepage": "https://github.com/moostjs/atscript/tree/main/packages/typescript#readme",
|
|
48
48
|
"license": "ISC",
|
|
49
|
+
"peerDependencies": {
|
|
50
|
+
"@atscript/core": "^0.0.18"
|
|
51
|
+
},
|
|
49
52
|
"dependencies": {
|
|
50
|
-
"@moostjs/event-cli": "^0.5.
|
|
51
|
-
"moost": "^0.5.
|
|
52
|
-
"@atscript/core": "^0.0.16"
|
|
53
|
+
"@moostjs/event-cli": "^0.5.30",
|
|
54
|
+
"moost": "^0.5.30"
|
|
53
55
|
},
|
|
54
56
|
"devDependencies": {
|
|
55
|
-
"vitest": "
|
|
57
|
+
"vitest": "3.2.4"
|
|
56
58
|
},
|
|
57
59
|
"scripts": {
|
|
58
60
|
"pub": "pnpm publish --access public",
|