@vizejs/nuxt-lint-config 0.315.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 +26 -0
- package/dist/index.d.mts +227 -0
- package/dist/index.mjs +281 -0
- package/package.json +58 -0
package/README.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# `@vizejs/nuxt-lint-config`
|
|
2
|
+
|
|
3
|
+
The engine-neutral, shareable Nuxt lint preset core used by `@vizejs/nuxt`.
|
|
4
|
+
It ports the project-aware directory, feature, and rule-block decisions from
|
|
5
|
+
`@nuxt/eslint-config` without requiring Nuxt or ESLint at runtime.
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import {
|
|
9
|
+
buildNuxtLintPlan,
|
|
10
|
+
resolveNuxtLintDirs,
|
|
11
|
+
resolveNuxtLintFeatures,
|
|
12
|
+
} from "@vizejs/nuxt-lint-config";
|
|
13
|
+
|
|
14
|
+
const dirs = resolveNuxtLintDirs({ src: ["src"] });
|
|
15
|
+
const features = resolveNuxtLintFeatures({ stylistic: true }, () => true);
|
|
16
|
+
const plan = buildNuxtLintPlan(features, dirs);
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The result is an ordered, engine-neutral plan using eslint-compatible rule
|
|
20
|
+
identifiers. `@vizejs/nuxt` consumes the same exports and emits them for
|
|
21
|
+
Vize's oxlint integration, so the module and standalone entry point cannot
|
|
22
|
+
drift into separate implementations.
|
|
23
|
+
|
|
24
|
+
Compatibility is pinned to the real `@nuxt/eslint` and
|
|
25
|
+
`@nuxt/eslint-config` packages by the committed differential oracle under
|
|
26
|
+
`test/nuxt-eslint-compat`.
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
//#region src/dirs.d.ts
|
|
2
|
+
/** A component directory declaration on a Nuxt layer. */
|
|
3
|
+
type NuxtComponentDirDeclaration = string | {
|
|
4
|
+
path?: string;
|
|
5
|
+
prefix?: string;
|
|
6
|
+
[key: string]: unknown;
|
|
7
|
+
};
|
|
8
|
+
/** The subset of a Nuxt layer's config that lint directories are derived from. */
|
|
9
|
+
interface NuxtLintLayer {
|
|
10
|
+
srcDir: string;
|
|
11
|
+
imports?: {
|
|
12
|
+
dirs?: Array<string | undefined | null>;
|
|
13
|
+
};
|
|
14
|
+
components?: boolean | NuxtComponentDirDeclaration[] | {
|
|
15
|
+
dirs?: NuxtComponentDirDeclaration[];
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/** The `nuxt.options.dir` overrides that lint directories honour. */
|
|
19
|
+
interface NuxtLintDirNames {
|
|
20
|
+
pages?: string;
|
|
21
|
+
layouts?: string;
|
|
22
|
+
plugins?: string;
|
|
23
|
+
middleware?: string;
|
|
24
|
+
modules?: string;
|
|
25
|
+
}
|
|
26
|
+
/** The Nuxt project state lint directories are derived from. */
|
|
27
|
+
interface NuxtLintProjectState {
|
|
28
|
+
/** Directory the emitted globs are relative to. */
|
|
29
|
+
rootDir: string;
|
|
30
|
+
/** `nuxt.options.dir`. Missing entries fall back to their Nuxt defaults. */
|
|
31
|
+
dir?: NuxtLintDirNames;
|
|
32
|
+
/** `nuxt.options._layers`, in Nuxt's own order. */
|
|
33
|
+
layers: NuxtLintLayer[];
|
|
34
|
+
}
|
|
35
|
+
/** Every directory list the generated lint config globs over. */
|
|
36
|
+
interface NuxtLintDirs {
|
|
37
|
+
pages: string[];
|
|
38
|
+
composables: string[];
|
|
39
|
+
components: string[];
|
|
40
|
+
componentsPrefixed: string[];
|
|
41
|
+
layouts: string[];
|
|
42
|
+
plugins: string[];
|
|
43
|
+
middleware: string[];
|
|
44
|
+
modules: string[];
|
|
45
|
+
servers: string[];
|
|
46
|
+
root: string[];
|
|
47
|
+
src: string[];
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The key order of {@link NuxtLintDirs}.
|
|
51
|
+
*
|
|
52
|
+
* Upstream serialises `dirs` with `Object.entries`, so this order is observable
|
|
53
|
+
* in the generated config file and the oracle asserts it.
|
|
54
|
+
*/
|
|
55
|
+
declare const NUXT_LINT_DIR_KEYS: readonly ["pages", "composables", "components", "componentsPrefixed", "layouts", "plugins", "middleware", "modules", "servers", "root", "src"];
|
|
56
|
+
/**
|
|
57
|
+
* Derive every lint directory from the Nuxt project state.
|
|
58
|
+
*
|
|
59
|
+
* `servers` and `root` are intentionally left empty: upstream's `getDirs` never
|
|
60
|
+
* populates them, and because an empty array is truthy the `||=` defaults in
|
|
61
|
+
* `resolveNuxtLintDirs` do not fire for a module-generated config either. The
|
|
62
|
+
* oracle pins that, so a future upstream change surfaces as a failure rather
|
|
63
|
+
* than as a silently different set of linted files.
|
|
64
|
+
*/
|
|
65
|
+
declare function collectNuxtLintDirs(state: NuxtLintProjectState): NuxtLintDirs;
|
|
66
|
+
/**
|
|
67
|
+
* Apply the directory defaults used when a config is written by hand rather
|
|
68
|
+
* than generated from a Nuxt instance.
|
|
69
|
+
*
|
|
70
|
+
* Upstream uses `||=`, so a directory list that is present but empty keeps its
|
|
71
|
+
* empty value. This port reproduces that, because generated configs rely on it.
|
|
72
|
+
*/
|
|
73
|
+
declare function resolveNuxtLintDirs(dirs: Partial<NuxtLintDirs> | undefined): NuxtLintDirs;
|
|
74
|
+
//#endregion
|
|
75
|
+
//#region src/features.d.ts
|
|
76
|
+
/**
|
|
77
|
+
* Feature-flag resolution for the shareable Nuxt lint config.
|
|
78
|
+
*
|
|
79
|
+
* This is a behavioural port of `resolveOptions().features` in
|
|
80
|
+
* `@nuxt/eslint-config` (see `test/nuxt-eslint-compat/`). The defaults are
|
|
81
|
+
* load-bearing: they decide which config blocks the generator emits, so they
|
|
82
|
+
* are pinned against the real package by the differential oracle rather than
|
|
83
|
+
* restated from documentation.
|
|
84
|
+
*/
|
|
85
|
+
/** Options for the Nuxt-specific rule block. */
|
|
86
|
+
interface NuxtLintNuxtOptions {
|
|
87
|
+
/**
|
|
88
|
+
* Enforce a recommended key order in `nuxt.config`.
|
|
89
|
+
*
|
|
90
|
+
* @default true when `stylistic` is enabled
|
|
91
|
+
*/
|
|
92
|
+
sortConfigKeys?: boolean;
|
|
93
|
+
}
|
|
94
|
+
/** Options for the tooling (module/library author) rule blocks. */
|
|
95
|
+
interface NuxtLintToolingOptions {
|
|
96
|
+
/** @default true */
|
|
97
|
+
regexp?: boolean;
|
|
98
|
+
/** @default true */
|
|
99
|
+
unicorn?: boolean;
|
|
100
|
+
/** @default true */
|
|
101
|
+
jsdoc?: boolean;
|
|
102
|
+
}
|
|
103
|
+
/** Options for the import rule block. */
|
|
104
|
+
interface NuxtLintImportOptions {
|
|
105
|
+
/** @default "eslint-plugin-import-x" */
|
|
106
|
+
package?: "eslint-plugin-import-lite" | "eslint-plugin-import-x";
|
|
107
|
+
}
|
|
108
|
+
/** Options for the TypeScript rule block. */
|
|
109
|
+
interface NuxtLintTypeScriptOptions {
|
|
110
|
+
/** @default true */
|
|
111
|
+
strict?: boolean;
|
|
112
|
+
/** Enables type-aware rules when set. */
|
|
113
|
+
tsconfigPath?: string;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* The `features` surface of the generated lint config.
|
|
117
|
+
*
|
|
118
|
+
* Mirrors `NuxtESLintFeaturesOptions` from `@nuxt/eslint-config`.
|
|
119
|
+
*/
|
|
120
|
+
interface NuxtLintFeatures {
|
|
121
|
+
/**
|
|
122
|
+
* Set up the baseline JavaScript, TypeScript and Vue rule blocks.
|
|
123
|
+
*
|
|
124
|
+
* @default true
|
|
125
|
+
*/
|
|
126
|
+
standalone?: boolean;
|
|
127
|
+
/**
|
|
128
|
+
* Enable rules aimed at Nuxt module and library authors.
|
|
129
|
+
*
|
|
130
|
+
* @default false
|
|
131
|
+
*/
|
|
132
|
+
tooling?: boolean | NuxtLintToolingOptions;
|
|
133
|
+
/**
|
|
134
|
+
* Enable the import rule block.
|
|
135
|
+
*
|
|
136
|
+
* @default true
|
|
137
|
+
*/
|
|
138
|
+
import?: boolean | NuxtLintImportOptions;
|
|
139
|
+
/**
|
|
140
|
+
* Enable stylistic (formatting) rules.
|
|
141
|
+
*
|
|
142
|
+
* @default false
|
|
143
|
+
*/
|
|
144
|
+
stylistic?: boolean | Record<string, unknown>;
|
|
145
|
+
/**
|
|
146
|
+
* Enable formatter delegation for non-script file types.
|
|
147
|
+
*
|
|
148
|
+
* @default false
|
|
149
|
+
*/
|
|
150
|
+
formatters?: boolean | Record<string, unknown>;
|
|
151
|
+
/** Options for the Nuxt-specific rule block. */
|
|
152
|
+
nuxt?: NuxtLintNuxtOptions;
|
|
153
|
+
/**
|
|
154
|
+
* Enable TypeScript support.
|
|
155
|
+
*
|
|
156
|
+
* Defaults to whether `typescript` is resolvable from the project.
|
|
157
|
+
*/
|
|
158
|
+
typescript?: boolean | NuxtLintTypeScriptOptions;
|
|
159
|
+
}
|
|
160
|
+
/** Every feature flag with its default applied. */
|
|
161
|
+
interface ResolvedNuxtLintFeatures {
|
|
162
|
+
standalone: boolean;
|
|
163
|
+
stylistic: boolean | Record<string, unknown>;
|
|
164
|
+
typescript: boolean | NuxtLintTypeScriptOptions;
|
|
165
|
+
tooling: boolean | NuxtLintToolingOptions;
|
|
166
|
+
formatters: boolean | Record<string, unknown>;
|
|
167
|
+
nuxt: NuxtLintNuxtOptions;
|
|
168
|
+
import: boolean | NuxtLintImportOptions;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Whether TypeScript is present in the project.
|
|
172
|
+
*
|
|
173
|
+
* `@nuxt/eslint-config` calls `local-pkg`'s `isPackageExists("typescript")`
|
|
174
|
+
* here. The probe is injected so the resolver stays a pure function: the
|
|
175
|
+
* generator passes the real detection, tests pass a fixed answer.
|
|
176
|
+
*/
|
|
177
|
+
type TypeScriptProbe = () => boolean;
|
|
178
|
+
/**
|
|
179
|
+
* Apply `@nuxt/eslint-config`'s feature defaults.
|
|
180
|
+
*
|
|
181
|
+
* Note that only *missing* keys take a default: an explicitly passed
|
|
182
|
+
* `undefined` value is spread over the defaults by the upstream implementation
|
|
183
|
+
* and therefore wins, which this port reproduces by spreading the same way.
|
|
184
|
+
*/
|
|
185
|
+
declare function resolveNuxtLintFeatures(features: NuxtLintFeatures | undefined, hasTypeScript: TypeScriptProbe): ResolvedNuxtLintFeatures;
|
|
186
|
+
/**
|
|
187
|
+
* Whether `nuxt/nuxt-config-keys-order` is enabled.
|
|
188
|
+
*
|
|
189
|
+
* Upstream reads `features.nuxt.sortConfigKeys` and falls back to
|
|
190
|
+
* `!!features.stylistic`, so enabling stylistic rules also turns on config-key
|
|
191
|
+
* sorting unless the project opts out explicitly.
|
|
192
|
+
*/
|
|
193
|
+
declare function shouldSortNuxtConfigKeys(features: ResolvedNuxtLintFeatures): boolean;
|
|
194
|
+
//#endregion
|
|
195
|
+
//#region src/plan.d.ts
|
|
196
|
+
/** Extensions every Nuxt-aware glob covers. */
|
|
197
|
+
declare const NUXT_LINT_GLOB_EXTS = "{js,ts,jsx,tsx,vue}";
|
|
198
|
+
/** Globs matching a `nuxt.config` file in either supported location. */
|
|
199
|
+
declare const NUXT_CONFIG_GLOBS: readonly ["**/.config/nuxt.?([cm])[jt]s?(x)", "**/nuxt.config.?([cm])[jt]s?(x)"];
|
|
200
|
+
/** Directories the generated config never lints. */
|
|
201
|
+
declare const NUXT_LINT_IGNORES: readonly ["**/dist", "**/node_modules", "**/.nuxt", "**/.output", "**/.vercel", "**/.netlify", "**/public"];
|
|
202
|
+
/** A severity as it appears in the plan. */
|
|
203
|
+
type NuxtLintSeverity = "off" | "warn" | "error";
|
|
204
|
+
/** One item of the generated config: a named block of rules over a glob set. */
|
|
205
|
+
interface NuxtLintConfigItem {
|
|
206
|
+
/** Stable identity, matching `@nuxt/eslint`'s config item names. */
|
|
207
|
+
name: string;
|
|
208
|
+
/** Files the block applies to. Absent means "every linted file". */
|
|
209
|
+
files?: string[];
|
|
210
|
+
/** Paths excluded from linting entirely. */
|
|
211
|
+
ignores?: string[];
|
|
212
|
+
/** Rule severities keyed by eslint-compatible rule id. */
|
|
213
|
+
rules?: Record<string, NuxtLintSeverity>;
|
|
214
|
+
/** Globals the block declares, so undefined-variable rules stay quiet. */
|
|
215
|
+
globals?: Record<string, "readonly" | "writable">;
|
|
216
|
+
}
|
|
217
|
+
/** Runtime globals Nuxt injects into every linted file. */
|
|
218
|
+
declare const NUXT_RUNTIME_GLOBALS: Record<string, "readonly">;
|
|
219
|
+
/**
|
|
220
|
+
* Build the Nuxt-specific part of the generated lint config, in emission order.
|
|
221
|
+
*
|
|
222
|
+
* The order is observable — later items override earlier ones — so it is part
|
|
223
|
+
* of the contract the oracle pins, not an implementation detail.
|
|
224
|
+
*/
|
|
225
|
+
declare function buildNuxtLintPlan(features: ResolvedNuxtLintFeatures, dirs: NuxtLintDirs): NuxtLintConfigItem[];
|
|
226
|
+
//#endregion
|
|
227
|
+
export { NUXT_CONFIG_GLOBS, NUXT_LINT_DIR_KEYS, NUXT_LINT_GLOB_EXTS, NUXT_LINT_IGNORES, NUXT_RUNTIME_GLOBALS, type NuxtComponentDirDeclaration, type NuxtLintConfigItem, type NuxtLintDirNames, type NuxtLintDirs, type NuxtLintFeatures, type NuxtLintImportOptions, type NuxtLintLayer, type NuxtLintNuxtOptions, type NuxtLintProjectState, type NuxtLintSeverity, type NuxtLintToolingOptions, type NuxtLintTypeScriptOptions, type ResolvedNuxtLintFeatures, type TypeScriptProbe, buildNuxtLintPlan, collectNuxtLintDirs, resolveNuxtLintDirs, resolveNuxtLintFeatures, shouldSortNuxtConfigKeys };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import { posix } from "node:path";
|
|
2
|
+
//#region src/paths.ts
|
|
3
|
+
/**
|
|
4
|
+
* POSIX path helpers with upstream `pathe` semantics.
|
|
5
|
+
*
|
|
6
|
+
* `@nuxt/eslint` builds every lint directory and glob with `pathe`, which
|
|
7
|
+
* normalises Windows separators to `/` and then behaves like `node:path/posix`.
|
|
8
|
+
* The generated globs are matched against POSIX-normalised paths on every
|
|
9
|
+
* platform, so reproducing that normalisation is what makes the Windows output
|
|
10
|
+
* identical to the Linux and macOS output.
|
|
11
|
+
*/
|
|
12
|
+
/** Replace Windows separators so POSIX path maths applies uniformly. */
|
|
13
|
+
function toPosix(value) {
|
|
14
|
+
return value.replace(/\\/g, "/");
|
|
15
|
+
}
|
|
16
|
+
/** `pathe.join` — POSIX join over separator-normalised segments. */
|
|
17
|
+
function posixJoin(...segments) {
|
|
18
|
+
return posix.join(...segments.map(toPosix));
|
|
19
|
+
}
|
|
20
|
+
/** `pathe.resolve` — POSIX resolve over separator-normalised segments. */
|
|
21
|
+
function posixResolve(...segments) {
|
|
22
|
+
return posix.resolve(...segments.map(toPosix));
|
|
23
|
+
}
|
|
24
|
+
/** `pathe.relative` — POSIX relative over separator-normalised paths. */
|
|
25
|
+
function posixRelative(from, to) {
|
|
26
|
+
return posix.relative(toPosix(from), toPosix(to));
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
29
|
+
//#region src/dirs.ts
|
|
30
|
+
/**
|
|
31
|
+
* Project-state to shareable lint-directory resolution.
|
|
32
|
+
*
|
|
33
|
+
* This is a behavioural port of `getDirs()` in `@nuxt/eslint` plus the `dirs`
|
|
34
|
+
* defaulting in `@nuxt/eslint-config`'s `resolveOptions()`. Every directory the
|
|
35
|
+
* generated lint config globs over is derived here, so this module is the input
|
|
36
|
+
* both the config emitter and the dev-server checker read.
|
|
37
|
+
*
|
|
38
|
+
* The exact resolution is pinned against the real packages by the differential
|
|
39
|
+
* oracle in `test/nuxt-eslint-compat/`.
|
|
40
|
+
*/
|
|
41
|
+
/**
|
|
42
|
+
* The key order of {@link NuxtLintDirs}.
|
|
43
|
+
*
|
|
44
|
+
* Upstream serialises `dirs` with `Object.entries`, so this order is observable
|
|
45
|
+
* in the generated config file and the oracle asserts it.
|
|
46
|
+
*/
|
|
47
|
+
const NUXT_LINT_DIR_KEYS = [
|
|
48
|
+
"pages",
|
|
49
|
+
"composables",
|
|
50
|
+
"components",
|
|
51
|
+
"componentsPrefixed",
|
|
52
|
+
"layouts",
|
|
53
|
+
"plugins",
|
|
54
|
+
"middleware",
|
|
55
|
+
"modules",
|
|
56
|
+
"servers",
|
|
57
|
+
"root",
|
|
58
|
+
"src"
|
|
59
|
+
];
|
|
60
|
+
function emptyDirs() {
|
|
61
|
+
return {
|
|
62
|
+
pages: [],
|
|
63
|
+
composables: [],
|
|
64
|
+
components: [],
|
|
65
|
+
componentsPrefixed: [],
|
|
66
|
+
layouts: [],
|
|
67
|
+
plugins: [],
|
|
68
|
+
middleware: [],
|
|
69
|
+
modules: [],
|
|
70
|
+
servers: [],
|
|
71
|
+
root: [],
|
|
72
|
+
src: []
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Normalise one layer-relative path into a root-relative POSIX path.
|
|
77
|
+
*
|
|
78
|
+
* The `~/` (and `~\`) prefix is stripped rather than resolved: upstream treats a
|
|
79
|
+
* tilde-prefixed `imports.dirs` entry as srcDir-relative, which is the same
|
|
80
|
+
* thing Nuxt's own alias resolution does for that option.
|
|
81
|
+
*/
|
|
82
|
+
function layerRelative(rootDir, srcDir, target) {
|
|
83
|
+
return posixRelative(rootDir, posixResolve(srcDir, target.replace(/^~[/\\]/, "")));
|
|
84
|
+
}
|
|
85
|
+
function collectComponentDirs(layer, dirs, toRootRelative) {
|
|
86
|
+
if (!layer.components || layer.components === true) {
|
|
87
|
+
dirs.components.push(toRootRelative("components"));
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const declarations = Array.isArray(layer.components) ? layer.components : layer.components.dirs ?? [];
|
|
91
|
+
for (const declaration of declarations) {
|
|
92
|
+
if (typeof declaration === "string") {
|
|
93
|
+
dirs.components.push(toRootRelative(declaration));
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (!declaration || typeof declaration.path !== "string") continue;
|
|
97
|
+
dirs.components.push(toRootRelative(declaration.path));
|
|
98
|
+
if (declaration.prefix) dirs.componentsPrefixed.push(toRootRelative(declaration.path));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Derive every lint directory from the Nuxt project state.
|
|
103
|
+
*
|
|
104
|
+
* `servers` and `root` are intentionally left empty: upstream's `getDirs` never
|
|
105
|
+
* populates them, and because an empty array is truthy the `||=` defaults in
|
|
106
|
+
* `resolveNuxtLintDirs` do not fire for a module-generated config either. The
|
|
107
|
+
* oracle pins that, so a future upstream change surfaces as a failure rather
|
|
108
|
+
* than as a silently different set of linted files.
|
|
109
|
+
*/
|
|
110
|
+
function collectNuxtLintDirs(state) {
|
|
111
|
+
const dirs = emptyDirs();
|
|
112
|
+
const dirNames = state.dir ?? {};
|
|
113
|
+
for (const layer of state.layers) {
|
|
114
|
+
const toRootRelative = (target) => layerRelative(state.rootDir, layer.srcDir, target);
|
|
115
|
+
dirs.src.push(toRootRelative(""));
|
|
116
|
+
dirs.pages.push(toRootRelative(dirNames.pages || "pages"));
|
|
117
|
+
dirs.layouts.push(toRootRelative(dirNames.layouts || "layouts"));
|
|
118
|
+
dirs.plugins.push(toRootRelative(dirNames.plugins || "plugins"));
|
|
119
|
+
dirs.middleware.push(toRootRelative(dirNames.middleware || "middleware"));
|
|
120
|
+
dirs.modules.push(toRootRelative(dirNames.modules || "modules"));
|
|
121
|
+
dirs.composables.push(toRootRelative("composables"));
|
|
122
|
+
dirs.composables.push(toRootRelative("utils"));
|
|
123
|
+
for (const dir of layer.imports?.dirs ?? []) if (dir) dirs.composables.push(toRootRelative(dir));
|
|
124
|
+
collectComponentDirs(layer, dirs, toRootRelative);
|
|
125
|
+
}
|
|
126
|
+
return dirs;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Apply the directory defaults used when a config is written by hand rather
|
|
130
|
+
* than generated from a Nuxt instance.
|
|
131
|
+
*
|
|
132
|
+
* Upstream uses `||=`, so a directory list that is present but empty keeps its
|
|
133
|
+
* empty value. This port reproduces that, because generated configs rely on it.
|
|
134
|
+
*/
|
|
135
|
+
function resolveNuxtLintDirs(dirs) {
|
|
136
|
+
const resolved = { ...dirs };
|
|
137
|
+
resolved.root ||= [".", "./app"];
|
|
138
|
+
resolved.src ||= resolved.root;
|
|
139
|
+
const src = resolved.src;
|
|
140
|
+
resolved.pages ||= src.map((dir) => `${dir}/pages`);
|
|
141
|
+
resolved.layouts ||= src.map((dir) => `${dir}/layouts`);
|
|
142
|
+
resolved.components ||= src.map((dir) => `${dir}/components`);
|
|
143
|
+
resolved.composables ||= src.map((dir) => `${dir}/composables`);
|
|
144
|
+
resolved.plugins ||= src.map((dir) => `${dir}/plugins`);
|
|
145
|
+
resolved.modules ||= src.map((dir) => `${dir}/modules`);
|
|
146
|
+
resolved.middleware ||= src.map((dir) => `${dir}/middleware`);
|
|
147
|
+
resolved.servers ||= src.map((dir) => `${dir}/servers`);
|
|
148
|
+
resolved.componentsPrefixed ||= [];
|
|
149
|
+
return resolved;
|
|
150
|
+
}
|
|
151
|
+
//#endregion
|
|
152
|
+
//#region src/features.ts
|
|
153
|
+
/**
|
|
154
|
+
* Apply `@nuxt/eslint-config`'s feature defaults.
|
|
155
|
+
*
|
|
156
|
+
* Note that only *missing* keys take a default: an explicitly passed
|
|
157
|
+
* `undefined` value is spread over the defaults by the upstream implementation
|
|
158
|
+
* and therefore wins, which this port reproduces by spreading the same way.
|
|
159
|
+
*/
|
|
160
|
+
function resolveNuxtLintFeatures(features, hasTypeScript) {
|
|
161
|
+
return {
|
|
162
|
+
standalone: true,
|
|
163
|
+
stylistic: false,
|
|
164
|
+
typescript: hasTypeScript(),
|
|
165
|
+
tooling: false,
|
|
166
|
+
formatters: false,
|
|
167
|
+
nuxt: {},
|
|
168
|
+
import: {},
|
|
169
|
+
...features
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Whether `nuxt/nuxt-config-keys-order` is enabled.
|
|
174
|
+
*
|
|
175
|
+
* Upstream reads `features.nuxt.sortConfigKeys` and falls back to
|
|
176
|
+
* `!!features.stylistic`, so enabling stylistic rules also turns on config-key
|
|
177
|
+
* sorting unless the project opts out explicitly.
|
|
178
|
+
*/
|
|
179
|
+
function shouldSortNuxtConfigKeys(features) {
|
|
180
|
+
const { sortConfigKeys = !!features.stylistic } = features.nuxt || {};
|
|
181
|
+
return sortConfigKeys;
|
|
182
|
+
}
|
|
183
|
+
//#endregion
|
|
184
|
+
//#region src/plan.ts
|
|
185
|
+
/** Extensions every Nuxt-aware glob covers. */
|
|
186
|
+
const NUXT_LINT_GLOB_EXTS = "{js,ts,jsx,tsx,vue}";
|
|
187
|
+
/** Globs matching a `nuxt.config` file in either supported location. */
|
|
188
|
+
const NUXT_CONFIG_GLOBS = ["**/.config/nuxt.?([cm])[jt]s?(x)", "**/nuxt.config.?([cm])[jt]s?(x)"];
|
|
189
|
+
/** Directories the generated config never lints. */
|
|
190
|
+
const NUXT_LINT_IGNORES = [
|
|
191
|
+
"**/dist",
|
|
192
|
+
"**/node_modules",
|
|
193
|
+
"**/.nuxt",
|
|
194
|
+
"**/.output",
|
|
195
|
+
"**/.vercel",
|
|
196
|
+
"**/.netlify",
|
|
197
|
+
"**/public"
|
|
198
|
+
];
|
|
199
|
+
/** Runtime globals Nuxt injects into every linted file. */
|
|
200
|
+
const NUXT_RUNTIME_GLOBALS = { $fetch: "readonly" };
|
|
201
|
+
const nestedGlob = `**/*.${NUXT_LINT_GLOB_EXTS}`;
|
|
202
|
+
/**
|
|
203
|
+
* Files allowed to have a single-word component name.
|
|
204
|
+
*
|
|
205
|
+
* Nuxt gives `app` and `error` a special meaning, layouts and pages are never
|
|
206
|
+
* referenced by tag name, and prefixed component directories get their name
|
|
207
|
+
* from the prefix — so `vue/multi-word-component-names` is off for all of them.
|
|
208
|
+
*/
|
|
209
|
+
function routeFiles(dirs) {
|
|
210
|
+
return [...new Set([
|
|
211
|
+
...dirs.src.flatMap((dir) => [posixJoin(dir, `app.${NUXT_LINT_GLOB_EXTS}`), posixJoin(dir, `error.${NUXT_LINT_GLOB_EXTS}`)]),
|
|
212
|
+
...dirs.layouts.map((dir) => posixJoin(dir, nestedGlob)),
|
|
213
|
+
...dirs.pages.map((dir) => posixJoin(dir, nestedGlob)),
|
|
214
|
+
...dirs.components.map((dir) => posixJoin(dir, "*", nestedGlob)),
|
|
215
|
+
...dirs.componentsPrefixed.map((dir) => posixJoin(dir, nestedGlob))
|
|
216
|
+
])].sort();
|
|
217
|
+
}
|
|
218
|
+
/** Files that must render exactly one root element. */
|
|
219
|
+
function singleRootFiles(dirs) {
|
|
220
|
+
return [
|
|
221
|
+
...dirs.layouts.map((dir) => posixJoin(dir, nestedGlob)),
|
|
222
|
+
...dirs.pages.map((dir) => posixJoin(dir, nestedGlob)),
|
|
223
|
+
...dirs.components.map((dir) => posixJoin(dir, `**/*.server.${NUXT_LINT_GLOB_EXTS}`))
|
|
224
|
+
].sort();
|
|
225
|
+
}
|
|
226
|
+
/** Files where `definePageMeta` is extracted at build time. */
|
|
227
|
+
function pageFiles(dirs) {
|
|
228
|
+
return dirs.pages.map((dir) => posixJoin(dir, nestedGlob)).sort();
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Build the Nuxt-specific part of the generated lint config, in emission order.
|
|
232
|
+
*
|
|
233
|
+
* The order is observable — later items override earlier ones — so it is part
|
|
234
|
+
* of the contract the oracle pins, not an implementation detail.
|
|
235
|
+
*/
|
|
236
|
+
function buildNuxtLintPlan(features, dirs) {
|
|
237
|
+
const items = [];
|
|
238
|
+
if (features.standalone !== false) items.push({
|
|
239
|
+
name: "nuxt/ignores",
|
|
240
|
+
ignores: [...NUXT_LINT_IGNORES]
|
|
241
|
+
});
|
|
242
|
+
items.push({
|
|
243
|
+
name: "nuxt/setup",
|
|
244
|
+
globals: { ...NUXT_RUNTIME_GLOBALS }
|
|
245
|
+
});
|
|
246
|
+
const singleRoot = singleRootFiles(dirs);
|
|
247
|
+
if (singleRoot.length > 0) items.push({
|
|
248
|
+
name: "nuxt/vue/single-root",
|
|
249
|
+
files: singleRoot,
|
|
250
|
+
rules: { "vue/no-multiple-template-root": "error" }
|
|
251
|
+
});
|
|
252
|
+
items.push({
|
|
253
|
+
name: "nuxt/rules",
|
|
254
|
+
rules: { "nuxt/prefer-import-meta": "error" }
|
|
255
|
+
});
|
|
256
|
+
const pages = pageFiles(dirs);
|
|
257
|
+
if (pages.length > 0) items.push({
|
|
258
|
+
name: "nuxt/pages",
|
|
259
|
+
files: pages,
|
|
260
|
+
rules: { "nuxt/no-page-meta-runtime-values": "error" }
|
|
261
|
+
});
|
|
262
|
+
items.push({
|
|
263
|
+
name: "nuxt/nuxt-config",
|
|
264
|
+
files: [...NUXT_CONFIG_GLOBS],
|
|
265
|
+
rules: { "nuxt/no-nuxt-config-test-key": "error" }
|
|
266
|
+
});
|
|
267
|
+
if (shouldSortNuxtConfigKeys(features)) items.push({
|
|
268
|
+
name: "nuxt/sort-config",
|
|
269
|
+
files: [...NUXT_CONFIG_GLOBS],
|
|
270
|
+
rules: { "nuxt/nuxt-config-keys-order": "error" }
|
|
271
|
+
});
|
|
272
|
+
const routes = routeFiles(dirs);
|
|
273
|
+
if (routes.length > 0) items.push({
|
|
274
|
+
name: "nuxt/disables/routes",
|
|
275
|
+
files: routes,
|
|
276
|
+
rules: { "vue/multi-word-component-names": "off" }
|
|
277
|
+
});
|
|
278
|
+
return items;
|
|
279
|
+
}
|
|
280
|
+
//#endregion
|
|
281
|
+
export { NUXT_CONFIG_GLOBS, NUXT_LINT_DIR_KEYS, NUXT_LINT_GLOB_EXTS, NUXT_LINT_IGNORES, NUXT_RUNTIME_GLOBALS, buildNuxtLintPlan, collectNuxtLintDirs, resolveNuxtLintDirs, resolveNuxtLintFeatures, shouldSortNuxtConfigKeys };
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vizejs/nuxt-lint-config",
|
|
3
|
+
"version": "0.315.1",
|
|
4
|
+
"description": "Shareable Nuxt-aware lint preset core for the Vize toolchain",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"config",
|
|
7
|
+
"lint",
|
|
8
|
+
"nuxt",
|
|
9
|
+
"oxlint",
|
|
10
|
+
"preset",
|
|
11
|
+
"vize",
|
|
12
|
+
"vue"
|
|
13
|
+
],
|
|
14
|
+
"homepage": "https://github.com/ubugeeei-prod/vize",
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/ubugeeei-prod/vize/issues"
|
|
17
|
+
},
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"author": "ubugeeei",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "https://github.com/ubugeeei-prod/vize.git",
|
|
23
|
+
"directory": "npm/framework/nuxt-lint-config"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist"
|
|
27
|
+
],
|
|
28
|
+
"type": "module",
|
|
29
|
+
"main": "./dist/index.mjs",
|
|
30
|
+
"types": "./dist/index.d.mts",
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"types": "./dist/index.d.mts",
|
|
34
|
+
"import": "./dist/index.mjs",
|
|
35
|
+
"default": "./dist/index.mjs"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"publishConfig": {
|
|
39
|
+
"access": "public"
|
|
40
|
+
},
|
|
41
|
+
"scripts": {
|
|
42
|
+
"build": "vp pack",
|
|
43
|
+
"dev": "vp pack --watch",
|
|
44
|
+
"test": "pnpm build && node --test src/*.test.ts",
|
|
45
|
+
"check": "vp check src vite.config.ts",
|
|
46
|
+
"check:fix": "vp check --fix src vite.config.ts",
|
|
47
|
+
"fmt": "vp fmt --write src vite.config.ts"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@types/node": "25.9.2",
|
|
51
|
+
"typescript": "6.0.3",
|
|
52
|
+
"vite": "npm:@voidzero-dev/vite-plus-core@0.1.21",
|
|
53
|
+
"vite-plus": "0.1.21"
|
|
54
|
+
},
|
|
55
|
+
"engines": {
|
|
56
|
+
"node": ">=22"
|
|
57
|
+
}
|
|
58
|
+
}
|