2022-07-26 11:41:33 +02:00
|
|
|
/* eslint-disable no-console */
|
2022-10-03 17:45:48 +11:00
|
|
|
import { join, relative } from 'path';
|
2022-07-26 11:41:33 +02:00
|
|
|
import { command } from 'execa';
|
|
|
|
import type { Options as ExecaOptions } from 'execa';
|
|
|
|
import pLimit from 'p-limit';
|
|
|
|
import prettyTime from 'pretty-hrtime';
|
2022-08-14 17:11:47 +08:00
|
|
|
import { copy, emptyDir, ensureDir, rename, writeFile } from 'fs-extra';
|
2022-08-16 12:16:39 +02:00
|
|
|
import { program } from 'commander';
|
2022-08-23 08:22:16 +08:00
|
|
|
import { AbortController } from 'node-abort-controller';
|
|
|
|
|
2022-08-14 17:11:47 +08:00
|
|
|
import reproTemplates from '../../code/lib/cli/src/repro-templates';
|
2022-08-16 12:16:39 +02:00
|
|
|
import storybookVersions from '../../code/lib/cli/src/versions';
|
|
|
|
import { JsPackageManagerFactory } from '../../code/lib/cli/src/js-package-manager/JsPackageManagerFactory';
|
2022-08-14 17:11:47 +08:00
|
|
|
|
2022-10-04 20:54:40 +11:00
|
|
|
import { maxConcurrentTasks } from '../utils/maxConcurrentTasks';
|
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-29 17:22:12 +02:00
|
|
|
import { getStackblitzUrl, renderTemplate } from './utils/template';
|
2022-08-17 18:32:31 +08:00
|
|
|
import { JsPackageManager } from '../../code/lib/cli/src/js-package-manager';
|
2022-10-05 22:55:58 +11:00
|
|
|
import { runRegistry } from '../tasks/run-registry';
|
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
|
|
|
|
2022-08-17 18:32:31 +08:00
|
|
|
const sbInit = async (cwd: string) => {
|
|
|
|
const sbCliBinaryPath = join(__dirname, `../../code/lib/cli/bin/index.js`);
|
|
|
|
console.log(`🎁 Installing storybook`);
|
|
|
|
const env = { STORYBOOK_DISABLE_TELEMETRY: 'true' };
|
2022-08-19 09:49:27 -06:00
|
|
|
await runCommand(`${sbCliBinaryPath} init --yes`, { cwd, env });
|
2022-08-17 18:32:31 +08:00
|
|
|
};
|
|
|
|
|
2022-08-22 17:40:30 +08:00
|
|
|
const LOCAL_REGISTRY_URL = 'http://localhost:6001';
|
2022-08-17 18:32:31 +08:00
|
|
|
const withLocalRegistry = async (packageManager: JsPackageManager, action: () => Promise<void>) => {
|
|
|
|
const prevUrl = packageManager.getRegistryURL();
|
|
|
|
try {
|
|
|
|
console.log(`📦 Configuring local registry: ${LOCAL_REGISTRY_URL}`);
|
|
|
|
packageManager.setRegistryURL(LOCAL_REGISTRY_URL);
|
|
|
|
await action();
|
|
|
|
} finally {
|
|
|
|
console.log(`📦 Restoring registry: ${prevUrl}`);
|
|
|
|
packageManager.setRegistryURL(prevUrl);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const addStorybook = async (baseDir: string, localRegistry: boolean) => {
|
2022-07-27 11:16:08 +02:00
|
|
|
const beforeDir = join(baseDir, BEFORE_DIR_NAME);
|
|
|
|
const afterDir = join(baseDir, AFTER_DIR_NAME);
|
2022-07-29 17:38:06 +02:00
|
|
|
const tmpDir = join(baseDir, 'tmp');
|
2022-07-26 11:41:33 +02:00
|
|
|
|
|
|
|
await ensureDir(tmpDir);
|
|
|
|
await emptyDir(tmpDir);
|
|
|
|
|
|
|
|
await copy(beforeDir, tmpDir);
|
|
|
|
|
2022-10-10 19:34:52 -04:00
|
|
|
const packageManager = JsPackageManagerFactory.getPackageManager({}, tmpDir);
|
2022-08-17 18:32:31 +08:00
|
|
|
if (localRegistry) {
|
|
|
|
await withLocalRegistry(packageManager, async () => {
|
|
|
|
packageManager.addPackageResolutions(storybookVersions);
|
2022-07-26 11:41:33 +02:00
|
|
|
|
2022-08-17 18:32:31 +08:00
|
|
|
await sbInit(tmpDir);
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
await sbInit(tmpDir);
|
|
|
|
}
|
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);
|
|
|
|
};
|
|
|
|
|
2022-08-16 12:16:39 +02:00
|
|
|
const runGenerators = async (
|
|
|
|
generators: (GeneratorConfig & { dirName: string })[],
|
|
|
|
localRegistry = true
|
|
|
|
) => {
|
2022-07-26 11:41:33 +02:00
|
|
|
console.log(`🤹♂️ Generating repros with a concurrency of ${maxConcurrentTasks}`);
|
|
|
|
|
|
|
|
const limit = pLimit(maxConcurrentTasks);
|
|
|
|
|
2022-08-23 17:45:00 +08:00
|
|
|
let controller: AbortController;
|
|
|
|
if (localRegistry) {
|
2022-09-06 22:40:59 -04:00
|
|
|
// @ts-expect-error (Converted from ts-ignore)
|
2022-08-23 17:45:00 +08:00
|
|
|
await publish.run();
|
|
|
|
console.log(`⚙️ Starting local registry: ${LOCAL_REGISTRY_URL}`);
|
2022-10-03 17:29:14 +11:00
|
|
|
controller = await runRegistry({ debug: true });
|
2022-08-23 17:45:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
await 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-08-17 18:32:31 +08:00
|
|
|
await addStorybook(baseDir, localRegistry);
|
2022-07-26 11:41:33 +02:00
|
|
|
|
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
|
|
|
);
|
|
|
|
})
|
|
|
|
)
|
|
|
|
);
|
2022-08-23 17:45:00 +08:00
|
|
|
|
|
|
|
if (controller) {
|
|
|
|
console.log(`🛑 Stopping local registry: ${LOCAL_REGISTRY_URL}`);
|
|
|
|
controller.abort();
|
|
|
|
console.log(`✅ Stopped`);
|
|
|
|
}
|
2022-08-23 17:46:17 +08:00
|
|
|
|
|
|
|
// FIXME: Kill dangling processes. For some reason in CI,
|
|
|
|
// the abort signal gets executed but the child process kill
|
|
|
|
// does not succeed?!?
|
|
|
|
process.exit(0);
|
2022-07-26 11:41:33 +02:00
|
|
|
};
|
|
|
|
|
2022-08-16 12:16:39 +02:00
|
|
|
const generate = async ({
|
|
|
|
template,
|
|
|
|
localRegistry,
|
|
|
|
}: {
|
|
|
|
template?: string;
|
|
|
|
localRegistry?: boolean;
|
|
|
|
}) => {
|
|
|
|
const generatorConfigs = Object.entries(reproTemplates)
|
|
|
|
.map(([dirName, configuration]) => ({
|
2022-07-29 17:22:12 +02:00
|
|
|
dirName,
|
|
|
|
...configuration,
|
2022-07-26 11:41:33 +02:00
|
|
|
}))
|
2022-08-16 12:16:39 +02:00
|
|
|
.filter(({ dirName }) => {
|
|
|
|
if (template) {
|
|
|
|
return dirName === template;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
});
|
2022-07-26 11:41:33 +02:00
|
|
|
|
2022-08-16 12:16:39 +02:00
|
|
|
runGenerators(generatorConfigs, localRegistry);
|
|
|
|
};
|
2022-07-26 11:41:33 +02:00
|
|
|
|
2022-08-16 12:16:39 +02:00
|
|
|
program
|
|
|
|
.description('Create a reproduction from a set of possible templates')
|
|
|
|
.option('--template <template>', 'Create a single template')
|
|
|
|
.option('--local-registry', 'Use local registry', false)
|
|
|
|
.action((options) => {
|
|
|
|
generate(options).catch((e) => {
|
|
|
|
console.trace(e);
|
|
|
|
process.exit(1);
|
|
|
|
});
|
|
|
|
})
|
|
|
|
.parse(process.argv);
|