lintmax 0.1.44

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,164 @@
1
+ //#region src/constants.ts
2
+ const BIOME_RULES_OFF = [
3
+ "noBarrelFile",
4
+ "noConditionalExpect",
5
+ "noConsole",
6
+ "noDefaultExport",
7
+ "noExcessiveCognitiveComplexity",
8
+ "noExcessiveLinesPerFile",
9
+ "noExcessiveLinesPerFunction",
10
+ "noExportedImports",
11
+ "noImplicitBoolean",
12
+ "noImportantStyles",
13
+ "noInlineStyles",
14
+ "noLeakedRender",
15
+ "useConsistentCurlyBraces",
16
+ "useConsistentMemberAccessibility",
17
+ "useExpect",
18
+ "noJsxLiterals",
19
+ "noJsxPropsBind",
20
+ "noMagicNumbers",
21
+ "noNestedTernary",
22
+ "noNodejsModules",
23
+ "noProcessGlobal",
24
+ "noReactNativeRawText",
25
+ "noReactSpecificProps",
26
+ "noSecrets",
27
+ "noSolidDestructuredProps",
28
+ "noTernary",
29
+ "noUndeclaredDependencies",
30
+ "noUnresolvedImports",
31
+ "useBaseline",
32
+ "useBlockStatements",
33
+ "useComponentExportOnlyModules",
34
+ "useDestructuring",
35
+ "useConsistentTestIt",
36
+ "useExplicitReturnType",
37
+ "useExplicitType",
38
+ "useImportExtensions",
39
+ "useNamingConvention",
40
+ "useQwikValidLexicalScope",
41
+ "useSingleVarDeclarator",
42
+ "useSolidForComponent",
43
+ "useSortedClasses"
44
+ ];
45
+ const BIOME_IGNORE_PATTERNS = [
46
+ "!!**/node_modules",
47
+ "!!**/.build",
48
+ "!!**/.cache",
49
+ "!!**/.source",
50
+ "!!**/.next",
51
+ "!!**/.output",
52
+ "!!**/.turbo",
53
+ "!!**/.venv",
54
+ "!!**/.wxt",
55
+ "!!**/_generated",
56
+ "!!**/Android",
57
+ "!!**/Darwin",
58
+ "!!**/dist",
59
+ "!!**/maestro",
60
+ "!!**/module_bindings",
61
+ "!!**/playwright-report",
62
+ "!!**/test-results",
63
+ "!!**/*.xcassets"
64
+ ];
65
+ const DEFAULT_SHARED_IGNORE_PATTERNS = [
66
+ "_generated/**",
67
+ ".next/**",
68
+ ".source/**",
69
+ "*.cjs",
70
+ "dist/**",
71
+ "generated/**",
72
+ "module_bindings/**",
73
+ "next-env.d.ts",
74
+ "readonly/**",
75
+ "expo/**/babel.config.js",
76
+ "expo/**/global.css",
77
+ "expo/**/metro.config.js",
78
+ "expo/**/uniwind-env.d.ts",
79
+ "expo/**/uniwind-types.d.ts"
80
+ ].map((p) => p.startsWith("**/") ? p : `**/${p}`);
81
+ const ESLINT_TEST_FILE_PATTERNS = [
82
+ "**/*.test.ts",
83
+ "**/*.test.tsx",
84
+ "**/*.spec.ts",
85
+ "**/*.spec.tsx",
86
+ "**/__tests__/**/*.ts",
87
+ "**/__tests__/**/*.tsx"
88
+ ];
89
+ const BIOME_PATTERN_RULE_OVERRIDES = [
90
+ {
91
+ includes: ["**/expo/**"],
92
+ rules: ["noProcessEnv"]
93
+ },
94
+ {
95
+ includes: ["**/maestro/**"],
96
+ rules: ["noAwaitInLoops"]
97
+ },
98
+ {
99
+ includes: ["**/auth.config.js"],
100
+ rules: [
101
+ "noUndeclaredEnvVars",
102
+ "noUndeclaredVariables",
103
+ "noProcessEnv"
104
+ ]
105
+ },
106
+ {
107
+ includes: ["**/*Script.ts", "**/*.generated.ts"],
108
+ rules: ["noTemplateCurlyInString"]
109
+ },
110
+ {
111
+ includes: ESLINT_TEST_FILE_PATTERNS,
112
+ rules: [
113
+ "noAwaitInLoops",
114
+ "noDelete",
115
+ "noEmptyBlockStatements",
116
+ "noProcessEnv",
117
+ "noUndeclaredVariables",
118
+ "useAwait",
119
+ "useLiteralKeys",
120
+ "useTopLevelRegex"
121
+ ]
122
+ }
123
+ ];
124
+ const OXLINT_PATTERN_RULE_OVERRIDES = [
125
+ {
126
+ files: ["**/expo/**/*.tsx", "**/expo/**/*.ts"],
127
+ rules: { "react-perf/jsx-no-new-object-as-prop": "off" }
128
+ },
129
+ {
130
+ files: ["**/convex/**/*.ts"],
131
+ rules: { "eslint-plugin-unicorn/filename-case": "off" }
132
+ },
133
+ {
134
+ files: ["**/*Script.ts", "**/*.generated.ts"],
135
+ rules: { "eslint/no-template-curly-in-string": "off" }
136
+ },
137
+ {
138
+ files: [...ESLINT_TEST_FILE_PATTERNS],
139
+ rules: {
140
+ "eslint-plugin-jest/valid-expect": "off",
141
+ "eslint-plugin-promise/param-names": "off",
142
+ "eslint-plugin-unicorn/consistent-function-scoping": "off",
143
+ "eslint-plugin-unicorn/no-array-for-each": "off",
144
+ "eslint-plugin-unicorn/no-immediate-mutation": "off",
145
+ "eslint-plugin-unicorn/no-useless-promise-resolve-reject": "off",
146
+ "eslint-plugin-unicorn/number-literal-case": "off",
147
+ "eslint-plugin-vitest/prefer-called-times": "off",
148
+ "no-await-in-loop": "off",
149
+ "no-control-regex": "off"
150
+ }
151
+ }
152
+ ];
153
+ const TAILWIND_ENTRY_CANDIDATES = [
154
+ "ui/src/styles/globals.css",
155
+ "src/styles/globals.css",
156
+ "app/globals.css",
157
+ "web/global.css",
158
+ "styles/globals.css",
159
+ "global.css"
160
+ ];
161
+ const SHARED_OVERRIDE_SYMBOL_KEY = "lintmax.sharedOverride";
162
+ const OXLINT_CLI_ALLOW = ["eslint/no-underscore-dangle"];
163
+ //#endregion
164
+ export { ESLINT_TEST_FILE_PATTERNS as a, SHARED_OVERRIDE_SYMBOL_KEY as c, DEFAULT_SHARED_IGNORE_PATTERNS as i, TAILWIND_ENTRY_CANDIDATES as l, BIOME_PATTERN_RULE_OVERRIDES as n, OXLINT_CLI_ALLOW as o, BIOME_RULES_OFF as r, OXLINT_PATTERN_RULE_OVERRIDES as s, BIOME_IGNORE_PATTERNS as t };
@@ -0,0 +1,9 @@
1
+ import { n as EslintOptions } from "./lintmax-types-CJ7VY33l.mjs";
2
+ import * as _$eslint_config0 from "eslint/config";
3
+ import { defineConfig } from "eslint/config";
4
+
5
+ //#region src/eslint.d.ts
6
+ declare const eslintFactory: (options?: EslintOptions) => ReturnType<typeof defineConfig>;
7
+ declare const defaultConfig: _$eslint_config0.Config[];
8
+ //#endregion
9
+ export { type EslintOptions, defaultConfig as default, eslintFactory as eslint };
@@ -0,0 +1,366 @@
1
+ import { a as ESLINT_TEST_FILE_PATTERNS, c as SHARED_OVERRIDE_SYMBOL_KEY, i as DEFAULT_SHARED_IGNORE_PATTERNS, l as TAILWIND_ENTRY_CANDIDATES } from "./constants-BRYEJmsZ.mjs";
2
+ import { c as findUnknownRules, d as normalizeObjectListInput, f as normalizePathListInput, g as warnToError, i as joinPath, l as isRecord, m as normalizeTailwindOption, p as normalizeRulesOffInput, r as isAbsolutePath } from "./path-Cu_Nf2ct.mjs";
3
+ import { existsSync } from "node:fs";
4
+ import eslintReact from "@eslint-react/eslint-plugin";
5
+ import { includeIgnoreFile } from "@eslint/compat";
6
+ import eslint from "@eslint/js";
7
+ import nextPlugin from "@next/eslint-plugin-next";
8
+ import eslintPluginBetterTailwindcss from "eslint-plugin-better-tailwindcss";
9
+ import { configs } from "eslint-plugin-perfectionist";
10
+ import preferArrow from "eslint-plugin-prefer-arrow-functions";
11
+ import reactPlugin from "eslint-plugin-react";
12
+ import reactHooks from "eslint-plugin-react-hooks";
13
+ import turbo from "eslint-plugin-turbo";
14
+ import { defineConfig, globalIgnores } from "eslint/config";
15
+ import tseslint from "typescript-eslint";
16
+ //#region src/eslint.ts
17
+ const sharedOverrideMarker = Symbol.for(SHARED_OVERRIDE_SYMBOL_KEY);
18
+ const normalizeAppendInput = ({ append }) => {
19
+ const out = [];
20
+ for (const value of normalizeObjectListInput({
21
+ allowNonPlain: true,
22
+ label: "eslint.append",
23
+ value: append
24
+ })) out.push(value);
25
+ return out;
26
+ };
27
+ const validateEslintOptions = ({ options }) => {
28
+ if (!options) return;
29
+ if (options.ignores !== void 0) normalizePathListInput({
30
+ allowUndefined: true,
31
+ label: "eslint.ignores",
32
+ value: options.ignores
33
+ });
34
+ if (options.off !== void 0) normalizeRulesOffInput({
35
+ label: "eslint.off",
36
+ value: options.off
37
+ });
38
+ if (options.append !== void 0) normalizeAppendInput({ append: options.append });
39
+ const tailwindEntrySetting = normalizeTailwindOption({
40
+ label: "eslint.tailwind",
41
+ value: options.tailwind
42
+ });
43
+ if (typeof tailwindEntrySetting === "string") {
44
+ const root = options.tsconfigRootDir ?? process.cwd();
45
+ const resolved = isAbsolutePath(tailwindEntrySetting) ? tailwindEntrySetting : joinPath(root, tailwindEntrySetting);
46
+ if (!existsSync(resolved)) throw new Error(`eslint.tailwind file not found: ${resolved}. Use an existing path, set eslint.tailwind to false, or remove eslint.tailwind to use auto-detection.`);
47
+ }
48
+ if (options.tsconfigRootDir !== void 0 && typeof options.tsconfigRootDir !== "string") throw new Error("eslint.tsconfigRootDir must be a string");
49
+ };
50
+ const collectKnownEslintRuleNames = ({ appendConfigs, baseConfigs }) => {
51
+ const knownRuleNames = /* @__PURE__ */ new Set();
52
+ const seenConfigs = /* @__PURE__ */ new WeakSet();
53
+ const collectPluginRuleNames = ({ plugins }) => {
54
+ if (!isRecord(plugins)) return;
55
+ for (const [pluginName, plugin] of Object.entries(plugins)) if (isRecord(plugin) && isRecord(plugin.rules)) for (const ruleName of Object.keys(plugin.rules)) knownRuleNames.add(`${pluginName}/${ruleName}`);
56
+ };
57
+ const collectFromConfig = ({ config, includeRules }) => {
58
+ if (typeof config !== "object" || config === null) return;
59
+ if (seenConfigs.has(config)) return;
60
+ seenConfigs.add(config);
61
+ if (Array.isArray(config)) {
62
+ for (const entry of config) collectFromConfig({
63
+ config: entry,
64
+ includeRules
65
+ });
66
+ return;
67
+ }
68
+ if (!isRecord(config)) return;
69
+ if (includeRules && isRecord(config.rules)) for (const ruleName of Object.keys(config.rules)) knownRuleNames.add(ruleName);
70
+ if ("plugins" in config) collectPluginRuleNames({ plugins: config.plugins });
71
+ if ("extends" in config) collectFromConfig({
72
+ config: config.extends,
73
+ includeRules: true
74
+ });
75
+ };
76
+ for (const config of baseConfigs) collectFromConfig({
77
+ config,
78
+ includeRules: true
79
+ });
80
+ for (const config of appendConfigs) collectFromConfig({
81
+ config,
82
+ includeRules: false
83
+ });
84
+ return knownRuleNames;
85
+ };
86
+ const getSharedAppendConfig = ({ config }) => {
87
+ return config[sharedOverrideMarker] === true ? config : null;
88
+ };
89
+ const resolveTailwindEntry = ({ root, tailwind }) => {
90
+ const tailwindSetting = tailwind ?? true;
91
+ if (tailwindSetting === false) return;
92
+ if (typeof tailwindSetting === "string") return isAbsolutePath(tailwindSetting) ? tailwindSetting : joinPath(root, tailwindSetting);
93
+ const matches = [];
94
+ for (const candidate of TAILWIND_ENTRY_CANDIDATES) {
95
+ const resolved = joinPath(root, candidate);
96
+ if (existsSync(resolved)) matches.push(resolved);
97
+ }
98
+ if (matches.length <= 1) return matches[0];
99
+ const preferredOnAmbiguous = [joinPath(root, "ui/src/styles/globals.css")];
100
+ for (const preferred of preferredOnAmbiguous) if (matches.includes(preferred)) return preferred;
101
+ const relMatches = matches.map((path) => path.slice(root.length + 1));
102
+ throw new Error(`Multiple Tailwind entry files found: ${relMatches.join(", ")}. Set eslint.tailwind to an explicit path.`);
103
+ };
104
+ const tailwindRules = (entryPoint) => entryPoint ? eslintPluginBetterTailwindcss.configs["recommended-error"].rules : {};
105
+ const eslintFactory = (options) => {
106
+ validateEslintOptions({ options });
107
+ const opts = options ?? {};
108
+ const root = opts.tsconfigRootDir ?? process.cwd();
109
+ const configs$1 = [];
110
+ const gitignorePath = joinPath(root, ".gitignore");
111
+ const normalizedIgnores = normalizePathListInput({
112
+ allowUndefined: true,
113
+ label: "eslint.ignores",
114
+ value: opts.ignores
115
+ });
116
+ const tailwindEntry = resolveTailwindEntry({
117
+ root,
118
+ tailwind: opts.tailwind
119
+ });
120
+ const tailwindSettings = {};
121
+ if (tailwindEntry) tailwindSettings["better-tailwindcss"] = { entryPoint: tailwindEntry };
122
+ configs$1.push(globalIgnores([...DEFAULT_SHARED_IGNORE_PATTERNS, ...normalizedIgnores]));
123
+ try {
124
+ configs$1.push(includeIgnoreFile(gitignorePath));
125
+ } catch (error) {
126
+ if (error instanceof Error) {
127
+ const message = error.message.toLowerCase();
128
+ if (!(message.includes("enoent") || message.includes("no such file"))) throw error;
129
+ } else throw error;
130
+ }
131
+ configs$1.push(...defineConfig(configs["recommended-natural"], { ignores: ["**/postcss.config.mjs"] }, {
132
+ extends: [
133
+ eslint.configs.recommended,
134
+ eslint.configs.all,
135
+ ...tseslint.configs.all,
136
+ ...tseslint.configs.recommended,
137
+ ...tseslint.configs.recommendedTypeChecked,
138
+ ...tseslint.configs.stylisticTypeChecked,
139
+ eslintReact.configs["strict-type-checked"],
140
+ eslintReact.configs.recommended
141
+ ],
142
+ files: [
143
+ "**/*.js",
144
+ "**/*.ts",
145
+ "**/*.tsx"
146
+ ],
147
+ plugins: {
148
+ preferArrow,
149
+ turbo
150
+ },
151
+ rules: {
152
+ "@eslint-react/avoid-shorthand-boolean": "off",
153
+ "@eslint-react/avoid-shorthand-fragment": "off",
154
+ "@eslint-react/jsx-dollar": "error",
155
+ "@eslint-react/jsx-shorthand-boolean": "error",
156
+ "@eslint-react/jsx-shorthand-fragment": "error",
157
+ "@eslint-react/naming-convention/component-name": "error",
158
+ "@eslint-react/naming-convention/ref-name": "error",
159
+ "@eslint-react/no-duplicate-key": "error",
160
+ "@eslint-react/no-missing-component-display-name": "error",
161
+ "@eslint-react/no-missing-context-display-name": "off",
162
+ "@eslint-react/no-unnecessary-key": "error",
163
+ "@typescript-eslint/consistent-return": "off",
164
+ "@typescript-eslint/consistent-type-imports": ["error", {
165
+ fixStyle: "separate-type-imports",
166
+ prefer: "type-imports"
167
+ }],
168
+ "@typescript-eslint/explicit-function-return-type": "off",
169
+ "@typescript-eslint/explicit-member-accessibility": "off",
170
+ "@typescript-eslint/explicit-module-boundary-types": "off",
171
+ "@typescript-eslint/init-declarations": "off",
172
+ "@typescript-eslint/naming-convention": ["error", {
173
+ format: [
174
+ "camelCase",
175
+ "UPPER_CASE",
176
+ "PascalCase"
177
+ ],
178
+ selector: "variable"
179
+ }],
180
+ "@typescript-eslint/no-confusing-void-expression": "off",
181
+ "@typescript-eslint/no-floating-promises": "off",
182
+ "@typescript-eslint/no-magic-numbers": "off",
183
+ "@typescript-eslint/no-misused-promises": [2, { checksVoidReturn: { attributes: false } }],
184
+ "@typescript-eslint/no-unnecessary-condition": ["error", { allowConstantLoopConditions: true }],
185
+ "@typescript-eslint/no-unsafe-type-assertion": "off",
186
+ "@typescript-eslint/no-useless-default-assignment": "off",
187
+ "@typescript-eslint/prefer-destructuring": ["error", {
188
+ array: false,
189
+ object: true
190
+ }],
191
+ "@typescript-eslint/prefer-readonly-parameter-types": "off",
192
+ "@typescript-eslint/strict-boolean-expressions": "off",
193
+ camelcase: "off",
194
+ "capitalized-comments": [
195
+ "error",
196
+ "always",
197
+ { ignorePattern: "oxlint|biome|console|let|const|return|if|for|throw" }
198
+ ],
199
+ curly: ["error", "multi"],
200
+ "id-length": "off",
201
+ "max-lines": "off",
202
+ "max-lines-per-function": "off",
203
+ "max-statements": "off",
204
+ "new-cap": ["error", { capIsNewExceptionPattern: ".*" }],
205
+ "no-duplicate-imports": ["error", { allowSeparateTypeImports: true }],
206
+ "no-magic-numbers": "off",
207
+ "no-nested-ternary": "off",
208
+ "no-ternary": "off",
209
+ "no-undefined": "off",
210
+ "no-underscore-dangle": "off",
211
+ "one-var": ["error", "never"],
212
+ "perfectionist/sort-imports": ["error", {
213
+ newlinesBetween: 0,
214
+ order: "asc",
215
+ type: "natural"
216
+ }],
217
+ "perfectionist/sort-objects": "off",
218
+ "perfectionist/sort-variable-declarations": "off",
219
+ "preferArrow/prefer-arrow-functions": ["error", { returnStyle: "implicit" }],
220
+ "require-atomic-updates": "off",
221
+ "sort-imports": "off",
222
+ "sort-keys": "off",
223
+ "sort-vars": "off"
224
+ }
225
+ }, {
226
+ plugins: eslintReact.configs["strict-type-checked"].plugins,
227
+ rules: {
228
+ ...warnToError({
229
+ ...eslintReact.configs["strict-type-checked"].rules,
230
+ ...eslintReact.configs.recommended.rules
231
+ }),
232
+ "@eslint-react/dom/no-string-style-prop": "error",
233
+ "@eslint-react/dom/no-unknown-property": "error",
234
+ "@eslint-react/jsx-no-undef": "error"
235
+ }
236
+ }), ...defineConfig(reactHooks.configs.flat["recommended-latest"], {
237
+ files: ["**/*.ts", "**/*.tsx"],
238
+ ...reactPlugin.configs.flat.all,
239
+ ...reactPlugin.configs.flat["jsx-runtime"],
240
+ languageOptions: {
241
+ ...reactPlugin.configs.flat.all?.languageOptions,
242
+ ...reactPlugin.configs.flat["jsx-runtime"]?.languageOptions,
243
+ globals: { React: "writable" }
244
+ },
245
+ plugins: {
246
+ "better-tailwindcss": eslintPluginBetterTailwindcss,
247
+ react: reactPlugin
248
+ },
249
+ rules: {
250
+ ...reactPlugin.configs["jsx-runtime"].rules,
251
+ ...reactPlugin.configs.all.rules,
252
+ ...tailwindRules(tailwindEntry),
253
+ "better-tailwindcss/enforce-consistent-line-wrapping": "off",
254
+ "react-hooks/exhaustive-deps": "error",
255
+ "react-hooks/incompatible-library": "error",
256
+ "react-hooks/preserve-manual-memoization": "off",
257
+ "react-hooks/set-state-in-effect": "off",
258
+ "react-hooks/unsupported-syntax": "error",
259
+ "react/forbid-component-props": "off",
260
+ "react/function-component-definition": "off",
261
+ "react/jsx-child-element-spacing": "off",
262
+ "react/jsx-closing-bracket-location": "off",
263
+ "react/jsx-curly-newline": "off",
264
+ "react/jsx-filename-extension": ["error", { extensions: [".tsx"] }],
265
+ "react/jsx-handler-names": "off",
266
+ "react/jsx-indent": "off",
267
+ "react/jsx-indent-props": "off",
268
+ "react/jsx-max-depth": "off",
269
+ "react/jsx-max-props-per-line": "off",
270
+ "react/jsx-newline": "off",
271
+ "react/jsx-no-bind": "off",
272
+ "react/jsx-no-literals": "off",
273
+ "react/jsx-one-expression-per-line": "off",
274
+ "react/jsx-props-no-spreading": "off",
275
+ "react/jsx-sort-props": ["error", { ignoreCase: true }],
276
+ "react/no-multi-comp": "off",
277
+ "react/prefer-read-only-props": "off",
278
+ "react/require-default-props": "off"
279
+ },
280
+ settings: tailwindSettings
281
+ }), ...defineConfig({
282
+ files: ["**/*.ts", "**/*.tsx"],
283
+ plugins: { "@next/next": nextPlugin },
284
+ rules: {
285
+ ...warnToError({
286
+ ...nextPlugin.configs.recommended.rules,
287
+ ...nextPlugin.configs["core-web-vitals"].rules
288
+ }),
289
+ "@next/next/no-duplicate-head": "off",
290
+ "@next/next/no-html-link-for-pages": "off"
291
+ }
292
+ }), ...defineConfig({
293
+ files: [...ESLINT_TEST_FILE_PATTERNS],
294
+ rules: {
295
+ "@typescript-eslint/await-thenable": "off",
296
+ "@typescript-eslint/no-misused-promises": "off",
297
+ "@typescript-eslint/require-await": "off",
298
+ "@typescript-eslint/strict-void-return": "off",
299
+ "no-await-in-loop": "off",
300
+ "no-control-regex": "off"
301
+ }
302
+ }), ...defineConfig({
303
+ files: ["**/convex/**/*.test.ts", "**/test-utils/**/*.ts"],
304
+ rules: {
305
+ "@typescript-eslint/no-unsafe-argument": "off",
306
+ "@typescript-eslint/no-unsafe-assignment": "off",
307
+ "@typescript-eslint/no-unsafe-call": "off",
308
+ "@typescript-eslint/no-unsafe-member-access": "off",
309
+ "@typescript-eslint/no-unsafe-return": "off"
310
+ }
311
+ }), ...defineConfig({
312
+ files: ["**/*Script.ts", "**/*.generated.ts"],
313
+ rules: { "no-template-curly-in-string": "off" }
314
+ }), ...defineConfig({
315
+ files: ["**/auth.config.js"],
316
+ rules: { "no-undef": "off" }
317
+ }));
318
+ const appendConfigs = normalizeAppendInput({ append: opts.append });
319
+ const knownRuleNames = collectKnownEslintRuleNames({
320
+ appendConfigs,
321
+ baseConfigs: configs$1
322
+ });
323
+ const normalizedRules = normalizeRulesOffInput({
324
+ label: "eslint.off",
325
+ value: opts.off
326
+ });
327
+ if (normalizedRules) {
328
+ const unknownRules = findUnknownRules({
329
+ knownRules: knownRuleNames,
330
+ rules: normalizedRules
331
+ });
332
+ if (unknownRules.length > 0) throw new Error(`eslint.off contains unknown eslint rules: ${unknownRules.join(", ")}`);
333
+ const overrideRules = {};
334
+ for (const [key, value] of Object.entries(normalizedRules)) overrideRules[key] = value;
335
+ configs$1.push({ rules: overrideRules });
336
+ }
337
+ for (const config of appendConfigs) {
338
+ const shared = getSharedAppendConfig({ config });
339
+ const unknownRules = shared && config.rules ? findUnknownRules({
340
+ knownRules: knownRuleNames,
341
+ rules: config.rules
342
+ }) : [];
343
+ if (unknownRules.length > 0) throw new Error(`overrides.eslint contains unknown eslint rules: ${unknownRules.join(", ")}`);
344
+ const sanitized = shared === null ? config : (() => {
345
+ const out = {};
346
+ if (shared.files !== void 0) out.files = shared.files;
347
+ if (shared.rules !== void 0) out.rules = shared.rules;
348
+ return out;
349
+ })();
350
+ configs$1.push(sanitized.rules ? {
351
+ ...sanitized,
352
+ rules: warnToError(sanitized.rules)
353
+ } : sanitized);
354
+ }
355
+ configs$1.push({
356
+ languageOptions: { parserOptions: {
357
+ projectService: true,
358
+ tsconfigRootDir: root
359
+ } },
360
+ linterOptions: { reportUnusedDisableDirectives: true }
361
+ });
362
+ return defineConfig(...configs$1);
363
+ };
364
+ const defaultConfig = eslintFactory();
365
+ //#endregion
366
+ export { defaultConfig as default, eslintFactory as eslint };
@@ -0,0 +1,102 @@
1
+ import { a as ESLINT_TEST_FILE_PATTERNS, i as DEFAULT_SHARED_IGNORE_PATTERNS } from "./constants-BRYEJmsZ.mjs";
2
+ import { $, Glob } from "bun";
3
+ //#region src/ignores.ts
4
+ /** biome-ignore-all lint/nursery/useNamedCaptureGroup: not needed */
5
+ const eslintDisableRe = /eslint-disable(?:-next-line)?\s+(.+?)(?:\s*\*\/|\s*$)/gu;
6
+ const oxlintDisableRe = /oxlint-disable(?:-next-line)?\s+(.+?)(?:\s*\*\/|\s*$)/gu;
7
+ const biomeIgnoreRe = /biome-ignore(?:-all)?\s+([\w/]+)/gu;
8
+ const tsIgnoreRe = /@ts-(?:ignore|expect-error|nocheck)/gu;
9
+ const trailingCommentRe = /\s*--.*$/u;
10
+ const trailingCloseRe = /\s*\*\/$/u;
11
+ const tsInlineRe = /@ts-(?:ignore|expect-error|nocheck)/u;
12
+ const DANGEROUS_PATTERNS = [
13
+ "no-unsafe-",
14
+ "no-non-null-assertion",
15
+ "@ts-ignore",
16
+ "@ts-nocheck",
17
+ "noNonNullAssertion"
18
+ ];
19
+ const DANGEROUS_NON_TEST_PATTERNS = [
20
+ "@ts-expect-error",
21
+ "no-explicit-any",
22
+ "noExplicitAny"
23
+ ];
24
+ const isTestFile = (f) => ESLINT_TEST_FILE_PATTERNS.some((p) => new Glob(p).match(f));
25
+ const isDangerousEntry = (rule, files) => {
26
+ if (DANGEROUS_PATTERNS.some((p) => rule.includes(p))) return true;
27
+ if (DANGEROUS_NON_TEST_PATTERNS.some((p) => rule.includes(p))) return files.some((f) => !isTestFile(f));
28
+ return false;
29
+ };
30
+ const parseRules = (line, re) => {
31
+ const rules = [];
32
+ re.lastIndex = 0;
33
+ let match = re.exec(line);
34
+ while (match) {
35
+ const raw = match[1];
36
+ if (raw) for (const r of raw.split(",")) {
37
+ const trimmed = r.trim().replace(trailingCommentRe, "").replace(trailingCloseRe, "");
38
+ if (trimmed) rules.push(trimmed);
39
+ }
40
+ match = re.exec(line);
41
+ }
42
+ return rules;
43
+ };
44
+ const scanIgnores = async (cwd) => {
45
+ const ruleFiles = /* @__PURE__ */ new Map();
46
+ const add = (rule, file) => {
47
+ const existing = ruleFiles.get(rule);
48
+ if (existing) existing.add(file);
49
+ else ruleFiles.set(rule, new Set([file]));
50
+ };
51
+ const lines = (await $`rg -n "^\s*//\s*eslint-disable|^\s*/\*\s*eslint-disable|^\s*//\s*oxlint-disable|^\s*/\*\s*oxlint-disable|^\s*/\*\*\s*biome-ignore|^\s*//\s*@ts-ignore|^\s*//\s*@ts-expect-error|^\s*//\s*@ts-nocheck|^\s*/\*\s*@ts-nocheck" ${cwd} -g '*.ts' -g '*.tsx' -g '!node_modules' -g '!*.d.ts' ${DEFAULT_SHARED_IGNORE_PATTERNS.flatMap((p) => ["-g", `!${p}`])}`.quiet().nothrow()).stdout.toString().trim().split("\n").filter(Boolean);
52
+ for (const line of lines) {
53
+ const firstColon = line.indexOf(":");
54
+ const secondColon = line.indexOf(":", firstColon + 1);
55
+ const file = line.slice(0, firstColon).replace(`${cwd}/`, "");
56
+ const content = secondColon > firstColon ? line.slice(secondColon + 1) : line.slice(firstColon + 1);
57
+ for (const rule of parseRules(content, eslintDisableRe)) add(rule, file);
58
+ for (const rule of parseRules(content, oxlintDisableRe)) add(rule, file);
59
+ for (const rule of parseRules(content, biomeIgnoreRe)) add(rule, file);
60
+ tsIgnoreRe.lastIndex = 0;
61
+ if (tsIgnoreRe.test(content)) {
62
+ const tsMatch = tsInlineRe.exec(content);
63
+ if (tsMatch) add(tsMatch[0], file);
64
+ }
65
+ }
66
+ return ruleFiles;
67
+ };
68
+ const formatIgnores = (ruleFiles, verbose) => {
69
+ if (ruleFiles.size === 0) return "no suppressions";
70
+ const entries = [...ruleFiles.entries()].map(([rule, files]) => ({
71
+ count: files.size,
72
+ files: [...files],
73
+ rule
74
+ })).toSorted((a, b) => b.count - a.count);
75
+ const dangerous = entries.filter((e) => isDangerousEntry(e.rule, e.files));
76
+ const safe = entries.filter((e) => !isDangerousEntry(e.rule, e.files));
77
+ const total = entries.reduce((sum, e) => sum + e.count, 0);
78
+ const lines = [];
79
+ if (dangerous.length > 0) {
80
+ const dangerousCount = dangerous.reduce((sum, e) => sum + e.count, 0);
81
+ lines.push(`!! ${dangerousCount} dangerous suppressions — fix these:`, "");
82
+ const maxRule = Math.max(...dangerous.map((e) => e.rule.length));
83
+ for (const e of dangerous) {
84
+ lines.push(` ${e.rule.padEnd(maxRule)} ${String(e.count).padStart(3)}`);
85
+ if (verbose) for (const f of e.files) lines.push(` ${f}`);
86
+ }
87
+ lines.push("");
88
+ }
89
+ lines.push(`${total} suppressions across ${entries.length} rules`, "");
90
+ const maxRule = Math.max(...entries.map((e) => e.rule.length));
91
+ for (const e of safe) {
92
+ lines.push(` ${e.rule.padEnd(maxRule)} ${String(e.count).padStart(3)}`);
93
+ if (verbose) for (const f of e.files) lines.push(` ${f}`);
94
+ }
95
+ return lines.join("\n");
96
+ };
97
+ const runIgnores = async (verbose) => {
98
+ const ruleFiles = await scanIgnores(process.cwd());
99
+ process.stdout.write(`${formatIgnores(ruleFiles, verbose)}\n`);
100
+ };
101
+ //#endregion
102
+ export { runIgnores };
@@ -0,0 +1,13 @@
1
+ import { r as SyncOptions, t as EslintImportAppendEntry } from "./lintmax-types-CJ7VY33l.mjs";
2
+
3
+ //#region src/index.d.ts
4
+ declare const sync: (options?: SyncOptions) => Promise<void>;
5
+ declare const eslintImport: ({
6
+ files,
7
+ from,
8
+ ignores,
9
+ name
10
+ }: Omit<EslintImportAppendEntry, "$lintmax">) => EslintImportAppendEntry;
11
+ declare const defineConfig: <T extends SyncOptions>(options: T) => T;
12
+ //#endregion
13
+ export { type EslintImportAppendEntry, type SyncOptions, defineConfig, eslintImport, sync };
package/dist/index.mjs ADDED
@@ -0,0 +1,2 @@
1
+ import { n as eslintImport, r as sync, t as defineConfig } from "./src-D4isRHIx.mjs";
2
+ export { defineConfig, eslintImport, sync };