@liangmi/vp-config 0.0.0 → 0.1.1
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 +108 -22
- 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",
|
|
@@ -64,7 +68,7 @@ const style = {
|
|
|
64
68
|
"typescript/consistent-type-definitions": ["warn", "interface"],
|
|
65
69
|
"typescript/consistent-indexed-object-style": ["warn", "record"],
|
|
66
70
|
"eslint/func-names": ["warn", "never"],
|
|
67
|
-
"
|
|
71
|
+
"typescript/method-signature-style": ["warn", "property"],
|
|
68
72
|
"unicorn/prefer-ternary": ["warn", "only-single-line"],
|
|
69
73
|
"import/exports-last": "off",
|
|
70
74
|
"func-style": "off",
|
|
@@ -86,7 +90,10 @@ const style = {
|
|
|
86
90
|
"import/no-nodejs-modules": "off",
|
|
87
91
|
"import/prefer-default-export": "off",
|
|
88
92
|
"prefer-await-to-then": "off",
|
|
89
|
-
"unicorn/no-null": "off"
|
|
93
|
+
"unicorn/no-null": "off",
|
|
94
|
+
"prefer-named-capture-group": "off",
|
|
95
|
+
"id-length": "off",
|
|
96
|
+
"promise/avoid-new": "off"
|
|
90
97
|
};
|
|
91
98
|
const restriction = {
|
|
92
99
|
"oxc/bad-bitwise-operator": "error",
|
|
@@ -211,6 +218,10 @@ const lintBase = {
|
|
|
211
218
|
restriction: "off",
|
|
212
219
|
pedantic: "off"
|
|
213
220
|
},
|
|
221
|
+
jsPlugins: [{
|
|
222
|
+
name: "liangmi",
|
|
223
|
+
specifier: "@liangmi/vp-config/oxlint-plugin"
|
|
224
|
+
}],
|
|
214
225
|
plugins: [
|
|
215
226
|
"eslint",
|
|
216
227
|
"oxc",
|
|
@@ -225,7 +236,11 @@ const lintBase = {
|
|
|
225
236
|
...restriction,
|
|
226
237
|
...pedantic,
|
|
227
238
|
...style,
|
|
228
|
-
"
|
|
239
|
+
"liangmi/no-orphan-vite-config": "error",
|
|
240
|
+
"liangmi/no-useless-vp-preset-imports": "error",
|
|
241
|
+
"liangmi/use-preset-vp-config": "error",
|
|
242
|
+
"liangmi/load-proper-vp-config-category": "error",
|
|
243
|
+
"liangmi/no-mixed-project": "error"
|
|
229
244
|
},
|
|
230
245
|
options: {
|
|
231
246
|
typeAware: true,
|
|
@@ -240,6 +255,24 @@ const lintBase = {
|
|
|
240
255
|
node: true,
|
|
241
256
|
vitest: true
|
|
242
257
|
},
|
|
258
|
+
plugins: ["vitest"],
|
|
259
|
+
rules: {
|
|
260
|
+
"vitest/consistent-test-filename": ["warn", { pattern: ".*.test.ts$" }],
|
|
261
|
+
"vitest/consistent-test-it": ["warn", {
|
|
262
|
+
fn: "it",
|
|
263
|
+
withinDescribe: "it"
|
|
264
|
+
}],
|
|
265
|
+
"vitest/no-hooks": "off",
|
|
266
|
+
"vitest/require-top-level-describe": "off",
|
|
267
|
+
"vitest/prefer-strict-boolean-matchers": "off",
|
|
268
|
+
"vitest/max-expects": "off",
|
|
269
|
+
"vitest/prefer-expect-assertions": "off",
|
|
270
|
+
"vitest/prefer-importing-vitest-globals": "off",
|
|
271
|
+
"vitest/no-importing-vitest-globals": "off",
|
|
272
|
+
"vitest/no-large-snapshots": "off",
|
|
273
|
+
"vitest/no-restricted-matchers": "off",
|
|
274
|
+
"vitest/no-restricted-vi-methods": "off"
|
|
275
|
+
},
|
|
243
276
|
files: ["*.test.ts", "*.spec.ts"]
|
|
244
277
|
},
|
|
245
278
|
{
|
|
@@ -261,42 +294,79 @@ const lintBase = {
|
|
|
261
294
|
]
|
|
262
295
|
};
|
|
263
296
|
//#endregion
|
|
297
|
+
//#region src/base/run.ts
|
|
298
|
+
const lintInput = ["!node_modules/.vp-config/info.json", "index.html"];
|
|
299
|
+
const runBase = {
|
|
300
|
+
tasks: {
|
|
301
|
+
cbuild: "vp build",
|
|
302
|
+
ccheck: {
|
|
303
|
+
command: "vp check",
|
|
304
|
+
input: lintInput
|
|
305
|
+
},
|
|
306
|
+
cfmt: "vp fmt",
|
|
307
|
+
cformat: "vp format",
|
|
308
|
+
clint: {
|
|
309
|
+
command: "vp lint",
|
|
310
|
+
input: lintInput
|
|
311
|
+
},
|
|
312
|
+
cpack: "vp pack",
|
|
313
|
+
ctest: "vp test"
|
|
314
|
+
},
|
|
315
|
+
cache: {
|
|
316
|
+
scripts: false,
|
|
317
|
+
tasks: true
|
|
318
|
+
}
|
|
319
|
+
};
|
|
320
|
+
//#endregion
|
|
321
|
+
//#region src/base/staged.ts
|
|
322
|
+
const stagedBase = { "*": "vp check --fix" };
|
|
323
|
+
//#endregion
|
|
264
324
|
//#region src/base/index.ts
|
|
265
325
|
const baseConfig = {
|
|
266
326
|
fmt: fmtBase,
|
|
267
|
-
lint: lintBase
|
|
327
|
+
lint: lintBase,
|
|
328
|
+
run: runBase,
|
|
329
|
+
staged: stagedBase
|
|
268
330
|
};
|
|
269
331
|
//#endregion
|
|
270
332
|
//#region src/website/fmt.ts
|
|
271
333
|
const fmtWebsite = {
|
|
334
|
+
...fmtBase,
|
|
272
335
|
jsxSingleQuote: true,
|
|
273
336
|
embeddedLanguageFormatting: "auto",
|
|
274
|
-
sortTailwindcss: false
|
|
275
|
-
...fmtBase
|
|
337
|
+
sortTailwindcss: false
|
|
276
338
|
};
|
|
277
339
|
//#endregion
|
|
278
340
|
//#region src/cli/index.ts
|
|
279
341
|
const cliConfig = {
|
|
280
342
|
fmt: fmtWebsite,
|
|
281
|
-
lint: mergeConfig(lintBase, mergeConfig(
|
|
343
|
+
lint: mergeConfig(lintBase, mergeConfig(cliOverride, componentOverride)),
|
|
344
|
+
pack: {
|
|
345
|
+
dts: false,
|
|
346
|
+
minify: true,
|
|
347
|
+
platform: "node",
|
|
348
|
+
nodeProtocol: "strip"
|
|
349
|
+
},
|
|
350
|
+
run: runBase,
|
|
351
|
+
staged: stagedBase
|
|
282
352
|
};
|
|
283
353
|
//#endregion
|
|
284
354
|
//#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),
|
|
355
|
+
function createConfigEntry(presetConfig, category) {
|
|
356
|
+
const entry = ((config) => defineMergedConfig(presetConfig, config, category));
|
|
357
|
+
const only = (parts, ...args) => defineMergedConfig(pickPresetConfig(presetConfig, parts), args[0], category);
|
|
358
|
+
const exclude = (parts, ...args) => defineMergedConfig(omitPresetConfig(presetConfig, parts), args[0], category);
|
|
289
359
|
return Object.assign(entry, {
|
|
290
360
|
only,
|
|
291
361
|
exclude
|
|
292
362
|
});
|
|
293
363
|
}
|
|
294
|
-
function defineMergedConfig(presetConfig, config) {
|
|
364
|
+
function defineMergedConfig(presetConfig, config, category) {
|
|
295
365
|
if (typeof config === "function") return defineConfig(async (env) => {
|
|
296
|
-
return mergePresetConfig(presetConfig, await config(env));
|
|
366
|
+
return trackRuntimeInfo(mergePresetConfig(presetConfig, await config(env)), category);
|
|
297
367
|
});
|
|
298
|
-
if (config instanceof Promise) return defineConfig(config.then((userConfig) => mergePresetConfig(presetConfig, userConfig)));
|
|
299
|
-
return defineConfig(mergePresetConfig(presetConfig, config));
|
|
368
|
+
if (config instanceof Promise) return defineConfig(config.then((userConfig) => trackRuntimeInfo(mergePresetConfig(presetConfig, userConfig), category)));
|
|
369
|
+
return defineConfig(trackRuntimeInfo(mergePresetConfig(presetConfig, config), category));
|
|
300
370
|
}
|
|
301
371
|
function pickPresetConfig(presetConfig, parts) {
|
|
302
372
|
return Object.fromEntries(parts.map((part) => [part, presetConfig[part]]));
|
|
@@ -314,6 +384,13 @@ function mergePresetConfig(presetConfig, userConfig) {
|
|
|
314
384
|
if (presetConfig.staged) config.staged = mergeStagedConfig(presetConfig.staged, userConfig.staged);
|
|
315
385
|
return config;
|
|
316
386
|
}
|
|
387
|
+
function trackRuntimeInfo(config, category) {
|
|
388
|
+
writeRuntimeInfo({
|
|
389
|
+
category,
|
|
390
|
+
config
|
|
391
|
+
});
|
|
392
|
+
return config;
|
|
393
|
+
}
|
|
317
394
|
function mergeLintConfig(presetLint, userLint) {
|
|
318
395
|
return mergeConfig(presetLint, userLint ?? {});
|
|
319
396
|
}
|
|
@@ -333,7 +410,14 @@ function isStagedObjectConfig(config) {
|
|
|
333
410
|
//#region src/lib/index.ts
|
|
334
411
|
const libConfig = {
|
|
335
412
|
fmt: fmtBase,
|
|
336
|
-
lint: mergeConfig(lintBase, {})
|
|
413
|
+
lint: mergeConfig(lintBase, {}),
|
|
414
|
+
pack: {
|
|
415
|
+
fixedExtension: true,
|
|
416
|
+
exports: true,
|
|
417
|
+
dts: { tsgo: true }
|
|
418
|
+
},
|
|
419
|
+
run: runBase,
|
|
420
|
+
staged: stagedBase
|
|
337
421
|
};
|
|
338
422
|
//#endregion
|
|
339
423
|
//#region src/website/index.ts
|
|
@@ -349,13 +433,15 @@ const websiteConfig = {
|
|
|
349
433
|
"prefer-blob-reading-methods": "warn",
|
|
350
434
|
"import/no-unassigned-import": ["error", { allow: ["**/*.css"] }]
|
|
351
435
|
}
|
|
352
|
-
})
|
|
436
|
+
}),
|
|
437
|
+
run: runBase,
|
|
438
|
+
staged: stagedBase
|
|
353
439
|
};
|
|
354
440
|
//#endregion
|
|
355
441
|
//#region src/index.ts
|
|
356
|
-
const base = createConfigEntry(baseConfig);
|
|
357
|
-
const cli = createConfigEntry(cliConfig);
|
|
358
|
-
const lib = createConfigEntry(libConfig);
|
|
359
|
-
const website = createConfigEntry(websiteConfig);
|
|
442
|
+
const base = createConfigEntry(baseConfig, "base");
|
|
443
|
+
const cli = createConfigEntry(cliConfig, "cli");
|
|
444
|
+
const lib = createConfigEntry(libConfig, "lib");
|
|
445
|
+
const website = createConfigEntry(websiteConfig, "website");
|
|
360
446
|
//#endregion
|
|
361
447
|
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.1",
|
|
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
|
}
|