@alevnyacow/nzmt 0.20.3 → 0.21.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.
Files changed (3) hide show
  1. package/README.md +4 -4
  2. package/bin/cli.js +31 -18
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -9,16 +9,16 @@
9
9
  <img src='https://img.shields.io/bundlephobia/minzip/%40alevnyacow/nzmt'></img>
10
10
  </p>
11
11
 
12
- Scaffold full-stack modules in Next.js in seconds with **Next Zod Modules Toolkit (NZMT)**.
12
+ Scaffold full-stack modules in Next.js in seconds with **Next Zod Modules Toolkit (NZMT)**.
13
13
 
14
- Get **a DDD-inspired architecture with a contract-first approach** — and Server Actions working out of the box. NZMT also comes with useful infrastructure like DI, logging, unified errors, and endpoint guards for your scaffolded modules. Batteries included!
14
+ Get a DDD-inspired architecture with a contract-first approach out of the box. NZMT also comes with useful infrastructure like DI, logging, unified errors, and endpoint guards.
15
15
 
16
- **API, services, stores, entities, validation, and React Query hooks — all generated for you.** No framework. No lock-in. Just production-ready Next.js.
16
+ Batteries included!
17
17
 
18
18
 
19
19
  # TL;DR
20
20
 
21
- Initialize NZMT once, run the scaffolder, and tweak a few files to get a production-ready DDD-inspired backend usable via Server Actions with ready-to-use React Query hooks.
21
+ Initialize NZMT once, run the scaffolder, and tweak a few files to get a production-ready backend usable via Server Actions with ready-to-use React Query hooks.
22
22
 
23
23
  # Quick start with Prisma
24
24
 
package/bin/cli.js CHANGED
@@ -1109,10 +1109,11 @@ function generateController(upperCase, lowerCase, crudService) {
1109
1109
  )
1110
1110
  }
1111
1111
 
