@wise/wds-codemods 0.0.1-experimental-4bee5c0 → 0.0.1-experimental-b974fa6
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/package.json +6 -1
- package/.changeset/better-impalas-drop.md +0 -5
- package/.changeset/config.json +0 -13
- package/.changeset/quick-mails-joke.md +0 -128
- package/.github/CODEOWNERS +0 -1
- package/.github/actions/bootstrap/action.yml +0 -49
- package/.github/actions/commitlint/action.yml +0 -27
- package/.github/actions/test/action.yml +0 -23
- package/.github/workflows/cd-cd.yml +0 -150
- package/.github/workflows/renovate.yml +0 -16
- package/.husky/commit-msg +0 -1
- package/.husky/pre-commit +0 -1
- package/.nvmrc +0 -1
- package/.prettierignore +0 -1
- package/.prettierrc.js +0 -5
- package/DEVELOPER.md +0 -783
- package/babel.config.js +0 -28
- package/commitlint.config.js +0 -3
- package/eslint.config.js +0 -15
- package/jest.config.js +0 -9
- package/mkdocs.yml +0 -4
- package/renovate.json +0 -9
- package/src/__tests__/runCodemod.test.ts +0 -96
- package/src/index.ts +0 -4
- package/src/runCodemod.ts +0 -88
- package/src/transforms/button/__tests__/button.test.tsx +0 -153
- package/src/transforms/button/transformer.ts +0 -418
- package/src/transforms/helpers/__tests__/createTestTransform.test.ts +0 -27
- package/src/transforms/helpers/__tests__/hasImport.test.ts +0 -52
- package/src/transforms/helpers/__tests__/iconUtils.test.ts +0 -207
- package/src/transforms/helpers/__tests__/jsxElementUtils.test.ts +0 -130
- package/src/transforms/helpers/__tests__/jsxReportingUtils.test.ts +0 -265
- package/src/transforms/helpers/createTestTransform.ts +0 -18
- package/src/transforms/helpers/hasImport.ts +0 -60
- package/src/transforms/helpers/iconUtils.ts +0 -87
- package/src/transforms/helpers/index.ts +0 -5
- package/src/transforms/helpers/jsxElementUtils.ts +0 -67
- package/src/transforms/helpers/jsxReportingUtils.ts +0 -224
- package/src/utils/__tests__/getOptions.test.ts +0 -170
- package/src/utils/__tests__/handleError.test.ts +0 -18
- package/src/utils/__tests__/loadTransformModules.test.ts +0 -51
- package/src/utils/__tests__/reportManualReview.test.ts +0 -42
- package/src/utils/getOptions.ts +0 -63
- package/src/utils/handleError.ts +0 -6
- package/src/utils/index.ts +0 -4
- package/src/utils/loadTransformModules.ts +0 -28
- package/src/utils/reportManualReview.ts +0 -17
- package/test-button.tsx +0 -230
- package/test-file.js +0 -2
- package/tsconfig.json +0 -14
- package/tsdown.config.js +0 -13
package/babel.config.js
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
export default {
|
|
2
|
-
presets: [
|
|
3
|
-
[
|
|
4
|
-
'@babel/preset-env',
|
|
5
|
-
{
|
|
6
|
-
targets: { node: 'current' },
|
|
7
|
-
modules: false,
|
|
8
|
-
},
|
|
9
|
-
],
|
|
10
|
-
'@babel/preset-typescript',
|
|
11
|
-
],
|
|
12
|
-
plugins: ['@babel/plugin-syntax-import-meta', 'babel-plugin-transform-import-meta'],
|
|
13
|
-
env: {
|
|
14
|
-
test: {
|
|
15
|
-
presets: [
|
|
16
|
-
[
|
|
17
|
-
'@babel/preset-env',
|
|
18
|
-
{
|
|
19
|
-
targets: { node: 'current' },
|
|
20
|
-
modules: 'commonjs',
|
|
21
|
-
},
|
|
22
|
-
],
|
|
23
|
-
'@babel/preset-typescript',
|
|
24
|
-
],
|
|
25
|
-
plugins: ['@babel/plugin-syntax-import-meta', 'babel-plugin-transform-import-meta'],
|
|
26
|
-
},
|
|
27
|
-
},
|
|
28
|
-
};
|
package/commitlint.config.js
DELETED
package/eslint.config.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import baseStrict from '@wise/eslint-config/base-strict';
|
|
2
|
-
import jest from '@wise/eslint-config/jest';
|
|
3
|
-
|
|
4
|
-
export default [
|
|
5
|
-
...baseStrict,
|
|
6
|
-
...jest,
|
|
7
|
-
{
|
|
8
|
-
ignores: ['dist', 'test-button.tsx'],
|
|
9
|
-
},
|
|
10
|
-
{
|
|
11
|
-
rules: {
|
|
12
|
-
'functional/immutable-data': 'off',
|
|
13
|
-
},
|
|
14
|
-
},
|
|
15
|
-
];
|
package/jest.config.js
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
export default {
|
|
2
|
-
testEnvironment: 'node',
|
|
3
|
-
testPathIgnorePatterns: ['<rootDir>/dist/'],
|
|
4
|
-
extensionsToTreatAsEsm: ['.ts', '.tsx'],
|
|
5
|
-
transform: {
|
|
6
|
-
'^.+\\.tsx?$': ['babel-jest', { configFile: './babel.config.js' }],
|
|
7
|
-
},
|
|
8
|
-
transformIgnorePatterns: ['/node_modules/'],
|
|
9
|
-
};
|
package/mkdocs.yml
DELETED
package/renovate.json
DELETED
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import { execSync } from 'child_process';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import { fileURLToPath } from 'url';
|
|
4
|
-
|
|
5
|
-
import { runCodemod } from '../runCodemod';
|
|
6
|
-
import * as utils from '../utils';
|
|
7
|
-
|
|
8
|
-
Object.defineProperty(globalThis, 'import', {
|
|
9
|
-
value: {
|
|
10
|
-
meta: {
|
|
11
|
-
url: 'file:///current/file/path.js',
|
|
12
|
-
},
|
|
13
|
-
},
|
|
14
|
-
writable: true,
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
jest.mock('node:fs/promises');
|
|
18
|
-
jest.mock('child_process', () => ({
|
|
19
|
-
execSync: jest.fn(),
|
|
20
|
-
}));
|
|
21
|
-
|
|
22
|
-
jest.mock('path', () => ({
|
|
23
|
-
resolve: jest.fn((...args: string[]) => args.join('/')),
|
|
24
|
-
dirname: jest.fn((filePath: string) => {
|
|
25
|
-
// Simple implementation that mimics path.dirname
|
|
26
|
-
const lastSlashIndex = filePath.lastIndexOf('/');
|
|
27
|
-
return lastSlashIndex !== -1 ? filePath.substring(0, lastSlashIndex) : '.';
|
|
28
|
-
}),
|
|
29
|
-
}));
|
|
30
|
-
|
|
31
|
-
jest.mock('../utils', () => ({
|
|
32
|
-
loadTransformModules: jest.fn(),
|
|
33
|
-
getOptions: jest.fn(),
|
|
34
|
-
handleError: jest.fn(),
|
|
35
|
-
}));
|
|
36
|
-
|
|
37
|
-
describe('runCodemod', () => {
|
|
38
|
-
const consoleDebugSpy = jest.spyOn(console, 'debug').mockImplementation(() => {});
|
|
39
|
-
const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(() => {});
|
|
40
|
-
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
41
|
-
|
|
42
|
-
afterEach(() => {
|
|
43
|
-
jest.clearAllMocks();
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it('should run jscodeshift with correct command using mock transformsDir', async () => {
|
|
47
|
-
const mockTransformsDir = '/mock/dist/transforms';
|
|
48
|
-
|
|
49
|
-
(path.resolve as jest.Mock).mockImplementation((...args: string[]) => args.join('/'));
|
|
50
|
-
(utils.loadTransformModules as jest.Mock).mockResolvedValue({
|
|
51
|
-
transformFiles: [Promise.resolve('transformA'), Promise.resolve('transformB')],
|
|
52
|
-
});
|
|
53
|
-
(utils.getOptions as jest.Mock).mockResolvedValue({
|
|
54
|
-
transformFile: 'transformA',
|
|
55
|
-
targetPath: 'src/',
|
|
56
|
-
dry: true,
|
|
57
|
-
print: false,
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
await runCodemod(mockTransformsDir);
|
|
61
|
-
|
|
62
|
-
expect(utils.loadTransformModules).toHaveBeenCalledWith(mockTransformsDir);
|
|
63
|
-
expect(utils.getOptions).toHaveBeenCalledWith(['transformA', 'transformB']);
|
|
64
|
-
expect(execSync).toHaveBeenCalledWith(
|
|
65
|
-
'npx jscodeshift -t /mock/dist/transforms/transformA.js src/ --dry',
|
|
66
|
-
{ stdio: 'inherit' },
|
|
67
|
-
);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it('should throw error if no transform scripts found', async () => {
|
|
71
|
-
(path.resolve as jest.Mock).mockReturnValue('/mock/dist/transforms');
|
|
72
|
-
(utils.loadTransformModules as jest.Mock).mockResolvedValue({
|
|
73
|
-
transformFiles: [],
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
await runCodemod('/mock/dist/transforms');
|
|
77
|
-
|
|
78
|
-
expect(execSync).not.toHaveBeenCalled();
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it('should log error if exception thrown', async () => {
|
|
82
|
-
const testError = new Error('Test error');
|
|
83
|
-
|
|
84
|
-
(utils.loadTransformModules as jest.Mock).mockRejectedValue(testError);
|
|
85
|
-
|
|
86
|
-
const mockExit = jest.spyOn(process, 'exit').mockImplementation(((code?: number) => {
|
|
87
|
-
throw new Error(`Process exited with code ${code}`);
|
|
88
|
-
}) as never);
|
|
89
|
-
|
|
90
|
-
await runCodemod('/mock/dist/transforms').catch(() => {});
|
|
91
|
-
|
|
92
|
-
expect(consoleErrorSpy).toHaveBeenCalledWith('Error running codemod:', testError.message);
|
|
93
|
-
|
|
94
|
-
mockExit.mockRestore();
|
|
95
|
-
});
|
|
96
|
-
});
|
package/src/index.ts
DELETED
package/src/runCodemod.ts
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import fs from 'node:fs/promises';
|
|
4
|
-
|
|
5
|
-
import { execSync } from 'child_process';
|
|
6
|
-
import path from 'path';
|
|
7
|
-
import { fileURLToPath } from 'url';
|
|
8
|
-
|
|
9
|
-
import { getOptions, handleError, loadTransformModules } from './utils';
|
|
10
|
-
|
|
11
|
-
const currentFilePath = fileURLToPath(import.meta.url);
|
|
12
|
-
const currentDirPath = path.dirname(currentFilePath);
|
|
13
|
-
|
|
14
|
-
async function runCodemod(transformsDir?: string) {
|
|
15
|
-
try {
|
|
16
|
-
const resolvedTransformsDir =
|
|
17
|
-
transformsDir ?? path.resolve(currentDirPath, '../dist/transforms');
|
|
18
|
-
console.debug(`Resolved transforms directory: ${resolvedTransformsDir}`);
|
|
19
|
-
|
|
20
|
-
const { transformFiles } = await loadTransformModules(resolvedTransformsDir);
|
|
21
|
-
|
|
22
|
-
if (transformFiles.length === 0) {
|
|
23
|
-
throw new Error(`No transform scripts found in directory: ${resolvedTransformsDir}`);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const resolvedTransformFiles = await Promise.all(transformFiles);
|
|
27
|
-
const options = await getOptions(resolvedTransformFiles);
|
|
28
|
-
|
|
29
|
-
const codemodPath = path.resolve(resolvedTransformsDir, `${options.transformFile}.js`);
|
|
30
|
-
console.debug(`Resolved codemod path: ${codemodPath}`);
|
|
31
|
-
|
|
32
|
-
const args = [
|
|
33
|
-
'-t',
|
|
34
|
-
codemodPath,
|
|
35
|
-
options.targetPath,
|
|
36
|
-
options.dry ? '--dry' : '',
|
|
37
|
-
options.print ? '--print' : '',
|
|
38
|
-
options.ignorePattern
|
|
39
|
-
? options.ignorePattern
|
|
40
|
-
.split(',')
|
|
41
|
-
.map((pattern) => `--ignore-pattern=${pattern.trim()}`)
|
|
42
|
-
.join(' ')
|
|
43
|
-
: '',
|
|
44
|
-
options.gitignore ? '--gitignore' : '',
|
|
45
|
-
].filter(Boolean);
|
|
46
|
-
|
|
47
|
-
const command = `npx jscodeshift ${args.join(' ')}`;
|
|
48
|
-
|
|
49
|
-
console.debug(`Running: ${command}`);
|
|
50
|
-
|
|
51
|
-
const reportPath = path.resolve(process.cwd(), 'codemod-report.txt');
|
|
52
|
-
|
|
53
|
-
try {
|
|
54
|
-
await fs.access(reportPath);
|
|
55
|
-
await fs.rm(reportPath);
|
|
56
|
-
console.debug(`Removed existing report file: ${reportPath}`);
|
|
57
|
-
} catch {
|
|
58
|
-
console.debug(`No existing report file to remove: ${reportPath}`);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
execSync(command, { stdio: 'inherit' });
|
|
62
|
-
|
|
63
|
-
try {
|
|
64
|
-
const reportContent = await fs.readFile(reportPath, 'utf8');
|
|
65
|
-
const lines = reportContent.split('\n').filter(Boolean);
|
|
66
|
-
if (lines.length) {
|
|
67
|
-
console.log(
|
|
68
|
-
`\n⚠️ ${lines.length} manual review${lines.length > 1 ? 's are' : ' is'} required. See ${reportPath} for details.`,
|
|
69
|
-
);
|
|
70
|
-
} else {
|
|
71
|
-
console.debug(`Report file exists but is empty: ${reportPath}`);
|
|
72
|
-
}
|
|
73
|
-
} catch {
|
|
74
|
-
console.debug(`No report file generated - no manual reviews needed`);
|
|
75
|
-
}
|
|
76
|
-
} catch (error: unknown) {
|
|
77
|
-
if (error instanceof Error) {
|
|
78
|
-
console.error('Error running codemod:', error.message);
|
|
79
|
-
} else {
|
|
80
|
-
console.error('Error running codemod:', error);
|
|
81
|
-
}
|
|
82
|
-
if (process.env.NODE_ENV !== 'test') {
|
|
83
|
-
process.exit(1);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export { runCodemod };
|
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs/promises';
|
|
2
|
-
|
|
3
|
-
import createTestTransform from '../../helpers/createTestTransform';
|
|
4
|
-
import transformer from '../transformer';
|
|
5
|
-
|
|
6
|
-
jest.mock(
|
|
7
|
-
'../../../utils',
|
|
8
|
-
(): Record<string, unknown> => ({
|
|
9
|
-
...jest.requireActual('../../../utils'),
|
|
10
|
-
reportManualReview: jest.fn(),
|
|
11
|
-
}),
|
|
12
|
-
);
|
|
13
|
-
|
|
14
|
-
describe('button codemod', () => {
|
|
15
|
-
const transform = createTestTransform(transformer);
|
|
16
|
-
it('renames ActionButton to Button and adds v2 and size', () => {
|
|
17
|
-
const input = `
|
|
18
|
-
import { ActionButton } from '@transferwise/components';
|
|
19
|
-
export default function Test() {
|
|
20
|
-
return <ActionButton>Click</ActionButton>;
|
|
21
|
-
}
|
|
22
|
-
`;
|
|
23
|
-
const output = transform({ source: input });
|
|
24
|
-
expect(output).toContain('<Button v2 size="sm">Click</Button>');
|
|
25
|
-
expect(output).not.toContain('ActionButton');
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it('adds v2 to Button if missing', () => {
|
|
29
|
-
const input = `
|
|
30
|
-
import { Button } from '@transferwise/components';
|
|
31
|
-
export default function Test() {
|
|
32
|
-
return <Button htmlType="button">Click</Button>;
|
|
33
|
-
}
|
|
34
|
-
`;
|
|
35
|
-
const output = transform({ source: input });
|
|
36
|
-
expect(output).toContain('<Button v2 type="button">Click</Button>');
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it('removes legacy props and adds new ones', () => {
|
|
40
|
-
const input = `
|
|
41
|
-
import { Button } from '@transferwise/components';
|
|
42
|
-
export default function Test() {
|
|
43
|
-
return (
|
|
44
|
-
<Button priority="primary" size="xs" type="accent" htmlType="submit" sentiment="positive">Test</Button>
|
|
45
|
-
);
|
|
46
|
-
}
|
|
47
|
-
`;
|
|
48
|
-
const output = transform({ source: input });
|
|
49
|
-
expect(output).toContain('size="sm"');
|
|
50
|
-
expect(output).toContain('priority="primary"');
|
|
51
|
-
expect(output).toContain('type="submit"');
|
|
52
|
-
expect(output).not.toContain(
|
|
53
|
-
'priority="primary" size="xs" type="accent" htmlType="submit" sentiment="positive"',
|
|
54
|
-
);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('converts enum props to string values', () => {
|
|
58
|
-
const input = `
|
|
59
|
-
import { Button } from '@transferwise/components';
|
|
60
|
-
export default function Test() {
|
|
61
|
-
return (
|
|
62
|
-
<Button priority={Priority.SECONDARY} type={ControlType.NEGATIVE}>Secondary Negative</Button>
|
|
63
|
-
);
|
|
64
|
-
}
|
|
65
|
-
`;
|
|
66
|
-
const output = transform({ source: input });
|
|
67
|
-
expect(output).toContain('priority="secondary"');
|
|
68
|
-
expect(output).toContain('sentiment="negative"');
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it('handles htmlType="submit" and legacy type/priority enums', () => {
|
|
72
|
-
const input = `
|
|
73
|
-
import { Button } from '@transferwise/components';
|
|
74
|
-
export default function Test() {
|
|
75
|
-
return (
|
|
76
|
-
<Button htmlType="submit" type={ControlType.ACCENT} priority={Priority.PRIMARY} disabled>
|
|
77
|
-
Button Disabled
|
|
78
|
-
</Button>
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
`;
|
|
82
|
-
const output = transform({ source: input });
|
|
83
|
-
expect(output).toContain('type="submit"');
|
|
84
|
-
expect(output).toContain('priority="primary"');
|
|
85
|
-
expect(output).toContain('disabled');
|
|
86
|
-
expect(output).not.toContain('htmlType');
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it('removes legacy props and keeps style and other non-legacy props', () => {
|
|
90
|
-
const input = `
|
|
91
|
-
import { Button } from '@transferwise/components';
|
|
92
|
-
export default function Test() {
|
|
93
|
-
return (
|
|
94
|
-
<Button type={ControlType.NEGATIVE} style={{ color: 'red' }} priority={Priority.PRIMARY}>
|
|
95
|
-
Primary Negative
|
|
96
|
-
</Button>
|
|
97
|
-
);
|
|
98
|
-
}
|
|
99
|
-
`;
|
|
100
|
-
const output = transform({ source: input });
|
|
101
|
-
expect(output).toContain('sentiment="negative"');
|
|
102
|
-
expect(output).toContain('priority="primary"');
|
|
103
|
-
expect(output).toContain("style={{ color: 'red' }}");
|
|
104
|
-
expect(output).not.toContain('ControlType');
|
|
105
|
-
expect(output).not.toContain('Priority');
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
it('adds v2 to Button if missing and keeps block/as/href', () => {
|
|
109
|
-
const input = `
|
|
110
|
-
import { Button } from '@transferwise/components';
|
|
111
|
-
export default function Test() {
|
|
112
|
-
return (
|
|
113
|
-
<Button as="a" href="/account" block>
|
|
114
|
-
Go to account
|
|
115
|
-
</Button>
|
|
116
|
-
);
|
|
117
|
-
}
|
|
118
|
-
`;
|
|
119
|
-
const output = transform({ source: input });
|
|
120
|
-
expect(output).toContain('v2');
|
|
121
|
-
expect(output).not.toContain('as="a"');
|
|
122
|
-
expect(output).toContain('href="/account"');
|
|
123
|
-
expect(output).toContain('block');
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
it('removes as="a" and adds href="#" if missing', () => {
|
|
127
|
-
const input = `
|
|
128
|
-
import { Button } from '@transferwise/components';
|
|
129
|
-
export default function Test() {
|
|
130
|
-
return (
|
|
131
|
-
<Button as="a">Link</Button>
|
|
132
|
-
);
|
|
133
|
-
}
|
|
134
|
-
`;
|
|
135
|
-
const output = transform({ source: input });
|
|
136
|
-
expect(output).not.toContain('as="a"');
|
|
137
|
-
expect(output).toContain('href="#"');
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
it('reports ambiguous size prop (identifier)', async () => {
|
|
141
|
-
const input = `
|
|
142
|
-
import { Button } from '@transferwise/components';
|
|
143
|
-
export default function Test() {
|
|
144
|
-
return <Button size={dynamicSize}>Test</Button>;
|
|
145
|
-
}
|
|
146
|
-
`;
|
|
147
|
-
transform({ source: input });
|
|
148
|
-
const report = await fs.readFile('codemod-report.txt', 'utf8');
|
|
149
|
-
expect(report).toContain(
|
|
150
|
-
'Manual review required: prop "size" on <Button> has unsupported value "dynamicSize".',
|
|
151
|
-
);
|
|
152
|
-
});
|
|
153
|
-
});
|