@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
@@ -1,130 +0,0 @@
1
- import jscodeshift, { type JSXAttribute, type JSXElement, JSXIdentifier } from 'jscodeshift';
2
-
3
- import {
4
- addAttributesIfMissing,
5
- hasAttribute,
6
- hasAttributeOnElement,
7
- setNameIfJSXIdentifier,
8
- } from '../jsxElementUtils';
9
-
10
- describe('jsxElementUtils', () => {
11
- function getRoot(source: string) {
12
- return jscodeshift(source);
13
- }
14
-
15
- describe('setNameIfJSXIdentifier', () => {
16
- it('renames JSXIdentifier element name', () => {
17
- const source = `<OldName />`;
18
- const root = getRoot(source);
19
- const element = (root.findJSXElements('OldName').at(0).get() as { node: JSXElement }).node;
20
- const result = setNameIfJSXIdentifier(element.openingElement.name, 'NewName');
21
- expect(result).toMatchObject({ type: 'JSXIdentifier', name: 'NewName' });
22
- });
23
-
24
- it('returns the same element if not JSXIdentifier', () => {
25
- const source = `<A.B />`;
26
- const root = getRoot(source);
27
- // Find the first JSXElement (which will be <A.B />)
28
- const found = root.find(jscodeshift.JSXElement);
29
- if (found.length === 0) {
30
- throw new Error('No JSX elements found for A.B');
31
- }
32
- const element = (found.at(0).get() as { node: JSXElement }).node;
33
- const result = setNameIfJSXIdentifier(element.openingElement.name, 'NewName');
34
- expect(result).toEqual(element.openingElement.name);
35
- });
36
-
37
- it('returns undefined if elementName is undefined', () => {
38
- const result = setNameIfJSXIdentifier(undefined, 'NewName');
39
- expect(result).toBeUndefined();
40
- });
41
- });
42
-
43
- describe('hasAttribute', () => {
44
- it('returns true if attribute with given name exists', () => {
45
- const source = `<div foo bar />`;
46
- const root = getRoot(source);
47
- const element = root.findJSXElements('div').nodes()[0];
48
- expect(hasAttribute(element.openingElement.attributes, 'foo')).toBe(true);
49
- expect(hasAttribute(element.openingElement.attributes, 'bar')).toBe(true);
50
- });
51
-
52
- it('returns false if attribute with given name does not exist', () => {
53
- const source = `<div foo />`;
54
- const root = getRoot(source);
55
- const element = root.findJSXElements('div').nodes()[0];
56
- expect(hasAttribute(element.openingElement.attributes, 'baz')).toBe(false);
57
- });
58
-
59
- it('ignores JSXSpreadAttribute and returns false if no matching JSXAttribute', () => {
60
- const source = `<div {...props} />`;
61
- const root = getRoot(source);
62
- const element = root.findJSXElements('div').nodes()[0];
63
- expect(hasAttribute(element.openingElement.attributes, 'foo')).toBe(false);
64
- });
65
-
66
- it('returns false if attributes is undefined or not array', () => {
67
- expect(hasAttribute(undefined, 'foo')).toBe(false);
68
- expect(hasAttribute(null as never, 'foo')).toBe(false);
69
- expect(hasAttribute([], 'foo')).toBe(false);
70
- });
71
- });
72
-
73
- describe('hasAttributeOnElement', () => {
74
- it('returns true if openingElement has attribute', () => {
75
- const source = `<div foo />`;
76
- const root = getRoot(source);
77
- const element = root.findJSXElements('div').nodes()[0];
78
- expect(hasAttributeOnElement(element.openingElement, 'foo')).toBe(true);
79
- });
80
-
81
- it('returns false if openingElement does not have attribute', () => {
82
- const source = `<div bar />`;
83
- const root = getRoot(source);
84
- const element = root.findJSXElements('div').nodes()[0];
85
- expect(hasAttributeOnElement(element.openingElement, 'foo')).toBe(false);
86
- });
87
- });
88
-
89
- describe('addAttributesIfMissing', () => {
90
- it('adds attributes if missing', () => {
91
- const source = `<div foo />`;
92
- const root = getRoot(source);
93
- const element = root.findJSXElements('div').nodes()[0];
94
- const j = jscodeshift;
95
- const newAttr = j.jsxAttribute(j.jsxIdentifier('bar'));
96
- addAttributesIfMissing(j, element.openingElement, [{ attribute: newAttr, name: 'bar' }]);
97
- expect(hasAttributeOnElement(element.openingElement, 'bar')).toBe(true);
98
- });
99
-
100
- it('does not add attribute if already present', () => {
101
- const source = `<div foo />`;
102
- const root = getRoot(source);
103
- const element = root.findJSXElements('div').nodes()[0];
104
- const j = jscodeshift;
105
- const existingAttr = element.openingElement.attributes?.find(
106
- (attr) =>
107
- attr.type === 'JSXAttribute' &&
108
- attr.name.type === 'JSXIdentifier' &&
109
- attr.name.name === 'foo',
110
- ) as JSXAttribute;
111
- addAttributesIfMissing(j, element.openingElement, [{ attribute: existingAttr, name: 'foo' }]);
112
- // attributes length should remain 1
113
- expect(
114
- element.openingElement.attributes?.filter((attr) => attr.type === 'JSXAttribute').length,
115
- ).toBe(1);
116
- });
117
-
118
- it('does nothing if openingElement.attributes is not an array', () => {
119
- const source = `<div />`;
120
- const root = getRoot(source);
121
- const element = root.findJSXElements('div').nodes()[0];
122
- const j = jscodeshift;
123
- element.openingElement.attributes = null as never;
124
- const newAttr = j.jsxAttribute(j.jsxIdentifier('bar'));
125
- expect(() =>
126
- addAttributesIfMissing(j, element.openingElement, [{ attribute: newAttr, name: 'bar' }]),
127
- ).not.toThrow();
128
- });
129
- });
130
- });
@@ -1,265 +0,0 @@
1
- import type { JSCodeshift, JSXAttribute, JSXElement } from 'jscodeshift';
2
-
3
- import { CodemodReporter, createReporter } from '../jsxReportingUtils';
4
-
5
- describe('CodemodReporter', () => {
6
- let issues: string[];
7
- let reporter: CodemodReporter;
8
- let mockJ: JSCodeshift;
9
-
10
- beforeEach(() => {
11
- issues = [];
12
-
13
- // Create a proper JSCodeshift mock
14
- mockJ = Object.assign(
15
- jest.fn(() => ({
16
- find: jest.fn(),
17
- toSource: jest.fn(() => 'mockedSource'),
18
- })),
19
- {
20
- toSource: jest.fn(() => 'mockedSource'),
21
- types: {},
22
- match: jest.fn(),
23
- template: jest.fn(),
24
- registerMethods: jest.fn(),
25
- },
26
- ) as unknown as JSCodeshift;
27
-
28
- reporter = createReporter(mockJ, issues);
29
- });
30
-
31
- // Helper function to create properly typed mock elements
32
- const createMockElement = (componentName: string, line = 42): JSXElement =>
33
- ({
34
- type: 'JSXElement',
35
- openingElement: {
36
- type: 'JSXOpeningElement',
37
- name: { type: 'JSXIdentifier', name: componentName },
38
- attributes: [],
39
- selfClosing: false,
40
- },
41
- closingElement: null,
42
- children: [],
43
- loc: { start: { line, column: 0 }, end: { line, column: 0 } },
44
- }) as unknown as JSXElement;
45
-
46
- describe('createReporter', () => {
47
- it('should create a reporter instance', () => {
48
- expect(reporter).toBeInstanceOf(CodemodReporter);
49
- });
50
- });
51
-
52
- describe('reportElement', () => {
53
- it('should report element issues', () => {
54
- const mockElement = createMockElement('Button', 42);
55
-
56
- reporter.reportElement(mockElement, 'has some issue');
57
- expect(issues).toHaveLength(1);
58
- expect(issues[0]).toBe('Manual review required: <Button> at line 42 has some issue.');
59
- });
60
- });
61
-
62
- describe('reportProp', () => {
63
- it('should report prop issues', () => {
64
- const mockElement = createMockElement('Button', 42);
65
-
66
- reporter.reportProp(mockElement, 'size', 'has invalid value');
67
- expect(issues).toHaveLength(1);
68
- expect(issues[0]).toBe(
69
- 'Manual review required: prop "size" on <Button> at line 42 has invalid value.',
70
- );
71
- });
72
- });
73
-
74
- describe('reportAttribute', () => {
75
- it('should report attribute issues with default reason', () => {
76
- const mockElement = createMockElement('Button', 42);
77
-
78
- const mockAttr = {
79
- type: 'JSXAttribute',
80
- name: { type: 'JSXIdentifier', name: 'size' },
81
- value: {
82
- type: 'JSXExpressionContainer',
83
- expression: { type: 'Identifier', name: 'someVar' },
84
- },
85
- loc: { start: { line: 42, column: 0 }, end: { line: 42, column: 0 } },
86
- } as unknown as JSXAttribute;
87
-
88
- reporter.reportAttribute(mockAttr, mockElement);
89
- expect(issues).toHaveLength(1);
90
- expect(issues[0]).toContain('prop "size" on <Button> at line 42');
91
- });
92
-
93
- it('should report attribute issues with custom reason', () => {
94
- const mockElement = createMockElement('Button', 42);
95
-
96
- const mockAttr = {
97
- type: 'JSXAttribute',
98
- name: { type: 'JSXIdentifier', name: 'size' },
99
- value: { type: 'StringLiteral', value: 'large' },
100
- loc: { start: { line: 42, column: 0 }, end: { line: 42, column: 0 } },
101
- } as unknown as JSXAttribute;
102
-
103
- reporter.reportAttribute(mockAttr, mockElement, 'custom reason');
104
- expect(issues).toHaveLength(1);
105
- expect(issues[0]).toBe(
106
- 'Manual review required: prop "size" on <Button> at line 42 custom reason.',
107
- );
108
- });
109
- });
110
-
111
- describe('reportSpreadProps', () => {
112
- it('should report spread props issue', () => {
113
- const mockElement = createMockElement('Button', 42);
114
-
115
- reporter.reportSpreadProps(mockElement);
116
- expect(issues).toHaveLength(1);
117
- expect(issues[0]).toBe(
118
- 'Manual review required: <Button> at line 42 contains spread props that need manual review.',
119
- );
120
- });
121
- });
122
-
123
- describe('reportPropWithChildren', () => {
124
- it('should report prop with children issue', () => {
125
- const mockElement = createMockElement('Button', 42);
126
-
127
- reporter.reportPropWithChildren(mockElement, 'text');
128
- expect(issues).toHaveLength(1);
129
- expect(issues[0]).toBe(
130
- 'Manual review required: prop "text" on <Button> at line 42 conflicts with children - both "text" prop and children are present.',
131
- );
132
- });
133
- });
134
-
135
- describe('reportUnsupportedValue', () => {
136
- it('should report unsupported value issue', () => {
137
- const mockElement = createMockElement('Button', 42);
138
-
139
- reporter.reportUnsupportedValue(mockElement, 'size', 'huge');
140
- expect(issues).toHaveLength(1);
141
- expect(issues[0]).toBe(
142
- 'Manual review required: prop "size" on <Button> at line 42 has unsupported value "huge".',
143
- );
144
- });
145
- });
146
-
147
- describe('reportAmbiguousExpression', () => {
148
- it('should report ambiguous expression issue', () => {
149
- const mockElement = createMockElement('Button', 42);
150
-
151
- reporter.reportAmbiguousExpression(mockElement, 'size');
152
- expect(issues).toHaveLength(1);
153
- expect(issues[0]).toBe(
154
- 'Manual review required: prop "size" on <Button> at line 42 contains a complex expression that needs manual review.',
155
- );
156
- });
157
- });
158
-
159
- describe('reportAmbiguousChildren', () => {
160
- it('should report ambiguous children issue', () => {
161
- const mockElement = createMockElement('Button', 42);
162
-
163
- reporter.reportAmbiguousChildren(mockElement, 'icon');
164
- expect(issues).toHaveLength(1);
165
- expect(issues[0]).toBe(
166
- 'Manual review required: <Button> at line 42 contains ambiguous icon that needs manual review.',
167
- );
168
- });
169
- });
170
-
171
- describe('reportDeprecatedProp', () => {
172
- it('should report deprecated prop without alternative', () => {
173
- const mockElement = createMockElement('Button', 42);
174
-
175
- reporter.reportDeprecatedProp(mockElement, 'flat');
176
- expect(issues).toHaveLength(1);
177
- expect(issues[0]).toBe(
178
- 'Manual review required: prop "flat" on <Button> at line 42 is deprecated.',
179
- );
180
- });
181
-
182
- it('should report deprecated prop with alternative', () => {
183
- const mockElement = createMockElement('Button', 42);
184
-
185
- reporter.reportDeprecatedProp(mockElement, 'flat', 'variant="text"');
186
- expect(issues).toHaveLength(1);
187
- expect(issues[0]).toBe(
188
- 'Manual review required: prop "flat" on <Button> at line 42 is deprecated Use variant="text" instead.',
189
- );
190
- });
191
- });
192
-
193
- describe('reportMissingRequiredProp', () => {
194
- it('should report missing required prop', () => {
195
- const mockElement = createMockElement('Button', 42);
196
-
197
- reporter.reportMissingRequiredProp(mockElement, 'children');
198
- expect(issues).toHaveLength(1);
199
- expect(issues[0]).toBe(
200
- 'Manual review required: prop "children" on <Button> at line 42 is required but missing.',
201
- );
202
- });
203
- });
204
-
205
- describe('reportConflictingProps', () => {
206
- it('should report conflicting props', () => {
207
- const mockElement = createMockElement('Button', 42);
208
-
209
- reporter.reportConflictingProps(mockElement, ['size', 'fullWidth']);
210
- expect(issues).toHaveLength(1);
211
- expect(issues[0]).toBe(
212
- 'Manual review required: <Button> at line 42 has conflicting props: "size", "fullWidth" cannot be used together.',
213
- );
214
- });
215
- });
216
-
217
- describe('reportAttributeIssues', () => {
218
- it('should not report anything with no attributes', () => {
219
- const mockElement = createMockElement('Button', 42);
220
-
221
- reporter.reportAttributeIssues(mockElement);
222
- expect(issues).toHaveLength(0);
223
- });
224
-
225
- it('should report spread props', () => {
226
- const mockElement = {
227
- ...createMockElement('Button', 42),
228
- openingElement: {
229
- ...createMockElement('Button', 42).openingElement,
230
- attributes: [
231
- { type: 'JSXSpreadAttribute', argument: { type: 'Identifier', name: 'props' } },
232
- ],
233
- },
234
- } as unknown as JSXElement;
235
-
236
- reporter.reportAttributeIssues(mockElement);
237
- expect(issues).toHaveLength(1);
238
- expect(issues[0]).toContain('spread props');
239
- });
240
-
241
- it('should report expression container attributes', () => {
242
- const mockElement = {
243
- ...createMockElement('Button', 42),
244
- openingElement: {
245
- ...createMockElement('Button', 42).openingElement,
246
- attributes: [
247
- {
248
- type: 'JSXAttribute',
249
- name: { type: 'JSXIdentifier', name: 'size' },
250
- value: {
251
- type: 'JSXExpressionContainer',
252
- expression: { type: 'Identifier', name: 'someVar' },
253
- },
254
- loc: { start: { line: 42, column: 0 }, end: { line: 42, column: 0 } },
255
- },
256
- ],
257
- },
258
- } as unknown as JSXElement;
259
-
260
- reporter.reportAttributeIssues(mockElement);
261
- expect(issues).toHaveLength(1);
262
- expect(issues[0]).toContain('prop "size" on <Button> at line 42');
263
- });
264
- });
265
- });
@@ -1,18 +0,0 @@
1
- import type { Options, Transform } from 'jscodeshift';
2
- import { applyTransform, type TestOptions } from 'jscodeshift/src/testUtils';
3
-
4
- /**
5
- * This function creates a test transform function that applies a given transformer to the input code.
6
- * It uses the 'tsx' parser to support both TypeScript and JSX syntax.
7
- *
8
- * The transform function is used to modify the input code based on the provided transformer.
9
- */
10
- function createTestTransform(transformer: Transform) {
11
- const options: Options = {};
12
- // Use 'tsx' parser to support both TypeScript and JSX syntax
13
- const testOptions: TestOptions = { parser: 'tsx' };
14
- return (input: { path?: string; source: string }) =>
15
- applyTransform(transformer, options, input, testOptions);
16
- }
17
-
18
- export default createTestTransform;
@@ -1,60 +0,0 @@
1
- import type { Collection, JSCodeshift } from 'jscodeshift';
2
-
3
- /**
4
- * Checks if a specific import exists in the given root collection and provides
5
- * a method to remove it if found.
6
- */
7
- function hasImport(
8
- root: Collection,
9
- sourceValue: string,
10
- importName: string,
11
- j: JSCodeshift,
12
- ): { exists: boolean; remove: () => void } {
13
- const importDeclarations = root.find(j.ImportDeclaration, {
14
- source: { value: sourceValue },
15
- });
16
-
17
- if (importDeclarations.size() === 0) {
18
- return {
19
- exists: false,
20
- remove: () => {},
21
- };
22
- }
23
-
24
- const namedImport = importDeclarations.find(j.ImportSpecifier, {
25
- imported: { name: importName },
26
- });
27
-
28
- const defaultImport = importDeclarations.find(j.ImportDefaultSpecifier, {
29
- local: { name: importName },
30
- });
31
-
32
- const exists = namedImport.size() > 0 || defaultImport.size() > 0;
33
-
34
- const remove = () => {
35
- importDeclarations.forEach((path) => {
36
- const filteredSpecifiers =
37
- path.node.specifiers?.filter((specifier) => {
38
- if (specifier.type === 'ImportSpecifier' && specifier.imported.name === importName) {
39
- return false;
40
- }
41
- if (specifier.type === 'ImportDefaultSpecifier' && specifier.local?.name === importName) {
42
- return false;
43
- }
44
- return true;
45
- }) ?? [];
46
-
47
- if (filteredSpecifiers.length === 0) {
48
- path.prune();
49
- } else {
50
- j(path).replaceWith(
51
- j.importDeclaration(filteredSpecifiers, path.node.source, path.node.importKind),
52
- );
53
- }
54
- });
55
- };
56
-
57
- return { exists, remove };
58
- }
59
-
60
- export default hasImport;
@@ -1,87 +0,0 @@
1
- import type { JSCodeshift, JSXElement, JSXExpressionContainer } from 'jscodeshift';
2
-
3
- /**
4
- * Process children of a JSX element to detect icon components and add iconStart or iconEnd attributes accordingly.
5
- * This is specific to icon handling but can be reused in codemods dealing with icon children.
6
- */
7
- const processIconChildren = (
8
- j: JSCodeshift,
9
- children: (JSXElement | JSXExpressionContainer | unknown)[] | undefined,
10
- iconImports: Set<string>,
11
- openingElement: JSXElement['openingElement'],
12
- ) => {
13
- if (!children || !openingElement.attributes) return;
14
-
15
- const unwrapJsxElement = (node: unknown): JSXElement | unknown => {
16
- if (
17
- typeof node === 'object' &&
18
- node !== null &&
19
- 'type' in node &&
20
- node.type === 'JSXExpressionContainer' &&
21
- j.JSXElement.check((node as JSXExpressionContainer).expression)
22
- ) {
23
- return (node as JSXExpressionContainer).expression;
24
- }
25
- return node;
26
- };
27
-
28
- const totalChildren = children.length;
29
-
30
- // Find index of icon child
31
- const iconChildIndex = children.findIndex((child) => {
32
- const unwrapped = unwrapJsxElement(child);
33
- return (
34
- j.JSXElement.check(unwrapped) &&
35
- unwrapped.openingElement.name.type === 'JSXIdentifier' &&
36
- iconImports.has(unwrapped.openingElement.name.name)
37
- );
38
- });
39
-
40
- if (iconChildIndex === -1) return;
41
-
42
- const iconChild = unwrapJsxElement(children[iconChildIndex]) as JSXElement;
43
-
44
- if (!iconChild || iconChild.openingElement.name.type !== 'JSXIdentifier') return;
45
-
46
- const iconName = iconChild.openingElement.name.name;
47
-
48
- // Determine if icon is closer to start or end
49
- const distanceToStart = iconChildIndex;
50
- const distanceToEnd = totalChildren - 1 - iconChildIndex;
51
- const iconPropName = distanceToStart <= distanceToEnd ? 'addonStart' : 'addonEnd';
52
-
53
- // Build: { type: 'icon', value: <IconName /> }
54
- const iconObject = j.objectExpression([
55
- j.property('init', j.identifier('type'), j.literal('icon')),
56
- j.property('init', j.identifier('value'), iconChild),
57
- ]);
58
- const iconProp = j.jsxAttribute(
59
- j.jsxIdentifier(iconPropName),
60
- j.jsxExpressionContainer(iconObject),
61
- );
62
-
63
- openingElement.attributes.push(iconProp);
64
-
65
- // Remove the icon child
66
- children.splice(iconChildIndex, 1);
67
-
68
- // Helper to check if a child is whitespace-only JSXText
69
- const isWhitespaceJsxText = (node: unknown): boolean => {
70
- return (
71
- typeof node === 'object' &&
72
- node !== null &&
73
- (node as { type?: unknown }).type === 'JSXText' &&
74
- typeof (node as { value?: string }).value === 'string' &&
75
- (node as { value?: string }).value!.trim() === ''
76
- );
77
- };
78
-
79
- // Remove adjacent whitespace-only JSXText node if any
80
- if (iconChildIndex - 1 >= 0 && isWhitespaceJsxText(children[iconChildIndex - 1])) {
81
- children.splice(iconChildIndex - 1, 1);
82
- } else if (isWhitespaceJsxText(children[iconChildIndex])) {
83
- children.splice(iconChildIndex, 1);
84
- }
85
- };
86
-
87
- export default processIconChildren;
@@ -1,5 +0,0 @@
1
- export { default as createTestTransform } from './createTestTransform';
2
- export { default as hasImport } from './hasImport';
3
- export { default as processIconChildren } from './iconUtils';
4
- export * from './jsxElementUtils';
5
- export * from './jsxReportingUtils';
@@ -1,67 +0,0 @@
1
- import type {
2
- JSCodeshift,
3
- JSXAttribute,
4
- JSXElement,
5
- JSXIdentifier,
6
- JSXMemberExpression,
7
- JSXNamespacedName,
8
- JSXSpreadAttribute,
9
- } from 'jscodeshift';
10
-
11
- /**
12
- * Rename a JSX element name if it is a JSXIdentifier.
13
- */
14
- export const setNameIfJSXIdentifier = (
15
- elementName: JSXIdentifier | JSXNamespacedName | JSXMemberExpression | undefined,
16
- newName: string,
17
- ): JSXIdentifier | JSXNamespacedName | JSXMemberExpression | undefined => {
18
- if (elementName && elementName.type === 'JSXIdentifier') {
19
- return { ...elementName, name: newName };
20
- }
21
- return elementName;
22
- };
23
-
24
- /**
25
- * Check if a list of attributes contains a specific attribute by name.
26
- */
27
- export const hasAttribute = (
28
- attributes: (JSXAttribute | JSXSpreadAttribute)[] | undefined,
29
- attributeName: string,
30
- ): boolean => {
31
- return (
32
- Array.isArray(attributes) &&
33
- attributes.some(
34
- (attr): attr is JSXAttribute =>
35
- attr.type === 'JSXAttribute' &&
36
- attr.name.type === 'JSXIdentifier' &&
37
- attr.name.name === attributeName,
38
- )
39
- );
40
- };
41
-
42
- /**
43
- * Check if a JSX element's openingElement has a specific attribute.
44
- */
45
- export const hasAttributeOnElement = (
46
- element: JSXElement['openingElement'],
47
- attributeName: string,
48
- ): boolean => {
49
- return hasAttribute(element.attributes, attributeName);
50
- };
51
-
52
- /**
53
- * Add specified attributes to a JSX element's openingElement if they are not already present.
54
- */
55
- export const addAttributesIfMissing = (
56
- j: JSCodeshift,
57
- openingElement: JSXElement['openingElement'],
58
- attributesToAdd: { attribute: JSXAttribute; name: string }[],
59
- ) => {
60
- if (!Array.isArray(openingElement.attributes)) return;
61
- const attrs = openingElement.attributes;
62
- attributesToAdd.forEach(({ attribute, name }) => {
63
- if (!hasAttributeOnElement(openingElement, name)) {
64
- attrs.push(attribute);
65
- }
66
- });
67
- };