Skip to content

Commit

Permalink
feat(create-rsbuild): allow to create via CLI options (#2903)
Browse files Browse the repository at this point in the history
  • Loading branch information
chenjiahan authored Jul 13, 2024
1 parent 614343b commit 29a8f2b
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 42 deletions.
2 changes: 2 additions & 0 deletions packages/create-rsbuild/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@
},
"devDependencies": {
"@clack/prompts": "^0.7.0",
"@types/minimist": "^1.2.5",
"@types/node": "18.x",
"deepmerge": "^4.3.1",
"minimist": "^1.2.8",
"rslog": "^1.2.2",
"typescript": "^5.5.2"
},
Expand Down
154 changes: 112 additions & 42 deletions packages/create-rsbuild/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
text,
} from '@clack/prompts';
import deepmerge from 'deepmerge';
import minimist from 'minimist';
import { logger } from 'rslog';

function cancelAndExit() {
Expand Down Expand Up @@ -67,49 +68,47 @@ function isEmptyDir(path: string) {
return files.length === 0 || (files.length === 1 && files[0] === '.git');
}

export async function main() {
console.log('');
logger.greet('◆ Create Rsbuild Project');

const cwd = process.cwd();
const pkgInfo = pkgFromUserAgent(process.env.npm_config_user_agent);
const pkgManager = pkgInfo ? pkgInfo.name : 'npm';
const packageRoot = path.resolve(__dirname, '..');
const packageJsonPath = path.join(packageRoot, 'package.json');
const { version } = require(packageJsonPath);

const projectName = checkCancel<string>(
await text({
message: 'Project name or path',
placeholder: 'rsbuild-project',
defaultValue: 'rsbuild-project',
validate(value) {
if (value.length === 0) {
return 'Project name is required';
}
},
}),
);

const { targetDir, packageName } = formatProjectName(projectName);
const distFolder = path.isAbsolute(targetDir)
? targetDir
: path.join(cwd, targetDir);
type Argv = {
help?: boolean;
dir?: string;
template?: string;
override?: boolean;
tools?: string | string[];
};

if (fs.existsSync(distFolder) && !isEmptyDir(distFolder)) {
const option = checkCancel<string>(
await select({
message: `"${targetDir}" is not empty, please choose:`,
options: [
{ value: 'yes', label: 'Continue and override files' },
{ value: 'no', label: 'Cancel operation' },
],
}),
);
function logHelpMessage() {
logger.log(`
Usage: create-rsbuild [options]
Options:
-h, --help display help for command
-d, --dir create project in specified directory
-t, --template specify the template to use
--tools select additional tools (biome, eslint, prettier)
--override override files in target directory
Templates:
react react-ts
vue3 vue3-ts
vue2 vue2-ts
lit lit-ts
preact preact-ts
svelte svelte-ts
solid solid-ts
vanilla vanilla-ts
`);
}

if (option === 'no') {
cancelAndExit();
}
async function getTemplate({ template }: Argv) {
if (template) {
const pair = template.split('-');
const language = pair[1] ?? 'js';
return {
framework: pair[0],
language,
};
}

const framework = checkCancel<string>(
Expand Down Expand Up @@ -138,7 +137,18 @@ export async function main() {
}),
);

const tools = checkCancel<string[]>(
return {
framework,
language,
};
}

async function getTools({ tools }: Argv) {
if (tools) {
return Array.isArray(tools) ? tools : [tools];
}

return checkCancel<string[]>(
await multiselect({
message: 'Select additional tools (press enter to continue)',
options: [
Expand All @@ -149,6 +159,66 @@ export async function main() {
required: false,
}),
);
}

export async function main() {
const argv = minimist<Argv>(process.argv.slice(2), {
alias: { h: 'help', d: 'dir', t: 'template' },
});

console.log('');
logger.greet('◆ Create Rsbuild Project');

if (argv.help) {
logHelpMessage();
return;
}

const cwd = process.cwd();
const pkgInfo = pkgFromUserAgent(process.env.npm_config_user_agent);
const pkgManager = pkgInfo ? pkgInfo.name : 'npm';
const packageRoot = path.resolve(__dirname, '..');
const packageJsonPath = path.join(packageRoot, 'package.json');
const { version } = require(packageJsonPath);

const projectName =
argv.dir ??
checkCancel<string>(
await text({
message: 'Project name or path',
placeholder: 'rsbuild-project',
defaultValue: 'rsbuild-project',
validate(value) {
if (value.length === 0) {
return 'Project name is required';
}
},
}),
);

const { targetDir, packageName } = formatProjectName(projectName);
const distFolder = path.isAbsolute(targetDir)
? targetDir
: path.join(cwd, targetDir);

if (!argv.override && fs.existsSync(distFolder) && !isEmptyDir(distFolder)) {
const option = checkCancel<string>(
await select({
message: `"${targetDir}" is not empty, please choose:`,
options: [
{ value: 'yes', label: 'Continue and override files' },
{ value: 'no', label: 'Cancel operation' },
],
}),
);

if (option === 'no') {
cancelAndExit();
}
}

const { framework, language } = await getTemplate(argv);
const tools = await getTools(argv);

const srcFolder = path.join(packageRoot, `template-${framework}-${language}`);
const commonFolder = path.join(packageRoot, 'template-common');
Expand Down
11 changes: 11 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 29a8f2b

Please sign in to comment.