@liangmi/vp-config 0.0.0-alpha.0 → 0.1.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/README.md CHANGED
@@ -1,8 +1,6 @@
1
1
  # @liangmi/vp-config
2
2
 
3
- Liang's opinionated Vite+ config presets for JavaScript development, including linting, formatting, task running and more.
4
-
5
- It is designed to work with different kinds of project, cli / tui development, library, and website development (WIP, waiting for [Oxlint's better Vue support](https://github.com/oxc-project/oxc/issues/15761))
3
+ Liang's united [Vite+](https://viteplus.dev/) config presets for JavaScript development opinionated, strict and designed to be universal with different kinds of projects.
6
4
 
7
5
  ## Usage
8
6
 
@@ -15,15 +13,116 @@ vp install -D @liangmi/vp-config
15
13
  And modify your `vite.config.ts` like that:
16
14
 
17
15
  ```typescript
18
- import { cli } from "@liangmi/vp-config";
16
+ import { base } from "@liangmi/vp-config";
17
+
18
+ export default base({
19
+ /* Your personal config overrides, will be merged deeply */
20
+ });
21
+ ```
22
+
23
+ > [!WARNING]
24
+ > Considering Vite+ is now still alpha, the internal API can be unstable and expected to change, please manage to use `@liangmi/vp-config` with the latest Vite+. If you found something that doesn't work expectedly, please [submit an issue](https://github.com/liangmiQwQ/vp-config/issues/new).
25
+
26
+ ### Categories
27
+
28
+ We provide four config categories for different kinds of projects.
29
+
30
+ | Category | Description | Recommended for |
31
+ | --------- | ------------------------------------------------------ | -------------------- |
32
+ | `base` | Pure and basic config | Workspace root |
33
+ | `cli` | Config for Node.js CLI and React/Vue Tui applications | CLI and TUI projects |
34
+ | `lib` | Config with library bundling defaults | Libraries |
35
+ | `website` | Config for browser environment and website development | Websites |
36
+
37
+ The `website` category is experimental and its defaults may change before the package reaches a stable release. React and browser linting are available, but Vue template linting is still waiting for [better Vue support in Oxlint](https://github.com/oxc-project/oxc/issues/15761).
38
+
39
+ For monorepos, different presets should be used in combination. We should use `base` the workspace root, and use other categories as needed.
40
+
41
+ ### Customizable
42
+
43
+ Each category is a wrapper of Vite+'s `defineConfig`. The config passed to it overrides and deeply merges with the preset.
44
+
45
+ > [!TIP]
46
+ >
47
+ > Deep merging means your config only needs to specify what should change. Nested preset options that you do not override remain enabled, while values from your config take precedence.
48
+
49
+ Use `.only()` to load only selected parts of a preset:
50
+
51
+ ```typescript
52
+ import { base } from "@liangmi/vp-config";
19
53
 
20
- export default cli({
21
- /* Your personal config overrides */
54
+ export default base.only(["lint", "fmt"], {
55
+ lint: {
56
+ /* Your own lint config */
57
+ },
58
+ fmt: {
59
+ /* Your own format config */
60
+ },
22
61
  });
23
62
  ```
24
63
 
25
- Considering Vite+ is now still alpha, the internal API can be unstable and expected to change, please manage to use `@liangmi/vp-config` with the latest Vite+. If you found something that doesn't work expectedly, please [submit an issue](https://github.com/liangmiQwQ/vp-config/issues/new).
64
+ Use `.exclude()` to omit selected parts while keeping the rest:
65
+
66
+ ```typescript
67
+ import { base } from "@liangmi/vp-config";
68
+
69
+ export default base.exclude(["staged"], {});
70
+ ```
71
+
72
+ Available config parts are `fmt`, `lint`, `pack`, `run`, and `staged`, depending on the selected category.
73
+
74
+ ## What's included by default
75
+
76
+ Vite+ is a united toolchain for JavaScript development, it includes linting, formatting, library bundling, git hooks, test, task runner, website development, etc.
77
+
78
+ `@liangmi/vp-config` tries to extract reusable configurations from these, in order to improve the development experience as much as possible.
79
+
80
+ ### Lint
81
+
82
+ The lint config prioritizes correctness and fast feedback. Rules that prevent bugs report errors, while style and fixable readability rules generally report warnings and are left to autofixes. Warnings also fail the lint command, keeping the codebase consistent without treating every style concern as a hand-written task.
83
+
84
+ All categories include a strict Oxlint config with type-aware linting and type checking enabled, which means you do not need to run `tsc` manually. Correctness, performance, suspicious, and nursery rules report errors.
85
+
86
+ `console.log` is not allowed except in the `cli` category and Node.js script files. Test files enable Vitest rules, while the `cli` and `website` categories add React and Vue component rules. The `website` category also enables browser-specific rules.
87
+
88
+ The included `liangmi` Oxlint plugin checks that presets are loaded correctly. It reports orphan `vite.config.ts` files, preset imports outside config files, missing preset wrappers, improper root or project categories, and configs that mix library and website signals.
89
+
90
+ ### Format
91
+
92
+ The format config follows a simple philosophy: remove syntax that does not improve readability, and let the formatter handle mechanical consistency.
93
+
94
+ All categories include an Oxfmt config using single quotes, no semicolons, no unnecessary trailing commas, sorted imports, and sorted `package.json` fields. Embedded-language formatting is disabled for `base` and `lib`, and enabled for `cli` and `website`.
95
+
96
+ ### Pack
97
+
98
+ The packaging presets provide the best-practice defaults for each kind of project while leaving project-specific details explicit. Because the package entry depends on the project's source layout, you still need to define `pack.entry` manually.
99
+
100
+ The `lib` preset generates `.d.ts` and package exports with fixed extensions. The `cli` preset targets Node.js, minifies output, strips `node:` protocol prefixes, and disables `dts` generation.
101
+
102
+ ### Cached commands
103
+
104
+ In order to make full use of Vite+'s powerful cache system without too much config and make it contributors-friendly, we provide cached tasks task wrappers for common Vite+ commands. This feature is included in all categories.
105
+
106
+ In most cases, they should be treated more like cached versions of Vite+ commands rather than normal user-defined tasks. For example, users can run `vpr ccheck` as a cached replacement for `vp check`.
107
+
108
+ | Task | Command |
109
+ | --------- | ----------- |
110
+ | `cbuild` | `vp build` |
111
+ | `cpack` | `vp pack` |
112
+ | `clint` | `vp lint` |
113
+ | `cfmt` | `vp fmt` |
114
+ | `cformat` | `vp format` |
115
+ | `ccheck` | `vp check` |
116
+ | `ctest` | `vp test` |
117
+
118
+ Run them with `vp run <task>`, such as `vp run cpack`, or shorthand `vpr cpack`. They can also be used in `package.json` scripts while retaining Vite+ task caching.
119
+
120
+ ### Staged files
121
+
122
+ Staged-file checks keep automatic fixes close to the commit workflow, so only code that is about to be committed is processed.
123
+
124
+ All categories run `vp check --fix` for staged files. Run `vp config` to install Vite+'s commit hook.
26
125
 
27
126
  ## License
28
127
 
29
- [MIT](./LICENSE) License © 2026-PRESENT Liang
128
+ [MIT](./LICENSE) License © 2026-PRESENT Liang & Contributors
@@ -0,0 +1,5 @@
1
+ //#region src/oxlint-plugin/constants.d.ts
2
+ declare const configNames: readonly ["base", "cli", "lib", "website"];
3
+ type ConfigName = (typeof configNames)[number];
4
+ //#endregion
5
+ export { ConfigName as t };
package/dist/index.d.mts CHANGED
@@ -1,4 +1,28 @@
1
+ import { UserConfig, defineConfig } from "vite-plus";
2
+ import { PackUserConfig } from "vite-plus/pack";
3
+
4
+ //#region src/entry.d.ts
5
+ interface PresetConfig {
6
+ fmt?: UserConfig['fmt'];
7
+ lint?: UserConfig['lint'];
8
+ pack?: PackUserConfig;
9
+ run?: UserConfig['run'];
10
+ staged?: UserConfig['staged'];
11
+ }
12
+ type ConfigArgs = Parameters<typeof defineConfig>;
13
+ type ConfigResult = ReturnType<typeof defineConfig>;
14
+ type Unique<T extends readonly unknown[]> = T extends readonly [infer Head, ...infer Tail] ? Head extends Tail[number] ? never : readonly [Head, ...Unique<Tail>] : T;
15
+ type ConfigPart<PresetConfig> = Extract<keyof NonNullable<PresetConfig>, string>;
16
+ type ConfigFunction<PresetConfig> = <const Parts extends readonly ConfigPart<PresetConfig>[]>(parts: Parts & Unique<Parts>, ...args: ConfigArgs) => ConfigResult;
17
+ type ConfigEntry<PresetConfig> = {
18
+ only: ConfigFunction<PresetConfig>;
19
+ exclude: ConfigFunction<PresetConfig>;
20
+ } & typeof defineConfig;
21
+ //#endregion
1
22
  //#region src/index.d.ts
2
- declare function fn(): string;
23
+ declare const base: ConfigEntry<PresetConfig>;
24
+ declare const cli: ConfigEntry<PresetConfig>;
25
+ declare const lib: ConfigEntry<PresetConfig>;
26
+ declare const website: ConfigEntry<PresetConfig>;
3
27
  //#endregion
4
- export { fn };
28
+ export { base, cli, lib, website };
package/dist/index.mjs CHANGED
@@ -1,6 +1,446 @@
1
- //#region src/index.ts
2
- function fn() {
3
- return "Hello, tsdown!";
1
+ import { c as writeRuntimeInfo } from "./info-CHtwxjhy.mjs";
2
+ import { defineConfig, mergeConfig } from "vite-plus";
3
+ //#region src/base/fmt.ts
4
+ const fmtBase = {
5
+ arrowParens: "avoid",
6
+ embeddedLanguageFormatting: "off",
7
+ singleQuote: true,
8
+ sortImports: { partitionByComment: true },
9
+ sortPackageJson: { sortScripts: true },
10
+ semi: false,
11
+ trailingComma: "none"
12
+ };
13
+ //#endregion
14
+ //#region src/shared/lint.ts
15
+ const cliOverride = {
16
+ env: { node: true },
17
+ plugins: ["node"],
18
+ rules: {
19
+ "no-console": "off",
20
+ "unicorn/no-process-exit": "off",
21
+ "node/no-path-concat": "error"
22
+ }
23
+ };
24
+ const componentOverride = {
25
+ env: { vue: true },
26
+ plugins: [
27
+ "react",
28
+ "react-perf",
29
+ "vue"
30
+ ],
31
+ rules: {
32
+ "react/react-in-jsx-scope": "off",
33
+ "react/no-clone-element": "error",
34
+ "react/no-react-children": "error",
35
+ "react/prefer-function-component": "error",
36
+ "vue/no-import-compiler-macros": "warn",
37
+ "react/rules-of-hooks": "error",
38
+ "react/jsx-no-useless-fragment": "warn",
39
+ "react/no-unescaped-entities": "warn",
40
+ "react/jsx-curly-brace-presence": ["warn", {
41
+ children: "never",
42
+ propElementValues: "always",
43
+ props: "never"
44
+ }],
45
+ "vue/define-emits-declaration": ["warn", "type-literal"],
46
+ "vue/define-props-declaration": ["warn", "type-based"],
47
+ "vue/next-tick-style": ["warn", "promise"],
48
+ "vue/prop-name-casing": ["warn", "camelCase"],
49
+ "vue/require-prop-types": "off",
50
+ "react/jsx-max-depth": "off",
51
+ "react/no-redundant-should-component-update": "off",
52
+ "react/jsx-props-no-spreading": "off"
53
+ }
54
+ };
55
+ //#endregion
56
+ //#region src/base/lint.ts
57
+ const suspicious = {
58
+ "no-shadow": "off",
59
+ "typescript/no-unsafe-type-assertion": "off"
60
+ };
61
+ const nursery = {
62
+ "no-undef": "off",
63
+ "import/named": "off",
64
+ "no-restricted-exports": "off"
65
+ };
66
+ const style = {
67
+ "no-duplicate-imports": ["warn", { allowSeparateTypeImports: true }],
68
+ "typescript/consistent-type-definitions": ["warn", "interface"],
69
+ "typescript/consistent-indexed-object-style": ["warn", "record"],
70
+ "eslint/func-names": ["warn", "never"],
71
+ "id-length": ["warn", { checkGeneric: false }],
72
+ "typescript/method-signature-style": ["warn", "property"],
73
+ "unicorn/prefer-ternary": ["warn", "only-single-line"],
74
+ "import/exports-last": "off",
75
+ "func-style": "off",
76
+ "init-declarations": "off",
77
+ "import/no-anonymous-default-export": "off",
78
+ "unicorn/no-await-expression-member": "off",
79
+ "max-params": "off",
80
+ "max-statements": "off",
81
+ "no-continue": "off",
82
+ "no-nested-ternary": "off",
83
+ "unicorn/no-nested-ternary": "off",
84
+ "no-magic-numbers": "off",
85
+ "import/no-namespace": "off",
86
+ "import/no-named-export": "off",
87
+ "no-ternary": "off",
88
+ "sort-imports": "off",
89
+ "sort-keys": "off",
90
+ "import/group-exports": "off",
91
+ "import/no-nodejs-modules": "off",
92
+ "import/prefer-default-export": "off",
93
+ "prefer-await-to-then": "off",
94
+ "unicorn/no-null": "off",
95
+ "eslint/prefer-named-capture-group": "off"
96
+ };
97
+ const restriction = {
98
+ "oxc/bad-bitwise-operator": "error",
99
+ "class-methods-use-this": "error",
100
+ "default-case": "error",
101
+ "typescript/explicit-function-return-type": "error",
102
+ "typescript/explicit-module-boundary-types": "error",
103
+ "import/extensions": [
104
+ "error",
105
+ "always",
106
+ {
107
+ checkTypeImports: true,
108
+ ignorePackages: true
109
+ }
110
+ ],
111
+ "unicorn/no-abusive-eslint-disable": "error",
112
+ "no-alert": "error",
113
+ "import/no-amd": "error",
114
+ "no-array-reduce": "error",
115
+ "unicorn/prefer-module": "error",
116
+ "no-console": "error",
117
+ "import/no-cycle": "error",
118
+ "oxc/no-const-enum": "error",
119
+ "typescript/no-dynamic-delete": "error",
120
+ "no-dynamic-require": "error",
121
+ "no-empty": "error",
122
+ "no-empty-function": "error",
123
+ "no-explicit-any": "error",
124
+ "no-implicit-globals": "error",
125
+ "typescript/no-import-type-side-effects": "error",
126
+ "typescript/no-invalid-void-type": "error",
127
+ "unicorn/no-magic-array-flat-depth": "error",
128
+ "typescript/no-namespace": "error",
129
+ "typescript/no-non-null-asserted-nullish-coalescing": "error",
130
+ "no-param-reassign": "error",
131
+ "unicorn/no-process-exit": "error",
132
+ "no-sequences": "error",
133
+ "no-var": "error",
134
+ "non-nullable-type-assertion-style": "error",
135
+ "unicorn/prefer-modern-math-apis": "error",
136
+ "unicode-bom": "error",
137
+ "prefer-node-protocol": "warn",
138
+ "unicorn/prefer-number-properties": "warn",
139
+ "no-array-for-each": "warn",
140
+ "no-div-regex": "warn",
141
+ "no-default-export": "warn",
142
+ "no-empty-object-type": "warn",
143
+ "no-proto": "warn"
144
+ };
145
+ const pedantic = {
146
+ eqeqeq: "error",
147
+ "oxc/branches-sharing-code": "error",
148
+ "array-callback-return": "error",
149
+ "unicorn/consistent-empty-array-spread": "error",
150
+ "max-classes-per-file": ["error", { max: 0 }],
151
+ "max-depth": ["error", { max: 5 }],
152
+ "eslint/max-nested-callbacks": ["error", { max: 6 }],
153
+ "unicorn/new-for-builtins": "error",
154
+ "no-case-declarations": "error",
155
+ "no-constructor-return": "error",
156
+ "typescript/no-deprecated": "error",
157
+ "no-fallthrough": "error",
158
+ "unicorn/no-immediate-mutation": "error",
159
+ "no-loop-func": "error",
160
+ "typescript/no-misused-promises": "error",
161
+ "typescript/no-mixed-enums": "error",
162
+ "unicorn/no-new-buffer": "error",
163
+ "no-object-constructor": "error",
164
+ "no-promise-executor-return": "error",
165
+ "no-self-compare": "error",
166
+ "no-throw-literal": "error",
167
+ "no-unnecessary-array-flat-depth": "error",
168
+ "typescript/only-throw-error": "error",
169
+ "prefer-array-some": "error",
170
+ "prefer-code-point": "error",
171
+ "typescript/prefer-enum-initializers": "error",
172
+ "unicorn/prefer-event-target": "error",
173
+ "typescript/prefer-nullish-coalescing": "error",
174
+ "typescript/prefer-promise-reject-errors": "error",
175
+ "typescript/require-await": "error",
176
+ "require-unicode-regexp": "error",
177
+ "typescript/return-await": ["error", "never"],
178
+ "unicorn/no-hex-escape": "warn",
179
+ "unicorn/no-typeof-undefined": "warn",
180
+ "unicorn/no-unnecessary-array-splice-count": "warn",
181
+ "unicorn/no-unnecessary-slice-end": "warn",
182
+ "unicorn/no-instanceof-array": "warn",
183
+ "no-else-return": "warn",
184
+ "typescript/ban-ts-comment": "warn",
185
+ "typescript/no-confusing-void-expression": "warn",
186
+ "unicorn/escape-case": "warn",
187
+ "unicorn/explicit-length-check": "warn",
188
+ "no-array-constructor": "warn",
189
+ "unicorn/no-useless-promise-resolve-reject": "warn",
190
+ "no-useless-undefined": "warn",
191
+ "prefer-date-now": "warn",
192
+ "prefer-import-meta-properties": "warn",
193
+ "prefer-includes": "warn",
194
+ "prefer-math-min-max": "warn",
195
+ "unicorn/prefer-prototype-methods": "warn",
196
+ "unicorn/prefer-regexp-test": "warn",
197
+ "unicorn/prefer-string-replace-all": "warn",
198
+ "unicorn/prefer-string-slice": "warn",
199
+ "prefer-type-error": "warn",
200
+ "unicorn/require-number-to-fixed-digits-argument": "warn",
201
+ "typescript/strict-void-return": "warn",
202
+ "unicorn/prefer-native-coercion-functions": "warn",
203
+ "unicorn/prefer-array-flat": "warn",
204
+ "eslint/no-useless-return": "warn",
205
+ "unicorn/no-useless-switch-case": "warn",
206
+ "no-lonely-if": "warn",
207
+ "unicorn/no-negation-in-equality-check": "warn",
208
+ "no-negated-condition": "warn"
209
+ };
210
+ const lintBase = {
211
+ categories: {
212
+ correctness: "error",
213
+ perf: "error",
214
+ suspicious: "error",
215
+ nursery: "error",
216
+ style: "warn",
217
+ restriction: "off",
218
+ pedantic: "off"
219
+ },
220
+ jsPlugins: [{
221
+ name: "liangmi",
222
+ specifier: "@liangmi/vp-config/oxlint-plugin"
223
+ }],
224
+ plugins: [
225
+ "eslint",
226
+ "oxc",
227
+ "import",
228
+ "promise",
229
+ "typescript",
230
+ "unicorn"
231
+ ],
232
+ rules: {
233
+ ...suspicious,
234
+ ...nursery,
235
+ ...restriction,
236
+ ...pedantic,
237
+ ...style,
238
+ "liangmi/no-orphan-vite-config": "error",
239
+ "liangmi/no-useless-vp-preset-imports": "error",
240
+ "liangmi/use-preset-vp-config": "error",
241
+ "liangmi/load-proper-vp-config-category": "error",
242
+ "liangmi/no-mixed-project": "error"
243
+ },
244
+ options: {
245
+ typeAware: true,
246
+ typeCheck: true,
247
+ denyWarnings: true,
248
+ reportUnusedDisableDirectives: "warn",
249
+ respectEslintDisableDirectives: false
250
+ },
251
+ overrides: [
252
+ {
253
+ env: {
254
+ node: true,
255
+ vitest: true
256
+ },
257
+ plugins: ["vitest"],
258
+ rules: {
259
+ "vitest/consistent-test-filename": ["warn", { pattern: ".*.test.ts$" }],
260
+ "vitest/consistent-test-it": ["warn", {
261
+ fn: "it",
262
+ withinDescribe: "it"
263
+ }],
264
+ "vitest/no-hooks": "off",
265
+ "vitest/require-top-level-describe": "off",
266
+ "vitest/prefer-strict-boolean-matchers": "off",
267
+ "vitest/max-expects": "off",
268
+ "vitest/prefer-expect-assertions": "off",
269
+ "vitest/prefer-importing-vitest-globals": "off",
270
+ "vitest/no-importing-vitest-globals": "off",
271
+ "vitest/no-large-snapshots": "off",
272
+ "vitest/no-restricted-matchers": "off",
273
+ "vitest/no-restricted-vi-methods": "off"
274
+ },
275
+ files: ["*.test.ts", "*.spec.ts"]
276
+ },
277
+ {
278
+ files: [
279
+ "./scripts/**",
280
+ "./script/**",
281
+ "./*.ts",
282
+ "./*.js"
283
+ ],
284
+ ...cliOverride
285
+ },
286
+ {
287
+ rules: {
288
+ "import/no-default-export": "off",
289
+ "no-console": "error"
290
+ },
291
+ files: ["*.config.ts"]
292
+ }
293
+ ]
294
+ };
295
+ //#endregion
296
+ //#region src/base/run.ts
297
+ const lintInput = ["!node_modules/.vp-config/info.json", "index.html"];
298
+ const runBase = {
299
+ tasks: {
300
+ cbuild: "vp build",
301
+ ccheck: {
302
+ command: "vp check",
303
+ input: lintInput
304
+ },
305
+ cfmt: "vp fmt",
306
+ cformat: "vp format",
307
+ clint: {
308
+ command: "vp lint",
309
+ input: lintInput
310
+ },
311
+ cpack: "vp pack",
312
+ ctest: "vp test"
313
+ },
314
+ cache: {
315
+ scripts: false,
316
+ tasks: true
317
+ }
318
+ };
319
+ //#endregion
320
+ //#region src/base/staged.ts
321
+ const stagedBase = { "*": "vp check --fix" };
322
+ //#endregion
323
+ //#region src/base/index.ts
324
+ const baseConfig = {
325
+ fmt: fmtBase,
326
+ lint: lintBase,
327
+ run: runBase,
328
+ staged: stagedBase
329
+ };
330
+ //#endregion
331
+ //#region src/website/fmt.ts
332
+ const fmtWebsite = {
333
+ ...fmtBase,
334
+ jsxSingleQuote: true,
335
+ embeddedLanguageFormatting: "auto",
336
+ sortTailwindcss: false
337
+ };
338
+ //#endregion
339
+ //#region src/cli/index.ts
340
+ const cliConfig = {
341
+ fmt: fmtWebsite,
342
+ lint: mergeConfig(lintBase, mergeConfig(cliOverride, componentOverride)),
343
+ pack: {
344
+ dts: false,
345
+ minify: true,
346
+ platform: "node",
347
+ nodeProtocol: "strip"
348
+ },
349
+ run: runBase,
350
+ staged: stagedBase
351
+ };
352
+ //#endregion
353
+ //#region src/entry.ts
354
+ function createConfigEntry(presetConfig, category) {
355
+ const entry = ((config) => defineMergedConfig(presetConfig, config, category));
356
+ const only = (parts, ...args) => defineMergedConfig(pickPresetConfig(presetConfig, parts), args[0], category);
357
+ const exclude = (parts, ...args) => defineMergedConfig(omitPresetConfig(presetConfig, parts), args[0], category);
358
+ return Object.assign(entry, {
359
+ only,
360
+ exclude
361
+ });
362
+ }
363
+ function defineMergedConfig(presetConfig, config, category) {
364
+ if (typeof config === "function") return defineConfig(async (env) => {
365
+ return trackRuntimeInfo(mergePresetConfig(presetConfig, await config(env)), category);
366
+ });
367
+ if (config instanceof Promise) return defineConfig(config.then((userConfig) => trackRuntimeInfo(mergePresetConfig(presetConfig, userConfig), category)));
368
+ return defineConfig(trackRuntimeInfo(mergePresetConfig(presetConfig, config), category));
369
+ }
370
+ function pickPresetConfig(presetConfig, parts) {
371
+ return Object.fromEntries(parts.map((part) => [part, presetConfig[part]]));
4
372
  }
373
+ function omitPresetConfig(presetConfig, parts) {
374
+ const excludedParts = new Set(parts);
375
+ return Object.fromEntries(Object.entries(presetConfig).filter(([part]) => !excludedParts.has(part)));
376
+ }
377
+ function mergePresetConfig(presetConfig, userConfig) {
378
+ const config = { ...userConfig };
379
+ if (presetConfig.fmt) config.fmt = mergeConfig(presetConfig.fmt, userConfig.fmt ?? {});
380
+ if (presetConfig.lint) config.lint = mergeLintConfig(presetConfig.lint, userConfig.lint);
381
+ if (presetConfig.pack) config.pack = mergePackConfig(presetConfig.pack, userConfig.pack);
382
+ if (presetConfig.run) config.run = mergeConfig(presetConfig.run, userConfig.run ?? {});
383
+ if (presetConfig.staged) config.staged = mergeStagedConfig(presetConfig.staged, userConfig.staged);
384
+ return config;
385
+ }
386
+ function trackRuntimeInfo(config, category) {
387
+ writeRuntimeInfo({
388
+ category,
389
+ config
390
+ });
391
+ return config;
392
+ }
393
+ function mergeLintConfig(presetLint, userLint) {
394
+ return mergeConfig(presetLint, userLint ?? {});
395
+ }
396
+ function mergePackConfig(presetPack, userPack) {
397
+ if (Array.isArray(userPack)) return userPack.map((packConfig) => mergeConfig(presetPack, packConfig));
398
+ return mergeConfig(presetPack, userPack ?? {});
399
+ }
400
+ function mergeStagedConfig(presetStaged, userStaged) {
401
+ if (!isStagedObjectConfig(presetStaged)) return userStaged ?? presetStaged;
402
+ if (userStaged && !isStagedObjectConfig(userStaged)) return userStaged;
403
+ return mergeConfig(presetStaged, userStaged ?? {});
404
+ }
405
+ function isStagedObjectConfig(config) {
406
+ return typeof config === "object";
407
+ }
408
+ //#endregion
409
+ //#region src/lib/index.ts
410
+ const libConfig = {
411
+ fmt: fmtBase,
412
+ lint: mergeConfig(lintBase, {}),
413
+ pack: {
414
+ fixedExtension: true,
415
+ exports: true,
416
+ dts: { tsgo: true }
417
+ },
418
+ run: runBase,
419
+ staged: stagedBase
420
+ };
421
+ //#endregion
422
+ //#region src/website/index.ts
423
+ const websiteConfig = {
424
+ fmt: fmtWebsite,
425
+ lint: mergeConfig(mergeConfig(lintBase, componentOverride), {
426
+ env: { browser: true },
427
+ rules: {
428
+ "prefer-dom-node-append": "warn",
429
+ "prefer-dom-node-dataset": "warn",
430
+ "unicorn/prefer-query-selector": "warn",
431
+ "prefer-dom-node-remove": "warn",
432
+ "prefer-blob-reading-methods": "warn",
433
+ "import/no-unassigned-import": ["error", { allow: ["**/*.css"] }]
434
+ }
435
+ }),
436
+ run: runBase,
437
+ staged: stagedBase
438
+ };
439
+ //#endregion
440
+ //#region src/index.ts
441
+ const base = createConfigEntry(baseConfig, "base");
442
+ const cli = createConfigEntry(cliConfig, "cli");
443
+ const lib = createConfigEntry(libConfig, "lib");
444
+ const website = createConfigEntry(websiteConfig, "website");
5
445
  //#endregion
6
- export { fn };
446
+ export { base, cli, lib, website };
@@ -0,0 +1,306 @@
1
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
2
+ import { dirname, join, normalize } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ //#region src/oxlint-plugin/constants.ts
5
+ const packageName = "@liangmi/vp-config";
6
+ const pluginName = "liangmi";
7
+ const infoDirectoryName = ".vp-config";
8
+ const infoFileName = "info.json";
9
+ const projectConfigNames = [
10
+ "cli",
11
+ "lib",
12
+ "website"
13
+ ];
14
+ const rootConfigNames = ["base"];
15
+ const configNames = [...rootConfigNames, ...projectConfigNames];
16
+ const viteConfigNames = [
17
+ "vite.config.ts",
18
+ "vite.config.mts",
19
+ "vite.config.cts",
20
+ "vite.config.js",
21
+ "vite.config.mjs",
22
+ "vite.config.cjs"
23
+ ];
24
+ function isVpConfigEntrySpecifier(specifier) {
25
+ return specifier === "@liangmi/vp-config" || /(?:^|[\\/])src[\\/]index\.ts$/u.test(specifier) || specifier.endsWith("/src/index.ts");
26
+ }
27
+ //#endregion
28
+ //#region src/oxlint-plugin/info.ts
29
+ function getInfoPath(configDirectory) {
30
+ return join(configDirectory, "node_modules", infoDirectoryName, infoFileName);
31
+ }
32
+ function readRuntimeInfo(configDirectory) {
33
+ const infoPath = getInfoPath(configDirectory);
34
+ if (!existsSync(infoPath)) return;
35
+ try {
36
+ const info = JSON.parse(readFileSync(infoPath, "utf8"));
37
+ return isRuntimeInfo(info) ? info : void 0;
38
+ } catch {
39
+ return;
40
+ }
41
+ }
42
+ function writeRuntimeInfo(input) {
43
+ const { stack } = /* @__PURE__ */ new Error("Find vite config caller");
44
+ if (!shouldWriteRuntimeInfo(stack)) return;
45
+ const configFile = findConfigFileFromStack(stack) ?? findCwdConfigFile();
46
+ if (!configFile) return;
47
+ writeRuntimeInfoFile(configFile, input);
48
+ }
49
+ function ensureRuntimeInfo(configFile) {
50
+ const configDirectory = dirname(configFile);
51
+ if (!hasConfigPackageJson(configFile)) return;
52
+ loadRuntimeInfo(configFile);
53
+ return readRuntimeInfo(configDirectory);
54
+ }
55
+ function cleanupRuntimeInfo(configDirectory) {
56
+ const info = readRuntimeInfo(configDirectory);
57
+ const nodeModulesDirectory = join(configDirectory, "node_modules");
58
+ rmSync(getInfoPath(configDirectory), { force: true });
59
+ rmSync(join(nodeModulesDirectory, infoDirectoryName), {
60
+ recursive: true,
61
+ force: true
62
+ });
63
+ if (info?.cleanup.createdNodeModules) rmSync(nodeModulesDirectory, {
64
+ recursive: true,
65
+ force: true
66
+ });
67
+ }
68
+ function writeRuntimeInfoFile(configFile, input) {
69
+ if (!hasConfigPackageJson(configFile)) return;
70
+ const configDirectory = dirname(configFile);
71
+ const createdNodeModules = !existsSync(join(configDirectory, "node_modules"));
72
+ const infoPath = getInfoPath(configDirectory);
73
+ rmSync(infoPath, { force: true });
74
+ mkdirSync(dirname(infoPath), { recursive: true });
75
+ writeFileSync(infoPath, `${JSON.stringify(createRuntimeInfo(configFile, input, createdNodeModules), null, 2)}\n`);
76
+ }
77
+ function shouldWriteRuntimeInfo(stack) {
78
+ return hasOxlintEntryStack(stack) || hasVitePlusResolveStack(stack);
79
+ }
80
+ function hasOxlintEntryStack(stack) {
81
+ return Boolean(stack && /node_modules[\\/](?:\.pnpm[\\/]oxlint@[^\\/]+[\\/]node_modules[\\/])?oxlint[\\/]dist[\\/]/u.test(stack));
82
+ }
83
+ function hasVitePlusResolveStack(stack) {
84
+ return Boolean(stack && /node_modules[\\/]vite-plus[\\/]dist[\\/]resolve-vite-config(?:-[^\\/]+)?\.js/u.test(stack));
85
+ }
86
+ function loadRuntimeInfo(configFile) {
87
+ const input = readRuntimeConfigInput(configFile);
88
+ if (input) writeRuntimeInfoFile(configFile, input);
89
+ }
90
+ function createRuntimeInfo(configFile, input, createdNodeModules) {
91
+ const configDirectory = dirname(configFile);
92
+ const configKeys = Object.keys(input.config);
93
+ const hasPack = configKeys.includes("pack");
94
+ const hasViteConfigFields = configKeys.some(isViteConfigField);
95
+ return {
96
+ version: 1,
97
+ configFile,
98
+ configDirectory,
99
+ category: input.category,
100
+ configKeys,
101
+ project: {
102
+ hasViteConfigFields,
103
+ hasPack
104
+ },
105
+ cleanup: { createdNodeModules }
106
+ };
107
+ }
108
+ const vitePlusConfigKeys = new Set([
109
+ "create",
110
+ "fmt",
111
+ "lint",
112
+ "pack",
113
+ "run",
114
+ "staged",
115
+ "test"
116
+ ]);
117
+ function isViteConfigField(key) {
118
+ return !vitePlusConfigKeys.has(key);
119
+ }
120
+ function findConfigFileFromStack(stack) {
121
+ if (!stack) return;
122
+ for (const rawLine of stack.split("\n")) {
123
+ const path = normalizeStackPath(rawLine);
124
+ const configPath = path ? resolveBundledConfigPath(path) : void 0;
125
+ if (configPath && viteConfigNames.includes(getPathBasename(configPath))) return configPath;
126
+ }
127
+ }
128
+ function getPathBasename(path) {
129
+ return path.split(/[\\/]/u).at(-1) ?? path;
130
+ }
131
+ function normalizeStackPath(line) {
132
+ const fileUrlMatch = /file:\/\/\/.*?vite\.config\.[cm]?[jt]s/u.exec(line);
133
+ if (fileUrlMatch) return fileURLToPath(fileUrlMatch[0]);
134
+ const absolutePathMatch = /((?:[a-zA-Z]:[\\/]|\/)[^:)]+vite\.config\.[cm]?[jt]s)/u.exec(line);
135
+ return absolutePathMatch ? normalize(absolutePathMatch[1]) : void 0;
136
+ }
137
+ function resolveBundledConfigPath(path) {
138
+ const bundledConfigMatch = /^(.*)([\\/])node_modules[\\/]\.vite-temp[\\/](vite\.config\.[cm]?[jt]s)$/u.exec(path);
139
+ return bundledConfigMatch ? `${bundledConfigMatch[1]}${bundledConfigMatch[2]}${bundledConfigMatch[3]}` : path;
140
+ }
141
+ function readRuntimeConfigInput(configFile) {
142
+ try {
143
+ return parseRuntimeConfigInput(readFileSync(configFile, "utf8"));
144
+ } catch {
145
+ return;
146
+ }
147
+ }
148
+ function parseRuntimeConfigInput(source) {
149
+ const imports = getConfigImports(source);
150
+ for (const [localName, category] of imports.named) {
151
+ const input = findConfigCallInput(source, localName, category);
152
+ if (input) return input;
153
+ }
154
+ for (const namespace of imports.namespaces) for (const category of configNames) {
155
+ const input = findConfigCallInput(source, `${namespace}.${category}`, category);
156
+ if (input) return input;
157
+ }
158
+ }
159
+ function getConfigImports(source) {
160
+ const named = /* @__PURE__ */ new Map();
161
+ const namespaces = /* @__PURE__ */ new Set();
162
+ for (const match of source.matchAll(/import\s*\{(?<imports>[^}]+)\}\s*from\s*['"](?<specifier>[^'"]+)['"]/gu)) {
163
+ if (!isConfigEntrySpecifier(match.groups?.specifier)) continue;
164
+ for (const specifier of splitSimpleList(match.groups?.imports ?? "")) {
165
+ const [importedName, localName] = specifier.split(/\s+as\s+/u).map((part) => part.trim());
166
+ const category = toConfigName(importedName);
167
+ if (category) named.set(localName || importedName, category);
168
+ }
169
+ }
170
+ for (const match of source.matchAll(/import\s*\*\s*as\s*(?<localName>[$A-Z_a-z][$\w]*)\s*from\s*['"](?<specifier>[^'"]+)['"]/gu)) if (isConfigEntrySpecifier(match.groups?.specifier) && match.groups?.localName) namespaces.add(match.groups.localName);
171
+ for (const match of source.matchAll(/const\s*\{(?<imports>[^}]+)\}\s*=\s*require\(\s*['"](?<specifier>[^'"]+)['"]\s*\)/gu)) {
172
+ if (!isConfigEntrySpecifier(match.groups?.specifier)) continue;
173
+ for (const specifier of splitSimpleList(match.groups?.imports ?? "")) {
174
+ const [importedName, localName] = specifier.split(/\s*:\s*/u).map((part) => part.trim());
175
+ const category = toConfigName(importedName);
176
+ if (category) named.set(localName || importedName, category);
177
+ }
178
+ }
179
+ return {
180
+ named,
181
+ namespaces
182
+ };
183
+ }
184
+ function findConfigCallInput(source, callee, category) {
185
+ const callPattern = new RegExp(`(^|[^$\\w])${callee.split(".").map((part) => escapeRegExp(part)).join(String.raw`\s*\.\s*`)}\\s*(?:\\.\\s*(?<method>only|exclude)\\s*)?\\(`, "gu");
186
+ for (const match of source.matchAll(callPattern)) {
187
+ const argument = readCallArgument(source, match.index + match[0].lastIndexOf("("), match.groups?.method ? 1 : 0);
188
+ const configKeys = argument ? readObjectKeys(argument) : [];
189
+ if (configKeys.length > 0 || argument?.trim() === "{}") return {
190
+ category,
191
+ config: Object.fromEntries(configKeys.map((key) => [key, true]))
192
+ };
193
+ }
194
+ }
195
+ function readCallArgument(source, openParenIndex, argumentIndex) {
196
+ const closeParenIndex = findClosingDelimiter(source, openParenIndex, "(", ")");
197
+ if (closeParenIndex === void 0) return;
198
+ return splitTopLevel(source.slice(openParenIndex + 1, closeParenIndex))[argumentIndex];
199
+ }
200
+ function readObjectKeys(source) {
201
+ const objectStart = source.search(/\S/u);
202
+ if (objectStart === -1 || source[objectStart] !== "{") return [];
203
+ const objectEnd = findClosingDelimiter(source, objectStart, "{", "}");
204
+ if (objectEnd === void 0) return [];
205
+ return splitTopLevel(source.slice(objectStart + 1, objectEnd)).flatMap(readPropertyKey);
206
+ }
207
+ function readPropertyKey(source) {
208
+ const property = source.trim();
209
+ if (!property || property.startsWith("...") || property.startsWith("[")) return [];
210
+ const quotedKeyMatch = /^['"](?<key>[^'"]+)['"]\s*[:(]/u.exec(property);
211
+ if (quotedKeyMatch?.groups?.key) return [quotedKeyMatch.groups.key];
212
+ const identifierKeyMatch = /^(?:async\s+|get\s+|set\s+)?(?<key>[$A-Z_a-z][$\w]*)\s*(?::|\(|$)/u.exec(property);
213
+ return identifierKeyMatch?.groups?.key ? [identifierKeyMatch.groups.key] : [];
214
+ }
215
+ function splitTopLevel(source) {
216
+ const parts = [];
217
+ let start = 0;
218
+ let depth = 0;
219
+ for (let index = 0; index < source.length; index += 1) {
220
+ const skippedIndex = skipSyntax(source, index);
221
+ if (skippedIndex !== index) {
222
+ index = skippedIndex;
223
+ continue;
224
+ }
225
+ const char = source[index];
226
+ if (char === "(" || char === "[" || char === "{") depth += 1;
227
+ else if (char === ")" || char === "]" || char === "}") depth -= 1;
228
+ else if (char === "," && depth === 0) {
229
+ parts.push(source.slice(start, index).trim());
230
+ start = index + 1;
231
+ }
232
+ }
233
+ parts.push(source.slice(start).trim());
234
+ return parts.filter(Boolean);
235
+ }
236
+ function findClosingDelimiter(source, openIndex, openDelimiter, closeDelimiter) {
237
+ let depth = 0;
238
+ for (let index = openIndex; index < source.length; index += 1) {
239
+ const skippedIndex = skipSyntax(source, index);
240
+ if (skippedIndex !== index) {
241
+ index = skippedIndex;
242
+ continue;
243
+ }
244
+ if (source[index] === openDelimiter) depth += 1;
245
+ else if (source[index] === closeDelimiter) {
246
+ depth -= 1;
247
+ if (depth === 0) return index;
248
+ }
249
+ }
250
+ }
251
+ function skipSyntax(source, index) {
252
+ const char = source[index];
253
+ const nextChar = source[index + 1];
254
+ if (char === "\"" || char === "'" || char === "`") return skipString(source, index, char);
255
+ if (char === "/" && nextChar === "/") return skipLineComment(source, index);
256
+ if (char === "/" && nextChar === "*") return skipBlockComment(source, index);
257
+ return index;
258
+ }
259
+ function skipString(source, start, quote) {
260
+ for (let index = start + 1; index < source.length; index += 1) if (source[index] === "\\") index += 1;
261
+ else if (source[index] === quote) return index;
262
+ return source.length - 1;
263
+ }
264
+ function skipLineComment(source, start) {
265
+ const end = source.indexOf("\n", start + 2);
266
+ return end === -1 ? source.length - 1 : end;
267
+ }
268
+ function skipBlockComment(source, start) {
269
+ const end = source.indexOf("*/", start + 2);
270
+ return end === -1 ? source.length - 1 : end + 1;
271
+ }
272
+ function splitSimpleList(source) {
273
+ return source.split(",").map((part) => part.trim()).filter(Boolean);
274
+ }
275
+ function isConfigEntrySpecifier(specifier) {
276
+ return specifier ? isVpConfigEntrySpecifier(specifier) : false;
277
+ }
278
+ function toConfigName(name) {
279
+ return configNames.includes(name) ? name : void 0;
280
+ }
281
+ function escapeRegExp(value) {
282
+ return value.replaceAll(/[.*+?^${}()|[\]\\]/gu, String.raw`\$&`);
283
+ }
284
+ function findCwdConfigFile() {
285
+ for (const configName of viteConfigNames) {
286
+ const configFile = join(process.cwd(), configName);
287
+ if (existsSync(configFile)) return configFile;
288
+ }
289
+ }
290
+ function hasConfigPackageJson(configFile) {
291
+ return existsSync(join(dirname(configFile), "package.json"));
292
+ }
293
+ function isRuntimeInfo(value) {
294
+ return typeof value === "object" && value !== null && "version" in value && value.version === 1 && "project" in value && typeof value.project === "object" && value.project !== null;
295
+ }
296
+ function isWebsiteProject(info) {
297
+ return existsSync(join(info.configDirectory, "index.html")) || info.project.hasViteConfigFields;
298
+ }
299
+ function isLibProject(info) {
300
+ return info.project.hasPack;
301
+ }
302
+ function isProject(info) {
303
+ return isWebsiteProject(info) || isLibProject(info);
304
+ }
305
+ //#endregion
306
+ export { isProject as a, writeRuntimeInfo as c, projectConfigNames as d, rootConfigNames as f, isLibProject as i, packageName as l, ensureRuntimeInfo as n, isWebsiteProject as o, viteConfigNames as p, getInfoPath as r, readRuntimeInfo as s, cleanupRuntimeInfo as t, pluginName as u };
@@ -0,0 +1,34 @@
1
+ import { t as ConfigName } from "../constants-D9DskZO3.mjs";
2
+
3
+ //#region src/oxlint-plugin/project.d.ts
4
+ declare function getAllowedConfigNames(filename: string): readonly ConfigName[];
5
+ declare function isVpConfigImportAllowed(filename: string, importedNames: readonly string[]): boolean;
6
+ //#endregion
7
+ //#region src/oxlint-plugin/info.d.ts
8
+ interface VpConfigRuntimeInfo {
9
+ version: 1;
10
+ configFile: string;
11
+ configDirectory: string;
12
+ category?: ConfigName;
13
+ configKeys: string[];
14
+ project: {
15
+ hasViteConfigFields: boolean;
16
+ hasPack: boolean;
17
+ };
18
+ cleanup: {
19
+ createdNodeModules: boolean;
20
+ };
21
+ }
22
+ interface RuntimeConfigInput {
23
+ category?: ConfigName;
24
+ config: Record<string, unknown>;
25
+ }
26
+ declare function getInfoPath(configDirectory: string): string;
27
+ declare function readRuntimeInfo(configDirectory: string): VpConfigRuntimeInfo | undefined;
28
+ declare function writeRuntimeInfo(input: RuntimeConfigInput): void;
29
+ declare function cleanupRuntimeInfo(configDirectory: string): void;
30
+ //#endregion
31
+ //#region src/oxlint-plugin/index.d.ts
32
+ declare const _default: import("@oxlint/plugins").Plugin;
33
+ //#endregion
34
+ export { cleanupRuntimeInfo, _default as default, getAllowedConfigNames, getInfoPath, isVpConfigImportAllowed, readRuntimeInfo, writeRuntimeInfo };
@@ -0,0 +1,161 @@
1
+ import { a as isProject, c as writeRuntimeInfo, d as projectConfigNames, f as rootConfigNames, i as isLibProject, l as packageName, n as ensureRuntimeInfo, o as isWebsiteProject, p as viteConfigNames, r as getInfoPath, s as readRuntimeInfo, t as cleanupRuntimeInfo, u as pluginName } from "../info-CHtwxjhy.mjs";
2
+ import { existsSync } from "node:fs";
3
+ import { basename, dirname, join } from "node:path";
4
+ import { definePlugin, defineRule } from "@oxlint/plugins";
5
+ //#region src/oxlint-plugin/ast.ts
6
+ function isVpConfigSpecifier(specifier) {
7
+ return specifier === packageName;
8
+ }
9
+ function isStringLiteral(node) {
10
+ return node.type === "Literal" && typeof node.value === "string";
11
+ }
12
+ //#endregion
13
+ //#region src/oxlint-plugin/project.ts
14
+ function getAllowedConfigNames(filename) {
15
+ if (!isViteConfigFile(filename)) return [];
16
+ const info = readRuntimeInfo(dirname(filename));
17
+ return (info ? isProject(info) : isStaticWebsiteProjectDirectory(dirname(filename))) ? projectConfigNames : rootConfigNames;
18
+ }
19
+ function isVpConfigImportAllowed(filename, importedNames) {
20
+ const allowedNames = getAllowedConfigNames(filename);
21
+ const allowedNameSet = new Set(allowedNames);
22
+ return allowedNames.length > 0 && importedNames.length > 0 && importedNames.every((name) => allowedNameSet.has(name));
23
+ }
24
+ function isViteConfigFile(filename) {
25
+ return viteConfigNames.includes(basename(filename));
26
+ }
27
+ function hasPackageJson(directory) {
28
+ return existsSync(join(directory, "package.json"));
29
+ }
30
+ function isStaticWebsiteProjectDirectory(directory) {
31
+ return existsSync(join(directory, "index.html"));
32
+ }
33
+ //#endregion
34
+ //#region src/oxlint-plugin/rules.ts
35
+ function canUseRuntimeInfo(filename) {
36
+ return isViteConfigFile(filename) && hasPackageJson(dirname(filename));
37
+ }
38
+ const rules = {
39
+ "no-orphan-vite-config": defineRule({
40
+ meta: {
41
+ type: "problem",
42
+ docs: {
43
+ description: "Require vite.config files to live next to package.json.",
44
+ recommended: true
45
+ },
46
+ messages: { orphan: "Keep vite.config.ts next to package.json." }
47
+ },
48
+ create(context) {
49
+ return { Program(node) {
50
+ if (isViteConfigFile(context.filename) && !hasPackageJson(dirname(context.filename))) context.report({
51
+ node,
52
+ messageId: "orphan"
53
+ });
54
+ } };
55
+ }
56
+ }),
57
+ "no-useless-vp-preset-imports": defineRule({
58
+ meta: {
59
+ type: "problem",
60
+ docs: {
61
+ description: `Disallow importing ${packageName} outside vite.config files.`,
62
+ recommended: true
63
+ },
64
+ messages: { outsideConfig: `Import ${packageName} only from vite.config.ts.` }
65
+ },
66
+ create(context) {
67
+ const reportModuleReference = (node, specifier) => {
68
+ if (!isVpConfigSpecifier(specifier) || isViteConfigFile(context.filename)) return;
69
+ context.report({
70
+ node,
71
+ messageId: "outsideConfig"
72
+ });
73
+ };
74
+ return {
75
+ ImportDeclaration(node) {
76
+ reportModuleReference(node.source, node.source.value);
77
+ },
78
+ ExportAllDeclaration(node) {
79
+ reportModuleReference(node.source, node.source.value);
80
+ },
81
+ ExportNamedDeclaration(node) {
82
+ if (node.source) reportModuleReference(node.source, node.source.value);
83
+ },
84
+ ImportExpression(node) {
85
+ if (isStringLiteral(node.source)) reportModuleReference(node.source, node.source.value);
86
+ }
87
+ };
88
+ }
89
+ }),
90
+ "use-preset-vp-config": defineRule({
91
+ meta: {
92
+ type: "problem",
93
+ docs: {
94
+ description: "Require vite.config.ts to be loaded through @liangmi/vp-config.",
95
+ recommended: true
96
+ },
97
+ messages: { missingRuntimeInfo: `Load vite.config.ts through ${packageName} so lint rules can inspect runtime config.` }
98
+ },
99
+ create(context) {
100
+ return { Program(node) {
101
+ if (!canUseRuntimeInfo(context.filename)) return;
102
+ if (!ensureRuntimeInfo(context.filename)) context.report({
103
+ node,
104
+ messageId: "missingRuntimeInfo"
105
+ });
106
+ } };
107
+ }
108
+ }),
109
+ "load-proper-vp-config-category": defineRule({
110
+ meta: {
111
+ type: "problem",
112
+ docs: {
113
+ description: `Load ${packageName} with a category that matches the package role.`,
114
+ recommended: true
115
+ },
116
+ messages: { wrongCategory: `Use {{expected}} from ${packageName} in this vite.config.ts.` }
117
+ },
118
+ create(context) {
119
+ return { Program(node) {
120
+ if (!canUseRuntimeInfo(context.filename)) return;
121
+ const info = ensureRuntimeInfo(context.filename);
122
+ if (!info?.category) return;
123
+ const allowed = isProject(info) ? projectConfigNames : rootConfigNames;
124
+ if (allowed.includes(info.category)) return;
125
+ context.report({
126
+ node,
127
+ messageId: "wrongCategory",
128
+ data: { expected: allowed.map((name) => `{ ${name} }`).join(" or ") }
129
+ });
130
+ } };
131
+ }
132
+ }),
133
+ "no-mixed-project": defineRule({
134
+ meta: {
135
+ type: "problem",
136
+ docs: {
137
+ description: "Disallow a vite config that is both website and library project.",
138
+ recommended: true
139
+ },
140
+ messages: { mixed: "Do not mix website and library project signals in one vite.config.ts." }
141
+ },
142
+ create(context) {
143
+ return { Program(node) {
144
+ if (!canUseRuntimeInfo(context.filename)) return;
145
+ const info = ensureRuntimeInfo(context.filename);
146
+ if (info && isWebsiteProject(info) && isLibProject(info)) context.report({
147
+ node,
148
+ messageId: "mixed"
149
+ });
150
+ } };
151
+ }
152
+ })
153
+ };
154
+ //#endregion
155
+ //#region src/oxlint-plugin/index.ts
156
+ var oxlint_plugin_default = definePlugin({
157
+ meta: { name: pluginName },
158
+ rules
159
+ });
160
+ //#endregion
161
+ export { cleanupRuntimeInfo, oxlint_plugin_default as default, getAllowedConfigNames, getInfoPath, isVpConfigImportAllowed, readRuntimeInfo, writeRuntimeInfo };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@liangmi/vp-config",
3
- "version": "0.0.0-alpha.0",
3
+ "version": "0.1.0",
4
4
  "description": "Liang's JavaScript development toolchain config with Vite+.",
5
5
  "homepage": "https://github.com/liangmiQwQ/vp-config#readme",
6
6
  "bugs": {
@@ -18,22 +18,28 @@
18
18
  "type": "module",
19
19
  "exports": {
20
20
  ".": "./dist/index.mjs",
21
+ "./oxlint-plugin": "./dist/oxlint-plugin/index.mjs",
21
22
  "./package.json": "./package.json"
22
23
  },
23
24
  "publishConfig": {
24
25
  "access": "public"
25
26
  },
26
27
  "devDependencies": {
27
- "@types/node": "^25.6.2",
28
- "@typescript/native-preview": "7.0.0-dev.20260509.2",
28
+ "@types/node": "^26.0.0",
29
+ "@typescript/native-preview": "7.0.0-dev.20260620.1",
29
30
  "bumpp": "^11.1.0",
30
31
  "typescript": "^6.0.3",
31
- "vite-plus": "latest"
32
+ "vite-plus": "^0.2.1"
33
+ },
34
+ "peerDependencies": {
35
+ "@oxlint/plugins": "1.61.0",
36
+ "vite-plus": ">=0.2.0"
32
37
  },
33
38
  "scripts": {
34
- "build": "vp pack",
39
+ "build": "vp run cpack",
40
+ "check": "vp run cpack && vp run ccheck",
35
41
  "dev": "vp pack --watch",
36
- "test": "vp test",
37
- "check": "vp check"
42
+ "release": "bumpp",
43
+ "test": "vp run ctest"
38
44
  }
39
45
  }