Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CLI: Sandbox script should use current version to init #25560

Merged
merged 4 commits into from
Jan 12, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion code/lib/cli/src/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ command('sandbox [filterValue]')
.option('-b --branch <branch>', 'Define the branch to download from', 'next')
JReinhold marked this conversation as resolved.
Show resolved Hide resolved
.option('--no-init', 'Whether to download a template without an initialized Storybook', false)
.action((filterValue, options) =>
sandbox({ filterValue, ...options }).catch((e) => {
sandbox({ filterValue, ...options }, pkg).catch((e) => {
logger.error(e);
process.exit(1);
})
Expand Down
2 changes: 1 addition & 1 deletion code/lib/cli/src/initiate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ const projectTypeInquirer = async (
process.exit(0);
};

async function doInitiate(
export async function doInitiate(
options: CommandOptions,
pkg: PackageJson
): Promise<
Expand Down
100 changes: 80 additions & 20 deletions code/lib/cli/src/sandbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,13 @@ import { downloadTemplate } from 'giget';

import { existsSync, readdir } from 'fs-extra';
import invariant from 'tiny-invariant';
import { lt, prerelease } from 'semver';
import type { Template, TemplateKey } from './sandbox-templates';
import { allTemplates as TEMPLATES } from './sandbox-templates';
import type { PackageJson, PackageManagerName } from './js-package-manager';
import { JsPackageManagerFactory } from './js-package-manager';
import versions from './versions';
import { doInitiate, initiate } from './initiate';

const logger = console;

Expand All @@ -17,20 +22,59 @@ interface SandboxOptions {
output?: string;
branch?: string;
init?: boolean;
packageManager: PackageManagerName;
}
type Choice = keyof typeof TEMPLATES;

const toChoices = (c: Choice): prompts.Choice => ({ title: TEMPLATES[c].name, value: c });

export const sandbox = async ({
output: outputDirectory,
filterValue,
branch,
init,
}: SandboxOptions) => {
export const sandbox = async (
{ output: outputDirectory, filterValue, branch, init, ...options }: SandboxOptions,
pkg: PackageJson
) => {
// Either get a direct match when users pass a template id, or filter through all templates
let selectedConfig: Template | undefined = TEMPLATES[filterValue as TemplateKey];
let selectedTemplate: Choice | null = selectedConfig ? (filterValue as TemplateKey) : null;
let templateId: Choice | null = selectedConfig ? (filterValue as TemplateKey) : null;

const { packageManager: pkgMgr } = options;

const packageManager = JsPackageManagerFactory.getPackageManager({
force: pkgMgr,
});
const latestVersion = await packageManager.latestVersion('@storybook/cli');
const currentVersion = versions['@storybook/cli'];
const isPrerelease = prerelease(currentVersion);
const isOutdated = lt(currentVersion, latestVersion);
const borderColor = isOutdated ? '#FC521F' : '#F1618C';

const downloadType =
!isOutdated && !isPrerelease && init ? 'after-storybook' : 'before-storybook';

const messages = {
welcome: `Creating a Storybook ${chalk.bold(currentVersion)} sandbox..`,
notLatest: chalk.red(dedent`
This version is behind the latest release, which is: ${chalk.bold(latestVersion)}!
You likely ran the init command through npx, which can use a locally cached version, to get the latest please run:
${chalk.bold('npx storybook@latest sandbox')}

You may want to CTRL+C to stop, and run with the latest version instead.
`),
longInitTime: chalk.yellow(
'The creation of the sandbox will take longer, because we will need to run init.'
),
prerelease: chalk.yellow('This is a pre-release version.'),
};

logger.log(
boxen(
[messages.welcome]
.concat(isOutdated && !isPrerelease ? [messages.notLatest] : [])
.concat(init && (isOutdated || isPrerelease) ? [messages.longInitTime] : [])
.concat(isPrerelease ? [messages.prerelease] : [])
.join('\n'),
{ borderStyle: 'round', padding: 1, borderColor }
)
);

if (!selectedConfig) {
const filterRegex = new RegExp(`^${filterValue || ''}`, 'i');
Expand Down Expand Up @@ -79,7 +123,7 @@ export const sandbox = async ({
}

if (choices.length === 1) {
[selectedTemplate] = choices;
[templateId] = choices;
} else {
logger.info(
boxen(
Expand All @@ -97,24 +141,24 @@ export const sandbox = async ({
)
);

selectedTemplate = await promptSelectedTemplate(choices);
templateId = await promptSelectedTemplate(choices);
}

const hasSelectedTemplate = !!(selectedTemplate ?? null);
const hasSelectedTemplate = !!(templateId ?? null);
if (!hasSelectedTemplate) {
logger.error('Somehow we got no templates. Please rerun this command!');
return;
}

selectedConfig = selectedTemplate ? TEMPLATES[selectedTemplate] : undefined;
selectedConfig = templateId ? TEMPLATES[templateId] : undefined;

if (!selectedConfig) {
throw new Error('🚨 Sandbox: please specify a valid template type');
}
}

let selectedDirectory = outputDirectory;
const outputDirectoryName = outputDirectory || selectedTemplate;
const outputDirectoryName = outputDirectory || templateId;
if (selectedDirectory && existsSync(`${selectedDirectory}`)) {
logger.info(`⚠️ ${selectedDirectory} already exists! Overwriting...`);
}
Expand Down Expand Up @@ -151,20 +195,33 @@ export const sandbox = async ({

logger.log('📦 Downloading sandbox template...');
try {
const templateType = init ? 'after-storybook' : 'before-storybook';
// Download the sandbox based on subfolder "after-storybook" and selected branch
const gitPath = `github:storybookjs/sandboxes/${selectedTemplate}/${templateType}#${branch}`;
const gitPath = `github:storybookjs/sandboxes/${templateId}/${downloadType}#${branch}`;
await downloadTemplate(gitPath, {
force: true,
dir: templateDestination,
});
// throw an error if templateDestination is an empty directory using fs-extra
if ((await readdir(templateDestination)).length === 0) {
throw new Error(
dedent`Template downloaded from ${chalk.blue(gitPath)} is empty.
Are you use it exists? Or did you want to set ${chalk.yellow(
selectedTemplate
)} to inDevelopment first?`
const selected = chalk.yellow(templateId);
throw new Error(dedent`
Template downloaded from ${chalk.blue(gitPath)} is empty.
Are you use it exists? Or did you want to set ${selected} to inDevelopment first?
`);
}

// when user wanted an sandbox that has been initiated, but force-downloaded the before-storybook directory
// then we need to initiate the sandbox
// this is to ensure we DO get the latest version of the template (output of the generator), but we initialize using the version of storybook that the CLI is.
// we warned the user about the fact they are running an old version of storybook
// we warned the user the sandbox step would take longer
if ((isOutdated || isPrerelease) && init) {
// we run doInitiate, instead of initiate, to avoid sending this init event to telemetry, because it's not a real world project
await doInitiate(
{
...options,
},
pkg
);
}
} catch (err) {
Expand All @@ -173,7 +230,10 @@ export const sandbox = async ({
}

const initMessage = init
? chalk.yellow(`yarn install\nyarn storybook`)
? chalk.yellow(dedent`
yarn install
yarn storybook
`)
: `Recreate your setup, then ${chalk.yellow(`npx storybook@latest init`)}`;

logger.info(
Expand Down
Loading