@wise/wds-codemods 0.0.1-experimental-2eb5228 → 0.0.1-experimental-0d8d466
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/.changeset/better-impalas-drop.md +1 -1
- package/.changeset/quick-mails-joke.md +128 -0
- package/.github/actions/test/action.yml +23 -0
- package/DEVELOPER.md +783 -0
- package/README.md +157 -14
- package/dist/index.js +10 -377
- package/dist/index.js.map +1 -1
- package/dist/transforms/button.d.ts +1 -5
- package/dist/transforms/button.js +96 -147
- package/dist/transforms/button.js.map +1 -1
- package/package.json +5 -7
- package/src/__tests__/runCodemod.test.ts +15 -57
- package/src/runCodemod.ts +1 -65
- package/src/transforms/button/__tests__/button.test.tsx +3 -25
- package/src/transforms/button/button.ts +126 -161
- package/src/transforms/helpers/createTestTransform.ts +2 -43
- package/src/transforms/helpers/index.ts +1 -1
- package/src/utils/__tests__/getOptions.test.ts +20 -69
- package/src/utils/getOptions.ts +2 -17
- package/src/transforms/helpers/__tests__/packageValidation.test.ts +0 -45
- package/src/transforms/helpers/packageValidation.ts +0 -53
- package/src/utils/__tests__/hasPackageVersion.test.ts +0 -190
- package/src/utils/hasPackageVersion.ts +0 -459
|
@@ -1,18 +1,17 @@
|
|
|
1
|
-
import fs from 'node:fs/promises';
|
|
2
|
-
|
|
3
1
|
import { execSync } from 'child_process';
|
|
4
2
|
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
5
4
|
|
|
6
5
|
import { runCodemod } from '../runCodemod';
|
|
7
6
|
import * as utils from '../utils';
|
|
8
|
-
import { findClosestGitignore, hasPackageVersionFromPath } from '../utils/hasPackageVersion';
|
|
9
7
|
|
|
10
|
-
// Mock dynamic import - only define once
|
|
11
|
-
const mockImport = jest.fn();
|
|
12
8
|
Object.defineProperty(globalThis, 'import', {
|
|
13
|
-
value:
|
|
9
|
+
value: {
|
|
10
|
+
meta: {
|
|
11
|
+
url: 'file:///current/file/path.js',
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
14
|
writable: true,
|
|
15
|
-
configurable: true,
|
|
16
15
|
});
|
|
17
16
|
|
|
18
17
|
jest.mock('node:fs/promises');
|
|
@@ -20,14 +19,10 @@ jest.mock('child_process', () => ({
|
|
|
20
19
|
execSync: jest.fn(),
|
|
21
20
|
}));
|
|
22
21
|
|
|
23
|
-
jest.mock('../utils/hasPackageVersion', () => ({
|
|
24
|
-
hasPackageVersionFromPath: jest.fn(),
|
|
25
|
-
findClosestGitignore: jest.fn(() => null), // Return null to simulate no gitignore found
|
|
26
|
-
}));
|
|
27
|
-
|
|
28
22
|
jest.mock('path', () => ({
|
|
29
23
|
resolve: jest.fn((...args: string[]) => args.join('/')),
|
|
30
24
|
dirname: jest.fn((filePath: string) => {
|
|
25
|
+
// Simple implementation that mimics path.dirname
|
|
31
26
|
const lastSlashIndex = filePath.lastIndexOf('/');
|
|
32
27
|
return lastSlashIndex !== -1 ? filePath.substring(0, lastSlashIndex) : '.';
|
|
33
28
|
}),
|
|
@@ -44,14 +39,6 @@ describe('runCodemod', () => {
|
|
|
44
39
|
const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation(() => {});
|
|
45
40
|
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
46
41
|
|
|
47
|
-
beforeEach(() => {
|
|
48
|
-
jest.clearAllMocks();
|
|
49
|
-
(fs.access as jest.Mock).mockRejectedValue(new Error('File not found'));
|
|
50
|
-
(fs.readFile as jest.Mock).mockRejectedValue(new Error('File not found'));
|
|
51
|
-
mockImport.mockResolvedValue({});
|
|
52
|
-
(hasPackageVersionFromPath as jest.Mock).mockReturnValue(true);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
42
|
afterEach(() => {
|
|
56
43
|
jest.clearAllMocks();
|
|
57
44
|
});
|
|
@@ -68,9 +55,6 @@ describe('runCodemod', () => {
|
|
|
68
55
|
targetPath: 'src/',
|
|
69
56
|
dry: true,
|
|
70
57
|
print: false,
|
|
71
|
-
gitignore: true,
|
|
72
|
-
ignorePattern: undefined,
|
|
73
|
-
isMonorepo: false,
|
|
74
58
|
});
|
|
75
59
|
|
|
76
60
|
await runCodemod(mockTransformsDir);
|
|
@@ -78,12 +62,12 @@ describe('runCodemod', () => {
|
|
|
78
62
|
expect(utils.loadTransformModules).toHaveBeenCalledWith(mockTransformsDir);
|
|
79
63
|
expect(utils.getOptions).toHaveBeenCalledWith(['transformA', 'transformB']);
|
|
80
64
|
expect(execSync).toHaveBeenCalledWith(
|
|
81
|
-
'npx jscodeshift -t /mock/dist/transforms/transformA.js src/ --
|
|
65
|
+
'npx jscodeshift -t /mock/dist/transforms/transformA.js src/ --dry',
|
|
82
66
|
{ stdio: 'inherit' },
|
|
83
67
|
);
|
|
84
68
|
});
|
|
85
69
|
|
|
86
|
-
it('should
|
|
70
|
+
it('should throw error if no transform scripts found', async () => {
|
|
87
71
|
(path.resolve as jest.Mock).mockReturnValue('/mock/dist/transforms');
|
|
88
72
|
(utils.loadTransformModules as jest.Mock).mockResolvedValue({
|
|
89
73
|
transformFiles: [],
|
|
@@ -91,10 +75,6 @@ describe('runCodemod', () => {
|
|
|
91
75
|
|
|
92
76
|
await runCodemod('/mock/dist/transforms');
|
|
93
77
|
|
|
94
|
-
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
|
95
|
-
'Error running codemod:',
|
|
96
|
-
'No transform scripts found in directory: /mock/dist/transforms',
|
|
97
|
-
);
|
|
98
78
|
expect(execSync).not.toHaveBeenCalled();
|
|
99
79
|
});
|
|
100
80
|
|
|
@@ -103,36 +83,14 @@ describe('runCodemod', () => {
|
|
|
103
83
|
|
|
104
84
|
(utils.loadTransformModules as jest.Mock).mockRejectedValue(testError);
|
|
105
85
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
});
|
|
86
|
+
const mockExit = jest.spyOn(process, 'exit').mockImplementation(((code?: number) => {
|
|
87
|
+
throw new Error(`Process exited with code ${code}`);
|
|
88
|
+
}) as never);
|
|
110
89
|
|
|
111
|
-
|
|
112
|
-
it('should include gitignore flag when gitignore file is found', async () => {
|
|
113
|
-
const mockTransformsDir = '/mock/dist/transforms';
|
|
114
|
-
const mockGitignorePath = '/path/to/.gitignore';
|
|
115
|
-
|
|
116
|
-
(findClosestGitignore as jest.Mock).mockReturnValueOnce(mockGitignorePath);
|
|
117
|
-
(path.resolve as jest.Mock).mockImplementation((...args: string[]) => args.join('/'));
|
|
118
|
-
(utils.loadTransformModules as jest.Mock).mockResolvedValue({
|
|
119
|
-
transformFiles: [Promise.resolve('transformA'), Promise.resolve('transformB')],
|
|
120
|
-
});
|
|
121
|
-
(utils.getOptions as jest.Mock).mockResolvedValue({
|
|
122
|
-
transformFile: 'transformA',
|
|
123
|
-
targetPath: 'src/',
|
|
124
|
-
dry: true,
|
|
125
|
-
print: false,
|
|
126
|
-
gitignore: true,
|
|
127
|
-
ignorePattern: undefined,
|
|
128
|
-
isMonorepo: false,
|
|
129
|
-
});
|
|
90
|
+
await runCodemod('/mock/dist/transforms').catch(() => {});
|
|
130
91
|
|
|
131
|
-
|
|
92
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith('Error running codemod:', testError.message);
|
|
132
93
|
|
|
133
|
-
|
|
134
|
-
`npx jscodeshift -t /mock/dist/transforms/transformA.js src/ --extensions=tsx,ts,jsx,js --ignore-pattern=**/node_modules/** --ignore-pattern=**/dist/** --ignore-pattern=**/*.d.ts --dry --ignore-config=${mockGitignorePath}`,
|
|
135
|
-
{ stdio: 'inherit' },
|
|
136
|
-
);
|
|
94
|
+
mockExit.mockRestore();
|
|
137
95
|
});
|
|
138
96
|
});
|
package/src/runCodemod.ts
CHANGED
|
@@ -7,26 +7,10 @@ import path from 'path';
|
|
|
7
7
|
import { fileURLToPath } from 'url';
|
|
8
8
|
|
|
9
9
|
import { getOptions, handleError, loadTransformModules } from './utils';
|
|
10
|
-
import { findClosestGitignore, hasPackageVersionFromPath } from './utils/hasPackageVersion';
|
|
11
10
|
|
|
12
11
|
const currentFilePath = fileURLToPath(import.meta.url);
|
|
13
12
|
const currentDirPath = path.dirname(currentFilePath);
|
|
14
13
|
|
|
15
|
-
// Function to get package requirements from a transform
|
|
16
|
-
async function getPackageRequirements(
|
|
17
|
-
transformPath: string,
|
|
18
|
-
): Promise<{ name: string; version: string }[]> {
|
|
19
|
-
try {
|
|
20
|
-
const transformModule = (await import(transformPath)) as {
|
|
21
|
-
packageRequirements?: { name: string; version: string }[];
|
|
22
|
-
};
|
|
23
|
-
return transformModule.packageRequirements ?? [];
|
|
24
|
-
} catch (error) {
|
|
25
|
-
console.debug('Could not read package requirements from transform:', error);
|
|
26
|
-
return [];
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
14
|
async function runCodemod(transformsDir?: string) {
|
|
31
15
|
try {
|
|
32
16
|
const resolvedTransformsDir =
|
|
@@ -45,55 +29,10 @@ async function runCodemod(transformsDir?: string) {
|
|
|
45
29
|
const codemodPath = path.resolve(resolvedTransformsDir, `${options.transformFile}.js`);
|
|
46
30
|
console.debug(`Resolved codemod path: ${codemodPath}`);
|
|
47
31
|
|
|
48
|
-
// Check package requirements from the transform
|
|
49
|
-
const packageRequirements = await getPackageRequirements(codemodPath);
|
|
50
|
-
const packageResults: Record<string, boolean> = {};
|
|
51
|
-
|
|
52
|
-
if (packageRequirements.length > 0) {
|
|
53
|
-
console.log('📦 Checking package requirements...');
|
|
54
|
-
|
|
55
|
-
for (const pkg of packageRequirements) {
|
|
56
|
-
console.log(`🔍 Checking for ${pkg.name}@${pkg.version} in ${options.targetPath}...`);
|
|
57
|
-
|
|
58
|
-
// Pass the monorepo flag to the package checker
|
|
59
|
-
const found = hasPackageVersionFromPath(
|
|
60
|
-
pkg.name,
|
|
61
|
-
pkg.version,
|
|
62
|
-
options.targetPath,
|
|
63
|
-
options.isMonorepo,
|
|
64
|
-
);
|
|
65
|
-
packageResults[`${pkg.name}@${pkg.version}`] = found;
|
|
66
|
-
|
|
67
|
-
if (found) {
|
|
68
|
-
console.log(`✅ Found ${pkg.name}@${pkg.version}`);
|
|
69
|
-
} else {
|
|
70
|
-
console.warn(
|
|
71
|
-
`❌ Package ${pkg.name}@${pkg.version} not found in target. Transform may be skipped.`,
|
|
72
|
-
);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
let gitignoreArg = '';
|
|
78
|
-
if (options.gitignore) {
|
|
79
|
-
const closestGitignore = findClosestGitignore(options.targetPath);
|
|
80
|
-
if (closestGitignore) {
|
|
81
|
-
console.debug(`Found .gitignore at: ${closestGitignore}`);
|
|
82
|
-
gitignoreArg = `--ignore-config=${closestGitignore}`;
|
|
83
|
-
} else {
|
|
84
|
-
console.debug('No .gitignore found in directory tree, skipping gitignore option');
|
|
85
|
-
gitignoreArg = '';
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
32
|
const args = [
|
|
90
33
|
'-t',
|
|
91
34
|
codemodPath,
|
|
92
35
|
options.targetPath,
|
|
93
|
-
'--extensions=tsx,ts,jsx,js',
|
|
94
|
-
'--ignore-pattern=**/node_modules/**',
|
|
95
|
-
'--ignore-pattern=**/dist/**',
|
|
96
|
-
'--ignore-pattern=**/*.d.ts',
|
|
97
36
|
options.dry ? '--dry' : '',
|
|
98
37
|
options.print ? '--print' : '',
|
|
99
38
|
options.ignorePattern
|
|
@@ -102,10 +41,7 @@ async function runCodemod(transformsDir?: string) {
|
|
|
102
41
|
.map((pattern) => `--ignore-pattern=${pattern.trim()}`)
|
|
103
42
|
.join(' ')
|
|
104
43
|
: '',
|
|
105
|
-
|
|
106
|
-
Object.keys(packageResults).length > 0
|
|
107
|
-
? `--packageResults='${JSON.stringify(packageResults)}'`
|
|
108
|
-
: '',
|
|
44
|
+
options.gitignore ? '--gitignore' : '',
|
|
109
45
|
].filter(Boolean);
|
|
110
46
|
|
|
111
47
|
const command = `npx jscodeshift ${args.join(' ')}`;
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
|
|
3
|
-
import createTestTransform
|
|
4
|
-
createTestTransformWithoutPackage,
|
|
5
|
-
} from '../../helpers/createTestTransform';
|
|
3
|
+
import createTestTransform from '../../helpers/createTestTransform';
|
|
6
4
|
import transformer from '../button';
|
|
7
5
|
|
|
8
6
|
jest.mock(
|
|
@@ -14,10 +12,7 @@ jest.mock(
|
|
|
14
12
|
);
|
|
15
13
|
|
|
16
14
|
describe('button codemod', () => {
|
|
17
|
-
const transform = createTestTransform(transformer
|
|
18
|
-
{ name: '@transferwise/components', version: '>=46.5.0' },
|
|
19
|
-
]);
|
|
20
|
-
|
|
15
|
+
const transform = createTestTransform(transformer);
|
|
21
16
|
it('renames ActionButton to Button and adds v2 and size', () => {
|
|
22
17
|
const input = `
|
|
23
18
|
import { ActionButton } from '@transferwise/components';
|
|
@@ -38,7 +33,7 @@ describe('button codemod', () => {
|
|
|
38
33
|
}
|
|
39
34
|
`;
|
|
40
35
|
const output = transform({ source: input });
|
|
41
|
-
expect(output).toContain('v2');
|
|
36
|
+
expect(output).toContain('<Button v2 type="button">Click</Button>');
|
|
42
37
|
});
|
|
43
38
|
|
|
44
39
|
it('removes legacy props and adds new ones', () => {
|
|
@@ -155,21 +150,4 @@ describe('button codemod', () => {
|
|
|
155
150
|
'Manual review required: prop "size" on <Button> has unsupported value "dynamicSize".',
|
|
156
151
|
);
|
|
157
152
|
});
|
|
158
|
-
|
|
159
|
-
it('skips transformation when package is not found', () => {
|
|
160
|
-
const noPackageTransform = createTestTransformWithoutPackage(
|
|
161
|
-
transformer,
|
|
162
|
-
'@transferwise/components',
|
|
163
|
-
'>=46.5.0',
|
|
164
|
-
);
|
|
165
|
-
|
|
166
|
-
const input = `
|
|
167
|
-
import { Button } from '@transferwise/components';
|
|
168
|
-
export default function Test() {
|
|
169
|
-
return <Button htmlType="button">Click</Button>;
|
|
170
|
-
}
|
|
171
|
-
`;
|
|
172
|
-
const output = noPackageTransform({ source: input });
|
|
173
|
-
expect(output.trim()).toBe(input.trim());
|
|
174
|
-
});
|
|
175
153
|
});
|
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
import type { API, FileInfo, JSCodeshift, JSXIdentifier, Options } from 'jscodeshift';
|
|
2
2
|
|
|
3
3
|
import reportManualReview from '../../utils/reportManualReview';
|
|
4
|
+
import hasImport from '../helpers/hasImport';
|
|
5
|
+
import processIconChildren from '../helpers/iconUtils';
|
|
4
6
|
import {
|
|
5
7
|
addAttributesIfMissing,
|
|
6
|
-
createReporter,
|
|
7
8
|
hasAttributeOnElement,
|
|
8
|
-
hasImport,
|
|
9
|
-
processIconChildren,
|
|
10
9
|
setNameIfJSXIdentifier,
|
|
11
|
-
|
|
12
|
-
} from '../helpers';
|
|
10
|
+
} from '../helpers/jsxElementUtils';
|
|
11
|
+
import { createReporter } from '../helpers/jsxReportingUtils';
|
|
13
12
|
|
|
14
13
|
export const parser = 'tsx';
|
|
15
|
-
export const packageRequirements = [{ name: '@transferwise/components', version: '>=46.5.0' }];
|
|
16
14
|
|
|
17
15
|
interface LegacyProps {
|
|
18
16
|
priority?: string;
|
|
@@ -84,16 +82,12 @@ const resolveType = (type?: string, htmlType?: string): string | null => {
|
|
|
84
82
|
'secondary',
|
|
85
83
|
'danger',
|
|
86
84
|
'link',
|
|
87
|
-
'button',
|
|
88
|
-
'reset',
|
|
89
|
-
'submit',
|
|
90
85
|
];
|
|
91
86
|
return type && legacyButtonTypes.includes(type) ? type : null;
|
|
92
87
|
};
|
|
93
88
|
|
|
94
|
-
const convertEnumValue = (value?: string):
|
|
95
|
-
if (!value) return
|
|
96
|
-
|
|
89
|
+
const convertEnumValue = (value?: string): string | undefined => {
|
|
90
|
+
if (!value) return value;
|
|
97
91
|
const strippedValue = value.replace(/^['"]|['"]$/gu, '');
|
|
98
92
|
const enumMapping: Record<string, string> = {
|
|
99
93
|
'Priority.SECONDARY': 'secondary',
|
|
@@ -102,18 +96,8 @@ const convertEnumValue = (value?: string): { converted: string | undefined; wasE
|
|
|
102
96
|
'ControlType.NEGATIVE': 'negative',
|
|
103
97
|
'ControlType.POSITIVE': 'positive',
|
|
104
98
|
'ControlType.ACCENT': 'accent',
|
|
105
|
-
'ControlType.BUTTON': 'button',
|
|
106
|
-
'ControlType.RESET': 'reset',
|
|
107
|
-
'HtmlType.SUBMIT': 'submit',
|
|
108
|
-
'HtmlType.BUTTON': 'button',
|
|
109
|
-
'HtmlType.RESET': 'reset',
|
|
110
|
-
'Sentiment.NEGATIVE': 'negative',
|
|
111
99
|
};
|
|
112
|
-
|
|
113
|
-
const wasEnum = strippedValue in enumMapping;
|
|
114
|
-
const converted = enumMapping[strippedValue] || strippedValue;
|
|
115
|
-
|
|
116
|
-
return { converted, wasEnum };
|
|
100
|
+
return enumMapping[strippedValue] || strippedValue;
|
|
117
101
|
};
|
|
118
102
|
|
|
119
103
|
/**
|
|
@@ -127,10 +111,6 @@ const convertEnumValue = (value?: string): { converted: string | undefined; wasE
|
|
|
127
111
|
* @returns {string} - The transformed source code.
|
|
128
112
|
*/
|
|
129
113
|
const transformer = (file: FileInfo, api: API, options: Options) => {
|
|
130
|
-
if (!validatePackageRequirements(options, packageRequirements)) {
|
|
131
|
-
return file.source;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
114
|
const j: JSCodeshift = api.jscodeshift;
|
|
135
115
|
const root = j(file.source);
|
|
136
116
|
const manualReviewIssues: string[] = [];
|
|
@@ -241,163 +221,144 @@ const transformer = (file: FileInfo, api: API, options: Options) => {
|
|
|
241
221
|
addAttributesIfMissing(j, openingElement, [
|
|
242
222
|
{ attribute: j.jsxAttribute(j.jsxIdentifier('v2')), name: 'v2' },
|
|
243
223
|
]);
|
|
244
|
-
|
|
245
224
|
processIconChildren(j, path.node.children, iconImports, openingElement);
|
|
246
225
|
|
|
247
226
|
const legacyProps: LegacyProps = {};
|
|
248
227
|
const legacyPropNames = ['priority', 'size', 'type', 'htmlType', 'sentiment'];
|
|
249
|
-
const keepAttributes: typeof openingElement.attributes = [];
|
|
250
228
|
|
|
251
|
-
|
|
252
|
-
if (
|
|
253
|
-
attr.type === 'JSXAttribute' &&
|
|
254
|
-
attr.name &&
|
|
255
|
-
attr.name.type === 'JSXIdentifier' &&
|
|
256
|
-
legacyPropNames.includes(attr.name.name)
|
|
257
|
-
) {
|
|
229
|
+
openingElement.attributes?.forEach((attr) => {
|
|
230
|
+
if (attr.type === 'JSXAttribute' && attr.name && attr.name.type === 'JSXIdentifier') {
|
|
258
231
|
const { name } = attr.name;
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
} else
|
|
267
|
-
|
|
268
|
-
const { converted } = convertEnumValue(rawStringValue);
|
|
269
|
-
legacyProps[name] = converted;
|
|
270
|
-
}
|
|
271
|
-
} else {
|
|
272
|
-
legacyProps[name] = undefined;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
// Check if this is an enum value that should be removed
|
|
276
|
-
const { wasEnum } = convertEnumValue(rawStringValue);
|
|
277
|
-
if (rawStringValue && wasEnum) {
|
|
278
|
-
migrated = true; // Remove enum values
|
|
279
|
-
} else if (name === 'size') {
|
|
280
|
-
const rawValue = legacyProps.size;
|
|
281
|
-
const resolved = resolveSize(rawValue);
|
|
282
|
-
const supportedSizes = ['xs', 'sm', 'md', 'lg', 'xl'];
|
|
283
|
-
if (
|
|
284
|
-
typeof rawValue === 'string' &&
|
|
285
|
-
typeof resolved === 'string' &&
|
|
286
|
-
supportedSizes.includes(resolved)
|
|
287
|
-
) {
|
|
288
|
-
migrated = true;
|
|
289
|
-
} else if (typeof rawValue === 'string') {
|
|
290
|
-
reporter.reportUnsupportedValue(path, 'size', rawValue);
|
|
291
|
-
} else if (rawValue !== undefined) {
|
|
292
|
-
reporter.reportAmbiguousExpression(path, 'size');
|
|
293
|
-
}
|
|
294
|
-
} else if (name === 'priority') {
|
|
295
|
-
const rawValue = legacyProps.priority;
|
|
296
|
-
const { converted } = convertEnumValue(rawValue);
|
|
297
|
-
const mapped = resolvePriority(legacyProps.type, converted);
|
|
298
|
-
const supportedPriorities = ['primary', 'secondary', 'tertiary', 'secondary-neutral'];
|
|
299
|
-
if (
|
|
300
|
-
typeof rawValue === 'string' &&
|
|
301
|
-
typeof mapped === 'string' &&
|
|
302
|
-
supportedPriorities.includes(mapped)
|
|
303
|
-
) {
|
|
304
|
-
migrated = true;
|
|
305
|
-
} else if (typeof rawValue === 'string') {
|
|
306
|
-
reporter.reportUnsupportedValue(path, 'priority', rawValue);
|
|
307
|
-
} else if (rawValue !== undefined) {
|
|
308
|
-
reporter.reportAmbiguousExpression(path, 'priority');
|
|
309
|
-
}
|
|
310
|
-
} else if (name === 'type' || name === 'htmlType') {
|
|
311
|
-
const rawType = legacyProps.type;
|
|
312
|
-
const rawHtmlType = legacyProps.htmlType;
|
|
313
|
-
const resolvedType =
|
|
314
|
-
typeof rawType === 'string'
|
|
315
|
-
? rawType
|
|
316
|
-
: rawType && typeof rawType === 'object'
|
|
317
|
-
? convertEnumValue(j(rawType).toSource()).converted
|
|
318
|
-
: undefined;
|
|
319
|
-
const resolved = resolveType(resolvedType, rawHtmlType);
|
|
320
|
-
const supportedTypes = ['button', 'reset', 'submit'];
|
|
321
|
-
if (typeof resolved === 'string' && supportedTypes.includes(resolved)) {
|
|
322
|
-
migrated = true;
|
|
323
|
-
} else if (typeof rawType === 'string' || typeof rawHtmlType === 'string') {
|
|
324
|
-
reporter.reportUnsupportedValue(path, 'type', rawType ?? rawHtmlType ?? '');
|
|
325
|
-
} else if (rawType !== undefined || rawHtmlType !== undefined) {
|
|
326
|
-
reporter.reportAmbiguousExpression(path, 'type');
|
|
327
|
-
}
|
|
328
|
-
} else if (name === 'sentiment') {
|
|
329
|
-
const rawValue = legacyProps.sentiment;
|
|
330
|
-
if (rawValue === 'negative') {
|
|
331
|
-
migrated = true;
|
|
332
|
-
} else if (typeof rawValue === 'string') {
|
|
333
|
-
reporter.reportUnsupportedValue(path, 'sentiment', rawValue);
|
|
334
|
-
} else if (rawValue !== undefined) {
|
|
335
|
-
reporter.reportAmbiguousExpression(path, 'sentiment');
|
|
232
|
+
if (legacyPropNames.includes(name)) {
|
|
233
|
+
if (attr.value) {
|
|
234
|
+
if (attr.value.type === 'StringLiteral') {
|
|
235
|
+
legacyProps[name] = attr.value.value;
|
|
236
|
+
} else if (attr.value.type === 'JSXExpressionContainer') {
|
|
237
|
+
legacyProps[name] = convertEnumValue(String(j(attr.value.expression).toSource()));
|
|
238
|
+
}
|
|
239
|
+
} else {
|
|
240
|
+
legacyProps[name] = undefined;
|
|
336
241
|
}
|
|
337
242
|
}
|
|
338
|
-
|
|
339
|
-
if (!migrated) keepAttributes.push(attr);
|
|
340
|
-
} else {
|
|
341
|
-
keepAttributes.push(attr);
|
|
342
243
|
}
|
|
343
244
|
});
|
|
344
245
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
246
|
+
if (openingElement.attributes) {
|
|
247
|
+
openingElement.attributes = openingElement.attributes.filter(
|
|
248
|
+
(attr) =>
|
|
249
|
+
!(
|
|
250
|
+
attr.type === 'JSXAttribute' &&
|
|
251
|
+
attr.name &&
|
|
252
|
+
legacyPropNames.includes((attr.name as JSXIdentifier).name)
|
|
253
|
+
),
|
|
254
|
+
);
|
|
354
255
|
}
|
|
355
256
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
typeof rawPriority === 'string' &&
|
|
361
|
-
typeof mappedPriority === 'string' &&
|
|
362
|
-
['primary', 'secondary', 'tertiary', 'secondary-neutral'].includes(mappedPriority)
|
|
363
|
-
) {
|
|
364
|
-
newAttributes.push(j.jsxAttribute(j.jsxIdentifier('priority'), j.literal(mappedPriority)));
|
|
365
|
-
}
|
|
257
|
+
if ('size' in legacyProps) {
|
|
258
|
+
const rawValue = legacyProps.size;
|
|
259
|
+
const resolved = resolveSize(rawValue);
|
|
260
|
+
const supportedSizes = ['xs', 'sm', 'md', 'lg', 'xl'];
|
|
366
261
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
newAttributes.push(j.jsxAttribute(j.jsxIdentifier('type'), j.literal(resolved)));
|
|
262
|
+
if (
|
|
263
|
+
typeof rawValue === 'string' &&
|
|
264
|
+
typeof resolved === 'string' &&
|
|
265
|
+
supportedSizes.includes(resolved)
|
|
266
|
+
) {
|
|
267
|
+
openingElement.attributes?.push(
|
|
268
|
+
j.jsxAttribute(j.jsxIdentifier('size'), j.literal(resolved)),
|
|
269
|
+
);
|
|
270
|
+
} else if (typeof rawValue === 'string') {
|
|
271
|
+
reporter.reportUnsupportedValue(path, 'size', rawValue);
|
|
272
|
+
} else if (rawValue !== undefined) {
|
|
273
|
+
reporter.reportAmbiguousExpression(path, 'size');
|
|
380
274
|
}
|
|
381
|
-
|
|
382
|
-
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if ('priority' in legacyProps) {
|
|
278
|
+
const rawValue = legacyProps.priority;
|
|
279
|
+
const converted = convertEnumValue(rawValue);
|
|
280
|
+
const mapped = resolvePriority(legacyProps.type, converted);
|
|
281
|
+
const supportedPriorities = ['primary', 'secondary', 'tertiary', 'secondary-neutral'];
|
|
282
|
+
|
|
283
|
+
if (
|
|
284
|
+
typeof rawValue === 'string' &&
|
|
285
|
+
typeof mapped === 'string' &&
|
|
286
|
+
supportedPriorities.includes(mapped)
|
|
287
|
+
) {
|
|
288
|
+
openingElement.attributes?.push(
|
|
289
|
+
j.jsxAttribute(j.jsxIdentifier('priority'), j.literal(mapped)),
|
|
290
|
+
);
|
|
291
|
+
} else if (typeof rawValue === 'string') {
|
|
292
|
+
reporter.reportUnsupportedValue(path, 'priority', rawValue);
|
|
293
|
+
} else if (rawValue !== undefined) {
|
|
294
|
+
reporter.reportAmbiguousExpression(path, 'priority');
|
|
383
295
|
}
|
|
384
296
|
}
|
|
385
297
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
298
|
+
if ('type' in legacyProps || 'htmlType' in legacyProps) {
|
|
299
|
+
const rawType = legacyProps.type;
|
|
300
|
+
const rawHtmlType = legacyProps.htmlType;
|
|
301
|
+
|
|
302
|
+
const resolvedType =
|
|
303
|
+
typeof rawType === 'string'
|
|
304
|
+
? rawType
|
|
305
|
+
: rawType && typeof rawType === 'object'
|
|
306
|
+
? convertEnumValue(j(rawType).toSource())
|
|
307
|
+
: undefined;
|
|
308
|
+
|
|
309
|
+
const resolved = resolveType(resolvedType, rawHtmlType);
|
|
310
|
+
|
|
311
|
+
const supportedTypes = [
|
|
312
|
+
'accent',
|
|
313
|
+
'negative',
|
|
314
|
+
'positive',
|
|
315
|
+
'primary',
|
|
316
|
+
'pay',
|
|
317
|
+
'secondary',
|
|
318
|
+
'danger',
|
|
319
|
+
'link',
|
|
320
|
+
'submit',
|
|
321
|
+
'button',
|
|
322
|
+
'reset',
|
|
323
|
+
];
|
|
324
|
+
|
|
325
|
+
if (typeof resolved === 'string' && supportedTypes.includes(resolved)) {
|
|
326
|
+
openingElement.attributes?.push(
|
|
327
|
+
j.jsxAttribute(j.jsxIdentifier('type'), j.literal(resolved)),
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
if (resolved === 'negative') {
|
|
331
|
+
openingElement.attributes?.push(
|
|
332
|
+
j.jsxAttribute(j.jsxIdentifier('sentiment'), j.literal('negative')),
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
} else if (typeof rawType === 'string' || typeof rawHtmlType === 'string') {
|
|
336
|
+
reporter.reportUnsupportedValue(path, 'type', rawType ?? rawHtmlType ?? '');
|
|
337
|
+
} else if (rawType !== undefined || rawHtmlType !== undefined) {
|
|
338
|
+
reporter.reportAmbiguousExpression(path, 'type');
|
|
339
|
+
}
|
|
389
340
|
}
|
|
390
341
|
|
|
391
|
-
|
|
342
|
+
if ('sentiment' in legacyProps) {
|
|
343
|
+
const rawValue = legacyProps.sentiment;
|
|
344
|
+
if (rawValue === 'negative') {
|
|
345
|
+
openingElement.attributes?.push(
|
|
346
|
+
j.jsxAttribute(j.jsxIdentifier('sentiment'), j.literal('negative')),
|
|
347
|
+
);
|
|
348
|
+
} else if (typeof rawValue === 'string') {
|
|
349
|
+
reporter.reportUnsupportedValue(path, 'sentiment', rawValue);
|
|
350
|
+
} else if (rawValue !== undefined) {
|
|
351
|
+
reporter.reportAmbiguousExpression(path, 'sentiment');
|
|
352
|
+
}
|
|
353
|
+
}
|
|
392
354
|
|
|
393
|
-
// as/href logic as before
|
|
394
355
|
let asIndex = -1;
|
|
395
356
|
let asValue: string | null = null;
|
|
396
357
|
let hrefExists = false;
|
|
397
358
|
let asAmbiguous = false;
|
|
398
359
|
let hrefAmbiguous = false;
|
|
399
360
|
|
|
400
|
-
|
|
361
|
+
openingElement.attributes?.forEach((attr, index) => {
|
|
401
362
|
if (attr.type === 'JSXAttribute' && attr.name) {
|
|
402
363
|
if (attr.name.name === 'as') {
|
|
403
364
|
if (attr.value) {
|
|
@@ -410,6 +371,7 @@ const transformer = (file: FileInfo, api: API, options: Options) => {
|
|
|
410
371
|
}
|
|
411
372
|
asIndex = index;
|
|
412
373
|
}
|
|
374
|
+
|
|
413
375
|
if (attr.name.name === 'href') {
|
|
414
376
|
hrefExists = true;
|
|
415
377
|
if (attr.value && attr.value.type !== 'StringLiteral') {
|
|
@@ -426,15 +388,18 @@ const transformer = (file: FileInfo, api: API, options: Options) => {
|
|
|
426
388
|
|
|
427
389
|
if (asValue === 'a') {
|
|
428
390
|
if (asIndex !== -1) {
|
|
429
|
-
|
|
391
|
+
openingElement.attributes = openingElement.attributes?.filter(
|
|
392
|
+
(_, idx) => idx !== asIndex,
|
|
393
|
+
);
|
|
430
394
|
}
|
|
431
395
|
if (!hrefExists) {
|
|
432
|
-
|
|
396
|
+
openingElement.attributes = [
|
|
397
|
+
...(openingElement.attributes ?? []),
|
|
398
|
+
j.jsxAttribute(j.jsxIdentifier('href'), j.literal('#')),
|
|
399
|
+
];
|
|
433
400
|
}
|
|
434
401
|
}
|
|
435
402
|
|
|
436
|
-
openingElement.attributes = newAttributes;
|
|
437
|
-
|
|
438
403
|
if ((openingElement.attributes ?? []).some((attr) => attr.type === 'JSXSpreadAttribute')) {
|
|
439
404
|
reporter.reportSpreadProps(path);
|
|
440
405
|
}
|