lenix 1.2.2 → 1.3.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,334 @@
1
+ {
2
+ "outFile": "eslint.config.mts",
3
+ "rules": {
4
+ "accessor-pairs": "error",
5
+ "array-callback-return": "warn",
6
+ "arrow-body-style": "error",
7
+ "block-scoped-var": "error",
8
+ "camelcase": "error",
9
+ "capitalized-comments": "warn",
10
+ "class-methods-use-this": "error",
11
+ "complexity": "warn",
12
+ "consistent-return": "warn",
13
+ "consistent-this": "warn",
14
+ "constructor-super": "warn",
15
+ "curly": "error",
16
+ "default-case": "error",
17
+ "default-case-last": "error",
18
+ "default-param-last": "error",
19
+ "dot-notation": "error",
20
+ "eqeqeq": "error",
21
+ "for-direction": "error",
22
+ "func-name-matching": "error",
23
+ "func-names": "warn",
24
+ "func-style": "error",
25
+ "getter-return": "error",
26
+ "grouped-accessor-pairs": "error",
27
+ "guard-for-in": "warn",
28
+ "id-denylist": "warn",
29
+ "id-length": "error",
30
+ "id-match": "error",
31
+ "init-declarations": "error",
32
+ "logical-assignment-operators": "error",
33
+ "max-classes-per-file": "error",
34
+ "max-depth": "error",
35
+ "max-lines": "error",
36
+ "max-lines-per-function": "error",
37
+ "max-nested-callbacks": "error",
38
+ "max-params": "error",
39
+ "max-statements": "error",
40
+ "new-cap": "warn",
41
+ "no-alert": "error",
42
+ "no-array-constructor": "warn",
43
+ "no-async-promise-executor": "warn",
44
+ "no-await-in-loop": "warn",
45
+ "no-bitwise": "warn",
46
+ "no-caller": "warn",
47
+ "no-case-declarations": "warn",
48
+ "no-class-assign": "error",
49
+ "no-compare-neg-zero": "error",
50
+ "no-cond-assign": "warn",
51
+ "no-console": "error",
52
+ "no-const-assign": "error",
53
+ "no-constant-binary-expression": "warn",
54
+ "no-constant-condition": "warn",
55
+ "no-constructor-return": "error",
56
+ "no-continue": "error",
57
+ "no-control-regex": "warn",
58
+ "no-debugger": "error",
59
+ "no-delete-var": "error",
60
+ "no-div-regex": "warn",
61
+ "no-dupe-args": "error",
62
+ "no-dupe-class-members": "error",
63
+ "no-dupe-else-if": "error",
64
+ "no-dupe-keys": "error",
65
+ "no-duplicate-case": "error",
66
+ "no-duplicate-imports": "error",
67
+ "no-else-return": "error",
68
+ "no-empty": "error",
69
+ "no-empty-character-class": "warn",
70
+ "no-empty-function": "error",
71
+ "no-empty-pattern": "error",
72
+ "no-empty-static-block": "error",
73
+ "no-eq-null": "error",
74
+ "no-eval": "error",
75
+ "no-ex-assign": "error",
76
+ "no-extend-native": "warn",
77
+ "no-extra-bind": "warn",
78
+ "no-extra-boolean-cast": "warn",
79
+ "no-extra-label": "warn",
80
+ "no-fallthrough": "warn",
81
+ "no-func-assign": "error",
82
+ "no-global-assign": "error",
83
+ "no-implicit-coercion": "warn",
84
+ "no-implicit-globals": "error",
85
+ "no-implied-eval": "error",
86
+ "no-import-assign": "error",
87
+ "no-inline-comments": "warn",
88
+ "no-inner-declarations": "error",
89
+ "no-invalid-regexp": "warn",
90
+ "no-invalid-this": "error",
91
+ "no-irregular-whitespace": "warn",
92
+ "no-iterator": "warn",
93
+ "no-label-var": "error",
94
+ "no-labels": "error",
95
+ "no-lone-blocks": "error",
96
+ "no-lonely-if": "error",
97
+ "no-loop-func": "warn",
98
+ "no-loss-of-precision": "error",
99
+ "no-magic-numbers": "error",
100
+ "no-misleading-character-class": "warn",
101
+ "no-multi-assign": "error",
102
+ "no-multi-str": "error",
103
+ "no-negated-condition": "warn",
104
+ "no-nested-ternary": "error",
105
+ "no-new": "error",
106
+ "no-new-func": "error",
107
+ "no-new-native-nonconstructor": "warn",
108
+ "no-new-wrappers": "warn",
109
+ "no-nonoctal-decimal-escape": "warn",
110
+ "no-obj-calls": "warn",
111
+ "no-object-constructor": "warn",
112
+ "no-octal": "error",
113
+ "no-octal-escape": "warn",
114
+ "no-param-reassign": "warn",
115
+ "no-plusplus": "warn",
116
+ "no-promise-executor-return": "error",
117
+ "no-proto": "warn",
118
+ "no-prototype-builtins": "warn",
119
+ "no-redeclare": "error",
120
+ "no-regex-spaces": "warn",
121
+ "no-restricted-exports": "error",
122
+ "no-restricted-globals": "warn",
123
+ "no-restricted-imports": "warn",
124
+ "no-restricted-properties": "warn",
125
+ "no-restricted-syntax": "warn",
126
+ "no-return-assign": "error",
127
+ "no-script-url": "warn",
128
+ "no-self-assign": "error",
129
+ "no-self-compare": "error",
130
+ "no-sequences": "warn",
131
+ "no-setter-return": "error",
132
+ "no-shadow": "warn",
133
+ "no-shadow-restricted-names": "error",
134
+ "no-sparse-arrays": "error",
135
+ "no-template-curly-in-string": "error",
136
+ "no-this-before-super": "error",
137
+ "no-throw-literal": "error",
138
+ "no-unassigned-vars": "error",
139
+ "no-undef": "warn",
140
+ "no-undef-init": "error",
141
+ "no-undefined": "warn",
142
+ "no-underscore-dangle": "warn",
143
+ "no-unexpected-multiline": "error",
144
+ "no-unmodified-loop-condition": "warn",
145
+ "no-unneeded-ternary": "error",
146
+ "no-unreachable": "error",
147
+ "no-unreachable-loop": "warn",
148
+ "no-unsafe-finally": "warn",
149
+ "no-unsafe-negation": "warn",
150
+ "no-unsafe-optional-chaining": "error",
151
+ "no-unused-expressions": "error",
152
+ "no-unused-labels": "error",
153
+ "no-unused-private-class-members": "error",
154
+ "no-unused-vars": "error",
155
+ "no-use-before-define": "error",
156
+ "no-useless-assignment": "error",
157
+ "no-useless-backreference": "warn",
158
+ "no-useless-call": "warn",
159
+ "no-useless-catch": "error",
160
+ "no-useless-computed-key": "warn",
161
+ "no-useless-concat": "error",
162
+ "no-useless-constructor": "warn",
163
+ "no-useless-escape": "warn",
164
+ "no-useless-rename": "error",
165
+ "no-useless-return": "error",
166
+ "no-var": "error",
167
+ "no-void": "error",
168
+ "no-warning-comments": "warn",
169
+ "no-with": "error",
170
+ "object-shorthand": "error",
171
+ "one-var": "error",
172
+ "operator-assignment": "warn",
173
+ "prefer-arrow-callback": "error",
174
+ "prefer-const": "error",
175
+ "prefer-destructuring": "error",
176
+ "prefer-exponentiation-operator": "error",
177
+ "prefer-named-capture-group": "warn",
178
+ "prefer-numeric-literals": "warn",
179
+ "prefer-object-has-own": "warn",
180
+ "prefer-object-spread": "error",
181
+ "prefer-promise-reject-errors": "warn",
182
+ "prefer-regex-literals": "warn",
183
+ "prefer-rest-params": "error",
184
+ "prefer-spread": "error",
185
+ "prefer-template": "error",
186
+ "preserve-caught-error": "error",
187
+ "radix": "warn",
188
+ "require-atomic-updates": "warn",
189
+ "require-await": "error",
190
+ "require-unicode-regexp": "warn",
191
+ "require-yield": "error",
192
+ "sort-imports": "warn",
193
+ "sort-keys": "error",
194
+ "sort-vars": "error",
195
+ "strict": "error",
196
+ "symbol-description": "error",
197
+ "unicode-bom": "warn",
198
+ "use-isnan": "error",
199
+ "valid-typeof": "error",
200
+ "vars-on-top": "error",
201
+ "yoda": "error",
202
+ "@typescript-eslint/adjacent-overload-signatures": "error",
203
+ "@typescript-eslint/array-type": "error",
204
+ "@typescript-eslint/ban-ts-comment": "error",
205
+ "@typescript-eslint/ban-tslint-comment": "error",
206
+ "@typescript-eslint/class-literal-property-style": "warn",
207
+ "@typescript-eslint/class-methods-use-this": "warn",
208
+ "@typescript-eslint/consistent-generic-constructors": "error",
209
+ "@typescript-eslint/consistent-indexed-object-style": "error",
210
+ "@typescript-eslint/consistent-type-assertions": "error",
211
+ "@typescript-eslint/consistent-type-definitions": "error",
212
+ "@typescript-eslint/consistent-type-imports": "error",
213
+ "@typescript-eslint/default-param-last": "warn",
214
+ "@typescript-eslint/explicit-function-return-type": "error",
215
+ "@typescript-eslint/explicit-member-accessibility": "warn",
216
+ "@typescript-eslint/explicit-module-boundary-types": "error",
217
+ "@typescript-eslint/init-declarations": "error",
218
+ "@typescript-eslint/max-params": "error",
219
+ "@typescript-eslint/member-ordering": "error",
220
+ "@typescript-eslint/method-signature-style": "error",
221
+ "@typescript-eslint/no-array-constructor": "error",
222
+ "@typescript-eslint/no-confusing-non-null-assertion": "error",
223
+ "@typescript-eslint/no-duplicate-enum-values": "error",
224
+ "@typescript-eslint/no-dynamic-delete": "error",
225
+ "@typescript-eslint/no-empty-function": "error",
226
+ "@typescript-eslint/no-empty-object-type": "error",
227
+ "@typescript-eslint/no-explicit-any": "error",
228
+ "@typescript-eslint/no-extra-non-null-assertion": "error",
229
+ "@typescript-eslint/no-extraneous-class": "error",
230
+ "@typescript-eslint/no-import-type-side-effects": "error",
231
+ "@typescript-eslint/no-inferrable-types": "error",
232
+ "@typescript-eslint/no-invalid-void-type": "error",
233
+ "@typescript-eslint/no-loop-func": "error",
234
+ "@typescript-eslint/no-magic-numbers": "warn",
235
+ "@typescript-eslint/no-misused-new": "error",
236
+ "@typescript-eslint/no-namespace": "error",
237
+ "@typescript-eslint/no-non-null-asserted-nullish-coalescing": "warn",
238
+ "@typescript-eslint/no-non-null-asserted-optional-chain": "error",
239
+ "@typescript-eslint/no-non-null-assertion": "error",
240
+ "@typescript-eslint/no-redeclare": "warn",
241
+ "@typescript-eslint/no-require-imports": "error",
242
+ "@typescript-eslint/no-restricted-imports": "warn",
243
+ "@typescript-eslint/no-restricted-types": "warn",
244
+ "@typescript-eslint/no-shadow": "warn",
245
+ "@typescript-eslint/no-this-alias": "error",
246
+ "@typescript-eslint/no-unnecessary-parameter-property-assignment": "error",
247
+ "@typescript-eslint/no-unnecessary-type-constraint": "error",
248
+ "@typescript-eslint/no-unsafe-declaration-merging": "error",
249
+ "@typescript-eslint/no-unsafe-function-type": "error",
250
+ "@typescript-eslint/no-unused-expressions": "warn",
251
+ "@typescript-eslint/no-unused-private-class-members": "error",
252
+ "@typescript-eslint/no-unused-vars": "error",
253
+ "@typescript-eslint/no-use-before-define": "error",
254
+ "@typescript-eslint/no-useless-constructor": "error",
255
+ "@typescript-eslint/no-useless-empty-export": "error",
256
+ "@typescript-eslint/no-wrapper-object-types": "error",
257
+ "@typescript-eslint/prefer-as-const": "error",
258
+ "@typescript-eslint/prefer-enum-initializers": "error",
259
+ "@typescript-eslint/prefer-for-of": "error",
260
+ "@typescript-eslint/prefer-function-type": "error",
261
+ "@typescript-eslint/prefer-literal-enum-member": "error",
262
+ "@typescript-eslint/prefer-namespace-keyword": "error",
263
+ "@typescript-eslint/triple-slash-reference": "error",
264
+ "@typescript-eslint/unified-signatures": "error",
265
+ "@typescript-eslint/await-thenable": "warn",
266
+ "@typescript-eslint/consistent-return": "error",
267
+ "@typescript-eslint/consistent-type-exports": "error",
268
+ "@typescript-eslint/dot-notation": "error",
269
+ "@typescript-eslint/naming-convention": "warn",
270
+ "@typescript-eslint/no-array-delete": "error",
271
+ "@typescript-eslint/no-base-to-string": "warn",
272
+ "@typescript-eslint/no-confusing-void-expression": "error",
273
+ "@typescript-eslint/no-deprecated": "error",
274
+ "@typescript-eslint/no-duplicate-type-constituents": "error",
275
+ "@typescript-eslint/no-floating-promises": "warn",
276
+ "@typescript-eslint/no-for-in-array": "error",
277
+ "@typescript-eslint/no-implied-eval": "error",
278
+ "@typescript-eslint/no-meaningless-void-operator": "error",
279
+ "@typescript-eslint/no-misused-promises": "error",
280
+ "@typescript-eslint/no-misused-spread": "error",
281
+ "@typescript-eslint/no-mixed-enums": "error",
282
+ "@typescript-eslint/no-redundant-type-constituents": "warn",
283
+ "@typescript-eslint/no-unnecessary-boolean-literal-compare": "error",
284
+ "@typescript-eslint/no-unnecessary-condition": "error",
285
+ "@typescript-eslint/no-unnecessary-qualifier": "warn",
286
+ "@typescript-eslint/no-unnecessary-template-expression": "error",
287
+ "@typescript-eslint/no-unnecessary-type-arguments": "error",
288
+ "@typescript-eslint/no-unnecessary-type-assertion": "error",
289
+ "@typescript-eslint/no-unnecessary-type-conversion": "warn",
290
+ "@typescript-eslint/no-unnecessary-type-parameters": "error",
291
+ "@typescript-eslint/no-unsafe-argument": "error",
292
+ "@typescript-eslint/no-unsafe-assignment": "error",
293
+ "@typescript-eslint/no-unsafe-call": "error",
294
+ "@typescript-eslint/no-unsafe-enum-comparison": "error",
295
+ "@typescript-eslint/no-unsafe-member-access": "error",
296
+ "@typescript-eslint/no-unsafe-return": "error",
297
+ "@typescript-eslint/no-unsafe-type-assertion": "error",
298
+ "@typescript-eslint/no-unsafe-unary-minus": "warn",
299
+ "@typescript-eslint/no-useless-default-assignment": "error",
300
+ "@typescript-eslint/non-nullable-type-assertion-style": "warn",
301
+ "@typescript-eslint/only-throw-error": "error",
302
+ "@typescript-eslint/prefer-destructuring": "error",
303
+ "@typescript-eslint/prefer-find": "error",
304
+ "@typescript-eslint/prefer-includes": "error",
305
+ "@typescript-eslint/prefer-nullish-coalescing": "error",
306
+ "@typescript-eslint/prefer-optional-chain": "error",
307
+ "@typescript-eslint/prefer-promise-reject-errors": "error",
308
+ "@typescript-eslint/prefer-readonly": "error",
309
+ "@typescript-eslint/prefer-readonly-parameter-types": "error",
310
+ "@typescript-eslint/prefer-reduce-type-parameter": "error",
311
+ "@typescript-eslint/prefer-regexp-exec": "warn",
312
+ "@typescript-eslint/prefer-return-this-type": "error",
313
+ "@typescript-eslint/prefer-string-starts-ends-with": "error",
314
+ "@typescript-eslint/promise-function-async": "error",
315
+ "@typescript-eslint/related-getter-setter-pairs": "error",
316
+ "@typescript-eslint/require-array-sort-compare": "error",
317
+ "@typescript-eslint/require-await": "error",
318
+ "@typescript-eslint/restrict-plus-operands": "error",
319
+ "@typescript-eslint/restrict-template-expressions": "error",
320
+ "@typescript-eslint/return-await": "error",
321
+ "@typescript-eslint/strict-boolean-expressions": "error",
322
+ "@typescript-eslint/strict-void-return": "warn",
323
+ "@typescript-eslint/switch-exhaustiveness-check": "error",
324
+ "@typescript-eslint/unbound-method": "warn",
325
+ "@typescript-eslint/use-unknown-in-catch-callback-variable": "error"
326
+ },
327
+ "completedSections": [
328
+ "Core — Other",
329
+ "TypeScript",
330
+ "TypeScript — type-aware"
331
+ ],
332
+ "pausedSection": null,
333
+ "pausedAt": 0
334
+ }
package/bin/Readme.md CHANGED
@@ -21,4 +21,13 @@ print(inspect({name = "Lenix"}))
21
21
  ### What it does
