@leyman/eslint-config 0.0.0-stage → 0.0.1

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,3 @@
1
+ import type { PackageJson } from './package-json-schema.js';
2
+ export declare const getInternalRegex: (packageJson: PackageJson) => RegExp;
3
+ //# sourceMappingURL=internal-regex.d.ts.map
@@ -0,0 +1,30 @@
1
+ const packageRegex = (packageName)=>{
2
+ const escaped = RegExp.escape(packageName);
3
+ return `${escaped}(?:$|/)`;
4
+ };
5
+ export const getInternalRegex = (packageJson)=>{
6
+ const internalPackages = new Set([
7
+ packageJson.name
8
+ ]);
9
+ for (const dependencies of [
10
+ packageJson.dependencies,
11
+ packageJson.devDependencies,
12
+ packageJson.optionalDependencies,
13
+ packageJson.peerDependencies
14
+ ]){
15
+ if (!dependencies) {
16
+ continue;
17
+ }
18
+ for (const [packageName, versionSpecifier] of Object.entries(dependencies)){
19
+ if (versionSpecifier.startsWith('workspace:')) {
20
+ internalPackages.add(packageName);
21
+ }
22
+ }
23
+ }
24
+ const packageRegexUnion = [
25
+ ...internalPackages
26
+ ].toSorted((a, b)=>a.localeCompare(b, 'en')).map((packageName)=>packageRegex(packageName)).join('|');
27
+ return new RegExp(`^(?:${packageRegexUnion})`, 'u');
28
+ };
29
+
30
+ //# sourceMappingURL=internal-regex.js.map
@@ -0,0 +1,12 @@
1
+ import type { ESLint } from 'eslint';
2
+ /**
3
+ * Restore the rule context members that ESLint 10 removed, for a plugin that still calls them.
4
+ *
5
+ * `eslint-plugin-react` has no release supporting ESLint 10 yet (jsx-eslint/eslint-plugin-react#4022),
6
+ * and some of its rules (e.g. `jsx-filename-extension`, `forward-ref-uses-ref`) throw without these.
7
+ *
8
+ * @param plugin - plugin whose rules call the removed members
9
+ * @returns copy of the plugin, with each rule given a context that has them
10
+ */
11
+ export declare const withLegacyRuleContext: (plugin: ESLint.Plugin) => ESLint.Plugin;
12
+ //# sourceMappingURL=legacy-rule-context.d.ts.map
@@ -0,0 +1,26 @@
1
+ const withLegacyMembers = (context)=>Object.freeze(Object.create(context, {
2
+ getCwd: {
3
+ value: ()=>context.cwd
4
+ },
5
+ getFilename: {
6
+ value: ()=>context.filename
7
+ },
8
+ getPhysicalFilename: {
9
+ value: ()=>context.physicalFilename
10
+ },
11
+ getSourceCode: {
12
+ value: ()=>context.sourceCode
13
+ }
14
+ }));
15
+ export const withLegacyRuleContext = (plugin)=>({
16
+ ...plugin,
17
+ rules: Object.fromEntries(Object.entries(plugin.rules).map(([name, rule])=>[
18
+ name,
19
+ {
20
+ ...rule,
21
+ create: (context)=>rule.create(withLegacyMembers(context))
22
+ }
23
+ ]))
24
+ });
25
+
26
+ //# sourceMappingURL=legacy-rule-context.js.map
@@ -0,0 +1,19 @@
1
+ import type { Linter } from 'eslint';
2
+ import type { PackageJson } from './package-json-schema.js';
3
+ /**
4
+ * The oldest Node.js major version a package supports: the major version of `engines.node` (`>=22`, `>=24.1.0`...),
5
+ * or the running version's when there is none, as private packages run on the toolchain only.
6
+ *
7
+ * @param packageJson - Loaded `package.json` file
8
+ * @returns Node.js major version
9
+ * @throws {Error} when `engines.node` doesn't start with `>=`
10
+ */
11
+ export declare const getNodeFloor: (packageJson: PackageJson) => number;
12
+ /**
13
+ * Turns off each rule that suggests an API the package's oldest supported Node.js doesn't have.
14
+ *
15
+ * @param packageJson - Loaded `package.json` file
16
+ * @returns The rules to turn off
17
+ */
18
+ export declare const nodeFloorRules: (packageJson: PackageJson) => Linter.RulesRecord;
19
+ //# sourceMappingURL=node-floor.d.ts.map
@@ -0,0 +1,32 @@
1
+ const apiRules = {
2
+ 'unicorn/prefer-error-is-error': 24,
3
+ 'unicorn/prefer-iterator-concat': 26,
4
+ 'unicorn/prefer-iterator-zip': null,
5
+ 'unicorn/prefer-promise-try': 23,
6
+ 'unicorn/prefer-regexp-escape': 24,
7
+ 'unicorn/prefer-uint8array-base64': 25,
8
+ 'unicorn/prefer-uint8array-hex': 25
9
+ };
10
+ export const getNodeFloor = (packageJson)=>{
11
+ const range = packageJson.engines?.node;
12
+ if (range === undefined) {
13
+ return Number(process.versions.node.split('.', 1)[0]);
14
+ }
15
+ const major = /^>=\s*(?<major>\d+)/v.exec(range)?.groups?.major;
16
+ if (major === undefined) {
17
+ throw new Error(`engines.node must be ">=<version>" to name a floor, not "${range}"`);
18
+ }
19
+ return Number(major);
20
+ };
21
+ export const nodeFloorRules = (packageJson)=>{
22
+ const floor = getNodeFloor(packageJson);
23
+ const rules = {};
24
+ for (const [rule, since] of Object.entries(apiRules)){
25
+ if (since === null || since > floor) {
26
+ rules[rule] = 'off';
27
+ }
28
+ }
29
+ return rules;
30
+ };
31
+
32
+ //# sourceMappingURL=node-floor.js.map
@@ -0,0 +1,56 @@
1
+ type Dependencies = Record<string, string>;
2
+ export interface PackageJson {
3
+ name: string;
4
+ engines?: {
5
+ node?: string;
6
+ };
7
+ dependencies?: Dependencies;
8
+ devDependencies?: Dependencies;
9
+ optionalDependencies?: Dependencies;
10
+ peerDependencies?: Dependencies;
11
+ }
12
+ export declare const packageJsonSchema: {
13
+ type: string;
14
+ properties: {
15
+ name: {
16
+ type: string;
17
+ };
18
+ engines: {
19
+ type: string;
20
+ properties: {
21
+ node: {
22
+ type: string;
23
+ };
24
+ };
25
+ };
26
+ dependencies: {
27
+ type: string;
28
+ additionalProperties: {
29
+ type: string;
30
+ };
31
+ };
32
+ devDependencies: {
33
+ type: string;
34
+ additionalProperties: {
35
+ type: string;
36
+ };
37
+ };
38
+ optionalDependencies: {
39
+ type: string;
40
+ additionalProperties: {
41
+ type: string;
42
+ };
43
+ };
44
+ peerDependencies: {
45
+ type: string;
46
+ additionalProperties: {
47
+ type: string;
48
+ };
49
+ };
50
+ };
51
+ required: string[];
52
+ };
53
+ type PackageJsonAsserter = (packageJson: unknown) => asserts packageJson is PackageJson;
54
+ export declare const assertIsPackageJson: PackageJsonAsserter;
55
+ export {};
56
+ //# sourceMappingURL=package-json-schema.d.ts.map
@@ -0,0 +1,42 @@
1
+ import { Ajv2020 } from 'ajv/dist/2020.js';
2
+ const dependenciesSchema = {
3
+ type: 'object',
4
+ additionalProperties: {
5
+ type: 'string'
6
+ }
7
+ };
8
+ export const packageJsonSchema = {
9
+ type: 'object',
10
+ properties: {
11
+ name: {
12
+ type: 'string'
13
+ },
14
+ engines: {
15
+ type: 'object',
16
+ properties: {
17
+ node: {
18
+ type: 'string'
19
+ }
20
+ }
21
+ },
22
+ dependencies: dependenciesSchema,
23
+ devDependencies: dependenciesSchema,
24
+ optionalDependencies: dependenciesSchema,
25
+ peerDependencies: dependenciesSchema
26
+ },
27
+ required: [
28
+ 'name'
29
+ ]
30
+ };
31
+ const ajv = new Ajv2020({
32
+ strict: true
33
+ });
34
+ const validator = ajv.compile(packageJsonSchema);
35
+ export const assertIsPackageJson = (packageJson)=>{
36
+ validator(packageJson);
37
+ if (validator.errors) {
38
+ throw new Error(`Not a valid package.json file: ${JSON.stringify(validator.errors, null, 2)}`);
39
+ }
40
+ };
41
+
42
+ //# sourceMappingURL=package-json-schema.js.map
@@ -0,0 +1,15 @@
1
+ import type { ESLint, Linter } from 'eslint';
2
+ /**
3
+ * Turn on a package's ESLint plugin (e.g. `generated-signature/eslint`) with its recommended rules.
4
+ *
5
+ * Plugins are passed in by each project's `eslint.config.js` rather than imported here: every
6
+ * package devDepends on this config, so depending on a package's plugin would be circular.
7
+ *
8
+ * @param plugin - plugin with `meta.namespace` and a flat `configs.recommended`
9
+ * @param files - file patterns to apply the rules to
10
+ * @param [ignores] - file patterns to leave out, e.g. tests
11
+ * @returns flat config entry
12
+ * @throws {TypeError} if the plugin lacks a namespace or a flat recommended config
13
+ */
14
+ export declare const packagePluginConfig: (plugin: ESLint.Plugin, files: readonly string[], ignores?: readonly string[]) => Linter.Config;
15
+ //# sourceMappingURL=package-plugins.d.ts.map
@@ -0,0 +1,24 @@
1
+ export const packagePluginConfig = (plugin, files, ignores = [])=>{
2
+ const namespace = plugin.meta?.namespace ?? '';
3
+ if (namespace === '') {
4
+ throw new TypeError(`ESLint plugin ${plugin.meta?.name ?? '<unnamed>'} has no meta.namespace`);
5
+ }
6
+ const recommended = plugin.configs?.recommended;
7
+ if (!recommended || Array.isArray(recommended) || 'extends' in recommended) {
8
+ throw new TypeError(`ESLint plugin ${namespace} has no flat configs.recommended object`);
9
+ }
10
+ return {
11
+ files: [
12
+ ...files
13
+ ],
14
+ ignores: [
15
+ ...ignores
16
+ ],
17
+ plugins: {
18
+ [namespace]: plugin
19
+ },
20
+ rules: recommended.rules
21
+ };
22
+ };
23
+
24
+ //# sourceMappingURL=package-plugins.js.map
@@ -0,0 +1,14 @@
1
+ import type { ESLint } from 'eslint';
2
+ export { default as eslintConfig } from '@eslint/js';
3
+ export declare const commentsPlugin: ESLint.Plugin;
4
+ export declare const prettierConfig: ESLint.Plugin;
5
+ export declare const importPlugin: ESLint.Plugin;
6
+ export declare const jsDocPlugin: ESLint.Plugin;
7
+ export declare const jsxA11yPlugin: ESLint.Plugin;
8
+ export declare const nodePlugin: ESLint.Plugin;
9
+ export declare const reactPlugin: ESLint.Plugin;
10
+ export declare const reactHooksPlugin: ESLint.Plugin;
11
+ export declare const regexpPlugin: ESLint.Plugin;
12
+ export declare const sonarPlugin: ESLint.Plugin;
13
+ export declare const unicornPlugin: ESLint.Plugin;
14
+ //# sourceMappingURL=plugins.d.ts.map
@@ -0,0 +1,26 @@
1
+ import rawCommentsPlugin from '@eslint-community/eslint-plugin-eslint-comments';
2
+ import rawPrettierConfig from 'eslint-config-prettier';
3
+ import rawImportPlugin from 'eslint-plugin-import-x';
4
+ import rawJsDocPlugin from 'eslint-plugin-jsdoc';
5
+ import rawJsxA11yPlugin from 'eslint-plugin-jsx-a11y';
6
+ import rawNodePlugin from 'eslint-plugin-n';
7
+ import rawReactPlugin from 'eslint-plugin-react';
8
+ import rawReactHooksPlugin from 'eslint-plugin-react-hooks';
9
+ import rawRegexpPlugin from 'eslint-plugin-regexp';
10
+ import rawSonarPlugin from 'eslint-plugin-sonarjs';
11
+ import rawUnicornPlugin from 'eslint-plugin-unicorn';
12
+ import { withLegacyRuleContext } from './legacy-rule-context.js';
13
+ export { default as eslintConfig } from '@eslint/js';
14
+ export const commentsPlugin = rawCommentsPlugin;
15
+ export const prettierConfig = rawPrettierConfig;
16
+ export const importPlugin = rawImportPlugin;
17
+ export const jsDocPlugin = rawJsDocPlugin;
18
+ export const jsxA11yPlugin = rawJsxA11yPlugin;
19
+ export const nodePlugin = rawNodePlugin;
20
+ export const reactPlugin = withLegacyRuleContext(rawReactPlugin);
21
+ export const reactHooksPlugin = rawReactHooksPlugin;
22
+ export const regexpPlugin = rawRegexpPlugin;
23
+ export const sonarPlugin = rawSonarPlugin;
24
+ export const unicornPlugin = rawUnicornPlugin;
25
+
26
+ //# sourceMappingURL=plugins.js.map
package/package.json CHANGED
@@ -1,6 +1,84 @@
1
1
  {
2
2
  "name": "@leyman/eslint-config",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.0.1",
4
+ "description": "Strict, opinionated ESLint flat config for TypeScript ESM packages, generated from each package's package.json: nearly every rule on, every exception explained.",
5
+ "engines": {
6
+ "node": ">=24.15.0"
7
+ },
8
+ "type": "module",
9
+ "author": "JacobLey",
10
+ "license": "MIT",
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "git+https://github.com/JacobLey/leyman.git",
14
+ "directory": "leyman/eslint-config"
15
+ },
16
+ "homepage": "https://github.com/JacobLey/leyman/tree/main/leyman/eslint-config#readme",
17
+ "bugs": {
18
+ "url": "https://github.com/JacobLey/leyman/issues"
19
+ },
20
+ "keywords": [
21
+ "eslint",
22
+ "eslint-config",
23
+ "eslintconfig",
24
+ "flat-config",
25
+ "typescript",
26
+ "typescript-eslint",
27
+ "esm",
28
+ "strict",
29
+ "opinionated",
30
+ "biome"
31
+ ],
32
+ "sideEffects": false,
33
+ "exports": {
34
+ ".": "./dist/eslint-config.js",
35
+ "./package-json-schema": "./dist/lib/package-json-schema.js"
36
+ },
37
+ "files": [
38
+ "dist",
39
+ "!dist/**/*.map",
40
+ "!dist/tsconfig.tsbuildinfo"
41
+ ],
42
+ "dependencies": {
43
+ "@eslint-community/eslint-plugin-eslint-comments": "^4.8.1",
44
+ "@eslint/js": "^10.0.1",
45
+ "@typescript-eslint/eslint-plugin": "^8.72.0",
46
+ "@typescript-eslint/parser": "^8.72.0",
47
+ "@typescript-eslint/utils": "^8.72.0",
48
+ "ajv": "^8.20.0",
49
+ "eslint-config-prettier": "^10.1.8",
50
+ "eslint-plugin-import-x": "^4.17.1",
51
+ "eslint-plugin-jsdoc": "^65.4.2",
52
+ "eslint-plugin-jsx-a11y": "^6.10.2",
53
+ "eslint-plugin-n": "^18.5.0",
54
+ "eslint-plugin-react": "^7.37.5",
55
+ "eslint-plugin-react-hooks": "^7.1.1",
56
+ "eslint-plugin-regexp": "^3.3.1",
57
+ "eslint-plugin-sonarjs": "^4.2.2",
58
+ "eslint-plugin-unicorn": "^79.0.0",
59
+ "globals": "^17.12.0"
60
+ },
61
+ "peerDependencies": {
62
+ "eslint": "^10.13.0",
63
+ "typescript": "*"
64
+ },
65
+ "devDependencies": {
66
+ "@biomejs/biome": "^2.6.4",
67
+ "@leyman/configs": "^0.0.1",
68
+ "@swc/cli": "^0.8.1",
69
+ "@swc/core": "^1.16.20",
70
+ "@types/chai": "^5.2.3",
71
+ "@types/eslint": "^9.6.1",
72
+ "@types/mocha": "^10.0.10",
73
+ "@types/node": "^24.19.2",
74
+ "@typescript/native": "npm:typescript@^7.0.2",
75
+ "c8": "^12.0.0",
76
+ "chai": "^6.2.2",
77
+ "eslint": "^10.13.0",
78
+ "mocha": "^12.0.3",
79
+ "typescript": "npm:@typescript/typescript6@^6.0.2"
80
+ },
81
+ "imports": {
82
+ "#internal/*": "./dist/*"
83
+ }
6
84
  }