├── .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 | [](https://travis-ci.org/imingyu/koa-vue-view)
3 | 
4 | [](https://www.npmjs.com/package/koa-vue-view)
5 | [](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 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | {{hight(app.name)}} - {{app.version}}
45 |
46 |
47 |
48 |
49 | {{layoutName}} - {{layoutVersion}}
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | - {{item.name}} {{ add(item.age, 1) }}
71 |
72 |
73 |
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 | | methodName |
167 | string |
168 | render |
169 | 在koa ctx注册渲染视图的方法名,默认render |
170 |
171 |
172 | | replaceBody |
173 | boolean |
174 | true |
175 | 是否使用渲染后的字符串替换ctx.body的内容 |
176 |
177 |
178 | | appendBody |
179 | boolean |
180 | false |
181 | replaceBody=true时,将渲染后的字符串追加到ctx.body中还是直接赋值给ctx.body |
182 |
183 |
184 | | filterHtml |
185 | function |
186 | |
187 | 可指定一个函数用于过滤render之后的html字符串,ctx.body=函数返回值=过滤后的字符串 |
188 |
189 |
190 | | cache |
191 | boolean |
192 | process.env.NODE_ENV === 'production' |
193 | 是否启用缓存,启用后仅在第一次加载视图时读取其内容,后续将从缓存中读取 |
194 |
195 |
196 | | renderer |
197 | object |
198 | require('vue-server-renderer').createRenderer() |
199 | vue ssr 渲染器 |
200 |
201 |
202 | | data |
203 | object|function |
204 | |
205 | 全局共享数据对象,在所以组件和页面中都可以共享使用,如果传递的是function,则执行function的this对象指向运行的组件或者页面的vue实例 |
206 |
207 |
208 | | vue mixin可接受的任意选项,如:data,methods,components |
209 | |
210 | |
211 |
212 | 将以mixin的方式,添加到每个渲染的页面的mixins中;
213 | |
214 |
215 |
216 |
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 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/examples/base/components/box1.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/examples/base/components/box2.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/examples/base/components/iv.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/examples/base/components/rd1.vue:
--------------------------------------------------------------------------------
1 | rd1
--------------------------------------------------------------------------------
/examples/base/components/rd2.vue:
--------------------------------------------------------------------------------
1 | rd2
--------------------------------------------------------------------------------
/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 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | {{hight(app.name)}} - {{app.version}}
9 |
10 |
11 |
12 |
13 | {{layoutName}} - {{layoutVersion}}
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/examples/base/views/User.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | - {{item.name}} {{ add(item.age, 1) }}
5 |
6 |
7 |
--------------------------------------------------------------------------------
/examples/base/views/abc.vue:
--------------------------------------------------------------------------------
1 | {{user}}
2 |
--------------------------------------------------------------------------------
/examples/base/views/base.vue:
--------------------------------------------------------------------------------
1 |
2 | {{user}}
3 |
4 |
--------------------------------------------------------------------------------
/examples/base/views/inputVueCallback.vue:
--------------------------------------------------------------------------------
1 |
2 | Hi
3 |
4 |
--------------------------------------------------------------------------------
/examples/base/views/more.vue:
--------------------------------------------------------------------------------
1 |
2 | {{msg}}
3 |
4 |
--------------------------------------------------------------------------------
/examples/base/views/priority.vue:
--------------------------------------------------------------------------------
1 | abc{{user}}{{age}}{{add(1,2)}}2
--------------------------------------------------------------------------------
/examples/base/views/top-bottom.vue:
--------------------------------------------------------------------------------
1 | abc123{{msg}}
456
--------------------------------------------------------------------------------
/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 = '',
21 | end = '';
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:之前的字符串
34 | spec.header += template.substring(0, template.indexOf(begin));
35 | //删除header +
36 | template = template.substring(template.indexOf(begin) + begin.length);
37 | //截取footer:之后的字符串
38 | spec.footer += template.substring(template.lastIndexOf(end) + end.length);
39 | //删除footer +
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 |
2 |
3 |
--------------------------------------------------------------------------------
/test/components/box2.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/test/components/iv.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/test/components/rd1.vue:
--------------------------------------------------------------------------------
1 | rd1
--------------------------------------------------------------------------------
/test/components/rd2.vue:
--------------------------------------------------------------------------------
1 | rd2
--------------------------------------------------------------------------------
/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('abc123Hi
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('abcInline.userState.ageInline.add:32
' === 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('abc123Hi
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 | {{user}}
2 |
--------------------------------------------------------------------------------
/test/views/base.vue:
--------------------------------------------------------------------------------
1 |
2 | {{user}}
3 |
4 |
--------------------------------------------------------------------------------
/test/views/inputVueCallback.vue:
--------------------------------------------------------------------------------
1 |
2 | Hi
3 |
4 |
--------------------------------------------------------------------------------
/test/views/more.vue:
--------------------------------------------------------------------------------
1 |
2 | {{msg}}
3 |
4 |
--------------------------------------------------------------------------------
/test/views/priority.vue:
--------------------------------------------------------------------------------
1 | abc{{user}}{{age}}{{add(1,2)}}2
--------------------------------------------------------------------------------
/test/views/r1.vue:
--------------------------------------------------------------------------------
1 |
2 | {{user}}
3 |
4 |
--------------------------------------------------------------------------------
/test/views/top-bottom.vue:
--------------------------------------------------------------------------------
1 | abc123{{msg}}
456
--------------------------------------------------------------------------------