提交 c76c029cafa22bfe8d9b50964e7a03b5b240d62b

作者 fanwh
0 个父辈

no message

正在显示 100 个修改的文件 包含 2379 行增加0 行删除

要显示太多修改。

为保证性能只显示 100 of 100+ 个文件。

  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 +};
... ...
  1 +coverage
  2 +lib/fixtures
... ...
  1 +{
  2 + "extends": "eslint-config-egg"
  3 +}
... ...
  1 +logs/
  2 +npm-debug.log
  3 +node_modules/
  4 +coverage/
  5 +.idea/
  6 +run/
  7 +.DS_Store
  8 +*.swp
  9 +test/fixtures/apps/assets-template/app/view/cache.html
... ...
  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 +
... ...
  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.
... ...
  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
... ...
  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 +}
... ...
  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 +};
... ...
  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 +};
... ...
  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
... ...
  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 +});
... ...
  1 +'use strict';
  2 +
  3 +exports.assets = {
  4 + devServer: {
  5 + enable: false,
  6 + waitStart: true,
  7 + },
  8 +};
... ...
  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 +}
... ...
  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;
... ...
  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;
... ...
  1 +'use strict';
  2 +
  3 +exports.HELPER_ASSETS = Symbol('Helper#assets');
... ...
  1 +'use strict';
  2 +
  3 +module.exports = assets => {
  4 + return `
  5 + <!doctype html>
  6 + <html>
  7 + <head>
  8 + ${assets.getStyle()}
  9 + </head>
  10 + <body>
  11 + <div id="root"></div>
  12 + ${assets.getContext()}
  13 + ${assets.getScript()}
  14 + </body>
  15 + </html>
  16 + `;
  17 +};
... ...
  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 +}
... ...
  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&#39;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 +});
... ...
  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 +module.exports = {
  4 + get cspnonce() {
  5 + return 'cspnonce';
  6 + },
  7 +};
... ...
  1 +'use strict';
  2 +
  3 +module.exports = app => {
  4 + const { router, controller } = app;
  5 +
  6 + router.get('/', controller.home.index);
  7 + router.get('/account', controller.home.account);
  8 + router.get('/renderString', controller.home.renderString);
  9 +};
... ...
  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 +'use strict';
  2 +
  3 +exports.assets = {
  4 + url: 'http://cdn.com',
  5 +};
... ...
  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 +{
  2 + "name": "egg-view-assets"
  3 +}
... ...
  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 +<!doctype html>
  2 +<html>
  3 + <head>
  4 + <%- helper.assets.getStyle() %>
  5 + </head>
  6 + <body>
  7 + <div id="root"></div>
  8 + <%- helper.assets.getContext() %>
  9 + <%- helper.assets.getScript() %>
  10 + </body>
  11 +</html>
... ...
  1 +<!doctype html>
  2 +<html>
  3 + <head>
  4 + {{ helper.assets.getStyle() | safe }}
  5 + </head>
  6 + <body>
  7 + <div id="root"></div>
  8 + {{ helper.assets.getContext() | safe }}
  9 + {{ helper.assets.getScript() | safe }}
  10 + </body>
  11 +</html>
... ...
  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 +'use strict';
  2 +
  3 +module.exports = {
  4 + assets: {
  5 + url: 'http://cdn.com',
  6 + },
  7 +};
... ...
  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 +module.exports = {
  4 + nunjucks: {
  5 + enable: true,
  6 + package: 'egg-view-nunjucks',
  7 + },
  8 + ejs: {
  9 + enable: true,
  10 + package: 'egg-view-ejs',
  11 + },
  12 +};
... ...
  1 +{
  2 + "name": "egg-view-assets"
  3 +}
... ...
  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 +module.exports = app => {
  4 + const { router, controller } = app;
  5 +
  6 + router.get('/', controller.home.index);
  7 + router.get('/account', controller.home.account);
  8 + router.get('/renderString', controller.home.renderString);
  9 +};
... ...
  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 +'use strict';
  2 +
  3 +exports.assets = {
  4 + url: 'http://cdn.com',
  5 +};
... ...
  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 +{
  2 + "name": "egg-view-assets"
  3 +}
... ...
  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 +module.exports = app => {
  4 + const { router, controller } = app;
  5 +
  6 + router.get('/', controller.home.index);
  7 + router.get('/port', controller.home.port);
  8 +};
... ...
  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 +{
  2 + "name": "egg-view-assets"
  3 +}
... ...
  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 +module.exports = app => {
  4 + const { router, controller } = app;
  5 +
  6 + router.get('/', controller.home.index);
  7 + router.get('/port', controller.home.port);
  8 +};
... ...
  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 +};
