25 changed files with 372 additions and 135 deletions
@ -1,96 +0,0 @@ |
|||
// Modified from
|
|||
// https://github.com/luxueyan/vite-transform-globby-import/blob/master/src/index.ts
|
|||
|
|||
// TODO Currently, it is not possible to monitor file addition and deletion. The content has been changed, the cache problem?
|
|||
import { join } from 'path'; |
|||
import { lstatSync } from 'fs'; |
|||
import glob from 'glob'; |
|||
import { createResolver, Resolver } from 'vite/dist/node/resolver.js'; |
|||
import { Transform } from 'vite/dist/node/transform.js'; |
|||
|
|||
const modulesDir: string = join(process.cwd(), '/node_modules/'); |
|||
|
|||
interface SharedConfig { |
|||
root?: string; |
|||
alias?: Record<string, string>; |
|||
resolvers?: Resolver[]; |
|||
} |
|||
|
|||
function template(template: string) { |
|||
return (data: { [x: string]: any }) => { |
|||
return template.replace(/#([^#]+)#/g, (_, g1) => data[g1] || g1); |
|||
}; |
|||
} |
|||
|
|||
const globbyTransform = function (config: SharedConfig): Transform { |
|||
const resolver = createResolver( |
|||
config.root || process.cwd(), |
|||
config.resolvers || [], |
|||
config.alias || {} |
|||
); |
|||
const cache = new Map(); |
|||
|
|||
const urlMap = new Map(); |
|||
return { |
|||
test({ path }) { |
|||
const filePath = path.replace('\u0000', ''); // why some path startsWith '\u0000'?
|
|||
try { |
|||
return ( |
|||
!filePath.startsWith(modulesDir) && |
|||
/\.(vue|js|jsx|ts|tsx)$/.test(filePath) && |
|||
lstatSync(filePath).isFile() |
|||
); |
|||
} catch { |
|||
return false; |
|||
} |
|||
}, |
|||
transform({ code, path, isBuild }) { |
|||
let result = cache.get(path); |
|||
if (!result) { |
|||
const reg = /import\s+([\w\s{}*]+)\s+from\s+(['"])globby(\?path)?!([^'"]+)\2/g; |
|||
const match = code.match(reg); |
|||
if (!match) return code; |
|||
const lastImport = urlMap.get(path); |
|||
if (lastImport && match) { |
|||
code = code.replace(lastImport, match[0]); |
|||
} |
|||
result = code.replace(reg, (_, g1, g2, g3, g4) => { |
|||
const filePath = path.replace('\u0000', ''); // why some path startsWith '\u0000'?
|
|||
// resolve path
|
|||
const resolvedFilePath = g4.startsWith('.') |
|||
? resolver.resolveRelativeRequest(filePath, g4) |
|||
: { pathname: resolver.requestToFile(g4) }; |
|||
const files = glob.sync(resolvedFilePath.pathname, { dot: true }); |
|||
let templateStr = 'import #name# from #file#'; // import default
|
|||
let name = g1; |
|||
const m = g1.match(/\{\s*(\w+)(\s+as\s+(\w+))?\s*\}/); // import module
|
|||
const m2 = g1.match(/\*\s+as\s+(\w+)/); // import * as all module
|
|||
if (m) { |
|||
templateStr = `import { ${m[1]} as #name# } from #file#`; |
|||
name = m[3] || m[1]; |
|||
} else if (m2) { |
|||
templateStr = 'import * as #name# from #file#'; |
|||
name = m2[1]; |
|||
} |
|||
const temRender = template(templateStr); |
|||
|
|||
const groups: Array<string>[] = []; |
|||
const replaceFiles = files.map((f, i) => { |
|||
const file = g2 + resolver.fileToRequest(f) + g2; |
|||
groups.push([name + i, file]); |
|||
return temRender({ name: name + i, file }); |
|||
}); |
|||
urlMap.set(path, replaceFiles.join('\n')); |
|||
return ( |
|||
replaceFiles.join('\n') + |
|||
(g3 ? '\n' + groups.map((v) => `${v[0]}._path = ${v[1]}`).join('\n') : '') + |
|||
`\nconst ${name} = { ${groups.map((v) => v[0]).join(',')} }\n` |
|||
); |
|||
}); |
|||
if (isBuild) cache.set(path, result); |
|||
} |
|||
return result; |
|||
}, |
|||
}; |
|||
}; |
|||
export default globbyTransform; |
|||
@ -0,0 +1,201 @@ |
|||
// Modified from
|
|||
// https://github.com/luxueyan/vite-transform-globby-import/blob/master/src/index.ts
|
|||
|
|||
// TODO Deleting files requires re-running the project
|
|||
import { join } from 'path'; |
|||
import { lstatSync } from 'fs'; |
|||
import glob from 'glob'; |
|||
import globrex from 'globrex'; |
|||
import dotProp from 'dot-prop'; |
|||
import { createResolver, Resolver } from 'vite/dist/node/resolver.js'; |
|||
import { Transform } from 'vite/dist/node/transform.js'; |
|||
|
|||
const modulesDir: string = join(process.cwd(), '/node_modules/'); |
|||
|
|||
interface SharedConfig { |
|||
root?: string; |
|||
alias?: Record<string, string>; |
|||
resolvers?: Resolver[]; |
|||
|
|||
includes?: string[]; |
|||
} |
|||
|
|||
function template(template: string) { |
|||
return (data: { [x: string]: any }) => { |
|||
return template.replace(/#([^#]+)#/g, (_, g1) => data[g1] || g1); |
|||
}; |
|||
} |
|||
|
|||
// TODO support hmr
|
|||
function hmr(isBuild = false) { |
|||
if (isBuild) return ''; |
|||
return ` |
|||
if (import.meta.hot) { |
|||
import.meta.hot.accept(); |
|||
}`;
|
|||
} |
|||
|
|||
// handle includes
|
|||
function fileInclude(includes: string | string[] | undefined, filePath: string) { |
|||
return !includes || !Array.isArray(includes) |
|||
? true |
|||
: includes.some((item) => filePath.startsWith(item)); |
|||
} |
|||
|
|||
// Bare exporter
|
|||
function compareString(modify: any, data: string[][]) { |
|||
return modify ? '\n' + data.map((v) => `${v[0]}._path = ${v[1]}`).join('\n') : ''; |
|||
} |
|||
|
|||
function varTemplate(data: string[][], name: string) { |
|||
//prepare deep data (for locales)
|
|||
let deepData: Record<string, object | string> = {}; |
|||
let hasDeepData = false; |
|||
|
|||
//data modify
|
|||
data.map((v) => { |
|||
//check for has deep data
|
|||
if (v[0].includes('/')) { |
|||
hasDeepData = true; |
|||
} |
|||
|
|||
// lastKey is a data
|
|||
let pathValue = v[0].replace(/\//g, '.').split('.'); |
|||
let lastKey: string | undefined = pathValue.pop(); |
|||
|
|||
let deepValue: Record<any, any> = {}; |
|||
if (lastKey) { |
|||
deepValue[lastKey.replace('_' + pathValue[0], '')] = lastKey; |
|||
} |
|||
|
|||
// Set Deep Value
|
|||
deepValue = Object.assign(deepValue, dotProp.get(deepData, pathValue.join('.'))); |
|||
dotProp.set(deepData, pathValue.join('.'), deepValue); |
|||
}); |
|||
|
|||
if (hasDeepData) { |
|||
return `const ${name} = ` + JSON.stringify(deepData).replace(/\"|\'/g, ''); |
|||
} |
|||
|
|||
return `const ${name} = { ${data.map((v) => v[0]).join(',')} }`; |
|||
} |
|||
|
|||
const globTransform = function (config: SharedConfig): Transform { |
|||
const resolver = createResolver( |
|||
config.root || process.cwd(), |
|||
config.resolvers || [], |
|||
config.alias || {} |
|||
); |
|||
const { includes } = config; |
|||
const cache = new Map(); |
|||
const urlMap = new Map(); |
|||
return { |
|||
test({ path }) { |
|||
const filePath = path.replace('\u0000', ''); // why some path startsWith '\u0000'?
|
|||
|
|||
try { |
|||
return ( |
|||
!filePath.startsWith(modulesDir) && |
|||
/\.(vue|js|jsx|ts|tsx)$/.test(filePath) && |
|||
fileInclude(includes, filePath) && |
|||
lstatSync(filePath).isFile() |
|||
); |
|||
} catch { |
|||
return false; |
|||
} |
|||
}, |
|||
transform({ code, path, isBuild }) { |
|||
let result = cache.get(path); |
|||
if (!result) { |
|||
const reg = /import\s+([\w\s{}*]+)\s+from\s+(['"])globby(\?locale)?(\?path)?!([^'"]+)\2/g; |
|||
const match = code.match(reg); |
|||
if (!match) return code; |
|||
const lastImport = urlMap.get(path); |
|||
if (lastImport && match) { |
|||
code = code.replace(lastImport, match[0]); |
|||
} |
|||
result = code.replace( |
|||
reg, |
|||
( |
|||
_, |
|||
// variable to export
|
|||
exportName, |
|||
// bare export or not
|
|||
bareExporter, |
|||
// is locale import
|
|||
isLocale, |
|||
// inject _path attr
|
|||
injectPath, |
|||
// path export
|
|||
globPath |
|||
) => { |
|||
const filePath = path.replace('\u0000', ''); // why some path startsWith '\u0000'?
|
|||
// resolve path
|
|||
|
|||
const resolvedFilePath = globPath.startsWith('.') |
|||
? resolver.resolveRelativeRequest(filePath, globPath) |
|||
: { pathname: resolver.requestToFile(globPath) }; |
|||
|
|||
const files = glob.sync(resolvedFilePath.pathname, { dot: true }); |
|||
|
|||
let templateStr = 'import #name# from #file#'; // import default
|
|||
let name = exportName; |
|||
const m = exportName.match(/\{\s*(\w+)(\s+as\s+(\w+))?\s*\}/); // import module
|
|||
const m2 = exportName.match(/\*\s+as\s+(\w+)/); // import * as all module
|
|||
if (m) { |
|||
templateStr = `import { ${m[1]} as #name# } from #file#`; |
|||
name = m[3] || m[1]; |
|||
} else if (m2) { |
|||
templateStr = 'import * as #name# from #file#'; |
|||
name = m2[1]; |
|||
} |
|||
|
|||
const templateRender = template(templateStr); |
|||
|
|||
const groups: Array<string>[] = []; |
|||
const replaceFiles = files.map((f, i) => { |
|||
const fileNameWithAlias = resolver.fileToRequest(f); |
|||
|
|||
const file = bareExporter + fileNameWithAlias + bareExporter; |
|||
|
|||
if (isLocale) { |
|||
const globrexRes = globrex(globPath, { extended: true, globstar: true }); |
|||
|
|||
// Get segments for files like an en/system ch/modules for:
|
|||
// ['en', 'system'] ['ch', 'modules']
|
|||
const matchedGroups = globrexRes.regex.exec(fileNameWithAlias); |
|||
|
|||
if (matchedGroups && matchedGroups.length) { |
|||
const matchedSegments = matchedGroups[1]; //first everytime "Full Match"
|
|||
const name = matchedGroups[2] + '_' + matchedSegments.split('/').shift(); |
|||
//send deep way like an (en/modules/system/dashboard) into groups
|
|||
groups.push([matchedSegments + name, file]); |
|||
return templateRender({ |
|||
name, |
|||
file, |
|||
}); |
|||
} |
|||
} else { |
|||
groups.push([name + i, file]); |
|||
return templateRender({ name: name + i, file }); |
|||
} |
|||
}); |
|||
// save in memory used result
|
|||
const filesJoined = replaceFiles.join('\n'); |
|||
|
|||
urlMap.set(path, filesJoined); |
|||
return [ |
|||
filesJoined, |
|||
compareString(injectPath, groups), |
|||
varTemplate(groups, name), |
|||
'', |
|||
].join('\n'); |
|||
} |
|||
); |
|||
if (isBuild) cache.set(path, result); |
|||
} |
|||
return `${result}${hmr(isBuild)}`; |
|||
}, |
|||
}; |
|||
}; |
|||
export default globTransform; |
|||
@ -0,0 +1,21 @@ |
|||
import type { LocaleType } from '/@/locales/types'; |
|||
import { appStore } from '/@/store/modules/app'; |
|||
|
|||
export function useLocale() { |
|||
/** |
|||
* |
|||
*/ |
|||
function getLocale(): string { |
|||
return appStore.getProjectConfig.locale; |
|||
} |
|||
|
|||
/** |
|||
* |
|||
* @param locale |
|||
*/ |
|||
async function changeLocale(locale: LocaleType): Promise<void> { |
|||
appStore.commitProjectConfigState({ locale: locale }); |
|||
} |
|||
|
|||
return { getLocale, changeLocale }; |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
import messages from 'globby?locale!/@/locales/lang/**/*.@(ts)'; |
|||
|
|||
export default messages; |
|||
@ -0,0 +1,3 @@ |
|||
export default { |
|||
someentry: 'some text', |
|||
}; |
|||
@ -0,0 +1,3 @@ |
|||
export default { |
|||
some: 'Get Out', |
|||
}; |
|||
@ -0,0 +1,3 @@ |
|||
export default { |
|||
button: 'Login', |
|||
}; |
|||
@ -0,0 +1,3 @@ |
|||
export default { |
|||
someentry: 'some text', |
|||
}; |
|||
@ -0,0 +1,3 @@ |
|||
export default { |
|||
some: 'Get Out', |
|||
}; |
|||
@ -0,0 +1,7 @@ |
|||
export default { |
|||
button: 'Login', |
|||
validation: { |
|||
account: 'Required Field account', |
|||
password: 'Required Field password', |
|||
}, |
|||
}; |
|||
@ -0,0 +1,3 @@ |
|||
export default { |
|||
someentry: '一些文本', |
|||
}; |
|||
@ -0,0 +1,3 @@ |
|||
export default { |
|||
some: '出去', |
|||
}; |
|||
@ -0,0 +1,3 @@ |
|||
export default { |
|||
button: '登录', |
|||
}; |
|||
@ -0,0 +1 @@ |
|||
export type LocaleType = 'zhCN' | 'en' | 'ru' | 'ja'; |
|||
@ -0,0 +1,35 @@ |
|||
import type { App } from 'vue'; |
|||
import type { I18n, Locale, I18nOptions } from 'vue-i18n'; |
|||
|
|||
import { createI18n } from 'vue-i18n'; |
|||
import localeMessages from '/@/locales'; |
|||
import { useLocale } from '/@/hooks/web/useLocale'; |
|||
|
|||
const { getLocale } = useLocale(); |
|||
|
|||
const localeData: I18nOptions = { |
|||
legacy: false, |
|||
locale: getLocale(), |
|||
// TODO: setting fallback inside settings
|
|||
fallbackLocale: 'en', |
|||
messages: localeMessages, |
|||
// availableLocales: ['ru'],
|
|||
sync: true, //If you don’t want to inherit locale from global scope, you need to set sync of i18n component option to false.
|
|||
silentTranslationWarn: false, // true - warning off
|
|||
silentFallbackWarn: true, |
|||
}; |
|||
|
|||
let i18n: I18n; |
|||
|
|||
// setup i18n instance with glob
|
|||
export function setupI18n(app: App) { |
|||
i18n = createI18n(localeData) as I18n; |
|||
setI18nLanguage(getLocale()); |
|||
app.use(i18n); |
|||
} |
|||
|
|||
export function setI18nLanguage(locale: Locale): void { |
|||
// @ts-ignore
|
|||
i18n.global.locale.value = locale; |
|||
// i18n.global.setLocaleMessage(locale, messages);
|
|||
} |
|||
Loading…
Reference in new issue