@elliemae/pui-cli 9.0.0-alpha.5 → 9.0.0-alpha.7

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
@@ -8,7 +8,7 @@
8
8
 
9
9
  ## Migration Guide
10
10
 
11
- ### pui-cli 9 (Node 24, pnpm 11, ESLint 9)
11
+ ### pui-cli 9 (Node 24, pnpm 11, ESLint 10)
12
12
 
13
13
  Team upgrade guide: [pui-cli 9 migration guide](docs/pui-cli-9-migration.md).
14
14
 
@@ -18,9 +18,9 @@ Install the bundled Cursor skill in a consumer repo:
18
18
  pnpm exec pui-cli skills install migrate-to-pui-cli-9 --target all
19
19
  ```
20
20
 
21
- ### ESLint 9 flat config (alpha)
21
+ ### ESLint 10 flat config (alpha)
22
22
 
23
- `pui-cli` now ships ESLint 9 with `typescript-eslint` v8 flat configs. Replace legacy `.eslintrc.cjs` with:
23
+ `pui-cli` ships ESLint 10 with `typescript-eslint` v8 flat configs. Legacy plugins that have not yet published ESLint 10–compatible releases (Storybook 6, redux-saga) are wrapped via `@eslint/compat`. Replace legacy `.eslintrc.cjs` with:
24
24
 
25
25
  ```js
26
26
  // eslint.config.mjs — React apps and libraries
