正在显示
173 个修改的文件
包含
3109 行增加
和
0 行删除
.autod.conf.js
0 → 100644
| 1 | +'use strict'; | ||
| 2 | + | ||
| 3 | +module.exports = { | ||
| 4 | + write: true, | ||
| 5 | + prefix: '^', | ||
| 6 | + plugin: 'autod-egg', | ||
| 7 | + test: [ | ||
| 8 | + 'test', | ||
| 9 | + 'benchmark', | ||
| 10 | + ], | ||
| 11 | + devdep: [ | ||
| 12 | + 'egg', | ||
| 13 | + 'egg-ci', | ||
| 14 | + 'egg-bin', | ||
| 15 | + 'autod', | ||
| 16 | + 'autod-egg', | ||
| 17 | + 'eslint', | ||
| 18 | + 'eslint-config-egg', | ||
| 19 | + 'webstorm-disable-index', | ||
| 20 | + ], | ||
| 21 | + exclude: [ | ||
| 22 | + './test/fixtures', | ||
| 23 | + './docs', | ||
| 24 | + './coverage', | ||
| 25 | + ], | ||
| 26 | +}; |
.eslintignore
0 → 100644
.gitignore
0 → 100644
History.md
0 → 100644
| 1 | + | ||
| 2 | +1.8.0 / 2021-10-16 | ||
| 3 | +================== | ||
| 4 | + | ||
| 5 | +**features** | ||
| 6 | + * [[`cdab574`](http://github.com/eggjs/egg-view-assets/commit/cdab574d0d5a95533a6b7a8af34f850b0168b532)] - feat: support config.nonce (#42) (Yiyu He <<dead_horse@qq.com>>) | ||
| 7 | + | ||
| 8 | +1.7.0 / 2021-04-08 | ||
| 9 | +================== | ||
| 10 | + | ||
| 11 | +**features** | ||
| 12 | + * [[`6c5b719`](http://github.com/eggjs/egg-view-assets/commit/6c5b71913994a0f18bf4585df6245cbba0390e3f)] - feat: start detecting port from devServer.port (#40) (Chen Yangjian <<252317+cyjake@users.noreply.github.com>>) | ||
| 13 | + | ||
| 14 | +1.6.2 / 2021-03-22 | ||
| 15 | +================== | ||
| 16 | + | ||
| 17 | +**fixes** | ||
| 18 | + * [[`ee0012e`](http://github.com/eggjs/egg-view-assets/commit/ee0012eca841b1ce2b3264e8154a9a4e3d5f8c15)] - fix: 修复autoPort时,port数据格式和fs模块要求数据格式不匹配问题 (#39) (拾邑 <<mr_suanmei@163.com>>) | ||
| 19 | + | ||
| 20 | +1.6.1 / 2020-06-09 | ||
| 21 | +================== | ||
| 22 | + | ||
| 23 | +**features** | ||
| 24 | + * [[`c7ec6e6`](http://github.com/eggjs/egg-view-assets/commit/c7ec6e671220a71db0ba59ef485f064ca622ccd7)] - feat: make sure `__webpack_public_path__` inserted just once (#31) (viko16 <<viko16@users.noreply.github.com>>) | ||
| 25 | + | ||
| 26 | +**fixes** | ||
| 27 | + * [[`c992877`](http://github.com/eggjs/egg-view-assets/commit/c992877307e4c4178e3e337a5ca43a982e3254e4)] - fix: prefer http://127.0.0.1 even if https is on (#37) (Chen Yangjian <<252317+cyjake@users.noreply.github.com>>) | ||
| 28 | + | ||
| 29 | +1.6.0 / 2019-09-19 | ||
| 30 | +================== | ||
| 31 | + | ||
| 32 | +**features** | ||
| 33 | + * [[`287aedb`](http://github.com/eggjs/egg-view-assets/commit/287aedb4e1ee58861e76198a364e4ff9d7122d92)] - feat: set protocol according to app.options.https (Chen Yangjian <<252317+cyjake@users.noreply.github.com>>) | ||
| 34 | + | ||
| 35 | +1.5.0 / 2019-04-09 | ||
| 36 | +================== | ||
| 37 | + | ||
| 38 | +**features** | ||
| 39 | + * [[`226de2e`](http://github.com/eggjs/egg-view-assets/commit/226de2e5ed9d89bac203ead5a47f2a91fd9158ad)] - feat: script support crossorigin attribute (#30) (仙森 <<dapixp@gmail.com>>) | ||
| 40 | + | ||
| 41 | +1.4.4 / 2019-01-29 | ||
| 42 | +================== | ||
| 43 | + | ||
| 44 | +**fixes** | ||
| 45 | + * [[`8f0092e`](http://github.com/eggjs/egg-view-assets/commit/8f0092ef4e4dd33b63dbf836bdad72bc7a0b88be)] - fix: use w3c valid link format (#29) (fengmk2 <<fengmk2@gmail.com>>) | ||
| 46 | + | ||
| 47 | +1.4.3 / 2019-01-08 | ||
| 48 | +================== | ||
| 49 | + | ||
| 50 | +**fixes** | ||
| 51 | + * [[`f5966ec`](http://github.com/eggjs/egg-view-assets/commit/f5966eca57fcfe5819617e264555ba00c9c6c95b)] - fix(devServer): reset port when app get port from agent (#28) (Haoliang Gao <<sakura9515@gmail.com>>) | ||
| 52 | + * [[`a5866bc`](http://github.com/eggjs/egg-view-assets/commit/a5866bc0b47a0df4d024f44b9fc05d39494fcf47)] - fix: remove unnecessary console (#27) (Haoliang Gao <<sakura9515@gmail.com>>) | ||
| 53 | + | ||
| 54 | +1.4.2 / 2019-01-07 | ||
| 55 | +================== | ||
| 56 | + | ||
| 57 | +**features** | ||
| 58 | + * [[`f6e9fb7`](http://github.com/eggjs/egg-view-assets/commit/f6e9fb7b51435516ce9d7f20a8de2e7a62b87c61)] - feat(devServer): support autoport in env (#26) (Haoliang Gao <<sakura9515@gmail.com>>) | ||
| 59 | + | ||
| 60 | +1.4.1 / 2019-01-07 | ||
| 61 | +================== | ||
| 62 | + | ||
| 63 | +**fixes** | ||
| 64 | + * [[`9378984`](http://github.com/eggjs/egg-view-assets/commit/9378984ad71465eb9cc46beba4936e8bb95f97f6)] - fix: port should be paased from agent to app (#25) (Haoliang Gao <<sakura9515@gmail.com>>) | ||
| 65 | + | ||
| 66 | +1.4.0 / 2019-01-06 | ||
| 67 | +================== | ||
| 68 | + | ||
| 69 | +**features** | ||
| 70 | + * [[`59d20a0`](http://github.com/eggjs/egg-view-assets/commit/59d20a0e7f1451aee14cb1a5117e8cda2d999498)] - feat: auto check port with autoPort (#24) (Haoliang Gao <<sakura9515@gmail.com>>) | ||
| 71 | + | ||
| 72 | +**others** | ||
| 73 | + * [[`bf05a99`](http://github.com/eggjs/egg-view-assets/commit/bf05a99da9661aa731c6938ec7e8443dd381d2eb)] - refactor: use inherit instead of pipe (#22) (Haoliang Gao <<sakura9515@gmail.com>>) | ||
| 74 | + * [[`d233b74`](http://github.com/eggjs/egg-view-assets/commit/d233b74cc38648a310146802385031d001d971e9)] - test: set proc null when exit by self (#23) (Haoliang Gao <<sakura9515@gmail.com>>) | ||
| 75 | + | ||
| 76 | +1.3.0 / 2018-08-20 | ||
| 77 | +================== | ||
| 78 | + | ||
| 79 | +**others** | ||
| 80 | + * [[`0815337`](http://github.com/eggjs/egg-view-assets/commit/0815337586c10ed393786bb7c3ad8f08242a9135)] - refactor: no need to encode context with base64 (#21) (fengmk2 <<fengmk2@gmail.com>>) | ||
| 81 | + | ||
| 82 | +1.2.0 / 2018-08-15 | ||
| 83 | +================== | ||
| 84 | + | ||
| 85 | +**features** | ||
| 86 | + * [[`6d9be84`](http://github.com/eggjs/egg-view-assets/commit/6d9be84495ae1e789e20276f33f184e736cf7e86)] - feat: manifest add absolute path and complete path support (#20) (仙森 <<dapixp@gmail.com>>) | ||
| 87 | + | ||
| 88 | +1.1.2 / 2018-04-16 | ||
| 89 | +================== | ||
| 90 | + | ||
| 91 | +**others** | ||
| 92 | + * [[`9b32a6e`](http://github.com/eggjs/egg-view-assets/commit/9b32a6ed4e2219e323946fb987e900a1477d66d9)] - refactor: atob should be minified (#16) (Haoliang Gao <<sakura9515@gmail.com>>) | ||
| 93 | + | ||
| 94 | +1.1.1 / 2018-04-15 | ||
| 95 | +================== | ||
| 96 | + | ||
| 97 | +**fixes** | ||
| 98 | + * [[`6d8793b`](http://github.com/eggjs/egg-view-assets/commit/6d8793b43e98618b2be76101011ad8732906f114)] - fix: atob browser compatible (#15) (Haoliang Gao <<sakura9515@gmail.com>>) | ||
| 99 | + * [[`2475b7c`](http://github.com/eggjs/egg-view-assets/commit/2475b7c54fe088e2469181eb63f1a48c7c94fdbf)] - fix: command can be run on Windows (#13) (Haoliang Gao <<sakura9515@gmail.com>>) | ||
| 100 | + * [[`ab29dee`](http://github.com/eggjs/egg-view-assets/commit/ab29deed14e0b6d266fc5a90b8f46950ce096431)] - fix: set context data more safely (#14) (Yiyu He <<dead_horse@qq.com>>) | ||
| 101 | + | ||
| 102 | +1.1.0 / 2018-04-04 | ||
| 103 | +================== | ||
| 104 | + | ||
| 105 | +**features** | ||
| 106 | + * [[`772164a`](http://github.com/eggjs/egg-view-assets/commit/772164a3669610659cd43614caf7825a74f30c65)] - feat: unittest environment is same as local (#12) (Haoliang Gao <<sakura9515@gmail.com>>) | ||
| 107 | + | ||
| 108 | +1.0.4 / 2018-04-01 | ||
| 109 | +================== | ||
| 110 | + | ||
| 111 | +**fixes** | ||
| 112 | + * [[`9d7fdac`](http://github.com/eggjs/egg-view-assets/commit/9d7fdac51c6799db63d948b37e94ad47a280a7c4)] - fix: should transform jsx to js (#11) (Haoliang Gao <<sakura9515@gmail.com>>) | ||
| 113 | + | ||
| 114 | +1.0.3 / 2018-04-01 | ||
| 115 | +================== | ||
| 116 | + | ||
| 117 | +**fixes** | ||
| 118 | + * [[`f58d766`](http://github.com/eggjs/egg-view-assets/commit/f58d766907f6e18627a44d42b3513e41b71bcf09)] - fix: check port when devServer is enabled (#10) (Haoliang Gao <<sakura9515@gmail.com>>) | ||
| 119 | + | ||
| 120 | +1.0.2 / 2018-03-29 | ||
| 121 | +================== | ||
| 122 | + | ||
| 123 | +**fixes** | ||
| 124 | + * [[`3f02556`](http://github.com/eggjs/egg-view-assets/commit/3f0255680aa8f6460faf08957efdff184c781b3f)] - fix: publicPath should contains leading and trailing slash (#9) (Haoliang Gao <<sakura9515@gmail.com>>) | ||
| 125 | + * [[`5db9e90`](http://github.com/eggjs/egg-view-assets/commit/5db9e906e85a414ae276e7de1033c1539aff8ea5)] - fix: more safe context when locals is from query (#8) (Haoliang Gao <<sakura9515@gmail.com>>) | ||
| 126 | + | ||
| 127 | +1.0.1 / 2018-03-15 | ||
| 128 | +================== | ||
| 129 | + | ||
| 130 | +**fixes** | ||
| 131 | + * [[`2ebef9d`](http://github.com/eggjs/egg-view-assets/commit/2ebef9d86235cc8c13dc262f1b767bdad77cea02)] - fix: publicPath should be __webpack_public_path__ (#7) (Haoliang Gao <<sakura9515@gmail.com>>) | ||
| 132 | + | ||
| 133 | +1.0.0 / 2018-03-15 | ||
| 134 | +================== | ||
| 135 | + | ||
| 136 | +**others** | ||
| 137 | + * [[`e369e2c`](http://github.com/eggjs/egg-view-assets/commit/e369e2c924bb09e949f27e5095c29d7bcddda68a)] - docs: update readme (#6) (Haoliang Gao <<sakura9515@gmail.com>>),fatal: No names found, cannot describe anything. | ||
| 138 | + |
LICENSE
0 → 100644
| 1 | +MIT License | ||
| 2 | + | ||
| 3 | +Copyright (c) 2017-present Alibaba Group Holding Limited and other contributors. | ||
| 4 | + | ||
| 5 | +Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| 6 | +of this software and associated documentation files (the "Software"), to deal | ||
| 7 | +in the Software without restriction, including without limitation the rights | ||
| 8 | +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| 9 | +copies of the Software, and to permit persons to whom the Software is | ||
| 10 | +furnished to do so, subject to the following conditions: | ||
| 11 | + | ||
| 12 | +The above copyright notice and this permission notice shall be included in all | ||
| 13 | +copies or substantial portions of the Software. | ||
| 14 | + | ||
| 15 | +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| 16 | +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| 17 | +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| 18 | +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| 19 | +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| 20 | +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| 21 | +SOFTWARE. |
README.md
0 → 100644
| 1 | +# egg-view-assets | ||
| 2 | + | ||
| 3 | +[![NPM version][npm-image]][npm-url] | ||
| 4 | +[![build status][travis-image]][travis-url] | ||
| 5 | +[![Test coverage][codecov-image]][codecov-url] | ||
| 6 | +[![David deps][david-image]][david-url] | ||
| 7 | +[![Known Vulnerabilities][snyk-image]][snyk-url] | ||
| 8 | +[![npm download][download-image]][download-url] | ||
| 9 | + | ||
| 10 | +[npm-image]: https://img.shields.io/npm/v/egg-view-assets.svg?style=flat-square | ||
| 11 | +[npm-url]: https://npmjs.org/package/egg-view-assets | ||
| 12 | +[travis-image]: https://img.shields.io/travis/eggjs/egg-view-assets.svg?style=flat-square | ||
| 13 | +[travis-url]: https://travis-ci.org/eggjs/egg-view-assets | ||
| 14 | +[codecov-image]: https://img.shields.io/codecov/c/github/eggjs/egg-view-assets.svg?style=flat-square | ||
| 15 | +[codecov-url]: https://codecov.io/github/eggjs/egg-view-assets?branch=master | ||
| 16 | +[david-image]: https://img.shields.io/david/eggjs/egg-view-assets.svg?style=flat-square | ||
| 17 | +[david-url]: https://david-dm.org/eggjs/egg-view-assets | ||
| 18 | +[snyk-image]: https://snyk.io/test/npm/egg-view-assets/badge.svg?style=flat-square | ||
| 19 | +[snyk-url]: https://snyk.io/test/npm/egg-view-assets | ||
| 20 | +[download-image]: https://img.shields.io/npm/dm/egg-view-assets.svg?style=flat-square | ||
| 21 | +[download-url]: https://npmjs.org/package/egg-view-assets | ||
| 22 | + | ||
| 23 | +Manage frontend assets in development and production. | ||
| 24 | + | ||
| 25 | +## Install | ||
| 26 | + | ||
| 27 | +```bash | ||
| 28 | +$ npm i egg-view-assets --save | ||
| 29 | +``` | ||
| 30 | + | ||
| 31 | +## Usage | ||
| 32 | + | ||
| 33 | +Add `egg-view-assets` as plugin | ||
| 34 | + | ||
| 35 | +```js | ||
| 36 | +// {app_root}/config/plugin.js | ||
| 37 | +exports.assets = { | ||
| 38 | + enable: true, | ||
| 39 | + package: 'egg-view-assets', | ||
| 40 | +}; | ||
| 41 | +``` | ||
| 42 | + | ||
| 43 | +Configuration, you can see full example in [egg-ant-design-pro]. | ||
| 44 | + | ||
| 45 | +```js | ||
| 46 | +// {app_root}/config/config.default.js | ||
| 47 | +exports.view = { | ||
| 48 | + mapping: { | ||
| 49 | + '.js': 'assets', | ||
| 50 | + }, | ||
| 51 | +}; | ||
| 52 | + | ||
| 53 | +exports.assets = { | ||
| 54 | + devServer: { | ||
| 55 | + command: 'roadhog dev', | ||
| 56 | + port: 8000, | ||
| 57 | + }, | ||
| 58 | +}; | ||
| 59 | +``` | ||
| 60 | + | ||
| 61 | +See [config/config.default.js](config/config.default.js) for more detail. | ||
| 62 | + | ||
| 63 | +## Questions & Suggestions | ||
| 64 | + | ||
| 65 | +Please open an issue [here](https://github.com/eggjs/egg/issues). | ||
| 66 | + | ||
| 67 | +## License | ||
| 68 | + | ||
| 69 | +[MIT](LICENSE) | ||
| 70 | + | ||
| 71 | +[egg-ant-design-pro]: https://github.com/eggjs/egg-ant-design-pro |
agent.js
0 → 100644
| 1 | +'use strict'; | ||
| 2 | + | ||
| 3 | +const assert = require('assert'); | ||
| 4 | +const DevServer = require('./lib/dev_server'); | ||
| 5 | + | ||
| 6 | +module.exports = agent => startDevServer(agent); | ||
| 7 | + | ||
| 8 | +function startDevServer(agent) { | ||
| 9 | + const assetsConfig = agent.config.assets; | ||
| 10 | + | ||
| 11 | + if (!assetsConfig.isLocalOrUnittest) return; | ||
| 12 | + if (!assetsConfig.devServer.enable) return; | ||
| 13 | + | ||
| 14 | + assert(assetsConfig.devServer.autoPort || assetsConfig.devServer.port, 'port or autoPort is required when devServer is enabled'); | ||
| 15 | + | ||
| 16 | + const server = new DevServer(agent); | ||
| 17 | + server.ready(err => { | ||
| 18 | + if (err) agent.coreLogger.error('[egg-view-assets]', err.message); | ||
| 19 | + }); | ||
| 20 | + | ||
| 21 | + if (assetsConfig.devServer.waitStart) { | ||
| 22 | + agent.beforeStart(async () => { | ||
| 23 | + await server.ready(); | ||
| 24 | + }); | ||
| 25 | + } | ||
| 26 | + | ||
| 27 | + agent.beforeClose(async () => { | ||
| 28 | + await server.close(); | ||
| 29 | + }); | ||
| 30 | +} |
app.js
0 → 100644
| 1 | +'use strict'; | ||
| 2 | + | ||
| 3 | +const fs = require('fs'); | ||
| 4 | +const path = require('path'); | ||
| 5 | +const assert = require('assert'); | ||
| 6 | +const AssetsView = require('./lib/assets_view'); | ||
| 7 | + | ||
| 8 | +module.exports = app => { | ||
| 9 | + const assetsConfig = app.config.assets; | ||
| 10 | + | ||
| 11 | + if (assetsConfig.devServer.enable && assetsConfig.isLocalOrUnittest) { | ||
| 12 | + let port = assetsConfig.devServer.port; | ||
| 13 | + if (assetsConfig.devServer.autoPort === true) { | ||
| 14 | + try { | ||
| 15 | + port = fs.readFileSync(assetsConfig.devServer.portPath, 'utf8'); | ||
| 16 | + assetsConfig.devServer.port = Number(port); | ||
| 17 | + } catch (err) { | ||
| 18 | + // istanbul ignore next | ||
| 19 | + throw new Error('check autoPort fail'); | ||
| 20 | + } | ||
| 21 | + } | ||
| 22 | + const protocol = app.options.https && assetsConfig.dynamicLocalIP ? 'https' : 'http'; | ||
| 23 | + assetsConfig.url = `${protocol}://127.0.0.1:${port}`; | ||
| 24 | + } | ||
| 25 | + | ||
| 26 | + // it should check manifest.json on deployment | ||
| 27 | + if (!assetsConfig.isLocalOrUnittest) { | ||
| 28 | + const manifestPath = path.join(app.config.baseDir, 'config/manifest.json'); | ||
| 29 | + assert(fs.existsSync(manifestPath), `${manifestPath} is required`); | ||
| 30 | + assetsConfig.manifest = require(manifestPath); | ||
| 31 | + } | ||
| 32 | + | ||
| 33 | + app.view.use('assets', AssetsView); | ||
| 34 | +}; |
app/extend/helper.js
0 → 100644
| 1 | +'use strict'; | ||
| 2 | + | ||
| 3 | +const AssetsContext = require('../../lib/assets_context'); | ||
| 4 | + | ||
| 5 | +const HELPER_ASSETS = require('../../lib/util/constant').HELPER_ASSETS; | ||
| 6 | +const ASSETS = Symbol('Helper#assets'); | ||
| 7 | + | ||
| 8 | +module.exports = { | ||
| 9 | + get assets() { | ||
| 10 | + if (this[ASSETS]) return this[ASSETS]; | ||
| 11 | + if (this.ctx[HELPER_ASSETS]) { | ||
| 12 | + this[ASSETS] = this.ctx[HELPER_ASSETS]; | ||
| 13 | + } else { | ||
| 14 | + this[ASSETS] = new AssetsContext(this.ctx); | ||
| 15 | + } | ||
| 16 | + return this[ASSETS]; | ||
| 17 | + }, | ||
| 18 | +}; |
appveyor.yml
0 → 100644
| 1 | +environment: | ||
| 2 | + matrix: | ||
| 3 | + - nodejs_version: '8' | ||
| 4 | + - nodejs_version: '10' | ||
| 5 | + | ||
| 6 | +install: | ||
| 7 | + - ps: Install-Product node $env:nodejs_version | ||
| 8 | + - npm i npminstall && node_modules\.bin\npminstall | ||
| 9 | + | ||
| 10 | +test_script: | ||
| 11 | + - node --version | ||
| 12 | + - npm --version | ||
| 13 | + - npm run test | ||
| 14 | + | ||
| 15 | +build: off |
config/config.default.js
0 → 100644
| 1 | +'use strict'; | ||
| 2 | + | ||
| 3 | +const path = require('path'); | ||
| 4 | + | ||
| 5 | +module.exports = appInfo => ({ | ||
| 6 | + /** | ||
| 7 | + * assets options | ||
| 8 | + * @member Config#assets | ||
| 9 | + * @property {String} url - the host of the assets, it will be `http://127.0.0.1:${devServer.port}` in development. | ||
| 10 | + * @property {String} publicPath - the base path of the assets | ||
| 11 | + * @property {String} templatePath - the file path of template rendering html | ||
| 12 | + * @property {String} templateViewEngine - the view engine for rendering template | ||
| 13 | + * @property {Boolean} crossorigin - if script is cross origin set this config to true | ||
| 14 | + * @property {Boolean} contextKey - the property name of context, default is `context` | ||
| 15 | + * @property {Boolean} devServer - configuration of local assets server | ||
| 16 | + * @property {Boolean} devServer.command - a command for starting a server, such as `webpack` | ||
| 17 | + * @property {Boolean} devServer.port - listening port for the server, it will be checked when starting | ||
| 18 | + * @property {Boolean} devServer.timeout - the timeout for checking listening port | ||
| 19 | + * @property {Boolean} devServer.env - custom environment | ||
| 20 | + * @property {Boolean} devServer.debug - show stdout/stderr for devServer | ||
| 21 | + * @property {Boolean} devServer.waitStart - whether wait devServer starting | ||
| 22 | + * @property {Function} nonce(ctx) - dynamic generate nonce for csp(https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) | ||
| 23 | + */ | ||
| 24 | + assets: { | ||
| 25 | + isLocalOrUnittest: appInfo.env === 'local' || appInfo.env === 'unittest', | ||
| 26 | + url: '', | ||
| 27 | + publicPath: '', | ||
| 28 | + templatePath: '', | ||
| 29 | + templateViewEngine: '', | ||
| 30 | + crossorigin: false, | ||
| 31 | + contextKey: 'context', | ||
| 32 | + dynamicLocalIP: true, | ||
| 33 | + devServer: { | ||
| 34 | + enable: true, | ||
| 35 | + command: '', | ||
| 36 | + autoPort: false, | ||
| 37 | + port: null, | ||
| 38 | + portPath: path.join(appInfo.baseDir, 'run/assetsPort'), | ||
| 39 | + env: {}, | ||
| 40 | + debug: false, | ||
| 41 | + timeout: 60 * 1000, | ||
| 42 | + waitStart: false, | ||
| 43 | + }, | ||
| 44 | + }, | ||
| 45 | +}); |
config/config.unittest.js
0 → 100644
lib/assets_context.js
0 → 100644
| 1 | +'use strict'; | ||
| 2 | + | ||
| 3 | +const assert = require('assert'); | ||
| 4 | + | ||
| 5 | +// URL consists of host, resourceBase and entry, | ||
| 6 | +// E.X. http://127.0.0.1:7001/public/index.js | ||
| 7 | +// host is http://127.0.0.1:7001 | ||
| 8 | +// resourceBase is /public/ | ||
| 9 | +// entry is index.js | ||
| 10 | +class Assets { | ||
| 11 | + | ||
| 12 | + constructor(ctx) { | ||
| 13 | + this.ctx = ctx; | ||
| 14 | + this.config = ctx.app.config.assets; | ||
| 15 | + this.isLocalOrUnittest = this.config.isLocalOrUnittest; | ||
| 16 | + this.manifest = this.config.manifest; | ||
| 17 | + this.crossorigin = this.config.crossorigin; | ||
| 18 | + // publicPath should contain trailing / and leading / | ||
| 19 | + this.publicPath = this.isLocalOrUnittest ? '/' : normalizePublicPath(this.config.publicPath); | ||
| 20 | + // make sure __webpack_public_path__ inserted just once | ||
| 21 | + this.hasWebpackGlobalVariableInserted = false; | ||
| 22 | + this.host = this.config.url; | ||
| 23 | + this.resourceBase = `${this.host}${this.publicPath}`; | ||
| 24 | + this.nonce = this.config.nonce && this.config.nonce(this.ctx); | ||
| 25 | + } | ||
| 26 | + | ||
| 27 | + getStyle(entry) { | ||
| 28 | + entry = entry || this.entryCss; | ||
| 29 | + return linkTpl({ url: this.getURL(entry) }); | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + getScript(entry) { | ||
| 33 | + entry = entry || this.entry; | ||
| 34 | + | ||
| 35 | + let script = ''; | ||
| 36 | + if (this.publicPath && !this.hasWebpackGlobalVariableInserted) { | ||
| 37 | + script = inlineScriptTpl(`window.__webpack_public_path__ = '${this.publicPath}';`, this.nonce); | ||
| 38 | + this.hasWebpackGlobalVariableInserted = true; | ||
| 39 | + } | ||
| 40 | + | ||
| 41 | + script += scriptTpl({ | ||
| 42 | + url: this.getURL(entry), | ||
| 43 | + crossorigin: this.crossorigin, | ||
| 44 | + }); | ||
| 45 | + | ||
| 46 | + return script; | ||
| 47 | + } | ||
| 48 | + | ||
| 49 | + getContext(data) { | ||
| 50 | + data = safeStringify(data || this.assetsContext || {}); | ||
| 51 | + return inlineScriptTpl(`(function(){window.${this.config.contextKey} = JSON.parse(decodeURIComponent("${data}"));})()`, this.nonce); | ||
| 52 | + } | ||
| 53 | + | ||
| 54 | + getURL(entry) { | ||
| 55 | + let urlpath = entry; | ||
| 56 | + if (!this.isLocalOrUnittest) { | ||
| 57 | + urlpath = this.manifest[urlpath]; | ||
| 58 | + assert(urlpath, `Don't find ${entry} in manifest.json`); | ||
| 59 | + } | ||
| 60 | + | ||
| 61 | + if (urlpath.startsWith('/')) { | ||
| 62 | + return `${this.host}${urlpath}`; | ||
| 63 | + } | ||
| 64 | + | ||
| 65 | + if (urlpath.startsWith('http://') || urlpath.startsWith('https://')) { | ||
| 66 | + return urlpath; | ||
| 67 | + } | ||
| 68 | + | ||
| 69 | + return `${this.resourceBase}${urlpath}`; | ||
| 70 | + } | ||
| 71 | + | ||
| 72 | + setEntry(entry) { | ||
| 73 | + this.entry = entry.replace(/\.jsx?$/, '.js'); | ||
| 74 | + this.entryCss = entry.replace(/\.jsx?$/, '.css'); | ||
| 75 | + } | ||
| 76 | + | ||
| 77 | + setContext(context) { | ||
| 78 | + this.assetsContext = context; | ||
| 79 | + } | ||
| 80 | + | ||
| 81 | +} | ||
| 82 | + | ||
| 83 | +module.exports = Assets; | ||
| 84 | + | ||
| 85 | +function linkTpl({ url }) { | ||
| 86 | + return `<link rel="stylesheet" href="${url}" />`; | ||
| 87 | +} | ||
| 88 | + | ||
| 89 | +function scriptTpl({ url, crossorigin }) { | ||
| 90 | + return '<script' + (crossorigin ? ' crossorigin' : '') + ` src="${url}"></script>`; | ||
| 91 | +} | ||
| 92 | + | ||
| 93 | +function inlineScriptTpl(content, nonce) { | ||
| 94 | + const startTag = nonce ? `<script nonce=${nonce}>` : '<script>'; | ||
| 95 | + return `${startTag}${content}</script>`; | ||
| 96 | +} | ||
| 97 | + | ||
| 98 | +function safeStringify(data) { | ||
| 99 | + if (!data) return ''; | ||
| 100 | + return encodeURIComponent(JSON.stringify(data)); | ||
| 101 | +} | ||
| 102 | + | ||
| 103 | +function normalizePublicPath(publicPath) { | ||
| 104 | + if (!publicPath) return '/'; | ||
| 105 | + let firstIndex = 0; | ||
| 106 | + if (publicPath[firstIndex] === '/') firstIndex++; | ||
| 107 | + let lastIndex = publicPath.length - 1; | ||
| 108 | + if (publicPath[lastIndex] === '/') lastIndex--; | ||
| 109 | + return '/' + publicPath.slice(firstIndex, lastIndex + 1) + '/'; | ||
| 110 | +} |
lib/assets_view.js
0 → 100644
| 1 | +'use strict'; | ||
| 2 | + | ||
| 3 | +const fs = require('mz/fs'); | ||
| 4 | +const TEMPLATE_CACHE = Symbol('AssetsView#templateCache'); | ||
| 5 | +const HELPER_ASSETS = require('./util/constant').HELPER_ASSETS; | ||
| 6 | +const renderDefault = require('./util/default_template'); | ||
| 7 | + | ||
| 8 | + | ||
| 9 | +class AssetsView { | ||
| 10 | + constructor(ctx) { | ||
| 11 | + this.ctx = ctx; | ||
| 12 | + this.config = ctx.app.config.assets; | ||
| 13 | + this.logger = ctx.coreLogger; | ||
| 14 | + | ||
| 15 | + if (!ctx.app[TEMPLATE_CACHE]) ctx.app[TEMPLATE_CACHE] = new Map(); | ||
| 16 | + this.cache = ctx.app[TEMPLATE_CACHE]; | ||
| 17 | + } | ||
| 18 | + | ||
| 19 | + async render(name, locals, options) { | ||
| 20 | + const templateViewEngine = options.templateViewEngine || this.config.templateViewEngine; | ||
| 21 | + const templatePath = options.templatePath || this.config.templatePath; | ||
| 22 | + | ||
| 23 | + const assets = this.ctx.helper.assets; | ||
| 24 | + assets.setEntry(options.name); | ||
| 25 | + assets.setContext(options.locals); | ||
| 26 | + | ||
| 27 | + if (templateViewEngine && templatePath) { | ||
| 28 | + // helper.assets will use this instance | ||
| 29 | + this.ctx[HELPER_ASSETS] = assets; | ||
| 30 | + | ||
| 31 | + const templateStr = await this.readFileWithCache(templatePath); | ||
| 32 | + this.logger.info('[egg-view-assets] use %s to render %s, entry is %s', | ||
| 33 | + templateViewEngine, templatePath, name); | ||
| 34 | + return await this.ctx.renderString(templateStr, locals, { | ||
| 35 | + viewEngine: templateViewEngine, | ||
| 36 | + }); | ||
| 37 | + } | ||
| 38 | + | ||
| 39 | + return renderDefault(assets); | ||
| 40 | + } | ||
| 41 | + | ||
| 42 | + async readFileWithCache(filepath) { | ||
| 43 | + let str = this.cache.get(filepath); | ||
| 44 | + if (str) return str; | ||
| 45 | + | ||
| 46 | + str = await fs.readFile(filepath, 'utf8'); | ||
| 47 | + this.cache.set(filepath, str); | ||
| 48 | + return str; | ||
| 49 | + } | ||
| 50 | + | ||
| 51 | + async renderString() { | ||
| 52 | + throw new Error('assets engine don\'t support renderString'); | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | +} | ||
| 56 | + | ||
| 57 | +module.exports = AssetsView; |
lib/dev_server.js
0 → 100644
| 1 | +'use strict'; | ||
| 2 | + | ||
| 3 | +const path = require('path'); | ||
| 4 | +const fs = require('mz/fs'); | ||
| 5 | +const spawn = require('cross-spawn'); | ||
| 6 | +const Base = require('sdk-base'); | ||
| 7 | +const sleep = require('mz-modules/sleep'); | ||
| 8 | +const awaitEvent = require('await-event'); | ||
| 9 | +const debug = require('debug')('egg-view-assets:dev_server'); | ||
| 10 | +const detectPort = require('detect-port'); | ||
| 11 | +const mkdirp = require('mz-modules/mkdirp'); | ||
| 12 | +const is = require('is-type-of'); | ||
| 13 | + | ||
| 14 | + | ||
| 15 | +class DevServer extends Base { | ||
| 16 | + constructor(app) { | ||
| 17 | + super({ | ||
| 18 | + initMethod: 'init', | ||
| 19 | + }); | ||
| 20 | + this.app = app; | ||
| 21 | + this.isClosed = false; | ||
| 22 | + } | ||
| 23 | + | ||
| 24 | + async init() { | ||
| 25 | + const { devServer } = this.app.config.assets; | ||
| 26 | + | ||
| 27 | + if (devServer.autoPort) { | ||
| 28 | + devServer.port = await detectPort(devServer.port || 10000); | ||
| 29 | + await mkdirp(path.dirname(devServer.portPath)); | ||
| 30 | + await fs.writeFile(devServer.portPath, devServer.port.toString()); | ||
| 31 | + } else { | ||
| 32 | + // check whether the port is using | ||
| 33 | + if (await this.checkPortExist()) { | ||
| 34 | + throw new Error(`port ${this.app.config.assets.devServer.port} has been used`); | ||
| 35 | + } | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | + // start dev server asynchronously | ||
| 39 | + this.startAsync(); | ||
| 40 | + await this.waitListen(); | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + startAsync() { | ||
| 44 | + const { devServer } = this.app.config.assets; | ||
| 45 | + devServer.command = this.replacePort(devServer.command); | ||
| 46 | + const [ command, ...args ] = devServer.command.split(/\s+/); | ||
| 47 | + | ||
| 48 | + const env = Object.assign({}, process.env, devServer.env); | ||
| 49 | + // env.PATH = `${this.app.config.baseDir}/node_modules/.bin:${env.PATH}`; | ||
| 50 | + // replace {port} | ||
| 51 | + Object.keys(env).forEach(key => { | ||
| 52 | + env[key] = this.replacePort(env[key]); | ||
| 53 | + }); | ||
| 54 | + const opt = { | ||
| 55 | + // disable stdout by default | ||
| 56 | + stdio: [ 'inherit', 'ignore', 'inherit' ], | ||
| 57 | + env, | ||
| 58 | + shell: process.platform === 'win32' | ||
| 59 | + }; | ||
| 60 | + if (devServer.cwd) opt.cwd = devServer.cwd; | ||
| 61 | + if (devServer.debug) opt.stdio[1] = 'inherit'; | ||
| 62 | + const proc = this.proc = spawn(command, args, opt); | ||
| 63 | + proc.once('error', err => this.exit(err)); | ||
| 64 | + proc.once('exit', code => this.exit(code)); | ||
| 65 | + } | ||
| 66 | + | ||
| 67 | + async checkPortExist() { | ||
| 68 | + const { devServer } = this.app.config.assets; | ||
| 69 | + const port = await detectPort(devServer.port); | ||
| 70 | + debug('check %s, get result %s', devServer.port, port); | ||
| 71 | + return port !== devServer.port; | ||
| 72 | + } | ||
| 73 | + | ||
| 74 | + async waitListen() { | ||
| 75 | + const logger = this.app.coreLogger; | ||
| 76 | + const { devServer } = this.app.config.assets; | ||
| 77 | + let timeout = devServer.timeout / 1000; | ||
| 78 | + let isSuccess = false; | ||
| 79 | + while (timeout > 0) { | ||
| 80 | + /* istanbul ignore if */ | ||
| 81 | + if (this.isClosed) { | ||
| 82 | + logger.warn('[egg-view-assets] Closing, but devServer is not listened'); | ||
| 83 | + return; | ||
| 84 | + } | ||
| 85 | + if (await this.checkPortExist()) { | ||
| 86 | + logger.warn('[egg-view-assets] Run "%s" success, listen on %s', devServer.command, devServer.port); | ||
| 87 | + // 成功启动 | ||
| 88 | + isSuccess = true; | ||
| 89 | + break; | ||
| 90 | + } | ||
| 91 | + timeout--; | ||
| 92 | + await sleep(1000); | ||
| 93 | + debug('waiting, %s remain', timeout); | ||
| 94 | + } | ||
| 95 | + | ||
| 96 | + if (isSuccess) return; | ||
| 97 | + const err = new Error(`Run "${devServer.command}" failed after ${devServer.timeout / 1000}s`); | ||
| 98 | + throw err; | ||
| 99 | + } | ||
| 100 | + | ||
| 101 | + async close() { | ||
| 102 | + this.isClosed = true; | ||
| 103 | + /* istanbul ignore if */ | ||
| 104 | + if (!this.proc) return; | ||
| 105 | + this.app.coreLogger.warn('[egg-view-assets] dev server will be killed'); | ||
| 106 | + this.proc.kill(); | ||
| 107 | + await awaitEvent(this.proc, 'exit'); | ||
| 108 | + this.proc = null; | ||
| 109 | + } | ||
| 110 | + | ||
| 111 | + exit(codeOrError) { | ||
| 112 | + const logger = this.app.coreLogger; | ||
| 113 | + this.proc = null; | ||
| 114 | + | ||
| 115 | + if (!(codeOrError instanceof Error)) { | ||
| 116 | + const { devServer } = this.app.config.assets; | ||
| 117 | + const code = codeOrError; | ||
| 118 | + const message = `[egg-view-assets] Run "${devServer.command}" exit with code ${code}`; | ||
| 119 | + if (!code || code === 0) { | ||
| 120 | + logger.info(message); | ||
| 121 | + return; | ||
| 122 | + } | ||
| 123 | + | ||
| 124 | + codeOrError = new Error(message); | ||
| 125 | + } | ||
| 126 | + | ||
| 127 | + logger.error(codeOrError); | ||
| 128 | + } | ||
| 129 | + | ||
| 130 | + replacePort(str) { | ||
| 131 | + if (!is.string(str)) return str; | ||
| 132 | + return str.replace('{port}', this.app.config.assets.devServer.port); | ||
| 133 | + } | ||
| 134 | + | ||
| 135 | +} | ||
| 136 | + | ||
| 137 | +module.exports = DevServer; |
lib/util/constant.js
0 → 100644
lib/util/default_template.js
0 → 100644
package.json
0 → 100644
| 1 | +{ | ||
| 2 | + "name": "egg-view-assets", | ||
| 3 | + "eggPlugin": { | ||
| 4 | + "name": "assets", | ||
| 5 | + "dependencies": [ | ||
| 6 | + "view" | ||
| 7 | + ] | ||
| 8 | + }, | ||
| 9 | + "version": "1.8.0", | ||
| 10 | + "description": "Manage frontend assets in development and production", | ||
| 11 | + "keywords": [ | ||
| 12 | + "egg", | ||
| 13 | + "eggPlugin", | ||
| 14 | + "egg-plugin", | ||
| 15 | + "assets" | ||
| 16 | + ], | ||
| 17 | + "dependencies": { | ||
| 18 | + "await-event": "^2.1.0", | ||
| 19 | + "cross-spawn": "^6.0.5", | ||
| 20 | + "debug": "^4.1.1", | ||
| 21 | + "detect-port": "^1.3.0", | ||
| 22 | + "is-type-of": "^1.2.1", | ||
| 23 | + "mz": "^2.7.0", | ||
| 24 | + "mz-modules": "^2.1.0", | ||
| 25 | + "sdk-base": "^3.5.1" | ||
| 26 | + }, | ||
| 27 | + "devDependencies": { | ||
| 28 | + "autod": "^3.0.1", | ||
| 29 | + "autod-egg": "^1.1.0", | ||
| 30 | + "egg": "^2.14.2", | ||
| 31 | + "egg-bin": "^4.10.0", | ||
| 32 | + "egg-ci": "^1.11.0", | ||
| 33 | + "egg-mock": "^3.21.0", | ||
| 34 | + "egg-view-ejs": "^2.0.0", | ||
| 35 | + "egg-view-nunjucks": "^2.1.6", | ||
| 36 | + "eslint": "^5.12.0", | ||
| 37 | + "eslint-config-egg": "^7.1.0", | ||
| 38 | + "puppeteer": "^1.11.0", | ||
| 39 | + "supertest": "^3.3.0", | ||
| 40 | + "uglify-js": "^3.3.21", | ||
| 41 | + "urllib": "^2.34.1", | ||
| 42 | + "webstorm-disable-index": "^1.2.0" | ||
| 43 | + }, | ||
| 44 | + "engines": { | ||
| 45 | + "node": ">=8.0.0" | ||
| 46 | + }, | ||
| 47 | + "scripts": { | ||
| 48 | + "dev": "egg-bin dev", | ||
| 49 | + "test": "npm run lint -- --fix && egg-bin pkgfiles && npm run test-local", | ||
| 50 | + "test-local": "egg-bin test", | ||
| 51 | + "cov": "egg-bin cov", | ||
| 52 | + "lint": "eslint .", | ||
| 53 | + "ci": "egg-bin pkgfiles --check && npm run lint && npm run cov", | ||
| 54 | + "pkgfiles": "egg-bin pkgfiles", | ||
| 55 | + "autod": "autod", | ||
| 56 | + "uglify": "uglifyjs lib/fixtures/decode.js -o lib/fixtures/decode.min.js" | ||
| 57 | + }, | ||
| 58 | + "files": [ | ||
| 59 | + "app", | ||
| 60 | + "lib", | ||
| 61 | + "config", | ||
| 62 | + "agent.js", | ||
| 63 | + "app.js" | ||
| 64 | + ], | ||
| 65 | + "ci": { | ||
| 66 | + "type": "github", | ||
| 67 | + "version": "8, 10, 12, 14, 16", | ||
| 68 | + "license": true | ||
| 69 | + }, | ||
| 70 | + "repository": { | ||
| 71 | + "type": "git", | ||
| 72 | + "url": "git+https://github.com/eggjs/egg-view-assets.git" | ||
| 73 | + }, | ||
| 74 | + "bugs": { | ||
| 75 | + "url": "https://github.com/eggjs/egg/issues" | ||
| 76 | + }, | ||
| 77 | + "homepage": "https://github.com/eggjs/egg-view-assets#readme", | ||
| 78 | + "author": "popomore", | ||
| 79 | + "license": "MIT" | ||
| 80 | +} |
test/assets.test.js
0 → 100644
| 1 | +'use strict'; | ||
| 2 | + | ||
| 3 | +const path = require('path'); | ||
| 4 | +const mock = require('egg-mock'); | ||
| 5 | +const fs = require('mz/fs'); | ||
| 6 | +const assert = require('assert'); | ||
| 7 | +const urllib = require('urllib'); | ||
| 8 | +const address = require('address'); | ||
| 9 | + | ||
| 10 | +describe('test/assets.test.js', () => { | ||
| 11 | + | ||
| 12 | + afterEach(mock.restore); | ||
| 13 | + | ||
| 14 | + describe('AssetsView with default template', () => { | ||
| 15 | + let app; | ||
| 16 | + | ||
| 17 | + describe('local', () => { | ||
| 18 | + before(() => { | ||
| 19 | + mock.env('local'); | ||
| 20 | + app = mock.cluster({ | ||
| 21 | + baseDir: 'apps/assets', | ||
| 22 | + }); | ||
| 23 | + // app.debug(); | ||
| 24 | + return app.ready(); | ||
| 25 | + }); | ||
| 26 | + after(() => app.close()); | ||
| 27 | + | ||
| 28 | + it('should GET /', () => { | ||
| 29 | + return app.httpRequest() | ||
| 30 | + .get('/') | ||
| 31 | + .expect(/<div id="root"><\/div>/) | ||
| 32 | + .expect(/<link rel="stylesheet" href="http:\/\/127.0.0.1:8000\/index.css" \/>/) | ||
| 33 | + .expect(/<script src="http:\/\/127.0.0.1:8000\/index.js"><\/script>/) | ||
| 34 | + .expect(/<script>window.__webpack_public_path__ = '\/';<\/script>/) | ||
| 35 | + .expect(res => { | ||
| 36 | + assert(res.text.includes('<script>(function(){window.context = JSON.parse(decodeURIComponent("%7B%22data%22%3A1%7D"));})()<\/script>')); | ||
| 37 | + }) | ||
| 38 | + .expect(200); | ||
| 39 | + }); | ||
| 40 | + | ||
| 41 | + it('should GET jsx', () => { | ||
| 42 | + return app.httpRequest() | ||
| 43 | + .get('/account') | ||
| 44 | + .expect(/<link rel="stylesheet" href="http:\/\/127.0.0.1:8000\/account.css" \/>/) | ||
| 45 | + .expect(/<script src="http:\/\/127.0.0.1:8000\/account.js"><\/script>/) | ||
| 46 | + .expect(200); | ||
| 47 | + }); | ||
| 48 | + }); | ||
| 49 | + | ||
| 50 | + describe('production', () => { | ||
| 51 | + let app; | ||
| 52 | + | ||
| 53 | + before(() => { | ||
| 54 | + mock.env('prod'); | ||
| 55 | + app = mock.cluster({ | ||
| 56 | + baseDir: 'apps/assets', | ||
| 57 | + }); | ||
| 58 | + return app.ready(); | ||
| 59 | + }); | ||
| 60 | + after(() => app.close()); | ||
| 61 | + | ||
| 62 | + it('should GET /', () => { | ||
| 63 | + return app.httpRequest() | ||
| 64 | + .get('/') | ||
| 65 | + .expect(/<div id="root"><\/div>/) | ||
| 66 | + .expect(/<link rel="stylesheet" href="http:\/\/cdn.com\/app\/public\/index.b8e2efea.css" \/>/) | ||
| 67 | + .expect(/<script src="http:\/\/cdn.com\/app\/public\/index.c4ae6394.js"><\/script>/) | ||
| 68 | + .expect(/<script>window.__webpack_public_path__ = '\/app\/public\/';<\/script>/) | ||
| 69 | + .expect(res => { | ||
| 70 | + assert(res.text.includes('<script>(function(){window.context = JSON.parse(decodeURIComponent("%7B%22data%22%3A1%7D"));})()<\/script>')); | ||
| 71 | + }) | ||
| 72 | + .expect(200); | ||
| 73 | + }); | ||
| 74 | + }); | ||
| 75 | + }); | ||
| 76 | + | ||
| 77 | + describe('AssetsView with custom template', () => { | ||
| 78 | + let app; | ||
| 79 | + | ||
| 80 | + describe('local', () => { | ||
| 81 | + before(() => { | ||
| 82 | + mock.env('local'); | ||
| 83 | + app = mock.cluster({ | ||
| 84 | + baseDir: 'apps/assets-template', | ||
| 85 | + }); | ||
| 86 | + return app.ready(); | ||
| 87 | + }); | ||
| 88 | + after(() => app.close()); | ||
| 89 | + | ||
| 90 | + it('should GET /', () => { | ||
| 91 | + return app.httpRequest() | ||
| 92 | + .get('/') | ||
| 93 | + .expect(/<div id="root"><\/div>/) | ||
| 94 | + .expect(/<link rel="stylesheet" href="http:\/\/127.0.0.1:8000\/index.css" \/>/) | ||
| 95 | + .expect(/<script src="http:\/\/127.0.0.1:8000\/index.js"><\/script>/) | ||
| 96 | + .expect(res => { | ||
| 97 | + assert(res.text.includes('<script>(function(){window.context = JSON.parse(decodeURIComponent("%7B%7D"));})()<\/script>')); | ||
| 98 | + }) | ||
| 99 | + .expect(200); | ||
| 100 | + }); | ||
| 101 | + | ||
| 102 | + it('should render context', () => { | ||
| 103 | + return app.httpRequest() | ||
| 104 | + .get('/context') | ||
| 105 | + .expect(res => { | ||
| 106 | + assert(res.text.includes('<script>(function(){window.context = JSON.parse(decodeURIComponent("%7B%22data%22%3A1%7D"));})()<\/script>')); | ||
| 107 | + }) | ||
| 108 | + .expect(200); | ||
| 109 | + }); | ||
| 110 | + | ||
| 111 | + it('should use template from render options', () => { | ||
| 112 | + return app.httpRequest() | ||
| 113 | + .get('/options') | ||
| 114 | + .expect(/<div id="root"><\/div>/) | ||
| 115 | + .expect(/<link rel="stylesheet" href="http:\/\/127.0.0.1:8000\/index.css" \/>/) | ||
| 116 | + .expect(/<script src="http:\/\/127.0.0.1:8000\/index.js"><\/script>/) | ||
| 117 | + .expect(res => { | ||
| 118 | + assert(res.text.includes('<script>(function(){window.context = JSON.parse(decodeURIComponent("%7B%7D"));})()<\/script>')); | ||
| 119 | + }) | ||
| 120 | + .expect(200); | ||
| 121 | + }); | ||
| 122 | + | ||
| 123 | + it('should use cache when template exist', async () => { | ||
| 124 | + const template = path.join(__dirname, 'fixtures/apps/assets-template/app/view/cache.html'); | ||
| 125 | + await fs.writeFile(template, '{{ data }}'); | ||
| 126 | + | ||
| 127 | + await app.httpRequest() | ||
| 128 | + .get('/cache') | ||
| 129 | + .expect(res => { | ||
| 130 | + assert(res.text.includes('<script>(function(){window.context = JSON.parse(decodeURIComponent("%7B%22data%22%3A1%7D"));})()<\/script>')); | ||
| 131 | + }) | ||
| 132 | + .expect(200); | ||
| 133 | + | ||
| 134 | + await fs.writeFile(template, 'override'); | ||
| 135 | + | ||
| 136 | + await app.httpRequest() | ||
| 137 | + .get('/cache') | ||
| 138 | + .expect(res => { | ||
| 139 | + assert(res.text.includes('<script>(function(){window.context = JSON.parse(decodeURIComponent("%7B%22data%22%3A1%7D"));})()<\/script>')); | ||
| 140 | + }) | ||
| 141 | + .expect(200); | ||
| 142 | + }); | ||
| 143 | + | ||
| 144 | + it('should throw when call renderString', () => { | ||
| 145 | + return app.httpRequest() | ||
| 146 | + .get('/renderString') | ||
| 147 | + .expect(/assets engine don't support renderString/) | ||
| 148 | + .expect(500); | ||
| 149 | + }); | ||
| 150 | + }); | ||
| 151 | + | ||
| 152 | + describe('prod', () => { | ||
| 153 | + before(() => { | ||
| 154 | + mock.env('prod'); | ||
| 155 | + app = mock.cluster({ | ||
| 156 | + baseDir: 'apps/assets-template', | ||
| 157 | + }); | ||
| 158 | + return app.ready(); | ||
| 159 | + }); | ||
| 160 | + after(() => app.close()); | ||
| 161 | + | ||
| 162 | + it('should GET /', () => { | ||
| 163 | + return app.httpRequest() | ||
| 164 | + .get('/') | ||
| 165 | + .expect(/<div id="root"><\/div>/) | ||
| 166 | + .expect(/<link rel="stylesheet" href="http:\/\/cdn.com\/index.b8e2efea.css" \/>/) | ||
| 167 | + .expect(/<script src="http:\/\/cdn.com\/index.c4ae6394.js"><\/script>/) | ||
| 168 | + .expect(res => { | ||
| 169 | + assert(res.text.includes('<script>(function(){window.context = JSON.parse(decodeURIComponent("%7B%7D"));})()<\/script>')); | ||
| 170 | + }) | ||
| 171 | + .expect(200); | ||
| 172 | + }); | ||
| 173 | + }); | ||
| 174 | + }); | ||
| 175 | + | ||
| 176 | + describe('in other view engine', () => { | ||
| 177 | + let app; | ||
| 178 | + | ||
| 179 | + describe('local', () => { | ||
| 180 | + before(() => { | ||
| 181 | + mock.env('local'); | ||
| 182 | + app = mock.cluster({ | ||
| 183 | + baseDir: 'apps/other-view-engine', | ||
| 184 | + }); | ||
| 185 | + return app.ready(); | ||
| 186 | + }); | ||
| 187 | + after(() => app.close()); | ||
| 188 | + | ||
| 189 | + it('should GET /', () => { | ||
| 190 | + return app.httpRequest() | ||
| 191 | + .get('/') | ||
| 192 | + .expect(/<link rel="stylesheet" href="http:\/\/127.0.0.1:8000\/index.css" \/>/) | ||
| 193 | + .expect(/<script src="http:\/\/127.0.0.1:8000\/index.js"><\/script>/) | ||
| 194 | + .expect(/<script>window.__webpack_public_path__ = '\/';<\/script>/) | ||
| 195 | + .expect(/<script>window.resourceBaseUrl = 'http:\/\/127.0.0.1:8000\/';<\/script/) | ||
| 196 | + .expect(res => { | ||
| 197 | + assert(res.text.includes('<script>(function(){window.context = JSON.parse(decodeURIComponent("%7B%22data%22%3A1%7D"));})()<\/script>')); | ||
| 198 | + }) | ||
| 199 | + .expect(200); | ||
| 200 | + }); | ||
| 201 | + }); | ||
| 202 | + | ||
| 203 | + describe('prod', () => { | ||
| 204 | + before(() => { | ||
| 205 | + mock.env('prod'); | ||
| 206 | + app = mock.cluster({ | ||
| 207 | + baseDir: 'apps/other-view-engine', | ||
| 208 | + }); | ||
| 209 | + return app.ready(); | ||
| 210 | + }); | ||
| 211 | + after(() => app.close()); | ||
| 212 | + | ||
| 213 | + it('should GET /', () => { | ||
| 214 | + return app.httpRequest() | ||
| 215 | + .get('/') | ||
| 216 | + .expect(/<link rel="stylesheet" href="http:\/\/cdn.com\/app\/public\/index.b8e2efea.css" \/>/) | ||
| 217 | + .expect(/<script src="http:\/\/cdn.com\/app\/public\/index.c4ae6394.js"><\/script>/) | ||
| 218 | + .expect(/<script>window.__webpack_public_path__ = '\/app\/public\/';<\/script>/) | ||
| 219 | + .expect(/<script>window.resourceBaseUrl = 'http:\/\/cdn.com\/app\/public\/';<\/script/) | ||
| 220 | + .expect(res => { | ||
| 221 | + assert(res.text.includes('<script>(function(){window.context = JSON.parse(decodeURIComponent("%7B%22data%22%3A1%7D"));})()<\/script>')); | ||
| 222 | + }) | ||
| 223 | + .expect(200); | ||
| 224 | + }); | ||
| 225 | + }); | ||
| 226 | + }); | ||
| 227 | + | ||
| 228 | + describe('custom assets.url', () => { | ||
| 229 | + let app; | ||
| 230 | + | ||
| 231 | + before(() => { | ||
| 232 | + mock.env('prod'); | ||
| 233 | + app = mock.cluster({ | ||
| 234 | + baseDir: 'apps/custom-assets-url', | ||
| 235 | + }); | ||
| 236 | + return app.ready(); | ||
| 237 | + }); | ||
| 238 | + after(() => app.close()); | ||
| 239 | + | ||
| 240 | + it('should GET /', () => { | ||
| 241 | + return app.httpRequest() | ||
| 242 | + .get('/') | ||
| 243 | + .expect(/<link rel="stylesheet" href="http:\/\/localhost\/index.b8e2efea.css" \/>/) | ||
| 244 | + .expect(200); | ||
| 245 | + }); | ||
| 246 | + }); | ||
| 247 | + | ||
| 248 | + describe('https assets.url with dynamicLocalIP', () => { | ||
| 249 | + let app; | ||
| 250 | + | ||
| 251 | + before(() => { | ||
| 252 | + mock.env('local'); | ||
| 253 | + app = mock.cluster({ | ||
| 254 | + baseDir: 'apps/https', | ||
| 255 | + port: 8443, | ||
| 256 | + https: { | ||
| 257 | + cert: path.join(__dirname, 'fixtures/apps/https/server.cert'), | ||
| 258 | + key: path.join(__dirname, 'fixtures/apps/https/server.key'), | ||
| 259 | + }, | ||
| 260 | + }); | ||
| 261 | + return app.ready(); | ||
| 262 | + }); | ||
| 263 | + after(() => app.close()); | ||
| 264 | + | ||
| 265 | + it('should GET /', () => { | ||
| 266 | + return urllib.request('https://127.0.0.1:8443', { | ||
| 267 | + dataType: 'text', | ||
| 268 | + rejectUnauthorized: false, | ||
| 269 | + }).then(response => { | ||
| 270 | + assert(response.status === 200); | ||
| 271 | + assert(response.data.includes('https://127.0.0.1:8000/index.css')); | ||
| 272 | + }); | ||
| 273 | + }); | ||
| 274 | + }); | ||
| 275 | + | ||
| 276 | + describe('https assets.url without dynamicLocalIP', () => { | ||
| 277 | + let app; | ||
| 278 | + | ||
| 279 | + before(() => { | ||
| 280 | + mock.env('local'); | ||
| 281 | + app = mock.cluster({ | ||
| 282 | + baseDir: 'apps/https-dynamic-ip', | ||
| 283 | + port: 8443, | ||
| 284 | + https: { | ||
| 285 | + cert: path.join(__dirname, 'fixtures/apps/https-dynamic-ip/server.cert'), | ||
| 286 | + key: path.join(__dirname, 'fixtures/apps/https-dynamic-ip/server.key'), | ||
| 287 | + }, | ||
| 288 | + }); | ||
| 289 | + return app.ready(); | ||
| 290 | + }); | ||
| 291 | + after(() => app.close()); | ||
| 292 | + | ||
| 293 | + it('should GET /', () => { | ||
| 294 | + return urllib.request(`https://${address.ip()}:8443`, { | ||
| 295 | + dataType: 'text', | ||
| 296 | + rejectUnauthorized: false, | ||
| 297 | + }).then(response => { | ||
| 298 | + assert(response.status === 200); | ||
| 299 | + assert(response.data.includes('http://127.0.0.1:8000/index.css')); | ||
| 300 | + }); | ||
| 301 | + }); | ||
| 302 | + }); | ||
| 303 | + | ||
| 304 | + describe('custom contextKey', () => { | ||
| 305 | + let app; | ||
| 306 | + | ||
| 307 | + before(() => { | ||
| 308 | + mock.env('local'); | ||
| 309 | + app = mock.cluster({ | ||
| 310 | + baseDir: 'apps/custom-context-key', | ||
| 311 | + }); | ||
| 312 | + return app.ready(); | ||
| 313 | + }); | ||
| 314 | + after(() => app.close()); | ||
| 315 | + | ||
| 316 | + it('should GET /', () => { | ||
| 317 | + return app.httpRequest() | ||
| 318 | + .get('/') | ||
| 319 | + .expect(/window.__context__ =/) | ||
| 320 | + .expect(200); | ||
| 321 | + }); | ||
| 322 | + }); | ||
| 323 | + | ||
| 324 | + describe('context security', () => { | ||
| 325 | + let app; | ||
| 326 | + | ||
| 327 | + before(() => { | ||
| 328 | + app = mock.cluster({ | ||
| 329 | + baseDir: 'apps/context-security', | ||
| 330 | + }); | ||
| 331 | + return app.ready(); | ||
| 332 | + }); | ||
| 333 | + after(() => app.close()); | ||
| 334 | + | ||
| 335 | + it('should GET /', () => { | ||
| 336 | + return app.httpRequest() | ||
| 337 | + .get('/?query=<x%E2%80%A8x>') | ||
| 338 | + .expect(res => { | ||
| 339 | + assert(res.text.includes('<script>(function(){window.context = JSON.parse(decodeURIComponent("%7B%22query%22%3A%22%3Cx%E2%80%A8x%3E%22%7D"));})()<\/script>')); | ||
| 340 | + }) | ||
| 341 | + .expect(200); | ||
| 342 | + }); | ||
| 343 | + }); | ||
| 344 | + | ||
| 345 | + describe('publicPath', () => { | ||
| 346 | + let app; | ||
| 347 | + | ||
| 348 | + describe('local', () => { | ||
| 349 | + before(() => { | ||
| 350 | + mock.env('local'); | ||
| 351 | + app = mock.app({ | ||
| 352 | + baseDir: 'apps/custom-public-path', | ||
| 353 | + }); | ||
| 354 | + return app.ready(); | ||
| 355 | + }); | ||
| 356 | + after(() => app.close()); | ||
| 357 | + | ||
| 358 | + it('should render with trailing /', () => { | ||
| 359 | + mock(app.config.assets, 'publicPath', '/public/'); | ||
| 360 | + | ||
| 361 | + let ctx = app.mockContext(); | ||
| 362 | + ctx.helper.assets.setEntry('index.js'); | ||
| 363 | + let script = ctx.helper.assets.getScript(); | ||
| 364 | + assert(script.includes('__webpack_public_path__ = \'/\';')); | ||
| 365 | + assert(script.includes('src="/index.js"')); | ||
| 366 | + let style = ctx.helper.assets.getStyle(); | ||
| 367 | + assert(style.includes('href="/index.css"')); | ||
| 368 | + | ||
| 369 | + ctx = app.mockContext(); | ||
| 370 | + script = ctx.helper.assets.getScript('index.js'); | ||
| 371 | + assert(script.includes('__webpack_public_path__ = \'/\';')); | ||
| 372 | + assert(script.includes('src="/index.js"')); | ||
| 373 | + style = ctx.helper.assets.getStyle('index.css'); | ||
| 374 | + assert(style.includes('href="/index.css"')); | ||
| 375 | + }); | ||
| 376 | + | ||
| 377 | + it('should render without trailing /', () => { | ||
| 378 | + mock(app.config.assets, 'publicPath', '/public'); | ||
| 379 | + | ||
| 380 | + let ctx = app.mockContext(); | ||
| 381 | + ctx.helper.assets.setEntry('index.js'); | ||
| 382 | + let script = ctx.helper.assets.getScript(); | ||
| 383 | + assert(script.includes('__webpack_public_path__ = \'/\';')); | ||
| 384 | + assert(script.includes('src="/index.js"')); | ||
| 385 | + let style = ctx.helper.assets.getStyle(); | ||
| 386 | + assert(style.includes('href="/index.css"')); | ||
| 387 | + | ||
| 388 | + ctx = app.mockContext(); | ||
| 389 | + script = ctx.helper.assets.getScript('index.js'); | ||
| 390 | + assert(script.includes('__webpack_public_path__ = \'/\';')); | ||
| 391 | + assert(script.includes('src="/index.js"')); | ||
| 392 | + style = ctx.helper.assets.getStyle('index.css'); | ||
| 393 | + assert(style.includes('href="/index.css"')); | ||
| 394 | + }); | ||
| 395 | + }); | ||
| 396 | + | ||
| 397 | + describe('prod', () => { | ||
| 398 | + before(() => { | ||
| 399 | + mock.env('prod'); | ||
| 400 | + app = mock.app({ | ||
| 401 | + baseDir: 'apps/custom-public-path', | ||
| 402 | + }); | ||
| 403 | + return app.ready(); | ||
| 404 | + }); | ||
| 405 | + after(() => app.close()); | ||
| 406 | + | ||
| 407 | + it('should render with trailing /', () => { | ||
| 408 | + mock(app.config.assets, 'publicPath', '/public/'); | ||
| 409 | + | ||
| 410 | + let ctx = app.mockContext(); | ||
| 411 | + ctx.helper.assets.setEntry('index.js'); | ||
| 412 | + let script = ctx.helper.assets.getScript(); | ||
| 413 | + assert(script.includes('__webpack_public_path__ = \'/public/\';')); | ||
| 414 | + assert(script.includes('src="/public/index.js"')); | ||
| 415 | + let style = ctx.helper.assets.getStyle(); | ||
| 416 | + assert(style.includes('href="/public/index.css"')); | ||
| 417 | + | ||
| 418 | + ctx = app.mockContext(); | ||
| 419 | + script = ctx.helper.assets.getScript('index.js'); | ||
| 420 | + assert(script.includes('__webpack_public_path__ = \'/public/\';')); | ||
| 421 | + assert(script.includes('src="/public/index.js"')); | ||
| 422 | + style = ctx.helper.assets.getStyle('index.css'); | ||
| 423 | + assert(style.includes('href="/public/index.css"')); | ||
| 424 | + }); | ||
| 425 | + | ||
| 426 | + it('should render without trailing /', () => { | ||
| 427 | + mock(app.config.assets, 'publicPath', '/public'); | ||
| 428 | + | ||
| 429 | + let ctx = app.mockContext(); | ||
| 430 | + ctx.helper.assets.setEntry('index.js'); | ||
| 431 | + let script = ctx.helper.assets.getScript(); | ||
| 432 | + assert(script.includes('__webpack_public_path__ = \'/public/\';')); | ||
| 433 | + assert(script.includes('src="/public/index.js"')); | ||
| 434 | + let style = ctx.helper.assets.getStyle(); | ||
| 435 | + assert(style.includes('href="/public/index.css"')); | ||
| 436 | + | ||
| 437 | + ctx = app.mockContext(); | ||
| 438 | + script = ctx.helper.assets.getScript('index.js'); | ||
| 439 | + assert(script.includes('__webpack_public_path__ = \'/public/\';')); | ||
| 440 | + assert(script.includes('src="/public/index.js"')); | ||
| 441 | + style = ctx.helper.assets.getStyle('index.css'); | ||
| 442 | + assert(style.includes('href="/public/index.css"')); | ||
| 443 | + }); | ||
| 444 | + }); | ||
| 445 | + }); | ||
| 446 | + | ||
| 447 | + describe('manifest checking', () => { | ||
| 448 | + let app; | ||
| 449 | + afterEach(() => app.close()); | ||
| 450 | + | ||
| 451 | + it('should check manifest.json on prod', async () => { | ||
| 452 | + mock.env('prod'); | ||
| 453 | + app = mock.app({ | ||
| 454 | + baseDir: 'apps/no-manifest', | ||
| 455 | + }); | ||
| 456 | + // app.debug(); | ||
| 457 | + try { | ||
| 458 | + await app.ready(); | ||
| 459 | + throw new Error('should not run'); | ||
| 460 | + } catch (err) { | ||
| 461 | + assert(err.message === path.join(__dirname, 'fixtures/apps/no-manifest/config/manifest.json') + ' is required'); | ||
| 462 | + } | ||
| 463 | + }); | ||
| 464 | + | ||
| 465 | + it('should not check manifest.json on local', async () => { | ||
| 466 | + mock.env('local'); | ||
| 467 | + app = mock.app({ | ||
| 468 | + baseDir: 'apps/no-manifest', | ||
| 469 | + }); | ||
| 470 | + // app.debug(); | ||
| 471 | + await app.ready(); | ||
| 472 | + }); | ||
| 473 | + | ||
| 474 | + it('should not check manifest.json on unittest', async () => { | ||
| 475 | + mock.env('unittest'); | ||
| 476 | + app = mock.app({ | ||
| 477 | + baseDir: 'apps/no-manifest', | ||
| 478 | + }); | ||
| 479 | + // app.debug(); | ||
| 480 | + await app.ready(); | ||
| 481 | + }); | ||
| 482 | + }); | ||
| 483 | + | ||
| 484 | + describe('complex manifest', () => { | ||
| 485 | + let app; | ||
| 486 | + before(() => { | ||
| 487 | + mock.env('default'); | ||
| 488 | + app = mock.app({ | ||
| 489 | + baseDir: 'apps/complex-manifest', | ||
| 490 | + }); | ||
| 491 | + return app.ready(); | ||
| 492 | + }); | ||
| 493 | + | ||
| 494 | + after(() => app.close()); | ||
| 495 | + afterEach(mock.restore); | ||
| 496 | + | ||
| 497 | + it('should publicPath work', () => { | ||
| 498 | + const ctx = app.mockContext(); | ||
| 499 | + ctx.helper.assets.setEntry('index.js'); | ||
| 500 | + const script = ctx.helper.assets.getScript(); | ||
| 501 | + assert(script.includes('__webpack_public_path__ = \'/public\/\';')); | ||
| 502 | + assert(script.includes('src="/public/index.js"')); | ||
| 503 | + }); | ||
| 504 | + | ||
| 505 | + it('should contain host if setting assets.url', () => { | ||
| 506 | + mock(app.config.assets, 'url', 'http://remotehost'); | ||
| 507 | + const ctx = app.mockContext(); | ||
| 508 | + ctx.helper.assets.setEntry('index.js'); | ||
| 509 | + const script = ctx.helper.assets.getScript(); | ||
| 510 | + assert(script.includes('__webpack_public_path__ = \'/public\/\';')); | ||
| 511 | + assert(script.includes('src="http://remotehost/public/index.js"')); | ||
| 512 | + const style = ctx.helper.assets.getStyle(); | ||
| 513 | + assert(style.includes('href="http://remotehost/index.css"')); | ||
| 514 | + }); | ||
| 515 | + | ||
| 516 | + it('should assets.publicPath not work if resource path is a absolute url', () => { | ||
| 517 | + const ctx = app.mockContext(); | ||
| 518 | + const style = ctx.helper.assets.getStyle('index.css'); | ||
| 519 | + assert(style.includes('href="/index.css"')); | ||
| 520 | + }); | ||
| 521 | + | ||
| 522 | + it('should assets.url not work if resource path is a complete url', () => { | ||
| 523 | + mock(app.config.assets, 'url', 'http://remotehost'); | ||
| 524 | + const ctx = app.mockContext(); | ||
| 525 | + const script = ctx.helper.assets.getScript('page1.js'); | ||
| 526 | + assert(script.includes('src="http://cdn.com/page1.js"')); | ||
| 527 | + }); | ||
| 528 | + }); | ||
| 529 | + | ||
| 530 | + describe('support crossorigin', () => { | ||
| 531 | + let app; | ||
| 532 | + before(() => { | ||
| 533 | + mock.env('default'); | ||
| 534 | + app = mock.app({ | ||
| 535 | + baseDir: 'apps/crossorigin', | ||
| 536 | + }); | ||
| 537 | + return app.ready(); | ||
| 538 | + }); | ||
| 539 | + | ||
| 540 | + after(() => app.close()); | ||
| 541 | + afterEach(mock.restore); | ||
| 542 | + | ||
| 543 | + it('should works', () => { | ||
| 544 | + const ctx = app.mockContext(); | ||
| 545 | + ctx.helper.assets.setEntry('index.js'); | ||
| 546 | + const script = ctx.helper.assets.getScript(); | ||
| 547 | + assert(script.includes('crossorigin')); | ||
| 548 | + }); | ||
| 549 | + }); | ||
| 550 | + | ||
| 551 | + describe('should insert webpack global variable just once', () => { | ||
| 552 | + let app; | ||
| 553 | + before(() => { | ||
| 554 | + mock.env('default'); | ||
| 555 | + app = mock.app({ | ||
| 556 | + baseDir: 'apps/multiple-getscript', | ||
| 557 | + }); | ||
| 558 | + return app.ready(); | ||
| 559 | + }); | ||
| 560 | + | ||
| 561 | + after(() => app.close()); | ||
| 562 | + afterEach(mock.restore); | ||
| 563 | + | ||
| 564 | + it('should works', () => { | ||
| 565 | + const ctx = app.mockContext(); | ||
| 566 | + | ||
| 567 | + const script = ctx.helper.assets.getScript('vendor.js'); | ||
| 568 | + assert(script.includes('__webpack_public_path__ = \'/\';')); | ||
| 569 | + assert(script.includes('src="/vendor.js"')); | ||
| 570 | + | ||
| 571 | + [ 'a.js', 'b.js', 'c.js' ].forEach(file => { | ||
| 572 | + const anotherScript = ctx.helper.assets.getScript(file); | ||
| 573 | + assert(!anotherScript.includes('__webpack_public_path__')); | ||
| 574 | + assert(anotherScript.includes(`src="/${file}"`)); | ||
| 575 | + }); | ||
| 576 | + | ||
| 577 | + }); | ||
| 578 | + }); | ||
| 579 | + | ||
| 580 | + describe('AssetsView with nonce', () => { | ||
| 581 | + let app; | ||
| 582 | + | ||
| 583 | + describe('local', () => { | ||
| 584 | + before(() => { | ||
| 585 | + mock.env('local'); | ||
| 586 | + app = mock.cluster({ | ||
| 587 | + baseDir: 'apps/assets-nonce', | ||
| 588 | + }); | ||
| 589 | + app.debug(); | ||
| 590 | + return app.ready(); | ||
| 591 | + }); | ||
| 592 | + after(() => app.close()); | ||
| 593 | + | ||
| 594 | + it('should GET /', () => { | ||
| 595 | + return app.httpRequest() | ||
| 596 | + .get('/') | ||
| 597 | + .expect(/<div id="root"><\/div>/) | ||
| 598 | + .expect(/<link rel="stylesheet" href="http:\/\/127.0.0.1:8000\/index.css" \/>/) | ||
| 599 | + .expect(/<script src="http:\/\/127.0.0.1:8000\/index.js"><\/script>/) | ||
| 600 | + .expect(/<script nonce=cspnonce>window.__webpack_public_path__ = '\/';<\/script>/) | ||
| 601 | + .expect(res => { | ||
| 602 | + assert(res.text.includes('<script nonce=cspnonce>(function(){window.context = JSON.parse(decodeURIComponent("%7B%22data%22%3A1%7D"));})()<\/script>')); | ||
| 603 | + }) | ||
| 604 | + .expect(200); | ||
| 605 | + }); | ||
| 606 | + | ||
| 607 | + it('should GET jsx', () => { | ||
| 608 | + return app.httpRequest() | ||
| 609 | + .get('/account') | ||
| 610 | + .expect(/<link rel="stylesheet" href="http:\/\/127.0.0.1:8000\/account.css" \/>/) | ||
| 611 | + .expect(/<script src="http:\/\/127.0.0.1:8000\/account.js"><\/script>/) | ||
| 612 | + .expect(200); | ||
| 613 | + }); | ||
| 614 | + }); | ||
| 615 | + | ||
| 616 | + describe('production', () => { | ||
| 617 | + let app; | ||
| 618 | + | ||
| 619 | + before(() => { | ||
| 620 | + mock.env('prod'); | ||
| 621 | + app = mock.cluster({ | ||
| 622 | + baseDir: 'apps/assets-nonce', | ||
| 623 | + }); | ||
| 624 | + return app.ready(); | ||
| 625 | + }); | ||
| 626 | + after(() => app.close()); | ||
| 627 | + | ||
| 628 | + it('should GET /', () => { | ||
| 629 | + return app.httpRequest() | ||
| 630 | + .get('/') | ||
| 631 | + .expect(/<div id="root"><\/div>/) | ||
| 632 | + .expect(/<link rel="stylesheet" href="http:\/\/cdn.com\/app\/public\/index.b8e2efea.css" \/>/) | ||
| 633 | + .expect(/<script src="http:\/\/cdn.com\/app\/public\/index.c4ae6394.js"><\/script>/) | ||
| 634 | + .expect(/<script nonce=cspnonce>window.__webpack_public_path__ = '\/app\/public\/';<\/script>/) | ||
| 635 | + .expect(res => { | ||
| 636 | + assert(res.text.includes('<script nonce=cspnonce>(function(){window.context = JSON.parse(decodeURIComponent("%7B%22data%22%3A1%7D"));})()<\/script>')); | ||
| 637 | + }) | ||
| 638 | + .expect(200); | ||
| 639 | + }); | ||
| 640 | + }); | ||
| 641 | + }); | ||
| 642 | +}); |
test/dev_server.test.js
0 → 100644
| 1 | +'use strict'; | ||
| 2 | + | ||
| 3 | +const net = require('net'); | ||
| 4 | +const path = require('path'); | ||
| 5 | +const mock = require('egg-mock'); | ||
| 6 | +const assert = require('assert'); | ||
| 7 | +const sleep = require('mz-modules/sleep'); | ||
| 8 | + | ||
| 9 | +describe('test/dev_server.test.js', () => { | ||
| 10 | + | ||
| 11 | + let app; | ||
| 12 | + afterEach(mock.restore); | ||
| 13 | + afterEach(() => app.close()); | ||
| 14 | + afterEach(() => sleep(5000)); | ||
| 15 | + | ||
| 16 | + it('should start/stop dev server', async () => { | ||
| 17 | + mock.env('local'); | ||
| 18 | + app = mock.cluster({ | ||
| 19 | + baseDir: 'apps/assets', | ||
| 20 | + }); | ||
| 21 | + app.debug(); | ||
| 22 | + await app.ready(); | ||
| 23 | + const reg = new RegExp(`Run "node ${path.join(__dirname, 'fixtures/apps/mocktool/server.js')}" success, listen on 8000`); | ||
| 24 | + app.expect('stdout', reg); | ||
| 25 | + | ||
| 26 | + await app.close(); | ||
| 27 | + app.expect('stdout', /\[egg-view-assets] dev server will be killed/); | ||
| 28 | + app.expect('stdout', /server stopped/); | ||
| 29 | + app.expect('stderr', /\[server] error/); | ||
| 30 | + }); | ||
| 31 | + | ||
| 32 | + it('should check first when port has been listened', async () => { | ||
| 33 | + mock.env('local'); | ||
| 34 | + const server = new net.Server(); | ||
| 35 | + server.listen(8000); | ||
| 36 | + app = mock.cluster({ | ||
| 37 | + baseDir: 'apps/assets', | ||
| 38 | + }); | ||
| 39 | + // app.debug(); | ||
| 40 | + await app.ready(); | ||
| 41 | + app.notExpect('stdout', /listen on 8000/); | ||
| 42 | + app.expect('stderr', /port 8000 has been used/); | ||
| 43 | + app.expect('stderr', /\[agent_worker] start error/); | ||
| 44 | + server.close(); | ||
| 45 | + }); | ||
| 46 | + | ||
| 47 | + it('should success when run command', async () => { | ||
| 48 | + mock.env('local'); | ||
| 49 | + app = mock.cluster({ | ||
| 50 | + baseDir: 'apps/not-listen', | ||
| 51 | + }); | ||
| 52 | + app.debug(); | ||
| 53 | + await app.ready(); | ||
| 54 | + | ||
| 55 | + await app.close(); | ||
| 56 | + await sleep(5000); | ||
| 57 | + | ||
| 58 | + // app.expect('stdout', /Closing, but devServer is not listened/); | ||
| 59 | + }); | ||
| 60 | + | ||
| 61 | + it('should custom devServer.cwd', async () => { | ||
| 62 | + mock(process.env, 'DEV_SERVER_DEBUG', true); | ||
| 63 | + mock.env('local'); | ||
| 64 | + app = mock.cluster({ | ||
| 65 | + baseDir: 'apps/custom-dev-server', | ||
| 66 | + }); | ||
| 67 | + app.debug(); | ||
| 68 | + await app.ready(); | ||
| 69 | + | ||
| 70 | + assert(app.stdout.includes('[server] cwd: ' + path.join(__dirname, 'fixtures/apps/custom-dev-server/config'))); | ||
| 71 | + }); | ||
| 72 | + | ||
| 73 | + it('should custom devServer.env', async () => { | ||
| 74 | + mock(process.env, 'DEV_SERVER_DEBUG', true); | ||
| 75 | + mock.env('local'); | ||
| 76 | + app = mock.cluster({ | ||
| 77 | + baseDir: 'apps/custom-dev-server', | ||
| 78 | + }); | ||
| 79 | + app.debug(); | ||
| 80 | + await app.ready(); | ||
| 81 | + | ||
| 82 | + assert(app.stdout.includes('[server] DEBUG: true')); | ||
| 83 | + }); | ||
| 84 | + | ||
| 85 | + it('should disable devServer.debug', async () => { | ||
| 86 | + mock(process.env, 'DEV_SERVER_DEBUG', false); | ||
| 87 | + mock.env('local'); | ||
| 88 | + app = mock.cluster({ | ||
| 89 | + baseDir: 'apps/custom-dev-server', | ||
| 90 | + }); | ||
| 91 | + app.debug(); | ||
| 92 | + await app.ready(); | ||
| 93 | + | ||
| 94 | + assert(!app.stdout.includes(path.join(__dirname, 'fixtures/apps/custom-dev-server/config'))); | ||
| 95 | + }); | ||
| 96 | + | ||
| 97 | + it('should log error when run command error', async () => { | ||
| 98 | + mock(process.env, 'DEV_SERVER_DEBUG', true); | ||
| 99 | + mock(process.env, 'EXIT', true); | ||
| 100 | + mock.env('local'); | ||
| 101 | + app = mock.cluster({ | ||
| 102 | + baseDir: 'apps/custom-dev-server', | ||
| 103 | + }); | ||
| 104 | + app.debug(); | ||
| 105 | + await app.ready(); | ||
| 106 | + | ||
| 107 | + const server = path.join(__dirname, 'fixtures/apps/custom-dev-server/config/server.js'); | ||
| 108 | + const errMsg = `[egg-view-assets] Run "node ${server}" exit with code 1`; | ||
| 109 | + assert(app.stderr.includes(errMsg)); | ||
| 110 | + }); | ||
| 111 | + | ||
| 112 | + it('should wait timeout', async () => { | ||
| 113 | + mock(process.env, 'DEV_SERVER_DEBUG', true); | ||
| 114 | + mock.env('local'); | ||
| 115 | + app = mock.cluster({ | ||
| 116 | + baseDir: 'apps/custom-dev-server', | ||
| 117 | + }); | ||
| 118 | + app.debug(); | ||
| 119 | + await app.ready(); | ||
| 120 | + | ||
| 121 | + await sleep(10000); | ||
| 122 | + const server = path.join(__dirname, 'fixtures/apps/custom-dev-server/config/server.js'); | ||
| 123 | + const errMsg = `[egg-view-assets] Run "node ${server}" failed after 5s`; | ||
| 124 | + assert(app.stderr.includes(errMsg)); | ||
| 125 | + }); | ||
| 126 | + | ||
| 127 | + it('should throw when command error', async () => { | ||
| 128 | + mock.env('local'); | ||
| 129 | + app = mock.cluster({ | ||
| 130 | + baseDir: 'apps/command-error', | ||
| 131 | + }); | ||
| 132 | + app.debug(); | ||
| 133 | + await app.ready(); | ||
| 134 | + | ||
| 135 | + app.expect('stderr', /spawn unknown ENOENT/); | ||
| 136 | + app.expect('stderr', /Run "unknown command" failed after 5s/); | ||
| 137 | + }); | ||
| 138 | + | ||
| 139 | + it('should check port when devServer is enabled', async () => { | ||
| 140 | + mock.env('local'); | ||
| 141 | + app = mock.cluster({ | ||
| 142 | + baseDir: 'apps/dev-server-no-port', | ||
| 143 | + }); | ||
| 144 | + // app.debug(); | ||
| 145 | + await app.ready(); | ||
| 146 | + | ||
| 147 | + app.expect('code', 1); | ||
| 148 | + app.expect('stderr', /port or autoPort is required when devServer is enabled/); | ||
| 149 | + }); | ||
| 150 | + | ||
| 151 | + it('should not check port when devServer is disabled', async () => { | ||
| 152 | + mock.env('local'); | ||
| 153 | + mock(process.env, 'DEV_SERVER_ENABLE', 'false'); | ||
| 154 | + app = mock.cluster({ | ||
| 155 | + baseDir: 'apps/dev-server-no-port', | ||
| 156 | + }); | ||
| 157 | + app.debug(); | ||
| 158 | + await app.ready(); | ||
| 159 | + | ||
| 160 | + app.expect('code', 0); | ||
| 161 | + app.expect('stdout', /egg started/); | ||
| 162 | + }); | ||
| 163 | + | ||
| 164 | + it('should auto check port with autoPort', async () => { | ||
| 165 | + mock.env('local'); | ||
| 166 | + const app1 = mock.cluster({ | ||
| 167 | + baseDir: 'apps/autoport', | ||
| 168 | + }); | ||
| 169 | + // app1.debug(); | ||
| 170 | + await app1.ready(); | ||
| 171 | + | ||
| 172 | + await app1.httpRequest() | ||
| 173 | + .get('/') | ||
| 174 | + .expect(/http:\/\/127.0.0.1:10000\/index.js/) | ||
| 175 | + .expect(200); | ||
| 176 | + await app1.httpRequest() | ||
| 177 | + .get('/port') | ||
| 178 | + .expect('10000') | ||
| 179 | + .expect(200); | ||
| 180 | + | ||
| 181 | + app1.expect('stdout', /\[server] listening 10000/); | ||
| 182 | + app1.expect('stdout', /\[server] SOCKET_SERVER: http:\/\/127.0.0.1:10000/); | ||
| 183 | + | ||
| 184 | + app = mock.cluster({ | ||
| 185 | + baseDir: 'apps/autoport', | ||
| 186 | + }); | ||
| 187 | + // app.debug(); | ||
| 188 | + try { | ||
| 189 | + await app.ready(); | ||
| 190 | + | ||
| 191 | + app.expect('stdout', /\[server] listening 10001/); | ||
| 192 | + } finally { | ||
| 193 | + await app1.close(); | ||
| 194 | + } | ||
| 195 | + }); | ||
| 196 | + | ||
| 197 | + it('should auto check port with autoPort and port offset', async () => { | ||
| 198 | + mock.env('local'); | ||
| 199 | + const app1 = mock.cluster({ | ||
| 200 | + baseDir: 'apps/autoport-offset', | ||
| 201 | + }); | ||
| 202 | + | ||
| 203 | + await app1.ready(); | ||
| 204 | + | ||
| 205 | + await app1.httpRequest() | ||
| 206 | + .get('/') | ||
| 207 | + .expect(/http:\/\/127.0.0.1:8000\/index.js/) | ||
| 208 | + .expect(200); | ||
| 209 | + await app1.httpRequest() | ||
| 210 | + .get('/port') | ||
| 211 | + .expect('8000') | ||
| 212 | + .expect(200); | ||
| 213 | + | ||
| 214 | + app1.expect('stdout', /\[server] listening 8000/); | ||
| 215 | + app1.expect('stdout', /\[server] SOCKET_SERVER: http:\/\/127.0.0.1:8000/); | ||
| 216 | + | ||
| 217 | + app = mock.cluster({ | ||
| 218 | + baseDir: 'apps/autoport-offset', | ||
| 219 | + }); | ||
| 220 | + // app.debug(); | ||
| 221 | + try { | ||
| 222 | + await app.ready(); | ||
| 223 | + | ||
| 224 | + app.expect('stdout', /\[server] listening 8001/); | ||
| 225 | + } finally { | ||
| 226 | + await app1.close(); | ||
| 227 | + } | ||
| 228 | + }); | ||
| 229 | +}); |
| 1 | +'use strict'; | ||
| 2 | + | ||
| 3 | +const Controller = require('egg').Controller; | ||
| 4 | + | ||
| 5 | +class HomeController extends Controller { | ||
| 6 | + async index() { | ||
| 7 | + await this.ctx.render('index.js', { | ||
| 8 | + data: 1, | ||
| 9 | + }); | ||
| 10 | + } | ||
| 11 | + | ||
| 12 | + async account() { | ||
| 13 | + await this.ctx.render('account.jsx'); | ||
| 14 | + } | ||
| 15 | + | ||
| 16 | + async renderString() { | ||
| 17 | + await this.ctx.renderString('', {}, { | ||
| 18 | + viewEngine: 'assets', | ||
| 19 | + }); | ||
| 20 | + } | ||
| 21 | +} | ||
| 22 | + | ||
| 23 | +module.exports = HomeController; |
| 1 | +'use strict'; | ||
| 2 | + | ||
| 3 | +const path = require('path'); | ||
| 4 | + | ||
| 5 | +exports.keys = '123456'; | ||
| 6 | +exports.view = { | ||
| 7 | + mapping: { | ||
| 8 | + '.js': 'assets', | ||
| 9 | + '.jsx': 'assets', | ||
| 10 | + }, | ||
| 11 | +}; | ||
| 12 | +exports.assets = { | ||
| 13 | + publicPath: '/app/public', | ||
| 14 | + devServer: { | ||
| 15 | + waitStart: true, | ||
| 16 | + command: 'node ' + path.join(__dirname, '../../mocktool/server.js'), | ||
| 17 | + port: 8000, | ||
| 18 | + env: {}, | ||
| 19 | + debug: true, | ||
| 20 | + }, | ||
| 21 | + nonce(ctx) { | ||
| 22 | + return ctx.cspnonce; | ||
| 23 | + }, | ||
| 24 | +}; |
| 1 | +{ | ||
| 2 | + "0.a94eff34.async.js": "0.a94eff34.async.js", | ||
| 3 | + "1.0647e11d.async.js": "1.0647e11d.async.js", | ||
| 4 | + "2.cbbca76b.async.js": "2.cbbca76b.async.js", | ||
| 5 | + "constants.js": "constants.e5eb201e.js", | ||
| 6 | + "index.css": "index.b8e2efea.css", | ||
| 7 | + "index.js": "index.c4ae6394.js", | ||
| 8 | + "static/yay.jpg": "static/yay.44dd3333.jpg" | ||
| 9 | +} |
test/fixtures/apps/assets-nonce/package.json
0 → 100644
| 1 | +'use strict'; | ||
| 2 | + | ||
| 3 | +const path = require('path'); | ||
| 4 | +const Controller = require('egg').Controller; | ||
| 5 | + | ||
| 6 | +class HomeController extends Controller { | ||
| 7 | + async index() { | ||
| 8 | + await this.ctx.render('index.js'); | ||
| 9 | + } | ||
| 10 | + | ||
| 11 | + async context() { | ||
| 12 | + await this.ctx.render('index.js', { | ||
| 13 | + data: 1, | ||
| 14 | + }); | ||
| 15 | + } | ||
| 16 | + | ||
| 17 | + async options() { | ||
| 18 | + await this.ctx.render('index.js', {}, { | ||
| 19 | + templatePath: path.join(__dirname, '../view/template.ejs'), | ||
| 20 | + templateViewEngine: 'ejs', | ||
| 21 | + }); | ||
| 22 | + } | ||
| 23 | + | ||
| 24 | + async cache() { | ||
| 25 | + await this.ctx.render('index.js', { data: 1 }, { | ||
| 26 | + templatePath: path.join(__dirname, '../view/template.ejs'), | ||
| 27 | + templateViewEngine: 'ejs', | ||
| 28 | + }); | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + async renderString() { | ||
| 32 | + await this.ctx.renderString('index.js', { data: 1 }, { | ||
| 33 | + viewEngine: 'assets', | ||
| 34 | + }); | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | +} | ||
| 38 | + | ||
| 39 | +module.exports = HomeController; |
| 1 | +'use strict'; | ||
| 2 | + | ||
| 3 | +module.exports = app => { | ||
| 4 | + const { router, controller } = app; | ||
| 5 | + | ||
| 6 | + router.get('/', controller.home.index); | ||
| 7 | + router.get('/context', controller.home.context); | ||
| 8 | + router.get('/options', controller.home.options); | ||
| 9 | + router.get('/cache', controller.home.cache); | ||
| 10 | + router.get('/renderString', controller.home.renderString); | ||
| 11 | +}; |
| 1 | +'use strict'; | ||
| 2 | + | ||
| 3 | +const path = require('path'); | ||
| 4 | + | ||
| 5 | +exports.keys = '123456'; | ||
| 6 | +exports.view = { | ||
| 7 | + mapping: { | ||
| 8 | + '.js': 'assets', | ||
| 9 | + }, | ||
| 10 | +}; | ||
| 11 | +exports.assets = { | ||
| 12 | + templatePath: path.join(__dirname, '../app/view/template.html'), | ||
| 13 | + templateViewEngine: 'nunjucks', | ||
| 14 | + devServer: { | ||
| 15 | + waitStart: true, | ||
| 16 | + command: 'node ' + path.join(__dirname, '../../mocktool/server.js'), | ||
| 17 | + port: 8000, | ||
| 18 | + env: {}, | ||
| 19 | + debug: true, | ||
| 20 | + }, | ||
| 21 | +}; |
| 1 | +{ | ||
| 2 | + "0.a94eff34.async.js": "0.a94eff34.async.js", | ||
| 3 | + "1.0647e11d.async.js": "1.0647e11d.async.js", | ||
| 4 | + "2.cbbca76b.async.js": "2.cbbca76b.async.js", | ||
| 5 | + "constants.js": "constants.e5eb201e.js", | ||
| 6 | + "index.css": "index.b8e2efea.css", | ||
| 7 | + "index.js": "index.c4ae6394.js", | ||
| 8 | + "static/yay.jpg": "static/yay.44dd3333.jpg" | ||
| 9 | +} |
| 1 | +'use strict'; | ||
| 2 | + | ||
| 3 | +const Controller = require('egg').Controller; | ||
| 4 | + | ||
| 5 | +class HomeController extends Controller { | ||
| 6 | + async index() { | ||
| 7 | + await this.ctx.render('index.js', { | ||
| 8 | + data: 1, | ||
| 9 | + }); | ||
| 10 | + } | ||
| 11 | + | ||
| 12 | + async account() { | ||
| 13 | + await this.ctx.render('account.jsx'); | ||
| 14 | + } | ||
| 15 | + | ||
| 16 | + async renderString() { | ||
| 17 | + await this.ctx.renderString('', {}, { | ||
| 18 | + viewEngine: 'assets', | ||
| 19 | + }); | ||
| 20 | + } | ||
| 21 | +} | ||
| 22 | + | ||
| 23 | +module.exports = HomeController; |
test/fixtures/apps/assets/app/router.js
0 → 100644
test/fixtures/apps/assets/app/view/index.js
0 → 100644
| 1 | +'use strict'; | ||
| 2 | + | ||
| 3 | +const path = require('path'); | ||
| 4 | + | ||
| 5 | +exports.keys = '123456'; | ||
| 6 | +exports.view = { | ||
| 7 | + mapping: { | ||
| 8 | + '.js': 'assets', | ||
| 9 | + '.jsx': 'assets', | ||
| 10 | + }, | ||
| 11 | +}; | ||
| 12 | +exports.assets = { | ||
| 13 | + publicPath: '/app/public', | ||
| 14 | + devServer: { | ||
| 15 | + waitStart: true, | ||
| 16 | + command: 'node ' + path.join(__dirname, '../../mocktool/server.js'), | ||
| 17 | + port: 8000, | ||
| 18 | + env: {}, | ||
| 19 | + debug: true, | ||
| 20 | + }, | ||
| 21 | +}; |
| 1 | +{ | ||
| 2 | + "0.a94eff34.async.js": "0.a94eff34.async.js", | ||
| 3 | + "1.0647e11d.async.js": "1.0647e11d.async.js", | ||
| 4 | + "2.cbbca76b.async.js": "2.cbbca76b.async.js", | ||
| 5 | + "constants.js": "constants.e5eb201e.js", | ||
| 6 | + "index.css": "index.b8e2efea.css", | ||
| 7 | + "index.js": "index.c4ae6394.js", | ||
| 8 | + "static/yay.jpg": "static/yay.44dd3333.jpg" | ||
| 9 | +} |
test/fixtures/apps/assets/package.json
0 → 100644
| 1 | +'use strict'; | ||
| 2 | + | ||
| 3 | +const Controller = require('egg').Controller; | ||
| 4 | + | ||
| 5 | +class HomeController extends Controller { | ||
| 6 | + async index() { | ||
| 7 | + await this.ctx.render('index.js'); | ||
| 8 | + } | ||
| 9 | + | ||
| 10 | + async port() { | ||
| 11 | + this.ctx.body = this.app.config.assets.devServer.port; | ||
| 12 | + } | ||
| 13 | +} | ||
| 14 | + | ||
| 15 | +module.exports = HomeController; |
| 1 | +'use strict'; | ||
| 2 | + | ||
| 3 | +const path = require('path'); | ||
| 4 | + | ||
| 5 | +exports.keys = '123456'; | ||
| 6 | +exports.view = { | ||
| 7 | + mapping: { | ||
| 8 | + '.js': 'assets', | ||
| 9 | + '.jsx': 'assets', | ||
| 10 | + }, | ||
| 11 | +}; | ||
| 12 | +exports.assets = { | ||
| 13 | + devServer: { | ||
| 14 | + waitStart: true, | ||
| 15 | + autoPort: true, | ||
| 16 | + port: 8000, | ||
| 17 | + command: 'node ' + path.join(__dirname, '../../mocktool/server.js') + ' {port}', | ||
| 18 | + env: { | ||
| 19 | + SOCKET_SERVER: 'http://127.0.0.1:{port}', | ||
| 20 | + }, | ||
| 21 | + debug: true, | ||
| 22 | + }, | ||
| 23 | +}; |
| 1 | +'use strict'; | ||
| 2 | + | ||
| 3 | +const Controller = require('egg').Controller; | ||
| 4 | + | ||
| 5 | +class HomeController extends Controller { | ||
| 6 | + async index() { | ||
| 7 | + await this.ctx.render('index.js'); | ||
| 8 | + } | ||
| 9 | + | ||
| 10 | + async port() { | ||
| 11 | + this.ctx.body = this.app.config.assets.devServer.port; | ||
| 12 | + } | ||
| 13 | +} | ||
| 14 | + | ||
| 15 | +module.exports = HomeController; |
test/fixtures/apps/autoport/app/router.js
0 → 100644
| 1 | +'use strict'; | ||
| 2 | + | ||
| 3 | +const path = require('path'); | ||
| 4 | + | ||
| 5 | +exports.keys = '123456'; | ||
| 6 | +exports.view = { | ||
| 7 | + mapping: { | ||
| 8 | + '.js': 'assets', | ||
| 9 | + '.jsx': 'assets', | ||
| 10 | + }, | ||
| 11 | +}; | ||
| 12 | +exports.assets = { | ||
| 13 | + devServer: { | ||
| 14 | + waitStart: true, | ||
| 15 | + autoPort: true, | ||
| 16 | + command: 'node ' + path.join(__dirname, '../../mocktool/server.js') + ' {port}', | ||
| 17 | + env: { | ||
| 18 | + SOCKET_SERVER: 'http://127.0.0.1:{port}', | ||
| 19 | + }, | ||
| 20 | + debug: true, | ||
| 21 | + }, | ||
| 22 | +}; |
test/fixtures/apps/autoport/package.json
0 → 100644
| 1 | +body { color: white; } |
| 1 | +'use strict'; | ||
| 2 | + | ||
| 3 | +const path = require('path'); | ||
| 4 | + | ||
| 5 | +exports.keys = '123456'; | ||
| 6 | +exports.view = { | ||
| 7 | + mapping: { | ||
| 8 | + '.js': 'assets', | ||
| 9 | + '.jsx': 'assets', | ||
| 10 | + }, | ||
| 11 | +}; | ||
| 12 | +exports.assets = { | ||
| 13 | + publicPath: '/public/', | ||
| 14 | + templateViewEngine: 'nunjucks', | ||
| 15 | + templatePath: path.join(__dirname, '../app/view/layout.html'), | ||
| 16 | +}; |
| 1 | +body { color: white; } |
test/fixtures/apps/crossorigin/app/router.js
0 → 100644
| 1 | +'use strict'; | ||
| 2 | + | ||
| 3 | +const path = require('path'); | ||
| 4 | + | ||
| 5 | +exports.keys = '123456'; | ||
| 6 | +exports.view = { | ||
| 7 | + mapping: { | ||
| 8 | + '.js': 'assets', | ||
| 9 | + }, | ||
| 10 | +}; | ||
| 11 | +exports.assets = { | ||
| 12 | + publicPath: '/public/', | ||
| 13 | + url: 'http://example.com', | ||
| 14 | + crossorigin: true, | ||
| 15 | + templateViewEngine: 'nunjucks', | ||
| 16 | + templatePath: path.join(__dirname, '../app/view/layout.html'), | ||
| 17 | +}; |
test/fixtures/apps/crossorigin/package.json
0 → 100644
| 1 | +'use strict'; | ||
| 2 | + | ||
| 3 | +const Controller = require('egg').Controller; | ||
| 4 | + | ||
| 5 | +class HomeController extends Controller { | ||
| 6 | + async index() { | ||
| 7 | + await this.ctx.render('index.js'); | ||
| 8 | + } | ||
| 9 | + | ||
| 10 | + async context() { | ||
| 11 | + await this.ctx.render('index.js', { | ||
| 12 | + data: 1, | ||
| 13 | + }); | ||
| 14 | + } | ||
| 15 | + | ||
| 16 | +} | ||
| 17 | + | ||
| 18 | +module.exports = HomeController; |
| 1 | +'use strict'; | ||
| 2 | + | ||
| 3 | +const path = require('path'); | ||
| 4 | + | ||
| 5 | +exports.keys = '123456'; | ||
| 6 | +exports.view = { | ||
| 7 | + mapping: { | ||
| 8 | + '.js': 'assets', | ||
| 9 | + }, | ||
| 10 | +}; | ||
| 11 | +exports.assets = { | ||
| 12 | + devServer: { | ||
| 13 | + waitStart: true, | ||
| 14 | + command: 'node ' + path.join(__dirname, '../../mocktool/server.js'), | ||
| 15 | + port: 8000, | ||
| 16 | + env: {}, | ||
| 17 | + debug: true, | ||
| 18 | + }, | ||
| 19 | +}; |
| 1 | +{ | ||
| 2 | + "0.a94eff34.async.js": "0.a94eff34.async.js", | ||
| 3 | + "1.0647e11d.async.js": "1.0647e11d.async.js", | ||
| 4 | + "2.cbbca76b.async.js": "2.cbbca76b.async.js", | ||
| 5 | + "constants.js": "constants.e5eb201e.js", | ||
| 6 | + "index.css": "index.b8e2efea.css", | ||
| 7 | + "index.js": "index.c4ae6394.js", | ||
| 8 | + "static/yay.jpg": "static/yay.44dd3333.jpg" | ||
| 9 | +} |
| 1 | +'use strict'; | ||
| 2 | + | ||
| 3 | +const path = require('path'); | ||
| 4 | + | ||
| 5 | +exports.keys = '123456'; | ||
| 6 | +exports.view = { | ||
| 7 | + mapping: { | ||
| 8 | + '.js': 'assets', | ||
| 9 | + }, | ||
| 10 | +}; | ||
| 11 | +exports.assets = { | ||
| 12 | + contextKey: '__context__', | ||
| 13 | + devServer: { | ||
| 14 | + waitStart: true, | ||
| 15 | + command: 'node ' + path.join(__dirname, '../../mocktool/server.js'), | ||
| 16 | + port: 8000, | ||
| 17 | + env: {}, | ||
| 18 | + debug: true, | ||
| 19 | + }, | ||
| 20 | +}; |
| 1 | +'use strict'; | ||
| 2 | + | ||
| 3 | +exports.keys = '123456'; | ||
| 4 | +exports.view = { | ||
| 5 | + mapping: { | ||
| 6 | + '.js': 'assets', | ||
| 7 | + }, | ||
| 8 | +}; | ||
| 9 | +exports.assets = { | ||
| 10 | + devServer: { | ||
| 11 | + waitStart: true, | ||
| 12 | + command: 'node ' + require.resolve('./server.js'), | ||
| 13 | + cwd: __dirname, | ||
| 14 | + port: 8000, | ||
| 15 | + env: { | ||
| 16 | + DEBUG: true, | ||
| 17 | + }, | ||
| 18 | + timeout: 5000, | ||
| 19 | + debug: process.env.DEV_SERVER_DEBUG === 'true', | ||
| 20 | + }, | ||
| 21 | +}; |
| 1 | +'use strict'; | ||
| 2 | + | ||
| 3 | +exports.keys = '123456'; | ||
| 4 | +exports.view = { | ||
| 5 | + mapping: { | ||
| 6 | + '.js': 'assets', | ||
| 7 | + }, | ||
| 8 | +}; | ||
| 9 | +exports.assets = { | ||
| 10 | + devServer: { | ||
| 11 | + enable: process.env.DEV_SERVER_ENABLE !== 'false', | ||
| 12 | + waitStart: true, | ||
| 13 | + command: 'node ' + require.resolve('./server.js'), | ||
| 14 | + cwd: __dirname, | ||
| 15 | + env: { | ||
| 16 | + DEBUG: true, | ||
| 17 | + }, | ||
| 18 | + timeout: 5000, | ||
| 19 | + debug: process.env.DEV_SERVER_DEBUG === 'true', | ||
| 20 | + }, | ||
| 21 | +}; |
| 1 | +'use strict'; | ||
| 2 | + | ||
| 3 | +const Controller = require('egg').Controller; | ||
| 4 | + | ||
| 5 | +class HomeController extends Controller { | ||
| 6 | + async index() { | ||
| 7 | + await this.ctx.render('index.js'); | ||
| 8 | + } | ||
| 9 | + | ||
| 10 | + async context() { | ||
| 11 | + await this.ctx.render('index.js', { | ||
| 12 | + data: 1, | ||
| 13 | + }); | ||
| 14 | + } | ||
| 15 | + | ||
| 16 | +} | ||
| 17 | + | ||
| 18 | +module.exports = HomeController; |
| 1 | +'use strict'; | ||
| 2 | + | ||
| 3 | +const path = require('path'); | ||
| 4 | + | ||
| 5 | +exports.keys = '123456'; | ||
| 6 | +exports.view = { | ||
| 7 | + mapping: { | ||
| 8 | + '.js': 'assets', | ||
| 9 | + }, | ||
| 10 | +}; | ||
| 11 | +exports.assets = { | ||
| 12 | + dynamicLocalIP: false, | ||
| 13 | + devServer: { | ||
| 14 | + waitStart: true, | ||
| 15 | + command: 'node ' + path.join(__dirname, '../../mocktool/server.js'), | ||
| 16 | + port: 8000, | ||
| 17 | + env: {}, | ||
| 18 | + debug: true, | ||
| 19 | + }, | ||
| 20 | +}; |
| 1 | +-----BEGIN CERTIFICATE----- | ||
| 2 | +MIIC9DCCAdygAwIBAgIJANhNsdON73XpMA0GCSqGSIb3DQEBCwUAMBkxFzAVBgNV | ||
| 3 | +BAMMDjEyNy4wLjAuMTo4NDQzMB4XDTE5MDkxODA5Mzk1MVoXDTE5MTAxODA5Mzk1 | ||
| 4 | +MVowGTEXMBUGA1UEAwwOMTI3LjAuMC4xOjg0NDMwggEiMA0GCSqGSIb3DQEBAQUA | ||
| 5 | +A4IBDwAwggEKAoIBAQDRNVNmZWcD5444kPDHn/oJmrZ0MtGbAgaJeDWWi88sfAHZ | ||
| 6 | +skrz8akYIHnU3kP3jQ2fJYdG5h8sO9aRMscKiBSfyVT0dbzIFJJwfPxC7myzRysx | ||
| 7 | +5p/J1uO7ZyUM2lo15ia//yShVjVG+YyUnwEUYE153z0KI9ni+rQIaEh9L9jgd18I | ||
| 8 | +oWlQYdcMpZI6udc967bMfIRYGeIap4bk32JlxYKffFs1PCtOB93G0JAFgBjCPlvf | ||
| 9 | +HysymSKjZkvTA8kqO3oqWBpqAW6pvVPk0XO8mtUjw5l/nHcTTehym+g3XdQ+W1vo | ||
| 10 | +KFChRXX7j8PmDVA2U5cJxjQFpao0N8q31u4oDq3pAgMBAAGjPzA9MBkGA1UdEQQS | ||
| 11 | +MBCCDjEyNy4wLjAuMTo4NDQzMAsGA1UdDwQEAwIHgDATBgNVHSUEDDAKBggrBgEF | ||
| 12 | +BQcDATANBgkqhkiG9w0BAQsFAAOCAQEAZSg3NlR7o4OUGFd2z+EQxuaceQ0OU/2l | ||
| 13 | +ydZ9Hjo/DyUR6dyB700X3EpbAXKh90i6fD6DN+LHW3rPqUIdCMTsQOE7o/vQwhka | ||
| 14 | +rBUUOjEVs1qRSUgCq4qZzErGFH70z0z1YGSCUGvd50mRDLPhhmhqMBMRLDNJL39J | ||
| 15 | +2txPMzfNKTr/NqJC+0GfWEUeyVK6blhaxVk+02HJ7Wn4IH61WWXoKz+H3HPuj23E | ||
| 16 | +etGKSLLnMpADjYRZU4/NAe+AOJxTVYTylUfST0Wm5XnvI/8tAjo6Ttzmfa0m1+OZ | ||
| 17 | +GZDt1+FF8c4eRLQpRtyF4p6BxUvrk5BrPZJL7YkcXRcmJcFEGsr9Kw== | ||
| 18 | +-----END CERTIFICATE----- |
| 1 | +-----BEGIN PRIVATE KEY----- | ||
| 2 | +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDRNVNmZWcD5444 | ||
| 3 | +kPDHn/oJmrZ0MtGbAgaJeDWWi88sfAHZskrz8akYIHnU3kP3jQ2fJYdG5h8sO9aR | ||
| 4 | +MscKiBSfyVT0dbzIFJJwfPxC7myzRysx5p/J1uO7ZyUM2lo15ia//yShVjVG+YyU | ||
| 5 | +nwEUYE153z0KI9ni+rQIaEh9L9jgd18IoWlQYdcMpZI6udc967bMfIRYGeIap4bk | ||
| 6 | +32JlxYKffFs1PCtOB93G0JAFgBjCPlvfHysymSKjZkvTA8kqO3oqWBpqAW6pvVPk | ||
| 7 | +0XO8mtUjw5l/nHcTTehym+g3XdQ+W1voKFChRXX7j8PmDVA2U5cJxjQFpao0N8q3 | ||
| 8 | +1u4oDq3pAgMBAAECggEBANBAKd3S8MGlPSA3v0ani4kF9bsjXxzS3lj3TTinwTnB | ||
| 9 | +00G1xgCgvTbkFQJ9oeCJC1cxT0CXD87DXeo5RoLIROdtzWBu1tXADKlm8OFbXt7y | ||
| 10 | +6B+LN+H+Q8Engm0R6hyfFlGmk/r5ypz1CihjQvrahiTbwHIYj+rurkl0LTxYZaXB | ||
| 11 | +wQ8scpyqB2TgScseJLlyESvyCYrfQ+PoqG+3Sm6WikDvEXBAJTSOgQ9RDIlL/o6z | ||
| 12 | +v5RESJmXIzlIHzt8SnneyitgjkjiW6T5s0CNIRe81aW+bl5K81tMKDDejiyzYEPM | ||
| 13 | +fWMLbQzARTnWui3c48BEv5Um6Txm60nL7xuIbm+VfpECgYEA6yK9LRTb73HMklCo | ||
| 14 | +Dyg/0Yrws9A13Fx7xMVRKGIQ6TIVUl2tl0fqbtohTrJC1YMrUFdsPtOpVrsvDx5z | ||
| 15 | +uMxfprlzF8lXWff/7oB1BWk7ljIZnA0BJ+f00eZRmpHkSvvrLBIfGAM1uilu9w5Q | ||
| 16 | +SvchbV00qcJAuiuMwl8XWBpKqaUCgYEA48WhC/kLnrjRUkbcOK+Y4m88+PUy4hZa | ||
| 17 | +NAY25t+4mUG6tDFe5N/YBj2YphGPBOGJfZbm0R06UCA5TpvdshORyu7KU80IQimC | ||
| 18 | +8OQKLJu/2mSlNL94xCzCONpUtMCkt7qJtHRlXiW32zSs8oMlJkCWScQmlvXpSOvF | ||
| 19 | +DtbdWNhCl/UCgYEArzReY7YUP3G7Lhb9cvXZv8hYnJN7Xqxm6PLiC0YvNaPE8W+u | ||
| 20 | +BnhmvZy5jssTM8ceQioyFQpgNqkZYpmdPsjCSRgMI2A6P2akhFGhYJvN85d3L6kd | ||
| 21 | +AGA9Mx38FkG5KwpVbKxAEQNrSEDRVzOKLqHAfZ4ivI9q7Y2/v6FdYp+MnKUCgYAe | ||
| 22 | +RXe/5Zt7vshjDPPbvMqu3WXXGI9oqNvYKjNFv9oC1E+B/0XDWVSWj70n5RGC7o6d | ||
| 23 | +WF7Yz++eEopLPj/amOs5cz8EoC1GzcmPRl9ryk9XMSUbu47LU7+EWtADLc82c941 | ||
| 24 | +U3rdZlnnEjsiB/f5+3990knmqkwU2vDXpgrI8gT3uQKBgQCP93Bcr4MQF1ogwFeI | ||
| 25 | +o/cWIMYI2vbZAo7e5X1I4JVYm0R3c7bsKJo8vrYMwKMGfGqj56iF9wduN6rKORhx | ||
| 26 | +rA/wPES4zORRum09ppk9UOR7jIVTUqbJ9qvbmwHuXn4rn1JDvmd+Pixi7e5Ftr7G | ||
| 27 | +vXP18pbM3/WexDXV7UvHrqCxxA== | ||
| 28 | +-----END PRIVATE KEY----- |
| 1 | +'use strict'; | ||
| 2 | + | ||
| 3 | +const Controller = require('egg').Controller; | ||
| 4 | + | ||
| 5 | +class HomeController extends Controller { | ||
| 6 | + async index() { | ||
| 7 | + await this.ctx.render('index.js'); | ||
| 8 | + } | ||
| 9 | + | ||
| 10 | + async context() { | ||
| 11 | + await this.ctx.render('index.js', { | ||
| 12 | + data: 1, | ||
| 13 | + }); | ||
| 14 | + } | ||
| 15 | + | ||
| 16 | +} | ||
| 17 | + | ||
| 18 | +module.exports = HomeController; |
test/fixtures/apps/https/app/router.js
0 → 100644
test/fixtures/apps/https/app/view/index.js
0 → 100644
| 1 | +'use strict'; | ||
| 2 | + | ||
| 3 | +const path = require('path'); | ||
| 4 | + | ||
| 5 | +exports.keys = '123456'; | ||
| 6 | +exports.view = { | ||
| 7 | + mapping: { | ||
| 8 | + '.js': 'assets', | ||
| 9 | + }, | ||
| 10 | +}; | ||
| 11 | +exports.assets = { | ||
| 12 | + devServer: { | ||
| 13 | + waitStart: true, | ||
| 14 | + command: 'node ' + path.join(__dirname, '../../mocktool/server.js'), | ||
| 15 | + port: 8000, | ||
| 16 | + env: {}, | ||
| 17 | + debug: true, | ||
| 18 | + }, | ||
| 19 | +}; |
test/fixtures/apps/https/package.json
0 → 100644
test/fixtures/apps/https/server.cert
0 → 100644
| 1 | +-----BEGIN CERTIFICATE----- | ||
| 2 | +MIIC9DCCAdygAwIBAgIJANhNsdON73XpMA0GCSqGSIb3DQEBCwUAMBkxFzAVBgNV | ||
| 3 | +BAMMDjEyNy4wLjAuMTo4NDQzMB4XDTE5MDkxODA5Mzk1MVoXDTE5MTAxODA5Mzk1 | ||
| 4 | +MVowGTEXMBUGA1UEAwwOMTI3LjAuMC4xOjg0NDMwggEiMA0GCSqGSIb3DQEBAQUA | ||
| 5 | +A4IBDwAwggEKAoIBAQDRNVNmZWcD5444kPDHn/oJmrZ0MtGbAgaJeDWWi88sfAHZ | ||
| 6 | +skrz8akYIHnU3kP3jQ2fJYdG5h8sO9aRMscKiBSfyVT0dbzIFJJwfPxC7myzRysx | ||
| 7 | +5p/J1uO7ZyUM2lo15ia//yShVjVG+YyUnwEUYE153z0KI9ni+rQIaEh9L9jgd18I | ||
| 8 | +oWlQYdcMpZI6udc967bMfIRYGeIap4bk32JlxYKffFs1PCtOB93G0JAFgBjCPlvf | ||
| 9 | +HysymSKjZkvTA8kqO3oqWBpqAW6pvVPk0XO8mtUjw5l/nHcTTehym+g3XdQ+W1vo | ||
| 10 | +KFChRXX7j8PmDVA2U5cJxjQFpao0N8q31u4oDq3pAgMBAAGjPzA9MBkGA1UdEQQS | ||
| 11 | +MBCCDjEyNy4wLjAuMTo4NDQzMAsGA1UdDwQEAwIHgDATBgNVHSUEDDAKBggrBgEF | ||
| 12 | +BQcDATANBgkqhkiG9w0BAQsFAAOCAQEAZSg3NlR7o4OUGFd2z+EQxuaceQ0OU/2l | ||
| 13 | +ydZ9Hjo/DyUR6dyB700X3EpbAXKh90i6fD6DN+LHW3rPqUIdCMTsQOE7o/vQwhka | ||
| 14 | +rBUUOjEVs1qRSUgCq4qZzErGFH70z0z1YGSCUGvd50mRDLPhhmhqMBMRLDNJL39J | ||
| 15 | +2txPMzfNKTr/NqJC+0GfWEUeyVK6blhaxVk+02HJ7Wn4IH61WWXoKz+H3HPuj23E | ||
| 16 | +etGKSLLnMpADjYRZU4/NAe+AOJxTVYTylUfST0Wm5XnvI/8tAjo6Ttzmfa0m1+OZ | ||
| 17 | +GZDt1+FF8c4eRLQpRtyF4p6BxUvrk5BrPZJL7YkcXRcmJcFEGsr9Kw== | ||
| 18 | +-----END CERTIFICATE----- |
test/fixtures/apps/https/server.key
0 → 100644
| 1 | +-----BEGIN PRIVATE KEY----- | ||
| 2 | +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDRNVNmZWcD5444 | ||
| 3 | +kPDHn/oJmrZ0MtGbAgaJeDWWi88sfAHZskrz8akYIHnU3kP3jQ2fJYdG5h8sO9aR | ||
| 4 | +MscKiBSfyVT0dbzIFJJwfPxC7myzRysx5p/J1uO7ZyUM2lo15ia//yShVjVG+YyU | ||
| 5 | +nwEUYE153z0KI9ni+rQIaEh9L9jgd18IoWlQYdcMpZI6udc967bMfIRYGeIap4bk | ||
| 6 | +32JlxYKffFs1PCtOB93G0JAFgBjCPlvfHysymSKjZkvTA8kqO3oqWBpqAW6pvVPk | ||
| 7 | +0XO8mtUjw5l/nHcTTehym+g3XdQ+W1voKFChRXX7j8PmDVA2U5cJxjQFpao0N8q3 | ||
| 8 | +1u4oDq3pAgMBAAECggEBANBAKd3S8MGlPSA3v0ani4kF9bsjXxzS3lj3TTinwTnB | ||
| 9 | +00G1xgCgvTbkFQJ9oeCJC1cxT0CXD87DXeo5RoLIROdtzWBu1tXADKlm8OFbXt7y | ||
| 10 | +6B+LN+H+Q8Engm0R6hyfFlGmk/r5ypz1CihjQvrahiTbwHIYj+rurkl0LTxYZaXB | ||
| 11 | +wQ8scpyqB2TgScseJLlyESvyCYrfQ+PoqG+3Sm6WikDvEXBAJTSOgQ9RDIlL/o6z | ||
| 12 | +v5RESJmXIzlIHzt8SnneyitgjkjiW6T5s0CNIRe81aW+bl5K81tMKDDejiyzYEPM | ||
| 13 | +fWMLbQzARTnWui3c48BEv5Um6Txm60nL7xuIbm+VfpECgYEA6yK9LRTb73HMklCo | ||
| 14 | +Dyg/0Yrws9A13Fx7xMVRKGIQ6TIVUl2tl0fqbtohTrJC1YMrUFdsPtOpVrsvDx5z | ||
| 15 | +uMxfprlzF8lXWff/7oB1BWk7ljIZnA0BJ+f00eZRmpHkSvvrLBIfGAM1uilu9w5Q | ||
| 16 | +SvchbV00qcJAuiuMwl8XWBpKqaUCgYEA48WhC/kLnrjRUkbcOK+Y4m88+PUy4hZa | ||
| 17 | +NAY25t+4mUG6tDFe5N/YBj2YphGPBOGJfZbm0R06UCA5TpvdshORyu7KU80IQimC | ||
| 18 | +8OQKLJu/2mSlNL94xCzCONpUtMCkt7qJtHRlXiW32zSs8oMlJkCWScQmlvXpSOvF | ||
| 19 | +DtbdWNhCl/UCgYEArzReY7YUP3G7Lhb9cvXZv8hYnJN7Xqxm6PLiC0YvNaPE8W+u | ||
| 20 | +BnhmvZy5jssTM8ceQioyFQpgNqkZYpmdPsjCSRgMI2A6P2akhFGhYJvN85d3L6kd | ||
| 21 | +AGA9Mx38FkG5KwpVbKxAEQNrSEDRVzOKLqHAfZ4ivI9q7Y2/v6FdYp+MnKUCgYAe | ||
| 22 | +RXe/5Zt7vshjDPPbvMqu3WXXGI9oqNvYKjNFv9oC1E+B/0XDWVSWj70n5RGC7o6d | ||
| 23 | +WF7Yz++eEopLPj/amOs5cz8EoC1GzcmPRl9ryk9XMSUbu47LU7+EWtADLc82c941 | ||
| 24 | +U3rdZlnnEjsiB/f5+3990knmqkwU2vDXpgrI8gT3uQKBgQCP93Bcr4MQF1ogwFeI | ||
| 25 | +o/cWIMYI2vbZAo7e5X1I4JVYm0R3c7bsKJo8vrYMwKMGfGqj56iF9wduN6rKORhx | ||
| 26 | +rA/wPES4zORRum09ppk9UOR7jIVTUqbJ9qvbmwHuXn4rn1JDvmd+Pixi7e5Ftr7G | ||
| 27 | +vXP18pbM3/WexDXV7UvHrqCxxA== | ||
| 28 | +-----END PRIVATE KEY----- |
test/fixtures/apps/mocktool/server.js
0 → 100644
| 1 | +'use strict'; | ||
| 2 | + | ||
| 3 | +const http = require('http'); | ||
| 4 | +const Koa = require('koa'); | ||
| 5 | + | ||
| 6 | +const port = Number(process.argv[2]) || 8000; | ||
| 7 | + | ||
| 8 | +console.info('[server] SOCKET_SERVER: ' + process.env.SOCKET_SERVER); | ||
| 9 | + | ||
| 10 | +const app = new Koa(); | ||
| 11 | +app.use(async ctx => { | ||
| 12 | + ctx.body = 'done'; | ||
| 13 | +}); | ||
| 14 | + | ||
| 15 | +const server = http.createServer(app.callback()); | ||
| 16 | +server.once('error', err => { | ||
| 17 | + console.error('[server]', err.stack); | ||
| 18 | + process.exit(1); | ||
| 19 | +}); | ||
| 20 | +server.once('listening', () => { | ||
| 21 | + console.info('[server] listening ' + port); | ||
| 22 | +}); | ||
| 23 | + | ||
| 24 | +console.info('[server] listen ' + port); | ||
| 25 | +server.listen(port); | ||
| 26 | + | ||
| 27 | +process.once('SIGTERM', () => { | ||
| 28 | + console.log('[server] server stopped'); | ||
| 29 | + process.exit(); | ||
| 30 | +}); | ||
| 31 | + | ||
| 32 | +console.error('[server] error'); |
test/fixtures/apps/no-manifest/package.json
0 → 100644
| 1 | +'use strict'; | ||
| 2 | + | ||
| 3 | +const Controller = require('egg').Controller; | ||
| 4 | + | ||
| 5 | +class HomeController extends Controller { | ||
| 6 | + async index() { | ||
| 7 | + await this.ctx.render('index.js', { | ||
| 8 | + data: 1, | ||
| 9 | + }); | ||
| 10 | + } | ||
| 11 | + | ||
| 12 | + async renderString() { | ||
| 13 | + await this.ctx.renderString('', {}, { | ||
| 14 | + viewEngine: 'assets', | ||
| 15 | + }); | ||
| 16 | + } | ||
| 17 | +} | ||
| 18 | + | ||
| 19 | +module.exports = HomeController; |
test/fixtures/apps/not-listen/app/router.js
0 → 100644
test/fixtures/apps/not-listen/package.json
0 → 100644
| 1 | +'use strict'; | ||
| 2 | + | ||
| 3 | +const path = require('path'); | ||
| 4 | + | ||
| 5 | +exports.keys = '123456'; | ||
| 6 | +exports.view = { | ||
| 7 | + mapping: { | ||
| 8 | + '.html': 'nunjucks', | ||
| 9 | + }, | ||
| 10 | +}; | ||
| 11 | +exports.assets = { | ||
| 12 | + publicPath: '/app/public', | ||
| 13 | + devServer: { | ||
| 14 | + waitStart: true, | ||
| 15 | + command: 'node ' + path.join(__dirname, '../../mocktool/server.js'), | ||
| 16 | + port: 8000, | ||
| 17 | + env: {}, | ||
| 18 | + }, | ||
| 19 | +}; |
| 1 | +{ | ||
| 2 | + "0.a94eff34.async.js": "0.a94eff34.async.js", | ||
| 3 | + "1.0647e11d.async.js": "1.0647e11d.async.js", | ||
| 4 | + "2.cbbca76b.async.js": "2.cbbca76b.async.js", | ||
| 5 | + "constants.js": "constants.e5eb201e.js", | ||
| 6 | + "index.css": "index.b8e2efea.css", | ||
| 7 | + "index.js": "index.c4ae6394.js", | ||
| 8 | + "static/yay.jpg": "static/yay.44dd3333.jpg" | ||
| 9 | +} |
test/fixtures/apps/tool-roadhog/package.json
0 → 100644
test/fixtures/apps/ui/app/controller/home.js
0 → 100644
test/fixtures/apps/ui/app/public/index.css
0 → 100644
| 1 | +body { color: white; } |
test/fixtures/apps/ui/app/public/index.js
0 → 100644
test/fixtures/apps/ui/app/router.js
0 → 100644
test/fixtures/apps/ui/app/view/index.js
0 → 100644
test/fixtures/apps/ui/app/view/layout.html
0 → 100644
| 1 | +'use strict'; | ||
| 2 | + | ||
| 3 | +const path = require('path'); | ||
| 4 | + | ||
| 5 | +exports.keys = '123456'; | ||
| 6 | +exports.view = { | ||
| 7 | + mapping: { | ||
| 8 | + '.js': 'assets', | ||
| 9 | + '.jsx': 'assets', | ||
| 10 | + }, | ||
| 11 | +}; | ||
| 12 | +exports.assets = { | ||
| 13 | + publicPath: '/public/', | ||
| 14 | + templateViewEngine: 'nunjucks', | ||
| 15 | + templatePath: path.join(__dirname, '../app/view/layout.html'), | ||
| 16 | +}; |
test/fixtures/apps/ui/config/manifest.json
0 → 100644
test/fixtures/apps/ui/config/plugin.js
0 → 100644
test/fixtures/apps/ui/package.json
0 → 100644
test/roadhog.test.js
0 → 100644
| 1 | +'use strict'; | ||
| 2 | + | ||
| 3 | +const mock = require('egg-mock'); | ||
| 4 | +const request = require('supertest'); | ||
| 5 | +const sleep = require('mz-modules/sleep'); | ||
| 6 | + | ||
| 7 | +describe.skip('roadhog', () => { | ||
| 8 | + let app; | ||
| 9 | + before(() => { | ||
| 10 | + mock.env('local'); | ||
| 11 | + app = mock.cluster({ | ||
| 12 | + baseDir: 'apps/tool-roadhog', | ||
| 13 | + }); | ||
| 14 | + return app.ready(); | ||
| 15 | + }); | ||
| 16 | + after(() => app.close()); | ||
| 17 | + | ||
| 18 | + it('should GET /', async () => { | ||
| 19 | + await app.httpRequest() | ||
| 20 | + .get('/') | ||
| 21 | + .expect(res => { | ||
| 22 | + res.text.includes('<link rel="stylesheet" href="http://127.0.0.1:8000/index.css"></link>'); | ||
| 23 | + res.text.includes('<script src="http://127.0.0.1:8000/index.js"></script>'); | ||
| 24 | + // res.text.includes('<script>window.context={}</script>'); | ||
| 25 | + }) | ||
| 26 | + .expect(200); | ||
| 27 | + | ||
| 28 | + await sleep(10000); | ||
| 29 | + | ||
| 30 | + await request('http://127.0.0.1:8000') | ||
| 31 | + .get('index.js') | ||
| 32 | + .expect(res => { | ||
| 33 | + console.log(res); | ||
| 34 | + }); | ||
| 35 | + }); | ||
| 36 | +}); |
test/ui.test.js
0 → 100644
| 1 | +'use strict'; | ||
| 2 | + | ||
| 3 | +const mock = require('egg-mock'); | ||
| 4 | +const puppeteer = require('puppeteer'); | ||
| 5 | +const sleep = require('mz-modules/sleep'); | ||
| 6 | +const assert = require('assert'); | ||
| 7 | + | ||
| 8 | +describe('test/ui.test.js', () => { | ||
| 9 | + let app; | ||
| 10 | + before(() => { | ||
| 11 | + mock.env('default'); | ||
| 12 | + app = mock.cluster({ | ||
| 13 | + baseDir: 'apps/ui', | ||
| 14 | + port: 7001, | ||
| 15 | + }); | ||
| 16 | + app.debug(); | ||
| 17 | + return app.ready(); | ||
| 18 | + }); | ||
| 19 | + after(() => app.close()); | ||
| 20 | + after(mock.restore); | ||
| 21 | + | ||
| 22 | + it('should request index.js', async () => { | ||
| 23 | + await app.httpRequest() | ||
| 24 | + .get('/public/index.js') | ||
| 25 | + .expect(/window.context.data/) | ||
| 26 | + .expect(200); | ||
| 27 | + }); | ||
| 28 | + | ||
| 29 | + it('should render html', async () => { | ||
| 30 | + await app.httpRequest() | ||
| 31 | + .get('/') | ||
| 32 | + .expect(res => { | ||
| 33 | + assert(res.text.includes('<script>(function(){window.context = JSON.parse(decodeURIComponent("%7B%22data%22%3A1%7D"));})()<\/script>')); | ||
| 34 | + }) | ||
| 35 | + .expect(200); | ||
| 36 | + }); | ||
| 37 | + | ||
| 38 | + it('should console', async () => { | ||
| 39 | + const browser = await puppeteer.launch({ | ||
| 40 | + args: [ '--no-sandbox', '--disable-setuid-sandbox' ], | ||
| 41 | + }); | ||
| 42 | + const page = await browser.newPage(); | ||
| 43 | + let text = ''; | ||
| 44 | + page.on('console', msg => (text += msg.text())); | ||
| 45 | + await page.goto('http://127.0.0.1:7001'); | ||
| 46 | + | ||
| 47 | + await sleep(5000); | ||
| 48 | + assert(text === 'data: 1'); | ||
| 49 | + }); | ||
| 50 | + | ||
| 51 | +}); |
请
注册
或
登录
后发表评论