Skip to content

Flexibility to build your application templates

License

Notifications You must be signed in to change notification settings

NoraH1to/setup-cli

Repository files navigation

Setup-CLI

Quickly setup everything, or custom it in simple way

test status Coverage Status npm


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.