Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Update Creating Generator instructions #829

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
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
61 changes: 34 additions & 27 deletions app/authoring/composability.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ Yeoman offers multiple ways for generators to build upon common ground. There's

In Yeoman, composability can be initiated in two ways:

* A generator can decide to compose itself with another generator (e.g., `generator-backbone` uses `generator-mocha`).
* An end user may also initiate the composition (e.g., Simon wants to generate a Backbone project with SASS and Rails). Note: end user initiated composition is a planned feature and currently not available.
- A generator can decide to compose itself with another generator (e.g., `generator-backbone` uses `generator-mocha`).
- An end user may also initiate the composition (e.g., Simon wants to generate a Backbone project with SASS and Rails). Note: end user initiated composition is a planned feature and currently not available.

## `this.composeWith()`

Expand All @@ -25,13 +25,17 @@ When composing, don't forget about [the running context and the run loop](/autho

`composeWith` takes two parameters.

1. `generatorPath` - A full path pointing to the generator you want to compose with (usually using `require.resolve()`).
2. `options` - An Object containing options to pass to the composed generator once it runs.
1. `generatorPath` - A full path pointing to the generator you want to compose with (usually using [`import.meta.resolve()`](https://nodejs.org/docs/latest-v20.x/api/esm.html#importmetaresolvespecifier) or poly fill with [import-meta-resolve](https://www.npmjs.com/package/import-meta-resolve)).
2. `options` - An Object containing options to pass to the composed generator once it runs.

When composing with a `peerDependencies` generator:

```js
this.composeWith(require.resolve('generator-bootstrap/generators/app'), {preprocessor: 'sass'});
import { resolve } from "import-meta-resolve";

this.composeWith(resolve("generator-bootstrap/generators/app"), {
preprocessor: "sass",
});
```

`require.resolve()` returns the path from where Node.js would load the provided module.
Expand All @@ -41,7 +45,7 @@ Note: If you need to pass `arguments` to a Generator based on a version of `yeom
Even though it is not an encouraged practice, you can also pass a generator namespace to `composeWith`. In that case, Yeoman will try to find that generator installed as a `peerDependencies` or globally on the end user system.

```js
this.composeWith('backbone:route', {rjs: true});
this.composeWith("backbone:route", { rjs: true });
```

### composing with a Generator class
Expand All @@ -55,44 +59,46 @@ This will let you compose with generator classes defined in your project or impo

```js
// Import generator-node's main generator
const NodeGenerator = require('generator-node/generators/app/index.js');
import NodeGenerator from "generator-node/generators/app/index.js";
import { resolve } from "import-meta-resolve";

// Compose with it
this.composeWith({
Generator: NodeGenerator,
path: require.resolve('generator-node/generators/app')
path: resolve("generator-node/generators/app"),
});
```

### <a name="order"></a>execution example

```js
// In my-generator/generators/turbo/index.js
module.exports = class extends Generator {
export default class extends Generator {
prompting() {
this.log('prompting - turbo');
this.log("prompting - turbo");
}

writing() {
this.log('writing - turbo');
this.log("writing - turbo");
}
};
}

// In my-generator/generators/electric/index.js
module.exports = class extends Generator {
export default class extends Generator {
prompting() {
this.log('prompting - zap');
this.log("prompting - zap");
}

writing() {
this.log('writing - zap');
this.log("writing - zap");
}
};

// In my-generator/generators/app/index.js
module.exports = class extends Generator {
export default class extends Generator {
initializing() {
this.composeWith(require.resolve('../turbo'));
this.composeWith(require.resolve('../electric'));
this.composeWith(resolve("../turbo"));
this.composeWith(resolve("../electric"));
}
};
```
Expand All @@ -117,18 +123,19 @@ which is composed of

## dependencies or peerDependencies

*npm* allows three types of dependencies:
_npm_ allows three types of dependencies:

- `dependencies` get installed local to the generator. It is the best option to control the version of the dependency used. This is the preferred option.
- `peerDependencies` get installed alongside the generator, as a sibling. For example, if `generator-backbone` declared `generator-gruntfile` as a peer dependency, the folder tree would look this way:

* `dependencies` get installed local to the generator. It is the best option to control the version of the dependency used. This is the preferred option.
* `peerDependencies` get installed alongside the generator, as a sibling. For example, if `generator-backbone` declared `generator-gruntfile` as a peer dependency, the folder tree would look this way:
```
├───generator-backbone/
└───generator-gruntfile/
```

```
├───generator-backbone/
└───generator-gruntfile/
```
* `devDependencies` for testing and development utility. This is not needed here.
- `devDependencies` for testing and development utility. This is not needed here.

When using `peerDependencies`, be aware other modules may also need the requested module. Take care not to create version conflicts by requesting a specific version (or a narrow range of versions). Yeoman's recommendation with `peerDependencies` is to always request _higher or equal to (>=)_ or _any (*)_ available versions. For example:
When using `peerDependencies`, be aware other modules may also need the requested module. Take care not to create version conflicts by requesting a specific version (or a narrow range of versions). Yeoman's recommendation with `peerDependencies` is to always request _higher or equal to (>=)_ or _any (\*)_ available versions. For example:

```json
{
Expand Down
12 changes: 6 additions & 6 deletions app/authoring/dependencies.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ on the command line in your project.

### Manage npm dependencies programmatically

You can programatically create or extend your `package.json` file if you don’t want to use a template but like to have fixed versions of your dependencies. Yeomans file system tools can help you to get this job done.
You can programmatically create or extend your `package.json` file if you don’t want to use a template but like to have fixed versions of your dependencies. Yeoman's file system tools can help you to get this job done.

Example defining `eslint` as dev dependency and `react` as dependency:

Expand Down Expand Up @@ -70,9 +70,9 @@ For example you want to install lodash as a dev dependency:

```js
generators.Base.extend({
installingLodash: function() {
this.yarnInstall(['lodash'], { 'dev': true });
}
installingLodash: function () {
this.yarnInstall(["lodash"], { dev: true });
},
});
```

Expand Down Expand Up @@ -100,9 +100,9 @@ generators.Base.extend({
this.installDependencies({
npm: false,
bower: true,
yarn: true
yarn: true,
});
}
},
});
```

Expand Down
2 changes: 1 addition & 1 deletion app/authoring/file-system.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ var beautify = require("gulp-beautify");
this.registerTransformStream(beautify({ indent_size: 2 }));
```

Note that **every file of any type will be passed through this stream**. Make sure any transform stream will passthrough the files it doesn't support. Tools like [gulp-if](https://github.com/robrich/gulp-if) or [gulp-filter](https://github.com/sindresorhus/gulp-filter) will help filter invalid types and pass them through.
Note that **every file of any type will be passed through this stream**. Make sure any transform stream will pass through the files it doesn't support. Tools like [gulp-if](https://github.com/robrich/gulp-if) or [gulp-filter](https://github.com/sindresorhus/gulp-filter) will help filter invalid types and pass them through.

You can basically use any _gulp_ plugins with the Yeoman transform stream to process generated files during the writing phase.

Expand Down
41 changes: 17 additions & 24 deletions app/authoring/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ In reading this section, you'll learn how to create and distribute your own.
Note: We built a <a href="https://github.com/yeoman/generator-generator">generator-generator</a> 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>


## Organizing your generators

### Setting up as a node module
Expand All @@ -30,12 +29,11 @@ Once inside your generator folder, create a `package.json` file. This file is a
"name": "generator-name",
"version": "0.1.0",
"description": "",
"files": [
"generators"
],
"type": "module",
"files": ["generators"],
"keywords": ["yeoman-generator"],
"dependencies": {
"yeoman-generator": "^1.0.0"
"yeoman-generator": "^6.0.0"
}
}
```
Expand Down Expand Up @@ -85,29 +83,25 @@ If you use this second directory structure, make sure you point the `files` prop

```json
{
"files": [
"app",
"router"
]
"files": ["app", "router"]
}
```


## Extending generator

Once you have this structure in place, it's time to write the actual generator.

Yeoman offers a base generator which you can extend to implement your own behavior. This base generator will add most of the functionalities you'd expect to ease your task.
Yeoman offers a base generator which you can extend to implement your own behaviour. This base generator will add most of the functionalities you'd expect to ease your task.

In the generator's index.js file, here's how you extend the base generator:

```js
var Generator = require('yeoman-generator');
import Generator from "yeoman-generator";

module.exports = class extends Generator {};
export default class extends Generator {}
```

We assign the extended generator to `module.exports` to make it available to the ecosystem. This is how we [export modules in Node.js](https://nodejs.org/api/modules.html#modules_module_exports).
We export the extended generator to make it available to the ecosystem. Node.js supports [ECMAScript modules](https://nodejs.org/api/esm.html#introduction).

### Overwriting the constructor

Expand All @@ -116,16 +110,18 @@ Some generator methods can only be called inside the `constructor` function. The
To override the generator constructor, add a constructor method like so:

```js
module.exports = class extends Generator {
import Generator from "yeoman-generator";

export default class extends Generator {
// The name `constructor` is important here
constructor(args, opts) {
// Calling the super constructor is important so our generator is correctly set up
super(args, opts);

// Next, add your custom code
this.option('babel'); // This method adds support for a `--babel` flag
this.option("babel"); // This method adds support for a `--babel` flag
}
};
}
```

### Adding your own functionality
Expand All @@ -135,20 +131,19 @@ Every method added to the prototype is run once the generator is called--and usu
Let's add some methods:

```js
module.exports = class extends Generator {
export default class extends Generator {
method1() {
this.log('method 1 just ran');
this.log("method 1 just ran");
}

method2() {
this.log('method 2 just ran');
this.log("method 2 just ran");
}
};
}
```

When we run the generator later, you'll see these lines logged to the console.


## Running the generator

At this point, you have a working generator. The next logical step would be to run it and see if it works.
Expand All @@ -163,7 +158,6 @@ npm link

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 `yo name` and you should see the `this.log`, defined earlier, rendered in the terminal. Congratulations, you just built your first generator!


### Finding the project root

While running a generator, Yeoman will try to figure some things out based on the context of the folder it's running from.
Expand All @@ -174,7 +168,6 @@ The Storage module creates the `.yo-rc.json` file. Calling `this.config.save()`

So, if your generator is not running in your current working directory, make sure you don't have a `.yo-rc.json` somewhere up the directory tree.


## Where to go from here?

After reading this, you should be able to create a local generator and run it.
Expand Down
16 changes: 9 additions & 7 deletions app/authoring/integrating-yeoman.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ In this example, let's assume `npm` wants to provide a `npm init` command to sca
First step is to instantiate a new environment instance.

```js
var yeoman = require('yeoman-environment');
var yeoman = require("yeoman-environment");
var env = yeoman.createEnv();
```

Expand All @@ -38,12 +38,12 @@ Then, we'll want to register our `generator-npm` so it can be used later. You ha
```js
// Here we register a generator based on its path. Providing the namespace
// is optional.
env.register(require.resolve('generator-npm'), 'npm:app');
env.register(require.resolve("generator-npm"), "npm:app");

// Or you can provide a generator constructor. Doing so, you need to provide
// a namespace manually
var GeneratorNPM = generators.Base.extend(/* put your methods in here */);
env.registerStub(GeneratorNPM, 'npm:app');
env.registerStub(GeneratorNPM, "npm:app");
```

Note that you can register as many generators as you want. Registered generators are just made available throughout the environment (to allow composability for example).
Expand All @@ -52,10 +52,10 @@ At this point, your environment is ready to run `npm:app`.

```js
// In its simplest form
env.run('npm:app', done);
env.run("npm:app", done);

// Or passing arguments and options
env.run('npm:app some-name', { 'skip-install': true }, done);
env.run("npm:app some-name", { "skip-install": true }, done);
```

There you go. You just need to put this code in a `bin` runnable file and you can run a Yeoman generator without using `yo`.
Expand All @@ -66,7 +66,7 @@ But what if you wish to provide access to every Yeoman generator installed on a

```js
env.lookup(function () {
env.run('angular');
env.run("angular");
});
```

Expand Down Expand Up @@ -110,12 +110,14 @@ An adapter should provide at least three methods.

### `Adapter#prompt()`

It provides the question-answer functionality (for instance, when you start `yo`, a set of possible actions is prompted to the user). Its signature and behavior follows these of [Inquirer.js](https://github.com/SBoudrias/Inquirer.js). When a generators call `this.prompt`, the call is in the end handled by the adapter.
It provides the question-answer functionality (for instance, when you start `yo`, a set of possible actions is prompted to the user). Its signature and behaviour follows these of [Inquirer.js](https://github.com/SBoudrias/Inquirer.js). When a generators call `this.prompt`, the call is in the end handled by the adapter.

### `Adapter#diff()`

Called internally when a conflict is encountered and the user ask for a diff between the old and the new file (both files content is passed as arguments).

### `Adapter#log()`

It's both a function and an object intended for generic output.
See [`lib/util/log.js`](https://github.com/yeoman/environment/blob/master/lib/util/log.js) for the complete list of methods to provide.

Expand Down
Loading