Skip to content

PluginScaffold ​

Creates a set of files once, based on a marker file, without updating or checking the contents of the created files.

If any target file already exists, the plugin leaves the marker in place and reports an error. Once all files have been written successfully, the marker is deleted.

The common headerTemplate is not added to these files.

ts
entries: Array<string>

An array of glob patterns for finding files within rootDir, such as ['features/*/.feature']

ts
createFiles: (params: {
  folderName: string;
  folderPath: string;
}) => Array<{
  filePath: string;
  content: string;
}>

Returns a set of files for the folder containing the marker.

folderPath and filePath must be absolute paths; otherwise, the plugin reports an error

Example: New feature

The marker creates the UI and public API for a feature slice. Once the marker is removed, the component can be edited manually: the plugin no longer overwrites it

ts
new PluginScaffold([
  {
    entries: ['features/*/.feature'],
    createFiles({ folderName, folderPath }) {
      function upperFirst(value: string) {
        return `${value.charAt(0).toUpperCase()}${value.slice(1)}`;
      }

      const componentName = upperFirst(folderName);

      return [
        {
          filePath: path.resolve(folderPath, `ui/${componentName}.tsx`),
          content: `export function ${componentName}() {
  return <button type="button">${componentName}</button>;
}`,
        },
        {
          filePath: path.resolve(folderPath, 'index.ts'),
          content: `export { ${componentName} } from './ui/${componentName}';`,
        },
      ];
    },
  },
]),
Run Sandbox
USER: features/addToCart/.feature was created with content:
text
ARFA Generator shows logs:
txt
[ARFA-GEN] [write] features/addToCart/ui/AddToCart.tsx
[ARFA-GEN] [write] features/addToCart/index.ts
[ARFA-GEN] [remove] features/addToCart/.feature
[ARFA-GEN] finished in 0ms
ARFA Generator generated files:
tsx
export function AddToCart() {
  return <button type="button">AddToCart</button>;
}

Example: Next.js App Router route

A marker in the route folder creates page.tsx and loading.tsx. If the marker is added again while page.tsx already exists, the plugin preserves both files and reports a conflict

ts
new PluginScaffold([
  {
    entries: ['app/**/.page'],
    createFiles({ folderName, folderPath }) {
      return [
        {
          filePath: path.resolve(folderPath, 'page.tsx'),
          content: `export default function Page() {
  return <main><h1>${folderName}</h1></main>;
}`,
        },
        {
          filePath: path.resolve(folderPath, 'loading.tsx'),
          content: `export default function Loading() {
  return <p>Loading...</p>;
}`,
        },
      ];
    },
  },
]),
Run Sandbox
USER: app/orders/.page was created with content:
text
ARFA Generator shows logs:
txt
[ARFA-GEN] [write] app/orders/page.tsx
[ARFA-GEN] [write] app/orders/loading.tsx
[ARFA-GEN] [remove] app/orders/.page
[ARFA-GEN] finished in 0ms
ARFA Generator generated files:
tsx
export default function Page() {
  return <main><h1>orders</h1></main>;
}

Example: Component with CSS and Storybook

A single marker creates a React component, a CSS module, and a Storybook story alongside it

ts
new PluginScaffold([
  {
    entries: ['shared/ui/*/.component'],
    createFiles({ folderName, folderPath }) {
      return [
        {
          filePath: path.resolve(folderPath, `${folderName}.tsx`),
          content: `import styles from './${folderName}.module.css';

type Props${folderName} = { label: string };

export function ${folderName}({ label }: Props${folderName}) {
  return <button className={styles.root} type="button">{label}</button>;
}`,
        },
        {
          filePath: path.resolve(folderPath, `${folderName}.module.css`),
          content: `.root {
  padding: 0.5rem 1rem;
}`,
        },
        {
          filePath: path.resolve(folderPath, `${folderName}.stories.tsx`),
          content: `import type { Meta, StoryObj } from '@storybook/react';

import { ${folderName} } from './${folderName}';

const meta = { component: ${folderName} } satisfies Meta<typeof ${folderName}>;

export default meta;

type TypeStory = StoryObj<typeof meta>;

export const Primary: TypeStory = { args: { label: '${folderName}' } };`,
        },
      ];
    },
  },
]),
Run Sandbox
USER: shared/ui/Button/.component was created with content:
text
ARFA Generator shows logs:
txt
[ARFA-GEN] [write] shared/ui/Button/Button.tsx
[ARFA-GEN] [write] shared/ui/Button/Button.module.css
[ARFA-GEN] [write] shared/ui/Button/Button.stories.tsx
[ARFA-GEN] [remove] shared/ui/Button/.component
[ARFA-GEN] finished in 0ms
ARFA Generator generated files:
tsx
import styles from './Button.module.css';

type PropsButton = { label: string };

export function Button({ label }: PropsButton) {
  return <button className={styles.root} type="button">{label}</button>;
}

MIT License