|
@@ -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;
|