Commit 8882d4e7eafabceb3c53cd5c22ad70ee166921c4

Authored by vben
1 parent 3a651767

wip: support multilingual configuration

build/vite/plugin/dynamicImport/index.ts renamed to build/vite/plugin/transform/dynamic-import/index.ts
1 1 // Used to import all files under `src/views`
2   -
3 2 // The built-in dynamic import of vite cannot meet the needs of importing all files under views
4   -
  3 +// Special usage ,Only for this project
5 4 import glob from 'glob';
6 5 import { Transform } from 'vite/dist/node/transform.js';
7 6  
... ... @@ -28,7 +27,6 @@ const dynamicImportTransform = function (env: any = {}): Transform {
28 27 return code;
29 28 }
30 29  
31   - // if (!isBuild) return code;
32 30 // Only convert the dir
33 31 try {
34 32 const files = glob.sync('src/views/**/**.{vue,tsx}', { cwd: process.cwd() });
... ...
build/vite/plugin/context/transform.ts renamed to build/vite/plugin/transform/globby/index.ts
1 1 // Modified from
2 2 // https://github.com/luxueyan/vite-transform-globby-import/blob/master/src/index.ts
3 3  
4   -// TODO Currently, it is not possible to monitor file addition and deletion. The content has been changed, the cache problem?
  4 +// TODO Deleting files requires re-running the project
5 5 import { join } from 'path';
6 6 import { lstatSync } from 'fs';
7 7 import glob from 'glob';
  8 +import globrex from 'globrex';
  9 +import dotProp from 'dot-prop';
8 10 import { createResolver, Resolver } from 'vite/dist/node/resolver.js';
9 11 import { Transform } from 'vite/dist/node/transform.js';
10 12  
... ... @@ -14,6 +16,8 @@ interface SharedConfig {
14 16 root?: string;
15 17 alias?: Record<string, string>;
16 18 resolvers?: Resolver[];
  19 +
  20 + includes?: string[];
17 21 }
18 22  
19 23 function template(template: string) {
... ... @@ -22,22 +26,78 @@ function template(template: string) {
22 26 };
23 27 }
24 28  
25   -const globbyTransform = function (config: SharedConfig): Transform {
  29 +// TODO support hmr
  30 +function hmr(isBuild = false) {
  31 + if (isBuild) return '';
  32 + return `
  33 + if (import.meta.hot) {
  34 + import.meta.hot.accept();
  35 + }`;
  36 +}
  37 +
  38 +// handle includes
  39 +function fileInclude(includes: string | string[] | undefined, filePath: string) {
  40 + return !includes || !Array.isArray(includes)
  41 + ? true
  42 + : includes.some((item) => filePath.startsWith(item));
  43 +}
  44 +
  45 +// Bare exporter
  46 +function compareString(modify: any, data: string[][]) {
  47 + return modify ? '\n' + data.map((v) => `${v[0]}._path = ${v[1]}`).join('\n') : '';
  48 +}
  49 +
  50 +function varTemplate(data: string[][], name: string) {
  51 + //prepare deep data (for locales)
  52 + let deepData: Record<string, object | string> = {};
  53 + let hasDeepData = false;
  54 +
  55 + //data modify
  56 + data.map((v) => {
  57 + //check for has deep data
  58 + if (v[0].includes('/')) {
  59 + hasDeepData = true;
  60 + }
  61 +
  62 + // lastKey is a data
  63 + let pathValue = v[0].replace(/\//g, '.').split('.');
  64 + let lastKey: string | undefined = pathValue.pop();
  65 +
  66 + let deepValue: Record<any, any> = {};
  67 + if (lastKey) {
  68 + deepValue[lastKey.replace('_' + pathValue[0], '')] = lastKey;
  69 + }
  70 +
  71 + // Set Deep Value
  72 + deepValue = Object.assign(deepValue, dotProp.get(deepData, pathValue.join('.')));
  73 + dotProp.set(deepData, pathValue.join('.'), deepValue);
  74 + });
  75 +
  76 + if (hasDeepData) {
  77 + return `const ${name} = ` + JSON.stringify(deepData).replace(/\"|\'/g, '');
  78 + }
  79 +
  80 + return `const ${name} = { ${data.map((v) => v[0]).join(',')} }`;
  81 +}
  82 +
  83 +const globTransform = function (config: SharedConfig): Transform {
26 84 const resolver = createResolver(
27 85 config.root || process.cwd(),
28 86 config.resolvers || [],
29 87 config.alias || {}
30 88 );
  89 + const { includes } = config;
31 90 const cache = new Map();
32   -
33 91 const urlMap = new Map();
34 92 return {
35 93 test({ path }) {
36 94 const filePath = path.replace('\u0000', ''); // why some path startsWith '\u0000'?
  95 +
37 96 try {
38 97 return (
39 98 !filePath.startsWith(modulesDir) &&
40 99 /\.(vue|js|jsx|ts|tsx)$/.test(filePath) &&
  100 + fileInclude(includes, filePath) &&
41 101 lstatSync(filePath).isFile()
42 102 );
43 103 } catch {
... ... @@ -47,50 +107,95 @@ const globbyTransform = function (config: SharedConfig): Transform {
47 107 transform({ code, path, isBuild }) {
48 108 let result = cache.get(path);
49 109 if (!result) {
50   - const reg = /import\s+([\w\s{}*]+)\s+from\s+(['"])globby(\?path)?!([^'"]+)\2/g;
  110 + const reg = /import\s+([\w\s{}*]+)\s+from\s+(['"])globby(\?locale)?(\?path)?!([^'"]+)\2/g;
51 111 const match = code.match(reg);
52 112 if (!match) return code;
53 113 const lastImport = urlMap.get(path);
54 114 if (lastImport && match) {
55 115 code = code.replace(lastImport, match[0]);
56 116 }
57   - result = code.replace(reg, (_, g1, g2, g3, g4) => {
58   - const filePath = path.replace('\u0000', ''); // why some path startsWith '\u0000'?
59   - // resolve path
60   - const resolvedFilePath = g4.startsWith('.')
61   - ? resolver.resolveRelativeRequest(filePath, g4)
62   - : { pathname: resolver.requestToFile(g4) };
63   - const files = glob.sync(resolvedFilePath.pathname, { dot: true });
64   - let templateStr = 'import #name# from #file#'; // import default
65   - let name = g1;
66   - const m = g1.match(/\{\s*(\w+)(\s+as\s+(\w+))?\s*\}/); // import module
67   - const m2 = g1.match(/\*\s+as\s+(\w+)/); // import * as all module
68   - if (m) {
69   - templateStr = `import { ${m[1]} as #name# } from #file#`;
70   - name = m[3] || m[1];
71   - } else if (m2) {
72   - templateStr = 'import * as #name# from #file#';
73   - name = m2[1];
  117 + result = code.replace(
  118 + reg,
  119 + (
  120 + _,
  121 + // variable to export
  122 + exportName,
  123 + // bare export or not
  124 + bareExporter,
  125 + // is locale import
  126 + isLocale,
  127 + // inject _path attr
  128 + injectPath,
  129 + // path export
  130 + globPath
  131 + ) => {
  132 + const filePath = path.replace('\u0000', ''); // why some path startsWith '\u0000'?
  133 + // resolve path
  134 +
  135 + const resolvedFilePath = globPath.startsWith('.')
  136 + ? resolver.resolveRelativeRequest(filePath, globPath)
  137 + : { pathname: resolver.requestToFile(globPath) };
  138 +
  139 + const files = glob.sync(resolvedFilePath.pathname, { dot: true });
  140 +
  141 + let templateStr = 'import #name# from #file#'; // import default
  142 + let name = exportName;
  143 + const m = exportName.match(/\{\s*(\w+)(\s+as\s+(\w+))?\s*\}/); // import module
  144 + const m2 = exportName.match(/\*\s+as\s+(\w+)/); // import * as all module
  145 + if (m) {
  146 + templateStr = `import { ${m[1]} as #name# } from #file#`;
  147 + name = m[3] || m[1];
  148 + } else if (m2) {
  149 + templateStr = 'import * as #name# from #file#';
  150 + name = m2[1];
  151 + }
  152 +
  153 + const templateRender = template(templateStr);
  154 +
  155 + const groups: Array<string>[] = [];
  156 + const replaceFiles = files.map((f, i) => {
  157 + const fileNameWithAlias = resolver.fileToRequest(f);
  158 +
  159 + const file = bareExporter + fileNameWithAlias + bareExporter;
  160 +
  161 + if (isLocale) {
  162 + const globrexRes = globrex(globPath, { extended: true, globstar: true });
  163 +
  164 + // Get segments for files like an en/system ch/modules for:
  165 + // ['en', 'system'] ['ch', 'modules']
  166 + const matchedGroups = globrexRes.regex.exec(fileNameWithAlias);
  167 +
  168 + if (matchedGroups && matchedGroups.length) {
  169 + const matchedSegments = matchedGroups[1]; //first everytime "Full Match"
  170 + const name = matchedGroups[2] + '_' + matchedSegments.split('/').shift();
  171 + //send deep way like an (en/modules/system/dashboard) into groups
  172 + groups.push([matchedSegments + name, file]);
  173 + return templateRender({
  174 + name,
  175 + file,
  176 + });
  177 + }
  178 + } else {
  179 + groups.push([name + i, file]);
  180 + return templateRender({ name: name + i, file });
  181 + }
  182 + });
  183 + // save in memory used result
  184 + const filesJoined = replaceFiles.join('\n');
  185 +
  186 + urlMap.set(path, filesJoined);
  187 + return [
  188 + filesJoined,
  189 + compareString(injectPath, groups),
  190 + varTemplate(groups, name),
  191 + '',
  192 + ].join('\n');
74 193 }
75   - const temRender = template(templateStr);
76   -
77   - const groups: Array<string>[] = [];
78   - const replaceFiles = files.map((f, i) => {
79   - const file = g2 + resolver.fileToRequest(f) + g2;
80   - groups.push([name + i, file]);
81   - return temRender({ name: name + i, file });
82   - });
83   - urlMap.set(path, replaceFiles.join('\n'));
84   - return (
85   - replaceFiles.join('\n') +
86   - (g3 ? '\n' + groups.map((v) => `${v[0]}._path = ${v[1]}`).join('\n') : '') +
87   - `\nconst ${name} = { ${groups.map((v) => v[0]).join(',')} }\n`
88   - );
89   - });
  194 + );
90 195 if (isBuild) cache.set(path, result);
91 196 }
92   - return result;
  197 + return `${result}${hmr(isBuild)}`;
93 198 },
94 199 };
95 200 };
96   -export default globbyTransform;
  201 +export default globTransform;
... ...
package.json
... ... @@ -22,7 +22,7 @@
22 22 },
23 23 "dependencies": {
24 24 "@iconify/iconify": "^2.0.0-rc.2",
25   - "@vueuse/core": "4.0.0-beta.41",
  25 + "@vueuse/core": "4.0.0-rc.3",
26 26 "ant-design-vue": "2.0.0-beta.15",
27 27 "apexcharts": "3.22.0",
28 28 "axios": "^0.21.0",
... ... @@ -35,7 +35,7 @@
35 35 "qrcode": "^1.4.4",
36 36 "vditor": "^3.6.2",
37 37 "vue": "^3.0.2",
38   - "vue-i18n": "^9.0.0-beta.7",
  38 + "vue-i18n": "^9.0.0-beta.8",
39 39 "vue-router": "^4.0.0-rc.3",
40 40 "vuex": "^4.0.0-rc.1",
41 41 "vuex-module-decorators": "^1.0.1",
... ... @@ -50,6 +50,7 @@
50 50 "@purge-icons/generated": "^0.4.1",
51 51 "@types/echarts": "^4.9.1",
52 52 "@types/fs-extra": "^9.0.4",
  53 + "@types/globrex": "^0.1.0",
53 54 "@types/koa-static": "^4.0.1",
54 55 "@types/lodash-es": "^4.17.3",
55 56 "@types/mockjs": "^1.0.3",
... ... @@ -68,6 +69,7 @@
68 69 "conventional-changelog-cli": "^2.1.1",
69 70 "conventional-changelog-custom-config": "^0.3.1",
70 71 "cross-env": "^7.0.2",
  72 + "dot-prop": "^6.0.0",
71 73 "dotenv": "^8.2.0",
72 74 "eslint": "^7.13.0",
73 75 "eslint-config-prettier": "^6.15.0",
... ... @@ -75,6 +77,7 @@
75 77 "eslint-plugin-vue": "^7.1.0",
76 78 "esno": "^0.2.4",
77 79 "fs-extra": "^9.0.1",
  80 + "globrex": "^0.1.2",
78 81 "husky": "^4.3.0",
79 82 "koa-static": "^5.0.0",
80 83 "less": "^3.12.2",
... ...
src/hooks/web/useLocale.ts 0 → 100644
  1 +import type { LocaleType } from '/@/locales/types';
  2 +import { appStore } from '/@/store/modules/app';
  3 +
  4 +export function useLocale() {
  5 + /**
  6 + *
  7 + */
  8 + function getLocale(): string {
  9 + return appStore.getProjectConfig.locale;
  10 + }
  11 +
  12 + /**
  13 + *
  14 + * @param locale
  15 + */
  16 + async function changeLocale(locale: LocaleType): Promise<void> {
  17 + appStore.commitProjectConfigState({ locale: locale });
  18 + }
  19 +
  20 + return { getLocale, changeLocale };
  21 +}
... ...
src/locales/index.ts 0 → 100644
  1 +import messages from 'globby?locale!/@/locales/lang/**/*.@(ts)';
  2 +
  3 +export default messages;
... ...
src/locales/lang/en/routes/menus/dashboard.ts 0 → 100644
  1 +export default {
  2 + someentry: 'some text',
  3 +};
... ...
src/locales/lang/en/system/basic.ts 0 → 100644
  1 +export default {
  2 + some: 'Get Out',
  3 +};
... ...
src/locales/lang/en/system/login.ts 0 → 100644
  1 +export default {
  2 + button: 'Login',
  3 +};
... ...
src/locales/lang/ru/routes/menus/dashboard.ts 0 → 100644
  1 +export default {
  2 + someentry: 'some text',
  3 +};
... ...
src/locales/lang/ru/system/basic.ts 0 → 100644
  1 +export default {
  2 + some: 'Get Out',
  3 +};
... ...
src/locales/lang/ru/system/login.ts 0 → 100644
  1 +export default {
  2 + button: 'Login',
  3 + validation: {
  4 + account: 'Required Field account',
  5 + password: 'Required Field password',
  6 + },
  7 +};
... ...
src/locales/lang/zhCN/routes/menus/dashboard.ts 0 → 100644
  1 +export default {
  2 + someentry: '一些文本',
  3 +};
... ...
src/locales/lang/zhCN/system/basic.ts 0 → 100644
  1 +export default {
  2 + some: '出去',
  3 +};
... ...
src/locales/lang/zhCN/system/login.ts 0 → 100644
  1 +export default {
  2 + button: '登录',
  3 +};
... ...
src/locales/types.ts 0 → 100644
  1 +export type LocaleType = 'zhCN' | 'en' | 'ru' | 'ja';
... ...
src/main.ts
... ... @@ -5,6 +5,7 @@ import { setupStore } from &#39;/@/store&#39;;
5 5 import { setupAntd } from '/@/setup/ant-design-vue';
6 6 import { setupErrorHandle } from '/@/setup/error-handle';
7 7 import { setupGlobDirectives } from '/@/setup/directives';
  8 +import { setupI18n } from '/@/setup/i18n';
8 9  
9 10 import { setupProdMockServer } from '../mock/_createProductionServer';
10 11 import { setApp } from '/@/setup/App';
... ... @@ -15,11 +16,16 @@ import { isDevMode, isProdMode, isUseMock } from &#39;/@/utils/env&#39;;
15 16  
16 17 import '/@/design/index.less';
17 18  
  19 +import '/@/locales/index';
  20 +
18 21 const app = createApp(App);
19 22  
20 23 // Configure component library
21 24 setupAntd(app);
22 25  
  26 +// Multilingual configuration
  27 +setupI18n(app);
  28 +
23 29 // Configure routing
24 30 setupRouter(app);
25 31  
... ...
src/settings/projectSetting.ts
... ... @@ -7,6 +7,7 @@ import { isProdMode } from &#39;/@/utils/env&#39;;
7 7  
8 8 // ! You need to clear the browser cache after the change
9 9 const setting: ProjectConfig = {
  10 + locale: 'en',
10 11 // color
11 12 // TODO 主题色
12 13 themeColor: primaryColor,
... ...
src/setup/i18n/index.ts 0 → 100644
  1 +import type { App } from 'vue';
  2 +import type { I18n, Locale, I18nOptions } from 'vue-i18n';
  3 +
  4 +import { createI18n } from 'vue-i18n';
  5 +import localeMessages from '/@/locales';
  6 +import { useLocale } from '/@/hooks/web/useLocale';
  7 +
  8 +const { getLocale } = useLocale();
  9 +
  10 +const localeData: I18nOptions = {
  11 + legacy: false,
  12 + locale: getLocale(),
  13 + // TODO: setting fallback inside settings
  14 + fallbackLocale: 'en',
  15 + messages: localeMessages,
  16 + // availableLocales: ['ru'],
  17 + sync: true, //If you don’t want to inherit locale from global scope, you need to set sync of i18n component option to false.
  18 + silentTranslationWarn: false, // true - warning off
  19 + silentFallbackWarn: true,
  20 +};
  21 +
  22 +let i18n: I18n;
  23 +
  24 +// setup i18n instance with glob
  25 +export function setupI18n(app: App) {
  26 + i18n = createI18n(localeData) as I18n;
  27 + setI18nLanguage(getLocale());
  28 + app.use(i18n);
  29 +}
  30 +
  31 +export function setI18nLanguage(locale: Locale): void {
  32 + // @ts-ignore
  33 + i18n.global.locale.value = locale;
  34 + // i18n.global.setLocaleMessage(locale, messages);
  35 +}
... ...
src/store/index.ts
1 1 import type { App } from 'vue';
2   -import { createStore, createLogger, Plugin } from 'vuex';
  2 +import {
  3 + createStore,
  4 + // createLogger, Plugin
  5 +} from 'vuex';
3 6 import { config } from 'vuex-module-decorators';
4 7 import { isDevMode } from '/@/utils/env';
5 8  
6 9 config.rawError = true;
7 10 const isDev = isDevMode();
8   -const plugins: Plugin<any>[] = isDev ? [createLogger()] : [];
  11 +// const plugins: Plugin<any>[] = isDev ? [createLogger()] : [];
9 12  
10 13 const store = createStore({
11 14 // modules: {},
12 15 strict: isDev,
13   - plugins,
  16 + // plugins,
14 17 });
15 18  
16 19 export function setupStore(app: App<Element>) {
... ...
src/types/config.d.ts
1 1 // 左侧菜单, 顶部菜单
2 2 import { MenuTypeEnum, MenuModeEnum, TriggerEnum } from '/@/enums/menuEnum';
3 3 import { ContentEnum, PermissionModeEnum, ThemeEnum, RouterTransitionEnum } from '/@/enums/appEnum';
4   -
  4 +import type { LocaleType } from '/@/locales/types';
5 5 export interface MessageSetting {
6 6 title: string;
7 7 // 取消按钮的文字,
... ... @@ -55,6 +55,7 @@ export interface HeaderSetting {
55 55 showNotice: boolean;
56 56 }
57 57 export interface ProjectConfig {
  58 + locale: LocaleType;
58 59 // header背景色
59 60 headerBgColor: string;
60 61 // 左侧菜单背景色
... ...
src/types/module.d.ts
... ... @@ -4,4 +4,6 @@ declare module &#39;globby!/@/router/routes/modules/**/*.@(ts)&#39;;
4 4  
5 5 declare module 'globby!/@/router/menus/modules/**/*.@(ts)';
6 6  
  7 +declare module 'globby?locale!/@/locales/lang/**/*.@(ts)';
  8 +
7 9 declare const React: string;
... ...
src/views/sys/login/Login.vue
... ... @@ -11,14 +11,14 @@
11 11  
12 12 <a-form class="mx-auto mt-10" :model="formData" :rules="formRules" ref="formRef">
13 13 <a-form-item name="account">
14   - <a-input size="large" v-model:value="formData.account" placeholder="Username: vben" />
  14 + <a-input size="large" v-model:value="formData.account" placeholder="username: vben" />
15 15 </a-form-item>
16 16 <a-form-item name="password">
17 17 <a-input-password
18 18 size="large"
19 19 visibilityToggle
20 20 v-model:value="formData.password"
21   - placeholder="Password: 123456"
  21 + placeholder="password: 123456"
22 22 />
23 23 </a-form-item>
24 24  
... ... @@ -28,13 +28,13 @@
28 28 <a-row>
29 29 <a-col :span="12">
30 30 <a-form-item>
31   - <!-- 未做逻辑,需要自行处理 -->
  31 + <!-- No logic, you need to deal with it yourself -->
32 32 <a-checkbox v-model:checked="autoLogin" size="small">自动登录</a-checkbox>
33 33 </a-form-item>
34 34 </a-col>
35 35 <a-col :span="12">
36 36 <a-form-item :style="{ 'text-align': 'right' }">
37   - <!-- 未做逻辑,需要自行处理 -->
  37 + <!-- No logic, you need to deal with it yourself -->
38 38 <a-button type="link" size="small">忘记密码</a-button>
39 39 </a-form-item>
40 40 </a-col>
... ... @@ -47,7 +47,7 @@
47 47 :block="true"
48 48 @click="login"
49 49 :loading="formState.loading"
50   - >登录</a-button
  50 + >{{ t('system.login.button') }}</a-button
51 51 >
52 52 </a-form-item>
53 53 </a-form>
... ... @@ -57,20 +57,15 @@
57 57 </div>
58 58 </template>
59 59 <script lang="ts">
60   - import {
61   - defineComponent,
62   - reactive,
63   - ref,
64   - unref,
65   - toRaw,
66   - // computed
67   - } from 'vue';
  60 + import { defineComponent, reactive, ref, unref, toRaw } from 'vue';
68 61 import { Checkbox } from 'ant-design-vue';
69 62  
70 63 import Button from '/@/components/Button/index.vue';
71 64 // import { BasicDragVerify, DragVerifyActionType } from '/@/components/Verify/index';
72 65  
73 66 import { userStore } from '/@/store/modules/user';
  67 + import { useI18n } from 'vue-i18n';
  68 +
74 69 // import { appStore } from '/@/store/modules/app';
75 70 import { useMessage } from '/@/hooks/web/useMessage';
76 71 import { useSetting } from '/@/hooks/core/useSetting';
... ... @@ -139,7 +134,7 @@
139 134 formState.loading = false;
140 135 }
141 136 }
142   -
  137 + const { t } = useI18n();
143 138 return {
144 139 formRef,
145 140 // verifyRef,
... ... @@ -151,6 +146,7 @@
151 146 // openLoginVerify: openLoginVerifyRef,
152 147 title: globSetting && globSetting.title,
153 148 logo,
  149 + t,
154 150 };
155 151 },
156 152 });
... ...
vite.config.ts
... ... @@ -4,8 +4,9 @@ import { resolve } from &#39;path&#39;;
4 4  
5 5 import { modifyVars } from './build/config/lessModifyVars';
6 6 import { createProxy } from './build/vite/proxy';
7   -import globbyTransform from './build/vite/plugin/context/transform';
8   -import dynamicImportTransform from './build/vite/plugin/dynamicImport/index';
  7 +
  8 +import globbyTransform from './build/vite/plugin/transform/globby';
  9 +import dynamicImportTransform from './build/vite/plugin/transform/dynamic-import';
9 10  
10 11 import { isDevFn, loadEnv } from './build/utils';
11 12  
... ... @@ -111,6 +112,11 @@ const viteConfig: UserConfig = {
111 112 },
112 113 define: {
113 114 __VERSION__: pkg.version,
  115 + // use vue-i18-next
  116 + // Suppress warning
  117 + __VUE_I18N_LEGACY_API__: false,
  118 + __VUE_I18N_FULL_INSTALL__: false,
  119 + __INTLIFY_PROD_DEVTOOLS__: false,
114 120 },
115 121 cssPreprocessOptions: {
116 122 less: {
... ... @@ -135,5 +141,13 @@ const viteConfig: UserConfig = {
135 141  
136 142 export default {
137 143 ...viteConfig,
138   - transforms: [globbyTransform(viteConfig), dynamicImportTransform(viteEnv)],
  144 + transforms: [
  145 + globbyTransform({
  146 + resolvers: viteConfig.resolvers,
  147 + root: viteConfig.root,
  148 + alias: viteConfig.alias,
  149 + includes: [resolve('src/router'), resolve('src/locales')],
  150 + }),
  151 + dynamicImportTransform(viteEnv),
  152 + ],
139 153 } as UserConfig;
... ...
yarn.lock
... ... @@ -1323,6 +1323,11 @@
1323 1323 dependencies:
1324 1324 "@types/node" "*"
1325 1325  
  1326 +"@types/globrex@^0.1.0":
  1327 + version "0.1.0"
  1328 + resolved "https://registry.npmjs.org/@types/globrex/-/globrex-0.1.0.tgz#baf4ac8e36947017612c01fde7c7b641dc0b6c89"
  1329 + integrity sha512-aBkxDgp/UbnluE+CIT3V3PoNewwOlLCzXSF3ipD86Slv8xVjwxrDAfSGbsfGgMzPo/fEMPXc+gNUJbtiugwfoA==
  1330 +
1326 1331 "@types/http-assert@*":
1327 1332 version "1.5.1"
1328 1333 resolved "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.1.tgz#d775e93630c2469c2f980fc27e3143240335db3b"
... ... @@ -1732,18 +1737,18 @@
1732 1737 vscode-languageserver-textdocument "^1.0.1"
1733 1738 vscode-uri "^2.1.2"
1734 1739  
1735   -"@vueuse/core@4.0.0-beta.41":
1736   - version "4.0.0-beta.41"
1737   - resolved "https://registry.npmjs.org/@vueuse/core/-/core-4.0.0-beta.41.tgz#0058aed5ade75ae2866283498009ad5172cbae84"
1738   - integrity sha512-CgUih65PzYScorm1S4F93e6XXm+qxA8GrRLOSB1kXaqtP6vXedwkBxKkNEYNACx4reL4VEHqM/BrM6FajXkQUg==
  1740 +"@vueuse/core@4.0.0-rc.3":
  1741 + version "4.0.0-rc.3"
  1742 + resolved "https://registry.npmjs.org/@vueuse/core/-/core-4.0.0-rc.3.tgz#5381ca657e10df596cd7027fc5c96b2d4b3a090c"
  1743 + integrity sha512-dQ/FZgo0z7kBFOvDWxuzaUrmuO8X1AlQk17e3PU1TVtG2Uu+mCvjPNbuvI2fjhTjl5rzPJawwoU2WZFj+nlFvw==
1739 1744 dependencies:
1740   - "@vueuse/shared" "4.0.0-beta.41"
  1745 + "@vueuse/shared" "4.0.0-rc.3"
1741 1746 vue-demi latest
1742 1747  
1743   -"@vueuse/shared@4.0.0-beta.41":
1744   - version "4.0.0-beta.41"
1745   - resolved "https://registry.npmjs.org/@vueuse/shared/-/shared-4.0.0-beta.41.tgz#395782ea2e580f1fc9488d25c89bd09f70170b25"
1746   - integrity sha512-dqnuEPPC3OUJ6L6rhMiOCuPWIR698DtdwOydwCZBISsG2V6gZ2QFND6xtRwLib6/lhUMYVYPwIz3hPjlx7BIzw==
  1748 +"@vueuse/shared@4.0.0-rc.3":
  1749 + version "4.0.0-rc.3"
  1750 + resolved "https://registry.npmjs.org/@vueuse/shared/-/shared-4.0.0-rc.3.tgz#42fb56fed3779f3b8a17a82c16a364bad20d01b7"
  1751 + integrity sha512-VY0x/XxpeTMHp/0FDiv1cgUUxkJGQl7liiM2AjR/J7+Ys/2Y2dijD5cAKViq9FGUPQQsOcLptMvMvUsDMoN4DA==
1747 1752 dependencies:
1748 1753 vue-demi latest
1749 1754  
... ... @@ -3222,6 +3227,13 @@ dot-prop@^5.1.0:
3222 3227 dependencies:
3223 3228 is-obj "^2.0.0"
3224 3229  
  3230 +dot-prop@^6.0.0:
  3231 + version "6.0.0"
  3232 + resolved "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.0.tgz#bd579fd704d970981c4b05de591db648959f2ebb"
  3233 + integrity sha512-xCbB8IN3IT+tdgoEPOnJmYTNJDrygGFOmiQEiVa5eAD+JEB1vTgMNhVGRnN5Eex/6amck7cdcrixb1qN9Go+GQ==
  3234 + dependencies:
  3235 + is-obj "^2.0.0"
  3236 +
3225 3237 dotenv-expand@^5.1.0:
3226 3238 version "5.1.0"
3227 3239 resolved "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0"
... ... @@ -4092,6 +4104,11 @@ globjoin@^0.1.4:
4092 4104 resolved "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz#2f4494ac8919e3767c5cbb691e9f463324285d43"
4093 4105 integrity sha1-L0SUrIkZ43Z8XLtpHp9GMyQoXUM=
4094 4106  
  4107 +globrex@^0.1.2:
  4108 + version "0.1.2"
  4109 + resolved "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz#dd5d9ec826232730cd6793a5e33a9302985e6098"
  4110 + integrity sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==
  4111 +
4095 4112 gonzales-pe@^4.3.0:
4096 4113 version "4.3.0"
4097 4114 resolved "https://registry.npmjs.org/gonzales-pe/-/gonzales-pe-4.3.0.tgz#fe9dec5f3c557eead09ff868c65826be54d067b3"
... ... @@ -8167,10 +8184,10 @@ vue-eslint-parser@^7.1.1:
8167 8184 esquery "^1.0.1"
8168 8185 lodash "^4.17.15"
8169 8186  
8170   -vue-i18n@^9.0.0-beta.7:
8171   - version "9.0.0-beta.7"
8172   - resolved "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.0.0-beta.7.tgz#f6fad5b4be218018aab4797f80dd2a95ee5236f9"
8173   - integrity sha512-hFl0XnV91P/4UyWvHYvdYxuk3GRnKIW9zXAm6hrUU4mOIwpqchi7jVQva2TJLr52Mpsu4zYXmzL1h5pgrKmCfQ==
  8187 +vue-i18n@^9.0.0-beta.8:
  8188 + version "9.0.0-beta.8"
  8189 + resolved "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.0.0-beta.8.tgz#92282d5b5e0e6f15cc04943ce11bf49db610468f"
  8190 + integrity sha512-tViSN96jLi0AKvAVi4twcYYN5Ld++SqN1/140ua+YWm/iRbO2M0rAcsZ7e6/4LTm6Pd1ldSwWihSuv2bSQmlnw==
8174 8191 dependencies:
8175 8192 source-map "0.6.1"
8176 8193  
... ...