zcb 0.3.4 → 0.3.6

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.
Files changed (118) hide show
  1. package/README.md +1 -1
  2. package/dist/cjs/cli.cjs +1 -1
  3. package/dist/cjs/cli.cjs.map +1 -1
  4. package/dist/cjs/index.cjs +1 -1
  5. package/dist/cjs/index.cjs.map +1 -1
  6. package/dist/cjs/templates/config.ts.hbs +1 -1
  7. package/dist/esm/cli.mjs +1 -1
  8. package/dist/esm/cli.mjs.map +1 -1
  9. package/dist/esm/index.mjs +1 -1
  10. package/dist/esm/index.mjs.map +1 -1
  11. package/dist/esm/templates/config.ts.hbs +1 -1
  12. package/dist/production.analysis.txt +23 -23
  13. package/dist/types/cjs/cli.d.cts +7 -1
  14. package/dist/types/cjs/cli.d.cts.map +1 -1
  15. package/dist/types/cjs/createConfigBuilder.d.cts +4 -4
  16. package/dist/types/cjs/createConfigBuilder.d.cts.map +1 -1
  17. package/dist/types/cjs/createConfigParser.d.cts.map +1 -1
  18. package/dist/types/cjs/transformers/runExperiments.d.cts.map +1 -1
  19. package/dist/types/cjs/utils/collateDefaultValues.d.cts +2 -2
  20. package/dist/types/cjs/utils/collateDefaultValues.d.cts.map +1 -1
  21. package/dist/types/cjs/utils/collateObjectPropertyDefaults.d.cts +2 -2
  22. package/dist/types/cjs/utils/collateObjectPropertyDefaults.d.cts.map +1 -1
  23. package/dist/types/cjs/utils/hasArrayInvalidDefaults.d.cts +3 -0
  24. package/dist/types/cjs/utils/hasArrayInvalidDefaults.d.cts.map +1 -0
  25. package/dist/types/cjs/utils/hasObjectPropertyDefaults.d.cts +3 -0
  26. package/dist/types/cjs/utils/hasObjectPropertyDefaults.d.cts.map +1 -0
  27. package/dist/types/cjs/utils/hasRecordInvalidDefaults.d.cts +3 -0
  28. package/dist/types/cjs/utils/hasRecordInvalidDefaults.d.cts.map +1 -0
  29. package/dist/types/cjs/utils/importValidateTransformWriteConfig.d.cts +2 -2
  30. package/dist/types/cjs/utils/importValidateTransformWriteConfig.d.cts.map +1 -1
  31. package/dist/types/cjs/utils/isInvalidPropertyOverride.d.cts +1 -1
  32. package/dist/types/cjs/utils/isInvalidPropertyOverride.d.cts.map +1 -1
  33. package/dist/types/cjs/utils/isPropertyReservedWord.d.cts.map +1 -1
  34. package/dist/types/cjs/utils/isSchemaValid.d.cts +2 -2
  35. package/dist/types/cjs/utils/isSchemaValid.d.cts.map +1 -1
  36. package/dist/types/cjs/utils/isValidPropertyDefinition.d.cts +2 -2
  37. package/dist/types/cjs/utils/isValidPropertyDefinition.d.cts.map +1 -1
  38. package/dist/types/cjs/utils/isValidValue.d.cts.map +1 -1
  39. package/dist/types/cjs/utils/transformConfig.d.cts.map +1 -1
  40. package/dist/types/cjs/utils/transformWriteConfig.d.cts.map +1 -1
  41. package/dist/types/esm/cli.d.ts +7 -1
  42. package/dist/types/esm/cli.d.ts.map +1 -1
  43. package/dist/types/esm/createConfigBuilder.d.ts +4 -4
  44. package/dist/types/esm/createConfigBuilder.d.ts.map +1 -1
  45. package/dist/types/esm/createConfigParser.d.ts.map +1 -1
  46. package/dist/types/esm/transformers/runExperiments.d.ts.map +1 -1
  47. package/dist/types/esm/utils/collateDefaultValues.d.ts +2 -2
  48. package/dist/types/esm/utils/collateDefaultValues.d.ts.map +1 -1
  49. package/dist/types/esm/utils/collateObjectPropertyDefaults.d.ts +2 -2
  50. package/dist/types/esm/utils/collateObjectPropertyDefaults.d.ts.map +1 -1
  51. package/dist/types/esm/utils/hasArrayInvalidDefaults.d.ts +3 -0
  52. package/dist/types/esm/utils/hasArrayInvalidDefaults.d.ts.map +1 -0
  53. package/dist/types/esm/utils/hasObjectPropertyDefaults.d.ts +3 -0
  54. package/dist/types/esm/utils/hasObjectPropertyDefaults.d.ts.map +1 -0
  55. package/dist/types/esm/utils/hasRecordInvalidDefaults.d.ts +3 -0
  56. package/dist/types/esm/utils/hasRecordInvalidDefaults.d.ts.map +1 -0
  57. package/dist/types/esm/utils/importValidateTransformWriteConfig.d.ts +2 -2
  58. package/dist/types/esm/utils/importValidateTransformWriteConfig.d.ts.map +1 -1
  59. package/dist/types/esm/utils/isInvalidPropertyOverride.d.ts +1 -1
  60. package/dist/types/esm/utils/isInvalidPropertyOverride.d.ts.map +1 -1
  61. package/dist/types/esm/utils/isPropertyReservedWord.d.ts.map +1 -1
  62. package/dist/types/esm/utils/isSchemaValid.d.ts +2 -2
  63. package/dist/types/esm/utils/isSchemaValid.d.ts.map +1 -1
  64. package/dist/types/esm/utils/isValidPropertyDefinition.d.ts +2 -2
  65. package/dist/types/esm/utils/isValidPropertyDefinition.d.ts.map +1 -1
  66. package/dist/types/esm/utils/isValidValue.d.ts.map +1 -1
  67. package/dist/types/esm/utils/transformConfig.d.ts.map +1 -1
  68. package/dist/types/esm/utils/transformWriteConfig.d.ts.map +1 -1
  69. package/dist/types/tsconfig.build.tsbuildinfo +1 -1
  70. package/package.json +49 -45
  71. package/pnpm-workspace.yaml +26 -0
  72. package/src/__testUtils__/builtConfig.ts +1 -1
  73. package/src/__testUtils__/configBuilder.ts +29 -23
  74. package/src/__testUtils__/schema.ts +2 -2
  75. package/src/cli.ts +9 -3
  76. package/src/createConfigBuilder.test.ts +30 -26
  77. package/src/createConfigBuilder.ts +45 -51
  78. package/src/createConfigParser.ts +1 -1
  79. package/src/createConfigReader.test.ts +2 -2
  80. package/src/createConfigReader.ts +2 -2
  81. package/src/templates/config.ts.hbs +1 -1
  82. package/src/transformers/cloneNonEnumerableValues.ts +1 -1
  83. package/src/transformers/runExperiments.ts +16 -5
  84. package/src/utils/collateDefaultValues.ts +15 -15
  85. package/src/utils/collateObjectPropertyDefaults.ts +6 -4
  86. package/src/utils/hasArrayInvalidDefaults.ts +6 -0
  87. package/src/utils/hasObjectPropertyDefaults.ts +5 -0
  88. package/src/utils/hasRecordInvalidDefaults.ts +6 -0
  89. package/src/utils/importValidateTransformWriteConfig.ts +25 -13
  90. package/src/utils/isInvalidPropertyOverride.ts +2 -2
  91. package/src/utils/isPropertyReservedWord.ts +1 -1
  92. package/src/utils/isSchemaValid.ts +2 -2
  93. package/src/utils/isValidPropertyDefinition.ts +3 -3
  94. package/src/utils/isValidValue.ts +1 -5
  95. package/src/utils/transformConfig.ts +8 -2
  96. package/src/utils/transformWriteConfig.test.ts +1 -1
  97. package/src/utils/transformWriteConfig.ts +6 -4
  98. package/dist/types/cjs/utils/arrayHasInvalidDefaults.d.cts +0 -3
  99. package/dist/types/cjs/utils/arrayHasInvalidDefaults.d.cts.map +0 -1
  100. package/dist/types/cjs/utils/jsonStringifyReplacer.d.cts +0 -109
  101. package/dist/types/cjs/utils/jsonStringifyReplacer.d.cts.map +0 -1
  102. package/dist/types/cjs/utils/objectPropertyHasDefaults.d.cts +0 -3
  103. package/dist/types/cjs/utils/objectPropertyHasDefaults.d.cts.map +0 -1
  104. package/dist/types/cjs/utils/recordHasInvalidDefaults.d.cts +0 -3
  105. package/dist/types/cjs/utils/recordHasInvalidDefaults.d.cts.map +0 -1
  106. package/dist/types/esm/utils/arrayHasInvalidDefaults.d.ts +0 -3
  107. package/dist/types/esm/utils/arrayHasInvalidDefaults.d.ts.map +0 -1
  108. package/dist/types/esm/utils/jsonStringifyReplacer.d.ts +0 -109
  109. package/dist/types/esm/utils/jsonStringifyReplacer.d.ts.map +0 -1
  110. package/dist/types/esm/utils/objectPropertyHasDefaults.d.ts +0 -3
  111. package/dist/types/esm/utils/objectPropertyHasDefaults.d.ts.map +0 -1
  112. package/dist/types/esm/utils/recordHasInvalidDefaults.d.ts +0 -3
  113. package/dist/types/esm/utils/recordHasInvalidDefaults.d.ts.map +0 -1
  114. package/src/__snapshots__/createConfigBuilder.test.ts.snap +0 -30
  115. package/src/utils/arrayHasInvalidDefaults.ts +0 -6
  116. package/src/utils/jsonStringifyReplacer.ts +0 -6
  117. package/src/utils/objectPropertyHasDefaults.ts +0 -5
  118. package/src/utils/recordHasInvalidDefaults.ts +0 -6
