Skip to content

Commit b84cad2

Browse files
authored
Merge pull request #3234 from gluestack/fix/upgrade-cli
feat: add tests for import path upgrades and enhance upgrade command …
2 parents 3b1281f + 3b00c77 commit b84cad2

File tree

3 files changed

+235
-3
lines changed

3 files changed

+235
-3
lines changed
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
const fs = require('fs-extra');
2+
const path = require('path');
3+
4+
// Mock the updateFileImports function behavior
5+
function updateImportPath(importPath) {
6+
// Special case for nativewind-utils imports
7+
if (importPath.startsWith('@gluestack-ui/nativewind-utils')) {
8+
return '@gluestack-ui/utils/nativewind-utils';
9+
}
10+
11+
// Check if it's a utils import (already in correct format)
12+
if (importPath.startsWith('@gluestack-ui/utils/')) {
13+
// Already in the correct format, don't modify
14+
return importPath;
15+
}
16+
17+
// Check if already upgraded (contains /core/ and ends with /creator)
18+
if (importPath.includes('/core/') && importPath.endsWith('/creator')) {
19+
// Already in the new format, don't modify
20+
return importPath;
21+
}
22+
23+
// Extract component name from import path
24+
const componentName = importPath.replace('@gluestack-ui/', '');
25+
const newImportPath = `@gluestack-ui/core/${componentName}/creator`;
26+
return newImportPath;
27+
}
28+
29+
describe('Upgrade command - Import transformation', () => {
30+
test('should upgrade old imports correctly', () => {
31+
expect(updateImportPath('@gluestack-ui/image')).toBe('@gluestack-ui/core/image/creator');
32+
expect(updateImportPath('@gluestack-ui/button')).toBe('@gluestack-ui/core/button/creator');
33+
expect(updateImportPath('@gluestack-ui/input')).toBe('@gluestack-ui/core/input/creator');
34+
});
35+
36+
test('should not duplicate nested paths on multiple upgrades', () => {
37+
// First upgrade
38+
const firstUpgrade = updateImportPath('@gluestack-ui/image');
39+
expect(firstUpgrade).toBe('@gluestack-ui/core/image/creator');
40+
41+
// Second upgrade - should not change
42+
const secondUpgrade = updateImportPath(firstUpgrade);
43+
expect(secondUpgrade).toBe('@gluestack-ui/core/image/creator');
44+
45+
// Third upgrade - should still not change
46+
const thirdUpgrade = updateImportPath(secondUpgrade);
47+
expect(thirdUpgrade).toBe('@gluestack-ui/core/image/creator');
48+
});
49+
50+
test('should handle nativewind-utils imports correctly', () => {
51+
expect(updateImportPath('@gluestack-ui/nativewind-utils')).toBe('@gluestack-ui/utils/nativewind-utils');
52+
53+
// Should not change if already upgraded
54+
expect(updateImportPath('@gluestack-ui/utils/nativewind-utils')).toBe('@gluestack-ui/utils/nativewind-utils');
55+
});
56+
57+
test('should not modify utils imports', () => {
58+
expect(updateImportPath('@gluestack-ui/utils/common')).toBe('@gluestack-ui/utils/common');
59+
expect(updateImportPath('@gluestack-ui/utils/hooks')).toBe('@gluestack-ui/utils/hooks');
60+
});
61+
62+
test('should handle various component names', () => {
63+
const components = [
64+
'accordion',
65+
'action-sheet',
66+
'alert',
67+
'alert-dialog',
68+
'avatar',
69+
'button',
70+
'checkbox',
71+
'divider',
72+
'fab',
73+
'form-control',
74+
'icon',
75+
'image',
76+
'input',
77+
'link',
78+
'menu',
79+
'modal',
80+
'popover',
81+
'pressable',
82+
'progress',
83+
'radio',
84+
'select',
85+
'slider',
86+
'spinner',
87+
'switch',
88+
'textarea',
89+
'toast',
90+
'tooltip'
91+
];
92+
93+
components.forEach(component => {
94+
const oldImport = `@gluestack-ui/${component}`;
95+
const newImport = updateImportPath(oldImport);
96+
expect(newImport).toBe(`@gluestack-ui/core/${component}/creator`);
97+
98+
// Verify it doesn't change on subsequent upgrades
99+
expect(updateImportPath(newImport)).toBe(newImport);
100+
});
101+
});
102+
});
103+
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
const fs = require('fs-extra');
2+
const path = require('path');
3+
4+
// Mock the updateFileImports function behavior
5+
function updateImportPath(importPath) {
6+
// Special case for nativewind-utils imports
7+
if (importPath.startsWith('@gluestack-ui/nativewind-utils')) {
8+
return '@gluestack-ui/utils/nativewind-utils';
9+
}
10+
11+
// Check if it's a utils import (already in correct format)
12+
if (importPath.startsWith('@gluestack-ui/utils/')) {
13+
// Already in the correct format, don't modify
14+
return importPath;
15+
}
16+
17+
// Check if already upgraded (contains /core/ and ends with /creator)
18+
if (importPath.includes('/core/') && importPath.endsWith('/creator')) {
19+
// Already in the new format, don't modify
20+
return importPath;
21+
}
22+
23+
// Extract component name from import path
24+
const componentName = importPath.replace('@gluestack-ui/', '');
25+
const newImportPath = `@gluestack-ui/core/${componentName}/creator`;
26+
return newImportPath;
27+
}
28+
29+
describe('Upgrade command - Import transformation', () => {
30+
test('should upgrade old imports correctly', () => {
31+
expect(updateImportPath('@gluestack-ui/image')).toBe(
32+
'@gluestack-ui/core/image/creator'
33+
);
34+
expect(updateImportPath('@gluestack-ui/button')).toBe(
35+
'@gluestack-ui/core/button/creator'
36+
);
37+
expect(updateImportPath('@gluestack-ui/input')).toBe(
38+
'@gluestack-ui/core/input/creator'
39+
);
40+
});
41+
42+
test('should not duplicate nested paths on multiple upgrades', () => {
43+
// First upgrade
44+
const firstUpgrade = updateImportPath('@gluestack-ui/image');
45+
expect(firstUpgrade).toBe('@gluestack-ui/core/image/creator');
46+
47+
// Second upgrade - should not change
48+
const secondUpgrade = updateImportPath(firstUpgrade);
49+
expect(secondUpgrade).toBe('@gluestack-ui/core/image/creator');
50+
51+
// Third upgrade - should still not change
52+
const thirdUpgrade = updateImportPath(secondUpgrade);
53+
expect(thirdUpgrade).toBe('@gluestack-ui/core/image/creator');
54+
});
55+
56+
test('should handle nativewind-utils imports correctly', () => {
57+
expect(updateImportPath('@gluestack-ui/nativewind-utils')).toBe(
58+
'@gluestack-ui/utils/nativewind-utils'
59+
);
60+
61+
// Should not change if already upgraded
62+
expect(updateImportPath('@gluestack-ui/utils/nativewind-utils')).toBe(
63+
'@gluestack-ui/utils/nativewind-utils'
64+
);
65+
});
66+
67+
test('should not modify utils imports', () => {
68+
expect(updateImportPath('@gluestack-ui/utils/common')).toBe(
69+
'@gluestack-ui/utils/common'
70+
);
71+
expect(updateImportPath('@gluestack-ui/utils/hooks')).toBe(
72+
'@gluestack-ui/utils/hooks'
73+
);
74+
});
75+
76+
test('should handle various component names', () => {
77+
const components = [
78+
'accordion',
79+
'action-sheet',
80+
'alert',
81+
'alert-dialog',
82+
'avatar',
83+
'button',
84+
'checkbox',
85+
'divider',
86+
'fab',
87+
'form-control',
88+
'icon',
89+
'image',
90+
'input',
91+
'link',
92+
'menu',
93+
'modal',
94+
'popover',
95+
'pressable',
96+
'progress',
97+
'radio',
98+
'select',
99+
'slider',
100+
'spinner',
101+
'switch',
102+
'textarea',
103+
'toast',
104+
'tooltip',
105+
];
106+
107+
components.forEach((component) => {
108+
const oldImport = `@gluestack-ui/${component}`;
109+
const newImport = updateImportPath(oldImport);
110+
expect(newImport).toBe(`@gluestack-ui/core/${component}/creator`);
111+
112+
// Verify it doesn't change on subsequent upgrades
113+
expect(updateImportPath(newImport)).toBe(newImport);
114+
});
115+
});
116+
});

