@venizia/dev-configs 0.2.0 → 0.2.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.
package/README.md CHANGED
@@ -63,21 +63,59 @@ earlier. Every IGNIS example does.
63
63
 
64
64
  | Entry point | What it gives |
65
65
  | :--- | :--- |
66
- | `@venizia/dev-configs` | `eslintConfigs` (flat config array), `prettierConfigs`, `BunCompiler` |
66
+ | `@venizia/dev-configs` | `eslintConfigs` (flat config array), `ReactEslintConfigs`, `prettierConfigs`, `BunCompiler` |
67
67
  | `@venizia/dev-configs/tsconfig.base.json` | Target and lib ES2024, `module` Node16, strict mode with `noImplicitAny`, `strictPropertyInitialization` and `useUnknownInCatchVariables` off, `noEmitOnError`, tests excluded |
68
68
  | `@venizia/dev-configs/tsconfig.common.json` | Extends the base and switches `module` and `moduleResolution` to `nodenext` |
69
69
 
70
70
  ## What each config sets
71
71
 
72
72
  `eslintConfigs` is `@minimaltech/eslint-node` (ESLint recommended, typescript-eslint, Prettier as a
73
- lint rule, naming conventions, `eslint-plugin-n`) plus three IGNIS rules:
73
+ lint rule, naming conventions, `curly`, `eslint-plugin-n`) plus these IGNIS rules:
74
74
 
75
75
  | Rule | Setting |
76
76
  | :--- | :--- |
77
77
  | `@typescript-eslint/no-explicit-any` | `off` |
78
- | `curly` | `['error', 'all']` |
78
+ | `@typescript-eslint/ban-ts-comment` | `error` - `@ts-expect-error` with a description is still allowed |
79
+ | `@typescript-eslint/no-namespace` | `error` |
79
80
  | `unicorn/switch-case-braces` | `['error', 'always']` |
80
81
 
82
+ `ReactEslintConfigs.create({ plugins })` is `eslintConfigs` plus what a React frontend needs. The
83
+ plugins come in as options, so a backend installs none of them:
84
+
85
+ ```js
86
+ // eslint.config.mjs
87
+ import { ReactEslintConfigs } from '@venizia/dev-configs';
88
+ import react from 'eslint-plugin-react';
89
+ import reactHooks from 'eslint-plugin-react-hooks';
90
+ import jsxA11y from 'eslint-plugin-jsx-a11y';
91
+
92
+ export default [
93
+ { ignores: ['dist/'] },
94
+ ...ReactEslintConfigs.create({ plugins: { react, reactHooks, jsxA11y } }),
95
+ ];
96
+ ```
97
+
98
+ | Adds | Setting |
99
+ | :--- | :--- |
100
+ | React correctness (`jsx-key`, `no-children-prop`, `no-deprecated`, ... - 14 rules) | `error`, `.jsx`/`.tsx` |
101
+ | `jsx-a11y` basics (`alt-text`, `aria-props`, `aria-role`, ... - 9 rules) | `error`, `.jsx`/`.tsx` |
102
+ | `react-hooks/rules-of-hooks` / `exhaustive-deps` | `error` / `warn`, every source file |
103
+ | `@typescript-eslint/consistent-type-imports` | `['error', { prefer: 'type-imports' }]` |
104
+ | `naming-convention` | the base rule, plus PascalCase functions (`function App()`), `.jsx`/`.tsx` |
105
+ | `no-floating-promises`, `no-void`, `no-invalid-this`, `no-use-before-define` | `off` - a UI fires promises from handlers and uses components above their declaration |
106
+ | `no-explicit-any` | `warn` (the base has it `off`) |
107
+ | `no-shadow` | `warn` |
108
+ | `no-unused-vars` | `warn`, `_`-prefixed names ignored |
109
+
110
+ Rules the automatic JSX runtime or `tsc` already enforce (`react-in-jsx-scope`, `prop-types`,
111
+ `jsx-no-undef`, ...) are left out.
112
+
113
+ - `reactVersion` defaults to `'detect'`. Under ESLint 10, eslint-plugin-react 7 cannot detect it and
114
+ aborts - pass the version: `create({ plugins, reactVersion: '19.2' })`.
115
+ - In an `eslint.config.ts`, `eslint-plugin-jsx-a11y` needs `@types/eslint-plugin-jsx-a11y`.
116
+ - `consistent-type-imports` keeps the default `fixStyle`; a codebase that prefers
117
+ `inline-type-imports` sets it on top.
118
+
81
119
  `prettierConfigs`:
