@atlaskit/side-navigation 1.1.2

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 (140) hide show
  1. package/CHANGELOG.md +517 -0
  2. package/LICENSE +13 -0
  3. package/README.md +16 -0
  4. package/build/tsconfig.json +17 -0
  5. package/codemods/0.8.0-change-css-fn-prop.ts +184 -0
  6. package/codemods/__tests__/0.8.0-change-css-fn-prop.ts +360 -0
  7. package/codemods/helpers/generic.ts +674 -0
  8. package/dist/cjs/common/constants.js +10 -0
  9. package/dist/cjs/common/styles.js +104 -0
  10. package/dist/cjs/components/Footer/index.js +67 -0
  11. package/dist/cjs/components/Header/index.js +73 -0
  12. package/dist/cjs/components/Item/button-item.js +47 -0
  13. package/dist/cjs/components/Item/custom-item.js +52 -0
  14. package/dist/cjs/components/Item/go-back-item.js +65 -0
  15. package/dist/cjs/components/Item/index.js +47 -0
  16. package/dist/cjs/components/Item/link-item.js +47 -0
  17. package/dist/cjs/components/Item/skeleton-item.js +43 -0
  18. package/dist/cjs/components/LoadingItems/index.js +51 -0
  19. package/dist/cjs/components/NavigationContent/index.js +52 -0
  20. package/dist/cjs/components/NavigationContent/styles.js +152 -0
  21. package/dist/cjs/components/NavigationFooter/index.js +27 -0
  22. package/dist/cjs/components/NavigationHeader/index.js +27 -0
  23. package/dist/cjs/components/NestableNavigationContent/context.js +51 -0
  24. package/dist/cjs/components/NestableNavigationContent/index.js +182 -0
  25. package/dist/cjs/components/NestableNavigationContent/nesting-motion.js +40 -0
  26. package/dist/cjs/components/NestingItem/hack-for-ert.js +8 -0
  27. package/dist/cjs/components/NestingItem/index.js +173 -0
  28. package/dist/cjs/components/NestingItem/styles.js +47 -0
  29. package/dist/cjs/components/Section/heading-item.js +35 -0
  30. package/dist/cjs/components/Section/index.js +31 -0
  31. package/dist/cjs/components/Section/section.js +45 -0
  32. package/dist/cjs/components/Section/skeleton-heading-item.js +39 -0
  33. package/dist/cjs/components/SideNavigation/index.js +41 -0
  34. package/dist/cjs/components/index.js +131 -0
  35. package/dist/cjs/index.js +131 -0
  36. package/dist/cjs/version.json +5 -0
  37. package/dist/es2019/common/constants.js +2 -0
  38. package/dist/es2019/common/styles.js +78 -0
  39. package/dist/es2019/components/Footer/index.js +52 -0
  40. package/dist/es2019/components/Header/index.js +46 -0
  41. package/dist/es2019/components/Item/button-item.js +25 -0
  42. package/dist/es2019/components/Item/custom-item.js +31 -0
  43. package/dist/es2019/components/Item/go-back-item.js +34 -0
  44. package/dist/es2019/components/Item/index.js +5 -0
  45. package/dist/es2019/components/Item/link-item.js +25 -0
  46. package/dist/es2019/components/Item/skeleton-item.js +28 -0
  47. package/dist/es2019/components/LoadingItems/index.js +38 -0
  48. package/dist/es2019/components/NavigationContent/index.js +38 -0
  49. package/dist/es2019/components/NavigationContent/styles.js +120 -0
  50. package/dist/es2019/components/NavigationFooter/index.js +18 -0
  51. package/dist/es2019/components/NavigationHeader/index.js +20 -0
  52. package/dist/es2019/components/NestableNavigationContent/context.js +41 -0
  53. package/dist/es2019/components/NestableNavigationContent/index.js +148 -0
  54. package/dist/es2019/components/NestableNavigationContent/nesting-motion.js +21 -0
  55. package/dist/es2019/components/NestingItem/hack-for-ert.js +1 -0
  56. package/dist/es2019/components/NestingItem/index.js +128 -0
  57. package/dist/es2019/components/NestingItem/styles.js +39 -0
  58. package/dist/es2019/components/Section/heading-item.js +22 -0
  59. package/dist/es2019/components/Section/index.js +3 -0
  60. package/dist/es2019/components/Section/section.js +25 -0
  61. package/dist/es2019/components/Section/skeleton-heading-item.js +24 -0
  62. package/dist/es2019/components/SideNavigation/index.js +30 -0
  63. package/dist/es2019/components/index.js +11 -0
  64. package/dist/es2019/index.js +3 -0
  65. package/dist/es2019/version.json +5 -0
  66. package/dist/esm/common/constants.js +2 -0
  67. package/dist/esm/common/styles.js +82 -0
  68. package/dist/esm/components/Footer/index.js +51 -0
  69. package/dist/esm/components/Header/index.js +50 -0
  70. package/dist/esm/components/Item/button-item.js +25 -0
  71. package/dist/esm/components/Item/custom-item.js +31 -0
  72. package/dist/esm/components/Item/go-back-item.js +41 -0
  73. package/dist/esm/components/Item/index.js +5 -0
  74. package/dist/esm/components/Item/link-item.js +25 -0
  75. package/dist/esm/components/Item/skeleton-item.js +29 -0
  76. package/dist/esm/components/LoadingItems/index.js +39 -0
  77. package/dist/esm/components/NavigationContent/index.js +37 -0
  78. package/dist/esm/components/NavigationContent/styles.js +130 -0
  79. package/dist/esm/components/NavigationFooter/index.js +17 -0
  80. package/dist/esm/components/NavigationHeader/index.js +18 -0
  81. package/dist/esm/components/NestableNavigationContent/context.js +36 -0
  82. package/dist/esm/components/NestableNavigationContent/index.js +163 -0
  83. package/dist/esm/components/NestableNavigationContent/nesting-motion.js +28 -0
  84. package/dist/esm/components/NestingItem/hack-for-ert.js +1 -0
  85. package/dist/esm/components/NestingItem/index.js +144 -0
  86. package/dist/esm/components/NestingItem/styles.js +34 -0
  87. package/dist/esm/components/Section/heading-item.js +21 -0
  88. package/dist/esm/components/Section/index.js +3 -0
  89. package/dist/esm/components/Section/section.js +24 -0
  90. package/dist/esm/components/Section/skeleton-heading-item.js +25 -0
  91. package/dist/esm/components/SideNavigation/index.js +28 -0
  92. package/dist/esm/components/index.js +11 -0
  93. package/dist/esm/index.js +3 -0
  94. package/dist/esm/version.json +5 -0
  95. package/dist/types/common/constants.d.ts +2 -0
  96. package/dist/types/common/styles.d.ts +10 -0
  97. package/dist/types/components/Footer/index.d.ts +4 -0
  98. package/dist/types/components/Header/index.d.ts +43 -0
  99. package/dist/types/components/Item/button-item.d.ts +5 -0
  100. package/dist/types/components/Item/custom-item.d.ts +13 -0
  101. package/dist/types/components/Item/go-back-item.d.ts +5 -0
  102. package/dist/types/components/Item/index.d.ts +10 -0
  103. package/dist/types/components/Item/link-item.d.ts +5 -0
  104. package/dist/types/components/Item/skeleton-item.d.ts +4 -0
  105. package/dist/types/components/LoadingItems/index.d.ts +30 -0
  106. package/dist/types/components/NavigationContent/index.d.ts +17 -0
  107. package/dist/types/components/NavigationContent/styles.d.ts +91 -0
  108. package/dist/types/components/NavigationFooter/index.d.ts +7 -0
  109. package/dist/types/components/NavigationHeader/index.d.ts +5 -0
  110. package/dist/types/components/NestableNavigationContent/context.d.ts +20 -0
  111. package/dist/types/components/NestableNavigationContent/index.d.ts +58 -0
  112. package/dist/types/components/NestableNavigationContent/nesting-motion.d.ts +18 -0
  113. package/dist/types/components/NestingItem/hack-for-ert.d.ts +2 -0
  114. package/dist/types/components/NestingItem/index.d.ts +91 -0
  115. package/dist/types/components/NestingItem/styles.d.ts +28 -0
  116. package/dist/types/components/Section/heading-item.d.ts +4 -0
  117. package/dist/types/components/Section/index.d.ts +6 -0
  118. package/dist/types/components/Section/section.d.ts +25 -0
  119. package/dist/types/components/Section/skeleton-heading-item.d.ts +4 -0
  120. package/dist/types/components/SideNavigation/index.d.ts +23 -0
  121. package/dist/types/components/index.d.ts +22 -0
  122. package/dist/types/index.d.ts +4 -0
  123. package/docs/00-intro.tsx +70 -0
  124. package/docs/01-side-navigation.tsx +33 -0
  125. package/docs/02-navigation-header.tsx +39 -0
  126. package/docs/03-navigation-content.tsx +40 -0
  127. package/docs/04-nestable-navigation-content.tsx +95 -0
  128. package/docs/05-navigation-footer.tsx +38 -0
  129. package/docs/99-loading-states.tsx +95 -0
  130. package/docs/button-item.tsx +38 -0
  131. package/docs/custom-item.tsx +45 -0
  132. package/docs/go-back-item.tsx +31 -0
  133. package/docs/heading-item.tsx +30 -0
  134. package/docs/link-item.tsx +39 -0
  135. package/docs/nesting-item.tsx +52 -0
  136. package/docs/section.tsx +40 -0
  137. package/docs/skeleton-heading-item.tsx +30 -0
  138. package/docs/skeleton-item.tsx +30 -0
  139. package/package.json +71 -0
  140. package/tsconfig.json +15 -0
