@ncksnydr/config-js 0.3.0 → 0.4.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
@@ -145,7 +145,69 @@ To change a setting, merge the preset with your own configuration:
145
145
  import preset from "@ncksnydr/config-js/vitest";
146
146
  import { defineConfig, mergeConfig } from "vitest/config";
147
147
 
148
- export default mergeConfig(preset, defineConfig({ test: { include: ["tests/**/*.test.ts"] } }));
148
+ export default mergeConfig(preset, defineConfig({ test: { testTimeout: 10_000 } }));
149
149
  ```
150
150
 
151
- The preset runs tests in Node and finds `*.test.ts` files next to the code. It restores mocks between tests. If it finds no tests, the run fails.
151
+ The preset runs tests in Node. It restores mocks between tests. If it finds no tests, the run fails.
152
+
153
+ The preset does not set `include`, so Vitest finds test files with its own pattern, such as `*.test.ts` and `*.test.tsx`. To use your own pattern, merge an `include` into the preset.
154
+
155
+ ## React
156
+
157
+ A React Consumer uses the React presets in place of the plain ones. Each React preset holds the plain preset and adds to it.
158
+
159
+ ### TypeScript for React
160
+
161
+ Extend the React base in your `tsconfig.json`:
162
+
163
+ ```json
164
+ {
165
+ "extends": "@ncksnydr/config-js/tsconfig-react"
166
+ }
167
+ ```
168
+
169
+ The React base adds the DOM types. It compiles JSX with React's automatic runtime, so a component file does not import React to use JSX. Install `react` and `@types/react`.
170
+
171
+ ### ESLint for React
172
+
173
+ Use the React configuration as your `eslint.config.js`:
174
+
175
+ ```js
176
+ export { default } from "@ncksnydr/config-js/eslint-react";
177
+ ```
178
+
179
+ The React configuration adds these rules to `.ts` and `.tsx` files:
180
+
181
+ - The recommended rules of `eslint-plugin-react`, `eslint-plugin-react-hooks` and `eslint-plugin-jsx-a11y`.
182
+ - A missing dependency in a hook such as `useEffect` is an error, not a warning.
183
+ - `useContext` is banned. Put shared state in a Zustand store.
184
+ - `fireEvent` from `@testing-library/react` is banned. Use `userEvent`.
185
+ - Text cannot be written inside JSX. Put the text in a variable, and use the variable in the JSX. Text in a prop is allowed.
186
+
187
+ ```text
188
+ Welcome.tsx
189
+ 2:13 error Strings not allowed in JSX files: "Welcome back" react/jsx-no-literals
190
+ ```
191
+
192
+ The configuration reads the React version from the `react` package that your code imports. Use `boundaries()` with it the same way as with the plain configuration.
193
+
194
+ ### Vitest for React
195
+
196
+ Use the React preset as your `vitest.config.ts`:
197
+
198
+ ```ts
199
+ export { default } from "@ncksnydr/config-js/vitest-react";
200
+ ```
201
+
202
+ The React preset holds the plain Vitest preset, and runs the tests in Vitest's browser mode, in Chromium through Playwright. The Storybook Vitest addon uses the same browser.
203
+
204
+ The React preset sets no `include`, so you can add the Storybook Vitest plugin to it. The plugin sets `include` to your stories. It prints a warning when the configuration already has an `include`.
205
+
206
+ Install Playwright and the Vitest Playwright provider. Then install Chromium once on each machine and in CI:
207
+
208
+ ```sh
209
+ pnpm add --save-dev playwright @vitest/browser-playwright
210
+ pnpm exec playwright install chromium
211
+ ```
212
+
213
+ In CI on Linux, use `pnpm exec playwright install --with-deps chromium`. This command also installs the system libraries that Chromium needs.
@@ -0,0 +1,2 @@
1
+ declare const _default: import("eslint/config").ConfigObject[];
2
+ export default _default;
@@ -0,0 +1,42 @@
1
+ import { fixupPluginRules } from "@eslint/compat";
2
+ import jsxA11y from "eslint-plugin-jsx-a11y";
3
+ import react from "eslint-plugin-react";
4
+ import reactHooks from "eslint-plugin-react-hooks";
5
+ import { defineConfig } from "eslint/config";
6
+ import base from "./eslint.js";
7
+ const reactWithVersionDetection = fixupPluginRules(react);
8
+ function reactPluginConfig(name) {
9
+ const config = react.configs.flat[name];
10
+ if (config === undefined) {
11
+ throw new Error(`eslint-plugin-react has no flat configuration named "${name}".`);
12
+ }
13
+ return { ...config, plugins: { react: reactWithVersionDetection } };
14
+ }
15
+ export default defineConfig(base, {
16
+ extends: [
17
+ jsxA11y.flatConfigs.recommended,
18
+ reactPluginConfig("recommended"),
19
+ reactPluginConfig("jsx-runtime"),
20
+ reactHooks.configs.flat.recommended,
21
+ ],
22
+ files: ["**/*.{ts,tsx}"],
23
+ rules: {
24
+ "no-restricted-imports": [
25
+ "error",
26
+ {
27
+ paths: [
28
+ { importNames: ["useContext"], message: "Put shared state in a Zustand store.", name: "react" },
29
+ { importNames: ["fireEvent"], message: "Use userEvent.", name: "@testing-library/react" },
30
+ ],
31
+ },
32
+ ],
33
+ "react-hooks/exhaustive-deps": "error",
34
+ "react/jsx-no-literals": ["error", { ignoreProps: true, noStrings: true }],
35
+ },
36
+ settings: {
37
+ react: {
38
+ version: "detect",
39
+ },
40
+ },
41
+ });
42
+ //# sourceMappingURL=eslint-react.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eslint-react.js","sourceRoot":"","sources":["../src/eslint-react.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAElD,OAAO,OAAO,MAAM,wBAAwB,CAAC;AAC7C,OAAO,KAAK,MAAM,qBAAqB,CAAC;AACxC,OAAO,UAAU,MAAM,2BAA2B,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,IAAI,MAAM,aAAa,CAAC;AAE/B,MAAM,yBAAyB,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;AAE1D,SAAS,iBAAiB,CAAC,IAAY;IACrC,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,wDAAwD,IAAI,IAAI,CAAC,CAAC;IACpF,CAAC;IACD,OAAO,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,yBAAyB,EAAE,EAAE,CAAC;AACtE,CAAC;AAED,eAAe,YAAY,CAAC,IAAI,EAAE;IAChC,OAAO,EAAE;QACP,OAAO,CAAC,WAAW,CAAC,WAAW;QAC/B,iBAAiB,CAAC,aAAa,CAAC;QAChC,iBAAiB,CAAC,aAAa,CAAC;QAChC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW;KACpC;IACD,KAAK,EAAE,CAAC,eAAe,CAAC;IACxB,KAAK,EAAE;QACL,uBAAuB,EAAE;YACvB,OAAO;YACP;gBACE,KAAK,EAAE;oBACL,EAAE,WAAW,EAAE,CAAC,YAAY,CAAC,EAAE,OAAO,EAAE,sCAAsC,EAAE,IAAI,EAAE,OAAO,EAAE;oBAC/F,EAAE,WAAW,EAAE,CAAC,WAAW,CAAC,EAAE,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE,wBAAwB,EAAE;iBAC1F;aACF;SACF;QACD,6BAA6B,EAAE,OAAO;QACtC,uBAAuB,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;KAC3E;IACD,QAAQ,EAAE;QACR,KAAK,EAAE;YACL,OAAO,EAAE,QAAQ;SAClB;KACF;CACF,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { type ViteUserConfig } from "vitest/config";
2
+ declare const preset: ViteUserConfig;
3
+ export default preset;
@@ -0,0 +1,15 @@
1
+ import { playwright } from "@vitest/browser-playwright";
2
+ import { defineConfig, mergeConfig } from "vitest/config";
3
+ import plain from "./vitest.js";
4
+ const preset = mergeConfig(plain, defineConfig({
5
+ test: {
6
+ browser: {
7
+ enabled: true,
8
+ headless: true,
9
+ instances: [{ browser: "chromium" }],
10
+ provider: playwright(),
11
+ },
12
+ },
13
+ }));
14
+ export default preset;
15
+ //# sourceMappingURL=vitest-react.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vitest-react.js","sourceRoot":"","sources":["../src/vitest-react.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,WAAW,EAAuB,MAAM,eAAe,CAAC;AAC/E,OAAO,KAAK,MAAM,aAAa,CAAC;AAEhC,MAAM,MAAM,GAAmB,WAAW,CACxC,KAAK,EACL,YAAY,CAAC;IACX,IAAI,EAAE;QACJ,OAAO,EAAE;YACP,OAAO,EAAE,IAAI;YACb,QAAQ,EAAE,IAAI;YACd,SAAS,EAAE,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC;YACpC,QAAQ,EAAE,UAAU,EAAE;SACvB;KACF;CACF,CAAC,CACH,CAAC;AAEF,eAAe,MAAM,CAAC"}
package/dist/vitest.d.ts CHANGED
@@ -1,2 +1,3 @@
1
- declare const _default: import("vite").UserConfig;
2
- export default _default;
1
+ import { type ViteUserConfig } from "vitest/config";
2
+ declare const preset: ViteUserConfig;
3
+ export default preset;
package/dist/vitest.js CHANGED
@@ -1,10 +1,10 @@
1
1
  import { defineConfig } from "vitest/config";
2
- export default defineConfig({
2
+ const preset = defineConfig({
3
3
  test: {
4
4
  environment: "node",
5
- include: ["**/*.test.ts"],
6
- restoreMocks: true,
7
5
  passWithNoTests: false,
6
+ restoreMocks: true,
8
7
  },
9
8
  });
9
+ export default preset;
10
10
  //# sourceMappingURL=vitest.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"vitest.js","sourceRoot":"","sources":["../src/vitest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,eAAe,YAAY,CAAC;IAC1B,IAAI,EAAE;QACJ,WAAW,EAAE,MAAM;QACnB,OAAO,EAAE,CAAC,cAAc,CAAC;QACzB,YAAY,EAAE,IAAI;QAClB,eAAe,EAAE,KAAK;KACvB;CACF,CAAC,CAAC"}
1
+ {"version":3,"file":"vitest.js","sourceRoot":"","sources":["../src/vitest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,MAAM,eAAe,CAAC;AAElE,MAAM,MAAM,GAAmB,YAAY,CAAC;IAC1C,IAAI,EAAE;QACJ,WAAW,EAAE,MAAM;QACnB,eAAe,EAAE,KAAK;QACtB,YAAY,EAAE,IAAI;KACnB;CACF,CAAC,CAAC;AAEH,eAAe,MAAM,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ncksnydr/config-js",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "description": "Shared TypeScript, ESLint, Prettier, Stylelint and Vitest configuration for Nick Snyder's codebases",
5
5
  "repository": {
6
6
  "directory": "packages/config-js",
@@ -13,6 +13,10 @@
13
13
  "types": "./dist/eslint.d.ts",
14
14
  "default": "./dist/eslint.js"
15
15
  },
16
+ "./eslint-react": {
17
+ "types": "./dist/eslint-react.d.ts",
18
+ "default": "./dist/eslint-react.js"
19
+ },
16
20
  "./prettier": {
17
21
  "types": "./dist/prettier.d.ts",
18
22
  "default": "./dist/prettier.js"
@@ -22,9 +26,14 @@
22
26
  "default": "./dist/stylelint.js"
23
27
  },
24
28
  "./tsconfig": "./tsconfig/base.json",
29
+ "./tsconfig-react": "./tsconfig/react.json",
25
30
  "./vitest": {
26
31
  "types": "./dist/vitest.d.ts",
27
32
  "default": "./dist/vitest.js"
33
+ },
34
+ "./vitest-react": {
35
+ "types": "./dist/vitest-react.d.ts",
36
+ "default": "./dist/vitest-react.js"
28
37
  }
29
38
  },
30
39
  "files": [
@@ -36,16 +45,24 @@
36
45
  "tsconfig"
37
46
  ],
38
47
  "peerDependencies": {
48
+ "@vitest/browser-playwright": "^5.0.0",
39
49
  "eslint": "^10.0.0",
50
+ "playwright": "^1.65.0",
40
51
  "prettier": "^3.5.0",
41
52
  "stylelint": "^18.0.0",
42
53
  "typescript": "^6.0.0",
43
54
  "vitest": "^5.0.0"
44
55
  },
45
56
  "peerDependenciesMeta": {
57
+ "@vitest/browser-playwright": {
58
+ "optional": true
59
+ },
46
60
  "eslint": {
47
61
  "optional": true
48
62
  },
63
+ "playwright": {
64
+ "optional": true
65
+ },
49
66
  "prettier": {
50
67
  "optional": true
51
68
  },
@@ -60,18 +77,27 @@
60
77
  }
61
78
  },
62
79
  "dependencies": {
80
+ "@eslint/compat": "^2.1.1",
63
81
  "@eslint/js": "^10.0.1",
64
82
  "@ianvs/prettier-plugin-sort-imports": "^4.7.1",
65
83
  "eslint-import-resolver-typescript": "^4.4.5",
66
84
  "eslint-plugin-import-x": "^4.18.1",
85
+ "eslint-plugin-jsx-a11y": "^6.10.2",
86
+ "eslint-plugin-react": "^7.37.5",
87
+ "eslint-plugin-react-hooks": "^7.1.1",
67
88
  "stylelint-order": "^8.1.3",
68
89
  "typescript-eslint": "^8.72.2"
69
90
  },
70
91
  "devDependencies": {
92
+ "@types/eslint-plugin-jsx-a11y": "^6.10.1",
71
93
  "@types/node": "^26.6.6",
94
+ "@types/react": "^19.3.0",
95
+ "@vitest/browser-playwright": "^5.1.0",
72
96
  "eslint": "^10.14.0",
97
+ "playwright": "^1.65.0",
73
98
  "postcss": "^8.5.32",
74
99
  "prettier": "^3.10.0",
100
+ "react": "^19.3.1",
75
101
  "stylelint": "^18.0.0",
76
102
  "typescript": "~6.0.3",
77
103
  "vitest": "^5.1.0"
@@ -0,0 +1,45 @@
1
+ import { fixupPluginRules } from "@eslint/compat";
2
+ import type { Linter } from "eslint";
3
+ import jsxA11y from "eslint-plugin-jsx-a11y";
4
+ import react from "eslint-plugin-react";
5
+ import reactHooks from "eslint-plugin-react-hooks";
6
+ import { defineConfig } from "eslint/config";
7
+ import base from "./eslint.js";
8
+
9
+ const reactWithVersionDetection = fixupPluginRules(react);
10
+
11
+ function reactPluginConfig(name: string): Linter.Config {
12
+ const config = react.configs.flat[name];
13
+ if (config === undefined) {
14
+ throw new Error(`eslint-plugin-react has no flat configuration named "${name}".`);
15
+ }
16
+ return { ...config, plugins: { react: reactWithVersionDetection } };
17
+ }
18
+
19
+ export default defineConfig(base, {
20
+ extends: [
21
+ jsxA11y.flatConfigs.recommended,
22
+ reactPluginConfig("recommended"),
23
+ reactPluginConfig("jsx-runtime"),
24
+ reactHooks.configs.flat.recommended,
25
+ ],
26
+ files: ["**/*.{ts,tsx}"],
27
+ rules: {
28
+ "no-restricted-imports": [
29
+ "error",
30
+ {
31
+ paths: [
32
+ { importNames: ["useContext"], message: "Put shared state in a Zustand store.", name: "react" },
33
+ { importNames: ["fireEvent"], message: "Use userEvent.", name: "@testing-library/react" },
34
+ ],
35
+ },
36
+ ],
37
+ "react-hooks/exhaustive-deps": "error",
38
+ "react/jsx-no-literals": ["error", { ignoreProps: true, noStrings: true }],
39
+ },
40
+ settings: {
41
+ react: {
42
+ version: "detect",
43
+ },
44
+ },
45
+ });
@@ -0,0 +1,19 @@
1
+ import { playwright } from "@vitest/browser-playwright";
2
+ import { defineConfig, mergeConfig, type ViteUserConfig } from "vitest/config";
3
+ import plain from "./vitest.js";
4
+
5
+ const preset: ViteUserConfig = mergeConfig(
6
+ plain,
7
+ defineConfig({
8
+ test: {
9
+ browser: {
10
+ enabled: true,
11
+ headless: true,
12
+ instances: [{ browser: "chromium" }],
13
+ provider: playwright(),
14
+ },
15
+ },
16
+ }),
17
+ );
18
+
19
+ export default preset;
package/src/vitest.ts CHANGED
@@ -1,10 +1,11 @@
1
- import { defineConfig } from "vitest/config";
1
+ import { defineConfig, type ViteUserConfig } from "vitest/config";
2
2
 
3
- export default defineConfig({
3
+ const preset: ViteUserConfig = defineConfig({
4
4
  test: {
5
5
  environment: "node",
6
- include: ["**/*.test.ts"],
7
- restoreMocks: true,
8
6
  passWithNoTests: false,
7
+ restoreMocks: true,
9
8
  },
10
9
  });
10
+
11
+ export default preset;
@@ -0,0 +1,8 @@
1
+ {
2
+ "$schema": "https://json.schemastore.org/tsconfig",
3
+ "extends": "./base.json",
4
+ "compilerOptions": {
5
+ "jsx": "react-jsx",
6
+ "lib": ["DOM", "DOM.Iterable", "ES2024"]
7
+ }
8
+ }