GithubHelp home page GithubHelp logo

jondot / hygen Goto Github PK

View Code? Open in Web Editor NEW
5.5K 50.0 249.0 10.25 MB

The simple, fast, and scalable code generator that lives in your project.

Home Page: http://www.hygen.io

License: MIT License

JavaScript 48.02% Perl 0.24% CSS 8.03% TypeScript 35.11% EJS 8.55% Raku 0.05%
nodejs generator tools cli

hygen's Introduction

hygen logo

build status npm version

hygen is the simple, fast, and scalable code generator that lives in your project.

Features

  • ✅ Build ad-hoc generators quickly and full on project scaffolds.
  • ✅ Local generators per project (and global, if you must)
  • ✅ Built-in scaffolds to quickly create generators
  • ✅ Full logic templates and rendering
  • ✅ Prompts and flows for taking in arguments
  • ✅ Automatic CLI arguments
  • ✅ Adding new files
  • ✅ Injecting into existing files
  • ✅ Running shell commands
  • ✅ Super fast, constantly optimized for speed

New in hygen v4.0.0: a positional NAME parameter. Now you can use $ hygen component new MyComponent instead of $ hygen component new --name MyComponent.

Used By

Wix     Airbnb     Mercedes Benz AG     Open Data Institute     Ableneo   City of Amsterdam     Accenture   Birdie     Kind     Ackee   Aerian Studios  Food and Agriculture Organization of the UN Cape Cod Commision   Tweak Things     Crema     Cureon     Astrocoders     Vega/IDL   Sporty Spots    Thrashplay   8base    Instamotionh Biotope Frontend Labs Swydo Gridsome Rosem LaboratorySheffield Hallam University Hackoregon Chilly Design

Scale Leap

Chat Logs

Stelace

Echobind.

Quick Start

Hygen can be used to supercharge your workflow with Redux, React Native, Express and more, by allowing you avoid manual work and generate, add, inject and perform custom operations on your codebase.

If you're on macOS and have Homebrew:

$ brew tap jondot/tap
$ brew install hygen

If you have node.js installed, you can install globally with npm (or Yarn):

$ npm i -g hygen

If you like a no-strings-attached approach, you can use npx without installing globally:

$ npx hygen ...

For other platforms, see releases.

Initialize hygen in your project (do this once per project):

$ cd your-project
$ hygen init self

Build your first generator, called mygen:

$ hygen generator new mygen

Loaded templates: _templates
       added: _templates/mygen/new/hello.ejs.t

Now you can use it:

$ hygen mygen new

Loaded templates: _templates
       added: app/hello.js

You'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).

You can build a generator that uses an interactive prompt to fill in a variable:

$ hygen generator with-prompt mygen

Loaded templates: _templates
       added: _templates/mygen/with-prompt/hello.ejs.t
       added: _templates/mygen/with-prompt/prompt.js

Done! Now let's use mygen:

$ hygen mygen with-prompt
? What's your message? hello

Loaded templates: _templates
       added: app/hello.js

Use a template repo

Want to start from a repo?

$ hygen init repo antfu/vitesse --to my-folder

Want to start from an existing repo on an existing project?

$ mkdir your-project && cd your-project
$ hygen init repo antfu/vitesse

What's Next?

Go to the documentation to get to know the rest of Hygen and generators.

If you're in a hurry:

  • To learn how to edit generator templates, look here
  • To see how to use generators look here
  • Take a look at the ecosystem and tooling built around Hygen.

A Different Kind of a Generator

hygen is a scalable generator. It has developer and team ergonomics as first priority.

It 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.

Plus, since they are not the product you are building they become notoriously hard to reason about.

Scratch Your Own Itch

Because hygen templates live in your project, it cuts the time from having an itch for generating code (Redux, anyone?) in your current project to code generated with it and others benefiting from it.

Template Locality

hygen picks up a local _templates folder at any folder level of your project you're working from.

This is important because:

  • Templates are project-local. A git clone of the project fetches all generators as well.
  • Different generators can be tucked in different parts of the project, making it contextual.
  • Template locality is scalable; different teams can maintain different generators.
  • 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).

And yet, if you don't like project-local templates:

  • You can have a global _templates folder (maybe a central git repo you maintain?) by populating an environment variable HYGEN_TMPLS
  • You can build a custom generator of your own with hygen at its core, and pack your own templates with it.

