@wise/wds-codemods 0.0.1-experimental-cbae00f → 0.0.1-experimental-926a862

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.
Files changed (60) hide show
  1. package/dist/index.js +133 -135
  2. package/dist/index.js.map +1 -1
  3. package/dist/reportManualReview-DQ00-OKx.js +50 -0
  4. package/dist/reportManualReview-DQ00-OKx.js.map +1 -0
  5. package/dist/transforms/button.js +493 -566
  6. package/dist/transforms/button.js.map +1 -1
  7. package/package.json +19 -14
  8. package/.changeset/better-impalas-drop.md +0 -5
  9. package/.changeset/config.json +0 -13
  10. package/.changeset/quick-mails-joke.md +0 -128
  11. package/.github/CODEOWNERS +0 -1
  12. package/.github/actions/bootstrap/action.yml +0 -49
  13. package/.github/actions/commitlint/action.yml +0 -27
  14. package/.github/actions/test/action.yml +0 -23
  15. package/.github/workflows/cd-cd.yml +0 -127
  16. package/.github/workflows/renovate.yml +0 -16
  17. package/.husky/commit-msg +0 -1
  18. package/.husky/pre-commit +0 -1
  19. package/.nvmrc +0 -1
  20. package/.prettierignore +0 -1
  21. package/.prettierrc.js +0 -5
  22. package/DEVELOPER.md +0 -783
  23. package/babel.config.js +0 -28
  24. package/commitlint.config.js +0 -3
  25. package/dist/index.d.ts +0 -1
  26. package/dist/transforms/button.d.ts +0 -16
  27. package/eslint.config.js +0 -15
  28. package/jest.config.js +0 -9
  29. package/mkdocs.yml +0 -4
  30. package/renovate.json +0 -9
  31. package/scripts/build.sh +0 -10
  32. package/src/__tests__/runCodemod.test.ts +0 -96
  33. package/src/index.ts +0 -4
  34. package/src/runCodemod.ts +0 -88
  35. package/src/transforms/button/__tests__/button.test.tsx +0 -153
  36. package/src/transforms/button/button.ts +0 -418
  37. package/src/transforms/helpers/__tests__/createTestTransform.test.ts +0 -27
  38. package/src/transforms/helpers/__tests__/hasImport.test.ts +0 -52
  39. package/src/transforms/helpers/__tests__/iconUtils.test.ts +0 -207
  40. package/src/transforms/helpers/__tests__/jsxElementUtils.test.ts +0 -130
  41. package/src/transforms/helpers/__tests__/jsxReportingUtils.test.ts +0 -265
  42. package/src/transforms/helpers/createTestTransform.ts +0 -18
  43. package/src/transforms/helpers/hasImport.ts +0 -60
  44. package/src/transforms/helpers/iconUtils.ts +0 -87
  45. package/src/transforms/helpers/index.ts +0 -5
  46. package/src/transforms/helpers/jsxElementUtils.ts +0 -67
  47. package/src/transforms/helpers/jsxReportingUtils.ts +0 -224
  48. package/src/utils/__tests__/getOptions.test.ts +0 -170
  49. package/src/utils/__tests__/handleError.test.ts +0 -18
  50. package/src/utils/__tests__/loadTransformModules.test.ts +0 -51
  51. package/src/utils/__tests__/reportManualReview.test.ts +0 -42
  52. package/src/utils/getOptions.ts +0 -63
  53. package/src/utils/handleError.ts +0 -6
  54. package/src/utils/index.ts +0 -4
  55. package/src/utils/loadTransformModules.ts +0 -28
  56. package/src/utils/reportManualReview.ts +0 -17
  57. package/test-button.tsx +0 -230
  58. package/test-file.js +0 -2
  59. package/tsconfig.json +0 -14
  60. package/tsup.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
- };
@@ -1,3 +0,0 @@
1
- export default {
2
- extends: ['@commitlint/config-conventional'],
3
- };
package/dist/index.d.ts DELETED
@@ -1 +0,0 @@
1
- #!/usr/bin/env node
@@ -1,16 +0,0 @@
1
- import { FileInfo, API, Options } from 'jscodeshift';
2
-
3
- declare const parser = "tsx";
4
- /**
5
- * This transform function modifies the Button and ActionButton components from the @transferwise/components library.
6
- * It updates the ActionButton component to use the Button component with specific attributes and mappings.
7
- * It also processes icon children and removes legacy props.
8
- *
9
- * @param {FileInfo} file - The file information object.
10
- * @param {API} api - The API object for jscodeshift.
11
- * @param {Options} options - The options object for jscodeshift.
12
- * @returns {string} - The transformed source code.
13
- */
14
- declare const transformer: (file: FileInfo, api: API, options: Options) => string;
15
-
16
- export { transformer as default, parser };
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
@@ -1,4 +0,0 @@
1
- site_name: Repository docs
2
-
3
- nav:
4
- - Introduction: index.md
package/renovate.json DELETED
@@ -1,9 +0,0 @@
1
- {
2
- "extends": ["github>transferwise/renovate-config:node-lib", "schedule:weekly"],
3
- "packageRules": [
4
- {
5
- "datasources": ["npm"],
6
- "registryUrls": ["https://registry.npmjs.org"]
7
- }
8
- ]
9
- }
package/scripts/build.sh DELETED
@@ -1,10 +0,0 @@
1
- #!/bin/bash
2
- set -e
3
-
4
- # Build main entry using tsup config
5
- tsup
6
-
7
- # Build all files in src/transforms except helpers and __tests__ directories
8
- for file in $(find src/transforms -type f -name '*.ts' ! -path 'src/transforms/helpers/*' ! -path '*/__tests__/*'); do
9
- tsup "$file" --format esm --out-dir dist/transforms --target es2022
10
- done
@@ -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
@@ -1,4 +0,0 @@
1
- #!/usr/bin/env node
2
- import { runCodemod } from './runCodemod';
3
-
4
- void runCodemod();
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 '../button';
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
- });