storybook/lib/cli/src/helpers.ts
Jonathan Share 97c3a2f16f Remove storyFormat option from CLI
This option doesn't appear to do anything at all anymore and as-such should be removed from the codebase.

Note: This is a breaking change to the CLI, while the parameter might not have done anything useful, it's removal could break scripts that were trying to set it.

I was a little brutal on the test cases here, I think I need to re-add one after some sleep
2021-10-03 21:13:07 +00:00

221 lines
6.4 KiB
TypeScript

/* eslint-disable no-param-reassign */
import path from 'path';
import fs from 'fs';
import fse from 'fs-extra';
import chalk from 'chalk';
import { satisfies } from '@storybook/semver';
import stripJsonComments from 'strip-json-comments';
import { SupportedFrameworks, SupportedLanguage } from './project_types';
import { JsPackageManager, PackageJson, PackageJsonWithDepsAndDevDeps } from './js-package-manager';
const logger = console;
export function getBowerJson() {
const bowerJsonPath = path.resolve('bower.json');
if (!fs.existsSync(bowerJsonPath)) {
return false;
}
const jsonContent = fs.readFileSync(bowerJsonPath, 'utf8');
return JSON.parse(jsonContent);
}
export function readFileAsJson(jsonPath: string, allowComments?: boolean) {
const filePath = path.resolve(jsonPath);
if (!fs.existsSync(filePath)) {
return false;
}
const fileContent = fs.readFileSync(filePath, 'utf8');
const jsonContent = allowComments ? stripJsonComments(fileContent) : fileContent;
try {
return JSON.parse(jsonContent);
} catch (e) {
logger.error(chalk.red(`Invalid json in file: ${filePath}`));
throw e;
}
}
export const writeFileAsJson = (jsonPath: string, content: unknown) => {
const filePath = path.resolve(jsonPath);
if (!fs.existsSync(filePath)) {
return false;
}
fs.writeFileSync(filePath, `${JSON.stringify(content, null, 2)}\n`);
return true;
};
export const commandLog = (message: string) => {
process.stdout.write(chalk.cyan(' • ') + message);
// Need `void` to be able to use this function in a then of a Promise<void>
return (errorMessage?: string | void, errorInfo?: string) => {
if (errorMessage) {
process.stdout.write(`. ${chalk.red('✖')}\n`);
logger.error(`\n ${chalk.red(errorMessage)}`);
if (!errorInfo) {
return;
}
const newErrorInfo = errorInfo
.split('\n')
.map((line) => ` ${chalk.dim(line)}`)
.join('\n');
logger.error(`${newErrorInfo}\n`);
return;
}
process.stdout.write(`. ${chalk.green('✓')}\n`);
};
};
export function paddedLog(message: string) {
const newMessage = message
.split('\n')
.map((line) => ` ${line}`)
.join('\n');
logger.log(newMessage);
}
export function getChars(char: string, amount: number) {
let line = '';
for (let lc = 0; lc < amount; lc += 1) {
line += char;
}
return line;
}
export function codeLog(codeLines: string[], leftPadAmount?: number) {
let maxLength = 0;
const newLines = codeLines.map((line) => {
maxLength = line.length > maxLength ? line.length : maxLength;
return line;
});
const finalResult = newLines
.map((line) => {
const rightPadAmount = maxLength - line.length;
let newLine = line + getChars(' ', rightPadAmount);
newLine = getChars(' ', leftPadAmount || 2) + chalk.inverse(` ${newLine} `);
return newLine;
})
.join('\n');
logger.log(finalResult);
}
/**
* Detect if any babel dependencies need to be added to the project
* @param {Object} packageJson The current package.json so we can inspect its contents
* @returns {Array} Contains the packages and versions that need to be installed
* @example
* const babelDependencies = await getBabelDependencies(packageManager, npmOptions, packageJson);
* // you can then spread the result when using installDependencies
* installDependencies(npmOptions, [
* `@storybook/react@${storybookVersion}`,
* ...babelDependencies,
* ]);
*/
export async function getBabelDependencies(
packageManager: JsPackageManager,
packageJson: PackageJsonWithDepsAndDevDeps
) {
const dependenciesToAdd = [];
let babelLoaderVersion = '^8.0.0-0';
const babelCoreVersion =
packageJson.dependencies['babel-core'] || packageJson.devDependencies['babel-core'];
if (!babelCoreVersion) {
if (!packageJson.dependencies['@babel/core'] && !packageJson.devDependencies['@babel/core']) {
const babelCoreInstallVersion = await packageManager.getVersion('@babel/core');
dependenciesToAdd.push(`@babel/core@${babelCoreInstallVersion}`);
}
} else {
const latestCompatibleBabelVersion = await packageManager.latestVersion(
'babel-core',
babelCoreVersion
);
// Babel 6
if (satisfies(latestCompatibleBabelVersion, '^6.0.0')) {
babelLoaderVersion = '^7.0.0';
}
}
if (!packageJson.dependencies['babel-loader'] && !packageJson.devDependencies['babel-loader']) {
const babelLoaderInstallVersion = await packageManager.getVersion(
'babel-loader',
babelLoaderVersion
);
dependenciesToAdd.push(`babel-loader@${babelLoaderInstallVersion}`);
}
return dependenciesToAdd;
}
export function addToDevDependenciesIfNotPresent(
packageJson: PackageJson,
name: string,
packageVersion: string
) {
if (!packageJson.dependencies[name] && !packageJson.devDependencies[name]) {
packageJson.devDependencies[name] = packageVersion;
}
}
export function copyTemplate(templateRoot: string) {
const templateDir = path.resolve(templateRoot, `template-csf/`);
if (!fs.existsSync(templateDir)) {
throw new Error(`Couldn't find template dir`);
}
fse.copySync(templateDir, '.', { overwrite: true });
}
export function copyComponents(framework: SupportedFrameworks, language: SupportedLanguage) {
const languageFolderMapping: Record<SupportedLanguage, string> = {
javascript: 'js',
typescript: 'ts',
};
const componentsPath = () => {
const frameworkPath = `frameworks/${framework}`;
const languageSpecific = path.resolve(
__dirname,
`${frameworkPath}/${languageFolderMapping[language]}`
);
if (fse.existsSync(languageSpecific)) {
return languageSpecific;
}
const jsFallback = path.resolve(
__dirname,
`${frameworkPath}/${languageFolderMapping.javascript}`
);
if (fse.existsSync(jsFallback)) {
return jsFallback;
}
const frameworkRootPath = path.resolve(__dirname, frameworkPath);
if (fse.existsSync(frameworkRootPath)) {
return frameworkRootPath;
}
throw new Error(`Unsupported framework: ${framework}`);
};
const targetPath = () => {
if (fse.existsSync('./src')) {
return './src/stories';
}
return './stories';
};
const destinationPath = targetPath();
fse.copySync(componentsPath(), destinationPath, { overwrite: true });
fse.copySync(path.resolve(__dirname, 'frameworks/common'), destinationPath, { overwrite: true });
}