Folder Structure is Command Structure

The templates folder structure maps directly to the command structure:

$ hygen worker new jobrunner

For this command, hygen worker new maps to _templates/worker/new and all files within worker/new are rendered.

Template 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.

Subcommands

A subcommand is a file inside a your folder structure. So if the structure is this:

_templates/
    worker/
      new/
        index.html.ejs
        setup.html.ejs

And you only want setup, you can run:

$ hygen worker new:setup

You can also use the subcommand as a regular expression so, these will do the same:

$ hygen worker new:se
$ hygen worker new:se.*

Frontmatter for Decluttering

Here's how a template looks like (in our example, index.ejs.t). Templates bodies are ejs:

---
to: app/workers/<%=name%>.js
---

class <%= h.capitalize(name) %> {
    work(){
        // your code here!
    }
}

The first part of the template is a front matter, 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.

All frontmatter metadata are also run through the template engine so feel free to use variables in the frontmatter as you wish.

There's one required metadata variable: to. to points to where this file will be placed (folders are created as needed).

Case changing

hygen provides ability to semantic case changing with change-case library, it's simple to use and very easy to understand.

There is a usecase for react based components generation:

---
to: components/<%= name %>/index.jsx
---
import React from 'react'

export const <%= name %> = ({ children }) => (
  <div className="<%= h.changeCase.paramCase(name) %>">{children}</div>"
)

With name HelloWorld will be compiled to:

import React from 'react'

export const HelloWorld = ({ children }) => (
  <div className="hello-world">{children}</div>"
)

You can see the full list here.

Addition, Injection, and More

By 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.

For this to work, you need to use inject: true with the accompanied inject-specific props.

---
to: package.json
inject: true
after: dependencies
skip_if: react-native-fs
---
"react-native-fs":"*",

This template will add the react-native-fs dependency into a package.json file, but it will not add it twice (see skip_if).

Here are the available mutually-exclusive options for where to inject at:

  • before | after - a regular expression / text to locate. The inject line will appear before or after the located line.
  • prepend | append - add a line to start or end of file respectively.
  • line_at - add a line at this exact line number.

You can guard against double injection:

  • skip_if - a regular expression / text. If exists injection is skipped.

Also 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:

  • eof_last - if falsy - trim blank line from the end of injection body, if truthy - insert it.

Build Your Own

hygen is highly embeddable. You should be able to use it by simply listing it as a dependency and having this kind of workflow in your binary.

const { runner } = require('hygen')
const Logger = require('hygen/dist/logger')
const path = require('path')
const defaultTemplates = path.join(__dirname, 'templates')

runner(process.argv.slice(2), {
  templates: defaultTemplates,
  cwd: process.cwd(),
  logger: new Logger.default(console.log.bind(console)),
  createPrompter: () => require('enquirer'),
  exec: (action, body) => {
    const opts = body && body.length > 0 ? { input: body } : {}
    return require('execa').shell(action, opts)
  },
  debug: !!process.env.DEBUG
})

Development

The Hygen codebase has a functional style where possible. This is because naturally, it feeds parameters and spits out files. Try to keep it that way.

Running hygen locally, rigged to your current codebase (note the additional -- to allow passing flags)

$ yarn hygen -- mailer new foobar

Running tests in watch mode:

$ yarn watch

Metaverse Testing

The easiest way to make an impact is to use the built-in metaverse tests suite, and then add the tests here.

The 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.

Internal Testing

Start Up Speed Testing

Many generators become painfully slow to use as the thing you want to generate grow (because, real life),

This is why hygen takes speed as a first class citizen, and sports a dedicated start-up timing suite:

$ yarn test:require

In 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.

Bundling a single file was evaluated (and the infrastructure is still there, using webpack) and wasn't faster than what we have right now.

Contributing

Fork, implement, add tests, pull request, get my everlasting thanks and a respectable place here :).

Thanks:

To all Contributors - you make this happen, thanks!

Copyright

Copyright (c) 2018 Dotan Nahum @jondot. See LICENSE for further details.

hygen's People

Contributors

