config-vp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ MIT License
2
+
3
+ Copyright (c) Orimay
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,331 @@
1
+ # config-vp
2
+
3
+ One shared [vite-plus](https://viteplus.dev) config for all your packages. Pick a project type and get linting, formatting, testing, building, and ready-to-run tasks — with zero boilerplate per package.
4
+
5
+ ## What you get
6
+
7
+ - **Linting** — a strict [oxlint](https://oxc.rs) ruleset (TypeScript, unicorn, import, jsdoc, promise) with stylistic and import-sorting rules.
8
+ - **Formatting** — an [oxfmt](https://oxc.rs) profile (single quotes, semicolons, trailing commas, Tailwind class sorting, import sorting).
9
+ - **Tasks** — `check`, `test`, `build`, `dev`, `release`, and more, tailored to your project type and runnable with `vp run <task>`.
10
+ - **Pre-commit checks** — staged files are checked and auto-fixed.
11
+ - **VSCode setup** — a one-command task to wire up the oxc extension at your workspace root.
12
+
13
+ Everything is opinionated and works out of the box. You can still override or extend any piece (see [Customizing](#customizing)).
14
+
15
+ ## Prerequisites
16
+
17
+ This package is driven by [vite-plus](https://viteplus.dev) — the `vp` CLI that runs your whole dev lifecycle (install, dev server, lint, format, test, build). Install it once, globally:
18
+
19
+ **Linux / macOS**
20
+
21
+ ```sh
22
+ curl -fsSL https://vite.plus | bash
23
+ ```
24
+
25
+ **Windows**
26
+
27
+ ```powershell
28
+ irm https://viteplus.dev/install.ps1 | iex
29
+ ```
30
+
31
+ Check it's available:
32
+
33
+ ```sh
34
+ vp --version
35
+ ```
36
+
37
+ ## Quick start
38
+
39
+ **1. Add the config to your package:**
40
+
41
+ ```sh
42
+ vp add -D config-vp
43
+ ```
44
+
45
+ This also pulls in everything the config needs (`oxlint`, `oxfmt`, and the lint plugins). If a compatible `vite-plus` is already in your workspace, that one is reused.
46
+
47
+ **2. Create `vite.config.ts`:**
48
+
49
+ ```ts
50
+ import { defineConfig } from 'config-vp';
51
+
52
+ const config = defineConfig({ type: 'lib' });
53
+ export default config;
54
+ ```
55
+
56
+ > **Important:** Assign to a `const` and export it — don't write `export default defineConfig(...)` inline. `vp` reads your default export statically to discover tasks; a direct call hides them and `vp run` reports "Task not found". See [Troubleshooting](#troubleshooting).
57
+
58
+ **3. Run something:**
59
+
60
+ ```sh
61
+ vp run check # lint + format, with autofix
62
+ vp run test # run tests
63
+ vp run build # build (vp pack / vite build / nuxt build, per type)
64
+ ```
65
+
66
+ That's it. Pick the `type` that matches your package below for the right build/dev tasks.
67
+
68
+ ## Project types
69
+
70
+ Set `type` to match what you're building. It drives Vue lint rules, library packaging, and the `build`/`dev`/`release` tasks.
71
+
72
+ | `type` | For | `build` | `dev` |
73
+ | --------------- | ------------------------------------ | --------------- | ----------------- |
74
+ | `lib` | A TypeScript library | `vp pack` | `vp pack --watch` |
75
+ | `lib:vue` | A Vue component library | `vp pack` | `vp pack --watch` |
76
+ | `lib:nuxt` | A Nuxt-targeted library | `vp pack` | `vp pack --watch` |
77
+ | `vue` | A Vue (Vite) application | `vite build` | `vite dev` |
78
+ | `nuxt:spa` | A Nuxt app, statically generated | `nuxt generate` | `nuxt dev` |
79
+ | `nuxt:ssr` | A Nuxt app, server-side rendered | `nuxt build` | `nuxt dev` |
80
+ | no `type` field | Shared root / non-buildable packages | _none_ | _none_ |
81
+
82
+ ### Recipes
83
+
84
+ **Library** — dual ESM + CJS output:
85
+
86
+ ```ts
87
+ import { defineConfig } from 'config-vp';
88
+
89
+ const config = defineConfig({
90
+ type: 'lib',
91
+ overrides: {
92
+ pack: {
93
+ entry: ['src/index.ts'],
94
+ format: ['esm', 'cjs'],
95
+ },
96
+ },
97
+ });
98
+ export default config;
99
+ ```
100
+
101
+ **Vue library** — same, with `type: 'lib:vue'` for SFC/template lint rules.
102
+
103
+ ```ts
104
+ import { defineConfig } from 'config-vp';
105
+
106
+ const config = defineConfig({
107
+ type: 'lib:vue',
108
+ overrides: {
109
+ pack: {
110
+ entry: ['src/index.ts'],
111
+ format: ['esm', 'cjs'],
112
+ },
113
+ },
114
+ });
115
+ export default config;
116
+ ```
117
+
118
+ **Nuxt app.** Nuxt doesn't read `vite.config.ts` — its Vite settings live under the `vite` key of `nuxt.config.ts`. Build the config in a small `nuxt.config.vite.ts` and wire it in:
119
+
120
+ ```ts
121
+ // nuxt.config.vite.ts
122
+ import type { NuxtConfig } from 'nuxt/schema';
123
+ import { defineConfig } from 'config-vp';
124
+
125
+ export const vite = defineConfig({
126
+ type: 'nuxt:spa', // or 'nuxt:ssr'
127
+ overrides: {
128
+ // any Vite/vite-plus options the app needs, e.g. plugins, define, test, …
129
+ },
130
+ }) as NuxtConfig['vite'];
131
+ ```
132
+
133
+ ```ts
134
+ // nuxt.config.ts
135
+ import { defineNuxtConfig } from 'nuxt/config';
136
+ import { vite } from './nuxt.config.vite';
137
+
138
+ export default defineNuxtConfig({ vite });
139
+ ```
140
+
141
+ The `export const vite = …` form is itself a const export, so `vp` discovers any `run.tasks` you add through `overrides.run`.
142
+
143
+ **Plain package** — lint + format + tests only, no build/dev (e.g. a config or scripts-only package). Just call `defineConfig()` with no `type`:
144
+
145
+ ```ts
146
+ import { defineConfig } from 'config-vp';
147
+
148
+ const config = defineConfig();
149
+ export default config;
150
+ ```
151
+
152
+ ## Tasks
153
+
154
+ Run any task with `vp run <task>`. Tasks are cached and run in dependency order across your workspace.
155
+
156
+ **Always available:**
157
+
158
+ | Task | Command | Purpose |
159
+ | --------------- | -------------------------------------- | -------------------------------- |
160
+ | `check` | `vp check --fix` | Lint + format, with autofix |
161
+ | `test` | `vp test --run --passWithNoTests` | Run the test suite once |
162
+ | `test:watch` | `vp test --passWithNoTests` | Run tests in watch mode |
163
+ | `test:coverage` | `vp test --coverage --passWithNoTests` | Run tests with a coverage report |
164
+
165
+ > `--passWithNoTests` means a package with no test files passes instead of failing — so `test`/`release` don't break in packages that don't have tests yet.
166
+
167
+ **Added by `type`:**
168
+
169
+ | Task | When | Command |
170
+ | --- | --- | --- |
171
+ | `build` | any `type` | see [Project types](#project-types) |
172
+ | `dev` | any `type` | see [Project types](#project-types) |
173
+ | `release` | `lib*` types | `vp check --fix && vp test --run --passWithNoTests && vp pack && vpx bumpp && pnpm publish` |
174
+
175
+ **Workspace root only:**
176
+
177
+ | Task | Command |
178
+ | -------------- | ----------------- |
179
+ | `setup:vscode` | `vp-setup-vscode` |
180
+
181
+ `setup:vscode` is added automatically to **workspace roots** (any package with a `pnpm-workspace.yaml` or a `workspaces` field, plus standalone projects). Nested sub-workspaces each get their own. It:
182
+
183
+ 1. Merges oxc settings into `.vscode/settings.json` (preserving your comments).
184
+ 2. Adds the `VoidZero.vite-plus-extension-pack` recommendation to `.vscode/extensions.json`.
185
+ 3. Installs the extension via the `code` CLI if available.
186
+
187
+ > For a whole-workspace lint/format, use the built-in `vp check`. To build every package, use `vp run -r build`.
188
+
189
+ ## Customizing
190
+
191
+ Two knobs, same shape — they differ only in how they merge:
192
+
193
+ - **`overrides`** — _replace_ semantics. Your values overwrite the defaults. Applied first.
194
+ - **`extends`** — _additive_ semantics. Arrays concatenate, objects deep-merge. Applied last, so it's never clobbered by an override.
195
+
196
+ ```ts
197
+ const config = defineConfig({
198
+ type: 'lib',
199
+ overrides: {
200
+ // Replace the pack entry point entirely
201
+ pack: {
202
+ entry: ['src/index.ts'],
203
+ format: ['esm', 'cjs'],
204
+ },
205
+ },
206
+ extends: {
207
+ // Add to the lint config without losing built-ins
208
+ lint: {
209
+ rules: { 'no-console': 'error' },
210
+ },
211
+ },
212
+ });
213
+ export default config;
214
+ ```
215
+
216
+ > `lint` is **always** deep-merged (never fully replaced), even under `overrides` — so you can't accidentally wipe the base ruleset.
217
+
218
+ ### Your existing Vite config goes here
219
+
220
+ `overrides` and `extends` accept **any** vite-plus `UserConfig` field, not just config-vp's own keys. So a whole normal Vite config — `plugins`, `resolve`, `define`, `server`, `optimizeDeps`, `worker`, `test`, `build`, `run.tasks`, … — drops straight into `extends` (additive) or `overrides` (replacing). There's nothing config-vp-specific to learn: keep writing Vite config, just nest it.
221
+
222
+ ```ts
223
+ const config = defineConfig({
224
+ type: 'nuxt:spa',
225
+ extends: {
226
+ plugins: [tailwindcss()],
227
+ define: { 'import.meta.env.VITE_RELEASE': JSON.stringify(release) },
228
+ optimizeDeps: { exclude: ['some-wasm-dep'] },
229
+ server: { fs: { allow: ['../..'] } },
230
+ test, // your vitest config
231
+ run: {
232
+ tasks: {
233
+ preview: { command: 'nuxt preview', cache: false },
234
+ deploy: { command: 'vpx tsx scripts/deploy.ts' },
235
+ },
236
+ },
237
+ },
238
+ });
239
+ export default config;
240
+ ```
241
+
242
+ Your entire app-specific Vite setup lives in `extends`, on top of config-vp's `nuxt:spa` defaults.
243
+
244
+ ### Ignore patterns (lint + format)
245
+
246
+ A top-level `ignorePatterns` is forwarded to **both** the linter and the formatter:
247
+
248
+ ```ts
249
+ defineConfig({
250
+ type: 'lib',
251
+ // Replace the built-in ignore list:
252
+ overrides: { ignorePatterns: ['only-this/**'] },
253
+ // …or add to it, keeping the defaults:
254
+ extends: { ignorePatterns: ['generated/**', 'vendor/**'] },
255
+ });
256
+ ```
257
+
258
+ ### Disable packaging
259
+
260
+ A `lib*` type includes packaging by default. Turn it off with `pack: false`:
261
+
262
+ ```ts
263
+ defineConfig({
264
+ type: 'lib',
265
+ overrides: { pack: false },
266
+ });
267
+ ```
268
+
269
+ ## Options
270
+
271
+ | Option | Type | Description |
272
+ | --- | --- | --- |
273
+ | `type` | `ProjectType` | `'lib' \| 'lib:vue' \| 'lib:nuxt' \| 'vue' \| 'nuxt:spa' \| 'nuxt:ssr'`. Omit for shared/root packages. |
274
+ | `overrides` | object | Replace-semantics customizations. Accepts `ignorePatterns` and `pack: false`. |
275
+ | `extends` | object | Additive customizations, applied after `overrides`. Same shape. |
276
+
277
+ ### Merge reference
278
+
279
+ Order: `base → overrides (replace) → extends (additive)`.
280
+
281
+ | Key | Under `overrides` | Under `extends` |
282
+ | --------------- | ------------------------------------- | -------------------- |
283
+ | `lint` | arrays concat, rules deep-merge | same |
284
+ | `fmt` | shallow-merge | recursive deep-merge |
285
+ | `pack` | shallow-merge (or `false` to disable) | recursive deep-merge |
286
+ | `staged` | shallow-merge | recursive deep-merge |
287
+ | `run` | shallow-merge, tasks combined | recursive deep-merge |
288
+ | arrays (others) | replace | concatenate |
289
+
290
+ ## Default ignore patterns
291
+
292
+ The base list applied to both linting and formatting (override or extend it via the [ignore-patterns shortcut](#ignore-patterns-lint--format)):
293
+
294
+ ```
295
+ *.log* **/.output **/.vp-tsconfig
296
+ **/.nuxt-storybook **/node_modules **/.vp-tmp-vite
297
+ **/.nuxt **/dist .env
298
+ **/.nitro **/.vue-types package-lock.json
299
+ **/.cache pnpm-lock.yaml
300
+ ```
301
+
302
+ ## API
303
+
304
+ The package exports a single function, `defineConfig`, along with its `ProjectType` and `ConfigOptions` types for annotation.
305
+
306
+ ## Troubleshooting
307
+
308
+ ### `vp run <task>` says "Task not found" (but `vp pack` works)
309
+
310
+ Your config almost certainly uses an inline default export. `vp` discovers tasks by statically reading the default export, and a direct call expression hides them. Use the `const` form:
311
+
312
+ ```ts
313
+ // ❌ tasks are invisible to `vp run`
314
+ export default defineConfig({ type: 'lib' });
315
+
316
+ // ✅ assign, then export
317
+ const config = defineConfig({ type: 'lib' });
318
+ export default config;
319
+ ```
320
+
321
+ ### Tasks show up duplicated or ambiguous across the workspace
322
+
323
+ Every workspace package needs a unique `name` in its `package.json`. An unnamed package (commonly the repo root) registers its tasks without a namespace, colliding with others. Give the root a name, e.g. `"name": "my-workspace-root"`.
324
+
325
+ ### `vp` crashes with `Cannot convert undefined or null to object`
326
+
327
+ The globally-installed `vp` and your workspace's `vite-plus` are different versions. Match the workspace `vite-plus` (in `package.json` and any `pnpm.overrides`) to `vp --version`, then run `vp install`.
328
+
329
+ ## License
330
+
331
+ MIT
@@ -0,0 +1,85 @@
1
+ #!/usr/bin/env node
2
+ /* eslint-disable typescript-eslint/no-unsafe-assignment, typescript-eslint/no-unsafe-argument, typescript-eslint/no-unsafe-member-access, typescript-eslint/no-unsafe-call, typescript-eslint/no-unsafe-return */
3
+
4
+ import { applyEdits, modify, parse } from 'jsonc-parser';
5
+ import { execSync } from 'node:child_process';
6
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
7
+ import { dirname, join } from 'node:path';
8
+
9
+ const EXTENSION_ID = 'VoidZero.vite-plus-extension-pack';
10
+
11
+ /** Walk up from cwd until a directory containing .vscode/ is found, or return cwd. */
12
+ function findVscodeRoot() {
13
+ let dir = process.cwd();
14
+ while (true) {
15
+ if (existsSync(join(dir, '.vscode'))) break;
16
+ const parent = dirname(dir);
17
+ if (parent === dir) break;
18
+ dir = parent;
19
+ }
20
+ return dir;
21
+ }
22
+
23
+ const vscodeDir = join(findVscodeRoot(), '.vscode');
24
+
25
+ const OXC_SETTINGS = {
26
+ 'editor.defaultFormatter': 'oxc.oxc-vscode',
27
+ 'editor.formatOnSave': true,
28
+ 'editor.formatOnSaveMode': 'file',
29
+ 'editor.codeActionsOnSave': {
30
+ 'source.fixAll.oxc': 'explicit',
31
+ },
32
+ 'oxc.enable': true,
33
+ 'oxc.typeAware': true,
34
+ };
35
+
36
+ const FORMAT_OPTIONS = { tabSize: 2, insertSpaces: true };
37
+
38
+ /** Apply key-value pairs to a JSONC file in-place, preserving comments. */
39
+ function mergeJsonc(filePath, entries) {
40
+ let text = existsSync(filePath) ? readFileSync(filePath, 'utf8') : '{}';
41
+ for (const [path, value] of entries) {
42
+ const edits = modify(text, path, value, { formattingOptions: FORMAT_OPTIONS });
43
+ text = applyEdits(text, edits);
44
+ }
45
+ writeFileSync(filePath, text);
46
+ }
47
+
48
+ // 1. Ensure .vscode dir exists
49
+ if (!existsSync(vscodeDir)) mkdirSync(vscodeDir);
50
+
51
+ // 2. Update .vscode/settings.json with oxc settings
52
+ const settingsEntries = Object.entries(OXC_SETTINGS).flatMap(([key, value]) => {
53
+ if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
54
+ return Object.entries(value).map(([subKey, subValue]) => [[key, subKey], subValue]);
55
+ }
56
+ return [[[key], value]];
57
+ });
58
+ mergeJsonc(join(vscodeDir, 'settings.json'), settingsEntries);
59
+ console.log('VSCode settings updated with oxc configuration.');
60
+
61
+ // 3. Add extension to .vscode/extensions.json recommendations
62
+ const extensionsPath = join(vscodeDir, 'extensions.json');
63
+ let extText = existsSync(extensionsPath)
64
+ ? readFileSync(extensionsPath, 'utf8')
65
+ : '{"recommendations":[]}';
66
+ const extJson = parse(extText);
67
+ const recs = extJson.recommendations ?? [];
68
+ if (!recs.includes(EXTENSION_ID)) {
69
+ const edits = modify(extText, ['recommendations', recs.length], EXTENSION_ID, {
70
+ formattingOptions: FORMAT_OPTIONS,
71
+ isArrayInsertion: true,
72
+ });
73
+ extText = applyEdits(extText, edits);
74
+ writeFileSync(extensionsPath, extText);
75
+ console.log(`Added ${EXTENSION_ID} to extensions.json recommendations.`);
76
+ } else {
77
+ console.log(`${EXTENSION_ID} already in extensions.json recommendations.`);
78
+ }
79
+
80
+ // 4. Install extension if `code` CLI is available
81
+ try {
82
+ execSync(`code --install-extension ${EXTENSION_ID} --force`, { stdio: 'inherit' });
83
+ } catch {
84
+ console.log(`Could not install extension automatically. Install it manually: ${EXTENSION_ID}`);
85
+ }
@@ -0,0 +1,22 @@
1
+ import { UserConfig } from "vite-plus";
2
+
3
+ //#region src/index.d.ts
4
+ type ProjectType = 'lib' | 'lib:nuxt' | 'lib:vue' | 'nuxt:spa' | 'nuxt:ssr' | 'vue';
5
+ type DeepPartial<T> = T extends ((...args: never[]) => unknown) ? T : T extends (infer U)[] ? DeepPartial<U>[] : T extends object ? { [K in keyof T]?: DeepPartial<T[K]> } : T;
6
+ interface ConfigOptions {
7
+ /** Project type — drives vue lint, pack inclusion, and dev/build/release tasks. */
8
+ type?: ProjectType;
9
+ /** Replace semantics: values replace defaults, objects shallow-merge at known keys. Exception: lint is always additive (deep-merged). Pass `pack: false` to suppress pack inclusion. */
10
+ overrides?: DeepPartial<UserConfig> & {
11
+ ignorePatterns?: string[];
12
+ pack?: unknown;
13
+ };
14
+ /** Additive semantics: arrays concatenate, objects deep-merge. Applied AFTER overrides. Pass `pack: false` to suppress pack inclusion. */
15
+ extends?: DeepPartial<UserConfig> & {
16
+ ignorePatterns?: string[];
17
+ pack?: unknown;
18
+ };
19
+ }
20
+ declare function defineConfig(options?: ConfigOptions): UserConfig;
21
+ //#endregion
22
+ export { ConfigOptions, ProjectType, defineConfig };
package/dist/index.mjs ADDED
@@ -0,0 +1,761 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ //#region src/ignore.ts
4
+ const ignorePatterns = [
5
+ "*.log*",
6
+ "**/.nuxt-storybook",
7
+ "**/.nuxt",
8
+ "**/.nitro",
9
+ "**/.cache",
10
+ "**/.output",
11
+ "**/node_modules",
12
+ "**/dist",
13
+ "**/.vue-types",
14
+ "**/.vp-tsconfig",
15
+ "**/.vp-tmp-vite",
16
+ ".env",
17
+ "package-lock.json",
18
+ "pnpm-lock.yaml"
19
+ ];
20
+ //#endregion
21
+ //#region src/fmt.ts
22
+ const fmt = {
23
+ ignorePatterns,
24
+ tabWidth: 2,
25
+ useTabs: false,
26
+ singleQuote: true,
27
+ semi: true,
28
+ proseWrap: "always",
29
+ arrowParens: "avoid",
30
+ bracketSameLine: false,
31
+ bracketSpacing: true,
32
+ vueIndentScriptAndStyle: true,
33
+ printWidth: 100,
34
+ endOfLine: "lf",
35
+ trailingComma: "all",
36
+ singleAttributePerLine: true,
37
+ experimentalTailwindcss: { attributes: [
38
+ "class",
39
+ "appear-from-class",
40
+ "appear-active-class",
41
+ "appear-to-class",
42
+ "enter-from-class",
43
+ "enter-active-class",
44
+ "enter-to-class",
45
+ "leave-from-class",
46
+ "leave-active-class",
47
+ "leave-to-class"
48
+ ] },
49
+ experimentalSortImports: {
50
+ groups: [
51
+ "type-import",
52
+ ["value-builtin", "value-external"],
53
+ "type-internal",
54
+ "value-internal",
55
+ [
56
+ "type-parent",
57
+ "type-sibling",
58
+ "type-index"
59
+ ],
60
+ [
61
+ "value-parent",
62
+ "value-sibling",
63
+ "value-index"
64
+ ],
65
+ "unknown"
66
+ ],
67
+ ignoreCase: false,
68
+ newlinesBetween: false
69
+ },
70
+ embeddedLanguageFormatting: "auto",
71
+ insertFinalNewline: true,
72
+ objectWrap: "preserve",
73
+ quoteProps: "consistent",
74
+ sortPackageJson: true,
75
+ overrides: [{
76
+ files: ["*.md"],
77
+ options: { proseWrap: "never" }
78
+ }]
79
+ };
80
+ //#endregion
81
+ //#region src/lint.ts
82
+ const lint = {
83
+ plugins: [
84
+ "typescript",
85
+ "unicorn",
86
+ "oxc",
87
+ "eslint",
88
+ "import",
89
+ "jsdoc",
90
+ "node",
91
+ "promise"
92
+ ],
93
+ jsPlugins: ["@stylistic/eslint-plugin", "eslint-plugin-perfectionist"],
94
+ categories: { correctness: "off" },
95
+ env: { builtin: true },
96
+ ignorePatterns,
97
+ options: {
98
+ typeCheck: true,
99
+ typeAware: true,
100
+ reportUnusedDisableDirectives: "deny",
101
+ denyWarnings: true,
102
+ maxWarnings: 0
103
+ },
104
+ rules: {
105
+ "constructor-super": "error",
106
+ "for-direction": "error",
107
+ "no-async-promise-executor": "error",
108
+ "no-case-declarations": "error",
109
+ "no-class-assign": "error",
110
+ "no-compare-neg-zero": "error",
111
+ "no-cond-assign": "error",
112
+ "no-const-assign": "error",
113
+ "no-constant-binary-expression": "error",
114
+ "no-constant-condition": "error",
115
+ "no-control-regex": "error",
116
+ "no-debugger": "error",
117
+ "no-delete-var": "error",
118
+ "no-dupe-class-members": "error",
119
+ "no-dupe-else-if": "error",
120
+ "no-dupe-keys": "error",
121
+ "no-duplicate-case": "error",
122
+ "no-empty": "error",
123
+ "no-empty-character-class": "error",
124
+ "no-empty-pattern": "error",
125
+ "no-empty-static-block": "error",
126
+ "no-ex-assign": "error",
127
+ "no-extra-boolean-cast": "error",
128
+ "no-fallthrough": "error",
129
+ "no-func-assign": "error",
130
+ "no-global-assign": "error",
131
+ "no-import-assign": "error",
132
+ "no-invalid-regexp": "error",
133
+ "no-irregular-whitespace": "error",
134
+ "no-loss-of-precision": "error",
135
+ "no-misleading-character-class": "error",
136
+ "no-new-native-nonconstructor": "error",
137
+ "no-nonoctal-decimal-escape": "error",
138
+ "no-obj-calls": "error",
139
+ "no-prototype-builtins": "error",
140
+ "no-redeclare": "error",
141
+ "no-regex-spaces": "error",
142
+ "no-self-assign": "error",
143
+ "no-setter-return": "error",
144
+ "no-shadow-restricted-names": "error",
145
+ "no-sparse-arrays": "error",
146
+ "no-this-before-super": "error",
147
+ "no-unexpected-multiline": "error",
148
+ "no-unsafe-finally": "error",
149
+ "no-unsafe-negation": "error",
150
+ "no-unsafe-optional-chaining": "error",
151
+ "no-unused-labels": "error",
152
+ "no-unused-private-class-members": "error",
153
+ "no-unused-vars": ["error", {
154
+ args: "all",
155
+ argsIgnorePattern: "^_",
156
+ caughtErrors: "all",
157
+ caughtErrorsIgnorePattern: "^_",
158
+ destructuredArrayIgnorePattern: "^_",
159
+ ignoreRestSiblings: true,
160
+ varsIgnorePattern: "^_"
161
+ }],
162
+ "no-useless-backreference": "error",
163
+ "no-useless-catch": "error",
164
+ "no-useless-escape": "error",
165
+ "no-with": "error",
166
+ "require-yield": "error",
167
+ "use-isnan": "error",
168
+ "valid-typeof": "error",
169
+ "@stylistic/array-bracket-spacing": ["error", "never"],
170
+ "@stylistic/arrow-parens": ["error", "as-needed"],
171
+ "@stylistic/arrow-spacing": ["error", {
172
+ after: true,
173
+ before: true
174
+ }],
175
+ "@stylistic/block-spacing": ["error", "always"],
176
+ "@stylistic/brace-style": ["error", "1tbs"],
177
+ "@stylistic/comma-dangle": ["error", "always-multiline"],
178
+ "@stylistic/comma-spacing": ["error", {
179
+ after: true,
180
+ before: false
181
+ }],
182
+ "@stylistic/comma-style": ["error", "last"],
183
+ "@stylistic/computed-property-spacing": [
184
+ "error",
185
+ "never",
186
+ { enforceForClassMembers: true }
187
+ ],
188
+ "@stylistic/dot-location": ["error", "property"],
189
+ "@stylistic/eol-last": "error",
190
+ "@stylistic/generator-star-spacing": ["error", {
191
+ after: true,
192
+ before: false
193
+ }],
194
+ "@stylistic/key-spacing": ["error", {
195
+ afterColon: true,
196
+ beforeColon: false
197
+ }],
198
+ "@stylistic/keyword-spacing": ["error", {
199
+ after: true,
200
+ before: true
201
+ }],
202
+ "@stylistic/lines-between-class-members": [
203
+ "error",
204
+ "always",
205
+ { exceptAfterSingleLine: true }
206
+ ],
207
+ "@stylistic/max-statements-per-line": ["error", { max: 1 }],
208
+ "@stylistic/multiline-ternary": ["error", "always-multiline"],
209
+ "@stylistic/new-parens": "error",
210
+ "@stylistic/no-extra-parens": ["error", "functions"],
211
+ "@stylistic/no-floating-decimal": "error",
212
+ "@stylistic/no-mixed-spaces-and-tabs": "error",
213
+ "@stylistic/no-multi-spaces": "error",
214
+ "@stylistic/no-multiple-empty-lines": ["error", {
215
+ max: 1,
216
+ maxBOF: 0,
217
+ maxEOF: 0
218
+ }],
219
+ "@stylistic/no-tabs": "error",
220
+ "@stylistic/no-trailing-spaces": "error",
221
+ "@stylistic/no-whitespace-before-property": "error",
222
+ "@stylistic/object-curly-spacing": ["error", "always"],
223
+ "@stylistic/operator-linebreak": [
224
+ "error",
225
+ "after",
226
+ { overrides: {
227
+ ":": "before",
228
+ "?": "before",
229
+ "|": "before",
230
+ "&": "before"
231
+ } }
232
+ ],
233
+ "@stylistic/padded-blocks": ["error", {
234
+ blocks: "never",
235
+ classes: "never",
236
+ switches: "never"
237
+ }],
238
+ "@stylistic/quote-props": ["error", "consistent-as-needed"],
239
+ "@stylistic/quotes": [
240
+ "error",
241
+ "single",
242
+ { avoidEscape: true }
243
+ ],
244
+ "@stylistic/rest-spread-spacing": ["error", "never"],
245
+ "@stylistic/semi": ["error", "always"],
246
+ "@stylistic/semi-spacing": ["error", {
247
+ after: true,
248
+ before: false
249
+ }],
250
+ "@stylistic/space-before-blocks": ["error", "always"],
251
+ "@stylistic/space-before-function-paren": ["error", {
252
+ anonymous: "always",
253
+ asyncArrow: "always",
254
+ named: "never"
255
+ }],
256
+ "@stylistic/space-in-parens": ["error", "never"],
257
+ "@stylistic/space-infix-ops": "error",
258
+ "@stylistic/space-unary-ops": ["error", {
259
+ nonwords: false,
260
+ words: true
261
+ }],
262
+ "@stylistic/spaced-comment": [
263
+ "error",
264
+ "always",
265
+ {
266
+ block: {
267
+ balanced: true,
268
+ exceptions: ["*"],
269
+ markers: ["!"]
270
+ },
271
+ line: {
272
+ exceptions: ["/", "#"],
273
+ markers: ["/"]
274
+ }
275
+ }
276
+ ],
277
+ "@stylistic/template-curly-spacing": "error",
278
+ "@stylistic/template-tag-spacing": ["error", "never"],
279
+ "@stylistic/type-annotation-spacing": ["error", {}],
280
+ "@stylistic/type-generic-spacing": "error",
281
+ "@stylistic/type-named-tuple-spacing": "error",
282
+ "@stylistic/wrap-iife": [
283
+ "error",
284
+ "any",
285
+ { functionPrototypeMethods: true }
286
+ ],
287
+ "@stylistic/yield-star-spacing": ["error", {
288
+ after: true,
289
+ before: false
290
+ }],
291
+ "@stylistic/jsx-closing-bracket-location": "error",
292
+ "@stylistic/jsx-closing-tag-location": "error",
293
+ "@stylistic/jsx-curly-brace-presence": ["error", { propElementValues: "always" }],
294
+ "@stylistic/jsx-curly-newline": "error",
295
+ "@stylistic/jsx-curly-spacing": ["error", "never"],
296
+ "@stylistic/jsx-equals-spacing": "error",
297
+ "@stylistic/jsx-first-prop-new-line": "error",
298
+ "@stylistic/jsx-function-call-newline": ["error", "multiline"],
299
+ "@stylistic/jsx-indent-props": ["error", 2],
300
+ "@stylistic/jsx-max-props-per-line": ["error", {
301
+ maximum: 1,
302
+ when: "multiline"
303
+ }],
304
+ "@stylistic/jsx-one-expression-per-line": ["error", { allow: "single-child" }],
305
+ "@stylistic/jsx-quotes": "error",
306
+ "@stylistic/jsx-tag-spacing": ["error", {
307
+ afterOpening: "never",
308
+ beforeClosing: "never",
309
+ beforeSelfClosing: "always",
310
+ closingSlash: "never"
311
+ }],
312
+ "@stylistic/jsx-wrap-multilines": ["error", {
313
+ arrow: "parens-new-line",
314
+ assignment: "parens-new-line",
315
+ condition: "parens-new-line",
316
+ declaration: "parens-new-line",
317
+ logical: "parens-new-line",
318
+ prop: "parens-new-line",
319
+ propertyValue: "parens-new-line",
320
+ return: "parens-new-line"
321
+ }],
322
+ "@typescript-eslint/await-thenable": "error",
323
+ "@typescript-eslint/ban-ts-comment": ["error", { minimumDescriptionLength: 10 }],
324
+ "no-array-constructor": "error",
325
+ "@typescript-eslint/no-array-delete": "error",
326
+ "@typescript-eslint/no-base-to-string": "error",
327
+ "@typescript-eslint/no-confusing-void-expression": "error",
328
+ "@typescript-eslint/no-deprecated": "error",
329
+ "@typescript-eslint/no-duplicate-enum-values": "error",
330
+ "@typescript-eslint/no-duplicate-type-constituents": "error",
331
+ "@typescript-eslint/no-dynamic-delete": "error",
332
+ "@typescript-eslint/no-empty-object-type": ["error", {
333
+ allowInterfaces: "with-single-extends",
334
+ allowObjectTypes: "never"
335
+ }],
336
+ "@typescript-eslint/no-explicit-any": "error",
337
+ "@typescript-eslint/no-extra-non-null-assertion": "error",
338
+ "@typescript-eslint/no-extraneous-class": "error",
339
+ "@typescript-eslint/no-floating-promises": "error",
340
+ "@typescript-eslint/no-for-in-array": "error",
341
+ "@typescript-eslint/no-implied-eval": "error",
342
+ "@typescript-eslint/no-invalid-void-type": ["error", { allowAsThisParameter: true }],
343
+ "@typescript-eslint/no-meaningless-void-operator": "error",
344
+ "@typescript-eslint/no-misused-new": "error",
345
+ "@typescript-eslint/no-misused-promises": "error",
346
+ "@typescript-eslint/no-misused-spread": "error",
347
+ "@typescript-eslint/no-mixed-enums": "error",
348
+ "@typescript-eslint/no-namespace": "error",
349
+ "@typescript-eslint/no-non-null-asserted-nullish-coalescing": "error",
350
+ "@typescript-eslint/no-non-null-asserted-optional-chain": "error",
351
+ "@typescript-eslint/no-redundant-type-constituents": "error",
352
+ "@typescript-eslint/no-require-imports": "error",
353
+ "@typescript-eslint/no-this-alias": "error",
354
+ "@typescript-eslint/no-unnecessary-boolean-literal-compare": "error",
355
+ "@typescript-eslint/no-unnecessary-template-expression": "error",
356
+ "@typescript-eslint/no-unnecessary-type-arguments": "error",
357
+ "@typescript-eslint/no-unnecessary-type-assertion": "error",
358
+ "@typescript-eslint/no-unnecessary-type-constraint": "error",
359
+ "@typescript-eslint/no-unsafe-argument": "error",
360
+ "@typescript-eslint/no-unsafe-assignment": "error",
361
+ "@typescript-eslint/no-unsafe-call": "error",
362
+ "@typescript-eslint/no-unsafe-declaration-merging": "error",
363
+ "@typescript-eslint/no-unsafe-enum-comparison": "error",
364
+ "@typescript-eslint/no-unsafe-function-type": "error",
365
+ "@typescript-eslint/no-unsafe-member-access": "error",
366
+ "@typescript-eslint/no-unsafe-return": "error",
367
+ "@typescript-eslint/no-unsafe-unary-minus": "error",
368
+ "no-unused-expressions": ["error", {
369
+ allowShortCircuit: true,
370
+ allowTernary: true,
371
+ allowTaggedTemplates: true
372
+ }],
373
+ "no-useless-constructor": "error",
374
+ "@typescript-eslint/no-wrapper-object-types": "error",
375
+ "@typescript-eslint/only-throw-error": "error",
376
+ "@typescript-eslint/prefer-as-const": "error",
377
+ "@typescript-eslint/prefer-literal-enum-member": "error",
378
+ "@typescript-eslint/prefer-namespace-keyword": "error",
379
+ "@typescript-eslint/prefer-promise-reject-errors": "error",
380
+ "@typescript-eslint/prefer-reduce-type-parameter": "error",
381
+ "@typescript-eslint/prefer-return-this-type": "error",
382
+ "@typescript-eslint/related-getter-setter-pairs": "error",
383
+ "@typescript-eslint/require-await": "error",
384
+ "@typescript-eslint/restrict-plus-operands": ["error", {
385
+ allowAny: false,
386
+ allowBoolean: false,
387
+ allowNullish: false,
388
+ allowNumberAndString: false,
389
+ allowRegExp: false
390
+ }],
391
+ "@typescript-eslint/return-await": ["error", "error-handling-correctness-only"],
392
+ "@typescript-eslint/triple-slash-reference": "error",
393
+ "@typescript-eslint/unbound-method": ["error", { ignoreStatic: true }],
394
+ "@typescript-eslint/use-unknown-in-catch-callback-variable": "error",
395
+ "@typescript-eslint/adjacent-overload-signatures": "error",
396
+ "@typescript-eslint/array-type": "error",
397
+ "@typescript-eslint/ban-tslint-comment": "error",
398
+ "@typescript-eslint/class-literal-property-style": "error",
399
+ "@typescript-eslint/consistent-generic-constructors": "error",
400
+ "@typescript-eslint/consistent-indexed-object-style": "error",
401
+ "@typescript-eslint/consistent-type-assertions": "error",
402
+ "@typescript-eslint/consistent-type-definitions": "error",
403
+ "@typescript-eslint/no-confusing-non-null-assertion": "error",
404
+ "no-empty-function": "error",
405
+ "@typescript-eslint/no-inferrable-types": "error",
406
+ "@typescript-eslint/non-nullable-type-assertion-style": "error",
407
+ "@typescript-eslint/prefer-for-of": "error",
408
+ "@typescript-eslint/prefer-function-type": "error",
409
+ "@typescript-eslint/prefer-includes": "error",
410
+ "@typescript-eslint/prefer-nullish-coalescing": ["error", { ignoreTernaryTests: true }],
411
+ "perfectionist/sort-variable-declarations": ["error", {
412
+ type: "natural",
413
+ order: "asc"
414
+ }],
415
+ "perfectionist/sort-intersection-types": ["error", {
416
+ type: "natural",
417
+ order: "asc"
418
+ }],
419
+ "perfectionist/sort-heritage-clauses": ["error", {
420
+ type: "natural",
421
+ order: "asc"
422
+ }],
423
+ "perfectionist/sort-array-includes": ["error", {
424
+ type: "natural",
425
+ order: "asc"
426
+ }],
427
+ "perfectionist/sort-named-imports": ["error", {
428
+ type: "natural",
429
+ order: "asc"
430
+ }],
431
+ "perfectionist/sort-named-exports": ["error", {
432
+ type: "natural",
433
+ order: "asc"
434
+ }],
435
+ "perfectionist/sort-union-types": ["error", {
436
+ type: "natural",
437
+ order: "asc"
438
+ }],
439
+ "perfectionist/sort-switch-case": ["error", {
440
+ type: "natural",
441
+ order: "asc"
442
+ }],
443
+ "perfectionist/sort-decorators": ["error", {
444
+ type: "natural",
445
+ order: "asc"
446
+ }],
447
+ "perfectionist/sort-jsx-props": ["error", {
448
+ type: "natural",
449
+ order: "asc"
450
+ }],
451
+ "perfectionist/sort-exports": ["error", {
452
+ type: "natural",
453
+ order: "asc"
454
+ }],
455
+ "perfectionist/sort-enums": ["error", {
456
+ type: "natural",
457
+ order: "asc"
458
+ }],
459
+ "perfectionist/sort-sets": ["error", {
460
+ type: "natural",
461
+ order: "asc"
462
+ }],
463
+ "perfectionist/sort-maps": ["error", {
464
+ type: "natural",
465
+ order: "asc"
466
+ }],
467
+ "@typescript-eslint/consistent-type-imports": ["error", { fixStyle: "separate-type-imports" }],
468
+ "prefer-const": ["error", {
469
+ destructuring: "all",
470
+ ignoreReadBeforeAssign: false
471
+ }]
472
+ },
473
+ overrides: [{
474
+ files: [
475
+ "**/*.ts",
476
+ "**/*.tsx",
477
+ "**/*.mts",
478
+ "**/*.cts"
479
+ ],
480
+ rules: {
481
+ "constructor-super": "off",
482
+ "no-class-assign": "off",
483
+ "no-const-assign": "off",
484
+ "no-dupe-class-members": "off",
485
+ "no-dupe-keys": "off",
486
+ "no-func-assign": "off",
487
+ "no-import-assign": "off",
488
+ "no-new-native-nonconstructor": "off",
489
+ "no-obj-calls": "off",
490
+ "no-redeclare": "off",
491
+ "no-setter-return": "off",
492
+ "no-this-before-super": "off",
493
+ "no-unsafe-negation": "off",
494
+ "no-var": "error",
495
+ "no-with": "off",
496
+ "prefer-rest-params": "error",
497
+ "prefer-spread": "error"
498
+ }
499
+ }]
500
+ };
501
+ //#endregion
502
+ //#region src/pack.ts
503
+ const pack = {
504
+ dts: true,
505
+ format: ["esm"],
506
+ sourcemap: false
507
+ };
508
+ //#endregion
509
+ //#region src/run.ts
510
+ function hasWorkspaceMarker(dir) {
511
+ if (existsSync(join(dir, "pnpm-workspace.yaml")) || existsSync(join(dir, "pnpm-workspace.yml"))) return true;
512
+ try {
513
+ const pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf8"));
514
+ return Array.isArray(pkg.workspaces);
515
+ } catch {
516
+ return false;
517
+ }
518
+ }
519
+ /**
520
+ * Directory of the vite config that called us. process.cwd() is unreliable — vp
521
+ * evaluates every package's config from the invocation cwd — so we recover the
522
+ * caller's location from the stack instead. vp bundles each config to
523
+ * `<pkg>/node_modules/.vite-temp/...`; the package dir is the part before that.
524
+ */
525
+ function callerPackageDir() {
526
+ const stack = (/* @__PURE__ */ new Error()).stack ?? "";
527
+ for (const line of stack.split("\n")) {
528
+ const temp = line.match(/(\/.+?)\/node_modules\/\.vite-temp\//);
529
+ if (temp) return temp[1];
530
+ const direct = line.match(/(\/.+?)\/[^/]*\.config\.[^/\s:)]+/);
531
+ if (direct && !direct[1].includes("/node_modules/")) return direct[1];
532
+ }
533
+ }
534
+ /** True when the calling config's package is the workspace root (or a standalone project). */
535
+ function isProjectRoot() {
536
+ const dir = callerPackageDir() ?? process.cwd();
537
+ if (hasWorkspaceMarker(dir)) return true;
538
+ let parent = dirname(dir);
539
+ while (parent !== dirname(parent)) {
540
+ if (hasWorkspaceMarker(parent)) return false;
541
+ parent = dirname(parent);
542
+ }
543
+ return true;
544
+ }
545
+ function buildRunConfig(options = {}) {
546
+ const tasks = {
547
+ "check": { command: "vp check --fix" },
548
+ "test": { command: "vp test --run --passWithNoTests" },
549
+ "test:watch": {
550
+ command: "vp test --passWithNoTests",
551
+ cache: false
552
+ },
553
+ "test:coverage": { command: "vp test --coverage --passWithNoTests" }
554
+ };
555
+ switch (true) {
556
+ case options.lib:
557
+ tasks.build = { command: "vp pack" };
558
+ tasks.dev = {
559
+ command: "vp pack --watch",
560
+ cache: false
561
+ };
562
+ tasks.release = {
563
+ command: "vp check --fix && vp test --run --passWithNoTests && vp pack && vpx bumpp && pnpm publish",
564
+ cache: false
565
+ };
566
+ break;
567
+ case options.nuxt === "spa":
568
+ tasks.build = { command: "nuxt generate" };
569
+ tasks.dev = {
570
+ command: "nuxt dev",
571
+ cache: false
572
+ };
573
+ break;
574
+ case options.nuxt === "ssr":
575
+ tasks.build = { command: "nuxt build" };
576
+ tasks.dev = {
577
+ command: "nuxt dev",
578
+ cache: false
579
+ };
580
+ break;
581
+ case options.vueApp:
582
+ tasks.build = { command: "vite build" };
583
+ tasks.dev = {
584
+ command: "vite dev",
585
+ cache: false
586
+ };
587
+ break;
588
+ }
589
+ if (isProjectRoot()) tasks["setup:vscode"] = {
590
+ command: "vp-setup-vscode",
591
+ cache: false
592
+ };
593
+ return { tasks };
594
+ }
595
+ //#endregion
596
+ //#region src/staged.ts
597
+ const staged = { "*.{js,ts,tsx,vue}": "vp check --fix" };
598
+ //#endregion
599
+ //#region src/vue-lint.ts
600
+ const vueLint = {
601
+ plugins: ["vue"],
602
+ overrides: [{
603
+ files: ["**/*.vue"],
604
+ rules: { "@stylistic/no-multiple-empty-lines": ["error", {
605
+ max: 1,
606
+ maxBOF: 1,
607
+ maxEOF: 0
608
+ }] }
609
+ }, {
610
+ files: ["**/*.stories.ts", "**/*.stories.tsx"],
611
+ rules: {
612
+ "@typescript-eslint/no-unsafe-assignment": "off",
613
+ "@typescript-eslint/no-unsafe-argument": "off",
614
+ "@typescript-eslint/no-unsafe-call": "off",
615
+ "@typescript-eslint/no-unsafe-member-access": "off",
616
+ "@typescript-eslint/no-unsafe-return": "off"
617
+ }
618
+ }],
619
+ rules: {
620
+ "vue/define-emits-declaration": ["error", "type-based"],
621
+ "vue/define-props-declaration": ["error", "type-based"],
622
+ "vue/define-props-destructuring": "allow",
623
+ "vue/no-export-in-script-setup": "error",
624
+ "vue/no-import-compiler-macros": "error",
625
+ "vue/no-lifecycle-after-await": "error",
626
+ "vue/no-multiple-slot-args": "error",
627
+ "vue/no-required-prop-with-default": "error",
628
+ "vue/no-this-in-before-route-enter": "error",
629
+ "vue/prefer-import-from-vue": "error",
630
+ "vue/require-default-export": "error",
631
+ "vue/require-typed-ref": "error",
632
+ "vue/valid-define-emits": "error",
633
+ "vue/valid-define-props": "error"
634
+ }
635
+ };
636
+ //#endregion
637
+ //#region src/index.ts
638
+ const DEEP_MERGE_KEYS = new Set([
639
+ "fmt",
640
+ "pack",
641
+ "staged"
642
+ ]);
643
+ function deepExtend(target, source) {
644
+ if (source === void 0) return target;
645
+ if (Array.isArray(target) && Array.isArray(source)) return [...target, ...source];
646
+ if (target !== null && typeof target === "object" && !Array.isArray(target) && source !== null && typeof source === "object" && !Array.isArray(source)) {
647
+ const result = { ...target };
648
+ for (const [key, value] of Object.entries(source)) result[key] = key in result ? deepExtend(result[key], value) : value;
649
+ return result;
650
+ }
651
+ return source;
652
+ }
653
+ function mergeLayers(base, ...layers) {
654
+ const result = { ...base };
655
+ for (const layer of layers) {
656
+ if (layer.lint) {
657
+ const { extends: layerExtends, ...layerLintRest } = layer.lint;
658
+ const baseArr = result.lint ? [result.lint] : [];
659
+ const combined = layerExtends !== void 0 ? [...baseArr, ...Array.isArray(layerExtends) ? layerExtends : [layerExtends]] : baseArr;
660
+ result.lint = combined.length > 0 ? {
661
+ extends: combined,
662
+ ...layerLintRest
663
+ } : { ...layerLintRest };
664
+ }
665
+ if (layer.run) result.run = {
666
+ ...result.run,
667
+ ...layer.run,
668
+ tasks: {
669
+ ...result.run?.tasks,
670
+ ...layer.run.tasks
671
+ }
672
+ };
673
+ for (const [key, value] of Object.entries(layer)) {
674
+ if (key === "lint" || key === "run") continue;
675
+ if (DEEP_MERGE_KEYS.has(key)) result[key] = {
676
+ ...result[key],
677
+ ...value
678
+ };
679
+ else result[key] = value;
680
+ }
681
+ }
682
+ return result;
683
+ }
684
+ function mergeLintConfigs(...configs) {
685
+ if (configs.length === 0) return {};
686
+ if (configs.length === 1) return { ...configs[0] };
687
+ return configs.reduce((acc, config) => {
688
+ const merged = { ...acc };
689
+ for (const [key, value] of Object.entries(config)) {
690
+ const existing = merged[key];
691
+ if (Array.isArray(existing) && Array.isArray(value)) merged[key] = [...existing, ...value];
692
+ else if (existing && typeof existing === "object" && !Array.isArray(existing) && value && typeof value === "object" && !Array.isArray(value)) merged[key] = {
693
+ ...existing,
694
+ ...value
695
+ };
696
+ else merged[key] = value;
697
+ }
698
+ return merged;
699
+ });
700
+ }
701
+ function defineConfig(options = {}) {
702
+ const t = options.type;
703
+ const isVue = t === "lib:vue" || t === "lib:nuxt" || t === "vue" || t === "nuxt:spa" || t === "nuxt:ssr";
704
+ const isLib = t === "lib" || t === "lib:vue" || t === "lib:nuxt";
705
+ const overridesPack = options.overrides?.pack;
706
+ const extendsPack = options.extends?.pack;
707
+ const isPack = (isLib || !!overridesPack || !!extendsPack) && overridesPack !== false && extendsPack !== false;
708
+ const lintLayers = [lint];
709
+ if (isVue) lintLayers.push(vueLint);
710
+ if (options.overrides?.lint) lintLayers.push(options.overrides.lint);
711
+ if (options.extends?.lint) lintLayers.push(options.extends.lint);
712
+ let mergedLint = mergeLintConfigs(...lintLayers);
713
+ if (options.overrides?.ignorePatterns) mergedLint = {
714
+ ...mergedLint,
715
+ ignorePatterns: options.overrides.ignorePatterns
716
+ };
717
+ if (options.extends?.ignorePatterns) mergedLint = {
718
+ ...mergedLint,
719
+ ignorePatterns: [...mergedLint.ignorePatterns ?? [], ...options.extends.ignorePatterns]
720
+ };
721
+ let mergedFmt = fmt;
722
+ if (options.overrides?.ignorePatterns) mergedFmt = {
723
+ ...mergedFmt,
724
+ ignorePatterns: options.overrides.ignorePatterns
725
+ };
726
+ if (options.extends?.ignorePatterns) mergedFmt = {
727
+ ...mergedFmt,
728
+ ignorePatterns: [...mergedFmt?.ignorePatterns ?? [], ...options.extends.ignorePatterns]
729
+ };
730
+ const run = buildRunConfig({
731
+ lib: isLib,
732
+ nuxt: t === "nuxt:spa" ? "spa" : t === "nuxt:ssr" ? "ssr" : void 0,
733
+ vueApp: t === "vue"
734
+ });
735
+ let result = {
736
+ lint: mergedLint,
737
+ fmt: mergedFmt,
738
+ staged,
739
+ run,
740
+ ...isPack && { pack }
741
+ };
742
+ if (options.overrides) {
743
+ const { lint: _l, ignorePatterns: _ip, pack: _p, ...rest } = options.overrides;
744
+ const layer = overridesPack !== void 0 && overridesPack !== false ? {
745
+ ...rest,
746
+ pack: overridesPack
747
+ } : rest;
748
+ if (Object.keys(layer).length > 0) result = mergeLayers(result, layer);
749
+ }
750
+ if (options.extends) {
751
+ const { lint: _l, ignorePatterns: _ip, pack: _p, ...rest } = options.extends;
752
+ const layer = extendsPack !== void 0 && extendsPack !== false ? {
753
+ ...rest,
754
+ pack: extendsPack
755
+ } : rest;
756
+ if (Object.keys(layer).length > 0) result = deepExtend(result, layer);
757
+ }
758
+ return result;
759
+ }
760
+ //#endregion
761
+ export { defineConfig };
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "config-vp",
3
+ "version": "1.0.0",
4
+ "description": "Shared vite-plus configuration — opinionated defaults for linting, formatting, task running, staged checks, and VSCode setup. Optional Vue and pack layers with deep-merge overrides.",
5
+ "keywords": [
6
+ "config",
7
+ "eslint",
8
+ "formatter",
9
+ "linter",
10
+ "monorepo",
11
+ "oxfmt",
12
+ "oxlint",
13
+ "perfectionist",
14
+ "stylistic",
15
+ "typescript",
16
+ "vite-plus",
17
+ "vscode",
18
+ "vue"
19
+ ],
20
+ "homepage": "https://github.com/orimay/config-vp#readme",
21
+ "bugs": {
22
+ "url": "https://github.com/orimay/config-vp/issues"
23
+ },
24
+ "license": "MIT",
25
+ "author": "Dmitrii Baranov <dmitrii.a.baranov@gmail.com>",
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+https://github.com/orimay/config-vp.git"
29
+ },
30
+ "bin": {
31
+ "vp-setup-vscode": "./bin/vp-setup-vscode.mjs"
32
+ },
33
+ "files": [
34
+ "bin",
35
+ "dist"
36
+ ],
37
+ "type": "module",
38
+ "main": "./dist/index.mjs",
39
+ "module": "./dist/index.mjs",
40
+ "types": "./dist/index.d.mts",
41
+ "exports": {
42
+ ".": {
43
+ "types": "./dist/index.d.mts",
44
+ "import": "./dist/index.mjs"
45
+ },
46
+ "./package.json": "./package.json"
47
+ },
48
+ "dependencies": {
49
+ "@stylistic/eslint-plugin": "^5.10.0",
50
+ "eslint-plugin-perfectionist": "^5.8.0",
51
+ "jsonc-parser": "^3.3.1",
52
+ "oxfmt": "^0.45.0",
53
+ "oxlint": "^1.60.0"
54
+ },
55
+ "devDependencies": {
56
+ "@types/node": "^24.10.1",
57
+ "typescript": "^6.0.2"
58
+ },
59
+ "peerDependencies": {
60
+ "vite-plus": ">=0.1.24"
61
+ }
62
+ }