22
22
 
23
23
  Adds environment variables to your shell config (for mac users: `~/.zshrc` or `~/.bashrc`) to automatically change your Lua modules configurations to `./lua_modules` instead of hidden nested directories.
24
+
25
+ ## ESLINT Usage
26
+ ### Build your own linter
27
+ ```zsh
28
+ npx lenix --lint
29
+ ```
30
+ ### Clone a preset lint
31
+ ```zsh
32
+ npx lenix --lint --preset
24
33
  ```
@@ -0,0 +1,285 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'fs';
3
+ import { fileURLToPath } from 'url';
4
+ import path from 'path';
5
+ import eslintJs from '@eslint/js';
6
+ import tseslint from 'typescript-eslint';
7
+ const [, , cmd, ...args] = process.argv;
8
+ if (cmd !== '--lint') {
9
+ process.stdout.write('Usage: lenix --lint [--preset] [--clone]\n');
10
+ process.exit(0);
11
+ }
12
+ process.argv = [process.argv[0], process.argv[1], ...args];
13
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
14
+ const c = { reset: '\x1b[0m', bold: '\x1b[1m', dim: '\x1b[2m', cyan: '\x1b[36m', yellow: '\x1b[33m', green: '\x1b[32m', red: '\x1b[31m', blue: '\x1b[34m', magenta: '\x1b[35m', gray: '\x1b[90m' };
15
+ const title = (s) => `${c.bold}${c.cyan}${s}${c.reset}`;
16
+ const section = (s) => `\n${c.bold}${c.blue}━━ ${s} ━━${c.reset}`;
17
+ const bold = (s) => `${c.bold}${s}${c.reset}`;
18
+ const dim = (s) => `${c.dim}${s}${c.reset}`;
19
+ const gray = (s) => `${c.gray}${s}${c.reset}`;
20
+ const tag = (s, col) => `${col}[${s}]${c.reset}`;
21
+ const pr = (s) => `${c.magenta}${s}${c.reset}`;
22
+ const ttyFd = fs.openSync('/dev/tty', 'r');
23
+ function readLine(question) {
24
+ process.stdout.write(question);
25
+ const buf = Buffer.alloc(1024);
26
+ let result = '';
27
+ while (true) {
28
+ const n = fs.readSync(ttyFd, buf, 0, 1, null);
29
+ if (n === 0)
30
+ break;
31
+ const ch = buf.toString('utf8', 0, n);
32
+ if (ch === '\n')
33
+ break;
34
+ if (ch === '\r')
35
+ continue;
36
+ result += ch;
37
+ }
38
+ return result;
39
+ }
40
+ const CHECKPOINT = '.lint-preset.json';
41
+ function saveCheckpoint(state) {
42
+ const data = {
43
+ outFile: state.outFile,
44
+ rules: state.allRules,
45
+ completedSections: [...state.completedSections],
46
+ pausedSection: state.pausedSection,
47
+ pausedAt: state.pausedAt,
48
+ };
49
+ fs.writeFileSync(CHECKPOINT, JSON.stringify(data, null, 2), 'utf8');
50
+ }
51
+ function loadCheckpoint() {
52
+ try {
53
+ if (!fs.existsSync(CHECKPOINT))
54
+ return null;
55
+ return JSON.parse(fs.readFileSync(CHECKPOINT, 'utf8'));
56
+ }
57
+ catch {
58
+ return null;
59
+ }
60
+ }
61
+ function buildPluginRules(rules, prefix) {
62
+ return Object.entries(rules)
63
+ .filter(([, r]) => !r.meta?.deprecated)
64
+ .map(([name, r]) => ({
65
+ fullName: `${prefix}/${name}`,
66
+ description: r.meta?.docs?.description ?? '',
67
+ fixable: !!r.meta?.fixable,
68
+ recommended: !!(r.meta?.docs?.recommended),
69
+ requiresTypeChecking: !!(r.meta?.docs?.requiresTypeChecking),
70
+ url: r.meta?.docs?.url ?? '',
71
+ }));
72
+ }
73
+ async function getCoreRules() {
74
+ try {
75
+ const { builtinRules } = await import('eslint/use-at-your-own-risk');
76
+ const byCat = {};
77
+ for (const [name, r] of builtinRules.entries()) {
78
+ if (r.meta?.deprecated)
79
+ continue;
80
+ const cat = r.meta?.docs?.category ?? 'Other';
81
+ if (!byCat[cat])
82
+ byCat[cat] = [];
83
+ byCat[cat].push({ fullName: name, description: r.meta?.docs?.description ?? '', fixable: !!r.meta?.fixable, recommended: !!(r.meta?.docs?.recommended), requiresTypeChecking: false, url: r.meta?.docs?.url ?? '' });
84
+ }
85
+ return byCat;
86
+ }
87
+ catch {
88
+ return { 'Core': Object.keys(eslintJs.configs.all.rules ?? {}).map(n => ({ fullName: n, description: '', fixable: false, recommended: false, requiresTypeChecking: false, url: '' })) };
89
+ }
90
+ }
91
+ function askRule(rule, idx, total) {
92
+ const tags = [rule.recommended ? tag('recommended', c.green) : null, rule.fixable ? tag('fixable', c.yellow) : null, rule.requiresTypeChecking ? tag('type-aware', c.red) : null].filter(Boolean).join(' ');
93
+ process.stdout.write(`\n${gray(`[${idx}/${total}]`)} ${bold(rule.fullName)} ${tags}\n`);
94
+ if (rule.description)
95
+ process.stdout.write(` ${dim(rule.description)}\n`);
96
+ if (rule.url)
97
+ process.stdout.write(` ${gray(rule.url)}\n`);
98
+ const a = readLine(pr(' → off/warn/error/skip/save [o/w/e/s/q]: ')).trim().toLowerCase();
99
+ if (!a || a === 's' || a === 'skip')
100
+ return null;
101
+ if (a === 'q' || a === 'save')
102
+ return 'save';
103
+ if (a === 'o' || a === 'off')
104
+ return [rule.fullName, 'off'];
105
+ if (a === 'w' || a === 'warn')
106
+ return [rule.fullName, 'warn'];
107
+ if (a === 'e' || a === 'error')
108
+ return [rule.fullName, 'error'];
109
+ process.stdout.write(gray(' Invalid, skipping.\n'));
110
+ return null;
111
+ }
112
+ function askSection(label, rules, state) {
113
+ if (state.completedSections.has(label)) {
114
+ process.stdout.write(gray(` ✓ Already completed: ${label}\n`));
115
+ return;
116
+ }
117
+ process.stdout.write(section(label) + '\n');
118
+ process.stdout.write(gray(` ${rules.length} rules — Enter=skip q=save & exit\n`));
119
+ const skip = readLine(pr(' Skip entire section? [y/N]: ')).trim().toLowerCase();
120
+ if (skip === 'y') {
121
+ state.completedSections.add(label);
122
+ saveCheckpoint({ ...state, pausedSection: null, pausedAt: 0 });
123
+ process.stdout.write(gray(' Skipped.\n'));
124
+ return;
125
+ }
126
+ const startAt = state.pausedSection === label ? (state.pausedAt ?? 0) : 0;
127
+ if (startAt > 0)
128
+ process.stdout.write(gray(` Resuming from rule ${startAt + 1}/${rules.length}\n`));
129
+ for (let i = startAt; i < rules.length; i++) {
130
+ const entry = askRule(rules[i], i + 1, rules.length);
131
+ if (entry === 'save') {
132
+ saveCheckpoint({ ...state, pausedSection: label, pausedAt: i });
133
+ process.stdout.write(`\n${tag('saved', c.green)} Progress saved to ${bold(CHECKPOINT)}\n`);
134
+ process.stdout.write(dim('Run the lint again to resume.\n\n'));
135
+ fs.closeSync(ttyFd);
136
+ process.exit(0);
137
+ }
138
+ if (entry)
139
+ state.allRules[entry[0]] = entry[1];
140
+ saveCheckpoint({ ...state, pausedSection: label, pausedAt: i + 1 });
141
+ }
142
+ state.completedSections.add(label);
143
+ saveCheckpoint({ ...state, pausedSection: null, pausedAt: 0 });
144
+ }
145
+ function generateConfig(allRules) {
146
+ const core = {};
147
+ const ts = {};
148
+ const reactR = {};
149
+ const imp = {};
150
+ for (const [k, v] of Object.entries(allRules)) {
151
+ if (k.startsWith('@typescript-eslint/'))
152
+ ts[k] = v;
153
+ else if (k.startsWith('react/') || k.startsWith('react-hooks/'))
154
+ reactR[k] = v;
155
+ else if (k.startsWith('import/'))
156
+ imp[k] = v;
157
+ else
158
+ core[k] = v;
159
+ }
160
+ const hasTs = Object.keys(ts).length > 0;
161
+ const hasReact = Object.keys(reactR).length > 0;
162
+ const hasImp = Object.keys(imp).length > 0;
163
+ const hasCore = Object.keys(core).length > 0;
164
+ const ind = (o, n) => JSON.stringify(o, null, 2).split('\n').map((l, i) => i === 0 ? l : ' '.repeat(n) + l).join('\n');
165
+ return [
166
+ `import js from '@eslint/js'`,
167
+ `import globals from 'globals'`,
168
+ hasTs ? `import tseslint from 'typescript-eslint'` : '',
169
+ hasReact ? `import pluginReact from 'eslint-plugin-react'` : '',
170
+ hasReact ? `import reactHooks from 'eslint-plugin-react-hooks'` : '',
171
+ hasImp ? `import importPlugin from 'eslint-plugin-import'` : '',
172
+ ``,
173
+ `export default [`,
174
+ ` { ignores: ['node_modules'] },`,
175
+ hasTs ? ` ...tseslint.configs.strictTypeChecked.map(config => ({ ...config, files: ['**/*.{ts,tsx,mts}'] })),` : '',
176
+ ` js.configs.recommended,`,
177
+ ` {`,
178
+ ` files: ['**/*.{ts,tsx,mts}'],`,
179
+ ` languageOptions: {`,
180
+ ` globals: { ...globals.browser, ...globals.node },`,
181
+ hasTs ? ` parserOptions: {\n projectService: true,\n tsconfigRootDir: import.meta.dirname,\n },` : '',
182
+ ` },`,
183
+ hasCore ? ` rules: ${ind(core, 4)},` : '',
184
+ ` },`,
185
+ hasTs ? ` {\n files: ['**/*.{ts,tsx,mts}'],\n rules: ${ind(ts, 4)},\n },` : '',
186
+ hasReact ? ` pluginReact.configs.flat['jsx-runtime'],` : '',
187
+ hasReact ? ` { settings: { react: { version: 'detect' } } },` : '',
188
+ hasReact ? ` {\n plugins: { 'react-hooks': reactHooks },\n rules: ${ind(reactR, 4)},\n },` : '',
189
+ hasImp ? ` {\n plugins: { import: importPlugin },\n rules: ${ind(imp, 4)},\n },` : '',
190
+ `]`,
191
+ ].filter(Boolean).join('\n');
192
+ }
193
+ const clone = process.argv.includes('--clone');
194
+ if (clone) {
195
+ const src = path.join(process.env.HOME, '.lint-preset.json');
196
+ const data = JSON.parse(fs.readFileSync(src, 'utf8'));
197
+ const config = generateConfig(data.rules);
198
+ fs.writeFileSync(data.outFile, config, 'utf8');
199
+ process.stdout.write(`${tag('done', c.green)} Written to ${bold(data.outFile)}\n`);
200
+ fs.closeSync(ttyFd);
201
+ process.exit(0);
202
+ }
203
+ const preset = process.argv.includes('--preset');
204
+ if (preset) {
205
+ const res = await fetch('https://raw.githubusercontent.com/LenixDev/lenix/refs/heads/main/bin/lint-preset.json');
206
+ const data = await res.json();
207
+ const config = generateConfig(data.rules);
208
+ fs.writeFileSync('eslint.config.mts', config, 'utf8');
209
+ process.stdout.write(`${tag('done', c.green)} Written to ${bold('eslint.config.mts')}\n`);
210
+ process.stdout.write(`${tag('notice', c.cyan)} Run: ${bold('npm add -D @eslint/js globals typescript-eslint')}\n`);
211
+ fs.closeSync(ttyFd);
212
+ process.exit(0);
213
+ }
214
+ async function main() {
215
+ process.stdout.write('\x1Bc');
216
+ process.stdout.write(title('╔═══════════════════════╗\n'));
217
+ process.stdout.write(title('║ ESLint Config ║\n'));
218
+ process.stdout.write(title('╚═══════════════════════╝\n'));
219
+ process.stdout.write(dim('\nWalks through every rule across all plugins.\n'));
220
+ process.stdout.write(dim('o=off w=warn e=error s/Enter=skip q=save & exit\n\n'));
221
+ const checkpoint = loadCheckpoint();
222
+ const state = { allRules: {}, completedSections: new Set(), outFile: 'eslint.config.mts', pausedSection: null, pausedAt: 0 };
223
+ if (checkpoint) {
224
+ process.stdout.write(`${tag('found', c.yellow)} Saved progress detected (${Object.keys(checkpoint.rules).length} rules configured).\n`);
225
+ const resume = readLine(pr('Resume? [Y/n]: ')).trim().toLowerCase();
226
+ if (resume !== 'n') {
227
+ state.outFile = checkpoint.outFile;
228
+ state.allRules = checkpoint.rules;
229
+ state.completedSections = new Set(checkpoint.completedSections);
230
+ state.pausedSection = checkpoint.pausedSection ?? null;
231
+ state.pausedAt = checkpoint.pausedAt ?? 0;
232
+ }
233
+ }
234
+ const coreByCategory = await getCoreRules();
235
+ for (const [cat, rules] of Object.entries(coreByCategory))
236
+ askSection(`Core — ${cat}`, rules, state);
237
+ const tsAll = buildPluginRules(tseslint.plugin.rules ?? {}, '@typescript-eslint');
238
+ askSection('TypeScript', tsAll.filter(r => !r.requiresTypeChecking), state);
239
+ askSection('TypeScript — type-aware', tsAll.filter(r => r.requiresTypeChecking), state);
240
+ // askSection('React', buildPluginRules((react.rules ?? {}) as Record<string, any>, 'react'), state)
241
+ // askSection('React Hooks', buildPluginRules((reactHooks.rules ?? {}) as Record<string, any>, 'react-hooks'), state)
242
+ // askSection('Imports', buildPluginRules((importPlugin.rules ?? {}) as Record<string, any>, 'import'), state)
243
+ const config = generateConfig(state.allRules);
244
+ let outFile = state.outFile;
245
+ if (fs.existsSync(outFile)) {
246
+ const overwrite = readLine(pr(`\n${outFile} already exists. Overwrite? [y/N]: `)).trim().toLowerCase();
247
+ if (overwrite !== 'y')
248
+ outFile = readLine(pr('Enter new filename: ')).trim() || outFile;
249
+ }
250
+ fs.writeFileSync(outFile, config, 'utf8');
251
+ // if (fs.existsSync(CHECKPOINT)) fs.unlinkSync(CHECKPOINT)
252
+ process.stdout.write(`\n${tag('done', c.green)} Written to ${bold(outFile)}\n`);
253
+ process.stdout.write(dim(`Rules configured: ${Object.keys(state.allRules).length}\n`));
254
+ const hasTs = Object.keys(state.allRules).some(k => k.startsWith('@typescript-eslint/'));
255
+ const hasReact = Object.keys(state.allRules).some(k => k.startsWith('react/') || k.startsWith('react-hooks/'));
256
+ const hasImp = Object.keys(state.allRules).some(k => k.startsWith('import/'));
257
+ const deps = [
258
+ '@eslint/js',
259
+ 'globals',
260
+ hasTs ? 'typescript-eslint' : '',
261
+ hasReact ? 'eslint-plugin-react eslint-plugin-react-hooks' : '',
262
+ hasImp ? 'eslint-plugin-import' : '',
263
+ ].filter(Boolean).join(' ');
264
+ if (hasTs) {
265
+ const tsconfig = {
266
+ compilerOptions: {
267
+ target: 'ESNext',
268
+ module: 'NodeNext',
269
+ moduleResolution: 'NodeNext',
270
+ strict: true,
271
+ },
272
+ include: ['**/*.ts', '**/*.mts', '**/*.tsx'],
273
+ exclude: ['node_modules'],
274
+ };
275
+ if (!fs.existsSync('tsconfig.json'))
276
+ fs.writeFileSync('tsconfig.json', JSON.stringify(tsconfig, null, 2), 'utf8');
277
+ else
278
+ process.stdout.write(dim('tsconfig.json already exists, skipping.\n'));
279
+ }
280
+ const pm = fs.existsSync('pnpm-lock.yaml') ? 'pnpm' : fs.existsSync('yarn.lock') ? 'yarn' : 'npm';
281
+ process.stdout.write(dim(`Run: ${pm} add -D ${deps}\n\n`));
282
+ fs.closeSync(ttyFd);
283
+ process.exit(0);
284
+ }
285
+ main().catch(e => { console.error(e); process.exit(1); });
package/bin/lint.mts ADDED
@@ -0,0 +1,322 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'fs'
3
+ import { fileURLToPath } from 'url'
4
+ import path from 'path'
5
+ import eslintJs from '@eslint/js'
6
+ import tseslint from 'typescript-eslint'
7
+ // import react from 'eslint-plugin-react'
8
+ // import reactHooks from 'eslint-plugin-react-hooks'
9
+ // import importPlugin from 'eslint-plugin-import'
10
+ import type { ESLint } from 'eslint'
11
+
12
+ const [,, cmd, ...args] = process.argv
13
+ if (cmd !== '--lint') {
14
+ process.stdout.write('Usage: lenix --lint [--preset] [--clone]\n')
15
+ process.exit(0)
16
+ }
17
+ process.argv = [process.argv[0], process.argv[1], ...args]
18
+
19
+ type PluginWithRules = ESLint.Plugin & { rules: Record<string, any> }
20
+
21
+ const __dirname = path.dirname(fileURLToPath(import.meta.url))
22
+
23
+ const c = { reset:'\x1b[0m',bold:'\x1b[1m',dim:'\x1b[2m',cyan:'\x1b[36m',yellow:'\x1b[33m',green:'\x1b[32m',red:'\x1b[31m',blue:'\x1b[34m',magenta:'\x1b[35m',gray:'\x1b[90m' }
24
+ const title = (s: string) => `${c.bold}${c.cyan}${s}${c.reset}`
25
+ const section = (s: string) => `\n${c.bold}${c.blue}━━ ${s} ━━${c.reset}`
26
+ const bold = (s: string) => `${c.bold}${s}${c.reset}`
27
+ const dim = (s: string) => `${c.dim}${s}${c.reset}`
28
+ const gray = (s: string) => `${c.gray}${s}${c.reset}`
29
+ const tag = (s: string, col: string) => `${col}[${s}]${c.reset}`
30
+ const pr = (s: string) => `${c.magenta}${s}${c.reset}`
31
+
32
+ const ttyFd = fs.openSync('/dev/tty', 'r')
33
+ function readLine(question: string): string {
34
+ process.stdout.write(question)
35
+ const buf = Buffer.alloc(1024)
36
+ let result = ''
37
+ while (true) {
38
+ const n = fs.readSync(ttyFd, buf, 0, 1, null)
39
+ if (n === 0) break
40
+ const ch = buf.toString('utf8', 0, n)
41
+ if (ch === '\n') break
42
+ if (ch === '\r') continue
43
+ result += ch
44
+ }
45
+ return result
46
+ }
47
+
48
+ const CHECKPOINT = '.lint-preset.json'
49
+
50
+ type State = {
51
+ outFile: string
52
+ allRules: Record<string, string>
53
+ completedSections: Set<string>
54
+ pausedSection: string | null
55
+ pausedAt: number
56
+ }
57
+
58
+ type CheckpointData = {
59
+ outFile: string
60
+ rules: Record<string, string>
61
+ completedSections: string[]
62
+ pausedSection: string | null
63
+ pausedAt: number
64
+ }
65
+
66
+ function saveCheckpoint(state: State) {
67
+ const data: CheckpointData = {
68
+ outFile: state.outFile,
69
+ rules: state.allRules,
70
+ completedSections: [...state.completedSections],
71
+ pausedSection: state.pausedSection,
72
+ pausedAt: state.pausedAt,
73
+ }
74
+ fs.writeFileSync(CHECKPOINT, JSON.stringify(data, null, 2), 'utf8')
75
+ }
76
+
77
+ function loadCheckpoint(): CheckpointData | null {
78
+ try {
79
+ if (!fs.existsSync(CHECKPOINT)) return null
80
+ return JSON.parse(fs.readFileSync(CHECKPOINT, 'utf8')) as CheckpointData
81
+ } catch { return null }
82
+ }
83
+
84
+ type RuleInfo = {
85
+ fullName: string
86
+ description: string
87
+ fixable: boolean
88
+ recommended: boolean
89
+ requiresTypeChecking: boolean
90
+ url: string
91
+ }
92
+
93
+ function buildPluginRules(rules: Record<string, any>, prefix: string): RuleInfo[] {
94
+ return Object.entries(rules)
95
+ .filter(([, r]) => !r.meta?.deprecated)
96
+ .map(([name, r]) => ({
97
+ fullName: `${prefix}/${name}`,
98
+ description: r.meta?.docs?.description ?? '',
99
+ fixable: !!r.meta?.fixable,
100
+ recommended: !!(r.meta?.docs?.recommended),
101
+ requiresTypeChecking: !!(r.meta?.docs?.requiresTypeChecking),
102
+ url: r.meta?.docs?.url ?? '',
103
+ }))
104
+ }
105
+
106
+ async function getCoreRules(): Promise<Record<string, RuleInfo[]>> {
107
+ try {
108
+ const { builtinRules } = await import('eslint/use-at-your-own-risk')
109
+ const byCat: Record<string, RuleInfo[]> = {}
110
+ for (const [name, r] of (builtinRules as Map<string, any>).entries()) {
111
+ if (r.meta?.deprecated) continue
112
+ const cat: string = r.meta?.docs?.category ?? 'Other'
113
+ if (!byCat[cat]) byCat[cat] = []
114
+ byCat[cat].push({ fullName: name, description: r.meta?.docs?.description ?? '', fixable: !!r.meta?.fixable, recommended: !!(r.meta?.docs?.recommended), requiresTypeChecking: false, url: r.meta?.docs?.url ?? '' })
115
+ }
116
+ return byCat
117
+ } catch {
118
+ return { 'Core': Object.keys(eslintJs.configs.all.rules ?? {}).map(n => ({ fullName: n, description: '', fixable: false, recommended: false, requiresTypeChecking: false, url: '' })) }
119
+ }
120
+ }
121
+
122
+ function askRule(rule: RuleInfo, idx: number, total: number): [string, string] | 'save' | null {
123
+ const tags = [rule.recommended ? tag('recommended',c.green) : null, rule.fixable ? tag('fixable',c.yellow) : null, rule.requiresTypeChecking ? tag('type-aware',c.red) : null].filter(Boolean).join(' ')
124
+ process.stdout.write(`\n${gray(`[${idx}/${total}]`)} ${bold(rule.fullName)} ${tags}\n`)
125
+ if (rule.description) process.stdout.write(` ${dim(rule.description)}\n`)
126
+ if (rule.url) process.stdout.write(` ${gray(rule.url)}\n`)
127
+ const a = readLine(pr(' → off/warn/error/skip/save [o/w/e/s/q]: ')).trim().toLowerCase()
128
+ if (!a || a === 's' || a === 'skip') return null
129
+ if (a === 'q' || a === 'save') return 'save'
130
+ if (a === 'o' || a === 'off') return [rule.fullName, 'off']
131
+ if (a === 'w' || a === 'warn') return [rule.fullName, 'warn']
132
+ if (a === 'e' || a === 'error') return [rule.fullName, 'error']
133
+ process.stdout.write(gray(' Invalid, skipping.\n'))
134
+ return null
135
+ }
136
+
137
+ function askSection(label: string, rules: RuleInfo[], state: State) {
138
+ if (state.completedSections.has(label)) {
139
+ process.stdout.write(gray(` ✓ Already completed: ${label}\n`))
140
+ return
141
+ }
142
+
143
+ process.stdout.write(section(label) + '\n')
144
+ process.stdout.write(gray(` ${rules.length} rules — Enter=skip q=save & exit\n`))
145
+
146
+ const skip = readLine(pr(' Skip entire section? [y/N]: ')).trim().toLowerCase()
147
+ if (skip === 'y') {
148
+ state.completedSections.add(label)
149
+ saveCheckpoint({ ...state, pausedSection: null, pausedAt: 0 })
150
+ process.stdout.write(gray(' Skipped.\n'))
151
+ return
152
+ }
153
+
154
+ const startAt = state.pausedSection === label ? (state.pausedAt ?? 0) : 0
155
+ if (startAt > 0) process.stdout.write(gray(` Resuming from rule ${startAt + 1}/${rules.length}\n`))
156
+
157
+ for (let i = startAt; i < rules.length; i++) {
158
+ const entry = askRule(rules[i], i + 1, rules.length)
159
+
160
+ if (entry === 'save') {
161
+ saveCheckpoint({ ...state, pausedSection: label, pausedAt: i })
162
+ process.stdout.write(`\n${tag('saved', c.green)} Progress saved to ${bold(CHECKPOINT)}\n`)
163
+ process.stdout.write(dim('Run the lint again to resume.\n\n'))
164
+ fs.closeSync(ttyFd)
165
+ process.exit(0)
166
+ }
167
+
168
+ if (entry) state.allRules[entry[0]] = entry[1]
169
+ saveCheckpoint({ ...state, pausedSection: label, pausedAt: i + 1 })
170
+ }
171
+
172
+ state.completedSections.add(label)
173
+ saveCheckpoint({ ...state, pausedSection: null, pausedAt: 0 })
174
+ }
175
+
176
+ function generateConfig(allRules: Record<string, string>): string {
177
+ const core: Record<string, string> = {}
178
+ const ts: Record<string, string> = {}
179
+ const reactR: Record<string, string> = {}
180
+ const imp: Record<string, string> = {}
181
+ for (const [k, v] of Object.entries(allRules)) {
182
+ if (k.startsWith('@typescript-eslint/')) ts[k] = v
183
+ else if (k.startsWith('react/') || k.startsWith('react-hooks/')) reactR[k] = v
184
+ else if (k.startsWith('import/')) imp[k] = v
185
+ else core[k] = v
186
+ }
187
+ const hasTs = Object.keys(ts).length > 0
188
+ const hasReact = Object.keys(reactR).length > 0
189
+ const hasImp = Object.keys(imp).length > 0
190
+ const hasCore = Object.keys(core).length > 0
191
+ const ind = (o: object, n: number) => JSON.stringify(o, null, 2).split('\n').map((l, i) => i === 0 ? l : ' '.repeat(n) + l).join('\n')
192
+ return [
193
+ `import js from '@eslint/js'`,
194
+ `import globals from 'globals'`,
195
+ hasTs ? `import tseslint from 'typescript-eslint'` : '',
196
+ hasReact ? `import pluginReact from 'eslint-plugin-react'` : '',
197
+ hasReact ? `import reactHooks from 'eslint-plugin-react-hooks'` : '',
198
+ hasImp ? `import importPlugin from 'eslint-plugin-import'` : '',
199
+ ``,
200
+ `export default [`,
201
+ ` { ignores: ['node_modules'] },`,
202
+ hasTs ? ` ...tseslint.configs.strictTypeChecked.map(config => ({ ...config, files: ['**/*.{ts,tsx,mts}'] })),` : '',
203
+ ` js.configs.recommended,`,
204
+ ` {`,
205
+ ` files: ['**/*.{ts,tsx,mts}'],`,
206
+ ` languageOptions: {`,
207
+ ` globals: { ...globals.browser, ...globals.node },`,
208
+ hasTs ? ` parserOptions: {\n projectService: true,\n tsconfigRootDir: import.meta.dirname,\n },` : '',
209
+ ` },`,
210
+ hasCore ? ` rules: ${ind(core, 4)},` : '',
211
+ ` },`,
212
+ hasTs ? ` {\n files: ['**/*.{ts,tsx,mts}'],\n rules: ${ind(ts, 4)},\n },` : '',
213
+ hasReact ? ` pluginReact.configs.flat['jsx-runtime'],` : '',
214
+ hasReact ? ` { settings: { react: { version: 'detect' } } },` : '',
215
+ hasReact ? ` {\n plugins: { 'react-hooks': reactHooks },\n rules: ${ind(reactR, 4)},\n },` : '',
216
+ hasImp ? ` {\n plugins: { import: importPlugin },\n rules: ${ind(imp, 4)},\n },` : '',
217
+ `]`,
218
+ ].filter(Boolean).join('\n')
219
+ }
220
+
221
+ const clone = process.argv.includes('--clone')
222
+ if (clone) {
223
+ const src = path.join(process.env.HOME!, '.lint-preset.json')
224
+ const data = JSON.parse(fs.readFileSync(src, 'utf8')) as CheckpointData
225
+ const config = generateConfig(data.rules)
226
+ fs.writeFileSync(data.outFile, config, 'utf8')
227
+ process.stdout.write(`${tag('done', c.green)} Written to ${bold(data.outFile)}\n`)
228
+ fs.closeSync(ttyFd)
229
+ process.exit(0)
230
+ }
231
+
232
+ const preset = process.argv.includes('--preset')
233
+ if (preset) {
234
+ const res = await fetch('https://raw.githubusercontent.com/LenixDev/lenix/refs/heads/main/bin/lint-preset.json')
235
+ const data = await res.json() as CheckpointData
236
+ const config = generateConfig(data.rules)
237
+ fs.writeFileSync('eslint.config.mts', config, 'utf8')
238
+ process.stdout.write(`${tag('done', c.green)} Written to ${bold('eslint.config.mts')}\n`)
239
+ process.stdout.write(`${tag('notice', c.cyan)} Run: ${bold('npm add -D @eslint/js globals typescript-eslint')}\n`)
240
+ fs.closeSync(ttyFd)
241
+ process.exit(0)
242
+ }
243
+
244
+ async function main() {
245
+ process.stdout.write('\x1Bc')
246
+ process.stdout.write(title('╔═══════════════════════╗\n'))
247
+ process.stdout.write(title('║ ESLint Config ║\n'))
248
+ process.stdout.write(title('╚═══════════════════════╝\n'))
249
+ process.stdout.write(dim('\nWalks through every rule across all plugins.\n'))
250
+ process.stdout.write(dim('o=off w=warn e=error s/Enter=skip q=save & exit\n\n'))
251
+
252
+ const checkpoint = loadCheckpoint()
253
+ const state: State = { allRules: {}, completedSections: new Set(), outFile: 'eslint.config.mts', pausedSection: null, pausedAt: 0 }
254
+
255
+ if (checkpoint) {
256
+ process.stdout.write(`${tag('found', c.yellow)} Saved progress detected (${Object.keys(checkpoint.rules).length} rules configured).\n`)
257
+ const resume = readLine(pr('Resume? [Y/n]: ')).trim().toLowerCase()
258
+ if (resume !== 'n') {
259
+ state.outFile = checkpoint.outFile
260
+ state.allRules = checkpoint.rules
261
+ state.completedSections = new Set(checkpoint.completedSections)
262
+ state.pausedSection = checkpoint.pausedSection ?? null
263
+ state.pausedAt = checkpoint.pausedAt ?? 0
264
+ }
265
+ }
266
+
267
+ const coreByCategory = await getCoreRules()
268
+ for (const [cat, rules] of Object.entries(coreByCategory))
269
+ askSection(`Core — ${cat}`, rules, state)
270
+
271
+ const tsAll = buildPluginRules(((tseslint.plugin as unknown) as PluginWithRules).rules ?? {}, '@typescript-eslint')
272
+ askSection('TypeScript', tsAll.filter(r => !r.requiresTypeChecking), state)
273
+ askSection('TypeScript — type-aware', tsAll.filter(r => r.requiresTypeChecking), state)
274
+ // askSection('React', buildPluginRules((react.rules ?? {}) as Record<string, any>, 'react'), state)
275
+ // askSection('React Hooks', buildPluginRules((reactHooks.rules ?? {}) as Record<string, any>, 'react-hooks'), state)
276
+ // askSection('Imports', buildPluginRules((importPlugin.rules ?? {}) as Record<string, any>, 'import'), state)
277
+
278
+ const config = generateConfig(state.allRules)
279
+ let outFile = state.outFile
280
+ if (fs.existsSync(outFile)) {
281
+ const overwrite = readLine(pr(`\n${outFile} already exists. Overwrite? [y/N]: `)).trim().toLowerCase()
282
+ if (overwrite !== 'y')
283
+ outFile = readLine(pr('Enter new filename: ')).trim() || outFile
284
+ }
285
+ fs.writeFileSync(outFile, config, 'utf8')
286
+ // if (fs.existsSync(CHECKPOINT)) fs.unlinkSync(CHECKPOINT)
287
+ process.stdout.write(`\n${tag('done', c.green)} Written to ${bold(outFile)}\n`)
288
+ process.stdout.write(dim(`Rules configured: ${Object.keys(state.allRules).length}\n`))
289
+ const hasTs = Object.keys(state.allRules).some(k => k.startsWith('@typescript-eslint/'))
290
+ const hasReact = Object.keys(state.allRules).some(k => k.startsWith('react/') || k.startsWith('react-hooks/'))
291
+ const hasImp = Object.keys(state.allRules).some(k => k.startsWith('import/'))
292
+ const deps = [
293
+ '@eslint/js',
294
+ 'globals',
295
+ hasTs ? 'typescript-eslint' : '',
296
+ hasReact ? 'eslint-plugin-react eslint-plugin-react-hooks' : '',
297
+ hasImp ? 'eslint-plugin-import' : '',
298
+ ].filter(Boolean).join(' ')
299
+ if (hasTs) {
300
+ const tsconfig = {
301
+ compilerOptions: {
302
+ target: 'ESNext',
303
+ module: 'NodeNext',
304
+ moduleResolution: 'NodeNext',
305
+ strict: true,
306
+ },
307
+ include: ['**/*.ts', '**/*.mts', '**/*.tsx'],
308
+ exclude: ['node_modules'],
309
+ }
310
+ if (!fs.existsSync('tsconfig.json'))
311
+ fs.writeFileSync('tsconfig.json', JSON.stringify(tsconfig, null, 2), 'utf8')
312
+ else
313
+ process.stdout.write(dim('tsconfig.json already exists, skipping.\n'))
314
+ }
315
+
316
+ const pm = fs.existsSync('pnpm-lock.yaml') ? 'pnpm' : fs.existsSync('yarn.lock') ? 'yarn' : 'npm'
317
+ process.stdout.write(dim(`Run: ${pm} add -D ${deps}\n\n`))
318
+ fs.closeSync(ttyFd)
319
+ process.exit(0)
320
+ }
321
+
322
+ main().catch(e => { console.error(e); process.exit(1) })
@@ -0,0 +1,10 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "strict": true,
7
+ "outDir": "dist"
8
+ },
9
+ "include": ["lint.mts"]
10
+ }
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "lenix",
3
- "version": "1.2.2",
3
+ "version": "1.3.0",
4
4
  "description": "Helpful modules for the software engineers",
