├── .eslintignore ├── .eslintrc.js ├── .github └── workflows │ └── gh-pages.yml ├── .gitignore ├── .npmrc ├── .prettierignore ├── .prettierrc.js ├── .stylelintrc.js ├── LICENSE ├── README.md ├── demo ├── .gitignore ├── README.MD ├── index.html ├── package.json ├── src │ ├── App.css │ ├── App.tsx │ ├── Footer.tsx │ ├── List.tsx │ ├── favicon.svg │ ├── header.tsx │ ├── index.css │ ├── logo.png │ ├── logo.svg │ ├── main.tsx │ └── vite-env.d.ts ├── tsconfig.json └── vite.config.ts ├── examples ├── cra-app │ ├── .env │ ├── .gitignore │ ├── .npmrc │ ├── README.md │ ├── components │ │ ├── XButton │ │ │ ├── A │ │ │ │ └── B │ │ │ │ │ └── a.md │ │ │ ├── index.jsx │ │ │ └── index.md │ │ ├── XTable │ │ │ ├── index.jsx │ │ │ └── index.md │ │ ├── XTable2 │ │ │ ├── index.jsx │ │ │ └── index.md │ │ ├── aa │ │ │ └── bb │ │ │ │ └── index.md │ │ ├── bb copy 2 │ │ │ └── bb copy │ │ │ │ └── index.js │ │ └── index.jsx │ ├── config-overrides.js │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── logo192.png │ │ ├── logo512.png │ │ ├── manifest.json │ │ └── robots.txt │ ├── src │ │ ├── App.css │ │ ├── App.jsx │ │ ├── App.test.js │ │ ├── Form.jsx │ │ ├── InnerWarpper.jsx │ │ ├── Pages │ │ │ ├── PageA │ │ │ │ └── index.jsx │ │ │ ├── PageB │ │ │ │ └── index.jsx │ │ │ └── index.js │ │ ├── b.jsx │ │ ├── index.css │ │ ├── index.js │ │ ├── logo.svg │ │ ├── reportWebVitals.js │ │ ├── search-filter.module.less │ │ └── setupTests.js │ └── yarn.lock ├── umi-app │ ├── .editorconfig │ ├── .gitignore │ ├── .npmrc │ ├── .prettierignore │ ├── .prettierrc │ ├── .umirc.ts │ ├── README.md │ ├── components │ │ ├── XButton │ │ │ ├── index.jsx │ │ │ ├── index.md │ │ │ └── index2.md │ │ ├── XTable │ │ │ ├── index.jsx │ │ │ └── index.md │ │ ├── XTable2 │ │ │ ├── index.jsx │ │ │ └── index.md │ │ └── index.jsx │ ├── mock │ │ └── .gitkeep │ ├── package.json │ ├── src │ │ ├── global.less │ │ └── pages │ │ │ ├── TableColspanRowspan │ │ │ ├── index.less │ │ │ └── index.tsx │ │ │ ├── index.less │ │ │ └── index.tsx │ ├── tsconfig.json │ └── typings.d.ts ├── vue2-app │ ├── .gitignore │ ├── .npmrc │ ├── README.md │ ├── babel.config.js │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ └── index.html │ ├── src │ │ ├── App.vue │ │ ├── assets │ │ │ └── logo.png │ │ ├── components │ │ │ ├── App.vue │ │ │ └── HelloWorld.vue │ │ └── main.js │ ├── vue.config.js │ └── yarn.lock ├── vue3-app │ ├── .gitignore │ ├── .npmrc │ ├── README.md │ ├── babel.config.js │ ├── package.json │ ├── pnpm-lock.yaml │ ├── public │ │ ├── favicon.ico │ │ └── index.html │ ├── src │ │ ├── App.vue │ │ ├── assets │ │ │ └── logo.png │ │ ├── components │ │ │ └── HelloWorld.vue │ │ └── main.js │ ├── vue.config.js │ └── yarn.lock └── vue3-vite │ ├── .gitignore │ ├── .npmrc │ ├── README.md │ ├── index.html │ ├── package.json │ ├── public │ └── favicon.ico │ ├── src │ ├── App.vue │ ├── assets │ │ └── logo.png │ ├── components │ │ └── HelloWorld.vue │ ├── env.d.ts │ └── main.ts │ ├── tsconfig.json │ ├── vite.config.ts │ └── yarn.lock ├── package.json ├── packages ├── dev-page │ ├── .gitignore │ ├── README.md │ ├── components.d.ts │ ├── index.html │ ├── moveDir.js │ ├── package.json │ ├── public │ │ └── vite.svg │ ├── src │ │ ├── App.vue │ │ ├── Layout.vue │ │ ├── assets │ │ │ └── vue.svg │ │ ├── components │ │ │ └── HelloWorld.vue │ │ ├── main.ts │ │ ├── pages │ │ │ ├── File │ │ │ │ ├── index.vue │ │ │ │ └── test.ts │ │ │ ├── components │ │ │ │ ├── A │ │ │ │ │ └── index.vue │ │ │ │ └── B │ │ │ │ │ └── index.vue │ │ │ └── readme │ │ │ │ └── index.vue │ │ ├── routes │ │ │ └── index.ts │ │ ├── style.css │ │ ├── style.less │ │ ├── styles │ │ │ └── var.less │ │ ├── utils.ts │ │ └── vite-env.d.ts │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.ts ├── dev-ui │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── index.html │ ├── index.js │ ├── package-lock.json │ ├── package.json │ ├── src │ │ ├── App.vue │ │ ├── IconCompents │ │ │ ├── Aim.vue │ │ │ ├── Ant.vue │ │ │ ├── Close.vue │ │ │ ├── Outline.vue │ │ │ ├── Proxy.vue │ │ │ ├── RightArrow.vue │ │ │ └── SvgIcon.vue │ │ ├── Layout │ │ │ ├── Nav.vue │ │ │ └── index.vue │ │ ├── Pages │ │ │ ├── Docs │ │ │ │ ├── SliderBar.vue │ │ │ │ ├── SliderLink.vue │ │ │ │ └── index.vue │ │ │ └── Setting │ │ │ │ └── index.vue │ │ ├── api │ │ │ └── index.ts │ │ ├── assets │ │ │ ├── General.svg │ │ │ └── logo.png │ │ ├── components │ │ │ ├── AimMode.vue │ │ │ ├── Dialog.vue │ │ │ ├── Folder.vue │ │ │ ├── OverLayer.ts │ │ │ └── ProxyMode.vue │ │ ├── env.d.ts │ │ ├── hooks │ │ │ ├── index.ts │ │ │ ├── useDocs.ts │ │ │ ├── useHotkeys.ts │ │ │ ├── usePrefix.ts │ │ │ ├── useRequest.ts │ │ │ ├── useRoute.ts │ │ │ └── useStore.ts │ │ ├── main.ts │ │ ├── style │ │ │ ├── code.css │ │ │ ├── md.less │ │ │ ├── overLayer.less │ │ │ └── vars.less │ │ └── utils │ │ │ ├── api.ts │ │ │ ├── index.ts │ │ │ └── launchEditor.ts │ ├── tsconfig.json │ └── vite.config.ts └── visual-dev │ ├── LICENSE │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── src │ ├── ast │ │ ├── index.ts │ │ ├── injectComponent.ts │ │ ├── insertReactAttr.ts │ │ └── insertVueAttr.ts │ ├── index.ts │ ├── server │ │ ├── MiddleWare │ │ │ ├── analysisFile.ts │ │ │ ├── getConfig.ts │ │ │ ├── getMenu.ts │ │ │ ├── index.ts │ │ │ ├── injectFile.ts │ │ │ └── pathMap.ts │ │ ├── createServer.ts │ │ └── index.ts │ ├── types │ │ ├── payload.ts │ │ └── types.d.ts │ ├── umi │ │ └── index.ts │ ├── utils │ │ ├── index.ts │ │ ├── parsePath.ts │ │ ├── resolvePath.ts │ │ └── uid.ts │ ├── vite │ │ └── index.ts │ └── webpack │ │ ├── analysisPlugin.ts │ │ ├── index.ts │ │ ├── injectFile.ts │ │ ├── parseReactLoader.ts │ │ └── parseVueLoader.ts │ ├── target │ └── npmlist.json │ └── tsconfig.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── scripts ├── clear.js ├── demo.js └── moveFiles.js ├── tsconfig.json └── types └── types.d.ts /.eslintignore: -------------------------------------------------------------------------------- 1 | dist 2 | examples 3 | packages/visual-dev/dev-ui 4 | packages/visual-dev/dist -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | const { defineConfig } = require('eslint-define-config'); 2 | 3 | module.exports = defineConfig({ 4 | root: true, 5 | parserOptions: { 6 | tsconfigRootDir: __dirname, 7 | sourceType: 'module', 8 | ecmaVersion: 2020, 9 | }, 10 | rules: { 11 | 'no-param-reassign': 0, 12 | }, 13 | extends: [require.resolve('@umijs/fabric/dist/eslint')], 14 | }); 15 | -------------------------------------------------------------------------------- /.github/workflows/gh-pages.yml: -------------------------------------------------------------------------------- 1 | name: pnpm Example Workflow 2 | on: 3 | push: 4 | jobs: 5 | build: 6 | runs-on: ubuntu-20.04 7 | strategy: 8 | matrix: 9 | node-version: [16] 10 | steps: 11 | - uses: actions/checkout@v2 12 | - uses: pnpm/action-setup@v2.0.1 13 | with: 14 | version: 6.20.3 15 | - name: Use Node.js ${{ matrix.node-version }} 16 | uses: actions/setup-node@v2 17 | with: 18 | node-version: ${{ matrix.node-version }} 19 | cache: 'pnpm' 20 | - name: Install dependencies 21 | run: pnpm install 22 | - name: Run unit build 23 | run: pnpm run build 24 | - name: Deploy to gh-pages 25 | uses: peaceiris/actions-gh-pages@v3 26 | with: 27 | deploy_key: ${{ secrets.ACTIONS_DEPLOY_KEY }} 28 | publish_dir: ./demo/dist 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | dist 4 | *.log 5 | explorations 6 | .idea 7 | *.local 8 | .vscode/ 9 | packages/visual-dev/package-lock.json 10 | packages/visual-dev/dev-ui 11 | packages/visual-dev/plugins 12 | packages/visual-dev/target 13 | demo/dev-ui 14 | demo/plugins 15 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | shamefully-hoist = true -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | docs/.vitepress/dist/ 2 | packages/vite/dist/ 3 | packages/*/CHANGELOG.md 4 | LICENSE.md 5 | .prettierignore 6 | yarn.lock 7 | .gitignore 8 | LICENSE 9 | .editorconfig 10 | .gitkeep 11 | .eslintignore 12 | examples 13 | public 14 | *.png 15 | assets 16 | .npmrc 17 | *.svg -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | const fabric = require('@umijs/fabric') 2 | 3 | module.exports = { 4 | ...fabric.prettier 5 | } 6 | -------------------------------------------------------------------------------- /.stylelintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [require.resolve('@umijs/fabric/dist/stylelint')], 3 | rules: { 4 | // your rules 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 haoming 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

visual-dev

2 | 3 |

4 | NPM Version 5 | NPM Downloads 6 | 7 | License 8 |

9 | 10 | ## Introduction 11 | 12 | Just one click, you can jump directly to the local IDE source code and **vue** and **react** are supported! ! 13 | 14 | 一键直接跳转到本地 IDE 源码,支持 vue 和 react !! 15 | 16 | ## Preview 17 | 18 | ![Jan-19-2022 01-34-02](https://user-images.githubusercontent.com/42735363/149988859-8577c98f-74ef-4a36-81a1-682d0e405253.gif) 19 | 20 | ## Motivation 21 | 22 | In a huge project, there are many different components on the page, but it will be very troublesome to find where the component is. Using this plugin, you only need to click to jump to the corresponding position of the ide. 23 | 24 | 在一个大型的项目中,页面上有很多不同的组件,但是要找到组件在哪里会很麻烦。使用这个插件,只需要点击对应的 dom,就能跳转到 IDE 的对应位置。 25 | 26 | ## Installation 27 | 28 | ```bash 29 | npm i visual-dev -D 30 | ``` 31 | 32 | ## Options 33 | 34 | ```typescript 35 | type Options = { 36 | /** 37 | * default open vscode. 38 | */ 39 | editor: Editor; //vscode webstorm atom sublime textmate emacs macvim phpstorm idea 支持多种编辑器,默认使用 vscode 40 | }; 41 | ``` 42 | 43 | ## Usage 44 | 45 | webpack 46 | 47 | ```js 48 | // webpack.config.js 49 | const VisualDev = require('visual-dev/plugins/webpack').default; 50 | 51 | module.exports = { 52 | plugins: [ 53 | new VisualDev({ 54 | editor: 'vscode', 55 | }), 56 | ], 57 | }; 58 | ``` 59 | 60 | umi 61 | 62 | ```js 63 | // .umiirc.ts 64 | { 65 | plugins: ['visual-dev/plugins/umi']; 66 | visualDev: { 67 | editor: 'vscode'; 68 | } 69 | } 70 | ``` 71 | 72 | vite 73 | 74 | ```js 75 | import { defineConfig } from 'vite'; 76 | import vue from '@vitejs/plugin-vue'; 77 | import VisualDev from 'visual-dev/plugins/vite'; 78 | 79 | // https://vitejs.dev/config/ 80 | export default defineConfig({ 81 | plugins: [ 82 | vue(), 83 | VisualDev({ 84 | editor: 'vscode', 85 | }), 86 | ], 87 | }); 88 | ``` 89 | 90 | the project is successfully launched, a small icon will appear in the lower left corner of the screen, which can be triggered by clicking. 91 | 92 | 项目启动成功后,在屏幕的左下角会有一个小图标出现,点击即可触发。 93 | 94 | ## WeChat 95 | 96 | 97 | 98 | ## License 99 | 100 | [MIT LICENSE](./LICENSE) 101 | -------------------------------------------------------------------------------- /demo/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | -------------------------------------------------------------------------------- /demo/README.MD: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wen-haoming/visual-dev/c5f9f6eac32ff003da0c0babe85da57d650f0a7f/demo/README.MD -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 10 | Vite App 11 | 12 | 13 |
14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /demo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vite-project", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "tsc && vite build", 7 | "preview": "vite preview" 8 | }, 9 | "dependencies": { 10 | "react": "^17.0.2", 11 | "react-dom": "^17.0.2" 12 | }, 13 | "devDependencies": { 14 | "@types/react": "^17.0.33", 15 | "@types/react-dom": "^17.0.10", 16 | "@vitejs/plugin-react": "^1.0.7", 17 | "typescript": "^4.4.4", 18 | "vite": "^2.7.2" 19 | }, 20 | "license": "ISC" 21 | } 22 | -------------------------------------------------------------------------------- /demo/src/App.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --brand: #57a9fb; 3 | --brand-font-color: #86909c; 4 | } 5 | .App { 6 | min-height: 100vh; 7 | display: flex; 8 | flex-direction: column; 9 | align-items: center; 10 | justify-content: center; 11 | font-size: calc(10px + 2vmin); 12 | } 13 | 14 | .App-logo { 15 | height: 40vmin; 16 | pointer-events: none; 17 | } 18 | 19 | .logo { 20 | display: flex; 21 | align-items: center; 22 | font-size: calc(10px + 5vmin); 23 | font-weight: 500; 24 | } 25 | 26 | .logo-hl { 27 | margin-right: 1rem; 28 | background-color: var(--brand); 29 | padding: 2px 10px; 30 | color: #fff; 31 | border-radius: 5px; 32 | } 33 | .list { 34 | font-size: 18px; 35 | text-align: left; 36 | } 37 | 38 | .logo-shl { 39 | color: var(--brand); 40 | } 41 | 42 | .App-link { 43 | color: var(--brand); 44 | } 45 | 46 | .title { 47 | max-width: 980px; 48 | padding: 0 50px; 49 | color: var(--brand-font-color); 50 | } 51 | 52 | li { 53 | color: var(--brand-font-color); 54 | } 55 | 56 | .code { 57 | font-size: 18px; 58 | background-color: var(--brand); 59 | color: #fff; 60 | padding: 5px 15px; 61 | border-radius: 3px; 62 | font-weight: 600; 63 | } 64 | 65 | .container { 66 | margin-top: 30px; 67 | margin-bottom: 40px; 68 | text-align: center; 69 | display: flex; 70 | align-items: center; 71 | color: var(--brand-font-color); 72 | font-size: 22px; 73 | } 74 | 75 | .container .text { 76 | display: inline-block; 77 | margin-right: 20px; 78 | animation: twinkling 2s ease-in-out infinite; 79 | } 80 | 81 | .vd-aim { 82 | } 83 | 84 | @keyframes twinkling { 85 | 0% { 86 | opacity: 0.5; 87 | filter: alpha(opacity=20); 88 | transform: scale(1); 89 | } 90 | 91 | 50% { 92 | opacity: 1; 93 | filter: alpha(opacity=50); 94 | transform: scale(1.12); 95 | } 96 | 97 | 100% { 98 | opacity: 0.5; 99 | filter: alpha(opacity=20); 100 | transform: scale(1); 101 | } 102 | } 103 | 104 | .footer { 105 | margin: 50px auto; 106 | font-size: 25px; 107 | } 108 | 109 | .footer .about-me { 110 | text-decoration: none; 111 | font-size: 16px; 112 | margin: 0 20px; 113 | color: var(--brand-font-color); 114 | } 115 | 116 | .footer .anticon-github { 117 | color: #000; 118 | } 119 | -------------------------------------------------------------------------------- /demo/src/App.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from 'react'; 2 | import Header from './header'; 3 | import List from './List'; 4 | import './App.css'; 5 | import Footer from './Footer'; 6 | 7 | function App() { 8 | const wrapper = useRef(); 9 | 10 | useEffect(() => { 11 | setTimeout(() => { 12 | const aimDom = document.querySelector('.vd-aim'); 13 | if (aimDom) { 14 | wrapper.current.appendChild(aimDom); 15 | } 16 | }, 500); 17 | }, []); 18 | 19 | return ( 20 |
21 | 22 |
23 |

