├── .gitignore ├── .npmignore ├── .travis.yml ├── .vscode └── launch.json ├── LICENSE ├── README.md ├── examples └── base │ ├── app.js │ ├── components │ ├── Age.vue │ ├── box1.vue │ ├── box2.vue │ ├── iv.vue │ ├── rd1.vue │ └── rd2.vue │ ├── more.js │ ├── package-lock.json │ ├── package.json │ └── views │ ├── Master.vue │ ├── User.vue │ ├── abc.vue │ ├── base.vue │ ├── inputVueCallback.vue │ ├── more.vue │ ├── priority.vue │ └── top-bottom.vue ├── lib ├── index.js ├── parse.js └── util.js ├── package-lock.json ├── package.json └── test ├── components ├── box1.vue ├── box2.vue ├── iv.vue ├── rd1.vue └── rd2.vue ├── index.js └── views ├── abc.vue ├── base.vue ├── inputVueCallback.vue ├── more.vue ├── priority.vue ├── r1.vue └── top-bottom.vue /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .gitignore 3 | test 4 | src 5 | npm-debug.log 6 | .travis.yml 7 | build 8 | examples -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "node" 4 | cache: 5 | directories: 6 | - "node_modules" 7 | before_install: 8 | - npm i mocha -g 9 | install: 10 | - npm install 11 | branches: 12 | only: 13 | - master -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // 使用 IntelliSense 了解相关属性。 3 | // 悬停以查看现有属性的描述。 4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Launch Program", 11 | "program": "${workspaceFolder}\\examples\\base\\more.js" 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 小明爱吃瓜 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # koa-vue-view 2 | [![Build Status](https://travis-ci.org/imingyu/koa-vue-view.svg?branch=master)](https://travis-ci.org/imingyu/koa-vue-view) 3 | ![image](https://img.shields.io/npm/l/koa-vue-view.svg) 4 | [![image](https://img.shields.io/npm/v/koa-vue-view.svg)](https://www.npmjs.com/package/koa-vue-view) 5 | [![image](https://img.shields.io/npm/dt/koa-vue-view.svg)](https://www.npmjs.com/package/koa-vue-view) 6 | 7 | A Koa view engine which renders Vue components on server. 8 | 9 | > 1.x的分支/npm包支持koa1;master分支和2.x版本的npm包支持koa2。 10 | 11 | # 需求 12 | 我熟悉书写vue的代码,感觉她的语法很简洁明了,并且支持组件化;我最近在学习使用koa编写node web应用,在koa框架中渲染视图有很多选择;但是我想在koa中使用vue来渲染视图; 13 | 14 | 我在调研了vue的ssr解决方案后,感觉她很好,但是不满足我的需求,我只是想用她的语法和组件化来实现视图渲染,渲染的数据想从koa的`ctx.state`中读取,也不想前后端同用同一套路由这种方式; 15 | 16 | 所以我觉得用vue的ssr的基础部分——服务端渲染vue实例,来完成我的需求,即此中间件诞生; 17 | 18 | ## 本中间件包含功能: 19 | - 服务端渲染vue语法的视图文件 20 | - 视图文件的语法采用vue组件的编写语法 21 | - 支持vue的组件化 22 | - 支持全局数据、组件等共享 23 | 24 | > 注意:本中间件虽然支持vue组件的编写语法,但是仅会处理其中的`template`部分,其他的如`style`,`script`等部分都会原样输出 25 | 26 | ## 待添加功能: 27 | - 不应编译视图文件中template标签中的前端用的vue代码 28 | 29 | # 安装 30 | ```bash 31 | npm i -S koa-vue-view 32 | ``` 33 | 34 | # 使用 35 | ```html 36 | 37 | 57 | 58 | 59 | 64 | 65 | 66 | 67 | 74 | ``` 75 | 76 | ```javascript 77 | var path = require('path'); 78 | var Koa = require('koa'); 79 | var VueView = require('koa-vue-view'); 80 | 81 | var app = new Koa(); 82 | app.use(VueView({ 83 | methodName: 'render',//在koa ctx注册渲染视图的方法名,默认render 84 | data: { 85 | _: require('lodash'), 86 | app: { 87 | name: 'Github', 88 | version: '1.0.0' 89 | } 90 | }, 91 | methods: { 92 | add(a, b) { 93 | return a + b; 94 | } 95 | }, 96 | components: { 97 | Master: { 98 | path: path.resolve(__dirname, './views/Master.vue'), 99 | data() { 100 | this.layoutVersion = '1.0.0'; 101 | return { 102 | layoutName: 'master' 103 | } 104 | }, 105 | methods: { 106 | hight(str) { 107 | return `***${str}***`; 108 | } 109 | } 110 | }, 111 | Age: path.resolve(__dirname, './components/Age.vue') 112 | } 113 | })); 114 | 115 | app.use(ctx => { 116 | ctx.state.users = [{ 117 | name: 'Tom', 118 | age: 20 119 | }, { 120 | name: 'Alice', 121 | age: 18 122 | }]; 123 | ctx.render(path.resolve(__dirname, './views/User.vue')); 124 | /* 125 | 或者 126 | ctx.render({ 127 | path:path.resolve(__dirname, './views/User.vue'), 128 | data(){ 129 | return {name:'Github'} 130 | }, 131 | methods:{ 132 | show(){} 133 | } 134 | }); 135 | */ 136 | }) 137 | 138 | 139 | app.listen(8200); 140 | ``` 141 | 142 | # 规约 143 | - 在读取视图文件内容时,会将其内容分割为三部分:`header`、`template`、`footer`; 144 | - `template`截取自文件中第一对顶级`template`标签中的内容; 145 | - `header`截取自文件中第一对顶级`template`标签的前面内容; 146 | - `footer`截取自文件中第一对顶级`template`标签的后面内容; 147 | - **视图文件中仅允许包含一对顶级`template`标签** 148 | - **渲染视图时仅渲染`template`部分** 149 | 150 | # Options 151 | ```javascript 152 | app.use(require('koa-vue-view')(options)); 153 | ``` 154 | 可接受的options选项: 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 214 | 215 | 216 |
选项类型默认值描述
methodNamestringrender在koa ctx注册渲染视图的方法名,默认render
replaceBodybooleantrue是否使用渲染后的字符串替换ctx.body的内容
appendBodybooleanfalsereplaceBody=true时,将渲染后的字符串追加到ctx.body中还是直接赋值给ctx.body
filterHtmlfunction可指定一个函数用于过滤render之后的html字符串,ctx.body=函数返回值=过滤后的字符串
cachebooleanprocess.env.NODE_ENV === 'production'是否启用缓存,启用后仅在第一次加载视图时读取其内容,后续将从缓存中读取
rendererobjectrequire('vue-server-renderer').createRenderer()vue ssr 渲染器
dataobject|function全局共享数据对象,在所以组件和页面中都可以共享使用,如果传递的是function,则执行function的this对象指向运行的组件或者页面的vue实例
vue mixin可接受的任意选项,如:data,methods,components 212 | 将以mixin的方式,添加到每个渲染的页面的mixins中; 213 |
217 | 218 | # Render 219 | ```javascript 220 | app.use(ctx => { 221 | ctx.render(文件路径|组件配置对象).then(html=>{}) 222 | }) 223 | ``` 224 | 225 | # 更新日志 226 | > 1.x对应的是koa1适用的版本,2.x对应的是koa2对应的版本; 227 | 228 | ## 2.1.6 | 1.1.6 229 | - 解决全局组件中引用全局组件时渲染出错的问题; 230 | - 加入`filterHtml`配置项,用于过滤渲染后的html字符串 231 | 232 | ## 2.1.5 233 | - fix issues[#1](https://github.com/imingyu/koa-vue-view/issues/1) 234 | 235 | ## 1.1.2 236 | - fix issues[#1](https://github.com/imingyu/koa-vue-view/issues/1) 237 | 238 | ## 2.1.3 239 | - 核心功能实现 240 | 241 | ## 1.1.1 242 | - 核心功能实现 243 | -------------------------------------------------------------------------------- /examples/base/app.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var Koa = require('koa'); 3 | var VueView = require('../../lib/index.js'); 4 | 5 | var app = new Koa(); 6 | app.use(VueView({ 7 | methodName: 'render', 8 | data: { 9 | //_: require('lodash'), 10 | app: { 11 | name: 'Github', 12 | version: '1.0.0' 13 | } 14 | }, 15 | methods: { 16 | add(a, b) { 17 | return a + b; 18 | } 19 | }, 20 | components: { 21 | Master: { 22 | path: path.resolve(__dirname, './views/Master.vue'), 23 | data() { 24 | this.layoutVersion = '1.0.0'; 25 | return { 26 | layoutName: 'master' 27 | } 28 | }, 29 | methods: { 30 | hight(str) { 31 | return `***${str}***`; 32 | } 33 | } 34 | }, 35 | Age: path.resolve(__dirname, './components/Age.vue') 36 | } 37 | })); 38 | 39 | app.use(ctx => { 40 | ctx.state.users = [{ 41 | name: 'Tom', 42 | age: 20 43 | }, { 44 | name: 'Alice', 45 | age: 18 46 | }]; 47 | ctx.render(path.resolve(__dirname, './views/User.vue')); 48 | }) 49 | 50 | app.on('error', (err, ctx) => { 51 | ctx.body = 'sdf:' + err.message; 52 | }) 53 | 54 | app.listen(8200); -------------------------------------------------------------------------------- /examples/base/components/Age.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/base/components/box1.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/base/components/box2.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/base/components/iv.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /examples/base/components/rd1.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/base/components/rd2.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/base/more.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var Koa = require('koa'); 3 | var VueView = require('../../lib/index.js'); 4 | 5 | var app = new Koa(); 6 | app.use(VueView({ 7 | replaceBody: false, 8 | methodName: 'rd1', 9 | data: { 10 | msg: 'rd1' 11 | }, 12 | components: { 13 | com: path.resolve(__dirname, './components/rd1.vue') 14 | } 15 | })); 16 | app.use(VueView({ 17 | replaceBody: false, 18 | methodName: 'rd2', 19 | data: { 20 | msg: 'rd2' 21 | }, 22 | components: { 23 | com: path.resolve(__dirname, './components/rd2.vue') 24 | } 25 | })); 26 | app.use((ctx, next) => { 27 | return Promise.all([ctx.rd1(path.resolve(__dirname, './views/more.vue')), ctx.rd2(path.resolve(__dirname, './views/more.vue'))]).then(([h1, h2]) => { 28 | ctx.body = (h1 + '').trim() + (h2 + '').trim(); 29 | next(ctx); 30 | }).catch(error => { 31 | ctx.throw(error); 32 | }) 33 | }) 34 | 35 | app.listen(8200); -------------------------------------------------------------------------------- /examples/base/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "base", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "accepts": { 8 | "version": "1.3.4", 9 | "resolved": "http://registry.npm.taobao.org/accepts/download/accepts-1.3.4.tgz", 10 | "integrity": "sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=", 11 | "requires": { 12 | "mime-types": "2.1.17", 13 | "negotiator": "0.6.1" 14 | } 15 | }, 16 | "any-promise": { 17 | "version": "1.3.0", 18 | "resolved": "http://registry.npm.taobao.org/any-promise/download/any-promise-1.3.0.tgz", 19 | "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" 20 | }, 21 | "co": { 22 | "version": "4.6.0", 23 | "resolved": "http://registry.npm.taobao.org/co/download/co-4.6.0.tgz", 24 | "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" 25 | }, 26 | "content-disposition": { 27 | "version": "0.5.2", 28 | "resolved": "http://registry.npm.taobao.org/content-disposition/download/content-disposition-0.5.2.tgz", 29 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" 30 | }, 31 | "content-type": { 32 | "version": "1.0.4", 33 | "resolved": "http://registry.npm.taobao.org/content-type/download/content-type-1.0.4.tgz", 34 | "integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js=" 35 | }, 36 | "cookies": { 37 | "version": "0.7.1", 38 | "resolved": "http://registry.npm.taobao.org/cookies/download/cookies-0.7.1.tgz", 39 | "integrity": "sha1-fIphX1SBxhq58WyDNzG8uPZjuZs=", 40 | "requires": { 41 | "depd": "1.1.1", 42 | "keygrip": "1.0.2" 43 | } 44 | }, 45 | "debug": { 46 | "version": "3.1.0", 47 | "resolved": "http://registry.npm.taobao.org/debug/download/debug-3.1.0.tgz", 48 | "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", 49 | "requires": { 50 | "ms": "2.0.0" 51 | } 52 | }, 53 | "deep-equal": { 54 | "version": "1.0.1", 55 | "resolved": "http://registry.npm.taobao.org/deep-equal/download/deep-equal-1.0.1.tgz", 56 | "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=" 57 | }, 58 | "delegates": { 59 | "version": "1.0.0", 60 | "resolved": "http://registry.npm.taobao.org/delegates/download/delegates-1.0.0.tgz", 61 | "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" 62 | }, 63 | "depd": { 64 | "version": "1.1.1", 65 | "resolved": "http://registry.npm.taobao.org/depd/download/depd-1.1.1.tgz", 66 | "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=" 67 | }, 68 | "destroy": { 69 | "version": "1.0.4", 70 | "resolved": "http://registry.npm.taobao.org/destroy/download/destroy-1.0.4.tgz", 71 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 72 | }, 73 | "ee-first": { 74 | "version": "1.1.1", 75 | "resolved": "http://registry.npm.taobao.org/ee-first/download/ee-first-1.1.1.tgz", 76 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 77 | }, 78 | "error-inject": { 79 | "version": "1.0.0", 80 | "resolved": "http://registry.npm.taobao.org/error-inject/download/error-inject-1.0.0.tgz", 81 | "integrity": "sha1-4rPZG1Su1nLzCdlQ0VSFD6EdTzc=" 82 | }, 83 | "escape-html": { 84 | "version": "1.0.3", 85 | "resolved": "http://registry.npm.taobao.org/escape-html/download/escape-html-1.0.3.tgz", 86 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 87 | }, 88 | "fresh": { 89 | "version": "0.5.2", 90 | "resolved": "http://registry.npm.taobao.org/fresh/download/fresh-0.5.2.tgz", 91 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 92 | }, 93 | "http-assert": { 94 | "version": "1.3.0", 95 | "resolved": "http://registry.npm.taobao.org/http-assert/download/http-assert-1.3.0.tgz", 96 | "integrity": "sha1-oxpc+IyHPsu1eWkH1NbxMujAHko=", 97 | "requires": { 98 | "deep-equal": "1.0.1", 99 | "http-errors": "1.6.2" 100 | } 101 | }, 102 | "http-errors": { 103 | "version": "1.6.2", 104 | "resolved": "http://registry.npm.taobao.org/http-errors/download/http-errors-1.6.2.tgz", 105 | "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", 106 | "requires": { 107 | "depd": "1.1.1", 108 | "inherits": "2.0.3", 109 | "setprototypeof": "1.0.3", 110 | "statuses": "1.3.1" 111 | } 112 | }, 113 | "inherits": { 114 | "version": "2.0.3", 115 | "resolved": "http://registry.npm.taobao.org/inherits/download/inherits-2.0.3.tgz", 116 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 117 | }, 118 | "is-generator-function": { 119 | "version": "1.0.6", 120 | "resolved": "http://registry.npm.taobao.org/is-generator-function/download/is-generator-function-1.0.6.tgz", 121 | "integrity": "sha1-nnFlPNFf/zQcecQVFGChMdMen8Q=" 122 | }, 123 | "keygrip": { 124 | "version": "1.0.2", 125 | "resolved": "http://registry.npm.taobao.org/keygrip/download/keygrip-1.0.2.tgz", 126 | "integrity": "sha1-rTKXxVcGneqLz+ek+kkbdcXd65E=" 127 | }, 128 | "koa": { 129 | "version": "2.3.0", 130 | "resolved": "http://registry.npm.taobao.org/koa/download/koa-2.3.0.tgz", 131 | "integrity": "sha1-nh6OTaQBg5xXuFJ+rcV/dhJ1Vac=", 132 | "requires": { 133 | "accepts": "1.3.4", 134 | "content-disposition": "0.5.2", 135 | "content-type": "1.0.4", 136 | "cookies": "0.7.1", 137 | "debug": "3.1.0", 138 | "delegates": "1.0.0", 139 | "depd": "1.1.1", 140 | "destroy": "1.0.4", 141 | "error-inject": "1.0.0", 142 | "escape-html": "1.0.3", 143 | "fresh": "0.5.2", 144 | "http-assert": "1.3.0", 145 | "http-errors": "1.6.2", 146 | "is-generator-function": "1.0.6", 147 | "koa-compose": "4.0.0", 148 | "koa-convert": "1.2.0", 149 | "koa-is-json": "1.0.0", 150 | "mime-types": "2.1.17", 151 | "on-finished": "2.3.0", 152 | "only": "0.0.2", 153 | "parseurl": "1.3.2", 154 | "statuses": "1.3.1", 155 | "type-is": "1.6.15", 156 | "vary": "1.1.2" 157 | } 158 | }, 159 | "koa-compose": { 160 | "version": "4.0.0", 161 | "resolved": "http://registry.npm.taobao.org/koa-compose/download/koa-compose-4.0.0.tgz", 162 | "integrity": "sha1-KAClE9nDYe8NY4UrA45Pby1adzw=" 163 | }, 164 | "koa-convert": { 165 | "version": "1.2.0", 166 | "resolved": "http://registry.npm.taobao.org/koa-convert/download/koa-convert-1.2.0.tgz", 167 | "integrity": "sha1-2kCHXfSd4FOQmNFwC1CCDOvNIdA=", 168 | "requires": { 169 | "co": "4.6.0", 170 | "koa-compose": "3.2.1" 171 | }, 172 | "dependencies": { 173 | "koa-compose": { 174 | "version": "3.2.1", 175 | "resolved": "http://registry.npm.taobao.org/koa-compose/download/koa-compose-3.2.1.tgz", 176 | "integrity": "sha1-qFzLQLfZhtjlo0Wzoazo6rz1Tec=", 177 | "requires": { 178 | "any-promise": "1.3.0" 179 | } 180 | } 181 | } 182 | }, 183 | "koa-is-json": { 184 | "version": "1.0.0", 185 | "resolved": "http://registry.npm.taobao.org/koa-is-json/download/koa-is-json-1.0.0.tgz", 186 | "integrity": "sha1-JzwH7c3Ljfaiwat9We52SRRR7BQ=" 187 | }, 188 | "media-typer": { 189 | "version": "0.3.0", 190 | "resolved": "http://registry.npm.taobao.org/media-typer/download/media-typer-0.3.0.tgz", 191 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 192 | }, 193 | "mime-db": { 194 | "version": "1.30.0", 195 | "resolved": "http://registry.npm.taobao.org/mime-db/download/mime-db-1.30.0.tgz", 196 | "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=" 197 | }, 198 | "mime-types": { 199 | "version": "2.1.17", 200 | "resolved": "http://registry.npm.taobao.org/mime-types/download/mime-types-2.1.17.tgz", 201 | "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", 202 | "requires": { 203 | "mime-db": "1.30.0" 204 | } 205 | }, 206 | "ms": { 207 | "version": "2.0.0", 208 | "resolved": "http://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz", 209 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 210 | }, 211 | "negotiator": { 212 | "version": "0.6.1", 213 | "resolved": "http://registry.npm.taobao.org/negotiator/download/negotiator-0.6.1.tgz", 214 | "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" 215 | }, 216 | "on-finished": { 217 | "version": "2.3.0", 218 | "resolved": "http://registry.npm.taobao.org/on-finished/download/on-finished-2.3.0.tgz", 219 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 220 | "requires": { 221 | "ee-first": "1.1.1" 222 | } 223 | }, 224 | "only": { 225 | "version": "0.0.2", 226 | "resolved": "http://registry.npm.taobao.org/only/download/only-0.0.2.tgz", 227 | "integrity": "sha1-Kv3oTQPlC5qO3EROMGEKcCle37Q=" 228 | }, 229 | "parseurl": { 230 | "version": "1.3.2", 231 | "resolved": "http://registry.npm.taobao.org/parseurl/download/parseurl-1.3.2.tgz", 232 | "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" 233 | }, 234 | "setprototypeof": { 235 | "version": "1.0.3", 236 | "resolved": "http://registry.npm.taobao.org/setprototypeof/download/setprototypeof-1.0.3.tgz", 237 | "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=" 238 | }, 239 | "statuses": { 240 | "version": "1.3.1", 241 | "resolved": "http://registry.npm.taobao.org/statuses/download/statuses-1.3.1.tgz", 242 | "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=" 243 | }, 244 | "type-is": { 245 | "version": "1.6.15", 246 | "resolved": "http://registry.npm.taobao.org/type-is/download/type-is-1.6.15.tgz", 247 | "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=", 248 | "requires": { 249 | "media-typer": "0.3.0", 250 | "mime-types": "2.1.17" 251 | } 252 | }, 253 | "vary": { 254 | "version": "1.1.2", 255 | "resolved": "http://registry.npm.taobao.org/vary/download/vary-1.1.2.tgz", 256 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 257 | } 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /examples/base/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "base", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "dev": "supervisor more.js" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "koa": "^2.3.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/base/views/Master.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/base/views/User.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/base/views/abc.vue: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /examples/base/views/base.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /examples/base/views/inputVueCallback.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /examples/base/views/more.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /examples/base/views/priority.vue: -------------------------------------------------------------------------------- 1 | abc -------------------------------------------------------------------------------- /examples/base/views/top-bottom.vue: -------------------------------------------------------------------------------- 1 | abc456 -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | var Vue = require('vue'); 2 | const parse = require('./parse.js'); 3 | const util = require('./util.js'); 4 | 5 | var optionsProps = ['methodName', 'cache', 'renderer', 'appendBody', 'replaceBody', 'filterHtml'], 6 | defaultOptions = () => { 7 | return { 8 | methodName: 'render', 9 | appendBody: false, 10 | replaceBody: true, 11 | cache: process.env.NODE_ENV === 'production', 12 | renderer: require('vue-server-renderer').createRenderer() 13 | } 14 | }, 15 | cacheMap = new Map(); 16 | 17 | var createKey = () => { 18 | return (Math.random() + '').replace('0.', ''); 19 | } 20 | 21 | var parseFile = (fileName, vueKey, isCache) => { 22 | var result = isCache ? cacheMap.get(vueKey + '+' + fileName) : null; 23 | if (!result) { 24 | result = parse(fileName); 25 | if (isCache) { 26 | cacheMap.set(vueKey + '+' + fileName, result); 27 | } 28 | } 29 | return result; 30 | } 31 | 32 | var convertComponent = (spec, vueKey, isCache) => { 33 | var result = {}; 34 | if (typeof spec === 'string') { 35 | if (util.fileIsExist(spec)) { 36 | result.path = spec; 37 | result.parseResult = parseFile(spec, vueKey, isCache); 38 | result.template = result.parseResult.template; 39 | } else { 40 | result.template = spec; 41 | } 42 | } else if (typeof spec === 'object') { 43 | if (spec.converted) { 44 | result = spec; 45 | } else { 46 | Object.assign(result, spec); 47 | if (!result.template && !result.path) { 48 | result = null; 49 | } else if (!result.template) { 50 | result.parseResult = parseFile(result.path, vueKey, isCache); 51 | result.template = result.parseResult.template; 52 | } 53 | result.converted = true; 54 | } 55 | } 56 | return result; 57 | } 58 | 59 | var inlineComponents = (component, compiledComponents) => { 60 | component.components = component.components || {}; 61 | for (let name in compiledComponents) { 62 | if (!component.components[name]) { 63 | component.components[name] = compiledComponents[name]; 64 | } 65 | } 66 | } 67 | 68 | var registerComponents = (vue, vueOptions, cacheParse, isVueInstance) => { 69 | var components = vueOptions.components; 70 | delete vueOptions.components; 71 | 72 | var compiledComponents = {}; 73 | 74 | for (let name in components) { 75 | let component = convertComponent(components[name], vueOptions.key || vue.key, cacheParse); 76 | inlineComponents(component, compiledComponents); 77 | 78 | compiledComponents[name] = component; 79 | 80 | if (vueOptions.data) { 81 | var oldData = vueOptions.data; 82 | component.mixins = component.mixins || []; 83 | component.mixins = [{ 84 | data() { 85 | return typeof oldData === 'function' ? oldData.call(this) : oldData; 86 | } 87 | }].concat(component.mixins); 88 | } 89 | if (isVueInstance) { 90 | vue.$options.components[name] = component; 91 | } else { 92 | vue.component(name, component); 93 | } 94 | } 95 | } 96 | 97 | var registerVue = (vue, vueOptions, cacheParse) => { 98 | if (vueOptions.components) { 99 | registerComponents(vue, vueOptions, cacheParse); 100 | } 101 | if (vueOptions.filters) { 102 | for (let filter in vueOptions.filters) { 103 | vue.filter(filter, vueOptions.filters[filter]); 104 | } 105 | delete vueOptions.filters; 106 | } 107 | if (vueOptions.directives) { 108 | for (let directive in vueOptions.directives) { 109 | vue.directive(directive, vueOptions.directives[directive]); 110 | } 111 | delete vueOptions.directives; 112 | } 113 | } 114 | 115 | var validData = data => typeof data === 'object' && data != null && Object.keys(data).length > 0; 116 | 117 | var VueView = function (options) { 118 | var KoaVue = Vue.extend({ 119 | data() { 120 | return {} 121 | } 122 | }); 123 | 124 | if (typeof options === 'function') { 125 | options = options(KoaVue) || {}; 126 | } 127 | var mixin = Object.assign({}, defaultOptions(), options); 128 | var ops = {}; 129 | for (var prop of optionsProps) { 130 | ops[prop] = mixin[prop]; 131 | delete mixin[prop]; 132 | } 133 | 134 | if (typeof mixin.data === 'object') { 135 | var oldData = mixin.data; 136 | mixin.data = function () { 137 | return oldData; 138 | } 139 | } 140 | 141 | KoaVue.mid = 'KoaVueFor' + ops.methodName; 142 | KoaVue.key = createKey(); 143 | 144 | registerVue(KoaVue, mixin, ops.cache); 145 | 146 | return function middleware(ctx, next) { 147 | ctx[ops.methodName] = function (viewSpec) { 148 | return new Promise((resolve, reject) => { 149 | var mixins = [mixin]; 150 | if (validData(ctx.state)) { 151 | mixins.push({ 152 | data() { 153 | return ctx.state; 154 | } 155 | }); 156 | } 157 | viewSpec = convertComponent(viewSpec, KoaVue.key, ops.isCache); 158 | viewSpec.name = viewSpec.name || 'KoaVueApp'; 159 | viewSpec.mixins = viewSpec.mixins || []; 160 | viewSpec.mixins = mixins.concat(viewSpec.mixins); 161 | 162 | var inlineComponents = viewSpec.components; 163 | delete viewSpec.components; 164 | viewSpec.beforeCreate = function () { 165 | this.$$mid = KoaVue.mid; 166 | if (inlineComponents) { 167 | registerComponents(this, { 168 | key: KoaVue.key, 169 | components: inlineComponents, 170 | data: mixin.data 171 | }, ops.isCache, true); 172 | } 173 | } 174 | var instance = new KoaVue(viewSpec); 175 | 176 | ops.renderer.renderToString(instance, (err, html) => { 177 | if (err) { 178 | reject(err); 179 | } else { 180 | if (viewSpec.parseResult) { 181 | html = (viewSpec.parseResult.header || '') + html + (viewSpec.parseResult.footer || ''); 182 | } 183 | html = typeof ops.filterHtml === 'function' ? ops.filterHtml(html) : html; 184 | if (ops.replaceBody) { 185 | if (ops.appendBody) { 186 | ctx.body += html; 187 | } else { 188 | ctx.body = html; 189 | } 190 | } 191 | resolve(html); 192 | } 193 | }) 194 | }) 195 | } 196 | return next(); 197 | } 198 | } 199 | module.exports = VueView; -------------------------------------------------------------------------------- /lib/parse.js: -------------------------------------------------------------------------------- 1 | var util = require('./util.js'); 2 | 3 | var parseSpec = fileName => { 4 | var file = {}; 5 | if (typeof fileName === 'object' && fileName.path) { 6 | Object.assign(file, fileName); 7 | } else { 8 | file.path = fileName + ''; 9 | } 10 | file.template = util.readFile(file.path); 11 | file.template = file.template; 12 | file.header = file.header || ''; 13 | file.footer = file.footer || ''; 14 | return file; 15 | } 16 | 17 | module.exports = function (fileName) { 18 | var spec = parseSpec(fileName), 19 | template = spec.template, 20 | begin = ''; 22 | spec.orgTemplate = template; 23 | template = (template + '').trim(); 24 | if (template.startsWith(begin) && template.endsWith(end)) { 25 | template = template.substring(begin.length, template.lastIndexOf(end)); 26 | } else if (template.startsWith(begin)) { 27 | spec.footer += template.substring(template.lastIndexOf(end) + end.length); 28 | template = template.substring(begin.length, template.lastIndexOf(end)); 29 | } else if (template.endsWith(end)) { 30 | spec.header += template.substring(0, template.indexOf(begin)); 31 | template = template.substring(template.indexOf(begin) + begin.length, template.lastIndexOf(end)); 32 | } else { 33 | //截取header: 40 | template = template.substring(0, template.lastIndexOf(end)); 41 | } 42 | spec.template = (template + '').trim(); 43 | return spec; 44 | } -------------------------------------------------------------------------------- /lib/util.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs') 2 | exports.readFile = fileName => { 3 | return fs.readFileSync(fileName, { 4 | encoding: 'utf8' 5 | }); 6 | } 7 | exports.fileIsExist = fileName => { 8 | return fs.existsSync(fileName); 9 | } 10 | exports.error = message => { 11 | return new Error(`[koa-vue-view]${message}`); 12 | } -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "koa-vue-view", 3 | "version": "2.1.4", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "accepts": { 8 | "version": "1.3.4", 9 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz", 10 | "integrity": "sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=", 11 | "dev": true, 12 | "requires": { 13 | "mime-types": "2.1.17", 14 | "negotiator": "0.6.1" 15 | } 16 | }, 17 | "ansi-regex": { 18 | "version": "2.1.1", 19 | "resolved": "http://registry.npm.taobao.org/ansi-regex/download/ansi-regex-2.1.1.tgz", 20 | "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" 21 | }, 22 | "ansi-styles": { 23 | "version": "2.2.1", 24 | "resolved": "http://registry.npm.taobao.org/ansi-styles/download/ansi-styles-2.2.1.tgz", 25 | "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" 26 | }, 27 | "any-promise": { 28 | "version": "1.3.0", 29 | "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", 30 | "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", 31 | "dev": true 32 | }, 33 | "assertion-error": { 34 | "version": "1.0.2", 35 | "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.2.tgz", 36 | "integrity": "sha1-E8pRXYYgbaC6xm6DTdOX2HWBCUw=", 37 | "dev": true 38 | }, 39 | "asynckit": { 40 | "version": "0.4.0", 41 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 42 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", 43 | "dev": true 44 | }, 45 | "balanced-match": { 46 | "version": "1.0.0", 47 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 48 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 49 | "dev": true 50 | }, 51 | "brace-expansion": { 52 | "version": "1.1.8", 53 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", 54 | "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", 55 | "dev": true, 56 | "requires": { 57 | "balanced-match": "1.0.0", 58 | "concat-map": "0.0.1" 59 | } 60 | }, 61 | "browser-stdout": { 62 | "version": "1.3.0", 63 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", 64 | "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=", 65 | "dev": true 66 | }, 67 | "chai": { 68 | "version": "4.1.2", 69 | "resolved": "https://registry.npmjs.org/chai/-/chai-4.1.2.tgz", 70 | "integrity": "sha1-D2RYS6ZC8PKs4oBiefTwbKI61zw=", 71 | "dev": true, 72 | "requires": { 73 | "assertion-error": "1.0.2", 74 | "check-error": "1.0.2", 75 | "deep-eql": "3.0.1", 76 | "get-func-name": "2.0.0", 77 | "pathval": "1.1.0", 78 | "type-detect": "4.0.3" 79 | } 80 | }, 81 | "chalk": { 82 | "version": "1.1.3", 83 | "resolved": "http://registry.npm.taobao.org/chalk/download/chalk-1.1.3.tgz", 84 | "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", 85 | "requires": { 86 | "ansi-styles": "2.2.1", 87 | "escape-string-regexp": "1.0.5", 88 | "has-ansi": "2.0.0", 89 | "strip-ansi": "3.0.1", 90 | "supports-color": "2.0.0" 91 | } 92 | }, 93 | "check-error": { 94 | "version": "1.0.2", 95 | "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", 96 | "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", 97 | "dev": true 98 | }, 99 | "co": { 100 | "version": "4.6.0", 101 | "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", 102 | "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", 103 | "dev": true 104 | }, 105 | "combined-stream": { 106 | "version": "1.0.5", 107 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", 108 | "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", 109 | "dev": true, 110 | "requires": { 111 | "delayed-stream": "1.0.0" 112 | } 113 | }, 114 | "commander": { 115 | "version": "2.11.0", 116 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", 117 | "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", 118 | "dev": true 119 | }, 120 | "component-emitter": { 121 | "version": "1.2.1", 122 | "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", 123 | "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", 124 | "dev": true 125 | }, 126 | "concat-map": { 127 | "version": "0.0.1", 128 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 129 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 130 | "dev": true 131 | }, 132 | "content-disposition": { 133 | "version": "0.5.2", 134 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", 135 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=", 136 | "dev": true 137 | }, 138 | "content-type": { 139 | "version": "1.0.4", 140 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 141 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", 142 | "dev": true 143 | }, 144 | "cookiejar": { 145 | "version": "2.1.1", 146 | "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.1.tgz", 147 | "integrity": "sha1-Qa1XsbVVlR7BcUEqgZQrHoIA00o=", 148 | "dev": true 149 | }, 150 | "cookies": { 151 | "version": "0.7.1", 152 | "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.7.1.tgz", 153 | "integrity": "sha1-fIphX1SBxhq58WyDNzG8uPZjuZs=", 154 | "dev": true, 155 | "requires": { 156 | "depd": "1.1.1", 157 | "keygrip": "1.0.2" 158 | } 159 | }, 160 | "core-util-is": { 161 | "version": "1.0.2", 162 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 163 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", 164 | "dev": true 165 | }, 166 | "debug": { 167 | "version": "3.1.0", 168 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 169 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 170 | "dev": true, 171 | "requires": { 172 | "ms": "2.0.0" 173 | } 174 | }, 175 | "deep-eql": { 176 | "version": "3.0.1", 177 | "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", 178 | "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", 179 | "dev": true, 180 | "requires": { 181 | "type-detect": "4.0.3" 182 | } 183 | }, 184 | "deep-equal": { 185 | "version": "1.0.1", 186 | "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", 187 | "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", 188 | "dev": true 189 | }, 190 | "delayed-stream": { 191 | "version": "1.0.0", 192 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 193 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", 194 | "dev": true 195 | }, 196 | "delegates": { 197 | "version": "1.0.0", 198 | "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", 199 | "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", 200 | "dev": true 201 | }, 202 | "depd": { 203 | "version": "1.1.1", 204 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", 205 | "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=", 206 | "dev": true 207 | }, 208 | "destroy": { 209 | "version": "1.0.4", 210 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 211 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=", 212 | "dev": true 213 | }, 214 | "diff": { 215 | "version": "3.3.1", 216 | "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz", 217 | "integrity": "sha512-MKPHZDMB0o6yHyDryUOScqZibp914ksXwAMYMTHj6KO8UeKsRYNJD3oNCKjTqZon+V488P7N/HzXF8t7ZR95ww==", 218 | "dev": true 219 | }, 220 | "ee-first": { 221 | "version": "1.1.1", 222 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 223 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=", 224 | "dev": true 225 | }, 226 | "error-inject": { 227 | "version": "1.0.0", 228 | "resolved": "https://registry.npmjs.org/error-inject/-/error-inject-1.0.0.tgz", 229 | "integrity": "sha1-4rPZG1Su1nLzCdlQ0VSFD6EdTzc=", 230 | "dev": true 231 | }, 232 | "escape-html": { 233 | "version": "1.0.3", 234 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 235 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", 236 | "dev": true 237 | }, 238 | "escape-string-regexp": { 239 | "version": "1.0.5", 240 | "resolved": "http://registry.npm.taobao.org/escape-string-regexp/download/escape-string-regexp-1.0.5.tgz", 241 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" 242 | }, 243 | "extend": { 244 | "version": "3.0.1", 245 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", 246 | "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", 247 | "dev": true 248 | }, 249 | "form-data": { 250 | "version": "2.3.1", 251 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.1.tgz", 252 | "integrity": "sha1-b7lPvXGIUwbXPRXMSX/kzE7NRL8=", 253 | "dev": true, 254 | "requires": { 255 | "asynckit": "0.4.0", 256 | "combined-stream": "1.0.5", 257 | "mime-types": "2.1.17" 258 | } 259 | }, 260 | "formidable": { 261 | "version": "1.1.1", 262 | "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.1.1.tgz", 263 | "integrity": "sha1-lriIb3w8NQi5Mta9cMTTqI818ak=", 264 | "dev": true 265 | }, 266 | "fresh": { 267 | "version": "0.5.2", 268 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 269 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=", 270 | "dev": true 271 | }, 272 | "fs.realpath": { 273 | "version": "1.0.0", 274 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 275 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 276 | "dev": true 277 | }, 278 | "get-func-name": { 279 | "version": "2.0.0", 280 | "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", 281 | "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", 282 | "dev": true 283 | }, 284 | "glob": { 285 | "version": "7.1.2", 286 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", 287 | "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", 288 | "dev": true, 289 | "requires": { 290 | "fs.realpath": "1.0.0", 291 | "inflight": "1.0.6", 292 | "inherits": "2.0.3", 293 | "minimatch": "3.0.4", 294 | "once": "1.4.0", 295 | "path-is-absolute": "1.0.1" 296 | } 297 | }, 298 | "growl": { 299 | "version": "1.10.3", 300 | "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", 301 | "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==", 302 | "dev": true 303 | }, 304 | "has-ansi": { 305 | "version": "2.0.0", 306 | "resolved": "http://registry.npm.taobao.org/has-ansi/download/has-ansi-2.0.0.tgz", 307 | "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", 308 | "requires": { 309 | "ansi-regex": "2.1.1" 310 | } 311 | }, 312 | "has-flag": { 313 | "version": "2.0.0", 314 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", 315 | "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", 316 | "dev": true 317 | }, 318 | "hash-sum": { 319 | "version": "1.0.2", 320 | "resolved": "http://registry.npm.taobao.org/hash-sum/download/hash-sum-1.0.2.tgz", 321 | "integrity": "sha1-M7QHd3VMZDJXPBIMw4CLvRDUfwQ=" 322 | }, 323 | "he": { 324 | "version": "1.1.1", 325 | "resolved": "http://registry.npm.taobao.org/he/download/he-1.1.1.tgz", 326 | "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=" 327 | }, 328 | "http-assert": { 329 | "version": "1.3.0", 330 | "resolved": "https://registry.npmjs.org/http-assert/-/http-assert-1.3.0.tgz", 331 | "integrity": "sha1-oxpc+IyHPsu1eWkH1NbxMujAHko=", 332 | "dev": true, 333 | "requires": { 334 | "deep-equal": "1.0.1", 335 | "http-errors": "1.6.2" 336 | } 337 | }, 338 | "http-errors": { 339 | "version": "1.6.2", 340 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", 341 | "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", 342 | "dev": true, 343 | "requires": { 344 | "depd": "1.1.1", 345 | "inherits": "2.0.3", 346 | "setprototypeof": "1.0.3", 347 | "statuses": "1.3.1" 348 | } 349 | }, 350 | "inflight": { 351 | "version": "1.0.6", 352 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 353 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 354 | "dev": true, 355 | "requires": { 356 | "once": "1.4.0", 357 | "wrappy": "1.0.2" 358 | } 359 | }, 360 | "inherits": { 361 | "version": "2.0.3", 362 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 363 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", 364 | "dev": true 365 | }, 366 | "is-generator-function": { 367 | "version": "1.0.6", 368 | "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.6.tgz", 369 | "integrity": "sha1-nnFlPNFf/zQcecQVFGChMdMen8Q=", 370 | "dev": true 371 | }, 372 | "isarray": { 373 | "version": "1.0.0", 374 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", 375 | "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", 376 | "dev": true 377 | }, 378 | "keygrip": { 379 | "version": "1.0.2", 380 | "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.0.2.tgz", 381 | "integrity": "sha1-rTKXxVcGneqLz+ek+kkbdcXd65E=", 382 | "dev": true 383 | }, 384 | "koa": { 385 | "version": "2.3.0", 386 | "resolved": "https://registry.npmjs.org/koa/-/koa-2.3.0.tgz", 387 | "integrity": "sha1-nh6OTaQBg5xXuFJ+rcV/dhJ1Vac=", 388 | "dev": true, 389 | "requires": { 390 | "accepts": "1.3.4", 391 | "content-disposition": "0.5.2", 392 | "content-type": "1.0.4", 393 | "cookies": "0.7.1", 394 | "debug": "3.1.0", 395 | "delegates": "1.0.0", 396 | "depd": "1.1.1", 397 | "destroy": "1.0.4", 398 | "error-inject": "1.0.0", 399 | "escape-html": "1.0.3", 400 | "fresh": "0.5.2", 401 | "http-assert": "1.3.0", 402 | "http-errors": "1.6.2", 403 | "is-generator-function": "1.0.6", 404 | "koa-compose": "4.0.0", 405 | "koa-convert": "1.2.0", 406 | "koa-is-json": "1.0.0", 407 | "mime-types": "2.1.17", 408 | "on-finished": "2.3.0", 409 | "only": "0.0.2", 410 | "parseurl": "1.3.2", 411 | "statuses": "1.3.1", 412 | "type-is": "1.6.15", 413 | "vary": "1.1.2" 414 | } 415 | }, 416 | "koa-compose": { 417 | "version": "4.0.0", 418 | "resolved": "http://registry.npm.taobao.org/koa-compose/download/koa-compose-4.0.0.tgz", 419 | "integrity": "sha1-KAClE9nDYe8NY4UrA45Pby1adzw=", 420 | "dev": true 421 | }, 422 | "koa-convert": { 423 | "version": "1.2.0", 424 | "resolved": "https://registry.npmjs.org/koa-convert/-/koa-convert-1.2.0.tgz", 425 | "integrity": "sha1-2kCHXfSd4FOQmNFwC1CCDOvNIdA=", 426 | "dev": true, 427 | "requires": { 428 | "co": "4.6.0", 429 | "koa-compose": "3.2.1" 430 | }, 431 | "dependencies": { 432 | "koa-compose": { 433 | "version": "3.2.1", 434 | "resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-3.2.1.tgz", 435 | "integrity": "sha1-qFzLQLfZhtjlo0Wzoazo6rz1Tec=", 436 | "dev": true, 437 | "requires": { 438 | "any-promise": "1.3.0" 439 | } 440 | } 441 | } 442 | }, 443 | "koa-is-json": { 444 | "version": "1.0.0", 445 | "resolved": "https://registry.npmjs.org/koa-is-json/-/koa-is-json-1.0.0.tgz", 446 | "integrity": "sha1-JzwH7c3Ljfaiwat9We52SRRR7BQ=", 447 | "dev": true 448 | }, 449 | "koa-router": { 450 | "version": "7.2.1", 451 | "resolved": "http://registry.npm.taobao.org/koa-router/download/koa-router-7.2.1.tgz", 452 | "integrity": "sha1-tApKs8attLQIld69AKnGQDBOMDk=", 453 | "dev": true, 454 | "requires": { 455 | "debug": "2.6.9", 456 | "http-errors": "1.6.2", 457 | "koa-compose": "3.2.1", 458 | "methods": "1.1.2", 459 | "path-to-regexp": "1.7.0" 460 | }, 461 | "dependencies": { 462 | "debug": { 463 | "version": "2.6.9", 464 | "resolved": "http://registry.npm.taobao.org/debug/download/debug-2.6.9.tgz", 465 | "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", 466 | "dev": true, 467 | "requires": { 468 | "ms": "2.0.0" 469 | } 470 | }, 471 | "koa-compose": { 472 | "version": "3.2.1", 473 | "resolved": "http://registry.npm.taobao.org/koa-compose/download/koa-compose-3.2.1.tgz", 474 | "integrity": "sha1-qFzLQLfZhtjlo0Wzoazo6rz1Tec=", 475 | "dev": true, 476 | "requires": { 477 | "any-promise": "1.3.0" 478 | } 479 | } 480 | } 481 | }, 482 | "lodash._reinterpolate": { 483 | "version": "3.0.0", 484 | "resolved": "http://registry.npm.taobao.org/lodash._reinterpolate/download/lodash._reinterpolate-3.0.0.tgz", 485 | "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=" 486 | }, 487 | "lodash.template": { 488 | "version": "4.4.0", 489 | "resolved": "http://registry.npm.taobao.org/lodash.template/download/lodash.template-4.4.0.tgz", 490 | "integrity": "sha1-5zoDhcg1VZF0bgILmWecaQ5o+6A=", 491 | "requires": { 492 | "lodash._reinterpolate": "3.0.0", 493 | "lodash.templatesettings": "4.1.0" 494 | } 495 | }, 496 | "lodash.templatesettings": { 497 | "version": "4.1.0", 498 | "resolved": "http://registry.npm.taobao.org/lodash.templatesettings/download/lodash.templatesettings-4.1.0.tgz", 499 | "integrity": "sha1-K01OlbpEDZFf8IvImeRVNmZxMxY=", 500 | "requires": { 501 | "lodash._reinterpolate": "3.0.0" 502 | } 503 | }, 504 | "lodash.uniq": { 505 | "version": "4.5.0", 506 | "resolved": "http://registry.npm.taobao.org/lodash.uniq/download/lodash.uniq-4.5.0.tgz", 507 | "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=" 508 | }, 509 | "media-typer": { 510 | "version": "0.3.0", 511 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 512 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=", 513 | "dev": true 514 | }, 515 | "methods": { 516 | "version": "1.1.2", 517 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 518 | "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", 519 | "dev": true 520 | }, 521 | "mime": { 522 | "version": "1.4.1", 523 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", 524 | "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", 525 | "dev": true 526 | }, 527 | "mime-db": { 528 | "version": "1.30.0", 529 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", 530 | "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=", 531 | "dev": true 532 | }, 533 | "mime-types": { 534 | "version": "2.1.17", 535 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", 536 | "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", 537 | "dev": true, 538 | "requires": { 539 | "mime-db": "1.30.0" 540 | } 541 | }, 542 | "minimatch": { 543 | "version": "3.0.4", 544 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 545 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 546 | "dev": true, 547 | "requires": { 548 | "brace-expansion": "1.1.8" 549 | } 550 | }, 551 | "minimist": { 552 | "version": "0.0.8", 553 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 554 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", 555 | "dev": true 556 | }, 557 | "mkdirp": { 558 | "version": "0.5.1", 559 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 560 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 561 | "dev": true, 562 | "requires": { 563 | "minimist": "0.0.8" 564 | } 565 | }, 566 | "mocha": { 567 | "version": "4.0.1", 568 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-4.0.1.tgz", 569 | "integrity": "sha512-evDmhkoA+cBNiQQQdSKZa2b9+W2mpLoj50367lhy+Klnx9OV8XlCIhigUnn1gaTFLQCa0kdNhEGDr0hCXOQFDw==", 570 | "dev": true, 571 | "requires": { 572 | "browser-stdout": "1.3.0", 573 | "commander": "2.11.0", 574 | "debug": "3.1.0", 575 | "diff": "3.3.1", 576 | "escape-string-regexp": "1.0.5", 577 | "glob": "7.1.2", 578 | "growl": "1.10.3", 579 | "he": "1.1.1", 580 | "mkdirp": "0.5.1", 581 | "supports-color": "4.4.0" 582 | }, 583 | "dependencies": { 584 | "supports-color": { 585 | "version": "4.4.0", 586 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", 587 | "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", 588 | "dev": true, 589 | "requires": { 590 | "has-flag": "2.0.0" 591 | } 592 | } 593 | } 594 | }, 595 | "ms": { 596 | "version": "2.0.0", 597 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 598 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", 599 | "dev": true 600 | }, 601 | "negotiator": { 602 | "version": "0.6.1", 603 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", 604 | "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=", 605 | "dev": true 606 | }, 607 | "on-finished": { 608 | "version": "2.3.0", 609 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 610 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 611 | "dev": true, 612 | "requires": { 613 | "ee-first": "1.1.1" 614 | } 615 | }, 616 | "once": { 617 | "version": "1.4.0", 618 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 619 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 620 | "dev": true, 621 | "requires": { 622 | "wrappy": "1.0.2" 623 | } 624 | }, 625 | "only": { 626 | "version": "0.0.2", 627 | "resolved": "https://registry.npmjs.org/only/-/only-0.0.2.tgz", 628 | "integrity": "sha1-Kv3oTQPlC5qO3EROMGEKcCle37Q=", 629 | "dev": true 630 | }, 631 | "parseurl": { 632 | "version": "1.3.2", 633 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", 634 | "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=", 635 | "dev": true 636 | }, 637 | "path-is-absolute": { 638 | "version": "1.0.1", 639 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 640 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 641 | "dev": true 642 | }, 643 | "path-parse": { 644 | "version": "1.0.5", 645 | "resolved": "http://registry.npm.taobao.org/path-parse/download/path-parse-1.0.5.tgz", 646 | "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=" 647 | }, 648 | "path-to-regexp": { 649 | "version": "1.7.0", 650 | "resolved": "http://registry.npm.taobao.org/path-to-regexp/download/path-to-regexp-1.7.0.tgz", 651 | "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", 652 | "dev": true, 653 | "requires": { 654 | "isarray": "0.0.1" 655 | }, 656 | "dependencies": { 657 | "isarray": { 658 | "version": "0.0.1", 659 | "resolved": "http://registry.npm.taobao.org/isarray/download/isarray-0.0.1.tgz", 660 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", 661 | "dev": true 662 | } 663 | } 664 | }, 665 | "pathval": { 666 | "version": "1.1.0", 667 | "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", 668 | "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", 669 | "dev": true 670 | }, 671 | "process-nextick-args": { 672 | "version": "1.0.7", 673 | "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", 674 | "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", 675 | "dev": true 676 | }, 677 | "qs": { 678 | "version": "6.5.1", 679 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", 680 | "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", 681 | "dev": true 682 | }, 683 | "readable-stream": { 684 | "version": "2.3.3", 685 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", 686 | "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", 687 | "dev": true, 688 | "requires": { 689 | "core-util-is": "1.0.2", 690 | "inherits": "2.0.3", 691 | "isarray": "1.0.0", 692 | "process-nextick-args": "1.0.7", 693 | "safe-buffer": "5.1.1", 694 | "string_decoder": "1.0.3", 695 | "util-deprecate": "1.0.2" 696 | } 697 | }, 698 | "resolve": { 699 | "version": "1.4.0", 700 | "resolved": "http://registry.npm.taobao.org/resolve/download/resolve-1.4.0.tgz", 701 | "integrity": "sha1-p1vgHFPaJdk0qY69DkxKcxL5KoY=", 702 | "requires": { 703 | "path-parse": "1.0.5" 704 | } 705 | }, 706 | "safe-buffer": { 707 | "version": "5.1.1", 708 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", 709 | "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", 710 | "dev": true 711 | }, 712 | "serialize-javascript": { 713 | "version": "1.4.0", 714 | "resolved": "http://registry.npm.taobao.org/serialize-javascript/download/serialize-javascript-1.4.0.tgz", 715 | "integrity": "sha1-fJWFFNtqwkQ6irwGLcn3iGp/YAU=" 716 | }, 717 | "setprototypeof": { 718 | "version": "1.0.3", 719 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.0.3.tgz", 720 | "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=", 721 | "dev": true 722 | }, 723 | "source-map": { 724 | "version": "0.5.6", 725 | "resolved": "http://registry.npm.taobao.org/source-map/download/source-map-0.5.6.tgz", 726 | "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=" 727 | }, 728 | "statuses": { 729 | "version": "1.3.1", 730 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", 731 | "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=", 732 | "dev": true 733 | }, 734 | "string_decoder": { 735 | "version": "1.0.3", 736 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", 737 | "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", 738 | "dev": true, 739 | "requires": { 740 | "safe-buffer": "5.1.1" 741 | } 742 | }, 743 | "strip-ansi": { 744 | "version": "3.0.1", 745 | "resolved": "http://registry.npm.taobao.org/strip-ansi/download/strip-ansi-3.0.1.tgz", 746 | "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", 747 | "requires": { 748 | "ansi-regex": "2.1.1" 749 | } 750 | }, 751 | "superagent": { 752 | "version": "3.6.3", 753 | "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.6.3.tgz", 754 | "integrity": "sha512-GjsfCFijfjqoz2tRiStSOoTdy7gNZOcK3ar4zONP9D8dXQWE+Qg7cbePHimRpapo06WUvoU3dmgi2e4q+sab5A==", 755 | "dev": true, 756 | "requires": { 757 | "component-emitter": "1.2.1", 758 | "cookiejar": "2.1.1", 759 | "debug": "3.1.0", 760 | "extend": "3.0.1", 761 | "form-data": "2.3.1", 762 | "formidable": "1.1.1", 763 | "methods": "1.1.2", 764 | "mime": "1.4.1", 765 | "qs": "6.5.1", 766 | "readable-stream": "2.3.3" 767 | } 768 | }, 769 | "supertest": { 770 | "version": "3.0.0", 771 | "resolved": "https://registry.npmjs.org/supertest/-/supertest-3.0.0.tgz", 772 | "integrity": "sha1-jUu2j9GDDuBwM7HFpamkAhyWUpY=", 773 | "dev": true, 774 | "requires": { 775 | "methods": "1.1.2", 776 | "superagent": "3.6.3" 777 | } 778 | }, 779 | "supports-color": { 780 | "version": "2.0.0", 781 | "resolved": "http://registry.npm.taobao.org/supports-color/download/supports-color-2.0.0.tgz", 782 | "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" 783 | }, 784 | "type-detect": { 785 | "version": "4.0.3", 786 | "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.3.tgz", 787 | "integrity": "sha1-Dj8mcLRAmbC0bChNE2p+9Jx0wuo=", 788 | "dev": true 789 | }, 790 | "type-is": { 791 | "version": "1.6.15", 792 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.15.tgz", 793 | "integrity": "sha1-yrEPtJCeRByChC6v4a1kbIGARBA=", 794 | "dev": true, 795 | "requires": { 796 | "media-typer": "0.3.0", 797 | "mime-types": "2.1.17" 798 | } 799 | }, 800 | "util-deprecate": { 801 | "version": "1.0.2", 802 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", 803 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", 804 | "dev": true 805 | }, 806 | "vary": { 807 | "version": "1.1.2", 808 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 809 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", 810 | "dev": true 811 | }, 812 | "vue": { 813 | "version": "2.4.4", 814 | "resolved": "http://registry.npm.taobao.org/vue/download/vue-2.4.4.tgz", 815 | "integrity": "sha1-6pVQuWpxRl/SuLF7YWc7NWGGF4k=" 816 | }, 817 | "vue-server-renderer": { 818 | "version": "2.4.4", 819 | "resolved": "http://registry.npm.taobao.org/vue-server-renderer/download/vue-server-renderer-2.4.4.tgz", 820 | "integrity": "sha1-vVnPTkLfryyeOIr4g3tUIN1RNg0=", 821 | "requires": { 822 | "chalk": "1.1.3", 823 | "hash-sum": "1.0.2", 824 | "he": "1.1.1", 825 | "lodash.template": "4.4.0", 826 | "lodash.uniq": "4.5.0", 827 | "resolve": "1.4.0", 828 | "serialize-javascript": "1.4.0", 829 | "source-map": "0.5.6" 830 | } 831 | }, 832 | "wrappy": { 833 | "version": "1.0.2", 834 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 835 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 836 | "dev": true 837 | } 838 | } 839 | } 840 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "koa-vue-view", 3 | "version": "2.1.6", 4 | "description": "A Koa view engine which renders Vue components on server.", 5 | "main": "lib/index.js", 6 | "directories": { 7 | "example": "examples", 8 | "lib": "lib" 9 | }, 10 | "scripts": { 11 | "test": "mocha test/index.js" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/imingyu/koa-vue-view.git" 16 | }, 17 | "keywords": [ 18 | "koa-view", 19 | "koa-vue-view", 20 | "koa-middleware" 21 | ], 22 | "author": "imingyu", 23 | "license": "MIT", 24 | "bugs": { 25 | "url": "https://github.com/imingyu/koa-vue-view/issues" 26 | }, 27 | "homepage": "https://github.com/imingyu/koa-vue-view#readme", 28 | "dependencies": { 29 | "vue": "^2.4.4", 30 | "vue-server-renderer": "^2.4.4" 31 | }, 32 | "devDependencies": { 33 | "chai": "^4.1.2", 34 | "koa": "^2.3.0", 35 | "koa-router": "^7.2.1", 36 | "mocha": "^4.0.1", 37 | "supertest": "^3.0.0" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /test/components/box1.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/components/box2.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/components/iv.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /test/components/rd1.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/components/rd2.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | var assert = require('chai').assert, 2 | path = require('path'), 3 | Koa = require('koa'), 4 | Router = require('koa-router'), 5 | http = require('http'), 6 | request = require('supertest'), 7 | VueView = require('../lib/index.js'); 8 | 9 | describe(`test koa-vue-view`, () => { 10 | describe('app.use', () => { 11 | it('基础', (done) => { 12 | var app = new Koa(); 13 | app.use(VueView({ 14 | methodName: 'rd' 15 | })); 16 | app.use(ctx => { 17 | assert.isTrue(typeof ctx.rd === 'function'); 18 | }) 19 | request(http.createServer(app.callback())) 20 | .get('/').end(function (err, res) { 21 | done(); 22 | }); 23 | }) 24 | it('传入回调函数', (done) => { 25 | var app = new Koa(); 26 | app.use(VueView(Vue => { 27 | return { 28 | sd: 1, 29 | methodName: 'render', 30 | components: { 31 | Iv: path.resolve(__dirname, './components/iv.vue') 32 | } 33 | } 34 | })); 35 | app.use(ctx => { 36 | assert.isTrue(typeof ctx.render === 'function'); 37 | ctx.render(path.resolve(__dirname, './views/inputVueCallback.vue')); 38 | }) 39 | request(http.createServer(app.callback())) 40 | .get('/').end(function (err, res) { 41 | if (err) return done(err); 42 | assert.isTrue('Hi' === res.text.trim()); 43 | done(); 44 | }); 45 | }) 46 | }) 47 | 48 | describe('渲染测试', () => { 49 | it('基础', (done) => { 50 | var app = new Koa(); 51 | app.use(VueView({ 52 | methodName: 'render' 53 | })); 54 | app.use(ctx => { 55 | ctx.state.user = 'Tom'; 56 | ctx.render(path.resolve(__dirname, './views/base.vue')); 57 | }) 58 | request(http.createServer(app.callback())) 59 | .get('/') 60 | .expect(200) 61 | .end(function (err, res) { 62 | if (err) return done(err); 63 | assert.isTrue('Tom' === res.text.trim()); 64 | done(); 65 | }); 66 | }) 67 | 68 | it('filterHtml', (done) => { 69 | var app = new Koa(); 70 | app.use(VueView({ 71 | methodName: 'render', 72 | filterHtml(html) { 73 | return 'a' + html; 74 | } 75 | })); 76 | app.use(ctx => { 77 | ctx.state.user = 'Tom'; 78 | ctx.render(path.resolve(__dirname, './views/base.vue')); 79 | }) 80 | request(http.createServer(app.callback())) 81 | .get('/') 82 | .expect(200) 83 | .end(function (err, res) { 84 | if (err) return done(err); 85 | assert.isTrue('aTom' === res.text.trim()); 86 | done(); 87 | }); 88 | }) 89 | 90 | it('静态头部尾部渲染', (done) => { 91 | var app = new Koa(); 92 | app.use(VueView({ 93 | methodName: 'render' 94 | })); 95 | app.use(ctx => { 96 | ctx.state.msg = 'Hi'; 97 | ctx.render(path.resolve(__dirname, './views/top-bottom.vue')); 98 | }) 99 | request(http.createServer(app.callback())) 100 | .get('/') 101 | .expect(200) 102 | .end(function (err, res) { 103 | if (err) return done(err); 104 | assert.isTrue('abc
123Hi
456' === res.text.trim()); 105 | done(); 106 | }); 107 | }) 108 | 109 | it('优先级渲染', (done) => { 110 | var app = new Koa(); 111 | app.use(VueView({ 112 | methodName: 'render', 113 | data: { 114 | user: 'Global.user', 115 | age: 'Global.age', 116 | }, 117 | methods: { 118 | add(a, b) { 119 | return 'Global.add:' + (a + b); 120 | } 121 | }, 122 | components: { 123 | Box: path.resolve(__dirname, './components/box1.vue') 124 | } 125 | })); 126 | app.use(ctx => { 127 | ctx.state.user = 'State.user'; 128 | ctx.state.age = 'State.age'; 129 | ctx.render({ 130 | path: path.resolve(__dirname, './views/priority.vue'), 131 | data: { 132 | user: 'Inline.user' 133 | }, 134 | methods: { 135 | add(a, b) { 136 | return 'Inline.add:' + (a + b); 137 | } 138 | }, 139 | components: { 140 | Box: path.resolve(__dirname, './components/box2.vue') 141 | } 142 | }); 143 | }) 144 | request(http.createServer(app.callback())) 145 | .get('/') 146 | .expect(200) 147 | .end(function (err, res) { 148 | if (err) return done(err); 149 | assert.isTrue('abc
Inline.userState.ageInline.add:3
2
' === res.text.trim()); 150 | done(); 151 | }); 152 | }) 153 | 154 | it('路由渲染1', (done) => { 155 | var app = new Koa(); 156 | app.use(VueView({ 157 | methodName: 'render' 158 | })); 159 | 160 | var router = new Router(); 161 | app.use(router.routes()); 162 | app.use(router.allowedMethods()); 163 | 164 | router.get('/abc', ctx => { 165 | ctx.state.users = ['Tom', 'Alice']; 166 | ctx.render(path.resolve(__dirname, './views/abc.vue')); 167 | }) 168 | 169 | app.use(ctx => { 170 | ctx.state.msg = 'Hi'; 171 | ctx.render(path.resolve(__dirname, './views/top-bottom.vue')); 172 | }) 173 | request(http.createServer(app.callback())) 174 | .get('/') 175 | .expect(200) 176 | .end(function (err, res) { 177 | if (err) return done(err); 178 | assert.isTrue('abc
123Hi
456' === res.text.trim()); 179 | done(); 180 | }); 181 | }) 182 | 183 | it('路由渲染2', (done) => { 184 | var app = new Koa(); 185 | app.use(VueView({ 186 | methodName: 'render' 187 | })); 188 | 189 | var router = new Router(); 190 | router.get('/abc', ctx => { 191 | ctx.state.user = 'Tom'; 192 | ctx.render(path.resolve(__dirname, './views/abc.vue')); 193 | }) 194 | 195 | app.use(router.routes()); 196 | app.use(router.allowedMethods()); 197 | 198 | app.use(ctx => { 199 | ctx.state.msg = 'Hi'; 200 | ctx.render(path.resolve(__dirname, './views/top-bottom.vue')); 201 | }) 202 | request(http.createServer(app.callback())) 203 | .get('/abc') 204 | .expect(200) 205 | .end(function (err, res) { 206 | if (err) return done(err); 207 | assert.isTrue('
Tom
' === res.text.trim()); 208 | done(); 209 | }); 210 | }) 211 | }) 212 | 213 | describe('use多个VueView', () => { 214 | it('基础', (done) => { 215 | var app = new Koa(); 216 | app.use(VueView({ 217 | replaceBody: false, 218 | methodName: 'rd1', 219 | data: { 220 | msg: 'rd1' 221 | }, 222 | components: { 223 | com: path.resolve(__dirname, './components/rd1.vue') 224 | } 225 | })); 226 | app.use(VueView({ 227 | replaceBody: false, 228 | methodName: 'rd2', 229 | data: { 230 | msg: 'rd2' 231 | }, 232 | components: { 233 | com: path.resolve(__dirname, './components/rd2.vue') 234 | } 235 | })); 236 | app.use((ctx, next) => { 237 | assert.isTrue(typeof ctx.rd1 === 'function'); 238 | assert.isTrue(typeof ctx.rd2 === 'function'); 239 | 240 | return Promise.all([ctx.rd1(path.resolve(__dirname, './views/more.vue')), ctx.rd2(path.resolve(__dirname, './views/more.vue'))]).then(([h1, h2]) => { 241 | ctx.body = (h1 + '').trim() + (h2 + '').trim(); 242 | next(ctx); 243 | }).catch(error => { 244 | ctx.throw(error); 245 | }) 246 | }) 247 | request(http.createServer(app.callback())) 248 | .get('/').end(function (err, res) { 249 | assert.isTrue('
rd1rd1
rd2rd2
' === res.text.trim()); 250 | done(); 251 | }); 252 | }) 253 | 254 | it('路由渲染', (done) => { 255 | var app = new Koa(); 256 | app.use(VueView({ 257 | methodName: 'rd1', 258 | data: { 259 | msg: 'rd1' 260 | }, 261 | components: { 262 | com: path.resolve(__dirname, './components/rd1.vue') 263 | } 264 | })); 265 | app.use(VueView({ 266 | methodName: 'rd2', 267 | data: { 268 | msg: 'rd2' 269 | }, 270 | components: { 271 | com: path.resolve(__dirname, './components/rd2.vue') 272 | } 273 | })); 274 | 275 | var router = new Router(); 276 | app.use(router.routes()); 277 | app.use(router.allowedMethods()); 278 | 279 | router.get('/r1', ctx => { 280 | ctx.state.user = 'r1'; 281 | ctx.rd1(path.resolve(__dirname, './views/r1.vue')); 282 | }) 283 | router.get('/r2', ctx => { 284 | ctx.state.user = 'r2'; 285 | ctx.rd2(path.resolve(__dirname, './views/r1.vue')); 286 | }) 287 | 288 | Promise.all([new Promise((resolve, reject) => { 289 | request(http.createServer(app.callback())) 290 | .get('/r1') 291 | .expect(200) 292 | .end(function (err, res) { 293 | if (err) return reject(err); 294 | 295 | resolve(res.text.trim()); 296 | }); 297 | }), new Promise((resolve, reject) => { 298 | request(http.createServer(app.callback())) 299 | .get('/r2') 300 | .expect(200) 301 | .end(function (err, res) { 302 | if (err) return reject(err); 303 | resolve(res.text.trim()); 304 | }); 305 | })]).then(([r1, r2]) => { 306 | assert.isTrue('
rd1r1
' === r1); 307 | assert.isTrue('
rd2r2
' === r2); 308 | done(); 309 | }).catch(err => { 310 | done(err); 311 | }) 312 | }) 313 | }) 314 | }); -------------------------------------------------------------------------------- /test/views/abc.vue: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /test/views/base.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /test/views/inputVueCallback.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /test/views/more.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /test/views/priority.vue: -------------------------------------------------------------------------------- 1 | abc -------------------------------------------------------------------------------- /test/views/r1.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /test/views/top-bottom.vue: -------------------------------------------------------------------------------- 1 | abc456 --------------------------------------------------------------------------------