voidzero-dev / voidzero-dev/vite-plus
Allow `vp staged` to resolve workspace-root config from subdirectories
- Dominant language
- Rust
- Stars
- 5.8k
- Forks
- 262
- Avg merge
- 1d 34m
- Merged PRs (30d)
- 135
Description
### Description
In a monorepo, `vp staged` fails when invoked from a workspace package if the `staged` config exists only in the workspace-root `vite.config.ts`.
This is common with coding agents: after inspecting or editing one package, they often keep that package as the working directory when running validation commands.
```text
repo/
├── package.json # workspaces: ["packages/*"]
├── vite.config.ts # contains staged config
└── packages/
└── app/
└── src/index.ts
```
```ts
// repo/vite.config.ts
export default {
staged: {
'*.{js,ts,tsx}': 'vp check --fix',
},
}
```
```sh
git add packages/app/src/index.ts
cd packages/app
vp staged
```
Actual result:
```text
error: No "staged" config found in vite.config.ts. Please add a staged config
```
A normal pre-commit hook usually hides this behavior because Git starts client-side hooks from the worktree root, regardless of where `git commit` was entered.
### Current behavior
The following was verified with an isolated Git monorepo:
| Invocation | Config | File/task scope |
| --- | --- | --- |
| Run from repository root | Root `vite.config.ts` | Repository |
| Run from a package with no local `staged` config | None; exits with an error | Nothing runs |
| Run from a package with a local `staged` config | Package config | Package CWD |
| Run from a package with `--cwd ` | Root config | Repository |
| Run through a Git pre-commit hook | Root config, because Git uses the worktree root | Repository |
Internally, Vite+ resolves one `staged` object from the invocation CWD and passes it through lint-staged's programmatic `config` option. lint-staged still resolves the Git repository and staged files, but its own multi-config discovery is bypassed.
### Ecosystem patterns
Existing tools use different models:
| Model | Tools | Behavior |
| --- | --- | --- |
| Hook manager | Git, Husky, simple-git-hooks | Git starts hooks at the worktree root. The manager executes a literal hook command; package-specific CWD or filtering is left to that command. |
| Central root config | pre-commit, Lefthook | The tool resolves the Git root and loads a repository-level config. Package behavior is expressed through filters or per-command root settings. |
| Distributed config | lint-staged | The tool can discover multiple configs, assign each staged file to its closest config, and run tasks from the selected config directory. Configs are isolated rather than merged. |
| Current Vite+ behavior | `vp staged` | Git state is repository-aware through lint-staged, while Vite config lookup remains limited to the invocation CWD. |
Relevant details:
- Git changes to the worktree root before client-side hooks: https://git-scm.com/docs/githooks
- Husky requires an explicit `cd` when a hook should run inside a nested project: https://typicode.github.io/husky/how-to.html#project-not-in-git-root-directory
- lint-staged documents closest-config selection, isolated configs, config-directory task CWD, and package scoping with `--cwd`: https://github.com/lint-staged/lint-staged#how-to-use-lint-staged-in-a-multi-package-monorepo
- Lefthook loads from the Git root and uses a command-level `root` option to change CWD and filter files; globs remain relative to the Git root: https://lefthook.dev/configuration/root/
- pre-commit resolves the Git top level and changes into it before running most commands: https://github.com/pre-commit/pre-commit/blob/main/pre_commit/main.py#L175-L199
### Design choices
There are two separate decisions. This issue does not propose a preferred combination.
#### 1. Where should config come from?
- **Current CWD only:** preserve existing behavior; improve documentation/error output and require callers to use root CWD or `--cwd`.
- **Workspace/Git root:** always use one central root policy.
- **Nearest ancestor config:** walk upward to the workspace root. This could stop at the first Vite config, or continue until finding one that defines `staged`.
- **Multiple configs:** discover package/root staged configs and assign files to them independently.
- **Explicit selection:** add an option such as `--config` or `--root`; this can supplement any implicit rule.
Important edge case: a package may have a `vite.config.ts` for build settings but keep `staged` only at the workspace root. “Nearest Vite config” and “nearest config defining staged” produce different results.
#### 2. What should the execution scope be?
- **Repository root:** task CWD, glob base, and staged-file scope all use the root.
- **Invocation directory:** an inherited/root config can be used while files and tasks remain scoped to the package from which the command was launched.
- **Config directory:** tasks and file matching follow whichever config was selected.
These choices have different consequences. Root scope may process staged files in sibling packages; invocation scope may run root-defined commands from a package CWD; config-directory scope becomes more complex if multiple configs are supported.
Possible combinations include:
- root config + root scope;
- nearest ancestor config + invocation scope;
- current behavior + an explicit config/root option;
- multiple configs + per-config task scope.
### Relevant implementation
- Current staged config resolution and inline lint-staged config: https://github.com/voidzero-dev/vite-plus/blob/main/packages/cli/src/staged/bin.ts#L139-L164
- Existing workspace-bounded config traversal: https://github.com/voidzero-dev/vite-plus/blob/main/packages/cli/src/resolve-vite-config.ts#L98-L115
- Existing use of traversal in `vp pack`: https://github.com/voidzero-dev/vite-plus/blob/main/packages/cli/src/pack-bin.ts#L148-L150
### Validations
- [x] Read the [Contributing Guidelines](https://github.com/voidzero-dev/vite-plus/blob/main/CONTRIBUTING.md).
- [x] Confirm this request is for Vite+ itself and not for Vite, Vitest, tsdown, Rolldown, or Oxc.
- [x] Checked that there is not already an open issue requesting the same behavior.
Contributor guide
Research direction
Start with packages/cli/src/staged/bin.ts, then read the workspace-bounded traversal in packages/cli/src/resolve-vite-config.ts and its use in packages/cli/src/pack-bin.ts. Reproduce the root-config-from-package scenario described above, then establish and validate a single config-resolution and execution-scope policy, including the package-local Vite config edge case.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- cli, tooling
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Needs clarification
- Newbie friendliness
- 35/100