diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml index 8a005721ac..5675d96c2f 100644 --- a/.github/workflows/e2e-test.yml +++ b/.github/workflows/e2e-test.yml @@ -320,6 +320,11 @@ jobs: node-version: 24 command: | vp check --fix + - name: vite-plus-monorepo-overrides + node-version: 24 + command: | + vp check --fix + vp run verify - name: varlet node-version: 22 command: | 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..9d641f7777 --- /dev/null +++ b/docs/guide/monorepo.md @@ -0,0 +1,178 @@ +# 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. + +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: + +```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', + 'vitest/no-disabled-tests': 'error', + }, + }, + ], + }, +}); +``` + +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. diff --git a/ecosystem-ci/repo.json b/ecosystem-ci/repo.json index e762c5d1e1..6bc4f6e79c 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": "d89fe8f6479679e32e7a105fad85a2139beb2802", + "forceFreshMigration": true + }, "varlet": { "repository": "https://github.com/varletjs/varlet.git", "branch": "dev",