├── 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 | 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 | {{slowText}} 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 |
    2 |
    p1
    3 |
    456
    4 |
    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 | 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 |
    2 |
    3 | {{app.control('input', { value: 'Hello' })}} 4 |
    5 |
    6 | {{app.control('select', { name: 'Web' })}} 7 |
    8 |
    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 | 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 | 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 | 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 |
    8 | offer 9 |
    123元
    10 |
    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 |
    6 |
    7 | 8 |
    9 |
    10 | 11 |
    12 |
    13 | 25 | -------------------------------------------------------------------------------- /packages/plover/test/fixtures/core/app/modules/child/views/view.art: -------------------------------------------------------------------------------- 1 | {{app.view('header', { title: 'Books' })}} 2 | 3 | 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 |
    13 |
    14 | 15 |
    16 |
    17 | 18 |
    19 |
    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 |
    20 |
    21 | 22 |
    23 |
    24 | 25 |
    26 |
    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 |
    20 |
    21 | 22 |
    23 |
    24 | 25 |
    26 |
    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 |
      11 |
    • 126 Hi
    • 12 |
    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 |
    copyright
    30 |

    Panel

    31 |
    32 |
      33 |
    • 0 book a
    • 34 |
    • 1 book b
    • 35 |
    • 2 book c
    • 36 |
    37 |
    38 |
    39 |

    Big 2

    40 |
      41 |
    • 126 Hi
    • 42 |
    43 |
    44 |

    Show

    45 | show is view 46 |
    47 |
    48 | slow text 49 | 50 |
    51 |
    52 | 53 |
    54 |
    55 | 60 |
    61 |
    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 |
      11 |
    • 126 Hi
    • 12 |
    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 |
    copyright
    36 |

    Panel

    37 |
    38 |
      39 |
    • 0 book a
    • 40 |
    • 1 book b
    • 41 |
    • 2 book c
    • 42 |
    43 |
    44 |
    45 |

    Big 2

    46 |
      47 |
    • 126 Hi
    • 48 |
    49 |
    50 |

    Show

    51 | show is view 52 |
    53 |
    54 | slow text 55 | 56 |
    57 |
    58 | 59 |
    60 |
    61 | 66 |
    67 |
    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 | --------------------------------------------------------------------------------