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/mean-suits-invite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"scaffolder-toolkit": minor
---

feat(option): update 'dk list' filtering to '--where' with new advanced syntax
17 changes: 7 additions & 10 deletions packages/devkit/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,8 @@ dk list
# List templates for a specific language (e.g., 'javascript')
dk list javascript

# List templates and filter by a substring (e.g., 'vue')
dk list --filter vue
# List templates and filter by a where clause (e.g., 'name:vue')
dk list --where name:vue
```

#### Options
Expand All @@ -179,7 +179,7 @@ The `dk list` command now uses the following options to control which templates
- **`--local`**: Only list templates from the local configuration file (`.devkit.json`).
- **`--global`**: Only list templates from the global configuration file (`~/.devkitrc`).
- **`--all`**: List templates from both the local and global configurations, merging them into a single list.
- **`--filter <string>`**: Filter templates by name or alias substring.
- **`--where <clause>`**: **Filter templates using one or more property clauses.** Clauses must be in the format **`property:value`** or **`property=value`** (e.g., `alias:rt`, `pm=npm`, `name:/^node/`). Multiple `--where` arguments form a logical **AND** filter.
- **`--mode <mode>`**: Sets the display mode for the template list. Options are **`tree`** (default, detailed view) or **`table`** (compact, column-based view).

#### Examples
Expand All @@ -196,14 +196,11 @@ dk list --global
# List templates from both local and global configs
dk list --all

# List templates and filter by name or alias substring
dk list --filter vue
# List templates and filter by alias 'rt' (using colon) AND package manager 'npm' (using equals)
dk list --where alias:rt --where pm=npm

# List javascript templates and filter by name or alias substring
dk list javascript --filter react

# List javascript templates and filter by name starting or containing
dk list javascript --filter r
# List javascript templates and filter by templates whose name starts with 'r'
dk list javascript --where name:/^r/

# List templates in a compact table format
dk list --mode table
Expand Down
8 changes: 4 additions & 4 deletions packages/devkit/TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,13 @@ This document tracks all planned and completed tasks for the Dev Kit project.
- [x] Refactor and restructure the utilities
- [x] Better json structure for languages translation
- [x] **Enhance `list` Command**: Add support for **different display modes** (e.g., table or tree structure). `tree` as default
- [ ] **Enhance `list` Command**: Add options to **filter by properties** (e.g., `packageManager`, `alias`, etc.).
- [x] **Enhance `list` Command**: Add options to **filter by properties** (e.g., `packageManager`, `alias`, etc.).
- [ ] **Enhance `list` Command**: Add flag to also see default config `--with-defaults`.
- [ ] Add wildcard support for template name in the `dk config update` and `dk config remove` commands.
- [ ] ** 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 a configuration validation step when initializing or updating the config file to ensure all required fields are present and correctly formatted.
- [ ] ** Enhance for organization Purpose **: Add new language `Typescript(ts)` with same code as javascript(js), also support for nodejs(node) template name for those who prefer it than the programming language name
- [ ] Add a configuration validation step when updating the config file to ensure all required fields are present and correctly formatted.
- [ ] **Dynamic Help Text**: Programmatically generate help text for options with constrained values (e.g., `--cache-strategy`) to ensure it's always up to date.
- [ ] **Skip Confirmation**: Add a global `-y` or `--yes` option to skip confirmation prompts in commands like `dk init`.
- [ ] **Skip Confirmation**: Add a global `-y`/`--yes` and `-n/--no` option to skip confirmation prompts in commands like `dk init`.
- [ ] **CLI Self-Update**: Implement a command to allow users to update the CLI itself. `dk upgrade`
- [ ] **Testing**: Stabilize the integration test of the `new` command

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ describe("dk config", () => {

expect(exitCode).toBe(0);
expect(all).toContain(
"Les valeurs pour l'option '--set' doivent être une série de paires clé-valeur (par ex., --set key1 value1 key2 value2).",
"Les valeurs pour l'option '--set' doivent être une série de paires clé-valeur (par ex. --set clé1 valeur1 clé2 valeur2).",
);
});

Expand Down
80 changes: 61 additions & 19 deletions packages/devkit/__tests__/integrations/list.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const localConfig: CliConfig = {
description: "A basic Vue template",
location: "https://github.com/vuejs/vue",
alias: "vb",
packageManager: "pnpm",
},
},
},
Expand All @@ -49,6 +50,7 @@ const localConfig: CliConfig = {
description: "A Node.js API boilerplate",
location: "https://github.com/node-api",
alias: "na",
packageManager: "yarn",
},
},
},
Expand Down Expand Up @@ -179,54 +181,94 @@ describe("dk list", () => {
expect(all).not.toContain("NODE");
});

it("should filter templates by name when --filter is used", async () => {
it("should filter templates by name using the new --where syntax", async () => {
await fs.writeJson(path.join(tempDir, LOCAL_CONFIG_FILE_NAME), localConfig);
await fs.writeJson(
path.join(globalConfigDir, GLOBAL_CONFIG_FILE_NAME),
globalConfig,
);

const { all, exitCode } = await execa(
"bun",
[CLI_PATH, "list", "--filter", "vue"],
[CLI_PATH, "list", "--where", "name:vue"],
{
all: true,
env: { HOME: globalConfigDir },
},
);

expect(exitCode).toBe(0);
expect(all).toContain("Using local configuration.");
expect(all).toContain("JAVASCRIPT");
expect(all).toContain("vue-basic");
expect(all).not.toContain("react-ts");
expect(all).not.toContain("NODE");
expect(all).not.toContain("PYTHON");
});

it("should filter templates by alias when --filter is used", async () => {
it("should filter templates by alias using the new --where syntax and exact regex match", async () => {
await fs.writeJson(path.join(tempDir, LOCAL_CONFIG_FILE_NAME), localConfig);
await fs.writeJson(
path.join(globalConfigDir, GLOBAL_CONFIG_FILE_NAME),
globalConfig,
);

const { all, exitCode } = await execa(
"bun",
[CLI_PATH, "list", "--filter", "rt"],
[CLI_PATH, "list", "--where", "alias:/^rt$/"],
{
all: true,
env: { HOME: globalConfigDir },
},
);

expect(exitCode).toBe(0);
expect(all).toContain("Using local configuration.");
expect(all).toContain("JAVASCRIPT");
expect(all).toContain("react-ts");
expect(all).not.toContain("vue-basic");
expect(all).not.toContain("NODE");
expect(all).not.toContain("PYTHON");
});

it("should filter templates by substring in packageManager, matching both npm and pnpm", async () => {
await fs.writeJson(path.join(tempDir, LOCAL_CONFIG_FILE_NAME), localConfig);

const { all, exitCode } = await execa(
"bun",
[CLI_PATH, "list", "--where", "pm:npm"],
{
all: true,
env: { HOME: globalConfigDir },
},
);

expect(exitCode).toBe(0);
expect(all).toContain("react-ts");
expect(all).toContain("vue-basic");
expect(all).not.toContain("node-api");
});

it("should filter templates by strict packageManager using regex", async () => {
await fs.writeJson(path.join(tempDir, LOCAL_CONFIG_FILE_NAME), localConfig);

const { all, exitCode } = await execa(
"bun",
[CLI_PATH, "list", "--where", "pm:/^npm$/"],
{
all: true,
env: { HOME: globalConfigDir },
},
);

expect(exitCode).toBe(0);
expect(all).toContain("react-ts");
expect(all).not.toContain("vue-basic");
});

it("should filter templates using multiple clauses (Logical AND)", async () => {
await fs.writeJson(path.join(tempDir, LOCAL_CONFIG_FILE_NAME), localConfig);

const { all, exitCode } = await execa(
"bun",
[CLI_PATH, "list", "--where", "alias:vb", "desc:vue"],
{
all: true,
env: { HOME: globalConfigDir },
},
);

expect(exitCode).toBe(0);
expect(all).toContain("vue-basic");
expect(all).not.toContain("react-ts");
expect(all).not.toContain("node-api");
});

it("should show an error if a language is provided but no templates are found for it", async () => {
Expand Down Expand Up @@ -331,7 +373,7 @@ describe("dk list", () => {
);
});

it("should filter templates by name when --filter is used", async () => {
it("should filter templates by name when --where is used in table mode", async () => {
await fs.writeJson(
path.join(tempDir, LOCAL_CONFIG_FILE_NAME),
localConfig,
Expand All @@ -343,7 +385,7 @@ describe("dk list", () => {

const { all, exitCode } = await execa(
"bun",
[CLI_PATH, "list", "--filter", "vue", "--mode", "table"],
[CLI_PATH, "list", "--where", "name:vue", "--mode", "table"],
{
all: true,
env: { HOME: globalConfigDir },
Expand Down
15 changes: 1 addition & 14 deletions packages/devkit/__tests__/units/commands/config/logic.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,9 @@ import {
handleNonInteractiveSettingsUpdate,
handleNonInteractiveTemplateUpdate,
} from "../../../../src/commands/config/logic.js";
import {
VALID_CACHE_STRATEGIES,
PackageManagers,
} from "../../../../src/utils/schema/schema.js";
import deepmerge from "deepmerge";
import { VALID_CACHE_STRATEGIES } from "../../../../src/utils/schema/schema.js";
import { DevkitError } from "../../../../src/utils/errors/base.js";
import { mocktFn } from "../../../../vitest.setup.js";
import {
validateAlias,
validateDescription,
validateLocation,
} from "../../../../src/utils/validations/templates.js";
import {
validatePackageManager,
validateCacheStrategy,
} from "../../../../src/utils/validations/config.js";

const {
mockReadAndMergeConfigs,
Expand Down
Loading