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.
- package/README.md +21 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +1687 -0
- package/dist/constants-BRYEJmsZ.mjs +164 -0
- package/dist/eslint.d.mts +9 -0
- package/dist/eslint.mjs +366 -0
- package/dist/ignores-B1QoL99d.mjs +102 -0
- package/dist/index.d.mts +13 -0
- package/dist/index.mjs +2 -0
- package/dist/lintmax-types-CJ7VY33l.d.mts +69 -0
- package/dist/path-Cu_Nf2ct.mjs +168 -0
- package/dist/src-D4isRHIx.mjs +822 -0
- package/oxlintrc.json +118 -0
- package/package.json +81 -0
- package/tsconfig.json +27 -0
|
@@ -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 };
|
package/dist/eslint.mjs
ADDED
|
@@ -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 };
|
package/dist/index.d.mts
ADDED
|
@@ -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