agung-wete avatar anithri avatar binier avatar blikblum avatar codyj110 avatar danielscw avatar esxiel avatar fyyyyy avatar grantbowering avatar handfish avatar hisorange avatar idrise avatar jkillian avatar jondot avatar jsjoeio avatar kamontat avatar kr5hn4 avatar lktslionel avatar luckened avatar milesj avatar minsangk avatar renansoares avatar ronp001 avatar samuelfullerthomas avatar seybsen avatar sharmilajesupaul avatar sheepfromheaven avatar snowbldr avatar tobmaster avatar whoaa512 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

hygen's Issues

Docs for `with-prompt` are misleading

I was just checking out the docs for the first time and found that the docs are misleading.

Following the instructions does not give the results demonstrated.

hygen generator new --name generatorName - builds a new generator for you
with-prompt new --name generatorName - the same as before, only this one will be prompt driven.

In fact, running with-prompt new --name generatorName throws error on the console, because there are no binaries called with-prompt.

I couldn't understand what are the docs trying to say here.

avoid getting prompted for params already in argv

This is a little annoying.
let's take the example-prompt templates from the repo

Designed usage:

$ hygen example-prompt new --name foo
? What's your message? hello

Loaded templates: src/templates
       added: hygen-examples/mailers/foo.js
       added: hygen-examples/mailers/hello/html.ejs
       added: hygen-examples/mailers/hello/subject.ejs
       added: hygen-examples/mailers/hello/text.ejs
      inject: hygen-examples/mailers/hello/html.ejs

Possible usage:

$ hygen example-prompt new --name foo --message hello
? What's your message? <Enter>

Loaded templates: src/templates
       added: hygen-examples/mailers/foo.js
       added: hygen-examples/mailers/hello/html.ejs
       added: hygen-examples/mailers/hello/subject.ejs
       added: hygen-examples/mailers/hello/text.ejs
      inject: hygen-examples/mailers/hello/html.ejs

It should be possible to avoid certain prompts if a param is already specified

Possible solution

  • make prompt.js optionally export a function.
  • pass in argv's to that function
  • let the developer decide on what to prompt for, taking into account what is already provided.

Document predefined locals

There seem to be some predefined locals that can be very useful, e.g. locals.actionfolder:

`sh: cp -r <%= locals.actionfolder %>/foo <%= cwd %>/`

Would be great to get this documented.
/cc @kswedberg

How can I force filename to be lowercase?

Hi! Thanks for creating this tool! I have one question.

Given this template:

---
to: src/reducers/<%= name %>.ts
---
hi

hygen redux new --name foo will generate src/reducers/foo.ts and hygen redux new --name Foo will generate src/reducers/Foo.ts. I want to force filename to be lowercase even the user gives Foo. Can we do for that?

Install and reuse generators across projects in a predictable way?

Is there a suggested way to install collections of generators? I feel this would be the ideal way to reuse generators across projects/teams/organizations in a predictable way. If there is not a way currently - this is a feature request! :)

From what I understand there are two main ways now:

  • Add them - more or less manually - to _templates in the current project. This is great for project-specific projects - but not ideal for generators you want to reuse.
  • Add them to the folder HYGEN_TMPLS points to. This is one way to share templates - but it lacks a good way to install versioned collections of packages across a team for example.

Help / Feature requests

Hi, great work going on here.

I just wanted some help.
I want to have the following in my generator:

  1. Enter how many items? ( or More items?)
  2. Enter item details? ( this should be called n times based on value of question 1)

What is the right approach for this.

Shell returns Error: write EPIPE

I'm trying to do:
chmod +x <%= cwd %>/filename.sh
in the file
_templates/<gen name>/new/<filename>.ejs
This returns Error: write EPIPE when I try to generate.

Should not install globally

Installing globally is not realy a good practice. Very useful but not a good practice. For exemple, I work on a micro-services architecture at my work. We have a lot of packages depending each on a different version of node and of dependencies.

From [email protected], there is npx included in node (available since july 2017) which can run a local package bin as it was install globally.

So for using hygen, I already can do :

npx hygen ...

instead of

hygen ...

Not really more difficult but a better practice

editor question option and template with ascii symbols rendered

  {
    type:    'editor',
    name:    'sample_response',
    message: 'Docs/ Sample API Response:',
  },

when I paste a text from a swagger spec in editor (which is either vim or emacs)

