├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── no-response.yml ├── pull_request_template.md └── workflows │ ├── cov.yml │ ├── release.yml │ ├── testOnLinux.yml │ ├── testOnMac.yml │ └── testOnWin.yml ├── .gitignore ├── .npmrc ├── CHANGELOG.md ├── LICENSE ├── README.md ├── README.zh-CN.md ├── codecov.yml ├── commitlint.config.js ├── examples ├── eruda │ └── README.md ├── portal │ └── README.md ├── proxy │ ├── .svrxrc.js │ ├── README.md │ └── public │ │ ├── index.html │ │ └── main.js ├── serve-static-page │ ├── .svrxrc.js │ ├── README.md │ └── public │ │ ├── index.html │ │ └── main.js └── weinre │ └── README.md ├── lerna.json ├── package.json └── packages ├── svrx-util ├── .npmrc ├── README.md ├── __tests__ │ ├── fixture │ │ ├── .svrx │ │ │ ├── config │ │ │ │ └── .svrxrc.js │ │ │ ├── plugins │ │ │ │ └── hello │ │ │ │ │ ├── 0.0.5 │ │ │ │ │ ├── index.js │ │ │ │ │ └── package.json │ │ │ │ │ ├── 1.0.0 │ │ │ │ │ ├── index.js │ │ │ │ │ └── package.json │ │ │ │ │ └── 1.0.1 │ │ │ │ │ ├── index.js │ │ │ │ │ └── package.json │ │ │ └── versions │ │ │ │ └── 1.0.6 │ │ │ │ ├── index.js │ │ │ │ ├── lib │ │ │ │ └── svrx.js │ │ │ │ └── package.json │ │ └── plugin │ │ │ ├── svrx-plugin-error-no-version │ │ │ ├── index.js │ │ │ └── package.json │ │ │ ├── svrx-plugin-no-package │ │ │ └── index.js │ │ │ └── svrx-plugin-test │ │ │ ├── assets │ │ │ ├── index.css │ │ │ └── index.js │ │ │ ├── index.js │ │ │ └── package.json │ ├── spec │ │ ├── logger.test.js │ │ ├── name-formatter.test.js │ │ ├── package-manager.test.js │ │ └── rc-file-read.test.js │ └── svrx-util.test.js ├── lib │ ├── c2k.js │ ├── logger.js │ ├── name-formatter.js │ ├── npCall │ │ └── index.js │ ├── package-manager │ │ ├── index.js │ │ ├── package-manager.js │ │ └── plugin-package-manager.js │ ├── rc-file-read.js │ └── svrx-util.js └── package.json └── svrx ├── .npmrc ├── README.md ├── __tests__ ├── fixture │ ├── plugin │ │ ├── historyApiFallback │ │ │ └── index.html │ │ ├── serve │ │ │ ├── demo.html │ │ │ ├── demo.js │ │ │ └── demo.png │ │ ├── svrx-plugin-depend │ │ │ ├── index.js │ │ │ └── package.json │ │ ├── svrx-plugin-no-package │ │ │ └── index.js │ │ └── svrx-plugin-test │ │ │ ├── assets │ │ │ ├── index.css │ │ │ └── index.js │ │ │ ├── index.js │ │ │ └── package.json │ └── router │ │ ├── rule.normal.js │ │ ├── rule.require.js │ │ └── static │ │ └── index.html ├── spec │ ├── configure │ │ ├── builtinConfig.js │ │ ├── cli.js │ │ ├── inline.js │ │ ├── pluginConfig.js │ │ └── ui.js │ ├── svrx.base.js │ ├── svrx.injector.js │ ├── svrx.io.js │ ├── svrx.plugin.js │ ├── svrx.plugin.proxy.js │ ├── svrx.router.js │ └── svrx.utility.js └── util.js ├── index.js ├── lib ├── config-list.js ├── configure │ ├── builtinOption.js │ ├── index.js │ ├── option.js │ ├── plugin.js │ ├── pluginInfo.js │ └── pluginOption.js ├── constant.js ├── injector │ ├── client │ │ └── index.js │ ├── index.js │ └── replace.js ├── io │ ├── client.js │ └── index.js ├── manager.js ├── middleware │ └── index.js ├── model.js ├── plugin │ ├── loader.js │ ├── svrx-plugin-cors │ │ └── index.js │ ├── svrx-plugin-history-api-fallback │ │ └── index.js │ ├── svrx-plugin-livereload │ │ ├── assets │ │ │ └── index.js │ │ └── index.js │ ├── svrx-plugin-open │ │ └── index.js │ ├── svrx-plugin-proxy │ │ └── index.js │ ├── svrx-plugin-serve │ │ └── index.js │ └── system.js ├── router │ ├── actions.js │ ├── index.js │ ├── loader.js │ ├── methods.js │ ├── route.js │ └── router.js ├── shared │ ├── cache.js │ ├── consts.js │ ├── events.js │ └── uid.js ├── svrx.js └── util │ ├── compose.js │ ├── gzip.js │ ├── helper.js │ ├── hmr.js │ ├── im.js │ ├── jsonSchemaDefaults.js │ ├── logger.js │ └── semver.js ├── package.json ├── resource └── cert │ ├── rootCA.crt │ └── rootCA.key └── scripts ├── gen.cert.js └── webpack.injector.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 2 9 | end_of_line = lf 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [*.md] 14 | insert_final_newline = false 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | packages/**/assets/**/*.js 3 | packages/**/dist/**/*.js 4 | /**/fixture 5 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@svrx", 3 | "overrides": [ 4 | { 5 | "files": "examples/**/*.js", 6 | "rules": { 7 | "no-unused-vars": "off" 8 | } 9 | }, { 10 | "files": "packages/**/__tests__/**/*.js", 11 | "rules": { 12 | "import/no-extraneous-dependencies": "off" 13 | } 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | 28 | 29 | - OS: [e.g. iOS] 30 | - Node: [e.g. 10.0.0, 12.0.0] 31 | - Browser: [e.g. chrome, safari] 32 | - Version: [e.g. 22] 33 | 34 | **Additional context** 35 | Add any other context about the problem here. 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/no-response.yml: -------------------------------------------------------------------------------- 1 | # Configuration for probot-no-response - https://github.com/probot/no-response 2 | 3 | # Number of days of inactivity before an Issue is closed for lack of response 4 | daysUntilClose: 14 5 | # Label requiring a response 6 | responseRequiredLabel: more-information-needed 7 | # Comment to post when closing an Issue for lack of response. Set to `false` to disable 8 | closeComment: > 9 | This issue has been automatically closed because there has been no response 10 | to our request for more information from the original author. With only the 11 | information that is currently in the issue, we don't have enough information 12 | to take action. Please reach out if you have or find the answers we need so 13 | that we can investigate further. 14 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Please check if the PR fulfills these requirements 4 | 5 | - [ ] The commit message follows our guidelines 6 | - [x] Tests is needed? 7 | - [ ] Tests for the changes have been added 8 | - [x] Docs is needed? 9 | - [ ] Docs have been added / updated 10 | 11 | ## What kind of change does this PR introduce? 12 | 13 | - [ ] Bug fix 14 | - [ ] Feature 15 | - [ ] Enhencement 16 | - [ ] Refactor 17 | - [ ] Documents 18 | - [ ] Others 19 | 20 | 21 | 22 | 1. 23 | 1. 24 | 25 | ## What is the related issue? 26 | 27 | 28 | 29 | 30 | ## Does this PR introduce a breaking change? 31 | 32 | - [ ] breaking change? 33 | 34 | 35 | 36 | ## Other information: 37 | -------------------------------------------------------------------------------- /.github/workflows/cov.yml: -------------------------------------------------------------------------------- 1 | name: cov 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: Use Node.js 16 | uses: actions/setup-node@v1 17 | with: 18 | node-version: '12.x' 19 | - run: npm install 20 | - run: npm run build --if-present 21 | - run: npm test 22 | - run: npm run report-coverage 23 | 24 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release to npm 2 | on: 3 | push: 4 | branches: 5 | - 'release/**' 6 | jobs: 7 | build: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | with: 12 | fetch-depth: 0 # to fetch git tags 13 | # Setup .npmrc file to publish to npm 14 | - uses: actions/setup-node@v1 15 | with: 16 | node-version: '12.x' 17 | registry-url: 'https://registry.npmjs.org' 18 | - run: npm install 19 | - run: npm run build 20 | - run: npm run lerna:publish 21 | env: 22 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 23 | -------------------------------------------------------------------------------- /.github/workflows/testOnLinux.yml: -------------------------------------------------------------------------------- 1 | name: test on linux 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: ubuntu-latest 12 | 13 | strategy: 14 | matrix: 15 | node-version: [8.9.x, 12.x, 15.x] 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Use Node.js ${{ matrix.node-version }} 20 | uses: actions/setup-node@v1 21 | with: 22 | node-version: ${{ matrix.node-version }} 23 | - run: npm install 24 | - run: npm run build --if-present 25 | - run: npm test 26 | -------------------------------------------------------------------------------- /.github/workflows/testOnMac.yml: -------------------------------------------------------------------------------- 1 | name: test on macos 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: macos-latest 12 | 13 | strategy: 14 | matrix: 15 | node-version: [8.9.x, 12.x, 15.x] 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Use Node.js ${{ matrix.node-version }} 20 | uses: actions/setup-node@v1 21 | with: 22 | node-version: ${{ matrix.node-version }} 23 | - run: npm install 24 | - run: npm run build --if-present 25 | - run: npm test 26 | -------------------------------------------------------------------------------- /.github/workflows/testOnWin.yml: -------------------------------------------------------------------------------- 1 | name: test on windows 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: windows-latest 12 | 13 | strategy: 14 | matrix: 15 | node-version: [8.9.x, 12.x, 15.x] 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Use Node.js ${{ matrix.node-version }} 20 | uses: actions/setup-node@v1 21 | with: 22 | node-version: ${{ matrix.node-version }} 23 | - run: npm install 24 | - run: npm run build --if-present 25 | - run: npm test 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | coverage.lcov 2 | package-lock.json 3 | npm-shrinkwrap.json 4 | 5 | # Created by https://www.gitignore.io/api/node 6 | # Edit at https://www.gitignore.io/?templates=node 7 | 8 | ### Node ### 9 | # Logs 10 | logs 11 | *.log 12 | npm-debug.log* 13 | yarn-debug.log* 14 | yarn-error.log* 15 | 16 | # Runtime data 17 | pids 18 | *.pid 19 | *.seed 20 | *.pid.lock 21 | 22 | # Directory for instrumented libs generated by jscoverage/JSCover 23 | lib-cov 24 | 25 | # Coverage directory used by tools like istanbul 26 | coverage 27 | 28 | # nyc test coverage 29 | .nyc_output 30 | 31 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 32 | .grunt 33 | 34 | # Bower dependency directory (https://bower.io/) 35 | bower_components 36 | 37 | # node-waf configuration 38 | .lock-wscript 39 | 40 | # Compiled binary addons (https://nodejs.org/api/addons.html) 41 | build/Release 42 | 43 | # Dependency directories 44 | node_modules/ 45 | jspm_packages/ 46 | 47 | # TypeScript v1 declaration files 48 | typings/ 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Optional REPL history 57 | .node_repl_history 58 | 59 | # Output of 'npm pack' 60 | *.tgz 61 | 62 | # Yarn Integrity file 63 | .yarn-integrity 64 | 65 | # dotenv environment variables file 66 | .env 67 | .env.test 68 | 69 | # parcel-bundler cache (https://parceljs.org/) 70 | .cache 71 | 72 | # next.js build output 73 | .next 74 | 75 | # nuxt.js build output 76 | .nuxt 77 | 78 | # vuepress build output 79 | .vuepress/dist 80 | 81 | # Serverless directories 82 | .serverless/ 83 | 84 | # FuseBox cache 85 | .fusebox/ 86 | 87 | # DynamoDB Local files 88 | .dynamodb/ 89 | 90 | # End of https://www.gitignore.io/api/node 91 | 92 | tmp/ 93 | 94 | .DS_Store 95 | .changelog 96 | .vscode 97 | .idea 98 | 99 | # ignore dist 100 | 101 | dist 102 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## v1.1.7 (2021-04-22) 2 | 3 | #### :bug: Bug Fix 4 | * `svrx-util` 5 | * Fix package install error on Windows ([@xuchaoying](https://github.com/xuchaoying)) 6 | 7 | ## v1.1.6 (2021-03-25) 8 | 9 | #### :bug: Bug Fix 10 | * `svrx` 11 | * [#195](https://github.com/svrxjs/svrx/pull/195) Fix start error on node@15, use child_process to exec npm commands directly ([@xuchaoying](https://github.com/xuchaoying)) 12 | 13 | ## v1.1.5 (2020-04-13) 14 | 15 | #### :rocket: New Feature 16 | * `svrx` 17 | * [#178](https://github.com/svrxjs/svrx/pull/178) Support lib require in route file ([@xuchaoying](https://github.com/xuchaoying)) 18 | 19 | #### :bug: Bug Fix 20 | * `svrx` 21 | * [#177](https://github.com/svrxjs/svrx/pull/177) Fix enable dashed plugins through cli shortcut ([@xuchaoying](https://github.com/xuchaoying)) 22 | 23 | ## v1.1.4 (2019-12-24) 24 | 25 | #### :rocket: New Feature 26 | * `svrx` 27 | * [#168](https://github.com/svrxjs/svrx/pull/168) Support svrx-plugin-ui ([@xuchaoying](https://github.com/xuchaoying)) 28 | 29 | #### :bug: Bug Fix 30 | * `svrx` 31 | * [#173](https://github.com/svrxjs/svrx/pull/173) Fix the priority of cors and route ([@xuchaoying](https://github.com/xuchaoying)) 32 | * [#167](https://github.com/svrxjs/svrx/pull/167) Fix `window.__svrx__` undefined when using require.js ([@xuchaoying](https://github.com/xuchaoying)) 33 | * `svrx-util`, `svrx` 34 | * [#165](https://github.com/svrxjs/svrx/pull/165) Revert global-npm to 0.3.0 ([@xuchaoying](https://github.com/xuchaoying)) 35 | 36 | #### :nail_care: Enhancement 37 | * `svrx-util` 38 | * [#174](https://github.com/svrxjs/svrx/pull/174) Put auto update package after local package load ([@xuchaoying](https://github.com/xuchaoying)) 39 | 40 | ## v1.1.3 (2019-12-02) 41 | 42 | #### :bug: Bug Fix 43 | * `svrx-util` 44 | * [#165](https://github.com/svrxjs/svrx/pull/165) Fix `Error: Cannot find module 'npm'` when starting svrx core from npm scripts ([@xuchaoying](https://github.com/xuchaoying)) 45 | 46 | ## v1.1.2 (2019-12-02) 47 | 48 | ## v1.1.1 (2019-11-28) 49 | 50 | #### :rocket: New Feature 51 | * `svrx-util` 52 | * [#159](https://github.com/svrxjs/svrx/pull/159) Add autoclean to package-manager ([@xuchaoying](https://github.com/xuchaoying)) 53 | 54 | ## v1.1.0 (2019-11-18) 55 | 56 | #### :rocket: New Feature 57 | * `svrx-util`, `svrx` 58 | * [#152](https://github.com/svrxjs/svrx/pull/152) Change plugin install strategy to global install ([@xuchaoying](https://github.com/xuchaoying)) 59 | 60 | Now the default path for plugin packages is changed from local `node_modules` in your project to `~/.svrx/plugins`. 61 | svrx still supports local plugin install, you can use `npm install --save-dev svrx-plugin-name` to save a plugin to your working directory. 62 | 63 | #### :bug: Bug Fix 64 | * `svrx` 65 | * [#151](https://github.com/svrxjs/svrx/pull/151) Upgrade the priority of built-in api ([@leeluolee](https://github.com/leeluolee)) 66 | 67 | ## v1.0.7 (2019-11-08) 68 | 69 | #### :bug: Bug Fix 70 | * `svrx-util` 71 | * [#148](https://github.com/svrxjs/svrx/pull/148) Replace global-npm.install with npminstall ([@xuchaoying](https://github.com/xuchaoying)) 72 | 73 | ## v1.0.6 (2019-11-01) 74 | 75 | #### :bug: Bug Fix 76 | * `svrx` 77 | * [#145](https://github.com/svrxjs/svrx/pull/145) Fix windows path parse error ([@int64ago](https://github.com/int64ago)) 78 | * [#143](https://github.com/svrxjs/svrx/pull/143) Fix livereload client error ([@leeluolee](https://github.com/leeluolee)) 79 | 80 | ## v1.0.5 (2019-10-29) 81 | 82 | #### :bug: Bug Fix 83 | * `svrx-util`, `svrx` 84 | * [#135](https://github.com/svrxjs/svrx/pull/135) Fix not working on Windows ([@xuchaoying](https://github.com/xuchaoying)) 85 | * `svrx-util` 86 | * [#134](https://github.com/svrxjs/svrx/pull/134) Fix wrong log display for scoped plugin ([@int64ago](https://github.com/int64ago)) 87 | * [#129](https://github.com/svrxjs/svrx/pull/129) Prevent modify package.json when plugin installation ([@xuchaoying](https://github.com/xuchaoying)) 88 | * `svrx` 89 | * [#132](https://github.com/svrxjs/svrx/pull/132) Fix process exit not working on Windows ([@xuchaoying](https://github.com/xuchaoying)) 90 | 91 | ## v1.0.4 (2019-10-18) 92 | 93 | #### :bug: Bug Fix 94 | * `svrx` 95 | * [#112](https://github.com/svrxjs/svrx/pull/112) Fix css is not appended if there's no
tag ([@leeluolee](https://github.com/leeluolee)) 96 | * [#119](https://github.com/svrxjs/svrx/pull/119) Fix routing update not working ([@leeluolee](https://github.com/leeluolee)) 97 | * [#120](https://github.com/svrxjs/svrx/pull/120) Fix scripts injecting bug ([@leeluolee](https://github.com/leeluolee)) 98 | 99 | ## v1.0.3 (2019-10-11) 100 | 101 | #### :rocket: New Feature 102 | * `svrx-util`, `svrx` 103 | * [#110](https://github.com/svrxjs/svrx/pull/110) Add watch(), del(), splice() to config ([@xuchaoying](https://github.com/xuchaoying)) 104 | 105 | ## v1.0.0 (2019-09-20) 106 | 107 | #### :rocket: New Feature 108 | * `svrx` 109 | * [#90](https://github.com/svrxjs/svrx/pull/90) Add plublic events ([@leeluolee](https://github.com/leeluolee)) 110 | * [#86](https://github.com/svrxjs/svrx/pull/86) Alias registService to regist ([@leeluolee](https://github.com/leeluolee)) 111 | 112 | #### :bug: Bug Fix 113 | * `svrx` 114 | * [#84](https://github.com/svrxjs/svrx/pull/84) Fix dashed plugin name parse error ([@xuchaoying](https://github.com/xuchaoying)) 115 | * `svrx-util`, `svrx` 116 | * [#83](https://github.com/svrxjs/svrx/pull/83) Return the right package info after npmi ([@xuchaoying](https://github.com/xuchaoying)) 117 | 118 | ## 0.0.9 (2019-09-04) 119 | 120 | ## 0.0.8 (2019-09-04) 121 | 122 | #### :bug: Bug Fix 123 | * `svrx` 124 | * [#77](https://github.com/svrxjs/svrx/pull/77) Fix number check for option value ([@xuchaoying](https://github.com/xuchaoying)) 125 | 126 | ## v0.0.7 (2019-08-26) 127 | 128 | #### :boom: Breaking Change 129 | * `svrx-util`, `svrx` 130 | * [#66](https://github.com/svrxjs/svrx/pull/66) Set default value of proxy.changeOrigin to true ([@xuchaoying](https://github.com/xuchaoying)) 131 | 132 | #### :bug: Bug Fix 133 | * `svrx` 134 | * [#26](https://github.com/svrxjs/svrx/pull/26) Fix parse plugin querystring with dot string ([@xuchaoying](https://github.com/xuchaoying)) 135 | * [#27](https://github.com/svrxjs/svrx/pull/27) Fix router not work when historyfallback set to true ([@xuchaoying](https://github.com/xuchaoying)) 136 | 137 | #### :nail_care: Enhancement 138 | * `svrx-util`, `svrx` 139 | * [#28](https://github.com/svrxjs/svrx/pull/28) Enable multi-process asynchronous plugin installation ([@leeluolee](https://github.com/leeluolee)) 140 | * `svrx` 141 | * [#25](https://github.com/svrxjs/svrx/pull/25) Support relative path to open plugin ([@leeluolee](https://github.com/leeluolee)) 142 | 143 | ## v0.0.6 (2019-07-30) 144 | 145 | #### :bug: Bug Fix 146 | * `svrx` 147 | * [#22](https://github.com/svrxjs/svrx/pull/22) Fix config.get() to get all default values ([@xuchaoying](https://github.com/xuchaoying)) 148 | 149 | #### :nail_care: Enhancement 150 | * `svrx` 151 | * [#23](https://github.com/svrxjs/svrx/pull/23) Add plugin version match check ([@xuchaoying](https://github.com/xuchaoying)) 152 | 153 | ## v0.0.5 (2019-07-25) 154 | 155 | #### :bug: Bug Fix 156 | * `svrx` 157 | * [#17](https://github.com/svrxjs/svrx/pull/17) Set charset for injected script ([@xuchaoying](https://github.com/xuchaoying)) 158 | * [#19](https://github.com/svrxjs/svrx/pull/19) Fix https not working ([@leeluolee](https://github.com/leeluolee)) 159 | 160 | ## v0.0.4 (2019-07-16) 161 | 162 | #### :rocket: New Feature 163 | * `svrx` 164 | * [#12](https://github.com/svrxjs/svrx/pull/12) Add proxy action ([@xuchaoying](https://github.com/xuchaoying)) 165 | * [#11](https://github.com/svrxjs/svrx/pull/11) Add --plugin to define plugin and options ([@xuchaoying](https://github.com/xuchaoying)) 166 | 167 | #### :nail_care: Enhancement 168 | * `svrx` 169 | * [#9](https://github.com/svrxjs/svrx/pull/9) Add global rc config ([@xuchaoying](https://github.com/xuchaoying)) 170 | * `svrx` 171 | * [#8](https://github.com/svrxjs/svrx/pull/8) Enhance help print ([@xuchaoying](https://github.com/xuchaoying)) 172 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 NetEase, Inc. and its affiliates. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |
3 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
2 |
3 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
hello world!
9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /examples/serve-static-page/public/main.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-alert */ 2 | alert('hello!'); 3 | -------------------------------------------------------------------------------- /examples/weinre/README.md: -------------------------------------------------------------------------------- 1 | # svrx-plugin-weinre 2 | 3 | The weinre plugin for svrx 4 | 5 | ```bash 6 | svrx --weinre 7 | ``` 8 | 9 |  10 | 11 | visit http://localhost:8001 12 | 13 |  14 | 15 |  16 | 17 | 18 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": [ 3 | "packages/*" 4 | ], 5 | "version": "1.1.7", 6 | "npmClient": "npm", 7 | "changelog": { 8 | "repo": "svrxjs/svrx", 9 | "labels": { 10 | "PR: new feature": ":rocket: New Feature", 11 | "PR: breaking change": ":boom: Breaking Change", 12 | "PR: bug fix": ":bug: Bug Fix", 13 | "PR: enhancement": ":nail_care: Enhancement" 14 | }, 15 | "cacheDir": ".changelog" 16 | }, 17 | "command": { 18 | "version": { 19 | "message": "chore(release): publish %s" 20 | }, 21 | "publish": { 22 | "ignoreChanges": [ 23 | "ignored-file", 24 | "*.md", 25 | "*.txt", 26 | "__tests__/**" 27 | ] 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "root", 3 | "private": true, 4 | "scripts": { 5 | "build": "lerna run build", 6 | "lint": "run-p lint:*", 7 | "lint:js": "eslint \"**/*.js\"", 8 | "fix": "run-p fix:*", 9 | "fix:js": "eslint \"**/*.js\" --fix", 10 | "test": "lerna run test", 11 | "clean": "run-s clean:*", 12 | "clean:dependency": "lerna clean -y && rimraf -rf node_modules", 13 | "report-coverage": "lerna run report-coverage", 14 | "lerna:version": "lerna version", 15 | "lerna:publish": "lerna publish from-git --yes", 16 | "postinstall": "lerna exec -- npm install", 17 | "prepare": "npm run build", 18 | "prepublishOnly": "lerna exec -- npm prune --production && lerna exec -- npm shrinkwrap" 19 | }, 20 | "devDependencies": { 21 | "@commitlint/cli": "^8.2.0", 22 | "@commitlint/config-conventional": "^8.2.0", 23 | "@svrx/eslint-config": "^1.0.0", 24 | "codecov": "^3.6.1", 25 | "eslint": "^6.4.0", 26 | "expect.js": "^0.3.1", 27 | "husky": "^3.0.5", 28 | "lerna": "^3.19.0", 29 | "lerna-changelog": "^0.8.3", 30 | "lint-staged": "^9.3.0", 31 | "mocha": "^6.1.4", 32 | "npm-run-all": "^4.1.5", 33 | "nyc": "^14.1.0", 34 | "rimraf": "^3.0.0", 35 | "sinon": "^7.5.0", 36 | "supertest": "^4.0.2", 37 | "webpack": "^4.41.0", 38 | "webpack-cli": "^3.3.9" 39 | }, 40 | "husky": { 41 | "hooks": { 42 | "pre-commit": "lint-staged", 43 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" 44 | } 45 | }, 46 | "dependencies": { 47 | "@svrx/svrx": "file:packages/svrx", 48 | "@svrx/util": "file:packages/svrx-util" 49 | }, 50 | "lint-staged": { 51 | "*.js": [ 52 | "eslint --fix", 53 | "git add" 54 | ] 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /packages/svrx-util/.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /packages/svrx-util/README.md: -------------------------------------------------------------------------------- 1 | # @svrx/util 2 | 3 | > General utilities for svrx. 4 | 5 | svrx(server-x) is a platform built for efficient front-end development. 6 | 7 | See our [website](https://svrx.io/) for more information. 8 | 9 | ## Install 10 | 11 | ```bash 12 | npm install --save-dev @svrx/util 13 | ``` -------------------------------------------------------------------------------- /packages/svrx-util/__tests__/fixture/.svrx/config/.svrxrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | https: true, 3 | open: false, 4 | }; 5 | -------------------------------------------------------------------------------- /packages/svrx-util/__tests__/fixture/.svrx/plugins/hello/0.0.5/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'hello', 3 | priority: 100, 4 | options: { 5 | limit: { 6 | type: 'number', 7 | default: 5, 8 | }, 9 | }, 10 | hooks: { 11 | async onRoute(ctx, next, { config }) { 12 | const limit = config.get('limit'); 13 | ctx.set('X-Svrx-Limit', limit); 14 | await next(); 15 | }, 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /packages/svrx-util/__tests__/fixture/.svrx/plugins/hello/0.0.5/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svrx-plugin-hello", 3 | "version": "0.0.5", 4 | "keywords": [ 5 | "svrx", 6 | "plugin" 7 | ], 8 | "license": "MIT", 9 | "main": "./index.js", 10 | "engines": { 11 | "svrx": "~0.0.1" 12 | }, 13 | "dependencies": {} 14 | } 15 | -------------------------------------------------------------------------------- /packages/svrx-util/__tests__/fixture/.svrx/plugins/hello/1.0.0/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'hello', 3 | priority: 100, 4 | options: { 5 | limit: { 6 | type: 'number', 7 | default: 100, 8 | }, 9 | }, 10 | hooks: { 11 | async onRoute(ctx, next, { config }) { 12 | const limit = config.get('limit'); 13 | ctx.set('X-Svrx-Limit', limit); 14 | await next(); 15 | }, 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /packages/svrx-util/__tests__/fixture/.svrx/plugins/hello/1.0.0/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svrx-plugin-hello", 3 | "version": "1.0.0", 4 | "keywords": [ 5 | "svrx", 6 | "plugin" 7 | ], 8 | "license": "MIT", 9 | "main": "./index.js", 10 | "engines": { 11 | "svrx": "^1.0.0" 12 | }, 13 | "dependencies": {} 14 | } 15 | -------------------------------------------------------------------------------- /packages/svrx-util/__tests__/fixture/.svrx/plugins/hello/1.0.1/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'hello', 3 | priority: 100, 4 | options: { 5 | limit: { 6 | type: 'number', 7 | default: 101, 8 | }, 9 | }, 10 | hooks: { 11 | async onRoute(ctx, next, { config }) { 12 | const limit = config.get('limit'); 13 | ctx.set('X-Svrx-Limit', limit); 14 | await next(); 15 | }, 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /packages/svrx-util/__tests__/fixture/.svrx/plugins/hello/1.0.1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svrx-plugin-hello", 3 | "version": "1.0.1", 4 | "keywords": [ 5 | "svrx", 6 | "plugin" 7 | ], 8 | "license": "MIT", 9 | "main": "./index.js", 10 | "engines": { 11 | "svrx": "^1.0.0" 12 | }, 13 | "dependencies": {} 14 | } 15 | -------------------------------------------------------------------------------- /packages/svrx-util/__tests__/fixture/.svrx/versions/1.0.6/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svrxjs/svrx/7abfdc3331fbbf5d06d8fd04eb4f82d00e3c683e/packages/svrx-util/__tests__/fixture/.svrx/versions/1.0.6/index.js -------------------------------------------------------------------------------- /packages/svrx-util/__tests__/fixture/.svrx/versions/1.0.6/lib/svrx.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svrxjs/svrx/7abfdc3331fbbf5d06d8fd04eb4f82d00e3c683e/packages/svrx-util/__tests__/fixture/.svrx/versions/1.0.6/lib/svrx.js -------------------------------------------------------------------------------- /packages/svrx-util/__tests__/fixture/plugin/svrx-plugin-error-no-version/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'error-no-version', 3 | priority: 100, 4 | }; 5 | -------------------------------------------------------------------------------- /packages/svrx-util/__tests__/fixture/plugin/svrx-plugin-error-no-version/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svrx-plugin-error-no-version", 3 | "keywords": [ 4 | "svrx", 5 | "plugin" 6 | ], 7 | "license": "MIT", 8 | "main": "./index.js", 9 | "engines": { 10 | "svrx": "^0.0.1" 11 | }, 12 | "dependencies": {} 13 | } 14 | -------------------------------------------------------------------------------- /packages/svrx-util/__tests__/fixture/plugin/svrx-plugin-no-package/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'no-package', 3 | hooks: { 4 | async onRoute(ctx, next, { config }) { 5 | const limit = config.get('limit'); 6 | ctx.set('X-Svrx-Limit', limit); 7 | await next(); 8 | }, 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /packages/svrx-util/__tests__/fixture/plugin/svrx-plugin-test/assets/index.css: -------------------------------------------------------------------------------- 1 | body{background: black} -------------------------------------------------------------------------------- /packages/svrx-util/__tests__/fixture/plugin/svrx-plugin-test/assets/index.js: -------------------------------------------------------------------------------- 1 | console.log('svrx-plugin-test') -------------------------------------------------------------------------------- /packages/svrx-util/__tests__/fixture/plugin/svrx-plugin-test/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'test', 3 | priority: 100, 4 | options: { 5 | limit: { 6 | type: 'number', 7 | default: 100, 8 | }, 9 | }, 10 | assets: { 11 | style: ['./assets/index.css'], 12 | script: ['./assets/index.js'], 13 | }, 14 | hooks: { 15 | async onRoute(ctx, next, { config }) { 16 | const limit = config.get('limit'); 17 | ctx.set('X-Svrx-Limit', limit); 18 | await next(); 19 | }, 20 | }, 21 | }; 22 | -------------------------------------------------------------------------------- /packages/svrx-util/__tests__/fixture/plugin/svrx-plugin-test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svrx-plugin-test", 3 | "version": "0.0.1", 4 | "keywords": [ 5 | "svrx", 6 | "plugin" 7 | ], 8 | "license": "MIT", 9 | "main": "./index.js", 10 | "engines": { 11 | "svrx": "^0.0.1" 12 | }, 13 | "dependencies": {} 14 | } 15 | -------------------------------------------------------------------------------- /packages/svrx-util/__tests__/spec/logger.test.js: -------------------------------------------------------------------------------- 1 | const expect = require('expect.js'); 2 | const { Writable } = require('stream'); 3 | const logger = require('../../lib/logger'); 4 | 5 | const { Logger } = logger; 6 | 7 | describe('logger', () => { 8 | function log(msg, label) { 9 | return new Promise((resolve) => { 10 | let cached = ''; 11 | Logger.stream = new Writable({}); 12 | Logger.stream._write = (chunk, encode, cb) => { 13 | cached += chunk.toString(); 14 | cb(); 15 | }; 16 | 17 | const l = new logger.Logger(); 18 | 19 | Logger.stream.on('finish', () => { 20 | Logger.stream = process.stdout; 21 | resolve(cached); 22 | }); 23 | l[label](msg); 24 | Logger.stream.end(); 25 | }); 26 | } 27 | 28 | it('log function', (done) => { 29 | Logger.setLevel('debug'); 30 | log('hello world', 'notify') 31 | .then((content) => { 32 | expect(content).to.match(/\[svrx\]/); 33 | return log('hello world', 'error'); 34 | }) 35 | .then((content) => { 36 | expect(content).to.match(/\[error\]/); 37 | return log('hello world', 'debug'); 38 | }) 39 | .then((content) => { 40 | expect(content).to.match(/\[debug\]/); 41 | return log('hello world', 'info'); 42 | }) 43 | .then((content) => { 44 | expect(content).to.match(/\[info\]/); 45 | return log('hello world', 'warn'); 46 | }) 47 | .then((content) => { 48 | expect(content).to.match(/\[warn\]/); 49 | Logger.setLevel('error'); 50 | done(); 51 | }); 52 | }); 53 | 54 | it('log level', (done) => { 55 | Logger.setLevel('error'); 56 | log('hello world', 'notify') 57 | .then((content) => { 58 | expect(content).to.match(/\[svrx\]/); 59 | return log('hello world', 'error'); 60 | }) 61 | .then((content) => { 62 | expect(content).to.match(/\[error\]/); 63 | return log('hello world', 'debug'); 64 | }) 65 | .then((content) => { 66 | expect(content).to.equal(''); 67 | return log('hello world', 'info'); 68 | }) 69 | .then((content) => { 70 | expect(content).to.equal(''); 71 | return log('hello world', 'warn'); 72 | }) 73 | .then((content) => { 74 | expect(content).to.equal(''); 75 | Logger.setLevel('error'); 76 | done(); 77 | }); 78 | }); 79 | 80 | it('Logger.lock', (done) => { 81 | Logger.lock(); 82 | 83 | log('hello world', 'notify').then((content) => { 84 | expect(content).to.equal(''); 85 | Logger.release(); 86 | log('hello world', 'notify').then((cnt) => { 87 | expect(cnt).to.match(/hello world/); 88 | done(); 89 | }); 90 | }); 91 | }); 92 | 93 | it('should change category throught #getPluginLogger()', () => { 94 | const pluginLogger = logger.getPluginLogger('test-plugin'); 95 | expect(pluginLogger.category).to.eql('test-plugin'); 96 | }); 97 | }); 98 | -------------------------------------------------------------------------------- /packages/svrx-util/__tests__/spec/name-formatter.test.js: -------------------------------------------------------------------------------- 1 | const expect = require('expect.js'); 2 | const { normalizePluginName, parsePluginName } = require('../../lib/name-formatter'); 3 | 4 | describe('Name Formatter', () => { 5 | const plugins = [ 6 | { name: 'svrx-plugin-foo', pluginName: 'foo' }, 7 | { name: 'svrx-plugin-foo-bar', pluginName: 'foo-bar' }, 8 | { name: '@scope/svrx-plugin-foo', pluginName: '@scope/foo' }, 9 | { name: '@scope/svrx-plugin-foo-bar', pluginName: '@scope/foo-bar' }, 10 | ]; 11 | 12 | it('normalizePluginName', () => { 13 | plugins.forEach((p) => { 14 | expect(normalizePluginName(p.pluginName)).to.equal(p.name); 15 | }); 16 | expect(normalizePluginName('@scope')).to.equal(null); 17 | expect(normalizePluginName('@scope/')).to.equal(null); 18 | expect(normalizePluginName('@SCOPE/bar')).to.equal(null); 19 | }); 20 | it('parsePluginName', () => { 21 | plugins.forEach((p) => { 22 | expect(parsePluginName(p.name)).to.equal(p.pluginName); 23 | }); 24 | expect(parsePluginName('svrx-not-a-legal-plugin')).to.equal(null); 25 | expect(parsePluginName('@scope/svrx-not-a-legal-plugin')).to.equal(null); 26 | expect(parsePluginName('@no_')).to.equal(null); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /packages/svrx-util/__tests__/spec/rc-file-read.test.js: -------------------------------------------------------------------------------- 1 | const expect = require('expect.js'); 2 | const libPath = require('path'); 3 | const rcFileRead = require('../../lib/rc-file-read'); 4 | 5 | const TEST_SVRX_DIR = libPath.join(__dirname, '../fixture/.svrx'); 6 | 7 | describe('rcFileRead', () => { 8 | const { SVRX_DIR } = process.env; 9 | before(() => { 10 | process.env.SVRX_DIR = TEST_SVRX_DIR; 11 | }); 12 | after(() => { 13 | process.env.SVRX_DIR = SVRX_DIR; 14 | }); 15 | 16 | it('should return global rc configs correctly', () => { 17 | const options = rcFileRead(); 18 | expect(options).to.eql({ 19 | https: true, 20 | open: false, 21 | }); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /packages/svrx-util/__tests__/svrx-util.test.js: -------------------------------------------------------------------------------- 1 | // 2 | // 3 | // const svrxUtil = require('..'); 4 | // 5 | // describe('svrx-util', () => { 6 | // it('needs tests'); 7 | // }); 8 | -------------------------------------------------------------------------------- /packages/svrx-util/lib/c2k.js: -------------------------------------------------------------------------------- 1 | /* istanbul ignore file */ 2 | /** 3 | * 4 | License 5 | MIT 6 | https://github.com/cyrilluce/koa2-connect#readme 7 | 8 | ## Fork chain 9 | - Copyright (c) 2014-present Vladimir Kurchatkin 10 | - Copyright (c) 2017-present cyrilluce 11 | - Copyright (c) 2019-present leeluolee 12 | */ 13 | 14 | /** 15 | * The middleware function does include the `next` callback so only resolve 16 | * the Promise when it's called. If it's never called, the middleware stack 17 | * completion will stall 18 | */ 19 | function handler(ctx, connectMiddleware, options) { 20 | options = options || {}; 21 | 22 | return new Promise((resolve, reject) => { 23 | function makeInjectedResponse(koaCtx, whenEnded) { 24 | const { res } = koaCtx; 25 | 26 | res.on('close', whenEnded).on('finish', whenEnded); 27 | 28 | // koa2.0 initial assign statusCode to 404, default reset it to 200 29 | let dummyRes; 30 | let statusCodeSetted = false; 31 | function default404to200() { 32 | if (!statusCodeSetted && res.statusCode === 404) { 33 | res.statusCode = 200; 34 | } 35 | } 36 | if (!dummyRes) { 37 | let buffer = Buffer.from([]); 38 | dummyRes = { 39 | __proto__: res, 40 | end(...args) { 41 | const cnt = args[0]; 42 | if (options.bubble) { 43 | ctx.respond = true; 44 | ctx.body = cnt ? Buffer.concat([buffer, Buffer.from(cnt)]) : buffer; 45 | resolve(false); // can't trigger finish or end 46 | } else { 47 | res.end(...args); 48 | } 49 | default404to200(); 50 | }, 51 | write(...args) { 52 | const cnt = args[0]; 53 | if (options.bubble) { 54 | ctx.respond = true; 55 | buffer = Buffer.concat([buffer, Buffer.from(cnt)]); 56 | } else { 57 | res.write(...args); 58 | } 59 | default404to200(); 60 | }, 61 | set statusCode(v) { 62 | statusCodeSetted = true; 63 | res.statusCode = v; 64 | }, 65 | get statusCode() { 66 | return res.statusCode; 67 | }, 68 | writeHead(...args) { 69 | statusCodeSetted = true; 70 | return res.writeHead(...args); 71 | }, 72 | setHeader(...args) { 73 | statusCodeSetted = true; 74 | return res.setHeader(...args); 75 | }, 76 | }; 77 | } 78 | 79 | return dummyRes; 80 | } 81 | // (req, res) 82 | const args = [ 83 | ctx.req, 84 | makeInjectedResponse(ctx, () => { 85 | resolve(false); 86 | }), 87 | ]; 88 | let assumeSync = true; 89 | // (req, res, next) or (err, req, res, next) 90 | if (connectMiddleware.length >= 3) { 91 | args.push((err) => { 92 | if (err) reject(err); 93 | else resolve(true); 94 | }); 95 | assumeSync = false; 96 | } 97 | // (err, req, res, next) 98 | if (connectMiddleware.length >= 4) { 99 | args.unshift(null); 100 | } 101 | connectMiddleware(...args); 102 | /** 103 | * If the middleware function does not declare receiving the `next` callback 104 | * assume that it's synchronous. 105 | */ 106 | if (assumeSync) { 107 | resolve(true); 108 | } 109 | }); 110 | } 111 | 112 | /** 113 | * Returns a Koa middleware function that varies its async logic based on if the 114 | * given middleware function declares at least 3 parameters, i.e. includes 115 | * the `next` callback function 116 | */ 117 | function koaConnect(connectMiddleware, options) { 118 | return async (ctx, next) => { 119 | ctx.respond = false; 120 | try { 121 | const goNext = await handler(ctx, connectMiddleware, options); 122 | 123 | if (goNext) { 124 | ctx.respond = true; 125 | return next(); 126 | } 127 | } catch (err) { 128 | ctx.respond = true; 129 | throw err; 130 | } 131 | return next(); 132 | }; 133 | } 134 | 135 | module.exports = koaConnect; 136 | -------------------------------------------------------------------------------- /packages/svrx-util/lib/logger.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk'); 2 | const ora = require('ora'); 3 | 4 | const LABEL_CONFIG = { 5 | silent: { 6 | index: 101, 7 | }, 8 | notify: { 9 | index: 100, 10 | text: 'svrx', 11 | color: 'Blue', 12 | }, 13 | error: { 14 | color: 'Red', 15 | index: 10, 16 | }, 17 | warn: { 18 | color: 'Yellow', 19 | index: 5, 20 | }, 21 | info: { 22 | color: 'Green', 23 | index: 3, 24 | }, 25 | debug: { 26 | color: 'Black', 27 | index: 1, 28 | }, 29 | }; 30 | 31 | const LEVELS = ['notify', 'error', 'warn', 'info', 'debug']; 32 | 33 | const STATE = { 34 | LOCKED: Symbol('LOCKED'), 35 | UNLOCKED: Symbol('UNLOCKED'), 36 | }; 37 | 38 | class Logger { 39 | static setLevel(level) { 40 | if (level in LABEL_CONFIG) { 41 | Logger.levelIndex = LABEL_CONFIG[level].index; 42 | } 43 | } 44 | 45 | static lock() { 46 | if (Logger.state === STATE.LOCKED) return; 47 | const stdout = Logger.stream; 48 | Logger.oldWrite = stdout._write; 49 | 50 | stdout._write = function write(...args) { 51 | args[0] = ''; 52 | return Logger.oldWrite.apply(this, args); 53 | }; 54 | 55 | Logger.state = STATE.LOCKED; 56 | } 57 | 58 | static release() { 59 | if (Logger.state !== STATE.LOCKED) return; 60 | Logger.stream._write = Logger.oldWrite; 61 | delete Logger.oldWrite; 62 | Logger.state = STATE.UNLOCKED; 63 | } 64 | 65 | constructor(category = 'global') { 66 | this.category = category; 67 | this.chalk = chalk; 68 | } 69 | 70 | _getWriteMsg(msg, label) { 71 | label = label || 'notify'; 72 | 73 | if (LEVELS.indexOf(label) === -1) { 74 | throw Error(`logger.${label}() isn't exsits`); 75 | } 76 | 77 | const { index } = LABEL_CONFIG[label]; 78 | 79 | const text = LABEL_CONFIG[label].text || label; 80 | 81 | if (index < Logger.levelIndex) return ''; 82 | 83 | const { category } = this; 84 | const { color } = LABEL_CONFIG[label]; 85 | const foreColor = color === 'White' ? 'gray' : 'white'; 86 | const bgColor = `bg${color}`; 87 | const padText = `[${text}${category === 'global' ? '' : `:${category}`}]`; 88 | const labelText = color ? chalk[foreColor][bgColor](padText) : padText; 89 | 90 | return `${labelText} ${msg}\n`; 91 | } 92 | 93 | write(msg, label, options) { 94 | options = options || {}; 95 | if (Logger.state === STATE.LOCKED && options.force !== true) { 96 | this.write('Logger is locked, some progress task need release', 'warn', { force: true }); 97 | return; 98 | } 99 | 100 | const content = this._getWriteMsg(msg, label); 101 | 102 | if (content) Logger.stream.write(content); 103 | } 104 | 105 | spin(msg, label) { 106 | const spinner = ora(this._getWriteMsg(msg, label)).start(); 107 | return () => { 108 | spinner.stop(); 109 | }; 110 | } 111 | 112 | progress(msg, label) { 113 | const releaseSpin = this.spin(msg, label); 114 | 115 | Logger.lock(); 116 | return function release() { 117 | Logger.release(); 118 | releaseSpin(); 119 | }; 120 | } 121 | 122 | log(...args) { 123 | return this.notify(...args); 124 | } 125 | } 126 | 127 | Logger.levelIndex = LABEL_CONFIG.warn.index; 128 | Logger.state = STATE.UNLOCKED; 129 | Logger.stream = process.stdout; 130 | 131 | LEVELS.forEach((level) => { 132 | Logger.prototype[level] = function write(msg) { 133 | this.write(msg, level); 134 | }; 135 | }); 136 | 137 | function getPluginLogger(name) { 138 | return new Logger(name); 139 | } 140 | 141 | const logger = new Logger(); 142 | 143 | logger.setLevel = Logger.setLevel; 144 | logger.Logger = Logger; 145 | logger.getPluginLogger = getPluginLogger; 146 | 147 | module.exports = logger; 148 | -------------------------------------------------------------------------------- /packages/svrx-util/lib/name-formatter.js: -------------------------------------------------------------------------------- 1 | const PLUGIN_PREFIX = 'svrx-plugin-'; 2 | const scopeAndNameRegex = /^@([a-z\d][\w-.]+)\/([a-z\d][\w-.]*)/; 3 | 4 | /** 5 | * combine pluginName to packageName 6 | * - foo -> svrx-plugin-foo 7 | * - foo-bar -> svrx-plugin-foo-bar 8 | * - @scope/foo -> @scope/svrx-plugin-foo 9 | * - @scope/foo-bar -> @scope/svrx-plugin-foo-bar 10 | * @param name 11 | * @returns {string|null|*} 12 | */ 13 | const normalizePluginName = (name) => { 14 | const combineName = (n) => (n.startsWith(PLUGIN_PREFIX) ? n : PLUGIN_PREFIX + n); 15 | const isScoped = name.startsWith('@'); 16 | 17 | if (isScoped) { 18 | const matches = scopeAndNameRegex.exec(name); 19 | if (matches) { 20 | const scope = matches[1]; 21 | const realName = matches[2]; 22 | return `@${scope}/${combineName(realName)}`; 23 | } 24 | return null; 25 | } 26 | return combineName(name); 27 | }; 28 | 29 | /** 30 | * parse packageName to pluginName (revert normalizePluginName()) 31 | * @param packageName 32 | * @returns {null|*} 33 | */ 34 | const parsePluginName = (packageName) => { 35 | const isScoped = packageName.startsWith('@'); 36 | const removePrefix = (n) => (n.startsWith(PLUGIN_PREFIX) ? n.slice(PLUGIN_PREFIX.length) : null); 37 | 38 | if (isScoped) { 39 | const matches = scopeAndNameRegex.exec(packageName); 40 | if (matches) { 41 | const scope = matches[1]; 42 | const realName = matches[2]; 43 | const formattedName = removePrefix(realName); 44 | return formattedName ? `@${scope}/${formattedName}` : null; 45 | } 46 | return null; 47 | } 48 | return removePrefix(packageName); 49 | }; 50 | 51 | module.exports = { 52 | normalizePluginName, 53 | parsePluginName, 54 | }; 55 | -------------------------------------------------------------------------------- /packages/svrx-util/lib/npCall/index.js: -------------------------------------------------------------------------------- 1 | // return promise by callback node-callback-style handler 2 | function npCall(callback, args, ctx) { 3 | args = args || []; 4 | 5 | return new Promise((resolve, reject) => { 6 | args.push((err, ret) => { 7 | if (err) return reject(err); 8 | return resolve(ret); 9 | }); 10 | 11 | callback.apply(ctx, args); 12 | }); 13 | } 14 | 15 | module.exports = npCall; 16 | -------------------------------------------------------------------------------- /packages/svrx-util/lib/package-manager/index.js: -------------------------------------------------------------------------------- 1 | const PluginPackageManager = require('./plugin-package-manager'); 2 | const PackageManager = require('./package-manager'); 3 | 4 | /** 5 | * @example 6 | const pm = PackageManagerCreator({ 7 | plugin: 'lodash', // will install svrx core if missing 8 | path: undefined, // if install from path 9 | version: '1.0.0', 10 | coreVersion: '1.0.1', // version of current svrx core 11 | autoClean: true, // auto remove old packages 12 | }); 13 | const pkg = await pm.load(); 14 | */ 15 | const PackageManagerCreator = ({ 16 | plugin, 17 | ...options 18 | } = {}) => (plugin 19 | ? new PluginPackageManager({ 20 | name: plugin, // plugin name: foo, foo-bar, @scope/foo 21 | ...options, 22 | }) 23 | : new PackageManager({ 24 | name: 'svrx', 25 | ...options, 26 | }) 27 | ); 28 | 29 | module.exports = PackageManagerCreator; 30 | -------------------------------------------------------------------------------- /packages/svrx-util/lib/package-manager/plugin-package-manager.js: -------------------------------------------------------------------------------- 1 | const semver = require('semver'); 2 | const PackageManager = require('./package-manager'); 3 | const { normalizePluginName } = require('../name-formatter'); 4 | 5 | class PluginPackageManager extends PackageManager { 6 | constructor(options) { 7 | super(options); 8 | this.packageName = normalizePluginName(this.name); 9 | } 10 | 11 | getRoot() { 12 | return this.PLUGIN_ROOT; 13 | } 14 | 15 | versionMatch(pkg) { 16 | const { coreVersion } = this; 17 | return semver.satisfies(coreVersion, pkg.pattern); 18 | } 19 | } 20 | 21 | module.exports = PluginPackageManager; 22 | -------------------------------------------------------------------------------- /packages/svrx-util/lib/rc-file-read.js: -------------------------------------------------------------------------------- 1 | const { cosmiconfigSync } = require('cosmiconfig'); 2 | const userHome = require('os').homedir(); 3 | const path = require('path'); 4 | const fs = require('fs'); 5 | 6 | const RC_FILES = ['.svrxrc.js', 'svrx.config.js']; 7 | const readGlobal = () => { 8 | const configRoot = process.env.SVRX_DIR; 9 | if (!configRoot && !userHome) { 10 | return {}; 11 | } 12 | 13 | const root = configRoot || path.resolve(userHome, '.svrx'); 14 | const fileName = RC_FILES.find((file) => fs.existsSync(path.join(root, 'config', file))); 15 | 16 | if (fileName) { 17 | return require(path.join(root, 'config', fileName)); // eslint-disable-line 18 | } 19 | 20 | return {}; 21 | }; 22 | 23 | const readScope = () => { 24 | const explorer = cosmiconfigSync('svrx', { 25 | searchPlaces: RC_FILES, 26 | }); 27 | const result = explorer.search(); 28 | if (result && !result.isEmpty) { 29 | return result.config; 30 | } 31 | return {}; 32 | }; 33 | 34 | module.exports = () => { 35 | const globalConfig = readGlobal(); 36 | const scopeConfig = readScope(); 37 | 38 | return { ...globalConfig, ...scopeConfig }; 39 | }; 40 | -------------------------------------------------------------------------------- /packages/svrx-util/lib/svrx-util.js: -------------------------------------------------------------------------------- 1 | exports.PackageManagerCreator = require('./package-manager'); 2 | exports.nameFormatter = require('./name-formatter'); 3 | exports.rcFileRead = require('./rc-file-read'); 4 | exports.logger = require('./logger'); 5 | exports.c2k = require('./c2k'); 6 | -------------------------------------------------------------------------------- /packages/svrx-util/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@svrx/util", 3 | "version": "1.1.7", 4 | "description": "util package of svrx", 5 | "homepage": "https://svrx.io/", 6 | "license": "MIT", 7 | "main": "lib/svrx-util.js", 8 | "directories": { 9 | "lib": "lib" 10 | }, 11 | "files": [ 12 | "lib", 13 | "npm-shrinkwrap.json" 14 | ], 15 | "publishConfig": { 16 | "access": "public" 17 | }, 18 | "scripts": { 19 | "test": "nyc --reporter=html --reporter=text mocha --recursive __tests__/spec --exit", 20 | "report-coverage": "nyc report --reporter=text-lcov > coverage.lcov && codecov" 21 | }, 22 | "repository": { 23 | "type": "git", 24 | "url": "git+https://github.com/svrxjs/svrx.git" 25 | }, 26 | "bugs": { 27 | "url": "https://github.com/svrxjs/svrx/issues" 28 | }, 29 | "dependencies": { 30 | "chalk": "^3.0.0", 31 | "cosmiconfig": "^6.0.0", 32 | "fs-extra": "^8.1.0", 33 | "lodash": "^4.17.11", 34 | "mkdirp": "^0.5.1", 35 | "ora": "^4.0.1", 36 | "rimraf": "^3.0.0", 37 | "semver": "^7.1.1", 38 | "tmp": "^0.1.0" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /packages/svrx/.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /packages/svrx/README.md: -------------------------------------------------------------------------------- 1 | # @svrx/svrx 2 | 3 | > svrx core. 4 | 5 | svrx(server-x) is a platform built for efficient front-end development. 6 | 7 | See our [website](https://svrx.io/) for more information. 8 | 9 | ## Install 10 | 11 | ```bash 12 | npm install --save-dev @svrx/svrx 13 | ``` -------------------------------------------------------------------------------- /packages/svrx/__tests__/fixture/plugin/historyApiFallback/index.html: -------------------------------------------------------------------------------- 1 | hi 2 | -------------------------------------------------------------------------------- /packages/svrx/__tests__/fixture/plugin/serve/demo.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/svrx/__tests__/fixture/plugin/serve/demo.js: -------------------------------------------------------------------------------- 1 | parseInt('123', 10); 2 | -------------------------------------------------------------------------------- /packages/svrx/__tests__/fixture/plugin/serve/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svrxjs/svrx/7abfdc3331fbbf5d06d8fd04eb4f82d00e3c683e/packages/svrx/__tests__/fixture/plugin/serve/demo.png -------------------------------------------------------------------------------- /packages/svrx/__tests__/fixture/plugin/svrx-plugin-depend/index.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | name: 'test', 4 | priority: 100, 5 | configSchema: { 6 | limit: { 7 | type: 'number', 8 | default: 100, 9 | }, 10 | }, 11 | assets: { 12 | style: ['./assets/index.css'], 13 | script: ['./assets/index.js'], 14 | }, 15 | hooks: { 16 | async onRoute(ctx, next, { config }) { 17 | const limit = config.get('limit'); 18 | ctx.set('X-Svrx-Limit', limit); 19 | await next(); 20 | }, 21 | }, 22 | }; 23 | -------------------------------------------------------------------------------- /packages/svrx/__tests__/fixture/plugin/svrx-plugin-depend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svrx-plugin-depend", 3 | "version": "0.0.1", 4 | "keywords": [ 5 | "svrx", 6 | "plugin" 7 | ], 8 | "license": "MIT", 9 | "main": "index.js", 10 | "dependencies": { 11 | "lodash.noop": "3.0.1" 12 | 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/svrx/__tests__/fixture/plugin/svrx-plugin-no-package/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'no-package', 3 | hooks: { 4 | async onRoute(ctx, next, { config }) { 5 | const limit = config.get('limit'); 6 | ctx.set('X-Svrx-Limit', limit); 7 | await next(); 8 | }, 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /packages/svrx/__tests__/fixture/plugin/svrx-plugin-test/assets/index.css: -------------------------------------------------------------------------------- 1 | body{background: black} -------------------------------------------------------------------------------- /packages/svrx/__tests__/fixture/plugin/svrx-plugin-test/assets/index.js: -------------------------------------------------------------------------------- 1 | console.log('svrx-plugin-test') -------------------------------------------------------------------------------- /packages/svrx/__tests__/fixture/plugin/svrx-plugin-test/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: 'test', 3 | priority: 100, 4 | configSchema: { 5 | limit: { 6 | type: 'number', 7 | default: 100, 8 | }, 9 | }, 10 | assets: { 11 | style: ['./assets/index.css'], 12 | script: ['./assets/index.js'], 13 | }, 14 | hooks: { 15 | async onRoute(ctx, next, { config }) { 16 | const limit = config.get('limit'); 17 | ctx.set('X-Svrx-Limit', limit); 18 | await next(); 19 | }, 20 | }, 21 | }; 22 | -------------------------------------------------------------------------------- /packages/svrx/__tests__/fixture/plugin/svrx-plugin-test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svrx-plugin-test", 3 | "version": "0.0.1", 4 | "keywords": [ 5 | "svrx", 6 | "plugin" 7 | ], 8 | "license": "MIT", 9 | "main": "./index.js", 10 | "engines": { 11 | "svrx": "^0.0.1" 12 | }, 13 | "dependencies": {} 14 | } 15 | -------------------------------------------------------------------------------- /packages/svrx/__tests__/fixture/router/rule.normal.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | get('/hello/:name').to.send('hello world'); 4 | 5 | get('/normal/:id').json({ code: 200 }); 6 | -------------------------------------------------------------------------------- /packages/svrx/__tests__/fixture/router/rule.require.js: -------------------------------------------------------------------------------- 1 | const bodyParserRelative = require('./node_modules/koa-bodyparser'); //relative path 2 | const bodyParser = require('koa-bodyparser'); 3 | 4 | post('/test/post').to.handle(bodyParser()).handle((ctx) => { 5 | ctx.type = 'html'; 6 | ctx.body = ctx.request.body; 7 | }); 8 | post('/test/post/relative').to.handle(bodyParserRelative()).handle((ctx) => { 9 | ctx.type = 'html'; 10 | ctx.body = ctx.request.body; 11 | }); 12 | -------------------------------------------------------------------------------- /packages/svrx/__tests__/fixture/router/static/index.html: -------------------------------------------------------------------------------- 1 | router -------------------------------------------------------------------------------- /packages/svrx/__tests__/spec/configure/builtinConfig.js: -------------------------------------------------------------------------------- 1 | const expect = require('expect.js'); 2 | const Option = require('../../../lib/configure/option'); 3 | const CONFIGS = require('../../../lib/config-list'); 4 | const { BUILTIN_PLUGIN } = require('../../../lib/constant'); 5 | const defaults = require('../../../lib/util/jsonSchemaDefaults'); 6 | const { createServer } = require('../../util'); 7 | 8 | const BUILTIN_DEFAULTS = defaults({ 9 | type: 'object', 10 | properties: CONFIGS, 11 | }); 12 | 13 | describe('Builtin Configs', () => { 14 | it('should fill all default values', () => { 15 | const server = createServer(); 16 | Object.keys(CONFIGS).forEach((key) => { 17 | const value = CONFIGS[key]; 18 | if (value.default !== undefined && key !== 'open' && key 19 | !== 'livereload') { 20 | expect(server.config.get(key)).to.eql(value.default); 21 | } 22 | }); 23 | }); 24 | 25 | it('should enable all builtin plugins by default', () => { 26 | const server = createServer(); 27 | const plugins = server.config.getPlugins(); 28 | expect(plugins.length).to.eql(BUILTIN_PLUGIN.length); 29 | expect(plugins.map((p) => p.getInfo('name'))).to.eql(BUILTIN_PLUGIN); 30 | }); 31 | 32 | it('should concat array values from CLI and RC', () => { 33 | const server = createServer({ 34 | proxy: [{ a: 'a' }], 35 | }, { 36 | proxy: [{ b: 'b' }], 37 | }); 38 | expect(server.config.get('proxy')).to.eql([{ a: 'a' }, { b: 'b' }]); 39 | }); 40 | }); 41 | 42 | describe('Config get', () => { 43 | const server = createServer({ 44 | port: 3000, 45 | plugins: [ 46 | { 47 | name: 'test', 48 | version: '0.0.1', 49 | inplace: true, 50 | configSchema: { 51 | foo: { 52 | type: 'string', 53 | default: 'bar', 54 | }, 55 | }, 56 | options: { 57 | op: 123, 58 | }, 59 | }, 60 | ], 61 | }); 62 | const { config } = server; 63 | const testPlugin = config.getPlugin('test'); 64 | 65 | it('should get builtin value corrently', () => { 66 | expect(config.get('port')).to.equal(3000); 67 | }); 68 | 69 | it('should get plugin info corrently', () => { 70 | expect(testPlugin.getInfo('version')).to.equal('0.0.1'); 71 | }); 72 | 73 | it('should get plugin option corrently', () => { 74 | expect(testPlugin.get('op')).to.equal(123); 75 | }); 76 | 77 | it('should get builtin options in plugin config with $', () => { 78 | expect(testPlugin.get('$.port')).to.equal(3000); 79 | }); 80 | 81 | it('should get all options (includes the defaults) when there\'s no path', () => { 82 | expect(config.get()).to.eql({ 83 | ...BUILTIN_DEFAULTS, port: 3000, open: false, livereload: false, 84 | }); 85 | }); 86 | 87 | it('should get all plugin options (includes the defaults) when there\'s no path', async () => { 88 | await server.setup(); 89 | expect(testPlugin.get()).to.eql({ 90 | op: 123, 91 | foo: 'bar', // defaults 92 | }); 93 | }); 94 | 95 | it('should return schema correctly', () => { 96 | expect(config.getSchema()).to.eql(CONFIGS); 97 | }); 98 | 99 | it('should return all external plugins when getExternalPlugins()', () => { 100 | expect(config.getExternalPlugins().map((p) => p.getInfo('name'))).to.eql(['test']); 101 | }); 102 | }); 103 | 104 | describe('Config set', () => { 105 | const server = createServer({ 106 | port: 3000, 107 | plugins: [ 108 | { 109 | name: 'test', 110 | version: '0.0.1', 111 | inplace: true, 112 | configSchema: { 113 | foo: { 114 | type: 'string', 115 | default: 'bar', 116 | }, 117 | }, 118 | options: { 119 | op: 123, 120 | }, 121 | }, 122 | ], 123 | }); 124 | const { config } = server; 125 | const testPlugin = config.getPlugin('test'); 126 | 127 | it('should set builtin value correctly', () => { 128 | config.set('port', 4000); 129 | expect(config.get('port')).to.equal(4000); 130 | config.set('port', 3000); 131 | }); 132 | 133 | it('should set plugin option correctly', () => { 134 | testPlugin.set('op', 321); 135 | testPlugin.set('other', 'other info'); 136 | expect(testPlugin.get('op')).to.equal(321); 137 | expect(testPlugin.get('other')).to.equal('other info'); 138 | }); 139 | 140 | it('should set builtin values in object correctly', () => { 141 | config.builtinsSet({ 142 | port: 4000, 143 | https: true, 144 | }); 145 | expect(config.get('port')).to.equal(4000); 146 | expect(config.get('https')).to.equal(true); 147 | config.builtinsSet({ 148 | port: 3000, 149 | https: false, 150 | }); 151 | }); 152 | }); 153 | 154 | describe('Functions', () => { 155 | let builtinConfig; 156 | before(async () => { 157 | const server = createServer({ 158 | port: 3000, 159 | plugins: [ 160 | { 161 | name: 'test', 162 | version: '0.0.1', 163 | inplace: true, 164 | configSchema: { 165 | foo: { 166 | type: 'string', 167 | default: 'bar', 168 | }, 169 | }, 170 | options: { 171 | op: 123, 172 | }, 173 | }, 174 | ], 175 | }); 176 | builtinConfig = server.config; 177 | }); 178 | 179 | it('should keep #watch() on configs', (done) => { 180 | const release = builtinConfig.watch((evt) => { 181 | expect(evt.affect()).to.equal(true); 182 | expect(evt.affect('watch.b.c')).to.equal(true); 183 | expect(evt.affect('watch')).to.equal(true); 184 | expect(evt.affect('watch.c')).to.equal(false); 185 | release(); 186 | done(); 187 | }); 188 | builtinConfig.set('watch.b.c', 'world'); 189 | }); 190 | 191 | it('should delete a config after #del()', () => { 192 | builtinConfig.set('test.del.item', 'hello'); 193 | expect(builtinConfig.get('test.del.item')).to.equal('hello'); 194 | builtinConfig.del('test.del.item'); 195 | expect(builtinConfig.get('test.del.item')).to.eql(undefined); 196 | 197 | builtinConfig.del('none.exists.config'); 198 | expect(builtinConfig.get('none.exists.config')).to.eql(undefined); 199 | }); 200 | 201 | it('should splice an array config after #splice()', () => { 202 | builtinConfig.set('test.splice.item', [1, 2, 3]); 203 | builtinConfig.splice('test.splice.item', 0, 1); 204 | expect(builtinConfig.get('test.splice.item')).to.eql([2, 3]); 205 | }); 206 | }); 207 | 208 | describe('Config Validate', () => { 209 | it('should validate single type configs', () => { 210 | const option = new Option({ 211 | https: 3000, // should be boolean 212 | port: 'port', // should be number 213 | svrx: 123, // should be string 214 | urls: 'should be object', // should be object 215 | }); 216 | const errors = option._validate(CONFIGS); 217 | expect(errors).not.to.equal(null); 218 | expect(errors.length).to.equal(4); 219 | expect(errors[0]).to.equal('Config Error: .https should be boolean'); 220 | expect(errors[1]).to.equal('Config Error: .port should be number'); 221 | expect(errors[2]).to.equal('Config Error: .svrx should be string'); 222 | expect(errors[3]).to.equal('Config Error: .urls should be object'); 223 | }); 224 | 225 | it('should validate multi type configs', () => { 226 | const option = new Option({ 227 | proxy: 123, // boolean,array,object 228 | open: ['a', 'b'], // boolean,string 229 | serve: 'string', 230 | }); 231 | const errors = option._validate(CONFIGS); 232 | expect(errors).not.to.equal(null); 233 | expect(errors.length).to.equal(3); 234 | expect(errors[0]) 235 | .to 236 | .equal('Config Error: .open should be boolean or string'); 237 | expect(errors[1]) 238 | .to 239 | .equal('Config Error: .proxy should be boolean or object or array'); 240 | expect(errors[2]) 241 | .to 242 | .equal('Config Error: .serve should be boolean or object'); 243 | }); 244 | 245 | it('should log the error path correctly', () => { 246 | const option = new Option({ 247 | serve: { 248 | base: 123, // string 249 | }, 250 | livereload: { 251 | exclude: true, 252 | }, 253 | }); 254 | const errors = option._validate(CONFIGS); 255 | expect(errors).not.to.equal(null); 256 | expect(errors.length).to.equal(2); 257 | expect(errors[0]) 258 | .to 259 | .equal('Config Error: .livereload.exclude should be string or array'); 260 | expect(errors[1]).to.equal('Config Error: .serve.base should be string'); 261 | }); 262 | }); 263 | -------------------------------------------------------------------------------- /packages/svrx/__tests__/spec/configure/inline.js: -------------------------------------------------------------------------------- 1 | const expect = require('expect.js'); 2 | const libPath = require('path'); 3 | const fs = require('fs'); 4 | const sinon = require('sinon'); 5 | const { createServer } = require('../../util'); 6 | require('../../../lib/configure/index'); // required for module get 7 | 8 | const TEST_PLUGIN_PATH = libPath.join(__dirname, '../../fixture/plugin/svrx-plugin-test'); 9 | const NO_PACKAGE_PLUGIN_PATH = libPath.join(__dirname, '../../fixture/plugin/svrx-plugin-no-package'); 10 | 11 | describe('Inline/RC File Config', () => { 12 | describe('plugins', () => { 13 | it('should pick string plugins correctly', () => { 14 | const server = createServer({ 15 | plugins: [ 16 | 'test', 'demo', 17 | ], 18 | }); 19 | const testPlugin = server.config.getPlugin('test'); 20 | const demoPlugin = server.config.getPlugin('demo'); 21 | expect(testPlugin).not.to.be(undefined); 22 | expect(demoPlugin).not.to.be(undefined); 23 | }); 24 | 25 | it('should pick obj plugins correctly', () => { 26 | const server = createServer({ 27 | plugins: [ 28 | { 29 | name: 'test', 30 | }, 31 | ], 32 | }); 33 | const testPlugin = server.config.getPlugin('test'); 34 | expect(testPlugin).not.to.be(undefined); 35 | }); 36 | 37 | // pass plugin by path 38 | describe('Local plugin name parse', () => { 39 | it('should parse normal plugin name correctly', () => { 40 | const server = createServer({ 41 | plugins: [ 42 | { 43 | path: TEST_PLUGIN_PATH, 44 | }, 45 | ], 46 | }); 47 | const testPlugin = server.config.getPlugin('test'); 48 | expect(testPlugin).not.to.be(undefined); 49 | }); 50 | it('should get plugin name without package.json correctly', () => { 51 | const server = createServer({ 52 | plugins: [ 53 | { 54 | path: NO_PACKAGE_PLUGIN_PATH, 55 | }, 56 | ], 57 | }); 58 | const testPlugin = server.config.getPlugin('no-package'); 59 | expect(testPlugin).not.to.be(undefined); 60 | }); 61 | it('should parse plugin name from path correctly', () => { 62 | const fakes = [ 63 | { path: '/fake/path/for/normal/plugin', name: 'svrx-plugin-foo', pluginName: 'foo' }, 64 | { path: '/fake/path/for/normal/dash/plugin', name: 'svrx-plugin-foo-bar', pluginName: 'foo-bar' }, 65 | { path: '/fake/path/for/scope/plugin', name: '@scope/svrx-plugin-foo', pluginName: '@scope/foo' }, 66 | { path: '/fake/path/for/scope/dash/plugin', name: '@scope/svrx-plugin-foo-bar', pluginName: '@scope/foo-bar' }, 67 | ]; 68 | const moduleId = '/svrx/packages/svrx/lib/configure/index.js'; 69 | 70 | fakes.forEach((fake) => { 71 | const fakePackagePath = libPath.join(fake.path, 'package.json'); 72 | const existsSyncStub = sinon.stub(fs, 'existsSync'); 73 | const configModule = module.children.find((mod) => mod.id.split(libPath.sep).join('/').endsWith(moduleId)); 74 | const requireStub = sinon.stub(configModule, 'require'); 75 | 76 | // fake functions 77 | requireStub.withArgs(fakePackagePath).callsFake(() => ({ name: fake.name })); 78 | existsSyncStub.withArgs(fakePackagePath).callsFake(() => true); 79 | 80 | const server = createServer({ 81 | plugins: [{ path: fake.path }], 82 | }); 83 | const testPlugin = server.config.getPlugin(fake.pluginName); 84 | expect(testPlugin).not.to.be(undefined); 85 | requireStub.restore(); 86 | existsSyncStub.restore(); 87 | }); 88 | }); 89 | }); 90 | }); 91 | }); 92 | -------------------------------------------------------------------------------- /packages/svrx/__tests__/spec/configure/pluginConfig.js: -------------------------------------------------------------------------------- 1 | const expect = require('expect.js'); 2 | const libPath = require('path'); 3 | const sinon = require('sinon'); 4 | const { createServer } = require('../../util'); 5 | 6 | const TEST_PLUGIN_PATH = libPath.join(__dirname, '../../fixture/plugin/svrx-plugin-test'); 7 | 8 | function requireEnsure(path) { 9 | delete require.cache[path]; 10 | /* eslint-disable global-require, import/no-dynamic-require */ 11 | return require(path); 12 | } 13 | 14 | describe('Plugin Config', () => { 15 | describe('functions', () => { 16 | let testPlugin; 17 | let builtinConfig; 18 | before(async () => { 19 | const server = createServer({ 20 | plugins: [{ 21 | path: TEST_PLUGIN_PATH, 22 | }], 23 | }); 24 | await server.setup(); 25 | testPlugin = server.config.getPlugin('test'); 26 | testPlugin = server.config.getPlugin('test'); 27 | builtinConfig = server.config; 28 | }); 29 | 30 | it('should return value correctly when #get()', () => { 31 | expect(testPlugin.get('none-exist')).to.equal(undefined); 32 | expect(testPlugin.get('limit')).to.equal(100); 33 | expect(testPlugin.get('$.port')).to.equal(8000); // builtin config 34 | }); 35 | 36 | it('should set value correctly when #set()', () => { 37 | testPlugin.set('foo', 'bar'); 38 | testPlugin.set('a.b', 'ab'); 39 | testPlugin.set('c.d', { obj: 'obj' }); 40 | expect(testPlugin.get('foo')).to.equal('bar'); 41 | expect(testPlugin.get('a.b')).to.equal('ab'); 42 | expect(testPlugin.get('c.d')).to.eql({ obj: 'obj' }); 43 | }); 44 | 45 | it('should not modify builtin configs by #set()', () => { 46 | testPlugin.set('$.port', 3000); 47 | expect(testPlugin.get('$.port')).to.not.equal(3000); 48 | }); 49 | 50 | it('should keep #watch() on configs', (done) => { 51 | const release = testPlugin.watch((evt) => { 52 | expect(evt.affect()).to.equal(true); 53 | expect(evt.affect('watch.b.c')).to.equal(true); 54 | expect(evt.affect('watch')).to.equal(true); 55 | expect(evt.affect('watch.c')).to.equal(false); 56 | release(); 57 | done(); 58 | }); 59 | testPlugin.set('watch.b.c', 'world'); 60 | }); 61 | 62 | it('should keep #watch($.port) on builtin configs', (done) => { 63 | const release = testPlugin.watch('$.port', (evt) => { 64 | expect(evt.affect()).to.equal(true); 65 | release(); 66 | done(); 67 | }); 68 | builtinConfig.set('port', 3000); 69 | }); 70 | 71 | it('should keep #watch($) on builtin configs', (done) => { 72 | const release = testPlugin.watch('$', (evt) => { 73 | expect(evt.affect('port')).to.equal(true); 74 | release(); 75 | done(); 76 | }); 77 | builtinConfig.set('port', 4000); 78 | }); 79 | 80 | it('should keep #watch($.a) multi-level builtin configs', (done) => { 81 | const release = testPlugin.watch('$.a', (evt) => { 82 | expect(evt.affect('b.c')).to.equal(true); 83 | expect(evt.affect('b.d')).to.equal(false); 84 | release(); 85 | done(); 86 | }); 87 | builtinConfig.set('a.b.c', 3000); 88 | }); 89 | 90 | it('should delete a config after #del()', () => { 91 | testPlugin.set('test.del.item', 'hello'); 92 | expect(testPlugin.get('test.del.item')).to.equal('hello'); 93 | testPlugin.del('test.del.item'); 94 | expect(testPlugin.get('test.del.item')).to.eql(undefined); 95 | 96 | testPlugin.del('none.exists.config'); 97 | expect(testPlugin.get('none.exists.config')).to.eql(undefined); 98 | }); 99 | 100 | it('should splice an array config after #splice()', () => { 101 | testPlugin.set('test.splice.item', [1, 2, 3]); 102 | testPlugin.splice('test.splice.item', 0, 1); 103 | expect(testPlugin.get('test.splice.item')).to.eql([2, 3]); 104 | }); 105 | }); 106 | 107 | it('should return default values when get plugin(load from path) option', async () => { 108 | const server = createServer({ 109 | plugins: [{ 110 | path: TEST_PLUGIN_PATH, 111 | }], 112 | }); 113 | await server.setup(); 114 | const testPlugin = server.config.getPlugin('test'); 115 | expect(testPlugin.get('limit')).to.equal(100); 116 | }); 117 | 118 | it('should return default values when get plugin(load from remote) option', async () => { 119 | const server = createServer({ 120 | plugins: ['remote'], 121 | }); 122 | const fakeLoadOne = sinon.fake.resolves({ 123 | name: 'remote', 124 | path: TEST_PLUGIN_PATH, 125 | module: requireEnsure(TEST_PLUGIN_PATH), 126 | version: requireEnsure(TEST_PLUGIN_PATH).version, 127 | pluginConfig: server.config.getPlugin('remote'), 128 | }); 129 | 130 | const pluginDetail = await fakeLoadOne(); 131 | await server.system.buildOne(pluginDetail); 132 | 133 | const testPlugin = server.config.getPlugin('remote'); 134 | expect(testPlugin.get('limit')).to.equal(100); 135 | expect(testPlugin.get('$.port')).to.equal(8000); 136 | sinon.restore(); 137 | }); 138 | 139 | it('should return all builtin options when get(\'$\')', () => { 140 | const server = createServer({ 141 | plugins: ['test'], 142 | }); 143 | const { config } = server; 144 | expect(config.getPlugin('test').get('$')).to.eql( 145 | config.get(), 146 | ); 147 | }); 148 | 149 | it('should return plugin schema using getSchema()', async () => { 150 | const server = createServer({ 151 | plugins: [{ 152 | path: TEST_PLUGIN_PATH, 153 | }], 154 | }); 155 | await server.setup(); 156 | const { config } = server; 157 | expect(config.getPlugin('test').getSchema()).to.eql({ limit: { type: 'number', default: 100 } }); 158 | }); 159 | }); 160 | -------------------------------------------------------------------------------- /packages/svrx/__tests__/spec/configure/ui.js: -------------------------------------------------------------------------------- 1 | const expect = require('expect.js'); 2 | const { createServer } = require('../../util'); 3 | 4 | describe('UI Configs', () => { 5 | let uiClientConfig = null; 6 | const server = createServer({ 7 | plugins: [ 8 | { 9 | name: 'ui', 10 | inplace: true, 11 | hooks: { 12 | onCreate({ config }) { 13 | uiClientConfig = config; 14 | }, 15 | }, 16 | }, 17 | ], 18 | }); 19 | const { config } = server; 20 | 21 | it('should consider ui as external plugin', () => { 22 | expect(config.getExternalPlugins().map((p) => p.getInfo('name'))).to.eql(['ui']); 23 | }); 24 | it('should return ui config as builtin config in client side', async () => { 25 | await server.setup(); 26 | // uiClientConfig is actually builtin config here 27 | expect(uiClientConfig.get()).to.eql(config.get()); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /packages/svrx/__tests__/spec/svrx.injector.js: -------------------------------------------------------------------------------- 1 | const request = require('supertest'); 2 | const { Duplex } = require('stream'); 3 | const expect = require('expect.js'); 4 | const Svrx = require('../../lib/svrx'); 5 | 6 | function bufferToStream(buffer) { 7 | const stream = new Duplex(); 8 | stream.push(buffer); 9 | stream.push(null); 10 | return stream; 11 | } 12 | 13 | describe('Injector', () => { 14 | describe('Basic', () => { 15 | const svrx = new Svrx({}); 16 | const { injector } = svrx; 17 | const MARK_TESTING = '__svrx_testing__'; 18 | 19 | injector.add('style', { content: 'body{padding:10px}' }); 20 | injector.add('style', { content: 'body{color:black}' }); 21 | injector.add('script', { content: 'window.test=true;' }); 22 | injector.add('style', { 23 | content: MARK_TESTING, 24 | test(referer) { 25 | return /\.md$/.test(referer); 26 | }, 27 | }); 28 | 29 | it('Integration: Basic', (done) => { 30 | request(svrx.callback()) 31 | .get(svrx.config.get('urls.script')) 32 | .set('accept-encoding', 'identity') 33 | .expect(/window\.test=true/, (err) => { 34 | if (err) return done(err); 35 | return request(svrx.callback()) 36 | .get(svrx.config.get('urls.style')) 37 | .set('accept-encoding', 'identity') 38 | .expect(/body\{padding:10px\}/) 39 | .end(done); 40 | }); 41 | }); 42 | 43 | it('valid file will return no error', () => { 44 | expect(() => { 45 | injector.add('style', { filename: 'content_not_exsits.js' }); 46 | }).to.not.throwError(); 47 | }); 48 | 49 | it('Integration: style join', () => request(svrx.callback()) 50 | .get(svrx.config.get('urls.style')) 51 | .set('accept-encoding', 'identity') 52 | .expect(/body\{padding:10px\}\nbody\{color:black\}/)); 53 | 54 | it('Integration: Gzip content-encoding', () => request(svrx.callback()) 55 | .get(svrx.config.get('urls.script')) 56 | .set('accept-encoding', 'gzip') 57 | .expect('content-encoding', 'gzip') 58 | .expect(/window\.test=true/)); 59 | 60 | it('Integration: Injection Testing', (done) => { 61 | request(svrx.callback()) 62 | .get(svrx.config.get('urls.style')) 63 | .set('accept-encoding', 'identity') 64 | .expect(/body\{padding:10px\}/) 65 | .expect((res) => { 66 | expect(res.text).to.not.match(new RegExp(MARK_TESTING)); 67 | }) 68 | .end((err) => { 69 | if (err) return done(err); 70 | return request(svrx.callback()) 71 | .get(svrx.config.get('urls.style')) 72 | .set('accept-encoding', 'identity') 73 | .set('Referer', 'test.md') 74 | .expect(/body\{padding:10px\}/) 75 | .expect(new RegExp(MARK_TESTING), done); 76 | }); 77 | }); 78 | }); 79 | 80 | describe('Transform content', () => { 81 | const svrx = new Svrx({ 82 | port: 8001, 83 | middlewares: [ 84 | { 85 | onCreate: () => async (ctx, next) => { 86 | switch (ctx.url) { 87 | case '/content': 88 | ctx.set('Content-Type', 'text/html'); 89 | ctx.body = ''; 90 | break; 91 | case '/buffer': 92 | ctx.set('Content-Type', 'text/html'); 93 | ctx.body = Buffer.from(''); 94 | break; 95 | case '/stream': 96 | ctx.set('Content-Type', 'text/html'); 97 | ctx.body = bufferToStream(Buffer.from('')); 98 | break; 99 | default: 100 | next(); 101 | } 102 | }, 103 | }, 104 | ], 105 | }); 106 | const { injector } = svrx; 107 | 108 | it('replace should work', () => { 109 | injector.replace('', (cap) => `${cap}`); 110 | 111 | return request(svrx.callback()) 112 | .get('/content') 113 | .set('accept-encoding', 'identity') 114 | .expect(/ 182 | 183 | mark`; 184 | break; 185 | case '/only-body': 186 | ctx.set('Content-Type', 'text/html'); 187 | ctx.body = ''; 188 | break; 189 | case '/only-body-stream': 190 | ctx.set('Content-Type', 'text/html'); 191 | ctx.body = bufferToStream(Buffer.from('')); 192 | break; 193 | case '/none-of-both': 194 | ctx.set('Content-Type', 'text/html'); 195 | ctx.body = bufferToStream(Buffer.from('Hello,World')); 196 | break; 197 | default: 198 | next(); 199 | } 200 | }, 201 | }, 202 | ], 203 | }); 204 | 205 | 206 | it('only last body will be inject', (done) => { 207 | request(svrx.callback()) 208 | .get('/doc-write-body') 209 | .expect(new RegExp('mark