UNPKG

@swell/cli

Version:

Swell's command line interface/utility

164 lines (163 loc) 6.52 kB
import { Flags } from '@oclif/core'; import { findUp } from 'find-up'; import { execSync, spawn } from 'node:child_process'; import fs from 'node:fs'; import ora from 'ora'; import { default as localConfig } from '../../lib/config.js'; import style from '../../lib/style.js'; import { ThemeSync } from '../../lib/theme-sync.js'; import { PushAppCommand } from '../../push-app-command.js'; export default class AppThemeDev extends PushAppCommand { static examples = [ 'swell theme dev', 'swell theme dev --local', 'swell theme dev --bundle', 'swell theme dev --storefront-select', 'swell theme dev --storefront-id <id>', ]; static flags = { bundle: Flags.boolean({ description: 'bundle assets', }), force: Flags.boolean({ default: false, description: 'force push all configurations', }), local: Flags.boolean({ description: 'connect to a running local storefront app dev server', }), 'no-push': Flags.boolean({ description: 'skip pushing theme files initially', }), 'storefront-id': Flags.string({ description: 'identify a storefront to preview and push theme files to', }), 'storefront-select': Flags.boolean({ default: false, description: 'prompt to select a storefront to preview and push theme files to', }), env: Flags.string({ char: 'e', description: 'target environment to push to, defaults to live', }), }; static summary = `Run a theme in dev mode from your local machine.`; appType = 'theme'; logAppLocalUrl(storeId, sessionId) { this.log(`View it at ${style.link(this.localFrontendUrl(storeId, sessionId))}\n`); } async run() { const { flags } = await this.parse(AppThemeDev); const { force, local } = flags; const noPush = flags['no-push']; await this.ensureLoggedIn(); // Set env from default storefront first const defaultStorefront = localConfig.getDefaultStorefront(this.appPath); await this.setStorefrontEnv(flags, defaultStorefront); if (!(await this.ensureAppExists(undefined, false))) { return; } if (local) { const currentStore = localConfig.getDefaultStore(); const sessionId = localConfig.getSessionId(currentStore); this.storefrontLocalUrl = this.localFrontendUrl(currentStore, sessionId); } await this.getStorefrontToPush(flags); await this.logOrientation({ env: this.api.envId || 'live' }); this.logStorefrontConnected(); this.saveCurrentStorefront(); if (!noPush) { await this.pushAppConfigs(force); } await this.startThemeDevServer(local); } async findAndInstallDependencies() { const spinner = ora('Locating package.json...').start(); try { const packageJsonLockPath = await findUp('package-lock.json', { type: 'file', }); if (packageJsonLockPath) { spinner.succeed('package-lock.json found. Skipping npm install.'); return null; } const packageJsonPath = await findUp('package.json', { type: 'file' }); if (!packageJsonPath) { spinner.info('package.json not found. Skipping bundle process.'); return null; } spinner.succeed(`Found package.json at ${packageJsonPath}`); spinner.start('Installing dependencies...'); execSync('npm install', { stdio: 'inherit' }); spinner.succeed('Dependencies installed'); return packageJsonPath; } catch { spinner.fail('Error locating package.json. Skipping bundle process.'); return null; } } async findAndTryScriptCommands() { try { const data = await fs.promises.readFile('package.json', 'utf8'); const packageJson = JSON.parse(data); if (!packageJson.scripts) { console.error('No scripts found in package.json. Try adding scripts to your package file.'); return; } if (!packageJson.scripts.bundle) { console.warn('"bundle" script not found in package.json'); return; } const childProcess = spawn('npm', ['run', 'bundle'], { env: { ...process.env, BROWSERSLIST_IGNORE_OLD_DATA: 'true' }, shell: true, stdio: 'pipe', }); childProcess.stdout.on('data', (data) => { const lines = data.toString().split(/\r?\n/); for (const line of lines) { const trimmedLine = line.trim(); if (trimmedLine) { console.log(trimmedLine); } } }); childProcess.on('exit', (code) => { if (code === 0) { console.log(`bundle completed successfully.`); } else { console.error(`bundle exited with code ${code}.`); } }); } catch (error) { console.error('Error processing package.json:', error); } } async startThemeDevServer(local) { const currentStore = localConfig.getDefaultStore(); const spinner = ora(); const bundle = process.argv.includes('--bundle'); if (bundle) { await this.findAndInstallDependencies(); await this.findAndTryScriptCommands(); } spinner.start(`Starting theme dev server...`); const storefrontUrl = local ? this.storefrontLocalUrl : this.storefrontFrontendUrl(currentStore, this.storefront, undefined, true); // Create and start theme sync const themeSync = new ThemeSync({ targetUrl: storefrontUrl, }); const port = await themeSync.start(); spinner.succeed(`Theme sync server started on port ${port}\n`); await this.watchForChanges({ onChange: () => themeSync.reload(), }); spinner.stop(); this.log(`Watching for changes. View your theme at ${style.link(`http://localhost:${port}`)}\n`); } }