@gx-design-vue/create-gx-cli 0.1.10 → 0.1.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (179) hide show
  1. package/package.json +2 -1
  2. package/template-gx-design-thin/.editorconfig +19 -0
  3. package/template-gx-design-thin/.env +4 -0
  4. package/template-gx-design-thin/.env.development +15 -0
  5. package/template-gx-design-thin/.env.production +28 -0
  6. package/template-gx-design-thin/.eslintignore +16 -0
  7. package/template-gx-design-thin/.prettierignore +9 -0
  8. package/template-gx-design-thin/LICENSE +21 -0
  9. package/template-gx-design-thin/README.md +112 -0
  10. package/template-gx-design-thin/build/constant.ts +1 -0
  11. package/template-gx-design-thin/build/generate/generateModifyVars.ts +14 -0
  12. package/template-gx-design-thin/build/rollupOptions/index.ts +22 -0
  13. package/template-gx-design-thin/build/util/hash.ts +17 -0
  14. package/template-gx-design-thin/build/util/index.ts +131 -0
  15. package/template-gx-design-thin/build/vite/cdn.ts +63 -0
  16. package/template-gx-design-thin/build/vite/optimizer.ts +26 -0
  17. package/template-gx-design-thin/build/vite/plugin/appConfig.ts +91 -0
  18. package/template-gx-design-thin/build/vite/plugin/autoImport.ts +26 -0
  19. package/template-gx-design-thin/build/vite/plugin/compress.ts +31 -0
  20. package/template-gx-design-thin/build/vite/plugin/html.ts +39 -0
  21. package/template-gx-design-thin/build/vite/plugin/index.ts +75 -0
  22. package/template-gx-design-thin/build/vite/plugin/mock.ts +14 -0
  23. package/template-gx-design-thin/build/vite/plugin/pwa.ts +31 -0
  24. package/template-gx-design-thin/build/vite/plugin/visualizer.ts +14 -0
  25. package/template-gx-design-thin/build/vite/plugin/viteMock/client.ts +88 -0
  26. package/template-gx-design-thin/build/vite/plugin/viteMock/createMockServer.ts +271 -0
  27. package/template-gx-design-thin/build/vite/plugin/viteMock/index.ts +69 -0
  28. package/template-gx-design-thin/build/vite/plugin/viteMock/types.ts +48 -0
  29. package/template-gx-design-thin/build/vite/plugin/viteMock/utils.ts +48 -0
  30. package/template-gx-design-thin/build/vite/plugin/viteNotice.ts +40 -0
  31. package/template-gx-design-thin/commitlint.config.cjs +32 -0
  32. package/template-gx-design-thin/config/default/defaultSettings.ts +78 -0
  33. package/template-gx-design-thin/config/default/network.ts +10 -0
  34. package/template-gx-design-thin/config/default/proxy.ts +47 -0
  35. package/template-gx-design-thin/config/default/theme.ts +3 -0
  36. package/template-gx-design-thin/config/index.ts +11 -0
  37. package/template-gx-design-thin/eslint.config.js +51 -0
  38. package/template-gx-design-thin/index.html +42 -0
  39. package/template-gx-design-thin/mock/_createProductionServer.ts +19 -0
  40. package/template-gx-design-thin/mock/_util.ts +33 -0
  41. package/template-gx-design-thin/mock/config/menu.ts +21 -0
  42. package/template-gx-design-thin/mock/config/user.ts +123 -0
  43. package/template-gx-design-thin/mock/datasSource/system/menu.ts +10 -0
  44. package/template-gx-design-thin/mock/datasSource/user/account.ts +30 -0
  45. package/template-gx-design-thin/mock/datasSource/user/index.ts +47 -0
  46. package/template-gx-design-thin/mock/util/crypto.ts +23 -0
  47. package/template-gx-design-thin/mock/util/table.ts +92 -0
  48. package/template-gx-design-thin/mock/util/utils.ts +73 -0
  49. package/template-gx-design-thin/package.json +129 -0
  50. package/template-gx-design-thin/pnpm-lock.yaml +12575 -0
  51. package/template-gx-design-thin/prettier.config.cjs +18 -0
  52. package/template-gx-design-thin/public/resource/css/index.css +119 -0
  53. package/template-gx-design-thin/public/resource/css/normalize.css +396 -0
  54. package/template-gx-design-thin/public/resource/img/favicon.ico +0 -0
  55. package/template-gx-design-thin/public/resource/img/logo.png +0 -0
  56. package/template-gx-design-thin/public/resource/img/pro_icon.svg +1 -0
  57. package/template-gx-design-thin/public/resource/img/pwa-192x192.png +0 -0
  58. package/template-gx-design-thin/public/resource/img/pwa-512x512.png +0 -0
  59. package/template-gx-design-thin/src/App.vue +42 -0
  60. package/template-gx-design-thin/src/assets/error_images/403.png +0 -0
  61. package/template-gx-design-thin/src/assets/error_images/404.png +0 -0
  62. package/template-gx-design-thin/src/assets/error_images/cloud.png +0 -0
  63. package/template-gx-design-thin/src/assets/login_images/login_background.svg +1 -0
  64. package/template-gx-design-thin/src/assets/logo.png +0 -0
  65. package/template-gx-design-thin/src/assets/menu_font/iconfont.css +94 -0
  66. package/template-gx-design-thin/src/assets/menu_font/iconfont.eot +0 -0
  67. package/template-gx-design-thin/src/assets/menu_font/iconfont.js +1 -0
  68. package/template-gx-design-thin/src/assets/menu_font/iconfont.json +142 -0
  69. package/template-gx-design-thin/src/assets/menu_font/iconfont.svg +1 -0
  70. package/template-gx-design-thin/src/assets/menu_font/iconfont.ttf +0 -0
  71. package/template-gx-design-thin/src/assets/menu_font/iconfont.woff +0 -0
  72. package/template-gx-design-thin/src/assets/menu_font/iconfont.woff2 +0 -0
  73. package/template-gx-design-thin/src/assets/menu_font/index.less +94 -0
  74. package/template-gx-design-thin/src/assets/public_icon/iconfont.css +42 -0
  75. package/template-gx-design-thin/src/assets/public_icon/iconfont.eot +0 -0
  76. package/template-gx-design-thin/src/assets/public_icon/iconfont.js +1 -0
  77. package/template-gx-design-thin/src/assets/public_icon/iconfont.json +51 -0
  78. package/template-gx-design-thin/src/assets/public_icon/iconfont.svg +1 -0
  79. package/template-gx-design-thin/src/assets/public_icon/iconfont.ttf +0 -0
  80. package/template-gx-design-thin/src/assets/public_icon/iconfont.woff +0 -0
  81. package/template-gx-design-thin/src/assets/public_icon/iconfont.woff2 +0 -0
  82. package/template-gx-design-thin/src/assets/public_icon/index.less +42 -0
  83. package/template-gx-design-thin/src/assets/public_images/nodata.svg +1 -0
  84. package/template-gx-design-thin/src/common/global.ts +4 -0
  85. package/template-gx-design-thin/src/components/GDesign/Result/index.tsx +144 -0
  86. package/template-gx-design-thin/src/components/GDesign/Result/style.less +140 -0
  87. package/template-gx-design-thin/src/components/GDesign/utils/index.ts +7 -0
  88. package/template-gx-design-thin/src/components/GlobalLayout/Confirm/index.ts +21 -0
  89. package/template-gx-design-thin/src/components/GlobalLayout/Empty/index.vue +18 -0
  90. package/template-gx-design-thin/src/components/GlobalLayout/RightContent/index.tsx +126 -0
  91. package/template-gx-design-thin/src/components/GlobalLayout/RightContent/style.ts +77 -0
  92. package/template-gx-design-thin/src/components/GlobalLayout/Spin/index.tsx +30 -0
  93. package/template-gx-design-thin/src/components/PageLoading/index.tsx +51 -0
  94. package/template-gx-design-thin/src/components/index.ts +6 -0
  95. package/template-gx-design-thin/src/core/ant-design/index.ts +10 -0
  96. package/template-gx-design-thin/src/core/gx-admin-design/index.ts +6 -0
  97. package/template-gx-design-thin/src/core/gx-design/index.ts +6 -0
  98. package/template-gx-design-thin/src/core/gx-pro-design/index.ts +8 -0
  99. package/template-gx-design-thin/src/core/index.ts +84 -0
  100. package/template-gx-design-thin/src/design/ant-design/index.less +4 -0
  101. package/template-gx-design-thin/src/design/ant-design/layout.less +22 -0
  102. package/template-gx-design-thin/src/design/ant-design/menu.less +48 -0
  103. package/template-gx-design-thin/src/design/ant-design/spin.less +23 -0
  104. package/template-gx-design-thin/src/design/ant-design/tooltip.less +7 -0
  105. package/template-gx-design-thin/src/design/color.less +1 -0
  106. package/template-gx-design-thin/src/design/config.less +5 -0
  107. package/template-gx-design-thin/src/design/imageEditor.less +180 -0
  108. package/template-gx-design-thin/src/design/index.less +95 -0
  109. package/template-gx-design-thin/src/design/mixin.less +65 -0
  110. package/template-gx-design-thin/src/design/normalize.less +391 -0
  111. package/template-gx-design-thin/src/design/root.less +3 -0
  112. package/template-gx-design-thin/src/hooks/core/index.ts +3 -0
  113. package/template-gx-design-thin/src/hooks/core/useRequest/index.ts +118 -0
  114. package/template-gx-design-thin/src/hooks/event/index.ts +3 -0
  115. package/template-gx-design-thin/src/hooks/event/useClipboard.ts +15 -0
  116. package/template-gx-design-thin/src/hooks/web/index.ts +5 -0
  117. package/template-gx-design-thin/src/hooks/web/useThemeStyle.ts +16 -0
  118. package/template-gx-design-thin/src/layout/BasicLayout.vue +123 -0
  119. package/template-gx-design-thin/src/layout/BlankLayout.vue +5 -0
  120. package/template-gx-design-thin/src/layout/ContentView.vue +50 -0
  121. package/template-gx-design-thin/src/layout/IframeView.vue +1 -0
  122. package/template-gx-design-thin/src/layout/UserLayout.vue +7 -0
  123. package/template-gx-design-thin/src/main.ts +34 -0
  124. package/template-gx-design-thin/src/plugins/index.ts +2 -0
  125. package/template-gx-design-thin/src/router/guard/index.ts +83 -0
  126. package/template-gx-design-thin/src/router/guard/permissions.ts +70 -0
  127. package/template-gx-design-thin/src/router/guard/stateGuard.ts +10 -0
  128. package/template-gx-design-thin/src/router/helper/routeHelper.ts +223 -0
  129. package/template-gx-design-thin/src/router/helper/utils.ts +19 -0
  130. package/template-gx-design-thin/src/router/index.ts +31 -0
  131. package/template-gx-design-thin/src/router/routes/index.ts +86 -0
  132. package/template-gx-design-thin/src/router/routes/modules/dataSource.ts +12 -0
  133. package/template-gx-design-thin/src/services/common/index.ts +11 -0
  134. package/template-gx-design-thin/src/services/systemCenter/index.ts +1 -0
  135. package/template-gx-design-thin/src/services/systemCenter/menu.ts +9 -0
  136. package/template-gx-design-thin/src/services/userCenter/account.ts +42 -0
  137. package/template-gx-design-thin/src/services/userCenter/index.ts +28 -0
  138. package/template-gx-design-thin/src/store/index.ts +32 -0
  139. package/template-gx-design-thin/src/store/modules/dict.ts +28 -0
  140. package/template-gx-design-thin/src/store/modules/global.ts +42 -0
  141. package/template-gx-design-thin/src/store/modules/permission.ts +19 -0
  142. package/template-gx-design-thin/src/store/modules/routes.ts +113 -0
  143. package/template-gx-design-thin/src/store/modules/tabsRouter.ts +76 -0
  144. package/template-gx-design-thin/src/store/modules/user.ts +136 -0
  145. package/template-gx-design-thin/src/utils/accessToken.ts +80 -0
  146. package/template-gx-design-thin/src/utils/crypto/base64.ts +101 -0
  147. package/template-gx-design-thin/src/utils/crypto/index.ts +57 -0
  148. package/template-gx-design-thin/src/utils/env.ts +50 -0
  149. package/template-gx-design-thin/src/utils/fetchFile.ts +81 -0
  150. package/template-gx-design-thin/src/utils/index.ts +123 -0
  151. package/template-gx-design-thin/src/utils/pageTitle.ts +20 -0
  152. package/template-gx-design-thin/src/utils/request/XHR.ts +139 -0
  153. package/template-gx-design-thin/src/utils/request/axiosCancel.ts +69 -0
  154. package/template-gx-design-thin/src/utils/request/checkStatus.ts +25 -0
  155. package/template-gx-design-thin/src/utils/request/index.ts +144 -0
  156. package/template-gx-design-thin/src/utils/request/typings.ts +171 -0
  157. package/template-gx-design-thin/src/utils/storage.ts +199 -0
  158. package/template-gx-design-thin/src/utils/uploadFile.ts +27 -0
  159. package/template-gx-design-thin/src/utils/util.ts +198 -0
  160. package/template-gx-design-thin/src/utils/validate.ts +216 -0
  161. package/template-gx-design-thin/src/views/Iframe/index.vue +76 -0
  162. package/template-gx-design-thin/src/views/Page/one.vue +13 -0
  163. package/template-gx-design-thin/src/views/Page/two.vue +13 -0
  164. package/template-gx-design-thin/src/views/exception/403/index.vue +7 -0
  165. package/template-gx-design-thin/src/views/exception/404/index.vue +9 -0
  166. package/template-gx-design-thin/src/views/user/login/index.vue +109 -0
  167. package/template-gx-design-thin/src/views/user/login/style.less +38 -0
  168. package/template-gx-design-thin/tsconfig.json +46 -0
  169. package/template-gx-design-thin/types/ant-design-import.d.ts +99 -0
  170. package/template-gx-design-thin/types/auto-imports.d.ts +81 -0
  171. package/template-gx-design-thin/types/components.d.ts +24 -0
  172. package/template-gx-design-thin/types/config.d.ts +44 -0
  173. package/template-gx-design-thin/types/global.d.ts +97 -0
  174. package/template-gx-design-thin/types/mock.d.ts +16 -0
  175. package/template-gx-design-thin/types/module.d.ts +20 -0
  176. package/template-gx-design-thin/types/response.d.ts +15 -0
  177. package/template-gx-design-thin/unocss.config.ts +101 -0
  178. package/template-gx-design-thin/vite.config.ts +120 -0
  179. package/template-gx-design-thin/yarn.lock +9492 -0
