123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222 |
- // 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 scopeKey = '';
- // const len=pathValue.length
- // const scope=pathValue[len-2]
- let lastKey: string | undefined = pathValue.pop();
- let deepValue: Record<any, any> = {};
- if (lastKey) {
- // Solve the problem of files with the same name in different folders
- const lastKeyList = lastKey.replace('_' + pathValue[0], '').split('_');
- const key = lastKeyList.pop();
- if (key) {
- deepValue[key] = 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 filePath = resolver.fileToRequest(f);
- const file = bareExporter + filePath + 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']
- // TODO The window system and mac system path are inconsistent?
- const fileNameWithAlias = filePath.replace(/^(\/src\/)/, '/@/');
- const matchedGroups = globrexRes.regex.exec(fileNameWithAlias);
- if (matchedGroups && matchedGroups.length) {
- const matchedSegments = matchedGroups[1]; //first everytime "Full Match"
- const matchList = matchedSegments.split('/').filter(Boolean);
- const lang = matchList.shift();
- const scope = matchList.pop();
- // Solve the problem of files with the same name in different folders
- const scopeKey = scope ? `${scope}_` : '';
- const fileName = matchedGroups[2];
- const name = scopeKey + fileName + '_' + lang;
- //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);
- // console.log('======================');
- // console.log(filesJoined, varTemplate(groups, name));
- // console.log('======================');
- return [
- filesJoined,
- compareString(injectPath, groups),
- varTemplate(groups, name),
- '',
- ].join('\n');
- }
- );
- if (isBuild) cache.set(path, result);
- }
- return `${result}${hmr(isBuild)}`;
- },
- };
- };
- export default globTransform;
|