├── website ├── static │ └── .nojekyll ├── CNAME ├── .npmrc ├── docs │ ├── api │ │ ├── __meta__.md │ │ ├── clearEscapeEffect.md │ │ ├── setGlobal.md │ │ ├── setOptioins.md │ │ ├── getGlobal.md │ │ ├── channel.md │ │ └── api.md │ ├── blog │ │ └── __meta__.md │ ├── guide │ │ ├── advance │ │ │ └── __meta__.md │ │ ├── concept │ │ │ └── __meta__.md │ │ ├── demo │ │ │ ├── __meta__.md │ │ │ └── demo.md │ │ └── quickStart │ │ │ └── __meta__.md │ └── plugins │ │ ├── __meta__.md │ │ ├── plugins.md │ │ └── es-module.md ├── babel.config.js ├── i18n │ ├── en │ │ └── docusaurus-plugin-content-docs │ │ │ ├── current │ │ │ ├── guide │ │ │ │ ├── start │ │ │ │ │ └── __meta__.md │ │ │ │ ├── develop │ │ │ │ │ └── __meta__.md │ │ │ │ └── advance │ │ │ │ │ └── __meta__.md │ │ │ ├── community │ │ │ │ └── discuss.md │ │ │ ├── api │ │ │ │ └── index.md │ │ │ └── issues │ │ │ │ └── multipleApp.md │ │ │ └── current.json │ └── zh │ │ ├── docusaurus-theme-classic │ │ ├── footer.json │ │ └── navbar.json │ │ └── docusaurus-plugin-content-docs │ │ └── current.json ├── src │ ├── components │ │ ├── sumarryImg │ │ │ └── index.jsx │ │ ├── config │ │ │ ├── _protectVariable.mdx │ │ │ ├── _basename.mdx │ │ │ ├── _viteConfig.mdx │ │ │ ├── _insulationVariable.mdx │ │ │ └── _domGetter.mdx │ │ ├── Highlight │ │ │ └── index.js │ │ └── lifeCycle │ │ │ ├── _onNotMatchRouter.mdx │ │ │ ├── _afterUnmount.mdx │ │ │ ├── _afterLoad.mdx │ │ │ ├── _beforeLoad.mdx │ │ │ ├── _beforeUnmount.mdx │ │ │ ├── _errorUnmountApp.mdx │ │ │ ├── _errorLoadApp.mdx │ │ │ ├── _errorMountApp.mdx │ │ │ ├── _afterEval.mdx │ │ │ ├── _beforeMount.mdx │ │ │ ├── _afterMount.mdx │ │ │ └── _beforeEval.mdx │ └── README.md ├── plugin │ └── slardar │ │ └── index.js ├── .gitignore ├── README.md ├── .editorconfig └── CHANGELOG.md ├── dev ├── app-angular │ ├── src │ │ ├── assets │ │ │ └── .gitkeep │ │ ├── app │ │ │ ├── app.component.css │ │ │ ├── home │ │ │ │ ├── home.component.css │ │ │ │ ├── home.component.html │ │ │ │ └── home.component.ts │ │ │ ├── dashboard │ │ │ │ ├── dashboard.component.css │ │ │ │ ├── dashboard.component.html │ │ │ │ └── dashboard.component.ts │ │ │ ├── pageNotFound │ │ │ │ ├── pageNotFound.component.css │ │ │ │ ├── pageNotFound.component.html │ │ │ │ └── pageNotFound.component.ts │ │ │ ├── productList │ │ │ │ ├── productList.component.css │ │ │ │ ├── productList.component.html │ │ │ │ └── productList.component.ts │ │ │ ├── topBar │ │ │ │ ├── topBar.component.css │ │ │ │ ├── topBar.component.ts │ │ │ │ └── topBar.component.html │ │ │ ├── app.component.ts │ │ │ ├── product.ts │ │ │ └── app.component.spec.ts │ │ ├── styles.less │ │ ├── environments │ │ │ ├── environment.prod.ts │ │ │ └── environment.ts │ │ ├── favicon.ico │ │ ├── typings │ │ │ └── index.d.ts │ │ ├── index.html │ │ ├── main.ts │ │ └── test.ts │ ├── .editorconfig │ ├── e2e │ │ ├── tsconfig.json │ │ ├── src │ │ │ ├── app.po.ts │ │ │ └── app.e2e-spec.ts │ │ └── protractor.conf.js │ ├── tsconfig.app.json │ ├── tsconfig.spec.json │ ├── tsconfig.json │ ├── .browserslistrc │ ├── README.md │ └── karma.conf.js ├── app-react-16 │ ├── public │ │ ├── dynamic.js │ │ └── index.html │ ├── webpack.config.babel.js │ ├── src │ │ ├── PageNotFound.tsx │ │ ├── ErrorBoundary.tsx │ │ ├── reportWebVitals.js │ │ ├── App.less │ │ └── index.tsx │ ├── typings.d.ts │ ├── .babelrc │ └── tsconfig.json ├── app-main │ ├── src │ │ ├── components │ │ │ ├── Link │ │ │ │ ├── index.less │ │ │ │ └── index.tsx │ │ │ ├── loadApp │ │ │ │ ├── index.less │ │ │ │ └── loadAppFunc.tsx │ │ │ ├── root │ │ │ │ └── index.less │ │ │ └── PageNotFound │ │ │ │ └── index.tsx │ │ ├── static │ │ │ ├── favicon.ico │ │ │ ├── img │ │ │ │ └── Garfish.png │ │ │ └── icons │ │ │ │ ├── Angular.svg │ │ │ │ ├── Vue.svg │ │ │ │ └── New.svg │ │ ├── less │ │ │ └── variable.less │ │ ├── index.tsx │ │ ├── store.ts │ │ └── garfishInit.ts │ ├── webpack.config.babel.js │ ├── public │ │ ├── favicon.ico │ │ └── index.html │ ├── typings.d.ts │ ├── .babelrc │ └── tsconfig.json ├── app-react-17 │ ├── src │ │ ├── components │ │ │ ├── Link │ │ │ │ ├── index.less │ │ │ │ └── index.tsx │ │ │ ├── lazyComponent.tsx │ │ │ ├── PageNotFound.tsx │ │ │ ├── ErrorBoundary.tsx │ │ │ └── detail │ │ │ │ ├── index.less │ │ │ │ └── index.tsx │ │ ├── less │ │ │ └── variable.less │ │ ├── reportWebVitals.js │ │ └── constant.ts │ ├── webpack.config.babel.js │ ├── public │ │ └── index.html │ ├── typings.d.ts │ ├── .babelrc │ └── tsconfig.json ├── app-vue-vite │ ├── .env.production │ ├── .env │ ├── public │ │ └── favicon.ico │ ├── src │ │ ├── assets │ │ │ └── logo.png │ │ ├── shimsVue.d.ts │ │ ├── main.ts │ │ ├── App.vue │ │ └── components │ │ │ └── HelloWorld.vue │ ├── index.html │ ├── README.md │ ├── package.json │ └── vite.config.ts ├── app-react-18 │ ├── webpack.config.babel.js │ ├── public │ │ └── index.html │ ├── src │ │ ├── PageNotFound.tsx │ │ ├── ErrorBoundary.tsx │ │ ├── reportWebVitals.js │ │ ├── root.tsx │ │ └── App.less │ ├── typings.d.ts │ ├── .babelrc │ └── tsconfig.json ├── app-vue-2 │ ├── babel.config.js │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ └── remoteModule.js │ ├── src │ │ ├── assets │ │ │ └── logo.png │ │ ├── store │ │ │ ├── index.js │ │ │ └── store.js │ │ ├── shimsVue.d.ts │ │ └── components │ │ │ ├── HelloGarfish.vue │ │ │ └── alertError.vue │ ├── README.md │ ├── vue.config.js │ └── tsconfig.json ├── app-vue │ ├── babel.config.js │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ └── remoteModule.js │ ├── src │ │ ├── assets │ │ │ └── logo.png │ │ ├── store │ │ │ ├── index.js │ │ │ └── store.js │ │ ├── App.vue │ │ └── components │ │ │ └── HelloWorld.vue │ ├── README.md │ ├── vue.config.js │ └── tsconfig.json ├── app-vue-3 │ ├── public │ │ ├── favicon.ico │ │ └── index.html │ ├── src │ │ ├── assets │ │ │ └── logo.png │ │ ├── shimsVue.d.ts │ │ ├── components │ │ │ ├── HelloGarfish.vue │ │ │ └── alertError.vue │ │ └── store.js │ ├── README.md │ ├── vue.config.js │ └── tsconfig.json ├── app-esm │ ├── package.json │ └── src │ │ ├── index.js │ │ └── index.html ├── util.js └── config.json ├── .npmrc ├── cypress.json ├── packages ├── test-suite │ ├── src │ │ └── index.ts │ └── tsup.config.ts ├── bridge-vue-v2 │ ├── src │ │ ├── index.ts │ │ └── types.d.ts │ ├── tsup.config.ts │ └── LICENSE ├── bridge-vue-v3 │ ├── src │ │ ├── index.ts │ │ └── types.d.ts │ ├── tsup.config.ts │ ├── README.md │ └── LICENSE ├── es-module │ ├── __tests__ │ │ └── case │ │ │ ├── importCheck │ │ │ ├── m3.js │ │ │ ├── m2.js │ │ │ └── m1.js │ │ │ ├── exportNamespace │ │ │ ├── m4.js │ │ │ ├── m5.js │ │ │ ├── m3.js │ │ │ ├── m2.js │ │ │ └── m1.js │ │ │ ├── variableCheck │ │ │ └── m2.js │ │ │ ├── dynamicImport │ │ │ ├── m2.js │ │ │ └── m1.js │ │ │ ├── executionOrder │ │ │ ├── m2.js │ │ │ ├── m3.js │ │ │ └── m1.js │ │ │ ├── strictModeCheck │ │ │ └── m1.js │ │ │ ├── resourceRedirect │ │ │ └── m1.js │ │ │ ├── importMeta │ │ │ └── m1.js │ │ │ ├── circularReference │ │ │ ├── m2.js │ │ │ └── m1.js │ │ │ └── exportDeclaration │ │ │ ├── m2.js │ │ │ └── m1.js │ ├── .npmignore │ ├── src │ │ └── index.ts │ ├── tsup.config.ts │ ├── README.md │ └── LICENSE ├── garfish │ ├── __tests__ │ │ ├── resources │ │ │ ├── dynamic.js │ │ │ ├── vueIndex.js │ │ │ ├── reactApp.html │ │ │ └── vueApp.html │ │ └── __snapshots__ │ │ │ └── run.spec.ts.snap │ ├── .npmignore │ ├── tsup.config.ts │ ├── src │ │ └── index.ts │ ├── index.d.ts │ ├── README.md │ └── LICENSE ├── core │ ├── .npmignore │ ├── src │ │ ├── index.ts │ │ └── plugins │ │ │ ├── lifecycle.ts │ │ │ └── performance │ │ │ └── index.ts │ ├── tsup.config.ts │ ├── __tests__ │ │ ├── __snapshots__ │ │ │ └── preloadPlugin.spec.ts.snap │ │ └── resources │ │ │ ├── reactApp.html │ │ │ ├── vueApp.html │ │ │ ├── vue3App.html │ │ │ └── asyncProviderApp.html │ └── LICENSE ├── hooks │ ├── .npmignore │ ├── tsup.config.ts │ ├── src │ │ ├── index.ts │ │ ├── asyncHook.ts │ │ └── syncHook.ts │ ├── README.md │ └── package.json ├── loader │ ├── .npmignore │ ├── tsup.config.ts │ ├── src │ │ ├── managers │ │ │ └── module.ts │ │ └── utils.ts │ ├── LICENSE │ └── package.json ├── router │ ├── .npmignore │ ├── tsup.config.ts │ ├── README.md │ ├── src │ │ └── utils │ │ │ └── index.ts │ ├── LICENSE │ └── package.json ├── utils │ ├── .npmignore │ ├── tsup.config.ts │ ├── src │ │ ├── logger.ts │ │ ├── index.ts │ │ └── garfish.ts │ ├── README.md │ └── LICENSE ├── browser-vm │ ├── .npmignore │ ├── src │ │ ├── index.ts │ │ ├── symbolTypes.ts │ │ ├── modules │ │ │ ├── uiEvent.ts │ │ │ └── mutationObserver.ts │ │ ├── dynamicNode │ │ │ └── processParams.ts │ │ ├── lifecycle.ts │ │ └── types.ts │ ├── __tests__ │ │ ├── resources │ │ │ ├── links │ │ │ │ ├── suffix.css │ │ │ │ ├── no-suffix │ │ │ │ └── no-content-type-suffix │ │ │ └── scripts │ │ │ │ ├── jsonp │ │ │ │ ├── no-type-jsonp │ │ │ │ ├── content-type-jsonp │ │ │ │ └── event-task.js │ │ └── modules │ │ │ └── uiEvent.spec.ts │ ├── tsup.config.ts │ └── LICENSE ├── browser-snapshot │ ├── .npmignore │ ├── tsup.config.ts │ ├── README.md │ ├── __tests__ │ │ ├── __snapshots__ │ │ │ ├── sandbox.spec.ts.snap │ │ │ ├── window.spec.ts.snap │ │ │ └── style.spec.ts.snap │ │ ├── event.spec.ts │ │ └── sandbox.spec.ts │ ├── src │ │ ├── patchers │ │ │ ├── history.ts │ │ │ ├── webpackjsonp.ts │ │ │ ├── multithread.ts │ │ │ └── interval.ts │ │ └── globalExtensions.ts │ └── LICENSE ├── remote-module │ ├── .npmignore │ ├── tsup.config.ts │ ├── src │ │ ├── apis │ │ │ ├── esModule.ts │ │ │ └── preload.ts │ │ ├── index.ts │ │ ├── hooks.ts │ │ └── actuator.ts │ └── LICENSE ├── bridge-react-v18 │ ├── src │ │ └── index.ts │ ├── tsup.config.ts │ ├── README.md │ └── LICENSE ├── bridge-react │ ├── src │ │ └── index.ts │ ├── tsup.config.ts │ ├── README.md │ └── LICENSE └── global.d.ts ├── renovate.json ├── pnpm-workspace.yaml ├── .husky ├── pre-commit └── commit-msg ├── scripts ├── devCypress.js ├── utils.js ├── e2e.js └── verifyCommit.js ├── .prettierrc ├── cypress ├── fixtures │ └── example.json ├── plugins │ └── index.js ├── support │ ├── index.js │ └── commands.js └── integration │ ├── common.js │ └── 2-the-vm-sandbox │ └── set-global-variable.spec.js ├── .editorconfig ├── .github └── ISSUE_TEMPLATE │ └── config.yml ├── babel.config.js ├── .pnpmfile.cjs ├── .ls-lint.yml ├── .gitignore ├── tsconfig.json └── jest.config.js /website/static/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /website/CNAME: -------------------------------------------------------------------------------- 1 | garfishjs.org 2 | -------------------------------------------------------------------------------- /dev/app-angular/src/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dev/app-react-16/public/dynamic.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dev/app-angular/src/app/app.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dev/app-main/src/components/Link/index.less: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry=https://registry.npmjs.org 2 | -------------------------------------------------------------------------------- /dev/app-angular/src/app/home/home.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dev/app-react-17/src/components/Link/index.less: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /cypress.json: -------------------------------------------------------------------------------- 1 | { 2 | "projectId": "yren6i" 3 | } 4 | -------------------------------------------------------------------------------- /dev/app-vue-vite/.env.production: -------------------------------------------------------------------------------- 1 | VITE_APP_BASE='' 2 | -------------------------------------------------------------------------------- /dev/app-angular/src/app/dashboard/dashboard.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /website/.npmrc: -------------------------------------------------------------------------------- 1 | registry = 'https://registry.npmjs.org' 2 | -------------------------------------------------------------------------------- /dev/app-angular/src/app/pageNotFound/pageNotFound.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dev/app-angular/src/app/productList/productList.component.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/test-suite/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './mock'; 2 | -------------------------------------------------------------------------------- /dev/app-vue-vite/.env: -------------------------------------------------------------------------------- 1 | VITE_APP_BASE='http://localhost:8091/' 2 | -------------------------------------------------------------------------------- /dev/app-angular/src/app/home/home.component.html: -------------------------------------------------------------------------------- 1 |

This is Home.

2 | -------------------------------------------------------------------------------- /dev/app-angular/src/styles.less: -------------------------------------------------------------------------------- 1 | .link { 2 | position: relative; 3 | } 4 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /packages/bridge-vue-v2/src/index.ts: -------------------------------------------------------------------------------- 1 | export { vueBridge } from './vueBridge'; 2 | -------------------------------------------------------------------------------- /packages/bridge-vue-v3/src/index.ts: -------------------------------------------------------------------------------- 1 | export { vueBridge } from './vueBridge'; 2 | -------------------------------------------------------------------------------- /packages/es-module/__tests__/case/importCheck/m3.js: -------------------------------------------------------------------------------- 1 | export const _name = 'm2'; 2 | -------------------------------------------------------------------------------- /packages/garfish/__tests__/resources/dynamic.js: -------------------------------------------------------------------------------- 1 | const hello = 'Hello World'; 2 | -------------------------------------------------------------------------------- /packages/es-module/__tests__/case/exportNamespace/m4.js: -------------------------------------------------------------------------------- 1 | export const name = 'm4'; 2 | -------------------------------------------------------------------------------- /packages/es-module/__tests__/case/importCheck/m2.js: -------------------------------------------------------------------------------- 1 | export { name } from './m3.js'; 2 | -------------------------------------------------------------------------------- /packages/es-module/__tests__/case/variableCheck/m2.js: -------------------------------------------------------------------------------- 1 | export const name = ['m2']; 2 | -------------------------------------------------------------------------------- /dev/app-main/webpack.config.babel.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./webpack.config.js'); 2 | -------------------------------------------------------------------------------- /dev/app-react-16/webpack.config.babel.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./webpack.config.js'); 2 | -------------------------------------------------------------------------------- /dev/app-react-17/webpack.config.babel.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./webpack.config.js'); 2 | -------------------------------------------------------------------------------- /dev/app-react-18/webpack.config.babel.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./webpack.config.js'); 2 | -------------------------------------------------------------------------------- /website/docs/api/__meta__.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: API 3 | collapsed: false 4 | order: 4 5 | --- 6 | -------------------------------------------------------------------------------- /dev/app-angular/src/app/dashboard/dashboard.component.html: -------------------------------------------------------------------------------- 1 |

This is Dashboard.

2 | 3 | -------------------------------------------------------------------------------- /dev/app-angular/src/app/pageNotFound/pageNotFound.component.html: -------------------------------------------------------------------------------- 1 | Oops, 404... 2 | -------------------------------------------------------------------------------- /dev/app-angular/src/app/productList/productList.component.html: -------------------------------------------------------------------------------- 1 |

This is List.

