├── .gitignore ├── .jtzrc.yml.example ├── LICENSE ├── index.ts ├── lab.json ├── package-lock.json ├── package.json ├── readme.md ├── src ├── cli.ts ├── getConfig.ts └── jsonToZod.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | lab.ts -------------------------------------------------------------------------------- /.jtzrc.yml.example: -------------------------------------------------------------------------------- 1 | zodValueOverrides: 2 | schema: 3 | page: 4 | created: "z.string().date()" 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright (c) 2021, Stefan Terdell 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | import {jsonToZod} from "./src/jsonToZod" 2 | export default jsonToZod 3 | export {jsonToZod} -------------------------------------------------------------------------------- /lab.json: -------------------------------------------------------------------------------- 1 | { 2 | "troublesome - key - 123": null, 3 | "str": "hello", 4 | "nr": 123, 5 | "bool": true, 6 | "obj": { 7 | "str": "hello", 8 | "nr": 123, 9 | "bool": true, 10 | "innerObject": { 11 | "str": "hello", 12 | "nr": 123, 13 | "bool": true 14 | } 15 | }, 16 | "emptyArr": [], 17 | "strArr": [ 18 | "hello" 19 | ], 20 | "nrArr": [ 21 | 123 22 | ], 23 | "boolArr": [ 24 | true 25 | ], 26 | "objectArr": [ 27 | { 28 | "str": "hello", 29 | "nr": 123, 30 | "bool": true 31 | } 32 | ], 33 | "arrArr": [ 34 | [] 35 | ], 36 | "arrArrArr": [ 37 | [ 38 | [] 39 | ] 40 | ] 41 | } -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "json-to-zod", 3 | "version": "1.1.2", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "json-to-zod", 9 | "version": "1.1.2", 10 | "license": "ISC", 11 | "dependencies": { 12 | "cosmiconfig": "^9.0.0", 13 | "prettier": "^2.3.2", 14 | "zod": "^3.7.1" 15 | }, 16 | "bin": { 17 | "json-to-zod": "src/cli.js" 18 | }, 19 | "devDependencies": { 20 | "@types/node": "^16.6.0", 21 | "@types/prettier": "^2.3.2", 22 | "copyfiles": "^2.4.1", 23 | "rimraf": "^3.0.2", 24 | "typescript": "^4.3.5" 25 | } 26 | }, 27 | "node_modules/@babel/code-frame": { 28 | "version": "7.24.7", 29 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", 30 | "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", 31 | "dependencies": { 32 | "@babel/highlight": "^7.24.7", 33 | "picocolors": "^1.0.0" 34 | }, 35 | "engines": { 36 | "node": ">=6.9.0" 37 | } 38 | }, 39 | "node_modules/@babel/helper-validator-identifier": { 40 | "version": "7.24.7", 41 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", 42 | "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", 43 | "engines": { 44 | "node": ">=6.9.0" 45 | } 46 | }, 47 | "node_modules/@babel/highlight": { 48 | "version": "7.24.7", 49 | "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", 50 | "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", 51 | "dependencies": { 52 | "@babel/helper-validator-identifier": "^7.24.7", 53 | "chalk": "^2.4.2", 54 | "js-tokens": "^4.0.0", 55 | "picocolors": "^1.0.0" 56 | }, 57 | "engines": { 58 | "node": ">=6.9.0" 59 | } 60 | }, 61 | "node_modules/@types/node": { 62 | "version": "16.6.0", 63 | "resolved": "https://registry.npmjs.org/@types/node/-/node-16.6.0.tgz", 64 | "integrity": "sha512-OyiZPohMMjZEYqcVo/UJ04GyAxXOJEZO/FpzyXxcH4r/ArrVoXHf4MbUrkLp0Tz7/p1mMKpo5zJ6ZHl8XBNthQ==", 65 | "dev": true 66 | }, 67 | "node_modules/@types/prettier": { 68 | "version": "2.3.2", 69 | "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.3.2.tgz", 70 | "integrity": "sha512-eI5Yrz3Qv4KPUa/nSIAi0h+qX0XyewOliug5F2QAtuRg6Kjg6jfmxe1GIwoIRhZspD1A0RP8ANrPwvEXXtRFog==", 71 | "dev": true 72 | }, 73 | "node_modules/ansi-regex": { 74 | "version": "5.0.0", 75 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", 76 | "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", 77 | "dev": true, 78 | "engines": { 79 | "node": ">=8" 80 | } 81 | }, 82 | "node_modules/ansi-styles": { 83 | "version": "4.3.0", 84 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 85 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 86 | "dev": true, 87 | "dependencies": { 88 | "color-convert": "^2.0.1" 89 | }, 90 | "engines": { 91 | "node": ">=8" 92 | }, 93 | "funding": { 94 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 95 | } 96 | }, 97 | "node_modules/argparse": { 98 | "version": "2.0.1", 99 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 100 | "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" 101 | }, 102 | "node_modules/balanced-match": { 103 | "version": "1.0.2", 104 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 105 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 106 | "dev": true 107 | }, 108 | "node_modules/brace-expansion": { 109 | "version": "1.1.11", 110 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 111 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 112 | "dev": true, 113 | "dependencies": { 114 | "balanced-match": "^1.0.0", 115 | "concat-map": "0.0.1" 116 | } 117 | }, 118 | "node_modules/callsites": { 119 | "version": "3.1.0", 120 | "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", 121 | "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", 122 | "engines": { 123 | "node": ">=6" 124 | } 125 | }, 126 | "node_modules/chalk": { 127 | "version": "2.4.2", 128 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", 129 | "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", 130 | "dependencies": { 131 | "ansi-styles": "^3.2.1", 132 | "escape-string-regexp": "^1.0.5", 133 | "supports-color": "^5.3.0" 134 | }, 135 | "engines": { 136 | "node": ">=4" 137 | } 138 | }, 139 | "node_modules/chalk/node_modules/ansi-styles": { 140 | "version": "3.2.1", 141 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", 142 | "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", 143 | "dependencies": { 144 | "color-convert": "^1.9.0" 145 | }, 146 | "engines": { 147 | "node": ">=4" 148 | } 149 | }, 150 | "node_modules/chalk/node_modules/color-convert": { 151 | "version": "1.9.3", 152 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", 153 | "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", 154 | "dependencies": { 155 | "color-name": "1.1.3" 156 | } 157 | }, 158 | "node_modules/chalk/node_modules/color-name": { 159 | "version": "1.1.3", 160 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", 161 | "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" 162 | }, 163 | "node_modules/cliui": { 164 | "version": "7.0.4", 165 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", 166 | "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", 167 | "dev": true, 168 | "dependencies": { 169 | "string-width": "^4.2.0", 170 | "strip-ansi": "^6.0.0", 171 | "wrap-ansi": "^7.0.0" 172 | } 173 | }, 174 | "node_modules/color-convert": { 175 | "version": "2.0.1", 176 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 177 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 178 | "dev": true, 179 | "dependencies": { 180 | "color-name": "~1.1.4" 181 | }, 182 | "engines": { 183 | "node": ">=7.0.0" 184 | } 185 | }, 186 | "node_modules/color-name": { 187 | "version": "1.1.4", 188 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 189 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 190 | "dev": true 191 | }, 192 | "node_modules/concat-map": { 193 | "version": "0.0.1", 194 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 195 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 196 | "dev": true 197 | }, 198 | "node_modules/copyfiles": { 199 | "version": "2.4.1", 200 | "resolved": "https://registry.npmjs.org/copyfiles/-/copyfiles-2.4.1.tgz", 201 | "integrity": "sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==", 202 | "dev": true, 203 | "dependencies": { 204 | "glob": "^7.0.5", 205 | "minimatch": "^3.0.3", 206 | "mkdirp": "^1.0.4", 207 | "noms": "0.0.0", 208 | "through2": "^2.0.1", 209 | "untildify": "^4.0.0", 210 | "yargs": "^16.1.0" 211 | }, 212 | "bin": { 213 | "copyfiles": "copyfiles", 214 | "copyup": "copyfiles" 215 | } 216 | }, 217 | "node_modules/core-util-is": { 218 | "version": "1.0.2", 219 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 220 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", 221 | "dev": true 222 | }, 223 | "node_modules/cosmiconfig": { 224 | "version": "9.0.0", 225 | "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", 226 | "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", 227 | "dependencies": { 228 | "env-paths": "^2.2.1", 229 | "import-fresh": "^3.3.0", 230 | "js-yaml": "^4.1.0", 231 | "parse-json": "^5.2.0" 232 | }, 233 | "engines": { 234 | "node": ">=14" 235 | }, 236 | "funding": { 237 | "url": "https://github.com/sponsors/d-fischer" 238 | }, 239 | "peerDependencies": { 240 | "typescript": ">=4.9.5" 241 | }, 242 | "peerDependenciesMeta": { 243 | "typescript": { 244 | "optional": true 245 | } 246 | } 247 | }, 248 | "node_modules/emoji-regex": { 249 | "version": "8.0.0", 250 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 251 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 252 | "dev": true 253 | }, 254 | "node_modules/env-paths": { 255 | "version": "2.2.1", 256 | "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", 257 | "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", 258 | "engines": { 259 | "node": ">=6" 260 | } 261 | }, 262 | "node_modules/error-ex": { 263 | "version": "1.3.2", 264 | "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", 265 | "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", 266 | "dependencies": { 267 | "is-arrayish": "^0.2.1" 268 | } 269 | }, 270 | "node_modules/escalade": { 271 | "version": "3.1.1", 272 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", 273 | "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", 274 | "dev": true, 275 | "engines": { 276 | "node": ">=6" 277 | } 278 | }, 279 | "node_modules/escape-string-regexp": { 280 | "version": "1.0.5", 281 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 282 | "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", 283 | "engines": { 284 | "node": ">=0.8.0" 285 | } 286 | }, 287 | "node_modules/fs.realpath": { 288 | "version": "1.0.0", 289 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 290 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 291 | "dev": true 292 | }, 293 | "node_modules/get-caller-file": { 294 | "version": "2.0.5", 295 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 296 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 297 | "dev": true, 298 | "engines": { 299 | "node": "6.* || 8.* || >= 10.*" 300 | } 301 | }, 302 | "node_modules/glob": { 303 | "version": "7.1.7", 304 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", 305 | "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", 306 | "deprecated": "Glob versions prior to v9 are no longer supported", 307 | "dev": true, 308 | "dependencies": { 309 | "fs.realpath": "^1.0.0", 310 | "inflight": "^1.0.4", 311 | "inherits": "2", 312 | "minimatch": "^3.0.4", 313 | "once": "^1.3.0", 314 | "path-is-absolute": "^1.0.0" 315 | }, 316 | "engines": { 317 | "node": "*" 318 | }, 319 | "funding": { 320 | "url": "https://github.com/sponsors/isaacs" 321 | } 322 | }, 323 | "node_modules/has-flag": { 324 | "version": "3.0.0", 325 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 326 | "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", 327 | "engines": { 328 | "node": ">=4" 329 | } 330 | }, 331 | "node_modules/import-fresh": { 332 | "version": "3.3.0", 333 | "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", 334 | "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", 335 | "dependencies": { 336 | "parent-module": "^1.0.0", 337 | "resolve-from": "^4.0.0" 338 | }, 339 | "engines": { 340 | "node": ">=6" 341 | }, 342 | "funding": { 343 | "url": "https://github.com/sponsors/sindresorhus" 344 | } 345 | }, 346 | "node_modules/inflight": { 347 | "version": "1.0.6", 348 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 349 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 350 | "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", 351 | "dev": true, 352 | "dependencies": { 353 | "once": "^1.3.0", 354 | "wrappy": "1" 355 | } 356 | }, 357 | "node_modules/inherits": { 358 | "version": "2.0.4", 359 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 360 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 361 | "dev": true 362 | }, 363 | "node_modules/is-arrayish": { 364 | "version": "0.2.1", 365 | "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", 366 | "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" 367 | }, 368 | "node_modules/is-fullwidth-code-point": { 369 | "version": "3.0.0", 370 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 371 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 372 | "dev": true, 373 | "engines": { 374 | "node": ">=8" 375 | } 376 | }, 377 | "node_modules/isarray": { 378 | "version": "0.0.1", 379 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", 380 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", 381 | "dev": true 382 | }, 383 | "node_modules/js-tokens": { 384 | "version": "4.0.0", 385 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 386 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" 387 | }, 388 | "node_modules/js-yaml": { 389 | "version": "4.1.0", 390 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", 391 | "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", 392 | "dependencies": { 393 | "argparse": "^2.0.1" 394 | }, 395 | "bin": { 396 | "js-yaml": "bin/js-yaml.js" 397 | } 398 | }, 399 | "node_modules/json-parse-even-better-errors": { 400 | "version": "2.3.1", 401 | "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", 402 | "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" 403 | }, 404 | "node_modules/lines-and-columns": { 405 | "version": "1.2.4", 406 | "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", 407 | "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" 408 | }, 409 | "node_modules/minimatch": { 410 | "version": "3.0.4", 411 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 412 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 413 | "dev": true, 414 | "dependencies": { 415 | "brace-expansion": "^1.1.7" 416 | }, 417 | "engines": { 418 | "node": "*" 419 | } 420 | }, 421 | "node_modules/mkdirp": { 422 | "version": "1.0.4", 423 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", 424 | "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", 425 | "dev": true, 426 | "bin": { 427 | "mkdirp": "bin/cmd.js" 428 | }, 429 | "engines": { 430 | "node": ">=10" 431 | } 432 | }, 433 | "node_modules/noms": { 434 | "version": "0.0.0", 435 | "resolved": "https://registry.npmjs.org/noms/-/noms-0.0.0.tgz", 436 | "integrity": "sha1-2o69nzr51nYJGbJ9nNyAkqczKFk=", 437 | "dev": true, 438 | "dependencies": { 439 | "inherits": "^2.0.1", 440 | "readable-stream": "~1.0.31" 441 | } 442 | }, 443 | "node_modules/once": { 444 | "version": "1.4.0", 445 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 446 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 447 | "dev": true, 448 | "dependencies": { 449 | "wrappy": "1" 450 | } 451 | }, 452 | "node_modules/parent-module": { 453 | "version": "1.0.1", 454 | "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", 455 | "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", 456 | "dependencies": { 457 | "callsites": "^3.0.0" 458 | }, 459 | "engines": { 460 | "node": ">=6" 461 | } 462 | }, 463 | "node_modules/parse-json": { 464 | "version": "5.2.0", 465 | "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", 466 | "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", 467 | "dependencies": { 468 | "@babel/code-frame": "^7.0.0", 469 | "error-ex": "^1.3.1", 470 | "json-parse-even-better-errors": "^2.3.0", 471 | "lines-and-columns": "^1.1.6" 472 | }, 473 | "engines": { 474 | "node": ">=8" 475 | }, 476 | "funding": { 477 | "url": "https://github.com/sponsors/sindresorhus" 478 | } 479 | }, 480 | "node_modules/path-is-absolute": { 481 | "version": "1.0.1", 482 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 483 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 484 | "dev": true, 485 | "engines": { 486 | "node": ">=0.10.0" 487 | } 488 | }, 489 | "node_modules/picocolors": { 490 | "version": "1.0.1", 491 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", 492 | "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==" 493 | }, 494 | "node_modules/prettier": { 495 | "version": "2.3.2", 496 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.2.tgz", 497 | "integrity": "sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==", 498 | "bin": { 499 | "prettier": "bin-prettier.js" 500 | }, 501 | "engines": { 502 | "node": ">=10.13.0" 503 | } 504 | }, 505 | "node_modules/process-nextick-args": { 506 | "version": "2.0.1", 507 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", 508 | "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", 509 | "dev": true 510 | }, 511 | "node_modules/readable-stream": { 512 | "version": "1.0.34", 513 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", 514 | "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", 515 | "dev": true, 516 | "dependencies": { 517 | "core-util-is": "~1.0.0", 518 | "inherits": "~2.0.1", 519 | "isarray": "0.0.1", 520 | "string_decoder": "~0.10.x" 521 | } 522 | }, 523 | "node_modules/require-directory": { 524 | "version": "2.1.1", 525 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 526 | "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", 527 | "dev": true, 528 | "engines": { 529 | "node": ">=0.10.0" 530 | } 531 | }, 532 | "node_modules/resolve-from": { 533 | "version": "4.0.0", 534 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", 535 | "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", 536 | "engines": { 537 | "node": ">=4" 538 | } 539 | }, 540 | "node_modules/rimraf": { 541 | "version": "3.0.2", 542 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", 543 | "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", 544 | "deprecated": "Rimraf versions prior to v4 are no longer supported", 545 | "dev": true, 546 | "dependencies": { 547 | "glob": "^7.1.3" 548 | }, 549 | "bin": { 550 | "rimraf": "bin.js" 551 | }, 552 | "funding": { 553 | "url": "https://github.com/sponsors/isaacs" 554 | } 555 | }, 556 | "node_modules/safe-buffer": { 557 | "version": "5.1.2", 558 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 559 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", 560 | "dev": true 561 | }, 562 | "node_modules/string_decoder": { 563 | "version": "0.10.31", 564 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", 565 | "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", 566 | "dev": true 567 | }, 568 | "node_modules/string-width": { 569 | "version": "4.2.2", 570 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", 571 | "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", 572 | "dev": true, 573 | "dependencies": { 574 | "emoji-regex": "^8.0.0", 575 | "is-fullwidth-code-point": "^3.0.0", 576 | "strip-ansi": "^6.0.0" 577 | }, 578 | "engines": { 579 | "node": ">=8" 580 | } 581 | }, 582 | "node_modules/strip-ansi": { 583 | "version": "6.0.0", 584 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", 585 | "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", 586 | "dev": true, 587 | "dependencies": { 588 | "ansi-regex": "^5.0.0" 589 | }, 590 | "engines": { 591 | "node": ">=8" 592 | } 593 | }, 594 | "node_modules/supports-color": { 595 | "version": "5.5.0", 596 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", 597 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", 598 | "dependencies": { 599 | "has-flag": "^3.0.0" 600 | }, 601 | "engines": { 602 | "node": ">=4" 603 | } 604 | }, 605 | "node_modules/through2": { 606 | "version": "2.0.5", 607 | "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", 608 | "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", 609 | "dev": true, 610 | "dependencies": { 611 | "readable-stream": "~2.3.6", 612 | "xtend": "~4.0.1" 613 | } 614 | }, 615 | "node_modules/through2/node_modules/isarray": { 616 | "version": "1.0.0", 617 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 618 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", 619 | "dev": true 620 | }, 621 | "node_modules/through2/node_modules/readable-stream": { 622 | "version": "2.3.7", 623 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", 624 | "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", 625 | "dev": true, 626 | "dependencies": { 627 | "core-util-is": "~1.0.0", 628 | "inherits": "~2.0.3", 629 | "isarray": "~1.0.0", 630 | "process-nextick-args": "~2.0.0", 631 | "safe-buffer": "~5.1.1", 632 | "string_decoder": "~1.1.1", 633 | "util-deprecate": "~1.0.1" 634 | } 635 | }, 636 | "node_modules/through2/node_modules/string_decoder": { 637 | "version": "1.1.1", 638 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", 639 | "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", 640 | "dev": true, 641 | "dependencies": { 642 | "safe-buffer": "~5.1.0" 643 | } 644 | }, 645 | "node_modules/typescript": { 646 | "version": "4.9.5", 647 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", 648 | "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", 649 | "devOptional": true, 650 | "bin": { 651 | "tsc": "bin/tsc", 652 | "tsserver": "bin/tsserver" 653 | }, 654 | "engines": { 655 | "node": ">=4.2.0" 656 | } 657 | }, 658 | "node_modules/untildify": { 659 | "version": "4.0.0", 660 | "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", 661 | "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", 662 | "dev": true, 663 | "engines": { 664 | "node": ">=8" 665 | } 666 | }, 667 | "node_modules/util-deprecate": { 668 | "version": "1.0.2", 669 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 670 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", 671 | "dev": true 672 | }, 673 | "node_modules/wrap-ansi": { 674 | "version": "7.0.0", 675 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", 676 | "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", 677 | "dev": true, 678 | "dependencies": { 679 | "ansi-styles": "^4.0.0", 680 | "string-width": "^4.1.0", 681 | "strip-ansi": "^6.0.0" 682 | }, 683 | "engines": { 684 | "node": ">=10" 685 | }, 686 | "funding": { 687 | "url": "https://github.com/chalk/wrap-ansi?sponsor=1" 688 | } 689 | }, 690 | "node_modules/wrappy": { 691 | "version": "1.0.2", 692 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 693 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 694 | "dev": true 695 | }, 696 | "node_modules/xtend": { 697 | "version": "4.0.2", 698 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", 699 | "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", 700 | "dev": true, 701 | "engines": { 702 | "node": ">=0.4" 703 | } 704 | }, 705 | "node_modules/y18n": { 706 | "version": "5.0.8", 707 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", 708 | "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", 709 | "dev": true, 710 | "engines": { 711 | "node": ">=10" 712 | } 713 | }, 714 | "node_modules/yargs": { 715 | "version": "16.2.0", 716 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", 717 | "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", 718 | "dev": true, 719 | "dependencies": { 720 | "cliui": "^7.0.2", 721 | "escalade": "^3.1.1", 722 | "get-caller-file": "^2.0.5", 723 | "require-directory": "^2.1.1", 724 | "string-width": "^4.2.0", 725 | "y18n": "^5.0.5", 726 | "yargs-parser": "^20.2.2" 727 | }, 728 | "engines": { 729 | "node": ">=10" 730 | } 731 | }, 732 | "node_modules/yargs-parser": { 733 | "version": "20.2.9", 734 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", 735 | "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", 736 | "dev": true, 737 | "engines": { 738 | "node": ">=10" 739 | } 740 | }, 741 | "node_modules/zod": { 742 | "version": "3.7.1", 743 | "resolved": "https://registry.npmjs.org/zod/-/zod-3.7.1.tgz", 744 | "integrity": "sha512-9TRjeMKz3xFUOl/CYhBmp/hCrMhk5U6qRnfy9q+HwmQPbMYp3qnM+/cPst+wzHQjfSBjHDYZq7S7TQnlAjqjFg==", 745 | "funding": { 746 | "url": "https://github.com/sponsors/colinhacks" 747 | } 748 | } 749 | } 750 | } 751 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "json-to-zod", 3 | "version": "1.1.4", 4 | "description": "Converts JSON objects or files into simple Zod schemas.", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "rimraf ./dist && tsc && copyfiles package.json ./dist && copyfiles LICENSE ./dist && copyfiles readme.md ./dist" 8 | }, 9 | "keywords": [ 10 | "zod", 11 | "json", 12 | "converter", 13 | "cli" 14 | ], 15 | "author": "Stefan Terdell", 16 | "contributors": [ 17 | "Renato Sinohara (https://github.com/rsinohara)" 18 | ], 19 | "license": "ISC", 20 | "repository": { 21 | "type": "git", 22 | "url": "https://github.com/StefanTerdell/json-to-zod" 23 | }, 24 | "bin": { 25 | "json-to-zod": "./src/cli.js" 26 | }, 27 | "dependencies": { 28 | "cosmiconfig": "^9.0.0", 29 | "prettier": "^2.3.2", 30 | "zod": "^3.7.1" 31 | }, 32 | "devDependencies": { 33 | "@types/node": "^16.6.0", 34 | "@types/prettier": "^2.3.2", 35 | "copyfiles": "^2.4.1", 36 | "rimraf": "^3.0.2", 37 | "typescript": "^4.3.5" 38 | }, 39 | "types": "index.d.ts" 40 | } 41 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Json-to-Zod 2 | 3 | ## Summary 4 | A very simple CLI tool to convert JSON objects or files into zod schemas. 5 | ## Usage 6 | ### CLI 7 | ```json-to-zod -s myJson.json -t mySchema.ts``` 8 | 9 | Options: 10 | - --source/-s [source file name] 11 | - --target/-t [(optional) target file name] 12 | - --name/-n [(optional) schema name in output] 13 | - --convertTuples/-c [(optional) handle tuples correctly] 14 | 15 | ### Programmatic 16 | ```typescript 17 | import { jsonToZod } from "json-to-zod" 18 | 19 | const myObject = { 20 | hello: "hi" 21 | } 22 | 23 | const result = jsonToZod(myObject) 24 | 25 | console.log(result) 26 | ``` 27 | ### Expected output: 28 | ``` 29 | const schema = z.object({hello: z.string()}); 30 | ``` 31 | 32 | ### Overriding zod values 33 | 34 | Since zod can be more specific about the primitives sometime you want to be 35 | more precise. 36 | 37 | Eg. if an string has been parsed to: 38 | 39 | ```typescript 40 | z.string(); 41 | ``` 42 | 43 | But you know it is a date and therefor should be called: 44 | 45 | ```typescript 46 | z.string().date(); 47 | ``` 48 | 49 | Then you can create a configuration file called: `.jtzrc.yml` and define 50 | schema overrides there. 51 | 52 | Take a look at the `.jtzrc.yml.example` file. 53 | 54 | ### Handling Tuples 55 | You can use the `convertTuples` option to handle tuples correctly. 56 | 57 | ```typescript 58 | import { jsonToZod } from "json-to-zod" 59 | 60 | const myTuple = [1, 'some string'] 61 | 62 | const result = jsonToZod(myTuple, "schema", false, true) 63 | 64 | console.log(result) 65 | ``` 66 | ### Expected output: 67 | ``` 68 | const schema = z.tuple([z.number(), z.string()]); 69 | ``` 70 | -------------------------------------------------------------------------------- /src/cli.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import { jsonToZod } from "./jsonToZod"; 3 | import { readFileSync, writeFileSync, existsSync } from "fs"; 4 | import { getConfig } from "./getConfig"; 5 | let sourceArgumentIndex = process.argv.indexOf("--source"); 6 | if (sourceArgumentIndex === -1) { 7 | sourceArgumentIndex = process.argv.indexOf("-s"); 8 | } 9 | if (sourceArgumentIndex === -1) { 10 | console.error( 11 | "Must supply source file with --source [filename] or -s [filename}" 12 | ); 13 | process.exit(1); 14 | } 15 | const sourceFilePath = process.argv[sourceArgumentIndex + 1]; 16 | if (!sourceFilePath) { 17 | console.error( 18 | `No source path was provided after ${process.argv[sourceArgumentIndex]}` 19 | ); 20 | process.exit(1); 21 | } 22 | const sourceFileExists = existsSync(sourceFilePath); 23 | if (!sourceFileExists) { 24 | console.error(`${sourceFilePath} doesn't exist`); 25 | process.exit(1); 26 | } 27 | let sourceFileContent: string; 28 | try { 29 | sourceFileContent = readFileSync(sourceFilePath, "utf-8"); 30 | } catch (e) { 31 | console.error("Failed to read sourcefile"); 32 | console.error(e); 33 | process.exit(1); 34 | } 35 | let sourceFileData: any; 36 | try { 37 | sourceFileData = JSON.parse(sourceFileContent); 38 | } catch (e) { 39 | console.error("Failed to parse sourcefile contents"); 40 | console.error(e); 41 | process.exit(1); 42 | } 43 | let targetArgumentIndex = process.argv.indexOf("--target"); 44 | if (targetArgumentIndex === -1) { 45 | targetArgumentIndex = process.argv.indexOf("-t"); 46 | } 47 | let targetFilePath: string = ""; 48 | if (targetArgumentIndex !== -1) { 49 | targetFilePath = process.argv[targetArgumentIndex + 1]; 50 | if (!targetFilePath) { 51 | console.error( 52 | `No target path was provided after ${process.argv[targetArgumentIndex]}` 53 | ); 54 | process.exit(1); 55 | } 56 | } 57 | let nameArgumentIndex = process.argv.indexOf("--name"); 58 | if (nameArgumentIndex === -1) { 59 | nameArgumentIndex = process.argv.indexOf("-n"); 60 | } 61 | let name: string = "schema"; 62 | if (nameArgumentIndex !== -1) { 63 | name = process.argv[nameArgumentIndex + 1]; 64 | if (!name) { 65 | console.error( 66 | `No schema name was provided after ${process.argv[nameArgumentIndex]}` 67 | ); 68 | process.exit(1); 69 | } 70 | } 71 | let convertTuplesArgumentIndex = process.argv.indexOf("--convertTuples"); 72 | if (convertTuplesArgumentIndex === -1) { 73 | convertTuplesArgumentIndex = process.argv.indexOf("-c"); 74 | } 75 | let convertTuples: boolean = false; 76 | if (convertTuplesArgumentIndex !== -1) { 77 | convertTuples = true; 78 | } 79 | if (targetFilePath) { 80 | let result: string; 81 | try { 82 | const config = getConfig(); 83 | result = jsonToZod(sourceFileData, name, true, convertTuples, config?.zodValueOverrides); 84 | 85 | } catch (e) { 86 | console.error("Failed to parse sourcefile content to Zod schema"); 87 | console.error(e); 88 | process.exit(1); 89 | } 90 | 91 | try { 92 | writeFileSync(targetFilePath, result); 93 | } catch (e) { 94 | console.error(`Failed to result to ${targetFilePath}`); 95 | console.error(e); 96 | process.exit(1); 97 | } 98 | } else { 99 | let result: string; 100 | try { 101 | const config = getConfig(); 102 | result = jsonToZod(sourceFileData, name, false, convertTuples, config?.zodValueOverrides); 103 | } catch (e) { 104 | console.error("Failed to parse sourcefile content to Zod schema"); 105 | console.error(e); 106 | process.exit(1); 107 | } 108 | console.log(result); 109 | } 110 | -------------------------------------------------------------------------------- /src/getConfig.ts: -------------------------------------------------------------------------------- 1 | import { z } from "zod"; 2 | import { cosmiconfigSync } from "cosmiconfig"; 3 | 4 | const ConfigSchema = z.object({ 5 | zodValueOverrides: z 6 | .object({ 7 | schema: z.record(z.record(z.string())), 8 | }) 9 | .optional(), 10 | }); 11 | 12 | export type TConfig = z.infer; 13 | 14 | export const getConfig = (): z.infer | null => { 15 | const explorerSync = cosmiconfigSync("jtz"); 16 | const result = explorerSync.search(); 17 | 18 | if (!result) { 19 | return null; 20 | } 21 | 22 | const { config } = result; 23 | const configValidationResult = ConfigSchema.safeParse(config); 24 | if (!configValidationResult.success) { 25 | console.error(configValidationResult.error); 26 | throw new Error( 27 | "Invalid Json To Zod config. Please double check your config file." 28 | ); 29 | } 30 | 31 | return config; 32 | }; 33 | -------------------------------------------------------------------------------- /src/jsonToZod.ts: -------------------------------------------------------------------------------- 1 | import { format } from "prettier"; 2 | import babelParser from "prettier/parser-babel"; 3 | import { TConfig } from "./getConfig"; 4 | 5 | export const jsonToZod = ( 6 | obj: any, 7 | name: string = "schema", 8 | module?: boolean, 9 | convertTuples: boolean = false, 10 | zodValueOverrides?: TConfig["zodValueOverrides"], 11 | ): string => { 12 | const parse = (obj: any, seen: object[]): string => { 13 | switch (typeof obj) { 14 | case "string": 15 | return "z.string()"; 16 | case "number": 17 | return "z.number()"; 18 | case "bigint": 19 | return "z.number().int()"; 20 | case "boolean": 21 | return "z.boolean()"; 22 | case "object": 23 | if (obj === null) { 24 | return "z.null()"; 25 | } 26 | if (seen.find((_obj) => Object.is(_obj, obj))) { 27 | throw "Circular objects are not supported"; 28 | } 29 | seen.push(obj); 30 | if (Array.isArray(obj)) { 31 | if (convertTuples) { 32 | return `z.tuple([${obj.map((item) => parse(item, seen)).join(", ")}])`; 33 | } else { 34 | const options = obj 35 | .map((obj) => parse(obj, seen)) 36 | .reduce( 37 | (acc: string[], curr: string) => 38 | acc.includes(curr) ? acc : [...acc, curr], 39 | [] 40 | ); 41 | if (options.length === 1) { 42 | return `z.array(${options[0]})`; 43 | } else if (options.length > 1) { 44 | return `z.array(z.union([${options}]))`; 45 | } else { 46 | return `z.array(z.unknown())`; 47 | } 48 | } 49 | } 50 | return `z.object({${Object.entries(obj).map(([k, v]) => { 51 | const overrideKey = k.toLowerCase(); 52 | const value = zodValueOverrides?.schema?.[name]?.[overrideKey]; 53 | return value ? `'${k}':${value}` : `'${k}':${parse(v, seen)}`; 54 | })}})`; 55 | case "undefined": 56 | return "z.undefined()"; 57 | case "function": 58 | return "z.function()"; 59 | case "symbol": 60 | default: 61 | return "z.unknown()"; 62 | } 63 | }; 64 | 65 | return module 66 | ? format( 67 | `import {z} from "zod"\n\nexport const ${name}=${parse(obj, [])}`, 68 | { 69 | parser: "babel", 70 | plugins: [babelParser], 71 | } 72 | ) 73 | : format(`const ${name}=${parse(obj, [])}`, { 74 | parser: "babel", 75 | plugins: [babelParser], 76 | }); 77 | }; 78 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "dist", 4 | "target": "ES2017", 5 | "declaration": true, 6 | "module": "commonjs", 7 | "strict": true, 8 | "skipLibCheck": true, 9 | "esModuleInterop": true 10 | }, 11 | "exclude": ["node_modules", "dist", "lab.ts", "lab.json"] 12 | } 13 | --------------------------------------------------------------------------------