{
  "products": [
    {
      "id": "BTC-USD",
      "name": "string",
      "abbr": "BTC-USD",
      "base_currency": "BTC",
      "quote_currency": "USD",
      "base_min_size": "123.00000000",
      "base_max_size": "123.00000000",
      "price_24h": "123.00000000",
      "price_latest": "123.00000000"
    }
  ]
}

I get the following in the template

* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @example
 * {
  &#34;products&#34;: [
    {
      &#34;id&#34;: &#34;BTC-USD&#34;,
      &#34;name&#34;: &#34;string&#34;,
      &#34;abbr&#34;: &#34;BTC-USD&#34;,
      &#34;base_currency&#34;: &#34;BTC&#34;,
      &#34;quote_currency&#34;: &#34;USD&#34;,
      &#34;base_min_size&#34;: &#34;123.00000000&#34;,
      &#34;base_max_size&#34;: &#34;123.00000000&#34;,
      &#34;price_24h&#34;: &#34;123.00000000&#34;,
      &#34;price_latest&#34;: &#34;123.00000000&#34;
    }
  ]
}
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

this is portion of my template

/**
<% if (description) { -%>
 * <%= description -%>
<% } %>
<% if (sample_response) { -%>
 * Sample Response
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @example
 * <%= sample_response.replace(/&#(\d+);/g, function (m, n) { return String.fromCharCode(n); }) -%>
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<% } %>
<% if (swagger_url) { -%>
 *
 * Swagger: @see <%= swagger_url -%>
<% } %>
 *
 * @returns {Promise}
 */

Evolving templates

Hey, hygen is looking great, thanks for open-sourcing your work 🙂

Have you yet thought about how to support evolving templates, or is that something you are not considering?

For example, suppose I have a generator processor, I use the template to create a bunch of processors with hygen processor new --name ex. I then want to update all the rendered processors (for example to migrate all the processors to v2) to use a newer configuration parameter, what would be the hygen way of doing it.

I have though about this a little, and it seems that since hygen is very heavy on "convention over configuration" (which I love and preach by the way, both in internal and open-source projects!), one would make a new V1toV2 migration in the generator, and simply put the new default in new (backing up V1 in newV1 if it is needed). This migrator would basically be adding, replacing and removing the differences based on regexes like skip_if et cetera.

Conditionally use templates?

For example, if I wanted support a --withCSS flag:


hygen component new --name foo

components/foo.js

export default function foo () {}

hygen component new --name foo --withCSS

components/name/foo.js

import './foo.css'

export default function foo () {}

components/name/foo.css

.foo {
  /**/
}

As far as I can tell there's no way to conditionally determine whether to add that foo.css file

skip_if without inject

What the title says, but if you want to hear why, keep reading.

First off, incredible tool, saved me hours of work. What I needed was not supported by any other generator. My workflow is quite different than what hygen offers, but it is flexible enough to fit in.

What I do is to generate web application modules. Basically, it allows add, modify, list, search and many more functionality for a database table. However, these modules require changes along the way. Also making mistakes during data entry is quite possible. Imagine answering 100 questions regarding to 10 or more fields. So what I did was to save the generator data in json, and if the same module name is used again, it simple loads up existing data and allows you to modify it.

This system work great; however, there are times when the module files needs to be customized beyond what the generator script is capable of doing. In these cases, it would be very good to have a marker in the file so that the generator will not replace it. I know I can skip manually but remember each page with modification in a project involving many modules is not feasible. skip_if working in regular files will make this usage possible.

HYGEN_TMPLS does not work as documented

hygen does not use the value in HYGEN_TMPLS as expected.

For example:

$ cd /dev/project
$ HYGEN_TMPLS=/home/me/_templates hygen

should look for templates in /home/me/_templates.

Actual behavior: hygen looks for templates in /dev/project/home/me/_templates.

(perhaps change path.join(cwd, process.env.HYGEN_TMPLS || '_templates'), to process.env.HYGEN_TMPLS in template-resolver.js?)

Better transform-helpers

Hey, @jondot! 🙌
I really don't understand and remember text formating helpers.
What about use change-case for better user experience?
For example:

<div>
  <%= t.kebab('SomeTextNotInKebabCase') %>
</div>

I think this helpers format must been more semantic and helpful.

Stdout not showed

Hi Dotan,
I don't receive the stdout when I launch a command via sh.
Just as an example:

---
sh: "echo 'hello'"
---

I don't see the output produced by this command..
I'm trying to launch a node script with sh that return an output.
Any help?
Thank you very much

How does it compare to plop?

I'm a big fan of code generators. I've been using plop for quite some time. Now I came across hygen and I'm wondering if it has any benefits over the competition?

Discover dynamically index.js path

Hi Dotan,
I'm playing a lot with your module.. :)
I'm trying to find a way to discover the location of the index.js when I run a command.
Suppose this case:
I'm in the root of the project, I run the command for creating a new component, I ask the user to specify a path for the component, now depending to the current directory I need to find a way to get the correct path for inject the component into index.js.
So:
current directory: Projects/funnyProject
I ask the user to insert the path, for example: src/atoms
the component will be created in Projects/funnyProject/src/atoms/ComponentName
now I need to inject the correct path into the index file src/index.js..

Have you some suggestions?
Thanks

recursive template listing and execution

This is just a nice to have ;)

It'd be great to have hygen at the top level of a project recursively search and list all actions spread throughout the project, current behavior only searches the current directory's _templates directory.

Here's a hasty hack I used in npm scripts:

"templates": "find . -path ./node_modules -prune -o -type d | grep '_templates/[A-z_\\-]*/[A-z_\\-]*'",

Then npm run templates yields:

> find . -path ./node_modules -prune -o -type d | grep '_templates/[A-z_\-]*/[A-z_\-]*'

./_templates/component/new-connected
./_templates/component/new
./_templates/generator/with-prompt
./_templates/generator/new
./_templates/generator/help
./_templates/icon/new
./imports/both/collections/_templates/new/collection
./imports/server/fixtures/_templates/new/fixture
./imports/server/cron/_templates/new/cron
./imports/client/atoms/_templates/new/atom
./imports/client/layouts/_templates/new/layout
./imports/client/redux/_templates/new/reducer
./imports/client/redux/_templates/new/action
./imports/client/routes/_templates/new/route
./imports/client/organisms/_templates/new/organism

The ability to execute hygen new atom from the top level of the project would be awesome.

Hello from kirpichik

Hello, @jondot!
Near half year ago I was inspired and created a little project kirpichik (it can be not working because it was in development and I changed api and architecture). It's a component generator, based on global installed "templates".

So, main idea was allow to users makes small declarative templates divided on small repositories and packages, but as can I see, your project do the same thing but more simple and elegant 😄

If you have any issues and ideas I will be happy to help you, because I really want to solve problem with "routine-written" code and components.

Thank you! 🎉

Advanced prompt documentation improvement