packages/gluestack-ui/src/commands/upgrade.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Command } from 'commander';
2-
import { log, spinner, confirm, isCancel, cancel,text } from '@clack/prompts';
2+
import { log, spinner, confirm, isCancel, cancel, text } from '@clack/prompts';
33
import fs from 'fs-extra';
44
import path from 'path';
55
import { spawnSync } from 'child_process';
@@ -302,7 +302,8 @@ async function updateImports(): Promise<void> {
302302
if (!fs.existsSync(componentsPath)) {
303303
s.stop('No components/ui folder found.');
304304
const customPath = await text({
305-
message: 'Please provide the path to your components folder (relative to project root):',
305+
message:
306+
'Please provide the path to your components folder (relative to project root):',
306307
placeholder: 'e.g., src/components, lib/components, app/components',
307308
validate: (value) => {
308309
if (!value) return 'Path is required';
@@ -311,7 +312,7 @@ async function updateImports(): Promise<void> {
311312
return `Path "${value}" does not exist`;
312313
}
313314
return;
314-
}
315+
},
315316
});
316317

317318
if (isCancel(customPath)) {
@@ -385,6 +386,18 @@ async function updateFileImports(filePath: string): Promise<boolean> {
385386
return `from '@gluestack-ui/utils/nativewind-utils'`;
386387
}
387388

389+
// Check if it's a utils import (already in correct format)
390+
if (importPath.startsWith('@gluestack-ui/utils/')) {
391+
// Already in the correct format, don't modify
392+
return match;
393+
}
394+
395+
// Check if already upgraded (contains /core/ and ends with /creator)
396+
if (importPath.includes('/core/') && importPath.endsWith('/creator')) {
397+
// Already in the new format, don't modify
398+
return match;
399+
}
400+
388401
// Extract component name from import path
389402
const componentName = importPath.replace('@gluestack-ui/', '');
390403
const newImportPath = `@gluestack-ui/core/${componentName}/creator`;

0 commit comments

Comments
 (0)