Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/rude-carpets-hammer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"scaffolder-toolkit": patch
---

refactor: Move `chalk` and `ora` to a single, centralized file for better code organization.
4 changes: 2 additions & 2 deletions packages/devkit/TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,15 @@ This document tracks all planned and completed tasks for the Dev Kit project.
- [ ] Add a configuration validation step when initializing or updating the config file to ensure all required fields are present and correctly formatted.
- [ ]: Enhance interactivity with the `dk config add` command
- [ ] **Enhance `list` Command**: Add flag to also see default config `--with-defaults`.
- [ ] ** Enhance for organization Purpose **: Add new language `Typescript` with same code as javascript
- [ ] ** Enhance for organization Purpose **: Add new language `Typescript` with same code as javascript, also support for nodejs template name for those who prefer it than the language
- [ ] Add wildcard support for template name in the `dk config update` and `dk config remove` commands.
- [ ] Use the interactive approach for the `dk config add` command (code already there)
- [ ] **Dynamic Error Messages**: Update error handling to dynamically generate lists of valid options (e.g., package managers, cache strategies) in error messages.
- [ ] **Skip Confirmation**: Add a global `-y` or `--yes` option to skip confirmation prompts in commands like `dk init`.
- [ ] **Color Configuration**: Add a feature to allow users to configure the colors for templates.
- [ ] **Dynamic Help Text**: Programmatically generate help text for options with constrained values (e.g., `--cache-strategy`) to ensure it's always up to date.
- [ ] **Testing**: Stabilize the integration test of the `new` command
- [ ] **Centralize Utilities**: Move `chalk` and `ora` to a single, centralized file for better code organization.
- [x] **Centralize Utilities**: Move `chalk` and `ora` to a single, centralized file for better code organization.
- [x] Enable GitHub discussions
- [x] Refactor and restructure the utilities
- [ ] Better json structure for languages
Expand Down
6 changes: 3 additions & 3 deletions packages/devkit/__tests__/integrations/config/add.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ describe("dk config add", () => {

expect(exitCode).toBe(1);
expect(all).toContain(
"An unexpected error occurred: Invalid value for language. Valid options are: javascript",
"❌ Devkit encountered an unexpected internal issue: Invalid value for language. Valid options are: javascript",
);
});

Expand All @@ -250,7 +250,7 @@ describe("dk config add", () => {

expect(exitCode).toBe(1);
expect(all).toContain(
"An unexpected error occurred: Template 'react-ts' already exists in the configuration. Use 'devkit config set' to update it.",
"❌ Devkit encountered an unexpected internal issue: Template 'react-ts' already exists in the configuration. Use 'devkit config set' to update it.",
);
});

