- 🎩 Template engine You can use template strings in text files, file names, and folder names.
- 💄 Highly customizable Can change caveat text, add extra command-line options.
Let's create create-greet
package in five steps.
# Any of the following commands will work
npx create-create-x@latest greet # recommended
npm init create-x greet
yarn create create-x greet
pnpm create create-x greet
bun create create-x greet
You will then be asked about your project.
cd create-greet
Then you can see the templates/default
folder where the actual template files go.
Note that .gitignore
files should be named gitignore
to avoid being ignored on publishing.
Run npm run build
or yarn build
to transpile TypeScript code into JavaScript. If you chose the default template, this step is not necessary.
Run npm publish
or yarn publish
to publish your create-greet
app to npm.
# Any of the following commands will work
npx create-greet ohayo
npm init greet ohayo
yarn create greet ohayo
pnpm create greet ohayo
bun create greet ohayo
Edit files inside templates/default
. Every file name, directory name, and a text file will be processed through Handlebars template engine to replace all template strings with the respective value.
Built-in variables are:
{{name}}
package name (e.g.ohayo
){{description}}
package description{{author}}
author name (e.g.John Doe
){{email}}
author email (e.g.john@example.com
){{contact}}
author name formatted with{{name}} <{{email}}>
. If email is missing, simply{{name}}
{{license}}
package license (e.g.MIT
){{year}}
current year (e.g.2021
){{template}} selected template name (e.g.
typescript`){{packageManager}} package manager (e.g.
yarn`)
Creates a new directory in the location defined by templateRoot
. It can be accessed via --template
flag (e.g. create-something <name> --template <template>
).
You might want to set promptForTemplate
to true
to explicitly ask the user to choose a template during the initialization phase. If promptForTemplate
is false
, which is the default behavior, default
template will be used unless the user explicitly selects a template via --template
cli flag.
In the following example, we assume that the variable name
is create-react-app
.
Convert text to UPPERCASE.
{{upper name}}
becomes CREATE-REACT-APP
.
Convert text to lowercase.
{{lower name}}
becomes create-react-app
.
Convert text to CapitalCase.
{{capital name}}
becomesCreateReactApp
{{capital name space=true}}
becomesCreate React App
.
Convert text to camelCase.
{{camel name}}
becomes createReactApp
.
Convert text to snake_case.
{{snake name}}
becomes create_react_app
.
Convert text to kebab-case.
{{kebab name}}
becomes create-react-app
.
Replace all word separators with single space.
{{space name}}
becomes create react app
Generates unique UUID string.
{{uuid}} // => a5df7100-da46-47a6-907e-afe861f48b39
{{upper (uuid)}} // => A5DF7100-DA46-47A6-907E-AFE861F48B39
The app configuration can be found in src/cli.js
(or src/cli.ts
if you choose the typescript
template).
import { resolve } from "path";
import { create } from "create-create-x";
create("create-greet", {
templateRoot: resolve(__dirname, "..", "templates"),
extra: {
language: {
type: "input",
describe: "greeting language",
default: "en",
prompt: "if-no-arg",
},
},
modifyName: (name) => `package-prefix-${name}`,
after: async ({ installNpmPackage }) => {
console.log("Installing additional packages");
await installNpmPackage("chalk");
},
caveat: `Your app has been created successfully!`,
});
templateRoot
is set to path.resolve(__dirname, '../templates')
. You can change this to any location you like.
(name: string) => string | Promise<string>
Modify name
property.
{
modifyName: (name) => (name.startsWith("create-") ? name : `create-${name}`);
}
object | undefined
Additional questions can be defined. These options will be available as CLI flags, interactive questions, and template strings. In the example above, --language
flag and the {{language}}
template string will be enabled in the app.
All possible options can be found in the yargs-interactive documentation.
Default value for a package description.
Default value for a package author.
Default value for a package author email.
Default value for a template.
Default value for license.
Default value for package manager. npm
, yarn
and pnpm
are available. undefined
to auto detect package manager.
Interactively asks users for a package description.
Interactively asks users for a package author.
Interactively asks users for a package author email.
Interactively asks users to select a template. If there are no multiple templates in the templates
directory, it won't display a prompt anyways.
Even if promptForTemplate
is set to false
, users can still specify a template via a command line flag --template <template>
.
create-something <name> --template <template>
Interactively asks users for a package license.
Interactively asks users for a package manager to use for installing packages from npm.
Skip initializing a git repository at a creation time.
Skip installing package dependencies at a creation time.
(options: AfterHookOptions) => void
Define after-hook script to be executed right after the initialization process.
string | ((options: AfterHookOptions) => string | void) | undefined
The caveat message will be shown after the entire process is completed.
create("create-greet", {
caveat: "Happy coding!",
});
create("create-greet", {
caveat: ({ answers }) => `Run -> cd ${answers.name} && make`,
});
create("create-greet", {
extra: {
plugin: {
type: "input",
describe: "plugin to be used in your project",
default: "some-plugin",
prompt: "if-no-arg",
},
},
after: async ({ installNpmPackage, answers }) => {
const plugin = answers.plugin;
console.log(`Installing additional package: ${plugin}`);
await installNpmPackage(plugin);
},
caveat: ({ packageDir }) => {
console.log("Next step:");
console.log(`cd ${packageDir} && npm start`);
},
});
{
// variables
name: string; // e.g. "create-greet"
template: string; // e.g. "default"
packageDir: string; // e.g. "/path/to/ohayo"
templateDir: string; // e.g. "/path/to/create-greet/templates/default"
year: number; // e.g. 2020
answers: {
name: string; // package name passed through `modifyName`
description: string; // description
author: string; // e.g. "John Doe"
email: string; // e.g. "john@example.com"
contact: string; // e.g. "John Doe <john@example.com>"
license: string; // e.g. "MIT"
[key: string]: string | number | boolean | any[]; // any values defined in the `extra` field.
};
// helper functions
run: (command: string, options?: CommonOptions<string>) => ExecaChildProcess<string>; // execute shell commands in the package dir
installNpmPackage: (packageName: string | [string], isDev?: boolean) => Promise<void>; // install npm package. uses package manager specified by --node-pm CLI param (default: auto-detect)
}
PRs are always welcomed.
Thanks goes to these wonderful people (emoji key):
uetchy 💻 📖 |
Shinyu Murakami 💻 |
Masayoshi Takahashi 💻 |
Alexander Liu 💻 |
Vilja 💻 |
Lucas Colombo 💻 |
Loïc Chen 💻 |
This project follows the all-contributors specification. Contributions of any kind welcome!