Skip to content

V3RON/metro-config-transformers

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

3 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Metro Config Transformers Logo

Metro Config Transformers

Create Metro config transformers that work in all possible combinations

npm version license TypeScript Metro


πŸ”„ Handles objects, functions, promises, and any combination with full type safety



Why this library exists?

Metro config transformers are a powerful way to customize your Metro bundler configuration, but there's a critical problem: there's no standardization for how transformers should work.

The problem

  1. No Standardization: Different transformer libraries handle Metro configs differently. Some expect objects, others expect functions, and some return Promises.

  2. Order dependency issues: Certain transformers need to return a Promise (async operations), which means they must be applied last. However, there's nothing preventing users from calling them first, leading to:

    • Silent failures
    • Runtime errors
    • Frustration and confusion
    • GitHub issues being created unnecessarily
  3. Composition complexity: Manually composing transformers that handle different input/output types (objects, functions, promises) is error-prone and requires deep understanding of Metro's internals.

The solution

This library serves as a foundation for config transformers, ensuring they work correctly in all possible combinations. Developers no longer need to be vigilant about:

  • βœ… Order of transformers - works correctly regardless of composition order
  • βœ… Input/output types - handles objects, functions, promises seamlessly
  • βœ… Async operations - Promise-based transformers work correctly in any position

Installation

npm install metro-config-transformers
# or
yarn add metro-config-transformers
# or
pnpm add metro-config-transformers
# or
bun add metro-config-transformers

Usage

Creating a transformer

Use createMetroConfigTransformer to create a transformer that handles all Metro config formats:

import { createMetroConfigTransformer } from "metro-config-transformers";
import type { ConfigT as MetroConfig } from "metro-config";

const withCustomTransformer = createMetroConfigTransformer(
  (config: MetroConfig) => {
    return {
      ...config,
      resolver: {
        ...config.resolver,
        sourceExts: [...(config.resolver.sourceExts || []), "custom"],
      },
    };
  }
);

// Works with all config formats:
const config1 = withCustomTransformer({
  /* config object */
});
const config2 = withCustomTransformer(() => ({
  /* config object */
}));
const config3 = withCustomTransformer(
  Promise.resolve({
    /* config object */
  })
);
const config4 = withCustomTransformer(async () => ({
  /* config object */
}));

Async transformers

Transformers can be async and work correctly regardless of order:

const withAsyncTransformer = createMetroConfigTransformer(
  async (config: MetroConfig) => {
    const someAsyncData = await fetchSomeData();
    return {
      ...config,
      // Use asyncData in config
    };
  }
);

Transformers with options

Transformers can accept options:

const withOptionsTransformer = createMetroConfigTransformer<{
  projectRoot: string;
}>((config: MetroConfig, options) => {
  return {
    ...config,
    projectRoot: options?.projectRoot || config.projectRoot,
  };
});

// Usage
const config = withOptionsTransformer(baseConfig, { projectRoot: __dirname });

Composing multiple transformers

Use composeMetroConfigTransformers to combine multiple transformers:

import { composeMetroConfigTransformers } from "metro-config-transformers";

const combinedTransformer = composeMetroConfigTransformers(
  withCustomTransformer,
  [withOptionsTransformer, { projectRoot: __dirname }], // Tuple syntax for options
  withAsyncTransformer, // Works correctly even if async!
  anotherTransformer
);

// Apply to your config
module.exports = combinedTransformer(getDefaultConfig(__dirname));

API reference

createMetroConfigTransformer<TOptions>(mutate)

Creates a Metro config transformer that handles all possible config formats.

Parameters:

  • mutate: (config: MetroConfig, options?: TOptions) => MetroConfig | Promise<MetroConfig>
    • Function that performs the actual config transformation
    • Can be sync or async

Returns: A transformer function that accepts any Metro config format and preserves its "shape":

  • Object input β†’ Object output (or Promise if transformation is async)
  • Promise input β†’ Promise output
  • Function input β†’ Function output
  • Async function input β†’ Async function output
  • Promise of function input β†’ Promise of function output

composeMetroConfigTransformers(...entries)

Composes multiple transformers together using a Babel-like tuple syntax.

Parameters:

  • entries: Array of TransformerEntry
    • Can be a plain transformer function: withTransformer
    • Or a tuple with options: [withTransformer, options]

Returns: A composed transformer function that applies all transformers in sequence

Type:

type TransformerEntry = Transformer<void> | [Transformer<TOptions>, TOptions];

Contributing

Contributions are welcome! Please feel free to submit a pull request.

License

MIT Β© Szymon Chmal

Related

About

Create Metro config transformers that work in all possible combinations

Topics

Resources

License

Stars

Watchers

Forks

Contributors