Skip to content

Commit f42b8c4

Browse files
committed
Refactor code and add support for more actions
1 parent 8661538 commit f42b8c4

File tree

6 files changed

+242
-113
lines changed

6 files changed

+242
-113
lines changed

library/eslint.config.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export default tseslint.config(
6161
},
6262
],
6363

64-
// Regexp
64+
// RegExp
6565
'regexp/no-super-linear-move': 'error', // Prevent DoS regexps
6666
'regexp/no-control-character': 'error', // Avoid unneeded regexps characters
6767
'regexp/no-octal': 'error', // Avoid unneeded regexps characters
@@ -91,6 +91,9 @@ export default tseslint.config(
9191
'jsdoc/require-param-type': 'off',
9292
'jsdoc/require-returns-type': 'off',
9393

94+
// RegExp
95+
'regexp/use-ignore-case': 'off', // We sometimes don't use the i flag for a better JSON Schema compatibility
96+
9497
// Security
9598
'security/detect-object-injection': 'off', // Too many false positives
9699
'security/detect-unsafe-regex': 'off', // Too many false positives, see https://github.com/eslint-community/eslint-plugin-security/issues/28 - we use the redos-detector plugin instead

library/src/actions/octal/octal.test.ts

-9
Original file line numberDiff line numberDiff line change
@@ -89,15 +89,6 @@ describe('octal', () => {
8989
'0o1234567',
9090
]);
9191
});
92-
93-
test('for 0O prefix', () => {
94-
expectNoActionIssue(action, [
95-
'0O000000000000000',
96-
'0O777777777777',
97-
'0O01234567',
98-
'0O1234567',
99-
]);
100-
});
10192
});
10293

