├── generate.sh ├── bigview-cli ├── README.md ├── tpl │ └── pagelet │ │ ├── index.html │ │ └── index.js ├── package.json └── index.js ├── src ├── gen.xxx ├── gen.tpl ├── package.json └── gen.js ├── .cnode.json ├── package.json ├── .gitignore ├── gulpfile.js └── README.md /generate.sh: -------------------------------------------------------------------------------- 1 | cp -rf images preview/ 2 | tocmd_conf -f README.md 3 | -------------------------------------------------------------------------------- /bigview-cli/README.md: -------------------------------------------------------------------------------- 1 | # bigview-cli 2 | 3 | ``` 4 | $ npm i -g bigview-cli 5 | ``` 6 | 7 | 8 | 9 | bpm a b c d -------------------------------------------------------------------------------- /bigview-cli/tpl/pagelet/index.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/gen.xxx: -------------------------------------------------------------------------------- 1 | module.exports = class book { 2 | 3 | name: string, 4 | 5 | coordinates: string, 6 | 7 | } -------------------------------------------------------------------------------- /src/gen.tpl: -------------------------------------------------------------------------------- 1 | module.exports = class {{ model }} { 2 | {% for k,v in attr %} 3 | {{k}}: {{v}}, 4 | {% else %} 5 | error 6 | {% endfor %} 7 | } -------------------------------------------------------------------------------- /.cnode.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "零基础十分钟教你用Node.js写生成器:你只需要5步", 3 | "file": "README.md", 4 | "tab": "share", 5 | "topic_id": "584812863ebad99b336b1e67" 6 | } -------------------------------------------------------------------------------- /src/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "a", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "bin": { 7 | "gen": "gen.js" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "keywords": [], 13 | "author": "", 14 | "license": "ISC", 15 | "dependencies": { 16 | "nunjucks": "^3.0.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-http-practice", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "start": "npm demo/bin/www" 7 | }, 8 | "dependencies": { 9 | }, 10 | "devDependencies": { 11 | "gulp": "^3.8.10", 12 | "gulp-gh-pages": "^0.4.0", 13 | "gulp-open": "^0.3.1", 14 | "gulp-rename": "^1.2.0", 15 | "shelljs": "^0.3.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /bigview-cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bigview-cli", 3 | "version": "1.1.11", 4 | "description": "", 5 | "main": "index.js", 6 | "bin": { 7 | "bpm": "index.js", 8 | "bigview": "index.js" 9 | }, 10 | "scripts": { 11 | "test": "echo \"Error: no test specified\" && exit 1" 12 | }, 13 | "keywords": [], 14 | "author": "", 15 | "license": "ISC", 16 | "dependencies": { 17 | "mkdirp": "^0.5.1", 18 | "tpl_apply": "^1.0.5" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /bigview-cli/tpl/pagelet/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Pagelet = require('biglet') 4 | 5 | module.exports = class MyPagelet extends Pagelet { 6 | constructor () { 7 | super() 8 | this.root = __dirname 9 | this.name = '{{name}}' 10 | this.data = {t: "测试" } 11 | this.selector = '{{name}}' 12 | this.location = '{{name}}' 13 | this.tpl = 'index.html' 14 | this.delay = 0 15 | } 16 | 17 | fetch () { 18 | let self = this 19 | return new Promise(function(resolve, reject){ 20 | setTimeout(function() { 21 | // self.owner.end() 22 | resolve(self.data) 23 | }, self.delay) 24 | }) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | # Compiled binary addons (http://nodejs.org/api/addons.html) 24 | build/Release 25 | 26 | # Dependency directory 27 | node_modules 28 | 29 | # Optional npm cache directory 30 | .npm 31 | 32 | # Optional REPL history 33 | .node_repl_history 34 | 35 | preview 36 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var gp_deploy = require('gulp-gh-pages'); 3 | var open = require("gulp-open"); 4 | var rename = require("gulp-rename"); 5 | require('shelljs/global'); 6 | 7 | var options = {} 8 | gulp.task('deploy', function () { 9 | return gulp.src('./preview/**/*') 10 | .pipe(gp_deploy(options)); 11 | }); 12 | 13 | gulp.task('rename',function () { 14 | if (exec('cp ./preview/README.html ./preview/index.html').code !== 0) { 15 | echo('Error: rename exec failed'); 16 | exit(1); 17 | } 18 | }); 19 | 20 | gulp.task('generate',function () { 21 | // Run external tool synchronously 22 | if (exec('sh ./generate.sh').code !== 0) { 23 | echo('Error: generate.sh exec failed'); 24 | exit(1); 25 | } 26 | }); 27 | 28 | gulp.task('show',['generate'] ,function () { 29 | console.log('show'); 30 | }); 31 | 32 | gulp.task('default',['generate', 'rename', 'deploy'] ,function () { 33 | console.log('default'); 34 | }); -------------------------------------------------------------------------------- /src/gen.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var fs = require('fs') 4 | var nunjucks = require('nunjucks') 5 | var argv = process.argv; 6 | var filePath = __dirname; 7 | var currentPath = process.cwd(); 8 | // 9 | // console.log(filePath) 10 | // console.log(currentPath) 11 | 12 | // cli parse 13 | argv.shift() 14 | argv.shift() 15 | console.log(argv) 16 | 17 | var data = { 18 | model: argv[0], 19 | attr:{ 20 | 21 | } 22 | } 23 | 24 | for(var i = 1; i < argv.length; i++) { 25 | var arr = argv[i].split(':') 26 | var k = arr[0]; 27 | var v = arr[1]; 28 | 29 | data.attr[k] = v 30 | } 31 | console.log('data = ') 32 | console.dir(data) 33 | 34 | // read tpl 35 | var tpl = fs.readFileSync(filePath + '/gen.tpl').toString() 36 | 37 | console.dir(data) 38 | 39 | // tpl compile 40 | var compiledData = nunjucks.renderString(tpl, data) 41 | 42 | console.log(compiledData) 43 | 44 | // write file 45 | fs.writeFileSync(currentPath + '/gen.xxx', compiledData) 46 | -------------------------------------------------------------------------------- /bigview-cli/index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var fs = require("fs") 4 | var argv = process.argv; 5 | argv.shift(); 6 | 7 | var file_path = __dirname; 8 | var current_path = process.cwd(); 9 | 10 | var tpl_apply = require('tpl_apply') 11 | 12 | for (var i = 1; i < argv.length; i++) { 13 | // console.log(argv[i]) 14 | var moduleName = argv[i] 15 | generatePageletModule (moduleName) 16 | } 17 | 18 | // 19 | function generatePageletModule (moduleName) { 20 | var files = fs.readdirSync(file_path + "/tpl/pagelet") 21 | // console.log(files) 22 | for(var i in files){ 23 | var file = files[i] 24 | gOne(file, moduleName) 25 | } 26 | } 27 | 28 | function gOne(tpl, moduleName) { 29 | if (/^\./.test(tpl)) return 30 | // console.log(tpl) 31 | var mkdirp = require('mkdirp'); 32 | 33 | var source = file_path + "/tpl/pagelet/" + tpl 34 | var destDir = process.cwd() + "/" + moduleName 35 | var dest = destDir + "/" + tpl 36 | 37 | mkdirp(destDir, function (err) { 38 | if (err) console.error(err) 39 | else console.log('generate ' + dest) 40 | 41 | tpl_apply.tpl_apply(source, { 42 | tpl: tpl, 43 | name: moduleName 44 | }, dest); 45 | }); 46 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 零基础十分钟教你用Node.js写生成器:你只需要5步 2 | 3 | 用Node.js写生成器是件非常简单的事儿,原因是 4 | 5 | - Node.js模块开发简单,js语法,而且二进制cli模块也极其简单 6 | - Npm发布包是所有开源的包管理器里最简单好用的 7 | - 辅助模块多,将近40万个左右 8 | 9 | 所以为了让大家零基础十分钟搞定生成器,这里精简一下,你只需要5步 10 | 11 | 1. 初始化模块 12 | 1. cli二进制模块 13 | 1. 模板引擎使用 14 | 1. 解析cli参数和路径 15 | 1. npm发布 16 | 17 | 这里假定你已经安装了Node.js,至于是什么版本,如何安装的并不重要。 18 | 19 | 先要介绍一下,什么是Npm? 20 | 21 | https://www.npmjs.com/ 22 | 23 | npm is the package manager for 24 | 25 | - browsers 26 | - javascript 27 | - nodejs 28 | - io.js 29 | - mobile 30 | - bower 31 | - docpad 32 | - test 33 | 34 | 简单理解:NPM(node package manager),通常称为node包管理器。顾名思义,它的主要功能就是管理node包,包括:安装、卸载、更新、查看、搜索、发布等。只要安装了Node.js,它会默认安装的。 35 | 36 | 它可不只是Node.js package manager,可见其定位是很广的,这从侧面也佐证了大前端和node全栈的机会。以前nodejs吹牛都是那异步说事儿,现在都是拿生态说事儿,这话不错,在09年谈异步,很多语言性能都很弱,但事情要以发展的眼光看,现在很多语言都支持了,而且性能还不错,所以才显得nodejs性能没那么突出。 37 | 38 | 无论做哪方面工作都可以使用npm,所以使用Node.js来开发各种模块都是非常方便的。 39 | 40 | ## 1)初始化模块 41 | 42 | 确认模块名称 43 | 44 | ``` 45 | $ npm info xxxxxx 46 | ``` 47 | 48 | 如果没有找到对应的包,说明你可以使用这个名字,然后在github建立仓库,clone到本地即可 49 | 50 | ``` 51 | $ git clone xxx 52 | $ npm init -y 53 | ``` 54 | 55 | 生成package.json文件,此文件为模块的描述文件,非常重要。 56 | 57 | ``` 58 | { 59 | "name": "a", 60 | "version": "1.0.0", 61 | "description": "", 62 | "main": "index.js", 63 | "scripts": { 64 | "test": "echo \"Error: no test specified\" && exit 1" 65 | }, 66 | "keywords": [], 67 | "author": "", 68 | "license": "ISC" 69 | } 70 | ``` 71 | 72 | 说明 73 | 74 | - main是模块的入口文件,即普通代码对外提供调用的api入口。 75 | - scripts是npm scripts非常便利的,只要在package.json所在目录下,你执行npm test就会调用这里的test配置。如果是test,start等内置命令之外的,可以通过`npm run xxx`来定义 76 | 77 | ## 2)cli二进制模块 78 | 79 | Node.js分2种模块 80 | 81 | - 普通模块,供代码调用 82 | - 二进制模块,提供cli调用 83 | 84 | 大家都知道,生成器是cli工具,所以我们应该使用cli二进制模块 85 | 86 | 手动修改package.json文件 87 | 88 | ``` 89 | { 90 | "name": "a", 91 | "version": "1.0.0", 92 | "description": "", 93 | "main": "index.js", 94 | "bin": { 95 | "gen": "gen.js" 96 | }, 97 | "scripts": { 98 | "test": "echo \"Error: no test specified\" && exit 1" 99 | }, 100 | "keywords": [], 101 | "author": "", 102 | "license": "ISC" 103 | } 104 | ``` 105 | 106 | 这里主要增加里一个`bin`的配置,bin里的`gen`为cli的具体命令,它的具体执行的文件gen.js,大家看到这是一个plain old object类型,所以可以配置多个命令的,各位可以按照自己的喜好来。 107 | 108 | 既然`gen`的执行文件是`gen.js`,我们当然需要创建创建它 109 | 110 | ``` 111 | $ touch gen.js 112 | ``` 113 | 114 | 填写 115 | 116 | ``` 117 | #!/usr/bin/env node 118 | 119 | var argv = process.argv; 120 | var filePath = __dirname; 121 | var currentPath = process.cwd(); 122 | 123 | console.log(argv) 124 | console.log(filePath) 125 | console.log(currentPath) 126 | 127 | ``` 128 | 129 | 说明 130 | 131 | - argv是命令行的参数 132 | - filePath是当前文件的路径,也就是以后安装后文件的路径,用于存放模板文件非常好 133 | - currentPath是当前shell上下文路径,也就是生成器要生成文件的目标位置 134 | 135 | 至此,二进制模块的代码就写完了,下面我们测一下 136 | 137 | 1)本地安装此模块 138 | 139 | 在package.json文件路径下,执行 140 | 141 | ``` 142 | $ npm link 143 | 144 | /Users/sang/.nvm/versions/node/v4.4.5/bin/gen -> /Users/sang/.nvm/versions/node/v4.4.5/lib/node_modules/a/gen.js 145 | /Users/sang/.nvm/versions/node/v4.4.5/lib/node_modules/a -> /Users/sang/workspace/github/i5ting/a 146 | ``` 147 | 148 | 此时说明已经安装成功了。 149 | 150 | 2)执行gen测试 151 | 152 | ``` 153 | $ gen 154 | [ '/Users/sang/.nvm/versions/node/v4.4.5/bin/node', 155 | '/Users/sang/.nvm/versions/node/v4.4.5/bin/gen' ] 156 | /Users/sang/workspace/github/i5ting/a 157 | /Users/sang/workspace/github/i5ting/a 158 | ``` 159 | 160 | 可以换不同的目录来测试一下,看看结果的不同,来体会上面3个变量的妙用。 161 | 162 | ## 3)模板引擎使用 163 | 164 | 模板引擎是一种复用思想,通过定义模板,用的时候和数据一起编译,生成html,以便浏览器渲染。从这个定义里我们可以找出几个关键点 165 | 166 | > 编译(模板 + 数据) => html 167 | 168 | 模板引擎有好多种,下面介绍2种典型的模板引擎 169 | 170 | - ejs:嵌入js语法的模板引擎(e = embed),类似于jsp,asp,erb的,在html里嵌入模板特性,如果熟悉html写起来就非常简单,只要区分哪些地方是可变,哪些地方是不变即可 171 | - jade:缩进式极简写法的模板引擎,发展历史 HAML -> Jade -> Slim -> Slm,最早是ruby里有的,目前以jade用的最多,这种写法虽好,,但需要大脑去转换,这其实是比较麻烦的,如果对html不是特别熟悉,这种思维转换是非常难受的。 172 | 173 | 更多见 https://github.com/tj/consolidate.js#supported-template-engines 174 | 175 | 这里我们选一个,目前Node.js里最火的应该也是最好的[Nunjucks](https://mozilla.github.io/nunjucks/),我感觉它和ejs比较像,但跟jade一样强大,语法据说出自Python的某款模板引擎 176 | 177 | ``` 178 | $ npm install --save nunjucks 179 | ``` 180 | 181 | 然后我们修改模板引擎 182 | 183 | ``` 184 | #!/usr/bin/env node 185 | 186 | // var argv = process.argv; 187 | // var filePath = __dirname; 188 | // var currentPath = process.cwd(); 189 | // 190 | // console.log(argv) 191 | // console.log(filePath) 192 | // console.log(currentPath) 193 | 194 | var nunjucks = require('nunjucks') 195 | 196 | var compiledData = nunjucks.renderString('Hello {{ username }}', { username: 'James' }); 197 | 198 | console.log(compiledData) 199 | ``` 200 | 201 | 注释一下前面说的3个变量,这里我们只看nunjucks代码。这是最简单的demo。 202 | 203 | - 1)引入nunjucks模块 204 | - 2)nunjucks.renderString方法是编译模板用的,它有2个参数 205 | - 第一个是模板字符串 206 | - 第二个是json数据 207 | - 3)compiledData就是编译后的结果 208 | 209 | 结合上面说的模板引擎原理, 210 | 211 | > 编译(模板 + 数据) => html 212 | 213 | 再理解一下,效果会更好。 214 | 215 | 但是这样看来对我们没啥用啊,生成器的内容总不能都写到字符串里吧?所以继续改造,把模板独立出去,然后通过文件读写来获取模板字符串。 216 | 217 | 创建一个gen.tpl,内容为`Hello {{ username }}`,下面我们看看如何修改gen.js来读取模板。 218 | 219 | ``` 220 | var fs = require('fs') 221 | var nunjucks = require('nunjucks') 222 | 223 | var tpl = fs.readFileSync('./gen.tpl').toString() 224 | 225 | var compiledData = nunjucks.renderString(tpl, { username: 'James' }); 226 | 227 | console.log(compiledData) 228 | ``` 229 | 230 | - 1) 引入fs模块,因为要读取文件 231 | - 2)fs.readFileSync('./gen.tpl').toString(),使用了一个读取文件的同步方法,并把文件内容转成字符串,原来是buffer 232 | 233 | 读文件还是挺简单吧。那么写文件呢? 234 | 235 | ``` 236 | fs.writeFileSync('./gen.xxx', compiledData) 237 | ``` 238 | 239 | 至此,一个生成器的模型就出来 240 | 241 | ``` 242 | #!/usr/bin/env node 243 | 244 | var fs = require('fs') 245 | var nunjucks = require('nunjucks') 246 | 247 | var tpl = fs.readFileSync('./gen.tpl').toString() 248 | 249 | var compiledData = nunjucks.renderString(tpl, { username: 'James' }); 250 | 251 | console.log(compiledData) 252 | 253 | fs.writeFileSync('./gen.xxx', compiledData) 254 | ``` 255 | 256 | 思考一下,可变得有哪些? 257 | 258 | - './gen.tpl'是输入模板 259 | - { username: 'James' } 要编译的数据 260 | - './gen.xxx'是最终的输出 261 | 262 | 那么,剩下的事儿就是围绕可变得内容来构造你想要的功能。 263 | 264 | ## 4)解析cli参数和路径 265 | 266 | 要说生成器,最经典的是rails的scaffold,曾经缔造了一个15分钟blog的神话 267 | 268 | ``` 269 | $ rails g book name:string coordinates:string 270 | ``` 271 | 272 | 如果我们要实现它,怎么做呢? 273 | 274 | - rails g是固定的用于生成的命令 275 | - book是模型名称,俗称表名 276 | - 而name和coordinates都是字段名称,string是表中的类型 277 | 278 | 可变的只有表名和字段信息。所以只要解析到这些就够了,换成我们的gen命令,大概是这样 279 | 280 | ``` 281 | $ gen book name:string coordinates:string 282 | ``` 283 | 284 | 修改gen.js代码 285 | 286 | ``` 287 | #!/usr/bin/env node 288 | 289 | var argv = process.argv; 290 | console.log(argv) 291 | ``` 292 | 293 | 执行gen命令的结果是 294 | 295 | ``` 296 | $ gen book name:string coordinates:string 297 | [ '/Users/sang/.nvm/versions/node/v4.4.5/bin/node', 298 | '/Users/sang/.nvm/versions/node/v4.4.5/bin/gen', 299 | 'book', 300 | 'name:string', 301 | 'coordinates:string' ] 302 | ``` 303 | 304 | 下面构造一个entity对象 305 | 306 | ``` 307 | var argv = process.argv; 308 | 309 | argv.shift() 310 | argv.shift() 311 | console.log(argv) 312 | 313 | var data = { 314 | model: argv[0], 315 | attr:{ 316 | 317 | } 318 | } 319 | 320 | for(var i = 1; i < argv.length; i++) { 321 | var arr = argv[i].split(':') 322 | var k = arr[0]; 323 | var v = arr[1]; 324 | 325 | data.attr[k] = v 326 | } 327 | 328 | console.dir(data) 329 | 330 | ``` 331 | 332 | 执行 333 | 334 | ``` 335 | $ gen book name:string coordinates:string 336 | [ 'book', 'name:string', 'coordinates:string' ] 337 | data = { 338 | model: 'book', 339 | attr: { 340 | name: 'string', 341 | coordinates: 'string' 342 | } 343 | } 344 | ``` 345 | 346 | 那这里的data可以做什么呢?想想模板引擎里的第二个参数~ 347 | 348 | ``` 349 | // tpl compile 350 | var compiledData = nunjucks.renderString(tpl, data) 351 | ``` 352 | 353 | 修改模板gen.tpl 354 | 355 | ``` 356 | module.exports = class {{ model }} { 357 | {% for k,v in attr %} 358 | {{k}}: {{v}}, 359 | {% else %} 360 | error 361 | {% endfor %} 362 | } 363 | ``` 364 | 365 | 结果gen.xxx为 366 | 367 | ``` 368 | module.exports = class book { 369 | 370 | name: string, 371 | 372 | coordinates: string, 373 | 374 | } 375 | ``` 376 | 377 | 这里是只是示意,具体当按照你想要的结果为准。 378 | 379 | ``` 380 | #!/usr/bin/env node 381 | 382 | var fs = require('fs') 383 | var nunjucks = require('nunjucks') 384 | var argv = process.argv; 385 | // var filePath = __dirname; 386 | // var currentPath = process.cwd(); 387 | // 388 | // console.log(filePath) 389 | // console.log(currentPath) 390 | 391 | // cli parse 392 | argv.shift() 393 | argv.shift() 394 | console.log(argv) 395 | 396 | var data = { 397 | model: argv[0], 398 | attr:{ 399 | 400 | } 401 | } 402 | 403 | for(var i = 1; i < argv.length; i++) { 404 | var arr = argv[i].split(':') 405 | var k = arr[0]; 406 | var v = arr[1]; 407 | 408 | data.attr[k] = v 409 | } 410 | console.log('data = ') 411 | console.dir(data) 412 | 413 | // read tpl 414 | var tpl = fs.readFileSync('./gen.tpl').toString() 415 | 416 | console.dir(data) 417 | 418 | // tpl compile 419 | var compiledData = nunjucks.renderString(tpl, data) 420 | 421 | console.log(compiledData) 422 | 423 | // write file 424 | fs.writeFileSync('./gen.xxx', compiledData) 425 | 426 | ``` 427 | 428 | 下面修改一下路径 429 | 430 | - tpl从__dirname走 431 | - 而结果需要写到process.cwd() 432 | 433 | 也就是我们前面说的那2个没有用到的变量filePath和currentPath。 434 | 435 | ``` 436 | #!/usr/bin/env node 437 | 438 | var fs = require('fs') 439 | var nunjucks = require('nunjucks') 440 | var argv = process.argv; 441 | var filePath = __dirname; 442 | var currentPath = process.cwd(); 443 | // 444 | // console.log(filePath) 445 | // console.log(currentPath) 446 | 447 | // cli parse 448 | argv.shift() 449 | argv.shift() 450 | console.log(argv) 451 | 452 | var data = { 453 | model: argv[0], 454 | attr:{ 455 | 456 | } 457 | } 458 | 459 | for(var i = 1; i < argv.length; i++) { 460 | var arr = argv[i].split(':') 461 | var k = arr[0]; 462 | var v = arr[1]; 463 | 464 | data.attr[k] = v 465 | } 466 | console.log('data = ') 467 | console.dir(data) 468 | 469 | // read tpl 470 | var tpl = fs.readFileSync(filePath + '/gen.tpl').toString() 471 | 472 | console.dir(data) 473 | 474 | // tpl compile 475 | var compiledData = nunjucks.renderString(tpl, data) 476 | 477 | console.log(compiledData) 478 | 479 | // write file 480 | fs.writeFileSync(currentPath + '/gen.xxx', compiledData) 481 | 482 | ``` 483 | 484 | 至此,完成了所有代码。此时你在任意目录输入 485 | 486 | ``` 487 | $ gen book name:string coordinates:string 488 | ``` 489 | 490 | 你会发现当前目录下会有一个gen.xxx文件,和我们之前看到的结果一样。 491 | 492 | ## 5)npm发布 493 | 494 | 在package.json目录里执行 495 | 496 | ``` 497 | $ npm publish . 498 | ``` 499 | 500 | 就可以了发布成功了。 501 | 502 | 如果你想增加版本号,再次发布,你需要2步 503 | 504 | ``` 505 | $ npm version patch 506 | $ npm publish . 507 | ``` 508 | 509 | 你可以自己测试一下 510 | 511 | ``` 512 | $ npm i -g xxxxxx 513 | ``` 514 | 515 | share给别人吧 516 | 517 | ## 更多 518 | 519 | - 异常:各种可能考虑到并处理 520 | - 测试:按照各位喜好 mocha, ava, jest 521 | - 工具模块:比如使用debug模块处理调试信息,日志等 522 | - argv解析模块:commander 或者yargs 523 | - 实用工具,比如各种大小写转换,驼峰式等 inflected 524 | 525 | ## 实例 526 | 527 | ### 关于bigview-cli 528 | 529 | 最开始版本的生成器,期望安装如下 530 | 531 | ``` 532 | $ npm i -g bigview-cli 533 | ``` 534 | 535 | 执行 536 | 537 | ``` 538 | $ bpm a b c d 539 | ``` 540 | 541 | 这样就可以同时生成多个模块了。 542 | 543 | ### 初始化 544 | 545 | package.json增加mkdirp和tpl_apply模块 546 | 547 | ``` 548 | { 549 | "name": "bigview-cli", 550 | "version": "1.1.11", 551 | "description": "", 552 | "main": "index.js", 553 | "bin": { 554 | "bpm": "index.js", 555 | "bigview": "index.js" 556 | }, 557 | "scripts": { 558 | "test": "echo \"Error: no test specified\" && exit 1" 559 | }, 560 | "keywords": [], 561 | "author": "", 562 | "license": "ISC", 563 | "dependencies": { 564 | "mkdirp": "^0.5.1", 565 | "tpl_apply": "^1.0.5" 566 | } 567 | } 568 | 569 | ``` 570 | 571 | 说明 572 | 573 | - mkdirp是无限级创建目录的库 574 | - tpl_apply是我写的基于handlebar模块的库 575 | 576 | ### index.js 577 | 578 | ``` 579 | #!/usr/bin/env node 580 | 581 | var fs = require("fs") 582 | var argv = process.argv; 583 | argv.shift(); 584 | 585 | var file_path = __dirname; 586 | var current_path = process.cwd(); 587 | 588 | var tpl_apply = require('tpl_apply') 589 | 590 | // 根据bpm a b c d 591 | // 获得abcd并创建目录,生产模板文件 592 | for (var i = 1; i < argv.length; i++) { 593 | // console.log(argv[i]) 594 | var moduleName = argv[i] 595 | generatePageletModule (moduleName) 596 | } 597 | 598 | // 生产单个Pagelet模块 599 | function generatePageletModule (moduleName) { 600 | // 读取/tpl/pagelet目录里的所有文件(目前只有一个层级,所以足够了) 601 | var files = fs.readdirSync(file_path + "/tpl/pagelet") 602 | // console.log(files) 603 | for(var i in files){ 604 | var file = files[i] 605 | // 根据模板文件,生产新的模块的文件 606 | gOne(file, moduleName) 607 | } 608 | } 609 | 610 | // 根据模板文件,生产新的模块的文件 611 | function gOne(tpl, moduleName) { 612 | if (/^\./.test(tpl)) return 613 | // console.log(tpl) 614 | var mkdirp = require('mkdirp'); 615 | 616 | var source = file_path + "/tpl/pagelet/" + tpl 617 | var destDir = process.cwd() + "/" + moduleName 618 | var dest = destDir + "/" + tpl 619 | 620 | // 先创建模块所需要的目录,不然无法写入文件的 621 | mkdirp(destDir, function (err) { 622 | if (err) console.error(err) 623 | else console.log('generate ' + dest) 624 | 625 | // 使用tpl_apply根据模板文件和数据 626 | // 生成dest目标文件 627 | tpl_apply.tpl_apply(source, { 628 | tpl: tpl, 629 | name: moduleName 630 | }, dest); 631 | }); 632 | } 633 | 634 | ``` 635 | 636 | tpl_apply还有更高级的用法,用到自己查 637 | 638 | ### 关于模板 639 | 640 | handlebars的模板是非常简单的,上面传的可变数据是 641 | 642 | ``` 643 | { 644 | tpl: tpl, 645 | name: moduleName 646 | } 647 | ``` 648 | 649 | 有变量name,所以在模板中,使用{{name}}即可 650 | 651 | ``` 652 | 'use strict' 653 | 654 | const Pagelet = require('biglet') 655 | 656 | module.exports = class MyPagelet extends Pagelet { 657 | constructor () { 658 | super() 659 | this.root = __dirname 660 | this.name = '{{name}}' 661 | this.data = {t: "测试" } 662 | this.selector = '{{name}}' 663 | this.location = '{{name}}' 664 | this.tpl = 'index.html' 665 | this.delay = 0 666 | } 667 | 668 | fetch () { 669 | let self = this 670 | return new Promise(function(resolve, reject){ 671 | setTimeout(function() { 672 | // self.owner.end() 673 | resolve(self.data) 674 | }, self.delay) 675 | }) 676 | } 677 | } 678 | 679 | ``` 680 | 681 | ### 总结 682 | 683 | 整体来说,还是一个非常实用的模块,而且比较简单,适合入门。如果说浪费时间的话,大概在如何确定模板的地方要思考,tpl目录里如何安排,这是业务决定的。 684 | 685 | ## 最后 686 | 687 | 生成器理论是可以生成一切内容的,那么生成能够生成器模板代码么?自己想想吧 688 | --------------------------------------------------------------------------------