2 | 3 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | 2 | packages: 3 | - 'packages/**' 4 | - 'dev/**' 5 | - 'website' 6 | -------------------------------------------------------------------------------- /website/docs/blog/__meta__.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: blog 3 | collapsed: false 4 | order: 4 5 | --- 6 | -------------------------------------------------------------------------------- /packages/core/.npmignore: -------------------------------------------------------------------------------- 1 | src/ 2 | node_modules/ 3 | __tests__/ 4 | .npmignore 5 | tsup.config.ts 6 | -------------------------------------------------------------------------------- /packages/garfish/.npmignore: -------------------------------------------------------------------------------- 1 | src/ 2 | node_modules/ 3 | __tests__/ 4 | .npmignore 5 | tsup.config.ts 6 | -------------------------------------------------------------------------------- /packages/hooks/.npmignore: -------------------------------------------------------------------------------- 1 | src/ 2 | node_modules/ 3 | __tests__/ 4 | .npmignore 5 | tsup.config.ts 6 | -------------------------------------------------------------------------------- /packages/loader/.npmignore: -------------------------------------------------------------------------------- 1 | src/ 2 | node_modules/ 3 | __tests__/ 4 | .npmignore 5 | tsup.config.ts 6 | -------------------------------------------------------------------------------- /packages/router/.npmignore: -------------------------------------------------------------------------------- 1 | src/ 2 | node_modules/ 3 | __tests__/ 4 | .npmignore 5 | tsup.config.ts 6 | -------------------------------------------------------------------------------- /packages/utils/.npmignore: -------------------------------------------------------------------------------- 1 | src/ 2 | node_modules/ 3 | __tests__/ 4 | .npmignore 5 | tsup.config.ts 6 | -------------------------------------------------------------------------------- /website/docs/guide/advance/__meta__.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 进阶 3 | collapsed: true 4 | order: 5 5 | --- 6 | -------------------------------------------------------------------------------- /website/docs/guide/concept/__meta__.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 核心能力 3 | collapsed: true 4 | order: 4 5 | --- 6 | -------------------------------------------------------------------------------- /website/docs/guide/demo/__meta__.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 接入指南 3 | collapsed: true 4 | order: 3 5 | --- 6 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx ls-lint && npx lint-staged 5 | -------------------------------------------------------------------------------- /dev/app-vue-2/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['@vue/cli-plugin-babel/preset'], 3 | }; 4 | -------------------------------------------------------------------------------- /dev/app-vue/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['@vue/cli-plugin-babel/preset'], 3 | }; 4 | -------------------------------------------------------------------------------- /packages/browser-vm/.npmignore: -------------------------------------------------------------------------------- 1 | src/ 2 | node_modules/ 3 | __tests__/ 4 | .npmignore 5 | tsup.config.ts 6 | -------------------------------------------------------------------------------- /packages/es-module/.npmignore: -------------------------------------------------------------------------------- 1 | src/ 2 | node_modules/ 3 | __tests__/ 4 | .npmignore 5 | tsup.config.ts 6 | -------------------------------------------------------------------------------- /packages/es-module/__tests__/case/dynamicImport/m2.js: -------------------------------------------------------------------------------- 1 | export const name = 'm2'; 2 | export default [1]; 3 | -------------------------------------------------------------------------------- /packages/es-module/__tests__/case/importCheck/m1.js: -------------------------------------------------------------------------------- 1 | // name 不存在,会报错 2 | import { name } from './m3.js'; 3 | -------------------------------------------------------------------------------- /scripts/devCypress.js: -------------------------------------------------------------------------------- 1 | const { runAllExample } = require('./runE2eProject.js'); 2 | 3 | runAllExample(); 4 | -------------------------------------------------------------------------------- /website/docs/guide/quickStart/__meta__.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 快速上手 3 | collapsed: false 4 | order: 1 5 | --- 6 | -------------------------------------------------------------------------------- /dev/app-vue/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacob-lcs/garfish/HEAD/dev/app-vue/public/favicon.ico -------------------------------------------------------------------------------- /packages/browser-snapshot/.npmignore: -------------------------------------------------------------------------------- 1 | src/ 2 | node_modules/ 3 | __tests__/ 4 | .npmignore 5 | tsup.config.ts 6 | -------------------------------------------------------------------------------- /packages/es-module/__tests__/case/exportNamespace/m5.js: -------------------------------------------------------------------------------- 1 | export * from './m3.js'; 2 | export * from './m4.js'; 3 | -------------------------------------------------------------------------------- /packages/remote-module/.npmignore: -------------------------------------------------------------------------------- 1 | src/ 2 | node_modules/ 3 | __tests__/ 4 | .npmignore 5 | tsup.config.ts 6 | -------------------------------------------------------------------------------- /website/docs/plugins/__meta__.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: garfish-plugins 3 | collapsed: false 4 | order: 1 5 | --- 6 | -------------------------------------------------------------------------------- /dev/app-angular/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true, 3 | }; 4 | -------------------------------------------------------------------------------- /dev/app-angular/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacob-lcs/garfish/HEAD/dev/app-angular/src/favicon.ico -------------------------------------------------------------------------------- /dev/app-main/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacob-lcs/garfish/HEAD/dev/app-main/public/favicon.ico -------------------------------------------------------------------------------- /dev/app-vue-2/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacob-lcs/garfish/HEAD/dev/app-vue-2/public/favicon.ico -------------------------------------------------------------------------------- /dev/app-vue-3/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacob-lcs/garfish/HEAD/dev/app-vue-3/public/favicon.ico -------------------------------------------------------------------------------- /dev/app-vue/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacob-lcs/garfish/HEAD/dev/app-vue/src/assets/logo.png -------------------------------------------------------------------------------- /dev/app-main/src/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacob-lcs/garfish/HEAD/dev/app-main/src/static/favicon.ico -------------------------------------------------------------------------------- /dev/app-vue-2/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacob-lcs/garfish/HEAD/dev/app-vue-2/src/assets/logo.png -------------------------------------------------------------------------------- /dev/app-vue-3/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacob-lcs/garfish/HEAD/dev/app-vue-3/src/assets/logo.png -------------------------------------------------------------------------------- /dev/app-vue-vite/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacob-lcs/garfish/HEAD/dev/app-vue-vite/public/favicon.ico -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "singleQuote": true, 4 | "printWidth": 80, 5 | "trailingComma": "all" 6 | } 7 | 8 | -------------------------------------------------------------------------------- /dev/app-vue-vite/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacob-lcs/garfish/HEAD/dev/app-vue-vite/src/assets/logo.png -------------------------------------------------------------------------------- /packages/es-module/__tests__/case/executionOrder/m2.js: -------------------------------------------------------------------------------- 1 | expect(globalThis.orderIndex).toBe(0); 2 | globalThis.orderIndex++; 3 | -------------------------------------------------------------------------------- /packages/es-module/__tests__/case/executionOrder/m3.js: -------------------------------------------------------------------------------- 1 | expect(globalThis.orderIndex).toBe(1); 2 | globalThis.orderIndex++; 3 | -------------------------------------------------------------------------------- /website/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | }; 4 | -------------------------------------------------------------------------------- /dev/app-main/src/static/img/Garfish.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacob-lcs/garfish/HEAD/dev/app-main/src/static/img/Garfish.png -------------------------------------------------------------------------------- /packages/bridge-react-v18/src/index.ts: -------------------------------------------------------------------------------- 1 | export { reactBridge } from './reactBridge'; 2 | export type { PropsInfo } from './types'; 3 | -------------------------------------------------------------------------------- /packages/bridge-react/src/index.ts: -------------------------------------------------------------------------------- 1 | export { reactBridge } from './reactBridge'; 2 | export type { PropsInfo } from './types'; 3 | -------------------------------------------------------------------------------- /packages/core/src/index.ts: -------------------------------------------------------------------------------- 1 | export type { interfaces } from './interface'; 2 | export { Garfish as default } from './garfish'; 3 | -------------------------------------------------------------------------------- /packages/global.d.ts: -------------------------------------------------------------------------------- 1 | declare const __DEV__: boolean; 2 | declare const __TEST__: boolean; 3 | declare const __VERSION__: string; 4 | -------------------------------------------------------------------------------- /website/docs/plugins/plugins.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Garfish 插件 3 | slug: /garfish-plugins 4 | order: 2 5 | --- 6 | 7 | ## Garfish 插件 8 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | export HUSKY_GIT_PARAMS=$1 5 | node scripts/verifyCommit.js 6 | -------------------------------------------------------------------------------- /dev/app-main/src/components/loadApp/index.less: -------------------------------------------------------------------------------- 1 | .app-item { 2 | width: 50%; 3 | text-align: center; 4 | padding: 0 20px; 5 | } 6 | -------------------------------------------------------------------------------- /dev/app-main/src/less/variable.less: -------------------------------------------------------------------------------- 1 | @import '../../node_modules/@arco-design/web-react/dist/css/index.less'; 2 | 3 | @prefix: main-app; -------------------------------------------------------------------------------- /packages/browser-vm/src/index.ts: -------------------------------------------------------------------------------- 1 | export { GarfishBrowserVm } from './pluginify'; 2 | export { Sandbox as default } from './sandbox'; 3 | -------------------------------------------------------------------------------- /packages/es-module/src/index.ts: -------------------------------------------------------------------------------- 1 | export { Runtime as default } from './runtime'; 2 | export { GarfishEsModule } from './pluginify'; 3 | -------------------------------------------------------------------------------- /packages/browser-vm/__tests__/resources/links/suffix.css: -------------------------------------------------------------------------------- 1 | /* suffix test case */ 2 | #root{ 3 | color: red; 4 | background: blue; 5 | } 6 | -------------------------------------------------------------------------------- /packages/browser-vm/__tests__/resources/links/no-suffix: -------------------------------------------------------------------------------- 1 | /* no-suffix test case */ 2 | #root{ 3 | color: red; 4 | background: yellow; 5 | } 6 | -------------------------------------------------------------------------------- /website/i18n/en/docusaurus-plugin-content-docs/current/guide/start/__meta__.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: guide 3 | collapsed: false 4 | order: 1 5 | --- 6 | -------------------------------------------------------------------------------- /dev/app-react-17/src/less/variable.less: -------------------------------------------------------------------------------- 1 | @import '../../node_modules/@arco-design/web-react/dist/css/index.less'; 2 | 3 | @prefix: sub-app-react17; 4 | -------------------------------------------------------------------------------- /website/i18n/en/docusaurus-plugin-content-docs/current/guide/develop/__meta__.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Quick start 3 | collapsed: true 4 | order: 2 5 | --- 6 | -------------------------------------------------------------------------------- /dev/app-main/src/components/root/index.less: -------------------------------------------------------------------------------- 1 | .loadApp-wrapper { 2 | display: flex; 3 | justify-content: space-around; 4 | padding-top: 60px; 5 | } 6 | -------------------------------------------------------------------------------- /dev/app-react-17/src/components/lazyComponent.tsx: -------------------------------------------------------------------------------- 1 | export default function () { 2 | return

React sub App lazyComponent