package/package.json CHANGED
@@ -1,24 +1,28 @@
1
1
  {
2
2
  "name": "zcb",
3
3
  "description": "Build configs with type safety from zod schema.",
4
- "version": "0.3.4",
4
+ "version": "0.3.6",
5
5
  "author": "miami-man",
6
6
  "license": "MIT",
7
7
  "homepage": "https://github.com/badbatch/zod-config-builder",
8
- "repository": "badbatch/zod-config-builder",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "badbatch/zod-config-builder"
11
+ },
9
12
  "bugs": "https://github.com/badbatch/zod-config-builder/issues",
10
13
  "type": "module",
11
14
  "bin": {
12
15
  "zcb": "./bin/zcb.js"
13
16
  },
14
- "main": "./dist/cjs/index.cjs",
17
+ "main": "./dist/cjs/index.mjs",
15
18
  "module": "./dist/esm/index.mjs",
16
- "types": "./dist/types/cjs/index.d.cts",
19
+ "types": "./dist/types/cjs/index.d.ts",
17
20
  "imports": {
18
21
  "#*": "./src/*"
19
22
  },
20
23
  "exports": {
21
24
  ".": {
25
+ "source": "./src/index.ts",
22
26
  "types": {
23
27
  "import": "./dist/types/esm/index.d.ts",
24
28
  "require": "./dist/types/cjs/index.d.cts"
@@ -27,6 +31,7 @@
27
31
  "require": "./dist/cjs/index.cjs"
28
32
  },
29
33
  "./cli": {
34
+ "source": "./src/cli.ts",
30
35
  "types": {
31
36
  "import": "./dist/types/esm/cli.d.ts",
32
37
  "require": "./dist/types/cjs/cli.d.cts"
@@ -42,56 +47,55 @@
42
47
  "@types/fs-extra": "^11.0.4",
43
48
  "@types/json-schema": "^7.0.15",
44
49
  "@types/lodash-es": "^4.17.12",
45
- "@types/shelljs": "^0.8.17",
46
- "@types/uuid": "^10.0.0",
47
- "@types/yargs": "^17.0.33",
48
- "core-js": "^3.45.0",
49
- "fs-extra": "^11.3.1",
50
- "handlebars": "^4.7.8",
50
+ "@types/shelljs": "^0.10.0",
51
+ "@types/uuid": "^11.0.0",
52
+ "@types/yargs": "^17.0.35",
53
+ "core-js": "^3.49.0",
54
+ "fs-extra": "^11.4.0",
55
+ "handlebars": "^4.7.9",
51
56
  "json-schema": "^0.4.0",
52
- "lodash-es": "^4.17.21",
57
+ "lodash-es": "^4.18.1",
53
58
  "shelljs": "^0.10.0",
54
59
  "ts-node": "^10.9.2",
55
60
  "ts-toolbelt": "^9.6.0",
56
- "type-fest": "^4.41.0",
57
- "uuid": "^11.1.0",
58
- "yargs": "^18.0.0",
59
- "zod": "^3.25.76",
60
- "zod-to-json-schema": "^3.24.6"
61
+ "type-fest": "^5.8.0",
62
+ "uuid": "^14.0.1",
63
+ "yargs": "^18.1.0",
64
+ "zod": "^4.4.3",
65
+ "zod-to-json-schema": "^3.25.2"
61
66
  },
62
67
  "devDependencies": {
63
- "@commitlint/cli": "^19.8.1",
64
- "@jest/globals": "^29.7.0",
65
- "@repodog/cli": "^1.6.45",
66
- "@repodog/commitlint-config": "^19.0.5",
67
- "@repodog/eslint-config": "^9.0.18",
68
- "@repodog/eslint-config-jest": "^9.0.6",
69
- "@repodog/jest-config": "^29.0.5",
70
- "@repodog/markdownlint-config": "^1.2.5",
71
- "@repodog/prettier-config": "^3.0.3",
72
- "@repodog/rollup-config": "^4.0.11",
73
- "@repodog/swc-config": "^1.0.12",
74
- "@repodog/syncpack-config": "^13.0.4",
75
- "@repodog/ts-config": "^5.0.5",
76
- "@rollup/plugin-swc": "^0.3.0",
77
- "@swc/core": "^1.13.3",
68
+ "@commitlint/cli": "^21.2.1",
69
+ "@jest/globals": "^30.4.1",
70
+ "@repodog/cli": "^1.7.0",
71
+ "@repodog/commitlint-config": "^21.0.3",
72
+ "@repodog/eslint-config": "^9.0.30",
73
+ "@repodog/eslint-config-jest": "^9.0.10",
74
+ "@repodog/jest-config": "^30.0.4",
75
+ "@repodog/markdownlint-config": "^1.2.7",
76
+ "@repodog/prettier-config": "^3.0.5",
77
+ "@repodog/rollup-config": "^4.0.18",
78
+ "@repodog/swc-config": "^1.0.16",
79
+ "@repodog/syncpack-config": "^15.0.4",
80
+ "@repodog/ts-config": "^6.0.3",
81
+ "@rollup/plugin-swc": "^0.4.1",
82
+ "@swc/core": "^1.15.47",
78
83
  "@swc/jest": "^0.2.39",
79
- "@types/jest": "^29.5.13",
80
- "@types/node": "^24.2.1",
81
- "cts-types": "^0.0.10",
82
- "del-cli": "^6.0.0",
83
- "eslint": "^9.33.0",
84
+ "@types/jest": "^30.0.0",
85
+ "@types/node": "^26.1.2",
86
+ "cts-types": "^0.0.11",
87
+ "del-cli": "^7.0.0",
88
+ "eslint": "^10.8.0",
84
89
  "generate-changelog": "^1.8.0",
85
90
  "husky": "^9.1.7",
86
- "jest": "^29.7.0",
87
- "jest-environment-jsdom": "^29.7.0",
88
- "jest-environment-node": "^29.7.0",
89
- "markdownlint-cli2": "^0.18.1",
90
- "prettier": "^3.6.2",
91
- "rollup": "^4.46.2",
91
+ "jest": "^30.4.2",
92
+ "jest-environment-node": "^30.4.1",
93
+ "markdownlint-cli2": "^0.23.2",
94
+ "prettier": "^3.9.6",
95
+ "rollup": "^4.62.3",
92
96
  "suppress-experimental-warnings": "^2.0.0",
93
- "syncpack": "^13.0.4",
94
- "typescript": "^5.9.2"
97
+ "syncpack": "^15.3.2",
98
+ "typescript": "^6.0.3"
95
99
  },
96
100
  "keywords": [
97
101
  "config",
@@ -113,7 +117,7 @@
113
117
  "lint:code": "eslint .",
114
118
  "lint:docs": "markdownlint-cli2 --config \".markdownlint.json\" \"**/*.md\" \"!**/node_modules/**\"",
115
119
  "repodog": "repodog",
116
- "syncpack": "syncpack format && syncpack list-mismatches && syncpack lint-semver-ranges",
120
+ "syncpack": "syncpack format --check && syncpack lint",
117
121
  "test": "COMPILER=swc node --require=suppress-experimental-warnings --experimental-vm-modules node_modules/jest/bin/jest.js",
118
122
  "type-check": "tsc --noEmit",
119
123
  "validate": "pnpm run syncpack && pnpm run build && pnpm run lint && pnpm run type-check && pnpm run test",
@@ -0,0 +1,26 @@
1
+ allowBuilds:
2
+ '@swc/core': false
3
+ core-js: false
4
+ unrs-resolver: false
5
+
6
+ minimumReleaseAgeExclude:
7
+ - '@repodog/cli-cut@1.4.30'
8
+ - '@repodog/cli-new@1.6.0'
9
+ - '@repodog/cli-postinstall@0.4.26'
10
+ - '@repodog/cli-publish@1.4.30'
11
+ - '@repodog/cli-setup@1.4.27'
12
+ - '@repodog/cli-utils@1.4.25'
13
+ - '@repodog/cli@1.7.0'
14
+ - '@repodog/commitlint-config@21.0.3'
15
+ - '@repodog/eslint-config-jest@9.0.10'
16
+ - '@repodog/eslint-config@9.0.30'
17
+ - '@repodog/rollup-config@4.0.18'
18
+ - '@repodog/swc-config@1.0.16'
19
+ - '@repodog/syncpack-config@15.0.4'
20
+
21
+ overrides:
22
+ 'body-parser@<1.20.6': '>=1.20.6'
23
+ 'fast-uri@>=3.0.0 <=3.1.3': '>=3.1.4'
24
+ 'js-yaml@^4.0.0': '^4.3.0'
25
+ 'js-yaml@>=5.0.0 <=5.2.1': '>=5.2.2'
26
+ 'markdown-it@<=14.1.1': '>=14.2.0'
@@ -1,4 +1,4 @@
1
- /* eslint-disable prettier/prettier, import-x/no-default-export, unicorn/numeric-separators-style, unicorn/no-null */
1
+ /* eslint-disable import-x/no-default-export, unicorn/numeric-separators-style, unicorn/no-null */
2
2
  /* This file is autogenerated, do not edit directly, your changes will not perist. */
3
3
 
4
4
  export default {
@@ -17,6 +17,32 @@ const pageBuilder = createConfigBuilder<PageType>(pageSchema);
17
17
  const sectionBuilder = createConfigBuilder<SectionType>(sectionSchema);
18
18
  const subsectionBuilder = sectionBuilder.$fork();
19
19
 
20
+ const contactDetailsPage = pageBuilder
21
+ .name('contactDetails')
22
+ .sections([
23
+ sectionBuilder.name('header').$flush(),
24
+ sectionBuilder
25
+ .name('body')
26
+ .sections([subsectionBuilder.name('main').$flush(), subsectionBuilder.name('sidebar').$flush()])
27
+ .$flush(),
28
+ sectionBuilder.name('footer').$flush(),
29
+ ])
30
+ .$flush();
31
+
32
+ const personalDetailPage = pageBuilder
33
+ .name('personalDetails')
34
+ .sections([
35
+ sectionBuilder.name('header').$flush(),
36
+ sectionBuilder
37
+ .name('body')
38
+ .sections([subsectionBuilder.name('main').$flush(), subsectionBuilder.name('sidebar').$flush()])
39
+ .$flush(),
40
+ sectionBuilder.name('footer').$flush(),
41
+ ])
42
+ .$flush();
43
+
44
+ // Required for how builder is executed
45
+ // eslint-disable-next-line unicorn/no-top-level-side-effects
20
46
  configBuilder
21
47
  .countryCode('GB')
22
48
  // Want to test using null value
@@ -26,31 +52,11 @@ configBuilder
26
52
  .enabled(true)
27
53
  .languageCodes(['en'])
28
54
  .locales(({ countryCode, languageCodes }) =>
29
- languageCodes?.length && countryCode ? languageCodes.map(code => `${code}_${countryCode}`) : [],
55
+ countryCode && languageCodes?.length ? languageCodes.map(code => `${code}_${countryCode}`) : [],
30
56
  )
31
57
  .pages({
32
- contactDetails: pageBuilder
33
- .name('contactDetails')
34
- .sections([
35
- sectionBuilder.name('header').$flush(),
36
- sectionBuilder
37
- .name('body')
38
- .sections([subsectionBuilder.name('main').$flush(), subsectionBuilder.name('sidebar').$flush()])
39
- .$flush(),
40
- sectionBuilder.name('footer').$flush(),
41
- ])
42
- .$flush(),
43
- personalDetails: pageBuilder
44
- .name('personalDetails')
45
- .sections([
46
- sectionBuilder.name('header').$flush(),
47
- sectionBuilder
48
- .name('body')
49
- .sections([subsectionBuilder.name('main').$flush(), subsectionBuilder.name('sidebar').$flush()])
50
- .$flush(),
51
- sectionBuilder.name('footer').$flush(),
52
- ])
53
- .$flush(),
58
+ contactDetails: contactDetailsPage,
59
+ personalDetails: personalDetailPage,
54
60
  })
55
61
  .routes([routeBuilder.page('personalDetails').$flush(), routeBuilder.page('contactDetails').$flush()])
56
62
  .timeouts({ apollo: 10_000 })
@@ -48,9 +48,9 @@ export const configSchema = z.object({
48
48
  languageCodes: z.array(z.enum(languageCodes)).optional(),
49
49
  locales: z.array(z.string().regex(/[a-z]{2}_[A-Z]{2}/)).optional(),
50
50
  name: z.string().optional(),
51
- pages: z.record(pageSchema).optional(),
51
+ pages: z.record(z.string(), pageSchema).optional(),
52
52
  routes: z.array(routeSchema).optional(),
53
- timeouts: z.record(z.number()).optional(),
53
+ timeouts: z.record(z.string(), z.number()).optional(),
54
54
  timezone: z.enum(timezones).optional(),
55
55
  });
56
56
 
package/src/cli.ts CHANGED
@@ -5,9 +5,15 @@ import yargs, { type Argv } from 'yargs';
5
5
  import { hideBin } from 'yargs/helpers';
6
6
  import { importValidateTransformWriteConfig } from './utils/importValidateTransformWriteConfig.ts';
7
7
 
8
- export type Commands = 'build' | 'watch';
8
+ export type CliCommands = 'build' | 'watch';
9
9
 
10
- const generateArguments = (cmdYargs: Argv) =>
10
+ export type CliArguments = Argv<{
11
+ 'experiments-callback-file'?: string;
12
+ 'input-file': string;
13
+ 'output-file': string;
14
+ }>;
15
+
16
+ const generateArguments = (cmdYargs: Argv): CliArguments =>
11
17
  cmdYargs
12
18
  .positional('input-file', {
13
19
  demandOption: true,
@@ -24,7 +30,7 @@ const generateArguments = (cmdYargs: Argv) =>
24
30
  type: 'string',
25
31
  });
26
32
 
27
- export const cli = () => {
33
+ export const cli = (): void => {
28
34
  // yargs does not provide a way to pass generic to type args.
29
35
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
30
36
  const argv = yargs(hideBin(process.argv)) as Argv<{
@@ -1,5 +1,5 @@
1
1
  import { kebabCase } from 'lodash-es';
2
- import { ZodType, z } from 'zod';
2
+ import { z } from 'zod';
3
3
  import {
4
4
  type ConfigType,
5
5
  type PageType,
@@ -112,13 +112,14 @@ describe('createConfigBuilder', () => {
112
112
  __experiment: 'FEAT_ALPHA@0.0.1',
113
113
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
114
114
  pages: expect.objectContaining({
115
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
115
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, unicorn/max-nested-calls
116
116
  contactDetails: expect.objectContaining({
117
117
  __experiment: 'FEAT_BRAVO@0.0.1',
118
118
  }),
119
119
  }),
120
120
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
121
121
  routes: expect.arrayContaining([
122
+ // eslint-disable-next-line unicorn/max-nested-calls
122
123
  expect.objectContaining({
123
124
  __experiment: 'FEAT_CHARLIE@0.0.1',
124
125
  }),
@@ -153,13 +154,14 @@ describe('createConfigBuilder', () => {
153
154
  __disabled: true,
154
155
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
155
156
  pages: expect.objectContaining({
156
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
157
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, unicorn/max-nested-calls
157
158
  contactDetails: expect.objectContaining({
158
159
  __disabled: true,
159
160
  }),
160
161
  }),
161
162
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
162
163
  routes: expect.arrayContaining([
164
+ // eslint-disable-next-line unicorn/max-nested-calls
163
165
  expect.objectContaining({
164
166
  __disabled: true,
165
167
  }),
@@ -223,7 +225,7 @@ describe('createConfigBuilder', () => {
223
225
  const { createConfigBuilder } = await import('./createConfigBuilder.ts');
224
226
 
225
227
  const extendedSchema = configSchema.extend({
226
- flags: z.record(z.boolean()).optional().default({ alpha: true, bravo: false, charlie: false }),
228
+ flags: z.record(z.string(), z.boolean()).optional().default({ alpha: true, bravo: false, charlie: false }),
227
229
  });
228
230
 
229
231
  const config = createConfigBuilder<z.infer<typeof extendedSchema>>(extendedSchema);
@@ -329,7 +331,7 @@ describe('createConfigBuilder', () => {
329
331
  const { createConfigBuilder } = await import('./createConfigBuilder.ts');
330
332
 
331
333
  const extendedSchema = configSchema.extend({
332
- flags: z.record(z.boolean().default(true)).optional(),
334
+ flags: z.record(z.string(), z.boolean().default(true)).optional(),
333
335
  });
334
336
 
335
337
  expect(() => createConfigBuilder<z.infer<typeof extendedSchema>>(extendedSchema)).toThrow(
@@ -587,7 +589,7 @@ describe('createConfigBuilder', () => {
587
589
  .languageCodes(['en'])
588
590
  .locales(({ countryCode, languageCodes }) =>
589
591
  // eslint-disable-next-line jest/no-conditional-in-test
590
- languageCodes?.length && countryCode ? languageCodes.map(code => `${code}_${countryCode}` as const) : [],
592
+ countryCode && languageCodes?.length ? languageCodes.map(code => `${code}_${countryCode}` as const) : [],
591
593
  );
592
594
 
593
595
  expect(config.$values()).toEqual({
@@ -606,7 +608,7 @@ describe('createConfigBuilder', () => {
606
608
  .languageCodes(['en'])
607
609
  .locales(({ countryCode, languageCodes }) =>
608
610
  // eslint-disable-next-line jest/no-conditional-in-test
609
- languageCodes?.length && countryCode ? languageCodes.map(code => `${code}_${countryCode}` as const) : [],
611
+ countryCode && languageCodes?.length ? languageCodes.map(code => `${code}_${countryCode}` as const) : [],
610
612
  )
611
613
  .languageCodes(['fr'], true);
612
614
 
@@ -626,7 +628,7 @@ describe('createConfigBuilder', () => {
626
628
  .languageCodes(['en'])
627
629
  .locales(({ countryCode, languageCodes }) =>
628
630
  // eslint-disable-next-line jest/no-conditional-in-test
629
- languageCodes?.length && countryCode ? languageCodes.map(code => `${code}_${countryCode}`) : [],
631
+ countryCode && languageCodes?.length ? languageCodes.map(code => `${code}_${countryCode}`) : [],
630
632
  );
631
633
 
632
634
  expect(config.$validate()).toBe(true);
@@ -635,27 +637,29 @@ describe('createConfigBuilder', () => {
635
637
  });
636
638
 
637
639
  describe('when a user stringifies the config', () => {
638
- describe('when a config value is a zod schema', () => {
639
- it('should stringify the value to JSON schema correctly', async () => {
640
- const { createConfigBuilder } = await import('./createConfigBuilder.ts');
641
-
642
- const extendedSchema = configSchema.extend({
643
- schema: z.instanceof(ZodType),
644
- });
645
-
646
- const config = createConfigBuilder<z.infer<typeof extendedSchema>>(extendedSchema);
640
+ it('should stringify the value to JSON correctly', async () => {
641
+ const { createConfigBuilder } = await import('./createConfigBuilder.ts');
642
+ const config = createConfigBuilder<ConfigType>(configSchema);
647
643
 
648
- config.schema(
649
- z
650
- .object({
651
- alpha: z.string(),
652
- bravo: z.boolean(),
653
- })
654
- .optional(),
644
+ config
645
+ .countryCode('GB')
646
+ .languageCodes(['en'])
647
+ .locales(({ countryCode, languageCodes }) =>
648
+ // eslint-disable-next-line jest/no-conditional-in-test
649
+ countryCode && languageCodes?.length ? languageCodes.map(code => `${code}_${countryCode}`) : [],
655
650
  );
656
651
 
657
- expect(config.$toJson()).toMatchSnapshot();
658
- });
652
+ expect(config.$toJson()).toMatchInlineSnapshot(`
653
+ "{
654
+ "countryCode": "GB",
655
+ "languageCodes": [
656
+ "en"
657
+ ],
658
+ "locales": [
659
+ "en_GB"
660
+ ]
661
+ }"
662
+ `);
659
663
  });
660
664
  });
661
665
  });
@@ -1,8 +1,6 @@
1
- import { type JSONSchema7 } from 'json-schema';
2
1
  import { cloneDeep, merge } from 'lodash-es';
3
2
  import { v4 as uuidV4 } from 'uuid';
4
- import { type ZodError, type z } from 'zod';
5
- import { zodToJsonSchema } from 'zod-to-json-schema';
3
+ import { ZodError, z } from 'zod';
6
4
  import { cloneNonEnumerableValues } from './transformers/cloneNonEnumerableValues.ts';
7
5
  import { collateDefaultValues } from './utils/collateDefaultValues.ts';
8
6
  import { isDerivedValueCallback } from './utils/isDerivedValueCallback.ts';
@@ -10,17 +8,16 @@ import { isInvalidPropertyOverride } from './utils/isInvalidPropertyOverride.ts'
10
8
  import { RESERVED_KEYWORDS, isPropertyReservedWord } from './utils/isPropertyReservedWord.ts';
11
9
  import { isSchemaValid } from './utils/isSchemaValid.ts';
12
10
  import { isValidValue } from './utils/isValidValue.ts';
13
- import { jsonStringifyReplacer } from './utils/jsonStringifyReplacer.ts';
14
11
  import { transformConfigSync } from './utils/transformConfig.ts';
15
12
 
16
13
  export type ConfigBuilder<ZodTypes> = {
17
14
  [Key in keyof Required<ZodTypes>]: (
18
15
  value: ZodTypes[Key] | ((c: ZodTypes) => ZodTypes[Key]),
19
- override?: boolean,
16
+ isOverride?: boolean,
20
17
  ) => ConfigBuilder<ZodTypes>;
21
18
  } & {
22
19
  $disable: () => ConfigBuilder<ZodTypes>;
23
- $errors: () => ZodError['errors'];
20
+ $errors: () => ZodError['issues'];
24
21
  $experiment: (key: string) => ConfigBuilder<ZodTypes>;
25
22
  $extend: (configBuilder: ConfigBuilder<ZodTypes>) => ConfigBuilder<ZodTypes>;
26
23
  $flush: () => ZodTypes;
@@ -38,7 +35,7 @@ export type CreateConfigBuilderOptions = {
38
35
  };
39
36
 
40
37
  export const createConfigBuilder = <ZodTypes>(
41
- zodSchema: z.ZodSchema,
38
+ zodSchema: z.ZodType,
42
39
  options: CreateConfigBuilderOptions = {},
43
40
  derivedValueCallbacks: Partial<
44
41
  Record<
@@ -58,9 +55,7 @@ export const createConfigBuilder = <ZodTypes>(
58
55
 
59
56
  type DerivedValueCallback<K extends keyof Config = keyof Config> = (c: Config) => Config[K];
60
57
 
61
- // Added to simplify type returned from zodToJsonSchema.
62
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
63
- const jsonSchema = zodToJsonSchema(zodSchema) as JSONSchema7;
58
+ const jsonSchema = z.toJSONSchema(zodSchema);
64
59
 
65
60
  if (isSchemaValid(jsonSchema)) {
66
61
  throw new Error(`The root type of a config schema must be "object", but received "${String(jsonSchema.type)}"`);
@@ -68,29 +63,29 @@ export const createConfigBuilder = <ZodTypes>(
68
63
 
69
64
  const uuid = options.overrides?.uuid ?? uuidV4;
70
65
 
71
- const addNonEnumeralBaseProperties = (conf: Config) => {
72
- Object.defineProperty(conf, '__zcb', {
73
- configurable: false,
74
- enumerable: false,
75
- value: true,
76
- });
77
-
78
- Object.defineProperty(conf, '__id', {
79
- configurable: false,
80
- enumerable: false,
81
- value: uuid(),
82
- });
83
-
84
- if (options.type) {
85
- Object.defineProperty(conf, '__type', {
66
+ const addNonEnumeralBaseProperties = (conf: Config): void => {
67
+ Object.defineProperties(conf, {
68
+ __id: {
86
69
  configurable: false,
87
70
  enumerable: false,
88
- value: options.type,
89
- });
90
- }
71
+ value: uuid(),
72
+ },
73
+ __zcb: {
74
+ configurable: false,
75
+ enumerable: false,
76
+ value: true,
77
+ },
78
+ ...(options.type && {
79
+ __type: {
80
+ configurable: false,
81
+ enumerable: false,
82
+ value: options.type,
83
+ },
84
+ }),
85
+ });
91
86
  };
92
87
 
93
- const createInitialConfig = () => {
88
+ const createInitialConfig = (): Config => {
94
89
  // Not casting here as loads of downstream impacts that make
95
90
  // subsequent typing really difficult.
96
91
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
@@ -102,7 +97,7 @@ export const createConfigBuilder = <ZodTypes>(
102
97
  let config = createInitialConfig();
103
98
  let callbacks: Partial<Record<keyof Config, DerivedValueCallback>> = { ...derivedValueCallbacks };
104
99
 
105
- // Aimed at making it easier for Typescript to derive type.
100
+ // Aimed at making it easier for TypeScript to derive type.
106
101
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
107
102
  const configBuilder = {
108
103
  $disable: () => {
@@ -119,9 +114,7 @@ export const createConfigBuilder = <ZodTypes>(
119
114
  zodSchema.parse(configBuilder.$values());
120
115
  return [];
121
116
  } catch (error: unknown) {
122
- // This will always be an instance of ZodError.
123
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
124
- return (error as ZodError).errors;
117
+ return error instanceof ZodError ? error.issues : [];
125
118
  }
126
119
  },
127
120
  $experiment: (key: string) => {
@@ -136,7 +129,7 @@ export const createConfigBuilder = <ZodTypes>(
136
129
  $extend: (builder: ConfigBuilder<Config>) => {
137
130
  config = transformConfigSync<Config>(builder.$values(), [cloneNonEnumerableValues]);
138
131
  // @ts-expect-error private property
139
- // As __callbacks is private property, Typescript doesn't know what type is.
132
+ // As __callbacks is private property, TypeScript doesn't know what type is.
140
133
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
141
134
  callbacks = { ...builder.__callbacks } as Partial<Record<keyof Config, DerivedValueCallback>>;
142
135
  },
@@ -146,7 +139,7 @@ export const createConfigBuilder = <ZodTypes>(
146
139
  return values;
147
140
  },
148
141
  $fork: () => createConfigBuilder<Config>(zodSchema, options, derivedValueCallbacks, initialValues),
149
- $toJson: () => JSON.stringify(configBuilder.$values(), jsonStringifyReplacer, 2),
142
+ $toJson: () => JSON.stringify(configBuilder.$values(), undefined, 2),
150
143
  $validate: () => {
151
144
  try {
152
145
  zodSchema.parse(configBuilder.$values());
@@ -157,13 +150,13 @@ export const createConfigBuilder = <ZodTypes>(
157
150
  }
158
151
  },
159
152
  $values: () => {
160
- for (const property in callbacks) {
161
- const callback = callbacks[property];
153
+ for (const propertyName in callbacks) {
154
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
155
+ const castPropertyName = propertyName as keyof Config;
156
+ const callback = callbacks[castPropertyName];
162
157
 
163
158
  if (callback) {
164
- // Typescript not inferring property is key of Config.
165
- // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
166
- config[property as keyof Config] = callback(config);
159
+ config[castPropertyName] = callback(config);
167
160
  }
168
161
  }
169
162
 
@@ -190,33 +183,34 @@ export const createConfigBuilder = <ZodTypes>(
190
183
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
191
184
  const castPropertyName = propertyName as keyof Config;
192
185
 
193
- if (!jsonSchema.required?.includes(propertyName) && !(propertyName in config)) {
186
+ if (!jsonSchema.required?.includes(propertyName) && !Object.hasOwn(config, propertyName)) {
194
187
  // @ts-expect-error Optional properties we want to exist in the config with undefined initial values.
195
188
  config[propertyName] = undefined;
196
189
  }
197
190
 
198
- // Aimed at making it easier for Typescript to derive type.
191
+ // Aimed at making it easier for TypeScript to derive type.
199
192
  // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
200
- configBuilder[castPropertyName] = ((value: Config[keyof Config] | DerivedValueCallback, override?: boolean) => {
201
- if (isInvalidPropertyOverride(config[castPropertyName], override)) {
193
+ configBuilder[castPropertyName] = ((value: Config[keyof Config] | DerivedValueCallback, toOverride?: boolean) => {
194
+ if (isInvalidPropertyOverride(config[castPropertyName], toOverride)) {
202
195
  throw new Error(
203
- `A value already exists for "${String(
204
- castPropertyName,
205
- )}". You may be trying to add a new values before flushing the old one. If you intended to override the existing value, pass in true as the second argument.`,
196
+ // ESLint unable to derive value can also be symbol or number
197
+ // eslint-disable-next-line unicorn/no-useless-coercion
198
+ `A value already exists for "${String(castPropertyName)}". You may be trying to add a new values before flushing the old one. If you intended to override the existing value, pass in true as the second argument.`,
206
199
  );
207
200
  }
208
201
 
209
- let propertyValue: Config[keyof Config];
210
202
  const MAX_DEPTH = 1;
211
203
 
212
204
  if (!isValidValue(value)) {
213
205
  throw new Error(
214
- `"${String(
215
- castPropertyName,
216
- )}" value has a depth greater than ${String(MAX_DEPTH)}. To pass in objects with a depth greater than ${String(MAX_DEPTH)}, create a builder for that config slice.`,
206
+ // ESLint unable to derive value can also be symbol or number
207
+ // eslint-disable-next-line unicorn/no-useless-coercion
208
+ `"${String(castPropertyName)}" value has a depth greater than ${String(MAX_DEPTH)}. To pass in objects with a depth greater than ${String(MAX_DEPTH)}, create a builder for that config slice.`,
217
209
  );
218
210
  }
219
211
 
212
+ let propertyValue: Config[keyof Config];
213
+
220
214
  if (isDerivedValueCallback<DerivedValueCallback>(value)) {
221
215
  callbacks[castPropertyName] = value;
222
216
  propertyValue = value(config);
@@ -5,7 +5,7 @@ import { transformConfig } from './utils/transformConfig.ts';
5
5
  export const createConfigParser = async <Config extends object>(
6
6
  config: Config,
7
7
  { experimentsCallback }: ConfigParserOptions = {},
8
- ) => {
8
+ ): Promise<Config> => {
9
9
  const handlers: TransformConfigHandler[] = [];
10
10
 
11
11
  if (experimentsCallback) {
@@ -20,8 +20,8 @@ describe('createConfigReader', () => {
20
20
  describe('when that value is a boolean', () => {
21
21
  it('should return the correct value', () => {
22
22
  const reader = createReader();
23
- const value = reader.read('enabled');
24
- expect(value).toBe(true);
23
+ const isEnabled = reader.read('enabled');
24
+ expect(isEnabled).toBe(true);
25
25
  });
26
26
  });
27
27