Fantastic feature. I ran into a few roadblocks that I eventually overcame myself, so here's a more advanced example in case you find it useful. It covers:

  • ensuring all questions are asked before next prompt is executed
  • ensuring answers from both prompts are returned (was getting an error for missing template variables if I didn't do this explicitly)
  • parsing the first set of answers to create the next set of prompt questions
  • accessing dynamically-created variable names in the templates
module.exports = {
  prompt: ({ inquirer }) => {
    // defining questions in arrays ensures all questions are asked before next prompt is executed
    const questions = [{
      type: 'input',
      name: 'name',
      message: 'Name of form? (ex: AddContactForm)',
    },
    {
      type: 'confirm',
      name: 'shouldValidateForm',
      message: 'Do you need to validate the form?',
    },
    {
      type: 'input',
      name: 'formFieldNames',
      message: 'Field names? (separate by comma: "field1, field2,field3")',
    }]

    return inquirer
      .prompt(questions)
      .then(answers => {
        const { formFieldNames } = answers
        const questions = []

        // these values can be retrieved in the template with: eval(field + '.validation')
        formFieldNames.split(',').forEach((field) => {
          questions.push({
            type: 'input',
            name: field + '.validation',
            message: `Input the validation for ${field} (ex: isBlank(${field}) && '${field} is required')`,
          })
        })

        // both set of answers must be returned as a merged object, else the previous set of answers won't be available to the templates
        return inquirer.prompt(questions).then(nextAnswers => Object.assign({}, answers, nextAnswers))
      })
  },
}

No "repository" field in package.json

Problem:
npm docs hygen opens npmjs.com page and when you click a documentation link, you will get 404 error.

There is no "Repository" section in sidebar on npmjs.com, so you need to g00gle hygen (Thanks the name is kinda unique).

A section, I meant, available in this example

Althought, BIG THANKS (and a star, ofc) to you for such a must-have package, an idea of which has been around for years 👍

Drop here a comment if this task will be time-consuming (or something else) and I'll send you a PR

if/else condition and loop

How to perform if else condition and loop using hygen ?

I'm looking for something like below , can hygen support ?

if /else:

#if( $direction < 10 )
  <strong>Go North</strong>
#else
  <strong>Go West</strong>
#end

Loop

<ul>
  #foreach( $product in $allProducts )
	<li>$product</li>
  #end
</ul>

Roadmap

Hello everyone,

I've decided to aggregate popular feature requests and track/indicate their status. Please feel free to use this thread to discuss these and most importantly - generate great ideas.

  • Load global (custom) helpers issue

Done! see more here http://www.hygen.io/extensibility

  • Recursively list and execute generators issue

Lacking a performant solution, we're opting to a best-practice contributed by @jfols. See official documentation here

  • dynamic prompt generation triggered by prompt input issue thanks: @jaykoontz

If true, generate file

Hi,

I'm playing around with Hygen, creating templates for repeated tasks I have. However I am looking for a solution to my issue:

I have a conditional logic in my generator prompt which is a boolean. I would like to generate a file or not depending on this value.

Is this at all possible? I've seen skip_if however this seems to work only for inject and is regex based.

Any ideas / solutions would be great. Thanks

Non-monospace font used in code blocks in docs

The code blocks in the docs use variants of Lucida Sans on Windows, which is not a monospace font.

This makes the ASCII art a little confusing:
Screenshot

You probably want Lucida Console instead.

Dead links

Quick Start ends with links to templates and generators.

If you are on /quick-start/ (note the trailing slash), these lead to /quick-start/templates, etc.

Run Hygen without hygen init

I'm new to hygen and am loving it so far! Great work.

I have a use case that is a bit outside the norm, but want to see if it could be handled today or if it's a feature I can help work on.

I want to use hygen as a component/function generator for multiple React and Node projects without running hygen init in all of them. @jondot sort of references this at #14 (comment), about not being a project generator, so this may be outside the scope of hygen.

I hacked on this a bit with a global .hygen.js in ~ and reference a template tree at ~/.hygen/templates/..., but I get Error: Cannot find module '[module-name]' errors if the project I'm in doesn't have those modules installed locally (or globally).

I know other tools like Yeoman, Plop, etc. can do this, but Hygen is soooo clean and well designed that I would love to use it for my use case.

Multiple output

@jondot how about allow to create multiple entities in single line? I think it may be very useful.

User can split name value by commas, like hygen template new --name A,B,C,D.

I think first variant more suitable, because commas usage in entities names are very strange and rare, as for me.

Conditional files

Hi Dotan,
Can I have an example of how to create a file depending to the option passed to the command?
As an example, I would like to create a component structure where if I pass the option --story creates also the story file.

Have you a slack channel or similar to have a chat? I'm ok with a github issues, but I don't want to pollute your issues with questions 😀

hygen doesn't work in 1.5.2 and newer versions

I got the same bug as #38 and it seams as it's still a bug in 1.5.7. I have tested installing every version from 1.5.1 to 1.5.7 (latest) and this bug begins showing up in 1.5.2.

It does not matter what command I run, always the same error even when running hygen init self or just hygen then I get (node:10480) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 2): TypeError: Path must be a string. Received undefined.

I'm using node 6.13.1 installed through nvm-windows on Windows 10.

Recursive template inclusion

It would be super useful for code organization to leverage subdirectories.
To enable structures like:

my_generator 
├ action
 ├ tests
   ├mytest.ejs
 ├ myclass.ejs
 ├ prompt.js
 ├ injections
  ├ myinject.ejs

Regarding the order I would suggest that the templates-resolver first collects all root level templates then all directory templates in the alphabetical order of the folder name.

Alternatively the template-resolver could be injected into the runner via the config to enable people to make their own template-resolve logic

YAMLException: can not read a block mapping entry; a multiline key may not be an implicit key at line 5, column 1

I get some cryptic error, trying to inject some code into existing src/apps/auth/api/index.js file

HYGEN_TMPLS=.templates hygen new "api"

