@elliemae/pui-cli 9.0.0-alpha.6 → 9.0.0-alpha.8

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.
@@ -4,7 +4,6 @@ import eslintConfigPrettier from 'eslint-config-prettier';
4
4
  import importX from 'eslint-plugin-import-x';
5
5
  import jest from 'eslint-plugin-jest';
6
6
  import testingLibrary from 'eslint-plugin-testing-library';
7
- import wdio from 'eslint-plugin-wdio';
8
7
  import globals from 'globals';
9
8
  import tseslint from 'typescript-eslint';
10
9
  import {
@@ -16,6 +15,7 @@ import {
16
15
  testingLibraryDomRules,
17
16
  testingLibraryReactRules,
18
17
  wdioGlobals,
18
+ wdioPlugin,
19
19
  wdioRecommendedRules,
20
20
  wdioSpecFiles,
21
21
  } from './presets.mjs';
@@ -119,10 +119,13 @@ export function createBaseFlatConfigs(tsRules) {
119
119
  },
120
120
  {
121
121
  files: wdioSpecFiles,
122
- plugins: { wdio, jest },
122
+ plugins: { wdio: wdioPlugin, jest },
123
123
  languageOptions: { globals: wdioGlobals },
124
124
  rules: {
125
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',
126
129
  'jest/valid-expect': 'off',
127
130
  },
128
131
  },
@@ -1,8 +1,11 @@
1
1
  /**
2
- * ESLint 10 removed deprecated context APIs (e.g. getSourceCode).
3
- * Wrap legacy plugins until their maintainers publish compatible releases.
2
+ * ESLint 10 removed deprecated context APIs (e.g. getSourceCode, getScope, getFilename).
3
+ * Shim only plugins that still call those APIs until maintainers publish compatible releases.
4
+ *
5
+ * @eslint/compat fixupPluginRules assumes ESLint 9 sourceCode passthroughs that are also
6
+ * unavailable in ESLint 10, so we shim legacy context methods directly on the rule context.
4
7
  */
5
- import { fixupConfigRules, fixupPluginRules } from '@eslint/compat';
8
+ import react from 'eslint-plugin-react';
6
9
  import reduxSaga from 'eslint-plugin-redux-saga';
7
10
  import storybook from 'eslint-plugin-storybook';
8
11
 
@@ -11,10 +14,139 @@ const storybookFlat =
11
14
  storybook.configs.recommended ??
12
15
  [];
13
16
 
17
+ /** @type {WeakMap<import('eslint').ESLint.Plugin, import('eslint').ESLint.Plugin>} */
18
+ const fixedPlugins = new WeakMap();
19
+
20
+ /**
21
+ * @param {import('eslint').Rule.RuleModule['create']} originalCreate
22
+ */
23
+ function wrapRuleCreateWithLegacyContext(originalCreate) {
24
+ return function create(context) {
25
+ if (
26
+ typeof context.getScope === 'function' &&
27
+ typeof context.getFilename === 'function'
28
+ ) {
29
+ return originalCreate(context);
30
+ }
31
+
32
+ const sourceCode = context.sourceCode;
33
+ let currentNode = sourceCode.ast;
34
+
35
+ const legacyContext = Object.assign(Object.create(context), {
36
+ getFilename() {
37
+ return context.filename;
38
+ },
39
+ getScope() {
40
+ return sourceCode.getScope(currentNode);
41
+ },
42
+ getAncestors() {
43
+ return sourceCode.getAncestors(currentNode);
44
+ },
45
+ getSourceCode() {
46
+ return sourceCode;
47
+ },
48
+ });
49
+
50
+ Object.freeze(legacyContext);
51
+
52
+ const visitor = originalCreate(legacyContext);
53
+
54
+ if (!visitor || typeof visitor !== 'object') {
55
+ return visitor;
56
+ }
57
+
58
+ for (const [methodName, method] of Object.entries(visitor)) {
59
+ if (typeof method !== 'function') {
60
+ continue;
61
+ }
62
+
63
+ if (methodName.startsWith('on')) {
64
+ visitor[methodName] = (...args) => {
65
+ currentNode = args[methodName === 'onCodePathSegmentLoop' ? 2 : 1];
66
+ return method.call(visitor, ...args);
67
+ };
68
+ continue;
69
+ }
70
+
71
+ visitor[methodName] = (node, ...rest) => {
72
+ currentNode = node;
73
+ return method.call(visitor, node, ...rest);
74
+ };
75
+ }
76
+
77
+ return visitor;
78
+ };
79
+ }
80
+
81
+ /**
82
+ * @param {import('eslint').ESLint.Plugin} plugin
83
+ * @returns {import('eslint').ESLint.Plugin}
84
+ */
85
+ function fixupLegacyPlugin(plugin) {
86
+ const cached = fixedPlugins.get(plugin);
87
+ if (cached) {
88
+ return cached;
89
+ }
90
+
91
+ if (!plugin.rules) {
92
+ fixedPlugins.set(plugin, plugin);
93
+ return plugin;
94
+ }
95
+
96
+ const rules = {};
97
+
98
+ for (const [ruleName, ruleDefinition] of Object.entries(plugin.rules)) {
99
+ if (typeof ruleDefinition === 'function') {
100
+ rules[ruleName] = wrapRuleCreateWithLegacyContext(ruleDefinition);
101
+ continue;
102
+ }
103
+
104
+ rules[ruleName] = {
105
+ ...ruleDefinition,
106
+ create: wrapRuleCreateWithLegacyContext(
107
+ ruleDefinition.create.bind(ruleDefinition),
108
+ ),
109
+ };
110
+ }
111
+
112
+ const fixed = { ...plugin, rules };
113
+ fixedPlugins.set(plugin, fixed);
114
+ return fixed;
115
+ }
116
+
117
+ /** @type {import('eslint').ESLint.Plugin} */
118
+ const storybookPlugin = fixupLegacyPlugin(storybook);
119
+
120
+ /**
121
+ * @param {import('eslint').Linter.Config[]} configs
122
+ * @returns {import('eslint').Linter.Config[]}
123
+ */
124
+ function fixupStorybookFlatConfigs(configs) {
125
+ return configs.map((configItem) => {
126
+ if (!configItem.plugins?.storybook) {
127
+ return configItem;
128
+ }
129
+
130
+ return {
131
+ ...configItem,
132
+ plugins: {
133
+ ...configItem.plugins,
134
+ storybook: storybookPlugin,
135
+ },
136
+ };
137
+ });
138
+ }
139
+
140
+ /** React — uses context.getFilename / getScope (eslint-plugin-react@7). */
141
+ /** @type {import('eslint').ESLint.Plugin} */
142
+ export const reactPlugin = fixupLegacyPlugin(react);
143
+
144
+ /** Redux-saga — uses context.getScope in no-yield-in-race. */
14
145
  /** @type {import('eslint').ESLint.Plugin} */
15
- export const reduxSagaPlugin = fixupPluginRules(reduxSaga);
146
+ export const reduxSagaPlugin = fixupLegacyPlugin(reduxSaga);
16
147
 
17
- /** Storybook flat presets with ESLint 10 compatibility shims applied. */
18
- export const storybookFlatConfigs = fixupConfigRules(
148
+ /** Storybook flat presets uses legacy context APIs (eslint-plugin-storybook@0.12). */
149
+ /** @type {import('eslint').Linter.Config[]} */
150
+ export const storybookFlatConfigs = fixupStorybookFlatConfigs(
19
151
  Array.isArray(storybookFlat) ? storybookFlat : [storybookFlat],
20
152
  );
@@ -75,10 +75,22 @@ export const reactPresetRules = {
75
75
  ...reduxSaga.configs.recommended.rules,
76
76
  };
77
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
+
78
87
  /** @type {import('eslint').Linter.RulesRecord} */
79
- export const wdioRecommendedRules = wdio.configs.recommended.rules ?? {};
88
+ export const wdioRecommendedRules = wdioFlatRecommended.rules ?? {};
80
89
 
81
90
  /** @type {Record<string, boolean>} */
82
- export const wdioGlobals = wdio.configs.recommended.globals ?? {};
91
+ export const wdioGlobals =
92
+ wdioFlatRecommended.languageOptions?.globals ??
93
+ wdio.configs.recommended.globals ??
94
+ {};
83
95
 
84
96
  export { storybookFlatConfigs };
@@ -1,9 +1,8 @@
1
1
  import { defineConfig } from 'eslint/config';
2
2
  import jsxA11y from 'eslint-plugin-jsx-a11y';
3
- import react from 'eslint-plugin-react';
4
3
  import reactHooks from 'eslint-plugin-react-hooks';
5
4
  import { baseFlatConfigs, baseFlatConfigsStrict } from './common.mjs';
6
- import { reduxSagaPlugin } from './compat.mjs';
5
+ import { reactPlugin, reduxSagaPlugin } from './compat.mjs';
7
6
  import {
8
7
  reactPresetRules,
9
8
  storybookFiles,
@@ -13,7 +12,7 @@ import { reactRules, reactStrictRules } from './rules.mjs';
13
12
 
14
13
  const reactPluginBlock = {
15
14
  plugins: {
16
- react,
15
+ react: reactPlugin,
17
16
  'react-hooks': reactHooks,
18
17
  'jsx-a11y': jsxA11y,
19
18
  'redux-saga': reduxSagaPlugin,
@@ -4,7 +4,6 @@ import eslintConfigPrettier from 'eslint-config-prettier';
4
4
  import importX from 'eslint-plugin-import-x';
5
5
  import jest from 'eslint-plugin-jest';
6
6
  import testingLibrary from 'eslint-plugin-testing-library';
7
- import wdio from 'eslint-plugin-wdio';
8
7
  import globals from 'globals';
9
8
  import tseslint from 'typescript-eslint';
10
9
  import {
@@ -16,6 +15,7 @@ import {
16
15
  testingLibraryDomRules,
17
16
  testingLibraryReactRules,
18
17
  wdioGlobals,
18
+ wdioPlugin,
19
19
  wdioRecommendedRules,
20
20
  wdioSpecFiles,
21
21
  } from './presets.mjs';
@@ -119,10 +119,13 @@ export function createBaseFlatConfigs(tsRules) {
119
119
  },
120
120
  {
121
121
  files: wdioSpecFiles,
122
- plugins: { wdio, jest },
122
+ plugins: { wdio: wdioPlugin, jest },
123
123
  languageOptions: { globals: wdioGlobals },
124
124
  rules: {
125
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',
126
129
  'jest/valid-expect': 'off',
127
130
  },
128
131
  },
@@ -1,8 +1,11 @@
1
1
  /**
2
- * ESLint 10 removed deprecated context APIs (e.g. getSourceCode).
3
- * Wrap legacy plugins until their maintainers publish compatible releases.
2
+ * ESLint 10 removed deprecated context APIs (e.g. getSourceCode, getScope, getFilename).
3
+ * Shim only plugins that still call those APIs until maintainers publish compatible releases.
4
+ *
5
+ * @eslint/compat fixupPluginRules assumes ESLint 9 sourceCode passthroughs that are also
6
+ * unavailable in ESLint 10, so we shim legacy context methods directly on the rule context.
4
7
  */
5
- import { fixupConfigRules, fixupPluginRules } from '@eslint/compat';
8
+ import react from 'eslint-plugin-react';
6
9
  import reduxSaga from 'eslint-plugin-redux-saga';
7
10
  import storybook from 'eslint-plugin-storybook';
8
11
 
@@ -11,10 +14,139 @@ const storybookFlat =
11
14
  storybook.configs.recommended ??
12
15
  [];
13
16
 
17
+ /** @type {WeakMap<import('eslint').ESLint.Plugin, import('eslint').ESLint.Plugin>} */
18
+ const fixedPlugins = new WeakMap();
19
+
20
+ /**
21
+ * @param {import('eslint').Rule.RuleModule['create']} originalCreate
22
+ */
23
+ function wrapRuleCreateWithLegacyContext(originalCreate) {
24
+ return function create(context) {
25
+ if (
26
+ typeof context.getScope === 'function' &&
27
+ typeof context.getFilename === 'function'
28
+ ) {
29
+ return originalCreate(context);
30
+ }
31
+
32
+ const sourceCode = context.sourceCode;
33
+ let currentNode = sourceCode.ast;
34
+
35
+ const legacyContext = Object.assign(Object.create(context), {
36
+ getFilename() {
37
+ return context.filename;
38
+ },
39
+ getScope() {
40
+ return sourceCode.getScope(currentNode);
41
+ },
42
+ getAncestors() {
43
+ return sourceCode.getAncestors(currentNode);
44
+ },
45
+ getSourceCode() {
46
+ return sourceCode;
47
+ },
48
+ });
49
+
50
+ Object.freeze(legacyContext);
51
+
52
+ const visitor = originalCreate(legacyContext);
53
+
54
+ if (!visitor || typeof visitor !== 'object') {
55
+ return visitor;
56
+ }
57
+
58
+ for (const [methodName, method] of Object.entries(visitor)) {
59
+ if (typeof method !== 'function') {
60
+ continue;
61
+ }
62
+
63
+ if (methodName.startsWith('on')) {
64
+ visitor[methodName] = (...args) => {
65
+ currentNode = args[methodName === 'onCodePathSegmentLoop' ? 2 : 1];
66
+ return method.call(visitor, ...args);
67
+ };
68
+ continue;
69
+ }
70
+
71
+ visitor[methodName] = (node, ...rest) => {
72
+ currentNode = node;
73
+ return method.call(visitor, node, ...rest);
74
+ };
75
+ }
76
+
77
+ return visitor;
78
+ };
79
+ }
80
+
81
+ /**
82
+ * @param {import('eslint').ESLint.Plugin} plugin
83
+ * @returns {import('eslint').ESLint.Plugin}
84
+ */
85
+ function fixupLegacyPlugin(plugin) {
86
+ const cached = fixedPlugins.get(plugin);
87
+ if (cached) {
88
+ return cached;
89
+ }
90
+
91
+ if (!plugin.rules) {
92
+ fixedPlugins.set(plugin, plugin);
93
+ return plugin;
94
+ }
95
+
96
+ const rules = {};
97
+
98
+ for (const [ruleName, ruleDefinition] of Object.entries(plugin.rules)) {
99
+ if (typeof ruleDefinition === 'function') {
100
+ rules[ruleName] = wrapRuleCreateWithLegacyContext(ruleDefinition);
101
+ continue;
102
+ }
103
+
104
+ rules[ruleName] = {
105
+ ...ruleDefinition,
106
+ create: wrapRuleCreateWithLegacyContext(
107
+ ruleDefinition.create.bind(ruleDefinition),
108
+ ),
109
+ };
110
+ }
111
+
112
+ const fixed = { ...plugin, rules };
113
+ fixedPlugins.set(plugin, fixed);
114
+ return fixed;
115
+ }
116
+
117
+ /** @type {import('eslint').ESLint.Plugin} */
118
+ const storybookPlugin = fixupLegacyPlugin(storybook);
119
+
120
+ /**
121
+ * @param {import('eslint').Linter.Config[]} configs
122
+ * @returns {import('eslint').Linter.Config[]}
123
+ */
124
+ function fixupStorybookFlatConfigs(configs) {
125
+ return configs.map((configItem) => {
126
+ if (!configItem.plugins?.storybook) {
127
+ return configItem;
128
+ }
129
+
130
+ return {
131
+ ...configItem,
132
+ plugins: {
133
+ ...configItem.plugins,
134
+ storybook: storybookPlugin,
135
+ },
136
+ };
137
+ });
138
+ }
139
+
140
+ /** React — uses context.getFilename / getScope (eslint-plugin-react@7). */
141
+ /** @type {import('eslint').ESLint.Plugin} */
142
+ export const reactPlugin = fixupLegacyPlugin(react);
143
+
144
+ /** Redux-saga — uses context.getScope in no-yield-in-race. */
14
145
  /** @type {import('eslint').ESLint.Plugin} */
15
- export const reduxSagaPlugin = fixupPluginRules(reduxSaga);
146
+ export const reduxSagaPlugin = fixupLegacyPlugin(reduxSaga);
16
147
 
17
- /** Storybook flat presets with ESLint 10 compatibility shims applied. */
18
- export const storybookFlatConfigs = fixupConfigRules(
148
+ /** Storybook flat presets uses legacy context APIs (eslint-plugin-storybook@0.12). */
149
+ /** @type {import('eslint').Linter.Config[]} */
150
+ export const storybookFlatConfigs = fixupStorybookFlatConfigs(
19
151
  Array.isArray(storybookFlat) ? storybookFlat : [storybookFlat],
20
152
  );
@@ -75,10 +75,22 @@ export const reactPresetRules = {
75
75
  ...reduxSaga.configs.recommended.rules,
76
76
  };
77
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
+
78
87
  /** @type {import('eslint').Linter.RulesRecord} */
79
- export const wdioRecommendedRules = wdio.configs.recommended.rules ?? {};
88
+ export const wdioRecommendedRules = wdioFlatRecommended.rules ?? {};
80
89
 
81
90
  /** @type {Record<string, boolean>} */
82
- export const wdioGlobals = wdio.configs.recommended.globals ?? {};
91
+ export const wdioGlobals =
92
+ wdioFlatRecommended.languageOptions?.globals ??
93
+ wdio.configs.recommended.globals ??
94
+ {};
83
95
 
84
96
  export { storybookFlatConfigs };
@@ -1,9 +1,8 @@
1
1
  import { defineConfig } from 'eslint/config';
2
2
  import jsxA11y from 'eslint-plugin-jsx-a11y';
3
- import react from 'eslint-plugin-react';
4
3
  import reactHooks from 'eslint-plugin-react-hooks';
5
4
  import { baseFlatConfigs, baseFlatConfigsStrict } from './common.mjs';
6
- import { reduxSagaPlugin } from './compat.mjs';
5
+ import { reactPlugin, reduxSagaPlugin } from './compat.mjs';
7
6
  import {
8
7
  reactPresetRules,
9
8
  storybookFiles,
@@ -13,7 +12,7 @@ import { reactRules, reactStrictRules } from './rules.mjs';
13
12
 
14
13
  const reactPluginBlock = {
15
14
  plugins: {
16
- react,
15
+ react: reactPlugin,
17
16
  'react-hooks': reactHooks,
18
17
  'jsx-a11y': jsxA11y,
19
18
  'redux-saga': reduxSagaPlugin,
@@ -1,4 +1,9 @@
1
+ /** React — uses context.getFilename / getScope (eslint-plugin-react@7). */
2
+ /** @type {import('eslint').ESLint.Plugin} */
3
+ export const reactPlugin: import("eslint").ESLint.Plugin;
4
+ /** Redux-saga — uses context.getScope in no-yield-in-race. */
1
5
  /** @type {import('eslint').ESLint.Plugin} */
2
6
  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;
7
+ /** Storybook flat presets uses legacy context APIs (eslint-plugin-storybook@0.12). */
8
+ /** @type {import('eslint').Linter.Config[]} */
9
+ export const storybookFlatConfigs: import("eslint").Linter.Config[];
@@ -18,6 +18,14 @@ 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>} */