diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/per_command/apps/web/index.html b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/per_command/apps/web/index.html new file mode 100644 index 0000000000..ce6d6280c2 --- /dev/null +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/per_command/apps/web/index.html @@ -0,0 +1,6 @@ + + + +

web

+ + diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/per_command/apps/web/package.json b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/per_command/apps/web/package.json new file mode 100644 index 0000000000..64630869c1 --- /dev/null +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/per_command/apps/web/package.json @@ -0,0 +1 @@ +{ "name": "web", "private": true } diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/per_command/package.json b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/per_command/package.json new file mode 100644 index 0000000000..f23425bd85 --- /dev/null +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/per_command/package.json @@ -0,0 +1,9 @@ +{ + "name": "per-command-config", + "type": "module", + "private": true, + "workspaces": [ + "apps/*", + "packages/*" + ] +} diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/per_command/packages/ui/package.json b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/per_command/packages/ui/package.json new file mode 100644 index 0000000000..dd63f88e1f --- /dev/null +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/per_command/packages/ui/package.json @@ -0,0 +1 @@ +{ "name": "ui", "private": true } diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/per_command/packages/ui/src/index.ts b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/per_command/packages/ui/src/index.ts new file mode 100644 index 0000000000..f538469c7d --- /dev/null +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/per_command/packages/ui/src/index.ts @@ -0,0 +1 @@ +export const ui = 'ui'; diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/per_command/vite.config.ts b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/per_command/vite.config.ts new file mode 100644 index 0000000000..754c5f8e4d --- /dev/null +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/per_command/vite.config.ts @@ -0,0 +1,3 @@ +export default { + defaultPackage: { build: './apps/web', pack: './packages/ui' }, +}; diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/per_command_fallthrough/index.html b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/per_command_fallthrough/index.html new file mode 100644 index 0000000000..31acf91c54 --- /dev/null +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/per_command_fallthrough/index.html @@ -0,0 +1,6 @@ + + + +

root app

+ + diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/per_command_fallthrough/package.json b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/per_command_fallthrough/package.json new file mode 100644 index 0000000000..eb32387cc7 --- /dev/null +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/per_command_fallthrough/package.json @@ -0,0 +1 @@ +{ "name": "per-command-fallthrough", "type": "module", "private": true, "workspaces": ["packages/*"] } diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/per_command_fallthrough/packages/ui/package.json b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/per_command_fallthrough/packages/ui/package.json new file mode 100644 index 0000000000..dd63f88e1f --- /dev/null +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/per_command_fallthrough/packages/ui/package.json @@ -0,0 +1 @@ +{ "name": "ui", "private": true } diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/per_command_fallthrough/packages/ui/src/index.ts b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/per_command_fallthrough/packages/ui/src/index.ts new file mode 100644 index 0000000000..f538469c7d --- /dev/null +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/per_command_fallthrough/packages/ui/src/index.ts @@ -0,0 +1 @@ +export const ui = 'ui'; diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/per_command_fallthrough/vite.config.ts b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/per_command_fallthrough/vite.config.ts new file mode 100644 index 0000000000..f46068d2cc --- /dev/null +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/per_command_fallthrough/vite.config.ts @@ -0,0 +1,3 @@ +export default { + defaultPackage: { pack: './packages/ui' }, +}; diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/snapshots.toml b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/snapshots.toml index b8e0be78a5..d52a2c6520 100644 --- a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/snapshots.toml +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/snapshots.toml @@ -59,3 +59,30 @@ defaultPackage inside `defineConfig({ ... } satisfies UserConfig)` is also honored: the wrapper on the defineConfig argument is unwrapped too. """ steps = [{ argv = ["vp", "build"], cwd = "dc_wrapper" }] + +[[case]] +name = "default_package_per_command" +vp = ["local", "global"] +comment = """ +The object form maps commands individually at a workspace root: `vp build` +targets ./apps/web while `vp pack` targets ./packages/ui, so one monorepo +can dev an app and pack a library (rfcs/cwd-flag.md). +""" +steps = [ + { argv = ["vp", "build"], cwd = "per_command" }, + { argv = ["vp", "pack"], cwd = "per_command" }, +] + +[[case]] +name = "default_package_per_command_fallthrough" +vp = ["local", "global"] +comment = """ +A command absent from the defaultPackage object falls through to the +normal resolution: the map only declares `pack`, so bare `vp build` at +this workspace root runs in place at the (runnable) root with no note, +while `vp pack` still routes to the declared ./packages/ui. +""" +steps = [ + { argv = ["vp", "build"], cwd = "per_command_fallthrough" }, + { argv = ["vp", "pack"], cwd = "per_command_fallthrough" }, +] diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/snapshots/default_package_per_command.global.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/snapshots/default_package_per_command.global.md new file mode 100644 index 0000000000..a56291d0b5 --- /dev/null +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/snapshots/default_package_per_command.global.md @@ -0,0 +1,31 @@ +# default_package_per_command + +The object form maps commands individually at a workspace root: `vp build` +targets ./apps/web while `vp pack` targets ./packages/ui, so one monorepo +can dev an app and pack a library (rfcs/cwd-flag.md). + +## `cd per_command && vp build` + +``` +VITE+ - The Unified Toolchain for the Web + +note: vp build: using ./apps/web (defaultPackage in vite.config.ts) +✓ 2 modules transformed. +computing gzip size... +dist/index.html kB │ gzip: kB + +✓ built in +``` + +## `cd per_command && vp pack` + +``` +VITE+ - The Unified Toolchain for the Web + +note: vp pack: using ./packages/ui (defaultPackage in vite.config.ts) +ℹ entry: src/index.ts +ℹ Build start +ℹ dist/index.mjs kB │ gzip: kB +ℹ 1 files, total: kB +✔ Build complete in +``` diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/snapshots/default_package_per_command.local.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/snapshots/default_package_per_command.local.md new file mode 100644 index 0000000000..43141d909f --- /dev/null +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/snapshots/default_package_per_command.local.md @@ -0,0 +1,27 @@ +# default_package_per_command + +The object form maps commands individually at a workspace root: `vp build` +targets ./apps/web while `vp pack` targets ./packages/ui, so one monorepo +can dev an app and pack a library (rfcs/cwd-flag.md). + +## `cd per_command && vp build` + +``` +note: vp build: using ./apps/web (defaultPackage in vite.config.ts) +✓ 2 modules transformed. +computing gzip size... +dist/index.html kB │ gzip: kB + +✓ built in +``` + +## `cd per_command && vp pack` + +``` +note: vp pack: using ./packages/ui (defaultPackage in vite.config.ts) +ℹ entry: src/index.ts +ℹ Build start +ℹ dist/index.mjs kB │ gzip: kB +ℹ 1 files, total: kB +✔ Build complete in +``` diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/snapshots/default_package_per_command_fallthrough.global.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/snapshots/default_package_per_command_fallthrough.global.md new file mode 100644 index 0000000000..00ec553fbd --- /dev/null +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/snapshots/default_package_per_command_fallthrough.global.md @@ -0,0 +1,31 @@ +# default_package_per_command_fallthrough + +A command absent from the defaultPackage object falls through to the +normal resolution: the map only declares `pack`, so bare `vp build` at +this workspace root runs in place at the (runnable) root with no note, +while `vp pack` still routes to the declared ./packages/ui. + +## `cd per_command_fallthrough && vp build` + +``` +VITE+ - The Unified Toolchain for the Web + +✓ 2 modules transformed. +computing gzip size... +dist/index.html kB │ gzip: kB + +✓ built in +``` + +## `cd per_command_fallthrough && vp pack` + +``` +VITE+ - The Unified Toolchain for the Web + +note: vp pack: using ./packages/ui (defaultPackage in vite.config.ts) +ℹ entry: src/index.ts +ℹ Build start +ℹ dist/index.mjs kB │ gzip: kB +ℹ 1 files, total: kB +✔ Build complete in +``` diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/snapshots/default_package_per_command_fallthrough.local.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/snapshots/default_package_per_command_fallthrough.local.md new file mode 100644 index 0000000000..cd4a43cebe --- /dev/null +++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/snapshots/default_package_per_command_fallthrough.local.md @@ -0,0 +1,27 @@ +# default_package_per_command_fallthrough + +A command absent from the defaultPackage object falls through to the +normal resolution: the map only declares `pack`, so bare `vp build` at +this workspace root runs in place at the (runnable) root with no note, +while `vp pack` still routes to the declared ./packages/ui. + +## `cd per_command_fallthrough && vp build` + +``` +✓ 2 modules transformed. +computing gzip size... +dist/index.html kB │ gzip: kB + +✓ built in +``` + +## `cd per_command_fallthrough && vp pack` + +``` +note: vp pack: using ./packages/ui (defaultPackage in vite.config.ts) +ℹ entry: src/index.ts +ℹ Build start +ℹ dist/index.mjs kB │ gzip: kB +ℹ 1 files, total: kB +✔ Build complete in +``` diff --git a/docs/config/index.md b/docs/config/index.md index 97e51c8ef9..bfab9ef6c5 100644 --- a/docs/config/index.md +++ b/docs/config/index.md @@ -45,4 +45,4 @@ export default { }; ``` -vp reads the value without executing the config, so `defaultPackage` also works at repository roots with no vite-plus dependency (for example a Laravel or Rails repo whose Vite app lives in `frontend/`, with vite-plus installed only there). That static read is why the value must stay a plain string literal rather than an expression. An explicit `-C` or positional target always wins over the config. +vp reads the values without executing the config, so `defaultPackage` also works at repository roots with no vite-plus dependency (for example a Laravel or Rails repo whose Vite app lives in `frontend/`, with vite-plus installed only there). That static read is why the values must stay plain string literals rather than expressions. An explicit `-C` or positional target always wins over the config. diff --git a/docs/guide/monorepo.md b/docs/guide/monorepo.md index e040d32d8f..60caef9ae8 100644 --- a/docs/guide/monorepo.md +++ b/docs/guide/monorepo.md @@ -232,6 +232,14 @@ note: vp dev: using ./apps/web (defaultPackage in vite.config.ts) This is the right choice for framework monorepos that are not JavaScript workspaces, such as a Laravel or Rails app with a `frontend/` directory: there is no package list to resolve, so `defaultPackage` points vp straight at the app. Because vp reads it without executing the config, it works even when `vite-plus` is installed only inside that subdirectory. +An object form maps commands individually, so `vp pack` can target a library while `vp dev` targets an app; a command absent from the object falls through to the resolution above: + +```ts [vite.config.ts] +export default { + defaultPackage: { dev: './apps/web', pack: './packages/ui' }, +}; +``` + ### Package scripts and workspace-wide tasks Keep package-specific scripts in each package when the command differs per app: diff --git a/packages/cli/binding/src/cli/app_target.rs b/packages/cli/binding/src/cli/app_target.rs index 283f7582df..ff361f45a4 100644 --- a/packages/cli/binding/src/cli/app_target.rs +++ b/packages/cli/binding/src/cli/app_target.rs @@ -332,6 +332,24 @@ enum Elicitation { WorkspaceRoot(vite_workspace::WorkspaceRoot), } +/// Applies a `defaultPackage` declaration to one command. A string covers +/// all four app commands; an object maps commands individually +/// (`{ dev: './apps/web', pack: './packages/ui' }`), and a command absent +/// from the object falls through to the picker/listing resolution. Every +/// other shape (a non-string, a non-static value) passes through for +/// [`resolve_default_package`] to report. +fn default_package_for_command( + command: &str, + value: vite_static_config::FieldValue, +) -> Option { + match value { + vite_static_config::FieldValue::Json(serde_json::Value::Object(map)) => { + map.get(command).cloned().map(vite_static_config::FieldValue::Json) + } + other => Some(other), + } +} + /// The RFC's resolution order, written once for both entry points: bare app /// command, then `defaultPackage` at the invocation root, then the workspace /// root itself. `defaultPackage` is a root-pointer concept: it applies where @@ -356,8 +374,10 @@ fn classify(subcommand: &SynthesizableSubcommand, cwd: &AbsolutePath) -> Classif // Resolved once and reused by `root_looks_runnable` below, so a bare // command at a root reads and parses the config a single time. let root_config = at_invocation_root.then(|| vite_static_config::resolve_static_config(cwd)); - if let Some(value) = - root_config.as_ref().and_then(|config| config.get_declared("defaultPackage")) + if let Some(value) = root_config + .as_ref() + .and_then(|config| config.get_declared("defaultPackage")) + .and_then(|value| default_package_for_command(command, value)) { return Classification::Elicit(command, Elicitation::DefaultPackage(value)); } diff --git a/packages/cli/src/define-config.ts b/packages/cli/src/define-config.ts index dada43d187..0426dc277f 100644 --- a/packages/cli/src/define-config.ts +++ b/packages/cli/src/define-config.ts @@ -56,12 +56,14 @@ declare module '@voidzero-dev/vite-plus-core' { /** * Default target directory for `vp dev` / `build` / `preview` / `pack` * when invoked bare in the directory containing this config (an implicit - * `vp -C `). Relative to the config file's directory. vp reads the - * value without executing the config, so it also works at roots with no - * vite-plus dependency; the static read is why it must stay a plain - * string literal. + * `vp -C `). A string targets all four commands; an object maps + * commands individually, and a command absent from the object falls + * through to the normal picker/listing resolution. Paths are relative to + * the config file's directory. vp reads them without executing the + * config, so this also works at roots with no vite-plus dependency; the + * static read is why the values must stay plain string literals. */ - defaultPackage?: string; + defaultPackage?: string | { dev?: string; build?: string; preview?: string; pack?: string }; run?: RunConfig; diff --git a/rfcs/cwd-flag.md b/rfcs/cwd-flag.md index 1fc784137e..6b9faaeabf 100644 --- a/rfcs/cwd-flag.md +++ b/rfcs/cwd-flag.md @@ -309,7 +309,7 @@ export default defineConfig({ }); ``` -- Type: `string`, a single directory. A per-command map can come later if real demand appears. +- Type: `string` (one directory for all four commands) or a per-command object (`{ dev: './apps/web', pack: './packages/ui' }`, added on review demand). A command absent from the object falls through to the picker/listing resolution. - Consulted when a bare app command runs in the directory containing the root config: a workspace root, or a non-workspace repo root. The non-workspace shape has no package list, so `defaultPackage` is the only mechanism that covers it. An explicit `-C` always wins. - A missing directory errors: `defaultPackage points to a missing directory: ./frontend`. - Read via static extraction (`vite_static_config` + the loader in `packages/cli/binding/src/cli/handler.rs`), like `run` config. At a non-workspace root there is no install to execute the config, so the file must work unexecuted: a plain default-export object with a static string value.