index.ts 2.27 KB
import { lodash as _ } from '@umijs/utils';
import { extname, parse, relative, sep } from 'path';

/**
 * convert path into componentName
 * like:
 *  - src/index.tsx => SrcIndex
 *  - components/Header.tsx => ComponentHeader
 *
 * @param filePath
 * @returns {string} componentName
 */
export const path2Component = (filePath: string): string => {
  const { ext } = parse(filePath);
  const filePathWithoutExt = filePath
    // remove extension
    .replace(ext, '')
    .split(sep)
    // upperFirst
    .map((item) => _.upperFirst(item.replace(/\W/g, '')))
    .join('');
  return filePathWithoutExt;
};

// @ts-ignore
export default (babel) => {
  const { types: t } = babel;

  return {
    visitor: {
      // @ts-ignore
      ExportDefaultDeclaration: {
        enter(path: any, state: any) {
          const def = path.node.declaration;
          const { cwd, filename } = state.file.opts;
          const relativePath = relative(cwd, filename);

          if (
            /^\.(tsx|jsx)$/.test(extname(relativePath)) &&
            // hidden relativePath
            !/(^|\/)\.[^\/\.]/g.test(relativePath) &&
            !relativePath.includes('node_modules')
          ) {
            let componentName = path2Component(relativePath);
            if (!componentName) {
              return;
            }

            // solve identifier conflict
            const identifiers = Object.keys(path.scope.bindings || {});
            // add index if conflict
            let idx = 0;
            // loop util componentName conflict
            while (identifiers.includes(componentName)) {
              componentName = `${componentName}${idx}`;
              idx += 1;
            }

            // generate component name identifier
            const named = t.identifier(componentName);

            if (t.isArrowFunctionExpression(def)) {
              const varDec = t.variableDeclaration('const', [
                t.variableDeclarator(named, def),
              ]);
              const [varDeclPath] = path.insertBefore(varDec);
              path.scope.registerDeclaration(varDeclPath);
              path.replaceWith(t.exportDefaultDeclaration(named));
            } else if (t.isFunctionDeclaration(def) && !def.id) {
              def.id = named;
            }
          }
        },
      },
    },
  };
};