{"id":28483076,"url":"https://github.com/rainbow-me/metro-plugin-anisotropic-transform","last_synced_at":"2025-06-28T15:30:58.122Z","repository":{"id":71080781,"uuid":"331410799","full_name":"rainbow-me/metro-plugin-anisotropic-transform","owner":"rainbow-me","description":"⚛️ 🧲 Intercept and mutate runtime dependency resolution.","archived":false,"fork":false,"pushed_at":"2024-07-17T19:56:57.000Z","size":400,"stargazers_count":19,"open_issues_count":0,"forks_count":1,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-06-07T21:06:54.215Z","etag":null,"topics":["metro","react-native"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rainbow-me.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-01-20T19:32:21.000Z","updated_at":"2025-04-14T17:52:29.000Z","dependencies_parsed_at":null,"dependency_job_id":"d0485329-9108-4e8f-913d-8ff75f2dad05","html_url":"https://github.com/rainbow-me/metro-plugin-anisotropic-transform","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/rainbow-me/metro-plugin-anisotropic-transform","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rainbow-me%2Fmetro-plugin-anisotropic-transform","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rainbow-me%2Fmetro-plugin-anisotropic-transform/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rainbow-me%2Fmetro-plugin-anisotropic-transform/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rainbow-me%2Fmetro-plugin-anisotropic-transform/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rainbow-me","download_url":"https://codeload.github.com/rainbow-me/metro-plugin-anisotropic-transform/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rainbow-me%2Fmetro-plugin-anisotropic-transform/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":262452123,"owners_count":23313384,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["metro","react-native"],"created_at":"2025-06-07T21:06:39.487Z","updated_at":"2025-06-28T15:30:58.113Z","avatar_url":"https://github.com/rainbow-me.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# metro-plugin-anisotropic-transform\n⚛️  🧲 Intercept and mutate runtime dependency resolution.\n\n## 💪 Motivation\n[`metro-plugin-anisotropic-transform`](.) is a transform plugin for [**React Native**](https://reactnative.dev)'s [**Metro Bundler**](https://github.com/facebook/metro). It is designed to inspect the relationships that exist between dependencies; specifically, those in the `node_modules/` directory which make a cyclic dependence to the root project.\n\nThis transform is designed to fulfill the following functionality:\n  - Suppress cyclic dependencies on the root project, which can lead to drastically increased installation time.\n  - Derisk the possibility of library dependencies relying upon runtime functionality exported by the root project.\n  - Prevent dependencies from squatting on critical functionality exported by other `node_modules`.\n\n### 🤔 How does it work?\nApplications built using [**React Native**](https://reactnative.dev) are forced to resolve **all** module dependencies at bundle time. This is because unlike the [**Node.js**](https://nodejs.org/en/) ecosystem, the entire dependency map of the compiled application must be resolved prior to app distrbution in order translate into a fixed application bundle that can be transported.\n\nThis makes the following impact on the compilation process:\n\n  - Dynamic `require`s are **not possible** in [**React Native**](https://reactnative.dev) when `inlineRequires` is set to `false`. All attempts to `import` and `require`, even those which have been deferred until execution time, must be resolved during the bundle phase.\n    - Note that it _is_ possible to [dynamically `require` in React Native](https://twitter.com/baconbrix/status/1357448848331988994?s=21), so in order to protect the transform from runtime misuse, `inlineRequires` must be set to `false`.\n  - The entire scope of an application's module resolution map can be determined and interrogated at bundle time.\n\n[`metro-plugin-anisotropic-transform`](.) utilizes these restrictions in library resolution to compare and handle relationships between the core application and children of the `node_modules` directory, and in these cases, resolve appropriately. \n\n## 📚 Guide\n\n### 🚀 1. Installing\n\nUsing [**Yarn**](https://yarnpkg.com):\n\n```sh\nyarn add --dev metro-plugin-anisotropic-transform\n```\n\n### 📝 2. Creating a `metro.transform.js`\n\nWe'll create our own custom [**Metro**](https://github.com/facebook/metro) transform to invoke the anisotropic transform.\n\n```javascript\nconst deepmerge = require(\"deepmerge\");\nconst { transform: anisotropic } = require(\"metro-plugin-anisotropic-transform\");\n\nmodule.exports.transform = function ({\n  src,\n  filename,\n  options,\n}) {\n  const opts = deepmerge(options, {\n    customTransformOptions: {\n      [\"metro-plugin-anisotropic-transform\"]: {\n        cyclicDependents: /.+\\/node_modules\\/expo\\/AppEntry\\.js$/,\n        globalScopeFilter: {\n          'react-native-animated-charts': {\n            exceptions: ['my-package'], // optional\n          },\n        },\n      },\n    },\n  });\n  return anisotropic({ src, filename, options: opts });\n};\n```\n\n\u003e **Note:** Here we use `deepmerge` to safely propagate received transform options from the preceding step in the bundler chain.\n\nInside `customTransformOptions`, we declare a child object under the key `metro-plugin-anisotropic-transform` which can be used to specify configuration arguments. In this example, we've defined a simple [`RegExp`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp) to permit a cyclic dependency on `/node_modules/expo/AppEntry.js`, which is required for [**Expo**](https://expo.io) projects. In this instance, any other dependencies in the `node_modules` directory which does not match this pattern will cause the bundler to fail.\n\n\u003e **Note:** In production environments, it is imported to declare the **full system path** to the resolved dependency. This is because bad actors could exploit a simple directory structure to create a _technically allowable_ path, i.e. `node_modules/evil-dangerous-package/node_modules/expo/AppEntry.js`.\n\nAdditionally, we define the `globalScopeFilter` property. This is used to escape any library dependencies from asserting a dependence upon another library in your `node_modules` directory. In this example, the metro bundler will terminate bundling if an included dependency asserts a dependence upon [`react-native-animated-charts`](https://github.com/rainbow-me/react-native-animated-charts).\n\n### 3. ♾️ Applying the Transform\n\nFinally, you'll need to update your `metro.config.js` to invoke the `metro.transform.js` during the bundle phase:\n\n```diff\nmodule.exports = {\n+  transformer: {\n+    babelTransformerPath: require.resolve(\"./metro.transform.js\"),\n+    getTransformOptions: () =\u003e ({ inlineRequires: false, allowOptionalDependencies: false }),\n+  },\n};\n```\n\nIf you're using [**Expo**](https://expo.io), you'll also need to update your `app.json` in addition to updating your `metro.config.js`:\n\n```diff\n{\n  \"expo\": {\n    \"packagerOpts\": {\n+      \"transformer\": \"./metro.transform.js\"\n    }\n  }\n}\n```\n\nAnd that's everything! Now whenever you rebundle your application, your application source will be passed through the anisotropic transform and any cyclic dependencies in your project will be detected.\n\n\u003e ⚠️  **Important!** Whenever you apply any changes to your bundler configuration, you **must** clear the cache by calling `react-native start --reset-cache`.\n\n\n## ⚙️ Options\n\n[`babel-plugin-anisotropic-transform`](.) defaults to the following configuration:\n\n```javascript\n{\n  madge: {\n    includeNpm: true,\n    fileExtensions: [\"js\", \"jsx\", \"ts\", \"tsx\"],\n    detectiveOptions: {\n      es6: {\n        mixedImports: true\n      }\n    },\n  }, \n  cyclicDependents: /a^/, /* by default, do not permit anything */\n  globalScopeFilter: {}, /* no filtering applied */\n  resolve: ({ type, referrer, ...extras }) =\u003e {\n    if (type === 'cyclicDependents') {\n      const {target} = extras;\n      throw new Error(`${name}: Detected a cyclic dependency.  (${referrer} =\u003e ${target})`);\n    } else if (type === 'globalScopeFilter') {\n      const {globalScope} = extras;\n      throw new Error(`${name}: Detected disallowed dependence upon ${globalScope.map(e =\u003e `\"${e}\"`).join(',')}. (${referrer})`);\n    }\n    throw new Error(`Encountered unimplemented type, \"${type}\".`);\n  },\n}\n```\n\n### `madge`\nControls invocation of the [`madge`](https://github.com/pahen/madge) tool, which is used to interrogate module relationships. See [**Madge Configuration**](https://github.com/pahen/madge#configuration).\n\n### `cyclicDependents`\nA [`RegExp`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp) which determines which cyclic dependencies are permitted to exist. By default, none are allowed.\n\n### `globalScopeFilter`\nAn object whose keys map to dependencies in your `node_modules` directory which are not permitted to be included by other dependencies. This is useful for preventing libraries from executing potentially priviledged functionality exported by another module.\n  - At this time, only the keys of this property are consumed by the transform. For future proofing, consider using an empty object `{}`.\n\n### `resolve`\nA function called when the anisotropic platform detects a sensitive relationship. By default, this is configured to `throw` and prevent the bundler from continuing.\n\n## 🌈 License\n[**MIT**](./LICENSE)\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frainbow-me%2Fmetro-plugin-anisotropic-transform","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frainbow-me%2Fmetro-plugin-anisotropic-transform","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frainbow-me%2Fmetro-plugin-anisotropic-transform/lists"}