diff --git a/code/frameworks/angular/src/builders/utils/run-compodoc.spec.ts b/code/frameworks/angular/src/builders/utils/run-compodoc.spec.ts index 5e25c046bb07..449d211fe05b 100644 --- a/code/frameworks/angular/src/builders/utils/run-compodoc.spec.ts +++ b/code/frameworks/angular/src/builders/utils/run-compodoc.spec.ts @@ -8,7 +8,7 @@ const mockRunScript = jest.fn(); jest.mock('@storybook/cli', () => ({ JsPackageManagerFactory: { getPackageManager: () => ({ - runPackageCommand: mockRunScript, + runPackageCommandSync: mockRunScript, }), }, })); diff --git a/code/frameworks/angular/src/builders/utils/run-compodoc.ts b/code/frameworks/angular/src/builders/utils/run-compodoc.ts index 6f167070789c..ebec682ba2e1 100644 --- a/code/frameworks/angular/src/builders/utils/run-compodoc.ts +++ b/code/frameworks/angular/src/builders/utils/run-compodoc.ts @@ -28,7 +28,7 @@ export const runCompodoc = ( const packageManager = JsPackageManagerFactory.getPackageManager(); try { - const stdout = packageManager.runPackageCommand( + const stdout = packageManager.runPackageCommandSync( 'compodoc', finalCompodocArgs, context.workspaceRoot diff --git a/code/lib/cli/src/add.ts b/code/lib/cli/src/add.ts index df350c7e1151..efced649c957 100644 --- a/code/lib/cli/src/add.ts +++ b/code/lib/cli/src/add.ts @@ -80,7 +80,7 @@ export async function add( pkgMgr = 'npm'; } const packageManager = JsPackageManagerFactory.getPackageManager({ force: pkgMgr }); - const packageJson = packageManager.retrievePackageJson(); + const packageJson = await packageManager.retrievePackageJson(); const [addonName, versionSpecifier] = getVersionSpecifier(addon); const { mainConfig, version: storybookVersion } = getStorybookInfo(packageJson); @@ -90,7 +90,7 @@ export async function add( } const main = await readConfig(mainConfig); logger.log(`Verifying ${addonName}`); - const latestVersion = packageManager.latestVersion(addonName); + const latestVersion = await packageManager.latestVersion(addonName); if (!latestVersion) { logger.error(`Unknown addon ${addonName}`); } @@ -100,7 +100,7 @@ export async function add( const version = versionSpecifier || (isStorybookAddon ? storybookVersion : latestVersion); const addonWithVersion = `${addonName}@${version}`; logger.log(`Installing ${addonWithVersion}`); - packageManager.addDependencies({ installAsDevDependencies: true }, [addonWithVersion]); + await packageManager.addDependencies({ installAsDevDependencies: true }, [addonWithVersion]); // add to main.js logger.log(`Adding '${addon}' to main.js addons field.`); diff --git a/code/lib/cli/src/automigrate/fixes/add-react.test.ts b/code/lib/cli/src/automigrate/fixes/add-react.test.ts index f497f4013016..42bb20b60e2e 100644 --- a/code/lib/cli/src/automigrate/fixes/add-react.test.ts +++ b/code/lib/cli/src/automigrate/fixes/add-react.test.ts @@ -3,7 +3,7 @@ import { addReact } from './add-react'; const checkAddReact = async (packageJson: PackageJson) => { const packageManager = { - retrievePackageJson: () => ({ dependencies: {}, devDependencies: {}, ...packageJson }), + retrievePackageJson: async () => ({ dependencies: {}, devDependencies: {}, ...packageJson }), } as JsPackageManager; return addReact.check({ packageManager }); }; diff --git a/code/lib/cli/src/automigrate/fixes/add-react.ts b/code/lib/cli/src/automigrate/fixes/add-react.ts index 420be48912f1..25ad88df2571 100644 --- a/code/lib/cli/src/automigrate/fixes/add-react.ts +++ b/code/lib/cli/src/automigrate/fixes/add-react.ts @@ -14,7 +14,7 @@ export const addReact: Fix = { id: 'addReact', async check({ packageManager }) { - const packageJson = packageManager.retrievePackageJson(); + const packageJson = await packageManager.retrievePackageJson(); const installedDependencies = new Set( Object.keys({ ...packageJson.dependencies, ...packageJson.devDependencies }) ); @@ -63,7 +63,10 @@ export const addReact: Fix = { async run({ packageManager, result: { additionalDependencies }, dryRun }) { if (!dryRun) { - packageManager.addDependencies({ installAsDevDependencies: true }, additionalDependencies); + await packageManager.addDependencies( + { installAsDevDependencies: true }, + additionalDependencies + ); } }, }; diff --git a/code/lib/cli/src/automigrate/fixes/angular-builders-multiproject.ts b/code/lib/cli/src/automigrate/fixes/angular-builders-multiproject.ts index 14d767e1ca59..2cd0a42fa987 100644 --- a/code/lib/cli/src/automigrate/fixes/angular-builders-multiproject.ts +++ b/code/lib/cli/src/automigrate/fixes/angular-builders-multiproject.ts @@ -12,14 +12,14 @@ export const angularBuildersMultiproject: Fix = { id: 'angular-builders', async check({ packageManager, configDir }) { - const packageJSON = packageManager.retrievePackageJson(); + const packageJSON = await packageManager.retrievePackageJson(); // Skip in case of NX if (isNxProject(packageJSON)) { return null; } - const allDependencies = packageManager.getAllDependencies(); + const allDependencies = await packageManager.getAllDependencies(); const angularVersion = allDependencies['@angular/core']; const angularCoerced = semver.coerce(angularVersion)?.version; @@ -98,7 +98,7 @@ export const angularBuilders: Fix = { angularJSON.write(); - packageManager.addScripts({ + await packageManager.addScripts({ storybook: `ng run ${angularProjectName}:storybook`, 'build-storybook': `ng run ${angularProjectName}:build-storybook`, }); diff --git a/code/lib/cli/src/automigrate/fixes/angular12.ts b/code/lib/cli/src/automigrate/fixes/angular12.ts index a778556e9207..c8b98e6b0f00 100644 --- a/code/lib/cli/src/automigrate/fixes/angular12.ts +++ b/code/lib/cli/src/automigrate/fixes/angular12.ts @@ -21,7 +21,7 @@ export const angular12: Fix = { id: 'angular12', async check({ packageManager, configDir }) { - const allDependencies = packageManager.getAllDependencies(); + const allDependencies = await packageManager.getAllDependencies(); const angularVersion = allDependencies['@angular/core']; const angularCoerced = semver.coerce(angularVersion)?.version; diff --git a/code/lib/cli/src/automigrate/fixes/builder-vite.ts b/code/lib/cli/src/automigrate/fixes/builder-vite.ts index c107cc6d7d17..9fef70b8b9d6 100644 --- a/code/lib/cli/src/automigrate/fixes/builder-vite.ts +++ b/code/lib/cli/src/automigrate/fixes/builder-vite.ts @@ -27,7 +27,7 @@ export const builderVite: Fix = { id: 'builder-vite', async check({ configDir, packageManager }) { - const packageJson = packageManager.retrievePackageJson(); + const packageJson = await packageManager.retrievePackageJson(); const { mainConfig } = await getStorybookData({ configDir, packageManager }); const builder = mainConfig.core?.builder; const builderName = typeof builder === 'string' ? builder : builder?.name; @@ -64,12 +64,12 @@ export const builderVite: Fix = { if (!dryRun) { delete dependencies['storybook-builder-vite']; delete devDependencies['storybook-builder-vite']; - packageManager.writePackageJson(packageJson); + await packageManager.writePackageJson(packageJson); } logger.info(`✅ Adding '@storybook/builder-vite' as dev dependency`); if (!dryRun) { - packageManager.addDependencies({ installAsDevDependencies: true }, [ + await packageManager.addDependencies({ installAsDevDependencies: true }, [ '@storybook/builder-vite', ]); } diff --git a/code/lib/cli/src/automigrate/fixes/cra5.ts b/code/lib/cli/src/automigrate/fixes/cra5.ts index 04ca458d6871..1280a5de3155 100644 --- a/code/lib/cli/src/automigrate/fixes/cra5.ts +++ b/code/lib/cli/src/automigrate/fixes/cra5.ts @@ -21,7 +21,7 @@ export const cra5: Fix = { id: 'cra5', async check({ packageManager, configDir }) { - const allDependencies = packageManager.getAllDependencies(); + const allDependencies = await packageManager.getAllDependencies(); const craVersion = allDependencies['react-scripts']; const craCoerced = semver.coerce(craVersion)?.version; diff --git a/code/lib/cli/src/automigrate/fixes/eslint-plugin.ts b/code/lib/cli/src/automigrate/fixes/eslint-plugin.ts index c5b155ff5366..9b461e97d1a7 100644 --- a/code/lib/cli/src/automigrate/fixes/eslint-plugin.ts +++ b/code/lib/cli/src/automigrate/fixes/eslint-plugin.ts @@ -25,7 +25,7 @@ export const eslintPlugin: Fix = { id: 'eslintPlugin', async check({ packageManager }) { - const allDependencies = packageManager.getAllDependencies(); + const allDependencies = await packageManager.getAllDependencies(); const eslintPluginStorybook = allDependencies['eslint-plugin-storybook']; const eslintDependency = allDependencies.eslint; @@ -64,8 +64,9 @@ export const eslintPlugin: Fix = { const deps = [`eslint-plugin-storybook`]; logger.info(`✅ Adding dependencies: ${deps}`); - if (!dryRun) - packageManager.addDependencies({ installAsDevDependencies: true, skipInstall }, deps); + if (!dryRun) { + await packageManager.addDependencies({ installAsDevDependencies: true, skipInstall }, deps); + } if (!dryRun && unsupportedExtension) { logger.info(dedent` diff --git a/code/lib/cli/src/automigrate/fixes/mdx-gfm.ts b/code/lib/cli/src/automigrate/fixes/mdx-gfm.ts index e4f0f6b8b7cd..7989b5c1517a 100644 --- a/code/lib/cli/src/automigrate/fixes/mdx-gfm.ts +++ b/code/lib/cli/src/automigrate/fixes/mdx-gfm.ts @@ -81,8 +81,10 @@ export const mdxgfm: Fix = { async run({ packageManager, dryRun, mainConfigPath, skipInstall }) { if (!dryRun) { - const packageJson = packageManager.retrievePackageJson(); - const versionToInstall = getStorybookVersionSpecifier(packageManager.retrievePackageJson()); + const packageJson = await packageManager.retrievePackageJson(); + const versionToInstall = getStorybookVersionSpecifier( + await packageManager.retrievePackageJson() + ); await packageManager.addDependencies( { installAsDevDependencies: true, skipInstall, packageJson }, [`@storybook/addon-mdx-gfm@${versionToInstall}`] diff --git a/code/lib/cli/src/automigrate/fixes/missing-babelrc.ts b/code/lib/cli/src/automigrate/fixes/missing-babelrc.ts index 07f94b1f4d50..2e13685fd8b4 100644 --- a/code/lib/cli/src/automigrate/fixes/missing-babelrc.ts +++ b/code/lib/cli/src/automigrate/fixes/missing-babelrc.ts @@ -24,7 +24,7 @@ export const missingBabelRc: Fix = { id: 'missing-babelrc', async check({ configDir, packageManager }) { - const packageJson = packageManager.retrievePackageJson(); + const packageJson = await packageManager.retrievePackageJson(); const { mainConfig, storybookVersion } = await getStorybookData({ configDir, packageManager }); if (!semver.gte(storybookVersion, '7.0.0')) { diff --git a/code/lib/cli/src/automigrate/fixes/new-frameworks.ts b/code/lib/cli/src/automigrate/fixes/new-frameworks.ts index 11c23cc5b348..d2be64bf877a 100644 --- a/code/lib/cli/src/automigrate/fixes/new-frameworks.ts +++ b/code/lib/cli/src/automigrate/fixes/new-frameworks.ts @@ -67,7 +67,7 @@ export const newFrameworks: Fix = { configDir: userDefinedConfigDir, packageManager, }) { - const packageJson = packageManager.retrievePackageJson(); + const packageJson = await packageManager.retrievePackageJson(); const { storybookVersion, mainConfig, mainConfigPath, configDir } = await getStorybookData({ packageManager, configDir: userDefinedConfigDir, @@ -108,7 +108,7 @@ export const newFrameworks: Fix = { return null; } - const allDependencies = packageManager.getAllDependencies(); + const allDependencies = await packageManager.getAllDependencies(); const builderInfo = await detectBuilderInfo({ mainConfig, @@ -448,7 +448,7 @@ export const newFrameworks: Fix = { if (dependenciesToRemove.length > 0) { logger.info(`✅ Removing dependencies: ${dependenciesToRemove.join(', ')}`); if (!dryRun) { - packageManager.removeDependencies( + await packageManager.removeDependencies( { skipInstall: skipInstall || dependenciesToAdd.length > 0, packageJson }, dependenciesToRemove ); @@ -460,7 +460,7 @@ export const newFrameworks: Fix = { if (!dryRun) { const versionToInstall = getStorybookVersionSpecifier(packageJson); const depsToAdd = dependenciesToAdd.map((dep) => `${dep}@${versionToInstall}`); - packageManager.addDependencies( + await packageManager.addDependencies( { installAsDevDependencies: true, skipInstall, packageJson }, depsToAdd ); diff --git a/code/lib/cli/src/automigrate/fixes/remove-global-client-apis.test.ts b/code/lib/cli/src/automigrate/fixes/remove-global-client-apis.test.ts index 0ae9971db053..a8fa9d050b9c 100644 --- a/code/lib/cli/src/automigrate/fixes/remove-global-client-apis.test.ts +++ b/code/lib/cli/src/automigrate/fixes/remove-global-client-apis.test.ts @@ -16,7 +16,7 @@ const check = async ({ packageJson = {}, contents }: any) => { }); } const packageManager = { - retrievePackageJson: () => ({ dependencies: {}, devDependencies: {}, ...packageJson }), + retrievePackageJson: async () => ({ dependencies: {}, devDependencies: {}, ...packageJson }), } as JsPackageManager; return migration.check({ packageManager }); }; diff --git a/code/lib/cli/src/automigrate/fixes/remove-global-client-apis.ts b/code/lib/cli/src/automigrate/fixes/remove-global-client-apis.ts index 9e725b810d73..9888b22a6be9 100644 --- a/code/lib/cli/src/automigrate/fixes/remove-global-client-apis.ts +++ b/code/lib/cli/src/automigrate/fixes/remove-global-client-apis.ts @@ -23,7 +23,7 @@ export const removedGlobalClientAPIs: Fix = { promptOnly: true, async check({ packageManager, configDir }) { - const packageJson = packageManager.retrievePackageJson(); + const packageJson = await packageManager.retrievePackageJson(); const { previewConfig } = getStorybookInfo(packageJson, configDir); diff --git a/code/lib/cli/src/automigrate/fixes/sb-binary.ts b/code/lib/cli/src/automigrate/fixes/sb-binary.ts index b97d1faa5536..22d0283e3de3 100644 --- a/code/lib/cli/src/automigrate/fixes/sb-binary.ts +++ b/code/lib/cli/src/automigrate/fixes/sb-binary.ts @@ -26,8 +26,8 @@ export const sbBinary: Fix = { id: 'storybook-binary', async check({ packageManager, configDir }) { - const packageJson = packageManager.retrievePackageJson(); - const allDependencies = packageManager.getAllDependencies(); + const packageJson = await packageManager.retrievePackageJson(); + const allDependencies = await packageManager.getAllDependencies(); const { storybookVersion } = await getStorybookData({ packageManager, configDir }); // Nx provides their own binary, so we don't need to do anything @@ -82,7 +82,7 @@ export const sbBinary: Fix = { if (hasSbBinary) { logger.info(`✅ Removing 'sb' dependency`); if (!dryRun) { - packageManager.removeDependencies( + await packageManager.removeDependencies( { skipInstall: skipInstall || !hasStorybookBinary, packageJson }, ['sb'] ); @@ -95,7 +95,7 @@ export const sbBinary: Fix = { logger.log(); if (!dryRun) { const versionToInstall = getStorybookVersionSpecifier(packageJson); - packageManager.addDependencies( + await packageManager.addDependencies( { installAsDevDependencies: true, packageJson, skipInstall }, [`storybook@${versionToInstall}`] ); diff --git a/code/lib/cli/src/automigrate/fixes/sb-scripts.ts b/code/lib/cli/src/automigrate/fixes/sb-scripts.ts index 9c84d7b75062..b624d494af5a 100644 --- a/code/lib/cli/src/automigrate/fixes/sb-scripts.ts +++ b/code/lib/cli/src/automigrate/fixes/sb-scripts.ts @@ -72,7 +72,7 @@ export const sbScripts: Fix = { id: 'sb-scripts', async check({ packageManager, configDir }) { - const packageJson = packageManager.retrievePackageJson(); + const packageJson = await packageManager.retrievePackageJson(); const { scripts = {} } = packageJson; const { storybookVersion } = await getStorybookData({ packageManager, configDir }); @@ -133,7 +133,7 @@ export const sbScripts: Fix = { logger.log(); - packageManager.addScripts(newScripts); + await packageManager.addScripts(newScripts); } }, }; diff --git a/code/lib/cli/src/automigrate/fixes/vue3.ts b/code/lib/cli/src/automigrate/fixes/vue3.ts index f1c5041e1885..0d3aaca104af 100644 --- a/code/lib/cli/src/automigrate/fixes/vue3.ts +++ b/code/lib/cli/src/automigrate/fixes/vue3.ts @@ -20,7 +20,7 @@ export const vue3: Fix = { id: 'vue3', async check({ configDir, packageManager }) { - const allDependencies = packageManager.getAllDependencies(); + const allDependencies = await packageManager.getAllDependencies(); const vueVersion = allDependencies.vue; const vueCoerced = semver.coerce(vueVersion)?.version; diff --git a/code/lib/cli/src/automigrate/fixes/webpack5.ts b/code/lib/cli/src/automigrate/fixes/webpack5.ts index edac5e468696..c60dc9f0eed1 100644 --- a/code/lib/cli/src/automigrate/fixes/webpack5.ts +++ b/code/lib/cli/src/automigrate/fixes/webpack5.ts @@ -26,7 +26,7 @@ export const webpack5: Fix = { id: 'webpack5', async check({ configDir, packageManager }) { - const allDependencies = packageManager.retrievePackageJson().dependencies; + const allDependencies = (await packageManager.retrievePackageJson()).dependencies; const webpackVersion = allDependencies.webpack; const webpackCoerced = semver.coerce(webpackVersion)?.version; @@ -72,7 +72,9 @@ export const webpack5: Fix = { deps.push('webpack@5'); } logger.info(`✅ Adding dependencies: ${deps}`); - if (!dryRun) packageManager.addDependencies({ installAsDevDependencies: true }, deps); + if (!dryRun) { + await packageManager.addDependencies({ installAsDevDependencies: true }, deps); + } logger.info('✅ Setting `core.builder` to `@storybook/builder-webpack5` in main.js'); if (!dryRun) { diff --git a/code/lib/cli/src/automigrate/helpers/mainConfigFile.ts b/code/lib/cli/src/automigrate/helpers/mainConfigFile.ts index c16b97afebd6..f843f57097f9 100644 --- a/code/lib/cli/src/automigrate/helpers/mainConfigFile.ts +++ b/code/lib/cli/src/automigrate/helpers/mainConfigFile.ts @@ -16,7 +16,7 @@ export const getStorybookData = async ({ packageManager: JsPackageManager; configDir: string; }) => { - const packageJson = packageManager.retrievePackageJson(); + const packageJson = await packageManager.retrievePackageJson(); const { mainConfig: mainConfigPath, version: storybookVersionSpecifier, diff --git a/code/lib/cli/src/automigrate/helpers/testing-helpers.ts b/code/lib/cli/src/automigrate/helpers/testing-helpers.ts index 2c9cd34d6251..3651fe472caf 100644 --- a/code/lib/cli/src/automigrate/helpers/testing-helpers.ts +++ b/code/lib/cli/src/automigrate/helpers/testing-helpers.ts @@ -15,8 +15,8 @@ jest.mock('@storybook/core-common', () => ({ export const makePackageManager = (packageJson: PackageJson) => { const { dependencies = {}, devDependencies = {}, peerDependencies = {} } = packageJson; return { - retrievePackageJson: () => ({ dependencies: {}, devDependencies: {}, ...packageJson }), - getAllDependencies: () => ({ + retrievePackageJson: async () => ({ dependencies: {}, devDependencies: {}, ...packageJson }), + getAllDependencies: async () => ({ ...dependencies, ...devDependencies, ...peerDependencies, diff --git a/code/lib/cli/src/automigrate/index.ts b/code/lib/cli/src/automigrate/index.ts index e412f483fcc8..14c1865262a8 100644 --- a/code/lib/cli/src/automigrate/index.ts +++ b/code/lib/cli/src/automigrate/index.ts @@ -148,7 +148,7 @@ export async function runFixes({ configDir: inferredConfigDir, mainConfig: mainConfigPath, version: storybookVersion, - } = getStorybookInfo(packageManager.retrievePackageJson(), userSpecifiedConfigDir); + } = getStorybookInfo(await packageManager.retrievePackageJson(), userSpecifiedConfigDir); const sbVersionCoerced = storybookVersion && semver.coerce(storybookVersion)?.version; if (!sbVersionCoerced) { diff --git a/code/lib/cli/src/babel-config.ts b/code/lib/cli/src/babel-config.ts index 70bc127ae9ee..e97b51f0328d 100644 --- a/code/lib/cli/src/babel-config.ts +++ b/code/lib/cli/src/babel-config.ts @@ -90,7 +90,7 @@ export const generateStorybookBabelConfig = async ({ target }: { target: string const packageManager = JsPackageManagerFactory.getPackageManager(); - packageManager.addDependencies({ installAsDevDependencies: true }, added); + await packageManager.addDependencies({ installAsDevDependencies: true }, added); } else { logger.info( `⚠️ Please remember to install the required dependencies yourself: (${added.join(', ')})` diff --git a/code/lib/cli/src/detect-webpack.ts b/code/lib/cli/src/detect-webpack.ts index 992eb0c09968..d0a980567d29 100644 --- a/code/lib/cli/src/detect-webpack.ts +++ b/code/lib/cli/src/detect-webpack.ts @@ -1,18 +1,18 @@ import type { JsPackageManager } from './js-package-manager'; -export const detectWebpack = (packageManager: JsPackageManager): number | false => { +export const detectWebpack = async (packageManager: JsPackageManager): Promise => { try { let out = ''; if (packageManager.type === 'npm') { try { // npm <= v7 - out = packageManager.executeCommand('npm', ['ls', 'webpack']); + out = await packageManager.executeCommand({ command: 'npm', args: ['ls', 'webpack'] }); } catch (e2) { // npm >= v8 - out = packageManager.executeCommand('npm', ['why', 'webpack']); + out = await packageManager.executeCommand({ command: 'npm', args: ['why', 'webpack'] }); } } else { - out = packageManager.executeCommand('yarn', ['why', 'webpack']); + out = await packageManager.executeCommand({ command: 'yarn', args: ['why', 'webpack'] }); } // if the user has BOTH webpack 4 and 5 installed already, we'll pick the safest options (4) diff --git a/code/lib/cli/src/dirs.ts b/code/lib/cli/src/dirs.ts index e5baef79237f..6f3fa0e06864 100644 --- a/code/lib/cli/src/dirs.ts +++ b/code/lib/cli/src/dirs.ts @@ -20,7 +20,9 @@ const resolveUsingBranchInstall = async (packageManager: JsPackageManager, reque // FIXME: this might not be the right version for community packages const version = versions[name] || (await packageManager.latestVersion(request)); - const url = getNpmTarballUrl(request, version, { registry: packageManager.getRegistryURL() }); + const url = getNpmTarballUrl(request, version, { + registry: await packageManager.getRegistryURL(), + }); // this unzips the tarball into the temp directory await downloadTarball({ url, dir: tempDirectory }); diff --git a/code/lib/cli/src/generators/ANGULAR/index.ts b/code/lib/cli/src/generators/ANGULAR/index.ts index 2740be11589a..b820339d95b9 100644 --- a/code/lib/cli/src/generators/ANGULAR/index.ts +++ b/code/lib/cli/src/generators/ANGULAR/index.ts @@ -14,11 +14,11 @@ const generator: Generator<{ projectName: string }> = async ( commandOptions ) => { const angularVersionFromDependencies = semver.coerce( - packageManager.retrievePackageJson().dependencies['@angular/core'] + (await packageManager.retrievePackageJson()).dependencies['@angular/core'] )?.version; const angularVersionFromDevDependencies = semver.coerce( - packageManager.retrievePackageJson().devDependencies['@angular/core'] + (await packageManager.retrievePackageJson()).devDependencies['@angular/core'] )?.version; const angularVersion = angularVersionFromDependencies || angularVersionFromDevDependencies; diff --git a/code/lib/cli/src/generators/RAX/index.ts b/code/lib/cli/src/generators/RAX/index.ts index 44243b677d06..e3a0acefc2f8 100644 --- a/code/lib/cli/src/generators/RAX/index.ts +++ b/code/lib/cli/src/generators/RAX/index.ts @@ -3,7 +3,7 @@ import type { Generator } from '../types'; const generator: Generator = async (packageManager, npmOptions, options) => { const [latestRaxVersion] = await packageManager.getVersions('rax'); - const packageJson = packageManager.retrievePackageJson(); + const packageJson = await packageManager.retrievePackageJson(); const raxVersion = packageJson.dependencies.rax || latestRaxVersion; @@ -16,7 +16,7 @@ const generator: Generator = async (packageManager, npmOptions, options) => { packageJson.dependencies['rax-text'] = packageJson.dependencies['rax-text'] || raxVersion; packageJson.dependencies['rax-view'] = packageJson.dependencies['rax-view'] || raxVersion; - packageManager.writePackageJson(packageJson); + await packageManager.writePackageJson(packageJson); await baseGenerator(packageManager, npmOptions, options, 'rax', { extraPackages: ['rax'], diff --git a/code/lib/cli/src/generators/REACT_NATIVE/index.ts b/code/lib/cli/src/generators/REACT_NATIVE/index.ts index f3cc42668ddf..dc3e14ed0f7e 100644 --- a/code/lib/cli/src/generators/REACT_NATIVE/index.ts +++ b/code/lib/cli/src/generators/REACT_NATIVE/index.ts @@ -7,7 +7,7 @@ const generator = async ( packageManager: JsPackageManager, npmOptions: NpmOptions ): Promise => { - const packageJson = packageManager.retrievePackageJson(); + const packageJson = await packageManager.retrievePackageJson(); const missingReactDom = !packageJson.dependencies['react-dom'] && !packageJson.devDependencies['react-dom']; @@ -41,7 +41,7 @@ const generator = async ( missingReactDom && reactVersion && `react-dom@${reactVersion}`, ].filter(Boolean); - packageManager.addDependencies({ ...npmOptions, packageJson }, packages); + await packageManager.addDependencies({ ...npmOptions, packageJson }, packages); packageManager.addScripts({ 'storybook-generate': 'sb-rn-get-stories', 'storybook-watch': 'sb-rn-watcher', diff --git a/code/lib/cli/src/generators/REACT_SCRIPTS/index.ts b/code/lib/cli/src/generators/REACT_SCRIPTS/index.ts index c8001d54f2a4..1871a13faedb 100644 --- a/code/lib/cli/src/generators/REACT_SCRIPTS/index.ts +++ b/code/lib/cli/src/generators/REACT_SCRIPTS/index.ts @@ -25,9 +25,8 @@ const generator: Generator = async (packageManager, npmOptions, options) => { } : {}; - const craVersion = semver.coerce( - packageManager.retrievePackageJson().dependencies['react-scripts'] - )?.version; + const packageJson = await packageManager.retrievePackageJson(); + const craVersion = semver.coerce(packageJson.dependencies['react-scripts'])?.version; const isCra5OrHigher = craVersion && semver.gte(craVersion, '5.0.0'); const updatedOptions = isCra5OrHigher ? { ...options, builder: CoreBuilder.Webpack5 } : options; diff --git a/code/lib/cli/src/generators/baseGenerator.ts b/code/lib/cli/src/generators/baseGenerator.ts index 65f732b97f20..9a44785b7146 100644 --- a/code/lib/cli/src/generators/baseGenerator.ts +++ b/code/lib/cli/src/generators/baseGenerator.ts @@ -197,7 +197,7 @@ export async function baseGenerator( const files = await fse.readdir(process.cwd()); - const packageJson = packageManager.retrievePackageJson(); + const packageJson = await packageManager.retrievePackageJson(); const installedDependencies = new Set( Object.keys({ ...packageJson.dependencies, ...packageJson.devDependencies }) ); @@ -274,17 +274,17 @@ export async function baseGenerator( const depsToInstall = [...versionedPackages, ...babelDependencies]; if (depsToInstall.length > 0) { - packageManager.addDependencies({ ...npmOptions, packageJson }, depsToInstall); + await packageManager.addDependencies({ ...npmOptions, packageJson }, depsToInstall); } if (addScripts) { - packageManager.addStorybookCommandInScripts({ + await packageManager.addStorybookCommandInScripts({ port: 6006, }); } if (addESLint) { - packageManager.addESLintConfig(); + await packageManager.addESLintConfig(); } if (addComponents) { diff --git a/code/lib/cli/src/helpers.test.ts b/code/lib/cli/src/helpers.test.ts index db242f85f52b..4f5c3d37afee 100644 --- a/code/lib/cli/src/helpers.test.ts +++ b/code/lib/cli/src/helpers.test.ts @@ -33,7 +33,7 @@ jest.mock('path', () => { }); const packageManagerMock = { - retrievePackageJson: () => ({ dependencies: {}, devDependencies: {} }), + retrievePackageJson: async () => ({ dependencies: {}, devDependencies: {} }), } as JsPackageManager; describe('Helpers', () => { diff --git a/code/lib/cli/src/initiate.ts b/code/lib/cli/src/initiate.ts index bd7b5712ac93..1d02074d40fa 100644 --- a/code/lib/cli/src/initiate.ts +++ b/code/lib/cli/src/initiate.ts @@ -41,7 +41,7 @@ import { HandledError } from './HandledError'; const logger = console; -const installStorybook = ( +const installStorybook = async ( projectType: Project, packageManager: JsPackageManager, options: CommandOptions @@ -53,7 +53,7 @@ const installStorybook = ( let packageJson; try { - packageJson = packageManager.readPackageJson(); + packageJson = await packageManager.readPackageJson(); } catch (err) { // } @@ -228,7 +228,7 @@ const installStorybook = ( }; try { - return runGenerator(); + return await runGenerator(); } catch (err) { logger.error(`\n ${chalk.red(err.stack)}`); throw new HandledError(err); @@ -291,7 +291,7 @@ async function doInitiate(options: CommandOptions, pkg: PackageJson): Promise; public abstract getRunStorybookCommand(): string; @@ -50,16 +51,17 @@ export abstract class JsPackageManager { // NOTE: for some reason yarn prefers the npm registry in // local development, so always use npm - setRegistryURL(url: string) { + async setRegistryURL(url: string) { if (url) { - this.executeCommand({ command: 'npm', args: ['config', 'set', 'registry', url] }); + await this.executeCommand({ command: 'npm', args: ['config', 'set', 'registry', url] }); } else { - this.executeCommand({ command: 'npm', args: ['config', 'delete', 'registry'] }); + await this.executeCommand({ command: 'npm', args: ['config', 'delete', 'registry'] }); } } - getRegistryURL() { - const url = this.executeCommand({ command: 'npm', args: ['config', 'get', 'registry'] }).trim(); + async getRegistryURL() { + const res = await this.executeCommand({ command: 'npm', args: ['config', 'get', 'registry'] }); + const url = res.trim(); return url === 'undefined' ? undefined : url; } @@ -70,7 +72,7 @@ export abstract class JsPackageManager { /** * Install dependencies listed in `package.json` */ - public installDependencies(): void { + public async installDependencies() { let done = commandLog('Preparing to install dependencies'); done(); logger.log(); @@ -79,7 +81,7 @@ export abstract class JsPackageManager { done = commandLog('Installing dependencies'); try { - this.runInstall(); + await this.runInstall(); } catch (e) { done('An error occurred while installing dependencies.'); throw new HandledError(e); @@ -91,17 +93,17 @@ export abstract class JsPackageManager { return this.cwd ? path.resolve(this.cwd, 'package.json') : path.resolve('package.json'); } - readPackageJson(): PackageJson { + async readPackageJson(): Promise { const packageJsonPath = this.packageJsonPath(); if (!fs.existsSync(packageJsonPath)) { throw new Error(`Could not read package.json file at ${packageJsonPath}`); } - const jsonContent = fs.readFileSync(packageJsonPath, 'utf8'); + const jsonContent = await readFile(packageJsonPath, 'utf8'); return JSON.parse(jsonContent); } - writePackageJson(packageJson: PackageJson) { + async writePackageJson(packageJson: PackageJson) { const packageJsonToWrite = { ...packageJson }; // make sure to not accidentally add empty fields if ( @@ -124,21 +126,21 @@ export abstract class JsPackageManager { } const content = `${JSON.stringify(packageJsonToWrite, null, 2)}\n`; - fs.writeFileSync(this.packageJsonPath(), content, 'utf8'); + await writeFile(this.packageJsonPath(), content, 'utf8'); } /** * Read the `package.json` file available in the directory the command was call from * If there is no `package.json` it will create one. */ - public retrievePackageJson(): PackageJsonWithDepsAndDevDeps { + public async retrievePackageJson(): Promise { let packageJson; try { - packageJson = this.readPackageJson(); + packageJson = await this.readPackageJson(); } catch (err) { if (err.message.includes('Could not read package.json')) { - this.initPackageJson(); - packageJson = this.readPackageJson(); + await this.initPackageJson(); + packageJson = await this.readPackageJson(); } else { throw new Error( dedent` @@ -159,8 +161,8 @@ export abstract class JsPackageManager { }; } - public getAllDependencies(): Record { - const { dependencies, devDependencies, peerDependencies } = this.retrievePackageJson(); + public async getAllDependencies(): Promise> { + const { dependencies, devDependencies, peerDependencies } = await this.retrievePackageJson(); return { ...dependencies, @@ -182,14 +184,14 @@ export abstract class JsPackageManager { * `@storybook/preview-api@${addonsVersion}`, * ]); */ - public addDependencies( + public async addDependencies( options: { skipInstall?: boolean; installAsDevDependencies?: boolean; packageJson?: PackageJson; }, dependencies: string[] - ): void { + ) { const { skipInstall } = options; if (skipInstall) { @@ -211,10 +213,10 @@ export abstract class JsPackageManager { ...dependenciesMap, }; } - this.writePackageJson(packageJson); + await this.writePackageJson(packageJson); } else { try { - this.runAddDeps(dependencies, options.installAsDevDependencies); + await this.runAddDeps(dependencies, options.installAsDevDependencies); } catch (e) { logger.error('An error occurred while installing dependencies.'); logger.log(e.message); @@ -310,11 +312,6 @@ export abstract class JsPackageManager { if (/(@storybook|^sb$|^storybook$)/.test(packageName)) { // @ts-expect-error (Converted from ts-ignore) current = storybookPackagesVersions[packageName]; - - // HEY THERE! IF THIS IS COMMITED IT WAS A MISTAKE. PLEASE UNDO THIS: - if (current) { - return `^${current}`; - } } let latest; @@ -355,22 +352,22 @@ export abstract class JsPackageManager { return versions.reverse().find((version) => satisfies(version, constraint)); } - public addStorybookCommandInScripts(options?: { port: number; preCommand?: string }) { + public async addStorybookCommandInScripts(options?: { port: number; preCommand?: string }) { const sbPort = options?.port ?? 6006; const storybookCmd = `storybook dev -p ${sbPort}`; const buildStorybookCmd = `storybook build`; const preCommand = options?.preCommand ? this.getRunCommand(options.preCommand) : undefined; - this.addScripts({ + await this.addScripts({ storybook: [preCommand, storybookCmd].filter(Boolean).join(' && '), 'build-storybook': [preCommand, buildStorybookCmd].filter(Boolean).join(' && '), }); } - public addESLintConfig() { - const packageJson = this.retrievePackageJson(); - this.writePackageJson({ + public async addESLintConfig() { + const packageJson = await this.retrievePackageJson(); + await this.writePackageJson({ ...packageJson, eslintConfig: { ...packageJson.eslintConfig, @@ -387,9 +384,9 @@ export abstract class JsPackageManager { }); } - public addScripts(scripts: Record) { - const packageJson = this.retrievePackageJson(); - this.writePackageJson({ + public async addScripts(scripts: Record) { + const packageJson = await this.retrievePackageJson(); + await this.writePackageJson({ ...packageJson, scripts: { ...packageJson.scripts, @@ -398,17 +395,20 @@ export abstract class JsPackageManager { }); } - public addPackageResolutions(versions: Record) { - const packageJson = this.retrievePackageJson(); + public async addPackageResolutions(versions: Record) { + const packageJson = await this.retrievePackageJson(); const resolutions = this.getResolutions(packageJson, versions); this.writePackageJson({ ...packageJson, ...resolutions }); } - protected abstract runInstall(): void; + protected abstract runInstall(): Promise; - protected abstract runAddDeps(dependencies: string[], installAsDevDependencies: boolean): void; + protected abstract runAddDeps( + dependencies: string[], + installAsDevDependencies: boolean + ): Promise; - protected abstract runRemoveDeps(dependencies: string[]): void; + protected abstract runRemoveDeps(dependencies: string[]): Promise; protected abstract getResolutions( packageJson: PackageJson, @@ -427,31 +427,65 @@ export abstract class JsPackageManager { ): // Use generic and conditional type to force `string[]` if fetchAllVersions is true and `string` if false Promise; - public abstract runPackageCommand(command: string, args: string[], cwd?: string): string; - public abstract findInstallations(pattern?: string[]): InstallationMetadata | undefined; + public abstract runPackageCommand(command: string, args: string[], cwd?: string): Promise; + public abstract runPackageCommandSync(command: string, args: string[], cwd?: string): string; + public abstract findInstallations(pattern?: string[]): Promise; - public executeCommand({ + public executeCommandSync({ command, args = [], stdio, cwd, ignoreError = false, env, - }: { + ...execaOptions + }: CommonOptions & { command: string; args: string[]; - stdio?: CommonOptions['stdio']; cwd?: string; ignoreError?: boolean; - env?: CommonOptions['env']; }): string { try { - const commandResult = commandSync(command, args, { + const commandResult = execaCommandSync(command, args, { + cwd: cwd ?? this.cwd, + stdio: stdio ?? 'pipe', + encoding: 'utf-8', + shell: true, + env, + ...execaOptions, + }); + + return commandResult.stdout ?? ''; + } catch (err) { + if (ignoreError !== true) { + throw err; + } + return ''; + } + } + + public async executeCommand({ + command, + args = [], + stdio, + cwd, + ignoreError = false, + env, + ...execaOptions + }: CommonOptions & { + command: string; + args: string[]; + cwd?: string; + ignoreError?: boolean; + }): Promise { + try { + const commandResult = await execaCommand([command, ...args].join(' '), { cwd: cwd ?? this.cwd, stdio: stdio ?? 'pipe', encoding: 'utf-8', shell: true, env, + ...execaOptions, }); return commandResult.stdout ?? ''; diff --git a/code/lib/cli/src/js-package-manager/NPMProxy.test.ts b/code/lib/cli/src/js-package-manager/NPMProxy.test.ts index d8f6e7d3706a..c0c8cb63be52 100644 --- a/code/lib/cli/src/js-package-manager/NPMProxy.test.ts +++ b/code/lib/cli/src/js-package-manager/NPMProxy.test.ts @@ -12,10 +12,10 @@ describe('NPM Proxy', () => { }); describe('initPackageJson', () => { - it('should run `npm init -y`', () => { - const executeCommandSpy = jest.spyOn(npmProxy, 'executeCommand').mockReturnValue(''); + it('should run `npm init -y`', async () => { + const executeCommandSpy = jest.spyOn(npmProxy, 'executeCommand').mockResolvedValueOnce(''); - npmProxy.initPackageJson(); + await npmProxy.initPackageJson(); expect(executeCommandSpy).toHaveBeenCalledWith( expect.objectContaining({ command: 'npm', args: ['init', '-y'] }) @@ -24,10 +24,10 @@ describe('NPM Proxy', () => { }); describe('setRegistryUrl', () => { - it('should run `npm config set registry https://foo.bar`', () => { - const executeCommandSpy = jest.spyOn(npmProxy, 'executeCommand').mockReturnValue(''); + it('should run `npm config set registry https://foo.bar`', async () => { + const executeCommandSpy = jest.spyOn(npmProxy, 'executeCommand').mockResolvedValueOnce(''); - npmProxy.setRegistryURL('https://foo.bar'); + await npmProxy.setRegistryURL('https://foo.bar'); expect(executeCommandSpy).toHaveBeenCalledWith( expect.objectContaining({ @@ -40,29 +40,39 @@ describe('NPM Proxy', () => { describe('installDependencies', () => { describe('npm6', () => { - it('should run `npm install`', () => { - const executeCommandSpy = jest.spyOn(npmProxy, 'executeCommand').mockReturnValue('6.0.0'); + it('should run `npm install`', async () => { + const executeCommandSpy = jest + .spyOn(npmProxy, 'executeCommand') + .mockResolvedValueOnce('6.0.0'); - npmProxy.installDependencies(); + await npmProxy.installDependencies(); - expect(executeCommandSpy).toHaveBeenLastCalledWith('npm', ['install'], expect.any(String)); + expect(executeCommandSpy).toHaveBeenLastCalledWith( + expect.objectContaining({ command: 'npm', args: ['install'] }) + ); }); }); describe('npm7', () => { - it('should run `npm install`', () => { - const executeCommandSpy = jest.spyOn(npmProxy, 'executeCommand').mockReturnValue('7.1.0'); + it('should run `npm install`', async () => { + const executeCommandSpy = jest + .spyOn(npmProxy, 'executeCommand') + .mockResolvedValueOnce('7.1.0'); - npmProxy.installDependencies(); + await npmProxy.installDependencies(); - expect(executeCommandSpy).toHaveBeenLastCalledWith('npm', ['install'], expect.any(String)); + expect(executeCommandSpy).toHaveBeenLastCalledWith( + expect.objectContaining({ command: 'npm', args: ['install'] }) + ); }); }); }); describe('runScript', () => { describe('npm6', () => { - it('should execute script `npm exec -- compodoc -e json -d .`', () => { - const executeCommandSpy = jest.spyOn(npmProxy, 'executeCommand').mockReturnValue('6.0.0'); + it('should execute script `npm exec -- compodoc -e json -d .`', async () => { + const executeCommandSpy = jest + .spyOn(npmProxy, 'executeCommand') + .mockResolvedValueOnce('6.0.0'); npmProxy.runPackageCommand('compodoc', ['-e', 'json', '-d', '.']); @@ -75,10 +85,12 @@ describe('NPM Proxy', () => { }); }); describe('npm7', () => { - it('should execute script `npm run compodoc -- -e json -d .`', () => { - const executeCommandSpy = jest.spyOn(npmProxy, 'executeCommand').mockReturnValue('7.1.0'); + it('should execute script `npm run compodoc -- -e json -d .`', async () => { + const executeCommandSpy = jest + .spyOn(npmProxy, 'executeCommand') + .mockResolvedValueOnce('7.1.0'); - npmProxy.runPackageCommand('compodoc', ['-e', 'json', '-d', '.']); + await npmProxy.runPackageCommand('compodoc', ['-e', 'json', '-d', '.']); expect(executeCommandSpy).toHaveBeenLastCalledWith( expect.objectContaining({ @@ -92,10 +104,14 @@ describe('NPM Proxy', () => { describe('addDependencies', () => { describe('npm6', () => { - it('with devDep it should run `npm install -D @storybook/preview-api`', () => { - const executeCommandSpy = jest.spyOn(npmProxy, 'executeCommand').mockReturnValue('6.0.0'); + it('with devDep it should run `npm install -D @storybook/preview-api`', async () => { + const executeCommandSpy = jest + .spyOn(npmProxy, 'executeCommand') + .mockResolvedValueOnce('6.0.0'); - npmProxy.addDependencies({ installAsDevDependencies: true }, ['@storybook/preview-api']); + await npmProxy.addDependencies({ installAsDevDependencies: true }, [ + '@storybook/preview-api', + ]); expect(executeCommandSpy).toHaveBeenLastCalledWith( expect.objectContaining({ @@ -106,10 +122,14 @@ describe('NPM Proxy', () => { }); }); describe('npm7', () => { - it('with devDep it should run `npm install -D @storybook/preview-api`', () => { - const executeCommandSpy = jest.spyOn(npmProxy, 'executeCommand').mockReturnValue('7.0.0'); + it('with devDep it should run `npm install -D @storybook/preview-api`', async () => { + const executeCommandSpy = jest + .spyOn(npmProxy, 'executeCommand') + .mockResolvedValueOnce('7.0.0'); - npmProxy.addDependencies({ installAsDevDependencies: true }, ['@storybook/preview-api']); + await npmProxy.addDependencies({ installAsDevDependencies: true }, [ + '@storybook/preview-api', + ]); expect(executeCommandSpy).toHaveBeenLastCalledWith( expect.objectContaining({ @@ -123,8 +143,10 @@ describe('NPM Proxy', () => { describe('removeDependencies', () => { describe('npm6', () => { - it('with devDep it should run `npm uninstall @storybook/preview-api`', () => { - const executeCommandSpy = jest.spyOn(npmProxy, 'executeCommand').mockReturnValue('6.0.0'); + it('with devDep it should run `npm uninstall @storybook/preview-api`', async () => { + const executeCommandSpy = jest + .spyOn(npmProxy, 'executeCommand') + .mockResolvedValueOnce('6.0.0'); npmProxy.removeDependencies({}, ['@storybook/preview-api']); @@ -134,10 +156,12 @@ describe('NPM Proxy', () => { }); }); describe('npm7', () => { - it('with devDep it should run `npm uninstall @storybook/preview-api`', () => { - const executeCommandSpy = jest.spyOn(npmProxy, 'executeCommand').mockReturnValue('7.0.0'); + it('with devDep it should run `npm uninstall @storybook/preview-api`', async () => { + const executeCommandSpy = jest + .spyOn(npmProxy, 'executeCommand') + .mockResolvedValueOnce('7.0.0'); - npmProxy.removeDependencies({}, ['@storybook/preview-api']); + await npmProxy.removeDependencies({}, ['@storybook/preview-api']); expect(executeCommandSpy).toHaveBeenLastCalledWith( expect.objectContaining({ command: 'npm', args: ['uninstall', '@storybook/preview-api'] }) @@ -145,13 +169,15 @@ describe('NPM Proxy', () => { }); }); describe('skipInstall', () => { - it('should only change package.json without running install', () => { - const executeCommandSpy = jest.spyOn(npmProxy, 'executeCommand').mockReturnValue('7.0.0'); + it('should only change package.json without running install', async () => { + const executeCommandSpy = jest + .spyOn(npmProxy, 'executeCommand') + .mockResolvedValueOnce('7.0.0'); const writePackageSpy = jest .spyOn(npmProxy, 'writePackageJson') - .mockImplementation(jest.fn); + .mockImplementation(jest.fn()); - npmProxy.removeDependencies( + await npmProxy.removeDependencies( { skipInstall: true, packageJson: { @@ -176,7 +202,9 @@ describe('NPM Proxy', () => { describe('latestVersion', () => { it('without constraint it returns the latest version', async () => { - const executeCommandSpy = jest.spyOn(npmProxy, 'executeCommand').mockReturnValue('"5.3.19"'); + const executeCommandSpy = jest + .spyOn(npmProxy, 'executeCommand') + .mockResolvedValueOnce('"5.3.19"'); const version = await npmProxy.latestVersion('@storybook/preview-api'); @@ -192,7 +220,7 @@ describe('NPM Proxy', () => { it('with constraint it returns the latest version satisfying the constraint', async () => { const executeCommandSpy = jest .spyOn(npmProxy, 'executeCommand') - .mockReturnValue('["4.25.3","5.3.19","6.0.0-beta.23"]'); + .mockResolvedValueOnce('["4.25.3","5.3.19","6.0.0-beta.23"]'); const version = await npmProxy.latestVersion('@storybook/preview-api', '5.X'); @@ -206,7 +234,7 @@ describe('NPM Proxy', () => { }); it('throws an error if command output is not a valid JSON', async () => { - jest.spyOn(npmProxy, 'executeCommand').mockReturnValue('NOT A JSON'); + jest.spyOn(npmProxy, 'executeCommand').mockResolvedValueOnce('NOT A JSON'); await expect(npmProxy.latestVersion('@storybook/preview-api')).rejects.toThrow(); }); @@ -216,7 +244,9 @@ describe('NPM Proxy', () => { it('with a Storybook package listed in versions.json it returns the version', async () => { // eslint-disable-next-line global-require const storybookAngularVersion = require('../versions').default['@storybook/angular']; - const executeCommandSpy = jest.spyOn(npmProxy, 'executeCommand').mockReturnValue('"5.3.19"'); + const executeCommandSpy = jest + .spyOn(npmProxy, 'executeCommand') + .mockResolvedValueOnce('"5.3.19"'); const version = await npmProxy.getVersion('@storybook/angular'); @@ -233,7 +263,7 @@ describe('NPM Proxy', () => { const packageVersion = '5.3.19'; const executeCommandSpy = jest .spyOn(npmProxy, 'executeCommand') - .mockReturnValue(`"${packageVersion}"`); + .mockResolvedValueOnce(`"${packageVersion}"`); const version = await npmProxy.getVersion('@storybook/react-native'); @@ -248,11 +278,13 @@ describe('NPM Proxy', () => { }); describe('addPackageResolutions', () => { - it('adds resolutions to package.json and account for existing resolutions', () => { - const writePackageSpy = jest.spyOn(npmProxy, 'writePackageJson').mockImplementation(jest.fn); + it('adds resolutions to package.json and account for existing resolutions', async () => { + const writePackageSpy = jest + .spyOn(npmProxy, 'writePackageJson') + .mockImplementation(jest.fn()); jest.spyOn(npmProxy, 'retrievePackageJson').mockImplementation( - jest.fn(() => ({ + jest.fn(async () => ({ dependencies: {}, devDependencies: {}, overrides: { @@ -264,7 +296,7 @@ describe('NPM Proxy', () => { const versions = { foo: 'x.x.x', }; - npmProxy.addPackageResolutions(versions); + await npmProxy.addPackageResolutions(versions); expect(writePackageSpy).toHaveBeenCalledWith({ dependencies: {}, @@ -280,7 +312,7 @@ describe('NPM Proxy', () => { describe('mapDependencies', () => { it('should display duplicated dependencies based on npm output', async () => { // npm ls --depth 10 --json - jest.spyOn(npmProxy, 'executeCommand').mockReturnValue(` + jest.spyOn(npmProxy, 'executeCommand').mockResolvedValueOnce(` { "dependencies": { "unrelated-and-should-be-filtered": { diff --git a/code/lib/cli/src/js-package-manager/NPMProxy.ts b/code/lib/cli/src/js-package-manager/NPMProxy.ts index 7dd867fc0946..090bc93fed16 100644 --- a/code/lib/cli/src/js-package-manager/NPMProxy.ts +++ b/code/lib/cli/src/js-package-manager/NPMProxy.ts @@ -24,8 +24,8 @@ export class NPMProxy extends JsPackageManager { installArgs: string[] | undefined; - initPackageJson() { - return this.executeCommand({ command: 'npm', args: ['init', '-y'] }); + async initPackageJson() { + await this.executeCommand({ command: 'npm', args: ['init', '-y'] }); } getRunStorybookCommand(): string { @@ -36,7 +36,7 @@ export class NPMProxy extends JsPackageManager { return `npm run ${command}`; } - getNpmVersion(): string { + async getNpmVersion(): Promise { return this.executeCommand({ command: 'npm', args: ['--version'] }); } @@ -47,7 +47,15 @@ export class NPMProxy extends JsPackageManager { return this.installArgs; } - public runPackageCommand(command: string, args: string[], cwd?: string): string { + public runPackageCommandSync(command: string, args: string[], cwd?: string): string { + return this.executeCommandSync({ + command: 'npm', + args: ['exec', '--', command, ...args], + cwd, + }); + } + + public async runPackageCommand(command: string, args: string[], cwd?: string): Promise { return this.executeCommand({ command: 'npm', args: ['exec', '--', command, ...args], @@ -55,9 +63,9 @@ export class NPMProxy extends JsPackageManager { }); } - public findInstallations() { + public async findInstallations() { const pipeToNull = platform() === 'win32' ? '2>NUL' : '2>/dev/null'; - const commandResult = this.executeCommand({ + const commandResult = await this.executeCommand({ command: 'npm', args: ['ls', '--json', '--depth=99', pipeToNull], // ignore errors, because npm ls will exit with code 1 if there are e.g. unmet peer dependencies @@ -81,37 +89,45 @@ export class NPMProxy extends JsPackageManager { }; } - protected runInstall(): void { - this.executeCommand('npm', ['install', ...this.getInstallArgs()], 'inherit'); + protected async runInstall() { + await this.executeCommand({ + command: 'npm', + args: ['install', ...this.getInstallArgs()], + stdio: 'inherit', + }); } - protected runAddDeps(dependencies: string[], installAsDevDependencies: boolean): void { + protected async runAddDeps(dependencies: string[], installAsDevDependencies: boolean) { let args = [...dependencies]; if (installAsDevDependencies) { args = ['-D', ...args]; } - this.executeCommand('npm', ['install', ...this.getInstallArgs(), ...args], 'inherit'); + await this.executeCommand({ + command: 'npm', + args: ['install', ...this.getInstallArgs(), ...args], + stdio: 'inherit', + }); } - protected runRemoveDeps(dependencies: string[]): void { + protected async runRemoveDeps(dependencies: string[]) { const args = [...dependencies]; - this.executeCommand({ + await this.executeCommand({ command: 'npm', args: ['uninstall', ...this.getInstallArgs(), ...args], stdio: 'inherit', }); } - protected runGetVersions( + protected async runGetVersions( packageName: string, fetchAllVersions: T ): Promise { const args = [fetchAllVersions ? 'versions' : 'version', '--json']; - const commandResult = this.executeCommand({ + const commandResult = await this.executeCommand({ command: 'npm', args: ['info', packageName, ...args], }); diff --git a/code/lib/cli/src/js-package-manager/PNPMProxy.test.ts b/code/lib/cli/src/js-package-manager/PNPMProxy.test.ts index b79723e1f962..eb82f1a06465 100644 --- a/code/lib/cli/src/js-package-manager/PNPMProxy.test.ts +++ b/code/lib/cli/src/js-package-manager/PNPMProxy.test.ts @@ -12,10 +12,10 @@ describe('NPM Proxy', () => { }); describe('initPackageJson', () => { - it('should run `npm init -y`', () => { - const executeCommandSpy = jest.spyOn(pnpmProxy, 'executeCommand').mockReturnValue(''); + it('should run `npm init -y`', async () => { + const executeCommandSpy = jest.spyOn(pnpmProxy, 'executeCommand').mockResolvedValueOnce(''); - pnpmProxy.initPackageJson(); + await pnpmProxy.initPackageJson(); expect(executeCommandSpy).toHaveBeenCalledWith( expect.objectContaining({ command: 'pnpm', args: ['init', '-y'] }) @@ -24,10 +24,10 @@ describe('NPM Proxy', () => { }); describe('setRegistryUrl', () => { - it('should run `npm config set registry https://foo.bar`', () => { - const executeCommandSpy = jest.spyOn(pnpmProxy, 'executeCommand').mockReturnValue(''); + it('should run `npm config set registry https://foo.bar`', async () => { + const executeCommandSpy = jest.spyOn(pnpmProxy, 'executeCommand').mockResolvedValueOnce(''); - pnpmProxy.setRegistryURL('https://foo.bar'); + await pnpmProxy.setRegistryURL('https://foo.bar'); expect(executeCommandSpy).toHaveBeenCalledWith( expect.objectContaining({ @@ -39,10 +39,12 @@ describe('NPM Proxy', () => { }); describe('installDependencies', () => { - it('should run `pnpm install`', () => { - const executeCommandSpy = jest.spyOn(pnpmProxy, 'executeCommand').mockReturnValue('7.1.0'); + it('should run `pnpm install`', async () => { + const executeCommandSpy = jest + .spyOn(pnpmProxy, 'executeCommand') + .mockResolvedValueOnce('7.1.0'); - pnpmProxy.installDependencies(); + await pnpmProxy.installDependencies(); expect(executeCommandSpy).toHaveBeenLastCalledWith( expect.objectContaining({ command: 'pnpm', args: ['install'] }) @@ -51,10 +53,12 @@ describe('NPM Proxy', () => { }); describe('runScript', () => { - it('should execute script `yarn compodoc -- -e json -d .`', () => { - const executeCommandSpy = jest.spyOn(pnpmProxy, 'executeCommand').mockReturnValue('7.1.0'); + it('should execute script `yarn compodoc -- -e json -d .`', async () => { + const executeCommandSpy = jest + .spyOn(pnpmProxy, 'executeCommand') + .mockResolvedValueOnce('7.1.0'); - pnpmProxy.runPackageCommand('compodoc', ['-e', 'json', '-d', '.']); + await pnpmProxy.runPackageCommand('compodoc', ['-e', 'json', '-d', '.']); expect(executeCommandSpy).toHaveBeenLastCalledWith( expect.objectContaining({ @@ -66,10 +70,14 @@ describe('NPM Proxy', () => { }); describe('addDependencies', () => { - it('with devDep it should run `pnpm add -D @storybook/preview-api`', () => { - const executeCommandSpy = jest.spyOn(pnpmProxy, 'executeCommand').mockReturnValue('6.0.0'); + it('with devDep it should run `pnpm add -D @storybook/preview-api`', async () => { + const executeCommandSpy = jest + .spyOn(pnpmProxy, 'executeCommand') + .mockResolvedValueOnce('6.0.0'); - pnpmProxy.addDependencies({ installAsDevDependencies: true }, ['@storybook/preview-api']); + await pnpmProxy.addDependencies({ installAsDevDependencies: true }, [ + '@storybook/preview-api', + ]); expect(executeCommandSpy).toHaveBeenLastCalledWith( expect.objectContaining({ command: 'pnpm', args: ['add', '-D', '@storybook/preview-api'] }) @@ -78,10 +86,12 @@ describe('NPM Proxy', () => { }); describe('removeDependencies', () => { - it('with devDep it should run `npm uninstall @storybook/preview-api`', () => { - const executeCommandSpy = jest.spyOn(pnpmProxy, 'executeCommand').mockReturnValue('6.0.0'); + it('with devDep it should run `npm uninstall @storybook/preview-api`', async () => { + const executeCommandSpy = jest + .spyOn(pnpmProxy, 'executeCommand') + .mockResolvedValueOnce('6.0.0'); - pnpmProxy.removeDependencies({}, ['@storybook/preview-api']); + await pnpmProxy.removeDependencies({}, ['@storybook/preview-api']); expect(executeCommandSpy).toHaveBeenLastCalledWith( expect.objectContaining({ command: 'pnpm', args: ['remove', '@storybook/preview-api'] }) @@ -89,13 +99,15 @@ describe('NPM Proxy', () => { }); describe('skipInstall', () => { - it('should only change package.json without running install', () => { - const executeCommandSpy = jest.spyOn(pnpmProxy, 'executeCommand').mockReturnValue('7.0.0'); + it('should only change package.json without running install', async () => { + const executeCommandSpy = jest + .spyOn(pnpmProxy, 'executeCommand') + .mockResolvedValueOnce('7.0.0'); const writePackageSpy = jest .spyOn(pnpmProxy, 'writePackageJson') - .mockImplementation(jest.fn); + .mockImplementation(jest.fn()); - pnpmProxy.removeDependencies( + await pnpmProxy.removeDependencies( { skipInstall: true, packageJson: { @@ -120,7 +132,9 @@ describe('NPM Proxy', () => { describe('latestVersion', () => { it('without constraint it returns the latest version', async () => { - const executeCommandSpy = jest.spyOn(pnpmProxy, 'executeCommand').mockReturnValue('"5.3.19"'); + const executeCommandSpy = jest + .spyOn(pnpmProxy, 'executeCommand') + .mockResolvedValueOnce('"5.3.19"'); const version = await pnpmProxy.latestVersion('@storybook/preview-api'); @@ -136,7 +150,7 @@ describe('NPM Proxy', () => { it('with constraint it returns the latest version satisfying the constraint', async () => { const executeCommandSpy = jest .spyOn(pnpmProxy, 'executeCommand') - .mockReturnValue('["4.25.3","5.3.19","6.0.0-beta.23"]'); + .mockResolvedValueOnce('["4.25.3","5.3.19","6.0.0-beta.23"]'); const version = await pnpmProxy.latestVersion('@storybook/preview-api', '5.X'); @@ -150,7 +164,7 @@ describe('NPM Proxy', () => { }); it('throws an error if command output is not a valid JSON', async () => { - jest.spyOn(pnpmProxy, 'executeCommand').mockReturnValue('NOT A JSON'); + jest.spyOn(pnpmProxy, 'executeCommand').mockResolvedValueOnce('NOT A JSON'); await expect(pnpmProxy.latestVersion('@storybook/preview-api')).rejects.toThrow(); }); @@ -160,7 +174,9 @@ describe('NPM Proxy', () => { it('with a Storybook package listed in versions.json it returns the version', async () => { // eslint-disable-next-line global-require const storybookAngularVersion = require('../versions').default['@storybook/angular']; - const executeCommandSpy = jest.spyOn(pnpmProxy, 'executeCommand').mockReturnValue('"5.3.19"'); + const executeCommandSpy = jest + .spyOn(pnpmProxy, 'executeCommand') + .mockResolvedValueOnce('"5.3.19"'); const version = await pnpmProxy.getVersion('@storybook/angular'); @@ -177,7 +193,7 @@ describe('NPM Proxy', () => { const packageVersion = '5.3.19'; const executeCommandSpy = jest .spyOn(pnpmProxy, 'executeCommand') - .mockReturnValue(`"${packageVersion}"`); + .mockResolvedValueOnce(`"${packageVersion}"`); const version = await pnpmProxy.getVersion('@storybook/react-native'); @@ -192,8 +208,10 @@ describe('NPM Proxy', () => { }); describe('addPackageResolutions', () => { - it('adds resolutions to package.json and account for existing resolutions', () => { - const writePackageSpy = jest.spyOn(pnpmProxy, 'writePackageJson').mockImplementation(jest.fn); + it('adds resolutions to package.json and account for existing resolutions', async () => { + const writePackageSpy = jest + .spyOn(pnpmProxy, 'writePackageJson') + .mockImplementation(jest.fn()); jest.spyOn(pnpmProxy, 'retrievePackageJson').mockImplementation( // @ts-expect-error (not strict) @@ -207,7 +225,7 @@ describe('NPM Proxy', () => { const versions = { foo: 'x.x.x', }; - pnpmProxy.addPackageResolutions(versions); + await pnpmProxy.addPackageResolutions(versions); expect(writePackageSpy).toHaveBeenCalledWith({ overrides: { @@ -221,7 +239,7 @@ describe('NPM Proxy', () => { describe('mapDependencies', () => { it('should display duplicated dependencies based on pnpm output', async () => { // pnpm list "@storybook/*" "storybook" --depth 10 --json - jest.spyOn(pnpmProxy, 'executeCommand').mockReturnValue(` + jest.spyOn(pnpmProxy, 'executeCommand').mockResolvedValueOnce(` [ { "peerDependencies": { diff --git a/code/lib/cli/src/js-package-manager/PNPMProxy.ts b/code/lib/cli/src/js-package-manager/PNPMProxy.ts index 609e60352252..1c644cdb387c 100644 --- a/code/lib/cli/src/js-package-manager/PNPMProxy.ts +++ b/code/lib/cli/src/js-package-manager/PNPMProxy.ts @@ -34,8 +34,8 @@ export class PNPMProxy extends JsPackageManager { return pathExistsSync(pnpmWorkspaceYaml); } - initPackageJson() { - return this.executeCommand({ + async initPackageJson() { + await this.executeCommand({ command: 'pnpm', args: ['init', '-y'], }); @@ -49,7 +49,7 @@ export class PNPMProxy extends JsPackageManager { return `pnpm run ${command}`; } - getPnpmVersion(): string { + async getPnpmVersion(): Promise { return this.executeCommand({ command: 'pnpm', args: ['--version'], @@ -67,7 +67,15 @@ export class PNPMProxy extends JsPackageManager { return this.installArgs; } - runPackageCommand(command: string, args: string[], cwd?: string): string { + public runPackageCommandSync(command: string, args: string[], cwd?: string): string { + return this.executeCommandSync({ + command: 'pnpm', + args: ['exec', command, ...args], + cwd, + }); + } + + async runPackageCommand(command: string, args: string[], cwd?: string): Promise { return this.executeCommand({ command: 'pnpm', args: ['exec', command, ...args], @@ -75,8 +83,8 @@ export class PNPMProxy extends JsPackageManager { }); } - public findInstallations(pattern: string[]) { - const commandResult = this.executeCommand({ + public async findInstallations(pattern: string[]) { + const commandResult = await this.executeCommand({ command: 'pnpm', args: ['list', pattern.map((p) => `"${p}"`).join(' '), '--json', '--depth=99'], }); @@ -98,45 +106,45 @@ export class PNPMProxy extends JsPackageManager { }; } - protected runInstall(): void { - this.executeCommand({ + protected async runInstall() { + await this.executeCommand({ command: 'pnpm', args: ['install', ...this.getInstallArgs()], stdio: 'inherit', }); } - protected runAddDeps(dependencies: string[], installAsDevDependencies: boolean): void { + protected async runAddDeps(dependencies: string[], installAsDevDependencies: boolean) { let args = [...dependencies]; if (installAsDevDependencies) { args = ['-D', ...args]; } - this.executeCommand({ + await this.executeCommand({ command: 'pnpm', args: ['add', ...args, ...this.getInstallArgs()], stdio: 'inherit', }); } - protected runRemoveDeps(dependencies: string[]): void { + protected async runRemoveDeps(dependencies: string[]) { const args = [...dependencies]; - this.executeCommand({ + await this.executeCommand({ command: 'pnpm', args: ['remove', ...args, ...this.getInstallArgs()], stdio: 'inherit', }); } - protected runGetVersions( + protected async runGetVersions( packageName: string, fetchAllVersions: T ): Promise { const args = [fetchAllVersions ? 'versions' : 'version', '--json']; - const commandResult = this.executeCommand({ + const commandResult = await this.executeCommand({ command: 'pnpm', args: ['info', packageName, ...args], }); diff --git a/code/lib/cli/src/js-package-manager/Yarn1Proxy.test.ts b/code/lib/cli/src/js-package-manager/Yarn1Proxy.test.ts index 1371d70b2191..fb9edaef3cc0 100644 --- a/code/lib/cli/src/js-package-manager/Yarn1Proxy.test.ts +++ b/code/lib/cli/src/js-package-manager/Yarn1Proxy.test.ts @@ -12,10 +12,10 @@ describe('Yarn 1 Proxy', () => { }); describe('initPackageJson', () => { - it('should run `yarn init -y`', () => { - const executeCommandSpy = jest.spyOn(yarn1Proxy, 'executeCommand').mockReturnValue(''); + it('should run `yarn init -y`', async () => { + const executeCommandSpy = jest.spyOn(yarn1Proxy, 'executeCommand').mockResolvedValueOnce(''); - yarn1Proxy.initPackageJson(); + await yarn1Proxy.initPackageJson(); expect(executeCommandSpy).toHaveBeenCalledWith( expect.objectContaining({ command: 'yarn', args: ['init', '-y'] }) @@ -24,10 +24,10 @@ describe('Yarn 1 Proxy', () => { }); describe('setRegistryUrl', () => { - it('should run `yarn config set npmRegistryServer https://foo.bar`', () => { - const executeCommandSpy = jest.spyOn(yarn1Proxy, 'executeCommand').mockReturnValue(''); + it('should run `yarn config set npmRegistryServer https://foo.bar`', async () => { + const executeCommandSpy = jest.spyOn(yarn1Proxy, 'executeCommand').mockResolvedValueOnce(''); - yarn1Proxy.setRegistryURL('https://foo.bar'); + await yarn1Proxy.setRegistryURL('https://foo.bar'); expect(executeCommandSpy).toHaveBeenCalledWith( expect.objectContaining({ @@ -39,10 +39,10 @@ describe('Yarn 1 Proxy', () => { }); describe('installDependencies', () => { - it('should run `yarn`', () => { - const executeCommandSpy = jest.spyOn(yarn1Proxy, 'executeCommand').mockReturnValue(''); + it('should run `yarn`', async () => { + const executeCommandSpy = jest.spyOn(yarn1Proxy, 'executeCommand').mockResolvedValueOnce(''); - yarn1Proxy.installDependencies(); + await yarn1Proxy.installDependencies(); expect(executeCommandSpy).toHaveBeenCalledWith( expect.objectContaining({ @@ -54,10 +54,12 @@ describe('Yarn 1 Proxy', () => { }); describe('runScript', () => { - it('should execute script `yarn compodoc -- -e json -d .`', () => { - const executeCommandSpy = jest.spyOn(yarn1Proxy, 'executeCommand').mockReturnValue('7.1.0'); + it('should execute script `yarn compodoc -- -e json -d .`', async () => { + const executeCommandSpy = jest + .spyOn(yarn1Proxy, 'executeCommand') + .mockResolvedValueOnce('7.1.0'); - yarn1Proxy.runPackageCommand('compodoc', ['-e', 'json', '-d', '.']); + await yarn1Proxy.runPackageCommand('compodoc', ['-e', 'json', '-d', '.']); expect(executeCommandSpy).toHaveBeenLastCalledWith( expect.objectContaining({ command: 'yarn', args: ['compodoc', '-e', 'json', '-d', '.'] }) @@ -66,10 +68,12 @@ describe('Yarn 1 Proxy', () => { }); describe('addDependencies', () => { - it('with devDep it should run `yarn install -D --ignore-workspace-root-check @storybook/preview-api`', () => { - const executeCommandSpy = jest.spyOn(yarn1Proxy, 'executeCommand').mockReturnValue(''); + it('with devDep it should run `yarn install -D --ignore-workspace-root-check @storybook/preview-api`', async () => { + const executeCommandSpy = jest.spyOn(yarn1Proxy, 'executeCommand').mockResolvedValueOnce(''); - yarn1Proxy.addDependencies({ installAsDevDependencies: true }, ['@storybook/preview-api']); + await yarn1Proxy.addDependencies({ installAsDevDependencies: true }, [ + '@storybook/preview-api', + ]); expect(executeCommandSpy).toHaveBeenCalledWith( expect.objectContaining({ @@ -81,8 +85,8 @@ describe('Yarn 1 Proxy', () => { }); describe('removeDependencies', () => { - it('should run `yarn remove --ignore-workspace-root-check @storybook/preview-api`', () => { - const executeCommandSpy = jest.spyOn(yarn1Proxy, 'executeCommand').mockReturnValue(''); + it('should run `yarn remove --ignore-workspace-root-check @storybook/preview-api`', async () => { + const executeCommandSpy = jest.spyOn(yarn1Proxy, 'executeCommand').mockResolvedValueOnce(''); yarn1Proxy.removeDependencies({}, ['@storybook/preview-api']); @@ -94,13 +98,15 @@ describe('Yarn 1 Proxy', () => { ); }); - it('skipInstall should only change package.json without running install', () => { - const executeCommandSpy = jest.spyOn(yarn1Proxy, 'executeCommand').mockReturnValue('7.0.0'); + it('skipInstall should only change package.json without running install', async () => { + const executeCommandSpy = jest + .spyOn(yarn1Proxy, 'executeCommand') + .mockResolvedValueOnce('7.0.0'); const writePackageSpy = jest .spyOn(yarn1Proxy, 'writePackageJson') - .mockImplementation(jest.fn); + .mockImplementation(jest.fn()); - yarn1Proxy.removeDependencies( + await yarn1Proxy.removeDependencies( { skipInstall: true, packageJson: { @@ -126,7 +132,7 @@ describe('Yarn 1 Proxy', () => { it('without constraint it returns the latest version', async () => { const executeCommandSpy = jest .spyOn(yarn1Proxy, 'executeCommand') - .mockReturnValue('{"type":"inspect","data":"5.3.19"}'); + .mockResolvedValueOnce('{"type":"inspect","data":"5.3.19"}'); const version = await yarn1Proxy.latestVersion('@storybook/preview-api'); @@ -142,7 +148,7 @@ describe('Yarn 1 Proxy', () => { it('with constraint it returns the latest version satisfying the constraint', async () => { const executeCommandSpy = jest .spyOn(yarn1Proxy, 'executeCommand') - .mockReturnValue('{"type":"inspect","data":["4.25.3","5.3.19","6.0.0-beta.23"]}'); + .mockResolvedValueOnce('{"type":"inspect","data":["4.25.3","5.3.19","6.0.0-beta.23"]}'); const version = await yarn1Proxy.latestVersion('@storybook/preview-api', '5.X'); @@ -156,20 +162,20 @@ describe('Yarn 1 Proxy', () => { }); it('throws an error if command output is not a valid JSON', async () => { - jest.spyOn(yarn1Proxy, 'executeCommand').mockReturnValue('NOT A JSON'); + jest.spyOn(yarn1Proxy, 'executeCommand').mockResolvedValueOnce('NOT A JSON'); await expect(yarn1Proxy.latestVersion('@storybook/preview-api')).rejects.toThrow(); }); }); describe('addPackageResolutions', () => { - it('adds resolutions to package.json and account for existing resolutions', () => { + it('adds resolutions to package.json and account for existing resolutions', async () => { const writePackageSpy = jest .spyOn(yarn1Proxy, 'writePackageJson') - .mockImplementation(jest.fn); + .mockImplementation(jest.fn()); jest.spyOn(yarn1Proxy, 'retrievePackageJson').mockImplementation( - jest.fn(() => ({ + jest.fn(async () => ({ dependencies: {}, devDependencies: {}, resolutions: { @@ -181,7 +187,7 @@ describe('Yarn 1 Proxy', () => { const versions = { foo: 'x.x.x', }; - yarn1Proxy.addPackageResolutions(versions); + await yarn1Proxy.addPackageResolutions(versions); expect(writePackageSpy).toHaveBeenCalledWith({ dependencies: {}, @@ -197,7 +203,7 @@ describe('Yarn 1 Proxy', () => { describe('mapDependencies', () => { it('should display duplicated dependencies based on yarn output', async () => { // yarn list --pattern "@storybook/*" "@storybook/react" --recursive --json - jest.spyOn(yarn1Proxy, 'executeCommand').mockReturnValue(` + jest.spyOn(yarn1Proxy, 'executeCommand').mockResolvedValueOnce(` { "type": "tree", "data": { diff --git a/code/lib/cli/src/js-package-manager/Yarn1Proxy.ts b/code/lib/cli/src/js-package-manager/Yarn1Proxy.ts index b51a91d37b5f..ad85c1f5452b 100644 --- a/code/lib/cli/src/js-package-manager/Yarn1Proxy.ts +++ b/code/lib/cli/src/js-package-manager/Yarn1Proxy.ts @@ -30,8 +30,8 @@ export class Yarn1Proxy extends JsPackageManager { return this.installArgs; } - initPackageJson() { - return this.executeCommand({ command: 'yarn', args: ['init', '-y'] }); + async initPackageJson() { + await this.executeCommand({ command: 'yarn', args: ['init', '-y'] }); } getRunStorybookCommand(): string { @@ -42,12 +42,16 @@ export class Yarn1Proxy extends JsPackageManager { return `yarn ${command}`; } - runPackageCommand(command: string, args: string[], cwd?: string): string { + public runPackageCommandSync(command: string, args: string[], cwd?: string): string { + return this.executeCommandSync({ command: `yarn`, args: [command, ...args], cwd }); + } + + async runPackageCommand(command: string, args: string[], cwd?: string): Promise { return this.executeCommand({ command: `yarn`, args: [command, ...args], cwd }); } - public findInstallations(pattern: string[]) { - const commandResult = this.executeCommand({ + public async findInstallations(pattern: string[]) { + const commandResult = await this.executeCommand({ command: 'yarn', args: ['list', '--pattern', pattern.map((p) => `"${p}"`).join(' '), '--recursive', '--json'], }); @@ -69,45 +73,45 @@ export class Yarn1Proxy extends JsPackageManager { }; } - protected runInstall(): void { - this.executeCommand({ + protected async runInstall() { + await this.executeCommand({ command: 'yarn', args: ['install', ...this.getInstallArgs()], stdio: 'inherit', }); } - protected runAddDeps(dependencies: string[], installAsDevDependencies: boolean): void { + protected async runAddDeps(dependencies: string[], installAsDevDependencies: boolean) { let args = [...dependencies]; if (installAsDevDependencies) { args = ['-D', ...args]; } - this.executeCommand({ + await this.executeCommand({ command: 'yarn', args: ['add', ...this.getInstallArgs(), ...args], stdio: 'inherit', }); } - protected runRemoveDeps(dependencies: string[]): void { + protected async runRemoveDeps(dependencies: string[]) { const args = [...dependencies]; - this.executeCommand({ + await this.executeCommand({ command: 'yarn', args: ['remove', ...this.getInstallArgs(), ...args], stdio: 'inherit', }); } - protected runGetVersions( + protected async runGetVersions( packageName: string, fetchAllVersions: T ): Promise { const args = [fetchAllVersions ? 'versions' : 'version', '--json']; - const commandResult = this.executeCommand({ + const commandResult = await this.executeCommand({ command: 'yarn', args: ['info', packageName, ...args], }); diff --git a/code/lib/cli/src/js-package-manager/Yarn2Proxy.test.ts b/code/lib/cli/src/js-package-manager/Yarn2Proxy.test.ts index 4156bf5816cf..f875254a6858 100644 --- a/code/lib/cli/src/js-package-manager/Yarn2Proxy.test.ts +++ b/code/lib/cli/src/js-package-manager/Yarn2Proxy.test.ts @@ -12,10 +12,10 @@ describe('Yarn 2 Proxy', () => { }); describe('initPackageJson', () => { - it('should run `yarn init`', () => { - const executeCommandSpy = jest.spyOn(yarn2Proxy, 'executeCommand').mockReturnValue(''); + it('should run `yarn init`', async () => { + const executeCommandSpy = jest.spyOn(yarn2Proxy, 'executeCommand').mockResolvedValueOnce(''); - yarn2Proxy.initPackageJson(); + await yarn2Proxy.initPackageJson(); expect(executeCommandSpy).toHaveBeenCalledWith( expect.objectContaining({ command: 'yarn', args: ['init'] }) @@ -24,10 +24,10 @@ describe('Yarn 2 Proxy', () => { }); describe('installDependencies', () => { - it('should run `yarn`', () => { - const executeCommandSpy = jest.spyOn(yarn2Proxy, 'executeCommand').mockReturnValue(''); + it('should run `yarn`', async () => { + const executeCommandSpy = jest.spyOn(yarn2Proxy, 'executeCommand').mockResolvedValueOnce(''); - yarn2Proxy.installDependencies(); + await yarn2Proxy.installDependencies(); expect(executeCommandSpy).toHaveBeenCalledWith( expect.objectContaining({ command: 'yarn', args: ['install'] }) @@ -36,10 +36,12 @@ describe('Yarn 2 Proxy', () => { }); describe('runScript', () => { - it('should execute script `yarn compodoc -- -e json -d .`', () => { - const executeCommandSpy = jest.spyOn(yarn2Proxy, 'executeCommand').mockReturnValue('7.1.0'); + it('should execute script `yarn compodoc -- -e json -d .`', async () => { + const executeCommandSpy = jest + .spyOn(yarn2Proxy, 'executeCommand') + .mockResolvedValueOnce('7.1.0'); - yarn2Proxy.runPackageCommand('compodoc', ['-e', 'json', '-d', '.']); + await yarn2Proxy.runPackageCommand('compodoc', ['-e', 'json', '-d', '.']); expect(executeCommandSpy).toHaveBeenLastCalledWith( expect.objectContaining({ @@ -51,10 +53,10 @@ describe('Yarn 2 Proxy', () => { }); describe('setRegistryUrl', () => { - it('should run `yarn config set npmRegistryServer https://foo.bar`', () => { - const executeCommandSpy = jest.spyOn(yarn2Proxy, 'executeCommand').mockReturnValue(''); + it('should run `yarn config set npmRegistryServer https://foo.bar`', async () => { + const executeCommandSpy = jest.spyOn(yarn2Proxy, 'executeCommand').mockResolvedValueOnce(''); - yarn2Proxy.setRegistryURL('https://foo.bar'); + await yarn2Proxy.setRegistryURL('https://foo.bar'); expect(executeCommandSpy).toHaveBeenCalledWith( expect.objectContaining({ @@ -66,10 +68,12 @@ describe('Yarn 2 Proxy', () => { }); describe('addDependencies', () => { - it('with devDep it should run `yarn install -D @storybook/preview-api`', () => { - const executeCommandSpy = jest.spyOn(yarn2Proxy, 'executeCommand').mockReturnValue(''); + it('with devDep it should run `yarn install -D @storybook/preview-api`', async () => { + const executeCommandSpy = jest.spyOn(yarn2Proxy, 'executeCommand').mockResolvedValueOnce(''); - yarn2Proxy.addDependencies({ installAsDevDependencies: true }, ['@storybook/preview-api']); + await yarn2Proxy.addDependencies({ installAsDevDependencies: true }, [ + '@storybook/preview-api', + ]); expect(executeCommandSpy).toHaveBeenCalledWith( expect.objectContaining({ command: 'yarn', args: ['add', '-D', '@storybook/preview-api'] }) @@ -78,10 +82,10 @@ describe('Yarn 2 Proxy', () => { }); describe('removeDependencies', () => { - it('should run `yarn remove @storybook/preview-api`', () => { - const executeCommandSpy = jest.spyOn(yarn2Proxy, 'executeCommand').mockReturnValue(''); + it('should run `yarn remove @storybook/preview-api`', async () => { + const executeCommandSpy = jest.spyOn(yarn2Proxy, 'executeCommand').mockResolvedValueOnce(''); - yarn2Proxy.removeDependencies({}, ['@storybook/preview-api']); + await yarn2Proxy.removeDependencies({}, ['@storybook/preview-api']); expect(executeCommandSpy).toHaveBeenCalledWith( expect.objectContaining({ @@ -91,13 +95,15 @@ describe('Yarn 2 Proxy', () => { ); }); - it('skipInstall should only change package.json without running install', () => { - const executeCommandSpy = jest.spyOn(yarn2Proxy, 'executeCommand').mockReturnValue('7.0.0'); + it('skipInstall should only change package.json without running install', async () => { + const executeCommandSpy = jest + .spyOn(yarn2Proxy, 'executeCommand') + .mockResolvedValueOnce('7.0.0'); const writePackageSpy = jest .spyOn(yarn2Proxy, 'writePackageJson') - .mockImplementation(jest.fn); + .mockImplementation(jest.fn()); - yarn2Proxy.removeDependencies( + await yarn2Proxy.removeDependencies( { skipInstall: true, packageJson: { @@ -123,7 +129,7 @@ describe('Yarn 2 Proxy', () => { it('without constraint it returns the latest version', async () => { const executeCommandSpy = jest .spyOn(yarn2Proxy, 'executeCommand') - .mockReturnValue('{"name":"@storybook/preview-api","version":"5.3.19"}'); + .mockResolvedValueOnce('{"name":"@storybook/preview-api","version":"5.3.19"}'); const version = await yarn2Proxy.latestVersion('@storybook/preview-api'); @@ -139,7 +145,7 @@ describe('Yarn 2 Proxy', () => { it('with constraint it returns the latest version satisfying the constraint', async () => { const executeCommandSpy = jest .spyOn(yarn2Proxy, 'executeCommand') - .mockReturnValue( + .mockResolvedValueOnce( '{"name":"@storybook/preview-api","versions":["4.25.3","5.3.19","6.0.0-beta.23"]}' ); @@ -155,20 +161,20 @@ describe('Yarn 2 Proxy', () => { }); it('throws an error if command output is not a valid JSON', async () => { - jest.spyOn(yarn2Proxy, 'executeCommand').mockReturnValue('NOT A JSON'); + jest.spyOn(yarn2Proxy, 'executeCommand').mockResolvedValueOnce('NOT A JSON'); await expect(yarn2Proxy.latestVersion('@storybook/preview-api')).rejects.toThrow(); }); }); describe('addPackageResolutions', () => { - it('adds resolutions to package.json and account for existing resolutions', () => { + it('adds resolutions to package.json and account for existing resolutions', async () => { const writePackageSpy = jest .spyOn(yarn2Proxy, 'writePackageJson') - .mockImplementation(jest.fn); + .mockImplementation(jest.fn()); jest.spyOn(yarn2Proxy, 'retrievePackageJson').mockImplementation( - jest.fn(() => ({ + jest.fn(async () => ({ dependencies: {}, devDependencies: {}, resolutions: { @@ -180,7 +186,8 @@ describe('Yarn 2 Proxy', () => { const versions = { foo: 'x.x.x', }; - yarn2Proxy.addPackageResolutions(versions); + + await yarn2Proxy.addPackageResolutions(versions); expect(writePackageSpy).toHaveBeenCalledWith({ dependencies: {}, @@ -196,7 +203,7 @@ describe('Yarn 2 Proxy', () => { describe('mapDependencies', () => { it('should display duplicated dependencies based on yarn2 output', async () => { // yarn info --name-only --recursive "@storybook/*" "storybook" - jest.spyOn(yarn2Proxy, 'executeCommand').mockReturnValue(` + jest.spyOn(yarn2Proxy, 'executeCommand').mockResolvedValueOnce(` "unrelated-and-should-be-filtered@npm:1.0.0" "@storybook/global@npm:5.0.0" "@storybook/instrumenter@npm:7.0.0-beta.12" diff --git a/code/lib/cli/src/js-package-manager/Yarn2Proxy.ts b/code/lib/cli/src/js-package-manager/Yarn2Proxy.ts index 400949b68099..e565e7f12ee2 100644 --- a/code/lib/cli/src/js-package-manager/Yarn2Proxy.ts +++ b/code/lib/cli/src/js-package-manager/Yarn2Proxy.ts @@ -16,8 +16,8 @@ export class Yarn2Proxy extends JsPackageManager { return this.installArgs; } - initPackageJson() { - return this.executeCommand({ command: 'yarn', args: ['init'] }); + async initPackageJson() { + await this.executeCommand({ command: 'yarn', args: ['init'] }); } getRunStorybookCommand(): string { @@ -28,12 +28,16 @@ export class Yarn2Proxy extends JsPackageManager { return `yarn ${command}`; } - runPackageCommand(command: string, args: string[], cwd?: string): string { + public runPackageCommandSync(command: string, args: string[], cwd?: string) { + return this.executeCommandSync({ command: 'yarn', args: [command, ...args], cwd }); + } + + async runPackageCommand(command: string, args: string[], cwd?: string) { return this.executeCommand({ command: 'yarn', args: [command, ...args], cwd }); } - public findInstallations(pattern: string[]) { - const commandResult = this.executeCommand({ + public async findInstallations(pattern: string[]) { + const commandResult = await this.executeCommand({ command: 'yarn', args: [ 'info', @@ -60,46 +64,46 @@ export class Yarn2Proxy extends JsPackageManager { }; } - protected runInstall(): void { - this.executeCommand({ + protected async runInstall() { + await this.executeCommand({ command: 'yarn', args: ['install', ...this.getInstallArgs()], stdio: 'inherit', }); } - protected runAddDeps(dependencies: string[], installAsDevDependencies: boolean): void { + protected async runAddDeps(dependencies: string[], installAsDevDependencies: boolean) { let args = [...dependencies]; if (installAsDevDependencies) { args = ['-D', ...args]; } - this.executeCommand({ + await this.executeCommand({ command: 'yarn', args: ['add', ...this.getInstallArgs(), ...args], stdio: 'inherit', }); } - protected runRemoveDeps(dependencies: string[]): void { + protected async runRemoveDeps(dependencies: string[]) { const args = [...dependencies]; - this.executeCommand({ + await this.executeCommand({ command: 'yarn', args: ['remove', ...this.getInstallArgs(), ...args], stdio: 'inherit', }); } - protected runGetVersions( + protected async runGetVersions( packageName: string, fetchAllVersions: T ): Promise { const field = fetchAllVersions ? 'versions' : 'version'; const args = ['--fields', field, '--json']; - const commandResult = this.executeCommand({ + const commandResult = await this.executeCommand({ command: 'yarn', args: ['npm', 'info', packageName, ...args], }); diff --git a/code/lib/cli/src/migrate.ts b/code/lib/cli/src/migrate.ts index 7162e205b1f5..2d0a3b6af16d 100644 --- a/code/lib/cli/src/migrate.ts +++ b/code/lib/cli/src/migrate.ts @@ -22,8 +22,8 @@ export async function migrate(migration: any, { glob, dryRun, list, rename, pars export async function addStorybookBlocksPackage() { const packageManager = JsPackageManagerFactory.getPackageManager(); - const packageJson = packageManager.retrievePackageJson(); - const versionToInstall = getStorybookVersionSpecifier(packageManager.retrievePackageJson()); + const packageJson = await packageManager.retrievePackageJson(); + const versionToInstall = getStorybookVersionSpecifier(await packageManager.retrievePackageJson()); logger.info(`✅ Adding "@storybook/blocks" package`); await packageManager.addDependencies({ installAsDevDependencies: true, packageJson }, [ `@storybook/blocks@${versionToInstall}`, diff --git a/code/lib/cli/src/upgrade.ts b/code/lib/cli/src/upgrade.ts index 6d25ed46c138..72fbab8545dd 100644 --- a/code/lib/cli/src/upgrade.ts +++ b/code/lib/cli/src/upgrade.ts @@ -189,7 +189,7 @@ export const doUpgrade = async ({ if (!dryRun) flags.push('--upgrade'); flags.push('--target'); flags.push(target); - flags = addExtraFlags(EXTRA_FLAGS, flags, packageManager.retrievePackageJson()); + flags = addExtraFlags(EXTRA_FLAGS, flags, await packageManager.retrievePackageJson()); const check = spawnSync('npx', ['npm-check-updates@latest', '/storybook/', ...flags], { stdio: 'pipe', shell: true, @@ -204,7 +204,7 @@ export const doUpgrade = async ({ if (!dryRun) { commandLog(`Installing upgrades`); - packageManager.installDependencies(); + await packageManager.installDependencies(); } let automigrationResults; diff --git a/scripts/sandbox/generate.ts b/scripts/sandbox/generate.ts index 8e3d1d0c3ad9..ce4d72f0001f 100755 --- a/scripts/sandbox/generate.ts +++ b/scripts/sandbox/generate.ts @@ -38,7 +38,7 @@ const sbInit = async (cwd: string, flags?: string[], debug?: boolean) => { }; const withLocalRegistry = async (packageManager: JsPackageManager, action: () => Promise) => { - const prevUrl = packageManager.getRegistryURL(); + const prevUrl = await packageManager.getRegistryURL(); let error; try { console.log(`📦 Configuring local registry: ${LOCAL_REGISTRY_URL}`); @@ -48,7 +48,7 @@ const withLocalRegistry = async (packageManager: JsPackageManager, action: () => error = e; } finally { console.log(`📦 Restoring registry: ${prevUrl}`); - packageManager.setRegistryURL(prevUrl); + await packageManager.setRegistryURL(prevUrl); if (error) { // eslint-disable-next-line no-unsafe-finally @@ -80,7 +80,7 @@ const addStorybook = async ({ const packageManager = JsPackageManagerFactory.getPackageManager({}, tmpDir); if (localRegistry) { await withLocalRegistry(packageManager, async () => { - packageManager.addPackageResolutions(storybookVersions); + await packageManager.addPackageResolutions(storybookVersions); await sbInit(tmpDir, flags, debug); }); diff --git a/scripts/tasks/sandbox-parts.ts b/scripts/tasks/sandbox-parts.ts index c26a6f24b8c7..f20a6fe17b31 100644 --- a/scripts/tasks/sandbox-parts.ts +++ b/scripts/tasks/sandbox-parts.ts @@ -332,7 +332,7 @@ async function linkPackageStories( ); } -function addExtraDependencies({ +async function addExtraDependencies({ cwd, dryRun, debug, @@ -350,7 +350,7 @@ function addExtraDependencies({ if (debug) logger.log('🎁 Adding extra deps', extraDeps); if (!dryRun) { const packageManager = JsPackageManagerFactory.getPackageManager({}, cwd); - packageManager.addDependencies({ installAsDevDependencies: true }, extraDeps); + await packageManager.addDependencies({ installAsDevDependencies: true }, extraDeps); } } @@ -481,7 +481,7 @@ export const addStories: Task['run'] = async ( } // Some addon stories require extra dependencies - addExtraDependencies({ cwd, dryRun, debug }); + await addExtraDependencies({ cwd, dryRun, debug }); await writeConfig(mainConfig); };