yeoman
Yeoman is one of the web's most popular tooling frameworks with over 400 contributed reusable plugins. The open source project led by Addy Osmani makes project scaffolding an easy step.
Yeoman是Web上最受欢迎的工具框架之一,拥有400多个可重用的可重用插件。 由Addy Osmani领导的开源项目使项目脚手架轻松完成。
This means that basic project structures and dependencies can now be setup with just one or two commands from the CLI. Is that not amazing? You don't have to install Express
, Passport
, Angular
, Mongoose
, etc that your MEAN project require manually, you just need to create and configure once, and then use throughout your other projects.
这意味着现在可以仅使用CLI中的一两个命令来设置基本项目结构和依赖项。 难道不令人惊奇吗? 您无需手动安装MEAN项目所需的Express
, Passport
, Angular
, Mongoose
等,只需创建和配置一次,然后在其他所有项目中使用。
Note that Yeoman is not just for JavaScript based projects but for anything you can make out of it.
请注意,Yeoman不仅适用于基于JavaScript的项目,还适用于您可以利用它进行的所有操作。
Yeoman's plugins are called generators
and are made in such a way that they suite a particular project requirements or workflow. It is not a surprise that out of the 400 contributed generators, you might not find one that suits you.
Yeoman的插件称为generators
,其制作方式使其适合特定的项目需求或工作流程。 不足为奇的是,在400个贡献生成器中,您可能找不到适合您的一个。
Today, I will guide you through 4 easy steps to create yours and also provide a demo at the end. We will end up with a MEAN generator for Scotch.
今天,我将指导您完成4个简单的步骤来创建您的步骤,并在最后提供一个演示。 我们将最终获得苏格兰威士忌的MEAN生成器。
步骤1:安装和设置 ( STEP 1: Installation and Setup )
As always, the first step is to install the dependencies and create a new generator project. The concept is actually using a yeoman generator to generate a generator template. That sounds silly right?
与往常一样,第一步是安装依赖项并创建一个新的生成器项目。 该概念实际上是使用yeoman生成器生成生成器模板。 听起来很傻吧?
The Yeoman team built a generator plugin that makes building a Yeoman custom generator quite easy. So install both Yeoman (yo
) and the generator (generator-generator
is the name):
Yeoman团队构建了一个生成器插件,这使得构建Yeoman定制生成器变得非常容易。 因此,请同时安装Yeoman( yo
)和发电机( generator-generator
是名称):
npm install -g yo generator-generator
This command installs both Yeoman and the generator plugin. Installing these tools does not create a project, they just download them to your file system. To setup a generator project, run:
此命令将同时安装Yeoman和generator插件。 安装这些工具不会创建项目,它们只是将它们下载到您的文件系统中。 要设置生成器项目,请运行:
yo generator
You will be asked two or more questions from the CLI including your GitHub username and your generator base name. Yeoman generators naming convention is in such a way that the generators are prefixed with generator-
and the name of the custom generator (base name) added right after the -
.
CLI会向您询问两个或多个问题,包括您的GitHub用户名和生成器基本名称。 Yeoman生成器的命名约定是这样的:生成器以generator-
器-为前缀,并且自定义生成器的名称(基本名称)紧跟在-
。
In our case, I chose, scotchmean
so that at the end, the generator will be used as generator-scotchmean
.
在本例中,我选择了scotchmean
以便最后将generator用作generator-scotchmean
。
You do not have to be intimidated by the file structure. Actually we just need one folder which is the generators
folder. The generators
folder has an app
directory with templates
and index.js
. The templates
are files that will be generated when we use this generators to scaffold a project and the index.js folder holds all the logics involved in generating a template.
您不必被文件结构吓到。 实际上,我们只需要一个文件夹,即generators
文件夹。 generators
文件夹有一个包含templates
和index.js
的app
目录。 templates
是当我们使用此生成器来构建项目时将生成的文件,而index.js文件夹包含生成模板所涉及的所有逻辑。
Before we move to step 2, clear the contents of app/templates/index.js
and replace with:
在进行步骤2之前,请清除app/templates/index.js
的内容并替换为:
'use strict';
//Require dependencies
var yeoman = require('yeoman-generator');
var chalk = require('chalk');
var yosay = require('yosay');
module.exports = yeoman.generators.Base.extend({
//Configurations will be loaded here.
});
步骤2:要求输入 ( STEP 2: Asking For Input )
One awesome feature of Yeoman is it's ability to scaffold a project using the user's decision from inputs. This means that we can configure a lot of options like the project's name, dependencies we need, and much more. Yeoman uses Inquire.js to accomplish this feature. To keep it minimal, we just need to get the project's name from input:
Yeoman的一项令人敬畏的功能是能够根据用户输入的决定来为项目提供支架。 这意味着我们可以配置很多选项,例如项目的名称,所需的依赖项等等。 Yeoman使用Inquire.js完成此功能。 为了使它最小化,我们只需要从输入中获取项目的名称:
//Configurations will be loaded here.
//Ask for user input
prompting: function() {
var done = this.async();
this.prompt({
type: 'input',
name: 'name',
message: 'Your project name',
//Defaults to the project's folder name if the input is skipped
default: this.appname
}, function(answers) {
this.props = answers
this.log(answers.name);
done();
}.bind(this));
},
//Writing Logic here
We are using the async()
method to make sure that the function does not exit before the actual work gets completed.
我们正在使用async()
方法来确保该函数在实际工作完成之前不会退出。
The prompt
method is used to get the user's input which is of type input
as there are other types.
prompt
方法用于获取用户输入,该input
是其他类型的 input
。
The name
property of the prompt is used to access the field's value from the project and the message
is the instruction. We also want it to default to the folder name if we do not provide a name while scaffolding.
提示的name
属性用于从项目中访问该字段的值,并且message
为指令。 如果我们在脚手架上不提供名称,我们也希望它默认为文件夹名称。
The second argument of prompt
is a callback which holds the values from the prompt(s). We have set this.props
up with the answers so it can be accessed from the "writing" logic.
prompt
的第二个参数是一个回调,其中包含提示中的值。 我们已经为this.props
设置了答案,以便可以从“编写”逻辑中访问它。
步骤3:使用模板脚手架 ( STEP 3: Scaffolding With Templates )
Template files are files generated when you use Yeoman with your custom generator to generate a project. These files are located within the templates
folder in the app
directory. It works like common view engines like EJS. You can dynamically fill in data into the templates with the inputs that were generated. We will use a basic MEAN folder structure for our templates:
模板文件是将Yeoman与自定义生成器一起使用以生成项目时生成的文件。 这些文件位于app
目录中的templates
文件夹中。 它像EJS这样的通用视图引擎一样工作。 您可以使用生成的输入将数据动态填写到模板中。 我们将为模板使用基本的MEAN文件夹结构:
|---public
|-----css
|-----app.css
|-----js
|-----app.js
|---routes
|-----all.js
|---model
|-----todo.js
|---views
|-----index.ejs
|---server.js
|---bower.json
|---package.json
|---etc
Just like a normal application, replace the above project structure with the contents of the templates
folder and then we will gradually populate them in a stepwise manner or create them along the process. All template logic are wrapped inside the writing
object in index.js:
就像普通的应用程序一样,将上面的项目结构替换为templates
文件夹的内容,然后我们将逐步地逐步填充它们,或者在此过程中创建它们。 所有模板逻辑都包装在index.js中的writing
对象内:
//Writing Logic here
writing: {
//Copy the configuration files
//Copy application files
//Install Dependencies
},
The files to be generated are better classified into configuration files and application files. The config files include package.json
, bower.json
, and other utility files for our project while the app files include server.js
, our routes, public directory and every other major files.
要生成的文件可以更好地分为配置文件和应用程序文件。 配置文件包括package.json
, bower.json
和用于我们项目的其他实用程序文件,而应用程序文件包括server.js
,我们的路由,公共目录以及所有其他主要文件。
We will first create the configuration files templates and then copy them. To create the files, open app/templates/_package.json
and replace with:
我们将首先创建配置文件模板,然后将其复制。 要创建文件,请打开app/templates/_package.json
并替换为:
{
"name": "<%= name %>",
"scripts": {
"start": "node server.js"
},
"dependencies": {
"express": "^4.13.3",
"ejs": "^2.3.4",
"body-parser": "^1.14.0",
"cookie-parser": "~1.0.1",
"mongoose":"^4.2.4"
}
}
Also replace app/templates/_bower.json
with:
app/templates/_bower.json
为:
{
"name": "<%= name %>",
"version": "0.0.0",
"dependencies": {
"bootstrap": "3.3.5",
"angular": "1.4.6"
}
}
Now create a bowerrc
in app/templates/
and fill it with:
现在在app/templates/
创建一个bowerrc
并填充:
{
"directory" : "app/public/libs"
}
Update app/index.js
:
更新app/index.js
:
//Writing Logic here
writing: {
//Copy the configuration files
config: function () {
this.fs.copyTpl(
this.templatePath('_package.json'),
this.destinationPath('package.json'), {
name: this.props.name
}
);
this.fs.copyTpl(
this.templatePath('_bower.json'),
this.destinationPath('bower.json'), {
name: this.props.name
}
);
this.fs.copy(
this.templatePath('bowerrc'),
this.destinationPath('.bowerrc')
);
},
//Copy application files
//Install Dependencies
},
Two methods, copy()
and copyTpl()
are used to the copy the templates from our template path to a destination path which Yeoman abstracts from us the implementation but helps determine the path we intend to copy the files to.
copy()
和copyTpl()
这两种方法用于将模板从我们的模板路径复制到目标路径,Yeoman从我们的实现中抽象出该路径,但有助于确定我们要将文件复制到的路径。
The difference between the two methods is that copyTpl()
takes a third parameter which is a list of data to be bound to the template file after it is generated while copy()
is used when there are no bindings required in the template.
两种方法之间的区别在于, copyTpl()
采用第三个参数,该参数是在生成模板文件后要绑定到模板文件的数据的列表,而当模板中不需要绑定时使用copy()
。
Now we have the setup files prepared and we can now go ahead and create the application files. I suggest we begin with the server.js
as it is our entry point:
现在我们已经准备好安装文件,现在可以继续创建应用程序文件了。 我建议我们从server.js
开始,因为它是我们的切入点:
//Dependencies
var express = require('express');
var app = express();
var path = require('path');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var mongoose = require('mongoose');
//Create a mongoose connection
mongoose.connect('mongodb://127.0.0.1:27017/scotchmean');
//Load custom dependencies
var routes = require('./routes/all');
//View engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
//Configure Body Pareser and Cookie Parser
app.use(bodyParser.json());
app.use(bodyParser.urlencoded());
app.use(cookieParser());
//Use the public folder for static files
app.use(express.static(path.join(__dirname, 'public')));
console.log(routes.getTodo)
//Create the routes
app.get('/', routes.index);
app.get('/todo', routes.getTodo);
app.post('/todo', routes.postTodo);
//Set port to env.Port or default to 8080
app.set('port', process.env.PORT || 8080);
//Listen to port for connections
app.listen(app.get('port'), function() {
console.log('App listening at port ' + app.get('port'));
});
The above snippet is a basic server file for Node. We just ended up loading routes (though yet to be implemented), configuring body and cookie parser, creating a connection to MongoDB and listening to a port.
上面的代码片段是Node的基本服务器文件。 我们刚刚结束了加载路由(尽管尚未实现),配置正文和cookie解析器,创建与MongoDB的连接以及侦听端口。
Next up is to create the routes which include: index
, getTodo
and postTodo
. Create a _routes
folder inside the templates
directory. Add _all.js
with the following content:
接下来是创建路由,包括: index
, getTodo
和postTodo
。 在templates
目录中创建一个_routes
文件夹。 添加具有以下内容的_all.js
:
//_routes/_all.js
var Todo = require('../model/todo').todo;
exports.index = function(req, res){
res.render('index');
};
exports.getTodo = function(req, res){
Todo.find().exec(function(err, todo){
if (err)
return res.send(err);
return res.json(todo);
});
};
exports.postTodo = function(req, res){
Todo.create(req.body, function(err, todo){
if (err)
return res.send(err);
return res.json(todo);
});
};
We can now create the Todo model that the route loaded. Create a _model
folder in templates
directory and add a file named _todo.js
in it with the following content:
现在,我们可以创建路线加载的Todo模型。 在templates
目录中创建一个_model
文件夹,并在其中添加一个名为_todo.js
的文件,其内容如下:
//_model/_todo.js
var mongoose = require('mongoose');
var todo = mongoose.model('Todo', {
content: String
});
exports.todo = todo;
Time to develop our views and public files. We need just one EJS view which is index
for our home page as seen in the routes. This file will live in the _views
folder in the templates
directory:
是时候发展我们的观点和公共文件了。 我们只需要一个EJS视图,该视图就是我们的主页的index
,如在路线中所示。 该文件将位于templates
目录的_views
文件夹中:
<html ng-app="app">
<body ng-controller="TodoController">
<div class="container">
<div class="col-md-4 col-md-offset-4">
<input type="text" ng-model="todo.content" class="form-control">
<button type="button" ng-click="addTodo()" class="btn btn-primary">Add</button>
<hr>
<ul>
<li ng-repeat="todo in todos">{{todo.content}}</li>
</ul>
</div>
</div>
<script src="libs/angular/angular.js"></script>
<script src="js/app.js"></script>
</body>
</html>
See how we are binding the name
value to the view in the title
. That is the most interesting part of this file. The rest is just basic HTML with Bootstrap and Angular codes.
了解我们如何将name
值绑定到title
的视图。 那是该文件最有趣的部分。 其余的只是带有Bootstrap和Angular代码的基本HTML。
Moving further, create the _public
folder with _css
and _js
directories. In the _js
folder add a file _app.js
to hold our Angular logic:
进一步,使用_css
和_js
目录创建_public
文件夹。 在_js
文件夹中添加文件_app.js
来保存我们的Angular逻辑:
angular.module('app', [])
.controller('TodoController', function($scope, $http) {
$scope.todos = [];
$scope.todo = {};
$http.get('/todo').success(function(data) {
$scope.todos = data;
});
$scope.addTodo = function() {
$http.post('/todo', $scope.todo).success(function(data) {
$scope.todos.unshift(data);
});
$scope.todo = {};
};
});
Now create a file in the _css
directory named _app.css
with this content:
现在,在_css
目录中创建一个名为_app.css
的文件,其内容如下:
body{
padding-top:50px;
}
We have created all the template files that our project need but yet to update the index.js
to be able to scaffold and serve them at project scaffold. Let us do so:
我们已经创建了项目需要的所有模板文件,但尚未更新index.js
以便能够进行脚手架并在项目脚手架上提供它们。 让我们这样做:
//Copy application files
app: function() {
//Server file
this.fs.copyTpl(
this.templatePath('_server.js'),
this.destinationPath('server.js'),
this.destinationPath('/views/index.ejs'), {
name: this.props.name
}
);
/Routes
this.fs.copy(
this.templatePath('_routes/_all.js'),
this.destinationPath('routes/all.js'));
// Model
this.fs.copy(
this.templatePath('_model/_todo.js'),
this.destinationPath('model/todo.js'));
// Views
this.fs.copyTpl(
this.templatePath('_views/_index.ejs'),
this.destinationPath('/views/index.ejs'), {
name: this.props.name
}
);
// Public/
this.fs.copy(
this.templatePath('_public/_css/_app.css'),
this.destinationPath('public/css/app.css')
);
this.fs.copy(
this.templatePath('_public/_js/_app.js'),
this.destinationPath('public/js/app.js')
);
}
},
//Install Dependencies
Finally on step 3 is to install all the dependencies once the scaffold is completed by updating index.js
to:
最后,在第3步是在完成脚手架后通过将index.js
更新为以下内容来安装所有依赖项:
//Install Dependencies
install: function() {
this.installDependencies();
}
步骤4:测试 ( STEP 4: Testing )
The most reasonable way to carry out integration test is not to publish to npm registry and then install before we can run yo scotchmean
to create a project.
进行集成测试的最合理方法是,不发布到npm注册表,然后安装,然后运行yo scotchmean
创建项目。
We can utilize npm's link
to assist us with linking the generator in the global node modules. To understand better, on the generator's project folder, run:
我们可以利用npm的link
来帮助我们在全局节点模块中链接生成器。 为了更好地理解,请在生成器的项目文件夹中运行:
npm link
Navigate to a folder you would love to scaffold a new MEAN project and run:
导航到您希望构建新的MEAN项目并运行的文件夹:
yo scotchmean
Respond to the prompts and watch Yo do its magic. Do have in mind that you do not need to keep running npm link
command to update changes before testing. Just edit and save then your project will be ready for another scaffold.
响应提示并观看Yo发挥其魔力。 请记住,您无需继续运行npm link
命令即可在测试之前更新更改。 只需编辑并保存,您的项目就可以准备使用另一个脚手架了。
When you are satisfied with what you have built, you can publish to npm.
当您对自己的构建感到满意时,可以将其发布到npm。
结论 ( Conclusion )
Hopefully, you can start making your life easier with Yeoman and it's generators. It doesn't end with a basic example; try something crazy, publish on npm, share on GitHub and earn some credits. Feel free to leave a comment below and Scotch will always give you all the attention yo deserve.
希望您可以开始使用Yeoman及其发电机来简化生活。 它不以一个基本的例子结尾。 尝试一些疯狂的事情,在npm上发布,在GitHub上共享并获得一些积分。 随时在下面发表评论,苏格兰威士忌将始终为您提供应有的关注。
翻译自: https://scotch.io/tutorials/create-a-custom-yeoman-generator-in-4-easy-steps
yeoman