Skip to content

Commit bc6d38e

Browse files
committed
refactor: simplify command parameters and improve configuration handling in project and export commands
1 parent 5ea0cf3 commit bc6d38e

16 files changed

+75
-76
lines changed

.changeset/free-wombats-train.md

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
"@codefast-ui/progress-circle": patch
3+
"@codefast-ui/checkbox-group": patch
4+
"@codefast-ui/input-number": patch
5+
"@codefast/typescript-config": patch
6+
"@codefast-ui/input": patch
7+
"@codefast/eslint-config": patch
8+
"@codefast/style-guide": patch
9+
"@codefast/hooks": patch
10+
"@codefast/cli": patch
11+
"@codefast/ui": patch
12+
---
13+
14+
refactor: simplify command parameters and improve configuration handling in project and export commands

commitlint.config.mjs

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ const config = {
22
extends: ["@commitlint/config-conventional"],
33
rules: {
44
"body-max-line-length": [2, "always", 350],
5+
"header-max-length": [2, "always", 150],
56
},
67
};
78

packages/cli/src/application/commands/create-project.command.ts

+3-4
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { inject, injectable } from "inversify";
33
import process from "node:process";
44

55
import type { CreateProjectUseCase } from "@/application/use-cases/create-project.use-case";
6-
import type { ConfigGroups } from "@/domain/entities/config-file";
76

87
import { TYPES } from "@/ioc/types";
98

