Setup-CLI
Quickly setup everything, or custom it in simple way
GIF made by VHS
✨ Features
ESM
only, pnpm
only.
-
Simple: Use in simple way.
-
Simple: Build in simple way.
-
Simple: Custom in simple way.
🪄 Usage
Install
pnpm add @norah1to/setup-cli -g
Help
setup-cli help
create
Create a app
setup-cli create
repo
Opt repo
# Recommend exec "setup-cli update" after do this
setup-cli repo <add|set|rm>
Show repo list
setup-cli repo list
update
Synchronize the local repo with the remote
setup-cli update
inject
Inject anything into current project
setup-cli inject
reset
Reset the CLI to the state it was first installed in
setup-cli reset
🚀 Custom
We provide the default templates.
You can build your own too.
Every template has files
,hook
two folder.
└───base-node
├───files
├───hook
└───...
index.js
in hook
must export a function by default, and must be an ESM
module.
It should be of type BaseHook
or InjectHook
, depending on the template type, so you should write hook like this.
// base-node/hook/index.js
/**
* @type {import("@norah1to/setup-cli").BaseHook}
*/
const hook = () => ({ ... });
export default hook;
Third-party deps
Of course, you can also use third-party dependencies through packaging tools.
I provide a template base on rollup
that uses a third-party dependency.
Base
Base templates must start with base-
like base-node
.
Its must contain metadata for Inject to use:
const hook = () => ({ ... });
// metadata
hook.meta = { ... }
export default hook;
Inject
Inject templates must start with inject-
like inject-lint
.
It does not need to additionally export metadata (temporarily)
⚙️ Hooks
Hooks is the core of custom templates, it provides many life cycle hooks.
You can customize the injection logic in it.
The following are described in order of execution.
All hooks are optional.
beforeGenerate
Will be executed at the beginning, usually used to collect requirements.
beforeMerge(options)
It will be executed when each file of the current template is about to be merged.
Used to make some preparations before merging a file.
options
Type: object
srcDir
Type: DirInfo
A virtual file tree rooted in the files
folder in the template.
destDir
Type: DirInfo
Virtual file tree of target folder.
src
Type: FileInfo
The virtual file object to be merged into the current template.
dest
Type: FileInfo
A virtual file object with the same name in the target directory.
onMerging(options): content
Used to overwrite the merge logic for files with the same name.
By default, files with the same name in the target path will be overwritten, but sometimes they need to be merged instead of overwritten.
For example when you need to merge package.json
:
onMerging({ srcDir, destDir, src, dest }) {
if (src === srcDir.get('package.json'))
return JSON.stringify(
dpMergePackageJson(dest.getJson(), src.getJson())
);
return src.getContent();
},
return
Type: string
Required: true
options
Type: object
srcDir
Type: DirInfo
destDir
Type: DirInfo
src
Type: FileInfo
dest
Type: FileInfo
afterMerge(options)
Execute after the current file is merged, the rest is the same as beforeMerge
afterGenerate(options)
Execute after the virtual file tree is generated.
options
Type: object
targetDir
Type: DirInfo
Virtual file tree of target folder.
afterOutput
Execute after outputting the virtual file tree to disk with formatting.
Usually used to execute some installation scripts, for example you need to customize eslint
configuration:
async afterOutput() {
const cwd = process.cwd();
await $.cd(__dir_target_root__);
$.verbose = true;
await $`pnpm create @eslint/config`;
}
Objects
DirInfo
Used to store folder information, won't modify the original file or folder on disk.
name
Type: string
Folder name.
pathname
Type: string
The full absolute path of the folder.
dirname
Type: string
The folder's parent folder path.
parent
Type: DirInfo
Parent folder information, null
if root.
isDir
Type: boolean
Whether it is a folder, used to determine the type when fuzzy searching in the tree.
exist
Type: boolean
Does the folder exist.
has(pathname: string, options)
Find if a file or folder exists in the tree.
/*
* └───foo
* ├───index.ts
* └───bar
* └───deep.ts
*/
const dir = new DirInfo({ pathname: '/foo' });
dir.has('/index.ts'); // true
dir.has('/bar'); // true
dir.has('/bar/deep.ts'); // true
dir.has('/bar/deep.ts', { type: 'dir' }); // false
pathname
Required: true
Relative path relative to folder.
options.type
Required: false
Type: "file"
,"dir"
get(pathname: string, options): DirInfo | FileInfo | undefined
Get item by path, it will return undefined
if not exist.
pathname
Same as above.
options.type
Same as above.
getMap(options): Record<string, DirInfo | FileInfo>
Get the child mapping table under the current folder, the key
is the file name or folder name.
options.type
Same as above.
ensure(pathname: string, options)
Make sure a file or folder exists, create it if it does not exist.
pathname
Same as above.
options.type
Same as above, but Required.
FileInfo
Used to store file information, won't modify the original file on disk.
dirname
Type: string
The directory where the file is located
filename
Type: string
Filename, including extension.
pathname
Type: string
The full absolute path of the file.
ext
Type: string
File extension.
exist
Type: boolean
Does the file exist.
parent
Type: DirInfo
Parent folder information.
isDir
Type: boolean
Whether it is a folder, used to determine the type when fuzzy searching in the tree.
setContent(content: string)
Set the content of the file.
getContent()
Return the content of the file, return undefined
when the file does not exist and no content is added.
getJson()
json
and yaml
files will parse their contents, and this method returns the parsed result object.
Returns undefined
if the file is empty, does not exist, or is malformed.