Skip to content

Commit

Permalink
fix(create): Dynamically find open port if 3000 in use
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelbromley committed Aug 1, 2024
1 parent 0f8bdb5 commit a40fbb1
Show file tree
Hide file tree
Showing 7 changed files with 29 additions and 41 deletions.
26 changes: 0 additions & 26 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions packages/create/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
},
"devDependencies": {
"@types/cross-spawn": "^6.0.6",
"@types/detect-port": "^1.3.5",
"@types/fs-extra": "^11.0.4",
"@types/handlebars": "^4.1.0",
"@types/semver": "^7.5.8",
Expand All @@ -38,7 +37,6 @@
"@vendure/common": "^3.0.0",
"commander": "^11.0.0",
"cross-spawn": "^7.0.3",
"detect-port": "^1.5.1",
"fs-extra": "^11.2.0",
"handlebars": "^4.7.8",
"picocolors": "^1.0.0",
Expand Down
27 changes: 20 additions & 7 deletions packages/create/src/create-vendure-app.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/* eslint-disable no-console */
import { intro, note, outro, select, spinner } from '@clack/prompts';
import { program } from 'commander';
import detectPort from 'detect-port';
import fs from 'fs-extra';
import os from 'os';
import path from 'path';
Expand All @@ -20,7 +19,7 @@ import {
scaffoldAlreadyExists,
yarnIsAvailable,
} from './helpers';
import { CliLogLevel, DbType, PackageManager } from './types';
import { CliLogLevel, PackageManager } from './types';

// eslint-disable-next-line @typescript-eslint/no-var-requires
const packageJson = require('../package.json');
Expand Down Expand Up @@ -63,15 +62,30 @@ export async function createVendureApp(
if (!runPreChecks(name, useNpm)) {
return;
}
if (await isServerPortInUse()) {
console.log(pc.red(`Port ${SERVER_PORT} is in use. Please make it available and then re-try.`));
process.exit(1);
}

intro(
`Let's create a ${pc.blue(pc.bold('Vendure App'))}${pc.dim(`v${packageJson.version as string}`)}`,
);

const portSpinner = spinner();
let port = SERVER_PORT;
const attemptedPortRange = 20;
portSpinner.start(`Establishing port...`);
while (await isServerPortInUse(port)) {
const nextPort = port + 1;
portSpinner.message(pc.yellow(`Port ${port} is in use. Attempting to use ${nextPort}`));
port = nextPort;
if (port > SERVER_PORT + attemptedPortRange) {
portSpinner.stop(pc.red('Could not find an available port'));
outro(
`Please ensure there is a port available between ${SERVER_PORT} and ${SERVER_PORT + attemptedPortRange}`,
);
process.exit(1);
}
}
portSpinner.stop(`Using port ${port}`);
process.env.PORT = port.toString();

const root = path.resolve(name);
const appName = path.basename(root);
const scaffoldExists = scaffoldAlreadyExists(root, name);
Expand Down Expand Up @@ -211,7 +225,6 @@ export async function createVendureApp(
const assetsDir = path.join(__dirname, '../assets');

const initialDataPath = path.join(assetsDir, 'initial-data.json');
const port = await detectPort(3000);
const vendureLogLevel =
logLevel === 'silent'
? LogLevel.Error
Expand Down
6 changes: 3 additions & 3 deletions packages/create/src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -414,13 +414,13 @@ function throwDatabaseSchemaDoesNotExist(dbName: string, schemaName: string) {
);
}

export function isServerPortInUse(): Promise<boolean> {
export function isServerPortInUse(port: number): Promise<boolean> {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const tcpPortUsed = require('tcp-port-used');
try {
return tcpPortUsed.check(SERVER_PORT);
return tcpPortUsed.check(port);
} catch (e: any) {
console.log(pc.yellow(`Warning: could not determine whether port ${SERVER_PORT} is available`));
console.log(pc.yellow(`Warning: could not determine whether port ${port} is available`));
return Promise.resolve(false);
}
}
1 change: 1 addition & 0 deletions packages/create/templates/.env.hbs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
APP_ENV=dev
PORT=3000
COOKIE_SECRET={{ cookieSecret }}
SUPERADMIN_USERNAME={{{ escapeSingle superadminIdentifier }}}
SUPERADMIN_PASSWORD={{{ escapeSingle superadminPassword }}}
Expand Down
1 change: 1 addition & 0 deletions packages/create/templates/environment.d.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ declare global {
namespace NodeJS {
interface ProcessEnv {
APP_ENV: string;
PORT: string;
COOKIE_SECRET: string;
SUPERADMIN_USERNAME: string;
SUPERADMIN_PASSWORD: string;
Expand Down
7 changes: 4 additions & 3 deletions packages/create/templates/vendure-config.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ import 'dotenv/config';
import path from 'path';

const IS_DEV = process.env.APP_ENV === 'dev';
const serverPort = +process.env.PORT || 3000;

export const config: VendureConfig = {
apiOptions: {
port: 3000,
port: serverPort,
adminApiPath: 'admin-api',
shopApiPath: 'shop-api',
// The following options are useful in development mode,
Expand Down Expand Up @@ -100,9 +101,9 @@ export const config: VendureConfig = {
}),
AdminUiPlugin.init({
route: 'admin',
port: 3002,
port: serverPort + 2,
adminUiConfig: {
apiPort: 3000,
apiPort: serverPort,
},
}),
],
Expand Down

0 comments on commit a40fbb1

Please sign in to comment.