Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 5241fbb

Browse files
committedMar 16, 2025··
Make reselect-codemods a Yarn workspace
1 parent 1b36b2f commit 5241fbb

29 files changed

+8327
-9926
lines changed
 

‎.github/workflows/test-reselect-codemods.yml

-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ jobs:
2525
uses: actions/setup-node@v4
2626
with:
2727
node-version: ${{ matrix.node-version }}
28-
cache-dependency-path: ./codemods
2928
cache: 'yarn'
3029

3130
- name: Check folder contents

‎codemods/.gitignore

+14-8
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
# Dependencies
2-
node_modules
2+
node_modules/
33

44
# Production
5-
build
5+
build/
66

77
# Generated files
8-
.docusaurus
8+
.docusaurus/
99
.cache-loader
1010

1111
# Misc
@@ -18,7 +18,7 @@ build
1818
npm-debug.log*
1919
yarn-debug.log*
2020
yarn-error.log*
21-
.cache
21+
.cache/
2222
.yarnrc
2323
.yarn/*
2424
!.yarn/patches
@@ -31,9 +31,15 @@ yarn-error.log*
3131

3232
tsconfig.vitest-temp.json
3333
.eslintcache
34+
*.tsbuildinfo
3435

35-
.yalc
36+
.yalc/
3637
.yalc.lock
37-
.vscode
38-
dist
39-
temp
38+
.vscode/
39+
coverage/
40+
build/
41+
lib/
42+
dist/
43+
temp/
44+
.tmp/
45+
.temp/

‎codemods/bin/cli.mjs

-39
This file was deleted.

‎codemods/eslint.config.mts

+121-15
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,29 @@
11
import js from '@eslint/js'
2-
import prettierConfig from 'eslint-config-prettier'
2+
import vitestPlugin from '@vitest/eslint-plugin'
3+
import prettierConfig from 'eslint-config-prettier/flat'
4+
import type { ConfigArray } from 'typescript-eslint'
35
import { config, configs, parser } from 'typescript-eslint'
46

5-
const ESLintConfig = config(
6-
{ name: 'ignores', ignores: ['**/dist/', '**/__testfixtures__/'] },
7-
{ name: 'javascript', ...js.configs.recommended },
7+
const eslintConfig: ConfigArray = config(
8+
{
9+
name: 'global-ignores',
10+
ignores: [
11+
'**/dist/',
12+
'**/.yalc/',
13+
'**/build/',
14+
'**/lib/',
15+
'**/temp/',
16+
'**/.temp/',
17+
'**/.tmp/',
18+
'**/.yarn/',
19+
'**/coverage/',
20+
'**/__testfixtures__/',
21+
],
22+
},
23+
{ name: `${js.meta.name}/recommended`, ...js.configs.recommended },
824
...configs.recommended,
925
...configs.stylistic,
10-
{ name: 'prettier-config', ...prettierConfig },
26+
{ name: 'vitest/recommended', ...vitestPlugin.configs.recommended },
1127
{
1228
name: 'main',
1329
languageOptions: {
@@ -16,27 +32,58 @@ const ESLintConfig = config(
1632
projectService: {
1733
defaultProject: './tsconfig.json',
1834
},
35+
tsconfigRootDir: import.meta.dirname,
1936
ecmaVersion: 'latest',
2037
},
2138
},
2239
rules: {
23-
'no-undef': [0],
2440
'@typescript-eslint/consistent-type-imports': [
2541
2,
26-
{ fixStyle: 'separate-type-imports', disallowTypeAnnotations: false },
42+
{
43+
prefer: 'type-imports',
44+
fixStyle: 'separate-type-imports',
45+
disallowTypeAnnotations: true,
46+
},
47+
],
48+
'@typescript-eslint/consistent-type-exports': [
49+
2,
50+
{ fixMixedExportsWithInlineTypeSpecifier: false },
51+
],
52+
'@typescript-eslint/no-explicit-any': [
53+
2,
54+
{ fixToUnknown: false, ignoreRestArgs: false },
2755
],
28-
'@typescript-eslint/consistent-type-exports': [2],
29-
'@typescript-eslint/no-unused-vars': [0],
30-
'@typescript-eslint/no-explicit-any': [0],
3156
'@typescript-eslint/no-empty-object-type': [
3257
2,
33-
{ allowInterfaces: 'with-single-extends' },
58+
{ allowInterfaces: 'never', allowObjectTypes: 'never' },
59+
],
60+
'@typescript-eslint/no-restricted-types': [
61+
2,
62+
{
63+
types: {
64+
'{}': {
65+
message: `
66+
- If you want to represent an empty object, use \`type EmptyObject = Record<string, never>\`.
67+
- If you want to represent an object literal, use either \`type AnyObject = Record<string, any>\` or \`object\`.
68+
- If you want to represent any non-nullish value, use \`type AnyNonNullishValue = NonNullable<unknown>\`.`,
69+
suggest: [
70+
'AnyNonNullishValue',
71+
'EmptyObject',
72+
'AnyObject',
73+
'object',
74+
'Record<string, never>',
75+
'Record<string, any>',
76+
'NonNullable<unknown>',
77+
],
78+
},
79+
},
80+
},
3481
],
3582
'@typescript-eslint/no-namespace': [
3683
2,
37-
{ allowDeclarations: true, allowDefinitionFiles: true },
84+
{ allowDeclarations: false, allowDefinitionFiles: true },
3885
],
39-
'@typescript-eslint/ban-ts-comment': [0],
86+
'@typescript-eslint/consistent-type-definitions': [2, 'type'],
4087
'sort-imports': [
4188
2,
4289
{
@@ -47,17 +94,76 @@ const ESLintConfig = config(
4794
allowSeparatedGroups: true,
4895
},
4996
],
97+
'@typescript-eslint/unified-signatures': [2],
98+
'@typescript-eslint/no-unnecessary-type-parameters': [2],
99+
'@typescript-eslint/no-invalid-void-type': [2],
100+
'@typescript-eslint/no-confusing-void-expression': [2],
101+
'@typescript-eslint/no-duplicate-type-constituents': [2],
102+
'@typescript-eslint/require-await': [2],
103+
'@typescript-eslint/no-redundant-type-constituents': [2],
104+
'@typescript-eslint/no-unnecessary-type-arguments': [2],
105+
'@typescript-eslint/no-unnecessary-type-assertion': [2],
106+
'@typescript-eslint/prefer-nullish-coalescing': [2],
107+
'@typescript-eslint/no-inferrable-types': [2],
108+
'object-shorthand': [2],
109+
110+
'no-undef': [0],
111+
'@typescript-eslint/no-unused-vars': [
112+
0,
113+
{
114+
vars: 'all',
115+
args: 'after-used',
116+
caughtErrors: 'all',
117+
ignoreRestSiblings: false,
118+
reportUsedIgnorePattern: false,
119+
},
120+
],
121+
'@typescript-eslint/ban-ts-comment': [
122+
0,
123+
[
124+
{
125+
'ts-expect-error': 'allow-with-description',
126+
'ts-ignore': true,
127+
'ts-nocheck': true,
128+
'ts-check': false,
129+
minimumDescriptionLength: 3,
130+
},
131+
],
132+
],
133+
'vitest/valid-title': [0],
134+
'vitest/no-alias-methods': [2],
135+
'vitest/no-disabled-tests': [2],
136+
'vitest/no-focused-tests': [2],
137+
'vitest/no-test-prefixes': [2],
138+
'vitest/no-test-return-statement': [2],
139+
'vitest/prefer-each': [2],
140+
'vitest/prefer-spy-on': [2],
141+
'vitest/prefer-to-be': [2],
142+
'vitest/prefer-to-contain': [2],
143+
'vitest/prefer-to-have-length': [2],
50144
},
145+
146+
settings: {
147+
vitest: {
148+
typecheck: true,
149+
},
150+
},
151+
51152
linterOptions: { reportUnusedDisableDirectives: 2 },
52153
},
53154
{
54155
name: 'commonjs',
55156
files: ['**/*.c[jt]s'],
56157
languageOptions: { sourceType: 'commonjs' },
57158
rules: {
58-
'@typescript-eslint/no-require-imports': [0],
159+
'@typescript-eslint/no-require-imports': [
160+
0,
161+
[{ allow: [], allowAsImport: false }],
162+
],
59163
},
60164
},
165+
166+
prettierConfig,
61167
)
62168

63-
export default ESLintConfig
169+
export default eslintConfig

‎codemods/package.json

+80-41
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,99 @@
11
{
22
"name": "reselect-codemods",
3-
"version": "0.1.0",
3+
"version": "0.0.1",
44
"description": "A collection of codemods for the reselect library",
5-
"bugs": {
6-
"url": "https://github.com/reduxjs/reselect/issues"
7-
},
8-
"scripts": {
9-
"format": "prettier -w . --config prettier.config.mjs",
10-
"format-check": "prettier -c . --config prettier.config.mjs",
11-
"lint": "eslint --flag unstable_ts_config -c eslint.config.mts",
12-
"lint-fix": "eslint --fix --flag unstable_ts_config -c eslint.config.mts",
13-
"test": "vitest --run"
14-
},
15-
"bin": "./bin/cli.mjs",
16-
"files": [
17-
"bin/*",
18-
"transforms/**/index.ts",
19-
"transforms/**/README.md",
20-
"package.json",
21-
"README.md"
22-
],
235
"keywords": [
246
"redux",
257
"redux-toolkit",
268
"reselect",
279
"codemod"
2810
],
11+
"homepage": "https://github.com/reduxjs/reselect/tree/master/codemods#readme",
12+
"bugs": {
13+
"url": "https://github.com/reduxjs/reselect/issues"
14+
},
15+
"repository": {
16+
"directory": "codemods",
17+
"type": "git",
18+
"url": "git+https://github.com/reduxjs/reselect.git"
19+
},
20+
"bin": "./dist/bin.js",
21+
"scripts": {
22+
"build": "tsup --config=$INIT_CWD/tsup.config.mts",
23+
"check-exports": "attw --pack $INIT_CWD",
24+
"check-package-json": "publint --pack=yarn --strict $INIT_CWD",
25+
"clean": "rimraf -g $INIT_CWD/'{dist,coverage,?(.)temp}'/",
26+
"format-check": "prettier --ignore-path=$INIT_CWD/.prettierignore --config=$INIT_CWD/prettier.config.mjs -c $INIT_CWD",
27+
"format": "prettier --ignore-path=$INIT_CWD/.prettierignore --config=$INIT_CWD/prettier.config.mjs -w $INIT_CWD",
28+
"lint-fix": "eslint --config=$INIT_CWD/eslint.config.mts --fix $INIT_CWD",
29+
"lint": "eslint --config=$INIT_CWD/eslint.config.mts $INIT_CWD",
30+
"prepack": "yarn clean && yarn build",
31+
"test-types": "tsc -p $INIT_CWD/tsconfig.json",
32+
"test": "vitest --run --config=$INIT_CWD/vitest.config.mts"
33+
},
2934
"dependencies": {
30-
"execa": "^9.4.0",
31-
"globby": "^14.0.2",
32-
"jscodeshift": "^17.0.0",
33-
"ts-node": "^10.9.2",
34-
"typescript": "^5.6.2"
35+
"globby": "^14.1.0",
36+
"jscodeshift": "^17.1.2"
3537
},
3638
"devDependencies": {
37-
"@types/eslint-config-prettier": "^6.11.3",
38-
"@types/eslint__js": "^8.42.3",
39+
"@arethetypeswrong/cli": "^0.17.4",
40+
"@eslint/js": "^9.22.0",
3941
"@types/jscodeshift": "^0.12.0",
40-
"@types/node": "^22.7.4",
41-
"eslint": "^9.11.1",
42-
"eslint-config-prettier": "^9.1.0",
43-
"jiti": "^2.1.0",
44-
"prettier": "^3.3.3",
45-
"typescript-eslint": "^8.8.0",
46-
"vitest": "^2.1.2"
42+
"@types/node": "^22.13.10",
43+
"@typescript-eslint/utils": "^8.26.1",
44+
"@vitest/eslint-plugin": "^1.1.37",
45+
"eslint": "^9.22.0",
46+
"eslint-config-prettier": "^10.1.1",
47+
"jiti": "^2.4.2",
48+
"prettier": "^3.5.3",
49+
"publint": "^0.3.9",
50+
"reselect": "workspace:^",
51+
"rimraf": "^6.0.1",
52+
"tsup": "^8.4.0",
53+
"typescript": "^5.8.2",
54+
"typescript-eslint": "^8.26.1",
55+
"vitest": "^3.0.8"
4756
},
57+
"sideEffects": false,
58+
"type": "module",
59+
"exports": {
60+
".": {
61+
"bun": {
62+
"types": "./dist/index.d.ts",
63+
"default": "./src/index.ts"
64+
},
65+
"module-sync": {
66+
"types": "./dist/index.d.ts",
67+
"default": "./dist/index.js"
68+
},
69+
"module": {
70+
"types": "./dist/index.d.ts",
71+
"default": "./dist/index.js"
72+
},
73+
"import": {
74+
"types": "./dist/index.d.ts",
75+
"default": "./dist/index.js"
76+
},
77+
"default": {
78+
"types": "./dist/index.d.cts",
79+
"default": "./dist/index.cjs"
80+
}
81+
},
82+
"./package.json": "./package.json"
83+
},
84+
"main": "./dist/index.js",
85+
"module": "./dist/index.js",
86+
"source": "./src/index.ts",
87+
"types": "./dist/index.d.ts",
88+
"files": [
89+
"dist",
90+
"src"
91+
],
4892
"engines": {
4993
"node": ">= 16"
5094
},
5195
"publishConfig": {
52-
"access": "public"
53-
},
54-
"repository": {
55-
"directory": "codemods",
56-
"type": "git",
57-
"url": "git+https://github.com/reduxjs/reselect.git"
58-
},
59-
"sideEffects": false
96+
"access": "public",
97+
"provenance": true
98+
}
6099
}

