rhine-lint 1.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.
@@ -0,0 +1,375 @@
1
+ import path from 'node:path'
2
+ import { fileURLToPath } from 'node:url'
3
+
4
+ import nextPlugin from '@next/eslint-plugin-next'
5
+ import reactPlugin from 'eslint-plugin-react';
6
+ import reactHooksPlugin from 'eslint-plugin-react-hooks';
7
+ import { fixupConfigRules } from '@eslint/compat'
8
+
9
+ import css from '@eslint/css'
10
+
11
+ // ... skipped standard imports ...
12
+
13
+ // Removed Patch Mock - No longer needed as we don't use eslint-config-next
14
+
15
+ // ...
16
+
17
+
18
+ import { FlatCompat } from '@eslint/eslintrc'
19
+ import js from '@eslint/js'
20
+ import json from '@eslint/json'
21
+ import markdown from '@eslint/markdown'
22
+ import tsParser from '@typescript-eslint/parser'
23
+ import { defineConfig } from 'eslint/config'
24
+ import eslintConfigPrettier from 'eslint-config-prettier/flat'
25
+ import { importX } from 'eslint-plugin-import-x'
26
+ import unusedImports from 'eslint-plugin-unused-imports'
27
+ import globals from 'globals'
28
+ import tseslint from 'typescript-eslint'
29
+
30
+ const __filename = fileURLToPath(import.meta.url)
31
+ const __dirname = path.dirname(__filename)
32
+ const compat = new FlatCompat({
33
+ baseDirectory: __dirname,
34
+ })
35
+
36
+ const globalConfig = defineConfig([
37
+ {
38
+ ignores: ['bun.lock', '.rhine-lint-cache/**'],
39
+ },
40
+ ])
41
+
42
+ const jsFileExtensions = 'cjs,js,jsx,mjs,mjsx'
43
+ const tsFileExtensions = 'cts,ts,tsx,mts,mtsx'
44
+ const scriptFileExtensions = `${jsFileExtensions},${tsFileExtensions}`
45
+ const allJsFiles = [`**/*.{${jsFileExtensions}}`]
46
+ const allTsFiles = [`**/*.{${tsFileExtensions}}`]
47
+ const allScriptFiles = [`**/*.{${scriptFileExtensions}}`]
48
+
49
+ export default function createConfig(overrides = {}) {
50
+ const OPTIONS = {
51
+ ENABLE_SCRIPT: true, // Set to enable typescript javascript file features
52
+ ENABLE_TYPE_CHECKED: true, // Set to enable type features
53
+ ENABLE_PROJECT_BASE_TYPE_CHECKED: false, // Set to enable project-based type features
54
+ ENABLE_FRONTEND: true, // Set to enable JSX, React, Reacts Hooks, and other frontend features
55
+ ENABLE_NEXT: false, // Set to enable Next.js and other frontend features
56
+ ENABLE_MARKDOWN: true, // Set to enable markdown file features
57
+ ENABLE_JSON: true, // Set to enable json file features
58
+ ENABLE_STYLESHEET: true, // Set to enable CSS, SCSS, SASS and other stylesheet features
59
+ IGNORE_PRETTIER: true, // Set to disable all rules that are unnecessary or might conflict with Prettier
60
+ TSCONFIG_PATH: './tsconfig.app.json', // path to tsconfig file
61
+ ...overrides
62
+ }
63
+
64
+ const tsConfig = []
65
+ if (OPTIONS.ENABLE_SCRIPT && OPTIONS.ENABLE_TYPE_CHECKED) {
66
+ tsConfig.push(
67
+ ...(OPTIONS.ENABLE_PROJECT_BASE_TYPE_CHECKED
68
+ ? [
69
+ {
70
+ ignores: ['eslint.config.ts'],
71
+ },
72
+ {
73
+ files: allScriptFiles,
74
+ languageOptions: {
75
+ parserOptions: {
76
+ projectService: true,
77
+ tsconfigRootDir: __dirname,
78
+ },
79
+ },
80
+ },
81
+ {
82
+ ...tseslint.configs.strictTypeChecked[0],
83
+ files: allScriptFiles,
84
+ },
85
+ {
86
+ ...tseslint.configs.strictTypeChecked[1],
87
+ files: allTsFiles,
88
+ },
89
+ {
90
+ ...tseslint.configs.strictTypeChecked[2],
91
+ files: allScriptFiles,
92
+ },
93
+ {
94
+ ...tseslint.configs.stylisticTypeChecked[2],
95
+ files: allScriptFiles,
96
+ },
97
+ ]
98
+ : [
99
+ {
100
+ ...tseslint.configs.strict[0],
101
+ files: allScriptFiles,
102
+ },
103
+ {
104
+ ...tseslint.configs.strict[1],
105
+ files: allTsFiles,
106
+ },
107
+ {
108
+ ...tseslint.configs.strict[2],
109
+ files: allScriptFiles,
110
+ },
111
+ {
112
+ ...tseslint.configs.stylistic[2],
113
+ files: allScriptFiles,
114
+ },
115
+ ]),
116
+ )
117
+ }
118
+
119
+ const scriptConfig = []
120
+ if (OPTIONS.ENABLE_SCRIPT) {
121
+ scriptConfig.push(
122
+ ...tseslint.config([
123
+ {
124
+ files: [
125
+ `*.{${scriptFileExtensions}}`,
126
+ `config/**/*.{${scriptFileExtensions}}`,
127
+ `scripts/**/*.{${scriptFileExtensions}}`,
128
+ `test/**/*.{${scriptFileExtensions}}`,
129
+ `spec/**/*.{${scriptFileExtensions}}`,
130
+ `tools/**/*.{${scriptFileExtensions}}`,
131
+ ],
132
+ languageOptions: {
133
+ globals: globals.node,
134
+ },
135
+ },
136
+ {
137
+ files: [`src/**/*.{${scriptFileExtensions}}`],
138
+ languageOptions: {
139
+ globals: globals.browser,
140
+ },
141
+ },
142
+ {
143
+ ...js.configs.recommended,
144
+ files: allScriptFiles,
145
+ },
146
+ ...tsConfig,
147
+ {
148
+ ...importX.flatConfigs.recommended,
149
+ files: allScriptFiles,
150
+ settings: {
151
+ 'import-x/resolver': {
152
+ typescript: {
153
+ alwaysTryTypes: true,
154
+ project: OPTIONS.TSCONFIG_PATH,
155
+ },
156
+ node: true,
157
+ },
158
+ },
159
+ },
160
+ {
161
+ ...importX.flatConfigs.typescript,
162
+ files: allScriptFiles,
163
+ settings: {
164
+ 'import-x/resolver': {
165
+ typescript: {
166
+ alwaysTryTypes: true,
167
+ project: OPTIONS.TSCONFIG_PATH,
168
+ },
169
+ node: true,
170
+ },
171
+ },
172
+ },
173
+ {
174
+ files: allScriptFiles,
175
+ plugins: {
176
+ 'unused-imports': unusedImports,
177
+ },
178
+ },
179
+ {
180
+ files: allScriptFiles,
181
+ languageOptions: {
182
+ parser: tsParser,
183
+ ecmaVersion: 'latest',
184
+ sourceType: 'module',
185
+ },
186
+ },
187
+ ]),
188
+ )
189
+ }
190
+
191
+ const frontendConfig = []
192
+ if (OPTIONS.ENABLE_FRONTEND) {
193
+ if (OPTIONS.ENABLE_NEXT) {
194
+ frontendConfig.push(
195
+ {
196
+ plugins: {
197
+ '@next/next': nextPlugin,
198
+ // Next.js projects need React plugins too!
199
+ 'react': reactPlugin,
200
+ 'react-hooks': reactHooksPlugin,
201
+ },
202
+ settings: {
203
+ react: {
204
+ version: "detect"
205
+ }
206
+ },
207
+ rules: {
208
+ ...nextPlugin.configs.recommended.rules,
209
+ ...nextPlugin.configs['core-web-vitals'].rules,
210
+ // We also want standard React/Hooks rules in Next.js
211
+ ...reactPlugin.configs.recommended.rules,
212
+ ...reactHooksPlugin.configs.recommended.rules,
213
+ "react/react-in-jsx-scope": "off",
214
+ "react/prop-types": "off"
215
+ }
216
+ }
217
+ )
218
+ } else {
219
+ frontendConfig.push(
220
+ {
221
+ plugins: {
222
+ 'react': reactPlugin,
223
+ 'react-hooks': reactHooksPlugin,
224
+ },
225
+ settings: {
226
+ react: {
227
+ version: "detect"
228
+ }
229
+ },
230
+ rules: {
231
+ ...reactPlugin.configs.recommended.rules,
232
+ ...reactHooksPlugin.configs.recommended.rules,
233
+ "react/react-in-jsx-scope": "off",
234
+ "react/prop-types": "off"
235
+ }
236
+ }
237
+ )
238
+ }
239
+ for (const nextConfigElement of frontendConfig) {
240
+ nextConfigElement.files ??= allScriptFiles
241
+ }
242
+ }
243
+
244
+ const cssConfig = []
245
+ if (OPTIONS.ENABLE_STYLESHEET) {
246
+ cssConfig.push(
247
+ ...defineConfig([
248
+ {
249
+ ...css.configs.recommended,
250
+ files: ['**/*.css'],
251
+ language: 'css/css',
252
+ },
253
+ ]),
254
+ )
255
+ }
256
+
257
+ const markdownConfig = []
258
+ if (OPTIONS.ENABLE_MARKDOWN) {
259
+ markdownConfig.push(
260
+ ...defineConfig([
261
+ {
262
+ ...markdown.configs.recommended[0],
263
+ files: ['**/*.md', '**/*.markdown'],
264
+ language: 'markdown/gfm',
265
+ },
266
+ ]),
267
+ )
268
+ }
269
+
270
+ const jsonConfig = []
271
+ if (OPTIONS.ENABLE_JSON) {
272
+ jsonConfig.push(
273
+ ...defineConfig([
274
+ {
275
+ ...json.configs.recommended,
276
+ files: ['**/*.json'],
277
+ ignores: ['**/tsconfig.json', '**/tsconfig.*.json'],
278
+ language: 'json/json',
279
+ },
280
+ {
281
+ ...json.configs.recommended,
282
+ files: ['**/*.jsonc', '**/*.json5', '**/tsconfig.json', '**/tsconfig.*.json'],
283
+ language: 'json/jsonc',
284
+ },
285
+ ]),
286
+ )
287
+ }
288
+
289
+ const prettierConfig = []
290
+ if (OPTIONS.IGNORE_PRETTIER) {
291
+ prettierConfig.push(eslintConfigPrettier)
292
+ }
293
+
294
+ const customConfig = defineConfig([
295
+ {
296
+ files: allScriptFiles,
297
+ rules: {
298
+ '@typescript-eslint/no-extraneous-class': 'off',
299
+ '@typescript-eslint/no-unnecessary-type-parameters': 'off',
300
+ '@typescript-eslint/no-unused-vars': 'off',
301
+ '@typescript-eslint/no-empty-function': 'off',
302
+ '@typescript-eslint/require-await': 'off',
303
+ 'import-x/no-anonymous-default-export': 'error',
304
+ 'import-x/no-named-as-default': 'error',
305
+ 'import-x/no-named-as-default-member': 'off',
306
+ 'import-x/order': [
307
+ 'error',
308
+ {
309
+ groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object', 'type'],
310
+ pathGroups: [
311
+ {
312
+ pattern: '@/**',
313
+ group: 'internal',
314
+ position: 'before',
315
+ },
316
+ ],
317
+ pathGroupsExcludedImportTypes: ['builtin'],
318
+ 'newlines-between': 'always',
319
+ 'newlines-between-types': 'always',
320
+ distinctGroup: false,
321
+ alphabetize: {
322
+ order: 'asc',
323
+ caseInsensitive: true,
324
+ },
325
+ sortTypesGroup: true,
326
+ },
327
+ ],
328
+ 'unused-imports/no-unused-imports': 'error',
329
+ 'unused-imports/no-unused-vars': [
330
+ 'off',
331
+ {
332
+ vars: 'all',
333
+ args: 'after-used',
334
+ },
335
+ ],
336
+ },
337
+ },
338
+ {
339
+ files: allScriptFiles,
340
+ rules: OPTIONS.ENABLE_FRONTEND
341
+ ? {
342
+ 'react-hooks/exhaustive-deps': 'error',
343
+ }
344
+ : {},
345
+ },
346
+ {
347
+ files: allScriptFiles,
348
+ rules:
349
+ OPTIONS.ENABLE_FRONTEND && OPTIONS.ENABLE_NEXT
350
+ ? {
351
+ '@next/next/no-img-element': 'error',
352
+ }
353
+ : {},
354
+ },
355
+ {
356
+ files: ['**/*.css'],
357
+ language: 'css/css',
358
+ rules: {
359
+ 'css/no-empty-blocks': 'off',
360
+ 'css/use-baseline': 'off',
361
+ },
362
+ },
363
+ ])
364
+
365
+ return [
366
+ ...globalConfig,
367
+ ...scriptConfig,
368
+ ...frontendConfig,
369
+ ...cssConfig,
370
+ ...markdownConfig,
371
+ ...jsonConfig,
372
+ ...prettierConfig,
373
+ ...customConfig,
374
+ ]
375
+ }
@@ -0,0 +1,13 @@
1
+ declare namespace _default {
2
+ let semi: boolean;
3
+ let singleQuote: boolean;
4
+ let jsxSingleQuote: boolean;
5
+ let trailingComma: string;
6
+ let bracketSpacing: boolean;
7
+ let printWidth: number;
8
+ let tabWidth: number;
9
+ let useTabs: boolean;
10
+ let arrowParens: string;
11
+ let endOfLine: string;
12
+ }
13
+ export default _default;
@@ -0,0 +1,13 @@
1
+
2
+ export default {
3
+ semi: false,
4
+ singleQuote: true,
5
+ jsxSingleQuote: true,
6
+ trailingComma: 'all',
7
+ bracketSpacing: true,
8
+ printWidth: 120,
9
+ tabWidth: 2,
10
+ useTabs: false,
11
+ arrowParens: 'always',
12
+ endOfLine: 'lf',
13
+ }
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from 'module';
3
+ // import cac from "cac";
4
+ import { loadUserConfig, generateTempConfig, cleanup } from "./core/config.js";
5
+ import { runEslint, runPrettier } from "./core/runner.js";
6
+ import { logError, logSuccess, logInfo } from "./utils/logger.js";
7
+ const require = createRequire(import.meta.url);
8
+ const pkg = require('../package.json');
9
+ const cac = require('cac');
10
+ const version = pkg.version || "0.0.0";
11
+ const cli = cac.cac ? cac.cac("rhine-lint") : cac("rhine-lint");
12
+ cli
13
+ .command("[...files]", "Lint files")
14
+ .option("--fix", "Fix lint errors")
15
+ .option("--config <path>", "Path to config file")
16
+ .option("--level <level>", "Project level (js, ts, frontend, nextjs)")
17
+ .option("--cache-dir <dir>", "Custom temporary cache directory")
18
+ .action(async (files, options) => {
19
+ const cwd = process.cwd();
20
+ // If files is empty, default to "."
21
+ const targetFiles = files.length > 0 ? files : ["."];
22
+ let usedCachePath;
23
+ try {
24
+ logInfo(`Starting Rhine Lint v${version}`);
25
+ // 1. Load User Config
26
+ // Note: We currently auto-detect config. explicit --config path support in loadUserConfig could be added if needed,
27
+ // but loadConfig from jiti handles discovery well.
28
+ const userConfigResult = await loadUserConfig(cwd);
29
+ if (userConfigResult.path) {
30
+ logInfo(`Using config: ${userConfigResult.path}`);
31
+ }
32
+ else {
33
+ logInfo("Using default configuration");
34
+ }
35
+ console.log();
36
+ // 2. Generate Temp Configs
37
+ const temps = await generateTempConfig(cwd, userConfigResult, options.level, options.cacheDir);
38
+ usedCachePath = temps.cachePath; // Save for cleanup
39
+ // 3. Run ESLint
40
+ const eslintResult = await runEslint(cwd, temps.eslintPath, options.fix, targetFiles);
41
+ console.log();
42
+ // 4. Run Prettier
43
+ const prettierResult = await runPrettier(cwd, temps.prettierPath, options.fix, targetFiles);
44
+ console.log();
45
+ if (eslintResult || prettierResult) {
46
+ logError("Linting completed with issues:");
47
+ if (eslintResult) {
48
+ logError(`ESLint: ${eslintResult}`);
49
+ }
50
+ else {
51
+ logSuccess(`ESLint: No issues found`);
52
+ }
53
+ if (prettierResult) {
54
+ logError(`Prettier: ${prettierResult}`);
55
+ }
56
+ else {
57
+ logSuccess(`Prettier: No issues found`);
58
+ }
59
+ process.exit(1);
60
+ }
61
+ logSuccess("Linting completed successfully.");
62
+ }
63
+ catch (e) {
64
+ logError("Unexpected error during linting.", e);
65
+ process.exit(1);
66
+ }
67
+ finally {
68
+ // 5. Cleanup
69
+ if (usedCachePath) {
70
+ await cleanup(usedCachePath);
71
+ }
72
+ }
73
+ });
74
+ cli.help();
75
+ cli.version(version);
76
+ cli.parse();
@@ -0,0 +1,14 @@
1
+ export type Config = {
2
+ type?: 'js' | 'ts' | 'frontend' | 'react' | 'nextjs';
3
+ cacheDir?: string;
4
+ fix?: boolean;
5
+ eslint?: {
6
+ config?: [
7
+ ];
8
+ overlay?: boolean;
9
+ };
10
+ prettier?: {
11
+ config?: {};
12
+ overlay?: boolean;
13
+ };
14
+ };
package/dist/config.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,14 @@
1
+ import { type Config } from "../config.js";
2
+ export declare function loadUserConfig(cwd: string): Promise<{
3
+ config: Config;
4
+ path?: string;
5
+ }>;
6
+ export declare function generateTempConfig(cwd: string, userConfigResult: {
7
+ config: Config;
8
+ path?: string;
9
+ }, cliLevel?: string, cliCacheDir?: string): Promise<{
10
+ eslintPath: string;
11
+ prettierPath: string;
12
+ cachePath: string;
13
+ }>;
14
+ export declare function cleanup(cachePath: string): Promise<void>;