UNPKG

gluestack-ui

Version:

A CLI tool for easily adding components from gluestack to your projects.

439 lines (438 loc) 20.4 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; (function (factory) { if (typeof module === "object" && typeof module.exports === "object") { var v = factory(require, exports); if (v !== undefined) module.exports = v; } else if (typeof define === "function" && define.amd) { define(["require", "exports", "commander", "@clack/prompts", "fs-extra", "path", "child_process", "simple-git"], factory); } })(function (require, exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.upgrade = void 0; const commander_1 = require("commander"); const prompts_1 = require("@clack/prompts"); const fs_extra_1 = __importDefault(require("fs-extra")); const path_1 = __importDefault(require("path")); const child_process_1 = require("child_process"); const simple_git_1 = __importDefault(require("simple-git")); // Utility: Is this an old gluestack package? function isOldGluestackPackage(pkg) { return (!pkg.includes('nightly') && (pkg.startsWith('@gluestack-ui') || pkg.startsWith('gluestack') || pkg.startsWith('@gluestack'))); } // Detect old gluestack packages in package.json function detectOldPackages() { return __awaiter(this, void 0, void 0, function* () { const packageJsonPath = path_1.default.join(process.cwd(), 'package.json'); if (!fs_extra_1.default.existsSync(packageJsonPath)) throw new Error('No package.json found'); const pkgJson = yield fs_extra_1.default.readJSON(packageJsonPath); const allDeps = Object.assign(Object.assign({}, pkgJson.dependencies), pkgJson.devDependencies); return Object.keys(allDeps).filter(isOldGluestackPackage); }); } // Check for uncommitted git changes function hasUncommittedChanges() { return __awaiter(this, void 0, void 0, function* () { try { const git = (0, simple_git_1.default)(process.cwd()); const status = yield git.status(); return status.files.length > 0; } catch (_a) { return false; } }); } // Remove old packages function removePackages(packages, packageManager) { if (!packages.length) return; const s = (0, prompts_1.spinner)(); s.start('Removing old gluestack packages...'); const cmds = { npm: 'npm uninstall', yarn: 'yarn remove', pnpm: 'pnpm remove', bun: 'bun remove', }; const cmd = cmds[packageManager]; if (!cmd) throw new Error('Unsupported package manager'); const result = (0, child_process_1.spawnSync)(cmd, packages, { cwd: process.cwd(), stdio: 'inherit', shell: true, }); if (result.error || result.status !== 0) throw new Error('Failed to remove packages'); s.stop('Old packages removed.'); } // Clean node_modules and reinstall dependencies function cleanAndReinstall(packageManager) { const s = (0, prompts_1.spinner)(); s.start('Cleaning node_modules and reinstalling dependencies...'); const nodeModulesPath = path_1.default.join(process.cwd(), 'node_modules'); const lockFiles = { npm: 'package-lock.json', yarn: 'yarn.lock', pnpm: 'pnpm-lock.yaml', bun: 'bun.lockb', }; try { // Remove node_modules if (fs_extra_1.default.existsSync(nodeModulesPath)) { fs_extra_1.default.removeSync(nodeModulesPath); } // Remove lock file const lockFile = lockFiles[packageManager]; if (lockFile && fs_extra_1.default.existsSync(path_1.default.join(process.cwd(), lockFile))) { fs_extra_1.default.removeSync(path_1.default.join(process.cwd(), lockFile)); } // Reinstall all dependencies const installCmds = { npm: 'npm install', yarn: 'yarn install', pnpm: 'pnpm install', bun: 'bun install', }; const installCmd = installCmds[packageManager]; const result = (0, child_process_1.spawnSync)(installCmd, [], { cwd: process.cwd(), stdio: 'inherit', shell: true, }); if (result.error || result.status !== 0) throw new Error('Failed to reinstall dependencies'); s.stop('Dependencies reinstalled successfully.'); } catch (error) { s.stop('Failed to clean and reinstall.'); throw error; } } // Install new packages function installPackages(packageManager) { const s = (0, prompts_1.spinner)(); s.start('Installing @gluestack-ui/core and @gluestack-ui/utils...'); const cmds = { npm: 'npm install', yarn: 'yarn add', pnpm: 'pnpm i', bun: 'bun add', }; const cmd = cmds[packageManager]; if (!cmd) throw new Error('Unsupported package manager'); const pkgs = [ '@gluestack-ui/core@3.0.10', '@gluestack-ui/utils@3.0.7', 'react-native-svg@15.13.0', '@gluestack/ui-next-adapter@3.0.3', ]; const result = (0, child_process_1.spawnSync)(cmd, pkgs, { cwd: process.cwd(), stdio: 'inherit', shell: true, }); if (result.error || result.status !== 0) throw new Error('Failed to install new packages'); s.stop('New packages installed.'); } // Update registry.tsx file in app folder (Next.js 15) function updateRegistryFile() { return __awaiter(this, void 0, void 0, function* () { const s = (0, prompts_1.spinner)(); s.start('Updating registry.tsx...'); const registryPath = path_1.default.join(process.cwd(), 'app', 'registry.tsx'); if (!fs_extra_1.default.existsSync(registryPath)) { s.stop('No app/registry.tsx found.'); return; } try { const content = yield fs_extra_1.default.readFile(registryPath, 'utf8'); let updated = false; let newContent = content; // Replace the flush import const flushImportRegex = /import\s+\{\s*flush\s*\}\s+from\s+['"]@gluestack-ui\/nativewind-utils\/flush['"];?\s*/g; if (flushImportRegex.test(newContent)) { newContent = newContent.replace(flushImportRegex, `import { flush } from "@gluestack-ui/utils/nativewind-utils";\n`); updated = true; } if (updated) { yield fs_extra_1.default.writeFile(registryPath, newContent, 'utf8'); prompts_1.log.info(`Updated app/registry.tsx`); } s.stop('Registry file updated.'); } catch (error) { s.stop('Failed to update registry file.'); prompts_1.log.warning(`Failed to update app/registry.tsx: ${error}`); } }); } // Update tailwind config files to remove old gluestack plugin function updateTailwindConfig() { return __awaiter(this, void 0, void 0, function* () { const s = (0, prompts_1.spinner)(); s.start('Updating tailwind config files...'); const tailwindConfigPaths = [ path_1.default.join(process.cwd(), 'tailwind.config.ts'), path_1.default.join(process.cwd(), 'tailwind.config.js'), ]; let updatedAny = false; for (const tailwindConfigPath of tailwindConfigPaths) { if (!fs_extra_1.default.existsSync(tailwindConfigPath)) { continue; } try { const content = yield fs_extra_1.default.readFile(tailwindConfigPath, 'utf8'); let updated = false; let newContent = content; // Remove the import statement const importRegex = /import\s+gluestackPlugin\s+from\s+['"]@gluestack-ui\/nativewind-utils\/tailwind-plugin['"];?\s*/g; if (importRegex.test(newContent)) { newContent = newContent.replace(importRegex, ''); updated = true; } // Remove the plugin from the plugins array const pluginRegex = /plugins:\s*\[([^\]]*gluestackPlugin[^\]]*)\]/g; newContent = newContent.replace(pluginRegex, (match, pluginsContent) => { // Remove gluestackPlugin from the plugins array const updatedPlugins = pluginsContent .split(',') .map((plugin) => plugin.trim()) .filter((plugin) => !plugin.includes('gluestackPlugin')) .join(', '); updated = true; return `plugins: [${updatedPlugins}]`; }); // Clean up empty plugins array newContent = newContent.replace(/plugins:\s*\[\s*\]/g, 'plugins: []'); if (updated) { yield fs_extra_1.default.writeFile(tailwindConfigPath, newContent, 'utf8'); const fileName = path_1.default.basename(tailwindConfigPath); prompts_1.log.info(`Updated ${fileName}`); updatedAny = true; } } catch (error) { const fileName = path_1.default.basename(tailwindConfigPath); prompts_1.log.warning(`Failed to update ${fileName}: ${error}`); } } if (updatedAny) { s.stop('Tailwind config files updated.'); } else { s.stop('No tailwind config files found or updated.'); } }); } // Update Next.js config files function updateNextConfig() { return __awaiter(this, void 0, void 0, function* () { const s = (0, prompts_1.spinner)(); s.start('Updating Next.js config files...'); const nextConfigPaths = [ path_1.default.join(process.cwd(), 'next.config.ts'), path_1.default.join(process.cwd(), 'next.config.js'), path_1.default.join(process.cwd(), 'next.config.mjs'), ]; let updatedFiles = 0; for (const configPath of nextConfigPaths) { if (!fs_extra_1.default.existsSync(configPath)) { continue; } try { const content = yield fs_extra_1.default.readFile(configPath, 'utf8'); let updated = false; let newContent = content; // Replace the old import statement const importRegex = /import\s+\{\s*withGluestackUI\s*\}\s+from\s+['"]@gluestack\/ui-next-adapter['"];?\s*/g; if (importRegex.test(newContent)) { newContent = newContent.replace(importRegex, `import { withGluestackUI } from "@gluestack/ui-next-adapter";\n`); updated = true; } if (updated) { yield fs_extra_1.default.writeFile(configPath, newContent, 'utf8'); const fileName = path_1.default.basename(configPath); prompts_1.log.info(`Updated ${fileName}`); updatedFiles++; } } catch (error) { const fileName = path_1.default.basename(configPath); prompts_1.log.warning(`Failed to update ${fileName}: ${error}`); } } if (updatedFiles > 0) { s.stop('Next.js config files updated.'); } else { s.stop('No Next.js config files found or updated.'); } }); } // Update import statements in components/ui folder function updateImports() { return __awaiter(this, void 0, void 0, function* () { const s = (0, prompts_1.spinner)(); s.start('Updating import statements...'); let componentsPath = path_1.default.join(process.cwd(), 'components', 'ui'); if (!fs_extra_1.default.existsSync(componentsPath)) { s.stop('No components/ui folder found.'); const customPath = yield (0, prompts_1.text)({ message: 'Please provide the path to your components folder (relative to project root):', placeholder: 'e.g., src/components, lib/components, app/components', validate: (value) => { if (!value) return 'Path is required'; const fullPath = path_1.default.join(process.cwd(), value); if (!fs_extra_1.default.existsSync(fullPath)) { return `Path "${value}" does not exist`; } return; } }); if ((0, prompts_1.isCancel)(customPath)) { (0, prompts_1.cancel)('Upgrade cancelled.'); process.exit(0); } componentsPath = path_1.default.join(process.cwd(), customPath); s.start('Updating import statements...'); } let updatedFiles = 0; // Recursively find all TypeScript/JavaScript files const files = yield fs_extra_1.default.readdir(componentsPath); for (const file of files) { const filePath = path_1.default.join(componentsPath, file); const stat = yield fs_extra_1.default.stat(filePath); if (stat.isDirectory()) { // Recursively process subdirectories yield processDirectory(filePath); } else if (file.endsWith('.ts') || file.endsWith('.tsx') || file.endsWith('.js') || file.endsWith('.jsx')) { const updated = yield updateFileImports(filePath); if (updated) updatedFiles++; } } s.stop(`Updated ${updatedFiles} files.`); }); } // Process a directory recursively function processDirectory(dirPath) { return __awaiter(this, void 0, void 0, function* () { const files = yield fs_extra_1.default.readdir(dirPath); for (const file of files) { const filePath = path_1.default.join(dirPath, file); const stat = yield fs_extra_1.default.stat(filePath); if (stat.isDirectory()) { yield processDirectory(filePath); } else if (file.endsWith('.ts') || file.endsWith('.tsx') || file.endsWith('.js') || file.endsWith('.jsx')) { yield updateFileImports(filePath); } } }); } // Update imports in a single file function updateFileImports(filePath) { return __awaiter(this, void 0, void 0, function* () { try { const content = yield fs_extra_1.default.readFile(filePath, 'utf8'); let updated = false; let newContent = content; // Regex to match imports from @gluestack-ui packages const importRegex = /from\s+['"](@gluestack-ui\/[^'"]+)['"]/g; newContent = newContent.replace(importRegex, (match, importPath) => { // Special case for nativewind-utils imports if (importPath.startsWith('@gluestack-ui/nativewind-utils')) { updated = true; return `from '@gluestack-ui/utils/nativewind-utils'`; } // Extract component name from import path const componentName = importPath.replace('@gluestack-ui/', ''); const newImportPath = `@gluestack-ui/core/${componentName}/creator`; updated = true; return `from '${newImportPath}'`; }); if (updated) { yield fs_extra_1.default.writeFile(filePath, newContent, 'utf8'); prompts_1.log.info(`Updated imports in: ${path_1.default.relative(process.cwd(), filePath)}`); } return updated; } catch (error) { prompts_1.log.warning(`Failed to update file ${filePath}: ${error}`); return false; } }); } exports.upgrade = new commander_1.Command() .name('upgrade') .description('Upgrade from old gluestack packages to gluestack-ui') .action(() => __awaiter(void 0, void 0, void 0, function* () { try { prompts_1.log.info('\n\x1b[1mGluestack UI Upgrade\x1b[0m'); const oldPackages = yield detectOldPackages(); if (!oldPackages.length) { prompts_1.log.info('No old gluestack packages found.'); return; } prompts_1.log.info('Found old packages:'); oldPackages.forEach((pkg) => prompts_1.log.info(' - ' + pkg)); if (yield hasUncommittedChanges()) { prompts_1.log.warning('You have uncommitted git changes. Please commit before upgrading.'); const proceed = yield (0, prompts_1.confirm)({ message: 'Continue anyway?' }); if ((0, prompts_1.isCancel)(proceed) || !proceed) { (0, prompts_1.cancel)('Upgrade cancelled.'); process.exit(0); } } // Detect package manager let packageManager = 'npm'; if (fs_extra_1.default.existsSync('yarn.lock')) packageManager = 'yarn'; else if (fs_extra_1.default.existsSync('pnpm-lock.yaml')) packageManager = 'pnpm'; else if (fs_extra_1.default.existsSync('bun.lockb')) packageManager = 'bun'; yield updateTailwindConfig(); installPackages(packageManager); removePackages(oldPackages, packageManager); cleanAndReinstall(packageManager); yield updateRegistryFile(); yield updateNextConfig(); yield updateImports(); prompts_1.log.success('\x1b[32mUpgrade complete!\x1b[0m'); prompts_1.log.info('All imports have been updated to use @gluestack-ui/*'); } catch (err) { prompts_1.log.error((err && err.message) || String(err)); process.exit(1); } })); });