10394
describe('should return dataset with issues', () => {

library/src/regex.ts

+10-4
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,18 @@ export const EMOJI_REGEX: RegExp =
4242

4343
/**
4444
* [Hexadecimal](https://en.wikipedia.org/wiki/Hexadecimal) regex.
45+
*
46+
* Hint: We decided against the `i` flag for better JSON Schema compatibility.
4547
*/
46-
export const HEXADECIMAL_REGEX: RegExp = /^(?:0[hx])?[\da-f]+$/iu;
48+
export const HEXADECIMAL_REGEX: RegExp = /^(?:0[hx])?[\da-fA-F]+$/u;
4749

4850
/**
4951
* [Hex color](https://en.wikipedia.org/wiki/Web_colors#Hex_triplet) regex.
52+
*
53+
* Hint: We decided against the `i` flag for better JSON Schema compatibility.
5054
*/
5155
export const HEX_COLOR_REGEX: RegExp =
52-
/^#(?:[\da-f]{3,4}|[\da-f]{6}|[\da-f]{8})$/iu;
56+
/^#(?:[\da-fA-F]{3,4}|[\da-fA-F]{6}|[\da-fA-F]{8})$/u;
5357

5458
/**
5559
* [IMEI](https://en.wikipedia.org/wiki/International_Mobile_Equipment_Identity) regex.
@@ -135,7 +139,7 @@ export const NANO_ID_REGEX: RegExp = /^[\w-]+$/u;
135139
/**
136140
* [Octal](https://en.wikipedia.org/wiki/Octal) regex.
137141
*/
138-
export const OCTAL_REGEX: RegExp = /^(?:0o)?[0-7]+$/iu;
142+
export const OCTAL_REGEX: RegExp = /^(?:0o)?[0-7]+$/u;
139143

140144
/**
141145
* [RFC 5322 email address](https://datatracker.ietf.org/doc/html/rfc5322#section-3.4.1) regex.
@@ -147,8 +151,10 @@ export const RFC_EMAIL_REGEX: RegExp =
147151

148152
/**
149153
* [ULID](https://github.com/ulid/spec) regex.
154+
*
155+
* Hint: We decided against the `i` flag for better JSON Schema compatibility.
150156
*/
151-
export const ULID_REGEX: RegExp = /^[\da-hjkmnp-tv-z]{26}$/iu;
157+
export const ULID_REGEX: RegExp = /^[\da-hjkmnp-tv-zA-HJKMNP-TV-Z]{26}$/u;
152158

153159
/**
154160
* [UUID](https://en.wikipedia.org/wiki/Universally_unique_identifier) regex.

packages/to-json-schema/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ All notable changes to the library will be documented in this file.
66

77
- Add support for `undefinedable` schema
88
- Add support for `base64`, `isoTime`, `isoDateTime`, `nonEmpty` and `url` action (pull request #962)
9+
- Add support for `bic`, `cuid2`, `empty`, `decimal`, `digits`, `emoji`, `hex_color`, `hexadecimal`, `nanoid`, `octal` and `ulid` action (pull request #998)
910
- Change Valibot peer dependency to v1.0.0
1011
- Change extraction of default value from `nullable`, `nullish` and `optional` schema
1112
- Change `force` to `errorMode` in config for better control (issue #889)

packages/to-json-schema/src/convertAction.test.ts

+173-61
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,54 @@ describe('convertAction', () => {
1717
});
1818
});
1919

20+
test('should convert bic action', () => {
21+
expect(convertAction({}, v.bic<string>(), undefined)).toStrictEqual({
22+
pattern: v.BIC_REGEX.source,
23+
});
24+
expect(
25+
convertAction({ type: 'string' }, v.bic<string>(), undefined)
26+
).toStrictEqual({
27+
type: 'string',
28+
pattern: v.BIC_REGEX.source,
29+
});
30+
});
31+
32+
test('should convert cuid2 action', () => {
33+
expect(convertAction({}, v.cuid2<string>(), undefined)).toStrictEqual({
34+
pattern: v.CUID2_REGEX.source,
35+
});
36+
expect(
37+
convertAction({ type: 'string' }, v.cuid2<string>(), undefined)
38+
).toStrictEqual({
39+
type: 'string',
40+
pattern: v.CUID2_REGEX.source,
41+
});
42+
});
43+
44+
test('should convert decimal action', () => {
45+
expect(convertAction({}, v.decimal<string>(), undefined)).toStrictEqual({
46+
pattern: v.DECIMAL_REGEX.source,
47+
});
48+
expect(
49+
convertAction({ type: 'string' }, v.decimal<string>(), undefined)
50+
).toStrictEqual({
51+
type: 'string',
52+
pattern: v.DECIMAL_REGEX.source,
53+
});
54+
});
55+
56+
test('should convert digits action', () => {
57+
expect(convertAction({}, v.digits<string>(), undefined)).toStrictEqual({
58+
pattern: v.DIGITS_REGEX.source,
59+
});
60+
expect(
61+
convertAction({ type: 'string' }, v.digits<string>(), undefined)
62+
).toStrictEqual({
63+
type: 'string',
64+
pattern: v.DIGITS_REGEX.source,
65+
});
66+
});
67+
2068
test('should convert description action', () => {
2169
expect(convertAction({}, v.description('test'), undefined)).toStrictEqual({
2270
description: 'test',
@@ -35,6 +83,95 @@ describe('convertAction', () => {
3583
});
3684
});
3785

86+
test('should convert emoji action', () => {
87+
expect(convertAction({}, v.emoji<string>(), undefined)).toStrictEqual({
88+
pattern: v.EMOJI_REGEX.source,
89+
});
90+
expect(
91+
convertAction({ type: 'string' }, v.emoji<string>(), undefined)
92+
).toStrictEqual({
93+
type: 'string',
94+
pattern: v.EMOJI_REGEX.source,
95+
});
96+
});
97+
98+
test('should convert empty action for strings', () => {
99+
expect(
100+
convertAction({ type: 'string' }, v.empty(), undefined)
101+
).toStrictEqual({
102+
type: 'string',
103+
maxLength: 0,
104+
});
105+
});
106+
107+
test('should convert empty action for arrays', () => {
108+
expect(
109+
convertAction({ type: 'array' }, v.empty(), undefined)
110+
).toStrictEqual({
111+
type: 'array',
112+
maxItems: 0,
113+
});
114+
});
115+
116+
test('should throw error for empty action with invalid type', () => {
117+
const action = v.empty();
118+
const error1 = 'The "empty" action is not supported on type "undefined".';
119+
expect(() => convertAction({}, action, undefined)).toThrowError(error1);
120+
expect(() =>
121+
convertAction({}, action, { errorMode: 'throw' })
122+
).toThrowError(error1);
123+
const error2 = 'The "empty" action is not supported on type "object".';
124+
expect(() =>
125+
convertAction({ type: 'object' }, action, undefined)
126+
).toThrowError(error2);
127+
expect(() =>
128+
convertAction({ type: 'object' }, action, { errorMode: 'throw' })
129+
).toThrowError(error2);
130+
});
131+
132+
test('should warn error for empty action with invalid type', () => {
133+
expect(convertAction({}, v.empty(), { errorMode: 'warn' })).toStrictEqual({
134+
maxLength: 0,
135+
});
136+
expect(console.warn).toHaveBeenLastCalledWith(
137+
'The "empty" action is not supported on type "undefined".'
138+
);
139+
expect(
140+
convertAction({ type: 'object' }, v.empty(), {
141+
errorMode: 'warn',
142+
})
143+
).toStrictEqual({ type: 'object', maxLength: 0 });
144+
expect(console.warn).toHaveBeenLastCalledWith(
145+
'The "empty" action is not supported on type "object".'
146+
);
147+
});
148+
149+
test('should convert hexadecimal action', () => {
150+
expect(convertAction({}, v.hexadecimal<string>(), undefined)).toStrictEqual(
151+
{
152+
pattern: v.HEXADECIMAL_REGEX.source,
153+
}
154+
);
155+
expect(
156+
convertAction({ type: 'string' }, v.hexadecimal<string>(), undefined)
157+
).toStrictEqual({
158+
type: 'string',
159+
pattern: v.HEXADECIMAL_REGEX.source,
160+
});
161+
});
162+
163+
test('should convert hex color action', () => {
164+
expect(convertAction({}, v.hexColor<string>(), undefined)).toStrictEqual({
165+
pattern: v.HEX_COLOR_REGEX.source,
166+
});
167+
expect(
168+
convertAction({ type: 'string' }, v.hexColor<string>(), undefined)
169+
).toStrictEqual({
170+
type: 'string',
171+
pattern: v.HEX_COLOR_REGEX.source,
172+
});
173+
});
174+
38175
test('should convert integer action', () => {
39176
expect(convertAction({}, v.integer<number>(), undefined)).toStrictEqual({
40177
type: 'integer',
@@ -418,6 +555,18 @@ describe('convertAction', () => {
418555
});
419556
});
420557

558+
test('should convert Nano ID action', () => {
559+
expect(convertAction({}, v.nanoid<string>(), undefined)).toStrictEqual({
560+
pattern: v.NANO_ID_REGEX.source,
561+
});
562+
expect(
563+
convertAction({ type: 'string' }, v.nanoid<string>(), undefined)
564+
).toStrictEqual({
565+
type: 'string',
566+
pattern: v.NANO_ID_REGEX.source,
567+
});
568+
});
569+
421570
test('should convert non empty action for strings', () => {
422571
expect(
423572
convertAction({ type: 'string' }, v.nonEmpty(), undefined)
@@ -472,6 +621,18 @@ describe('convertAction', () => {
472621
);
473622
});
474623

624+
test('should convert octal action', () => {
625+
expect(convertAction({}, v.octal<string>(), undefined)).toStrictEqual({
626+
pattern: v.OCTAL_REGEX.source,
627+
});
628+
expect(
629+
convertAction({ type: 'string' }, v.octal<string>(), undefined)
630+
).toStrictEqual({
631+
type: 'string',
632+
pattern: v.OCTAL_REGEX.source,
633+
});
634+
});
635+
475636
test('should convert supported regex action', () => {
476637
expect(
477638
convertAction({ type: 'string' }, v.regex<string>(/[a-zA-Z]/), undefined)
@@ -510,6 +671,18 @@ describe('convertAction', () => {
510671
});
511672
});
512673

674+
test('should convert ULID action', () => {
675+
expect(convertAction({}, v.ulid<string>(), undefined)).toStrictEqual({
676+
pattern: v.ULID_REGEX.source,
677+
});
678+
expect(
679+
convertAction({ type: 'string' }, v.ulid<string>(), undefined)
680+
).toStrictEqual({
681+
type: 'string',
682+
pattern: v.ULID_REGEX.source,
683+
});
684+
});
685+
513686
test('should convert url action', () => {
514687
expect(convertAction({}, v.url<string>(), undefined)).toStrictEqual({
515688
format: 'uri',
@@ -612,65 +785,4 @@ describe('convertAction', () => {
612785
'The "transform" action cannot be converted to JSON Schema.'
613786
);
614787
});
615-
616-
test('should convert BIC action', () => {
617-
expect(convertAction({ type: 'string' }, v.bic(), undefined)).toStrictEqual(
618-
{
619-
type: 'string',
620-
pattern: v.BIC_REGEX.source,
621-
}
622-
);
623-
});
624-
625-
test('should convert CUID2 action', () => {
626-
expect(
627-
convertAction({ type: 'string' }, v.cuid2(), undefined)
628-
).toStrictEqual({
629-
type: 'string',
630-
pattern: v.CUID2_REGEX.source,
631-
});
632-
});
633-
634-
test('should convert decimal action', () => {
635-
expect(
636-
convertAction({ type: 'string' }, v.decimal(), undefined)
637-
).toStrictEqual({
638-
type: 'string',
639-
pattern: v.DECIMAL_REGEX.source,
640-
});
641-
});
642-
643-
test('should convert digits action', () => {
644-
expect(
645-
convertAction({ type: 'string' }, v.digits(), undefined)
646-
).toStrictEqual({
647-
type: 'string',
648-
pattern: v.DIGITS_REGEX.source,
649-
});
650-
});
651-
652-
test('should convert empty action with strings', () => {
653-
expect(
654-
convertAction({ type: 'string' }, v.empty(), undefined)
655-
).toStrictEqual({
656-
type: 'string',
657-
maxLength: 0,
658-
});
659-
});
660-
661-
test('should convert empty action with arrays', () => {
662-
expect(
663-
convertAction({ type: 'array' }, v.empty(), undefined)
664-
).toStrictEqual({
665-
type: 'array',
666-
maxItems: 0,
667-
});
668-
});
669-
670-
test('should throw error for unsupported empty action', () => {
671-
const error = 'The "empty" action is not supported on type "number".';
672-
expect(() =>
673-
convertAction({ type: 'number' }, v.empty(), undefined)
674-
).toThrowError(error);
675-
});
676788
});

0 commit comments

Comments
 (0)