118 lines
3.2 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-27 16:11:04 +02:00
import { copy, emptyDir, ensureDir, readFile, rename } 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-26 11:41:33 +02:00
type DataEntry = {
script: string;
};
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
};
const runGenerators = async (generators: GeneratorConfig[]) => {
console.log(`🤹‍♂️ Generating repros with a concurrency of ${maxConcurrentTasks}`);
const limit = pLimit(maxConcurrentTasks);
return Promise.all(
generators.map(({ name, script }) =>
limit(async () => {
const time = process.hrtime();
console.log(`🧬 generating ${name}`);
const baseDir = join(OUTPUT_DIRECTORY, name);
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);
console.log(
`✅ Created ${name} in ./${relative(process.cwd(), baseDir)} successfully in ${prettyTime(
process.hrtime(time)
)}`
);
})
)
);
};
const generate = async ({ config }: { config: string }) => {
const configContents = await readFile(config, 'utf8');
const data: Record<string, DataEntry> = yaml.load(configContents);
runGenerators(
Object.entries(data).map(([name, configuration]) => ({
name,
script: configuration.script,
}))
);
};
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);
});