@forinda/kickjs-cli 2.2.3 → 2.2.5
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/dist/cli.mjs +230 -82
- package/dist/index.d.mts +7 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +178 -59
- package/dist/index.mjs.map +1 -1
- package/dist/{typegen-BncsvEr-.mjs → typegen-CTXqSva4.mjs} +2 -2
- package/dist/{typegen-BncsvEr-.mjs.map → typegen-CTXqSva4.mjs.map} +1 -1
- package/package.json +5 -3
package/dist/cli.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @forinda/kickjs-cli v2.2.
|
|
2
|
+
* @forinda/kickjs-cli v2.2.5
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Felix Orinda
|
|
5
5
|
*
|
|
@@ -15,6 +15,7 @@ import { fileURLToPath, pathToFileURL } from "node:url";
|
|
|
15
15
|
import { createInterface } from "node:readline";
|
|
16
16
|
import { execSync, fork } from "node:child_process";
|
|
17
17
|
import { access, mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises";
|
|
18
|
+
import pkg from "pluralize";
|
|
18
19
|
import { arch, platform, release } from "node:os";
|
|
19
20
|
//#region \0rolldown/runtime.js
|
|
20
21
|
var __defProp = Object.defineProperty;
|
|
@@ -742,18 +743,30 @@ export class UserService {
|
|
|
742
743
|
|
|
743
744
|
### Modules
|
|
744
745
|
|
|
745
|
-
|
|
746
|
+
Modules implement \`AppModule\` and wire controllers via \`buildRoutes()\`:
|
|
746
747
|
|
|
747
748
|
\`\`\`ts
|
|
748
|
-
import {
|
|
749
|
+
import { type AppModule, type ModuleRoutes, buildRoutes } from '@forinda/kickjs'
|
|
749
750
|
import { UserController } from './user.controller'
|
|
750
|
-
import { UserService } from './user.service'
|
|
751
751
|
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
752
|
+
export class UserModule implements AppModule {
|
|
753
|
+
routes(): ModuleRoutes {
|
|
754
|
+
return {
|
|
755
|
+
path: '/users',
|
|
756
|
+
router: buildRoutes(UserController),
|
|
757
|
+
controller: UserController,
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
\`\`\`
|
|
762
|
+
|
|
763
|
+
Register all modules in \`src/modules/index.ts\`:
|
|
764
|
+
|
|
765
|
+
\`\`\`ts
|
|
766
|
+
import type { AppModuleClass } from '@forinda/kickjs'
|
|
767
|
+
import { UserModule } from './user/user.module'
|
|
768
|
+
|
|
769
|
+
export const modules: AppModuleClass[] = [UserModule]
|
|
757
770
|
\`\`\`
|
|
758
771
|
|
|
759
772
|
### RequestContext
|
|
@@ -864,6 +877,86 @@ Hot-reload of \`.env\` changes during dev is wired up automatically via
|
|
|
864
877
|
\`envWatchPlugin()\` in \`vite.config.ts\` — edit \`.env\`, the dev server
|
|
865
878
|
reloads, and the next \`config.get()\` re-parses with the new values.
|
|
866
879
|
|
|
880
|
+
### Standalone Env Utilities (No DI Required)
|
|
881
|
+
|
|
882
|
+
These functions work anywhere — scripts, CLI tools, plain files, outside \`@Service\`/\`@Controller\`:
|
|
883
|
+
|
|
884
|
+
\`\`\`ts
|
|
885
|
+
import { defineEnv, loadEnv, getEnv, reloadEnv, resetEnvCache, baseEnvSchema } from '@forinda/kickjs/config'
|
|
886
|
+
import { z } from 'zod'
|
|
887
|
+
|
|
888
|
+
// Define and parse schema
|
|
889
|
+
const schema = defineEnv((base) =>
|
|
890
|
+
base.extend({ DATABASE_URL: z.string().url() })
|
|
891
|
+
)
|
|
892
|
+
const env = loadEnv(schema) // Parse + validate process.env
|
|
893
|
+
console.log(env.PORT) // 3000 (coerced to number)
|
|
894
|
+
console.log(env.DATABASE_URL) // validated URL string
|
|
895
|
+
|
|
896
|
+
// Get single value
|
|
897
|
+
const port = getEnv('PORT') // typed after kick typegen
|
|
898
|
+
|
|
899
|
+
// Reload after .env changes (HMR calls this automatically)
|
|
900
|
+
reloadEnv()
|
|
901
|
+
|
|
902
|
+
// Reset cache in tests that swap schemas
|
|
903
|
+
resetEnvCache()
|
|
904
|
+
\`\`\`
|
|
905
|
+
|
|
906
|
+
| Function | Purpose |
|
|
907
|
+
|----------|---------|
|
|
908
|
+
| \`defineEnv(fn)\` | Extend base schema with custom Zod keys |
|
|
909
|
+
| \`loadEnv(schema?)\` | Parse \`process.env\`, validate, cache, return typed object |
|
|
910
|
+
| \`getEnv(key, schema?)\` | Get single validated env value |
|
|
911
|
+
| \`reloadEnv()\` | Re-read \`.env\` from disk, re-parse with same schema |
|
|
912
|
+
| \`resetEnvCache()\` | Clear parsed cache AND registered schema (for tests) |
|
|
913
|
+
| \`baseEnvSchema\` | Base Zod schema: \`PORT\`, \`NODE_ENV\`, \`LOG_LEVEL\` |
|
|
914
|
+
|
|
915
|
+
## Standalone Utilities (No DI Required)
|
|
916
|
+
|
|
917
|
+
These utilities work outside decorated classes:
|
|
918
|
+
|
|
919
|
+
### Logger
|
|
920
|
+
|
|
921
|
+
\`\`\`ts
|
|
922
|
+
import { Logger, createLogger } from '@forinda/kickjs'
|
|
923
|
+
|
|
924
|
+
const log = Logger.for('MyScript') // Static factory
|
|
925
|
+
log.info('Processing started')
|
|
926
|
+
log.error('Something failed')
|
|
927
|
+
|
|
928
|
+
const log2 = createLogger('Worker') // Function form
|
|
929
|
+
\`\`\`
|
|
930
|
+
|
|
931
|
+
### Injection Tokens
|
|
932
|
+
|
|
933
|
+
\`\`\`ts
|
|
934
|
+
import { createToken } from '@forinda/kickjs'
|
|
935
|
+
|
|
936
|
+
// Type-safe DI tokens for factory/interface binding
|
|
937
|
+
const DB_URL = createToken<string>('config.database.url')
|
|
938
|
+
const FEATURE_FLAGS = createToken<FeatureFlags>('app.features')
|
|
939
|
+
\`\`\`
|
|
940
|
+
|
|
941
|
+
### Reactivity
|
|
942
|
+
|
|
943
|
+
\`\`\`ts
|
|
944
|
+
import { ref, computed, watch, reactive } from '@forinda/kickjs'
|
|
945
|
+
|
|
946
|
+
const count = ref(0)
|
|
947
|
+
const doubled = computed(() => count.value * 2)
|
|
948
|
+
const stop = watch(() => count.value, (val) => console.log(val))
|
|
949
|
+
count.value++ // logs 1
|
|
950
|
+
\`\`\`
|
|
951
|
+
|
|
952
|
+
### HTTP Errors
|
|
953
|
+
|
|
954
|
+
\`\`\`ts
|
|
955
|
+
import { HttpException, HttpStatus } from '@forinda/kickjs'
|
|
956
|
+
|
|
957
|
+
throw new HttpException(HttpStatus.NOT_FOUND, 'User not found')
|
|
958
|
+
\`\`\`
|
|
959
|
+
|
|
867
960
|
## Testing
|
|
868
961
|
|
|
869
962
|
Tests live in \`src/**/*.test.ts\`:
|
|
@@ -898,7 +991,6 @@ Run tests:
|
|
|
898
991
|
- \`@Roles('admin', 'user')\` — role-based access control
|
|
899
992
|
|
|
900
993
|
### DI Decorators
|
|
901
|
-
- \`@Module({ controllers, providers, imports })\` — define module
|
|
902
994
|
- \`@Service()\` — singleton service (DI-registered)
|
|
903
995
|
- \`@Repository()\` — repository (semantic alias for @Service)
|
|
904
996
|
- \`@Autowired()\` — property injection
|
|
@@ -976,7 +1068,7 @@ ${template === "ddd" ? `\`\`\`
|
|
|
976
1068
|
├── <name>.repository.ts # Data access (@Repository)
|
|
977
1069
|
├── <name>.dto.ts # Request/response schemas (Zod)
|
|
978
1070
|
├── <name>.entity.ts # Domain entity (optional)
|
|
979
|
-
└── <name>.module.ts # Module definition (
|
|
1071
|
+
└── <name>.module.ts # Module definition (implements AppModule)
|
|
980
1072
|
\`\`\`
|
|
981
1073
|
` : template === "cqrs" ? `\`\`\`
|
|
982
1074
|
<name>/
|
|
@@ -990,7 +1082,7 @@ ${template === "ddd" ? `\`\`\`
|
|
|
990
1082
|
│ └── <name>-created.event.ts
|
|
991
1083
|
├── <name>.controller.ts # HTTP routes
|
|
992
1084
|
├── <name>.repository.ts # Data access
|
|
993
|
-
└── <name>.module.ts # Module definition
|
|
1085
|
+
└── <name>.module.ts # Module definition (implements AppModule)
|
|
994
1086
|
\`\`\`
|
|
995
1087
|
` : template === "graphql" ? `\`\`\`
|
|
996
1088
|
resolvers/
|
|
@@ -1003,7 +1095,7 @@ resolvers/
|
|
|
1003
1095
|
├── <name>.controller.ts # HTTP routes (@Controller)
|
|
1004
1096
|
├── <name>.service.ts # Business logic (@Service)
|
|
1005
1097
|
├── <name>.dto.ts # Request/response schemas (Zod)
|
|
1006
|
-
└── <name>.module.ts # Module definition (
|
|
1098
|
+
└── <name>.module.ts # Module definition (implements AppModule)
|
|
1007
1099
|
\`\`\`
|
|
1008
1100
|
` : `\`\`\`
|
|
1009
1101
|
src/
|
|
@@ -1039,8 +1131,8 @@ If not using generators:
|
|
|
1039
1131
|
- [ ] Create \`src/modules/<name>/<name>.controller.ts\`
|
|
1040
1132
|
- [ ] Add \`@Controller('/path')\` decorator
|
|
1041
1133
|
- [ ] Add route handlers with \`@Get()\`, \`@Post()\`, etc.
|
|
1042
|
-
- [ ] Create module file with
|
|
1043
|
-
- [ ] Register module in \`src/modules/index.ts\`
|
|
1134
|
+
- [ ] Create module file implementing \`AppModule\` with \`routes()\` returning \`{ path, router: buildRoutes(Controller), controller }\`
|
|
1135
|
+
- [ ] Register module in \`src/modules/index.ts\` (\`AppModuleClass[]\` array)
|
|
1044
1136
|
- [ ] Test with \`kick dev\`
|
|
1045
1137
|
|
|
1046
1138
|
### Manual Service
|
|
@@ -1048,7 +1140,7 @@ If not using generators:
|
|
|
1048
1140
|
- [ ] Create \`src/modules/<name>/<name>.service.ts\`
|
|
1049
1141
|
- [ ] Add \`@Service()\` decorator
|
|
1050
1142
|
- [ ] Inject dependencies with \`@Autowired()\`
|
|
1051
|
-
- [ ]
|
|
1143
|
+
- [ ] Inject via \`@Autowired()\` where needed
|
|
1052
1144
|
- [ ] Write unit tests
|
|
1053
1145
|
|
|
1054
1146
|
### New Middleware
|
|
@@ -1073,9 +1165,12 @@ Use \`kick add\` to install KickJS packages with correct peer dependencies:
|
|
|
1073
1165
|
### Generate CRUD Module
|
|
1074
1166
|
|
|
1075
1167
|
\`\`\`bash
|
|
1076
|
-
kick g scaffold user name:string email:string age:number
|
|
1168
|
+
kick g scaffold user name:string email:string:optional age:number
|
|
1077
1169
|
\`\`\`
|
|
1078
1170
|
|
|
1171
|
+
Append \`:optional\` for optional fields (shell-safe, no quoting needed).
|
|
1172
|
+
Quoted \`?\` syntax also works: \`"email:string?"\` or \`"email?:string"\`.
|
|
1173
|
+
|
|
1079
1174
|
This creates a full CRUD module with:
|
|
1080
1175
|
- Controller with GET, POST, PUT, DELETE routes
|
|
1081
1176
|
- Service with business logic
|
|
@@ -1191,7 +1286,17 @@ private config!: ConfigService
|
|
|
1191
1286
|
const port = this.config.get('PORT') // typed: number
|
|
1192
1287
|
\`\`\`
|
|
1193
1288
|
|
|
1194
|
-
3. **
|
|
1289
|
+
3. **Standalone utilities** (no DI — works in scripts, CLI, plain files):
|
|
1290
|
+
\`\`\`ts
|
|
1291
|
+
import { loadEnv, getEnv, reloadEnv, resetEnvCache } from '@forinda/kickjs/config'
|
|
1292
|
+
|
|
1293
|
+
const env = loadEnv(schema) // Parse + validate all vars
|
|
1294
|
+
const port = getEnv('PORT') // Single value lookup
|
|
1295
|
+
reloadEnv() // Re-read .env from disk
|
|
1296
|
+
resetEnvCache() // Full reset (for tests)
|
|
1297
|
+
\`\`\`
|
|
1298
|
+
|
|
1299
|
+
4. **Direct \`process.env\`** — avoid in app code; bypasses Zod
|
|
1195
1300
|
coercion and the typed \`KickEnv\` registry.
|
|
1196
1301
|
|
|
1197
1302
|
> **Pitfall**: never delete \`import './config'\` from \`src/index.ts\`.
|
|
@@ -1200,6 +1305,22 @@ const port = this.config.get('PORT') // typed: number
|
|
|
1200
1305
|
> \`@Value()\` only works because of its raw \`process.env\` fallback —
|
|
1201
1306
|
> Zod coercion + schema defaults are silently skipped.
|
|
1202
1307
|
|
|
1308
|
+
## Standalone Utilities (No DI Required)
|
|
1309
|
+
|
|
1310
|
+
These work anywhere — scripts, plain files, outside \`@Service\`/\`@Controller\`:
|
|
1311
|
+
|
|
1312
|
+
| Utility | Import | Example |
|
|
1313
|
+
|---------|--------|---------|
|
|
1314
|
+
| \`Logger.for(name)\` | \`@forinda/kickjs\` | \`const log = Logger.for('MyScript')\` |
|
|
1315
|
+
| \`createLogger(name)\` | \`@forinda/kickjs\` | \`const log = createLogger('Worker')\` |
|
|
1316
|
+
| \`createToken<T>(name)\` | \`@forinda/kickjs\` | \`const TOKEN = createToken<string>('db.url')\` |
|
|
1317
|
+
| \`ref(value)\` | \`@forinda/kickjs\` | \`const count = ref(0)\` |
|
|
1318
|
+
| \`computed(fn)\` | \`@forinda/kickjs\` | \`const doubled = computed(() => count.value * 2)\` |
|
|
1319
|
+
| \`watch(source, cb)\` | \`@forinda/kickjs\` | \`watch(() => count.value, (v) => log(v))\` |
|
|
1320
|
+
| \`reactive(obj)\` | \`@forinda/kickjs\` | \`const state = reactive({ count: 0 })\` |
|
|
1321
|
+
| \`HttpException\` | \`@forinda/kickjs\` | \`throw new HttpException(404, 'Not found')\` |
|
|
1322
|
+
| \`HttpStatus\` | \`@forinda/kickjs\` | \`HttpStatus.NOT_FOUND // 404\` |
|
|
1323
|
+
|
|
1203
1324
|
## Key Decorators
|
|
1204
1325
|
|
|
1205
1326
|
### HTTP Routes
|
|
@@ -1214,7 +1335,7 @@ const port = this.config.get('PORT') // typed: number
|
|
|
1214
1335
|
### Dependency Injection
|
|
1215
1336
|
| Decorator | Purpose |
|
|
1216
1337
|
|-----------|---------|
|
|
1217
|
-
|
|
|
1338
|
+
| \`AppModule\` interface | Define feature module (implements \`routes()\`) |
|
|
1218
1339
|
| \`@Service()\` | Register singleton service |
|
|
1219
1340
|
| \`@Repository()\` | Register repository |
|
|
1220
1341
|
| \`@Autowired()\` | Property injection |
|
|
@@ -1310,6 +1431,26 @@ async function initProject(options) {
|
|
|
1310
1431
|
await writeFileSafe(join(dir, "README.md"), generateReadme(name, template, packageManager));
|
|
1311
1432
|
await writeFileSafe(join(dir, "CLAUDE.md"), generateClaude(name, template, packageManager));
|
|
1312
1433
|
await writeFileSafe(join(dir, "AGENTS.md"), generateAgents(name, template, packageManager));
|
|
1434
|
+
if (options.installDeps) {
|
|
1435
|
+
console.log(`\n Installing dependencies with ${packageManager}...\n`);
|
|
1436
|
+
try {
|
|
1437
|
+
execSync(`${packageManager} install`, {
|
|
1438
|
+
cwd: dir,
|
|
1439
|
+
stdio: "inherit"
|
|
1440
|
+
});
|
|
1441
|
+
console.log("\n Dependencies installed successfully!");
|
|
1442
|
+
} catch {
|
|
1443
|
+
console.log(`\n Warning: ${packageManager} install failed. Run it manually.`);
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
try {
|
|
1447
|
+
const { runTypegen } = await Promise.resolve().then(() => typegen_exports);
|
|
1448
|
+
await runTypegen({
|
|
1449
|
+
cwd: dir,
|
|
1450
|
+
allowDuplicates: true,
|
|
1451
|
+
silent: true
|
|
1452
|
+
});
|
|
1453
|
+
} catch {}
|
|
1313
1454
|
if (options.initGit) try {
|
|
1314
1455
|
execSync("git init", {
|
|
1315
1456
|
cwd: dir,
|
|
@@ -1331,26 +1472,6 @@ async function initProject(options) {
|
|
|
1331
1472
|
} catch {
|
|
1332
1473
|
log("Warning: git init failed (git may not be installed)");
|
|
1333
1474
|
}
|
|
1334
|
-
if (options.installDeps) {
|
|
1335
|
-
console.log(`\n Installing dependencies with ${packageManager}...\n`);
|
|
1336
|
-
try {
|
|
1337
|
-
execSync(`${packageManager} install`, {
|
|
1338
|
-
cwd: dir,
|
|
1339
|
-
stdio: "inherit"
|
|
1340
|
-
});
|
|
1341
|
-
console.log("\n Dependencies installed successfully!");
|
|
1342
|
-
} catch {
|
|
1343
|
-
console.log(`\n Warning: ${packageManager} install failed. Run it manually.`);
|
|
1344
|
-
}
|
|
1345
|
-
}
|
|
1346
|
-
try {
|
|
1347
|
-
const { runTypegen } = await Promise.resolve().then(() => typegen_exports);
|
|
1348
|
-
await runTypegen({
|
|
1349
|
-
cwd: dir,
|
|
1350
|
-
allowDuplicates: true,
|
|
1351
|
-
silent: true
|
|
1352
|
-
});
|
|
1353
|
-
} catch {}
|
|
1354
1475
|
console.log("\n Project scaffolded successfully!");
|
|
1355
1476
|
console.log();
|
|
1356
1477
|
const needsCd = dir !== process.cwd();
|
|
@@ -1522,26 +1643,18 @@ function toKebabCase(name) {
|
|
|
1522
1643
|
}
|
|
1523
1644
|
/**
|
|
1524
1645
|
* Pluralize a kebab-case name for directory/file names.
|
|
1525
|
-
*
|
|
1646
|
+
* Uses the `pluralize` npm package for correct English pluralization
|
|
1647
|
+
* including irregulars (person → people, status → statuses, child → children).
|
|
1526
1648
|
*/
|
|
1527
1649
|
function pluralize(name) {
|
|
1528
|
-
|
|
1529
|
-
if (name.endsWith("x") || name.endsWith("z")) return name + "es";
|
|
1530
|
-
if (name.endsWith("sh") || name.endsWith("ch")) return name + "es";
|
|
1531
|
-
if (name.endsWith("y") && !/[aeiou]y$/.test(name)) return name.slice(0, -1) + "ies";
|
|
1532
|
-
return name + "s";
|
|
1650
|
+
return pkg.plural(name);
|
|
1533
1651
|
}
|
|
1534
1652
|
/**
|
|
1535
1653
|
* Pluralize a PascalCase name for class identifiers.
|
|
1536
|
-
* If already plural (ends in 's'), returns as-is.
|
|
1537
1654
|
* Used for `List${pluralPascal}UseCase` to avoid `ListUserssUseCase`.
|
|
1538
1655
|
*/
|
|
1539
1656
|
function pluralizePascal(name) {
|
|
1540
|
-
|
|
1541
|
-
if (name.endsWith("x") || name.endsWith("z")) return name + "es";
|
|
1542
|
-
if (name.endsWith("sh") || name.endsWith("ch")) return name + "es";
|
|
1543
|
-
if (name.endsWith("y") && !/[aeiou]y$/i.test(name)) return name.slice(0, -1) + "ies";
|
|
1544
|
-
return name + "s";
|
|
1657
|
+
return pkg.plural(name);
|
|
1545
1658
|
}
|
|
1546
1659
|
//#endregion
|
|
1547
1660
|
//#region src/generators/templates/module-index.ts
|
|
@@ -3585,13 +3698,14 @@ const CQRS_FOLDER_MAP = {
|
|
|
3585
3698
|
* 3. Standalone default directory
|
|
3586
3699
|
*/
|
|
3587
3700
|
function resolveOutDir(options) {
|
|
3588
|
-
const { type, outDir, moduleName, modulesDir = "src/modules", defaultDir, pattern = "ddd" } = options;
|
|
3701
|
+
const { type, outDir, moduleName, modulesDir = "src/modules", defaultDir, pattern = "ddd", shouldPluralize = true } = options;
|
|
3589
3702
|
if (outDir) return resolve(outDir);
|
|
3590
3703
|
if (moduleName) {
|
|
3591
3704
|
const folderMap = pattern === "ddd" ? DDD_FOLDER_MAP : pattern === "cqrs" ? CQRS_FOLDER_MAP : FLAT_FOLDER_MAP;
|
|
3592
|
-
const
|
|
3705
|
+
const kebab = toKebabCase(moduleName);
|
|
3706
|
+
const folder = shouldPluralize ? pluralize(kebab) : kebab;
|
|
3593
3707
|
const subfolder = folderMap[type] ?? "";
|
|
3594
|
-
const base = join(modulesDir,
|
|
3708
|
+
const base = join(modulesDir, folder);
|
|
3595
3709
|
return resolve(subfolder ? join(base, subfolder) : base);
|
|
3596
3710
|
}
|
|
3597
3711
|
return resolve(defaultDir);
|
|
@@ -3606,7 +3720,8 @@ async function generateMiddleware(options) {
|
|
|
3606
3720
|
moduleName,
|
|
3607
3721
|
modulesDir,
|
|
3608
3722
|
defaultDir: "src/middleware",
|
|
3609
|
-
pattern
|
|
3723
|
+
pattern,
|
|
3724
|
+
shouldPluralize: options.pluralize ?? true
|
|
3610
3725
|
});
|
|
3611
3726
|
const kebab = toKebabCase(name);
|
|
3612
3727
|
const camel = toCamelCase(name);
|
|
@@ -3650,7 +3765,8 @@ async function generateGuard(options) {
|
|
|
3650
3765
|
moduleName,
|
|
3651
3766
|
modulesDir,
|
|
3652
3767
|
defaultDir: "src/guards",
|
|
3653
|
-
pattern
|
|
3768
|
+
pattern,
|
|
3769
|
+
shouldPluralize: options.pluralize ?? true
|
|
3654
3770
|
});
|
|
3655
3771
|
const kebab = toKebabCase(name);
|
|
3656
3772
|
const camel = toCamelCase(name);
|
|
@@ -3707,7 +3823,8 @@ async function generateService(options) {
|
|
|
3707
3823
|
moduleName,
|
|
3708
3824
|
modulesDir,
|
|
3709
3825
|
defaultDir: "src/services",
|
|
3710
|
-
pattern
|
|
3826
|
+
pattern,
|
|
3827
|
+
shouldPluralize: options.pluralize ?? true
|
|
3711
3828
|
});
|
|
3712
3829
|
const kebab = toKebabCase(name);
|
|
3713
3830
|
const pascal = toPascalCase(name);
|
|
@@ -3736,7 +3853,8 @@ async function generateController(options) {
|
|
|
3736
3853
|
moduleName,
|
|
3737
3854
|
modulesDir,
|
|
3738
3855
|
defaultDir: "src/controllers",
|
|
3739
|
-
pattern
|
|
3856
|
+
pattern,
|
|
3857
|
+
shouldPluralize: options.pluralize ?? true
|
|
3740
3858
|
});
|
|
3741
3859
|
const kebab = toKebabCase(name);
|
|
3742
3860
|
const pascal = toPascalCase(name);
|
|
@@ -3777,7 +3895,8 @@ async function generateDto(options) {
|
|
|
3777
3895
|
moduleName,
|
|
3778
3896
|
modulesDir,
|
|
3779
3897
|
defaultDir: "src/dtos",
|
|
3780
|
-
pattern
|
|
3898
|
+
pattern,
|
|
3899
|
+
shouldPluralize: options.pluralize ?? true
|
|
3781
3900
|
});
|
|
3782
3901
|
const kebab = toKebabCase(name);
|
|
3783
3902
|
const pascal = toPascalCase(name);
|
|
@@ -3996,7 +4115,10 @@ export class ${pascal}Job {
|
|
|
3996
4115
|
* json → z.any()
|
|
3997
4116
|
* enum:a,b → z.enum(['a','b'])
|
|
3998
4117
|
*
|
|
3999
|
-
*
|
|
4118
|
+
* Mark optional fields — three equivalent syntaxes:
|
|
4119
|
+
* body:text:optional ← recommended (shell-safe, no quoting needed)
|
|
4120
|
+
* body?:text ← needs quoting in bash/zsh ("body?:text")
|
|
4121
|
+
* body:text? ← needs quoting in bash/zsh ("body:text?")
|
|
4000
4122
|
*/
|
|
4001
4123
|
const TYPE_MAP = {
|
|
4002
4124
|
string: {
|
|
@@ -4048,11 +4170,23 @@ function parseFields(raw) {
|
|
|
4048
4170
|
return raw.map((f) => {
|
|
4049
4171
|
const colonIdx = f.indexOf(":");
|
|
4050
4172
|
if (colonIdx === -1) throw new Error(`Invalid field: "${f}". Use format: name:type (e.g. title:string)`);
|
|
4051
|
-
|
|
4052
|
-
|
|
4173
|
+
let namePart = f.slice(0, colonIdx);
|
|
4174
|
+
let typePart = f.slice(colonIdx + 1);
|
|
4053
4175
|
if (!namePart || !typePart) throw new Error(`Invalid field: "${f}". Use format: name:type (e.g. title:string)`);
|
|
4054
|
-
|
|
4055
|
-
|
|
4176
|
+
let optional = false;
|
|
4177
|
+
if (typePart.endsWith(":optional")) {
|
|
4178
|
+
typePart = typePart.slice(0, -9);
|
|
4179
|
+
optional = true;
|
|
4180
|
+
}
|
|
4181
|
+
if (namePart.endsWith("?")) {
|
|
4182
|
+
namePart = namePart.slice(0, -1);
|
|
4183
|
+
optional = true;
|
|
4184
|
+
}
|
|
4185
|
+
if (typePart.endsWith("?")) {
|
|
4186
|
+
typePart = typePart.slice(0, -1);
|
|
4187
|
+
optional = true;
|
|
4188
|
+
}
|
|
4189
|
+
const cleanType = typePart;
|
|
4056
4190
|
if (cleanType.startsWith("enum:")) {
|
|
4057
4191
|
const values = cleanType.slice(5).split(",");
|
|
4058
4192
|
return {
|
|
@@ -4517,14 +4651,16 @@ async function autoRegisterModule(modulesDir, pascal, plural) {
|
|
|
4517
4651
|
//#region src/generators/test.ts
|
|
4518
4652
|
async function generateTest(options) {
|
|
4519
4653
|
const { name, moduleName, modulesDir } = options;
|
|
4654
|
+
const shouldPluralize = options.pluralize ?? true;
|
|
4520
4655
|
const kebab = toKebabCase(name);
|
|
4521
4656
|
const pascal = toPascalCase(name);
|
|
4522
4657
|
const files = [];
|
|
4523
4658
|
let outDir;
|
|
4524
4659
|
if (options.outDir) outDir = resolve(options.outDir);
|
|
4525
4660
|
else if (moduleName) {
|
|
4526
|
-
const
|
|
4527
|
-
|
|
4661
|
+
const modKebab = toKebabCase(moduleName);
|
|
4662
|
+
const modFolder = shouldPluralize ? pluralize(modKebab) : modKebab;
|
|
4663
|
+
outDir = resolve(join(modulesDir ?? "src/modules", modFolder, "__tests__"));
|
|
4528
4664
|
} else outDir = resolve("src/__tests__");
|
|
4529
4665
|
const filePath = join(outDir, `${kebab}.test.ts`);
|
|
4530
4666
|
await writeFileSafe(filePath, `import { describe, it, expect, beforeEach } from 'vitest'
|
|
@@ -5703,52 +5839,60 @@ function registerGenerateCommand(program) {
|
|
|
5703
5839
|
const dryRun = isDryRun(cmd);
|
|
5704
5840
|
setDryRun(dryRun);
|
|
5705
5841
|
const config = await loadKickConfig(process.cwd());
|
|
5706
|
-
const
|
|
5842
|
+
const mc = resolveModuleConfig(config);
|
|
5843
|
+
const modulesDir = mc.dir ?? "src/modules";
|
|
5707
5844
|
printGenerated(await generateMiddleware({
|
|
5708
5845
|
name,
|
|
5709
5846
|
outDir: opts.out,
|
|
5710
5847
|
moduleName: opts.module,
|
|
5711
5848
|
modulesDir,
|
|
5712
|
-
pattern: config?.pattern
|
|
5849
|
+
pattern: config?.pattern,
|
|
5850
|
+
pluralize: mc.pluralize ?? true
|
|
5713
5851
|
}), dryRun);
|
|
5714
5852
|
});
|
|
5715
5853
|
gen.command("guard <name>").description("Generate a route guard (auth, roles, etc.)\n Use -m to scope it to a module: kick g guard admin -m users").option("-o, --out <dir>", "Output directory (overrides --module)").option("-m, --module <module>", "Place inside a module folder").action(async (name, opts, cmd) => {
|
|
5716
5854
|
const dryRun = isDryRun(cmd);
|
|
5717
5855
|
setDryRun(dryRun);
|
|
5718
5856
|
const config = await loadKickConfig(process.cwd());
|
|
5719
|
-
const
|
|
5857
|
+
const mc = resolveModuleConfig(config);
|
|
5858
|
+
const modulesDir = mc.dir ?? "src/modules";
|
|
5720
5859
|
printGenerated(await generateGuard({
|
|
5721
5860
|
name,
|
|
5722
5861
|
outDir: opts.out,
|
|
5723
5862
|
moduleName: opts.module,
|
|
5724
5863
|
modulesDir,
|
|
5725
|
-
pattern: config?.pattern
|
|
5864
|
+
pattern: config?.pattern,
|
|
5865
|
+
pluralize: mc.pluralize ?? true
|
|
5726
5866
|
}), dryRun);
|
|
5727
5867
|
});
|
|
5728
5868
|
gen.command("service <name>").description("Generate a @Service() class\n Use -m to scope it to a module: kick g service payment -m orders").option("-o, --out <dir>", "Output directory (overrides --module)").option("-m, --module <module>", "Place inside a module folder").action(async (name, opts, cmd) => {
|
|
5729
5869
|
const dryRun = isDryRun(cmd);
|
|
5730
5870
|
setDryRun(dryRun);
|
|
5731
5871
|
const config = await loadKickConfig(process.cwd());
|
|
5732
|
-
const
|
|
5872
|
+
const mc = resolveModuleConfig(config);
|
|
5873
|
+
const modulesDir = mc.dir ?? "src/modules";
|
|
5733
5874
|
printGenerated(await generateService({
|
|
5734
5875
|
name,
|
|
5735
5876
|
outDir: opts.out,
|
|
5736
5877
|
moduleName: opts.module,
|
|
5737
5878
|
modulesDir,
|
|
5738
|
-
pattern: config?.pattern
|
|
5879
|
+
pattern: config?.pattern,
|
|
5880
|
+
pluralize: mc.pluralize ?? true
|
|
5739
5881
|
}), dryRun);
|
|
5740
5882
|
});
|
|
5741
5883
|
gen.command("controller <name>").description("Generate a @Controller() class with basic routes\n Use -m to scope it to a module: kick g controller auth -m users").option("-o, --out <dir>", "Output directory (overrides --module)").option("-m, --module <module>", "Place inside a module folder").action(async (name, opts, cmd) => {
|
|
5742
5884
|
const dryRun = isDryRun(cmd);
|
|
5743
5885
|
setDryRun(dryRun);
|
|
5744
5886
|
const config = await loadKickConfig(process.cwd());
|
|
5745
|
-
const
|
|
5887
|
+
const mc = resolveModuleConfig(config);
|
|
5888
|
+
const modulesDir = mc.dir ?? "src/modules";
|
|
5746
5889
|
printGenerated(await generateController({
|
|
5747
5890
|
name,
|
|
5748
5891
|
outDir: opts.out,
|
|
5749
5892
|
moduleName: opts.module,
|
|
5750
5893
|
modulesDir,
|
|
5751
|
-
pattern: config?.pattern
|
|
5894
|
+
pattern: config?.pattern,
|
|
5895
|
+
pluralize: mc.pluralize ?? true
|
|
5752
5896
|
}), dryRun);
|
|
5753
5897
|
await runPostTypegen(dryRun);
|
|
5754
5898
|
});
|
|
@@ -5756,24 +5900,28 @@ function registerGenerateCommand(program) {
|
|
|
5756
5900
|
const dryRun = isDryRun(cmd);
|
|
5757
5901
|
setDryRun(dryRun);
|
|
5758
5902
|
const config = await loadKickConfig(process.cwd());
|
|
5759
|
-
const
|
|
5903
|
+
const mc = resolveModuleConfig(config);
|
|
5904
|
+
const modulesDir = mc.dir ?? "src/modules";
|
|
5760
5905
|
printGenerated(await generateDto({
|
|
5761
5906
|
name,
|
|
5762
5907
|
outDir: opts.out,
|
|
5763
5908
|
moduleName: opts.module,
|
|
5764
5909
|
modulesDir,
|
|
5765
|
-
pattern: config?.pattern
|
|
5910
|
+
pattern: config?.pattern,
|
|
5911
|
+
pluralize: mc.pluralize ?? true
|
|
5766
5912
|
}), dryRun);
|
|
5767
5913
|
});
|
|
5768
5914
|
gen.command("test <name>").description("Generate a Vitest test scaffold\n Use -m to scope it to a module: kick g test user-service -m users").option("-o, --out <dir>", "Output directory (overrides --module)").option("-m, --module <module>", "Place inside a module's __tests__/ folder").action(async (name, opts, cmd) => {
|
|
5769
5915
|
const dryRun = isDryRun(cmd);
|
|
5770
5916
|
setDryRun(dryRun);
|
|
5771
|
-
const
|
|
5917
|
+
const mc = resolveModuleConfig(await loadKickConfig(process.cwd()));
|
|
5918
|
+
const modulesDir = mc.dir ?? "src/modules";
|
|
5772
5919
|
printGenerated(await generateTest({
|
|
5773
5920
|
name,
|
|
5774
5921
|
outDir: opts.out,
|
|
5775
5922
|
moduleName: opts.module,
|
|
5776
|
-
modulesDir
|
|
5923
|
+
modulesDir,
|
|
5924
|
+
pluralize: mc.pluralize ?? true
|
|
5777
5925
|
}), dryRun);
|
|
5778
5926
|
});
|
|
5779
5927
|
gen.command("resolver <name>").description("Generate a GraphQL @Resolver class with @Query and @Mutation methods").option("-o, --out <dir>", "Output directory", "src/resolvers").action(async (name, opts, cmd) => {
|
|
@@ -5793,11 +5941,11 @@ function registerGenerateCommand(program) {
|
|
|
5793
5941
|
queue: opts.queue
|
|
5794
5942
|
}), dryRun);
|
|
5795
5943
|
});
|
|
5796
|
-
gen.command("scaffold <name> [fields...]").description("Generate a full CRUD module from field definitions\n Example: kick g scaffold Post title:string body:text published:boolean
|
|
5944
|
+
gen.command("scaffold <name> [fields...]").description("Generate a full CRUD module from field definitions\n Example: kick g scaffold Post title:string body:text:optional published:boolean:optional\n Types: string, text, number, int, float, boolean, date, email, url, uuid, json, enum:a,b,c\n Optional: append :optional (shell-safe): description:text:optional\n or use ? with quoting: \"description:text?\" or \"description?:text\"").option("--no-entity", "Skip entity and value object generation").option("--no-tests", "Skip test file generation").option("--no-pluralize", "Use singular names (skip auto-pluralization)").option("--modules-dir <dir>", "Modules directory").action(async (name, rawFields, opts, cmd) => {
|
|
5797
5945
|
const dryRun = isDryRun(cmd);
|
|
5798
5946
|
setDryRun(dryRun);
|
|
5799
5947
|
if (rawFields.length === 0) {
|
|
5800
|
-
console.error("\n Error: At least one field is required.\n Usage: kick g scaffold <name> <field:type> [field:type...]\n Example: kick g scaffold Post title:string body:text published:boolean\n");
|
|
5948
|
+
console.error("\n Error: At least one field is required.\n Usage: kick g scaffold <name> <field:type> [field:type...]\n Example: kick g scaffold Post title:string body:text:optional published:boolean:optional\n Optional: append :optional (shell-safe, no quoting needed)\n");
|
|
5801
5949
|
process.exit(1);
|
|
5802
5950
|
}
|
|
5803
5951
|
const mc = resolveModuleConfig(await loadKickConfig(process.cwd()));
|
|
@@ -6613,10 +6761,10 @@ function registerTypegenCommand(program) {
|
|
|
6613
6761
|
//#endregion
|
|
6614
6762
|
//#region src/cli.ts
|
|
6615
6763
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
6616
|
-
const pkg = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8"));
|
|
6764
|
+
const pkg$1 = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8"));
|
|
6617
6765
|
async function main() {
|
|
6618
6766
|
const program = new Command();
|
|
6619
|
-
program.name("kick").description("KickJS — A production-grade, decorator-driven Node.js framework").version(pkg.version);
|
|
6767
|
+
program.name("kick").description("KickJS — A production-grade, decorator-driven Node.js framework").version(pkg$1.version);
|
|
6620
6768
|
const config = await loadKickConfig(process.cwd());
|
|
6621
6769
|
registerInitCommand(program);
|
|
6622
6770
|
registerGenerateCommand(program);
|
package/dist/index.d.mts
CHANGED
|
@@ -228,6 +228,7 @@ interface GenerateMiddlewareOptions {
|
|
|
228
228
|
moduleName?: string;
|
|
229
229
|
modulesDir?: string;
|
|
230
230
|
pattern?: ProjectPattern;
|
|
231
|
+
pluralize?: boolean;
|
|
231
232
|
}
|
|
232
233
|
declare function generateMiddleware(options: GenerateMiddlewareOptions): Promise<string[]>;
|
|
233
234
|
//#endregion
|
|
@@ -238,6 +239,7 @@ interface GenerateGuardOptions {
|
|
|
238
239
|
moduleName?: string;
|
|
239
240
|
modulesDir?: string;
|
|
240
241
|
pattern?: ProjectPattern;
|
|
242
|
+
pluralize?: boolean;
|
|
241
243
|
}
|
|
242
244
|
declare function generateGuard(options: GenerateGuardOptions): Promise<string[]>;
|
|
243
245
|
//#endregion
|
|
@@ -248,6 +250,7 @@ interface GenerateServiceOptions {
|
|
|
248
250
|
moduleName?: string;
|
|
249
251
|
modulesDir?: string;
|
|
250
252
|
pattern?: ProjectPattern;
|
|
253
|
+
pluralize?: boolean;
|
|
251
254
|
}
|
|
252
255
|
declare function generateService(options: GenerateServiceOptions): Promise<string[]>;
|
|
253
256
|
//#endregion
|
|
@@ -258,6 +261,7 @@ interface GenerateControllerOptions {
|
|
|
258
261
|
moduleName?: string;
|
|
259
262
|
modulesDir?: string;
|
|
260
263
|
pattern?: ProjectPattern;
|
|
264
|
+
pluralize?: boolean;
|
|
261
265
|
}
|
|
262
266
|
declare function generateController(options: GenerateControllerOptions): Promise<string[]>;
|
|
263
267
|
//#endregion
|
|
@@ -268,6 +272,7 @@ interface GenerateDtoOptions {
|
|
|
268
272
|
moduleName?: string;
|
|
269
273
|
modulesDir?: string;
|
|
270
274
|
pattern?: ProjectPattern;
|
|
275
|
+
pluralize?: boolean;
|
|
271
276
|
}
|
|
272
277
|
declare function generateDto(options: GenerateDtoOptions): Promise<string[]>;
|
|
273
278
|
//#endregion
|
|
@@ -294,7 +299,8 @@ declare function toCamelCase(name: string): string;
|
|
|
294
299
|
declare function toKebabCase(name: string): string;
|
|
295
300
|
/**
|
|
296
301
|
* Pluralize a kebab-case name for directory/file names.
|
|
297
|
-
*
|
|
302
|
+
* Uses the `pluralize` npm package for correct English pluralization
|
|
303
|
+
* including irregulars (person → people, status → statuses, child → children).
|
|
298
304
|
*/
|
|
299
305
|
declare function pluralize(name: string): string;
|
|
300
306
|
//#endregion
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/config.ts","../src/generators/module.ts","../src/generators/adapter.ts","../src/generators/middleware.ts","../src/generators/guard.ts","../src/generators/service.ts","../src/generators/controller.ts","../src/generators/dto.ts","../src/generators/project.ts","../src/utils/naming.ts"],"mappings":";;;UAIiB,qBAAA;EAAqB;EAEpC,IAAA;EAFoC;EAIpC,WAAA;EAAA;;;;;AAeF;;;EANE,KAAA;EAMwB;EAJxB,OAAA;AAAA;;KAIU,cAAA;;KAGA,iBAAA;;UAKK,cAAA;EACf,IAAA;AAAA;;KAIU,cAAA,GAAiB,iBAAA,GAAkB,cAAA;;;AAS/C;;;;;KAAY,eAAA;;UAGK,aAAA;EAuBkB;;;;EAlBjC,MAAA;EA4BA;;;AAIF;EA3BE,MAAA;;;;;;;;;;;AAiEF;;EApDE,eAAA,GAAkB,eAAA;EA6DR;;;;;;;;;EAnDV,OAAA;AAAA;;UAIe,YAAA;EAiEf;EA/DA,GAAA;EAiEA;;;;;;;;;;;;;EAnDA,IAAA,GAAO,cAAA;EAuFL;EArFF,SAAA;EAqFQ;AAKV;;;;EApFE,SAAA;EAoF2B;;;;AA6B7B;;;;;EAvGE,gBAAA;AAAA;;UAIe,UAAA;;;;AC7GjB;;;;;EDsHE,OAAA,GAAU,cAAA;ECrHQ;;;;AAOnB;;;;;;;ED0HC,OAAA,GAAU,YAAA;ECnHV;EDuHA,UAAA;ECtHA;EDwHA,WAAA,GAAc,cAAA;ECtHd;EDwHA,SAAA;ECvHA;EDyHA,SAAA;ECrHA;;;AAwBF;;;;;;;;;;ED2GE,QAAA,GAAW,KAAA;IAAiB,GAAA;IAAa,IAAA;EAAA;;;;AE/J3C;;;;;;;;EF2KE,OAAA,GAAU,aAAA;;EAEV,QAAA,GAAW,qBAAA;;EAEX,KAAA;IACE,UAAA;IACA,MAAA;IACA,aAAA;IACA,MAAA;EAAA;AAAA;;iBAKY,YAAA,CAAa,MAAA,EAAQ,UAAA,GAAa,UAAA;;iBA6B5B,cAAA,CAAe,GAAA,WAAc,OAAA,CAAQ,UAAA;;;KChN/C,eAAA;AAAA,KACA,QAAA,GAAW,eAAA;AAAA,UASb,qBAAA;EACR,IAAA;EACA,UAAA;EACA,QAAA;EACA,OAAA;EACA,IAAA,GAAO,QAAA;EACP,OAAA;EACA,KAAA;EACA,OAAA,GAAU,cAAA;EACV,MAAA;EDVwB;ECYxB,SAAA;EDTyB;ECWzB,gBAAA;AAAA;;ADNF;;;;;AAKA;;;;iBCyBsB,cAAA,CAAe,OAAA,EAAS,qBAAA,GAAwB,OAAA;;;UCzD5D,sBAAA;EACR,IAAA;EACA,MAAA;AAAA;AAAA,iBAGoB,eAAA,CAAgB,OAAA,EAAS,sBAAA,GAAyB,OAAA;;;UCH9D,yBAAA;EACR,IAAA;EACA,MAAA;EACA,UAAA;EACA,UAAA;EACA,OAAA,GAAU,cAAA;AAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/config.ts","../src/generators/module.ts","../src/generators/adapter.ts","../src/generators/middleware.ts","../src/generators/guard.ts","../src/generators/service.ts","../src/generators/controller.ts","../src/generators/dto.ts","../src/generators/project.ts","../src/utils/naming.ts"],"mappings":"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"}
|