5
5
  "author": "Lenix",
6
6
  "license": "GPL-3.0",
7
+ "type": "module",
7
8
  "keywords": [
8
9
  "lenix",
9
10
  "modules",
@@ -12,7 +13,9 @@
12
13
  "luarocks",
13
14
  "fivem",
14
15
  "fxmanifest",
15
- "api"
16
+ "api",
17
+ "oop",
18
+ "class"
16
19
  ],
17
20
  "repository": {
18
21
  "type": "git",
@@ -21,18 +24,30 @@
21
24
  "main": "./dist/index.js",
22
25
  "types": "./dist/index.d.ts",
23
26
  "bin": {
24
- "mac-lua-modules": "bin/mac-lua_modules.sh"
27
+ "mac-lua-modules": "bin/mac-lua_modules.sh",
28
+ "lenix": "./bin/dist/lint.mjs"
25
29
  },
26
30
  "files": [
27
31
  "dist",
28
32
  "bin"
29
33
  ],
34
+ "dependencies": {
35
+ "@eslint/js": "^10.0.1",
36
+ "eslint": "^10.0.3",
37
+ "eslint-plugin-import": "^2.32.0",
38
+ "eslint-plugin-react": "^7.37.5",
39
+ "eslint-plugin-react-hooks": "^7.0.1",
40
+ "globals": "^17.4.0",
41
+ "typescript-eslint": "^8.57.0"
42
+ },
30
43
  "devDependencies": {
44
+ "@types/eslint": "^9.6.1",
45
+ "@types/node": "^20.7.1",
31
46
  "tsc-alias": "^1.8.16",
32
- "typescript": "^5.3.3",
33
- "@types/node": "^20.7.1"
47
+ "tsx": "^4.21.0",
48
+ "typescript": "^5.3.3"
34
49
  },
35
50
  "scripts": {
36
- "build": "tsc -p src/fxmanifest/tsconfig.json && tsc-alias"
51
+ "build": "tsc -p src/fxmanifest/tsconfig.json && tsc-alias && tsc -p bin/tsconfig.json"
37
52
  }
38
53
  }