... ...
  1 +{
  2 + "name": "egg-view-assets"
  3 +}
... ...
  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 +
  11 +module.exports = HomeController;
... ...
  1 +'use strict';
  2 +
  3 +module.exports = app => {
  4 + const { router, controller } = app;
  5 +
  6 + router.get('/', controller.home.index);
  7 +};
... ...
  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: 'unknown command',
  13 + port: 8000,
  14 + debug: true,
  15 + timeout: 5000,
  16 + },
  17 +};
... ...
  1 +{
  2 + "name": "egg-view-assets"
  3 +}
... ...
  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 +
  13 +module.exports = HomeController;
... ...
  1 +body { color: white; }
\ No newline at end of file
... ...
  1 +'use strict';
  2 +
  3 +/* global window */
  4 +console.log('data:', window.context.data);
... ...
  1 +'use strict';
  2 +
  3 +module.exports = app => {
  4 + const { router, controller } = app;
  5 +
  6 + router.get('/', controller.home.index);
  7 +};
... ...
  1 +<!doctype html>
  2 +<html>
  3 + <head>
  4 + {{ helper.assets.getStyle() | safe }}
  5 + </head>
  6 + <body>
  7 + <div id="root"></div>
  8 + <script>window.atob = undefined;</script>
  9 + {{ helper.assets.getContext() | safe }}
  10 + {{ helper.assets.getScript() | safe }}
  11 + </body>
  12 +</html>
... ...
  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 +{
  2 + "index.js": "index.js",
  3 + "index.css": "/index.css",
  4 + "page1.js": "http://cdn.com/page1.js"
  5 +}
... ...
  1 +'use strict';
  2 +
  3 +module.exports = {
  4 + nunjucks: {
  5 + enable: true,
  6 + package: 'egg-view-nunjucks',
  7 + },
  8 +};
... ...
  1 +{
  2 + "name": "egg-view-assets"
  3 +}
... ...
  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 + query: this.ctx.query.query,
  9 + });
  10 + }
  11 +}
  12 +
  13 +module.exports = HomeController;
... ...
  1 +'use strict';
  2 +
  3 +module.exports = app => {
  4 + const { router, controller } = app;
  5 +
  6 + router.get('/', controller.home.index);
  7 +};
... ...
  1 +'use strict';
  2 +
  3 +exports.keys = '123456';
  4 +exports.view = {
  5 + mapping: {
  6 + '.js': 'assets',
  7 + },
  8 +};
  9 +exports.assets = {
  10 +};
... ...
  1 +{
  2 + "index.css": "index.css",
  3 + "index.js": "index.js"
  4 +}
... ...
  1 +{
  2 + "name": "egg-view-assets"
  3 +}
... ...
  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 +
  13 +module.exports = HomeController;
... ...
  1 +body { color: white; }
\ No newline at end of file
... ...
  1 +'use strict';
  2 +
  3 +/* global window */
  4 +console.log('data:', window.context.data);
... ...
  1 +'use strict';
  2 +
  3 +module.exports = app => {
  4 + const { router, controller } = app;
  5 +
  6 + router.get('/', controller.home.index);
  7 +};
... ...
  1 +<!doctype html>
  2 +<html>
  3 + <head>
  4 + {{ helper.assets.getStyle() | safe }}
  5 + </head>
  6 + <body>
  7 + <div id="root"></div>
  8 + <script>window.atob = undefined;</script>
  9 + {{ helper.assets.getContext() | safe }}
  10 + {{ helper.assets.getScript() | safe }}
  11 + </body>
  12 +</html>
... ...
  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 +};
... ...
  1 +{
  2 + "index.js": "/index.js",
  3 + "index.css": "/index.css"
  4 +}
... ...
  1 +'use strict';
  2 +
  3 +module.exports = {
  4 + nunjucks: {
  5 + enable: true,
  6 + package: 'egg-view-nunjucks',
  7 + },
  8 +};
... ...
  1 +{
  2 + "name": "crossorigin"
  3 +}
... ...
  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 +module.exports = app => {
  4 + const { router, controller } = app;
  5 +
  6 + router.get('/', controller.home.index);
  7 + router.get('/context', controller.home.context);
  8 +};
... ...
  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 +'use strict';
  2 +
  3 +exports.assets = {
  4 + url: 'http://localhost',
  5 +};
... ...
  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 +{
  2 + "name": "egg-view-assets"
  3 +}
... ...
  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 +
  11 +module.exports = HomeController;
... ...
  1 +'use strict';
  2 +
  3 +module.exports = app => {
  4 + const { router, controller } = app;
  5 +
  6 + router.get('/', controller.home.index);
  7 +};
... ...
  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 +};
... ...
注册登录 后发表评论