‎codemods/src/bin.ts

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#!/usr/bin/env node
2+
3+
import { globby } from 'globby'
4+
import * as child_process from 'node:child_process'
5+
import * as path from 'node:path'
6+
import { fileURLToPath } from 'node:url'
7+
import { promisify } from 'node:util'
8+
9+
const __filename = fileURLToPath(import.meta.url)
10+
const __dirname = path.dirname(__filename)
11+
12+
const execFile = promisify(child_process.execFile)
13+
14+
const extensions = 'ts,tsx,js,jsx,mts,cts,mjs,cjs'
15+
16+
const transformerDirectory = path.join(__dirname, '..', 'dist', 'transforms')
17+
const transformerFilePath = path.join(
18+
transformerDirectory,
19+
`${process.argv[2]}.js`,
20+
)
21+
22+
const runCodemod = async () => {
23+
const targetedFiles = await globby(process.argv.slice(3))
24+
25+
await execFile(
26+
'jscodeshift',
27+
['-t', transformerFilePath, '--extensions', extensions, ...targetedFiles],
28+
{ shell: true },
29+
)
30+
}
31+
32+
void runCodemod()

‎codemods/src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { convertInputSelectorsToArrayTransform } from './transforms/convertInputSelectorsToArray.js'

‎codemods/transforms/convertInputSelectorsToArray/index.ts ‎codemods/src/transforms/convertInputSelectorsToArray.ts