? API Method Name: test
? API Method URL: test/method
? App: auth
? method: GET
? Params (separated by ,): 

Loaded templates: .templates
YAMLException: can not read a block mapping entry; a multiline key may not be an implicit key at line 5, column 1:
    
    ^

Available actions:
new: api, app, component

.templates/new/api/prompt.js

const _ = require('lodash');
const fs = require('fs');

function getDirectories(path) {
  return fs.readdirSync(path).filter(function (file) {
    return fs.statSync(path+'/'+file).isDirectory();
  });
}

module.exports = [
  {
    type:    'input',
    name:    'name',
    message: 'API Method Name:',
    validate(value) {
      if (!value.length) {
        return 'API method must have a name.';
      }
      return true;
    },
  },
  {
    type:    'input',
    name:    'url',
    message: 'API Method URL:',
    validate(value) {
      if (!value.length) {
        return 'API method must have an URL.';
      }
      return true;
    },
  },
  {
    type:    'checkbox',
    name:    'appname',
    message: 'App:',
    choices: getDirectories('src/apps'),
  },
  {
    type:    'checkbox',
    name:    'method',
    message: 'HTTP method:',
    choices: [
      {
        name:    'GET',
        value:   'get',
        default: true,
      },
      {
        name:    'POST',
        value:   'post',
        default: false,
      },
      {
        name:    'PUT',
        value:   'put',
        default: false,
      },
      {
        name:    'DELETE',
        value:   'delete',
        default: false,
      },
    ],
  },
  {
    type:    'input',
    name:    'params',
    message: 'Method Params (separated by ,):',
  },
];

.templates/new/api/api.ejs.t

---
to: "src/apps/<%= h.inflection.dasherize(appname).toLowerCase() %>/api/index.js
inject: true
skip_if: <%= name %>
before: "export default {"
---
<%
const appName = h.inflection.dasherize(appname).toLowerCase();
const apiName = h.inflection.dasherize(name).toLowerCase();
%>

/**
 * Documentation
 * @param {String} code - Explanation
 * @returns {Promise}
 */
function <%= apiName %>({ <%= params%>, headers = {}, }) {
  return axios.<%= method%>(`<%= apiURL%>`, { headers, });
}

this is src/apps/auth/api/index.js where I try to inject new method

/**
 * Backend Auth API Endpoints
 */

import { helpers as h, } from '@/lib';
import axios from '@/api/axios';

/**
 * Activate the user by invitation code
 * @param {String} code - Invitation code
 * @param {String} agent - Verified agent information
 * @returns {Promise}
 */
function activate({
  code = h.mandatory('invitation code'),
  agent = h.mandatory('agent'),
  headers = {},
}) {
  return axios.put(`verification/${code}`, agent, { headers, });
}


export default {
  login,
  logout,
  resetPassword,
  verification,
  activate,
};

Expose inflection on prompt callback

In order to provide a default value based on a previous set param, i'm using an external module (camelize) saved on templates folder:

const camelize = require('../../_utils/camelize')

module.exports = {
  prompt: ({ inquirer, args }) => {
    return inquirer.prompt([{
      type: 'input',
      name: 'path',
      message: 'Route path:'
    },
    {
      type: 'input',
      name: 'routeName',
      message: 'Route name:',
      default ({path}) {
        return camelize(path, '/') + 'Route'
      }
    }])
  }
}

It works fine but has some drawbacks:

  • _utils is exposed as a generator
  • this could be accomplished with inflection without resorting to custom code

I could install inflection in project but seems overkill.

If inflection is injected into prompt callback we could reuse it. Something like:

module.exports = {
  prompt: ({ inquirer, args, inflection }) => {
    return inquirer.prompt([{
      type: 'input',
      name: 'path',
      message: 'Route path:'
    },
    {
      type: 'input',
      name: 'routeName',
      message: 'Route name:',
      default ({path}) {
        return inflection.camelize(path.replace('/', '_')) + 'Route'
      }
    }])
  }
}

Global Utilities

Hey there!

First of all, great work, thanks.
I have a question though - is it possible to create a global utilities file and use it inside my templates?
Let's say I want to have a function that converts the following name myComponent to a dashed format my-component.

I saw the built in utils, but what about custom ones?