1112
- function generateAPIRoutes(lowerCase, upperCase) {
1112
+ function generateAPIRoutes(lowerCase, upperCase, entity) {
1113
+ const requiredEntity = entity || entityName
1113
1114
  const projectRoot = findProjectRoot()
1114
1115
  const fileText = fs.readFileSync(
1115
- path.resolve(projectRoot, `${config.coreFolder}${config.paths.controllers}`, entityName, `${entityName}.controller.ts`),
1116
+ path.resolve(projectRoot, `${config.coreFolder}${config.paths.controllers}`, requiredEntity, `${requiredEntity}.controller.ts`),
1116
1117
  'utf-8'
1117
1118
  )
1118
1119
 
@@ -1130,13 +1131,13 @@ function generateAPIRoutes(lowerCase, upperCase) {
1130
1131
  return acc
1131
1132
  }, {})
1132
1133
 
1133
- const controllerHandlersRootPath = path.resolve(projectRoot, config.coreFolder, 'app', 'api', `${entityName}-controller`)
1134
+ const controllerHandlersRootPath = path.resolve(projectRoot, config.coreFolder, 'app', 'api', `${requiredEntity}-controller`)
1134
1135
 
1135
1136
  fs.mkdirSync(controllerHandlersRootPath, { recursive: true })
1136
1137
 
1137
1138
  if (rootMethods.length) {
1138
1139
  fs.writeFileSync(path.resolve(controllerHandlersRootPath, 'route.ts'), [
1139
- `import type { ${upperCase}Controller } from '@${config.paths.controllers}/${entityName}'`,
1140
+ `import type { ${upperCase}Controller } from '@${config.paths.controllers}/${requiredEntity}'`,
1140
1141
  `import { fromDI } from '@${config.paths.di}'`,
1141
1142
  '',
1142
1143
  `const controller = fromDI<${upperCase}Controller>('${upperCase}Controller')`,
@@ -1149,7 +1150,7 @@ function generateAPIRoutes(lowerCase, upperCase) {
1149
1150
  const nestedFolder = path.resolve(controllerHandlersRootPath, currentPath)
1150
1151
  fs.mkdirSync(nestedFolder, { recursive: true })
1151
1152
  fs.writeFileSync(path.resolve(nestedFolder, 'route.ts'), [
1152
- `import type { ${upperCase}Controller } from '@${config.paths.controllers}/${entityName}'`,
1153
+ `import type { ${upperCase}Controller } from '@${config.paths.controllers}/${requiredEntity}'`,
1153
1154
  `import { fromDI } from '@${config.paths.di}'`,
1154
1155
  '',
1155
1156
  `const controller = fromDI<${upperCase}Controller>('${upperCase}Controller')`,
@@ -1159,11 +1160,12 @@ function generateAPIRoutes(lowerCase, upperCase) {
1159
1160
  }
1160
1161
  }
1161
1162
 
1162
- function generateQueries(lowerCase, upperCase) {
1163
+ function generateQueries(lowerCase, upperCase, entity) {
1164
+ const requiredEntity = entity || entityName
1163
1165
  const projectRoot = findProjectRoot()
1164
1166
 
1165
1167
  const fileText = fs.readFileSync(
1166
- path.resolve(projectRoot, `${config.coreFolder}${config.paths.controllers}`, entityName, `${entityName}.controller.ts`),
1168
+ path.resolve(projectRoot, `${config.coreFolder}${config.paths.controllers}`, requiredEntity, `${requiredEntity}.controller.ts`),
1167
1169
  'utf-8'
1168
1170
  )
1169
1171
 
@@ -1181,7 +1183,7 @@ function generateQueries(lowerCase, upperCase) {
1181
1183
  return acc
1182
1184
  }, {})
1183
1185
 
1184
- const controllerQueriesPath = path.resolve(projectRoot, `${config.coreFolder}${config.paths.queries}`, `${entityName}`, 'endpoints')
1186
+ const controllerQueriesPath = path.resolve(projectRoot, `${config.coreFolder}${config.paths.queries}`, `${requiredEntity}`, 'endpoints')
1185
1187
  let scaffoldedMethods = []
1186
1188
 
1187
1189
  fs.mkdirSync(controllerQueriesPath, { recursive: true })
@@ -1198,18 +1200,18 @@ function generateQueries(lowerCase, upperCase) {
1198
1200
  }
1199
1201
  fs.writeFileSync(fileName, [
1200
1202
  `import { ${rootMethod === 'GET' ? 'useQuery' : 'useMutation, useQueryClient'} } from '@tanstack/react-query'`,
1201
- `import type { ${upperCase}API } from '@${config.paths.controllers}/${entityName}'`,
1203
+ `import type { ${upperCase}API } from '@${config.paths.controllers}/${requiredEntity}'`,
1202
1204
  `import { apiRequest } from '@${config.paths.clientUtils}'`,
1203
1205
  '',
1204
1206
  `type Method = ${upperCase}API['endpoints']['${rootMethod}']`,
1205
1207
  ``,
1206
- `const endpoint = '/api/${entityName}-controller'`,
1208
+ `const endpoint = '/api/${requiredEntity}-controller'`,
1207
1209
  ``,
1208
1210
  rootMethod === 'GET'
1209
1211
  ? [
1210
1212
  `export const use${rootMethod} = (payload: Method['payload']) => {`,
1211
1213
  `\treturn useQuery<Method['response'], Method['error']>({`,
1212
- `\t\tqueryKey: ['${entityName}', '${rootMethod}', payload],`,
1214
+ `\t\tqueryKey: ['${requiredEntity}', '${rootMethod}', payload],`,
1213
1215
  `\t\tqueryFn: () => apiRequest(endpoint, 'GET')(payload)`,
1214
1216
  `\t})`,
1215
1217
  `}`
@@ -1219,7 +1221,7 @@ function generateQueries(lowerCase, upperCase) {
1219
1221
  `\tconst queryClient = useQueryClient()`,
1220
1222
  `\treturn useMutation<Method['response'], Method['error'], Method['payload']>({`,
1221
1223
  `\t\tmutationFn: apiRequest(endpoint, '${rootMethod}'),`,
1222
- `\t\tonSuccess: () => { queryClient.invalidateQueries({ queryKey: ['${entityName}'], exact: false }) }`,
1224
+ `\t\tonSuccess: () => { queryClient.invalidateQueries({ queryKey: ['${requiredEntity}'], exact: false }) }`,
1223
1225
  `\t})`,
1224
1226
  `}`
1225
1227
  ].join('\n')
@@ -1240,18 +1242,18 @@ function generateQueries(lowerCase, upperCase) {
1240
1242
 
1241
1243
  fs.writeFileSync(fileName, [
1242
1244
  `import { ${method === 'GET' ? 'useQuery' : 'useMutation, useQueryClient' } } from '@tanstack/react-query'`,
1243
- `import type { ${upperCase}API } from '@${config.paths.controllers}/${entityName}'`,
1245
+ `import type { ${upperCase}API } from '@${config.paths.controllers}/${requiredEntity}'`,
1244
1246
  `import { apiRequest } from '@${config.paths.clientUtils}'`,
1245
1247
  '',
1246
1248
  `type Method = ${upperCase}API['endpoints']['${fullMethodName}']`,
1247
1249
  ``,
1248
- `const endpoint = '/api/${entityName}-controller/${currentPath}'`,
1250
+ `const endpoint = '/api/${requiredEntity}-controller/${currentPath}'`,
1249
1251
  ``,
1250
1252
  method === 'GET'
1251
1253
  ? [
1252
1254
  `export const use${nameForHook} = (payload: Method['payload']) => {`,
1253
1255
  `\treturn useQuery<Method['response'], Method['error']>({`,
1254
- `\t\tqueryKey: ['${entityName}', ${currentPath.split('/').map(x => `'${x}'`).join(', ')}, payload],`,
1256
+ `\t\tqueryKey: ['${requiredEntity}', ${currentPath.split('/').map(x => `'${x}'`).join(', ')}, payload],`,
1255
1257
  `\t\tqueryFn: () => apiRequest(endpoint, 'GET')(payload)`,
1256
1258
  `\t})`,
1257
1259
  `}`
@@ -1261,7 +1263,7 @@ function generateQueries(lowerCase, upperCase) {
1261
1263
  `\tconst queryClient = useQueryClient()`,
1262
1264
  `\treturn useMutation<Method['response'], Method['error'], Method['payload']>({`,
1263
1265
  `\t\tmutationFn: apiRequest(endpoint, '${method}'),`,
1264
- `\t\tonSuccess: () => { queryClient.invalidateQueries({ queryKey: ['${entityName}'], exact: false }) }`,
1266
+ `\t\tonSuccess: () => { queryClient.invalidateQueries({ queryKey: ['${requiredEntity}'], exact: false }) }`,
1265
1267
  `\t})`,
1266
1268
  `}`
1267
1269
  ].join('\n')
@@ -1279,7 +1281,7 @@ function generateQueries(lowerCase, upperCase) {
1279
1281
 
1280
1282
  fs.writeFileSync(path.resolve(controllerQueriesPath, 'index.ts'), scaffoldedMethods.map(x => `export * from './${x}'`).join('\n'))
1281
1283
 
1282
- const indexPath = path.resolve(projectRoot, `${config.coreFolder}${config.paths.queries}`, `${entityName}`)
1284
+ const indexPath = path.resolve(projectRoot, `${config.coreFolder}${config.paths.queries}`, `${requiredEntity}`)
1283
1285
  fs.writeFileSync(path.resolve(indexPath, 'index.ts'), `export * as ${upperCase}Queries from './endpoints'`)
1284
1286
 
1285
1287
  }
@@ -1295,7 +1297,17 @@ if (command === 'queries') {
1295
1297
  var [lowerCase, upperCase] = camelizeVariants(entityName)
1296
1298
 
1297
1299
  generateQueries(lowerCase, upperCase)
1300
+ }
1298
1301
 
1302
+ if (command.toLowerCase() === 'routes-with-queries' || command === 'rq') {
1303
+ const projectRoot = findProjectRoot()
1304
+ const controllersFolder = path.resolve(projectRoot, `${config.coreFolder}${config.paths.controllers}`)
1305
+ const controllerEntities = fs.readdirSync(controllersFolder, { withFileTypes: true }).filter(x => x.isDirectory()).map(x => x.name)
1306
+ for (const entity of controllerEntities) {
1307
+ var [lowerCase, upperCase] = camelizeVariants(entity)
1308
+ generateAPIRoutes(lowerCase, upperCase, entity)
1309
+ generateQueries(lowerCase, upperCase, entity)
1310
+ }
1299
1311
  }
1300
1312
 
1301
1313
  if (command.toLowerCase() === 'controller' || command === 'c') {
@@ -1328,4 +1340,5 @@ if (command.toLowerCase() === 'crud-api') {
1328
1340
  generateAPIRoutes(lowerCase, upperCase)
1329
1341
  generateQueries(lowerCase, upperCase)
1330
1342
  process.exit(0)
1331
- }
1343
+ }
1344
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alevnyacow/nzmt",
3
- "version": "0.20.3",
3
+ "version": "0.21.0",
4
4
  "description": "Next Zod Modules Toolkit",
5
5
  "keywords": ["next", "full-stack", "server", "backend", "cli", "scaffolder", "zod", "rest", "contract programming", "react-query", "ddd", "domain-driven"],
6
6
  "repository": {