From 419ebaf6df9f3c48c19c05e38d4c8375aed31e26 Mon Sep 17 00:00:00 2001 From: MK Date: Fri, 8 May 2026 14:58:47 +0800 Subject: [PATCH 1/9] docs: add monorepo overrides guide --- docs/.vitepress/config.mts | 1 + docs/config/fmt.md | 2 + docs/config/lint.md | 2 + docs/guide/monorepo.md | 175 +++++++++++++++++++++++++++++++++++++ 4 files changed, 180 insertions(+) create mode 100644 docs/guide/monorepo.md diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 932500090b..256a13aaaa 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -29,6 +29,7 @@ const guideSidebar = [ { text: 'Creating a Project', link: '/guide/create' }, { text: 'Migrate to Vite+', link: '/guide/migrate' }, { text: 'Installing Dependencies', link: '/guide/install' }, + { text: 'Monorepo', link: '/guide/monorepo' }, { text: 'Environment', link: '/guide/env' }, { text: 'Why Vite+', link: '/guide/why' }, ], diff --git a/docs/config/fmt.md b/docs/config/fmt.md index d034398509..254ed73b2a 100644 --- a/docs/config/fmt.md +++ b/docs/config/fmt.md @@ -16,3 +16,5 @@ export default defineConfig({ }, }); ``` + +For package-specific formatting settings in a workspace, use [`fmt.overrides`](/guide/monorepo#format-overrides) from the root `vite.config.ts`. diff --git a/docs/config/lint.md b/docs/config/lint.md index 718d2d7d9f..3d097b6603 100644 --- a/docs/config/lint.md +++ b/docs/config/lint.md @@ -22,3 +22,5 @@ export default defineConfig({ ``` We recommend enabling both `options.typeAware` and `options.typeCheck` so `vp lint` and `vp check` can use the full type-aware path. + +For package-specific lint rules in a workspace, use [`lint.overrides`](/guide/monorepo#root-config-with-overrides) from the root `vite.config.ts`. diff --git a/docs/guide/monorepo.md b/docs/guide/monorepo.md new file mode 100644 index 0000000000..e739382260 --- /dev/null +++ b/docs/guide/monorepo.md @@ -0,0 +1,175 @@ +# Monorepos + +Vite+ works well in monorepos when the shared tool configuration lives in the workspace root. Put the root defaults in `vite.config.ts`, then use `overrides` to apply package-specific lint and format settings. + +This is the recommended pattern for shared quality tooling because the root config stays type-safe and composable, while each app or package can still keep its own Vite, Vitest, framework, or runtime files when that is useful. + +## Root Config With Overrides + +Use `lint.overrides` for Oxlint rules that only apply to some packages: + +```ts [vite.config.ts] +import { defineConfig } from 'vite-plus'; + +export default defineConfig({ + lint: { + plugins: ['typescript'], + options: { + typeAware: true, + typeCheck: true, + }, + rules: { + 'no-console': ['error', { allow: ['warn', 'error'] }], + }, + overrides: [ + { + files: ['apps/web/**', 'packages/ui/**'], + plugins: ['typescript', 'react'], + rules: { + 'react/self-closing-comp': 'error', + }, + }, + { + files: ['apps/api/**'], + env: { + node: true, + }, + rules: { + 'no-console': 'off', + }, + }, + { + files: ['**/*.test.ts', '**/*.spec.ts'], + plugins: ['typescript', 'vitest'], + rules: { + '@typescript-eslint/no-explicit-any': 'off', + }, + }, + ], + }, +}); +``` + +Globs are resolved from the root `vite.config.ts`, so use workspace paths such as `apps/web/**`, `apps/api/**`, and `packages/ui/**`. + +::: tip +When a `lint.overrides` entry sets `plugins`, include the plugins needed for that file group. If you omit `plugins`, the override uses the base `lint.plugins` value. +::: + +## Format Overrides + +Use `fmt.overrides` for file or package-specific Oxfmt options. Formatter overrides put their settings under `options`: + +```ts [vite.config.ts] +import { defineConfig } from 'vite-plus'; + +export default defineConfig({ + fmt: { + singleQuote: true, + semi: true, + overrides: [ + { + files: ['apps/api/**'], + options: { + printWidth: 120, + }, + }, + { + files: ['**/*.md'], + options: { + proseWrap: 'always', + }, + }, + ], + }, +}); +``` + +## Splitting Config Files + +You can still split configuration across your repository. Export normal JavaScript objects from nearby files, import them in the root config, and merge them into the matching override. + +```ts [tooling/lint/react.ts] +import type { OxlintOverride } from 'vite-plus/lint'; + +export const reactLint = { + plugins: ['typescript', 'react'], + rules: { + 'react/self-closing-comp': 'error', + }, +} satisfies Omit; +``` + +```ts [tooling/lint/node.ts] +import type { OxlintOverride } from 'vite-plus/lint'; + +export const nodeLint = { + env: { + node: true, + }, + rules: { + 'no-console': 'off', + }, +} satisfies Omit; +``` + +```ts [vite.config.ts] +import { defineConfig } from 'vite-plus'; + +import { nodeLint } from './tooling/lint/node'; +import { reactLint } from './tooling/lint/react'; + +export default defineConfig({ + lint: { + plugins: ['typescript'], + options: { + typeAware: true, + typeCheck: true, + }, + overrides: [ + { + files: ['apps/web/**', 'packages/ui/**'], + ...reactLint, + }, + { + files: ['apps/api/**'], + ...nodeLint, + }, + ], + }, +}); +``` + +This keeps the behavior centralized while letting each team or package own the pieces of config it needs. + +## App Commands + +The root `vite.config.ts` is most valuable for shared linting, formatting, staged checks, and task definitions. For project-specific development, build, and test behavior, use the setup that best matches each app: + +- Pass a folder to built-in Vite commands when you want to target one app: + +```bash +vp dev apps/web +vp build apps/web +``` + +- Keep package-specific scripts in each package when the command differs per app: + +```json [apps/api/package.json] +{ + "scripts": { + "dev": "tsx watch src/index.ts", + "build": "tsc -p tsconfig.json" + } +} +``` + +- Run scripts across the workspace with `vp run`: + +```bash +vp run -r build +vp run -r --parallel dev +vp run --filter ./apps/web build +``` + +See the [Run guide](/guide/run) for recursive, parallel, filtered, and cached workspace tasks. From cbb076912b7631ef4a7665c47272731dca38f50c Mon Sep 17 00:00:00 2001 From: MK Date: Fri, 8 May 2026 17:26:39 +0800 Subject: [PATCH 2/9] ci: add monorepo overrides ecosystem case --- .github/workflows/e2e-test.yml | 4 ++++ ecosystem-ci/repo.json | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml index 8a005721ac..df3520d381 100644 --- a/.github/workflows/e2e-test.yml +++ b/.github/workflows/e2e-test.yml @@ -320,6 +320,10 @@ jobs: node-version: 24 command: | vp check --fix + - name: vite-plus-monorepo-overrides + node-version: 24 + command: | + vp run verify - name: varlet node-version: 22 command: | diff --git a/ecosystem-ci/repo.json b/ecosystem-ci/repo.json index e762c5d1e1..593c8d00eb 100644 --- a/ecosystem-ci/repo.json +++ b/ecosystem-ci/repo.json @@ -120,6 +120,12 @@ "hash": "6192f60653c124ae068efaf5d7d0a4134c95edbd", "forceFreshMigration": true }, + "vite-plus-monorepo-overrides": { + "repository": "https://github.com/why-reproductions-are-required/vite-plus-monorepo-overrides.git", + "branch": "main", + "hash": "fe830ccb9ed6442f48005e4985f8d66f7f980d3c", + "forceFreshMigration": true + }, "varlet": { "repository": "https://github.com/varletjs/varlet.git", "branch": "dev", From d1479aac52822fb2f67d3516d503d72d60b57027 Mon Sep 17 00:00:00 2001 From: MK Date: Fri, 8 May 2026 17:32:07 +0800 Subject: [PATCH 3/9] docs: use singular monorepo wording --- docs/guide/monorepo.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/guide/monorepo.md b/docs/guide/monorepo.md index e739382260..1f8622fc83 100644 --- a/docs/guide/monorepo.md +++ b/docs/guide/monorepo.md @@ -1,6 +1,6 @@ -# Monorepos +# Monorepo -Vite+ works well in monorepos when the shared tool configuration lives in the workspace root. Put the root defaults in `vite.config.ts`, then use `overrides` to apply package-specific lint and format settings. +Vite+ works well in a monorepo when the shared tool configuration lives in the workspace root. Put the root defaults in `vite.config.ts`, then use `overrides` to apply package-specific lint and format settings. This is the recommended pattern for shared quality tooling because the root config stays type-safe and composable, while each app or package can still keep its own Vite, Vitest, framework, or runtime files when that is useful. From 95806c9f4da19084476520c9fc02933e5b817a3c Mon Sep 17 00:00:00 2001 From: MK Date: Fri, 8 May 2026 17:43:20 +0800 Subject: [PATCH 4/9] ci: format monorepo overrides fixture before verify --- .github/workflows/e2e-test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml index df3520d381..5675d96c2f 100644 --- a/.github/workflows/e2e-test.yml +++ b/.github/workflows/e2e-test.yml @@ -323,6 +323,7 @@ jobs: - name: vite-plus-monorepo-overrides node-version: 24 command: | + vp check --fix vp run verify - name: varlet node-version: 22 From b00ba358110785dc265c79534625f539f58712b8 Mon Sep 17 00:00:00 2001 From: MK Date: Fri, 8 May 2026 17:56:15 +0800 Subject: [PATCH 5/9] docs: verify monorepo vitest plugin example --- docs/guide/monorepo.md | 3 +++ ecosystem-ci/repo.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/guide/monorepo.md b/docs/guide/monorepo.md index 1f8622fc83..9d641f7777 100644 --- a/docs/guide/monorepo.md +++ b/docs/guide/monorepo.md @@ -4,6 +4,8 @@ Vite+ works well in a monorepo when the shared tool configuration lives in the w This is the recommended pattern for shared quality tooling because the root config stays type-safe and composable, while each app or package can still keep its own Vite, Vitest, framework, or runtime files when that is useful. +The examples below come from the runnable [`vite-plus-monorepo-overrides`](https://github.com/why-reproductions-are-required/vite-plus-monorepo-overrides) fixture. + ## Root Config With Overrides Use `lint.overrides` for Oxlint rules that only apply to some packages: @@ -43,6 +45,7 @@ export default defineConfig({ plugins: ['typescript', 'vitest'], rules: { '@typescript-eslint/no-explicit-any': 'off', + 'vitest/no-disabled-tests': 'error', }, }, ], diff --git a/ecosystem-ci/repo.json b/ecosystem-ci/repo.json index 593c8d00eb..6bc4f6e79c 100644 --- a/ecosystem-ci/repo.json +++ b/ecosystem-ci/repo.json @@ -123,7 +123,7 @@ "vite-plus-monorepo-overrides": { "repository": "https://github.com/why-reproductions-are-required/vite-plus-monorepo-overrides.git", "branch": "main", - "hash": "fe830ccb9ed6442f48005e4985f8d66f7f980d3c", + "hash": "d89fe8f6479679e32e7a105fad85a2139beb2802", "forceFreshMigration": true }, "varlet": { From baf30eeafa11f7c0f2d046097b636b55bb27a786 Mon Sep 17 00:00:00 2001 From: MK Date: Sat, 9 May 2026 09:35:33 +0800 Subject: [PATCH 6/9] ci: pin monorepo fixture vp command fix --- ecosystem-ci/repo.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ecosystem-ci/repo.json b/ecosystem-ci/repo.json index 6bc4f6e79c..e184aaabf8 100644 --- a/ecosystem-ci/repo.json +++ b/ecosystem-ci/repo.json @@ -123,7 +123,7 @@ "vite-plus-monorepo-overrides": { "repository": "https://github.com/why-reproductions-are-required/vite-plus-monorepo-overrides.git", "branch": "main", - "hash": "d89fe8f6479679e32e7a105fad85a2139beb2802", + "hash": "813ddcf84ff2f9da34e4fe5bcaaade05c64d27f8", "forceFreshMigration": true }, "varlet": { From eec1100ea42bea38b243526acd9c1fb952631555 Mon Sep 17 00:00:00 2001 From: MK Date: Sat, 9 May 2026 09:46:10 +0800 Subject: [PATCH 7/9] docs: clarify lint override plugins behavior --- docs/guide/monorepo.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guide/monorepo.md b/docs/guide/monorepo.md index 9d641f7777..29095b00f6 100644 --- a/docs/guide/monorepo.md +++ b/docs/guide/monorepo.md @@ -56,7 +56,7 @@ export default defineConfig({ Globs are resolved from the root `vite.config.ts`, so use workspace paths such as `apps/web/**`, `apps/api/**`, and `packages/ui/**`. ::: tip -When a `lint.overrides` entry sets `plugins`, include the plugins needed for that file group. If you omit `plugins`, the override uses the base `lint.plugins` value. +When a `lint.overrides` entry sets `plugins`, that list replaces the base `lint.plugins` list for matched files. Include every plugin needed by that file group, such as `['typescript', 'react']`. Omit `plugins` only when the override should inherit the base list unchanged. ::: ## Format Overrides From d9556b2a706389a499e6e511f08310c2be488f76 Mon Sep 17 00:00:00 2001 From: MK Date: Sat, 9 May 2026 10:21:18 +0800 Subject: [PATCH 8/9] docs: remove fixture mention from monorepo guide --- docs/guide/monorepo.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/guide/monorepo.md b/docs/guide/monorepo.md index 29095b00f6..e2690e1983 100644 --- a/docs/guide/monorepo.md +++ b/docs/guide/monorepo.md @@ -4,8 +4,6 @@ Vite+ works well in a monorepo when the shared tool configuration lives in the w This is the recommended pattern for shared quality tooling because the root config stays type-safe and composable, while each app or package can still keep its own Vite, Vitest, framework, or runtime files when that is useful. -The examples below come from the runnable [`vite-plus-monorepo-overrides`](https://github.com/why-reproductions-are-required/vite-plus-monorepo-overrides) fixture. - ## Root Config With Overrides Use `lint.overrides` for Oxlint rules that only apply to some packages: From 068746649e70b98663c3ae11ebfdb23e5548f7eb Mon Sep 17 00:00:00 2001 From: "MK (fengmk2)" Date: Mon, 11 May 2026 10:02:22 +0800 Subject: [PATCH 9/9] Apply suggestions from code review Co-authored-by: Christoph Nakazawa Signed-off-by: MK (fengmk2) --- docs/.vitepress/config.mts | 40 ++++++++++----------- docs/.vitepress/theme/components/Footer.vue | 15 ++++---- docs/.vitepress/theme/index.ts | 2 +- docs/guide/monorepo.md | 8 ++--- vite.config.ts | 1 + 5 files changed, 33 insertions(+), 33 deletions(-) diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index 256a13aaaa..04c098330c 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -3,8 +3,8 @@ import { resolve } from 'node:path'; import type { VoidZeroThemeConfig } from '@voidzero-dev/vitepress-theme'; import { extendConfig } from '@voidzero-dev/vitepress-theme/config'; import { defineConfig, type HeadConfig } from 'vitepress'; +import { groupIconMdPlugin, groupIconVitePlugin } from 'vitepress-plugin-group-icons'; import { withMermaid } from 'vitepress-plugin-mermaid'; -import { groupIconMdPlugin, groupIconVitePlugin } from 'vitepress-plugin-group-icons' const taskRunnerGuideItems = [ { @@ -29,7 +29,6 @@ const guideSidebar = [ { text: 'Creating a Project', link: '/guide/create' }, { text: 'Migrate to Vite+', link: '/guide/migrate' }, { text: 'Installing Dependencies', link: '/guide/install' }, - { text: 'Monorepo', link: '/guide/monorepo' }, { text: 'Environment', link: '/guide/env' }, { text: 'Why Vite+', link: '/guide/why' }, ], @@ -73,6 +72,7 @@ const guideSidebar = [ { text: 'IDE Integration', link: '/guide/ide-integration' }, { text: 'CI', link: '/guide/ci' }, { text: 'Commit Hooks', link: '/guide/commit-hooks' }, + { text: 'Monorepo Guide', link: '/guide/monorepo' }, { text: 'Troubleshooting', link: '/guide/troubleshooting' }, ], }, @@ -127,9 +127,9 @@ export default extendConfig( plugins: [ groupIconVitePlugin({ customIcon: { - tsdown: 'https://tsdown.dev/tsdown.svg' - } - }) + tsdown: 'https://tsdown.dev/tsdown.svg', + }, + }), ], }, themeConfig: { @@ -197,24 +197,24 @@ export default extendConfig( footer: { copyright: `© ${new Date().getFullYear()} VoidZero Inc. and Vite+ contributors.`, nav: [ - { - title: "Company", + { + title: 'Company', items: [ - { text: "VoidZero", link: "https://voidzero.dev" }, - { text: "Vite", link: "https://vite.dev" }, - { text: "Vitest", link: "https://vitest.dev" }, - { text: "Rolldown", link: "https://rolldown.rs" }, - { text: "Oxc", link: "https://oxc.rs" }, + { text: 'VoidZero', link: 'https://voidzero.dev' }, + { text: 'Vite', link: 'https://vite.dev' }, + { text: 'Vitest', link: 'https://vitest.dev' }, + { text: 'Rolldown', link: 'https://rolldown.rs' }, + { text: 'Oxc', link: 'https://oxc.rs' }, ], }, ], - social: [ - { icon: "github", link: "https://github.com/voidzero-dev/vite-plus" }, - { icon: "x", link: "https://x.com/voidzerodev" }, - { icon: "discord", link: "https://discord.gg/cC6TEVFKSx" }, - { icon: "bluesky", link: "https://bsky.app/profile/voidzero.dev" }, - ], - } + social: [ + { icon: 'github', link: 'https://github.com/voidzero-dev/vite-plus' }, + { icon: 'x', link: 'https://x.com/voidzerodev' }, + { icon: 'discord', link: 'https://discord.gg/cC6TEVFKSx' }, + { icon: 'bluesky', link: 'https://bsky.app/profile/voidzero.dev' }, + ], + }, }, transformHead({ page, pageData }) { const url = 'https://viteplus.dev/' + page.replace(/\.md$/, '').replace(/index$/, ''); @@ -250,7 +250,7 @@ export default extendConfig( }, markdown: { config(md) { - md.use(groupIconMdPlugin) + md.use(groupIconMdPlugin); }, }, }), diff --git a/docs/.vitepress/theme/components/Footer.vue b/docs/.vitepress/theme/components/Footer.vue index 70ff6a950f..03a1ce344a 100644 --- a/docs/.vitepress/theme/components/Footer.vue +++ b/docs/.vitepress/theme/components/Footer.vue @@ -6,19 +6,18 @@ sponsorLink="https://github.com/voidzero-dev/vite-plus/blob/main/CONTRIBUTING.md" />
-
+
diff --git a/docs/.vitepress/theme/index.ts b/docs/.vitepress/theme/index.ts index 329cef7bfd..9d50a93e76 100644 --- a/docs/.vitepress/theme/index.ts +++ b/docs/.vitepress/theme/index.ts @@ -4,7 +4,7 @@ import type { Theme } from 'vitepress'; import Layout from './Layout.vue'; import './styles.css'; -import 'virtual:group-icons.css' +import 'virtual:group-icons.css'; export default { extends: BaseTheme, diff --git a/docs/guide/monorepo.md b/docs/guide/monorepo.md index e2690e1983..def5b9aa08 100644 --- a/docs/guide/monorepo.md +++ b/docs/guide/monorepo.md @@ -1,8 +1,8 @@ # Monorepo -Vite+ works well in a monorepo when the shared tool configuration lives in the workspace root. Put the root defaults in `vite.config.ts`, then use `overrides` to apply package-specific lint and format settings. +Vite+ supports monorepos with `vite.config.ts` at the root. You can define the defaults for `lint`, `fmt`, etc. at the root, and use `overrides` to apply package-specific lint and format settings. -This is the recommended pattern for shared quality tooling because the root config stays type-safe and composable, while each app or package can still keep its own Vite, Vitest, framework, or runtime files when that is useful. +Because `vite.config.ts` is just JavaScript, you can choose to put your entire config into this file or compose it using regular JavaScript imports. You can still have separate `vite.config.ts` files in each package for the Vite, Vitest, framework or runtime configuration. ## Root Config With Overrides @@ -86,9 +86,9 @@ export default defineConfig({ }); ``` -## Splitting Config Files +## Composing Configuration Files -You can still split configuration across your repository. Export normal JavaScript objects from nearby files, import them in the root config, and merge them into the matching override. +You can split configuration across your repository and compose them using JavaScript imports. Export JavaScript objects from nearby files or packages, import them in the root config, and merge them into the matching override. ```ts [tooling/lint/react.ts] import type { OxlintOverride } from 'vite-plus/lint'; diff --git a/vite.config.ts b/vite.config.ts index 093cde7a97..5b208c4458 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -71,6 +71,7 @@ export default defineConfig({ 'packages/cli/snap-tests/fmt-ignore-patterns/src/ignored', 'packages/cli/snap-tests-global/migration-lint-staged-ts-config', 'docs/**', + '!docs/.vitepress/**', 'ecosystem-ci/*/**', 'packages/test/**.cjs', 'packages/test/**.cts',