Skip to content

PluginReexport ​

Generates re-export barrel files and local package.json files for selected folders. It can also be used to create a RootStore, a reducers map, an object containing images or icons, and more.

By default, the plugin creates two files for each matched folder:

  • _reexport.ts with the result of template;
  • package.json with "main" and "types" pointing to the re-export file.

The depth of re-export generation depends on entries: utils/* selects only the immediate subfolders of utils, while utils/** selects utils itself and all nested folders. With utils/**, the generated file structure looks like this:

text
utils/
├── _reexport.ts
├── package.json
├── scroll.ts
└── date/
    ├── _reexport.ts
    ├── package.json
    ├── getTime.ts
    └── format/
        ├── _reexport.ts
        ├── package.json
        └── formatDate.ts

With utils/*, re-export files would be created only in utils/date.

ts
entries: Array<string>

An array of glob patterns for finding folders within rootDir

ts
template: (params: { 
  folderName: string, 
  files: Array<{
    ext: string;
    name: string;
    fileName: string; 
  }> 
}) => string

A template function that returns the re-export file content. files is a sorted list of immediate child files and folders

ts
exclude?: RegExp

A regular expression for excluding files and directories from processing

ts
noPackage?: boolean

If true, package.json is not generated

ts
onlyPackage?: boolean

If true, only package.json is generated, without a re-export file

ts
validateContent?: (params: {
  ext: string;
  name: string;
  content: string;
  fileName: string;
  folderName: string;
}) => boolean

A function for validating file content. If it returns false, the file is not added to the re-exports. Only files are checked; child folders are added without content validation

ts
fileNameTemplate?: (params: { 
  folderName: string 
}) => string

A template function for the generated re-export file name. Defaults to _reexport.ts

Example: File content validation

Only files that export a function with the same name as the file are included in the re-export file, allowing naming checks and content validation

ts
new PluginReexport([
  {
    entries: ['actions'],
    template({ files }) {
      return files.map(({ name }) => `export * from './${name}';`).join('\n');
    },
    validateContent({ content, name }) {
      return content.includes(`export async function ${name}(`);
    },
  },
]),
Run Sandbox
USER: actions/getOrders.ts was created with content:
ts
export async function getOrders() {}
ARFA Generator shows logs:
txt
[ARFA-GEN] [write] actions/package.json
[ARFA-GEN] [write] actions/_reexport.ts
[ARFA-GEN] finished in 0ms
ARFA Generator generated files:
ts
// this file is generated

export * from './getOrders';

Example: index.ts instead of _reexport.ts and package.json

This configuration generates only index.ts, with additional filtering enabled

ts
new PluginReexport([
  {
    entries: ['utils'],
    exclude: /\.css$/,
    noPackage: true,
    fileNameTemplate() {
      return `index.ts`;
    },
    template({ files }) {
      return files.map(({ name }) => `export * from './${name}';`).join('\n');
    },
  },
]),
Run Sandbox
USER: utils/getTime.ts was created with content:
ts
export function getTime() {}
ARFA Generator shows logs:
txt
[ARFA-GEN] [write] utils/index.ts
[ARFA-GEN] finished in 0ms
ARFA Generator generated files:
ts
// this file is generated

export * from './getTime';

Example: package.json points to a specific file

In component folders, keeping a single package.json instead of index.ts is often more convenient. In this setup, validateContent cannot be used to validate the main file

ts
new PluginReexport([
  {
    entries: ['components/*'],
    onlyPackage: true,
    fileNameTemplate({ folderName }) {
      return `${folderName}.tsx`;
    },
    template() {
      return '';
    },
  },
]),
Run Sandbox
USER: components/Button/Button.tsx was created with content:
tsx
export function Button() {}
ARFA Generator shows logs:
txt
[ARFA-GEN] [write] components/Button/package.json
[ARFA-GEN] finished in 0ms
ARFA Generator generated files:
json
{ "main": "Button.tsx", "types": "Button.tsx" }

Example: Nested re-export generation

Creates _reexport.ts and package.json in utils and all nested folders. This makes nested exports available through a single import from utils

ts
new PluginReexport([
  {
    entries: ['utils/**'],
    template({ files }) {
      return files.map(({ name }) => `export * from './${name}';`).join('\n');
    },
  },
]),
Run Sandbox
USER: utils/scroll.ts was created with content:
ts
export function scroll() {}
USER: utils/date/getTime.ts was created with content:
ts
export function getTime() {}
USER: utils/date/format/formatDate.ts was created with content:
ts
export function formatDate() {}
ARFA Generator shows logs:
txt
[ARFA-GEN] [write] utils/package.json
[ARFA-GEN] [write] utils/_reexport.ts
[ARFA-GEN] [write] utils/date/package.json
[ARFA-GEN] [write] utils/date/_reexport.ts
[ARFA-GEN] [write] utils/date/format/package.json
[ARFA-GEN] [write] utils/date/format/_reexport.ts
[ARFA-GEN] finished in 0ms
ARFA Generator generated files:
ts
// this file is generated

export * from './date';
export * from './scroll';

Example: Separate re-exports for types and values

Files with the .types.ts suffix are re-exported through export type *, while the others remain runtime exports

ts
new PluginReexport([
  {
    entries: ['models'],
    template({ files }) {
      return files
        .map(({ name, fileName }) => {
          const exportType = fileName.endsWith('.types.ts') ? ' type' : '';

          return `export${exportType} * from './${name}';`;
        })
        .join('\n');
    },
  },
]),
Run Sandbox
USER: models/order.types.ts was created with content:
ts
export type TypeOrder = { id: string };
USER: models/formatOrder.ts was created with content:
ts
export function formatOrder() {}
ARFA Generator shows logs:
txt
[ARFA-GEN] [write] models/package.json
[ARFA-GEN] [write] models/_reexport.ts
[ARFA-GEN] finished in 0ms
ARFA Generator generated files:
ts
// this file is generated

export * from './formatOrder';
export type * from './order.types';

Example: Public API for feature modules

Creates a public entry point for each feature module, excluding tests, stories, styles, and private files

ts
new PluginReexport([
  {
    entries: ['features/*'],
    exclude: /(^_|\.test\.|\.stories\.|\.css$)/,
    template({ files }) {
      return files.map(({ name }) => `export * from './${name}';`).join('\n');
    },
  },
]),
Run Sandbox
USER: features/orders/api.ts was created with content:
ts
export async function getOrders() {}
USER: features/orders/ui.tsx was created with content:
tsx
export function Orders() {}
ARFA Generator shows logs:
txt
[ARFA-GEN] [write] features/orders/package.json
[ARFA-GEN] [write] features/orders/_reexport.ts
[ARFA-GEN] finished in 0ms
ARFA Generator generated files:
ts
// this file is generated

export * from './api';
export * from './ui';

Example: Lazy loading React pages

The entry file creates a React.lazy component for each page and updates when files are added or deleted

ts
new PluginReexport([
  {
    entries: ['pages'],
    exclude: /(\.test\.tsx$|\.stories\.tsx$|\.css$)/,
    template({ files }) {
      const pages = files.filter(({ ext }) => ext === '.tsx');
      const exports = pages
        .map(({ name }) => `export const ${name} = lazy(() => import('./${name}'));`)
        .join('\n');

      return `import { lazy } from 'react';\n\n${exports}`;
    },
  },
]),
Run Sandbox
USER: pages/HomePage.tsx was created with content:
tsx
export default function HomePage() {}
ARFA Generator shows logs:
txt
[ARFA-GEN] [write] pages/package.json
[ARFA-GEN] [write] pages/_reexport.ts
[ARFA-GEN] finished in 0ms
ARFA Generator generated files:
ts
// this file is generated

import { lazy } from 'react';

export const HomePage = lazy(() => import('./HomePage'));

Example: Asset re-exports with tree-shaking

Bundlers generally treat svg, png, and similar files as having a default export. In this example, the re-export file is named after the folder

ts
new PluginReexport([
  {
    entries: ['assets/icons'],
    fileNameTemplate({ folderName }) {
      return `${folderName}.ts`;
    },
    template({ files }) {
      return files.map(({ name, fileName }) => `export { default as ${name} } from './${fileName}';`).join('\n');
    },
  },
]),
Run Sandbox
USER: assets/icons/logo.svg was created with content:
txt
<svg />
ARFA Generator shows logs:
txt
[ARFA-GEN] [write] assets/icons/package.json
[ARFA-GEN] [write] assets/icons/icons.ts
[ARFA-GEN] finished in 0ms
ARFA Generator generated files:
ts
// this file is generated

export { default as logo } from './logo.svg';

Example: Re-exports as a single object or class

In some cases, strict variable naming within files is unnecessary (here, every file uses Store), because meaningful names are derived from the file names

ts
new PluginReexport([
  {
    entries: ['stores'],
    fileNameTemplate() {
      return `RootStore.ts`;
    },
    template({ files }) {
      function upperFirst(value: string) {
        return `${value.charAt(0).toUpperCase()}${value.slice(1)}`;
      }

      const imports = files
        .map(({ name, fileName }) => `import { default as ${upperFirst(name)} } from './${fileName}';`)
        .join('\n');

      const fields = files.map(({ name }) => `  ${name} = new ${upperFirst(name)}();`).join('\n');

      return `${imports}\n\nexport class RootStore {\n${fields}\n}`;
    },
  },
]),
Run Sandbox
USER: stores/user.ts was created with content:
ts
export default class Store {}
ARFA Generator shows logs:
txt
[ARFA-GEN] [write] stores/package.json
[ARFA-GEN] [write] stores/RootStore.ts
[ARFA-GEN] finished in 0ms
ARFA Generator generated files:
ts
// this file is generated

import { default as User } from './user.ts';

export class RootStore {
  user = new User();
}

Example: Building a reducer map

The same aggregation pattern can be used to assemble Redux reducers: each field name is derived from the file name

ts
new PluginReexport([
  {
    entries: ['store/reducers'],
    fileNameTemplate() {
      return `reducers.ts`;
    },
    template({ files }) {
      const imports = files.map(({ name }) => `import ${name} from './${name}';`).join('\n');
      const fields = files.map(({ name }) => `  ${name},`).join('\n');

      return `${imports}\n\nexport const reducers = {\n${fields}\n};`;
    },
  },
]),
Run Sandbox
USER: store/reducers/orders.ts was created with content:
ts
export default function orders() {}
ARFA Generator shows logs:
txt
[ARFA-GEN] [write] store/reducers/package.json
[ARFA-GEN] [write] store/reducers/reducers.ts
[ARFA-GEN] finished in 0ms
ARFA Generator generated files:
ts
// this file is generated

import orders from './orders';

export const reducers = {
  orders,
};

MIT License