Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions code/frameworks/nextjs-server/src/next-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { ChildProcess } from 'child_process';
import { spawn } from 'child_process';
import { existsSync } from 'fs';
import type { StorybookNextJSOptions } from './types';
import { verifyPort } from './verifyPort';

const logger = console;
let childProcess: ChildProcess | undefined;
Expand Down Expand Up @@ -48,15 +49,16 @@ function addRewrites(
}

export const withStorybook = ({
port = 3000,
port = process.env.PORT ?? 3000,
sbPort = 34567,
managerPath = 'storybook',
previewPath = 'storybook-preview',
configDir = '.storybook',
appDir = undefined,
} = {}) => {
const isAppDir = appDir ?? existsSync('app');
const storybookNextJSOptions: StorybookNextJSOptions = {
appDir: appDir ?? existsSync('./app'),
appDir: isAppDir,
managerPath,
previewPath,
};
Expand Down Expand Up @@ -86,6 +88,8 @@ export const withStorybook = ({
}
);

verifyPort(port, { appDir: isAppDir, previewPath });

return (config: NextConfig) => ({
...config,
rewrites: addRewrites(config.rewrites, [
Expand Down
89 changes: 89 additions & 0 deletions code/frameworks/nextjs-server/src/verifyPort.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { join, dirname } from 'path';
import { ensureDir, exists, readFile, writeFile } from 'fs-extra';

interface VerifyOptions {
pid: number;
ppid: number;
port: string | number;
appDir: boolean;
previewPath: string;
}

const writePidFilePages = async ({ previewPath }: VerifyOptions) => {
const pidFile = join(process.cwd(), 'pages', previewPath, 'pid.tsx');

if (await exists(pidFile)) return;

await ensureDir(dirname(pidFile));
const pidTsx = `
import type { InferGetServerSidePropsType, GetServerSideProps } from 'next'

export const getServerSideProps = (async () => {
return { props: { ppid: process.ppid } }
}) satisfies GetServerSideProps<{ ppid: number }>;

export default function Page(
{ ppid }: InferGetServerSidePropsType<typeof getServerSideProps>
) {
const ppidTag = '__ppid_' + ppid + '__';
return <>{ppidTag}</>;
};
`;
await writeFile(pidFile, pidTsx);
};

const writePidFileApp = async ({ previewPath }: VerifyOptions) => {
const pidFile = join(process.cwd(), 'app', '(sb)', previewPath, 'pid', 'page.tsx');

if (await exists(pidFile)) return;

await ensureDir(dirname(pidFile));
const pidTsx = `
const page = () => {
const ppidTag = '__ppid_' + process.ppid + '__';
return <>{ppidTag}</>;
};
export default page;`;
await writeFile(pidFile, pidTsx);
};

const PPID_RE = /__ppid_(\d+)__/;
const checkPidRoute = async ({ pid, ppid, port, previewPath }: VerifyOptions) => {
const res = await fetch(`http://localhost:${port}/${previewPath}/pid`);
const pidHtml = await res.text();
const match = PPID_RE.exec(pidHtml);
const pidMatch = match?.[1].toString();

if (pidMatch === pid.toString() || pidMatch === ppid.toString()) {
console.log(`Verified NextJS pid ${pidMatch} is running on port ${port}`);
} else {
console.error(`NextJS server failed to start on port ${port}`);
console.error(`Wanted pid ${pid} or parent ${ppid}, got ${pidMatch}`);
console.error(`${pid.toString() === pidMatch} || ${ppid.toString() === pidMatch}`);
process.exit(1);
}
};

/**
* Helper function to verify that the NextJS
* server is actually running on the port we
* requested. Since NextJS can run multiple
* processes, defer to the parent process if
* it has already written to the pid file.
*/
export const verifyPort = (
port: string | number,
{ appDir, previewPath }: { appDir: boolean; previewPath: string }
) => {
const { pid, ppid } = process;

setTimeout(async () => {
try {
const writePidFile = appDir ? writePidFileApp : writePidFilePages;
await writePidFile({ pid, ppid, port, appDir, previewPath });
setTimeout(() => checkPidRoute({ pid, ppid, port, appDir, previewPath }), 100);
} catch (e) {
console.error(e);
}
}, 200);
};