@liangmi/vp-config 0.0.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 +107 -8
- package/dist/constants-D9DskZO3.d.mts +5 -0
- package/dist/index.mjs +106 -21
- package/dist/info-CHtwxjhy.mjs +306 -0
- package/dist/oxlint-plugin/index.d.mts +34 -0
- package/dist/oxlint-plugin/index.mjs +161 -0
- package/package.json +10 -10
package/README.md
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# @liangmi/vp-config
|
|
2
2
|
|
|
3
|
-
Liang's
|
|
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 {
|
|
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
|
|
21
|
-
|
|
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
|
-
|
|
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
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { c as writeRuntimeInfo } from "./info-CHtwxjhy.mjs";
|
|
1
2
|
import { defineConfig, mergeConfig } from "vite-plus";
|
|
2
3
|
//#region src/base/fmt.ts
|
|
3
4
|
const fmtBase = {
|
|
@@ -53,7 +54,10 @@ const componentOverride = {
|
|
|
53
54
|
};
|
|
54
55
|
//#endregion
|
|
55
56
|
//#region src/base/lint.ts
|
|
56
|
-
const suspicious = {
|
|
57
|
+
const suspicious = {
|
|
58
|
+
"no-shadow": "off",
|
|
59
|
+
"typescript/no-unsafe-type-assertion": "off"
|
|
60
|
+
};
|
|
57
61
|
const nursery = {
|
|
58
62
|
"no-undef": "off",
|
|
59
63
|
"import/named": "off",
|
|
@@ -65,6 +69,7 @@ const style = {
|
|
|
65
69
|
"typescript/consistent-indexed-object-style": ["warn", "record"],
|
|
66
70
|
"eslint/func-names": ["warn", "never"],
|
|
67
71
|
"id-length": ["warn", { checkGeneric: false }],
|
|
72
|
+
"typescript/method-signature-style": ["warn", "property"],
|
|
68
73
|
"unicorn/prefer-ternary": ["warn", "only-single-line"],
|
|
69
74
|
"import/exports-last": "off",
|
|
70
75
|
"func-style": "off",
|
|
@@ -86,7 +91,8 @@ const style = {
|
|
|
86
91
|
"import/no-nodejs-modules": "off",
|
|
87
92
|
"import/prefer-default-export": "off",
|
|
88
93
|
"prefer-await-to-then": "off",
|
|
89
|
-
"unicorn/no-null": "off"
|
|
94
|
+
"unicorn/no-null": "off",
|
|
95
|
+
"eslint/prefer-named-capture-group": "off"
|
|
90
96
|
};
|
|
91
97
|
const restriction = {
|
|
92
98
|
"oxc/bad-bitwise-operator": "error",
|
|
@@ -211,6 +217,10 @@ const lintBase = {
|
|
|
211
217
|
restriction: "off",
|
|
212
218
|
pedantic: "off"
|
|
213
219
|
},
|
|
220
|
+
jsPlugins: [{
|
|
221
|
+
name: "liangmi",
|
|
222
|
+
specifier: "@liangmi/vp-config/oxlint-plugin"
|
|
223
|
+
}],
|
|
214
224
|
plugins: [
|
|
215
225
|
"eslint",
|
|
216
226
|
"oxc",
|
|
@@ -225,7 +235,11 @@ const lintBase = {
|
|
|
225
235
|
...restriction,
|
|
226
236
|
...pedantic,
|
|
227
237
|
...style,
|
|
228
|
-
"
|
|
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"
|
|
229
243
|
},
|
|
230
244
|
options: {
|
|
231
245
|
typeAware: true,
|
|
@@ -240,6 +254,24 @@ const lintBase = {
|
|
|
240
254
|
node: true,
|
|
241
255
|
vitest: true
|
|
242
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
|
+
},
|
|
243
275
|
files: ["*.test.ts", "*.spec.ts"]
|
|
244
276
|
},
|
|
245
277
|
{
|
|
@@ -261,42 +293,79 @@ const lintBase = {
|
|
|
261
293
|
]
|
|
262
294
|
};
|
|
263
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
|
|
264
323
|
//#region src/base/index.ts
|
|
265
324
|
const baseConfig = {
|
|
266
325
|
fmt: fmtBase,
|
|
267
|
-
lint: lintBase
|
|
326
|
+
lint: lintBase,
|
|
327
|
+
run: runBase,
|
|
328
|
+
staged: stagedBase
|
|
268
329
|
};
|
|
269
330
|
//#endregion
|
|
270
331
|
//#region src/website/fmt.ts
|
|
271
332
|
const fmtWebsite = {
|
|
333
|
+
...fmtBase,
|
|
272
334
|
jsxSingleQuote: true,
|
|
273
335
|
embeddedLanguageFormatting: "auto",
|
|
274
|
-
sortTailwindcss: false
|
|
275
|
-
...fmtBase
|
|
336
|
+
sortTailwindcss: false
|
|
276
337
|
};
|
|
277
338
|
//#endregion
|
|
278
339
|
//#region src/cli/index.ts
|
|
279
340
|
const cliConfig = {
|
|
280
341
|
fmt: fmtWebsite,
|
|
281
|
-
lint: mergeConfig(lintBase, mergeConfig(
|
|
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
|
|
282
351
|
};
|
|
283
352
|
//#endregion
|
|
284
353
|
//#region src/entry.ts
|
|
285
|
-
function createConfigEntry(presetConfig) {
|
|
286
|
-
const entry = ((config) => defineMergedConfig(presetConfig, config));
|
|
287
|
-
const only = (parts, ...args) => defineMergedConfig(pickPresetConfig(presetConfig, parts),
|
|
288
|
-
const exclude = (parts, ...args) => defineMergedConfig(omitPresetConfig(presetConfig, parts),
|
|
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);
|
|
289
358
|
return Object.assign(entry, {
|
|
290
359
|
only,
|
|
291
360
|
exclude
|
|
292
361
|
});
|
|
293
362
|
}
|
|
294
|
-
function defineMergedConfig(presetConfig, config) {
|
|
363
|
+
function defineMergedConfig(presetConfig, config, category) {
|
|
295
364
|
if (typeof config === "function") return defineConfig(async (env) => {
|
|
296
|
-
return mergePresetConfig(presetConfig, await config(env));
|
|
365
|
+
return trackRuntimeInfo(mergePresetConfig(presetConfig, await config(env)), category);
|
|
297
366
|
});
|
|
298
|
-
if (config instanceof Promise) return defineConfig(config.then((userConfig) => mergePresetConfig(presetConfig, userConfig)));
|
|
299
|
-
return defineConfig(mergePresetConfig(presetConfig, config));
|
|
367
|
+
if (config instanceof Promise) return defineConfig(config.then((userConfig) => trackRuntimeInfo(mergePresetConfig(presetConfig, userConfig), category)));
|
|
368
|
+
return defineConfig(trackRuntimeInfo(mergePresetConfig(presetConfig, config), category));
|
|
300
369
|
}
|
|
301
370
|
function pickPresetConfig(presetConfig, parts) {
|
|
302
371
|
return Object.fromEntries(parts.map((part) => [part, presetConfig[part]]));
|
|
@@ -314,6 +383,13 @@ function mergePresetConfig(presetConfig, userConfig) {
|
|
|
314
383
|
if (presetConfig.staged) config.staged = mergeStagedConfig(presetConfig.staged, userConfig.staged);
|
|
315
384
|
return config;
|
|
316
385
|
}
|
|
386
|
+
function trackRuntimeInfo(config, category) {
|
|
387
|
+
writeRuntimeInfo({
|
|
388
|
+
category,
|
|
389
|
+
config
|
|
390
|
+
});
|
|
391
|
+
return config;
|
|
392
|
+
}
|
|
317
393
|
function mergeLintConfig(presetLint, userLint) {
|
|
318
394
|
return mergeConfig(presetLint, userLint ?? {});
|
|
319
395
|
}
|
|
@@ -333,7 +409,14 @@ function isStagedObjectConfig(config) {
|
|
|
333
409
|
//#region src/lib/index.ts
|
|
334
410
|
const libConfig = {
|
|
335
411
|
fmt: fmtBase,
|
|
336
|
-
lint: mergeConfig(lintBase, {})
|
|
412
|
+
lint: mergeConfig(lintBase, {}),
|
|
413
|
+
pack: {
|
|
414
|
+
fixedExtension: true,
|
|
415
|
+
exports: true,
|
|
416
|
+
dts: { tsgo: true }
|
|
417
|
+
},
|
|
418
|
+
run: runBase,
|
|
419
|
+
staged: stagedBase
|
|
337
420
|
};
|
|
338
421
|
//#endregion
|
|
339
422
|
//#region src/website/index.ts
|
|
@@ -349,13 +432,15 @@ const websiteConfig = {
|
|
|
349
432
|
"prefer-blob-reading-methods": "warn",
|
|
350
433
|
"import/no-unassigned-import": ["error", { allow: ["**/*.css"] }]
|
|
351
434
|
}
|
|
352
|
-
})
|
|
435
|
+
}),
|
|
436
|
+
run: runBase,
|
|
437
|
+
staged: stagedBase
|
|
353
438
|
};
|
|
354
439
|
//#endregion
|
|
355
440
|
//#region src/index.ts
|
|
356
|
-
const base = createConfigEntry(baseConfig);
|
|
357
|
-
const cli = createConfigEntry(cliConfig);
|
|
358
|
-
const lib = createConfigEntry(libConfig);
|
|
359
|
-
const website = createConfigEntry(websiteConfig);
|
|
441
|
+
const base = createConfigEntry(baseConfig, "base");
|
|
442
|
+
const cli = createConfigEntry(cliConfig, "cli");
|
|
443
|
+
const lib = createConfigEntry(libConfig, "lib");
|
|
444
|
+
const website = createConfigEntry(websiteConfig, "website");
|
|
360
445
|
//#endregion
|
|
361
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.
|
|
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,28 +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": "^
|
|
28
|
-
"@typescript/native-preview": "7.0.0-dev.
|
|
28
|
+
"@types/node": "^26.0.0",
|
|
29
|
+
"@typescript/native-preview": "7.0.0-dev.20260620.1",
|
|
29
30
|
"bumpp": "^11.1.0",
|
|
30
|
-
"tinyglobby": "^0.2.17",
|
|
31
31
|
"typescript": "^6.0.3",
|
|
32
|
-
"vite-plus": "^0.1
|
|
33
|
-
"yaml": "^2.9.0"
|
|
32
|
+
"vite-plus": "^0.2.1"
|
|
34
33
|
},
|
|
35
34
|
"peerDependencies": {
|
|
36
|
-
"
|
|
35
|
+
"@oxlint/plugins": "1.61.0",
|
|
36
|
+
"vite-plus": ">=0.2.0"
|
|
37
37
|
},
|
|
38
38
|
"scripts": {
|
|
39
|
-
"build": "vp
|
|
40
|
-
"check": "vp
|
|
39
|
+
"build": "vp run cpack",
|
|
40
|
+
"check": "vp run cpack && vp run ccheck",
|
|
41
41
|
"dev": "vp pack --watch",
|
|
42
42
|
"release": "bumpp",
|
|
43
|
-
"test": "vp
|
|
43
|
+
"test": "vp run ctest"
|
|
44
44
|
}
|
|
45
45
|
}
|