zcb 0.0.26 → 0.1.0
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/README.md +24 -24
- package/dist/cjs/cli.cjs +6 -6
- package/dist/cjs/cli.cjs.map +1 -1
- package/dist/cjs/index.cjs +19 -14
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/cli.mjs +6 -6
- package/dist/esm/cli.mjs.map +1 -1
- package/dist/esm/index.mjs +19 -14
- package/dist/esm/index.mjs.map +1 -1
- package/dist/types/cjs/createConfigBuilder.d.cts +9 -9
- package/dist/types/cjs/createConfigBuilder.d.cts.map +1 -1
- package/dist/types/cjs/utils/jsonStringifyReplacer.d.cts +15 -0
- package/dist/types/cjs/utils/jsonStringifyReplacer.d.cts.map +1 -0
- package/dist/types/esm/createConfigBuilder.d.ts +9 -9
- package/dist/types/esm/createConfigBuilder.d.ts.map +1 -1
- package/dist/types/esm/utils/jsonStringifyReplacer.d.ts +15 -0
- package/dist/types/esm/utils/jsonStringifyReplacer.d.ts.map +1 -0
- package/dist/types/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +16 -15
- package/src/__snapshots__/createConfigBuilder.test.ts.snap +30 -0
- package/src/__testUtils__/configBuilder.ts +12 -12
- package/src/createConfigBuilder.test.ts +78 -53
- package/src/createConfigBuilder.ts +23 -22
- package/src/utils/importValidateTransformWriteConfig.ts +6 -6
- package/src/utils/isPropertyReservedWord.ts +9 -9
- package/src/utils/jsonStringifyReplacer.ts +6 -0
- package/src/utils/transformConfig.test.ts +2 -2
- package/tsconfig.build.json +1 -4
- package/tsconfig.json +0 -3
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zcb",
|
|
3
3
|
"description": "Build configs with type safety from zod schema.",
|
|
4
|
-
"version": "0.0
|
|
4
|
+
"version": "0.1.0",
|
|
5
5
|
"author": "Dylan Aubrey",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"homepage": "https://github.com/badbatch/zod-config-builder",
|
|
@@ -15,12 +15,14 @@
|
|
|
15
15
|
"module": "./dist/esm/index.mjs",
|
|
16
16
|
"types": "./dist/types/cjs/index.d.cts",
|
|
17
17
|
"exports": {
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
".": {
|
|
19
|
+
"types": {
|
|
20
|
+
"import": "./dist/types/esm/index.d.ts",
|
|
21
|
+
"require": "./dist/types/cjs/index.d.cts"
|
|
22
|
+
},
|
|
23
|
+
"import": "./dist/esm/index.mjs",
|
|
24
|
+
"require": "./dist/cjs/index.cjs"
|
|
25
|
+
}
|
|
24
26
|
},
|
|
25
27
|
"publishConfig": {
|
|
26
28
|
"access": "public"
|
|
@@ -34,6 +36,7 @@
|
|
|
34
36
|
"handlebars": "^4.7.7",
|
|
35
37
|
"json-schema": "^0.4.0",
|
|
36
38
|
"shelljs": "^0.8.5",
|
|
39
|
+
"ts-toolbelt": "^9.6.0",
|
|
37
40
|
"type-fest": "^3.10.0",
|
|
38
41
|
"yargs": "^15.4.1",
|
|
39
42
|
"zod": "^3.21.4",
|
|
@@ -64,7 +67,7 @@
|
|
|
64
67
|
"@commitlint/prompt-cli": "^17.6.3",
|
|
65
68
|
"@jest/globals": "^29.5.0",
|
|
66
69
|
"@repodog/babel-config": "^1.1.11",
|
|
67
|
-
"@repodog/cli": "^1.
|
|
70
|
+
"@repodog/cli": "^1.4.1",
|
|
68
71
|
"@repodog/commitlint-config": "^1.1.6",
|
|
69
72
|
"@repodog/eslint-config": "^1.1.19",
|
|
70
73
|
"@repodog/eslint-config-jest": "^1.1.10",
|
|
@@ -73,7 +76,7 @@
|
|
|
73
76
|
"@repodog/prettier-config": "^1.1.5",
|
|
74
77
|
"@repodog/rollup-config": "^1.1.8",
|
|
75
78
|
"@repodog/syncpack-config": "^1.2.3",
|
|
76
|
-
"@repodog/ts-config": "^1.
|
|
79
|
+
"@repodog/ts-config": "^1.3.0",
|
|
77
80
|
"@rollup/plugin-babel": "^6.0.3",
|
|
78
81
|
"@rollup/plugin-commonjs": "^25.0.7",
|
|
79
82
|
"@rollup/plugin-image": "^3.0.2",
|
|
@@ -82,15 +85,15 @@
|
|
|
82
85
|
"@rollup/plugin-terser": "^0.4.1",
|
|
83
86
|
"@types/jest": "^25.2.3",
|
|
84
87
|
"@types/lodash-es": "^4.17.9",
|
|
85
|
-
"@types/node": "^
|
|
88
|
+
"@types/node": "^20.11.0",
|
|
86
89
|
"@typescript-eslint/eslint-plugin": "^5.59.5",
|
|
87
90
|
"@typescript-eslint/parser": "^5.59.5",
|
|
88
91
|
"babel-jest": "^29.5.0",
|
|
89
92
|
"babel-plugin-codegen": "^4.1.5",
|
|
90
93
|
"babel-plugin-macros": "^3.1.0",
|
|
91
94
|
"core-js": "^3.30.2",
|
|
92
|
-
"cts-types": "^0.0.
|
|
93
|
-
"del-cli": "^
|
|
95
|
+
"cts-types": "^0.0.6",
|
|
96
|
+
"del-cli": "^5.1.0",
|
|
94
97
|
"eslint": "^8.40.0",
|
|
95
98
|
"eslint-config-prettier": "^8.8.0",
|
|
96
99
|
"eslint-import-resolver-typescript": "^3.5.5",
|
|
@@ -103,7 +106,6 @@
|
|
|
103
106
|
"eslint-plugin-sort-keys-fix": "^1.1.2",
|
|
104
107
|
"eslint-plugin-typescript-sort-keys": "^2.3.0",
|
|
105
108
|
"eslint-plugin-unicorn": "^46.0.1",
|
|
106
|
-
"fast-check": "^3.14.0",
|
|
107
109
|
"generate-changelog": "^1.8.0",
|
|
108
110
|
"husky": "^8.0.3",
|
|
109
111
|
"identity-obj-proxy": "^3.0.0",
|
|
@@ -118,9 +120,8 @@
|
|
|
118
120
|
"rollup-plugin-copy": "^3.4.0",
|
|
119
121
|
"rollup-plugin-sourcemaps": "^0.6.3",
|
|
120
122
|
"suppress-experimental-warnings": "^1.1.17",
|
|
121
|
-
"syncpack": "^12.
|
|
123
|
+
"syncpack": "^12.3.0",
|
|
122
124
|
"ts-node": "^10.9.1",
|
|
123
|
-
"ts-toolbelt": "^9.6.0",
|
|
124
125
|
"typescript": "^5.0.4"
|
|
125
126
|
},
|
|
126
127
|
"keywords": [
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
|
+
|
|
3
|
+
exports[`createConfigBuilder when a user stringifies the config when a config value is a zod schema should stringify the value to JSON schema correctly 1`] = `
|
|
4
|
+
"{
|
|
5
|
+
"schema": {
|
|
6
|
+
"anyOf": [
|
|
7
|
+
{
|
|
8
|
+
"not": {}
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"type": "object",
|
|
12
|
+
"properties": {
|
|
13
|
+
"alpha": {
|
|
14
|
+
"type": "string"
|
|
15
|
+
},
|
|
16
|
+
"bravo": {
|
|
17
|
+
"type": "boolean"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"required": [
|
|
21
|
+
"alpha",
|
|
22
|
+
"bravo"
|
|
23
|
+
],
|
|
24
|
+
"additionalProperties": false
|
|
25
|
+
}
|
|
26
|
+
],
|
|
27
|
+
"$schema": "http://json-schema.org/draft-07/schema#"
|
|
28
|
+
}
|
|
29
|
+
}"
|
|
30
|
+
`;
|
|
@@ -15,7 +15,7 @@ const configBuilder = createConfigBuilder<ConfigType>(configSchema);
|
|
|
15
15
|
const routeBuilder = createConfigBuilder<RouteType>(routeSchema, { path: ({ page }) => kebabCase(page) });
|
|
16
16
|
const pageBuilder = createConfigBuilder<PageType>(pageSchema);
|
|
17
17
|
const sectionBuilder = createConfigBuilder<SectionType>(sectionSchema);
|
|
18
|
-
const subsectionBuilder = sectionBuilder
|
|
18
|
+
const subsectionBuilder = sectionBuilder.$fork();
|
|
19
19
|
|
|
20
20
|
configBuilder
|
|
21
21
|
.countryCode('GB')
|
|
@@ -30,27 +30,27 @@ configBuilder
|
|
|
30
30
|
contactDetails: pageBuilder
|
|
31
31
|
.name('contactDetails')
|
|
32
32
|
.sections([
|
|
33
|
-
sectionBuilder.name('header')
|
|
33
|
+
sectionBuilder.name('header').$flush(),
|
|
34
34
|
sectionBuilder
|
|
35
35
|
.name('body')
|
|
36
|
-
.sections([subsectionBuilder.name('main')
|
|
37
|
-
|
|
38
|
-
sectionBuilder.name('footer')
|
|
36
|
+
.sections([subsectionBuilder.name('main').$flush(), subsectionBuilder.name('sidebar').$flush()])
|
|
37
|
+
.$flush(),
|
|
38
|
+
sectionBuilder.name('footer').$flush(),
|
|
39
39
|
])
|
|
40
|
-
|
|
40
|
+
.$flush(),
|
|
41
41
|
personalDetails: pageBuilder
|
|
42
42
|
.name('personalDetails')
|
|
43
43
|
.sections([
|
|
44
|
-
sectionBuilder.name('header')
|
|
44
|
+
sectionBuilder.name('header').$flush(),
|
|
45
45
|
sectionBuilder
|
|
46
46
|
.name('body')
|
|
47
|
-
.sections([subsectionBuilder.name('main')
|
|
48
|
-
|
|
49
|
-
sectionBuilder.name('footer')
|
|
47
|
+
.sections([subsectionBuilder.name('main').$flush(), subsectionBuilder.name('sidebar').$flush()])
|
|
48
|
+
.$flush(),
|
|
49
|
+
sectionBuilder.name('footer').$flush(),
|
|
50
50
|
])
|
|
51
|
-
|
|
51
|
+
.$flush(),
|
|
52
52
|
})
|
|
53
|
-
.routes([routeBuilder.page('personalDetails')
|
|
53
|
+
.routes([routeBuilder.page('personalDetails').$flush(), routeBuilder.page('contactDetails').$flush()])
|
|
54
54
|
.timeouts({ apollo: 10_000 })
|
|
55
55
|
.timezone('Europe/London');
|
|
56
56
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { kebabCase } from 'lodash-es';
|
|
2
|
-
import { z } from 'zod';
|
|
2
|
+
import { ZodType, z } from 'zod';
|
|
3
3
|
import {
|
|
4
4
|
type ConfigType,
|
|
5
5
|
type PageType,
|
|
@@ -27,11 +27,11 @@ describe('createConfigBuilder', () => {
|
|
|
27
27
|
const { createConfigBuilder } = await import('./createConfigBuilder.ts');
|
|
28
28
|
|
|
29
29
|
const invalidSchema = z.object({
|
|
30
|
-
values: z.string(),
|
|
30
|
+
$values: z.string(),
|
|
31
31
|
});
|
|
32
32
|
|
|
33
33
|
expect(() => createConfigBuilder<z.infer<typeof invalidSchema>>(invalidSchema)).toThrow(
|
|
34
|
-
`"values" is a reserved keyword within the config builder. Please use a different property name. The full list of reserved keywords is: ${[
|
|
34
|
+
`"$values" is a reserved keyword within the config builder. Please use a different property name. The full list of reserved keywords is: ${[
|
|
35
35
|
...RESERVED_KEYWORDS,
|
|
36
36
|
].join(', ')}`
|
|
37
37
|
);
|
|
@@ -42,9 +42,9 @@ describe('createConfigBuilder', () => {
|
|
|
42
42
|
it('should add the disabled flag to the config', async () => {
|
|
43
43
|
const { createConfigBuilder } = await import('./createConfigBuilder.ts');
|
|
44
44
|
const config = createConfigBuilder<ConfigType>(configSchema);
|
|
45
|
-
config
|
|
45
|
+
config.$disable().name('alpha');
|
|
46
46
|
// @ts-expect-error private property
|
|
47
|
-
expect(config
|
|
47
|
+
expect(config.$values().__disabled).toBe(true);
|
|
48
48
|
});
|
|
49
49
|
});
|
|
50
50
|
|
|
@@ -52,9 +52,9 @@ describe('createConfigBuilder', () => {
|
|
|
52
52
|
it('should add the experiment to the config', async () => {
|
|
53
53
|
const { createConfigBuilder } = await import('./createConfigBuilder.ts');
|
|
54
54
|
const config = createConfigBuilder<ConfigType>(configSchema);
|
|
55
|
-
config
|
|
55
|
+
config.$experiment('FEAT_ALPHA@0.0.1').name('alpha');
|
|
56
56
|
// @ts-expect-error private property
|
|
57
|
-
expect(config
|
|
57
|
+
expect(config.$values().__experiment).toBe('FEAT_ALPHA@0.0.1');
|
|
58
58
|
});
|
|
59
59
|
});
|
|
60
60
|
|
|
@@ -67,13 +67,13 @@ describe('createConfigBuilder', () => {
|
|
|
67
67
|
|
|
68
68
|
config
|
|
69
69
|
.name('alpha')
|
|
70
|
-
.pages({ contactDetails: page.name('contactDetails')
|
|
71
|
-
.routes([route.page('contactDetails')
|
|
70
|
+
.pages({ contactDetails: page.name('contactDetails').$flush() })
|
|
71
|
+
.routes([route.page('contactDetails').$flush()]);
|
|
72
72
|
|
|
73
73
|
const childConfig = createConfigBuilder<ConfigType>(configSchema);
|
|
74
|
-
childConfig
|
|
74
|
+
childConfig.$extend(config);
|
|
75
75
|
|
|
76
|
-
expect(childConfig
|
|
76
|
+
expect(childConfig.$values()).toEqual({
|
|
77
77
|
name: 'alpha',
|
|
78
78
|
pages: {
|
|
79
79
|
contactDetails: { name: 'contactDetails' },
|
|
@@ -89,15 +89,15 @@ describe('createConfigBuilder', () => {
|
|
|
89
89
|
const page = createConfigBuilder<PageType>(pageSchema);
|
|
90
90
|
|
|
91
91
|
config
|
|
92
|
-
|
|
92
|
+
.$experiment('FEAT_ALPHA@0.0.1')
|
|
93
93
|
.name('alpha')
|
|
94
|
-
.pages({ contactDetails: page
|
|
95
|
-
.routes([route
|
|
94
|
+
.pages({ contactDetails: page.$experiment('FEAT_BRAVO@0.0.1').name('contactDetails').$flush() })
|
|
95
|
+
.routes([route.$experiment('FEAT_CHARLIE@0.0.1').page('contactDetails').$flush()]);
|
|
96
96
|
|
|
97
97
|
const childConfig = createConfigBuilder<ConfigType>(configSchema);
|
|
98
|
-
childConfig
|
|
98
|
+
childConfig.$extend(config);
|
|
99
99
|
|
|
100
|
-
expect(childConfig
|
|
100
|
+
expect(childConfig.$values()).toEqual(
|
|
101
101
|
expect.objectContaining({
|
|
102
102
|
__experiment: 'FEAT_ALPHA@0.0.1',
|
|
103
103
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
@@ -125,15 +125,15 @@ describe('createConfigBuilder', () => {
|
|
|
125
125
|
const page = createConfigBuilder<PageType>(pageSchema);
|
|
126
126
|
|
|
127
127
|
config
|
|
128
|
-
|
|
128
|
+
.$disable()
|
|
129
129
|
.name('alpha')
|
|
130
|
-
.pages({ contactDetails: page
|
|
131
|
-
.routes([route
|
|
130
|
+
.pages({ contactDetails: page.$disable().name('contactDetails').$flush() })
|
|
131
|
+
.routes([route.$disable().page('contactDetails').$flush()]);
|
|
132
132
|
|
|
133
133
|
const childConfig = createConfigBuilder<ConfigType>(configSchema);
|
|
134
|
-
childConfig
|
|
134
|
+
childConfig.$extend(config);
|
|
135
135
|
|
|
136
|
-
expect(childConfig
|
|
136
|
+
expect(childConfig.$values()).toEqual(
|
|
137
137
|
expect.objectContaining({
|
|
138
138
|
__disabled: true,
|
|
139
139
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
@@ -159,9 +159,9 @@ describe('createConfigBuilder', () => {
|
|
|
159
159
|
const route = createConfigBuilder<RouteType>(routeSchema, { path: ({ page }) => kebabCase(page) });
|
|
160
160
|
route.page('personalDetails');
|
|
161
161
|
const childRoute = createConfigBuilder<RouteType>(routeSchema);
|
|
162
|
-
childRoute
|
|
162
|
+
childRoute.$extend(route);
|
|
163
163
|
childRoute.page('contactDetails', true);
|
|
164
|
-
expect(childRoute
|
|
164
|
+
expect(childRoute.$values()).toEqual({ page: 'contactDetails', path: 'contact-details' });
|
|
165
165
|
});
|
|
166
166
|
});
|
|
167
167
|
|
|
@@ -176,7 +176,7 @@ describe('createConfigBuilder', () => {
|
|
|
176
176
|
});
|
|
177
177
|
|
|
178
178
|
const config = createConfigBuilder<z.infer<typeof extendedSchema>>(extendedSchema);
|
|
179
|
-
expect(config
|
|
179
|
+
expect(config.$values()).toEqual({ description: 'This is the description.' });
|
|
180
180
|
});
|
|
181
181
|
});
|
|
182
182
|
|
|
@@ -189,7 +189,7 @@ describe('createConfigBuilder', () => {
|
|
|
189
189
|
});
|
|
190
190
|
|
|
191
191
|
const config = createConfigBuilder<z.infer<typeof extendedSchema>>(extendedSchema);
|
|
192
|
-
expect(config
|
|
192
|
+
expect(config.$values()).toEqual({ flags: { alpha: true, bravo: false, charlie: false } });
|
|
193
193
|
});
|
|
194
194
|
});
|
|
195
195
|
|
|
@@ -202,7 +202,7 @@ describe('createConfigBuilder', () => {
|
|
|
202
202
|
});
|
|
203
203
|
|
|
204
204
|
const config = createConfigBuilder<z.infer<typeof extendedSchema>>(extendedSchema);
|
|
205
|
-
expect(config
|
|
205
|
+
expect(config.$values()).toEqual({ colors: ['red', 'yellow', 'pink', 'green'] });
|
|
206
206
|
});
|
|
207
207
|
});
|
|
208
208
|
});
|
|
@@ -275,14 +275,14 @@ describe('createConfigBuilder', () => {
|
|
|
275
275
|
const { createConfigBuilder } = await import('./createConfigBuilder.ts');
|
|
276
276
|
const config = createConfigBuilder<ConfigType>(configSchema);
|
|
277
277
|
config.countryCode('GB');
|
|
278
|
-
expect(config
|
|
278
|
+
expect(config.$values()).toEqual({ countryCode: 'GB' });
|
|
279
279
|
});
|
|
280
280
|
|
|
281
281
|
it('should be a valid config', async () => {
|
|
282
282
|
const { createConfigBuilder } = await import('./createConfigBuilder.ts');
|
|
283
283
|
const config = createConfigBuilder<ConfigType>(configSchema);
|
|
284
284
|
config.countryCode('GB');
|
|
285
|
-
expect(config
|
|
285
|
+
expect(config.$validate()).toBe(true);
|
|
286
286
|
});
|
|
287
287
|
});
|
|
288
288
|
|
|
@@ -291,14 +291,14 @@ describe('createConfigBuilder', () => {
|
|
|
291
291
|
const { createConfigBuilder } = await import('./createConfigBuilder.ts');
|
|
292
292
|
const config = createConfigBuilder<ConfigType>(configSchema);
|
|
293
293
|
config.languageCodes(['en']);
|
|
294
|
-
expect(config
|
|
294
|
+
expect(config.$values()).toEqual({ languageCodes: ['en'] });
|
|
295
295
|
});
|
|
296
296
|
|
|
297
297
|
it('should be a valid config', async () => {
|
|
298
298
|
const { createConfigBuilder } = await import('./createConfigBuilder.ts');
|
|
299
299
|
const config = createConfigBuilder<ConfigType>(configSchema);
|
|
300
300
|
config.languageCodes(['en']);
|
|
301
|
-
expect(config
|
|
301
|
+
expect(config.$validate()).toBe(true);
|
|
302
302
|
});
|
|
303
303
|
});
|
|
304
304
|
|
|
@@ -307,14 +307,14 @@ describe('createConfigBuilder', () => {
|
|
|
307
307
|
const { createConfigBuilder } = await import('./createConfigBuilder.ts');
|
|
308
308
|
const config = createConfigBuilder<ConfigType>(configSchema);
|
|
309
309
|
config.timeouts({ apollo: 120_000 });
|
|
310
|
-
expect(config
|
|
310
|
+
expect(config.$values()).toEqual({ timeouts: { apollo: 120_000 } });
|
|
311
311
|
});
|
|
312
312
|
|
|
313
313
|
it('should be a valid config', async () => {
|
|
314
314
|
const { createConfigBuilder } = await import('./createConfigBuilder.ts');
|
|
315
315
|
const config = createConfigBuilder<ConfigType>(configSchema);
|
|
316
316
|
config.timeouts({ apollo: 120_000 });
|
|
317
|
-
expect(config
|
|
317
|
+
expect(config.$validate()).toBe(true);
|
|
318
318
|
});
|
|
319
319
|
});
|
|
320
320
|
|
|
@@ -326,11 +326,11 @@ describe('createConfigBuilder', () => {
|
|
|
326
326
|
const page = createConfigBuilder<PageType>(pageSchema);
|
|
327
327
|
|
|
328
328
|
config.pages({
|
|
329
|
-
contactDetails: page.name('contactDetails')
|
|
330
|
-
personalDetails: page.name('personalDetails')
|
|
329
|
+
contactDetails: page.name('contactDetails').$flush(),
|
|
330
|
+
personalDetails: page.name('personalDetails').$flush(),
|
|
331
331
|
});
|
|
332
332
|
|
|
333
|
-
expect(config
|
|
333
|
+
expect(config.$values()).toEqual({
|
|
334
334
|
pages: {
|
|
335
335
|
contactDetails: {
|
|
336
336
|
name: 'contactDetails',
|
|
@@ -348,11 +348,11 @@ describe('createConfigBuilder', () => {
|
|
|
348
348
|
const page = createConfigBuilder<PageType>(pageSchema);
|
|
349
349
|
|
|
350
350
|
config.pages({
|
|
351
|
-
contactDetails: page.name('contactDetails')
|
|
352
|
-
personalDetails: page.name('personalDetails')
|
|
351
|
+
contactDetails: page.name('contactDetails').$flush(),
|
|
352
|
+
personalDetails: page.name('personalDetails').$flush(),
|
|
353
353
|
});
|
|
354
354
|
|
|
355
|
-
expect(config
|
|
355
|
+
expect(config.$validate()).toBe(true);
|
|
356
356
|
});
|
|
357
357
|
});
|
|
358
358
|
|
|
@@ -392,7 +392,7 @@ describe('createConfigBuilder', () => {
|
|
|
392
392
|
// no catch
|
|
393
393
|
}
|
|
394
394
|
|
|
395
|
-
expect(config
|
|
395
|
+
expect(config.$values()).toEqual({});
|
|
396
396
|
});
|
|
397
397
|
});
|
|
398
398
|
});
|
|
@@ -403,17 +403,17 @@ describe('createConfigBuilder', () => {
|
|
|
403
403
|
const { createConfigBuilder } = await import('./createConfigBuilder.ts');
|
|
404
404
|
const config = createConfigBuilder<ConfigType>(configSchema);
|
|
405
405
|
const route = createConfigBuilder<RouteType>(routeSchema, { path: ({ page }) => kebabCase(page) });
|
|
406
|
-
const subRoute = route
|
|
406
|
+
const subRoute = route.$fork();
|
|
407
407
|
|
|
408
408
|
config.routes([
|
|
409
|
-
route.page('personalDetails')
|
|
409
|
+
route.page('personalDetails').$flush(),
|
|
410
410
|
route
|
|
411
411
|
.page('contactDetails')
|
|
412
|
-
.routes([subRoute.page('deliveryAddress')
|
|
413
|
-
|
|
412
|
+
.routes([subRoute.page('deliveryAddress').$flush(), subRoute.page('billingAddress').$flush()])
|
|
413
|
+
.$flush(),
|
|
414
414
|
]);
|
|
415
415
|
|
|
416
|
-
expect(config
|
|
416
|
+
expect(config.$values()).toEqual({
|
|
417
417
|
routes: [
|
|
418
418
|
{ page: 'personalDetails', path: 'personal-details' },
|
|
419
419
|
{
|
|
@@ -434,17 +434,17 @@ describe('createConfigBuilder', () => {
|
|
|
434
434
|
const route = createConfigBuilder<RouteType>(routeSchema, { path: ({ page }) => kebabCase(page) });
|
|
435
435
|
|
|
436
436
|
config.routes([
|
|
437
|
-
route.page('personalDetails')
|
|
437
|
+
route.page('personalDetails').$flush(),
|
|
438
438
|
route
|
|
439
439
|
.page('contactDetails')
|
|
440
440
|
.routes(() => {
|
|
441
|
-
const subRoute = route
|
|
442
|
-
return [subRoute.page('deliveryAddress')
|
|
441
|
+
const subRoute = route.$fork();
|
|
442
|
+
return [subRoute.page('deliveryAddress').$flush(), subRoute.page('billingAddress').$flush()];
|
|
443
443
|
})
|
|
444
|
-
|
|
444
|
+
.$flush(),
|
|
445
445
|
]);
|
|
446
446
|
|
|
447
|
-
expect(config
|
|
447
|
+
expect(config.$validate()).toBe(true);
|
|
448
448
|
});
|
|
449
449
|
});
|
|
450
450
|
|
|
@@ -468,7 +468,7 @@ describe('createConfigBuilder', () => {
|
|
|
468
468
|
// no catch
|
|
469
469
|
}
|
|
470
470
|
|
|
471
|
-
expect(config
|
|
471
|
+
expect(config.$values()).toEqual({});
|
|
472
472
|
});
|
|
473
473
|
});
|
|
474
474
|
});
|
|
@@ -486,7 +486,7 @@ describe('createConfigBuilder', () => {
|
|
|
486
486
|
languageCodes?.length && countryCode ? languageCodes.map(code => `${code}_${countryCode}` as const) : []
|
|
487
487
|
);
|
|
488
488
|
|
|
489
|
-
expect(config
|
|
489
|
+
expect(config.$values()).toEqual({ countryCode: 'GB', languageCodes: ['en'], locales: ['en_GB'] });
|
|
490
490
|
});
|
|
491
491
|
|
|
492
492
|
it('should update the value to the config automatically', async () => {
|
|
@@ -502,7 +502,7 @@ describe('createConfigBuilder', () => {
|
|
|
502
502
|
)
|
|
503
503
|
.languageCodes(['fr'], true);
|
|
504
504
|
|
|
505
|
-
expect(config
|
|
505
|
+
expect(config.$values()).toEqual({ countryCode: 'GB', languageCodes: ['fr'], locales: ['fr_GB'] });
|
|
506
506
|
});
|
|
507
507
|
|
|
508
508
|
it('should be a valid config', async () => {
|
|
@@ -517,7 +517,32 @@ describe('createConfigBuilder', () => {
|
|
|
517
517
|
languageCodes?.length && countryCode ? languageCodes.map(code => `${code}_${countryCode}`) : []
|
|
518
518
|
);
|
|
519
519
|
|
|
520
|
-
expect(config
|
|
520
|
+
expect(config.$validate()).toBe(true);
|
|
521
|
+
});
|
|
522
|
+
});
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
describe('when a user stringifies the config', () => {
|
|
526
|
+
describe('when a config value is a zod schema', () => {
|
|
527
|
+
it('should stringify the value to JSON schema correctly', async () => {
|
|
528
|
+
const { createConfigBuilder } = await import('./createConfigBuilder.ts');
|
|
529
|
+
|
|
530
|
+
const extendedSchema = configSchema.extend({
|
|
531
|
+
schema: z.instanceof(ZodType),
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
const config = createConfigBuilder<z.infer<typeof extendedSchema>>(extendedSchema);
|
|
535
|
+
|
|
536
|
+
config.schema(
|
|
537
|
+
z
|
|
538
|
+
.object({
|
|
539
|
+
alpha: z.string(),
|
|
540
|
+
bravo: z.boolean(),
|
|
541
|
+
})
|
|
542
|
+
.optional()
|
|
543
|
+
);
|
|
544
|
+
|
|
545
|
+
expect(config.$toJson()).toMatchSnapshot();
|
|
521
546
|
});
|
|
522
547
|
});
|
|
523
548
|
});
|
|
@@ -10,6 +10,7 @@ import { RESERVED_KEYWORDS, isPropertyReservedWord } from './utils/isPropertyRes
|
|
|
10
10
|
import { isSchemaValid } from './utils/isSchemaValid.ts';
|
|
11
11
|
import { isValidPropertyDefinition } from './utils/isValidPropertyDefinition.ts';
|
|
12
12
|
import { isValidValue } from './utils/isValidValue.ts';
|
|
13
|
+
import { jsonStringifyReplacer } from './utils/jsonStringifyReplacer.ts';
|
|
13
14
|
import { objectHasInvalidDefaults } from './utils/objectHasInvalidDefaults.ts';
|
|
14
15
|
import { recordHasInvalidDefaults } from './utils/recordHasInvalidDefaults.ts';
|
|
15
16
|
import { transformConfigSync } from './utils/transformConfig.ts';
|
|
@@ -20,15 +21,15 @@ export type ConfigBuilder<ZodTypes> = {
|
|
|
20
21
|
override?: boolean
|
|
21
22
|
) => ConfigBuilder<ZodTypes>;
|
|
22
23
|
} & {
|
|
23
|
-
disable: () => ConfigBuilder<ZodTypes>;
|
|
24
|
-
errors: () => ZodError['errors'];
|
|
25
|
-
experiment: (key: string) => ConfigBuilder<ZodTypes>;
|
|
26
|
-
extend: (configBuilder: ConfigBuilder<ZodTypes>) => ConfigBuilder<ZodTypes>;
|
|
27
|
-
flush: () => ZodTypes;
|
|
28
|
-
fork: () => ConfigBuilder<ZodTypes>;
|
|
29
|
-
toJson: () => string;
|
|
30
|
-
validate: () => boolean;
|
|
31
|
-
values: () => ZodTypes;
|
|
24
|
+
$disable: () => ConfigBuilder<ZodTypes>;
|
|
25
|
+
$errors: () => ZodError['errors'];
|
|
26
|
+
$experiment: (key: string) => ConfigBuilder<ZodTypes>;
|
|
27
|
+
$extend: (configBuilder: ConfigBuilder<ZodTypes>) => ConfigBuilder<ZodTypes>;
|
|
28
|
+
$flush: () => ZodTypes;
|
|
29
|
+
$fork: () => ConfigBuilder<ZodTypes>;
|
|
30
|
+
$toJson: () => string;
|
|
31
|
+
$validate: () => boolean;
|
|
32
|
+
$values: () => ZodTypes;
|
|
32
33
|
};
|
|
33
34
|
|
|
34
35
|
export const createConfigBuilder = <ZodTypes>(
|
|
@@ -61,7 +62,7 @@ export const createConfigBuilder = <ZodTypes>(
|
|
|
61
62
|
let callbacks: Partial<Record<keyof Config, DerivedValueCallback>> = { ...derivedValueCallbacks };
|
|
62
63
|
|
|
63
64
|
const configBuilder = {
|
|
64
|
-
disable: () => {
|
|
65
|
+
$disable: () => {
|
|
65
66
|
Object.defineProperty(config, NonEmumeralProperties.DISABLED, {
|
|
66
67
|
configurable: false,
|
|
67
68
|
enumerable: false,
|
|
@@ -70,15 +71,15 @@ export const createConfigBuilder = <ZodTypes>(
|
|
|
70
71
|
|
|
71
72
|
return configBuilder;
|
|
72
73
|
},
|
|
73
|
-
errors: () => {
|
|
74
|
+
$errors: () => {
|
|
74
75
|
try {
|
|
75
|
-
zodSchema.parse(configBuilder
|
|
76
|
+
zodSchema.parse(configBuilder.$values());
|
|
76
77
|
return [];
|
|
77
78
|
} catch (error: unknown) {
|
|
78
79
|
return (error as ZodError).errors;
|
|
79
80
|
}
|
|
80
81
|
},
|
|
81
|
-
experiment: (key: string) => {
|
|
82
|
+
$experiment: (key: string) => {
|
|
82
83
|
Object.defineProperty(config, NonEmumeralProperties.EXPERIMENT, {
|
|
83
84
|
configurable: false,
|
|
84
85
|
enumerable: false,
|
|
@@ -87,13 +88,13 @@ export const createConfigBuilder = <ZodTypes>(
|
|
|
87
88
|
|
|
88
89
|
return configBuilder;
|
|
89
90
|
},
|
|
90
|
-
extend: (builder: ConfigBuilder<Config>) => {
|
|
91
|
-
config = transformConfigSync<Config>(builder
|
|
91
|
+
$extend: (builder: ConfigBuilder<Config>) => {
|
|
92
|
+
config = transformConfigSync<Config>(builder.$values(), [cloneNonEnumerableValues]);
|
|
92
93
|
// @ts-expect-error private property
|
|
93
94
|
callbacks = { ...builder.__callbacks } as Partial<Record<keyof Config, DerivedValueCallback>>;
|
|
94
95
|
},
|
|
95
|
-
flush: () => {
|
|
96
|
-
const values = configBuilder
|
|
96
|
+
$flush: () => {
|
|
97
|
+
const values = configBuilder.$values();
|
|
97
98
|
config = {} as Config;
|
|
98
99
|
|
|
99
100
|
Object.defineProperty(config, NonEmumeralProperties.ZCB, {
|
|
@@ -104,18 +105,18 @@ export const createConfigBuilder = <ZodTypes>(
|
|
|
104
105
|
|
|
105
106
|
return values;
|
|
106
107
|
},
|
|
107
|
-
fork: () => createConfigBuilder<Config>(zodSchema, derivedValueCallbacks),
|
|
108
|
-
toJson: () => JSON.stringify(configBuilder
|
|
109
|
-
validate: () => {
|
|
108
|
+
$fork: () => createConfigBuilder<Config>(zodSchema, derivedValueCallbacks),
|
|
109
|
+
$toJson: () => JSON.stringify(configBuilder.$values(), jsonStringifyReplacer, 2),
|
|
110
|
+
$validate: () => {
|
|
110
111
|
try {
|
|
111
|
-
zodSchema.parse(configBuilder
|
|
112
|
+
zodSchema.parse(configBuilder.$values());
|
|
112
113
|
return true;
|
|
113
114
|
} catch (error: unknown) {
|
|
114
115
|
console.error(error);
|
|
115
116
|
return false;
|
|
116
117
|
}
|
|
117
118
|
},
|
|
118
|
-
values: () => {
|
|
119
|
+
$values: () => {
|
|
119
120
|
for (const property in callbacks) {
|
|
120
121
|
const callback = callbacks[property];
|
|
121
122
|
|
|
@@ -13,20 +13,20 @@ export const importValidateTransformWriteConfig = (
|
|
|
13
13
|
) => {
|
|
14
14
|
import(resolve(process.cwd(), inputFile))
|
|
15
15
|
.then(({ default: configBuilder }: { default: ConfigBuilder<object> }) => {
|
|
16
|
-
if (!configBuilder
|
|
16
|
+
if (!configBuilder.$validate()) {
|
|
17
17
|
shelljs.echo(`zcd ${command} => invalid config`);
|
|
18
|
-
shelljs.echo(`zcd ${command} => config values:\n${configBuilder
|
|
19
|
-
shelljs.echo(`zcd ${command} => errors:\n${JSON.stringify(configBuilder
|
|
18
|
+
shelljs.echo(`zcd ${command} => config values:\n${configBuilder.$toJson()}\n`);
|
|
19
|
+
shelljs.echo(`zcd ${command} => errors:\n${JSON.stringify(configBuilder.$errors(), undefined, 2)}\n`);
|
|
20
20
|
shelljs.exit(1);
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
shelljs.echo('zcd ${command} => valid config');
|
|
24
|
-
shelljs.echo(`zcd ${command} => config values:\n${configBuilder
|
|
24
|
+
shelljs.echo(`zcd ${command} => config values:\n${configBuilder.$toJson()}\n`);
|
|
25
25
|
|
|
26
26
|
if (experimentCallbackFile) {
|
|
27
27
|
import(resolve(process.cwd(), experimentCallbackFile))
|
|
28
28
|
.then(({ default: experimentsCallback }: { default: SetupExperimentsCallback }) => {
|
|
29
|
-
void transformWriteConfig(configBuilder
|
|
29
|
+
void transformWriteConfig(configBuilder.$values(), { experimentsCallback, outputFile });
|
|
30
30
|
})
|
|
31
31
|
.catch((error: unknown) => {
|
|
32
32
|
if (error instanceof Error) {
|
|
@@ -41,7 +41,7 @@ export const importValidateTransformWriteConfig = (
|
|
|
41
41
|
return;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
void transformWriteConfig(configBuilder
|
|
44
|
+
void transformWriteConfig(configBuilder.$values(), { outputFile });
|
|
45
45
|
})
|
|
46
46
|
.catch((error: unknown) => {
|
|
47
47
|
if (error instanceof Error) {
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
export const RESERVED_KEYWORDS = new Set([
|
|
2
|
-
'disable',
|
|
3
|
-
'errors',
|
|
4
|
-
'experiment',
|
|
5
|
-
'extend',
|
|
6
|
-
'flush',
|
|
7
|
-
'fork',
|
|
8
|
-
'toJson',
|
|
9
|
-
'validate',
|
|
10
|
-
'values',
|
|
2
|
+
'$disable',
|
|
3
|
+
'$errors',
|
|
4
|
+
'$experiment',
|
|
5
|
+
'$extend',
|
|
6
|
+
'$flush',
|
|
7
|
+
'$fork',
|
|
8
|
+
'$toJson',
|
|
9
|
+
'$validate',
|
|
10
|
+
'$values',
|
|
11
11
|
]);
|
|
12
12
|
|
|
13
13
|
export const isPropertyReservedWord = (propertyName: string) => RESERVED_KEYWORDS.has(propertyName);
|