UNPKG

11.1 kBMarkdownView Raw
1<h3 align="center">
2 <img src="https://raw.githubusercontent.com/jondot/hygen/master/media/hygen.png" alt="hygen logo" width=400 />
3</h3>
4
5[![build status](https://img.shields.io/travis/jondot/hygen/master.svg)](https://travis-ci.org/jondot/hygen)
6[![npm version](https://img.shields.io/npm/v/hygen.svg)](https://www.npmjs.com/package/hygen)
7
8`hygen` is the simple, fast, and scalable code generator that lives _in_ your project.
9
10<div align="center">
11 <img src="https://raw.githubusercontent.com/jondot/hygen/master/media/hygen.gif" width=600/>
12</div>
13
14
15## Features
16
17✅ Build ad-hoc generators quickly and full on project scaffolds.
18✅ Local generators per project (and global, if you must)
19✅ Built-in scaffolds to quickly create generators
20✅ Full logic templates and rendering
21✅ Prompts and flows for taking in arguments
22✅ Automatic CLI arguments
23✅ Adding new files
24✅ Injecting into existing files
25✅ Running shell commands
26✅ Super fast, constantly optimized for speed
27
28
29> New in hygen v4.0.0: a positional `NAME` parameter.
30> Now you can use `$ hygen component new MyComponent` instead of `$ hygen component new --name MyComponent`.
31
32## Quick Start
33
34Hygen can be used to supercharge your workflow with [Redux](http://www.hygen.io/redux), [React Native](http://www.hygen.io/react-native), [Express](http://www.hygen.io/express) and more, by allowing you avoid manual work and generate, add, inject and perform custom operations on your codebase.
35
36If you're on macOS and have Homebrew:
37
38```
39$ brew tap jondot/tap
40$ brew install hygen
41```
42
43If you have node.js installed, you can install globally with `npm` (or Yarn):
44
45```
46$ npm i -g hygen
47```
48
49If you like a no-strings-attached approach, you can use `npx` without installing globally:
50
51```
52$ npx hygen ...
53```
54
55For other platforms, see [releases](https://github.com/jondot/hygen/releases).
56
57Initialize `hygen` in your project (do this once per project):
58
59```
60$ cd your-project
61$ hygen init self
62```
63
64Build your first generator, called `mygen`:
65
66```
67$ hygen generator new mygen
68
69Loaded templates: _templates
70 added: _templates/mygen/new/hello.ejs.t
71```
72
73Now you can use it:
74
75```
76$ hygen mygen new
77
78Loaded templates: _templates
79 added: app/hello.js
80```
81
82You've generated content into the current working directory in `app/`. To see how the generator is built, look at `_templates` (which you should check-in to your project from now on, by the way).
83
84You can build a generator that uses an interactive prompt to fill in a variable:
85
86```
87$ hygen generator with-prompt mygen
88
89Loaded templates: _templates
90 added: _templates/mygen/with-prompt/hello.ejs.t
91 added: _templates/mygen/with-prompt/prompt.js
92```
93
94Done! Now let's use `mygen`:
95
96```
97$ hygen mygen with-prompt
98? What's your message? hello
99
100Loaded templates: _templates
101 added: app/hello.js
102```
103
104## What's Next?
105
106Go to the [documentation](http://www.hygen.io/quick-start) to get to know the rest of Hygen and generators.
107
108If you're in a hurry:
109
110* To learn how to edit generator templates, [look here](http://www.hygen.io/templates)
111* To see how to use generators [look here](http://www.hygen.io/generators)
112* Take a look at the [ecosystem](http://www.hygen.io/packages) and tooling built around Hygen.
113
114## A Different Kind of a Generator
115
116`hygen` is a scalable generator. It has developer and team ergonomics as first priority.
117
118It avoids "blessed" or dedicated projects that codifies code generation, which before you know it, become a product you build, needs testing, CI, separate pull request reviews, and ultimately sucks up your time and becomes this super hated thing no one likes to touch anymore.
119
120Plus, since they are not the product you are building they become notoriously hard to reason about.
121
122## Scratch Your Own Itch
123
124Because `hygen` templates live _in_ your project, it cuts the time from having an itch for generating code (Redux, anyone?) in your current
125project to code generated with it and others benefiting from it.
126
127### Template Locality
128
129`hygen` picks up a local `_templates` folder
130at any folder level of your project you're working from.
131
132This is important because:
133
134* Templates are project-local. A git clone of the project fetches all generators as well.
135* Different generators can be tucked in different parts of the project, making it contextual.
136* Template locality is scalable; different teams can maintain different generators.
137* When you change your code, you can make changes in the template and pack in the same commit, to be reviewed and merged in the same PR (as opposed to installing different "plugins" or different templates from out-of-repo places).
138
139And yet, if you don't like project-local templates:
140
141* You can have a global `_templates` folder (maybe a central git repo you maintain?) by populating an environment variable `HYGEN_TMPLS`
142* You can build a [custom generator](#build-your-own) of your own with `hygen` at its core, and pack your own templates with it.
143
144### Folder Structure is Command Structure
145
146The templates folder structure _maps directly_ to the command structure:
147
148```
149$ hygen worker new jobrunner
150```
151
152For this command, `hygen worker new` maps to `_templates/worker/new` and all files within `worker/new` are rendered.
153
154Template parameters are given with `--flag VALUE`, as many as you'd like. In this example we've set a parameter named `name` to the value `jobrunner`.
155
156### Subcommands
157
158A subcommand is a file inside a your folder structure. So if the structure is this:
159
160```
161_templates/
162 worker/
163 new/
164 index.html.ejs
165 setup.html.ejs
166```
167
168And you only want `setup`, you can run:
169
170```
171$ hygen worker new:setup
172```
173
174You can also use the subcommand as a regular expression so, these will do the same:
175
176```
177$ hygen worker new:se
178```
179
180```
181$ hygen worker new:se.*
182```
183
184### Frontmatter for Decluttering
185
186Here's how a template looks like (in our example, `index.ejs.t`). Templates bodies are [ejs](https://github.com/tj/ejs):
187
188```javascript
189---
190to: app/workers/<%=name%>.js
191---
192
193class <%= h.capitalize(name) %> {
194 work(){
195 // your code here!
196 }
197}
198```
199
200The first part of the template is a [front matter](https://jekyllrb.com/docs/frontmatter/), idea borrowed from Markdown, this is the metadata part of a `hygen` template and is part of the reason of why your templates will feel more lightweight and flexible.
201
202All frontmatter metadata _are also run through the template engine_ so feel free to use variables in the frontmatter as you wish.
203
204There's one required metadata variable: `to`.
205`to` points to where this file will be placed (folders are created as needed).
206
207### Case changing
208
209`hygen` provides ability to semantic case changing with `change-case` library, it's simple to use and very easy to understand.
210
211There is a usecase for react based components generation:
212
213```yaml
214---
215to: components/<%= name %>/index.jsx
216---
217import React from 'react'
218
219export const <%= name %> = ({ children }) => (
220 <div className="<%= h.changeCase.paramCase(name) %>">{children}</div>"
221)
222```
223
224With name `HelloWorld` will be compiled to:
225
226```js
227import React from 'react'
228
229export const HelloWorld = ({ children }) => (
230 <div className="hello-world">{children}</div>"
231)
232```
233
234You can see the full list [here](https://github.com/blakeembrey/change-case).
235
236### Addition, Injection, and More
237
238By default templates are 'added' to your project as a new target file, but you can also choose to inject a template _into_ an existing target file.
239
240For this to work, you need to use `inject: true` with the accompanied inject-specific props.
241
242```yaml
243---
244to: package.json
245inject: true
246after: dependencies
247skip_if: react-native-fs
248---
249"react-native-fs":"*",
250```
251
252This template will add the `react-native-fs` dependency into a `package.json` file, but it will not add it twice (see `skip_if`).
253
254Here are the available mutually-exclusive options for where to inject at:
255
256* `before | after` - a regular expression / text to locate. The inject line will appear before or after the located line.
257* `prepend | append` - add a line to start or end of file respectively.
258* `line_at` - add a line at this exact line number.
259
260You can guard against double injection:
261
262* `skip_if` - a regular expression / text. If exists injection is skipped.
263
264Also you can insert or remove empty line to injection body. That feature very useful if your editor or formatter automatically insert blank line at the end of file on save:
265
266* `eof_last` - if falsy - trim blank line from the end of injection body, if truthy - insert it.
267
268### Build Your Own
269
270`hygen` is highly embeddable. You should be able to use it by simply listing it
271as a dependency and having [this kind of workflow](src/bin.js) in your binary.
272
273```javascript
274const { runner } = require('hygen')
275const Logger = require('hygen/lib/logger')
276const path = require('path')
277const defaultTemplates = path.join(__dirname, 'templates')
278
279runner(process.argv.slice(2), {
280 templates: defaultTemplates,
281 cwd: process.cwd(),
282 logger: new Logger(console.log.bind(console)),
283 createPrompter: () => require('enquirer'),
284 exec: (action, body) => {
285 const opts = body && body.length > 0 ? { input: body } : {}
286 return require('execa').shell(action, opts)
287 },
288 debug: !!process.env.DEBUG
289})
290```
291
292# Development
293
294The Hygen codebase has a functional style where possible. This is because naturally, it
295feeds parameters and spits out files. Try to keep it that way.
296
297Running `hygen` locally, rigged to your current codebase (note the additional `--` to allow passing flags)
298
299```
300$ yarn hygen -- mailer new foobar
301```
302
303Running tests in watch mode:
304
305```
306$ yarn watch
307```
308
309## Metaverse Testing
310
311The easiest way to make an impact is to use the built-in [`metaverse`](src/__tests__/metaverse) tests suite, and then add the tests [here](src/__tests__/metaverse.spec.js).
312
313The idea here is to copy templates from any project that use `hygen` and to test that it works at all times. This keeps tabs on the hygen universe / ecosystem (nicknamed metaverse) and makes sure this test suite breaks before `hygen` clients break.
314
315## Internal Testing
316
317## Start Up Speed Testing
318
319Many generators become painfully slow to use as the thing you want to generate grow (because, real life),
320
321This is why `hygen` takes speed as a first class citizen, and sports a dedicated start-up timing suite:
322
323```
324$ yarn test:require
325```
326
327In addition, thought is given to which dependencies to take in, how their file structure fan out and what kind of disk access (due to `require`) would `hygen` ultimately have when we run it. This is recorded with every test run.
328
329Bundling a single file was evaluated (and the infrastructure is still there, using `webpack`) and wasn't faster than what we have right now.
330
331# Contributing
332
333Fork, implement, add tests, pull request, get my everlasting thanks and a respectable place here :).
334
335### Thanks:
336
337To all [Contributors](https://github.com/jondot/hygen/graphs/contributors) - you make this happen, thanks!
338
339# Copyright
340
341Copyright (c) 2018 [Dotan Nahum](http://gplus.to/dotan) [@jondot](http://twitter.com/jondot). See [LICENSE](LICENSE.txt) for further details.