82
120
 
83
121
  | Setting | Value |
@@ -1 +1 @@
1
- {"version":3,"file":"eslint.d.ts","sourceRoot":"","sources":["../src/eslint.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGrC,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAcxC,CAAC"}
1
+ {"version":3,"file":"eslint.d.ts","sourceRoot":"","sources":["../src/eslint.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGrC,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,MAAM,EAexC,CAAC"}
package/dist/eslint.js CHANGED
@@ -11,12 +11,13 @@ exports.eslintConfigs = [
11
11
  {
12
12
  rules: {
13
13
  "@typescript-eslint/no-explicit-any": "off",
14
+ "@typescript-eslint/ban-ts-comment": "error",
15
+ "@typescript-eslint/no-namespace": "error",
14
16
  },
15
17
  },
16
18
  {
17
19
  plugins: { unicorn: eslint_plugin_unicorn_1.default },
18
20
  rules: {
19
- curly: ["error", "all"],
20
21
  "unicorn/switch-case-braces": ["error", "always"],
21
22
  },
22
23
  },
@@ -1 +1 @@
1
- {"version":3,"file":"eslint.js","sourceRoot":"","sources":["../src/eslint.ts"],"names":[],"mappings":";;;;;;AAAA,2EAAqD;AAErD,kFAA4C;AAE/B,QAAA,aAAa,GAAoB;IAC5C,GAAG,qBAAa;IAChB;QACE,KAAK,EAAE;YACL,oCAAoC,EAAE,KAAK;SAC5C;KACF;IACD;QACE,OAAO,EAAE,EAAE,OAAO,EAAP,+BAAO,EAAE;QACpB,KAAK,EAAE;YACL,KAAK,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC;YACvB,4BAA4B,EAAE,CAAC,OAAO,EAAE,QAAQ,CAAC;SAClD;KACF;CACF,CAAC"}
1
+ {"version":3,"file":"eslint.js","sourceRoot":"","sources":["../src/eslint.ts"],"names":[],"mappings":";;;;;;AAAA,2EAAqD;AAErD,kFAA4C;AAE/B,QAAA,aAAa,GAAoB;IAC5C,GAAG,qBAAa;IAChB;QACE,KAAK,EAAE;YACL,oCAAoC,EAAE,KAAK;YAC3C,mCAAmC,EAAE,OAAO;YAC5C,iCAAiC,EAAE,OAAO;SAC3C;KACF;IACD;QACE,OAAO,EAAE,EAAE,OAAO,EAAP,+BAAO,EAAE;QACpB,KAAK,EAAE;YACL,4BAA4B,EAAE,CAAC,OAAO,EAAE,QAAQ,CAAC;SAClD;KACF;CACF,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from "./compile";
2
2
  export * from "./eslint";
3
3
  export * from "./prettier";
4
+ export * from "./react";
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,YAAY,CAAC;AAC3B,cAAc,SAAS,CAAC"}
package/dist/index.js CHANGED
@@ -17,4 +17,5 @@ Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./compile"), exports);
18
18
  __exportStar(require("./eslint"), exports);
19
19
  __exportStar(require("./prettier"), exports);
20
+ __exportStar(require("./react"), exports);
20
21
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,4CAA0B;AAC1B,2CAAyB;AACzB,6CAA2B"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,4CAA0B;AAC1B,2CAAyB;AACzB,6CAA2B;AAC3B,0CAAwB"}
@@ -0,0 +1,32 @@
1
+ import type { ESLint, Linter } from "eslint";
2
+ /** Without `configs`: each plugin types its presets its own way, and only the rules are used here. */
3
+ type TRulesPlugin = Omit<ESLint.Plugin, "configs">;
4
+ export interface IReactEslintPlugins {
5
+ react: TRulesPlugin;
6
+ reactHooks: TRulesPlugin;
7
+ jsxA11y: TRulesPlugin;
8
+ }
9
+ /**
10
+ * The base preset plus what a React frontend needs on top: the React, hooks and accessibility rules,
11
+ * and the base rules a UI relaxes on purpose. The plugins come in as options, so a backend that
12
+ * never calls this installs none of them.
13
+ *
14
+ * ```js
15
+ * import react from 'eslint-plugin-react';
16
+ * import reactHooks from 'eslint-plugin-react-hooks';
17
+ * import jsxA11y from 'eslint-plugin-jsx-a11y';
18
+ *
19
+ * export default ReactEslintConfigs.create({ plugins: { react, reactHooks, jsxA11y } });
20
+ * ```
21
+ *
22
+ * `reactVersion` defaults to `detect`. Under ESLint 10, eslint-plugin-react 7 cannot detect it and
23
+ * aborts, so pass the version there (`reactVersion: '19.2'`).
24
+ */
25
+ export declare class ReactEslintConfigs {
26
+ static create(opts: {
27
+ plugins: IReactEslintPlugins;
28
+ reactVersion?: string;
29
+ }): Linter.Config[];
30
+ }
31
+ export {};
32
+ //# sourceMappingURL=react.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAG7C,sGAAsG;AACtG,KAAK,YAAY,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;AAEnD,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,YAAY,CAAC;IACpB,UAAU,EAAE,YAAY,CAAC;IACzB,OAAO,EAAE,YAAY,CAAC;CACvB;AAID;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,kBAAkB;IAC7B,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE;QAClB,OAAO,EAAE,mBAAmB,CAAC;QAC7B,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,GAAG,MAAM,CAAC,MAAM,EAAE;CA+FpB"}
package/dist/react.js ADDED
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ReactEslintConfigs = void 0;
4
+ const eslint_1 = require("./eslint");
5
+ const NAMING_CONVENTION = "@typescript-eslint/naming-convention";
6
+ /**
7
+ * The base preset plus what a React frontend needs on top: the React, hooks and accessibility rules,
8
+ * and the base rules a UI relaxes on purpose. The plugins come in as options, so a backend that
9
+ * never calls this installs none of them.
10
+ *
11
+ * ```js
12
+ * import react from 'eslint-plugin-react';
13
+ * import reactHooks from 'eslint-plugin-react-hooks';
14
+ * import jsxA11y from 'eslint-plugin-jsx-a11y';
15
+ *
16
+ * export default ReactEslintConfigs.create({ plugins: { react, reactHooks, jsxA11y } });
17
+ * ```
18
+ *
19
+ * `reactVersion` defaults to `detect`. Under ESLint 10, eslint-plugin-react 7 cannot detect it and
20
+ * aborts, so pass the version there (`reactVersion: '19.2'`).
21
+ */
22
+ class ReactEslintConfigs {
23
+ static create(opts) {
24
+ const { react, reactHooks, jsxA11y } = opts.plugins;
25
+ const { reactVersion = "detect" } = opts;
26
+ // The last config that sets it wins, as in ESLint itself.
27
+ const baseNaming = [...eslint_1.eslintConfigs]
28
+ .reverse()
29
+ .find((config) => config.rules?.[NAMING_CONVENTION])?.rules?.[NAMING_CONVENTION];
30
+ // A bare severity carries no selectors; nothing to extend then but the severity.
31
+ const [severity, ...selectors] = Array.isArray(baseNaming) ? baseNaming : [baseNaming ?? "error"];
32
+ return [
33
+ ...eslint_1.eslintConfigs,
34
+ {
35
+ files: ["**/*.{jsx,tsx}"],
36
+ plugins: { react, "jsx-a11y": jsxA11y },
37
+ settings: { react: { version: reactVersion } },
38
+ rules: {
39
+ // The base naming rule, plus PascalCase functions: a component declared as `function App()`.
40
+ [NAMING_CONVENTION]: [
41
+ severity,
42
+ ...selectors,
43
+ { selector: "function", format: ["camelCase", "PascalCase"] },
44
+ ],
45
+ // Correctness rules from react's recommended set. Left out: what the automatic JSX runtime or
46
+ // tsc already covers (react-in-jsx-scope, jsx-uses-react, prop-types, jsx-no-undef,
47
+ // jsx-no-duplicate-props, no-unknown-property).
48
+ "react/display-name": "error",
49
+ "react/jsx-boolean-value": "error",
50
+ "react/jsx-key": "error",
51
+ "react/jsx-no-comment-textnodes": "error",
52
+ "react/jsx-no-target-blank": "error",
53
+ "react/no-children-prop": "error",
54
+ "react/no-danger-with-children": "error",
55
+ "react/no-deprecated": "error",
56
+ "react/no-direct-mutation-state": "error",
57
+ "react/no-find-dom-node": "error",
58
+ "react/no-is-mounted": "error",
59
+ "react/no-render-return-value": "error",
60
+ "react/no-string-refs": "error",
61
+ "react/require-render-return": "error",
62
+ "jsx-a11y/alt-text": "error",
63
+ "jsx-a11y/anchor-has-content": "error",
64
+ "jsx-a11y/aria-props": "error",
65
+ "jsx-a11y/aria-proptypes": "error",
66
+ "jsx-a11y/aria-role": "error",
67
+ "jsx-a11y/aria-unsupported-elements": "error",
68
+ "jsx-a11y/role-has-required-aria-props": "error",
69
+ "jsx-a11y/role-supports-aria-props": "error",
70
+ "jsx-a11y/tabindex-no-positive": "error",
71
+ },
72
+ },
73
+ {
74
+ // Custom hooks live in .ts files too.
75
+ files: ["**/*.{js,jsx,ts,tsx}"],
76
+ plugins: { "react-hooks": reactHooks },
77
+ rules: {
78
+ "react-hooks/rules-of-hooks": "error",
79
+ "react-hooks/exhaustive-deps": "warn",
80
+ },
81
+ },
82
+ {
83
+ rules: {
84
+ "@typescript-eslint/consistent-type-imports": [
85
+ "error",
86
+ { prefer: "type-imports" },
87
+ ],
88
+ // A UI hands promises to event handlers and effects that do not await them.
89
+ "@typescript-eslint/no-floating-promises": "off",
90
+ "no-void": "off",
91
+ // Function components, not classes; a component may be used above its declaration.
92
+ "@typescript-eslint/no-invalid-this": "off",
93
+ "@typescript-eslint/no-use-before-define": "off",
94
+ "@typescript-eslint/no-explicit-any": "warn",
95
+ "@typescript-eslint/no-shadow": "warn",
96
+ "@typescript-eslint/no-unused-vars": [
97
+ "warn",
98
+ {
99
+ argsIgnorePattern: "^_",
100
+ varsIgnorePattern: "^_",
101
+ caughtErrorsIgnorePattern: "^_",
102
+ ignoreRestSiblings: true,
103
+ },
104
+ ],
105
+ },
106
+ },
107
+ ];
108
+ }
109
+ }
110
+ exports.ReactEslintConfigs = ReactEslintConfigs;
111
+ //# sourceMappingURL=react.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"react.js","sourceRoot":"","sources":["../src/react.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@venizia/dev-configs",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Shareable development configurations for TypeScript projects: ESLint v9 flat config (with unicorn plugin), Prettier formatting, and TypeScript compiler options with critical decorator support (experimentalDecorators, emitDecoratorMetadata, useDefineForClassFields). Single source of truth for consistent code style, type checking, and build settings across monorepo packages. 3-line consumption pattern per config.",
5
5
  "keywords": [
6
6
  "build-tools",