@atlaskit/popper 6.4.0 → 7.0.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # @atlaskit/popper
2
2
 
3
+ ## 7.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#117686](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/pull-requests/117686)
8
+ [`6b9372e48276d`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/6b9372e48276d) -
9
+ Remove old codemod and update dependencies.
10
+
11
+ ## 7.0.0
12
+
13
+ ### Major Changes
14
+
15
+ - [#117363](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/117363)
16
+ [`10a0f7f6c2027`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/10a0f7f6c2027) -
17
+ This package's `peerDependencies` have been adjusted for `react` and/or `react-dom` to reflect the
18
+ status of only supporting React 18 going forward. No explicit breaking change to React support has
19
+ been made in this release, but this is to signify going forward, breaking changes for React 16 or
20
+ React 17 may come via non-major semver releases.
21
+
22
+ Please refer this community post for more details:
23
+ https://community.developer.atlassian.com/t/rfc-78-dropping-support-for-react-16-and-rendering-in-a-react-18-concurrent-root-in-jira-and-confluence/87026
24
+
3
25
  ## 6.4.0
4
26
 
5
27
  ### Minor Changes
@@ -38,4 +38,4 @@ export interface CustomPopperProps<Modifiers> {
38
38
  */
39
39
  shouldFitViewport?: boolean;
40
40
  }
41
- export declare function Popper<CustomModifiers>({ children, offset, placement, referenceElement, modifiers, strategy, shouldFitViewport, }: CustomPopperProps<CustomModifiers>): JSX.Element;
41
+ export declare function Popper<CustomModifiers>({ children, offset, placement, referenceElement, modifiers, strategy, shouldFitViewport, }: CustomPopperProps<CustomModifiers>): React.JSX.Element;
@@ -41,4 +41,4 @@ export interface CustomPopperProps<Modifiers> {
41
41
  */
42
42
  shouldFitViewport?: boolean;
43
43
  }
44
- export declare function Popper<CustomModifiers>({ children, offset, placement, referenceElement, modifiers, strategy, shouldFitViewport, }: CustomPopperProps<CustomModifiers>): JSX.Element;
44
+ export declare function Popper<CustomModifiers>({ children, offset, placement, referenceElement, modifiers, strategy, shouldFitViewport, }: CustomPopperProps<CustomModifiers>): React.JSX.Element;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/popper",
3
- "version": "6.4.0",
3
+ "version": "7.0.1",
4
4
  "description": "A wrapper for React Popper for situations which require a bespoke popup where other ADS components are deemed unsuitable",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/"
@@ -30,22 +30,24 @@
30
30
  "react-popper": "^2.3.0"
31
31
  },
32
32
  "peerDependencies": {
33
- "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
33
+ "react": "^18.2.0"
34
34
  },
