gluestack-ui
Version:
A CLI tool for easily adding components from gluestack to your projects.
439 lines (438 loc) • 20.4 kB
JavaScript
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);
}
}));
});