@@ -0,0 +1,674 @@
1
+ import { NodePath } from 'ast-types/lib/node-path';
2
+ import core, {
3
+ ASTPath,
4
+ ImportDeclaration,
5
+ ImportDefaultSpecifier,
6
+ ImportSpecifier,
7
+ JSXAttribute,
8
+ JSXElement,
9
+ Node,
10
+ Program,
11
+ } from 'jscodeshift';
12
+ import { Collection } from 'jscodeshift/src/Collection';
13
+
14
+ export type Nullable<T> = T | null;
15
+
16
+ export function hasImportDeclaration(
17
+ j: core.JSCodeshift,
18
+ source: string,
19
+ importPath: string,
20
+ ): boolean {
21
+ return (
22
+ j(source)
23
+ .find(j.ImportDeclaration)
24
+ .filter(
25
+ (path: ASTPath<ImportDeclaration>) =>
26
+ path.node.source.value === importPath,
27
+ ).length > 0
28
+ );
29
+ }
30
+
31
+ export function getDefaultSpecifierName({
32
+ j,
33
+ base,
34
+ packageName,
35
+ }: {
36
+ j: core.JSCodeshift;
37
+ base: Collection<any>;
38
+ packageName: string;
39
+ }): Nullable<string> {
40
+ const specifiers = base
41
+ .find(j.ImportDeclaration)
42
+ .filter((path) => path.node.source.value === packageName)
43
+ .find(j.ImportDefaultSpecifier);
44
+
45
+ if (!specifiers.length) {
46
+ return null;
47
+ }
48
+ return specifiers.nodes()[0]!.local!.name;
49
+ }
50
+
51
+ export function getJSXAttributesByName({
52
+ j,
53
+ element,
54
+ attributeName,
55
+ }: {
56
+ j: core.JSCodeshift;
57
+ element: JSXElement;
58
+ attributeName: string;
59
+ }): Collection<JSXAttribute> {
60
+ return j(element)
61
+ .find(j.JSXOpeningElement)
62
+ .find(j.JSXAttribute)
63
+ .filter((attribute) => {
64
+ const matches = j(attribute)
65
+ // This will find identifiers on nested jsx elements
66
+ // so we are going to do a filter to ensure we are only
67
+ // going one level deep
68
+ .find(j.JSXIdentifier)
69
+ .filter((identifer) => {
70
+ j(identifer).closest(j.JSXOpeningElement);
71
+ // Checking we are on the same level as the jsx element
72
+ const closest = j(identifer).closest(j.JSXOpeningElement).nodes()[0];
73
+
74
+ if (!closest) {
75
+ return false;
76
+ }
77
+ return (
78
+ closest.name.type === 'JSXIdentifier' &&
79
+ element.openingElement.name.type === 'JSXIdentifier' &&
80
+ element.openingElement.name.name === closest.name.name
81
+ );
82
+ })
83
+ .filter((identifier) => identifier.value.name === attributeName);
84
+ return Boolean(matches.length);
85
+ });
86
+ }
87
+
88
+ export function hasJSXAttributesByName({
89
+ j,
90
+ element,
91
+ attributeName,
92
+ }: {
93
+ j: core.JSCodeshift;
94
+ element: JSXElement;
95
+ attributeName: string;
96
+ }): boolean {
97
+ return getJSXAttributesByName({ j, element, attributeName }).length > 0;
98
+ }
99
+
100
+ export function removeImport({
101
+ j,
102
+ base,
103
+ packageName,
104
+ }: {
105
+ j: core.JSCodeshift;
106
+ base: Collection<any>;
107
+ packageName: string;
108
+ }) {
109
+ base
110
+ .find(j.ImportDeclaration)
111
+ .filter((path) => path.node.source.value === packageName)
112
+ .remove();
113
+ }
114
+
115
+ export function tryCreateImport({
116
+ j,
117
+ base,
118
+ relativeToPackage,
119
+ packageName,
120
+ }: {
121
+ j: core.JSCodeshift;
122
+ base: Collection<any>;
123
+ relativeToPackage: string;
124
+ packageName: string;
125
+ }) {
126
+ const exists: boolean =
127
+ base
128
+ .find(j.ImportDeclaration)
129
+ .filter((path) => path.value.source.value === packageName).length > 0;
130
+
131
+ if (exists) {
132
+ return;
133
+ }
134
+
135
+ base
136
+ .find(j.ImportDeclaration)
137
+ .filter((path) => path.value.source.value === relativeToPackage)
138
+ .insertBefore(j.importDeclaration([], j.literal(packageName)));
139
+ }
140
+
141
+ export function addToImport({
142
+ j,
143
+ base,
144
+ importSpecifier,
145
+ packageName,
146
+ }: {
147
+ j: core.JSCodeshift;
148
+ base: Collection<any>;
149
+ importSpecifier: ImportSpecifier | ImportDefaultSpecifier;
150
+ packageName: string;
151
+ }) {
152
+ base
153
+ .find(j.ImportDeclaration)
154
+ .filter((path) => path.value.source.value === packageName)
155
+ .replaceWith((declaration) => {
156
+ return j.importDeclaration(
157
+ [
158
+ // we are appending to the existing specifiers
159
+ // We are doing a filter hear because sometimes specifiers can be removed
160
+ // but they hand around in the declaration
161
+ ...(declaration.value.specifiers || []).filter(
162
+ (item) => item.type === 'ImportSpecifier' && item.imported != null,
163
+ ),
164
+ importSpecifier,
165
+ ],
166
+ j.literal(packageName),
167
+ );
168
+ });
169
+ }
170
+
171
+ export function doesIdentifierExist({
172
+ j,
173
+ base,
174
+ name,
175
+ }: {
176
+ j: core.JSCodeshift;
177
+ base: Collection<any>;
178
+ name: string;
179
+ }): boolean {
180
+ return (
181
+ base.find(j.Identifier).filter((identifer) => identifer.value.name === name)
182
+ .length > 0
183
+ );
184
+ }
185
+
186
+ export function isUsingSupportedSpread({
187
+ j,
188
+ base,
189
+ element,
190
+ }: {
191
+ j: core.JSCodeshift;
192
+ base: Collection<any>;
193
+ element: NodePath<JSXElement, JSXElement>;
194
+ }): boolean {
195
+ const isUsingSpread: boolean =
196
+ j(element).find(j.JSXSpreadAttribute).length > 0;
197
+
198
+ if (!isUsingSpread) {
199
+ return true;
200
+ }
201
+
202
+ return (
203
+ j(element)
204
+ .find(j.JSXSpreadAttribute)
205
+ .filter((spread) => {
206
+ const argument = spread.value.argument;
207
+ // in place expression is supported
208
+ if (argument.type === 'ObjectExpression') {
209
+ return true;
210
+ }
211
+
212
+ // Supporting identifiers that point to an a local object expression
213
+ if (argument.type === 'Identifier') {
214
+ return (
215
+ base.find(j.VariableDeclarator).filter((declarator): boolean => {
216
+ return Boolean(
217
+ declarator.value.id.type === 'Identifier' &&
218
+ declarator.value.init &&
219
+ declarator.value.init.type === 'ObjectExpression',
220
+ );
221
+ }).length > 0
222
+ );
223
+ }
224
+
225
+ // We don't support anything else
226
+ return false;
227
+ }).length > 0
228
+ );
229
+ }
230
+
231
+ export function isOnlyUsingNameForJSX({
232
+ j,
233
+ base,
234
+ name,
235
+ }: {
236
+ j: core.JSCodeshift;
237
+ base: Collection<any>;
238
+ name: string;
239
+ }): boolean {
240
+ const jsxIdentifierCount: number = base
241
+ .find(j.JSXIdentifier)
242
+ .filter((identifier) => identifier.value.name === name).length;
243
+
244
+ // Not used in JSX at all
245
+ if (jsxIdentifierCount === 0) {
246
+ return false;
247
+ }
248
+
249
+ const nonJSXIdentifierCount: number = base
250
+ .find(j.Identifier)
251
+ .filter((identifier) => {
252
+ if (identifier.value.name !== name) {
253
+ return false;
254
+ }
255
+
256
+ // @ts-ignore
257
+ if (identifier.value.type === 'JSXIdentifier') {
258
+ return false;
259
+ }
260
+
261
+ // Excluding exports
262
+ if (j(identifier).closest(j.ImportDefaultSpecifier).length) {
263
+ return false;
264
+ }
265
+ if (j(identifier).closest(j.ImportSpecifier).length) {
266
+ return false;
267
+ }
268
+
269
+ return true;
270
+ }).length;
271
+
272
+ if (nonJSXIdentifierCount > 0) {
273
+ return false;
274
+ }
275
+
276
+ return true;
277
+ }
278
+
279
+ export function getSafeImportName({
280
+ j,
281
+ base,
282
+ currentDefaultSpecifierName,
283
+ desiredName,
284
+ fallbackName,
285
+ }: {
286
+ j: core.JSCodeshift;
287
+ base: Collection<any>;
288
+ currentDefaultSpecifierName: string;
289
+ desiredName: string;
290
+ fallbackName: string;
291
+ }) {
292
+ if (currentDefaultSpecifierName === desiredName) {
293
+ return desiredName;
294
+ }
295
+
296
+ const isUsed: boolean = doesIdentifierExist({ j, base, name: desiredName });
297
+
298
+ return isUsed ? fallbackName : desiredName;
299
+ }
300
+
301
+ export function isUsingThroughSpread({
302
+ j,
303
+ base,
304
+ element,
305
+ propName,
306
+ }: {
307
+ j: core.JSCodeshift;
308
+ base: Collection<any>;
309
+ element: NodePath<JSXElement, JSXElement>;
310
+ propName: string;
311
+ }): boolean {
312
+ if (!isUsingSupportedSpread({ j, base, element })) {
313
+ return false;
314
+ }
315
+
316
+ const isUsedThroughExpression: boolean =
317
+ j(element)
318
+ .find(j.JSXSpreadAttribute)
319
+ .find(j.ObjectExpression)
320
+ .filter((item) => {
321
+ const match: boolean =
322
+ item.value.properties.filter(
323
+ (property) =>
324
+ property.type === 'ObjectProperty' &&
325
+ property.key.type === 'Identifier' &&
326
+ property.key.name === propName,
327
+ ).length > 0;
328
+
329
+ return match;
330
+ }).length > 0;
331
+
332
+ if (isUsedThroughExpression) {
333
+ return true;
334
+ }
335
+
336
+ const isUsedThroughIdentifier: boolean =
337
+ j(element)
338
+ .find(j.JSXSpreadAttribute)
339
+ .find(j.Identifier)
340
+ .filter((identifier): boolean => {
341
+ return (
342
+ base
343
+ .find(j.VariableDeclarator)
344
+ .filter(
345
+ (declarator) =>
346
+ declarator.value.id.type === 'Identifier' &&
347
+ declarator.value.id.name === identifier.value.name,
348
+ )
349
+ .filter((declarator) => {
350
+ const value = declarator.value;
351
+ if (value.id.type !== 'Identifier') {
352
+ return false;
353
+ }
354
+
355
+ if (value.id.name !== identifier.value.name) {
356
+ return false;
357
+ }
358
+
359
+ if (!value.init) {
360
+ return false;
361
+ }
362
+
363
+ if (value.init.type !== 'ObjectExpression') {
364
+ return false;
365
+ }
366
+
367
+ const match: boolean =
368
+ value.init.properties.filter(
369
+ (property) =>
370
+ property.type === 'ObjectProperty' &&
371
+ property.key.type === 'Identifier' &&
372
+ property.key.name === propName,
373
+ ).length > 0;
374
+
375
+ return match;
376
+ }).length > 0
377
+ );
378
+ }).length > 0;
379
+
380
+ return isUsedThroughIdentifier;
381
+ }
382
+
383
+ export function isUsingProp({
384
+ j,
385
+ base,
386
+ element,
387
+ propName,
388
+ }: {
389
+ j: core.JSCodeshift;
390
+ base: Collection<any>;
391
+ element: NodePath<JSXElement, JSXElement>;
392
+ propName: string;
393
+ }): boolean {
394
+ return (
395
+ hasJSXAttributesByName({
396
+ j,
397
+ element: element.value,
398
+ attributeName: propName,
399
+ }) ||
400
+ isUsingThroughSpread({
401
+ j,
402
+ base,
403
+ element,
404
+ propName,
405
+ })
406
+ );
407
+ }
408
+
409
+ // not replacing newlines (which \s does)
410
+ const spacesAndTabs: RegExp = /[ \t]{2,}/g;
411
+ const lineStartWithSpaces: RegExp = /^[ \t]*/gm;
412
+
413
+ function clean(value: string): string {
414
+ return (
415
+ value
416
+ .replace(spacesAndTabs, ' ')
417
+ .replace(lineStartWithSpaces, '')
418
+ // using .trim() to clear the any newlines before the first text and after last text
419
+ .trim()
420
+ );
421
+ }
422
+
423
+ export function addCommentBeforeJSX(
424
+ j: core.JSCodeshift,
425
+ element: NodePath,
426
+ message: string,
427
+ ) {
428
+ const content: string = `\nTODO: (from codemod) ${clean(message)}\n`;
429
+
430
+ const comment = j.commentBlock(content, false, true);
431
+
432
+ element.insertBefore(comment);
433
+ }
434
+
435
+ export function addCommentToStartOfFile({
436
+ j,
437
+ base,
438
+ message,
439
+ }: {
440
+ j: core.JSCodeshift;
441
+ base: Collection<Node>;
442
+ message: string;
443
+ }) {
444
+ addCommentBefore({
445
+ j,
446
+ target: base.find(j.Program),
447
+ message,
448
+ });
449
+ }
450
+
451
+ export function addCommentBefore({
452
+ j,
453
+ target,
454
+ message,
455
+ }: {
456
+ j: core.JSCodeshift;
457
+ target:
458
+ | Collection<Node>
459
+ | Collection<Program>
460
+ | Collection<ImportDeclaration>;
461
+ message: string;
462
+ }) {
463
+ const content: string = ` TODO: (from codemod) ${clean(message)} `;
464
+ target.forEach((path) => {
465
+ path.value.comments = path.value.comments || [];
466
+
467
+ const exists = path.value.comments.find(
468
+ (comment) => comment.value === content,
469
+ );
470
+
471
+ // avoiding duplicates of the same comment
472
+ if (exists) {
473
+ return;
474
+ }
475
+
476
+ path.value.comments.push(j.commentBlock(content));
477
+ });
478
+ }
479
+
480
+ export function shiftDefaultImport({
481
+ j,
482
+ base,
483
+ defaultName,
484
+ oldPackagePath,
485
+ newPackagePath,
486
+ }: {
487
+ j: core.JSCodeshift;
488
+ base: Collection<any>;
489
+ defaultName: string;
490
+ oldPackagePath: string;
491
+ newPackagePath: string;
492
+ }) {
493
+ tryCreateImport({
494
+ j,
495
+ base,
496
+ relativeToPackage: oldPackagePath,
497
+ packageName: newPackagePath,
498
+ });
499
+
500
+ addToImport({
501
+ j,
502
+ base,
503
+ importSpecifier: j.importDefaultSpecifier(j.identifier(defaultName)),
504
+ packageName: newPackagePath,
505
+ });
506
+
507
+ // removing old default specifier
508
+ base
509
+ .find(j.ImportDeclaration)
510
+ .filter((path) => path.node.source.value === oldPackagePath)
511
+ .find(j.ImportDefaultSpecifier)
512
+ .remove();
513
+ }
514
+
515
+ type Option =
516
+ | {
517
+ type: 'change-name';
518
+ oldName: string;
519
+ newName: string;
520
+ fallbackNameAlias: string;
521
+ }
522
+ | {
523
+ type: 'keep-name';
524
+ name: string;
525
+ behaviour: 'move-to-default-import' | 'keep-as-named-import';
526
+ };
527
+
528
+ // try to avoid this one if you can. I'm not super happy with it
529
+ export function changeImportFor({
530
+ j,
531
+ base,
532
+ option,
533
+ oldPackagePath,
534
+ newPackagePath,
535
+ }: {
536
+ j: core.JSCodeshift;
537
+ base: Collection<any>;
538
+ option: Option;
539
+ oldPackagePath: string;
540
+ newPackagePath: string;
541
+ }) {
542
+ const currentName: string =
543
+ option.type === 'change-name' ? option.oldName : option.name;
544
+ const desiredName: string =
545
+ option.type === 'change-name' ? option.newName : option.name;
546
+
547
+ const isUsingName: boolean =
548
+ base
549
+ .find(j.ImportDeclaration)
550
+ .filter((path) => path.node.source.value === oldPackagePath)
551
+ .find(j.ImportSpecifier)
552
+ .find(j.Identifier)
553
+ .filter((identifier) => identifier.value.name === currentName).length > 0;
554
+
555
+ if (!isUsingName) {
556
+ return;
557
+ }
558
+
559
+ const existingAlias: Nullable<string> =
560
+ base
561
+ .find(j.ImportDeclaration)
562
+ .filter((path) => path.node.source.value === oldPackagePath)
563
+ .find(j.ImportSpecifier)
564
+ .nodes()
565
+ .map(
566
+ (specifier): Nullable<string> => {
567
+ if (specifier.imported.name !== currentName) {
568
+ return null;
569
+ }
570
+ // If aliased: return the alias
571
+ if (specifier.local && specifier.local.name !== currentName) {
572
+ return specifier.local.name;
573
+ }
574
+
575
+ return null;
576
+ },
577
+ )
578
+ .filter(Boolean)[0] || null;
579
+
580
+ base
581
+ .find(j.ImportDeclaration)
582
+ .filter((path) => path.node.source.value === oldPackagePath)
583
+ .find(j.ImportSpecifier)
584
+ .find(j.Identifier)
585
+ .filter((identifier) => {
586
+ if (identifier.value.name === currentName) {
587
+ return true;
588
+ }
589
+ if (identifier.value.name === existingAlias) {
590
+ return true;
591
+ }
592
+ return false;
593
+ })
594
+ .remove();
595
+
596
+ // Check to see if need to create new package path
597
+ // Try create an import declaration just before the old import
598
+ tryCreateImport({
599
+ j,
600
+ base,
601
+ relativeToPackage: oldPackagePath,
602
+ packageName: newPackagePath,
603
+ });
604
+
605
+ if (option.type === 'keep-name') {
606
+ const newSpecifier: ImportSpecifier | ImportDefaultSpecifier = (() => {
607
+ if (option.behaviour === 'keep-as-named-import') {
608
+ if (existingAlias) {
609
+ return j.importSpecifier(
610
+ j.identifier(desiredName),
611
+ j.identifier(existingAlias),
612
+ );
613
+ }
614
+
615
+ return j.importSpecifier(j.identifier(desiredName));
616
+ }
617
+
618
+ // moving to default specifier
619
+ return j.importDefaultSpecifier(
620
+ j.identifier(existingAlias || desiredName),
621
+ );
622
+ })();
623
+
624
+ // We don't need to touch anything else in the file
625
+
626
+ addToImport({
627
+ j,
628
+ base,
629
+ importSpecifier: newSpecifier,
630
+ packageName: newPackagePath,
631
+ });
632
+ return;
633
+ }
634
+
635
+ const isNewNameAvailable: boolean =
636
+ base.find(j.Identifier).filter((i) => i.value.name === option.newName)
637
+ .length === 0;
638
+
639
+ const newSpecifier: ImportSpecifier = (() => {
640
+ if (existingAlias) {
641
+ return j.importSpecifier(
642
+ j.identifier(desiredName),
643
+ j.identifier(existingAlias),
644
+ );
645
+ }
646
+
647
+ if (isNewNameAvailable) {
648
+ return j.importSpecifier(j.identifier(desiredName));
649
+ }
650
+
651
+ // new type name is not available: need to use a new alias
652
+ return j.importSpecifier(
653
+ j.identifier(desiredName),
654
+ j.identifier(option.fallbackNameAlias),
655
+ );
656
+ })();
657
+
658
+ addToImport({
659
+ j,
660
+ base,
661
+ importSpecifier: newSpecifier,
662
+ packageName: newPackagePath,
663
+ });
664
+
665
+ // Change usages of old type in file
666
+ base
667
+ .find(j.Identifier)
668
+ .filter((identifier) => identifier.value.name === option.oldName)
669
+ .replaceWith(
670
+ j.identifier(
671
+ isNewNameAvailable ? option.newName : option.fallbackNameAlias,
672
+ ),
673
+ );
674
+ }