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';
|
2022-07-29 16:38:03 +02:00
|
|
|
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);
|
|
|
|
});
|