136 lines
3.9 KiB
TypeScript
Raw Normal View History

2022-07-26 11:41:33 +02:00
/* eslint-disable no-console */
import path, { join, relative } from 'path';
import program from 'commander';
import { command } from 'execa';
import type { Options as ExecaOptions } from 'execa';
import yaml from 'js-yaml';
import pLimit from 'p-limit';
import prettyTime from 'pretty-hrtime';
2022-07-29 17:22:12 +02:00
import { copy, emptyDir, ensureDir, readFile, rename, writeFile } from 'fs-extra';
2022-07-27 11:16:08 +02:00
// @ts-ignore
import { maxConcurrentTasks } from '../utils/concurrency';
2022-07-26 11:41:33 +02:00
2022-07-27 11:16:08 +02:00
import { localizeYarnConfigFiles, setupYarn } from './utils/yarn';
import { GeneratorConfig } from './utils/types';
2022-07-29 17:22:12 +02:00
import { getStackblitzUrl, renderTemplate } from './utils/template';
2022-07-26 11:41:33 +02:00
const OUTPUT_DIRECTORY = join(__dirname, '..', '..', 'repros');
2022-07-27 16:09:29 +02:00
const BEFORE_DIR_NAME = 'before-storybook';
const AFTER_DIR_NAME = 'after-storybook';
2022-07-26 11:41:33 +02:00
const addStorybook = async (baseDir: string) => {
2022-07-27 11:16:08 +02:00
const beforeDir = join(baseDir, BEFORE_DIR_NAME);
const afterDir = join(baseDir, AFTER_DIR_NAME);
2022-07-26 11:41:33 +02:00
const tmpDir = join(baseDir, '.tmp');
await ensureDir(tmpDir);
await emptyDir(tmpDir);
await copy(beforeDir, tmpDir);
const sbCliBinaryPath = join(__dirname, `../../code/lib/cli/bin/index.js`);
await runCommand(`${sbCliBinaryPath} init`, {
cwd: tmpDir,
2022-07-27 11:16:08 +02:00
env: {
STORYBOOK_DISABLE_TELEMETRY: 'true',
},
2022-07-26 11:41:33 +02:00
});
await rename(tmpDir, afterDir);
};
2022-07-27 11:16:08 +02:00
export const runCommand = async (script: string, options: ExecaOptions) => {
const shouldDebug = !!process.env.DEBUG;
2022-07-26 11:41:33 +02:00
if (shouldDebug) {
2022-07-26 13:14:22 +02:00
console.log(`Running command: ${script}`);
2022-07-26 11:41:33 +02:00
}
2022-07-26 13:14:22 +02:00
return command(script, { stdout: shouldDebug ? 'inherit' : 'ignore', ...options });
2022-07-26 11:41:33 +02:00
};
2022-07-29 17:22:12 +02:00
const addDocumentation = async (
baseDir: string,
{ name, dirName }: { name: string; dirName: string }
) => {
const afterDir = join(baseDir, AFTER_DIR_NAME);
const stackblitzConfigPath = join(__dirname, 'templates', '.stackblitzrc');
const readmePath = join(__dirname, 'templates', 'item.ejs');
await copy(stackblitzConfigPath, join(afterDir, '.stackblitzrc'));
const stackblitzUrl = getStackblitzUrl(dirName);
const contents = await renderTemplate(readmePath, {
name,
stackblitzUrl,
});
await writeFile(join(afterDir, 'README.md'), contents);
};
const runGenerators = async (generators: (GeneratorConfig & { dirName: string })[]) => {
2022-07-26 11:41:33 +02:00
console.log(`🤹‍♂️ Generating repros with a concurrency of ${maxConcurrentTasks}`);
const limit = pLimit(maxConcurrentTasks);
return Promise.all(
2022-07-29 17:22:12 +02:00
generators.map(({ dirName, name, script }) =>
2022-07-26 11:41:33 +02:00
limit(async () => {
const time = process.hrtime();
console.log(`🧬 generating ${name}`);
2022-07-29 17:22:12 +02:00
const baseDir = join(OUTPUT_DIRECTORY, dirName);
2022-07-27 11:16:08 +02:00
const beforeDir = join(baseDir, BEFORE_DIR_NAME);
2022-07-26 11:41:33 +02:00
2022-07-27 11:16:08 +02:00
await emptyDir(baseDir);
await ensureDir(beforeDir);
2022-07-26 11:41:33 +02:00
2022-07-27 11:16:08 +02:00
await setupYarn({ cwd: baseDir });
2022-07-26 13:14:22 +02:00
2022-07-26 11:41:33 +02:00
await runCommand(script, { cwd: beforeDir });
2022-07-27 11:16:08 +02:00
await localizeYarnConfigFiles(baseDir, beforeDir);
2022-07-26 11:41:33 +02:00
await addStorybook(baseDir);
2022-07-29 17:22:12 +02:00
await addDocumentation(baseDir, { name, dirName });
2022-07-26 11:41:33 +02:00
console.log(
2022-07-29 17:22:12 +02:00
`✅ Created ${dirName} in ./${relative(
process.cwd(),
baseDir
)} successfully in ${prettyTime(process.hrtime(time))}`
2022-07-26 11:41:33 +02:00
);
})
)
);
};
const generate = async ({ config }: { config: string }) => {
const configContents = await readFile(config, 'utf8');
2022-07-29 17:22:12 +02:00
const data: Record<string, GeneratorConfig> = yaml.load(configContents);
2022-07-26 11:41:33 +02:00
runGenerators(
2022-07-29 17:22:12 +02:00
Object.entries(data).map(([dirName, configuration]) => ({
dirName,
...configuration,
2022-07-26 11:41:33 +02:00
}))
);
};
program
.description('Create a reproduction from a set of possible templates')
.option(
'-c --config <config>',
'Choose a custom configuration file (.yml format)',
path.join(__dirname, 'repro-config.yml')
);
program.parse(process.argv);
const options = program.opts() as { config: string };
generate(options).catch((e) => {
2022-07-27 11:16:08 +02:00
console.trace(e);
2022-07-26 11:41:33 +02:00
process.exit(1);
});