├── .editorconfig ├── .eslintrc.js ├── .github └── workflows │ └── deploy.yml ├── .gitignore ├── .husky ├── commit-msg ├── lint-stagedrc.js └── pre-commit ├── .ls-lint.yml ├── README.md ├── commitlint.config.js ├── favicon.ico ├── index.html ├── package.json ├── pnpm-lock.yaml ├── src ├── App.vue ├── api │ └── index.ts ├── components │ └── Header.vue ├── const │ ├── code.ts │ ├── imports.ts │ └── index.ts ├── main.ts ├── repl-store │ └── index.ts ├── types │ ├── dts │ │ └── shims-vue.d.ts │ ├── index.ts │ └── replStore.ts └── utils │ ├── download │ ├── index.ts │ └── template │ │ ├── Readme.md │ │ ├── index.html │ │ ├── main.js │ │ ├── package.json │ │ └── vite.config.js │ ├── encode.ts │ ├── index.ts │ └── version.ts ├── tsconfig.json └── vite.config.ts /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset=utf-8 5 | end_of_line=LF 6 | insert_final_newline=true 7 | indent_style=space 8 | indent_size=2 9 | max_line_length = 100 10 | 11 | [*.{yml,yaml,json}] 12 | indent_style = space 13 | indent_size = 2 14 | 15 | [*.md] 16 | trim_trailing_whitespace = false 17 | 18 | [Makefile] 19 | indent_style = tab -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | browser: true, 5 | node: true, 6 | jest: true, 7 | es6: true, 8 | }, 9 | parser: 'vue-eslint-parser', 10 | parserOptions: { 11 | parser: '@typescript-eslint/parser', 12 | sourceType: 'module', 13 | ecmaVersion: 2020, 14 | ecmaFeatures: { 15 | jsx: true, 16 | tsx: true, 17 | }, 18 | }, 19 | plugins: ['@typescript-eslint', 'import'], 20 | extends: [ 21 | 'plugin:vue/vue3-recommended', 22 | 'eslint:recommended', 23 | 'plugin:import/recommended', 24 | 'plugin:import/typescript', 25 | 'plugin:@typescript-eslint/recommended', 26 | '@vue/eslint-config-typescript/recommended', 27 | ], 28 | globals: { 29 | defineProps: 'readonly', 30 | defineEmits: 'readonly', 31 | defineExpose: 'readonly', 32 | withDefaults: 'readonly', 33 | }, 34 | rules: { 35 | quotes: ['error', 'single', { avoidEscape: true, allowTemplateLiterals: true }], 36 | 'no-console': 'off', 37 | 'no-debugger': 'warn', 38 | 'no-undef': 'off', 39 | 'no-unused-vars': 'off', 40 | '@typescript-eslint/no-unused-vars': ['error'], 41 | camelcase: ['error', { properties: 'never' }], 42 | semi: ['error', 'never'], 43 | 'sort-imports': [ 44 | 'error', 45 | { 46 | ignoreDeclarationSort: true, 47 | }, 48 | ], 49 | '@typescript-eslint/member-delimiter-style': [ 50 | 'error', 51 | { 52 | multiline: { 53 | delimiter: 'none', 54 | requireLast: false, 55 | }, 56 | singleline: { 57 | delimiter: 'semi', 58 | requireLast: false, 59 | }, 60 | }, 61 | ], 62 | 'import/no-unused-modules': 'error', 63 | 'import/no-unresolved': 'off', 64 | 65 | // vue 66 | 'vue/max-attributes-per-line': ['error', { 67 | 'singleline': { 68 | 'max': 1 69 | }, 70 | 'multiline': { 71 | 'max': 1 72 | }, 73 | }], 74 | 'vue/html-closing-bracket-spacing': 'error', 75 | 'vue/require-default-prop': 'error', 76 | 'vue/require-explicit-emits': 'error', 77 | }, 78 | ignorePatterns: ['dist', 'node_modules'], 79 | } 80 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy Pages 2 | on: 3 | push: 4 | branches: 5 | - main 6 | jobs: 7 | build-and-deploy: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v2 11 | 12 | - name: Install pnpm 13 | uses: pnpm/action-setup@v2.0.1 14 | with: 15 | version: 6.23.2 16 | 17 | - name: Set node version to 16 18 | uses: actions/setup-node@v2 19 | with: 20 | node-version: 16 21 | cache: "pnpm" 22 | 23 | - run: pnpm install 24 | 25 | - name: Build 26 | run: pnpm run build 27 | 28 | - name: Deploy 29 | uses: JamesIves/github-pages-deploy-action@releases/v3 30 | with: 31 | ACCESS_TOKEN: ${{ secrets.PAGES_TOKEN }} 32 | BRANCH: gh-pages 33 | repositoryName: brenner8023/devui-playground 34 | FOLDER: dist 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | .pnpm-debug.log* 4 | dist 5 | src/types/dts/components.d.ts 6 | src/types/dts/auto-imports.d.ts 7 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | pnpm exec commitlint --edit $1 5 | -------------------------------------------------------------------------------- /.husky/lint-stagedrc.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | '*.{js,jsx,ts,tsx,vue}': ['eslint --fix'], 4 | } 5 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | pnpm run ls-lint && pnpm exec lint-staged -c .husky/lint-stagedrc.js 5 | -------------------------------------------------------------------------------- /.ls-lint.yml: -------------------------------------------------------------------------------- 1 | ls: 2 | src/*: 3 | .dir: kebab-case 4 | .ts: camelCase 5 | .tsx: PascalCase 6 | .spec.ts: camelCase 7 | .d.ts: kebab-case 8 | .vue: PascalCase 9 | .md: PascalCase 10 | 11 | ignore: 12 | - node_modules 13 | - dist 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # devui-playground 2 | 3 | 基于`@vue/repl`实现 4 | 5 | ## 技术栈 6 | 7 | - `@vue/repl` 8 | - vue3.x 9 | - vite 10 | - unocss 11 | - typescript 12 | - pnpm 13 | 14 | ## 感谢 15 | 16 | - [element-plus-playground](//github.com/element-plus/element-plus-playground) 17 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | extends: ['@commitlint/config-conventional'], 4 | } 5 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DevCloudFE/devui-playground/332de843e19d5847f8643b74c85bf9668da8b5fe/favicon.ico -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | DevUI Playground 9 | 10 | 11 | 12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "devui-playground", 3 | "version": "0.1.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "dev": "vite --host", 8 | "build": "vite build", 9 | "lint": "pnpm run ls-lint && pnpm run eslint", 10 | "eslint": "eslint --cache --fix ./src --ext .vue,.js,.ts,.tsx", 11 | "ls-lint": "ls-lint", 12 | "prepare": "husky install", 13 | "test": "echo \"Error: no test specified\" && exit 1" 14 | }, 15 | "author": "", 16 | "license": "MIT", 17 | "devDependencies": { 18 | "@commitlint/cli": "^16.1.0", 19 | "@commitlint/config-conventional": "^16.0.0", 20 | "@ls-lint/ls-lint": "^1.10.0", 21 | "@types/file-saver": "^2.0.5", 22 | "@types/node": "^17.0.10", 23 | "@typescript-eslint/eslint-plugin": "^5.10.2", 24 | "@typescript-eslint/parser": "^5.10.2", 25 | "@vitejs/plugin-vue": "^2.1.0", 26 | "@vue/eslint-config-typescript": "^10.0.0", 27 | "eslint": "^8.8.0", 28 | "eslint-plugin-import": "^2.25.4", 29 | "eslint-plugin-vue": "^8.4.0", 30 | "husky": "^7.0.4", 31 | "lint-staged": "^12.3.3", 32 | "typescript": "^4.5.5", 33 | "unocss": "^0.22.5", 34 | "unplugin-auto-import": "^0.5.11", 35 | "unplugin-vue-components": "^0.17.14", 36 | "vite": "^2.7.13", 37 | "vite-plugin-inspect": "^0.3.13" 38 | }, 39 | "dependencies": { 40 | "@idux/cdk": "^1.0.0-alpha.4", 41 | "@idux/components": "^1.0.0-alpha.5", 42 | "@vue/repl": "^1.0.0", 43 | "@vueuse/core": "^7.5.4", 44 | "compare-versions": "^4.1.3", 45 | "file-saver": "^2.0.5", 46 | "jszip": "^3.7.1", 47 | "vue": "^3.2.29" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 46 | 47 | 52 | -------------------------------------------------------------------------------- /src/api/index.ts: -------------------------------------------------------------------------------- 1 | 2 | export const fetchVersions = (pkg: string) => { 3 | return useFetch( 4 | `https://data.jsdelivr.com/v1/package/npm/${pkg}`, { 5 | initialData: [], 6 | afterFetch: (ctx) => ((ctx.data = ctx.data.versions), ctx), 7 | }) 8 | .json().data 9 | } 10 | -------------------------------------------------------------------------------- /src/components/Header.vue: -------------------------------------------------------------------------------- 1 | 50 | 51 | 92 | -------------------------------------------------------------------------------- /src/const/code.ts: -------------------------------------------------------------------------------- 1 | 2 | export const setupDevui = 'devui.js' 3 | 4 | export const devuiCode = ` 5 | import { getCurrentInstance } from 'vue' 6 | import DevUI from 'vue-devui' 7 | import { ThemeServiceInit, infinityTheme } from 'devui-theme' 8 | 9 | const install = (app) => { 10 | app.use(DevUI) 11 | }; 12 | 13 | const loadIconCss = () => { 14 | const link = document.createElement('link') 15 | link.rel = 'stylesheet' 16 | link.href = 'https://unpkg.com/@devui-design/icons@latest/icomoon/devui-icon.css' 17 | document.body.appendChild(link) 18 | } 19 | 20 | const loadCss = () => { 21 | const link = document.createElement('link') 22 | link.rel = 'stylesheet' 23 | link.href = '#DEVUI_CSS_HREF#' 24 | document.body.appendChild(link) 25 | } 26 | 27 | ThemeServiceInit({ infinityTheme }, 'infinityTheme') 28 | 29 | export const setupDevui = () => { 30 | const instance = getCurrentInstance() 31 | instance.appContext.app.use({ install }) 32 | loadIconCss() 33 | loadCss() 34 | };` 35 | 36 | export const defaultFile = 'App.vue' 37 | 38 | export const defaultCode = ` 45 | 46 | ` 52 | -------------------------------------------------------------------------------- /src/const/imports.ts: -------------------------------------------------------------------------------- 1 | import { VersionRecord } from '@/types' 2 | 3 | export const genImportsMap = (versions: VersionRecord) => { 4 | const { Vue, DevUI } = versions 5 | 6 | return { 7 | 'devui-theme': { 8 | pkg: 'devui-theme', 9 | version: 'latest', 10 | file: '/index.es.js', 11 | }, 12 | 'vue-devui': { 13 | pkg: 'vue-devui', 14 | version: DevUI, 15 | file: '/vue-devui.es.js', 16 | }, 17 | vue: { 18 | pkg: 'vue', 19 | version: Vue, 20 | file: '/dist/vue.esm-browser.js', 21 | }, 22 | 'vue-router': { 23 | pkg: 'vue-router', 24 | version: 'latest', 25 | file: '/dist/vue-router.esm-browser.js', 26 | }, 27 | '@floating-ui/dom': { 28 | pkg: '@floating-ui/dom', 29 | version: 'latest', 30 | file: '/dist/floating-ui.dom.esm.js', 31 | }, 32 | '@floating-ui/core': { 33 | pkg: '@floating-ui/core', 34 | version: 'latest', 35 | file: '/dist/floating-ui.core.esm.js', 36 | }, 37 | '@vueuse/core': { 38 | pkg: '@vueuse/core', 39 | version: 'latest', 40 | file: '/index.mjs', 41 | }, 42 | '@vue/devtools-api': { 43 | pkg: '@vue/devtools-api', 44 | version: 'latest', 45 | file: '/lib/esm/index.js', 46 | }, 47 | '@vueuse/shared': { 48 | pkg: '@vueuse/shared', 49 | version: 'latest', 50 | file: '/index.mjs', 51 | }, 52 | 'vue-demi': { 53 | pkg: 'vue-demi', 54 | version: 'latest', 55 | file: '/lib/index.mjs', 56 | }, 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/const/index.ts: -------------------------------------------------------------------------------- 1 | 2 | export { devuiCode, defaultCode, setupDevui, defaultFile } from './code' 3 | 4 | export { genImportsMap } from './imports' 5 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import '@vue/repl/style.css' 3 | import 'uno.css' 4 | import '@idux/components/default.min.css' 5 | 6 | import { 7 | IDUX_ICON_DEPENDENCIES, 8 | addIconDefinitions, 9 | } from '@idux/components/icon' 10 | 11 | // 静态加载: `IDUX_ICON_DEPENDENCIES` 是 `@idux` 的部分组件默认所使用到图标,建议在此时静态引入。 12 | addIconDefinitions(IDUX_ICON_DEPENDENCIES) 13 | 14 | import App from './App.vue' 15 | 16 | createApp(App).mount('#app') 17 | -------------------------------------------------------------------------------- /src/repl-store/index.ts: -------------------------------------------------------------------------------- 1 | import { reactive, watchEffect } from 'vue' 2 | import { File, compileFile } from '@vue/repl' 3 | import type { OutputModes, SFCOptions, Store, StoreState } from '@vue/repl' 4 | import type { PendingCompiler, ReplStoreParam, VersionKey, VersionRecord } from '@/types' 5 | import { defaultCode, defaultFile, devuiCode, genImportsMap, setupDevui } from '@/const' 6 | import { decodeData, encodeData, genLink } from '@/utils' 7 | 8 | const getInitFiles = (serializedState = '') => { 9 | let files: StoreState['files'] = { 10 | [defaultFile]: new File(defaultFile, defaultCode) 11 | } 12 | if (serializedState) { 13 | try { 14 | files = {} 15 | const res = JSON.parse(decodeData(serializedState)) 16 | for (const filename of Object.keys(res)) { 17 | files[filename] = new File(filename, res[filename]) 18 | } 19 | } catch (err) { 20 | console.log(err) 21 | console.log('Json parse error: src/repl-store/index.ts') 22 | } 23 | } 24 | 25 | return files 26 | } 27 | 28 | const genVueLink = (version: string) => { 29 | const compilerSfc = genLink( 30 | '@vue/compiler-sfc', 31 | version, 32 | '/dist/compiler-sfc.esm-browser.js', 33 | ) 34 | const runtimeDom = genLink( 35 | '@vue/runtime-dom', 36 | version, 37 | '/dist/runtime-dom.esm-browser.js', 38 | ) 39 | 40 | return { 41 | compilerSfc, 42 | runtimeDom, 43 | } 44 | } 45 | 46 | const genImports = (versions: VersionRecord) => { 47 | const deps = { 48 | ...genImportsMap(versions), 49 | } 50 | 51 | return Object.fromEntries( 52 | Object.entries(deps).map(([key, info]) => [key, genLink(info.pkg, info.version, info.file)]) 53 | ) 54 | } 55 | 56 | export class ReplStore implements Store { 57 | state: StoreState 58 | compiler!: typeof import('vue/compiler-sfc') 59 | options?: SFCOptions 60 | versions: VersionRecord 61 | initialShowOutput = false 62 | initialOutputMode: OutputModes = 'preview' 63 | 64 | private pendingCompiler: PendingCompiler = null 65 | 66 | constructor({ 67 | serializedState = '', 68 | versions = { Vue: 'latest', DevUI: 'latest' }, 69 | }: ReplStoreParam) { 70 | const files = getInitFiles(serializedState) 71 | const mainFile = files[defaultFile] ? defaultFile : Object.keys(files)[0] 72 | this.state = reactive({ 73 | mainFile, 74 | files, 75 | activeFile: files[mainFile], 76 | errors: [], 77 | vueRuntimeURL: '', 78 | }) 79 | this.versions = versions 80 | this.initImportMap() 81 | } 82 | 83 | private initImportMap() { 84 | if (!this.state.files['import-map.json']) { 85 | this.state.files['import-map.json'] = new File( 86 | 'import-map.json', 87 | JSON.stringify({ imports: {} }, null, 2) 88 | ) 89 | } 90 | } 91 | 92 | async initStore() { 93 | await this.setVueVersion(this.versions.Vue) 94 | await this.setDevuiVersion(this.versions.DevUI) 95 | 96 | // this.state.files[setupDevui] = new File( 97 | // setupDevui, 98 | // devuiCode, 99 | // true 100 | // ) 101 | 102 | watchEffect(() => compileFile(this, this.state.activeFile)) 103 | 104 | for (const file of Object.keys(this.state.files)) { 105 | if (file !== defaultFile) { 106 | compileFile(this, this.state.files[file]) 107 | } 108 | } 109 | } 110 | 111 | setActive(filename: string) { 112 | this.state.activeFile = this.state.files[filename] 113 | } 114 | 115 | // 新增文件 116 | public addFile(fileOrFilename: string | File) { 117 | const file = typeof fileOrFilename === 'string' ? 118 | new File(fileOrFilename) : 119 | fileOrFilename 120 | this.state.files[file.filename] = file 121 | 122 | if (!file.hidden) this.setActive(file.filename) 123 | } 124 | 125 | // 删除文件 126 | public deleteFile(filename: string) { 127 | if (window?.confirm(`Confirm to delete ${filename}?`)) { 128 | if (this.state.activeFile.filename === filename) { 129 | this.state.activeFile = this.state.files[this.state.mainFile] 130 | } 131 | delete this.state.files[filename] 132 | } 133 | } 134 | 135 | public getFiles() { 136 | const exported: Record = {} 137 | for (const filename of Object.keys(this.state.files)) { 138 | exported[filename] = this.state.files[filename].code 139 | } 140 | return exported 141 | } 142 | 143 | async setFiles(newFiles: Record, mainFile = defaultFile) { 144 | const files: Record = {} 145 | if (mainFile === defaultFile && !newFiles[mainFile]) { 146 | files[mainFile] = new File(mainFile, defaultCode) 147 | } 148 | for (const [filename, file] of Object.entries(newFiles)) { 149 | files[filename] = new File(filename, file) 150 | } 151 | for (const file of Object.values(files)) { 152 | await compileFile(this, file) 153 | } 154 | this.state.mainFile = mainFile 155 | this.state.files = files 156 | this.initImportMap() 157 | this.setActive(mainFile) 158 | } 159 | 160 | private setImportMap(map: { imports: Record }) { 161 | try { 162 | this.state.files['import-map.json'].code = JSON.stringify(map, null, 2) 163 | } catch (e) { 164 | this.state.errors = [ 165 | `stringify error in import-map.json: ${(e as Error).message}`, 166 | ] 167 | } 168 | } 169 | 170 | serialize() { 171 | const arr = Object 172 | .entries(this.getFiles()) 173 | .filter(([file]) => file !== setupDevui) 174 | .map(([file, content]) => { 175 | if (file === 'import-map.json') { 176 | try { 177 | const importMap = JSON.stringify(this.getImportMap()) 178 | return [file, importMap] 179 | // eslint-disable-next-line no-empty 180 | } catch { } 181 | } 182 | return [file, content] 183 | }) 184 | const data = JSON.stringify(Object.fromEntries(arr)) 185 | return `#${encodeData(data)}` 186 | } 187 | 188 | getImportMap() { 189 | try { 190 | return JSON.parse(this.state.files['import-map.json'].code) 191 | } catch (e) { 192 | this.state.errors = [ 193 | `Syntax error in import-map.json: ${(e as Error).message}`, 194 | ] 195 | return {} 196 | } 197 | } 198 | 199 | private addDeps() { 200 | const importMap = this.getImportMap() 201 | importMap.imports = { 202 | ...importMap.imports, 203 | ...genImports(this.versions), 204 | } 205 | this.setImportMap(importMap) 206 | } 207 | 208 | public async setVersion(key: VersionKey, version: string) { 209 | switch (key) { 210 | case 'DevUI': 211 | await this.setDevuiVersion(version) 212 | compileFile(this, this.state.files[setupDevui]) 213 | break 214 | case 'Vue': 215 | await this.setVueVersion(version) 216 | break 217 | } 218 | } 219 | 220 | private async setDevuiVersion(version: string) { 221 | this.versions.DevUI = version 222 | 223 | const href = genLink('vue-devui', version, '/style.css') 224 | 225 | this.state.files[setupDevui] = new File( 226 | setupDevui, 227 | devuiCode.replace('#DEVUI_CSS_HREF#', href), 228 | true, 229 | ) 230 | 231 | this.addDeps() 232 | } 233 | 234 | private async setVueVersion(version: string) { 235 | const { compilerSfc, runtimeDom } = genVueLink(version) 236 | 237 | this.pendingCompiler = import(/* @vite-ignore */compilerSfc) 238 | this.compiler = await this.pendingCompiler 239 | this.pendingCompiler = null 240 | 241 | this.state.vueRuntimeURL = runtimeDom 242 | 243 | this.versions.Vue = version 244 | 245 | this.addDeps() 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /src/types/dts/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import type { DefineComponent } from 'vue' 3 | const component: DefineComponent 4 | export default component 5 | } 6 | -------------------------------------------------------------------------------- /src/types/index.ts: -------------------------------------------------------------------------------- 1 | 2 | export { 3 | ReplStoreParam, 4 | VersionKey, 5 | VersionRecord, 6 | PendingCompiler, 7 | } from './replStore' 8 | -------------------------------------------------------------------------------- /src/types/replStore.ts: -------------------------------------------------------------------------------- 1 | 2 | export interface ReplStoreParam { 3 | 4 | /** @default '' */ 5 | serializedState?: string 6 | versions?: VersionRecord 7 | } 8 | 9 | export type VersionKey = 'Vue' | 'DevUI' 10 | export type VersionRecord = Record 11 | 12 | export type PendingCompiler = Promise | null 13 | -------------------------------------------------------------------------------- /src/utils/download/index.ts: -------------------------------------------------------------------------------- 1 | import { saveAs } from 'file-saver' 2 | import indexHtml from './template/index.html?raw' 3 | import mainJs from './template/main.js?raw' 4 | import pkgJson from './template/package.json?raw' 5 | import viteCfg from './template/vite.config.js?raw' 6 | import readme from './template/Readme.md?raw' 7 | import type { ReplStore } from '@/repl-store' 8 | 9 | export const downloadProject = async (store: ReplStore) => { 10 | const excludes = ['devui.js', 'import-map.json'] 11 | const { default: JSZip } = await import('jszip') 12 | const jsZip = new JSZip() 13 | 14 | jsZip.file('index.html', indexHtml) 15 | jsZip.file('package.json', pkgJson) 16 | jsZip.file('vite.config.js', viteCfg) 17 | jsZip.file('README.md', readme) 18 | 19 | const srcFolder = jsZip.folder('src') 20 | srcFolder?.file('main.js', mainJs) 21 | 22 | const files = store.getFiles() 23 | for (const file in files) { 24 | if (excludes.includes(file)) { 25 | continue 26 | } 27 | let code = files[file] 28 | if (file === 'App.vue') { 29 | code = code 30 | .replace("import { setupDevui } from './devui.js'\n", '') 31 | .replace("// don't remove\n", '') 32 | .replace('setupDevui()\n', '') 33 | } 34 | srcFolder?.file(file, code) 35 | } 36 | 37 | const blob = await jsZip.generateAsync({ type: 'blob' }) 38 | saveAs(blob, 'vue-devui-starter.zip') 39 | } 40 | -------------------------------------------------------------------------------- /src/utils/download/template/Readme.md: -------------------------------------------------------------------------------- 1 | # Vue DevUI Starter 2 | 3 | This is a project template using [Vite](https://vitejs.dev/). It requires [Node.js](https://nodejs.org) v12+. 4 | 5 | To start: 6 | 7 | ```sh 8 | pnpm i 9 | pnpm run dev 10 | ``` 11 | -------------------------------------------------------------------------------- /src/utils/download/template/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vue DevUI Starter 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/utils/download/template/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import DevUI from 'vue-devui' 3 | import 'vue-devui/style.css' 4 | 5 | import App from './App.vue' 6 | 7 | createApp(App).use(DevUI).mount('#app') 8 | -------------------------------------------------------------------------------- /src/utils/download/template/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "devui-starter", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "vite build", 7 | "serve": "vite preview" 8 | }, 9 | "dependencies": { 10 | "vue": "^3.2.0", 11 | "vue-devui": "latest" 12 | }, 13 | "devDependencies": { 14 | "@vitejs/plugin-vue": "^2.1.0", 15 | "@vue/compiler-sfc": "^3.2.0", 16 | "vite": "^2.7.13" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/utils/download/template/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import vue from '@vitejs/plugin-vue' 3 | 4 | export default defineConfig({ 5 | plugins: [vue()], 6 | }) 7 | -------------------------------------------------------------------------------- /src/utils/encode.ts: -------------------------------------------------------------------------------- 1 | 2 | export const encodeData = (data: string): string => { 3 | return btoa(unescape(encodeURIComponent(data))) 4 | } 5 | 6 | export const decodeData = (base64: string): string => { 7 | return decodeURIComponent(escape(atob(base64))) 8 | } 9 | -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | 2 | export { decodeData, encodeData } from './encode' 3 | 4 | export { genLink, getVueVersions, getDevuiVersions } from './version' 5 | 6 | export { downloadProject } from './download' 7 | -------------------------------------------------------------------------------- /src/utils/version.ts: -------------------------------------------------------------------------------- 1 | 2 | import { fetchVersions } from '@/api' 3 | import { compare } from 'compare-versions' 4 | 5 | export const genLink = (pkg: string, version?: string, file = '') => { 6 | const ver = version ? `@${version}` : '' 7 | return `https://unpkg.com/${pkg}${ver}${file}` 8 | } 9 | 10 | export const getVueVersions = () => { 11 | const versions = fetchVersions('vue') 12 | return computed(() => versions.value?.filter(ver => compare(ver, '3.2.34', '>='))) 13 | } 14 | 15 | export const getDevuiVersions = () => { 16 | const versions = fetchVersions('vue-devui') 17 | return computed(() => versions.value?.filter(ver => compare(ver, '1.0.0-rc.5', '>='))) 18 | } 19 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "lib": ["ESNext", "DOM"], 5 | "module": "ESNext", 6 | "rootDir": ".", 7 | "baseUrl": ".", 8 | "moduleResolution": "node", 9 | "resolveJsonModule": true, 10 | "outDir": "dist", 11 | "esModuleInterop": true, 12 | "sourceMap": true, 13 | "forceConsistentCasingInFileNames": true, 14 | "strict": true, 15 | "noUnusedLocals": true, 16 | "skipLibCheck": true, 17 | "types": ["vite/client", "node"], 18 | "paths": { 19 | "@/*": ["src/*"] 20 | } 21 | }, 22 | "include": ["src/**/*.ts", "src/**/*.vue"], 23 | "exclude": ["node_modules", "dist"] 24 | } 25 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import { defineConfig } from 'vite' 3 | import Unocss from 'unocss/vite' 4 | import { presetUno } from 'unocss' 5 | import vue from '@vitejs/plugin-vue' 6 | import Components from 'unplugin-vue-components/vite' 7 | import { IduxResolver } from 'unplugin-vue-components/resolvers' 8 | import AutoImport from 'unplugin-auto-import/vite' 9 | import Inspect from 'vite-plugin-inspect' 10 | 11 | const pathSrc = path.resolve(__dirname, 'src') 12 | 13 | export default defineConfig(async () => { 14 | 15 | return { 16 | base: './', 17 | resolve: { 18 | alias: { 19 | '@': pathSrc, 20 | }, 21 | }, 22 | server: { 23 | port: 2022, 24 | }, 25 | plugins: [ 26 | vue({ 27 | reactivityTransform: `${pathSrc}/**/*`, 28 | }), 29 | AutoImport({ 30 | imports: ['vue', '@vueuse/core'], 31 | resolvers: [IduxResolver()], 32 | dts: path.resolve(`${pathSrc}/types/dts`, 'auto-imports.d.ts'), 33 | }), 34 | Components({ 35 | resolvers: [IduxResolver()], 36 | dts: path.resolve(`${pathSrc}/types/dts`, 'components.d.ts'), 37 | }), 38 | Unocss({ 39 | presets: [presetUno()], 40 | }), 41 | Inspect(), 42 | ], 43 | } 44 | }) 45 | --------------------------------------------------------------------------------