@@ -13,16 +12,16 @@ export class CreateProjectCommand {
1312

1413
/**
1514
* Returns a Commander.js command for creating or configuring a project.
16-
* @param configGroups - Configuration groups for project files.
15+
*
1716
* @returns Configured Commander.js command.
1817
*/
19-
getCommand(configGroups: ConfigGroups): Command {
18+
getCommand(): Command {
2019
return new Command()
2120
.name("create-project")
2221
.description("Create a new Next.js project with TypeScript, TailwindCSS, and linting setup")
2322
.argument("[project-name]", "Name of the project to create")
2423
.action(async (projectName: string) => {
25-
await this.createProjectUseCase.execute(projectName, configGroups);
24+
await this.createProjectUseCase.execute(projectName);
2625

2726
process.exit(0);
2827
});

packages/cli/src/application/use-cases/create-project.use-case.ts

+7-4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type { Project } from "@/domain/entities/project";
88
import type { ProjectRepositoryInterface } from "@/domain/interfaces/project.repository";
99

1010
import { handleError } from "@/application/utilities/error-handler";
11+
import { ConfigService } from "@/infrastructure/services/config.service";
1112
import { TYPES } from "@/ioc/types";
1213

1314
@injectable()
@@ -17,18 +18,20 @@ export class CreateProjectUseCase {
1718
@inject(TYPES.FileSystemPort) private fileSystemPort: FileSystemPort,
1819
@inject(TYPES.CommandExecutorPort) private commandExecutorPort: CommandExecutorPort,
1920
@inject(TYPES.PromptPort) private promptPort: PromptPort,
21+
@inject(TYPES.ConfigService) private configService: ConfigService,
2022
) {}
2123

2224
/**
2325
* Executes the project creation or configuration process.
24-
* @param projectNameArg - Optional project name provided via CLI.
25-
* @param configGroups - Configuration groups for project files.
26+
* @param projectName - Optional project name provided via CLI.
27+
*
2628
* @returns The created or configured project.
2729
*/
28-
async execute(projectNameArg?: string, configGroups: ConfigGroups = {}): Promise<Project> {
30+
async execute(projectName?: string): Promise<Project> {
2931
try {
3032
this.commandExecutorPort.checkEnvironment();
31-
const project = await this.checkExistingProject(projectNameArg);
33+
const project = await this.checkExistingProject(projectName);
34+
const configGroups = this.configService.getConfigGroups();
3235

3336
this.createOrConfigureProject(project, configGroups);
3437
this.displaySuccessMessage(project);

packages/cli/src/application/use-cases/update-exports.use-case.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export class UpdateExportsUseCase {
2121
): Promise<void> {
2222
try {
2323
console.info("Searching for packages...");
24-
const packageJsonPaths = await this.packageRepository.findAllPackages(config.defaultPackageConfig);
24+
const packageJsonPaths = await this.packageRepository.findAllPackages(config);
2525

2626
if (packageJsonPaths.length === 0) {
2727
console.warn("No packages found.");
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
11
import { z } from "zod";
22

3-
export const ConfigFileSchema = z.object({
3+
export const configFileSchema = z.object({
44
path: z.string().describe("Relative path to the configuration file"),
55
content: z.string().describe("Content of the configuration file"),
66
description: z.string().optional().describe("Description of the configuration file's purpose"),
77
});
88

9-
export const ConfigCategorySchema = z.array(ConfigFileSchema).describe("Array of configuration files for a category");
9+
export const ConfigCategorySchema = z.array(configFileSchema).describe("Array of configuration files for a category");
1010

1111
export const ConfigGroupsSchema = z
1212
.record(z.string(), ConfigCategorySchema)
1313
.describe("Configuration groups by category");
1414

15-
export type ConfigFile = z.infer<typeof ConfigFileSchema>;
16-
1715
export type ConfigGroups = z.infer<typeof ConfigGroupsSchema>;

packages/cli/src/domain/entities/package-config.ts

+17-15
Original file line numberDiff line numberDiff line change
@@ -45,21 +45,23 @@ export const exportConfigSchema = z.object({
4545

4646
export const PackageExportsSchema = z.record(z.string(), z.union([exportConfigSchema, z.string()]));
4747

48-
export const packageJsonSchema = z.object({
49-
dependencies: z
50-
.record(z.string(), z.string())
51-
.optional()
52-
.describe("List of runtime dependencies with their versions"),
53-
devDependencies: z
54-
.record(z.string(), z.string())
55-
.optional()
56-
.describe("List of development dependencies with their versions"),
57-
exports: PackageExportsSchema.optional().describe("Export mappings for the package"),
58-
name: z.string().describe("Name of the package"),
59-
scripts: z.record(z.string(), z.string()).describe("Script commands defined for the package"),
60-
"simple-git-hooks": z.record(z.string(), z.string()).optional().describe("Git hooks configuration for the package"),
61-
version: z.string().optional().describe("Version of the package"),
62-
});
48+
export const packageJsonSchema = z
49+
.object({
50+
dependencies: z
51+
.record(z.string(), z.string())
52+
.optional()
53+
.describe("List of runtime dependencies with their versions"),
54+
devDependencies: z
55+
.record(z.string(), z.string())
56+
.optional()
57+
.describe("List of development dependencies with their versions"),
58+
exports: PackageExportsSchema.optional().describe("Export mappings for the package"),
59+
name: z.string().describe("Name of the package"),
60+
scripts: z.record(z.string(), z.string()).describe("Script commands defined for the package"),
61+
"simple-git-hooks": z.record(z.string(), z.string()).optional().describe("Git hooks configuration for the package"),
62+
version: z.string().optional().describe("Version of the package"),
63+
})
64+
.catchall(z.unknown());
6365

6466
export type PackageConfig = z.infer<typeof packageConfigSchema>;
6567

packages/cli/src/domain/entities/project.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { z } from "zod";
44
* Schema for validating project configuration.
55
* Ensures the project name is URL-friendly, not reserved, and the directory is valid.
66
*/
7-
export const ProjectSchema = z.object({
7+
export const projectSchema = z.object({
88
name: z
99
.string()
1010
.min(1, "Project name cannot be empty")
@@ -23,4 +23,4 @@ export const ProjectSchema = z.object({
2323
packageJsonExists: z.boolean().describe("Whether package.json exists in the project directory"),
2424
});
2525

26-
export type Project = z.infer<typeof ProjectSchema>;
26+
export type Project = z.infer<typeof projectSchema>;
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
import type { ConfigGroups } from "@/domain/entities/config-file";
2-
import type { ScriptConfig } from "@/domain/entities/package-config";
32

43
/**
54
* Interface for accessing CLI configuration, including config groups for a create-project
65
* and script configuration for update-exports.
76
*/
87
export interface ConfigServiceInterface {
98
getConfigGroups: () => ConfigGroups;
10-
getScriptConfig: () => ScriptConfig;
119
}

packages/cli/src/domain/interfaces/package.repository.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import type { AnalysisResult, PackageConfig, PackageExports, ScriptConfig } from
55
*/
66
export interface PackageRepository {
77
analyzeImports: (indexFilePath: string, packageConfig: PackageConfig) => AnalysisResult;
8-
findAllPackages: (config: PackageConfig) => Promise<string[]>;
8+
findAllPackages: (configPath?: string) => Promise<string[]>;
99
generateExports: (
1010
packageName: string,
1111
imports: AnalysisResult["imports"],

packages/cli/src/infrastructure/adapters/cli.adapter.ts

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { Command } from "commander";
22
import { inject, injectable } from "inversify";
33

4-
import type { ConfigServiceInterface } from "@/domain/interfaces/config.service";
54
import type { PackageInfoServiceInterface } from "@/domain/interfaces/package-info.service";
65

76
import { CreateProjectCommand } from "@/application/commands/create-project.command";
@@ -14,7 +13,6 @@ export class CLIAdapter {
1413
@inject(TYPES.CreateProjectCommand) private createProjectCommand: CreateProjectCommand,
1514
@inject(TYPES.UpdateExportsCommand) private updateExportsCommand: UpdateExportsCommand,
1615
@inject(TYPES.PackageInfoService) private packageInfoService: PackageInfoServiceInterface,
17-
@inject(TYPES.ConfigService) private configService: ConfigServiceInterface,
1816
) {}
1917

2018
/**
@@ -27,8 +25,8 @@ export class CLIAdapter {
2725
.name("codefast")
2826
.description("CodeFast CLI - A development toolkit for CodeFast.")
2927
.version(this.packageInfoService.getPackageVersion(), "-v, --version", "display CLI version")
30-
.addCommand(this.createProjectCommand.getCommand(this.configService.getConfigGroups()))
31-
.addCommand(this.updateExportsCommand.getCommand(this.configService.getScriptConfig()))
28+
.addCommand(this.createProjectCommand.getCommand())
29+
.addCommand(this.updateExportsCommand.getCommand())
3230
.parse(process.argv);
3331
}
3432
}

packages/cli/src/infrastructure/repositories/file-system-package.repository.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ export class FileSystemPackageRepository implements PackageRepository {
4242
@inject(TYPES.FileSystemUtility) private fileSystemUtility: FileSystemUtility,
4343
) {}
4444

45-
async findAllPackages(config: PackageConfig): Promise<string[]> {
46-
return await glob(config.packageJsonPath, {
45+
async findAllPackages(config: ScriptConfig): Promise<string[]> {
46+
return await glob(config.packagesGlob, {
4747
ignore: this.DEFAULT_IGNORE_PATTERN,
4848
});
4949
}
@@ -140,12 +140,12 @@ export class FileSystemPackageRepository implements PackageRepository {
140140
const exports: PackageExports = {
141141
".": existingExports["."] ?? {
142142
import: {
143-
types: "./dist/types/index.d.ts",
144143
default: "./dist/esm/index.js",
144+
types: "./dist/types/index.d.ts",
145145
},
146146
require: {
147-
types: "./dist/types/index.d.cts",
148147
default: "./dist/cjs/index.cjs",
148+
types: "./dist/types/index.d.cts",
149149
},
150150
},
151151
};
@@ -163,12 +163,12 @@ export class FileSystemPackageRepository implements PackageRepository {
163163

164164
exports[exportPath] = {
165165
import: {
166-
types: typesOutput,
167166
default: esmOutput,
167+
types: typesOutput,
168168
},
169169
require: {
170-
types: typesCjsOutput,
171170
default: cjsOutput,
171+
types: typesCjsOutput,
172172
},
173173
};
174174
}

packages/cli/src/infrastructure/repositories/file-system-project.repository.ts

+8-8
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import type { ProjectRepositoryInterface } from "@/domain/interfaces/project.rep
99
import type { FileSystemUtility } from "@/infrastructure/utilities/file-system-utility";
1010

1111
import { PackageJson, packageJsonSchema } from "@/domain/entities/package-config";
12-
import { ProjectSchema } from "@/domain/entities/project";
12+
import { projectSchema } from "@/domain/entities/project";
1313
import { TYPES } from "@/ioc/types";
1414

1515
@injectable()
@@ -21,26 +21,26 @@ export class FileSystemProjectRepository implements ProjectRepositoryInterface {
2121
@inject(TYPES.FileSystemUtility) private fileSystemUtility: FileSystemUtility,
2222
) {}
2323

24-
async checkExistingProject(projectNameArg?: string): Promise<Project> {
24+
async checkExistingProject(projectName?: string): Promise<Project> {
2525
let fullPath = process.cwd();
26-
let projectName = "";
26+
let name = "";
2727
const packageJsonPath = path.join(process.cwd(), "package.json");
2828
const packageJsonExists = this.fileSystemPort.exists(packageJsonPath);
2929

3030
if (packageJsonExists) {
3131
const result = packageJsonSchema.safeParse(JSON.parse(this.fileSystemPort.readFile(packageJsonPath)));
3232

3333
if (result.success) {
34-
projectName = result.data.name;
34+
name = result.data.name;
3535
}
3636
} else {
37-
projectName = await this.getValidProjectName(projectNameArg);
37+
name = await this.getValidProjectName(projectName);
3838

39-
fullPath = path.resolve(process.cwd(), projectName);
39+
fullPath = path.resolve(process.cwd(), name);
4040
}
4141

4242
return {
43-
name: projectName,
43+
name,
4444
directory: fullPath,
4545
packageJsonExists,
4646
};
@@ -210,7 +210,7 @@ import "@/app/globals.css";`;
210210

211211
private async getValidProjectName(initialName?: string, attempts = 0, maxAttempts = 3): Promise<string> {
212212
const projectName = initialName ?? (await this.promptPort.prompt("Enter project name: "));
213-
const result = ProjectSchema.safeParse({ name: projectName, directory: "", packageJsonExists: false });
213+
const result = projectSchema.safeParse({ name: projectName, directory: "", packageJsonExists: false });
214214

215215
if (result.success) {
216216
const fullPath = path.resolve(process.cwd(), projectName);

packages/cli/src/infrastructure/services/config.service.ts

+1-25
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { injectable } from "inversify";
22

33
import type { ConfigGroups } from "@/domain/entities/config-file";
4-
import type { ScriptConfig } from "@/domain/entities/package-config";
54
import type { ConfigServiceInterface } from "@/domain/interfaces/config.service";
65

76
@injectable()
@@ -155,6 +154,7 @@ const config = {
155154
extends: ["@commitlint/config-conventional"],
156155
rules: {
157156
"body-max-line-length": [2, "always", 350],
157+
"header-max-length": [2, "always", 150],
158158
},
159159
};
160160
@@ -316,31 +316,7 @@ export const fontVariables = cn(fontGeistSans.variable, fontGeistMono.variable);
316316
],
317317
};
318318

319-
private readonly scriptConfig: ScriptConfig = {
320-
packagesGlob: "./packages/**/package.json",
321-
defaultPackageConfig: {
322-
cjsOutputPattern: "./dist/cjs/{dir}/{name}.cjs",
323-
esmOutputPattern: "./dist/esm/{dir}/{name}.js",
324-
packageJsonPath: "package.json",
325-
srcIndexPath: "src/index.ts",
326-
typesOutputCjsPattern: "./dist/types/{dir}/{name}.d.ts",
327-
typesOutputPattern: "./dist/types/{dir}/{name}.d.ts",
328-
},
329-
customPackageConfigs: {
330-
"@codefast/ui": {
331-
exportPathPrefixesToRemove: ["components"],
332-
},
333-
"@codefast/style-guide": {
334-
exportPathPrefixesToRemove: ["configs"],
335-
},
336-
},
337-
};
338-
339319
getConfigGroups(): ConfigGroups {
340320
return this.configGroups;
341321
}
342-
343-
getScriptConfig(): ScriptConfig {
344-
return this.scriptConfig;
345-
}
346322
}

packages/cli/src/ioc/container.ts

+4
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ const container = new Container();
3232
// Domain bindings
3333
container.bind<ProjectRepositoryInterface>(TYPES.ProjectRepository).to(FileSystemProjectRepository).inSingletonScope(); // Handles project creation and file updates
3434
container.bind<PackageRepository>(TYPES.PackageRepository).to(FileSystemPackageRepository).inSingletonScope(); // Manages package.json exports
35+
3536
container.bind<PackageInfoServiceInterface>(TYPES.PackageInfoService).to(NodePackageInfoService).inSingletonScope(); // Provides package version
3637
container
3738
.bind<DependencyConfigServiceInterface>(TYPES.DependencyConfigService)
@@ -44,13 +45,16 @@ container.bind<FileSystemPort>(TYPES.FileSystemPort).to(NodeFileSystemAdapter).i
4445
container.bind<CommandExecutorPort>(TYPES.CommandExecutorPort).to(NodeCommandExecutorAdapter).inSingletonScope(); // Command execution
4546
container.bind<PromptPort>(TYPES.PromptPort).to(NodePromptAdapter).inSingletonScope(); // User input prompting
4647
container.bind<AnalysisPort>(TYPES.AnalysisPort).to(TsMorphAnalysisAdapter).inSingletonScope(); // Import analysis
48+
4749
container.bind<CreateProjectUseCase>(TYPES.CreateProjectUseCase).to(CreateProjectUseCase).inSingletonScope(); // Project creation logic
4850
container.bind<UpdateExportsUseCase>(TYPES.UpdateExportsUseCase).to(UpdateExportsUseCase).inSingletonScope(); // Export update logic
51+
4952
container.bind<CreateProjectCommand>(TYPES.CreateProjectCommand).to(CreateProjectCommand).inSingletonScope(); // CLI command for project creation
5053
container.bind<UpdateExportsCommand>(TYPES.UpdateExportsCommand).to(UpdateExportsCommand).inSingletonScope(); // CLI command for export updates
5154

5255
// Infrastructure bindings
5356
container.bind<CLIAdapter>(TYPES.CLIAdapter).to(CLIAdapter).inSingletonScope(); // CLI setup and command registration
57+
5458
container.bind<FileSystemUtility>(TYPES.FileSystemUtility).to(FileSystemUtility).inSingletonScope(); // File system utility functions
5559

5660
export { container };

0 commit comments

Comments
 (0)