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.tswith the result oftemplate;package.jsonwith"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:
utils/
├── _reexport.ts
├── package.json
├── scroll.ts
└── date/
├── _reexport.ts
├── package.json
├── getTime.ts
└── format/
├── _reexport.ts
├── package.json
└── formatDate.tsWith utils/*, re-export files would be created only in utils/date.
ts | An array of glob patterns for finding folders within |
ts | A template function that returns the re-export file content. |
ts | A regular expression for excluding files and directories from processing |
ts | If |
ts | If |
ts | A function for validating file content. If it returns |
ts | A template function for the generated re-export file name. Defaults to |
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
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
export async function getOrders() {}
[ARFA-GEN] [write] actions/package.json
[ARFA-GEN] [write] actions/_reexport.ts
[ARFA-GEN] finished in 0ms
// 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
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
export function getTime() {}
[ARFA-GEN] [write] utils/index.ts
[ARFA-GEN] finished in 0ms
// 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
new PluginReexport([
{
entries: ['components/*'],
onlyPackage: true,
fileNameTemplate({ folderName }) {
return `${folderName}.tsx`;
},
template() {
return '';
},
},
]),
Run Sandbox
export function Button() {}
[ARFA-GEN] [write] components/Button/package.json
[ARFA-GEN] finished in 0ms
{ "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
new PluginReexport([
{
entries: ['utils/**'],
template({ files }) {
return files.map(({ name }) => `export * from './${name}';`).join('\n');
},
},
]),
Run Sandbox
export function scroll() {}
export function getTime() {}
export function formatDate() {}
[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
// 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
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
export type TypeOrder = { id: string };
export function formatOrder() {}
[ARFA-GEN] [write] models/package.json
[ARFA-GEN] [write] models/_reexport.ts
[ARFA-GEN] finished in 0ms
// 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
new PluginReexport([
{
entries: ['features/*'],
exclude: /(^_|\.test\.|\.stories\.|\.css$)/,
template({ files }) {
return files.map(({ name }) => `export * from './${name}';`).join('\n');
},
},
]),
Run Sandbox
export async function getOrders() {}
export function Orders() {}
[ARFA-GEN] [write] features/orders/package.json
[ARFA-GEN] [write] features/orders/_reexport.ts
[ARFA-GEN] finished in 0ms
// 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
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
export default function HomePage() {}
[ARFA-GEN] [write] pages/package.json
[ARFA-GEN] [write] pages/_reexport.ts
[ARFA-GEN] finished in 0ms
// 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
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
<svg />
[ARFA-GEN] [write] assets/icons/package.json
[ARFA-GEN] [write] assets/icons/icons.ts
[ARFA-GEN] finished in 0ms
// 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
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
export default class Store {}
[ARFA-GEN] [write] stores/package.json
[ARFA-GEN] [write] stores/RootStore.ts
[ARFA-GEN] finished in 0ms
// 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
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
export default function orders() {}
[ARFA-GEN] [write] store/reducers/package.json
[ARFA-GEN] [write] store/reducers/reducers.ts
[ARFA-GEN] finished in 0ms
// this file is generated
import orders from './orders';
export const reducers = {
orders,
};