@nustackjs/lint 0.0.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/LICENSE +21 -0
- package/README.md +106 -0
- package/dist/index.d.mts +104 -0
- package/dist/index.mjs +346 -0
- package/dist/module.d.mts +14 -0
- package/dist/module.json +9 -0
- package/dist/module.mjs +140 -0
- package/dist/shared/lint.Cq_cclOu.d.mts +41 -0
- package/dist/types.d.mts +5 -0
- package/package.json +103 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 - Present Zerya and contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# NuStack Lint
|
|
2
|
+
|
|
3
|
+
[![Nuxt][nuxt-src]][nuxt-href]
|
|
4
|
+
|
|
5
|
+
> [!CAUTION]
|
|
6
|
+
> Very early in development. The ESLint engine is a stepping stone toward an
|
|
7
|
+
> oxlint-based v1.0 — see the [roadmap](./docs/roadmap.md). Don't depend on it yet.
|
|
8
|
+
|
|
9
|
+
Zero-config ESLint for Nuxt. Add one module and get a full setup — no `eslint.config` to
|
|
10
|
+
assemble, no plugin list to keep in sync across projects. It detects what your project
|
|
11
|
+
uses and turns on the rules that fit, on top of
|
|
12
|
+
[`@nuxt/eslint`](https://eslint.nuxt.com) and
|
|
13
|
+
[`@antfu/eslint-config`](https://github.com/antfu/eslint-config). Part of
|
|
14
|
+
[NuStack](../../README.md).
|
|
15
|
+
|
|
16
|
+
Three reasons it exists:
|
|
17
|
+
|
|
18
|
+
- **Plug it in.** Stop reconfiguring lint on every project. One module bundles and wires
|
|
19
|
+
the antfu base, `@nuxt/eslint`, and a growing set of Nuxt-aware rules, and upgrades
|
|
20
|
+
them together.
|
|
21
|
+
- **Catch more.** Deep, Nuxt-aware checks that go well past formatting — promoting
|
|
22
|
+
ecosystem best practices and catching the slop that otherwise slips through code
|
|
23
|
+
review, including what AI agents generate. The ruleset keeps growing. The custom rules
|
|
24
|
+
ship as standalone, [Oxlint](https://oxc.rs)-ready plugins
|
|
25
|
+
([nuxt](../../packages/lint-plugin-nuxt), [vueuse](../../packages/lint-plugin-vueuse),
|
|
26
|
+
[vite](../../packages/lint-plugin-vite),
|
|
27
|
+
[nuxt-ecosystem](../../packages/lint-plugin-nuxt-ecosystem)) that you can also use on
|
|
28
|
+
their own.
|
|
29
|
+
- **Policy as code.** When there are several ways to do the same thing, it picks one and
|
|
30
|
+
enforces it — so the decision lives in the linter instead of in every code review.
|
|
31
|
+
Disagree with a pick? Override it; everything is configurable.
|
|
32
|
+
|
|
33
|
+
## Setup
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npx nuxi module add @nustackjs/lint
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
That's the only module you list — `@nuxt/eslint` is bundled and installed for you (in
|
|
40
|
+
composable mode), so there's no `eslint` key to configure:
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
// nuxt.config.ts
|
|
44
|
+
export default defineNuxtConfig({
|
|
45
|
+
modules: ['@nustackjs/lint'],
|
|
46
|
+
})
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Then point `eslint.config.ts` at the generated config (it already wires in `withNuxt()`
|
|
50
|
+
for you — config lives here, not in `nuxt.config`):
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
// eslint.config.ts
|
|
54
|
+
export { default } from './.nuxt/nustack-eslint.mjs'
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
To pass options, call the factory instead — same file, `withNuxt()` still prewired:
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
// eslint.config.ts
|
|
61
|
+
import { nustack } from './.nuxt/nustack-eslint.mjs'
|
|
62
|
+
|
|
63
|
+
export default nustack({ variant: 'pedantic' })
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
eslint . # quick checks (fast)
|
|
68
|
+
NUSTACK_LINT_DEPTH=full eslint . # + type-aware checks (CI)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
See [Configuration](./docs/configuration.md), [Rules](./docs/rules.md) and
|
|
72
|
+
[Migration](./docs/migration.md).
|
|
73
|
+
|
|
74
|
+
## Non-Nuxt projects
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
// eslint.config.ts
|
|
78
|
+
import nustack from '@nustackjs/lint/config'
|
|
79
|
+
|
|
80
|
+
export default nustack({ base: { type: 'lib' } })
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Config inspector
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
npx @eslint/config-inspector --config eslint.config.ts
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Contributing
|
|
90
|
+
|
|
91
|
+
`DEVELOPMENT.md` is the design contract — read it first.
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
pnpm install
|
|
95
|
+
pnpm dev:prepare # build module + nuxt prepare playground
|
|
96
|
+
pnpm lint # dogfoods the preset on its own source
|
|
97
|
+
pnpm test
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## License
|
|
101
|
+
|
|
102
|
+
[MIT](./LICENSE) © Zerya
|
|
103
|
+
|
|
104
|
+
<!-- Badges -->
|
|
105
|
+
[nuxt-src]: https://img.shields.io/badge/Nuxt-020420?logo=nuxt
|
|
106
|
+
[nuxt-href]: https://nuxt.com
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { Linter } from 'eslint';
|
|
2
|
+
import { FlatConfigComposer } from 'eslint-flat-config-utils';
|
|
3
|
+
import antfu from '@antfu/eslint-config';
|
|
4
|
+
import { N as NustackContext } from './shared/lint.Cq_cclOu.mjs';
|
|
5
|
+
export { E as EMPTY_CONTEXT } from './shared/lint.Cq_cclOu.mjs';
|
|
6
|
+
|
|
7
|
+
type Rules = Record<string, Linter.RuleEntry>;
|
|
8
|
+
/** Opinion strength — static, set in `eslint.config.ts`. Cumulative. */
|
|
9
|
+
type Variant = 'minimal' | 'recommended' | 'pedantic';
|
|
10
|
+
/** Analysis depth — per-run, from `NUSTACK_LINT_DEPTH`. Cumulative. */
|
|
11
|
+
type Depth = 'quick' | 'full';
|
|
12
|
+
/** Base options shared by every per-concern option object. */
|
|
13
|
+
interface ConcernOptions {
|
|
14
|
+
/** Per-concern rule changes, merged after the concern's defaults. */
|
|
15
|
+
rules?: Rules;
|
|
16
|
+
/** @deprecated Use `rules` instead. */
|
|
17
|
+
overrides?: Rules;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Options accepted by `@antfu/eslint-config`'s factory. */
|
|
21
|
+
type AntfuOptions = NonNullable<Parameters<typeof antfu>[0]>;
|
|
22
|
+
|
|
23
|
+
interface NuxtConcernOptions extends ConcernOptions {
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface NuxtUiConcernOptions extends ConcernOptions {
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface TailwindConcernOptions extends ConcernOptions {
|
|
30
|
+
/** Override the auto-detected Tailwind entry point (`@import "tailwindcss"`). */
|
|
31
|
+
entryPoint?: string;
|
|
32
|
+
/**
|
|
33
|
+
* Extra `better-tailwindcss` shared settings, merged over the defaults — e.g.
|
|
34
|
+
* `attributes`, `callees`, `tailwindConfig`. Rule options (printWidth etc.) go
|
|
35
|
+
* through `rules`, not here.
|
|
36
|
+
*/
|
|
37
|
+
settings?: Record<string, unknown>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface ViteConcernOptions extends ConcernOptions {
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface VueConcernOptions extends ConcernOptions {
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface VueUseConcernOptions extends ConcernOptions {
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
type Awaitable<T> = T | Promise<T>;
|
|
50
|
+
type NustackFlatConfig = Linter.Config | Linter.Config[] | FlatConfigComposer;
|
|
51
|
+
type NustackUserConfig = Awaitable<NustackFlatConfig>;
|
|
52
|
+
/** A per-concern toggle: `true`/`undefined` = default, `false` = off, object = tune. */
|
|
53
|
+
type ConcernToggle<T> = boolean | T;
|
|
54
|
+
/**
|
|
55
|
+
* Options for the public nustack ESLint factory. Mirrors `@antfu/eslint-config`'s
|
|
56
|
+
* shape: one options object, then optional flat config objects appended last.
|
|
57
|
+
* `depth` is intentionally absent — it is read per-run from `NUSTACK_LINT_DEPTH`.
|
|
58
|
+
*/
|
|
59
|
+
interface NustackLintOptions {
|
|
60
|
+
/** Opinion strength. Default `recommended`. Cumulative: minimal ⊂ recommended ⊂ pedantic. */
|
|
61
|
+
variant?: Variant;
|
|
62
|
+
/** The antfu style base. Object tunes it, `false` disables it. */
|
|
63
|
+
base?: AntfuOptions | false;
|
|
64
|
+
/** Core Nuxt conventions (auto-imports, runtimeConfig, process.env). */
|
|
65
|
+
nuxt?: ConcernToggle<NuxtConcernOptions>;
|
|
66
|
+
/** SFC conventions (vue/block-lang etc.). */
|
|
67
|
+
vue?: ConcernToggle<VueConcernOptions>;
|
|
68
|
+
/** VueUse usage conventions. */
|
|
69
|
+
vueUse?: ConcernToggle<VueUseConcernOptions>;
|
|
70
|
+
/** Vite build/runtime conventions. */
|
|
71
|
+
vite?: ConcernToggle<ViteConcernOptions>;
|
|
72
|
+
/** Nuxt UI component preferences. Auto-gated on `@nuxt/ui` detection. */
|
|
73
|
+
nuxtUi?: ConcernToggle<NuxtUiConcernOptions>;
|
|
74
|
+
/** Tailwind class sorting/correctness. Auto-gated on a detected entry point. */
|
|
75
|
+
tailwind?: ConcernToggle<TailwindConcernOptions>;
|
|
76
|
+
/** Global rule changes, merged after every concern. */
|
|
77
|
+
rules?: Rules;
|
|
78
|
+
/** @deprecated Use `rules` instead. */
|
|
79
|
+
overrides?: Rules;
|
|
80
|
+
/** Detected project context. Injected by the generated `.nuxt/nustack-eslint.mjs`. */
|
|
81
|
+
context?: NustackContext;
|
|
82
|
+
}
|
|
83
|
+
type NustackOptions = NustackLintOptions;
|
|
84
|
+
/**
|
|
85
|
+
* Wraps a `withNuxt()` composer with the full nustack config. Mirrors antfu: the
|
|
86
|
+
* style base is prepended, then each enabled, context-gated concern is appended,
|
|
87
|
+
* then global rules and user flat configs. Returns the same composer for chaining.
|
|
88
|
+
*
|
|
89
|
+
* The generated `.nuxt/nustack-eslint.mjs` calls this with the detected context in
|
|
90
|
+
* `options.context`; callers normally import the pre-bound `nustack` from there
|
|
91
|
+
* and may pass their own options plus file-scoped flat configs.
|
|
92
|
+
*/
|
|
93
|
+
declare function applyNustackConfig(base: FlatConfigComposer, options?: NustackLintOptions, ...userConfigs: NustackUserConfig[]): FlatConfigComposer;
|
|
94
|
+
/**
|
|
95
|
+
* Public standalone (non-Nuxt) entry. Builds a composer from scratch — no `withNuxt()` —
|
|
96
|
+
* so a plain TypeScript/Vue repo can consume the nustack preset directly. The
|
|
97
|
+
* Nuxt/project-detected concerns default off here since there's no Nuxt project
|
|
98
|
+
* to detect; enable them explicitly if you want them. Used to dogfood `@nustackjs/lint`
|
|
99
|
+
* on its own (non-Nuxt) source.
|
|
100
|
+
*/
|
|
101
|
+
declare function nustack(options?: NustackLintOptions, ...userConfigs: NustackUserConfig[]): FlatConfigComposer;
|
|
102
|
+
|
|
103
|
+
export { NustackContext, applyNustackConfig, nustack as default, nustack };
|
|
104
|
+
export type { Awaitable, Depth, NustackFlatConfig, NustackLintOptions, NustackOptions, NustackUserConfig, Rules, Variant };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
import { defu } from 'defu';
|
|
2
|
+
import { composer } from 'eslint-flat-config-utils';
|
|
3
|
+
import antfu from '@antfu/eslint-config';
|
|
4
|
+
import nuxtPlugin from '@nustackjs/lint-plugin-nuxt';
|
|
5
|
+
import nuxtEcosystemPlugin from '@nustackjs/lint-plugin-nuxt-ecosystem';
|
|
6
|
+
import betterTailwind from 'eslint-plugin-better-tailwindcss';
|
|
7
|
+
import { getDefaultCallees, getDefaultAttributes } from 'eslint-plugin-better-tailwindcss/api/defaults';
|
|
8
|
+
import { MatcherType } from 'eslint-plugin-better-tailwindcss/api/types';
|
|
9
|
+
import vitePlugin from '@nustackjs/lint-plugin-vite';
|
|
10
|
+
import vueUsePlugin from '@nustackjs/lint-plugin-vueuse';
|
|
11
|
+
|
|
12
|
+
const ANTFU_DEFAULTS = {
|
|
13
|
+
stylistic: true,
|
|
14
|
+
vue: true,
|
|
15
|
+
rules: {
|
|
16
|
+
// Nuxt pages/layouts legitimately have multiple root nodes.
|
|
17
|
+
"vue/no-multiple-template-root": "off",
|
|
18
|
+
// `process` is a Nuxt/Nitro global; @nustack/nuxt/no-process-env handles app code.
|
|
19
|
+
"node/prefer-global/process": "off",
|
|
20
|
+
"style/brace-style": ["warn", "1tbs"]
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
function resolveTypescriptOptions(userBase, depth) {
|
|
24
|
+
const userTypescript = userBase?.typescript;
|
|
25
|
+
if (depth === "full") {
|
|
26
|
+
return defu(
|
|
27
|
+
typeof userTypescript === "object" && userTypescript !== null ? userTypescript : {},
|
|
28
|
+
{ tsconfigPath: "tsconfig.json" }
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
return typeof userTypescript === "object" && userTypescript !== null ? userTypescript : true;
|
|
32
|
+
}
|
|
33
|
+
function antfuBase(userBase, depth) {
|
|
34
|
+
if (userBase === false)
|
|
35
|
+
return null;
|
|
36
|
+
return antfu({
|
|
37
|
+
...defu(userBase, ANTFU_DEFAULTS),
|
|
38
|
+
typescript: resolveTypescriptOptions(userBase, depth)
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const VARIANT_ORDER = { minimal: 0, recommended: 1, pedantic: 2 };
|
|
43
|
+
function variantAtLeast(variant, min) {
|
|
44
|
+
return VARIANT_ORDER[variant] >= VARIANT_ORDER[min];
|
|
45
|
+
}
|
|
46
|
+
function resolveConcernRules(options) {
|
|
47
|
+
return {
|
|
48
|
+
...options.overrides,
|
|
49
|
+
...options.rules
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const GLOB_APP$2 = ["**/*.vue", "**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}"];
|
|
54
|
+
const GLOB_NUXT_CONFIG = ["**/nuxt.config.{ts,js,mjs,mts,cjs,cts}"];
|
|
55
|
+
const IGNORE_NON_APP = [
|
|
56
|
+
"**/server/**",
|
|
57
|
+
"**/scripts/**",
|
|
58
|
+
"**/packages/**",
|
|
59
|
+
"**/*.{config,test,spec}.*",
|
|
60
|
+
"**/*.d.ts"
|
|
61
|
+
];
|
|
62
|
+
const recommendedRules$2 = nuxtPlugin.configs.recommended.rules ?? {};
|
|
63
|
+
function nuxtConfig(ctx, axes, opts = {}) {
|
|
64
|
+
const rules = resolveConcernRules(opts);
|
|
65
|
+
const configs = [
|
|
66
|
+
{
|
|
67
|
+
// Secret leakage is a correctness/security floor — on at every variant.
|
|
68
|
+
name: "nustack/nuxt/runtime-config",
|
|
69
|
+
files: GLOB_NUXT_CONFIG,
|
|
70
|
+
plugins: { "@nustack/nuxt": nuxtPlugin },
|
|
71
|
+
rules: {
|
|
72
|
+
"@nustack/nuxt/no-secret-in-public-runtimeconfig": "error"
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
];
|
|
76
|
+
if (variantAtLeast(axes.variant, "recommended")) {
|
|
77
|
+
configs.push(
|
|
78
|
+
{
|
|
79
|
+
name: "nustack/nuxt/auto-imports",
|
|
80
|
+
files: GLOB_APP$2,
|
|
81
|
+
ignores: IGNORE_NON_APP,
|
|
82
|
+
plugins: { "@nustack/nuxt": nuxtPlugin },
|
|
83
|
+
rules: {
|
|
84
|
+
...recommendedRules$2,
|
|
85
|
+
"@nustack/nuxt/no-process-env": "off",
|
|
86
|
+
"@nustack/nuxt/no-secret-in-public-runtimeconfig": "off",
|
|
87
|
+
"@nustack/nuxt/no-explicit-auto-import": ["error", {
|
|
88
|
+
imports: ctx.autoImports,
|
|
89
|
+
components: ctx.components
|
|
90
|
+
}]
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: "@nustack/nuxt/no-process-env",
|
|
95
|
+
files: GLOB_APP$2,
|
|
96
|
+
// process.env is legitimate in build/server config; only flag app code.
|
|
97
|
+
ignores: IGNORE_NON_APP,
|
|
98
|
+
plugins: { "@nustack/nuxt": nuxtPlugin },
|
|
99
|
+
rules: {
|
|
100
|
+
"@nustack/nuxt/no-process-env": recommendedRules$2["@nustack/nuxt/no-process-env"]
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
if (Object.keys(rules).length) {
|
|
106
|
+
configs.push({
|
|
107
|
+
name: "nustack/nuxt/rules",
|
|
108
|
+
files: GLOB_APP$2,
|
|
109
|
+
rules
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
return configs;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const uiRules = nuxtEcosystemPlugin.configs.ui.rules ?? {};
|
|
116
|
+
function nuxtUiConfig(_ctx, _axes, opts = {}) {
|
|
117
|
+
const rules = resolveConcernRules(opts);
|
|
118
|
+
return [
|
|
119
|
+
{
|
|
120
|
+
name: "nustack/nuxt-ui",
|
|
121
|
+
files: ["**/*.vue"],
|
|
122
|
+
plugins: { "@nustack/nuxt-ui": nuxtEcosystemPlugin },
|
|
123
|
+
rules: {
|
|
124
|
+
...uiRules,
|
|
125
|
+
...rules
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const GLOB_CODE = ["**/*.vue", "**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}"];
|
|
132
|
+
function tailwindConfig(ctx, axes, opts = {}) {
|
|
133
|
+
const rules = resolveConcernRules(opts);
|
|
134
|
+
const uiAttributes = ctx.modules.nuxtUi ? [
|
|
135
|
+
// Bound `:ui` — better-tailwindcss normalizes it to `v-bind:ui`;
|
|
136
|
+
// the leading `:` in the config name is the signal it does so.
|
|
137
|
+
[":ui", [{ match: MatcherType.ObjectValue }]],
|
|
138
|
+
["ui", [{ match: MatcherType.ObjectValue }]]
|
|
139
|
+
] : [];
|
|
140
|
+
const entryPoint = opts.entryPoint ?? ctx.tailwind.entryPoint;
|
|
141
|
+
return [
|
|
142
|
+
{
|
|
143
|
+
name: "nustack/tailwind",
|
|
144
|
+
files: GLOB_CODE,
|
|
145
|
+
plugins: { "better-tailwindcss": betterTailwind },
|
|
146
|
+
settings: {
|
|
147
|
+
"better-tailwindcss": defu(opts.settings, {
|
|
148
|
+
...entryPoint ? { entryPoint } : {},
|
|
149
|
+
attributes: [...getDefaultAttributes(), ...uiAttributes],
|
|
150
|
+
callees: getDefaultCallees()
|
|
151
|
+
})
|
|
152
|
+
},
|
|
153
|
+
rules: {
|
|
154
|
+
"better-tailwindcss/enforce-consistent-class-order": "warn",
|
|
155
|
+
...variantAtLeast(axes.variant, "pedantic") ? { "better-tailwindcss/enforce-consistent-line-wrapping": "warn" } : {},
|
|
156
|
+
"better-tailwindcss/no-unregistered-classes": "warn",
|
|
157
|
+
"better-tailwindcss/no-conflicting-classes": "error",
|
|
158
|
+
"better-tailwindcss/no-duplicate-classes": "error",
|
|
159
|
+
...rules
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function typeAwareConfig() {
|
|
166
|
+
return [
|
|
167
|
+
{
|
|
168
|
+
name: "nustack/type-aware",
|
|
169
|
+
files: ["**/*.{ts,tsx,mts,cts}", "**/*.vue"],
|
|
170
|
+
languageOptions: {
|
|
171
|
+
parserOptions: {
|
|
172
|
+
projectService: true
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
rules: {
|
|
176
|
+
// Surfaces usage of APIs marked `@deprecated` — a headline full-depth check.
|
|
177
|
+
"ts/no-deprecated": "warn"
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
];
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const GLOB_APP$1 = ["**/*.vue", "**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}"];
|
|
184
|
+
const IGNORE_NON_CLIENT$1 = [
|
|
185
|
+
"**/server/**",
|
|
186
|
+
"**/scripts/**",
|
|
187
|
+
"**/packages/**",
|
|
188
|
+
"**/*.{config,test,spec}.*",
|
|
189
|
+
"**/*.d.ts"
|
|
190
|
+
];
|
|
191
|
+
const recommendedRules$1 = vitePlugin.configs.recommended.rules ?? {};
|
|
192
|
+
function viteConfig(axes, opts = {}) {
|
|
193
|
+
const rules = resolveConcernRules(opts);
|
|
194
|
+
const configs = [];
|
|
195
|
+
if (variantAtLeast(axes.variant, "recommended")) {
|
|
196
|
+
configs.push({
|
|
197
|
+
name: "nustack/vite",
|
|
198
|
+
files: GLOB_APP$1,
|
|
199
|
+
ignores: IGNORE_NON_CLIENT$1,
|
|
200
|
+
plugins: { "@nustack/vite": vitePlugin },
|
|
201
|
+
rules: recommendedRules$1
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
if (Object.keys(rules).length) {
|
|
205
|
+
configs.push({
|
|
206
|
+
name: "nustack/vite/rules",
|
|
207
|
+
files: GLOB_APP$1,
|
|
208
|
+
ignores: IGNORE_NON_CLIENT$1,
|
|
209
|
+
rules
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
return configs;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function vueConfig(_ctx, _axes, opts = {}) {
|
|
216
|
+
const rules = resolveConcernRules(opts);
|
|
217
|
+
return [
|
|
218
|
+
{
|
|
219
|
+
name: "nustack/vue",
|
|
220
|
+
files: ["**/*.vue"],
|
|
221
|
+
rules: {
|
|
222
|
+
"vue/block-lang": ["error", {
|
|
223
|
+
script: { lang: "ts", allowNoLang: false }
|
|
224
|
+
}],
|
|
225
|
+
...variantAtLeast(_axes.variant, "recommended") ? {
|
|
226
|
+
"vue/define-emits-declaration": ["warn", "type-literal"],
|
|
227
|
+
"vue/define-props-destructuring": ["warn", {
|
|
228
|
+
destructure: "always"
|
|
229
|
+
}],
|
|
230
|
+
"vue/html-comment-content-newline": "warn",
|
|
231
|
+
"vue/html-comment-indent": "warn",
|
|
232
|
+
"vue/no-duplicate-class-names": "warn",
|
|
233
|
+
"vue/no-empty-component-block": "warn",
|
|
234
|
+
"vue/no-import-compiler-macros": "error"
|
|
235
|
+
} : {},
|
|
236
|
+
...rules
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
];
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const GLOB_APP = ["**/*.vue", "**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}"];
|
|
243
|
+
const IGNORE_NON_CLIENT = [
|
|
244
|
+
"**/server/**",
|
|
245
|
+
"**/scripts/**",
|
|
246
|
+
"**/packages/**",
|
|
247
|
+
"**/*.{config,test,spec}.*",
|
|
248
|
+
"**/*.d.ts"
|
|
249
|
+
];
|
|
250
|
+
const recommendedRules = vueUsePlugin.configs.recommended.rules ?? {};
|
|
251
|
+
function vueUseConfig(axes, opts = {}) {
|
|
252
|
+
const rules = resolveConcernRules(opts);
|
|
253
|
+
const configs = [];
|
|
254
|
+
if (variantAtLeast(axes.variant, "recommended")) {
|
|
255
|
+
configs.push({
|
|
256
|
+
name: "nustack/vueuse",
|
|
257
|
+
files: GLOB_APP,
|
|
258
|
+
ignores: IGNORE_NON_CLIENT,
|
|
259
|
+
plugins: { "@nustack/vueuse": vueUsePlugin },
|
|
260
|
+
rules: recommendedRules
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
if (Object.keys(rules).length) {
|
|
264
|
+
configs.push({
|
|
265
|
+
name: "nustack/vueuse/rules",
|
|
266
|
+
files: GLOB_APP,
|
|
267
|
+
ignores: IGNORE_NON_CLIENT,
|
|
268
|
+
rules
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
return configs;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const EMPTY_CONTEXT = {
|
|
275
|
+
modules: { nuxtUi: false, pinia: false, nuxtImage: false, nuxtContent: false },
|
|
276
|
+
tailwind: { detected: false, entryPoint: null },
|
|
277
|
+
autoImports: [],
|
|
278
|
+
components: []
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
function resolveDepth() {
|
|
282
|
+
return process.env.NUSTACK_LINT_DEPTH === "full" ? "full" : "quick";
|
|
283
|
+
}
|
|
284
|
+
function mergeContext(context) {
|
|
285
|
+
return defu(context, EMPTY_CONTEXT);
|
|
286
|
+
}
|
|
287
|
+
function isEnabled(toggle, gate) {
|
|
288
|
+
if (toggle === false)
|
|
289
|
+
return false;
|
|
290
|
+
if (toggle === true || typeof toggle === "object" && toggle !== null)
|
|
291
|
+
return true;
|
|
292
|
+
return gate;
|
|
293
|
+
}
|
|
294
|
+
function subOptions(toggle) {
|
|
295
|
+
return typeof toggle === "object" && toggle !== null ? toggle : {};
|
|
296
|
+
}
|
|
297
|
+
function resolveRules(options) {
|
|
298
|
+
return {
|
|
299
|
+
...options.overrides,
|
|
300
|
+
...options.rules
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
function applyNustackConfig(base, options = {}, ...userConfigs) {
|
|
304
|
+
const variant = options.variant ?? "recommended";
|
|
305
|
+
const depth = resolveDepth();
|
|
306
|
+
const ctx = mergeContext(options.context);
|
|
307
|
+
const axes = { variant};
|
|
308
|
+
const antfu = antfuBase(options.base, depth);
|
|
309
|
+
if (antfu)
|
|
310
|
+
base.prepend(antfu);
|
|
311
|
+
const configs = [];
|
|
312
|
+
if (isEnabled(options.nuxt, true))
|
|
313
|
+
configs.push(...nuxtConfig(ctx, axes, subOptions(options.nuxt)));
|
|
314
|
+
if (isEnabled(options.vue, true))
|
|
315
|
+
configs.push(...vueConfig(ctx, axes, subOptions(options.vue)));
|
|
316
|
+
if (isEnabled(options.vueUse, true))
|
|
317
|
+
configs.push(...vueUseConfig(axes, subOptions(options.vueUse)));
|
|
318
|
+
if (isEnabled(options.vite, true))
|
|
319
|
+
configs.push(...viteConfig(axes, subOptions(options.vite)));
|
|
320
|
+
if (isEnabled(options.tailwind, ctx.tailwind.detected))
|
|
321
|
+
configs.push(...tailwindConfig(ctx, axes, subOptions(options.tailwind)));
|
|
322
|
+
if (isEnabled(options.nuxtUi, ctx.modules.nuxtUi))
|
|
323
|
+
configs.push(...nuxtUiConfig(ctx, axes, subOptions(options.nuxtUi)));
|
|
324
|
+
if (depth === "full")
|
|
325
|
+
configs.push(...typeAwareConfig());
|
|
326
|
+
const rules = resolveRules(options);
|
|
327
|
+
if (Object.keys(rules).length) {
|
|
328
|
+
configs.push({
|
|
329
|
+
name: "nustack/rules",
|
|
330
|
+
rules
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
return base.append(...configs, ...userConfigs);
|
|
334
|
+
}
|
|
335
|
+
function nustack(options = {}, ...userConfigs) {
|
|
336
|
+
return applyNustackConfig(composer(), {
|
|
337
|
+
nuxt: false,
|
|
338
|
+
nuxtUi: false,
|
|
339
|
+
vite: false,
|
|
340
|
+
vueUse: false,
|
|
341
|
+
...options,
|
|
342
|
+
context: options.context ?? EMPTY_CONTEXT
|
|
343
|
+
}, ...userConfigs);
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
export { EMPTY_CONTEXT, applyNustackConfig, nustack as default, nustack };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
|
+
export { N as NustackContext } from './shared/lint.Cq_cclOu.mjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Options are intentionally empty: `@nustackjs/lint` is zero-config. It auto-detects
|
|
6
|
+
* the project's modules, auto-imports, components and Tailwind entry point, and
|
|
7
|
+
* activates the matching rule packs automatically.
|
|
8
|
+
*/
|
|
9
|
+
interface ModuleOptions {
|
|
10
|
+
}
|
|
11
|
+
declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
|
|
12
|
+
|
|
13
|
+
export { _default as default };
|
|
14
|
+
export type { ModuleOptions };
|
package/dist/module.json
ADDED
package/dist/module.mjs
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { resolveAlias, defineNuxtModule, installModule } from '@nuxt/kit';
|
|
2
|
+
import { defu } from 'defu';
|
|
3
|
+
import { existsSync } from 'node:fs';
|
|
4
|
+
import { mkdir, writeFile, readFile } from 'node:fs/promises';
|
|
5
|
+
import { join, dirname, relative } from 'node:path';
|
|
6
|
+
|
|
7
|
+
const TAILWIND_IMPORT_RE = /@import\s+["']tailwindcss["']/;
|
|
8
|
+
const MODULE_FLAGS = {
|
|
9
|
+
"@nuxt/ui": "nuxtUi",
|
|
10
|
+
"@nuxt/ui-pro": "nuxtUi",
|
|
11
|
+
"@pinia/nuxt": "pinia",
|
|
12
|
+
"@nuxt/image": "nuxtImage",
|
|
13
|
+
"@nuxt/content": "nuxtContent"
|
|
14
|
+
};
|
|
15
|
+
function setupNustackContext(nuxt) {
|
|
16
|
+
let unimport;
|
|
17
|
+
let nitroUnimport;
|
|
18
|
+
let components = [];
|
|
19
|
+
nuxt.hook("imports:context", (ctx) => {
|
|
20
|
+
unimport = ctx;
|
|
21
|
+
});
|
|
22
|
+
nuxt.hook("nitro:init", (nitro) => {
|
|
23
|
+
nitroUnimport = nitro.unimport;
|
|
24
|
+
});
|
|
25
|
+
nuxt.hook("components:extend", (list) => {
|
|
26
|
+
components = [...new Set(list.map((c) => c.pascalName))].sort();
|
|
27
|
+
});
|
|
28
|
+
async function collectAutoImports() {
|
|
29
|
+
const imports = [
|
|
30
|
+
...await unimport?.getImports() ?? [],
|
|
31
|
+
...await nitroUnimport?.getImports() ?? []
|
|
32
|
+
];
|
|
33
|
+
const names = imports.filter((i) => !i.type).map((i) => i.as || i.name).filter((name) => Boolean(name));
|
|
34
|
+
return [...new Set(names)].sort();
|
|
35
|
+
}
|
|
36
|
+
function detectModules() {
|
|
37
|
+
const installed = /* @__PURE__ */ new Set();
|
|
38
|
+
for (const m of nuxt.options._installedModules ?? []) {
|
|
39
|
+
const name = m.meta?.name;
|
|
40
|
+
if (name)
|
|
41
|
+
installed.add(name);
|
|
42
|
+
}
|
|
43
|
+
for (const m of nuxt.options.modules ?? []) {
|
|
44
|
+
if (typeof m === "string")
|
|
45
|
+
installed.add(m);
|
|
46
|
+
}
|
|
47
|
+
const modules = {
|
|
48
|
+
nuxtUi: false,
|
|
49
|
+
pinia: false,
|
|
50
|
+
nuxtImage: false,
|
|
51
|
+
nuxtContent: false
|
|
52
|
+
};
|
|
53
|
+
for (const [name, flag] of Object.entries(MODULE_FLAGS)) {
|
|
54
|
+
if (installed.has(name))
|
|
55
|
+
modules[flag] = true;
|
|
56
|
+
}
|
|
57
|
+
return modules;
|
|
58
|
+
}
|
|
59
|
+
async function detectTailwind() {
|
|
60
|
+
for (const entry of nuxt.options.css ?? []) {
|
|
61
|
+
if (typeof entry !== "string" || !entry.endsWith(".css"))
|
|
62
|
+
continue;
|
|
63
|
+
const abs = resolveAlias(entry, nuxt.options.alias);
|
|
64
|
+
if (!existsSync(abs))
|
|
65
|
+
continue;
|
|
66
|
+
try {
|
|
67
|
+
const source = await readFile(abs, "utf-8");
|
|
68
|
+
if (TAILWIND_IMPORT_RE.test(source)) {
|
|
69
|
+
return {
|
|
70
|
+
detected: true,
|
|
71
|
+
// better-tailwindcss resolves `entryPoint` relative to the lint cwd
|
|
72
|
+
// (the project root), so store it relative to rootDir.
|
|
73
|
+
entryPoint: relative(nuxt.options.rootDir, abs)
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
} catch {
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return { detected: false, entryPoint: null };
|
|
80
|
+
}
|
|
81
|
+
async function buildContext() {
|
|
82
|
+
return {
|
|
83
|
+
modules: detectModules(),
|
|
84
|
+
tailwind: await detectTailwind(),
|
|
85
|
+
autoImports: await collectAutoImports(),
|
|
86
|
+
components
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
async function writeContextFile() {
|
|
90
|
+
const context = await buildContext();
|
|
91
|
+
const file = join(nuxt.options.buildDir, "nustack-eslint.mjs");
|
|
92
|
+
await mkdir(dirname(file), { recursive: true });
|
|
93
|
+
await writeFile(file, generateCode(context), "utf-8");
|
|
94
|
+
}
|
|
95
|
+
nuxt.hook("builder:generateApp", writeContextFile);
|
|
96
|
+
}
|
|
97
|
+
function generateCode(context) {
|
|
98
|
+
return [
|
|
99
|
+
"// Generated by @nustackjs/lint \u2014 do not edit.",
|
|
100
|
+
"// Regenerated on every `nuxt prepare`.",
|
|
101
|
+
`import { applyNustackConfig as factory } from '@nustackjs/lint/config'`,
|
|
102
|
+
`import withNuxt from './eslint.config.mjs'`,
|
|
103
|
+
"",
|
|
104
|
+
`export const nustackContext = ${JSON.stringify(context, null, 2)}`,
|
|
105
|
+
"",
|
|
106
|
+
"/**",
|
|
107
|
+
" * Project-aware ESLint config, with `withNuxt()` already wired in. Pass one",
|
|
108
|
+
" * options object, then optional flat config objects. Returns the composer, so",
|
|
109
|
+
" * you can chain `.append()` / `.override()` / `.remove()` as an escape hatch.",
|
|
110
|
+
" */",
|
|
111
|
+
"export function nustack(options = {}, ...configs) {",
|
|
112
|
+
" return factory(withNuxt(), { ...options, context: nustackContext }, ...configs)",
|
|
113
|
+
"}",
|
|
114
|
+
"",
|
|
115
|
+
"export const nustackLint = nustack",
|
|
116
|
+
"",
|
|
117
|
+
"// Default export = the ready-to-use config, so `export { default } from` works.",
|
|
118
|
+
"export default nustack()",
|
|
119
|
+
""
|
|
120
|
+
].join("\n");
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function hasModule(nuxt, name) {
|
|
124
|
+
return nuxt.options.modules.some((m) => (Array.isArray(m) ? m[0] : m) === name);
|
|
125
|
+
}
|
|
126
|
+
const module$1 = defineNuxtModule({
|
|
127
|
+
meta: {
|
|
128
|
+
name: "@nustackjs/lint"
|
|
129
|
+
},
|
|
130
|
+
defaults: {},
|
|
131
|
+
async setup(_options, nuxt) {
|
|
132
|
+
nuxt.options.eslint = defu(nuxt.options.eslint, { config: {} });
|
|
133
|
+
nuxt.options.eslint.config.standalone = false;
|
|
134
|
+
if (!hasModule(nuxt, "@nuxt/eslint"))
|
|
135
|
+
await installModule("@nuxt/eslint");
|
|
136
|
+
setupNustackContext(nuxt);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
export { module$1 as default };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shape of the project context that the Nuxt module resolves at `nuxt prepare`
|
|
3
|
+
* time and serializes into `.nuxt/nustack-eslint.mjs`. The ESLint config factory
|
|
4
|
+
* (`nustack`) reads this back to decide which rule packs to activate, so the
|
|
5
|
+
* lint config always matches what the project actually uses — zero config.
|
|
6
|
+
*/
|
|
7
|
+
interface NustackContext {
|
|
8
|
+
/** Detected Nuxt modules that have a dedicated rule pack. */
|
|
9
|
+
modules: {
|
|
10
|
+
/** `@nuxt/ui` — activates the Tailwind + Nuxt UI pack. */
|
|
11
|
+
nuxtUi: boolean;
|
|
12
|
+
/** `@pinia/nuxt` — reserved for a future pack. */
|
|
13
|
+
pinia: boolean;
|
|
14
|
+
/** `@nuxt/image` — contributes auto-imported components. */
|
|
15
|
+
nuxtImage: boolean;
|
|
16
|
+
/** `@nuxt/content` — reserved for a future pack. */
|
|
17
|
+
nuxtContent: boolean;
|
|
18
|
+
};
|
|
19
|
+
tailwind: {
|
|
20
|
+
/** Whether a Tailwind v4 CSS entry point was found. */
|
|
21
|
+
detected: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Path to the CSS file that imports Tailwind (`@import "tailwindcss"`),
|
|
24
|
+
* relative to the project root. `null` when not detected.
|
|
25
|
+
*/
|
|
26
|
+
entryPoint: string | null;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Every identifier Nuxt makes globally available without an explicit import,
|
|
30
|
+
* pulled from the resolved unimport registry (Vue reactivity, Nuxt
|
|
31
|
+
* composables, project `composables/` + `utils/`, module-provided helpers).
|
|
32
|
+
*/
|
|
33
|
+
autoImports: string[];
|
|
34
|
+
/** Globally registered component names (Nuxt built-ins + project + modules). */
|
|
35
|
+
components: string[];
|
|
36
|
+
}
|
|
37
|
+
/** A safe, empty context used as a fallback when the generated file is absent. */
|
|
38
|
+
declare const EMPTY_CONTEXT: NustackContext;
|
|
39
|
+
|
|
40
|
+
export { EMPTY_CONTEXT as E };
|
|
41
|
+
export type { NustackContext as N };
|
package/dist/types.d.mts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nustackjs/lint",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.0.1",
|
|
5
|
+
"description": "Zero-config, project-aware ESLint (oxlint-ready) for Nuxt — plug it in and catch Nuxt bugs, not just style.",
|
|
6
|
+
"author": "Norbiros <me@norbiros.dev>",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"homepage": "https://github.com/Zerya-Dev/nustack/tree/master/modules/lint#readme",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://github.com/Zerya-Dev/nustack.git",
|
|
12
|
+
"directory": "modules/lint"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"eslint",
|
|
16
|
+
"oxlint",
|
|
17
|
+
"nuxt",
|
|
18
|
+
"nustack",
|
|
19
|
+
"lint",
|
|
20
|
+
"vue"
|
|
21
|
+
],
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public"
|
|
24
|
+
},
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"types": "./dist/module.d.mts",
|
|
28
|
+
"import": "./dist/module.mjs"
|
|
29
|
+
},
|
|
30
|
+
"./config": {
|
|
31
|
+
"types": "./dist/index.d.mts",
|
|
32
|
+
"import": "./dist/index.mjs"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"main": "./dist/module.mjs",
|
|
36
|
+
"typesVersions": {
|
|
37
|
+
"*": {
|
|
38
|
+
".": [
|
|
39
|
+
"./dist/module.d.mts"
|
|
40
|
+
],
|
|
41
|
+
"config": [
|
|
42
|
+
"./dist/index.d.mts"
|
|
43
|
+
]
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"files": [
|
|
47
|
+
"LICENSE",
|
|
48
|
+
"dist"
|
|
49
|
+
],
|
|
50
|
+
"workspaces": [
|
|
51
|
+
"playground"
|
|
52
|
+
],
|
|
53
|
+
"peerDependencies": {
|
|
54
|
+
"eslint": ">=9.0.0",
|
|
55
|
+
"nuxt": ">=3.0.0"
|
|
56
|
+
},
|
|
57
|
+
"dependencies": {
|
|
58
|
+
"@antfu/eslint-config": "^9.0.0",
|
|
59
|
+
"@nustackjs/lint-plugin-nuxt": "^0.0.1",
|
|
60
|
+
"@nustackjs/lint-plugin-nuxt-ecosystem": "^0.0.1",
|
|
61
|
+
"@nustackjs/lint-plugin-vite": "^0.0.1",
|
|
62
|
+
"@nustackjs/lint-plugin-vueuse": "^0.0.1",
|
|
63
|
+
"@nuxt/eslint": "^1.15.2",
|
|
64
|
+
"@nuxt/kit": "^4.4.7",
|
|
65
|
+
"@typescript-eslint/utils": "^8.60.1",
|
|
66
|
+
"defu": "^6.1.7",
|
|
67
|
+
"eslint-flat-config-utils": "^3.2.0",
|
|
68
|
+
"eslint-plugin-better-tailwindcss": "^3.7.10",
|
|
69
|
+
"eslint-plugin-vue": "^10.5.0",
|
|
70
|
+
"unimport": "^6.3.0",
|
|
71
|
+
"vue-eslint-parser": "^10.4.1"
|
|
72
|
+
},
|
|
73
|
+
"devDependencies": {
|
|
74
|
+
"@eslint/config-inspector": "^1.5.0",
|
|
75
|
+
"@nuxt/devtools": "^3.2.4",
|
|
76
|
+
"@nuxt/eslint-config": "^1.15.2",
|
|
77
|
+
"@nuxt/module-builder": "^1.0.2",
|
|
78
|
+
"@nuxt/schema": "^4.4.7",
|
|
79
|
+
"@nuxt/test-utils": "^4.0.3",
|
|
80
|
+
"@types/node": "latest",
|
|
81
|
+
"@typescript-eslint/parser": "^8.60.1",
|
|
82
|
+
"@typescript-eslint/rule-tester": "^8.60.1",
|
|
83
|
+
"changelogen": "^0.6.2",
|
|
84
|
+
"eslint": "^10.4.1",
|
|
85
|
+
"nuxt": "^4.4.7",
|
|
86
|
+
"tailwindcss": "^4.0.0",
|
|
87
|
+
"typescript": "~6.0.3",
|
|
88
|
+
"unbuild": "^3.6.1",
|
|
89
|
+
"vitest": "^4.1.8",
|
|
90
|
+
"vue-tsc": "^3.3.3"
|
|
91
|
+
},
|
|
92
|
+
"scripts": {
|
|
93
|
+
"dev": "npm run dev:prepare && nuxt dev playground",
|
|
94
|
+
"dev:build": "nuxt build playground",
|
|
95
|
+
"dev:prepare": "pnpm --filter '@nustackjs/lint-plugin-*' build && nuxt-module-build build && nuxt-module-build prepare && nuxt prepare playground",
|
|
96
|
+
"dev:inspector": "npx @eslint/config-inspector --config eslint.config.ts",
|
|
97
|
+
"build:inspector": "npx @eslint/config-inspector build --config eslint.config.ts --out-dir ./.eslint-config-inspector",
|
|
98
|
+
"lint": "eslint .",
|
|
99
|
+
"test": "pnpm --filter '@nustackjs/lint-plugin-*' build && vitest run",
|
|
100
|
+
"test:watch": "vitest watch",
|
|
101
|
+
"test:types": "vue-tsc --noEmit && cd playground && vue-tsc --noEmit"
|
|
102
|
+
}
|
|
103
|
+
}
|