From f6276b4e82dfdb5e8999ddbb04d4b503c1b83fe6 Mon Sep 17 00:00:00 2001 From: PunGrumpy <108584943+PunGrumpy@users.noreply.github.com> Date: Sat, 6 Jun 2026 00:25:46 +0700 Subject: [PATCH 1/6] feat(cli): pass ultracite --frameworks through init command plumbing Add framework id parsing helpers and thread selected frameworks from the CLI into ultracite init, including dry-run plan output and runtime guards. --- .../cli/src/commands/shared/command-ui.ts | 14 ++++ .../cli/src/commands/shared/plan-steps.ts | 4 +- .../cli/src/commands/shared/post-scaffold.ts | 12 +++- .../commands/shared/post-setup-pipeline.ts | 9 ++- packages/cli/src/commands/shared/ultracite.ts | 23 +++++++ packages/cli/src/pm-exec.ts | 9 ++- packages/cli/src/ultracite-framework.ts | 65 +++++++++++++++++++ 7 files changed, 130 insertions(+), 6 deletions(-) create mode 100644 packages/cli/src/ultracite-framework.ts diff --git a/packages/cli/src/commands/shared/command-ui.ts b/packages/cli/src/commands/shared/command-ui.ts index 3bc4007..2e8e1fa 100644 --- a/packages/cli/src/commands/shared/command-ui.ts +++ b/packages/cli/src/commands/shared/command-ui.ts @@ -2,6 +2,7 @@ import * as p from "@clack/prompts"; import pc from "picocolors"; import { CLIError } from "../../errors"; import { renderVernoTitle } from "../../ui"; +import type { UltraciteFrameworkId } from "../../ultracite-framework"; import type { UltraciteLinterId } from "../../ultracite-linter"; import type { CommandStepPlan } from "./plan-steps"; @@ -18,6 +19,19 @@ export const requireUltraciteLinter = ( return linter; }; +export const requireUltraciteFrameworks = ( + frameworks: readonly UltraciteFrameworkId[] | undefined, + command: string, +): readonly UltraciteFrameworkId[] => { + if (frameworks === undefined || frameworks.length === 0) { + throw new CLIError( + `Ultracite init requires at least one framework. Pass --frameworks with the ultracite add-on or use interactive ${command}.`, + { code: "ULTRACITE" }, + ); + } + return frameworks; +}; + export const printStepPlanDryRun = (args: { readonly title: string; readonly metaLines: readonly string[]; diff --git a/packages/cli/src/commands/shared/plan-steps.ts b/packages/cli/src/commands/shared/plan-steps.ts index 10236ef..73883f9 100644 --- a/packages/cli/src/commands/shared/plan-steps.ts +++ b/packages/cli/src/commands/shared/plan-steps.ts @@ -5,6 +5,7 @@ import { getShadcnBootstrapCommand, getUltraciteInitCommand, } from "../../pm-exec"; +import type { UltraciteFrameworkId } from "../../ultracite-framework"; import type { UltraciteLinterId } from "../../ultracite-linter"; export interface CommandStepSpec { @@ -129,6 +130,7 @@ export type UltracitePlanMode = readonly kind: "run"; readonly nonInteractive: boolean; readonly linter?: UltraciteLinterId; + readonly frameworks?: readonly UltraciteFrameworkId[]; } | { readonly kind: "already-configured" } | { readonly kind: "skip"; readonly reason: string }; @@ -163,7 +165,7 @@ export const appendUltraciteStep = ( const u = getUltraciteInitCommand( args.packageManager, args.mode.nonInteractive ? "quiet" : "interactive", - { linter: args.mode.linter }, + { frameworks: args.mode.frameworks, linter: args.mode.linter }, ); steps.push({ command: { args: u.args, cwd: args.projectDir, file: u.file }, diff --git a/packages/cli/src/commands/shared/post-scaffold.ts b/packages/cli/src/commands/shared/post-scaffold.ts index 0caa93b..6cde055 100644 --- a/packages/cli/src/commands/shared/post-scaffold.ts +++ b/packages/cli/src/commands/shared/post-scaffold.ts @@ -8,6 +8,7 @@ import { getUltraciteInitCommand, } from "../../pm-exec"; import type { UltraciteInitMode } from "../../pm-exec"; +import type { UltraciteFrameworkId } from "../../ultracite-framework"; import type { UltraciteLinterId } from "../../ultracite-linter"; import { runProcess } from "../../run"; @@ -78,12 +79,19 @@ export const runUltraciteIfEnabled = async ( packageManager: PackageManager, projectDir: string, mode: UltraciteInitMode, - runOptions?: { readonly ciSafe?: boolean; readonly linter?: UltraciteLinterId }, + runOptions?: { + readonly ciSafe?: boolean; + readonly linter?: UltraciteLinterId; + readonly frameworks?: readonly UltraciteFrameworkId[]; + }, ): Promise => { if (!enabled) { return; } - const u = getUltraciteInitCommand(packageManager, mode, { linter: runOptions?.linter }); + const u = getUltraciteInitCommand(packageManager, mode, { + frameworks: runOptions?.frameworks, + linter: runOptions?.linter, + }); const ciSafe = runOptions?.ciSafe ?? mode === "quiet"; await runProcess(u.file, u.args, { ciSafe, cwd: projectDir, stepId: "ultracite" }); }; diff --git a/packages/cli/src/commands/shared/post-setup-pipeline.ts b/packages/cli/src/commands/shared/post-setup-pipeline.ts index 456722b..893e0c2 100644 --- a/packages/cli/src/commands/shared/post-setup-pipeline.ts +++ b/packages/cli/src/commands/shared/post-setup-pipeline.ts @@ -2,8 +2,9 @@ import * as p from "@clack/prompts"; import pc from "picocolors"; import type { PackageManager } from "@vernostudio/template-generator"; import { ensureAppGlobalsBaseLayerAtEnd } from "../../app-globals"; +import type { UltraciteFrameworkId } from "../../ultracite-framework"; import type { UltraciteLinterId } from "../../ultracite-linter"; -import { requireUltraciteLinter } from "./command-ui"; +import { requireUltraciteFrameworks, requireUltraciteLinter } from "./command-ui"; import { runInstallIfEnabled, runShadcnIfEnabled, runUltraciteIfEnabled } from "./post-scaffold"; const SHADCN_BANNER_BASE = "bootstrap (init/apply) + add --all"; @@ -22,6 +23,7 @@ export interface PostSetupPipelineContext { readonly enabled: boolean; readonly nonInteractive: boolean; readonly linter?: UltraciteLinterId; + readonly frameworks?: readonly UltraciteFrameworkId[]; }; readonly commandName: "create" | "init"; readonly writeManifest: () => Promise; @@ -64,6 +66,7 @@ export const runPostSetupPipeline = async (ctx: PostSetupPipelineContext): Promi if (ctx.ultracite.enabled) { const linter = requireUltraciteLinter(ctx.ultracite.linter, ctx.commandName); + const frameworks = requireUltraciteFrameworks(ctx.ultracite.frameworks, ctx.commandName); await p.tasks([ { @@ -71,6 +74,7 @@ export const runPostSetupPipeline = async (ctx: PostSetupPipelineContext): Promi task: async (message) => { message?.("ultracite init (quiet)…"); await runUltraciteIfEnabled(true, ctx.packageManager, ctx.projectDir, "quiet", { + frameworks, linter, }); return "ultracite init complete"; @@ -81,10 +85,11 @@ export const runPostSetupPipeline = async (ctx: PostSetupPipelineContext): Promi if (!ctx.ultracite.nonInteractive) { process.stdout.write( - `\n${pc.cyan("ultracite")} — Linter: ${linter}. Continue in Ultracite for frameworks, editors, and hooks.\n\n`, + `\n${pc.cyan("ultracite")} — Linter: ${linter}. Frameworks: ${frameworks.join(", ")}. Continue in Ultracite for editors and hooks.\n\n`, ); await runUltraciteIfEnabled(true, ctx.packageManager, ctx.projectDir, "interactive", { ciSafe: false, + frameworks, linter, }); } diff --git a/packages/cli/src/commands/shared/ultracite.ts b/packages/cli/src/commands/shared/ultracite.ts index 799b16d..a8e0666 100644 --- a/packages/cli/src/commands/shared/ultracite.ts +++ b/packages/cli/src/commands/shared/ultracite.ts @@ -1,5 +1,7 @@ import { ULTRACITE_LINTER_IDS, isUltraciteLinterId } from "../../ultracite-linter"; import type { UltraciteLinterId } from "../../ultracite-linter"; +import { parseUltraciteFrameworksArg } from "../../ultracite-framework"; +import type { UltraciteFrameworkId } from "../../ultracite-framework"; export const parseUltraciteLinterFlag = ( options: { readonly linter?: string }, @@ -20,3 +22,24 @@ export const parseUltraciteLinterFlag = ( } return undefined; }; + +export const parseUltraciteFrameworksFlag = ( + options: { readonly frameworks?: string }, + ultraciteOn: boolean, +): UltraciteFrameworkId[] | undefined => { + if (!ultraciteOn) { + if (options.frameworks !== undefined && options.frameworks.length > 0) { + throw new Error("--frameworks requires ultracite in --addons."); + } + return undefined; + } + const raw = options.frameworks; + if (raw === undefined || raw.length === 0) { + return undefined; + } + const parsed = parseUltraciteFrameworksArg(raw); + if (parsed.length === 0) { + throw new Error("At least one framework is required for --frameworks."); + } + return parsed; +}; diff --git a/packages/cli/src/pm-exec.ts b/packages/cli/src/pm-exec.ts index d9fd3a4..1f9cbd1 100644 --- a/packages/cli/src/pm-exec.ts +++ b/packages/cli/src/pm-exec.ts @@ -1,5 +1,6 @@ import type { PackageManager } from "@vernostudio/template-generator"; import { getShadcnExecSpec, getUltraciteExecSpec } from "@vernostudio/template-generator"; +import type { UltraciteFrameworkId } from "./ultracite-framework"; import type { UltraciteLinterId } from "./ultracite-linter"; export const getPmInstallCommand = ( @@ -58,7 +59,10 @@ export type UltraciteInitMode = "interactive" | "quiet"; export const getUltraciteInitCommand = ( pm: PackageManager, mode: UltraciteInitMode, - options?: { readonly linter?: UltraciteLinterId }, + options?: { + readonly linter?: UltraciteLinterId; + readonly frameworks?: readonly UltraciteFrameworkId[]; + }, ): { readonly file: string; readonly args: readonly string[]; @@ -68,6 +72,9 @@ export const getUltraciteInitCommand = ( if (options?.linter !== undefined) { parts.push("--linter", options.linter); } + if (options?.frameworks !== undefined && options.frameworks.length > 0) { + parts.push("--frameworks", ...options.frameworks); + } if (mode === "quiet") { parts.push("--quiet"); } diff --git a/packages/cli/src/ultracite-framework.ts b/packages/cli/src/ultracite-framework.ts new file mode 100644 index 0000000..ea51f3e --- /dev/null +++ b/packages/cli/src/ultracite-framework.ts @@ -0,0 +1,65 @@ +import type { FrontendId } from "@vernostudio/template-generator"; + +/** Values for `ultracite init --frameworks` (Ultracite preset extends). */ +export type UltraciteFrameworkId = + | "angular" + | "astro" + | "nestjs" + | "next" + | "qwik" + | "react" + | "remix" + | "solid" + | "svelte" + | "tanstack" + | "vue"; + +export const ULTRACITE_FRAMEWORK_IDS: readonly UltraciteFrameworkId[] = [ + "react", + "next", + "solid", + "vue", + "svelte", + "qwik", + "remix", + "tanstack", + "angular", + "astro", + "nestjs", +] as const; + +/** Default `--frameworks` for Verno Next.js scaffold when the flag is omitted in `-y` mode. */ +export const DEFAULT_ULTRACITE_FRAMEWORKS: readonly UltraciteFrameworkId[] = ["react", "next"]; + +export const isUltraciteFrameworkId = (value: string | undefined): value is UltraciteFrameworkId => + value !== undefined && (ULTRACITE_FRAMEWORK_IDS as readonly string[]).includes(value); + +export const defaultUltraciteFrameworksFromFrontend = ( + frontend: FrontendId, +): readonly UltraciteFrameworkId[] => { + if (frontend === "next") { + return DEFAULT_ULTRACITE_FRAMEWORKS; + } + return DEFAULT_ULTRACITE_FRAMEWORKS; +}; + +/** Parses comma- or space-separated framework ids; dedupes while preserving order. */ +export const parseUltraciteFrameworksArg = (raw: string | undefined): UltraciteFrameworkId[] => { + if (raw === undefined || raw.length === 0) { + return []; + } + const parts = raw + .split(/[,\s]+/u) + .map((part) => part.trim()) + .filter((part) => part.length > 0); + const out: UltraciteFrameworkId[] = []; + for (const part of parts) { + if (!isUltraciteFrameworkId(part)) { + throw new Error(`Invalid framework "${part}". Use: ${ULTRACITE_FRAMEWORK_IDS.join(" | ")}`); + } + if (!out.includes(part)) { + out.push(part); + } + } + return out; +}; From 8851f91d7dcc5e123c8fef3025a321a9bd5abb31 Mon Sep 17 00:00:00 2001 From: PunGrumpy <108584943+PunGrumpy@users.noreply.github.com> Date: Sat, 6 Jun 2026 00:25:53 +0700 Subject: [PATCH 2/6] feat(cli): add --frameworks flag to create and init commands Resolve ultracite framework presets from flags, interactive wizards, or -y defaults, then persist the selection in the Verno manifest. --- packages/cli/src/commands/create/args.ts | 19 +++++++++-- packages/cli/src/commands/create/index.ts | 2 ++ packages/cli/src/commands/create/plan.ts | 7 +++- packages/cli/src/commands/create/prompts.ts | 36 +++++++++++++++++--- packages/cli/src/commands/init/args.ts | 17 +++++++-- packages/cli/src/commands/init/index.ts | 1 + packages/cli/src/commands/init/plan.ts | 1 + packages/cli/src/commands/init/prompts.ts | 35 ++++++++++++++++++- packages/cli/src/commands/shared/manifest.ts | 4 +++ packages/cli/src/index.ts | 8 +++++ 10 files changed, 118 insertions(+), 12 deletions(-) diff --git a/packages/cli/src/commands/create/args.ts b/packages/cli/src/commands/create/args.ts index aca2ead..01973e2 100644 --- a/packages/cli/src/commands/create/args.ts +++ b/packages/cli/src/commands/create/args.ts @@ -7,6 +7,8 @@ import type { import { FRONTENDS, PACKAGE_IDS } from "@vernostudio/template-generator"; import { DEFAULT_ULTRACITE_LINTER } from "../../ultracite-linter"; import type { UltraciteLinterId } from "../../ultracite-linter"; +import { defaultUltraciteFrameworksFromFrontend } from "../../ultracite-framework"; +import type { UltraciteFrameworkId } from "../../ultracite-framework"; import { parseAddonsArg } from "../shared/addons"; import { splitCommaList } from "../shared/comma-list"; import { @@ -16,7 +18,7 @@ import { PACKAGE_MANAGERS, } from "../shared/input-primitives"; import type { UiMode } from "../shared/input-primitives"; -import { parseUltraciteLinterFlag } from "../shared/ultracite"; +import { parseUltraciteFrameworksFlag, parseUltraciteLinterFlag } from "../shared/ultracite"; export { DEFAULT_SHADCN_PRESET, isPackageManager, isUiMode, PACKAGE_MANAGERS, type UiMode }; @@ -69,6 +71,8 @@ export interface CreateCommandOptions { readonly shadcnPreset?: string; /** `ultracite init --linter` when the ultracite add-on is enabled. */ readonly linter?: string; + /** `ultracite init --frameworks` when the ultracite add-on is enabled. */ + readonly frameworks?: string; } export const toCreateCommandOptions = (raw: { @@ -85,9 +89,11 @@ export const toCreateCommandOptions = (raw: { readonly ui?: string; readonly shadcnPreset?: string; readonly linter?: string; + readonly frameworks?: string; }): CreateCommandOptions => ({ addons: raw.addons, dryRun: raw.dryRun ?? false, + frameworks: raw.frameworks, frontend: raw.frontend, linter: raw.linter, noGit: raw.noGit ?? false, @@ -115,6 +121,8 @@ export interface ResolvedCreateInputs { readonly packages: readonly PackageId[]; /** Always set when ultracite add-on runs: CLI `--linter`, or interactive wizard, or `-y` default. */ readonly ultraciteLinter?: UltraciteLinterId; + /** Always set when ultracite add-on runs: CLI `--frameworks`, interactive wizard, or `-y` default. */ + readonly ultraciteFrameworks?: readonly UltraciteFrameworkId[]; readonly ui: UiMode; readonly useShadcn: boolean; } @@ -196,8 +204,12 @@ export const resolveCreateInputsNonInteractive = ( const packageManager = resolvePackageManagerNonInteractive(options); const ui = resolveUiNonInteractive(options); const ultraciteOn = addons.includes("ultracite"); - const flagged = parseUltraciteLinterFlag(options, ultraciteOn); - const ultraciteLinter = ultraciteOn ? (flagged ?? DEFAULT_ULTRACITE_LINTER) : undefined; + const flaggedLinter = parseUltraciteLinterFlag(options, ultraciteOn); + const ultraciteLinter = ultraciteOn ? (flaggedLinter ?? DEFAULT_ULTRACITE_LINTER) : undefined; + const flaggedFrameworks = parseUltraciteFrameworksFlag(options, ultraciteOn); + const ultraciteFrameworks = ultraciteOn + ? (flaggedFrameworks ?? defaultUltraciteFrameworksFromFrontend(frontend)) + : undefined; return { addons, @@ -211,6 +223,7 @@ export const resolveCreateInputsNonInteractive = ( runUltracite: ultraciteOn, shadcnPreset: options.shadcnPreset ?? DEFAULT_SHADCN_PRESET, ui, + ultraciteFrameworks, ultraciteLinter, useShadcn: ui === "shadcn" && !options.skipShadcn, }; diff --git a/packages/cli/src/commands/create/index.ts b/packages/cli/src/commands/create/index.ts index a9c8452..31a3fe0 100644 --- a/packages/cli/src/commands/create/index.ts +++ b/packages/cli/src/commands/create/index.ts @@ -123,6 +123,7 @@ export const runCreate = async (args: { }, ultracite: { enabled: resolved.runUltracite, + frameworks: resolved.ultraciteFrameworks, linter: resolved.ultraciteLinter, nonInteractive: resolved.nonInteractive, }, @@ -153,6 +154,7 @@ export const runCreate = async (args: { skip_git: !resolved.doGit, skip_install: !resolved.doInstall, ui: resolved.ui, + ultracite_frameworks: resolved.ultraciteFrameworks?.join(","), }); printDoneNextSteps(`Project "${resolved.name}" is ready.`, nextSteps); diff --git a/packages/cli/src/commands/create/plan.ts b/packages/cli/src/commands/create/plan.ts index c87c5d5..953eb0d 100644 --- a/packages/cli/src/commands/create/plan.ts +++ b/packages/cli/src/commands/create/plan.ts @@ -68,7 +68,12 @@ export const buildCreatePlan = ( appendUltraciteStep(steps, { id: "ultracite", mode: resolved.runUltracite - ? { kind: "run", linter: resolved.ultraciteLinter, nonInteractive: resolved.nonInteractive } + ? { + frameworks: resolved.ultraciteFrameworks, + kind: "run", + linter: resolved.ultraciteLinter, + nonInteractive: resolved.nonInteractive, + } : { kind: "skip", reason: "Skipped (ultracite add-on off, --skip-ultracite, or declined)" }, packageManager: resolved.packageManager, projectDir, diff --git a/packages/cli/src/commands/create/prompts.ts b/packages/cli/src/commands/create/prompts.ts index 302e9eb..c0abad0 100644 --- a/packages/cli/src/commands/create/prompts.ts +++ b/packages/cli/src/commands/create/prompts.ts @@ -4,7 +4,11 @@ import type { AddonId, PackageId, PackageManager } from "@vernostudio/template-g import { UserCancelledError } from "../../errors"; import { renderVernoTitle } from "../../ui"; import { parseAddonsArg } from "../shared/addons"; -import { parseUltraciteLinterFlag } from "../shared/ultracite"; +import { parseUltraciteFrameworksFlag, parseUltraciteLinterFlag } from "../shared/ultracite"; +import { DEFAULT_ULTRACITE_FRAMEWORKS, ULTRACITE_FRAMEWORK_IDS } from "../../ultracite-framework"; +import type { UltraciteFrameworkId } from "../../ultracite-framework"; +import { DEFAULT_ULTRACITE_LINTER } from "../../ultracite-linter"; +import type { UltraciteLinterId } from "../../ultracite-linter"; import { DEFAULT_SHADCN_PRESET, DEFAULT_WORKSPACE_PACKAGES, @@ -15,8 +19,6 @@ import { parsePackagesArg, } from "./args"; import type { CreateCommandOptions, ResolvedCreateInputs, UiMode } from "./args"; -import { DEFAULT_ULTRACITE_LINTER } from "../../ultracite-linter"; -import type { UltraciteLinterId } from "../../ultracite-linter"; const exitOnCancel = (value: unknown): void => { if (p.isCancel(value)) { @@ -244,6 +246,30 @@ const readUltraciteLinter = async ( ); }; +const readUltraciteFrameworks = async ( + options: CreateCommandOptions, + ultraciteOn: boolean, +): Promise => { + const fromFlag = parseUltraciteFrameworksFlag(options, ultraciteOn); + if (!ultraciteOn) { + return undefined; + } + if (fromFlag !== undefined) { + return fromFlag; + } + return assertValue( + await p.multiselect({ + initialValues: [...DEFAULT_ULTRACITE_FRAMEWORKS], + message: "Ultracite frameworks (passed as --frameworks)", + options: ULTRACITE_FRAMEWORK_IDS.map((id) => ({ + label: id, + value: id, + })), + required: true, + }), + ) as UltraciteFrameworkId[]; +}; + export const runInteractiveCreateWizard = async (args: { readonly name?: string; readonly options: CreateCommandOptions; @@ -267,6 +293,7 @@ export const runInteractiveCreateWizard = async (args: { const packages = await readPackagesInteractive(options, turborepoOn); const ultraciteOn = addons.includes("ultracite"); const ultraciteLinter = await readUltraciteLinter(options, ultraciteOn); + const ultraciteFrameworks = await readUltraciteFrameworks(options, ultraciteOn); p.log.step("Tooling — package manager and UI"); const packageManager = await readPackageManager(options); @@ -280,7 +307,7 @@ export const runInteractiveCreateWizard = async (args: { const useShadcn = ui === "shadcn" && !hasSkipShadcn; const runUltracite = ultraciteOn; const ultraciteSummary = runUltracite - ? `yes — ultracite init --linter ${ultraciteLinter ?? DEFAULT_ULTRACITE_LINTER} (after install)` + ? `yes — ultracite init --linter ${ultraciteLinter ?? DEFAULT_ULTRACITE_LINTER} --frameworks ${(ultraciteFrameworks ?? DEFAULT_ULTRACITE_FRAMEWORKS).join(" ")} (after install)` : "no"; const summaryLines: string[] = [ @@ -322,6 +349,7 @@ export const runInteractiveCreateWizard = async (args: { runUltracite, shadcnPreset, ui, + ultraciteFrameworks, ultraciteLinter, useShadcn, }; diff --git a/packages/cli/src/commands/init/args.ts b/packages/cli/src/commands/init/args.ts index 6040cfa..cee51b6 100644 --- a/packages/cli/src/commands/init/args.ts +++ b/packages/cli/src/commands/init/args.ts @@ -1,5 +1,7 @@ import { DEFAULT_ULTRACITE_LINTER } from "../../ultracite-linter"; import type { UltraciteLinterId } from "../../ultracite-linter"; +import { defaultUltraciteFrameworksFromFrontend } from "../../ultracite-framework"; +import type { UltraciteFrameworkId } from "../../ultracite-framework"; import type { AddonId, PackageManager } from "@vernostudio/template-generator"; import { parseAddonsArg } from "../shared/addons"; import { @@ -9,7 +11,7 @@ import { PACKAGE_MANAGERS, } from "../shared/input-primitives"; import type { UiMode } from "../shared/input-primitives"; -import { parseUltraciteLinterFlag } from "../shared/ultracite"; +import { parseUltraciteFrameworksFlag, parseUltraciteLinterFlag } from "../shared/ultracite"; export { DEFAULT_SHADCN_PRESET, isPackageManager, isUiMode, PACKAGE_MANAGERS, type UiMode }; @@ -24,6 +26,7 @@ export interface InitCommandOptions { readonly ui?: string; readonly shadcnPreset?: string; readonly linter?: string; + readonly frameworks?: string; readonly packageManager?: string; } @@ -37,10 +40,12 @@ export const toInitCommandOptions = (raw: { readonly ui?: string; readonly shadcnPreset?: string; readonly linter?: string; + readonly frameworks?: string; readonly packageManager?: string; }): InitCommandOptions => ({ addons: raw.addons, dryRun: raw.dryRun ?? false, + frameworks: raw.frameworks, linter: raw.linter, noInstall: raw.noInstall ?? false, packageManager: raw.packageManager, @@ -59,6 +64,7 @@ export interface ResolvedInitInputs { readonly runUltracite: boolean; readonly shadcnPreset: string; readonly ultraciteLinter?: UltraciteLinterId; + readonly ultraciteFrameworks?: readonly UltraciteFrameworkId[]; readonly ui: UiMode; readonly useShadcn: boolean; readonly addons: AddonId[]; @@ -103,8 +109,12 @@ export const resolveInitInputsNonInteractive = ( const packageManager = resolvePackageManagerNonInteractive(options); const ui = resolveUiNonInteractive(options); const ultraciteOn = addons.includes("ultracite"); - const flagged = parseUltraciteLinterFlag(options, ultraciteOn); - const ultraciteLinter = ultraciteOn ? (flagged ?? DEFAULT_ULTRACITE_LINTER) : undefined; + const flaggedLinter = parseUltraciteLinterFlag(options, ultraciteOn); + const ultraciteLinter = ultraciteOn ? (flaggedLinter ?? DEFAULT_ULTRACITE_LINTER) : undefined; + const flaggedFrameworks = parseUltraciteFrameworksFlag(options, ultraciteOn); + const ultraciteFrameworks = ultraciteOn + ? (flaggedFrameworks ?? defaultUltraciteFrameworksFromFrontend("next")) + : undefined; const useShadcn = ui === "shadcn" && !options.skipShadcn; @@ -116,6 +126,7 @@ export const resolveInitInputsNonInteractive = ( runUltracite: ultraciteOn, shadcnPreset: options.shadcnPreset ?? DEFAULT_SHADCN_PRESET, ui, + ultraciteFrameworks, ultraciteLinter, useShadcn, }; diff --git a/packages/cli/src/commands/init/index.ts b/packages/cli/src/commands/init/index.ts index f0e037f..a2b15ee 100644 --- a/packages/cli/src/commands/init/index.ts +++ b/packages/cli/src/commands/init/index.ts @@ -126,6 +126,7 @@ export const runInit = async (args: { options: InitCommandOptions }): Promise { if (resolved.runUltracite && !detected.hasUltracite) { return { + frameworks: resolved.ultraciteFrameworks, kind: "run", linter: resolved.ultraciteLinter, nonInteractive: resolved.nonInteractive, diff --git a/packages/cli/src/commands/init/prompts.ts b/packages/cli/src/commands/init/prompts.ts index 8560476..373ab73 100644 --- a/packages/cli/src/commands/init/prompts.ts +++ b/packages/cli/src/commands/init/prompts.ts @@ -3,7 +3,9 @@ import pc from "picocolors"; import { UserCancelledError } from "../../errors"; import { renderVernoTitle } from "../../ui"; import { parseAddonsArg } from "../shared/addons"; -import { parseUltraciteLinterFlag } from "../shared/ultracite"; +import { parseUltraciteFrameworksFlag, parseUltraciteLinterFlag } from "../shared/ultracite"; +import { DEFAULT_ULTRACITE_FRAMEWORKS, ULTRACITE_FRAMEWORK_IDS } from "../../ultracite-framework"; +import type { UltraciteFrameworkId } from "../../ultracite-framework"; import type { AddonId, PackageManager } from "@vernostudio/template-generator"; import { DEFAULT_ULTRACITE_LINTER } from "../../ultracite-linter"; import type { UltraciteLinterId } from "../../ultracite-linter"; @@ -158,6 +160,30 @@ const readUltraciteLinter = async ( ); }; +const readUltraciteFrameworks = async ( + options: InitCommandOptions, + ultraciteOn: boolean, +): Promise => { + const fromFlag = parseUltraciteFrameworksFlag(options, ultraciteOn); + if (!ultraciteOn) { + return undefined; + } + if (fromFlag !== undefined) { + return fromFlag; + } + return assertValue( + await p.multiselect({ + initialValues: [...DEFAULT_ULTRACITE_FRAMEWORKS], + message: "Ultracite frameworks (passed as --frameworks)", + options: ULTRACITE_FRAMEWORK_IDS.map((id) => ({ + label: id, + value: id, + })), + required: true, + }), + ) as UltraciteFrameworkId[]; +}; + const addonsSummary = (addons: readonly AddonId[]): string => addons.length > 0 ? addons.join(", ") : "none"; @@ -202,6 +228,9 @@ export const runInteractiveInitWizard = async (args: { const doInstall = await readDoInstall(hasNoInstall); const ultraciteLinter = ultraciteOn ? await readUltraciteLinter(options, ultraciteOn) : undefined; + const ultraciteFrameworks = ultraciteOn + ? await readUltraciteFrameworks(options, ultraciteOn) + : undefined; const useShadcn = ui === "shadcn" && !hasSkipShadcn; @@ -214,6 +243,9 @@ export const runInteractiveInitWizard = async (args: { if (ultraciteOn) { summaryLines.push(`Ultracite linter: ${ultraciteLinter ?? DEFAULT_ULTRACITE_LINTER}`); + summaryLines.push( + `Ultracite frameworks: ${(ultraciteFrameworks ?? DEFAULT_ULTRACITE_FRAMEWORKS).join(", ")}`, + ); } p.log.step("Review — confirm to continue"); @@ -236,6 +268,7 @@ export const runInteractiveInitWizard = async (args: { runUltracite: ultraciteOn, shadcnPreset, ui, + ultraciteFrameworks, ultraciteLinter, useShadcn, }; diff --git a/packages/cli/src/commands/shared/manifest.ts b/packages/cli/src/commands/shared/manifest.ts index 3cfd22f..af8a67c 100644 --- a/packages/cli/src/commands/shared/manifest.ts +++ b/packages/cli/src/commands/shared/manifest.ts @@ -7,6 +7,7 @@ import type { PackageId, PackageManager, } from "@vernostudio/template-generator"; +import type { UltraciteFrameworkId } from "../../ultracite-framework"; import type { UltraciteLinterId } from "../../ultracite-linter"; import { readCliPackageVersion } from "../../cli-version"; import { VERNO_MANIFEST_DIR } from "../../constants"; @@ -26,6 +27,7 @@ export interface VernoManifest { readonly studio: "Verno Studio"; readonly ui: UiMode; readonly ultraciteLinter?: UltraciteLinterId; + readonly ultraciteFrameworks?: readonly UltraciteFrameworkId[]; } export const mergeManifestAddons = ( @@ -51,6 +53,7 @@ export const buildVernoManifestForCreate = (args: { shadcnPreset: args.resolved.useShadcn ? args.resolved.shadcnPreset : undefined, studio: "Verno Studio", ui: args.resolved.ui, + ultraciteFrameworks: args.resolved.runUltracite ? args.resolved.ultraciteFrameworks : undefined, ultraciteLinter: args.resolved.runUltracite ? args.resolved.ultraciteLinter : undefined, }); @@ -70,6 +73,7 @@ export const buildVernoManifestForInit = (args: { shadcnPreset: args.resolved.useShadcn ? args.resolved.shadcnPreset : undefined, studio: "Verno Studio", ui: args.resolved.ui, + ultraciteFrameworks: args.resolved.runUltracite ? args.resolved.ultraciteFrameworks : undefined, ultraciteLinter: args.resolved.runUltracite ? args.resolved.ultraciteLinter : undefined, }); diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index e5e0cf4..4fbd76a 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -31,6 +31,10 @@ program "--linter ", "biome | oxlint | eslint (ultracite add-on; -y default oxlint; interactive wizard asks unless set)", ) + .option( + "--frameworks ", + "Ultracite preset extends: react, next, solid, vue, ... (ultracite add-on; -y defaults react,next; interactive wizard asks)", + ) .option("--no-install", "Skip dependency install") .option("--no-git", "Skip git init") .option("--skip-shadcn", "Skip shadcn bootstrap") @@ -54,6 +58,10 @@ program "--linter ", "biome | oxlint | eslint (ultracite add-on; interactive wizard asks unless set)", ) + .option( + "--frameworks ", + "Ultracite preset extends: react, next, solid, vue, ... (ultracite add-on; -y defaults react,next; interactive wizard asks)", + ) .option("-p, --package-manager ", "bun | pnpm | npm") .option("-y, --yes", "Non-interactive mode", false) .option("--dry-run", "Print the plan without writing files", false) From 03189dc869b715f435b8add11b92f0e9cbffb915 Mon Sep 17 00:00:00 2001 From: PunGrumpy <108584943+PunGrumpy@users.noreply.github.com> Date: Sat, 6 Jun 2026 00:25:59 +0700 Subject: [PATCH 3/6] test(cli): cover ultracite --frameworks parsing and init wiring Add unit tests for framework id parsing, flag validation, pm-exec args, and create/init input resolution. --- packages/cli/__tests__/create-args.test.ts | 18 ++++++++ packages/cli/__tests__/create-plan.test.ts | 4 ++ packages/cli/__tests__/init-args.test.ts | 28 ++++++++++++ packages/cli/__tests__/pm-exec.test.ts | 44 ++++++++++++++++++- .../cli/__tests__/ultracite-framework.test.ts | 41 +++++++++++++++++ 5 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 packages/cli/__tests__/ultracite-framework.test.ts diff --git a/packages/cli/__tests__/create-args.test.ts b/packages/cli/__tests__/create-args.test.ts index feb15f8..9c43c7b 100644 --- a/packages/cli/__tests__/create-args.test.ts +++ b/packages/cli/__tests__/create-args.test.ts @@ -63,9 +63,27 @@ describe("resolveCreateInputsNonInteractive", () => { expect(r.addons).toContain("ultracite"); expect(r.runUltracite).toBe(true); expect(r.ultraciteLinter).toBe("oxlint"); + expect(r.ultraciteFrameworks).toEqual(["react", "next"]); expect(r.nonInteractive).toBe(true); }); + test("accepts --frameworks react,next with ultracite", () => { + const r = resolveCreateInputsNonInteractive( + "u", + toCreateCommandOptions({ addons: "ultracite", frameworks: "react,next", yes: true }), + ); + expect(r.ultraciteFrameworks).toEqual(["react", "next"]); + }); + + test("rejects --frameworks without ultracite add-on", () => { + expect(() => + resolveCreateInputsNonInteractive( + "n", + toCreateCommandOptions({ addons: "turborepo", frameworks: "react", yes: true }), + ), + ).toThrow("--frameworks requires ultracite"); + }); + test("accepts --linter oxlint with ultracite", () => { const r = resolveCreateInputsNonInteractive( "u", diff --git a/packages/cli/__tests__/create-plan.test.ts b/packages/cli/__tests__/create-plan.test.ts index cb3f833..d1d2e5f 100644 --- a/packages/cli/__tests__/create-plan.test.ts +++ b/packages/cli/__tests__/create-plan.test.ts @@ -24,6 +24,9 @@ describe("buildCreatePlan", () => { const ultra = steps.find((s) => s.id === "ultracite")?.command; expect(ultra?.args).toContain("--linter"); expect(ultra?.args).toContain("oxlint"); + expect(ultra?.args).toContain("--frameworks"); + expect(ultra?.args).toContain("react"); + expect(ultra?.args).toContain("next"); }); test("interactive create passes --linter from resolved inputs (wizard or future flags)", () => { @@ -39,6 +42,7 @@ describe("buildCreatePlan", () => { runUltracite: true, shadcnPreset: "nova", ui: "shadcn", + ultraciteFrameworks: ["react", "next"], ultraciteLinter: "oxlint", useShadcn: true, }; diff --git a/packages/cli/__tests__/init-args.test.ts b/packages/cli/__tests__/init-args.test.ts index f279237..263f2b4 100644 --- a/packages/cli/__tests__/init-args.test.ts +++ b/packages/cli/__tests__/init-args.test.ts @@ -70,9 +70,37 @@ describe("resolveInitInputsNonInteractive", () => { expect(r.addons).toContain("ultracite"); expect(r.runUltracite).toBe(true); expect(r.ultraciteLinter).toBe("oxlint"); + expect(r.ultraciteFrameworks).toEqual(["react", "next"]); expect(r.nonInteractive).toBe(true); }); + test("accepts --frameworks react next with ultracite", () => { + const r = resolveInitInputsNonInteractive({ + addons: "ultracite", + dryRun: false, + frameworks: "react next", + noInstall: false, + skipShadcn: false, + skipUltracite: false, + yes: true, + }); + expect(r.ultraciteFrameworks).toEqual(["react", "next"]); + }); + + test("rejects --frameworks without ultracite add-on", () => { + expect(() => + resolveInitInputsNonInteractive({ + addons: "turborepo", + dryRun: false, + frameworks: "react", + noInstall: false, + skipShadcn: false, + skipUltracite: false, + yes: true, + }), + ).toThrow("--frameworks requires ultracite"); + }); + test("accepts --linter oxlint with ultracite", () => { const r = resolveInitInputsNonInteractive({ addons: "ultracite", diff --git a/packages/cli/__tests__/pm-exec.test.ts b/packages/cli/__tests__/pm-exec.test.ts index b6b3492..7a2176a 100644 --- a/packages/cli/__tests__/pm-exec.test.ts +++ b/packages/cli/__tests__/pm-exec.test.ts @@ -60,13 +60,55 @@ describe("getUltraciteInitCommand", () => { expect(cmd.args).toEqual(["dlx", spec, "init", "--pm", "pnpm", "--linter", "biome", "--quiet"]); }); - test("interactive mode: no --quiet; optional --linter", () => { + test("quiet mode with frameworks passes --frameworks before --quiet", () => { + const spec = getUltraciteExecSpec(); + const cmd = getUltraciteInitCommand("pnpm", "quiet", { + frameworks: ["react", "next"], + linter: "oxlint", + }); + expect(cmd.args).toEqual([ + "dlx", + spec, + "init", + "--pm", + "pnpm", + "--linter", + "oxlint", + "--frameworks", + "react", + "next", + "--quiet", + ]); + }); + + test("interactive mode: no --quiet; optional --linter and --frameworks", () => { const spec = getUltraciteExecSpec(); const cmd = getUltraciteInitCommand("bun", "interactive", { linter: "oxlint" }); expect(cmd.args).toEqual(["x", spec, "init", "--pm", "bun", "--linter", "oxlint"]); expect(cmd.args).not.toContain("--quiet"); }); + test("interactive mode with frameworks passes --frameworks", () => { + const spec = getUltraciteExecSpec(); + const cmd = getUltraciteInitCommand("bun", "interactive", { + frameworks: ["react", "next"], + linter: "oxlint", + }); + expect(cmd.args).toEqual([ + "x", + spec, + "init", + "--pm", + "bun", + "--linter", + "oxlint", + "--frameworks", + "react", + "next", + ]); + expect(cmd.args).not.toContain("--quiet"); + }); + test("interactive mode without linter omits --linter so Ultracite can prompt", () => { const spec = getUltraciteExecSpec(); const cmd = getUltraciteInitCommand("bun", "interactive"); diff --git a/packages/cli/__tests__/ultracite-framework.test.ts b/packages/cli/__tests__/ultracite-framework.test.ts new file mode 100644 index 0000000..f8edda3 --- /dev/null +++ b/packages/cli/__tests__/ultracite-framework.test.ts @@ -0,0 +1,41 @@ +import { describe, expect, test } from "bun:test"; +import { + DEFAULT_ULTRACITE_FRAMEWORKS, + defaultUltraciteFrameworksFromFrontend, + parseUltraciteFrameworksArg, +} from "../src/ultracite-framework"; +import { parseUltraciteFrameworksFlag } from "../src/commands/shared/ultracite"; + +describe("parseUltraciteFrameworksArg", () => { + test("parses comma-separated frameworks and dedupes", () => { + expect(parseUltraciteFrameworksArg("react,next,react")).toEqual(["react", "next"]); + }); + + test("parses space-separated frameworks", () => { + expect(parseUltraciteFrameworksArg("react next")).toEqual(["react", "next"]); + }); + + test("rejects unknown framework ids", () => { + expect(() => parseUltraciteFrameworksArg("react,unknown")).toThrow( + 'Invalid framework "unknown"', + ); + }); +}); + +describe("defaultUltraciteFrameworksFromFrontend", () => { + test("defaults to react and next for next frontend", () => { + expect(defaultUltraciteFrameworksFromFrontend("next")).toEqual(DEFAULT_ULTRACITE_FRAMEWORKS); + }); +}); + +describe("parseUltraciteFrameworksFlag", () => { + test("returns undefined when ultracite is off and flag omitted", () => { + expect(parseUltraciteFrameworksFlag({}, false)).toBeUndefined(); + }); + + test("throws when flag is set without ultracite add-on", () => { + expect(() => parseUltraciteFrameworksFlag({ frameworks: "react" }, false)).toThrow( + "--frameworks requires ultracite", + ); + }); +}); From 6c0dbf30a4ad54a6606612e3e9be443ef268011d Mon Sep 17 00:00:00 2001 From: PunGrumpy <108584943+PunGrumpy@users.noreply.github.com> Date: Sat, 6 Jun 2026 00:26:04 +0700 Subject: [PATCH 4/6] docs(cli): document ultracite --frameworks flag Describe framework preset selection for create and init, including -y defaults and supported framework ids. --- packages/cli/README.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/cli/README.md b/packages/cli/README.md index c653e90..b95b43b 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -24,13 +24,13 @@ bun x @vernostudio/cli create Spin up a new project from the stack this repo ships. Pick add-ons, answer prompts, or pass **`--yes`**. -| Mode | What you get | -| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------ | -| **Next.js app** | Single-app layout: Next.js + TypeScript, scoped as `@/…` | -| **Turborepo** | Monorepo root with `apps/web` and optional `packages/*` | -| **Workspace packages** | `typescript-config` and/or `design-system` (when Turborepo is on; interactive default is both) | -| **shadcn** | `shadcn init` (preset `nova` by default) + `shadcn add --all` — app root, or `packages/design-system` when that package exists | -| **Ultracite** | `ultracite init` with linter **`oxlint`**, **`biome`**, or **`eslint`** (`ultracite init --linter`) | +| Mode | What you get | +| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Next.js app** | Single-app layout: Next.js + TypeScript, scoped as `@/…` | +| **Turborepo** | Monorepo root with `apps/web` and optional `packages/*` | +| **Workspace packages** | `typescript-config` and/or `design-system` (when Turborepo is on; interactive default is both) | +| **shadcn** | `shadcn init` (preset `nova` by default) + `shadcn add --all` — app root, or `packages/design-system` when that package exists | +| **Ultracite** | `ultracite init` with linter **`oxlint`**, **`biome`**, or **`eslint`** and frameworks such as **`react`**, **`next`** (`--linter`, `--frameworks`) | ### Non-interactive and dry runs @@ -73,6 +73,7 @@ UI & shadcn: Ultracite: --linter biome | oxlint | eslint (needs ultracite in --addons; -y defaults to oxlint) + --frameworks react, next, solid, vue, ... (needs ultracite; -y defaults react,next) --skip-ultracite Skip ultracite add-on and ultracite init Install & git: From 550d784980e9eb2af9779478314388bb1b38d736 Mon Sep 17 00:00:00 2001 From: PunGrumpy <108584943+PunGrumpy@users.noreply.github.com> Date: Sat, 6 Jun 2026 00:26:05 +0700 Subject: [PATCH 5/6] chore: add changeset for ultracite frameworks support --- .changeset/ultracite-frameworks-flag.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/ultracite-frameworks-flag.md diff --git a/.changeset/ultracite-frameworks-flag.md b/.changeset/ultracite-frameworks-flag.md new file mode 100644 index 0000000..da3c40e --- /dev/null +++ b/.changeset/ultracite-frameworks-flag.md @@ -0,0 +1,5 @@ +--- +"@vernostudio/cli": patch +--- + +Add `--frameworks` support for Ultracite init: interactive multiselect in create/init wizards, `react next` defaults in `-y` mode, and manifest persistence for selected preset extends From e04ab9bd0f0979b3ccb64c068ae004b3a08229d8 Mon Sep 17 00:00:00 2001 From: PunGrumpy <108584943+PunGrumpy@users.noreply.github.com> Date: Sat, 6 Jun 2026 00:40:04 +0700 Subject: [PATCH 6/6] refactor(cli): simplify ultracite framework defaults after deslop Remove the no-op defaultUltraciteFrameworksFromFrontend helper and use DEFAULT_ULTRACITE_FRAMEWORKS directly; align init wizard calls with create. --- packages/cli/__tests__/ultracite-framework.test.ts | 12 +----------- packages/cli/src/commands/create/args.ts | 4 ++-- packages/cli/src/commands/create/prompts.ts | 2 +- packages/cli/src/commands/init/args.ts | 4 ++-- packages/cli/src/commands/init/prompts.ts | 8 +++----- packages/cli/src/ultracite-framework.ts | 11 ----------- 6 files changed, 9 insertions(+), 32 deletions(-) diff --git a/packages/cli/__tests__/ultracite-framework.test.ts b/packages/cli/__tests__/ultracite-framework.test.ts index f8edda3..edc557c 100644 --- a/packages/cli/__tests__/ultracite-framework.test.ts +++ b/packages/cli/__tests__/ultracite-framework.test.ts @@ -1,9 +1,5 @@ import { describe, expect, test } from "bun:test"; -import { - DEFAULT_ULTRACITE_FRAMEWORKS, - defaultUltraciteFrameworksFromFrontend, - parseUltraciteFrameworksArg, -} from "../src/ultracite-framework"; +import { parseUltraciteFrameworksArg } from "../src/ultracite-framework"; import { parseUltraciteFrameworksFlag } from "../src/commands/shared/ultracite"; describe("parseUltraciteFrameworksArg", () => { @@ -22,12 +18,6 @@ describe("parseUltraciteFrameworksArg", () => { }); }); -describe("defaultUltraciteFrameworksFromFrontend", () => { - test("defaults to react and next for next frontend", () => { - expect(defaultUltraciteFrameworksFromFrontend("next")).toEqual(DEFAULT_ULTRACITE_FRAMEWORKS); - }); -}); - describe("parseUltraciteFrameworksFlag", () => { test("returns undefined when ultracite is off and flag omitted", () => { expect(parseUltraciteFrameworksFlag({}, false)).toBeUndefined(); diff --git a/packages/cli/src/commands/create/args.ts b/packages/cli/src/commands/create/args.ts index 01973e2..34bd160 100644 --- a/packages/cli/src/commands/create/args.ts +++ b/packages/cli/src/commands/create/args.ts @@ -7,7 +7,7 @@ import type { import { FRONTENDS, PACKAGE_IDS } from "@vernostudio/template-generator"; import { DEFAULT_ULTRACITE_LINTER } from "../../ultracite-linter"; import type { UltraciteLinterId } from "../../ultracite-linter"; -import { defaultUltraciteFrameworksFromFrontend } from "../../ultracite-framework"; +import { DEFAULT_ULTRACITE_FRAMEWORKS } from "../../ultracite-framework"; import type { UltraciteFrameworkId } from "../../ultracite-framework"; import { parseAddonsArg } from "../shared/addons"; import { splitCommaList } from "../shared/comma-list"; @@ -208,7 +208,7 @@ export const resolveCreateInputsNonInteractive = ( const ultraciteLinter = ultraciteOn ? (flaggedLinter ?? DEFAULT_ULTRACITE_LINTER) : undefined; const flaggedFrameworks = parseUltraciteFrameworksFlag(options, ultraciteOn); const ultraciteFrameworks = ultraciteOn - ? (flaggedFrameworks ?? defaultUltraciteFrameworksFromFrontend(frontend)) + ? (flaggedFrameworks ?? DEFAULT_ULTRACITE_FRAMEWORKS) : undefined; return { diff --git a/packages/cli/src/commands/create/prompts.ts b/packages/cli/src/commands/create/prompts.ts index c0abad0..7e8a6f5 100644 --- a/packages/cli/src/commands/create/prompts.ts +++ b/packages/cli/src/commands/create/prompts.ts @@ -267,7 +267,7 @@ const readUltraciteFrameworks = async ( })), required: true, }), - ) as UltraciteFrameworkId[]; + ); }; export const runInteractiveCreateWizard = async (args: { diff --git a/packages/cli/src/commands/init/args.ts b/packages/cli/src/commands/init/args.ts index cee51b6..976fe32 100644 --- a/packages/cli/src/commands/init/args.ts +++ b/packages/cli/src/commands/init/args.ts @@ -1,6 +1,6 @@ import { DEFAULT_ULTRACITE_LINTER } from "../../ultracite-linter"; import type { UltraciteLinterId } from "../../ultracite-linter"; -import { defaultUltraciteFrameworksFromFrontend } from "../../ultracite-framework"; +import { DEFAULT_ULTRACITE_FRAMEWORKS } from "../../ultracite-framework"; import type { UltraciteFrameworkId } from "../../ultracite-framework"; import type { AddonId, PackageManager } from "@vernostudio/template-generator"; import { parseAddonsArg } from "../shared/addons"; @@ -113,7 +113,7 @@ export const resolveInitInputsNonInteractive = ( const ultraciteLinter = ultraciteOn ? (flaggedLinter ?? DEFAULT_ULTRACITE_LINTER) : undefined; const flaggedFrameworks = parseUltraciteFrameworksFlag(options, ultraciteOn); const ultraciteFrameworks = ultraciteOn - ? (flaggedFrameworks ?? defaultUltraciteFrameworksFromFrontend("next")) + ? (flaggedFrameworks ?? DEFAULT_ULTRACITE_FRAMEWORKS) : undefined; const useShadcn = ui === "shadcn" && !options.skipShadcn; diff --git a/packages/cli/src/commands/init/prompts.ts b/packages/cli/src/commands/init/prompts.ts index 373ab73..c8a15f7 100644 --- a/packages/cli/src/commands/init/prompts.ts +++ b/packages/cli/src/commands/init/prompts.ts @@ -181,7 +181,7 @@ const readUltraciteFrameworks = async ( })), required: true, }), - ) as UltraciteFrameworkId[]; + ); }; const addonsSummary = (addons: readonly AddonId[]): string => @@ -227,10 +227,8 @@ export const runInteractiveInitWizard = async (args: { p.log.step("Post-init"); const doInstall = await readDoInstall(hasNoInstall); - const ultraciteLinter = ultraciteOn ? await readUltraciteLinter(options, ultraciteOn) : undefined; - const ultraciteFrameworks = ultraciteOn - ? await readUltraciteFrameworks(options, ultraciteOn) - : undefined; + const ultraciteLinter = await readUltraciteLinter(options, ultraciteOn); + const ultraciteFrameworks = await readUltraciteFrameworks(options, ultraciteOn); const useShadcn = ui === "shadcn" && !hasSkipShadcn; diff --git a/packages/cli/src/ultracite-framework.ts b/packages/cli/src/ultracite-framework.ts index ea51f3e..57dc428 100644 --- a/packages/cli/src/ultracite-framework.ts +++ b/packages/cli/src/ultracite-framework.ts @@ -1,5 +1,3 @@ -import type { FrontendId } from "@vernostudio/template-generator"; - /** Values for `ultracite init --frameworks` (Ultracite preset extends). */ export type UltraciteFrameworkId = | "angular" @@ -34,15 +32,6 @@ export const DEFAULT_ULTRACITE_FRAMEWORKS: readonly UltraciteFrameworkId[] = ["r export const isUltraciteFrameworkId = (value: string | undefined): value is UltraciteFrameworkId => value !== undefined && (ULTRACITE_FRAMEWORK_IDS as readonly string[]).includes(value); -export const defaultUltraciteFrameworksFromFrontend = ( - frontend: FrontendId, -): readonly UltraciteFrameworkId[] => { - if (frontend === "next") { - return DEFAULT_ULTRACITE_FRAMEWORKS; - } - return DEFAULT_ULTRACITE_FRAMEWORKS; -}; - /** Parses comma- or space-separated framework ids; dedupes while preserving order. */ export const parseUltraciteFrameworksArg = (raw: string | undefined): UltraciteFrameworkId[] => { if (raw === undefined || raw.length === 0) {