├── packages
├── plover-web
│ ├── test
│ │ ├── web
│ │ │ ├── session.js
│ │ │ ├── params.js
│ │ │ ├── flash.js
│ │ │ ├── charset.js
│ │ │ ├── query.js
│ │ │ └── csrf.js
│ │ ├── fixtures
│ │ │ └── app
│ │ │ │ ├── public
│ │ │ │ ├── ok.txt
│ │ │ │ ├── big.txt
│ │ │ │ └── favicon.ico
│ │ │ │ └── config
│ │ │ │ └── app.js
│ │ ├── security
│ │ │ └── assert-method.js
│ │ └── plugin.js
│ ├── lib
│ │ ├── web
│ │ │ ├── params.js
│ │ │ ├── session.js
│ │ │ ├── flash.js
│ │ │ ├── charset.js
│ │ │ ├── query.js
│ │ │ └── csrf.js
│ │ ├── util
│ │ │ └── util.js
│ │ └── security
│ │ │ ├── assert-method.js
│ │ │ └── security-headers.js
│ └── package.json
├── plover-assets
│ ├── test
│ │ ├── fixtures
│ │ │ ├── scan
│ │ │ │ ├── a.txt
│ │ │ │ ├── b.txt
│ │ │ │ ├── .gitignore
│ │ │ │ ├── README.md
│ │ │ │ ├── dist
│ │ │ │ │ ├── a.txt
│ │ │ │ │ └── b.txt
│ │ │ │ ├── assets
│ │ │ │ │ ├── css
│ │ │ │ │ │ └── test.less
│ │ │ │ │ └── js
│ │ │ │ │ │ └── view.js
│ │ │ │ └── package.json
│ │ │ ├── app
│ │ │ │ ├── public2
│ │ │ │ │ └── g
│ │ │ │ │ │ ├── lib
│ │ │ │ │ │ ├── css
│ │ │ │ │ │ │ ├── tab.css
│ │ │ │ │ │ │ └── tab-da39a3ee5e.css
│ │ │ │ │ │ └── js
│ │ │ │ │ │ │ ├── tab.js
│ │ │ │ │ │ │ └── tab-da39a3ee5e.js
│ │ │ │ │ │ ├── list
│ │ │ │ │ │ ├── js
│ │ │ │ │ │ │ ├── mytab.js
│ │ │ │ │ │ │ ├── view.js
│ │ │ │ │ │ │ ├── mytab-da39a3ee5e.js
│ │ │ │ │ │ │ └── view-da39a3ee5e.js
│ │ │ │ │ │ └── css
│ │ │ │ │ │ │ ├── mytab.css
│ │ │ │ │ │ │ ├── view.css
│ │ │ │ │ │ │ ├── mytab-da39a3ee5e.css
│ │ │ │ │ │ │ └── view-534f283a0c.css
│ │ │ │ │ │ ├── form
│ │ │ │ │ │ └── css
│ │ │ │ │ │ │ ├── select.css
│ │ │ │ │ │ │ └── select-da39a3ee5e.css
│ │ │ │ │ │ ├── layouts
│ │ │ │ │ │ ├── js
│ │ │ │ │ │ │ ├── view.js
│ │ │ │ │ │ │ └── view-da39a3ee5e.js
│ │ │ │ │ │ └── css
│ │ │ │ │ │ │ ├── view.css
│ │ │ │ │ │ │ └── view-534f283a0c.css
│ │ │ │ │ │ ├── index
│ │ │ │ │ │ ├── css
│ │ │ │ │ │ │ ├── view.css
│ │ │ │ │ │ │ ├── test.css
│ │ │ │ │ │ │ ├── view-b8e63ceacb.css
│ │ │ │ │ │ │ └── test-63b507f051.css
│ │ │ │ │ │ └── js
│ │ │ │ │ │ │ ├── a.js
│ │ │ │ │ │ │ ├── b.js
│ │ │ │ │ │ │ ├── a-17384ca4fc.js
│ │ │ │ │ │ │ └── b-400ea80d88.js
│ │ │ │ │ │ └── manifest.json
│ │ │ │ ├── modules
│ │ │ │ │ ├── lib
│ │ │ │ │ │ └── assets
│ │ │ │ │ │ │ ├── css
│ │ │ │ │ │ │ └── tab.css
│ │ │ │ │ │ │ └── js
│ │ │ │ │ │ │ └── tab.js
│ │ │ │ │ ├── list
│ │ │ │ │ │ ├── assets
│ │ │ │ │ │ │ ├── js
│ │ │ │ │ │ │ │ ├── mytab.js
│ │ │ │ │ │ │ │ └── view.js
│ │ │ │ │ │ │ └── css
│ │ │ │ │ │ │ │ ├── mytab.css
│ │ │ │ │ │ │ │ └── view.less
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ └── views
│ │ │ │ │ │ │ └── view.art
│ │ │ │ │ ├── form
│ │ │ │ │ │ ├── assets
│ │ │ │ │ │ │ └── css
│ │ │ │ │ │ │ │ └── select.css
│ │ │ │ │ │ └── views
│ │ │ │ │ │ │ └── select.art
│ │ │ │ │ ├── layouts
│ │ │ │ │ │ ├── assets
│ │ │ │ │ │ │ ├── css
│ │ │ │ │ │ │ │ └── view.less
│ │ │ │ │ │ │ └── js
│ │ │ │ │ │ │ │ └── view.js
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ └── views
│ │ │ │ │ │ │ └── view.art
│ │ │ │ │ └── index
│ │ │ │ │ │ └── assets
│ │ │ │ │ │ ├── css
│ │ │ │ │ │ ├── test.less
│ │ │ │ │ │ └── view.css
│ │ │ │ │ │ └── js
│ │ │ │ │ │ ├── a.js
│ │ │ │ │ │ └── b.js
│ │ │ │ ├── public
│ │ │ │ │ └── g
│ │ │ │ │ │ ├── index.html
│ │ │ │ │ │ └── manifest.json
│ │ │ │ ├── tmp
│ │ │ │ │ └── ef9c0e0caa2f19753743b470fbb5c66f65d79832.css
│ │ │ │ └── config
│ │ │ │ │ └── app.js
│ │ │ ├── vendor
│ │ │ │ └── node_modules
│ │ │ │ │ ├── bootstrap
│ │ │ │ │ ├── dist
│ │ │ │ │ │ └── bootstrap.css
│ │ │ │ │ └── package.json
│ │ │ │ │ ├── vue
│ │ │ │ │ └── package.json
│ │ │ │ │ └── jquery
│ │ │ │ │ └── package.json
│ │ │ ├── empty
│ │ │ │ └── package.json
│ │ │ ├── simple
│ │ │ │ ├── modules
│ │ │ │ │ ├── layouts
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ └── views
│ │ │ │ │ │ │ └── view.ejs
│ │ │ │ │ └── page
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ └── views
│ │ │ │ │ │ └── view.ejs
│ │ │ │ └── config
│ │ │ │ │ └── app.js
│ │ │ └── expect
│ │ │ │ ├── simple.html
│ │ │ │ ├── list-concat.html
│ │ │ │ └── list.html
│ │ ├── simple-helper.js
│ │ ├── util
│ │ │ ├── concat-url.js
│ │ │ └── scan-url.js
│ │ ├── helper.js
│ │ ├── vendor.js
│ │ └── middleware.js
│ ├── lib
│ │ ├── util
│ │ │ ├── concat-url.js
│ │ │ ├── util.js
│ │ │ └── scan-dir.js
│ │ ├── plugin.js
│ │ ├── builder
│ │ │ └── digest-assets.js
│ │ ├── vendor.js
│ │ └── simple-helper.js
│ └── package.json
├── plover-module-resolver
│ ├── test
│ │ ├── fixtures
│ │ │ ├── empty
│ │ │ │ └── .keep
│ │ │ ├── simple
│ │ │ │ └── index.js
│ │ │ ├── app
│ │ │ │ ├── modules
│ │ │ │ │ ├── ignore
│ │ │ │ │ ├── index
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ ├── views
│ │ │ │ │ │ │ ├── view.art
│ │ │ │ │ │ │ └── .view.art
│ │ │ │ │ │ └── assets
│ │ │ │ │ │ │ ├── js
│ │ │ │ │ │ │ └── view.js
│ │ │ │ │ │ │ └── css
│ │ │ │ │ │ │ └── view.less
│ │ │ │ │ ├── offer
│ │ │ │ │ │ └── views
│ │ │ │ │ │ │ └── item.art
│ │ │ │ │ ├── noreload
│ │ │ │ │ │ └── package.json
│ │ │ │ │ └── testplugin
│ │ │ │ │ │ ├── package.json
│ │ │ │ │ │ └── plugin.js
│ │ │ │ ├── ignore-config
│ │ │ │ │ ├── ignore
│ │ │ │ │ └── a.json
│ │ │ │ ├── node_modules
│ │ │ │ │ ├── item
│ │ │ │ │ │ ├── .keep
│ │ │ │ │ │ └── package.json
│ │ │ │ │ ├── ignore
│ │ │ │ │ │ └── index.js
│ │ │ │ │ ├── not
│ │ │ │ │ │ └── package.json
│ │ │ │ │ ├── less
│ │ │ │ │ │ └── package.json
│ │ │ │ │ ├── offer
│ │ │ │ │ │ └── package.json
│ │ │ │ │ ├── @ali
│ │ │ │ │ │ └── coo
│ │ │ │ │ │ │ └── package.json
│ │ │ │ │ └── webpack
│ │ │ │ │ │ └── package.json
│ │ │ │ ├── invalid-config
│ │ │ │ │ └── invalid.json
│ │ │ │ ├── tpl-modules
│ │ │ │ │ └── big-offers
│ │ │ │ │ │ └── index.js
│ │ │ │ ├── config
│ │ │ │ │ ├── app.json
│ │ │ │ │ ├── edit
│ │ │ │ │ │ └── a.json
│ │ │ │ │ └── urls.js
│ │ │ │ ├── conf
│ │ │ │ │ └── plover.json
│ │ │ │ ├── conflict
│ │ │ │ │ ├── a
│ │ │ │ │ │ └── package.json
│ │ │ │ │ └── other-a
│ │ │ │ │ │ └── package.json
│ │ │ │ └── package.json
│ │ │ ├── hello
│ │ │ │ ├── views
│ │ │ │ │ ├── edit.form
│ │ │ │ │ ├── item.ejs
│ │ │ │ │ ├── view.art
│ │ │ │ │ └── controls
│ │ │ │ │ │ └── shop.art
│ │ │ │ ├── assets
│ │ │ │ │ ├── css
│ │ │ │ │ │ ├── ignore
│ │ │ │ │ │ ├── item.less
│ │ │ │ │ │ ├── view.less
│ │ │ │ │ │ └── controls
│ │ │ │ │ │ │ └── shop.less
│ │ │ │ │ └── js
│ │ │ │ │ │ ├── edit.js
│ │ │ │ │ │ └── view.js
│ │ │ │ └── package.json
│ │ │ ├── conflict
│ │ │ │ ├── a
│ │ │ │ │ └── package.json
│ │ │ │ └── anothor-a
│ │ │ │ │ └── package.json
│ │ │ ├── app-lib-no-deps
│ │ │ │ └── package.json
│ │ │ ├── dep-ok
│ │ │ │ ├── b
│ │ │ │ │ └── package.json
│ │ │ │ └── a
│ │ │ │ │ └── package.json
│ │ │ ├── noassets
│ │ │ │ └── package.json
│ │ │ ├── withpkg
│ │ │ │ └── package.json
│ │ │ ├── dep-not-compatible
│ │ │ │ ├── b
│ │ │ │ │ └── package.json
│ │ │ │ └── a
│ │ │ │ │ └── package.json
│ │ │ ├── app-lib
│ │ │ │ ├── node_modules
│ │ │ │ │ └── other-item
│ │ │ │ │ │ └── package.json
│ │ │ │ └── package.json
│ │ │ ├── pureassets
│ │ │ │ └── package.json
│ │ │ ├── invalid-json
│ │ │ │ └── package.json
│ │ │ └── dep-not-exists
│ │ │ │ └── a
│ │ │ │ └── package.json
│ │ ├── util.js
│ │ └── get-module-info.js
│ ├── lib
│ │ ├── index.js
│ │ ├── vertify.js
│ │ ├── util.js
│ │ └── get-module-base-info.js
│ ├── README.md
│ └── package.json
├── ploverx
│ ├── test
│ │ ├── fixtures
│ │ │ └── app
│ │ │ │ ├── public
│ │ │ │ └── ok.txt
│ │ │ │ ├── modules
│ │ │ │ ├── hello
│ │ │ │ │ ├── views
│ │ │ │ │ │ └── show.ejs
│ │ │ │ │ └── index.js
│ │ │ │ └── layouts
│ │ │ │ │ ├── index.js
│ │ │ │ │ └── views
│ │ │ │ │ └── index.ejs
│ │ │ │ ├── package.json
│ │ │ │ ├── config
│ │ │ │ ├── routes.js
│ │ │ │ └── app.js
│ │ │ │ └── lib
│ │ │ │ └── try-logger.js
│ │ └── index.js
│ ├── README.md
│ ├── lib
│ │ ├── index.js
│ │ └── util.js
│ ├── package.json
│ └── bin
│ │ └── build.js
├── plover
│ ├── test
│ │ ├── fixtures
│ │ │ ├── index-mods
│ │ │ │ └── modules
│ │ │ │ │ ├── a
│ │ │ │ │ └── index.js
│ │ │ │ │ ├── b
│ │ │ │ │ └── index.js
│ │ │ │ │ ├── c
│ │ │ │ │ ├── plugin.js
│ │ │ │ │ └── package.json
│ │ │ │ │ └── d
│ │ │ │ │ └── package.json
│ │ │ ├── util
│ │ │ │ ├── config
│ │ │ │ │ ├── config
│ │ │ │ │ │ ├── hsf
│ │ │ │ │ │ │ └── config.js
│ │ │ │ │ │ ├── app.json
│ │ │ │ │ │ ├── edit
│ │ │ │ │ │ │ └── index.json
│ │ │ │ │ │ └── urls.js
│ │ │ │ │ ├── ignore-config
│ │ │ │ │ │ ├── ignore
│ │ │ │ │ │ └── a.json
│ │ │ │ │ └── invalid-config
│ │ │ │ │ │ └── invalid.json
│ │ │ │ └── util
│ │ │ │ │ ├── lib
│ │ │ │ │ └── a.js
│ │ │ │ │ └── node_modules
│ │ │ │ │ └── a.js
│ │ │ ├── components
│ │ │ │ ├── startup
│ │ │ │ │ ├── lib
│ │ │ │ │ │ ├── filters
│ │ │ │ │ │ │ ├── api.js
│ │ │ │ │ │ │ └── box.js
│ │ │ │ │ │ ├── helpers
│ │ │ │ │ │ │ └── price.js
│ │ │ │ │ │ ├── services
│ │ │ │ │ │ │ └── product.js
│ │ │ │ │ │ └── middlewares
│ │ │ │ │ │ │ ├── d.js
│ │ │ │ │ │ │ ├── a.js
│ │ │ │ │ │ │ ├── hello.js
│ │ │ │ │ │ │ ├── c.js
│ │ │ │ │ │ │ └── b.js
│ │ │ │ │ └── node_modules
│ │ │ │ │ │ └── hello
│ │ │ │ │ │ └── index.js
│ │ │ │ └── plugin
│ │ │ │ │ └── modules
│ │ │ │ │ ├── test
│ │ │ │ │ ├── package.json
│ │ │ │ │ └── app.js
│ │ │ │ │ ├── last
│ │ │ │ │ ├── package.json
│ │ │ │ │ └── app.js
│ │ │ │ │ └── test-withorder
│ │ │ │ │ ├── package.json
│ │ │ │ │ └── app.js
│ │ │ ├── core
│ │ │ │ └── app
│ │ │ │ │ ├── modules
│ │ │ │ │ ├── assets
│ │ │ │ │ │ ├── assets
│ │ │ │ │ │ │ ├── js
│ │ │ │ │ │ │ │ ├── child.js
│ │ │ │ │ │ │ │ ├── layout.js
│ │ │ │ │ │ │ │ ├── view.js
│ │ │ │ │ │ │ │ └── layoutejs.js
│ │ │ │ │ │ │ └── css
│ │ │ │ │ │ │ │ ├── child.less
│ │ │ │ │ │ │ │ ├── layout.css
│ │ │ │ │ │ │ │ ├── view.less
│ │ │ │ │ │ │ │ └── layoutejs.less
│ │ │ │ │ │ ├── views
│ │ │ │ │ │ │ ├── child.art
│ │ │ │ │ │ │ ├── view.art
│ │ │ │ │ │ │ ├── navigate.art
│ │ │ │ │ │ │ ├── layout.art
│ │ │ │ │ │ │ └── layoutejs.ejs
│ │ │ │ │ │ └── index.js
│ │ │ │ │ ├── form
│ │ │ │ │ │ ├── assets
│ │ │ │ │ │ │ └── css
│ │ │ │ │ │ │ │ ├── form.css
│ │ │ │ │ │ │ │ └── input.css
│ │ │ │ │ │ └── views
│ │ │ │ │ │ │ ├── option.art
│ │ │ │ │ │ │ ├── input.art
│ │ │ │ │ │ │ ├── select.art
│ │ │ │ │ │ │ └── form.art
│ │ │ │ │ ├── index
│ │ │ │ │ │ ├── views
│ │ │ │ │ │ │ ├── engine-not-found.xml
│ │ │ │ │ │ │ ├── banner.art
│ │ │ │ │ │ │ ├── layoutNotFound.art
│ │ │ │ │ │ │ └── view.art
│ │ │ │ │ │ └── index.js
│ │ │ │ │ ├── engine
│ │ │ │ │ │ ├── views
│ │ │ │ │ │ │ └── returnPromise.my
│ │ │ │ │ │ └── index.js
│ │ │ │ │ ├── filter
│ │ │ │ │ │ ├── views
│ │ │ │ │ │ │ └── view.art
│ │ │ │ │ │ └── index.js
│ │ │ │ │ ├── child
│ │ │ │ │ │ ├── views
│ │ │ │ │ │ │ ├── show.art
│ │ │ │ │ │ │ ├── elements
│ │ │ │ │ │ │ │ ├── async.art
│ │ │ │ │ │ │ │ ├── footer.art
│ │ │ │ │ │ │ │ ├── book-item.ejs
│ │ │ │ │ │ │ │ ├── item.art
│ │ │ │ │ │ │ │ └── big.art
│ │ │ │ │ │ │ ├── renderChildError.art
│ │ │ │ │ │ │ ├── renderAsyncChildError.art
│ │ │ │ │ │ │ ├── header.art
│ │ │ │ │ │ │ ├── books.ejs
│ │ │ │ │ │ │ ├── panel.ejs
│ │ │ │ │ │ │ └── view.art
│ │ │ │ │ │ └── index.js
│ │ │ │ │ ├── helper
│ │ │ │ │ │ ├── views
│ │ │ │ │ │ │ ├── async.art
│ │ │ │ │ │ │ └── view.art
│ │ │ │ │ │ └── index.js
│ │ │ │ │ ├── dev
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ └── index-update.js
│ │ │ │ │ ├── layouts
│ │ │ │ │ │ ├── views
│ │ │ │ │ │ │ ├── mobile.art
│ │ │ │ │ │ │ ├── view.art
│ │ │ │ │ │ │ └── index.art
│ │ │ │ │ │ └── index.js
│ │ │ │ │ ├── app-helper
│ │ │ │ │ │ ├── index.js
│ │ │ │ │ │ └── views
│ │ │ │ │ │ │ └── child-view-not-found.art
│ │ │ │ │ ├── buffer
│ │ │ │ │ │ └── index.js
│ │ │ │ │ ├── syntax-error
│ │ │ │ │ │ └── index.js
│ │ │ │ │ ├── myplugin
│ │ │ │ │ │ ├── package.json
│ │ │ │ │ │ └── lib
│ │ │ │ │ │ │ ├── async-engine.js
│ │ │ │ │ │ │ └── plugin.js
│ │ │ │ │ ├── api
│ │ │ │ │ │ └── index.js
│ │ │ │ │ ├── elements
│ │ │ │ │ │ └── views
│ │ │ │ │ │ │ └── offer.art
│ │ │ │ │ ├── es6-class
│ │ │ │ │ │ └── index.js
│ │ │ │ │ └── service
│ │ │ │ │ │ └── index.js
│ │ │ │ │ └── lib
│ │ │ │ │ ├── filters
│ │ │ │ │ ├── api.js
│ │ │ │ │ └── box.js
│ │ │ │ │ ├── services
│ │ │ │ │ ├── code-service.js
│ │ │ │ │ └── product-service.js
│ │ │ │ │ └── helpers
│ │ │ │ │ ├── xview.js
│ │ │ │ │ ├── url-helper.js
│ │ │ │ │ ├── cms.js
│ │ │ │ │ └── assets.js
│ │ │ ├── index
│ │ │ │ └── config
│ │ │ │ │ ├── plover.json
│ │ │ │ │ └── urls.js
│ │ │ ├── index-dep
│ │ │ │ └── modules
│ │ │ │ │ ├── b
│ │ │ │ │ └── package.json
│ │ │ │ │ └── a
│ │ │ │ │ └── package.json
│ │ │ └── expects
│ │ │ │ ├── index-with-layout-false.html
│ │ │ │ ├── index-offer.html
│ │ │ │ ├── index-with-layout-simple.html
│ │ │ │ ├── index-with-filter.html
│ │ │ │ ├── index-with-layout-mobile.html
│ │ │ │ ├── helper-async.html
│ │ │ │ ├── index.html
│ │ │ │ ├── index-with-default-layout.html
│ │ │ │ ├── index-with-banner.html
│ │ │ │ ├── app-helper-child-view-not-found.html
│ │ │ │ ├── helper.html
│ │ │ │ ├── assets-navigate.html
│ │ │ │ ├── assets-disable-autowaire.html
│ │ │ │ ├── assets.html
│ │ │ │ ├── assets-layoutejs.html
│ │ │ │ ├── child-production.html
│ │ │ │ └── child.html
│ │ ├── helpers
│ │ │ └── app.js
│ │ ├── util.js
│ │ ├── util
│ │ │ ├── util.js
│ │ │ ├── config.js
│ │ │ ├── error-handler.js
│ │ │ └── invoker.js
│ │ ├── core
│ │ │ ├── render-helper.js
│ │ │ └── helper-container.js
│ │ └── components
│ │ │ ├── plugin.js
│ │ │ ├── startup
│ │ │ └── index.js
│ │ │ └── service.js
│ ├── lib
│ │ ├── util
│ │ │ ├── error-handler.js
│ │ │ ├── config.js
│ │ │ ├── invoker.js
│ │ │ └── util.js
│ │ ├── components
│ │ │ ├── startup
│ │ │ │ ├── middleware.js
│ │ │ │ └── index.js
│ │ │ └── plugin.js
│ │ └── core
│ │ │ ├── helper-container.js
│ │ │ └── action-runner.js
│ └── package.json
├── plover-xview
│ ├── test
│ │ ├── fixtures
│ │ │ ├── app
│ │ │ │ └── modules
│ │ │ │ │ ├── index
│ │ │ │ │ ├── views
│ │ │ │ │ │ ├── child.art
│ │ │ │ │ │ ├── view.art
│ │ │ │ │ │ └── coverage.art
│ │ │ │ │ └── index.js
│ │ │ │ │ └── layouts
│ │ │ │ │ ├── index.js
│ │ │ │ │ └── views
│ │ │ │ │ └── view.art
│ │ │ └── expect
│ │ │ │ ├── coverage.html
│ │ │ │ └── index.html
│ │ └── plugin.js
│ ├── lib
│ │ ├── plugin.js
│ │ └── helper.js
│ ├── README.md
│ └── package.json
├── plover-util
│ ├── lib
│ │ ├── index.js
│ │ ├── assign.js
│ │ ├── delegate.js
│ │ ├── safe-string.js
│ │ ├── array.js
│ │ ├── lang.js
│ │ └── route-info.js
│ ├── README.md
│ ├── test
│ │ ├── index.js
│ │ ├── array.js
│ │ ├── safe-string.js
│ │ ├── assign.js
│ │ ├── delegate.js
│ │ ├── route-info.js
│ │ └── lang.js
│ └── package.json
├── plover-router
│ ├── lib
│ │ ├── method.js
│ │ └── plugin.js
│ ├── package.json
│ └── test
│ │ └── plugin.js
└── plover-logger
│ ├── package.json
│ └── README.md
├── .eslintignore
├── .eslintrc
├── .gitignore
├── ci.sh
├── .travis.yml
├── package.json
└── README.md
/packages/plover-web/test/web/session.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/scan/a.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/scan/b.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/scan/.gitignore:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/scan/README.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/scan/dist/a.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/scan/dist/b.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/empty/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/ploverx/test/fixtures/app/public/ok.txt:
--------------------------------------------------------------------------------
1 | ok!
2 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | coverage/
2 | fixtures/
3 | node_modules/
4 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | extends: 'eslint-config-bce'
3 | }
4 |
5 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/scan/assets/css/test.less:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/scan/assets/js/view.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/simple/index.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-web/test/fixtures/app/public/ok.txt:
--------------------------------------------------------------------------------
1 | ok!
2 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/index-mods/modules/a/index.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/index-mods/modules/b/index.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/util/config/config/hsf/config.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/util/config/ignore-config/ignore:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/public2/g/lib/css/tab.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/public2/g/lib/js/tab.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/public2/g/list/js/mytab.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/public2/g/list/js/view.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/scan/package.json:
--------------------------------------------------------------------------------
1 | {
2 | }
3 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/app/modules/ignore:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/hello/views/edit.form:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/hello/views/item.ejs:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/hello/views/view.art:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/components/startup/lib/filters/api.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/components/startup/lib/filters/box.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/modules/lib/assets/css/tab.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/modules/lib/assets/js/tab.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/modules/list/assets/js/mytab.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/modules/list/assets/js/view.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/public2/g/form/css/select.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/public2/g/layouts/js/view.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/public2/g/list/css/mytab.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/app/ignore-config/ignore:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/app/modules/index/index.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/app/node_modules/item/.keep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/hello/assets/css/ignore:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/hello/assets/css/item.less:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/hello/assets/css/view.less:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/hello/assets/js/edit.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/hello/assets/js/view.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/components/startup/lib/helpers/price.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/components/startup/lib/services/product.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/assets/assets/js/child.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/assets/assets/js/layout.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/assets/assets/js/view.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/form/assets/css/form.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/form/assets/css/input.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/util/config/invalid-config/invalid.json:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/modules/form/assets/css/select.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/modules/layouts/assets/css/view.less:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/modules/layouts/assets/js/view.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/modules/list/assets/css/mytab.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/modules/list/assets/css/view.less:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/public/g/index.html:
--------------------------------------------------------------------------------
1 | Hello World
2 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/public2/g/lib/css/tab-da39a3ee5e.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/public2/g/lib/js/tab-da39a3ee5e.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/public2/g/list/css/view.css:
--------------------------------------------------------------------------------
1 | compiled:
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/public2/g/list/js/mytab-da39a3ee5e.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/public2/g/list/js/view-da39a3ee5e.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/app/invalid-config/invalid.json:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/app/modules/index/views/view.art:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/app/modules/offer/views/item.art:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/app/node_modules/ignore/index.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/hello/views/controls/shop.art:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/components/startup/node_modules/hello/index.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/assets/assets/css/child.less:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/assets/assets/css/layout.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/assets/assets/css/view.less:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/assets/assets/js/layoutejs.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/util/util/lib/a.js:
--------------------------------------------------------------------------------
1 | module.exports = 'a';
2 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/public2/g/form/css/select-da39a3ee5e.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/public2/g/index/css/view.css:
--------------------------------------------------------------------------------
1 | body { }
2 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/public2/g/index/js/a.js:
--------------------------------------------------------------------------------
1 | var a = 1;
2 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/public2/g/index/js/b.js:
--------------------------------------------------------------------------------
1 | var b = 2;
2 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/public2/g/layouts/css/view.css:
--------------------------------------------------------------------------------
1 | compiled:
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/public2/g/layouts/js/view-da39a3ee5e.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/public2/g/list/css/mytab-da39a3ee5e.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/app/modules/index/assets/js/view.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/app/tpl-modules/big-offers/index.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/hello/assets/css/controls/shop.less:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/assets/assets/css/layoutejs.less:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/index/views/engine-not-found.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/ploverx/test/fixtures/app/modules/hello/views/show.ejs:
--------------------------------------------------------------------------------
1 |
Hello
2 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/modules/index/assets/css/test.less:
--------------------------------------------------------------------------------
1 | body: {}
2 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/modules/index/assets/css/view.css:
--------------------------------------------------------------------------------
1 | body { }
2 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/modules/index/assets/js/a.js:
--------------------------------------------------------------------------------
1 | var a = 1;
2 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/modules/index/assets/js/b.js:
--------------------------------------------------------------------------------
1 | var b = 2;
2 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/vendor/node_modules/bootstrap/dist/bootstrap.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/app/modules/index/assets/css/view.less:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/engine/views/returnPromise.my:
--------------------------------------------------------------------------------
1 | Hello
2 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/util/util/node_modules/a.js:
--------------------------------------------------------------------------------
1 | module.exports = 'a';
2 |
3 |
--------------------------------------------------------------------------------
/packages/ploverx/test/fixtures/app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ploverx-test"
3 | }
4 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/public2/g/index/css/test.css:
--------------------------------------------------------------------------------
1 | compiled: body: {}
2 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/public2/g/index/css/view-b8e63ceacb.css:
--------------------------------------------------------------------------------
1 | body { }
2 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/public2/g/index/js/a-17384ca4fc.js:
--------------------------------------------------------------------------------
1 | var a = 1;
2 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/public2/g/index/js/b-400ea80d88.js:
--------------------------------------------------------------------------------
1 | var b = 2;
2 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/public2/g/layouts/css/view-534f283a0c.css:
--------------------------------------------------------------------------------
1 | compiled:
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/public2/g/list/css/view-534f283a0c.css:
--------------------------------------------------------------------------------
1 | compiled:
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/filter/views/view.art:
--------------------------------------------------------------------------------
1 | filter
2 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/index/config/plover.json:
--------------------------------------------------------------------------------
1 | {
2 | "test": "hello"
3 | }
4 |
5 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/util/config/config/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "port": 4100
3 | }
4 |
5 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/empty/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "empty"
3 | }
4 |
5 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/app/config/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "port": 4100
3 | }
4 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/app/modules/index/views/.view.art:
--------------------------------------------------------------------------------
1 | // 隐藏文件会忽略的
2 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/util/config/ignore-config/a.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hello"
3 | }
4 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/public2/g/index/css/test-63b507f051.css:
--------------------------------------------------------------------------------
1 | compiled: body: {}
2 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/app/conf/plover.json:
--------------------------------------------------------------------------------
1 | {
2 | "test": "hello"
3 | }
4 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/app/config/edit/a.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "plover"
3 | }
4 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/app/conflict/a/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "a"
3 | }
4 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/conflict/a/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "a"
3 | }
4 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/child/views/show.art:
--------------------------------------------------------------------------------
1 | Show
2 | show is view
3 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/helper/views/async.art:
--------------------------------------------------------------------------------
1 | {{cms.render('/cms/123')}}
2 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/index-mods/modules/c/plugin.js:
--------------------------------------------------------------------------------
1 | module.exports = function() {
2 | };
3 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/app/ignore-config/a.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hello"
3 | }
4 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/conflict/anothor-a/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "a"
3 | }
4 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/index/views/banner.art:
--------------------------------------------------------------------------------
1 | Banner
2 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/util/config/config/edit/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "plover"
3 | }
4 |
5 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/tmp/ef9c0e0caa2f19753743b470fbb5c66f65d79832.css:
--------------------------------------------------------------------------------
1 | compiled: body: {}
2 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/app/conflict/other-a/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "a"
3 | }
4 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/app/node_modules/not/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "not"
3 | }
4 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/child/views/elements/async.art:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/child/views/elements/footer.art:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/child/views/renderChildError.art:
--------------------------------------------------------------------------------
1 | {{app.view('renderError')}}
2 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/app-lib-no-deps/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app-lib-no-deps"
3 | }
4 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/hello/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hello",
3 | "plover": {}
4 | }
5 |
--------------------------------------------------------------------------------
/packages/plover-web/test/fixtures/app/public/big.txt:
--------------------------------------------------------------------------------
1 | 1234567890
2 | 1234567890
3 | 1234567890
4 | 1234567890
5 |
6 |
--------------------------------------------------------------------------------
/packages/plover-xview/test/fixtures/app/modules/index/views/child.art:
--------------------------------------------------------------------------------
1 | {{$.viewdata('age', 2)}}
2 | Child
3 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/child/views/elements/book-item.ejs:
--------------------------------------------------------------------------------
1 | <%= index%> <%= name %>
2 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/form/views/option.art:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/packages/ploverx/test/fixtures/app/modules/hello/index.js:
--------------------------------------------------------------------------------
1 | exports.show = function() {
2 | this.render();
3 | };
4 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/modules/list/index.js:
--------------------------------------------------------------------------------
1 | exports.view = function() {
2 | this.render();
3 | };
4 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/simple/modules/layouts/index.js:
--------------------------------------------------------------------------------
1 | exports.view = ctx => {
2 | ctx.render();
3 | };
4 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/simple/modules/page/index.js:
--------------------------------------------------------------------------------
1 | exports.view = ctx => {
2 | ctx.render();
3 | };
4 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/dep-ok/b/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "b",
3 | "version": "1.0.3"
4 | }
5 |
--------------------------------------------------------------------------------
/packages/plover-xview/test/fixtures/app/modules/layouts/index.js:
--------------------------------------------------------------------------------
1 | exports.view = ctx => {
2 | ctx.render();
3 | };
4 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/child/views/renderAsyncChildError.art:
--------------------------------------------------------------------------------
1 | {{app.view('renderAsyncError')}}
2 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/index-dep/modules/b/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "b",
3 | "version": "2.0.0"
4 | }
5 |
6 |
--------------------------------------------------------------------------------
/packages/ploverx/test/fixtures/app/modules/layouts/index.js:
--------------------------------------------------------------------------------
1 | exports.index = function() {
2 | this.render();
3 | };
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | !**/test/fixtures/**/node_modules/
3 | coverage/
4 | yarn.lock
5 | package-lock.json
6 | logs/
7 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/modules/layouts/index.js:
--------------------------------------------------------------------------------
1 | exports.view = function() {
2 | this.render();
3 | };
4 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/noassets/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "noassets",
3 | "plover": {}
4 | }
5 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/withpkg/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "withpkg",
3 | "version": "1.0.1"
4 | }
5 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/dev/index.js:
--------------------------------------------------------------------------------
1 | exports.view = function() {
2 | this.body = 'dev';
3 | };
4 |
5 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/form/views/input.art:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/layouts/views/mobile.art:
--------------------------------------------------------------------------------
1 |
2 | {{content}}
3 |
4 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/modules/form/views/select.art:
--------------------------------------------------------------------------------
1 | {{assets.css('css/common.css')}}
2 |
3 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/public/g/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "list/css/mytabs.css": "list/css/mytabs-123.css"
3 | }
4 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/simple/config/app.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | assets: {
3 | simple: true
4 | }
5 | };
6 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/vendor/node_modules/vue/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue",
3 | "version": "1.0.0"
4 | }
5 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/app/node_modules/item/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "item",
3 | "plover": {}
4 | }
5 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/app/node_modules/less/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "less",
3 | "plover": {}
4 | }
5 |
--------------------------------------------------------------------------------
/packages/plover-xview/lib/plugin.js:
--------------------------------------------------------------------------------
1 | module.exports = function(app) {
2 | app.addHelper('$', require('./helper'));
3 | };
4 |
5 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/app/node_modules/offer/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "offer",
3 | "plover": {}
4 | }
5 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/dep-not-compatible/b/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "b",
3 | "version": "2.0.0"
4 | }
5 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/dev/index-update.js:
--------------------------------------------------------------------------------
1 | exports.view = function() {
2 | this.body = 'dev-update';
3 | };
4 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/vendor/node_modules/bootstrap/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bootstrap",
3 | "version": "1.0.9"
4 | }
5 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/vendor/node_modules/jquery/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jquery",
3 | "version": "2.0.0"
4 | }
5 |
6 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/lib/filters/api.js:
--------------------------------------------------------------------------------
1 | exports.beforeAction = function() {
2 | this.ctx.set('X-API', '112233');
3 | };
4 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/index-mods/modules/d/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "d",
3 | "plover": {
4 | "assets": true
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/app/node_modules/@ali/coo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "plover": {
3 | "name": "coo"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/app/node_modules/webpack/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "webpack",
3 | "version": "2.0.0"
4 | }
5 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/app-helper/index.js:
--------------------------------------------------------------------------------
1 | exports['child-view-not-found'] = function() {
2 | this.render();
3 | };
4 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/buffer/index.js:
--------------------------------------------------------------------------------
1 | exports.view = function() {
2 | this.body = new Buffer('hello world');
3 | };
4 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/index/views/layoutNotFound.art:
--------------------------------------------------------------------------------
1 |
2 | Layout Not Found
3 |
4 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/syntax-error/index.js:
--------------------------------------------------------------------------------
1 | module.exports = function() {
2 | yield '123';
3 | this.render();
4 | }
5 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/app-lib/node_modules/other-item/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "other-item",
3 | "plover": {}
4 | }
5 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/app-helper/views/child-view-not-found.art:
--------------------------------------------------------------------------------
1 | child view not found
2 | {{app.view('not-found')}}
3 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/index-mods/modules/c/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "c",
3 | "plover": {
4 | "plugin": "plugin.js"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/lib/services/code-service.js:
--------------------------------------------------------------------------------
1 | exports.generate = function() {
2 | return Math.floor(Math.random() * 1000);
3 | };
4 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/engine/index.js:
--------------------------------------------------------------------------------
1 | exports.returnPromise = function() {
2 | this.layout = false;
3 | this.render();
4 | };
5 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/expects/index-with-layout-false.html:
--------------------------------------------------------------------------------
1 | plover
2 | version: 1.0.0
3 | nodejs webframework
4 |
5 |
--------------------------------------------------------------------------------
/packages/ploverx/test/fixtures/app/modules/layouts/views/index.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <%- content %>
5 |
6 |
7 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/pureassets/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "plover": {
3 | "name": "pureassets",
4 | "assets": true
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/packages/plover-web/test/fixtures/app/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/alibaba/plover/HEAD/packages/plover-web/test/fixtures/app/public/favicon.ico
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/child/views/elements/item.art:
--------------------------------------------------------------------------------
1 | Name: {{item.name}}
2 | Price: {{formatPrice(item.price)}}
3 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/myplugin/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "myplugin",
3 | "plover": {
4 | "plugin": "lib/plugin"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/expects/index-offer.html:
--------------------------------------------------------------------------------
1 |
5 |
6 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/index/config/urls.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | google: 'http://www.google.com',
3 | baidu: 'http://www.baidu.com'
4 | };
5 |
6 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/util/config/config/urls.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | google: 'http://www.google.com',
3 | baidu: 'http://www.baidu.com'
4 | };
5 |
--------------------------------------------------------------------------------
/ci.sh:
--------------------------------------------------------------------------------
1 | for file in packages/*
2 | do
3 | if test -d $file
4 | then
5 | (cd $file; npm install)
6 | fi
7 | done
8 | npm install
9 | npm run ci
10 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/app-lib/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app-lib",
3 | "dependencies": {
4 | "other-item": "1.0.0"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/app/modules/noreload/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "noreload",
3 | "plover": {
4 | "reload": false
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/invalid-json/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "invalid-json",
3 | "dependencies": {
4 | "a": "1.1.1",
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/components/plugin/modules/test/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "test",
3 | "plover": {
4 | "plugin": "app.js"
5 | }
6 | }
7 |
8 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/expects/index-with-layout-simple.html:
--------------------------------------------------------------------------------
1 | simple layout
2 | plover
3 | version: 1.0.0
4 | nodejs webframework
5 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/index-dep/modules/a/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "a",
3 | "plover": {
4 | "dep": {
5 | "b": "~1.0.0"
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/app/config/urls.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | 'google': 'http://www.google.com',
3 | 'baidu': 'http://www.baidu.com'
4 | };
5 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/app/modules/testplugin/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "testplugin",
3 | "plover": {
4 | "plugin": "plugin.js"
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/dep-ok/a/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "a",
3 | "plover": {
4 | "dep": {
5 | "b": "~1.0.0"
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/api/index.js:
--------------------------------------------------------------------------------
1 | exports.offer = function() {
2 | this.type = 'json';
3 | this.render({ id: 123, name: 'test offer' });
4 | };
5 |
--------------------------------------------------------------------------------
/packages/ploverx/test/fixtures/app/config/routes.js:
--------------------------------------------------------------------------------
1 | module.exports = (app) => {
2 | app.get('/hello', 'hello#show');
3 | app.use(require('../lib/try-logger')())
4 | };
5 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/dep-not-exists/a/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "a",
3 | "plover": {
4 | "dep": {
5 | "b": "~1.0.0"
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/components/plugin/modules/last/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "last",
3 | "plover": {
4 | "plugin": "app.js",
5 | "level": 4
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/dep-not-compatible/a/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "a",
3 | "plover": {
4 | "dep": {
5 | "b": "~1.0.0"
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/components/plugin/modules/test-withorder/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "test-withorder",
3 | "plover": {
4 | "plugin": "app.js"
5 | }
6 | }
7 |
8 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/components/startup/lib/middlewares/d.js:
--------------------------------------------------------------------------------
1 | module.exports = function() {
2 | return function* () {
3 | this.body = this.body + ' & d';
4 | };
5 | };
6 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/assets/views/child.art:
--------------------------------------------------------------------------------
1 | {{assets.css('/child-lib.css')}}
2 | {{assets.js('/child-lib.js')}}
3 |
4 | child
5 |
6 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/components/plugin/modules/last/app.js:
--------------------------------------------------------------------------------
1 | module.exports = function(app) {
2 | app.addMiddleware(function* () {
3 | this.body = 'last';
4 | });
5 | };
6 |
7 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/helper/index.js:
--------------------------------------------------------------------------------
1 | exports.view = function() {
2 | this.render();
3 | };
4 |
5 |
6 | exports.async = function() {
7 | this.render();
8 | };
9 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/expects/index-with-filter.html:
--------------------------------------------------------------------------------
1 |
2 |
plover
3 |
version: 1.0.0
4 |
nodejs webframework
5 |
6 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/elements/views/offer.art:
--------------------------------------------------------------------------------
1 |
2 |
{{offer.name}}
3 |
{{offer.price}}
4 |
5 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/expects/index-with-layout-mobile.html:
--------------------------------------------------------------------------------
1 |
2 |
plover
3 |
version: 1.0.0
4 |
nodejs webframework
5 |
6 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/components/startup/lib/middlewares/a.js:
--------------------------------------------------------------------------------
1 | /**
2 | * async function
3 | */
4 | module.exports = async (ctx, next) => {
5 | ctx.body = 'a';
6 | await next();
7 | };
8 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/es6-class/index.js:
--------------------------------------------------------------------------------
1 | class Controller {
2 | getData() {
3 | this.render({ name: 'plover' });
4 | }
5 | }
6 |
7 | module.exports = new Controller();
8 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - stable
4 | - 12.18
5 | - 14.5
6 | script:
7 | - ./ci.sh
8 | after_script:
9 | - npm install codecov
10 | - ./node_modules/.bin/codecov
11 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/expects/helper-async.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | cms: /cms/123
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/index/views/view.art:
--------------------------------------------------------------------------------
1 | {{name}}
2 | version: {{version}}
3 | {{desc}}
4 |
5 | {{if query.banner}}
6 | {{app.view('banner')}}
7 | {{/if}}
8 |
--------------------------------------------------------------------------------
/packages/plover-xview/test/fixtures/app/modules/index/index.js:
--------------------------------------------------------------------------------
1 | exports.view = ctx => {
2 | ctx.render();
3 | };
4 |
5 |
6 | exports.coverage = ctx => {
7 | ctx.layout = false;
8 | ctx.render();
9 | };
10 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/child/views/header.art:
--------------------------------------------------------------------------------
1 | {{app.set('title', title)}}
2 | {{title}}
3 |
4 |
5 | {{app.control('elements/big', { title: 'Big' })}}
6 |
7 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/form/views/select.art:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/layouts/views/view.art:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{title}}
5 |
6 |
7 | {{content}}
8 |
9 |
10 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/child/views/books.ejs:
--------------------------------------------------------------------------------
1 |
2 | <% books.forEach(function(name, index) { %>
3 | <%- app.control('elements/book-item', { index: index, name: name }) %>
4 | <% }) %>
5 |
6 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/myplugin/lib/async-engine.js:
--------------------------------------------------------------------------------
1 | exports.async = true;
2 |
3 | exports.compile = function(source) {
4 | return function() {
5 | return Promise.resolve(source);
6 | };
7 | };
8 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/lib/filters/box.js:
--------------------------------------------------------------------------------
1 | exports.afterRender = function() {
2 | const route = this.route;
3 | this.body =
4 | `
5 | ${this.body}
6 |
`;
7 | };
8 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/assets/views/view.art:
--------------------------------------------------------------------------------
1 | {{assets.css('/view-lib.css')}}
2 | {{assets.js('/view-lib.js')}}
3 | Assets
4 | {{app.view('child')}}
5 | {{app.control('form:form', { name: 'my-form' })}}
6 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/config/app.js:
--------------------------------------------------------------------------------
1 | const pathUtil = require('path');
2 |
3 |
4 | module.exports = {
5 | applicationRoot: pathUtil.join(__dirname, '..'),
6 | assets: {
7 | digest: true
8 | }
9 | };
10 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/layouts/views/index.art:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{title}}-layouts#index
5 |
6 |
7 | {{content}}
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/service/index.js:
--------------------------------------------------------------------------------
1 | exports.view = function() {
2 | const id = this.query.id;
3 | const product = this.productService.get(id);
4 |
5 | this.render({ id: product.id, name: product.name });
6 | };
7 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/simple/modules/page/views/view.ejs:
--------------------------------------------------------------------------------
1 | <% assets.css('page.css') %>
2 | <% assets.js('react.js', 'lib') %>
3 | <% assets.js('topbar.js') %>
4 | <% assets.js('page.js') %>
5 |
6 |
7 | Hello
8 |
9 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/components/startup/lib/middlewares/hello.js:
--------------------------------------------------------------------------------
1 | /**
2 | * function that return async function
3 | */
4 | module.exports = function() {
5 | return async (ctx) => {
6 | ctx.body = 'hello';
7 | };
8 | };
9 |
10 |
--------------------------------------------------------------------------------
/packages/plover-xview/test/fixtures/app/modules/index/views/view.art:
--------------------------------------------------------------------------------
1 | {{$.viewdata('productUrl', 'http://www.ploverjs.com')}}
2 | {{$.viewdata({ name: 'plover', version: '3.0' })}}
3 | {{$.viewdata(null)}}
4 | Hello World!
5 | {{app.control('child')}}
6 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/form/views/form.art:
--------------------------------------------------------------------------------
1 |
9 |
--------------------------------------------------------------------------------
/packages/plover-xview/test/fixtures/app/modules/index/views/coverage.art:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{$.metaTags()}}
6 |
7 |
8 | Hello
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/packages/plover-xview/test/fixtures/app/modules/layouts/views/view.art:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{$.metaTags({viewdata: true})}}
6 |
7 |
8 | {{content}}
9 |
10 |
11 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/myplugin/lib/plugin.js:
--------------------------------------------------------------------------------
1 | module.exports = function(app) {
2 | require('plover-arttemplate/lib/plugin')(app);
3 | require('plover-ejs/lib/plugin')(app);
4 | app.addEngine('my', require('./async-engine'));
5 | };
6 |
7 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/lib/index.js:
--------------------------------------------------------------------------------
1 | exports = module.exports = require('./resolver');
2 | exports.Resolver = require('./resolver');
3 | exports.getModuleBaseInfo = require('./get-module-base-info');
4 | exports.getModuleInfo = require('./get-module-info');
5 |
6 |
--------------------------------------------------------------------------------
/packages/plover-xview/test/fixtures/expect/coverage.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Hello
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/lib/helpers/xview.js:
--------------------------------------------------------------------------------
1 | exports.$init = function() {
2 | this.emptyClass = function(v) {
3 | return v ? '' : 'empty';
4 | };
5 | };
6 |
7 |
8 | exports.formatPrice = function(price) {
9 | return price + '元';
10 | };
11 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/assets/views/navigate.art:
--------------------------------------------------------------------------------
1 | {{= content}}
2 |
3 |
4 | {{each myassets.css as item}}
5 | - {{item.url}}
6 | {{/each}}
7 | {{each myassets.js as item}}
8 | - {{item.url}}
9 | {{/each}}
10 |
11 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/expects/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | plover
8 | version: 1.0.0
9 | nodejs webframework
10 |
11 |
12 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/components/startup/lib/middlewares/c.js:
--------------------------------------------------------------------------------
1 | /**
2 | * function that return regular function
3 | */
4 | module.exports = function() {
5 | return (ctx, next) => {
6 | ctx.body = ctx.body + ' & c';
7 | return next();
8 | }
9 | };
10 |
11 |
--------------------------------------------------------------------------------
/packages/plover-xview/README.md:
--------------------------------------------------------------------------------
1 | # plover xview
2 |
3 | [![NPM version][npm-image]][npm-url]
4 |
5 | Plover视图插件,提供页面渲染的工具函数和重用方案。
6 |
7 | [npm-image]: https://img.shields.io/npm/v/plover-xview.svg?style=flat-square
8 | [npm-url]: https://www.npmjs.com/package/plover-xview
9 |
10 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/components/startup/lib/middlewares/b.js:
--------------------------------------------------------------------------------
1 | /**
2 | * function that return async function
3 | */
4 | module.exports = function() {
5 | return async (ctx, next) => {
6 | ctx.body = ctx.body + ' & b';
7 | await next();
8 | };
9 | };
10 |
11 |
--------------------------------------------------------------------------------
/packages/ploverx/README.md:
--------------------------------------------------------------------------------
1 | # ploverx
2 |
3 |
4 | [![NPM version][npm-image]][npm-url]
5 |
6 |
7 | 集成常用plover插件,提供全栈nodejs web应用开发环境。
8 |
9 |
10 | [npm-image]: https://img.shields.io/npm/v/ploverx.svg?style=flat-square
11 | [npm-url]: https://www.npmjs.com/package/ploverx
12 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/components/plugin/modules/test/app.js:
--------------------------------------------------------------------------------
1 | module.exports = function(app) {
2 | app.addMiddleware(function* (next) {
3 | if (this.url === '/test') {
4 | this.body = 'hello test';
5 | } else {
6 | yield* next;
7 | }
8 | });
9 | };
10 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/child/views/elements/big.art:
--------------------------------------------------------------------------------
1 | {{title}}
2 |
3 |
4 | {{=app.control('elements/book-item', { name: 'Hi', index: 126 })}}
5 |
6 |
7 |
8 |
9 | {{app.view('show')}}
10 |
11 |
--------------------------------------------------------------------------------
/packages/plover-util/lib/index.js:
--------------------------------------------------------------------------------
1 | exports.assign = require('./assign');
2 | exports.Array = require('./array');
3 | exports.delegate = require('./delegate');
4 | exports.Lang = require('./lang');
5 | exports.RouteInfo = require('./route-info');
6 | exports.SafeString = require('./safe-string');
7 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/lib/helpers/url-helper.js:
--------------------------------------------------------------------------------
1 | class UrlHelper {
2 | constructor(rd) {
3 | this.ctx = rd.ctx;
4 | }
5 |
6 |
7 | getOfferUrl(id) {
8 | return `http://${this.ctx.hostname}/offer/${id}`;
9 | }
10 | }
11 |
12 |
13 | module.exports = UrlHelper;
14 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/expects/index-with-default-layout.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -layouts#index
5 |
6 |
7 | plover
8 | version: 1.0.0
9 | nodejs webframework
10 |
11 |
12 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/app/modules/testplugin/plugin.js:
--------------------------------------------------------------------------------
1 | module.exports = function(app) {
2 | app.addMiddleware(function* (next) {
3 | if (this.url === '/testplugin') {
4 | this.body = 'hello testplugin';
5 | } else {
6 | yield next;
7 | }
8 | });
9 | };
10 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/child/views/panel.ejs:
--------------------------------------------------------------------------------
1 | Panel
2 |
3 | <%- app.view('books') %>
4 |
5 |
6 |
7 | <%- app.control('elements/big', { title: 'Big 2' }) %>
8 |
9 |
10 | <%- app.control('elements/async') %>
11 |
--------------------------------------------------------------------------------
/packages/plover-util/README.md:
--------------------------------------------------------------------------------
1 | # plover-util
2 |
3 |
4 | [![NPM version][npm-image]][npm-url]
5 |
6 |
7 | 【核心模块】核心模块中常用工具模块。
8 |
9 | **注: 核心中没用到的模块不放到此包中**
10 |
11 | [npm-image]: https://img.shields.io/npm/v/plover-util.svg?style=flat-square
12 | [npm-url]: https://www.npmjs.com/package/plover-util
13 |
--------------------------------------------------------------------------------
/packages/plover-util/lib/assign.js:
--------------------------------------------------------------------------------
1 | module.exports = function() {
2 | const args = arguments;
3 | const des = args[0];
4 | for (let i = 1, c = args.length; i < c; i++) {
5 | const src = args[i];
6 | for (const k in src) {
7 | des[k] = src[k];
8 | }
9 | }
10 | return des;
11 | };
12 |
--------------------------------------------------------------------------------
/packages/plover-util/lib/delegate.js:
--------------------------------------------------------------------------------
1 | module.exports = function(o, target, methods) {
2 | for (const name of methods) {
3 | const fn = target[name];
4 | if (!fn) {
5 | throw new Error(`target method undefined: ${name}`);
6 | }
7 | o[name] = fn.bind(target);
8 | }
9 | return o;
10 | };
11 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/components/plugin/modules/test-withorder/app.js:
--------------------------------------------------------------------------------
1 | module.exports = function(app) {
2 | app.addMiddleware(function* (next) {
3 | if (this.url === '/test-withorder') {
4 | this.body = 'hello test-withorder';
5 | } else {
6 | yield* next;
7 | }
8 | });
9 | };
10 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/README.md:
--------------------------------------------------------------------------------
1 | # plover-module-resolver
2 |
3 |
4 | [![NPM version][npm-image]][npm-url]
5 |
6 | 【核心模块】 plover模块加载和解析器
7 |
8 |
9 | [npm-image]: https://img.shields.io/npm/v/plover-module-resolver.svg?style=flat-square
10 | [npm-url]: https://www.npmjs.com/package/plover-module-resolver
11 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/expects/index-with-banner.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | plover
8 | version: 1.0.0
9 | nodejs webframework
10 | Banner
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/expects/app-helper-child-view-not-found.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | child view not found
8 |
9 | Not Found: not-found
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/packages/plover-util/lib/safe-string.js:
--------------------------------------------------------------------------------
1 | class SafeString {
2 | constructor(string) {
3 | this.string = typeof string === 'string' ?
4 | string : '' + string;
5 | }
6 |
7 |
8 | toString() {
9 | return this.string;
10 | }
11 |
12 |
13 | toHTML() {
14 | return this.string;
15 | }
16 | }
17 |
18 |
19 | module.exports = SafeString;
20 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/assets/views/layout.art:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{title}}
5 | {{assets.css('/layout-lib.css')}}
6 | {{assets.js('/layout-lib.js')}}
7 | {{= assets.cssTag()}}
8 |
9 |
10 | {{content}}
11 | {{= assets.jsTag()}}
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/assets/views/layoutejs.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | <%- assets.css('/layout-lib.css') %>
6 | <%- assets.js('/layout-lib.js') %>
7 | <%- assets.cssTag() %>
8 |
9 |
10 | <%- content %>
11 | <%- assets.jsTag() %>
12 |
13 |
14 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/fixtures/app/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "dependencies": {
4 | "@ali/coo": "1.0.0",
5 | "ignore": "1.0.0",
6 | "item": "1.0.0",
7 | "not": "1.0.0",
8 | "offer": "1.0.0",
9 | "not-exists": "2.0.0"
10 | },
11 | "devDependencies": {
12 | "less": "2.0.0",
13 | "webpack": "1.0.0"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/helper/views/view.art:
--------------------------------------------------------------------------------
1 |
2 |
offer
3 |
{{xview.formatPrice(123)}}
4 |
5 |
6 |
7 |
offer
8 |
{{xview.formatPrice(456)}}
9 |
10 |
--------------------------------------------------------------------------------
/packages/plover-router/lib/method.js:
--------------------------------------------------------------------------------
1 | /* eslint no-underscore-dangle: 0 */
2 |
3 | module.exports = function PloverMethod(ctx, next) {
4 | if (ctx.method === 'POST') {
5 | const method = ctx.headers['x-http-method-override'] ||
6 | ctx.request.body._method;
7 | if (method) {
8 | ctx.method = method.toUpperCase();
9 | }
10 | }
11 | return next();
12 | };
13 |
--------------------------------------------------------------------------------
/packages/plover-util/lib/array.js:
--------------------------------------------------------------------------------
1 | /**
2 | * push src中的所有项到des
3 | * @param {Array} des - 目标数组
4 | * @param {Array} src - 需要合并的数组
5 | * @return {Array} - 目标数组
6 | */
7 | exports.pushAll = function(des, src) {
8 | if (!src || !src.length) {
9 | return des;
10 | }
11 |
12 | for (const item of src) {
13 | des.push(item);
14 | }
15 |
16 | return des;
17 | };
18 |
--------------------------------------------------------------------------------
/packages/plover-util/test/index.js:
--------------------------------------------------------------------------------
1 | const util = require('..');
2 |
3 |
4 | describe('plover-util', function() {
5 | const fields = [
6 | 'Array',
7 | 'delegate',
8 | 'Lang',
9 | 'RouteInfo',
10 | 'SafeString'
11 | ];
12 |
13 | fields.forEach(name => {
14 | it(`#${name}`, function() {
15 | util[name].should.not.undefined();
16 | });
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/lib/services/product-service.js:
--------------------------------------------------------------------------------
1 | class ProductService {
2 | constructor(ctx) {
3 | this.ctx = ctx;
4 | }
5 |
6 |
7 | get(id) {
8 | const code = this.ctx.codeService.generate();
9 | return {
10 | id: id,
11 | name: `product-${id}`,
12 | code: code
13 | };
14 | }
15 | }
16 |
17 |
18 | module.exports = ProductService;
19 |
--------------------------------------------------------------------------------
/packages/plover-util/test/array.js:
--------------------------------------------------------------------------------
1 | const arrayUtil = require('..').Array;
2 |
3 |
4 | describe('plover-util/lib/array', function() {
5 | it('#pushAll', function() {
6 | const a = [1, 2, 3];
7 | const b = ['a', 'b', 'c'];
8 | arrayUtil.pushAll(a, b);
9 | a.should.eql([1, 2, 3, 'a', 'b', 'c']);
10 |
11 | arrayUtil.pushAll([1, 2, 3], null).should.eql([1, 2, 3]);
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/simple/modules/layouts/views/view.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 | <% assets.css('//at.alicdn.com/t/font_919397_0v0s6t15b72l.css') %>
4 | <% assets.css('lib.css') %>
5 | <% assets.js('semantic.js') %>
6 | <%- assets.cssTag() %>
7 | <%- assets.jsTag('lib') %>
8 |
9 |
10 | <%- content %>
11 | <%- assets.jsTag() %>
12 |
13 |
14 |
--------------------------------------------------------------------------------
/packages/plover-web/lib/web/params.js:
--------------------------------------------------------------------------------
1 | const PARAMS = Symbol('params');
2 |
3 |
4 | module.exports = function(app) {
5 | Object.defineProperty(app.context, 'params', {
6 | get: function() {
7 | let params = this[PARAMS];
8 | if (!params) {
9 | params = Object.assign({}, this.query, this.request.body);
10 | this[PARAMS] = params;
11 | }
12 | return params;
13 | }
14 | });
15 | };
16 |
--------------------------------------------------------------------------------
/packages/plover-web/lib/web/session.js:
--------------------------------------------------------------------------------
1 | const session = require('koa-session');
2 |
3 |
4 | module.exports = function(opts, app) {
5 | opts = Object.assign({}, opts);
6 | opts.store = createStore(opts.store, opts.storeOpts);
7 | return session(opts, app);
8 | };
9 |
10 |
11 | function createStore(store, opts) {
12 | if (store === 'redis') {
13 | return require('koa-redis')(opts);
14 | }
15 | return null;
16 | }
17 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/expects/helper.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 |
12 |
offer
13 |
456元
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/packages/plover-util/test/safe-string.js:
--------------------------------------------------------------------------------
1 | const SafeString = require('..').SafeString;
2 |
3 |
4 | describe('plover-util/lib/safe-string', function() {
5 | it('use as string', function() {
6 | let s = new SafeString('');
7 | s.toString().should.equal('');
8 | s.toHTML().should.equal('');
9 |
10 | s = new SafeString(123);
11 | s.toString().should.equal('123');
12 | });
13 | });
14 |
--------------------------------------------------------------------------------
/packages/plover-util/test/assign.js:
--------------------------------------------------------------------------------
1 | const assign = require('..').assign;
2 |
3 |
4 | describe('plover-util/lib/assign', function() {
5 | it('test', function() {
6 | const des = {};
7 | const des2 = assign(des, { a: 1 }, { a: 2, b: 3 });
8 | des.should.equal(des2);
9 | des2.should.eql({ a: 2, b: 3 });
10 |
11 | assign({ k: 123 }, { b: undefined, c: null })
12 | .should.eql({ k: 123, b: undefined, c: null });
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/packages/plover-assets/lib/util/concat-url.js:
--------------------------------------------------------------------------------
1 | module.exports = function(prefix, list) {
2 | const results = [];
3 | let now = '';
4 | for (const url of list) {
5 | if (now !== '') {
6 | now += ',';
7 | }
8 | now += url;
9 | if (now.length > 1500) {
10 | results.push(prefix + now);
11 | now = '';
12 | }
13 | }
14 |
15 | if (now) {
16 | results.push(prefix + now);
17 | }
18 |
19 | return results;
20 | };
21 |
22 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/modules/list/views/view.art:
--------------------------------------------------------------------------------
1 | Hello World
2 |
3 | {{assets.css('lib:css/tabs.css', 'lib')}}
4 | {{assets.css('css/mytabs.css')}}
5 |
6 | {{assets.js('lib:js/tabs.js', 'lib')}}
7 | {{assets.js('js/mytabs.js')}}
8 |
9 |
10 |
11 |
12 |
13 | {{app.control('form#select')}}
14 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/layouts/index.js:
--------------------------------------------------------------------------------
1 | exports.view = function() {
2 | this.render();
3 | };
4 |
5 |
6 | exports.index = function() {
7 | this.render();
8 | };
9 |
10 |
11 | exports.mobile = function() {
12 | this.render();
13 | };
14 |
15 |
16 | exports.simple = function() {
17 | this.body = 'simple layout\n' + this.data.content;
18 | };
19 |
20 |
21 | exports.invalid = function() {
22 | // no render, page will 404
23 | };
24 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/modules/layouts/views/view.art:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{assets.cssTag('lib')}}
4 | {{assets.cssTag()}}
5 | {{assets.css('http://g.cdn.com/bootstrap.css', 'lib')}}
6 | {{assets.css('lib:css/vue.css')}}
7 | {{assets.js('http://g.cdn.com/jquery.js', 'lib')}}
8 | {{assets.js('lib:css/vue.js')}}
9 |
10 |
11 | {{content}}
12 | {{assets.jsTag('lib')}}
13 | {{assets.jsTag()}}
14 |
15 |
16 |
--------------------------------------------------------------------------------
/packages/ploverx/test/fixtures/app/lib/try-logger.js:
--------------------------------------------------------------------------------
1 | const logger = require('plover-logger')('my:lib/hello');
2 |
3 |
4 | module.exports = function() {
5 | logger.info('init try-logger middleware');
6 | return async (ctx, next) => {
7 | if (ctx.path === '/logger') {
8 | const level = ctx.query.level || 'info';
9 | logger[level]('some logger message');
10 | ctx.body = `logger ${level}`;
11 | } else {
12 | await next();
13 | }
14 | };
15 | };
16 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/filter/index.js:
--------------------------------------------------------------------------------
1 | exports.view = function() {
2 | this.layout = false;
3 | this.render();
4 | };
5 |
6 |
7 | exports.beforeRender = function* () {
8 | if (this.query.break === 'true') {
9 | this.body = this.query.body ? 'break in beforeRender' : null;
10 | return false;
11 | }
12 | };
13 |
14 | exports.afterRender = function() {
15 | if (this.query.filterAfter === 'true') {
16 | this.body = 'after:' + this.body;
17 | }
18 | };
19 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/lib/helpers/cms.js:
--------------------------------------------------------------------------------
1 | class CMSHelper {
2 | constructor(rd, viewRender) {
3 | this.rd = rd;
4 | this.viewRender = viewRender;
5 | }
6 |
7 | render(url) {
8 | const defer = new Promise(function(resolve) {
9 | setTimeout(function() {
10 | resolve({ content: 'cms: ' + url });
11 | }, 100);
12 | });
13 | return this.viewRender.renderAsync(this.rd, defer, url);
14 | }
15 | }
16 |
17 |
18 | module.exports = CMSHelper;
19 |
20 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/expect/simple.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Hello
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/packages/plover-web/lib/web/flash.js:
--------------------------------------------------------------------------------
1 | const KEY = 'plover-flash';
2 | const FLASH = Symbol(KEY);
3 |
4 |
5 | module.exports = function(app) {
6 | Object.defineProperty(app.context, 'flash', {
7 | get: function() {
8 | return this[FLASH];
9 | }
10 | });
11 |
12 |
13 | return async function PloverFlash(ctx, next) {
14 | ctx[FLASH] = ctx.session[KEY] || {};
15 | delete ctx.session[KEY];
16 |
17 | await next();
18 |
19 | if (ctx.status === 302) {
20 | ctx.session[KEY] = ctx[FLASH];
21 | }
22 | };
23 | };
24 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/util.js:
--------------------------------------------------------------------------------
1 | const pathUtil = require('path');
2 | const util = require('../lib/util');
3 |
4 |
5 | describe('plover-moduler-resolver/lib/util', function() {
6 | it('util.isPloverModule', function() {
7 | const root = pathUtil.join(__dirname, 'fixtures/app/node_modules');
8 | util.isPloverModule(pathUtil.join(root, 'ignore')).should.be.false();
9 | util.isPloverModule(pathUtil.join(root, 'item')).should.be.true();
10 | util.isPloverModule(pathUtil.join(root, 'not')).should.be.false();
11 | });
12 | });
13 |
--------------------------------------------------------------------------------
/packages/plover-web/lib/util/util.js:
--------------------------------------------------------------------------------
1 | const minimatch = require('minimatch');
2 |
3 |
4 | exports.regularRules = function(rules) {
5 | rules = rules || [];
6 | rules = Array.isArray(rules) ? rules : [rules];
7 | return rules.map(rule => {
8 | if (typeof rule === 'string') {
9 | rule = minimatch.makeRe(rule);
10 | }
11 | return rule;
12 | });
13 | };
14 |
15 |
16 | exports.testRules = function(rules, path) {
17 | return rules.length && rules.some(rule => rule.test(path));
18 | };
19 |
20 |
21 | exports.CSRF_CHECKED = Symbol('CSRF_CHECKED');
22 |
--------------------------------------------------------------------------------
/packages/plover-xview/test/fixtures/expect/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Hello World!
11 | Child
12 |
13 |
14 |
--------------------------------------------------------------------------------
/packages/plover/test/helpers/app.js:
--------------------------------------------------------------------------------
1 | const pathUtil = require('path');
2 | const request = require('supertest');
3 |
4 | const plover = require('../../');
5 | const equal = require('../util').equalWith;
6 |
7 |
8 | describe('helpers/app', function() {
9 | const root = pathUtil.join(__dirname, '../fixtures/core/app');
10 | const app = plover({ applicationRoot: root });
11 | const agent = request.agent(app.callback());
12 |
13 |
14 | it('quick render child view not found', function() {
15 | return agent.get('/app-helper/child-view-not-found')
16 | .expect(equal('app-helper-child-view-not-found.html'));
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/assets/index.js:
--------------------------------------------------------------------------------
1 | exports.view = function() {
2 | this.layout = this.query.layoutejs ? 'layoutejs' : 'layout';
3 | this.render();
4 | };
5 |
6 |
7 | exports.child = function() {
8 | this.render();
9 | };
10 |
11 |
12 | exports.layout = function() {
13 | this.render();
14 | };
15 |
16 |
17 | exports.layoutejs = exports.layout;
18 |
19 |
20 | exports.navigate = function* () {
21 | this.layout = false;
22 | const result = yield this.navigate('view');
23 | const data = {
24 | content: result.content,
25 | myassets: result.assets.default
26 | };
27 | this.render(data);
28 | };
29 |
--------------------------------------------------------------------------------
/packages/plover-web/test/web/params.js:
--------------------------------------------------------------------------------
1 | const Koa = require('koa');
2 | const request = require('supertest');
3 |
4 | describe('plover-web/web/params', () => {
5 | it('ctx.params', () => {
6 | const app = new Koa();
7 | require('../../lib/web/params')(app);
8 | app.use(require('koa-bodyparser')());
9 | app.use(ctx => {
10 | const params = ctx.params;
11 | params.should.equal(ctx.params); // cached
12 | ctx.body = ctx.params;
13 | });
14 |
15 | return request(app.callback())
16 | .post('/?page=23')
17 | .send({ name: 'plover' })
18 | .expect({ page: '23', name: 'plover' });
19 | });
20 | });
21 |
22 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/expects/assets-navigate.html:
--------------------------------------------------------------------------------
1 | Assets
2 |
3 | child
4 |
5 |
13 |
14 | - /view-lib.css
15 | - /form/css/input.css
16 | - /form/css/form.css
17 | - /assets/css/view.css
18 | - /child-lib.css
19 | - /assets/css/child.css
20 | - /view-lib.js
21 | - /assets/js/view.js
22 | - /child-lib.js
23 | - /assets/js/child.js
24 |
25 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/child/views/view.art:
--------------------------------------------------------------------------------
1 | {{app.view('header', { title: 'Books' })}}
2 |
3 |
4 | {{each items as item }}
5 | - {{app.control('elements/item', { item: item })}}
6 | {{/each}}
7 |
8 |
9 | {{app.control('not-found:view')}}
10 |
11 | {{app.view('child:not-found')}}
12 | {{app.control('child:not-found')}}
13 |
14 | {{app.control('elements/footer')}}
15 |
16 | {{app.view('panel')}}
17 |
18 | {{app.control('form:form', {
19 | name: 'Plover',
20 | className: 'my-select',
21 | options: [
22 | { value: 1, text: 'one' },
23 | { value: 2, text: 'two' },
24 | { value: 3, text: 'three' }
25 | ]
26 | })}}
27 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/app/public2/g/manifest.json:
--------------------------------------------------------------------------------
1 | {"form/css/select.css":"form/css/select-da39a3ee5e.css","index/css/test.css":"index/css/test-63b507f051.css","index/css/view.css":"index/css/view-b8e63ceacb.css","index/js/a.js":"index/js/a-17384ca4fc.js","index/js/b.js":"index/js/b-400ea80d88.js","layouts/css/view.css":"layouts/css/view-534f283a0c.css","layouts/js/view.js":"layouts/js/view-da39a3ee5e.js","lib/css/tab.css":"lib/css/tab-da39a3ee5e.css","lib/js/tab.js":"lib/js/tab-da39a3ee5e.js","list/css/mytab.css":"list/css/mytab-da39a3ee5e.css","list/css/view.css":"list/css/view-534f283a0c.css","list/js/mytab.js":"list/js/mytab-da39a3ee5e.js","list/js/view.js":"list/js/view-da39a3ee5e.js"}
--------------------------------------------------------------------------------
/packages/plover-assets/test/simple-helper.js:
--------------------------------------------------------------------------------
1 | const pathUtil = require('path');
2 | const mm = require('plover-test-mate');
3 |
4 | const plugin = require('../lib/plugin');
5 |
6 |
7 | describe('simple-helper', function() {
8 | const appRoot = pathUtil.join(__dirname, 'fixtures/simple');
9 | const expectRoot = pathUtil.join(__dirname, 'fixtures/expect');
10 |
11 | it('use simple helper', function() {
12 | const app = mm({
13 | applicationRoot: appRoot,
14 | expectRoot: expectRoot,
15 | port: 60005,
16 | env: 'test'
17 | });
18 |
19 | app.install('plover-ejs');
20 | app.install(plugin);
21 |
22 | return app.test('/page', 'simple.html');
23 | });
24 | });
25 |
26 |
--------------------------------------------------------------------------------
/packages/plover-web/lib/security/assert-method.js:
--------------------------------------------------------------------------------
1 | module.exports = function(app) {
2 | app.context.assertMethod = assertMethod;
3 | };
4 |
5 |
6 | /**
7 | * 验证Http Method
8 | *
9 | * @param {String|Array} name - 名称,如 get post put
10 | */
11 | function assertMethod(name) {
12 | let valid = false;
13 | const method = this.method;
14 |
15 | if (typeof name === 'string') {
16 | valid = name.toUpperCase() === method;
17 | } else if (Array.isArray(name)) {
18 | valid = name.some(item => {
19 | return item.toUpperCase() === method;
20 | });
21 | }
22 |
23 | if (!valid) {
24 | this.throw(401, `invalid request method, expect: ${name}, actual: ${method}`);
25 | }
26 | }
27 |
28 |
--------------------------------------------------------------------------------
/packages/plover/test/util.js:
--------------------------------------------------------------------------------
1 | const pathUtil = require('path');
2 | const fs = require('fs');
3 |
4 |
5 | exports.fixture = function(path) {
6 | path = pathUtil.join(__dirname, 'fixtures', path);
7 | return fs.readFileSync(path, 'utf-8');
8 | };
9 |
10 |
11 | exports.htmlEqual = function(html) {
12 | return function(res) {
13 | trimSpace(res.text).should.equal(trimSpace(html));
14 | };
15 | };
16 |
17 |
18 | exports.equalWith = function(path) {
19 | path = pathUtil.join('expects', path);
20 | return exports.htmlEqual(exports.fixture(path));
21 | };
22 |
23 |
24 | /*
25 | * 去掉标签之前和之后的空格
26 | */
27 | function trimSpace(html) {
28 | return html.trim().replace(/>\s+/g, '>').replace(/\s+
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | Assets
11 | child
12 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/packages/plover-assets/lib/plugin.js:
--------------------------------------------------------------------------------
1 | const UrlBuilder = require('./url-builder');
2 |
3 |
4 | module.exports = function(app) {
5 | const settings = app.settings;
6 | const config = settings.assets || (settings.assets = {});
7 | // 简单资源模式
8 | if (config.simple) {
9 | app.addHelper('assets', require('./simple-helper'));
10 | return;
11 | }
12 |
13 | require('./vendor')(app);
14 | app.addHelper('assets', require('./helper'));
15 |
16 | // 其他插件可以使用`assetsHandler`扩展资源处理器
17 | // 使用`ploverAssetsUrlBuilder`替换UrlBuilder
18 | app.ploverAssetsHandler = require('./handler');
19 | app.ploverAssetsUrlBuilder = new UrlBuilder(app);
20 |
21 | if (settings.development || config.enableMiddleware) {
22 | app.addMiddleware(require('./middleware'), 0);
23 | }
24 | };
25 |
26 |
--------------------------------------------------------------------------------
/packages/plover-router/lib/plugin.js:
--------------------------------------------------------------------------------
1 | const assert = require('assert');
2 | const router = require('./router');
3 |
4 |
5 | module.exports = function(app) {
6 | app.use(require('./method'), { after: 'koa-bodyparser' });
7 | const fn = app.config.routes || app.settings.routes || function() {};
8 | assert(typeof fn === 'function', 'config.routes should be typeof function.');
9 |
10 | const info = router(fn);
11 |
12 | const middlewares = info.middlewares;
13 | for (const item of middlewares) {
14 | const opts = Object.assign({}, item.options);
15 | opts.match = item.match;
16 | app.use(item.middleware, opts);
17 | }
18 |
19 | const routes = info.routes;
20 | for (const route of routes) {
21 | app.addRoute(route.match, route.to, { method: route.verb });
22 | }
23 | };
24 |
--------------------------------------------------------------------------------
/packages/ploverx/lib/index.js:
--------------------------------------------------------------------------------
1 | const plover = require('plover');
2 | const util = require('./util');
3 | const installLogger = require('./install-logger');
4 |
5 |
6 | /* eslint no-process-env: 0, no-console: 0 */
7 |
8 |
9 | module.exports = function(options) {
10 | options = options || {};
11 | const settings = util.loadSettings(options.applicationRoot);
12 |
13 | const restore = installLogger(settings);
14 |
15 | const app = plover(settings);
16 |
17 | app.run = () => {
18 | const port = (settings.server || {}).port || 4000;
19 | app.listen(port).then(() => {
20 | console.log(`server started: 127.0.0.1:${port}, env: ${settings.env}`);
21 | });
22 | };
23 |
24 | // for test
25 | app.__restoreLogger = restore; // eslint-disable-line
26 |
27 | return app;
28 | };
29 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/expect/list-concat.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Hello World
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/packages/plover-util/test/delegate.js:
--------------------------------------------------------------------------------
1 | const delegate = require('..').delegate;
2 |
3 |
4 | describe('plover-util/lib/delegate', function() {
5 | it('should delegate methods to target object', function() {
6 | const target = {
7 | name: 'hello',
8 | say: function() {
9 | return this.name;
10 | },
11 | ok: function() {
12 | return this.name + ' ok';
13 | }
14 | };
15 |
16 | const o = {};
17 | delegate(o, target, ['say', 'ok']);
18 | o.say().should.equal('hello');
19 | o.ok().should.equal('hello ok');
20 | });
21 |
22 |
23 | it('should throw error when target method undefined', function() {
24 | const target = {};
25 | const o = {};
26 | (() => {
27 | delegate(o, target, ['hello']);
28 | }).should.throw('target method undefined: hello');
29 | });
30 | });
31 |
--------------------------------------------------------------------------------
/packages/plover-web/test/fixtures/app/config/app.js:
--------------------------------------------------------------------------------
1 | const pathUtil = require('path');
2 |
3 |
4 | module.exports = {
5 | applicationRoot: pathUtil.join(__dirname, '..'),
6 |
7 | security: {
8 | headers: {
9 | 'X-Frame-Options': false
10 | }
11 | },
12 |
13 |
14 | web: {
15 | keys: ['17e6b6bc6129097383dcad4fa1602233'],
16 |
17 | favicon: pathUtil.join(__dirname, '../public/favicon.ico'),
18 | rtime: {},
19 | conditional: {},
20 | etag: {},
21 |
22 | cors: {
23 | match: ['*.google.com']
24 | },
25 |
26 | bodyParser: {},
27 |
28 | static: {},
29 |
30 | outputCharset: true,
31 |
32 | compress: {
33 | enable: true,
34 | filter: function(contentType) {
35 | return (/text/i.test(contentType));
36 | },
37 | threshold: 20,
38 | flush: require('zlib').Z_SYNC_FLUSH
39 | }
40 | }
41 | };
42 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/lib/vertify.js:
--------------------------------------------------------------------------------
1 | const semver = require('semver');
2 |
3 |
4 | /**
5 | * 验证模块依赖是否兼容
6 | *
7 | * @param {Map} modules - 模块列表
8 | */
9 | module.exports = function(modules) {
10 | for (const info of modules.values()) {
11 | vertifyModule(modules, info);
12 | }
13 | };
14 |
15 |
16 | function vertifyModule(modules, info) {
17 | const dep = info.dep;
18 | if (!dep) {
19 | return;
20 | }
21 |
22 | for (const name in dep) {
23 | const dinfo = modules.get(name);
24 | if (!dinfo) {
25 | throw new Error(`the module ${name} which is required by ${info.name} can not be found.`); // eslint-disable-line
26 | }
27 | const valid = semver.satisfies(dinfo.version, dep[name]);
28 | if (!valid) {
29 | throw new Error(`${name}@${dinfo.version} is not compatible with ${info.name} which depend on ${name}@${dep[name]}`); // eslint-disable-line
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/packages/plover-util/lib/route-info.js:
--------------------------------------------------------------------------------
1 | const re = /^([^#:]+)[#:](.+)$/;
2 |
3 |
4 | /**
5 | * 解析url构造一个route信息,用于定位一个资源
6 | *
7 | * @param {Object} parent - 父route信息
8 | * @param {String} url - 路径
9 | * @return {Object} - route信息
10 | * - module
11 | * - action
12 | */
13 | exports.parse = function(parent, url) {
14 | const route = {};
15 | const match = re.exec(url);
16 | if (match) {
17 | route.module = match[1];
18 | route.action = match[2];
19 | } else {
20 | route.module = parent.module;
21 | route.action = url;
22 | }
23 |
24 | route.parent = parent;
25 | route.root = parent.root;
26 |
27 | return exports.regular(route);
28 | };
29 |
30 |
31 | /**
32 | * 规范化route对象
33 | * @param {Object} route - route信息
34 | * @return {Object} - 规范后的route信息
35 | */
36 | exports.regular = function(route) {
37 | route.action = route.action || 'view';
38 | route.url = route.module + ':' + route.action;
39 | return route;
40 | };
41 |
42 |
--------------------------------------------------------------------------------
/packages/plover-web/test/security/assert-method.js:
--------------------------------------------------------------------------------
1 | const Koa = require('koa');
2 | const request = require('supertest');
3 | const co = require('co');
4 | const assertMethod = require('../../lib/security/assert-method');
5 |
6 |
7 | describe('plover-web/security/assert-method', () => {
8 | const app = new Koa();
9 | assertMethod(app);
10 |
11 | app.use(ctx => {
12 | if (ctx.path === '/post') {
13 | ctx.assertMethod('post');
14 | }
15 | if (ctx.path === '/get') {
16 | ctx.assertMethod(['get']);
17 | }
18 | ctx.body = 'ok';
19 | });
20 |
21 | const agent = request.agent(app.callback());
22 |
23 | it('test', () => {
24 | return co(function* () {
25 | yield agent.get('/get').expect('ok');
26 | yield agent.post('/get')
27 | .expect(401, 'invalid request method, expect: get, actual: POST');
28 |
29 | yield agent.post('/post').expect('ok');
30 | yield agent.get('/post').expect(401);
31 | });
32 | });
33 | });
34 |
35 |
--------------------------------------------------------------------------------
/packages/plover-util/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "plover-util",
3 | "version": "1.9.0",
4 | "description": "【核心模块】有些在核心中使用的工具模块,但在插件中也要使用,所以独立出来。",
5 | "main": "lib/index",
6 | "scripts": {
7 | "test": "mocha --require should --reporter spec --recursive --check-leaks test/",
8 | "cov": "istanbul cover _mocha -- --require should --recursive --check-leaks test/ -R spec"
9 | },
10 | "files": [
11 | "lib"
12 | ],
13 | "dependencies": {},
14 | "devDependencies": {
15 | "istanbul": "^1.1.0-alpha",
16 | "mocha": "^8.0.1",
17 | "should": "^13.1.2"
18 | },
19 | "repository": {
20 | "type": "git",
21 | "url": "git+https://github.com/alibaba/plover.git"
22 | },
23 | "publishConfig": {
24 | "registry": "https://registry.npmjs.org"
25 | },
26 | "author": "bencode@163.com",
27 | "license": "Apache-2.0",
28 | "bugs": {
29 | "url": "https://github.com/alibaba/plover/issues"
30 | },
31 | "homepage": "https://github.com/alibaba/plover#readme"
32 | }
33 |
--------------------------------------------------------------------------------
/packages/plover/test/util/util.js:
--------------------------------------------------------------------------------
1 | const pathUtil = require('path');
2 |
3 | const gutil = require('../../lib/util/util');
4 |
5 |
6 | describe('util/util', function() {
7 | it('util.delegateGetters', function() {
8 | const Service = function(name) {
9 | this.name = name;
10 | };
11 |
12 | const services = {
13 | $get: function(name) {
14 | return new Service(name);
15 | },
16 |
17 | myName: 'hello'
18 | };
19 |
20 | gutil.delegateGetters(services, ['dsService', 'offerService', 'myName']);
21 | services.dsService.name.should.equal('dsService');
22 | services.myName.should.equal('hello');
23 | });
24 |
25 |
26 | it('util.loadModule', function() {
27 | const root = pathUtil.join(__dirname, '../fixtures/util/util');
28 | gutil.loadModule(root, './lib/a').should.equal('a');
29 | gutil.loadModule(root, 'a').should.equal('a');
30 | gutil.loadModule(root, pathUtil.join(root, 'lib/a')).should.equal('a');
31 | });
32 | });
33 |
34 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "plover-module-resolver",
3 | "version": "2.0.0",
4 | "description": "【核心模块】 plover模块加载和解析器。",
5 | "main": "lib/index.js",
6 | "scripts": {
7 | "test": "mocha --require should --reporter spec --recursive --check-leaks test/",
8 | "cov": "istanbul cover _mocha -- --require should --recursive --check-leaks test/ -R spec"
9 | },
10 | "files": [
11 | "lib"
12 | ],
13 | "dependencies": {
14 | "debug": "^4.1.1",
15 | "resolve-from": "^5.0.0",
16 | "semver": "^7.3.2"
17 | },
18 | "devDependencies": {
19 | "istanbul": "^1.1.0-alpha",
20 | "mocha": "^8.0.1",
21 | "should": "^13.1.2"
22 | },
23 | "repository": {
24 | "type": "git",
25 | "url": "git+https://github.com/alibaba/plover.git"
26 | },
27 | "author": "bencode@163.com",
28 | "license": "Apache-2.0",
29 | "bugs": {
30 | "url": "https://github.com/alibaba/plover/issues"
31 | },
32 | "homepage": "https://github.com/alibaba/plover#readme"
33 | }
34 |
--------------------------------------------------------------------------------
/packages/plover/lib/util/error-handler.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 错误处理中间件
3 | */
4 |
5 | const util = require('util');
6 |
7 | const logger = require('plover-logger')('plover:util/error-handler');
8 |
9 |
10 | module.exports = function(config) {
11 | const development = (config || {}).env === 'development';
12 | return function errorHandler(ctx, next) {
13 | return next().catch(e => {
14 | const status = e.status || 500;
15 | // 只处理500及以上的异常
16 | if (status < 500) {
17 | throw e;
18 | }
19 |
20 | logger.error(e);
21 | ctx.app.emit('error', e, ctx);
22 | ctx.status = status;
23 |
24 | const message = development ?
25 | '' + util.inspect(e) + '\n' + (e.stack || '') + '
' :
26 | 'Internel Server Error';
27 |
28 | if (ctx.is('application/json') ||
29 | !ctx.accepts('html') && ctx.accepts('application/json')) {
30 | ctx.body = { success: false, message };
31 | } else {
32 | ctx.body = message;
33 | }
34 | });
35 | };
36 | };
37 |
--------------------------------------------------------------------------------
/packages/plover-web/test/web/flash.js:
--------------------------------------------------------------------------------
1 | const Koa = require('koa');
2 | const request = require('supertest');
3 | const co = require('co');
4 |
5 |
6 | describe('plover-web/web/flash', function() {
7 | it('test', function() {
8 | const app = new Koa();
9 |
10 | app.keys = ['0627e'];
11 |
12 | app.use(require('koa-session')({}, app));
13 | app.use(require('../../lib/web/flash')(app));
14 |
15 | app.use(ctx => {
16 | if (ctx.path === '/update') {
17 | ctx.flash.errors = {
18 | message: 'some error happen'
19 | };
20 | ctx.redirect('/save');
21 | } else if (ctx.path === '/save') {
22 | ctx.body = ctx.flash.errors;
23 | } else {
24 | ctx.body = 'hello';
25 | }
26 | });
27 |
28 | const agent = request.agent(app.callback());
29 | return co(function* () {
30 | yield agent.get('/update').expect(302);
31 | yield agent.get('/save').expect({ message: 'some error happen' });
32 |
33 | yield agent.get('/hello').expect('hello');
34 | });
35 | });
36 | });
37 |
--------------------------------------------------------------------------------
/packages/plover-logger/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "plover-logger",
3 | "version": "1.7.0",
4 | "description": "【核心模块】提供统一的日志接口,让使用者不用关心日志实现,方便日志实现切换。",
5 | "main": "lib/index.js",
6 | "scripts": {
7 | "test": "mocha --require should --reporter spec --recursive --check-leaks test/",
8 | "cov": "istanbul cover _mocha -- --require should --recursive --check-leaks test/ -R spec"
9 | },
10 | "files": [
11 | "lib"
12 | ],
13 | "dependencies": {
14 | "debug": "^4.1.1"
15 | },
16 | "devDependencies": {
17 | "istanbul": "^1.1.0-alpha",
18 | "mocha": "^8.0.1",
19 | "should": "^13.1.2",
20 | "sinon": "^9.0.2"
21 | },
22 | "repository": {
23 | "type": "git",
24 | "url": "git+https://github.com/alibaba/plover.git"
25 | },
26 | "publishConfig": {
27 | "registry": "https://registry.npmjs.org"
28 | },
29 | "author": "bencode@163.com",
30 | "license": "Apache-2.0",
31 | "bugs": {
32 | "url": "https://github.com/alibaba/plover/issues"
33 | },
34 | "homepage": "https://github.com/alibaba/plover#readme"
35 | }
36 |
--------------------------------------------------------------------------------
/packages/plover-logger/README.md:
--------------------------------------------------------------------------------
1 | # plover-logger
2 |
3 |
4 | [![NPM version][npm-image]][npm-url]
5 |
6 |
7 | 【核心模块】提供统一的日志接口,让使用者不用关心日志实现,方便日志实现切换。
8 |
9 | ## Usage
10 | ```js
11 | const log = require('plover-logger')('namespace');
12 |
13 | log.info('init application: %s', name);
14 | log.error(e);
15 | log.debug('data return: %o', data);
16 | log.warn('data already exist: %o', o);
17 | ```
18 |
19 | ## Level
20 | 分为`error`, `warn`, `info`, `debug`四个日志级别。
21 |
22 | | name | priority |
23 | |:-----|:--------:|
24 | | error | 1 |
25 | | warn | 2 |
26 | | info | 3 |
27 | | debug | 4 |
28 |
29 | 日志级别从高到低分别为:
30 |
31 | `error` > `warn` > `info` > `debug`
32 |
33 | 日志级别默认为`warn`,此时仅显示`error`、`warn`级别的日志信息。可以通过设置`DEBUG`环境变量来显示`namespace`下`info`和`debug`级别的日志信息。例如:
34 |
35 | ```bash
36 | $ DEBUG=namespace node example/app
37 | ```
38 |
39 | **Note**
40 |
41 | 更多`DEBUG`的设置方式,请参考[debug模块](https://github.com/visionmedia/debug).
42 |
43 |
44 | [npm-image]: https://img.shields.io/npm/v/plover-logger.svg?style=flat-square
45 | [npm-url]: https://www.npmjs.com/package/plover-logger
46 |
--------------------------------------------------------------------------------
/packages/plover-web/lib/web/charset.js:
--------------------------------------------------------------------------------
1 | const debug = require('debug')('plover-web:charset');
2 |
3 |
4 | const map = {
5 | gbk: 1
6 | };
7 |
8 | const rCharset = /;\s+charset=([-\w]+)/i;
9 |
10 |
11 | /*
12 | * 根据需要转换输出编码
13 | *
14 | * @param {Object} opts 参数
15 | * - outputCharset 编码
16 | */
17 | module.exports = function() {
18 | return async function OutputCharset(ctx, next) {
19 | await next();
20 |
21 | const charset = ctx.query._output_charset; // eslint-disable-line
22 | if (!charset || !map[charset]) {
23 | return;
24 | }
25 |
26 | const body = ctx.body;
27 | if (typeof body === 'string') {
28 | debug('encode to %s', charset);
29 | const iconv = require('iconv-lite');
30 | ctx.body = iconv.encode(body, charset);
31 |
32 | const ctype = ctx.response.headers['content-type'];
33 | if (ctype) {
34 | const newCtype = ctype.replace(rCharset, '') + '; charset=' + charset;
35 | debug('set content-type: %s', newCtype);
36 | ctx.set('content-type', newCtype);
37 | }
38 | }
39 | };
40 | };
41 |
42 |
--------------------------------------------------------------------------------
/packages/plover-web/test/web/charset.js:
--------------------------------------------------------------------------------
1 | const Koa = require('koa');
2 | const request = require('supertest');
3 | const co = require('co');
4 | const charset = require('../../lib/web/charset');
5 |
6 |
7 | /* eslint max-nested-callbacks: [2, 4] */
8 |
9 |
10 | describe('plover-web/web/charset', () => {
11 | it('output gbk with query _output_charset', () => {
12 | const app = new Koa();
13 | app.use(charset());
14 | app.use(ctx => {
15 | if (ctx.path === '/a') {
16 | ctx.body = '中国';
17 | } else if (ctx.path === '/b') {
18 | ctx.body = { data: '中国' };
19 | }
20 | });
21 |
22 | const agent = request.agent(app.callback());
23 | return co(function* () {
24 | yield agent.get('/a').expect('中国');
25 |
26 | yield agent.get('/a?_output_charset=gbk')
27 | .expect('content-type', 'text/plain; charset=gbk')
28 | .expect(o => {
29 | o.buffered.should.be.true();
30 | });
31 |
32 | // json data should not encode
33 | yield agent.get('/b?_output_charset=gbk')
34 | .expect({ data: '中国' });
35 | });
36 | });
37 | });
38 |
--------------------------------------------------------------------------------
/packages/ploverx/lib/util.js:
--------------------------------------------------------------------------------
1 | /* eslint no-process-env: 0 */
2 |
3 | const pathUtil = require('path');
4 | const fs = require('fs');
5 |
6 |
7 | exports.loadSettings = function(root) {
8 | const configRoot = pathUtil.join(root, 'config');
9 |
10 | const settings = {
11 | applicationRoot: root,
12 | env: process.env.NODE_ENV || 'development',
13 | disableDefaultRouter: true,
14 | defaultLayout: 'layouts:index'
15 | };
16 |
17 | const appConfig = require(pathUtil.join(configRoot, 'app.js'));
18 | Object.assign(settings, appConfig);
19 |
20 | prepareLibModulesDir(settings);
21 |
22 | const routesPath = pathUtil.join(configRoot, 'routes.js');
23 | if (fs.existsSync(routesPath)) {
24 | settings.routes = require(routesPath);
25 | }
26 |
27 | return settings;
28 | };
29 |
30 |
31 | function prepareLibModulesDir(settings) {
32 | const libs = [pathUtil.join(__dirname, '../')];
33 |
34 | let appLibs = settings.libModulesDir || settings.applicationRoot;
35 | if (!Array.isArray(appLibs)) {
36 | appLibs = [appLibs];
37 | }
38 |
39 | settings.libModulesDir = libs.concat(appLibs);
40 | }
41 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/util/concat-url.js:
--------------------------------------------------------------------------------
1 | const concatUrl = require('../../lib/util/concat-url');
2 |
3 |
4 | describe('util/concat-url', function() {
5 | it('should concat url', function() {
6 | concatUrl('http://assets??', ['a.js', 'b.js'])
7 | .should.eql(['http://assets??a.js,b.js']);
8 | });
9 |
10 |
11 | it('should return muti urls when url lenth > 1500', function() {
12 | const list = [];
13 | for (let i = 0; i < 1000; i++) {
14 | list.push('a.js');
15 | }
16 | const prefix = 'http://assets??';
17 | const urls = concatUrl(prefix, list);
18 | (urls.length > 1).should.be.true();
19 | for (const url of urls) {
20 | (url.length < 1600).should.be.true();
21 | }
22 | });
23 |
24 |
25 | it('for coverage', function() {
26 | const url = 'a.js';
27 | const times = Math.floor(1500 / (url.length + 1)) + 1;
28 | const list = [];
29 | for (let i = 0; i < times; i++) {
30 | list.push(url);
31 | }
32 | const prefix = 'http://assets??';
33 | const urls = concatUrl(prefix, list);
34 | urls.length.should.equal(1);
35 | });
36 | });
37 |
38 |
--------------------------------------------------------------------------------
/packages/plover-web/lib/web/query.js:
--------------------------------------------------------------------------------
1 | const querystring = require('querystring');
2 | const qs = require('qs');
3 |
4 | const QUERY = Symbol('query');
5 |
6 |
7 | module.exports = function(app) {
8 | Object.defineProperty(app.context, 'query', {
9 | get: function() {
10 | let parsed = this[QUERY];
11 | if (!parsed) {
12 | parsed = parse(this.querystring);
13 | this[QUERY] = parsed;
14 | }
15 | return parsed;
16 | }
17 | });
18 | };
19 |
20 |
21 | const rSimple = /^[-\w]+$/;
22 |
23 | function parse(str) {
24 | const obj = querystring.parse(str);
25 | const keys = Object.keys(obj);
26 | const isSimple = keys.every(key => rSimple.test(key));
27 | if (isSimple) {
28 | return filter(obj, keys);
29 | }
30 |
31 | const results = qs.parse(str, { allowDots: true });
32 | return filter(results, keys);
33 | }
34 |
35 |
36 | function filter(obj, keys) {
37 | keys.forEach(key => {
38 | const val = obj[key];
39 | if (val && Array.isArray(val) && rSimple.test(key)) {
40 | obj[key] = val[val.length - 1]; // use array's last item
41 | }
42 | });
43 | return obj;
44 | }
45 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/fixtures/expect/list.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | Hello World
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/packages/plover/test/util/config.js:
--------------------------------------------------------------------------------
1 | const Path = require('path');
2 |
3 | const config = require('../../lib/util/config');
4 |
5 |
6 | describe('util/config', function() {
7 | it('可以载入目录中的配置信息', function() {
8 | let path = Path.join(__dirname, '../fixtures/util/config/config');
9 | let o = config.load(path);
10 | o.should.eql({
11 | app: {
12 | port: 4100
13 | },
14 |
15 | edit: {
16 | name: 'plover'
17 | },
18 |
19 | urls: {
20 | google: 'http://www.google.com',
21 | baidu: 'http://www.baidu.com'
22 | }
23 |
24 | });
25 |
26 | path = Path.join(__dirname, 'not-exists');
27 | o = config.load(path);
28 | o.should.eql({});
29 | });
30 |
31 |
32 | it('载入出错时会抛出异常', function() {
33 | const path = Path.join(__dirname, '../fixtures/util/config/invalid-config');
34 | (function() {
35 | config.load(path);
36 | }.should.throw());
37 | });
38 |
39 |
40 | it('忽略非json|js的文件', function() {
41 | const path = Path.join(__dirname, '../fixtures/util/config/ignore-config');
42 | const o = config.load(path);
43 | o.should.eql({ a: { name: 'hello' } });
44 | });
45 | });
46 |
47 |
--------------------------------------------------------------------------------
/packages/plover-router/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "plover-router",
3 | "version": "3.3.1",
4 | "description": "为Plover应用提供Restful形式API。",
5 | "scripts": {
6 | "test": "mocha --require should --reporter spec --recursive --check-leaks test/",
7 | "cov": "istanbul cover _mocha -- --require should --recursive --check-leaks test/ -R spec"
8 | },
9 | "files": [
10 | "lib"
11 | ],
12 | "dependencies": {
13 | "inflection": "^1.12.0"
14 | },
15 | "devDependencies": {
16 | "istanbul": "^1.1.0-alpha",
17 | "mocha": "^8.0.1",
18 | "plover": "^4.0.0-beta",
19 | "plover-test-mate": "^3.1.0",
20 | "plover-web": "^3.1.0",
21 | "should": "^13.2.3",
22 | "sinon": "^9.0.2"
23 | },
24 | "plover": {
25 | "plugin": "lib/plugin.js"
26 | },
27 | "repository": {
28 | "type": "git",
29 | "url": "git+https://github.com/alibaba/plover.git"
30 | },
31 | "publishConfig": {
32 | "registry": "https://registry.npmjs.org"
33 | },
34 | "author": "bencode@163.com",
35 | "license": "Apache-2.0",
36 | "bugs": {
37 | "url": "https://github.com/alibaba/plover/issues"
38 | },
39 | "homepage": "https://github.com/alibaba/plover#readme"
40 | }
41 |
--------------------------------------------------------------------------------
/packages/plover-xview/test/plugin.js:
--------------------------------------------------------------------------------
1 | const pathUtil = require('path');
2 | const mm = require('plover-test-mate');
3 | const plugin = require('../lib/plugin');
4 |
5 |
6 | describe('plugin', () => {
7 | const applicationRoot = pathUtil.join(__dirname, 'fixtures/app');
8 | const expectRoot = pathUtil.join(__dirname, 'fixtures/expect');
9 |
10 | describe('xview', () => {
11 | const app = mm({
12 | applicationRoot,
13 | expectRoot,
14 | xview: {
15 | viewdata: {
16 | site: 'xview'
17 | }
18 | }
19 | });
20 |
21 | app.install('plover-arttemplate');
22 | app.install(plugin);
23 |
24 | app.use((ctx, next) => {
25 | ctx.csrf = 'csrf-token-123';
26 | ctx.ctoken = 'ctoken-456';
27 | return next();
28 | });
29 | app.it('/', 'index.html');
30 | });
31 |
32 |
33 | describe('coverage', () => {
34 | const app = mm({
35 | applicationRoot,
36 | expectRoot
37 | });
38 |
39 | app.install('plover-arttemplate');
40 | app.install(plugin);
41 |
42 | app.use((ctx, next) => {
43 | return next();
44 | });
45 | app.it('/index/coverage', 'coverage.html');
46 | });
47 | });
48 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/index/index.js:
--------------------------------------------------------------------------------
1 | exports.view = function() {
2 | const layout = this.query.layout;
3 | if (layout) {
4 | this.layout = layout === 'false' ? false : layout;
5 | }
6 |
7 | this.render({
8 | name: 'plover',
9 | version: '1.0.0',
10 | desc: 'nodejs webframework'
11 | });
12 | };
13 |
14 |
15 | exports.navigate = function* () {
16 | if (this.query.yield === 'true') {
17 | return yield this.navigate('view');
18 | }
19 | return this.navigate('view');
20 | };
21 |
22 |
23 | exports.banner = function() {
24 | this.render();
25 | };
26 |
27 |
28 | exports.returnFalse = function() {
29 | return false;
30 | };
31 |
32 |
33 | exports.setBody = function() {
34 | this.body = 'hello world';
35 | };
36 |
37 |
38 | exports.layoutNotFound = function() {
39 | this.layout = 'notfound';
40 | this.render();
41 | };
42 |
43 |
44 | exports.offer = function() {
45 | this.layout = false;
46 | this.view = 'elements:offer';
47 | const offer = {
48 | name: 'p1',
49 | price: 456
50 | };
51 | this.render({ offer: offer });
52 | };
53 |
54 |
55 | exports['engine-not-found'] = function() {
56 | this.render();
57 | };
58 |
--------------------------------------------------------------------------------
/packages/plover-web/test/web/query.js:
--------------------------------------------------------------------------------
1 | const Koa = require('koa');
2 | const request = require('supertest');
3 | const parseQuery = require('../../lib/web/query');
4 |
5 |
6 | describe('plover-web/web/query', function() {
7 | const app = new Koa();
8 | parseQuery(app);
9 | app.use(ctx => {
10 | ctx.body = ctx.query;
11 | });
12 | const agent = request.agent(app.callback());
13 |
14 | const tests = {
15 | 'a=1&b=2': { a: '1', b: '2' },
16 | 'a=1&a=2': { a: '2' },
17 | 'a[]=1&a[]=2': { a: ['1', '2'] },
18 | 'a[2]=1&a[1]=2&a[0]=100': { a: ['100', '2', '1'] },
19 | 'a.a=1&a.b=2': { a: { a: '1', b: '2' } }
20 | };
21 |
22 | Object.keys(tests).forEach(key => {
23 | const value = tests[key];
24 | it(key, () => {
25 | return agent.get('/?' + key).expect(value);
26 | });
27 | });
28 |
29 |
30 | it('should cached when call `ctx.query` multiple times', function() {
31 | const myapp = new Koa();
32 | parseQuery(myapp);
33 | myapp.use(ctx => {
34 | const query = ctx.query;
35 | (query === ctx.query).should.be.true();
36 | ctx.body = 'ok';
37 | });
38 | return request(myapp.callback())
39 | .get('/').expect('ok');
40 | });
41 | });
42 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/helper.js:
--------------------------------------------------------------------------------
1 | const pathUtil = require('path');
2 | const mm = require('plover-test-mate');
3 |
4 | const plugin = require('../lib/plugin');
5 |
6 |
7 | describe('helper', function() {
8 | const appRoot = pathUtil.join(__dirname, 'fixtures/app');
9 | const expectRoot = pathUtil.join(__dirname, 'fixtures/expect');
10 |
11 | it('use assets helper', function() {
12 | const app = mm({
13 | applicationRoot: appRoot,
14 | expectRoot: expectRoot,
15 | port: 60001,
16 | env: 'test'
17 | });
18 |
19 | app.install('plover-arttemplate');
20 | app.install(plugin);
21 |
22 | return app.test('/list', 'list.html');
23 | });
24 |
25 | it('assets tags with url concat', function() {
26 | const app = mm({
27 | applicationRoot: appRoot,
28 | expectRoot: expectRoot,
29 | env: 'production',
30 | port: 60002,
31 | assets: {
32 | enableConcat: true,
33 | concatItems: [
34 | { match: /^\/g\/(.*)$/, prefix: '/g/??' }
35 | ]
36 | }
37 | });
38 |
39 | app.install('plover-arttemplate');
40 | app.install(plugin);
41 |
42 | return app.test('/list', 'list-concat.html');
43 | });
44 | });
45 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/expects/assets.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | Assets
16 |
17 | child
18 |
19 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/expects/assets-layoutejs.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | Assets
16 |
17 | child
18 |
19 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/packages/plover/lib/components/startup/middleware.js:
--------------------------------------------------------------------------------
1 | const compose = require('koa-compose');
2 | const util = require('../../util/util');
3 |
4 |
5 | module.exports = function(app) {
6 | const settings = app.settings;
7 | const root = settings.applicationRoot;
8 |
9 | const list = settings.middlewares || [];
10 | for (let item of list) {
11 | // 默认级别为3
12 | if (typeof item === 'string') {
13 | item = { module: item, level: 3 };
14 | }
15 |
16 | // middleware域是为了兼容原来的配置
17 | // 建议使用module或modules属性
18 | let mws = item.module || item.modules || item.middleware;
19 | mws = Array.isArray(mws) ? mws : [mws];
20 | loadMiddlewares(app, root, mws, item);
21 | }
22 | };
23 |
24 |
25 | function loadMiddlewares(app, root, mws, options) {
26 | mws = mws.map(path => {
27 | let mw = util.loadModule(root, path);
28 | mw = util.convertMiddleware(app, mw, options);
29 | mw.$name = path;
30 | return mw;
31 | });
32 |
33 | let middleware = null;
34 | if (mws.length > 1) {
35 | middleware = compose(mws);
36 | middleware.$name = 'compose-' +
37 | mws.map(function(mw) {
38 | return mw.name || mw.$name;
39 | }).join('|');
40 | } else {
41 | middleware = mws[0];
42 | }
43 |
44 | app.use(middleware, options);
45 | }
46 |
--------------------------------------------------------------------------------
/packages/plover-util/test/route-info.js:
--------------------------------------------------------------------------------
1 | const RouteInfo = require('..').RouteInfo;
2 |
3 |
4 | describe('plover-util/lib/route-info', function() {
5 | it('#parse', function() {
6 | const parent = { module: 'index' };
7 |
8 | let route = RouteInfo.parse(parent, 'view');
9 | route.module.should.equal('index');
10 | route.action.should.equal('view');
11 |
12 | route = RouteInfo.parse(parent, 'offer:item');
13 | route.module.should.equal('offer');
14 | route.action.should.equal('item');
15 |
16 | route = RouteInfo.parse(parent, 'tools/photos#show');
17 | route.module.should.equal('tools/photos');
18 | route.action.should.equal('show');
19 |
20 | route = RouteInfo.parse(parent);
21 | route.module.should.equal('index');
22 | route.action.should.equal('view');
23 |
24 | route = RouteInfo.parse(parent, 'lib:css/tabs.css');
25 | route.module.should.equal('lib');
26 | route.action.should.equal('css/tabs.css');
27 | });
28 |
29 |
30 | it('#regular', function() {
31 | let route = { module: 'index', action: 'item' };
32 | RouteInfo.regular(route);
33 | route.url.should.equal('index:item');
34 |
35 | route = { module: 'offer' };
36 | RouteInfo.regular(route);
37 | route.url.should.equal('offer:view');
38 | });
39 | });
40 |
--------------------------------------------------------------------------------
/packages/plover-xview/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "plover-xview",
3 | "version": "3.2.0",
4 | "description": "Plover视图插件,提供页面渲染的工具函数和重用方案。",
5 | "main": "lib/index",
6 | "scripts": {
7 | "test": "mocha --require should --reporter spec --recursive --check-leaks test/",
8 | "cov": "istanbul cover _mocha -- --require should --recursive --check-leaks test/ -R spec"
9 | },
10 | "files": [
11 | "lib"
12 | ],
13 | "dependencies": {
14 | "create-tag": "^0.2.0",
15 | "escape-html": "^1.0.3",
16 | "output-formatter": "^1.0.2",
17 | "plover-util": "^1.8.0"
18 | },
19 | "devDependencies": {
20 | "istanbul": "^1.1.0-alpha",
21 | "mocha": "^8.0.1",
22 | "plover": "^4.0.0-beta",
23 | "plover-arttemplate": "^3.4.0",
24 | "plover-test-mate": "^3.0.0",
25 | "should": "^13.1.2",
26 | "supertest": "^4.0.2"
27 | },
28 | "plover": {
29 | "plugin": "lib/plugin.js"
30 | },
31 | "repository": {
32 | "type": "git",
33 | "url": "git+https://github.com/alibaba/plover.git"
34 | },
35 | "publishConfig": {
36 | "registry": "https://registry.npmjs.org"
37 | },
38 | "author": "bencode@163.com",
39 | "license": "Apache-2.0",
40 | "bugs": {
41 | "url": "https://github.com/alibaba/plover/issues"
42 | },
43 | "homepage": "https://github.com/alibaba/plover#readme"
44 | }
45 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/vendor.js:
--------------------------------------------------------------------------------
1 | const pathUtil = require('path');
2 | const plover = require('plover');
3 | const plugin = require('../lib/plugin');
4 |
5 |
6 | describe('vendor', function() {
7 | const root = pathUtil.join(__dirname, 'fixtures/vendor');
8 |
9 | it('should load assets modules', function() {
10 | const app = plover({
11 | applicationRoot: root,
12 | assets: {
13 | vendors: [
14 | 'jquery',
15 | 'bootstrap',
16 | { name: 'vue', dist: 'output' }
17 | ]
18 | }
19 | });
20 | plugin(app);
21 |
22 | const resolver = app.moduleResolver;
23 | resolver.resolve('jquery')
24 | .should.eql({
25 | name: 'jquery',
26 | version: '2.0.0',
27 | path: pathUtil.join(root, 'node_modules/jquery'),
28 | assets: true,
29 | assetsRoot: '',
30 | reload: false,
31 | build: false
32 | });
33 |
34 | resolver.resolve('bootstrap').path
35 | .should.equal(pathUtil.join(root, 'node_modules/bootstrap/dist'));
36 |
37 | resolver.resolve('vue').path
38 | .should.equal(pathUtil.join(root, 'node_modules/vue/output'));
39 | });
40 |
41 |
42 | it('coverage for no assets config', function() {
43 | const app = plover({ applicationRoot: root });
44 | plugin(app);
45 | });
46 | });
47 |
--------------------------------------------------------------------------------
/packages/plover/lib/util/config.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const pathUtil = require('path');
3 |
4 |
5 | /**
6 | * 加载由目录中的`json`和`js`文件组织成的配置信息
7 | * 此方法采用同步方式加载文件,所以不能在请求会话中使用
8 | *
9 | * @param {String} dir - 目录
10 | * @return {Object} - 配置信息
11 | */
12 | exports.load = function(dir) {
13 | const config = {};
14 | if (!fs.existsSync(dir)) {
15 | return config;
16 | }
17 |
18 | const files = fs.readdirSync(dir);
19 | for (const file of files) {
20 | const path = pathUtil.join(dir, file);
21 | const ext = pathUtil.extname(path);
22 | const name = pathUtil.basename(file, ext);
23 | const stat = fs.statSync(path);
24 |
25 | const item = stat.isDirectory() ?
26 | tryLoadFromDir(path) : tryLoadFromFile(path);
27 |
28 | if (item) {
29 | config[name] = item;
30 | }
31 | }
32 |
33 | return config;
34 | };
35 |
36 |
37 | /*
38 | * 从目录加载配置信息
39 | */
40 | function tryLoadFromDir(dir) {
41 | let path = null;
42 | try {
43 | path = require.resolve(dir);
44 | } catch (e) {
45 | // 忽略非模块的目录
46 | return null;
47 | }
48 | return require(path);
49 | }
50 |
51 |
52 | /*
53 | * 从文件加载配置信息
54 | */
55 | function tryLoadFromFile(path) {
56 | const ext = pathUtil.extname(path);
57 | if (ext === '.js' || ext === '.json') {
58 | return require(path);
59 | }
60 | return null;
61 | }
62 |
63 |
--------------------------------------------------------------------------------
/packages/plover-assets/lib/builder/digest-assets.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs-extra');
2 | const pathUtil = require('path');
3 | const crypto = require('crypto');
4 | const scanDir = require('../util/scan-dir');
5 |
6 |
7 | module.exports = function(settings, options) {
8 | const assets = settings.assets || {};
9 | if (!assets.digest) {
10 | return;
11 | }
12 |
13 | const root = options.outputDir;
14 | const list = scanDir(root);
15 |
16 | const manifest = {};
17 | for (const path of list) {
18 | const hash = digest(path);
19 | const ext = pathUtil.extname(path);
20 | const filename = pathUtil.basename(path, ext) + '-' + hash;
21 | const outpath = pathUtil.join(pathUtil.dirname(path), filename + ext);
22 | fs.copySync(path, outpath);
23 |
24 | const from = pathUtil.relative(root, path).replace(/\\/g, '/');
25 | const to = pathUtil.relative(root, outpath).replace(/\\/g, '/');
26 | manifest[from] = to;
27 | }
28 |
29 | writeManifest(root, manifest);
30 | };
31 |
32 |
33 | function digest(path) {
34 | const shasum = crypto.createHash('sha1');
35 | const buf = fs.readFileSync(path);
36 | shasum.update(buf);
37 | return shasum.digest('hex').substr(0, 10);
38 | }
39 |
40 |
41 | function writeManifest(root, manifest) {
42 | const path = pathUtil.join(root, 'manifest.json');
43 | fs.writeFileSync(path, JSON.stringify(manifest));
44 | }
45 |
--------------------------------------------------------------------------------
/packages/ploverx/test/index.js:
--------------------------------------------------------------------------------
1 | const pathUtil = require('path');
2 | const co = require('co');
3 | const sinon = require('sinon');
4 | const request = require('supertest');
5 | const ploverx = require('../');
6 |
7 |
8 | describe('ploverx', () => {
9 | let app = null;
10 | let agent = null;
11 |
12 | before(() => {
13 | const root = pathUtil.join(__dirname, 'fixtures/app');
14 | app = ploverx({ applicationRoot: root });
15 | agent = request(app.callback());
16 | });
17 |
18 | after(() => {
19 | app.__restoreLogger(); // eslint-disable-line
20 | });
21 |
22 |
23 | it('should run app with default plugins', () => {
24 | return agent.get('/hello')
25 | .expect(/Hello<\/h1>/);
26 | });
27 |
28 |
29 | it('server static file', () => {
30 | return agent.get('/ok.txt').expect('ok!\n');
31 | });
32 |
33 |
34 | it('logger with winston', () => {
35 | return co(function* () {
36 | yield agent.get('/logger')
37 | .expect('logger info');
38 |
39 | yield agent.get('/logger?level=warn')
40 | .expect('logger warn');
41 |
42 |
43 | yield agent.get('/logger?level=error')
44 | .expect('logger error');
45 | });
46 | });
47 |
48 |
49 | it('app.run', () => {
50 | sinon.spy(app, 'listen');
51 | app.run();
52 | app.listen.calledWith(4000).should.be.true();
53 | app.listen.restore();
54 | });
55 | });
56 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "plover-packages",
3 | "version": "4.7.2",
4 | "description": "专注于模块化的NodeJs Web框架",
5 | "scripts": {
6 | "lint": "eslint .",
7 | "test": "mocha --require should --reporter spec --recursive --check-leaks --exit --timeout 5000 $(find packages/*/test -name '*.js' ! -path 'packages/*/test/fixtures/*')",
8 | "cov": "istanbul cover _mocha -- --require should --recursive --check-leaks --exit --timeout 5000 $(find packages/*/test -name '*.js' ! -path 'packages/*/test/fixtures/*')",
9 | "travis": "istanbul cover _mocha --report lcovonly -- --require should --recursive --check-leaks --exit --timeout 5000 $(find packages/*/test -name '*.js' ! -path 'packages/*/test/fixtures/*')",
10 | "ci": "npm run lint && npm run travis",
11 | "changelog": "conventional-changelog -p angular -i CHANGELOG.md -o CHANGELOG.md -s"
12 | },
13 | "repository": {
14 | "type": "git",
15 | "url": "git+https://github.com/alibaba/plover.git"
16 | },
17 | "devDependencies": {
18 | "conventional-changelog-cli": "^2.0.5",
19 | "eslint": "^5.4.0",
20 | "eslint-config-bce": "^5.0.0",
21 | "istanbul": "^1.1.0-alpha",
22 | "mocha": "^5.2.0",
23 | "should": "^13.2.3"
24 | },
25 | "author": "bencode@163.com",
26 | "license": "Apache-2.0",
27 | "bugs": {
28 | "url": "https://github.com/alibaba/plover/issues"
29 | },
30 | "homepage": "https://github.com/alibaba/plover#readme"
31 | }
32 |
--------------------------------------------------------------------------------
/packages/plover/test/core/render-helper.js:
--------------------------------------------------------------------------------
1 | const t = require('../../lib/core/render-helper').__test; // eslint-disable-line
2 | const PLACE_HOLDER = t.PLACE_HOLDER;
3 | const mergeDepends = t.mergeDepends;
4 |
5 |
6 | describe('core/render-helper', function() {
7 | const toResult = content => {
8 | return { content: content };
9 | };
10 |
11 | it('.mergeDepends', function() {
12 | const content = `
13 | ABC${PLACE_HOLDER}0000000000
14 | BCD${PLACE_HOLDER}0000000001
15 | ${PLACE_HOLDER}0000000003${PLACE_HOLDER}0000000002
16 | ${PLACE_HOLDER}00000000042${PLACE_HOLDER}0000000005988
17 | OTHER
18 | `;
19 |
20 | const rd = {};
21 | const depends = [
22 | 'child a',
23 | 'child b',
24 | 'child c',
25 | 'child d',
26 | 'child e',
27 | 'child f'
28 | ].map(toResult);
29 |
30 | const result = mergeDepends(rd, content, depends);
31 | const expect = `
32 | ABCchild a
33 | BCDchild b
34 | child dchild c
35 | child e2child f988
36 | OTHER
37 | `;
38 | result.should.be.equal(expect);
39 | });
40 |
41 |
42 | it('.mergeDepends no leading', function() {
43 | const content = `${PLACE_HOLDER}0000000000hi${PLACE_HOLDER}0000000001`;
44 | const rd = {};
45 | const depends = [
46 | 'child a',
47 | 'child b'
48 | ].map(toResult);
49 |
50 | const result = mergeDepends(rd, content, depends);
51 | const expect = 'child ahichild b';
52 | result.should.be.equal(expect);
53 | });
54 | });
55 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Plover - 专注于模块化的NodeJs Web框架
2 | ====
3 |
4 |
5 | [![NPM version][npm-image]][npm-url]
6 | [![build status][travis-image]][travis-url]
7 | [![Test coverage][coveralls-image]][coveralls-url]
8 |
9 |
10 | Plover是一个专注于 **模块化方式构建web应用** 的NodeJs MVC web框架。不同于其它web框架,它提供完整的应用和模块化模型可以让复杂的业务功能可以方便地抽象成多个模块的方式进行独立开发,让应用可以像搭积目的方式拼装模块而成。
11 |
12 | Plover基于[koa](http://koajs.com)构建,它可以很方便地独立部署或者集成到其他koa的应用一起部署。
13 |
14 | Plover专注于 **模块化** ,为 **快速构建web应用** 提供最佳方案 。
15 |
16 |
17 | ## 参与贡献
18 |
19 | 欢迎以[Issue](https://github.com/alibaba/plover/issues)或[PullRequest](https://github.com/alibaba/plover/pulls)的方式参于共建。
20 |
21 | 提交代码前请确保通过**eslint**和**单元测试**
22 |
23 | 关于commit规范请参考[Angular Commit规范](https://github.com/angular/angular.js/blob/master/CONTRIBUTING.md#commit-message-format)
24 | 关于lint规范请参考[eslint-config-bce](https://github.com/bencode/eslint-config-bce/blob/master/.eslintrc)
25 |
26 |
27 | ## 相关资源
28 |
29 |
30 | - [开发文档](https://github.com/ploverjs/guides)
31 | - [examples](https://github.com/ploverjs/examples)
32 | - [ploverjs](https://github.com/ploverjs)
33 |
34 |
35 |
36 | [npm-image]: https://img.shields.io/npm/v/ploverx.svg?style=flat-square
37 | [npm-url]: https://www.npmjs.com/package/ploverx
38 | [travis-image]: https://img.shields.io/travis/alibaba/plover/master.svg?style=flat-square
39 | [travis-url]: https://travis-ci.org/alibaba/plover
40 | [coveralls-image]: https://img.shields.io/codecov/c/github/alibaba/plover.svg?style=flat-square
41 | [coveralls-url]: https://codecov.io/github/alibaba/plover?branch=master
42 |
--------------------------------------------------------------------------------
/packages/plover-assets/lib/vendor.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const pathUtil = require('path');
3 | const resolveFrom = require('resolve-from');
4 |
5 | const debug = require('debug')('plover-assets:vendor');
6 |
7 | const rNamespace = /^@[-\w]+\//;
8 |
9 |
10 | module.exports = function(app) {
11 | const settings = app.settings;
12 | const config = settings.assets || {};
13 | const vendors = config.vendors;
14 | if (!vendors) {
15 | return;
16 | }
17 |
18 | debug('try load module for assets vendor: %s', vendors);
19 | for (const item of vendors) {
20 | loadVendor(app, settings, item);
21 | }
22 | };
23 |
24 |
25 | function loadVendor(app, settings, item) {
26 | item = typeof item === 'string' ? { name: item } : item;
27 | const path = resolveFrom(
28 | settings.applicationRoot,
29 | item.name + '/package.json'
30 | );
31 | const root = pathUtil.dirname(path);
32 | const pkg = require(path);
33 |
34 | const tryPath = pathUtil.join(root, 'dist');
35 | const dist = item.dist ? pathUtil.join(root, item.dist) :
36 | isDir(tryPath) ? tryPath : root;
37 |
38 | const info = {
39 | name: pkg.name.replace(rNamespace, ''), // remove namespace
40 | version: pkg.version,
41 | path: dist,
42 | assets: true,
43 | assetsRoot: '',
44 | reload: false,
45 | build: false
46 | };
47 |
48 | debug('try load assets vendor: %o', info);
49 | app.moduleResolver.pushModule(info);
50 | }
51 |
52 |
53 | function isDir(path) {
54 | return fs.existsSync(path) && fs.statSync(path).isDirectory();
55 | }
56 |
57 |
--------------------------------------------------------------------------------
/packages/plover-util/test/lang.js:
--------------------------------------------------------------------------------
1 | const lang = require('..').Lang;
2 |
3 |
4 | /* eslint no-empty-function: 0 */
5 |
6 |
7 | describe('plover-util/lib/lang', function() {
8 | it('.isGenerator', function() {
9 | const fn = function* () {
10 | yield 1;
11 | };
12 |
13 | lang.isGenerator(fn()).should.be.true();
14 | lang.isGenerator({}).should.be.false();
15 | });
16 |
17 |
18 | it('.isGeneratorFunction', function() {
19 | const fn = function* () {
20 | yield 2;
21 | };
22 |
23 | lang.isGeneratorFunction(fn).should.be.true();
24 | lang.isGeneratorFunction(function() {}).should.be.false();
25 | });
26 |
27 |
28 | it('.isPromise', function() {
29 | lang.isPromise(new Promise(function() {})).should.be.true();
30 | lang.isPromise({ then: function() {} }).should.be.true();
31 | lang.isPromise(function() {}).should.be.false();
32 | });
33 |
34 |
35 | it('.isAsyncFunction', function() {
36 | lang.isAsyncFunction(async function() {}).should.be.true();
37 | lang.isAsyncFunction(async() => null).should.be.true();
38 | lang.isAsyncFunction(function() {}).should.be.false();
39 | lang.isAsyncFunction(1).should.be.false();
40 | (!!lang.isAsyncFunction(null)).should.be.false();
41 | });
42 |
43 |
44 | it('.isPureFunction', function() {
45 | lang.isPureFunction(function() {}).should.be.true();
46 | lang.isPureFunction(() => {}).should.be.true();
47 | lang.isPureFunction(function* () {}).should.be.false();
48 | lang.isPureFunction(async function() {}).should.be.false();
49 | });
50 | });
51 |
--------------------------------------------------------------------------------
/packages/plover-assets/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "plover-assets",
3 | "version": "5.0.0",
4 | "description": "plover assets helper and middleware",
5 | "main": "lib/index",
6 | "scripts": {
7 | "test": "mocha --require should --reporter spec --recursive --check-leaks test",
8 | "cov": "istanbul cover _mocha -- --require should --recursive --check-leaks test -R spec"
9 | },
10 | "files": [
11 | "lib"
12 | ],
13 | "dependencies": {
14 | "co": "^4.6.0",
15 | "create-tag": "^0.2.0",
16 | "debug": "^4.1.1",
17 | "fs-extra": "^9.0.1",
18 | "koa-sendfile": "^2.0.0",
19 | "lodash": "^4.17.4",
20 | "minimatch": "^3.0.4",
21 | "mz": "^2.6.0",
22 | "plover-logger": "^1.4.4",
23 | "plover-module-resolver": "^1.6.7",
24 | "plover-util": "^1.7.0",
25 | "resolve-from": "^5.0.0"
26 | },
27 | "devDependencies": {
28 | "istanbul": "^1.1.0-alpha",
29 | "mocha": "^8.0.1",
30 | "plover": "^4.0.0-beta",
31 | "plover-arttemplate": "^3.0.0",
32 | "plover-ejs": "^1.1.0",
33 | "plover-test-mate": "^3.0.0",
34 | "should": "^13.1.2",
35 | "supertest": "^4.0.2"
36 | },
37 | "plover": {
38 | "plugin": "lib/plugin.js"
39 | },
40 | "repository": {
41 | "type": "git",
42 | "url": "git+https://github.com/alibaba/plover.git"
43 | },
44 | "publishConfig": {
45 | "registry": "https://registry.npmjs.org"
46 | },
47 | "author": "bencode@163.com",
48 | "license": "Apache-2.0",
49 | "bugs": {
50 | "url": "https://github.com/alibaba/plover/issues"
51 | },
52 | "homepage": "https://github.com/alibaba/plover#readme"
53 | }
54 |
--------------------------------------------------------------------------------
/packages/ploverx/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ploverx",
3 | "version": "5.0.0-beta.1",
4 | "description": "集成常用plover插件,提供全栈nodejs web应用开发环境。",
5 | "main": "lib/index",
6 | "bin": {
7 | "plover-build": "bin/build.js"
8 | },
9 | "scripts": {
10 | "test": "mocha --require should --reporter spec --recursive --bail --check-leaks test/",
11 | "cov": "istanbul cover _mocha -- --require should --recursive --bail --check-leaks test/ -R spec"
12 | },
13 | "files": [
14 | "bin",
15 | "lib"
16 | ],
17 | "dependencies": {
18 | "bluebird": "^3.5.0",
19 | "commander": "^5.1.0",
20 | "fs-extra": "^9.0.1",
21 | "plover": "^4.0.0-beta",
22 | "plover-arttemplate": "^3.1.0",
23 | "plover-assets": "^5.0.0",
24 | "plover-benchmark": "^1.1.0",
25 | "plover-ejs": "^1.1.0",
26 | "plover-logger": "^1.4.4",
27 | "plover-router": "^3.3.1",
28 | "plover-web": "^4.0.0",
29 | "plover-xview": "^3.0.0",
30 | "winston": "^3.0.0",
31 | "winston-daily-rotate-file": "^4.0.5"
32 | },
33 | "devDependencies": {
34 | "co": "^4.6.0",
35 | "istanbul": "^1.1.0-alpha",
36 | "mocha": "^8.0.0",
37 | "should": "^13.2.3",
38 | "sinon": "^9.0.2",
39 | "supertest": "^4.0.2"
40 | },
41 | "repository": {
42 | "type": "git",
43 | "url": "git+https://github.com/alibaba/plover.git"
44 | },
45 | "publishConfig": {
46 | "registry": "https://registry.npmjs.org"
47 | },
48 | "author": "bencode@163.com",
49 | "license": "Apache-2.0",
50 | "bugs": {
51 | "url": "https://github.com/alibaba/plover/issues"
52 | },
53 | "homepage": "https://github.com/alibaba/plover#readme"
54 | }
55 |
--------------------------------------------------------------------------------
/packages/plover-web/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "plover-web",
3 | "version": "4.0.0",
4 | "description": "【插件】集成常用web中间件,提供通用web功能。",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "mocha --require should --reporter spec --recursive --check-leaks test/",
8 | "cov": "istanbul cover _mocha -- --require should --recursive --check-leaks test/"
9 | },
10 | "files": [
11 | "lib"
12 | ],
13 | "dependencies": {
14 | "@koa/cors": "^3.1.0",
15 | "debug": "^4.0.1",
16 | "iconv-lite": "^0.6.1",
17 | "koa-bodyparser": "^4.2.0",
18 | "koa-compress": "^4.0.1",
19 | "koa-conditional-get": "^2.0.0",
20 | "koa-csrf": "^2.5.0",
21 | "koa-etag": "^3.0.0",
22 | "koa-favicon": "^2.0.0",
23 | "koa-response-time": "^2.0.0",
24 | "koa-session": "^6.0.0",
25 | "koa-static": "^5.0.0",
26 | "minimatch": "^3.0.4",
27 | "path-to-regexp": "^6.1.0",
28 | "plover-logger": "^1.4.4",
29 | "qs": "^6.5.0"
30 | },
31 | "devDependencies": {
32 | "co": "^4.6.0",
33 | "istanbul": "^1.1.0-alpha",
34 | "koa": "^2.2.0",
35 | "mocha": "^8.0.1",
36 | "plover": "^4.0.0-beta",
37 | "plover-test-mate": "^3.0.0",
38 | "should": "^13.1.2",
39 | "supertest": "^4.0.2"
40 | },
41 | "plover": {
42 | "plugin": "lib/plugin.js"
43 | },
44 | "repository": {
45 | "type": "git",
46 | "url": "git+https://github.com/alibaba/plover.git"
47 | },
48 | "publishConfig": {
49 | "registry": "https://registry.npmjs.org"
50 | },
51 | "license": "Apache-2.0",
52 | "bugs": {
53 | "url": "https://github.com/alibaba/plover/issues"
54 | },
55 | "homepage": "https://github.com/alibaba/plover#readme"
56 | }
57 |
--------------------------------------------------------------------------------
/packages/plover/test/components/plugin.js:
--------------------------------------------------------------------------------
1 | const pathUtil = require('path');
2 | const co = require('co');
3 | const request = require('supertest');
4 |
5 | const plover = require('../../');
6 |
7 |
8 | describe('components/plugin', function() {
9 | const root = pathUtil.join(__dirname, '../fixtures/components/plugin');
10 | const settings = { applicationRoot: root };
11 |
12 | it('可以加载并初始化插件', function() {
13 | const app = plover(settings);
14 | const agent = request(app.callback());
15 |
16 | return co(function* () {
17 | yield agent.get('/test')
18 | .expect('hello test');
19 |
20 | yield agent.get('/test-withorder')
21 | .expect('hello test-withorder');
22 |
23 | yield agent.get('/last')
24 | .expect('last');
25 | });
26 | });
27 |
28 |
29 | it('可以通过配置禁掉插件', function() {
30 | const app = plover({
31 | applicationRoot: root,
32 | plugins: {
33 | test: false,
34 | 'test-withorder': {
35 | enable: false
36 | }
37 | }
38 | });
39 |
40 | const agent = request(app.callback());
41 |
42 | return co(function* () {
43 | yield agent.get('/test')
44 | .expect('last');
45 | });
46 | });
47 |
48 |
49 | it('还可以通过disable禁掉插件', function() {
50 | const app = plover({
51 | applicationRoot: root,
52 | plugins: {
53 | test: { disable: true }
54 | }
55 | });
56 |
57 | const agent = request(app.callback());
58 | return co(function* () {
59 | yield agent.get('/test')
60 | .expect('last');
61 |
62 | yield agent.get('/test-withorder')
63 | .expect('hello test-withorder');
64 | });
65 | });
66 | });
67 |
68 |
--------------------------------------------------------------------------------
/packages/ploverx/test/fixtures/app/config/app.js:
--------------------------------------------------------------------------------
1 | const pathUtil = require('path');
2 |
3 |
4 | /**
5 | * 服务器相关配置
6 | */
7 | /*
8 | exports.server = {
9 | // http server启动端口号
10 | port: 4000
11 | };
12 | */
13 |
14 |
15 | /**
16 | * 日志配置
17 | */
18 | exports.loggers = {
19 | system: {
20 | match: /^plover/,
21 | level: 'info',
22 | consoleLevel: 'warn',
23 | file: pathUtil.join(__dirname, '../logs/plover.log'),
24 | errorFile: pathUtil.join(__dirname, '../logs/plover-error.log')
25 | },
26 |
27 | app: {
28 | level: 'info',
29 | consoleLevel: 'info',
30 | file: pathUtil.join(__dirname, '../logs/app.log'),
31 | errorFile: pathUtil.join(__dirname, '../logs/app-error.log')
32 | }
33 | };
34 |
35 |
36 | /**
37 | * 服务
38 | */
39 | exports.services = {
40 | };
41 |
42 |
43 | /**
44 | * 模板帮助方法
45 | */
46 | exports.helpers = {
47 | };
48 |
49 |
50 | /**
51 | * web中间件相关配置
52 | */
53 | exports.web = {
54 | // 用于设置app.keys, 实际应用需要重新产生一个
55 | // http://koajs.com/#app.keys
56 | keys: ['17e6b6bc6129097383dcad4fa1602233'],
57 |
58 | // https://github.com/koajs/favicon
59 | favicon: pathUtil.join(__dirname, '../public/favicon.ico'),
60 |
61 | // https://github.com/koajs/response-time
62 | rtime: {},
63 |
64 | static: {}
65 | };
66 |
67 |
68 | /**
69 | * 安全相关配置
70 | */
71 | exports.security = {
72 | };
73 |
74 |
75 | /**
76 | * plover-assets插件相关配置
77 | */
78 | exports.assets = {
79 | // 是否启用plover-assets中间件模块
80 | // 启动时静态资源由plover应用处理
81 | // 用于不将前端资源交给cdn处理的简单应用场景
82 | // 在开发模式下总是开启的,方便开发。
83 | enableMiddleware: true,
84 |
85 | // 是否开启urlConcat功能,生效于非开发环境
86 | enableConcat: true,
87 |
88 | // 当打开urlConcat功能时使用
89 | concatItems: [
90 | { match: /^\/g\/(.*)$/, prefix: '/g/??' }
91 | ]
92 | };
93 |
--------------------------------------------------------------------------------
/packages/plover/test/components/startup/index.js:
--------------------------------------------------------------------------------
1 | const pathUtil = require('path');
2 | const sinon = require('sinon');
3 |
4 |
5 | const StartupComponent = require('../../../lib/components/startup');
6 |
7 |
8 | describe('components/startup', function() {
9 | it('plover会根据配置加载各种部件(middleware, service等)', function() {
10 | const settings = getSettings();
11 |
12 | const app = createMockApp(settings);
13 | new StartupComponent(app); // eslint-disable-line
14 |
15 | app.addRoute.called.should.be.true();
16 | app.use.callCount.should.equal(1);
17 | app.addService.called.should.be.true();
18 | app.addFilter.called.should.be.true();
19 | app.addHelper.called.should.be.true();
20 | });
21 | });
22 |
23 |
24 | function getSettings() {
25 | const root = pathUtil.join(__dirname, '../../fixtures/components/startup');
26 |
27 | const settings = {
28 | applicationRoot: root,
29 |
30 | routes: {
31 | '/offer/:offerId': 'offer/view'
32 | },
33 |
34 | middlewares: [
35 | './lib/middlewares/hello.js'
36 | ],
37 |
38 | services: {
39 | productService: './lib/services/product.js'
40 | },
41 |
42 | filters: [
43 | './lib/filters/box.js',
44 | {
45 | module: './lib/filters/api.js',
46 | match: '/api'
47 | }
48 | ],
49 |
50 | helpers: {
51 | priceHelper: './lib/helpers/price.js'
52 | }
53 | };
54 |
55 | return settings;
56 | }
57 |
58 |
59 | function createMockApp(settings) {
60 | const app = {
61 | settings: settings,
62 | config: {},
63 |
64 | addRoute: sinon.spy(),
65 | use: sinon.spy(),
66 | addService: sinon.spy(),
67 | addFilter: sinon.spy(),
68 | addHelper: sinon.spy()
69 | };
70 |
71 | return app;
72 | }
73 |
74 |
--------------------------------------------------------------------------------
/packages/plover/lib/util/invoker.js:
--------------------------------------------------------------------------------
1 | const lang = require('plover-util/lib/lang');
2 |
3 | const logger = require('plover-logger')('plover:util/invoker');
4 |
5 |
6 | /**
7 | * 用于遍历调用拦截器方法
8 | *
9 | * @param {Array} list - 挡截器列表
10 | * @param {String} method - 方法名
11 | * @param {ActionContext} context - 上下文
12 | * @param {Boolean} reverse - 是否逆序调用
13 | * @return {NavigateResult} - 结果
14 | */
15 | /* eslint complexity: [2, 10] */
16 | exports.filter = function* filter(list, method, context, reverse) {
17 | const ctx = context.ctx || {}; // for ActionContext in unit test without ctx field
18 | for (let i = 0, c = list.length; i < c; i++) {
19 | const index = reverse ? c - i - 1 : i;
20 | const item = list[index];
21 | const fn = item.filter[method];
22 | if (fn && (!item.match || item.match.test(ctx.path))) {
23 | const name = item.name || item.filter.name || '';
24 | logger.debug('%s.%s', name, method);
25 | const result = yield* exports.run(fn, context);
26 | if (exports.isSuccess(result)) {
27 | return result;
28 | }
29 | }
30 | }
31 | return null;
32 | };
33 |
34 |
35 | exports.run = function* (fn, context) {
36 | return lang.isAsyncFunction(fn) ? yield fn.call(context, context) :
37 | lang.isGeneratorFunction(fn) ? yield* fn.call(context, context) :
38 | fn.call(context, context);
39 | };
40 |
41 |
42 | /**
43 | * 判断controller或filter的结果是否ok了
44 | * 如果ok了,就表示得到了想要的结果需要break了
45 | *
46 | * @param {NavigateResult} result - 结果
47 | * @return {Boolean} - 是否结束
48 | */
49 | exports.isSuccess = function(result) {
50 | // 快速测试,大多数是这种情况
51 | if (result === undefined || result === null) {
52 | return false;
53 | }
54 | return result === false || typeof result === 'object';
55 | };
56 |
57 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/modules/child/index.js:
--------------------------------------------------------------------------------
1 | exports.view = function() {
2 | const items = [
3 | {
4 | name: 'Javascript高级程序设计',
5 | price: 84.2
6 | },
7 |
8 | {
9 | name: 'Javascript语言精粹',
10 | price: 24.2
11 | }
12 | ];
13 |
14 | this.render({
15 | items: items,
16 | formatPrice: formatPrice
17 | });
18 | };
19 |
20 |
21 | exports.header = function() {
22 | const title = this.query.title;
23 | this.render({ title: title });
24 | };
25 |
26 |
27 | exports.panel = function() {
28 | const slowText = new Promise(resolve => {
29 | setTimeout(() => {
30 | resolve('slow text')
31 | }, 10);
32 | });
33 | this.render({ slowText: slowText });
34 | };
35 |
36 |
37 | exports.books = function* () {
38 | yield sleep(10);
39 | const data = {
40 | books: ['book a', 'book b', 'book c']
41 | };
42 | this.render(data);
43 | };
44 |
45 |
46 | exports.show = function* () {
47 | yield sleep(10);
48 | this.render();
49 | }
50 |
51 |
52 | exports['view-not-found'] = function() {
53 | this.render();
54 | };
55 |
56 |
57 | exports.renderChildError = function() {
58 | this.render();
59 | };
60 |
61 |
62 | exports.renderError = function() {
63 | throw new Error('some error happen');
64 | };
65 |
66 |
67 | exports.renderAsyncChildError = function() {
68 | this.render();
69 | };
70 |
71 | exports.renderAsyncError = function* () {
72 | yield sleep(10);
73 | throw new Error('some error happen');
74 | };
75 |
76 |
77 | exports['not-found'] = function* () {
78 | yield sleep(10);
79 | return false;
80 | };
81 |
82 |
83 | function formatPrice(v) {
84 | return v + '元';
85 | }
86 |
87 |
88 | function sleep(time) {
89 | return new Promise(resolve => {
90 | setTimeout(resolve, time);
91 | });
92 | }
93 |
--------------------------------------------------------------------------------
/packages/plover-web/test/web/csrf.js:
--------------------------------------------------------------------------------
1 | const Koa = require('koa');
2 | const request = require('supertest');
3 | const co = require('co');
4 |
5 |
6 | /* eslint max-nested-callbacks: [2, 4] */
7 |
8 |
9 | describe('plover-web/web/csrf', () => {
10 | const app = new Koa();
11 | app.keys = ['test'];
12 | app.use(require('koa-session')({}, app));
13 | require('koa-csrf')(app);
14 |
15 | const opts = {
16 | match: '/csrf',
17 |
18 | ignore: [
19 | '/ignore/*',
20 | /^\/skip\//
21 | ]
22 | };
23 |
24 | app.use(require('../../lib/web/csrf').middleware(opts));
25 | app.use(async(ctx, next) => {
26 | if (ctx.path === '/getcsrf') {
27 | ctx.body = ctx.csrf;
28 | } else {
29 | await next();
30 | }
31 | });
32 | app.use(ctx => {
33 | ctx.body = 'ok';
34 | });
35 |
36 | const agent = request.agent(app.callback());
37 |
38 | it('`get` ignore csrf check for default', () => {
39 | return agent.get('/').expect('ok');
40 | });
41 |
42 |
43 | it('`post` should check csrf for default', () => {
44 | return agent.post('/').expect(403);
45 | });
46 |
47 |
48 | it('config match force csrf check', () => {
49 | return agent.get('/csrf').expect(403);
50 | });
51 |
52 |
53 | it('config ignore skip csrf check', () => {
54 | return agent.post('/ignore/123').expect('ok');
55 | });
56 |
57 |
58 | it('config ignore rules with regexp', () => {
59 | return agent.post('/skip/123').expect('ok');
60 | });
61 |
62 |
63 | it('pass csrf check', () => {
64 | return co(function* () {
65 | let csrf = false;
66 | yield agent.get('/getcsrf').expect(o => {
67 | csrf = o.text;
68 | });
69 | csrf.should.not.empty();
70 | yield agent.post('/?_csrf=' + csrf).expect('ok');
71 | });
72 | });
73 | });
74 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/lib/util.js:
--------------------------------------------------------------------------------
1 | const pathUtil = require('path');
2 | const fs = require('fs');
3 |
4 |
5 | /**
6 | * 判断是否plover模块
7 | * 在package.json中有plover配置信息,就认为是plover模块
8 | *
9 | * @param {String} path - 目录
10 | * @return {Boolean} - 是否plover模块
11 | */
12 | exports.isPloverModule = function(path) {
13 | const pkgpath = pathUtil.join(path, 'package.json');
14 | if (fs.existsSync(pkgpath)) {
15 | const pinfo = require(pkgpath);
16 | return !!(pinfo && pinfo.plover);
17 | }
18 | return false;
19 | };
20 |
21 |
22 | /**
23 | * 判断是否目录
24 | *
25 | * @param {String} path - 路径
26 | * @return {Boolean} - 是否目录
27 | */
28 | exports.isDir = function(path) {
29 | return fs.existsSync(path) && fs.statSync(path).isDirectory();
30 | };
31 |
32 |
33 | /**
34 | * 判断是否空目录
35 | *
36 | * @param {String} path - 路径
37 | * @return {Boolean} - 是否目录
38 | */
39 | exports.isEmptyDir = function(path) {
40 | const files = fs.readdirSync(path);
41 | for (const file of files) {
42 | if (/^[-\w]/.test(file)) {
43 | return false;
44 | }
45 | }
46 | return true;
47 | };
48 |
49 |
50 | /**
51 | * 深度优先遍历出文件夹中的文件,忽略隐藏文件
52 | *
53 | * @param {String} path - 目录
54 | * @return {Array} - 文件列表
55 | */
56 | exports.scan = function(path) {
57 | const list = [];
58 | if (!exports.isDir(path)) {
59 | return list;
60 | }
61 |
62 | const files = fs.readdirSync(path);
63 | for (const file of files) {
64 | if (!file.startsWith('.')) {
65 | const thispath = pathUtil.join(path, file);
66 | const stat = fs.statSync(thispath);
67 | if (stat.isFile()) {
68 | list.push(thispath);
69 | } else { // directory
70 | list.push.apply(list, exports.scan(thispath));
71 | }
72 | }
73 | }
74 |
75 | return list;
76 | };
77 |
78 |
--------------------------------------------------------------------------------
/packages/ploverx/bin/build.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | const assert = require('assert');
4 | const pathUtil = require('path');
5 | const co = require('co');
6 | const Builder = require('plover-assets/lib/builder');
7 | const util = require('../lib/util');
8 |
9 |
10 | /* eslint no-console: 0, no-process-exit: 0, no-process-env: 0 */
11 |
12 |
13 | module.exports = build;
14 |
15 |
16 | function build(opts) {
17 | opts = Object.assign({}, opts);
18 |
19 | const appRoot = opts.applicationRoot;
20 | assert(appRoot, '`options.applicationRoot` required');
21 | opts.outputDir = opts.outputDir || pathUtil.join(appRoot, 'public');
22 |
23 | const settings = util.loadSettings(appRoot);
24 | console.log('build assets %s -> %s', appRoot, opts.outputDir);
25 |
26 | const builder = new Builder(settings);
27 | return co(function* () {
28 | yield builder.buildApp(opts);
29 | });
30 | }
31 |
32 |
33 | /* istanbul ignore next */
34 | if (require.main === module) {
35 | // 标识编译过程,可在配置中使用
36 | process.env.PLOVER_ASSETS_BUILD = 'true';
37 |
38 | const program = require('commander');
39 | program
40 | .version(require('../package.json').version)
41 | .option('--applicationRoot [applicationRoot]')
42 | .option('-o, --outputDir [outputDir]', 'output dir')
43 | .parse(process.argv);
44 |
45 | const appRoot = program.applicationRoot ?
46 | pathUtil.resolve(program.applicationRoot) : process.cwd();
47 | const outputDir = program.outputDir && pathUtil.resolve(program.outputDir);
48 |
49 | const opts = {
50 | applicationRoot: appRoot,
51 | outputDir: outputDir
52 | };
53 |
54 | build(opts)
55 | .then(() => console.log('BUILD_SUCCESS'))
56 | .catch(e => {
57 | console.error('BUILD_ERROR');
58 | e = e || {};
59 | console.error(e.stack || e);
60 | process.exit(1);
61 | });
62 | }
63 |
64 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/expects/child-production.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Books
5 |
6 |
7 | Books
8 |
9 |
Big
10 |
13 |
14 |
Show
15 | show is view
16 |
17 |
18 |
19 | Name: Javascript高级程序设计
20 | Price: 84.2元
21 |
22 | Name: Javascript语言精粹
23 | Price: 24.2元
24 |
25 |
26 |
27 |
28 |
29 |
30 | Panel
31 |
32 |
33 | - 0 book a
34 | - 1 book b
35 | - 2 book c
36 |
37 |
38 |
39 |
Big 2
40 |
43 |
44 |
Show
45 | show is view
46 |
47 |
48 |
49 |
50 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/packages/plover/test/core/helper-container.js:
--------------------------------------------------------------------------------
1 | const pathUtil = require('path');
2 | const request = require('supertest');
3 |
4 | const plover = require('../../');
5 |
6 | const equal = require('../util').equalWith;
7 |
8 |
9 | describe('core/helper-container', function() {
10 | const root = pathUtil.join(__dirname, '../fixtures/core/app');
11 | const app = plover({
12 | applicationRoot: root,
13 | helpers: {
14 | urlHelper: './lib/helpers/url-helper',
15 | xview: './lib/helpers/xview',
16 | cms: './lib/helpers/cms',
17 | assets: './lib/helpers/assets'
18 | }
19 | });
20 |
21 |
22 | it('use helper', function() {
23 | return request(app.callback())
24 | .get('/helper')
25 | .expect(equal('helper.html'));
26 | });
27 |
28 |
29 | it('async render', function() {
30 | return request(app.callback())
31 | .get('/helper/async')
32 | .expect(equal('helper-async.html'));
33 | });
34 |
35 |
36 | it('with assets', function() {
37 | return request(app.callback())
38 | .get('/assets')
39 | .expect(equal('assets.html'));
40 | });
41 |
42 |
43 | it('render assets use layoutejs', function() {
44 | return request(app.callback())
45 | .get('/assets?layoutejs=true')
46 | .expect(equal('assets-layoutejs.html'));
47 | });
48 |
49 |
50 | it('navigate with transform assets', function() {
51 | return request(app.callback())
52 | .get('/assets/navigate')
53 | .expect(equal('assets-navigate.html'));
54 | });
55 |
56 |
57 | it('disable assets', function() {
58 | const myapp = plover({
59 | applicationRoot: root,
60 | helpers: {
61 | assets: './lib/helpers/assets'
62 | },
63 | assets: {
64 | disableAutowire: true
65 | }
66 | });
67 | return request(myapp.callback())
68 | .get('/assets')
69 | .expect(equal('assets-disable-autowaire.html'));
70 | });
71 | });
72 |
--------------------------------------------------------------------------------
/packages/plover-web/lib/web/csrf.js:
--------------------------------------------------------------------------------
1 | const debug = require('debug')('plover-web:web/csrf');
2 | const util = require('../util/util');
3 |
4 |
5 | /* eslint no-underscore-dangle: 0 */
6 |
7 |
8 | module.exports = function(app, config) {
9 | config = config || {};
10 | if (config.enable === false) {
11 | return;
12 | }
13 |
14 | debug('install csrf module: %o', config);
15 | require('koa-csrf')(app.server, config);
16 | const mw = middleware(config);
17 | app.use(mw, { level: 0 });
18 | };
19 |
20 |
21 | module.exports.middleware = middleware;
22 | //~
23 |
24 |
25 | function middleware(opts) {
26 | opts = opts || {};
27 | const matchRules = util.regularRules(opts.match);
28 | const ignoreRules = util.regularRules(opts.ignore);
29 |
30 | debug('rules, match: %o, ignore: %o', matchRules, ignoreRules);
31 |
32 | return function PloverCsrf(ctx, next) {
33 | // 如果match匹配了,就检查csrf
34 | if (util.testRules(matchRules, ctx.path)) {
35 | return nextWithAssert(ctx, next);
36 | }
37 |
38 | // 忽略 get, head, options 请求
39 | const method = ctx.method;
40 | if (method === 'GET' ||
41 | method === 'HEAD' ||
42 | method === 'OPTIONS') {
43 | return next();
44 | }
45 |
46 | // multipart自己处理csrf
47 | if (ctx.is('multipart')) {
48 | return next();
49 | }
50 |
51 | // 如果ignore规则匹配,就忽略检查
52 | if (util.testRules(ignoreRules, ctx.path)) {
53 | debug('ignore check csrf ctoken: %s', ctx.path);
54 | return next();
55 | }
56 |
57 | return nextWithAssert(ctx, next);
58 | };
59 | }
60 |
61 |
62 | function nextWithAssert(ctx, next) {
63 | const assertCsrf = ctx.assertCsrf || ctx.assertCSRF;
64 | if (assertCsrf) {
65 | const body = ctx.request.body || {};
66 | debug('check csrf: %s, %s', ctx.path, body._csrf);
67 | assertCsrf.call(ctx, body);
68 |
69 | // 设置标识域,表示已校验过ctoken
70 | ctx[util.CSRF_CHECKED] = true;
71 | }
72 | return next();
73 | }
74 |
--------------------------------------------------------------------------------
/packages/plover/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "plover",
3 | "version": "4.0.0",
4 | "description": "专注于模块化的NodeJs Web框架",
5 | "main": "lib/index.js",
6 | "scripts": {
7 | "test": "mocha --require should --reporter spec --recursive --check-leaks --timeout 5000 $(find test -name '*.js' ! -path 'test/fixtures/*')",
8 | "cov": "istanbul cover _mocha -- --require should --recursive --check-leaks --exit --timeout 5000 $(find test -name '*.js' ! -path 'test/fixtures/*') -R spec"
9 | },
10 | "files": [
11 | "lib"
12 | ],
13 | "dependencies": {
14 | "antsort": "^1.1.2",
15 | "co": "^4.6.0",
16 | "debug": "^4.1.1",
17 | "delegates": "^1.0.0",
18 | "depd": "^2.0.0",
19 | "escape-html": "^1.0.3",
20 | "jsonp-body": "^1.0.0",
21 | "koa": "^2.5.2",
22 | "koa-compose": "^4.1.0",
23 | "koa-convert": "^1.2.0",
24 | "minimatch": "^3.0.4",
25 | "mz": "^2.6.0",
26 | "output-formatter": "^1.0.1",
27 | "path-to-regexp": "^6.1.0",
28 | "plover-logger": "^1.7.0",
29 | "plover-module-resolver": "^2.0.0",
30 | "plover-util": "^1.7.0",
31 | "ready-callback": "^2.1.0",
32 | "resolve-from": "^5.0.0"
33 | },
34 | "devDependencies": {
35 | "istanbul": "^1.1.0-alpha",
36 | "koa-bodyparser": "^4.2.1",
37 | "koa-session": "^6.0.0",
38 | "mocha": "^8.0.1",
39 | "plover-arttemplate": "^3.0.0",
40 | "plover-ejs": "^1.1.0",
41 | "semver": "^7.3.2",
42 | "should": "^13.2.3",
43 | "sinon": "^9.0.2",
44 | "supertest": "^4.0.2"
45 | },
46 | "engines": {
47 | "node": ">=7.6.0"
48 | },
49 | "publishConfig": {
50 | "registry": "https://registry.npmjs.org"
51 | },
52 | "repository": {
53 | "type": "git",
54 | "url": "git+https://github.com/alibaba/plover.git"
55 | },
56 | "author": "bencode@163.com",
57 | "license": "Apache-2.0",
58 | "bugs": {
59 | "url": "https://github.com/alibaba/plover/issues"
60 | },
61 | "homepage": "https://github.com/alibaba/plover#readme"
62 | }
63 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/expects/child.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Books
5 |
6 |
7 | Books
8 |
9 |
Big
10 |
13 |
14 |
Show
15 | show is view
16 |
17 |
18 |
19 | Name: Javascript高级程序设计
20 | Price: 84.2元
21 |
22 | Name: Javascript语言精粹
23 | Price: 24.2元
24 |
25 |
26 |
27 | Not Found: child:not-found:view
28 |
29 |
30 | Not Found: child:not-found
31 |
32 |
33 | Not Found: child:child:not-found
34 |
35 |
36 | Panel
37 |
38 |
39 | - 0 book a
40 | - 1 book b
41 | - 2 book c
42 |
43 |
44 |
45 |
Big 2
46 |
49 |
50 |
Show
51 | show is view
52 |
53 |
54 |
55 |
56 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/packages/plover/lib/components/plugin.js:
--------------------------------------------------------------------------------
1 | const assert = require('assert');
2 | const pathUtil = require('path');
3 | const antsort = require('antsort');
4 |
5 |
6 | const logger = require('plover-logger')('plover:components/plugin');
7 |
8 |
9 | class Plugin {
10 | /**
11 | * 加载插件
12 | *
13 | * 插件是一个plover模块
14 | * plover模块是一个`package.json`中配置plover域的node模块
15 | * 插件要求plover域中配置属性`plugin: path`
16 | *
17 | * @param {PloverApplication} app - Plover应用对象
18 | */
19 | constructor(app) {
20 | let plugins = getEnablePlugins(app);
21 | plugins = antsort(plugins, { defaultLevel: 3 });
22 | logger.info('install plugins:\n%o', plugins.map(info => info.name));
23 |
24 | for (const info of plugins) {
25 | logger.info('startup plugin: %s', info.name);
26 | const path = pathUtil.join(info.path, info.plugin);
27 | const fn = require(path);
28 | assert(
29 | typeof fn === 'function',
30 | 'plugin module should be a function: ' + path
31 | );
32 | fn(app.proto);
33 | }
34 | }
35 | }
36 |
37 |
38 | module.exports = Plugin;
39 |
40 |
41 | function getEnablePlugins(app) {
42 | const config = app.settings.plugins || {};
43 |
44 | const modules = app.moduleResolver.list();
45 | const plugins = modules.filter(info => {
46 | if (!info.plugin) {
47 | return false;
48 | }
49 |
50 | /*
51 | * 可以通过配置关闭插件
52 | *
53 | * ```
54 | * {
55 | * plugins: {
56 | * [name]: false
57 | * [name]: {
58 | * enable: false
59 | * }
60 | * [name]: {
61 | * disable: true
62 | * }
63 | * }
64 | * }
65 | * ```
66 | */
67 | const name = info.name;
68 | const pconfig = config[name];
69 | if (pconfig === false ||
70 | (pconfig && pconfig.enable === false) ||
71 | (pconfig && pconfig.disable)) {
72 | return false;
73 | }
74 |
75 | return true;
76 | });
77 |
78 | return plugins;
79 | }
80 |
81 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/lib/get-module-base-info.js:
--------------------------------------------------------------------------------
1 | const assert = require('assert');
2 | const fs = require('fs');
3 | const pathUtil = require('path');
4 |
5 | const util = require('./util');
6 |
7 |
8 | const rNamespace = /^@[-\w]+\//;
9 |
10 |
11 | /**
12 | * 取得模块基本信息
13 | *
14 | * @param {String} path - 模块路径
15 | * @param {Object} options - 可选参数
16 | * - ensure 需要验证package.json plover属性必须存在
17 | * - namespace 匿名模块名字空间
18 | * @return {Object} - 模块基本信息
19 | * - name
20 | * - version
21 | * - path
22 | */
23 | module.exports = function(path, options) {
24 | if (!util.isDir(path) || util.isEmptyDir(path)) {
25 | return null;
26 | }
27 |
28 | options = options || {};
29 | if (options.ensure && !util.isPloverModule(path)) {
30 | return null;
31 | }
32 |
33 | return getFromPackage(path) || getFromDeduce(path, options);
34 | };
35 |
36 |
37 | function getFromPackage(path) {
38 | const pkgPath = pathUtil.join(path, 'package.json');
39 | // 根据package.json生成基本信息
40 | if (!fs.existsSync(pkgPath)) {
41 | return null;
42 | }
43 |
44 | const o = readJson(pkgPath);
45 | const info = o.plover || {};
46 |
47 | // plover.name配置了就使用它,否则从name属性去掉名字空间得到
48 | info.name = info.name || o.name.replace(rNamespace, '');
49 | info.version = info.version || o.version || '0.0.0';
50 | info.path = path;
51 |
52 | assert(info.name, 'name required');
53 |
54 | return info;
55 | }
56 |
57 |
58 | function getFromDeduce(path, options) {
59 | const info = {
60 | name: pathUtil.basename(path),
61 | version: '0.0.0',
62 | path: path
63 | };
64 |
65 | const namespace = options.namespace;
66 | if (namespace) {
67 | info.name = namespace + '/' + info.name;
68 | info.namespace = namespace;
69 | }
70 |
71 | return info;
72 | }
73 |
74 |
75 | function readJson(path) {
76 | const body = fs.readFileSync(path, 'utf-8');
77 | try {
78 | return JSON.parse(body);
79 | } catch (e) {
80 | throw new Error('invalid json file: ' + path);
81 | }
82 | }
83 |
84 |
--------------------------------------------------------------------------------
/packages/plover-assets/lib/util/util.js:
--------------------------------------------------------------------------------
1 | const pathUtil = require('path');
2 | const fs = require('mz/fs');
3 | const crypto = require('crypto');
4 |
5 |
6 | exports.getTempDir = function(settings) {
7 | return pathUtil.join(settings.applicationRoot, 'tmp');
8 | };
9 |
10 |
11 | exports.getPublicDir = function(settings) {
12 | return (settings.assets || {}).publicRoot ||
13 | pathUtil.join(settings.applicationRoot, 'public');
14 | };
15 |
16 |
17 | exports.getAssetsPrefix = function(settings) {
18 | return (settings.assets || {}).prefix || '/g';
19 | };
20 |
21 |
22 | exports.loadBuildConfig = function* (info) {
23 | const path = pathUtil.join(info.path, 'build.js');
24 | const config = (yield fs.exists(path)) ? require(path) : {};
25 | const obj = info.build ? info.build :
26 | info.build === false ? { enable: false } : null;
27 | return Object.assign({}, config, obj);
28 | };
29 |
30 |
31 | const rQueryStamp = /\?.*$/;
32 | const rSlash = /^\//;
33 |
34 | /**
35 | * 取得缓存地址
36 | *
37 | * @param {String} path - 原始地址
38 | * @param {Object} settings - 配置
39 | * @return {String} - 缓存地址
40 | */
41 | exports.getCachePath = function(path, settings) {
42 | const tmpdir = exports.getTempDir(settings);
43 | const shasum = crypto.createHash('sha1');
44 | shasum.update(path);
45 | const filename = shasum.digest('hex') +
46 | pathUtil.extname(path).replace(rQueryStamp, '');
47 | return pathUtil.join(tmpdir, filename);
48 | };
49 |
50 |
51 | exports.loadManifest = function(settings) {
52 | const publicDir = exports.getPublicDir(settings);
53 | const prefix = exports.getAssetsPrefix(settings);
54 | const path = settings.assets.manifest ||
55 | pathUtil.join(publicDir, prefix, 'manifest.json');
56 | if (!fs.existsSync(path)) {
57 | return null;
58 | }
59 |
60 | const map = new Map();
61 | const obj = JSON.parse(fs.readFileSync(path, 'utf-8'));
62 | for (const key in obj) {
63 | const url = obj[key].replace(rSlash, '');
64 | map.set(key, url);
65 | }
66 | return map;
67 | };
68 |
--------------------------------------------------------------------------------
/packages/plover-xview/lib/helper.js:
--------------------------------------------------------------------------------
1 | const escape = require('escape-html');
2 | const fmt = require('output-formatter');
3 | const SafeString = require('plover-util/lib/safe-string');
4 | const assign = require('plover-util/lib/assign');
5 |
6 |
7 | const RD = Symbol('rd');
8 | const VIEW_DATA = Symbol('viewdata');
9 |
10 |
11 | /*
12 | * 此方法在由框架自动调用,用于注入一些帮助方法
13 | *
14 | * 视图中可以使用以下数据和方法
15 | *
16 | * - csrf
17 | * - ctoken
18 | *
19 | * - $.viewdata(name, value)
20 | * - $.viewdata(map)
21 | */
22 |
23 | class Helper {
24 | static $init(rd) {
25 | rd.data.csrf = rd.ctx.csrf;
26 | rd.data.ctoken = rd.ctx.ctoken;
27 | }
28 |
29 |
30 | constructor(rd) {
31 | this[RD] = rd;
32 | }
33 |
34 |
35 | viewdata(...args) {
36 | const layoutData = this[RD].layout.data;
37 | const cache = layoutData[VIEW_DATA] || (layoutData[VIEW_DATA] = {});
38 | const type = typeof args[0];
39 | if (type === 'string') {
40 | cache[args[0]] = args[1];
41 | } else if (type === 'object' && args[0]) {
42 | assign(cache, args[0]);
43 | } else {
44 | console.warn(fmt.yellow('invalid viewdata: '), args[0]); // eslint-disable-line
45 | }
46 | return '';
47 | }
48 |
49 |
50 | metaTags(opts) {
51 | opts = opts || {};
52 | const rd = this[RD];
53 | const ctx = rd.ctx;
54 | const settings = ctx.settings;
55 | const config = settings.xview || {};
56 |
57 | let tags = ``;
58 | if (ctx.csrf) {
59 | tags += `\n`;
60 | }
61 | if (ctx.ctoken) {
62 | tags += `\n`;
63 | }
64 |
65 | if (opts.viewdata) {
66 | const data = {};
67 | config.viewdata && assign(data, config.viewdata);
68 | assign(data, rd.layout.data[VIEW_DATA]);
69 | tags += `\n`; // eslint-disable-line
70 | }
71 |
72 | return new SafeString(tags);
73 | }
74 | }
75 |
76 |
77 | module.exports = Helper;
78 |
--------------------------------------------------------------------------------
/packages/plover/lib/core/helper-container.js:
--------------------------------------------------------------------------------
1 | const logger = require('plover-logger')('plover:core/helper-container');
2 | const util = require('../util/util');
3 |
4 |
5 | const RD = Symbol('rd');
6 | const VIEW_RENDER = Symbol('viewRender');
7 | const NAVIGATOR = Symbol('navigator');
8 | const APP = Symbol('app');
9 | const CACHE = Symbol('cache');
10 |
11 | const initList = [];
12 |
13 | class HelperContainer {
14 | /**
15 | * Helper容器,用于管理helper对象
16 | * 模板中只有用到某个helper才去构造
17 | *
18 | * @param {RenderData} rd - 渲染环境
19 | * @param {ViewRender} viewRender - viewRender
20 | * @param {Navigator} navigator - navigator
21 | * @param {PloverApplication} app - plover app对象
22 | */
23 | constructor(rd, viewRender, navigator, app) {
24 | this[RD] = rd;
25 | this[VIEW_RENDER] = viewRender;
26 | this[NAVIGATOR] = navigator;
27 | this[APP] = app;
28 | this[CACHE] = null;
29 |
30 | for (const helper of initList) {
31 | helper.$init.call(this, rd, viewRender, navigator, app);
32 | }
33 | }
34 | }
35 |
36 |
37 | function get(name) {
38 | const cache = this[CACHE] || (this[CACHE] = new Map());
39 | let helper = null;
40 | if (cache.has(name)) {
41 | helper = cache.get(name);
42 | } else {
43 | helper = HelperContainer.helpers[name];
44 | if (typeof helper === 'function') {
45 | logger.debug('create helper object: %s', name);
46 | const Helper = helper;
47 | helper = new Helper(
48 | this[RD], this[VIEW_RENDER],
49 | this[NAVIGATOR], this[APP]
50 | );
51 | }
52 | cache.set(name, helper);
53 | }
54 | return helper;
55 | }
56 |
57 |
58 | module.exports = HelperContainer;
59 |
60 |
61 | HelperContainer.refine = function(app) {
62 | HelperContainer.helpers = app.helpers;
63 | const names = Object.keys(app.helpers);
64 |
65 | util.delegateGetters(HelperContainer.prototype, names, get);
66 |
67 | for (const name of names) {
68 | const helper = app.helpers[name];
69 | if (typeof helper.$init === 'function') {
70 | initList.push(helper);
71 | }
72 | }
73 | };
74 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/util/scan-url.js:
--------------------------------------------------------------------------------
1 | const pathUtil = require('path');
2 | const scanDir = require('../../lib/util/scan-dir');
3 |
4 |
5 | describe('util/scan-dir', function() {
6 | const dir = pathUtil.join(__dirname, '../fixtures/scan');
7 |
8 | const toRelative = path => pathUtil.relative(dir, path);
9 |
10 | it('scan dir files', function() {
11 | const list = scanDir(dir).map(toRelative);
12 | list.should.eql([
13 | 'README.md',
14 | 'a.txt',
15 | 'assets/css/test.less',
16 | 'assets/js/view.js',
17 | 'b.txt',
18 | 'dist/a.txt',
19 | 'dist/b.txt',
20 | 'package.json'
21 | ]);
22 | });
23 |
24 |
25 | it('scan dir with ignore rules', function() {
26 | const options = {
27 | ignore: ['package.json', 'README.md', 'README', 'dist/']
28 | };
29 | const list = scanDir(dir, options).map(toRelative);
30 | list.should.eql([
31 | 'a.txt',
32 | 'assets/css/test.less',
33 | 'assets/js/view.js',
34 | 'b.txt'
35 | ]);
36 | });
37 |
38 |
39 | it('scan dir with match rules', function() {
40 | const options = {
41 | match: ['a.txt'],
42 | ignore: ['*.txt', 'README.md', 'package.json', 'dist/']
43 | };
44 |
45 | const list = scanDir(dir, options).map(toRelative);
46 | list.should.eql([
47 | 'a.txt',
48 | 'assets/css/test.less',
49 | 'assets/js/view.js'
50 | ]);
51 | });
52 |
53 |
54 | it('options.relative', function() {
55 | const options = {
56 | ignore: ['scan/package.json', 'scan/dist/'],
57 | relative: pathUtil.dirname(dir)
58 | };
59 |
60 | const list = scanDir(dir, options).map(toRelative);
61 | list.should.eql([
62 | 'README.md',
63 | 'a.txt',
64 | 'assets/css/test.less',
65 | 'assets/js/view.js',
66 | 'b.txt'
67 | ]);
68 | });
69 |
70 |
71 | it('match only', function() {
72 | const options = {
73 | match: ['**/*.js', '*.json']
74 | };
75 |
76 | const list = scanDir(dir, options).map(toRelative);
77 | list.should.eql([
78 | 'assets/js/view.js',
79 | 'package.json'
80 | ]);
81 | });
82 | });
83 |
84 |
--------------------------------------------------------------------------------
/packages/plover/lib/components/startup/index.js:
--------------------------------------------------------------------------------
1 | const logger = require('plover-logger')('plover:components/startup');
2 | const util = require('../../util/util');
3 |
4 |
5 | class Startup {
6 | /**
7 | * 根据配置信息初始化应用
8 | * 主要添加以下plover部件:
9 | * 服务,中间件,渲染引擎,渲染帮助方法和渲染拦截器
10 | *
11 | * @param {PloverApplication} app - plover应用对象
12 | */
13 | constructor(app) {
14 | const settings = app.settings;
15 | const root = settings.applicationRoot;
16 |
17 | // 服务
18 | settings.services && addServices(app, root, settings.services);
19 |
20 | // 中间件
21 | require('./middleware')(app);
22 |
23 | // 路由信息,可以在settings配置也可以独立文件配置
24 | const routes = settings.routes;
25 | routes && addRoutes(app, routes);
26 |
27 | // 渲染帮助方法
28 | settings.helpers && addHelpers(app, root, settings.helpers);
29 |
30 | // 渲染拦截器
31 | settings.filters && addFilters(app, root, settings.filters);
32 | }
33 | }
34 |
35 |
36 | module.exports = Startup;
37 |
38 |
39 | /*
40 | * 添加服务
41 | */
42 | function addServices(app, root, services) {
43 | logger.info('add services: %o', services);
44 | for (const name in services) {
45 | const service = util.loadModule(root, services[name]);
46 | app.addService(name, service);
47 | }
48 | }
49 |
50 |
51 | /*
52 | * 添加路由
53 | */
54 | function addRoutes(app, routes) {
55 | logger.info('add routes: %o', routes);
56 | for (const pattern in routes) {
57 | app.addRoute(pattern, routes[pattern]);
58 | }
59 | }
60 |
61 |
62 | /*
63 | * 添加渲染帮助方法
64 | */
65 | function addHelpers(app, root, helpers) {
66 | logger.info('add helpers: %o', helpers);
67 | for (const name in helpers) {
68 | const helper = util.loadModule(root, helpers[name]);
69 | app.addHelper(name, helper);
70 | }
71 | }
72 |
73 |
74 | function addFilters(app, root, filters) {
75 | logger.info('add filters: %o', filters);
76 | for (let item of filters) {
77 | if (typeof item === 'string') {
78 | item = { module: item };
79 | }
80 | const filter = util.loadModule(root, item.module);
81 | // 设置$name属性是为了方便日志记录
82 | filter.$name = filter.$name || item.module;
83 | app.addFilter(filter, item);
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/packages/plover/test/util/error-handler.js:
--------------------------------------------------------------------------------
1 | const co = require('co');
2 | const Koa = require('koa');
3 | const request = require('supertest');
4 | const sinon = require('sinon');
5 | const Logger = require('plover-logger');
6 |
7 | const errorHandler = require('../../lib/util/error-handler');
8 |
9 |
10 | describe('util/error-handler', () => {
11 | beforeEach(() => {
12 | sinon.stub(Logger.prototype, 'error');
13 | });
14 |
15 |
16 | afterEach(() => {
17 | Logger.prototype.error.restore();
18 | });
19 |
20 |
21 | it('内层中间件出错时,开发环境会在页面打印异常', () => {
22 | const app = new Koa();
23 | const err = new Error('发生了错误怎么办');
24 | app.use(errorHandler({ env: 'development' }));
25 | app.use(ctx => {
26 | if (ctx.query.error === 'string') {
27 | throw err.message;
28 | }
29 | throw err;
30 | });
31 | return co(function* () {
32 | const agent = request.agent(app.callback());
33 | yield agent.get('/')
34 | .expect(500)
35 | .expect(/发生了错误怎么办?/);
36 |
37 | Logger.prototype.error.calledWith(err).should.be.true();
38 | Logger.prototype.error.reset();
39 |
40 | yield agent.get('/?error=string')
41 | .expect(500);
42 |
43 | Logger.prototype.error.calledWith(err.message).should.be.true();
44 | });
45 | });
46 |
47 |
48 | it('不处理500以下的错误', () => {
49 | const app = new Koa();
50 | app.use(errorHandler());
51 | app.use(ctx => {
52 | ctx.throw(400, '不允许进入');
53 | });
54 |
55 | return request(app.callback())
56 | .get('/')
57 | .expect(400);
58 | });
59 |
60 |
61 | it('非开发环境不会打印具体错误', async() => {
62 | const app = new Koa();
63 | app.use(errorHandler());
64 | app.use(() => {
65 | throw new Error('出现了一个粗心大意的错误');
66 | });
67 |
68 | const agent = request(app.callback());
69 |
70 | await agent.get('/')
71 | .expect(500)
72 | .expect('Internel Server Error');
73 |
74 | const res = { success: false, message: 'Internel Server Error' };
75 |
76 | await agent.get('/')
77 | .set('accept', 'application/json')
78 | .expect(res);
79 |
80 | await agent.post('/')
81 | .set('content-type', 'application/json')
82 | .expect(res);
83 | });
84 | });
85 |
--------------------------------------------------------------------------------
/packages/plover/test/fixtures/core/app/lib/helpers/assets.js:
--------------------------------------------------------------------------------
1 | const SafeString = require('plover-util/lib/safe-string')
2 |
3 |
4 | class AssetsHelper {
5 | constructor(rd, viewRender) {
6 | this.rd = rd;
7 | this.viewRender = viewRender;
8 | }
9 |
10 |
11 | css(url) {
12 | push(this, 'css', url);
13 | }
14 |
15 |
16 | js(url) {
17 | push(this, 'js', url);
18 | }
19 |
20 |
21 | cssTag() {
22 | return createTag(this, 'css', url => {
23 | return ``;
24 | });
25 | }
26 |
27 |
28 | jsTag() {
29 | return createTag(this, 'js', url => {
30 | return ``;
31 | });
32 | }
33 |
34 |
35 | transform(assets) {
36 | const fn = function(item) {
37 | return { url: getUrl(item) };
38 | };
39 | for (const group in assets) {
40 | const bag = assets[group];
41 | bag.css = bag.css.map(fn);
42 | bag.js = bag.js.map(fn);
43 | }
44 | }
45 | }
46 |
47 |
48 | module.exports = AssetsHelper;
49 |
50 |
51 | function push(self, type, url) {
52 | const assets = self.rd.assets;
53 | const group = self.rd.route.type === 'layout' ? 'layout' : 'default';
54 | const bag = assets[group] ||
55 | (assets[group] = { css: [], js: [] });
56 | const list = bag[type];
57 | list.push({ url: url });
58 | }
59 |
60 |
61 | function createTag(self, type, fn) {
62 | const assets = self.rd.assets;
63 | const defer = new Promise(resolve => {
64 | resolve(function() {
65 | let layoutList = [];
66 | let defaultList = [];
67 | if (assets.layout) {
68 | layoutList = layoutList.concat(assets.layout[type]);
69 | }
70 | if (assets.default) {
71 | defaultList = defaultList.concat(assets.default[type]);
72 | }
73 |
74 | const list = layoutList.concat(defaultList);
75 | const tags = list.map(item => {
76 | return fn(getUrl(item));
77 | });
78 |
79 | return { content: new SafeString(tags.join('\n')) };
80 | });
81 | });
82 |
83 | return self.viewRender.renderAsync(self.rd, defer, 'assets-' + type);
84 | }
85 |
86 |
87 | function getUrl(item) {
88 | if (item.url) {
89 | return item.url;
90 | }
91 | const route = item.route;
92 | return `/${route.module}/${route.action}`;
93 | }
94 |
--------------------------------------------------------------------------------
/packages/plover/lib/core/action-runner.js:
--------------------------------------------------------------------------------
1 | const assert = require('assert');
2 | const logger = require('plover-logger')('plover:core/action-runner');
3 | const invoker = require('../util/invoker');
4 |
5 |
6 | class ActionRunner {
7 | /**
8 | * 用于运行控制器Action方法
9 | *
10 | * 控制器中暴露的方法被称为`Action`
11 | * 此类被`Navigator`使用,一个`Navigator`对象包含一个`ActionRunner`
12 | *
13 | * @param {PloverApplication} app - Plover应用对象
14 | * @param {Navigator} navigator - Navigator对象
15 | * - filters {Array} - Filter列表
16 | */
17 | constructor(app, navigator) {
18 | this.filters = navigator.filters;
19 | }
20 |
21 |
22 | /**
23 | * 运行`Action`方法
24 | *
25 | * 1. 首先顺序遍历运行filters中的`beforeAction`方法
26 | * 2. 再运行当前控制器`beforeAction`方法
27 | * 3. 运行控制器Action方法
28 | * 4. 运行当前控制器`afterAction`方法
29 | * 5. 逆序遍历运行`afterAction`方法
30 | *
31 | * @param {NavigateData} rd - NavigateData
32 | * @param {ActionContext} ctx - 上下文,即`Action`中可以使用的`this`
33 | * @return {Any} - 运行结果
34 | */
35 | * run(rd, ctx) {
36 | const filters = this.filters;
37 | const route = rd.route;
38 | let ret = null;
39 |
40 | logger.debug('run action: %s[%s]', route.module, route.action);
41 |
42 | if (filters.length) {
43 | ret = yield* invoker.filter(filters, 'beforeAction', ctx);
44 | if (invoker.isSuccess(ret)) {
45 | return ret;
46 | }
47 | }
48 |
49 | ret = yield* runAction(rd, ctx);
50 | if (invoker.isSuccess(ret)) {
51 | return ret;
52 | }
53 |
54 | if (filters.length) {
55 | return yield* invoker.filter(filters, 'afterAction', ctx, true);
56 | }
57 |
58 | return ret;
59 | }
60 | }
61 |
62 |
63 | module.exports = ActionRunner;
64 |
65 |
66 | /* eslint complexity: [2, 9] */
67 | function* runAction(rd, ctx) {
68 | const module = rd.module;
69 | let ret = null;
70 | let fn = module.beforeAction;
71 |
72 | if (fn) {
73 | ret = yield* invoker.run(fn, ctx);
74 | if (invoker.isSuccess(ret)) {
75 | return ret;
76 | }
77 | }
78 |
79 | fn = module[rd.route.action];
80 | assert(fn, 'action method should exists');
81 | ret = yield* invoker.run(fn, ctx);
82 | if (invoker.isSuccess(ret)) {
83 | return ret;
84 | }
85 |
86 | fn = module.afterAction;
87 | if (fn) {
88 | ret = yield* invoker.run(fn, ctx);
89 | if (invoker.isSuccess(ret)) {
90 | return ret;
91 | }
92 | }
93 |
94 | return ret;
95 | }
96 |
--------------------------------------------------------------------------------
/packages/plover/test/components/service.js:
--------------------------------------------------------------------------------
1 | const co = require('co');
2 | const sinon = require('sinon');
3 | const request = require('supertest');
4 | const plover = require('../../');
5 |
6 | /* eslint require-yield: 0 */
7 |
8 | describe('components/service', function() {
9 | const settings = { applicationRoot: 'somepath' };
10 |
11 | it('添加和使用服务', function() {
12 | const app = plover(settings);
13 |
14 | const calc = {
15 | add: function(a, b) {
16 | return a + b;
17 | }
18 | };
19 |
20 | app.addService('calc', calc);
21 |
22 | app.addMiddleware(function* () {
23 | this.body = '' + this.calc.add(1, 2);
24 | });
25 |
26 | (function() {
27 | app.addService('calc', calc);
28 | }).should.throw(/service name conflict: calc/);
29 |
30 | return request(app.callback())
31 | .get('/')
32 | .expect('3');
33 | });
34 |
35 |
36 | it('添加和使用多实例服务', function() {
37 | const Offer = function(ctx) {
38 | this.url = ctx.url;
39 | this.size = Offer.size;
40 | };
41 |
42 | Offer.startup = function() {
43 | Offer.size = 100;
44 | };
45 |
46 | // 没用过的服务不应该初始化
47 | const NotUsed = sinon.spy();
48 |
49 | const app = plover(settings);
50 |
51 | app.addService('offer', Offer);
52 | app.addService('not-used', NotUsed);
53 |
54 | app.addMiddleware(function* () {
55 | this.body = this.offer.url + '/' + this.offer.size;
56 | });
57 |
58 | return co(function* () {
59 | yield request(app.callback())
60 | .get('/hello')
61 | .expect('/hello/100');
62 |
63 | NotUsed.called.should.be.false();
64 | });
65 | });
66 |
67 |
68 | it('需要异步初始化的服务', async function() {
69 | const CacheService = {
70 | async startup() {
71 | await sleep(100);
72 | this.cache = {
73 | name: 'plover'
74 | };
75 | },
76 |
77 | async get(name) {
78 | return this.cache[name];
79 | }
80 | };
81 |
82 | const app = plover(settings);
83 | app.addService('$cache', CacheService);
84 | app.use(async ctx => {
85 | ctx.body = await ctx.$cache.get('name');
86 | });
87 |
88 | await app.start();
89 | await request(app.callback())
90 | .get('/').expect('plover');
91 | });
92 | });
93 |
94 |
95 | function sleep(time) {
96 | return new Promise(resolve => {
97 | setTimeout(resolve, time);
98 | });
99 | }
100 |
--------------------------------------------------------------------------------
/packages/plover/lib/util/util.js:
--------------------------------------------------------------------------------
1 | const pathUtil = require('path');
2 | const resolveFrom = require('resolve-from');
3 | const convert = require('koa-convert');
4 | const lang = require('plover-util/lib/lang');
5 | const { pathToRegexp } = require('path-to-regexp');
6 |
7 | const depd = require('depd')('plover');
8 |
9 |
10 | /* eslint no-prototype-builtins: 0 */
11 |
12 |
13 | /**
14 | * 代理属性访问到指定方法
15 | *
16 | * @param {Object} obj - 会在此对象上创建属性
17 | * @param {Oject} names - 属性名称列表
18 | * @param {String} to - 方法,默认为obj.$get
19 | */
20 | exports.delegateGetters = function(obj, names, to) {
21 | to = to || obj.$get;
22 | names.forEach(name => {
23 | if (!obj.hasOwnProperty(name)) {
24 | Object.defineProperty(obj, name, {
25 | enumerable: true,
26 | get: function() {
27 | return to.call(this, name);
28 | }
29 | });
30 | }
31 | });
32 | };
33 |
34 |
35 | const rRelativePath = /^\./;
36 |
37 |
38 | /**
39 | * 根据路径加载模块
40 | *
41 | * 如果是相对路径,则相对于应用目录加载
42 | * 否则就会加载应用依赖的模块
43 | *
44 | * @param {String} root - 应用根目录
45 | * @param {String} path - 路径
46 | * @return {Object} - 已加载的模块
47 | */
48 | exports.loadModule = function(root, path) {
49 | if (rRelativePath.test(path)) {
50 | path = pathUtil.join(root, path);
51 | } else if (!pathUtil.isAbsolute(path)) {
52 | path = resolveFrom(root, path);
53 | }
54 | return require(path);
55 | };
56 |
57 |
58 | /**
59 | * 转化中间件成标准格式
60 | *
61 | * @param {Application} app - Ploveration Application
62 | * @param {Function} mw - 中间件对象
63 | * @param {Object} options - 配置
64 | * @return {Middleware} - 中间件函数
65 | */
66 | exports.convertMiddleware = function(app, mw, options) {
67 | // 中间件是普通function时,需要初始化
68 | // 接口形式是middleware(config, koaapp, ploverapp)
69 | if (lang.isPureFunction(mw) && options.prepare !== false) {
70 | mw = mw(app.config, app.server, app.proto);
71 | }
72 | if (lang.isGeneratorFunction(mw)) {
73 | depd('support for generators will be removed in v4.');
74 | const name = mw.name;
75 | mw = convert(mw);
76 | mw.$name = name;
77 | }
78 | return mw;
79 | };
80 |
81 |
82 | exports.patternToRe = function(pattern) {
83 | if (pattern && typeof pattern === 'string') {
84 | return pathToRegexp(pattern.replace(/\/\*/g, '/(.*)'));
85 | }
86 | return pattern || null;
87 | };
88 |
89 |
90 | exports.tryStartupComponent = function(app, name, obj) {
91 | if (typeof obj.startup === 'function') {
92 | const defer = obj.startup(app);
93 | if (lang.isPromise(defer)) {
94 | const done = app.readyCallback(name);
95 | defer.then(done).catch(done);
96 | }
97 | }
98 | };
99 |
--------------------------------------------------------------------------------
/packages/plover-assets/lib/simple-helper.js:
--------------------------------------------------------------------------------
1 | const createTag = require('create-tag');
2 | const assetsUtil = require('./util/util');
3 |
4 |
5 | const rAbs = /^(\w+:)?\/\//;
6 | const rSlash = /^\//;
7 | const rSlashEnd = /\/$/;
8 |
9 |
10 | class Helper {
11 | static startup(app) {
12 | const settings = app.settings;
13 | const config = settings.assets || {};
14 |
15 | const prefix = config.prefix || '/g';
16 | const urlPrefix = settings.development ? prefix :
17 | (config.urlPrefix || prefix);
18 |
19 | this.urlPrefix = urlPrefix.replace(rSlashEnd, '');
20 | this.manifest = assetsUtil.loadManifest(settings);
21 | }
22 |
23 |
24 | constructor(rd) {
25 | this.assets = rd.assets;
26 | this.viewType = rd.route.type;
27 | this.urlPrefix = Helper.urlPrefix;
28 | this.manifest = Helper.manifest;
29 | }
30 |
31 |
32 | url(url) {
33 | if (rAbs.test(url)) {
34 | return url;
35 | }
36 | url = url.replace(rSlash, '');
37 | if (this.manifest) {
38 | url = this.manifest.get(url) || url;
39 | }
40 | return rAbs.test(url) ? url : this.urlPrefix + '/' + url;
41 | }
42 |
43 |
44 | css(url, group) {
45 | push(this, 'css', url, group);
46 | }
47 |
48 |
49 | js(url, group) {
50 | push(this, 'js', url, group);
51 | }
52 |
53 |
54 | cssTag(group, opts) {
55 | const urls = getUrls(this, group, 'css');
56 | return urls.map(url => {
57 | const attrs = Object.assign({
58 | rel: 'stylesheet',
59 | href: url
60 | }, opts);
61 | return createTag('link', attrs);
62 | }).join('\n');
63 | }
64 |
65 |
66 | jsTag(group, opts) {
67 | const urls = getUrls(this, group, 'js');
68 | return urls.map(url => {
69 | const attrs = Object.assign({ src: url }, opts);
70 | return createTag('script', attrs, '');
71 | }).join('\n');
72 | }
73 | }
74 |
75 |
76 | module.exports = Helper;
77 |
78 |
79 | function push(self, type, url, group) {
80 | if (!group) {
81 | group = self.viewType === 'layout' ? 'layout' : 'default';
82 | }
83 | url = self.url(url);
84 | const bag = self.assets[group] ||
85 | (self.assets[group] = { css: [], js: [] });
86 | const list = bag[type];
87 | list.push({ url });
88 | }
89 |
90 |
91 | function getUrls(self, groups, type) {
92 | groups = groups || ['layout', 'default'];
93 | if (!Array.isArray(groups)) {
94 | groups = [groups];
95 | }
96 |
97 | return groups.reduce((acc, group) => {
98 | const list = (self.assets[group] || {})[type];
99 | return list ? acc.concat(list) : acc;
100 | }, []).map(item => item.url);
101 | }
102 |
--------------------------------------------------------------------------------
/packages/plover/test/util/invoker.js:
--------------------------------------------------------------------------------
1 | const co = require('co');
2 |
3 | const invoker = require('../../lib/util/invoker');
4 |
5 | /* eslint require-yield: 0 */
6 |
7 | describe('util/invoker', function() {
8 | it('.filter', function() {
9 | const ctx = {};
10 |
11 | const f1 = {
12 | before: function* () {
13 | this.f1 = 'hello';
14 | this.should.equal(ctx);
15 | }
16 | };
17 |
18 | const f2 = {
19 | before: function() {
20 | this.f2 = 'world';
21 | }
22 | };
23 |
24 |
25 | const f3 = {};
26 |
27 | const list = [
28 | { filter: f1 },
29 | { filter: f2 },
30 | { filter: f3 }
31 | ];
32 |
33 | return co(function* () {
34 | yield invoker.filter(list, 'before', ctx);
35 | ctx.f1.should.equal('hello');
36 | ctx.f2.should.equal('world');
37 | });
38 | });
39 |
40 |
41 | it('.filter - break with false', function() {
42 | const ctx = { };
43 |
44 | const f1 = {
45 | before: async function() {
46 | this.f1 = 'hello';
47 | return false;
48 | }
49 | };
50 |
51 | const f2 = {
52 | before: function* () {
53 | this.f2 = 'world';
54 | }
55 | };
56 |
57 | const list = [
58 | { filter: f1 },
59 | { filter: f2 }
60 | ];
61 |
62 | return co(function* () {
63 | yield invoker.filter(list, 'before', ctx);
64 | ctx.f1.should.equal('hello');
65 | (ctx.f2 === undefined).should.be.true();
66 | });
67 | });
68 |
69 |
70 | it('.filter - break with object', function() {
71 | const ctx = { };
72 | const f1 = {
73 | before: function() {
74 | return { context: 'hello world' };
75 | }
76 | };
77 | const f2 = {
78 | before: function() {
79 | }
80 | };
81 | const list = [
82 | { filter: f1 },
83 | { filter: f2 }
84 | ];
85 |
86 | co(function* () {
87 | const o = yield invoker.filter(list, 'before', ctx);
88 | o.should.eql({ context: 'hello world' });
89 | });
90 | });
91 |
92 |
93 | it('.filter - reverse', function() {
94 | const bag = [];
95 |
96 | const f1 = {
97 | before: function() {
98 | bag.push('hello');
99 | }
100 | };
101 |
102 | const f2 = {
103 | before: function() {
104 | bag.push('world');
105 | }
106 | };
107 |
108 | const list = [
109 | { filter: f1 },
110 | { filter: f2 }
111 | ];
112 |
113 | return co(function* () {
114 | yield invoker.filter(list, 'before', {}, true);
115 | bag.should.eql(['world', 'hello']);
116 | });
117 | });
118 | });
119 |
120 |
--------------------------------------------------------------------------------
/packages/plover-assets/test/middleware.js:
--------------------------------------------------------------------------------
1 | const pathUtil = require('path');
2 | const co = require('co');
3 | const fs = require('fs-extra');
4 | const mm = require('plover-test-mate');
5 |
6 | const util = require('../lib/util/util');
7 | const middleware = require('../lib/middleware');
8 | const handler = require('../lib/handler');
9 |
10 |
11 | describe('middleware', function() {
12 | const root = pathUtil.join(__dirname, './fixtures/app');
13 | const app = mm({ applicationRoot: root });
14 |
15 | app.addMiddleware(middleware);
16 | app.use(ctx => {
17 | ctx.body = 'ok';
18 | });
19 |
20 | handler.add('css', '.less', LessHandler);
21 |
22 | before(function() {
23 | fs.emptyDirSync(pathUtil.join(root, 'tmp'));
24 | });
25 |
26 |
27 | it('should yield next if not an assets request', function() {
28 | return app.get('/hello').expect('ok');
29 | });
30 |
31 |
32 | it('module assets', function() {
33 | return app.get('/g/index/css/view.css')
34 | .expect('body { }\n');
35 | });
36 |
37 |
38 | it('module assets with handler', function() {
39 | return app.get('/g/index/css/test.css')
40 | .expect('compiled: body: {}\n');
41 | });
42 |
43 |
44 | it('module assets not exists', function() {
45 | return app.get('/g/index/css/not-exists.css')
46 | .expect(404);
47 | });
48 |
49 |
50 | it('concat assets', function() {
51 | return app.get('/g/??index.html,index/js/a.js,index/js/b.js,index/css/test.css,not-found.js') // eslint-disable-line
52 | .expect('Hello World\n\nvar a = 1;\n\nvar b = 2;\n\ncompiled: body: {}\n\n');
53 | });
54 |
55 |
56 | const myapp = mm({ applicationRoot: root, env: 'production' });
57 | myapp.addMiddleware(middleware);
58 |
59 | it('should with cache when run in no dev mode', function() {
60 | return co(function* () {
61 | const url = '/g/index/css/test.css';
62 | const cachePath = util.getCachePath(url, myapp.settings);
63 | fs.existsSync(cachePath).should.not.true();
64 | const expect = 'compiled: body: {}\n';
65 |
66 | yield myapp.get(url).expect(expect);
67 |
68 | fs.existsSync(cachePath).should.be.true();
69 |
70 | yield myapp.get(url).expect(expect);
71 | });
72 | });
73 |
74 |
75 | it('direct access /public file when run in no dev mode', function() {
76 | return myapp.get('/g/index.html').expect('Hello World\n');
77 | });
78 |
79 |
80 | it('module not found', function() {
81 | return app.get('/g/not-exists/js/view.js')
82 | .expect(404);
83 | });
84 |
85 |
86 | it('invalid module assets url', function() {
87 | return app.get('/g/abc.js')
88 | .expect(404);
89 | });
90 | });
91 |
92 |
93 | function LessHandler(path, source) {
94 | return 'compiled: ' + source;
95 | }
96 |
97 |
--------------------------------------------------------------------------------
/packages/plover-router/test/plugin.js:
--------------------------------------------------------------------------------
1 | const mm = require('plover-test-mate');
2 |
3 |
4 | describe('plover-route/lib/plugin', () => {
5 | it('config routes', async() => {
6 | const app = create({
7 | applicationRoot: __dirname,
8 | routes: r => {
9 | r.get('/profile', 'users#show');
10 | r.use('/hello', ctx => {
11 | ctx.body = 'hello';
12 | });
13 | }
14 | });
15 |
16 | app.use(ctx => {
17 | if (ctx.route) {
18 | ctx.body = ctx.route;
19 | }
20 | });
21 |
22 | await app.get('/profile').expect({
23 | module: 'users',
24 | action: 'show',
25 | query: {}
26 | });
27 |
28 | await app.get('/hello').expect('hello');
29 | });
30 |
31 |
32 | it('routes with namespace', async() => {
33 | const app = create({
34 | applicationRoot: __dirname,
35 | disableDefaultRouter: true,
36 | routes: r => {
37 | r.namespace('/users', () => {
38 | r.use(async(ctx, next) => {
39 | await next();
40 | if (ctx.route) {
41 | ctx.body = ctx.route.url;
42 | } else {
43 | ctx.body = ctx.path;
44 | }
45 | });
46 |
47 | r.get('config', 'users#config');
48 | });
49 | }
50 | });
51 |
52 | await app.get('/users/config').expect('users:config');
53 | await app.get('/users/messages').expect('/users/messages');
54 | await app.get('/users/').expect('/users/');
55 | await app.get('/users').expect(404);
56 | });
57 |
58 |
59 | it('routes not config, for coverage', () => {
60 | create({
61 | applicationRoot: __dirname
62 | });
63 |
64 | (true).should.be.ok();
65 | });
66 |
67 |
68 | describe('http method', () => {
69 | const app = create({
70 | applicationRoot: __dirname,
71 | web: {
72 | csrf: {
73 | ignore: ['/*']
74 | }
75 | }
76 | });
77 |
78 | app.use(ctx => {
79 | ctx.body = ctx.method;
80 | });
81 |
82 | it('normal post', async() => {
83 | await app.agent.post('/save')
84 | .send({})
85 | .expect('POST');
86 | });
87 |
88 |
89 | it('put with _method', async() => {
90 | await app.agent.post('/update')
91 | .send({ _method: 'put' })
92 | .expect('PUT');
93 | });
94 |
95 |
96 | it('patch with header: x-http-method-override', async() => {
97 | await app.agent.post('/update')
98 | .set('X-HTTP-Method-Override', 'patch')
99 | .expect('PATCH');
100 | });
101 | });
102 | });
103 |
104 |
105 | function create(opts) {
106 | const app = mm(opts);
107 | app.install('plover-web');
108 | app.install(require('../lib/plugin'));
109 | return app;
110 | }
111 |
--------------------------------------------------------------------------------
/packages/plover-web/test/plugin.js:
--------------------------------------------------------------------------------
1 | const pathUtil = require('path');
2 | const request = require('supertest');
3 | const plover = require('plover');
4 |
5 | const plugin = require('../lib/plugin');
6 |
7 |
8 | describe('plover-web/plugin', function() {
9 | const root = pathUtil.join(__dirname, './fixtures/app');
10 | const settings = require(pathUtil.join(root, 'config/app.js'));
11 | const app = plover(settings);
12 |
13 | plugin(app);
14 | hello(app);
15 | login(app);
16 |
17 | const agent = request.agent(app.callback());
18 |
19 | it('etag and rtime', () => {
20 | return agent.get('/hello')
21 | .expect(function(res) {
22 | res.header.etag.should.not.empty();
23 | res.header['x-response-time'].should.not.empty();
24 | })
25 | .expect('hello');
26 | });
27 |
28 |
29 | it('favicon', () => {
30 | return agent.get('/favicon.ico').expect(200);
31 | });
32 |
33 |
34 | it('static', () => {
35 | return agent.get('/ok.txt').expect('ok!\n');
36 | });
37 |
38 |
39 | it('compress', () => {
40 | return agent.get('/big.txt')
41 | .expect('Content-Encoding', 'gzip');
42 | });
43 |
44 |
45 | it('not setting.web', () => {
46 | const myapp = plover({ applicationRoot: __dirname });
47 | plugin(myapp);
48 | hello(myapp);
49 | return request(myapp.callback())
50 | .get('/hello').expect('hello');
51 | });
52 |
53 |
54 | it('security headers', () => {
55 | return agent.get('/hello')
56 | .expect('X-XSS-Protection', '1; mode=block')
57 | .expect('X-Content-Type-Options', 'nosniff')
58 | .expect('X-Download-Options', 'noopen');
59 | });
60 |
61 |
62 | it('session', async() => {
63 | await agent.get('/login').expect('ok');
64 | await agent.get('/user').expect({ name: 'tester' });
65 | });
66 |
67 |
68 | it('cors', async() => {
69 | await agent.get('/hello')
70 | .set('Host', 'www.google.com')
71 | .set('Origin', 'http://www.google.com:8080')
72 | .expect('hello')
73 | .expect('access-control-allow-origin', 'http://www.google.com:8080');
74 |
75 | const res = await agent.get('/hello')
76 | .set('Origin', 'http://www.google.com:8080');
77 |
78 | res.text.should.equal('hello');
79 | (res.headers['access-control-allow-origin'] === undefined).should.ok();
80 | });
81 | });
82 |
83 |
84 | function hello(app) {
85 | app.use(async(ctx, next) => {
86 | if (ctx.path === '/hello') {
87 | ctx.body = 'hello';
88 | } else {
89 | await next();
90 | }
91 | });
92 | }
93 |
94 |
95 | function login(app) {
96 | app.use(async(ctx, next) => {
97 | if (ctx.path === '/login') {
98 | ctx.session.user = { name: 'tester' };
99 | ctx.body = 'ok';
100 | } else if (ctx.path === '/user') {
101 | ctx.body = ctx.session.user;
102 | } else {
103 | await next();
104 | }
105 | });
106 | }
107 |
--------------------------------------------------------------------------------
/packages/plover-assets/lib/util/scan-dir.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const pathUtil = require('path');
3 | const minimatch = require('minimatch');
4 |
5 |
6 | const debug = require('debug')('plover-assets-util:scan-dir');
7 |
8 |
9 | /**
10 | * scan dir files with ignore or match rules
11 | *
12 | * @param {String} dir - dir for scan
13 | * @param {Object} options - options
14 | * {
15 | * match: {Array} - match rules
16 | * ignore: {Array} - ignore rules
17 | * relative: {String} - relative path, use dir for default
18 | * }
19 | * @return {Array} - paths
20 | */
21 | module.exports = function(dir, options) {
22 | options = options || {};
23 |
24 | const results = [];
25 |
26 | const opts = {
27 | dirMatch: [],
28 | fileMatch: [],
29 | dirIgnore: [],
30 | fileIgnore: [],
31 | relative: options.relative || dir
32 | };
33 |
34 | makeRules(options.match, opts.dirMatch, opts.fileMatch);
35 | makeRules(options.ignore, opts.dirIgnore, opts.fileIgnore);
36 |
37 | const matchOnly = options.match && !options.ignore;
38 | opts.fileMatchOnly = matchOnly && opts.fileMatch.length;
39 | opts.dirMatchOnly = matchOnly && !opts.fileMatch.length;
40 |
41 | scan(dir, dir, opts, results);
42 |
43 | return results;
44 | };
45 |
46 |
47 | function scan(root, dir, options, results) {
48 | const files = fs.readdirSync(dir);
49 | files.forEach(file => {
50 | const path = pathUtil.join(dir, file);
51 | const stat = fs.statSync(path);
52 | const rpath = pathUtil.relative(options.relative, path);
53 |
54 | /* istanbul ignore else */
55 | if (stat.isFile()) {
56 | if (test(
57 | file, rpath,
58 | options.fileMatch, options.fileIgnore, options.fileMatchOnly
59 | )) {
60 | results.push(path);
61 | }
62 | } else if (stat.isDirectory()) {
63 | if (test(
64 | file, rpath,
65 | options.dirMatch, options.dirIgnore, options.dirMatchOnly
66 | )) {
67 | scan(root, path, options, results);
68 | }
69 | }
70 | });
71 | }
72 |
73 |
74 | function makeRules(patterns, dirRules, fileRules) {
75 | const rDir = /\/$/;
76 | const toRe = pattern => minimatch.makeRe(pattern);
77 | patterns && patterns.forEach(pattern => {
78 | if (rDir.test(pattern)) {
79 | pattern = pattern.replace(rDir, '');
80 | dirRules.push(toRe(pattern));
81 | } else {
82 | fileRules.push(toRe(pattern));
83 | }
84 | });
85 | }
86 |
87 |
88 | /* eslint max-params: [2, 5] */
89 | function test(file, rpath, match, ignore, matchOnly) {
90 | debug(
91 | 'test: %s, match: %o, ignore: %o, matchOnly: %s',
92 | rpath, match, ignore, matchOnly
93 | );
94 |
95 | const fn = re => re.test(rpath);
96 | if (match.length && match.some(fn)) {
97 | return true;
98 | }
99 | if (matchOnly) {
100 | return false;
101 | }
102 | if (ignore.length && ignore.some(fn)) {
103 | return false;
104 | }
105 | return !file.startsWith('.');
106 | }
107 |
108 |
--------------------------------------------------------------------------------
/packages/plover-module-resolver/test/get-module-info.js:
--------------------------------------------------------------------------------
1 | const pathUtil = require('path');
2 |
3 | const getModuleInfo = require('../lib/get-module-info');
4 |
5 |
6 | /* global __dirname */
7 |
8 |
9 | describe('plover-module-resolver/lib/getModuleInfo(path)', function() {
10 | it('取得模块信息', function() {
11 | const path = pathUtil.join(__dirname, 'fixtures/hello');
12 | const info = getModuleInfo(path);
13 | info.should.eql({
14 | name: 'hello',
15 | version: '0.0.0',
16 | assets: true,
17 | assetsRoot: 'assets/',
18 | path: path,
19 | views: {
20 | view: {
21 | template: 'views/view.art',
22 | js: 'js/view.js',
23 | css: 'css/view.css'
24 | },
25 |
26 | item: {
27 | template: 'views/item.ejs',
28 | js: undefined,
29 | css: 'css/item.css'
30 | },
31 |
32 | edit: {
33 | template: 'views/edit.form',
34 | js: 'js/edit.js',
35 | css: undefined
36 | },
37 |
38 | 'controls/shop': {
39 | template: 'views/controls/shop.art',
40 | js: undefined,
41 | css: 'css/controls/shop.css'
42 | }
43 | }
44 | });
45 | });
46 |
47 |
48 | it('空目录不算模块或目录不存都不算模块', function() {
49 | const path = pathUtil.join(__dirname, 'fixtures/empty');
50 | let info = getModuleInfo(path);
51 | (!!info).should.not.be.ok();
52 |
53 | info = getModuleInfo('notexist');
54 | (!!info).should.not.be.ok();
55 | });
56 |
57 |
58 | it('package.json存在plover节点不存在也不能算模块', function() {
59 | const path = pathUtil.join(__dirname, 'fixtures/withpkg');
60 | const info = getModuleInfo(path, { ensure: true });
61 | (!!info).should.not.be.ok();
62 | });
63 |
64 |
65 | it('没有package.json的模块', function() {
66 | const path = pathUtil.join(__dirname, 'fixtures/simple');
67 | const info = getModuleInfo(path);
68 | info.should.eql({
69 | name: 'simple',
70 | version: '0.0.0',
71 | path: path,
72 | views: {}
73 | });
74 | });
75 |
76 |
77 | it('纯assets模块', function() {
78 | const path = pathUtil.join(__dirname, 'fixtures/pureassets');
79 | const info = getModuleInfo(path);
80 | info.should.eql({
81 | name: 'pureassets',
82 | version: '0.0.0',
83 | path: path,
84 | views: {},
85 | assets: true,
86 | assetsRoot: ''
87 | });
88 | });
89 |
90 |
91 | it('不包含assets的模块', function() {
92 | const path = pathUtil.join(__dirname, 'fixtures/noassets');
93 | const info = getModuleInfo(path);
94 | info.should.eql({
95 | name: 'noassets',
96 | version: '0.0.0',
97 | path: path,
98 | views: {}
99 | });
100 | });
101 |
102 |
103 | it('无效的package.json会输出文件路径信息方便排查', function() {
104 | const path = pathUtil.join(__dirname, 'fixtures/invalid-json');
105 | const pkgPath = pathUtil.join(path, 'package.json');
106 | (function() {
107 | getModuleInfo(path);
108 | }).should.throw(`invalid json file: ${pkgPath}`);
109 | });
110 | });
111 |
112 |
--------------------------------------------------------------------------------