-
Notifications
You must be signed in to change notification settings - Fork 381
/
index.html
50 lines (47 loc) · 18.3 KB
/
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><title>Writing Your Own Yeoman Generator | Yeoman</title><meta name=viewport content="width=device-width"><link href="http://fonts.googleapis.com/css?family=Roboto+Slab:400,700|Roboto:400,700,700italic,400italic" rel=stylesheet><link rel=stylesheet href=/static/bundle-4963.138cb6b273.css><link href=/blog/atom.xml type=application/atom+xml rel=alternate title="Yeoman Blog Atom Feed"><link rel="shortcut icon" href=../static/favicon.b25e58c4b8.ico></head><body class=documentation><header class=page-header><div class=container><div class=mobile-bar><h1 class=logo><a href="/"><img src=/static/logo.f0661e3819.png alt=Yeoman></a></h1><button class="mobile-menu-toggle ir">open</button></div><nav class=main-menu><ul class=main-nav><li><a href="/learning/">Using Yeoman</a><ul> <li><a href=/learning/index.html>Getting started</a></li> <li><a href=/codelab.html>Tutorial (codelab)</a></li> <li><a href=/learning/resources.html>Resources</a></li> <li><a href=/learning/deployment.html>Deployment</a></li> <li><a href=/learning/faq.html>FAQ</a></li> <li><a href=/learning/support.html>Support</a></li></ul></li><li><a href="/generators/">Discovering generators</a></li> <li><a href="/authoring/" class=active>Creating a generator</a><ul> <li><a href=/authoring/index.html class=active>Getting started</a></li> <li><a href=/authoring/running-context.html>Running Context</a></li> <li><a href=/authoring/user-interactions.html>User Interactions</a></li> <li><a href=/authoring/composability.html>Composability</a></li> <li><a href=/authoring/dependencies.html>Managing Dependencies</a></li> <li><a href=/authoring/file-system.html>Interacting with the file system</a></li> <li><a href=/authoring/gruntfile.html>Creating a Gruntfile</a></li> <li><a href=/authoring/storage.html>Storing user configs</a></li> <li><a href=/authoring/testing.html>Unit testing</a></li> <li><a href=/authoring/debugging.html>Debugging Generators</a></li> <li><a href=/authoring/integrating-yeoman.html>Integrating Yeoman in other tools</a></li> <li><a href="http://yeoman.github.io/generator/">Full API documentation</a></li></ul></li><li><a href="/blog/">Blog</a></li> <li><a href="/contributing/">Contributing</a><ul> <li><a href=/contributing/index.html>Contributing</a></li> <li><a href=/contributing/opening-issues.html>How to open an issue</a></li> <li><a href=/contributing/pull-request.html>How to submit a PR</a></li> <li><a href=/contributing/style-guide.html>Style Guide</a></li> <li><a href=/contributing/testing-guidelines.html>Testing Guidelines</a></li> <li><a href=/contributing/ticketing.html>Issue system overview</a></li></ul></li></ul></nav></div></header><div id=content><h2 class=page-title><div class=container>Writing Your Own Yeoman Generator</div></h2><div class="container has-sidebar clearfix"><nav class=context-nav><ul><li><a href=/authoring/index.html class=active>Getting started</a></li> <li><a href=/authoring/running-context.html>Running Context</a></li> <li><a href=/authoring/user-interactions.html>User Interactions</a></li> <li><a href=/authoring/composability.html>Composability</a></li> <li><a href=/authoring/dependencies.html>Managing Dependencies</a></li> <li><a href=/authoring/file-system.html>Interacting with the file system</a></li> <li><a href=/authoring/gruntfile.html>Creating a Gruntfile</a></li> <li><a href=/authoring/storage.html>Storing user configs</a></li> <li><a href=/authoring/testing.html>Unit testing</a></li> <li><a href=/authoring/debugging.html>Debugging Generators</a></li> <li><a href=/authoring/integrating-yeoman.html>Integrating Yeoman in other tools</a></li> <li><a href="http://yeoman.github.io/generator/">Full API documentation</a></li></ul></nav><article class=main><section class=content-chunk><p>Generators are the building blocks of the Yeoman ecosystem. They're the plugins run by <code>yo</code> to generate files for end users.</p><p>In reading this section, you'll learn how to create and distribute your own.</p><aside class=excerpt>Note: We built a [generator-generator](https://github.com/yeoman/generator-generator) to help users get started with their own generator. Feel free to use it to bootstrap your own generator once you understand the below concepts.</aside><h2>Organizing your generators</h2><h3>Setting up as a node module</h3><p>A generator is, at its core, a Node.js module.</p><p>First, create a folder within which you'll write your generator. This folder must be named <code>generator-name</code> (where <code>name</code> is the name of your generator). This is important, as Yeoman relies on the file system to find available generators.</p><p>Once inside your generator folder, create a <code>package.json</code> file. This file is a Node.js module manifest. You can generate this file by running <code>npm init</code> from your command line or by entering the following manually:</p><div class=highlight><pre><code class=language-json data-lang=json><span class=p>{</span>
<span class=nt>"name"</span><span class=p>:</span> <span class=s2>"generator-name"</span><span class=p>,</span>
<span class=nt>"version"</span><span class=p>:</span> <span class=s2>"0.1.0"</span><span class=p>,</span>
<span class=nt>"description"</span><span class=p>:</span> <span class=s2>""</span><span class=p>,</span>
<span class=nt>"files"</span><span class=p>:</span> <span class=p>[</span>
<span class=s2>"app"</span><span class=p>,</span>
<span class=s2>"router"</span>
<span class=p>],</span>
<span class=nt>"keywords"</span><span class=p>:</span> <span class=p>[</span><span class=s2>"yeoman-generator"</span><span class=p>],</span>
<span class=nt>"dependencies"</span><span class=p>:</span> <span class=p>{</span>
<span class=nt>"yeoman-generator"</span><span class=p>:</span> <span class=s2>"^0.17.3"</span>
<span class=p>}</span>
<span class=p>}</span>
</code></pre></div><p>The <code>name</code> property must be prefixed by <code>generator-</code>. The <code>keywords</code> property must contain <code>"yeoman-generator"</code> and the repo must have a description to be indexed by our <a href="/generators/">generators page</a>.</p><p>You should make sure you set the latest version of <code>yeoman-generator</code> as a dependency. You can do this by running: <code>npm install --save yeoman-generator</code>.</p><p>The <code>files</code> property must be an array of files and directories that is used by your generator.</p><p>Add other <a href=https://docs.npmjs.com/files/package.json><code>package.json</code> properties</a> as needed.</p><h3>Folder tree</h3><p>Yeoman is deeply linked to the file system and to how you structure your directory tree. Each sub-generator is contained within its own folder.</p><p>The default generator used when you call <code>yo name</code> is the <code>app</code> generator. This must be contained within the <code>app/</code> directory.</p><p>Sub-generators, used when you call <code>yo name:subcommand</code>, are stored in folders named exactly like the sub command.</p><p>In an example project, a directory tree could look like this:</p><div class=highlight><pre><code class=language-text data-lang=text>├───package.json
├───app/
│ └───index.js
└───router/
└───index.js
</code></pre></div><p>This generator will expose <code>yo name</code> and <code>yo name:router</code> commands.</p><p>You may not like keeping all your code at the root of your folder. Luckily, Yeoman allows for two different directory structures. It'll look in <code>./</code> and in <code>generators/</code> to register available generators.</p><p>The previous example can be written as follows:</p><div class=highlight><pre><code class=language-text data-lang=text>├───package.json
└───generators/
├───app/
│ └───index.js
└───router/
└───index.js
</code></pre></div><h2>Extending generator</h2><p>Once you have this structure in place, it's time to write the actual generator in <code>app/index.js</code>.</p><p>Yeoman offers base generators which you can extend to implement your own behavior. These base generators will add most of the functionality you'd expect to ease your task.</p><p>Here's how you'd extend a base generator:</p><div class=highlight><pre><code class=language-js data-lang=js><span class=kd>var</span> <span class=nx>generators</span> <span class=o>=</span> <span class=nx>require</span><span class=p>(</span><span class=s1>'yeoman-generator'</span><span class=p>);</span>
<span class=nx>module</span><span class=p>.</span><span class=nx>exports</span> <span class=o>=</span> <span class=nx>generators</span><span class=p>.</span><span class=nx>Base</span><span class=p>.</span><span class=nx>extend</span><span class=p>();</span>
</code></pre></div><p>If you'd like to require a <code>name</code> argument for your generator (for example <code>foo</code> in <code>yo name:router foo</code>) that will be assigned to <code>this.name</code>, you can instead do the following:</p><div class=highlight><pre><code class=language-js data-lang=js><span class=kd>var</span> <span class=nx>generators</span> <span class=o>=</span> <span class=nx>require</span><span class=p>(</span><span class=s1>'yeoman-generator'</span><span class=p>);</span>
<span class=nx>module</span><span class=p>.</span><span class=nx>exports</span> <span class=o>=</span> <span class=nx>generators</span><span class=p>.</span><span class=nx>NamedBase</span><span class=p>.</span><span class=nx>extend</span><span class=p>();</span>
</code></pre></div><p>Either type of generator can be extended to create an app generator or a subcommand generator. <code>Base</code> is extended, most often, for an app generator and <code>NamedBase</code> for a subcommand generator where a filename is required.</p><p>The <code>extend</code> method will extend the base class and allow you to provide a new prototype. This functionality comes from the <a href=https://github.com/SBoudrias/class-extend>Class-extend</a> module and should be familiar if you've ever worked with Backbone.</p><p>We assign the extended generator to <code>module.exports</code> to make it available to the ecosystem. This is how we <a href=https://nodejs.org/api/modules.html#modules_module_exports>export modules in Node.js</a>.</p><h3>Overwriting the constructor</h3><p>Some generator methods can only be called inside the <code>constructor</code> function. These special methods may do things like set up important state controls and may not function outside of the constructor.</p><p>To override the generator constructor, you pass a constructor function to <code>extend()</code> like so:</p><div class=highlight><pre><code class=language-js data-lang=js><span class=nx>module</span><span class=p>.</span><span class=nx>exports</span> <span class=o>=</span> <span class=nx>generators</span><span class=p>.</span><span class=nx>Base</span><span class=p>.</span><span class=nx>extend</span><span class=p>({</span>
<span class=c1>// The name `constructor` is important here</span>
<span class=nx>constructor</span><span class=o>:</span> <span class=kd>function</span> <span class=p>()</span> <span class=p>{</span>
<span class=c1>// Calling the super constructor is important so our generator is correctly set up</span>
<span class=nx>generators</span><span class=p>.</span><span class=nx>Base</span><span class=p>.</span><span class=nx>apply</span><span class=p>(</span><span class=k>this</span><span class=p>,</span> <span class=nx>arguments</span><span class=p>);</span>
<span class=c1>// Next, add your custom code</span>
<span class=k>this</span><span class=p>.</span><span class=nx>option</span><span class=p>(</span><span class=s1>'coffee'</span><span class=p>);</span> <span class=c1>// This method adds support for a `--coffee` flag</span>
<span class=p>}</span>
<span class=p>});</span>
</code></pre></div><h3>Adding your own functionality</h3><p>Every method added to the prototype is run once the generator is called--and usually in sequence. But, as we'll see in the next section, some special method names will trigger a specific run order.</p><p>Let's add some methods:</p><div class=highlight><pre><code class=language-js data-lang=js><span class=nx>module</span><span class=p>.</span><span class=nx>exports</span> <span class=o>=</span> <span class=nx>generators</span><span class=p>.</span><span class=nx>Base</span><span class=p>.</span><span class=nx>extend</span><span class=p>({</span>
<span class=nx>method1</span><span class=o>:</span> <span class=kd>function</span> <span class=p>()</span> <span class=p>{</span>
<span class=nx>console</span><span class=p>.</span><span class=nx>log</span><span class=p>(</span><span class=s1>'method 1 just ran'</span><span class=p>);</span>
<span class=p>},</span>
<span class=nx>method2</span><span class=o>:</span> <span class=kd>function</span> <span class=p>()</span> <span class=p>{</span>
<span class=nx>console</span><span class=p>.</span><span class=nx>log</span><span class=p>(</span><span class=s1>'method 2 just ran'</span><span class=p>);</span>
<span class=p>}</span>
<span class=p>});</span>
</code></pre></div><p>When we run the generator later, you'll see these lines logged to the console.</p><h2>Running the generator</h2><p>At this point, you have a working generator. The next logical step would be to run it and see if it works.</p><p>Since you're developing the generator locally, it's not yet available as a global npm module. A global module may be created and symlinked to a local one, using npm. Here's what you'll want to do:</p><p>On the command line, from the root of your generator project (in the <code>generator-name/</code> folder), type:</p><div class=highlight><pre><code class=language-text data-lang=text>npm link
</code></pre></div><p>That will install your project dependencies and symlink a global module to your local file. After npm is done, you'll be able to call <code>yo name</code> and you should see the <code>console.log</code>, defined earlier, rendered in the terminal. Congratulations, you just built your first generator!</p><h3>Finding the project root</h3><p>While running a generator, Yeoman will try to figure some things out based on the context of the folder it's running from.</p><p>Most importantly, Yeoman searches the directory tree for a <code>.yo-rc.json</code> file. If found, it considers the location of the file as the root of the project. Behind the scenes, Yeoman will change the current directory to the <code>.yo-rc.json</code> file location and run the requested generator there.</p><p>The Storage module creates the <code>.yo-rc.json</code> file. Calling <code>this.config.save()</code> from a generator for the first time will create the file.</p><p>So, if your generator is not running in your current working directory, make sure you don't have a <code>.yo-rc.json</code> somewhere up the directory tree.</p><h2>Where to go from here?</h2><p>After reading this, you should be able to create a local generator and run it.</p><p>If this is your first time writing a generator, you should definitely read the next section on <a href=/authoring/running-context.html>running context and the run loop</a>. This section is vital to understanding the context in which your generator will run, and to ensure that it will compose well with other generators in the Yeoman ecosystem. The other sections of the documentation will present functionality available within the Yeoman core to help you achieve your goals.</p><div class=contribute-doc>Found a typo? An unclear example? Help us improve our documentation by forking and sending your fixes and suggestions. <a href=https://github.com/yeoman/yeoman.io/blob/master/app/authoring/index.md>Improve this Page!</a></div></section></article></div></div><footer class=page-footer><ul class="footer-links footer-links-social"><li><a href=https://plus.google.com/101063139999404044459/posts class=icon><img src=/static/social-gplus.cdac08228d.svg alt=Google+></a></li> <li><a href=https://twitter.com/yeoman class=icon><img src=/static/social-twitter.60eb981a15.svg alt=Twitter></a></li> <li><a href=https://github.com/yeoman/yeoman class=icon><img src=/static/social-github.767995ad17.svg alt=Github></a></li> <li><a href=/blog/atom.xml class=icon><img src=/static/social-feed.c635057faf.svg alt=Feed></a></li></ul><ul class="footer-links footer-links-contribute"><li><a href="http://yeoman.github.io/generator/" class=btn>API</a></li> <li><a href=https://github.com/yeoman/yeoman.io/blob/master/app/authoring/index.md class="btn btn-improve" title="Edit this page on Github to help improve the site">Improve this page</a></li></ul></footer><script src=//ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js></script><script src=/static/bundle-5022.6f6c082cfd.js async></script><script>!function(t,e,n){"undefined"!=typeof n.module&&n.module.exports?n.module.exports=e():"undefined"!=typeof n.define&&"function"==n.define&&n.define.amd?define(t,e):n[t]=e()}("$script",function(){function t(t,e){for(var n=0,o=t.length;o>n;++n)if(!e(t[n]))return l;return 1}function e(e,n){t(e,function(t){return!n(t)})}function n(i,c,l){function p(t){return t.call?t():s[t]}function g(){if(!--w){s[y]=1,v&&v();for(var n in f)t(n.split("|"),p)&&!e(f[n],p)&&(f[n]=[])}}i=i[m]?i:[i];var h=c&&c.call,v=h?c:l,y=h?i.join(""):c,w=i.length;return setTimeout(function(){e(i,function(t){return d[t]?(y&&(u[y]=1),2==d[t]&&g()):(d[t]=1,y&&(u[y]=1),o(!a.test(t)&&r?r+t+".js":t,g),void 0)})},0),n}function o(t,e){var n=i.createElement("script"),o=l;n.onload=n.onerror=n[v]=function(){n[g]&&!/^c|loade/.test(n[g])||o||(n.onload=n[v]=null,o=1,d[t]=2,e())},n.async=1,n.src=t,c.insertBefore(n,c.firstChild)}var r,i=document,c=i.getElementsByTagName("head")[0],a=/^https?:\/\//,s={},u={},f={},d={},l=!1,m="push",p="DOMContentLoaded",g="readyState",h="addEventListener",v="onreadystatechange";return!i[g]&&i[h]&&(i[h](p,function y(){i.removeEventListener(p,y,l),i[g]="complete"},l),i[g]="loading"),n.get=o,n.order=function(t,e,o){!function r(i){i=t.shift(),t.length?n(i,r):n(i,e,o)}()},n.path=function(t){r=t},n.ready=function(o,r,i){o=o[m]?o:[o];var c=[];return!e(o,function(t){s[t]||c[m](t)})&&t(o,function(t){return s[t]})?r():!function(t){f[t]=f[t]||[],f[t][m](r),i&&i(c)}(o.join("|")),n},n},this);var _gaq=[["_setAccount","UA-32956520-1"],["_setDomainName",".yeoman.io"],["_trackPageview"],["_trackPageLoadTime"]];$script("http://www.google-analytics.com/ga.js"),$script("https://apis.google.com/js/plusone.js"),$script("//platform.twitter.com/widgets.js");</script></body></html>