UNPKG

lucy-cli

Version:
376 lines (321 loc) 14.8 kB
#!/usr/bin/env node --no-warnings import { dirname } from 'path'; import { fileURLToPath } from 'url'; import { existsSync } from 'fs'; import chalk from 'chalk'; import settings from './settings.json' with { type: "json" };; import projectPackageJSON from '../package.json' with { type: "json" };; import { join } from 'path'; import fs from 'fs/promises'; import { init } from './init.js'; import { sync } from './sync.js'; import { runGulp, installPackages, killAllProcesses, cleanupWatchers, createTemplateFolder, updateLucyConfigFromPackageJson } from './helpers.js'; import { prepare } from './prepare.js'; import { spawn, spawnSync } from 'child_process'; import os from 'os'; export type ModulesSettings = { packageRoot: string; targetFolder: string; args: string[]; settings: LucySettings; } export type LucySettings = { modules: { [libName: string]: { url: string; branch: string; path?: string; noCompile?: boolean; }; }; wixSettings: { compilerOptions: { composite: boolean; noEmit: boolean; lib: string[]; jsx: string; }; exclude: string[]; }; initialized: boolean; wixPackages: { [packageName: string]: string; }; devPackages: { [packageName: string]: string; }; scripts: { [commandName: string]: string; }; }; export type ModuleSettings = { packageRoot: string; targetFolder: string; args: string[]; wixConfigPath: string; lucyConfigPath: string; packageJsonPath: string; settings: LucySettings; lockVersion: boolean; force: boolean; veloConfigName: string; } export type ProjectSettings = { // packages?: Record<string, string>; modules?: Record<string, string>; lucySettings?: LucySettings; packageJSON?: Record<string, any>; } export const orange = chalk.hex('#FFA500'); export const blue = chalk.blueBright; export const green = chalk.greenBright; export const red = chalk.redBright; export const yellow = chalk.yellow; export const magenta = chalk.magentaBright; // eslint-disable-next-line @typescript-eslint/naming-convention const __filename = fileURLToPath(import.meta.url); // eslint-disable-next-line @typescript-eslint/naming-convention const __dirname = dirname(__filename); // const cwd = process.cwd(); // const command = `watchman watch-del '${cwd}'`; // killAllProcesses('@wix/cli/bin/wix.cjs'); // Matches processes running the Wix CLI // killAllProcesses('wix:dev'); process.on('exit', (code) => { killAllProcesses('@wix/cli/bin/wix.cjs'); // Matches processes running the Wix CLI killAllProcesses('wix:dev'); cleanupWatchers(); console.log(`🚪 ${magenta.underline('Process exiting with code:')} ${orange(code)}`); }); process.on('SIGINT', () => { console.log(`🐕 ${green.underline('Received Ctrl+C (SIGINT), cleaning up...')}`); killAllProcesses('@wix/cli/bin/wix.cjs'); // Matches processes running the Wix CLI killAllProcesses('wix:dev'); cleanupWatchers(); process.exit(); // Exit explicitly after handling }); process.on('SIGTERM', () => { console.log(`🛑 ${red.underline('Received termination signal (SIGTERM), cleaning up...')}`); killAllProcesses('@wix/cli/bin/wix.cjs'); // Matches processes running the Wix CLI killAllProcesses('wix:dev'); cleanupWatchers(); process.exit(); // Exit explicitly after handling }); process.on('uncaughtException', (error) => { console.error(`💥 ${red.underline('Uncaught Exception:')}`, error); killAllProcesses('@wix/cli/bin/wix.cjs'); // Matches processes running the Wix CLI killAllProcesses('wix:dev'); cleanupWatchers(); process.exit(1); // Exit with an error code }); process.on('unhandledRejection', (reason, promise) => { console.error(`🚨 ${yellow.underline('Unhandled Rejection at:')} ${orange(promise)}`); console.error(`🚨 ${red.underline('Reason:')} ${reason}`); cleanupWatchers(); killAllProcesses('@wix/cli/bin/wix.cjs'); // Matches processes running the Wix CLI killAllProcesses('wix:dev'); cleanupWatchers(); process.exit(1); // Exit with an error code }); /** * Main function * @returns {Promise<void>} */ async function main(): Promise<void> { // INFO: Module settings const moduleSettings: ModuleSettings = { packageRoot: dirname(__dirname), targetFolder: process.cwd(), args: process.argv.slice(2), settings, wixConfigPath: join(process.cwd(), 'wix.config.json'), lucyConfigPath: join(process.cwd(), 'lucy.json'), packageJsonPath: join(process.cwd(), 'package.json'), force: false, lockVersion: false, veloConfigName: 'config.json' } let projectSettings: ProjectSettings = {}; if(moduleSettings.args.includes('version') || moduleSettings.args.includes('-v')){ console.log("🐾" + blue.bold(` => ${projectPackageJSON.version}`)); return; } if (moduleSettings.args.includes('templates')) { const templatesPath = join(os.homedir(), '.lucy-cli'); if (!existsSync(templatesPath)) { console.log((`💩 ${red.underline.bold("=> Lucy templates folder not found at =>")} ${orange(templatesPath)}`)); console.log(chalk.yellow('🐕 Creating templates folder with default template...')); await createTemplateFolder(moduleSettings); } console.log(`🐕 ${blue.underline('Opening templates folder at:')} ${orange(templatesPath)}`); let command: string; switch (process.platform) { case 'darwin': command = 'open'; break; case 'win32': command = 'start'; break; default: command = 'xdg-open'; break; } const child = spawn(command, [templatesPath], { detached: true, stdio: 'ignore' }); child.on('error', (err) => { console.error(`💩 ${red.underline.bold('Failed to open folder:')} ${orange(err.message)}`); }); child.unref(); return; } // Run velo sync if(moduleSettings.args.includes('velo-sync')){ await sync(moduleSettings, projectSettings); return; } if(moduleSettings.args.includes('help') || moduleSettings.args.includes('-h')){ console.log("🦮 " + green.underline.bold(' => Lucy CLI Help')); console.log("Usage: lucy-cli <command> [options]"); console.log("\nCommands:"); console.log("🦮 " + magenta.bold('init') + " : Initializes caontaining a WIX project to enable full TS support"); console.log("🦮 " + magenta.bold('dev') + " : Starts the development environment. This includes setting up any required services for local development."); console.log("🦮 " + magenta.bold('build-prod') + " : Builds the project in production mode, optimizing files for deployment."); console.log("🦮 " + magenta.bold('prepare') + " : Prepares the project by installing packages & initializing git modules, configured in lucy.json"); console.log("🦮 " + magenta.bold('velo-sync') + " : Synchronizes wix collections(velo-sync -h for help)"); console.log("🦮 " + magenta.bold('install') + " : Installs all Wix npm packages listed in the 'lucy.json' file in the project directory."); console.log("🦮 " + magenta.bold('fix') + " : Runs a fix command to resolve common issues in development or production settings."); console.log("🦮 " + magenta.bold('docs') + " : Generates documentation for the project."); console.log("🦮 " + magenta.bold('cypress') + " : Starts the cypress test runner."); console.log("🦮 " + magenta.bold('templates') + " : Opens the Lucy CLI templates folder."); console.log("🦮 " + magenta.bold('sync-pkgs') + " : Syncs dependencies from package.json to lucy.json."); console.log("🦮 " + magenta.bold('e2e') + " : Starts the cypress test runner in CI mode. first argument is the key second is the build id <e2e <somekey <someID>"); console.log("\nOptions:"); console.log("🦮 " + magenta.bold('-h, help') + " : Displays this help message."); console.log("🦮 " + magenta.bold('-v, version') + " : Displays the current version of Lucy CLI as defined in the project’s package.json."); console.log("🦮 " + magenta.bold('-f, force') + " : Forces specific commands to execute even if they may lead to potential issues."); console.log(" Used for functions like deleting obsolete pages or initializing missing components."); console.log("🦮 " + magenta.bold('-l') + " : Locks package versions to those specified in the configuration file lucy.json"); console.log("\nExamples:"); console.log("🦮 " + magenta.bold('lucy-cli init') + " : Initializes a new Wix project."); console.log("🦮 " + magenta.bold('lucy-cli dev') + " : Starts the development environment."); console.log("🦮 " + magenta.bold('lucy-cli sync') + " : Synchronizes database and settings."); console.log("🦮 " + magenta.bold('lucy-cli install') + " : Installs all Wix npm packages from 'lucy.json'."); console.log("🦮 " + magenta.bold('lucy-cli dev -f') + " : Starts the dev environment with forced settings."); console.log("🦮 " + magenta.bold('lucy-cli install -l') + " : Installs Wix npm packages, respecting locked versions specified in the configuration."); return; } if (!existsSync(moduleSettings.wixConfigPath)) { console.log((`💩 ${red.underline.bold("=> This is not a WIX project =>")} ${orange(moduleSettings.targetFolder)}`)); return; } //INFO: Collect project settings if(moduleSettings.args.includes('-f')) moduleSettings.force = true; if(moduleSettings.args.includes('-l')) moduleSettings.lockVersion = true; if(existsSync(moduleSettings.packageJsonPath)) { const packageJSONraw = await fs.readFile(join(moduleSettings.packageJsonPath), 'utf8'); try { projectSettings.packageJSON = JSON.parse(packageJSONraw); if(moduleSettings.force) { console.log("❗️" + red.underline(' => Forcing')); moduleSettings.force = true; } } catch (parseError) { console.log((`💩 ${red.underline.bold("=> Error parsing package.json =>")} ${orange(parseError)}`)); return; } } if(existsSync(moduleSettings.lucyConfigPath)) { try { const data = await fs.readFile(moduleSettings.lucyConfigPath, 'utf8'); projectSettings.lucySettings = JSON.parse(data); } catch (parseError) { console.log((`💩 ${red.underline.bold("=> Error parsing Lucy.json =>")} ${orange(parseError)}`)); } } else { if(!moduleSettings.args.includes('init')) { return console.log(yellow.underline.bold('🐶 => Project not Initialized! Please initialize using "lucy-cli init"')); }; } if(!projectSettings.lucySettings?.initialized) { if(!moduleSettings.args.includes('init')) { return console.log(yellow.underline.bold('🐶 => Project not Initialized! Please initialize using "lucy-cli init"')); } } if(moduleSettings.args.includes('-l')) moduleSettings.lockVersion = true; console.log("🐕" + magenta.underline(' => Lucy CLI => RUNNING: ' + orange('Press Ctrl+C to stop.'))); // INFO: Run commands if(moduleSettings.args.includes('init')){ if(projectSettings.lucySettings?.initialized && !moduleSettings.force) { console.log((`💩 ${red.underline.bold("=> This project is already initialized =>")} ${orange(moduleSettings.targetFolder)}`)); console.log("🐕" + magenta.underline(' => Use -f to force initialization')); return; } console.log("🐕" + magenta.underline(' => Initializing project')); init(moduleSettings, projectSettings); return; } if(moduleSettings.args.includes('docs')){ const res = spawnSync('yarn docs', { shell: true, stdio: 'inherit' }); if (res.error) { return console.log((`💩 ${red.underline.bold("=> Failed to Docs generated => ")} ${orange(res.error.message)}`)); } return console.log("🐕" + blue.underline(` => Docs generated!`)); } if(moduleSettings.args.includes('cypress')){ const res = spawnSync('yarn cypress', { shell: true, stdio: 'inherit' }); if (res.error) { return console.log((`💩 ${red.underline.bold("=> Failed to start cypress => ")} ${orange(res.error.message)}`)); } return console.log("🐕" + blue.underline(` => Started Cypress`)); } if (moduleSettings.args.includes('e2e')) { // Extract arguments const e2eIndex = moduleSettings.args.indexOf('e2e'); const key = moduleSettings.args[e2eIndex + 1]; const buildId = moduleSettings.args[e2eIndex + 2]; // Validate that both arguments are provided if (!key && !buildId) { console.log(`💩 ${red.underline.bold("=> Missing required arguments:")} ${orange("key")} and ${orange("build ID")}`); process.exit(1); } // Run Cypress with the provided arguments const res = spawnSync(`yarn e2e --key ${key} --ci-build-id ${buildId}`, { shell: true, stdio: 'inherit' }); if (res.error) { console.log(`💩 ${red.underline.bold("=> Failed to start Cypress =>")} ${orange(res.error.message)}`); process.exit(1); } return console.log("🐕 " + blue.underline(`=> Started Cypress successfully`)); } if(moduleSettings.args.includes('prepare')){ await prepare( moduleSettings, projectSettings); return; } if(moduleSettings.args.includes('install')){ if(!projectSettings.lucySettings?.initialized) { console.log((`💩 ${red.underline.bold("=> This project is not initialized =>")} ${orange(moduleSettings.targetFolder)}`)); console.log("🐕" + magenta.underline(' => Use init to initialize')); return; } await installPackages(projectSettings.lucySettings.wixPackages, projectSettings.lucySettings.devPackages, moduleSettings.targetFolder, moduleSettings.lockVersion); return; } if(moduleSettings.args.includes('dev')){ runGulp(moduleSettings, projectSettings, 'dev'); return; } if(moduleSettings.args.includes('build-prod')){ runGulp(moduleSettings, projectSettings, 'build-prod'); return; } if(moduleSettings.args.includes('fix')){ runGulp(moduleSettings, projectSettings, 'fix-wix'); return; } if(moduleSettings.args.includes('sync-pkgs')){ console.log("🐕" + magenta.underline(' => Syncing package.json with lucy.json')); if (!existsSync(moduleSettings.packageJsonPath)) { console.log((`💩 ${red.underline.bold("=> package.json not found at =>")} ${orange(moduleSettings.packageJsonPath)}`)); return; } if (!existsSync(moduleSettings.lucyConfigPath)) { console.log((`💩 ${red.underline.bold("=> lucy.json not found at =>")} ${orange(moduleSettings.lucyConfigPath)}`)); return; } await updateLucyConfigFromPackageJson(moduleSettings.packageJsonPath, moduleSettings.lucyConfigPath); return; } console.log("🐕" + blue.underline.bold(' => Running dev')); runGulp(moduleSettings, projectSettings, 'dev'); } main();