transform.ts 3.3 KB
Newer Older
V
vben 已提交
1
// 修改自
V
vben 已提交
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
// https://github.com/luxueyan/vite-transform-globby-import/blob/master/src/index.ts

// TODO 目前还不能监听文件新增及删除 内容已经改变,缓存问题?
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);
V
vben 已提交
52 53
        if (!match) return code;
        const lastImport = urlMap.get(path);
V
vben 已提交
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
        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;