35
35
  "devDependencies": {
36
36
  "@af/accessibility-testing": "*",
37
37
  "@af/integration-testing": "*",
38
38
  "@af/visual-regression": "*",
39
- "@atlaskit/ds-lib": "^3.5.0",
39
+ "@atlaskit/button": "^21.1.0",
40
+ "@atlaskit/docs": "*",
41
+ "@atlaskit/ds-lib": "^4.0.0",
42
+ "@atlaskit/link": "*",
43
+ "@atlaskit/section-message": "*",
40
44
  "@atlaskit/ssr": "*",
41
- "@atlaskit/visual-regression": "*",
45
+ "@atlaskit/theme": "^17.0.0",
46
+ "@atlaskit/tokens": "^4.1.0",
42
47
  "@emotion/react": "^11.7.1",
43
48
  "@emotion/styled": "^11.0.0",
44
49
  "@testing-library/react": "^13.4.0",
45
50
  "@testing-library/user-event": "^14.4.3",
46
- "@types/jscodeshift": "^0.11.0",
47
- "ast-types": "^0.13.3",
48
- "jscodeshift": "^0.13.0",
49
51
  "react-dom": "^18.2.0",
50
52
  "react-lorem-component": "^0.13.0",
51
53
  "typescript": "~5.4.2"
@@ -1,152 +0,0 @@
1
- import {
2
- type API,
3
- type ASTPath,
4
- type default as core,
5
- type FileInfo,
6
- type JSXElement,
7
- JSXExpressionContainer,
8
- type Literal,
9
- type Options,
10
- StringLiteral,
11
- } from 'jscodeshift';
12
- import { type Collection } from 'jscodeshift/src/Collection';
13
-
14
- import {
15
- messageForModifierProps,
16
- messageForUsingExpression,
17
- messageForUsingVariable,
18
- } from './utils/constants';
19
- import {
20
- addCommentToStartOfFile,
21
- getJSXAttributesByName,
22
- getSpecifierName,
23
- hasImportDeclaration,
24
- isUsingProp,
25
- updateRenderProps,
26
- } from './utils/helpers';
27
-
28
- const updateOffsetNumbers = (value: string, j: core.JSCodeshift, attribute: ASTPath<any>) => {
29
- if (value.includes(',')) {
30
- // Split by comma
31
- const offsetArray: Literal[] = value
32
- .split(',')
33
- //@ts-ignore
34
- .map((elem) => j.literal(parseInt(elem.replace(/\D/g, ''))));
35
- if (offsetArray.length === 2) {
36
- j(attribute).replaceWith(j.jsxExpressionContainer(j.arrayExpression(offsetArray)));
37
- }
38
- } else {
39
- // Split by space but check if it is a single number
40
- const offsetArray: Literal[] = value
41
- .split(' ')
42
- .filter((elem) => elem.length)
43
- .map((elem) => j.literal(parseInt(elem.replace(/\D/g, ''))));
44
- if (offsetArray.length === 2) {
45
- j(attribute).replaceWith(j.jsxExpressionContainer(j.arrayExpression(offsetArray)));
46
- } else if (offsetArray.length === 1) {
47
- j(attribute).replaceWith(
48
- j.jsxExpressionContainer(j.arrayExpression([offsetArray[0], j.literal(0)])),
49
- );
50
- }
51
- }
52
- };
53
-
54
- const isJSExpression = (value: string) =>
55
- value.includes('%') || value.includes('vw') || value.includes('vh');
56
-
57
- function updateOffset(j: core.JSCodeshift, source: Collection<any>, specifier: string) {
58
- source.findJSXElements(specifier).forEach((path: ASTPath<JSXElement>) => {
59
- const offsetExpr = getJSXAttributesByName({
60
- j,
61
- element: path,
62
- attributeName: 'offset',
63
- });
64
-
65
- const stringLiteral = offsetExpr.filter((attr) => {
66
- return attr.value!.value!.type === 'StringLiteral';
67
- });
68
-
69
- const expression = offsetExpr.filter((attr) => {
70
- return attr.value!.value!.type === 'JSXExpressionContainer';
71
- });
72
-
73
- if (stringLiteral.length > 0) {
74
- stringLiteral.find(StringLiteral).forEach((attribute) => {
75
- const expression = attribute.value;
76
- updateOffsetNumbers(expression.value, j, attribute);
77
- });
78
- } else {
79
- expression.find(JSXExpressionContainer).forEach((attribute) => {
80
- const expression = attribute.value.expression;
81
- if (expression.type === 'StringLiteral') {
82
- const value = expression.value;
83
- // Not testing for cases like '10 + 10%' because I assume if you're
84
- // adding or taking numbers it's with units that are not supported
85
- // and will be picked up by the first case
86
- if (isJSExpression(value)) {
87
- addCommentToStartOfFile({
88
- j,
89
- base: source,
90
- message: messageForUsingExpression,
91
- });
92
- } else {
93
- updateOffsetNumbers(value, j, attribute);
94
- }
95
- } else if (expression.type === 'NumericLiteral') {
96
- // If it is a single number convert to [number, 0]
97
- j(attribute).replaceWith(
98
- j.jsxExpressionContainer(j.arrayExpression([expression, j.literal(0)])),
99
- );
100
- } else if (expression.type === 'Identifier') {
101
- // If there is a variable add this comment
102
- addCommentToStartOfFile({
103
- j,
104
- base: source,
105
- message: messageForUsingVariable,
106
- });
107
- }
108
- });
109
- }
110
- });
111
- }
112
-
113
- function updateModifierProp(j: core.JSCodeshift, source: Collection<any>, specifier: string) {
114
- source.findJSXElements(specifier).forEach((element) => {
115
- if (isUsingProp({ j, base: source, element, propName: 'modifiers' })) {
116
- addCommentToStartOfFile({
117
- j,
118
- base: source,
119
- message: messageForModifierProps,
120
- });
121
- }
122
- });
123
- }
124
-
125
- export default function transformer(file: FileInfo, { jscodeshift: j }: API, options: Options) {
126
- const source = j(file.source);
127
-
128
- // Exit early if not relevant
129
- // We are doing this so we don't touch the formatting of unrelated files
130
- if (!hasImportDeclaration(j, file.source, '@atlaskit/popper')) {
131
- return file.source;
132
- }
133
-
134
- const specifier = getSpecifierName({
135
- j,
136
- base: source,
137
- packageName: '@atlaskit/popper',
138
- component: 'Popper',
139
- });
140
-
141
- if (!specifier) {
142
- return file.source;
143
- }
144
-
145
- updateRenderProps(j, source, specifier, 'outOfBoundaries', 'isReferenceHidden');
146
- updateRenderProps(j, source, specifier, 'scheduleUpdate', 'update');
147
- updateOffset(j, source, specifier);
148
- updateModifierProp(j, source, specifier);
149
-
150
- // const base: Collection<any> = j(file.source);
151
- return source.toSource(options.printOptions || { quote: 'single' });
152
- }
@@ -1,72 +0,0 @@
1
- jest.autoMockOff();
2
-
3
- import transformer from '../5.0.0-lite-mode';
4
-
5
- const defineInlineTest = require('jscodeshift/dist/testUtils').defineInlineTest;
6
-
7
- describe('Changing popper usage', () => {
8
- // TODO this probably isn't worth including, as they still have to do some major work to
9
- // re-work these modifiers.
10
-
11
- /**
12
- * Modifiers:
13
- * - format has been changed from object of objects, to array of objects, with the key for each
14
- * modifier replaced with a name key:value pair inside the modifier object
15
- * - modifier-specific options have been moved inside an options: key:value pair
16
- * - modifier options have been changed significantly: check the popper.js docs for more info
17
- */
18
- defineInlineTest(
19
- { default: transformer, parser: 'tsx' },
20
- {},
21
- `
22
- import { Popper } from '@atlaskit/popper';
23
-
24
- export default () => {
25
- customModifiers = {flip: {enabled: true}};
26
- return (
27
- <Popper modifiers={customModifiers}>
28
- {({
29
- ref,
30
- style,
31
- outOfBoundaries,
32
- scheduleUpdate,
33
- }) => (
34
- <>
35
- {outOfBoundaries && (
36
- <div ref={ref} style={style} onClick={scheduleUpdate} />
37
- )}
38
- </>
39
- )}
40
- </Popper>
41
- )};
42
- `,
43
- `
44
- /* TODO: (from codemod) Popper.js has been upgraded from 1.14.1 to 2.4.2,
45
- and as a result the modifier prop has changed significantly. The format has been
46
- changed from object of objects, to array of objects, with the key for each modifier
47
- replaced with a name key:value pair inside the modifier object, and an options:object
48
- pair for configuration and other changes unique to each modifier.
49
- Further details can be found in the popper docs: https://popper.js.org/docs/v2/modifiers/ */
50
- import { Popper } from '@atlaskit/popper';
51
-
52
- export default () => {
53
- customModifiers = {flip: {enabled: true}};
54
- return (
55
- <Popper modifiers={customModifiers}>
56
- {({
57
- ref,
58
- style,
59
- isReferenceHidden: outOfBoundaries,
60
- update: scheduleUpdate
61
- }) => (
62
- <>
63
- {outOfBoundaries && (
64
- <div ref={ref} style={style} onClick={scheduleUpdate} />
65
- )}
66
- </>
67
- )}
68
- </Popper>
69
- );};
70
- `,
71
- );
72
- });
@@ -1,346 +0,0 @@
1
- jest.autoMockOff();
2
-
3
- import transformer from '../5.0.0-lite-mode';
4
-
5
- const defineInlineTest = require('jscodeshift/dist/testUtils').defineInlineTest;
6
-
7
- describe('Update offset prop', () => {
8
- describe('offset as a string', () => {
9
- defineInlineTest(
10
- { default: transformer, parser: 'tsx' },
11
- {},
12
- `
13
- import { Popper } from '@atlaskit/popper';
14
-
15
- export default () => (
16
- <Popper offset='5px 8px'>
17
- {({ ref, style }) => (
18
- <div
19
- ref={ref}
20
- style={style}
21
- />
22
- )}
23
- </Popper>
24
- );
25
- `,
26
- `
27
- import { Popper } from '@atlaskit/popper';
28
-
29
- export default () => (
30
- <Popper offset={[5, 8]}>
31
- {({ ref, style }) => (
32
- <div
33
- ref={ref}
34
- style={style}
35
- />
36
- )}
37
- </Popper>
38
- );
39
- `,
40
- 'should convert string literal to array notation',
41
- );
42
-
43
- defineInlineTest(
44
- { default: transformer, parser: 'tsx' },
45
- {},
46
- `
47
- import { Popper } from '@atlaskit/popper';
48
-
49
- export default () => (
50
- <Popper offset='5px, 8px'>
51
- {({ ref, style }) => (
52
- <div
53
- ref={ref}
54
- style={style}
55
- />
56
- )}
57
- </Popper>
58
- );
59
- `,
60
- `
61
- import { Popper } from '@atlaskit/popper';
62
-
63
- export default () => (
64
- <Popper offset={[5, 8]}>
65
- {({ ref, style }) => (
66
- <div
67
- ref={ref}
68
- style={style}
69
- />
70
- )}
71
- </Popper>
72
- );
73
- `,
74
- 'should convert string literal to array notation - with comma',
75
- );
76
- });
77
-
78
- describe('offset as object expression', () => {
79
- defineInlineTest(
80
- { default: transformer, parser: 'tsx' },
81
- {},
82
- `
83
- import { Popper } from '@atlaskit/popper';
84
-
85
- export default () => (
86
- <Popper offset={'5px 8px'}>
87
- {({ ref, style }) => (
88
- <div
89
- ref={ref}
90
- style={style}
91
- />
92
- )}
93
- </Popper>
94
- );
95
- `,
96
- `
97
- import { Popper } from '@atlaskit/popper';
98
-
99
- export default () => (
100
- <Popper offset={[5, 8]}>
101
- {({ ref, style }) => (
102
- <div
103
- ref={ref}
104
- style={style}
105
- />
106
- )}
107
- </Popper>
108
- );
109
- `,
110
- `should convert object to array notation`,
111
- );
112
-
113
- defineInlineTest(
114
- { default: transformer, parser: 'tsx' },
115
- {},
116
- `
117
- import { Popper } from '@atlaskit/popper';
118
-
119
- function numCommaOffset() {
120
- return (
121
- <Popper offset={'5px, 8px'}>
122
- {({ ref, style }) => (
123
- <div
124
- ref={ref}
125
- style={style}
126
- />
127
- )}
128
- </Popper>
129
- );
130
- }
131
- `,
132
- `
133
- import { Popper } from '@atlaskit/popper';
134
-
135
- function numCommaOffset() {
136
- return (
137
- <Popper offset={[5, 8]}>
138
- {({ ref, style }) => (
139
- <div
140
- ref={ref}
141
- style={style}
142
- />
143
- )}
144
- </Popper>
145
- );
146
- }
147
- `,
148
- `should convert number with comma as offset`,
149
- );
150
-
151
- defineInlineTest(
152
- { default: transformer, parser: 'tsx' },
153
- {},
154
- `
155
- import { Popper } from '@atlaskit/popper';
156
-
157
- function numStringOffset() {
158
- return (
159
- <Popper offset={'10'}>
160
- {({ ref, style }) => (
161
- <div
162
- ref={ref}
163
- style={style}
164
- />
165
- )}
166
- </Popper>
167
- );
168
- }
169
- `,
170
- `
171
- import { Popper } from '@atlaskit/popper';
172
-
173
- function numStringOffset() {
174
- return (
175
- <Popper offset={[10, 0]}>
176
- {({ ref, style }) => (
177
- <div
178
- ref={ref}
179
- style={style}
180
- />
181
- )}
182
- </Popper>
183
- );
184
- }
185
- `,
186
- `should add default value as 0 when one offset is missing - string`,
187
- );
188
-
189
- defineInlineTest(
190
- { default: transformer, parser: 'tsx' },
191
- {},
192
- `
193
- import { Popper } from '@atlaskit/popper';
194
-
195
- function numOffset() {
196
- return (
197
- <Popper offset={10}>
198
- {({ ref, style }) => (
199
- <div
200
- ref={ref}
201
- style={style}
202
- />
203
- )}
204
- </Popper>
205
- );
206
- }
207
- `,
208
- `
209
- import { Popper } from '@atlaskit/popper';
210
-
211
- function numOffset() {
212
- return (
213
- <Popper offset={[10, 0]}>
214
- {({ ref, style }) => (
215
- <div
216
- ref={ref}
217
- style={style}
218
- />
219
- )}
220
- </Popper>
221
- );
222
- }
223
- `,
224
- `should add default value as 0 when one offset is missing - number`,
225
- );
226
- });
227
-
228
- defineInlineTest(
229
- { default: transformer, parser: 'tsx' },
230
- {},
231
- `
232
- import { Popper } from '@atlaskit/popper';
233
-
234
- export default () => (
235
- <Popper offset={'5px + 7vh, 8px'}>
236
- {({ ref, style }) => (
237
- <div
238
- ref={ref}
239
- style={style}
240
- />
241
- )}
242
- </Popper>
243
- );
244
- `,
245
- `
246
- /* TODO: (from codemod) Popper.js has been upgraded from 1.14.1 to 2.4.2,
247
- and as a result the offset prop has changed to be an array. e.g '0px 8px' -> [0, 8]
248
- Along with this change you cannot use vw, vh or % units or addition or multiplication
249
- Change the offset value to use pixel values
250
- Further details can be found in the popper docs https://popper.js.org/docs/v2/modifiers/offset/ */
251
- import { Popper } from '@atlaskit/popper';
252
-
253
- export default () => (
254
- <Popper offset={'5px + 7vh, 8px'}>
255
- {({ ref, style }) => (
256
- <div
257
- ref={ref}
258
- style={style}
259
- />
260
- )}
261
- </Popper>
262
- );
263
- `,
264
- `should add correct document string for vh, vw and %`,
265
- );
266
-
267
- /**
268
- * If a user is passing in a variable for offset then we should leave a comment to
269
- * update it themselves
270
- */
271
- defineInlineTest(
272
- { default: transformer, parser: 'tsx' },
273
- {},
274
- `
275
- import { Popper } from '@atlaskit/popper';
276
-
277
- function directOffset({offset}) {
278
- return (
279
- <Popper offset={offset}>
280
- {({ ref, style }) => (
281
- <div
282
- ref={ref}
283
- style={style}
284
- />
285
- )}
286
- </Popper>
287
- );
288
- }
289
- `,
290
- `
291
- /* TODO: (from codemod) Popper.js has been upgraded from 1.14.1 to 2.4.2, and as a result the offset
292
- prop has changed to be an array. e.g '0px 8px' -> [0, 8]
293
- As you are using a variable, you will have change the offset prop manually
294
- Further details can be found in the popper docs https://popper.js.org/docs/v2/modifiers/offset/ */
295
- import { Popper } from '@atlaskit/popper';
296
-
297
- function directOffset({offset}) {
298
- return (
299
- <Popper offset={offset}>
300
- {({ ref, style }) => (
301
- <div
302
- ref={ref}
303
- style={style}
304
- />
305
- )}
306
- </Popper>
307
- );
308
- }
309
- `,
310
- `should add correct document string for variable`,
311
- );
312
-
313
- defineInlineTest(
314
- { default: transformer, parser: 'tsx' },
315
- {},
316
- `
317
- import { Manager, Popper as Popeye } from '@atlaskit/popper';
318
-
319
- export default () => (
320
- <Popeye offset={'5px 8px'}>
321
- {({ ref, style }) => (
322
- <div
323
- ref={ref}
324
- style={style}
325
- />
326
- )}
327
- </Popeye>
328
- );
329
- `,
330
- `
331
- import { Manager, Popper as Popeye } from '@atlaskit/popper';
332
-
333
- export default () => (
334
- <Popeye offset={[5, 8]}>
335
- {({ ref, style }) => (
336
- <div
337
- ref={ref}
338
- style={style}
339
- />
340
- )}
341
- </Popeye>
342
- );
343
- `,
344
- `should works with an aliased import and other imports`,
345
- );
346
- });
@@ -1,59 +0,0 @@
1
- jest.autoMockOff();
2
-
3
- import transformer from '../5.0.0-lite-mode';
4
-
5
- const defineInlineTest = require('jscodeshift/dist/testUtils').defineInlineTest;
6
-
7
- describe('Update props', () => {
8
- /**
9
- * It should rename outOfBoundaries and scheduleUpdate props
10
- * Render Props:
11
- * - `outOfBoundaries` has been replaced with `isReferenceHidden`, and is now true when the popper is hidden (i.e. by a
12
- * scroll container)
13
- * - `scheduleUpdate`, for async updates, has been renamed to `update`, and now returns a Promise.
14
- */
15
- defineInlineTest(
16
- { default: transformer, parser: 'tsx' },
17
- {},
18
- `
19
- import { Popper } from '@atlaskit/popper';
20
-
21
- export default () => (
22
- <Popper>
23
- {({
24
- ref,
25
- style,
26
- outOfBoundaries,
27
- scheduleUpdate
28
- }) => (
29
- <>
30
- {outOfBoundaries && (
31
- <div ref={ref} style={style} onClick={scheduleUpdate} />
32
- )}
33
- </>
34
- )}
35
- </Popper>
36
- )
37
- `,
38
- `
39
- import { Popper } from '@atlaskit/popper';
40
-
41
- export default () => (
42
- <Popper>
43
- {({
44
- ref,
45
- style,
46
- isReferenceHidden: outOfBoundaries,
47
- update: scheduleUpdate
48
- }) => (
49
- <>
50
- {outOfBoundaries && (
51
- <div ref={ref} style={style} onClick={scheduleUpdate} />
52
- )}
53
- </>
54
- )}
55
- </Popper>
56
- )
57
- `,
58
- );
59
- });
@@ -1,23 +0,0 @@
1
- export const messageForUsingExpression = `
2
- Popper.js has been upgraded from 1.14.1 to 2.4.2,
3
- and as a result the offset prop has changed to be an array. e.g '0px 8px' -> [0, 8]
4
- Along with this change you cannot use vw, vh or % units or addition or multiplication
5
- Change the offset value to use pixel values
6
- Further details can be found in the popper docs https://popper.js.org/docs/v2/modifiers/offset/
7
- `;
8
-
9
- export const messageForUsingVariable = `
10
- Popper.js has been upgraded from 1.14.1 to 2.4.2, and as a result the offset
11
- prop has changed to be an array. e.g '0px 8px' -> [0, 8]
12
- As you are using a variable, you will have change the offset prop manually
13
- Further details can be found in the popper docs https://popper.js.org/docs/v2/modifiers/offset/
14
- `;
15
-
16
- export const messageForModifierProps = `
17
- Popper.js has been upgraded from 1.14.1 to 2.4.2,
18
- and as a result the modifier prop has changed significantly. The format has been
19
- changed from object of objects, to array of objects, with the key for each modifier
20
- replaced with a name key:value pair inside the modifier object, and an options:object
21
- pair for configuration and other changes unique to each modifier.
22
- Further details can be found in the popper docs: https://popper.js.org/docs/v2/modifiers/
23
- `;
@@ -1,333 +0,0 @@
1
- import { type NodePath } from 'ast-types/lib/node-path';
2
- import {
3
- type ASTPath,
4
- type default as core,
5
- type ImportDeclaration,
6
- type JSXAttribute,
7
- type JSXElement,
8
- type Node,
9
- } from 'jscodeshift';
10
- import { type Collection } from 'jscodeshift/src/Collection';
11
-
12
- export type Nullable<T> = T | null;
13
-
14
- export function hasImportDeclaration(
15
- j: core.JSCodeshift,
16
- source: string,
17
- importPath: string,
18
- ): boolean {
19
- return (
20
- j(source)
21
- .find(j.ImportDeclaration)
22
- .filter((path: ASTPath<ImportDeclaration>) => path.node.source.value === importPath).length >
23
- 0
24
- );
25
- }
26
-
27
- export function getDefaultSpecifierName({
28
- j,
29
- base,
30
- packageName,
31
- }: {
32
- j: core.JSCodeshift;
33
- base: Collection<any>;
34
- packageName: string;
35
- }): Nullable<string> {
36
- const specifiers = base
37
- .find(j.ImportDeclaration)
38
- .filter((path) => path.node.source.value === packageName)
39
- .find(j.ImportDefaultSpecifier);
40
-
41
- if (!specifiers.length) {
42
- return null;
43
- }
44
- return specifiers.nodes()[0]!.local!.name;
45
- }
46
-
47
- export function getSpecifierName({
48
- j,
49
- base,
50
- packageName,
51
- component,
52
- }: {
53
- j: core.JSCodeshift;
54
- base: Collection<any>;
55
- packageName: string;
56
- component: string;
57
- }): Nullable<string> {
58
- const specifiers = base
59
- .find(j.ImportDeclaration)
60
- .filter((path) => path.node.source.value === packageName)
61
- .find(j.ImportSpecifier);
62
-
63
- if (!specifiers.length) {
64
- return null;
65
- }
66
- const specifierNode = specifiers.nodes().find((node) => node.imported.name === component);
67
- if (!specifierNode) {
68
- return null;
69
- }
70
- // @ts-ignore
71
- return specifierNode.local.name;
72
- }
73
-
74
- export function getJSXAttributesByName({
75
- j,
76
- element,
77
- attributeName,
78
- }: {
79
- j: core.JSCodeshift;
80
- element: JSXElement | ASTPath<JSXElement>;
81
- attributeName: string;
82
- }): Collection<JSXAttribute> {
83
- return j(element)
84
- .find(j.JSXOpeningElement)
85
- .find(j.JSXAttribute)
86
- .filter((attribute) => {
87
- const matches = j(attribute)
88
- .find(j.JSXIdentifier)
89
- .filter((identifier) => identifier.value.name === attributeName);
90
- return Boolean(matches.length);
91
- });
92
- }
93
-
94
- export function hasJSXAttributesByName({
95
- j,
96
- element,
97
- attributeName,
98
- }: {
99
- j: core.JSCodeshift;
100
- element: JSXElement;
101
- attributeName: string;
102
- }): boolean {
103
- return getJSXAttributesByName({ j, element, attributeName }).length > 0;
104
- }
105
-
106
- export function isUsingSupportedSpread({
107
- j,
108
- base,
109
- element,
110
- }: {
111
- j: core.JSCodeshift;
112
- base: Collection<any>;
113
- element: NodePath<JSXElement, JSXElement>;
114
- }): boolean {
115
- const isUsingSpread: boolean = j(element).find(j.JSXSpreadAttribute).length > 0;
116
-
117
- if (!isUsingSpread) {
118
- return true;
119
- }
120
-
121
- return (
122
- j(element)
123
- .find(j.JSXSpreadAttribute)
124
- .filter((spread) => {
125
- const argument = spread.value.argument;
126
- // in place expression is supported
127
- if (argument.type === 'ObjectExpression') {
128
- return true;
129
- }
130
-
131
- // Supporting identifiers that point to an a local object expression
132
- if (argument.type === 'Identifier') {
133
- return (
134
- base.find(j.VariableDeclarator).filter((declarator): boolean => {
135
- return (
136
- declarator.value.id.type === 'Identifier' &&
137
- // @ts-ignore
138
- declarator.value.init.type === 'ObjectExpression'
139
- );
140
- }).length > 0
141
- );
142
- }
143
-
144
- // We don't support anything else
145
- return false;
146
- }).length > 0
147
- );
148
- }
149
-
150
- export function isUsingThroughSpread({
151
- j,
152
- base,
153
- element,
154
- propName,
155
- }: {
156
- j: core.JSCodeshift;
157
- base: Collection<any>;
158
- element: NodePath<JSXElement, JSXElement>;
159
- propName: string;
160
- }): boolean {
161
- if (!isUsingSupportedSpread({ j, base, element })) {
162
- return false;
163
- }
164
-
165
- const isUsedThroughExpression: boolean =
166
- j(element)
167
- .find(j.JSXSpreadAttribute)
168
- .find(j.ObjectExpression)
169
- .filter((item) => {
170
- const match: boolean =
171
- item.value.properties.filter(
172
- (property) =>
173
- property.type === 'ObjectProperty' &&
174
- property.key.type === 'Identifier' &&
175
- property.key.name === propName,
176
- ).length > 0;
177
-
178
- return match;
179
- }).length > 0;
180
-
181
- if (isUsedThroughExpression) {
182
- return true;
183
- }
184
-
185
- const isUsedThroughIdentifier: boolean =
186
- j(element)
187
- .find(j.JSXSpreadAttribute)
188
- .find(j.Identifier)
189
- .filter((identifier): boolean => {
190
- return (
191
- base
192
- .find(j.VariableDeclarator)
193
- .filter(
194
- (declarator) =>
195
- declarator.value.id.type === 'Identifier' &&
196
- declarator.value.id.name === identifier.value.name,
197
- )
198
- .filter((declarator) => {
199
- const value = declarator.value;
200
- if (value.id.type !== 'Identifier') {
201
- return false;
202
- }
203
-
204
- if (value.id.name !== identifier.value.name) {
205
- return false;
206
- }
207
- // @ts-ignore
208
- if (value.init.type !== 'ObjectExpression') {
209
- return false;
210
- }
211
-
212
- const match: boolean =
213
- // @ts-ignore
214
- value.init.properties.filter(
215
- // @ts-ignore
216
- (property) =>
217
- property.type === 'ObjectProperty' &&
218
- property.key.type === 'Identifier' &&
219
- property.key.name === propName,
220
- ).length > 0;
221
-
222
- return match;
223
- }).length > 0
224
- );
225
- }).length > 0;
226
-
227
- return isUsedThroughIdentifier;
228
- }
229
-
230
- export function isUsingProp({
231
- j,
232
- base,
233
- element,
234
- propName,
235
- }: {
236
- j: core.JSCodeshift;
237
- base: Collection<any>;
238
- element: NodePath<JSXElement, JSXElement>;
239
- propName: string;
240
- }): boolean {
241
- return (
242
- hasJSXAttributesByName({
243
- j,
244
- element: element.value,
245
- attributeName: propName,
246
- }) ||
247
- isUsingThroughSpread({
248
- j,
249
- base,
250
- element,
251
- propName,
252
- })
253
- );
254
- }
255
-
256
- // not replacing newlines (which \s does)
257
- const spacesAndTabs: RegExp = /[ \t]{2,}/g;
258
- const lineStartWithSpaces: RegExp = /^[ \t]*/gm;
259
-
260
- function clean(value: string): string {
261
- return (
262
- value
263
- .replace(spacesAndTabs, ' ')
264
- .replace(lineStartWithSpaces, '')
265
- // using .trim() to clear the any newlines before the first text and after last text
266
- .trim()
267
- );
268
- }
269
-
270
- export function addCommentToStartOfFile({
271
- j,
272
- base,
273
- message,
274
- }: {
275
- j: core.JSCodeshift;
276
- base: Collection<Node>;
277
- message: string;
278
- }) {
279
- addCommentBefore({
280
- j,
281
- // @ts-ignore
282
- target: base.find(j.Program),
283
- message,
284
- });
285
- }
286
-
287
- export function addCommentBefore({
288
- j,
289
- target,
290
- message,
291
- }: {
292
- j: core.JSCodeshift;
293
- target: Collection<Node>;
294
- message: string;
295
- }) {
296
- const content: string = ` TODO: (from codemod) ${clean(message)} `;
297
- target.forEach((path) => {
298
- path.value.comments = path.value.comments || [];
299
-
300
- const exists = path.value.comments.find((comment) => comment.value === content);
301
-
302
- // avoiding duplicates of the same comment
303
- if (exists) {
304
- return;
305
- }
306
-
307
- path.value.comments.push(j.commentBlock(content));
308
- });
309
- }
310
-
311
- export function updateRenderProps(
312
- j: core.JSCodeshift,
313
- source: Collection<any>,
314
- specifier: string,
315
- oldProperty: string,
316
- newProperty: string,
317
- ) {
318
- source.findJSXElements(specifier).forEach((element: ASTPath<JSXElement>) => {
319
- j(element)
320
- .find(j.ArrowFunctionExpression)
321
- .find(j.ObjectPattern)
322
- .find(j.ObjectProperty)
323
- .filter(
324
- // @ts-ignore
325
- (path: ASTPath<ObjectProperty>) => path.value.key.name === oldProperty,
326
- )
327
- .forEach((path) => {
328
- j(path).replaceWith(
329
- j.property('init', j.identifier(newProperty), j.identifier(oldProperty)),
330
- );
331
- });
332
- });
333
- }