@forinda/kickjs-cli 3.2.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -89
- package/dist/cli.mjs +940 -47
- package/dist/index.d.mts +252 -9
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +148 -26
- package/dist/index.mjs.map +1 -1
- package/dist/{typegen-C30frihW.mjs → typegen-vI1eqGLK.mjs} +446 -11
- package/dist/typegen-vI1eqGLK.mjs.map +1 -0
- package/package.json +9 -14
- package/dist/typegen-C30frihW.mjs.map +0 -1
package/dist/index.d.mts
CHANGED
|
@@ -38,6 +38,33 @@ type RepoTypeConfig = BuiltinRepoType$1 | CustomRepoType;
|
|
|
38
38
|
* entirely (the route entries will keep `body: unknown`).
|
|
39
39
|
*/
|
|
40
40
|
type SchemaValidator = 'zod' | false;
|
|
41
|
+
/**
|
|
42
|
+
* One entry in the typed `assetMap` config record (`assets-plan.md`).
|
|
43
|
+
* Each entry names a source directory whose files become addressable
|
|
44
|
+
* via the `assets.<name>.*` typed accessor at runtime.
|
|
45
|
+
*/
|
|
46
|
+
interface AssetMapEntry {
|
|
47
|
+
/**
|
|
48
|
+
* Source directory, relative to project root. Required. The directory
|
|
49
|
+
* must exist when `kick build` runs — `loadKickConfig` warns when an
|
|
50
|
+
* entry points at a missing directory but doesn't fail the load
|
|
51
|
+
* (the typegen + build steps surface the error in context instead).
|
|
52
|
+
*/
|
|
53
|
+
src: string;
|
|
54
|
+
/**
|
|
55
|
+
* Destination directory inside `dist/`. Defaults to `dist/<name>/`
|
|
56
|
+
* where `<name>` is the assetMap key. Override when the consumer of
|
|
57
|
+
* the assets expects a non-standard layout (e.g. an existing
|
|
58
|
+
* downstream tool reads from `dist/templates/...`).
|
|
59
|
+
*/
|
|
60
|
+
dest?: string;
|
|
61
|
+
/**
|
|
62
|
+
* Glob pattern for which files to include. Defaults to `**\/*` (all
|
|
63
|
+
* files). Files that don't match are NOT copied — `assetMap` is
|
|
64
|
+
* selective by design (unlike `copyDirs` which copies everything).
|
|
65
|
+
*/
|
|
66
|
+
glob?: string;
|
|
67
|
+
}
|
|
41
68
|
/** Typegen settings — controls .kickjs/types/* generation */
|
|
42
69
|
interface TypegenConfig {
|
|
43
70
|
/**
|
|
@@ -150,14 +177,6 @@ interface KickConfig {
|
|
|
150
177
|
* packageManager: 'pnpm'
|
|
151
178
|
*/
|
|
152
179
|
packageManager?: PackageManager;
|
|
153
|
-
/** @deprecated Use `modules.dir` instead */
|
|
154
|
-
modulesDir?: string;
|
|
155
|
-
/** @deprecated Use `modules.repo` instead */
|
|
156
|
-
defaultRepo?: RepoTypeConfig;
|
|
157
|
-
/** @deprecated Use `modules.schemaDir` instead */
|
|
158
|
-
schemaDir?: string;
|
|
159
|
-
/** @deprecated Use `modules.pluralize` instead */
|
|
160
|
-
pluralize?: boolean;
|
|
161
180
|
/**
|
|
162
181
|
* Directories to copy to dist/ after build.
|
|
163
182
|
* Useful for EJS templates, email templates, static assets, etc.
|
|
@@ -175,6 +194,48 @@ interface KickConfig {
|
|
|
175
194
|
src: string;
|
|
176
195
|
dest?: string;
|
|
177
196
|
}>;
|
|
197
|
+
/**
|
|
198
|
+
* Build output settings. The asset manager + `kick build`'s copy
|
|
199
|
+
* steps honour these — adopters who use Vite's `build.outDir =
|
|
200
|
+
* 'out'` (or any non-default) should mirror the value here so
|
|
201
|
+
* `assets.x.y()` paths line up with where Vite actually wrote.
|
|
202
|
+
*
|
|
203
|
+
* @example
|
|
204
|
+
* ```ts
|
|
205
|
+
* build: { outDir: 'out' }
|
|
206
|
+
* ```
|
|
207
|
+
*/
|
|
208
|
+
build?: {
|
|
209
|
+
/**
|
|
210
|
+
* Output directory, relative to project root. Defaults to
|
|
211
|
+
* `'dist'`. The asset manager emits its manifest + copies
|
|
212
|
+
* assetMap entries into this directory (under a per-namespace
|
|
213
|
+
* subdirectory by default; override per-entry via `dest`).
|
|
214
|
+
*/
|
|
215
|
+
outDir?: string;
|
|
216
|
+
};
|
|
217
|
+
/**
|
|
218
|
+
* Typed, addressable assets — see `assets-plan.md`. Each entry maps
|
|
219
|
+
* a logical namespace name to a source directory. The build pipeline
|
|
220
|
+
* auto-derives the necessary copy step + emits a manifest at
|
|
221
|
+
* `dist/.kickjs-assets.json`; the runtime exposes
|
|
222
|
+
* `import { assets } from '@forinda/kickjs'` so adopters can resolve
|
|
223
|
+
* paths without dev/prod branching.
|
|
224
|
+
*
|
|
225
|
+
* `copyDirs` is unchanged — `assetMap` is a separate, opt-in surface.
|
|
226
|
+
* Adopters who want raw directory copies keep using `copyDirs`; those
|
|
227
|
+
* who want typed addressable assets add `assetMap` entries.
|
|
228
|
+
*
|
|
229
|
+
* @example
|
|
230
|
+
* ```ts
|
|
231
|
+
* assetMap: {
|
|
232
|
+
* mails: { src: 'src/templates/mails' },
|
|
233
|
+
* reports: { src: 'src/templates/reports', glob: '**\/*.{ejs,html}' },
|
|
234
|
+
* schemas: { src: 'src/schemas', glob: '**\/*.json' },
|
|
235
|
+
* }
|
|
236
|
+
* ```
|
|
237
|
+
*/
|
|
238
|
+
assetMap?: Record<string, AssetMapEntry>;
|
|
178
239
|
/**
|
|
179
240
|
* Typegen settings — controls `.kickjs/types/*` generation including
|
|
180
241
|
* the schema validator used for body type extraction.
|
|
@@ -309,6 +370,188 @@ interface InitProjectOptions {
|
|
|
309
370
|
/** Scaffold a new KickJS project */
|
|
310
371
|
declare function initProject(options: InitProjectOptions): Promise<void>;
|
|
311
372
|
//#endregion
|
|
373
|
+
//#region src/generator-extension/define.d.ts
|
|
374
|
+
/**
|
|
375
|
+
* Plugin generator API per architecture.md §21.2.3 — lets first-party
|
|
376
|
+
* AND third-party packages ship `kick g <name>` scaffolders the same
|
|
377
|
+
* way the framework's built-in generators do.
|
|
378
|
+
*
|
|
379
|
+
* Plugins declare a discovery file in their `package.json`:
|
|
380
|
+
*
|
|
381
|
+
* ```json
|
|
382
|
+
* {
|
|
383
|
+
* "name": "@my-org/kickjs-cqrs",
|
|
384
|
+
* "kickjs": { "generators": "./dist/generators.js" }
|
|
385
|
+
* }
|
|
386
|
+
* ```
|
|
387
|
+
*
|
|
388
|
+
* The discovery file exports a typed manifest:
|
|
389
|
+
*
|
|
390
|
+
* ```ts
|
|
391
|
+
* import { defineGenerator } from '@forinda/kickjs-cli'
|
|
392
|
+
*
|
|
393
|
+
* export default [
|
|
394
|
+
* defineGenerator({
|
|
395
|
+
* name: 'command',
|
|
396
|
+
* description: 'Generate a CQRS command + handler',
|
|
397
|
+
* args: [{ name: 'name', required: true }],
|
|
398
|
+
* files: (ctx) => [
|
|
399
|
+
* {
|
|
400
|
+
* path: `src/modules/${ctx.kebab}/commands/create-${ctx.kebab}.command.ts`,
|
|
401
|
+
* content: `// generated command for ${ctx.pascal}`,
|
|
402
|
+
* },
|
|
403
|
+
* ],
|
|
404
|
+
* }),
|
|
405
|
+
* ]
|
|
406
|
+
* ```
|
|
407
|
+
*
|
|
408
|
+
* `kick g command Order` then dispatches against the registered
|
|
409
|
+
* generator and writes the returned files relative to `cwd`.
|
|
410
|
+
*/
|
|
411
|
+
/**
|
|
412
|
+
* Resolved naming variants + project context handed to a generator's
|
|
413
|
+
* `files()` factory. Keys mirror `TemplateContext` so first-party
|
|
414
|
+
* generators that rely on the same shape can be migrated to plugin
|
|
415
|
+
* generators without rewriting their templates.
|
|
416
|
+
*/
|
|
417
|
+
interface GeneratorContext {
|
|
418
|
+
/** Raw name passed on the command line (`kick g resolver UserPost` → `'UserPost'`). */
|
|
419
|
+
name: string;
|
|
420
|
+
/** PascalCase form (`UserPost`). */
|
|
421
|
+
pascal: string;
|
|
422
|
+
/** kebab-case form (`user-post`). */
|
|
423
|
+
kebab: string;
|
|
424
|
+
/** camelCase form (`userPost`). */
|
|
425
|
+
camel: string;
|
|
426
|
+
/** snake_case form (`user_post`). */
|
|
427
|
+
snake: string;
|
|
428
|
+
/** Pluralized PascalCase (`UserPosts`) — present when the project enables `pluralize`. */
|
|
429
|
+
pluralPascal?: string;
|
|
430
|
+
/** Pluralized kebab-case (`user-posts`). */
|
|
431
|
+
pluralKebab?: string;
|
|
432
|
+
/** Pluralized camelCase (`userPosts`). */
|
|
433
|
+
pluralCamel?: string;
|
|
434
|
+
/** Modules directory from `kick.config.ts` (default `'src/modules'`). */
|
|
435
|
+
modulesDir: string;
|
|
436
|
+
/** Working directory for the generator — usually `process.cwd()`. */
|
|
437
|
+
cwd: string;
|
|
438
|
+
/** Positional arguments passed AFTER the name (e.g. `kick g command Order extra1 extra2` → `['extra1', 'extra2']`). */
|
|
439
|
+
args: string[];
|
|
440
|
+
/** Flag values from the command line — booleans for switches, strings for `--key value`. */
|
|
441
|
+
flags: Record<string, string | boolean>;
|
|
442
|
+
}
|
|
443
|
+
/** A single output file the generator wants written. */
|
|
444
|
+
interface GeneratorFile {
|
|
445
|
+
/**
|
|
446
|
+
* Output path. Relative paths resolve against `ctx.cwd`; absolute
|
|
447
|
+
* paths are used as-is. Parent directories are created automatically.
|
|
448
|
+
*/
|
|
449
|
+
path: string;
|
|
450
|
+
/** File contents written verbatim (UTF-8). */
|
|
451
|
+
content: string;
|
|
452
|
+
}
|
|
453
|
+
/** CLI argument descriptor surfaced in `kick g --list` help. */
|
|
454
|
+
interface GeneratorArg {
|
|
455
|
+
name: string;
|
|
456
|
+
required?: boolean;
|
|
457
|
+
description?: string;
|
|
458
|
+
}
|
|
459
|
+
/** CLI flag descriptor — boolean unless `takesValue: true`. */
|
|
460
|
+
interface GeneratorFlag {
|
|
461
|
+
name: string;
|
|
462
|
+
alias?: string;
|
|
463
|
+
description?: string;
|
|
464
|
+
takesValue?: boolean;
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Spec returned by {@link defineGenerator}. Plugin discovery files
|
|
468
|
+
* export `GeneratorSpec[]` as their default export.
|
|
469
|
+
*/
|
|
470
|
+
interface GeneratorSpec {
|
|
471
|
+
/**
|
|
472
|
+
* Dispatch name — `kick g <name>` looks for an exact match against
|
|
473
|
+
* this string after the built-in generators are checked.
|
|
474
|
+
*/
|
|
475
|
+
name: string;
|
|
476
|
+
/** Description shown in `kick g --list` and `--help`. */
|
|
477
|
+
description: string;
|
|
478
|
+
/** Optional argument descriptors — informational, surfaced in help output. */
|
|
479
|
+
args?: readonly GeneratorArg[];
|
|
480
|
+
/** Optional flag descriptors — informational, surfaced in help output. */
|
|
481
|
+
flags?: readonly GeneratorFlag[];
|
|
482
|
+
/** Build the output files for one invocation. May return a Promise. */
|
|
483
|
+
files(ctx: GeneratorContext): GeneratorFile[] | Promise<GeneratorFile[]>;
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Identity factory — returns the spec verbatim. Exists for type
|
|
487
|
+
* inference and forward-compatibility (future fields can be added with
|
|
488
|
+
* defaults).
|
|
489
|
+
*
|
|
490
|
+
* @example
|
|
491
|
+
* ```ts
|
|
492
|
+
* import { defineGenerator } from '@forinda/kickjs-cli'
|
|
493
|
+
*
|
|
494
|
+
* export default [
|
|
495
|
+
* defineGenerator({
|
|
496
|
+
* name: 'command',
|
|
497
|
+
* description: 'Generate a CQRS command + handler',
|
|
498
|
+
* files: (ctx) => [
|
|
499
|
+
* {
|
|
500
|
+
* path: `src/modules/${ctx.kebab}/commands/${ctx.kebab}.command.ts`,
|
|
501
|
+
* content: `// command for ${ctx.pascal}`,
|
|
502
|
+
* },
|
|
503
|
+
* ],
|
|
504
|
+
* }),
|
|
505
|
+
* ]
|
|
506
|
+
* ```
|
|
507
|
+
*/
|
|
508
|
+
declare function defineGenerator(spec: GeneratorSpec): GeneratorSpec;
|
|
509
|
+
//#endregion
|
|
510
|
+
//#region src/generator-extension/discover.d.ts
|
|
511
|
+
/**
|
|
512
|
+
* One row in the discovered registry. `source` is the npm package name
|
|
513
|
+
* the generator came from — surfaced in error messages so adopters can
|
|
514
|
+
* see which plugin owns a given generator.
|
|
515
|
+
*/
|
|
516
|
+
interface DiscoveredGenerator {
|
|
517
|
+
source: string;
|
|
518
|
+
spec: GeneratorSpec;
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Plugin discovery result, kept around even when no generators were
|
|
522
|
+
* registered so callers can distinguish "no plugins installed" from
|
|
523
|
+
* "no plugins matched the requested name."
|
|
524
|
+
*/
|
|
525
|
+
interface DiscoveryResult {
|
|
526
|
+
generators: DiscoveredGenerator[];
|
|
527
|
+
/** Packages whose `kickjs.generators` was loaded successfully. */
|
|
528
|
+
loaded: string[];
|
|
529
|
+
/**
|
|
530
|
+
* Packages we tried to load but failed — typically a missing entry
|
|
531
|
+
* file or a default export that wasn't an array of GeneratorSpec.
|
|
532
|
+
*/
|
|
533
|
+
failed: Array<{
|
|
534
|
+
source: string;
|
|
535
|
+
reason: string;
|
|
536
|
+
}>;
|
|
537
|
+
}
|
|
538
|
+
//#endregion
|
|
539
|
+
//#region src/generator-extension/context.d.ts
|
|
540
|
+
/**
|
|
541
|
+
* Build a {@link GeneratorContext} from the raw name + invocation
|
|
542
|
+
* arguments. Centralises the case-transformation logic so every plugin
|
|
543
|
+
* generator sees the same shape regardless of how the name was typed
|
|
544
|
+
* on the command line (`Post` vs `post` vs `user_post`).
|
|
545
|
+
*/
|
|
546
|
+
declare function buildGeneratorContext(input: {
|
|
547
|
+
name: string;
|
|
548
|
+
args?: string[];
|
|
549
|
+
flags?: Record<string, string | boolean>;
|
|
550
|
+
modulesDir?: string;
|
|
551
|
+
cwd?: string;
|
|
552
|
+
pluralize?: boolean;
|
|
553
|
+
}): GeneratorContext;
|
|
554
|
+
//#endregion
|
|
312
555
|
//#region src/utils/naming.d.ts
|
|
313
556
|
/** Convert a name to PascalCase */
|
|
314
557
|
declare function toPascalCase(name: string): string;
|
|
@@ -323,5 +566,5 @@ declare function toKebabCase(name: string): string;
|
|
|
323
566
|
*/
|
|
324
567
|
declare function pluralize(name: string): string;
|
|
325
568
|
//#endregion
|
|
326
|
-
export { type KickCommandDefinition, type KickConfig, defineConfig, generateAdapter, generateController, generateDto, generateGuard, generateMiddleware, generateModule, generateService, initProject, loadKickConfig, pluralize, toCamelCase, toKebabCase, toPascalCase };
|
|
569
|
+
export { type DiscoveredGenerator, type DiscoveryResult, type GeneratorArg, type GeneratorContext, type GeneratorFile, type GeneratorFlag, type GeneratorSpec, type KickCommandDefinition, type KickConfig, buildGeneratorContext, defineConfig, defineGenerator, generateAdapter, generateController, generateDto, generateGuard, generateMiddleware, generateModule, generateService, initProject, loadKickConfig, pluralize, toCamelCase, toKebabCase, toPascalCase };
|
|
327
570
|
//# sourceMappingURL=index.d.mts.map
|
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":";;;
|
|
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/generator-extension/define.ts","../src/generator-extension/discover.ts","../src/generator-extension/context.ts","../src/utils/naming.ts"],"mappings":";;;UAKiB,qBAAA;EAAqB;EAEpC,IAAA;EAFoC;EAIpC,WAAA;EAAA;;;;;AAeF;;;EANE,KAAA;EAMwB;EAJxB,OAAA;AAAA;;KAIU,cAAA;;KAGA,cAAA;;KAKA,iBAAA;AAKZ;AAAA,UAAiB,cAAA;EACf,IAAA;AAAA;;KAIU,cAAA,GAAiB,iBAAA,GAAkB,cAAA;;;;;AAS/C;;;KAAY,eAAA;;AAOZ;;;;UAAiB,aAAA;EAcf;;;;AAUF;;EAjBE,GAAA;EAwCiC;;;;;;EAjCjC,IAAA;EA2CO;AAIT;;;;EAzCE,IAAA;AAAA;;UAIe,aAAA;EA6Df;;;;EAxDA,MAAA;EAsEyB;;;;EAjEzB,MAAA;EAqHW;;;;;;;;;;;;EAxGX,eAAA,GAAkB,eAAA;EAwGlB;;;;;;;;;EA9FA,OAAA;AAAA;;UAIe,YAAA;EAoJf;EAlJA,GAAA;EAoJE;;;;;AAOJ;;;;;;;;EA7IE,IAAA,GAAO,cAAA;EA0Ka;EAxKpB,SAAA;;;;;;EAMA,SAAA;EAkKmE;;;;AClSrE;;;;;ED0IE,gBAAA;AAAA;;UAIe,UAAA;EC7IqB;AAOrC;;;;;;;ED+IC,OAAA,GAAU,cAAA;ECxIV;;;;;;;;;;;EDoJA,OAAA,GAAU,YAAA;EC/HwB;;;;;;;;;;;;;;;ED+IlC,cAAA,GAAiB,cAAA;EEzLG;;;;;;;;;;;;ACLyB;EH6M7C,QAAA,GAAW,KAAA;IAAiB,GAAA;IAAa,IAAA;EAAA;EGzMzC;;;;;;;;AAOF;;;EH8ME,KAAA;IG9MgD;;;;;;IHqN9C,MAAA;EAAA;EI9NM;;;;;;;;;;;;;AASV;;;;;;;;EJ4OE,QAAA,GAAW,MAAA,SAAe,aAAA;;;;AKvPmB;;;;;;;;ELmQ7C,OAAA,GAAU,aAAA;EK5PA;EL8PV,QAAA,GAAW,qBAAA;EK7PF;EL+PT,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;;;KClS/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;EDTwB;ECWxB,gBAAA;AAAA;;ADNF;;;;;AAKA;;;;iBCcsB,cAAA,CAAe,OAAA,EAAS,qBAAA,GAAwB,OAAA;;;UC/C5D,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;EACV,SAAA;AAAA;AAAA,iBAGoB,kBAAA,CAAmB,OAAA,EAAS,yBAAA,GAA4B,OAAA;;;UCTpE,oBAAA;EACR,IAAA;EACA,MAAA;EACA,UAAA;EACA,UAAA;EACA,OAAA,GAAU,cAAA;EACV,SAAA;AAAA;AAAA,iBAGoB,aAAA,CAAc,OAAA,EAAS,oBAAA,GAAuB,OAAA;;;UCT1D,sBAAA;EACR,IAAA;EACA,MAAA;EACA,UAAA;EACA,UAAA;EACA,OAAA,GAAU,cAAA;EACV,SAAA;AAAA;AAAA,iBAGoB,eAAA,CAAgB,OAAA,EAAS,sBAAA,GAAyB,OAAA;;;UCT9D,yBAAA;EACR,IAAA;EACA,MAAA;EACA,UAAA;EACA,UAAA;EACA,OAAA,GAAU,cAAA;EACV,SAAA;AAAA;AAAA,iBAGoB,kBAAA,CAAmB,OAAA,EAAS,yBAAA,GAA4B,OAAA;;;UCTpE,kBAAA;EACR,IAAA;EACA,MAAA;EACA,UAAA;EACA,UAAA;EACA,OAAA,GAAU,cAAA;EACV,SAAA;AAAA;AAAA,iBAGoB,WAAA,CAAY,OAAA,EAAS,kBAAA,GAAqB,OAAA;;;KCiB3D,eAAA;AAAA,UAEK,kBAAA;EACR,IAAA;EACA,SAAA;EACA,cAAA;EACA,OAAA;EACA,WAAA;EACA,QAAA,GAAW,eAAA;EACX,WAAA;EACA,QAAA;AAAA;ARlBF;AAAA,iBQsBsB,WAAA,CAAY,OAAA,EAAS,kBAAA,GAAqB,OAAA;;;;ARzChE;;;;;;;;;;AAmBA;;;;;AAGA;;;;;AAKA;;;;;AAKA;;;;;AAKA;;;;;AASA;;;;;AAOA;;USdiB,gBAAA;ETca;ESZ5B,IAAA;ET0BA;ESxBA,MAAA;ET8BI;ES5BJ,KAAA;ETgCe;ES9Bf,KAAA;;EAEA,KAAA;ETiCA;ES/BA,YAAA;ETiDA;ES/CA,WAAA;ETyDA;ESvDA,WAAA;ETuDO;ESrDP,UAAA;ETyD2B;ESvD3B,GAAA;ETuEqB;ESrErB,IAAA;ETqEA;ESnEA,KAAA,EAAO,MAAA;AAAA;;UAIQ,aAAA;ETiFC;;AAIlB;;EShFE,IAAA;ETyFU;ESvFV,OAAA;AAAA;;UAIe,YAAA;EACf,IAAA;EACA,QAAA;EACA,WAAA;AAAA;;UAIe,aAAA;EACf,IAAA;EACA,KAAA;EACA,WAAA;EACA,UAAA;AAAA;;;;;UAOe,aAAA;ET+Hb;;;;ES1HF,IAAA;ET6JU;ES3JV,WAAA;ET6JW;ES3JX,IAAA,YAAgB,YAAA;ET8Jd;ES5JF,KAAA,YAAiB,aAAA;ET8Jf;ES5JF,KAAA,CAAM,GAAA,EAAK,gBAAA,GAAmB,aAAA,KAAkB,OAAA,CAAQ,aAAA;AAAA;;ATkK1D;;;;;;;;;AA6BA;;;;;;;;;;;;AClSA;iBQ6HgB,eAAA,CAAgB,IAAA,EAAM,aAAA,GAAgB,aAAA;;;ATvItD;;;;;AAAA,UUOiB,mBAAA;EACf,MAAA;EACA,IAAA,EAAM,aAAA;AAAA;;AVUR;;;;UUFiB,eAAA;EACf,UAAA,EAAY,mBAAA;EVIY;EUFxB,MAAA;EVEwB;;AAK1B;;EUFE,MAAA,EAAQ,KAAA;IAAQ,MAAA;IAAgB,MAAA;EAAA;AAAA;;;AVzBlC;;;;;;AAAA,iBWWgB,qBAAA,CAAsB,KAAA;EACpC,IAAA;EACA,IAAA;EACA,KAAA,GAAQ,MAAA;EACR,UAAA;EACA,GAAA;EACA,SAAA;AAAA,IACE,gBAAA;;;;iBCpBY,YAAA,CAAa,IAAA;;iBAOb,WAAA,CAAY,IAAA;;iBAMZ,WAAA,CAAY,IAAA;;;;;;iBAYZ,SAAA,CAAU,IAAA"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @forinda/kickjs-cli
|
|
2
|
+
* @forinda/kickjs-cli v4.0.0
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Felix Orinda
|
|
5
5
|
*
|
|
@@ -8,13 +8,13 @@
|
|
|
8
8
|
*
|
|
9
9
|
* @license MIT
|
|
10
10
|
*/
|
|
11
|
-
import { dirname, join, resolve } from "node:path";
|
|
11
|
+
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
12
12
|
import { access, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
13
13
|
import * as clack from "@clack/prompts";
|
|
14
14
|
import pc from "picocolors";
|
|
15
15
|
import pkg from "pluralize";
|
|
16
16
|
import { execSync } from "node:child_process";
|
|
17
|
-
import { readFileSync } from "node:fs";
|
|
17
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
18
18
|
import { fileURLToPath } from "node:url";
|
|
19
19
|
/** Write a file, creating parent directories if needed. Skips writing in dry run mode. */
|
|
20
20
|
async function writeFileSafe(filePath, content) {
|
|
@@ -1543,15 +1543,15 @@ function generateEntryFile(name, template, version, packages = []) {
|
|
|
1543
1543
|
const gqlAdapters = [];
|
|
1544
1544
|
if (packages.includes("devtools")) {
|
|
1545
1545
|
gqlImports.push(`import { DevToolsAdapter } from '@forinda/kickjs-devtools'`);
|
|
1546
|
-
gqlAdapters.push(`
|
|
1546
|
+
gqlAdapters.push(` DevToolsAdapter(),`);
|
|
1547
1547
|
}
|
|
1548
1548
|
if (packages.includes("otel")) {
|
|
1549
1549
|
gqlImports.push(`import { OtelAdapter } from '@forinda/kickjs-otel'`);
|
|
1550
|
-
gqlAdapters.push(`
|
|
1550
|
+
gqlAdapters.push(` OtelAdapter({ serviceName: '${name}' }),`);
|
|
1551
1551
|
}
|
|
1552
1552
|
if (packages.includes("swagger")) {
|
|
1553
1553
|
gqlImports.push(`import { SwaggerAdapter } from '@forinda/kickjs-swagger'`);
|
|
1554
|
-
gqlAdapters.push(`
|
|
1554
|
+
gqlAdapters.push(` SwaggerAdapter({ info: { title: '${name}', version: '${version}' } }),`);
|
|
1555
1555
|
}
|
|
1556
1556
|
return `import 'reflect-metadata'
|
|
1557
1557
|
// Side-effect import — registers the extended env schema with kickjs
|
|
@@ -1584,15 +1584,15 @@ ${gqlAdapters.length ? gqlAdapters.join("\n") + "\n" : ""} new GraphQLAdapter
|
|
|
1584
1584
|
const cqrsAdapters = [];
|
|
1585
1585
|
if (packages.includes("otel")) {
|
|
1586
1586
|
cqrsImports.push(`import { OtelAdapter } from '@forinda/kickjs-otel'`);
|
|
1587
|
-
cqrsAdapters.push(`
|
|
1587
|
+
cqrsAdapters.push(` OtelAdapter({ serviceName: '${name}' }),`);
|
|
1588
1588
|
}
|
|
1589
1589
|
if (packages.includes("devtools")) {
|
|
1590
1590
|
cqrsImports.push(`import { DevToolsAdapter } from '@forinda/kickjs-devtools'`);
|
|
1591
|
-
cqrsAdapters.push(`
|
|
1591
|
+
cqrsAdapters.push(` DevToolsAdapter(),`);
|
|
1592
1592
|
}
|
|
1593
1593
|
if (packages.includes("swagger")) {
|
|
1594
1594
|
cqrsImports.push(`import { SwaggerAdapter } from '@forinda/kickjs-swagger'`);
|
|
1595
|
-
cqrsAdapters.push(`
|
|
1595
|
+
cqrsAdapters.push(` SwaggerAdapter({\n info: { title: '${name}', version: '${version}' },\n }),`);
|
|
1596
1596
|
}
|
|
1597
1597
|
if (packages.includes("graphql")) {
|
|
1598
1598
|
cqrsImports.push(`import { GraphQLAdapter } from '@forinda/kickjs-graphql'`);
|
|
@@ -1611,7 +1611,7 @@ ${cqrsImports.length ? cqrsImports.join("\n") + "\n" : ""}import { modules } fro
|
|
|
1611
1611
|
|
|
1612
1612
|
// Export the app for the Vite plugin (dev mode)
|
|
1613
1613
|
export const app = await bootstrap({
|
|
1614
|
-
modules,${cqrsImports.length ? `\n adapters: [\n${cqrsAdapters.join("\n")}\n // Uncomment for WebSocket support:\n //
|
|
1614
|
+
modules,${cqrsImports.length ? `\n adapters: [\n${cqrsAdapters.join("\n")}\n // Uncomment for WebSocket support:\n // WsAdapter(),\n // Uncomment when Redis is available:\n // QueueAdapter({\n // provider: new BullMQProvider({ host: 'localhost', port: 6379 }),\n // }),\n ],` : `\n adapters: [\n // Uncomment for WebSocket support:\n // WsAdapter(),\n // Uncomment when Redis is available:\n // QueueAdapter({\n // provider: new BullMQProvider({ host: 'localhost', port: 6379 }),\n // }),\n ],`}
|
|
1615
1615
|
})
|
|
1616
1616
|
`;
|
|
1617
1617
|
}
|
|
@@ -1620,15 +1620,15 @@ export const app = await bootstrap({
|
|
|
1620
1620
|
const adapters = [];
|
|
1621
1621
|
if (packages.includes("swagger")) {
|
|
1622
1622
|
imports.push(`import { SwaggerAdapter } from '@forinda/kickjs-swagger'`);
|
|
1623
|
-
adapters.push(`
|
|
1623
|
+
adapters.push(` SwaggerAdapter({ info: { title: '${name}', version: '${version}' } }),`);
|
|
1624
1624
|
}
|
|
1625
1625
|
if (packages.includes("devtools")) {
|
|
1626
1626
|
imports.push(`import { DevToolsAdapter } from '@forinda/kickjs-devtools'`);
|
|
1627
|
-
adapters.push(`
|
|
1627
|
+
adapters.push(` DevToolsAdapter(),`);
|
|
1628
1628
|
}
|
|
1629
1629
|
if (packages.includes("otel")) {
|
|
1630
1630
|
imports.push(`import { OtelAdapter } from '@forinda/kickjs-otel'`);
|
|
1631
|
-
adapters.push(`
|
|
1631
|
+
adapters.push(` OtelAdapter({ serviceName: '${name}' }),`);
|
|
1632
1632
|
}
|
|
1633
1633
|
if (packages.includes("graphql")) {
|
|
1634
1634
|
imports.push(`import { GraphQLAdapter } from '@forinda/kickjs-graphql'`);
|
|
@@ -1652,15 +1652,15 @@ export const app = await bootstrap({ modules${adapters.length ? `,\n adapters:
|
|
|
1652
1652
|
const restAdapters = [];
|
|
1653
1653
|
if (packages.includes("devtools")) {
|
|
1654
1654
|
restImports.push(`import { DevToolsAdapter } from '@forinda/kickjs-devtools'`);
|
|
1655
|
-
restAdapters.push(`
|
|
1655
|
+
restAdapters.push(` DevToolsAdapter(),`);
|
|
1656
1656
|
}
|
|
1657
1657
|
if (packages.includes("swagger")) {
|
|
1658
1658
|
restImports.push(`import { SwaggerAdapter } from '@forinda/kickjs-swagger'`);
|
|
1659
|
-
restAdapters.push(`
|
|
1659
|
+
restAdapters.push(` SwaggerAdapter({\n info: { title: '${name}', version: '${version}' },\n }),`);
|
|
1660
1660
|
}
|
|
1661
1661
|
if (packages.includes("otel")) {
|
|
1662
1662
|
restImports.push(`import { OtelAdapter } from '@forinda/kickjs-otel'`);
|
|
1663
|
-
restAdapters.push(`
|
|
1663
|
+
restAdapters.push(` OtelAdapter({ serviceName: '${name}' }),`);
|
|
1664
1664
|
}
|
|
1665
1665
|
return `import 'reflect-metadata'
|
|
1666
1666
|
// Side-effect import — registers the extended env schema with kickjs
|
|
@@ -3038,7 +3038,7 @@ generated \`KickRoutes\` namespace (refreshed on \`kick dev\` and \`kick typegen
|
|
|
3038
3038
|
\`\`\`ts
|
|
3039
3039
|
import { Controller, Get, Post, type Ctx } from '@forinda/kickjs'
|
|
3040
3040
|
|
|
3041
|
-
@Controller(
|
|
3041
|
+
@Controller()
|
|
3042
3042
|
export class UserController {
|
|
3043
3043
|
@Get('/')
|
|
3044
3044
|
async findAll(ctx: Ctx<KickRoutes.UserController['findAll']>) {
|
|
@@ -3319,7 +3319,7 @@ Run tests:
|
|
|
3319
3319
|
## Decorators Reference
|
|
3320
3320
|
|
|
3321
3321
|
### Route Decorators
|
|
3322
|
-
- \`@Controller(
|
|
3322
|
+
- \`@Controller()\` — mark a class as an HTTP controller (path comes from \`routes().path\`)
|
|
3323
3323
|
- \`@Get('/'), @Post('/'), @Put('/'), @Delete('/'), @Patch('/')\` — HTTP methods
|
|
3324
3324
|
- \`@Middleware(fn)\` — attach middleware
|
|
3325
3325
|
- \`@Public()\` — skip authentication (requires @forinda/kickjs-auth)
|
|
@@ -3466,7 +3466,7 @@ Then:
|
|
|
3466
3466
|
If not using generators:
|
|
3467
3467
|
|
|
3468
3468
|
- [ ] Create \`src/modules/<name>/<name>.controller.ts\`
|
|
3469
|
-
- [ ] Add \`@Controller(
|
|
3469
|
+
- [ ] Add \`@Controller()\` decorator
|
|
3470
3470
|
- [ ] Add route handlers with \`@Get()\`, \`@Post()\`, etc.
|
|
3471
3471
|
- [ ] Create module file implementing \`AppModule\` with \`routes()\` returning \`{ path, router: buildRoutes(Controller), controller }\`
|
|
3472
3472
|
- [ ] Register module in \`src/modules/index.ts\` (\`AppModuleClass[]\` array)
|
|
@@ -3528,8 +3528,8 @@ import { AuthAdapter, JwtStrategy } from '@forinda/kickjs-auth'
|
|
|
3528
3528
|
bootstrap({
|
|
3529
3529
|
modules,
|
|
3530
3530
|
adapters: [
|
|
3531
|
-
|
|
3532
|
-
strategies: [
|
|
3531
|
+
AuthAdapter({
|
|
3532
|
+
strategies: [JwtStrategy({ secret: process.env.JWT_SECRET! })],
|
|
3533
3533
|
}),
|
|
3534
3534
|
],
|
|
3535
3535
|
})
|
|
@@ -3559,7 +3559,7 @@ import { WsAdapter } from '@forinda/kickjs-ws'
|
|
|
3559
3559
|
|
|
3560
3560
|
bootstrap({
|
|
3561
3561
|
modules,
|
|
3562
|
-
adapters: [
|
|
3562
|
+
adapters: [WsAdapter()],
|
|
3563
3563
|
})
|
|
3564
3564
|
\`\`\`
|
|
3565
3565
|
|
|
@@ -3663,7 +3663,7 @@ These work anywhere — scripts, plain files, outside \`@Service\`/\`@Controller
|
|
|
3663
3663
|
### HTTP Routes
|
|
3664
3664
|
| Decorator | Purpose |
|
|
3665
3665
|
|-----------|---------|
|
|
3666
|
-
| \`@Controller(
|
|
3666
|
+
| \`@Controller()\` | Define route prefix |
|
|
3667
3667
|
| \`@Get('/'), @Post('/')\` | HTTP method handlers |
|
|
3668
3668
|
| \`@Middleware(fn)\` | Attach middleware |
|
|
3669
3669
|
| \`@Public()\` | Skip auth (requires auth adapter) |
|
|
@@ -3804,7 +3804,7 @@ async function initProject(options) {
|
|
|
3804
3804
|
}
|
|
3805
3805
|
}
|
|
3806
3806
|
try {
|
|
3807
|
-
const { runTypegen } = await import("./typegen-
|
|
3807
|
+
const { runTypegen } = await import("./typegen-vI1eqGLK.mjs");
|
|
3808
3808
|
await runTypegen({
|
|
3809
3809
|
cwd: dir,
|
|
3810
3810
|
allowDuplicates: true,
|
|
@@ -3902,7 +3902,10 @@ async function loadKickConfig(cwd) {
|
|
|
3902
3902
|
try {
|
|
3903
3903
|
const { pathToFileURL } = await import("node:url");
|
|
3904
3904
|
const mod = await import(pathToFileURL(filepath).href);
|
|
3905
|
-
|
|
3905
|
+
const config = mod.default ?? mod;
|
|
3906
|
+
const warnings = validateAssetMap(config, cwd);
|
|
3907
|
+
for (const warning of warnings) console.warn(` Warning: ${warning}`);
|
|
3908
|
+
return config;
|
|
3906
3909
|
} catch (err) {
|
|
3907
3910
|
if (filename.endsWith(".ts")) console.warn(`Warning: Failed to load ${filename}. TypeScript config files require a runtime loader (e.g. tsx, ts-node) or use kick.config.js/.mjs instead.`);
|
|
3908
3911
|
continue;
|
|
@@ -3910,7 +3913,126 @@ async function loadKickConfig(cwd) {
|
|
|
3910
3913
|
}
|
|
3911
3914
|
return null;
|
|
3912
3915
|
}
|
|
3916
|
+
/**
|
|
3917
|
+
* Validate `assetMap` entries on a loaded config. Returns a list of
|
|
3918
|
+
* human-readable warnings; the caller decides how to surface them
|
|
3919
|
+
* (typically `console.warn`). Never throws — `kick g` and other
|
|
3920
|
+
* unrelated commands should keep working even when the assetMap is
|
|
3921
|
+
* misconfigured.
|
|
3922
|
+
*
|
|
3923
|
+
* Checks:
|
|
3924
|
+
*
|
|
3925
|
+
* - Each entry's `src` is a non-empty string.
|
|
3926
|
+
* - The `src` directory exists on disk (otherwise the typegen + build
|
|
3927
|
+
* steps will fail later with cryptic errors).
|
|
3928
|
+
* - `dest` doesn't escape the project root (defensive — a `dest:
|
|
3929
|
+
* '../../etc'` typo could write files outside the workspace).
|
|
3930
|
+
* - The namespace key is a non-empty string and doesn't include a
|
|
3931
|
+
* `/` (would conflict with the `<namespace>/<key>` manifest format).
|
|
3932
|
+
*/
|
|
3933
|
+
function validateAssetMap(config, cwd) {
|
|
3934
|
+
const warnings = [];
|
|
3935
|
+
if (!config?.assetMap) return warnings;
|
|
3936
|
+
const root = resolve(cwd);
|
|
3937
|
+
for (const [namespace, entry] of Object.entries(config.assetMap)) {
|
|
3938
|
+
if (!namespace || namespace.includes("/")) {
|
|
3939
|
+
warnings.push(`assetMap key '${namespace}' is invalid — must be a non-empty string without '/'`);
|
|
3940
|
+
continue;
|
|
3941
|
+
}
|
|
3942
|
+
if (typeof entry?.src !== "string" || entry.src.length === 0) {
|
|
3943
|
+
warnings.push(`assetMap.${namespace} is missing a non-empty 'src' field`);
|
|
3944
|
+
continue;
|
|
3945
|
+
}
|
|
3946
|
+
if (!existsSync(resolve(cwd, entry.src))) warnings.push(`assetMap.${namespace}.src ('${entry.src}') does not exist — typegen + build will fail`);
|
|
3947
|
+
if (entry.dest) {
|
|
3948
|
+
if (escapesRoot(resolve(cwd, entry.dest), root)) warnings.push(`assetMap.${namespace}.dest ('${entry.dest}') resolves outside the project root — refusing to copy`);
|
|
3949
|
+
}
|
|
3950
|
+
}
|
|
3951
|
+
return warnings;
|
|
3952
|
+
}
|
|
3953
|
+
/**
|
|
3954
|
+
* Returns true when `path` (absolute) resolves outside of `root`
|
|
3955
|
+
* (also absolute). Uses `path.relative` for accuracy:
|
|
3956
|
+
*
|
|
3957
|
+
* - The result is empty when paths are identical (inside).
|
|
3958
|
+
* - It starts with `..` when the path traverses outside the root.
|
|
3959
|
+
* - It's absolute (Windows: cross-drive) when there's no relative
|
|
3960
|
+
* path between them.
|
|
3961
|
+
*
|
|
3962
|
+
* Avoids the prefix-match pitfalls of `startsWith` (e.g. `/app`
|
|
3963
|
+
* matching `/app2/...`, or case-mismatches on macOS / Windows).
|
|
3964
|
+
*/
|
|
3965
|
+
function escapesRoot(path, root) {
|
|
3966
|
+
const rel = relative(root, path);
|
|
3967
|
+
return rel === "" ? false : rel.startsWith("..") || isAbsolute(rel);
|
|
3968
|
+
}
|
|
3969
|
+
//#endregion
|
|
3970
|
+
//#region src/generator-extension/define.ts
|
|
3971
|
+
/**
|
|
3972
|
+
* Identity factory — returns the spec verbatim. Exists for type
|
|
3973
|
+
* inference and forward-compatibility (future fields can be added with
|
|
3974
|
+
* defaults).
|
|
3975
|
+
*
|
|
3976
|
+
* @example
|
|
3977
|
+
* ```ts
|
|
3978
|
+
* import { defineGenerator } from '@forinda/kickjs-cli'
|
|
3979
|
+
*
|
|
3980
|
+
* export default [
|
|
3981
|
+
* defineGenerator({
|
|
3982
|
+
* name: 'command',
|
|
3983
|
+
* description: 'Generate a CQRS command + handler',
|
|
3984
|
+
* files: (ctx) => [
|
|
3985
|
+
* {
|
|
3986
|
+
* path: `src/modules/${ctx.kebab}/commands/${ctx.kebab}.command.ts`,
|
|
3987
|
+
* content: `// command for ${ctx.pascal}`,
|
|
3988
|
+
* },
|
|
3989
|
+
* ],
|
|
3990
|
+
* }),
|
|
3991
|
+
* ]
|
|
3992
|
+
* ```
|
|
3993
|
+
*/
|
|
3994
|
+
function defineGenerator(spec) {
|
|
3995
|
+
return spec;
|
|
3996
|
+
}
|
|
3997
|
+
//#endregion
|
|
3998
|
+
//#region src/generator-extension/context.ts
|
|
3999
|
+
/** Convert any string to snake_case (`UserPost` / `user-post` → `user_post`). */
|
|
4000
|
+
function toSnakeCase(name) {
|
|
4001
|
+
return toKebabCase(name).replace(/-/g, "_");
|
|
4002
|
+
}
|
|
4003
|
+
/**
|
|
4004
|
+
* Build a {@link GeneratorContext} from the raw name + invocation
|
|
4005
|
+
* arguments. Centralises the case-transformation logic so every plugin
|
|
4006
|
+
* generator sees the same shape regardless of how the name was typed
|
|
4007
|
+
* on the command line (`Post` vs `post` vs `user_post`).
|
|
4008
|
+
*/
|
|
4009
|
+
function buildGeneratorContext(input) {
|
|
4010
|
+
const cwd = input.cwd ?? process.cwd();
|
|
4011
|
+
const usePlural = input.pluralize ?? true;
|
|
4012
|
+
const pascal = toPascalCase(input.name);
|
|
4013
|
+
const camel = toCamelCase(input.name);
|
|
4014
|
+
const kebab = toKebabCase(input.name);
|
|
4015
|
+
const snake = toSnakeCase(input.name);
|
|
4016
|
+
const ctx = {
|
|
4017
|
+
name: input.name,
|
|
4018
|
+
pascal,
|
|
4019
|
+
camel,
|
|
4020
|
+
kebab,
|
|
4021
|
+
snake,
|
|
4022
|
+
modulesDir: input.modulesDir ?? "src/modules",
|
|
4023
|
+
cwd,
|
|
4024
|
+
args: input.args ?? [],
|
|
4025
|
+
flags: input.flags ?? {}
|
|
4026
|
+
};
|
|
4027
|
+
if (usePlural) {
|
|
4028
|
+
const pluralKebab = pluralize(kebab);
|
|
4029
|
+
ctx.pluralKebab = pluralKebab;
|
|
4030
|
+
ctx.pluralPascal = toPascalCase(pluralKebab);
|
|
4031
|
+
ctx.pluralCamel = toCamelCase(pluralKebab);
|
|
4032
|
+
}
|
|
4033
|
+
return ctx;
|
|
4034
|
+
}
|
|
3913
4035
|
//#endregion
|
|
3914
|
-
export { defineConfig, generateAdapter, generateController, generateDto, generateGuard, generateMiddleware, generateModule, generateService, initProject, loadKickConfig, pluralize, toCamelCase, toKebabCase, toPascalCase };
|
|
4036
|
+
export { buildGeneratorContext, defineConfig, defineGenerator, generateAdapter, generateController, generateDto, generateGuard, generateMiddleware, generateModule, generateService, initProject, loadKickConfig, pluralize, toCamelCase, toKebabCase, toPascalCase };
|
|
3915
4037
|
|
|
3916
4038
|
//# sourceMappingURL=index.mjs.map
|