@swell/cli
Version:
Swell's command line interface/utility
178 lines (177 loc) • 8.69 kB
JavaScript
import { Flags } from '@oclif/core';
import { default as localConfig } from '../../../lib/config.js';
import style from '../../../lib/style.js';
import { PushAppCommand } from '../../../push-app-command.js';
import AppDev from '../dev.js';
export default class AppFrontendDev extends PushAppCommand {
static examples = [
'swell app frontend dev',
'swell app frontend dev --no-push',
'swell app frontend dev --proxy-port 3000',
'swell app frontend dev --storefront-select',
'swell app frontend dev --storefront-id <id>',
];
static flags = {
...AppDev.flags,
'proxy-port': Flags.integer({
description: 'specify the port for an existing frontend proxy',
default: 3001,
}),
'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',
}),
'app-dev': Flags.boolean({
description: 'indicates frontend app is running in app dev mode',
default: true,
}),
};
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, 'proxy-port': proxyPort, 'app-dev': isAppDev } = flags;
const noPush = flags['no-push'];
if (!(await this.ensureAppExists(undefined, false))) {
return;
}
if (!noPush && !isAppDev) {
await this.pushAppConfigs();
}
if (this.app.type === 'storefront') {
await this.getStorefrontToPush(flags);
this.logStorefrontConnected();
}
this.saveCurrentStorefront();
if (!isAppDev) {
this.log(`Starting app dev server...\n`);
}
const serverPort = await this.startProxyServer(proxyPort || port);
await this.execFrontendProject(serverPort, isAppDev);
}
async execFrontendProject(serverPort, isAppDev) {
const projectType = this.getFrontendProjectType(!isAppDev);
if (!projectType) {
return;
}
const currentStore = localConfig.getDefaultStore();
let serverReadyDetected = false;
await this.execFrontend(projectType.devCommand.replace('${PORT}', String(serverPort)), (string) => {
// Framework-specific output handling and ready detection
switch (projectType.slug) {
case 'nextjs': {
// 1. NOISE FILTER: Hide unwanted output
if (string.includes('Using vars defined in .dev.vars')) {
return false;
}
// 2. URL FILTER: Hide localhost/network URLs (confusing - users should use Swell tunnel)
if (/- local:\s+http:\/\//i.test(string) ||
/- network:\s+http:\/\//i.test(string) ||
/ready on http:\/\/localhost/i.test(string)) {
return false;
}
// 3. READY DETECTION: Detect server ready, show our message
if (!serverReadyDetected &&
(/✓ ready in \d+/.test(string) || /✓ starting/.test(string))) {
serverReadyDetected = true;
this.log(`Started ${projectType.name} dev server on port ${serverPort}.\n`);
const sessionId = localConfig.getSessionId(currentStore);
setTimeout(() => this.logAppLocalUrl(currentStore, sessionId), 100);
if (!isAppDev) {
this.watchForChanges();
}
}
break;
}
case 'astro': {
// 2. URL FILTER: Hide localhost/network URLs (confusing - users should use Swell tunnel)
if (string.includes('┃ Local') || string.includes('┃ Network')) {
return false;
}
// 3. READY DETECTION: Detect server ready, show our message
if (!serverReadyDetected &&
/watching for file changes/i.test(string)) {
serverReadyDetected = true;
this.log(`Started ${projectType.name} dev server on port ${serverPort}.\n`);
const sessionId = localConfig.getSessionId(currentStore);
setTimeout(() => this.logAppLocalUrl(currentStore, sessionId), 100);
if (!isAppDev) {
this.watchForChanges();
}
}
break;
}
case 'angular': {
// 1. NOISE FILTER: Hide unwanted output
if (string.includes('press h + enter to show help')) {
return false;
}
// 2. URL FILTER: Hide localhost/network URLs (confusing - users should use Swell tunnel)
if (/➜\s+local:\s+http:\/\//i.test(string)) {
return false;
}
// 3. READY DETECTION: Detect server ready, show our message
if (!serverReadyDetected &&
(/application bundle generation complete/.test(string) ||
/watch mode enabled/.test(string))) {
serverReadyDetected = true;
this.log(`Started ${projectType.name} dev server on port ${serverPort}.\n`);
const sessionId = localConfig.getSessionId(currentStore);
setTimeout(() => this.logAppLocalUrl(currentStore, sessionId), 100);
if (!isAppDev) {
this.watchForChanges();
}
}
break;
}
case 'hono': {
// 2. URL FILTER: Hide localhost URLs (confusing - users should use Swell tunnel)
// Note: For Hono, the ready signal IS the URL line, so we handle both in section 3
// 3. READY DETECTION: Detect server ready, show our message, hide the URL
if (!serverReadyDetected &&
/ready on http:\/\/localhost:\d+/i.test(string)) {
serverReadyDetected = true;
this.log(`Started ${projectType.name} dev server on port ${serverPort}.\n`);
const sessionId = localConfig.getSessionId(currentStore);
setTimeout(() => this.logAppLocalUrl(currentStore, sessionId), 100);
if (!isAppDev) {
this.watchForChanges();
}
return false; // Hide the "Ready on localhost" line
}
break;
}
case 'nuxt': {
// 2. URL FILTER: Hide localhost/network URLs (confusing - users should use Swell tunnel)
if (/➜ local:\s+http:\/\//i.test(string) ||
/➜ network:/i.test(string)) {
return false;
}
// 3. READY DETECTION: Detect server ready, show our message
if (!serverReadyDetected && /nuxt \d+\.\d+\.\d+/i.test(string)) {
serverReadyDetected = true;
this.log(`Started ${projectType.name} dev server on port ${serverPort}.\n`);
const sessionId = localConfig.getSessionId(currentStore);
setTimeout(() => this.logAppLocalUrl(currentStore, sessionId), 100);
if (!isAppDev) {
this.watchForChanges();
}
}
break;
}
// No default
}
});
}
}