Expand Down Expand Up @@ -283,7 +283,7 @@ describe("dk config add", () => {

expect(exitCode).toBe(1);
expect(all).toContain(
"An unexpected error occurred: Alias 'rt' already exists for another template in this language. Please choose a different alias.",
"❌ Devkit encountered an unexpected internal issue: Alias 'rt' already exists for another template in this language. Please choose a different alias.",
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ describe("dk config", () => {

expect(exitCode).toBe(1);
expect(all).toContain(
"n unexpected error occurred: No local configuration file found. Run 'devkit config init --local' to create one.",
"❌ Devkit encountered an unexpected internal issue: No local configuration file found. Run 'devkit config init --local' to create one.",
);
});
});
2 changes: 1 addition & 1 deletion packages/devkit/__tests__/integrations/config/list.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ describe("dk config list", () => {

expect(exitCode).toBe(1);
expect(all).toContain(
"An unexpected error occurred: Global configuration file not found.",
"❌ Devkit encountered an unexpected internal issue: Global configuration file not found.",
);
});

Expand Down
4 changes: 2 additions & 2 deletions packages/devkit/__tests__/integrations/config/remove.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ describe("dk config remove", () => {

expect(exitCode).toBe(1);
expect(all).toContain(
"An unexpected error occurred: Template 'not-found-1, not-found-2' not found in configuration.",
"❌ Devkit encountered an unexpected internal issue: Template 'not-found-1, not-found-2' not found in configuration.",
);
});

Expand All @@ -229,7 +229,7 @@ describe("dk config remove", () => {

expect(exitCode).toBe(1);
expect(all).toContain(
"An unexpected error occurred: Invalid value for language. Valid options are: javascript",
"❌ Devkit encountered an unexpected internal issue: Invalid value for language. Valid options are: javascript",
);
});

Expand Down
35 changes: 16 additions & 19 deletions packages/devkit/__tests__/units/commands/config/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { describe, it, expect, vi, beforeEach } from "vitest";
import { setupConfigCommand } from "../../../../src/commands/config/index.js";
import { mockSpinner, mockChalk, mocktFn } from "../../../../vitest.setup.js";
import { mockSpinner, mocktFn, mockLogger } from "../../../../vitest.setup.js";

const {
mockReadAndMergeConfigs,
Expand Down Expand Up @@ -48,7 +48,7 @@ vi.mock("../../../../src/commands/config/list.js", () => ({
setupListCommand: mockSetupListCommand,
}));

vi.spyOn(console, "log").mockImplementation(() => {});
console.log = mockLogger.log;

describe("setupConfigCommand", () => {
let mockProgram: any;
Expand Down Expand Up @@ -110,7 +110,7 @@ describe("setupConfigCommand", () => {

expect(mockSpinner.warn).toHaveBeenCalledOnce();
expect(mockSpinner.warn).toHaveBeenCalledWith(
mockChalk.green("warning.no_command_or_option_provided"),
mockLogger.colors.green("warning.no_command_or_option_provided"),
);
});

Expand All @@ -131,7 +131,7 @@ describe("setupConfigCommand", () => {
false,
);
expect(mockSpinner.succeed).toHaveBeenCalledWith(
mockChalk.green("config.set.success"),
mockLogger.colors.green("config.set.success"),
);
});

Expand All @@ -148,7 +148,7 @@ describe("setupConfigCommand", () => {

expect(mockHandleNonInteractiveSettingsUpdate).not.toHaveBeenCalled();
expect(mockSpinner.fail).toHaveBeenCalledWith(
mockChalk.redBright("error.command.set.invalid_format"),
mockLogger.colors.redBright("error.command.set.invalid_format"),
);
});

Expand All @@ -161,16 +161,15 @@ describe("setupConfigCommand", () => {
config: mockConfig,
source: "local",
});
const consoleLogSpy = vi.spyOn(console, "log");

setupConfigCommand(mockProgram);
await mockAction(["language"], {});

expect(consoleLogSpy).toHaveBeenCalledWith(
mockChalk.yellow.bold("language") + ": " + "typescript",
expect(mockLogger.log).toHaveBeenCalledWith(
mockLogger.colors.yellowBold("language") + ": " + "typescript",
);
expect(mockSpinner.succeed).toHaveBeenCalledWith(
mockChalk.green("config.get.success"),
mockLogger.colors.green("config.get.success"),
);
});

Expand All @@ -186,19 +185,18 @@ describe("setupConfigCommand", () => {
config: mockConfig,
source: "local",
});
const consoleLogSpy = vi.spyOn(console, "log");

setupConfigCommand(mockProgram);
await mockAction(["language", "packageManager"], {});

expect(consoleLogSpy).toHaveBeenCalledWith(
mockChalk.yellow.bold("language") + ": " + "typescript",
expect(mockLogger.log).toHaveBeenCalledWith(
mockLogger.colors.yellowBold("language") + ": " + "typescript",
);
expect(consoleLogSpy).toHaveBeenCalledWith(
mockChalk.yellow.bold("packageManager") + ": " + "bun",
expect(mockLogger.log).toHaveBeenCalledWith(
mockLogger.colors.yellowBold("packageManager") + ": " + "bun",
);
expect(mockSpinner.succeed).toHaveBeenCalledWith(
mockChalk.green("config.get.success"),
mockLogger.colors.green("config.get.success"),
);
});

Expand All @@ -208,20 +206,19 @@ describe("setupConfigCommand", () => {
config: mockConfig,
source: "local",
});
const consoleLogSpy = vi.spyOn(console, "log");

setupConfigCommand(mockProgram);
await mockAction(["nonexistent_key"], {});

expect(consoleLogSpy).toHaveBeenCalledWith(
mockChalk.redBright(
expect(mockLogger.log).toHaveBeenCalledWith(
mockLogger.colors.redBright(
mocktFn("config.get.not_found", {
key: "nonexistent_key",
}),
),
);
expect(mockSpinner.succeed).toHaveBeenCalledWith(
mockChalk.green("config.get.success"),
mockLogger.colors.green("config.get.success"),
);
});

Expand Down
9 changes: 6 additions & 3 deletions packages/devkit/__tests__/units/commands/config/list.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { vi, describe, it, expect, beforeEach } from "vitest";
import { setupListCommand } from "../../../../src/commands/config/list.js";
import { mockSpinner, mockChalk, mocktFn } from "../../../../vitest.setup.js";
import { mockSpinner, mockLogger, mocktFn } from "../../../../vitest.setup.js";
import { DevkitError } from "../../../../src/utils/errors/base.js";

const {
Expand Down Expand Up @@ -33,7 +33,7 @@ vi.mock("#core/template/printer.js", () => ({
printTemplates: mockPrintTemplates,
}));

const consoleLogSpy = vi.spyOn(console, "log").mockImplementation(() => {});
const consoleLogSpy = mockLogger.log;

describe("setupListCommand", () => {
let mockConfigCommand: any;
Expand Down Expand Up @@ -106,6 +106,9 @@ describe("setupListCommand", () => {
expect(mockSpinner.info).toHaveBeenCalledWith(
mocktFn("config.get.source.local"),
);
expect(mockLogger.log).toHaveBeenCalled();

expect(mockPrintSettings).toHaveBeenCalledOnce();
expect(mockPrintSettings).toHaveBeenCalledWith(sampleConfig.settings);
expect(mockPrintTemplates).toHaveBeenCalledWith(
"javascript",
Expand Down Expand Up @@ -177,7 +180,7 @@ describe("setupListCommand", () => {
);
expect(mockPrintSettings).toHaveBeenCalledWith({});
expect(consoleLogSpy).toHaveBeenCalledWith(
mockChalk.yellow(mocktFn("list.templates.not_found")),
mockLogger.colors.yellow(mocktFn("list.templates.not_found")),
);
expect(mockPrintTemplates).not.toHaveBeenCalled();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
handleNonInteractiveSettingsUpdate,
handleNonInteractiveTemplateUpdate,
} from "../../../../src/commands/config/logic.ts";
import { mocktFn } from "../../../../vitest.setup.ts";
import { mockLogger, mocktFn } from "../../../../vitest.setup.ts";

const {
mockSelect,
Expand Down Expand Up @@ -63,7 +63,6 @@ describe("Interactive Config Prompts", () => {

beforeEach(() => {
vi.clearAllMocks();
vi.spyOn(console, "log").mockImplementation(() => {});
});

afterEach(() => {
Expand All @@ -84,7 +83,7 @@ describe("Interactive Config Prompts", () => {
"fr",
false,
);
expect(console.log).toHaveBeenCalledWith(mocktFn("config.set.success"));
expect(mockLogger.log).toHaveBeenCalledWith(mocktFn("config.set.success"));
});

it("should handle a full template update flow correctly (description)", async () => {
Expand All @@ -105,7 +104,7 @@ describe("Interactive Config Prompts", () => {
{ description: "A cool new description" },
false,
);
expect(console.log).toHaveBeenCalledWith(
expect(mockLogger.log).toHaveBeenCalledWith(
mocktFn("config.update.success", { templateName: "web" }),
);
});
Expand Down
10 changes: 4 additions & 6 deletions packages/devkit/__tests__/units/commands/config/remove.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { vi, describe, it, expect, beforeEach } from "vitest";
import { setupRemoveCommand } from "../../../../src/commands/config/remove.js";
import { mockSpinner, mockChalk, mocktFn } from "../../../../vitest.setup.js";
import { mockSpinner, mockLogger, mocktFn } from "../../../../vitest.setup.js";
import { DevkitError } from "../../../../src/utils/errors/base.js";

const {
Expand Down Expand Up @@ -36,8 +36,6 @@ vi.mock("#utils/validations/config.js", () => ({
validateProgrammingLanguage: mockValidateProgrammingLanguage,
}));

vi.spyOn(console, "log").mockImplementation(() => {});

describe("setupRemoveCommand", () => {
let mockConfigCommand: any;

Expand Down Expand Up @@ -100,7 +98,7 @@ describe("setupRemoveCommand", () => {
await actionFn("javascript", ["vue-basic"], { global: false });

expect(mockSpinner.start).toHaveBeenCalledWith(
mockChalk.cyan(mocktFn("remove_template.start")),
mockLogger.colors.cyan(mocktFn("remove_template.start")),
);
expect(mockSaveLocalConfig).toHaveBeenCalledWith({
settings: {},
Expand Down Expand Up @@ -275,7 +273,7 @@ describe("setupRemoveCommand", () => {
});

it("should remove existing templates and warn about non-existent ones", async () => {
const consoleLogSpy = vi.spyOn(console, "log");
const consoleLogSpy = mockLogger.log;
const initialConfig = structuredClone(sampleConfig);
mockReadAndMergeConfigs.mockResolvedValueOnce({
config: initialConfig,
Expand Down Expand Up @@ -310,7 +308,7 @@ describe("setupRemoveCommand", () => {
}),
);
expect(consoleLogSpy).toHaveBeenCalledWith(
mockChalk.yellow(
mockLogger.colors.yellow(
mocktFn("remove_template.not_found_warning", {
template: "non-existent",
}),
Expand Down
14 changes: 7 additions & 7 deletions packages/devkit/__tests__/units/commands/config/update.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { vi, describe, it, expect, beforeEach } from "vitest";
import { setupUpdateCommand } from "../../../../src/commands/config/update.js";
import { mockSpinner, mockChalk, mocktFn } from "../../../../vitest.setup.js";
import { mockSpinner, mocktFn, mockLogger } from "../../../../vitest.setup.js";
import { DevkitError } from "../../../../src/utils/errors/base.js";

const { mockHandleErrorAndExit, mockHandleNonInteractiveTemplateUpdate } =
Expand All @@ -19,7 +19,7 @@ vi.mock("../../../../src/commands/config/logic.js", () => ({
handleNonInteractiveTemplateUpdate: mockHandleNonInteractiveTemplateUpdate,
}));

const consoleLogSpy = vi.spyOn(console, "log").mockImplementation(() => {});
const consoleLogSpy = mockLogger.log;
const mockProcessExit = vi
.spyOn(process, "exit")
.mockImplementation((() => {}) as unknown as never);
Expand Down Expand Up @@ -98,7 +98,7 @@ describe("setupUpdateCommand", () => {
await actionFn("javascript", ["my-template"], defaultCmdOptions);

expect(mockSpinner.start).toHaveBeenCalledWith(
mockChalk.cyan(
mockLogger.colors.cyan(
mocktFn("config.update.updating", { templateName: "my-template" }),
),
);
Expand All @@ -114,7 +114,7 @@ describe("setupUpdateCommand", () => {
);
expect(mockSpinner.stop).toHaveBeenCalled();
expect(consoleLogSpy).toHaveBeenCalledWith(
mockChalk.green(
mockLogger.colors.green(
`\n✔ ${mocktFn("config.update.success_summary", {
count: "1",
templateName: "my-template",
Expand Down Expand Up @@ -145,7 +145,7 @@ describe("setupUpdateCommand", () => {
false,
);
expect(consoleLogSpy).toHaveBeenCalledWith(
mockChalk.green(
mockLogger.colors.green(
`\n✔ ${mocktFn("config.update.success_summary", {
count: "2",
templateName: "temp1, temp2",
Expand Down Expand Up @@ -174,7 +174,7 @@ describe("setupUpdateCommand", () => {

expect(mockHandleNonInteractiveTemplateUpdate).toHaveBeenCalledTimes(3);
expect(consoleLogSpy).toHaveBeenCalledWith(
mockChalk.yellow(
mockLogger.colors.yellow(
`\n${mocktFn("config.update.single_fail", {
templateName: "temp2",
error: mocktFn("error.template.not_found", { template: "temp2" }),
Expand Down Expand Up @@ -210,7 +210,7 @@ describe("setupUpdateCommand", () => {
expect(mockSpinner.stop).toHaveBeenCalled();
expect(mockHandleErrorAndExit).not.toHaveBeenCalled();
expect(consoleLogSpy).toHaveBeenCalledWith(
mockChalk.yellow(
mockLogger.colors.yellow(
`\n${mocktFn("config.update.single_fail", {
templateName: "my-template",
error: "unknown error",
Expand Down
Loading