@pob/codemods 0.17.1 → 0.18.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/bin/migrate.mjs CHANGED
@@ -13,7 +13,7 @@ const globPaths = argv[1];
13
13
  if (!transformerName || !globPaths) {
14
14
  const transformerList = readdirSync(
15
15
  new URL("../lib/transforms/", import.meta.url),
16
- );
16
+ ).filter((fileName) => fileName.endsWith(".cjs"));
17
17
  throw new Error(
18
18
  `Usage: pob-migrate <transformer-name> <glob-paths>\n${transformerList
19
19
  .map((t) => `- "${t.slice(0, -4)}"`)
@@ -0,0 +1,492 @@
1
+ {
2
+ "@pob/forbid-non-native-fetch-import": "@pob/forbid-non-native-fetch-import",
3
+ "@pob/forbid-non-native-node-imports": "@pob/forbid-non-native-node-imports",
4
+ "@pob/no-useless-template-literal": "@pob/no-useless-template-literal",
5
+ "@pob/react-function-component-return-react-node": "@pob/react-function-component-return-react-node",
6
+ "@pob/react-named-import": "@pob/react-named-import",
7
+ "@typescript-eslint/adjacent-overload-signatures": "typescript/adjacent-overload-signatures",
8
+ "@typescript-eslint/array-type": "typescript/array-type",
9
+ "@typescript-eslint/await-thenable": "typescript/await-thenable",
10
+ "@typescript-eslint/ban-ts-comment": "typescript/ban-ts-comment",
11
+ "@typescript-eslint/ban-tslint-comment": "typescript/ban-tslint-comment",
12
+ "@typescript-eslint/class-literal-property-style": "typescript/class-literal-property-style",
13
+ "@typescript-eslint/consistent-generic-constructors": "typescript/consistent-generic-constructors",
14
+ "@typescript-eslint/consistent-indexed-object-style": "typescript/consistent-indexed-object-style",
15
+ "@typescript-eslint/consistent-type-assertions": "typescript/consistent-type-assertions",
16
+ "@typescript-eslint/consistent-type-definitions": "typescript/consistent-type-definitions",
17
+ "@typescript-eslint/consistent-type-exports": "typescript/consistent-type-exports",
18
+ "@typescript-eslint/consistent-type-imports": "typescript/consistent-type-imports",
19
+ "@typescript-eslint/default-param-last": "default-param-last",
20
+ "@typescript-eslint/dot-notation": "typescript/dot-notation",
21
+ "@typescript-eslint/explicit-function-return-type": "typescript/explicit-function-return-type",
22
+ "@typescript-eslint/explicit-module-boundary-types": "typescript/explicit-module-boundary-types",
23
+ "@typescript-eslint/max-params": "max-params",
24
+ "@typescript-eslint/method-signature-style": "typescript/method-signature-style",
25
+ "@typescript-eslint/no-array-constructor": "no-array-constructor",
26
+ "@typescript-eslint/no-array-delete": "typescript/no-array-delete",
27
+ "@typescript-eslint/no-base-to-string": "typescript/no-base-to-string",
28
+ "@typescript-eslint/no-confusing-non-null-assertion": "typescript/no-confusing-non-null-assertion",
29
+ "@typescript-eslint/no-confusing-void-expression": "typescript/no-confusing-void-expression",
30
+ "@typescript-eslint/no-deprecated": "typescript/no-deprecated",
31
+ "@typescript-eslint/no-dupe-class-members": "no-dupe-class-members",
32
+ "@typescript-eslint/no-duplicate-enum-values": "typescript/no-duplicate-enum-values",
33
+ "@typescript-eslint/no-duplicate-type-constituents": "typescript/no-duplicate-type-constituents",
34
+ "@typescript-eslint/no-dynamic-delete": "typescript/no-dynamic-delete",
35
+ "@typescript-eslint/no-empty-function": "no-empty-function",
36
+ "@typescript-eslint/no-empty-object-type": "typescript/no-empty-object-type",
37
+ "@typescript-eslint/no-explicit-any": "typescript/no-explicit-any",
38
+ "@typescript-eslint/no-extra-non-null-assertion": "typescript/no-extra-non-null-assertion",
39
+ "@typescript-eslint/no-floating-promises": "typescript/no-floating-promises",
40
+ "@typescript-eslint/no-for-in-array": "typescript/no-for-in-array",
41
+ "@typescript-eslint/no-implied-eval": "typescript/no-implied-eval",
42
+ "@typescript-eslint/no-import-type-side-effects": "typescript/no-import-type-side-effects",
43
+ "@typescript-eslint/no-inferrable-types": "typescript/no-inferrable-types",
44
+ "@typescript-eslint/no-invalid-void-type": "typescript/no-invalid-void-type",
45
+ "@typescript-eslint/no-loop-func": "no-loop-func",
46
+ "@typescript-eslint/no-loss-of-precision": "no-loss-of-precision",
47
+ "@typescript-eslint/no-meaningless-void-operator": "typescript/no-meaningless-void-operator",
48
+ "@typescript-eslint/no-misused-new": "typescript/no-misused-new",
49
+ "@typescript-eslint/no-misused-promises": "typescript/no-misused-promises",
50
+ "@typescript-eslint/no-misused-spread": "typescript/no-misused-spread",
51
+ "@typescript-eslint/no-mixed-enums": "typescript/no-mixed-enums",
52
+ "@typescript-eslint/no-namespace": "typescript/no-namespace",
53
+ "@typescript-eslint/no-non-null-asserted-nullish-coalescing": "typescript/no-non-null-asserted-nullish-coalescing",
54
+ "@typescript-eslint/no-non-null-asserted-optional-chain": "typescript/no-non-null-asserted-optional-chain",
55
+ "@typescript-eslint/no-non-null-assertion": "typescript/no-non-null-assertion",
56
+ "@typescript-eslint/no-redeclare": "no-redeclare",
57
+ "@typescript-eslint/no-redundant-type-constituents": "typescript/no-redundant-type-constituents",
58
+ "@typescript-eslint/no-this-alias": "typescript/no-this-alias",
59
+ "@typescript-eslint/no-unnecessary-boolean-literal-compare": "typescript/no-unnecessary-boolean-literal-compare",
60
+ "@typescript-eslint/no-unnecessary-condition": "typescript/no-unnecessary-condition",
61
+ "@typescript-eslint/no-unnecessary-qualifier": "typescript/no-unnecessary-qualifier",
62
+ "@typescript-eslint/no-unnecessary-template-expression": "typescript/no-unnecessary-template-expression",
63
+ "@typescript-eslint/no-unnecessary-type-assertion": "typescript/no-unnecessary-type-assertion",
64
+ "@typescript-eslint/no-unnecessary-type-constraint": "typescript/no-unnecessary-type-constraint",
65
+ "@typescript-eslint/no-unnecessary-type-conversion": "typescript/no-unnecessary-type-conversion",
66
+ "@typescript-eslint/no-unnecessary-type-parameters": "typescript/no-unnecessary-type-parameters",
67
+ "@typescript-eslint/no-unsafe-argument": "typescript/no-unsafe-argument",
68
+ "@typescript-eslint/no-unsafe-assignment": "typescript/no-unsafe-assignment",
69
+ "@typescript-eslint/no-unsafe-call": "typescript/no-unsafe-call",
70
+ "@typescript-eslint/no-unsafe-declaration-merging": "typescript/no-unsafe-declaration-merging",
71
+ "@typescript-eslint/no-unsafe-enum-comparison": "typescript/no-unsafe-enum-comparison",
72
+ "@typescript-eslint/no-unsafe-function-type": "typescript/no-unsafe-function-type",
73
+ "@typescript-eslint/no-unsafe-member-access": "typescript/no-unsafe-member-access",
74
+ "@typescript-eslint/no-unsafe-return": "typescript/no-unsafe-return",
75
+ "@typescript-eslint/no-unsafe-unary-minus": "typescript/no-unsafe-unary-minus",
76
+ "@typescript-eslint/no-unused-expressions": "no-unused-expressions",
77
+ "@typescript-eslint/no-unused-private-class-members": "no-unused-private-class-members",
78
+ "@typescript-eslint/no-unused-vars": "no-unused-vars",
79
+ "@typescript-eslint/no-use-before-define": "no-use-before-define",
80
+ "@typescript-eslint/no-useless-constructor": "no-useless-constructor",
81
+ "@typescript-eslint/no-useless-default-assignment": "typescript/no-useless-default-assignment",
82
+ "@typescript-eslint/no-useless-empty-export": "typescript/no-useless-empty-export",
83
+ "@typescript-eslint/no-wrapper-object-types": "typescript/no-wrapper-object-types",
84
+ "@typescript-eslint/non-nullable-type-assertion-style": "typescript/non-nullable-type-assertion-style",
85
+ "@typescript-eslint/only-throw-error": "typescript/only-throw-error",
86
+ "@typescript-eslint/parameter-properties": "typescript/parameter-properties",
87
+ "@typescript-eslint/prefer-as-const": "typescript/prefer-as-const",
88
+ "@typescript-eslint/prefer-enum-initializers": "typescript/prefer-enum-initializers",
89
+ "@typescript-eslint/prefer-find": "typescript/prefer-find",
90
+ "@typescript-eslint/prefer-function-type": "typescript/prefer-function-type",
91
+ "@typescript-eslint/prefer-literal-enum-member": "typescript/prefer-literal-enum-member",
92
+ "@typescript-eslint/prefer-namespace-keyword": "typescript/prefer-namespace-keyword",
93
+ "@typescript-eslint/prefer-optional-chain": "typescript/prefer-optional-chain",
94
+ "@typescript-eslint/prefer-promise-reject-errors": "typescript/prefer-promise-reject-errors",
95
+ "@typescript-eslint/prefer-reduce-type-parameter": "typescript/prefer-reduce-type-parameter",
96
+ "@typescript-eslint/prefer-regexp-exec": "typescript/prefer-regexp-exec",
97
+ "@typescript-eslint/prefer-return-this-type": "typescript/prefer-return-this-type",
98
+ "@typescript-eslint/prefer-string-starts-ends-with": "typescript/prefer-string-starts-ends-with",
99
+ "@typescript-eslint/related-getter-setter-pairs": "typescript/related-getter-setter-pairs",
100
+ "@typescript-eslint/require-await": "typescript/require-await",
101
+ "@typescript-eslint/restrict-plus-operands": "typescript/restrict-plus-operands",
102
+ "@typescript-eslint/restrict-template-expressions": "typescript/restrict-template-expressions",
103
+ "@typescript-eslint/return-await": "typescript/return-await",
104
+ "@typescript-eslint/switch-exhaustiveness-check": "typescript/switch-exhaustiveness-check",
105
+ "@typescript-eslint/triple-slash-reference": "typescript/triple-slash-reference",
106
+ "@typescript-eslint/unified-signatures": "typescript/unified-signatures",
107
+ "@typescript-eslint/use-unknown-in-catch-callback-variable": "typescript/use-unknown-in-catch-callback-variable",
108
+ "array-callback-return": "array-callback-return",
109
+ "block-scoped-var": "block-scoped-var",
110
+ "camelcase": "eslint-js/camelcase",
111
+ "complexity": "complexity",
112
+ "constructor-super": "constructor-super",
113
+ "curly": "curly",
114
+ "default-case": "default-case",
115
+ "default-case-last": "default-case-last",
116
+ "default-param-last": "default-param-last",
117
+ "dot-notation": "eslint-js/dot-notation",
118
+ "eqeqeq": "eqeqeq",
119
+ "for-direction": "for-direction",
120
+ "func-names": "func-names",
121
+ "getter-return": "getter-return",
122
+ "grouped-accessor-pairs": "grouped-accessor-pairs",
123
+ "guard-for-in": "guard-for-in",
124
+ "import-x/extensions": "import/extensions",
125
+ "import-x/first": "import/first",
126
+ "import-x/named": "import/named",
127
+ "import-x/namespace": "import/namespace",
128
+ "import-x/newline-after-import": "import/newline-after-import",
129
+ "import-x/no-absolute-path": "import/no-absolute-path",
130
+ "import-x/no-amd": "import/no-amd",
131
+ "import-x/no-anonymous-default-export": "import/no-anonymous-default-export",
132
+ "import-x/no-commonjs": "import/no-commonjs",
133
+ "import-x/no-cycle": "import/no-cycle",
134
+ "import-x/no-duplicates": "import/no-duplicates",
135
+ "import-x/no-dynamic-require": "import/no-dynamic-require",
136
+ "import-x/no-empty-named-blocks": "import/no-empty-named-blocks",
137
+ "import-x/no-mutable-exports": "import/no-mutable-exports",
138
+ "import-x/no-named-as-default": "import/no-named-as-default",
139
+ "import-x/no-named-as-default-member": "import/no-named-as-default-member",
140
+ "import-x/no-named-default": "import/no-named-default",
141
+ "import-x/no-self-import": "import/no-self-import",
142
+ "import-x/no-webpack-loader-syntax": "import/no-webpack-loader-syntax",
143
+ "jsx-a11y/alt-text": "jsx_a11y/alt-text",
144
+ "jsx-a11y/anchor-has-content": "jsx_a11y/anchor-has-content",
145
+ "jsx-a11y/anchor-is-valid": "jsx_a11y/anchor-is-valid",
146
+ "jsx-a11y/aria-activedescendant-has-tabindex": "jsx_a11y/aria-activedescendant-has-tabindex",
147
+ "jsx-a11y/aria-props": "jsx_a11y/aria-props",
148
+ "jsx-a11y/aria-proptypes": "jsx_a11y/aria-proptypes",
149
+ "jsx-a11y/aria-role": "jsx_a11y/aria-role",
150
+ "jsx-a11y/aria-unsupported-elements": "jsx_a11y/aria-unsupported-elements",
151
+ "jsx-a11y/autocomplete-valid": "jsx_a11y/autocomplete-valid",
152
+ "jsx-a11y/click-events-have-key-events": "jsx_a11y/click-events-have-key-events",
153
+ "jsx-a11y/heading-has-content": "jsx_a11y/heading-has-content",
154
+ "jsx-a11y/html-has-lang": "jsx_a11y/html-has-lang",
155
+ "jsx-a11y/iframe-has-title": "jsx_a11y/iframe-has-title",
156
+ "jsx-a11y/img-redundant-alt": "jsx_a11y/img-redundant-alt",
157
+ "jsx-a11y/interactive-supports-focus": "jsx_a11y/interactive-supports-focus",
158
+ "jsx-a11y/label-has-associated-control": "jsx_a11y/label-has-associated-control",
159
+ "jsx-a11y/media-has-caption": "jsx_a11y/media-has-caption",
160
+ "jsx-a11y/mouse-events-have-key-events": "jsx_a11y/mouse-events-have-key-events",
161
+ "jsx-a11y/no-access-key": "jsx_a11y/no-access-key",
162
+ "jsx-a11y/no-autofocus": "jsx_a11y/no-autofocus",
163
+ "jsx-a11y/no-distracting-elements": "jsx_a11y/no-distracting-elements",
164
+ "jsx-a11y/no-interactive-element-to-noninteractive-role": "jsx_a11y/no-interactive-element-to-noninteractive-role",
165
+ "jsx-a11y/no-noninteractive-element-interactions": "jsx_a11y/no-noninteractive-element-interactions",
166
+ "jsx-a11y/no-noninteractive-element-to-interactive-role": "jsx_a11y/no-noninteractive-element-to-interactive-role",
167
+ "jsx-a11y/no-noninteractive-tabindex": "jsx_a11y/no-noninteractive-tabindex",
168
+ "jsx-a11y/no-redundant-roles": "jsx_a11y/no-redundant-roles",
169
+ "jsx-a11y/no-static-element-interactions": "jsx_a11y/no-static-element-interactions",
170
+ "jsx-a11y/role-has-required-aria-props": "jsx_a11y/role-has-required-aria-props",
171
+ "jsx-a11y/role-supports-aria-props": "jsx_a11y/role-supports-aria-props",
172
+ "jsx-a11y/scope": "jsx_a11y/scope",
173
+ "jsx-a11y/tabindex-no-positive": "jsx_a11y/tabindex-no-positive",
174
+ "max-depth": "max-depth",
175
+ "max-params": "max-params",
176
+ "n/no-deprecated-api": "node-js/no-deprecated-api",
177
+ "n/no-exports-assign": "node/no-exports-assign",
178
+ "n/no-new-require": "node/no-new-require",
179
+ "n/no-path-concat": "node/no-path-concat",
180
+ "n/no-unpublished-bin": "node-js/no-unpublished-bin",
181
+ "n/no-unsupported-features/es-builtins": "node-js/no-unsupported-features/es-builtins",
182
+ "n/no-unsupported-features/es-syntax": "node-js/no-unsupported-features/es-syntax",
183
+ "n/no-unsupported-features/node-builtins": "node-js/no-unsupported-features/node-builtins",
184
+ "new-cap": "new-cap",
185
+ "no-alert": "no-alert",
186
+ "no-array-constructor": "no-array-constructor",
187
+ "no-async-promise-executor": "no-async-promise-executor",
188
+ "no-bitwise": "no-bitwise",
189
+ "no-caller": "no-caller",
190
+ "no-case-declarations": "no-case-declarations",
191
+ "no-class-assign": "no-class-assign",
192
+ "no-compare-neg-zero": "no-compare-neg-zero",
193
+ "no-cond-assign": "no-cond-assign",
194
+ "no-console": "no-console",
195
+ "no-const-assign": "no-const-assign",
196
+ "no-constant-binary-expression": "no-constant-binary-expression",
197
+ "no-constant-condition": "no-constant-condition",
198
+ "no-constructor-return": "no-constructor-return",
199
+ "no-control-regex": "no-control-regex",
200
+ "no-debugger": "no-debugger",
201
+ "no-delete-var": "no-delete-var",
202
+ "no-dupe-class-members": "no-dupe-class-members",
203
+ "no-dupe-else-if": "no-dupe-else-if",
204
+ "no-dupe-keys": "no-dupe-keys",
205
+ "no-duplicate-case": "no-duplicate-case",
206
+ "no-empty-character-class": "no-empty-character-class",
207
+ "no-empty-function": "no-empty-function",
208
+ "no-empty-pattern": "no-empty-pattern",
209
+ "no-empty-static-block": "no-empty-static-block",
210
+ "no-eval": "no-eval",
211
+ "no-ex-assign": "no-ex-assign",
212
+ "no-extend-native": "no-extend-native",
213
+ "no-extra-bind": "no-extra-bind",
214
+ "no-extra-boolean-cast": "no-extra-boolean-cast",
215
+ "no-extra-label": "no-extra-label",
216
+ "no-fallthrough": "no-fallthrough",
217
+ "no-func-assign": "no-func-assign",
218
+ "no-global-assign": "no-global-assign",
219
+ "no-implied-eval": "no-implied-eval",
220
+ "no-import-assign": "no-import-assign",
221
+ "no-invalid-regexp": "no-invalid-regexp",
222
+ "no-irregular-whitespace": "no-irregular-whitespace",
223
+ "no-label-var": "no-label-var",
224
+ "no-labels": "no-labels",
225
+ "no-lone-blocks": "eslint-js/no-lone-blocks",
226
+ "no-lonely-if": "no-lonely-if",
227
+ "no-loop-func": "no-loop-func",
228
+ "no-loss-of-precision": "no-loss-of-precision",
229
+ "no-misleading-character-class": "no-misleading-character-class",
230
+ "no-multi-assign": "no-multi-assign",
231
+ "no-multi-str": "no-multi-str",
232
+ "no-nested-ternary": "no-nested-ternary",
233
+ "no-new": "no-new",
234
+ "no-new-func": "no-new-func",
235
+ "no-new-native-nonconstructor": "no-new-native-nonconstructor",
236
+ "no-new-wrappers": "no-new-wrappers",
237
+ "no-nonoctal-decimal-escape": "no-nonoctal-decimal-escape",
238
+ "no-obj-calls": "no-obj-calls",
239
+ "no-object-constructor": "no-object-constructor",
240
+ "no-promise-executor-return": "no-promise-executor-return",
241
+ "no-proto": "no-proto",
242
+ "no-prototype-builtins": "no-prototype-builtins",
243
+ "no-redeclare": "no-redeclare",
244
+ "no-regex-spaces": "no-regex-spaces",
245
+ "no-restricted-exports": "no-restricted-exports",
246
+ "no-restricted-globals": "no-restricted-globals",
247
+ "no-restricted-properties": "no-restricted-properties",
248
+ "no-restricted-syntax": "eslint-js/no-restricted-syntax",
249
+ "no-return-assign": "no-return-assign",
250
+ "no-script-url": "no-script-url",
251
+ "no-self-assign": "no-self-assign",
252
+ "no-self-compare": "no-self-compare",
253
+ "no-sequences": "no-sequences",
254
+ "no-setter-return": "no-setter-return",
255
+ "no-shadow-restricted-names": "no-shadow-restricted-names",
256
+ "no-sparse-arrays": "no-sparse-arrays",
257
+ "no-template-curly-in-string": "no-template-curly-in-string",
258
+ "no-this-before-super": "no-this-before-super",
259
+ "no-throw-literal": "no-throw-literal",
260
+ "no-unassigned-vars": "no-unassigned-vars",
261
+ "no-undef-init": "eslint-js/no-undef-init",
262
+ "no-unexpected-multiline": "no-unexpected-multiline",
263
+ "no-unneeded-ternary": "no-unneeded-ternary",
264
+ "no-unreachable": "no-unreachable",
265
+ "no-unreachable-loop": "no-unreachable-loop",
266
+ "no-unsafe-finally": "no-unsafe-finally",
267
+ "no-unsafe-negation": "no-unsafe-negation",
268
+ "no-unsafe-optional-chaining": "no-unsafe-optional-chaining",
269
+ "no-unused-expressions": "no-unused-expressions",
270
+ "no-unused-labels": "no-unused-labels",
271
+ "no-unused-private-class-members": "no-unused-private-class-members",
272
+ "no-unused-vars": "no-unused-vars",
273
+ "no-use-before-define": "no-use-before-define",
274
+ "no-useless-assignment": "no-useless-assignment",
275
+ "no-useless-backreference": "no-useless-backreference",
276
+ "no-useless-catch": "no-useless-catch",
277
+ "no-useless-computed-key": "no-useless-computed-key",
278
+ "no-useless-concat": "no-useless-concat",
279
+ "no-useless-constructor": "no-useless-constructor",
280
+ "no-useless-escape": "no-useless-escape",
281
+ "no-useless-rename": "no-useless-rename",
282
+ "no-useless-return": "no-useless-return",
283
+ "no-var": "no-var",
284
+ "no-void": "no-void",
285
+ "no-with": "no-with",
286
+ "object-shorthand": "object-shorthand",
287
+ "prefer-arrow-callback": "prefer-arrow-callback",
288
+ "prefer-const": "prefer-const",
289
+ "prefer-exponentiation-operator": "prefer-exponentiation-operator",
290
+ "prefer-numeric-literals": "prefer-numeric-literals",
291
+ "prefer-object-has-own": "prefer-object-has-own",
292
+ "prefer-object-spread": "prefer-object-spread",
293
+ "prefer-promise-reject-errors": "prefer-promise-reject-errors",
294
+ "prefer-regex-literals": "prefer-regex-literals",
295
+ "prefer-rest-params": "prefer-rest-params",
296
+ "prefer-spread": "prefer-spread",
297
+ "prefer-template": "prefer-template",
298
+ "preserve-caught-error": "preserve-caught-error",
299
+ "radix": "radix",
300
+ "react-hooks/exhaustive-deps": "react/exhaustive-deps",
301
+ "react-hooks/rules-of-hooks": "react/rules-of-hooks",
302
+ "react/button-has-type": "react/button-has-type",
303
+ "react/function-component-definition": "react/function-component-definition",
304
+ "react/hook-use-state": "react/hook-use-state",
305
+ "react/jsx-boolean-value": "react/jsx-boolean-value",
306
+ "react/jsx-curly-brace-presence": "react/jsx-curly-brace-presence",
307
+ "react/jsx-fragments": "react/jsx-fragments",
308
+ "react/jsx-key": "react/jsx-key",
309
+ "react/jsx-no-comment-textnodes": "react/jsx-no-comment-textnodes",
310
+ "react/jsx-no-constructed-context-values": "react/jsx-no-constructed-context-values",
311
+ "react/jsx-no-duplicate-props": "react/jsx-no-duplicate-props",
312
+ "react/jsx-no-leaked-render": "@pob/jsx-no-leaked-render",
313
+ "react/jsx-no-script-url": "react/jsx-no-script-url",
314
+ "react/jsx-no-target-blank": "react/jsx-no-target-blank",
315
+ "react/jsx-no-undef": "react/jsx-no-undef",
316
+ "react/jsx-no-useless-fragment": "react/jsx-no-useless-fragment",
317
+ "react/jsx-pascal-case": "react/jsx-pascal-case",
318
+ "react/jsx-sort-props": "@pob/jsx-sort-props",
319
+ "react/no-array-index-key": "react/no-array-index-key",
320
+ "react/no-children-prop": "react/no-children-prop",
321
+ "react/no-danger-with-children": "react/no-danger-with-children",
322
+ "react/no-direct-mutation-state": "react/no-direct-mutation-state",
323
+ "react/no-find-dom-node": "react/no-find-dom-node",
324
+ "react/no-is-mounted": "react/no-is-mounted",
325
+ "react/no-namespace": "react/no-namespace",
326
+ "react/no-render-return-value": "react/no-render-return-value",
327
+ "react/no-string-refs": "react/no-string-refs",
328
+ "react/no-unknown-property": "react/no-unknown-property",
329
+ "react/no-unstable-nested-components": "react/no-unstable-nested-components",
330
+ "react/prefer-es6-class": "react/prefer-es6-class",
331
+ "react/require-render-return": "react/require-render-return",
332
+ "react/self-closing-comp": "react/self-closing-comp",
333
+ "regexp/confusing-quantifier": "regexp/confusing-quantifier",
334
+ "regexp/control-character-escape": "regexp/control-character-escape",
335
+ "regexp/match-any": "regexp/match-any",
336
+ "regexp/negation": "regexp/negation",
337
+ "regexp/no-contradiction-with-assertion": "regexp/no-contradiction-with-assertion",
338
+ "regexp/no-dupe-characters-character-class": "regexp/no-dupe-characters-character-class",
339
+ "regexp/no-dupe-disjunctions": "regexp/no-dupe-disjunctions",
340
+ "regexp/no-empty-alternative": "regexp/no-empty-alternative",
341
+ "regexp/no-empty-capturing-group": "regexp/no-empty-capturing-group",
342
+ "regexp/no-empty-character-class": "regexp/no-empty-character-class",
343
+ "regexp/no-empty-group": "regexp/no-empty-group",
344
+ "regexp/no-empty-lookarounds-assertion": "regexp/no-empty-lookarounds-assertion",
345
+ "regexp/no-empty-string-literal": "regexp/no-empty-string-literal",
346
+ "regexp/no-escape-backspace": "regexp/no-escape-backspace",
347
+ "regexp/no-extra-lookaround-assertions": "regexp/no-extra-lookaround-assertions",
348
+ "regexp/no-invalid-regexp": "regexp/no-invalid-regexp",
349
+ "regexp/no-invisible-character": "regexp/no-invisible-character",
350
+ "regexp/no-lazy-ends": "regexp/no-lazy-ends",
351
+ "regexp/no-legacy-features": "regexp/no-legacy-features",
352
+ "regexp/no-misleading-capturing-group": "regexp/no-misleading-capturing-group",
353
+ "regexp/no-misleading-unicode-character": "regexp/no-misleading-unicode-character",
354
+ "regexp/no-missing-g-flag": "regexp/no-missing-g-flag",
355
+ "regexp/no-non-standard-flag": "regexp/no-non-standard-flag",
356
+ "regexp/no-obscure-range": "regexp/no-obscure-range",
357
+ "regexp/no-optional-assertion": "regexp/no-optional-assertion",
358
+ "regexp/no-potentially-useless-backreference": "regexp/no-potentially-useless-backreference",
359
+ "regexp/no-super-linear-backtracking": "regexp/no-super-linear-backtracking",
360
+ "regexp/no-trivially-nested-assertion": "regexp/no-trivially-nested-assertion",
361
+ "regexp/no-trivially-nested-quantifier": "regexp/no-trivially-nested-quantifier",
362
+ "regexp/no-unused-capturing-group": "regexp/no-unused-capturing-group",
363
+ "regexp/no-useless-assertions": "regexp/no-useless-assertions",
364
+ "regexp/no-useless-backreference": "regexp/no-useless-backreference",
365
+ "regexp/no-useless-character-class": "regexp/no-useless-character-class",
366
+ "regexp/no-useless-dollar-replacements": "regexp/no-useless-dollar-replacements",
367
+ "regexp/no-useless-escape": "regexp/no-useless-escape",
368
+ "regexp/no-useless-flag": "regexp/no-useless-flag",
369
+ "regexp/no-useless-lazy": "regexp/no-useless-lazy",
370
+ "regexp/no-useless-non-capturing-group": "regexp/no-useless-non-capturing-group",
371
+ "regexp/no-useless-quantifier": "regexp/no-useless-quantifier",
372
+ "regexp/no-useless-range": "regexp/no-useless-range",
373
+ "regexp/no-useless-set-operand": "regexp/no-useless-set-operand",
374
+ "regexp/no-useless-string-literal": "regexp/no-useless-string-literal",
375
+ "regexp/no-useless-two-nums-quantifier": "regexp/no-useless-two-nums-quantifier",
376
+ "regexp/no-zero-quantifier": "regexp/no-zero-quantifier",
377
+ "regexp/optimal-lookaround-quantifier": "regexp/optimal-lookaround-quantifier",
378
+ "regexp/optimal-quantifier-concatenation": "regexp/optimal-quantifier-concatenation",
379
+ "regexp/prefer-character-class": "regexp/prefer-character-class",
380
+ "regexp/prefer-d": "regexp/prefer-d",
381
+ "regexp/prefer-plus-quantifier": "regexp/prefer-plus-quantifier",
382
+ "regexp/prefer-predefined-assertion": "regexp/prefer-predefined-assertion",
383
+ "regexp/prefer-question-quantifier": "regexp/prefer-question-quantifier",
384
+ "regexp/prefer-range": "regexp/prefer-range",
385
+ "regexp/prefer-set-operation": "regexp/prefer-set-operation",
386
+ "regexp/prefer-star-quantifier": "regexp/prefer-star-quantifier",
387
+ "regexp/prefer-unicode-codepoint-escapes": "regexp/prefer-unicode-codepoint-escapes",
388
+ "regexp/prefer-w": "regexp/prefer-w",
389
+ "regexp/simplify-set-operations": "regexp/simplify-set-operations",
390
+ "regexp/sort-flags": "regexp/sort-flags",
391
+ "regexp/strict": "regexp/strict",
392
+ "regexp/use-ignore-case": "regexp/use-ignore-case",
393
+ "require-await": "require-await",
394
+ "require-yield": "require-yield",
395
+ "sort-imports": "sort-imports",
396
+ "symbol-description": "symbol-description",
397
+ "unicorn/catch-error-name": "unicorn/catch-error-name",
398
+ "unicorn/consistent-assert": "unicorn/consistent-assert",
399
+ "unicorn/consistent-date-clone": "unicorn/consistent-date-clone",
400
+ "unicorn/consistent-existence-index-check": "unicorn/consistent-existence-index-check",
401
+ "unicorn/consistent-template-literal-escape": "unicorn/consistent-template-literal-escape",
402
+ "unicorn/custom-error-definition": "unicorn/custom-error-definition",
403
+ "unicorn/error-message": "unicorn/error-message",
404
+ "unicorn/escape-case": "unicorn/escape-case",
405
+ "unicorn/expiring-todo-comments": "unicorn-js/expiring-todo-comments",
406
+ "unicorn/explicit-length-check": "unicorn/explicit-length-check",
407
+ "unicorn/new-for-builtins": "unicorn/new-for-builtins",
408
+ "unicorn/no-accessor-recursion": "unicorn/no-accessor-recursion",
409
+ "unicorn/no-array-method-this-argument": "unicorn/no-array-method-this-argument",
410
+ "unicorn/no-array-reduce": "unicorn/no-array-reduce",
411
+ "unicorn/no-array-reverse": "unicorn/no-array-reverse",
412
+ "unicorn/no-array-sort": "unicorn/no-array-sort",
413
+ "unicorn/no-await-expression-member": "unicorn/no-await-expression-member",
414
+ "unicorn/no-document-cookie": "unicorn/no-document-cookie",
415
+ "unicorn/no-hex-escape": "unicorn/no-hex-escape",
416
+ "unicorn/no-immediate-mutation": "unicorn/no-immediate-mutation",
417
+ "unicorn/no-instanceof-builtins": "unicorn/no-instanceof-builtins",
418
+ "unicorn/no-invalid-remove-event-listener": "unicorn/no-invalid-remove-event-listener",
419
+ "unicorn/no-nested-ternary": "unicorn/no-nested-ternary",
420
+ "unicorn/no-new-array": "unicorn/no-new-array",
421
+ "unicorn/no-new-buffer": "unicorn/no-new-buffer",
422
+ "unicorn/no-process-exit": "unicorn/no-process-exit",
423
+ "unicorn/no-static-only-class": "unicorn/no-static-only-class",
424
+ "unicorn/no-this-assignment": "unicorn/no-this-assignment",
425
+ "unicorn/no-typeof-undefined": "unicorn/no-typeof-undefined",
426
+ "unicorn/no-unnecessary-await": "unicorn/no-unnecessary-await",
427
+ "unicorn/no-unnecessary-polyfills": "unicorn-js/no-unnecessary-polyfills",
428
+ "unicorn/no-unnecessary-slice-end": "unicorn/no-unnecessary-slice-end",
429
+ "unicorn/no-unreadable-iife": "unicorn/no-unreadable-iife",
430
+ "unicorn/no-useless-collection-argument": "unicorn/no-useless-collection-argument",
431
+ "unicorn/no-useless-fallback-in-spread": "unicorn/no-useless-fallback-in-spread",
432
+ "unicorn/no-useless-iterator-to-array": "unicorn/no-useless-iterator-to-array",
433
+ "unicorn/no-useless-length-check": "unicorn/no-useless-length-check",
434
+ "unicorn/no-useless-promise-resolve-reject": "unicorn/no-useless-promise-resolve-reject",
435
+ "unicorn/no-useless-spread": "unicorn/no-useless-spread",
436
+ "unicorn/number-literal-case": "unicorn/number-literal-case",
437
+ "unicorn/numeric-separators-style": "unicorn/numeric-separators-style",
438
+ "unicorn/prefer-add-event-listener": "unicorn-js/prefer-add-event-listener",
439
+ "unicorn/prefer-array-find": "unicorn/prefer-array-find",
440
+ "unicorn/prefer-array-flat": "unicorn/prefer-array-flat",
441
+ "unicorn/prefer-array-flat-map": "unicorn/prefer-array-flat-map",
442
+ "unicorn/prefer-array-index-of": "unicorn/prefer-array-index-of",
443
+ "unicorn/prefer-array-some": "unicorn/prefer-array-some",
444
+ "unicorn/prefer-at": "unicorn/prefer-at",
445
+ "unicorn/prefer-blob-reading-methods": "unicorn/prefer-blob-reading-methods",
446
+ "unicorn/prefer-class-fields": "unicorn/prefer-class-fields",
447
+ "unicorn/prefer-code-point": "unicorn/prefer-code-point",
448
+ "unicorn/prefer-date-now": "unicorn/prefer-date-now",
449
+ "unicorn/prefer-default-parameters": "unicorn/prefer-default-parameters",
450
+ "unicorn/prefer-dom-node-append": "unicorn/prefer-dom-node-append",
451
+ "unicorn/prefer-dom-node-dataset": "unicorn/prefer-dom-node-dataset",
452
+ "unicorn/prefer-dom-node-remove": "unicorn/prefer-dom-node-remove",
453
+ "unicorn/prefer-dom-node-text-content": "unicorn/prefer-dom-node-text-content",
454
+ "unicorn/prefer-event-target": "unicorn/prefer-event-target",
455
+ "unicorn/prefer-export-from": "unicorn/prefer-export-from",
456
+ "unicorn/prefer-global-this": "unicorn/prefer-global-this",
457
+ "unicorn/prefer-includes": "unicorn/prefer-includes",
458
+ "unicorn/prefer-json-parse-buffer": "unicorn-js/prefer-json-parse-buffer",
459
+ "unicorn/prefer-logical-operator-over-ternary": "unicorn/prefer-logical-operator-over-ternary",
460
+ "unicorn/prefer-math-min-max": "unicorn/prefer-math-min-max",
461
+ "unicorn/prefer-math-trunc": "unicorn/prefer-math-trunc",
462
+ "unicorn/prefer-modern-dom-apis": "unicorn/prefer-modern-dom-apis",
463
+ "unicorn/prefer-modern-math-apis": "unicorn/prefer-modern-math-apis",
464
+ "unicorn/prefer-module": "unicorn/prefer-module",
465
+ "unicorn/prefer-native-coercion-functions": "unicorn/prefer-native-coercion-functions",
466
+ "unicorn/prefer-negative-index": "unicorn/prefer-negative-index",
467
+ "unicorn/prefer-node-protocol": "unicorn/prefer-node-protocol",
468
+ "unicorn/prefer-object-from-entries": "unicorn/prefer-object-from-entries",
469
+ "unicorn/prefer-optional-catch-binding": "unicorn/prefer-optional-catch-binding",
470
+ "unicorn/prefer-prototype-methods": "unicorn/prefer-prototype-methods",
471
+ "unicorn/prefer-regexp-test": "unicorn/prefer-regexp-test",
472
+ "unicorn/prefer-response-static-json": "unicorn/prefer-response-static-json",
473
+ "unicorn/prefer-set-size": "unicorn/prefer-set-size",
474
+ "unicorn/prefer-single-call": "unicorn/prefer-single-call",
475
+ "unicorn/prefer-spread": "unicorn/prefer-spread",
476
+ "unicorn/prefer-string-slice": "unicorn/prefer-string-slice",
477
+ "unicorn/prefer-string-starts-ends-with": "unicorn/prefer-string-starts-ends-with",
478
+ "unicorn/prefer-string-trim-start-end": "unicorn/prefer-string-trim-start-end",
479
+ "unicorn/prefer-switch": "unicorn-js/prefer-switch",
480
+ "unicorn/prefer-top-level-await": "unicorn/prefer-top-level-await",
481
+ "unicorn/prefer-type-error": "unicorn/prefer-type-error",
482
+ "unicorn/relative-url-style": "unicorn/relative-url-style",
483
+ "unicorn/require-array-join-separator": "unicorn/require-array-join-separator",
484
+ "unicorn/require-number-to-fixed-digits-argument": "unicorn/require-number-to-fixed-digits-argument",
485
+ "unicorn/require-post-message-target-origin": "unicorn/require-post-message-target-origin",
486
+ "unicorn/switch-case-break-position": "unicorn/switch-case-break-position",
487
+ "unicorn/text-encoding-identifier-case": "unicorn/text-encoding-identifier-case",
488
+ "unicorn/throw-new-error": "unicorn/throw-new-error",
489
+ "use-isnan": "use-isnan",
490
+ "valid-typeof": "valid-typeof",
491
+ "yoda": "yoda"
492
+ }
package/lib/index.js ADDED
@@ -0,0 +1,18 @@
1
+ import { fileURLToPath } from "node:url";
2
+ import runJscodeshift from "./runJscodeshift.cjs";
3
+
4
+ /**
5
+ * Runs a transform of lib/transforms on `paths`, like `pob-migrate`.
6
+ *
7
+ * @param {string} transformName
8
+ * @param {string[]} paths
9
+ * @param {Record<string, unknown>} [options] jscodeshift runner options
10
+ */
11
+ export const runTransform = (transformName, paths, options = {}) =>
12
+ runJscodeshift(
13
+ fileURLToPath(
14
+ new URL(`./transforms/${transformName}.cjs`, import.meta.url),
15
+ ),
16
+ paths,
17
+ options,
18
+ );
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+
3
+ const { run: jscodeshift } = require("jscodeshift/src/Runner");
4
+
5
+ module.exports = (...args) => {
6
+ return jscodeshift(...args);
7
+ };
@@ -0,0 +1,146 @@
1
+ /* oxlint-disable unicorn/prefer-array-some */
2
+ /* oxlint-disable unicorn/no-array-method-this-argument */
3
+
4
+ "use strict";
5
+
6
+ const { prependImport } = require("../utils/prependImport.cjs");
7
+
8
+ const jestToNodeGlobalMethods = {
9
+ test: "test",
10
+ describe: "describe",
11
+ it: "it",
12
+ beforeAll: "before",
13
+ afterAll: "after",
14
+ beforeEach: "beforeEach",
15
+ afterEach: "afterEach",
16
+ };
17
+ const propertiesMap = {
18
+ toBe: { default: "equal", not: "notEqual" },
19
+ toStrictEqual: { default: "equal", not: "notEqual" },
20
+ toEqual: { default: "equal", not: "notEqual" },
21
+ };
22
+
23
+ // const mocks = {
24
+ // toHaveBeenCalledTimes: {
25
+ // default: 'mock.calls.length',
26
+ // },
27
+ // };
28
+
29
+ module.exports = (file, api, options) => {
30
+ if (
31
+ !(
32
+ file.path.endsWith(".test.js") ||
33
+ file.path.endsWith(".test.ts") ||
34
+ file.path.endsWith(".test.tsx")
35
+ )
36
+ ) {
37
+ throw new Error(`Invalid file: "${file.path}"`);
38
+ }
39
+
40
+ const j = api.jscodeshift.withParser("tsx");
41
+ const root = j(file.source);
42
+
43
+ function updateTopLevelMethods() {
44
+ const topLevelMethodsUsed = new Set();
45
+
46
+ root
47
+ .find(j.CallExpression, {
48
+ callee: {
49
+ type: j.Identifier.name,
50
+ name: (name) => Object.keys(jestToNodeGlobalMethods).includes(name),
51
+ },
52
+ })
53
+ .replaceWith((p) => {
54
+ const { value } = p;
55
+ const replaceValue = jestToNodeGlobalMethods[value.callee.name];
56
+ topLevelMethodsUsed.add(replaceValue);
57
+
58
+ return j.callExpression(j.identifier(replaceValue), value.arguments);
59
+ });
60
+
61
+ if (topLevelMethodsUsed.size > 0) {
62
+ prependImport(
63
+ j,
64
+ root,
65
+ j.importDeclaration(
66
+ [...topLevelMethodsUsed].map((method) =>
67
+ j.importSpecifier(j.identifier(method)),
68
+ ),
69
+ j.stringLiteral("node:test"),
70
+ ),
71
+ );
72
+ }
73
+ }
74
+
75
+ function updateAssertions() {
76
+ let hasAssertions = false;
77
+
78
+ root
79
+ .find(j.MemberExpression, {
80
+ object: {
81
+ callee: { name: "expect" },
82
+ },
83
+ })
84
+ .forEach((memberExpressionPath) => {
85
+ const { value: memberExpression, parentPath } = memberExpressionPath;
86
+ let memberExpressionPathToReplace = memberExpressionPath;
87
+ let propertyName = memberExpression.property.name;
88
+ let not = false;
89
+ if (propertyName === "not") {
90
+ not = true;
91
+ memberExpressionPathToReplace = parentPath;
92
+ propertyName = parentPath.value.property.name;
93
+ }
94
+
95
+ if (!propertiesMap[propertyName]) {
96
+ console.warn(
97
+ `Unsupported expect property: ${propertyName} in ${file.path}`,
98
+ );
99
+ return;
100
+ }
101
+
102
+ const assertPropertyName =
103
+ propertiesMap[propertyName][not ? "not" : "default"];
104
+ const callExpressionPath = memberExpressionPathToReplace.parentPath;
105
+
106
+ const value = memberExpression.object.arguments[0];
107
+ const expected = callExpressionPath.value.arguments[0];
108
+
109
+ hasAssertions = true;
110
+ callExpressionPath.value.callee = j.memberExpression(
111
+ j.identifier("assert"),
112
+ j.identifier(assertPropertyName),
113
+ );
114
+ callExpressionPath.value.arguments = [value, expected];
115
+ });
116
+
117
+ if (hasAssertions) {
118
+ root
119
+ .find(j.ImportDeclaration, { source: { value: "node:assert" } })
120
+ .remove();
121
+ if (
122
+ // oxlint-disable-next-line no-constant-condition
123
+ (!root.find(j.ImportDeclaration),
124
+ { source: { value: "node:assert/strict" } })
125
+ ) {
126
+ prependImport(
127
+ j,
128
+ root,
129
+ j.importDeclaration(
130
+ [j.importDefaultSpecifier(j.identifier("assert"))],
131
+ j.stringLiteral("node:assert/strict"),
132
+ ),
133
+ );
134
+ }
135
+ }
136
+ }
137
+
138
+ updateTopLevelMethods();
139
+ updateAssertions();
140
+
141
+ root
142
+ .find(j.ImportDeclaration, { source: { value: "@jest/globals" } })
143
+ .remove();
144
+
145
+ return root.toSource(options);
146
+ };
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+
3
+ const { parse } = require("@babel/parser");
4
+ const { rewriteDirectiveComments } = require("../utils/oxlintDirectives.cjs");
5
+
6
+ /**
7
+ * Rewrites the eslint directive comments of rules moved to oxlint, for the
8
+ * major of @pob/eslint-config that no longer runs them in eslint.
9
+ *
10
+ * @type {import("jscodeshift").Transform}
11
+ */
12
+ module.exports = (file) => {
13
+ const isTypescript = /\.[cm]?ts$/.test(file.path);
14
+ const { comments = [] } = parse(file.source, {
15
+ // .cjs files may use sloppy mode syntax
16
+ sourceType: /\.c[jt]s$/.test(file.path) ? "script" : "unambiguous",
17
+ allowReturnOutsideFunction: true,
18
+ // only comments are needed: syntax errors do not matter
19
+ errorRecovery: true,
20
+ plugins: isTypescript
21
+ ? ["typescript", "decorators-legacy"]
22
+ : ["typescript", "jsx", "decorators-legacy"],
23
+ });
24
+ const result = rewriteDirectiveComments(
25
+ file.source,
26
+ /** @type {{ type: string; value: string; start: number; end: number }[]} */ (
27
+ comments
28
+ ),
29
+ );
30
+ return result === file.source ? undefined : result;
31
+ };
@@ -0,0 +1,194 @@
1
+ "use strict";
2
+
3
+ const {
4
+ eslintToOxlintRuleNames,
5
+ isRemovedEslintPluginRule,
6
+ } = require("../utils/oxlintDirectives.cjs");
7
+
8
+ // configs of @pob/eslint-config and @pob/eslint-config-typescript-react left
9
+ // without rules once oxlint runs every rule it supports
10
+ const removedConfigNames = new Set([
11
+ "allowImplicitReturnType",
12
+ "allowUnsafe",
13
+ "allowUnsafeAsWarn",
14
+ "app",
15
+ "oxlint",
16
+ ]);
17
+
18
+ const renamedConfigNames = new Map([
19
+ ["nodeCommonjs", "node"],
20
+ ["nodeModule", "node"],
21
+ ]);
22
+
23
+ /**
24
+ * Updates eslint.config.js for the major of @pob/eslint-config running every
25
+ * rule supported by oxlint in oxlint only: removes the configs left empty,
26
+ * renames the deprecated ones, and removes the rule entries of rules moved to
27
+ * oxlint (reported: their options go in .oxlintrc.json).
28
+ *
29
+ * @type {import("jscodeshift").Transform}
30
+ */
31
+ module.exports = (file, api) => {
32
+ const j = api.jscodeshift.withParser("tsx");
33
+ const root = j(file.source);
34
+ let hasModifications = false;
35
+
36
+ /** @param {any} node */
37
+ const getLine = (node) => (node.loc ? `:${node.loc.start.line}` : "");
38
+
39
+ /**
40
+ * @param {any} key
41
+ * @param {boolean} [computed]
42
+ */
43
+ const getKeyName = (key, computed) => {
44
+ if (key.type === "Identifier" && !computed) return key.name;
45
+ if (
46
+ (key.type === "StringLiteral" || key.type === "Literal") &&
47
+ typeof key.value === "string"
48
+ ) {
49
+ return key.value;
50
+ }
51
+ return undefined;
52
+ };
53
+
54
+ /** @param {any} node */
55
+ const getPropertyName = (node) => getKeyName(node.property, node.computed);
56
+
57
+ /** `<x>.configs.<name>` or `configs.<name>` */
58
+ const isConfigsMember = (/** @type {any} */ node) =>
59
+ node.type === "MemberExpression" &&
60
+ ((node.object.type === "Identifier" && node.object.name === "configs") ||
61
+ (node.object.type === "MemberExpression" &&
62
+ getPropertyName(node.object) === "configs"));
63
+
64
+ /**
65
+ * Removes the closest spread element of an array containing `path`, like
66
+ * `...apply({ configs: [] })`.
67
+ *
68
+ * @param {any} path
69
+ */
70
+ const removeEnclosingSpread = (path) => {
71
+ let current = path.parent;
72
+ while (current) {
73
+ if (
74
+ current.value.type === "SpreadElement" &&
75
+ current.parent.value.type === "ArrayExpression"
76
+ ) {
77
+ current.prune();
78
+ return true;
79
+ }
80
+ current = current.parent;
81
+ }
82
+ return false;
83
+ };
84
+
85
+ /** @param {any} node */
86
+ const isConfigsProperty = (node) =>
87
+ (node.type === "Property" || node.type === "ObjectProperty") &&
88
+ getKeyName(node.key, node.computed) === "configs";
89
+
90
+ /**
91
+ * Removes an element of an array, and the apply() call whose `configs` it
92
+ * leaves empty.
93
+ *
94
+ * @param {any} elementPath
95
+ */
96
+ const pruneArrayElement = (elementPath) => {
97
+ const array = elementPath.parent;
98
+ elementPath.prune();
99
+ if (
100
+ array.value.elements.length === 0 &&
101
+ isConfigsProperty(array.parent.value)
102
+ ) {
103
+ removeEnclosingSpread(array);
104
+ }
105
+ };
106
+
107
+ root
108
+ .find(j.MemberExpression)
109
+ .filter((path) => isConfigsMember(path.value))
110
+ .forEach((path) => {
111
+ const name = getPropertyName(path.value);
112
+ if (!name) return;
113
+
114
+ const renamed = renamedConfigNames.get(name);
115
+ if (renamed) {
116
+ path.value.property = j.identifier(renamed);
117
+ path.value.computed = false;
118
+ hasModifications = true;
119
+ return;
120
+ }
121
+
122
+ if (!removedConfigNames.has(name)) return;
123
+
124
+ const parent = path.parent.value;
125
+ if (
126
+ parent.type === "SpreadElement" &&
127
+ path.parent.parent.value.type === "ArrayExpression"
128
+ ) {
129
+ pruneArrayElement(path.parent);
130
+ hasModifications = true;
131
+ return;
132
+ }
133
+
134
+ if (
135
+ isConfigsProperty(parent) &&
136
+ parent.value === path.value &&
137
+ removeEnclosingSpread(path)
138
+ ) {
139
+ hasModifications = true;
140
+ return;
141
+ }
142
+
143
+ api.report(
144
+ `${file.path}${getLine(path.value)}: "configs.${name}" was removed, update this usage by hand.`,
145
+ );
146
+ });
147
+
148
+ // rule entries of rules moved to oxlint
149
+ root
150
+ .find(j.Node)
151
+ .filter(
152
+ (path) =>
153
+ (path.value.type === "Property" ||
154
+ path.value.type === "ObjectProperty") &&
155
+ getKeyName(path.value.key, path.value.computed) === "rules" &&
156
+ path.value.value.type === "ObjectExpression",
157
+ )
158
+ .forEach((path) => {
159
+ const rules = path.value.value;
160
+ rules.properties = rules.properties.filter((/** @type {any} */ rule) => {
161
+ const ruleName = getKeyName(rule.key, rule.computed);
162
+ if (typeof ruleName !== "string") return true;
163
+ const oxlintRuleName = eslintToOxlintRuleNames[ruleName];
164
+ if (!oxlintRuleName && !isRemovedEslintPluginRule(ruleName)) {
165
+ return true;
166
+ }
167
+ hasModifications = true;
168
+ api.report(
169
+ oxlintRuleName
170
+ ? `${file.path}${getLine(rule)}: "${ruleName}" is checked by oxlint: configure "${oxlintRuleName}" in .oxlintrc.json instead.`
171
+ : `${file.path}${getLine(rule)}: "${ruleName}" was removed.`,
172
+ );
173
+ return false;
174
+ });
175
+ if (rules.properties.length > 0) return;
176
+
177
+ // remove the config when the rules were all it configured
178
+ const config = path.parent;
179
+ config.value.properties = config.value.properties.filter(
180
+ (/** @type {any} */ property) => property !== path.value,
181
+ );
182
+ const isEmptyConfig = config.value.properties.every(
183
+ (/** @type {any} */ property) =>
184
+ ["files", "ignores", "name"].includes(
185
+ getKeyName(property.key, property.computed) ?? "",
186
+ ),
187
+ );
188
+ if (isEmptyConfig && config.parent.value.type === "ArrayExpression") {
189
+ pruneArrayElement(config);
190
+ }
191
+ });
192
+
193
+ return hasModifications ? root.toSource({ trailingComma: true }) : undefined;
194
+ };
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+
3
+ module.exports = (file, api, options) => {
4
+ const j = api.jscodeshift.withParser("tsx");
5
+ const root = j(file.source);
6
+ let hasModifications = false;
7
+
8
+ function processFunctionComponent(path) {
9
+ const node = path.value;
10
+ const { returnType } = node;
11
+ if (returnType == null) return;
12
+ if (returnType.type !== "TSTypeAnnotation") return;
13
+ const { typeAnnotation } = returnType;
14
+ if (typeAnnotation == null) return;
15
+
16
+ if (
17
+ typeAnnotation.type === "TSTypeReference" &&
18
+ typeAnnotation.typeName &&
19
+ typeAnnotation.typeName.type === "Identifier"
20
+ ) {
21
+ if (typeAnnotation.typeName.name === "ReactElement") {
22
+ typeAnnotation.typeName.name = "ReactNode";
23
+ hasModifications = true;
24
+ }
25
+ } else if (
26
+ typeAnnotation.type === "TSUnionType" &&
27
+ typeAnnotation.types.every((unionTypeItem) => {
28
+ if (unionTypeItem.type === "TSTypeReference") {
29
+ return (
30
+ unionTypeItem.typeName &&
31
+ unionTypeItem.typeName.type === "Identifier" &&
32
+ unionTypeItem.typeName.name === "ReactElement"
33
+ );
34
+ }
35
+
36
+ if (unionTypeItem.type === "TSNullKeyword") {
37
+ return true;
38
+ }
39
+
40
+ return false;
41
+ })
42
+ ) {
43
+ returnType.typeAnnotation = j.tsTypeReference(j.identifier("ReactNode"));
44
+ hasModifications = true;
45
+ }
46
+ }
47
+
48
+ root.find(j.FunctionDeclaration).forEach(processFunctionComponent);
49
+
50
+ if (hasModifications) {
51
+ root.find(j.ImportDeclaration).forEach((importDeclaration) => {
52
+ const { node } = importDeclaration;
53
+ if (node.source.value !== "react") return;
54
+ if (node.importKind !== "type") return;
55
+
56
+ node.specifiers = node.specifiers.filter((importSpecifier) => {
57
+ if (importSpecifier.local.name !== "ReactElement") return true;
58
+ if (importSpecifier.importKind !== "value") return true;
59
+ return (
60
+ j(importDeclaration)
61
+ .closestScope()
62
+ // oxlint-disable-next-line unicorn/no-array-method-this-argument -- not an array
63
+ .find(j.Identifier, { name: "ReactElement" })
64
+ .filter((p) => {
65
+ if (p.value.start === importSpecifier.local.start) return false;
66
+ if (p.parentPath.value.type === "Property" && p.name === "key") {
67
+ return false;
68
+ }
69
+ if (p.name === "property") return false;
70
+ return true;
71
+ })
72
+ .size() > 0
73
+ );
74
+ });
75
+ if (
76
+ !node.specifiers.some(
77
+ (specifier) =>
78
+ specifier.type === "ImportSpecifier" &&
79
+ specifier.imported.name === "ReactNode",
80
+ )
81
+ ) {
82
+ node.specifiers.push(j.importSpecifier(j.identifier("ReactNode")));
83
+ }
84
+ });
85
+ }
86
+
87
+ return hasModifications ? root.toSource(options) : null;
88
+ };
@@ -0,0 +1,220 @@
1
+ "use strict";
2
+
3
+ // eslint rule name → oxlint rule name, for every rule of the oxlint configs of
4
+ // @pob/eslint-config and @pob/eslint-config-typescript-react, frozen at the
5
+ // last version running them in eslint too.
6
+ const eslintToOxlintRuleNames = require("../data/eslint-to-oxlint-rule-names.json");
7
+
8
+ // plugins no longer loaded by @pob/eslint-config: eslint reports a directive
9
+ // naming one of their rules as "Definition for rule ... was not found"
10
+ const removedEslintPluginPrefixes = [
11
+ "@pob/",
12
+ "@typescript-eslint/",
13
+ "jsx-a11y/",
14
+ "react-hooks/",
15
+ "react/",
16
+ "regexp/",
17
+ "unicorn/",
18
+ ];
19
+
20
+ // rules of the eslint-plugin-react JS plugin of oxlint (react-js), removed
21
+ // with eslint-plugin-react: renamed when moved to @pob/eslint-plugin
22
+ const renamedOxlintRuleNames = new Map([
23
+ ["react-js/jsx-no-leaked-render", "@pob/jsx-no-leaked-render"],
24
+ ["react-js/jsx-sort-props", "@pob/jsx-sort-props"],
25
+ ]);
26
+ const removedOxlintPluginPrefixes = ["react-js/"];
27
+
28
+ /** @param {string} ruleName */
29
+ const isRemovedEslintPluginRule = (ruleName) =>
30
+ removedEslintPluginPrefixes.some((prefix) => ruleName.startsWith(prefix));
31
+
32
+ const directiveRegExp =
33
+ /^\s*(eslint|oxlint)-(disable-next-line|disable-line|disable|enable)(?=\s|$)/;
34
+
35
+ // same as eslint: the description starts at the first " -- "
36
+ const descriptionRegExp = /\s-{2,}\s/;
37
+
38
+ /**
39
+ * @param {string} commentValue the comment without its delimiters
40
+ */
41
+ const parseDirective = (commentValue) => {
42
+ const [content, ...descriptionParts] = commentValue.split(descriptionRegExp);
43
+ const match = directiveRegExp.exec(content);
44
+ if (!match) return undefined;
45
+ const [prefix, tool, kind] = match;
46
+ const ruleList = content.slice(prefix.length);
47
+ return {
48
+ tool,
49
+ kind,
50
+ ruleNames: ruleList
51
+ .split(",")
52
+ .map((ruleName) => ruleName.trim())
53
+ .filter(Boolean),
54
+ description:
55
+ descriptionParts.length > 0
56
+ ? commentValue.slice(content.length).trim()
57
+ : undefined,
58
+ };
59
+ };
60
+
61
+ /**
62
+ * @param {string[]} ruleNames
63
+ */
64
+ const splitRuleNames = (ruleNames) => {
65
+ /** @type {Set<string>} */
66
+ const eslintRuleNames = new Set();
67
+ /** @type {Set<string>} */
68
+ const oxlintRuleNames = new Set();
69
+ for (const ruleName of ruleNames) {
70
+ const oxlintRuleName = eslintToOxlintRuleNames[ruleName];
71
+ if (oxlintRuleName) {
72
+ oxlintRuleNames.add(oxlintRuleName);
73
+ } else if (!isRemovedEslintPluginRule(ruleName)) {
74
+ eslintRuleNames.add(ruleName);
75
+ }
76
+ }
77
+ return {
78
+ eslintRuleNames: [...eslintRuleNames],
79
+ oxlintRuleNames: [...oxlintRuleNames],
80
+ };
81
+ };
82
+
83
+ /**
84
+ * @param {"eslint" | "oxlint"} tool
85
+ * @param {string} kind
86
+ * @param {string[]} ruleNames
87
+ * @param {string | undefined} description
88
+ */
89
+ const formatDirective = (tool, kind, ruleNames, description) =>
90
+ [`${tool}-${kind}`, ruleNames.join(", "), description]
91
+ .filter(Boolean)
92
+ .join(" ");
93
+
94
+ /**
95
+ * Renames or removes the rules of the react-js JS plugin in an oxlint
96
+ * directive comment.
97
+ *
98
+ * @param {{ type: "CommentLine" | "CommentBlock" }} comment
99
+ * @param {{ kind: string; ruleNames: string[]; description: string | undefined }} directive
100
+ * @returns {string | null | undefined}
101
+ */
102
+ const rewriteOxlintDirective = (comment, { kind, ruleNames, description }) => {
103
+ const newRuleNames = [
104
+ ...new Set(
105
+ ruleNames
106
+ .map((ruleName) => renamedOxlintRuleNames.get(ruleName) ?? ruleName)
107
+ .filter(
108
+ (ruleName) =>
109
+ !removedOxlintPluginPrefixes.some((prefix) =>
110
+ ruleName.startsWith(prefix),
111
+ ),
112
+ ),
113
+ ),
114
+ ];
115
+ if (
116
+ newRuleNames.length === ruleNames.length &&
117
+ newRuleNames.every((ruleName, index) => ruleName === ruleNames[index])
118
+ ) {
119
+ return undefined;
120
+ }
121
+ if (newRuleNames.length === 0) return null;
122
+ const text = formatDirective("oxlint", kind, newRuleNames, description);
123
+ return comment.type === "CommentBlock" ? `/* ${text} */` : `// ${text}`;
124
+ };
125
+
126
+ /**
127
+ * Rewrites an eslint directive comment for @pob/eslint-config running the
128
+ * moved rules in oxlint only: their names move to an `oxlint-` directive of
129
+ * the same kind, rules of removed eslint plugins are dropped.
130
+ *
131
+ * @param {{ type: "CommentLine" | "CommentBlock"; value: string }} comment
132
+ * @returns {string | null | undefined} the new comment text, `null` to remove
133
+ * the comment, `undefined` when unchanged
134
+ */
135
+ const rewriteDirectiveComment = (comment) => {
136
+ const directive = parseDirective(comment.value);
137
+ if (!directive) return undefined;
138
+ const { tool, kind, ruleNames, description } = directive;
139
+ if (tool === "oxlint") return rewriteOxlintDirective(comment, directive);
140
+
141
+ // a blanket directive disables every rule, now in both tools
142
+ if (ruleNames.length === 0) {
143
+ const eslintDirective = `/* ${comment.value.trim()} */`;
144
+ const oxlintDirective = formatDirective("oxlint", kind, [], description);
145
+ return comment.type === "CommentLine"
146
+ ? `${eslintDirective} // ${oxlintDirective}`
147
+ : `${eslintDirective} /* ${oxlintDirective} */`;
148
+ }
149
+
150
+ const { eslintRuleNames, oxlintRuleNames } = splitRuleNames(ruleNames);
151
+ if (oxlintRuleNames.length === 0) {
152
+ if (eslintRuleNames.length === ruleNames.length) return undefined;
153
+ if (eslintRuleNames.length === 0) return null;
154
+ }
155
+
156
+ const directives = [
157
+ eslintRuleNames.length > 0 &&
158
+ formatDirective("eslint", kind, eslintRuleNames, description),
159
+ oxlintRuleNames.length > 0 &&
160
+ formatDirective("oxlint", kind, oxlintRuleNames, description),
161
+ ].filter(Boolean);
162
+
163
+ if (comment.type === "CommentBlock") {
164
+ return directives.map((text) => `/* ${text} */`).join(" ");
165
+ }
166
+ // a line comment ends the line: only the last directive can be one
167
+ return directives
168
+ .map((text, index) =>
169
+ index === directives.length - 1 ? `// ${text}` : `/* ${text} */`,
170
+ )
171
+ .join(" ");
172
+ };
173
+
174
+ /**
175
+ * @param {string} source
176
+ * @param {{ type: string; value: string; start: number; end: number }[]} comments
177
+ * @returns {string}
178
+ */
179
+ const rewriteDirectiveComments = (source, comments) => {
180
+ let result = source;
181
+ // from the end, so that the offsets of the previous comments stay valid
182
+ for (const comment of comments.toSorted((a, b) => b.start - a.start)) {
183
+ if (comment.type !== "CommentLine" && comment.type !== "CommentBlock") {
184
+ continue;
185
+ }
186
+ const replacement = rewriteDirectiveComment(
187
+ /** @type {{ type: "CommentLine" | "CommentBlock"; value: string }} */ (
188
+ comment
189
+ ),
190
+ );
191
+ if (replacement === undefined) continue;
192
+
193
+ let start = comment.start;
194
+ let end = comment.end;
195
+ if (replacement === null) {
196
+ // remove the whole line when the comment is alone on it
197
+ const lineStart = result.lastIndexOf("\n", start - 1) + 1;
198
+ const lineEndIndex = result.indexOf("\n", end);
199
+ const lineEnd = lineEndIndex === -1 ? result.length : lineEndIndex;
200
+ const before = result.slice(lineStart, start);
201
+ const after = result.slice(end, lineEnd);
202
+ if (before.trim() === "" && after.trim() === "") {
203
+ start = lineStart;
204
+ end = lineEndIndex === -1 ? lineEnd : lineEnd + 1;
205
+ } else if (before.trim() !== "") {
206
+ // trailing comment: also remove the spaces before it
207
+ start = lineStart + before.trimEnd().length;
208
+ }
209
+ }
210
+ result = result.slice(0, start) + (replacement ?? "") + result.slice(end);
211
+ }
212
+ return result;
213
+ };
214
+
215
+ module.exports = {
216
+ eslintToOxlintRuleNames,
217
+ isRemovedEslintPluginRule,
218
+ rewriteDirectiveComment,
219
+ rewriteDirectiveComments,
220
+ };
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+
3
+ exports.prependImport = (j, source, importDeclaration) => {
4
+ const existingImports = source.find(j.ImportDeclaration);
5
+ if (existingImports.length > 0) {
6
+ const firstImport = existingImports.at(0);
7
+ const firstImportNode = firstImport.nodes()[0];
8
+ if (firstImportNode?.comments) {
9
+ importDeclaration.comments = firstImportNode.comments;
10
+ firstImportNode.comments = null;
11
+ }
12
+
13
+ firstImport.insertBefore(importDeclaration);
14
+ return;
15
+ }
16
+
17
+ const firstNode = source.find(j.Program).get("body", 0).node;
18
+ const { comments } = firstNode;
19
+ if (comments?.length) {
20
+ const comment = comments[0];
21
+
22
+ // Only move comments that look like file-level comments. Ignore
23
+ // line-level and JSDoc-style comments because these probably belong
24
+ // to the first node, rather than the file.
25
+ if (
26
+ (comment.type === "Block" || comment.type === "CommentBlock") &&
27
+ !comment.value.startsWith("*")
28
+ ) {
29
+ importDeclaration.comments = comments;
30
+ firstNode.comments = null;
31
+ }
32
+ }
33
+
34
+ source.get("program", "body").unshift(importDeclaration);
35
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pob/codemods",
3
- "version": "0.17.1",
3
+ "version": "0.18.1",
4
4
  "description": "codemods for pob",
5
5
  "keywords": [],
6
6
  "homepage": "https://github.com/christophehurpeau/pob",
@@ -18,10 +18,9 @@
18
18
  "pob-migrate": "./bin/migrate.mjs"
19
19
  },
20
20
  "files": [
21
- "src",
22
- "build",
23
21
  "bin",
24
- "lib/index.js"
22
+ "lib",
23
+ "!lib/**/*.test.js"
25
24
  ],
26
25
  "type": "module",
27
26
  "main": "./lib/index.js",
@@ -33,6 +32,7 @@
33
32
  "access": "public"
34
33
  },
35
34
  "dependencies": {
35
+ "@babel/parser": "7.29.9",
36
36
  "jscodeshift": "17.4.0"
37
37
  },
38
38
  "devDependencies": {