Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
136 changes: 86 additions & 50 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Creating and maintaining CLI tooling and generators can be a very time consuming

- Putting generator templates as close to your project as possible
- TypeScript first to give you full flexibility over what your generators can do and how
- Using JavaScript template strings instead of hard to debug templating syntaxes like EJS or Mustache
- Using JavaScript template strings instead of hard to debug templating syntaxes like EJS or Mustache
- A functional programming approach that keeps your generators robust and easy to follow

## Installation
Expand All @@ -31,15 +31,20 @@ export interface Context extends PinionContext {
name: string
}

export const generate = (context: Context) => generator(context)
// Ask prompts here (using Inquirer) that should be available to all other generators
.then(prompt([{
type: 'input',
name: 'name',
message: 'What is the name of your app?'
}]))
// Run all *.tpl.ts generators in this folder
.then(runGenerators(__dirname))
export const generate = (context: Context) =>
generator(context)
// Ask prompts here (using Inquirer) that should be available to all other generators
.then(
prompt([
{
type: 'input',
name: 'name',
message: 'What is the name of your app?'
}
])
)
// Run all *.tpl.ts generators in this folder
.then(runGenerators(__dirname))
```

Then create a `.pinion/readme.tpl.ts` like this:
Expand All @@ -50,15 +55,15 @@ import { Context } from './pinion'

// A template to render using JavaScript template strings
const template = ({ name }: Context) =>
`# ${name}
`# ${name}

This is a readme generated by Pinion

Copyright (c) ${new Date().getFullYear()}
`

export const generate = (context: Context) => generator(context)
.then(renderTemplate(template, toFile('readme.md')))
export const generate = (context: Context) =>
generator(context).then(renderTemplate(template, toFile('readme.md')))
```

Then run
Expand All @@ -80,8 +85,7 @@ export interface Context extends PinionContext {
name: string
}

export const generate = (context: Context) => generator(context)
.then(/* operations run here */)
export const generate = (context: Context) => generator(context).then(/* operations run here */)
```

## Command line options
Expand All @@ -91,10 +95,11 @@ A generator can define command line options using [Yargs](https://yargs.js.org/)
```ts
import { Argv } from '@feathershq/pinion'

export const command = (yargs: Argv) => yargs
.usage('Usage: $0 <command> [options]')
.command('app', 'Generate my awesome app', () => {})
.help()
export const command = (yargs: Argv) =>
yargs
.usage('Usage: $0 <command> [options]')
.command('app', 'Generate my awesome app', () => {})
.help()
```

## Operations
Expand All @@ -114,14 +119,18 @@ export interface Context extends PinionContext {
name: string
}

export const generate = (context: Context) => generator(context)
.then(prompt(context => [{
type: 'input',
name: 'name',
message: 'What is the name of your app?',
// Only show prompt if there is no name
when: !context.name
}]))
export const generate = (context: Context) =>
generator(context).then(
prompt((context) => [
{
type: 'input',
name: 'name',
message: 'What is the name of your app?',
// Only show prompt if there is no name
when: !context.name
}
])
)
```

### renderTemplate
Expand All @@ -139,24 +148,28 @@ export interface Context extends PinionContext {
name: string
}

export const template = ({ name } : Context) =>
`# ${name}
export const template = ({ name }: Context) =>
`# ${name}

This is a readme generated by Pinion

Copyright (c) ${new Date().getFullYear()}
`


export const generate = (context: Context) => generator(context)
.then(prompt(context => [{
type: 'input',
name: 'name',
message: 'What is the name of your app?',
// Only show prompt if there is no --name CLI argument
when: !context.name
}]))
.then(renderTemplate(template, toFile('readme.md')))
export const generate = (context: Context) =>
generator(context)
.then(
prompt((context) => [
{
type: 'input',
name: 'name',
message: 'What is the name of your app?',
// Only show prompt if there is no --name CLI argument
when: !context.name
}
])
)
.then(renderTemplate(template, toFile('readme.md')))
```

### when
Expand Down Expand Up @@ -198,9 +211,10 @@ export const generate = (context: Context) => generator(context)
```ts
import { PinionContext, inject, before, prepend, toFile } from '@feathershq/pinion'

export const generate = (context: PinionContext) => generator(context)
.then(inject('Injected before copyright notice', before('Copyright (c)'), toFile('readme.md')))
.then(inject('Appended hello world', append(), toFile('readme.md')))
export const generate = (context: PinionContext) =>
generator(context)
.then(inject('Injected before copyright notice', before('Copyright (c)'), toFile('readme.md')))
.then(inject('Appended hello world', append(), toFile('readme.md')))
```

### copyFiles
Expand All @@ -210,8 +224,8 @@ export const generate = (context: PinionContext) => generator(context)
```ts
import { PinionContext, copyFiles, fromFile, toFile } from '@feathershq/pinion'

export const generate = (context: PinionContext) => generator(context)
.then(copyFiles(fromFile(__dirname, 'static'), toFile('.')))
export const generate = (context: PinionContext) =>
generator(context).then(copyFiles(fromFile(__dirname, 'static'), toFile('.')))
```

### writeJSON
Expand All @@ -221,8 +235,8 @@ export const generate = (context: PinionContext) => generator(context)
```ts
import { PinionContext, writeJSON, toFile } from '@feathershq/pinion'

export const generate = (context: PinionContext) => generator(context)
.then(writeJSON({ description: 'Something' }, toFile('package.json')))
export const generate = (context: PinionContext) =>
generator(context).then(writeJSON({ description: 'Something' }, toFile('package.json')))
```

### mergeJSON
Expand All @@ -231,7 +245,7 @@ export const generate = (context: PinionContext) => generator(context)

### readJSON

`readJSON(fromFile, converter, fallback?)` loads a JSON file, parses it and extends the `context` with the data returned by `converter`.
`readJSON(fromFile, converter, fallback?)` loads a JSON file, parses it and extends the `context` with the data returned by `converter`.

```ts
import { PackageJson } from 'type-fest'
Expand Down Expand Up @@ -259,9 +273,31 @@ export const generate = (context: Context) => generator(context)
```ts
import { PinionContext, generator, install } from '@feathershq/pinion'

export const generate = (context: Context) => generator(context)
.then(install(['@feathersjs/feathers']))
.then(install(['ts-node'], true))
export const generate = (context: Context) =>
generator(context)
.then(install(['@feathersjs/feathers']))
.then(install(['ts-node'], true))
```

### exec

`exec(command|context => command, (args | args[])|context => (args | args[]), options|context => options)` executes a command using [`execa`](https://github.com/sindresorhus/execa).

```ts
import { PinionContext, generator, exec } from '@feathershq/pinion'

export const generate = (context: Context) => generator(context).then(exec('git', 'init'))
```

### confirmGitIsWorkingTreeClean

`confirmGitIsWorkingTreeClean()` checks if the current directory is a git repository and if the working tree is clean.

```ts
import { PinionContext, generator, confirmGitIsWorkingTreeClean } from '@feathershq/pinion'

export const generate = (context: Context) =>
generator(context).then(confirmGitIsWorkingTreeClean()).then(/* whatever needs to be done */)
```

### runGenerators
Expand Down
Loading