Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cli): let choose the package manager #1247

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
21 changes: 14 additions & 7 deletions packages/cli/src/commands/add.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ import { existsSync, promises as fs } from "node:fs";
import path from "node:path";
import process from "node:process";
import color from "chalk";
import { Command } from "commander";
import { Command, Option } from "commander";
import { execa } from "execa";
import * as v from "valibot";
import { AGENTS, COMMANDS, detectPM } from "../utils/auto-detect.js";
import { ConfigError, error, handleError } from "../utils/errors.js";
import { type Config, getConfig } from "../utils/get-config.js";
import { getEnvProxy } from "../utils/get-env-proxy.js";
import { ConfigError, error, handleError } from "../utils/errors.js";
import { intro, prettifyList } from "../utils/prompt-helpers.js";
import * as p from "../utils/prompts.js";
import {
fetchTree,
getItemTargetPath,
Expand All @@ -16,9 +19,6 @@ import {
resolveTree,
} from "../utils/registry";
import { transformImports } from "../utils/transformers.js";
import * as p from "../utils/prompts.js";
import { intro, prettifyList } from "../utils/prompt-helpers.js";
import { detectPM } from "../utils/auto-detect.js";

const highlight = (...args: unknown[]) => color.bold.cyan(...args);

Expand All @@ -30,6 +30,7 @@ const addOptionsSchema = v.object({
cwd: v.string(),
path: v.optional(v.string()),
deps: v.boolean(),
pm: v.optional(v.picklist(AGENTS)),
proxy: v.optional(v.string()),
});

Expand All @@ -46,6 +47,12 @@ export const add = new Command()
.option("-o, --overwrite", "overwrite existing files", false)
.option("--proxy <proxy>", "fetch components from registry using a proxy", getEnvProxy())
.option("-p, --path <path>", "the path to add the component to")
.addOption(
new Option(
"--pm <name>",
"specify the package manager. if not provided, the tool will auto-detect one based on the current project."
).choices(AGENTS)
)
.action(async (components, opts) => {
try {
intro();
Expand Down Expand Up @@ -244,11 +251,11 @@ async function runAdd(cwd: string, config: Config, options: AddOptions) {
}

// Install dependencies.
const commands = await detectPM(cwd, options.deps);
const commands = !options.pm ? await detectPM(cwd, options.deps) : COMMANDS[options.pm];
if (commands) {
const [pm, add] = commands.add.split(" ") as [string, string];
tasks.push({
title: `${highlight(pm)}: Installing dependencies`,
title: `${highlight(pm)} Installing dependencies`,
enabled: dependencies.size > 0,
async task() {
await execa(pm, [add, ...dependencies], {
Expand Down
38 changes: 21 additions & 17 deletions packages/cli/src/commands/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,26 @@ import { existsSync, promises as fs } from "node:fs";
import path from "node:path";
import process from "node:process";
import color from "chalk";
import * as v from "valibot";
import { Command, Option } from "commander";
import { execa } from "execa";
import * as cliConfig from "../utils/get-config.js";
import type { Config } from "../utils/get-config.js";
import { error, handleError } from "../utils/errors.js";
import { getBaseColors, getRegistryBaseColor, getStyles } from "../utils/registry";
import * as templates from "../utils/templates.js";
import * as p from "../utils/prompts.js";
import { intro, prettifyList } from "../utils/prompt-helpers.js";
import { resolveImport } from "../utils/resolve-imports.js";
import { syncSvelteKit } from "../utils/sveltekit.js";
import * as v from "valibot";
import {
AGENTS,
COMMANDS,
type DetectLanguageResult,
detectConfigs,
detectLanguage,
detectPM,
} from "../utils/auto-detect.js";
import { error, handleError } from "../utils/errors.js";
import type { Config } from "../utils/get-config.js";
import * as cliConfig from "../utils/get-config.js";
import { intro, prettifyList } from "../utils/prompt-helpers.js";
import * as p from "../utils/prompts.js";
import { getBaseColors, getRegistryBaseColor, getStyles } from "../utils/registry";
import { resolveImport } from "../utils/resolve-imports.js";
import { syncSvelteKit } from "../utils/sveltekit.js";
import * as templates from "../utils/templates.js";

const PROJECT_DEPENDENCIES = ["tailwind-variants", "clsx", "tailwind-merge"] as const;
const highlight = (...args: unknown[]) => color.bold.cyan(...args);
Expand All @@ -36,6 +38,7 @@ const initOptionsSchema = v.object({
componentsAlias: v.optional(v.string()),
utilsAlias: v.optional(v.string()),
deps: v.boolean(),
pm: v.optional(v.picklist(AGENTS)),
});

type InitOptions = v.InferOutput<typeof initOptionsSchema>;
Expand All @@ -50,15 +53,16 @@ export const init = new Command()
styles.map((style) => style.name)
)
)
.addOption(
new Option("--base-color <name>", "the base color for the components").choices(
baseColors.map((color) => color.name)
)
)
.option("--css <path>", "path to the global CSS file")
.option("--tailwind-config <path>", "path to the tailwind config file")
.option("--components-alias <path>", "import alias for components")
.option("--utils-alias <path>", "import alias for utils")
.addOption(
new Option(
"--pm <name>",
"specify the package manager. If not provided, the tool will auto-detect one based on the current project."
).choices(AGENTS)
)
.action(async (opts) => {
intro();
const options = v.parse(initOptionsSchema, opts);
Expand Down Expand Up @@ -363,11 +367,11 @@ export async function runInit(cwd: string, config: Config, options: InitOptions)
});

// Install dependencies.
const commands = await detectPM(cwd, options.deps);
const commands = !options.pm ? await detectPM(cwd, options.deps) : COMMANDS[options.pm];
if (commands) {
const [pm, add] = commands.add.split(" ") as [string, string];
tasks.push({
title: `${highlight(pm)}: Installing dependencies`,
title: `${highlight(pm)} Installing dependencies`,
enabled: options.deps,
async task() {
await execa(pm, [add, ...PROJECT_DEPENDENCIES], {
Expand Down
23 changes: 15 additions & 8 deletions packages/cli/src/commands/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@ import { existsSync, promises as fs } from "node:fs";
import path from "node:path";
import process from "node:process";
import color from "chalk";
import { Command } from "commander";
import { Command, Option } from "commander";
import { execa } from "execa";
import * as v from "valibot";
import { type Config, getConfig } from "../utils/get-config.js";
import { AGENTS, COMMANDS, detectPM } from "../utils/auto-detect.js";
import { error, handleError } from "../utils/errors.js";
import { type Config, getConfig } from "../utils/get-config.js";
import { getEnvProxy } from "../utils/get-env-proxy.js";
import { intro, prettifyList } from "../utils/prompt-helpers.js";
import * as p from "../utils/prompts.js";
import { fetchTree, getItemTargetPath, getRegistryIndex, resolveTree } from "../utils/registry";
import { UTILS, UTILS_JS } from "../utils/templates.js";
import { transformImports } from "../utils/transformers.js";
import * as p from "../utils/prompts.js";
import { intro, prettifyList } from "../utils/prompt-helpers.js";
import { getEnvProxy } from "../utils/get-env-proxy.js";
import { detectPM } from "../utils/auto-detect.js";

const highlight = (msg: string) => color.bold.cyan(msg);

Expand All @@ -23,6 +23,7 @@ const updateOptionsSchema = v.object({
cwd: v.string(),
proxy: v.optional(v.string()),
yes: v.boolean(),
pm: v.optional(v.picklist(AGENTS)),
});

type UpdateOptions = v.InferOutput<typeof updateOptionsSchema>;
Expand All @@ -35,6 +36,12 @@ export const update = new Command()
.option("-a, --all", "update all existing components", false)
.option("-y, --yes", "skip confirmation prompt", false)
.option("--proxy <proxy>", "fetch components from registry using a proxy", getEnvProxy())
.addOption(
new Option(
"--pm <name>",
"specify the package manager. if not provided, the tool will auto-detect one based on the current project."
).choices(AGENTS)
)
.action(async (components, opts) => {
intro();

Expand Down Expand Up @@ -225,11 +232,11 @@ async function runUpdate(cwd: string, config: Config, options: UpdateOptions) {
}

// Install dependencies.
const commands = await detectPM(cwd, true);
const commands = !options.pm ? await detectPM(cwd, true) : COMMANDS[options.pm];
if (commands) {
const [pm, add] = commands.add.split(" ") as [string, string];
tasks.push({
title: `${highlight(pm)}: Installing dependencies`,
title: `${highlight(pm)} Installing dependencies`,
enabled: dependencies.size > 0,
async task() {
await execa(pm, [add, ...dependencies], {
Expand Down
14 changes: 11 additions & 3 deletions packages/cli/src/utils/auto-detect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,18 @@ import path from "node:path";
import ignore, { type Ignore } from "ignore";
import { type TsConfigResult, getTsconfig } from "get-tsconfig";
import { detect } from "package-manager-detector";
import { AGENTS, type Agent, COMMANDS } from "package-manager-detector/agents";
import {
type Agent,
AGENTS as _AGENTS,
COMMANDS as _COMMANDS,
} from "package-manager-detector/agents";
import * as p from "./prompts.js";
import { cancel } from "./prompt-helpers.js";

export const AGENTS = _AGENTS.filter((agent) => !agent.includes("@"));

export const COMMANDS = _COMMANDS;

const STYLESHEETS = [
"app.css",
"app.pcss",
Expand Down Expand Up @@ -96,12 +104,12 @@ export function detectLanguage(cwd: string): DetectLanguageResult | undefined {
if (jsConfig !== null) return { type: "jsconfig.json", config: jsConfig };
}

type Options = Array<{ value: Agent | undefined; label: Agent | "None" }>;
export async function detectPM(cwd: string, prompt: boolean) {
type Options = Array<{ value: Agent | undefined; label: Agent | "None" }>;
let { agent } = await detect({ cwd });

if (agent === undefined && prompt) {
const options: Options = AGENTS.filter((agent) => !agent.includes("@")).map((pm) => ({
const options: Options = AGENTS.map((pm) => ({
value: pm,
label: pm,
}));
Expand Down
Loading