#!/usr/bin/env node /** * Switch between local and published versions of @wizardconnect packages * * Usage: * node /path/to/wizardconnect/contrib/switch-versions.js [local|published] * * This script modifies package.json in the current working directory to switch * between local file: references and published npm versions of @wizardconnect packages. */ import { readFileSync, writeFileSync, existsSync, readdirSync } from 'fs'; import { resolve, dirname } from 'path'; import { fileURLToPath } from 'url'; import { execSync } from 'child_process'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); // Get the wizardconnect root directory (one level up from contrib) const LIB_ROOT = resolve(__dirname, '..'); const PACKAGE_SCOPE = '@wizardconnect/'; // Dynamically discover packages from workspace configuration function discoverPackages() { const packages = {}; try { const rootPackageJsonPath = resolve(LIB_ROOT, 'package.json'); const rootPackageJson = JSON.parse(readFileSync(rootPackageJsonPath, 'utf8')); if (rootPackageJson.workspaces) { for (const workspace of rootPackageJson.workspaces) { const workspacePattern = typeof workspace === 'string' ? workspace : workspace.package; const baseDir = workspacePattern.split('*')[0].replace(/\/$/, ''); const baseDirPath = resolve(LIB_ROOT, baseDir); if (existsSync(baseDirPath)) { const entries = readdirSync(baseDirPath, { withFileTypes: true }); for (const entry of entries) { if (entry.isDirectory()) { const workspacePath = `${baseDir}/${entry.name}`; const packageJsonPath = resolve(LIB_ROOT, workspacePath, 'package.json'); if (existsSync(packageJsonPath)) { const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8')); if (packageJson.name && packageJson.name.startsWith(PACKAGE_SCOPE)) { packages[packageJson.name] = workspacePath; } } } } } } } } catch (error) { console.error('Error: Could not read workspace configuration:', error.message); process.exit(1); } return packages; } function getLatestPublishedVersions(packages) { const versions = {}; for (const packageName of Object.keys(packages)) { try { console.log(`Querying npm for latest version of ${packageName}...`); const result = execSync(`npm view ${packageName} version`, { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }); const latestVersion = result.trim(); versions[packageName] = latestVersion; console.log(` Found latest version: ${packageName}@${latestVersion}`); } catch (error) { console.warn(` Could not get latest version for ${packageName}: ${error.message}`); // Fall back to local version if npm query fails const packagePath = packages[packageName]; const packageJsonPath = resolve(LIB_ROOT, packagePath, 'package.json'); if (existsSync(packageJsonPath)) { const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8')); versions[packageName] = packageJson.version; console.log(` Using local version: ${packageName}@${packageJson.version}`); } } } return versions; } function switchDeps(deps, packages, mode, versions) { let changed = false; if (!deps) return changed; for (const [packageName, localPath] of Object.entries(packages)) { if (!deps[packageName]) continue; if (mode === 'local') { const absolutePath = resolve(LIB_ROOT, localPath); deps[packageName] = `file:${absolutePath}`; changed = true; console.log(` ${packageName} -> file:${absolutePath}`); } else { const currentValue = deps[packageName]; const newValue = `^${versions[packageName]}`; if (currentValue !== newValue) { deps[packageName] = newValue; changed = true; console.log(` ${packageName} -> ${newValue}`); } } } return changed; } function main() { const args = process.argv.slice(2); const mode = args[0]; if (!mode || !['local', 'published'].includes(mode)) { console.error('Usage: node switch-versions.js [local|published]'); console.error(''); console.error(' local - Switch to local file: references'); console.error(' published - Switch to latest published npm versions'); process.exit(1); } const packageJsonPath = resolve(process.cwd(), 'package.json'); if (!existsSync(packageJsonPath)) { console.error('Error: No package.json found in current directory'); console.error('Please run this script from a project directory that contains package.json'); process.exit(1); } try { const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8')); const packages = discoverPackages(); if (Object.keys(packages).length === 0) { console.error('Error: No @wizardconnect packages found in workspace'); process.exit(1); } console.log(`Found packages: ${Object.keys(packages).join(', ')}`); console.log(''); let versions = {}; if (mode === 'published') { versions = getLatestPublishedVersions(packages); console.log(''); } console.log(`Switching to ${mode} versions:`); const changed = switchDeps(packageJson.dependencies, packages, mode, versions) | switchDeps(packageJson.devDependencies, packages, mode, versions); if (!changed) { console.log('No @wizardconnect packages found to switch'); return; } writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n'); console.log(''); console.log(`Successfully switched to ${mode} versions`); console.log('Remember to run "npm install" to update your node_modules'); } catch (error) { console.error('Error:', error.message); process.exit(1); } } main();