diff --git a/code/lib/cli/package.json b/code/lib/cli/package.json index 03d37dd63554..5fe6d5d5f40e 100644 --- a/code/lib/cli/package.json +++ b/code/lib/cli/package.json @@ -93,6 +93,7 @@ "simple-update-notifier": "^2.0.0", "strip-json-comments": "^3.0.1", "tempy": "^1.0.1", + "tiny-invariant": "^1.3.1", "ts-dedent": "^2.0.0", "util-deprecate": "^1.0.2" }, diff --git a/code/lib/cli/src/HandledError.ts b/code/lib/cli/src/HandledError.ts index 4a603fed1844..429d9620df3a 100644 --- a/code/lib/cli/src/HandledError.ts +++ b/code/lib/cli/src/HandledError.ts @@ -1,9 +1,9 @@ export class HandledError extends Error { public handled = true; - constructor(messageOrError: string | Error) { - super(typeof messageOrError === 'string' ? messageOrError : messageOrError.message); + constructor(error: unknown) { + super(String(error)); - if (typeof messageOrError !== 'string') this.cause = messageOrError; + if (typeof error !== 'string') this.cause = error; } } diff --git a/code/lib/cli/src/add.ts b/code/lib/cli/src/add.ts index 8728da80ad5f..20e1c42bc811 100644 --- a/code/lib/cli/src/add.ts +++ b/code/lib/cli/src/add.ts @@ -37,7 +37,10 @@ const postinstallAddon = async (addonName: string, options: PostinstallOptions) const getVersionSpecifier = (addon: string) => { const groups = /^(...*)@(.*)$/.exec(addon); - return groups ? [groups[1], groups[2]] : [addon, undefined]; + if (groups) { + return [groups[0], groups[2]] as const; + } + return [addon, undefined] as const; }; const requireMain = (configDir: string) => { @@ -79,6 +82,12 @@ export async function add( const packageJson = await packageManager.retrievePackageJson(); const { mainConfig, configDir } = getStorybookInfo(packageJson); + if (typeof configDir === 'undefined') { + throw new Error(dedent` + Unable to find storybook config directory + `); + } + if (checkInstalled(addon, requireMain(configDir))) { throw new Error(dedent` Addon ${addon} is already installed; we skipped adding it to your ${mainConfig}. diff --git a/code/lib/cli/src/automigrate/fixes/angular-builders-multiproject.test.ts b/code/lib/cli/src/automigrate/fixes/angular-builders-multiproject.test.ts index b9066b9c48f2..c09e3b11e538 100644 --- a/code/lib/cli/src/automigrate/fixes/angular-builders-multiproject.test.ts +++ b/code/lib/cli/src/automigrate/fixes/angular-builders-multiproject.test.ts @@ -29,6 +29,7 @@ jest.mock('../../generators/ANGULAR/helpers', () => ({ })); describe('is Nx project', () => { + // @ts-expect-error (Type 'null' is not comparable) const packageManager = { getPackageVersion: () => { return null; diff --git a/code/lib/cli/src/automigrate/fixes/autodocs-true.ts b/code/lib/cli/src/automigrate/fixes/autodocs-true.ts index c3c2adc59f06..f84c3f7ee778 100644 --- a/code/lib/cli/src/automigrate/fixes/autodocs-true.ts +++ b/code/lib/cli/src/automigrate/fixes/autodocs-true.ts @@ -1,15 +1,13 @@ import chalk from 'chalk'; import { dedent } from 'ts-dedent'; -import type { StorybookConfigRaw } from '@storybook/types'; - import type { Fix } from '../types'; import { updateMainConfig } from '../helpers/mainConfigFile'; const logger = console; interface AutodocsTrueFrameworkRunOptions { - value?: StorybookConfigRaw['docs']['autodocs']; + value?: boolean | 'tag'; } /** @@ -83,7 +81,7 @@ export const autodocsTrue: Fix = { async run({ result: { value }, dryRun, mainConfigPath }) { logger.info(`✅ Setting 'docs.autodocs' to true in main.js`); if (!dryRun) { - await updateMainConfig({ mainConfigPath, dryRun }, async (main) => { + await updateMainConfig({ mainConfigPath, dryRun: !!dryRun }, async (main) => { main.removeField(['docs', 'docsPage']); main.setFieldValue(['docs', 'autodocs'], value ?? true); }); diff --git a/code/lib/cli/src/automigrate/fixes/bare-mdx-stories-glob.ts b/code/lib/cli/src/automigrate/fixes/bare-mdx-stories-glob.ts index b4f81f08a8fb..175e69c23b14 100644 --- a/code/lib/cli/src/automigrate/fixes/bare-mdx-stories-glob.ts +++ b/code/lib/cli/src/automigrate/fixes/bare-mdx-stories-glob.ts @@ -116,7 +116,7 @@ export const bareMdxStoriesGlob: Fix = { ${JSON.stringify(nextStoriesEntries, null, 2)}`); if (!dryRun) { - await updateMainConfig({ mainConfigPath, dryRun }, async (main) => { + await updateMainConfig({ mainConfigPath, dryRun: !!dryRun }, async (main) => { main.setFieldValue(['stories'], nextStoriesEntries); }); } diff --git a/code/lib/cli/src/automigrate/fixes/builder-vite.ts b/code/lib/cli/src/automigrate/fixes/builder-vite.ts index b7ee1317957c..b1d31444e914 100644 --- a/code/lib/cli/src/automigrate/fixes/builder-vite.ts +++ b/code/lib/cli/src/automigrate/fixes/builder-vite.ts @@ -75,7 +75,7 @@ export const builderVite: Fix = { logger.info(`✅ Updating main.js to use vite builder`); if (!dryRun) { - await updateMainConfig({ dryRun, mainConfigPath }, async (main) => { + await updateMainConfig({ dryRun: !!dryRun, mainConfigPath }, async (main) => { const updatedBuilder = typeof builder === 'string' ? '@storybook/builder-vite' diff --git a/code/lib/cli/src/automigrate/fixes/cra5.test.ts b/code/lib/cli/src/automigrate/fixes/cra5.test.ts index 1eedffc0a12f..a2eb150f30ff 100644 --- a/code/lib/cli/src/automigrate/fixes/cra5.test.ts +++ b/code/lib/cli/src/automigrate/fixes/cra5.test.ts @@ -109,6 +109,7 @@ describe('cra5 fix', () => { }); }); describe('no cra dependency', () => { + // @ts-expect-error (Type 'null' is not comparable) const packageManager = { getPackageVersion: () => { return null; diff --git a/code/lib/cli/src/automigrate/fixes/eslint-plugin.test.ts b/code/lib/cli/src/automigrate/fixes/eslint-plugin.test.ts index 54a527848ae6..e896f461f22f 100644 --- a/code/lib/cli/src/automigrate/fixes/eslint-plugin.test.ts +++ b/code/lib/cli/src/automigrate/fixes/eslint-plugin.test.ts @@ -93,9 +93,9 @@ describe('eslint-plugin fix', () => { checkEslint({ packageJson, }) - ).resolves.toMatchObject({ - unsupportedExtension: undefined, - }); + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"warn: Unable to find .eslintrc config file, skipping"` + ); }); it('when .eslintrc is using unsupported extension', async () => { @@ -104,7 +104,9 @@ describe('eslint-plugin fix', () => { packageJson, eslintExtension: 'yml', }) - ).resolves.toMatchObject({ unsupportedExtension: 'yml' }); + ).rejects.toThrowErrorMatchingInlineSnapshot( + `"warn: Unable to find .eslintrc config file, skipping"` + ); }); }); }); diff --git a/code/lib/cli/src/automigrate/fixes/eslint-plugin.ts b/code/lib/cli/src/automigrate/fixes/eslint-plugin.ts index a34f30e78340..25e5dfd03204 100644 --- a/code/lib/cli/src/automigrate/fixes/eslint-plugin.ts +++ b/code/lib/cli/src/automigrate/fixes/eslint-plugin.ts @@ -33,15 +33,15 @@ export const eslintPlugin: Fix = { return null; } - let eslintFile; - let unsupportedExtension; + let eslintFile: string | null = null; + let unsupportedExtension: string | undefined; try { eslintFile = findEslintFile(); } catch (err) { - unsupportedExtension = err.message; + unsupportedExtension = String(err); } - if (!eslintFile && !unsupportedExtension) { + if (!eslintFile || !unsupportedExtension) { logger.warn('Unable to find .eslintrc config file, skipping'); return null; } diff --git a/code/lib/cli/src/automigrate/fixes/mdx-1-to-2.ts b/code/lib/cli/src/automigrate/fixes/mdx-1-to-2.ts index 2ff4c8b4c6eb..108dde3c5aa3 100644 --- a/code/lib/cli/src/automigrate/fixes/mdx-1-to-2.ts +++ b/code/lib/cli/src/automigrate/fixes/mdx-1-to-2.ts @@ -47,7 +47,7 @@ export const mdx1to2: Fix = { async check() { const storiesMdxFiles = await globby('./!(node_modules)**/*.(story|stories).mdx'); - return storiesMdxFiles.length ? { storiesMdxFiles } : undefined; + return storiesMdxFiles.length ? { storiesMdxFiles } : null; }, prompt({ storiesMdxFiles }) { diff --git a/code/lib/cli/src/automigrate/fixes/mdx-gfm.ts b/code/lib/cli/src/automigrate/fixes/mdx-gfm.ts index 1601dce89572..7c3fd0130231 100644 --- a/code/lib/cli/src/automigrate/fixes/mdx-gfm.ts +++ b/code/lib/cli/src/automigrate/fixes/mdx-gfm.ts @@ -33,6 +33,10 @@ export const mdxgfm: Fix = { let pattern; + if (typeof configDir === 'undefined') { + return false; + } + if (typeof item === 'string') { pattern = slash(join(configDir, item)); } else if (typeof item === 'object') { @@ -41,6 +45,10 @@ export const mdxgfm: Fix = { pattern = slash(join(configDir, directory, files)); } + if (!pattern) { + return false; + } + const files = await glob(pattern, commonGlobOptions(pattern)); return files.some((f) => f.endsWith('.mdx')); @@ -94,7 +102,7 @@ export const mdxgfm: Fix = { [`@storybook/addon-mdx-gfm@${versionToInstall}`] ); - await updateMainConfig({ mainConfigPath, dryRun }, async (main) => { + await updateMainConfig({ mainConfigPath, dryRun: !!dryRun }, async (main) => { logger.info(`✅ Adding "@storybook/addon-mdx-gfm" addon`); if (!dryRun) { main.appendValueToArray(['addons'], '@storybook/addon-mdx-gfm'); diff --git a/code/lib/cli/src/automigrate/fixes/missing-babelrc.test.ts b/code/lib/cli/src/automigrate/fixes/missing-babelrc.test.ts index f5306d9b0363..91a3230141fe 100644 --- a/code/lib/cli/src/automigrate/fixes/missing-babelrc.test.ts +++ b/code/lib/cli/src/automigrate/fixes/missing-babelrc.test.ts @@ -69,7 +69,7 @@ describe('missing-babelrc fix', () => { afterEach(jest.restoreAllMocks); it('skips when storybook version < 7.0.0', async () => { - await expect(check({ storybookVersion: '6.3.2' })).resolves.toBeNull(); + await expect(check({ storybookVersion: '6.3.2', main: {} })).resolves.toBeNull(); }); it('skips when babelrc config is present', async () => { diff --git a/code/lib/cli/src/automigrate/fixes/missing-babelrc.ts b/code/lib/cli/src/automigrate/fixes/missing-babelrc.ts index 5c70808657a6..14332f15b5f5 100644 --- a/code/lib/cli/src/automigrate/fixes/missing-babelrc.ts +++ b/code/lib/cli/src/automigrate/fixes/missing-babelrc.ts @@ -52,7 +52,7 @@ export const missingBabelRc: Fix = { filename: '__fake__.js', // somehow needed to detect .babelrc.* files }); - if (!config.config && !config.babelrc && !packageJson.babel) { + if (!config?.config && !config?.babelrc && !packageJson.babel) { return { needsBabelRc: true }; } } diff --git a/code/lib/cli/src/automigrate/fixes/new-frameworks.test.ts b/code/lib/cli/src/automigrate/fixes/new-frameworks.test.ts index 441af07c311f..5e63d6d22fff 100644 --- a/code/lib/cli/src/automigrate/fixes/new-frameworks.test.ts +++ b/code/lib/cli/src/automigrate/fixes/new-frameworks.test.ts @@ -26,6 +26,7 @@ const checkNewFrameworks = async ({ storybookVersion, rendererPackage, configDir: '', + mainConfigPath: ' ', }); }; diff --git a/code/lib/cli/src/automigrate/fixes/new-frameworks.ts b/code/lib/cli/src/automigrate/fixes/new-frameworks.ts index cecf3f852df3..657372d70ea8 100644 --- a/code/lib/cli/src/automigrate/fixes/new-frameworks.ts +++ b/code/lib/cli/src/automigrate/fixes/new-frameworks.ts @@ -4,6 +4,7 @@ import semver from 'semver'; import { frameworkPackages, rendererPackages } from '@storybook/core-common'; import type { Preset } from '@storybook/types'; +import invariant from 'tiny-invariant'; import type { Fix } from '../types'; import { getStorybookVersionSpecifier } from '../../helpers'; import { @@ -26,13 +27,13 @@ interface NewFrameworkRunOptions { dependenciesToRemove: string[]; hasFrameworkInMainConfig: boolean; frameworkPackage: string; - metaFramework: string; + metaFramework: string | undefined; renderer: string; addonsToRemove: string[]; frameworkOptions: Record; rendererOptions: Record; addonOptions: Record; - builderConfig: string | Record; + builderConfig: string | Record | undefined; builderInfo: { name: string; options: Record; @@ -69,13 +70,17 @@ export const newFrameworks: Fix = { return null; } + if (typeof configDir === 'undefined') { + return null; + } + const packageJson = await packageManager.retrievePackageJson(); const frameworkPackageName = getFrameworkPackageName(mainConfig); const rendererPackageName = rendererPackage ?? - (await getRendererPackageNameFromFramework(frameworkPackageName)) ?? + (await getRendererPackageNameFromFramework(frameworkPackageName as string)) ?? (await detectRenderer(packageJson)); let hasFrameworkInMainConfig = !!frameworkPackageName; @@ -220,7 +225,9 @@ export const newFrameworks: Fix = { `); } - return { + invariant(mainConfigPath, 'Missing main config path.'); + + const result: Awaited['check']>> = { mainConfigPath, dependenciesToAdd, dependenciesToRemove, @@ -239,6 +246,7 @@ export const newFrameworks: Fix = { builderConfig, metaFramework, }; + return result; }, prompt({ @@ -453,7 +461,7 @@ export const newFrameworks: Fix = { } } - await updateMainConfig({ mainConfigPath, dryRun }, async (main) => { + await updateMainConfig({ mainConfigPath, dryRun: !!dryRun }, async (main) => { logger.info(`✅ Updating main.js`); logger.info(`✅ Updating "framework" field`); diff --git a/code/lib/cli/src/automigrate/fixes/sb-scripts.ts b/code/lib/cli/src/automigrate/fixes/sb-scripts.ts index 8471d3c1a6c3..24ac7854ef4d 100644 --- a/code/lib/cli/src/automigrate/fixes/sb-scripts.ts +++ b/code/lib/cli/src/automigrate/fixes/sb-scripts.ts @@ -1,6 +1,7 @@ import chalk from 'chalk'; import { dedent } from 'ts-dedent'; import semver from 'semver'; +import type { PackageJson } from '@storybook/types'; import type { Fix } from '../types'; import type { PackageJsonWithDepsAndDevDeps } from '../../js-package-manager'; @@ -18,10 +19,14 @@ const logger = console; * that do contain the actual sb binary, and not something like "npm run start-storybook" * which could actually be a custom script even though the name matches the legacy binary name */ -export const getStorybookScripts = (allScripts: Record) => { +export const getStorybookScripts = (allScripts: NonNullable) => { return Object.keys(allScripts).reduce((acc, key) => { + const currentScript = allScripts[key]; + if (currentScript == null) { + return acc; + } let isStorybookScript = false; - const allWordsFromScript = allScripts[key].split(' '); + const allWordsFromScript = currentScript.split(' '); const newScript = allWordsFromScript .map((currentWord, index) => { const previousWord = allWordsFromScript[index - 1]; @@ -51,7 +56,7 @@ export const getStorybookScripts = (allScripts: Record) => { if (isStorybookScript) { acc[key] = { - before: allScripts[key], + before: currentScript, after: newScript, }; } @@ -90,7 +95,7 @@ export const sbScripts: Fix = { prompt({ storybookVersion, storybookScripts }) { const sbFormatted = chalk.cyan(`Storybook ${storybookVersion}`); - const newScriptsMessage = Object.keys(storybookScripts).reduce((acc, scriptKey) => { + const newScriptsMessage = Object.keys(storybookScripts).reduce((acc: string[], scriptKey) => { acc.push( [ chalk.bold(scriptKey), diff --git a/code/lib/cli/src/automigrate/fixes/vue3.test.ts b/code/lib/cli/src/automigrate/fixes/vue3.test.ts index 6d7f61d77186..cfafca574165 100644 --- a/code/lib/cli/src/automigrate/fixes/vue3.test.ts +++ b/code/lib/cli/src/automigrate/fixes/vue3.test.ts @@ -133,6 +133,7 @@ describe('vue3 fix', () => { }); describe('no vue dependency', () => { it('should no-op', async () => { + // @ts-expect-error (Type 'null' is not comparable) const packageManager = { getPackageVersion: (packageName) => { return null; diff --git a/code/lib/cli/src/automigrate/fixes/webpack5.test.ts b/code/lib/cli/src/automigrate/fixes/webpack5.test.ts index db03eeb57a08..37a601f40c0a 100644 --- a/code/lib/cli/src/automigrate/fixes/webpack5.test.ts +++ b/code/lib/cli/src/automigrate/fixes/webpack5.test.ts @@ -132,6 +132,7 @@ describe('webpack5 fix', () => { }); }); describe('no webpack dependency', () => { + // @ts-expect-error (Type 'null' is not comparable) const packageManager = { getPackageVersion: () => { return null; diff --git a/code/lib/cli/src/automigrate/fixes/webpack5.ts b/code/lib/cli/src/automigrate/fixes/webpack5.ts index bcae50749a1e..8b54ee2d5a8c 100644 --- a/code/lib/cli/src/automigrate/fixes/webpack5.ts +++ b/code/lib/cli/src/automigrate/fixes/webpack5.ts @@ -8,7 +8,7 @@ import { updateMainConfig } from '../helpers/mainConfigFile'; const logger = console; interface Webpack5RunOptions { - webpackVersion: string; + webpackVersion: string | null; storybookVersion: string; } @@ -22,7 +22,7 @@ interface Webpack5RunOptions { * - Add core.builder = 'webpack5' to main.js * - Add 'webpack5' as a project dependency */ -export const webpack5: Fix = { +export const webpack5 = { id: 'webpack5', async check({ configDir, packageManager, mainConfig, storybookVersion }) { @@ -75,9 +75,9 @@ export const webpack5: Fix = { logger.info('✅ Setting `core.builder` to `@storybook/builder-webpack5` in main.js'); if (!dryRun) { - await updateMainConfig({ mainConfigPath, dryRun }, async (main) => { + await updateMainConfig({ mainConfigPath, dryRun: !!dryRun }, async (main) => { main.setFieldValue(['core', 'builder'], '@storybook/builder-webpack5'); }); } }, -}; +} satisfies Fix; diff --git a/code/lib/cli/src/automigrate/fixes/wrap-require-utils.ts b/code/lib/cli/src/automigrate/fixes/wrap-require-utils.ts index c1a33b3119f6..99bf4b8d550c 100644 --- a/code/lib/cli/src/automigrate/fixes/wrap-require-utils.ts +++ b/code/lib/cli/src/automigrate/fixes/wrap-require-utils.ts @@ -85,7 +85,7 @@ export function isRequireWrapperNecessary( if ( t.isArrayExpression(node) && - node.elements.some((element) => isRequireWrapperNecessary(element)) + node.elements.some((element) => element && isRequireWrapperNecessary(element)) ) { cb(node); return true; @@ -164,11 +164,11 @@ export function wrapValueWithRequireWrapper(config: ConfigFile, node: t.Node) { isRequireWrapperNecessary(node, (n) => { if (t.isStringLiteral(n)) { const wrapperNode = getReferenceToRequireWrapper(config, n.value); - Object.keys(n).forEach((k: keyof typeof n) => { - delete n[k]; + Object.keys(n).forEach((k) => { + delete n[k as keyof typeof n]; }); - Object.keys(wrapperNode).forEach((k: keyof typeof wrapperNode) => { - (n as any)[k] = wrapperNode[k]; + Object.keys(wrapperNode).forEach((k) => { + (n as any)[k] = wrapperNode[k as keyof typeof wrapperNode]; }); } diff --git a/code/lib/cli/src/automigrate/fixes/wrap-require.ts b/code/lib/cli/src/automigrate/fixes/wrap-require.ts index 509b51bae989..3651eb2c57a8 100644 --- a/code/lib/cli/src/automigrate/fixes/wrap-require.ts +++ b/code/lib/cli/src/automigrate/fixes/wrap-require.ts @@ -26,6 +26,10 @@ export const wrapRequire: Fix = { const isStorybookInMonorepo = await packageManager.isStorybookInMonorepo(); const isPnp = await detectPnp(); + if (!mainConfigPath) { + return null; + } + const config = await readConfig(mainConfigPath); if (!isStorybookInMonorepo && !isPnp) { @@ -54,7 +58,7 @@ export const wrapRequire: Fix = { async run({ dryRun, mainConfigPath, result }) { return new Promise((resolve, reject) => { - updateMainConfig({ dryRun, mainConfigPath }, (mainConfig) => { + updateMainConfig({ dryRun: !!dryRun, mainConfigPath }, (mainConfig) => { try { getFieldsForRequireWrapper(mainConfig).forEach((node) => { wrapValueWithRequireWrapper(mainConfig, node); @@ -62,10 +66,10 @@ export const wrapRequire: Fix = { if (getRequireWrapperName(mainConfig) === null) { if ( - mainConfig.fileName.endsWith('.cjs') || - mainConfig.fileName.endsWith('.cts') || - mainConfig.fileName.endsWith('.cjsx') || - mainConfig.fileName.endsWith('.ctsx') + mainConfig?.fileName?.endsWith('.cjs') || + mainConfig?.fileName?.endsWith('.cts') || + mainConfig?.fileName?.endsWith('.cjsx') || + mainConfig?.fileName?.endsWith('.ctsx') ) { mainConfig.setRequireImport(['dirname', 'join'], 'path'); } else { diff --git a/code/lib/cli/src/automigrate/helpers/checkWebpack5Builder.test.ts b/code/lib/cli/src/automigrate/helpers/checkWebpack5Builder.test.ts index 89428a48fb2d..06ae7a3d251c 100644 --- a/code/lib/cli/src/automigrate/helpers/checkWebpack5Builder.test.ts +++ b/code/lib/cli/src/automigrate/helpers/checkWebpack5Builder.test.ts @@ -44,6 +44,7 @@ describe('checkWebpack5Builder', () => { it('should return null and log a warning if mainConfig is missing', async () => { const result = await checkWebpack5Builder({ + // @ts-expect-error (Type 'undefined' is not assignable to type 'StorybookConfigRaw'.) mainConfig: undefined, storybookVersion: '6.3.0', }); diff --git a/code/lib/cli/src/automigrate/helpers/eslintPlugin.ts b/code/lib/cli/src/automigrate/helpers/eslintPlugin.ts index 548b0856a74c..992f54d69120 100644 --- a/code/lib/cli/src/automigrate/helpers/eslintPlugin.ts +++ b/code/lib/cli/src/automigrate/helpers/eslintPlugin.ts @@ -47,7 +47,10 @@ export async function extractEslintInfo(packageManager: JsPackageManager): Promi return { hasEslint, isStorybookPluginInstalled, eslintConfigFile }; } -export async function configureEslintPlugin(eslintFile: string, packageManager: JsPackageManager) { +export async function configureEslintPlugin( + eslintFile: string | undefined, + packageManager: JsPackageManager +) { if (eslintFile) { paddedLog(`Configuring Storybook ESLint plugin at ${eslintFile}`); if (eslintFile.endsWith('json')) { @@ -55,7 +58,10 @@ export async function configureEslintPlugin(eslintFile: string, packageManager: const existingConfigValue = Array.isArray(eslintConfig.extends) ? eslintConfig.extends : [eslintConfig.extends].filter(Boolean); - eslintConfig.extends = [...(existingConfigValue || []), 'plugin:storybook/recommended']; + eslintConfig.extends = [ + ...(existingConfigValue || []), + 'plugin:storybook/recommended', + ] as string[]; const eslintFileContents = await readFile(eslintFile, 'utf8'); const spaces = detectIndent(eslintFileContents).amount || 2; diff --git a/code/lib/cli/src/automigrate/helpers/getMigrationSummary.ts b/code/lib/cli/src/automigrate/helpers/getMigrationSummary.ts index 583d1b9170ee..e23c29dcea4d 100644 --- a/code/lib/cli/src/automigrate/helpers/getMigrationSummary.ts +++ b/code/lib/cli/src/automigrate/helpers/getMigrationSummary.ts @@ -57,8 +57,8 @@ export function getMigrationSummary({ }: { fixResults: Record; fixSummary: FixSummary; - installationMetadata: InstallationMetadata; - logFile?: string; + installationMetadata?: InstallationMetadata | null; + logFile: string; }) { const messages = []; messages.push(getGlossaryMessages(fixSummary, fixResults, logFile).join(messageDivider)); @@ -75,7 +75,9 @@ export function getMigrationSummary({ And reach out on Discord if you need help: ${chalk.yellow('https://discord.gg/storybook')} `); - const duplicatedDepsMessage = getDuplicatedDepsWarnings(installationMetadata); + const duplicatedDepsMessage = installationMetadata + ? getDuplicatedDepsWarnings(installationMetadata) + : getDuplicatedDepsWarnings(); if (duplicatedDepsMessage) { messages.push(duplicatedDepsMessage.join(messageDivider)); diff --git a/code/lib/cli/src/automigrate/helpers/mainConfigFile.test.ts b/code/lib/cli/src/automigrate/helpers/mainConfigFile.test.ts index f31ca41f0a0f..1d1d4c2095ac 100644 --- a/code/lib/cli/src/automigrate/helpers/mainConfigFile.test.ts +++ b/code/lib/cli/src/automigrate/helpers/mainConfigFile.test.ts @@ -9,6 +9,7 @@ describe('getBuilderPackageName', () => { const packageName = getBuilderPackageName(undefined); expect(packageName).toBeNull(); + // @ts-expect-error (Argument of type 'null' is not assignable) const packageName2 = getBuilderPackageName(null); expect(packageName2).toBeNull(); }); @@ -76,6 +77,7 @@ describe('getFrameworkPackageName', () => { const packageName = getFrameworkPackageName(undefined); expect(packageName).toBeNull(); + // @ts-expect-error (Argument of type 'null' is not assignable) const packageName2 = getFrameworkPackageName(null); expect(packageName2).toBeNull(); }); @@ -132,6 +134,7 @@ describe('getFrameworkPackageName', () => { describe('getRendererPackageNameFromFramework', () => { it('should return null when given no package name', () => { + // @ts-expect-error (Argument of type 'undefined' is not assignable) const packageName = getRendererPackageNameFromFramework(undefined); expect(packageName).toBeNull(); }); diff --git a/code/lib/cli/src/automigrate/helpers/mainConfigFile.ts b/code/lib/cli/src/automigrate/helpers/mainConfigFile.ts index 188e9bd9d99b..2dd9de625704 100644 --- a/code/lib/cli/src/automigrate/helpers/mainConfigFile.ts +++ b/code/lib/cli/src/automigrate/helpers/mainConfigFile.ts @@ -84,7 +84,7 @@ export const getStorybookData = async ({ configDir: userDefinedConfigDir, }: { packageManager: JsPackageManager; - configDir: string; + configDir?: string; }) => { const packageJson = await packageManager.retrievePackageJson(); const { @@ -102,7 +102,7 @@ export const getStorybookData = async ({ mainConfig = (await loadMainConfig({ configDir, noCache: true })) as StorybookConfigRaw; } catch (err) { throw new Error( - dedent`Unable to find or evaluate ${chalk.blue(mainConfigPath)}: ${err.message}` + dedent`Unable to find or evaluate ${chalk.blue(mainConfigPath)}: ${String(err)}` ); } @@ -178,5 +178,5 @@ export const getAddonNames = (mainConfig: StorybookConfig): string[] => { .replace(/\/preset$/, ''); }); - return addonList.filter(Boolean); + return addonList.filter((item): item is NonNullable => item != null); }; diff --git a/code/lib/cli/src/automigrate/helpers/new-frameworks-utils.ts b/code/lib/cli/src/automigrate/helpers/new-frameworks-utils.ts index 3f7348e0c7a5..e5fba48f2d71 100644 --- a/code/lib/cli/src/automigrate/helpers/new-frameworks-utils.ts +++ b/code/lib/cli/src/automigrate/helpers/new-frameworks-utils.ts @@ -71,13 +71,13 @@ export const detectBuilderInfo = async ({ packageManager: JsPackageManager; }): Promise<{ name: BuilderType; options: any }> => { let builderName: BuilderType; - let builderOrFrameworkName; + let builderOrFrameworkName: string | undefined; const { core = {}, framework } = mainConfig; const { builder } = core; const builderPackageName = getBuilderPackageName(mainConfig); - const frameworkPackageName = getFrameworkPackageName(mainConfig); + const frameworkPackageName = getFrameworkPackageName(mainConfig) as string; let builderOptions = typeof builder !== 'string' ? builder?.options ?? {} : {}; @@ -133,12 +133,12 @@ export const detectBuilderInfo = async ({ if ( builderOrFrameworkName?.includes('vite') || - communityFrameworks.vite.includes(builderOrFrameworkName) + (builderOrFrameworkName && communityFrameworks.vite.includes(builderOrFrameworkName)) ) { builderName = 'vite'; } else if ( builderOrFrameworkName?.includes('webpack') || - communityFrameworks.webpack5.includes(builderOrFrameworkName) + (builderOrFrameworkName && communityFrameworks.webpack5.includes(builderOrFrameworkName)) ) { builderName = 'webpack5'; } else { @@ -154,7 +154,7 @@ export const detectBuilderInfo = async ({ }; }; -export const getNextjsAddonOptions = (addons: Preset[]) => { +export const getNextjsAddonOptions = (addons: Preset[] | undefined) => { const nextjsAddon = addons?.find((addon) => typeof addon === 'string' ? addon === 'storybook-addon-next' diff --git a/code/lib/cli/src/automigrate/index.ts b/code/lib/cli/src/automigrate/index.ts index 3e8e5af0e461..1ef8ec9ce1a2 100644 --- a/code/lib/cli/src/automigrate/index.ts +++ b/code/lib/cli/src/automigrate/index.ts @@ -8,6 +8,7 @@ import dedent from 'ts-dedent'; import { join } from 'path'; import { getStorybookInfo, loadMainConfig } from '@storybook/core-common'; +import invariant from 'tiny-invariant'; import { JsPackageManagerFactory, useNpmWarning } from '../js-package-manager'; import type { PackageManagerName } from '../js-package-manager'; @@ -63,8 +64,8 @@ export const automigrate = async ({ hideMigrationSummary = false, }: FixOptions = {}): Promise<{ fixResults: Record; - preCheckFailure: PreCheckFailure; -}> => { + preCheckFailure?: PreCheckFailure; +} | null> => { if (list) { logAvailableMigrations(); return null; @@ -181,7 +182,8 @@ export async function runFixes({ try { await loadMainConfig({ configDir }); } catch (err) { - if (err.message.includes('No configuration files have been found')) { + const errMessage = String(err); + if (errMessage.includes('No configuration files have been found')) { logger.info( dedent`[Storybook automigrate] Could not find or evaluate your Storybook main.js config directory at ${chalk.blue( configDir @@ -196,7 +198,7 @@ export async function runFixes({ logger.info( dedent`[Storybook automigrate] ❌ Failed trying to evaluate ${chalk.blue( mainConfigPath - )} with the following error: ${err.message}` + )} with the following error: ${errMessage}` ); logger.info('Please fix the error and try again.'); @@ -228,8 +230,10 @@ export async function runFixes({ }); } catch (error) { logger.info(`⚠️ failed to check fix ${chalk.bold(f.id)}`); - logger.error(`\n${error.stack}`); - fixSummary.failed[f.id] = error.message; + if (error instanceof Error) { + logger.error(`\n${error.stack}`); + fixSummary.failed[f.id] = error.message; + } fixResults[f.id] = FixStatus.CHECK_FAILED; } @@ -246,7 +250,7 @@ export async function runFixes({ }) ); - let runAnswer: { fix: boolean }; + let runAnswer: { fix: boolean } | undefined; try { if (dryRun) { @@ -303,8 +307,11 @@ export async function runFixes({ } if (!f.promptOnly) { + invariant(runAnswer, 'runAnswer must be defined if not promptOnly'); if (runAnswer.fix) { try { + invariant(typeof f.run === 'function', 'run method should be available in fix.'); + invariant(mainConfigPath, 'Main config path should be defined to run migration.'); await f.run({ result, packageManager, @@ -318,7 +325,8 @@ export async function runFixes({ fixSummary.succeeded.push(f.id); } catch (error) { fixResults[f.id] = FixStatus.FAILED; - fixSummary.failed[f.id] = error.message; + fixSummary.failed[f.id] = + error instanceof Error ? error.message : 'Failed to run migration'; logger.info(`❌ error when running ${chalk.cyan(f.id)} migration`); logger.info(error); diff --git a/code/lib/cli/src/automigrate/types.ts b/code/lib/cli/src/automigrate/types.ts index a2c124c09629..1befb24bf22c 100644 --- a/code/lib/cli/src/automigrate/types.ts +++ b/code/lib/cli/src/automigrate/types.ts @@ -15,14 +15,14 @@ export interface RunOptions { packageManager: JsPackageManager; result: ResultType; dryRun?: boolean; - mainConfigPath?: string; + mainConfigPath: string; skipInstall?: boolean; } export interface Fix { id: string; promptOnly?: boolean; - check: (options: CheckOptions) => Promise; + check: (options: CheckOptions) => Promise; prompt: (result: ResultType) => string; run?: (options: RunOptions) => Promise; } diff --git a/code/lib/cli/src/build.ts b/code/lib/cli/src/build.ts index 0cffa3a1e75c..1f5f423e3ba5 100644 --- a/code/lib/cli/src/build.ts +++ b/code/lib/cli/src/build.ts @@ -1,8 +1,11 @@ import { sync as readUpSync } from 'read-pkg-up'; import { buildStaticStandalone, withTelemetry } from '@storybook/core-server'; import { cache } from '@storybook/core-common'; +import invariant from 'tiny-invariant'; export const build = async (cliOptions: any) => { + const readUpResult = readUpSync({ cwd: __dirname }); + invariant(readUpResult, 'Failed to find the closest package.json file.'); const options = { ...cliOptions, configDir: cliOptions.configDir || './.storybook', @@ -10,7 +13,7 @@ export const build = async (cliOptions: any) => { ignorePreview: !!cliOptions.previewUrl && !cliOptions.forceBuildPreview, configType: 'PRODUCTION', cache, - packageJson: readUpSync({ cwd: __dirname }).packageJson, + packageJson: readUpResult.packageJson, }; await withTelemetry('build', { cliOptions, presetOptions: options }, () => buildStaticStandalone(options) diff --git a/code/lib/cli/src/detect.ts b/code/lib/cli/src/detect.ts index b62288bc82e5..dc02fbe4325b 100644 --- a/code/lib/cli/src/detect.ts +++ b/code/lib/cli/src/detect.ts @@ -44,7 +44,7 @@ const hasPeerDependency = ( return !!version; }; -type SearchTuple = [string, (version: string) => boolean | undefined]; +type SearchTuple = [string, ((version: string) => boolean) | undefined]; const getFrameworkPreset = ( packageJson: PackageJsonWithMaybeDeps, diff --git a/code/lib/cli/src/dev.ts b/code/lib/cli/src/dev.ts index 216a15595a73..ee82ae2cb7c4 100644 --- a/code/lib/cli/src/dev.ts +++ b/code/lib/cli/src/dev.ts @@ -4,6 +4,7 @@ import { logger, instance as npmLog } from '@storybook/node-logger'; import { buildDevStandalone, withTelemetry } from '@storybook/core-server'; import { cache } from '@storybook/core-common'; import type { CLIOptions } from '@storybook/types'; +import invariant from 'tiny-invariant'; function printError(error: any) { // this is a weird bugfix, somehow 'node-pre-gyp' is polluting the npmLog header @@ -39,13 +40,15 @@ function printError(error: any) { export const dev = async (cliOptions: CLIOptions) => { process.env.NODE_ENV = process.env.NODE_ENV || 'development'; + const readUpResult = readUpSync({ cwd: __dirname }); + invariant(readUpResult, 'Failed to find the closest package.json file.'); const options = { ...cliOptions, configDir: cliOptions.configDir || './.storybook', configType: 'DEVELOPMENT', ignorePreview: !!cliOptions.previewUrl && !cliOptions.forceBuildPreview, cache, - packageJson: readUpSync({ cwd: __dirname }).packageJson, + packageJson: readUpSync({ cwd: __dirname })?.packageJson, } as Parameters[0]; await withTelemetry( diff --git a/code/lib/cli/src/dirs.ts b/code/lib/cli/src/dirs.ts index 1dfc1045b6f5..554b003a97a3 100644 --- a/code/lib/cli/src/dirs.ts +++ b/code/lib/cli/src/dirs.ts @@ -4,6 +4,7 @@ import downloadTarball from '@ndelangen/get-tarball'; import getNpmTarballUrl from 'get-npm-tarball-url'; import * as tempy from 'tempy'; +import invariant from 'tiny-invariant'; import { externalFrameworks } from './project_types'; import type { SupportedFrameworks, SupportedRenderers } from './project_types'; import type { JsPackageManager } from './js-package-manager'; @@ -49,12 +50,14 @@ export async function getRendererDir( }) ); } catch (e) { + invariant(e instanceof Error); errors.push(e); } try { return await resolveUsingBranchInstall(packageManager, frameworkPackageName); } catch (e) { + invariant(e instanceof Error); errors.push(e); } diff --git a/code/lib/cli/src/doctor/getIncompatibleAddons.ts b/code/lib/cli/src/doctor/getIncompatibleAddons.ts index 135865f949c7..465ad661a892 100644 --- a/code/lib/cli/src/doctor/getIncompatibleAddons.ts +++ b/code/lib/cli/src/doctor/getIncompatibleAddons.ts @@ -1,4 +1,5 @@ import type { StorybookConfig } from '@storybook/types'; +import type { SemVer } from 'semver'; import semver from 'semver'; import { getAddonNames } from '../automigrate/helpers/mainConfigFile'; import { JsPackageManagerFactory } from '../js-package-manager'; @@ -36,7 +37,9 @@ export const getIncompatibleAddons = async ( '@storybook/addon-queryparams': '6.2.9', }; - const addons = getAddonNames(mainConfig).filter((addon) => addon in incompatibleList); + const addons = getAddonNames(mainConfig).filter( + (addon): addon is keyof typeof incompatibleList => addon in incompatibleList + ); const dependencies = await packageManager.getAllDependencies(); const storybookPackages = Object.keys(dependencies).filter((dep) => dep.includes('storybook')); @@ -59,11 +62,15 @@ export const getIncompatibleAddons = async ( const incompatibleAddons: { name: string; version: string }[] = []; addonVersions.forEach(({ name, version: installedVersion }) => { - if (installedVersion === null) return; + if (installedVersion === null) { + return; + } const addonVersion = incompatibleList[name]; try { - if (semver.lte(semver.coerce(installedVersion), semver.coerce(addonVersion))) { + if ( + semver.lte(semver.coerce(installedVersion) as SemVer, semver.coerce(addonVersion) as SemVer) + ) { incompatibleAddons.push({ name, version: installedVersion }); } } catch (err) { diff --git a/code/lib/cli/src/doctor/getMismatchingVersionsWarning.ts b/code/lib/cli/src/doctor/getMismatchingVersionsWarning.ts index 9aa0d424e01d..665bfa92a93c 100644 --- a/code/lib/cli/src/doctor/getMismatchingVersionsWarning.ts +++ b/code/lib/cli/src/doctor/getMismatchingVersionsWarning.ts @@ -4,7 +4,10 @@ import { frameworkPackages } from '@storybook/core-common'; import type { InstallationMetadata } from '../js-package-manager/types'; import storybookCorePackages from '../versions'; -function getPrimaryVersion(name: string, installationMetadata?: InstallationMetadata) { +function getPrimaryVersion(name: string | undefined, installationMetadata?: InstallationMetadata) { + if (!name) { + return undefined; + } const packageMetadata = installationMetadata?.dependencies[name]; if (!packageMetadata) { return undefined; @@ -19,7 +22,7 @@ export function getMismatchingVersionsWarnings( ): string | undefined { const messages: string[] = []; try { - const frameworkPackageName = Object.keys(installationMetadata?.dependencies).find( + const frameworkPackageName = Object.keys(installationMetadata?.dependencies || []).find( (packageName) => { return Object.keys(frameworkPackages).includes(packageName); } @@ -41,7 +44,7 @@ export function getMismatchingVersionsWarnings( let packageToDisplay: string; if (semver.lt(cliVersion, frameworkVersion)) { versionToCompare = frameworkVersion; - packageToDisplay = frameworkPackageName; + packageToDisplay = frameworkPackageName as string; } else { versionToCompare = cliVersion; packageToDisplay = 'storybook'; @@ -53,7 +56,7 @@ export function getMismatchingVersionsWarnings( )} (from the ${chalk.cyan(packageToDisplay)} package) or higher.` ); - const filteredDependencies = Object.entries(installationMetadata?.dependencies).filter( + const filteredDependencies = Object.entries(installationMetadata?.dependencies || []).filter( ([name, packages]) => { if (Object.keys(storybookCorePackages).includes(name)) { const packageVersion = packages[0].version; @@ -71,7 +74,7 @@ export function getMismatchingVersionsWarnings( .map( ([name, dep]) => `${chalk.hex('#ff9800')(name)}: ${dep[0].version} ${ - allDependencies[name] ? '(in your package.json)' : '' + allDependencies?.[name] ? '(in your package.json)' : '' }` ) .join('\n') @@ -84,7 +87,7 @@ export function getMismatchingVersionsWarnings( )} to upgrade all of your Storybook packages to the latest version. Alternatively you can try manually changing the versions to match in your package.json. We also recommend regenerating your lockfile, or running the following command to possibly deduplicate your Storybook package versions: ${chalk.cyan( - installationMetadata.dedupeCommand + installationMetadata?.dedupeCommand )}` ); diff --git a/code/lib/cli/src/doctor/index.ts b/code/lib/cli/src/doctor/index.ts index cfafb899f3a4..a6a304ce1ea6 100644 --- a/code/lib/cli/src/doctor/index.ts +++ b/code/lib/cli/src/doctor/index.ts @@ -65,7 +65,7 @@ export const doctor = async ({ }); storybookVersion = storybookData.storybookVersion; mainConfig = storybookData.mainConfig; - } catch (err) { + } catch (err: any) { if (err.message.includes('No configuration files have been found')) { logger.info( dedent`[Storybook doctor] Could not find or evaluate your Storybook main.js config directory at ${chalk.blue( @@ -85,6 +85,10 @@ export const doctor = async ({ process.exit(1); } + if (!mainConfig) { + throw new Error('mainConfig is undefined'); + } + const incompatibleAddonList = await getIncompatibleAddons(mainConfig); if (incompatibleAddonList.length > 0) { diagnosticMessages.push(incompatibleAddons.prompt({ incompatibleAddonList })); @@ -95,7 +99,7 @@ export const doctor = async ({ 'storybook', ]); - const allDependencies = await packageManager.getAllDependencies(); + const allDependencies = (await packageManager.getAllDependencies()) as Record; const mismatchingVersionMessage = getMismatchingVersionsWarnings( installationMetadata, allDependencies @@ -103,7 +107,12 @@ export const doctor = async ({ if (mismatchingVersionMessage) { diagnosticMessages.push(mismatchingVersionMessage); } else { - diagnosticMessages.push(getDuplicatedDepsWarnings(installationMetadata)?.join('\n')); + const list = installationMetadata + ? getDuplicatedDepsWarnings(installationMetadata) + : getDuplicatedDepsWarnings(); + if (list) { + diagnosticMessages.push(list?.join('\n')); + } } logger.info(); diff --git a/code/lib/cli/src/generate.ts b/code/lib/cli/src/generate.ts index c1572af2c132..bc396ec428b7 100644 --- a/code/lib/cli/src/generate.ts +++ b/code/lib/cli/src/generate.ts @@ -7,6 +7,7 @@ import { sync as readUpSync } from 'read-pkg-up'; import { logger } from '@storybook/node-logger'; import { addToGlobalContext } from '@storybook/telemetry'; +import invariant from 'tiny-invariant'; import type { CommandOptions } from './generators/types'; import { initiate } from './initiate'; import { add } from './add'; @@ -25,7 +26,9 @@ import { doctor } from './doctor'; addToGlobalContext('cliVersion', versions.storybook); -const pkg = readUpSync({ cwd: __dirname }).packageJson; +const readUpResult = readUpSync({ cwd: __dirname }); +invariant(readUpResult, 'Failed to find the closest package.json file.'); +const pkg = readUpResult.packageJson; const consoleLogger = console; const command = (name: string) => diff --git a/code/lib/cli/src/generators/ANGULAR/index.ts b/code/lib/cli/src/generators/ANGULAR/index.ts index c8c7f3f288b3..4a368e8d0af3 100644 --- a/code/lib/cli/src/generators/ANGULAR/index.ts +++ b/code/lib/cli/src/generators/ANGULAR/index.ts @@ -42,7 +42,7 @@ const generator: Generator<{ projectName: string }> = async ( const { root, projectType } = angularProject; const { projects } = angularJSON; - const useCompodoc = commandOptions.yes ? true : await promptForCompoDocs(); + const useCompodoc = commandOptions?.yes ? true : await promptForCompoDocs(); const storybookFolder = root ? `${root}/.storybook` : '.storybook'; angularJSON.addStorybookEntries({ diff --git a/code/lib/cli/src/generators/REACT_NATIVE/index.ts b/code/lib/cli/src/generators/REACT_NATIVE/index.ts index dc3e14ed0f7e..e3b8dcfa50c0 100644 --- a/code/lib/cli/src/generators/REACT_NATIVE/index.ts +++ b/code/lib/cli/src/generators/REACT_NATIVE/index.ts @@ -34,12 +34,13 @@ const generator = async ( const babelDependencies = await getBabelDependencies(packageManager, packageJson); - const packages = [ - ...babelDependencies, - ...packagesWithFixedVersion, - ...resolvedPackages, - missingReactDom && reactVersion && `react-dom@${reactVersion}`, - ].filter(Boolean); + const packages: string[] = []; + packages.push(...babelDependencies); + packages.push(...packagesWithFixedVersion); + packages.push(...resolvedPackages); + if (missingReactDom && reactVersion) { + packages.push(`react-dom@${reactVersion}`); + } await packageManager.addDependencies({ ...npmOptions, packageJson }, packages); packageManager.addScripts({ diff --git a/code/lib/cli/src/generators/baseGenerator.ts b/code/lib/cli/src/generators/baseGenerator.ts index 6083fae9e873..813ba2d1dd59 100644 --- a/code/lib/cli/src/generators/baseGenerator.ts +++ b/code/lib/cli/src/generators/baseGenerator.ts @@ -2,6 +2,7 @@ import path from 'path'; import fse from 'fs-extra'; import { dedent } from 'ts-dedent'; import ora from 'ora'; +import invariant from 'tiny-invariant'; import type { NpmOptions } from '../NpmOptions'; import type { SupportedRenderers, SupportedFrameworks, Builder } from '../project_types'; import { SupportedLanguage, externalFrameworks, CoreBuilder } from '../project_types'; @@ -52,7 +53,7 @@ const getBuilderDetails = (builder: string) => { return builder; }; -const getExternalFramework = (framework: string) => +const getExternalFramework = (framework?: string) => externalFrameworks.find( (exFramework) => framework !== undefined && @@ -61,7 +62,7 @@ const getExternalFramework = (framework: string) => exFramework?.frameworks?.some?.((item) => item === framework)) ); -const getFrameworkPackage = (framework: string, renderer: string, builder: string) => { +const getFrameworkPackage = (framework: string | undefined, renderer: string, builder: string) => { const externalFramework = getExternalFramework(framework); const storybookBuilder = builder?.replace(/^@storybook\/builder-/, ''); const storybookFramework = framework?.replace(/^@storybook\//, ''); @@ -92,7 +93,7 @@ const getFrameworkPackage = (framework: string, renderer: string, builder: strin return externalFramework.packageName; }; -const getRendererPackage = (framework: string, renderer: string) => { +const getRendererPackage = (framework: string | undefined, renderer: string) => { const externalFramework = getExternalFramework(framework); if (externalFramework !== undefined) return externalFramework.renderer || externalFramework.packageName; @@ -118,12 +119,13 @@ const getFrameworkDetails = ( rendererId: SupportedRenderers; } => { const frameworkPackage = getFrameworkPackage(framework, renderer, builder); + invariant(frameworkPackage, 'Missing framework package.'); const frameworkPackagePath = shouldApplyRequireWrapperOnPackageNames ? applyRequireWrapper(frameworkPackage) : frameworkPackage; - const rendererPackage = getRendererPackage(framework, renderer); + const rendererPackage = getRendererPackage(framework, renderer) as string; const rendererPackagePath = shouldApplyRequireWrapperOnPackageNames ? applyRequireWrapper(rendererPackage) : rendererPackage; @@ -170,7 +172,7 @@ const hasInteractiveStories = (rendererId: SupportedRenderers) => ); const hasFrameworkTemplates = (framework?: SupportedFrameworks) => - ['angular', 'nextjs'].includes(framework); + framework ? ['angular', 'nextjs'].includes(framework) : false; export async function baseGenerator( packageManager: JsPackageManager, @@ -232,7 +234,7 @@ export async function baseGenerator( ...options, }; - const swc = useSWC({ builder }); + const swc = useSWC ? useSWC({ builder }) : false; if (swc) { skipBabel = true; @@ -241,8 +243,8 @@ export async function baseGenerator( const extraAddonsToInstall = typeof extraAddonPackages === 'function' ? await extraAddonPackages({ - builder: builder || builderInclude, - framework: framework || frameworkInclude, + builder: (builder || builderInclude) as string, + framework: (framework || frameworkInclude) as string, }) : extraAddonPackages; @@ -250,7 +252,7 @@ export async function baseGenerator( const addons = [ '@storybook/addon-links', '@storybook/addon-essentials', - ...stripVersions(extraAddonsToInstall), + ...stripVersions(extraAddonsToInstall || []), ].filter(Boolean); // added to package.json @@ -258,7 +260,7 @@ export async function baseGenerator( '@storybook/addon-links', '@storybook/addon-essentials', '@storybook/blocks', - ...extraAddonsToInstall, + ...(extraAddonsToInstall || []), ].filter(Boolean); if (hasInteractiveStories(rendererId)) { @@ -303,8 +305,8 @@ export async function baseGenerator( const extraPackagesToInstall = typeof extraPackages === 'function' ? await extraPackages({ - builder: builder || builderInclude, - framework: framework || frameworkInclude, + builder: (builder || builderInclude) as string, + framework: (framework || frameworkInclude) as string, }) : extraPackages; @@ -313,11 +315,12 @@ export async function baseGenerator( getExternalFramework(rendererId) ? undefined : `@storybook/${rendererId}`, ...frameworkPackages, ...addonPackages, - ...extraPackagesToInstall, + ...(extraPackagesToInstall || []), ].filter(Boolean); const packages = [...new Set(allPackages)].filter( - (packageToInstall) => !installedDependencies.has(getPackageDetails(packageToInstall)[0]) + (packageToInstall) => + !installedDependencies.has(getPackageDetails(packageToInstall as string)[0]) ); logger.log(); @@ -325,7 +328,7 @@ export async function baseGenerator( indent: 2, text: `Getting the correct version of ${packages.length} packages`, }).start(); - const versionedPackages = await packageManager.getVersionedPackages(packages); + const versionedPackages = await packageManager.getVersionedPackages(packages as string[]); versionedPackagesSpinner.succeed(); const depsToInstall = [...versionedPackages]; @@ -371,7 +374,7 @@ export async function baseGenerator( if (hasEslint && !isStorybookPluginInstalled) { if (skipPrompts || (await suggestESLintPlugin())) { depsToInstall.push('eslint-plugin-storybook'); - await configureEslintPlugin(eslintConfigFile, packageManager); + await configureEslintPlugin(eslintConfigFile ?? undefined, packageManager); } } } @@ -447,7 +450,7 @@ export async function baseGenerator( await configurePreview({ frameworkPreviewParts, - storybookConfigFolder, + storybookConfigFolder: storybookConfigFolder as string, language, rendererId, }); @@ -460,6 +463,9 @@ export async function baseGenerator( if (addComponents) { const templateLocation = hasFrameworkTemplates(framework) ? framework : rendererId; + if (!templateLocation) { + throw new Error(`Could not find template location for ${framework} or ${rendererId}`); + } await copyTemplateFiles({ renderer: templateLocation, packageManager, diff --git a/code/lib/cli/src/helpers.test.ts b/code/lib/cli/src/helpers.test.ts index 01897444db11..c15ae604a1a9 100644 --- a/code/lib/cli/src/helpers.test.ts +++ b/code/lib/cli/src/helpers.test.ts @@ -168,4 +168,13 @@ describe('Helpers', () => { }).toThrowError("Couldn't find any official storybook packages in package.json"); }); }); + + describe('coerceSemver', () => { + it(`should throw if the version argument is invalid semver string`, () => { + const invalidSemverString = 'hello, world'; + expect(() => { + helpers.coerceSemver(invalidSemverString); + }).toThrowError(`Could not coerce ${invalidSemverString} into a semver.`); + }); + }); }); diff --git a/code/lib/cli/src/helpers.ts b/code/lib/cli/src/helpers.ts index db5cfb34d1ac..605fab2d1849 100644 --- a/code/lib/cli/src/helpers.ts +++ b/code/lib/cli/src/helpers.ts @@ -3,10 +3,11 @@ import chalk from 'chalk'; import fs from 'fs'; import fse from 'fs-extra'; import path, { join } from 'path'; -import { satisfies } from 'semver'; +import { coerce, satisfies } from 'semver'; import stripJsonComments from 'strip-json-comments'; import findUp from 'find-up'; +import invariant from 'tiny-invariant'; import { getCliDir, getRendererDir } from './dirs'; import type { JsPackageManager, @@ -163,8 +164,14 @@ export function addToDevDependenciesIfNotPresent( name: string, packageVersion: string ) { - if (!packageJson.dependencies[name] && !packageJson.devDependencies[name]) { - packageJson.devDependencies[name] = packageVersion; + if (!packageJson.dependencies?.[name] && !packageJson.devDependencies?.[name]) { + if (packageJson.devDependencies) { + packageJson.devDependencies[name] = packageVersion; + } else { + packageJson.devDependencies = { + [name]: packageVersion, + }; + } } } @@ -287,11 +294,9 @@ export async function adjustTemplate(templatePath: string, templateData: Record< // and if it exists, returns the version of that package from the specified package.json export function getStorybookVersionSpecifier(packageJson: PackageJsonWithDepsAndDevDeps) { const allDeps = { ...packageJson.dependencies, ...packageJson.devDependencies }; - const storybookPackage = Object.keys(allDeps).find( - (name: keyof typeof storybookMonorepoPackages) => { - return storybookMonorepoPackages[name]; - } - ); + const storybookPackage = Object.keys(allDeps).find((name: string) => { + return storybookMonorepoPackages[name as keyof typeof storybookMonorepoPackages]; + }); if (!storybookPackage) { throw new Error(`Couldn't find any official storybook packages in package.json`); @@ -303,3 +308,9 @@ export function getStorybookVersionSpecifier(packageJson: PackageJsonWithDepsAnd export async function isNxProject() { return findUp.sync('nx.json'); } + +export function coerceSemver(version: string) { + const coercedSemver = coerce(version); + invariant(coercedSemver != null, `Could not coerce ${version} into a semver.`); + return coercedSemver; +} diff --git a/code/lib/cli/src/initiate.ts b/code/lib/cli/src/initiate.ts index 3afd703a1e08..8811b88c5d85 100644 --- a/code/lib/cli/src/initiate.ts +++ b/code/lib/cli/src/initiate.ts @@ -9,6 +9,7 @@ import { NxProjectDetectedError } from '@storybook/core-events/server-errors'; import dedent from 'ts-dedent'; import boxen from 'boxen'; import { readdirSync } from 'fs-extra'; +import type { Builder } from './project_types'; import { installableProjectTypes, ProjectType } from './project_types'; import { detect, isStorybookInstantiated, detectLanguage, detectPnp } from './detect'; import { commandLog, codeLog, paddedLog } from './helpers'; @@ -53,10 +54,10 @@ const installStorybook = async ( const generatorOptions: GeneratorOptions = { language, - builder: options.builder, + builder: options.builder as Builder, linkable: !!options.linkable, - pnp: pnp || options.usePnp, - yes: options.yes, + pnp: pnp || (options.usePnp as boolean), + yes: options.yes as boolean, projectType, }; @@ -187,7 +188,7 @@ const installStorybook = async ( try { return await runGenerator(); - } catch (err) { + } catch (err: any) { if (err?.message !== 'Canceled by the user' && err?.stack) { logger.error(`\n ${chalk.red(err.stack)}`); } @@ -336,9 +337,9 @@ async function doInitiate( } } else { try { - projectType = await detect(packageManager, options); + projectType = (await detect(packageManager, options)) as ProjectType; } catch (err) { - done(err.message); + done(String(err)); throw new HandledError(err); } } @@ -430,7 +431,7 @@ export async function initiate(options: CommandOptions, pkg: PackageJson): Promi () => doInitiate(options, pkg) ); - if (initiateResult.shouldRunDev) { + if (initiateResult?.shouldRunDev) { const { projectType, packageManager, storybookCommand } = initiateResult; logger.log('\nRunning Storybook'); diff --git a/code/lib/cli/src/js-package-manager/JsPackageManager.ts b/code/lib/cli/src/js-package-manager/JsPackageManager.ts index 1cb3d93ba8c7..5a3e82be3c43 100644 --- a/code/lib/cli/src/js-package-manager/JsPackageManager.ts +++ b/code/lib/cli/src/js-package-manager/JsPackageManager.ts @@ -6,7 +6,8 @@ import path from 'path'; import fs from 'fs'; import dedent from 'ts-dedent'; -import { readFile, readFileSync, writeFile } from 'fs-extra'; +import { readFile, writeFile, readFileSync } from 'fs-extra'; +import invariant from 'tiny-invariant'; import { commandLog } from '../helpers'; import type { PackageJson, PackageJsonWithDepsAndDevDeps } from './PackageJson'; import storybookPackagesVersions from '../versions'; @@ -143,6 +144,9 @@ export abstract class JsPackageManager { } packageJsonPath(): string { + if (!this.cwd) { + throw new Error('Missing cwd'); + } return path.resolve(this.cwd, 'package.json'); } @@ -191,15 +195,14 @@ export abstract class JsPackageManager { try { packageJson = await this.readPackageJson(); } catch (err) { - if (err.message.includes('Could not read package.json')) { + const errMessage = String(err); + if (errMessage.includes('Could not read package.json')) { await this.initPackageJson(); packageJson = await this.readPackageJson(); } else { throw new Error( dedent` - There was an error while reading the package.json file at ${this.packageJsonPath()}: ${ - err.message - } + There was an error while reading the package.json file at ${this.packageJsonPath()}: ${errMessage} Please fix the error and try again. ` ); @@ -214,7 +217,7 @@ export abstract class JsPackageManager { }; } - public async getAllDependencies(): Promise> { + public async getAllDependencies(): Promise>> { const { dependencies, devDependencies, peerDependencies } = await this.retrievePackageJson(); return { @@ -249,6 +252,7 @@ export abstract class JsPackageManager { if (skipInstall) { const { packageJson } = options; + invariant(packageJson, 'Missing packageJson.'); const dependenciesMap = dependencies.reduce((acc, dep) => { const [packageName, packageVersion] = getPackageDetails(dep); @@ -269,8 +273,8 @@ export abstract class JsPackageManager { await this.writePackageJson(packageJson); } else { try { - await this.runAddDeps(dependencies, options.installAsDevDependencies); - } catch (e) { + await this.runAddDeps(dependencies, Boolean(options.installAsDevDependencies)); + } catch (e: any) { logger.error('\nAn error occurred while installing dependencies:'); logger.log(e.message); throw new HandledError(e); @@ -301,6 +305,7 @@ export abstract class JsPackageManager { if (skipInstall) { const { packageJson } = options; + invariant(packageJson, 'Missing packageJson.'); dependencies.forEach((dep) => { if (packageJson.devDependencies) { delete packageJson.devDependencies[dep]; @@ -316,7 +321,7 @@ export abstract class JsPackageManager { await this.runRemoveDeps(dependencies); } catch (e) { logger.error('An error occurred while removing dependencies.'); - logger.log(e.message); + logger.log(String(e)); throw new HandledError(e); } } @@ -360,7 +365,7 @@ export abstract class JsPackageManager { * @param constraint A valid semver constraint, example: '1.x || >=2.5.0 || 5.0.0 - 7.2.3' */ public async getVersion(packageName: string, constraint?: string): Promise { - let current: string; + let current: string | undefined; if (/(@storybook|^sb$|^storybook$)/.test(packageName)) { // @ts-expect-error (Converted from ts-ignore) @@ -372,11 +377,11 @@ export abstract class JsPackageManager { latest = await this.latestVersion(packageName, constraint); } catch (e) { if (current) { - logger.warn(`\n ${chalk.yellow(e.message)}`); + logger.warn(`\n ${chalk.yellow(String(e))}`); return current; } - logger.error(`\n ${chalk.red(e.message)}`); + logger.error(`\n ${chalk.red(String(e))}`); throw new HandledError(e); } @@ -401,8 +406,14 @@ export abstract class JsPackageManager { const versions = await this.runGetVersions(packageName, true); - // Get the latest version satisfying the constraint - return versions.reverse().find((version) => satisfies(version, constraint)); + const latestVersionSatisfyingTheConstraint = versions + .reverse() + .find((version) => satisfies(version, constraint)); + invariant( + latestVersionSatisfyingTheConstraint != null, + 'No version satisfying the constraint.' + ); + return latestVersionSatisfyingTheConstraint; } public async addStorybookCommandInScripts(options?: { port: number; preCommand?: string }) { diff --git a/code/lib/cli/src/js-package-manager/PNPMProxy.ts b/code/lib/cli/src/js-package-manager/PNPMProxy.ts index 33571c0e4dda..765b5bd55063 100644 --- a/code/lib/cli/src/js-package-manager/PNPMProxy.ts +++ b/code/lib/cli/src/js-package-manager/PNPMProxy.ts @@ -137,7 +137,7 @@ export class PNPMProxy extends JsPackageManager { ); return packageJSON; - } catch (error) { + } catch (error: any) { if (error.code !== 'MODULE_NOT_FOUND') { console.error('Error while fetching package version in PNPM PnP mode:', error); } diff --git a/code/lib/cli/src/js-package-manager/Yarn2Proxy.ts b/code/lib/cli/src/js-package-manager/Yarn2Proxy.ts index b1780c474404..f69d7c8d0b3d 100644 --- a/code/lib/cli/src/js-package-manager/Yarn2Proxy.ts +++ b/code/lib/cli/src/js-package-manager/Yarn2Proxy.ts @@ -151,7 +151,7 @@ export class Yarn2Proxy extends JsPackageManager { const virtualPath = path.join(pkg.packageLocation, 'package.json'); return crossFs.readJsonSync(virtualPath); - } catch (error) { + } catch (error: any) { if (error.code !== 'MODULE_NOT_FOUND') { console.error('Error while fetching package version in Yarn PnP mode:', error); } diff --git a/code/lib/cli/src/link.ts b/code/lib/cli/src/link.ts index 4531256c9960..3af08643874b 100644 --- a/code/lib/cli/src/link.ts +++ b/code/lib/cli/src/link.ts @@ -45,7 +45,9 @@ export const exec = async ( resolve(undefined); } else { logger.error(chalk.red(`An error occurred while executing: \`${command}\``)); - logger.info(errorMessage); + if (errorMessage) { + logger.info(errorMessage); + } reject(new Error(`command exited with code: ${code}: `)); } }); diff --git a/code/lib/cli/src/project_types.ts b/code/lib/cli/src/project_types.ts index 05c64b402423..8d2809f97379 100644 --- a/code/lib/cli/src/project_types.ts +++ b/code/lib/cli/src/project_types.ts @@ -2,12 +2,18 @@ import { minVersion, validRange } from 'semver'; function ltMajor(versionRange: string, major: number) { // Uses validRange to avoid a throw from minVersion if an invalid range gets passed - return validRange(versionRange) && minVersion(versionRange).major < major; + if (validRange(versionRange)) { + return (minVersion(versionRange)?.major ?? Infinity) < major; + } + return false; } function eqMajor(versionRange: string, major: number) { // Uses validRange to avoid a throw from minVersion if an invalid range gets passed - return validRange(versionRange) && minVersion(versionRange).major === major; + if (validRange(versionRange)) { + return minVersion(versionRange)?.major === major; + } + return false; } /** A list of all frameworks that are supported, but use a package outside the storybook monorepo */ @@ -124,7 +130,7 @@ export const supportedTemplates: TemplateConfiguration[] = [ vuetify: (versionRange) => ltMajor(versionRange, 3), }, matcherFunction: ({ dependencies }) => { - return dependencies.some(Boolean); + return dependencies?.some(Boolean) ?? false; }, }, { @@ -135,7 +141,7 @@ export const supportedTemplates: TemplateConfiguration[] = [ nuxt: (versionRange) => ltMajor(versionRange, 3), }, matcherFunction: ({ dependencies }) => { - return dependencies.some(Boolean); + return dependencies?.some(Boolean) ?? false; }, }, { @@ -145,42 +151,42 @@ export const supportedTemplates: TemplateConfiguration[] = [ vue: (versionRange) => versionRange === 'next' || eqMajor(versionRange, 3), }, matcherFunction: ({ dependencies }) => { - return dependencies.some(Boolean); + return dependencies?.some(Boolean) ?? false; }, }, { preset: ProjectType.EMBER, dependencies: ['ember-cli'], matcherFunction: ({ dependencies }) => { - return dependencies.every(Boolean); + return dependencies?.every(Boolean) ?? true; }, }, { preset: ProjectType.NEXTJS, dependencies: ['next'], matcherFunction: ({ dependencies }) => { - return dependencies.every(Boolean); + return dependencies?.every(Boolean) ?? true; }, }, { preset: ProjectType.QWIK, dependencies: ['@builder.io/qwik'], matcherFunction: ({ dependencies }) => { - return dependencies.every(Boolean); + return dependencies?.every(Boolean) ?? true; }, }, { preset: ProjectType.REACT_PROJECT, peerDependencies: ['react'], matcherFunction: ({ peerDependencies }) => { - return peerDependencies.every(Boolean); + return peerDependencies?.every(Boolean) ?? true; }, }, { preset: ProjectType.REACT_NATIVE, dependencies: ['react-native', 'react-native-scripts'], matcherFunction: ({ dependencies }) => { - return dependencies.some(Boolean); + return dependencies?.some(Boolean) ?? false; }, }, { @@ -190,28 +196,28 @@ export const supportedTemplates: TemplateConfiguration[] = [ // For standard CRA projects dependencies: ['react-scripts'], matcherFunction: ({ dependencies, files }) => { - return dependencies.every(Boolean) || files.every(Boolean); + return (dependencies?.every(Boolean) || files?.every(Boolean)) ?? false; }, }, { preset: ProjectType.ANGULAR, dependencies: ['@angular/core'], matcherFunction: ({ dependencies }) => { - return dependencies.every(Boolean); + return dependencies?.every(Boolean) ?? true; }, }, { preset: ProjectType.WEB_COMPONENTS, dependencies: ['lit-element', 'lit-html', 'lit'], matcherFunction: ({ dependencies }) => { - return dependencies.some(Boolean); + return dependencies?.some(Boolean) ?? false; }, }, { preset: ProjectType.PREACT, dependencies: ['preact'], matcherFunction: ({ dependencies }) => { - return dependencies.every(Boolean); + return dependencies?.every(Boolean) ?? true; }, }, { @@ -219,21 +225,21 @@ export const supportedTemplates: TemplateConfiguration[] = [ preset: ProjectType.SVELTEKIT, dependencies: ['@sveltejs/kit'], matcherFunction: ({ dependencies }) => { - return dependencies.every(Boolean); + return dependencies?.every(Boolean) ?? true; }, }, { preset: ProjectType.SVELTE, dependencies: ['svelte'], matcherFunction: ({ dependencies }) => { - return dependencies.every(Boolean); + return dependencies?.every(Boolean) ?? true; }, }, { preset: ProjectType.SOLID, dependencies: ['solid-js'], matcherFunction: ({ dependencies }) => { - return dependencies.every(Boolean); + return dependencies?.every(Boolean) ?? true; }, }, // DO NOT MOVE ANY TEMPLATES BELOW THIS LINE @@ -242,14 +248,14 @@ export const supportedTemplates: TemplateConfiguration[] = [ preset: ProjectType.WEBPACK_REACT, dependencies: ['react', 'webpack'], matcherFunction: ({ dependencies }) => { - return dependencies.every(Boolean); + return dependencies?.every(Boolean) ?? true; }, }, { preset: ProjectType.REACT, dependencies: ['react'], matcherFunction: ({ dependencies }) => { - return dependencies.every(Boolean); + return dependencies?.every(Boolean) ?? true; }, }, ]; @@ -264,7 +270,7 @@ export const unsupportedTemplate: TemplateConfiguration = { nuxt: (versionRange) => eqMajor(versionRange, 3), }, matcherFunction: ({ dependencies }) => { - return dependencies.some(Boolean); + return dependencies?.some(Boolean) ?? false; }, }; diff --git a/code/lib/cli/src/repro-generators/scripts.ts b/code/lib/cli/src/repro-generators/scripts.ts index 2aa59fc2ee40..17241f4b037f 100644 --- a/code/lib/cli/src/repro-generators/scripts.ts +++ b/code/lib/cli/src/repro-generators/scripts.ts @@ -47,7 +47,7 @@ type ExecOptions = globalThis.Parameters[2]; export interface Options extends Parameters { appName: string; creationPath: string; - cwd?: string; + cwd: string; e2e: boolean; pnp: boolean; } @@ -186,7 +186,7 @@ const addAdditionalFiles = async ({ additionalFiles, cwd }: Options) => { logger.info(`⤵️ Adding required files`); await Promise.all( - additionalFiles.map(async (file) => { + (additionalFiles ?? []).map(async (file) => { await outputFile(path.resolve(cwd, file.path), file.contents, { encoding: 'utf-8' }); }) ); diff --git a/code/lib/cli/src/sandbox.ts b/code/lib/cli/src/sandbox.ts index 2938414adbfa..397fda11830f 100644 --- a/code/lib/cli/src/sandbox.ts +++ b/code/lib/cli/src/sandbox.ts @@ -6,6 +6,7 @@ import { dedent } from 'ts-dedent'; import { downloadTemplate } from 'giget'; import { existsSync, readdir } from 'fs-extra'; +import invariant from 'tiny-invariant'; import type { Template, TemplateKey } from './sandbox-templates'; import { allTemplates as TEMPLATES } from './sandbox-templates'; @@ -105,7 +106,7 @@ export const sandbox = async ({ return; } - selectedConfig = TEMPLATES[selectedTemplate]; + selectedConfig = selectedTemplate ? TEMPLATES[selectedTemplate] : undefined; if (!selectedConfig) { throw new Error('🚨 Sandbox: please specify a valid template type'); @@ -124,7 +125,7 @@ export const sandbox = async ({ type: 'text', message: 'Enter the output directory', name: 'directory', - initial: outputDirectoryName, + initial: outputDirectoryName ?? undefined, validate: async (directoryName) => existsSync(directoryName) ? `${directoryName} already exists. Please choose another name.` @@ -139,6 +140,7 @@ export const sandbox = async ({ ); selectedDirectory = directory; } + invariant(selectedDirectory); try { const templateDestination = path.isAbsolute(selectedDirectory) @@ -166,7 +168,7 @@ export const sandbox = async ({ ); } } catch (err) { - logger.error(`🚨 Failed to download sandbox template: ${err.message}`); + logger.error(`🚨 Failed to download sandbox template: ${String(err)}`); throw err; } diff --git a/code/lib/cli/src/upgrade.ts b/code/lib/cli/src/upgrade.ts index dd31f177df53..aefd6bc8bc07 100644 --- a/code/lib/cli/src/upgrade.ts +++ b/code/lib/cli/src/upgrade.ts @@ -6,7 +6,7 @@ import { withTelemetry } from '@storybook/core-server'; import type { PackageJsonWithMaybeDeps, PackageManagerName } from './js-package-manager'; import { getPackageDetails, JsPackageManagerFactory, useNpmWarning } from './js-package-manager'; -import { commandLog } from './helpers'; +import { coerceSemver, commandLog } from './helpers'; import { automigrate } from './automigrate'; import { isCorePackage } from './utils'; @@ -51,7 +51,7 @@ export const checkVersionConsistency = () => { .split('\n'); const storybookPackages = lines .map(getStorybookVersion) - .filter(Boolean) + .filter((item): item is NonNullable => !!item) .filter((pkg) => isCorePackage(pkg.package)); if (!storybookPackages.length) { logger.warn('No storybook core packages found.'); @@ -97,9 +97,9 @@ export const addExtraFlags = ( (acc, entry) => { const [pattern, extra] = entry; const [pkg, specifier] = getPackageDetails(pattern); - const pkgVersion = dependencies[pkg] || devDependencies[pkg]; + const pkgVersion = dependencies?.[pkg] || devDependencies?.[pkg]; - if (pkgVersion && semver.satisfies(semver.coerce(pkgVersion), specifier)) { + if (pkgVersion && specifier && semver.satisfies(coerceSemver(pkgVersion), specifier)) { return [...acc, ...extra]; } diff --git a/code/lib/cli/tsconfig.json b/code/lib/cli/tsconfig.json index b0f65014db55..4b4e13c55d49 100644 --- a/code/lib/cli/tsconfig.json +++ b/code/lib/cli/tsconfig.json @@ -2,10 +2,10 @@ "extends": "../../tsconfig.json", "compilerOptions": { "types": ["node", "jest"], - "strict": false, + "strict": true, "skipLibCheck": true, - "strictNullChecks": false, - "resolveJsonModule": true + "resolveJsonModule": true, + "noEmit": true }, "include": ["src/**/*"] } diff --git a/code/presets/create-react-app/src/helpers/processCraConfig.ts b/code/presets/create-react-app/src/helpers/processCraConfig.ts index b0931784cf5a..c023e2c08ac1 100644 --- a/code/presets/create-react-app/src/helpers/processCraConfig.ts +++ b/code/presets/create-react-app/src/helpers/processCraConfig.ts @@ -63,7 +63,7 @@ export const processCraConfig = async ( return [ ...rules, { - // @ts-expect-error (not type correct) + // @ts-expect-error (broken typings from webpack) oneOf: oneOf.map((oneOfRule: RuleSetRule): RuleSetRule => { if (oneOfRule.type === 'asset/resource') { if (isStorybook530) { diff --git a/code/yarn.lock b/code/yarn.lock index d833f34c6910..ee2f0e4ba4d6 100644 --- a/code/yarn.lock +++ b/code/yarn.lock @@ -5437,6 +5437,7 @@ __metadata: slash: "npm:^5.0.0" strip-json-comments: "npm:^3.1.1" tempy: "npm:^1.0.1" + tiny-invariant: "npm:^1.3.1" ts-dedent: "npm:^2.0.0" typescript: "npm:^5.3.2" util-deprecate: "npm:^1.0.2" @@ -8001,17 +8002,7 @@ __metadata: languageName: node linkType: hard -"@types/jest@npm:*": - version: 29.5.10 - resolution: "@types/jest@npm:29.5.10" - dependencies: - expect: "npm:^29.0.0" - pretty-format: "npm:^29.0.0" - checksum: b46171d59d12a5f69bbe710f65eaf59a8073337c6b4a67dff8158575caec53f1c61f8a7d645b34d6ac3c4ea398acd30f0c5d1c4a131c0c918798019264a3397d - languageName: node - linkType: hard - -"@types/jest@npm:28.1.3": +"@types/jest@npm:*, @types/jest@npm:28.1.3": version: 28.1.3 resolution: "@types/jest@npm:28.1.3" dependencies: @@ -14930,7 +14921,7 @@ __metadata: languageName: node linkType: hard -"expect@npm:^29.0.0, expect@npm:^29.7.0": +"expect@npm:^29.7.0": version: 29.7.0 resolution: "expect@npm:29.7.0" dependencies: diff --git a/scripts/yarn.lock b/scripts/yarn.lock index c36ce2a9b38e..d08afd195ba0 100644 --- a/scripts/yarn.lock +++ b/scripts/yarn.lock @@ -2610,9 +2610,9 @@ __metadata: linkType: hard "@octokit/openapi-types@npm:^19.0.2": - version: 19.0.2 - resolution: "@octokit/openapi-types@npm:19.0.2" - checksum: e003a3b7471edfa970911252c19ce9331d935699cc1e91a1e151316b585c3b2f5251bc5ba137b7e14aed8a9b3890fdf67edc5cc5af4805bf4b44f5869544e678 + version: 19.1.0 + resolution: "@octokit/openapi-types@npm:19.1.0" + checksum: ae8081f52b797b91a12d4f6cddc475699c9d34b06645b337adc77d30b583d8fe8506597a45c42f8f1a96bfb2a9d092cee257d8a65d718bfeed23a0d153448eea languageName: node linkType: hard