voidzero-dev / voidzero-dev/vite-plus

Monorepo: nested package's lint rules/jsPlugins silently dropped when workspace root has a lint block

Open
#2,628 1 comment 1 reaction 0 assignees View on GitHub
Dominant language
Rust
Stars
5.8k
Forks
262
Avg merge
1d 34m
Merged PRs (30d)
135

Description

## Summary

In a pnpm workspace/monorepo, when the **root** `vite.config.ts` has its own `lint` block, a **nested package's** `lint.rules` and `lint.jsPlugins` are silently ignored by `vp lint`. Remove the root's `lint` block and the nested config is forwarded correctly, so the nested config itself is valid.

Tested on vite-plus 0.3.0 (oxlint 1.79).

## Minimal reproduction

Self-contained, 6 files, no custom plugin needed. Run with `pnpm install` then `cd apps/child && vp lint`.

```
repro/
├── package.json # devDeps: vite-plus 0.3.0
├── pnpm-workspace.yaml # packages: ["apps/*"]
├── vite.config.ts # ROOT: has a lint block
└── apps/
└── child/
├── package.json # devDeps: vite-plus 0.3.0
├── vite.config.ts # CHILD: lint.rules + lint.jsPlugins
└── src/bad.ts # throws a string literal
```

Root `vite.config.ts`:

```ts
import { defineConfig } from 'vite-plus';

export default defineConfig({
lint: {
jsPlugins: [{ name: 'vite-plus', specifier: 'vite-plus/oxlint-plugin' }],
rules: { 'vite-plus/prefer-vite-plus-imports': 'error' },
options: { typeAware: true, typeCheck: true },
},
});
```

Child `apps/child/vite.config.ts`:

```ts
import { defineConfig } from 'vite-plus';

export default defineConfig({
lint: {
rules: { 'no-throw-literal': 'error' },
jsPlugins: [
{ name: 'ghost', specifier: 'this-plugin-is-definitely-not-installed' },
],
},
});
```

Child `apps/child/src/bad.ts`:

```ts
export function ping(): string {
throw 'a string literal'; // violates no-throw-literal ('ghost' jsPlugin would crash oxlint if loaded)
}
```

## Actual

```bash
$ cd apps/child && vp lint
# (no output — neither the rule nor the plugin is applied, and loading the
# nonexistent "ghost" plugin does not even get attempted)
```

## Expected

`throw 'a string literal'` should be reported by `no-throw-literal`, and the `ghost` jsPlugin load should be attempted (would expect a loud error for a nonexistent plugin).

## Proof the nested config is fine (and the bug is the root's lint block)

Root `lint` block present:

```bash
$ cd apps/child && vp lint
( no output )
```

Root `lint` block removed (only the `lint` block lines deleted):

```bash
$ cd apps/child && vp lint
src/bad.ts:2:9: error eslint(no-throw-literal): Expected an error object to be thrown
... [if child also has the ghost jsPlugins entry, it fails loudly instead:]
Failed to parse oxlint configuration file.
x Failed to load JS plugin: this-plugin-is-definitely-not-installed
```

Same behavior in `vp lint --print-config`: nested/root custom rules and jsPlugins never appear in the printed oxlint config when the root has a `lint` block.

Also worth noting: the same config in a **single-package (non-monorepo) project forwards `lint.jsPlugins` correctly**, so forwarding works in the simple case and breaks specifically with a root `lint` block present.

## Related

- #997 — nested package `ignorePatterns` dropped during config merging (closed; this may share the config-merge code path).

Contributor guide

Open the contributing guide

Research direction

Start with the six-file reproduction, run pnpm install, then run cd apps/child && vp lint and vp lint --print-config with the root lint block present. Compare the printed configuration with the root and child vite.config.ts files; done means the child no-throw-literal rule and jsPlugins are forwarded or reported rather than silently dropped.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
tooling
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
74/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.