diff --git a/code/lib/core-common/src/utils/validate-config.ts b/code/lib/core-common/src/utils/validate-config.ts index 3314a6b3c1a..3c5d7201724 100644 --- a/code/lib/core-common/src/utils/validate-config.ts +++ b/code/lib/core-common/src/utils/validate-config.ts @@ -6,7 +6,9 @@ const renderers = ['html', 'preact', 'react', 'server', 'svelte', 'vue', 'vue3', const rendererNames = [...renderers, ...renderers.map((renderer) => `@storybook/${renderer}`)]; -export function validateFrameworkName(frameworkName: string | undefined) { +export function validateFrameworkName( + frameworkName: string | undefined +): asserts frameworkName is string { const automigrateMessage = `Please run 'npx storybook@next automigrate' to automatically fix your config. See the migration guide for more information: diff --git a/code/lib/core-server/src/build-dev.ts b/code/lib/core-server/src/build-dev.ts index 7d1a223e170..ee045cde1a3 100644 --- a/code/lib/core-server/src/build-dev.ts +++ b/code/lib/core-server/src/build-dev.ts @@ -31,13 +31,15 @@ export async function buildDevStandalone( options: CLIOptions & LoadOptions & BuilderOptions ): Promise<{ port: number; address: string; networkAddress: string }> { const { packageJson, versionUpdates } = options; - const { version } = packageJson; - invariant(version !== undefined, 'Expected package.json version to be defined.'); + invariant( + packageJson.version !== undefined, + `Expected package.json#version to be defined in the "${packageJson.name}" package}` + ); // updateInfo are cached, so this is typically pretty fast const [port, versionCheck] = await Promise.all([ getServerPort(options.port), versionUpdates - ? updateCheck(version) + ? updateCheck(packageJson.version) : Promise.resolve({ success: false, cached: false, data: {}, time: Date.now() }), ]); @@ -64,10 +66,9 @@ 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')); @@ -84,7 +85,8 @@ export async function buildDevStandalone( }); const { renderer, builder, disableTelemetry } = await presets.apply('core', {}); - invariant(builder, 'no builder configured!'); + + invariant(builder, 'No builder configured in core.builder'); if (!options.disableTelemetry && !disableTelemetry) { if (versionCheck.success && !versionCheck.cached) { @@ -98,9 +100,8 @@ export async function buildDevStandalone( getManagerBuilder(), ]); - const resolvedRenderer = renderer - ? resolveAddonName(options.configDir, renderer, options) - : undefined; + const resolvedRenderer = renderer && resolveAddonName(options.configDir, renderer, options); + // Load second pass: all presets are applied in order presets = await loadAllPresets({ corePresets: [ @@ -128,10 +129,10 @@ export async function buildDevStandalone( fullOptions ); - const previewTotalTime = previewResult && previewResult.totalTime; - const managerTotalTime = managerResult ? managerResult.totalTime : undefined; - const previewStats = previewResult && previewResult.stats; - const managerStats = managerResult && managerResult.stats; + const previewTotalTime = previewResult?.totalTime; + const managerTotalTime = managerResult?.totalTime; + const previewStats = previewResult?.stats; + const managerStats = managerResult?.stats; if (options.webpackStatsJson) { const target = options.webpackStatsJson === true ? options.outputDir : options.webpackStatsJson; @@ -162,7 +163,7 @@ export async function buildDevStandalone( if (!options.quiet) { outputStartupInformation({ updateInfo: versionCheck, - version, + version: packageJson.version, name, address, networkAddress, diff --git a/code/lib/core-server/src/build-static.ts b/code/lib/core-server/src/build-static.ts index b40316e6f92..575f049351d 100644 --- a/code/lib/core-server/src/build-static.ts +++ b/code/lib/core-server/src/build-static.ts @@ -173,21 +173,20 @@ export async function buildStaticStandalone(options: BuildStaticStandaloneOption storyStoreV7: !!features?.storyStoreV7, }); - const initializedStoryIndexGeneratorPromise = generator.initialize().then(() => generator); + initializedStoryIndexGenerator = generator.initialize().then(() => generator); effects.push( extractStoriesJson( join(options.outputDir, 'stories.json'), - initializedStoryIndexGeneratorPromise, + initializedStoryIndexGenerator as Promise, convertToIndexV3 ) ); effects.push( extractStoriesJson( join(options.outputDir, 'index.json'), - initializedStoryIndexGeneratorPromise + initializedStoryIndexGenerator as Promise ) ); - initializedStoryIndexGenerator = initializedStoryIndexGeneratorPromise; } if (!core?.disableProjectJson) { diff --git a/code/lib/core-server/src/utils/get-builders.ts b/code/lib/core-server/src/utils/get-builders.ts index 7ed83e29361..4a9b9f14daf 100644 --- a/code/lib/core-server/src/utils/get-builders.ts +++ b/code/lib/core-server/src/utils/get-builders.ts @@ -10,15 +10,10 @@ export async function getPreviewBuilder( builderName: string, configDir: string ): Promise> { - let builderPackage: string; - if (builderName) { - builderPackage = require.resolve( - ['webpack5'].includes(builderName) ? `@storybook/builder-${builderName}` : builderName, - { paths: [configDir] } - ); - } else { - throw new Error('no builder configured!'); - } + const builderPackage = require.resolve( + ['webpack5'].includes(builderName) ? `@storybook/builder-${builderName}` : builderName, + { paths: [configDir] } + ); const previewBuilder = await import(pathToFileURL(builderPackage).href); return previewBuilder; } diff --git a/code/lib/core-server/src/utils/getStoryIndexGenerator.ts b/code/lib/core-server/src/utils/getStoryIndexGenerator.ts index cf78ecec576..44d49850fe4 100644 --- a/code/lib/core-server/src/utils/getStoryIndexGenerator.ts +++ b/code/lib/core-server/src/utils/getStoryIndexGenerator.ts @@ -14,40 +14,40 @@ export async function getStoryIndexGenerator( }, options: Options, serverChannel: ServerChannel -) { - if (features?.buildStoriesJson || features?.storyStoreV7) { - const workingDir = process.cwd(); - const directories = { - configDir: options.configDir, - workingDir, - }; - const stories = options.presets.apply('stories'); - const deprecatedStoryIndexers = options.presets.apply('storyIndexers', []); - const indexers = options.presets.apply('indexers', []); - const docsOptions = options.presets.apply('docs', {}); - const normalizedStories = normalizeStories(await stories, directories); +): Promise { + if (!features?.buildStoriesJson && !features?.storyStoreV7) { + return undefined; + } + const workingDir = process.cwd(); + const directories = { + configDir: options.configDir, + workingDir, + }; + const stories = options.presets.apply('stories'); + const deprecatedStoryIndexers = options.presets.apply('storyIndexers', []); + const indexers = options.presets.apply('indexers', []); + const docsOptions = options.presets.apply('docs', {}); + const normalizedStories = normalizeStories(await stories, directories); - const generator = new StoryIndexGenerator(normalizedStories, { - ...directories, - storyIndexers: await deprecatedStoryIndexers, - indexers: await indexers, - docs: await docsOptions, - workingDir, - storiesV2Compatibility: !features?.storyStoreV7, - storyStoreV7: features.storyStoreV7 ?? false, - }); + const generator = new StoryIndexGenerator(normalizedStories, { + ...directories, + storyIndexers: await deprecatedStoryIndexers, + indexers: await indexers, + docs: await docsOptions, + workingDir, + storiesV2Compatibility: !features?.storyStoreV7, + storyStoreV7: features.storyStoreV7 ?? false, + }); - const initializedStoryIndexGenerator = generator.initialize().then(() => generator); + const initializedStoryIndexGenerator = generator.initialize().then(() => generator); - useStoriesJson({ - router, - initializedStoryIndexGenerator, - normalizedStories, - serverChannel, - workingDir, - }); + useStoriesJson({ + router, + initializedStoryIndexGenerator, + normalizedStories, + serverChannel, + workingDir, + }); - return initializedStoryIndexGenerator; - } - return Promise.resolve(undefined); + return initializedStoryIndexGenerator; } diff --git a/code/lib/core-server/src/utils/server-statics.ts b/code/lib/core-server/src/utils/server-statics.ts index a5ccc59fbd2..60938469294 100644 --- a/code/lib/core-server/src/utils/server-statics.ts +++ b/code/lib/core-server/src/utils/server-statics.ts @@ -12,7 +12,8 @@ import { dedent } from 'ts-dedent'; import { defaultStaticDirs } from './constants'; export async function useStatics(router: any, options: Options) { - const staticDirs = await options.presets.apply('staticDirs'); + const staticDirs = + (await options.presets.apply('staticDirs')) ?? []; const faviconPath = await options.presets.apply('favicon'); if (options.staticDir && !isEqual(staticDirs, defaultStaticDirs)) { @@ -26,7 +27,7 @@ export async function useStatics(router: any, options: Options) { } const statics = [ - ...(staticDirs ?? []).map((dir) => (typeof dir === 'string' ? dir : `${dir.from}:${dir.to}`)), + ...staticDirs.map((dir) => (typeof dir === 'string' ? dir : `${dir.from}:${dir.to}`)), ...(options.staticDir || []), ]; diff --git a/code/lib/core-server/src/withTelemetry.ts b/code/lib/core-server/src/withTelemetry.ts index 1888f733cd0..706439b4bfd 100644 --- a/code/lib/core-server/src/withTelemetry.ts +++ b/code/lib/core-server/src/withTelemetry.ts @@ -4,6 +4,7 @@ import { loadAllPresets, cache } from '@storybook/core-common'; import { telemetry, getPrecedingUpgrade, oneWayHash } from '@storybook/telemetry'; import type { EventType } from '@storybook/telemetry'; import { logger } from '@storybook/node-logger'; +import invariant from 'tiny-invariant'; type TelemetryOptions = { cliOptions: CLIOptions; @@ -66,7 +67,7 @@ async function getErrorLevel({ } export async function sendTelemetryError( - error: Error, + error: unknown, eventType: EventType, options: TelemetryOptions ) { @@ -80,13 +81,17 @@ export async function sendTelemetryError( if (errorLevel !== 'none') { const precedingUpgrade = await getPrecedingUpgrade(); + invariant( + error instanceof Error, + 'The error passed to sendTelemetryError was not an Error, please only send Errors' + ); await telemetry( 'error', { eventType, precedingUpgrade, error: errorLevel === 'full' ? error : undefined, - errorHash: oneWayHash(error.message || ''), + errorHash: oneWayHash(error.message), }, { immediate: true, @@ -132,8 +137,8 @@ export async function withTelemetry( } const { printError = logger.error } = options; - printError(error instanceof Error ? error.message : String(error)); - if (error instanceof Error) await sendTelemetryError(error, eventType, options); + printError(error); + await sendTelemetryError(error, eventType, options); throw error; } finally { diff --git a/code/lib/node-logger/src/index.ts b/code/lib/node-logger/src/index.ts index 4cade02d29b..09e0f282878 100644 --- a/code/lib/node-logger/src/index.ts +++ b/code/lib/node-logger/src/index.ts @@ -25,7 +25,8 @@ export const logger = { plain: (message: string): void => console.log(message), line: (count = 1): void => console.log(`${Array(count - 1).fill('\n')}`), warn: (message: string): void => npmLog.warn('', message), - error: (message: string): void => npmLog.error('', message), + // npmLog supports anything we log, it will just stringify it + error: (message: unknown): void => npmLog.error('', message as string), trace: ({ message, time }: { message: string; time: [number, number] }): void => npmLog.info('', `${message} (${colors.purple(prettyTime(time))})`), setLevel: (level = 'info'): void => {