; 3 | } 4 | -------------------------------------------------------------------------------- /packages/es-module/__tests__/case/strictModeCheck/m1.js: -------------------------------------------------------------------------------- 1 | function a() { 2 | return function b() { 3 | with ({}) { 4 | } 5 | }; 6 | } 7 | a(); 8 | -------------------------------------------------------------------------------- /website/i18n/en/docusaurus-plugin-content-docs/current/guide/advance/__meta__.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Advanced features 3 | collapsed: true 4 | order: 3 5 | --- 6 | -------------------------------------------------------------------------------- /packages/core/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import pkg from './package.json'; 2 | import { baseTsup } from '../../tsup.config'; 3 | 4 | export const tsup = baseTsup(pkg); 5 | -------------------------------------------------------------------------------- /packages/es-module/__tests__/case/exportNamespace/m3.js: -------------------------------------------------------------------------------- 1 | export const name = 'm3'; 2 | export const a = [1]; 3 | export const b = [2]; 4 | export default [3]; 5 | -------------------------------------------------------------------------------- /packages/hooks/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import pkg from './package.json'; 2 | import { baseTsup } from '../../tsup.config'; 3 | 4 | export const tsup = baseTsup(pkg); 5 | -------------------------------------------------------------------------------- /packages/loader/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import pkg from './package.json'; 2 | import { baseTsup } from '../../tsup.config'; 3 | 4 | export const tsup = baseTsup(pkg); 5 | -------------------------------------------------------------------------------- /packages/router/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import pkg from './package.json'; 2 | import { baseTsup } from '../../tsup.config'; 3 | 4 | export const tsup = baseTsup(pkg); 5 | -------------------------------------------------------------------------------- /packages/utils/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import pkg from './package.json'; 2 | import { baseTsup } from '../../tsup.config'; 3 | 4 | export const tsup = baseTsup(pkg); 5 | -------------------------------------------------------------------------------- /packages/bridge-react/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import pkg from './package.json'; 2 | import { baseTsup } from '../../tsup.config'; 3 | 4 | export const tsup = baseTsup(pkg); 5 | -------------------------------------------------------------------------------- /packages/browser-vm/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import pkg from './package.json'; 2 | import { baseTsup } from '../../tsup.config'; 3 | 4 | export const tsup = baseTsup(pkg); 5 | -------------------------------------------------------------------------------- /packages/es-module/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import pkg from './package.json'; 2 | import { baseTsup } from '../../tsup.config'; 3 | 4 | export const tsup = baseTsup(pkg); 5 | -------------------------------------------------------------------------------- /packages/test-suite/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import pkg from './package.json'; 2 | import { baseTsup } from '../../tsup.config'; 3 | 4 | export const tsup = baseTsup(pkg); 5 | -------------------------------------------------------------------------------- /packages/bridge-react-v18/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import pkg from './package.json'; 2 | import { baseTsup } from '../../tsup.config'; 3 | 4 | export const tsup = baseTsup(pkg); 5 | -------------------------------------------------------------------------------- /packages/bridge-vue-v2/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import pkg from './package.json'; 2 | import { baseTsup } from '../../tsup.config'; 3 | 4 | export const tsup = baseTsup(pkg); 5 | -------------------------------------------------------------------------------- /packages/bridge-vue-v3/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import pkg from './package.json'; 2 | import { baseTsup } from '../../tsup.config'; 3 | 4 | export const tsup = baseTsup(pkg); 5 | -------------------------------------------------------------------------------- /packages/browser-snapshot/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import pkg from './package.json'; 2 | import { baseTsup } from '../../tsup.config'; 3 | 4 | export const tsup = baseTsup(pkg); 5 | -------------------------------------------------------------------------------- /packages/remote-module/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import pkg from './package.json'; 2 | import { baseTsup } from '../../tsup.config'; 3 | 4 | export const tsup = baseTsup(pkg); 5 | -------------------------------------------------------------------------------- /dev/app-angular/src/typings/index.d.ts: -------------------------------------------------------------------------------- 1 | export {}; 2 | declare global { 3 | interface Window { 4 | __GARFISH__: boolean; 5 | __GARFISH_EXPORTS__: any; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/browser-vm/__tests__/resources/links/no-content-type-suffix: -------------------------------------------------------------------------------- 1 | /* no-content-type-suffix test case */ 2 | #root{ 3 | color: red; 4 | background: gray; 5 | } 6 | 7 | -------------------------------------------------------------------------------- /website/docs/plugins/es-module.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Garfish es-module plugins 3 | slug: /garfish-plugins/es-module.md 4 | order: 1 5 | --- 6 | 7 | ## garfish es-module plugin 8 | -------------------------------------------------------------------------------- /packages/browser-vm/__tests__/resources/scripts/jsonp: -------------------------------------------------------------------------------- 1 | 2 | const testInfo = { 3 | key: 'jsonpVariable', 4 | value: true 5 | }; 6 | 7 | window[testInfo.key] = testInfo.value; 8 | -------------------------------------------------------------------------------- /packages/browser-vm/__tests__/resources/scripts/no-type-jsonp: -------------------------------------------------------------------------------- 1 | 2 | const testInfo = { 3 | key: 'noTypeJsonp', 4 | value: true 5 | }; 6 | 7 | window[testInfo.key] = testInfo.value; 8 | -------------------------------------------------------------------------------- /dev/app-vue-2/src/store/index.js: -------------------------------------------------------------------------------- 1 | import store from './store'; 2 | import Vue from 'vue'; 3 | import Vuex from 'vuex'; 4 | 5 | Vue.use(Vuex); 6 | 7 | export default new Vuex.Store(store); 8 | -------------------------------------------------------------------------------- /dev/app-vue/src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Vuex from 'vuex'; 3 | import store from './store'; 4 | 5 | Vue.use(Vuex); 6 | 7 | export default new Vuex.Store(store); 8 | -------------------------------------------------------------------------------- /packages/garfish/__tests__/resources/vueIndex.js: -------------------------------------------------------------------------------- 1 | const dynamicScript = document.createElement('script'); 2 | dynamicScript.src = 'dynamic.js'; 3 | document.head.appendChild(dynamicScript); 4 | -------------------------------------------------------------------------------- /packages/browser-vm/__tests__/resources/scripts/content-type-jsonp: -------------------------------------------------------------------------------- 1 | 2 | const testInfo = { 3 | key: 'contentTypeJsonp', 4 | value: true 5 | }; 6 | 7 | window[testInfo.key] = testInfo.value; 8 | -------------------------------------------------------------------------------- /packages/es-module/__tests__/case/resourceRedirect/m1.js: -------------------------------------------------------------------------------- 1 | import * as _ from 'https://unpkg.com/lodash-es'; 2 | import lodash from 'https://unpkg.com/lodash-es'; 3 | 4 | expect(_.default === lodash); 5 | -------------------------------------------------------------------------------- /cypress/fixtures/example.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Using fixtures to represent data", 3 | "email": "hello@cypress.io", 4 | "body": "Fixtures are a great way to mock data for responses to routes" 5 | } 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | -------------------------------------------------------------------------------- /packages/utils/src/logger.ts: -------------------------------------------------------------------------------- 1 | import createDebug from 'debug'; 2 | 3 | const log = createDebug('garfish'); 4 | 5 | export const coreLog = log.extend('core'); 6 | export const routerLog = log.extend('router'); 7 | -------------------------------------------------------------------------------- /website/i18n/zh/docusaurus-theme-classic/footer.json: -------------------------------------------------------------------------------- 1 | { 2 | "copyright": { 3 | "message": "Copyright © 2021 ByteDance, Inc. Powered By Garfish Team", 4 | "description": "The footer copyright" 5 | } 6 | } -------------------------------------------------------------------------------- /packages/garfish/tsup.config.ts: -------------------------------------------------------------------------------- 1 | import pkg from './package.json'; 2 | import { baseTsup } from '../../tsup.config'; 3 | 4 | export const tsup = { 5 | ...baseTsup(pkg), 6 | format: ['esm', 'cjs', 'iife'], 7 | }; 8 | -------------------------------------------------------------------------------- /dev/app-vue-2/src/shimsVue.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | declare module '*.vue' { 3 | import type { DefineComponent } from 'vue'; 4 | const component: DefineComponent<{}, {}, any>; 5 | export default component; 6 | } 7 | -------------------------------------------------------------------------------- /dev/app-vue-3/src/shimsVue.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | declare module '*.vue' { 3 | import type { DefineComponent } from 'vue'; 4 | const component: DefineComponent<{}, {}, any>; 5 | export default component; 6 | } 7 | -------------------------------------------------------------------------------- /dev/app-vue-vite/src/shimsVue.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | declare module '*.vue' { 3 | import type { DefineComponent } from 'vue'; 4 | const component: DefineComponent<{}, {}, any>; 5 | export default component; 6 | } 7 | -------------------------------------------------------------------------------- /dev/app-react-16/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | app react v17 6 | 7 | 8 |
9 | 10 | 11 | -------------------------------------------------------------------------------- /dev/app-react-17/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | app react v17 6 | 7 | 8 |
9 | 10 | 11 | -------------------------------------------------------------------------------- /dev/app-react-18/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | app react v17 6 | 7 | 8 |
9 | 10 | 11 | -------------------------------------------------------------------------------- /dev/app-angular/src/app/topBar/topBar.component.css: -------------------------------------------------------------------------------- 1 | .link { 2 | display: inline-block; 3 | font-size: 16px; 4 | margin: 20px; 5 | /* color: white; */ 6 | text-decoration: underline; 7 | } 8 | .active { 9 | color: darkred; 10 | } 11 | -------------------------------------------------------------------------------- /packages/garfish/src/index.ts: -------------------------------------------------------------------------------- 1 | export type { interfaces } from '@garfish/core'; 2 | export { default as Garfish } from '@garfish/core'; 3 | export { GarfishInstance as default } from './instance'; 4 | export { defineCustomElements } from './customElement'; 5 | -------------------------------------------------------------------------------- /dev/app-react-17/src/components/PageNotFound.tsx: -------------------------------------------------------------------------------- 1 | import { Result } from '@arco-design/web-react'; 2 | 3 | const PageNotFound = () => { 4 | return ; 5 | }; 6 | 7 | export default PageNotFound; 8 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Questions & Discussions 4 | url: https://github.com/modern-js-dev/garfish/discussions 5 | about: Use GitHub discussions for message-board style questions and discussions. 6 | -------------------------------------------------------------------------------- /dev/app-angular/src/app/topBar/topBar.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-topBar', 5 | templateUrl: './topBar.component.html', 6 | styleUrls: ['./topBar.component.css'], 7 | }) 8 | export class TopBarComponent {} 9 | -------------------------------------------------------------------------------- /packages/browser-vm/__tests__/resources/scripts/event-task.js: -------------------------------------------------------------------------------- 1 | setTimeout(() => { 2 | window.execOrder.push('macro task'); 3 | }); 4 | 5 | Promise.resolve().then(() => { 6 | window.execOrder.push('micro task'); 7 | }); 8 | 9 | window.execOrder.push('normal task'); 10 | -------------------------------------------------------------------------------- /dev/app-react-16/src/PageNotFound.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Result } from '@arco-design/web-react'; 3 | 4 | const PageNotFound = () => { 5 | return ; 6 | }; 7 | 8 | export default PageNotFound; 9 | -------------------------------------------------------------------------------- /dev/app-react-18/src/PageNotFound.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Result } from '@arco-design/web-react'; 3 | 4 | const PageNotFound = () => { 5 | return ; 6 | }; 7 | 8 | export default PageNotFound; 9 | -------------------------------------------------------------------------------- /packages/hooks/src/index.ts: -------------------------------------------------------------------------------- 1 | export { PluginSystem } from './pluginSystem'; 2 | export { SyncHook } from './syncHook'; 3 | export { AsyncHook } from './asyncHook'; 4 | export { SyncWaterfallHook } from './syncWaterfallHook'; 5 | export { AsyncWaterfallHook } from './asyncWaterfallHooks'; 6 | -------------------------------------------------------------------------------- /packages/core/__tests__/__snapshots__/preloadPlugin.spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Core: preload plugin disablePreloadApp is false use setRanking 1`] = `"[{\\"appName\\":\\"vue-app\\",\\"count\\":2},{\\"appName\\":\\"react-app\\",\\"count\\":1}]"`; 4 | -------------------------------------------------------------------------------- /dev/app-main/src/components/PageNotFound/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Result } from '@arco-design/web-react'; 3 | 4 | const PageNotFound = () => { 5 | return ; 6 | }; 7 | 8 | export default PageNotFound; 9 | -------------------------------------------------------------------------------- /dev/app-angular/src/app/dashboard/dashboard.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-product-detail', 5 | templateUrl: './dashboard.component.html', 6 | styleUrls: ['./dashboard.component.css'], 7 | }) 8 | export class DashboardComponent {} 9 | -------------------------------------------------------------------------------- /website/src/components/sumarryImg/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | 4 | export function summaryImg (url) { 5 | return ( 6 |
7 | 8 | 9 | 10 |
11 | ) 12 | } 13 | -------------------------------------------------------------------------------- /website/plugin/slardar/index.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = function pluginSlardar(context, options) { 4 | return { 5 | name: 'plugin-slardar', 6 | getClientModules() { 7 | return [path.resolve(__dirname, 'initSlardar.js')]; 8 | }, 9 | }; 10 | }; 11 | -------------------------------------------------------------------------------- /dev/app-angular/src/app/pageNotFound/pageNotFound.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-pageNotFound', 5 | templateUrl: './pageNotFound.component.html', 6 | styleUrls: ['./pageNotFound.component.css'], 7 | }) 8 | export class PageNotFoundComponent {} 9 | -------------------------------------------------------------------------------- /packages/es-module/__tests__/case/importMeta/m1.js: -------------------------------------------------------------------------------- 1 | expect(Object.getPrototypeOf(import.meta)).toBe(null); 2 | 3 | expect(Object.getOwnPropertyDescriptor(import.meta, 'url')).toEqual({ 4 | writable: true, 5 | enumerable: true, 6 | configurable: true, 7 | value: 'http://localhost/case/importMeta/m1.js', 8 | }); 9 | -------------------------------------------------------------------------------- /packages/router/README.md: -------------------------------------------------------------------------------- 1 | # `@garfish/router` 2 | 3 | [![NPM version](https://img.shields.io/npm/v/@garfish/router.svg?style=flat-square)](https://www.npmjs.com/package/@garfish/router) 4 | 5 | ## Usage 6 | 7 | ```js 8 | import router from '@garfish/router'; 9 | 10 | // TODO: DEMONSTRATE API 11 | ``` 12 | -------------------------------------------------------------------------------- /packages/utils/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './utils'; 2 | export * from './queue'; 3 | export * from './sentry'; 4 | export * from './domApis'; 5 | export * from './garfish'; 6 | export * from './mimeType'; 7 | export * from './container'; 8 | export * from './templateParse'; 9 | export * from './logger'; 10 | -------------------------------------------------------------------------------- /packages/core/src/plugins/lifecycle.ts: -------------------------------------------------------------------------------- 1 | import { interfaces } from '../interface'; 2 | 3 | export function GarfishOptionsLife(options, name: string) { 4 | return function (): interfaces.Plugin { 5 | return { 6 | name, 7 | version: __VERSION__, 8 | ...options, 9 | }; 10 | }; 11 | } 12 | -------------------------------------------------------------------------------- /packages/core/__tests__/resources/reactApp.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | vue sub app 5 | 6 | 7 |
8 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /packages/utils/README.md: -------------------------------------------------------------------------------- 1 | # `@garfish/utils` 2 | 3 | [![NPM version](https://img.shields.io/npm/v/@garfish/utils.svg?style=flat-square)](https://www.npmjs.com/package/@garfish/utils) 4 | 5 | ## Usage 6 | 7 | ```js 8 | import { isObject } from '@garfish/utils'; 9 | 10 | console.log(isObject({})); // true 11 | ``` 12 | -------------------------------------------------------------------------------- /dev/app-angular/src/app/topBar/topBar.component.html: -------------------------------------------------------------------------------- 1 | home 2 | dashboard 3 | list 4 | -------------------------------------------------------------------------------- /packages/es-module/__tests__/case/executionOrder/m1.js: -------------------------------------------------------------------------------- 1 | expect(globalThis.orderIndex).toBe(2); 2 | globalThis.orderIndex++; 3 | 4 | import './m2.js'; 5 | 6 | expect(globalThis.orderIndex).toBe(3); 7 | globalThis.orderIndex++; 8 | 9 | import './m3.js'; 10 | 11 | expect(globalThis.orderIndex).toBe(4); 12 | globalThis.orderIndex++; 13 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | presets: [ 4 | [ 5 | '@babel/preset-env', 6 | { 7 | useBuiltIns: 'entry', 8 | targets: ['last 2 versions', 'ie >= 9'], 9 | modules: 'commonjs', 10 | }, 11 | ], 12 | ], 13 | plugins: ['@babel/plugin-transform-regenerator'], 14 | }; 15 | 16 | -------------------------------------------------------------------------------- /dev/app-react-18/src/ErrorBoundary.tsx: -------------------------------------------------------------------------------- 1 | import { Result } from '@arco-design/web-react'; 2 | 3 | const Error = () => { 4 | return ( 5 | 10 | ); 11 | }; 12 | 13 | export default Error; 14 | -------------------------------------------------------------------------------- /dev/app-vue-3/src/components/HelloGarfish.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 13 | -------------------------------------------------------------------------------- /dev/app-vue-2/src/components/HelloGarfish.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 13 | -------------------------------------------------------------------------------- /packages/garfish/index.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | export * from './dist/index'; 5 | import defaultInstance from './dist/index'; 6 | 7 | export { default as Garfish } from '@garfish/core'; 8 | export default defaultInstance; 9 | -------------------------------------------------------------------------------- /dev/app-react-17/src/components/ErrorBoundary.tsx: -------------------------------------------------------------------------------- 1 | import { Result } from '@arco-design/web-react'; 2 | 3 | const Error = () => { 4 | return ( 5 | 10 | ); 11 | }; 12 | 13 | export default Error; 14 | -------------------------------------------------------------------------------- /packages/garfish/__tests__/__snapshots__/run.spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`Core: run methods auto render and destroy vue app 1`] = ` 4 | Array [ 5 | "http://localhost/resources/vueIndex.js", 6 | "./resources/vueApp.html", 7 | undefined, 8 | "http://localhost/resources/dynamic.js", 9 | ] 10 | `; 11 | -------------------------------------------------------------------------------- /dev/app-angular/src/app/home/home.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-home', 5 | templateUrl: './home.component.html', 6 | styleUrls: ['./home.component.css'], 7 | }) 8 | export class HomeComponent { 9 | share() { 10 | window.alert('The product has been shared!'); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /scripts/utils.js: -------------------------------------------------------------------------------- 1 | const execa = require('execa'); 2 | const chalk = require('chalk'); 3 | 4 | const step = (msg) => { 5 | console.log(chalk.cyan(msg)); 6 | }; 7 | 8 | const run = async (bin, args, opts = {}) => { 9 | return await execa(bin, args, { stdio: 'inherit', ...opts }); 10 | }; 11 | 12 | module.exports = { 13 | run, 14 | step, 15 | }; 16 | -------------------------------------------------------------------------------- /dev/app-react-17/src/components/detail/index.less: -------------------------------------------------------------------------------- 1 | @import '../../less/variable.less'; 2 | 3 | .@{prefix}-descriptions-title, 4 | .@{prefix}-descriptions-item-value { 5 | color: var(--color-text-4); 6 | } 7 | .@{prefix}-descriptions-title{ 8 | text-align: left; 9 | } 10 | .@{prefix}-breadcrumb-item:last-child { 11 | color: var(--color-text-4); 12 | } 13 | -------------------------------------------------------------------------------- /packages/browser-snapshot/README.md: -------------------------------------------------------------------------------- 1 | # `@garfish/browser-snapshot` 2 | 3 | [![NPM version](https://img.shields.io/npm/v/@garfish/browser-snapshot.svg?style=flat-square)](https://www.npmjs.com/package/@garfish/browser-snapshot) 4 | 5 | ## Usage 6 | 7 | ```js 8 | import spSandbox from '@garfish/browser-snapshot'; 9 | 10 | // TODO: DEMONSTRATE API 11 | ``` 12 | -------------------------------------------------------------------------------- /dev/app-angular/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@angular/core'; 2 | import { Component } from '@angular/core'; 3 | 4 | @Component({ 5 | selector: 'app-root', 6 | templateUrl: './app.component.html', 7 | styleUrls: ['./app.component.css'], 8 | }) 9 | @Injectable({ 10 | providedIn: 'root', 11 | }) 12 | export class AppComponent {} 13 | -------------------------------------------------------------------------------- /dev/app-esm/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@garfish-dev/esm", 3 | "private": true, 4 | "scripts": { 5 | "start": "http-server ./src --cors -p 8099", 6 | "build": "cp -r src/. dist" 7 | }, 8 | "publishConfig": { 9 | "registry": "https://registry.npmjs.org" 10 | }, 11 | "devDependencies": { 12 | "http-server": "^14.1.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/es-module/__tests__/case/dynamicImport/m1.js: -------------------------------------------------------------------------------- 1 | import * as m2 from './m2.js'; 2 | import arr, { name } from './m2.js'; 3 | 4 | import('./m2.js').then((module) => { 5 | expect(module === m2).toBe(true); 6 | expect(module.default === arr).toBe(true); 7 | expect(module.name === name).toBe(true); 8 | expect(Object.keys(module).length).toBe(2); 9 | }); 10 | -------------------------------------------------------------------------------- /dev/app-angular/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.ts] 12 | quote_type = single 13 | 14 | [*.md] 15 | max_line_length = off 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /dev/app-react-16/src/ErrorBoundary.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Result } from '@arco-design/web-react'; 3 | 4 | const Error = () => { 5 | return ( 6 | 11 | ); 12 | }; 13 | 14 | export default Error; 15 | -------------------------------------------------------------------------------- /packages/es-module/__tests__/case/circularReference/m2.js: -------------------------------------------------------------------------------- 1 | import { name } from './m1.js'; 2 | import * as m1 from './m1.js'; 3 | 4 | export const _name = 'm2'; 5 | 6 | expect(() => name).toThrowError(/.?name.?/g); 7 | expect(() => m1.name).toThrowError(/.?name.?/g); 8 | 9 | setTimeout(() => { 10 | expect(name).toBe('m1'); 11 | expect(m1.name).toBe('m1'); 12 | }); 13 | -------------------------------------------------------------------------------- /packages/utils/src/garfish.ts: -------------------------------------------------------------------------------- 1 | export const __LOADER_FLAG__ = Symbol.for('__LOADER_FLAG__'); 2 | export const __GARFISH_FLAG__ = Symbol.for('__GARFISH_FLAG__'); 3 | export const __MockHtml__ = '__garfishmockhtml__'; 4 | export const __MockBody__ = '__garfishmockbody__'; 5 | export const __MockHead__ = '__garfishmockhead__'; 6 | export const __REMOVE_NODE__ = '__garfishremovenode__'; 7 | -------------------------------------------------------------------------------- /website/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | /output 7 | /output_resource 8 | 9 | # Generated files 10 | .docusaurus 11 | .cache-loader 12 | 13 | # Misc 14 | .DS_Store 15 | .env.local 16 | .env.development.local 17 | .env.test.local 18 | .env.production.local 19 | 20 | npm-debug.log* 21 | yarn-debug.log* 22 | yarn-error.log* 23 | -------------------------------------------------------------------------------- /dev/app-angular/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Angular 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /dev/app-angular/e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "../tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "../out-tsc/e2e", 6 | "module": "commonjs", 7 | "target": "es2018", 8 | "types": [ 9 | "jasmine", 10 | "jasminewd2", 11 | "node" 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /dev/app-angular/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "extends": "./tsconfig.json", 4 | "compilerOptions": { 5 | "outDir": "./out-tsc/app", 6 | "types": [] 7 | }, 8 | "files": [ 9 | "src/main.ts", 10 | "src/polyfills.ts" 11 | ], 12 | "include": [ 13 | "src/**/*.d.ts" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /website/i18n/en/docusaurus-plugin-content-docs/current/community/discuss.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Garfish community 3 | slug: /community/discuss 4 | order: 1 5 | --- 6 | 7 | ### Github Discussions 8 | 9 | [Garfish Discussions](https://github.com/bytedance/garfish/discussions) 10 | 11 | 12 | -------------------------------------------------------------------------------- /website/src/components/config/_protectVariable.mdx: -------------------------------------------------------------------------------- 1 | import Highlight from '@site/src/components/Highlight'; 2 | 3 | - Type: string[] 4 | - 在开启沙箱的情况下,提供使得 window 上的某些变量处于受保护状态的能力:这些值的读写不会受到沙箱隔离机制的影响,所有应用均可读取到,可选; 5 | - 若希望在应用间共享 window 上的某些值,可将该值放置在数组中; 6 | - 该属性与 [setGlobalValue](/api/setGlobalObject) 功能相同,推荐使用 `protectVariable` 属性,通过 `protectVariable` 可以明确的感知哪些值可能在应用间相互影响; 7 | -------------------------------------------------------------------------------- /dev/app-angular/e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo(): Promise { 5 | return browser.get(browser.baseUrl) as Promise; 6 | } 7 | 8 | getTitleText(): Promise { 9 | return element( 10 | by.css('app-root .content span'), 11 | ).getText() as Promise; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /dev/app-main/typings.d.ts: -------------------------------------------------------------------------------- 1 | declare interface Window { 2 | Garfish: any; 3 | __GARFISH__: any; 4 | } 5 | 6 | declare module '*.css'; 7 | declare module '*.less'; 8 | declare module '*.png'; 9 | declare module '*.svg' { 10 | export function ReactComponent( 11 | props: React.SVGProps, 12 | ): React.ReactElement; 13 | const url: string; 14 | export default url; 15 | } 16 | -------------------------------------------------------------------------------- /dev/app-react-17/typings.d.ts: -------------------------------------------------------------------------------- 1 | declare interface Window { 2 | Garfish: any; 3 | __GARFISH__: any; 4 | } 5 | 6 | declare module '*.css'; 7 | declare module '*.less'; 8 | declare module '*.png'; 9 | declare module '*.svg' { 10 | export function ReactComponent( 11 | props: React.SVGProps, 12 | ): React.ReactElement; 13 | const url: string; 14 | export default url; 15 | } 16 | -------------------------------------------------------------------------------- /dev/app-react-18/typings.d.ts: -------------------------------------------------------------------------------- 1 | declare interface Window { 2 | Garfish: any; 3 | __GARFISH__: any; 4 | } 5 | 6 | declare module '*.css'; 7 | declare module '*.less'; 8 | declare module '*.png'; 9 | declare module '*.svg' { 10 | export function ReactComponent( 11 | props: React.SVGProps, 12 | ): React.ReactElement; 13 | const url: string; 14 | export default url; 15 | } 16 | -------------------------------------------------------------------------------- /dev/app-vue-3/src/components/alertError.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 18 | -------------------------------------------------------------------------------- /dev/app-vue-vite/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /website/docs/api/clearEscapeEffect.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Garfish.clearEscapeEffect 3 | slug: /api/clearEscapeEffect 4 | order: 9 5 | --- 6 | 7 | 用来清除逃逸沙箱的变量。 8 | 9 | > 在微前端应用下,子应用将默认开启沙箱模式。在沙箱模式下,若发现有一些特殊的行为会逃逸沙箱系统,可以使用此方法来清除逃逸的变量; 10 | 11 | ## Type 12 | ```ts 13 | clearEscapeEffect: (key: string, value?: any) => void; 14 | ``` 15 | 16 | ## 示例 17 | 18 | ```js 19 | Garfish.clearEscapeEffect('webpackJsonp'); 20 | ``` 21 | -------------------------------------------------------------------------------- /website/src/components/Highlight/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default function Highlight({ children, color }) { 4 | return ( 5 | 13 | {children} 14 | 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /dev/app-esm/src/index.js: -------------------------------------------------------------------------------- 1 | let div; 2 | 3 | export function render() { 4 | div = document.createElement('div'); 5 | const span = document.createElement('span'); 6 | span.innerText = 'esmApp content'; 7 | div.id = 'esmAppContent'; 8 | div.appendChild(span); 9 | document.body.appendChild(div); 10 | } 11 | 12 | export function destroy() { 13 | if (div) { 14 | document.body.removeChild(div); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /dev/app-vue-2/src/components/alertError.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 18 | -------------------------------------------------------------------------------- /scripts/e2e.js: -------------------------------------------------------------------------------- 1 | const { run, step } = require('./utils'); 2 | const { runAllExample } = require('./runE2eProject'); 3 | 4 | runAllExample().then(() => { 5 | // once here, all resources are available 6 | step('\n start e2e test...'); 7 | const spawnInstance = run('pnpm', [ 8 | process.env.TEST_ENV_OPEN ? 'cy:open' : 'cy:run', 9 | ]); 10 | spawnInstance.stdout?.on('data', (msg) => step(msg.toString())); 11 | }); 12 | -------------------------------------------------------------------------------- /packages/browser-snapshot/__tests__/__snapshots__/sandbox.spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`test sandbox dom sandbox 1`] = ` 4 | NodeList [ 5 | , 8 | 8 | 9 | `; 10 | 11 | exports[`test sandbox dom sandbox 2`] = ` 12 | 13 | 16 | 17 | `; 18 | 19 | exports[`test sandbox dom sandbox 3`] = ` 20 | 21 | 24 | 27 | 28 | `; 29 | -------------------------------------------------------------------------------- /dev/app-angular/src/app/product.ts: -------------------------------------------------------------------------------- 1 | export interface Product { 2 | id: number; 3 | name: string; 4 | price: number; 5 | description: string; 6 | } 7 | 8 | export const products = [ 9 | { 10 | id: 1, 11 | name: 'Phone XL', 12 | price: 799, 13 | description: 'A large phone with one of the best screens', 14 | }, 15 | { 16 | id: 2, 17 | name: 'Phone Mini', 18 | price: 699, 19 | description: 'A great phone with one of the best cameras', 20 | }, 21 | { 22 | id: 3, 23 | name: 'Phone Standard', 24 | price: 299, 25 | description: '', 26 | }, 27 | ]; 28 | -------------------------------------------------------------------------------- /dev/app-main/src/static/icons/Vue.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.ls-lint.yml: -------------------------------------------------------------------------------- 1 | ls: 2 | packages/{src,__tests__}: 3 | .js: camelCase 4 | .ts: camelCase 5 | .d.ts: camelCase 6 | .spec.ts: camelCase 7 | 8 | dev/*/{src}: 9 | .js: camelCase | point.case 10 | .ts: camelCase | point.case 11 | .jsx: camelCase | point.case 12 | .tsx: camelCase | point.case | PascalCase 13 | .d.ts: camelCase 14 | .component.ts: camelCase | point.case 15 | 16 | website/{src,docs}: 17 | .js: camelCase 18 | .ts: camelCase 19 | .jsx: camelCase 20 | .tsx: camelCase 21 | .d.ts: camelCase 22 | 23 | scripts: 24 | .js: camelCase 25 | 26 | -------------------------------------------------------------------------------- /dev/app-vue-vite/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, loadEnv } from 'vite'; 2 | import vue from '@vitejs/plugin-vue'; 3 | import portMap from '../config.json'; 4 | 5 | const appName = 'dev/vite'; 6 | const port = portMap[appName].port; 7 | 8 | export default ({ mode }) => { 9 | process.env = { 10 | ...process.env, 11 | ...loadEnv(mode, process.cwd()), 12 | }; 13 | 14 | return defineConfig({ 15 | base: `http://localhost:${port}`, 16 | server: { 17 | port, 18 | cors: true, 19 | origin: `http://localhost:${port}`, 20 | }, 21 | plugins: [vue()], 22 | }); 23 | }; 24 | -------------------------------------------------------------------------------- /website/docs/guide/demo/demo.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 概述 3 | slug: /guide/demo 4 | order: 1 5 | --- 6 | 7 | 本节我们会详细讲述不同框架下的子应用如何接入 Garfish, 提供抄得走的接入案例,以下所有 demo 均可在 [garfish demo](https://github.com/modern-js-dev/garfish/tree/main/dev) 中找到实际使用案例,目前提供的 demo 案例包含: 8 | 9 | - react (version 16, 17, 18) 10 | - vue (version 2, 3) 11 | - vite (version 2) 12 | - angular (version 13) 13 | ## demo 案例 14 | 15 | 子应用的导出提供通过 `@garfish/bridge-*` 的方式和自定义导出函数两种方式,我们将在下列 demo 案例中分别讲述。 16 | 17 | - [react 子应用](/guide/demo/react) 18 | - [vue 子应用](/guide/demo/vue) 19 | - [vite 子应用](/guide/demo/vite) 20 | - [angular 子应用](/guide/demo/angular) 21 | -------------------------------------------------------------------------------- /website/src/components/lifeCycle/_beforeUnmount.mdx: -------------------------------------------------------------------------------- 1 | import Highlight from '@site/src/components/Highlight'; 2 | 3 | - Type: ( appInfo: AppInfo, appInstance: interfaces.App) => void 4 | - Kind: `sync`, `sequential` 5 | - Previous Hook: `beforeLoad`、`afterLoad`、`beforeMount`、`afterMount` 6 | - Trigger: 7 | - 在调用 `app.unmount` 或 `app.hide` 触发该 `hook`,用户除了手动调用这两个方法外,`Garfish Router` 托管模式还会自动触发 8 | - 在使用 `app.unmount` 渲染应用是 `cacheMode` 为 `false`; 9 | - 在使用 `app.hide` 渲染应用是 `cacheMode` 为 `true`; 10 | - 此时子应用 DOM 元素还未卸载,副作用尚未清除; 11 | - 此时子应用 DOM 树已渲染完成,garfish 实例 `activeApps` 中已添加当前子应用 app 实例; 12 | -------------------------------------------------------------------------------- /website/src/components/lifeCycle/_errorUnmountApp.mdx: -------------------------------------------------------------------------------- 1 | import Highlight from '@site/src/components/Highlight'; 2 | 3 | - Type: (error: Error, appInfo: AppInfo, appInstance: interfaces.App)=> void 4 | - 一旦设置该 hook,子应用销毁错误不会向上 throw 到文档流中,全局错误监听将无法捕获到; 5 | - Kind: `sync`, `sequential` 6 | - Trigger: 7 | 8 | - 在 `app.unmount` 或 `app.hide` 销毁过程中出现异常则会触发该 `hook`,用户除了手动调用这两个方法外,`Garfish Router` 托管模式还会自动触发 9 | 10 | - 示例 11 | 12 | ```ts 13 | Garfish.run({ 14 | ..., 15 | errorUnmountApp(error, appInfo) { 16 | console.log('子应用销毁异常', appInfo.name); 17 | console.error(error); 18 | } 19 | }) 20 | ``` 21 | -------------------------------------------------------------------------------- /website/src/components/lifeCycle/_errorLoadApp.mdx: -------------------------------------------------------------------------------- 1 | import Highlight from '@site/src/components/Highlight'; 2 | 3 | - Type: (error: Error, appInfo: AppInfo, appInstance: interfaces.App) => void 4 | - 该 `hook` 的参数分别为:`error` 实例、 `appInfo` 信息、`appInstance` 应用实例 5 | - 一旦设置该 hook,子应用加载错误不会 throw 到文档流中,全局错误监听将无法捕获到; 6 | - Kind: `sync`, `sequential` 7 | - Trigger: 8 | 9 | - 在调用 `Garfish.load` 过程中,并且加载失败时触发该 `hook` 10 | 11 | - 示例 12 | 13 | ```ts 14 | Garfish.run({ 15 | ..., 16 | errorLoadApp(error, appInfo) { 17 | console.log('子应用加载异常', appInfo.name); 18 | console.error(error); 19 | } 20 | }) 21 | ``` 22 | -------------------------------------------------------------------------------- /packages/browser-vm/src/symbolTypes.ts: -------------------------------------------------------------------------------- 1 | export const GARFISH_NAMESPACE_PREFIX = '__Garfish__'; 2 | export const GARFISH_OPTIMIZE_NAME = '__garfish_optimize__'; 3 | export const __proxyNode__ = Symbol.for('garfish.proxyNode'); 4 | export const __domWrapper__ = Symbol.for('garfish.domWrapper'); 5 | export const __windowBind__ = Symbol.for('garfish.windowBind'); 6 | export const __sandboxMap__ = Symbol.for('garfish.sandboxMap'); 7 | export const __documentBind__ = Symbol.for('garfish.documentBind'); 8 | export const __garfishGlobal__ = Symbol.for('garfish.globalObject'); 9 | export const __elementSandboxTag__ = Symbol.for('garfish.elementSandboxTag'); 10 | -------------------------------------------------------------------------------- /dev/app-main/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env", 4 | "@babel/preset-typescript", 5 | "@babel/preset-react" 6 | ], 7 | "plugins": [ 8 | "@babel/plugin-syntax-dynamic-import", 9 | [ 10 | "@babel/plugin-transform-runtime", 11 | { 12 | "regenerator": true 13 | } 14 | ], 15 | [ 16 | "@babel/plugin-proposal-decorators", 17 | { 18 | "legacy": true 19 | } 20 | ], 21 | "@babel/plugin-proposal-class-properties", 22 | [ 23 | "@babel/plugin-transform-react-jsx", 24 | { 25 | "runtime": "automatic" 26 | } 27 | ] 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /website/src/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | home: true 3 | heroImage: /logo.png 4 | tagline: 一站式微前端解决方案 5 | actionButtons: 6 | - text: 指南 7 | link: /guide 8 | - text: 快速开始 9 | link: /guide 10 | extraClass: github grey 11 | # - text: GitHub 12 | # link: https://github.com/bytedance/garfish 13 | # extraClass: github grey 14 | # icon: fa fa-github 15 | # target: _blank 16 | features: 17 | - title: 易用 18 | details: 任意前端框架均可使用,使用简单灵活,包括完备的路由系统 19 | - title: 完备 20 | details: 包含构建微前端系统时所需要的基本能力:运行时框架、cli、部署平台 21 | - title: 生产可用 22 | details: 已在字节内经受过足够大量的线上系统的考验及打磨,健壮性值得信赖 23 | footer: | 24 | Copyright © 2021 Bytedance. 25 | --- 26 | -------------------------------------------------------------------------------- /website/src/components/config/_domGetter.mdx: -------------------------------------------------------------------------------- 1 | import Highlight from '@site/src/components/Highlight'; 2 | 3 | - Type: interfaces.DomGetter 4 | 5 | ```ts 6 | export type DomGetter = 7 | | string 8 | | (() => Element | null) 9 | | (() => Promise); 10 | ``` 11 | 12 | - 子应用的默认挂载点,可选,没有默认值,若省略需要在子应用 AppInfo 中单独指定。二者同时存在时,子应用指定优先级更高; 13 | - 当提供 `string` 类型时需要其值是 `selector`, Garfish 内部会使用 `document.querySelector(domGetter)` 去选中子应用的挂载点 14 | - 当提供 `string` 类型的 `domGetter` 时,子应用在触发渲染后并不会若当前文档流上并不存在挂载点,`Garfish` 框架内部在 `3s` 内轮讯是否有挂载点 15 | - 当提供函数时,将在子应用挂载过程中执行此函数,并期望返回一个 dom 元素; 16 | - 若 `domGetter` 在子应用渲染时无法查询到挂载点,则会丢出 `domGetter` 无效的异常 17 | -------------------------------------------------------------------------------- /website/src/components/lifeCycle/_errorMountApp.mdx: -------------------------------------------------------------------------------- 1 | import Highlight from '@site/src/components/Highlight'; 2 | 3 | - Type: (error: Error, appInfo: AppInfo, appInstance: interfaces.App) => void 4 | - 一旦设置该 hook,子应用加载错误不会 throw 到文档流中,全局错误监听将无法捕获到; 5 | - Kind: `sync`, `sequential` 6 | - Previous Hook: `beforeLoad`、`afterLoad`、`beforeMount`、`afterMount` 7 | - Trigger: 8 | 9 | - 在渲染过程中出现异常会触发该 `hook`,子应用同步执行的代码出现异常会触发该 `hook`,异步代码无法触发 10 | 11 | - 示例 12 | 13 | ```ts 14 | Garfish.run({ 15 | ..., 16 | errorMountApp(error, appInfo) { 17 | console.log('子应用渲染异常', appInfo.name); 18 | console.error(error); 19 | } 20 | }) 21 | ``` 22 | -------------------------------------------------------------------------------- /website/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. 5 | 6 | ## [0.1.7](https://github.com/bytedance/garfish/compare/v0.1.5...0.1.7) (2021-09-07) 7 | 8 | **Note:** Version bump only for package garfish-docs 9 | 10 | ## [0.1.6](https://github.com/bytedance/garfish/compare/v0.1.5...0.1.6) (2021-09-06) 11 | 12 | **Note:** Version bump only for package garfish-docs 13 | 14 | ## [0.0.36](https://github.com/bytedance/garfish/compare/v0.0.35...0.0.36) (2021-07-29) 15 | 16 | **Note:** Version bump only for package garfish-docs 17 | -------------------------------------------------------------------------------- /website/docs/api/setGlobal.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Garfish.setGlobalObject 3 | slug: /api/setGlobalObject 4 | order: 8 5 | --- 6 | 7 | 用于子应用设置真实 window 的值。 8 | 9 | > 在微前端应用下,子应用将默认开启沙箱模式。在沙箱模式下,子应用中全局变量为被 proxy 的 'fakeWindow',而全局变量(真实 window)默认会被隔离。若子应用需求设置真实 window 的值,可以通过此方法获取。 10 | 11 | :::tip 12 | 1. 一般情况下我们不建议直接通过此 API 设置真实 window; 13 | 2. 若需要设置真实 window 上的环境变量,可通过 [`protectVariable`](/api/run#protectvariable) 属性,将需要共享的属性放入列表中即可通过子应用的全局变量获取; 14 | ::: 15 | 16 | ## Type 17 | ```ts 18 | setGlobalValue(key: string, value?: any): void; 19 | ``` 20 | ## 示例 21 | 22 | ```ts 23 | import Garfish from 'garfish'; 24 | 25 | Garfish.setGlobalValue(key: string | symbol, value: any); 26 | ``` 27 | -------------------------------------------------------------------------------- /packages/browser-snapshot/__tests__/event.spec.ts: -------------------------------------------------------------------------------- 1 | import { PatchEvent } from '../src/patchers/event'; 2 | 3 | describe('test sandbox ', () => { 4 | it('dom sandbox', () => { 5 | const event = new Event('flag-event'); 6 | const evn = new PatchEvent(); 7 | let flag1: string | null = null; 8 | window.addEventListener('flag-event', () => (flag1 = 'flag1')); 9 | 10 | evn.activate(); 11 | 12 | let flag2: string | null = null; 13 | window.addEventListener('flag-event', () => (flag2 = 'flag2')); 14 | 15 | evn.deactivate(); 16 | 17 | window.dispatchEvent(event); 18 | expect(flag1).toBe('flag1'); 19 | expect(flag2).toBe(null); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /packages/es-module/__tests__/case/exportDeclaration/m2.js: -------------------------------------------------------------------------------- 1 | // 测试各种 export 和变量更改检测 2 | export const name = 'm2'; 3 | 4 | export var a = 1; 5 | export const b = 2; 6 | export let c = 3, 7 | d = 4; 8 | 9 | export function fn1() { 10 | return 'fn1'; 11 | } 12 | export const fn2 = () => 'fn2'; 13 | 14 | export class cls { 15 | world() { 16 | return 'cls.world'; 17 | } 18 | } 19 | 20 | function fn3() { 21 | return 'fn3'; 22 | } 23 | 24 | export { a as aa, fn3 as _fn3 }; 25 | 26 | export default ['default']; 27 | 28 | const obj = { 29 | name1: 'n1', 30 | name2: 'n2', 31 | }; 32 | 33 | export const { name1, name2: bar } = obj; 34 | 35 | setTimeout(() => { 36 | a = 'aa'; 37 | }); 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/node_modules/ 2 | dist/ 3 | lib/ 4 | es/ 5 | .DS_Store 6 | .idea 7 | **/coverage/ 8 | .vscode 9 | .rts2_cache/ 10 | .yarn/ 11 | temp/ 12 | /docs 13 | # .npmrc 14 | **/.angular 15 | .wireit 16 | 17 | # test result 18 | cypress/screenshots 19 | cypress/videos 20 | cypress/downloads 21 | 22 | 23 | # Editor directories and files 24 | .idea 25 | *.suo 26 | *.ntvs* 27 | *.njsproj 28 | *.sln 29 | 30 | # Test files 31 | **/coverage 32 | test.js 33 | 34 | # Log files 35 | **/npm-debug.log* 36 | **/yarn-debug.log* 37 | **/yarn-error.log* 38 | lerna-debug.log 39 | 40 | 41 | # use pnpm ignore lock files 42 | package-lock.json 43 | yarn.lock 44 | **/package-lock.json 45 | **/yarn.lock 46 | .wireit 47 | -------------------------------------------------------------------------------- /dev/app-vue-2/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /dev/app-vue-3/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /dev/app-vue/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /dev/app-vue/src/App.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 19 | 20 | 30 | -------------------------------------------------------------------------------- /dev/app-vue/src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 18 | 19 | 20 | 39 | -------------------------------------------------------------------------------- /website/src/components/lifeCycle/_afterEval.mdx: -------------------------------------------------------------------------------- 1 | import Highlight from '@site/src/components/Highlight'; 2 | 3 | - Type: (appInfo: AppInfo, code: string, env: Record, url: string, options) => void 4 | - 该 `hook` 的参数分别为:`appInfo` 信息、`code` 执行的代码、`env` 要注入的环境变量,`url` 应用访问地址、`options` 参数选项例如 `async` 是否异步执行、`noEntry` 是否是 `noEntry` 模式; 5 | - Kind: `sync`, `sequential` 6 | - Previous Hook: `beforeLoad`、`afterLoad` 7 | - Trigger: 8 | 9 | - 在实际执行代码后。`afterMount` 触发前触发; 10 | - 子应用 html 内的 script 和动态创建的脚本执行时都会触发该 hook 11 | 12 | - 示例 13 | 14 | ```ts 15 | Garfish.afterEval({ 16 | ..., 17 | afterEval(appInfo) { 18 | console.log('子应用代码执行完成', appInfo.name); 19 | } 20 | }) 21 | ``` 22 | -------------------------------------------------------------------------------- /packages/browser-vm/__tests__/modules/uiEvent.spec.ts: -------------------------------------------------------------------------------- 1 | import { MouseEventPatch } from '../../src/modules/uiEvent'; 2 | 3 | /** 4 | * The logic of UIEvent is referenced from qiankun typography 5 | * https://github.com/umijs/qiankun/pull/593/files 6 | */ 7 | test('patch UIEvent', async () => { 8 | const dispatchEventAction = jest.fn(); 9 | 10 | const dom = document.createElement('a'); 11 | dom.onclick = dispatchEventAction; 12 | 13 | const evt = new MouseEventPatch('click', { 14 | view: new Proxy(window, {}), // fakeWindow can't use to MouseEventPatch 15 | bubbles: true, 16 | cancelable: false, 17 | }); 18 | dom.dispatchEvent(evt); 19 | 20 | expect(dispatchEventAction).toBeCalledTimes(1); 21 | }); 22 | -------------------------------------------------------------------------------- /dev/app-react-16/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env", 4 | "@babel/preset-typescript", 5 | [ 6 | "@babel/preset-react", { 7 | "runtime": "automatic" 8 | } 9 | ] 10 | ], 11 | "plugins": [ 12 | "@babel/plugin-syntax-dynamic-import", 13 | [ 14 | "@babel/plugin-transform-runtime", 15 | { 16 | "regenerator": true 17 | } 18 | ], 19 | [ 20 | "@babel/plugin-proposal-decorators", 21 | { 22 | "legacy": true 23 | } 24 | ], 25 | "@babel/plugin-proposal-class-properties", 26 | [ 27 | "@babel/plugin-transform-react-jsx", 28 | { 29 | "runtime": "automatic" 30 | } 31 | ] 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /dev/app-react-17/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env", 4 | "@babel/preset-typescript", 5 | [ 6 | "@babel/preset-react", { 7 | "runtime": "automatic" 8 | } 9 | ] 10 | ], 11 | "plugins": [ 12 | "@babel/plugin-syntax-dynamic-import", 13 | [ 14 | "@babel/plugin-transform-runtime", 15 | { 16 | "regenerator": true 17 | } 18 | ], 19 | [ 20 | "@babel/plugin-proposal-decorators", 21 | { 22 | "legacy": true 23 | } 24 | ], 25 | "@babel/plugin-proposal-class-properties", 26 | [ 27 | "@babel/plugin-transform-react-jsx", 28 | { 29 | "runtime": "automatic" 30 | } 31 | ] 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /dev/app-react-18/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env", 4 | "@babel/preset-typescript", 5 | [ 6 | "@babel/preset-react", { 7 | "runtime": "automatic" 8 | } 9 | ] 10 | ], 11 | "plugins": [ 12 | "@babel/plugin-syntax-dynamic-import", 13 | [ 14 | "@babel/plugin-transform-runtime", 15 | { 16 | "regenerator": true 17 | } 18 | ], 19 | [ 20 | "@babel/plugin-proposal-decorators", 21 | { 22 | "legacy": true 23 | } 24 | ], 25 | "@babel/plugin-proposal-class-properties", 26 | [ 27 | "@babel/plugin-transform-react-jsx", 28 | { 29 | "runtime": "automatic" 30 | } 31 | ] 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /packages/browser-vm/src/modules/uiEvent.ts: -------------------------------------------------------------------------------- 1 | import { getType } from '@garfish/utils'; 2 | 3 | // The logic of UIEvent is referenced from qiankun typography 4 | // https://github.com/umijs/qiankun/pull/593/files 5 | // TODO: fix normal mouse event instanceof MouseEvent === false 6 | export class MouseEventPatch extends MouseEvent { 7 | constructor(typeArg: string, mouseEventInit?: MouseEventInit) { 8 | if (mouseEventInit && getType(mouseEventInit.view) === 'window') { 9 | mouseEventInit.view = window; 10 | } 11 | super(typeArg, mouseEventInit); 12 | } 13 | } 14 | 15 | export function UiEventOverride() { 16 | return { 17 | override: { 18 | MouseEvent: MouseEventPatch as any, 19 | }, 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /website/docs/api/setOptioins.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Garfish.setOptions 3 | slug: /api/setOptions 4 | order: 5 5 | --- 6 | 7 | 在 `Garfish.run` 调用前用于动态更新配置信息的API,参数与 [Garfish.run](/api/run) 保持一致。 8 | ## 设计初衷 9 | 在某些场景下,微前端应用的初始化参数信息在运行过程中可能会出现混乱不一致的情况,提供该 `API` 用于用户在复杂场景下设置全局微前端应用参数信息,此 API 作用于全局 Garfish 实例。 10 | 11 | :::info 12 | `Garfish.setOptions` 用于微前端应用启动前的参数变更。在触发了 `run` 方法后,不可以再通过 `setOptions` 更改应用配置。 13 | ::: 14 | 15 | ## 示例 16 | 17 | ```js 18 | // 主应用 index.js 19 | import Garfish from 'garfish'; 20 | 21 | Garfish.setOptions({ 22 | basename: '/', 23 | domGetter: '#container', 24 | // xxx 25 | }); 26 | ``` 27 | 28 | ## 参数 29 | 30 | `Options` 31 | 32 | - 此 API 参数与 [Garfish.run](/api/run) 保持一致。 请参考 [Garfish.run](/api/run#参数); 33 | -------------------------------------------------------------------------------- /dev/app-vue-2/public/remoteModule.js: -------------------------------------------------------------------------------- 1 | // const loadModule = require('loadModule'); 2 | // console.log(loadModule); 3 | 4 | const components = { 5 | cmOne: { 6 | props: ['text'], 7 | data: () => ({ 8 | name: 'chen', 9 | }), 10 | render(h) { 11 | return h('div', {}, [this.text, '---', this.name]); 12 | }, 13 | }, 14 | 15 | cmTwo: { 16 | props: ['text'], 17 | data: () => ({ 18 | name: 'tao', 19 | }), 20 | render(h) { 21 | return h('div', {}, [this.text, '---', this.name]); 22 | }, 23 | }, 24 | }; 25 | 26 | module.exports = new Promise((resolve) => { 27 | console.log('loading other modules.'); 28 | setTimeout(() => { 29 | resolve(components); 30 | }, 100); 31 | }); 32 | -------------------------------------------------------------------------------- /dev/app-vue/public/remoteModule.js: -------------------------------------------------------------------------------- 1 | // const loadModule = require('loadModule'); 2 | // console.log(loadModule); 3 | 4 | const components = { 5 | cmOne: { 6 | props: ['text'], 7 | data: () => ({ 8 | name: 'chen', 9 | }), 10 | render(h) { 11 | return h('div', {}, [this.text, '---', this.name]); 12 | }, 13 | }, 14 | 15 | cmTwo: { 16 | props: ['text'], 17 | data: () => ({ 18 | name: 'tao', 19 | }), 20 | render(h) { 21 | return h('div', {}, [this.text, '---', this.name]); 22 | }, 23 | }, 24 | }; 25 | 26 | module.exports = new Promise((resolve) => { 27 | console.log('loading other modules.'); 28 | setTimeout(() => { 29 | resolve(components); 30 | }, 100); 31 | }); 32 | -------------------------------------------------------------------------------- /packages/loader/src/managers/module.ts: -------------------------------------------------------------------------------- 1 | export class ModuleManager { 2 | public moduleCode: string; 3 | public url: string | null; 4 | public originUrl?: string; 5 | public alias: string | null; 6 | 7 | constructor(moduleCode: string, url?: string) { 8 | this.alias = null; 9 | this.url = url || null; 10 | this.moduleCode = moduleCode; 11 | } 12 | 13 | setAlias(name: string) { 14 | if (name && typeof name === 'string') { 15 | this.alias = name; 16 | } 17 | } 18 | 19 | clone() { 20 | // @ts-ignore 21 | const cloned = new this.constructor(); 22 | cloned.url = this.url; 23 | cloned.alias = this.alias; 24 | cloned.moduleCode = this.moduleCode; 25 | return cloned; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /dev/app-angular/tsconfig.json: -------------------------------------------------------------------------------- 1 | /* To learn more about this file see: https://angular.io/config/tsconfig. */ 2 | { 3 | "compileOnSave": false, 4 | "compilerOptions": { 5 | "baseUrl": "./", 6 | "outDir": "./dist/out-tsc", 7 | "typeRoots": ["./src/typings"], 8 | "sourceMap": true, 9 | "declaration": false, 10 | "downlevelIteration": true, 11 | "experimentalDecorators": true, 12 | "moduleResolution": "node", 13 | "importHelpers": true, 14 | "target": "es2015", 15 | "module": "esnext", 16 | "lib": [ 17 | "es2018", 18 | "dom" 19 | ] 20 | }, 21 | "angularCompilerOptions": { 22 | "enableI18nLegacyMessageIdFormat": false, 23 | "strictInjectionParameters": true, 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/browser-vm/src/modules/mutationObserver.ts: -------------------------------------------------------------------------------- 1 | import { Sandbox } from '../sandbox'; 2 | 3 | export function observerModule(_sandbox: Sandbox) { 4 | const observerSet = new Set(); 5 | 6 | class ProxyMutationObserver extends MutationObserver { 7 | constructor(cb: MutationCallback) { 8 | super(cb); 9 | observerSet.add(this); 10 | } 11 | } 12 | 13 | const recover = () => { 14 | observerSet.forEach((observer) => { 15 | if (typeof observer.disconnect === 'function') observer.disconnect(); 16 | }); 17 | observerSet.clear(); 18 | }; 19 | 20 | return { 21 | recover, 22 | override: { 23 | MutationObserver: ProxyMutationObserver as Function, 24 | }, 25 | }; 26 | } 27 | -------------------------------------------------------------------------------- /packages/hooks/src/asyncHook.ts: -------------------------------------------------------------------------------- 1 | import { ArgsType, SyncHook } from './syncHook'; 2 | 3 | type CallbackReturnType = void | false | Promise; 4 | 5 | export class AsyncHook extends SyncHook { 6 | emit(...data: ArgsType): Promise { 7 | let result; 8 | const ls = Array.from(this.listeners); 9 | if (ls.length > 0) { 10 | let i = 0; 11 | const call = (prev?: any) => { 12 | if (prev === false) { 13 | return false; // Abort process 14 | } else if (i < ls.length) { 15 | return Promise.resolve(ls[i++].apply(null, data)).then(call); 16 | } 17 | }; 18 | result = call(); 19 | } 20 | return Promise.resolve(result); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /dev/app-angular/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false, 7 | }; 8 | 9 | /* 10 | * For easier debugging in development mode, you can import the following file 11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 12 | * 13 | * This import should be commented out in production mode because it will have a negative impact 14 | * on performance if an error is thrown. 15 | */ 16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 17 | -------------------------------------------------------------------------------- /cypress/support/index.js: -------------------------------------------------------------------------------- 1 | // *********************************************************** 2 | // This example support/index.js is processed and 3 | // loaded automatically before your test files. 4 | // 5 | // This is a great place to put global configuration and 6 | // behavior that modifies Cypress. 7 | // 8 | // You can change the location of this file or turn off 9 | // automatically serving support files with the 10 | // 'supportFile' configuration option. 11 | // 12 | // You can read more here: 13 | // https://on.cypress.io/configuration 14 | // *********************************************************** 15 | 16 | // Import commands.js using ES2015 syntax: 17 | import './commands'; 18 | 19 | // Alternatively you can use CommonJS syntax: 20 | // require('./commands') 21 | -------------------------------------------------------------------------------- /packages/es-module/__tests__/case/exportDeclaration/m1.js: -------------------------------------------------------------------------------- 1 | import dd, { 2 | name, 3 | a, 4 | b, 5 | c, 6 | d, 7 | aa, 8 | fn1, 9 | fn2, 10 | _fn3, 11 | cls, 12 | name1, 13 | bar, 14 | default as dd2, 15 | } from './m2.js'; 16 | 17 | expect(name).toBe('m2'); 18 | expect(dd.length).toBe(1); 19 | expect(dd[0]).toBe('default'); 20 | expect(dd2 === dd).toBe(true); 21 | expect(a).toBe(1); 22 | expect(b).toBe(2); 23 | expect(c).toBe(3); 24 | expect(d).toBe(4); 25 | expect(aa).toBe(1); 26 | expect(fn1()).toBe('fn1'); 27 | expect(fn2()).toBe('fn2'); 28 | expect(_fn3()).toBe('fn3'); 29 | expect(new cls().world()).toBe('cls.world'); 30 | expect(name1).toBe('n1'); 31 | expect(bar).toBe('n2'); 32 | 33 | setTimeout(() => { 34 | expect(a).toBe('aa'); 35 | }); 36 | -------------------------------------------------------------------------------- /website/i18n/zh/docusaurus-plugin-content-docs/current.json: -------------------------------------------------------------------------------- 1 | { 2 | "version.label": { 3 | "message": "Next", 4 | "description": "The label for version current" 5 | }, 6 | "sidebar.guide.category.指南": { 7 | "message": "指南", 8 | "description": "The label for category 指南 in sidebar guide" 9 | }, 10 | "sidebar.guide.category.快速开始": { 11 | "message": "快速开始", 12 | "description": "The label for category 快速开始 in sidebar guide" 13 | }, 14 | "sidebar.guide.category.进阶功能": { 15 | "message": "进阶功能", 16 | "description": "The label for category 进阶功能 in sidebar guide" 17 | }, 18 | "sidebar.api.category.Garfish methods": { 19 | "message": "Garfish methods", 20 | "description": "The label for category Garfish methods in sidebar api" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /dev/app-angular/e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | import { browser, logging } from 'protractor'; 3 | 4 | describe('workspace-project App', () => { 5 | let page: AppPage; 6 | 7 | beforeEach(() => { 8 | page = new AppPage(); 9 | }); 10 | 11 | it('should display welcome message', () => { 12 | page.navigateTo(); 13 | expect(page.getTitleText()).toEqual('angular app is running!'); 14 | }); 15 | 16 | afterEach(async () => { 17 | // Assert that there are no errors emitted from the browser 18 | const logs = await browser.manage().logs().get(logging.Type.BROWSER); 19 | expect(logs).not.toContain( 20 | jasmine.objectContaining({ 21 | level: logging.Level.SEVERE, 22 | } as logging.Entry), 23 | ); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /website/src/components/lifeCycle/_beforeMount.mdx: -------------------------------------------------------------------------------- 1 | import Highlight from '@site/src/components/Highlight'; 2 | 3 | - Type: (appInfo: AppInfo, appInstance: interfaces.App, cacheMode: boolean) => void 4 | - 该 `hook` 的参数分别为:`appInfo` 信息、`appInstance` 应用实例、是否为 `缓存模式` 渲染和销毁 5 | - Kind: `sync`, `sequential` 6 | - Previous Hook: `beforeEval`、`afterEval` 7 | - Trigger: 8 | 9 | - 此时子应用资源准备完成,运行时环境初始化完成,准备开始渲染子应用 DOM 树; 10 | - 在调用 `app.mount` 或 `app.show` 触发该 `hook`,用户除了手动调用这两个方法外,`Garfish Router` 托管模式还会自动触发 11 | - 在使用 `app.mount` 渲染应用是 `cacheMode` 为 `false`; 12 | - 在使用 `app.show` 渲染应用是 `cacheMode` 为 `true`; 13 | 14 | - 示例 15 | 16 | ```ts 17 | Garfish.run({ 18 | ..., 19 | beforeMount(appInfo) { 20 | console.log('子应用开始渲染', appInfo.name); 21 | } 22 | }) 23 | ``` 24 | -------------------------------------------------------------------------------- /dev/app-esm/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | esm-app 8 | 13 | 14 | 15 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /dev/app-vue-vite/src/App.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 14 | 15 | 27 | -------------------------------------------------------------------------------- /packages/remote-module/src/apis/esModule.ts: -------------------------------------------------------------------------------- 1 | import { isObject, isPromise } from '@garfish/utils'; 2 | 3 | type ESModuleResult = { 4 | default: T; 5 | __esModule: true; 6 | }; 7 | 8 | // prettier-ignore 9 | export function esModule>(obj: T): Promise ? P : T>>; 10 | export function esModule>(obj: T): T; 11 | export function esModule(obj: T): ESModuleResult; 12 | 13 | export function esModule(obj: any) { 14 | if (isObject(obj) && obj.__esModule === true) { 15 | return obj; 16 | } else if (isPromise(obj)) { 17 | return obj.then(esModule); 18 | } else { 19 | const esm = { default: obj }; 20 | Object.defineProperty(esm, '__esModule', { value: true }); 21 | return esm; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/remote-module/src/index.ts: -------------------------------------------------------------------------------- 1 | import { hooks } from './hooks'; 2 | import { loader, cacheModules } from './common'; 3 | import { preload } from './apis/preload'; 4 | import { esModule } from './apis/esModule'; 5 | import { loadModule } from './apis/loadModule'; 6 | import { loadModuleSync } from './apis/loadModuleSync'; 7 | import { setModuleConfig } from './apis/setModuleConfig'; 8 | 9 | // Remote module loader uses singleton mode 10 | const Apis = { 11 | preload, 12 | esModule, 13 | loadModule, 14 | loadModuleSync, 15 | setModuleConfig, 16 | hooks, 17 | loader, 18 | cacheModules, 19 | }; 20 | 21 | export { 22 | preload, 23 | esModule, 24 | loadModule, 25 | loadModuleSync, 26 | setModuleConfig, 27 | hooks, 28 | loader, 29 | cacheModules, 30 | Apis as default, 31 | }; 32 | -------------------------------------------------------------------------------- /website/i18n/en/docusaurus-plugin-content-docs/current.json: -------------------------------------------------------------------------------- 1 | { 2 | "version.label": { 3 | "message": "Next", 4 | "description": "The label for version current" 5 | }, 6 | "sidebar.guide.category.指南": { 7 | "message": "guide", 8 | "description": "The label for category 指南 in sidebar guide" 9 | }, 10 | "sidebar.guide.category.快速开始": { 11 | "message": "Quick start", 12 | "description": "The label for category 快速开始 in sidebar guide" 13 | }, 14 | "sidebar.guide.category.进阶功能": { 15 | "message": "Advanced features", 16 | "description": "The label for category 进阶功能 in sidebar guide" 17 | }, 18 | "sidebar.api.category.Garfish methods": { 19 | "message": "Garfish methods", 20 | "description": "The label for category Garfish methods in sidebar api" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /website/src/components/lifeCycle/_afterMount.mdx: -------------------------------------------------------------------------------- 1 | import Highlight from '@site/src/components/Highlight'; 2 | 3 | - Type: (appInfo: AppInfo, appInstance: interfaces.App, cacheMode: boolean) => void 4 | - 该 `hook` 的参数分别为:`appInfo` 信息、`appInstance` 应用实例、是否为 `缓存模式` 渲染和销毁 5 | - Kind: `sync`, `sequential` 6 | - Previous Hook: `beforeLoad`、`afterLoad`、`beforeMount` 7 | - Trigger: 8 | 9 | - 此时子应用 DOM 树已渲染完成,garfish 实例 `activeApps` 中已添加当前子应用 app 实例; 10 | - 在挂载过程中,会调用应用生命周期中的 [`render` 函数](/guide/start#2导出-provider-函数),用户可在挂载前定义相关操作; 11 | - 若挂载过程中出现异常,会触发 [errorMountApp](/api/run#errormountapp),同时会清除已创建的 app 渲染容器 appContainer 12 | 13 | - 示例 14 | 15 | ```ts 16 | Garfish.run({ 17 | ..., 18 | afterMount(appInfo) { 19 | console.log('子应用渲染结束', appInfo.name); 20 | } 21 | }) 22 | ``` 23 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "rootDir": ".", 5 | "outDir": "dist", 6 | "module": "ESNext", 7 | "target": "es2016", 8 | "resolveJsonModule": true, 9 | "lib": ["ES2018","dom"], 10 | "moduleResolution": "node", 11 | "allowJs": true, 12 | "sourceMap": true, 13 | "declaration": true, 14 | "noImplicitThis": true, 15 | "esModuleInterop": true, 16 | "emitDecoratorMetadata": true, 17 | "strictNullChecks": true, 18 | "experimentalDecorators": true, 19 | "skipLibCheck": true, 20 | "paths": { 21 | "@garfish/*": ["packages/*/src"] 22 | }, 23 | "types": ["jasmine", "jest"] 24 | }, 25 | "include": [ 26 | "packages/*/src", 27 | "packages/*/__tests__", 28 | "packages/global.d.ts" 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /dev/app-vue/src/store/store.js: -------------------------------------------------------------------------------- 1 | export default { 2 | state: { 3 | id: 1, 4 | todos: [{ id: 1, text: 'write a vue mudole demo', done: false }], 5 | }, 6 | 7 | mutations: { 8 | done(state, id) { 9 | // state.todos.forEach((item) => item.id === id && (item.done = true)); 10 | this.state.todo = this.state.todo.filter((item) => item.id !== id); 11 | }, 12 | add(state, item) { 13 | state.id += 1; 14 | item.id = state.id; 15 | state.todos.push(item); 16 | }, 17 | }, 18 | 19 | actions: { 20 | done(context, id) { 21 | context.commit('done', id); 22 | }, 23 | add(context, item) { 24 | context.commit('add', item); 25 | }, 26 | }, 27 | 28 | getters: { 29 | doneTodos: (state) => state.todos.filter((todo) => todo.done), 30 | }, 31 | }; 32 | -------------------------------------------------------------------------------- /website/i18n/en/docusaurus-plugin-content-docs/current/issues/multipleApp.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Multiple Garfish Instances 3 | slug: /issues/multipleApp 4 | order: 2 5 | --- 6 | 7 | ## Non-Nested Scenarios 8 | 9 | - In non-nested scenarios, sub-applications should not introduce Garfish packages in the installation and import them for use. 10 | - If you want to use the Garfish package in a micro front-end scenario, you can use the interface via `window.Garfish` when you are in the micro front-end environment. 11 | 12 | ```js 13 | if (window.__GARFISH__) { 14 | window.Garfish.xx; 15 | } 16 | ``` 17 | 18 | ## Nested scenarios 19 | 20 | - Garfish is currently designed internally to support nested scenarios, so if the business has a claim on this piece you can use it to help us advance our capabilities in nested scenarios together. 21 | -------------------------------------------------------------------------------- /packages/core/__tests__/resources/vueApp.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | vue sub app 5 | 6 | 7 |
8 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /website/src/components/lifeCycle/_beforeEval.mdx: -------------------------------------------------------------------------------- 1 | import Highlight from '@site/src/components/Highlight'; 2 | 3 | - Type: (appInfo: AppInfo, code: string, env: Record, url: string, options) => void 4 | - 该 `hook` 的参数分别为:`appInfo` 信息、`code` 执行的代码、`env` 要注入的环境变量,`url` 代码的资源地址、`options` 参数选项(例如 `async` 是否异步执行、`noEntry` 是否是 `noEntry` 模式); 5 | - Kind: `sync`, `sequential` 6 | - Previous Hook: `beforeMount` 7 | - Trigger: 8 | 9 | - 在子应用挂载过程中、实际执行代码前触发该 hook; 10 | - 应用 html 内的 script 和动态创建的脚本执行时都会触发该 hook 11 | - 此时 DOM 树已添加至文档流中,子应用代码准备执行; 12 | - 若代码执行过程中抛出异常,则将触发 [errorMountApp](/api/run#errormountapp),否则触发 [beforeEval](/api/run#afterEval) 13 | 14 | - 示例 15 | 16 | ```ts 17 | Garfish.beforeEval({ 18 | ..., 19 | beforeEval(appInfo) { 20 | console.log('子应用代码开始执行', appInfo.name); 21 | } 22 | }) 23 | ``` 24 | -------------------------------------------------------------------------------- /packages/browser-vm/src/dynamicNode/processParams.ts: -------------------------------------------------------------------------------- 1 | import { handlerParams } from '../utils'; 2 | 3 | export function injectHandlerParams() { 4 | if (window.MutationObserver) { 5 | const rawObserver = window.MutationObserver.prototype.observe; 6 | MutationObserver.prototype.observe = function () { 7 | return rawObserver.apply(this, handlerParams(arguments)); 8 | }; 9 | } 10 | 11 | // in iframe not modify activeElement 12 | const desc = Object.getOwnPropertyDescriptor( 13 | window.Document.prototype, 14 | 'activeElement', 15 | ); 16 | const rawActiveEl = desc && desc.get; 17 | if (rawActiveEl) { 18 | Object.defineProperty(window.Document.prototype, 'activeElement', { 19 | get(...args) { 20 | return rawActiveEl.apply(handlerParams([this])[0], handlerParams(args)); 21 | }, 22 | }); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/core/__tests__/resources/vue3App.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | vue3 sub app 5 | 6 | 7 |
8 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /dev/app-angular/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode, NgModuleRef } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | import { AppModule } from './app/app.module'; 4 | import { environment } from './environments/environment'; 5 | 6 | if (environment.production) { 7 | enableProdMode(); 8 | } 9 | 10 | let app: void | NgModuleRef; 11 | 12 | async function render() { 13 | await platformBrowserDynamic() 14 | .bootstrapModule(AppModule) 15 | .catch((err) => console.error(err)); 16 | } 17 | 18 | if (!(window as any).__GARFISH__) { 19 | render(); 20 | } 21 | 22 | export const provider = () => { 23 | return { 24 | render, 25 | destroy({ dom }) { 26 | const root = dom 27 | ? dom.querySelector('#root') 28 | : document.querySelector('#root'); 29 | }, 30 | }; 31 | }; 32 | -------------------------------------------------------------------------------- /packages/garfish/__tests__/resources/reactApp.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | react sub app 5 | 6 | 7 |
8 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /dev/app-angular/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting, 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: { 11 | context( 12 | path: string, 13 | deep?: boolean, 14 | filter?: RegExp, 15 | ): { 16 | keys(): string[]; 17 | (id: string): T; 18 | }; 19 | }; 20 | 21 | // First, initialize the Angular testing environment. 22 | getTestBed().initTestEnvironment( 23 | BrowserDynamicTestingModule, 24 | platformBrowserDynamicTesting(), 25 | ); 26 | // Then we find all the tests. 27 | const context = require.context('./', true, /\.spec\.ts$/); 28 | // And load the modules. 29 | context.keys().map(context); 30 | -------------------------------------------------------------------------------- /dev/app-main/src/store.ts: -------------------------------------------------------------------------------- 1 | import { observable, computed, action, makeAutoObservable } from 'mobx'; 2 | class Store { 3 | @observable price = 2; 4 | @observable counter = 1; 5 | @observable activeApp = ''; 6 | @observable apps = []; 7 | @observable isMounted = undefined; 8 | 9 | constructor() { 10 | makeAutoObservable(this); 11 | } 12 | 13 | @computed get total() { 14 | return this.price * this.counter; 15 | } 16 | 17 | @action.bound 18 | increment() { 19 | this.counter++; 20 | } 21 | 22 | @action.bound 23 | decrement() { 24 | this.counter--; 25 | } 26 | 27 | @action 28 | setActiveApp(name) { 29 | this.activeApp = name; 30 | } 31 | @action 32 | setApps(apps) { 33 | this.apps = apps; 34 | } 35 | @action 36 | setIsMounted(isMounted) { 37 | this.isMounted = isMounted; 38 | } 39 | } 40 | 41 | export default Store; 42 | export const store = new Store(); 43 | -------------------------------------------------------------------------------- /website/docs/api/getGlobal.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Garfish.getGlobalObject 3 | slug: /api/getGlobalObject 4 | order: 7 5 | --- 6 | import Highlight from '@site/src/components/Highlight'; 7 | 8 | 用于子应用获取真实 window 的值。 9 | 10 | > 在微前端应用下,子应用将默认开启沙箱模式。在沙箱模式下,子应用中全局变量为被 proxy 的 'fakeWindow',而全局变量(真实 window)默认会被隔离。若子应用需求获取真实 window 的值,可以通过此方法获取。 11 | 12 | 13 | :::tip 14 | 1. 一般情况下我们不建议直接通过此 API 获取真实 window,这样建议的原因是: 15 | - 使用此 API 后子应用产生了一个无法独立运行的逻辑,导致子应用失去独立运行的能力; 16 | - 由于环境变量的修改并不是单向数据流,造成主应用无法感知哪些子应用会去修改 window 上的哪些变量,可能造成数据管理的混乱; 17 | 18 | 2. 若需要获取真实 window 上的环境变量,可通过 [`protectVariable`](/api/run#protectvariable) 属性,将需要共享的属性放入列表中即可通过子应用的全局变量获取,这样主应用能感知到哪些值是会被修改的,哪些值是不会被修改的,能在一定程度上控制 `window` 变量的修改; 19 | ::: 20 | 21 | ## Type 22 | ```ts 23 | getGlobalObject: () => Window & typeof globalThis; 24 | ``` 25 | ## 示例 26 | 27 | ```js 28 | import Garfish from 'garfish'; 29 | 30 | const nativeWindow = Garfish.getGlobalObject(); 31 | ``` 32 | -------------------------------------------------------------------------------- /cypress/integration/common.js: -------------------------------------------------------------------------------- 1 | import matches from 'lodash/matches'; 2 | 3 | export function findMultiAndMatch(count, findObj, matchObj, equalObj, timeout) { 4 | let data = []; 5 | function assert(body) { 6 | data = data.concat( 7 | body?.list?.filter(matches(findObj)).filter(matches(matchObj)) || [], 8 | ); 9 | if (data.length < count) { 10 | cy.wait('@post', { timeout }).its('request.body').then(assert); 11 | } else { 12 | data[0] && expect(data[0]).to.nested.include(equalObj); 13 | expect(data.length).to.be.equal(count); 14 | } 15 | } 16 | cy.wait('@post').its('request.body').then(assert); 17 | } 18 | 19 | export function matchMultiAndMatch( 20 | count, 21 | findObj, 22 | matchObj, 23 | equalObj, 24 | timeout = 3000, 25 | ) { 26 | // findMultiAndMatch(count, matches(findObj), matches(matchObj)); 27 | findMultiAndMatch(count, findObj, matchObj, equalObj, timeout); 28 | } 29 | -------------------------------------------------------------------------------- /dev/app-main/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Garfish Demo 6 | 33 | 34 | 35 |
36 | 37 | 38 | -------------------------------------------------------------------------------- /packages/browser-snapshot/__tests__/sandbox.spec.ts: -------------------------------------------------------------------------------- 1 | import { Sandbox } from '../src/sandbox'; 2 | 3 | describe('test sandbox ', () => { 4 | it('dom sandbox', () => { 5 | document.head.innerHTML = ` 6 | 7 | `; 8 | 9 | const obj: { 10 | name: string; 11 | age: string; 12 | some?: string; 13 | } = { 14 | name: 'zhoushaw', 15 | age: '24', 16 | some: 'test delete', 17 | }; 18 | 19 | const sb = new Sandbox('app1', [], obj); 20 | 21 | const st = document.createElement('style'); 22 | st.style.cssText = 'background: red;'; 23 | document.head.appendChild(st); 24 | obj.name = 'wu'; 25 | obj.age = '23'; 26 | delete obj.some; 27 | 28 | sb.deactivate(); 29 | 30 | const result = document.querySelectorAll('style'); 31 | expect(result).toMatchSnapshot(); 32 | expect(obj).toMatchSnapshot(); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /dev/app-angular/.browserslistrc: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # For the full list of supported browsers by the Angular framework, please see: 6 | # https://angular.io/guide/browser-support 7 | 8 | # You can see what browsers were selected by your queries by running: 9 | # npx browserslist 10 | 11 | last 1 Chrome version 12 | last 1 Firefox version 13 | last 2 Edge major versions 14 | last 2 Safari major versions 15 | last 2 iOS major versions 16 | Firefox ESR 17 | not IE 9-10 # Angular support for IE 9-10 has been deprecated and will be removed as of Angular v11. To opt-in, remove the 'not' prefix on this line. 18 | not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line. 19 | -------------------------------------------------------------------------------- /packages/garfish/__tests__/resources/vueApp.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | vue sub app 5 | 6 | 7 | 8 |
9 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /cypress/support/commands.js: -------------------------------------------------------------------------------- 1 | // *********************************************** 2 | // This example commands.js shows you how to 3 | // create various custom commands and overwrite 4 | // existing commands. 5 | // 6 | // For more comprehensive examples of custom 7 | // commands please read more here: 8 | // https://on.cypress.io/custom-commands 9 | // *********************************************** 10 | // 11 | // 12 | // -- This is a parent command -- 13 | // Cypress.Commands.add('login', (email, password) => { ... }) 14 | // 15 | // 16 | // -- This is a child command -- 17 | // Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... }) 18 | // 19 | // 20 | // -- This is a dual command -- 21 | // Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... }) 22 | // 23 | // 24 | // -- This will overwrite an existing command -- 25 | // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... }) 26 | -------------------------------------------------------------------------------- /website/docs/api/channel.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Garfish.channel 3 | slug: /api/channel 4 | order: 5 5 | --- 6 | 7 | import Highlight from '@site/src/components/Highlight'; 8 | 9 | 用于应用间的通信,`Garfish.channel` 为 Garfish 的实例属性,该属性是 [EventEmitter2](https://github.com/EventEmitter2/EventEmitter2) 的实例。 10 | 11 | ### Type 12 | 13 | ```ts 14 | channel: EventEmitter2; 15 | ``` 16 | 17 | ### 默认值 18 | 19 | - 无 20 | 21 | ### 示例 22 | 23 | ```js 24 | // 子应用监听登录事件 25 | const App = () => { 26 | const handleLogin = (userInfo) => { 27 | console.log(`${userInfo.name} has login`); 28 | }; 29 | 30 | useEffect(() => { 31 | window?.Garfish.channel.on('login', handleLogin); 32 | return () => { 33 | window?.Garfish.channel.removeListener('login', handleLogin); 34 | }; 35 | }); 36 | }; 37 | 38 | // 主应用触发监听事件 39 | api.getLoginInfo.then((res) => { 40 | if (res.code === 0) { 41 | window.Garfish.channel.emit('login', res.data); 42 | } 43 | }); 44 | ``` 45 | -------------------------------------------------------------------------------- /dev/app-react-17/src/constant.ts: -------------------------------------------------------------------------------- 1 | export const prefixCls = 'sub-app-react17'; 2 | 3 | export const backToMainStr = ` 4 | \`\`\`javascript 5 | // window.history.replaceState(null, '', '/examples/main/index'); 6 | window.Garfish.router.push({ path: '/main' }) 7 | \`\`\` 8 | `; 9 | 10 | export const channelWithMainStr = ` 11 | \`\`\`javascript 12 | window?.Garfish?.channel.emit('event', 'hello, 我是 react17 子应用'); 13 | \`\`\` 14 | `; 15 | 16 | export const increaseStr = ` 17 | \`\`\`javascript 18 | props.store.increment(); 19 | \`\`\` 20 | `; 21 | 22 | export const hmrStr = ` 23 | \`\`\`javascript 24 | 25 | \`\`\` 26 | `; 27 | 28 | export const toVue3Str = ` 29 | \`\`\`javascript 30 | // 子应用间跳转推荐使用 window.history.replaceState 或 Garfish.router.push 31 | // 若使用框架自身路由 api 会默认带上basename 32 | 33 | // 方式一: 34 | window.history.replaceState(null, '', '/examples/vue3/home'); 35 | 36 | // 方式二:Garfish.router.push 37 | Garfish.router.push({ path: '/vue3/home' }); 38 | \`\`\` 39 | `; 40 | -------------------------------------------------------------------------------- /dev/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "dev/main": { 3 | "pkgName": "@garfish-dev/main", 4 | "port": 8090 5 | }, 6 | "dev/react17": { 7 | "pkgName": "@garfish-dev/react17", 8 | "port": 8091 9 | }, 10 | "dev/react16": { 11 | "pkgName": "@garfish-dev/react16", 12 | "port": 8092 13 | }, 14 | "dev/react18": { 15 | "pkgName": "@garfish-dev/react18", 16 | "port": 8098 17 | }, 18 | "dev/vue3": { 19 | "pkgName": "@garfish-dev/vue3", 20 | "port": 8093 21 | }, 22 | "dev/vue2": { 23 | "pkgName": "@garfish-dev/vue2", 24 | "port": 8094 25 | }, 26 | "dev/vite": { 27 | "pkgName": "@garfish-dev/vite", 28 | "port": 8095 29 | }, 30 | "dev/vue-sub": { 31 | "pkgName": "@garfish-dev/vue-sub", 32 | "port": 8096 33 | }, 34 | "dev/angular": { 35 | "pkgName": "@garfish-dev/angular", 36 | "port": 8097 37 | }, 38 | "dev/esm": { 39 | "pkgName": "@garfish-dev/esm", 40 | "port": 8099 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/bridge-vue-v3/src/types.d.ts: -------------------------------------------------------------------------------- 1 | export type PropsInfo = { 2 | appName: string; 3 | dom: Element | ShadowRoot | Document; 4 | basename: string; 5 | appRenderInfo: Record; 6 | props: Record; 7 | }; 8 | 9 | export type LoadRootComponent = (opts: PropsInfo) => Promise; 10 | 11 | export type TypeComponent = 12 | | { 13 | rootComponent: T; 14 | loadRootComponent?: LoadRootComponent; 15 | } 16 | | { 17 | rootComponent?: T; 18 | loadRootComponent: LoadRootComponent; 19 | }; 20 | 21 | export type OptionalType = { 22 | createApp: T; 23 | canUpdate: boolean; // by default, allow parcels created with garfish-react-bridge to be updated 24 | appOptions: ( 25 | opts: Record, 26 | ) => Record | Record; 27 | handleInstance: (vueInstance: K, opts: PropsInfo) => void; 28 | }; 29 | 30 | export type UserOptions = TypeComponent & 31 | Partial>; 32 | -------------------------------------------------------------------------------- /dev/app-main/src/static/icons/New.svg: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | -------------------------------------------------------------------------------- /dev/app-vue-2/vue.config.js: -------------------------------------------------------------------------------- 1 | const { getPublicPath, getPort } = require('../util'); 2 | const appName = 'dev/vue2'; 3 | 4 | module.exports = { 5 | chainWebpack: (config) => { 6 | config.optimization.delete('splitChunks'); 7 | config.module 8 | .rule('images') 9 | .use('url-loader') 10 | .loader('url-loader') 11 | .tap((options) => Object.assign(options, { limit: 1024 })); 12 | }, 13 | configureWebpack: () => { 14 | return { 15 | entry: './src/main.ts', 16 | output: { 17 | filename: '[name].[hash].js', 18 | chunkFilename: '[name].[hash].js', 19 | libraryTarget: 'umd', 20 | globalObject: 'window', 21 | }, 22 | }; 23 | }, 24 | publicPath: getPublicPath(appName), 25 | devServer: { 26 | inline: true, 27 | hot: true, 28 | host: '0.0.0.0', 29 | port: getPort(appName), 30 | historyApiFallback: true, 31 | disableHostCheck: true, 32 | headers: { 33 | 'Access-Control-Allow-Origin': '*', 34 | }, 35 | }, 36 | }; 37 | -------------------------------------------------------------------------------- /dev/app-main/src/components/loadApp/loadAppFunc.tsx: -------------------------------------------------------------------------------- 1 | import Garfish from 'garfish'; 2 | import { store } from '../../store'; 3 | 4 | export const loadAppFunc = async ({ appName, basename, domID }) => { 5 | let app; 6 | const loadPromise = () => { 7 | const _app: any = store.apps.find((v: any) => v.name === appName); 8 | // 模拟异步请求 9 | return new Promise(async (resolve, reject) => { 10 | setTimeout(async () => { 11 | const app = await Garfish.loadApp(appName, { 12 | entry: _app?.entry, 13 | basename, 14 | domGetter: () => document.getElementById(domID), 15 | // 缓存设置,建议开启缓存避免重复的编译代码造成的性能浪费 16 | cache: true, 17 | }); 18 | resolve(app); 19 | }, 1000); 20 | }); 21 | }; 22 | 23 | const mountApp = async () => { 24 | app = await loadPromise(); 25 | // 若已经渲染触发 show,只有首次渲染触发 mount,后面渲染都可以触发 show 提供性能 26 | app && !app.mounted ? await app.mount() : app?.show(); 27 | }; 28 | 29 | await mountApp(); 30 | return app; 31 | }; 32 | -------------------------------------------------------------------------------- /dev/app-vue-vite/src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 34 | 35 | 40 | -------------------------------------------------------------------------------- /packages/router/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | import { RouterHook } from '../config'; 2 | 3 | export async function asyncForEach( 4 | arr: T[], 5 | callback: (v: T, k: number, O: T[]) => Promise, 6 | ) { 7 | const length = arr.length; 8 | let k = 0; 9 | while (k < length) { 10 | const kValue = arr[k]; 11 | await callback(kValue, k, arr); 12 | k++; 13 | } 14 | } 15 | 16 | export function toMiddleWare(to, from, cb: RouterHook) { 17 | return new Promise((resolve, reject) => { 18 | try { 19 | cb(to, from, resolve); 20 | } catch (err) { 21 | reject(err); 22 | } 23 | }); 24 | } 25 | 26 | export function createEvent(type) { 27 | let e; 28 | // Compatible with ie 29 | if ( 30 | navigator.userAgent.indexOf('MSIE') !== -1 || 31 | navigator.appVersion.indexOf('Trident/') > 0 32 | ) { 33 | e = document.createEvent('UIEvents'); 34 | e.initUIEvent(type.toLowerCase(), true, false, window, 0); 35 | } else { 36 | e = new Event(type.toLowerCase()); 37 | } 38 | return e; 39 | } 40 | -------------------------------------------------------------------------------- /dev/app-vue/vue.config.js: -------------------------------------------------------------------------------- 1 | const { getPublicPath, getPort } = require('../util'); 2 | const appName = 'dev/vue-sub'; 3 | 4 | module.exports = { 5 | chainWebpack: (config) => { 6 | config.optimization.delete('splitChunks'); 7 | config.module 8 | .rule('images') 9 | .use('url-loader') 10 | .loader('url-loader') 11 | .tap((options) => Object.assign(options, { limit: 1024 })); 12 | }, 13 | configureWebpack: () => { 14 | return { 15 | entry: './src/main.js', 16 | output: { 17 | filename: '[name].[hash].js', 18 | chunkFilename: '[name].[hash].js', 19 | libraryTarget: 'umd', 20 | globalObject: 'window', 21 | }, 22 | }; 23 | }, 24 | publicPath: getPublicPath(appName), 25 | 26 | devServer: { 27 | inline: true, 28 | hot: true, 29 | host: '0.0.0.0', 30 | port: getPort(appName), 31 | historyApiFallback: true, 32 | disableHostCheck: true, 33 | headers: { 34 | 'Access-Control-Allow-Origin': '*', 35 | }, 36 | }, 37 | }; 38 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | // For a detailed explanation regarding each configuration property, visit: 2 | // https://jestjs.io/docs/en/configuration.html 3 | 4 | module.exports = { 5 | clearMocks: true, 6 | testTimeout: 20000, 7 | testEnvironment: 'jsdom', 8 | preset: 'jest-puppeteer', 9 | coverageDirectory: 'coverage', 10 | coveragePathIgnorePatterns: ['__tests__', '/node_modules/'], 11 | coverageProvider: 'v8', 12 | globals: { 13 | __DEV__: true, 14 | __TEST__: true, 15 | __VERSION__: '"unknow"', 16 | }, 17 | preset: 'ts-jest', 18 | transformIgnorePatterns: [ 19 | // Change MODULE_NAME_HERE to your module that isn't being compiled 20 | '/node_modules/(?!(@garfish)).+\\.js$', 21 | ], 22 | transform: { 23 | '^.+\\.(t|j)sx?$': ['@swc/jest'], 24 | }, 25 | rootDir: __dirname, 26 | testMatch: ['/packages/**/__tests__/**/*spec.[jt]s?(x)'], 27 | testPathIgnorePatterns: ['/node_modules/', '/dev/'], 28 | moduleNameMapper: { 29 | '@garfish/(.*)': 'packages/$1/src', 30 | }, 31 | }; 32 | -------------------------------------------------------------------------------- /website/docs/api/api.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 概览 3 | slug: /api 4 | order: 1 5 | --- 6 | 7 | ```ts 8 | import Garfish from "garfish"; 9 | ``` 10 | 在主应用中,我们通过 `import Garfish from "garfish";` 来引入 Garfish,并调用相关 Garfish api 去注册子应用或运行微前端应用。 11 | 12 | 其中,Garfish 是 `garfish` 包默认导出的实例,实例上包含微前端相关API,用户可以通过相应 API 完成对整个微前端应用的管理。 13 | 14 | :::tip 15 | 这里需要特殊说明的是,子应用不需要额外引入 Garfish 实例,子应用可通过 `window.Garfish` 获取全局 Garfish 实例信息,参考 [Garfish 环境变量](../guide/quickStart/env.md)。 16 | ::: 17 | 18 | 19 | ## Garfish 实例方法 20 | - [Garfish.run](/api/run) (用于初始化应用参数、启动路由监听,当路由发生变化时自动激活应用或销毁应用) 21 | - [Garfish.registerApp](/api/registerapp)(用于动态注册应用信息) 22 | - [Garfish.loadApp](/api/loadapp)(可以手动控制子应用加载和销毁) 23 | - [Garfish.router](/api/router)(提供路由跳转和路由守卫能力) 24 | - [Garfish.channel](/api/channel)(提供应用间通信的能力) 25 | - [Garfish.setExternal](/api/setexternal)(支持应用间的依赖共享) 26 | - [Garfish.getGlobalObject](/api/getglobalobject)(用于获取真实 Window) 27 | - [Garfish.setGlobalObject](/api/getglobalobject)(用于设置真实 Window 的值) 28 | - [Garfish.clearEscapeEffect](/api/getglobalobject)(用于清除逃逸的副作用) 29 | -------------------------------------------------------------------------------- /packages/remote-module/src/apis/preload.ts: -------------------------------------------------------------------------------- 1 | import { assert, isAbsolute } from '@garfish/utils'; 2 | import { hooks } from '../hooks'; 3 | import { processAlias } from './setModuleConfig'; 4 | import { loader, resourcesStore } from '../common'; 5 | 6 | // Preload the static resources of the module, so that the module can be loaded synchronously 7 | export function preload(urls: string | Array) { 8 | if (!Array.isArray(urls)) urls = [urls]; 9 | 10 | return Promise.all( 11 | urls.map((url) => { 12 | url = processAlias(url)[0]; 13 | assert( 14 | isAbsolute(url), 15 | `The loading of the remote module must be an absolute path. "${url}"`, 16 | ); 17 | return loader.loadModule(url).then((data) => { 18 | if (data.resourceManager) { 19 | data.resourceManager.originUrl = url; 20 | resourcesStore.push(data.resourceManager); 21 | hooks.lifecycle.preloaded.emit(data.resourceManager); 22 | } 23 | return data; 24 | }); 25 | }), 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /packages/es-module/__tests__/case/exportNamespace/m1.js: -------------------------------------------------------------------------------- 1 | import * as m2 from './m2.js'; 2 | import * as m3 from './m3.js'; 3 | import * as m5 from './m5.js'; 4 | import m2Default from './m2.js'; 5 | import { d } from './m2.js'; 6 | 7 | // 模块对象是不能扩展的 8 | expect(() => { 9 | m2.toString = () => {}; 10 | }).toThrow(); 11 | // 模块对象的原型为 null 12 | expect(Object.getPrototypeOf(m2)).toBe(null); 13 | 14 | expect(m2.toString).toBeUndefined(); 15 | expect(m2[Symbol.toStringTag]).toBe('Module'); 16 | 17 | expect(d).toBe(1); 18 | expect(m2Default).toEqual([3]); 19 | 20 | expect(Object.keys(m2).length).toBe(10); 21 | expect(m2._name).toBe('m2'); 22 | expect(m2.a).toEqual([1]); 23 | expect(m2.b).toEqual([2]); 24 | expect(m2.d).toBe(1); 25 | expect(m2.default).toEqual([3]); 26 | expect(m2.m3Namespace === m3).toBe(true); 27 | expect(m2.n1).toBe('m3'); 28 | expect(m2.n2).toBe('m3'); 29 | expect(m2.name).toBe('m3'); 30 | expect(m2.nn).toBe('m3'); 31 | 32 | // export * 中有相同的字段,会被忽略,default 字段也会被忽略 33 | expect('name' in m5).toBe(false); 34 | expect('default' in m5).toBe(false); 35 | -------------------------------------------------------------------------------- /scripts/verifyCommit.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const chalk = require('chalk'); 3 | const msgPath = process.env.HUSKY_GIT_PARAMS; 4 | const msg = fs.readFileSync(msgPath, 'utf-8').trim(); 5 | const ng = 6 | 'https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#heading=h.greljkmo14y0'; 7 | const commitRE = 8 | /^(revert: )?(feat|fix|docs|dx|style|refactor|perf|test|workflow|build|ci|chore|types|wip|release)(\(.+\))?: .{1,50}/; 9 | 10 | if (!commitRE.test(msg)) { 11 | console.log(); 12 | console.error( 13 | ` ${chalk.bgRed.white(' ERROR ')} ${chalk.red( 14 | 'invalid commit message format.', 15 | )}\n\n` + 16 | chalk.red( 17 | ' Proper commit message format is required for automated changelog generation. Examples:\n\n', 18 | ) + 19 | ` ${chalk.green('feat(compiler): add `comments` option')}\n` + 20 | ` ${chalk.green('fix(event): handle events error (close #28)')}\n\n` + 21 | chalk.red(` See '${ng}' for more details.\n`), 22 | ); 23 | process.exit(1); 24 | } 25 | -------------------------------------------------------------------------------- /dev/app-vue-3/vue.config.js: -------------------------------------------------------------------------------- 1 | const { getPublicPath, getPort } = require('../util'); 2 | const appName = 'dev/vue3'; 3 | 4 | module.exports = { 5 | chainWebpack: (config) => { 6 | config.optimization.delete('splitChunks'); 7 | config.module 8 | .rule('images') 9 | .use('url-loader') 10 | .loader('url-loader') 11 | .tap((options) => Object.assign(options, { limit: 1024 })); 12 | }, 13 | configureWebpack: () => { 14 | return { 15 | entry: './src/main.ts', 16 | output: { 17 | filename: '[name].[hash].js', 18 | chunkFilename: '[name].[hash].js', 19 | libraryTarget: 'umd', 20 | globalObject: 'window', 21 | }, 22 | }; 23 | }, 24 | publicPath: getPublicPath(appName), 25 | devServer: { 26 | inline: true, 27 | hot: true, 28 | host: '0.0.0.0', 29 | port: getPort(appName), 30 | historyApiFallback: true, 31 | allowedHosts: ['all'], 32 | disableHostCheck: true, 33 | headers: { 34 | 'Access-Control-Allow-Origin': '*', 35 | }, 36 | }, 37 | }; 38 | -------------------------------------------------------------------------------- /dev/app-angular/e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Protractor configuration file, see link for more information 3 | // https://github.com/angular/protractor/blob/master/lib/config.ts 4 | 5 | const { SpecReporter, StacktraceOption } = require('jasmine-spec-reporter'); 6 | 7 | /** 8 | * @type { import("protractor").Config } 9 | */ 10 | exports.config = { 11 | allScriptsTimeout: 11000, 12 | specs: ['./src/**/*.e2e-spec.ts'], 13 | capabilities: { 14 | browserName: 'chrome', 15 | }, 16 | directConnect: true, 17 | baseUrl: 'http://localhost:8097/', 18 | framework: 'jasmine', 19 | jasmineNodeOpts: { 20 | showColors: true, 21 | defaultTimeoutInterval: 30000, 22 | print: function () {}, 23 | }, 24 | onPrepare() { 25 | require('ts-node').register({ 26 | project: require('path').join(__dirname, './tsconfig.json'), 27 | }); 28 | jasmine.getEnv().addReporter( 29 | new SpecReporter({ 30 | spec: { 31 | displayStacktrace: StacktraceOption.PRETTY, 32 | }, 33 | }), 34 | ); 35 | }, 36 | }; 37 | -------------------------------------------------------------------------------- /packages/browser-snapshot/src/patchers/multithread.ts: -------------------------------------------------------------------------------- 1 | import { Snapshot } from './interceptor'; 2 | export class MultithreadInterceptor { 3 | snapshotBefore: Snapshot; 4 | targetToProtect: HTMLElement; 5 | uuid: string; 6 | constructor() { 7 | this.uuid = '' + -new Date(); 8 | this.targetToProtect.setAttribute('gar', this.uuid); 9 | } 10 | beforeAdd() { 11 | this.snapshotBefore = Snapshot.take(document.getElementsByTagName('style')); 12 | } 13 | afterAdd() { 14 | const now = Snapshot.take(document.getElementsByTagName('style')); 15 | const diff = this.snapshotBefore.diff(now); 16 | diff.created.arrDoms.forEach((styleElement: HTMLStyleElement) => { 17 | const styleSheet: CSSStyleSheet = styleElement.sheet as CSSStyleSheet; 18 | if (!styleSheet.cssRules) { 19 | return; 20 | } 21 | for ( 22 | let i = 0, rule: CSSStyleRule; 23 | (rule = styleSheet.cssRules[i] as CSSStyleRule); 24 | i++ 25 | ) { 26 | rule.selectorText += `[gar=${this.uuid}] `; 27 | } 28 | }); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/bridge-vue-v2/src/types.d.ts: -------------------------------------------------------------------------------- 1 | export type PropsInfo = { 2 | appName: string; 3 | dom: Element | ShadowRoot | Document; 4 | basename: string; 5 | appRenderInfo: Record; 6 | props: Record; 7 | }; 8 | 9 | export type LoadRootComponent = (opts: PropsInfo) => Promise; 10 | 11 | export type TypeComponent = 12 | | { 13 | rootComponent: T; 14 | loadRootComponent?: LoadRootComponent; 15 | } 16 | | { 17 | rootComponent?: T; 18 | loadRootComponent: LoadRootComponent; 19 | }; 20 | 21 | export type OptionalType any> = { 22 | Vue: T; 23 | canUpdate: boolean; // by default, allow parcels created with garfish-react-bridge to be updated 24 | appOptions: ( 25 | opts: Record, 26 | ) => Record | Record; 27 | handleInstance: (vueInstance: InstanceType, opts: PropsInfo) => void; 28 | }; 29 | 30 | export type UserOptions< 31 | T extends new (...args: any) => any, 32 | U, 33 | > = TypeComponent & Partial>; 34 | -------------------------------------------------------------------------------- /dev/app-react-17/src/components/detail/index.tsx: -------------------------------------------------------------------------------- 1 | import { useSearchParams, useNavigate } from 'react-router-dom'; 2 | import { detail } from '../constant'; 3 | import { Descriptions, Breadcrumb } from '@arco-design/web-react'; 4 | import './index.less'; 5 | 6 | const BreadcrumbItem = Breadcrumb.Item; 7 | 8 | const Detail = () => { 9 | const navigate = useNavigate(); 10 | const [searchParams] = useSearchParams(); 11 | const content = searchParams.get('id') && detail[searchParams.get('id')!]; 12 | 13 | return ( 14 |
15 | 16 | 17 | navigate({ pathname: '/list' })}>List 18 | 19 | {searchParams.get('id')} 20 | 21 | 22 | 29 |
30 | ); 31 | }; 32 | 33 | export default Detail; 34 | -------------------------------------------------------------------------------- /dev/app-vue/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "outDir": "build", 5 | "target": "esnext", 6 | "module": "esnext", 7 | "strict": true, 8 | "lib": ["esnext", "dom", "dom.iterable", "scripthost"], 9 | "experimentalDecorators": true, 10 | "sourceMap": true, 11 | "allowJs": false, 12 | "jsx": "react-jsx", 13 | "moduleResolution": "node", 14 | "resolveJsonModule": true, 15 | "forceConsistentCasingInFileNames": true, 16 | "noImplicitReturns": true, 17 | "noImplicitThis": true, 18 | "noImplicitAny": false, 19 | "strictNullChecks": true, 20 | "suppressImplicitAnyIndexErrors": true, 21 | "esModuleInterop": true, 22 | "skipLibCheck": true, 23 | "skipDefaultLibCheck": true, 24 | "isolatedModules": false, 25 | "noUnusedLocals": true, 26 | "plugins": [ 27 | { 28 | "name": "typescript-styled-plugin" 29 | } 30 | ] 31 | }, 32 | "exclude": ["node_modules"], 33 | "include": [ 34 | "src", 35 | ], 36 | "paths": { 37 | "@/*": ["src/*"] 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /dev/app-vue-2/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "outDir": "build", 5 | "target": "esnext", 6 | "module": "esnext", 7 | "strict": true, 8 | "lib": ["esnext", "dom", "dom.iterable", "scripthost"], 9 | "experimentalDecorators": true, 10 | "sourceMap": true, 11 | "allowJs": false, 12 | "jsx": "react-jsx", 13 | "moduleResolution": "node", 14 | "resolveJsonModule": true, 15 | "forceConsistentCasingInFileNames": true, 16 | "noImplicitReturns": true, 17 | "noImplicitThis": true, 18 | "noImplicitAny": false, 19 | "strictNullChecks": true, 20 | "suppressImplicitAnyIndexErrors": true, 21 | "esModuleInterop": true, 22 | "skipLibCheck": true, 23 | "skipDefaultLibCheck": true, 24 | "isolatedModules": false, 25 | "noUnusedLocals": true, 26 | "plugins": [ 27 | { 28 | "name": "typescript-styled-plugin" 29 | } 30 | ] 31 | }, 32 | "exclude": ["node_modules"], 33 | "include": [ 34 | "src", 35 | ], 36 | "paths": { 37 | "@/*": ["src/*"] 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /dev/app-vue-3/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "outDir": "build", 5 | "target": "esnext", 6 | "module": "esnext", 7 | "strict": true, 8 | "lib": ["esnext", "dom", "dom.iterable", "scripthost"], 9 | "experimentalDecorators": true, 10 | "sourceMap": true, 11 | "allowJs": false, 12 | "jsx": "react-jsx", 13 | "moduleResolution": "node", 14 | "resolveJsonModule": true, 15 | "forceConsistentCasingInFileNames": true, 16 | "noImplicitReturns": true, 17 | "noImplicitThis": true, 18 | "noImplicitAny": false, 19 | "strictNullChecks": true, 20 | "suppressImplicitAnyIndexErrors": true, 21 | "esModuleInterop": true, 22 | "skipLibCheck": true, 23 | "skipDefaultLibCheck": true, 24 | "isolatedModules": false, 25 | "noUnusedLocals": true, 26 | "plugins": [ 27 | { 28 | "name": "typescript-styled-plugin" 29 | } 30 | ] 31 | }, 32 | "exclude": ["node_modules"], 33 | "include": [ 34 | "src", 35 | ], 36 | "paths": { 37 | "@/*": ["src/*"] 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/bridge-vue-v3/README.md: -------------------------------------------------------------------------------- 1 | # `@garfish/bridge-vue-v3` 2 | 3 | [![NPM version](https://img.shields.io/npm/v/@garfish/bridge-vue-v3.svg?style=flat-square)](https://www.npmjs.com/package/@garfish/bridge-vue-v3) 4 | 5 | Vue bridge for vue v3 subapp. For more details, [check here](https://www.garfishjs.org/guide/bridge) 6 | ## Usage 7 | 8 | ```jsx 9 | // child app 10 | import { vueBridge } from '@garfish/bridge-vue-v3'; 11 | 12 | function App() { 13 | return
content
; 14 | } 15 | 16 | export const provider = vueBridge({ 17 | rootComponent: App, 18 | appOptions: ({ basename, dom, appName, props }) => { 19 | // pass the options to createApp. check hhttps://vuejs.org/api/application.html#createApp 20 | return { 21 | el: '#app', 22 | render: () => h(App), 23 | }; 24 | }, 25 | handleInstance: (vueInstance, { basename, dom, appName, props }) => { 26 | // you can do something in handleInstance after get the vueInstance 27 | vueInstance.use(newRouter(basename)); 28 | vueInstance.provide(stateSymbol, createState()); 29 | }, 30 | }); 31 | 32 | ``` 33 | -------------------------------------------------------------------------------- /packages/browser-snapshot/src/patchers/interval.ts: -------------------------------------------------------------------------------- 1 | const rawInterval = window.setInterval; 2 | const rawClearInterval = window.clearInterval; 3 | 4 | export class PatchInterval { 5 | private intervals: Array = []; 6 | constructor() {} 7 | 8 | public activate() { 9 | // @ts-ignore 10 | window.setInterval = ( 11 | handler: Function, 12 | timeout?: number, 13 | ...args: any[] 14 | ) => { 15 | const intervalId = rawInterval(handler, timeout, ...args); 16 | this.intervals = [...this.intervals, intervalId]; 17 | return intervalId; 18 | }; 19 | 20 | // @ts-ignore 21 | window.clearInterval = (intervalId: number) => { 22 | this.intervals = this.intervals.filter((id) => id !== intervalId); 23 | return rawClearInterval(intervalId); 24 | }; 25 | } 26 | 27 | public deactivate(_clearEffects?: boolean) { 28 | if (_clearEffects) { 29 | this.intervals.forEach((id) => window.clearInterval(id)); 30 | } 31 | window.setInterval = rawInterval; 32 | window.clearInterval = rawClearInterval; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /packages/es-module/README.md: -------------------------------------------------------------------------------- 1 | # `@garfish/es-module` 2 | 3 | [![NPM version](https://img.shields.io/npm/v/@garfish/es-module.svg?style=flat-square)](https://www.npmjs.com/package/@garfish/es-module) 4 | 5 | Inspired by [virtual-es-module](https://github.com/imtaotao/virtual-es-module). 6 | 7 | ## Usage 8 | 9 | ```js 10 | import Runtime from '@garfish/es-module'; 11 | // One runtime, one project 12 | const runtime = new Runtime(); 13 | 14 | const module = await runtime.importByUrl('./a.mjs'); 15 | console.log(module); 16 | 17 | const module = await runtime.importByCode(` 18 | import * as m from './a.mjs'; 19 | export default 1; 20 | `); 21 | console.log(module); 22 | ``` 23 | 24 | ## Use in Garfish 25 | 26 | `@garfish/es-module` will bring serious above-the-fold performance problems, child applications should not use `esModule` in production environments. 27 | 28 | ```js 29 | import { GarfishEsModule } from '@garfish/es-module'; 30 | 31 | Garfish.run({ 32 | ... 33 | plugins: [ 34 | ... 35 | GarfishEsModule({ 36 | excludes: ['appName'], 37 | }), 38 | ], 39 | }) 40 | ``` 41 | -------------------------------------------------------------------------------- /dev/app-react-18/src/root.tsx: -------------------------------------------------------------------------------- 1 | import { ConfigProvider } from '@arco-design/web-react'; 2 | import { BrowserRouter, Routes, Route } from 'react-router-dom'; 3 | import App from './App'; 4 | import PageNotFound from './PageNotFound'; 5 | import './App.less'; 6 | import { AppInfo } from '@garfish/bridge-react-v18'; 7 | 8 | export const prefixCls = 'sub-app-react16'; 9 | 10 | const Index =
This is Home Page.
; 11 | const About =
This is About Page.
; 12 | 13 | const RootComponent = (appInfo: AppInfo) => { 14 | const routes = ( 15 | 16 | }> 17 | 18 | 19 | } /> 20 | 21 | 22 | ); 23 | return ( 24 | 25 | {routes} 26 | 27 | ); 28 | }; 29 | 30 | export default RootComponent; 31 | -------------------------------------------------------------------------------- /dev/app-vue-3/src/store.js: -------------------------------------------------------------------------------- 1 | import { reactive, readonly, provide, inject } from 'vue'; 2 | 3 | export const stateSymbol = Symbol('state'); 4 | export const incrementSymbol = Symbol('increment'); 5 | 6 | export const createState = () => { 7 | const state = reactive({ 8 | counter: 0, 9 | id: 1, 10 | todos: [{ id: 1, text: 'default todo', done: false }], 11 | }); 12 | 13 | const increment = () => state.counter++; 14 | 15 | const done = (id) => { 16 | state.todos = state.todos.filter((item) => item.id !== id); 17 | }; 18 | 19 | const add = (item) => { 20 | state.id += 1; 21 | item.id = state.id; 22 | // item.id = state.todos.length + 1; 23 | state.todos.push(item); 24 | console.log('state.todos', state.todos); 25 | }; 26 | 27 | const getDoneTodos = (state) => { 28 | return state.todos.filter((todo) => todo.done); 29 | }; 30 | 31 | return { increment, done, add, getDoneTodos, state: readonly(state) }; 32 | }; 33 | 34 | export const useState = () => inject(stateSymbol); 35 | 36 | export const provideState = () => provide(stateSymbol, createState()); 37 | -------------------------------------------------------------------------------- /packages/remote-module/src/hooks.ts: -------------------------------------------------------------------------------- 1 | import type { ModuleManager } from '@garfish/loader'; 2 | import { 3 | PluginSystem, 4 | SyncHook, 5 | SyncWaterfallHook, 6 | AsyncWaterfallHook, 7 | } from '@garfish/hooks'; 8 | import type { Actuator } from './actuator'; 9 | import type { ModuleConfig, ModuleInfo } from './common'; 10 | 11 | export interface BeforeLoadArgs { 12 | url: string; 13 | options?: ModuleConfig; 14 | } 15 | 16 | export interface afterLoadArgs { 17 | url: string; 18 | code: string; 19 | exports: Record; 20 | } 21 | 22 | export const hooks = new PluginSystem({ 23 | preloaded: new SyncHook<[ModuleManager], any>(), 24 | initModule: new SyncHook<[Actuator], any>('initModule'), 25 | beforeLoadModule: new SyncWaterfallHook('beforeLoadModule'), 26 | asyncBeforeLoadModule: new AsyncWaterfallHook( 27 | 'asyncBeforeLoadModule', 28 | ), 29 | afterLoadModule: new SyncWaterfallHook('afterLoadModule'), 30 | asyncAfterLoadModule: new AsyncWaterfallHook( 31 | 'asyncAfterLoadModule', 32 | ), 33 | }); 34 | -------------------------------------------------------------------------------- /packages/core/__tests__/resources/asyncProviderApp.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | async provider app 5 | 6 | 7 |
8 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /dev/app-angular/README.md: -------------------------------------------------------------------------------- 1 | # Angular 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 10.2.0. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build. 16 | 17 | ## Running unit tests 18 | 19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). 24 | 25 | ## Further help 26 | 27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page. 28 | -------------------------------------------------------------------------------- /packages/browser-vm/src/lifecycle.ts: -------------------------------------------------------------------------------- 1 | import { SyncHook, PluginSystem } from '@garfish/hooks'; 2 | import type { interfaces } from '@garfish/core'; 3 | import type { FakeWindow } from './types'; 4 | 5 | export function sandboxLifecycle() { 6 | return new PluginSystem({ 7 | closed: new SyncHook<[], void>(), 8 | stared: new SyncHook<[FakeWindow?], void>(), 9 | appendNode: new SyncHook<[Element, Element, Element, string], void>(), 10 | beforeClearEffect: new SyncHook<[], void>(), 11 | afterClearEffect: new SyncHook<[], void>(), 12 | beforeInvoke: new SyncHook< 13 | [ 14 | { code: string }, 15 | string?, 16 | Record?, 17 | interfaces.ExecScriptOptions?, 18 | ], 19 | void 20 | >(), 21 | afterInvoke: new SyncHook< 22 | [ 23 | { code: string }, 24 | string?, 25 | Record?, 26 | interfaces.ExecScriptOptions?, 27 | ], 28 | void 29 | >(), 30 | invokeError: new SyncHook< 31 | [Error, string?, Record?, interfaces.ExecScriptOptions?], 32 | void 33 | >(), 34 | }); 35 | } 36 | -------------------------------------------------------------------------------- /dev/app-main/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "outDir": "build", 5 | "target": "esnext", 6 | "module": "esnext", 7 | "strict": true, 8 | "lib": ["esnext", "dom", "dom.iterable", "scripthost"], 9 | "experimentalDecorators": true, 10 | "sourceMap": true, 11 | "allowJs": false, 12 | "jsx": "react-jsx", 13 | "moduleResolution": "node", 14 | "resolveJsonModule": true, 15 | "forceConsistentCasingInFileNames": true, 16 | "noImplicitReturns": true, 17 | "noImplicitThis": true, 18 | "noImplicitAny": false, 19 | "strictNullChecks": true, 20 | "suppressImplicitAnyIndexErrors": true, 21 | "esModuleInterop": true, 22 | "skipLibCheck": true, 23 | "skipDefaultLibCheck": true, 24 | "isolatedModules": false, 25 | "noUnusedLocals": true, 26 | "plugins": [ 27 | { 28 | "name": "typescript-styled-plugin" 29 | } 30 | ] 31 | }, 32 | "exclude": ["node_modules"], 33 | "include": [ 34 | "src/**/*.ts", 35 | "src/**/*.tsx", 36 | "./typings.d.ts" 37 | ], 38 | "paths": { 39 | "@/*": ["src/*"] 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /dev/app-react-16/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "outDir": "build", 5 | "target": "esnext", 6 | "module": "esnext", 7 | "strict": true, 8 | "lib": ["esnext", "dom", "dom.iterable", "scripthost"], 9 | "experimentalDecorators": true, 10 | "sourceMap": true, 11 | "allowJs": false, 12 | "jsx": "react", 13 | "moduleResolution": "node", 14 | "resolveJsonModule": true, 15 | "forceConsistentCasingInFileNames": true, 16 | "noImplicitReturns": true, 17 | "noImplicitThis": true, 18 | "noImplicitAny": false, 19 | "strictNullChecks": true, 20 | "suppressImplicitAnyIndexErrors": true, 21 | "esModuleInterop": true, 22 | "skipLibCheck": true, 23 | "skipDefaultLibCheck": true, 24 | "isolatedModules": false, 25 | "noUnusedLocals": true, 26 | "plugins": [ 27 | { 28 | "name": "typescript-styled-plugin" 29 | } 30 | ] 31 | }, 32 | "exclude": ["node_modules"], 33 | "include": [ 34 | "src/**/*.ts", 35 | "src/**/*.tsx", 36 | "./typings.d.ts" 37 | ], 38 | "paths": { 39 | "@/*": ["src/*"] 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /dev/app-main/src/garfishInit.ts: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Garfish from 'garfish'; 3 | import * as ReactDom from 'react-dom'; 4 | import * as mobxReact from 'mobx-react'; 5 | import * as ReactRouterDom from 'react-router-dom'; 6 | import { Message } from '@arco-design/web-react'; 7 | import { store } from './store'; 8 | import { Config } from './config'; 9 | import { localApps } from './constant'; 10 | 11 | export const GarfishInit = async () => { 12 | const apps = localApps; 13 | console.log('Garfish.run apps', apps); 14 | store.setApps(apps); 15 | Garfish.setExternal({ 16 | react: React, 17 | 'react-dom': ReactDom, 18 | 'react-router-dom': ReactRouterDom, 19 | 'mobx-react': mobxReact, 20 | }); 21 | 22 | Garfish.channel.on('event', (msg: string) => { 23 | Message.success(`主应用收到消息:${msg}`); 24 | }); 25 | 26 | Garfish.router.beforeEach((to, from, next) => { 27 | next(); 28 | }); 29 | 30 | Garfish.router.afterEach((to, from, next) => { 31 | next(); 32 | }); 33 | 34 | try { 35 | Garfish.run(Config); 36 | } catch (error) { 37 | console.log('garfish init error', error); 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /dev/app-react-17/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "outDir": "build", 5 | "target": "esnext", 6 | "module": "esnext", 7 | "strict": true, 8 | "lib": ["esnext", "dom", "dom.iterable", "scripthost"], 9 | "experimentalDecorators": true, 10 | "sourceMap": true, 11 | "allowJs": false, 12 | "jsx": "react-jsx", 13 | "moduleResolution": "node", 14 | "resolveJsonModule": true, 15 | "forceConsistentCasingInFileNames": true, 16 | "noImplicitReturns": true, 17 | "noImplicitThis": true, 18 | "noImplicitAny": false, 19 | "strictNullChecks": true, 20 | "suppressImplicitAnyIndexErrors": true, 21 | "esModuleInterop": true, 22 | "skipLibCheck": true, 23 | "skipDefaultLibCheck": true, 24 | "isolatedModules": false, 25 | "noUnusedLocals": true, 26 | "plugins": [ 27 | { 28 | "name": "typescript-styled-plugin" 29 | } 30 | ] 31 | }, 32 | "exclude": ["node_modules"], 33 | "include": [ 34 | "src/**/*.ts", 35 | "src/**/*.tsx", 36 | "./typings.d.ts" 37 | ], 38 | "paths": { 39 | "@/*": ["src/*"] 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /dev/app-react-18/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "outDir": "build", 5 | "target": "esnext", 6 | "module": "esnext", 7 | "strict": true, 8 | "lib": ["esnext", "dom", "dom.iterable", "scripthost"], 9 | "experimentalDecorators": true, 10 | "sourceMap": true, 11 | "allowJs": false, 12 | "jsx": "react-jsx", 13 | "moduleResolution": "node", 14 | "resolveJsonModule": true, 15 | "forceConsistentCasingInFileNames": true, 16 | "noImplicitReturns": true, 17 | "noImplicitThis": true, 18 | "noImplicitAny": false, 19 | "strictNullChecks": true, 20 | "suppressImplicitAnyIndexErrors": true, 21 | "esModuleInterop": true, 22 | "skipLibCheck": true, 23 | "skipDefaultLibCheck": true, 24 | "isolatedModules": false, 25 | "noUnusedLocals": true, 26 | "plugins": [ 27 | { 28 | "name": "typescript-styled-plugin" 29 | } 30 | ] 31 | }, 32 | "exclude": ["node_modules"], 33 | "include": [ 34 | "src/**/*.ts", 35 | "src/**/*.tsx", 36 | "./typings.d.ts" 37 | ], 38 | "paths": { 39 | "@/*": ["src/*"] 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/core/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Bytedance Inc. 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/hooks/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@garfish/hooks", 3 | "version": "1.8.2", 4 | "description": "hooks module.", 5 | "keywords": [ 6 | "garfish", 7 | "hooks" 8 | ], 9 | "author": "chentao.arthur ", 10 | "homepage": "http://garfish.bytedance.com", 11 | "license": "MIT", 12 | "exports": { 13 | ".": { 14 | "import": "./dist/esm/index.js", 15 | "require": "./dist/index.js" 16 | }, 17 | "./*": "./*" 18 | }, 19 | "main": "dist/index.js", 20 | "module": "dist/esm/index.js", 21 | "types": "./src/index.ts", 22 | "scripts": { 23 | "build": "rimraf dist && tsup src/index.ts", 24 | "dev": "cross-env WATCH=true tsup src/index.ts" 25 | }, 26 | "dependencies": { 27 | "@garfish/utils": "workspace:*" 28 | }, 29 | "repository": { 30 | "type": "git", 31 | "url": "git+https://github.com/bytedance/garfish.git" 32 | }, 33 | "bugs": { 34 | "url": "https://github.com/bytedance/garfish/issues" 35 | }, 36 | "publishConfig": { 37 | "registry": "https://registry.npmjs.org", 38 | "access": "public", 39 | "types": "./dist/index.d.ts" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/es-module/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Bytedance Inc. 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/garfish/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Bytedance Inc. 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/hooks/src/syncHook.ts: -------------------------------------------------------------------------------- 1 | import { warn } from '@garfish/utils'; 2 | 3 | export type Callback = (...args: ArgsType) => K; 4 | export type ArgsType = T extends Array ? T : Array; 5 | 6 | export class SyncHook { 7 | public type: string = ''; 8 | public listeners = new Set>(); 9 | 10 | constructor(type?: string) { 11 | if (type) this.type = type; 12 | } 13 | 14 | on(fn: Callback) { 15 | if (typeof fn === 'function') { 16 | this.listeners.add(fn); 17 | } else if (__DEV__) { 18 | warn('Invalid parameter in "Hook".'); 19 | } 20 | } 21 | 22 | once(fn: Callback) { 23 | const self = this; 24 | this.on(function wrapper(...args: Array) { 25 | self.remove(wrapper); 26 | return fn.apply(null, args); 27 | }); 28 | } 29 | 30 | emit(...data: ArgsType) { 31 | if (this.listeners.size > 0) { 32 | this.listeners.forEach((fn) => fn.apply(null, data)); 33 | } 34 | } 35 | 36 | remove(fn: Callback) { 37 | return this.listeners.delete(fn); 38 | } 39 | 40 | removeAll() { 41 | this.listeners.clear(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/loader/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Bytedance Inc. 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/router/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Bytedance Inc. 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/utils/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Bytedance Inc. 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 | -------------------------------------------------------------------------------- /cypress/integration/2-the-vm-sandbox/set-global-variable.spec.js: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | const basename = '/examples'; 4 | 5 | describe('whole process vm sandbox set variable', () => { 6 | beforeEach(() => { 7 | Cypress.env({ 8 | garfishRunConfig: { 9 | basename: basename, 10 | disablePreloadApp: true, 11 | sandbox: { 12 | snapshot: false, 13 | }, 14 | }, 15 | }); 16 | }); 17 | 18 | it('set global history variable', () => { 19 | const ProxyVariableTitle = 'vm sandbox'; 20 | cy.visit('http://localhost:8090'); 21 | 22 | cy.window().then((win) => { 23 | win.history.pushState({}, 'react16', `${basename}/react16/vm-sandbox`); 24 | cy.contains('[data-test=title]', ProxyVariableTitle) 25 | .then(() => { 26 | expect(win.history.scrollRestoration).to.equal('auto'); 27 | }) 28 | .then(() => { 29 | return cy.get('[data-test=click-set-history-proxy-variable]').click(); 30 | }) 31 | .then(() => { 32 | expect(win.history.scrollRestoration).to.equal('manual'); 33 | }); 34 | }); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /packages/bridge-react/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Bytedance Inc. 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/bridge-vue-v2/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Bytedance Inc. 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/bridge-vue-v3/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Bytedance Inc. 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/browser-vm/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Bytedance Inc. 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 | -------------------------------------------------------------------------------- /dev/app-angular/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma'), 14 | ], 15 | client: { 16 | clearContext: false, // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, './coverage/angular'), 20 | reports: ['html', 'lcovonly', 'text-summary'], 21 | fixWebpackSourcePaths: true, 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false, 30 | restartOnFileChange: true, 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /packages/bridge-react-v18/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Bytedance Inc. 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/browser-snapshot/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Bytedance Inc. 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 | -------------------------------------------------------------------------------- /dev/app-react-16/src/App.less: -------------------------------------------------------------------------------- 1 | @import '../node_modules/@arco-design/web-react/dist/css/index.less'; 2 | 3 | @prefix: sub-app-react16; 4 | 5 | .@{prefix}-layout-content { 6 | .App { 7 | text-align: center; 8 | background-color: #282c34; 9 | font-size: 18px; 10 | overflow: hidden; 11 | color: white; 12 | } 13 | 14 | .App-logo { 15 | height: 200px; 16 | pointer-events: none; 17 | } 18 | 19 | .App ul { 20 | display: inline-block; 21 | list-style: none; 22 | } 23 | 24 | .App li { 25 | display: inline-block; 26 | list-style: none; 27 | padding: 10px; 28 | } 29 | 30 | .App a:visited { 31 | color: #61dafb; 32 | } 33 | 34 | .App a.tabActive { 35 | color: #7fffd4; 36 | } 37 | 38 | @media (prefers-reduced-motion: no-preference) { 39 | .App-logo { 40 | animation: App-logo-spin infinite 20s linear; 41 | } 42 | } 43 | 44 | .click-btn { 45 | color: coral; 46 | cursor: pointer; 47 | margin-top: 6px; 48 | } 49 | 50 | @keyframes App-logo-spin { 51 | from { 52 | transform: rotate(0deg); 53 | } 54 | to { 55 | transform: rotate(360deg); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /dev/app-react-18/src/App.less: -------------------------------------------------------------------------------- 1 | @import '../node_modules/@arco-design/web-react/dist/css/index.less'; 2 | 3 | @prefix: sub-app-react16; 4 | 5 | .@{prefix}-layout-content { 6 | .App { 7 | text-align: center; 8 | background-color: #282c34; 9 | font-size: 18px; 10 | overflow: hidden; 11 | color: white; 12 | } 13 | 14 | .App-logo { 15 | height: 200px; 16 | pointer-events: none; 17 | } 18 | 19 | .App ul { 20 | display: inline-block; 21 | list-style: none; 22 | } 23 | 24 | .App li { 25 | display: inline-block; 26 | list-style: none; 27 | padding: 10px; 28 | } 29 | 30 | .App a:visited { 31 | color: #61dafb; 32 | } 33 | 34 | .App a.active { 35 | color: #7fffd4; 36 | } 37 | 38 | @media (prefers-reduced-motion: no-preference) { 39 | .App-logo { 40 | animation: App-logo-spin infinite 20s linear; 41 | } 42 | } 43 | 44 | .click-btn { 45 | color: coral; 46 | cursor: pointer; 47 | margin-top: 6px; 48 | } 49 | 50 | @keyframes App-logo-spin { 51 | from { 52 | transform: rotate(0deg); 53 | } 54 | to { 55 | transform: rotate(360deg); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /packages/remote-module/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Bytedance Inc. 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 | 23 | 24 | -------------------------------------------------------------------------------- /dev/app-angular/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | beforeEach(async () => { 7 | await TestBed.configureTestingModule({ 8 | imports: [RouterTestingModule], 9 | declarations: [AppComponent], 10 | }).compileComponents(); 11 | }); 12 | 13 | it('should create the app', () => { 14 | const fixture = TestBed.createComponent(AppComponent); 15 | const app = fixture.componentInstance; 16 | expect(app).toBeTruthy(); 17 | }); 18 | 19 | it(`should have as title 'angular'`, () => { 20 | const fixture = TestBed.createComponent(AppComponent); 21 | const app = fixture.componentInstance; 22 | expect(app.title).toEqual('angular'); 23 | }); 24 | 25 | it('should render title', () => { 26 | const fixture = TestBed.createComponent(AppComponent); 27 | fixture.detectChanges(); 28 | const compiled = fixture.nativeElement; 29 | expect(compiled.querySelector('.content span').textContent).toContain( 30 | 'angular app is running!', 31 | ); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /packages/browser-vm/src/types.ts: -------------------------------------------------------------------------------- 1 | import { Sandbox } from './sandbox'; 2 | import type { Node } from '@garfish/utils'; 3 | import type { LoaderOptions } from '@garfish/loader'; 4 | 5 | export type FakeWindow = Window & Record; 6 | export type Module = (sandbox: Sandbox) => OverridesData | void; 7 | 8 | export interface OverridesData { 9 | recover?: () => void; 10 | prepare?: () => void; 11 | created?: (context: Sandbox['global']) => void; 12 | override?: Record; 13 | } 14 | export interface ReplaceGlobalVariables { 15 | recoverList: Array; 16 | prepareList: Array; 17 | createdList: Array; 18 | overrideList: Record; 19 | } 20 | 21 | export interface SandboxOptions { 22 | namespace: string; 23 | baseUrl?: string; 24 | fixBaseUrl?: boolean; 25 | disableWith?: boolean; 26 | strictIsolation?: boolean; 27 | modules?: Array; 28 | sourceList?: Array<{ tagName: string; url: string }>; 29 | loaderOptions?: LoaderOptions; 30 | el?: () => Element | ShadowRoot | null; 31 | protectVariable?: () => Array; 32 | insulationVariable?: () => Array; 33 | } 34 | -------------------------------------------------------------------------------- /dev/app-vue-2/src/store/store.js: -------------------------------------------------------------------------------- 1 | export default { 2 | state: { 3 | id: 1, 4 | todos: [{ id: 1, text: 'default todo', done: false }], 5 | basename: '', 6 | mainAppProps: {}, 7 | }, 8 | 9 | mutations: { 10 | done(state, id) { 11 | this.state.todos = this.state.todos.filter((item) => item.id !== id); 12 | }, 13 | add(state, item) { 14 | state.id += 1; 15 | item.id = state.id; 16 | state.todos.push(item); 17 | }, 18 | setProps(state, item) { 19 | state.mainAppProps = item; 20 | }, 21 | setBasename(state, basename) { 22 | state.basename = basename; 23 | }, 24 | }, 25 | 26 | actions: { 27 | done(context, id) { 28 | context.commit('done', id); 29 | }, 30 | add(context, item) { 31 | context.commit('add', item); 32 | }, 33 | setProps(context, item) { 34 | context.commit('setProps', item); 35 | }, 36 | setBasename(context, basename) { 37 | context.commit('setBasename', basename); 38 | }, 39 | }, 40 | 41 | getters: { 42 | doneTodos: (state) => { 43 | if (state.todos && state.todos.length > 0) { 44 | return state.todos.filter((todo) => todo.done); 45 | } 46 | }, 47 | }, 48 | }; 49 | -------------------------------------------------------------------------------- /packages/core/src/plugins/performance/index.ts: -------------------------------------------------------------------------------- 1 | import { getRenderNode } from '@garfish/utils'; 2 | import { interfaces } from '../../index'; 3 | import { SubAppObserver } from './subAppObserver'; 4 | 5 | // Key nodes in Garfish corresponding to the life cycle of registration 6 | export function GarfishPerformance() { 7 | return function (): interfaces.Plugin { 8 | const subAppMap = {}; 9 | return { 10 | name: 'performance', 11 | 12 | beforeLoad(appInfo) { 13 | if (!subAppMap[appInfo.name] && appInfo.domGetter) { 14 | subAppMap[appInfo.name] = new SubAppObserver({ 15 | subAppRootSelector: appInfo.domGetter, 16 | }); 17 | } 18 | subAppMap[appInfo.name].subAppBeforeLoad(appInfo.entry); 19 | }, 20 | 21 | afterLoad(appInfo, appInstance: interfaces.App) { 22 | if (appInstance) { 23 | appInstance.appPerformance = subAppMap[appInfo.name] as any; 24 | } 25 | }, 26 | 27 | beforeMount(appInfo) { 28 | subAppMap[appInfo.name].subAppBeforeMount(appInfo.entry); 29 | }, 30 | 31 | beforeUnmount(appInfo) { 32 | subAppMap[appInfo.name].subAppUnmount(appInfo.entry); 33 | }, 34 | }; 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /packages/remote-module/src/actuator.ts: -------------------------------------------------------------------------------- 1 | import { evalWithEnv } from '@garfish/utils'; 2 | import { ModuleManager } from '@garfish/loader'; 3 | import { hooks } from './hooks'; 4 | import { currentApp, moduleConfig } from './common'; 5 | 6 | export class Actuator { 7 | private manager: ModuleManager; 8 | public env: Record; 9 | 10 | constructor(manager: ModuleManager, externals?: Record) { 11 | this.manager = manager; 12 | this.env = { 13 | exports: {}, 14 | module: null, 15 | require: (key) => 16 | (externals || {})[key] || 17 | (moduleConfig.externals && moduleConfig.externals[key]) || 18 | currentApp?.context?.externals[key], 19 | }; 20 | this.env.module = this.env; 21 | hooks.lifecycle.initModule.emit(this); 22 | } 23 | 24 | execScript() { 25 | const { url, moduleCode } = this.manager; 26 | if (currentApp) { 27 | // Avoid conflict with Garfish cjs 28 | currentApp.execScript(moduleCode, this.env, url, { noEntry: true }); 29 | } else { 30 | const sourceUrl = `\n${url ? `//# sourceURL=${url}\n` : ''}`; 31 | evalWithEnv(`;${moduleCode}\n${sourceUrl}`, this.env, window); 32 | } 33 | return this.env; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /dev/app-react-16/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { reactBridge } from '@garfish/bridge-react'; 4 | import RootComponent from './root'; 5 | import ErrorBoundary from './ErrorBoundary'; 6 | 7 | // 在首次加载和执行时会触发该函数 8 | // export const provider = (props) => { 9 | // const root = props.dom 10 | // ? props.dom.querySelector("#root") 11 | // : document.querySelector("#root"); 12 | 13 | // return { 14 | // render() { 15 | // ReactDOM.render(, root); 16 | // }, 17 | // destroy({ dom }) { 18 | // ReactDOM.unmountComponentAtNode( 19 | // dom ? dom.querySelector("#root") : document.querySelector("#root") 20 | // ); 21 | // }, 22 | // }; 23 | // }; 24 | 25 | export const provider = reactBridge({ 26 | el: '#root', 27 | rootComponent: RootComponent, 28 | errorBoundary: () => , 29 | }); 30 | 31 | // 这能够让子应用独立运行起来,以保证后续子应用能脱离主应用独立运行,方便调试、开发 32 | if (!window.__GARFISH__) { 33 | ReactDOM.render( 34 | , 39 | document.querySelector('#root'), 40 | ); 41 | } 42 | -------------------------------------------------------------------------------- /packages/router/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@garfish/router", 3 | "version": "1.8.2", 4 | "description": "router module.", 5 | "keywords": [ 6 | "garfish", 7 | "router" 8 | ], 9 | "author": "zhouxiao ", 10 | "homepage": "http://garfish.bytedance.com", 11 | "license": "MIT", 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/bytedance/garfish.git" 15 | }, 16 | "bugs": { 17 | "url": "https://github.com/bytedance/garfish/issues" 18 | }, 19 | "exports": { 20 | ".": { 21 | "import": "./dist/esm/index.js", 22 | "require": "./dist/index.js" 23 | }, 24 | "./*": "./*" 25 | }, 26 | "main": "dist/index.js", 27 | "module": "dist/esm/index.js", 28 | "types": "./src/index.ts", 29 | "scripts": { 30 | "build": "rimraf dist && tsup src/index.ts", 31 | "dev": "cross-env WATCH=true tsup src/index.ts" 32 | }, 33 | "dependencies": { 34 | "@garfish/core": "workspace:*", 35 | "@garfish/utils": "workspace:*" 36 | }, 37 | "publishConfig": { 38 | "registry": "https://registry.npmjs.org", 39 | "access": "public", 40 | "types": "./dist/index.d.ts" 41 | }, 42 | "gitHead": "da33dd16bb9e99588f34079f8b961d0cf9f059fc" 43 | } 44 | -------------------------------------------------------------------------------- /packages/loader/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@garfish/loader", 3 | "version": "1.8.2", 4 | "description": "loader module.", 5 | "keywords": [ 6 | "garfish", 7 | "loader" 8 | ], 9 | "author": "chentao.arthur ", 10 | "homepage": "http://garfish.bytedance.com", 11 | "license": "MIT", 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/bytedance/garfish.git" 15 | }, 16 | "bugs": { 17 | "url": "https://github.com/bytedance/garfish/issues" 18 | }, 19 | "exports": { 20 | ".": { 21 | "import": "./dist/esm/index.js", 22 | "require": "./dist/index.js" 23 | }, 24 | "./*": "./*" 25 | }, 26 | "main": "dist/index.js", 27 | "module": "dist/esm/index.js", 28 | "types": "./src/index.ts", 29 | "scripts": { 30 | "build": "rimraf dist && tsup src/index.ts", 31 | "dev": "cross-env WATCH=true tsup src/index.ts" 32 | }, 33 | "dependencies": { 34 | "@garfish/hooks": "workspace:*", 35 | "@garfish/utils": "workspace:*" 36 | }, 37 | "publishConfig": { 38 | "registry": "https://registry.npmjs.org", 39 | "access": "public", 40 | "types": "./dist/index.d.ts" 41 | }, 42 | "gitHead": "e8bf48ea8ca8db408a4a0a1cdab8ce4a50ab279b" 43 | } 44 | -------------------------------------------------------------------------------- /packages/loader/src/utils.ts: -------------------------------------------------------------------------------- 1 | import { error, parseContentType } from '@garfish/utils'; 2 | import { Manager, Loader } from './index'; 3 | 4 | export async function request(url: string, config: RequestInit) { 5 | const result = await fetch(url, config || {}); 6 | // Response codes greater than "400" are regarded as errors 7 | if (result.status >= 400) { 8 | error(`"${url}" load failed with status "${result.status}"`); 9 | } 10 | const code = await result.text(); 11 | const type = result.headers.get('content-type') || ''; 12 | const size = Number(result.headers.get('content-size')); 13 | const mimeType = parseContentType(type || ''); 14 | 15 | return { 16 | code, 17 | result, 18 | mimeType, 19 | type, 20 | size: Number.isNaN(size) ? null : size, 21 | }; 22 | } 23 | 24 | export function copyResult(result) { 25 | if (result.resourceManager) { 26 | result.resourceManager = (result.resourceManager as Manager).clone(); 27 | } 28 | return result; 29 | } 30 | 31 | // Compatible with old api 32 | export function mergeConfig(loader: Loader, url: string) { 33 | const extra = loader.requestConfig; 34 | const config = typeof extra === 'function' ? extra(url) : extra; 35 | return { mode: 'cors', ...config } as RequestInit; 36 | } 37 | --------------------------------------------------------------------------------