Just one click, you can jump directly to the local IDE source code!

24 | 25 |
26 | Try clicking and dragging 👉 27 |
28 | npm i -D visual-dev 29 |
30 |
31 | ); 32 | } 33 | 34 | export default App; 35 | -------------------------------------------------------------------------------- /demo/src/Footer.tsx: -------------------------------------------------------------------------------- 1 | const Footer = () => { 2 | return ( 3 | 23 | ); 24 | }; 25 | 26 | export default Footer; 27 | -------------------------------------------------------------------------------- /demo/src/List.tsx: -------------------------------------------------------------------------------- 1 | const List = () => { 2 | return ( 3 | 8 | ); 9 | }; 10 | 11 | export default List; 12 | -------------------------------------------------------------------------------- /demo/src/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /demo/src/header.tsx: -------------------------------------------------------------------------------- 1 | import logo from './logo.png'; 2 | 3 | const Header = () => { 4 | return ( 5 |
6 |
7 |
visual
8 |
dev
9 |
10 |
11 | ); 12 | }; 13 | 14 | export default Header; 15 | -------------------------------------------------------------------------------- /demo/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 4 | 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; 5 | -webkit-font-smoothing: antialiased; 6 | -moz-osx-font-smoothing: grayscale; 7 | } 8 | 9 | code { 10 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace; 11 | } 12 | -------------------------------------------------------------------------------- /demo/src/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wen-haoming/visual-dev/c5f9f6eac32ff003da0c0babe85da57d650f0a7f/demo/src/logo.png -------------------------------------------------------------------------------- /demo/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /demo/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById('root'), 11 | ); 12 | -------------------------------------------------------------------------------- /demo/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /demo/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": false, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["./src"] 20 | } 21 | -------------------------------------------------------------------------------- /demo/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import react from '@vitejs/plugin-react'; 3 | import VisualDev from './plugins/vite'; 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | plugins: [VisualDev({ noServer: true }), react()], 8 | base: '/visual-dev', 9 | }); 10 | -------------------------------------------------------------------------------- /examples/cra-app/.env: -------------------------------------------------------------------------------- 1 | SKIP_PREFLIGHT_CHECK=true -------------------------------------------------------------------------------- /examples/cra-app/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /examples/cra-app/.npmrc: -------------------------------------------------------------------------------- 1 | shamefully-hoist = false -------------------------------------------------------------------------------- /examples/cra-app/README.md: -------------------------------------------------------------------------------- 1 | # Getting Started with Create React App 2 | 3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 4 | 5 | ## Available Scripts 6 | 7 | In the project directory, you can run: 8 | 9 | ### `yarn start` 10 | 11 | Runs the app in the development mode.\ 12 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 13 | 14 | The page will reload if you make edits.\ 15 | You will also see any lint errors in the console. 16 | 17 | ### `yarn test` 18 | 19 | Launches the test runner in the interactive watch mode.\ 20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 21 | 22 | ### `yarn build` 23 | 24 | Builds the app for production to the `build` folder.\ 25 | It correctly bundles React in production mode and optimizes the build for the best performance. 26 | 27 | The build is minified and the filenames include the hashes.\ 28 | Your app is ready to be deployed! 29 | 30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 31 | 32 | ### `yarn eject` 33 | 34 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!** 35 | 36 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 37 | 38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. 39 | 40 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. 41 | 42 | ## Learn More 43 | 44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 45 | 46 | To learn React, check out the [React documentation](https://reactjs.org/). 47 | 48 | ### Code Splitting 49 | 50 | This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting) 51 | 52 | ### Analyzing the Bundle Size 53 | 54 | This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size) 55 | 56 | ### Making a Progressive Web App 57 | 58 | This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app) 59 | 60 | ### Advanced Configuration 61 | 62 | This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration) 63 | 64 | ### Deployment 65 | 66 | This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment) 67 | 68 | ### `yarn build` fails to minify 69 | 70 | This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify) 71 | -------------------------------------------------------------------------------- /examples/cra-app/components/XButton/A/B/a.md: -------------------------------------------------------------------------------- 1 | # 123 2 | 3 | ## 1213123 -------------------------------------------------------------------------------- /examples/cra-app/components/XButton/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const XButton = ()=>{ 4 | return
Button
5 | } 6 | 7 | export default XButton -------------------------------------------------------------------------------- /examples/cra-app/components/XButton/index.md: -------------------------------------------------------------------------------- 1 | # XButton 2 | ## 1231 3 | 4 | ### 123123 5 | 6 | ```ts 7 | const a = 123; 8 | const b = function(){ 9 | return 'sdfsdf' 10 | } 11 | ``` -------------------------------------------------------------------------------- /examples/cra-app/components/XTable/index.jsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wen-haoming/visual-dev/c5f9f6eac32ff003da0c0babe85da57d650f0a7f/examples/cra-app/components/XTable/index.jsx -------------------------------------------------------------------------------- /examples/cra-app/components/XTable/index.md: -------------------------------------------------------------------------------- 1 | # Xtable 2 | 3 | ## 1231 4 | 5 | ### 123123 -------------------------------------------------------------------------------- /examples/cra-app/components/XTable2/index.jsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wen-haoming/visual-dev/c5f9f6eac32ff003da0c0babe85da57d650f0a7f/examples/cra-app/components/XTable2/index.jsx -------------------------------------------------------------------------------- /examples/cra-app/components/XTable2/index.md: -------------------------------------------------------------------------------- 1 | # Xtable2 2 | 3 | ## 1231 4 | 5 | ### 123123 -------------------------------------------------------------------------------- /examples/cra-app/components/aa/bb/index.md: -------------------------------------------------------------------------------- 1 | 2 | ## 123 3 | 4 | | 参数 | 说明 | 类型 | 5 | |---------|--------------|------------------------------------------------------| 6 | | current | 当前值 | `number` | 7 | | inc | 加,默认加 1 | `(delta?:number) => void` | 8 | | dec | 减,默认减 1 | `(delta?:number) => void` | 9 | | set | 设置 current | `(value: number` \| `((c: number) => number)) => void` | 10 | | reset | 重置为默认值 | `() => void` | 11 | 12 | ### Params 13 | 14 | | 参数 | 说明 | 类型 | 默认值 | 15 | |--------------|--------|----------|--------| 16 | | initialValue | 默认值 | `number` | 0 | 17 | | min | 最小值 | `number` | - | 18 | | max | 最大值 | `number` | - | -------------------------------------------------------------------------------- /examples/cra-app/components/bb copy 2/bb copy/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wen-haoming/visual-dev/c5f9f6eac32ff003da0c0babe85da57d650f0a7f/examples/cra-app/components/bb copy 2/bb copy/index.js -------------------------------------------------------------------------------- /examples/cra-app/components/index.jsx: -------------------------------------------------------------------------------- 1 | 1231 -------------------------------------------------------------------------------- /examples/cra-app/config-overrides.js: -------------------------------------------------------------------------------- 1 | const { override, addWebpackPlugin } = require('customize-cra') 2 | const path = require('path') 3 | // const WebpackDevToolPLugin = require('visual-dev/webpack').default 4 | const WebpackDevToolPLugin = require('../../packages/visual-dev/plugins/webpack').default 5 | 6 | module.exports = override(addWebpackPlugin(new WebpackDevToolPLugin({ 7 | injectFile:false, 8 | analysisPlugin:{ 9 | rootPath:'./src/Pages' 10 | }, 11 | shortcuts:{ 12 | inspect:['c','v','b'] 13 | } 14 | }))) 15 | -------------------------------------------------------------------------------- /examples/cra-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cra-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@ant-design/icons": "^4.7.0", 7 | "@ant-design/pro-form": "^1.43.1", 8 | "@ant-design/pro-layout": "^6.26.2", 9 | "@testing-library/jest-dom": "^5.11.4", 10 | "@testing-library/react": "^11.1.0", 11 | "@testing-library/user-event": "^12.1.10", 12 | "antd": "^4.16.13", 13 | "customize-cra": "^1.0.0", 14 | "react": "^17.0.2", 15 | "react-dom": "^17.0.2", 16 | "react-scripts": "4.0.3", 17 | "visual-dev": "0.3.0", 18 | "web-vitals": "^1.0.1" 19 | }, 20 | "scripts": { 21 | "start": "REACT_APP_NO_CLEAR_CONSOLE=true react-app-rewired start", 22 | "build": "react-app-rewired build", 23 | "test": "react-app-rewired test", 24 | "eject": "react-app-rewired eject" 25 | }, 26 | "eslintConfig": { 27 | "extends": [ 28 | "react-app", 29 | "react-app/jest" 30 | ] 31 | }, 32 | "browserslist": { 33 | "production": [ 34 | ">0.2%", 35 | "not dead", 36 | "not op_mini all" 37 | ], 38 | "development": [ 39 | "last 1 chrome version", 40 | "last 1 firefox version", 41 | "last 1 safari version" 42 | ] 43 | }, 44 | "devDependencies": { 45 | "react-app-rewired": "^2.1.8" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /examples/cra-app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wen-haoming/visual-dev/c5f9f6eac32ff003da0c0babe85da57d650f0a7f/examples/cra-app/public/favicon.ico -------------------------------------------------------------------------------- /examples/cra-app/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 |
33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /examples/cra-app/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wen-haoming/visual-dev/c5f9f6eac32ff003da0c0babe85da57d650f0a7f/examples/cra-app/public/logo192.png -------------------------------------------------------------------------------- /examples/cra-app/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wen-haoming/visual-dev/c5f9f6eac32ff003da0c0babe85da57d650f0a7f/examples/cra-app/public/logo512.png -------------------------------------------------------------------------------- /examples/cra-app/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /examples/cra-app/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /examples/cra-app/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | padding-top: 100px; 4 | } 5 | 6 | .App-logo { 7 | height: 40vmin; 8 | pointer-events: none; 9 | } 10 | 11 | @media (prefers-reduced-motion: no-preference) { 12 | .App-logo { 13 | animation: App-logo-spin infinite 20s linear; 14 | } 15 | } 16 | 17 | .App-header { 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | 40 | :root{ 41 | --b:red; 42 | } -------------------------------------------------------------------------------- /examples/cra-app/src/App.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import logo from './logo.svg'; 3 | import './App.css'; 4 | import B from './b'; 5 | import Form from './Form'; 6 | import PageA from './Pages/PageA' 7 | import PageB from './Pages/PageB' 8 | 9 | function App() { 10 | 11 | return
12 | 13 | 14 |
15 | logo 16 |

123 17 | 18 |

19 |
20 |
21 | 绝对定位 22 |
23 |
24 | 我有margin,padding,border 25 |
26 |
27 | 我有margin,padding,border 28 |
29 | 30 | Learn React 31 | 32 |
33 |
; 34 | } 35 | 36 | export default App; 37 | -------------------------------------------------------------------------------- /examples/cra-app/src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /examples/cra-app/src/Form.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { Card, Input, Tabs } from 'antd'; 3 | import { UpOutlined, DownOutlined } from '@ant-design/icons'; 4 | import ProForm, { QueryFilter, ProFormText, ProFormDatePicker } from '@ant-design/pro-form'; 5 | import styles from './search-filter.module.less'; 6 | 7 | const { TabPane } = Tabs; 8 | const AdvancedSearch = (props) => { 9 | const { onSearch, onTypeChange, defaultType = 'articles', onFilterChange } = props; 10 | const [searchText, setSearchText] = useState(); 11 | const [showFilter, setShowFilter] = useState(true); 12 | const quickSearch = ['小程序开发', '入驻', 'ISV 权限']; 13 | return ( 14 |
15 | { 16 | setSearchText(e.target.value); 17 | }} onSearch={onSearch} style={{ maxWidth: 522, width: '100%' }}/> 18 |
19 | {quickSearch.map((text) => ( { 20 | setSearchText(text); 21 | if (onSearch) { 22 | onSearch(text); 23 | } 24 | }}> 25 | {text} 26 | ))} 27 |
28 |
29 | { 30 | setShowFilter(!showFilter); 31 | }}> 32 | 高级筛选 {showFilter ? : } 33 | }> 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 |
); 48 | }; 49 | export default AdvancedSearch; 50 | -------------------------------------------------------------------------------- /examples/cra-app/src/InnerWarpper.jsx: -------------------------------------------------------------------------------- 1 | const InnerWarpper = ()=>{ 2 | return
3 |
4 |
5 |

InnerWarpper

6 |
7 |
8 |
9 | } 10 | 11 | export default InnerWarpper -------------------------------------------------------------------------------- /examples/cra-app/src/Pages/PageA/index.jsx: -------------------------------------------------------------------------------- 1 | export default () =>{ 2 | return
123
3 | } 4 | -------------------------------------------------------------------------------- /examples/cra-app/src/Pages/PageB/index.jsx: -------------------------------------------------------------------------------- 1 | export default () =>{ 2 | return
123
3 | } 4 | -------------------------------------------------------------------------------- /examples/cra-app/src/Pages/index.js: -------------------------------------------------------------------------------- 1 | import PageA from './PageA' 2 | import PageB from './PageB' 3 | 4 | export default { 5 | PageA, 6 | PageB 7 | } 8 | -------------------------------------------------------------------------------- /examples/cra-app/src/b.jsx: -------------------------------------------------------------------------------- 1 | import { Button } from "antd"; 2 | import React from 'react'; 3 | 4 | const B = () => { 5 | return
6 |
7 |
; 8 | }; 9 | 10 | export default B; -------------------------------------------------------------------------------- /examples/cra-app/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /examples/cra-app/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App.jsx'; 5 | import reportWebVitals from './reportWebVitals'; 6 | import 'antd/dist/antd.css' 7 | 8 | ReactDOM.render( 9 | 10 | 11 | , 12 | document.getElementById('root') 13 | ); 14 | 15 | // If you want to start measuring performance in your app, pass a function 16 | // to log results (for example: reportWebVitals(console.log)) 17 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 18 | reportWebVitals(); 19 | -------------------------------------------------------------------------------- /examples/cra-app/src/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/cra-app/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /examples/cra-app/src/search-filter.module.less: -------------------------------------------------------------------------------- 1 | @import '~antd/es/style/themes/default.less'; 2 | 3 | .selfTrigger { 4 | margin-left: 12px; 5 | } 6 | 7 | .hiddenFilter { 8 | .filter { 9 | height: 0; 10 | } 11 | :global { 12 | .ant-tabs-top > .ant-tabs-nav { 13 | margin-bottom: 0; 14 | } 15 | } 16 | } 17 | 18 | .filter { 19 | overflow: hidden; 20 | } 21 | 22 | .quickSearch { 23 | display: inline-block; 24 | line-height: 40px; 25 | span { 26 | margin-left: 16px; 27 | color: #999; 28 | cursor: pointer; 29 | &:hover { 30 | opacity: 0.85; 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /examples/cra-app/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /examples/umi-app/.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | 15 | [Makefile] 16 | indent_style = tab 17 | -------------------------------------------------------------------------------- /examples/umi-app/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /npm-debug.log* 6 | /yarn-error.log 7 | /yarn.lock 8 | /package-lock.json 9 | 10 | # production 11 | /dist 12 | 13 | # misc 14 | .DS_Store 15 | 16 | # umi 17 | /src/.umi 18 | /src/.umi-production 19 | /src/.umi-test 20 | /.env.local 21 | -------------------------------------------------------------------------------- /examples/umi-app/.npmrc: -------------------------------------------------------------------------------- 1 | shamefully-hoist = true -------------------------------------------------------------------------------- /examples/umi-app/.prettierignore: -------------------------------------------------------------------------------- 1 | **/*.md 2 | **/*.svg 3 | **/*.ejs 4 | **/*.html 5 | package.json 6 | .umi 7 | .umi-production 8 | .umi-test 9 | .editorconfig 10 | .gitkeep 11 | -------------------------------------------------------------------------------- /examples/umi-app/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all", 4 | "printWidth": 80, 5 | "overrides": [ 6 | { 7 | "files": ".prettierrc", 8 | "options": { "parser": "json" } 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /examples/umi-app/.umirc.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'umi'; 2 | 3 | export default defineConfig({ 4 | nodeModulesTransform: { 5 | type: 'none', 6 | }, 7 | routes: [{ path: '/', component: '@/pages/index' }], 8 | fastRefresh: {}, 9 | // plugins:[require.resolve('../../packages/visual-dev/plugins/umi'),], 10 | // visualDev:{} 11 | // plugins:['visual-dev/plugins/umi'], 12 | // chainWebpack(memo) { 13 | // memo.plugin('web-devtools').use(WebpackDevtoolPlugin); 14 | // }, 15 | }); 16 | -------------------------------------------------------------------------------- /examples/umi-app/README.md: -------------------------------------------------------------------------------- 1 | # umi project 2 | 3 | ## Getting Started 4 | 5 | Install dependencies, 6 | 7 | ```bash 8 | $ yarn 9 | ``` 10 | 11 | Start the dev server, 12 | 13 | ```bash 14 | $ yarn start 15 | ``` 16 | -------------------------------------------------------------------------------- /examples/umi-app/components/XButton/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const XButton = ()=>{ 4 | return
Button
5 | } 6 | 7 | export default XButton -------------------------------------------------------------------------------- /examples/umi-app/components/XButton/index.md: -------------------------------------------------------------------------------- 1 | # XButton 2 | ## 1231 3 | 4 | ### 123123 5 | 6 | 7 | 8 | ```ts 9 | const a = 123; 10 | const b = function(){ 11 | return 'sdfsdf' 12 | } 13 | ``` -------------------------------------------------------------------------------- /examples/umi-app/components/XButton/index2.md: -------------------------------------------------------------------------------- 1 | # index2.md 2 | -------------------------------------------------------------------------------- /examples/umi-app/components/XTable/index.jsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wen-haoming/visual-dev/c5f9f6eac32ff003da0c0babe85da57d650f0a7f/examples/umi-app/components/XTable/index.jsx -------------------------------------------------------------------------------- /examples/umi-app/components/XTable/index.md: -------------------------------------------------------------------------------- 1 | # Xtable 2 | 3 | ## 1231 4 | 5 | ### 123123 -------------------------------------------------------------------------------- /examples/umi-app/components/XTable2/index.jsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wen-haoming/visual-dev/c5f9f6eac32ff003da0c0babe85da57d650f0a7f/examples/umi-app/components/XTable2/index.jsx -------------------------------------------------------------------------------- /examples/umi-app/components/XTable2/index.md: -------------------------------------------------------------------------------- 1 | # Xtable2 2 | 3 | ## 1231 4 | 5 | ### 123123 -------------------------------------------------------------------------------- /examples/umi-app/components/index.jsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wen-haoming/visual-dev/c5f9f6eac32ff003da0c0babe85da57d650f0a7f/examples/umi-app/components/index.jsx -------------------------------------------------------------------------------- /examples/umi-app/mock/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wen-haoming/visual-dev/c5f9f6eac32ff003da0c0babe85da57d650f0a7f/examples/umi-app/mock/.gitkeep -------------------------------------------------------------------------------- /examples/umi-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "start": "umi dev", 5 | "build": "umi build", 6 | "postinstall": "umi generate tmp", 7 | "prettier": "prettier --write '**/*.{js,jsx,tsx,ts,less,md,json}'", 8 | "test": "umi-test", 9 | "test:coverage": "umi-test --coverage" 10 | }, 11 | "gitHooks": { 12 | "pre-commit": "lint-staged" 13 | }, 14 | "lint-staged": { 15 | "*.{js,jsx,less,md,json}": [ 16 | "prettier --write" 17 | ], 18 | "*.ts?(x)": [ 19 | "prettier --parser=typescript --write" 20 | ] 21 | }, 22 | "dependencies": { 23 | "@ant-design/icons": "^4.0.2", 24 | "@ant-design/pro-layout": "^6.5.0", 25 | "react": "17.x", 26 | "react-dom": "17.x", 27 | "umi": "^3.5.17", 28 | "visual-dev": "0.3.1" 29 | }, 30 | "devDependencies": { 31 | "@types/react": "^17.0.0", 32 | "@types/react-dom": "^17.0.0", 33 | "@umijs/preset-react": "1.x", 34 | "@umijs/preset-ui": "^2.2.9", 35 | "@umijs/test": "^3.5.17", 36 | "lint-staged": "^10.0.7", 37 | "prettier": "^2.2.0", 38 | "typescript": "^4.1.2", 39 | "yorkie": "^2.0.0" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /examples/umi-app/src/global.less: -------------------------------------------------------------------------------- 1 | #root { 2 | width: 100%; 3 | height: 100%; 4 | } 5 | -------------------------------------------------------------------------------- /examples/umi-app/src/pages/TableColspanRowspan/index.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wen-haoming/visual-dev/c5f9f6eac32ff003da0c0babe85da57d650f0a7f/examples/umi-app/src/pages/TableColspanRowspan/index.less -------------------------------------------------------------------------------- /examples/umi-app/src/pages/TableColspanRowspan/index.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import styles from "./index.less"; 3 | import { Table } from "antd"; 4 | 5 | // In the fifth row, other columns are merged into first column 6 | // by setting it's colSpan to be 0 7 | const renderContent = (value, row, index) => { 8 | const obj = { 9 | children: value, 10 | props: {} 11 | }; 12 | if (index === 4) { 13 | obj.props.colSpan = 0; 14 | } 15 | return obj; 16 | }; 17 | 18 | const columns = [ 19 | { 20 | title: "Name", 21 | dataIndex: "name", 22 | render: (text, row, index) => { 23 | if (index < 4) { 24 | return {text}; 25 | } 26 | return { 27 | children: {text}, 28 | props: { 29 | colSpan: 5 30 | } 31 | }; 32 | } 33 | }, 34 | { 35 | title: "Age", 36 | dataIndex: "age", 37 | render: renderContent 38 | }, 39 | { 40 | title: "Home phone", 41 | colSpan: 2, 42 | dataIndex: "tel", 43 | render: (value, row, index) => { 44 | const obj = { 45 | children: value, 46 | props: {} 47 | }; 48 | if (index === 2) { 49 | obj.props.rowSpan = 2; 50 | } 51 | // These two are merged into above cell 52 | if (index === 3) { 53 | obj.props.rowSpan = 0; 54 | } 55 | if (index === 4) { 56 | obj.props.colSpan = 0; 57 | } 58 | return obj; 59 | } 60 | }, 61 | { 62 | title: "Phone", 63 | colSpan: 0, 64 | dataIndex: "phone", 65 | render: renderContent 66 | }, 67 | { 68 | title: "Address", 69 | dataIndex: "address", 70 | render: renderContent 71 | } 72 | ]; 73 | 74 | const data = [ 75 | { 76 | key: "1", 77 | name: "John Brown", 78 | age: 32, 79 | tel: "0571-22098909", 80 | phone: 18889898989, 81 | address: "New York No. 1 Lake Park" 82 | }, 83 | { 84 | key: "2", 85 | name: "Jim Green", 86 | tel: "0571-22098333", 87 | phone: 18889898888, 88 | age: 42, 89 | address: "London No. 1 Lake Park" 90 | }, 91 | { 92 | key: "3", 93 | name: "Joe Black", 94 | age: 32, 95 | tel: "0575-22098909", 96 | phone: 18900010002, 97 | address: "Sidney No. 1 Lake Park" 98 | }, 99 | { 100 | key: "4", 101 | name: "Jim Red", 102 | age: 18, 103 | tel: "0575-22098909", 104 | phone: 18900010002, 105 | address: "London No. 2 Lake Park" 106 | }, 107 | { 108 | key: "5", 109 | name: "Jake White", 110 | age: 18, 111 | tel: "0575-22098909", 112 | phone: 18900010002, 113 | address: "Dublin No. 2 Lake Park" 114 | } 115 | ]; 116 | 117 | export default () => ( 118 |
119 |
120 | 121 | 122 | 123 | ); 124 | -------------------------------------------------------------------------------- /examples/umi-app/src/pages/index.less: -------------------------------------------------------------------------------- 1 | .title { 2 | background: rgb(121, 242, 157); 3 | } 4 | 5 | #components-layout-demo-top-side-2 .logo { 6 | float: left; 7 | width: 120px; 8 | height: 31px; 9 | margin: 16px 24px 16px 0; 10 | background: rgba(255, 255, 255, 0.3); 11 | } 12 | 13 | .ant-row-rtl #components-layout-demo-top-side-2 .logo { 14 | float: right; 15 | margin: 16px 0 16px 24px; 16 | } 17 | 18 | .site-layout-background { 19 | background: #fff; 20 | } 21 | -------------------------------------------------------------------------------- /examples/umi-app/src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import { Layout, Menu, Breadcrumb, Button, Avatar } from 'antd'; 2 | import { UserOutlined, LaptopOutlined, NotificationOutlined } from '@ant-design/icons'; 3 | import './index.less'; 4 | import TableColspanRowspan from './TableColspanRowspan'; 5 | const { 6 | SubMenu 7 | } = Menu; 8 | const { 9 | Header, 10 | Content, 11 | Sider 12 | } = Layout; 13 | export default (() => { 14 | return 17 | 18 |
19 |
20 | 21 | nav 1 22 | nav 2 23 | nav 3 24 | 25 |
26 | 27 | 28 | 32 | } title="subnav 1"> 33 | option1 34 | option2 35 | option3 36 | option4 37 | 38 | } title="subnav 2"> 39 | option5 40 | option6 41 | option7 42 | option8 43 | 44 | } title="subnav 3"> 45 | option9 46 | option10 47 | option11 48 | option12 49 | 50 | 51 | 52 | 55 | ; 58 | Home 59 | List 60 | App 61 | 62 | ;Avatar;;;; 67 | Content 68 | 69 | 70 | 71 |
; 72 | }); 73 | -------------------------------------------------------------------------------- /examples/umi-app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | "resolveJsonModule": true, 7 | "importHelpers": true, 8 | "jsx": "react-jsx", 9 | "esModuleInterop": true, 10 | "sourceMap": true, 11 | "baseUrl": "./", 12 | "strict": true, 13 | "paths": { 14 | "@/*": ["src/*"], 15 | "@@/*": ["src/.umi/*"] 16 | }, 17 | "allowSyntheticDefaultImports": true 18 | }, 19 | "include": [ 20 | "mock/**/*", 21 | "src/**/*", 22 | "config/**/*", 23 | ".umirc.ts", 24 | "typings.d.ts" 25 | ], 26 | "exclude": [ 27 | "node_modules", 28 | "lib", 29 | "es", 30 | "dist", 31 | "typings", 32 | "**/__test__", 33 | "test", 34 | "docs", 35 | "tests" 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /examples/umi-app/typings.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.css'; 2 | declare module '*.less'; 3 | declare module '*.png'; 4 | declare module '*.svg' { 5 | export function ReactComponent( 6 | props: React.SVGProps, 7 | ): React.ReactElement; 8 | const url: string; 9 | export default url; 10 | } 11 | -------------------------------------------------------------------------------- /examples/vue2-app/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | -------------------------------------------------------------------------------- /examples/vue2-app/.npmrc: -------------------------------------------------------------------------------- 1 | shamefully-hoist = true -------------------------------------------------------------------------------- /examples/vue2-app/README.md: -------------------------------------------------------------------------------- 1 | # vue2-app 2 | 3 | ## Project setup 4 | ``` 5 | npm install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | npm run serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | npm run build 16 | ``` 17 | 18 | ### Lints and fixes files 19 | ``` 20 | npm run lint 21 | ``` 22 | 23 | ### Customize configuration 24 | See [Configuration Reference](https://cli.vuejs.org/config/). 25 | -------------------------------------------------------------------------------- /examples/vue2-app/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /examples/vue2-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue2-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "@vue/cli": "^4.5.15", 12 | "@web-devtools/webpack": "^0.0.3", 13 | "core-js": "^3.6.5", 14 | "visual-dev": "0.2.0", 15 | "vue": "2.6.11" 16 | }, 17 | "devDependencies": { 18 | "@vue/cli-plugin-babel": "~4.5.0", 19 | "@vue/cli-plugin-eslint": "~4.5.0", 20 | "@vue/cli-service": "~4.5.0", 21 | "babel-eslint": "^10.1.0", 22 | "eslint": "^6.7.2", 23 | "eslint-plugin-vue": "^6.2.2", 24 | "vue-template-compiler": "2.6.11" 25 | }, 26 | "eslintConfig": { 27 | "root": true, 28 | "env": { 29 | "node": true 30 | }, 31 | "extends": [ 32 | "plugin:vue/essential", 33 | "eslint:recommended" 34 | ], 35 | "parserOptions": { 36 | "parser": "babel-eslint" 37 | }, 38 | "rules": {} 39 | }, 40 | "browserslist": [ 41 | "> 1%", 42 | "last 2 versions", 43 | "not dead" 44 | ] 45 | } 46 | -------------------------------------------------------------------------------- /examples/vue2-app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wen-haoming/visual-dev/c5f9f6eac32ff003da0c0babe85da57d650f0a7f/examples/vue2-app/public/favicon.ico -------------------------------------------------------------------------------- /examples/vue2-app/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 |
16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /examples/vue2-app/src/App.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 22 | 23 | 33 | -------------------------------------------------------------------------------- /examples/vue2-app/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wen-haoming/visual-dev/c5f9f6eac32ff003da0c0babe85da57d650f0a7f/examples/vue2-app/src/assets/logo.png -------------------------------------------------------------------------------- /examples/vue2-app/src/components/App.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/vue2-app/src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 47 | 48 | 49 | 65 | -------------------------------------------------------------------------------- /examples/vue2-app/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | 4 | Vue.config.productionTip = false 5 | 6 | new Vue({ 7 | render: h => h(App), 8 | }).$mount('#app') 9 | -------------------------------------------------------------------------------- /examples/vue2-app/vue.config.js: -------------------------------------------------------------------------------- 1 | // const WebpackDevToolPLugin = require('visual-dev/plugins/webpack').default 2 | const WebpackDevToolPLugin = require('../../packages/visual-dev/plugins/webpack').default 3 | 4 | module.exports = { 5 | devServer: { 6 | proxy: WebpackDevToolPLugin.proxy({ 7 | '/api': { 8 | target: '', 9 | ws: true, 10 | changeOrigin: true, 11 | }, 12 | '/foo': { 13 | target: '' 14 | } 15 | }) 16 | }, 17 | configureWebpack: { 18 | plugins: [ 19 | new WebpackDevToolPLugin({ 20 | injectFile:false 21 | }) 22 | ] 23 | } 24 | } -------------------------------------------------------------------------------- /examples/vue3-app/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | -------------------------------------------------------------------------------- /examples/vue3-app/.npmrc: -------------------------------------------------------------------------------- 1 | shamefully-hoist=true 2 | -------------------------------------------------------------------------------- /examples/vue3-app/README.md: -------------------------------------------------------------------------------- 1 | # vue3-app 2 | 3 | ## Project setup 4 | ``` 5 | pnpm install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | pnpm run serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | pnpm run build 16 | ``` 17 | 18 | ### Lints and fixes files 19 | ``` 20 | pnpm run lint 21 | ``` 22 | 23 | ### Customize configuration 24 | See [Configuration Reference](https://cli.vuejs.org/config/). 25 | -------------------------------------------------------------------------------- /examples/vue3-app/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /examples/vue3-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue3-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "core-js": "^3.6.5", 12 | "visual-dev": "0.2.1", 13 | "vue": "^3.0.0" 14 | }, 15 | "devDependencies": { 16 | "@vue/cli-plugin-babel": "~4.5.0", 17 | "@vue/cli-plugin-eslint": "~4.5.0", 18 | "@vue/cli-service": "~4.5.0", 19 | "@vue/compiler-sfc": "^3.0.0", 20 | "babel-eslint": "^10.1.0", 21 | "eslint": "^6.7.2", 22 | "eslint-plugin-vue": "^7.0.0" 23 | }, 24 | "eslintConfig": { 25 | "root": true, 26 | "env": { 27 | "node": true 28 | }, 29 | "extends": [ 30 | "plugin:vue/vue3-essential", 31 | "eslint:recommended" 32 | ], 33 | "parserOptions": { 34 | "parser": "babel-eslint" 35 | }, 36 | "rules": {} 37 | }, 38 | "browserslist": [ 39 | "> 1%", 40 | "last 2 versions", 41 | "not dead" 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /examples/vue3-app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wen-haoming/visual-dev/c5f9f6eac32ff003da0c0babe85da57d650f0a7f/examples/vue3-app/public/favicon.ico -------------------------------------------------------------------------------- /examples/vue3-app/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 |
16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /examples/vue3-app/src/App.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 21 | 22 | 32 | -------------------------------------------------------------------------------- /examples/vue3-app/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wen-haoming/visual-dev/c5f9f6eac32ff003da0c0babe85da57d650f0a7f/examples/vue3-app/src/assets/logo.png -------------------------------------------------------------------------------- /examples/vue3-app/src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 41 | 42 | 43 | 62 | -------------------------------------------------------------------------------- /examples/vue3-app/src/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | 4 | createApp(App).mount('#app') 5 | -------------------------------------------------------------------------------- /examples/vue3-app/vue.config.js: -------------------------------------------------------------------------------- 1 | // const WebpackDevToolPLugin = require('visual-dev/plugins/webpack').default 2 | const WebpackDevToolPLugin = require('../../packages/visual-dev/plugins/webpack').default 3 | 4 | module.exports = { 5 | devServer: { 6 | proxy: WebpackDevToolPLugin.proxy({ 7 | '/api': { 8 | target: '', 9 | ws: true, 10 | changeOrigin: true 11 | }, 12 | '/foo': { 13 | target: '' 14 | } 15 | }) 16 | }, 17 | configureWebpack: { 18 | plugins: [ 19 | new WebpackDevToolPLugin({ 20 | analysisPlugin:{ 21 | rootPath:'./src/Pages' 22 | }, 23 | }) 24 | ] 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/vue3-vite/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | -------------------------------------------------------------------------------- /examples/vue3-vite/.npmrc: -------------------------------------------------------------------------------- 1 | shamefully-hoist = true -------------------------------------------------------------------------------- /examples/vue3-vite/README.md: -------------------------------------------------------------------------------- 1 | # Vue 3 + Typescript + Vite 2 | 3 | This template should help get you started developing with Vue 3 and Typescript in Vite. The template uses Vue 3 ` 12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/vue3-vite/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue3-vite", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "vue-tsc --noEmit && vite build", 7 | "serve": "vite preview" 8 | }, 9 | "dependencies": { 10 | "vue": "^3.2.16" 11 | }, 12 | "devDependencies": { 13 | "@vitejs/plugin-vue": "^1.9.3", 14 | "typescript": "^4.4.3", 15 | "vite": "^2.6.4", 16 | "vue-tsc": "^0.3.0" 17 | } 18 | } -------------------------------------------------------------------------------- /examples/vue3-vite/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wen-haoming/visual-dev/c5f9f6eac32ff003da0c0babe85da57d650f0a7f/examples/vue3-vite/public/favicon.ico -------------------------------------------------------------------------------- /examples/vue3-vite/src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 23 | -------------------------------------------------------------------------------- /examples/vue3-vite/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wen-haoming/visual-dev/c5f9f6eac32ff003da0c0babe85da57d650f0a7f/examples/vue3-vite/src/assets/logo.png -------------------------------------------------------------------------------- /examples/vue3-vite/src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 34 | 35 | 52 | -------------------------------------------------------------------------------- /examples/vue3-vite/src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare module '*.vue' { 4 | import { DefineComponent } from 'vue' 5 | // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types 6 | const component: DefineComponent<{}, {}, any> 7 | export default component 8 | } 9 | -------------------------------------------------------------------------------- /examples/vue3-vite/src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | 4 | createApp(App).mount('#app') 5 | -------------------------------------------------------------------------------- /examples/vue3-vite/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "useDefineForClassFields": true, 5 | "module": "esnext", 6 | "moduleResolution": "node", 7 | "strict": true, 8 | "jsx": "preserve", 9 | "sourceMap": true, 10 | "resolveJsonModule": true, 11 | "esModuleInterop": true, 12 | "lib": ["esnext", "dom"] 13 | }, 14 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"] 15 | } 16 | -------------------------------------------------------------------------------- /examples/vue3-vite/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import vue from '@vitejs/plugin-vue' 3 | import VisualDev from '../../packages/visual-dev/src/vite' 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | plugins: [vue(),VisualDev()] 8 | }) 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "visual-dev", 3 | "private": true, 4 | "engines": { 5 | "node": ">=10.0.0" 6 | }, 7 | "description": "Just one click, you can jump directly to the local IDE source code!", 8 | "workspaces": [ 9 | "packages/*" 10 | ], 11 | "scripts": { 12 | "dev:visual-dev": "cd ./packages/visual-dev && npm run dev", 13 | "dev:dev-page": "cd ./packages/dev-page && npm run dev", 14 | "dev": "run-p dev:*", 15 | "build:visual-dev": "cd ./packages/visual-dev && npm run build", 16 | "build:dev-page": "cd ./packages/dev-page && npm run build", 17 | "build": "run-p build:*", 18 | "start": "cd examples && cd cra-app && pnpm start", 19 | "clean": "node ./scripts/clear.js", 20 | "format": "prettier --write ./packages && npm run lint", 21 | "lint": "eslint ./packages --ext js,jsx,ts,tsx,vue --fix --color", 22 | "start:page": "cd ./packages/dev-page && pnpm dev" 23 | }, 24 | "repository": { 25 | "type": "git", 26 | "url": "git+https://github.com/wen-haoming/visual-dev.git" 27 | }, 28 | "keywords": [ 29 | "react", 30 | "vue", 31 | "visual-dev", 32 | "webpack", 33 | "umi", 34 | "vite" 35 | ], 36 | "author": "wen-haoming", 37 | "license": "MIT", 38 | "bugs": { 39 | "url": "https://github.com/wen-haoming/visual-dev/issues" 40 | }, 41 | "gitHooks": { 42 | "pre-commit": "lint-staged --concurrent false", 43 | "commit-msg": "fabric verify-commit" 44 | }, 45 | "lint-staged": { 46 | "*": "prettier --write", 47 | "packages/**/*.{js,jsx,ts,tsx}": [ 48 | "eslint --fix", 49 | "prettier --write" 50 | ] 51 | }, 52 | "homepage": "https://github.com/wen-haoming/visual-dev#readme", 53 | "devDependencies": { 54 | "@typescript-eslint/eslint-plugin": "^4.33.0", 55 | "@typescript-eslint/parser": "^4.33.0", 56 | "@umijs/fabric": "2.6.2", 57 | "chokidar": "^3.5.3", 58 | "cross-env": "^7.0.3", 59 | "eslint": "^7.32.0", 60 | "fs-extra": "^10.0.0", 61 | "lint-staged": "^11.2.6", 62 | "npm-run-all": "^4.1.5", 63 | "prettier": "2.4.1", 64 | "typescript": "^4.5.5", 65 | "yorkie": "^2.0.0", 66 | "eslint-define-config": "^1.2.3", 67 | "eslint-plugin-node": "^11.1.0" 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /packages/dev-page/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /packages/dev-page/README.md: -------------------------------------------------------------------------------- 1 | # Vue 3 + TypeScript + Vite 2 | 3 | This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 ` 4 |
5 | 6 | -------------------------------------------------------------------------------- /packages/dev-page/moveDir.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs-extra'); 3 | 4 | const resolve = (str) => path.resolve(__dirname, str); 5 | 6 | fs.copySync(resolve('./dist'), resolve('../visual-dev/plugins/server/dev-page')); 7 | -------------------------------------------------------------------------------- /packages/dev-page/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dev-page", 3 | "private": true, 4 | "version": "0.0.0", 5 | "scripts": { 6 | "dev": "vite", 7 | "build": "vue-tsc --noEmit && vite build && node ./moveDir.js", 8 | "preview": "vite preview" 9 | }, 10 | "dependencies": { 11 | "@ant-design/icons-vue": "^6.1.0", 12 | "@antv/g6": "^4.6.15", 13 | "@vueuse/core": "^9.1.0", 14 | "ant-design-vue": "^3.2.10", 15 | "vue": "^3.2.37", 16 | "vue-router": "4" 17 | }, 18 | "devDependencies": { 19 | "@vitejs/plugin-vue": "^3.0.0", 20 | "less": "^4.1.1", 21 | "typescript": "^4.6.4", 22 | "unplugin-vue-components": "^0.21.2", 23 | "vite": "^3.0.0", 24 | "vue-tsc": "^0.38.4" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/dev-page/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/dev-page/src/App.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /packages/dev-page/src/Layout.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 70 | 71 | 119 | -------------------------------------------------------------------------------- /packages/dev-page/src/assets/vue.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/dev-page/src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 32 | 33 | 38 | -------------------------------------------------------------------------------- /packages/dev-page/src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue'; 2 | import App from './App.vue'; 3 | import Antd from 'ant-design-vue'; 4 | import router from './routes'; 5 | import './style.css'; 6 | import './style.less'; 7 | 8 | const app = createApp(App); 9 | 10 | app.use(Antd); 11 | app.use(router); 12 | 13 | app.mount('#root'); 14 | -------------------------------------------------------------------------------- /packages/dev-page/src/pages/File/index.vue: -------------------------------------------------------------------------------- 1 | 123 | 124 | 129 | 150 | -------------------------------------------------------------------------------- /packages/dev-page/src/pages/File/test.ts: -------------------------------------------------------------------------------- 1 | export const dataTest = { 2 | data: { 3 | nodes: [ 4 | { 5 | id: 'index.js', 6 | label: 'index.js', 7 | name: '/Users/wenhaoming/Desktop/project/visual-dev/examples/cra-app/src/index.js', 8 | }, 9 | { 10 | id: 'App.jsx', 11 | label: 'App.jsx', 12 | name: '/Users/wenhaoming/Desktop/project/visual-dev/examples/cra-app/src/App.jsx', 13 | }, 14 | { 15 | id: 'reportWebVitals.js', 16 | label: 'reportWebVitals.js', 17 | name: '/Users/wenhaoming/Desktop/project/visual-dev/examples/cra-app/src/reportWebVitals.js', 18 | }, 19 | { 20 | id: 'index.css', 21 | label: 'index.css', 22 | name: '/Users/wenhaoming/Desktop/project/visual-dev/examples/cra-app/src/index.css', 23 | }, 24 | { 25 | id: 'App.css', 26 | label: 'App.css', 27 | name: '/Users/wenhaoming/Desktop/project/visual-dev/examples/cra-app/src/App.css', 28 | }, 29 | { 30 | id: 'b.jsx', 31 | label: 'b.jsx', 32 | name: '/Users/wenhaoming/Desktop/project/visual-dev/examples/cra-app/src/b.jsx', 33 | }, 34 | { 35 | id: 'Form.jsx', 36 | label: 'Form.jsx', 37 | name: '/Users/wenhaoming/Desktop/project/visual-dev/examples/cra-app/src/Form.jsx', 38 | }, 39 | { 40 | id: 'index.jsx', 41 | label: 'index.jsx', 42 | name: '/Users/wenhaoming/Desktop/project/visual-dev/examples/cra-app/src/Pages/PageA/index.jsx', 43 | }, 44 | { 45 | id: 'index.jsx', 46 | label: 'index.jsx', 47 | name: '/Users/wenhaoming/Desktop/project/visual-dev/examples/cra-app/src/Pages/PageB/index.jsx', 48 | }, 49 | ], 50 | edges: [ 51 | { 52 | source: 'index.js', 53 | target: 'App.jsx', 54 | style: { 55 | endArrow: { 56 | path: 'M 0,0 L 20,-7.5\n L 13.333333333333334,0 L 20,7.5 Z', 57 | fill: '#F6BD16', 58 | }, 59 | }, 60 | }, 61 | { 62 | source: 'index.js', 63 | target: 'reportWebVitals.js', 64 | style: { 65 | endArrow: { 66 | path: 'M 0,0 L 20,-7.5\n L 13.333333333333334,0 L 20,7.5 Z', 67 | fill: '#F6BD16', 68 | }, 69 | }, 70 | }, 71 | { 72 | source: 'index.js', 73 | target: 'index.css', 74 | style: { 75 | endArrow: { 76 | path: 'M 0,0 L 20,-7.5\n L 13.333333333333334,0 L 20,7.5 Z', 77 | fill: '#F6BD16', 78 | }, 79 | }, 80 | }, 81 | { 82 | source: 'App.jsx', 83 | target: 'App.css', 84 | style: { 85 | endArrow: { 86 | path: 'M 0,0 L 20,-7.5\n L 13.333333333333334,0 L 20,7.5 Z', 87 | fill: '#F6BD16', 88 | }, 89 | }, 90 | }, 91 | { 92 | source: 'App.jsx', 93 | target: 'b.jsx', 94 | style: { 95 | endArrow: { 96 | path: 'M 0,0 L 20,-7.5\n L 13.333333333333334,0 L 20,7.5 Z', 97 | fill: '#F6BD16', 98 | }, 99 | }, 100 | }, 101 | { 102 | source: 'App.jsx', 103 | target: 'Form.jsx', 104 | style: { 105 | endArrow: { 106 | path: 'M 0,0 L 20,-7.5\n L 13.333333333333334,0 L 20,7.5 Z', 107 | fill: '#F6BD16', 108 | }, 109 | }, 110 | }, 111 | { 112 | source: 'App.jsx', 113 | target: 'index.jsx', 114 | style: { 115 | endArrow: { 116 | path: 'M 0,0 L 20,-7.5\n L 13.333333333333334,0 L 20,7.5 Z', 117 | fill: '#F6BD16', 118 | }, 119 | }, 120 | }, 121 | { 122 | source: 'App.jsx', 123 | target: 'index.jsx', 124 | style: { 125 | endArrow: { 126 | path: 'M 0,0 L 20,-7.5\n L 13.333333333333334,0 L 20,7.5 Z', 127 | fill: '#F6BD16', 128 | }, 129 | }, 130 | }, 131 | { 132 | source: 'index.css', 133 | target: 'index.css', 134 | style: { 135 | endArrow: { 136 | path: 'M 0,0 L 20,-7.5\n L 13.333333333333334,0 L 20,7.5 Z', 137 | fill: '#F6BD16', 138 | }, 139 | }, 140 | }, 141 | { 142 | source: 'App.css', 143 | target: 'App.css', 144 | style: { 145 | endArrow: { 146 | path: 'M 0,0 L 20,-7.5\n L 13.333333333333334,0 L 20,7.5 Z', 147 | fill: '#F6BD16', 148 | }, 149 | }, 150 | }, 151 | ], 152 | }, 153 | }; 154 | -------------------------------------------------------------------------------- /packages/dev-page/src/pages/components/A/index.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /packages/dev-page/src/pages/components/B/index.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /packages/dev-page/src/pages/readme/index.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /packages/dev-page/src/routes/index.ts: -------------------------------------------------------------------------------- 1 | import type { RouteRecordRaw } from 'vue-router'; 2 | import { createRouter, createWebHistory } from 'vue-router'; 3 | 4 | const routes: Readonly = [ 5 | { 6 | path: '/', 7 | component: () => import('@/pages/readme/index.vue'), 8 | }, 9 | { 10 | path: '/file', 11 | component: () => import('@/pages/File/index.vue'), 12 | }, 13 | { 14 | path: '/components', 15 | children: [ 16 | { 17 | path: '/components/a', 18 | component: () => import('@/pages/components/A/index.vue'), 19 | }, 20 | { 21 | path: '/components/b', 22 | component: () => import('@/pages/components/B/index.vue'), 23 | }, 24 | ], 25 | }, 26 | ]; 27 | 28 | export default createRouter({ 29 | history: createWebHistory(), 30 | routes, // `routes: routes` 的缩写 31 | }); 32 | -------------------------------------------------------------------------------- /packages/dev-page/src/style.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | padding: 0; 4 | margin: 0; 5 | } 6 | -------------------------------------------------------------------------------- /packages/dev-page/src/style.less: -------------------------------------------------------------------------------- 1 | // @font-size:12px; 2 | 3 | // .abc: { 4 | // font-size:@font-size; 5 | // } 6 | -------------------------------------------------------------------------------- /packages/dev-page/src/styles/var.less: -------------------------------------------------------------------------------- 1 | @primary-color: #165dff; 2 | @font-size-base: 14px; 3 | -------------------------------------------------------------------------------- /packages/dev-page/src/utils.ts: -------------------------------------------------------------------------------- 1 | export const EDITORS = { 2 | sublime: 'subl://open?url=file://{path}&line={line}&column={column}', 3 | textmate: 'txmt://open?url=file://{path}&line={line}&column={column}', 4 | emacs: 'emacs://open?url=file://{path}&line={line}&column={column}', 5 | macvim: 'mvim://open/?url=file://{path}&line={line}&column={column}', 6 | phpstorm: 'phpstorm://open?file={path}&line={line}&column={column}', 7 | webstorm: 'webstorm://open?file={path}&line={line}&column={column}', 8 | idea: 'idea://open?file={path}&line={line}&column={column}', 9 | vscode: 'vscode://file/{path}:{line}:{column}', 10 | 'vscode-insiders': 'vscode-insiders://file/{path}:{line}:{column}', 11 | atom: 'atom://core/open/file?filename={path}&line={line}&column={column}', 12 | }; 13 | 14 | export type Editor = keyof typeof EDITORS; 15 | 16 | export function launchEditor({ 17 | editor, 18 | srcPath, 19 | line, 20 | column, 21 | }: { 22 | editor: Editor; 23 | srcPath: string; 24 | line?: number | string; 25 | column?: number | string; 26 | }): string { 27 | return EDITORS[editor].replace(/{(.*?)}/g, (_$: any, $1: any): string => { 28 | if ($1 === 'path') { 29 | return srcPath; 30 | } 31 | if ($1 === 'line') { 32 | return String(line); 33 | } 34 | return String(column); 35 | }); 36 | } 37 | -------------------------------------------------------------------------------- /packages/dev-page/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare module '*.vue' { 4 | import type { DefineComponent } from 'vue'; 5 | 6 | const component: DefineComponent<{}, {}, any>; 7 | export default component; 8 | } 9 | -------------------------------------------------------------------------------- /packages/dev-page/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "moduleResolution": "Node", 7 | "strict": true, 8 | "sourceMap": true, 9 | "resolveJsonModule": true, 10 | "isolatedModules": true, 11 | "esModuleInterop": true, 12 | "lib": ["ESNext", "DOM"], 13 | "skipLibCheck": true, 14 | "baseUrl": "./", 15 | "paths": { 16 | "@": ["src/*"] 17 | } 18 | }, 19 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], 20 | "references": [{ "path": "./tsconfig.node.json" }] 21 | } 22 | -------------------------------------------------------------------------------- /packages/dev-page/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "module": "ESNext", 5 | "moduleResolution": "Node", 6 | "allowSyntheticDefaultImports": true 7 | }, 8 | "include": ["vite.config.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/dev-page/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import vue from '@vitejs/plugin-vue'; 3 | import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers'; 4 | import Components from 'unplugin-vue-components/vite'; 5 | import path from 'path'; 6 | 7 | // https://vitejs.dev/config/ 8 | export default defineConfig({ 9 | plugins: [ 10 | vue(), 11 | Components({ 12 | dts: true, 13 | resolvers: [AntDesignVueResolver({ importStyle: 'less' })], 14 | }), 15 | ], 16 | resolve: { 17 | alias: { 18 | '@': path.resolve(__dirname, './src'), 19 | }, 20 | }, 21 | css: { 22 | preprocessorOptions: { 23 | less: { 24 | modifyVars: { 25 | '@primary-color': '#165DFF', 26 | '@menu-item-height': '30px', 27 | '@menu-inline-toplevel-item-height': '30px', 28 | // 'link-color': '#FFCCCC', 29 | // 'border-radius-base': '2px', 30 | }, 31 | javascriptEnabled: true, 32 | additionalData: `@import '@/styles/var.less';`, 33 | }, 34 | }, 35 | }, 36 | }); 37 | -------------------------------------------------------------------------------- /packages/dev-ui/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | -------------------------------------------------------------------------------- /packages/dev-ui/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 haoming 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/dev-ui/README.md: -------------------------------------------------------------------------------- 1 | # Vue 3 + Typescript + Vite 2 | 3 | This template should help get you started developing with Vue 3 and Typescript in Vite. 4 | 5 | ## Recommended IDE Setup 6 | 7 | - [VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.volar) 8 | 9 | ## Type Support For `.vue` Imports in TS 10 | 11 | Since TypeScript cannot handle type information for `.vue` imports, they are shimmed to be a generic Vue component type by default. In most cases this is fine if you don't really care about component prop types outside of templates. However, if you wish to get actual prop types in `.vue` imports (for example to get props validation when using manual `h(...)` calls), you can enable Volar's `.vue` type support plugin by running `Volar: Switch TS Plugin on/off` from VSCode command palette. 12 | -------------------------------------------------------------------------------- /packages/dev-ui/index.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/dev-ui/index.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | exports.dirName = 'web_devtools_assets'; 4 | exports.assetsDir = path.resolve(__dirname, `./dist/${dirName}`); 5 | exports.templateFile = path.resolve(__dirname, './dist/index.html'); 6 | -------------------------------------------------------------------------------- /packages/dev-ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dev-ui", 3 | "version": "0.0.8", 4 | "main": "vite.config.ts", 5 | "scripts": { 6 | "dev": "vite", 7 | "build": "vue-tsc --noEmit && vite build", 8 | "serve": "vite preview" 9 | }, 10 | "dependencies": { 11 | "@antv/g6": "^4.6.15", 12 | "hotkeys-js": "^3.8.7", 13 | "less": "^4.1.1", 14 | "less-loader": "^10.0.1", 15 | "markdown-it": "^12.2.0", 16 | "vue": "3.2.19" 17 | }, 18 | "license": "MIT", 19 | "devDependencies": { 20 | "@types/markdown-it": "^12.2.1", 21 | "@vitejs/plugin-vue": "^1.6.0", 22 | "@vue/compiler-sfc": "^3.0.5", 23 | "@web-devtools/core": "0.0.6", 24 | "typescript": "^4.3.2", 25 | "vite": "^2.5.1", 26 | "vue-tsc": "^0.2.2" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/dev-ui/src/App.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 36 | 51 | -------------------------------------------------------------------------------- /packages/dev-ui/src/IconCompents/Aim.vue: -------------------------------------------------------------------------------- 1 | 19 | -------------------------------------------------------------------------------- /packages/dev-ui/src/IconCompents/Ant.vue: -------------------------------------------------------------------------------- 1 | 34 | -------------------------------------------------------------------------------- /packages/dev-ui/src/IconCompents/Close.vue: -------------------------------------------------------------------------------- 1 | 29 | -------------------------------------------------------------------------------- /packages/dev-ui/src/IconCompents/Outline.vue: -------------------------------------------------------------------------------- 1 | 6 | 9 | 22 | -------------------------------------------------------------------------------- /packages/dev-ui/src/IconCompents/Proxy.vue: -------------------------------------------------------------------------------- 1 | 19 | -------------------------------------------------------------------------------- /packages/dev-ui/src/IconCompents/RightArrow.vue: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /packages/dev-ui/src/IconCompents/SvgIcon.vue: -------------------------------------------------------------------------------- 1 | 6 | 15 | 111 | -------------------------------------------------------------------------------- /packages/dev-ui/src/Layout/Nav.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 30 | 31 | 86 | -------------------------------------------------------------------------------- /packages/dev-ui/src/Layout/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 22 | 23 | 88 | -------------------------------------------------------------------------------- /packages/dev-ui/src/Pages/Docs/SliderBar.vue: -------------------------------------------------------------------------------- 1 | 7 | 12 | 25 | -------------------------------------------------------------------------------- /packages/dev-ui/src/Pages/Docs/SliderLink.vue: -------------------------------------------------------------------------------- 1 | 40 | 77 | 145 | -------------------------------------------------------------------------------- /packages/dev-ui/src/Pages/Docs/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 10 | 24 | -------------------------------------------------------------------------------- /packages/dev-ui/src/Pages/Setting/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 14 | 26 | -------------------------------------------------------------------------------- /packages/dev-ui/src/api/index.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wen-haoming/visual-dev/c5f9f6eac32ff003da0c0babe85da57d650f0a7f/packages/dev-ui/src/api/index.ts -------------------------------------------------------------------------------- /packages/dev-ui/src/assets/General.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/dev-ui/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wen-haoming/visual-dev/c5f9f6eac32ff003da0c0babe85da57d650f0a7f/packages/dev-ui/src/assets/logo.png -------------------------------------------------------------------------------- /packages/dev-ui/src/components/AimMode.vue: -------------------------------------------------------------------------------- 1 | 114 | 115 | 120 | 121 | 128 | -------------------------------------------------------------------------------- /packages/dev-ui/src/components/Dialog.vue: -------------------------------------------------------------------------------- 1 | 90 | 91 | 96 | 129 | -------------------------------------------------------------------------------- /packages/dev-ui/src/components/Folder.vue: -------------------------------------------------------------------------------- 1 | 18 | 37 | 82 | -------------------------------------------------------------------------------- /packages/dev-ui/src/components/ProxyMode.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 27 | 28 | 55 | -------------------------------------------------------------------------------- /packages/dev-ui/src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | declare module '*.vue' { 3 | import type { DefineComponent } from 'vue'; 4 | // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types 5 | const component: DefineComponent<{}, {}, any>; 6 | export default component; 7 | } 8 | declare interface Window { 9 | isDemo?: boolean; 10 | } 11 | -------------------------------------------------------------------------------- /packages/dev-ui/src/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export * from './usePrefix'; 2 | export * from './useRoute'; 3 | export * from './useDocs'; 4 | export * from './useStore'; 5 | export * from './useHotkeys'; 6 | export * from './useRequest'; 7 | -------------------------------------------------------------------------------- /packages/dev-ui/src/hooks/useDocs.ts: -------------------------------------------------------------------------------- 1 | import { reactive, provide, inject, onMounted } from 'vue'; 2 | import { useRequest } from './'; 3 | 4 | export const useDocsNamespace = 'useDocs'; 5 | 6 | const rawData = { 7 | sliderObject: {}, 8 | currentSlider: {}, 9 | docsContent: '', 10 | setDocsContent(content: string) { 11 | this.docsContent = content; 12 | }, 13 | }; 14 | 15 | export const createDoscContext = () => { 16 | const data = reactive(rawData); 17 | onMounted(() => { 18 | useRequest('getMenu').then((res: any) => { 19 | data.sliderObject = res; 20 | }); 21 | }); 22 | provide(useDocsNamespace, data); 23 | return data; 24 | }; 25 | 26 | export const useDocs = () => { 27 | return inject(useDocsNamespace); 28 | }; 29 | -------------------------------------------------------------------------------- /packages/dev-ui/src/hooks/useHotkeys.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable react-hooks/rules-of-hooks */ 2 | import hotkeys from 'hotkeys-js'; 3 | import { onMounted } from 'vue'; 4 | import type { KeyHandler } from 'hotkeys-js'; 5 | 6 | export const scoped = { 7 | toggle_drawer: 'toggleDrawer', 8 | toggle_aim: 'toggleAim', 9 | close_drawer: 'closeDrawer', 10 | open_aim: 'openAim', 11 | close_aim: 'closeAim', 12 | close_all: 'closeAll', 13 | }; 14 | 15 | type Options = Partial< 16 | Record< 17 | keyof typeof scoped, 18 | { 19 | keys: string[][]; 20 | callback: KeyHandler; 21 | } 22 | > 23 | >; 24 | 25 | export const useHotkeys = (options: Options) => { 26 | onMounted(() => { 27 | Object.entries(options).forEach(([, value]) => { 28 | value.keys.forEach((keys) => { 29 | hotkeys(keys.join('+'), value.callback); 30 | }); 31 | }); 32 | }); 33 | 34 | return { 35 | update(updateOptions: Options) { 36 | Object.entries(updateOptions).forEach(([, value]) => { 37 | value.keys.forEach((keys) => { 38 | hotkeys(keys.join('+'), value.callback); 39 | }); 40 | }); 41 | }, 42 | unbind() { 43 | Object.entries(options).forEach(([scope, value]) => { 44 | hotkeys.unbind(value.keys.join('+'), scope); 45 | }); 46 | }, 47 | }; 48 | }; 49 | -------------------------------------------------------------------------------- /packages/dev-ui/src/hooks/usePrefix.ts: -------------------------------------------------------------------------------- 1 | import { provide, inject, ref } from 'vue'; 2 | 3 | export const usePrefixNamespace = 'useRoute'; 4 | 5 | export const createPrefixContext = (prefix: string) => { 6 | const prefixData = ref(); 7 | prefixData.value = prefix; 8 | provide(usePrefixNamespace, prefixData.value); 9 | return prefixData.value; 10 | }; 11 | 12 | export const usePrefix = (prefix: string) => { 13 | return `${inject(usePrefixNamespace)}-${prefix}`; 14 | }; 15 | -------------------------------------------------------------------------------- /packages/dev-ui/src/hooks/useRequest.ts: -------------------------------------------------------------------------------- 1 | import { urlPrefix } from '../utils'; 2 | 3 | export const useRequest: any = async (path: string, requestInit?: RequestInit) => { 4 | return await fetch(`${urlPrefix}/${path}`, requestInit).then((res) => res.json()); 5 | }; 6 | -------------------------------------------------------------------------------- /packages/dev-ui/src/hooks/useRoute.ts: -------------------------------------------------------------------------------- 1 | import { reactive, provide, inject } from 'vue'; 2 | 3 | export const useRouteNamespace = 'useRoute'; 4 | 5 | interface Route { 6 | route: string; 7 | component: any; 8 | } 9 | 10 | const rawData = { 11 | routes: [ 12 | { 13 | route: 'Docs', 14 | // component: shallowRef(defineAsyncComponent(() => import('../Pages/Docs/index.vue'))), 15 | }, 16 | ], 17 | currentRoute: { 18 | route: '', 19 | component: null, 20 | }, 21 | changeCurrentRoute(route: Route) { 22 | this.currentRoute = route; 23 | }, 24 | }; 25 | 26 | export const createRouteContext = () => { 27 | const data = reactive(rawData); 28 | data.currentRoute = data.routes[0] as any; 29 | 30 | provide(useRouteNamespace, data); 31 | return data; 32 | }; 33 | 34 | export const useRoute = () => { 35 | return inject(useRouteNamespace); 36 | }; 37 | -------------------------------------------------------------------------------- /packages/dev-ui/src/hooks/useStore.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable react-hooks/rules-of-hooks */ 2 | import { inject, reactive, provide, onMounted, onUnmounted, watchEffect } from 'vue'; 3 | import type { DevConfig } from '../main'; 4 | import { useHotkeys } from './useHotkeys'; 5 | 6 | export const useAimNamespace = 'useStore'; 7 | const rawData = { 8 | type: '', 9 | component: '', 10 | visibile: false, 11 | isAimStatus: false, 12 | devConfig: { 13 | editor: 'vscode', 14 | mode: '', 15 | shortcuts: { 16 | inspect: [], 17 | }, 18 | } as DevConfig, 19 | setVisibile(visibile: boolean) { 20 | this.visibile = visibile; 21 | }, 22 | setIsAimStatus(isAimStatus: boolean) { 23 | this.isAimStatus = isAimStatus; 24 | }, 25 | openDrawer() { 26 | this.component = ''; 27 | this.type = ''; 28 | this.setVisibile(true); 29 | }, 30 | closeAll() { 31 | this.component = ''; 32 | this.type = ''; 33 | this.setVisibile(false); 34 | this.setIsAimStatus(false); 35 | }, 36 | }; 37 | 38 | export const createStore = () => { 39 | const data = reactive(rawData); 40 | 41 | const { update } = useHotkeys({ 42 | close_all: { 43 | keys: [['esc']], 44 | callback() { 45 | data.closeAll(); 46 | }, 47 | }, 48 | toggle_aim: { 49 | keys: [ 50 | ['command', 'shift', 'x'], 51 | ['ctrl', 'shift', 'x'], 52 | ], 53 | callback() { 54 | data.setIsAimStatus(!data.isAimStatus); 55 | }, 56 | }, 57 | }); 58 | 59 | const handlekeydown = (e: HTMLElementEventMap['keydown']) => { 60 | switch (e.key) { 61 | case 'Escape': 62 | data.setIsAimStatus(false); 63 | return null; 64 | default: 65 | return null; 66 | } 67 | }; 68 | 69 | watchEffect(() => { 70 | if (data.devConfig.shortcuts?.inspect?.length) { 71 | update({ 72 | open_aim: { 73 | keys: [data.devConfig.shortcuts?.inspect], 74 | callback: () => data.setIsAimStatus(!data.isAimStatus), 75 | }, 76 | }); 77 | } 78 | }); 79 | 80 | onMounted(() => { 81 | window.addEventListener<'keydown'>('keydown', handlekeydown, false); 82 | }); 83 | 84 | onUnmounted(() => { 85 | window.removeEventListener<'keydown'>('keydown', handlekeydown, false); 86 | }); 87 | 88 | provide(useAimNamespace, reactive(data)); 89 | return data; 90 | }; 91 | 92 | export const useStore = () => { 93 | return inject(useAimNamespace); 94 | }; 95 | -------------------------------------------------------------------------------- /packages/dev-ui/src/main.ts: -------------------------------------------------------------------------------- 1 | import './style/vars.less'; 2 | import { createApp } from 'vue'; 3 | import App from './App.vue'; 4 | import type { EDITORS } from 'utils'; 5 | import type { Options } from 'http-proxy-middleware'; 6 | 7 | createApp(App).mount('#dev-tools'); 8 | 9 | export interface DevConfig { 10 | mode: string; 11 | editor: keyof typeof EDITORS; 12 | devServerProxy?: Record | false; 13 | shortcuts?: { 14 | inspect?: [string, string] | [string, string, string] | []; 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /packages/dev-ui/src/style/md.less: -------------------------------------------------------------------------------- 1 | #dev-tools-drawer { 2 | .l-content { 3 | h1, 4 | h2, 5 | h3, 6 | h4, 7 | h5, 8 | h6 { 9 | margin: 0; 10 | line-height: 1.25; 11 | } 12 | 13 | h1, 14 | h2, 15 | h3, 16 | h4, 17 | h5, 18 | h6, 19 | strong, 20 | b { 21 | font-weight: 600; 22 | } 23 | 24 | h1:hover .header-anchor, 25 | h1:focus .header-anchor, 26 | h2:hover .header-anchor, 27 | h2:focus .header-anchor, 28 | h3:hover .header-anchor, 29 | h3:focus .header-anchor, 30 | h4:hover .header-anchor, 31 | h4:focus .header-anchor, 32 | h5:hover .header-anchor, 33 | h5:focus .header-anchor, 34 | h6:hover .header-anchor, 35 | h6:focus .header-anchor { 36 | opacity: 1; 37 | } 38 | 39 | h1 { 40 | margin-top: 1.5rem; 41 | font-size: 1.9rem; 42 | } 43 | 44 | @media screen and (min-width: 420px) { 45 | h1 { 46 | font-size: 2.2rem; 47 | } 48 | } 49 | 50 | h2 { 51 | margin-top: 2.25rem; 52 | margin-bottom: 1.25rem; 53 | padding-bottom: 0.3rem; 54 | font-size: 1.65rem; 55 | line-height: 1.25; 56 | border-bottom: 1px solid var(--v-divider); 57 | /* overflow-x: auto; */ 58 | } 59 | 60 | h2 + h3 { 61 | margin-top: 1.5rem; 62 | } 63 | 64 | h3 { 65 | margin-top: 2rem; 66 | font-size: 1.35rem; 67 | } 68 | 69 | h4 { 70 | font-size: 1.15rem; 71 | } 72 | 73 | p, 74 | ol, 75 | ul { 76 | margin: 1rem 0; 77 | line-height: 1.7; 78 | } 79 | 80 | a, 81 | area, 82 | button, 83 | [role='button'], 84 | input, 85 | label, 86 | select, 87 | summary, 88 | textarea { 89 | touch-action: manipulation; 90 | } 91 | 92 | a { 93 | color: var(--v-brand); 94 | text-decoration: none; 95 | } 96 | 97 | a:hover { 98 | text-decoration: underline; 99 | } 100 | 101 | a.header-anchor { 102 | float: left; 103 | margin-top: 0.125em; 104 | margin-left: -0.87em; 105 | padding-right: 0.23em; 106 | opacity: 0; 107 | } 108 | 109 | a.header-anchor:hover, 110 | a.header-anchor:focus { 111 | text-decoration: none; 112 | } 113 | 114 | figure { 115 | margin: 0; 116 | } 117 | 118 | img { 119 | max-width: 100%; 120 | } 121 | 122 | ul, 123 | ol { 124 | padding-left: 1.25em; 125 | } 126 | 127 | li > ul, 128 | li > ol { 129 | margin: 0; 130 | } 131 | 132 | table { 133 | @apply w-full; 134 | } 135 | 136 | tr { 137 | @apply border-b border-gray-400 border-opacity-20; 138 | } 139 | 140 | th { 141 | @apply text-left font-600; 142 | } 143 | 144 | td, 145 | th { 146 | @apply p-1 py-2; 147 | } 148 | 149 | blockquote { 150 | margin: 1rem 0; 151 | padding: 0.25rem 0 0.25rem 1rem; 152 | color: var(--v-text); 153 | font-size: 1rem; 154 | border-left: 0.2rem solid #8885; 155 | @apply bg-gray-400 bg-opacity-10; 156 | } 157 | 158 | blockquote > p { 159 | margin: 0; 160 | } 161 | 162 | form { 163 | margin: 0; 164 | } 165 | 166 | .theme.sidebar-open .sidebar-mask { 167 | display: block; 168 | } 169 | 170 | .theme.no-navbar > h1, 171 | .theme.no-navbar > h2, 172 | .theme.no-navbar > h3, 173 | .theme.no-navbar > h4, 174 | .theme.no-navbar > h5, 175 | .theme.no-navbar > h6 { 176 | margin-top: 1.5rem; 177 | padding-top: 0; 178 | } 179 | 180 | .theme.no-navbar aside { 181 | top: 0; 182 | } 183 | 184 | @media screen and (min-width: 720px) { 185 | .theme.no-sidebar aside { 186 | display: none; 187 | } 188 | 189 | .theme.no-sidebar main { 190 | margin-left: 0; 191 | } 192 | } 193 | 194 | .sidebar-mask { 195 | position: fixed; 196 | z-index: 2; 197 | display: none; 198 | width: 100vw; 199 | height: 100vh; 200 | } 201 | 202 | .nav-btn { 203 | display: flex; 204 | color: var(--v-text); 205 | font-size: 1.05rem; 206 | background: none; 207 | border: 0; 208 | outline: none; 209 | cursor: pointer; 210 | opacity: 0.8; 211 | } 212 | .nav-btn:hover { 213 | opacity: 1; 214 | } 215 | .nav-btn svg { 216 | margin: auto; 217 | } 218 | .external-link { 219 | font-size: 0.95rem; 220 | opacity: 0.7; 221 | } 222 | 223 | .icon-btn { 224 | @apply inline-block cursor-pointer select-none !outline-none; 225 | @apply opacity-75 transition duration-200 ease-in-out align-middle rounded p-2; 226 | @apply hover:(opacity-100 bg-gray-400 bg-opacity-10); 227 | } 228 | 229 | .icon-btn.disabled { 230 | @apply opacity-25 pointer-events-none; 231 | } 232 | 233 | .inline-icon-btn { 234 | @apply text-primary-deep; 235 | @apply inline-block rounded p-0.5 text-2xl align-middle; 236 | @apply border border-primary border-opacity-20 border-solid; 237 | } 238 | 239 | p > img { 240 | @apply rounded-2xl; 241 | } 242 | 243 | li svg { 244 | vertical-align: middle; 245 | transform: translateY(-10%); 246 | } 247 | 248 | table { 249 | width: 100%; 250 | border: 1px solid #ebedf1; 251 | border-collapse: collapse; 252 | th { 253 | font-weight: 600; 254 | background: #f9fafb; 255 | } 256 | th, 257 | td { 258 | padding: 10px 24px; 259 | border: 1px solid #ebedf1; 260 | } 261 | } 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /packages/dev-ui/src/style/overLayer.less: -------------------------------------------------------------------------------- 1 | .__layer-dev-tool { 2 | background-color: rgba(53, 134, 255, 0.4) !important; 3 | outline: 1px solid #fff !important; 4 | cursor: pointer !important; 5 | transition: all 0.1s; 6 | } 7 | 8 | #visual_overLayer { 9 | position: fixed; 10 | top: 0; 11 | right: 0; 12 | bottom: 0; 13 | left: 0; 14 | background-color: rgba(0, 0, 0, 0.1); 15 | pointer-events: none; 16 | } 17 | -------------------------------------------------------------------------------- /packages/dev-ui/src/style/vars.less: -------------------------------------------------------------------------------- 1 | /* @import './md.less'; */ 2 | // @import './code.css'; 3 | // @import './vars.less'; 4 | 5 | @prefix-cls: ~'vd'; 6 | 7 | :root { 8 | --v-brand: #57a9fb; 9 | --v-brand-active: #3491fa; 10 | 11 | --v-inspect-bg: #fff; 12 | --v-inspect-tt: #454545; 13 | --v-inspect-sub-tt: #909090; 14 | --v-inspect-c-tt: #fff; 15 | --v-inspect-frame-react: #57a9fb; 16 | --v-inspect-frame-vue: #81b948; 17 | --v-inspect-box-shadow: 0 0.4px 2.2px rgba(0, 0, 0, 0.1), 0 1.8px 5.3px rgba(0, 0, 0, 0.06), 18 | 0 5.7px 10px rgba(0, 0, 0, 0.035), 0 14.8px 17.9px rgba(0, 0, 0, 0.042), 19 | 0 35.6px 33.4px rgba(0, 0, 0, 0.05); 20 | // --v-inspect-bg:rgba(30, 32, 44, .95); 21 | /** 22 | * Typography 23 | * --------------------------------------------------------------------- */ 24 | --font-family-base: 'Inter', apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, 25 | Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; 26 | --font-family-mono: 'IBM Plex Mono', source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 27 | monospace; 28 | } 29 | -------------------------------------------------------------------------------- /packages/dev-ui/src/utils/api.ts: -------------------------------------------------------------------------------- 1 | export const urlPrefix = `http://localhost:10078/web-devtools`; 2 | 3 | export const getRequest = (path: string) => { 4 | return fetch(`${urlPrefix}/${path}`).then((res) => res.json()); 5 | }; 6 | 7 | export const postRequest = (path: string, body: any) => { 8 | return fetch(`${urlPrefix}/${path}`, { 9 | method: 'post', 10 | body: JSON.stringify(body), 11 | headers: { 12 | 'Content-Type': 'application/json', 13 | }, 14 | }).then((res) => res.json()); 15 | }; 16 | -------------------------------------------------------------------------------- /packages/dev-ui/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './api'; 2 | export * from './launchEditor'; 3 | 4 | export const prefix = `http://localhost:10078`; 5 | 6 | export const getFetch = (url: string) => { 7 | return fetch(`${prefix}/web-devtools/${url}`).then((res) => res.json()); 8 | }; 9 | 10 | export const getParentNode = (ele: Element, cb: (ele: Element) => boolean) => { 11 | if (!ele) return null; 12 | 13 | while (!cb(ele) && ele.parentNode && ele !== document.body) { 14 | // eslint-disable-next-line no-param-reassign 15 | (ele as any) = ele.parentNode; 16 | } 17 | return ele; 18 | }; 19 | 20 | export function getElementDimensions(domElement: Element) { 21 | const calculatedStyle = window.getComputedStyle(domElement); 22 | const Rect = domElement.getBoundingClientRect(); 23 | 24 | return { 25 | borderLeftWidth: `${parseInt(calculatedStyle.borderLeftWidth, 10)}px`, 26 | borderRightWidth: `${parseInt(calculatedStyle.borderRightWidth, 10)}px`, 27 | borderTopWidth: `${parseInt(calculatedStyle.borderTopWidth, 10)}px`, 28 | borderBottomWidth: `${parseInt(calculatedStyle.borderBottomWidth, 10)}px`, 29 | marginLeft: `${parseInt(calculatedStyle.marginLeft, 10)}px`, 30 | marginRight: `${parseInt(calculatedStyle.marginRight, 10)}px`, 31 | marginTop: `${parseInt(calculatedStyle.marginTop, 10)}px`, 32 | marginBottom: `${parseInt(calculatedStyle.marginBottom, 10)}px`, 33 | paddingLeft: `${parseInt(calculatedStyle.paddingLeft, 10)}px`, 34 | paddingRight: `${parseInt(calculatedStyle.paddingRight, 10)}px`, 35 | paddingTop: `${parseInt(calculatedStyle.paddingTop, 10)}px`, 36 | paddingBottom: `${parseInt(calculatedStyle.paddingBottom, 10)}px`, 37 | left: `${Rect.left + window.scrollX - parseInt(calculatedStyle.marginLeft, 10)}px`, 38 | top: `${Rect.top + window.scrollY - parseInt(calculatedStyle.marginTop, 10)}px`, 39 | width: `${ 40 | Rect.width + 41 | parseInt(calculatedStyle.marginLeft, 10) + 42 | parseInt(calculatedStyle.marginRight, 10) 43 | }px`, 44 | height: `${ 45 | Rect.height + 46 | parseInt(calculatedStyle.marginTop, 10) + 47 | parseInt(calculatedStyle.marginBottom, 10) 48 | }px`, 49 | }; 50 | } 51 | 52 | // '/src/App.jsx:5:4' --> App.jsx || '\\src\\App.jsx:5:4' --> App.jsx 53 | export const getCompNameFromStringPath = (srcPath: string) => { 54 | const path = srcPath.split(':')[0]; 55 | const pathNames = path.split(/[/\\]/); 56 | return pathNames[pathNames.length - 1]; 57 | }; 58 | -------------------------------------------------------------------------------- /packages/dev-ui/src/utils/launchEditor.ts: -------------------------------------------------------------------------------- 1 | export const EDITORS = { 2 | sublime: 'subl://open?url=file://{path}&line={line}&column={column}', 3 | textmate: 'txmt://open?url=file://{path}&line={line}&column={column}', 4 | emacs: 'emacs://open?url=file://{path}&line={line}&column={column}', 5 | macvim: 'mvim://open/?url=file://{path}&line={line}&column={column}', 6 | phpstorm: 'phpstorm://open?file={path}&line={line}&column={column}', 7 | webstorm: 'webstorm://open?file={path}&line={line}&column={column}', 8 | idea: 'idea://open?file={path}&line={line}&column={column}', 9 | vscode: 'vscode://file/{path}:{line}:{column}', 10 | 'vscode-insiders': 'vscode-insiders://file/{path}:{line}:{column}', 11 | atom: 'atom://core/open/file?filename={path}&line={line}&column={column}', 12 | }; 13 | 14 | const openGithub = `https://github.com/wen-haoming/visual-dev/blob/master/demo{path}#L{line}`; 15 | 16 | export type Editor = keyof typeof EDITORS; 17 | 18 | export function launchEditor({ 19 | editor, 20 | srcPath, 21 | line, 22 | column, 23 | }: { 24 | editor: Editor; 25 | srcPath: string; 26 | line?: number | string; 27 | column?: number | string; 28 | }): string { 29 | return (window.isDemo ? openGithub : EDITORS[editor]).replace( 30 | /{(.*?)}/g, 31 | (_$: any, $1: any): string => { 32 | if ($1 === 'path') { 33 | return srcPath; 34 | } 35 | if ($1 === 'line') { 36 | return String(line); 37 | } 38 | return String(column); 39 | }, 40 | ); 41 | } 42 | -------------------------------------------------------------------------------- /packages/dev-ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "strict": true, 5 | "target": "ES2018", 6 | "module": "ESNext", 7 | "noImplicitAny": false, 8 | "declaration": true, 9 | "moduleResolution": "Node", 10 | "esModuleInterop": true, 11 | "jsx": "preserve", 12 | "sourceMap": true, 13 | "lib": ["ES2018", "DOM"], 14 | "allowSyntheticDefaultImports": true, 15 | "forceConsistentCasingInFileNames": true, 16 | "resolveJsonModule": true, 17 | "baseUrl": "./src", 18 | "rootDir": ".", 19 | "typeRoots": ["node_modules/@types"], 20 | "paths": { 21 | "@": ["./src/*"] 22 | } 23 | }, 24 | "exclude": ["node_modules", "**/__test?__", "**/dist"], 25 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"] 26 | } 27 | -------------------------------------------------------------------------------- /packages/dev-ui/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import vue from '@vitejs/plugin-vue'; 3 | import path from 'path'; 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | plugins: [vue()], 8 | publicDir: 'web_devtools_assets', 9 | css: { 10 | preprocessorOptions: { 11 | less: {}, 12 | }, 13 | }, 14 | resolve: { 15 | alias: { 16 | '@': path.resolve(__dirname, './src'), 17 | }, 18 | }, 19 | server: { 20 | port: 1654, 21 | }, 22 | }); 23 | -------------------------------------------------------------------------------- /packages/visual-dev/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 haoming 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/visual-dev/README.md: -------------------------------------------------------------------------------- 1 |

visual-dev

2 | 3 |

4 | NPM Version 5 | NPM Downloads 6 | 7 | License 8 |

9 | 10 | ## Introduction 11 | 12 | Just one click, you can jump directly to the local IDE source code and **vue** and **react** are supported! ! 13 | 14 | 一键直接跳转到本地 IDE 源码,支持 vue 和 react !! 15 | 16 | ## Preview 17 | 18 | ![Jan-19-2022 01-34-02](https://user-images.githubusercontent.com/42735363/149988859-8577c98f-74ef-4a36-81a1-682d0e405253.gif) 19 | 20 | ## Motivation 21 | 22 | In a huge project, there are many different components on the page, but it will be very troublesome to find where the component is. Using this plugin, you only need to click to jump to the corresponding position of the ide. 23 | 24 | 在一个大型的项目中,页面上有很多不同的组件,但是要找到组件在哪里会很麻烦。使用这个插件,只需要点击跳转到 IDE 的对应位置 25 | 26 | ## Installation 27 | 28 | ```bash 29 | npm i visual-dev -D 30 | ``` 31 | 32 | ## Options 33 | 34 | ```typescript 35 | type Options = { 36 | /** 37 | * default open vscode. 38 | */ 39 | editor: Editor; //vscode webstorm atom sublime textmate emacs macvim phpstorm idea 支持多种编辑器,默认使用 vscode 40 | }; 41 | ``` 42 | 43 | ## Usage 44 | 45 | webpack 46 | 47 | ```js 48 | // webpack.config.js 49 | const VisualDev = require('visual-dev/plugins/webpack').default; 50 | 51 | module.exports = { 52 | plugins: [ 53 | new VisualDev({ 54 | editor: 'vscode', 55 | }), 56 | ], 57 | }; 58 | ``` 59 | 60 | umi 61 | 62 | ```js 63 | // .umiirc.ts 64 | { 65 | plugins: ['visual-dev/plugins/umi']; 66 | visualDev: { 67 | editor: 'vscode'; 68 | } 69 | } 70 | ``` 71 | 72 | vite 73 | 74 | ```js 75 | import { defineConfig } from 'vite'; 76 | import vue from '@vitejs/plugin-vue'; 77 | import VisualDev from 'visual-dev/plugins/vite'; 78 | 79 | // https://vitejs.dev/config/ 80 | export default defineConfig({ 81 | plugins: [ 82 | vue(), 83 | VisualDev({ 84 | editor: 'vscode', 85 | }), 86 | ], 87 | }); 88 | ``` 89 | 90 | the project is successfully launched, a small icon will appear in the lower left corner of the screen, which can be triggered by clicking. 91 | 92 | 项目启动成功后,在屏幕的左下角会有一个小图标出现,点击即可触发。 93 | 94 | ## WeChat 95 | 96 | 97 | 98 | ## License 99 | 100 | [MIT LICENSE](./LICENSE) 101 | -------------------------------------------------------------------------------- /packages/visual-dev/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "visual-dev", 3 | "version": "0.3.14", 4 | "description": "Just one click, you can jump directly to the local IDE source code!", 5 | "main": "./plugins/index.js", 6 | "types": "./plugins/index.d.ts", 7 | "scripts": { 8 | "dev": "tsc -w -p ./", 9 | "build": "tsc" 10 | }, 11 | "files": [ 12 | "README.md", 13 | "plugins", 14 | "dev-ui", 15 | "LICENSE", 16 | "package.json" 17 | ], 18 | "keywords": [], 19 | "author": "wen-haoming", 20 | "license": "MIT", 21 | "dependencies": { 22 | "@babel/core": "^7.15.0", 23 | "@babel/generator": "^7.15.4", 24 | "@babel/parser": "^7.15.3", 25 | "@babel/types": "^7.15.0", 26 | "@vue/compiler-dom": "^3.2.21", 27 | "cors": "^2.8.5", 28 | "express": "^4.17.1", 29 | "fs-extra": "^10.0.0", 30 | "magic-string": "^0.25.7" 31 | }, 32 | "peerDependencies": { 33 | "umi": ">=3.x", 34 | "vite": ">2.x", 35 | "webpack": ">4.x" 36 | }, 37 | "devDependencies": { 38 | "@types/babel-core": "^6.25.7", 39 | "@types/babel-generator": "^6.25.4", 40 | "@types/babel-template": "^6.25.2", 41 | "@types/babel-traverse": "^6.25.6", 42 | "@types/express": "^4.17.13", 43 | "@types/fs-extra": "^9.0.12", 44 | "@types/http-proxy-middleware": "^1.0.0", 45 | "@types/markdown-it": "^12.0.1", 46 | "@types/webpack": "^5.28.0", 47 | "diacritics": "^1.3.0", 48 | "escape-html": "^1.0.3", 49 | "nodemon": "^2.0.12", 50 | "typescript": "^4.4.2", 51 | "umi": "^3.5.20" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /packages/visual-dev/src/ast/index.ts: -------------------------------------------------------------------------------- 1 | export * from './injectComponent'; 2 | export * from './insertVueAttr'; 3 | export * from './insertReactAttr'; 4 | -------------------------------------------------------------------------------- /packages/visual-dev/src/ast/injectComponent.ts: -------------------------------------------------------------------------------- 1 | import * as t from '@babel/types'; 2 | import type { Visitor } from '@babel/core'; 3 | import template from '@babel/template'; 4 | 5 | interface Props { 6 | componentIdentifier: string; 7 | moduleStringLiteral: string; 8 | column: number; 9 | line: number; 10 | } 11 | 12 | export const injectCoponent = (props: Props): (() => { visitor: Visitor }) => { 13 | const { componentIdentifier, moduleStringLiteral, column, line } = props; 14 | 15 | return () => ({ 16 | visitor: { 17 | Program(path) { 18 | const { body } = path.node; 19 | 20 | const importDeclaration = body.filter((itemNode) => t.isImportDeclaration(itemNode)); 21 | 22 | let targetIndex = importDeclaration.findIndex( 23 | (itemNode) => 24 | t.isImportDeclaration(itemNode) && itemNode.source.value === moduleStringLiteral, 25 | ); 26 | 27 | if (targetIndex === -1) { 28 | // 不存在就增加module 29 | targetIndex = importDeclaration.length; 30 | 31 | body.splice( 32 | targetIndex, 33 | 0, 34 | t.importDeclaration( 35 | [ 36 | t.importSpecifier( 37 | t.identifier(componentIdentifier), 38 | t.identifier(componentIdentifier), 39 | ), 40 | ], 41 | t.stringLiteral(moduleStringLiteral), 42 | ) as any, 43 | ); 44 | } else { 45 | // 存在就增加 46 | const targetNode: any = body[targetIndex]; 47 | // 检查是否有已经引用过的 组件进来 48 | const componentIdentifierIndex = targetNode.specifiers.findIndex( 49 | (node: any) => 50 | t.isImportSpecifier(node) && (node.imported as any).name === componentIdentifier, 51 | ); 52 | 53 | if (componentIdentifierIndex === -1) { 54 | // 引了这个库,但是没用过 55 | targetNode.specifiers.push( 56 | t.importSpecifier( 57 | t.identifier(componentIdentifier), 58 | t.identifier(componentIdentifier), 59 | ), 60 | ); 61 | } else { 62 | // 用过就不管 63 | } 64 | } 65 | }, 66 | JSXOpeningElement: { 67 | enter(path) { 68 | const { line: currentLine, column: currentColumn } = path.node.loc?.start || { 69 | line: 0, 70 | column: 0, 71 | }; 72 | if (line === currentLine && currentColumn === column) { 73 | const parentNode: any = path.parent; 74 | if (parentNode && parentNode.children) { 75 | const newNode = template( 76 | `<${componentIdentifier}>${componentIdentifier}`, 77 | { 78 | sourceType: 'module', 79 | allowImportExportEverywhere: true, 80 | plugins: ['typescript', 'jsx'], 81 | }, 82 | )(); 83 | parentNode.children.unshift(newNode); 84 | } 85 | } 86 | }, 87 | }, 88 | }, 89 | }); 90 | }; 91 | -------------------------------------------------------------------------------- /packages/visual-dev/src/ast/insertReactAttr.ts: -------------------------------------------------------------------------------- 1 | import { parse } from '@babel/parser'; 2 | import { transformFromAst } from '@babel/core'; 3 | import type { Visitor } from '@babel/core'; 4 | import { jsxIdentifier, jsxAttribute, stringLiteral } from '@babel/types'; 5 | 6 | export const pathMap: Record = {}; 7 | 8 | export const insertJSXElementPathPlugin = (): { visitor: Visitor } => { 9 | return { 10 | visitor: { 11 | JSXOpeningElement: { 12 | enter(path, state) { 13 | const filePath = state?.file?.opts?.filename; 14 | 15 | if (filePath.match(/node_modules/g) || !filePath) return; 16 | const componentName = ((path.node as any)?.name?.name || '') as string; 17 | 18 | const { line, column } = path.node.loc?.start || { line: 0, column: 0 }; 19 | 20 | const absolutePath = `${state.filename}:${line.toString()}:${column.toString()}`; 21 | 22 | const relativePath = `${absolutePath.replace(process.cwd(), '')}`; 23 | 24 | const attr: any = jsxAttribute( 25 | jsxIdentifier(`data-v-p`), 26 | stringLiteral(`${absolutePath}|${relativePath}|${componentName}|react`), 27 | ); 28 | 29 | (path.node as any).attributes.push(attr); 30 | }, 31 | }, 32 | }, 33 | }; 34 | }; 35 | 36 | export const insertReactAttr = (source: string, filePath: string): string => { 37 | const ast = parse(source.toString(), { 38 | sourceType: 'unambiguous', 39 | allowUndeclaredExports: true, 40 | allowImportExportEverywhere: true, 41 | plugins: ['typescript', 'jsx'], 42 | }); 43 | 44 | const { code } = transformFromAst(ast as any, source.toString(), { 45 | plugins: [insertJSXElementPathPlugin], 46 | filename: filePath, 47 | }); 48 | return code || ''; 49 | }; 50 | -------------------------------------------------------------------------------- /packages/visual-dev/src/ast/insertVueAttr.ts: -------------------------------------------------------------------------------- 1 | import { parse, transform } from '@vue/compiler-dom'; 2 | import MagicString from 'magic-string'; 3 | 4 | export const insertVueAttr = (source: string, filePath: string): string => { 5 | const ast = parse(source); 6 | const s: any = new MagicString(source); 7 | 8 | const tplAst: any = ast.children.find((item: any) => item.tag === 'template'); 9 | 10 | if (tplAst) { 11 | transform(tplAst, { 12 | nodeTransforms: [ 13 | (node) => { 14 | // 1 is NodeTypes.Element 15 | if (node.type === 1 && node.tagType === 0 && node.tag !== 'template') { 16 | const { start } = node.loc; 17 | 18 | const tagLen = node.tag.length; 19 | 20 | const idx = start.offset + tagLen; 21 | 22 | const absolutePath = `${filePath}:${start.line.toString()}:${start.column.toString()}`; 23 | 24 | const relativePath = `${absolutePath.replace(process.cwd(), '')}`; 25 | 26 | const attrValue = `${absolutePath}|${relativePath}|${node.tag}|vue`; 27 | 28 | const attr = ` data-v-p="${attrValue}" `; 29 | 30 | s.appendLeft(idx + 1, attr); 31 | } 32 | }, 33 | ], 34 | }); 35 | } 36 | return s.toString(); 37 | }; 38 | -------------------------------------------------------------------------------- /packages/visual-dev/src/index.ts: -------------------------------------------------------------------------------- 1 | import type { Options } from 'http-proxy-middleware'; 2 | 3 | export * from './server'; 4 | 5 | export const ASSETS_DIR = 'assets'; 6 | export const CLIENT_ASSETS_DIR = '_dev-tools'; 7 | export const SERVER_PORT = 10078; 8 | 9 | export interface PluginOptions { 10 | injectFile?: boolean; // inject font-end code 11 | editor?: 12 | | 'sublime' 13 | | 'textmate' 14 | | 'emacs' 15 | | 'macvim' 16 | | 'phpstorm' 17 | | 'webstorm' 18 | | 'idea' 19 | | 'vscode' 20 | | 'vscode-insiders' 21 | | 'atom'; // open editor config 22 | port?: number; 23 | resolve?: { 24 | includes: string[]; 25 | }; 26 | devServerProxy?: Record | false; 27 | mode?: 'aim'; 28 | noServer?: boolean; 29 | shortcuts?: { 30 | inspect?: [string, string, string] | [string, string]; 31 | }; 32 | 33 | analysisPlugin: { 34 | rootPath?: string; 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /packages/visual-dev/src/server/MiddleWare/analysisFile.ts: -------------------------------------------------------------------------------- 1 | import type { RequestHandler } from 'express'; 2 | import type { ServerProps } from '../../'; 3 | 4 | // const analysisPluginData = { 5 | // nodes: [ 6 | // { id: '/src/index.js', label: '/src/index.js', name: 'index.js' }, 7 | // ], 8 | // edges: [ 9 | // { source: '/src/index.js', target: '/src/reportWebVitals.js' }, 10 | // ], 11 | // }; 12 | 13 | export const analysisFile = (props: ServerProps): RequestHandler => { 14 | return (req, res) => { 15 | res.send({ 16 | data: props.analysisFileData, 17 | }); 18 | }; 19 | }; 20 | 21 | export default analysisFile; 22 | -------------------------------------------------------------------------------- /packages/visual-dev/src/server/MiddleWare/getConfig.ts: -------------------------------------------------------------------------------- 1 | import type { RequestHandler } from 'express'; 2 | import type { PluginOptions } from '../../'; 3 | 4 | export const getConfig = (props: PluginOptions): RequestHandler => { 5 | return (req, res) => { 6 | res.send({ 7 | mode: props.mode, 8 | editor: props.editor || 'vscode', 9 | devServerProxy: props.devServerProxy ? props.devServerProxy : false, 10 | shortcuts: props.shortcuts || {}, 11 | }); 12 | }; 13 | }; 14 | 15 | export default getConfig; 16 | -------------------------------------------------------------------------------- /packages/visual-dev/src/server/MiddleWare/getMenu.ts: -------------------------------------------------------------------------------- 1 | import type { RequestHandler } from 'express'; 2 | import { resolvePath } from '../../utils'; 3 | import path from 'path'; 4 | 5 | interface Props { 6 | includes: string[]; // file or dir 7 | } 8 | 9 | export const getMenu = (props: Props): RequestHandler => { 10 | const { includes = [] } = props; 11 | const mdFile = resolvePath([path.resolve(process.cwd(), './README.MD'), ...includes], { 12 | ext: ['md'], 13 | dealString: (conrent) => conrent, 14 | }); 15 | 16 | return (req, res) => { 17 | res.send(JSON.stringify(mdFile)); 18 | }; 19 | }; 20 | 21 | export default getMenu; 22 | -------------------------------------------------------------------------------- /packages/visual-dev/src/server/MiddleWare/index.ts: -------------------------------------------------------------------------------- 1 | export * from './getMenu'; 2 | export * from './injectFile'; 3 | export * from './pathMap'; 4 | export * from './getConfig'; 5 | -------------------------------------------------------------------------------- /packages/visual-dev/src/server/MiddleWare/injectFile.ts: -------------------------------------------------------------------------------- 1 | import { parsePath } from '../../utils'; 2 | import { parse } from '@babel/parser'; 3 | import fs from 'fs-extra'; 4 | import { transformFromAst } from '@babel/core'; 5 | import { injectCoponent } from '../../ast'; 6 | import type { RequestHandler } from 'express'; 7 | 8 | export const injectFile: RequestHandler = (req, res) => { 9 | const { filePath, column, line } = parsePath(req.body.filePath); 10 | 11 | let { component } = req.body as { component: string; componentType?: string }; 12 | 13 | component = component.replace(/^./, ($1) => $1.toUpperCase()); 14 | 15 | const absPath = process.cwd() + filePath; 16 | if (!fs.existsSync(absPath)) return; 17 | 18 | const file = fs.readFileSync(absPath, 'utf-8'); 19 | 20 | const ast = parse(file, { 21 | sourceType: 'unambiguous', 22 | allowUndeclaredExports: true, 23 | allowImportExportEverywhere: true, 24 | plugins: ['typescript', 'jsx'], 25 | }); 26 | 27 | const { code = '' } = transformFromAst(ast as any, file, { 28 | plugins: [ 29 | injectCoponent({ 30 | componentIdentifier: component, 31 | moduleStringLiteral: 'antd', 32 | column, 33 | line, 34 | }), 35 | ], 36 | }); 37 | 38 | fs.writeFileSync(absPath, code); 39 | 40 | res.send('ok'); 41 | }; 42 | 43 | export default injectFile; 44 | -------------------------------------------------------------------------------- /packages/visual-dev/src/server/MiddleWare/pathMap.ts: -------------------------------------------------------------------------------- 1 | import type { RequestHandler } from 'express'; 2 | 3 | export const pathMapMid = (): RequestHandler => { 4 | return (req, res) => { 5 | res.send(JSON.stringify({})); 6 | }; 7 | }; 8 | 9 | export default pathMapMid; 10 | -------------------------------------------------------------------------------- /packages/visual-dev/src/server/createServer.ts: -------------------------------------------------------------------------------- 1 | import express, { json } from 'express'; 2 | import cors from 'cors'; 3 | import path from 'path'; 4 | import { SERVER_PORT } from '../index'; 5 | import type { PluginOptions } from '../'; 6 | import analysisFile from './MiddleWare/analysisFile'; 7 | import fs from 'fs-extra'; 8 | 9 | export interface ServerProps { 10 | analysisFileData: { 11 | nodes: { id: string; label: string; name: string }[]; 12 | edges: { source: string; target: string }[]; 13 | }; 14 | } 15 | 16 | export const createServer = async (options: PluginOptions, props: ServerProps) => { 17 | // const { resolve = { includes: [''] } } = options; 18 | let { port = SERVER_PORT } = options; 19 | const app = express(); 20 | 21 | // 如果端口冲突了,替换当前端口 22 | app.engine('.html', function (filePath, _options, callback) { 23 | fs.readFile(filePath, function (err, content) { 24 | if (err) return callback(err); 25 | const rendered = content.toString().replace('10078', String(port)); 26 | return callback(null, rendered); 27 | }); 28 | }); 29 | 30 | // 读取本地 dev-page 文件 31 | app.use(express.static(path.resolve(__dirname, './dev-page'))); 32 | 33 | app.use(cors()); 34 | // @ts-ignore 35 | app.use(json()); 36 | 37 | app.get('/web-devtools/analysisFile', analysisFile(props)); 38 | 39 | app.get('/*', (req, res) => res.render(path.resolve(__dirname, './dev-page/index.html'))); 40 | 41 | const startListen = (appPort: number) => { 42 | app.listen(appPort, () => { 43 | // eslint-disable-next-line 44 | console.log(`visual-dev is listening on port : ${port}`); 45 | }); 46 | }; 47 | process.on('uncaughtException', (err) => { 48 | if (err.message && err.message.includes('address already in use')) { 49 | startListen((port += 1)); 50 | } 51 | }); 52 | startListen(port); 53 | }; 54 | -------------------------------------------------------------------------------- /packages/visual-dev/src/server/index.ts: -------------------------------------------------------------------------------- 1 | export * from './createServer'; 2 | -------------------------------------------------------------------------------- /packages/visual-dev/src/types/payload.ts: -------------------------------------------------------------------------------- 1 | export type WSPayload = LaunchEditorPayload; 2 | 3 | export interface LaunchEditorPayload { 4 | type: 'launch-editor-payload'; 5 | path: string; 6 | } 7 | -------------------------------------------------------------------------------- /packages/visual-dev/src/types/types.d.ts: -------------------------------------------------------------------------------- 1 | declare module '@babel/traverse' { 2 | import Traverse from 'babel-traverse'; 3 | 4 | export * from 'babel-traverse'; 5 | 6 | export default Traverse; 7 | } 8 | 9 | declare module '@babel/core' { 10 | export * from 'babel-core'; 11 | } 12 | 13 | declare module '@babel/generator' { 14 | import generator from 'babel-generator'; 15 | 16 | export * from 'babel-generator'; 17 | 18 | export default generator; 19 | } 20 | 21 | // import Traverse from '@babel/traverse' 22 | 23 | declare module '@babel/template' { 24 | import template from 'babel-template'; 25 | 26 | export * from 'bablel-template'; 27 | 28 | export default template; 29 | } 30 | 31 | declare module 'prismjs'; 32 | 33 | declare module 'escape-html'; 34 | -------------------------------------------------------------------------------- /packages/visual-dev/src/umi/index.ts: -------------------------------------------------------------------------------- 1 | import type { IApi } from 'umi'; 2 | import type { Compiler } from 'webpack'; 3 | // import { createServer } from '../server'; 4 | import { insertJSXElementPathPlugin } from '../ast'; 5 | 6 | import fs from 'fs'; 7 | import path from 'path'; 8 | 9 | const staticDir = 'assets'; 10 | const assetsDir = path.resolve(__dirname, `../../dev-ui/${staticDir}`); 11 | 12 | const htmlTplPath = path.resolve(__dirname, '../../dev-ui/index.html'); 13 | class WriteAndCopy { 14 | public apply(compiler: Compiler) { 15 | compiler.hooks.emit.tap('writeFileAndCopyFile', (compilation: any) => { 16 | // copy file 17 | const dirs = fs.readdirSync(assetsDir); 18 | 19 | dirs.forEach((fileName) => { 20 | const content = fs.readFileSync(`${assetsDir}/${fileName}`, 'utf-8'); 21 | // eslint-disable-next-line no-param-reassign 22 | compilation.assets[`${staticDir}/${fileName}`] = { 23 | source() { 24 | return content; 25 | }, 26 | size() { 27 | return content.length; 28 | }, 29 | }; 30 | }); 31 | }); 32 | } 33 | } 34 | 35 | export default (api: IApi) => { 36 | api.describe({ 37 | key: 'visualDev', 38 | config: { 39 | default: { 40 | devServerProxy: false, 41 | editor: 'vscode', 42 | mode: 'aim', 43 | }, 44 | schema(joi) { 45 | return joi.object(); 46 | }, 47 | onChange: api.ConfigChangeType.regenerateTmpFiles, 48 | }, 49 | enableBy: api.EnableBy.config, 50 | }); 51 | 52 | api.modifyBabelOpts((babelOptions) => { 53 | babelOptions.plugins.unshift([insertJSXElementPathPlugin]); 54 | return babelOptions; 55 | }); 56 | 57 | api.chainWebpack((config) => { 58 | config.plugin('writeAndCopy').use(WriteAndCopy); 59 | return config; 60 | }); 61 | 62 | api.modifyHTML(($) => { 63 | $('body').append(`${fs.readFileSync(htmlTplPath)}`); 64 | return $; 65 | }); 66 | 67 | api.onStart(() => { 68 | // createServer(api.config.visualDev); 69 | }); 70 | }; 71 | -------------------------------------------------------------------------------- /packages/visual-dev/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from './resolvePath'; 2 | export * from './parsePath'; 3 | -------------------------------------------------------------------------------- /packages/visual-dev/src/utils/parsePath.ts: -------------------------------------------------------------------------------- 1 | export const parsePath = ( 2 | path: string, 3 | ): { raw: string; filePath: string; column: number; line: number } => { 4 | // eslint-disable-next-line 5 | const [raw, filePath, line, column] = path.match(/(.*?)\:([\d]*?)\:([\d]*?)$/) || []; 6 | return { 7 | raw, 8 | filePath, 9 | column: Number(column), 10 | line: Number(line), 11 | }; 12 | }; 13 | -------------------------------------------------------------------------------- /packages/visual-dev/src/utils/resolvePath.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import path from 'path'; 3 | 4 | const joinPath = (...args: string[]) => path.join(...args); 5 | 6 | interface Options { 7 | ext?: string[]; 8 | dealString?: (content: string) => string; 9 | } 10 | 11 | export const resolvePath = (includes: string[], options?: Options) => { 12 | const { ext = [], dealString } = options || {}; 13 | 14 | const resultMapObj: any = {}; 15 | includes.forEach((curPath) => { 16 | if (!curPath) return; 17 | const isDirectory = fs.statSync(curPath).isDirectory(); 18 | const isFile = fs.statSync(curPath).isFile(); 19 | const basename = path.basename(curPath); 20 | 21 | if (isDirectory && fs.readdirSync(curPath).length) { 22 | const dirs = fs.readdirSync(curPath); 23 | resultMapObj[basename] = resolvePath( 24 | dirs.map((basePath) => joinPath(curPath, basePath)), 25 | options, 26 | ); 27 | } else if (isFile && ext.includes(path.extname(curPath).substr(1).toLocaleLowerCase())) { 28 | const content = fs.readFileSync(curPath, 'utf-8'); 29 | resultMapObj[basename] = dealString ? dealString(content) : content; 30 | } 31 | }); 32 | return resultMapObj; 33 | }; 34 | -------------------------------------------------------------------------------- /packages/visual-dev/src/utils/uid.ts: -------------------------------------------------------------------------------- 1 | export const uid = (end?: number) => { 2 | return Math.random().toString(36).slice(2, end); 3 | }; 4 | -------------------------------------------------------------------------------- /packages/visual-dev/src/vite/index.ts: -------------------------------------------------------------------------------- 1 | import type { PluginOption } from 'vite'; 2 | import { insertVueAttr, insertReactAttr } from '../ast'; 3 | import path from 'path'; 4 | import fs from 'fs'; 5 | // import { createServer } from '../server'; 6 | // import type { PluginOptions } from '../'; 7 | 8 | const assetsDir = path.resolve(__dirname, `../../dev-ui/assets`); 9 | const templateFile = path.resolve(__dirname, '../../dev-ui/index.html'); 10 | 11 | const vitePlugin = (): PluginOption => { 12 | return { 13 | name: 'visual-dev', 14 | enforce: 'pre', 15 | transform(code, id) { 16 | if (/\.(j|t)sx$/.test(id)) { 17 | return insertReactAttr(code, id); 18 | } 19 | if (/\.vue$/.test(id)) { 20 | return insertVueAttr(code, id); 21 | } 22 | return code; 23 | }, 24 | transformIndexHtml(html) { 25 | let targetTemplate = fs.readFileSync(templateFile, 'utf-8'); 26 | 27 | targetTemplate = targetTemplate.replace(/\/assets/g, assetsDir); 28 | 29 | html = html.replace(/([\s\S]*)<\/body>/g, ($1: string, $2: string) => { 30 | return $2 + targetTemplate; 31 | }); 32 | return html; 33 | }, 34 | buildStart() { 35 | // if (options.noServer) return; 36 | // createServer(options); 37 | }, 38 | }; 39 | }; 40 | 41 | export default vitePlugin; 42 | -------------------------------------------------------------------------------- /packages/visual-dev/src/webpack/analysisPlugin.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-unused-vars */ 2 | import path from 'path'; 3 | import type { Compiler } from 'webpack'; 4 | import type { ServerProps } from '../server/createServer'; 5 | import G6 from '@antv/g6'; 6 | 7 | const LEGAL_FILES = ['.js', '.ts', '.jsx', '.tsx', '.css']; 8 | 9 | type NODE = { 10 | id: string; 11 | label: string; 12 | name: string; 13 | }; 14 | 15 | type EDGE = { 16 | source: string; 17 | target: string; 18 | }; 19 | 20 | type analysisData = { 21 | nodes: NODE[]; 22 | edges: EDGE[]; 23 | }; 24 | 25 | function checkFile(filePath: string) { 26 | const ext = path.extname(filePath); 27 | return LEGAL_FILES.includes(ext) && !filePath.includes('/node_modules/'); 28 | } 29 | 30 | interface Options { 31 | rootPath?: string; 32 | } 33 | 34 | const analysisPlugin = (compiler: Compiler, options: Options, serverProps: ServerProps) => { 35 | if (!options) { 36 | return; 37 | } 38 | 39 | if (options && !options.rootPath) { 40 | console.error('开启 analysisPlugin 的时候需要配置 rootPath'); 41 | return; 42 | } 43 | 44 | const { rootPath } = options; 45 | const dependenciesTree: Record< 46 | string, 47 | { 48 | parent: string; 49 | next: string[]; 50 | } 51 | > = {}; 52 | 53 | compiler.hooks.normalModuleFactory.tap('analysisPlugin', (nmf) => { 54 | nmf.hooks.afterResolve.tap('DepAnalysisPlugin', (result: any) => { 55 | const resourceResolveData = 56 | result.resourceResolveData || result.createData?.resourceResolveData || {}; 57 | const curFile = resourceResolveData?.path as string; 58 | const parentFile = resourceResolveData.context ? resourceResolveData.context.issuer : null; 59 | // 对于合法文件进行分析 60 | if (curFile && checkFile(curFile)) { 61 | // dependenciesFileMap[curFile] = dependenciesFileMap[curFile] || []; 62 | // 如果有父级文件,则直接添加 63 | if (parentFile) { 64 | if (!dependenciesTree[curFile]) { 65 | dependenciesTree[curFile] = { 66 | parent: parentFile, 67 | next: [], 68 | }; 69 | } 70 | if (dependenciesTree[parentFile]) { 71 | dependenciesTree[parentFile].next.push(curFile); 72 | } 73 | } else { 74 | // 说明是根路径 75 | dependenciesTree[curFile] = { 76 | parent: curFile, 77 | next: [], 78 | }; 79 | } 80 | } 81 | }); 82 | }); 83 | 84 | compiler.hooks.done.tap('DepAnalysisPlugin', async (stats) => { 85 | if (rootPath) { 86 | // const routers = fs.readdirSync(path.resolve(process.cwd(), rootPath)); 87 | const data: analysisData = { 88 | nodes: Object.keys(dependenciesTree).map((p) => { 89 | return { 90 | id: p.match('[^/]+(?!.*/)')![0], 91 | label: p.match('[^/]+(?!.*/)')![0], 92 | name: p, 93 | }; 94 | }), 95 | edges: Object.entries(dependenciesTree).reduce< 96 | { 97 | source: string; 98 | target: string; 99 | }[] 100 | >((pre, [p, { parent, next }]) => { 101 | p = p.replace(process.cwd(), ''); 102 | const edges = next.map((nextItem) => { 103 | return { 104 | source: p.match('[^/]+(?!.*/)')![0], 105 | target: nextItem.match('[^/]+(?!.*/)')![0], 106 | style: { 107 | endArrow: { 108 | path: G6.Arrow.vee(), 109 | fill: '#F6BD16', 110 | }, 111 | }, 112 | }; 113 | }); 114 | return [...pre, ...edges]; 115 | }, []), 116 | }; 117 | serverProps.analysisFileData = data; 118 | } 119 | }); 120 | }; 121 | export default analysisPlugin; 122 | -------------------------------------------------------------------------------- /packages/visual-dev/src/webpack/index.ts: -------------------------------------------------------------------------------- 1 | import analysisPlugin from './analysisPlugin'; 2 | import type { ServerProps } from '../server'; 3 | import { createServer } from '../server'; 4 | import type { Compiler } from 'webpack'; 5 | import type { PluginOptions } from '../'; 6 | import type { Options } from 'http-proxy-middleware'; 7 | 8 | const defaultOptions = { 9 | injectFile: true, 10 | }; 11 | 12 | const newProxyOptions: Record = {}; 13 | 14 | export const WebpackDevtoolPlugin = class { 15 | public options: PluginOptions; 16 | 17 | // rewrite proxy 18 | static proxy = (proxyOptions: Record) => { 19 | Object.entries(proxyOptions).forEach(([url, val]) => { 20 | if (typeof val === 'string') { 21 | newProxyOptions[url] = { 22 | target: val, 23 | }; 24 | } else { 25 | newProxyOptions[url] = { 26 | ...val, 27 | }; 28 | } 29 | }); 30 | 31 | return proxyOptions; 32 | }; 33 | 34 | constructor(options: PluginOptions) { 35 | this.options = { ...defaultOptions, ...options }; 36 | } 37 | apply(compiler: Compiler) { 38 | const serverProps = {} as ServerProps; 39 | // 依赖分析 40 | analysisPlugin(compiler, this.options.analysisPlugin, serverProps); 41 | 42 | // 启动服务 43 | compiler.hooks.environment.tap('createServer', () => { 44 | createServer(this.options, serverProps); 45 | }); 46 | 47 | // compiler.options.module?.rules.push({ 48 | // test: /\.(j|t)sx$/, 49 | // use: { 50 | // loader: path.resolve(__dirname, './parseReactLoader.js'), 51 | // }, 52 | // }); 53 | 54 | // compiler.options.module?.rules.push({ 55 | // test: /\.vue$/, 56 | // use: { 57 | // loader: path.resolve(__dirname, './parseVueLoader.js'), 58 | // }, 59 | // }); 60 | 61 | // if (this.options.injectFile) { 62 | // compiler.hooks.emit.tap('injectFile', injectFile); 63 | // } 64 | } 65 | }; 66 | 67 | export default WebpackDevtoolPlugin; 68 | -------------------------------------------------------------------------------- /packages/visual-dev/src/webpack/injectFile.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import path from 'path'; 3 | 4 | const staticDir = 'assets'; 5 | const templateFile = path.resolve(__dirname, '../../dev-ui/index.html'); 6 | const assetsDir = path.resolve(__dirname, `../../dev-ui/${staticDir}`); 7 | 8 | const injectFile = (compilation: any) => { 9 | const mathchHtmlFile = Object.keys(compilation.assets).filter((filePath) => 10 | /\.html$/.test(filePath), 11 | ); 12 | 13 | mathchHtmlFile.forEach((filePath) => { 14 | let htmlContent = compilation.assets['index.html'].source(); 15 | 16 | // eslint-disable-next-line no-param-reassign 17 | compilation.assets[filePath] = { 18 | source() { 19 | const targetTemplate = fs.readFileSync(templateFile, 'utf-8'); 20 | htmlContent = htmlContent.replace(/([\s\S]*)<\/body>/g, ($1: string, $2: string) => { 21 | return $2 + targetTemplate; 22 | }); 23 | return htmlContent; 24 | }, 25 | size() { 26 | return htmlContent.length; 27 | }, 28 | }; 29 | }); 30 | 31 | // copy file 32 | const dirs = fs.readdirSync(assetsDir); 33 | 34 | dirs.forEach((fileName) => { 35 | const content = fs.readFileSync(`${assetsDir}/${fileName}`, 'utf-8'); 36 | // eslint-disable-next-line no-param-reassign 37 | compilation.assets[`${staticDir}/${fileName}`] = { 38 | source() { 39 | return content; 40 | }, 41 | size() { 42 | return content.length; 43 | }, 44 | }; 45 | }); 46 | }; 47 | 48 | export default injectFile; 49 | -------------------------------------------------------------------------------- /packages/visual-dev/src/webpack/parseReactLoader.ts: -------------------------------------------------------------------------------- 1 | import { insertReactAttr } from '../ast/insertReactAttr'; 2 | 3 | export const devtoolLoader = function webpackLoader(this: any, source: any) { 4 | const { resourcePath: filePath } = this; 5 | 6 | return insertReactAttr(source, filePath); 7 | }; 8 | 9 | export default devtoolLoader; 10 | -------------------------------------------------------------------------------- /packages/visual-dev/src/webpack/parseVueLoader.ts: -------------------------------------------------------------------------------- 1 | import { insertVueAttr } from '../ast'; 2 | 3 | export const parseVueLoader = function webpackLoader(this: any, source: string) { 4 | const { resourcePath: filePath } = this; 5 | return insertVueAttr(source, filePath); 6 | }; 7 | 8 | export default parseVueLoader; 9 | -------------------------------------------------------------------------------- /packages/visual-dev/target/npmlist.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.0.9", 3 | "name": "visual-dev", 4 | "dependencies": { 5 | "@babel/core": { "version": "7.15.5" }, 6 | "@babel/generator": { "version": "7.15.4" }, 7 | "@babel/parser": { "version": "7.15.7" }, 8 | "@babel/template": { "version": "7.15.4" }, 9 | "@babel/traverse": { "version": "7.15.4" }, 10 | "@babel/types": { "version": "7.15.6" }, 11 | "@types/cors": { "version": "2.8.12" }, 12 | "@umijs/launch-editor": { "version": "1.0.1" }, 13 | "body-parser": { "version": "1.19.0" }, 14 | "cors": { "version": "2.8.5" }, 15 | "express": { "version": "4.17.1" }, 16 | "fs-extra": { "version": "10.0.0" }, 17 | "glob": { "version": "7.2.0" }, 18 | "http-proxy-middleware": { "version": "2.0.1" }, 19 | "markdown-it": { "version": "12.2.0" }, 20 | "prismjs": { "version": "1.25.0" }, 21 | "slash2": { "version": "2.0.0" } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/visual-dev/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": ["src/**/*"], 4 | "exclude": ["node_modules", "**/*.spec.ts"], 5 | "compilerOptions": { 6 | "target": "ES2019", 7 | "outDir": "./plugins", 8 | "module": "commonjs", 9 | "lib": ["ESNext"], 10 | "sourceMap": false 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'packages/*' 3 | - 'examples/*' 4 | - 'demo' 5 | -------------------------------------------------------------------------------- /scripts/clear.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs-extra'); 3 | 4 | const resolve = (str) => path.resolve(__dirname, str); 5 | 6 | fs.removeSync(resolve('../packages/visual-dev/dist')); 7 | fs.removeSync(resolve('../packages/visual-dev/dev-ui')); 8 | fs.removeSync(resolve('../packages/visual-dev/plugins')); 9 | fs.removeSync(resolve('../packages/dev-ui/dist')); 10 | fs.removeSync(resolve('../demo/dev-ui')); 11 | fs.removeSync(resolve('../demo/plugins')); 12 | fs.removeSync(resolve('../demo/dist')); 13 | -------------------------------------------------------------------------------- /scripts/demo.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs-extra'); 3 | 4 | const resolve = (str) => path.resolve(__dirname, str); 5 | 6 | fs.copySync(resolve('../demo/dev-ui/assets'), resolve('../demo/dist/assets')); 7 | -------------------------------------------------------------------------------- /scripts/moveFiles.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs-extra'); 3 | 4 | const resolve = (str) => path.resolve(__dirname, str); 5 | 6 | fs.copySync(resolve('../packages/dev-ui/dist'), resolve('../packages/visual-dev/dev-ui')); 7 | fs.copySync(resolve('../packages/visual-dev/src'), resolve('../demo/plugins')); 8 | fs.copySync(resolve('../packages/dev-ui/dist'), resolve('../demo/dev-ui')); 9 | 10 | // 插件路径的转换(为了注入脚本到 demo 页面提供正确的路径) 11 | const transformFlePath = resolve('../demo/plugins/vite/index.ts'); 12 | 13 | let newContent = fs.readFileSync(transformFlePath, 'utf-8'); 14 | 15 | newContent = newContent.replace( 16 | `targetTemplate.replace(/\\/assets/g, assetsDir);`, 17 | `targetTemplate.replace(/\\/assets/g, '/visual-dev/assets');`, 18 | ); 19 | 20 | newContent = newContent.replace('createServer({});', ''); 21 | 22 | fs.writeFileSync(transformFlePath, newContent); 23 | 24 | const transformFlePath2 = resolve('../demo/plugins/ast/insertReactAttr.ts'); 25 | 26 | let newContent2 = fs.readFileSync(transformFlePath2, 'utf-8'); 27 | 28 | newContent2 = newContent2.replace( 29 | 'stringLiteral(`${absolutePath}|${relativePath}|${componentName}|react`),', 30 | 'stringLiteral(`${relativePath}|${relativePath}|${componentName}|react`),', 31 | ); 32 | 33 | fs.writeFileSync(transformFlePath2, newContent2); 34 | 35 | // stringLiteral(`${absolutePath}|${relativePath}|${componentName}|react`), 36 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "typeRoots": ["node_modules/@types"], 4 | "target": "ES2018", 5 | "moduleResolution": "node", 6 | "strict": true, 7 | "declaration": true, 8 | "noImplicitOverride": true, 9 | "noUnusedLocals": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true 12 | }, 13 | "exclude": ["node_modules", "**/__test?__", "**/dist"] 14 | } 15 | -------------------------------------------------------------------------------- /types/types.d.ts: -------------------------------------------------------------------------------- 1 | declare module '@babel/traverse' { 2 | import Traverse from '@types/babel-traverse'; 3 | 4 | export * from '@types/babel-traverse'; 5 | 6 | export default Traverse; 7 | } 8 | 9 | declare module '@babel/core' { 10 | import core from '@types/babel-core'; 11 | 12 | export * from '@types/babel-core'; 13 | 14 | export default core; 15 | } 16 | 17 | declare module '@babel/generator' { 18 | import generator from '@types/babel-generator'; 19 | 20 | export * from '@types/babel-generator'; 21 | 22 | export default generator; 23 | } 24 | 25 | // import Traverse from '@babel/traverse' 26 | --------------------------------------------------------------------------------