@heyq02/rslint-config 0.0.0-stage → 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ari <mail@heyq02.cn>
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 CHANGED
@@ -1,3 +1,94 @@
1
- # Temporary Holding Version
1
+ # @heyq02/rslint-config
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [![npm version][npm-version-src]][npm-version-href]
4
+ [![Unit Test][unit-test-src]][unit-test-href]
5
+
6
+ Opinionated [Rslint](https://rslint.rs/) flat-config preset, structured like [`@sxzz/eslint-config`](https://github.com/sxzz/eslint-config) and built on `@rslint/core` native presets.
7
+
8
+ ## Features
9
+
10
+ - One-line config via `heyq02()`
11
+ - JavaScript + TypeScript presets (`recommended` / `strict` / type-checked / stylistic)
12
+ - Built-in Unicorn, import, Node, and Promise rules
13
+ - Optional React, React Hooks, JSX a11y, Jest, and Rstest (auto-detected)
14
+ - Ignores common build artifacts (`dist`, `coverage`, lockfiles, …)
15
+ - Special-case overrides for CLIs, tests, config files, and components
16
+
17
+ > Rslint only lints JS/TS script extensions (`.js` / `.ts` / `.jsx` / `.tsx` / …). Vue, JSON, YAML, and Markdown are out of scope.
18
+
19
+ ## Install
20
+
21
+ ```bash
22
+ pnpm add -D @heyq02/rslint-config @rslint/core
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ ```ts
28
+ // rslint.config.ts
29
+ import { heyq02 } from "@heyq02/rslint-config";
30
+
31
+ export default heyq02({
32
+ // Features auto-detect installed packages when omitted
33
+ typescript: { typeChecked: true, strict: true },
34
+ react: true,
35
+ jest: false,
36
+ });
37
+ ```
38
+
39
+ Append project overrides as extra arguments:
40
+
41
+ ```ts
42
+ export default heyq02(
43
+ { typescript: true },
44
+ {
45
+ rules: {
46
+ "no-console": "off",
47
+ },
48
+ },
49
+ );
50
+ ```
51
+
52
+ ### Options
53
+
54
+ | Option | Default | Description |
55
+ | ------------ | ------------------- | ----------------------------------------------------------------------- |
56
+ | `typescript` | auto (`typescript`) | `true` / `false` / `{ typeChecked, strict, stylistic, projectService }` |
57
+ | `javascript` | `true` | JavaScript recommended + opinionated rules |
58
+ | `unicorn` | `true` | Unicorn recommended + overrides |
59
+ | `imports` | `true` | Import recommended + no-default-export, etc. |
60
+ | `node` | `true` (`module`) | `true` / `'module'` / `'script'` / `'mixed'` |
61
+ | `promise` | `true` | Promise recommended (relaxed for async/await) |
62
+ | `react` | auto (`react`) | React + React Hooks (+ jsx-a11y) |
63
+ | `jsxA11y` | with react | JSX accessibility rules |
64
+ | `jest` | auto (`jest`) | Jest recommended + style |
65
+ | `rstest` | auto (`rstest`) | Rstest recommended |
66
+ | `ignores` | `[]` | Extra global ignore patterns |
67
+
68
+ ### Presets
69
+
70
+ ```ts
71
+ import { presetJavaScript, presetBasic, presetAll } from "@heyq02/rslint-config";
72
+
73
+ export default presetBasic();
74
+ ```
75
+
76
+ ## Development
77
+
78
+ ```bash
79
+ vp install
80
+ vp pack
81
+ vp check
82
+ pnpm release # bumpp → tag v* → GitHub Release + npm publish
83
+ ```
84
+
85
+ ## License
86
+
87
+ [MIT](./LICENSE) © Ari
88
+
89
+ <!-- Badges -->
90
+
91
+ [npm-version-src]: https://img.shields.io/npm/v/@heyq02/rslint-config.svg
92
+ [npm-version-href]: https://npmjs.com/package/@heyq02/rslint-config
93
+ [unit-test-src]: https://github.com/heyq02/rslint-config/actions/workflows/unit-test.yml/badge.svg
94
+ [unit-test-href]: https://github.com/heyq02/rslint-config/actions/workflows/unit-test.yml
@@ -0,0 +1,183 @@
1
+ import { RslintConfig, RslintConfigEntry } from "@rslint/core";
2
+ //#region src/types.d.ts
3
+ export type TypedFlatConfig = RslintConfigEntry;
4
+ export type ResolvableConfig = TypedFlatConfig | TypedFlatConfig[] | RslintConfig | false | null | undefined;
5
+ export type TypeScriptPreset = "recommended" | "recommendedTypeChecked" | "strict" | "strictTypeChecked";
6
+ export interface TypeScriptOptions {
7
+ /**
8
+ * Enable type-aware TypeScript rules.
9
+ * When true, uses `*TypeChecked` presets and enables `projectService` by default.
10
+ * @default false
11
+ */
12
+ typeChecked?: boolean;
13
+ /**
14
+ * Use TypeScript strict presets instead of recommended.
15
+ * @default false
16
+ */
17
+ strict?: boolean;
18
+ /**
19
+ * Layer TypeScript stylistic rules after the baseline.
20
+ * @default true
21
+ */
22
+ stylistic?: boolean;
23
+ /**
24
+ * Enable automatic TypeScript project discovery for type-aware rules.
25
+ * Defaults to `true` when `typeChecked` is enabled.
26
+ */
27
+ projectService?: boolean;
28
+ }
29
+ export type NodePreset = boolean | "module" | "script" | "mixed";
30
+ export interface Options {
31
+ /**
32
+ * Global ignore patterns. Merged with the built-in excludes.
33
+ */
34
+ ignores?: string[];
35
+ /**
36
+ * JavaScript support. @default true
37
+ */
38
+ javascript?: boolean;
39
+ /**
40
+ * TypeScript support. Auto-enabled when `typescript` is installed.
41
+ * Pass an object for type-checked / strict / stylistic controls.
42
+ * @default auto
43
+ */
44
+ typescript?: boolean | TypeScriptOptions;
45
+ /**
46
+ * Unicorn rules. @default true
47
+ */
48
+ unicorn?: boolean;
49
+ /**
50
+ * Import rules. @default true
51
+ */
52
+ imports?: boolean;
53
+ /**
54
+ * Node.js rules.
55
+ * - `true` / `'module'` — ESM-oriented Node rules (default)
56
+ * - `'script'` — CommonJS-oriented
57
+ * - `'mixed'` — upstream mixed recommended preset
58
+ * @default true
59
+ */
60
+ node?: NodePreset;
61
+ /**
62
+ * Promise rules. @default true
63
+ */
64
+ promise?: boolean;
65
+ /**
66
+ * React + React Hooks. Auto-enabled when React is detected.
67
+ * @default auto
68
+ */
69
+ react?: boolean;
70
+ /**
71
+ * JSX accessibility rules. Auto-enabled with React when unset.
72
+ * @default auto with react
73
+ */
74
+ jsxA11y?: boolean;
75
+ /**
76
+ * Jest rules. Auto-enabled when `jest` is detected.
77
+ * @default auto
78
+ */
79
+ jest?: boolean;
80
+ /**
81
+ * Rstest rules. Auto-enabled when `rstest` / `@rstest/core` is detected.
82
+ * @default auto
83
+ */
84
+ rstest?: boolean;
85
+ }
86
+ //#endregion
87
+ //#region src/configs/ignores.d.ts
88
+ export declare const ignores: (extra?: string[]) => TypedFlatConfig[];
89
+ //#endregion
90
+ //#region src/configs/imports.d.ts
91
+ export declare const imports: (options?: {
92
+ typescript?: boolean;
93
+ }) => TypedFlatConfig[];
94
+ //#endregion
95
+ //#region src/configs/javascript.d.ts
96
+ export declare const restrictedSyntaxJs: string[];
97
+ export declare const javascript: () => TypedFlatConfig[];
98
+ //#endregion
99
+ //#region src/configs/jest.d.ts
100
+ export declare const jest: () => TypedFlatConfig[];
101
+ //#endregion
102
+ //#region src/configs/node.d.ts
103
+ export declare const node: (preset?: Exclude<NodePreset, false>) => TypedFlatConfig[];
104
+ //#endregion
105
+ //#region src/configs/promise.d.ts
106
+ export declare const promise: () => TypedFlatConfig[];
107
+ //#endregion
108
+ //#region src/configs/react.d.ts
109
+ export declare const jsxA11y: () => TypedFlatConfig[];
110
+ export declare const react: (options?: {
111
+ jsxA11y?: boolean;
112
+ }) => TypedFlatConfig[];
113
+ //#endregion
114
+ //#region src/configs/rstest.d.ts
115
+ export declare const rstest: () => TypedFlatConfig[];
116
+ //#endregion
117
+ //#region src/configs/special-cases.d.ts
118
+ export declare const specialCases: () => TypedFlatConfig[];
119
+ //#endregion
120
+ //#region src/configs/typescript.d.ts
121
+ export declare const typescript: (options?: true | TypeScriptOptions) => TypedFlatConfig[];
122
+ //#endregion
123
+ //#region src/configs/unicorn.d.ts
124
+ export declare const unicorn: () => TypedFlatConfig[];
125
+ //#endregion
126
+ //#region src/env.d.ts
127
+ export declare const hasTypeScript: () => boolean;
128
+ export declare const hasReact: () => boolean;
129
+ export declare const hasJest: () => boolean;
130
+ export declare const hasRstest: () => boolean;
131
+ export declare function isInEditorEnv(): boolean;
132
+ export declare function isInGitHooksOrLintStaged(): boolean;
133
+ //#endregion
134
+ //#region src/globs.d.ts
135
+ export declare const GLOB_SRC_EXT = "?([cm])[jt]s?(x)";
136
+ export declare const GLOB_SRC = "**/*.?([cm])[jt]s?(x)";
137
+ export declare const GLOB_JS = "**/*.?([cm])js";
138
+ export declare const GLOB_JSX = "**/*.jsx";
139
+ export declare const GLOB_TS = "**/*.?([cm])ts";
140
+ export declare const GLOB_TSX = "**/*.tsx";
141
+ export declare const GLOB_TESTS: readonly ["**/__tests__/**/*.?([cm])[jt]s?(x)", "**/*.spec.?([cm])[jt]s?(x)", "**/*.test.?([cm])[jt]s?(x)", "**/*.bench.?([cm])[jt]s?(x)", "**/*.benchmark.?([cm])[jt]s?(x)"];
142
+ export declare const GLOB_NODE_MODULES: "**/node_modules";
143
+ export declare const GLOB_DIST: "**/dist";
144
+ export declare const GLOB_LOCKFILE: string[];
145
+ export declare const GLOB_EXCLUDE: string[];
146
+ //#endregion
147
+ //#region src/presets.d.ts
148
+ /** Ignore common files and include JavaScript support. */
149
+ export declare const presetJavaScript: () => TypedFlatConfig[];
150
+ /** Includes `presetJavaScript` and TypeScript support. */
151
+ export declare const presetBasic: (typescriptOptions?: true | TypeScriptOptions) => TypedFlatConfig[];
152
+ /**
153
+ * Full opinionated preset:
154
+ * - JS + TS
155
+ * - Unicorn / import / node / promise
156
+ * - React / Jest / Rstest when detected
157
+ * - Special-case overrides
158
+ */
159
+ export declare const presetAll: () => TypedFlatConfig[];
160
+ /**
161
+ * `@heyq02`'s opinionated Rslint config preset.
162
+ *
163
+ * @example
164
+ * ```ts
165
+ * // rslint.config.ts
166
+ * import { heyq02 } from '@heyq02/rslint-config'
167
+ *
168
+ * export default heyq02({
169
+ * typescript: { typeChecked: true, strict: true },
170
+ * react: true,
171
+ * })
172
+ * ```
173
+ */
174
+ export declare function heyq02(options?: Options, ...userConfigs: ResolvableConfig[]): TypedFlatConfig[];
175
+ /** Alias of {@link heyq02}. */
176
+ export declare const rslintConfig: typeof heyq02;
177
+ //#endregion
178
+ //#region src/utils.d.ts
179
+ /** Flatten nested config values into a single entry list. */
180
+ export declare function flattenConfigs(...configs: ResolvableConfig[]): TypedFlatConfig[];
181
+ export declare function toArray<T>(value: T | T[]): T[];
182
+ //#endregion
183
+ export type { RslintConfig, RslintConfigEntry };
package/dist/index.mjs ADDED
@@ -0,0 +1,522 @@
1
+ import { createRequire } from "node:module";
2
+ import { globalIgnores, globals, importPlugin, jestPlugin, js, jsxA11yPlugin, nodePlugin, promisePlugin, reactHooksPlugin, reactPlugin, rstestPlugin, ts, unicornPlugin } from "@rslint/core";
3
+ import process from "node:process";
4
+ //#region src/globs.ts
5
+ const GLOB_SRC_EXT = "?([cm])[jt]s?(x)";
6
+ const GLOB_SRC = `**/*.${GLOB_SRC_EXT}`;
7
+ const GLOB_JS = "**/*.?([cm])js";
8
+ const GLOB_JSX = "**/*.jsx";
9
+ const GLOB_TS = "**/*.?([cm])ts";
10
+ const GLOB_TSX = "**/*.tsx";
11
+ const GLOB_TESTS = [
12
+ `**/__tests__/**/*.${GLOB_SRC_EXT}`,
13
+ `**/*.spec.${GLOB_SRC_EXT}`,
14
+ `**/*.test.${GLOB_SRC_EXT}`,
15
+ `**/*.bench.${GLOB_SRC_EXT}`,
16
+ `**/*.benchmark.${GLOB_SRC_EXT}`
17
+ ];
18
+ const GLOB_NODE_MODULES = "**/node_modules";
19
+ const GLOB_DIST = "**/dist";
20
+ const GLOB_LOCKFILE = [
21
+ "**/package-lock.json",
22
+ "**/yarn.lock",
23
+ "**/pnpm-lock.yaml",
24
+ "**/bun.lock",
25
+ "**/bun.lockb"
26
+ ];
27
+ const GLOB_EXCLUDE = [
28
+ GLOB_NODE_MODULES,
29
+ GLOB_DIST,
30
+ ...GLOB_LOCKFILE,
31
+ "**/coverage",
32
+ "**/temp",
33
+ "**/tmp",
34
+ "**/fixtures",
35
+ "**/output",
36
+ "**/.vitepress/cache",
37
+ "**/.nuxt",
38
+ "**/.next",
39
+ "**/.vercel",
40
+ "**/.changeset",
41
+ "**/.idea",
42
+ "**/.output",
43
+ "**/.vite-inspect",
44
+ "**/.nitro",
45
+ "**/.turbo",
46
+ "**/CHANGELOG*.md",
47
+ "**/*.min.*",
48
+ "**/LICENSE*",
49
+ "**/__snapshots__",
50
+ "**/auto-import?(s).d.ts",
51
+ "**/components.d.ts"
52
+ ];
53
+ //#endregion
54
+ //#region src/configs/ignores.ts
55
+ const ignores = (extra = []) => [{
56
+ ...globalIgnores([...GLOB_EXCLUDE, ...extra]),
57
+ name: "heyq02/global-ignores"
58
+ }];
59
+ //#endregion
60
+ //#region src/configs/imports.ts
61
+ const imports = (options = {}) => [{
62
+ ...importPlugin.configs.recommended,
63
+ files: [GLOB_SRC],
64
+ name: "heyq02/imports/recommended"
65
+ }, {
66
+ files: [GLOB_SRC],
67
+ name: "heyq02/imports",
68
+ rules: {
69
+ "import/first": "error",
70
+ "import/no-default-export": "error",
71
+ "import/no-duplicates": ["error", { "prefer-inline": true }],
72
+ "import/no-mutable-exports": "error",
73
+ "import/no-named-default": "error",
74
+ ...options.typescript ? {
75
+ "import/default": "off",
76
+ "import/named": "off",
77
+ "import/namespace": "off",
78
+ "import/no-unresolved": "off"
79
+ } : {}
80
+ }
81
+ }];
82
+ //#endregion
83
+ //#region src/configs/javascript.ts
84
+ const restrictedSyntaxJs = ["ForInStatement", "LabeledStatement"];
85
+ const javascript = () => [{
86
+ ...js.configs.recommended,
87
+ name: "heyq02/js/recommended"
88
+ }, {
89
+ files: [GLOB_SRC],
90
+ languageOptions: {
91
+ ecmaVersion: "latest",
92
+ globals: {
93
+ ...globals.browser,
94
+ ...globals.es2026,
95
+ ...globals.nodeBuiltin
96
+ },
97
+ sourceType: "module"
98
+ },
99
+ name: "heyq02/js",
100
+ rules: {
101
+ "array-callback-return": "error",
102
+ "block-scoped-var": "error",
103
+ "dot-notation": "warn",
104
+ eqeqeq: ["error", "smart"],
105
+ "no-alert": "warn",
106
+ "no-console": ["warn", { allow: [
107
+ "warn",
108
+ "error",
109
+ "info",
110
+ "clear"
111
+ ] }],
112
+ "no-debugger": "warn",
113
+ "no-duplicate-imports": "error",
114
+ "no-empty": ["error", { allowEmptyCatch: true }],
115
+ "no-fallthrough": ["warn", { commentPattern: String.raw`break[\s\w]*omitted` }],
116
+ "no-inner-declarations": "error",
117
+ "no-lonely-if": "error",
118
+ "no-multi-str": "error",
119
+ "no-restricted-syntax": ["error", ...restrictedSyntaxJs],
120
+ "no-unused-expressions": ["error", {
121
+ allowShortCircuit: true,
122
+ allowTaggedTemplates: true,
123
+ allowTernary: true
124
+ }],
125
+ "no-unused-vars": ["error", {
126
+ args: "after-used",
127
+ argsIgnorePattern: "^_",
128
+ ignoreRestSiblings: true,
129
+ varsIgnorePattern: "^_"
130
+ }],
131
+ "no-useless-call": "error",
132
+ "no-useless-computed-key": "error",
133
+ "no-useless-constructor": "error",
134
+ "no-useless-rename": "error",
135
+ "no-var": "error",
136
+ "no-void": "error",
137
+ "object-shorthand": [
138
+ "error",
139
+ "always",
140
+ {
141
+ avoidQuotes: true,
142
+ ignoreConstructors: false
143
+ }
144
+ ],
145
+ "one-var": ["error", { initialized: "never" }],
146
+ "prefer-arrow-callback": ["error", {
147
+ allowNamedFunctions: false,
148
+ allowUnboundThis: true
149
+ }],
150
+ "prefer-const": ["warn", {
151
+ destructuring: "all",
152
+ ignoreReadBeforeAssign: true
153
+ }],
154
+ "prefer-exponentiation-operator": "error",
155
+ "prefer-regex-literals": ["error", { disallowRedundantWrapping: true }],
156
+ "prefer-rest-params": "error",
157
+ "prefer-spread": "error",
158
+ "prefer-template": "error",
159
+ "require-await": "error",
160
+ "unicode-bom": ["error", "never"],
161
+ "use-isnan": ["error", {
162
+ enforceForIndexOf: true,
163
+ enforceForSwitchCase: true
164
+ }],
165
+ "valid-typeof": ["error", { requireStringLiterals: true }],
166
+ "vars-on-top": "error"
167
+ }
168
+ }];
169
+ //#endregion
170
+ //#region src/configs/jest.ts
171
+ const jest = () => [{
172
+ ...jestPlugin.configs.recommended,
173
+ files: [...GLOB_TESTS],
174
+ name: "heyq02/jest/recommended"
175
+ }, {
176
+ ...jestPlugin.configs.style,
177
+ files: [...GLOB_TESTS],
178
+ name: "heyq02/jest/style"
179
+ }];
180
+ //#endregion
181
+ //#region src/configs/node.ts
182
+ const node = (preset = "module") => {
183
+ if (preset === "mixed") return nodePlugin.configs.recommended.map((entry, index) => ({
184
+ ...entry,
185
+ name: entry.name ?? `heyq02/node/recommended-${index}`
186
+ }));
187
+ if (preset === "script") return [{
188
+ ...nodePlugin.configs.recommendedScript,
189
+ files: [GLOB_SRC],
190
+ name: "heyq02/node/recommended-script"
191
+ }];
192
+ return [{
193
+ ...nodePlugin.configs.recommendedModule,
194
+ files: [GLOB_SRC],
195
+ name: "heyq02/node/recommended-module"
196
+ }, {
197
+ files: [GLOB_SRC],
198
+ name: "heyq02/node",
199
+ plugins: ["node"],
200
+ rules: {
201
+ "node/handle-callback-err": ["error", "^(err|error)$"],
202
+ "node/no-deprecated-api": "error",
203
+ "node/no-exports-assign": "error",
204
+ "node/no-new-require": "error",
205
+ "node/no-path-concat": "error",
206
+ "node/prefer-global/buffer": ["error", "never"],
207
+ "node/prefer-global/process": ["error", "never"],
208
+ "node/process-exit-as-throw": "error"
209
+ }
210
+ }];
211
+ };
212
+ //#endregion
213
+ //#region src/configs/promise.ts
214
+ const promise = () => [{
215
+ ...promisePlugin.configs.recommended,
216
+ files: [GLOB_SRC],
217
+ name: "heyq02/promise/recommended"
218
+ }, {
219
+ files: [GLOB_SRC],
220
+ name: "heyq02/promise",
221
+ rules: {
222
+ "promise/always-return": "off",
223
+ "promise/catch-or-return": "off",
224
+ "promise/no-nesting": "off"
225
+ }
226
+ }];
227
+ //#endregion
228
+ //#region src/configs/react.ts
229
+ const reactFiles = [
230
+ GLOB_JSX,
231
+ GLOB_TSX,
232
+ "**/*.{jsx,tsx}"
233
+ ];
234
+ const jsxA11y = () => [{
235
+ ...jsxA11yPlugin.configs.recommended,
236
+ files: reactFiles,
237
+ name: "heyq02/jsx-a11y/recommended"
238
+ }];
239
+ const react = (options = {}) => {
240
+ const configs = [{
241
+ ...reactPlugin.configs.recommended,
242
+ files: reactFiles,
243
+ name: "heyq02/react/recommended"
244
+ }, {
245
+ ...reactHooksPlugin.configs.recommended,
246
+ files: reactFiles,
247
+ name: "heyq02/react-hooks/recommended"
248
+ }];
249
+ if (options.jsxA11y !== false) configs.push(...jsxA11y());
250
+ return configs;
251
+ };
252
+ //#endregion
253
+ //#region src/configs/rstest.ts
254
+ const rstest = () => [{
255
+ ...rstestPlugin.configs.recommended,
256
+ files: [...GLOB_TESTS],
257
+ name: "heyq02/rstest/recommended"
258
+ }];
259
+ //#endregion
260
+ //#region src/configs/special-cases.ts
261
+ const specialCases = () => [
262
+ {
263
+ files: [
264
+ "**/scripts/*",
265
+ "**/cli.*",
266
+ "**/bin/**"
267
+ ],
268
+ name: "heyq02/special/cli",
269
+ rules: { "no-console": "off" }
270
+ },
271
+ {
272
+ files: [`**/*.{test,spec}.${GLOB_SRC_EXT}`],
273
+ name: "heyq02/special/tests",
274
+ rules: {
275
+ "no-unused-expressions": "off",
276
+ "@typescript-eslint/no-unused-expressions": "off"
277
+ }
278
+ },
279
+ {
280
+ files: [
281
+ `**/*config*.${GLOB_SRC_EXT}`,
282
+ `**/{views,pages,routes,middleware,plugins,api,modules}/${GLOB_SRC}`,
283
+ `**/{index,vite,esbuild,rollup,rolldown,webpack,rspack,farm,unloader,nuxt,rslint}.${GLOB_SRC_EXT}`,
284
+ "**/*.d.ts",
285
+ "**/.prettierrc*"
286
+ ],
287
+ name: "heyq02/special/allow-default-export",
288
+ rules: { "import/no-default-export": "off" }
289
+ },
290
+ {
291
+ files: ["**/ISSUE_TEMPLATE/**"],
292
+ name: "heyq02/special/github",
293
+ rules: { "unicorn/filename-case": "off" }
294
+ },
295
+ {
296
+ files: [GLOB_JSX, GLOB_TSX],
297
+ name: "heyq02/special/components",
298
+ rules: {
299
+ "unicorn/no-anonymous-default-export": "off",
300
+ "import/no-default-export": "off"
301
+ }
302
+ }
303
+ ];
304
+ //#endregion
305
+ //#region src/configs/typescript.ts
306
+ function resolveTypeScriptOptions(options) {
307
+ if (options === true) return {
308
+ typeChecked: false,
309
+ strict: false,
310
+ stylistic: true
311
+ };
312
+ return {
313
+ typeChecked: options.typeChecked ?? false,
314
+ strict: options.strict ?? false,
315
+ stylistic: options.stylistic ?? true,
316
+ projectService: options.projectService
317
+ };
318
+ }
319
+ const typescript = (options = true) => {
320
+ const opts = resolveTypeScriptOptions(options);
321
+ const baseline = opts.strict && opts.typeChecked ? ts.configs.strictTypeChecked : opts.strict ? ts.configs.strict : opts.typeChecked ? ts.configs.recommendedTypeChecked : ts.configs.recommended;
322
+ const stylistic = opts.stylistic ? opts.typeChecked ? ts.configs.stylisticTypeChecked : ts.configs.stylistic : [];
323
+ const enableProjectService = opts.projectService ?? (opts.typeChecked ? true : void 0);
324
+ return [
325
+ ...baseline,
326
+ ...stylistic,
327
+ {
328
+ files: [GLOB_TS, GLOB_TSX],
329
+ name: "heyq02/typescript",
330
+ ...enableProjectService === void 0 ? {} : { languageOptions: { parserOptions: { projectService: enableProjectService } } },
331
+ rules: {
332
+ "@typescript-eslint/ban-ts-comment": "off",
333
+ "@typescript-eslint/consistent-type-assertions": ["error", {
334
+ assertionStyle: "as",
335
+ objectLiteralTypeAssertions: "allow-as-parameter"
336
+ }],
337
+ "@typescript-eslint/consistent-type-imports": ["error", {
338
+ disallowTypeAnnotations: false,
339
+ fixStyle: "inline-type-imports"
340
+ }],
341
+ "@typescript-eslint/method-signature-style": ["error", "property"],
342
+ "@typescript-eslint/no-empty-object-type": "off",
343
+ "@typescript-eslint/no-explicit-any": "off",
344
+ "@typescript-eslint/no-import-type-side-effects": "error",
345
+ "@typescript-eslint/no-non-null-assertion": "off",
346
+ "@typescript-eslint/no-redeclare": "error",
347
+ "@typescript-eslint/no-unused-expressions": ["error", {
348
+ allowShortCircuit: true,
349
+ allowTaggedTemplates: true,
350
+ allowTernary: true
351
+ }],
352
+ "@typescript-eslint/no-unused-vars": ["error", {
353
+ args: "after-used",
354
+ argsIgnorePattern: "^_",
355
+ ignoreRestSiblings: true,
356
+ varsIgnorePattern: "^_"
357
+ }],
358
+ "@typescript-eslint/no-useless-constructor": "error",
359
+ "@typescript-eslint/prefer-as-const": "warn",
360
+ "@typescript-eslint/prefer-literal-enum-member": ["error", { allowBitwiseExpressions: true }],
361
+ "no-restricted-syntax": [
362
+ "error",
363
+ ...restrictedSyntaxJs,
364
+ "TSEnumDeclaration[const=true]"
365
+ ],
366
+ "no-unused-vars": "off"
367
+ }
368
+ },
369
+ {
370
+ files: ["**/*.d.ts"],
371
+ name: "heyq02/typescript/dts-rules",
372
+ rules: {
373
+ "import/no-duplicates": "off",
374
+ "no-restricted-syntax": "off"
375
+ }
376
+ },
377
+ {
378
+ files: [GLOB_JS, "**/*.cjs"],
379
+ name: "heyq02/typescript/cjs-rules",
380
+ rules: { "@typescript-eslint/no-require-imports": "off" }
381
+ }
382
+ ];
383
+ };
384
+ //#endregion
385
+ //#region src/configs/unicorn.ts
386
+ const unicorn = () => [{
387
+ ...unicornPlugin.configs.recommended,
388
+ files: [GLOB_SRC],
389
+ name: "heyq02/unicorn/recommended"
390
+ }, {
391
+ files: [GLOB_SRC],
392
+ name: "heyq02/unicorn",
393
+ rules: {
394
+ "unicorn/filename-case": ["error", {
395
+ cases: {
396
+ kebabCase: true,
397
+ pascalCase: true
398
+ },
399
+ ignore: [
400
+ String.raw`^[A-Z]+\..*$`,
401
+ String.raw`^[a-z]{2}-[A-Z]{2}$`,
402
+ String.raw`import_map\.json`
403
+ ]
404
+ }],
405
+ "unicorn/no-null": "off",
406
+ "unicorn/no-this-assignment": "off",
407
+ "unicorn/prefer-ternary": "off",
408
+ "unicorn/no-process-exit": "off"
409
+ }
410
+ }];
411
+ //#endregion
412
+ //#region src/env.ts
413
+ const require = createRequire(import.meta.url);
414
+ const cwd = process.cwd();
415
+ function isPackageExists(name) {
416
+ try {
417
+ require.resolve(name, { paths: [cwd] });
418
+ return true;
419
+ } catch {
420
+ return false;
421
+ }
422
+ }
423
+ const hasTypeScript = () => isPackageExists("typescript");
424
+ const hasReact = () => isPackageExists("react") || isPackageExists("react-dom") || isPackageExists("next") || isPackageExists("preact") || isPackageExists("@remix-run/react");
425
+ const hasJest = () => isPackageExists("jest") || isPackageExists("@jest/globals");
426
+ const hasRstest = () => isPackageExists("rstest") || isPackageExists("@rstest/core") || isPackageExists("@rstest/adapter");
427
+ function isInEditorEnv() {
428
+ if (process.env.CI) return false;
429
+ if (isInGitHooksOrLintStaged()) return false;
430
+ return !!(process.env.VSCODE_PID || process.env.VSCODE_CWD || process.env.JETBRAINS_IDE || process.env.VIM || process.env.NVIM);
431
+ }
432
+ function isInGitHooksOrLintStaged() {
433
+ return !!(process.env.GIT_PARAMS || process.env.VSCODE_GIT_COMMAND || process.env.npm_lifecycle_script?.startsWith("lint-staged"));
434
+ }
435
+ //#endregion
436
+ //#region src/utils.ts
437
+ /** Flatten nested config values into a single entry list. */
438
+ function flattenConfigs(...configs) {
439
+ const result = [];
440
+ for (const config of configs) {
441
+ if (!config) continue;
442
+ if (Array.isArray(config)) {
443
+ result.push(...flattenConfigs(...config));
444
+ continue;
445
+ }
446
+ result.push(config);
447
+ }
448
+ return result;
449
+ }
450
+ function toArray(value) {
451
+ return Array.isArray(value) ? value : [value];
452
+ }
453
+ //#endregion
454
+ //#region src/presets.ts
455
+ /** Ignore common files and include JavaScript support. */
456
+ const presetJavaScript = () => [
457
+ ...ignores(),
458
+ ...javascript(),
459
+ ...imports({ typescript: false }),
460
+ ...unicorn(),
461
+ ...node(),
462
+ ...promise()
463
+ ];
464
+ /** Includes `presetJavaScript` and TypeScript support. */
465
+ const presetBasic = (typescriptOptions = true) => [
466
+ ...ignores(),
467
+ ...javascript(),
468
+ ...typescript(typescriptOptions),
469
+ ...imports({ typescript: true }),
470
+ ...unicorn(),
471
+ ...node(),
472
+ ...promise()
473
+ ];
474
+ /**
475
+ * Full opinionated preset:
476
+ * - JS + TS
477
+ * - Unicorn / import / node / promise
478
+ * - React / Jest / Rstest when detected
479
+ * - Special-case overrides
480
+ */
481
+ const presetAll = () => heyq02({
482
+ typescript: true,
483
+ react: true,
484
+ jest: true,
485
+ rstest: true,
486
+ jsxA11y: true
487
+ });
488
+ /**
489
+ * `@heyq02`'s opinionated Rslint config preset.
490
+ *
491
+ * @example
492
+ * ```ts
493
+ * // rslint.config.ts
494
+ * import { heyq02 } from '@heyq02/rslint-config'
495
+ *
496
+ * export default heyq02({
497
+ * typescript: { typeChecked: true, strict: true },
498
+ * react: true,
499
+ * })
500
+ * ```
501
+ */
502
+ function heyq02(options = {}, ...userConfigs) {
503
+ const { ignores: extraIgnores = [], javascript: enableJavaScript = true, typescript: enableTypeScript = hasTypeScript(), unicorn: enableUnicorn = true, imports: enableImports = true, node: enableNode = true, promise: enablePromise = true, react: enableReact = hasReact(), jsxA11y: enableJsxA11y, jest: enableJest = hasJest(), rstest: enableRstest = hasRstest() } = options;
504
+ const configs = [...ignores(extraIgnores)];
505
+ if (enableJavaScript) configs.push(...javascript());
506
+ if (enableTypeScript) configs.push(...typescript(enableTypeScript === true ? true : enableTypeScript));
507
+ if (enableImports) configs.push(...imports({ typescript: Boolean(enableTypeScript) }));
508
+ if (enableUnicorn) configs.push(...unicorn());
509
+ if (enableNode) configs.push(...node(enableNode === true ? "module" : enableNode));
510
+ if (enablePromise) configs.push(...promise());
511
+ if (enableReact) configs.push(...react({ jsxA11y: enableJsxA11y ?? true }));
512
+ else if (enableJsxA11y) configs.push(...jsxA11y());
513
+ if (enableJest) configs.push(...jest());
514
+ if (enableRstest) configs.push(...rstest());
515
+ configs.push(...specialCases());
516
+ configs.push(...flattenConfigs(...userConfigs));
517
+ return configs;
518
+ }
519
+ /** Alias of {@link heyq02}. */
520
+ const rslintConfig = heyq02;
521
+ //#endregion
522
+ export { GLOB_DIST, GLOB_EXCLUDE, GLOB_JS, GLOB_JSX, GLOB_LOCKFILE, GLOB_NODE_MODULES, GLOB_SRC, GLOB_SRC_EXT, GLOB_TESTS, GLOB_TS, GLOB_TSX, flattenConfigs, hasJest, hasReact, hasRstest, hasTypeScript, heyq02, ignores, imports, isInEditorEnv, isInGitHooksOrLintStaged, javascript, jest, jsxA11y, node, presetAll, presetBasic, presetJavaScript, promise, react, restrictedSyntaxJs, rslintConfig, rstest, specialCases, toArray, typescript, unicorn };
package/package.json CHANGED
@@ -1,6 +1,61 @@
1
1
  {
2
2
  "name": "@heyq02/rslint-config",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.0.0",
4
+ "description": "Opinionated Rslint flat-config preset.",
5
+ "keywords": [
6
+ "flat-config",
7
+ "lint",
8
+ "rslint",
9
+ "rslint-config",
10
+ "typescript"
11
+ ],
12
+ "homepage": "https://github.com/heyq02/rslint-config#readme",
13
+ "bugs": {
14
+ "url": "https://github.com/heyq02/rslint-config/issues"
15
+ },
16
+ "license": "MIT",
17
+ "author": "Ari <mail@heyq02.cn>",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/heyq02/rslint-config.git"
21
+ },
22
+ "files": [
23
+ "dist"
24
+ ],
25
+ "type": "module",
26
+ "exports": {
27
+ ".": "./dist/index.mjs",
28
+ "./package.json": "./package.json"
29
+ },
30
+ "publishConfig": {
31
+ "access": "public"
32
+ },
33
+ "devDependencies": {
34
+ "@rslint/core": "^0.9.4",
35
+ "@types/node": "^26.1.1",
36
+ "bumpp": "^11.1.0",
37
+ "typescript": "^7.0.2",
38
+ "vite-plus": "1.0.0"
39
+ },
40
+ "peerDependencies": {
41
+ "@rslint/core": "^0.9.0"
42
+ },
43
+ "devEngines": {
44
+ "packageManager": {
45
+ "name": "pnpm",
46
+ "version": "12.10.1",
47
+ "onFail": "download"
48
+ }
49
+ },
50
+ "scripts": {
51
+ "build": "vp pack",
52
+ "dev": "vp pack --watch",
53
+ "test": "echo Skip",
54
+ "check": "vp check",
55
+ "lint": "rslint .",
56
+ "lint:fix": "rslint --fix .",
57
+ "format": "vp fmt",
58
+ "typecheck": "vp check --no-fmt --no-lint",
59
+ "release": "bumpp"
60
+ }
6
61
  }