Skip to content
19 changes: 12 additions & 7 deletions code/lib/core-server/src/build-dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
validateFrameworkName,
} from '@storybook/core-common';
import prompts from 'prompts';
import invariant from 'tiny-invariant';
import { global } from '@storybook/global';
import { telemetry } from '@storybook/telemetry';

Expand All @@ -31,7 +32,7 @@ export async function buildDevStandalone(
): Promise<{ port: number; address: string; networkAddress: string }> {
const { packageJson, versionUpdates } = options;
const { version } = packageJson;

invariant(version !== undefined, 'Expected package.json version to be defined.');
// updateInfo are cached, so this is typically pretty fast
const [port, versionCheck] = await Promise.all([
getServerPort(options.port),
Expand Down Expand Up @@ -63,9 +64,10 @@ export async function buildDevStandalone(

const config = await loadMainConfig(options);
const { framework } = config;
invariant(framework, 'framework is required in Storybook v7');
const corePresets = [];

const frameworkName = typeof framework === 'string' ? framework : framework?.name;
const frameworkName = typeof framework === 'string' ? framework : framework.name;
validateFrameworkName(frameworkName);

corePresets.push(join(frameworkName, 'preset'));
Expand All @@ -82,30 +84,34 @@ export async function buildDevStandalone(
});

const { renderer, builder, disableTelemetry } = await presets.apply<CoreConfig>('core', {});
invariant(builder, 'no builder configured!');

if (!options.disableTelemetry && !disableTelemetry) {
if (versionCheck.success && !versionCheck.cached) {
telemetry('version-update');
}
}

const builderName = typeof builder === 'string' ? builder : builder?.name;
const builderName = typeof builder === 'string' ? builder : builder.name;
const [previewBuilder, managerBuilder] = await Promise.all([
getPreviewBuilder(builderName, options.configDir),
getManagerBuilder(),
]);

const resolvedRenderer = renderer
? resolveAddonName(options.configDir, renderer, options)
: undefined;
// Load second pass: all presets are applied in order
presets = await loadAllPresets({
corePresets: [
require.resolve('@storybook/core-server/dist/presets/common-preset'),
...(managerBuilder.corePresets || []),
...(previewBuilder.corePresets || []),
...(renderer ? [resolveAddonName(options.configDir, renderer, options)] : []),
...(resolvedRenderer ? [resolvedRenderer] : []),
...corePresets,
require.resolve('@storybook/core-server/dist/presets/babel-cache-preset'),
],
overridePresets: previewBuilder.overridePresets,
overridePresets: previewBuilder.overridePresets ?? [],
...options,
});

Expand All @@ -123,8 +129,7 @@ export async function buildDevStandalone(
);

const previewTotalTime = previewResult && previewResult.totalTime;
const managerTotalTime = managerResult && managerResult.totalTime;

const managerTotalTime = managerResult ? managerResult.totalTime : undefined;
const previewStats = previewResult && previewResult.stats;
const managerStats = managerResult && managerResult.stats;

Expand Down
19 changes: 13 additions & 6 deletions code/lib/core-server/src/build-static.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,15 @@ export async function buildStaticStandalone(options: BuildStaticStandaloneOption

const [previewBuilder, managerBuilder] = await getBuilders({ ...options, presets });
const { renderer } = await presets.apply<CoreConfig>('core', {});

const resolvedRenderer = renderer
? resolveAddonName(options.configDir, renderer, options)
: undefined;
presets = await loadAllPresets({
corePresets: [
require.resolve('@storybook/core-server/dist/presets/common-preset'),
...(managerBuilder.corePresets || []),
...(previewBuilder.corePresets || []),
...(renderer ? [resolveAddonName(options.configDir, renderer, options)] : []),
...(resolvedRenderer ? [resolvedRenderer] : []),
...corePresets,
require.resolve('@storybook/core-server/dist/presets/babel-cache-preset'),
],
Expand Down Expand Up @@ -151,7 +153,8 @@ export async function buildStaticStandalone(options: BuildStaticStandaloneOption
);
effects.push(copy(coreServerPublicDir, options.outputDir));

let initializedStoryIndexGenerator: Promise<StoryIndexGenerator> = Promise.resolve(undefined);
let initializedStoryIndexGenerator: Promise<StoryIndexGenerator | undefined> =
Promise.resolve(undefined);
if ((features?.buildStoriesJson || features?.storyStoreV7) && !options.ignorePreview) {
const workingDir = process.cwd();
const directories = {
Expand All @@ -167,17 +170,21 @@ export async function buildStaticStandalone(options: BuildStaticStandaloneOption
storyStoreV7: !!features?.storyStoreV7,
});

initializedStoryIndexGenerator = generator.initialize().then(() => generator);
const initializedStoryIndexGeneratorPromise = generator.initialize().then(() => generator);
effects.push(
extractStoriesJson(
join(options.outputDir, 'stories.json'),
initializedStoryIndexGenerator,
initializedStoryIndexGeneratorPromise,
convertToIndexV3
)
);
effects.push(
extractStoriesJson(join(options.outputDir, 'index.json'), initializedStoryIndexGenerator)
extractStoriesJson(
join(options.outputDir, 'index.json'),
initializedStoryIndexGeneratorPromise
)
);
initializedStoryIndexGenerator = initializedStoryIndexGeneratorPromise;
}

if (!core?.disableProjectJson) {
Expand Down
20 changes: 10 additions & 10 deletions code/lib/core-server/src/dev-server.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import express from 'express';
import compression from 'compression';
import invariant from 'tiny-invariant';

import type { CoreConfig, Options, StorybookConfig } from '@storybook/types';

Expand Down Expand Up @@ -34,31 +35,29 @@ export async function storybookDevServer(options: Options) {
getServerChannel(server)
);

let indexError: Error;
let indexError: Error | undefined;
// try get index generator, if failed, send telemetry without storyCount, then rethrow the error
const initializedStoryIndexGenerator: Promise<StoryIndexGenerator> = getStoryIndexGenerator(
features,
options,
serverChannel
).catch((err) => {
indexError = err;
return undefined;
});
const initializedStoryIndexGenerator: Promise<StoryIndexGenerator | undefined> =
getStoryIndexGenerator(features ?? {}, options, serverChannel).catch((err) => {
indexError = err;
return undefined;
});

app.use(compression({ level: 1 }));

if (typeof options.extendServer === 'function') {
options.extendServer(server);
}

app.use(getAccessControlMiddleware(core?.crossOriginIsolated));
app.use(getAccessControlMiddleware(core?.crossOriginIsolated ?? false));
app.use(getCachingMiddleware());

getMiddleware(options.configDir)(router);

app.use(router);

const { port, host, initialPath } = options;
invariant(port, 'expected options to have a port');
const proto = options.https ? 'https' : 'http';
const { address, networkAddress } = getServerAddresses(port, host, proto, initialPath);

Expand All @@ -67,6 +66,7 @@ export async function storybookDevServer(options: Options) {
server.listen({ port, host }, (error: Error) => (error ? reject(error) : resolve()));
});

invariant(core?.builder, 'no builder configured!');
const builderName = typeof core?.builder === 'string' ? core.builder : core?.builder?.name;

const [previewBuilder, managerBuilder] = await Promise.all([
Expand Down
23 changes: 17 additions & 6 deletions code/lib/core-server/src/presets/common-preset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export const staticDirs: PresetPropertyFn<'staticDirs'> = async (values = []) =>
];

export const favicon = async (
value: string,
value: string | undefined,
options: Pick<Options, 'presets' | 'configDir' | 'staticDir'>
) => {
if (value) {
Expand Down Expand Up @@ -257,8 +257,13 @@ type WhatsNewResponse = {
excerpt: string;
};

type OptionsWithRequiredCache = Exclude<Options, 'cache'> & Required<Pick<Options, 'cache'>>;

// eslint-disable-next-line @typescript-eslint/naming-convention
export const experimental_serverChannel = async (channel: Channel, options: Options) => {
export const experimental_serverChannel = async (
channel: Channel,
options: OptionsWithRequiredCache
) => {
const coreOptions = await options.presets.apply('core');

channel.on(SET_WHATS_NEW_CACHE, async (data: WhatsNewCache) => {
Expand All @@ -277,7 +282,10 @@ export const experimental_serverChannel = async (channel: Channel, options: Opti
throw response;
})) as WhatsNewResponse;

const main = await readConfig(findConfigFile('main', options.configDir));
const configFileName = findConfigFile('main', options.configDir);
if (!configFileName)
throw new Error(`unable to find storybook main file in ${options.configDir}`);
const main = await readConfig(configFileName);
const disableWhatsNewNotifications = main.getFieldValue([
'core',
'disableWhatsNewNotifications',
Expand All @@ -293,7 +301,7 @@ export const experimental_serverChannel = async (channel: Channel, options: Opti
} satisfies WhatsNewData;
channel.emit(RESULT_WHATS_NEW_DATA, { data });
} catch (e) {
logger.verbose(e);
logger.verbose(e instanceof Error ? e.message : String(e));
channel.emit(RESULT_WHATS_NEW_DATA, {
data: { status: 'ERROR' } satisfies WhatsNewData,
});
Expand All @@ -305,7 +313,10 @@ export const experimental_serverChannel = async (channel: Channel, options: Opti
async ({ disableWhatsNewNotifications }: { disableWhatsNewNotifications: boolean }) => {
const isTelemetryEnabled = coreOptions.disableTelemetry !== true;
try {
const main = await readConfig(findConfigFile('main', options.configDir));
const configFileName = findConfigFile('main', options.configDir);
if (!configFileName)
throw new Error(`unable to find storybook main file in ${options.configDir}`);
const main = await readConfig(configFileName);
main.setFieldValue(['core', 'disableWhatsNewNotifications'], disableWhatsNewNotifications);
await writeConfig(main);

Expand All @@ -314,7 +325,7 @@ export const experimental_serverChannel = async (channel: Channel, options: Opti
}
} catch (error) {
if (isTelemetryEnabled) {
await sendTelemetryError(error, 'core-config', {
await sendTelemetryError(error as Error, 'core-config', {
cliOptions: options,
presetOptions: { ...options, corePresets: [], overridePresets: [] },
skipPrompt: true,
Expand Down
2 changes: 1 addition & 1 deletion code/lib/core-server/src/standalone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { buildDevStandalone } from './build-dev';

async function build(options: any = {}, frameworkOptions: any = {}) {
const { mode = 'dev' } = options;
const { packageJson } = readUpSync({ cwd: __dirname });
const packageJson = readUpSync({ cwd: __dirname })?.packageJson;

const commonOptions = {
...options,
Expand Down
Loading