+14-14
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,17 @@ import type {
1010
} from 'jscodeshift'
1111
import type { TestOptions } from 'jscodeshift/src/testUtils.js'
1212

13-
interface NamedFunctionCallExpression extends CallExpression {
13+
type NamedFunctionCallExpression = {
1414
callee: Identifier
15-
}
15+
} & CallExpression
1616

17-
interface NamedVariableDeclarator extends VariableDeclarator {
17+
type NamedVariableDeclarator = {
1818
id: Identifier
19-
}
19+
} & VariableDeclarator
2020

21-
interface NamedVariableDeclaration extends VariableDeclaration {
21+
type NamedVariableDeclaration = {
2222
declarations: NamedVariableDeclarator[]
23-
}
23+
} & VariableDeclaration
2424

2525
const CREATE_SELECTOR_CREATOR = 'createSelectorCreator'
2626

@@ -119,9 +119,7 @@ const collectSelectorCreatorsNamesWithTypes = (
119119
const getIdentifierVariableDeclarator = (
120120
root: Collection,
121121
identifier: Identifier,
122-
) => {
123-
return root.findVariableDeclarators(identifier.name).nodes()[0]
124-
}
122+
) => root.findVariableDeclarators(identifier.name).nodes()[0]
125123

126124
const isLastArgumentObject = (
127125
j: JSCodeshift,
@@ -132,16 +130,18 @@ const isLastArgumentObject = (
132130
return j.ObjectExpression.check(lastArgument)
133131
}
134132

135-
const transform: Transform = (fileInfo, api) => {
133+
export const convertInputSelectorsToArrayTransform: Transform = (
134+
fileInfo,
135+
api,
136+
) => {
136137
const { j } = api
137138

138139
const root = j(fileInfo.source)
139140

140141
const isNamedFunctionCallExpression = (
141142
path: ASTPath<CallExpression>,
142-
): path is ASTPath<NamedFunctionCallExpression> => {
143-
return j.Identifier.check(path.value.callee)
144-
}
143+
): path is ASTPath<NamedFunctionCallExpression> =>
144+
j.Identifier.check(path.value.callee)
145145

146146
const allNamedFunctionCallExpressions = root
147147
.find(j.CallExpression)
@@ -221,4 +221,4 @@ const transform: Transform = (fileInfo, api) => {
221221

222222
export const parser = 'tsx' satisfies TestOptions['parser']
223223

224-
export default transform
224+
export default convertInputSelectorsToArrayTransform
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import { globby } from 'globby'
2+
import { applyTransform } from 'jscodeshift/src/testUtils.js'
3+
import * as fs from 'node:fs/promises'
4+
import * as path from 'node:path'
5+
import {
6+
convertInputSelectorsToArrayTransform,
7+
parser,
8+
} from '../src/transforms/convertInputSelectorsToArray.js'
9+
import {
10+
fixtureParentDirectoryPath,
11+
runCodemodCLI,
12+
tempParentDirectoryPath,
13+
} from './test-utils.js'
14+
15+
const transformerName = path.basename(import.meta.filename, '.test.ts')
16+
17+
const fixtureDirectoryPath = path.join(
18+
fixtureParentDirectoryPath,
19+
transformerName,
20+
)
21+
22+
const tempDirectoryPath = path.join(tempParentDirectoryPath, transformerName)
23+
24+
describe(convertInputSelectorsToArrayTransform, () => {
25+
describe(transformerName, async () => {
26+
const entries = await globby('**/*.input.*', {
27+
cwd: fixtureDirectoryPath,
28+
absolute: true,
29+
objectMode: true,
30+
})
31+
32+
await Promise.all(
33+
entries.map(async ({ name: fileName, path: testInputPath }) => {
34+
const extension = path.extname(fileName)
35+
36+
const testName = path.basename(fileName, `.input${extension}`)
37+
38+
const inputFilePath = path.join(
39+
fixtureDirectoryPath,
40+
`${testName}.input${extension}`,
41+
)
42+
43+
const outputFilePath = path.join(
44+
fixtureDirectoryPath,
45+
`${testName}.output${extension}`,
46+
)
47+
48+
const inputFileContent = await fs.readFile(inputFilePath, {
49+
encoding: 'utf-8',
50+
})
51+
52+
const expectedOutput = await fs.readFile(outputFilePath, {
53+
encoding: 'utf-8',
54+
})
55+
56+
describe(`${testName}${extension}`, () => {
57+
it('transforms correctly', () => {
58+
const actualOutput = applyTransform(
59+
{ default: convertInputSelectorsToArrayTransform, parser },
60+
{},
61+
{
62+
path: testInputPath,
63+
source: inputFileContent,
64+
},
65+
{ parser },
66+
)
67+
68+
expect(actualOutput).toBe(expectedOutput.trim())
69+
})
70+
71+
it('is idempotent', () => {
72+
const actualOutput = applyTransform(
73+
{ default: convertInputSelectorsToArrayTransform, parser },
74+
{},
75+
{
76+
path: testInputPath,
77+
source: inputFileContent,
78+
},
79+
{ parser },
80+
)
81+
82+
expect(actualOutput).toBe(expectedOutput.trim())
83+
})
84+
})
85+
}),
86+
)
87+
})
88+
})
89+
90+
describe('cli test', { todo: true }, () => {
91+
test('glob patterns work', async () => {
92+
await expect(
93+
runCodemodCLI([transformerName, './**/*-*.*.{m,c,}ts{x,}'], {
94+
cwd: tempDirectoryPath,
95+
}),
96+
).resolves.not.toThrow()
97+
98+
const outputFileNames = await globby('**/*.output.ts', {
99+
cwd: fixtureDirectoryPath,
100+
absolute: true,
101+
})
102+
103+
const expectedContents = Object.fromEntries(
104+
await Promise.all(
105+
outputFileNames.map(
106+
async outputFile =>
107+
[
108+
`${path.basename(outputFile, '.ts')}.*`,
109+
await fs.readFile(outputFile, { encoding: 'utf-8' }),
110+
] as const,
111+
),
112+
),
113+
)
114+
115+
const outputFiles = await globby(Object.keys(expectedContents), {
116+
absolute: true,
117+
cwd: tempDirectoryPath,
118+
})
119+
120+
const outputContents = await Promise.all(
121+
outputFiles.map(async filePath =>
122+
fs.readFile(filePath, { encoding: 'utf-8' }),
123+
),
124+
)
125+
126+
const values = Object.entries(expectedContents)
127+
128+
values.forEach(([key, value]) => {
129+
expect(outputContents).toStrictEqual(
130+
Array(outputContents.length).fill(expectedContents[key]),
131+
)
132+
})
133+
})
134+
})

‎codemods/tests/test-utils.ts

+69
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import type { ExecFileOptionsWithOtherEncoding } from 'node:child_process'
2+
import * as child_process from 'node:child_process'
3+
import * as path from 'node:path'
4+
import { promisify } from 'node:util'
5+
import packageJson from '../package.json' with { type: 'json' }
6+
7+
const execFile = promisify(child_process.execFile)
8+
9+
export const fixtureParentDirectoryPath = path.join(
10+
import.meta.dirname,
11+
'__testfixtures__',
12+
)
13+
14+
export const tempParentDirectoryPath = path.join(
15+
import.meta.dirname,
16+
'..',
17+
'.temp',
18+
)
19+
20+
export const extensionsToTest = [
21+
'ts',
22+
'tsx',
23+
'js',
24+
'jsx',
25+
'mts',
26+
'cts',
27+
'mjs',
28+
'cjs',
29+
] as const
30+
31+
export const TSExtensions = ['ts', 'tsx', 'mts', 'cts'] as const
32+
33+
export const JSExtensions = ['js', 'jsx', 'mjs', 'cjs'] as const
34+
35+
const defaultCLICommand = packageJson.name
36+
37+
const defaultCLIArguments = [] as const satisfies string[]
38+
39+
const defaultExecFileOptions = {
40+
encoding: 'utf-8',
41+
shell: true,
42+
} as const satisfies ExecFileOptionsWithOtherEncoding
43+
44+
export const runCodemodCLI = async (
45+
CLIArguments: readonly string[] = [],
46+
execFileOptions?: Partial<ExecFileOptionsWithOtherEncoding>,
47+
): Promise<{
48+
stderr: string
49+
stdout: string
50+
}> => {
51+
const { stderr, stdout } = await execFile(
52+
defaultCLICommand,
53+
[...defaultCLIArguments, ...CLIArguments],
54+
{
55+
...defaultExecFileOptions,
56+
...execFileOptions,
57+
},
58+
)
59+
60+
if (stderr) {
61+
console.log(stderr.trim())
62+
}
63+
64+
if (stdout) {
65+
console.log(stdout.trim())
66+
}
67+
68+
return { stderr, stdout }
69+
}

‎codemods/transformTestUtils.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { globby } from 'globby'
12
import type { Transform } from 'jscodeshift'
23
import type { TestOptions } from 'jscodeshift/src/testUtils.js'
34
import { applyTransform } from 'jscodeshift/src/testUtils.js'
@@ -11,8 +12,6 @@ export const runTransformTest = (
1112
fixturePath: string,
1213
) => {
1314
describe(transformName, async () => {
14-
const { globby } = await import('globby')
15-
1615
const entries = await globby('**/*.input.*', {
1716
cwd: fixturePath,
1817
absolute: true,

‎codemods/transforms/convertInputSelectorsToArray/convertInputSelectorsToArray.test.ts

-10
This file was deleted.

‎codemods/tsconfig.build.json

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"extends": "./tsconfig.json",
3+
"compilerOptions": {
4+
"declarationMap": true,
5+
"rootDir": "./src"
6+
},
7+
"include": ["src"]
8+
}

‎codemods/tsconfig.json

+10-3
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,30 @@
33
"allowJs": true,
44
"allowSyntheticDefaultImports": true,
55
"checkJs": true,
6+
"declaration": true,
67
"esModuleInterop": true,
78
"forceConsistentCasingInFileNames": true,
89
"isolatedModules": true,
9-
"jsx": "preserve",
10+
"jsx": "react",
11+
"lib": ["DOM", "ESNext"],
1012
"module": "NodeNext",
1113
"moduleResolution": "NodeNext",
1214
"noEmit": true,
1315
"noErrorTruncation": true,
1416
"noFallthroughCasesInSwitch": true,
17+
"noImplicitOverride": true,
18+
"noImplicitReturns": true,
1519
"outDir": "./dist",
20+
"resolveJsonModule": true,
1621
"rootDir": "./",
1722
"skipLibCheck": true,
23+
"sourceMap": true,
1824
"strict": true,
1925
"target": "ESNext",
2026
"types": ["vitest/globals", "vitest/importMeta"],
2127
"useDefineForClassFields": true,
22-
"useUnknownInCatchVariables": true
28+
"useUnknownInCatchVariables": true,
29+
"verbatimModuleSyntax": true
2330
},
24-
"exclude": ["**/__testfixtures__/**"]
31+
"exclude": ["dist", "**/__testfixtures__/**"]
2532
}

‎codemods/tsup.config.mts

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import * as path from 'node:path'
2+
import { fileURLToPath } from 'node:url'
3+
import type { Options } from 'tsup'
4+
import { defineConfig } from 'tsup'
5+
import packageJson from './package.json' with { type: 'json' }
6+
7+
const __dirname = path.dirname(fileURLToPath(import.meta.url))
8+
9+
const tsupConfig = defineConfig((overrideOptions): Options[] => {
10+
const commonOptions = {
11+
clean: true,
12+
entry: ['src/index.ts', 'src/transforms/**/*.ts'],
13+
removeNodeProtocol: false,
14+
shims: true,
15+
sourcemap: true,
16+
splitting: false,
17+
target: ['esnext', 'node20'],
18+
tsconfig: path.join(__dirname, 'tsconfig.build.json'),
19+
...overrideOptions,
20+
} satisfies Options
21+
22+
return [
23+
{
24+
...commonOptions,
25+
name: `${packageJson.name} Modern ESM`,
26+
format: ['esm'],
27+
},
28+
{
29+
...commonOptions,
30+
name: `${packageJson.name} CJS Development`,
31+
format: ['cjs'],
32+
},
33+
{
34+
...commonOptions,
35+
name: `${packageJson.name} CLI Development`,
36+
entry: {
37+
bin: path.join(__dirname, 'src', 'bin.ts'),
38+
},
39+
external: [packageJson.name],
40+
format: ['cjs', 'esm'],
41+
minify: true,
42+
},
43+
{
44+
...commonOptions,
45+
name: `${packageJson.name} ESM Type definitions`,
46+
dts: {
47+
only: true,
48+
},
49+
format: ['esm'],
50+
},
51+
{
52+
...commonOptions,
53+
name: `${packageJson.name} CJS Type definitions`,
54+
dts: {
55+
only: true,
56+
},
57+
format: ['cjs'],
58+
},
59+
]
60+
})
61+
62+
export default tsupConfig

‎codemods/vitest.config.mts

+37-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,44 @@
1+
import * as path from 'path'
12
import { defineConfig } from 'vitest/config'
3+
import packageJson from './package.json' with { type: 'json' }
24

3-
export default defineConfig({
5+
const vitestConfig = defineConfig({
46
test: {
7+
dir: path.join(import.meta.dirname, 'tests'),
8+
name: packageJson.name,
9+
root: import.meta.dirname,
10+
11+
coverage: {
12+
include: ['src'],
13+
extension: ['.ts', '.tsx', '.js', '.jsx', '.mts', '.mjs', '.cts', '.cjs'],
14+
},
15+
16+
reporters: process.env.GITHUB_ACTIONS
17+
? [['github-actions'], ['verbose']]
18+
: [['verbose']],
19+
20+
typecheck: {
21+
enabled: true,
22+
tsconfig: path.join(import.meta.dirname, 'tsconfig.json'),
23+
},
24+
25+
clearMocks: true,
26+
unstubEnvs: true,
27+
unstubGlobals: true,
28+
529
globals: true,
630
watch: false,
31+
32+
chaiConfig: {
33+
truncateThreshold: 10_000,
34+
},
35+
36+
globalSetup: ['./vitest.global.setup.ts'],
37+
},
38+
39+
define: {
40+
'import.meta.vitest': 'undefined',
741
},
842
})
43+
44+
export default vitestConfig

‎codemods/vitest.global.setup.ts

+108
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import * as fs from 'node:fs/promises'
2+
import path from 'node:path'
3+
import type { TestProject } from 'vitest/node'
4+
import {
5+
JSExtensions,
6+
TSExtensions,
7+
fixtureParentDirectoryPath,
8+
tempParentDirectoryPath,
9+
} from './tests/test-utils.js'
10+
11+
export async function setup(_testProject: TestProject): Promise<void> {
12+
await fs.rm(tempParentDirectoryPath, { force: true, recursive: true })
13+
14+
await fs.mkdir(tempParentDirectoryPath, { recursive: true })
15+
16+
await fs.cp(fixtureParentDirectoryPath, tempParentDirectoryPath, {
17+
recursive: true,
18+
filter: source =>
19+
!source.endsWith('.output.ts') && !source.endsWith('.output.js'),
20+
})
21+
22+
const directoryEntries = await Promise.all(
23+
(
24+
await fs.readdir(tempParentDirectoryPath, {
25+
recursive: true,
26+
encoding: 'utf-8',
27+
withFileTypes: true,
28+
})
29+
)
30+
.filter(directoryEntry => directoryEntry.isFile())
31+
.map(async directoryEntry => {
32+
const pathExtension = path.extname(directoryEntry.name)
33+
34+
const basename = path.basename(
35+
directoryEntry.name,
36+
`.input${pathExtension}`,
37+
)
38+
39+
const newPath = path.join(
40+
directoryEntry.parentPath,
41+
`${basename}.output${pathExtension}`,
42+
)
43+
44+
const oldPath = path.join(
45+
directoryEntry.parentPath,
46+
directoryEntry.name,
47+
)
48+
49+
await fs.rename(oldPath, newPath)
50+
51+
return newPath
52+
}),
53+
)
54+
55+
directoryEntries
56+
.filter(directoryEntry => directoryEntry.endsWith('.ts'))
57+
.map(async directoryEntry => {
58+
await Promise.all(
59+
TSExtensions.map(extensionToTest =>
60+
fs.copyFile(
61+
directoryEntry,
62+
path.join(
63+
path.dirname(directoryEntry),
64+
`${path.basename(directoryEntry, '.ts')}.${extensionToTest}`,
65+
),
66+
),
67+
),
68+
)
69+
})
70+
71+
directoryEntries
72+
.filter(directoryEntry => directoryEntry.endsWith('.js'))
73+
.map(async directoryEntry => {
74+
await Promise.all(
75+
JSExtensions.map(extensionToTest =>
76+
fs.copyFile(
77+
directoryEntry,
78+
path.join(
79+
path.dirname(directoryEntry),
80+
`${path.basename(directoryEntry, '.js')}.${extensionToTest}`,
81+
),
82+
),
83+
),
84+
)
85+
})
86+
87+
// await Promise.all(
88+
// directoryEntries.map(async directoryEntry => {
89+
// await Promise.all(
90+
// extensionsToTest.map(extensionToTest =>
91+
// fs.copyFile(
92+
// directoryEntry,
93+
// path.join(
94+
// path.dirname(directoryEntry),
95+
// `${path.basename(directoryEntry, '.ts')}.${extensionToTest}`,
96+
// ),
97+
// ),
98+
// ),
99+
// )
100+
// }),
101+
// )
102+
}
103+
104+
export async function teardown(): Promise<void> {
105+
if (process.env.KEEP_TEMP_DIR !== 'true') {
106+
await fs.rm(tempParentDirectoryPath, { force: true, recursive: true })
107+
}
108+
}

‎codemods/yarn.lock

-4,112
This file was deleted.

‎package.json

+3
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@
5454
"url": "https://github.com/reduxjs/reselect.git"
5555
},
5656
"license": "MIT",
57+
"workspaces": [
58+
"codemods"
59+
],
5760
"devDependencies": {
5861
"@reduxjs/toolkit": "^2.0.1",
5962
"@testing-library/react": "^14.1.2",

‎yarn.lock

+7,633-5,680
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)
Please sign in to comment.