-
-
Notifications
You must be signed in to change notification settings - Fork 9.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
242 additions
and
94 deletions.
There are no files selected for viewing
100 changes: 100 additions & 0 deletions
100
code/lib/cli/src/automigrate/helpers/getDuplicatedDepsWarnings.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import chalk from 'chalk'; | ||
import { frameworkPackages, rendererPackages } from '@storybook/core-common'; | ||
import { hasMultipleVersions } from './hasMultipleVersions'; | ||
import type { InstallationMetadata } from '../../js-package-manager/types'; | ||
|
||
export const messageDivider = '\n\n'; | ||
|
||
// These packages are aliased by Storybook, so it doesn't matter if they're duplicated | ||
export const allowList = [ | ||
'@storybook/csf', | ||
// see this file for more info: code/lib/preview/src/globals/types.ts | ||
'@storybook/addons', | ||
'@storybook/channel-postmessage', | ||
'@storybook/channel-websocket', | ||
'@storybook/channels', | ||
'@storybook/client-api', | ||
'@storybook/client-logger', | ||
'@storybook/core-client', | ||
'@storybook/core-events', | ||
'@storybook/preview-web', | ||
'@storybook/preview-api', | ||
'@storybook/store', | ||
|
||
// see this file for more info: code/ui/manager/src/globals/types.ts | ||
'@storybook/components', | ||
'@storybook/router', | ||
'@storybook/theming', | ||
'@storybook/api', | ||
'@storybook/manager-api', | ||
]; | ||
|
||
// These packages definitely will cause issues if they're duplicated | ||
export const disallowList = [ | ||
Object.keys(rendererPackages), | ||
Object.keys(frameworkPackages), | ||
'@storybook/instrumenter', | ||
]; | ||
|
||
export function getDuplicatedDepsWarnings( | ||
installationMetadata?: InstallationMetadata | ||
): string[] | undefined { | ||
if ( | ||
!installationMetadata?.duplicatedDependencies || | ||
Object.keys(installationMetadata.duplicatedDependencies).length === 0 | ||
) { | ||
return undefined; | ||
} | ||
|
||
const messages: string[] = []; | ||
|
||
const { critical, trivial } = Object.entries( | ||
installationMetadata?.duplicatedDependencies | ||
).reduce<{ | ||
critical: string[]; | ||
trivial: string[]; | ||
}>( | ||
(acc, [dep, versions]) => { | ||
if (allowList.includes(dep)) { | ||
return acc; | ||
} | ||
|
||
const hasMultipleMajorVersions = hasMultipleVersions(versions); | ||
|
||
if (disallowList.includes(dep) && hasMultipleMajorVersions) { | ||
acc.critical.push(`${chalk.redBright(dep)}:\n${versions.join(', ')}`); | ||
} else { | ||
acc.trivial.push(`${chalk.hex('#ff9800')(dep)}:\n${versions.join(', ')}`); | ||
} | ||
|
||
return acc; | ||
}, | ||
{ critical: [], trivial: [] } | ||
); | ||
|
||
if (critical.length > 0) { | ||
messages.push( | ||
`${chalk.bold( | ||
'Critical:' | ||
)} The following dependencies are duplicated and WILL cause unexpected behavior:` | ||
); | ||
messages.push(critical.join(messageDivider)); | ||
} | ||
|
||
if (trivial.length > 0) { | ||
messages.push( | ||
`${chalk.bold( | ||
'Attention:' | ||
)} The following dependencies are duplicated which might cause unexpected behavior:` | ||
); | ||
messages.push(trivial.join(messageDivider)); | ||
} | ||
|
||
messages.push( | ||
`You can find more information for a given dependency by running ${chalk.cyan( | ||
`${installationMetadata.infoCommand} <package-name>` | ||
)}` | ||
); | ||
|
||
return messages; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
import chalk from 'chalk'; | ||
import boxen from 'boxen'; | ||
import { createWriteStream, move, remove } from 'fs-extra'; | ||
import tempy from 'tempy'; | ||
import dedent from 'ts-dedent'; | ||
|
||
import { join } from 'path'; | ||
import { JsPackageManagerFactory } from '../js-package-manager'; | ||
import type { PackageManagerName } from '../js-package-manager'; | ||
|
||
import { getStorybookData } from '../automigrate/helpers/mainConfigFile'; | ||
import { getDuplicatedDepsWarnings } from '../automigrate/helpers/getDuplicatedDepsWarnings'; | ||
import { cleanLog } from '../automigrate/helpers/cleanLog'; | ||
import { getIncompatibleAddons } from '../automigrate/helpers/getIncompatibleAddons'; | ||
import { incompatibleAddons } from '../automigrate/fixes/incompatible-addons'; | ||
|
||
const logger = console; | ||
const LOG_FILE_NAME = 'doctor-storybook.log'; | ||
const LOG_FILE_PATH = join(process.cwd(), LOG_FILE_NAME); | ||
let TEMP_LOG_FILE_PATH = ''; | ||
|
||
const originalStdOutWrite = process.stdout.write.bind(process.stdout); | ||
const originalStdErrWrite = process.stderr.write.bind(process.stdout); | ||
|
||
const augmentLogsToFile = () => { | ||
TEMP_LOG_FILE_PATH = tempy.file({ name: LOG_FILE_NAME }); | ||
const logStream = createWriteStream(TEMP_LOG_FILE_PATH); | ||
|
||
process.stdout.write = (d: string) => { | ||
originalStdOutWrite(d); | ||
return logStream.write(cleanLog(d)); | ||
}; | ||
process.stderr.write = (d: string) => { | ||
return logStream.write(cleanLog(d)); | ||
}; | ||
}; | ||
|
||
const cleanup = () => { | ||
process.stdout.write = originalStdOutWrite; | ||
process.stderr.write = originalStdErrWrite; | ||
}; | ||
|
||
type DoctorOptions = { | ||
configDir?: string; | ||
packageManager?: PackageManagerName; | ||
}; | ||
|
||
export const doctor = async ({ | ||
configDir: userSpecifiedConfigDir, | ||
packageManager: pkgMgr, | ||
}: DoctorOptions = {}) => { | ||
augmentLogsToFile(); | ||
const diagnosticMessages: string[] = []; | ||
|
||
logger.info('🩺 checking the health of your Storybook..'); | ||
|
||
const packageManager = JsPackageManagerFactory.getPackageManager({ force: pkgMgr }); | ||
let storybookVersion; | ||
let mainConfig; | ||
|
||
try { | ||
const storybookData = await getStorybookData({ | ||
configDir: userSpecifiedConfigDir, | ||
packageManager, | ||
}); | ||
storybookVersion = storybookData.storybookVersion; | ||
mainConfig = storybookData.mainConfig; | ||
} catch (err) { | ||
if (err.message.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( | ||
userSpecifiedConfigDir || '.storybook' | ||
)} so the doctor command cannot proceed. You might be running this command in a monorepo or a non-standard project structure. If that is the case, please rerun this command by specifying the path to your Storybook config directory via the --config-dir option.` | ||
); | ||
} | ||
logger.info(dedent`[Storybook doctor] ❌ ${err.message}`); | ||
logger.info('Please fix the error and try again.'); | ||
} | ||
|
||
if (!storybookVersion) { | ||
logger.info(dedent` | ||
[Storybook automigrate] ❌ Unable to determine storybook version so the automigrations will be skipped. | ||
🤔 Are you running automigrate from your project directory? Please specify your Storybook config directory with the --config-dir flag. | ||
`); | ||
process.exit(1); | ||
} | ||
|
||
const installationMetadata = await packageManager.findInstallations([ | ||
'@storybook/*', | ||
'storybook', | ||
]); | ||
|
||
diagnosticMessages.push(getDuplicatedDepsWarnings(installationMetadata)?.join('\n')); | ||
|
||
const incompatibleAddonList = await getIncompatibleAddons(mainConfig); | ||
if (incompatibleAddonList.length > 0) { | ||
diagnosticMessages.push(incompatibleAddons.prompt({ incompatibleAddonList })); | ||
} | ||
|
||
logger.info(); | ||
|
||
const finalMessages = diagnosticMessages.filter(Boolean); | ||
|
||
if (finalMessages.length > 0) { | ||
finalMessages.push(`You can find the full logs in ${chalk.cyan(LOG_FILE_PATH)}`); | ||
|
||
logger.info( | ||
boxen(finalMessages.join('\n\n-------\n\n'), { | ||
borderStyle: 'round', | ||
padding: 1, | ||
title: 'Diagnostics', | ||
borderColor: 'red', | ||
}) | ||
); | ||
await move(TEMP_LOG_FILE_PATH, join(process.cwd(), LOG_FILE_NAME), { overwrite: true }); | ||
} else { | ||
logger.info('🥳 Your Storybook project looks good!'); | ||
await remove(TEMP_LOG_FILE_PATH); | ||
} | ||
logger.info(); | ||
|
||
cleanup(); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters