@swell/cli
Version:
Swell's command line interface/utility
109 lines (108 loc) • 4.65 kB
JavaScript
import { Flags } from '@oclif/core';
import getPort, { portNumbers } from 'get-port';
import ngrok from 'ngrok';
import ora from 'ora';
import { default as localConfig } from '../../../lib/config.js';
import style from '../../../lib/style.js';
import { PushAppCommand } from '../../../push-app-command.js';
const FALLBACK_PORT = 3000;
export default class AppFrontendDev extends PushAppCommand {
static examples = [
'swell app frontend dev',
'swell app frontend dev --no-push',
'swell app frontend dev --port 3000',
'swell app frontend dev --storefront-select',
'swell app frontend dev --storefront-id <id>',
];
static flags = {
'no-push': Flags.boolean({
description: 'skip pushing app files initially',
}),
port: Flags.integer({
char: 'p',
description: 'override the default port to run the app locally',
}),
'storefront-id': Flags.string({
description: 'identify a storefront to preview with and push theme files to',
}),
'storefront-select': Flags.boolean({
default: false,
description: 'prompt to select a storefront to preview with',
}),
};
static orientation = {
env: 'test',
};
static summary = `Run a frontend app in dev mode from your local machine.`;
logAppLocalUrl(storeId, sessionId) {
const frontendAppRoute = this.app.type === 'storefront' && this.app.storefront?.external
? `/${this.app.private_id}`
: '';
this.log(`View it at ${style.link(`${this.localFrontendUrl(storeId, sessionId)}${frontendAppRoute}`)}\n`);
}
async run() {
const { flags } = await this.parse(AppFrontendDev);
const { port } = flags;
const noPush = flags['no-push'];
if (!(await this.ensureAppExists(undefined, false))) {
return;
}
if (!noPush) {
await this.pushAppConfigs();
}
if (this.app.type === 'storefront') {
await this.getStorefrontToPush(flags);
this.logStorefrontConnected();
}
this.saveCurrentStorefront();
const projectType = this.getFrontendProjectType();
await this.startDevServer(projectType, port);
}
async startDevServer(projectType, port) {
const currentStore = localConfig.getDefaultStore();
const spinner = ora();
// Find an open port starting at 3000
const freePort = port ||
(await getPort({ port: portNumbers(3000, 3100) })) ||
FALLBACK_PORT;
this.log(`Starting ${projectType.name} dev server...\n`);
// Start ngrok proxy
const proxyUrl = await ngrok.connect({
addr: freePort,
region: 'us', // TODO: make it configurable
});
await this.updateLocalProxy(proxyUrl, spinner, this.storefront?.id);
return this.exec(projectType.devCommand.replace('${PORT}', String(freePort)), (string) => {
// Hide useless astro warning about image optimization
if (string.includes('The current configuration does not support image optimization')) {
return false;
}
// Hide useless astro warning about .dev.vars
if (string.includes('There is no `.dev.vars` file')) {
return false;
}
// Hide astro localhost URL/network as it can confuse the user
if (string.includes('┃ Local') || string.includes('┃ Network')) {
return false;
}
// Started, show app url (TODO for nextjs)
if (string.includes('watching for file changes')) {
spinner.succeed(`Started ${projectType.name} dev server on port ${freePort}.\n`);
const sessionId = localConfig.getSessionId(currentStore);
setTimeout(() => this.logAppLocalUrl(currentStore, sessionId), 100);
this.watchForChanges();
}
});
}
async updateLocalProxy(proxyUrl, spinner, storefrontId) {
const storefront = this.app.type === 'storefront' &&
(await this.getAppStorefront({ storefront_id: storefrontId }));
await this.handleRequestErrors(async () => this.api.put({ adminPath: `/client/apps/${this.app.id}/local-proxy` }, {
body: {
proxy_url: proxyUrl,
storefront_id: storefront?.id || null,
storefront_slug: storefront?.slug || null,
},
}), () => spinner.fail('Error starting local proxy'));
}
}