├── .commitlintrc.js ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .github ├── renovate.json5 └── workflows │ ├── greetings.yml │ └── test.yml ├── .gitignore ├── .husky ├── commit-msg └── pre-commit ├── .lintstagedrc ├── .npmrc ├── .prettierignore ├── .prettierrc ├── LICENSE ├── README.md ├── build.esm.js ├── jest.config.js ├── package.json ├── packages ├── react-imvc-v2 │ ├── .github │ │ └── workflows │ │ │ └── playwright.yml │ ├── .gitignore │ ├── .travis.yml │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── bin │ │ └── scripts.js │ ├── build │ │ ├── assetsHelper.js │ │ ├── babel.js │ │ ├── compileNodeModules.js │ │ ├── createGulpTask.js │ │ ├── createWebpackConfig.js │ │ ├── index.js │ │ ├── setupDevEnv.js │ │ └── util.js │ ├── component │ │ ├── ControllerProxy.js │ │ ├── ErrorBoundary.js │ │ ├── EventWrapper.js │ │ ├── Input.js │ │ ├── Link.js │ │ ├── NavLink.js │ │ ├── OuterClickWrapper.js │ │ ├── Prefetch.js │ │ ├── Script.js │ │ ├── Style.js │ │ ├── ViewManager.js │ │ └── index.js │ ├── config │ │ ├── babel.js │ │ ├── config.defaults.js │ │ └── index.js │ ├── context │ │ └── index.js │ ├── controller │ │ ├── actions.js │ │ └── index.js │ ├── doc │ │ ├── e2e.md │ │ ├── images │ │ │ ├── 01.png │ │ │ ├── 02.png │ │ │ ├── 03.png │ │ │ ├── 04.png │ │ │ ├── 05.png │ │ │ ├── 06.png │ │ │ ├── 07.png │ │ │ ├── 08.png │ │ │ └── 09.png │ │ ├── index.md │ │ └── upgrade.md │ ├── e2e │ │ ├── example.ts │ │ └── lazy.spec.ts │ ├── entry │ │ ├── client.js │ │ └── server.js │ ├── gulpfile.js │ ├── hoc │ │ ├── connect.js │ │ └── lazy.tsx │ ├── hook │ │ ├── index.js │ │ ├── useCtrl.js │ │ ├── useModel.js │ │ ├── useModelActions.js │ │ └── useModelState.js │ ├── index.js │ ├── middleware │ │ ├── cacheView.js │ │ ├── createCache.js │ │ └── shareRoot.js │ ├── mocha-runner.js │ ├── nyc.config.js │ ├── package-lock.json │ ├── package.json │ ├── page │ │ ├── createPageRouter.js │ │ └── view.js │ ├── playwright.config.ts │ ├── playwright │ │ └── index.ts │ ├── pnpm-lock.yaml │ ├── polyfill │ │ ├── console.js │ │ └── index.js │ ├── project │ │ ├── build.js │ │ ├── package.json │ │ ├── preview.js │ │ ├── routes │ │ │ ├── Layout.js │ │ │ ├── index.js │ │ │ └── my_router.js │ │ ├── src │ │ │ ├── __tests__ │ │ │ │ ├── a.test.js │ │ │ │ └── a.test.ts │ │ │ ├── basic-state │ │ │ │ ├── Controller.js │ │ │ │ └── __tests__ │ │ │ │ │ ├── a.test.js │ │ │ │ │ └── a.test.ts │ │ │ ├── batch-refresh │ │ │ │ └── Controller.tsx │ │ │ ├── error-boundary │ │ │ │ └── Controller.js │ │ │ ├── img │ │ │ │ └── react.png │ │ │ ├── index.js │ │ │ ├── lazy │ │ │ │ ├── A.tsx │ │ │ │ ├── B.tsx │ │ │ │ ├── C.tsx │ │ │ │ ├── D.tsx │ │ │ │ ├── E.tsx │ │ │ │ └── controller.tsx │ │ │ ├── life-cycle │ │ │ │ └── Controller.js │ │ │ ├── model │ │ │ │ └── Controller.js │ │ │ ├── prefetch-header │ │ │ │ ├── Controller.tsx │ │ │ │ └── preload.css │ │ │ ├── preload │ │ │ │ ├── Controller.js │ │ │ │ └── style.css │ │ │ ├── render-view │ │ │ │ └── Controller.js │ │ │ ├── static-view-csr │ │ │ │ └── Controller.js │ │ │ └── static-view │ │ │ │ └── Controller.js │ │ ├── start.js │ │ └── tsconfig.json │ ├── scripts │ │ ├── build.js │ │ ├── start.js │ │ └── test.js │ ├── start │ │ ├── babel.js │ │ └── index.js │ ├── test │ │ ├── imvc-test.js │ │ └── util-test.js │ ├── tsconfig.json │ └── util │ │ ├── htmlescape.ts │ │ └── index.ts └── react-imvc-v3 │ ├── .github │ ├── CODE_OF_CONDUCT.md │ ├── CONTRIBUTE.md │ └── workflows │ │ ├── nodejs.yml │ │ └── playwright.yml │ ├── .gitignore │ ├── .prettierrc.json │ ├── CHANGELOG.md │ ├── LICENSE │ ├── README.md │ ├── bin │ └── scripts.js │ ├── build │ ├── createGulpTask.d.ts │ ├── createGulpTask.js │ ├── createWebpackConfig.d.ts │ ├── createWebpackConfig.js │ ├── index.d.ts │ ├── index.js │ ├── setup-dev-env.d.ts │ └── setup-dev-env.js │ ├── component │ ├── ControllerProxy │ │ ├── index.d.ts │ │ └── index.js │ ├── ErrorBoundary │ │ ├── index.d.ts │ │ └── index.js │ ├── EventWrapper │ │ ├── index.d.ts │ │ └── index.js │ ├── Input │ │ ├── index.d.ts │ │ └── index.js │ ├── Link │ │ ├── index.d.ts │ │ └── index.js │ ├── NavLink │ │ ├── index.d.ts │ │ └── index.js │ ├── OuterClickWrapper │ │ ├── index.d.ts │ │ └── index.js │ ├── Prefetch │ │ ├── index.d.ts │ │ └── index.js │ ├── Script │ │ ├── index.d.ts │ │ └── index.js │ ├── Style │ │ ├── index.d.ts │ │ └── index.js │ ├── ViewManager │ │ ├── index.d.ts │ │ └── index.js │ ├── index.d.ts │ └── index.js │ ├── config │ ├── babel.d.ts │ ├── babel.js │ ├── config.defaults.d.ts │ ├── config.defaults.js │ ├── index.d.ts │ └── index.js │ ├── context │ ├── index.d.ts │ └── index.js │ ├── controller │ ├── actions.d.ts │ ├── actions.js │ ├── attachDevToolsIfPossible.d.ts │ ├── attachDevToolsIfPossible.js │ ├── index.d.ts │ └── index.js │ ├── doc │ ├── Config │ │ ├── README.md │ │ ├── detail.md │ │ └── usage.md │ ├── Isomorphic │ │ ├── README.md │ │ ├── api.md │ │ ├── bom.md │ │ ├── cache.md │ │ ├── components.md │ │ ├── context.md │ │ ├── data-fetch.md │ │ ├── data-manage.md │ │ ├── error-handle.md │ │ ├── event-handle.md │ │ ├── hoc.md │ │ ├── hooks.md │ │ ├── life-circle.md │ │ ├── persistence.md │ │ ├── render.md │ │ ├── route.md │ │ ├── sass.md │ │ ├── serverRenderer.md │ │ └── static-file.md │ ├── MIGRATION_V2.md │ ├── MIGRATION_V3.md │ ├── README.V2.md │ ├── README.md │ └── Server │ │ ├── README.md │ │ ├── layout.md │ │ └── middleware.md │ ├── e2e │ ├── example.ts │ └── lazy.spec.ts │ ├── entry │ ├── client.d.ts │ ├── client.js │ ├── server.d.ts │ └── server.js │ ├── hoc │ ├── connect.d.ts │ ├── connect.js │ ├── lazy.js │ └── lazy.ts │ ├── hook │ ├── index.d.ts │ ├── index.js │ ├── useCtrl.d.ts │ ├── useCtrl.js │ ├── useModel.d.ts │ ├── useModel.js │ ├── useModelActions.d.ts │ ├── useModelActions.js │ ├── useModelState.d.ts │ └── useModelState.js │ ├── imvc-types.d.ts │ ├── jest.config.js │ ├── middleware │ ├── cacheView.d.ts │ ├── cacheView.js │ ├── createCache.d.ts │ ├── createCache.js │ ├── shareRoot.d.ts │ └── shareRoot.js │ ├── nyc.config.js │ ├── package-lock.json │ ├── package.json │ ├── page │ ├── createPageRouter.d.ts │ ├── createPageRouter.js │ ├── view.d.ts │ └── view.js │ ├── playwright.config.ts │ ├── playwright │ ├── index.d.ts │ └── index.js │ ├── pnpm-lock.yaml │ ├── project │ ├── imvc.config.tsx │ ├── package.json │ ├── routes │ │ ├── Layout.tsx │ │ ├── index.ts │ │ └── my_router.ts │ ├── src │ │ ├── api-map │ │ │ └── Controller.tsx │ │ ├── api │ │ │ └── Controller.tsx │ │ ├── basic-state │ │ │ └── Controller.tsx │ │ ├── batch-refresh │ │ │ └── Controller.tsx │ │ ├── componentWillCreate │ │ │ └── Controller.tsx │ │ ├── connect │ │ │ └── Controller.tsx │ │ ├── content-hash │ │ │ ├── Controller.tsx │ │ │ ├── OpenSans-Bold.ttf │ │ │ └── style.css │ │ ├── error-boundary │ │ │ └── Controller.tsx │ │ ├── es6_dynamic │ │ │ └── Controller.tsx │ │ ├── es6_import │ │ │ └── Controller.tsx │ │ ├── es6_module │ │ │ └── Controller.tsx │ │ ├── event-wrapper │ │ │ └── Controller.tsx │ │ ├── getInitialState │ │ │ └── Controller.tsx │ │ ├── history │ │ │ └── Controller.tsx │ │ ├── hook │ │ │ ├── Controller.tsx │ │ │ └── Model.ts │ │ ├── img │ │ │ ├── happy.svg │ │ │ └── react.png │ │ ├── index.ts │ │ ├── input │ │ │ └── Controller.tsx │ │ ├── lazy │ │ │ ├── A.tsx │ │ │ ├── B.tsx │ │ │ ├── C.tsx │ │ │ ├── D.tsx │ │ │ ├── E.tsx │ │ │ └── controller.tsx │ │ ├── link-from │ │ │ ├── Controller.tsx │ │ │ └── preload.css │ │ ├── link-to │ │ │ └── Controller.tsx │ │ ├── loading │ │ │ └── Controller.tsx │ │ ├── location │ │ │ └── Controller.tsx │ │ ├── outer-click-wrapper │ │ │ └── Controller.tsx │ │ ├── prefetch-header │ │ │ ├── Controller.tsx │ │ │ └── preload.css │ │ ├── render-view │ │ │ └── Controller.tsx │ │ ├── restapi │ │ │ └── Controller.tsx │ │ ├── scroll │ │ │ └── Controller.tsx │ │ ├── static-view-csr │ │ │ └── Controller.tsx │ │ ├── static-view │ │ │ └── Controller.tsx │ │ ├── style │ │ │ ├── Controller.tsx │ │ │ ├── Dynamic.tsx │ │ │ ├── base.scss │ │ │ ├── controller.scss │ │ │ ├── nav.scss │ │ │ └── preload.css │ │ ├── type.d.ts │ │ └── unscroll │ │ │ └── Controller.tsx │ └── start.ts │ ├── src │ ├── bin │ │ └── scripts.ts │ ├── build │ │ ├── assetsHelper.ts │ │ ├── compileNodeModules.ts │ │ ├── createGulpTask.ts │ │ ├── createWebpackConfig.ts │ │ ├── imvc-style-loader.ts │ │ ├── imvc-style-plugin.ts │ │ ├── index.ts │ │ ├── setupDevEnv.ts │ │ └── util.ts │ ├── component │ │ ├── ControllerProxy.ts │ │ ├── ErrorBoundary.ts │ │ ├── EventWrapper.tsx │ │ ├── Input.ts │ │ ├── Link.tsx │ │ ├── NavLink.tsx │ │ ├── OuterClickWrapper.ts │ │ ├── Prefetch.ts │ │ ├── Script.tsx │ │ ├── Style.tsx │ │ ├── ViewManager.tsx │ │ └── index.ts │ ├── config │ │ ├── babel.ts │ │ ├── config.defaults.ts │ │ └── index.ts │ ├── context │ │ └── index.ts │ ├── controller │ │ ├── actions.ts │ │ └── index.tsx │ ├── entry │ │ ├── client.ts │ │ └── server.ts │ ├── hoc │ │ ├── connect.tsx │ │ └── lazy.tsx │ ├── hook │ │ ├── index.ts │ │ ├── useCtrl.ts │ │ ├── useModel.ts │ │ ├── useModelActions.ts │ │ └── useModelState.ts │ ├── imvc.d.ts │ ├── index.ts │ ├── middleware │ │ ├── cacheView.ts │ │ ├── createCache.ts │ │ └── shareRoot.ts │ ├── page │ │ ├── createPageRouter.tsx │ │ └── view.tsx │ ├── playwright │ │ └── index.ts │ ├── polyfill │ │ ├── console.ts │ │ └── index.ts │ ├── scripts │ │ ├── build.ts │ │ ├── start.ts │ │ └── test.ts │ ├── start │ │ ├── babel.ts │ │ └── index.ts │ └── util │ │ └── index.ts │ ├── start │ ├── babel.d.ts │ ├── babel.js │ ├── index.d.ts │ └── index.js │ ├── test │ ├── build.spec.ts │ ├── component.spec.ts │ ├── config.spec.ts │ ├── config.ts │ ├── controller.spec.ts │ ├── hoc.spec.ts │ ├── hook.spec.ts │ ├── imvc.test.ts │ ├── middleware.spec.ts │ ├── test.d.ts │ ├── util.spec.ts │ └── util │ │ ├── fetchContent.ts │ │ ├── index.ts │ │ ├── kill.ts │ │ └── setTimeOutAsync.ts │ ├── tsconfig.json │ └── util │ ├── index.d.ts │ └── index.js ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── tsconfig.cjs.json ├── tsconfig.esm.json └── tsconfig.json /.commitlintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@commitlint/config-conventional'], 3 | } 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | #root = true 2 | 3 | [*] 4 | indent_style = space 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | max_line_length = 100 10 | indent_size = 2 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | examples/.cache 2 | node_modules 3 | dist 4 | cjs 5 | esm 6 | */**/expected 7 | projects/** -------------------------------------------------------------------------------- /.github/renovate.json5: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["config:base", "schedule:weekly", "group:allNonMajor"], 3 | "labels": ["dependencies"], 4 | "pin": false, 5 | "rangeStrategy": "bump", 6 | "node": false, 7 | "packageRules": [ 8 | { 9 | "depTypeList": ["peerDependencies"], 10 | "enabled": false 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /.github/workflows/greetings.yml: -------------------------------------------------------------------------------- 1 | name: Greetings 2 | 3 | on: [pull_request, issues] 4 | 5 | jobs: 6 | greeting: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/first-interaction@v1 10 | with: 11 | repo-token: ${{ secrets.GITHUB_TOKEN }} 12 | issue-message: 'Thank feedback. We will check it later:-)' 13 | pr-message: 'Thank for your PR. We will check it later:-)' 14 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Lint & Test 2 | 3 | # Controls when the action will run. 4 | on: 5 | # Triggers the workflow on push or pull request events but only for the main branch 6 | push: 7 | branches: [main] 8 | pull_request: 9 | branches: [main] 10 | 11 | # Allows you to run this workflow manually from the Actions tab 12 | workflow_dispatch: 13 | 14 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 15 | jobs: 16 | remesh: 17 | # The type of runner that the job will run on 18 | runs-on: ${{ matrix.os }} 19 | strategy: 20 | matrix: 21 | node-version: [14.x, 16.x, 17.x, 18.x] 22 | os: [ubuntu-latest, macos-latest, windows-latest] 23 | 24 | # Steps represent a sequence of tasks that will be executed as part of the job 25 | steps: 26 | - name: Check out 27 | uses: actions/checkout@v2 28 | 29 | - uses: pnpm/action-setup@v2.0.1 30 | with: 31 | version: '6' 32 | 33 | - name: Setup Node.js ${{ matrix.node-version }} 34 | uses: actions/setup-node@v1.4.4 35 | with: 36 | node-version: ${{ matrix.node-version }} 37 | cache: 'pnpm' 38 | 39 | - name: Init 40 | run: pnpm run init 41 | 42 | - name: Build & Test 43 | run: pnpm run build && pnpm run test 44 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx --no-install commitlint -e 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx --no -- lint-staged 5 | -------------------------------------------------------------------------------- /.lintstagedrc: -------------------------------------------------------------------------------- 1 | { 2 | "*.{json,yml,md,html,js,ts,tsx,vue}": [ 3 | "prettier --write" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry = 'https://registry.npmjs.org/' 2 | prefer-workspace-packages = true 3 | auto-install-peers = true 4 | strict-peer-dependencies = false -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | examples/.cache 2 | node_modules 3 | dist 4 | esm -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "semi": false, 4 | "tabWidth": 4, 5 | "trailingComma": "all", 6 | "singleQuote": true, 7 | "arrowParens": "always", 8 | "jsxSingleQuote": false, 9 | "endOfLine": "lf" 10 | } 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 工业聚 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 | # react-imvc 2 | 3 | [![npm version](https://img.shields.io/npm/v/react-imvc.svg?style=flat-square)](https://www.npmjs.com/package/react-imvc) 4 | [![npm downloads](https://img.shields.io/npm/dm/react-imvc.svg?style=flat-square)](https://www.npmjs.com/package/react-imvc) 5 | [![build status](https://img.shields.io/travis/Lucifier129/react-imvc/master.svg?style=flat-square)](https://travis-ci.org/Lucifier129/react-imvc) 6 | [![coverage](https://img.shields.io/codecov/c/github/Lucifier129/react-imvc/master.svg?style=flat-square)](https://codecov.io/gh/Lucifier129/react-imvc) 7 | 8 | ## Introduction 9 | 10 | `react-imvc` is a server-side rendering solution for React 16+. 11 | 12 | - [v2 documentation](./packages/react-imvc-v2/README.md) 13 | - [v3 documentation](./packages/react-imvc-v3/README.md) 14 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | transform: { 3 | '.(ts|tsx)': 'ts-jest', 4 | }, 5 | testRegex: '(/__tests__/*.|\\.(test|spec))\\.(ts|tsx|js|jsx)$', 6 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], 7 | coveragePathIgnorePatterns: ['/example/', '/node_modules/', '/__tests__/'], 8 | coverageThreshold: { 9 | global: { 10 | branches: 90, 11 | functions: 95, 12 | lines: 95, 13 | statements: 95, 14 | }, 15 | }, 16 | collectCoverageFrom: ['packages/*/src/**/*.{ts,tsx}'], 17 | rootDir: __dirname, 18 | testEnvironment: 'jsdom', 19 | moduleNameMapper: {}, 20 | testPathIgnorePatterns: ['/node_modules/', '/examples/'], 21 | } 22 | -------------------------------------------------------------------------------- /packages/react-imvc-v2/.github/workflows/playwright.yml: -------------------------------------------------------------------------------- 1 | name: Playwright Tests 2 | on: 3 | push: 4 | branches: [main, master] 5 | pull_request: 6 | branches: [main, master] 7 | jobs: 8 | test: 9 | timeout-minutes: 60 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: actions/setup-node@v2 14 | with: 15 | node-version: '14.x' 16 | - name: Install dependencies 17 | run: npm ci 18 | - name: Install Playwright 19 | run: npx playwright install --with-deps 20 | - name: Run Playwright tests 21 | run: npx playwright test 22 | - uses: actions/upload-artifact@v2 23 | if: always() 24 | with: 25 | name: playwright-report 26 | path: playwright-report/ 27 | retention-days: 30 28 | -------------------------------------------------------------------------------- /packages/react-imvc-v2/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | 6 | # production 7 | publish 8 | project_publish 9 | .idea 10 | 11 | # misc 12 | .DS_Store 13 | npm-debug.log 14 | .vscode 15 | _site 16 | mock/test.json 17 | test-results/ 18 | playwright-report/ 19 | -------------------------------------------------------------------------------- /packages/react-imvc-v2/.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '10.16.0' 4 | install: 5 | - npm install 6 | script: 7 | - npm run test 8 | -------------------------------------------------------------------------------- /packages/react-imvc-v2/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 工业聚 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 | -------------------------------------------------------------------------------- /packages/react-imvc-v2/bin/scripts.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | let querystring = require('query-string') 4 | let spawn = require('cross-spawn') 5 | let command = process.argv[2] 6 | let args = process.argv.slice(3) 7 | let [script, params = ''] = command.split('?') 8 | let query = querystring.parse(params) 9 | params = Object.keys(query).map((key) => (query[key] ? `--${key}=${query[key]}` : `--${key}`)) 10 | let result 11 | 12 | let nodeMajorVersion = Number(process.versions.node.split('.')[0]) 13 | 14 | switch (script) { 15 | case 'build': 16 | case 'start': 17 | case 'test': 18 | result = spawn.sync( 19 | 'node', 20 | [ 21 | nodeMajorVersion >= 18 ? '--openssl-legacy-provider' : '', 22 | ...params, 23 | require.resolve('../scripts/' + script), 24 | ...args, 25 | ].filter(Boolean), 26 | { 27 | stdio: 'inherit', 28 | }, 29 | ) 30 | break 31 | default: 32 | console.log('Unknown script "' + script + '".') 33 | break 34 | } 35 | 36 | if (result) { 37 | switch (result.signal) { 38 | case 'SIGKILL': 39 | console.log( 40 | 'The build failed because the process exited too early. ' + 41 | 'This probably means the system ran out of memory or someone called ' + 42 | '`kill -9` on the process.', 43 | ) 44 | process.exit(1) 45 | break 46 | case 'SIGTERM': 47 | console.log( 48 | 'The build failed because the process exited too early. ' + 49 | 'Someone might have called `kill` or `killall`, or the system could ' + 50 | 'be shutting down.', 51 | ) 52 | process.exit(1) 53 | break 54 | } 55 | process.exit(result.status) 56 | } 57 | -------------------------------------------------------------------------------- /packages/react-imvc-v2/build/babel.js: -------------------------------------------------------------------------------- 1 | const babel = require('../config/babel') 2 | const defaultConfig = require('../config/config.defaults') 3 | 4 | require('@babel/register')({ 5 | ...babel(true, defaultConfig), 6 | extensions: ['.es6', '.es', '.jsx', '.js', '.mjs', '.ts', '.tsx'], 7 | }) 8 | 9 | module.exports = require('./index') 10 | -------------------------------------------------------------------------------- /packages/react-imvc-v2/build/compileNodeModules.js: -------------------------------------------------------------------------------- 1 | exports.checkFilename = (filename, rules) => { 2 | if (!rules) { 3 | return !filename.includes('node_modules') 4 | } 5 | let isMatched = false 6 | for (const rule of rules) { 7 | if (typeof rule === 'string') { 8 | if (filename.includes(rule)) { 9 | isMatched = true 10 | break 11 | } 12 | } else if (typeof rule === 'function') { 13 | if (rule(filename)) { 14 | isMatched = true 15 | break 16 | } 17 | } else { 18 | if (rule.test(filename)) { 19 | isMatched = true 20 | break 21 | } 22 | } 23 | } 24 | return isMatched 25 | } 26 | -------------------------------------------------------------------------------- /packages/react-imvc-v2/build/util.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const fs = require('fs') 3 | const { readFromPackageJson } = require('webpack-node-externals/utils') 4 | 5 | function getPackageRelativePath(config) { 6 | if (typeof config !== 'object') { 7 | config = {} 8 | } 9 | const cwd = process.cwd() 10 | if (typeof config.root !== 'string' || config.root === cwd) { 11 | return 'package.json' 12 | } 13 | const rootPackagePath = path.join(config.root, './package.json') 14 | const rootParentPackagePath = path.join(config.root, '../package.json') 15 | if (fs.existsSync(rootPackagePath)) { 16 | return path.relative(cwd, rootPackagePath) 17 | } else if (fs.existsSync(rootParentPackagePath)) { 18 | return path.relative(cwd, rootParentPackagePath) 19 | } 20 | return 'package.json' 21 | } 22 | exports.getPackageRelativePath = getPackageRelativePath 23 | 24 | exports.getExternals = function (config) { 25 | return readFromPackageJson({ fileName: getPackageRelativePath(config) }) 26 | } 27 | 28 | exports.matchExternals = (externals, modulePath) => { 29 | for (let i = 0; i < externals.length; i++) { 30 | if (modulePath.startsWith(externals[i])) { 31 | return true 32 | } 33 | } 34 | return false 35 | } 36 | -------------------------------------------------------------------------------- /packages/react-imvc-v2/component/ControllerProxy.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | /** 4 | * ViewProxy 把 react 组件生命周期同步到 controller 里 5 | * 根据 state 更新 document.title 6 | */ 7 | export default class ControllerProxy extends React.Component { 8 | static ignoreErrors = true 9 | updateDocumentTitle() { 10 | let { controller } = this.props 11 | let { html } = controller.store.getState() 12 | 13 | if (html && html.title !== document.title) { 14 | document.title = html.title 15 | } 16 | } 17 | async emit(method) { 18 | let { controller } = this.props 19 | try { 20 | if (typeof controller[method] === 'function') { 21 | await controller[method]() 22 | } 23 | } catch (error) { 24 | if (controller.errorDidCatch) { 25 | controller.errorDidCatch(error, 'controller') 26 | } else { 27 | throw error 28 | } 29 | } 30 | } 31 | componentDidMount() { 32 | let { controller } = this.props 33 | this.updateDocumentTitle() 34 | if (!controller.meta.hadMounted) { 35 | controller.meta.hadMounted = true 36 | this.emit('componentDidFirstMount') 37 | } 38 | this.emit('componentDidMount') 39 | } 40 | componentWillUnmount() { 41 | this.emit('componentWillUnmount') 42 | } 43 | componentDidUpdate() { 44 | this.updateDocumentTitle() 45 | } 46 | render() { 47 | return null 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /packages/react-imvc-v2/component/ErrorBoundary.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import GlobalContext from '../context' 3 | 4 | // fixed: webpack rebuild lost original React.createElement 5 | let createElement = React.originalCreateElement || React.createElement 6 | 7 | export default class ErrorBoundary extends React.Component { 8 | static ignoreErrors = true 9 | static contextType = GlobalContext 10 | static getDerivedStateFromError() { 11 | return { hasError: true } 12 | } 13 | static defaultProps = { 14 | fallback: null, 15 | } 16 | state = { 17 | hasError: false, 18 | } 19 | catchError(error) { 20 | let { ctrl } = this.context 21 | if (ctrl.errorDidCatch) { 22 | ctrl.errorDidCatch(error, 'view') 23 | } 24 | } 25 | componentDidCatch(error) { 26 | this.catchError(error) 27 | } 28 | render() { 29 | if (this.state.hasError) { 30 | return this.props.fallback 31 | } 32 | let prevCreateElement = React.createElement 33 | React.createElement = createElement 34 | try { 35 | return this.props.children() 36 | } catch (error) { 37 | this.catchError(error) 38 | return this.props.fallback 39 | } finally { 40 | React.createElement = prevCreateElement 41 | } 42 | } 43 | } 44 | 45 | export const withFallback = (fallback) => (InputComponent) => { 46 | function Component(props) { 47 | return createElement(ErrorBoundary, { fallback }, () => createElement(InputComponent, props)) 48 | } 49 | 50 | const displayName = InputComponent.name || InputComponent.displayName 51 | 52 | Component.name = `ErrorBoundary(${displayName})` 53 | 54 | return Component 55 | } 56 | -------------------------------------------------------------------------------- /packages/react-imvc-v2/component/EventWrapper.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import GlobalContext from '../context' 3 | 4 | const isHandler = (key) => /^on[A-Z]+/.test(key) 5 | 6 | export default class EventWrapper extends React.Component { 7 | static contextType = GlobalContext 8 | static defaultProps = { 9 | as: 'div', 10 | } 11 | render() { 12 | const { children, as: Tag, ...props } = this.props 13 | const { handlers } = this.context 14 | for (let key in props) { 15 | if (isHandler(key)) { 16 | const handler = handlers[props[key]] 17 | if ('function' === typeof handler) { 18 | props[key] = handler 19 | } 20 | } 21 | } 22 | return {children} 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/react-imvc-v2/component/NavLink.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import classnames from 'classnames' 3 | import connect from '../hoc/connect' 4 | import Link from './Link' 5 | 6 | const withLocation = connect(({ state }) => { 7 | return { 8 | location: state.location, 9 | } 10 | }) 11 | 12 | export default withLocation(NavLink) 13 | 14 | function NavLink({ isActive: getIsActive, location, className, activeClassName, style, activeStyle, to, ...rest }) { 15 | let isActive = checkActive(getIsActive, to, location) 16 | let finalClassName = classnames(className, isActive && activeClassName) 17 | let finalStyle = isActive ? { ...style, ...activeStyle } : style 18 | return 19 | } 20 | 21 | function checkActive(getIsActive, path, location) { 22 | return getIsActive ? !!getIsActive(path, location) : path === location.raw 23 | } 24 | -------------------------------------------------------------------------------- /packages/react-imvc-v2/component/OuterClickWrapper.js: -------------------------------------------------------------------------------- 1 | import React, { Component, Children } from 'react' 2 | import { findDOMNode } from 'react-dom' 3 | 4 | export default class OuterClickWrapper extends Component { 5 | componentDidMount() { 6 | if (document.addEventListener) { 7 | document.addEventListener('click', this.handleOutterClick) 8 | } else if (document.attachEvent) { 9 | document.attachEvent('onclick', this.handleOutterClick) 10 | } 11 | } 12 | componentWillUnmount() { 13 | if (document.removeEventListener) { 14 | document.removeEventListener('click', this.handleOutterClick) 15 | } else if (document.detachEvent) { 16 | document.detachEvent('onclick', this.handleOutterClick) 17 | } 18 | } 19 | // 结点是否包含结点 20 | contains(rootNode, node) { 21 | if (typeof rootNode.contains === 'function') { 22 | return rootNode.contains(node) 23 | } 24 | while (node) { 25 | if (node === rootNode) { 26 | return true 27 | } 28 | node = node.parentNode 29 | } 30 | return false 31 | } 32 | handleOutterClick = (event) => { 33 | let { onClick } = this.props 34 | if (!onClick) { 35 | return 36 | } 37 | let root = findDOMNode(this) 38 | let isContains = this.contains(root, event.target || event.srcElement) 39 | if (!isContains) { 40 | onClick(event) 41 | } 42 | } 43 | render() { 44 | return Children.only(this.props.children) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /packages/react-imvc-v2/component/Prefetch.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import GlobalContext from '../context' 3 | 4 | export default class Prefetch extends React.Component { 5 | static contextType = GlobalContext 6 | componentDidMount() { 7 | this.context.prefetch(this.props.src) 8 | } 9 | render() { 10 | return null 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/react-imvc-v2/component/Script.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default function Script(props) { 4 | let children = props.children || '' 5 | return ( 6 | 32 | 32 | 28 | {view} 29 | 30 | ) 31 | }, 32 | } 33 | 34 | export default config 35 | -------------------------------------------------------------------------------- /packages/react-imvc-v3/project/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "project", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "yj_gu", 11 | "license": "ISC" 12 | } 13 | -------------------------------------------------------------------------------- /packages/react-imvc-v3/project/routes/Layout.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Script from '../../src/component/Script' 3 | import { RenderProps } from '../../src/' 4 | 5 | export default function Page(props: RenderProps) { 6 | return ( 7 | 8 | 9 | 10 | 14 | 15 | 19 | 20 | {props.title} 21 | 22 | 23 | 24 | 25 | 26 |
27 |