Currently I have to copy paste this utility function to each of my templates, but it is obviously not a good way.

Is there an option to do so?

`--help` feature request

$ hygen generator new --help

I expected to be able to discover from the CLI what command line arguments an action can use. I ended up having to read the action directly to discover that 'name' and 'action' were the two arguments.

Cannot use colon (:) character in "skip-if"

There appears to be an issue where the : character cannot be used in "skip-if", even if you escape it using double backslashes.

Yaml part of generator:

to: src/state/entities/entities.reducer.ts
inject: true
skip_if: <%= type.toLowerCase().slice(1) + '\\: ' + type.toLowerCase().slice(1) + 'DefaultState\\,' %>
after: "export const defaultState: IEntityReducerState = \\{"

Error message:

YAMLException: incomplete explicit mapping pair; a key node is missed; or followed by a non-tabulated empty line at line 3, column 47:
     ... ype.toLowerCase().slice(1) + '\\: ' + type.toLowerCase().slice(1 ...
                                         ^

TypeError: logger.ok is not a function, using as a library

Using the code from the readme:

#!/usr/bin/env node
const { runner } = require('hygen')
const path = require('path')
const defaultTemplates = path.join(__dirname, 'templates')

runner(process.argv.slice(2), {
  templates: defaultTemplates,
  cwd: process.cwd(),
  logger: console,
  debug: !!process.env.DEBUG
})

Results in this warning:

Loaded templates: _templates
TypeError: logger.ok is not a function

Available actions:
generator: help, new, with-prompt

The files are generated properly but my guess is console isn't sufficient as a logger.

Provide way to define variable to be used in frontmatter

I have the following frontmatter:

---
to: "<%= fileScope === 'global' ? h.rootDir() + '/src/common/views/' + h.inflection.transform(name, ['underscore', 'dasherize']).replace('-view', '') : h.inflection.transform(name, ['underscore', 'dasherize'])%>.js"
---

The h.inflection.transform(name, ['underscore', 'dasherize']) is being used twice making code harder to read

I could not find a way to declare a variable to be used in the frontmatter like can be done in template body.

I tried

---
dashName: <%= h.inflection.transform(name, ['underscore', 'dasherize']) %>
to: "<%= fileScope === 'global' ? h.rootDir() + '/src/common/views/' + dashName.replace('-view', '') : dashName %>.js"
---

But it gives an error dashName not defined

Shell doesn't work: `TypeError: exec is not a function`

Hello! 👋

Context:
While creating a new template with shell command via sh:, I encounter an error:
TypeError: exec is not a function

Template example:

// _templates/generate/my-struct/shell.ejs.t
---
sh: "echo 'hello'"
---

Hygen version:
hygen v1.5.8

Thoughts:
I think maybe it's in src/types.js due to the error name TypeError. Seems like my sh type of string is not the intended type.

Question:
How can I run a shell command correctly from a Hygen template?

<%= Name %> destroys camelcase

Hey , what great tool. I just wanted to point out that I noticed when i was using the blessed uppercase Name variable on a camel case word it destroys the camel case. First letter gets uppercase and the rest get lowercase. Not really a problem but for those templating out some react components the doc may be a bit misleading. Would be nice to indicate that in the doc to save others the puzzling troubleshooting.
template-body

[Feature Request] Add regexp to (in)validate input with prompt

Hi,

It could be very useful to be allowed to add a regexp field in prompt to check if typing matches the good format.

For example, i use hygen to generate some elements for angularjs. Some have to be writtent like this "hello-world". It should not start with a number and should contain a dash.

What do you think about this ? Ok if I make a PR about this ?

`hygen init self` doesn't work in new project

When running hygen init self I get TypeError: Path must be a string. Received undefined.

Tried both the node.js and standalone versions (1.5.5)

I'm using node 8.9.4 installed through nvm on LinuxMint 18.3 (Ubuntu 16.04)

Logging info is being show when running generator

When running a generator some logging info is being show:

hygen view new
? Scope: global
? View name: PersonXView

Loaded templates: D:\repositories\endocrinassist\webclient/_templates
added: D:\repositories\endocrinassist\webclient/src/common/views/person-x.js
results [ { type: 'add',
subject: 'D:\repositories\endocrinassist\webclient/src/common/views/person-x.js',
status: 'added',
timing: 0 } ]

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.