@@ -0,0 +1,39 @@
1
+ import type { PluginOption } from 'vite'
2
+ import { createHtmlPlugin } from 'vite-plugin-html'
3
+
4
+ import cdnModules from '../cdn'
5
+ import { GLOB_CONFIG_FILE_NAME } from '../../constant'
6
+ import { defaultSettings } from '../../../config'
7
+ import pkg from '../../../package.json'
8
+
9
+ const { title, publicPath, useCdn } = defaultSettings
10
+
11
+ export function configHtmlPlugin(_: ViteEnv, isBuild: boolean) {
12
+ const path = publicPath.endsWith('/') ? publicPath : `${publicPath}/`
13
+
14
+ const getAppConfigSrc = () => {
15
+ return `${path || '/'}${GLOB_CONFIG_FILE_NAME}?v=${pkg.version}-${new Date().getTime()}`
16
+ }
17
+
18
+ const htmlPlugin: PluginOption[] = createHtmlPlugin({
19
+ minify: isBuild,
20
+ inject: {
21
+ // Inject data into ejs template
22
+ data: {
23
+ VUE_APP_TITLE: title,
24
+ injectScript: useCdn && isBuild ? cdnModules.map(e => e.js).filter(el => el) : [],
25
+ injectLink: useCdn && isBuild ? cdnModules.map(e => e.css).filter(el => el) : []
26
+ },
27
+ // Embed the generated app.config.js file
28
+ tags: isBuild
29
+ ? [
30
+ {
31
+ tag: 'script',
32
+ attrs: { src: getAppConfigSrc() }
33
+ }
34
+ ]
35
+ : []
36
+ }
37
+ })
38
+ return htmlPlugin
39
+ }
@@ -0,0 +1,75 @@
1
+ import type { PluginOption } from 'vite'
2
+
3
+ import vue from '@vitejs/plugin-vue'
4
+ import vueJsx from '@vitejs/plugin-vue-jsx'
5
+ import VueDevTools from 'vite-plugin-vue-devtools'
6
+ import legacy from '@vitejs/plugin-legacy'
7
+
8
+ import vueSetupExtend from 'vite-plugin-vue-setup-extend'
9
+
10
+ import Unocss from 'unocss/vite'
11
+
12
+ import { configPwaConfig } from './pwa'
13
+ import { configHtmlPlugin } from './html'
14
+ import { configMockPlugin } from './mock'
15
+ import viteNotice from './viteNotice'
16
+ import { createAutoImport } from './autoImport'
17
+ import { configCompressPlugin } from './compress'
18
+ import { createAppConfigPlugin } from './appConfig'
19
+ import { configVisualizerConfig } from './visualizer'
20
+
21
+ export async function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
22
+ const {
23
+ VITE_APP_ENV,
24
+ VITE_USE_MOCK,
25
+ VITE_LEGACY,
26
+ VITE_BUILD_COMPRESS,
27
+ VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE
28
+ } = viteEnv
29
+
30
+ const isDev = VITE_APP_ENV === 'dev'
31
+
32
+ const vitePlugins: PluginOption[] = [
33
+ // have to
34
+ vue(),
35
+ // have to
36
+ vueJsx()
37
+ ]
38
+
39
+ // vite-plugin-app-info
40
+ vitePlugins.push(await createAppConfigPlugin({ isBuild }))
41
+ // vite-plugin-vue-devtools
42
+ vitePlugins.push(VueDevTools() as any)
43
+ // vite-plugin-windicss
44
+ vitePlugins.push(Unocss())
45
+ // vite-plugin-vue-setup-extend
46
+ vitePlugins.push(vueSetupExtend())
47
+ // @vitejs/plugin-legacy
48
+ VITE_LEGACY && isBuild && vitePlugins.push(legacy())
49
+ // vite-plugin-html
50
+ vitePlugins.push(configHtmlPlugin(viteEnv, isBuild))
51
+ // vite-plugin-build-info
52
+ vitePlugins.push(await viteNotice())
53
+ // vite-plugin-mock
54
+ const useMock = isDev || VITE_USE_MOCK
55
+ useMock && vitePlugins.push(configMockPlugin())
56
+
57
+ // rollup-plugin-visualizer
58
+ vitePlugins.push(configVisualizerConfig())
59
+
60
+ // unplugin-auto-import/vite
61
+ vitePlugins.push(createAutoImport())
62
+
63
+ // The following plugins only work in the production environment
64
+ if (isBuild) {
65
+ // rollup-plugin-gzip
66
+ vitePlugins.push(
67
+ configCompressPlugin(VITE_BUILD_COMPRESS, VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE)
68
+ )
69
+
70
+ // vite-plugin-pwa
71
+ vitePlugins.push(configPwaConfig(viteEnv))
72
+ }
73
+
74
+ return vitePlugins
75
+ }
@@ -0,0 +1,14 @@
1
+ import { viteMockServe } from './viteMock'
2
+
3
+ export function configMockPlugin() {
4
+ return viteMockServe({
5
+ ignore: /^_/,
6
+ mockPath: 'mock',
7
+ enable: true,
8
+ injectCode: `
9
+ import { setupProdMockServer } from '../mock/_createProductionServer';
10
+
11
+ setupProdMockServer();
12
+ `,
13
+ })
14
+ }
@@ -0,0 +1,31 @@
1
+ import { VitePWA } from 'vite-plugin-pwa'
2
+
3
+ import { defaultSettings } from '../../../config'
4
+
5
+ export function configPwaConfig(env: ViteEnv) {
6
+ const { title, shortName } = defaultSettings
7
+ const { VITE_USE_PWA } = env
8
+
9
+ if (VITE_USE_PWA) {
10
+ const pwaPlugin = VitePWA({
11
+ manifest: {
12
+ name: title || 'GX Pro Admin',
13
+ short_name: shortName || 'gx_pro_admin',
14
+ icons: [
15
+ {
16
+ src: './resource/img/pwa-192x192.png',
17
+ sizes: '192x192',
18
+ type: 'image/png'
19
+ },
20
+ {
21
+ src: './resource/img/pwa-512x512.png',
22
+ sizes: '512x512',
23
+ type: 'image/png'
24
+ }
25
+ ]
26
+ }
27
+ })
28
+ return pwaPlugin
29
+ }
30
+ return []
31
+ }
@@ -0,0 +1,14 @@
1
+ import { visualizer } from 'rollup-plugin-visualizer'
2
+ import { isReportMode } from '../../util'
3
+
4
+ export function configVisualizerConfig() {
5
+ if (isReportMode()) {
6
+ return visualizer({
7
+ filename: './node_modules/.cache/visualizer/stats.html',
8
+ open: true,
9
+ gzipSize: true,
10
+ brotliSize: true
11
+ }) as Plugin
12
+ }
13
+ return []
14
+ }
@@ -0,0 +1,88 @@
1
+ import mockJs from 'mockjs'
2
+ import { pathToRegexp } from 'path-to-regexp'
3
+
4
+ const Mock = mockJs as any
5
+
6
+ export function createProdMockServer(mockList: any[]) {
7
+ Mock.XHR.prototype.__send = Mock.XHR.prototype.send
8
+ Mock.XHR.prototype.send = function () {
9
+ if (this.custom.xhr) {
10
+ this.custom.xhr.withCredentials = this.withCredentials || false
11
+
12
+ if (this.responseType) {
13
+ this.custom.xhr.responseType = this.responseType
14
+ }
15
+ }
16
+ if (this.custom.requestHeaders) {
17
+ const headers: any = {}
18
+ for (let k in this.custom.requestHeaders) {
19
+ headers[k.toString().toLowerCase()] = this.custom.requestHeaders[k]
20
+ }
21
+ this.custom.options = Object.assign({}, this.custom.options, { headers })
22
+ }
23
+ this.__send.apply(this, arguments)
24
+ }
25
+
26
+ Mock.XHR.prototype.proxy_open = Mock.XHR.prototype.open
27
+
28
+ Mock.XHR.prototype.open = function () {
29
+ let responseType = this.responseType
30
+ this.proxy_open(...arguments)
31
+ if (this.custom.xhr) {
32
+ if (responseType) {
33
+ this.custom.xhr.responseType = responseType
34
+ }
35
+ }
36
+ }
37
+
38
+ for (const { url, method, response, timeout } of mockList) {
39
+ __setupMock__(timeout)
40
+ Mock.mock(
41
+ pathToRegexp(url, undefined, { end: false }),
42
+ method || 'get',
43
+ __XHR2ExpressReqWrapper__(response)
44
+ )
45
+ }
46
+ }
47
+
48
+ function __param2Obj__(url: string) {
49
+ const search = url.split('?')[1]
50
+ if (!search) {
51
+ return {}
52
+ }
53
+ return JSON.parse(
54
+ '{"' +
55
+ decodeURIComponent(search)
56
+ .replace(/"/g, '\\"')
57
+ .replace(/&/g, '","')
58
+ .replace(/=/g, '":"')
59
+ .replace(/\+/g, ' ') +
60
+ '"}'
61
+ )
62
+ }
63
+
64
+ function __XHR2ExpressReqWrapper__(handle: (d: any) => any) {
65
+ return function (options: any) {
66
+ let result = null
67
+ if (typeof handle === 'function') {
68
+ const { body, type, url, headers } = options
69
+ result = handle({
70
+ method: type,
71
+ body: JSON.parse(body),
72
+ query: __param2Obj__(url),
73
+ headers
74
+ })
75
+ } else {
76
+ result = handle
77
+ }
78
+
79
+ return Mock.mock(result)
80
+ }
81
+ }
82
+
83
+ function __setupMock__(timeout = 0) {
84
+ timeout &&
85
+ Mock.setup({
86
+ timeout
87
+ })
88
+ }
@@ -0,0 +1,271 @@
1
+ import type { MockMethod, Recordable, RespThisType, ViteMockOptions } from './types'
2
+ import path from 'node:path'
3
+ import fs from 'node:fs'
4
+ import chokidar from 'chokidar'
5
+ import colors from 'picocolors'
6
+ import url from 'node:url'
7
+ import fg from 'fast-glob'
8
+ import Mock from 'mockjs'
9
+ import { match, pathToRegexp } from 'path-to-regexp'
10
+ import { isAbsPath, isArray, isFunction, isRegExp, sleep } from './utils'
11
+ import type { IncomingMessage, NextHandleFunction } from 'connect'
12
+ import type { GetOutputFile } from 'bundle-require'
13
+ import { bundleRequire, JS_EXT_RE } from 'bundle-require'
14
+ import type { ResolvedConfig } from 'vite'
15
+
16
+ export let mockData: MockMethod[] = []
17
+
18
+ export async function createMockServer(
19
+ opt: ViteMockOptions = { mockPath: 'mock', configPath: 'vite.mock.config' },
20
+ config: ResolvedConfig,
21
+ ) {
22
+ opt = {
23
+ mockPath: 'mock',
24
+ watchFiles: true,
25
+ configPath: 'vite.mock.config.ts',
26
+ logger: true,
27
+ cors: true,
28
+ ...opt,
29
+ }
30
+
31
+ if (mockData.length > 0)
32
+ return
33
+ mockData = await getMockConfig(opt, config)
34
+ await createWatch(opt, config)
35
+ }
36
+
37
+ // request match
38
+ export async function requestMiddleware(opt: ViteMockOptions) {
39
+ const { logger = true } = opt
40
+ const middleware: NextHandleFunction = async (req, res, next) => {
41
+ let queryParams: {
42
+ query?: {
43
+ [key: string]: any
44
+ }
45
+ pathname?: string | null
46
+ } = {}
47
+
48
+ if (req.url) {
49
+ queryParams = url.parse(req.url, true)
50
+ }
51
+
52
+ const reqUrl = queryParams.pathname
53
+
54
+ const matchRequest = mockData.find((item) => {
55
+ if (!reqUrl || !item || !item.url) {
56
+ return false
57
+ }
58
+ if (item.method && item.method.toUpperCase() !== req.method) {
59
+ return false
60
+ }
61
+ return pathToRegexp(item.url).test(reqUrl)
62
+ })
63
+
64
+ if (matchRequest) {
65
+ const isGet = req.method && req.method.toUpperCase() === 'GET'
66
+ const { response, rawResponse, timeout, statusCode, url } = matchRequest
67
+
68
+ if (timeout) {
69
+ await sleep(timeout)
70
+ }
71
+
72
+ const urlMatch = match(url, { decode: decodeURIComponent })
73
+
74
+ let query = queryParams.query as any
75
+ if (reqUrl) {
76
+ if ((isGet && JSON.stringify(query) === '{}') || !isGet) {
77
+ const params = (urlMatch(reqUrl) as any).params
78
+ if (JSON.stringify(params) !== '{}') {
79
+ query = (urlMatch(reqUrl) as any).params || {}
80
+ } else {
81
+ query = queryParams.query || {}
82
+ }
83
+ }
84
+ }
85
+
86
+ const self: RespThisType = { req, res, parseJson: parseJson.bind(null, req) }
87
+ if (isFunction(rawResponse)) {
88
+ await rawResponse.bind(self)(req, res)
89
+ } else {
90
+ const body = await parseJson(req)
91
+ res.setHeader('Content-Type', 'application/json')
92
+ if (opt) {
93
+ res.setHeader('Access-Control-Allow-Credentials', true)
94
+ res.setHeader('Access-Control-Allow-Origin', req.headers.origin || '*')
95
+ }
96
+ res.statusCode = statusCode || 200
97
+ const mockResponse = isFunction(response)
98
+ ? response.bind(self)({ url: req.url as any, body, query, headers: req.headers })
99
+ : response
100
+ res.end(JSON.stringify(Mock.mock(mockResponse)))
101
+ }
102
+
103
+ logger && loggerOutput('request invoke', req.url!)
104
+ return
105
+ }
106
+ next()
107
+ }
108
+ return middleware
109
+ }
110
+
111
+ // create watch mock
112
+ function createWatch(opt: ViteMockOptions, config: ResolvedConfig) {
113
+ const { configPath, logger, watchFiles } = opt
114
+
115
+ if (!watchFiles) {
116
+ return
117
+ }
118
+
119
+ const { absConfigPath, absMockPath } = getPath(opt)
120
+
121
+ if (process.env.VITE_DISABLED_WATCH_MOCK === 'true') {
122
+ return
123
+ }
124
+
125
+ const watchDir = []
126
+ const exitsConfigPath = fs.existsSync(absConfigPath)
127
+
128
+ exitsConfigPath && configPath ? watchDir.push(absConfigPath) : watchDir.push(absMockPath)
129
+
130
+ const watcher = chokidar.watch(watchDir, {
131
+ ignoreInitial: true,
132
+ // ignore files generated by `bundle require`
133
+ ignored: '**/_*.bundled_*.(mjs|cjs)',
134
+ })
135
+
136
+ watcher.on('all', async (event, file) => {
137
+ logger && loggerOutput(`mock file ${event}`, file)
138
+ mockData = await getMockConfig(opt, config)
139
+ })
140
+ }
141
+
142
+ // clear cache
143
+ // function cleanRequireCache(opt: ViteMockOptions) {
144
+ // if (typeof require === 'undefined' || !require.cache) {
145
+ // return
146
+ // }
147
+ // const { absConfigPath, absMockPath } = getPath(opt)
148
+ // Object.keys(require.cache).forEach((file) => {
149
+ // if (file === absConfigPath || file.indexOf(absMockPath) > -1) {
150
+ // delete require.cache[file]
151
+ // }
152
+ // })
153
+ // }
154
+
155
+ function parseJson(req: IncomingMessage): Promise<Recordable> {
156
+ return new Promise((resolve) => {
157
+ let body = ''
158
+ let jsonStr = ''
159
+ req.on('data', function (chunk) {
160
+ body += chunk
161
+ })
162
+ req.on('end', function () {
163
+ try {
164
+ jsonStr = JSON.parse(body)
165
+ } catch (err) {
166
+ jsonStr = ''
167
+ }
168
+ resolve(jsonStr as any)
169
+ return
170
+ })
171
+ })
172
+ }
173
+
174
+ // load mock .ts files and watch
175
+ async function getMockConfig(opt: ViteMockOptions, config: ResolvedConfig) {
176
+ const { absConfigPath, absMockPath } = getPath(opt)
177
+ const { ignore, configPath, logger } = opt
178
+
179
+ let ret: MockMethod[] = []
180
+ if (configPath && fs.existsSync(absConfigPath)) {
181
+ logger && loggerOutput(`load mock data from`, absConfigPath)
182
+ ret = await resolveModule(absConfigPath, config)
183
+ return ret
184
+ }
185
+
186
+ const mockFiles = fg
187
+ .sync(`**/*.{ts,mjs,js}`, {
188
+ cwd: absMockPath,
189
+ })
190
+ .filter((item) => {
191
+ if (!ignore) {
192
+ return true
193
+ }
194
+ if (isFunction(ignore)) {
195
+ return !ignore(item)
196
+ }
197
+ if (isRegExp(ignore)) {
198
+ return !ignore.test(path.basename(item))
199
+ }
200
+ return true
201
+ })
202
+ try {
203
+ ret = []
204
+ const resolveModulePromiseList = []
205
+
206
+ for (let index = 0; index < mockFiles.length; index++) {
207
+ const mockFile = mockFiles[index]
208
+ resolveModulePromiseList.push(resolveModule(path.join(absMockPath, mockFile), config))
209
+ }
210
+ const loadAllResult = await Promise.all(resolveModulePromiseList)
211
+ for (const resultModule of loadAllResult) {
212
+ let mod = resultModule
213
+ if (!isArray(mod)) {
214
+ mod = [mod]
215
+ }
216
+ ret = [...ret, ...mod]
217
+ }
218
+ } catch (error: any) {
219
+ loggerOutput(`mock reload error`, error)
220
+ ret = []
221
+ }
222
+ return ret
223
+ }
224
+
225
+ // fixed file generation format
226
+ // use a random path to avoid import cache
227
+ const getOutputFile: GetOutputFile = (filepath, format) => {
228
+ const dirname = path.dirname(filepath)
229
+ const basename = path.basename(filepath)
230
+ const randomname = `${Date.now()}_${Math.random().toString(36).substring(2, 15)}`
231
+ return path.resolve(
232
+ dirname,
233
+ `_${basename.replace(JS_EXT_RE, `.bundled_${randomname}.${format === 'esm' ? 'mjs' : 'cjs'}`)}`,
234
+ )
235
+ }
236
+
237
+ // Inspired by vite
238
+ // support mock .ts files
239
+ async function resolveModule(p: string, config: ResolvedConfig): Promise<any> {
240
+ const mockData = await bundleRequire({
241
+ filepath: p,
242
+ getOutputFile,
243
+ })
244
+
245
+ let mod = mockData.mod.default || mockData.mod
246
+ if (isFunction(mod)) {
247
+ mod = await mod({ env: config.env, mode: config.mode, command: config.command })
248
+ }
249
+ return mod
250
+ }
251
+
252
+ // get custom config file path and mock dir path
253
+ function getPath(opt: ViteMockOptions) {
254
+ const { mockPath, configPath } = opt
255
+ const cwd = process.cwd()
256
+ const absMockPath = isAbsPath(mockPath) ? mockPath! : path.join(cwd, mockPath || '')
257
+ const absConfigPath = path.join(cwd, configPath || '')
258
+ return {
259
+ absMockPath,
260
+ absConfigPath,
261
+ }
262
+ }
263
+
264
+ function loggerOutput(title: string, msg: string, type: 'info' | 'error' = 'info') {
265
+ const tag = type === 'info' ? colors.cyan(`[vite:mock]`) : colors.red(`[vite:mock-server]`)
266
+ return console.log(
267
+ `${colors.dim(new Date().toLocaleTimeString())} ${tag} ${colors.green(title)} ${colors.dim(
268
+ msg,
269
+ )}`,
270
+ )
271
+ }
@@ -0,0 +1,69 @@
1
+ (async () => {
2
+ try {
3
+ await import('mockjs')
4
+ } catch (e) {
5
+ throw new Error('vite-plugin-vue-mock requires mockjs to be present in the dependency tree.')
6
+ }
7
+ })()
8
+
9
+ import type { Plugin, ResolvedConfig } from 'vite'
10
+ import { normalizePath } from 'vite'
11
+ import path from 'node:path'
12
+ import type { ViteMockOptions } from './types'
13
+ import { fileExists } from './utils'
14
+ import { createMockServer, requestMiddleware } from './createMockServer'
15
+
16
+ function getDefaultPath(supportTs = true) {
17
+ return path.resolve(process.cwd(), `src/main.${supportTs ? 'ts' : 'js'}`)
18
+ }
19
+
20
+ export function viteMockServe(opt: ViteMockOptions = {}): Plugin {
21
+ let isDev = false
22
+ let needSourcemap = false
23
+ let config: ResolvedConfig
24
+ let defaultPath = getDefaultPath()
25
+ if (!fileExists(defaultPath)) {
26
+ defaultPath = getDefaultPath(false)
27
+ if (!fileExists(defaultPath)) {
28
+ defaultPath = ''
29
+ }
30
+ }
31
+
32
+ const defaultEnter = normalizePath(defaultPath)
33
+
34
+ const { injectFile = defaultEnter } = opt
35
+
36
+ return {
37
+ name: 'vite:mock',
38
+ enforce: 'pre' as const,
39
+ configResolved(resolvedConfig) {
40
+ config = resolvedConfig
41
+ isDev = config.command === 'serve'
42
+ needSourcemap = !!resolvedConfig.build.sourcemap
43
+ isDev && createMockServer(opt, config)
44
+ },
45
+
46
+ configureServer: async ({ middlewares }) => {
47
+ const { enable = isDev } = opt
48
+ if (!enable) {
49
+ return
50
+ }
51
+ const middleware = await requestMiddleware(opt)
52
+ middlewares.use(middleware)
53
+ },
54
+ transform: (code, id) => {
55
+ if (isDev || !injectFile || !id.endsWith(injectFile)) {
56
+ return null
57
+ }
58
+
59
+ const { injectCode = '' } = opt
60
+
61
+ return {
62
+ map: needSourcemap ? this.getCombinedSourcemap() : null,
63
+ code: `${code}\n${injectCode}`
64
+ }
65
+ }
66
+ }
67
+ }
68
+
69
+ export * from './types'
@@ -0,0 +1,48 @@
1
+ import { IncomingMessage, ServerResponse } from 'http'
2
+
3
+ export interface ViteMockOptions {
4
+ mockPath?: string;
5
+ configPath?: string;
6
+ injectFile?: string;
7
+ injectCode?: string;
8
+ ignore?: RegExp | ((fileName: string) => boolean);
9
+ watchFiles?: boolean;
10
+ enable?: boolean;
11
+ cors?: boolean;
12
+ /**
13
+ * Automatic recognition, no need to configure again
14
+ * @deprecated Deprecated after 2.8.0
15
+ */
16
+ supportTs?: boolean;
17
+ logger?: boolean;
18
+ }
19
+
20
+ export interface RespThisType {
21
+ req: IncomingMessage
22
+ res: ServerResponse
23
+ parseJson: () => any
24
+ }
25
+
26
+ export type MethodType = 'get' | 'post' | 'put' | 'delete' | 'patch'
27
+
28
+ export type Recordable<T = any> = Record<string, T>
29
+
30
+ export declare interface MockMethod {
31
+ url: string
32
+ method?: MethodType
33
+ timeout?: number
34
+ statusCode?: number
35
+ response?:
36
+ | ((
37
+ this: RespThisType,
38
+ opt: { url: Recordable; body: Recordable; query: Recordable; headers: Recordable },
39
+ ) => any)
40
+ | any
41
+ rawResponse?: (this: RespThisType, req: IncomingMessage, res: ServerResponse) => void
42
+ }
43
+
44
+ export interface MockConfig {
45
+ env: Record<string, any>
46
+ mode: string
47
+ command: 'build' | 'serve'
48
+ }
@@ -0,0 +1,48 @@
1
+ import fs from 'node:fs'
2
+
3
+ const toString = Object.prototype.toString
4
+
5
+ export function is(val: unknown, type: string) {
6
+ return toString.call(val) === `[object ${type}]`
7
+ }
8
+
9
+ export function fileExists(f: string) {
10
+ try {
11
+ fs.accessSync(f, fs.constants.W_OK);
12
+ return true;
13
+ } catch (error) {
14
+ return false;
15
+ }
16
+ }
17
+
18
+ export function isFunction<T = Function>(val: unknown): val is T {
19
+ return is(val, 'Function') || is(val, 'AsyncFunction')
20
+ }
21
+
22
+ export function isArray(val: any): val is Array<any> {
23
+ return val && Array.isArray(val)
24
+ }
25
+
26
+ export function isRegExp(val: unknown): val is RegExp {
27
+ return is(val, 'RegExp')
28
+ }
29
+
30
+ export function isAbsPath(path: string | undefined) {
31
+ if (!path) {
32
+ return false
33
+ }
34
+ // Windows 路径格式:C:\ 或 \\ 开头,或已含盘符(D:\path\to\file)
35
+ if (/^([a-zA-Z]:\\|\\\\|(?:\/|\uFF0F){2,})/.test(path)) {
36
+ return true
37
+ }
38
+ // Unix/Linux 路径格式:/ 开头
39
+ return /^\/[^/]/.test(path)
40
+ }
41
+
42
+ export function sleep(time: number) {
43
+ return new Promise((resolve) => {
44
+ setTimeout(() => {
45
+ resolve('')
46
+ }, time)
47
+ })
48
+ }