@@ -185,7 +185,7 @@ const skillsCmd = {
185
185
  type: "string"
186
186
  }).option("target", {
187
187
  describe: "Install destination: cursor (.cursor/skills), claude (.claude/skills), copilot (.github/skills), or all",
188
- choices: SKILL_TARGETS,
188
+ choices: [...SKILL_TARGETS],
189
189
  default: "cursor"
190
190
  }).option("force", {
191
191
  describe: "Overwrite an existing skill directory",
@@ -1,11 +1,9 @@
1
1
  import eslint from '@eslint/js';
2
2
  import { defineConfig } from 'eslint/config';
3
3
  import eslintConfigPrettier from 'eslint-config-prettier';
4
- import eslintComments from 'eslint-plugin-eslint-comments';
5
4
  import importX from 'eslint-plugin-import-x';
6
5
  import jest from 'eslint-plugin-jest';
7
6
  import testingLibrary from 'eslint-plugin-testing-library';
8
- import wdio from 'eslint-plugin-wdio';
9
7
  import globals from 'globals';
10
8
  import tseslint from 'typescript-eslint';
11
9
  import {
@@ -17,6 +15,7 @@ import {
17
15
  testingLibraryDomRules,
18
16
  testingLibraryReactRules,
19
17
  wdioGlobals,
18
+ wdioPlugin,
20
19
  wdioRecommendedRules,
21
20
  wdioSpecFiles,
22
21
  } from './presets.mjs';
@@ -51,7 +50,6 @@ export function createBaseFlatConfigs(tsRules) {
51
50
  eslintConfigPrettier,
52
51
  {
53
52
  plugins: {
54
- 'eslint-comments': eslintComments,
55
53
  'import-x': importX,
56
54
  },
57
55
  languageOptions: {
@@ -121,10 +119,13 @@ export function createBaseFlatConfigs(tsRules) {
121
119
  },
122
120
  {
123
121
  files: wdioSpecFiles,
124
- plugins: { wdio, jest },
122
+ plugins: { wdio: wdioPlugin, jest },
125
123
  languageOptions: { globals: wdioGlobals },
126
124
  rules: {
127
125
  ...wdioRecommendedRules,
126
+ // JS e2e specs are not type-checked; use await-expect instead of no-floating-promise.
127
+ 'wdio/await-expect': 'error',
128
+ 'wdio/no-floating-promise': 'off',
128
129
  'jest/valid-expect': 'off',
129
130
  },
130
131
  },
@@ -0,0 +1,20 @@
1
+ /**
2
+ * ESLint 10 removed deprecated context APIs (e.g. getSourceCode).
3
+ * Wrap legacy plugins until their maintainers publish compatible releases.
4
+ */
5
+ import { fixupConfigRules, fixupPluginRules } from '@eslint/compat';
6
+ import reduxSaga from 'eslint-plugin-redux-saga';
7
+ import storybook from 'eslint-plugin-storybook';
8
+
9
+ const storybookFlat =
10
+ storybook.configs['flat/recommended'] ??
11
+ storybook.configs.recommended ??
12
+ [];
13
+
14
+ /** @type {import('eslint').ESLint.Plugin} */
15
+ export const reduxSagaPlugin = fixupPluginRules(reduxSaga);
16
+
17
+ /** Storybook flat presets with ESLint 10 compatibility shims applied. */
18
+ export const storybookFlatConfigs = fixupConfigRules(
19
+ Array.isArray(storybookFlat) ? storybookFlat : [storybookFlat],
20
+ );
@@ -1,16 +1,15 @@
1
1
  /**
2
2
  * Preset rule bundles and file globs computed once at module load (not per config factory call).
3
3
  */
4
- import eslintComments from 'eslint-plugin-eslint-comments';
5
4
  import importX from 'eslint-plugin-import-x';
6
5
  import jest from 'eslint-plugin-jest';
7
6
  import jsxA11y from 'eslint-plugin-jsx-a11y';
8
7
  import react from 'eslint-plugin-react';
9
8
  import reactHooks from 'eslint-plugin-react-hooks';
10
9
  import reduxSaga from 'eslint-plugin-redux-saga';
11
- import storybook from 'eslint-plugin-storybook';
12
10
  import testingLibrary from 'eslint-plugin-testing-library';
13
11
  import wdio from 'eslint-plugin-wdio';
12
+ import { storybookFlatConfigs } from './compat.mjs';
14
13
 
15
14
  /** Application and integration tests (Jest + relaxed type-checked rules). */
16
15
  export const testFiles = [
@@ -50,7 +49,6 @@ export const storybookFiles = [
50
49
 
51
50
  /** @type {import('eslint').Linter.RulesRecord} */
52
51
  export const sharedCoreRules = {
53
- ...eslintComments.configs.recommended.rules,
54
52
  ...importX.configs.recommended.rules,
55
53
  'import-x/named': 'off',
56
54
  };
@@ -77,18 +75,22 @@ export const reactPresetRules = {
77
75
  ...reduxSaga.configs.recommended.rules,
78
76
  };
79
77
 
78
+ /**
79
+ * eslint-plugin-wdio v9 default export exposes config severities as `rules` when
80
+ * typescript-eslint is installed. Use flat/recommended's nested plugin instead.
81
+ */
82
+ const wdioFlatRecommended =
83
+ wdio.configs['flat/recommended'] ?? wdio.configs.recommended;
84
+
85
+ export const wdioPlugin = wdioFlatRecommended.plugins?.wdio ?? wdio;
86
+
80
87
  /** @type {import('eslint').Linter.RulesRecord} */
81
- export const wdioRecommendedRules = wdio.configs.recommended.rules ?? {};
88
+ export const wdioRecommendedRules = wdioFlatRecommended.rules ?? {};
82
89
 
83
90
  /** @type {Record<string, boolean>} */
84
- export const wdioGlobals = wdio.configs.recommended.globals ?? {};
85
-
86
- const storybookFlat =
87
- storybook.configs['flat/recommended'] ??
88
- storybook.configs.recommended ??
89
- [];
91
+ export const wdioGlobals =
92
+ wdioFlatRecommended.languageOptions?.globals ??
93
+ wdio.configs.recommended.globals ??
94
+ {};
90
95
 
91
- /** Normalized once — Storybook flat config may be array or single object. */
92
- export const storybookFlatConfigs = Array.isArray(storybookFlat)
93
- ? storybookFlat
94
- : [storybookFlat];
96
+ export { storybookFlatConfigs };
@@ -2,8 +2,8 @@ import { defineConfig } from 'eslint/config';
2
2
  import jsxA11y from 'eslint-plugin-jsx-a11y';
3
3
  import react from 'eslint-plugin-react';
4
4
  import reactHooks from 'eslint-plugin-react-hooks';
5
- import reduxSaga from 'eslint-plugin-redux-saga';
6
5
  import { baseFlatConfigs, baseFlatConfigsStrict } from './common.mjs';
6
+ import { reduxSagaPlugin } from './compat.mjs';
7
7
  import {
8
8
  reactPresetRules,
9
9
  storybookFiles,
@@ -16,7 +16,7 @@ const reactPluginBlock = {
16
16
  react,
17
17
  'react-hooks': reactHooks,
18
18
  'jsx-a11y': jsxA11y,
19
- 'redux-saga': reduxSaga,
19
+ 'redux-saga': reduxSagaPlugin,
20
20
  },
21
21
  settings: {
22
22
  react: { version: 'detect' },
@@ -38,7 +38,6 @@ export const baseRules = {
38
38
  'no-param-reassign': ['error', { props: false }],
39
39
  'prefer-template': 'error',
40
40
  'require-yield': 'off',
41
- 'eslint-comments/disable-enable-pair': 'off',
42
41
  };
43
42
 
44
43
  /** Relaxed type-checked rules for test files (shared by default and strict configs). */
@@ -151,7 +151,7 @@ const skillsCmd = {
151
151
  type: "string"
152
152
  }).option("target", {
153
153
  describe: "Install destination: cursor (.cursor/skills), claude (.claude/skills), copilot (.github/skills), or all",
154
- choices: SKILL_TARGETS,
154
+ choices: [...SKILL_TARGETS],
155
155
  default: "cursor"
156
156
  }).option("force", {
157
157
  describe: "Overwrite an existing skill directory",
@@ -1,11 +1,9 @@
1
1
  import eslint from '@eslint/js';
2
2
  import { defineConfig } from 'eslint/config';
3
3
  import eslintConfigPrettier from 'eslint-config-prettier';
4
- import eslintComments from 'eslint-plugin-eslint-comments';
5
4
  import importX from 'eslint-plugin-import-x';
6
5
  import jest from 'eslint-plugin-jest';
7
6
  import testingLibrary from 'eslint-plugin-testing-library';
8
- import wdio from 'eslint-plugin-wdio';
9
7
  import globals from 'globals';
10
8
  import tseslint from 'typescript-eslint';
11
9
  import {
@@ -17,6 +15,7 @@ import {
17
15
  testingLibraryDomRules,
18
16
  testingLibraryReactRules,
19
17
  wdioGlobals,
18
+ wdioPlugin,
20
19
  wdioRecommendedRules,
21
20
  wdioSpecFiles,
22
21
  } from './presets.mjs';
@@ -51,7 +50,6 @@ export function createBaseFlatConfigs(tsRules) {
51
50
  eslintConfigPrettier,
52
51
  {
53
52
  plugins: {
54
- 'eslint-comments': eslintComments,
55
53
  'import-x': importX,
56
54
  },
57
55
  languageOptions: {
@@ -121,10 +119,13 @@ export function createBaseFlatConfigs(tsRules) {
121
119
  },
122
120
  {
123
121
  files: wdioSpecFiles,
124
- plugins: { wdio, jest },
122
+ plugins: { wdio: wdioPlugin, jest },
125
123
  languageOptions: { globals: wdioGlobals },
126
124
  rules: {
127
125
  ...wdioRecommendedRules,
126
+ // JS e2e specs are not type-checked; use await-expect instead of no-floating-promise.
127
+ 'wdio/await-expect': 'error',
128
+ 'wdio/no-floating-promise': 'off',
128
129
  'jest/valid-expect': 'off',
129
130
  },
130
131
  },
@@ -0,0 +1,20 @@
1
+ /**
2
+ * ESLint 10 removed deprecated context APIs (e.g. getSourceCode).
3
+ * Wrap legacy plugins until their maintainers publish compatible releases.
4
+ */
5
+ import { fixupConfigRules, fixupPluginRules } from '@eslint/compat';
6
+ import reduxSaga from 'eslint-plugin-redux-saga';
7
+ import storybook from 'eslint-plugin-storybook';
8
+
9
+ const storybookFlat =
10
+ storybook.configs['flat/recommended'] ??
11
+ storybook.configs.recommended ??
12
+ [];
13
+
14
+ /** @type {import('eslint').ESLint.Plugin} */
15
+ export const reduxSagaPlugin = fixupPluginRules(reduxSaga);
16
+
17
+ /** Storybook flat presets with ESLint 10 compatibility shims applied. */
18
+ export const storybookFlatConfigs = fixupConfigRules(
19
+ Array.isArray(storybookFlat) ? storybookFlat : [storybookFlat],
20
+ );
@@ -1,16 +1,15 @@
1
1
  /**
2
2
  * Preset rule bundles and file globs computed once at module load (not per config factory call).
3
3
  */
4
- import eslintComments from 'eslint-plugin-eslint-comments';
5
4
  import importX from 'eslint-plugin-import-x';
6
5
  import jest from 'eslint-plugin-jest';
7
6
  import jsxA11y from 'eslint-plugin-jsx-a11y';
8
7
  import react from 'eslint-plugin-react';
9
8
  import reactHooks from 'eslint-plugin-react-hooks';
10
9
  import reduxSaga from 'eslint-plugin-redux-saga';
11
- import storybook from 'eslint-plugin-storybook';
12
10
  import testingLibrary from 'eslint-plugin-testing-library';
13
11
  import wdio from 'eslint-plugin-wdio';
12
+ import { storybookFlatConfigs } from './compat.mjs';
14
13
 
15
14
  /** Application and integration tests (Jest + relaxed type-checked rules). */
16
15
  export const testFiles = [
@@ -50,7 +49,6 @@ export const storybookFiles = [
50
49
 
51
50
  /** @type {import('eslint').Linter.RulesRecord} */
52
51
  export const sharedCoreRules = {
53
- ...eslintComments.configs.recommended.rules,
54
52
  ...importX.configs.recommended.rules,
55
53
  'import-x/named': 'off',
56
54
  };
@@ -77,18 +75,22 @@ export const reactPresetRules = {
77
75
  ...reduxSaga.configs.recommended.rules,
78
76
  };
79
77
 
78
+ /**
79
+ * eslint-plugin-wdio v9 default export exposes config severities as `rules` when
80
+ * typescript-eslint is installed. Use flat/recommended's nested plugin instead.
81
+ */
82
+ const wdioFlatRecommended =
83
+ wdio.configs['flat/recommended'] ?? wdio.configs.recommended;
84
+
85
+ export const wdioPlugin = wdioFlatRecommended.plugins?.wdio ?? wdio;
86
+
80
87
  /** @type {import('eslint').Linter.RulesRecord} */
81
- export const wdioRecommendedRules = wdio.configs.recommended.rules ?? {};
88
+ export const wdioRecommendedRules = wdioFlatRecommended.rules ?? {};
82
89
 
83
90
  /** @type {Record<string, boolean>} */
84
- export const wdioGlobals = wdio.configs.recommended.globals ?? {};
85
-
86
- const storybookFlat =
87
- storybook.configs['flat/recommended'] ??
88
- storybook.configs.recommended ??
89
- [];
91
+ export const wdioGlobals =
92
+ wdioFlatRecommended.languageOptions?.globals ??
93
+ wdio.configs.recommended.globals ??
94
+ {};
90
95
 
91
- /** Normalized once — Storybook flat config may be array or single object. */
92
- export const storybookFlatConfigs = Array.isArray(storybookFlat)
93
- ? storybookFlat
94
- : [storybookFlat];
96
+ export { storybookFlatConfigs };
@@ -2,8 +2,8 @@ import { defineConfig } from 'eslint/config';
2
2
  import jsxA11y from 'eslint-plugin-jsx-a11y';
3
3
  import react from 'eslint-plugin-react';
4
4
  import reactHooks from 'eslint-plugin-react-hooks';
5
- import reduxSaga from 'eslint-plugin-redux-saga';
6
5
  import { baseFlatConfigs, baseFlatConfigsStrict } from './common.mjs';
6
+ import { reduxSagaPlugin } from './compat.mjs';
7
7
  import {
8
8
  reactPresetRules,
9
9
  storybookFiles,
@@ -16,7 +16,7 @@ const reactPluginBlock = {
16
16
  react,
17
17
  'react-hooks': reactHooks,
18
18
  'jsx-a11y': jsxA11y,
19
- 'redux-saga': reduxSaga,
19
+ 'redux-saga': reduxSagaPlugin,
20
20
  },
21
21
  settings: {
22
22
  react: { version: 'detect' },
@@ -38,7 +38,6 @@ export const baseRules = {
38
38
  'no-param-reassign': ['error', { props: false }],
39
39
  'prefer-template': 'error',
40
40
  'require-yield': 'off',
41
- 'eslint-comments/disable-enable-pair': 'off',
42
41
  };
43
42
 
44
43
  /** Relaxed type-checked rules for test files (shared by default and strict configs). */
@@ -0,0 +1,4 @@
1
+ /** @type {import('eslint').ESLint.Plugin} */
2
+ export const reduxSagaPlugin: import("eslint").ESLint.Plugin;
3
+ /** Storybook flat presets with ESLint 10 compatibility shims applied. */
4
+ export const storybookFlatConfigs: import("@eslint/compat").FixupConfigArray;
@@ -18,52 +18,17 @@ export const testingLibraryDomRules: import("eslint").Linter.RulesRecord;
18
18
  export const testingLibraryReactRules: import("eslint").Linter.RulesRecord;
19
19
  /** @type {import('eslint').Linter.RulesRecord} */
20
20
  export const reactPresetRules: import("eslint").Linter.RulesRecord;
21
+ export const wdioPlugin: {
22
+ configs: {};
23
+ rules: {
24
+ "await-expect": import("eslint").Rule.RuleModule;
25
+ "no-debug": import("eslint").Rule.RuleModule;
26
+ "no-pause": import("eslint").Rule.RuleModule;
27
+ };
28
+ };
21
29
  /** @type {import('eslint').Linter.RulesRecord} */
22
30
  export const wdioRecommendedRules: import("eslint").Linter.RulesRecord;
23
31
  /** @type {Record<string, boolean>} */
24
32
  export const wdioGlobals: Record<string, boolean>;
25
- /** Normalized once — Storybook flat config may be array or single object. */
26
- export const storybookFlatConfigs: ({
27
- name: string;
28
- plugins: {
29
- readonly storybook: any;
30
- };
31
- files?: undefined;
32
- rules?: undefined;
33
- } | {
34
- name: string;
35
- files: string[];
36
- rules: {
37
- readonly "react-hooks/rules-of-hooks": "off";
38
- readonly "import/no-anonymous-default-export": "off";
39
- readonly "storybook/await-interactions": "error";
40
- readonly "storybook/context-in-play-function": "error";
41
- readonly "storybook/default-exports": "error";
42
- readonly "storybook/hierarchy-separator": "warn";
43
- readonly "storybook/no-redundant-story-name": "warn";
44
- readonly "storybook/prefer-pascal-case": "warn";
45
- readonly "storybook/story-exports": "error";
46
- readonly "storybook/use-storybook-expect": "error";
47
- readonly "storybook/use-storybook-testing-library": "error";
48
- readonly "storybook/no-uninstalled-addons"?: undefined;
49
- };
50
- plugins?: undefined;
51
- } | {
52
- name: string;
53
- files: string[];
54
- rules: {
55
- readonly "storybook/no-uninstalled-addons": "error";
56
- readonly "react-hooks/rules-of-hooks"?: undefined;
57
- readonly "import/no-anonymous-default-export"?: undefined;
58
- readonly "storybook/await-interactions"?: undefined;
59
- readonly "storybook/context-in-play-function"?: undefined;
60
- readonly "storybook/default-exports"?: undefined;
61
- readonly "storybook/hierarchy-separator"?: undefined;
62
- readonly "storybook/no-redundant-story-name"?: undefined;
63
- readonly "storybook/prefer-pascal-case"?: undefined;
64
- readonly "storybook/story-exports"?: undefined;
65
- readonly "storybook/use-storybook-expect"?: undefined;
66
- readonly "storybook/use-storybook-testing-library"?: undefined;
67
- };
68
- plugins?: undefined;
69
- })[];
33
+ export { storybookFlatConfigs };
34
+ import { storybookFlatConfigs } from './compat.mjs';
@@ -44,7 +44,6 @@ export const baseRules: {
44
44
  })[];
45
45
  'prefer-template': string;
46
46
  'require-yield': string;
47
- 'eslint-comments/disable-enable-pair': string;
48
47
  };
49
48
  /** Relaxed type-checked rules for test files (shared by default and strict configs). */
50
49
  export const typescriptTestRelaxedRules: {
@@ -120,7 +119,6 @@ export const jsRules: {
120
119
  })[];
121
120
  'prefer-template': string;
122
121
  'require-yield': string;
123
- 'eslint-comments/disable-enable-pair': string;
124
122
  };
125
123
  export const typescriptRules: {
126
124
  'max-lines': (string | {
@@ -205,7 +203,6 @@ export const typescriptRules: {
205
203
  })[];
206
204
  'prefer-template': string;
207
205
  'require-yield': string;
208
- 'eslint-comments/disable-enable-pair': string;
209
206
  };
210
207
  /** Stricter TypeScript rules (error on unsafe any usage). */
211
208
  export const typescriptStrictRules: {
@@ -291,7 +288,6 @@ export const typescriptStrictRules: {
291
288
  })[];
292
289
  'prefer-template': string;
293
290
  'require-yield': string;
294
- 'eslint-comments/disable-enable-pair': string;
295
291
  };
296
292
  export const reactRules: {
297
293
  'jsx-a11y/aria-props': string;