Skip to content

Providing a cache key when custom transformers use external inputs #918

Open

Description

@robhogan

Raised by @cancit in a Tweet.

Do you want to request a feature or report a bug?
Feature/docs

What is the current behavior?
When using custom plugins (like babel-plugin-transform-inline-environment-variables) outside factors may influence the build, which breaks transform cache invalidation.

What is the expected behavior?
We probably can't do anything automatically for arbitrary plugins, but at the very least there should be a documented way to tell the transform cache about outside factors. We have cacheVersion, but it's not well documented ("There is generally no need to set this explicitly") and feels surprising.

A way to explicitly specify/allowlist environment variables in particular (like we do with Buck) might be worthwhile, since that's likely to be the most common outside factor.

Workaround

// metro.config.js
module.exports = {
  cacheVersion: [process.env.VAR_ONE, process.env.VAR_TWO].join('-') // Hash this if the values aren't safe for a file name
  // ...
}

Activity

  1. bigcupcoffee commented on Feb 24, 2026

    @bigcupcoffee

    Hey @robhogan, does metro currently include babel plugins in transform cache keys? It looks like it doesn't so even removing/adding a babel plugin has no effect on the cache. Would it be possible to include those?

  2. robhogan commented on Feb 24, 2026

    @robhogan
    CollaboratorAuthor

    @bigcupcoffee - not currently, but #1638 looks to address that.

    I need to have a closer look to see whether that's the right wiring or whether we can lift it up to the main thread, but I'd expect to merge that in some form before the next release.

  3. bigcupcoffee commented on Feb 24, 2026

    @bigcupcoffee

    Thanks for the response! I see, that will handle the main cases including dynamically computed plugin config values, pretty nice!

    I was also interested if it's possible to only include plugins that affected the file in its cache key? E.g. injecting a variable usually affects a single export file, but if it's done via a babel plugin the entire cache will get reset if the value changes

    From what I've seen this doesn't exactly look feasible to do, in which case if we still want some caching with such plugin we better have something else do that plugin's job?

  4. robhogan commented on Feb 24, 2026

    @robhogan
    CollaboratorAuthor

    Yeah, that's complicated to do efficiently - Metro effectively treats the transformer as a black box that's meant to be a pure function of explicit inputs + whatever the transformer wants to include in its getCacheKey (which is not a function of the file), we have no mechanism to express "this file is invariant under this input".

    The basic problem here is simple to state - how do you know what plugins will affect a particular file, before you've even parsed it?

    Maybe you can't do that, but once you've transformed once you can potentially collect inputs that were never read, and store the cache in such a way that you can hit that result the next time (even under different inputs) and work out whether you can reuse it.

    That's both a big change to the way the cache works though, and has really gnarly edge cases - what if the only reason an input isn't used is because another plugin eliminated some code that would've used it, conditional on a different input? Similarly, what if one plugin conditionally injects code that uses another input?

    We'd need a different API for consuming transform inputs that we could track, similar to babel.caller() but possibly stricter - like react hooks, something you couldn't consume conditionally. It's an interesting project, to be sure, but it is a project.

  5. bigcupcoffee commented on Feb 25, 2026

    @bigcupcoffee

    Also I'm not sure if #1638 covers the case of babel plugin version change? In my experience nowadays a common case for --reset-cache is when we bump reanimated's worklets plugin and it explicitly checks the versions that fails with cache in runtime, would be great to ensure that's covered as it also applies to any other plugin that gets bumped

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions