@kanjijs/cli 0.2.0-beta.15 → 0.2.0-beta.16
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/core/src/di/module-compiler.d.ts +1 -0
- package/dist/index.js +171 -43
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -14418,7 +14418,8 @@ class Container {
|
|
|
14418
14418
|
resolve(token) {
|
|
14419
14419
|
const provider = this.providers.get(token);
|
|
14420
14420
|
if (!provider) {
|
|
14421
|
-
|
|
14421
|
+
const tokenName = typeof token === "function" ? token.name ?? "anonymous" : String(token);
|
|
14422
|
+
throw new Error(`[DI] Provider not found for token: ${tokenName}`);
|
|
14422
14423
|
}
|
|
14423
14424
|
if (provider.instance) {
|
|
14424
14425
|
return provider.instance;
|
|
@@ -14468,12 +14469,14 @@ var init_container = __esm(() => {
|
|
|
14468
14469
|
KanjijsIoC.providers.set(target, provider);
|
|
14469
14470
|
}
|
|
14470
14471
|
if (!provider) {
|
|
14471
|
-
|
|
14472
|
+
const targetName2 = typeof target === "function" ? target.name ?? "anonymous" : String(target);
|
|
14473
|
+
throw new Error(`Provider not found for token: ${targetName2}`);
|
|
14472
14474
|
}
|
|
14473
14475
|
if (provider.instance) {
|
|
14474
14476
|
return provider.instance;
|
|
14475
14477
|
}
|
|
14476
|
-
|
|
14478
|
+
const targetName = typeof target === "function" ? target.name ?? "anonymous" : String(target);
|
|
14479
|
+
console.log(`[DI] Creating NEW instance for ${targetName}`);
|
|
14477
14480
|
if (provider.useValue !== undefined) {
|
|
14478
14481
|
provider.instance = provider.useValue;
|
|
14479
14482
|
} else if (provider.useClass) {
|
|
@@ -14503,12 +14506,11 @@ class ModuleCompiler {
|
|
|
14503
14506
|
this.scan(rootModule);
|
|
14504
14507
|
this.validate();
|
|
14505
14508
|
const container = new Container;
|
|
14506
|
-
|
|
14507
|
-
|
|
14508
|
-
|
|
14509
|
-
container.register(token, definition);
|
|
14510
|
-
}
|
|
14509
|
+
const rootNode = this.nodes.get(rootModule);
|
|
14510
|
+
if (!rootNode) {
|
|
14511
|
+
return container;
|
|
14511
14512
|
}
|
|
14513
|
+
this.registerProviders(rootNode, container, new Set);
|
|
14512
14514
|
return container;
|
|
14513
14515
|
}
|
|
14514
14516
|
scan(target) {
|
|
@@ -14595,16 +14597,28 @@ class ModuleCompiler {
|
|
|
14595
14597
|
return overrideToken || paramType;
|
|
14596
14598
|
});
|
|
14597
14599
|
} else if ("useFactory" in provider) {
|
|
14598
|
-
targetName = typeof provider.provide === "function" ? provider.provide.name : String(provider.provide);
|
|
14600
|
+
targetName = typeof provider.provide === "function" ? provider.provide.name ?? "anonymous" : String(provider.provide);
|
|
14599
14601
|
dependencies = provider.inject || [];
|
|
14600
14602
|
}
|
|
14601
14603
|
for (const dep of dependencies) {
|
|
14602
14604
|
if (!visibleTokens.has(dep)) {
|
|
14603
|
-
const depName = typeof dep === "function" ? dep.name : String(dep);
|
|
14605
|
+
const depName = typeof dep === "function" ? dep.name ?? "anonymous" : String(dep);
|
|
14604
14606
|
throw new Error(`[Kanjijs] strict-di-error: Provider '${targetName}' in Module '${moduleName}' ` + `depends on '${depName}', but it is not visible. ` + `Make sure it is imported and exported by the source module.`);
|
|
14605
14607
|
}
|
|
14606
14608
|
}
|
|
14607
14609
|
}
|
|
14610
|
+
registerProviders(node, container, visited) {
|
|
14611
|
+
if (visited.has(node))
|
|
14612
|
+
return;
|
|
14613
|
+
visited.add(node);
|
|
14614
|
+
for (const imp of node.imports) {
|
|
14615
|
+
this.registerProviders(imp, container, visited);
|
|
14616
|
+
}
|
|
14617
|
+
for (const [token, provider] of node.providers) {
|
|
14618
|
+
const { provide: _provide, ...definition } = provider;
|
|
14619
|
+
container.register(token, definition);
|
|
14620
|
+
}
|
|
14621
|
+
}
|
|
14608
14622
|
}
|
|
14609
14623
|
var init_module_compiler = __esm(() => {
|
|
14610
14624
|
init_metadata();
|
|
@@ -19990,7 +20004,7 @@ var import_picocolors2 = __toESM(require_picocolors(), 1);
|
|
|
19990
20004
|
import path2 from "path";
|
|
19991
20005
|
class GenerateCommand extends Command2 {
|
|
19992
20006
|
load(program2) {
|
|
19993
|
-
program2.command("g <type> <name>").description("Generate a new resource (e.g. 'kanjijs g resource users')").alias("generate").action(async (type, name) => {
|
|
20007
|
+
program2.command("g <type> <name>").description("Generate a new resource (e.g. 'kanjijs g resource users')").alias("generate").option("-r, --repository", "Generate with Repository pattern").action(async (type, name, options) => {
|
|
19994
20008
|
if (type !== "resource") {
|
|
19995
20009
|
console.error(import_picocolors2.default.red(`Unknown type: ${type}. Only 'resource' is supported.`));
|
|
19996
20010
|
process.exit(1);
|
|
@@ -19998,22 +20012,133 @@ class GenerateCommand extends Command2 {
|
|
|
19998
20012
|
const capitalize = (s) => s.charAt(0).toUpperCase() + s.slice(1);
|
|
19999
20013
|
const className = capitalize(name);
|
|
20000
20014
|
const lowerName = name.toLowerCase();
|
|
20001
|
-
const
|
|
20015
|
+
const modulesDir = path2.join(process.cwd(), "src", "modules");
|
|
20016
|
+
const targetDir = path2.join(modulesDir, lowerName);
|
|
20002
20017
|
await import_fs_extra.default.ensureDir(targetDir);
|
|
20003
|
-
|
|
20018
|
+
try {
|
|
20019
|
+
let repositoryClass = "";
|
|
20020
|
+
let serviceImports = "";
|
|
20021
|
+
let serviceConstructor = `constructor(private readonly logger: Logger) {}`;
|
|
20022
|
+
let serviceMethods = ` findAll() {
|
|
20023
|
+
this.logger.info("Finding all ${lowerName}");
|
|
20024
|
+
return "This action returns all ${lowerName}";
|
|
20025
|
+
}`;
|
|
20026
|
+
if (options.repository) {
|
|
20027
|
+
const commonDir = path2.join(process.cwd(), "src", "common");
|
|
20028
|
+
const baseRepoPath = path2.join(commonDir, "base.repository.ts");
|
|
20029
|
+
if (!await import_fs_extra.default.pathExists(baseRepoPath)) {
|
|
20030
|
+
await import_fs_extra.default.ensureDir(commonDir);
|
|
20031
|
+
const baseRepoTpl = `import { type DrizzleDb } from "@kanjijs/store";
|
|
20032
|
+
import { eq } from "drizzle-orm";
|
|
20033
|
+
import { type PgTableWithColumns } from "drizzle-orm/pg-core";
|
|
20034
|
+
|
|
20035
|
+
/**
|
|
20036
|
+
* Base Repository for generic CRUD operations.
|
|
20037
|
+
* Table-First: Infers types from Drizzle schema.
|
|
20038
|
+
*/
|
|
20039
|
+
export abstract class BaseRepository<
|
|
20040
|
+
TTable extends PgTableWithColumns<any>,
|
|
20041
|
+
TEntity = TTable["$inferSelect"],
|
|
20042
|
+
TCreate = TTable["$inferInsert"],
|
|
20043
|
+
TUpdate = Partial<TTable["$inferInsert"]>
|
|
20044
|
+
> {
|
|
20045
|
+
constructor(
|
|
20046
|
+
protected readonly db: DrizzleDb,
|
|
20047
|
+
protected readonly table: TTable,
|
|
20048
|
+
protected readonly idColumn: any
|
|
20049
|
+
) {}
|
|
20050
|
+
|
|
20051
|
+
async findAll(): Promise<TEntity[]> {
|
|
20052
|
+
return this.db.select().from(this.table) as unknown as Promise<TEntity[]>;
|
|
20053
|
+
}
|
|
20054
|
+
|
|
20055
|
+
async findById(id: number | string): Promise<TEntity | undefined> {
|
|
20056
|
+
const result = await this.db
|
|
20057
|
+
.select()
|
|
20058
|
+
.from(this.table)
|
|
20059
|
+
.where(eq(this.idColumn, id));
|
|
20060
|
+
|
|
20061
|
+
// @ts-ignore
|
|
20062
|
+
return result[0];
|
|
20063
|
+
}
|
|
20064
|
+
|
|
20065
|
+
async create(data: TCreate): Promise<TEntity> {
|
|
20066
|
+
// @ts-ignore
|
|
20067
|
+
const result = await this.db.insert(this.table).values(data).returning();
|
|
20068
|
+
// @ts-ignore
|
|
20069
|
+
return result[0];
|
|
20070
|
+
}
|
|
20071
|
+
|
|
20072
|
+
async update(id: number | string, data: TUpdate): Promise<TEntity | undefined> {
|
|
20073
|
+
const result = await this.db
|
|
20074
|
+
.update(this.table)
|
|
20075
|
+
// @ts-ignore
|
|
20076
|
+
.set(data)
|
|
20077
|
+
.where(eq(this.idColumn, id))
|
|
20078
|
+
.returning();
|
|
20079
|
+
|
|
20080
|
+
// @ts-ignore
|
|
20081
|
+
return result[0];
|
|
20082
|
+
}
|
|
20083
|
+
|
|
20084
|
+
async delete(id: number | string): Promise<TEntity | undefined> {
|
|
20085
|
+
const result = await this.db
|
|
20086
|
+
.delete(this.table)
|
|
20087
|
+
.where(eq(this.idColumn, id))
|
|
20088
|
+
.returning();
|
|
20089
|
+
|
|
20090
|
+
// @ts-ignore
|
|
20091
|
+
return result[0];
|
|
20092
|
+
}
|
|
20093
|
+
}
|
|
20094
|
+
`;
|
|
20095
|
+
await import_fs_extra.default.writeFile(baseRepoPath, baseRepoTpl);
|
|
20096
|
+
console.log(import_picocolors2.default.green(`CREATED src/common/base.repository.ts (First time setup)`));
|
|
20097
|
+
}
|
|
20098
|
+
repositoryClass = `${className}Repository`;
|
|
20099
|
+
const repoPath = path2.join(targetDir, `${lowerName}.repository.ts`);
|
|
20100
|
+
const repoTpl = `import { Injectable, Inject } from "@kanjijs/core";
|
|
20101
|
+
import { DATABASE_CLIENT, type DrizzleDb } from "@kanjijs/store";
|
|
20102
|
+
import { BaseRepository } from "../../common/base.repository";
|
|
20103
|
+
// import { ${lowerName} } from "../../db/schema"; // TODO: Import your Drizzle table
|
|
20104
|
+
|
|
20105
|
+
@Injectable()
|
|
20106
|
+
export class ${repositoryClass} extends BaseRepository<any> { // TODO: Replace 'any' with 'typeof ${lowerName}'
|
|
20107
|
+
constructor(@Inject(DATABASE_CLIENT) db: DrizzleDb) {
|
|
20108
|
+
super(db, {} as any, "id"); // TODO: Pass real table and ID column
|
|
20109
|
+
}
|
|
20110
|
+
}
|
|
20111
|
+
`;
|
|
20112
|
+
await import_fs_extra.default.writeFile(repoPath, repoTpl);
|
|
20113
|
+
console.log(import_picocolors2.default.white(`CREATE ${lowerName}.repository.ts`));
|
|
20114
|
+
serviceImports = `import { ${repositoryClass} } from "./${lowerName}.repository";`;
|
|
20115
|
+
serviceConstructor = `constructor(
|
|
20116
|
+
private readonly logger: Logger,
|
|
20117
|
+
private readonly repo: ${repositoryClass}
|
|
20118
|
+
) {}`;
|
|
20119
|
+
serviceMethods = ` async findAll() {
|
|
20120
|
+
this.logger.info("Finding all ${lowerName}");
|
|
20121
|
+
return this.repo.findAll();
|
|
20122
|
+
}
|
|
20123
|
+
|
|
20124
|
+
async create(data: any) {
|
|
20125
|
+
return this.repo.create(data);
|
|
20126
|
+
}`;
|
|
20127
|
+
}
|
|
20128
|
+
const serviceTpl = `import { Injectable } from "@kanjijs/core";
|
|
20004
20129
|
import { Logger } from "@kanjijs/logger";
|
|
20130
|
+
${serviceImports}
|
|
20005
20131
|
|
|
20006
20132
|
@Injectable()
|
|
20007
20133
|
export class ${className}Service {
|
|
20008
|
-
|
|
20134
|
+
${serviceConstructor}
|
|
20009
20135
|
|
|
20010
|
-
|
|
20011
|
-
this.logger.info("Finding all ${lowerName}");
|
|
20012
|
-
return "This action returns all ${lowerName}";
|
|
20013
|
-
}
|
|
20136
|
+
${serviceMethods}
|
|
20014
20137
|
}
|
|
20015
20138
|
`;
|
|
20016
|
-
|
|
20139
|
+
await import_fs_extra.default.writeFile(path2.join(targetDir, `${lowerName}.service.ts`), serviceTpl);
|
|
20140
|
+
console.log(import_picocolors2.default.white(`CREATE ${lowerName}.service.ts`));
|
|
20141
|
+
const controllerTpl = `import { Controller, Get, Post, Body } from "@kanjijs/core";
|
|
20017
20142
|
import { type Context } from "hono";
|
|
20018
20143
|
import { ${className}Service } from "./${lowerName}.service";
|
|
20019
20144
|
import { Contract } from "@kanjijs/contracts";
|
|
@@ -20035,46 +20160,49 @@ export class ${className}Controller {
|
|
|
20035
20160
|
}
|
|
20036
20161
|
|
|
20037
20162
|
@Post("/", { contract: Create${className}Contract })
|
|
20038
|
-
create(
|
|
20039
|
-
|
|
20163
|
+
create(c: Context) {
|
|
20164
|
+
// Infer keys from contract (Contract-first)
|
|
20165
|
+
const body = c.get("kanji.validated.body");
|
|
20166
|
+
return c.json(this.service.create(body));
|
|
20040
20167
|
}
|
|
20041
20168
|
}
|
|
20042
20169
|
`;
|
|
20043
|
-
|
|
20044
|
-
|
|
20045
|
-
|
|
20170
|
+
await import_fs_extra.default.writeFile(path2.join(targetDir, `${lowerName}.controller.ts`), controllerTpl);
|
|
20171
|
+
console.log(import_picocolors2.default.white(`CREATE ${lowerName}.controller.ts`));
|
|
20172
|
+
let moduleProviders = `[${className}Service]`;
|
|
20173
|
+
let moduleImports = `import { ${className}Controller } from "./${lowerName}.controller";
|
|
20174
|
+
import { ${className}Service } from "./${lowerName}.service";`;
|
|
20175
|
+
if (options.repository) {
|
|
20176
|
+
moduleProviders = `[${className}Service, ${repositoryClass}]`;
|
|
20177
|
+
moduleImports += `
|
|
20178
|
+
import { ${repositoryClass} } from "./${lowerName}.repository";`;
|
|
20179
|
+
}
|
|
20180
|
+
const moduleTpl = `import { Module } from "@kanjijs/core";
|
|
20046
20181
|
import { LoggerModule } from "@kanjijs/logger";
|
|
20182
|
+
${moduleImports}
|
|
20047
20183
|
|
|
20048
20184
|
@Module({
|
|
20049
20185
|
imports: [LoggerModule],
|
|
20050
20186
|
controllers: [${className}Controller],
|
|
20051
|
-
providers:
|
|
20187
|
+
providers: ${moduleProviders}
|
|
20052
20188
|
})
|
|
20053
20189
|
export class ${className}Module {}
|
|
20054
20190
|
`;
|
|
20055
|
-
try {
|
|
20056
|
-
await import_fs_extra.default.writeFile(path2.join(targetDir, `${lowerName}.service.ts`), serviceTpl);
|
|
20057
|
-
await import_fs_extra.default.writeFile(path2.join(targetDir, `${lowerName}.controller.ts`), controllerTpl);
|
|
20058
20191
|
await import_fs_extra.default.writeFile(path2.join(targetDir, `${lowerName}.module.ts`), moduleTpl);
|
|
20059
|
-
console.log(import_picocolors2.default.green(`
|
|
20060
|
-
Generated resource ${lowerName} in src/modules/${lowerName}`));
|
|
20061
|
-
console.log(import_picocolors2.default.white(`CREATE ${lowerName}.service.ts`));
|
|
20062
|
-
console.log(import_picocolors2.default.white(`CREATE ${lowerName}.controller.ts`));
|
|
20063
20192
|
console.log(import_picocolors2.default.white(`CREATE ${lowerName}.module.ts`));
|
|
20064
20193
|
const appModulePath = path2.join(process.cwd(), "src", "app.module.ts");
|
|
20065
20194
|
if (await import_fs_extra.default.pathExists(appModulePath)) {
|
|
20066
20195
|
let content = await import_fs_extra.default.readFile(appModulePath, "utf-8");
|
|
20067
20196
|
const moduleName = `${className}Module`;
|
|
20068
|
-
|
|
20069
|
-
|
|
20070
|
-
|
|
20071
|
-
|
|
20072
|
-
|
|
20197
|
+
if (!content.includes(moduleName)) {
|
|
20198
|
+
const importStatement = `import { ${className}Module } from "./modules/${lowerName}/${lowerName}.module";`;
|
|
20199
|
+
if (!content.includes(importStatement)) {
|
|
20200
|
+
const lastImportIndex = content.lastIndexOf("import ");
|
|
20201
|
+
const endOfLastImport = content.indexOf(`
|
|
20073
20202
|
`, lastImportIndex) + 1;
|
|
20074
|
-
|
|
20075
|
-
|
|
20076
|
-
|
|
20077
|
-
if (!alreadyIncluded) {
|
|
20203
|
+
content = `${content.slice(0, endOfLastImport)}${importStatement}
|
|
20204
|
+
${content.slice(endOfLastImport)}`;
|
|
20205
|
+
}
|
|
20078
20206
|
if (content.includes("imports: [")) {
|
|
20079
20207
|
content = content.replace("imports: [", `imports: [${moduleName}, `);
|
|
20080
20208
|
} else {
|
|
@@ -20086,9 +20214,9 @@ Generated resource ${lowerName} in src/modules/${lowerName}`));
|
|
|
20086
20214
|
} else {
|
|
20087
20215
|
console.log(import_picocolors2.default.dim(`WARN: ${moduleName} already in AppModule`));
|
|
20088
20216
|
}
|
|
20089
|
-
} else {
|
|
20090
|
-
console.error(import_picocolors2.default.yellow(`WARN: AppModule not found at ${appModulePath}`));
|
|
20091
20217
|
}
|
|
20218
|
+
console.log(import_picocolors2.default.green(`
|
|
20219
|
+
Success! Generated resource ${lowerName}.`));
|
|
20092
20220
|
} catch (err) {
|
|
20093
20221
|
console.error(import_picocolors2.default.red("Error generating resource:"), err);
|
|
20094
20222
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kanjijs/cli",
|
|
3
|
-
"version": "0.2.0-beta.
|
|
3
|
+
"version": "0.2.0-beta.16",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"kanji": "./dist/index.js"
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"fs-extra": "^11.3.3",
|
|
22
22
|
"picocolors": "^1.1.1",
|
|
23
23
|
"openapi-typescript-codegen": "^0.25.0",
|
|
24
|
-
"@kanjijs/openapi": "^0.2.0-beta.
|
|
24
|
+
"@kanjijs/openapi": "^0.2.0-beta.16"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"@types/fs-extra": "^11.0.4"
|