contrib: Add switch-version tool

Allows for easilly switching between version; either between local or
published or from published to latest published
This commit is contained in:
Dagur Valberg Johannsson 2026-03-06 15:47:43 +01:00
parent 8c02f72eee
commit b855aa5dcb
No known key found for this signature in database
GPG key ID: FD701804AEE88107

180
contrib/switch-versions.mjs Executable file
View file

@ -0,0 +1,180 @@
#!/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();