diff --git a/404.html b/404.html index cbcac47dbc..31d60b9541 100644 --- a/404.html +++ b/404.html @@ -13,7 +13,7 @@ - + @@ -21,7 +21,7 @@
Skip to main content

Page Not Found

We could not find what you were looking for.

Please contact the owner of the site that linked you to the original URL and let them know their link is broken.

- + \ No newline at end of file diff --git a/_src/Configuration.md b/_src/Configuration.md index f62e22b8fc..b61517d038 100644 --- a/_src/Configuration.md +++ b/_src/Configuration.md @@ -103,6 +103,14 @@ A list of directories outside of [`projectRoot`](#projectroot) that can contain Despite the naming of this option, it isn't related solely to file watching. Even in an offline build (for example, in CI), all files must be visible to Metro through the combination of `watchFolders` and `projectRoot`. ::: +:::info + +Note that, as with any other file Metro needs to resolve, targets of any symlinks within your `watchFolders` *must also be within `watchFolders`* and not otherwise excluded. + +If you have a Metro project within a workspace, such as a [Yarn workspace](https://classic.yarnpkg.com/lang/en/docs/workspaces/) (a subdirectory of a Yarn workspace root), it's likely you'll want to include your workspace *root* path in your configured `watchFolders` so that Metro can resolve other workspaces or hoisted `node_modules`. Similarly, to use [linked packages](https://classic.yarnpkg.com/lang/en/docs/cli/link/), you'll need to list those package source locations (or a containing directory) in `watchFolders`. + +::: + #### `transformerPath` Type: `string` @@ -391,30 +399,6 @@ In a future release of Metro, this option will become `true` by default. --- -#### `unstable_enableSymlinks`
Experimental
- -Type: `boolean` - -Enable experimental support for projects containing symbolic links (symlinks). - -When enabled, Metro traverses symlinks during module and asset [resolution](./Resolution.md), instead of ignoring symlinks. Note that, as with any other file Metro needs to resolve, the symlink target *must be within configured [watched folders](#watchfolders)* and not otherwise excluded. - -Defaults to `true` since Metro v0.79.0. - -:::info - -For example, if you have a Metro project within a [Yarn workspace](https://classic.yarnpkg.com/lang/en/docs/workspaces/) (a subdirectory of a Yarn workspace root), it's likely you'll want to include your workspace *root* path in your configured [`watchFolders`](#watchfolders) so that Metro can resolve other workspaces or hoisted `node_modules`. Similarly, to use [linked packages](https://classic.yarnpkg.com/lang/en/docs/cli/link/), you'll need to list those package source locations (or a containing directory) in [`watchFolders`](#watchfolders). - -::: - -:::note - -In a future release of Metro, this option will be removed (symlink support will be always-on). - -::: - ---- - ### Transformer Options diff --git a/assets/js/163e71e1.40d4e4cc.js b/assets/js/163e71e1.40d4e4cc.js new file mode 100644 index 0000000000..4138c6b6eb --- /dev/null +++ b/assets/js/163e71e1.40d4e4cc.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkmetro_website=self.webpackChunkmetro_website||[]).push([[662],{3905:(e,n,t)=>{t.r(n),t.d(n,{MDXContext:()=>s,MDXProvider:()=>u,mdx:()=>x,useMDXComponents:()=>p,withMDXComponents:()=>m});var r=t(67294);function o(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function a(){return a=Object.assign||function(e){for(var n=1;n=0||(o[t]=e[t]);return o}(e,n);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(o[t]=e[t])}return o}var s=r.createContext({}),m=function(e){return function(n){var t=p(n.components);return r.createElement(e,a({},n,{components:t}))}},p=function(e){var n=r.useContext(s),t=n;return e&&(t="function"==typeof e?e(n):l(l({},n),e)),t},u=function(e){var n=p(e.components);return r.createElement(s.Provider,{value:n},e.children)},c="mdxType",h={inlineCode:"code",wrapper:function(e){var n=e.children;return r.createElement(r.Fragment,{},n)}},f=r.forwardRef((function(e,n){var t=e.components,o=e.mdxType,a=e.originalType,i=e.parentName,s=d(e,["components","mdxType","originalType","parentName"]),m=p(t),u=o,c=m["".concat(i,".").concat(u)]||m[u]||h[u]||a;return t?r.createElement(c,l(l({ref:n},s),{},{components:t})):r.createElement(c,l({ref:n},s))}));function x(e,n){var t=arguments,o=n&&n.mdxType;if("string"==typeof e||o){var a=t.length,i=new Array(a);i[0]=f;var l={};for(var d in n)hasOwnProperty.call(n,d)&&(l[d]=n[d]);l.originalType=e,l[c]="string"==typeof e?e:o,i[1]=l;for(var s=2;s{t.r(n),t.d(n,{assets:()=>m,contentTitle:()=>d,default:()=>h,frontMatter:()=>l,metadata:()=>s,toc:()=>p});var r=t(87462),o=t(63366),a=(t(67294),t(3905)),i=["components"],l={id:"getting-started",title:"Getting Started"},d=void 0,s={unversionedId:"getting-started",id:"getting-started",title:"Getting Started",description:"Install Metro using npm:",source:"@site/../docs/GettingStarted.md",sourceDirName:".",slug:"/getting-started",permalink:"/docs/getting-started",draft:!1,editUrl:"https://github.com/facebook/metro/edit/main/docs/../docs/GettingStarted.md",tags:[],version:"current",lastUpdatedAt:1726493799,formattedLastUpdatedAt:"Sep 16, 2024",frontMatter:{id:"getting-started",title:"Getting Started"},sidebar:"docs",next:{title:"Concepts",permalink:"/docs/concepts"}},m={},p=[{value:"Running metro",id:"running-metro",level:2},{value:"Running Programmatically",id:"running-programmatically",level:3},{value:"Method runMetro(config)",id:"method-runmetroconfig",level:3},{value:"Method runServer(config, options)",id:"method-runserverconfig-options",level:3},{value:"Options",id:"options",level:4},{value:"Method runBuild(config, options)",id:"method-runbuildconfig-options",level:3},{value:"Options",id:"options-1",level:4},{value:"Method createConnectMiddleware(config)",id:"method-createconnectmiddlewareconfig",level:3},{value:"Options",id:"options-2",level:4},{value:"Available options",id:"available-options",level:2},{value:"Configuration",id:"configuration",level:3},{value:"URL and bundle request",id:"url-and-bundle-request",level:2},{value:"Assets",id:"assets",level:3},{value:"Bundle",id:"bundle",level:3},{value:"Source maps",id:"source-maps",level:3},{value:"JavaScript transformer",id:"javascript-transformer",level:2},{value:"Method transform(module)",id:"method-transformmodule",level:3},{value:"Method getCacheKey()",id:"method-getcachekey",level:3}],u={toc:p},c="wrapper";function h(e){var n=e.components,t=(0,o.Z)(e,i);return(0,a.mdx)(c,(0,r.Z)({},u,t,{components:n,mdxType:"MDXLayout"}),(0,a.mdx)("p",null,"Install Metro using ",(0,a.mdx)("a",{parentName:"p",href:"https://www.npmjs.com/"},(0,a.mdx)("inlineCode",{parentName:"a"},"npm")),":"),(0,a.mdx)("pre",null,(0,a.mdx)("code",{parentName:"pre",className:"language-bash"},"npm install --save-dev metro metro-core\n")),(0,a.mdx)("p",null,"Or via ",(0,a.mdx)("a",{parentName:"p",href:"https://yarnpkg.com/"},(0,a.mdx)("inlineCode",{parentName:"a"},"yarn")),":"),(0,a.mdx)("pre",null,(0,a.mdx)("code",{parentName:"pre",className:"language-bash"},"yarn add --dev metro metro-core\n")),(0,a.mdx)("h2",{id:"running-metro"},"Running ",(0,a.mdx)("inlineCode",{parentName:"h2"},"metro")),(0,a.mdx)("p",null,"You can run Metro by either running the ",(0,a.mdx)("a",{parentName:"p",href:"/docs/cli"},"CLI")," or by calling it programmatically."),(0,a.mdx)("h3",{id:"running-programmatically"},"Running Programmatically"),(0,a.mdx)("p",null,"First, require the module by doing:"),(0,a.mdx)("pre",null,(0,a.mdx)("code",{parentName:"pre",className:"language-js"},"const Metro = require('metro');\n")),(0,a.mdx)("p",null,"Within the object returned, several main methods are given:"),(0,a.mdx)("h3",{id:"method-runmetroconfig"},"Method ",(0,a.mdx)("inlineCode",{parentName:"h3"},"runMetro(config)")),(0,a.mdx)("p",null,"Given the config, a ",(0,a.mdx)("inlineCode",{parentName:"p"},"metro-server")," will be returned. You can then hook this into a proper HTTP(S) server by using its ",(0,a.mdx)("inlineCode",{parentName:"p"},"processRequest")," method:"),(0,a.mdx)("pre",null,(0,a.mdx)("code",{parentName:"pre",className:"language-js"},"'use strict';\n\nconst http = require('http');\nconst Metro = require('metro');\n\n// We first load the config from the file system\nMetro.loadConfig().then(async (config) => {\n const metroBundlerServer = await Metro.runMetro(config);\n\n const httpServer = http.createServer(\n metroBundlerServer.processRequest.bind(metroBundlerServer),\n );\n\n httpServer.listen(8081);\n});\n")),(0,a.mdx)("p",null,"In order to be also compatible with Express apps, ",(0,a.mdx)("inlineCode",{parentName:"p"},"processRequest")," will also call its third parameter when the request could not be handled by Metro. This allows you to integrate the server with your existing server, or to extend a new one:"),(0,a.mdx)("pre",null,(0,a.mdx)("code",{parentName:"pre",className:"language-js"},"const httpServer = http.createServer((req, res) => {\n metroBundlerServer.processRequest(req, res, () => {\n // Metro does not know how to handle the request.\n });\n});\n")),(0,a.mdx)("p",null,"If you are using ",(0,a.mdx)("a",{parentName:"p",href:"http://expressjs.com/"},"Express"),", you can just pass ",(0,a.mdx)("inlineCode",{parentName:"p"},"processRequest")," as a middleware:"),(0,a.mdx)("pre",null,(0,a.mdx)("code",{parentName:"pre",className:"language-js"},"const express = require('express');\nconst app = express();\n\napp.use(\n metroBundlerServer.processRequest.bind(metroBundlerServer),\n);\n\napp.listen(8081);\n")),(0,a.mdx)("h3",{id:"method-runserverconfig-options"},"Method ",(0,a.mdx)("inlineCode",{parentName:"h3"},"runServer(config, options)")),(0,a.mdx)("p",null,"Starts a development server based on the given configuration and options. Returns the server.\nWe recommend using ",(0,a.mdx)("inlineCode",{parentName:"p"},"runMetro")," instead of ",(0,a.mdx)("inlineCode",{parentName:"p"},"runServer"),", ",(0,a.mdx)("inlineCode",{parentName:"p"},"runMetro")," calls this function."),(0,a.mdx)("h4",{id:"options"},"Options"),(0,a.mdx)("ul",null,(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("inlineCode",{parentName:"li"},"host (string)"),": Where to host the server on."),(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("inlineCode",{parentName:"li"},"onReady (Function)"),": Called when the server is ready to serve requests."),(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("inlineCode",{parentName:"li"},"secure (boolean)"),": ",(0,a.mdx)("strong",{parentName:"li"},"DEPRECATED")," Whether the server should run on ",(0,a.mdx)("inlineCode",{parentName:"li"},"https")," instead of ",(0,a.mdx)("inlineCode",{parentName:"li"},"http"),"."),(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("inlineCode",{parentName:"li"},"secureKey (string)"),": ",(0,a.mdx)("strong",{parentName:"li"},"DEPRECATED")," The key to use for ",(0,a.mdx)("inlineCode",{parentName:"li"},"https")," when ",(0,a.mdx)("inlineCode",{parentName:"li"},"secure")," is on."),(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("inlineCode",{parentName:"li"},"secureCert (string)"),": ",(0,a.mdx)("strong",{parentName:"li"},"DEPRECATED")," The cert to use for ",(0,a.mdx)("inlineCode",{parentName:"li"},"https")," when ",(0,a.mdx)("inlineCode",{parentName:"li"},"secure")," is on."),(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("inlineCode",{parentName:"li"},"secureServerOptions (Object)"),": The options object to pass to Metro's HTTPS server. The presence of this object will make Metro's server run on ",(0,a.mdx)("inlineCode",{parentName:"li"},"https"),". Refer to the ",(0,a.mdx)("a",{parentName:"li",href:"https://nodejs.org/api/https.html#https_https_createserver_options_requestlistener"},"Node docs")," for valid options."),(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("inlineCode",{parentName:"li"},"waitForBundler (boolean)"),": Whether to wait for the bundler to finish initializing before returning the server instance.")),(0,a.mdx)("pre",null,(0,a.mdx)("code",{parentName:"pre",className:"language-js"},"const config = await Metro.loadConfig();\n\nawait Metro.runServer(config);\n")),(0,a.mdx)("pre",null,(0,a.mdx)("code",{parentName:"pre",className:"language-js"},"const fs = require('fs');\n\nconst config = await Metro.loadConfig();\n\nawait Metro.runServer(config, {\n secureServerOptions: {\n ca: fs.readFileSync('path/to/ca'),\n cert: fs.readFileSync('path/to/cert'),\n key: fs.readFileSync('path/to/key'),\n }\n});\n")),(0,a.mdx)("h3",{id:"method-runbuildconfig-options"},"Method ",(0,a.mdx)("inlineCode",{parentName:"h3"},"runBuild(config, options)")),(0,a.mdx)("p",null,"Given a configuration and a set of options that you would typically pass to a server, plus a set of options specific to the bundle itself, a bundle will be built. The return value is a Promise that resolves to an object with two properties, ",(0,a.mdx)("inlineCode",{parentName:"p"},"code")," and ",(0,a.mdx)("inlineCode",{parentName:"p"},"map"),". This is useful at build time."),(0,a.mdx)("h4",{id:"options-1"},"Options"),(0,a.mdx)("ul",null,(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("inlineCode",{parentName:"li"},"dev (boolean)"),": Create a development version of the build (",(0,a.mdx)("inlineCode",{parentName:"li"},"process.env.NODE_ENV = 'development'"),")."),(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("inlineCode",{parentName:"li"},"entry (string)"),": Pointing to the entry file to bundle."),(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("inlineCode",{parentName:"li"},"onBegin (Function)"),": Called when the bundling starts."),(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("inlineCode",{parentName:"li"},"onComplete (Function)"),": Called when the bundling finishes."),(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("inlineCode",{parentName:"li"},"onProgress (Function)"),": Called during the bundle, every time there's new information available about the module count/progress."),(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("inlineCode",{parentName:"li"},"minify (boolean)"),": Whether Metro should minify the bundle."),(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("inlineCode",{parentName:"li"},"out (string)"),": Path to the output bundle."),(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("inlineCode",{parentName:"li"},"platform ('web' | 'android' | 'ios')"),": Which platform to bundle for if a list of platforms is provided."),(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("inlineCode",{parentName:"li"},"sourceMap (boolean)"),": Whether Metro should generate source maps."),(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("inlineCode",{parentName:"li"},"sourceMapUrl (string)"),": URL where the source map can be found. It defaults to the same same URL as the bundle, but changing the extension from ",(0,a.mdx)("inlineCode",{parentName:"li"},".bundle")," to ",(0,a.mdx)("inlineCode",{parentName:"li"},".map"),". When ",(0,a.mdx)("inlineCode",{parentName:"li"},"inlineSourceMap")," is ",(0,a.mdx)("inlineCode",{parentName:"li"},"true"),", this property has no effect.")),(0,a.mdx)("pre",null,(0,a.mdx)("code",{parentName:"pre",className:"language-js"},"const config = await Metro.loadConfig();\n\nawait Metro.runBuild(config, {\n entry: 'index.js',\n platform: 'ios',\n minify: true,\n out: '/Users/Metro/metro-ios.js'\n});\n")),(0,a.mdx)("h3",{id:"method-createconnectmiddlewareconfig"},"Method ",(0,a.mdx)("inlineCode",{parentName:"h3"},"createConnectMiddleware(config)")),(0,a.mdx)("p",null,"Instead of creating the full server, creates a Connect middleware that answers to bundle requests. This middleware can then be plugged into your own servers. The ",(0,a.mdx)("inlineCode",{parentName:"p"},"port")," parameter is optional and only used for logging purposes."),(0,a.mdx)("h4",{id:"options-2"},"Options"),(0,a.mdx)("ul",null,(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("inlineCode",{parentName:"li"},"port (number)"),": Port for the Connect middleware (only for logging purposes).")),(0,a.mdx)("pre",null,(0,a.mdx)("code",{parentName:"pre",className:"language-js"},"const Metro = require('metro');\nconst express = require('express');\nconst app = express();\nconst server = require('http').Server(app);\n\nMetro.loadConfig().then(async config => {\n const connectMiddleware = await Metro.createConnectMiddleware(config);\n const {server: {port}} = config;\n\n app.use(connectMiddleware.middleware);\n server.listen(port);\n connectMiddleware.attachHmrServer(server);\n});\n")),(0,a.mdx)("h2",{id:"available-options"},"Available options"),(0,a.mdx)("h3",{id:"configuration"},"Configuration"),(0,a.mdx)("p",null,"Check ",(0,a.mdx)("a",{parentName:"p",href:"/docs/configuration"},"Configuring Metro")," for details on configuration options."),(0,a.mdx)("h2",{id:"url-and-bundle-request"},"URL and bundle request"),(0,a.mdx)("p",null,"The server has the ability to serve assets, bundles and source maps for those bundles."),(0,a.mdx)("h3",{id:"assets"},"Assets"),(0,a.mdx)("p",null,"In order to request an asset, you can freely use the ",(0,a.mdx)("inlineCode",{parentName:"p"},"require")," method as if it was another JS file. The server will treat this specific ",(0,a.mdx)("inlineCode",{parentName:"p"},"require")," calls and make them return the path to that file. When an asset is requested (an asset is recognized by its extension, which has to be on the ",(0,a.mdx)("inlineCode",{parentName:"p"},"assetExts")," array) it is generally served as-is."),(0,a.mdx)("p",null,"However, the server is also able to serve specific assets depending on the platform and on the requested size (in the case of images). The way you specify the platform is via the dotted suffix (e.g. ",(0,a.mdx)("inlineCode",{parentName:"p"},".ios"),") and the resolution via the at suffix (e.g. ",(0,a.mdx)("inlineCode",{parentName:"p"},"@2x"),"). This is transparently handled for you when using ",(0,a.mdx)("inlineCode",{parentName:"p"},"require"),"."),(0,a.mdx)("h3",{id:"bundle"},"Bundle"),(0,a.mdx)("p",null,"Any JS file can be used as the root for a bundle request. The file will be looked in the ",(0,a.mdx)("inlineCode",{parentName:"p"},"projectRoot"),". All files that are required by the root will be recursively included. In order to request a bundle, just change the extension from ",(0,a.mdx)("inlineCode",{parentName:"p"},".js")," to ",(0,a.mdx)("inlineCode",{parentName:"p"},".bundle"),". Options for building the bundle are passed as query parameters (all optional)."),(0,a.mdx)("ul",null,(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("inlineCode",{parentName:"li"},"dev"),": build the bundle in development mode or not. Maps 1:1 to the ",(0,a.mdx)("inlineCode",{parentName:"li"},"dev")," setting of the bundles. Pass ",(0,a.mdx)("inlineCode",{parentName:"li"},"true")," or ",(0,a.mdx)("inlineCode",{parentName:"li"},"false")," as strings into the URL."),(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("inlineCode",{parentName:"li"},"platform"),": platform requesting the bundle. Can be ",(0,a.mdx)("inlineCode",{parentName:"li"},"ios")," or ",(0,a.mdx)("inlineCode",{parentName:"li"},"android"),". Maps 1:1 to the ",(0,a.mdx)("inlineCode",{parentName:"li"},"platform")," setting of the bundles."),(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("inlineCode",{parentName:"li"},"minify"),": whether code should be minified or not. Maps 1:1 to the ",(0,a.mdx)("inlineCode",{parentName:"li"},"minify")," setting of the bundles. Pass ",(0,a.mdx)("inlineCode",{parentName:"li"},"true")," or ",(0,a.mdx)("inlineCode",{parentName:"li"},"false")," as strings into the URL."),(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("inlineCode",{parentName:"li"},"excludeSource"),": whether sources should be included in the source map or not. Pass ",(0,a.mdx)("inlineCode",{parentName:"li"},"true")," or ",(0,a.mdx)("inlineCode",{parentName:"li"},"false")," as strings into the URL.")),(0,a.mdx)("p",null,"For instance, requesting ",(0,a.mdx)("inlineCode",{parentName:"p"},"http://localhost:8081/foo/bar/baz.bundle?dev=true&platform=ios")," will create a bundle out of ",(0,a.mdx)("inlineCode",{parentName:"p"},"foo/bar/baz.js")," for iOS in development mode."),(0,a.mdx)("h3",{id:"source-maps"},"Source maps"),(0,a.mdx)("p",null,"Source maps are built for each bundle by using the same URL as the bundle (thus, the same as the JS file acting as a root). This will only work when ",(0,a.mdx)("inlineCode",{parentName:"p"},"inlineSourceMap")," is set to ",(0,a.mdx)("inlineCode",{parentName:"p"},"false"),". All options you passed to the bundle will be added to the source map URL; otherwise, they wouldn't match."),(0,a.mdx)("h2",{id:"javascript-transformer"},"JavaScript transformer"),(0,a.mdx)("p",null,"The JavaScript transformer (",(0,a.mdx)("a",{parentName:"p",href:"/docs/configuration#babeltransformerpath"},(0,a.mdx)("inlineCode",{parentName:"a"},"babelTransformerPath")),") is the place where JS code will be manipulated; useful for calling Babel. The transformer can export two methods:"),(0,a.mdx)("h3",{id:"method-transformmodule"},"Method ",(0,a.mdx)("inlineCode",{parentName:"h3"},"transform(module)")),(0,a.mdx)("p",null,"Mandatory method that will transform code. The object received has information about the module being transformed (e.g its path, code...) and the returned object has to contain an ",(0,a.mdx)("inlineCode",{parentName:"p"},"ast")," key that is the AST representation of the transformed code. The default shipped transformer does the bare minimum amount of work by just parsing the code to AST:"),(0,a.mdx)("pre",null,(0,a.mdx)("code",{parentName:"pre",className:"language-js"},"const babylon = require('@babel/parser');\n\nmodule.exports.transform = (file: {filename: string, src: string}) => {\n const ast = babylon.parse(file.src, {sourceType: 'module'});\n\n return {ast};\n};\n")),(0,a.mdx)("p",null,"If you would like to plug-in Babel, you can simply do that by passing the code to it:"),(0,a.mdx)("pre",null,(0,a.mdx)("code",{parentName:"pre",className:"language-js"},"const {transformSync} = require('@babel/core');\n\nmodule.exports.transform = file => {\n return transformSync(file.src, {\n // Babel options...\n });\n};\n")),(0,a.mdx)("h3",{id:"method-getcachekey"},"Method ",(0,a.mdx)("inlineCode",{parentName:"h3"},"getCacheKey()")),(0,a.mdx)("p",null,"Optional method that returns the cache key of the transformer. When using different transformers, this allows to correctly tie a transformed file to the transformer that converted it. The result of the method has to be a ",(0,a.mdx)("inlineCode",{parentName:"p"},"string"),"."))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/163e71e1.d684e31e.js b/assets/js/163e71e1.d684e31e.js deleted file mode 100644 index 1f0a525b16..0000000000 --- a/assets/js/163e71e1.d684e31e.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkmetro_website=self.webpackChunkmetro_website||[]).push([[662],{3905:(e,n,t)=>{t.r(n),t.d(n,{MDXContext:()=>s,MDXProvider:()=>u,mdx:()=>x,useMDXComponents:()=>p,withMDXComponents:()=>m});var r=t(67294);function o(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function a(){return a=Object.assign||function(e){for(var n=1;n=0||(o[t]=e[t]);return o}(e,n);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(o[t]=e[t])}return o}var s=r.createContext({}),m=function(e){return function(n){var t=p(n.components);return r.createElement(e,a({},n,{components:t}))}},p=function(e){var n=r.useContext(s),t=n;return e&&(t="function"==typeof e?e(n):l(l({},n),e)),t},u=function(e){var n=p(e.components);return r.createElement(s.Provider,{value:n},e.children)},c="mdxType",h={inlineCode:"code",wrapper:function(e){var n=e.children;return r.createElement(r.Fragment,{},n)}},f=r.forwardRef((function(e,n){var t=e.components,o=e.mdxType,a=e.originalType,i=e.parentName,s=d(e,["components","mdxType","originalType","parentName"]),m=p(t),u=o,c=m["".concat(i,".").concat(u)]||m[u]||h[u]||a;return t?r.createElement(c,l(l({ref:n},s),{},{components:t})):r.createElement(c,l({ref:n},s))}));function x(e,n){var t=arguments,o=n&&n.mdxType;if("string"==typeof e||o){var a=t.length,i=new Array(a);i[0]=f;var l={};for(var d in n)hasOwnProperty.call(n,d)&&(l[d]=n[d]);l.originalType=e,l[c]="string"==typeof e?e:o,i[1]=l;for(var s=2;s{t.r(n),t.d(n,{assets:()=>m,contentTitle:()=>d,default:()=>h,frontMatter:()=>l,metadata:()=>s,toc:()=>p});var r=t(87462),o=t(63366),a=(t(67294),t(3905)),i=["components"],l={id:"getting-started",title:"Getting Started"},d=void 0,s={unversionedId:"getting-started",id:"getting-started",title:"Getting Started",description:"Install Metro using npm:",source:"@site/../docs/GettingStarted.md",sourceDirName:".",slug:"/getting-started",permalink:"/docs/getting-started",draft:!1,editUrl:"https://github.com/facebook/metro/edit/main/docs/../docs/GettingStarted.md",tags:[],version:"current",lastUpdatedAt:1725545477,formattedLastUpdatedAt:"Sep 5, 2024",frontMatter:{id:"getting-started",title:"Getting Started"},sidebar:"docs",next:{title:"Concepts",permalink:"/docs/concepts"}},m={},p=[{value:"Running metro",id:"running-metro",level:2},{value:"Running Programmatically",id:"running-programmatically",level:3},{value:"Method runMetro(config)",id:"method-runmetroconfig",level:3},{value:"Method runServer(config, options)",id:"method-runserverconfig-options",level:3},{value:"Options",id:"options",level:4},{value:"Method runBuild(config, options)",id:"method-runbuildconfig-options",level:3},{value:"Options",id:"options-1",level:4},{value:"Method createConnectMiddleware(config)",id:"method-createconnectmiddlewareconfig",level:3},{value:"Options",id:"options-2",level:4},{value:"Available options",id:"available-options",level:2},{value:"Configuration",id:"configuration",level:3},{value:"URL and bundle request",id:"url-and-bundle-request",level:2},{value:"Assets",id:"assets",level:3},{value:"Bundle",id:"bundle",level:3},{value:"Source maps",id:"source-maps",level:3},{value:"JavaScript transformer",id:"javascript-transformer",level:2},{value:"Method transform(module)",id:"method-transformmodule",level:3},{value:"Method getCacheKey()",id:"method-getcachekey",level:3}],u={toc:p},c="wrapper";function h(e){var n=e.components,t=(0,o.Z)(e,i);return(0,a.mdx)(c,(0,r.Z)({},u,t,{components:n,mdxType:"MDXLayout"}),(0,a.mdx)("p",null,"Install Metro using ",(0,a.mdx)("a",{parentName:"p",href:"https://www.npmjs.com/"},(0,a.mdx)("inlineCode",{parentName:"a"},"npm")),":"),(0,a.mdx)("pre",null,(0,a.mdx)("code",{parentName:"pre",className:"language-bash"},"npm install --save-dev metro metro-core\n")),(0,a.mdx)("p",null,"Or via ",(0,a.mdx)("a",{parentName:"p",href:"https://yarnpkg.com/"},(0,a.mdx)("inlineCode",{parentName:"a"},"yarn")),":"),(0,a.mdx)("pre",null,(0,a.mdx)("code",{parentName:"pre",className:"language-bash"},"yarn add --dev metro metro-core\n")),(0,a.mdx)("h2",{id:"running-metro"},"Running ",(0,a.mdx)("inlineCode",{parentName:"h2"},"metro")),(0,a.mdx)("p",null,"You can run Metro by either running the ",(0,a.mdx)("a",{parentName:"p",href:"/docs/cli"},"CLI")," or by calling it programmatically."),(0,a.mdx)("h3",{id:"running-programmatically"},"Running Programmatically"),(0,a.mdx)("p",null,"First, require the module by doing:"),(0,a.mdx)("pre",null,(0,a.mdx)("code",{parentName:"pre",className:"language-js"},"const Metro = require('metro');\n")),(0,a.mdx)("p",null,"Within the object returned, several main methods are given:"),(0,a.mdx)("h3",{id:"method-runmetroconfig"},"Method ",(0,a.mdx)("inlineCode",{parentName:"h3"},"runMetro(config)")),(0,a.mdx)("p",null,"Given the config, a ",(0,a.mdx)("inlineCode",{parentName:"p"},"metro-server")," will be returned. You can then hook this into a proper HTTP(S) server by using its ",(0,a.mdx)("inlineCode",{parentName:"p"},"processRequest")," method:"),(0,a.mdx)("pre",null,(0,a.mdx)("code",{parentName:"pre",className:"language-js"},"'use strict';\n\nconst http = require('http');\nconst Metro = require('metro');\n\n// We first load the config from the file system\nMetro.loadConfig().then(async (config) => {\n const metroBundlerServer = await Metro.runMetro(config);\n\n const httpServer = http.createServer(\n metroBundlerServer.processRequest.bind(metroBundlerServer),\n );\n\n httpServer.listen(8081);\n});\n")),(0,a.mdx)("p",null,"In order to be also compatible with Express apps, ",(0,a.mdx)("inlineCode",{parentName:"p"},"processRequest")," will also call its third parameter when the request could not be handled by Metro. This allows you to integrate the server with your existing server, or to extend a new one:"),(0,a.mdx)("pre",null,(0,a.mdx)("code",{parentName:"pre",className:"language-js"},"const httpServer = http.createServer((req, res) => {\n metroBundlerServer.processRequest(req, res, () => {\n // Metro does not know how to handle the request.\n });\n});\n")),(0,a.mdx)("p",null,"If you are using ",(0,a.mdx)("a",{parentName:"p",href:"http://expressjs.com/"},"Express"),", you can just pass ",(0,a.mdx)("inlineCode",{parentName:"p"},"processRequest")," as a middleware:"),(0,a.mdx)("pre",null,(0,a.mdx)("code",{parentName:"pre",className:"language-js"},"const express = require('express');\nconst app = express();\n\napp.use(\n metroBundlerServer.processRequest.bind(metroBundlerServer),\n);\n\napp.listen(8081);\n")),(0,a.mdx)("h3",{id:"method-runserverconfig-options"},"Method ",(0,a.mdx)("inlineCode",{parentName:"h3"},"runServer(config, options)")),(0,a.mdx)("p",null,"Starts a development server based on the given configuration and options. Returns the server.\nWe recommend using ",(0,a.mdx)("inlineCode",{parentName:"p"},"runMetro")," instead of ",(0,a.mdx)("inlineCode",{parentName:"p"},"runServer"),", ",(0,a.mdx)("inlineCode",{parentName:"p"},"runMetro")," calls this function."),(0,a.mdx)("h4",{id:"options"},"Options"),(0,a.mdx)("ul",null,(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("inlineCode",{parentName:"li"},"host (string)"),": Where to host the server on."),(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("inlineCode",{parentName:"li"},"onReady (Function)"),": Called when the server is ready to serve requests."),(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("inlineCode",{parentName:"li"},"secure (boolean)"),": ",(0,a.mdx)("strong",{parentName:"li"},"DEPRECATED")," Whether the server should run on ",(0,a.mdx)("inlineCode",{parentName:"li"},"https")," instead of ",(0,a.mdx)("inlineCode",{parentName:"li"},"http"),"."),(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("inlineCode",{parentName:"li"},"secureKey (string)"),": ",(0,a.mdx)("strong",{parentName:"li"},"DEPRECATED")," The key to use for ",(0,a.mdx)("inlineCode",{parentName:"li"},"https")," when ",(0,a.mdx)("inlineCode",{parentName:"li"},"secure")," is on."),(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("inlineCode",{parentName:"li"},"secureCert (string)"),": ",(0,a.mdx)("strong",{parentName:"li"},"DEPRECATED")," The cert to use for ",(0,a.mdx)("inlineCode",{parentName:"li"},"https")," when ",(0,a.mdx)("inlineCode",{parentName:"li"},"secure")," is on."),(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("inlineCode",{parentName:"li"},"secureServerOptions (Object)"),": The options object to pass to Metro's HTTPS server. The presence of this object will make Metro's server run on ",(0,a.mdx)("inlineCode",{parentName:"li"},"https"),". Refer to the ",(0,a.mdx)("a",{parentName:"li",href:"https://nodejs.org/api/https.html#https_https_createserver_options_requestlistener"},"Node docs")," for valid options."),(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("inlineCode",{parentName:"li"},"waitForBundler (boolean)"),": Whether to wait for the bundler to finish initializing before returning the server instance.")),(0,a.mdx)("pre",null,(0,a.mdx)("code",{parentName:"pre",className:"language-js"},"const config = await Metro.loadConfig();\n\nawait Metro.runServer(config);\n")),(0,a.mdx)("pre",null,(0,a.mdx)("code",{parentName:"pre",className:"language-js"},"const fs = require('fs');\n\nconst config = await Metro.loadConfig();\n\nawait Metro.runServer(config, {\n secureServerOptions: {\n ca: fs.readFileSync('path/to/ca'),\n cert: fs.readFileSync('path/to/cert'),\n key: fs.readFileSync('path/to/key'),\n }\n});\n")),(0,a.mdx)("h3",{id:"method-runbuildconfig-options"},"Method ",(0,a.mdx)("inlineCode",{parentName:"h3"},"runBuild(config, options)")),(0,a.mdx)("p",null,"Given a configuration and a set of options that you would typically pass to a server, plus a set of options specific to the bundle itself, a bundle will be built. The return value is a Promise that resolves to an object with two properties, ",(0,a.mdx)("inlineCode",{parentName:"p"},"code")," and ",(0,a.mdx)("inlineCode",{parentName:"p"},"map"),". This is useful at build time."),(0,a.mdx)("h4",{id:"options-1"},"Options"),(0,a.mdx)("ul",null,(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("inlineCode",{parentName:"li"},"dev (boolean)"),": Create a development version of the build (",(0,a.mdx)("inlineCode",{parentName:"li"},"process.env.NODE_ENV = 'development'"),")."),(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("inlineCode",{parentName:"li"},"entry (string)"),": Pointing to the entry file to bundle."),(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("inlineCode",{parentName:"li"},"onBegin (Function)"),": Called when the bundling starts."),(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("inlineCode",{parentName:"li"},"onComplete (Function)"),": Called when the bundling finishes."),(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("inlineCode",{parentName:"li"},"onProgress (Function)"),": Called during the bundle, every time there's new information available about the module count/progress."),(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("inlineCode",{parentName:"li"},"minify (boolean)"),": Whether Metro should minify the bundle."),(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("inlineCode",{parentName:"li"},"out (string)"),": Path to the output bundle."),(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("inlineCode",{parentName:"li"},"platform ('web' | 'android' | 'ios')"),": Which platform to bundle for if a list of platforms is provided."),(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("inlineCode",{parentName:"li"},"sourceMap (boolean)"),": Whether Metro should generate source maps."),(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("inlineCode",{parentName:"li"},"sourceMapUrl (string)"),": URL where the source map can be found. It defaults to the same same URL as the bundle, but changing the extension from ",(0,a.mdx)("inlineCode",{parentName:"li"},".bundle")," to ",(0,a.mdx)("inlineCode",{parentName:"li"},".map"),". When ",(0,a.mdx)("inlineCode",{parentName:"li"},"inlineSourceMap")," is ",(0,a.mdx)("inlineCode",{parentName:"li"},"true"),", this property has no effect.")),(0,a.mdx)("pre",null,(0,a.mdx)("code",{parentName:"pre",className:"language-js"},"const config = await Metro.loadConfig();\n\nawait Metro.runBuild(config, {\n entry: 'index.js',\n platform: 'ios',\n minify: true,\n out: '/Users/Metro/metro-ios.js'\n});\n")),(0,a.mdx)("h3",{id:"method-createconnectmiddlewareconfig"},"Method ",(0,a.mdx)("inlineCode",{parentName:"h3"},"createConnectMiddleware(config)")),(0,a.mdx)("p",null,"Instead of creating the full server, creates a Connect middleware that answers to bundle requests. This middleware can then be plugged into your own servers. The ",(0,a.mdx)("inlineCode",{parentName:"p"},"port")," parameter is optional and only used for logging purposes."),(0,a.mdx)("h4",{id:"options-2"},"Options"),(0,a.mdx)("ul",null,(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("inlineCode",{parentName:"li"},"port (number)"),": Port for the Connect middleware (only for logging purposes).")),(0,a.mdx)("pre",null,(0,a.mdx)("code",{parentName:"pre",className:"language-js"},"const Metro = require('metro');\nconst express = require('express');\nconst app = express();\nconst server = require('http').Server(app);\n\nMetro.loadConfig().then(async config => {\n const connectMiddleware = await Metro.createConnectMiddleware(config);\n const {server: {port}} = config;\n\n app.use(connectMiddleware.middleware);\n server.listen(port);\n connectMiddleware.attachHmrServer(server);\n});\n")),(0,a.mdx)("h2",{id:"available-options"},"Available options"),(0,a.mdx)("h3",{id:"configuration"},"Configuration"),(0,a.mdx)("p",null,"Check ",(0,a.mdx)("a",{parentName:"p",href:"/docs/configuration"},"Configuring Metro")," for details on configuration options."),(0,a.mdx)("h2",{id:"url-and-bundle-request"},"URL and bundle request"),(0,a.mdx)("p",null,"The server has the ability to serve assets, bundles and source maps for those bundles."),(0,a.mdx)("h3",{id:"assets"},"Assets"),(0,a.mdx)("p",null,"In order to request an asset, you can freely use the ",(0,a.mdx)("inlineCode",{parentName:"p"},"require")," method as if it was another JS file. The server will treat this specific ",(0,a.mdx)("inlineCode",{parentName:"p"},"require")," calls and make them return the path to that file. When an asset is requested (an asset is recognized by its extension, which has to be on the ",(0,a.mdx)("inlineCode",{parentName:"p"},"assetExts")," array) it is generally served as-is."),(0,a.mdx)("p",null,"However, the server is also able to serve specific assets depending on the platform and on the requested size (in the case of images). The way you specify the platform is via the dotted suffix (e.g. ",(0,a.mdx)("inlineCode",{parentName:"p"},".ios"),") and the resolution via the at suffix (e.g. ",(0,a.mdx)("inlineCode",{parentName:"p"},"@2x"),"). This is transparently handled for you when using ",(0,a.mdx)("inlineCode",{parentName:"p"},"require"),"."),(0,a.mdx)("h3",{id:"bundle"},"Bundle"),(0,a.mdx)("p",null,"Any JS file can be used as the root for a bundle request. The file will be looked in the ",(0,a.mdx)("inlineCode",{parentName:"p"},"projectRoot"),". All files that are required by the root will be recursively included. In order to request a bundle, just change the extension from ",(0,a.mdx)("inlineCode",{parentName:"p"},".js")," to ",(0,a.mdx)("inlineCode",{parentName:"p"},".bundle"),". Options for building the bundle are passed as query parameters (all optional)."),(0,a.mdx)("ul",null,(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("inlineCode",{parentName:"li"},"dev"),": build the bundle in development mode or not. Maps 1:1 to the ",(0,a.mdx)("inlineCode",{parentName:"li"},"dev")," setting of the bundles. Pass ",(0,a.mdx)("inlineCode",{parentName:"li"},"true")," or ",(0,a.mdx)("inlineCode",{parentName:"li"},"false")," as strings into the URL."),(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("inlineCode",{parentName:"li"},"platform"),": platform requesting the bundle. Can be ",(0,a.mdx)("inlineCode",{parentName:"li"},"ios")," or ",(0,a.mdx)("inlineCode",{parentName:"li"},"android"),". Maps 1:1 to the ",(0,a.mdx)("inlineCode",{parentName:"li"},"platform")," setting of the bundles."),(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("inlineCode",{parentName:"li"},"minify"),": whether code should be minified or not. Maps 1:1 to the ",(0,a.mdx)("inlineCode",{parentName:"li"},"minify")," setting of the bundles. Pass ",(0,a.mdx)("inlineCode",{parentName:"li"},"true")," or ",(0,a.mdx)("inlineCode",{parentName:"li"},"false")," as strings into the URL."),(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("inlineCode",{parentName:"li"},"excludeSource"),": whether sources should be included in the source map or not. Pass ",(0,a.mdx)("inlineCode",{parentName:"li"},"true")," or ",(0,a.mdx)("inlineCode",{parentName:"li"},"false")," as strings into the URL.")),(0,a.mdx)("p",null,"For instance, requesting ",(0,a.mdx)("inlineCode",{parentName:"p"},"http://localhost:8081/foo/bar/baz.bundle?dev=true&platform=ios")," will create a bundle out of ",(0,a.mdx)("inlineCode",{parentName:"p"},"foo/bar/baz.js")," for iOS in development mode."),(0,a.mdx)("h3",{id:"source-maps"},"Source maps"),(0,a.mdx)("p",null,"Source maps are built for each bundle by using the same URL as the bundle (thus, the same as the JS file acting as a root). This will only work when ",(0,a.mdx)("inlineCode",{parentName:"p"},"inlineSourceMap")," is set to ",(0,a.mdx)("inlineCode",{parentName:"p"},"false"),". All options you passed to the bundle will be added to the source map URL; otherwise, they wouldn't match."),(0,a.mdx)("h2",{id:"javascript-transformer"},"JavaScript transformer"),(0,a.mdx)("p",null,"The JavaScript transformer (",(0,a.mdx)("a",{parentName:"p",href:"/docs/configuration#babeltransformerpath"},(0,a.mdx)("inlineCode",{parentName:"a"},"babelTransformerPath")),") is the place where JS code will be manipulated; useful for calling Babel. The transformer can export two methods:"),(0,a.mdx)("h3",{id:"method-transformmodule"},"Method ",(0,a.mdx)("inlineCode",{parentName:"h3"},"transform(module)")),(0,a.mdx)("p",null,"Mandatory method that will transform code. The object received has information about the module being transformed (e.g its path, code...) and the returned object has to contain an ",(0,a.mdx)("inlineCode",{parentName:"p"},"ast")," key that is the AST representation of the transformed code. The default shipped transformer does the bare minimum amount of work by just parsing the code to AST:"),(0,a.mdx)("pre",null,(0,a.mdx)("code",{parentName:"pre",className:"language-js"},"const babylon = require('@babel/parser');\n\nmodule.exports.transform = (file: {filename: string, src: string}) => {\n const ast = babylon.parse(file.src, {sourceType: 'module'});\n\n return {ast};\n};\n")),(0,a.mdx)("p",null,"If you would like to plug-in Babel, you can simply do that by passing the code to it:"),(0,a.mdx)("pre",null,(0,a.mdx)("code",{parentName:"pre",className:"language-js"},"const {transformSync} = require('@babel/core');\n\nmodule.exports.transform = file => {\n return transformSync(file.src, {\n // Babel options...\n });\n};\n")),(0,a.mdx)("h3",{id:"method-getcachekey"},"Method ",(0,a.mdx)("inlineCode",{parentName:"h3"},"getCacheKey()")),(0,a.mdx)("p",null,"Optional method that returns the cache key of the transformer. When using different transformers, this allows to correctly tie a transformed file to the transformer that converted it. The result of the method has to be a ",(0,a.mdx)("inlineCode",{parentName:"p"},"string"),"."))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/37a9b1a2.d7f3ecdf.js b/assets/js/37a9b1a2.d7f3ecdf.js deleted file mode 100644 index 4060c84bbf..0000000000 --- a/assets/js/37a9b1a2.d7f3ecdf.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkmetro_website=self.webpackChunkmetro_website||[]).push([[536],{3905:(e,n,t)=>{t.r(n),t.d(n,{MDXContext:()=>u,MDXProvider:()=>c,mdx:()=>b,useMDXComponents:()=>m,withMDXComponents:()=>s});var r=t(67294);function i(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function a(){return a=Object.assign||function(e){for(var n=1;n=0||(i[t]=e[t]);return i}(e,n);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(i[t]=e[t])}return i}var u=r.createContext({}),s=function(e){return function(n){var t=m(n.components);return r.createElement(e,a({},n,{components:t}))}},m=function(e){var n=r.useContext(u),t=n;return e&&(t="function"==typeof e?e(n):o(o({},n),e)),t},c=function(e){var n=m(e.components);return r.createElement(u.Provider,{value:n},e.children)},p="mdxType",f={inlineCode:"code",wrapper:function(e){var n=e.children;return r.createElement(r.Fragment,{},n)}},h=r.forwardRef((function(e,n){var t=e.components,i=e.mdxType,a=e.originalType,l=e.parentName,u=d(e,["components","mdxType","originalType","parentName"]),s=m(t),c=i,p=s["".concat(l,".").concat(c)]||s[c]||f[c]||a;return t?r.createElement(p,o(o({ref:n},u),{},{components:t})):r.createElement(p,o({ref:n},u))}));function b(e,n){var t=arguments,i=n&&n.mdxType;if("string"==typeof e||i){var a=t.length,l=new Array(a);l[0]=h;var o={};for(var d in n)hasOwnProperty.call(n,d)&&(o[d]=n[d]);o.originalType=e,o[p]="string"==typeof e?e:i,l[1]=o;for(var u=2;u{t.r(n),t.d(n,{assets:()=>s,contentTitle:()=>d,default:()=>f,frontMatter:()=>o,metadata:()=>u,toc:()=>m});var r=t(87462),i=t(63366),a=(t(67294),t(3905)),l=["components"],o={id:"bundling",title:"Bundle Formats"},d=void 0,u={unversionedId:"bundling",id:"bundling",title:"Bundle Formats",description:"When bundling, each of the modules gets assigned a numeric id, meaning no dynamic requires are supported. Requires are changed by its numeric version, and modules are stored in different possible formats. Three different formats of bundling are supported:",source:"@site/../docs/Bundling.md",sourceDirName:".",slug:"/bundling",permalink:"/docs/bundling",draft:!1,editUrl:"https://github.com/facebook/metro/edit/main/docs/../docs/Bundling.md",tags:[],version:"current",lastUpdatedAt:1725545477,formattedLastUpdatedAt:"Sep 5, 2024",frontMatter:{id:"bundling",title:"Bundle Formats"},sidebar:"docs",previous:{title:"Local Development Setup",permalink:"/docs/local-development"},next:{title:"Caching",permalink:"/docs/caching"}},s={},m=[{value:"Plain bundle",id:"plain-bundle",level:2},{value:"Indexed RAM bundle",id:"indexed-ram-bundle",level:2},{value:"File RAM bundle",id:"file-ram-bundle",level:2}],c={toc:m},p="wrapper";function f(e){var n=e.components,t=(0,i.Z)(e,l);return(0,a.mdx)(p,(0,r.Z)({},c,t,{components:n,mdxType:"MDXLayout"}),(0,a.mdx)("p",null,"When bundling, each of the modules gets assigned a numeric id, meaning no dynamic requires are supported. Requires are changed by its numeric version, and modules are stored in different possible formats. Three different formats of bundling are supported:"),(0,a.mdx)("h2",{id:"plain-bundle"},"Plain bundle"),(0,a.mdx)("p",null,"This is the standard bundling format. In this format, all files are wrapped with a function call, then added to the global file. This is useful for environments that expect a JS only bundle (e.g. a browser). Just requiring the entry point with the ",(0,a.mdx)("inlineCode",{parentName:"p"},".bundle")," extension should trigger a build of it."),(0,a.mdx)("h2",{id:"indexed-ram-bundle"},"Indexed RAM bundle"),(0,a.mdx)("p",null,"This format composes the bundle as a binary file, which format has the following parts (all numbers are expressed in Little Endian):"),(0,a.mdx)("ul",null,(0,a.mdx)("li",{parentName:"ul"},"A magic number: a ",(0,a.mdx)("inlineCode",{parentName:"li"},"uint32")," must be located at the beginning of the file, with the value ",(0,a.mdx)("inlineCode",{parentName:"li"},"0xFB0BD1E5"),". This is used to verify the file."),(0,a.mdx)("li",{parentName:"ul"},"An offset table: the table is a sequence of ",(0,a.mdx)("inlineCode",{parentName:"li"},"uint32")," pairs, with a header",(0,a.mdx)("ul",{parentName:"li"},(0,a.mdx)("li",{parentName:"ul"},"For the header, two ",(0,a.mdx)("inlineCode",{parentName:"li"},"uint32"),"s can be found: the length of the table, and the length of the startup code."),(0,a.mdx)("li",{parentName:"ul"},"For the pairs, they represent the offset in the file and the length of code module, in bytes."))),(0,a.mdx)("li",{parentName:"ul"},"Each of the modules, finished by a null byte (",(0,a.mdx)("inlineCode",{parentName:"li"},"\\0"),").")),(0,a.mdx)("pre",null,(0,a.mdx)("code",{parentName:"pre"},"` 0 1 2 3 4 5 6\n 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3\n+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n| Magic number | Header size |\n+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n| Startup code size | Module 0 offset |\n+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n| Module 0 length | |\n+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +\n| |\n+ ... +\n| |\n+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n| | Module n offset |\n+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n| Module n length | Module 0 code | Module 0 code | ... | \\0 |\n+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n| Module 1 code | Module 1 code | ... | \\0 | |\n+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +\n| |\n+ ... +\n| |\n+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n| | Module n code | Module n code | ... | \\0 |\n+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+`\n")),(0,a.mdx)("p",null,"This structure is optimal for an environment that is able to load all code in memory at once:"),(0,a.mdx)("ul",null,(0,a.mdx)("li",{parentName:"ul"},"By using the offset table, one can load any module in constant time, where the code for module ",(0,a.mdx)("inlineCode",{parentName:"li"},"x")," is located at ",(0,a.mdx)("inlineCode",{parentName:"li"},"file[(x + 3) * sizeof(uint32)]"),". Since there is a null character (",(0,a.mdx)("inlineCode",{parentName:"li"},"\\0"),") separating all modules, usually length does not even need to be used, and the module can be loaded directly as an ASCIIZ string."),(0,a.mdx)("li",{parentName:"ul"},"Startup code is always found at ",(0,a.mdx)("inlineCode",{parentName:"li"},"file[sizeof(uint32)]"),".")),(0,a.mdx)("p",null,"This bundling is usually used by iOS."),(0,a.mdx)("h2",{id:"file-ram-bundle"},"File RAM bundle"),(0,a.mdx)("p",null,"Each module is stored as a file, with the name ",(0,a.mdx)("inlineCode",{parentName:"p"},"js-modules/${id}.js"),", plus an extra file called ",(0,a.mdx)("inlineCode",{parentName:"p"},"UNBUNDLE")," is created, which its only content is the magic number, ",(0,a.mdx)("inlineCode",{parentName:"p"},"0xFB0BD1E5"),". Note that the ",(0,a.mdx)("inlineCode",{parentName:"p"},"UNBUNDLE")," file is created at the root.\nThis bundling is usually used by Android, since package contents are zipped, and access to a zipped file is much faster. If the indexed format was used instead, all the bundled should be unzipped at once to get the code for the corresponding module."))}f.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/37a9b1a2.fa60b8af.js b/assets/js/37a9b1a2.fa60b8af.js new file mode 100644 index 0000000000..ef65e7b198 --- /dev/null +++ b/assets/js/37a9b1a2.fa60b8af.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkmetro_website=self.webpackChunkmetro_website||[]).push([[536],{3905:(e,n,t)=>{t.r(n),t.d(n,{MDXContext:()=>u,MDXProvider:()=>c,mdx:()=>b,useMDXComponents:()=>m,withMDXComponents:()=>s});var r=t(67294);function i(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function a(){return a=Object.assign||function(e){for(var n=1;n=0||(i[t]=e[t]);return i}(e,n);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(i[t]=e[t])}return i}var u=r.createContext({}),s=function(e){return function(n){var t=m(n.components);return r.createElement(e,a({},n,{components:t}))}},m=function(e){var n=r.useContext(u),t=n;return e&&(t="function"==typeof e?e(n):o(o({},n),e)),t},c=function(e){var n=m(e.components);return r.createElement(u.Provider,{value:n},e.children)},p="mdxType",f={inlineCode:"code",wrapper:function(e){var n=e.children;return r.createElement(r.Fragment,{},n)}},h=r.forwardRef((function(e,n){var t=e.components,i=e.mdxType,a=e.originalType,l=e.parentName,u=d(e,["components","mdxType","originalType","parentName"]),s=m(t),c=i,p=s["".concat(l,".").concat(c)]||s[c]||f[c]||a;return t?r.createElement(p,o(o({ref:n},u),{},{components:t})):r.createElement(p,o({ref:n},u))}));function b(e,n){var t=arguments,i=n&&n.mdxType;if("string"==typeof e||i){var a=t.length,l=new Array(a);l[0]=h;var o={};for(var d in n)hasOwnProperty.call(n,d)&&(o[d]=n[d]);o.originalType=e,o[p]="string"==typeof e?e:i,l[1]=o;for(var u=2;u{t.r(n),t.d(n,{assets:()=>s,contentTitle:()=>d,default:()=>f,frontMatter:()=>o,metadata:()=>u,toc:()=>m});var r=t(87462),i=t(63366),a=(t(67294),t(3905)),l=["components"],o={id:"bundling",title:"Bundle Formats"},d=void 0,u={unversionedId:"bundling",id:"bundling",title:"Bundle Formats",description:"When bundling, each of the modules gets assigned a numeric id, meaning no dynamic requires are supported. Requires are changed by its numeric version, and modules are stored in different possible formats. Three different formats of bundling are supported:",source:"@site/../docs/Bundling.md",sourceDirName:".",slug:"/bundling",permalink:"/docs/bundling",draft:!1,editUrl:"https://github.com/facebook/metro/edit/main/docs/../docs/Bundling.md",tags:[],version:"current",lastUpdatedAt:1726493799,formattedLastUpdatedAt:"Sep 16, 2024",frontMatter:{id:"bundling",title:"Bundle Formats"},sidebar:"docs",previous:{title:"Local Development Setup",permalink:"/docs/local-development"},next:{title:"Caching",permalink:"/docs/caching"}},s={},m=[{value:"Plain bundle",id:"plain-bundle",level:2},{value:"Indexed RAM bundle",id:"indexed-ram-bundle",level:2},{value:"File RAM bundle",id:"file-ram-bundle",level:2}],c={toc:m},p="wrapper";function f(e){var n=e.components,t=(0,i.Z)(e,l);return(0,a.mdx)(p,(0,r.Z)({},c,t,{components:n,mdxType:"MDXLayout"}),(0,a.mdx)("p",null,"When bundling, each of the modules gets assigned a numeric id, meaning no dynamic requires are supported. Requires are changed by its numeric version, and modules are stored in different possible formats. Three different formats of bundling are supported:"),(0,a.mdx)("h2",{id:"plain-bundle"},"Plain bundle"),(0,a.mdx)("p",null,"This is the standard bundling format. In this format, all files are wrapped with a function call, then added to the global file. This is useful for environments that expect a JS only bundle (e.g. a browser). Just requiring the entry point with the ",(0,a.mdx)("inlineCode",{parentName:"p"},".bundle")," extension should trigger a build of it."),(0,a.mdx)("h2",{id:"indexed-ram-bundle"},"Indexed RAM bundle"),(0,a.mdx)("p",null,"This format composes the bundle as a binary file, which format has the following parts (all numbers are expressed in Little Endian):"),(0,a.mdx)("ul",null,(0,a.mdx)("li",{parentName:"ul"},"A magic number: a ",(0,a.mdx)("inlineCode",{parentName:"li"},"uint32")," must be located at the beginning of the file, with the value ",(0,a.mdx)("inlineCode",{parentName:"li"},"0xFB0BD1E5"),". This is used to verify the file."),(0,a.mdx)("li",{parentName:"ul"},"An offset table: the table is a sequence of ",(0,a.mdx)("inlineCode",{parentName:"li"},"uint32")," pairs, with a header",(0,a.mdx)("ul",{parentName:"li"},(0,a.mdx)("li",{parentName:"ul"},"For the header, two ",(0,a.mdx)("inlineCode",{parentName:"li"},"uint32"),"s can be found: the length of the table, and the length of the startup code."),(0,a.mdx)("li",{parentName:"ul"},"For the pairs, they represent the offset in the file and the length of code module, in bytes."))),(0,a.mdx)("li",{parentName:"ul"},"Each of the modules, finished by a null byte (",(0,a.mdx)("inlineCode",{parentName:"li"},"\\0"),").")),(0,a.mdx)("pre",null,(0,a.mdx)("code",{parentName:"pre"},"` 0 1 2 3 4 5 6\n 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3\n+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n| Magic number | Header size |\n+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n| Startup code size | Module 0 offset |\n+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n| Module 0 length | |\n+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +\n| |\n+ ... +\n| |\n+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n| | Module n offset |\n+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n| Module n length | Module 0 code | Module 0 code | ... | \\0 |\n+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n| Module 1 code | Module 1 code | ... | \\0 | |\n+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +\n| |\n+ ... +\n| |\n+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n| | Module n code | Module n code | ... | \\0 |\n+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+`\n")),(0,a.mdx)("p",null,"This structure is optimal for an environment that is able to load all code in memory at once:"),(0,a.mdx)("ul",null,(0,a.mdx)("li",{parentName:"ul"},"By using the offset table, one can load any module in constant time, where the code for module ",(0,a.mdx)("inlineCode",{parentName:"li"},"x")," is located at ",(0,a.mdx)("inlineCode",{parentName:"li"},"file[(x + 3) * sizeof(uint32)]"),". Since there is a null character (",(0,a.mdx)("inlineCode",{parentName:"li"},"\\0"),") separating all modules, usually length does not even need to be used, and the module can be loaded directly as an ASCIIZ string."),(0,a.mdx)("li",{parentName:"ul"},"Startup code is always found at ",(0,a.mdx)("inlineCode",{parentName:"li"},"file[sizeof(uint32)]"),".")),(0,a.mdx)("p",null,"This bundling is usually used by iOS."),(0,a.mdx)("h2",{id:"file-ram-bundle"},"File RAM bundle"),(0,a.mdx)("p",null,"Each module is stored as a file, with the name ",(0,a.mdx)("inlineCode",{parentName:"p"},"js-modules/${id}.js"),", plus an extra file called ",(0,a.mdx)("inlineCode",{parentName:"p"},"UNBUNDLE")," is created, which its only content is the magic number, ",(0,a.mdx)("inlineCode",{parentName:"p"},"0xFB0BD1E5"),". Note that the ",(0,a.mdx)("inlineCode",{parentName:"p"},"UNBUNDLE")," file is created at the root.\nThis bundling is usually used by Android, since package contents are zipped, and access to a zipped file is much faster. If the indexed format was used instead, all the bundled should be unzipped at once to get the code for the corresponding module."))}f.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/51001101.e0c4b585.js b/assets/js/51001101.e0c4b585.js new file mode 100644 index 0000000000..74dcdf9b00 --- /dev/null +++ b/assets/js/51001101.e0c4b585.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkmetro_website=self.webpackChunkmetro_website||[]).push([[645],{3905:(e,t,n)=>{n.r(t),n.d(t,{MDXContext:()=>m,MDXProvider:()=>p,mdx:()=>g,useMDXComponents:()=>d,withMDXComponents:()=>c});var r=n(67294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(){return o=Object.assign||function(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var m=r.createContext({}),c=function(e){return function(t){var n=d(t.components);return r.createElement(e,o({},t,{components:n}))}},d=function(e){var t=r.useContext(m),n=t;return e&&(n="function"==typeof e?e(t):s(s({},t),e)),n},p=function(e){var t=d(e.components);return r.createElement(m.Provider,{value:t},e.children)},u="mdxType",h={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},f=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,i=e.parentName,m=l(e,["components","mdxType","originalType","parentName"]),c=d(n),p=a,u=c["".concat(i,".").concat(p)]||c[p]||h[p]||o;return n?r.createElement(u,s(s({ref:t},m),{},{components:n})):r.createElement(u,s({ref:t},m))}));function g(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,i=new Array(o);i[0]=f;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[u]="string"==typeof e?e:a,i[1]=s;for(var m=2;m{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>l,default:()=>h,frontMatter:()=>s,metadata:()=>m,toc:()=>d});var r=n(87462),a=n(63366),o=(n(67294),n(3905)),i=["components"],s={id:"caching",title:"Caching"},l=void 0,m={unversionedId:"caching",id:"caching",title:"Caching",description:"Out of the box, Metro speeds up builds using a local cache of transformed modules. Thanks to this cache, Metro doesn't need to retransform modules unless the source code (or current configuration) has changed since the last time they were transformed.",source:"@site/../docs/Caching.md",sourceDirName:".",slug:"/caching",permalink:"/docs/caching",draft:!1,editUrl:"https://github.com/facebook/metro/edit/main/docs/../docs/Caching.md",tags:[],version:"current",lastUpdatedAt:1726493799,formattedLastUpdatedAt:"Sep 16, 2024",frontMatter:{id:"caching",title:"Caching"},sidebar:"docs",previous:{title:"Bundle Formats",permalink:"/docs/bundling"},next:{title:"Module Resolution",permalink:"/docs/resolution"}},c={},d=[{value:"Built-in cache stores",id:"built-in-cache-stores",level:2},{value:"Custom cache stores",id:"custom-cache-stores",level:2}],p={toc:d},u="wrapper";function h(e){var t=e.components,n=(0,a.Z)(e,i);return(0,o.mdx)(u,(0,r.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,o.mdx)("p",null,"Out of the box, Metro speeds up builds using a ",(0,o.mdx)("strong",{parentName:"p"},"local cache")," of ",(0,o.mdx)("a",{parentName:"p",href:"/docs/concepts#transformation"},"transformed")," modules. Thanks to this cache, Metro doesn't need to retransform modules unless the source code (or current configuration) has changed since the last time they were transformed."),(0,o.mdx)("p",null,"Metro also has the ability to use a ",(0,o.mdx)("strong",{parentName:"p"},"remote cache"),". This can dramatically speed up builds for larger teams and/or larger codebases by reducing the amount of time spent locally building remote changes even further. For example, this is how we use Metro to build React Native apps at Meta (a codebase with many thousands of files and hundreds of daily active engineers)."),(0,o.mdx)("p",null,"A typical setup for a remote cache involves:"),(0,o.mdx)("ol",null,(0,o.mdx)("li",{parentName:"ol"},"A storage backend specific to your team (e.g."," ",(0,o.mdx)("a",{parentName:"li",href:"https://internalfb.com/sevmanager/view/3"},"S3")," ","bucket)."),(0,o.mdx)("li",{parentName:"ol"},"Running ",(0,o.mdx)("a",{parentName:"li",href:"/docs/cli#build-entry"},(0,o.mdx)("inlineCode",{parentName:"a"},"metro build"))," periodically (e.g. in a CI job) to populate the cache, using ",(0,o.mdx)("inlineCode",{parentName:"li"},"HttpStore")," (or a custom read/write cache store) in your Metro config."),(0,o.mdx)("li",{parentName:"ol"},"Configuring Metro on your development machines to read from the cache, using ",(0,o.mdx)("inlineCode",{parentName:"li"},"HttpGetStore")," (or a custom read-only cache store) in your Metro config.")),(0,o.mdx)("p",null,"The main option for configuring the Metro cache is ",(0,o.mdx)("a",{parentName:"p",href:"/docs/configuration#cachestores"},(0,o.mdx)("inlineCode",{parentName:"a"},"cacheStores")),". Typically, the local cache (e.g. ",(0,o.mdx)("inlineCode",{parentName:"p"},"FileStore"),") should be listed first, followed by the remote cache (e.g. ",(0,o.mdx)("inlineCode",{parentName:"p"},"HttpCache"),")."),(0,o.mdx)("h2",{id:"built-in-cache-stores"},"Built-in cache stores"),(0,o.mdx)("p",null,"Metro provides a number of built-in cache store implementations for use with the ",(0,o.mdx)("a",{parentName:"p",href:"/docs/configuration#cachestores"},(0,o.mdx)("inlineCode",{parentName:"a"},"cacheStores"))," config option:"),(0,o.mdx)("ul",null,(0,o.mdx)("li",{parentName:"ul"},(0,o.mdx)("strong",{parentName:"li"},(0,o.mdx)("inlineCode",{parentName:"strong"},"FileStore({root: string})"))," will store cache entries as files under the directory specified by ",(0,o.mdx)("inlineCode",{parentName:"li"},"root"),"."),(0,o.mdx)("li",{parentName:"ul"},(0,o.mdx)("strong",{parentName:"li"},(0,o.mdx)("inlineCode",{parentName:"strong"},"AutoCleanFileStore()"))," is a ",(0,o.mdx)("inlineCode",{parentName:"li"},"FileStore")," that periodically cleans up old entries. It accepts the same options as ",(0,o.mdx)("inlineCode",{parentName:"li"},"FileStore")," plus the following:",(0,o.mdx)("ul",{parentName:"li"},(0,o.mdx)("li",{parentName:"ul"},(0,o.mdx)("strong",{parentName:"li"},(0,o.mdx)("inlineCode",{parentName:"strong"},"options.intervalMs: number"))," is the time in milliseconds between cleanup attempts. Defaults to 10 minutes."),(0,o.mdx)("li",{parentName:"ul"},(0,o.mdx)("strong",{parentName:"li"},(0,o.mdx)("inlineCode",{parentName:"strong"},"options.cleanupThresholdMs: number"))," is the minimum time in milliseconds since the last modification of an entry before it can be deleted. Defaults to 3 days."))),(0,o.mdx)("li",{parentName:"ul"},(0,o.mdx)("strong",{parentName:"li"},(0,o.mdx)("inlineCode",{parentName:"strong"},"HttpStore(options)"))," is a bare-bones remote cache client that reads (",(0,o.mdx)("inlineCode",{parentName:"li"},"GET"),") and writes (",(0,o.mdx)("inlineCode",{parentName:"li"},"PUT"),") compressed cache artifacts over HTTP or HTTPS.",(0,o.mdx)("ul",{parentName:"li"},(0,o.mdx)("li",{parentName:"ul"},(0,o.mdx)("strong",{parentName:"li"},(0,o.mdx)("inlineCode",{parentName:"strong"},"options.endpoint: string"))," is the base URL for the cache server. For example, an ",(0,o.mdx)("inlineCode",{parentName:"li"},"HttpStore")," with ",(0,o.mdx)("inlineCode",{parentName:"li"},"'http://www.example.com/endpoint'")," as the endpoint would issue requests to URLs such as ",(0,o.mdx)("inlineCode",{parentName:"li"},"http://www.example.com/endpoint/c083bff944879d9f528cf185eba0f496bc10a47d"),"."),(0,o.mdx)("li",{parentName:"ul"},(0,o.mdx)("strong",{parentName:"li"},(0,o.mdx)("inlineCode",{parentName:"strong"},"options.timeout: number"))," is the timeout for requests to the cache server, in milliseconds. Defaults to 5000."),(0,o.mdx)("li",{parentName:"ul"},(0,o.mdx)("strong",{parentName:"li"},(0,o.mdx)("inlineCode",{parentName:"strong"},"options.family: 4 | 6"))," is the same as the ",(0,o.mdx)("inlineCode",{parentName:"li"},"family")," parameter to Node's ",(0,o.mdx)("a",{parentName:"li",href:"https://nodejs.org/api/http.html#httprequesturl-options-callback"},(0,o.mdx)("inlineCode",{parentName:"a"},"http.request")),"."),(0,o.mdx)("li",{parentName:"ul"},(0,o.mdx)("strong",{parentName:"li"},(0,o.mdx)("inlineCode",{parentName:"strong"},"options.cert"),", ",(0,o.mdx)("inlineCode",{parentName:"strong"},"options.ca"),", ",(0,o.mdx)("inlineCode",{parentName:"strong"},"options.key")),": HTTPS options passed directly to ",(0,o.mdx)("a",{parentName:"li",href:"https://nodejs.org/api/https.html"},"Node's built-in HTTPS client"),"."))),(0,o.mdx)("li",{parentName:"ul"},(0,o.mdx)("strong",{parentName:"li"},(0,o.mdx)("inlineCode",{parentName:"strong"},"HttpGetStore(options)"))," is a read-only version of ",(0,o.mdx)("inlineCode",{parentName:"li"},"HttpStore"),".")),(0,o.mdx)("p",null,"You can import these classes from the ",(0,o.mdx)("inlineCode",{parentName:"p"},"metro-cache")," package or get them through the function form of ",(0,o.mdx)("inlineCode",{parentName:"p"},"cacheStores"),":"),(0,o.mdx)("pre",null,(0,o.mdx)("code",{parentName:"pre",className:"language-js"},"// metro.config.js\nconst os = require('node:os');\nconst path = require('node:path');\n\nmodule.exports = {\n cacheStores: ({ FileStore }) => [\n new FileStore({\n root: path.join(os.tmpdir(), 'metro-cache'),\n }),\n ],\n};\n\n")),(0,o.mdx)("h2",{id:"custom-cache-stores"},"Custom cache stores"),(0,o.mdx)("p",null,"To implement a custom cache store, pass an instance of a class with the following interface into ",(0,o.mdx)("a",{parentName:"p",href:"/docs/configuration#cachestores"},(0,o.mdx)("inlineCode",{parentName:"a"},"cacheStores")),":"),(0,o.mdx)("pre",null,(0,o.mdx)("code",{parentName:"pre",className:"language-flow"},"interface CacheStore {\n // Read an entry from the cache. Returns `null` if not found.\n get(key: Buffer): ?T | Promise;\n\n // Write an entry to the cache (if writable) or do nothing (if read-only)\n set(key: Buffer, value: T): void | Promise;\n\n // Clear the cache (if possible) or do nothing\n clear(): void | Promise;\n}\n\ntype JsonSerializable = /* Any JSON-serializable value */;\n")),(0,o.mdx)("p",null,"The value of a cache entry is either an instance of ",(0,o.mdx)("a",{parentName:"p",href:"https://nodejs.org/api/buffer.html#buffer"},(0,o.mdx)("inlineCode",{parentName:"a"},"Buffer"))," or a JSON-serializable value (with unspecified internal structure in both cases). For a given cache key, ",(0,o.mdx)("inlineCode",{parentName:"p"},"get()")," ",(0,o.mdx)("em",{parentName:"p"},"must")," return the same type of value that was originally provided to ",(0,o.mdx)("inlineCode",{parentName:"p"},"set()"),"."))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/51001101.f523a36e.js b/assets/js/51001101.f523a36e.js deleted file mode 100644 index b89a249325..0000000000 --- a/assets/js/51001101.f523a36e.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkmetro_website=self.webpackChunkmetro_website||[]).push([[645],{3905:(e,t,n)=>{n.r(t),n.d(t,{MDXContext:()=>m,MDXProvider:()=>p,mdx:()=>g,useMDXComponents:()=>d,withMDXComponents:()=>c});var r=n(67294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(){return o=Object.assign||function(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var m=r.createContext({}),c=function(e){return function(t){var n=d(t.components);return r.createElement(e,o({},t,{components:n}))}},d=function(e){var t=r.useContext(m),n=t;return e&&(n="function"==typeof e?e(t):s(s({},t),e)),n},p=function(e){var t=d(e.components);return r.createElement(m.Provider,{value:t},e.children)},u="mdxType",h={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},f=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,i=e.parentName,m=l(e,["components","mdxType","originalType","parentName"]),c=d(n),p=a,u=c["".concat(i,".").concat(p)]||c[p]||h[p]||o;return n?r.createElement(u,s(s({ref:t},m),{},{components:n})):r.createElement(u,s({ref:t},m))}));function g(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,i=new Array(o);i[0]=f;var s={};for(var l in t)hasOwnProperty.call(t,l)&&(s[l]=t[l]);s.originalType=e,s[u]="string"==typeof e?e:a,i[1]=s;for(var m=2;m{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>l,default:()=>h,frontMatter:()=>s,metadata:()=>m,toc:()=>d});var r=n(87462),a=n(63366),o=(n(67294),n(3905)),i=["components"],s={id:"caching",title:"Caching"},l=void 0,m={unversionedId:"caching",id:"caching",title:"Caching",description:"Out of the box, Metro speeds up builds using a local cache of transformed modules. Thanks to this cache, Metro doesn't need to retransform modules unless the source code (or current configuration) has changed since the last time they were transformed.",source:"@site/../docs/Caching.md",sourceDirName:".",slug:"/caching",permalink:"/docs/caching",draft:!1,editUrl:"https://github.com/facebook/metro/edit/main/docs/../docs/Caching.md",tags:[],version:"current",lastUpdatedAt:1725545477,formattedLastUpdatedAt:"Sep 5, 2024",frontMatter:{id:"caching",title:"Caching"},sidebar:"docs",previous:{title:"Bundle Formats",permalink:"/docs/bundling"},next:{title:"Module Resolution",permalink:"/docs/resolution"}},c={},d=[{value:"Built-in cache stores",id:"built-in-cache-stores",level:2},{value:"Custom cache stores",id:"custom-cache-stores",level:2}],p={toc:d},u="wrapper";function h(e){var t=e.components,n=(0,a.Z)(e,i);return(0,o.mdx)(u,(0,r.Z)({},p,n,{components:t,mdxType:"MDXLayout"}),(0,o.mdx)("p",null,"Out of the box, Metro speeds up builds using a ",(0,o.mdx)("strong",{parentName:"p"},"local cache")," of ",(0,o.mdx)("a",{parentName:"p",href:"/docs/concepts#transformation"},"transformed")," modules. Thanks to this cache, Metro doesn't need to retransform modules unless the source code (or current configuration) has changed since the last time they were transformed."),(0,o.mdx)("p",null,"Metro also has the ability to use a ",(0,o.mdx)("strong",{parentName:"p"},"remote cache"),". This can dramatically speed up builds for larger teams and/or larger codebases by reducing the amount of time spent locally building remote changes even further. For example, this is how we use Metro to build React Native apps at Meta (a codebase with many thousands of files and hundreds of daily active engineers)."),(0,o.mdx)("p",null,"A typical setup for a remote cache involves:"),(0,o.mdx)("ol",null,(0,o.mdx)("li",{parentName:"ol"},"A storage backend specific to your team (e.g."," ",(0,o.mdx)("a",{parentName:"li",href:"https://internalfb.com/sevmanager/view/3"},"S3")," ","bucket)."),(0,o.mdx)("li",{parentName:"ol"},"Running ",(0,o.mdx)("a",{parentName:"li",href:"/docs/cli#build-entry"},(0,o.mdx)("inlineCode",{parentName:"a"},"metro build"))," periodically (e.g. in a CI job) to populate the cache, using ",(0,o.mdx)("inlineCode",{parentName:"li"},"HttpStore")," (or a custom read/write cache store) in your Metro config."),(0,o.mdx)("li",{parentName:"ol"},"Configuring Metro on your development machines to read from the cache, using ",(0,o.mdx)("inlineCode",{parentName:"li"},"HttpGetStore")," (or a custom read-only cache store) in your Metro config.")),(0,o.mdx)("p",null,"The main option for configuring the Metro cache is ",(0,o.mdx)("a",{parentName:"p",href:"/docs/configuration#cachestores"},(0,o.mdx)("inlineCode",{parentName:"a"},"cacheStores")),". Typically, the local cache (e.g. ",(0,o.mdx)("inlineCode",{parentName:"p"},"FileStore"),") should be listed first, followed by the remote cache (e.g. ",(0,o.mdx)("inlineCode",{parentName:"p"},"HttpCache"),")."),(0,o.mdx)("h2",{id:"built-in-cache-stores"},"Built-in cache stores"),(0,o.mdx)("p",null,"Metro provides a number of built-in cache store implementations for use with the ",(0,o.mdx)("a",{parentName:"p",href:"/docs/configuration#cachestores"},(0,o.mdx)("inlineCode",{parentName:"a"},"cacheStores"))," config option:"),(0,o.mdx)("ul",null,(0,o.mdx)("li",{parentName:"ul"},(0,o.mdx)("strong",{parentName:"li"},(0,o.mdx)("inlineCode",{parentName:"strong"},"FileStore({root: string})"))," will store cache entries as files under the directory specified by ",(0,o.mdx)("inlineCode",{parentName:"li"},"root"),"."),(0,o.mdx)("li",{parentName:"ul"},(0,o.mdx)("strong",{parentName:"li"},(0,o.mdx)("inlineCode",{parentName:"strong"},"AutoCleanFileStore()"))," is a ",(0,o.mdx)("inlineCode",{parentName:"li"},"FileStore")," that periodically cleans up old entries. It accepts the same options as ",(0,o.mdx)("inlineCode",{parentName:"li"},"FileStore")," plus the following:",(0,o.mdx)("ul",{parentName:"li"},(0,o.mdx)("li",{parentName:"ul"},(0,o.mdx)("strong",{parentName:"li"},(0,o.mdx)("inlineCode",{parentName:"strong"},"options.intervalMs: number"))," is the time in milliseconds between cleanup attempts. Defaults to 10 minutes."),(0,o.mdx)("li",{parentName:"ul"},(0,o.mdx)("strong",{parentName:"li"},(0,o.mdx)("inlineCode",{parentName:"strong"},"options.cleanupThresholdMs: number"))," is the minimum time in milliseconds since the last modification of an entry before it can be deleted. Defaults to 3 days."))),(0,o.mdx)("li",{parentName:"ul"},(0,o.mdx)("strong",{parentName:"li"},(0,o.mdx)("inlineCode",{parentName:"strong"},"HttpStore(options)"))," is a bare-bones remote cache client that reads (",(0,o.mdx)("inlineCode",{parentName:"li"},"GET"),") and writes (",(0,o.mdx)("inlineCode",{parentName:"li"},"PUT"),") compressed cache artifacts over HTTP or HTTPS.",(0,o.mdx)("ul",{parentName:"li"},(0,o.mdx)("li",{parentName:"ul"},(0,o.mdx)("strong",{parentName:"li"},(0,o.mdx)("inlineCode",{parentName:"strong"},"options.endpoint: string"))," is the base URL for the cache server. For example, an ",(0,o.mdx)("inlineCode",{parentName:"li"},"HttpStore")," with ",(0,o.mdx)("inlineCode",{parentName:"li"},"'http://www.example.com/endpoint'")," as the endpoint would issue requests to URLs such as ",(0,o.mdx)("inlineCode",{parentName:"li"},"http://www.example.com/endpoint/c083bff944879d9f528cf185eba0f496bc10a47d"),"."),(0,o.mdx)("li",{parentName:"ul"},(0,o.mdx)("strong",{parentName:"li"},(0,o.mdx)("inlineCode",{parentName:"strong"},"options.timeout: number"))," is the timeout for requests to the cache server, in milliseconds. Defaults to 5000."),(0,o.mdx)("li",{parentName:"ul"},(0,o.mdx)("strong",{parentName:"li"},(0,o.mdx)("inlineCode",{parentName:"strong"},"options.family: 4 | 6"))," is the same as the ",(0,o.mdx)("inlineCode",{parentName:"li"},"family")," parameter to Node's ",(0,o.mdx)("a",{parentName:"li",href:"https://nodejs.org/api/http.html#httprequesturl-options-callback"},(0,o.mdx)("inlineCode",{parentName:"a"},"http.request")),"."),(0,o.mdx)("li",{parentName:"ul"},(0,o.mdx)("strong",{parentName:"li"},(0,o.mdx)("inlineCode",{parentName:"strong"},"options.cert"),", ",(0,o.mdx)("inlineCode",{parentName:"strong"},"options.ca"),", ",(0,o.mdx)("inlineCode",{parentName:"strong"},"options.key")),": HTTPS options passed directly to ",(0,o.mdx)("a",{parentName:"li",href:"https://nodejs.org/api/https.html"},"Node's built-in HTTPS client"),"."))),(0,o.mdx)("li",{parentName:"ul"},(0,o.mdx)("strong",{parentName:"li"},(0,o.mdx)("inlineCode",{parentName:"strong"},"HttpGetStore(options)"))," is a read-only version of ",(0,o.mdx)("inlineCode",{parentName:"li"},"HttpStore"),".")),(0,o.mdx)("p",null,"You can import these classes from the ",(0,o.mdx)("inlineCode",{parentName:"p"},"metro-cache")," package or get them through the function form of ",(0,o.mdx)("inlineCode",{parentName:"p"},"cacheStores"),":"),(0,o.mdx)("pre",null,(0,o.mdx)("code",{parentName:"pre",className:"language-js"},"// metro.config.js\nconst os = require('node:os');\nconst path = require('node:path');\n\nmodule.exports = {\n cacheStores: ({ FileStore }) => [\n new FileStore({\n root: path.join(os.tmpdir(), 'metro-cache'),\n }),\n ],\n};\n\n")),(0,o.mdx)("h2",{id:"custom-cache-stores"},"Custom cache stores"),(0,o.mdx)("p",null,"To implement a custom cache store, pass an instance of a class with the following interface into ",(0,o.mdx)("a",{parentName:"p",href:"/docs/configuration#cachestores"},(0,o.mdx)("inlineCode",{parentName:"a"},"cacheStores")),":"),(0,o.mdx)("pre",null,(0,o.mdx)("code",{parentName:"pre",className:"language-flow"},"interface CacheStore {\n // Read an entry from the cache. Returns `null` if not found.\n get(key: Buffer): ?T | Promise;\n\n // Write an entry to the cache (if writable) or do nothing (if read-only)\n set(key: Buffer, value: T): void | Promise;\n\n // Clear the cache (if possible) or do nothing\n clear(): void | Promise;\n}\n\ntype JsonSerializable = /* Any JSON-serializable value */;\n")),(0,o.mdx)("p",null,"The value of a cache entry is either an instance of ",(0,o.mdx)("a",{parentName:"p",href:"https://nodejs.org/api/buffer.html#buffer"},(0,o.mdx)("inlineCode",{parentName:"a"},"Buffer"))," or a JSON-serializable value (with unspecified internal structure in both cases). For a given cache key, ",(0,o.mdx)("inlineCode",{parentName:"p"},"get()")," ",(0,o.mdx)("em",{parentName:"p"},"must")," return the same type of value that was originally provided to ",(0,o.mdx)("inlineCode",{parentName:"p"},"set()"),"."))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/6373d68f.cfd624d3.js b/assets/js/6373d68f.cfd624d3.js new file mode 100644 index 0000000000..0b64a4b3cf --- /dev/null +++ b/assets/js/6373d68f.cfd624d3.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkmetro_website=self.webpackChunkmetro_website||[]).push([[874],{3905:(e,n,a)=>{a.r(n),a.d(n,{MDXContext:()=>d,MDXProvider:()=>c,mdx:()=>h,useMDXComponents:()=>s,withMDXComponents:()=>p});var t=a(67294);function o(e,n,a){return n in e?Object.defineProperty(e,n,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[n]=a,e}function i(){return i=Object.assign||function(e){for(var n=1;n=0||(o[a]=e[a]);return o}(e,n);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(t=0;t=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(o[a]=e[a])}return o}var d=t.createContext({}),p=function(e){return function(n){var a=s(n.components);return t.createElement(e,i({},n,{components:a}))}},s=function(e){var n=t.useContext(d),a=n;return e&&(a="function"==typeof e?e(n):m(m({},n),e)),a},c=function(e){var n=s(e.components);return t.createElement(d.Provider,{value:n},e.children)},u="mdxType",f={inlineCode:"code",wrapper:function(e){var n=e.children;return t.createElement(t.Fragment,{},n)}},x=t.forwardRef((function(e,n){var a=e.components,o=e.mdxType,i=e.originalType,r=e.parentName,d=l(e,["components","mdxType","originalType","parentName"]),p=s(a),c=o,u=p["".concat(r,".").concat(c)]||p[c]||f[c]||i;return a?t.createElement(u,m(m({ref:n},d),{},{components:a})):t.createElement(u,m({ref:n},d))}));function h(e,n){var a=arguments,o=n&&n.mdxType;if("string"==typeof e||o){var i=a.length,r=new Array(i);r[0]=x;var m={};for(var l in n)hasOwnProperty.call(n,l)&&(m[l]=n[l]);m.originalType=e,m[u]="string"==typeof e?e:o,r[1]=m;for(var d=2;d{a.r(n),a.d(n,{assets:()=>p,contentTitle:()=>l,default:()=>f,frontMatter:()=>m,metadata:()=>d,toc:()=>s});var t=a(87462),o=a(63366),i=(a(67294),a(3905)),r=["components"],m={id:"source-map-format",title:"Source Map Format"},l=void 0,d={unversionedId:"source-map-format",id:"source-map-format",title:"Source Map Format",description:"Metro produces standard source maps along with its JavaScript bundle output. In addition to the standard information, Metro encodes extra information in vendor-specific fields within the source map. This page serves as a specification for this encoding.",source:"@site/../docs/SourceMapFormat.md",sourceDirName:".",slug:"/source-map-format",permalink:"/docs/source-map-format",draft:!1,editUrl:"https://github.com/facebook/metro/edit/main/docs/../docs/SourceMapFormat.md",tags:[],version:"current",lastUpdatedAt:1726493799,formattedLastUpdatedAt:"Sep 16, 2024",frontMatter:{id:"source-map-format",title:"Source Map Format"},sidebar:"docs",previous:{title:"Module Resolution",permalink:"/docs/resolution"}},p={},s=[{value:"x_facebook_sources",id:"x_facebook_sources",level:2},{value:"Metadata tuple",id:"metadata-tuple",level:3},{value:"Function map",id:"function-map",level:4},{value:"Function map mappings field encoding",id:"function-map-mappings-field-encoding",level:5},{value:"Example",id:"example",level:3},{value:"x_google_ignoreList",id:"x_google_ignorelist",level:2}],c={toc:s},u="wrapper";function f(e){var n=e.components,a=(0,o.Z)(e,r);return(0,i.mdx)(u,(0,t.Z)({},c,a,{components:n,mdxType:"MDXLayout"}),(0,i.mdx)("p",null,"Metro produces standard ",(0,i.mdx)("a",{parentName:"p",href:"https://sourcemaps.info/spec.html"},"source maps")," along with its JavaScript bundle output. In addition to the standard information, Metro encodes extra information in ",(0,i.mdx)("a",{parentName:"p",href:"https://sourcemaps.info/spec.html#h.ghqpj1ytqjbm"},"vendor-specific fields")," within the source map. This page serves as a specification for this encoding."),(0,i.mdx)("admonition",{type:"note"},(0,i.mdx)("p",{parentName:"admonition"},"The content on this page assumes familiarity with the ",(0,i.mdx)("a",{parentName:"p",href:"https://sourcemaps.info/spec.html"},"source map specification"),". Check out ",(0,i.mdx)("a",{parentName:"p",href:"https://www.bugsnag.com/blog/source-maps"},"Anatomy of source maps")," for a general introduction to source maps and how they work.")),(0,i.mdx)("h2",{id:"x_facebook_sources"},(0,i.mdx)("inlineCode",{parentName:"h2"},"x_facebook_sources")),(0,i.mdx)("p",null,"The ",(0,i.mdx)("inlineCode",{parentName:"p"},"x_facebook_sources")," field encodes metadata about source files in a source map. Each piece of metadata represents some attribute intrinsic to the source code of that particular file - for example, the result of running some analysis over the AST. This allows tools such as debuggers and JS engines to access such analyses efficiently, without needing to parse or even have access to the source code."),(0,i.mdx)("p",null,"In the same way that the standard ",(0,i.mdx)("a",{parentName:"p",href:"https://sourcemaps.info/spec.html#h.ghqpj1ytqjbm:~:text=sourceRoot%22%3A%20%22%22%2C-,%22sources%22%3A,-%5B%22foo.js%22%2C%20%22bar"},(0,i.mdx)("inlineCode",{parentName:"a"},"sources"))," field is a list of source URLs and ",(0,i.mdx)("a",{parentName:"p",href:"https://sourcemaps.info/spec.html#h.ghqpj1ytqjbm:~:text=js%22%2C%20%22bar.js%22%5D%2C-,%22sourcesContent%22%3A,-%5Bnull%2C%20null%5D%2C"},(0,i.mdx)("inlineCode",{parentName:"a"},"sourcesContent"))," is a list of (optional) source code strings, ",(0,i.mdx)("inlineCode",{parentName:"p"},"x_facebook_sources")," is a list of optional ",(0,i.mdx)("strong",{parentName:"p"},"metadata tuples"),". The ",(0,i.mdx)("em",{parentName:"p"},"i"),"-th metadata tuple (",(0,i.mdx)("inlineCode",{parentName:"p"},"x_facebook_sources[i]"),") corresponds to the source file whose URL is ",(0,i.mdx)("inlineCode",{parentName:"p"},"sources[i]"),"."),(0,i.mdx)("p",null,"In nested (indexed) source maps, ",(0,i.mdx)("inlineCode",{parentName:"p"},"x_facebook_sources")," may appear as part of any nested source map in ",(0,i.mdx)("a",{parentName:"p",href:"https://sourcemaps.info/spec.html#h.535es3xeprgt"},(0,i.mdx)("inlineCode",{parentName:"a"},"sections"))," that itself has a ",(0,i.mdx)("inlineCode",{parentName:"p"},"sources")," field."),(0,i.mdx)("p",null,"If present, ",(0,i.mdx)("inlineCode",{parentName:"p"},"x_facebook_sources")," may be a different length than ",(0,i.mdx)("inlineCode",{parentName:"p"},"sources")," (but usually shouldn't be). In particular, if it's shorter than ",(0,i.mdx)("inlineCode",{parentName:"p"},"sources"),", ",(0,i.mdx)("inlineCode",{parentName:"p"},"x_facebook_sources")," interpreted as if it were padded with ",(0,i.mdx)("inlineCode",{parentName:"p"},"null")," values to match the length of ",(0,i.mdx)("inlineCode",{parentName:"p"},"sources"),"."),(0,i.mdx)("admonition",{type:"info"},(0,i.mdx)("p",{parentName:"admonition"},"If you are writing a tool that processes source maps generated by Metro, and want to generate a new source map containing a valid ",(0,i.mdx)("inlineCode",{parentName:"p"},"x_facebook_sources")," field, you'll mainly need to ensure that ",(0,i.mdx)("inlineCode",{parentName:"p"},"x_facebook_sources[i]")," still corresponds to ",(0,i.mdx)("inlineCode",{parentName:"p"},"sources[i]")," in the output - even if your tool reorders, adds or deletes elements in ",(0,i.mdx)("inlineCode",{parentName:"p"},"sources"),". Notably, this can be done ",(0,i.mdx)("em",{parentName:"p"},"without")," parsing/decoding the metadata tuples: unless your tool actively needs to access the information within them, you can treat them as opaque blobs of JSON."),(0,i.mdx)("p",{parentName:"admonition"},"If a tool cannot guarantee that the ",(0,i.mdx)("inlineCode",{parentName:"p"},"sources")," and ",(0,i.mdx)("inlineCode",{parentName:"p"},"x_facebook_sources")," arrays will stay in sync, it should delete the ",(0,i.mdx)("inlineCode",{parentName:"p"},"x_facebook_sources")," field from its output.")),(0,i.mdx)("h3",{id:"metadata-tuple"},"Metadata tuple"),(0,i.mdx)("p",null,"Each metadata tuple is encoded as an array of zero or more entries. Each entry may be ",(0,i.mdx)("inlineCode",{parentName:"p"},"null")," to signify that it's missing. A run of trailing ",(0,i.mdx)("inlineCode",{parentName:"p"},"null"),"s may be truncated from the end of the tuple with no change in meaning. The metadata tuple itself may also be ",(0,i.mdx)("inlineCode",{parentName:"p"},"null")," to signify that the source file has no associated metadata."),(0,i.mdx)("p",null,"The indices in each metadata tuple are assigned as follows:"),(0,i.mdx)("ul",null,(0,i.mdx)("li",{parentName:"ul"},"Index 0: ",(0,i.mdx)("a",{parentName:"li",href:"#function-map"},"Function map")," or ",(0,i.mdx)("inlineCode",{parentName:"li"},"null"),".",(0,i.mdx)("ul",{parentName:"li"},(0,i.mdx)("li",{parentName:"ul"},"In Metro, this is the result of calling ",(0,i.mdx)("a",{parentName:"li",href:"https://github.com/facebook/metro/blob/main/packages/metro-source-map/src/generateFunctionMap.js"},(0,i.mdx)("inlineCode",{parentName:"a"},"generateFunctionMap"))," on the source AST."))),(0,i.mdx)("li",{parentName:"ul"},"Index 1-\u221e: Reserved for future use.")),(0,i.mdx)("h4",{id:"function-map"},"Function map"),(0,i.mdx)("p",null,"A function map is encoded as an object with the following two fields:"),(0,i.mdx)("ul",null,(0,i.mdx)("li",{parentName:"ul"},(0,i.mdx)("inlineCode",{parentName:"li"},"names"),": An array of strings."),(0,i.mdx)("li",{parentName:"ul"},(0,i.mdx)("inlineCode",{parentName:"li"},"mappings"),": A string following the ",(0,i.mdx)("a",{parentName:"li",href:"#function-map-mappings-field-encoding"},"encoding")," described below.")),(0,i.mdx)("p",null,"When decoded, ",(0,i.mdx)("inlineCode",{parentName:"p"},"mappings")," represents a list of 3-tuples of integers: ",(0,i.mdx)("inlineCode",{parentName:"p"},"(column, nameIndex, line), (column, nameIndex, line), ..."),". The list is ordered by ",(0,i.mdx)("inlineCode",{parentName:"p"},"line")," and then ",(0,i.mdx)("inlineCode",{parentName:"p"},"column"),"."),(0,i.mdx)("p",null,"The presence of a 3-tuple ",(0,i.mdx)("inlineCode",{parentName:"p"},"(column, nameIndex, line)")," means that the ",(0,i.mdx)("em",{parentName:"p"},"local function name")," in the code region beginning at ",(0,i.mdx)("inlineCode",{parentName:"p"},"line")," and ",(0,i.mdx)("inlineCode",{parentName:"p"},"column")," (in the source file described by the current metadata tuple) is ",(0,i.mdx)("inlineCode",{parentName:"p"},"names[nameIndex]"),"."),(0,i.mdx)("h5",{id:"function-map-mappings-field-encoding"},"Function map ",(0,i.mdx)("inlineCode",{parentName:"h5"},"mappings")," field encoding"),(0,i.mdx)("p",null,"The value of the ",(0,i.mdx)("inlineCode",{parentName:"p"},"mappings")," field is described by the ",(0,i.mdx)("em",{parentName:"p"},"Mappings")," production of the grammar detailed below."),(0,i.mdx)("ol",null,(0,i.mdx)("li",{parentName:"ol"},(0,i.mdx)("inlineCode",{parentName:"li"},'Mappings = [ ";" ] LineMappings { ";" { ";" } LineMappings }')),(0,i.mdx)("li",{parentName:"ol"},(0,i.mdx)("inlineCode",{parentName:"li"},'LineMappings = FirstColumnMapping "," ColumnMapping')),(0,i.mdx)("li",{parentName:"ol"},(0,i.mdx)("inlineCode",{parentName:"li"},"FirstColumnMapping = VLQ VLQ VLQ")),(0,i.mdx)("li",{parentName:"ol"},(0,i.mdx)("inlineCode",{parentName:"li"},"ColumnMapping = VLQ VLQ [ VLQ ]")),(0,i.mdx)("li",{parentName:"ol"},(0,i.mdx)("inlineCode",{parentName:"li"},"VLQ =")," ",(0,i.mdx)("em",{parentName:"li"},"A single Base64-encoded variable-length quantity, as defined in the ",(0,i.mdx)("a",{parentName:"em",href:"https://sourcemaps.info/spec.html#h.crcf4lqeivt8"},"source map specification")),".")),(0,i.mdx)("admonition",{type:"note"},(0,i.mdx)("p",{parentName:"admonition"},"The above grammar uses the following BNF-like notation:"),(0,i.mdx)("table",{parentName:"admonition"},(0,i.mdx)("thead",{parentName:"table"},(0,i.mdx)("tr",{parentName:"thead"},(0,i.mdx)("th",{parentName:"tr",align:null},"Notation"),(0,i.mdx)("th",{parentName:"tr",align:null},"Meaning"))),(0,i.mdx)("tbody",{parentName:"table"},(0,i.mdx)("tr",{parentName:"tbody"},(0,i.mdx)("td",{parentName:"tr",align:null},(0,i.mdx)("inlineCode",{parentName:"td"},"[ X ]")),(0,i.mdx)("td",{parentName:"tr",align:null},(0,i.mdx)("em",{parentName:"td"},"X")," appears zero or 1 times.")),(0,i.mdx)("tr",{parentName:"tbody"},(0,i.mdx)("td",{parentName:"tr",align:null},(0,i.mdx)("inlineCode",{parentName:"td"},"{ X }")),(0,i.mdx)("td",{parentName:"tr",align:null},(0,i.mdx)("em",{parentName:"td"},"X")," appears 0 or more times.")),(0,i.mdx)("tr",{parentName:"tbody"},(0,i.mdx)("td",{parentName:"tr",align:null},(0,i.mdx)("inlineCode",{parentName:"td"},'"foo"')),(0,i.mdx)("td",{parentName:"tr",align:null},"The literal characters ",(0,i.mdx)("inlineCode",{parentName:"td"},"foo"),"."))))),(0,i.mdx)("p",null,"The three VLQs in ",(0,i.mdx)("em",{parentName:"p"},"FirstColumnMapping")," or ",(0,i.mdx)("em",{parentName:"p"},"ColumnMapping")," represent, in this order:"),(0,i.mdx)("ol",null,(0,i.mdx)("li",{parentName:"ol"},(0,i.mdx)("strong",{parentName:"li"},"Column delta"),":",(0,i.mdx)("ul",{parentName:"li"},(0,i.mdx)("li",{parentName:"ul"},"In ",(0,i.mdx)("em",{parentName:"li"},"FirstColumnMapping"),": The column offset from the beginning of the line. (0 = first column)"),(0,i.mdx)("li",{parentName:"ul"},"In ",(0,i.mdx)("em",{parentName:"li"},"ColumnMapping"),": The column offset from the last-encountered ",(0,i.mdx)("em",{parentName:"li"},"FirstColumnMapping")," or ",(0,i.mdx)("em",{parentName:"li"},"ColumnMapping"),"."))),(0,i.mdx)("li",{parentName:"ol"},(0,i.mdx)("strong",{parentName:"li"},"Name delta"),": The name index offset from the last-encountered ",(0,i.mdx)("em",{parentName:"li"},"FirstColumnMapping")," or ",(0,i.mdx)("em",{parentName:"li"},"ColumnMapping"),". This is ",(0,i.mdx)("em",{parentName:"li"},"not")," reset between lines."),(0,i.mdx)("li",{parentName:"ol"},(0,i.mdx)("strong",{parentName:"li"},"Line delta"),": The line offset from the last-encountered ",(0,i.mdx)("em",{parentName:"li"},"FirstColumnMapping")," or ",(0,i.mdx)("em",{parentName:"li"},"ColumnMapping"),". This is ",(0,i.mdx)("em",{parentName:"li"},"not")," reset between lines.",(0,i.mdx)("ul",{parentName:"li"},(0,i.mdx)("li",{parentName:"ul"},"This MUST be 0 (Base64 VLQ: ",(0,i.mdx)("inlineCode",{parentName:"li"},"A"),") if it is part of a ",(0,i.mdx)("em",{parentName:"li"},"ColumnMapping"),"."),(0,i.mdx)("li",{parentName:"ul"},"Implementations SHOULD omit this field from the encoded form of ",(0,i.mdx)("em",{parentName:"li"},"ColumnMapping"),".")))),(0,i.mdx)("h3",{id:"example"},"Example"),(0,i.mdx)("p",null,"Given a single source file called ",(0,i.mdx)("inlineCode",{parentName:"p"},"file.js"),", a complete source map might look like this:"),(0,i.mdx)("admonition",{type:"note"},(0,i.mdx)("p",{parentName:"admonition"},"Comments are for illustrative purposes - the source map format does not allow comments.")),(0,i.mdx)("pre",null,(0,i.mdx)("code",{parentName:"pre",className:"language-json"},'{\n "version": 3,\n "sources": ["file.js"],\n "sourcesContent": ["function a(){} function b(){}"],\n "mappings": "AAAA", // NOTE: Simplified\n "x_facebook_sources": [\n // Metadata tuple for source #0 (file.js)\n [\n // Metadata item #0.0 = function map for source #0 (file.js)\n {\n // a from 1:0\n // from 1:14\n // b from 1:15\n // (See detailed decoding procedure below.)\n "mappings": "AAA,cC,CC",\n "names": [\n "a",\n "",\n "b",\n ]\n }\n ]\n ]\n}\n')),(0,i.mdx)("p",null,"The decoding procedure for the function map in the above example is illustrated by the following code:"),(0,i.mdx)("pre",null,(0,i.mdx)("code",{parentName:"pre",className:"language-js"},"const decoded = [];\nconst names = ['a', '', 'b']; // From the function map\n\nlet column = 0, nameIndex = 0, line = 1;\ncolumn += 0 /* A */; nameIndex += 0 /* A */; line += 0 /* A */;\ndecoded.push({column, name: names[nameIndex] /* 'a' */, line});\n\ncolumn += 14 /* c */; nameIndex += 1 /* C */; // no line delta\ndecoded.push({column, name: names[nameIndex] /* '' */, line});\n\ncolumn += 1 /* C */; nameIndex += 1 /* C */; // no line delta\ndecoded.push({column, name: names[nameIndex] /* 'b' */, line});\n\n/*\n decoded = [\n {column: 0, name: 'a', line: 1},\n {column: 14, name: '', line: 1},\n {column: 15, name: 'b', line: 1},\n ]\n*/\n")),(0,i.mdx)("h2",{id:"x_google_ignorelist"},(0,i.mdx)("inlineCode",{parentName:"h2"},"x_google_ignoreList")),(0,i.mdx)("p",null,"Metro's source maps include the ",(0,i.mdx)("a",{parentName:"p",href:"https://developer.chrome.com/articles/x-google-ignore-list/"},(0,i.mdx)("inlineCode",{parentName:"a"},"x_google_ignoreList"))," field by default. The ",(0,i.mdx)("a",{parentName:"p",href:"/docs/configuration#isthirdpartymodule"},(0,i.mdx)("inlineCode",{parentName:"a"},"serializer.isThirdPartyModule"))," option can be used to control which modules are ignore-listed."))}f.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/6373d68f.d1846fd8.js b/assets/js/6373d68f.d1846fd8.js deleted file mode 100644 index d3d24d27f6..0000000000 --- a/assets/js/6373d68f.d1846fd8.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkmetro_website=self.webpackChunkmetro_website||[]).push([[874],{3905:(e,n,a)=>{a.r(n),a.d(n,{MDXContext:()=>d,MDXProvider:()=>c,mdx:()=>h,useMDXComponents:()=>s,withMDXComponents:()=>p});var t=a(67294);function o(e,n,a){return n in e?Object.defineProperty(e,n,{value:a,enumerable:!0,configurable:!0,writable:!0}):e[n]=a,e}function i(){return i=Object.assign||function(e){for(var n=1;n=0||(o[a]=e[a]);return o}(e,n);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(t=0;t=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(o[a]=e[a])}return o}var d=t.createContext({}),p=function(e){return function(n){var a=s(n.components);return t.createElement(e,i({},n,{components:a}))}},s=function(e){var n=t.useContext(d),a=n;return e&&(a="function"==typeof e?e(n):m(m({},n),e)),a},c=function(e){var n=s(e.components);return t.createElement(d.Provider,{value:n},e.children)},u="mdxType",f={inlineCode:"code",wrapper:function(e){var n=e.children;return t.createElement(t.Fragment,{},n)}},x=t.forwardRef((function(e,n){var a=e.components,o=e.mdxType,i=e.originalType,r=e.parentName,d=l(e,["components","mdxType","originalType","parentName"]),p=s(a),c=o,u=p["".concat(r,".").concat(c)]||p[c]||f[c]||i;return a?t.createElement(u,m(m({ref:n},d),{},{components:a})):t.createElement(u,m({ref:n},d))}));function h(e,n){var a=arguments,o=n&&n.mdxType;if("string"==typeof e||o){var i=a.length,r=new Array(i);r[0]=x;var m={};for(var l in n)hasOwnProperty.call(n,l)&&(m[l]=n[l]);m.originalType=e,m[u]="string"==typeof e?e:o,r[1]=m;for(var d=2;d{a.r(n),a.d(n,{assets:()=>p,contentTitle:()=>l,default:()=>f,frontMatter:()=>m,metadata:()=>d,toc:()=>s});var t=a(87462),o=a(63366),i=(a(67294),a(3905)),r=["components"],m={id:"source-map-format",title:"Source Map Format"},l=void 0,d={unversionedId:"source-map-format",id:"source-map-format",title:"Source Map Format",description:"Metro produces standard source maps along with its JavaScript bundle output. In addition to the standard information, Metro encodes extra information in vendor-specific fields within the source map. This page serves as a specification for this encoding.",source:"@site/../docs/SourceMapFormat.md",sourceDirName:".",slug:"/source-map-format",permalink:"/docs/source-map-format",draft:!1,editUrl:"https://github.com/facebook/metro/edit/main/docs/../docs/SourceMapFormat.md",tags:[],version:"current",lastUpdatedAt:1725545477,formattedLastUpdatedAt:"Sep 5, 2024",frontMatter:{id:"source-map-format",title:"Source Map Format"},sidebar:"docs",previous:{title:"Module Resolution",permalink:"/docs/resolution"}},p={},s=[{value:"x_facebook_sources",id:"x_facebook_sources",level:2},{value:"Metadata tuple",id:"metadata-tuple",level:3},{value:"Function map",id:"function-map",level:4},{value:"Function map mappings field encoding",id:"function-map-mappings-field-encoding",level:5},{value:"Example",id:"example",level:3},{value:"x_google_ignoreList",id:"x_google_ignorelist",level:2}],c={toc:s},u="wrapper";function f(e){var n=e.components,a=(0,o.Z)(e,r);return(0,i.mdx)(u,(0,t.Z)({},c,a,{components:n,mdxType:"MDXLayout"}),(0,i.mdx)("p",null,"Metro produces standard ",(0,i.mdx)("a",{parentName:"p",href:"https://sourcemaps.info/spec.html"},"source maps")," along with its JavaScript bundle output. In addition to the standard information, Metro encodes extra information in ",(0,i.mdx)("a",{parentName:"p",href:"https://sourcemaps.info/spec.html#h.ghqpj1ytqjbm"},"vendor-specific fields")," within the source map. This page serves as a specification for this encoding."),(0,i.mdx)("admonition",{type:"note"},(0,i.mdx)("p",{parentName:"admonition"},"The content on this page assumes familiarity with the ",(0,i.mdx)("a",{parentName:"p",href:"https://sourcemaps.info/spec.html"},"source map specification"),". Check out ",(0,i.mdx)("a",{parentName:"p",href:"https://www.bugsnag.com/blog/source-maps"},"Anatomy of source maps")," for a general introduction to source maps and how they work.")),(0,i.mdx)("h2",{id:"x_facebook_sources"},(0,i.mdx)("inlineCode",{parentName:"h2"},"x_facebook_sources")),(0,i.mdx)("p",null,"The ",(0,i.mdx)("inlineCode",{parentName:"p"},"x_facebook_sources")," field encodes metadata about source files in a source map. Each piece of metadata represents some attribute intrinsic to the source code of that particular file - for example, the result of running some analysis over the AST. This allows tools such as debuggers and JS engines to access such analyses efficiently, without needing to parse or even have access to the source code."),(0,i.mdx)("p",null,"In the same way that the standard ",(0,i.mdx)("a",{parentName:"p",href:"https://sourcemaps.info/spec.html#h.ghqpj1ytqjbm:~:text=sourceRoot%22%3A%20%22%22%2C-,%22sources%22%3A,-%5B%22foo.js%22%2C%20%22bar"},(0,i.mdx)("inlineCode",{parentName:"a"},"sources"))," field is a list of source URLs and ",(0,i.mdx)("a",{parentName:"p",href:"https://sourcemaps.info/spec.html#h.ghqpj1ytqjbm:~:text=js%22%2C%20%22bar.js%22%5D%2C-,%22sourcesContent%22%3A,-%5Bnull%2C%20null%5D%2C"},(0,i.mdx)("inlineCode",{parentName:"a"},"sourcesContent"))," is a list of (optional) source code strings, ",(0,i.mdx)("inlineCode",{parentName:"p"},"x_facebook_sources")," is a list of optional ",(0,i.mdx)("strong",{parentName:"p"},"metadata tuples"),". The ",(0,i.mdx)("em",{parentName:"p"},"i"),"-th metadata tuple (",(0,i.mdx)("inlineCode",{parentName:"p"},"x_facebook_sources[i]"),") corresponds to the source file whose URL is ",(0,i.mdx)("inlineCode",{parentName:"p"},"sources[i]"),"."),(0,i.mdx)("p",null,"In nested (indexed) source maps, ",(0,i.mdx)("inlineCode",{parentName:"p"},"x_facebook_sources")," may appear as part of any nested source map in ",(0,i.mdx)("a",{parentName:"p",href:"https://sourcemaps.info/spec.html#h.535es3xeprgt"},(0,i.mdx)("inlineCode",{parentName:"a"},"sections"))," that itself has a ",(0,i.mdx)("inlineCode",{parentName:"p"},"sources")," field."),(0,i.mdx)("p",null,"If present, ",(0,i.mdx)("inlineCode",{parentName:"p"},"x_facebook_sources")," may be a different length than ",(0,i.mdx)("inlineCode",{parentName:"p"},"sources")," (but usually shouldn't be). In particular, if it's shorter than ",(0,i.mdx)("inlineCode",{parentName:"p"},"sources"),", ",(0,i.mdx)("inlineCode",{parentName:"p"},"x_facebook_sources")," interpreted as if it were padded with ",(0,i.mdx)("inlineCode",{parentName:"p"},"null")," values to match the length of ",(0,i.mdx)("inlineCode",{parentName:"p"},"sources"),"."),(0,i.mdx)("admonition",{type:"info"},(0,i.mdx)("p",{parentName:"admonition"},"If you are writing a tool that processes source maps generated by Metro, and want to generate a new source map containing a valid ",(0,i.mdx)("inlineCode",{parentName:"p"},"x_facebook_sources")," field, you'll mainly need to ensure that ",(0,i.mdx)("inlineCode",{parentName:"p"},"x_facebook_sources[i]")," still corresponds to ",(0,i.mdx)("inlineCode",{parentName:"p"},"sources[i]")," in the output - even if your tool reorders, adds or deletes elements in ",(0,i.mdx)("inlineCode",{parentName:"p"},"sources"),". Notably, this can be done ",(0,i.mdx)("em",{parentName:"p"},"without")," parsing/decoding the metadata tuples: unless your tool actively needs to access the information within them, you can treat them as opaque blobs of JSON."),(0,i.mdx)("p",{parentName:"admonition"},"If a tool cannot guarantee that the ",(0,i.mdx)("inlineCode",{parentName:"p"},"sources")," and ",(0,i.mdx)("inlineCode",{parentName:"p"},"x_facebook_sources")," arrays will stay in sync, it should delete the ",(0,i.mdx)("inlineCode",{parentName:"p"},"x_facebook_sources")," field from its output.")),(0,i.mdx)("h3",{id:"metadata-tuple"},"Metadata tuple"),(0,i.mdx)("p",null,"Each metadata tuple is encoded as an array of zero or more entries. Each entry may be ",(0,i.mdx)("inlineCode",{parentName:"p"},"null")," to signify that it's missing. A run of trailing ",(0,i.mdx)("inlineCode",{parentName:"p"},"null"),"s may be truncated from the end of the tuple with no change in meaning. The metadata tuple itself may also be ",(0,i.mdx)("inlineCode",{parentName:"p"},"null")," to signify that the source file has no associated metadata."),(0,i.mdx)("p",null,"The indices in each metadata tuple are assigned as follows:"),(0,i.mdx)("ul",null,(0,i.mdx)("li",{parentName:"ul"},"Index 0: ",(0,i.mdx)("a",{parentName:"li",href:"#function-map"},"Function map")," or ",(0,i.mdx)("inlineCode",{parentName:"li"},"null"),".",(0,i.mdx)("ul",{parentName:"li"},(0,i.mdx)("li",{parentName:"ul"},"In Metro, this is the result of calling ",(0,i.mdx)("a",{parentName:"li",href:"https://github.com/facebook/metro/blob/main/packages/metro-source-map/src/generateFunctionMap.js"},(0,i.mdx)("inlineCode",{parentName:"a"},"generateFunctionMap"))," on the source AST."))),(0,i.mdx)("li",{parentName:"ul"},"Index 1-\u221e: Reserved for future use.")),(0,i.mdx)("h4",{id:"function-map"},"Function map"),(0,i.mdx)("p",null,"A function map is encoded as an object with the following two fields:"),(0,i.mdx)("ul",null,(0,i.mdx)("li",{parentName:"ul"},(0,i.mdx)("inlineCode",{parentName:"li"},"names"),": An array of strings."),(0,i.mdx)("li",{parentName:"ul"},(0,i.mdx)("inlineCode",{parentName:"li"},"mappings"),": A string following the ",(0,i.mdx)("a",{parentName:"li",href:"#function-map-mappings-field-encoding"},"encoding")," described below.")),(0,i.mdx)("p",null,"When decoded, ",(0,i.mdx)("inlineCode",{parentName:"p"},"mappings")," represents a list of 3-tuples of integers: ",(0,i.mdx)("inlineCode",{parentName:"p"},"(column, nameIndex, line), (column, nameIndex, line), ..."),". The list is ordered by ",(0,i.mdx)("inlineCode",{parentName:"p"},"line")," and then ",(0,i.mdx)("inlineCode",{parentName:"p"},"column"),"."),(0,i.mdx)("p",null,"The presence of a 3-tuple ",(0,i.mdx)("inlineCode",{parentName:"p"},"(column, nameIndex, line)")," means that the ",(0,i.mdx)("em",{parentName:"p"},"local function name")," in the code region beginning at ",(0,i.mdx)("inlineCode",{parentName:"p"},"line")," and ",(0,i.mdx)("inlineCode",{parentName:"p"},"column")," (in the source file described by the current metadata tuple) is ",(0,i.mdx)("inlineCode",{parentName:"p"},"names[nameIndex]"),"."),(0,i.mdx)("h5",{id:"function-map-mappings-field-encoding"},"Function map ",(0,i.mdx)("inlineCode",{parentName:"h5"},"mappings")," field encoding"),(0,i.mdx)("p",null,"The value of the ",(0,i.mdx)("inlineCode",{parentName:"p"},"mappings")," field is described by the ",(0,i.mdx)("em",{parentName:"p"},"Mappings")," production of the grammar detailed below."),(0,i.mdx)("ol",null,(0,i.mdx)("li",{parentName:"ol"},(0,i.mdx)("inlineCode",{parentName:"li"},'Mappings = [ ";" ] LineMappings { ";" { ";" } LineMappings }')),(0,i.mdx)("li",{parentName:"ol"},(0,i.mdx)("inlineCode",{parentName:"li"},'LineMappings = FirstColumnMapping "," ColumnMapping')),(0,i.mdx)("li",{parentName:"ol"},(0,i.mdx)("inlineCode",{parentName:"li"},"FirstColumnMapping = VLQ VLQ VLQ")),(0,i.mdx)("li",{parentName:"ol"},(0,i.mdx)("inlineCode",{parentName:"li"},"ColumnMapping = VLQ VLQ [ VLQ ]")),(0,i.mdx)("li",{parentName:"ol"},(0,i.mdx)("inlineCode",{parentName:"li"},"VLQ =")," ",(0,i.mdx)("em",{parentName:"li"},"A single Base64-encoded variable-length quantity, as defined in the ",(0,i.mdx)("a",{parentName:"em",href:"https://sourcemaps.info/spec.html#h.crcf4lqeivt8"},"source map specification")),".")),(0,i.mdx)("admonition",{type:"note"},(0,i.mdx)("p",{parentName:"admonition"},"The above grammar uses the following BNF-like notation:"),(0,i.mdx)("table",{parentName:"admonition"},(0,i.mdx)("thead",{parentName:"table"},(0,i.mdx)("tr",{parentName:"thead"},(0,i.mdx)("th",{parentName:"tr",align:null},"Notation"),(0,i.mdx)("th",{parentName:"tr",align:null},"Meaning"))),(0,i.mdx)("tbody",{parentName:"table"},(0,i.mdx)("tr",{parentName:"tbody"},(0,i.mdx)("td",{parentName:"tr",align:null},(0,i.mdx)("inlineCode",{parentName:"td"},"[ X ]")),(0,i.mdx)("td",{parentName:"tr",align:null},(0,i.mdx)("em",{parentName:"td"},"X")," appears zero or 1 times.")),(0,i.mdx)("tr",{parentName:"tbody"},(0,i.mdx)("td",{parentName:"tr",align:null},(0,i.mdx)("inlineCode",{parentName:"td"},"{ X }")),(0,i.mdx)("td",{parentName:"tr",align:null},(0,i.mdx)("em",{parentName:"td"},"X")," appears 0 or more times.")),(0,i.mdx)("tr",{parentName:"tbody"},(0,i.mdx)("td",{parentName:"tr",align:null},(0,i.mdx)("inlineCode",{parentName:"td"},'"foo"')),(0,i.mdx)("td",{parentName:"tr",align:null},"The literal characters ",(0,i.mdx)("inlineCode",{parentName:"td"},"foo"),"."))))),(0,i.mdx)("p",null,"The three VLQs in ",(0,i.mdx)("em",{parentName:"p"},"FirstColumnMapping")," or ",(0,i.mdx)("em",{parentName:"p"},"ColumnMapping")," represent, in this order:"),(0,i.mdx)("ol",null,(0,i.mdx)("li",{parentName:"ol"},(0,i.mdx)("strong",{parentName:"li"},"Column delta"),":",(0,i.mdx)("ul",{parentName:"li"},(0,i.mdx)("li",{parentName:"ul"},"In ",(0,i.mdx)("em",{parentName:"li"},"FirstColumnMapping"),": The column offset from the beginning of the line. (0 = first column)"),(0,i.mdx)("li",{parentName:"ul"},"In ",(0,i.mdx)("em",{parentName:"li"},"ColumnMapping"),": The column offset from the last-encountered ",(0,i.mdx)("em",{parentName:"li"},"FirstColumnMapping")," or ",(0,i.mdx)("em",{parentName:"li"},"ColumnMapping"),"."))),(0,i.mdx)("li",{parentName:"ol"},(0,i.mdx)("strong",{parentName:"li"},"Name delta"),": The name index offset from the last-encountered ",(0,i.mdx)("em",{parentName:"li"},"FirstColumnMapping")," or ",(0,i.mdx)("em",{parentName:"li"},"ColumnMapping"),". This is ",(0,i.mdx)("em",{parentName:"li"},"not")," reset between lines."),(0,i.mdx)("li",{parentName:"ol"},(0,i.mdx)("strong",{parentName:"li"},"Line delta"),": The line offset from the last-encountered ",(0,i.mdx)("em",{parentName:"li"},"FirstColumnMapping")," or ",(0,i.mdx)("em",{parentName:"li"},"ColumnMapping"),". This is ",(0,i.mdx)("em",{parentName:"li"},"not")," reset between lines.",(0,i.mdx)("ul",{parentName:"li"},(0,i.mdx)("li",{parentName:"ul"},"This MUST be 0 (Base64 VLQ: ",(0,i.mdx)("inlineCode",{parentName:"li"},"A"),") if it is part of a ",(0,i.mdx)("em",{parentName:"li"},"ColumnMapping"),"."),(0,i.mdx)("li",{parentName:"ul"},"Implementations SHOULD omit this field from the encoded form of ",(0,i.mdx)("em",{parentName:"li"},"ColumnMapping"),".")))),(0,i.mdx)("h3",{id:"example"},"Example"),(0,i.mdx)("p",null,"Given a single source file called ",(0,i.mdx)("inlineCode",{parentName:"p"},"file.js"),", a complete source map might look like this:"),(0,i.mdx)("admonition",{type:"note"},(0,i.mdx)("p",{parentName:"admonition"},"Comments are for illustrative purposes - the source map format does not allow comments.")),(0,i.mdx)("pre",null,(0,i.mdx)("code",{parentName:"pre",className:"language-json"},'{\n "version": 3,\n "sources": ["file.js"],\n "sourcesContent": ["function a(){} function b(){}"],\n "mappings": "AAAA", // NOTE: Simplified\n "x_facebook_sources": [\n // Metadata tuple for source #0 (file.js)\n [\n // Metadata item #0.0 = function map for source #0 (file.js)\n {\n // a from 1:0\n // from 1:14\n // b from 1:15\n // (See detailed decoding procedure below.)\n "mappings": "AAA,cC,CC",\n "names": [\n "a",\n "",\n "b",\n ]\n }\n ]\n ]\n}\n')),(0,i.mdx)("p",null,"The decoding procedure for the function map in the above example is illustrated by the following code:"),(0,i.mdx)("pre",null,(0,i.mdx)("code",{parentName:"pre",className:"language-js"},"const decoded = [];\nconst names = ['a', '', 'b']; // From the function map\n\nlet column = 0, nameIndex = 0, line = 1;\ncolumn += 0 /* A */; nameIndex += 0 /* A */; line += 0 /* A */;\ndecoded.push({column, name: names[nameIndex] /* 'a' */, line});\n\ncolumn += 14 /* c */; nameIndex += 1 /* C */; // no line delta\ndecoded.push({column, name: names[nameIndex] /* '' */, line});\n\ncolumn += 1 /* C */; nameIndex += 1 /* C */; // no line delta\ndecoded.push({column, name: names[nameIndex] /* 'b' */, line});\n\n/*\n decoded = [\n {column: 0, name: 'a', line: 1},\n {column: 14, name: '', line: 1},\n {column: 15, name: 'b', line: 1},\n ]\n*/\n")),(0,i.mdx)("h2",{id:"x_google_ignorelist"},(0,i.mdx)("inlineCode",{parentName:"h2"},"x_google_ignoreList")),(0,i.mdx)("p",null,"Metro's source maps include the ",(0,i.mdx)("a",{parentName:"p",href:"https://developer.chrome.com/articles/x-google-ignore-list/"},(0,i.mdx)("inlineCode",{parentName:"a"},"x_google_ignoreList"))," field by default. The ",(0,i.mdx)("a",{parentName:"p",href:"/docs/configuration#isthirdpartymodule"},(0,i.mdx)("inlineCode",{parentName:"a"},"serializer.isThirdPartyModule"))," option can be used to control which modules are ignore-listed."))}f.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/6681bed1.0222a461.js b/assets/js/6681bed1.0222a461.js new file mode 100644 index 0000000000..8f83e661da --- /dev/null +++ b/assets/js/6681bed1.0222a461.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkmetro_website=self.webpackChunkmetro_website||[]).push([[76],{3905:(e,t,n)=>{n.r(t),n.d(t,{MDXContext:()=>i,MDXProvider:()=>s,mdx:()=>g,useMDXComponents:()=>u,withMDXComponents:()=>p});var r=n(67294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function l(){return l=Object.assign||function(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var i=r.createContext({}),p=function(e){return function(t){var n=u(t.components);return r.createElement(e,l({},t,{components:n}))}},u=function(e){var t=r.useContext(i),n=t;return e&&(n="function"==typeof e?e(t):m(m({},t),e)),n},s=function(e){var t=u(e.components);return r.createElement(i.Provider,{value:t},e.children)},x="mdxType",c={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},N=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,l=e.originalType,d=e.parentName,i=o(e,["components","mdxType","originalType","parentName"]),p=u(n),s=a,x=p["".concat(d,".").concat(s)]||p[s]||c[s]||l;return n?r.createElement(x,m(m({ref:t},i),{},{components:n})):r.createElement(x,m({ref:t},i))}));function g(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var l=n.length,d=new Array(l);d[0]=N;var m={};for(var o in t)hasOwnProperty.call(t,o)&&(m[o]=t[o]);m.originalType=e,m[x]="string"==typeof e?e:a,d[1]=m;for(var i=2;i{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>o,default:()=>c,frontMatter:()=>m,metadata:()=>i,toc:()=>u});var r=n(87462),a=n(63366),l=(n(67294),n(3905)),d=["components"],m={id:"cli",title:"Metro CLI Options"},o=void 0,i={unversionedId:"cli",id:"cli",title:"Metro CLI Options",description:"The metro command line runner has a number of useful options. You can run `metro",source:"@site/../docs/CLI.md",sourceDirName:".",slug:"/cli",permalink:"/docs/cli",draft:!1,editUrl:"https://github.com/facebook/metro/edit/main/docs/../docs/CLI.md",tags:[],version:"current",lastUpdatedAt:1726493799,formattedLastUpdatedAt:"Sep 16, 2024",frontMatter:{id:"cli",title:"Metro CLI Options"},sidebar:"docs",previous:{title:"Configuring Metro",permalink:"/docs/configuration"},next:{title:"Package Exports Support (Experimental)",permalink:"/docs/package-exports"}},p={},u=[{value:"build <entry>",id:"build-entry",level:2},{value:"Options",id:"options",level:3},{value:"serve",id:"serve",level:2},{value:"get-dependencies <entryFile>",id:"get-dependencies-entryfile",level:2},{value:"Options",id:"options-1",level:3}],s={toc:u},x="wrapper";function c(e){var t=e.components,n=(0,a.Z)(e,d);return(0,l.mdx)(x,(0,r.Z)({},s,n,{components:t,mdxType:"MDXLayout"}),(0,l.mdx)("p",null,"The ",(0,l.mdx)("inlineCode",{parentName:"p"},"metro")," command line runner has a number of useful options. You can run ",(0,l.mdx)("inlineCode",{parentName:"p"},"metro\n--help")," to view all available options. Here is a brief overview:"),(0,l.mdx)("h2",{id:"build-entry"},(0,l.mdx)("inlineCode",{parentName:"h2"},"build ")),(0,l.mdx)("p",null,"Generates a JavaScript bundle containing the specified entrypoint and its descendants."),(0,l.mdx)("h3",{id:"options"},"Options"),(0,l.mdx)("table",null,(0,l.mdx)("thead",{parentName:"table"},(0,l.mdx)("tr",{parentName:"thead"},(0,l.mdx)("th",{parentName:"tr",align:null},"Option"),(0,l.mdx)("th",{parentName:"tr",align:null},"Alias"),(0,l.mdx)("th",{parentName:"tr",align:null},"Description"),(0,l.mdx)("th",{parentName:"tr",align:null},"Value"))),(0,l.mdx)("tbody",{parentName:"table"},(0,l.mdx)("tr",{parentName:"tbody"},(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"out")),(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"O")),(0,l.mdx)("td",{parentName:"tr",align:null},"File name where to store the output"),(0,l.mdx)("td",{parentName:"tr",align:null},"String")),(0,l.mdx)("tr",{parentName:"tbody"},(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"platform")),(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"p")),(0,l.mdx)("td",{parentName:"tr",align:null},"Which platform to bundle for"),(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"web"),", ",(0,l.mdx)("inlineCode",{parentName:"td"},"android"),", ",(0,l.mdx)("inlineCode",{parentName:"td"},"ios"))),(0,l.mdx)("tr",{parentName:"tbody"},(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"minify")),(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"z")),(0,l.mdx)("td",{parentName:"tr",align:null},"Whether Metro should minify the bundle"),(0,l.mdx)("td",{parentName:"tr",align:null},"Boolean")),(0,l.mdx)("tr",{parentName:"tbody"},(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"dev")),(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"g")),(0,l.mdx)("td",{parentName:"tr",align:null},"Create a development version of the build (",(0,l.mdx)("inlineCode",{parentName:"td"},"process.env.NODE_ENV = 'development'"),")"),(0,l.mdx)("td",{parentName:"tr",align:null},"Boolean")),(0,l.mdx)("tr",{parentName:"tbody"},(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"config")),(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"c")),(0,l.mdx)("td",{parentName:"tr",align:null},"Location of the ",(0,l.mdx)("inlineCode",{parentName:"td"},"metro.config.js")," to use"),(0,l.mdx)("td",{parentName:"tr",align:null},"String")),(0,l.mdx)("tr",{parentName:"tbody"},(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"max-workers")),(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"j")),(0,l.mdx)("td",{parentName:"tr",align:null},"The number of workers Metro should parallelize the transformer on"),(0,l.mdx)("td",{parentName:"tr",align:null},"Number")),(0,l.mdx)("tr",{parentName:"tbody"},(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"project-roots")),(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"P")),(0,l.mdx)("td",{parentName:"tr",align:null},"The root folder of your project"),(0,l.mdx)("td",{parentName:"tr",align:null},"Array")),(0,l.mdx)("tr",{parentName:"tbody"},(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"source-map")),(0,l.mdx)("td",{parentName:"tr",align:null}),(0,l.mdx)("td",{parentName:"tr",align:null},"Whether Metro should generate source maps"),(0,l.mdx)("td",{parentName:"tr",align:null},"Boolean")),(0,l.mdx)("tr",{parentName:"tbody"},(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"source-map-url")),(0,l.mdx)("td",{parentName:"tr",align:null}),(0,l.mdx)("td",{parentName:"tr",align:null},"URL where the source map can be found"),(0,l.mdx)("td",{parentName:"tr",align:null},"String")),(0,l.mdx)("tr",{parentName:"tbody"},(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"legacy-bundler")),(0,l.mdx)("td",{parentName:"tr",align:null}),(0,l.mdx)("td",{parentName:"tr",align:null},"Whether Metro should use the legacy bundler"),(0,l.mdx)("td",{parentName:"tr",align:null},"Boolean")),(0,l.mdx)("tr",{parentName:"tbody"},(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"resolver-option")),(0,l.mdx)("td",{parentName:"tr",align:null}),(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("a",{parentName:"td",href:"/docs/resolution#customresolveroptions-string-mixed"},"Custom resolver options")," of the form ",(0,l.mdx)("inlineCode",{parentName:"td"},"key=value")),(0,l.mdx)("td",{parentName:"tr",align:null},"Array")),(0,l.mdx)("tr",{parentName:"tbody"},(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"transform-option")),(0,l.mdx)("td",{parentName:"tr",align:null}),(0,l.mdx)("td",{parentName:"tr",align:null},"Custom transform options of the form ",(0,l.mdx)("inlineCode",{parentName:"td"},"key=value")),(0,l.mdx)("td",{parentName:"tr",align:null},"Array")))),(0,l.mdx)("h2",{id:"serve"},(0,l.mdx)("inlineCode",{parentName:"h2"},"serve")),(0,l.mdx)("p",null,"Starts Metro on the given port, building bundles on the fly."),(0,l.mdx)("h2",{id:"get-dependencies-entryfile"},(0,l.mdx)("inlineCode",{parentName:"h2"},"get-dependencies ")),(0,l.mdx)("p",null,"List all dependencies that will be bundled for a given entry point."),(0,l.mdx)("h3",{id:"options-1"},"Options"),(0,l.mdx)("table",null,(0,l.mdx)("thead",{parentName:"table"},(0,l.mdx)("tr",{parentName:"thead"},(0,l.mdx)("th",{parentName:"tr",align:null},"Option"),(0,l.mdx)("th",{parentName:"tr",align:null},"Description"))),(0,l.mdx)("tbody",{parentName:"table"},(0,l.mdx)("tr",{parentName:"tbody"},(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"entry-file")),(0,l.mdx)("td",{parentName:"tr",align:null},"Absolute path to the root JS file. This can also be given as the first positional arg.")),(0,l.mdx)("tr",{parentName:"tbody"},(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"output")),(0,l.mdx)("td",{parentName:"tr",align:null},"File name where to store the output, ex. /tmp/dependencies.txt")),(0,l.mdx)("tr",{parentName:"tbody"},(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"platform")),(0,l.mdx)("td",{parentName:"tr",align:null},"The platform extension used for selecting modules")),(0,l.mdx)("tr",{parentName:"tbody"},(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"transformer")),(0,l.mdx)("td",{parentName:"tr",align:null},"Specify a custom transformer to be used")),(0,l.mdx)("tr",{parentName:"tbody"},(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"max-workers")),(0,l.mdx)("td",{parentName:"tr",align:null},"Specifies the maximum number of workers the worker-pool will spawn for transforming files. This defaults to the number of the cores available on your machine.")),(0,l.mdx)("tr",{parentName:"tbody"},(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"dev")),(0,l.mdx)("td",{parentName:"tr",align:null},"If false, skip all dev-only code path")),(0,l.mdx)("tr",{parentName:"tbody"},(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"verbose")),(0,l.mdx)("td",{parentName:"tr",align:null},"Enables logging")))))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/6681bed1.bfd5a18a.js b/assets/js/6681bed1.bfd5a18a.js deleted file mode 100644 index 51f4c2de8e..0000000000 --- a/assets/js/6681bed1.bfd5a18a.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkmetro_website=self.webpackChunkmetro_website||[]).push([[76],{3905:(e,t,n)=>{n.r(t),n.d(t,{MDXContext:()=>i,MDXProvider:()=>s,mdx:()=>g,useMDXComponents:()=>u,withMDXComponents:()=>p});var r=n(67294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function l(){return l=Object.assign||function(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var i=r.createContext({}),p=function(e){return function(t){var n=u(t.components);return r.createElement(e,l({},t,{components:n}))}},u=function(e){var t=r.useContext(i),n=t;return e&&(n="function"==typeof e?e(t):m(m({},t),e)),n},s=function(e){var t=u(e.components);return r.createElement(i.Provider,{value:t},e.children)},x="mdxType",c={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},N=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,l=e.originalType,d=e.parentName,i=o(e,["components","mdxType","originalType","parentName"]),p=u(n),s=a,x=p["".concat(d,".").concat(s)]||p[s]||c[s]||l;return n?r.createElement(x,m(m({ref:t},i),{},{components:n})):r.createElement(x,m({ref:t},i))}));function g(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var l=n.length,d=new Array(l);d[0]=N;var m={};for(var o in t)hasOwnProperty.call(t,o)&&(m[o]=t[o]);m.originalType=e,m[x]="string"==typeof e?e:a,d[1]=m;for(var i=2;i{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>o,default:()=>c,frontMatter:()=>m,metadata:()=>i,toc:()=>u});var r=n(87462),a=n(63366),l=(n(67294),n(3905)),d=["components"],m={id:"cli",title:"Metro CLI Options"},o=void 0,i={unversionedId:"cli",id:"cli",title:"Metro CLI Options",description:"The metro command line runner has a number of useful options. You can run `metro",source:"@site/../docs/CLI.md",sourceDirName:".",slug:"/cli",permalink:"/docs/cli",draft:!1,editUrl:"https://github.com/facebook/metro/edit/main/docs/../docs/CLI.md",tags:[],version:"current",lastUpdatedAt:1725545477,formattedLastUpdatedAt:"Sep 5, 2024",frontMatter:{id:"cli",title:"Metro CLI Options"},sidebar:"docs",previous:{title:"Configuring Metro",permalink:"/docs/configuration"},next:{title:"Package Exports Support (Experimental)",permalink:"/docs/package-exports"}},p={},u=[{value:"build <entry>",id:"build-entry",level:2},{value:"Options",id:"options",level:3},{value:"serve",id:"serve",level:2},{value:"get-dependencies <entryFile>",id:"get-dependencies-entryfile",level:2},{value:"Options",id:"options-1",level:3}],s={toc:u},x="wrapper";function c(e){var t=e.components,n=(0,a.Z)(e,d);return(0,l.mdx)(x,(0,r.Z)({},s,n,{components:t,mdxType:"MDXLayout"}),(0,l.mdx)("p",null,"The ",(0,l.mdx)("inlineCode",{parentName:"p"},"metro")," command line runner has a number of useful options. You can run ",(0,l.mdx)("inlineCode",{parentName:"p"},"metro\n--help")," to view all available options. Here is a brief overview:"),(0,l.mdx)("h2",{id:"build-entry"},(0,l.mdx)("inlineCode",{parentName:"h2"},"build ")),(0,l.mdx)("p",null,"Generates a JavaScript bundle containing the specified entrypoint and its descendants."),(0,l.mdx)("h3",{id:"options"},"Options"),(0,l.mdx)("table",null,(0,l.mdx)("thead",{parentName:"table"},(0,l.mdx)("tr",{parentName:"thead"},(0,l.mdx)("th",{parentName:"tr",align:null},"Option"),(0,l.mdx)("th",{parentName:"tr",align:null},"Alias"),(0,l.mdx)("th",{parentName:"tr",align:null},"Description"),(0,l.mdx)("th",{parentName:"tr",align:null},"Value"))),(0,l.mdx)("tbody",{parentName:"table"},(0,l.mdx)("tr",{parentName:"tbody"},(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"out")),(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"O")),(0,l.mdx)("td",{parentName:"tr",align:null},"File name where to store the output"),(0,l.mdx)("td",{parentName:"tr",align:null},"String")),(0,l.mdx)("tr",{parentName:"tbody"},(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"platform")),(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"p")),(0,l.mdx)("td",{parentName:"tr",align:null},"Which platform to bundle for"),(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"web"),", ",(0,l.mdx)("inlineCode",{parentName:"td"},"android"),", ",(0,l.mdx)("inlineCode",{parentName:"td"},"ios"))),(0,l.mdx)("tr",{parentName:"tbody"},(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"minify")),(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"z")),(0,l.mdx)("td",{parentName:"tr",align:null},"Whether Metro should minify the bundle"),(0,l.mdx)("td",{parentName:"tr",align:null},"Boolean")),(0,l.mdx)("tr",{parentName:"tbody"},(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"dev")),(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"g")),(0,l.mdx)("td",{parentName:"tr",align:null},"Create a development version of the build (",(0,l.mdx)("inlineCode",{parentName:"td"},"process.env.NODE_ENV = 'development'"),")"),(0,l.mdx)("td",{parentName:"tr",align:null},"Boolean")),(0,l.mdx)("tr",{parentName:"tbody"},(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"config")),(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"c")),(0,l.mdx)("td",{parentName:"tr",align:null},"Location of the ",(0,l.mdx)("inlineCode",{parentName:"td"},"metro.config.js")," to use"),(0,l.mdx)("td",{parentName:"tr",align:null},"String")),(0,l.mdx)("tr",{parentName:"tbody"},(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"max-workers")),(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"j")),(0,l.mdx)("td",{parentName:"tr",align:null},"The number of workers Metro should parallelize the transformer on"),(0,l.mdx)("td",{parentName:"tr",align:null},"Number")),(0,l.mdx)("tr",{parentName:"tbody"},(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"project-roots")),(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"P")),(0,l.mdx)("td",{parentName:"tr",align:null},"The root folder of your project"),(0,l.mdx)("td",{parentName:"tr",align:null},"Array")),(0,l.mdx)("tr",{parentName:"tbody"},(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"source-map")),(0,l.mdx)("td",{parentName:"tr",align:null}),(0,l.mdx)("td",{parentName:"tr",align:null},"Whether Metro should generate source maps"),(0,l.mdx)("td",{parentName:"tr",align:null},"Boolean")),(0,l.mdx)("tr",{parentName:"tbody"},(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"source-map-url")),(0,l.mdx)("td",{parentName:"tr",align:null}),(0,l.mdx)("td",{parentName:"tr",align:null},"URL where the source map can be found"),(0,l.mdx)("td",{parentName:"tr",align:null},"String")),(0,l.mdx)("tr",{parentName:"tbody"},(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"legacy-bundler")),(0,l.mdx)("td",{parentName:"tr",align:null}),(0,l.mdx)("td",{parentName:"tr",align:null},"Whether Metro should use the legacy bundler"),(0,l.mdx)("td",{parentName:"tr",align:null},"Boolean")),(0,l.mdx)("tr",{parentName:"tbody"},(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"resolver-option")),(0,l.mdx)("td",{parentName:"tr",align:null}),(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("a",{parentName:"td",href:"/docs/resolution#customresolveroptions-string-mixed"},"Custom resolver options")," of the form ",(0,l.mdx)("inlineCode",{parentName:"td"},"key=value")),(0,l.mdx)("td",{parentName:"tr",align:null},"Array")),(0,l.mdx)("tr",{parentName:"tbody"},(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"transform-option")),(0,l.mdx)("td",{parentName:"tr",align:null}),(0,l.mdx)("td",{parentName:"tr",align:null},"Custom transform options of the form ",(0,l.mdx)("inlineCode",{parentName:"td"},"key=value")),(0,l.mdx)("td",{parentName:"tr",align:null},"Array")))),(0,l.mdx)("h2",{id:"serve"},(0,l.mdx)("inlineCode",{parentName:"h2"},"serve")),(0,l.mdx)("p",null,"Starts Metro on the given port, building bundles on the fly."),(0,l.mdx)("h2",{id:"get-dependencies-entryfile"},(0,l.mdx)("inlineCode",{parentName:"h2"},"get-dependencies ")),(0,l.mdx)("p",null,"List all dependencies that will be bundled for a given entry point."),(0,l.mdx)("h3",{id:"options-1"},"Options"),(0,l.mdx)("table",null,(0,l.mdx)("thead",{parentName:"table"},(0,l.mdx)("tr",{parentName:"thead"},(0,l.mdx)("th",{parentName:"tr",align:null},"Option"),(0,l.mdx)("th",{parentName:"tr",align:null},"Description"))),(0,l.mdx)("tbody",{parentName:"table"},(0,l.mdx)("tr",{parentName:"tbody"},(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"entry-file")),(0,l.mdx)("td",{parentName:"tr",align:null},"Absolute path to the root JS file. This can also be given as the first positional arg.")),(0,l.mdx)("tr",{parentName:"tbody"},(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"output")),(0,l.mdx)("td",{parentName:"tr",align:null},"File name where to store the output, ex. /tmp/dependencies.txt")),(0,l.mdx)("tr",{parentName:"tbody"},(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"platform")),(0,l.mdx)("td",{parentName:"tr",align:null},"The platform extension used for selecting modules")),(0,l.mdx)("tr",{parentName:"tbody"},(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"transformer")),(0,l.mdx)("td",{parentName:"tr",align:null},"Specify a custom transformer to be used")),(0,l.mdx)("tr",{parentName:"tbody"},(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"max-workers")),(0,l.mdx)("td",{parentName:"tr",align:null},"Specifies the maximum number of workers the worker-pool will spawn for transforming files. This defaults to the number of the cores available on your machine.")),(0,l.mdx)("tr",{parentName:"tbody"},(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"dev")),(0,l.mdx)("td",{parentName:"tr",align:null},"If false, skip all dev-only code path")),(0,l.mdx)("tr",{parentName:"tbody"},(0,l.mdx)("td",{parentName:"tr",align:null},(0,l.mdx)("inlineCode",{parentName:"td"},"verbose")),(0,l.mdx)("td",{parentName:"tr",align:null},"Enables logging")))))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/812be9b9.6b8aca99.js b/assets/js/812be9b9.6b8aca99.js deleted file mode 100644 index 4227b0873a..0000000000 --- a/assets/js/812be9b9.6b8aca99.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkmetro_website=self.webpackChunkmetro_website||[]).push([[599],{3905:(e,a,t)=>{t.r(a),t.d(a,{MDXContext:()=>d,MDXProvider:()=>x,mdx:()=>f,useMDXComponents:()=>p,withMDXComponents:()=>s});var n=t(67294);function r(e,a,t){return a in e?Object.defineProperty(e,a,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[a]=t,e}function o(){return o=Object.assign||function(e){for(var a=1;a=0||(r[t]=e[t]);return r}(e,a);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(r[t]=e[t])}return r}var d=n.createContext({}),s=function(e){return function(a){var t=p(a.components);return n.createElement(e,o({},a,{components:t}))}},p=function(e){var a=n.useContext(d),t=a;return e&&(t="function"==typeof e?e(a):i(i({},a),e)),t},x=function(e){var a=p(e.components);return n.createElement(d.Provider,{value:a},e.children)},u="mdxType",c={inlineCode:"code",wrapper:function(e){var a=e.children;return n.createElement(n.Fragment,{},a)}},h=n.forwardRef((function(e,a){var t=e.components,r=e.mdxType,o=e.originalType,l=e.parentName,d=m(e,["components","mdxType","originalType","parentName"]),s=p(t),x=r,u=s["".concat(l,".").concat(x)]||s[x]||c[x]||o;return t?n.createElement(u,i(i({ref:a},d),{},{components:t})):n.createElement(u,i({ref:a},d))}));function f(e,a){var t=arguments,r=a&&a.mdxType;if("string"==typeof e||r){var o=t.length,l=new Array(o);l[0]=h;var i={};for(var m in a)hasOwnProperty.call(a,m)&&(i[m]=a[m]);i.originalType=e,i[u]="string"==typeof e?e:r,l[1]=i;for(var d=2;d{t.r(a),t.d(a,{assets:()=>s,contentTitle:()=>m,default:()=>c,frontMatter:()=>i,metadata:()=>d,toc:()=>p});var n=t(87462),r=t(63366),o=(t(67294),t(3905)),l=["components"],i={id:"resolution",title:"Module Resolution"},m=void 0,d={unversionedId:"resolution",id:"resolution",title:"Module Resolution",description:"Module resolution is the process of translating module names to module paths at build time. For example, if your project contains the code:",source:"@site/../docs/Resolution.md",sourceDirName:".",slug:"/resolution",permalink:"/docs/resolution",draft:!1,editUrl:"https://github.com/facebook/metro/edit/main/docs/../docs/Resolution.md",tags:[],version:"current",lastUpdatedAt:1725545477,formattedLastUpdatedAt:"Sep 5, 2024",frontMatter:{id:"resolution",title:"Module Resolution"},sidebar:"docs",previous:{title:"Caching",permalink:"/docs/caching"},next:{title:"Source Map Format",permalink:"/docs/source-map-format"}},s={},p=[{value:"Resolution algorithm",id:"resolution-algorithm",level:2},{value:"Resolution types",id:"resolution-types",level:3},{value:"Source file",id:"source-file",level:4},{value:"Asset files",id:"asset-files",level:4},{value:"Empty module",id:"empty-module",level:4},{value:"Algorithm",id:"algorithm",level:3},{value:"RESOLVE",id:"resolve",level:4},{value:"RESOLVE_MODULE",id:"resolve_module",level:4},{value:"RESOLVE_PACKAGE",id:"resolve_package",level:4},{value:"RESOLVE_PACKAGE_EXPORTS",id:"resolve_package_exports",level:4},{value:"RESOLVE_FILE",id:"resolve_file",level:4},{value:"RESOLVE_ASSET",id:"resolve_asset",level:4},{value:"RESOLVE_HASTE",id:"resolve_haste",level:4},{value:"Resolution context",id:"resolution-context",level:3},{value:"assetExts: $ReadOnlySet<string>",id:"assetexts-readonlysetstring",level:4},{value:"dev: boolean",id:"dev-boolean",level:4},{value:"doesFileExist: string => boolean",id:"doesfileexist-string--boolean",level:4},{value:"nodeModulesPaths: $ReadOnlyArray<string>",id:"nodemodulespaths-readonlyarraystring",level:4},{value:"preferNativePlatform: boolean",id:"prefernativeplatform-boolean",level:4},{value:"redirectModulePath: string => string | false",id:"redirectmodulepath-string--string--false",level:4},{value:"resolveAsset: (dirPath: string, assetName: string, extension: string) => ?$ReadOnlyArray<string>",id:"resolveasset-dirpath-string-assetname-string-extension-string--readonlyarraystring",level:4},{value:"sourceExts: $ReadOnlyArray<string>",id:"sourceexts-readonlyarraystring",level:4},{value:"mainFields: $ReadOnlyArray<string>",id:"mainfields-readonlyarraystring",level:4},{value:"getPackage: string => PackageJson",id:"getpackage-string--packagejson",level:4},{value:'getPackageForModule: (absoluteModulePath: string) => ?PackageInfo
Deprecated
',id:"getpackageformodule-absolutemodulepath-string--packageinfo-deprecated",level:4},{value:"resolveHasteModule: string => ?string",id:"resolvehastemodule-string--string",level:4},{value:"resolveHastePackage: string => ?string",id:"resolvehastepackage-string--string",level:4},{value:"allowHaste: boolean",id:"allowhaste-boolean",level:4},{value:"disableHierarchicalLookup: boolean",id:"disablehierarchicallookup-boolean",level:4},{value:"extraNodeModules: ?{[string]: string}",id:"extranodemodules-string-string",level:4},{value:"originModulePath: string",id:"originmodulepath-string",level:4},{value:"customResolverOptions: {[string]: mixed}",id:"customresolveroptions-string-mixed",level:4},{value:"resolveRequest: CustomResolver",id:"resolverequest-customresolver",level:4},{value:"dependency: ?Dependency",id:"dependency-dependency",level:4},{value:"Caching",id:"caching",level:2}],x={toc:p},u="wrapper";function c(e){var a=e.components,t=(0,r.Z)(e,l);return(0,o.mdx)(u,(0,n.Z)({},x,t,{components:a,mdxType:"MDXLayout"}),(0,o.mdx)("p",null,"Module resolution is the process of translating module names to module paths at build time. For example, if your project contains the code:"),(0,o.mdx)("pre",null,(0,o.mdx)("code",{parentName:"pre",className:"language-js"},"// src/App.js\nimport {View} from 'react-native';\n// ...\n")),(0,o.mdx)("p",null,"Metro needs to know where in your project to load the ",(0,o.mdx)("inlineCode",{parentName:"p"},"react-native")," module from. This will typically resolve to something like ",(0,o.mdx)("inlineCode",{parentName:"p"},"node_modules/react-native/index.js"),"."),(0,o.mdx)("p",null,"Likewise, if your project contains the (similar) code:"),(0,o.mdx)("pre",null,(0,o.mdx)("code",{parentName:"pre",className:"language-js"},"// src/App.js\nimport Comp from './Component';\n// ...\n")),(0,o.mdx)("p",null,"Metro needs to understand that you are referring to, say, ",(0,o.mdx)("inlineCode",{parentName:"p"},"src/Component.js"),", and not another file named ",(0,o.mdx)("inlineCode",{parentName:"p"},"Component")," that may also exist elsewhere."),(0,o.mdx)("p",null,"Metro implements a version of ",(0,o.mdx)("a",{parentName:"p",href:"https://nodejs.org/api/modules.html#loading-from-node_modules-folders"},"Node's module resolution algorithm"),", augmented with additional Metro-specific features."),(0,o.mdx)("p",null,"These Metro-specific features include:"),(0,o.mdx)("ul",null,(0,o.mdx)("li",{parentName:"ul"},(0,o.mdx)("strong",{parentName:"li"},"Haste"),": An opt-in mechanism for importing modules by their globally-unique name anywhere in the project, e.g. ",(0,o.mdx)("inlineCode",{parentName:"li"},"import Foo from 'Foo'"),"."),(0,o.mdx)("li",{parentName:"ul"},(0,o.mdx)("strong",{parentName:"li"},"Platform extensions"),": Used by ",(0,o.mdx)("a",{parentName:"li",href:"https://reactnative.dev/docs/platform-specific-code#platform-specific-extensions"},"React Native")," to allow developers to write platform-specific versions of their JavaScript modules."),(0,o.mdx)("li",{parentName:"ul"},(0,o.mdx)("strong",{parentName:"li"},"Asset extensions and image resolutions"),": Used by ",(0,o.mdx)("a",{parentName:"li",href:"https://reactnative.dev/docs/images#static-image-resources"},"React Native")," to automatically select the best version of an image asset based on the device's screen density at runtime."),(0,o.mdx)("li",{parentName:"ul"},(0,o.mdx)("strong",{parentName:"li"},"Custom resolvers"),": Metro integrators can provide their own resolver implementations to override almost everything about how modules are resolved.")),(0,o.mdx)("h2",{id:"resolution-algorithm"},"Resolution algorithm"),(0,o.mdx)("p",null,"Given a ",(0,o.mdx)("a",{parentName:"p",href:"#resolution-context"},"resolution context")," ",(0,o.mdx)("em",{parentName:"p"},"context"),", a module name ",(0,o.mdx)("em",{parentName:"p"},"moduleName"),", and an optional platform identifier ",(0,o.mdx)("em",{parentName:"p"},"platform"),", Metro's resolver performs ",(0,o.mdx)("a",{parentName:"p",href:"#resolve"},(0,o.mdx)("strong",{parentName:"a"},"RESOLVE")),"(",(0,o.mdx)("em",{parentName:"p"},"context"),", ",(0,o.mdx)("em",{parentName:"p"},"moduleName"),", ",(0,o.mdx)("em",{parentName:"p"},"platform"),"), which either returns one of the ",(0,o.mdx)("a",{parentName:"p",href:"#resolution-types"},"resolution types"),", or throws an error."),(0,o.mdx)("h3",{id:"resolution-types"},"Resolution types"),(0,o.mdx)("h4",{id:"source-file"},"Source file"),(0,o.mdx)("p",null,"The request is resolved to some absolute path representing a physical file on disk."),(0,o.mdx)("h4",{id:"asset-files"},"Asset files"),(0,o.mdx)("p",null,"The request is resolved to one or more absolute paths representing physical files on disk."),(0,o.mdx)("h4",{id:"empty-module"},"Empty module"),(0,o.mdx)("p",null,"The request is resolved to a built-in empty module, namely the one specified in ",(0,o.mdx)("a",{parentName:"p",href:"/docs/configuration#emptymodulepath"},(0,o.mdx)("inlineCode",{parentName:"a"},"resolver.emptyModulePath")),"."),(0,o.mdx)("h3",{id:"algorithm"},"Algorithm"),(0,o.mdx)("admonition",{type:"note"},(0,o.mdx)("p",{parentName:"admonition"},"These are the rules that Metro's default resolver follows. Refer to ",(0,o.mdx)("a",{parentName:"p",href:"https://github.com/facebook/metro/blob/main/packages/metro-resolver/src/resolve.js"},(0,o.mdx)("inlineCode",{parentName:"a"},"metro-resolver"),"'s source code")," for more details.")),(0,o.mdx)("h4",{id:"resolve"},"RESOLVE"),(0,o.mdx)("p",null,"Parameters: (",(0,o.mdx)("em",{parentName:"p"},"context"),", ",(0,o.mdx)("em",{parentName:"p"},"moduleName"),", ",(0,o.mdx)("em",{parentName:"p"},"platform"),")"),(0,o.mdx)("ol",null,(0,o.mdx)("li",{parentName:"ol"},"If a ",(0,o.mdx)("a",{parentName:"li",href:"#resolverequest-customresolver"},"custom resolver")," is defined, then",(0,o.mdx)("ol",{parentName:"li"},(0,o.mdx)("li",{parentName:"ol"},"Return the result of the custom resolver."))),(0,o.mdx)("li",{parentName:"ol"},"Otherwise, attempt to resolve ",(0,o.mdx)("em",{parentName:"li"},"moduleName")," as a path",(0,o.mdx)("ol",{parentName:"li"},(0,o.mdx)("li",{parentName:"ol"},"Let ",(0,o.mdx)("em",{parentName:"li"},"absoluteModuleName")," be the result of prepending the current directory (i.e. parent of ",(0,o.mdx)("a",{parentName:"li",href:"#originmodulepath-string"},(0,o.mdx)("inlineCode",{parentName:"a"},"context.originModulePath")),") with ",(0,o.mdx)("em",{parentName:"li"},"moduleName"),"."),(0,o.mdx)("li",{parentName:"ol"},"Return the result of ",(0,o.mdx)("a",{parentName:"li",href:"#resolve_module"},(0,o.mdx)("strong",{parentName:"a"},"RESOLVE_MODULE")),"(",(0,o.mdx)("em",{parentName:"li"},"context"),", ",(0,o.mdx)("em",{parentName:"li"},"absoluteModuleName"),", ",(0,o.mdx)("em",{parentName:"li"},"platform"),"), or continue."))),(0,o.mdx)("li",{parentName:"ol"},"Apply ",(0,o.mdx)("a",{parentName:"li",href:"#redirectmodulepath-string--string--false"},"redirections")," to ",(0,o.mdx)("em",{parentName:"li"},"moduleName"),". If this results in an ",(0,o.mdx)("a",{parentName:"li",href:"#empty-module"},"empty module"),", then",(0,o.mdx)("ol",{parentName:"li"},(0,o.mdx)("li",{parentName:"ol"},"Return the empty module."))),(0,o.mdx)("li",{parentName:"ol"},"If ",(0,o.mdx)("a",{parentName:"li",href:"#allowhaste-boolean"},"Haste resolutions are allowed"),", then",(0,o.mdx)("ol",{parentName:"li"},(0,o.mdx)("li",{parentName:"ol"},"Get the result of ",(0,o.mdx)("a",{parentName:"li",href:"#resolve_haste"},(0,o.mdx)("strong",{parentName:"a"},"RESOLVE_HASTE")),"(",(0,o.mdx)("em",{parentName:"li"},"context"),", ",(0,o.mdx)("em",{parentName:"li"},"moduleName"),", ",(0,o.mdx)("em",{parentName:"li"},"platform"),")."),(0,o.mdx)("li",{parentName:"ol"},"If resolved as a Haste package path, then",(0,o.mdx)("ol",{parentName:"li"},(0,o.mdx)("li",{parentName:"ol"},"Perform the algorithm for resolving a path (step 2 above). Throw an error if this resolution fails.\nFor example, if the Haste package path for ",(0,o.mdx)("inlineCode",{parentName:"li"},"'a/b'")," is ",(0,o.mdx)("inlineCode",{parentName:"li"},"foo/package.json"),", perform step 2 as if ",(0,o.mdx)("em",{parentName:"li"},"moduleName")," was ",(0,o.mdx)("inlineCode",{parentName:"li"},"foo/c"),"."))))),(0,o.mdx)("li",{parentName:"ol"},"If ",(0,o.mdx)("a",{parentName:"li",href:"#disableHierarchicalLookup-boolean"},(0,o.mdx)("inlineCode",{parentName:"a"},"context.disableHierarchicalLookup"))," is not ",(0,o.mdx)("inlineCode",{parentName:"li"},"true"),", then",(0,o.mdx)("ol",{parentName:"li"},(0,o.mdx)("li",{parentName:"ol"},"Try resolving ",(0,o.mdx)("em",{parentName:"li"},"moduleName")," under ",(0,o.mdx)("inlineCode",{parentName:"li"},"node_modules")," from the current directory (i.e. parent of ",(0,o.mdx)("a",{parentName:"li",href:"#originmodulepath-string"},(0,o.mdx)("inlineCode",{parentName:"a"},"context.originModulePath")),") up to the root directory."),(0,o.mdx)("li",{parentName:"ol"},"Perform ",(0,o.mdx)("a",{parentName:"li",href:"#resolve_package"},(0,o.mdx)("strong",{parentName:"a"},"RESOLVE_PACKAGE")),"(",(0,o.mdx)("em",{parentName:"li"},"context"),", ",(0,o.mdx)("em",{parentName:"li"},"modulePath"),", ",(0,o.mdx)("em",{parentName:"li"},"platform"),") for each candidate path."))),(0,o.mdx)("li",{parentName:"ol"},"For each element ",(0,o.mdx)("em",{parentName:"li"},"nodeModulesPath")," of ",(0,o.mdx)("a",{parentName:"li",href:"#nodemodulespaths-readonlyarraystring"},(0,o.mdx)("inlineCode",{parentName:"a"},"context.nodeModulesPaths")),":",(0,o.mdx)("ol",{parentName:"li"},(0,o.mdx)("li",{parentName:"ol"},"Try resolving ",(0,o.mdx)("em",{parentName:"li"},"moduleName")," under ",(0,o.mdx)("em",{parentName:"li"},"nodeModulesPath")," as if the latter was another ",(0,o.mdx)("inlineCode",{parentName:"li"},"node_modules")," directory (similar to step 5 above)."),(0,o.mdx)("li",{parentName:"ol"},"Perform ",(0,o.mdx)("a",{parentName:"li",href:"#resolve_package"},(0,o.mdx)("strong",{parentName:"a"},"RESOLVE_PACKAGE")),"(",(0,o.mdx)("em",{parentName:"li"},"context"),", ",(0,o.mdx)("em",{parentName:"li"},"modulePath"),", ",(0,o.mdx)("em",{parentName:"li"},"platform"),") for each candidate path."))),(0,o.mdx)("li",{parentName:"ol"},"If ",(0,o.mdx)("a",{parentName:"li",href:"#extranodemodules-string-string"},(0,o.mdx)("inlineCode",{parentName:"a"},"context.extraNodeModules"))," is set:",(0,o.mdx)("ol",{parentName:"li"},(0,o.mdx)("li",{parentName:"ol"},"Split ",(0,o.mdx)("em",{parentName:"li"},"moduleName")," into a package name (including an optional ",(0,o.mdx)("a",{parentName:"li",href:"https://docs.npmjs.com/cli/v8/using-npm/scope"},"scope"),") and relative path."),(0,o.mdx)("li",{parentName:"ol"},"Look up the package name in ",(0,o.mdx)("a",{parentName:"li",href:"#extranodemodules-string-string"},(0,o.mdx)("inlineCode",{parentName:"a"},"context.extraNodeModules")),". If found, then",(0,o.mdx)("ol",{parentName:"li"},(0,o.mdx)("li",{parentName:"ol"},"Construct a path ",(0,o.mdx)("em",{parentName:"li"},"modulePath")," by replacing the package name part of ",(0,o.mdx)("em",{parentName:"li"},"moduleName")," with the value found in ",(0,o.mdx)("a",{parentName:"li",href:"#extranodemodules-string-string"},(0,o.mdx)("inlineCode",{parentName:"a"},"context.extraNodeModules"))),(0,o.mdx)("li",{parentName:"ol"},"Return the result of ",(0,o.mdx)("a",{parentName:"li",href:"#resolve_package"},(0,o.mdx)("strong",{parentName:"a"},"RESOLVE_PACKAGE")),"(",(0,o.mdx)("em",{parentName:"li"},"context"),", ",(0,o.mdx)("em",{parentName:"li"},"modulePath"),", ",(0,o.mdx)("em",{parentName:"li"},"platform"),")."))))),(0,o.mdx)("li",{parentName:"ol"},"If no valid resolution has been found, throw a resolution failure error.")),(0,o.mdx)("h4",{id:"resolve_module"},"RESOLVE_MODULE"),(0,o.mdx)("p",null,"Parameters: (",(0,o.mdx)("em",{parentName:"p"},"context"),", ",(0,o.mdx)("em",{parentName:"p"},"moduleName"),", ",(0,o.mdx)("em",{parentName:"p"},"platform"),")"),(0,o.mdx)("ol",null,(0,o.mdx)("li",{parentName:"ol"},"Let ",(0,o.mdx)("em",{parentName:"li"},"filePath")," be the result of applying ",(0,o.mdx)("a",{parentName:"li",href:"#redirectmodulepath-string--string--false"},"redirections")," to ",(0,o.mdx)("em",{parentName:"li"},"moduleName"),". This may locate a replacement subpath from a containing ",(0,o.mdx)("inlineCode",{parentName:"li"},"package.json")," file based on the ",(0,o.mdx)("a",{parentName:"li",href:"https://github.com/defunctzombie/package-browser-field-spec"},(0,o.mdx)("inlineCode",{parentName:"a"},"browser")," field spec"),"."),(0,o.mdx)("li",{parentName:"ol"},"Return the result of ",(0,o.mdx)("a",{parentName:"li",href:"#resolve_file"},(0,o.mdx)("strong",{parentName:"a"},"RESOLVE_FILE")),"(",(0,o.mdx)("em",{parentName:"li"},"context"),", ",(0,o.mdx)("em",{parentName:"li"},"filePath"),", ",(0,o.mdx)("em",{parentName:"li"},"platform"),"), or continue."),(0,o.mdx)("li",{parentName:"ol"},"Otherwise, let ",(0,o.mdx)("em",{parentName:"li"},"dirPath")," be the directory path of ",(0,o.mdx)("em",{parentName:"li"},"filePath"),"."),(0,o.mdx)("li",{parentName:"ol"},"If a file ",(0,o.mdx)("em",{parentName:"li"},"dirPath")," + ",(0,o.mdx)("inlineCode",{parentName:"li"},"'package.json'")," exists, resolve based on the ",(0,o.mdx)("a",{parentName:"li",href:"https://github.com/defunctzombie/package-browser-field-spec"},(0,o.mdx)("inlineCode",{parentName:"a"},"browser")," field spec"),":",(0,o.mdx)("ol",{parentName:"li"},(0,o.mdx)("li",{parentName:"ol"},"Let ",(0,o.mdx)("em",{parentName:"li"},"mainModulePath")," be the result of reading the package's entry path using ",(0,o.mdx)("a",{parentName:"li",href:"#mainfields-readonlyarraystring"},(0,o.mdx)("inlineCode",{parentName:"a"},"context.mainFields")),"."),(0,o.mdx)("li",{parentName:"ol"},"Return the result of ",(0,o.mdx)("a",{parentName:"li",href:"#resolve_file"},(0,o.mdx)("strong",{parentName:"a"},"RESOLVE_FILE")),"(",(0,o.mdx)("em",{parentName:"li"},"context"),", ",(0,o.mdx)("em",{parentName:"li"},"mainModulePath"),", ",(0,o.mdx)("em",{parentName:"li"},"platform"),"), or continue."),(0,o.mdx)("li",{parentName:"ol"},"Return the result of ",(0,o.mdx)("a",{parentName:"li",href:"#resolve_file"},(0,o.mdx)("strong",{parentName:"a"},"RESOLVE_FILE")),"(",(0,o.mdx)("em",{parentName:"li"},"context"),", ",(0,o.mdx)("em",{parentName:"li"},"mainModulePath")," + ",(0,o.mdx)("inlineCode",{parentName:"li"},"'/index'"),", ",(0,o.mdx)("em",{parentName:"li"},"platform"),")."),(0,o.mdx)("li",{parentName:"ol"},"Throw an error if no resolution could be found.")))),(0,o.mdx)("h4",{id:"resolve_package"},"RESOLVE_PACKAGE"),(0,o.mdx)("p",null,"Parameters: (",(0,o.mdx)("em",{parentName:"p"},"context"),", ",(0,o.mdx)("em",{parentName:"p"},"moduleName"),", ",(0,o.mdx)("em",{parentName:"p"},"platform"),")"),(0,o.mdx)("ol",null,(0,o.mdx)("li",{parentName:"ol"},"If ",(0,o.mdx)("inlineCode",{parentName:"li"},"context.enablePackageExports")," is enabled, and a containing ",(0,o.mdx)("inlineCode",{parentName:"li"},"package.json")," file contains the field ",(0,o.mdx)("inlineCode",{parentName:"li"},'"exports"'),", get result of ",(0,o.mdx)("a",{parentName:"li",href:"#resolve_package-exports"},(0,o.mdx)("strong",{parentName:"a"},"RESOLVE_PACKAGE_EXPORTS")),"(",(0,o.mdx)("em",{parentName:"li"},"context"),", ",(0,o.mdx)("em",{parentName:"li"},"packagePath"),", ",(0,o.mdx)("em",{parentName:"li"},"filePath"),", ",(0,o.mdx)("em",{parentName:"li"},"exportsField"),", ",(0,o.mdx)("em",{parentName:"li"},"platform"),").",(0,o.mdx)("ol",{parentName:"li"},(0,o.mdx)("li",{parentName:"ol"},"If resolved path exists, return result."),(0,o.mdx)("li",{parentName:"ol"},"Else, log either a package configuration or package encapsulation warning."))),(0,o.mdx)("li",{parentName:"ol"},"Return the result of ",(0,o.mdx)("a",{parentName:"li",href:"#resolve_module"},(0,o.mdx)("strong",{parentName:"a"},"RESOLVE_MODULE")),"(",(0,o.mdx)("em",{parentName:"li"},"context"),", ",(0,o.mdx)("em",{parentName:"li"},"filePath"),", ",(0,o.mdx)("em",{parentName:"li"},"platform"),").")),(0,o.mdx)("h4",{id:"resolve_package_exports"},"RESOLVE_PACKAGE_EXPORTS"),(0,o.mdx)("p",null,"Parameters: (",(0,o.mdx)("em",{parentName:"p"},"context"),", ",(0,o.mdx)("em",{parentName:"p"},"packagePath"),", ",(0,o.mdx)("em",{parentName:"p"},"filePath"),", ",(0,o.mdx)("em",{parentName:"p"},"exportsField"),", ",(0,o.mdx)("em",{parentName:"p"},"platform"),")"),(0,o.mdx)("blockquote",null,(0,o.mdx)("p",{parentName:"blockquote"},"Resolves a package subpath based on the ",(0,o.mdx)("a",{parentName:"p",href:"https://nodejs.org/docs/latest-v19.x/api/packages.html#package-entry-points"},"Package Entry Points spec")," (the ",(0,o.mdx)("inlineCode",{parentName:"p"},'"exports"')," field), when ",(0,o.mdx)("a",{parentName:"p",href:"./configuration#unstable_enablepackageexports-experimental"},(0,o.mdx)("inlineCode",{parentName:"a"},"resolver.unstable_enablePackageExports"))," is enabled.")),(0,o.mdx)("ol",null,(0,o.mdx)("li",{parentName:"ol"},"Let ",(0,o.mdx)("em",{parentName:"li"},"subpath")," be the relative path from ",(0,o.mdx)("em",{parentName:"li"},"packagePath")," to ",(0,o.mdx)("em",{parentName:"li"},"filePath"),", or ",(0,o.mdx)("inlineCode",{parentName:"li"},"'.'"),"."),(0,o.mdx)("li",{parentName:"ol"},"If ",(0,o.mdx)("em",{parentName:"li"},"exportsField")," contains an invalid configuration or values, raise an ",(0,o.mdx)("inlineCode",{parentName:"li"},"InvalidPackageConfigurationError"),"."),(0,o.mdx)("li",{parentName:"ol"},"If ",(0,o.mdx)("em",{parentName:"li"},"subpath")," is not defined by ",(0,o.mdx)("em",{parentName:"li"},"exportsField"),", raise a ",(0,o.mdx)("inlineCode",{parentName:"li"},"PackagePathNotExportedError"),"."),(0,o.mdx)("li",{parentName:"ol"},"Let ",(0,o.mdx)("em",{parentName:"li"},"target")," be the result of matching ",(0,o.mdx)("em",{parentName:"li"},"subpath")," in ",(0,o.mdx)("em",{parentName:"li"},"exportsField")," after applying any ",(0,o.mdx)("a",{parentName:"li",href:"https://nodejs.org/docs/latest-v19.x/api/packages.html#conditional-exports"},"conditional exports")," and/or substituting a ",(0,o.mdx)("a",{parentName:"li",href:"https://nodejs.org/docs/latest-v19.x/api/packages.html#subpath-patterns"},"subpath pattern match"),".",(0,o.mdx)("ol",{parentName:"li"},(0,o.mdx)("li",{parentName:"ol"},"Condition names will be asserted from the union of ",(0,o.mdx)("inlineCode",{parentName:"li"},"context.unstable_conditionNames")," and ",(0,o.mdx)("inlineCode",{parentName:"li"},"context.unstable_conditionNamesByPlatform")," for ",(0,o.mdx)("em",{parentName:"li"},"platform"),", in the order defined by ",(0,o.mdx)("em",{parentName:"li"},"exportsField"),"."))),(0,o.mdx)("li",{parentName:"ol"},"If ",(0,o.mdx)("em",{parentName:"li"},"target")," refers to an ",(0,o.mdx)("a",{parentName:"li",href:"#assetexts-readonlysetstring"},"asset"),", then",(0,o.mdx)("ol",{parentName:"li"},(0,o.mdx)("li",{parentName:"ol"},"Return the result of ",(0,o.mdx)("a",{parentName:"li",href:"#resolve_asset"},(0,o.mdx)("strong",{parentName:"a"},"RESOLVE_ASSET")),"(",(0,o.mdx)("em",{parentName:"li"},"context"),", ",(0,o.mdx)("em",{parentName:"li"},"target"),", ",(0,o.mdx)("em",{parentName:"li"},"platform"),")."))),(0,o.mdx)("li",{parentName:"ol"},"Return ",(0,o.mdx)("em",{parentName:"li"},"target")," as a ",(0,o.mdx)("a",{parentName:"li",href:"#source-file"},"source file resolution")," ",(0,o.mdx)("strong",{parentName:"li"},"without")," applying redirections or trying any platform or extension variants.")),(0,o.mdx)("h4",{id:"resolve_file"},"RESOLVE_FILE"),(0,o.mdx)("p",null,"Parameters: (",(0,o.mdx)("em",{parentName:"p"},"context"),", ",(0,o.mdx)("em",{parentName:"p"},"filePath"),", ",(0,o.mdx)("em",{parentName:"p"},"platform"),")"),(0,o.mdx)("ol",null,(0,o.mdx)("li",{parentName:"ol"},"If the path refers to an ",(0,o.mdx)("a",{parentName:"li",href:"#assetexts-readonlysetstring"},"asset"),", then",(0,o.mdx)("ol",{parentName:"li"},(0,o.mdx)("li",{parentName:"ol"},"Return the result of ",(0,o.mdx)("a",{parentName:"li",href:"#resolve_asset"},(0,o.mdx)("strong",{parentName:"a"},"RESOLVE_ASSET")),"(",(0,o.mdx)("em",{parentName:"li"},"context"),", ",(0,o.mdx)("em",{parentName:"li"},"filePath"),", ",(0,o.mdx)("em",{parentName:"li"},"platform"),")."))),(0,o.mdx)("li",{parentName:"ol"},"Otherwise, if the path ",(0,o.mdx)("a",{parentName:"li",href:"#doesfileexist-string--boolean"},"exists"),", then",(0,o.mdx)("ol",{parentName:"li"},(0,o.mdx)("li",{parentName:"ol"},"Try all platform and extension variants in sequence. Return a ",(0,o.mdx)("a",{parentName:"li",href:"#source-file"},"source file resolution")," for the first one that ",(0,o.mdx)("a",{parentName:"li",href:"#doesfileexist-string--boolean"},"exists")," after applying ",(0,o.mdx)("a",{parentName:"li",href:"#redirectmodulepath-string--string--false"},"redirections"),". For example, if ",(0,o.mdx)("em",{parentName:"li"},"platform")," is ",(0,o.mdx)("inlineCode",{parentName:"li"},"android")," and ",(0,o.mdx)("a",{parentName:"li",href:"#sourceexts-readonlyarraystring"},(0,o.mdx)("inlineCode",{parentName:"a"},"context.sourceExts"))," is ",(0,o.mdx)("inlineCode",{parentName:"li"},"['js', 'jsx']"),", try this sequence of potential file names:",(0,o.mdx)("ol",{parentName:"li"},(0,o.mdx)("li",{parentName:"ol"},(0,o.mdx)("em",{parentName:"li"},"moduleName")," + ",(0,o.mdx)("inlineCode",{parentName:"li"},"'.android.js'")),(0,o.mdx)("li",{parentName:"ol"},(0,o.mdx)("em",{parentName:"li"},"moduleName")," + ",(0,o.mdx)("inlineCode",{parentName:"li"},"'.native.js'")," (if ",(0,o.mdx)("a",{parentName:"li",href:"#prefernativeplatform-boolean"},(0,o.mdx)("inlineCode",{parentName:"a"},"context.preferNativePlatform"))," is ",(0,o.mdx)("inlineCode",{parentName:"li"},"true"),")"),(0,o.mdx)("li",{parentName:"ol"},(0,o.mdx)("em",{parentName:"li"},"moduleName")," + ",(0,o.mdx)("inlineCode",{parentName:"li"},"'.js'")),(0,o.mdx)("li",{parentName:"ol"},(0,o.mdx)("em",{parentName:"li"},"moduleName")," + ",(0,o.mdx)("inlineCode",{parentName:"li"},"'.android.jsx'")),(0,o.mdx)("li",{parentName:"ol"},(0,o.mdx)("em",{parentName:"li"},"moduleName")," + ",(0,o.mdx)("inlineCode",{parentName:"li"},"'.native.jsx'")," (if ",(0,o.mdx)("a",{parentName:"li",href:"#prefernativeplatform-boolean"},(0,o.mdx)("inlineCode",{parentName:"a"},"context.preferNativePlatform"))," is ",(0,o.mdx)("inlineCode",{parentName:"li"},"true"),")"),(0,o.mdx)("li",{parentName:"ol"},(0,o.mdx)("em",{parentName:"li"},"moduleName")," + ",(0,o.mdx)("inlineCode",{parentName:"li"},"'.jsx'"))))))),(0,o.mdx)("h4",{id:"resolve_asset"},"RESOLVE_ASSET"),(0,o.mdx)("p",null,"Parameters: (",(0,o.mdx)("em",{parentName:"p"},"context"),", ",(0,o.mdx)("em",{parentName:"p"},"filePath"),", ",(0,o.mdx)("em",{parentName:"p"},"platform"),")"),(0,o.mdx)("ol",null,(0,o.mdx)("li",{parentName:"ol"},"Use ",(0,o.mdx)("a",{parentName:"li",href:"#resolveasset-dirpath-string-assetname-string-extension-string--readonlyarraystring"},(0,o.mdx)("inlineCode",{parentName:"a"},"context.resolveAsset"))," to collect all asset variants."),(0,o.mdx)("li",{parentName:"ol"},"Return an ",(0,o.mdx)("a",{parentName:"li",href:"#asset-files"},"asset resolution")," containing the collected asset paths.")),(0,o.mdx)("h4",{id:"resolve_haste"},"RESOLVE_HASTE"),(0,o.mdx)("p",null,"Parameters: (",(0,o.mdx)("em",{parentName:"p"},"context"),", ",(0,o.mdx)("em",{parentName:"p"},"moduleName"),", ",(0,o.mdx)("em",{parentName:"p"},"platform"),")"),(0,o.mdx)("ol",null,(0,o.mdx)("li",{parentName:"ol"},"Try resolving ",(0,o.mdx)("em",{parentName:"li"},"moduleName")," as a ",(0,o.mdx)("a",{parentName:"li",href:"#resolvehastemodule-string--string"},"Haste module"),".\nIf found, then",(0,o.mdx)("ol",{parentName:"li"},(0,o.mdx)("li",{parentName:"ol"},"Return result as a ",(0,o.mdx)("a",{parentName:"li",href:"#source-file"},"source file resolution")," ",(0,o.mdx)("strong",{parentName:"li"},"without")," applying redirections or trying any platform or extension variants."))),(0,o.mdx)("li",{parentName:"ol"},"Try resolving ",(0,o.mdx)("em",{parentName:"li"},"moduleName")," as a ",(0,o.mdx)("a",{parentName:"li",href:"#resolvehastepackage-string--string"},"Haste (global) package"),", or a path ",(0,o.mdx)("em",{parentName:"li"},"relative")," to a Haste package.\nFor example, if ",(0,o.mdx)("em",{parentName:"li"},"moduleName")," is ",(0,o.mdx)("inlineCode",{parentName:"li"},"'a/b/c'"),", try the following potential Haste package names:",(0,o.mdx)("ol",{parentName:"li"},(0,o.mdx)("li",{parentName:"ol"},(0,o.mdx)("inlineCode",{parentName:"li"},"'a/b/c'"),", relative path ",(0,o.mdx)("inlineCode",{parentName:"li"},"''")),(0,o.mdx)("li",{parentName:"ol"},(0,o.mdx)("inlineCode",{parentName:"li"},"'a/b'"),", relative path ",(0,o.mdx)("inlineCode",{parentName:"li"},"'./c'")),(0,o.mdx)("li",{parentName:"ol"},(0,o.mdx)("inlineCode",{parentName:"li"},"'a'"),", with relative path ",(0,o.mdx)("inlineCode",{parentName:"li"},"'./b/c'"))))),(0,o.mdx)("h3",{id:"resolution-context"},"Resolution context"),(0,o.mdx)("h4",{id:"assetexts-readonlysetstring"},(0,o.mdx)("inlineCode",{parentName:"h4"},"assetExts: $ReadOnlySet")),(0,o.mdx)("p",null,"The set of file extensions used to identify asset files. Defaults to ",(0,o.mdx)("a",{parentName:"p",href:"/docs/configuration#assetexts"},(0,o.mdx)("inlineCode",{parentName:"a"},"resolver.assetExts")),"."),(0,o.mdx)("h4",{id:"dev-boolean"},(0,o.mdx)("inlineCode",{parentName:"h4"},"dev: boolean")),(0,o.mdx)("p",null,(0,o.mdx)("inlineCode",{parentName:"p"},"true")," if the resolution is for a development bundle, or ",(0,o.mdx)("inlineCode",{parentName:"p"},"false")," otherwise."),(0,o.mdx)("h4",{id:"doesfileexist-string--boolean"},(0,o.mdx)("inlineCode",{parentName:"h4"},"doesFileExist: string => boolean")),(0,o.mdx)("p",null,"Returns ",(0,o.mdx)("inlineCode",{parentName:"p"},"true")," if the file with the given path exists, or ",(0,o.mdx)("inlineCode",{parentName:"p"},"false")," otherwise."),(0,o.mdx)("p",null,"By default, Metro implements this by consulting an in-memory map of the filesystem that has been prepared in advance. This approach avoids disk I/O during module resolution."),(0,o.mdx)("h4",{id:"nodemodulespaths-readonlyarraystring"},(0,o.mdx)("inlineCode",{parentName:"h4"},"nodeModulesPaths: $ReadOnlyArray")),(0,o.mdx)("p",null,"A list of paths to check for modules after looking through all ",(0,o.mdx)("inlineCode",{parentName:"p"},"node_modules")," directories."),(0,o.mdx)("p",null,"By default this is set to ",(0,o.mdx)("a",{parentName:"p",href:"/docs/configuration#nodemodulespaths"},(0,o.mdx)("inlineCode",{parentName:"a"},"resolver.nodeModulesPaths"))),(0,o.mdx)("h4",{id:"prefernativeplatform-boolean"},(0,o.mdx)("inlineCode",{parentName:"h4"},"preferNativePlatform: boolean")),(0,o.mdx)("p",null,"If ",(0,o.mdx)("inlineCode",{parentName:"p"},"true"),", try ",(0,o.mdx)("inlineCode",{parentName:"p"},".native.${ext}")," before ",(0,o.mdx)("inlineCode",{parentName:"p"},".${ext}")," and after ",(0,o.mdx)("inlineCode",{parentName:"p"},".${platform}.${ext}")," during resolution. Metro sets this to ",(0,o.mdx)("inlineCode",{parentName:"p"},"true"),"."),(0,o.mdx)("h4",{id:"redirectmodulepath-string--string--false"},(0,o.mdx)("inlineCode",{parentName:"h4"},"redirectModulePath: string => string | false")),(0,o.mdx)("p",null,"Rewrites a module path, or returns ",(0,o.mdx)("inlineCode",{parentName:"p"},"false")," to redirect to the special ",(0,o.mdx)("a",{parentName:"p",href:"#empty-module"},"empty module"),". In the default resolver, the resolution algorithm terminates with an ",(0,o.mdx)("a",{parentName:"p",href:"#empty-module"},"empty module result")," if ",(0,o.mdx)("inlineCode",{parentName:"p"},"redirectModulePath")," returns ",(0,o.mdx)("inlineCode",{parentName:"p"},"false"),"."),(0,o.mdx)("p",null,"Metro uses this to implement the ",(0,o.mdx)("inlineCode",{parentName:"p"},"package.json")," ",(0,o.mdx)("a",{parentName:"p",href:"https://github.com/defunctzombie/package-browser-field-spec"},(0,o.mdx)("inlineCode",{parentName:"a"},"browser")," field spec"),", particularly the ability to ",(0,o.mdx)("a",{parentName:"p",href:"https://github.com/defunctzombie/package-browser-field-spec#replace-specific-files---advanced"},"replace")," and ",(0,o.mdx)("a",{parentName:"p",href:"https://github.com/defunctzombie/package-browser-field-spec#ignore-a-module"},"ignore")," specific files."),(0,o.mdx)("p",null,"The default implementation of this function respects ",(0,o.mdx)("a",{parentName:"p",href:"/docs/configuration#resolvermainfields"},(0,o.mdx)("inlineCode",{parentName:"a"},"resolver.resolverMainFields")),"."),(0,o.mdx)("h4",{id:"resolveasset-dirpath-string-assetname-string-extension-string--readonlyarraystring"},(0,o.mdx)("inlineCode",{parentName:"h4"},"resolveAsset: (dirPath: string, assetName: string, extension: string) => ?$ReadOnlyArray")),(0,o.mdx)("p",null,"Given a directory path, the base asset name and an extension, returns a list of all the asset file names that match the given base name in that directory, or ",(0,o.mdx)("inlineCode",{parentName:"p"},"null")," if no such files are found. The default implementation considers each of ",(0,o.mdx)("a",{parentName:"p",href:"/docs/configuration#assetresolutions"},(0,o.mdx)("inlineCode",{parentName:"a"},"resolver.assetResolutions"))," and uses the ",(0,o.mdx)("inlineCode",{parentName:"p"},"${assetName}@${resolution}${extension}")," format for asset variant file names."),(0,o.mdx)("p",null,"See also ",(0,o.mdx)("a",{parentName:"p",href:"https://reactnative.dev/docs/images#static-image-resources"},"Static Image Resources")," in the React Native docs."),(0,o.mdx)("h4",{id:"sourceexts-readonlyarraystring"},(0,o.mdx)("inlineCode",{parentName:"h4"},"sourceExts: $ReadOnlyArray")),(0,o.mdx)("p",null,"The list of file extensions to try, in order, when resolving a module path that does not exist on disk. Defaults to ",(0,o.mdx)("a",{parentName:"p",href:"/docs/configuration#sourceexts"},(0,o.mdx)("inlineCode",{parentName:"a"},"resolver.sourceExts")),"."),(0,o.mdx)("h4",{id:"mainfields-readonlyarraystring"},(0,o.mdx)("inlineCode",{parentName:"h4"},"mainFields: $ReadOnlyArray")),(0,o.mdx)("p",null,"The ordered list of fields in ",(0,o.mdx)("inlineCode",{parentName:"p"},"package.json")," that should be read to resolve a package's main entry point (and any subpath file replacements) per the ",(0,o.mdx)("a",{parentName:"p",href:"https://github.com/defunctzombie/package-browser-field-spec"},'"browser" field spec'),". Defaults to ",(0,o.mdx)("a",{parentName:"p",href:"/docs/configuration#resolvermainfields"},(0,o.mdx)("inlineCode",{parentName:"a"},"resolver.resolverMainFields")),"."),(0,o.mdx)("h4",{id:"getpackage-string--packagejson"},(0,o.mdx)("inlineCode",{parentName:"h4"},"getPackage: string => PackageJson")),(0,o.mdx)("p",null,"Given the path to a ",(0,o.mdx)("inlineCode",{parentName:"p"},"package.json")," file, returns the parsed file contents."),(0,o.mdx)("h4",{id:"getpackageformodule-absolutemodulepath-string--packageinfo-deprecated"},(0,o.mdx)("inlineCode",{parentName:"h4"},"getPackageForModule: (absoluteModulePath: string) => ?PackageInfo")," ",(0,o.mdx)("div",{class:"label deprecated"},"Deprecated")),(0,o.mdx)("p",null,"Given a candidate absolute module path that may exist under a package, locates and returns the closest package root (working upwards from the given path, stopping at the nearest ",(0,o.mdx)("inlineCode",{parentName:"p"},"node_modules"),"), parsed ",(0,o.mdx)("inlineCode",{parentName:"p"},"package.json")," contents, and the package-relative path of the given path."),(0,o.mdx)("h4",{id:"resolvehastemodule-string--string"},(0,o.mdx)("inlineCode",{parentName:"h4"},"resolveHasteModule: string => ?string")),(0,o.mdx)("p",null,"Resolves a Haste module name to an absolute path. Returns ",(0,o.mdx)("inlineCode",{parentName:"p"},"null")," if no such module exists."),(0,o.mdx)("p",null,"The default implementation of this function uses ",(0,o.mdx)("a",{parentName:"p",href:"https://www.npmjs.com/package/metro-file-map"},"metro-file-map"),"'s ",(0,o.mdx)("inlineCode",{parentName:"p"},"getModule")," method."),(0,o.mdx)("h4",{id:"resolvehastepackage-string--string"},(0,o.mdx)("inlineCode",{parentName:"h4"},"resolveHastePackage: string => ?string")),(0,o.mdx)("p",null,"Resolves a Haste (global) package name to an absolute ",(0,o.mdx)("inlineCode",{parentName:"p"},"package.json")," path. Returns ",(0,o.mdx)("inlineCode",{parentName:"p"},"null")," if no such package exists."),(0,o.mdx)("p",null,"The default implementation of this function uses ",(0,o.mdx)("a",{parentName:"p",href:"https://www.npmjs.com/package/metro-file-map"},"metro-file-map"),"'s ",(0,o.mdx)("inlineCode",{parentName:"p"},"getPackage")," method and can be turned on or off using ",(0,o.mdx)("a",{parentName:"p",href:"/docs/configuration#enableglobalpackages"},(0,o.mdx)("inlineCode",{parentName:"a"},"resolver.enableGlobalPackages")),"."),(0,o.mdx)("h4",{id:"allowhaste-boolean"},(0,o.mdx)("inlineCode",{parentName:"h4"},"allowHaste: boolean")),(0,o.mdx)("p",null,(0,o.mdx)("inlineCode",{parentName:"p"},"true")," if Haste resolutions are allowed in the current context, ",(0,o.mdx)("inlineCode",{parentName:"p"},"false")," otherwise."),(0,o.mdx)("h4",{id:"disablehierarchicallookup-boolean"},(0,o.mdx)("inlineCode",{parentName:"h4"},"disableHierarchicalLookup: boolean")),(0,o.mdx)("p",null,"If ",(0,o.mdx)("inlineCode",{parentName:"p"},"true"),", the resolver should not perform lookup in ",(0,o.mdx)("inlineCode",{parentName:"p"},"node_modules")," directories per the Node resolution algorithm. Defaults to ",(0,o.mdx)("a",{parentName:"p",href:"/docs/configuration#disablehierarchicallookup"},(0,o.mdx)("inlineCode",{parentName:"a"},"resolver.disableHierarchicalLookup")),"."),(0,o.mdx)("h4",{id:"extranodemodules-string-string"},(0,o.mdx)("inlineCode",{parentName:"h4"},"extraNodeModules: ?{[string]: string}")),(0,o.mdx)("p",null,"A mapping of package names to directories that is consulted after the standard lookup through ",(0,o.mdx)("inlineCode",{parentName:"p"},"node_modules")," as well as any ",(0,o.mdx)("a",{parentName:"p",href:"#nodemodulespaths-readonlyarraystring"},(0,o.mdx)("inlineCode",{parentName:"a"},"nodeModulesPaths")),"."),(0,o.mdx)("h4",{id:"originmodulepath-string"},(0,o.mdx)("inlineCode",{parentName:"h4"},"originModulePath: string")),(0,o.mdx)("p",null,"The path to the current module, e.g. the one containing the ",(0,o.mdx)("inlineCode",{parentName:"p"},"import")," we are currently resolving."),(0,o.mdx)("h4",{id:"customresolveroptions-string-mixed"},(0,o.mdx)("inlineCode",{parentName:"h4"},"customResolverOptions: {[string]: mixed}")),(0,o.mdx)("p",null,"Any custom options passed to the resolver. By default, Metro populates this based on URL parameters in the bundle request, e.g. ",(0,o.mdx)("inlineCode",{parentName:"p"},"http://localhost:8081/index.bundle?resolver.key=value")," becomes ",(0,o.mdx)("inlineCode",{parentName:"p"},"{key: 'value'}"),"."),(0,o.mdx)("h4",{id:"resolverequest-customresolver"},(0,o.mdx)("inlineCode",{parentName:"h4"},"resolveRequest: CustomResolver")),(0,o.mdx)("p",null,"A alternative resolver function to which the current request may be delegated. Defaults to ",(0,o.mdx)("a",{parentName:"p",href:"/docs/configuration#resolvereqeuest"},(0,o.mdx)("inlineCode",{parentName:"a"},"resolver.resolveRequest")),"."),(0,o.mdx)("p",null,"Metro expects ",(0,o.mdx)("inlineCode",{parentName:"p"},"resolveRequest")," to have the following signature:"),(0,o.mdx)("pre",null,(0,o.mdx)("code",{parentName:"pre",className:"language-flow"},"function resolveRequest(\n context: ResolutionContext,\n moduleName: string,\n platform: string | null,\n): Resolution {\n // ...\n}\n\ntype Resolution =\n | {type: 'empty'}\n | {type: 'sourceFile', filePath: string}\n | {type: 'assetFiles', filePaths: $ReadOnlyArray};\n")),(0,o.mdx)("p",null,"When calling the default resolver with a non-null ",(0,o.mdx)("inlineCode",{parentName:"p"},"resolveRequest")," function, it represents a custom resolver and will always be called, fully replacing the default resolution logic."),(0,o.mdx)("p",null,"Inside a custom resolver, ",(0,o.mdx)("inlineCode",{parentName:"p"},"resolveRequest")," is set to the default resolver function, for easy chaining and customization."),(0,o.mdx)("h4",{id:"dependency-dependency"},(0,o.mdx)("inlineCode",{parentName:"h4"},"dependency: ?Dependency")),(0,o.mdx)("p",null,"A dependency descriptor corresponding to the current resolution request. This is provided for diagnostic purposes ",(0,o.mdx)("em",{parentName:"p"},"only")," and may not be used for semantic purposes. See the ",(0,o.mdx)("a",{parentName:"p",href:"#caching"},"Caching")," section for more information."),(0,o.mdx)("pre",null,(0,o.mdx)("code",{parentName:"pre",className:"language-flow"},"type Dependency = {\n // The literal name provided to a require or import call. For example 'foo' in\n // case of `require('foo')`.\n name: string,\n\n data: {\n // A locally unique key for this dependency within the origin module.\n key: string,\n\n // Source locations from the Babel AST, relative to the origin module, where\n // this dependency was encountered. This may be an empty array.\n locs: $ReadOnlyArray,\n\n asyncType: 'async' | 'prefetch' | 'weak' | null,\n\n // Other properties are considered internal and may change in the future.\n ...\n },\n};\n")),(0,o.mdx)("h2",{id:"caching"},"Caching"),(0,o.mdx)("p",null,"Resolver results may be cached under the following conditions:"),(0,o.mdx)("ol",null,(0,o.mdx)("li",{parentName:"ol"},"For given origin module paths ",(0,o.mdx)("em",{parentName:"li"},"A")," and ",(0,o.mdx)("em",{parentName:"li"},"B")," and target module name ",(0,o.mdx)("em",{parentName:"li"},"M"),", the resolution for ",(0,o.mdx)("em",{parentName:"li"},"M")," may be reused if ",(0,o.mdx)("strong",{parentName:"li"},"all")," of the following conditions hold:",(0,o.mdx)("ol",{parentName:"li"},(0,o.mdx)("li",{parentName:"ol"},(0,o.mdx)("em",{parentName:"li"},"A")," and ",(0,o.mdx)("em",{parentName:"li"},"B")," are in the same directory."),(0,o.mdx)("li",{parentName:"ol"},"The contents of ",(0,o.mdx)("a",{parentName:"li",href:"#dev"},(0,o.mdx)("inlineCode",{parentName:"a"},"dev"))," and ",(0,o.mdx)("a",{parentName:"li",href:"#customresolveroptions-string-mixed"},(0,o.mdx)("inlineCode",{parentName:"a"},"customResolverOptions"))," are equivalent ( = serialize to JSON the same) in both calls to the resolver."))),(0,o.mdx)("li",{parentName:"ol"},"Any cache of resolutions must be invalidated if any file in the project has changed.")),(0,o.mdx)("p",null,"Custom resolvers must adhere to these assumptions, e.g. they may not return different resolutions for origin modules in the same directory under the same ",(0,o.mdx)("inlineCode",{parentName:"p"},"customResolverOptions"),"."))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/812be9b9.8f22fd96.js b/assets/js/812be9b9.8f22fd96.js new file mode 100644 index 0000000000..782aa2ead1 --- /dev/null +++ b/assets/js/812be9b9.8f22fd96.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkmetro_website=self.webpackChunkmetro_website||[]).push([[599],{3905:(e,a,t)=>{t.r(a),t.d(a,{MDXContext:()=>d,MDXProvider:()=>x,mdx:()=>f,useMDXComponents:()=>p,withMDXComponents:()=>s});var n=t(67294);function r(e,a,t){return a in e?Object.defineProperty(e,a,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[a]=t,e}function o(){return o=Object.assign||function(e){for(var a=1;a=0||(r[t]=e[t]);return r}(e,a);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(r[t]=e[t])}return r}var d=n.createContext({}),s=function(e){return function(a){var t=p(a.components);return n.createElement(e,o({},a,{components:t}))}},p=function(e){var a=n.useContext(d),t=a;return e&&(t="function"==typeof e?e(a):i(i({},a),e)),t},x=function(e){var a=p(e.components);return n.createElement(d.Provider,{value:a},e.children)},u="mdxType",c={inlineCode:"code",wrapper:function(e){var a=e.children;return n.createElement(n.Fragment,{},a)}},h=n.forwardRef((function(e,a){var t=e.components,r=e.mdxType,o=e.originalType,l=e.parentName,d=m(e,["components","mdxType","originalType","parentName"]),s=p(t),x=r,u=s["".concat(l,".").concat(x)]||s[x]||c[x]||o;return t?n.createElement(u,i(i({ref:a},d),{},{components:t})):n.createElement(u,i({ref:a},d))}));function f(e,a){var t=arguments,r=a&&a.mdxType;if("string"==typeof e||r){var o=t.length,l=new Array(o);l[0]=h;var i={};for(var m in a)hasOwnProperty.call(a,m)&&(i[m]=a[m]);i.originalType=e,i[u]="string"==typeof e?e:r,l[1]=i;for(var d=2;d{t.r(a),t.d(a,{assets:()=>s,contentTitle:()=>m,default:()=>c,frontMatter:()=>i,metadata:()=>d,toc:()=>p});var n=t(87462),r=t(63366),o=(t(67294),t(3905)),l=["components"],i={id:"resolution",title:"Module Resolution"},m=void 0,d={unversionedId:"resolution",id:"resolution",title:"Module Resolution",description:"Module resolution is the process of translating module names to module paths at build time. For example, if your project contains the code:",source:"@site/../docs/Resolution.md",sourceDirName:".",slug:"/resolution",permalink:"/docs/resolution",draft:!1,editUrl:"https://github.com/facebook/metro/edit/main/docs/../docs/Resolution.md",tags:[],version:"current",lastUpdatedAt:1726493799,formattedLastUpdatedAt:"Sep 16, 2024",frontMatter:{id:"resolution",title:"Module Resolution"},sidebar:"docs",previous:{title:"Caching",permalink:"/docs/caching"},next:{title:"Source Map Format",permalink:"/docs/source-map-format"}},s={},p=[{value:"Resolution algorithm",id:"resolution-algorithm",level:2},{value:"Resolution types",id:"resolution-types",level:3},{value:"Source file",id:"source-file",level:4},{value:"Asset files",id:"asset-files",level:4},{value:"Empty module",id:"empty-module",level:4},{value:"Algorithm",id:"algorithm",level:3},{value:"RESOLVE",id:"resolve",level:4},{value:"RESOLVE_MODULE",id:"resolve_module",level:4},{value:"RESOLVE_PACKAGE",id:"resolve_package",level:4},{value:"RESOLVE_PACKAGE_EXPORTS",id:"resolve_package_exports",level:4},{value:"RESOLVE_FILE",id:"resolve_file",level:4},{value:"RESOLVE_ASSET",id:"resolve_asset",level:4},{value:"RESOLVE_HASTE",id:"resolve_haste",level:4},{value:"Resolution context",id:"resolution-context",level:3},{value:"assetExts: $ReadOnlySet<string>",id:"assetexts-readonlysetstring",level:4},{value:"dev: boolean",id:"dev-boolean",level:4},{value:"doesFileExist: string => boolean",id:"doesfileexist-string--boolean",level:4},{value:"nodeModulesPaths: $ReadOnlyArray<string>",id:"nodemodulespaths-readonlyarraystring",level:4},{value:"preferNativePlatform: boolean",id:"prefernativeplatform-boolean",level:4},{value:"redirectModulePath: string => string | false",id:"redirectmodulepath-string--string--false",level:4},{value:"resolveAsset: (dirPath: string, assetName: string, extension: string) => ?$ReadOnlyArray<string>",id:"resolveasset-dirpath-string-assetname-string-extension-string--readonlyarraystring",level:4},{value:"sourceExts: $ReadOnlyArray<string>",id:"sourceexts-readonlyarraystring",level:4},{value:"mainFields: $ReadOnlyArray<string>",id:"mainfields-readonlyarraystring",level:4},{value:"getPackage: string => PackageJson",id:"getpackage-string--packagejson",level:4},{value:'getPackageForModule: (absoluteModulePath: string) => ?PackageInfo
Deprecated
',id:"getpackageformodule-absolutemodulepath-string--packageinfo-deprecated",level:4},{value:"resolveHasteModule: string => ?string",id:"resolvehastemodule-string--string",level:4},{value:"resolveHastePackage: string => ?string",id:"resolvehastepackage-string--string",level:4},{value:"allowHaste: boolean",id:"allowhaste-boolean",level:4},{value:"disableHierarchicalLookup: boolean",id:"disablehierarchicallookup-boolean",level:4},{value:"extraNodeModules: ?{[string]: string}",id:"extranodemodules-string-string",level:4},{value:"originModulePath: string",id:"originmodulepath-string",level:4},{value:"customResolverOptions: {[string]: mixed}",id:"customresolveroptions-string-mixed",level:4},{value:"resolveRequest: CustomResolver",id:"resolverequest-customresolver",level:4},{value:"dependency: ?Dependency",id:"dependency-dependency",level:4},{value:"Caching",id:"caching",level:2}],x={toc:p},u="wrapper";function c(e){var a=e.components,t=(0,r.Z)(e,l);return(0,o.mdx)(u,(0,n.Z)({},x,t,{components:a,mdxType:"MDXLayout"}),(0,o.mdx)("p",null,"Module resolution is the process of translating module names to module paths at build time. For example, if your project contains the code:"),(0,o.mdx)("pre",null,(0,o.mdx)("code",{parentName:"pre",className:"language-js"},"// src/App.js\nimport {View} from 'react-native';\n// ...\n")),(0,o.mdx)("p",null,"Metro needs to know where in your project to load the ",(0,o.mdx)("inlineCode",{parentName:"p"},"react-native")," module from. This will typically resolve to something like ",(0,o.mdx)("inlineCode",{parentName:"p"},"node_modules/react-native/index.js"),"."),(0,o.mdx)("p",null,"Likewise, if your project contains the (similar) code:"),(0,o.mdx)("pre",null,(0,o.mdx)("code",{parentName:"pre",className:"language-js"},"// src/App.js\nimport Comp from './Component';\n// ...\n")),(0,o.mdx)("p",null,"Metro needs to understand that you are referring to, say, ",(0,o.mdx)("inlineCode",{parentName:"p"},"src/Component.js"),", and not another file named ",(0,o.mdx)("inlineCode",{parentName:"p"},"Component")," that may also exist elsewhere."),(0,o.mdx)("p",null,"Metro implements a version of ",(0,o.mdx)("a",{parentName:"p",href:"https://nodejs.org/api/modules.html#loading-from-node_modules-folders"},"Node's module resolution algorithm"),", augmented with additional Metro-specific features."),(0,o.mdx)("p",null,"These Metro-specific features include:"),(0,o.mdx)("ul",null,(0,o.mdx)("li",{parentName:"ul"},(0,o.mdx)("strong",{parentName:"li"},"Haste"),": An opt-in mechanism for importing modules by their globally-unique name anywhere in the project, e.g. ",(0,o.mdx)("inlineCode",{parentName:"li"},"import Foo from 'Foo'"),"."),(0,o.mdx)("li",{parentName:"ul"},(0,o.mdx)("strong",{parentName:"li"},"Platform extensions"),": Used by ",(0,o.mdx)("a",{parentName:"li",href:"https://reactnative.dev/docs/platform-specific-code#platform-specific-extensions"},"React Native")," to allow developers to write platform-specific versions of their JavaScript modules."),(0,o.mdx)("li",{parentName:"ul"},(0,o.mdx)("strong",{parentName:"li"},"Asset extensions and image resolutions"),": Used by ",(0,o.mdx)("a",{parentName:"li",href:"https://reactnative.dev/docs/images#static-image-resources"},"React Native")," to automatically select the best version of an image asset based on the device's screen density at runtime."),(0,o.mdx)("li",{parentName:"ul"},(0,o.mdx)("strong",{parentName:"li"},"Custom resolvers"),": Metro integrators can provide their own resolver implementations to override almost everything about how modules are resolved.")),(0,o.mdx)("h2",{id:"resolution-algorithm"},"Resolution algorithm"),(0,o.mdx)("p",null,"Given a ",(0,o.mdx)("a",{parentName:"p",href:"#resolution-context"},"resolution context")," ",(0,o.mdx)("em",{parentName:"p"},"context"),", a module name ",(0,o.mdx)("em",{parentName:"p"},"moduleName"),", and an optional platform identifier ",(0,o.mdx)("em",{parentName:"p"},"platform"),", Metro's resolver performs ",(0,o.mdx)("a",{parentName:"p",href:"#resolve"},(0,o.mdx)("strong",{parentName:"a"},"RESOLVE")),"(",(0,o.mdx)("em",{parentName:"p"},"context"),", ",(0,o.mdx)("em",{parentName:"p"},"moduleName"),", ",(0,o.mdx)("em",{parentName:"p"},"platform"),"), which either returns one of the ",(0,o.mdx)("a",{parentName:"p",href:"#resolution-types"},"resolution types"),", or throws an error."),(0,o.mdx)("h3",{id:"resolution-types"},"Resolution types"),(0,o.mdx)("h4",{id:"source-file"},"Source file"),(0,o.mdx)("p",null,"The request is resolved to some absolute path representing a physical file on disk."),(0,o.mdx)("h4",{id:"asset-files"},"Asset files"),(0,o.mdx)("p",null,"The request is resolved to one or more absolute paths representing physical files on disk."),(0,o.mdx)("h4",{id:"empty-module"},"Empty module"),(0,o.mdx)("p",null,"The request is resolved to a built-in empty module, namely the one specified in ",(0,o.mdx)("a",{parentName:"p",href:"/docs/configuration#emptymodulepath"},(0,o.mdx)("inlineCode",{parentName:"a"},"resolver.emptyModulePath")),"."),(0,o.mdx)("h3",{id:"algorithm"},"Algorithm"),(0,o.mdx)("admonition",{type:"note"},(0,o.mdx)("p",{parentName:"admonition"},"These are the rules that Metro's default resolver follows. Refer to ",(0,o.mdx)("a",{parentName:"p",href:"https://github.com/facebook/metro/blob/main/packages/metro-resolver/src/resolve.js"},(0,o.mdx)("inlineCode",{parentName:"a"},"metro-resolver"),"'s source code")," for more details.")),(0,o.mdx)("h4",{id:"resolve"},"RESOLVE"),(0,o.mdx)("p",null,"Parameters: (",(0,o.mdx)("em",{parentName:"p"},"context"),", ",(0,o.mdx)("em",{parentName:"p"},"moduleName"),", ",(0,o.mdx)("em",{parentName:"p"},"platform"),")"),(0,o.mdx)("ol",null,(0,o.mdx)("li",{parentName:"ol"},"If a ",(0,o.mdx)("a",{parentName:"li",href:"#resolverequest-customresolver"},"custom resolver")," is defined, then",(0,o.mdx)("ol",{parentName:"li"},(0,o.mdx)("li",{parentName:"ol"},"Return the result of the custom resolver."))),(0,o.mdx)("li",{parentName:"ol"},"Otherwise, attempt to resolve ",(0,o.mdx)("em",{parentName:"li"},"moduleName")," as a path",(0,o.mdx)("ol",{parentName:"li"},(0,o.mdx)("li",{parentName:"ol"},"Let ",(0,o.mdx)("em",{parentName:"li"},"absoluteModuleName")," be the result of prepending the current directory (i.e. parent of ",(0,o.mdx)("a",{parentName:"li",href:"#originmodulepath-string"},(0,o.mdx)("inlineCode",{parentName:"a"},"context.originModulePath")),") with ",(0,o.mdx)("em",{parentName:"li"},"moduleName"),"."),(0,o.mdx)("li",{parentName:"ol"},"Return the result of ",(0,o.mdx)("a",{parentName:"li",href:"#resolve_module"},(0,o.mdx)("strong",{parentName:"a"},"RESOLVE_MODULE")),"(",(0,o.mdx)("em",{parentName:"li"},"context"),", ",(0,o.mdx)("em",{parentName:"li"},"absoluteModuleName"),", ",(0,o.mdx)("em",{parentName:"li"},"platform"),"), or continue."))),(0,o.mdx)("li",{parentName:"ol"},"Apply ",(0,o.mdx)("a",{parentName:"li",href:"#redirectmodulepath-string--string--false"},"redirections")," to ",(0,o.mdx)("em",{parentName:"li"},"moduleName"),". If this results in an ",(0,o.mdx)("a",{parentName:"li",href:"#empty-module"},"empty module"),", then",(0,o.mdx)("ol",{parentName:"li"},(0,o.mdx)("li",{parentName:"ol"},"Return the empty module."))),(0,o.mdx)("li",{parentName:"ol"},"If ",(0,o.mdx)("a",{parentName:"li",href:"#allowhaste-boolean"},"Haste resolutions are allowed"),", then",(0,o.mdx)("ol",{parentName:"li"},(0,o.mdx)("li",{parentName:"ol"},"Get the result of ",(0,o.mdx)("a",{parentName:"li",href:"#resolve_haste"},(0,o.mdx)("strong",{parentName:"a"},"RESOLVE_HASTE")),"(",(0,o.mdx)("em",{parentName:"li"},"context"),", ",(0,o.mdx)("em",{parentName:"li"},"moduleName"),", ",(0,o.mdx)("em",{parentName:"li"},"platform"),")."),(0,o.mdx)("li",{parentName:"ol"},"If resolved as a Haste package path, then",(0,o.mdx)("ol",{parentName:"li"},(0,o.mdx)("li",{parentName:"ol"},"Perform the algorithm for resolving a path (step 2 above). Throw an error if this resolution fails.\nFor example, if the Haste package path for ",(0,o.mdx)("inlineCode",{parentName:"li"},"'a/b'")," is ",(0,o.mdx)("inlineCode",{parentName:"li"},"foo/package.json"),", perform step 2 as if ",(0,o.mdx)("em",{parentName:"li"},"moduleName")," was ",(0,o.mdx)("inlineCode",{parentName:"li"},"foo/c"),"."))))),(0,o.mdx)("li",{parentName:"ol"},"If ",(0,o.mdx)("a",{parentName:"li",href:"#disableHierarchicalLookup-boolean"},(0,o.mdx)("inlineCode",{parentName:"a"},"context.disableHierarchicalLookup"))," is not ",(0,o.mdx)("inlineCode",{parentName:"li"},"true"),", then",(0,o.mdx)("ol",{parentName:"li"},(0,o.mdx)("li",{parentName:"ol"},"Try resolving ",(0,o.mdx)("em",{parentName:"li"},"moduleName")," under ",(0,o.mdx)("inlineCode",{parentName:"li"},"node_modules")," from the current directory (i.e. parent of ",(0,o.mdx)("a",{parentName:"li",href:"#originmodulepath-string"},(0,o.mdx)("inlineCode",{parentName:"a"},"context.originModulePath")),") up to the root directory."),(0,o.mdx)("li",{parentName:"ol"},"Perform ",(0,o.mdx)("a",{parentName:"li",href:"#resolve_package"},(0,o.mdx)("strong",{parentName:"a"},"RESOLVE_PACKAGE")),"(",(0,o.mdx)("em",{parentName:"li"},"context"),", ",(0,o.mdx)("em",{parentName:"li"},"modulePath"),", ",(0,o.mdx)("em",{parentName:"li"},"platform"),") for each candidate path."))),(0,o.mdx)("li",{parentName:"ol"},"For each element ",(0,o.mdx)("em",{parentName:"li"},"nodeModulesPath")," of ",(0,o.mdx)("a",{parentName:"li",href:"#nodemodulespaths-readonlyarraystring"},(0,o.mdx)("inlineCode",{parentName:"a"},"context.nodeModulesPaths")),":",(0,o.mdx)("ol",{parentName:"li"},(0,o.mdx)("li",{parentName:"ol"},"Try resolving ",(0,o.mdx)("em",{parentName:"li"},"moduleName")," under ",(0,o.mdx)("em",{parentName:"li"},"nodeModulesPath")," as if the latter was another ",(0,o.mdx)("inlineCode",{parentName:"li"},"node_modules")," directory (similar to step 5 above)."),(0,o.mdx)("li",{parentName:"ol"},"Perform ",(0,o.mdx)("a",{parentName:"li",href:"#resolve_package"},(0,o.mdx)("strong",{parentName:"a"},"RESOLVE_PACKAGE")),"(",(0,o.mdx)("em",{parentName:"li"},"context"),", ",(0,o.mdx)("em",{parentName:"li"},"modulePath"),", ",(0,o.mdx)("em",{parentName:"li"},"platform"),") for each candidate path."))),(0,o.mdx)("li",{parentName:"ol"},"If ",(0,o.mdx)("a",{parentName:"li",href:"#extranodemodules-string-string"},(0,o.mdx)("inlineCode",{parentName:"a"},"context.extraNodeModules"))," is set:",(0,o.mdx)("ol",{parentName:"li"},(0,o.mdx)("li",{parentName:"ol"},"Split ",(0,o.mdx)("em",{parentName:"li"},"moduleName")," into a package name (including an optional ",(0,o.mdx)("a",{parentName:"li",href:"https://docs.npmjs.com/cli/v8/using-npm/scope"},"scope"),") and relative path."),(0,o.mdx)("li",{parentName:"ol"},"Look up the package name in ",(0,o.mdx)("a",{parentName:"li",href:"#extranodemodules-string-string"},(0,o.mdx)("inlineCode",{parentName:"a"},"context.extraNodeModules")),". If found, then",(0,o.mdx)("ol",{parentName:"li"},(0,o.mdx)("li",{parentName:"ol"},"Construct a path ",(0,o.mdx)("em",{parentName:"li"},"modulePath")," by replacing the package name part of ",(0,o.mdx)("em",{parentName:"li"},"moduleName")," with the value found in ",(0,o.mdx)("a",{parentName:"li",href:"#extranodemodules-string-string"},(0,o.mdx)("inlineCode",{parentName:"a"},"context.extraNodeModules"))),(0,o.mdx)("li",{parentName:"ol"},"Return the result of ",(0,o.mdx)("a",{parentName:"li",href:"#resolve_package"},(0,o.mdx)("strong",{parentName:"a"},"RESOLVE_PACKAGE")),"(",(0,o.mdx)("em",{parentName:"li"},"context"),", ",(0,o.mdx)("em",{parentName:"li"},"modulePath"),", ",(0,o.mdx)("em",{parentName:"li"},"platform"),")."))))),(0,o.mdx)("li",{parentName:"ol"},"If no valid resolution has been found, throw a resolution failure error.")),(0,o.mdx)("h4",{id:"resolve_module"},"RESOLVE_MODULE"),(0,o.mdx)("p",null,"Parameters: (",(0,o.mdx)("em",{parentName:"p"},"context"),", ",(0,o.mdx)("em",{parentName:"p"},"moduleName"),", ",(0,o.mdx)("em",{parentName:"p"},"platform"),")"),(0,o.mdx)("ol",null,(0,o.mdx)("li",{parentName:"ol"},"Let ",(0,o.mdx)("em",{parentName:"li"},"filePath")," be the result of applying ",(0,o.mdx)("a",{parentName:"li",href:"#redirectmodulepath-string--string--false"},"redirections")," to ",(0,o.mdx)("em",{parentName:"li"},"moduleName"),". This may locate a replacement subpath from a containing ",(0,o.mdx)("inlineCode",{parentName:"li"},"package.json")," file based on the ",(0,o.mdx)("a",{parentName:"li",href:"https://github.com/defunctzombie/package-browser-field-spec"},(0,o.mdx)("inlineCode",{parentName:"a"},"browser")," field spec"),"."),(0,o.mdx)("li",{parentName:"ol"},"Return the result of ",(0,o.mdx)("a",{parentName:"li",href:"#resolve_file"},(0,o.mdx)("strong",{parentName:"a"},"RESOLVE_FILE")),"(",(0,o.mdx)("em",{parentName:"li"},"context"),", ",(0,o.mdx)("em",{parentName:"li"},"filePath"),", ",(0,o.mdx)("em",{parentName:"li"},"platform"),"), or continue."),(0,o.mdx)("li",{parentName:"ol"},"Otherwise, let ",(0,o.mdx)("em",{parentName:"li"},"dirPath")," be the directory path of ",(0,o.mdx)("em",{parentName:"li"},"filePath"),"."),(0,o.mdx)("li",{parentName:"ol"},"If a file ",(0,o.mdx)("em",{parentName:"li"},"dirPath")," + ",(0,o.mdx)("inlineCode",{parentName:"li"},"'package.json'")," exists, resolve based on the ",(0,o.mdx)("a",{parentName:"li",href:"https://github.com/defunctzombie/package-browser-field-spec"},(0,o.mdx)("inlineCode",{parentName:"a"},"browser")," field spec"),":",(0,o.mdx)("ol",{parentName:"li"},(0,o.mdx)("li",{parentName:"ol"},"Let ",(0,o.mdx)("em",{parentName:"li"},"mainModulePath")," be the result of reading the package's entry path using ",(0,o.mdx)("a",{parentName:"li",href:"#mainfields-readonlyarraystring"},(0,o.mdx)("inlineCode",{parentName:"a"},"context.mainFields")),"."),(0,o.mdx)("li",{parentName:"ol"},"Return the result of ",(0,o.mdx)("a",{parentName:"li",href:"#resolve_file"},(0,o.mdx)("strong",{parentName:"a"},"RESOLVE_FILE")),"(",(0,o.mdx)("em",{parentName:"li"},"context"),", ",(0,o.mdx)("em",{parentName:"li"},"mainModulePath"),", ",(0,o.mdx)("em",{parentName:"li"},"platform"),"), or continue."),(0,o.mdx)("li",{parentName:"ol"},"Return the result of ",(0,o.mdx)("a",{parentName:"li",href:"#resolve_file"},(0,o.mdx)("strong",{parentName:"a"},"RESOLVE_FILE")),"(",(0,o.mdx)("em",{parentName:"li"},"context"),", ",(0,o.mdx)("em",{parentName:"li"},"mainModulePath")," + ",(0,o.mdx)("inlineCode",{parentName:"li"},"'/index'"),", ",(0,o.mdx)("em",{parentName:"li"},"platform"),")."),(0,o.mdx)("li",{parentName:"ol"},"Throw an error if no resolution could be found.")))),(0,o.mdx)("h4",{id:"resolve_package"},"RESOLVE_PACKAGE"),(0,o.mdx)("p",null,"Parameters: (",(0,o.mdx)("em",{parentName:"p"},"context"),", ",(0,o.mdx)("em",{parentName:"p"},"moduleName"),", ",(0,o.mdx)("em",{parentName:"p"},"platform"),")"),(0,o.mdx)("ol",null,(0,o.mdx)("li",{parentName:"ol"},"If ",(0,o.mdx)("inlineCode",{parentName:"li"},"context.enablePackageExports")," is enabled, and a containing ",(0,o.mdx)("inlineCode",{parentName:"li"},"package.json")," file contains the field ",(0,o.mdx)("inlineCode",{parentName:"li"},'"exports"'),", get result of ",(0,o.mdx)("a",{parentName:"li",href:"#resolve_package-exports"},(0,o.mdx)("strong",{parentName:"a"},"RESOLVE_PACKAGE_EXPORTS")),"(",(0,o.mdx)("em",{parentName:"li"},"context"),", ",(0,o.mdx)("em",{parentName:"li"},"packagePath"),", ",(0,o.mdx)("em",{parentName:"li"},"filePath"),", ",(0,o.mdx)("em",{parentName:"li"},"exportsField"),", ",(0,o.mdx)("em",{parentName:"li"},"platform"),").",(0,o.mdx)("ol",{parentName:"li"},(0,o.mdx)("li",{parentName:"ol"},"If resolved path exists, return result."),(0,o.mdx)("li",{parentName:"ol"},"Else, log either a package configuration or package encapsulation warning."))),(0,o.mdx)("li",{parentName:"ol"},"Return the result of ",(0,o.mdx)("a",{parentName:"li",href:"#resolve_module"},(0,o.mdx)("strong",{parentName:"a"},"RESOLVE_MODULE")),"(",(0,o.mdx)("em",{parentName:"li"},"context"),", ",(0,o.mdx)("em",{parentName:"li"},"filePath"),", ",(0,o.mdx)("em",{parentName:"li"},"platform"),").")),(0,o.mdx)("h4",{id:"resolve_package_exports"},"RESOLVE_PACKAGE_EXPORTS"),(0,o.mdx)("p",null,"Parameters: (",(0,o.mdx)("em",{parentName:"p"},"context"),", ",(0,o.mdx)("em",{parentName:"p"},"packagePath"),", ",(0,o.mdx)("em",{parentName:"p"},"filePath"),", ",(0,o.mdx)("em",{parentName:"p"},"exportsField"),", ",(0,o.mdx)("em",{parentName:"p"},"platform"),")"),(0,o.mdx)("blockquote",null,(0,o.mdx)("p",{parentName:"blockquote"},"Resolves a package subpath based on the ",(0,o.mdx)("a",{parentName:"p",href:"https://nodejs.org/docs/latest-v19.x/api/packages.html#package-entry-points"},"Package Entry Points spec")," (the ",(0,o.mdx)("inlineCode",{parentName:"p"},'"exports"')," field), when ",(0,o.mdx)("a",{parentName:"p",href:"./configuration#unstable_enablepackageexports-experimental"},(0,o.mdx)("inlineCode",{parentName:"a"},"resolver.unstable_enablePackageExports"))," is enabled.")),(0,o.mdx)("ol",null,(0,o.mdx)("li",{parentName:"ol"},"Let ",(0,o.mdx)("em",{parentName:"li"},"subpath")," be the relative path from ",(0,o.mdx)("em",{parentName:"li"},"packagePath")," to ",(0,o.mdx)("em",{parentName:"li"},"filePath"),", or ",(0,o.mdx)("inlineCode",{parentName:"li"},"'.'"),"."),(0,o.mdx)("li",{parentName:"ol"},"If ",(0,o.mdx)("em",{parentName:"li"},"exportsField")," contains an invalid configuration or values, raise an ",(0,o.mdx)("inlineCode",{parentName:"li"},"InvalidPackageConfigurationError"),"."),(0,o.mdx)("li",{parentName:"ol"},"If ",(0,o.mdx)("em",{parentName:"li"},"subpath")," is not defined by ",(0,o.mdx)("em",{parentName:"li"},"exportsField"),", raise a ",(0,o.mdx)("inlineCode",{parentName:"li"},"PackagePathNotExportedError"),"."),(0,o.mdx)("li",{parentName:"ol"},"Let ",(0,o.mdx)("em",{parentName:"li"},"target")," be the result of matching ",(0,o.mdx)("em",{parentName:"li"},"subpath")," in ",(0,o.mdx)("em",{parentName:"li"},"exportsField")," after applying any ",(0,o.mdx)("a",{parentName:"li",href:"https://nodejs.org/docs/latest-v19.x/api/packages.html#conditional-exports"},"conditional exports")," and/or substituting a ",(0,o.mdx)("a",{parentName:"li",href:"https://nodejs.org/docs/latest-v19.x/api/packages.html#subpath-patterns"},"subpath pattern match"),".",(0,o.mdx)("ol",{parentName:"li"},(0,o.mdx)("li",{parentName:"ol"},"Condition names will be asserted from the union of ",(0,o.mdx)("inlineCode",{parentName:"li"},"context.unstable_conditionNames")," and ",(0,o.mdx)("inlineCode",{parentName:"li"},"context.unstable_conditionNamesByPlatform")," for ",(0,o.mdx)("em",{parentName:"li"},"platform"),", in the order defined by ",(0,o.mdx)("em",{parentName:"li"},"exportsField"),"."))),(0,o.mdx)("li",{parentName:"ol"},"If ",(0,o.mdx)("em",{parentName:"li"},"target")," refers to an ",(0,o.mdx)("a",{parentName:"li",href:"#assetexts-readonlysetstring"},"asset"),", then",(0,o.mdx)("ol",{parentName:"li"},(0,o.mdx)("li",{parentName:"ol"},"Return the result of ",(0,o.mdx)("a",{parentName:"li",href:"#resolve_asset"},(0,o.mdx)("strong",{parentName:"a"},"RESOLVE_ASSET")),"(",(0,o.mdx)("em",{parentName:"li"},"context"),", ",(0,o.mdx)("em",{parentName:"li"},"target"),", ",(0,o.mdx)("em",{parentName:"li"},"platform"),")."))),(0,o.mdx)("li",{parentName:"ol"},"Return ",(0,o.mdx)("em",{parentName:"li"},"target")," as a ",(0,o.mdx)("a",{parentName:"li",href:"#source-file"},"source file resolution")," ",(0,o.mdx)("strong",{parentName:"li"},"without")," applying redirections or trying any platform or extension variants.")),(0,o.mdx)("h4",{id:"resolve_file"},"RESOLVE_FILE"),(0,o.mdx)("p",null,"Parameters: (",(0,o.mdx)("em",{parentName:"p"},"context"),", ",(0,o.mdx)("em",{parentName:"p"},"filePath"),", ",(0,o.mdx)("em",{parentName:"p"},"platform"),")"),(0,o.mdx)("ol",null,(0,o.mdx)("li",{parentName:"ol"},"If the path refers to an ",(0,o.mdx)("a",{parentName:"li",href:"#assetexts-readonlysetstring"},"asset"),", then",(0,o.mdx)("ol",{parentName:"li"},(0,o.mdx)("li",{parentName:"ol"},"Return the result of ",(0,o.mdx)("a",{parentName:"li",href:"#resolve_asset"},(0,o.mdx)("strong",{parentName:"a"},"RESOLVE_ASSET")),"(",(0,o.mdx)("em",{parentName:"li"},"context"),", ",(0,o.mdx)("em",{parentName:"li"},"filePath"),", ",(0,o.mdx)("em",{parentName:"li"},"platform"),")."))),(0,o.mdx)("li",{parentName:"ol"},"Otherwise, if the path ",(0,o.mdx)("a",{parentName:"li",href:"#doesfileexist-string--boolean"},"exists"),", then",(0,o.mdx)("ol",{parentName:"li"},(0,o.mdx)("li",{parentName:"ol"},"Try all platform and extension variants in sequence. Return a ",(0,o.mdx)("a",{parentName:"li",href:"#source-file"},"source file resolution")," for the first one that ",(0,o.mdx)("a",{parentName:"li",href:"#doesfileexist-string--boolean"},"exists")," after applying ",(0,o.mdx)("a",{parentName:"li",href:"#redirectmodulepath-string--string--false"},"redirections"),". For example, if ",(0,o.mdx)("em",{parentName:"li"},"platform")," is ",(0,o.mdx)("inlineCode",{parentName:"li"},"android")," and ",(0,o.mdx)("a",{parentName:"li",href:"#sourceexts-readonlyarraystring"},(0,o.mdx)("inlineCode",{parentName:"a"},"context.sourceExts"))," is ",(0,o.mdx)("inlineCode",{parentName:"li"},"['js', 'jsx']"),", try this sequence of potential file names:",(0,o.mdx)("ol",{parentName:"li"},(0,o.mdx)("li",{parentName:"ol"},(0,o.mdx)("em",{parentName:"li"},"moduleName")," + ",(0,o.mdx)("inlineCode",{parentName:"li"},"'.android.js'")),(0,o.mdx)("li",{parentName:"ol"},(0,o.mdx)("em",{parentName:"li"},"moduleName")," + ",(0,o.mdx)("inlineCode",{parentName:"li"},"'.native.js'")," (if ",(0,o.mdx)("a",{parentName:"li",href:"#prefernativeplatform-boolean"},(0,o.mdx)("inlineCode",{parentName:"a"},"context.preferNativePlatform"))," is ",(0,o.mdx)("inlineCode",{parentName:"li"},"true"),")"),(0,o.mdx)("li",{parentName:"ol"},(0,o.mdx)("em",{parentName:"li"},"moduleName")," + ",(0,o.mdx)("inlineCode",{parentName:"li"},"'.js'")),(0,o.mdx)("li",{parentName:"ol"},(0,o.mdx)("em",{parentName:"li"},"moduleName")," + ",(0,o.mdx)("inlineCode",{parentName:"li"},"'.android.jsx'")),(0,o.mdx)("li",{parentName:"ol"},(0,o.mdx)("em",{parentName:"li"},"moduleName")," + ",(0,o.mdx)("inlineCode",{parentName:"li"},"'.native.jsx'")," (if ",(0,o.mdx)("a",{parentName:"li",href:"#prefernativeplatform-boolean"},(0,o.mdx)("inlineCode",{parentName:"a"},"context.preferNativePlatform"))," is ",(0,o.mdx)("inlineCode",{parentName:"li"},"true"),")"),(0,o.mdx)("li",{parentName:"ol"},(0,o.mdx)("em",{parentName:"li"},"moduleName")," + ",(0,o.mdx)("inlineCode",{parentName:"li"},"'.jsx'"))))))),(0,o.mdx)("h4",{id:"resolve_asset"},"RESOLVE_ASSET"),(0,o.mdx)("p",null,"Parameters: (",(0,o.mdx)("em",{parentName:"p"},"context"),", ",(0,o.mdx)("em",{parentName:"p"},"filePath"),", ",(0,o.mdx)("em",{parentName:"p"},"platform"),")"),(0,o.mdx)("ol",null,(0,o.mdx)("li",{parentName:"ol"},"Use ",(0,o.mdx)("a",{parentName:"li",href:"#resolveasset-dirpath-string-assetname-string-extension-string--readonlyarraystring"},(0,o.mdx)("inlineCode",{parentName:"a"},"context.resolveAsset"))," to collect all asset variants."),(0,o.mdx)("li",{parentName:"ol"},"Return an ",(0,o.mdx)("a",{parentName:"li",href:"#asset-files"},"asset resolution")," containing the collected asset paths.")),(0,o.mdx)("h4",{id:"resolve_haste"},"RESOLVE_HASTE"),(0,o.mdx)("p",null,"Parameters: (",(0,o.mdx)("em",{parentName:"p"},"context"),", ",(0,o.mdx)("em",{parentName:"p"},"moduleName"),", ",(0,o.mdx)("em",{parentName:"p"},"platform"),")"),(0,o.mdx)("ol",null,(0,o.mdx)("li",{parentName:"ol"},"Try resolving ",(0,o.mdx)("em",{parentName:"li"},"moduleName")," as a ",(0,o.mdx)("a",{parentName:"li",href:"#resolvehastemodule-string--string"},"Haste module"),".\nIf found, then",(0,o.mdx)("ol",{parentName:"li"},(0,o.mdx)("li",{parentName:"ol"},"Return result as a ",(0,o.mdx)("a",{parentName:"li",href:"#source-file"},"source file resolution")," ",(0,o.mdx)("strong",{parentName:"li"},"without")," applying redirections or trying any platform or extension variants."))),(0,o.mdx)("li",{parentName:"ol"},"Try resolving ",(0,o.mdx)("em",{parentName:"li"},"moduleName")," as a ",(0,o.mdx)("a",{parentName:"li",href:"#resolvehastepackage-string--string"},"Haste (global) package"),", or a path ",(0,o.mdx)("em",{parentName:"li"},"relative")," to a Haste package.\nFor example, if ",(0,o.mdx)("em",{parentName:"li"},"moduleName")," is ",(0,o.mdx)("inlineCode",{parentName:"li"},"'a/b/c'"),", try the following potential Haste package names:",(0,o.mdx)("ol",{parentName:"li"},(0,o.mdx)("li",{parentName:"ol"},(0,o.mdx)("inlineCode",{parentName:"li"},"'a/b/c'"),", relative path ",(0,o.mdx)("inlineCode",{parentName:"li"},"''")),(0,o.mdx)("li",{parentName:"ol"},(0,o.mdx)("inlineCode",{parentName:"li"},"'a/b'"),", relative path ",(0,o.mdx)("inlineCode",{parentName:"li"},"'./c'")),(0,o.mdx)("li",{parentName:"ol"},(0,o.mdx)("inlineCode",{parentName:"li"},"'a'"),", with relative path ",(0,o.mdx)("inlineCode",{parentName:"li"},"'./b/c'"))))),(0,o.mdx)("h3",{id:"resolution-context"},"Resolution context"),(0,o.mdx)("h4",{id:"assetexts-readonlysetstring"},(0,o.mdx)("inlineCode",{parentName:"h4"},"assetExts: $ReadOnlySet")),(0,o.mdx)("p",null,"The set of file extensions used to identify asset files. Defaults to ",(0,o.mdx)("a",{parentName:"p",href:"/docs/configuration#assetexts"},(0,o.mdx)("inlineCode",{parentName:"a"},"resolver.assetExts")),"."),(0,o.mdx)("h4",{id:"dev-boolean"},(0,o.mdx)("inlineCode",{parentName:"h4"},"dev: boolean")),(0,o.mdx)("p",null,(0,o.mdx)("inlineCode",{parentName:"p"},"true")," if the resolution is for a development bundle, or ",(0,o.mdx)("inlineCode",{parentName:"p"},"false")," otherwise."),(0,o.mdx)("h4",{id:"doesfileexist-string--boolean"},(0,o.mdx)("inlineCode",{parentName:"h4"},"doesFileExist: string => boolean")),(0,o.mdx)("p",null,"Returns ",(0,o.mdx)("inlineCode",{parentName:"p"},"true")," if the file with the given path exists, or ",(0,o.mdx)("inlineCode",{parentName:"p"},"false")," otherwise."),(0,o.mdx)("p",null,"By default, Metro implements this by consulting an in-memory map of the filesystem that has been prepared in advance. This approach avoids disk I/O during module resolution."),(0,o.mdx)("h4",{id:"nodemodulespaths-readonlyarraystring"},(0,o.mdx)("inlineCode",{parentName:"h4"},"nodeModulesPaths: $ReadOnlyArray")),(0,o.mdx)("p",null,"A list of paths to check for modules after looking through all ",(0,o.mdx)("inlineCode",{parentName:"p"},"node_modules")," directories."),(0,o.mdx)("p",null,"By default this is set to ",(0,o.mdx)("a",{parentName:"p",href:"/docs/configuration#nodemodulespaths"},(0,o.mdx)("inlineCode",{parentName:"a"},"resolver.nodeModulesPaths"))),(0,o.mdx)("h4",{id:"prefernativeplatform-boolean"},(0,o.mdx)("inlineCode",{parentName:"h4"},"preferNativePlatform: boolean")),(0,o.mdx)("p",null,"If ",(0,o.mdx)("inlineCode",{parentName:"p"},"true"),", try ",(0,o.mdx)("inlineCode",{parentName:"p"},".native.${ext}")," before ",(0,o.mdx)("inlineCode",{parentName:"p"},".${ext}")," and after ",(0,o.mdx)("inlineCode",{parentName:"p"},".${platform}.${ext}")," during resolution. Metro sets this to ",(0,o.mdx)("inlineCode",{parentName:"p"},"true"),"."),(0,o.mdx)("h4",{id:"redirectmodulepath-string--string--false"},(0,o.mdx)("inlineCode",{parentName:"h4"},"redirectModulePath: string => string | false")),(0,o.mdx)("p",null,"Rewrites a module path, or returns ",(0,o.mdx)("inlineCode",{parentName:"p"},"false")," to redirect to the special ",(0,o.mdx)("a",{parentName:"p",href:"#empty-module"},"empty module"),". In the default resolver, the resolution algorithm terminates with an ",(0,o.mdx)("a",{parentName:"p",href:"#empty-module"},"empty module result")," if ",(0,o.mdx)("inlineCode",{parentName:"p"},"redirectModulePath")," returns ",(0,o.mdx)("inlineCode",{parentName:"p"},"false"),"."),(0,o.mdx)("p",null,"Metro uses this to implement the ",(0,o.mdx)("inlineCode",{parentName:"p"},"package.json")," ",(0,o.mdx)("a",{parentName:"p",href:"https://github.com/defunctzombie/package-browser-field-spec"},(0,o.mdx)("inlineCode",{parentName:"a"},"browser")," field spec"),", particularly the ability to ",(0,o.mdx)("a",{parentName:"p",href:"https://github.com/defunctzombie/package-browser-field-spec#replace-specific-files---advanced"},"replace")," and ",(0,o.mdx)("a",{parentName:"p",href:"https://github.com/defunctzombie/package-browser-field-spec#ignore-a-module"},"ignore")," specific files."),(0,o.mdx)("p",null,"The default implementation of this function respects ",(0,o.mdx)("a",{parentName:"p",href:"/docs/configuration#resolvermainfields"},(0,o.mdx)("inlineCode",{parentName:"a"},"resolver.resolverMainFields")),"."),(0,o.mdx)("h4",{id:"resolveasset-dirpath-string-assetname-string-extension-string--readonlyarraystring"},(0,o.mdx)("inlineCode",{parentName:"h4"},"resolveAsset: (dirPath: string, assetName: string, extension: string) => ?$ReadOnlyArray")),(0,o.mdx)("p",null,"Given a directory path, the base asset name and an extension, returns a list of all the asset file names that match the given base name in that directory, or ",(0,o.mdx)("inlineCode",{parentName:"p"},"null")," if no such files are found. The default implementation considers each of ",(0,o.mdx)("a",{parentName:"p",href:"/docs/configuration#assetresolutions"},(0,o.mdx)("inlineCode",{parentName:"a"},"resolver.assetResolutions"))," and uses the ",(0,o.mdx)("inlineCode",{parentName:"p"},"${assetName}@${resolution}${extension}")," format for asset variant file names."),(0,o.mdx)("p",null,"See also ",(0,o.mdx)("a",{parentName:"p",href:"https://reactnative.dev/docs/images#static-image-resources"},"Static Image Resources")," in the React Native docs."),(0,o.mdx)("h4",{id:"sourceexts-readonlyarraystring"},(0,o.mdx)("inlineCode",{parentName:"h4"},"sourceExts: $ReadOnlyArray")),(0,o.mdx)("p",null,"The list of file extensions to try, in order, when resolving a module path that does not exist on disk. Defaults to ",(0,o.mdx)("a",{parentName:"p",href:"/docs/configuration#sourceexts"},(0,o.mdx)("inlineCode",{parentName:"a"},"resolver.sourceExts")),"."),(0,o.mdx)("h4",{id:"mainfields-readonlyarraystring"},(0,o.mdx)("inlineCode",{parentName:"h4"},"mainFields: $ReadOnlyArray")),(0,o.mdx)("p",null,"The ordered list of fields in ",(0,o.mdx)("inlineCode",{parentName:"p"},"package.json")," that should be read to resolve a package's main entry point (and any subpath file replacements) per the ",(0,o.mdx)("a",{parentName:"p",href:"https://github.com/defunctzombie/package-browser-field-spec"},'"browser" field spec'),". Defaults to ",(0,o.mdx)("a",{parentName:"p",href:"/docs/configuration#resolvermainfields"},(0,o.mdx)("inlineCode",{parentName:"a"},"resolver.resolverMainFields")),"."),(0,o.mdx)("h4",{id:"getpackage-string--packagejson"},(0,o.mdx)("inlineCode",{parentName:"h4"},"getPackage: string => PackageJson")),(0,o.mdx)("p",null,"Given the path to a ",(0,o.mdx)("inlineCode",{parentName:"p"},"package.json")," file, returns the parsed file contents."),(0,o.mdx)("h4",{id:"getpackageformodule-absolutemodulepath-string--packageinfo-deprecated"},(0,o.mdx)("inlineCode",{parentName:"h4"},"getPackageForModule: (absoluteModulePath: string) => ?PackageInfo")," ",(0,o.mdx)("div",{class:"label deprecated"},"Deprecated")),(0,o.mdx)("p",null,"Given a candidate absolute module path that may exist under a package, locates and returns the closest package root (working upwards from the given path, stopping at the nearest ",(0,o.mdx)("inlineCode",{parentName:"p"},"node_modules"),"), parsed ",(0,o.mdx)("inlineCode",{parentName:"p"},"package.json")," contents, and the package-relative path of the given path."),(0,o.mdx)("h4",{id:"resolvehastemodule-string--string"},(0,o.mdx)("inlineCode",{parentName:"h4"},"resolveHasteModule: string => ?string")),(0,o.mdx)("p",null,"Resolves a Haste module name to an absolute path. Returns ",(0,o.mdx)("inlineCode",{parentName:"p"},"null")," if no such module exists."),(0,o.mdx)("p",null,"The default implementation of this function uses ",(0,o.mdx)("a",{parentName:"p",href:"https://www.npmjs.com/package/metro-file-map"},"metro-file-map"),"'s ",(0,o.mdx)("inlineCode",{parentName:"p"},"getModule")," method."),(0,o.mdx)("h4",{id:"resolvehastepackage-string--string"},(0,o.mdx)("inlineCode",{parentName:"h4"},"resolveHastePackage: string => ?string")),(0,o.mdx)("p",null,"Resolves a Haste (global) package name to an absolute ",(0,o.mdx)("inlineCode",{parentName:"p"},"package.json")," path. Returns ",(0,o.mdx)("inlineCode",{parentName:"p"},"null")," if no such package exists."),(0,o.mdx)("p",null,"The default implementation of this function uses ",(0,o.mdx)("a",{parentName:"p",href:"https://www.npmjs.com/package/metro-file-map"},"metro-file-map"),"'s ",(0,o.mdx)("inlineCode",{parentName:"p"},"getPackage")," method and can be turned on or off using ",(0,o.mdx)("a",{parentName:"p",href:"/docs/configuration#enableglobalpackages"},(0,o.mdx)("inlineCode",{parentName:"a"},"resolver.enableGlobalPackages")),"."),(0,o.mdx)("h4",{id:"allowhaste-boolean"},(0,o.mdx)("inlineCode",{parentName:"h4"},"allowHaste: boolean")),(0,o.mdx)("p",null,(0,o.mdx)("inlineCode",{parentName:"p"},"true")," if Haste resolutions are allowed in the current context, ",(0,o.mdx)("inlineCode",{parentName:"p"},"false")," otherwise."),(0,o.mdx)("h4",{id:"disablehierarchicallookup-boolean"},(0,o.mdx)("inlineCode",{parentName:"h4"},"disableHierarchicalLookup: boolean")),(0,o.mdx)("p",null,"If ",(0,o.mdx)("inlineCode",{parentName:"p"},"true"),", the resolver should not perform lookup in ",(0,o.mdx)("inlineCode",{parentName:"p"},"node_modules")," directories per the Node resolution algorithm. Defaults to ",(0,o.mdx)("a",{parentName:"p",href:"/docs/configuration#disablehierarchicallookup"},(0,o.mdx)("inlineCode",{parentName:"a"},"resolver.disableHierarchicalLookup")),"."),(0,o.mdx)("h4",{id:"extranodemodules-string-string"},(0,o.mdx)("inlineCode",{parentName:"h4"},"extraNodeModules: ?{[string]: string}")),(0,o.mdx)("p",null,"A mapping of package names to directories that is consulted after the standard lookup through ",(0,o.mdx)("inlineCode",{parentName:"p"},"node_modules")," as well as any ",(0,o.mdx)("a",{parentName:"p",href:"#nodemodulespaths-readonlyarraystring"},(0,o.mdx)("inlineCode",{parentName:"a"},"nodeModulesPaths")),"."),(0,o.mdx)("h4",{id:"originmodulepath-string"},(0,o.mdx)("inlineCode",{parentName:"h4"},"originModulePath: string")),(0,o.mdx)("p",null,"The path to the current module, e.g. the one containing the ",(0,o.mdx)("inlineCode",{parentName:"p"},"import")," we are currently resolving."),(0,o.mdx)("h4",{id:"customresolveroptions-string-mixed"},(0,o.mdx)("inlineCode",{parentName:"h4"},"customResolverOptions: {[string]: mixed}")),(0,o.mdx)("p",null,"Any custom options passed to the resolver. By default, Metro populates this based on URL parameters in the bundle request, e.g. ",(0,o.mdx)("inlineCode",{parentName:"p"},"http://localhost:8081/index.bundle?resolver.key=value")," becomes ",(0,o.mdx)("inlineCode",{parentName:"p"},"{key: 'value'}"),"."),(0,o.mdx)("h4",{id:"resolverequest-customresolver"},(0,o.mdx)("inlineCode",{parentName:"h4"},"resolveRequest: CustomResolver")),(0,o.mdx)("p",null,"A alternative resolver function to which the current request may be delegated. Defaults to ",(0,o.mdx)("a",{parentName:"p",href:"/docs/configuration#resolvereqeuest"},(0,o.mdx)("inlineCode",{parentName:"a"},"resolver.resolveRequest")),"."),(0,o.mdx)("p",null,"Metro expects ",(0,o.mdx)("inlineCode",{parentName:"p"},"resolveRequest")," to have the following signature:"),(0,o.mdx)("pre",null,(0,o.mdx)("code",{parentName:"pre",className:"language-flow"},"function resolveRequest(\n context: ResolutionContext,\n moduleName: string,\n platform: string | null,\n): Resolution {\n // ...\n}\n\ntype Resolution =\n | {type: 'empty'}\n | {type: 'sourceFile', filePath: string}\n | {type: 'assetFiles', filePaths: $ReadOnlyArray};\n")),(0,o.mdx)("p",null,"When calling the default resolver with a non-null ",(0,o.mdx)("inlineCode",{parentName:"p"},"resolveRequest")," function, it represents a custom resolver and will always be called, fully replacing the default resolution logic."),(0,o.mdx)("p",null,"Inside a custom resolver, ",(0,o.mdx)("inlineCode",{parentName:"p"},"resolveRequest")," is set to the default resolver function, for easy chaining and customization."),(0,o.mdx)("h4",{id:"dependency-dependency"},(0,o.mdx)("inlineCode",{parentName:"h4"},"dependency: ?Dependency")),(0,o.mdx)("p",null,"A dependency descriptor corresponding to the current resolution request. This is provided for diagnostic purposes ",(0,o.mdx)("em",{parentName:"p"},"only")," and may not be used for semantic purposes. See the ",(0,o.mdx)("a",{parentName:"p",href:"#caching"},"Caching")," section for more information."),(0,o.mdx)("pre",null,(0,o.mdx)("code",{parentName:"pre",className:"language-flow"},"type Dependency = {\n // The literal name provided to a require or import call. For example 'foo' in\n // case of `require('foo')`.\n name: string,\n\n data: {\n // A locally unique key for this dependency within the origin module.\n key: string,\n\n // Source locations from the Babel AST, relative to the origin module, where\n // this dependency was encountered. This may be an empty array.\n locs: $ReadOnlyArray,\n\n asyncType: 'async' | 'prefetch' | 'weak' | null,\n\n // Other properties are considered internal and may change in the future.\n ...\n },\n};\n")),(0,o.mdx)("h2",{id:"caching"},"Caching"),(0,o.mdx)("p",null,"Resolver results may be cached under the following conditions:"),(0,o.mdx)("ol",null,(0,o.mdx)("li",{parentName:"ol"},"For given origin module paths ",(0,o.mdx)("em",{parentName:"li"},"A")," and ",(0,o.mdx)("em",{parentName:"li"},"B")," and target module name ",(0,o.mdx)("em",{parentName:"li"},"M"),", the resolution for ",(0,o.mdx)("em",{parentName:"li"},"M")," may be reused if ",(0,o.mdx)("strong",{parentName:"li"},"all")," of the following conditions hold:",(0,o.mdx)("ol",{parentName:"li"},(0,o.mdx)("li",{parentName:"ol"},(0,o.mdx)("em",{parentName:"li"},"A")," and ",(0,o.mdx)("em",{parentName:"li"},"B")," are in the same directory."),(0,o.mdx)("li",{parentName:"ol"},"The contents of ",(0,o.mdx)("a",{parentName:"li",href:"#dev"},(0,o.mdx)("inlineCode",{parentName:"a"},"dev"))," and ",(0,o.mdx)("a",{parentName:"li",href:"#customresolveroptions-string-mixed"},(0,o.mdx)("inlineCode",{parentName:"a"},"customResolverOptions"))," are equivalent ( = serialize to JSON the same) in both calls to the resolver."))),(0,o.mdx)("li",{parentName:"ol"},"Any cache of resolutions must be invalidated if any file in the project has changed.")),(0,o.mdx)("p",null,"Custom resolvers must adhere to these assumptions, e.g. they may not return different resolutions for origin modules in the same directory under the same ",(0,o.mdx)("inlineCode",{parentName:"p"},"customResolverOptions"),"."))}c.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/8b9b5fbc.10a336ef.js b/assets/js/8b9b5fbc.10a336ef.js new file mode 100644 index 0000000000..40d19d9774 --- /dev/null +++ b/assets/js/8b9b5fbc.10a336ef.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkmetro_website=self.webpackChunkmetro_website||[]).push([[424],{3905:(e,t,n)=>{n.r(t),n.d(t,{MDXContext:()=>m,MDXProvider:()=>c,mdx:()=>f,useMDXComponents:()=>s,withMDXComponents:()=>d});var r=n(67294);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(){return a=Object.assign||function(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var m=r.createContext({}),d=function(e){return function(t){var n=s(t.components);return r.createElement(e,a({},t,{components:n}))}},s=function(e){var t=r.useContext(m),n=t;return e&&(n="function"==typeof e?e(t):p(p({},t),e)),n},c=function(e){var t=s(e.components);return r.createElement(m.Provider,{value:t},e.children)},u="mdxType",g={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},h=r.forwardRef((function(e,t){var n=e.components,o=e.mdxType,a=e.originalType,i=e.parentName,m=l(e,["components","mdxType","originalType","parentName"]),d=s(n),c=o,u=d["".concat(i,".").concat(c)]||d[c]||g[c]||a;return n?r.createElement(u,p(p({ref:t},m),{},{components:n})):r.createElement(u,p({ref:t},m))}));function f(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=n.length,i=new Array(a);i[0]=h;var p={};for(var l in t)hasOwnProperty.call(t,l)&&(p[l]=t[l]);p.originalType=e,p[u]="string"==typeof e?e:o,i[1]=p;for(var m=2;m{n.r(t),n.d(t,{assets:()=>d,contentTitle:()=>l,default:()=>g,frontMatter:()=>p,metadata:()=>m,toc:()=>s});var r=n(87462),o=n(63366),a=(n(67294),n(3905)),i=["components"],p={id:"local-development",title:"Local Development Setup"},l=void 0,m={unversionedId:"local-development",id:"local-development",title:"Local Development Setup",description:"This page includes tips for developers working on Metro itself, including how to test your changes within other local projects.",source:"@site/../docs/LocalDevelopment.md",sourceDirName:".",slug:"/local-development",permalink:"/docs/local-development",draft:!1,editUrl:"https://github.com/facebook/metro/edit/main/docs/../docs/LocalDevelopment.md",tags:[],version:"current",lastUpdatedAt:1726493799,formattedLastUpdatedAt:"Sep 16, 2024",frontMatter:{id:"local-development",title:"Local Development Setup"},sidebar:"docs",previous:{title:"Troubleshooting",permalink:"/docs/troubleshooting"},next:{title:"Bundle Formats",permalink:"/docs/bundling"}},d={},s=[{value:"Testing Metro Changes inside a React Native Project",id:"testing-metro-changes-inside-a-react-native-project",level:3},{value:"Debug Logging",id:"debug-logging",level:3}],c={toc:s},u="wrapper";function g(e){var t=e.components,n=(0,o.Z)(e,i);return(0,a.mdx)(u,(0,r.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,a.mdx)("p",null,"This page includes tips for developers working on Metro itself, including how to test your changes within other local projects."),(0,a.mdx)("h3",{id:"testing-metro-changes-inside-a-react-native-project"},"Testing Metro Changes inside a React Native Project"),(0,a.mdx)("p",null,"When developing Metro, running your iterations against a local target project can be a great way to test the impact of your changes end-to-end."),(0,a.mdx)("p",null,"Our recommended workflow is to use ",(0,a.mdx)("a",{parentName:"p",href:"https://classic.yarnpkg.com/en/docs/cli/link"},(0,a.mdx)("inlineCode",{parentName:"a"},"yarn link"))," to register local ",(0,a.mdx)("inlineCode",{parentName:"p"},"metro")," packages within your development clone and then hot-switch to these versions in the consuming project. These instructions cover linking a local Metro clone with a bare workflow React Native app (i.e. having run ",(0,a.mdx)("inlineCode",{parentName:"p"},"npx react-native init MetroTestApp"),")."),(0,a.mdx)("pre",null,(0,a.mdx)("code",{parentName:"pre",className:"language-sh"},".\n\u2514\u2500\u2500 Development\n \u251c\u2500\u2500 metro # metro clone\n \u2514\u2500\u2500 MetroTestApp # target project\n")),(0,a.mdx)("ol",null,(0,a.mdx)("li",{parentName:"ol"},(0,a.mdx)("p",{parentName:"li"},(0,a.mdx)("strong",{parentName:"p"},"Use ",(0,a.mdx)("inlineCode",{parentName:"strong"},"yarn link")," in your ",(0,a.mdx)("inlineCode",{parentName:"strong"},"metro")," clone to register local packages")),(0,a.mdx)("p",{parentName:"li"},"From inside our ",(0,a.mdx)("inlineCode",{parentName:"p"},"metro")," clone, ",(0,a.mdx)("inlineCode",{parentName:"p"},"yarn link")," is responsible for registering local package folders to be linked to elsewhere."),(0,a.mdx)("p",{parentName:"li"},"We recommend using ",(0,a.mdx)("inlineCode",{parentName:"p"},"npm exec --workspaces")," to register all packages in the ",(0,a.mdx)("inlineCode",{parentName:"p"},"metro")," repo \u2014 these can be individually linked into the target project later."),(0,a.mdx)("pre",{parentName:"li"},(0,a.mdx)("code",{parentName:"pre"},"npm exec --workspaces -- yarn link\n"))),(0,a.mdx)("li",{parentName:"ol"},(0,a.mdx)("p",{parentName:"li"},(0,a.mdx)("strong",{parentName:"p"},"Use ",(0,a.mdx)("inlineCode",{parentName:"strong"},"yarn link")," to replace Metro packages in your target project")),(0,a.mdx)("p",{parentName:"li"},"From inside our target project folder, ",(0,a.mdx)("inlineCode",{parentName:"p"},"yarn link ")," can be used to apply our registered ",(0,a.mdx)("inlineCode",{parentName:"p"},"metro")," packages for that project only."),(0,a.mdx)("pre",{parentName:"li"},(0,a.mdx)("code",{parentName:"pre",className:"language-sh"},"# Links 3 packages\nyarn link metro metro-config metro-runtime\n")),(0,a.mdx)("p",{parentName:"li"},"Note: At mininum, the ",(0,a.mdx)("inlineCode",{parentName:"p"},"metro")," and ",(0,a.mdx)("inlineCode",{parentName:"p"},"metro-runtime")," packages need to be linked.")),(0,a.mdx)("li",{parentName:"ol"},(0,a.mdx)("p",{parentName:"li"},(0,a.mdx)("strong",{parentName:"p"},"Configure Metro ",(0,a.mdx)("inlineCode",{parentName:"strong"},"watchFolders")," to work with our linked packages")),(0,a.mdx)("p",{parentName:"li"},"Because ",(0,a.mdx)("inlineCode",{parentName:"p"},"yarn link")," has included files outside of the immediate React Native project folder, we need to inform Metro that this set of files exists (as it will not automatically follow the symlinks). Add the following to your ",(0,a.mdx)("inlineCode",{parentName:"p"},"metro.config.js"),":"),(0,a.mdx)("pre",{parentName:"li"},(0,a.mdx)("code",{parentName:"pre",className:"language-diff"},"+ const path = require('path');\n\n module.exports = {\n+ watchFolders: [\n+ path.resolve(__dirname, './node_modules'),\n+ // Include necessary file paths for `yarn link`ed modules\n+ path.resolve(__dirname, '../metro/packages'),\n+ path.resolve(__dirname, '../metro/node_modules'),\n+ ],\n ...\n };\n")),(0,a.mdx)("p",{parentName:"li"},(0,a.mdx)("strong",{parentName:"p"},"Run Metro")),(0,a.mdx)("p",{parentName:"li"},"Now we should be able to run Metro within our target project. Remember to restart this command after any code changes you make to ",(0,a.mdx)("inlineCode",{parentName:"p"},"metro")," or to the target project's ",(0,a.mdx)("inlineCode",{parentName:"p"},"metro.config.js")," file."),(0,a.mdx)("pre",{parentName:"li"},(0,a.mdx)("code",{parentName:"pre"},"yarn react-native start\n"))),(0,a.mdx)("li",{parentName:"ol"},(0,a.mdx)("p",{parentName:"li"},(0,a.mdx)("strong",{parentName:"p"},"(Optional) Clean up with ",(0,a.mdx)("inlineCode",{parentName:"strong"},"yarn unlink"))),(0,a.mdx)("p",{parentName:"li"},"If you want to restore the remote (i.e. production npm) versions of ",(0,a.mdx)("inlineCode",{parentName:"p"},"metro")," packages in your target project, step 2 (and 1) can be repeated with ",(0,a.mdx)("inlineCode",{parentName:"p"},"yarn unlink"),"."))),(0,a.mdx)("h3",{id:"debug-logging"},"Debug Logging"),(0,a.mdx)("p",null,"Metro uses the ",(0,a.mdx)("a",{parentName:"p",href:"https://www.npmjs.com/package/debug"},"debug")," package to write logs under named debug scopes (for example: ",(0,a.mdx)("inlineCode",{parentName:"p"},"Metro:WatchmanWatcher"),"). Set the ",(0,a.mdx)("inlineCode",{parentName:"p"},"DEBUG")," environment variable before starting Metro to enable logs matching the supplied pattern."),(0,a.mdx)("p",null,"The snippet below provides a pattern matching all Metro-defined messages."),(0,a.mdx)("pre",null,(0,a.mdx)("code",{parentName:"pre"},"DEBUG='Metro:*' yarn metro serve\n")))}g.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/8b9b5fbc.a81dcaf8.js b/assets/js/8b9b5fbc.a81dcaf8.js deleted file mode 100644 index 7ed9c126c2..0000000000 --- a/assets/js/8b9b5fbc.a81dcaf8.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkmetro_website=self.webpackChunkmetro_website||[]).push([[424],{3905:(e,t,n)=>{n.r(t),n.d(t,{MDXContext:()=>m,MDXProvider:()=>c,mdx:()=>f,useMDXComponents:()=>s,withMDXComponents:()=>d});var r=n(67294);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(){return a=Object.assign||function(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var m=r.createContext({}),d=function(e){return function(t){var n=s(t.components);return r.createElement(e,a({},t,{components:n}))}},s=function(e){var t=r.useContext(m),n=t;return e&&(n="function"==typeof e?e(t):p(p({},t),e)),n},c=function(e){var t=s(e.components);return r.createElement(m.Provider,{value:t},e.children)},u="mdxType",g={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},h=r.forwardRef((function(e,t){var n=e.components,o=e.mdxType,a=e.originalType,i=e.parentName,m=l(e,["components","mdxType","originalType","parentName"]),d=s(n),c=o,u=d["".concat(i,".").concat(c)]||d[c]||g[c]||a;return n?r.createElement(u,p(p({ref:t},m),{},{components:n})):r.createElement(u,p({ref:t},m))}));function f(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=n.length,i=new Array(a);i[0]=h;var p={};for(var l in t)hasOwnProperty.call(t,l)&&(p[l]=t[l]);p.originalType=e,p[u]="string"==typeof e?e:o,i[1]=p;for(var m=2;m{n.r(t),n.d(t,{assets:()=>d,contentTitle:()=>l,default:()=>g,frontMatter:()=>p,metadata:()=>m,toc:()=>s});var r=n(87462),o=n(63366),a=(n(67294),n(3905)),i=["components"],p={id:"local-development",title:"Local Development Setup"},l=void 0,m={unversionedId:"local-development",id:"local-development",title:"Local Development Setup",description:"This page includes tips for developers working on Metro itself, including how to test your changes within other local projects.",source:"@site/../docs/LocalDevelopment.md",sourceDirName:".",slug:"/local-development",permalink:"/docs/local-development",draft:!1,editUrl:"https://github.com/facebook/metro/edit/main/docs/../docs/LocalDevelopment.md",tags:[],version:"current",lastUpdatedAt:1725545477,formattedLastUpdatedAt:"Sep 5, 2024",frontMatter:{id:"local-development",title:"Local Development Setup"},sidebar:"docs",previous:{title:"Troubleshooting",permalink:"/docs/troubleshooting"},next:{title:"Bundle Formats",permalink:"/docs/bundling"}},d={},s=[{value:"Testing Metro Changes inside a React Native Project",id:"testing-metro-changes-inside-a-react-native-project",level:3},{value:"Debug Logging",id:"debug-logging",level:3}],c={toc:s},u="wrapper";function g(e){var t=e.components,n=(0,o.Z)(e,i);return(0,a.mdx)(u,(0,r.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,a.mdx)("p",null,"This page includes tips for developers working on Metro itself, including how to test your changes within other local projects."),(0,a.mdx)("h3",{id:"testing-metro-changes-inside-a-react-native-project"},"Testing Metro Changes inside a React Native Project"),(0,a.mdx)("p",null,"When developing Metro, running your iterations against a local target project can be a great way to test the impact of your changes end-to-end."),(0,a.mdx)("p",null,"Our recommended workflow is to use ",(0,a.mdx)("a",{parentName:"p",href:"https://classic.yarnpkg.com/en/docs/cli/link"},(0,a.mdx)("inlineCode",{parentName:"a"},"yarn link"))," to register local ",(0,a.mdx)("inlineCode",{parentName:"p"},"metro")," packages within your development clone and then hot-switch to these versions in the consuming project. These instructions cover linking a local Metro clone with a bare workflow React Native app (i.e. having run ",(0,a.mdx)("inlineCode",{parentName:"p"},"npx react-native init MetroTestApp"),")."),(0,a.mdx)("pre",null,(0,a.mdx)("code",{parentName:"pre",className:"language-sh"},".\n\u2514\u2500\u2500 Development\n \u251c\u2500\u2500 metro # metro clone\n \u2514\u2500\u2500 MetroTestApp # target project\n")),(0,a.mdx)("ol",null,(0,a.mdx)("li",{parentName:"ol"},(0,a.mdx)("p",{parentName:"li"},(0,a.mdx)("strong",{parentName:"p"},"Use ",(0,a.mdx)("inlineCode",{parentName:"strong"},"yarn link")," in your ",(0,a.mdx)("inlineCode",{parentName:"strong"},"metro")," clone to register local packages")),(0,a.mdx)("p",{parentName:"li"},"From inside our ",(0,a.mdx)("inlineCode",{parentName:"p"},"metro")," clone, ",(0,a.mdx)("inlineCode",{parentName:"p"},"yarn link")," is responsible for registering local package folders to be linked to elsewhere."),(0,a.mdx)("p",{parentName:"li"},"We recommend using ",(0,a.mdx)("inlineCode",{parentName:"p"},"npm exec --workspaces")," to register all packages in the ",(0,a.mdx)("inlineCode",{parentName:"p"},"metro")," repo \u2014 these can be individually linked into the target project later."),(0,a.mdx)("pre",{parentName:"li"},(0,a.mdx)("code",{parentName:"pre"},"npm exec --workspaces -- yarn link\n"))),(0,a.mdx)("li",{parentName:"ol"},(0,a.mdx)("p",{parentName:"li"},(0,a.mdx)("strong",{parentName:"p"},"Use ",(0,a.mdx)("inlineCode",{parentName:"strong"},"yarn link")," to replace Metro packages in your target project")),(0,a.mdx)("p",{parentName:"li"},"From inside our target project folder, ",(0,a.mdx)("inlineCode",{parentName:"p"},"yarn link ")," can be used to apply our registered ",(0,a.mdx)("inlineCode",{parentName:"p"},"metro")," packages for that project only."),(0,a.mdx)("pre",{parentName:"li"},(0,a.mdx)("code",{parentName:"pre",className:"language-sh"},"# Links 3 packages\nyarn link metro metro-config metro-runtime\n")),(0,a.mdx)("p",{parentName:"li"},"Note: At mininum, the ",(0,a.mdx)("inlineCode",{parentName:"p"},"metro")," and ",(0,a.mdx)("inlineCode",{parentName:"p"},"metro-runtime")," packages need to be linked.")),(0,a.mdx)("li",{parentName:"ol"},(0,a.mdx)("p",{parentName:"li"},(0,a.mdx)("strong",{parentName:"p"},"Configure Metro ",(0,a.mdx)("inlineCode",{parentName:"strong"},"watchFolders")," to work with our linked packages")),(0,a.mdx)("p",{parentName:"li"},"Because ",(0,a.mdx)("inlineCode",{parentName:"p"},"yarn link")," has included files outside of the immediate React Native project folder, we need to inform Metro that this set of files exists (as it will not automatically follow the symlinks). Add the following to your ",(0,a.mdx)("inlineCode",{parentName:"p"},"metro.config.js"),":"),(0,a.mdx)("pre",{parentName:"li"},(0,a.mdx)("code",{parentName:"pre",className:"language-diff"},"+ const path = require('path');\n\n module.exports = {\n+ watchFolders: [\n+ path.resolve(__dirname, './node_modules'),\n+ // Include necessary file paths for `yarn link`ed modules\n+ path.resolve(__dirname, '../metro/packages'),\n+ path.resolve(__dirname, '../metro/node_modules'),\n+ ],\n ...\n };\n")),(0,a.mdx)("p",{parentName:"li"},(0,a.mdx)("strong",{parentName:"p"},"Run Metro")),(0,a.mdx)("p",{parentName:"li"},"Now we should be able to run Metro within our target project. Remember to restart this command after any code changes you make to ",(0,a.mdx)("inlineCode",{parentName:"p"},"metro")," or to the target project's ",(0,a.mdx)("inlineCode",{parentName:"p"},"metro.config.js")," file."),(0,a.mdx)("pre",{parentName:"li"},(0,a.mdx)("code",{parentName:"pre"},"yarn react-native start\n"))),(0,a.mdx)("li",{parentName:"ol"},(0,a.mdx)("p",{parentName:"li"},(0,a.mdx)("strong",{parentName:"p"},"(Optional) Clean up with ",(0,a.mdx)("inlineCode",{parentName:"strong"},"yarn unlink"))),(0,a.mdx)("p",{parentName:"li"},"If you want to restore the remote (i.e. production npm) versions of ",(0,a.mdx)("inlineCode",{parentName:"p"},"metro")," packages in your target project, step 2 (and 1) can be repeated with ",(0,a.mdx)("inlineCode",{parentName:"p"},"yarn unlink"),"."))),(0,a.mdx)("h3",{id:"debug-logging"},"Debug Logging"),(0,a.mdx)("p",null,"Metro uses the ",(0,a.mdx)("a",{parentName:"p",href:"https://www.npmjs.com/package/debug"},"debug")," package to write logs under named debug scopes (for example: ",(0,a.mdx)("inlineCode",{parentName:"p"},"Metro:WatchmanWatcher"),"). Set the ",(0,a.mdx)("inlineCode",{parentName:"p"},"DEBUG")," environment variable before starting Metro to enable logs matching the supplied pattern."),(0,a.mdx)("p",null,"The snippet below provides a pattern matching all Metro-defined messages."),(0,a.mdx)("pre",null,(0,a.mdx)("code",{parentName:"pre"},"DEBUG='Metro:*' yarn metro serve\n")))}g.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/ae2a35ac.c3e41158.js b/assets/js/ae2a35ac.c3e41158.js new file mode 100644 index 0000000000..1a1f961204 --- /dev/null +++ b/assets/js/ae2a35ac.c3e41158.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkmetro_website=self.webpackChunkmetro_website||[]).push([[708],{3905:(e,t,n)=>{n.r(t),n.d(t,{MDXContext:()=>l,MDXProvider:()=>c,mdx:()=>h,useMDXComponents:()=>m,withMDXComponents:()=>d});var a=n(67294);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(){return i=Object.assign||function(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var l=a.createContext({}),d=function(e){return function(t){var n=m(t.components);return a.createElement(e,i({},t,{components:n}))}},m=function(e){var t=a.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):s(s({},t),e)),n},c=function(e){var t=m(e.components);return a.createElement(l.Provider,{value:t},e.children)},u="mdxType",x={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},g=a.forwardRef((function(e,t){var n=e.components,o=e.mdxType,i=e.originalType,r=e.parentName,l=p(e,["components","mdxType","originalType","parentName"]),d=m(n),c=o,u=d["".concat(r,".").concat(c)]||d[c]||x[c]||i;return n?a.createElement(u,s(s({ref:t},l),{},{components:n})):a.createElement(u,s({ref:t},l))}));function h(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var i=n.length,r=new Array(i);r[0]=g;var s={};for(var p in t)hasOwnProperty.call(t,p)&&(s[p]=t[p]);s.originalType=e,s[u]="string"==typeof e?e:o,r[1]=s;for(var l=2;l{n.r(t),n.d(t,{assets:()=>d,contentTitle:()=>p,default:()=>x,frontMatter:()=>s,metadata:()=>l,toc:()=>m});var a=n(87462),o=n(63366),i=(n(67294),n(3905)),r=["components"],s={id:"package-exports",title:"Package Exports Support (Experimental)"},p=void 0,l={unversionedId:"package-exports",id:"package-exports",title:"Package Exports Support (Experimental)",description:"Background",source:"@site/../docs/PackageExports.md",sourceDirName:".",slug:"/package-exports",permalink:"/docs/package-exports",draft:!1,editUrl:"https://github.com/facebook/metro/edit/main/docs/../docs/PackageExports.md",tags:[],version:"current",lastUpdatedAt:1726493799,formattedLastUpdatedAt:"Sep 16, 2024",frontMatter:{id:"package-exports",title:"Package Exports Support (Experimental)"},sidebar:"docs",previous:{title:"Metro CLI Options",permalink:"/docs/cli"},next:{title:"Troubleshooting",permalink:"/docs/troubleshooting"}},d={},m=[{value:"Background",id:"background",level:2},{value:"Configuration options",id:"configuration-options",level:2},{value:"Summary of breaking changes",id:"summary-of-breaking-changes",level:2},{value:"Breaking: Match "exports" first, then fall back to legacy resolution",id:"breaking-match-exports-first-then-fall-back-to-legacy-resolution",level:3},{value:"Example",id:"example",level:4},{value:"Breaking: Import specifiers are matched exactly",id:"breaking-import-specifiers-are-matched-exactly",level:3},{value:"Example",id:"example-1",level:4},{value:"Package encapsulation is lenient",id:"package-encapsulation-is-lenient",level:3},{value:"Migration guide for package maintainers",id:"migration-guide-for-package-maintainers",level:2},{value:"Recommended: Introducing "exports" is a breaking change",id:"recommended-introducing-exports-is-a-breaking-change",level:3},{value:"Package subpaths",id:"package-subpaths",level:3},{value:"File extensions are important!",id:"file-extensions-are-important",level:4},{value:"Subpath patterns do not permit expansion",id:"subpath-patterns-do-not-permit-expansion",level:4},{value:"Replacing "browser" and "react-native" fields",id:"replacing-browser-and-react-native-fields",level:3},{value:"Example: Use conditional exports to target web and React Native",id:"example-use-conditional-exports-to-target-web-and-react-native",level:4},{value:"Replacing platform-specific extensions",id:"replacing-platform-specific-extensions",level:3},{value:"Use Platform.select() (React Native)",id:"use-platformselect-react-native",level:4},{value:"Asset files",id:"asset-files",level:3}],c={toc:m},u="wrapper";function x(e){var t=e.components,n=(0,o.Z)(e,r);return(0,i.mdx)(u,(0,a.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,i.mdx)("h2",{id:"background"},"Background"),(0,i.mdx)("p",null,"Introduced in Node.js 12.7.0, Package Exports is a modern approach for npm packages to specify ",(0,i.mdx)("strong",{parentName:"p"},"entry points")," \u2014 the mapping of package subpaths which can be externally imported and which file(s) they should resolve to."),(0,i.mdx)("p",null,"When Package Exports support is enabled via ",(0,i.mdx)("a",{parentName:"p",href:"/docs/configuration/#unstable_enablepackageexports-experimental"},(0,i.mdx)("inlineCode",{parentName:"a"},"resolver.unstable_enablePackageExports")),", Metro's ",(0,i.mdx)("a",{parentName:"p",href:"/docs/resolution#algorithm"},"module resolution algorithm")," will consider the ",(0,i.mdx)("inlineCode",{parentName:"p"},'"exports"')," field in ",(0,i.mdx)("inlineCode",{parentName:"p"},"package.json")," files."),(0,i.mdx)("ul",null,(0,i.mdx)("li",{parentName:"ul"},(0,i.mdx)("a",{parentName:"li",href:"https://nodejs.org/docs/latest-v19.x/api/packages.html#package-entry-points"},"Node.js spec")),(0,i.mdx)("li",{parentName:"ul"},(0,i.mdx)("a",{parentName:"li",href:"https://github.com/react-native-community/discussions-and-proposals/blob/main/proposals/0534-metro-package-exports-support.md"},"RFC for Package Exports in Metro")),(0,i.mdx)("li",{parentName:"ul"},"React Native announcement post (coming soon!)")),(0,i.mdx)("h2",{id:"configuration-options"},"Configuration options"),(0,i.mdx)("table",null,(0,i.mdx)("thead",{parentName:"table"},(0,i.mdx)("tr",{parentName:"thead"},(0,i.mdx)("th",{parentName:"tr",align:null},"Option"),(0,i.mdx)("th",{parentName:"tr",align:null},"Description"))),(0,i.mdx)("tbody",{parentName:"table"},(0,i.mdx)("tr",{parentName:"tbody"},(0,i.mdx)("td",{parentName:"tr",align:null},(0,i.mdx)("a",{parentName:"td",href:"/docs/configuration/#unstable_enablepackageexports-experimental"},(0,i.mdx)("inlineCode",{parentName:"a"},"resolver.unstable_enablePackageExports"))),(0,i.mdx)("td",{parentName:"tr",align:null},"Enable Package Exports support.")),(0,i.mdx)("tr",{parentName:"tbody"},(0,i.mdx)("td",{parentName:"tr",align:null},(0,i.mdx)("a",{parentName:"td",href:"/docs/configuration/#unstable_conditionnames-experimental"},(0,i.mdx)("inlineCode",{parentName:"a"},"resolver.unstable_conditionNames"))),(0,i.mdx)("td",{parentName:"tr",align:null},"The set of condition names to assert when resolving conditional exports.")),(0,i.mdx)("tr",{parentName:"tbody"},(0,i.mdx)("td",{parentName:"tr",align:null},(0,i.mdx)("a",{parentName:"td",href:"/docs/configuration/#unstable_conditionsbyplatform-experimental"},(0,i.mdx)("inlineCode",{parentName:"a"},"resolver.unstable_conditionsByPlatform"))),(0,i.mdx)("td",{parentName:"tr",align:null},"The additional condition names to assert when resolving for a given platform target.")))),(0,i.mdx)("h2",{id:"summary-of-breaking-changes"},"Summary of breaking changes"),(0,i.mdx)("admonition",{type:"info"},(0,i.mdx)("p",{parentName:"admonition"},(0,i.mdx)("strong",{parentName:"p"},"Package Exports resolution is available since Metro 0.76.1 and is disabled by default"),". We will provide the option to disable it for a long time yet, and have no plans to remove existing non-",(0,i.mdx)("inlineCode",{parentName:"p"},'"exports"')," resolution behaviour.")),(0,i.mdx)("p",null,"Since Package Exports features overlap with existing React Native concepts (such as ",(0,i.mdx)("a",{parentName:"p",href:"https://reactnative.dev/docs/platform-specific-code"},"platform-specific extensions"),"), and since ",(0,i.mdx)("inlineCode",{parentName:"p"},'"exports"')," had been live in the npm ecosystem for some time, we reached out to the React Native community to make sure our implementation would meet developers' needs (",(0,i.mdx)("a",{parentName:"p",href:"https://github.com/react-native-community/discussions-and-proposals/pull/534"},"PR"),", ",(0,i.mdx)("a",{parentName:"p",href:"https://github.com/react-native-community/discussions-and-proposals/blob/main/proposals/0534-metro-package-exports-support.md"},"final RFC"),")."),(0,i.mdx)("p",null,"This led us to create an implementation of Package Exports in Metro that is spec-compliant (necessitating some breaking changes), but backwards compatible otherwise (helping apps with existing imports to migrate gradually)."),(0,i.mdx)("h3",{id:"breaking-match-exports-first-then-fall-back-to-legacy-resolution"},"Breaking: Match ",(0,i.mdx)("inlineCode",{parentName:"h3"},'"exports"')," first, then fall back to legacy resolution"),(0,i.mdx)("p",null,"If present in a ",(0,i.mdx)("inlineCode",{parentName:"p"},"package.json")," file, ",(0,i.mdx)("inlineCode",{parentName:"p"},'"exports"')," will be the first field consulted when resolving a package."),(0,i.mdx)("ul",null,(0,i.mdx)("li",{parentName:"ul"},(0,i.mdx)("inlineCode",{parentName:"li"},'"exports"')," will be used instead of any existing ",(0,i.mdx)("inlineCode",{parentName:"li"},'"react-native"'),", ",(0,i.mdx)("inlineCode",{parentName:"li"},'"browser"'),", or ",(0,i.mdx)("inlineCode",{parentName:"li"},'"main"')," field \u2014\xa0or a file on disk at the same subpath (edge case)."),(0,i.mdx)("li",{parentName:"ul"},(0,i.mdx)("strong",{parentName:"li"},"Fallback"),": If the requested subpath is not matched in ",(0,i.mdx)("inlineCode",{parentName:"li"},'"exports"'),", Metro will try to resolve it again, considering the above fields.")),(0,i.mdx)("p",null,"Subpaths matched in ",(0,i.mdx)("inlineCode",{parentName:"p"},'"exports"')," (including via ",(0,i.mdx)("a",{parentName:"p",href:"https://nodejs.org/docs/latest-v19.x/api/packages.html#subpath-patterns"},"subpath patterns"),") will use the exact target file path specified by a package."),(0,i.mdx)("ul",null,(0,i.mdx)("li",{parentName:"ul"},"Metro will not expand ",(0,i.mdx)("a",{parentName:"li",href:"/docs/configuration/#sourceexts"},(0,i.mdx)("inlineCode",{parentName:"a"},"sourceExts"))," against the import specifier."),(0,i.mdx)("li",{parentName:"ul"},"Metro will not resolve ",(0,i.mdx)("a",{parentName:"li",href:"https://reactnative.dev/docs/platform-specific-code"},"platform-specific extensions")," against the target file."),(0,i.mdx)("li",{parentName:"ul"},(0,i.mdx)("strong",{parentName:"li"},"Unchanged"),": Metro will expand ",(0,i.mdx)("a",{parentName:"li",href:"/docs/configuration#assetresolutions"},"asset densities")," (e.g. ",(0,i.mdx)("inlineCode",{parentName:"li"},"icon.png")," \u2192 ",(0,i.mdx)("inlineCode",{parentName:"li"},"icon@2x.png"),") if the target file ",(0,i.mdx)("a",{parentName:"li",href:"/docs/configuration/#assetexts"},"is an asset"),".")),(0,i.mdx)("h4",{id:"example"},"Example"),(0,i.mdx)("p",null,"For a package without an ",(0,i.mdx)("inlineCode",{parentName:"p"},'"exports"')," field, Metro tries multiple potential file locations based on the import specifier:"),(0,i.mdx)("pre",null,(0,i.mdx)("code",{parentName:"pre",className:"language-js"},"import FooComponent from 'some-pkg/FooComponent';\n// Tries .[platform].js, .native.js, .js (+ TypeScript variants)\n")),(0,i.mdx)("p",null,"However, if ",(0,i.mdx)("inlineCode",{parentName:"p"},'"./FooComponent"')," is listed in ",(0,i.mdx)("inlineCode",{parentName:"p"},'"exports"'),", Metro matches the import specifier to this subpath, and uses the target file specified by the package with no further rules:"),(0,i.mdx)("pre",null,(0,i.mdx)("code",{parentName:"pre",className:"language-js"},"import FooComponent from 'some-pkg/FooComponent';\n// Resolves exact target from \"exports\" only\n")),(0,i.mdx)("admonition",{type:"note"},(0,i.mdx)("p",{parentName:"admonition"},"We have no plans to drop platform-specific extensions for packages not using ",(0,i.mdx)("inlineCode",{parentName:"p"},'"exports"'),", or in app code.")),(0,i.mdx)("h3",{id:"breaking-import-specifiers-are-matched-exactly"},"Breaking: Import specifiers are matched exactly"),(0,i.mdx)("p",null,"Previously, import specifiers (the string given to ",(0,i.mdx)("inlineCode",{parentName:"p"},"import")," or ",(0,i.mdx)("inlineCode",{parentName:"p"},"require()"),") could be defined using both extensioned or extensionless paths. This is no longer the case for subpath keys in the ",(0,i.mdx)("inlineCode",{parentName:"p"},'"exports"')," field."),(0,i.mdx)("h4",{id:"example-1"},"Example"),(0,i.mdx)("pre",null,(0,i.mdx)("code",{parentName:"pre",className:"language-json"},'{\n "name": "some-pkg",\n "exports": {\n "./FooComponent": "./src/FooComponent.js"\n }\n}\n')),(0,i.mdx)("pre",null,(0,i.mdx)("code",{parentName:"pre",className:"language-js"},'import FooComponent from \'some-pkg/FooComponent.js\';\n// Inaccessible unless the package had also listed "./FooComponent.js"\n// as an "exports" key\n')),(0,i.mdx)("p",null,"Note that this behaviour also applies for subpath patterns: ",(0,i.mdx)("inlineCode",{parentName:"p"},'"./*": "./src/*.js"')," is distinct from ",(0,i.mdx)("inlineCode",{parentName:"p"},'"./*.js": "./src/*.js"'),"."),(0,i.mdx)("h3",{id:"package-encapsulation-is-lenient"},"Package encapsulation is lenient"),(0,i.mdx)("p",null,"In Node.js, it is an error to import package subpaths that aren't explicitly listed in ",(0,i.mdx)("inlineCode",{parentName:"p"},'"exports"'),". In Metro, we've decided to handle these errors leniently and resolve modules following the old behavior as necessary. This is intended to reduce user friction for previously allowed imports in existing Metro projects."),(0,i.mdx)("p",null,"Instead of throwing an error, Metro will log a warning and fall back to file-based resolution."),(0,i.mdx)("pre",null,(0,i.mdx)("code",{parentName:"pre",className:"language-sh"},'warn: You have imported the module "foo/private/fn.js" which is not listed in\nthe "exports" of "foo". Consider updating your call site or asking the package\nmaintainer(s) to expose this API.\n')),(0,i.mdx)("admonition",{type:"note"},(0,i.mdx)("p",{parentName:"admonition"},"We plan to implement a strict mode for package encapsulation in future, to align with Node's default behavior. ",(0,i.mdx)("strong",{parentName:"p"},"We recommend that all developers fix encapsulation warnings in their code"),".")),(0,i.mdx)("h2",{id:"migration-guide-for-package-maintainers"},"Migration guide for package maintainers"),(0,i.mdx)("p",null,(0,i.mdx)("strong",{parentName:"p"},"Adding an ",(0,i.mdx)("inlineCode",{parentName:"strong"},'"exports"')," field to your package is entirely optional"),". Existing package resolution features will behave identically for packages which don't use ",(0,i.mdx)("inlineCode",{parentName:"p"},'"exports"')," \u2014 and we have no plans to remove this behaviour."),(0,i.mdx)("h3",{id:"recommended-introducing-exports-is-a-breaking-change"},"Recommended: Introducing ",(0,i.mdx)("inlineCode",{parentName:"h3"},'"exports"')," is a breaking change"),(0,i.mdx)("p",null,"The Node.js spec gives guidance on migrating to ",(0,i.mdx)("inlineCode",{parentName:"p"},'"exports"')," in a non-breaking manner, however this is challenging in practice. For instance, if your React Native package uses ",(0,i.mdx)("a",{parentName:"p",href:"https://reactnative.dev/docs/platform-specific-code"},"platform-specific extensions")," on its public exports, this is a breaking change by default."),(0,i.mdx)("blockquote",null,(0,i.mdx)("p",{parentName:"blockquote"},"To make the introduction of ",(0,i.mdx)("inlineCode",{parentName:"p"},'"exports"')," non-breaking, ensure that every previously supported entry point is exported. It is best to explicitly specify entry points so that the package's public API is well-defined."),(0,i.mdx)("p",{parentName:"blockquote"},"\u2014\xa0",(0,i.mdx)("a",{parentName:"p",href:"https://nodejs.org/docs/latest-v19.x/api/packages.html#package-entry-points"},"https://nodejs.org/docs/latest-v19.x/api/packages.html#package-entry-points"))),(0,i.mdx)("h3",{id:"package-subpaths"},"Package subpaths"),(0,i.mdx)("admonition",{type:"caution"},(0,i.mdx)("p",{parentName:"admonition"},(0,i.mdx)("strong",{parentName:"p"},"Please do not rely on ",(0,i.mdx)("a",{parentName:"strong",href:"#package-encapsulation-is-lenient"},"lenient package encapsulation")," under Metro.")," While Metro does this for backwards compatibility, packages should follow how ",(0,i.mdx)("inlineCode",{parentName:"p"},'"exports"')," is documented in the spec and strictly implemented by other tools.")),(0,i.mdx)("h4",{id:"file-extensions-are-important"},"File extensions are important!"),(0,i.mdx)("p",null,"Each subpath is an exact specifier (",(0,i.mdx)("a",{parentName:"p",href:"https://github.com/react-native-community/discussions-and-proposals/blob/main/proposals/0534-metro-package-exports-support.md#exact-path-specifiers"},"see section in RFC"),")."),(0,i.mdx)("p",null,"We recommend continuing to use ",(0,i.mdx)("strong",{parentName:"p"},"extensionless specifiers")," for subpaths in packages targeting React Native \u2014\xa0or ",(0,i.mdx)("strong",{parentName:"p"},"defining both extensioned and extensionless specifiers"),". This will match matching existing user expectations."),(0,i.mdx)("pre",null,(0,i.mdx)("code",{parentName:"pre",className:"language-json"},' "exports": {\n ".": "./src/index.js",\n "./FooComponent": "./src/FooComponent.js",\n "./FooComponent.js": "./src/FooComponent.js"\n }\n')),(0,i.mdx)("h4",{id:"subpath-patterns-do-not-permit-expansion"},"Subpath patterns do not permit expansion"),(0,i.mdx)("p",null,"Subpath patterns are a shorthand for mapping multiple subpaths \u2014\xa0they do not permit path expansion (strictly a substring replacement), however will match nested directories (",(0,i.mdx)("a",{parentName:"p",href:"https://github.com/react-native-community/discussions-and-proposals/blob/main/proposals/0534-metro-package-exports-support.md#subpath-patterns"},"see section in RFC"),")."),(0,i.mdx)("p",null,"Only one ",(0,i.mdx)("inlineCode",{parentName:"p"},"*")," is permitted per side of a subpath pattern."),(0,i.mdx)("pre",null,(0,i.mdx)("code",{parentName:"pre",className:"language-json"},' "exports": {\n ".": "./index.js",\n "./utils/*": "./utils/*.js"\n }\n')),(0,i.mdx)("ul",null,(0,i.mdx)("li",{parentName:"ul"},(0,i.mdx)("inlineCode",{parentName:"li"},"'pkg/utils/foo'")," matches ",(0,i.mdx)("inlineCode",{parentName:"li"},"'pkg/utils/foo.js'"),"."),(0,i.mdx)("li",{parentName:"ul"},(0,i.mdx)("inlineCode",{parentName:"li"},"'pkg/utils/foo/bar'")," matches ",(0,i.mdx)("inlineCode",{parentName:"li"},"'pkg/utils/foo/bar.js'"),"."),(0,i.mdx)("li",{parentName:"ul"},(0,i.mdx)("inlineCode",{parentName:"li"},"'pkg/utils/foo'")," ",(0,i.mdx)("strong",{parentName:"li"},"does not match")," ",(0,i.mdx)("inlineCode",{parentName:"li"},"'pkg/utils/foo.bar.js'"),".")),(0,i.mdx)("h3",{id:"replacing-browser-and-react-native-fields"},"Replacing ",(0,i.mdx)("inlineCode",{parentName:"h3"},'"browser"')," and ",(0,i.mdx)("inlineCode",{parentName:"h3"},'"react-native"')," fields"),(0,i.mdx)("p",null,"We've introduced ",(0,i.mdx)("inlineCode",{parentName:"p"},'"react-native"')," as a community condition (for use with conditional exports). This represents React Native, the framework, sitting alongside other recognised runtimes such as ",(0,i.mdx)("inlineCode",{parentName:"p"},'"node"')," and ",(0,i.mdx)("inlineCode",{parentName:"p"},'"deno"')," (",(0,i.mdx)("a",{parentName:"p",href:"https://github.com/nodejs/node/pull/45367"},"RFC"),")."),(0,i.mdx)("blockquote",null,(0,i.mdx)("p",{parentName:"blockquote"},(0,i.mdx)("a",{parentName:"p",href:"https://nodejs.org/docs/latest-v19.x/api/packages.html#community-conditions-definitions"},"Community Conditions Definitions \u2014\xa0",(0,i.mdx)("strong",{parentName:"a"},(0,i.mdx)("inlineCode",{parentName:"strong"},'"react-native"')))),(0,i.mdx)("p",{parentName:"blockquote"},(0,i.mdx)("em",{parentName:"p"},'Will be matched by the React Native framework (all platforms). To target React Native for Web, "browser" should be specified before this condition.'))),(0,i.mdx)("p",null,"This replaces the previous ",(0,i.mdx)("inlineCode",{parentName:"p"},'"react-native"')," root field. The priority order for how this was previously resolved was determined by projects, ",(0,i.mdx)("a",{parentName:"p",href:"https://github.com/expo/router/issues/37#issuecomment-1275925758"},"which created ambiguity when using React Native for Web"),". Under ",(0,i.mdx)("inlineCode",{parentName:"p"},'"exports"'),", ",(0,i.mdx)("em",{parentName:"p"},"packages concretely define the resolution order for conditional entry points")," \u2014\xa0removing this ambiguity."),(0,i.mdx)("h4",{id:"example-use-conditional-exports-to-target-web-and-react-native"},"Example: Use conditional exports to target web and React Native"),(0,i.mdx)("pre",null,(0,i.mdx)("code",{parentName:"pre",className:"language-json"},' "exports": {\n "browser": "./dist/index-browser.js",\n "react-native": "./dist/index-react-native.js",\n "default": "./dist/index.js"\n }\n')),(0,i.mdx)("admonition",{type:"note"},(0,i.mdx)("p",{parentName:"admonition"},"We chose not to introduce ",(0,i.mdx)("inlineCode",{parentName:"p"},'"android"')," and ",(0,i.mdx)("inlineCode",{parentName:"p"},'"ios"')," conditions, due to the prevalence of other existing platform selection methods, and the complexity of how this behavior might work across frameworks. We recommend the ",(0,i.mdx)("a",{parentName:"p",href:"https://reactnative.dev/docs/platform#select"},(0,i.mdx)("inlineCode",{parentName:"a"},"Platform.select()"))," API instead.")),(0,i.mdx)("h3",{id:"replacing-platform-specific-extensions"},"Replacing platform-specific extensions"),(0,i.mdx)("blockquote",null,(0,i.mdx)("p",{parentName:"blockquote"},(0,i.mdx)("strong",{parentName:"p"},"Breaking change"),": Subpaths matched in ",(0,i.mdx)("inlineCode",{parentName:"p"},'"exports"')," (including via ",(0,i.mdx)("a",{parentName:"p",href:"https://nodejs.org/docs/latest-v19.x/api/packages.html#subpath-patterns"},"subpath patterns"),") will use the exact file path specified by a package, and will not attempt to expand ",(0,i.mdx)("inlineCode",{parentName:"p"},"sourceExts")," or platform-specific extensions.")),(0,i.mdx)("h4",{id:"use-platformselect-react-native"},"Use ",(0,i.mdx)("a",{parentName:"h4",href:"https://reactnative.dev/docs/platform#select"},(0,i.mdx)("inlineCode",{parentName:"a"},"Platform.select()"))," (React Native)"),(0,i.mdx)("pre",null,(0,i.mdx)("code",{parentName:"pre",className:"language-json"},' "exports": {\n "./FooComponent": "./src/FooComponent.js"\n }\n')),(0,i.mdx)("pre",null,(0,i.mdx)("code",{parentName:"pre",className:"language-js"},"// src/FooComponent.js\n\nconst FooComponent = Platform.select({\n android: require('./FooComponentAndroid.js'),\n ios: require('FooComponentIOS.js'),\n});\n\nexport default FooComponent;\n")),(0,i.mdx)("h3",{id:"asset-files"},"Asset files"),(0,i.mdx)("p",null,"As with source files, assets must be listed in ",(0,i.mdx)("inlineCode",{parentName:"p"},'"exports"')," to be imported without warnings. Asset files with ",(0,i.mdx)("a",{parentName:"p",href:"/docs/configuration#assetresolutions"},"multiple densities"),", e.g. ",(0,i.mdx)("inlineCode",{parentName:"p"},"icon.png")," and ",(0,i.mdx)("inlineCode",{parentName:"p"},"icon@2x.png"),", will continue to work without being listed individually."),(0,i.mdx)("p",null,"Using subpath patterns can be a convenient method to export many assets. We recommend specifying asset subpaths ",(0,i.mdx)("strong",{parentName:"p"},"with their file extension"),"."),(0,i.mdx)("pre",null,(0,i.mdx)("code",{parentName:"pre",className:"language-json"},'{\n "exports": {\n "./assets/*.png": "./dist/assets/*.png"\n }\n}\n')))}x.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/ae2a35ac.ec9f76b2.js b/assets/js/ae2a35ac.ec9f76b2.js deleted file mode 100644 index 82af78534e..0000000000 --- a/assets/js/ae2a35ac.ec9f76b2.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkmetro_website=self.webpackChunkmetro_website||[]).push([[708],{3905:(e,t,n)=>{n.r(t),n.d(t,{MDXContext:()=>l,MDXProvider:()=>c,mdx:()=>h,useMDXComponents:()=>m,withMDXComponents:()=>d});var a=n(67294);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function i(){return i=Object.assign||function(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var l=a.createContext({}),d=function(e){return function(t){var n=m(t.components);return a.createElement(e,i({},t,{components:n}))}},m=function(e){var t=a.useContext(l),n=t;return e&&(n="function"==typeof e?e(t):s(s({},t),e)),n},c=function(e){var t=m(e.components);return a.createElement(l.Provider,{value:t},e.children)},u="mdxType",x={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},g=a.forwardRef((function(e,t){var n=e.components,o=e.mdxType,i=e.originalType,r=e.parentName,l=p(e,["components","mdxType","originalType","parentName"]),d=m(n),c=o,u=d["".concat(r,".").concat(c)]||d[c]||x[c]||i;return n?a.createElement(u,s(s({ref:t},l),{},{components:n})):a.createElement(u,s({ref:t},l))}));function h(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var i=n.length,r=new Array(i);r[0]=g;var s={};for(var p in t)hasOwnProperty.call(t,p)&&(s[p]=t[p]);s.originalType=e,s[u]="string"==typeof e?e:o,r[1]=s;for(var l=2;l{n.r(t),n.d(t,{assets:()=>d,contentTitle:()=>p,default:()=>x,frontMatter:()=>s,metadata:()=>l,toc:()=>m});var a=n(87462),o=n(63366),i=(n(67294),n(3905)),r=["components"],s={id:"package-exports",title:"Package Exports Support (Experimental)"},p=void 0,l={unversionedId:"package-exports",id:"package-exports",title:"Package Exports Support (Experimental)",description:"Background",source:"@site/../docs/PackageExports.md",sourceDirName:".",slug:"/package-exports",permalink:"/docs/package-exports",draft:!1,editUrl:"https://github.com/facebook/metro/edit/main/docs/../docs/PackageExports.md",tags:[],version:"current",lastUpdatedAt:1725545477,formattedLastUpdatedAt:"Sep 5, 2024",frontMatter:{id:"package-exports",title:"Package Exports Support (Experimental)"},sidebar:"docs",previous:{title:"Metro CLI Options",permalink:"/docs/cli"},next:{title:"Troubleshooting",permalink:"/docs/troubleshooting"}},d={},m=[{value:"Background",id:"background",level:2},{value:"Configuration options",id:"configuration-options",level:2},{value:"Summary of breaking changes",id:"summary-of-breaking-changes",level:2},{value:"Breaking: Match "exports" first, then fall back to legacy resolution",id:"breaking-match-exports-first-then-fall-back-to-legacy-resolution",level:3},{value:"Example",id:"example",level:4},{value:"Breaking: Import specifiers are matched exactly",id:"breaking-import-specifiers-are-matched-exactly",level:3},{value:"Example",id:"example-1",level:4},{value:"Package encapsulation is lenient",id:"package-encapsulation-is-lenient",level:3},{value:"Migration guide for package maintainers",id:"migration-guide-for-package-maintainers",level:2},{value:"Recommended: Introducing "exports" is a breaking change",id:"recommended-introducing-exports-is-a-breaking-change",level:3},{value:"Package subpaths",id:"package-subpaths",level:3},{value:"File extensions are important!",id:"file-extensions-are-important",level:4},{value:"Subpath patterns do not permit expansion",id:"subpath-patterns-do-not-permit-expansion",level:4},{value:"Replacing "browser" and "react-native" fields",id:"replacing-browser-and-react-native-fields",level:3},{value:"Example: Use conditional exports to target web and React Native",id:"example-use-conditional-exports-to-target-web-and-react-native",level:4},{value:"Replacing platform-specific extensions",id:"replacing-platform-specific-extensions",level:3},{value:"Use Platform.select() (React Native)",id:"use-platformselect-react-native",level:4},{value:"Asset files",id:"asset-files",level:3}],c={toc:m},u="wrapper";function x(e){var t=e.components,n=(0,o.Z)(e,r);return(0,i.mdx)(u,(0,a.Z)({},c,n,{components:t,mdxType:"MDXLayout"}),(0,i.mdx)("h2",{id:"background"},"Background"),(0,i.mdx)("p",null,"Introduced in Node.js 12.7.0, Package Exports is a modern approach for npm packages to specify ",(0,i.mdx)("strong",{parentName:"p"},"entry points")," \u2014 the mapping of package subpaths which can be externally imported and which file(s) they should resolve to."),(0,i.mdx)("p",null,"When Package Exports support is enabled via ",(0,i.mdx)("a",{parentName:"p",href:"/docs/configuration/#unstable_enablepackageexports-experimental"},(0,i.mdx)("inlineCode",{parentName:"a"},"resolver.unstable_enablePackageExports")),", Metro's ",(0,i.mdx)("a",{parentName:"p",href:"/docs/resolution#algorithm"},"module resolution algorithm")," will consider the ",(0,i.mdx)("inlineCode",{parentName:"p"},'"exports"')," field in ",(0,i.mdx)("inlineCode",{parentName:"p"},"package.json")," files."),(0,i.mdx)("ul",null,(0,i.mdx)("li",{parentName:"ul"},(0,i.mdx)("a",{parentName:"li",href:"https://nodejs.org/docs/latest-v19.x/api/packages.html#package-entry-points"},"Node.js spec")),(0,i.mdx)("li",{parentName:"ul"},(0,i.mdx)("a",{parentName:"li",href:"https://github.com/react-native-community/discussions-and-proposals/blob/main/proposals/0534-metro-package-exports-support.md"},"RFC for Package Exports in Metro")),(0,i.mdx)("li",{parentName:"ul"},"React Native announcement post (coming soon!)")),(0,i.mdx)("h2",{id:"configuration-options"},"Configuration options"),(0,i.mdx)("table",null,(0,i.mdx)("thead",{parentName:"table"},(0,i.mdx)("tr",{parentName:"thead"},(0,i.mdx)("th",{parentName:"tr",align:null},"Option"),(0,i.mdx)("th",{parentName:"tr",align:null},"Description"))),(0,i.mdx)("tbody",{parentName:"table"},(0,i.mdx)("tr",{parentName:"tbody"},(0,i.mdx)("td",{parentName:"tr",align:null},(0,i.mdx)("a",{parentName:"td",href:"/docs/configuration/#unstable_enablepackageexports-experimental"},(0,i.mdx)("inlineCode",{parentName:"a"},"resolver.unstable_enablePackageExports"))),(0,i.mdx)("td",{parentName:"tr",align:null},"Enable Package Exports support.")),(0,i.mdx)("tr",{parentName:"tbody"},(0,i.mdx)("td",{parentName:"tr",align:null},(0,i.mdx)("a",{parentName:"td",href:"/docs/configuration/#unstable_conditionnames-experimental"},(0,i.mdx)("inlineCode",{parentName:"a"},"resolver.unstable_conditionNames"))),(0,i.mdx)("td",{parentName:"tr",align:null},"The set of condition names to assert when resolving conditional exports.")),(0,i.mdx)("tr",{parentName:"tbody"},(0,i.mdx)("td",{parentName:"tr",align:null},(0,i.mdx)("a",{parentName:"td",href:"/docs/configuration/#unstable_conditionsbyplatform-experimental"},(0,i.mdx)("inlineCode",{parentName:"a"},"resolver.unstable_conditionsByPlatform"))),(0,i.mdx)("td",{parentName:"tr",align:null},"The additional condition names to assert when resolving for a given platform target.")))),(0,i.mdx)("h2",{id:"summary-of-breaking-changes"},"Summary of breaking changes"),(0,i.mdx)("admonition",{type:"info"},(0,i.mdx)("p",{parentName:"admonition"},(0,i.mdx)("strong",{parentName:"p"},"Package Exports resolution is available since Metro 0.76.1 and is disabled by default"),". We will provide the option to disable it for a long time yet, and have no plans to remove existing non-",(0,i.mdx)("inlineCode",{parentName:"p"},'"exports"')," resolution behaviour.")),(0,i.mdx)("p",null,"Since Package Exports features overlap with existing React Native concepts (such as ",(0,i.mdx)("a",{parentName:"p",href:"https://reactnative.dev/docs/platform-specific-code"},"platform-specific extensions"),"), and since ",(0,i.mdx)("inlineCode",{parentName:"p"},'"exports"')," had been live in the npm ecosystem for some time, we reached out to the React Native community to make sure our implementation would meet developers' needs (",(0,i.mdx)("a",{parentName:"p",href:"https://github.com/react-native-community/discussions-and-proposals/pull/534"},"PR"),", ",(0,i.mdx)("a",{parentName:"p",href:"https://github.com/react-native-community/discussions-and-proposals/blob/main/proposals/0534-metro-package-exports-support.md"},"final RFC"),")."),(0,i.mdx)("p",null,"This led us to create an implementation of Package Exports in Metro that is spec-compliant (necessitating some breaking changes), but backwards compatible otherwise (helping apps with existing imports to migrate gradually)."),(0,i.mdx)("h3",{id:"breaking-match-exports-first-then-fall-back-to-legacy-resolution"},"Breaking: Match ",(0,i.mdx)("inlineCode",{parentName:"h3"},'"exports"')," first, then fall back to legacy resolution"),(0,i.mdx)("p",null,"If present in a ",(0,i.mdx)("inlineCode",{parentName:"p"},"package.json")," file, ",(0,i.mdx)("inlineCode",{parentName:"p"},'"exports"')," will be the first field consulted when resolving a package."),(0,i.mdx)("ul",null,(0,i.mdx)("li",{parentName:"ul"},(0,i.mdx)("inlineCode",{parentName:"li"},'"exports"')," will be used instead of any existing ",(0,i.mdx)("inlineCode",{parentName:"li"},'"react-native"'),", ",(0,i.mdx)("inlineCode",{parentName:"li"},'"browser"'),", or ",(0,i.mdx)("inlineCode",{parentName:"li"},'"main"')," field \u2014\xa0or a file on disk at the same subpath (edge case)."),(0,i.mdx)("li",{parentName:"ul"},(0,i.mdx)("strong",{parentName:"li"},"Fallback"),": If the requested subpath is not matched in ",(0,i.mdx)("inlineCode",{parentName:"li"},'"exports"'),", Metro will try to resolve it again, considering the above fields.")),(0,i.mdx)("p",null,"Subpaths matched in ",(0,i.mdx)("inlineCode",{parentName:"p"},'"exports"')," (including via ",(0,i.mdx)("a",{parentName:"p",href:"https://nodejs.org/docs/latest-v19.x/api/packages.html#subpath-patterns"},"subpath patterns"),") will use the exact target file path specified by a package."),(0,i.mdx)("ul",null,(0,i.mdx)("li",{parentName:"ul"},"Metro will not expand ",(0,i.mdx)("a",{parentName:"li",href:"/docs/configuration/#sourceexts"},(0,i.mdx)("inlineCode",{parentName:"a"},"sourceExts"))," against the import specifier."),(0,i.mdx)("li",{parentName:"ul"},"Metro will not resolve ",(0,i.mdx)("a",{parentName:"li",href:"https://reactnative.dev/docs/platform-specific-code"},"platform-specific extensions")," against the target file."),(0,i.mdx)("li",{parentName:"ul"},(0,i.mdx)("strong",{parentName:"li"},"Unchanged"),": Metro will expand ",(0,i.mdx)("a",{parentName:"li",href:"/docs/configuration#assetresolutions"},"asset densities")," (e.g. ",(0,i.mdx)("inlineCode",{parentName:"li"},"icon.png")," \u2192 ",(0,i.mdx)("inlineCode",{parentName:"li"},"icon@2x.png"),") if the target file ",(0,i.mdx)("a",{parentName:"li",href:"/docs/configuration/#assetexts"},"is an asset"),".")),(0,i.mdx)("h4",{id:"example"},"Example"),(0,i.mdx)("p",null,"For a package without an ",(0,i.mdx)("inlineCode",{parentName:"p"},'"exports"')," field, Metro tries multiple potential file locations based on the import specifier:"),(0,i.mdx)("pre",null,(0,i.mdx)("code",{parentName:"pre",className:"language-js"},"import FooComponent from 'some-pkg/FooComponent';\n// Tries .[platform].js, .native.js, .js (+ TypeScript variants)\n")),(0,i.mdx)("p",null,"However, if ",(0,i.mdx)("inlineCode",{parentName:"p"},'"./FooComponent"')," is listed in ",(0,i.mdx)("inlineCode",{parentName:"p"},'"exports"'),", Metro matches the import specifier to this subpath, and uses the target file specified by the package with no further rules:"),(0,i.mdx)("pre",null,(0,i.mdx)("code",{parentName:"pre",className:"language-js"},"import FooComponent from 'some-pkg/FooComponent';\n// Resolves exact target from \"exports\" only\n")),(0,i.mdx)("admonition",{type:"note"},(0,i.mdx)("p",{parentName:"admonition"},"We have no plans to drop platform-specific extensions for packages not using ",(0,i.mdx)("inlineCode",{parentName:"p"},'"exports"'),", or in app code.")),(0,i.mdx)("h3",{id:"breaking-import-specifiers-are-matched-exactly"},"Breaking: Import specifiers are matched exactly"),(0,i.mdx)("p",null,"Previously, import specifiers (the string given to ",(0,i.mdx)("inlineCode",{parentName:"p"},"import")," or ",(0,i.mdx)("inlineCode",{parentName:"p"},"require()"),") could be defined using both extensioned or extensionless paths. This is no longer the case for subpath keys in the ",(0,i.mdx)("inlineCode",{parentName:"p"},'"exports"')," field."),(0,i.mdx)("h4",{id:"example-1"},"Example"),(0,i.mdx)("pre",null,(0,i.mdx)("code",{parentName:"pre",className:"language-json"},'{\n "name": "some-pkg",\n "exports": {\n "./FooComponent": "./src/FooComponent.js"\n }\n}\n')),(0,i.mdx)("pre",null,(0,i.mdx)("code",{parentName:"pre",className:"language-js"},'import FooComponent from \'some-pkg/FooComponent.js\';\n// Inaccessible unless the package had also listed "./FooComponent.js"\n// as an "exports" key\n')),(0,i.mdx)("p",null,"Note that this behaviour also applies for subpath patterns: ",(0,i.mdx)("inlineCode",{parentName:"p"},'"./*": "./src/*.js"')," is distinct from ",(0,i.mdx)("inlineCode",{parentName:"p"},'"./*.js": "./src/*.js"'),"."),(0,i.mdx)("h3",{id:"package-encapsulation-is-lenient"},"Package encapsulation is lenient"),(0,i.mdx)("p",null,"In Node.js, it is an error to import package subpaths that aren't explicitly listed in ",(0,i.mdx)("inlineCode",{parentName:"p"},'"exports"'),". In Metro, we've decided to handle these errors leniently and resolve modules following the old behavior as necessary. This is intended to reduce user friction for previously allowed imports in existing Metro projects."),(0,i.mdx)("p",null,"Instead of throwing an error, Metro will log a warning and fall back to file-based resolution."),(0,i.mdx)("pre",null,(0,i.mdx)("code",{parentName:"pre",className:"language-sh"},'warn: You have imported the module "foo/private/fn.js" which is not listed in\nthe "exports" of "foo". Consider updating your call site or asking the package\nmaintainer(s) to expose this API.\n')),(0,i.mdx)("admonition",{type:"note"},(0,i.mdx)("p",{parentName:"admonition"},"We plan to implement a strict mode for package encapsulation in future, to align with Node's default behavior. ",(0,i.mdx)("strong",{parentName:"p"},"We recommend that all developers fix encapsulation warnings in their code"),".")),(0,i.mdx)("h2",{id:"migration-guide-for-package-maintainers"},"Migration guide for package maintainers"),(0,i.mdx)("p",null,(0,i.mdx)("strong",{parentName:"p"},"Adding an ",(0,i.mdx)("inlineCode",{parentName:"strong"},'"exports"')," field to your package is entirely optional"),". Existing package resolution features will behave identically for packages which don't use ",(0,i.mdx)("inlineCode",{parentName:"p"},'"exports"')," \u2014 and we have no plans to remove this behaviour."),(0,i.mdx)("h3",{id:"recommended-introducing-exports-is-a-breaking-change"},"Recommended: Introducing ",(0,i.mdx)("inlineCode",{parentName:"h3"},'"exports"')," is a breaking change"),(0,i.mdx)("p",null,"The Node.js spec gives guidance on migrating to ",(0,i.mdx)("inlineCode",{parentName:"p"},'"exports"')," in a non-breaking manner, however this is challenging in practice. For instance, if your React Native package uses ",(0,i.mdx)("a",{parentName:"p",href:"https://reactnative.dev/docs/platform-specific-code"},"platform-specific extensions")," on its public exports, this is a breaking change by default."),(0,i.mdx)("blockquote",null,(0,i.mdx)("p",{parentName:"blockquote"},"To make the introduction of ",(0,i.mdx)("inlineCode",{parentName:"p"},'"exports"')," non-breaking, ensure that every previously supported entry point is exported. It is best to explicitly specify entry points so that the package's public API is well-defined."),(0,i.mdx)("p",{parentName:"blockquote"},"\u2014\xa0",(0,i.mdx)("a",{parentName:"p",href:"https://nodejs.org/docs/latest-v19.x/api/packages.html#package-entry-points"},"https://nodejs.org/docs/latest-v19.x/api/packages.html#package-entry-points"))),(0,i.mdx)("h3",{id:"package-subpaths"},"Package subpaths"),(0,i.mdx)("admonition",{type:"caution"},(0,i.mdx)("p",{parentName:"admonition"},(0,i.mdx)("strong",{parentName:"p"},"Please do not rely on ",(0,i.mdx)("a",{parentName:"strong",href:"#package-encapsulation-is-lenient"},"lenient package encapsulation")," under Metro.")," While Metro does this for backwards compatibility, packages should follow how ",(0,i.mdx)("inlineCode",{parentName:"p"},'"exports"')," is documented in the spec and strictly implemented by other tools.")),(0,i.mdx)("h4",{id:"file-extensions-are-important"},"File extensions are important!"),(0,i.mdx)("p",null,"Each subpath is an exact specifier (",(0,i.mdx)("a",{parentName:"p",href:"https://github.com/react-native-community/discussions-and-proposals/blob/main/proposals/0534-metro-package-exports-support.md#exact-path-specifiers"},"see section in RFC"),")."),(0,i.mdx)("p",null,"We recommend continuing to use ",(0,i.mdx)("strong",{parentName:"p"},"extensionless specifiers")," for subpaths in packages targeting React Native \u2014\xa0or ",(0,i.mdx)("strong",{parentName:"p"},"defining both extensioned and extensionless specifiers"),". This will match matching existing user expectations."),(0,i.mdx)("pre",null,(0,i.mdx)("code",{parentName:"pre",className:"language-json"},' "exports": {\n ".": "./src/index.js",\n "./FooComponent": "./src/FooComponent.js",\n "./FooComponent.js": "./src/FooComponent.js"\n }\n')),(0,i.mdx)("h4",{id:"subpath-patterns-do-not-permit-expansion"},"Subpath patterns do not permit expansion"),(0,i.mdx)("p",null,"Subpath patterns are a shorthand for mapping multiple subpaths \u2014\xa0they do not permit path expansion (strictly a substring replacement), however will match nested directories (",(0,i.mdx)("a",{parentName:"p",href:"https://github.com/react-native-community/discussions-and-proposals/blob/main/proposals/0534-metro-package-exports-support.md#subpath-patterns"},"see section in RFC"),")."),(0,i.mdx)("p",null,"Only one ",(0,i.mdx)("inlineCode",{parentName:"p"},"*")," is permitted per side of a subpath pattern."),(0,i.mdx)("pre",null,(0,i.mdx)("code",{parentName:"pre",className:"language-json"},' "exports": {\n ".": "./index.js",\n "./utils/*": "./utils/*.js"\n }\n')),(0,i.mdx)("ul",null,(0,i.mdx)("li",{parentName:"ul"},(0,i.mdx)("inlineCode",{parentName:"li"},"'pkg/utils/foo'")," matches ",(0,i.mdx)("inlineCode",{parentName:"li"},"'pkg/utils/foo.js'"),"."),(0,i.mdx)("li",{parentName:"ul"},(0,i.mdx)("inlineCode",{parentName:"li"},"'pkg/utils/foo/bar'")," matches ",(0,i.mdx)("inlineCode",{parentName:"li"},"'pkg/utils/foo/bar.js'"),"."),(0,i.mdx)("li",{parentName:"ul"},(0,i.mdx)("inlineCode",{parentName:"li"},"'pkg/utils/foo'")," ",(0,i.mdx)("strong",{parentName:"li"},"does not match")," ",(0,i.mdx)("inlineCode",{parentName:"li"},"'pkg/utils/foo.bar.js'"),".")),(0,i.mdx)("h3",{id:"replacing-browser-and-react-native-fields"},"Replacing ",(0,i.mdx)("inlineCode",{parentName:"h3"},'"browser"')," and ",(0,i.mdx)("inlineCode",{parentName:"h3"},'"react-native"')," fields"),(0,i.mdx)("p",null,"We've introduced ",(0,i.mdx)("inlineCode",{parentName:"p"},'"react-native"')," as a community condition (for use with conditional exports). This represents React Native, the framework, sitting alongside other recognised runtimes such as ",(0,i.mdx)("inlineCode",{parentName:"p"},'"node"')," and ",(0,i.mdx)("inlineCode",{parentName:"p"},'"deno"')," (",(0,i.mdx)("a",{parentName:"p",href:"https://github.com/nodejs/node/pull/45367"},"RFC"),")."),(0,i.mdx)("blockquote",null,(0,i.mdx)("p",{parentName:"blockquote"},(0,i.mdx)("a",{parentName:"p",href:"https://nodejs.org/docs/latest-v19.x/api/packages.html#community-conditions-definitions"},"Community Conditions Definitions \u2014\xa0",(0,i.mdx)("strong",{parentName:"a"},(0,i.mdx)("inlineCode",{parentName:"strong"},'"react-native"')))),(0,i.mdx)("p",{parentName:"blockquote"},(0,i.mdx)("em",{parentName:"p"},'Will be matched by the React Native framework (all platforms). To target React Native for Web, "browser" should be specified before this condition.'))),(0,i.mdx)("p",null,"This replaces the previous ",(0,i.mdx)("inlineCode",{parentName:"p"},'"react-native"')," root field. The priority order for how this was previously resolved was determined by projects, ",(0,i.mdx)("a",{parentName:"p",href:"https://github.com/expo/router/issues/37#issuecomment-1275925758"},"which created ambiguity when using React Native for Web"),". Under ",(0,i.mdx)("inlineCode",{parentName:"p"},'"exports"'),", ",(0,i.mdx)("em",{parentName:"p"},"packages concretely define the resolution order for conditional entry points")," \u2014\xa0removing this ambiguity."),(0,i.mdx)("h4",{id:"example-use-conditional-exports-to-target-web-and-react-native"},"Example: Use conditional exports to target web and React Native"),(0,i.mdx)("pre",null,(0,i.mdx)("code",{parentName:"pre",className:"language-json"},' "exports": {\n "browser": "./dist/index-browser.js",\n "react-native": "./dist/index-react-native.js",\n "default": "./dist/index.js"\n }\n')),(0,i.mdx)("admonition",{type:"note"},(0,i.mdx)("p",{parentName:"admonition"},"We chose not to introduce ",(0,i.mdx)("inlineCode",{parentName:"p"},'"android"')," and ",(0,i.mdx)("inlineCode",{parentName:"p"},'"ios"')," conditions, due to the prevalence of other existing platform selection methods, and the complexity of how this behavior might work across frameworks. We recommend the ",(0,i.mdx)("a",{parentName:"p",href:"https://reactnative.dev/docs/platform#select"},(0,i.mdx)("inlineCode",{parentName:"a"},"Platform.select()"))," API instead.")),(0,i.mdx)("h3",{id:"replacing-platform-specific-extensions"},"Replacing platform-specific extensions"),(0,i.mdx)("blockquote",null,(0,i.mdx)("p",{parentName:"blockquote"},(0,i.mdx)("strong",{parentName:"p"},"Breaking change"),": Subpaths matched in ",(0,i.mdx)("inlineCode",{parentName:"p"},'"exports"')," (including via ",(0,i.mdx)("a",{parentName:"p",href:"https://nodejs.org/docs/latest-v19.x/api/packages.html#subpath-patterns"},"subpath patterns"),") will use the exact file path specified by a package, and will not attempt to expand ",(0,i.mdx)("inlineCode",{parentName:"p"},"sourceExts")," or platform-specific extensions.")),(0,i.mdx)("h4",{id:"use-platformselect-react-native"},"Use ",(0,i.mdx)("a",{parentName:"h4",href:"https://reactnative.dev/docs/platform#select"},(0,i.mdx)("inlineCode",{parentName:"a"},"Platform.select()"))," (React Native)"),(0,i.mdx)("pre",null,(0,i.mdx)("code",{parentName:"pre",className:"language-json"},' "exports": {\n "./FooComponent": "./src/FooComponent.js"\n }\n')),(0,i.mdx)("pre",null,(0,i.mdx)("code",{parentName:"pre",className:"language-js"},"// src/FooComponent.js\n\nconst FooComponent = Platform.select({\n android: require('./FooComponentAndroid.js'),\n ios: require('FooComponentIOS.js'),\n});\n\nexport default FooComponent;\n")),(0,i.mdx)("h3",{id:"asset-files"},"Asset files"),(0,i.mdx)("p",null,"As with source files, assets must be listed in ",(0,i.mdx)("inlineCode",{parentName:"p"},'"exports"')," to be imported without warnings. Asset files with ",(0,i.mdx)("a",{parentName:"p",href:"/docs/configuration#assetresolutions"},"multiple densities"),", e.g. ",(0,i.mdx)("inlineCode",{parentName:"p"},"icon.png")," and ",(0,i.mdx)("inlineCode",{parentName:"p"},"icon@2x.png"),", will continue to work without being listed individually."),(0,i.mdx)("p",null,"Using subpath patterns can be a convenient method to export many assets. We recommend specifying asset subpaths ",(0,i.mdx)("strong",{parentName:"p"},"with their file extension"),"."),(0,i.mdx)("pre",null,(0,i.mdx)("code",{parentName:"pre",className:"language-json"},'{\n "exports": {\n "./assets/*.png": "./dist/assets/*.png"\n }\n}\n')))}x.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/ba2a2799.68dc6d81.js b/assets/js/ba2a2799.68dc6d81.js new file mode 100644 index 0000000000..6b45d1b548 --- /dev/null +++ b/assets/js/ba2a2799.68dc6d81.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkmetro_website=self.webpackChunkmetro_website||[]).push([[122],{3905:(e,n,t)=>{t.r(n),t.d(n,{MDXContext:()=>m,MDXProvider:()=>c,mdx:()=>f,useMDXComponents:()=>p,withMDXComponents:()=>s});var a=t(67294);function o(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function r(){return r=Object.assign||function(e){for(var n=1;n=0||(o[t]=e[t]);return o}(e,n);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(o[t]=e[t])}return o}var m=a.createContext({}),s=function(e){return function(n){var t=p(n.components);return a.createElement(e,r({},n,{components:t}))}},p=function(e){var n=a.useContext(m),t=n;return e&&(t="function"==typeof e?e(n):l(l({},n),e)),t},c=function(e){var n=p(e.components);return a.createElement(m.Provider,{value:n},e.children)},u="mdxType",h={inlineCode:"code",wrapper:function(e){var n=e.children;return a.createElement(a.Fragment,{},n)}},x=a.forwardRef((function(e,n){var t=e.components,o=e.mdxType,r=e.originalType,i=e.parentName,m=d(e,["components","mdxType","originalType","parentName"]),s=p(t),c=o,u=s["".concat(i,".").concat(c)]||s[c]||h[c]||r;return t?a.createElement(u,l(l({ref:n},m),{},{components:t})):a.createElement(u,l({ref:n},m))}));function f(e,n){var t=arguments,o=n&&n.mdxType;if("string"==typeof e||o){var r=t.length,i=new Array(r);i[0]=x;var l={};for(var d in n)hasOwnProperty.call(n,d)&&(l[d]=n[d]);l.originalType=e,l[u]="string"==typeof e?e:o,i[1]=l;for(var m=2;m{t.r(n),t.d(n,{assets:()=>s,contentTitle:()=>d,default:()=>h,frontMatter:()=>l,metadata:()=>m,toc:()=>p});var a=t(87462),o=t(63366),r=(t(67294),t(3905)),i=["components"],l={id:"configuration",title:"Configuring Metro"},d=void 0,m={unversionedId:"configuration",id:"configuration",title:"Configuring Metro",description:"A Metro config can be created in these three ways (ordered by priority):",source:"@site/../docs/Configuration.md",sourceDirName:".",slug:"/configuration",permalink:"/docs/configuration",draft:!1,editUrl:"https://github.com/facebook/metro/edit/main/docs/../docs/Configuration.md",tags:[],version:"current",lastUpdatedAt:1726493799,formattedLastUpdatedAt:"Sep 16, 2024",frontMatter:{id:"configuration",title:"Configuring Metro"},sidebar:"docs",previous:{title:"Module API",permalink:"/docs/module-api"},next:{title:"Metro CLI Options",permalink:"/docs/cli"}},s={},p=[{value:"Configuration Structure",id:"configuration-structure",level:2},{value:"General Options",id:"general-options",level:3},{value:"cacheStores",id:"cachestores",level:4},{value:"cacheVersion",id:"cacheversion",level:4},{value:"projectRoot",id:"projectroot",level:4},{value:"watchFolders",id:"watchfolders",level:4},{value:"transformerPath",id:"transformerpath",level:4},{value:"reporter",id:"reporter",level:4},{value:"resetCache",id:"resetcache",level:4},{value:"stickyWorkers",id:"stickyworkers",level:4},{value:"maxWorkers",id:"maxworkers",level:4},{value:"fileMapCacheDirectory",id:"filemapcachedirectory",level:4},{value:'hasteMapCacheDirectory
Deprecated
',id:"hastemapcachedirectory-deprecated",level:4},{value:"unstable_perfLoggerFactory",id:"unstable_perfloggerfactory",level:4},{value:"Resolver Options",id:"resolver-options",level:3},{value:"assetExts",id:"assetexts",level:4},{value:"sourceExts",id:"sourceexts",level:4},{value:"resolverMainFields",id:"resolvermainfields",level:4},{value:"disableHierarchicalLookup",id:"disablehierarchicallookup",level:4},{value:"emptyModulePath",id:"emptymodulepath",level:4},{value:"enableGlobalPackages",id:"enableglobalpackages",level:4},{value:"extraNodeModules",id:"extranodemodules",level:4},{value:"nodeModulesPaths",id:"nodemodulespaths",level:4},{value:"resolveRequest",id:"resolverequest",level:4},{value:"useWatchman",id:"usewatchman",level:4},{value:"blockList",id:"blocklist",level:4},{value:"hasteImplModulePath",id:"hasteimplmodulepath",level:4},{value:"platforms",id:"platforms",level:4},{value:"requireCycleIgnorePatterns",id:"requirecycleignorepatterns",level:4},{value:'unstable_conditionNames
Experimental
',id:"unstable_conditionnames-experimental",level:4},{value:'unstable_conditionsByPlatform
Experimental
',id:"unstable_conditionsbyplatform-experimental",level:4},{value:'unstable_enablePackageExports
Experimental
',id:"unstable_enablepackageexports-experimental",level:4},{value:"Transformer Options",id:"transformer-options",level:3},{value:'asyncRequireModulePath
Deprecated
',id:"asyncrequiremodulepath-deprecated",level:4},{value:"dynamicDepsInPackages",id:"dynamicdepsinpackages",level:4},{value:"getTransformOptions",id:"gettransformoptions",level:4},{value:"minifierPath",id:"minifierpath",level:4},{value:"minifierConfig",id:"minifierconfig",level:4},{value:"optimizationSizeLimit",id:"optimizationsizelimit",level:4},{value:"React Native Only",id:"react-native-only",level:4},{value:"assetPlugins",id:"assetplugins",level:4},{value:"assetRegistryPath",id:"assetregistrypath",level:4},{value:"Babel-specific transformer options",id:"babel-specific-transformer-options",level:3},{value:"babelTransformerPath",id:"babeltransformerpath",level:4},{value:"enableBabelRCLookup",id:"enablebabelrclookup",level:4},{value:"enableBabelRuntime",id:"enablebabelruntime",level:4},{value:"hermesParser",id:"hermesparser",level:4},{value:"Serializer Options",id:"serializer-options",level:3},{value:"getRunModuleStatement",id:"getrunmodulestatement",level:4},{value:"createModuleIdFactory",id:"createmoduleidfactory",level:4},{value:"getPolyfills",id:"getpolyfills",level:4},{value:"getModulesRunBeforeMainModule",id:"getmodulesrunbeforemainmodule",level:4},{value:"processModuleFilter",id:"processmodulefilter",level:4},{value:"isThirdPartyModule",id:"isthirdpartymodule",level:4},{value:"Server Options",id:"server-options",level:3},{value:"port",id:"port",level:4},{value:"useGlobalHotkey",id:"useglobalhotkey",level:4},{value:'enhanceMiddleware
Deprecated
',id:"enhancemiddleware-deprecated",level:4},{value:"rewriteRequestUrl",id:"rewriterequesturl",level:4},{value:"forwardClientLogs",id:"forwardclientlogs",level:4},{value:"Watcher Options",id:"watcher-options",level:3},{value:"additionalExts",id:"additionalexts",level:4},{value:"healthCheck.enabled",id:"healthcheckenabled",level:4},{value:"healthCheck.filePrefix",id:"healthcheckfileprefix",level:4},{value:"healthCheck.interval",id:"healthcheckinterval",level:4},{value:"healthCheck.timeout",id:"healthchecktimeout",level:4},{value:"watchman.deferStates",id:"watchmandeferstates",level:4},{value:"Merging Configurations",id:"merging-configurations",level:2},{value:"Merging Example",id:"merging-example",level:4}],c={toc:p},u="wrapper";function h(e){var n=e.components,t=(0,o.Z)(e,i);return(0,r.mdx)(u,(0,a.Z)({},c,t,{components:n,mdxType:"MDXLayout"}),(0,r.mdx)("p",null,"A Metro config can be created in these three ways (ordered by priority):"),(0,r.mdx)("ol",null,(0,r.mdx)("li",{parentName:"ol"},(0,r.mdx)("inlineCode",{parentName:"li"},"metro.config.js")),(0,r.mdx)("li",{parentName:"ol"},(0,r.mdx)("inlineCode",{parentName:"li"},"metro.config.json")),(0,r.mdx)("li",{parentName:"ol"},"The ",(0,r.mdx)("inlineCode",{parentName:"li"},"metro")," field in ",(0,r.mdx)("inlineCode",{parentName:"li"},"package.json"))),(0,r.mdx)("p",null,"You can also give a custom file to the configuration by specifying ",(0,r.mdx)("inlineCode",{parentName:"p"},"--config ")," when calling the CLI."),(0,r.mdx)("admonition",{type:"note"},(0,r.mdx)("p",{parentName:"admonition"},"When Metro is started via the React Native CLI, some defaults are different from those mentioned below.\nSee the ",(0,r.mdx)("a",{parentName:"p",href:"https://github.com/facebook/react-native/blob/main/packages/community-cli-plugin/src/utils/loadMetroConfig.js"},"React Native repository")," for details.")),(0,r.mdx)("h2",{id:"configuration-structure"},"Configuration Structure"),(0,r.mdx)("p",null,"The configuration is based on ",(0,r.mdx)("a",{parentName:"p",href:"/docs/concepts"},"our concepts"),", which means that for every module we have a separate config option. A common configuration structure in Metro looks like this:"),(0,r.mdx)("pre",null,(0,r.mdx)("code",{parentName:"pre",className:"language-js"},"module.exports = {\n /* general options */\n\n resolver: {\n /* resolver options */\n },\n transformer: {\n /* transformer options */\n },\n serializer: {\n /* serializer options */\n },\n server: {\n /* server options */\n },\n watcher: {\n /* watcher options */\n watchman: {\n /* Watchman-specific options */\n }\n }\n};\n")),(0,r.mdx)("h3",{id:"general-options"},"General Options"),(0,r.mdx)("h4",{id:"cachestores"},(0,r.mdx)("inlineCode",{parentName:"h4"},"cacheStores")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"CacheStores")," (see details below)"),(0,r.mdx)("p",null,"A list of storage adapters for Metro's ",(0,r.mdx)("a",{parentName:"p",href:"/docs/caching"},"transformer cache"),". This can be any combination of ",(0,r.mdx)("a",{parentName:"p",href:"/docs/caching#built-in-cache-stores"},"built-in cache stores")," and ",(0,r.mdx)("a",{parentName:"p",href:"/docs/caching#custom-cache-stores"},"custom cache stores"),". Defaults to using a temporary directory on disk as the only cache store."),(0,r.mdx)("p",null,"When Metro needs to transform a module, it first computes a machine-independent cache key for that file, and uses it to try to read from each of the stores in order. Once Metro has obtained the output of the transformer (whether already cached or not), it writes the transform result to ",(0,r.mdx)("em",{parentName:"p"},"all")," of the stores that returned ",(0,r.mdx)("inlineCode",{parentName:"p"},"null")," (a cache miss) for that key."),(0,r.mdx)("pre",null,(0,r.mdx)("code",{parentName:"pre",className:"language-flow"},"type CacheStores =\n | Array>\n | ((MetroCache) => Array<\n CacheStore\n >);\n\n// The exports of 'metro-cache'\ntype MetroCache = {\n FileStore,\n AutoCleanFileStore,\n HttpStore,\n HttpGetStore,\n ...\n};\n\ntype JsonSerializable = /* Any JSON-serializable value */;\n")),(0,r.mdx)("h4",{id:"cacheversion"},(0,r.mdx)("inlineCode",{parentName:"h4"},"cacheVersion")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"string")),(0,r.mdx)("p",null,"An arbitrary string appended to all cache keys in the project before they are hashed. There is generally no need to set this explicitly, as Metro will automatically derive the correct cache keys from your project config and the contents of source files."),(0,r.mdx)("h4",{id:"projectroot"},(0,r.mdx)("inlineCode",{parentName:"h4"},"projectRoot")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"string")),(0,r.mdx)("p",null,"The root folder of your project. If your project depends on any files outside this root, their containing directories must be listed in ",(0,r.mdx)("a",{parentName:"p",href:"#watchfolders"},(0,r.mdx)("inlineCode",{parentName:"a"},"watchFolders")),"."),(0,r.mdx)("admonition",{type:"note"},(0,r.mdx)("p",{parentName:"admonition"},"If your Metro project is developed in a monorepo and includes files from multiple logical packages, you'll generally want to set ",(0,r.mdx)("inlineCode",{parentName:"p"},"projectRoot")," to the root of your repository, or at least high enough in the hierarchy that all relevant files are reachable without separately configuring ",(0,r.mdx)("inlineCode",{parentName:"p"},"watchFolders"),".")),(0,r.mdx)("h4",{id:"watchfolders"},(0,r.mdx)("inlineCode",{parentName:"h4"},"watchFolders")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"Array")),(0,r.mdx)("p",null,"A list of directories outside of ",(0,r.mdx)("a",{parentName:"p",href:"#projectroot"},(0,r.mdx)("inlineCode",{parentName:"a"},"projectRoot"))," that can contain source files for the project."),(0,r.mdx)("admonition",{type:"note"},(0,r.mdx)("p",{parentName:"admonition"},"Despite the naming of this option, it isn't related solely to file watching. Even in an offline build (for example, in CI), all files must be visible to Metro through the combination of ",(0,r.mdx)("inlineCode",{parentName:"p"},"watchFolders")," and ",(0,r.mdx)("inlineCode",{parentName:"p"},"projectRoot"),".")),(0,r.mdx)("admonition",{type:"info"},(0,r.mdx)("p",{parentName:"admonition"},"Note that, as with any other file Metro needs to resolve, targets of any symlinks within your ",(0,r.mdx)("inlineCode",{parentName:"p"},"watchFolders")," ",(0,r.mdx)("em",{parentName:"p"},"must also be within ",(0,r.mdx)("inlineCode",{parentName:"em"},"watchFolders"))," and not otherwise excluded."),(0,r.mdx)("p",{parentName:"admonition"},"If you have a Metro project within a workspace, such as a ",(0,r.mdx)("a",{parentName:"p",href:"https://classic.yarnpkg.com/lang/en/docs/workspaces/"},"Yarn workspace")," (a subdirectory of a Yarn workspace root), it's likely you'll want to include your workspace ",(0,r.mdx)("em",{parentName:"p"},"root")," path in your configured ",(0,r.mdx)("inlineCode",{parentName:"p"},"watchFolders")," so that Metro can resolve other workspaces or hoisted ",(0,r.mdx)("inlineCode",{parentName:"p"},"node_modules"),". Similarly, to use ",(0,r.mdx)("a",{parentName:"p",href:"https://classic.yarnpkg.com/lang/en/docs/cli/link/"},"linked packages"),", you'll need to list those package source locations (or a containing directory) in ",(0,r.mdx)("inlineCode",{parentName:"p"},"watchFolders"),".")),(0,r.mdx)("h4",{id:"transformerpath"},(0,r.mdx)("inlineCode",{parentName:"h4"},"transformerPath")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"string")),(0,r.mdx)("p",null,"The absolute path of a module (or a package name resolvable from the ",(0,r.mdx)("inlineCode",{parentName:"p"},"metro")," package) that implements a transformer."),(0,r.mdx)("p",null,"See the implementation of Metro's default transformer (",(0,r.mdx)("a",{parentName:"p",href:"https://github.com/facebook/metro/blob/main/packages/metro-transform-worker/src/index.js"},(0,r.mdx)("inlineCode",{parentName:"a"},"metro-transform-worker")),") for more information about the transformer interface."),(0,r.mdx)("h4",{id:"reporter"},(0,r.mdx)("inlineCode",{parentName:"h4"},"reporter")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"{update: (event: ReportableEvent) => void}")),(0,r.mdx)("p",null,"Used to report the status of the bundler during the bundling process. The default implementation prints most events to the terminal."),(0,r.mdx)("p",null,"See also the ",(0,r.mdx)("a",{parentName:"p",href:"https://github.com/facebook/metro/blob/main/packages/metro/src/lib/reporting.js"},"definition of ",(0,r.mdx)("inlineCode",{parentName:"a"},"ReportableEvent"))," in Metro's source code."),(0,r.mdx)("h4",{id:"resetcache"},(0,r.mdx)("inlineCode",{parentName:"h4"},"resetCache")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"boolean")),(0,r.mdx)("p",null,"If ",(0,r.mdx)("inlineCode",{parentName:"p"},"true"),", Metro will reset the transformer cache (see ",(0,r.mdx)("a",{parentName:"p",href:"#cachestores"},(0,r.mdx)("inlineCode",{parentName:"a"},"cacheStores")),") and the file map cache (see ",(0,r.mdx)("a",{parentName:"p",href:"#filemapcachedirectory"},(0,r.mdx)("inlineCode",{parentName:"a"},"fileMapCacheDirectory")),") on startup."),(0,r.mdx)("h4",{id:"stickyworkers"},(0,r.mdx)("inlineCode",{parentName:"h4"},"stickyWorkers")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"boolean")),(0,r.mdx)("p",null,"If ",(0,r.mdx)("inlineCode",{parentName:"p"},"true"),", Metro will use a stable mapping from files to transformer workers, so the same file is always transformed by the same worker. This can improve initial build performance if the transformer is expensive to initialize, but can slow down concurrent builds with different configurations (e.g. multiple React Native apps connected to one Metro server). Defaults to ",(0,r.mdx)("inlineCode",{parentName:"p"},"true"),"."),(0,r.mdx)("h4",{id:"maxworkers"},(0,r.mdx)("inlineCode",{parentName:"h4"},"maxWorkers")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"number")),(0,r.mdx)("p",null,"The number of workers to use for parallel processing in Metro. Defaults to approximately half of the number of cores available on the machine, as reported by ",(0,r.mdx)("a",{parentName:"p",href:"https://nodejs.org/api/os.html#oscpus"},(0,r.mdx)("inlineCode",{parentName:"a"},"os.cpus()")),"."),(0,r.mdx)("admonition",{type:"note"},(0,r.mdx)("ol",{parentName:"admonition"},(0,r.mdx)("li",{parentName:"ol"},"Values exceeding the number of available cores have no effect."),(0,r.mdx)("li",{parentName:"ol"},"If ",(0,r.mdx)("inlineCode",{parentName:"li"},"maxWorkers")," is set to 1 or lower, worker code will run in the main Metro process instead of concurrently."),(0,r.mdx)("li",{parentName:"ol"},"Metro has two separate worker pools - one for transformation and one for building the file map. Each pool has its worker count set to ",(0,r.mdx)("inlineCode",{parentName:"li"},"maxWorkers")," independently."))),(0,r.mdx)("h4",{id:"filemapcachedirectory"},(0,r.mdx)("inlineCode",{parentName:"h4"},"fileMapCacheDirectory")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"string")),(0,r.mdx)("p",null,"The path to the ",(0,r.mdx)("inlineCode",{parentName:"p"},"metro-file-map")," cache directory, defaults to ",(0,r.mdx)("inlineCode",{parentName:"p"},"os.tmpdir()"),"."),(0,r.mdx)("h4",{id:"hastemapcachedirectory-deprecated"},(0,r.mdx)("inlineCode",{parentName:"h4"},"hasteMapCacheDirectory")," ",(0,r.mdx)("div",{class:"label deprecated"},"Deprecated")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"string")),(0,r.mdx)("p",null,"Alias of ",(0,r.mdx)("a",{parentName:"p",href:"#filemapcachedirectory"},(0,r.mdx)("inlineCode",{parentName:"a"},"fileMapCacheDirectory"))),(0,r.mdx)("h4",{id:"unstable_perfloggerfactory"},(0,r.mdx)("inlineCode",{parentName:"h4"},"unstable_perfLoggerFactory")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"PerfLoggerFactory")),(0,r.mdx)("p",null,"A logger factory function that can be used to get insights about Metro performance timings and metadata for events including startup, bundling and HMR. Metro expects ",(0,r.mdx)("inlineCode",{parentName:"p"},"unstable_perfLoggerFactory")," to have the following signature:"),(0,r.mdx)("pre",null,(0,r.mdx)("code",{parentName:"pre",className:"language-flow"},"function unstable_perfLoggerFactory(\n type: string,\n opts: $ReadOnly<{\n key?: string\n }>,\n): RootPerfLogger {\n // ...\n};\n")),(0,r.mdx)("ul",null,(0,r.mdx)("li",{parentName:"ul"},(0,r.mdx)("strong",{parentName:"li"},(0,r.mdx)("inlineCode",{parentName:"strong"},"type"))," Type of event being logged, e.g. ",(0,r.mdx)("inlineCode",{parentName:"li"},"'STARTUP'"),", ",(0,r.mdx)("inlineCode",{parentName:"li"},"'BUNDLING_REQUEST'"),", ",(0,r.mdx)("inlineCode",{parentName:"li"},"'HMR'"),". See type definition of ",(0,r.mdx)("a",{parentName:"li",href:"https://github.com/facebook/metro/blob/main/packages/metro-config/src/configTypes.flow.js"},"PerfLoggerFactory")," for a full list of event types."),(0,r.mdx)("li",{parentName:"ul"},(0,r.mdx)("strong",{parentName:"li"},(0,r.mdx)("inlineCode",{parentName:"strong"},"opts")),(0,r.mdx)("ul",{parentName:"li"},(0,r.mdx)("li",{parentName:"ul"},(0,r.mdx)("strong",{parentName:"li"},(0,r.mdx)("inlineCode",{parentName:"strong"},"key")),": An opaque identifier to distinguish between instances of an event type (e.g. multiple, possibly concurrent, HMR requests).")))),(0,r.mdx)("p",null,(0,r.mdx)("inlineCode",{parentName:"p"},"unstable_perfLoggerFactory")," should return an object implementing the ",(0,r.mdx)("a",{parentName:"p",href:"https://github.com/facebook/metro/blob/main/packages/metro-config/src/configTypes.flow.js"},"RootPerfLogger")," interface. For example, a factory function returning a no-op RootPerfLogger could be implemented as follows:"),(0,r.mdx)("pre",null,(0,r.mdx)("code",{parentName:"pre",className:"language-javascript"},"const unstable_perfLoggerFactory = (type, factoryOpts) => {\n const getLogger = subSpanLabel => {\n const logger = {\n start(opts) {},\n end(status, opts) {},\n subSpan(label) {\n return getLogger(`${subSpanLabel ?? ''}/${label}`);\n },\n point(name, opts) {},\n annotate(annotations) {},\n };\n return logger;\n };\n\n return getLogger();\n};\n")),(0,r.mdx)("hr",null),(0,r.mdx)("h3",{id:"resolver-options"},"Resolver Options"),(0,r.mdx)("h4",{id:"assetexts"},(0,r.mdx)("inlineCode",{parentName:"h4"},"assetExts")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"Array")),(0,r.mdx)("p",null,"The list of asset file extensions to include in the bundle. For example, including ",(0,r.mdx)("inlineCode",{parentName:"p"},"'ttf'")," allows Metro bundles to reference ",(0,r.mdx)("inlineCode",{parentName:"p"},".ttf")," files. This is used primarily to enable React Native's ",(0,r.mdx)("a",{parentName:"p",href:"https://reactnative.dev/docs/images"},"image asset support"),". The default list includes many common image, video and audio file extensions. See ",(0,r.mdx)("a",{parentName:"p",href:"https://github.com/facebook/metro/blob/main/packages/metro-config/src/defaults/defaults.js#L16"},"Metro's source code")," for the full list."),(0,r.mdx)("h4",{id:"sourceexts"},(0,r.mdx)("inlineCode",{parentName:"h4"},"sourceExts")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"Array")),(0,r.mdx)("p",null,"The list of source file extensions to include in the bundle. For example, including ",(0,r.mdx)("inlineCode",{parentName:"p"},"'ts'")," allows Metro to include ",(0,r.mdx)("inlineCode",{parentName:"p"},".ts")," files in the bundle."),(0,r.mdx)("p",null,"The order of these extensions defines the order to match files on disk. For more information, see ",(0,r.mdx)("a",{parentName:"p",href:"https://metrobundler.dev/docs/resolution"},"Module Resolution"),"."),(0,r.mdx)("p",null,"Defaults to ",(0,r.mdx)("inlineCode",{parentName:"p"},"['js', 'jsx', 'json', 'ts', 'tsx']"),"."),(0,r.mdx)("h4",{id:"resolvermainfields"},(0,r.mdx)("inlineCode",{parentName:"h4"},"resolverMainFields")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"Array")),(0,r.mdx)("p",null,"The list of fields in ",(0,r.mdx)("inlineCode",{parentName:"p"},"package.json")," that Metro will treat as describing a package's entry points. The default is ",(0,r.mdx)("inlineCode",{parentName:"p"},"['browser', 'main']"),", so the resolver will use the ",(0,r.mdx)("inlineCode",{parentName:"p"},"browser")," field if it exists and ",(0,r.mdx)("inlineCode",{parentName:"p"},"main")," otherwise."),(0,r.mdx)("p",null,"Metro's default resolver processes each of these fields according to the ",(0,r.mdx)("a",{parentName:"p",href:"https://github.com/defunctzombie/package-browser-field-spec"},(0,r.mdx)("inlineCode",{parentName:"a"},"browser")," field spec"),", including the ability to ",(0,r.mdx)("a",{parentName:"p",href:"https://github.com/defunctzombie/package-browser-field-spec#replace-specific-files---advanced"},"replace")," and ",(0,r.mdx)("a",{parentName:"p",href:"https://github.com/defunctzombie/package-browser-field-spec#ignore-a-module"},"ignore")," specific files. For more information, see ",(0,r.mdx)("a",{parentName:"p",href:"https://metrobundler.dev/docs/resolution"},"Module Resolution"),"."),(0,r.mdx)("admonition",{type:"note"},(0,r.mdx)("p",{parentName:"admonition"},"When using React Native, ",(0,r.mdx)("inlineCode",{parentName:"p"},"resolverMainFields")," defaults to ",(0,r.mdx)("inlineCode",{parentName:"p"},"['react-native', 'browser', 'main']"),".")),(0,r.mdx)("h4",{id:"disablehierarchicallookup"},(0,r.mdx)("inlineCode",{parentName:"h4"},"disableHierarchicalLookup")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"boolean")),(0,r.mdx)("p",null,"Whether to disable ",(0,r.mdx)("a",{parentName:"p",href:"https://nodejs.org/api/modules.html#modules_loading_from_node_modules_folders"},"looking up modules in ",(0,r.mdx)("inlineCode",{parentName:"a"},"node_modules")," folders"),". This only affects the default search through the directory tree, not other Metro options like ",(0,r.mdx)("inlineCode",{parentName:"p"},"extraNodeModules")," or ",(0,r.mdx)("inlineCode",{parentName:"p"},"nodeModulesPaths"),". Defaults to ",(0,r.mdx)("inlineCode",{parentName:"p"},"false"),"."),(0,r.mdx)("h4",{id:"emptymodulepath"},(0,r.mdx)("inlineCode",{parentName:"h4"},"emptyModulePath")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"string")),(0,r.mdx)("p",null,'What module to use as the canonical "empty" module when one is needed. Defaults to using the one included in ',(0,r.mdx)("inlineCode",{parentName:"p"},"metro-runtime"),". You only need to change this if Metro is installed outside of your project."),(0,r.mdx)("h4",{id:"enableglobalpackages"},(0,r.mdx)("inlineCode",{parentName:"h4"},"enableGlobalPackages")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"boolean"),"."),(0,r.mdx)("p",null,"Whether to automatically resolve references to first-party packages (e.g. workspaces) in your project. Any ",(0,r.mdx)("inlineCode",{parentName:"p"},"package.json")," file with a valid ",(0,r.mdx)("inlineCode",{parentName:"p"},"name")," property within ",(0,r.mdx)("inlineCode",{parentName:"p"},"projectRoot")," or ",(0,r.mdx)("inlineCode",{parentName:"p"},"watchFolders")," (but outside of ",(0,r.mdx)("inlineCode",{parentName:"p"},"node_modules"),") counts as a package for this purpose. Defaults to ",(0,r.mdx)("inlineCode",{parentName:"p"},"false"),"."),(0,r.mdx)("h4",{id:"extranodemodules"},(0,r.mdx)("inlineCode",{parentName:"h4"},"extraNodeModules")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"{[string]: string}")),(0,r.mdx)("p",null,"A mapping of package names to directories that is consulted after the standard lookup through ",(0,r.mdx)("inlineCode",{parentName:"p"},"node_modules")," as well as any ",(0,r.mdx)("a",{parentName:"p",href:"#nodemodulespaths"},(0,r.mdx)("inlineCode",{parentName:"a"},"nodeModulesPaths")),". For more information, see ",(0,r.mdx)("a",{parentName:"p",href:"https://metrobundler.dev/docs/resolution"},"Module Resolution"),"."),(0,r.mdx)("h4",{id:"nodemodulespaths"},(0,r.mdx)("inlineCode",{parentName:"h4"},"nodeModulesPaths")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"Array")),(0,r.mdx)("p",null,"A list of paths to check for modules after looking through all ",(0,r.mdx)("inlineCode",{parentName:"p"},"node_modules")," directories. This is useful if third-party dependencies are installed in a different location outside of the direct path of source files. For more information, see ",(0,r.mdx)("a",{parentName:"p",href:"https://metrobundler.dev/docs/resolution"},"Module Resolution"),"."),(0,r.mdx)("h4",{id:"resolverequest"},(0,r.mdx)("inlineCode",{parentName:"h4"},"resolveRequest")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("a",{parentName:"p",href:"/docs/resolution#resolverequest-customresolver"},(0,r.mdx)("inlineCode",{parentName:"a"},"?CustomResolver"))),(0,r.mdx)("p",null,"An optional function used to override the default resolution algorithm. This is particularly useful for cases where aliases or custom protocols are used. For example:"),(0,r.mdx)("pre",null,(0,r.mdx)("code",{parentName:"pre",className:"language-javascript"},"resolveRequest: (context, moduleName, platform) => {\n if (moduleName.startsWith('my-custom-resolver:')) {\n // Logic to resolve the module name to a file path...\n // NOTE: Throw an error if there is no resolution.\n return {\n filePath: 'path/to/file',\n type: 'sourceFile',\n };\n }\n // Optionally, chain to the standard Metro resolver.\n return context.resolveRequest(context, moduleName, platform);\n}\n")),(0,r.mdx)("p",null,"For more information on customizing the resolver, see ",(0,r.mdx)("a",{parentName:"p",href:"https://metrobundler.dev/docs/resolution"},"Module Resolution"),"."),(0,r.mdx)("h4",{id:"usewatchman"},(0,r.mdx)("inlineCode",{parentName:"h4"},"useWatchman")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"boolean")),(0,r.mdx)("p",null,"If set to ",(0,r.mdx)("inlineCode",{parentName:"p"},"false"),", prevents Metro from using Watchman (even if it's installed)."),(0,r.mdx)("h4",{id:"blocklist"},(0,r.mdx)("inlineCode",{parentName:"h4"},"blockList")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"RegExp")," or ",(0,r.mdx)("inlineCode",{parentName:"p"},"Array")),(0,r.mdx)("p",null,"A regular expression (or list of regular expressions) defining which paths to exclude from Metro's file map. Files whose absolute paths match these patterns are effectively hidden from Metro and cannot be resolved or imported in the current project."),(0,r.mdx)("h4",{id:"hasteimplmodulepath"},(0,r.mdx)("inlineCode",{parentName:"h4"},"hasteImplModulePath")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"?string")),(0,r.mdx)("p",null,"The path to the Haste implementation for the current project. Haste is an opt-in mechanism for importing modules by their globally-unique name anywhere in the project, e.g. ",(0,r.mdx)("inlineCode",{parentName:"p"},"import Foo from 'Foo'"),"."),(0,r.mdx)("p",null,"Metro expects this module to have the following signature:"),(0,r.mdx)("pre",null,(0,r.mdx)("code",{parentName:"pre",className:"language-flow"},"module.exports = {\n getHasteName(filePath: string): ?string {\n // ...\n },\n};\n")),(0,r.mdx)("p",null,(0,r.mdx)("inlineCode",{parentName:"p"},"getHasteName")," should return a short, globally unique name for the module whose path is ",(0,r.mdx)("inlineCode",{parentName:"p"},"filePath"),", or ",(0,r.mdx)("inlineCode",{parentName:"p"},"null")," if the module should not be accessible via Haste."),(0,r.mdx)("h4",{id:"platforms"},(0,r.mdx)("inlineCode",{parentName:"h4"},"platforms")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"Array")),(0,r.mdx)("p",null,"Additional platforms to resolve. Defaults to ",(0,r.mdx)("inlineCode",{parentName:"p"},"['ios', 'android', 'windows', 'web']"),"."),(0,r.mdx)("p",null,"For more information, see ",(0,r.mdx)("a",{parentName:"p",href:"/docs/resolution"},"Module Resolution")," and ",(0,r.mdx)("a",{parentName:"p",href:"https://reactnative.dev/docs/platform-specific-code#platform-specific-extensions"},"React Native's documentation for platform-specific extensions"),"."),(0,r.mdx)("h4",{id:"requirecycleignorepatterns"},(0,r.mdx)("inlineCode",{parentName:"h4"},"requireCycleIgnorePatterns")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"Array")),(0,r.mdx)("p",null,"In development mode, suppress require cycle warnings for any cycle involving a module that matches any of these expressions. This is useful for third-party code and first-party expected cycles."),(0,r.mdx)("p",null,"Note that if you specify your own value for this config option it will replace (not concatenate with) Metro's default."),(0,r.mdx)("p",null,"Defaults to ",(0,r.mdx)("inlineCode",{parentName:"p"},"[/(^|\\/|\\\\)node_modules($|\\/|\\\\)/]"),"."),(0,r.mdx)("h4",{id:"unstable_conditionnames-experimental"},(0,r.mdx)("inlineCode",{parentName:"h4"},"unstable_conditionNames")," ",(0,r.mdx)("div",{class:"label experimental"},"Experimental")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"Array")),(0,r.mdx)("admonition",{type:"note"},(0,r.mdx)("p",{parentName:"admonition"},"This setting will take effect when ",(0,r.mdx)("a",{parentName:"p",href:"#unstable_enablepackageexports-experimental"},(0,r.mdx)("inlineCode",{parentName:"a"},"unstable_enablePackageExports"))," is ",(0,r.mdx)("inlineCode",{parentName:"p"},"true"),". It may not behave as described while this feature is experimental.")),(0,r.mdx)("p",null,"The set of ",(0,r.mdx)("a",{parentName:"p",href:"https://nodejs.org/docs/latest-v18.x/api/packages.html#conditional-exports"},"condition names")," to assert globally when interpreting the ",(0,r.mdx)("a",{parentName:"p",href:"https://nodejs.org/docs/latest-v18.x/api/packages.html#exports"},(0,r.mdx)("inlineCode",{parentName:"a"},'"exports"')," field")," in package.json."),(0,r.mdx)("p",null,"Conditions may be any string value and are resolved in the order specified by each package. Node.js documents a number of ",(0,r.mdx)("a",{parentName:"p",href:"https://nodejs.org/docs/latest-v18.x/api/packages.html#community-conditions-definitions"},"community conditions")," which are commonly used by package authors. The ",(0,r.mdx)("inlineCode",{parentName:"p"},"default")," condition is always matched."),(0,r.mdx)("p",null,"Defaults to ",(0,r.mdx)("inlineCode",{parentName:"p"},"['require']"),"."),(0,r.mdx)("admonition",{type:"note"},(0,r.mdx)("p",{parentName:"admonition"},"When using React Native, ",(0,r.mdx)("inlineCode",{parentName:"p"},"unstable_conditionNames")," defaults to ",(0,r.mdx)("inlineCode",{parentName:"p"},"['require', 'react-native']"),".")),(0,r.mdx)("h4",{id:"unstable_conditionsbyplatform-experimental"},(0,r.mdx)("inlineCode",{parentName:"h4"},"unstable_conditionsByPlatform")," ",(0,r.mdx)("div",{class:"label experimental"},"Experimental")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"{[platform: string]: Array}")),(0,r.mdx)("admonition",{type:"note"},(0,r.mdx)("p",{parentName:"admonition"},"This setting will take effect when ",(0,r.mdx)("a",{parentName:"p",href:"#unstable_enablepackageexports-experimental"},(0,r.mdx)("inlineCode",{parentName:"a"},"unstable_enablePackageExports"))," is ",(0,r.mdx)("inlineCode",{parentName:"p"},"true"),". It may not behave as described while this feature is experimental.")),(0,r.mdx)("p",null,"The set of additional ",(0,r.mdx)("a",{parentName:"p",href:"https://nodejs.org/docs/latest-v18.x/api/packages.html#conditional-exports"},"condition names")," to dynamically assert by platform (see ",(0,r.mdx)("a",{parentName:"p",href:"#platforms"},(0,r.mdx)("inlineCode",{parentName:"a"},"platforms")),") when interpreting the ",(0,r.mdx)("a",{parentName:"p",href:"https://nodejs.org/docs/latest-v18.x/api/packages.html#exports"},(0,r.mdx)("inlineCode",{parentName:"a"},'"exports"')," field")," in package.json."),(0,r.mdx)("p",null,"Matched conditions are merged with ",(0,r.mdx)("a",{parentName:"p",href:"#unstable-conditionnames"},(0,r.mdx)("inlineCode",{parentName:"a"},"unstable_conditionNames"))," before resolution. With the defaults for both options, the conditions ",(0,r.mdx)("inlineCode",{parentName:"p"},"new Set(['require', 'browser'])")," will be asserted when requesting a ",(0,r.mdx)("inlineCode",{parentName:"p"},"web")," bundle, and ",(0,r.mdx)("inlineCode",{parentName:"p"},"new Set(['require'])")," otherwise. Again, these are resolved in the order specified by each package."),(0,r.mdx)("p",null,"Defaults to ",(0,r.mdx)("inlineCode",{parentName:"p"},"\u200c{ web: ['browser'] }"),"."),(0,r.mdx)("h4",{id:"unstable_enablepackageexports-experimental"},(0,r.mdx)("inlineCode",{parentName:"h4"},"unstable_enablePackageExports")," ",(0,r.mdx)("div",{class:"label experimental"},"Experimental")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"boolean")),(0,r.mdx)("p",null,"Enable experimental ",(0,r.mdx)("a",{parentName:"p",href:"https://nodejs.org/docs/latest-v18.x/api/packages.html#package-entry-points"},"Package Exports")," support. Under this mode, Metro will read the ",(0,r.mdx)("a",{parentName:"p",href:"https://nodejs.org/docs/latest-v18.x/api/packages.html#exports"},(0,r.mdx)("inlineCode",{parentName:"a"},'"exports"')," field")," in ",(0,r.mdx)("inlineCode",{parentName:"p"},"package.json")," files when present and use it to resolve package entry points."),(0,r.mdx)("p",null,"When no match is found in ",(0,r.mdx)("inlineCode",{parentName:"p"},'"exports"'),", Metro will log a warning and fall back to resolving modules without considering ",(0,r.mdx)("inlineCode",{parentName:"p"},'"exports"'),". This makes this mode largely backwards-compatible, with the following exceptions:"),(0,r.mdx)("ul",null,(0,r.mdx)("li",{parentName:"ul"},"If a module is matched in ",(0,r.mdx)("inlineCode",{parentName:"li"},'"exports"'),", ",(0,r.mdx)("a",{parentName:"li",href:"#sourceexts"},(0,r.mdx)("inlineCode",{parentName:"a"},"sourceExts"))," and ",(0,r.mdx)("a",{parentName:"li",href:"#platforms"},(0,r.mdx)("inlineCode",{parentName:"a"},"platforms"))," will not be considered (i.e. platform-specific extensions will not be used). This is done for compatibility with Node."),(0,r.mdx)("li",{parentName:"ul"},"If a module exists at a file path that is also listed in ",(0,r.mdx)("inlineCode",{parentName:"li"},'"exports"'),", and the ",(0,r.mdx)("inlineCode",{parentName:"li"},'"exports"')," entry maps to a different file, the ",(0,r.mdx)("inlineCode",{parentName:"li"},'"exports"')," entry will be preferred.")),(0,r.mdx)("p",null,"Defaults to ",(0,r.mdx)("inlineCode",{parentName:"p"},"false"),"."),(0,r.mdx)("admonition",{type:"note"},(0,r.mdx)("p",{parentName:"admonition"},"In a future release of Metro, this option will become ",(0,r.mdx)("inlineCode",{parentName:"p"},"true")," by default.")),(0,r.mdx)("hr",null),(0,r.mdx)("h3",{id:"transformer-options"},"Transformer Options"),(0,r.mdx)("a",{name:"asyncrequiremodulepath"}),(0,r.mdx)("h4",{id:"asyncrequiremodulepath-deprecated"},(0,r.mdx)("inlineCode",{parentName:"h4"},"asyncRequireModulePath")," ",(0,r.mdx)("div",{class:"label deprecated"},"Deprecated")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"string")),(0,r.mdx)("p",null,"The name of a module that provides the ",(0,r.mdx)("inlineCode",{parentName:"p"},"asyncRequire")," function, which is used to implement ",(0,r.mdx)("a",{parentName:"p",href:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import"},"dynamic ",(0,r.mdx)("inlineCode",{parentName:"a"},"import()"))," at runtime. Defaults to ",(0,r.mdx)("a",{parentName:"p",href:"https://github.com/facebook/metro/blob/main/packages/metro-runtime/src/modules/asyncRequire.js"},(0,r.mdx)("inlineCode",{parentName:"a"},"metro-runtime/src/modules/asyncRequire")),"."),(0,r.mdx)("admonition",{type:"note"},(0,r.mdx)("p",{parentName:"admonition"},"The module named by ",(0,r.mdx)("inlineCode",{parentName:"p"},"asyncRequireModulePath")," is ",(0,r.mdx)("a",{parentName:"p",href:"/docs/resolution"},"resolved")," relative to the module containing the original ",(0,r.mdx)("inlineCode",{parentName:"p"},"import()")," call. In particular, assuming the default value of ",(0,r.mdx)("inlineCode",{parentName:"p"},"asyncRequireModulePath")," is in use, the project must have a compatible version of ",(0,r.mdx)("inlineCode",{parentName:"p"},"metro-runtime")," installed in ",(0,r.mdx)("inlineCode",{parentName:"p"},"node_modules"),".")),(0,r.mdx)("admonition",{type:"info"},(0,r.mdx)("p",{parentName:"admonition"},"In older versions of Metro, a custom ",(0,r.mdx)("inlineCode",{parentName:"p"},"asyncRequireModulePath")," could be used as part of a bundle splitting solution. This usage is now deprecated in favor of the ",(0,r.mdx)("a",{parentName:"p",href:"https://github.com/react-native-community/discussions-and-proposals/blob/main/proposals/0605-lazy-bundling.md#__loadbundleasync-in-metro"},(0,r.mdx)("inlineCode",{parentName:"a"},"__loadBundleAsync"))," API.")),(0,r.mdx)("h4",{id:"dynamicdepsinpackages"},(0,r.mdx)("inlineCode",{parentName:"h4"},"dynamicDepsInPackages")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"'throwAtRuntime' | 'reject'")),(0,r.mdx)("p",null,"Controls how Metro handles dependencies that cannot be statically analyzed at build time. For example, ",(0,r.mdx)("inlineCode",{parentName:"p"},"require('./' + someFunction() + '.js')")," cannot be resolved without knowing what ",(0,r.mdx)("inlineCode",{parentName:"p"},"someFunction()")," will return."),(0,r.mdx)("ul",null,(0,r.mdx)("li",{parentName:"ul"},(0,r.mdx)("strong",{parentName:"li"},(0,r.mdx)("inlineCode",{parentName:"strong"},"'throwAtRuntime'"))," (the default): Metro does not stop bundling, but the ",(0,r.mdx)("inlineCode",{parentName:"li"},"require")," call will throw at runtime."),(0,r.mdx)("li",{parentName:"ul"},(0,r.mdx)("strong",{parentName:"li"},(0,r.mdx)("inlineCode",{parentName:"strong"},"'reject'")),": Metro will stop bundling and report an error to the user.")),(0,r.mdx)("h4",{id:"gettransformoptions"},(0,r.mdx)("inlineCode",{parentName:"h4"},"getTransformOptions")),(0,r.mdx)("p",null,"Type: Function (see details below)"),(0,r.mdx)("p",null,"A function called by Metro to calculate additional options for the transformer and serializer based on the specific bundle being built."),(0,r.mdx)("p",null,"Metro expects ",(0,r.mdx)("inlineCode",{parentName:"p"},"getTransformOptions")," to have the following signature:"),(0,r.mdx)("pre",null,(0,r.mdx)("code",{parentName:"pre",className:"language-flow"},"function getTransformOptions(\n entryPoints: $ReadOnlyArray,\n options: {\n dev: boolean,\n hot: boolean,\n platform: ?string,\n },\n getDependenciesOf: (path: string) => Promise>,\n): Promise {\n // ...\n}\n")),(0,r.mdx)("p",null,(0,r.mdx)("inlineCode",{parentName:"p"},"getTransformOptions")," receives these parameters:"),(0,r.mdx)("ul",null,(0,r.mdx)("li",{parentName:"ul"},(0,r.mdx)("strong",{parentName:"li"},(0,r.mdx)("inlineCode",{parentName:"strong"},"entryPoints")),": Absolute paths to the bundle's entry points (typically just one)."),(0,r.mdx)("li",{parentName:"ul"},(0,r.mdx)("strong",{parentName:"li"},(0,r.mdx)("inlineCode",{parentName:"strong"},"options")),":",(0,r.mdx)("ul",{parentName:"li"},(0,r.mdx)("li",{parentName:"ul"},(0,r.mdx)("strong",{parentName:"li"},(0,r.mdx)("inlineCode",{parentName:"strong"},"dev")),": Whether the bundle is being built in development mode."),(0,r.mdx)("li",{parentName:"ul"},(0,r.mdx)("strong",{parentName:"li"},(0,r.mdx)("inlineCode",{parentName:"strong"},"hot")),": ",(0,r.mdx)("div",{class:"label deprecated"},"Deprecated")," Always true."),(0,r.mdx)("li",{parentName:"ul"},(0,r.mdx)("strong",{parentName:"li"},(0,r.mdx)("inlineCode",{parentName:"strong"},"platform")),": The target platform (e.g. ",(0,r.mdx)("inlineCode",{parentName:"li"},"ios"),", ",(0,r.mdx)("inlineCode",{parentName:"li"},"android"),")."))),(0,r.mdx)("li",{parentName:"ul"},(0,r.mdx)("strong",{parentName:"li"},(0,r.mdx)("inlineCode",{parentName:"strong"},"getDependenciesOf")),": A function which, given an absolute path to a module, returns a promise that resolves to the absolute paths of the module's transitive dependencies.")),(0,r.mdx)("p",null,(0,r.mdx)("inlineCode",{parentName:"p"},"getTransformOptions")," should return a promise that resolves to an object with the following properties:"),(0,r.mdx)("pre",null,(0,r.mdx)("code",{parentName:"pre",className:"language-flow"},"type ExtraTransformOptions = {\n preloadedModules?: {[path: string]: true} | false,\n ramGroups?: Array,\n transform?: {\n inlineRequires?: {blockList: {[string]: true}} | boolean,\n nonInlinedRequires?: $ReadOnlyArray,\n },\n};\n")),(0,r.mdx)("ul",null,(0,r.mdx)("li",{parentName:"ul"},(0,r.mdx)("strong",{parentName:"li"},(0,r.mdx)("inlineCode",{parentName:"strong"},"preloadedModules")),": A plain object whose keys represent a set of absolute paths. When serializing an ",(0,r.mdx)("a",{parentName:"li",href:"https://reactnative.dev/docs/ram-bundles-inline-requires#enable-the-ram-format"},"indexed RAM bundle"),", the modules in this set will be marked for eager evaluation at runtime."),(0,r.mdx)("li",{parentName:"ul"},(0,r.mdx)("strong",{parentName:"li"},(0,r.mdx)("inlineCode",{parentName:"strong"},"ramGroups")),": An array of absolute paths. When serializing an ",(0,r.mdx)("a",{parentName:"li",href:"https://reactnative.dev/docs/ram-bundles-inline-requires#enable-the-ram-format"},"indexed RAM bundle"),", each of the listed modules will be serialized along with its transitive dependencies. At runtime, the modules will all be parsed together as soon as any one of them is evaluated."),(0,r.mdx)("li",{parentName:"ul"},(0,r.mdx)("strong",{parentName:"li"},(0,r.mdx)("inlineCode",{parentName:"strong"},"transform")),": Advanced options for the transformer.",(0,r.mdx)("ul",{parentName:"li"},(0,r.mdx)("li",{parentName:"ul"},(0,r.mdx)("strong",{parentName:"li"},(0,r.mdx)("inlineCode",{parentName:"strong"},"inlineRequires")),":",(0,r.mdx)("ul",{parentName:"li"},(0,r.mdx)("li",{parentName:"ul"},"If ",(0,r.mdx)("inlineCode",{parentName:"li"},"inlineRequires")," is a boolean, it controls whether ",(0,r.mdx)("a",{parentName:"li",href:"https://reactnative.dev/docs/ram-bundles-inline-requires#inline-requires"},"inline requires")," are enabled in this bundle."),(0,r.mdx)("li",{parentName:"ul"},"If ",(0,r.mdx)("inlineCode",{parentName:"li"},"inlineRequires")," is an object, inline requires are enabled in all modules, except ones whose absolute paths appear as keys of ",(0,r.mdx)("inlineCode",{parentName:"li"},"inlineRequires.blockList"),"."))),(0,r.mdx)("li",{parentName:"ul"},(0,r.mdx)("strong",{parentName:"li"},(0,r.mdx)("inlineCode",{parentName:"strong"},"nonInlinedRequires")),": An array of unresolved module specifiers (e.g. ",(0,r.mdx)("inlineCode",{parentName:"li"},"react"),", ",(0,r.mdx)("inlineCode",{parentName:"li"},"react-native"),") to never inline, even when inline requires are enabled.")))),(0,r.mdx)("h4",{id:"minifierpath"},(0,r.mdx)("inlineCode",{parentName:"h4"},"minifierPath")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"string")," (default: ",(0,r.mdx)("inlineCode",{parentName:"p"},"'metro-minify-terser'"),")"),(0,r.mdx)("p",null,"Path, or package name resolvable from ",(0,r.mdx)("inlineCode",{parentName:"p"},"metro-transform-worker"),", to the minifier that minifies the code after transformation."),(0,r.mdx)("h4",{id:"minifierconfig"},(0,r.mdx)("inlineCode",{parentName:"h4"},"minifierConfig")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"{[key: string]: mixed}")),(0,r.mdx)("p",null,"Configuration object that will be passed to the minifier (it should be serializable)."),(0,r.mdx)("h4",{id:"optimizationsizelimit"},(0,r.mdx)("inlineCode",{parentName:"h4"},"optimizationSizeLimit")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"number")),(0,r.mdx)("p",null,"Define a threshold (in bytes) to disable some expensive optimizations for big files."),(0,r.mdx)("h4",{id:"react-native-only"},"React Native Only"),(0,r.mdx)("h4",{id:"assetplugins"},(0,r.mdx)("inlineCode",{parentName:"h4"},"assetPlugins")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"Array")),(0,r.mdx)("p",null,"List of modules to call to modify Asset data"),(0,r.mdx)("h4",{id:"assetregistrypath"},(0,r.mdx)("inlineCode",{parentName:"h4"},"assetRegistryPath")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"string")),(0,r.mdx)("p",null,"Where to fetch the assets from."),(0,r.mdx)("h3",{id:"babel-specific-transformer-options"},"Babel-specific transformer options"),(0,r.mdx)("h4",{id:"babeltransformerpath"},(0,r.mdx)("inlineCode",{parentName:"h4"},"babelTransformerPath")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"string")),(0,r.mdx)("p",null,"The name of a module that compiles code with Babel, returning an AST and optional metadata. Defaults to ",(0,r.mdx)("inlineCode",{parentName:"p"},"metro-babel-transformer"),"."),(0,r.mdx)("p",null,"Refer to the source code of ",(0,r.mdx)("a",{parentName:"p",href:"https://github.com/facebook/metro/blob/main/packages/metro-babel-transformer/src/index.js"},(0,r.mdx)("inlineCode",{parentName:"a"},"metro-babel-transformer"))," and ",(0,r.mdx)("a",{parentName:"p",href:"https://github.com/facebook/react-native/blob/main/packages/react-native-babel-transformer/src/index.js"},(0,r.mdx)("inlineCode",{parentName:"a"},"@react-native/metro-babel-transformer"))," for details on implementing a custom Babel transformer."),(0,r.mdx)("admonition",{type:"note"},(0,r.mdx)("p",{parentName:"admonition"},"This option only has an effect under the default ",(0,r.mdx)("a",{parentName:"p",href:"#transformerpath"},(0,r.mdx)("inlineCode",{parentName:"a"},"transformerPath")),". Custom transformers may ignore it.")),(0,r.mdx)("h4",{id:"enablebabelrclookup"},(0,r.mdx)("inlineCode",{parentName:"h4"},"enableBabelRCLookup")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"boolean")),(0,r.mdx)("p",null,"Whether to enable searching for Babel configuration files. This is passed to Babel as the ",(0,r.mdx)("a",{parentName:"p",href:"https://babeljs.io/docs/en/options#babelrc"},(0,r.mdx)("inlineCode",{parentName:"a"},"babelrc"))," config option. Defaults to ",(0,r.mdx)("inlineCode",{parentName:"p"},"true"),"."),(0,r.mdx)("admonition",{type:"note"},(0,r.mdx)("p",{parentName:"admonition"},"This option only has an effect under the default ",(0,r.mdx)("a",{parentName:"p",href:"#transformerpath"},(0,r.mdx)("inlineCode",{parentName:"a"},"transformerPath")),". Custom transformers may ignore it. Custom ",(0,r.mdx)("a",{parentName:"p",href:"#babeltransformerpath"},"Babel transformers")," should respect this option.")),(0,r.mdx)("h4",{id:"enablebabelruntime"},(0,r.mdx)("inlineCode",{parentName:"h4"},"enableBabelRuntime")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"boolean | string")),(0,r.mdx)("p",null,"Whether the transformer should use the ",(0,r.mdx)("inlineCode",{parentName:"p"},"@babel/transform/runtime")," plugin. Defaults to ",(0,r.mdx)("inlineCode",{parentName:"p"},"true"),"."),(0,r.mdx)("p",null,"If the value is a string, it is treated as a runtime version number and passed as ",(0,r.mdx)("inlineCode",{parentName:"p"},"version")," to the ",(0,r.mdx)("inlineCode",{parentName:"p"},"@babel/plugin-transform-runtime")," configuration. This allows you to optimize the generated Babel runtime calls based on the version installed in your project."),(0,r.mdx)("admonition",{type:"note"},(0,r.mdx)("p",{parentName:"admonition"},"This option only works under the default settings for React Native. It may have no effect in a project that uses custom ",(0,r.mdx)("a",{parentName:"p",href:"#transformerpath"},(0,r.mdx)("inlineCode",{parentName:"a"},"transformerPath")),", a custom ",(0,r.mdx)("a",{parentName:"p",href:"#babeltransformerpath"},(0,r.mdx)("inlineCode",{parentName:"a"},"babelTransformerPath"))," or a custom ",(0,r.mdx)("a",{parentName:"p",href:"https://babeljs.io/docs/en/config-files"},"Babel config file"),".")),(0,r.mdx)("h4",{id:"hermesparser"},(0,r.mdx)("inlineCode",{parentName:"h4"},"hermesParser")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"boolean")),(0,r.mdx)("p",null,"Whether to use the ",(0,r.mdx)("a",{parentName:"p",href:"https://www.npmjs.com/package/hermes-parser"},(0,r.mdx)("inlineCode",{parentName:"a"},"hermes-parser"))," package to parse JavaScript source files, instead of Babel. Defaults to ",(0,r.mdx)("inlineCode",{parentName:"p"},"false"),"."),(0,r.mdx)("admonition",{type:"note"},(0,r.mdx)("p",{parentName:"admonition"},"This option only has an effect under the default ",(0,r.mdx)("a",{parentName:"p",href:"#transformerpath"},(0,r.mdx)("inlineCode",{parentName:"a"},"transformerPath"))," and the ",(0,r.mdx)("a",{parentName:"p",href:"#babeltransformerpath"},"Babel transformers")," built into Metro. Custom transformers and custom ",(0,r.mdx)("a",{parentName:"p",href:"#babeltransformerpath"},"Babel transformers")," may ignore it.")),(0,r.mdx)("hr",null),(0,r.mdx)("h3",{id:"serializer-options"},"Serializer Options"),(0,r.mdx)("h4",{id:"getrunmodulestatement"},(0,r.mdx)("inlineCode",{parentName:"h4"},"getRunModuleStatement")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"(number | string) => string")),(0,r.mdx)("p",null,"Specify the format of the initial require statements that are appended at the end of the bundle. By default is ",(0,r.mdx)("inlineCode",{parentName:"p"},"__r(${moduleId});"),"."),(0,r.mdx)("h4",{id:"createmoduleidfactory"},(0,r.mdx)("inlineCode",{parentName:"h4"},"createModuleIdFactory")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"() => (path: string) => number")),(0,r.mdx)("p",null,"Used to generate the module id for ",(0,r.mdx)("inlineCode",{parentName:"p"},"require")," statements."),(0,r.mdx)("h4",{id:"getpolyfills"},(0,r.mdx)("inlineCode",{parentName:"h4"},"getPolyfills")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"({platform: ?string}) => $ReadOnlyArray")),(0,r.mdx)("p",null,"An optional list of polyfills to include in the bundle. The list defaults to a set of common polyfills for Number, String, Array, Object..."),(0,r.mdx)("h4",{id:"getmodulesrunbeforemainmodule"},(0,r.mdx)("inlineCode",{parentName:"h4"},"getModulesRunBeforeMainModule")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"(entryFilePath: string) => Array")),(0,r.mdx)("p",null,"An array of modules to be required before the entry point. It should contain the absolute path of each module. Note that this will add the additional require statements only if the passed modules are already included as part of the bundle."),(0,r.mdx)("h4",{id:"processmodulefilter"},(0,r.mdx)("inlineCode",{parentName:"h4"},"processModuleFilter")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"(module: Array) => boolean")),(0,r.mdx)("p",null,"A filter function to discard specific modules from the output."),(0,r.mdx)("h4",{id:"isthirdpartymodule"},(0,r.mdx)("inlineCode",{parentName:"h4"},"isThirdPartyModule")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"(module: {path: string, ...}) => boolean")),(0,r.mdx)("p",null,"A function that determines which modules are added to the ",(0,r.mdx)("a",{parentName:"p",href:"https://developer.chrome.com/articles/x-google-ignore-list/"},(0,r.mdx)("inlineCode",{parentName:"a"},"x_google_ignoreList"))," field of the source map. This supports ",(0,r.mdx)("a",{parentName:"p",href:"https://developer.chrome.com/blog/devtools-modern-web-debugging/#just-my-code"},'"Just My Code"')," debugging in Chrome DevTools and other compatible debuggers."),(0,r.mdx)("p",null,"Defaults to returning ",(0,r.mdx)("inlineCode",{parentName:"p"},"true")," for modules with a path component named ",(0,r.mdx)("inlineCode",{parentName:"p"},"node_modules"),"."),(0,r.mdx)("admonition",{type:"note"},(0,r.mdx)("p",{parentName:"admonition"},"In addition to modules marked as ignored by ",(0,r.mdx)("inlineCode",{parentName:"p"},"isThirdPartyModule"),", Metro will also automatically add modules generated by the bundler itself to the ignore list.")),(0,r.mdx)("hr",null),(0,r.mdx)("h3",{id:"server-options"},"Server Options"),(0,r.mdx)("p",null,"These options are used when Metro serves the content."),(0,r.mdx)("h4",{id:"port"},(0,r.mdx)("inlineCode",{parentName:"h4"},"port")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"number")),(0,r.mdx)("p",null,"Which port to listen on."),(0,r.mdx)("h4",{id:"useglobalhotkey"},(0,r.mdx)("inlineCode",{parentName:"h4"},"useGlobalHotkey")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"boolean")),(0,r.mdx)("p",null,"Whether we should enable CMD+R hotkey for refreshing the bundle."),(0,r.mdx)("h4",{id:"enhancemiddleware-deprecated"},(0,r.mdx)("inlineCode",{parentName:"h4"},"enhanceMiddleware")," ",(0,r.mdx)("div",{class:"label deprecated"},"Deprecated")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"(Middleware, MetroServer) => Middleware")),(0,r.mdx)("p",null,"A function that allows attaching custom ",(0,r.mdx)("a",{parentName:"p",href:"https://www.npmjs.com/package/connect"},(0,r.mdx)("inlineCode",{parentName:"a"},"connect"))," middleware to Metro. For example:"),(0,r.mdx)("admonition",{type:"tip"},(0,r.mdx)("p",{parentName:"admonition"},"You can use ",(0,r.mdx)("a",{parentName:"p",href:"https://www.npmjs.com/package/connect#mount-middleware"},(0,r.mdx)("inlineCode",{parentName:"a"},"connect()"))," as a utility to extend the base ",(0,r.mdx)("inlineCode",{parentName:"p"},"metroMiddleware")," and to mount additional middleware handlers.")),(0,r.mdx)("pre",null,(0,r.mdx)("code",{parentName:"pre",className:"language-ts"},"enhanceMiddleware: (metroMiddleware: Middleware, metroServer: MetroServer) => {\n return connect()\n .use(metroMiddleware)\n .use('/custom-endpoint', customEndpointMiddleware());\n},\n")),(0,r.mdx)("p",null,"The ",(0,r.mdx)("inlineCode",{parentName:"p"},"Middleware")," type is an alias for ",(0,r.mdx)("a",{parentName:"p",href:"https://github.com/DefinitelyTyped/DefinitelyTyped/blob/876b9ec96ba02d0c84b1e49af5890c8f5aa2dfe3/types/connect/index.d.ts#L29"},(0,r.mdx)("inlineCode",{parentName:"a"},"connect.HandleFunction")),"."),(0,r.mdx)("h4",{id:"rewriterequesturl"},(0,r.mdx)("inlineCode",{parentName:"h4"},"rewriteRequestUrl")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"string => string")),(0,r.mdx)("p",null,"A function that will be called every time Metro processes a URL, after normalization of non-standard query-string delimiters using ",(0,r.mdx)("a",{parentName:"p",href:"https://www.npmjs.com/package/jsc-safe-url"},(0,r.mdx)("inlineCode",{parentName:"a"},"jsc-safe-url")),". Metro will use the return value of this function as if it were the original URL provided by the client. This applies to all incoming HTTP requests (after any custom middleware), as well as bundle URLs in ",(0,r.mdx)("inlineCode",{parentName:"p"},"/symbolicate")," request payloads and within the hot reloading protocol."),(0,r.mdx)("h4",{id:"forwardclientlogs"},(0,r.mdx)("inlineCode",{parentName:"h4"},"forwardClientLogs")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"boolean")),(0,r.mdx)("p",null,"Enable forwarding of ",(0,r.mdx)("inlineCode",{parentName:"p"},"client_log")," events (when client logs are ",(0,r.mdx)("a",{parentName:"p",href:"https://github.com/facebook/metro/blob/614ad14a85b22958129ee94e04376b096f03ccb1/packages/metro/src/lib/createWebsocketServer.js#L20"},"configured"),") to the reporter. Defaults to ",(0,r.mdx)("inlineCode",{parentName:"p"},"true"),"."),(0,r.mdx)("hr",null),(0,r.mdx)("h3",{id:"watcher-options"},"Watcher Options"),(0,r.mdx)("p",null,"Options for the filesystem watcher."),(0,r.mdx)("admonition",{type:"note"},(0,r.mdx)("p",{parentName:"admonition"},"Dot notation in this section indicates a nested configuration object, e.g. ",(0,r.mdx)("inlineCode",{parentName:"p"},"watchman.deferStates")," \u2192 ",(0,r.mdx)("inlineCode",{parentName:"p"},"watchman: { deferStates: ... }"),".")),(0,r.mdx)("h4",{id:"additionalexts"},(0,r.mdx)("inlineCode",{parentName:"h4"},"additionalExts")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"Array")),(0,r.mdx)("p",null,"The extensions which Metro should watch in addition to ",(0,r.mdx)("inlineCode",{parentName:"p"},"sourceExts"),", but which will not be automatically tried by the resolver."),(0,r.mdx)("p",null,"Therefore, the two behavior differences from ",(0,r.mdx)("inlineCode",{parentName:"p"},"resolver.sourceExts")," when importing a module are:"),(0,r.mdx)("ul",null,(0,r.mdx)("li",{parentName:"ul"},"Modules can only be required when fully specified (e.g. ",(0,r.mdx)("inlineCode",{parentName:"li"},"import moduleA from 'moduleA.mjs'"),")."),(0,r.mdx)("li",{parentName:"ul"},"No platform-specific resolution is performed.")),(0,r.mdx)("p",null,"Defaults to ",(0,r.mdx)("inlineCode",{parentName:"p"},"['cjs', 'mjs']"),"."),(0,r.mdx)("h4",{id:"healthcheckenabled"},(0,r.mdx)("inlineCode",{parentName:"h4"},"healthCheck.enabled")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"boolean")),(0,r.mdx)("p",null,"Whether to periodically check the health of the filesystem watcher by writing a temporary file to the project and waiting for it to be observed."),(0,r.mdx)("p",null,"The default value is ",(0,r.mdx)("inlineCode",{parentName:"p"},"false"),"."),(0,r.mdx)("h4",{id:"healthcheckfileprefix"},(0,r.mdx)("inlineCode",{parentName:"h4"},"healthCheck.filePrefix")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"string")),(0,r.mdx)("p",null,"If watcher health checks are enabled, this property controls the name of the temporary file that will be written into the project filesystem."),(0,r.mdx)("p",null,"The default value is ",(0,r.mdx)("inlineCode",{parentName:"p"},"'.metro-health-check'"),"."),(0,r.mdx)("admonition",{type:"note"},(0,r.mdx)("p",{parentName:"admonition"},"There's no need to commit health check files to source control. If you choose to enable health checks in your project, make sure you add ",(0,r.mdx)("inlineCode",{parentName:"p"},".metro-health-check*")," to your ",(0,r.mdx)("inlineCode",{parentName:"p"},".gitignore")," file to avoid generating unnecessary changes.")),(0,r.mdx)("h4",{id:"healthcheckinterval"},(0,r.mdx)("inlineCode",{parentName:"h4"},"healthCheck.interval")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"number")),(0,r.mdx)("p",null,"If watcher health checks are enabled, this property controls how often they occur (in milliseconds)."),(0,r.mdx)("p",null,"The default value is 30000."),(0,r.mdx)("h4",{id:"healthchecktimeout"},(0,r.mdx)("inlineCode",{parentName:"h4"},"healthCheck.timeout")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"number")),(0,r.mdx)("p",null,"If watcher health checks are enabled, this property controls the time (in milliseconds) Metro will wait for a file change to be observed before considering the check to have failed."),(0,r.mdx)("p",null,"The default value is 5000."),(0,r.mdx)("h4",{id:"watchmandeferstates"},(0,r.mdx)("inlineCode",{parentName:"h4"},"watchman.deferStates")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"Array")),(0,r.mdx)("p",null,"Applies when using Watchman. Metro will ",(0,r.mdx)("a",{parentName:"p",href:"https://facebook.github.io/watchman/docs/cmd/subscribe.html#defer"},"defer processing filesystem updates")," while these ",(0,r.mdx)("a",{parentName:"p",href:"https://facebook.github.io/watchman/docs/cmd/state-enter.html"},"states")," are asserted in the watch. This is useful for debouncing builds while the filesystem hasn't settled, e.g. during large source control operations."),(0,r.mdx)("p",null,"The default value is ",(0,r.mdx)("inlineCode",{parentName:"p"},"['hg.update']"),"."),(0,r.mdx)("h2",{id:"merging-configurations"},"Merging Configurations"),(0,r.mdx)("p",null,"Using the ",(0,r.mdx)("inlineCode",{parentName:"p"},"metro-config")," package it is possible to merge multiple configurations together."),(0,r.mdx)("table",null,(0,r.mdx)("thead",{parentName:"table"},(0,r.mdx)("tr",{parentName:"thead"},(0,r.mdx)("th",{parentName:"tr",align:null},"Method"),(0,r.mdx)("th",{parentName:"tr",align:null},"Description"))),(0,r.mdx)("tbody",{parentName:"table"},(0,r.mdx)("tr",{parentName:"tbody"},(0,r.mdx)("td",{parentName:"tr",align:null},(0,r.mdx)("inlineCode",{parentName:"td"},"mergeConfig(...configs): MergedConfig")),(0,r.mdx)("td",{parentName:"tr",align:null},"Returns the merged configuration of two or more configuration objects.")))),(0,r.mdx)("admonition",{type:"note"},(0,r.mdx)("p",{parentName:"admonition"},"Arrays and function based config parameters do not deeply merge and will instead override any pre-existing config parameters.\nThis allows overriding and removing default config parameters such as ",(0,r.mdx)("inlineCode",{parentName:"p"},"platforms")," or ",(0,r.mdx)("inlineCode",{parentName:"p"},"getModulesRunBeforeMainModule")," that may not be required in your environment.")),(0,r.mdx)("h4",{id:"merging-example"},"Merging Example"),(0,r.mdx)("pre",null,(0,r.mdx)("code",{parentName:"pre",className:"language-js"},"// metro.config.js\nconst { mergeConfig } = require('metro-config');\n\nconst configA = {\n /* general options */\n\n resolver: {\n /* resolver options */\n },\n transformer: {\n /* transformer options */\n },\n serializer: {\n /* serializer options */\n },\n server: {\n /* server options */\n }\n};\n\nconst configB = {\n /* general options */\n\n resolver: {\n /* resolver options */\n },\n transformer: {\n /* transformer options */\n },\n serializer: {\n /* serializer options */\n },\n server: {\n /* server options */\n }\n};\n\nmodule.exports = mergeConfig(configA, configB);\n")))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/ba2a2799.cb6433e7.js b/assets/js/ba2a2799.cb6433e7.js deleted file mode 100644 index 383f2f410a..0000000000 --- a/assets/js/ba2a2799.cb6433e7.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkmetro_website=self.webpackChunkmetro_website||[]).push([[122],{3905:(e,n,t)=>{t.r(n),t.d(n,{MDXContext:()=>m,MDXProvider:()=>c,mdx:()=>f,useMDXComponents:()=>p,withMDXComponents:()=>s});var a=t(67294);function o(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function r(){return r=Object.assign||function(e){for(var n=1;n=0||(o[t]=e[t]);return o}(e,n);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(o[t]=e[t])}return o}var m=a.createContext({}),s=function(e){return function(n){var t=p(n.components);return a.createElement(e,r({},n,{components:t}))}},p=function(e){var n=a.useContext(m),t=n;return e&&(t="function"==typeof e?e(n):l(l({},n),e)),t},c=function(e){var n=p(e.components);return a.createElement(m.Provider,{value:n},e.children)},u="mdxType",h={inlineCode:"code",wrapper:function(e){var n=e.children;return a.createElement(a.Fragment,{},n)}},x=a.forwardRef((function(e,n){var t=e.components,o=e.mdxType,r=e.originalType,i=e.parentName,m=d(e,["components","mdxType","originalType","parentName"]),s=p(t),c=o,u=s["".concat(i,".").concat(c)]||s[c]||h[c]||r;return t?a.createElement(u,l(l({ref:n},m),{},{components:t})):a.createElement(u,l({ref:n},m))}));function f(e,n){var t=arguments,o=n&&n.mdxType;if("string"==typeof e||o){var r=t.length,i=new Array(r);i[0]=x;var l={};for(var d in n)hasOwnProperty.call(n,d)&&(l[d]=n[d]);l.originalType=e,l[u]="string"==typeof e?e:o,i[1]=l;for(var m=2;m{t.r(n),t.d(n,{assets:()=>s,contentTitle:()=>d,default:()=>h,frontMatter:()=>l,metadata:()=>m,toc:()=>p});var a=t(87462),o=t(63366),r=(t(67294),t(3905)),i=["components"],l={id:"configuration",title:"Configuring Metro"},d=void 0,m={unversionedId:"configuration",id:"configuration",title:"Configuring Metro",description:"A Metro config can be created in these three ways (ordered by priority):",source:"@site/../docs/Configuration.md",sourceDirName:".",slug:"/configuration",permalink:"/docs/configuration",draft:!1,editUrl:"https://github.com/facebook/metro/edit/main/docs/../docs/Configuration.md",tags:[],version:"current",lastUpdatedAt:1725545477,formattedLastUpdatedAt:"Sep 5, 2024",frontMatter:{id:"configuration",title:"Configuring Metro"},sidebar:"docs",previous:{title:"Module API",permalink:"/docs/module-api"},next:{title:"Metro CLI Options",permalink:"/docs/cli"}},s={},p=[{value:"Configuration Structure",id:"configuration-structure",level:2},{value:"General Options",id:"general-options",level:3},{value:"cacheStores",id:"cachestores",level:4},{value:"cacheVersion",id:"cacheversion",level:4},{value:"projectRoot",id:"projectroot",level:4},{value:"watchFolders",id:"watchfolders",level:4},{value:"transformerPath",id:"transformerpath",level:4},{value:"reporter",id:"reporter",level:4},{value:"resetCache",id:"resetcache",level:4},{value:"stickyWorkers",id:"stickyworkers",level:4},{value:"maxWorkers",id:"maxworkers",level:4},{value:"fileMapCacheDirectory",id:"filemapcachedirectory",level:4},{value:'hasteMapCacheDirectory
Deprecated
',id:"hastemapcachedirectory-deprecated",level:4},{value:"unstable_perfLoggerFactory",id:"unstable_perfloggerfactory",level:4},{value:"Resolver Options",id:"resolver-options",level:3},{value:"assetExts",id:"assetexts",level:4},{value:"sourceExts",id:"sourceexts",level:4},{value:"resolverMainFields",id:"resolvermainfields",level:4},{value:"disableHierarchicalLookup",id:"disablehierarchicallookup",level:4},{value:"emptyModulePath",id:"emptymodulepath",level:4},{value:"enableGlobalPackages",id:"enableglobalpackages",level:4},{value:"extraNodeModules",id:"extranodemodules",level:4},{value:"nodeModulesPaths",id:"nodemodulespaths",level:4},{value:"resolveRequest",id:"resolverequest",level:4},{value:"useWatchman",id:"usewatchman",level:4},{value:"blockList",id:"blocklist",level:4},{value:"hasteImplModulePath",id:"hasteimplmodulepath",level:4},{value:"platforms",id:"platforms",level:4},{value:"requireCycleIgnorePatterns",id:"requirecycleignorepatterns",level:4},{value:'unstable_conditionNames
Experimental
',id:"unstable_conditionnames-experimental",level:4},{value:'unstable_conditionsByPlatform
Experimental
',id:"unstable_conditionsbyplatform-experimental",level:4},{value:'unstable_enablePackageExports
Experimental
',id:"unstable_enablepackageexports-experimental",level:4},{value:'unstable_enableSymlinks
Experimental
',id:"unstable_enablesymlinks-experimental",level:4},{value:"Transformer Options",id:"transformer-options",level:3},{value:'asyncRequireModulePath
Deprecated
',id:"asyncrequiremodulepath-deprecated",level:4},{value:"dynamicDepsInPackages",id:"dynamicdepsinpackages",level:4},{value:"getTransformOptions",id:"gettransformoptions",level:4},{value:"minifierPath",id:"minifierpath",level:4},{value:"minifierConfig",id:"minifierconfig",level:4},{value:"optimizationSizeLimit",id:"optimizationsizelimit",level:4},{value:"React Native Only",id:"react-native-only",level:4},{value:"assetPlugins",id:"assetplugins",level:4},{value:"assetRegistryPath",id:"assetregistrypath",level:4},{value:"Babel-specific transformer options",id:"babel-specific-transformer-options",level:3},{value:"babelTransformerPath",id:"babeltransformerpath",level:4},{value:"enableBabelRCLookup",id:"enablebabelrclookup",level:4},{value:"enableBabelRuntime",id:"enablebabelruntime",level:4},{value:"hermesParser",id:"hermesparser",level:4},{value:"Serializer Options",id:"serializer-options",level:3},{value:"getRunModuleStatement",id:"getrunmodulestatement",level:4},{value:"createModuleIdFactory",id:"createmoduleidfactory",level:4},{value:"getPolyfills",id:"getpolyfills",level:4},{value:"getModulesRunBeforeMainModule",id:"getmodulesrunbeforemainmodule",level:4},{value:"processModuleFilter",id:"processmodulefilter",level:4},{value:"isThirdPartyModule",id:"isthirdpartymodule",level:4},{value:"Server Options",id:"server-options",level:3},{value:"port",id:"port",level:4},{value:"useGlobalHotkey",id:"useglobalhotkey",level:4},{value:'enhanceMiddleware
Deprecated
',id:"enhancemiddleware-deprecated",level:4},{value:"rewriteRequestUrl",id:"rewriterequesturl",level:4},{value:"forwardClientLogs",id:"forwardclientlogs",level:4},{value:"Watcher Options",id:"watcher-options",level:3},{value:"additionalExts",id:"additionalexts",level:4},{value:"healthCheck.enabled",id:"healthcheckenabled",level:4},{value:"healthCheck.filePrefix",id:"healthcheckfileprefix",level:4},{value:"healthCheck.interval",id:"healthcheckinterval",level:4},{value:"healthCheck.timeout",id:"healthchecktimeout",level:4},{value:"watchman.deferStates",id:"watchmandeferstates",level:4},{value:"Merging Configurations",id:"merging-configurations",level:2},{value:"Merging Example",id:"merging-example",level:4}],c={toc:p},u="wrapper";function h(e){var n=e.components,t=(0,o.Z)(e,i);return(0,r.mdx)(u,(0,a.Z)({},c,t,{components:n,mdxType:"MDXLayout"}),(0,r.mdx)("p",null,"A Metro config can be created in these three ways (ordered by priority):"),(0,r.mdx)("ol",null,(0,r.mdx)("li",{parentName:"ol"},(0,r.mdx)("inlineCode",{parentName:"li"},"metro.config.js")),(0,r.mdx)("li",{parentName:"ol"},(0,r.mdx)("inlineCode",{parentName:"li"},"metro.config.json")),(0,r.mdx)("li",{parentName:"ol"},"The ",(0,r.mdx)("inlineCode",{parentName:"li"},"metro")," field in ",(0,r.mdx)("inlineCode",{parentName:"li"},"package.json"))),(0,r.mdx)("p",null,"You can also give a custom file to the configuration by specifying ",(0,r.mdx)("inlineCode",{parentName:"p"},"--config ")," when calling the CLI."),(0,r.mdx)("admonition",{type:"note"},(0,r.mdx)("p",{parentName:"admonition"},"When Metro is started via the React Native CLI, some defaults are different from those mentioned below.\nSee the ",(0,r.mdx)("a",{parentName:"p",href:"https://github.com/facebook/react-native/blob/main/packages/community-cli-plugin/src/utils/loadMetroConfig.js"},"React Native repository")," for details.")),(0,r.mdx)("h2",{id:"configuration-structure"},"Configuration Structure"),(0,r.mdx)("p",null,"The configuration is based on ",(0,r.mdx)("a",{parentName:"p",href:"/docs/concepts"},"our concepts"),", which means that for every module we have a separate config option. A common configuration structure in Metro looks like this:"),(0,r.mdx)("pre",null,(0,r.mdx)("code",{parentName:"pre",className:"language-js"},"module.exports = {\n /* general options */\n\n resolver: {\n /* resolver options */\n },\n transformer: {\n /* transformer options */\n },\n serializer: {\n /* serializer options */\n },\n server: {\n /* server options */\n },\n watcher: {\n /* watcher options */\n watchman: {\n /* Watchman-specific options */\n }\n }\n};\n")),(0,r.mdx)("h3",{id:"general-options"},"General Options"),(0,r.mdx)("h4",{id:"cachestores"},(0,r.mdx)("inlineCode",{parentName:"h4"},"cacheStores")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"CacheStores")," (see details below)"),(0,r.mdx)("p",null,"A list of storage adapters for Metro's ",(0,r.mdx)("a",{parentName:"p",href:"/docs/caching"},"transformer cache"),". This can be any combination of ",(0,r.mdx)("a",{parentName:"p",href:"/docs/caching#built-in-cache-stores"},"built-in cache stores")," and ",(0,r.mdx)("a",{parentName:"p",href:"/docs/caching#custom-cache-stores"},"custom cache stores"),". Defaults to using a temporary directory on disk as the only cache store."),(0,r.mdx)("p",null,"When Metro needs to transform a module, it first computes a machine-independent cache key for that file, and uses it to try to read from each of the stores in order. Once Metro has obtained the output of the transformer (whether already cached or not), it writes the transform result to ",(0,r.mdx)("em",{parentName:"p"},"all")," of the stores that returned ",(0,r.mdx)("inlineCode",{parentName:"p"},"null")," (a cache miss) for that key."),(0,r.mdx)("pre",null,(0,r.mdx)("code",{parentName:"pre",className:"language-flow"},"type CacheStores =\n | Array>\n | ((MetroCache) => Array<\n CacheStore\n >);\n\n// The exports of 'metro-cache'\ntype MetroCache = {\n FileStore,\n AutoCleanFileStore,\n HttpStore,\n HttpGetStore,\n ...\n};\n\ntype JsonSerializable = /* Any JSON-serializable value */;\n")),(0,r.mdx)("h4",{id:"cacheversion"},(0,r.mdx)("inlineCode",{parentName:"h4"},"cacheVersion")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"string")),(0,r.mdx)("p",null,"An arbitrary string appended to all cache keys in the project before they are hashed. There is generally no need to set this explicitly, as Metro will automatically derive the correct cache keys from your project config and the contents of source files."),(0,r.mdx)("h4",{id:"projectroot"},(0,r.mdx)("inlineCode",{parentName:"h4"},"projectRoot")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"string")),(0,r.mdx)("p",null,"The root folder of your project. If your project depends on any files outside this root, their containing directories must be listed in ",(0,r.mdx)("a",{parentName:"p",href:"#watchfolders"},(0,r.mdx)("inlineCode",{parentName:"a"},"watchFolders")),"."),(0,r.mdx)("admonition",{type:"note"},(0,r.mdx)("p",{parentName:"admonition"},"If your Metro project is developed in a monorepo and includes files from multiple logical packages, you'll generally want to set ",(0,r.mdx)("inlineCode",{parentName:"p"},"projectRoot")," to the root of your repository, or at least high enough in the hierarchy that all relevant files are reachable without separately configuring ",(0,r.mdx)("inlineCode",{parentName:"p"},"watchFolders"),".")),(0,r.mdx)("h4",{id:"watchfolders"},(0,r.mdx)("inlineCode",{parentName:"h4"},"watchFolders")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"Array")),(0,r.mdx)("p",null,"A list of directories outside of ",(0,r.mdx)("a",{parentName:"p",href:"#projectroot"},(0,r.mdx)("inlineCode",{parentName:"a"},"projectRoot"))," that can contain source files for the project."),(0,r.mdx)("admonition",{type:"note"},(0,r.mdx)("p",{parentName:"admonition"},"Despite the naming of this option, it isn't related solely to file watching. Even in an offline build (for example, in CI), all files must be visible to Metro through the combination of ",(0,r.mdx)("inlineCode",{parentName:"p"},"watchFolders")," and ",(0,r.mdx)("inlineCode",{parentName:"p"},"projectRoot"),".")),(0,r.mdx)("h4",{id:"transformerpath"},(0,r.mdx)("inlineCode",{parentName:"h4"},"transformerPath")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"string")),(0,r.mdx)("p",null,"The absolute path of a module (or a package name resolvable from the ",(0,r.mdx)("inlineCode",{parentName:"p"},"metro")," package) that implements a transformer."),(0,r.mdx)("p",null,"See the implementation of Metro's default transformer (",(0,r.mdx)("a",{parentName:"p",href:"https://github.com/facebook/metro/blob/main/packages/metro-transform-worker/src/index.js"},(0,r.mdx)("inlineCode",{parentName:"a"},"metro-transform-worker")),") for more information about the transformer interface."),(0,r.mdx)("h4",{id:"reporter"},(0,r.mdx)("inlineCode",{parentName:"h4"},"reporter")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"{update: (event: ReportableEvent) => void}")),(0,r.mdx)("p",null,"Used to report the status of the bundler during the bundling process. The default implementation prints most events to the terminal."),(0,r.mdx)("p",null,"See also the ",(0,r.mdx)("a",{parentName:"p",href:"https://github.com/facebook/metro/blob/main/packages/metro/src/lib/reporting.js"},"definition of ",(0,r.mdx)("inlineCode",{parentName:"a"},"ReportableEvent"))," in Metro's source code."),(0,r.mdx)("h4",{id:"resetcache"},(0,r.mdx)("inlineCode",{parentName:"h4"},"resetCache")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"boolean")),(0,r.mdx)("p",null,"If ",(0,r.mdx)("inlineCode",{parentName:"p"},"true"),", Metro will reset the transformer cache (see ",(0,r.mdx)("a",{parentName:"p",href:"#cachestores"},(0,r.mdx)("inlineCode",{parentName:"a"},"cacheStores")),") and the file map cache (see ",(0,r.mdx)("a",{parentName:"p",href:"#filemapcachedirectory"},(0,r.mdx)("inlineCode",{parentName:"a"},"fileMapCacheDirectory")),") on startup."),(0,r.mdx)("h4",{id:"stickyworkers"},(0,r.mdx)("inlineCode",{parentName:"h4"},"stickyWorkers")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"boolean")),(0,r.mdx)("p",null,"If ",(0,r.mdx)("inlineCode",{parentName:"p"},"true"),", Metro will use a stable mapping from files to transformer workers, so the same file is always transformed by the same worker. This can improve initial build performance if the transformer is expensive to initialize, but can slow down concurrent builds with different configurations (e.g. multiple React Native apps connected to one Metro server). Defaults to ",(0,r.mdx)("inlineCode",{parentName:"p"},"true"),"."),(0,r.mdx)("h4",{id:"maxworkers"},(0,r.mdx)("inlineCode",{parentName:"h4"},"maxWorkers")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"number")),(0,r.mdx)("p",null,"The number of workers to use for parallel processing in Metro. Defaults to approximately half of the number of cores available on the machine, as reported by ",(0,r.mdx)("a",{parentName:"p",href:"https://nodejs.org/api/os.html#oscpus"},(0,r.mdx)("inlineCode",{parentName:"a"},"os.cpus()")),"."),(0,r.mdx)("admonition",{type:"note"},(0,r.mdx)("ol",{parentName:"admonition"},(0,r.mdx)("li",{parentName:"ol"},"Values exceeding the number of available cores have no effect."),(0,r.mdx)("li",{parentName:"ol"},"If ",(0,r.mdx)("inlineCode",{parentName:"li"},"maxWorkers")," is set to 1 or lower, worker code will run in the main Metro process instead of concurrently."),(0,r.mdx)("li",{parentName:"ol"},"Metro has two separate worker pools - one for transformation and one for building the file map. Each pool has its worker count set to ",(0,r.mdx)("inlineCode",{parentName:"li"},"maxWorkers")," independently."))),(0,r.mdx)("h4",{id:"filemapcachedirectory"},(0,r.mdx)("inlineCode",{parentName:"h4"},"fileMapCacheDirectory")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"string")),(0,r.mdx)("p",null,"The path to the ",(0,r.mdx)("inlineCode",{parentName:"p"},"metro-file-map")," cache directory, defaults to ",(0,r.mdx)("inlineCode",{parentName:"p"},"os.tmpdir()"),"."),(0,r.mdx)("h4",{id:"hastemapcachedirectory-deprecated"},(0,r.mdx)("inlineCode",{parentName:"h4"},"hasteMapCacheDirectory")," ",(0,r.mdx)("div",{class:"label deprecated"},"Deprecated")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"string")),(0,r.mdx)("p",null,"Alias of ",(0,r.mdx)("a",{parentName:"p",href:"#filemapcachedirectory"},(0,r.mdx)("inlineCode",{parentName:"a"},"fileMapCacheDirectory"))),(0,r.mdx)("h4",{id:"unstable_perfloggerfactory"},(0,r.mdx)("inlineCode",{parentName:"h4"},"unstable_perfLoggerFactory")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"PerfLoggerFactory")),(0,r.mdx)("p",null,"A logger factory function that can be used to get insights about Metro performance timings and metadata for events including startup, bundling and HMR. Metro expects ",(0,r.mdx)("inlineCode",{parentName:"p"},"unstable_perfLoggerFactory")," to have the following signature:"),(0,r.mdx)("pre",null,(0,r.mdx)("code",{parentName:"pre",className:"language-flow"},"function unstable_perfLoggerFactory(\n type: string,\n opts: $ReadOnly<{\n key?: string\n }>,\n): RootPerfLogger {\n // ...\n};\n")),(0,r.mdx)("ul",null,(0,r.mdx)("li",{parentName:"ul"},(0,r.mdx)("strong",{parentName:"li"},(0,r.mdx)("inlineCode",{parentName:"strong"},"type"))," Type of event being logged, e.g. ",(0,r.mdx)("inlineCode",{parentName:"li"},"'STARTUP'"),", ",(0,r.mdx)("inlineCode",{parentName:"li"},"'BUNDLING_REQUEST'"),", ",(0,r.mdx)("inlineCode",{parentName:"li"},"'HMR'"),". See type definition of ",(0,r.mdx)("a",{parentName:"li",href:"https://github.com/facebook/metro/blob/main/packages/metro-config/src/configTypes.flow.js"},"PerfLoggerFactory")," for a full list of event types."),(0,r.mdx)("li",{parentName:"ul"},(0,r.mdx)("strong",{parentName:"li"},(0,r.mdx)("inlineCode",{parentName:"strong"},"opts")),(0,r.mdx)("ul",{parentName:"li"},(0,r.mdx)("li",{parentName:"ul"},(0,r.mdx)("strong",{parentName:"li"},(0,r.mdx)("inlineCode",{parentName:"strong"},"key")),": An opaque identifier to distinguish between instances of an event type (e.g. multiple, possibly concurrent, HMR requests).")))),(0,r.mdx)("p",null,(0,r.mdx)("inlineCode",{parentName:"p"},"unstable_perfLoggerFactory")," should return an object implementing the ",(0,r.mdx)("a",{parentName:"p",href:"https://github.com/facebook/metro/blob/main/packages/metro-config/src/configTypes.flow.js"},"RootPerfLogger")," interface. For example, a factory function returning a no-op RootPerfLogger could be implemented as follows:"),(0,r.mdx)("pre",null,(0,r.mdx)("code",{parentName:"pre",className:"language-javascript"},"const unstable_perfLoggerFactory = (type, factoryOpts) => {\n const getLogger = subSpanLabel => {\n const logger = {\n start(opts) {},\n end(status, opts) {},\n subSpan(label) {\n return getLogger(`${subSpanLabel ?? ''}/${label}`);\n },\n point(name, opts) {},\n annotate(annotations) {},\n };\n return logger;\n };\n\n return getLogger();\n};\n")),(0,r.mdx)("hr",null),(0,r.mdx)("h3",{id:"resolver-options"},"Resolver Options"),(0,r.mdx)("h4",{id:"assetexts"},(0,r.mdx)("inlineCode",{parentName:"h4"},"assetExts")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"Array")),(0,r.mdx)("p",null,"The list of asset file extensions to include in the bundle. For example, including ",(0,r.mdx)("inlineCode",{parentName:"p"},"'ttf'")," allows Metro bundles to reference ",(0,r.mdx)("inlineCode",{parentName:"p"},".ttf")," files. This is used primarily to enable React Native's ",(0,r.mdx)("a",{parentName:"p",href:"https://reactnative.dev/docs/images"},"image asset support"),". The default list includes many common image, video and audio file extensions. See ",(0,r.mdx)("a",{parentName:"p",href:"https://github.com/facebook/metro/blob/main/packages/metro-config/src/defaults/defaults.js#L16"},"Metro's source code")," for the full list."),(0,r.mdx)("h4",{id:"sourceexts"},(0,r.mdx)("inlineCode",{parentName:"h4"},"sourceExts")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"Array")),(0,r.mdx)("p",null,"The list of source file extensions to include in the bundle. For example, including ",(0,r.mdx)("inlineCode",{parentName:"p"},"'ts'")," allows Metro to include ",(0,r.mdx)("inlineCode",{parentName:"p"},".ts")," files in the bundle."),(0,r.mdx)("p",null,"The order of these extensions defines the order to match files on disk. For more information, see ",(0,r.mdx)("a",{parentName:"p",href:"https://metrobundler.dev/docs/resolution"},"Module Resolution"),"."),(0,r.mdx)("p",null,"Defaults to ",(0,r.mdx)("inlineCode",{parentName:"p"},"['js', 'jsx', 'json', 'ts', 'tsx']"),"."),(0,r.mdx)("h4",{id:"resolvermainfields"},(0,r.mdx)("inlineCode",{parentName:"h4"},"resolverMainFields")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"Array")),(0,r.mdx)("p",null,"The list of fields in ",(0,r.mdx)("inlineCode",{parentName:"p"},"package.json")," that Metro will treat as describing a package's entry points. The default is ",(0,r.mdx)("inlineCode",{parentName:"p"},"['browser', 'main']"),", so the resolver will use the ",(0,r.mdx)("inlineCode",{parentName:"p"},"browser")," field if it exists and ",(0,r.mdx)("inlineCode",{parentName:"p"},"main")," otherwise."),(0,r.mdx)("p",null,"Metro's default resolver processes each of these fields according to the ",(0,r.mdx)("a",{parentName:"p",href:"https://github.com/defunctzombie/package-browser-field-spec"},(0,r.mdx)("inlineCode",{parentName:"a"},"browser")," field spec"),", including the ability to ",(0,r.mdx)("a",{parentName:"p",href:"https://github.com/defunctzombie/package-browser-field-spec#replace-specific-files---advanced"},"replace")," and ",(0,r.mdx)("a",{parentName:"p",href:"https://github.com/defunctzombie/package-browser-field-spec#ignore-a-module"},"ignore")," specific files. For more information, see ",(0,r.mdx)("a",{parentName:"p",href:"https://metrobundler.dev/docs/resolution"},"Module Resolution"),"."),(0,r.mdx)("admonition",{type:"note"},(0,r.mdx)("p",{parentName:"admonition"},"When using React Native, ",(0,r.mdx)("inlineCode",{parentName:"p"},"resolverMainFields")," defaults to ",(0,r.mdx)("inlineCode",{parentName:"p"},"['react-native', 'browser', 'main']"),".")),(0,r.mdx)("h4",{id:"disablehierarchicallookup"},(0,r.mdx)("inlineCode",{parentName:"h4"},"disableHierarchicalLookup")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"boolean")),(0,r.mdx)("p",null,"Whether to disable ",(0,r.mdx)("a",{parentName:"p",href:"https://nodejs.org/api/modules.html#modules_loading_from_node_modules_folders"},"looking up modules in ",(0,r.mdx)("inlineCode",{parentName:"a"},"node_modules")," folders"),". This only affects the default search through the directory tree, not other Metro options like ",(0,r.mdx)("inlineCode",{parentName:"p"},"extraNodeModules")," or ",(0,r.mdx)("inlineCode",{parentName:"p"},"nodeModulesPaths"),". Defaults to ",(0,r.mdx)("inlineCode",{parentName:"p"},"false"),"."),(0,r.mdx)("h4",{id:"emptymodulepath"},(0,r.mdx)("inlineCode",{parentName:"h4"},"emptyModulePath")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"string")),(0,r.mdx)("p",null,'What module to use as the canonical "empty" module when one is needed. Defaults to using the one included in ',(0,r.mdx)("inlineCode",{parentName:"p"},"metro-runtime"),". You only need to change this if Metro is installed outside of your project."),(0,r.mdx)("h4",{id:"enableglobalpackages"},(0,r.mdx)("inlineCode",{parentName:"h4"},"enableGlobalPackages")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"boolean"),"."),(0,r.mdx)("p",null,"Whether to automatically resolve references to first-party packages (e.g. workspaces) in your project. Any ",(0,r.mdx)("inlineCode",{parentName:"p"},"package.json")," file with a valid ",(0,r.mdx)("inlineCode",{parentName:"p"},"name")," property within ",(0,r.mdx)("inlineCode",{parentName:"p"},"projectRoot")," or ",(0,r.mdx)("inlineCode",{parentName:"p"},"watchFolders")," (but outside of ",(0,r.mdx)("inlineCode",{parentName:"p"},"node_modules"),") counts as a package for this purpose. Defaults to ",(0,r.mdx)("inlineCode",{parentName:"p"},"false"),"."),(0,r.mdx)("h4",{id:"extranodemodules"},(0,r.mdx)("inlineCode",{parentName:"h4"},"extraNodeModules")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"{[string]: string}")),(0,r.mdx)("p",null,"A mapping of package names to directories that is consulted after the standard lookup through ",(0,r.mdx)("inlineCode",{parentName:"p"},"node_modules")," as well as any ",(0,r.mdx)("a",{parentName:"p",href:"#nodemodulespaths"},(0,r.mdx)("inlineCode",{parentName:"a"},"nodeModulesPaths")),". For more information, see ",(0,r.mdx)("a",{parentName:"p",href:"https://metrobundler.dev/docs/resolution"},"Module Resolution"),"."),(0,r.mdx)("h4",{id:"nodemodulespaths"},(0,r.mdx)("inlineCode",{parentName:"h4"},"nodeModulesPaths")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"Array")),(0,r.mdx)("p",null,"A list of paths to check for modules after looking through all ",(0,r.mdx)("inlineCode",{parentName:"p"},"node_modules")," directories. This is useful if third-party dependencies are installed in a different location outside of the direct path of source files. For more information, see ",(0,r.mdx)("a",{parentName:"p",href:"https://metrobundler.dev/docs/resolution"},"Module Resolution"),"."),(0,r.mdx)("h4",{id:"resolverequest"},(0,r.mdx)("inlineCode",{parentName:"h4"},"resolveRequest")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("a",{parentName:"p",href:"/docs/resolution#resolverequest-customresolver"},(0,r.mdx)("inlineCode",{parentName:"a"},"?CustomResolver"))),(0,r.mdx)("p",null,"An optional function used to override the default resolution algorithm. This is particularly useful for cases where aliases or custom protocols are used. For example:"),(0,r.mdx)("pre",null,(0,r.mdx)("code",{parentName:"pre",className:"language-javascript"},"resolveRequest: (context, moduleName, platform) => {\n if (moduleName.startsWith('my-custom-resolver:')) {\n // Logic to resolve the module name to a file path...\n // NOTE: Throw an error if there is no resolution.\n return {\n filePath: 'path/to/file',\n type: 'sourceFile',\n };\n }\n // Optionally, chain to the standard Metro resolver.\n return context.resolveRequest(context, moduleName, platform);\n}\n")),(0,r.mdx)("p",null,"For more information on customizing the resolver, see ",(0,r.mdx)("a",{parentName:"p",href:"https://metrobundler.dev/docs/resolution"},"Module Resolution"),"."),(0,r.mdx)("h4",{id:"usewatchman"},(0,r.mdx)("inlineCode",{parentName:"h4"},"useWatchman")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"boolean")),(0,r.mdx)("p",null,"If set to ",(0,r.mdx)("inlineCode",{parentName:"p"},"false"),", prevents Metro from using Watchman (even if it's installed)."),(0,r.mdx)("h4",{id:"blocklist"},(0,r.mdx)("inlineCode",{parentName:"h4"},"blockList")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"RegExp")," or ",(0,r.mdx)("inlineCode",{parentName:"p"},"Array")),(0,r.mdx)("p",null,"A regular expression (or list of regular expressions) defining which paths to exclude from Metro's file map. Files whose absolute paths match these patterns are effectively hidden from Metro and cannot be resolved or imported in the current project."),(0,r.mdx)("h4",{id:"hasteimplmodulepath"},(0,r.mdx)("inlineCode",{parentName:"h4"},"hasteImplModulePath")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"?string")),(0,r.mdx)("p",null,"The path to the Haste implementation for the current project. Haste is an opt-in mechanism for importing modules by their globally-unique name anywhere in the project, e.g. ",(0,r.mdx)("inlineCode",{parentName:"p"},"import Foo from 'Foo'"),"."),(0,r.mdx)("p",null,"Metro expects this module to have the following signature:"),(0,r.mdx)("pre",null,(0,r.mdx)("code",{parentName:"pre",className:"language-flow"},"module.exports = {\n getHasteName(filePath: string): ?string {\n // ...\n },\n};\n")),(0,r.mdx)("p",null,(0,r.mdx)("inlineCode",{parentName:"p"},"getHasteName")," should return a short, globally unique name for the module whose path is ",(0,r.mdx)("inlineCode",{parentName:"p"},"filePath"),", or ",(0,r.mdx)("inlineCode",{parentName:"p"},"null")," if the module should not be accessible via Haste."),(0,r.mdx)("h4",{id:"platforms"},(0,r.mdx)("inlineCode",{parentName:"h4"},"platforms")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"Array")),(0,r.mdx)("p",null,"Additional platforms to resolve. Defaults to ",(0,r.mdx)("inlineCode",{parentName:"p"},"['ios', 'android', 'windows', 'web']"),"."),(0,r.mdx)("p",null,"For more information, see ",(0,r.mdx)("a",{parentName:"p",href:"/docs/resolution"},"Module Resolution")," and ",(0,r.mdx)("a",{parentName:"p",href:"https://reactnative.dev/docs/platform-specific-code#platform-specific-extensions"},"React Native's documentation for platform-specific extensions"),"."),(0,r.mdx)("h4",{id:"requirecycleignorepatterns"},(0,r.mdx)("inlineCode",{parentName:"h4"},"requireCycleIgnorePatterns")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"Array")),(0,r.mdx)("p",null,"In development mode, suppress require cycle warnings for any cycle involving a module that matches any of these expressions. This is useful for third-party code and first-party expected cycles."),(0,r.mdx)("p",null,"Note that if you specify your own value for this config option it will replace (not concatenate with) Metro's default."),(0,r.mdx)("p",null,"Defaults to ",(0,r.mdx)("inlineCode",{parentName:"p"},"[/(^|\\/|\\\\)node_modules($|\\/|\\\\)/]"),"."),(0,r.mdx)("h4",{id:"unstable_conditionnames-experimental"},(0,r.mdx)("inlineCode",{parentName:"h4"},"unstable_conditionNames")," ",(0,r.mdx)("div",{class:"label experimental"},"Experimental")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"Array")),(0,r.mdx)("admonition",{type:"note"},(0,r.mdx)("p",{parentName:"admonition"},"This setting will take effect when ",(0,r.mdx)("a",{parentName:"p",href:"#unstable_enablepackageexports-experimental"},(0,r.mdx)("inlineCode",{parentName:"a"},"unstable_enablePackageExports"))," is ",(0,r.mdx)("inlineCode",{parentName:"p"},"true"),". It may not behave as described while this feature is experimental.")),(0,r.mdx)("p",null,"The set of ",(0,r.mdx)("a",{parentName:"p",href:"https://nodejs.org/docs/latest-v18.x/api/packages.html#conditional-exports"},"condition names")," to assert globally when interpreting the ",(0,r.mdx)("a",{parentName:"p",href:"https://nodejs.org/docs/latest-v18.x/api/packages.html#exports"},(0,r.mdx)("inlineCode",{parentName:"a"},'"exports"')," field")," in package.json."),(0,r.mdx)("p",null,"Conditions may be any string value and are resolved in the order specified by each package. Node.js documents a number of ",(0,r.mdx)("a",{parentName:"p",href:"https://nodejs.org/docs/latest-v18.x/api/packages.html#community-conditions-definitions"},"community conditions")," which are commonly used by package authors. The ",(0,r.mdx)("inlineCode",{parentName:"p"},"default")," condition is always matched."),(0,r.mdx)("p",null,"Defaults to ",(0,r.mdx)("inlineCode",{parentName:"p"},"['require']"),"."),(0,r.mdx)("admonition",{type:"note"},(0,r.mdx)("p",{parentName:"admonition"},"When using React Native, ",(0,r.mdx)("inlineCode",{parentName:"p"},"unstable_conditionNames")," defaults to ",(0,r.mdx)("inlineCode",{parentName:"p"},"['require', 'react-native']"),".")),(0,r.mdx)("h4",{id:"unstable_conditionsbyplatform-experimental"},(0,r.mdx)("inlineCode",{parentName:"h4"},"unstable_conditionsByPlatform")," ",(0,r.mdx)("div",{class:"label experimental"},"Experimental")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"{[platform: string]: Array}")),(0,r.mdx)("admonition",{type:"note"},(0,r.mdx)("p",{parentName:"admonition"},"This setting will take effect when ",(0,r.mdx)("a",{parentName:"p",href:"#unstable_enablepackageexports-experimental"},(0,r.mdx)("inlineCode",{parentName:"a"},"unstable_enablePackageExports"))," is ",(0,r.mdx)("inlineCode",{parentName:"p"},"true"),". It may not behave as described while this feature is experimental.")),(0,r.mdx)("p",null,"The set of additional ",(0,r.mdx)("a",{parentName:"p",href:"https://nodejs.org/docs/latest-v18.x/api/packages.html#conditional-exports"},"condition names")," to dynamically assert by platform (see ",(0,r.mdx)("a",{parentName:"p",href:"#platforms"},(0,r.mdx)("inlineCode",{parentName:"a"},"platforms")),") when interpreting the ",(0,r.mdx)("a",{parentName:"p",href:"https://nodejs.org/docs/latest-v18.x/api/packages.html#exports"},(0,r.mdx)("inlineCode",{parentName:"a"},'"exports"')," field")," in package.json."),(0,r.mdx)("p",null,"Matched conditions are merged with ",(0,r.mdx)("a",{parentName:"p",href:"#unstable-conditionnames"},(0,r.mdx)("inlineCode",{parentName:"a"},"unstable_conditionNames"))," before resolution. With the defaults for both options, the conditions ",(0,r.mdx)("inlineCode",{parentName:"p"},"new Set(['require', 'browser'])")," will be asserted when requesting a ",(0,r.mdx)("inlineCode",{parentName:"p"},"web")," bundle, and ",(0,r.mdx)("inlineCode",{parentName:"p"},"new Set(['require'])")," otherwise. Again, these are resolved in the order specified by each package."),(0,r.mdx)("p",null,"Defaults to ",(0,r.mdx)("inlineCode",{parentName:"p"},"\u200c{ web: ['browser'] }"),"."),(0,r.mdx)("h4",{id:"unstable_enablepackageexports-experimental"},(0,r.mdx)("inlineCode",{parentName:"h4"},"unstable_enablePackageExports")," ",(0,r.mdx)("div",{class:"label experimental"},"Experimental")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"boolean")),(0,r.mdx)("p",null,"Enable experimental ",(0,r.mdx)("a",{parentName:"p",href:"https://nodejs.org/docs/latest-v18.x/api/packages.html#package-entry-points"},"Package Exports")," support. Under this mode, Metro will read the ",(0,r.mdx)("a",{parentName:"p",href:"https://nodejs.org/docs/latest-v18.x/api/packages.html#exports"},(0,r.mdx)("inlineCode",{parentName:"a"},'"exports"')," field")," in ",(0,r.mdx)("inlineCode",{parentName:"p"},"package.json")," files when present and use it to resolve package entry points."),(0,r.mdx)("p",null,"When no match is found in ",(0,r.mdx)("inlineCode",{parentName:"p"},'"exports"'),", Metro will log a warning and fall back to resolving modules without considering ",(0,r.mdx)("inlineCode",{parentName:"p"},'"exports"'),". This makes this mode largely backwards-compatible, with the following exceptions:"),(0,r.mdx)("ul",null,(0,r.mdx)("li",{parentName:"ul"},"If a module is matched in ",(0,r.mdx)("inlineCode",{parentName:"li"},'"exports"'),", ",(0,r.mdx)("a",{parentName:"li",href:"#sourceexts"},(0,r.mdx)("inlineCode",{parentName:"a"},"sourceExts"))," and ",(0,r.mdx)("a",{parentName:"li",href:"#platforms"},(0,r.mdx)("inlineCode",{parentName:"a"},"platforms"))," will not be considered (i.e. platform-specific extensions will not be used). This is done for compatibility with Node."),(0,r.mdx)("li",{parentName:"ul"},"If a module exists at a file path that is also listed in ",(0,r.mdx)("inlineCode",{parentName:"li"},'"exports"'),", and the ",(0,r.mdx)("inlineCode",{parentName:"li"},'"exports"')," entry maps to a different file, the ",(0,r.mdx)("inlineCode",{parentName:"li"},'"exports"')," entry will be preferred.")),(0,r.mdx)("p",null,"Defaults to ",(0,r.mdx)("inlineCode",{parentName:"p"},"false"),"."),(0,r.mdx)("admonition",{type:"note"},(0,r.mdx)("p",{parentName:"admonition"},"In a future release of Metro, this option will become ",(0,r.mdx)("inlineCode",{parentName:"p"},"true")," by default.")),(0,r.mdx)("hr",null),(0,r.mdx)("h4",{id:"unstable_enablesymlinks-experimental"},(0,r.mdx)("inlineCode",{parentName:"h4"},"unstable_enableSymlinks")," ",(0,r.mdx)("div",{class:"label experimental"},"Experimental")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"boolean")),(0,r.mdx)("p",null,"Enable experimental support for projects containing symbolic links (symlinks)."),(0,r.mdx)("p",null,"When enabled, Metro traverses symlinks during module and asset ",(0,r.mdx)("a",{parentName:"p",href:"/docs/resolution"},"resolution"),", instead of ignoring symlinks. Note that, as with any other file Metro needs to resolve, the symlink target ",(0,r.mdx)("em",{parentName:"p"},"must be within configured ",(0,r.mdx)("a",{parentName:"em",href:"#watchfolders"},"watched folders"))," and not otherwise excluded."),(0,r.mdx)("p",null,"Defaults to ",(0,r.mdx)("inlineCode",{parentName:"p"},"true")," since Metro v0.79.0."),(0,r.mdx)("admonition",{type:"info"},(0,r.mdx)("p",{parentName:"admonition"},"For example, if you have a Metro project within a ",(0,r.mdx)("a",{parentName:"p",href:"https://classic.yarnpkg.com/lang/en/docs/workspaces/"},"Yarn workspace")," (a subdirectory of a Yarn workspace root), it's likely you'll want to include your workspace ",(0,r.mdx)("em",{parentName:"p"},"root")," path in your configured ",(0,r.mdx)("a",{parentName:"p",href:"#watchfolders"},(0,r.mdx)("inlineCode",{parentName:"a"},"watchFolders"))," so that Metro can resolve other workspaces or hoisted ",(0,r.mdx)("inlineCode",{parentName:"p"},"node_modules"),". Similarly, to use ",(0,r.mdx)("a",{parentName:"p",href:"https://classic.yarnpkg.com/lang/en/docs/cli/link/"},"linked packages"),", you'll need to list those package source locations (or a containing directory) in ",(0,r.mdx)("a",{parentName:"p",href:"#watchfolders"},(0,r.mdx)("inlineCode",{parentName:"a"},"watchFolders")),".")),(0,r.mdx)("admonition",{type:"note"},(0,r.mdx)("p",{parentName:"admonition"},"In a future release of Metro, this option will be removed (symlink support will be always-on).")),(0,r.mdx)("hr",null),(0,r.mdx)("h3",{id:"transformer-options"},"Transformer Options"),(0,r.mdx)("a",{name:"asyncrequiremodulepath"}),(0,r.mdx)("h4",{id:"asyncrequiremodulepath-deprecated"},(0,r.mdx)("inlineCode",{parentName:"h4"},"asyncRequireModulePath")," ",(0,r.mdx)("div",{class:"label deprecated"},"Deprecated")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"string")),(0,r.mdx)("p",null,"The name of a module that provides the ",(0,r.mdx)("inlineCode",{parentName:"p"},"asyncRequire")," function, which is used to implement ",(0,r.mdx)("a",{parentName:"p",href:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import"},"dynamic ",(0,r.mdx)("inlineCode",{parentName:"a"},"import()"))," at runtime. Defaults to ",(0,r.mdx)("a",{parentName:"p",href:"https://github.com/facebook/metro/blob/main/packages/metro-runtime/src/modules/asyncRequire.js"},(0,r.mdx)("inlineCode",{parentName:"a"},"metro-runtime/src/modules/asyncRequire")),"."),(0,r.mdx)("admonition",{type:"note"},(0,r.mdx)("p",{parentName:"admonition"},"The module named by ",(0,r.mdx)("inlineCode",{parentName:"p"},"asyncRequireModulePath")," is ",(0,r.mdx)("a",{parentName:"p",href:"/docs/resolution"},"resolved")," relative to the module containing the original ",(0,r.mdx)("inlineCode",{parentName:"p"},"import()")," call. In particular, assuming the default value of ",(0,r.mdx)("inlineCode",{parentName:"p"},"asyncRequireModulePath")," is in use, the project must have a compatible version of ",(0,r.mdx)("inlineCode",{parentName:"p"},"metro-runtime")," installed in ",(0,r.mdx)("inlineCode",{parentName:"p"},"node_modules"),".")),(0,r.mdx)("admonition",{type:"info"},(0,r.mdx)("p",{parentName:"admonition"},"In older versions of Metro, a custom ",(0,r.mdx)("inlineCode",{parentName:"p"},"asyncRequireModulePath")," could be used as part of a bundle splitting solution. This usage is now deprecated in favor of the ",(0,r.mdx)("a",{parentName:"p",href:"https://github.com/react-native-community/discussions-and-proposals/blob/main/proposals/0605-lazy-bundling.md#__loadbundleasync-in-metro"},(0,r.mdx)("inlineCode",{parentName:"a"},"__loadBundleAsync"))," API.")),(0,r.mdx)("h4",{id:"dynamicdepsinpackages"},(0,r.mdx)("inlineCode",{parentName:"h4"},"dynamicDepsInPackages")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"'throwAtRuntime' | 'reject'")),(0,r.mdx)("p",null,"Controls how Metro handles dependencies that cannot be statically analyzed at build time. For example, ",(0,r.mdx)("inlineCode",{parentName:"p"},"require('./' + someFunction() + '.js')")," cannot be resolved without knowing what ",(0,r.mdx)("inlineCode",{parentName:"p"},"someFunction()")," will return."),(0,r.mdx)("ul",null,(0,r.mdx)("li",{parentName:"ul"},(0,r.mdx)("strong",{parentName:"li"},(0,r.mdx)("inlineCode",{parentName:"strong"},"'throwAtRuntime'"))," (the default): Metro does not stop bundling, but the ",(0,r.mdx)("inlineCode",{parentName:"li"},"require")," call will throw at runtime."),(0,r.mdx)("li",{parentName:"ul"},(0,r.mdx)("strong",{parentName:"li"},(0,r.mdx)("inlineCode",{parentName:"strong"},"'reject'")),": Metro will stop bundling and report an error to the user.")),(0,r.mdx)("h4",{id:"gettransformoptions"},(0,r.mdx)("inlineCode",{parentName:"h4"},"getTransformOptions")),(0,r.mdx)("p",null,"Type: Function (see details below)"),(0,r.mdx)("p",null,"A function called by Metro to calculate additional options for the transformer and serializer based on the specific bundle being built."),(0,r.mdx)("p",null,"Metro expects ",(0,r.mdx)("inlineCode",{parentName:"p"},"getTransformOptions")," to have the following signature:"),(0,r.mdx)("pre",null,(0,r.mdx)("code",{parentName:"pre",className:"language-flow"},"function getTransformOptions(\n entryPoints: $ReadOnlyArray,\n options: {\n dev: boolean,\n hot: boolean,\n platform: ?string,\n },\n getDependenciesOf: (path: string) => Promise>,\n): Promise {\n // ...\n}\n")),(0,r.mdx)("p",null,(0,r.mdx)("inlineCode",{parentName:"p"},"getTransformOptions")," receives these parameters:"),(0,r.mdx)("ul",null,(0,r.mdx)("li",{parentName:"ul"},(0,r.mdx)("strong",{parentName:"li"},(0,r.mdx)("inlineCode",{parentName:"strong"},"entryPoints")),": Absolute paths to the bundle's entry points (typically just one)."),(0,r.mdx)("li",{parentName:"ul"},(0,r.mdx)("strong",{parentName:"li"},(0,r.mdx)("inlineCode",{parentName:"strong"},"options")),":",(0,r.mdx)("ul",{parentName:"li"},(0,r.mdx)("li",{parentName:"ul"},(0,r.mdx)("strong",{parentName:"li"},(0,r.mdx)("inlineCode",{parentName:"strong"},"dev")),": Whether the bundle is being built in development mode."),(0,r.mdx)("li",{parentName:"ul"},(0,r.mdx)("strong",{parentName:"li"},(0,r.mdx)("inlineCode",{parentName:"strong"},"hot")),": ",(0,r.mdx)("div",{class:"label deprecated"},"Deprecated")," Always true."),(0,r.mdx)("li",{parentName:"ul"},(0,r.mdx)("strong",{parentName:"li"},(0,r.mdx)("inlineCode",{parentName:"strong"},"platform")),": The target platform (e.g. ",(0,r.mdx)("inlineCode",{parentName:"li"},"ios"),", ",(0,r.mdx)("inlineCode",{parentName:"li"},"android"),")."))),(0,r.mdx)("li",{parentName:"ul"},(0,r.mdx)("strong",{parentName:"li"},(0,r.mdx)("inlineCode",{parentName:"strong"},"getDependenciesOf")),": A function which, given an absolute path to a module, returns a promise that resolves to the absolute paths of the module's transitive dependencies.")),(0,r.mdx)("p",null,(0,r.mdx)("inlineCode",{parentName:"p"},"getTransformOptions")," should return a promise that resolves to an object with the following properties:"),(0,r.mdx)("pre",null,(0,r.mdx)("code",{parentName:"pre",className:"language-flow"},"type ExtraTransformOptions = {\n preloadedModules?: {[path: string]: true} | false,\n ramGroups?: Array,\n transform?: {\n inlineRequires?: {blockList: {[string]: true}} | boolean,\n nonInlinedRequires?: $ReadOnlyArray,\n },\n};\n")),(0,r.mdx)("ul",null,(0,r.mdx)("li",{parentName:"ul"},(0,r.mdx)("strong",{parentName:"li"},(0,r.mdx)("inlineCode",{parentName:"strong"},"preloadedModules")),": A plain object whose keys represent a set of absolute paths. When serializing an ",(0,r.mdx)("a",{parentName:"li",href:"https://reactnative.dev/docs/ram-bundles-inline-requires#enable-the-ram-format"},"indexed RAM bundle"),", the modules in this set will be marked for eager evaluation at runtime."),(0,r.mdx)("li",{parentName:"ul"},(0,r.mdx)("strong",{parentName:"li"},(0,r.mdx)("inlineCode",{parentName:"strong"},"ramGroups")),": An array of absolute paths. When serializing an ",(0,r.mdx)("a",{parentName:"li",href:"https://reactnative.dev/docs/ram-bundles-inline-requires#enable-the-ram-format"},"indexed RAM bundle"),", each of the listed modules will be serialized along with its transitive dependencies. At runtime, the modules will all be parsed together as soon as any one of them is evaluated."),(0,r.mdx)("li",{parentName:"ul"},(0,r.mdx)("strong",{parentName:"li"},(0,r.mdx)("inlineCode",{parentName:"strong"},"transform")),": Advanced options for the transformer.",(0,r.mdx)("ul",{parentName:"li"},(0,r.mdx)("li",{parentName:"ul"},(0,r.mdx)("strong",{parentName:"li"},(0,r.mdx)("inlineCode",{parentName:"strong"},"inlineRequires")),":",(0,r.mdx)("ul",{parentName:"li"},(0,r.mdx)("li",{parentName:"ul"},"If ",(0,r.mdx)("inlineCode",{parentName:"li"},"inlineRequires")," is a boolean, it controls whether ",(0,r.mdx)("a",{parentName:"li",href:"https://reactnative.dev/docs/ram-bundles-inline-requires#inline-requires"},"inline requires")," are enabled in this bundle."),(0,r.mdx)("li",{parentName:"ul"},"If ",(0,r.mdx)("inlineCode",{parentName:"li"},"inlineRequires")," is an object, inline requires are enabled in all modules, except ones whose absolute paths appear as keys of ",(0,r.mdx)("inlineCode",{parentName:"li"},"inlineRequires.blockList"),"."))),(0,r.mdx)("li",{parentName:"ul"},(0,r.mdx)("strong",{parentName:"li"},(0,r.mdx)("inlineCode",{parentName:"strong"},"nonInlinedRequires")),": An array of unresolved module specifiers (e.g. ",(0,r.mdx)("inlineCode",{parentName:"li"},"react"),", ",(0,r.mdx)("inlineCode",{parentName:"li"},"react-native"),") to never inline, even when inline requires are enabled.")))),(0,r.mdx)("h4",{id:"minifierpath"},(0,r.mdx)("inlineCode",{parentName:"h4"},"minifierPath")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"string")," (default: ",(0,r.mdx)("inlineCode",{parentName:"p"},"'metro-minify-terser'"),")"),(0,r.mdx)("p",null,"Path, or package name resolvable from ",(0,r.mdx)("inlineCode",{parentName:"p"},"metro-transform-worker"),", to the minifier that minifies the code after transformation."),(0,r.mdx)("h4",{id:"minifierconfig"},(0,r.mdx)("inlineCode",{parentName:"h4"},"minifierConfig")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"{[key: string]: mixed}")),(0,r.mdx)("p",null,"Configuration object that will be passed to the minifier (it should be serializable)."),(0,r.mdx)("h4",{id:"optimizationsizelimit"},(0,r.mdx)("inlineCode",{parentName:"h4"},"optimizationSizeLimit")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"number")),(0,r.mdx)("p",null,"Define a threshold (in bytes) to disable some expensive optimizations for big files."),(0,r.mdx)("h4",{id:"react-native-only"},"React Native Only"),(0,r.mdx)("h4",{id:"assetplugins"},(0,r.mdx)("inlineCode",{parentName:"h4"},"assetPlugins")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"Array")),(0,r.mdx)("p",null,"List of modules to call to modify Asset data"),(0,r.mdx)("h4",{id:"assetregistrypath"},(0,r.mdx)("inlineCode",{parentName:"h4"},"assetRegistryPath")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"string")),(0,r.mdx)("p",null,"Where to fetch the assets from."),(0,r.mdx)("h3",{id:"babel-specific-transformer-options"},"Babel-specific transformer options"),(0,r.mdx)("h4",{id:"babeltransformerpath"},(0,r.mdx)("inlineCode",{parentName:"h4"},"babelTransformerPath")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"string")),(0,r.mdx)("p",null,"The name of a module that compiles code with Babel, returning an AST and optional metadata. Defaults to ",(0,r.mdx)("inlineCode",{parentName:"p"},"metro-babel-transformer"),"."),(0,r.mdx)("p",null,"Refer to the source code of ",(0,r.mdx)("a",{parentName:"p",href:"https://github.com/facebook/metro/blob/main/packages/metro-babel-transformer/src/index.js"},(0,r.mdx)("inlineCode",{parentName:"a"},"metro-babel-transformer"))," and ",(0,r.mdx)("a",{parentName:"p",href:"https://github.com/facebook/react-native/blob/main/packages/react-native-babel-transformer/src/index.js"},(0,r.mdx)("inlineCode",{parentName:"a"},"@react-native/metro-babel-transformer"))," for details on implementing a custom Babel transformer."),(0,r.mdx)("admonition",{type:"note"},(0,r.mdx)("p",{parentName:"admonition"},"This option only has an effect under the default ",(0,r.mdx)("a",{parentName:"p",href:"#transformerpath"},(0,r.mdx)("inlineCode",{parentName:"a"},"transformerPath")),". Custom transformers may ignore it.")),(0,r.mdx)("h4",{id:"enablebabelrclookup"},(0,r.mdx)("inlineCode",{parentName:"h4"},"enableBabelRCLookup")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"boolean")),(0,r.mdx)("p",null,"Whether to enable searching for Babel configuration files. This is passed to Babel as the ",(0,r.mdx)("a",{parentName:"p",href:"https://babeljs.io/docs/en/options#babelrc"},(0,r.mdx)("inlineCode",{parentName:"a"},"babelrc"))," config option. Defaults to ",(0,r.mdx)("inlineCode",{parentName:"p"},"true"),"."),(0,r.mdx)("admonition",{type:"note"},(0,r.mdx)("p",{parentName:"admonition"},"This option only has an effect under the default ",(0,r.mdx)("a",{parentName:"p",href:"#transformerpath"},(0,r.mdx)("inlineCode",{parentName:"a"},"transformerPath")),". Custom transformers may ignore it. Custom ",(0,r.mdx)("a",{parentName:"p",href:"#babeltransformerpath"},"Babel transformers")," should respect this option.")),(0,r.mdx)("h4",{id:"enablebabelruntime"},(0,r.mdx)("inlineCode",{parentName:"h4"},"enableBabelRuntime")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"boolean | string")),(0,r.mdx)("p",null,"Whether the transformer should use the ",(0,r.mdx)("inlineCode",{parentName:"p"},"@babel/transform/runtime")," plugin. Defaults to ",(0,r.mdx)("inlineCode",{parentName:"p"},"true"),"."),(0,r.mdx)("p",null,"If the value is a string, it is treated as a runtime version number and passed as ",(0,r.mdx)("inlineCode",{parentName:"p"},"version")," to the ",(0,r.mdx)("inlineCode",{parentName:"p"},"@babel/plugin-transform-runtime")," configuration. This allows you to optimize the generated Babel runtime calls based on the version installed in your project."),(0,r.mdx)("admonition",{type:"note"},(0,r.mdx)("p",{parentName:"admonition"},"This option only works under the default settings for React Native. It may have no effect in a project that uses custom ",(0,r.mdx)("a",{parentName:"p",href:"#transformerpath"},(0,r.mdx)("inlineCode",{parentName:"a"},"transformerPath")),", a custom ",(0,r.mdx)("a",{parentName:"p",href:"#babeltransformerpath"},(0,r.mdx)("inlineCode",{parentName:"a"},"babelTransformerPath"))," or a custom ",(0,r.mdx)("a",{parentName:"p",href:"https://babeljs.io/docs/en/config-files"},"Babel config file"),".")),(0,r.mdx)("h4",{id:"hermesparser"},(0,r.mdx)("inlineCode",{parentName:"h4"},"hermesParser")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"boolean")),(0,r.mdx)("p",null,"Whether to use the ",(0,r.mdx)("a",{parentName:"p",href:"https://www.npmjs.com/package/hermes-parser"},(0,r.mdx)("inlineCode",{parentName:"a"},"hermes-parser"))," package to parse JavaScript source files, instead of Babel. Defaults to ",(0,r.mdx)("inlineCode",{parentName:"p"},"false"),"."),(0,r.mdx)("admonition",{type:"note"},(0,r.mdx)("p",{parentName:"admonition"},"This option only has an effect under the default ",(0,r.mdx)("a",{parentName:"p",href:"#transformerpath"},(0,r.mdx)("inlineCode",{parentName:"a"},"transformerPath"))," and the ",(0,r.mdx)("a",{parentName:"p",href:"#babeltransformerpath"},"Babel transformers")," built into Metro. Custom transformers and custom ",(0,r.mdx)("a",{parentName:"p",href:"#babeltransformerpath"},"Babel transformers")," may ignore it.")),(0,r.mdx)("hr",null),(0,r.mdx)("h3",{id:"serializer-options"},"Serializer Options"),(0,r.mdx)("h4",{id:"getrunmodulestatement"},(0,r.mdx)("inlineCode",{parentName:"h4"},"getRunModuleStatement")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"(number | string) => string")),(0,r.mdx)("p",null,"Specify the format of the initial require statements that are appended at the end of the bundle. By default is ",(0,r.mdx)("inlineCode",{parentName:"p"},"__r(${moduleId});"),"."),(0,r.mdx)("h4",{id:"createmoduleidfactory"},(0,r.mdx)("inlineCode",{parentName:"h4"},"createModuleIdFactory")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"() => (path: string) => number")),(0,r.mdx)("p",null,"Used to generate the module id for ",(0,r.mdx)("inlineCode",{parentName:"p"},"require")," statements."),(0,r.mdx)("h4",{id:"getpolyfills"},(0,r.mdx)("inlineCode",{parentName:"h4"},"getPolyfills")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"({platform: ?string}) => $ReadOnlyArray")),(0,r.mdx)("p",null,"An optional list of polyfills to include in the bundle. The list defaults to a set of common polyfills for Number, String, Array, Object..."),(0,r.mdx)("h4",{id:"getmodulesrunbeforemainmodule"},(0,r.mdx)("inlineCode",{parentName:"h4"},"getModulesRunBeforeMainModule")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"(entryFilePath: string) => Array")),(0,r.mdx)("p",null,"An array of modules to be required before the entry point. It should contain the absolute path of each module. Note that this will add the additional require statements only if the passed modules are already included as part of the bundle."),(0,r.mdx)("h4",{id:"processmodulefilter"},(0,r.mdx)("inlineCode",{parentName:"h4"},"processModuleFilter")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"(module: Array) => boolean")),(0,r.mdx)("p",null,"A filter function to discard specific modules from the output."),(0,r.mdx)("h4",{id:"isthirdpartymodule"},(0,r.mdx)("inlineCode",{parentName:"h4"},"isThirdPartyModule")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"(module: {path: string, ...}) => boolean")),(0,r.mdx)("p",null,"A function that determines which modules are added to the ",(0,r.mdx)("a",{parentName:"p",href:"https://developer.chrome.com/articles/x-google-ignore-list/"},(0,r.mdx)("inlineCode",{parentName:"a"},"x_google_ignoreList"))," field of the source map. This supports ",(0,r.mdx)("a",{parentName:"p",href:"https://developer.chrome.com/blog/devtools-modern-web-debugging/#just-my-code"},'"Just My Code"')," debugging in Chrome DevTools and other compatible debuggers."),(0,r.mdx)("p",null,"Defaults to returning ",(0,r.mdx)("inlineCode",{parentName:"p"},"true")," for modules with a path component named ",(0,r.mdx)("inlineCode",{parentName:"p"},"node_modules"),"."),(0,r.mdx)("admonition",{type:"note"},(0,r.mdx)("p",{parentName:"admonition"},"In addition to modules marked as ignored by ",(0,r.mdx)("inlineCode",{parentName:"p"},"isThirdPartyModule"),", Metro will also automatically add modules generated by the bundler itself to the ignore list.")),(0,r.mdx)("hr",null),(0,r.mdx)("h3",{id:"server-options"},"Server Options"),(0,r.mdx)("p",null,"These options are used when Metro serves the content."),(0,r.mdx)("h4",{id:"port"},(0,r.mdx)("inlineCode",{parentName:"h4"},"port")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"number")),(0,r.mdx)("p",null,"Which port to listen on."),(0,r.mdx)("h4",{id:"useglobalhotkey"},(0,r.mdx)("inlineCode",{parentName:"h4"},"useGlobalHotkey")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"boolean")),(0,r.mdx)("p",null,"Whether we should enable CMD+R hotkey for refreshing the bundle."),(0,r.mdx)("h4",{id:"enhancemiddleware-deprecated"},(0,r.mdx)("inlineCode",{parentName:"h4"},"enhanceMiddleware")," ",(0,r.mdx)("div",{class:"label deprecated"},"Deprecated")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"(Middleware, MetroServer) => Middleware")),(0,r.mdx)("p",null,"A function that allows attaching custom ",(0,r.mdx)("a",{parentName:"p",href:"https://www.npmjs.com/package/connect"},(0,r.mdx)("inlineCode",{parentName:"a"},"connect"))," middleware to Metro. For example:"),(0,r.mdx)("admonition",{type:"tip"},(0,r.mdx)("p",{parentName:"admonition"},"You can use ",(0,r.mdx)("a",{parentName:"p",href:"https://www.npmjs.com/package/connect#mount-middleware"},(0,r.mdx)("inlineCode",{parentName:"a"},"connect()"))," as a utility to extend the base ",(0,r.mdx)("inlineCode",{parentName:"p"},"metroMiddleware")," and to mount additional middleware handlers.")),(0,r.mdx)("pre",null,(0,r.mdx)("code",{parentName:"pre",className:"language-ts"},"enhanceMiddleware: (metroMiddleware: Middleware, metroServer: MetroServer) => {\n return connect()\n .use(metroMiddleware)\n .use('/custom-endpoint', customEndpointMiddleware());\n},\n")),(0,r.mdx)("p",null,"The ",(0,r.mdx)("inlineCode",{parentName:"p"},"Middleware")," type is an alias for ",(0,r.mdx)("a",{parentName:"p",href:"https://github.com/DefinitelyTyped/DefinitelyTyped/blob/876b9ec96ba02d0c84b1e49af5890c8f5aa2dfe3/types/connect/index.d.ts#L29"},(0,r.mdx)("inlineCode",{parentName:"a"},"connect.HandleFunction")),"."),(0,r.mdx)("h4",{id:"rewriterequesturl"},(0,r.mdx)("inlineCode",{parentName:"h4"},"rewriteRequestUrl")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"string => string")),(0,r.mdx)("p",null,"A function that will be called every time Metro processes a URL, after normalization of non-standard query-string delimiters using ",(0,r.mdx)("a",{parentName:"p",href:"https://www.npmjs.com/package/jsc-safe-url"},(0,r.mdx)("inlineCode",{parentName:"a"},"jsc-safe-url")),". Metro will use the return value of this function as if it were the original URL provided by the client. This applies to all incoming HTTP requests (after any custom middleware), as well as bundle URLs in ",(0,r.mdx)("inlineCode",{parentName:"p"},"/symbolicate")," request payloads and within the hot reloading protocol."),(0,r.mdx)("h4",{id:"forwardclientlogs"},(0,r.mdx)("inlineCode",{parentName:"h4"},"forwardClientLogs")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"boolean")),(0,r.mdx)("p",null,"Enable forwarding of ",(0,r.mdx)("inlineCode",{parentName:"p"},"client_log")," events (when client logs are ",(0,r.mdx)("a",{parentName:"p",href:"https://github.com/facebook/metro/blob/614ad14a85b22958129ee94e04376b096f03ccb1/packages/metro/src/lib/createWebsocketServer.js#L20"},"configured"),") to the reporter. Defaults to ",(0,r.mdx)("inlineCode",{parentName:"p"},"true"),"."),(0,r.mdx)("hr",null),(0,r.mdx)("h3",{id:"watcher-options"},"Watcher Options"),(0,r.mdx)("p",null,"Options for the filesystem watcher."),(0,r.mdx)("admonition",{type:"note"},(0,r.mdx)("p",{parentName:"admonition"},"Dot notation in this section indicates a nested configuration object, e.g. ",(0,r.mdx)("inlineCode",{parentName:"p"},"watchman.deferStates")," \u2192 ",(0,r.mdx)("inlineCode",{parentName:"p"},"watchman: { deferStates: ... }"),".")),(0,r.mdx)("h4",{id:"additionalexts"},(0,r.mdx)("inlineCode",{parentName:"h4"},"additionalExts")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"Array")),(0,r.mdx)("p",null,"The extensions which Metro should watch in addition to ",(0,r.mdx)("inlineCode",{parentName:"p"},"sourceExts"),", but which will not be automatically tried by the resolver."),(0,r.mdx)("p",null,"Therefore, the two behavior differences from ",(0,r.mdx)("inlineCode",{parentName:"p"},"resolver.sourceExts")," when importing a module are:"),(0,r.mdx)("ul",null,(0,r.mdx)("li",{parentName:"ul"},"Modules can only be required when fully specified (e.g. ",(0,r.mdx)("inlineCode",{parentName:"li"},"import moduleA from 'moduleA.mjs'"),")."),(0,r.mdx)("li",{parentName:"ul"},"No platform-specific resolution is performed.")),(0,r.mdx)("p",null,"Defaults to ",(0,r.mdx)("inlineCode",{parentName:"p"},"['cjs', 'mjs']"),"."),(0,r.mdx)("h4",{id:"healthcheckenabled"},(0,r.mdx)("inlineCode",{parentName:"h4"},"healthCheck.enabled")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"boolean")),(0,r.mdx)("p",null,"Whether to periodically check the health of the filesystem watcher by writing a temporary file to the project and waiting for it to be observed."),(0,r.mdx)("p",null,"The default value is ",(0,r.mdx)("inlineCode",{parentName:"p"},"false"),"."),(0,r.mdx)("h4",{id:"healthcheckfileprefix"},(0,r.mdx)("inlineCode",{parentName:"h4"},"healthCheck.filePrefix")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"string")),(0,r.mdx)("p",null,"If watcher health checks are enabled, this property controls the name of the temporary file that will be written into the project filesystem."),(0,r.mdx)("p",null,"The default value is ",(0,r.mdx)("inlineCode",{parentName:"p"},"'.metro-health-check'"),"."),(0,r.mdx)("admonition",{type:"note"},(0,r.mdx)("p",{parentName:"admonition"},"There's no need to commit health check files to source control. If you choose to enable health checks in your project, make sure you add ",(0,r.mdx)("inlineCode",{parentName:"p"},".metro-health-check*")," to your ",(0,r.mdx)("inlineCode",{parentName:"p"},".gitignore")," file to avoid generating unnecessary changes.")),(0,r.mdx)("h4",{id:"healthcheckinterval"},(0,r.mdx)("inlineCode",{parentName:"h4"},"healthCheck.interval")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"number")),(0,r.mdx)("p",null,"If watcher health checks are enabled, this property controls how often they occur (in milliseconds)."),(0,r.mdx)("p",null,"The default value is 30000."),(0,r.mdx)("h4",{id:"healthchecktimeout"},(0,r.mdx)("inlineCode",{parentName:"h4"},"healthCheck.timeout")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"number")),(0,r.mdx)("p",null,"If watcher health checks are enabled, this property controls the time (in milliseconds) Metro will wait for a file change to be observed before considering the check to have failed."),(0,r.mdx)("p",null,"The default value is 5000."),(0,r.mdx)("h4",{id:"watchmandeferstates"},(0,r.mdx)("inlineCode",{parentName:"h4"},"watchman.deferStates")),(0,r.mdx)("p",null,"Type: ",(0,r.mdx)("inlineCode",{parentName:"p"},"Array")),(0,r.mdx)("p",null,"Applies when using Watchman. Metro will ",(0,r.mdx)("a",{parentName:"p",href:"https://facebook.github.io/watchman/docs/cmd/subscribe.html#defer"},"defer processing filesystem updates")," while these ",(0,r.mdx)("a",{parentName:"p",href:"https://facebook.github.io/watchman/docs/cmd/state-enter.html"},"states")," are asserted in the watch. This is useful for debouncing builds while the filesystem hasn't settled, e.g. during large source control operations."),(0,r.mdx)("p",null,"The default value is ",(0,r.mdx)("inlineCode",{parentName:"p"},"['hg.update']"),"."),(0,r.mdx)("h2",{id:"merging-configurations"},"Merging Configurations"),(0,r.mdx)("p",null,"Using the ",(0,r.mdx)("inlineCode",{parentName:"p"},"metro-config")," package it is possible to merge multiple configurations together."),(0,r.mdx)("table",null,(0,r.mdx)("thead",{parentName:"table"},(0,r.mdx)("tr",{parentName:"thead"},(0,r.mdx)("th",{parentName:"tr",align:null},"Method"),(0,r.mdx)("th",{parentName:"tr",align:null},"Description"))),(0,r.mdx)("tbody",{parentName:"table"},(0,r.mdx)("tr",{parentName:"tbody"},(0,r.mdx)("td",{parentName:"tr",align:null},(0,r.mdx)("inlineCode",{parentName:"td"},"mergeConfig(...configs): MergedConfig")),(0,r.mdx)("td",{parentName:"tr",align:null},"Returns the merged configuration of two or more configuration objects.")))),(0,r.mdx)("admonition",{type:"note"},(0,r.mdx)("p",{parentName:"admonition"},"Arrays and function based config parameters do not deeply merge and will instead override any pre-existing config parameters.\nThis allows overriding and removing default config parameters such as ",(0,r.mdx)("inlineCode",{parentName:"p"},"platforms")," or ",(0,r.mdx)("inlineCode",{parentName:"p"},"getModulesRunBeforeMainModule")," that may not be required in your environment.")),(0,r.mdx)("h4",{id:"merging-example"},"Merging Example"),(0,r.mdx)("pre",null,(0,r.mdx)("code",{parentName:"pre",className:"language-js"},"// metro.config.js\nconst { mergeConfig } = require('metro-config');\n\nconst configA = {\n /* general options */\n\n resolver: {\n /* resolver options */\n },\n transformer: {\n /* transformer options */\n },\n serializer: {\n /* serializer options */\n },\n server: {\n /* server options */\n }\n};\n\nconst configB = {\n /* general options */\n\n resolver: {\n /* resolver options */\n },\n transformer: {\n /* transformer options */\n },\n serializer: {\n /* serializer options */\n },\n server: {\n /* server options */\n }\n};\n\nmodule.exports = mergeConfig(configA, configB);\n")))}h.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/ba92a0db.b7d52f23.js b/assets/js/ba92a0db.38175987.js similarity index 66% rename from assets/js/ba92a0db.b7d52f23.js rename to assets/js/ba92a0db.38175987.js index 4c4c07770f..d00ba4aa07 100644 --- a/assets/js/ba92a0db.b7d52f23.js +++ b/assets/js/ba92a0db.38175987.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmetro_website=self.webpackChunkmetro_website||[]).push([[177],{3905:(e,t,r)=>{r.r(t),r.d(t,{MDXContext:()=>c,MDXProvider:()=>u,mdx:()=>g,useMDXComponents:()=>m,withMDXComponents:()=>p});var n=r(67294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(){return a=Object.assign||function(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var c=n.createContext({}),p=function(e){return function(t){var r=m(t.components);return n.createElement(e,a({},t,{components:r}))}},m=function(e){var t=n.useContext(c),r=t;return e&&(r="function"==typeof e?e(t):l(l({},t),e)),r},u=function(e){var t=m(e.components);return n.createElement(c.Provider,{value:t},e.children)},d="mdxType",f={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},h=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,a=e.originalType,i=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),p=m(r),u=o,d=p["".concat(i,".").concat(u)]||p[u]||f[u]||a;return r?n.createElement(d,l(l({ref:t},c),{},{components:r})):n.createElement(d,l({ref:t},c))}));function g(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=r.length,i=new Array(a);i[0]=h;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[d]="string"==typeof e?e:o,i[1]=l;for(var c=2;c{r.r(t),r.d(t,{assets:()=>p,contentTitle:()=>s,default:()=>f,frontMatter:()=>l,metadata:()=>c,toc:()=>m});var n=r(87462),o=r(63366),a=(r(67294),r(3905)),i=["components"],l={id:"troubleshooting",title:"Troubleshooting"},s=void 0,c={unversionedId:"troubleshooting",id:"troubleshooting",title:"Troubleshooting",description:"Uh oh, something went wrong? Use this guide to resolve issues with Metro.",source:"@site/../docs/Troubleshooting.md",sourceDirName:".",slug:"/troubleshooting",permalink:"/docs/troubleshooting",draft:!1,editUrl:"https://github.com/facebook/metro/edit/main/docs/../docs/Troubleshooting.md",tags:[],version:"current",lastUpdatedAt:1725545477,formattedLastUpdatedAt:"Sep 5, 2024",frontMatter:{id:"troubleshooting",title:"Troubleshooting"},sidebar:"docs",previous:{title:"Package Exports Support (Experimental)",permalink:"/docs/package-exports"},next:{title:"Local Development Setup",permalink:"/docs/local-development"}},p={},m=[{value:"Still unresolved?",id:"still-unresolved",level:3}],u={toc:m},d="wrapper";function f(e){var t=e.components,r=(0,o.Z)(e,i);return(0,a.mdx)(d,(0,n.Z)({},u,r,{components:t,mdxType:"MDXLayout"}),(0,a.mdx)("p",null,"Uh oh, something went wrong? Use this guide to resolve issues with Metro."),(0,a.mdx)("ol",null,(0,a.mdx)("li",{parentName:"ol"},"Clear Watchman watches: ",(0,a.mdx)("inlineCode",{parentName:"li"},"watchman watch-del-all")),(0,a.mdx)("li",{parentName:"ol"},"Delete ",(0,a.mdx)("inlineCode",{parentName:"li"},"node_modules")," and run ",(0,a.mdx)("inlineCode",{parentName:"li"},"yarn install")),(0,a.mdx)("li",{parentName:"ol"},"Reset Metro's cache by passing the ",(0,a.mdx)("inlineCode",{parentName:"li"},"--reset-cache")," flag, or adding ",(0,a.mdx)("inlineCode",{parentName:"li"},"resetCache: true")," to your Metro configuration file."),(0,a.mdx)("li",{parentName:"ol"},"Remove the cache: ",(0,a.mdx)("inlineCode",{parentName:"li"},"rm -rf ${TMPDIR:-/tmp}/metro-*")),(0,a.mdx)("li",{parentName:"ol"},"Update Metro to the ",(0,a.mdx)("a",{parentName:"li",href:"https://www.npmjs.com/package/metro"},"latest version"))),(0,a.mdx)("h3",{id:"still-unresolved"},"Still unresolved?"),(0,a.mdx)("p",null,"See the ",(0,a.mdx)("a",{parentName:"p",href:"/help"},"Help")," pages."))}f.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkmetro_website=self.webpackChunkmetro_website||[]).push([[177],{3905:(e,t,r)=>{r.r(t),r.d(t,{MDXContext:()=>c,MDXProvider:()=>u,mdx:()=>g,useMDXComponents:()=>m,withMDXComponents:()=>p});var n=r(67294);function o(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function a(){return a=Object.assign||function(e){for(var t=1;t=0||(o[r]=e[r]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(o[r]=e[r])}return o}var c=n.createContext({}),p=function(e){return function(t){var r=m(t.components);return n.createElement(e,a({},t,{components:r}))}},m=function(e){var t=n.useContext(c),r=t;return e&&(r="function"==typeof e?e(t):l(l({},t),e)),r},u=function(e){var t=m(e.components);return n.createElement(c.Provider,{value:t},e.children)},d="mdxType",f={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},h=n.forwardRef((function(e,t){var r=e.components,o=e.mdxType,a=e.originalType,i=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),p=m(r),u=o,d=p["".concat(i,".").concat(u)]||p[u]||f[u]||a;return r?n.createElement(d,l(l({ref:t},c),{},{components:r})):n.createElement(d,l({ref:t},c))}));function g(e,t){var r=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=r.length,i=new Array(a);i[0]=h;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[d]="string"==typeof e?e:o,i[1]=l;for(var c=2;c{r.r(t),r.d(t,{assets:()=>p,contentTitle:()=>s,default:()=>f,frontMatter:()=>l,metadata:()=>c,toc:()=>m});var n=r(87462),o=r(63366),a=(r(67294),r(3905)),i=["components"],l={id:"troubleshooting",title:"Troubleshooting"},s=void 0,c={unversionedId:"troubleshooting",id:"troubleshooting",title:"Troubleshooting",description:"Uh oh, something went wrong? Use this guide to resolve issues with Metro.",source:"@site/../docs/Troubleshooting.md",sourceDirName:".",slug:"/troubleshooting",permalink:"/docs/troubleshooting",draft:!1,editUrl:"https://github.com/facebook/metro/edit/main/docs/../docs/Troubleshooting.md",tags:[],version:"current",lastUpdatedAt:1726493799,formattedLastUpdatedAt:"Sep 16, 2024",frontMatter:{id:"troubleshooting",title:"Troubleshooting"},sidebar:"docs",previous:{title:"Package Exports Support (Experimental)",permalink:"/docs/package-exports"},next:{title:"Local Development Setup",permalink:"/docs/local-development"}},p={},m=[{value:"Still unresolved?",id:"still-unresolved",level:3}],u={toc:m},d="wrapper";function f(e){var t=e.components,r=(0,o.Z)(e,i);return(0,a.mdx)(d,(0,n.Z)({},u,r,{components:t,mdxType:"MDXLayout"}),(0,a.mdx)("p",null,"Uh oh, something went wrong? Use this guide to resolve issues with Metro."),(0,a.mdx)("ol",null,(0,a.mdx)("li",{parentName:"ol"},"Clear Watchman watches: ",(0,a.mdx)("inlineCode",{parentName:"li"},"watchman watch-del-all")),(0,a.mdx)("li",{parentName:"ol"},"Delete ",(0,a.mdx)("inlineCode",{parentName:"li"},"node_modules")," and run ",(0,a.mdx)("inlineCode",{parentName:"li"},"yarn install")),(0,a.mdx)("li",{parentName:"ol"},"Reset Metro's cache by passing the ",(0,a.mdx)("inlineCode",{parentName:"li"},"--reset-cache")," flag, or adding ",(0,a.mdx)("inlineCode",{parentName:"li"},"resetCache: true")," to your Metro configuration file."),(0,a.mdx)("li",{parentName:"ol"},"Remove the cache: ",(0,a.mdx)("inlineCode",{parentName:"li"},"rm -rf ${TMPDIR:-/tmp}/metro-*")),(0,a.mdx)("li",{parentName:"ol"},"Update Metro to the ",(0,a.mdx)("a",{parentName:"li",href:"https://www.npmjs.com/package/metro"},"latest version"))),(0,a.mdx)("h3",{id:"still-unresolved"},"Still unresolved?"),(0,a.mdx)("p",null,"See the ",(0,a.mdx)("a",{parentName:"p",href:"/help"},"Help")," pages."))}f.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/c026fe4c.8ca3da37.js b/assets/js/c026fe4c.876ba969.js similarity index 58% rename from assets/js/c026fe4c.8ca3da37.js rename to assets/js/c026fe4c.876ba969.js index 430383eb2a..011976c755 100644 --- a/assets/js/c026fe4c.8ca3da37.js +++ b/assets/js/c026fe4c.876ba969.js @@ -1 +1 @@ -"use strict";(self.webpackChunkmetro_website=self.webpackChunkmetro_website||[]).push([[476],{3905:(e,t,n)=>{n.r(t),n.d(t,{MDXContext:()=>c,MDXProvider:()=>u,mdx:()=>v,useMDXComponents:()=>d,withMDXComponents:()=>p});var r=n(67294);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(){return a=Object.assign||function(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var c=r.createContext({}),p=function(e){return function(t){var n=d(t.components);return r.createElement(e,a({},t,{components:n}))}},d=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},u=function(e){var t=d(e.components);return r.createElement(c.Provider,{value:t},e.children)},m="mdxType",f={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},h=r.forwardRef((function(e,t){var n=e.components,o=e.mdxType,a=e.originalType,i=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),p=d(n),u=o,m=p["".concat(i,".").concat(u)]||p[u]||f[u]||a;return n?r.createElement(m,l(l({ref:t},c),{},{components:n})):r.createElement(m,l({ref:t},c))}));function v(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=n.length,i=new Array(a);i[0]=h;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[m]="string"==typeof e?e:o,i[1]=l;for(var c=2;c{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>s,default:()=>f,frontMatter:()=>l,metadata:()=>c,toc:()=>d});var r=n(87462),o=n(63366),a=(n(67294),n(3905)),i=["components"],l={id:"concepts",title:"Concepts"},s=void 0,c={unversionedId:"concepts",id:"concepts",title:"Concepts",description:"Metro is a JavaScript bundler. It takes in an entry file and various options, and gives you back a single JavaScript file that includes all your code and its dependencies.",source:"@site/../docs/Concepts.md",sourceDirName:".",slug:"/concepts",permalink:"/docs/concepts",draft:!1,editUrl:"https://github.com/facebook/metro/edit/main/docs/../docs/Concepts.md",tags:[],version:"current",lastUpdatedAt:1725545477,formattedLastUpdatedAt:"Sep 5, 2024",frontMatter:{id:"concepts",title:"Concepts"},sidebar:"docs",previous:{title:"Getting Started",permalink:"/docs/getting-started"},next:{title:"Bundling API",permalink:"/docs/api"}},p={},d=[{value:"Resolution",id:"resolution",level:3},{value:"Transformation",id:"transformation",level:3},{value:"Serialization",id:"serialization",level:3},{value:"Modules",id:"modules",level:2}],u={toc:d},m="wrapper";function f(e){var t=e.components,n=(0,o.Z)(e,i);return(0,a.mdx)(m,(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,a.mdx)("p",null,"Metro is a JavaScript bundler. It takes in an entry file and various options, and gives you back a single JavaScript file that includes all your code and its dependencies."),(0,a.mdx)("p",null,"Metro has three separate stages in its bundling process:"),(0,a.mdx)("ol",null,(0,a.mdx)("li",{parentName:"ol"},"Resolution"),(0,a.mdx)("li",{parentName:"ol"},"Transformation"),(0,a.mdx)("li",{parentName:"ol"},"Serialization")),(0,a.mdx)("h3",{id:"resolution"},"Resolution"),(0,a.mdx)("p",null,"Metro needs to build a graph of all the modules that are required from the entry point. To find which file is required from another file Metro uses a resolver. In reality this stage happens in parallel with the transformation stage."),(0,a.mdx)("h3",{id:"transformation"},"Transformation"),(0,a.mdx)("p",null,"All modules go through a transformer. A transformer is responsible for converting (transpiling) a module to a format that is understandable by the target platform (eg. React Native). Transformation of modules happens in parallel based on the amount of cores that you have."),(0,a.mdx)("h3",{id:"serialization"},"Serialization"),(0,a.mdx)("p",null,"As soon as all the modules have been transformed they will be serialized. A serializer combines the modules to generate one or multiple bundles. A bundle is literally a bundle of modules combined into a single JavaScript file."),(0,a.mdx)("h2",{id:"modules"},"Modules"),(0,a.mdx)("p",null,"Metro has been split out into multiple modules corresponding to every step in the flow, each with their own responsibility. This means that we have a resolver, transformer, and serializer. These modules can be swapped out depending on your needs."))}f.isMDXComponent=!0}}]); \ No newline at end of file +"use strict";(self.webpackChunkmetro_website=self.webpackChunkmetro_website||[]).push([[476],{3905:(e,t,n)=>{n.r(t),n.d(t,{MDXContext:()=>c,MDXProvider:()=>u,mdx:()=>v,useMDXComponents:()=>d,withMDXComponents:()=>p});var r=n(67294);function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function a(){return a=Object.assign||function(e){for(var t=1;t=0||(o[n]=e[n]);return o}(e,t);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}var c=r.createContext({}),p=function(e){return function(t){var n=d(t.components);return r.createElement(e,a({},t,{components:n}))}},d=function(e){var t=r.useContext(c),n=t;return e&&(n="function"==typeof e?e(t):l(l({},t),e)),n},u=function(e){var t=d(e.components);return r.createElement(c.Provider,{value:t},e.children)},m="mdxType",f={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},h=r.forwardRef((function(e,t){var n=e.components,o=e.mdxType,a=e.originalType,i=e.parentName,c=s(e,["components","mdxType","originalType","parentName"]),p=d(n),u=o,m=p["".concat(i,".").concat(u)]||p[u]||f[u]||a;return n?r.createElement(m,l(l({ref:t},c),{},{components:n})):r.createElement(m,l({ref:t},c))}));function v(e,t){var n=arguments,o=t&&t.mdxType;if("string"==typeof e||o){var a=n.length,i=new Array(a);i[0]=h;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[m]="string"==typeof e?e:o,i[1]=l;for(var c=2;c{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>s,default:()=>f,frontMatter:()=>l,metadata:()=>c,toc:()=>d});var r=n(87462),o=n(63366),a=(n(67294),n(3905)),i=["components"],l={id:"concepts",title:"Concepts"},s=void 0,c={unversionedId:"concepts",id:"concepts",title:"Concepts",description:"Metro is a JavaScript bundler. It takes in an entry file and various options, and gives you back a single JavaScript file that includes all your code and its dependencies.",source:"@site/../docs/Concepts.md",sourceDirName:".",slug:"/concepts",permalink:"/docs/concepts",draft:!1,editUrl:"https://github.com/facebook/metro/edit/main/docs/../docs/Concepts.md",tags:[],version:"current",lastUpdatedAt:1726493799,formattedLastUpdatedAt:"Sep 16, 2024",frontMatter:{id:"concepts",title:"Concepts"},sidebar:"docs",previous:{title:"Getting Started",permalink:"/docs/getting-started"},next:{title:"Bundling API",permalink:"/docs/api"}},p={},d=[{value:"Resolution",id:"resolution",level:3},{value:"Transformation",id:"transformation",level:3},{value:"Serialization",id:"serialization",level:3},{value:"Modules",id:"modules",level:2}],u={toc:d},m="wrapper";function f(e){var t=e.components,n=(0,o.Z)(e,i);return(0,a.mdx)(m,(0,r.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,a.mdx)("p",null,"Metro is a JavaScript bundler. It takes in an entry file and various options, and gives you back a single JavaScript file that includes all your code and its dependencies."),(0,a.mdx)("p",null,"Metro has three separate stages in its bundling process:"),(0,a.mdx)("ol",null,(0,a.mdx)("li",{parentName:"ol"},"Resolution"),(0,a.mdx)("li",{parentName:"ol"},"Transformation"),(0,a.mdx)("li",{parentName:"ol"},"Serialization")),(0,a.mdx)("h3",{id:"resolution"},"Resolution"),(0,a.mdx)("p",null,"Metro needs to build a graph of all the modules that are required from the entry point. To find which file is required from another file Metro uses a resolver. In reality this stage happens in parallel with the transformation stage."),(0,a.mdx)("h3",{id:"transformation"},"Transformation"),(0,a.mdx)("p",null,"All modules go through a transformer. A transformer is responsible for converting (transpiling) a module to a format that is understandable by the target platform (eg. React Native). Transformation of modules happens in parallel based on the amount of cores that you have."),(0,a.mdx)("h3",{id:"serialization"},"Serialization"),(0,a.mdx)("p",null,"As soon as all the modules have been transformed they will be serialized. A serializer combines the modules to generate one or multiple bundles. A bundle is literally a bundle of modules combined into a single JavaScript file."),(0,a.mdx)("h2",{id:"modules"},"Modules"),(0,a.mdx)("p",null,"Metro has been split out into multiple modules corresponding to every step in the flow, each with their own responsibility. This means that we have a resolver, transformer, and serializer. These modules can be swapped out depending on your needs."))}f.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/e08abecb.2232dd98.js b/assets/js/e08abecb.2232dd98.js deleted file mode 100644 index 978e6ba7ed..0000000000 --- a/assets/js/e08abecb.2232dd98.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkmetro_website=self.webpackChunkmetro_website||[]).push([[350],{3905:(e,t,n)=>{n.r(t),n.d(t,{MDXContext:()=>m,MDXProvider:()=>s,mdx:()=>x,useMDXComponents:()=>u,withMDXComponents:()=>p});var r=n(67294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(){return o=Object.assign||function(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var m=r.createContext({}),p=function(e){return function(t){var n=u(t.components);return r.createElement(e,o({},t,{components:n}))}},u=function(e){var t=r.useContext(m),n=t;return e&&(n="function"==typeof e?e(t):d(d({},t),e)),n},s=function(e){var t=u(e.components);return r.createElement(m.Provider,{value:t},e.children)},c="mdxType",f={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},h=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,i=e.parentName,m=l(e,["components","mdxType","originalType","parentName"]),p=u(n),s=a,c=p["".concat(i,".").concat(s)]||p[s]||f[s]||o;return n?r.createElement(c,d(d({ref:t},m),{},{components:n})):r.createElement(c,d({ref:t},m))}));function x(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,i=new Array(o);i[0]=h;var d={};for(var l in t)hasOwnProperty.call(t,l)&&(d[l]=t[l]);d.originalType=e,d[c]="string"==typeof e?e:a,i[1]=d;for(var m=2;m{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>l,default:()=>f,frontMatter:()=>d,metadata:()=>m,toc:()=>u});var r=n(87462),a=n(63366),o=(n(67294),n(3905)),i=["components"],d={id:"module-api",title:"Module API"},l=void 0,m={unversionedId:"module-api",id:"module-api",title:"Module API",description:"Metro is designed to allow code written for Node (or for bundlers targeting the Web) to run mostly unmodified. The main APIs available to application code are listed below.",source:"@site/../docs/ModuleAPI.md",sourceDirName:".",slug:"/module-api",permalink:"/docs/module-api",draft:!1,editUrl:"https://github.com/facebook/metro/edit/main/docs/../docs/ModuleAPI.md",tags:[],version:"current",lastUpdatedAt:1725545477,formattedLastUpdatedAt:"Sep 5, 2024",frontMatter:{id:"module-api",title:"Module API"},sidebar:"docs",previous:{title:"Bundling API",permalink:"/docs/api"},next:{title:"Configuring Metro",permalink:"/docs/configuration"}},p={},u=[{value:"require()",id:"require",level:2},{value:"Advanced usage: require at runtime",id:"advanced-usage-require-at-runtime",level:3},{value:"module.exports",id:"moduleexports",level:2},{value:"ES Modules syntax (import and export)",id:"es-modules-syntax-import-and-export",level:2},{value:"import() (dynamic import)",id:"import-dynamic-import",level:2},{value:"require.resolveWeak()",id:"requireresolveweak",level:2}],s={toc:u},c="wrapper";function f(e){var t=e.components,n=(0,a.Z)(e,i);return(0,o.mdx)(c,(0,r.Z)({},s,n,{components:t,mdxType:"MDXLayout"}),(0,o.mdx)("p",null,"Metro is designed to allow code written for Node (or for bundlers targeting the Web) to run mostly unmodified. The main APIs available to application code are listed below."),(0,o.mdx)("h2",{id:"require"},(0,o.mdx)("inlineCode",{parentName:"h2"},"require()")),(0,o.mdx)("p",null,"Similar to Node's ",(0,o.mdx)("a",{parentName:"p",href:"https://nodejs.org/api/modules.html#requireid"},(0,o.mdx)("inlineCode",{parentName:"a"},"require()"))," function. ",(0,o.mdx)("inlineCode",{parentName:"p"},"require()")," takes a module name (or path) and returns the result of evaluating that module's code. Modules referenced by ",(0,o.mdx)("inlineCode",{parentName:"p"},"require()")," will be added to the bundle."),(0,o.mdx)("pre",null,(0,o.mdx)("code",{parentName:"pre",className:"language-js"},"const localModule = require('./path/module');\nconst asset = require('./path/asset.png');\nconst jsonData = require('./path/data.json');\nconst {View} = require('react-native');\n")),(0,o.mdx)("p",null,"The argument to ",(0,o.mdx)("inlineCode",{parentName:"p"},"require()")," must be a compile-time constant. The ",(0,o.mdx)("a",{parentName:"p",href:"/docs/configuration#dynamicdepsinpackages"},(0,o.mdx)("inlineCode",{parentName:"a"},"dynamicDepsInPackages"))," config option controls whether calling ",(0,o.mdx)("inlineCode",{parentName:"p"},"require()")," with a non-constant argument will fail at build time or at runtime."),(0,o.mdx)("h3",{id:"advanced-usage-require-at-runtime"},"Advanced usage: ",(0,o.mdx)("inlineCode",{parentName:"h3"},"require")," at runtime"),(0,o.mdx)("p",null,"At build time, Metro ",(0,o.mdx)("a",{parentName:"p",href:"/docs/resolution"},"resolves")," module names to absolute paths and ",(0,o.mdx)("a",{parentName:"p",href:"/docs/configuration#createmoduleidfactory"},"assigns an opaque module ID")," to each one."),(0,o.mdx)("p",null,"At runtime, ",(0,o.mdx)("inlineCode",{parentName:"p"},"require")," refers to a function that takes an opaque module ID (",(0,o.mdx)("em",{parentName:"p"},"not")," a name or path) and returns a module. This can be useful if you already have a module ID returned by another module API, such as ",(0,o.mdx)("a",{parentName:"p",href:"#require-resolveweak"},(0,o.mdx)("inlineCode",{parentName:"a"},"require.resolveWeak")),"."),(0,o.mdx)("pre",null,(0,o.mdx)("code",{parentName:"pre",className:"language-js"},"const localModule = require('./path/module');\nconst id = require.resolveWeak('./path/module');\n// Bypass the restriction on non-constant require() arguments\nconst dynamicRequire = require;\ndynamicRequire(id) === localModule; // true\n")),(0,o.mdx)("h2",{id:"moduleexports"},(0,o.mdx)("inlineCode",{parentName:"h2"},"module.exports")),(0,o.mdx)("p",null,"Similar to ",(0,o.mdx)("a",{parentName:"p",href:"https://nodejs.org/api/modules.html#moduleexports"},(0,o.mdx)("inlineCode",{parentName:"a"},"module.exports"))," in Node. The ",(0,o.mdx)("inlineCode",{parentName:"p"},"module.exports")," property holds the value ",(0,o.mdx)("inlineCode",{parentName:"p"},"require()")," will return for the current module after it finishes evaluating."),(0,o.mdx)("h2",{id:"es-modules-syntax-import-and-export"},"ES Modules syntax (",(0,o.mdx)("inlineCode",{parentName:"h2"},"import")," and ",(0,o.mdx)("inlineCode",{parentName:"h2"},"export"),")"),(0,o.mdx)("p",null,"We currently recommend the use of ",(0,o.mdx)("a",{parentName:"p",href:"https://babeljs.io/docs/babel-plugin-transform-modules-commonjs"},(0,o.mdx)("inlineCode",{parentName:"a"},"@babel/plugin-transform-modules-commonjs"))," in Metro projects to support ",(0,o.mdx)("inlineCode",{parentName:"p"},"import")," and ",(0,o.mdx)("inlineCode",{parentName:"p"},"export"),"."),(0,o.mdx)("admonition",{type:"note"},(0,o.mdx)("p",{parentName:"admonition"},"In React Native projects that use ",(0,o.mdx)("inlineCode",{parentName:"p"},"@react-native/babel-preset"),", ",(0,o.mdx)("inlineCode",{parentName:"p"},"import")," and ",(0,o.mdx)("inlineCode",{parentName:"p"},"export")," are supported out of the box.")),(0,o.mdx)("h2",{id:"import-dynamic-import"},(0,o.mdx)("inlineCode",{parentName:"h2"},"import()")," (dynamic import)"),(0,o.mdx)("p",null,(0,o.mdx)("a",{parentName:"p",href:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import"},(0,o.mdx)("inlineCode",{parentName:"a"},"import()"))," calls are supported out of the box. In React Native, using ",(0,o.mdx)("inlineCode",{parentName:"p"},"import()")," automatically splits your application code so that it loads faster during development, without affecting release builds."),(0,o.mdx)("admonition",{type:"info"},(0,o.mdx)("p",{parentName:"admonition"},(0,o.mdx)("strong",{parentName:"p"},"For framework implementers"),":"),(0,o.mdx)("ol",{parentName:"admonition"},(0,o.mdx)("li",{parentName:"ol"},"Enable ",(0,o.mdx)("a",{parentName:"li",href:"https://github.com/react-native-community/discussions-and-proposals/blob/main/proposals/0605-lazy-bundling.md"},"lazy bundling")," by adding ",(0,o.mdx)("inlineCode",{parentName:"li"},"&lazy=true")," to the initial HTTP bundle URL your framework requests from Metro."),(0,o.mdx)("li",{parentName:"ol"},"At runtime, ",(0,o.mdx)("inlineCode",{parentName:"li"},"import()")," calls a framework-defined function to ",(0,o.mdx)("a",{parentName:"li",href:"https://github.com/react-native-community/discussions-and-proposals/blob/main/proposals/0605-lazy-bundling.md#__loadbundleasync-in-metro"},"fetch and evaluate")," the split bundle. Your framework ",(0,o.mdx)("strong",{parentName:"li"},"must")," implement this function if it uses the ",(0,o.mdx)("inlineCode",{parentName:"li"},"lazy=true")," parameter, or runtime errors will occur."))),(0,o.mdx)("h2",{id:"requireresolveweak"},(0,o.mdx)("inlineCode",{parentName:"h2"},"require.resolveWeak()")),(0,o.mdx)("p",null,"Takes a module name (or path) and returns that module's opaque ID, without including it in the bundle. This is a specialised API intended to be used by frameworks; application code will rarely need to use it directly. See the section about ",(0,o.mdx)("a",{parentName:"p",href:"#advanced-usage-require-at-runtime"},"using ",(0,o.mdx)("inlineCode",{parentName:"a"},"require")," at runtime"),"."))}f.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/e08abecb.8af74805.js b/assets/js/e08abecb.8af74805.js new file mode 100644 index 0000000000..8807c40c59 --- /dev/null +++ b/assets/js/e08abecb.8af74805.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkmetro_website=self.webpackChunkmetro_website||[]).push([[350],{3905:(e,t,n)=>{n.r(t),n.d(t,{MDXContext:()=>m,MDXProvider:()=>s,mdx:()=>x,useMDXComponents:()=>u,withMDXComponents:()=>p});var r=n(67294);function a(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(){return o=Object.assign||function(e){for(var t=1;t=0||(a[n]=e[n]);return a}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}var m=r.createContext({}),p=function(e){return function(t){var n=u(t.components);return r.createElement(e,o({},t,{components:n}))}},u=function(e){var t=r.useContext(m),n=t;return e&&(n="function"==typeof e?e(t):d(d({},t),e)),n},s=function(e){var t=u(e.components);return r.createElement(m.Provider,{value:t},e.children)},c="mdxType",f={inlineCode:"code",wrapper:function(e){var t=e.children;return r.createElement(r.Fragment,{},t)}},h=r.forwardRef((function(e,t){var n=e.components,a=e.mdxType,o=e.originalType,i=e.parentName,m=l(e,["components","mdxType","originalType","parentName"]),p=u(n),s=a,c=p["".concat(i,".").concat(s)]||p[s]||f[s]||o;return n?r.createElement(c,d(d({ref:t},m),{},{components:n})):r.createElement(c,d({ref:t},m))}));function x(e,t){var n=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var o=n.length,i=new Array(o);i[0]=h;var d={};for(var l in t)hasOwnProperty.call(t,l)&&(d[l]=t[l]);d.originalType=e,d[c]="string"==typeof e?e:a,i[1]=d;for(var m=2;m{n.r(t),n.d(t,{assets:()=>p,contentTitle:()=>l,default:()=>f,frontMatter:()=>d,metadata:()=>m,toc:()=>u});var r=n(87462),a=n(63366),o=(n(67294),n(3905)),i=["components"],d={id:"module-api",title:"Module API"},l=void 0,m={unversionedId:"module-api",id:"module-api",title:"Module API",description:"Metro is designed to allow code written for Node (or for bundlers targeting the Web) to run mostly unmodified. The main APIs available to application code are listed below.",source:"@site/../docs/ModuleAPI.md",sourceDirName:".",slug:"/module-api",permalink:"/docs/module-api",draft:!1,editUrl:"https://github.com/facebook/metro/edit/main/docs/../docs/ModuleAPI.md",tags:[],version:"current",lastUpdatedAt:1726493799,formattedLastUpdatedAt:"Sep 16, 2024",frontMatter:{id:"module-api",title:"Module API"},sidebar:"docs",previous:{title:"Bundling API",permalink:"/docs/api"},next:{title:"Configuring Metro",permalink:"/docs/configuration"}},p={},u=[{value:"require()",id:"require",level:2},{value:"Advanced usage: require at runtime",id:"advanced-usage-require-at-runtime",level:3},{value:"module.exports",id:"moduleexports",level:2},{value:"ES Modules syntax (import and export)",id:"es-modules-syntax-import-and-export",level:2},{value:"import() (dynamic import)",id:"import-dynamic-import",level:2},{value:"require.resolveWeak()",id:"requireresolveweak",level:2}],s={toc:u},c="wrapper";function f(e){var t=e.components,n=(0,a.Z)(e,i);return(0,o.mdx)(c,(0,r.Z)({},s,n,{components:t,mdxType:"MDXLayout"}),(0,o.mdx)("p",null,"Metro is designed to allow code written for Node (or for bundlers targeting the Web) to run mostly unmodified. The main APIs available to application code are listed below."),(0,o.mdx)("h2",{id:"require"},(0,o.mdx)("inlineCode",{parentName:"h2"},"require()")),(0,o.mdx)("p",null,"Similar to Node's ",(0,o.mdx)("a",{parentName:"p",href:"https://nodejs.org/api/modules.html#requireid"},(0,o.mdx)("inlineCode",{parentName:"a"},"require()"))," function. ",(0,o.mdx)("inlineCode",{parentName:"p"},"require()")," takes a module name (or path) and returns the result of evaluating that module's code. Modules referenced by ",(0,o.mdx)("inlineCode",{parentName:"p"},"require()")," will be added to the bundle."),(0,o.mdx)("pre",null,(0,o.mdx)("code",{parentName:"pre",className:"language-js"},"const localModule = require('./path/module');\nconst asset = require('./path/asset.png');\nconst jsonData = require('./path/data.json');\nconst {View} = require('react-native');\n")),(0,o.mdx)("p",null,"The argument to ",(0,o.mdx)("inlineCode",{parentName:"p"},"require()")," must be a compile-time constant. The ",(0,o.mdx)("a",{parentName:"p",href:"/docs/configuration#dynamicdepsinpackages"},(0,o.mdx)("inlineCode",{parentName:"a"},"dynamicDepsInPackages"))," config option controls whether calling ",(0,o.mdx)("inlineCode",{parentName:"p"},"require()")," with a non-constant argument will fail at build time or at runtime."),(0,o.mdx)("h3",{id:"advanced-usage-require-at-runtime"},"Advanced usage: ",(0,o.mdx)("inlineCode",{parentName:"h3"},"require")," at runtime"),(0,o.mdx)("p",null,"At build time, Metro ",(0,o.mdx)("a",{parentName:"p",href:"/docs/resolution"},"resolves")," module names to absolute paths and ",(0,o.mdx)("a",{parentName:"p",href:"/docs/configuration#createmoduleidfactory"},"assigns an opaque module ID")," to each one."),(0,o.mdx)("p",null,"At runtime, ",(0,o.mdx)("inlineCode",{parentName:"p"},"require")," refers to a function that takes an opaque module ID (",(0,o.mdx)("em",{parentName:"p"},"not")," a name or path) and returns a module. This can be useful if you already have a module ID returned by another module API, such as ",(0,o.mdx)("a",{parentName:"p",href:"#require-resolveweak"},(0,o.mdx)("inlineCode",{parentName:"a"},"require.resolveWeak")),"."),(0,o.mdx)("pre",null,(0,o.mdx)("code",{parentName:"pre",className:"language-js"},"const localModule = require('./path/module');\nconst id = require.resolveWeak('./path/module');\n// Bypass the restriction on non-constant require() arguments\nconst dynamicRequire = require;\ndynamicRequire(id) === localModule; // true\n")),(0,o.mdx)("h2",{id:"moduleexports"},(0,o.mdx)("inlineCode",{parentName:"h2"},"module.exports")),(0,o.mdx)("p",null,"Similar to ",(0,o.mdx)("a",{parentName:"p",href:"https://nodejs.org/api/modules.html#moduleexports"},(0,o.mdx)("inlineCode",{parentName:"a"},"module.exports"))," in Node. The ",(0,o.mdx)("inlineCode",{parentName:"p"},"module.exports")," property holds the value ",(0,o.mdx)("inlineCode",{parentName:"p"},"require()")," will return for the current module after it finishes evaluating."),(0,o.mdx)("h2",{id:"es-modules-syntax-import-and-export"},"ES Modules syntax (",(0,o.mdx)("inlineCode",{parentName:"h2"},"import")," and ",(0,o.mdx)("inlineCode",{parentName:"h2"},"export"),")"),(0,o.mdx)("p",null,"We currently recommend the use of ",(0,o.mdx)("a",{parentName:"p",href:"https://babeljs.io/docs/babel-plugin-transform-modules-commonjs"},(0,o.mdx)("inlineCode",{parentName:"a"},"@babel/plugin-transform-modules-commonjs"))," in Metro projects to support ",(0,o.mdx)("inlineCode",{parentName:"p"},"import")," and ",(0,o.mdx)("inlineCode",{parentName:"p"},"export"),"."),(0,o.mdx)("admonition",{type:"note"},(0,o.mdx)("p",{parentName:"admonition"},"In React Native projects that use ",(0,o.mdx)("inlineCode",{parentName:"p"},"@react-native/babel-preset"),", ",(0,o.mdx)("inlineCode",{parentName:"p"},"import")," and ",(0,o.mdx)("inlineCode",{parentName:"p"},"export")," are supported out of the box.")),(0,o.mdx)("h2",{id:"import-dynamic-import"},(0,o.mdx)("inlineCode",{parentName:"h2"},"import()")," (dynamic import)"),(0,o.mdx)("p",null,(0,o.mdx)("a",{parentName:"p",href:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import"},(0,o.mdx)("inlineCode",{parentName:"a"},"import()"))," calls are supported out of the box. In React Native, using ",(0,o.mdx)("inlineCode",{parentName:"p"},"import()")," automatically splits your application code so that it loads faster during development, without affecting release builds."),(0,o.mdx)("admonition",{type:"info"},(0,o.mdx)("p",{parentName:"admonition"},(0,o.mdx)("strong",{parentName:"p"},"For framework implementers"),":"),(0,o.mdx)("ol",{parentName:"admonition"},(0,o.mdx)("li",{parentName:"ol"},"Enable ",(0,o.mdx)("a",{parentName:"li",href:"https://github.com/react-native-community/discussions-and-proposals/blob/main/proposals/0605-lazy-bundling.md"},"lazy bundling")," by adding ",(0,o.mdx)("inlineCode",{parentName:"li"},"&lazy=true")," to the initial HTTP bundle URL your framework requests from Metro."),(0,o.mdx)("li",{parentName:"ol"},"At runtime, ",(0,o.mdx)("inlineCode",{parentName:"li"},"import()")," calls a framework-defined function to ",(0,o.mdx)("a",{parentName:"li",href:"https://github.com/react-native-community/discussions-and-proposals/blob/main/proposals/0605-lazy-bundling.md#__loadbundleasync-in-metro"},"fetch and evaluate")," the split bundle. Your framework ",(0,o.mdx)("strong",{parentName:"li"},"must")," implement this function if it uses the ",(0,o.mdx)("inlineCode",{parentName:"li"},"lazy=true")," parameter, or runtime errors will occur."))),(0,o.mdx)("h2",{id:"requireresolveweak"},(0,o.mdx)("inlineCode",{parentName:"h2"},"require.resolveWeak()")),(0,o.mdx)("p",null,"Takes a module name (or path) and returns that module's opaque ID, without including it in the bundle. This is a specialised API intended to be used by frameworks; application code will rarely need to use it directly. See the section about ",(0,o.mdx)("a",{parentName:"p",href:"#advanced-usage-require-at-runtime"},"using ",(0,o.mdx)("inlineCode",{parentName:"a"},"require")," at runtime"),"."))}f.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/fc1f0afe.00628609.js b/assets/js/fc1f0afe.00628609.js new file mode 100644 index 0000000000..8f9e8cd6cc --- /dev/null +++ b/assets/js/fc1f0afe.00628609.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkmetro_website=self.webpackChunkmetro_website||[]).push([[371],{3905:(e,n,t)=>{t.r(n),t.d(n,{MDXContext:()=>l,MDXProvider:()=>m,mdx:()=>g,useMDXComponents:()=>c,withMDXComponents:()=>s});var r=t(67294);function o(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function a(){return a=Object.assign||function(e){for(var n=1;n=0||(o[t]=e[t]);return o}(e,n);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(o[t]=e[t])}return o}var l=r.createContext({}),s=function(e){return function(n){var t=c(n.components);return r.createElement(e,a({},n,{components:t}))}},c=function(e){var n=r.useContext(l),t=n;return e&&(t="function"==typeof e?e(n):d(d({},n),e)),t},m=function(e){var n=c(e.components);return r.createElement(l.Provider,{value:n},e.children)},u="mdxType",f={inlineCode:"code",wrapper:function(e){var n=e.children;return r.createElement(r.Fragment,{},n)}},x=r.forwardRef((function(e,n){var t=e.components,o=e.mdxType,a=e.originalType,i=e.parentName,l=p(e,["components","mdxType","originalType","parentName"]),s=c(t),m=o,u=s["".concat(i,".").concat(m)]||s[m]||f[m]||a;return t?r.createElement(u,d(d({ref:n},l),{},{components:t})):r.createElement(u,d({ref:n},l))}));function g(e,n){var t=arguments,o=n&&n.mdxType;if("string"==typeof e||o){var a=t.length,i=new Array(a);i[0]=x;var d={};for(var p in n)hasOwnProperty.call(n,p)&&(d[p]=n[p]);d.originalType=e,d[u]="string"==typeof e?e:o,i[1]=d;for(var l=2;l{t.r(n),t.d(n,{assets:()=>s,contentTitle:()=>p,default:()=>f,frontMatter:()=>d,metadata:()=>l,toc:()=>c});var r=t(87462),o=t(63366),a=(t(67294),t(3905)),i=["components"],d={id:"api",title:"Bundling API"},p=void 0,l={unversionedId:"api",id:"api",title:"Bundling API",description:"Quick Start",source:"@site/../docs/API.md",sourceDirName:".",slug:"/api",permalink:"/docs/api",draft:!1,editUrl:"https://github.com/facebook/metro/edit/main/docs/../docs/API.md",tags:[],version:"current",lastUpdatedAt:1726493799,formattedLastUpdatedAt:"Sep 16, 2024",frontMatter:{id:"api",title:"Bundling API"},sidebar:"docs",previous:{title:"Concepts",permalink:"/docs/concepts"},next:{title:"Module API",permalink:"/docs/module-api"}},s={},c=[{value:"Quick Start",id:"quick-start",level:2},{value:"Reference",id:"reference",level:2},{value:"loadConfig(<options>)",id:"loadconfigoptions",level:3},{value:"async runMetro(config)",id:"async-runmetroconfig",level:3},{value:"async runBuild(config, <options>)",id:"async-runbuildconfig-options",level:3},{value:"async runServer(config, <options>)",id:"async-runserverconfig-options",level:3},{value:"createConnectMiddleware(config, <options>)",id:"createconnectmiddlewareconfig-options",level:3}],m={toc:c},u="wrapper";function f(e){var n=e.components,t=(0,o.Z)(e,i);return(0,a.mdx)(u,(0,r.Z)({},m,t,{components:n,mdxType:"MDXLayout"}),(0,a.mdx)("h2",{id:"quick-start"},"Quick Start"),(0,a.mdx)("ul",null,(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("p",{parentName:"li"},"Compile a file"),(0,a.mdx)("pre",{parentName:"li"},(0,a.mdx)("code",{parentName:"pre",className:"language-js"},"const config = await Metro.loadConfig();\n\nawait Metro.runBuild(config, {\n entry: 'index.js',\n out: 'bundle.js',\n});\n"))),(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("p",{parentName:"li"},"Run a server and watch the filesystem for changes"),(0,a.mdx)("pre",{parentName:"li"},(0,a.mdx)("code",{parentName:"pre",className:"language-js"},"const config = await Metro.loadConfig();\n\nawait Metro.runServer(config);\n"))),(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("p",{parentName:"li"},"Create a Connect middleware and plug it into a server"),(0,a.mdx)("pre",{parentName:"li"},(0,a.mdx)("code",{parentName:"pre",className:"language-js"},"const Metro = require('metro');\nconst express = require('express');\nconst app = express();\nconst server = require('http').Server(app);\n\nMetro.loadConfig().then(async config => {\n const connectMiddleware = await Metro.createConnectMiddleware(config);\n const {server: {port}} = config;\n\n app.use(connectMiddleware.middleware);\n server.listen(port);\n connectMiddleware.attachHmrServer(server);\n});\n")))),(0,a.mdx)("h2",{id:"reference"},"Reference"),(0,a.mdx)("p",null,"All functions exposed below accept an additional ",(0,a.mdx)("inlineCode",{parentName:"p"},"config")," option. This object should be the ",(0,a.mdx)("a",{parentName:"p",href:"/docs/cli"},"Metro configuration")," exposed by your ",(0,a.mdx)("inlineCode",{parentName:"p"},"metro.config.js")," file - you can obtain it using ",(0,a.mdx)("inlineCode",{parentName:"p"},"Metro.loadConfig"),"."),(0,a.mdx)("h3",{id:"loadconfigoptions"},(0,a.mdx)("inlineCode",{parentName:"h3"},"loadConfig()")),(0,a.mdx)("p",null,(0,a.mdx)("strong",{parentName:"p"},"Basic options:")," ",(0,a.mdx)("inlineCode",{parentName:"p"},"config"),", ",(0,a.mdx)("inlineCode",{parentName:"p"},"cwd")),(0,a.mdx)("p",null,"Load the Metro configuration, either from ",(0,a.mdx)("inlineCode",{parentName:"p"},"config")," in options if specified, or by traversing the directory hierarchy from ",(0,a.mdx)("inlineCode",{parentName:"p"},"cwd")," to the root until it finds a file (by default ",(0,a.mdx)("inlineCode",{parentName:"p"},"metro.config.js"),"). The returned configuration will have been normalized and merged with Metro's default values."),(0,a.mdx)("h3",{id:"async-runmetroconfig"},(0,a.mdx)("inlineCode",{parentName:"h3"},"async runMetro(config)")),(0,a.mdx)("p",null,"Creates a Metro server based on the config and returns it. You can use this as a middleware in your existing server."),(0,a.mdx)("h3",{id:"async-runbuildconfig-options"},(0,a.mdx)("inlineCode",{parentName:"h3"},"async runBuild(config, )")),(0,a.mdx)("p",null,(0,a.mdx)("strong",{parentName:"p"},"Required options:")," ",(0,a.mdx)("inlineCode",{parentName:"p"},"entry"),", ",(0,a.mdx)("inlineCode",{parentName:"p"},"out")),(0,a.mdx)("p",null,(0,a.mdx)("strong",{parentName:"p"},"Basic options:")," ",(0,a.mdx)("inlineCode",{parentName:"p"},"dev"),", ",(0,a.mdx)("inlineCode",{parentName:"p"},"minify"),", ",(0,a.mdx)("inlineCode",{parentName:"p"},"platform"),", ",(0,a.mdx)("inlineCode",{parentName:"p"},"sourceMap"),", ",(0,a.mdx)("inlineCode",{parentName:"p"},"sourceMapUrl")),(0,a.mdx)("p",null,"Bundles ",(0,a.mdx)("inlineCode",{parentName:"p"},"entry")," for the given ",(0,a.mdx)("inlineCode",{parentName:"p"},"platform"),", and saves it to location ",(0,a.mdx)("inlineCode",{parentName:"p"},"out"),". If ",(0,a.mdx)("inlineCode",{parentName:"p"},"sourceMap")," is set, also generates a source map. The source map will be inlined, unless ",(0,a.mdx)("inlineCode",{parentName:"p"},"sourceMapUrl")," is also defined. In the latter case, a new file will be generated with the basename of the ",(0,a.mdx)("inlineCode",{parentName:"p"},"sourceMapUrl")," parameter."),(0,a.mdx)("h3",{id:"async-runserverconfig-options"},(0,a.mdx)("inlineCode",{parentName:"h3"},"async runServer(config, )")),(0,a.mdx)("p",null,(0,a.mdx)("strong",{parentName:"p"},"Basic options:")," ",(0,a.mdx)("inlineCode",{parentName:"p"},"host"),", ",(0,a.mdx)("inlineCode",{parentName:"p"},"port"),", ",(0,a.mdx)("inlineCode",{parentName:"p"},"secureServerOptions"),", ",(0,a.mdx)("inlineCode",{parentName:"p"},"secure (DEPRECATED)"),", ",(0,a.mdx)("inlineCode",{parentName:"p"},"secureKey (DEPRECATED)"),", ",(0,a.mdx)("inlineCode",{parentName:"p"},"secureCert (DEPRECATED)")),(0,a.mdx)("p",null,"Starts a full Metro HTTP server. It will listen on the specified ",(0,a.mdx)("inlineCode",{parentName:"p"},"host:port"),", and can then be queried to retrieve bundles for various entry points. If the ",(0,a.mdx)("inlineCode",{parentName:"p"},"secureServerOptions")," family of options are present, the server will be exposed over HTTPS."),(0,a.mdx)("p",null,(0,a.mdx)("inlineCode",{parentName:"p"},"secure"),", ",(0,a.mdx)("inlineCode",{parentName:"p"},"secureKey"),", ",(0,a.mdx)("inlineCode",{parentName:"p"},"secureCert")," are now deprecated and will be removed in a later release. The presence of ",(0,a.mdx)("inlineCode",{parentName:"p"},"secureServerOptions"),", along with its options will make Metro run over https."),(0,a.mdx)("h3",{id:"createconnectmiddlewareconfig-options"},(0,a.mdx)("inlineCode",{parentName:"h3"},"createConnectMiddleware(config, )")),(0,a.mdx)("p",null,(0,a.mdx)("strong",{parentName:"p"},"Basic options:")," ",(0,a.mdx)("inlineCode",{parentName:"p"},"port"),", ",(0,a.mdx)("inlineCode",{parentName:"p"},"onBundleBuilt")),(0,a.mdx)("p",null,"Instead of creating the full server, creates a Connect middleware that answers to bundle requests. This middleware can then be plugged into your own servers. The ",(0,a.mdx)("inlineCode",{parentName:"p"},"port")," parameter is optional and only used for logging purposes. The ",(0,a.mdx)("inlineCode",{parentName:"p"},"onBundleBuilt")," function is optional, is passed the bundle name, and is called when the server has finishing creating the bundle."))}f.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/fc1f0afe.42b7720d.js b/assets/js/fc1f0afe.42b7720d.js deleted file mode 100644 index c1a6503557..0000000000 --- a/assets/js/fc1f0afe.42b7720d.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkmetro_website=self.webpackChunkmetro_website||[]).push([[371],{3905:(e,n,t)=>{t.r(n),t.d(n,{MDXContext:()=>l,MDXProvider:()=>m,mdx:()=>g,useMDXComponents:()=>c,withMDXComponents:()=>s});var r=t(67294);function o(e,n,t){return n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t,e}function a(){return a=Object.assign||function(e){for(var n=1;n=0||(o[t]=e[t]);return o}(e,n);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);for(r=0;r=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(o[t]=e[t])}return o}var l=r.createContext({}),s=function(e){return function(n){var t=c(n.components);return r.createElement(e,a({},n,{components:t}))}},c=function(e){var n=r.useContext(l),t=n;return e&&(t="function"==typeof e?e(n):d(d({},n),e)),t},m=function(e){var n=c(e.components);return r.createElement(l.Provider,{value:n},e.children)},u="mdxType",f={inlineCode:"code",wrapper:function(e){var n=e.children;return r.createElement(r.Fragment,{},n)}},x=r.forwardRef((function(e,n){var t=e.components,o=e.mdxType,a=e.originalType,i=e.parentName,l=p(e,["components","mdxType","originalType","parentName"]),s=c(t),m=o,u=s["".concat(i,".").concat(m)]||s[m]||f[m]||a;return t?r.createElement(u,d(d({ref:n},l),{},{components:t})):r.createElement(u,d({ref:n},l))}));function g(e,n){var t=arguments,o=n&&n.mdxType;if("string"==typeof e||o){var a=t.length,i=new Array(a);i[0]=x;var d={};for(var p in n)hasOwnProperty.call(n,p)&&(d[p]=n[p]);d.originalType=e,d[u]="string"==typeof e?e:o,i[1]=d;for(var l=2;l{t.r(n),t.d(n,{assets:()=>s,contentTitle:()=>p,default:()=>f,frontMatter:()=>d,metadata:()=>l,toc:()=>c});var r=t(87462),o=t(63366),a=(t(67294),t(3905)),i=["components"],d={id:"api",title:"Bundling API"},p=void 0,l={unversionedId:"api",id:"api",title:"Bundling API",description:"Quick Start",source:"@site/../docs/API.md",sourceDirName:".",slug:"/api",permalink:"/docs/api",draft:!1,editUrl:"https://github.com/facebook/metro/edit/main/docs/../docs/API.md",tags:[],version:"current",lastUpdatedAt:1725545477,formattedLastUpdatedAt:"Sep 5, 2024",frontMatter:{id:"api",title:"Bundling API"},sidebar:"docs",previous:{title:"Concepts",permalink:"/docs/concepts"},next:{title:"Module API",permalink:"/docs/module-api"}},s={},c=[{value:"Quick Start",id:"quick-start",level:2},{value:"Reference",id:"reference",level:2},{value:"loadConfig(<options>)",id:"loadconfigoptions",level:3},{value:"async runMetro(config)",id:"async-runmetroconfig",level:3},{value:"async runBuild(config, <options>)",id:"async-runbuildconfig-options",level:3},{value:"async runServer(config, <options>)",id:"async-runserverconfig-options",level:3},{value:"createConnectMiddleware(config, <options>)",id:"createconnectmiddlewareconfig-options",level:3}],m={toc:c},u="wrapper";function f(e){var n=e.components,t=(0,o.Z)(e,i);return(0,a.mdx)(u,(0,r.Z)({},m,t,{components:n,mdxType:"MDXLayout"}),(0,a.mdx)("h2",{id:"quick-start"},"Quick Start"),(0,a.mdx)("ul",null,(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("p",{parentName:"li"},"Compile a file"),(0,a.mdx)("pre",{parentName:"li"},(0,a.mdx)("code",{parentName:"pre",className:"language-js"},"const config = await Metro.loadConfig();\n\nawait Metro.runBuild(config, {\n entry: 'index.js',\n out: 'bundle.js',\n});\n"))),(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("p",{parentName:"li"},"Run a server and watch the filesystem for changes"),(0,a.mdx)("pre",{parentName:"li"},(0,a.mdx)("code",{parentName:"pre",className:"language-js"},"const config = await Metro.loadConfig();\n\nawait Metro.runServer(config);\n"))),(0,a.mdx)("li",{parentName:"ul"},(0,a.mdx)("p",{parentName:"li"},"Create a Connect middleware and plug it into a server"),(0,a.mdx)("pre",{parentName:"li"},(0,a.mdx)("code",{parentName:"pre",className:"language-js"},"const Metro = require('metro');\nconst express = require('express');\nconst app = express();\nconst server = require('http').Server(app);\n\nMetro.loadConfig().then(async config => {\n const connectMiddleware = await Metro.createConnectMiddleware(config);\n const {server: {port}} = config;\n\n app.use(connectMiddleware.middleware);\n server.listen(port);\n connectMiddleware.attachHmrServer(server);\n});\n")))),(0,a.mdx)("h2",{id:"reference"},"Reference"),(0,a.mdx)("p",null,"All functions exposed below accept an additional ",(0,a.mdx)("inlineCode",{parentName:"p"},"config")," option. This object should be the ",(0,a.mdx)("a",{parentName:"p",href:"/docs/cli"},"Metro configuration")," exposed by your ",(0,a.mdx)("inlineCode",{parentName:"p"},"metro.config.js")," file - you can obtain it using ",(0,a.mdx)("inlineCode",{parentName:"p"},"Metro.loadConfig"),"."),(0,a.mdx)("h3",{id:"loadconfigoptions"},(0,a.mdx)("inlineCode",{parentName:"h3"},"loadConfig()")),(0,a.mdx)("p",null,(0,a.mdx)("strong",{parentName:"p"},"Basic options:")," ",(0,a.mdx)("inlineCode",{parentName:"p"},"config"),", ",(0,a.mdx)("inlineCode",{parentName:"p"},"cwd")),(0,a.mdx)("p",null,"Load the Metro configuration, either from ",(0,a.mdx)("inlineCode",{parentName:"p"},"config")," in options if specified, or by traversing the directory hierarchy from ",(0,a.mdx)("inlineCode",{parentName:"p"},"cwd")," to the root until it finds a file (by default ",(0,a.mdx)("inlineCode",{parentName:"p"},"metro.config.js"),"). The returned configuration will have been normalized and merged with Metro's default values."),(0,a.mdx)("h3",{id:"async-runmetroconfig"},(0,a.mdx)("inlineCode",{parentName:"h3"},"async runMetro(config)")),(0,a.mdx)("p",null,"Creates a Metro server based on the config and returns it. You can use this as a middleware in your existing server."),(0,a.mdx)("h3",{id:"async-runbuildconfig-options"},(0,a.mdx)("inlineCode",{parentName:"h3"},"async runBuild(config, )")),(0,a.mdx)("p",null,(0,a.mdx)("strong",{parentName:"p"},"Required options:")," ",(0,a.mdx)("inlineCode",{parentName:"p"},"entry"),", ",(0,a.mdx)("inlineCode",{parentName:"p"},"out")),(0,a.mdx)("p",null,(0,a.mdx)("strong",{parentName:"p"},"Basic options:")," ",(0,a.mdx)("inlineCode",{parentName:"p"},"dev"),", ",(0,a.mdx)("inlineCode",{parentName:"p"},"minify"),", ",(0,a.mdx)("inlineCode",{parentName:"p"},"platform"),", ",(0,a.mdx)("inlineCode",{parentName:"p"},"sourceMap"),", ",(0,a.mdx)("inlineCode",{parentName:"p"},"sourceMapUrl")),(0,a.mdx)("p",null,"Bundles ",(0,a.mdx)("inlineCode",{parentName:"p"},"entry")," for the given ",(0,a.mdx)("inlineCode",{parentName:"p"},"platform"),", and saves it to location ",(0,a.mdx)("inlineCode",{parentName:"p"},"out"),". If ",(0,a.mdx)("inlineCode",{parentName:"p"},"sourceMap")," is set, also generates a source map. The source map will be inlined, unless ",(0,a.mdx)("inlineCode",{parentName:"p"},"sourceMapUrl")," is also defined. In the latter case, a new file will be generated with the basename of the ",(0,a.mdx)("inlineCode",{parentName:"p"},"sourceMapUrl")," parameter."),(0,a.mdx)("h3",{id:"async-runserverconfig-options"},(0,a.mdx)("inlineCode",{parentName:"h3"},"async runServer(config, )")),(0,a.mdx)("p",null,(0,a.mdx)("strong",{parentName:"p"},"Basic options:")," ",(0,a.mdx)("inlineCode",{parentName:"p"},"host"),", ",(0,a.mdx)("inlineCode",{parentName:"p"},"port"),", ",(0,a.mdx)("inlineCode",{parentName:"p"},"secureServerOptions"),", ",(0,a.mdx)("inlineCode",{parentName:"p"},"secure (DEPRECATED)"),", ",(0,a.mdx)("inlineCode",{parentName:"p"},"secureKey (DEPRECATED)"),", ",(0,a.mdx)("inlineCode",{parentName:"p"},"secureCert (DEPRECATED)")),(0,a.mdx)("p",null,"Starts a full Metro HTTP server. It will listen on the specified ",(0,a.mdx)("inlineCode",{parentName:"p"},"host:port"),", and can then be queried to retrieve bundles for various entry points. If the ",(0,a.mdx)("inlineCode",{parentName:"p"},"secureServerOptions")," family of options are present, the server will be exposed over HTTPS."),(0,a.mdx)("p",null,(0,a.mdx)("inlineCode",{parentName:"p"},"secure"),", ",(0,a.mdx)("inlineCode",{parentName:"p"},"secureKey"),", ",(0,a.mdx)("inlineCode",{parentName:"p"},"secureCert")," are now deprecated and will be removed in a later release. The presence of ",(0,a.mdx)("inlineCode",{parentName:"p"},"secureServerOptions"),", along with its options will make Metro run over https."),(0,a.mdx)("h3",{id:"createconnectmiddlewareconfig-options"},(0,a.mdx)("inlineCode",{parentName:"h3"},"createConnectMiddleware(config, )")),(0,a.mdx)("p",null,(0,a.mdx)("strong",{parentName:"p"},"Basic options:")," ",(0,a.mdx)("inlineCode",{parentName:"p"},"port"),", ",(0,a.mdx)("inlineCode",{parentName:"p"},"onBundleBuilt")),(0,a.mdx)("p",null,"Instead of creating the full server, creates a Connect middleware that answers to bundle requests. This middleware can then be plugged into your own servers. The ",(0,a.mdx)("inlineCode",{parentName:"p"},"port")," parameter is optional and only used for logging purposes. The ",(0,a.mdx)("inlineCode",{parentName:"p"},"onBundleBuilt")," function is optional, is passed the bundle name, and is called when the server has finishing creating the bundle."))}f.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/runtime~main.498a6a89.js b/assets/js/runtime~main.498a6a89.js new file mode 100644 index 0000000000..0254b2f85c --- /dev/null +++ b/assets/js/runtime~main.498a6a89.js @@ -0,0 +1 @@ +(()=>{"use strict";var e,t,r,a,o,n={},f={};function c(e){var t=f[e];if(void 0!==t)return t.exports;var r=f[e]={id:e,loaded:!1,exports:{}};return n[e].call(r.exports,r,r.exports,c),r.loaded=!0,r.exports}c.m=n,e=[],c.O=(t,r,a,o)=>{if(!r){var n=1/0;for(b=0;b=o)&&Object.keys(c.O).every((e=>c.O[e](r[i])))?r.splice(i--,1):(f=!1,o0&&e[b-1][2]>o;b--)e[b]=e[b-1];e[b]=[r,a,o]},c.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return c.d(t,{a:t}),t},r=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,c.t=function(e,a){if(1&a&&(e=this(e)),8&a)return e;if("object"==typeof e&&e){if(4&a&&e.__esModule)return e;if(16&a&&"function"==typeof e.then)return e}var o=Object.create(null);c.r(o);var n={};t=t||[null,r({}),r([]),r(r)];for(var f=2&a&&e;"object"==typeof f&&!~t.indexOf(f);f=r(f))Object.getOwnPropertyNames(f).forEach((t=>n[t]=()=>e[t]));return n.default=()=>e,c.d(o,n),o},c.d=(e,t)=>{for(var r in t)c.o(t,r)&&!c.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},c.f={},c.e=e=>Promise.all(Object.keys(c.f).reduce(((t,r)=>(c.f[r](e,t),t)),[])),c.u=e=>"assets/js/"+({53:"935f2afb",76:"6681bed1",93:"78e9b454",122:"ba2a2799",123:"6c52fe38",177:"ba92a0db",195:"c4f5d8e4",350:"e08abecb",371:"fc1f0afe",424:"8b9b5fbc",476:"c026fe4c",514:"1be78505",536:"37a9b1a2",576:"48f2cf36",599:"812be9b9",645:"51001101",662:"163e71e1",665:"feaddec7",708:"ae2a35ac",874:"6373d68f",918:"17896441",920:"1a4e3797"}[e]||e)+"."+{53:"cb0033a7",76:"0222a461",93:"f3220979",122:"68dc6d81",123:"a64d4123",177:"38175987",195:"8184aa50",350:"8af74805",371:"00628609",389:"d8e0215b",424:"10a336ef",426:"512010ee",476:"876ba969",514:"3b482365",536:"fa60b8af",576:"2a9694db",599:"8f22fd96",645:"e0c4b585",662:"40d4e4cc",665:"5a1694ae",708:"c3e41158",874:"cfd624d3",894:"3a7135e9",918:"d528c200",920:"d90546f0",945:"6f43f8c7",972:"7f5ac13f"}[e]+".js",c.miniCssF=e=>{},c.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),c.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),a={},o="metro-website:",c.l=(e,t,r,n)=>{if(a[e])a[e].push(t);else{var f,i;if(void 0!==r)for(var d=document.getElementsByTagName("script"),b=0;b{f.onerror=f.onload=null,clearTimeout(s);var o=a[e];if(delete a[e],f.parentNode&&f.parentNode.removeChild(f),o&&o.forEach((e=>e(r))),t)return t(r)},s=setTimeout(u.bind(null,void 0,{type:"timeout",target:f}),12e4);f.onerror=u.bind(null,f.onerror),f.onload=u.bind(null,f.onload),i&&document.head.appendChild(f)}},c.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},c.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),c.p="/",c.gca=function(e){return e={17896441:"918",51001101:"645","935f2afb":"53","6681bed1":"76","78e9b454":"93",ba2a2799:"122","6c52fe38":"123",ba92a0db:"177",c4f5d8e4:"195",e08abecb:"350",fc1f0afe:"371","8b9b5fbc":"424",c026fe4c:"476","1be78505":"514","37a9b1a2":"536","48f2cf36":"576","812be9b9":"599","163e71e1":"662",feaddec7:"665",ae2a35ac:"708","6373d68f":"874","1a4e3797":"920"}[e]||e,c.p+c.u(e)},(()=>{var e={303:0,532:0};c.f.j=(t,r)=>{var a=c.o(e,t)?e[t]:void 0;if(0!==a)if(a)r.push(a[2]);else if(/^(303|532)$/.test(t))e[t]=0;else{var o=new Promise(((r,o)=>a=e[t]=[r,o]));r.push(a[2]=o);var n=c.p+c.u(t),f=new Error;c.l(n,(r=>{if(c.o(e,t)&&(0!==(a=e[t])&&(e[t]=void 0),a)){var o=r&&("load"===r.type?"missing":r.type),n=r&&r.target&&r.target.src;f.message="Loading chunk "+t+" failed.\n("+o+": "+n+")",f.name="ChunkLoadError",f.type=o,f.request=n,a[1](f)}}),"chunk-"+t,t)}},c.O.j=t=>0===e[t];var t=(t,r)=>{var a,o,[n,f,i]=r,d=0;if(n.some((t=>0!==e[t]))){for(a in f)c.o(f,a)&&(c.m[a]=f[a]);if(i)var b=i(c)}for(t&&t(r);d{"use strict";var e,t,r,a,o,f={},n={};function c(e){var t=n[e];if(void 0!==t)return t.exports;var r=n[e]={id:e,loaded:!1,exports:{}};return f[e].call(r.exports,r,r.exports,c),r.loaded=!0,r.exports}c.m=f,e=[],c.O=(t,r,a,o)=>{if(!r){var f=1/0;for(b=0;b=o)&&Object.keys(c.O).every((e=>c.O[e](r[d])))?r.splice(d--,1):(n=!1,o0&&e[b-1][2]>o;b--)e[b]=e[b-1];e[b]=[r,a,o]},c.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return c.d(t,{a:t}),t},r=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,c.t=function(e,a){if(1&a&&(e=this(e)),8&a)return e;if("object"==typeof e&&e){if(4&a&&e.__esModule)return e;if(16&a&&"function"==typeof e.then)return e}var o=Object.create(null);c.r(o);var f={};t=t||[null,r({}),r([]),r(r)];for(var n=2&a&&e;"object"==typeof n&&!~t.indexOf(n);n=r(n))Object.getOwnPropertyNames(n).forEach((t=>f[t]=()=>e[t]));return f.default=()=>e,c.d(o,f),o},c.d=(e,t)=>{for(var r in t)c.o(t,r)&&!c.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},c.f={},c.e=e=>Promise.all(Object.keys(c.f).reduce(((t,r)=>(c.f[r](e,t),t)),[])),c.u=e=>"assets/js/"+({53:"935f2afb",76:"6681bed1",93:"78e9b454",122:"ba2a2799",123:"6c52fe38",177:"ba92a0db",195:"c4f5d8e4",350:"e08abecb",371:"fc1f0afe",424:"8b9b5fbc",476:"c026fe4c",514:"1be78505",536:"37a9b1a2",576:"48f2cf36",599:"812be9b9",645:"51001101",662:"163e71e1",665:"feaddec7",708:"ae2a35ac",874:"6373d68f",918:"17896441",920:"1a4e3797"}[e]||e)+"."+{53:"cb0033a7",76:"bfd5a18a",93:"f3220979",122:"cb6433e7",123:"a64d4123",177:"b7d52f23",195:"8184aa50",350:"2232dd98",371:"42b7720d",389:"d8e0215b",424:"a81dcaf8",426:"512010ee",476:"8ca3da37",514:"3b482365",536:"d7f3ecdf",576:"2a9694db",599:"6b8aca99",645:"f523a36e",662:"d684e31e",665:"5a1694ae",708:"ec9f76b2",874:"d1846fd8",894:"3a7135e9",918:"d528c200",920:"d90546f0",945:"6f43f8c7",972:"7f5ac13f"}[e]+".js",c.miniCssF=e=>{},c.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),c.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),a={},o="metro-website:",c.l=(e,t,r,f)=>{if(a[e])a[e].push(t);else{var n,d;if(void 0!==r)for(var i=document.getElementsByTagName("script"),b=0;b{n.onerror=n.onload=null,clearTimeout(s);var o=a[e];if(delete a[e],n.parentNode&&n.parentNode.removeChild(n),o&&o.forEach((e=>e(r))),t)return t(r)},s=setTimeout(u.bind(null,void 0,{type:"timeout",target:n}),12e4);n.onerror=u.bind(null,n.onerror),n.onload=u.bind(null,n.onload),d&&document.head.appendChild(n)}},c.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},c.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),c.p="/",c.gca=function(e){return e={17896441:"918",51001101:"645","935f2afb":"53","6681bed1":"76","78e9b454":"93",ba2a2799:"122","6c52fe38":"123",ba92a0db:"177",c4f5d8e4:"195",e08abecb:"350",fc1f0afe:"371","8b9b5fbc":"424",c026fe4c:"476","1be78505":"514","37a9b1a2":"536","48f2cf36":"576","812be9b9":"599","163e71e1":"662",feaddec7:"665",ae2a35ac:"708","6373d68f":"874","1a4e3797":"920"}[e]||e,c.p+c.u(e)},(()=>{var e={303:0,532:0};c.f.j=(t,r)=>{var a=c.o(e,t)?e[t]:void 0;if(0!==a)if(a)r.push(a[2]);else if(/^(303|532)$/.test(t))e[t]=0;else{var o=new Promise(((r,o)=>a=e[t]=[r,o]));r.push(a[2]=o);var f=c.p+c.u(t),n=new Error;c.l(f,(r=>{if(c.o(e,t)&&(0!==(a=e[t])&&(e[t]=void 0),a)){var o=r&&("load"===r.type?"missing":r.type),f=r&&r.target&&r.target.src;n.message="Loading chunk "+t+" failed.\n("+o+": "+f+")",n.name="ChunkLoadError",n.type=o,n.request=f,a[1](n)}}),"chunk-"+t,t)}},c.O.j=t=>0===e[t];var t=(t,r)=>{var a,o,[f,n,d]=r,i=0;if(f.some((t=>0!==e[t]))){for(a in n)c.o(n,a)&&(c.m[a]=n[a]);if(d)var b=d(c)}for(t&&t(r);i - +
-

Bundling API

Quick Start​

  • Compile a file

    const config = await Metro.loadConfig();

    await Metro.runBuild(config, {
    entry: 'index.js',
    out: 'bundle.js',
    });
  • Run a server and watch the filesystem for changes

    const config = await Metro.loadConfig();

    await Metro.runServer(config);
  • Create a Connect middleware and plug it into a server

    const Metro = require('metro');
    const express = require('express');
    const app = express();
    const server = require('http').Server(app);

    Metro.loadConfig().then(async config => {
    const connectMiddleware = await Metro.createConnectMiddleware(config);
    const {server: {port}} = config;

    app.use(connectMiddleware.middleware);
    server.listen(port);
    connectMiddleware.attachHmrServer(server);
    });

Reference​

All functions exposed below accept an additional config option. This object should be the Metro configuration exposed by your metro.config.js file - you can obtain it using Metro.loadConfig.

loadConfig(<options>)​

Basic options: config, cwd

Load the Metro configuration, either from config in options if specified, or by traversing the directory hierarchy from cwd to the root until it finds a file (by default metro.config.js). The returned configuration will have been normalized and merged with Metro's default values.

async runMetro(config)​

Creates a Metro server based on the config and returns it. You can use this as a middleware in your existing server.

async runBuild(config, <options>)​

Required options: entry, out

Basic options: dev, minify, platform, sourceMap, sourceMapUrl

Bundles entry for the given platform, and saves it to location out. If sourceMap is set, also generates a source map. The source map will be inlined, unless sourceMapUrl is also defined. In the latter case, a new file will be generated with the basename of the sourceMapUrl parameter.

async runServer(config, <options>)​

Basic options: host, port, secureServerOptions, secure (DEPRECATED), secureKey (DEPRECATED), secureCert (DEPRECATED)

Starts a full Metro HTTP server. It will listen on the specified host:port, and can then be queried to retrieve bundles for various entry points. If the secureServerOptions family of options are present, the server will be exposed over HTTPS.

secure, secureKey, secureCert are now deprecated and will be removed in a later release. The presence of secureServerOptions, along with its options will make Metro run over https.

createConnectMiddleware(config, <options>)​

Basic options: port, onBundleBuilt

Instead of creating the full server, creates a Connect middleware that answers to bundle requests. This middleware can then be plugged into your own servers. The port parameter is optional and only used for logging purposes. The onBundleBuilt function is optional, is passed the bundle name, and is called when the server has finishing creating the bundle.

- +

Bundling API

Quick Start​

  • Compile a file

    const config = await Metro.loadConfig();

    await Metro.runBuild(config, {
    entry: 'index.js',
    out: 'bundle.js',
    });
  • Run a server and watch the filesystem for changes

    const config = await Metro.loadConfig();

    await Metro.runServer(config);
  • Create a Connect middleware and plug it into a server

    const Metro = require('metro');
    const express = require('express');
    const app = express();
    const server = require('http').Server(app);

    Metro.loadConfig().then(async config => {
    const connectMiddleware = await Metro.createConnectMiddleware(config);
    const {server: {port}} = config;

    app.use(connectMiddleware.middleware);
    server.listen(port);
    connectMiddleware.attachHmrServer(server);
    });

Reference​

All functions exposed below accept an additional config option. This object should be the Metro configuration exposed by your metro.config.js file - you can obtain it using Metro.loadConfig.

loadConfig(<options>)​

Basic options: config, cwd

Load the Metro configuration, either from config in options if specified, or by traversing the directory hierarchy from cwd to the root until it finds a file (by default metro.config.js). The returned configuration will have been normalized and merged with Metro's default values.

async runMetro(config)​

Creates a Metro server based on the config and returns it. You can use this as a middleware in your existing server.

async runBuild(config, <options>)​

Required options: entry, out

Basic options: dev, minify, platform, sourceMap, sourceMapUrl

Bundles entry for the given platform, and saves it to location out. If sourceMap is set, also generates a source map. The source map will be inlined, unless sourceMapUrl is also defined. In the latter case, a new file will be generated with the basename of the sourceMapUrl parameter.

async runServer(config, <options>)​

Basic options: host, port, secureServerOptions, secure (DEPRECATED), secureKey (DEPRECATED), secureCert (DEPRECATED)

Starts a full Metro HTTP server. It will listen on the specified host:port, and can then be queried to retrieve bundles for various entry points. If the secureServerOptions family of options are present, the server will be exposed over HTTPS.

secure, secureKey, secureCert are now deprecated and will be removed in a later release. The presence of secureServerOptions, along with its options will make Metro run over https.

createConnectMiddleware(config, <options>)​

Basic options: port, onBundleBuilt

Instead of creating the full server, creates a Connect middleware that answers to bundle requests. This middleware can then be plugged into your own servers. The port parameter is optional and only used for logging purposes. The onBundleBuilt function is optional, is passed the bundle name, and is called when the server has finishing creating the bundle.

+ \ No newline at end of file diff --git a/docs/bundling/index.html b/docs/bundling/index.html index 8629ef6506..1f80d3a194 100644 --- a/docs/bundling/index.html +++ b/docs/bundling/index.html @@ -13,7 +13,7 @@ - + @@ -21,8 +21,8 @@

Bundle Formats

When bundling, each of the modules gets assigned a numeric id, meaning no dynamic requires are supported. Requires are changed by its numeric version, and modules are stored in different possible formats. Three different formats of bundling are supported:

Plain bundle​

This is the standard bundling format. In this format, all files are wrapped with a function call, then added to the global file. This is useful for environments that expect a JS only bundle (e.g. a browser). Just requiring the entry point with the .bundle extension should trigger a build of it.

Indexed RAM bundle​

This format composes the bundle as a binary file, which format has the following parts (all numbers are expressed in Little Endian):

  • A magic number: a uint32 must be located at the beginning of the file, with the value 0xFB0BD1E5. This is used to verify the file.
  • An offset table: the table is a sequence of uint32 pairs, with a header
    • For the header, two uint32s can be found: the length of the table, and the length of the startup code.
    • For the pairs, they represent the offset in the file and the length of code module, in bytes.
  • Each of the modules, finished by a null byte (\0).
` 0                   1                   2                   3                   4                   5                   6
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Magic number | Header size |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Startup code size | Module 0 offset |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Module 0 length | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +
| |
+ ... +
| |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| | Module n offset |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Module n length | Module 0 code | Module 0 code | ... | \0 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Module 1 code | Module 1 code | ... | \0 | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +
| |
+ ... +
| |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| | Module n code | Module n code | ... | \0 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+`

This structure is optimal for an environment that is able to load all code in memory at once:

  • By using the offset table, one can load any module in constant time, where the code for module x is located at file[(x + 3) * sizeof(uint32)]. Since there is a null character (\0) separating all modules, usually length does not even need to be used, and the module can be loaded directly as an ASCIIZ string.
  • Startup code is always found at file[sizeof(uint32)].

This bundling is usually used by iOS.

File RAM bundle​

Each module is stored as a file, with the name js-modules/${id}.js, plus an extra file called UNBUNDLE is created, which its only content is the magic number, 0xFB0BD1E5. Note that the UNBUNDLE file is created at the root. -This bundling is usually used by Android, since package contents are zipped, and access to a zipped file is much faster. If the indexed format was used instead, all the bundled should be unzipped at once to get the code for the corresponding module.

- +This bundling is usually used by Android, since package contents are zipped, and access to a zipped file is much faster. If the indexed format was used instead, all the bundled should be unzipped at once to get the code for the corresponding module.

+ \ No newline at end of file diff --git a/docs/caching/index.html b/docs/caching/index.html index d0fad561a3..1984290f4e 100644 --- a/docs/caching/index.html +++ b/docs/caching/index.html @@ -13,15 +13,15 @@ - +
-

Caching

Out of the box, Metro speeds up builds using a local cache of transformed modules. Thanks to this cache, Metro doesn't need to retransform modules unless the source code (or current configuration) has changed since the last time they were transformed.

Metro also has the ability to use a remote cache. This can dramatically speed up builds for larger teams and/or larger codebases by reducing the amount of time spent locally building remote changes even further. For example, this is how we use Metro to build React Native apps at Meta (a codebase with many thousands of files and hundreds of daily active engineers).

A typical setup for a remote cache involves:

  1. A storage backend specific to your team (e.g. S3 bucket).
  2. Running metro build periodically (e.g. in a CI job) to populate the cache, using HttpStore (or a custom read/write cache store) in your Metro config.
  3. Configuring Metro on your development machines to read from the cache, using HttpGetStore (or a custom read-only cache store) in your Metro config.

The main option for configuring the Metro cache is cacheStores. Typically, the local cache (e.g. FileStore) should be listed first, followed by the remote cache (e.g. HttpCache).

Built-in cache stores​

Metro provides a number of built-in cache store implementations for use with the cacheStores config option:

  • FileStore({root: string}) will store cache entries as files under the directory specified by root.
  • AutoCleanFileStore() is a FileStore that periodically cleans up old entries. It accepts the same options as FileStore plus the following:
    • options.intervalMs: number is the time in milliseconds between cleanup attempts. Defaults to 10 minutes.
    • options.cleanupThresholdMs: number is the minimum time in milliseconds since the last modification of an entry before it can be deleted. Defaults to 3 days.
  • HttpStore(options) is a bare-bones remote cache client that reads (GET) and writes (PUT) compressed cache artifacts over HTTP or HTTPS.
    • options.endpoint: string is the base URL for the cache server. For example, an HttpStore with 'http://www.example.com/endpoint' as the endpoint would issue requests to URLs such as http://www.example.com/endpoint/c083bff944879d9f528cf185eba0f496bc10a47d.
    • options.timeout: number is the timeout for requests to the cache server, in milliseconds. Defaults to 5000.
    • options.family: 4 | 6 is the same as the family parameter to Node's http.request.
    • options.cert, options.ca, options.key: HTTPS options passed directly to Node's built-in HTTPS client.
  • HttpGetStore(options) is a read-only version of HttpStore.

You can import these classes from the metro-cache package or get them through the function form of cacheStores:

// metro.config.js
const os = require('node:os');
const path = require('node:path');

module.exports = {
cacheStores: ({ FileStore }) => [
new FileStore({
root: path.join(os.tmpdir(), 'metro-cache'),
}),
],
};

Custom cache stores​

To implement a custom cache store, pass an instance of a class with the following interface into cacheStores:

interface CacheStore<T: Buffer | JsonSerializable> {
// Read an entry from the cache. Returns `null` if not found.
get(key: Buffer): ?T | Promise<?T>;

// Write an entry to the cache (if writable) or do nothing (if read-only)
set(key: Buffer, value: T): void | Promise<void>;

// Clear the cache (if possible) or do nothing
clear(): void | Promise<void>;
}

type JsonSerializable = /* Any JSON-serializable value */;

The value of a cache entry is either an instance of Buffer or a JSON-serializable value (with unspecified internal structure in both cases). For a given cache key, get() must return the same type of value that was originally provided to set().

- +

Caching

Out of the box, Metro speeds up builds using a local cache of transformed modules. Thanks to this cache, Metro doesn't need to retransform modules unless the source code (or current configuration) has changed since the last time they were transformed.

Metro also has the ability to use a remote cache. This can dramatically speed up builds for larger teams and/or larger codebases by reducing the amount of time spent locally building remote changes even further. For example, this is how we use Metro to build React Native apps at Meta (a codebase with many thousands of files and hundreds of daily active engineers).

A typical setup for a remote cache involves:

  1. A storage backend specific to your team (e.g. S3 bucket).
  2. Running metro build periodically (e.g. in a CI job) to populate the cache, using HttpStore (or a custom read/write cache store) in your Metro config.
  3. Configuring Metro on your development machines to read from the cache, using HttpGetStore (or a custom read-only cache store) in your Metro config.

The main option for configuring the Metro cache is cacheStores. Typically, the local cache (e.g. FileStore) should be listed first, followed by the remote cache (e.g. HttpCache).

Built-in cache stores​

Metro provides a number of built-in cache store implementations for use with the cacheStores config option:

  • FileStore({root: string}) will store cache entries as files under the directory specified by root.
  • AutoCleanFileStore() is a FileStore that periodically cleans up old entries. It accepts the same options as FileStore plus the following:
    • options.intervalMs: number is the time in milliseconds between cleanup attempts. Defaults to 10 minutes.
    • options.cleanupThresholdMs: number is the minimum time in milliseconds since the last modification of an entry before it can be deleted. Defaults to 3 days.
  • HttpStore(options) is a bare-bones remote cache client that reads (GET) and writes (PUT) compressed cache artifacts over HTTP or HTTPS.
    • options.endpoint: string is the base URL for the cache server. For example, an HttpStore with 'http://www.example.com/endpoint' as the endpoint would issue requests to URLs such as http://www.example.com/endpoint/c083bff944879d9f528cf185eba0f496bc10a47d.
    • options.timeout: number is the timeout for requests to the cache server, in milliseconds. Defaults to 5000.
    • options.family: 4 | 6 is the same as the family parameter to Node's http.request.
    • options.cert, options.ca, options.key: HTTPS options passed directly to Node's built-in HTTPS client.
  • HttpGetStore(options) is a read-only version of HttpStore.

You can import these classes from the metro-cache package or get them through the function form of cacheStores:

// metro.config.js
const os = require('node:os');
const path = require('node:path');

module.exports = {
cacheStores: ({ FileStore }) => [
new FileStore({
root: path.join(os.tmpdir(), 'metro-cache'),
}),
],
};

Custom cache stores​

To implement a custom cache store, pass an instance of a class with the following interface into cacheStores:

interface CacheStore<T: Buffer | JsonSerializable> {
// Read an entry from the cache. Returns `null` if not found.
get(key: Buffer): ?T | Promise<?T>;

// Write an entry to the cache (if writable) or do nothing (if read-only)
set(key: Buffer, value: T): void | Promise<void>;

// Clear the cache (if possible) or do nothing
clear(): void | Promise<void>;
}

type JsonSerializable = /* Any JSON-serializable value */;

The value of a cache entry is either an instance of Buffer or a JSON-serializable value (with unspecified internal structure in both cases). For a given cache key, get() must return the same type of value that was originally provided to set().

+ \ No newline at end of file diff --git a/docs/cli/index.html b/docs/cli/index.html index 75c0f92d06..8a64f2d6a4 100644 --- a/docs/cli/index.html +++ b/docs/cli/index.html @@ -13,7 +13,7 @@ - + @@ -21,8 +21,8 @@

Metro CLI Options

The metro command line runner has a number of useful options. You can run metro ---help to view all available options. Here is a brief overview:

build <entry>​

Generates a JavaScript bundle containing the specified entrypoint and its descendants.

Options​

OptionAliasDescriptionValue
outOFile name where to store the outputString
platformpWhich platform to bundle forweb, android, ios
minifyzWhether Metro should minify the bundleBoolean
devgCreate a development version of the build (process.env.NODE_ENV = 'development')Boolean
configcLocation of the metro.config.js to useString
max-workersjThe number of workers Metro should parallelize the transformer onNumber
project-rootsPThe root folder of your projectArray
source-mapWhether Metro should generate source mapsBoolean
source-map-urlURL where the source map can be foundString
legacy-bundlerWhether Metro should use the legacy bundlerBoolean
resolver-optionCustom resolver options of the form key=valueArray
transform-optionCustom transform options of the form key=valueArray

serve​

Starts Metro on the given port, building bundles on the fly.

get-dependencies <entryFile>​

List all dependencies that will be bundled for a given entry point.

Options​

OptionDescription
entry-fileAbsolute path to the root JS file. This can also be given as the first positional arg.
outputFile name where to store the output, ex. /tmp/dependencies.txt
platformThe platform extension used for selecting modules
transformerSpecify a custom transformer to be used
max-workersSpecifies the maximum number of workers the worker-pool will spawn for transforming files. This defaults to the number of the cores available on your machine.
devIf false, skip all dev-only code path
verboseEnables logging
- +--help to view all available options. Here is a brief overview:

build <entry>​

Generates a JavaScript bundle containing the specified entrypoint and its descendants.

Options​

OptionAliasDescriptionValue
outOFile name where to store the outputString
platformpWhich platform to bundle forweb, android, ios
minifyzWhether Metro should minify the bundleBoolean
devgCreate a development version of the build (process.env.NODE_ENV = 'development')Boolean
configcLocation of the metro.config.js to useString
max-workersjThe number of workers Metro should parallelize the transformer onNumber
project-rootsPThe root folder of your projectArray
source-mapWhether Metro should generate source mapsBoolean
source-map-urlURL where the source map can be foundString
legacy-bundlerWhether Metro should use the legacy bundlerBoolean
resolver-optionCustom resolver options of the form key=valueArray
transform-optionCustom transform options of the form key=valueArray

serve​

Starts Metro on the given port, building bundles on the fly.

get-dependencies <entryFile>​

List all dependencies that will be bundled for a given entry point.

Options​

OptionDescription
entry-fileAbsolute path to the root JS file. This can also be given as the first positional arg.
outputFile name where to store the output, ex. /tmp/dependencies.txt
platformThe platform extension used for selecting modules
transformerSpecify a custom transformer to be used
max-workersSpecifies the maximum number of workers the worker-pool will spawn for transforming files. This defaults to the number of the cores available on your machine.
devIf false, skip all dev-only code path
verboseEnables logging
+ \ No newline at end of file diff --git a/docs/concepts/index.html b/docs/concepts/index.html index 3b68aa7518..4a6cc03c23 100644 --- a/docs/concepts/index.html +++ b/docs/concepts/index.html @@ -13,15 +13,15 @@ - +
-

Concepts

Metro is a JavaScript bundler. It takes in an entry file and various options, and gives you back a single JavaScript file that includes all your code and its dependencies.

Metro has three separate stages in its bundling process:

  1. Resolution
  2. Transformation
  3. Serialization

Resolution​

Metro needs to build a graph of all the modules that are required from the entry point. To find which file is required from another file Metro uses a resolver. In reality this stage happens in parallel with the transformation stage.

Transformation​

All modules go through a transformer. A transformer is responsible for converting (transpiling) a module to a format that is understandable by the target platform (eg. React Native). Transformation of modules happens in parallel based on the amount of cores that you have.

Serialization​

As soon as all the modules have been transformed they will be serialized. A serializer combines the modules to generate one or multiple bundles. A bundle is literally a bundle of modules combined into a single JavaScript file.

Modules​

Metro has been split out into multiple modules corresponding to every step in the flow, each with their own responsibility. This means that we have a resolver, transformer, and serializer. These modules can be swapped out depending on your needs.

- +

Concepts

Metro is a JavaScript bundler. It takes in an entry file and various options, and gives you back a single JavaScript file that includes all your code and its dependencies.

Metro has three separate stages in its bundling process:

  1. Resolution
  2. Transformation
  3. Serialization

Resolution​

Metro needs to build a graph of all the modules that are required from the entry point. To find which file is required from another file Metro uses a resolver. In reality this stage happens in parallel with the transformation stage.

Transformation​

All modules go through a transformer. A transformer is responsible for converting (transpiling) a module to a format that is understandable by the target platform (eg. React Native). Transformation of modules happens in parallel based on the amount of cores that you have.

Serialization​

As soon as all the modules have been transformed they will be serialized. A serializer combines the modules to generate one or multiple bundles. A bundle is literally a bundle of modules combined into a single JavaScript file.

Modules​

Metro has been split out into multiple modules corresponding to every step in the flow, each with their own responsibility. This means that we have a resolver, transformer, and serializer. These modules can be swapped out depending on your needs.

+ \ No newline at end of file diff --git a/docs/configuration/index.html b/docs/configuration/index.html index 594ea1240a..58d6c6955d 100644 --- a/docs/configuration/index.html +++ b/docs/configuration/index.html @@ -13,7 +13,7 @@ - + @@ -21,9 +21,9 @@

Configuring Metro

A Metro config can be created in these three ways (ordered by priority):

  1. metro.config.js
  2. metro.config.json
  3. The metro field in package.json

You can also give a custom file to the configuration by specifying --config <path/to/config> when calling the CLI.

note

When Metro is started via the React Native CLI, some defaults are different from those mentioned below. -See the React Native repository for details.

Configuration Structure​

The configuration is based on our concepts, which means that for every module we have a separate config option. A common configuration structure in Metro looks like this:

module.exports = {
/* general options */

resolver: {
/* resolver options */
},
transformer: {
/* transformer options */
},
serializer: {
/* serializer options */
},
server: {
/* server options */
},
watcher: {
/* watcher options */
watchman: {
/* Watchman-specific options */
}
}
};

General Options​

cacheStores​

Type: CacheStores (see details below)

A list of storage adapters for Metro's transformer cache. This can be any combination of built-in cache stores and custom cache stores. Defaults to using a temporary directory on disk as the only cache store.

When Metro needs to transform a module, it first computes a machine-independent cache key for that file, and uses it to try to read from each of the stores in order. Once Metro has obtained the output of the transformer (whether already cached or not), it writes the transform result to all of the stores that returned null (a cache miss) for that key.

type CacheStores =
| Array<CacheStore<Buffer | JsonSerializable>>
| ((MetroCache) => Array<
CacheStore<Buffer | JsonSerializable>
>);

// The exports of 'metro-cache'
type MetroCache = {
FileStore,
AutoCleanFileStore,
HttpStore,
HttpGetStore,
...
};

type JsonSerializable = /* Any JSON-serializable value */;

cacheVersion​

Type: string

An arbitrary string appended to all cache keys in the project before they are hashed. There is generally no need to set this explicitly, as Metro will automatically derive the correct cache keys from your project config and the contents of source files.

projectRoot​

Type: string

The root folder of your project. If your project depends on any files outside this root, their containing directories must be listed in watchFolders.

note

If your Metro project is developed in a monorepo and includes files from multiple logical packages, you'll generally want to set projectRoot to the root of your repository, or at least high enough in the hierarchy that all relevant files are reachable without separately configuring watchFolders.

watchFolders​

Type: Array<string>

A list of directories outside of projectRoot that can contain source files for the project.

note

Despite the naming of this option, it isn't related solely to file watching. Even in an offline build (for example, in CI), all files must be visible to Metro through the combination of watchFolders and projectRoot.

transformerPath​

Type: string

The absolute path of a module (or a package name resolvable from the metro package) that implements a transformer.

See the implementation of Metro's default transformer (metro-transform-worker) for more information about the transformer interface.

reporter​

Type: {update: (event: ReportableEvent) => void}

Used to report the status of the bundler during the bundling process. The default implementation prints most events to the terminal.

See also the definition of ReportableEvent in Metro's source code.

resetCache​

Type: boolean

If true, Metro will reset the transformer cache (see cacheStores) and the file map cache (see fileMapCacheDirectory) on startup.

stickyWorkers​

Type: boolean

If true, Metro will use a stable mapping from files to transformer workers, so the same file is always transformed by the same worker. This can improve initial build performance if the transformer is expensive to initialize, but can slow down concurrent builds with different configurations (e.g. multiple React Native apps connected to one Metro server). Defaults to true.

maxWorkers​

Type: number

The number of workers to use for parallel processing in Metro. Defaults to approximately half of the number of cores available on the machine, as reported by os.cpus().

note
  1. Values exceeding the number of available cores have no effect.
  2. If maxWorkers is set to 1 or lower, worker code will run in the main Metro process instead of concurrently.
  3. Metro has two separate worker pools - one for transformation and one for building the file map. Each pool has its worker count set to maxWorkers independently.

fileMapCacheDirectory​

Type: string

The path to the metro-file-map cache directory, defaults to os.tmpdir().

hasteMapCacheDirectory
Deprecated
​

Type: string

Alias of fileMapCacheDirectory

unstable_perfLoggerFactory​

Type: PerfLoggerFactory

A logger factory function that can be used to get insights about Metro performance timings and metadata for events including startup, bundling and HMR. Metro expects unstable_perfLoggerFactory to have the following signature:

function unstable_perfLoggerFactory(
type: string,
opts: $ReadOnly<{
key?: string
}>,
): RootPerfLogger {
// ...
};
  • type Type of event being logged, e.g. 'STARTUP', 'BUNDLING_REQUEST', 'HMR'. See type definition of PerfLoggerFactory for a full list of event types.
  • opts
    • key: An opaque identifier to distinguish between instances of an event type (e.g. multiple, possibly concurrent, HMR requests).

unstable_perfLoggerFactory should return an object implementing the RootPerfLogger interface. For example, a factory function returning a no-op RootPerfLogger could be implemented as follows:

const unstable_perfLoggerFactory = (type, factoryOpts) => {
const getLogger = subSpanLabel => {
const logger = {
start(opts) {},
end(status, opts) {},
subSpan(label) {
return getLogger(`${subSpanLabel ?? ''}/${label}`);
},
point(name, opts) {},
annotate(annotations) {},
};
return logger;
};

return getLogger();
};

Resolver Options​

assetExts​

Type: Array<string>

The list of asset file extensions to include in the bundle. For example, including 'ttf' allows Metro bundles to reference .ttf files. This is used primarily to enable React Native's image asset support. The default list includes many common image, video and audio file extensions. See Metro's source code for the full list.

sourceExts​

Type: Array<string>

The list of source file extensions to include in the bundle. For example, including 'ts' allows Metro to include .ts files in the bundle.

The order of these extensions defines the order to match files on disk. For more information, see Module Resolution.

Defaults to ['js', 'jsx', 'json', 'ts', 'tsx'].

resolverMainFields​

Type: Array<string>

The list of fields in package.json that Metro will treat as describing a package's entry points. The default is ['browser', 'main'], so the resolver will use the browser field if it exists and main otherwise.

Metro's default resolver processes each of these fields according to the browser field spec, including the ability to replace and ignore specific files. For more information, see Module Resolution.

note

When using React Native, resolverMainFields defaults to ['react-native', 'browser', 'main'].

disableHierarchicalLookup​

Type: boolean

Whether to disable looking up modules in node_modules folders. This only affects the default search through the directory tree, not other Metro options like extraNodeModules or nodeModulesPaths. Defaults to false.

emptyModulePath​

Type: string

What module to use as the canonical "empty" module when one is needed. Defaults to using the one included in metro-runtime. You only need to change this if Metro is installed outside of your project.

enableGlobalPackages​

Type: boolean.

Whether to automatically resolve references to first-party packages (e.g. workspaces) in your project. Any package.json file with a valid name property within projectRoot or watchFolders (but outside of node_modules) counts as a package for this purpose. Defaults to false.

extraNodeModules​

Type: {[string]: string}

A mapping of package names to directories that is consulted after the standard lookup through node_modules as well as any nodeModulesPaths. For more information, see Module Resolution.

nodeModulesPaths​

Type: Array<string>

A list of paths to check for modules after looking through all node_modules directories. This is useful if third-party dependencies are installed in a different location outside of the direct path of source files. For more information, see Module Resolution.

resolveRequest​

Type: ?CustomResolver

An optional function used to override the default resolution algorithm. This is particularly useful for cases where aliases or custom protocols are used. For example:

resolveRequest: (context, moduleName, platform) => {
if (moduleName.startsWith('my-custom-resolver:')) {
// Logic to resolve the module name to a file path...
// NOTE: Throw an error if there is no resolution.
return {
filePath: 'path/to/file',
type: 'sourceFile',
};
}
// Optionally, chain to the standard Metro resolver.
return context.resolveRequest(context, moduleName, platform);
}

For more information on customizing the resolver, see Module Resolution.

useWatchman​

Type: boolean

If set to false, prevents Metro from using Watchman (even if it's installed).

blockList​

Type: RegExp or Array<RegExp>

A regular expression (or list of regular expressions) defining which paths to exclude from Metro's file map. Files whose absolute paths match these patterns are effectively hidden from Metro and cannot be resolved or imported in the current project.

hasteImplModulePath​

Type: ?string

The path to the Haste implementation for the current project. Haste is an opt-in mechanism for importing modules by their globally-unique name anywhere in the project, e.g. import Foo from 'Foo'.

Metro expects this module to have the following signature:

module.exports = {
getHasteName(filePath: string): ?string {
// ...
},
};

getHasteName should return a short, globally unique name for the module whose path is filePath, or null if the module should not be accessible via Haste.

platforms​

Type: Array<string>

Additional platforms to resolve. Defaults to ['ios', 'android', 'windows', 'web'].

For more information, see Module Resolution and React Native's documentation for platform-specific extensions.

requireCycleIgnorePatterns​

Type: Array<RegExp>

In development mode, suppress require cycle warnings for any cycle involving a module that matches any of these expressions. This is useful for third-party code and first-party expected cycles.

Note that if you specify your own value for this config option it will replace (not concatenate with) Metro's default.

Defaults to [/(^|\/|\\)node_modules($|\/|\\)/].

unstable_conditionNames
Experimental
​

Type: Array<string>

note

This setting will take effect when unstable_enablePackageExports is true. It may not behave as described while this feature is experimental.

The set of condition names to assert globally when interpreting the "exports" field in package.json.

Conditions may be any string value and are resolved in the order specified by each package. Node.js documents a number of community conditions which are commonly used by package authors. The default condition is always matched.

Defaults to ['require'].

note

When using React Native, unstable_conditionNames defaults to ['require', 'react-native'].

unstable_conditionsByPlatform
Experimental
​

Type: {[platform: string]: Array<string>}

note

This setting will take effect when unstable_enablePackageExports is true. It may not behave as described while this feature is experimental.

The set of additional condition names to dynamically assert by platform (see platforms) when interpreting the "exports" field in package.json.

Matched conditions are merged with unstable_conditionNames before resolution. With the defaults for both options, the conditions new Set(['require', 'browser']) will be asserted when requesting a web bundle, and new Set(['require']) otherwise. Again, these are resolved in the order specified by each package.

Defaults to β€Œ{ web: ['browser'] }.

unstable_enablePackageExports
Experimental
​

Type: boolean

Enable experimental Package Exports support. Under this mode, Metro will read the "exports" field in package.json files when present and use it to resolve package entry points.

When no match is found in "exports", Metro will log a warning and fall back to resolving modules without considering "exports". This makes this mode largely backwards-compatible, with the following exceptions:

  • If a module is matched in "exports", sourceExts and platforms will not be considered (i.e. platform-specific extensions will not be used). This is done for compatibility with Node.
  • If a module exists at a file path that is also listed in "exports", and the "exports" entry maps to a different file, the "exports" entry will be preferred.

Defaults to false.

note

In a future release of Metro, this option will become true by default.


Type: boolean

Enable experimental support for projects containing symbolic links (symlinks).

When enabled, Metro traverses symlinks during module and asset resolution, instead of ignoring symlinks. Note that, as with any other file Metro needs to resolve, the symlink target must be within configured watched folders and not otherwise excluded.

Defaults to true since Metro v0.79.0.

info

For example, if you have a Metro project within a Yarn workspace (a subdirectory of a Yarn workspace root), it's likely you'll want to include your workspace root path in your configured watchFolders so that Metro can resolve other workspaces or hoisted node_modules. Similarly, to use linked packages, you'll need to list those package source locations (or a containing directory) in watchFolders.

note

In a future release of Metro, this option will be removed (symlink support will be always-on).


Transformer Options​

asyncRequireModulePath
Deprecated
​

Type: string

The name of a module that provides the asyncRequire function, which is used to implement dynamic import() at runtime. Defaults to metro-runtime/src/modules/asyncRequire.

note

The module named by asyncRequireModulePath is resolved relative to the module containing the original import() call. In particular, assuming the default value of asyncRequireModulePath is in use, the project must have a compatible version of metro-runtime installed in node_modules.

info

In older versions of Metro, a custom asyncRequireModulePath could be used as part of a bundle splitting solution. This usage is now deprecated in favor of the __loadBundleAsync API.

dynamicDepsInPackages​

Type: 'throwAtRuntime' | 'reject'

Controls how Metro handles dependencies that cannot be statically analyzed at build time. For example, require('./' + someFunction() + '.js') cannot be resolved without knowing what someFunction() will return.

  • 'throwAtRuntime' (the default): Metro does not stop bundling, but the require call will throw at runtime.
  • 'reject': Metro will stop bundling and report an error to the user.

getTransformOptions​

Type: Function (see details below)

A function called by Metro to calculate additional options for the transformer and serializer based on the specific bundle being built.

Metro expects getTransformOptions to have the following signature:

function getTransformOptions(
entryPoints: $ReadOnlyArray<string>,
options: {
dev: boolean,
hot: boolean,
platform: ?string,
},
getDependenciesOf: (path: string) => Promise<Array<string>>,
): Promise<ExtraTransformOptions> {
// ...
}

getTransformOptions receives these parameters:

  • entryPoints: Absolute paths to the bundle's entry points (typically just one).
  • options:
    • dev: Whether the bundle is being built in development mode.
    • hot:
      Deprecated
      Always true.
    • platform: The target platform (e.g. ios, android).
  • getDependenciesOf: A function which, given an absolute path to a module, returns a promise that resolves to the absolute paths of the module's transitive dependencies.

getTransformOptions should return a promise that resolves to an object with the following properties:

type ExtraTransformOptions = {
preloadedModules?: {[path: string]: true} | false,
ramGroups?: Array<string>,
transform?: {
inlineRequires?: {blockList: {[string]: true}} | boolean,
nonInlinedRequires?: $ReadOnlyArray<string>,
},
};
  • preloadedModules: A plain object whose keys represent a set of absolute paths. When serializing an indexed RAM bundle, the modules in this set will be marked for eager evaluation at runtime.
  • ramGroups: An array of absolute paths. When serializing an indexed RAM bundle, each of the listed modules will be serialized along with its transitive dependencies. At runtime, the modules will all be parsed together as soon as any one of them is evaluated.
  • transform: Advanced options for the transformer.
    • inlineRequires:
      • If inlineRequires is a boolean, it controls whether inline requires are enabled in this bundle.
      • If inlineRequires is an object, inline requires are enabled in all modules, except ones whose absolute paths appear as keys of inlineRequires.blockList.
    • nonInlinedRequires: An array of unresolved module specifiers (e.g. react, react-native) to never inline, even when inline requires are enabled.

minifierPath​

Type: string (default: 'metro-minify-terser')

Path, or package name resolvable from metro-transform-worker, to the minifier that minifies the code after transformation.

minifierConfig​

Type: {[key: string]: mixed}

Configuration object that will be passed to the minifier (it should be serializable).

optimizationSizeLimit​

Type: number

Define a threshold (in bytes) to disable some expensive optimizations for big files.

React Native Only​

assetPlugins​

Type: Array<string>

List of modules to call to modify Asset data

assetRegistryPath​

Type: string

Where to fetch the assets from.

Babel-specific transformer options​

babelTransformerPath​

Type: string

The name of a module that compiles code with Babel, returning an AST and optional metadata. Defaults to metro-babel-transformer.

Refer to the source code of metro-babel-transformer and @react-native/metro-babel-transformer for details on implementing a custom Babel transformer.

note

This option only has an effect under the default transformerPath. Custom transformers may ignore it.

enableBabelRCLookup​

Type: boolean

Whether to enable searching for Babel configuration files. This is passed to Babel as the babelrc config option. Defaults to true.

note

This option only has an effect under the default transformerPath. Custom transformers may ignore it. Custom Babel transformers should respect this option.

enableBabelRuntime​

Type: boolean | string

Whether the transformer should use the @babel/transform/runtime plugin. Defaults to true.

If the value is a string, it is treated as a runtime version number and passed as version to the @babel/plugin-transform-runtime configuration. This allows you to optimize the generated Babel runtime calls based on the version installed in your project.

note

This option only works under the default settings for React Native. It may have no effect in a project that uses custom transformerPath, a custom babelTransformerPath or a custom Babel config file.

hermesParser​

Type: boolean

Whether to use the hermes-parser package to parse JavaScript source files, instead of Babel. Defaults to false.

note

This option only has an effect under the default transformerPath and the Babel transformers built into Metro. Custom transformers and custom Babel transformers may ignore it.


Serializer Options​

getRunModuleStatement​

Type: (number | string) => string

Specify the format of the initial require statements that are appended at the end of the bundle. By default is __r(${moduleId});.

createModuleIdFactory​

Type: () => (path: string) => number

Used to generate the module id for require statements.

getPolyfills​

Type: ({platform: ?string}) => $ReadOnlyArray<string>

An optional list of polyfills to include in the bundle. The list defaults to a set of common polyfills for Number, String, Array, Object...

getModulesRunBeforeMainModule​

Type: (entryFilePath: string) => Array<string>

An array of modules to be required before the entry point. It should contain the absolute path of each module. Note that this will add the additional require statements only if the passed modules are already included as part of the bundle.

processModuleFilter​

Type: (module: Array<Module>) => boolean

A filter function to discard specific modules from the output.

isThirdPartyModule​

Type: (module: {path: string, ...}) => boolean

A function that determines which modules are added to the x_google_ignoreList field of the source map. This supports "Just My Code" debugging in Chrome DevTools and other compatible debuggers.

Defaults to returning true for modules with a path component named node_modules.

note

In addition to modules marked as ignored by isThirdPartyModule, Metro will also automatically add modules generated by the bundler itself to the ignore list.


Server Options​

These options are used when Metro serves the content.

port​

Type: number

Which port to listen on.

useGlobalHotkey​

Type: boolean

Whether we should enable CMD+R hotkey for refreshing the bundle.

enhanceMiddleware
Deprecated
​

Type: (Middleware, MetroServer) => Middleware

A function that allows attaching custom connect middleware to Metro. For example:

tip

You can use connect() as a utility to extend the base metroMiddleware and to mount additional middleware handlers.

enhanceMiddleware: (metroMiddleware: Middleware, metroServer: MetroServer) => {
return connect()
.use(metroMiddleware)
.use('/custom-endpoint', customEndpointMiddleware());
},

The Middleware type is an alias for connect.HandleFunction.

rewriteRequestUrl​

Type: string => string

A function that will be called every time Metro processes a URL, after normalization of non-standard query-string delimiters using jsc-safe-url. Metro will use the return value of this function as if it were the original URL provided by the client. This applies to all incoming HTTP requests (after any custom middleware), as well as bundle URLs in /symbolicate request payloads and within the hot reloading protocol.

forwardClientLogs​

Type: boolean

Enable forwarding of client_log events (when client logs are configured) to the reporter. Defaults to true.


Watcher Options​

Options for the filesystem watcher.

note

Dot notation in this section indicates a nested configuration object, e.g. watchman.deferStates β†’ watchman: { deferStates: ... }.

additionalExts​

Type: Array<string>

The extensions which Metro should watch in addition to sourceExts, but which will not be automatically tried by the resolver.

Therefore, the two behavior differences from resolver.sourceExts when importing a module are:

  • Modules can only be required when fully specified (e.g. import moduleA from 'moduleA.mjs').
  • No platform-specific resolution is performed.

Defaults to ['cjs', 'mjs'].

healthCheck.enabled​

Type: boolean

Whether to periodically check the health of the filesystem watcher by writing a temporary file to the project and waiting for it to be observed.

The default value is false.

healthCheck.filePrefix​

Type: string

If watcher health checks are enabled, this property controls the name of the temporary file that will be written into the project filesystem.

The default value is '.metro-health-check'.

note

There's no need to commit health check files to source control. If you choose to enable health checks in your project, make sure you add .metro-health-check* to your .gitignore file to avoid generating unnecessary changes.

healthCheck.interval​

Type: number

If watcher health checks are enabled, this property controls how often they occur (in milliseconds).

The default value is 30000.

healthCheck.timeout​

Type: number

If watcher health checks are enabled, this property controls the time (in milliseconds) Metro will wait for a file change to be observed before considering the check to have failed.

The default value is 5000.

watchman.deferStates​

Type: Array<string>

Applies when using Watchman. Metro will defer processing filesystem updates while these states are asserted in the watch. This is useful for debouncing builds while the filesystem hasn't settled, e.g. during large source control operations.

The default value is ['hg.update'].

Merging Configurations​

Using the metro-config package it is possible to merge multiple configurations together.

MethodDescription
mergeConfig(...configs): MergedConfigReturns the merged configuration of two or more configuration objects.
note

Arrays and function based config parameters do not deeply merge and will instead override any pre-existing config parameters. -This allows overriding and removing default config parameters such as platforms or getModulesRunBeforeMainModule that may not be required in your environment.

Merging Example​

// metro.config.js
const { mergeConfig } = require('metro-config');

const configA = {
/* general options */

resolver: {
/* resolver options */
},
transformer: {
/* transformer options */
},
serializer: {
/* serializer options */
},
server: {
/* server options */
}
};

const configB = {
/* general options */

resolver: {
/* resolver options */
},
transformer: {
/* transformer options */
},
serializer: {
/* serializer options */
},
server: {
/* server options */
}
};

module.exports = mergeConfig(configA, configB);
- +See the React Native repository for details.

Configuration Structure​

The configuration is based on our concepts, which means that for every module we have a separate config option. A common configuration structure in Metro looks like this:

module.exports = {
/* general options */

resolver: {
/* resolver options */
},
transformer: {
/* transformer options */
},
serializer: {
/* serializer options */
},
server: {
/* server options */
},
watcher: {
/* watcher options */
watchman: {
/* Watchman-specific options */
}
}
};

General Options​

cacheStores​

Type: CacheStores (see details below)

A list of storage adapters for Metro's transformer cache. This can be any combination of built-in cache stores and custom cache stores. Defaults to using a temporary directory on disk as the only cache store.

When Metro needs to transform a module, it first computes a machine-independent cache key for that file, and uses it to try to read from each of the stores in order. Once Metro has obtained the output of the transformer (whether already cached or not), it writes the transform result to all of the stores that returned null (a cache miss) for that key.

type CacheStores =
| Array<CacheStore<Buffer | JsonSerializable>>
| ((MetroCache) => Array<
CacheStore<Buffer | JsonSerializable>
>);

// The exports of 'metro-cache'
type MetroCache = {
FileStore,
AutoCleanFileStore,
HttpStore,
HttpGetStore,
...
};

type JsonSerializable = /* Any JSON-serializable value */;

cacheVersion​

Type: string

An arbitrary string appended to all cache keys in the project before they are hashed. There is generally no need to set this explicitly, as Metro will automatically derive the correct cache keys from your project config and the contents of source files.

projectRoot​

Type: string

The root folder of your project. If your project depends on any files outside this root, their containing directories must be listed in watchFolders.

note

If your Metro project is developed in a monorepo and includes files from multiple logical packages, you'll generally want to set projectRoot to the root of your repository, or at least high enough in the hierarchy that all relevant files are reachable without separately configuring watchFolders.

watchFolders​

Type: Array<string>

A list of directories outside of projectRoot that can contain source files for the project.

note

Despite the naming of this option, it isn't related solely to file watching. Even in an offline build (for example, in CI), all files must be visible to Metro through the combination of watchFolders and projectRoot.

info

Note that, as with any other file Metro needs to resolve, targets of any symlinks within your watchFolders must also be within watchFolders and not otherwise excluded.

If you have a Metro project within a workspace, such as a Yarn workspace (a subdirectory of a Yarn workspace root), it's likely you'll want to include your workspace root path in your configured watchFolders so that Metro can resolve other workspaces or hoisted node_modules. Similarly, to use linked packages, you'll need to list those package source locations (or a containing directory) in watchFolders.

transformerPath​

Type: string

The absolute path of a module (or a package name resolvable from the metro package) that implements a transformer.

See the implementation of Metro's default transformer (metro-transform-worker) for more information about the transformer interface.

reporter​

Type: {update: (event: ReportableEvent) => void}

Used to report the status of the bundler during the bundling process. The default implementation prints most events to the terminal.

See also the definition of ReportableEvent in Metro's source code.

resetCache​

Type: boolean

If true, Metro will reset the transformer cache (see cacheStores) and the file map cache (see fileMapCacheDirectory) on startup.

stickyWorkers​

Type: boolean

If true, Metro will use a stable mapping from files to transformer workers, so the same file is always transformed by the same worker. This can improve initial build performance if the transformer is expensive to initialize, but can slow down concurrent builds with different configurations (e.g. multiple React Native apps connected to one Metro server). Defaults to true.

maxWorkers​

Type: number

The number of workers to use for parallel processing in Metro. Defaults to approximately half of the number of cores available on the machine, as reported by os.cpus().

note
  1. Values exceeding the number of available cores have no effect.
  2. If maxWorkers is set to 1 or lower, worker code will run in the main Metro process instead of concurrently.
  3. Metro has two separate worker pools - one for transformation and one for building the file map. Each pool has its worker count set to maxWorkers independently.

fileMapCacheDirectory​

Type: string

The path to the metro-file-map cache directory, defaults to os.tmpdir().

hasteMapCacheDirectory
Deprecated
​

Type: string

Alias of fileMapCacheDirectory

unstable_perfLoggerFactory​

Type: PerfLoggerFactory

A logger factory function that can be used to get insights about Metro performance timings and metadata for events including startup, bundling and HMR. Metro expects unstable_perfLoggerFactory to have the following signature:

function unstable_perfLoggerFactory(
type: string,
opts: $ReadOnly<{
key?: string
}>,
): RootPerfLogger {
// ...
};
  • type Type of event being logged, e.g. 'STARTUP', 'BUNDLING_REQUEST', 'HMR'. See type definition of PerfLoggerFactory for a full list of event types.
  • opts
    • key: An opaque identifier to distinguish between instances of an event type (e.g. multiple, possibly concurrent, HMR requests).

unstable_perfLoggerFactory should return an object implementing the RootPerfLogger interface. For example, a factory function returning a no-op RootPerfLogger could be implemented as follows:

const unstable_perfLoggerFactory = (type, factoryOpts) => {
const getLogger = subSpanLabel => {
const logger = {
start(opts) {},
end(status, opts) {},
subSpan(label) {
return getLogger(`${subSpanLabel ?? ''}/${label}`);
},
point(name, opts) {},
annotate(annotations) {},
};
return logger;
};

return getLogger();
};

Resolver Options​

assetExts​

Type: Array<string>

The list of asset file extensions to include in the bundle. For example, including 'ttf' allows Metro bundles to reference .ttf files. This is used primarily to enable React Native's image asset support. The default list includes many common image, video and audio file extensions. See Metro's source code for the full list.

sourceExts​

Type: Array<string>

The list of source file extensions to include in the bundle. For example, including 'ts' allows Metro to include .ts files in the bundle.

The order of these extensions defines the order to match files on disk. For more information, see Module Resolution.

Defaults to ['js', 'jsx', 'json', 'ts', 'tsx'].

resolverMainFields​

Type: Array<string>

The list of fields in package.json that Metro will treat as describing a package's entry points. The default is ['browser', 'main'], so the resolver will use the browser field if it exists and main otherwise.

Metro's default resolver processes each of these fields according to the browser field spec, including the ability to replace and ignore specific files. For more information, see Module Resolution.

note

When using React Native, resolverMainFields defaults to ['react-native', 'browser', 'main'].

disableHierarchicalLookup​

Type: boolean

Whether to disable looking up modules in node_modules folders. This only affects the default search through the directory tree, not other Metro options like extraNodeModules or nodeModulesPaths. Defaults to false.

emptyModulePath​

Type: string

What module to use as the canonical "empty" module when one is needed. Defaults to using the one included in metro-runtime. You only need to change this if Metro is installed outside of your project.

enableGlobalPackages​

Type: boolean.

Whether to automatically resolve references to first-party packages (e.g. workspaces) in your project. Any package.json file with a valid name property within projectRoot or watchFolders (but outside of node_modules) counts as a package for this purpose. Defaults to false.

extraNodeModules​

Type: {[string]: string}

A mapping of package names to directories that is consulted after the standard lookup through node_modules as well as any nodeModulesPaths. For more information, see Module Resolution.

nodeModulesPaths​

Type: Array<string>

A list of paths to check for modules after looking through all node_modules directories. This is useful if third-party dependencies are installed in a different location outside of the direct path of source files. For more information, see Module Resolution.

resolveRequest​

Type: ?CustomResolver

An optional function used to override the default resolution algorithm. This is particularly useful for cases where aliases or custom protocols are used. For example:

resolveRequest: (context, moduleName, platform) => {
if (moduleName.startsWith('my-custom-resolver:')) {
// Logic to resolve the module name to a file path...
// NOTE: Throw an error if there is no resolution.
return {
filePath: 'path/to/file',
type: 'sourceFile',
};
}
// Optionally, chain to the standard Metro resolver.
return context.resolveRequest(context, moduleName, platform);
}

For more information on customizing the resolver, see Module Resolution.

useWatchman​

Type: boolean

If set to false, prevents Metro from using Watchman (even if it's installed).

blockList​

Type: RegExp or Array<RegExp>

A regular expression (or list of regular expressions) defining which paths to exclude from Metro's file map. Files whose absolute paths match these patterns are effectively hidden from Metro and cannot be resolved or imported in the current project.

hasteImplModulePath​

Type: ?string

The path to the Haste implementation for the current project. Haste is an opt-in mechanism for importing modules by their globally-unique name anywhere in the project, e.g. import Foo from 'Foo'.

Metro expects this module to have the following signature:

module.exports = {
getHasteName(filePath: string): ?string {
// ...
},
};

getHasteName should return a short, globally unique name for the module whose path is filePath, or null if the module should not be accessible via Haste.

platforms​

Type: Array<string>

Additional platforms to resolve. Defaults to ['ios', 'android', 'windows', 'web'].

For more information, see Module Resolution and React Native's documentation for platform-specific extensions.

requireCycleIgnorePatterns​

Type: Array<RegExp>

In development mode, suppress require cycle warnings for any cycle involving a module that matches any of these expressions. This is useful for third-party code and first-party expected cycles.

Note that if you specify your own value for this config option it will replace (not concatenate with) Metro's default.

Defaults to [/(^|\/|\\)node_modules($|\/|\\)/].

unstable_conditionNames
Experimental
​

Type: Array<string>

note

This setting will take effect when unstable_enablePackageExports is true. It may not behave as described while this feature is experimental.

The set of condition names to assert globally when interpreting the "exports" field in package.json.

Conditions may be any string value and are resolved in the order specified by each package. Node.js documents a number of community conditions which are commonly used by package authors. The default condition is always matched.

Defaults to ['require'].

note

When using React Native, unstable_conditionNames defaults to ['require', 'react-native'].

unstable_conditionsByPlatform
Experimental
​

Type: {[platform: string]: Array<string>}

note

This setting will take effect when unstable_enablePackageExports is true. It may not behave as described while this feature is experimental.

The set of additional condition names to dynamically assert by platform (see platforms) when interpreting the "exports" field in package.json.

Matched conditions are merged with unstable_conditionNames before resolution. With the defaults for both options, the conditions new Set(['require', 'browser']) will be asserted when requesting a web bundle, and new Set(['require']) otherwise. Again, these are resolved in the order specified by each package.

Defaults to β€Œ{ web: ['browser'] }.

unstable_enablePackageExports
Experimental
​

Type: boolean

Enable experimental Package Exports support. Under this mode, Metro will read the "exports" field in package.json files when present and use it to resolve package entry points.

When no match is found in "exports", Metro will log a warning and fall back to resolving modules without considering "exports". This makes this mode largely backwards-compatible, with the following exceptions:

  • If a module is matched in "exports", sourceExts and platforms will not be considered (i.e. platform-specific extensions will not be used). This is done for compatibility with Node.
  • If a module exists at a file path that is also listed in "exports", and the "exports" entry maps to a different file, the "exports" entry will be preferred.

Defaults to false.

note

In a future release of Metro, this option will become true by default.


Transformer Options​

asyncRequireModulePath
Deprecated
​

Type: string

The name of a module that provides the asyncRequire function, which is used to implement dynamic import() at runtime. Defaults to metro-runtime/src/modules/asyncRequire.

note

The module named by asyncRequireModulePath is resolved relative to the module containing the original import() call. In particular, assuming the default value of asyncRequireModulePath is in use, the project must have a compatible version of metro-runtime installed in node_modules.

info

In older versions of Metro, a custom asyncRequireModulePath could be used as part of a bundle splitting solution. This usage is now deprecated in favor of the __loadBundleAsync API.

dynamicDepsInPackages​

Type: 'throwAtRuntime' | 'reject'

Controls how Metro handles dependencies that cannot be statically analyzed at build time. For example, require('./' + someFunction() + '.js') cannot be resolved without knowing what someFunction() will return.

  • 'throwAtRuntime' (the default): Metro does not stop bundling, but the require call will throw at runtime.
  • 'reject': Metro will stop bundling and report an error to the user.

getTransformOptions​

Type: Function (see details below)

A function called by Metro to calculate additional options for the transformer and serializer based on the specific bundle being built.

Metro expects getTransformOptions to have the following signature:

function getTransformOptions(
entryPoints: $ReadOnlyArray<string>,
options: {
dev: boolean,
hot: boolean,
platform: ?string,
},
getDependenciesOf: (path: string) => Promise<Array<string>>,
): Promise<ExtraTransformOptions> {
// ...
}

getTransformOptions receives these parameters:

  • entryPoints: Absolute paths to the bundle's entry points (typically just one).
  • options:
    • dev: Whether the bundle is being built in development mode.
    • hot:
      Deprecated
      Always true.
    • platform: The target platform (e.g. ios, android).
  • getDependenciesOf: A function which, given an absolute path to a module, returns a promise that resolves to the absolute paths of the module's transitive dependencies.

getTransformOptions should return a promise that resolves to an object with the following properties:

type ExtraTransformOptions = {
preloadedModules?: {[path: string]: true} | false,
ramGroups?: Array<string>,
transform?: {
inlineRequires?: {blockList: {[string]: true}} | boolean,
nonInlinedRequires?: $ReadOnlyArray<string>,
},
};
  • preloadedModules: A plain object whose keys represent a set of absolute paths. When serializing an indexed RAM bundle, the modules in this set will be marked for eager evaluation at runtime.
  • ramGroups: An array of absolute paths. When serializing an indexed RAM bundle, each of the listed modules will be serialized along with its transitive dependencies. At runtime, the modules will all be parsed together as soon as any one of them is evaluated.
  • transform: Advanced options for the transformer.
    • inlineRequires:
      • If inlineRequires is a boolean, it controls whether inline requires are enabled in this bundle.
      • If inlineRequires is an object, inline requires are enabled in all modules, except ones whose absolute paths appear as keys of inlineRequires.blockList.
    • nonInlinedRequires: An array of unresolved module specifiers (e.g. react, react-native) to never inline, even when inline requires are enabled.

minifierPath​

Type: string (default: 'metro-minify-terser')

Path, or package name resolvable from metro-transform-worker, to the minifier that minifies the code after transformation.

minifierConfig​

Type: {[key: string]: mixed}

Configuration object that will be passed to the minifier (it should be serializable).

optimizationSizeLimit​

Type: number

Define a threshold (in bytes) to disable some expensive optimizations for big files.

React Native Only​

assetPlugins​

Type: Array<string>

List of modules to call to modify Asset data

assetRegistryPath​

Type: string

Where to fetch the assets from.

Babel-specific transformer options​

babelTransformerPath​

Type: string

The name of a module that compiles code with Babel, returning an AST and optional metadata. Defaults to metro-babel-transformer.

Refer to the source code of metro-babel-transformer and @react-native/metro-babel-transformer for details on implementing a custom Babel transformer.

note

This option only has an effect under the default transformerPath. Custom transformers may ignore it.

enableBabelRCLookup​

Type: boolean

Whether to enable searching for Babel configuration files. This is passed to Babel as the babelrc config option. Defaults to true.

note

This option only has an effect under the default transformerPath. Custom transformers may ignore it. Custom Babel transformers should respect this option.

enableBabelRuntime​

Type: boolean | string

Whether the transformer should use the @babel/transform/runtime plugin. Defaults to true.

If the value is a string, it is treated as a runtime version number and passed as version to the @babel/plugin-transform-runtime configuration. This allows you to optimize the generated Babel runtime calls based on the version installed in your project.

note

This option only works under the default settings for React Native. It may have no effect in a project that uses custom transformerPath, a custom babelTransformerPath or a custom Babel config file.

hermesParser​

Type: boolean

Whether to use the hermes-parser package to parse JavaScript source files, instead of Babel. Defaults to false.

note

This option only has an effect under the default transformerPath and the Babel transformers built into Metro. Custom transformers and custom Babel transformers may ignore it.


Serializer Options​

getRunModuleStatement​

Type: (number | string) => string

Specify the format of the initial require statements that are appended at the end of the bundle. By default is __r(${moduleId});.

createModuleIdFactory​

Type: () => (path: string) => number

Used to generate the module id for require statements.

getPolyfills​

Type: ({platform: ?string}) => $ReadOnlyArray<string>

An optional list of polyfills to include in the bundle. The list defaults to a set of common polyfills for Number, String, Array, Object...

getModulesRunBeforeMainModule​

Type: (entryFilePath: string) => Array<string>

An array of modules to be required before the entry point. It should contain the absolute path of each module. Note that this will add the additional require statements only if the passed modules are already included as part of the bundle.

processModuleFilter​

Type: (module: Array<Module>) => boolean

A filter function to discard specific modules from the output.

isThirdPartyModule​

Type: (module: {path: string, ...}) => boolean

A function that determines which modules are added to the x_google_ignoreList field of the source map. This supports "Just My Code" debugging in Chrome DevTools and other compatible debuggers.

Defaults to returning true for modules with a path component named node_modules.

note

In addition to modules marked as ignored by isThirdPartyModule, Metro will also automatically add modules generated by the bundler itself to the ignore list.


Server Options​

These options are used when Metro serves the content.

port​

Type: number

Which port to listen on.

useGlobalHotkey​

Type: boolean

Whether we should enable CMD+R hotkey for refreshing the bundle.

enhanceMiddleware
Deprecated
​

Type: (Middleware, MetroServer) => Middleware

A function that allows attaching custom connect middleware to Metro. For example:

tip

You can use connect() as a utility to extend the base metroMiddleware and to mount additional middleware handlers.

enhanceMiddleware: (metroMiddleware: Middleware, metroServer: MetroServer) => {
return connect()
.use(metroMiddleware)
.use('/custom-endpoint', customEndpointMiddleware());
},

The Middleware type is an alias for connect.HandleFunction.

rewriteRequestUrl​

Type: string => string

A function that will be called every time Metro processes a URL, after normalization of non-standard query-string delimiters using jsc-safe-url. Metro will use the return value of this function as if it were the original URL provided by the client. This applies to all incoming HTTP requests (after any custom middleware), as well as bundle URLs in /symbolicate request payloads and within the hot reloading protocol.

forwardClientLogs​

Type: boolean

Enable forwarding of client_log events (when client logs are configured) to the reporter. Defaults to true.


Watcher Options​

Options for the filesystem watcher.

note

Dot notation in this section indicates a nested configuration object, e.g. watchman.deferStates β†’ watchman: { deferStates: ... }.

additionalExts​

Type: Array<string>

The extensions which Metro should watch in addition to sourceExts, but which will not be automatically tried by the resolver.

Therefore, the two behavior differences from resolver.sourceExts when importing a module are:

  • Modules can only be required when fully specified (e.g. import moduleA from 'moduleA.mjs').
  • No platform-specific resolution is performed.

Defaults to ['cjs', 'mjs'].

healthCheck.enabled​

Type: boolean

Whether to periodically check the health of the filesystem watcher by writing a temporary file to the project and waiting for it to be observed.

The default value is false.

healthCheck.filePrefix​

Type: string

If watcher health checks are enabled, this property controls the name of the temporary file that will be written into the project filesystem.

The default value is '.metro-health-check'.

note

There's no need to commit health check files to source control. If you choose to enable health checks in your project, make sure you add .metro-health-check* to your .gitignore file to avoid generating unnecessary changes.

healthCheck.interval​

Type: number

If watcher health checks are enabled, this property controls how often they occur (in milliseconds).

The default value is 30000.

healthCheck.timeout​

Type: number

If watcher health checks are enabled, this property controls the time (in milliseconds) Metro will wait for a file change to be observed before considering the check to have failed.

The default value is 5000.

watchman.deferStates​

Type: Array<string>

Applies when using Watchman. Metro will defer processing filesystem updates while these states are asserted in the watch. This is useful for debouncing builds while the filesystem hasn't settled, e.g. during large source control operations.

The default value is ['hg.update'].

Merging Configurations​

Using the metro-config package it is possible to merge multiple configurations together.

MethodDescription
mergeConfig(...configs): MergedConfigReturns the merged configuration of two or more configuration objects.
note

Arrays and function based config parameters do not deeply merge and will instead override any pre-existing config parameters. +This allows overriding and removing default config parameters such as platforms or getModulesRunBeforeMainModule that may not be required in your environment.

Merging Example​

// metro.config.js
const { mergeConfig } = require('metro-config');

const configA = {
/* general options */

resolver: {
/* resolver options */
},
transformer: {
/* transformer options */
},
serializer: {
/* serializer options */
},
server: {
/* server options */
}
};

const configB = {
/* general options */

resolver: {
/* resolver options */
},
transformer: {
/* transformer options */
},
serializer: {
/* serializer options */
},
server: {
/* server options */
}
};

module.exports = mergeConfig(configA, configB);
+ \ No newline at end of file diff --git a/docs/getting-started/index.html b/docs/getting-started/index.html index 296c6a7bb3..e4971e5c7f 100644 --- a/docs/getting-started/index.html +++ b/docs/getting-started/index.html @@ -13,7 +13,7 @@ - + @@ -21,8 +21,8 @@

Getting Started

Install Metro using npm:

npm install --save-dev metro metro-core

Or via yarn:

yarn add --dev metro metro-core

Running metro​

You can run Metro by either running the CLI or by calling it programmatically.

Running Programmatically​

First, require the module by doing:

const Metro = require('metro');

Within the object returned, several main methods are given:

Method runMetro(config)​

Given the config, a metro-server will be returned. You can then hook this into a proper HTTP(S) server by using its processRequest method:

'use strict';

const http = require('http');
const Metro = require('metro');

// We first load the config from the file system
Metro.loadConfig().then(async (config) => {
const metroBundlerServer = await Metro.runMetro(config);

const httpServer = http.createServer(
metroBundlerServer.processRequest.bind(metroBundlerServer),
);

httpServer.listen(8081);
});

In order to be also compatible with Express apps, processRequest will also call its third parameter when the request could not be handled by Metro. This allows you to integrate the server with your existing server, or to extend a new one:

const httpServer = http.createServer((req, res) => {
metroBundlerServer.processRequest(req, res, () => {
// Metro does not know how to handle the request.
});
});

If you are using Express, you can just pass processRequest as a middleware:

const express = require('express');
const app = express();

app.use(
metroBundlerServer.processRequest.bind(metroBundlerServer),
);

app.listen(8081);

Method runServer(config, options)​

Starts a development server based on the given configuration and options. Returns the server. -We recommend using runMetro instead of runServer, runMetro calls this function.

Options​

  • host (string): Where to host the server on.
  • onReady (Function): Called when the server is ready to serve requests.
  • secure (boolean): DEPRECATED Whether the server should run on https instead of http.
  • secureKey (string): DEPRECATED The key to use for https when secure is on.
  • secureCert (string): DEPRECATED The cert to use for https when secure is on.
  • secureServerOptions (Object): The options object to pass to Metro's HTTPS server. The presence of this object will make Metro's server run on https. Refer to the Node docs for valid options.
  • waitForBundler (boolean): Whether to wait for the bundler to finish initializing before returning the server instance.
const config = await Metro.loadConfig();

await Metro.runServer(config);
const fs = require('fs');

const config = await Metro.loadConfig();

await Metro.runServer(config, {
secureServerOptions: {
ca: fs.readFileSync('path/to/ca'),
cert: fs.readFileSync('path/to/cert'),
key: fs.readFileSync('path/to/key'),
}
});

Method runBuild(config, options)​

Given a configuration and a set of options that you would typically pass to a server, plus a set of options specific to the bundle itself, a bundle will be built. The return value is a Promise that resolves to an object with two properties, code and map. This is useful at build time.

Options​

  • dev (boolean): Create a development version of the build (process.env.NODE_ENV = 'development').
  • entry (string): Pointing to the entry file to bundle.
  • onBegin (Function): Called when the bundling starts.
  • onComplete (Function): Called when the bundling finishes.
  • onProgress (Function): Called during the bundle, every time there's new information available about the module count/progress.
  • minify (boolean): Whether Metro should minify the bundle.
  • out (string): Path to the output bundle.
  • platform ('web' | 'android' | 'ios'): Which platform to bundle for if a list of platforms is provided.
  • sourceMap (boolean): Whether Metro should generate source maps.
  • sourceMapUrl (string): URL where the source map can be found. It defaults to the same same URL as the bundle, but changing the extension from .bundle to .map. When inlineSourceMap is true, this property has no effect.
const config = await Metro.loadConfig();

await Metro.runBuild(config, {
entry: 'index.js',
platform: 'ios',
minify: true,
out: '/Users/Metro/metro-ios.js'
});

Method createConnectMiddleware(config)​

Instead of creating the full server, creates a Connect middleware that answers to bundle requests. This middleware can then be plugged into your own servers. The port parameter is optional and only used for logging purposes.

Options​

  • port (number): Port for the Connect middleware (only for logging purposes).
const Metro = require('metro');
const express = require('express');
const app = express();
const server = require('http').Server(app);

Metro.loadConfig().then(async config => {
const connectMiddleware = await Metro.createConnectMiddleware(config);
const {server: {port}} = config;

app.use(connectMiddleware.middleware);
server.listen(port);
connectMiddleware.attachHmrServer(server);
});

Available options​

Configuration​

Check Configuring Metro for details on configuration options.

URL and bundle request​

The server has the ability to serve assets, bundles and source maps for those bundles.

Assets​

In order to request an asset, you can freely use the require method as if it was another JS file. The server will treat this specific require calls and make them return the path to that file. When an asset is requested (an asset is recognized by its extension, which has to be on the assetExts array) it is generally served as-is.

However, the server is also able to serve specific assets depending on the platform and on the requested size (in the case of images). The way you specify the platform is via the dotted suffix (e.g. .ios) and the resolution via the at suffix (e.g. @2x). This is transparently handled for you when using require.

Bundle​

Any JS file can be used as the root for a bundle request. The file will be looked in the projectRoot. All files that are required by the root will be recursively included. In order to request a bundle, just change the extension from .js to .bundle. Options for building the bundle are passed as query parameters (all optional).

  • dev: build the bundle in development mode or not. Maps 1:1 to the dev setting of the bundles. Pass true or false as strings into the URL.
  • platform: platform requesting the bundle. Can be ios or android. Maps 1:1 to the platform setting of the bundles.
  • minify: whether code should be minified or not. Maps 1:1 to the minify setting of the bundles. Pass true or false as strings into the URL.
  • excludeSource: whether sources should be included in the source map or not. Pass true or false as strings into the URL.

For instance, requesting http://localhost:8081/foo/bar/baz.bundle?dev=true&platform=ios will create a bundle out of foo/bar/baz.js for iOS in development mode.

Source maps​

Source maps are built for each bundle by using the same URL as the bundle (thus, the same as the JS file acting as a root). This will only work when inlineSourceMap is set to false. All options you passed to the bundle will be added to the source map URL; otherwise, they wouldn't match.

JavaScript transformer​

The JavaScript transformer (babelTransformerPath) is the place where JS code will be manipulated; useful for calling Babel. The transformer can export two methods:

Method transform(module)​

Mandatory method that will transform code. The object received has information about the module being transformed (e.g its path, code...) and the returned object has to contain an ast key that is the AST representation of the transformed code. The default shipped transformer does the bare minimum amount of work by just parsing the code to AST:

const babylon = require('@babel/parser');

module.exports.transform = (file: {filename: string, src: string}) => {
const ast = babylon.parse(file.src, {sourceType: 'module'});

return {ast};
};

If you would like to plug-in Babel, you can simply do that by passing the code to it:

const {transformSync} = require('@babel/core');

module.exports.transform = file => {
return transformSync(file.src, {
// Babel options...
});
};

Method getCacheKey()​

Optional method that returns the cache key of the transformer. When using different transformers, this allows to correctly tie a transformed file to the transformer that converted it. The result of the method has to be a string.

- +We recommend using runMetro instead of runServer, runMetro calls this function.

Options​

  • host (string): Where to host the server on.
  • onReady (Function): Called when the server is ready to serve requests.
  • secure (boolean): DEPRECATED Whether the server should run on https instead of http.
  • secureKey (string): DEPRECATED The key to use for https when secure is on.
  • secureCert (string): DEPRECATED The cert to use for https when secure is on.
  • secureServerOptions (Object): The options object to pass to Metro's HTTPS server. The presence of this object will make Metro's server run on https. Refer to the Node docs for valid options.
  • waitForBundler (boolean): Whether to wait for the bundler to finish initializing before returning the server instance.
const config = await Metro.loadConfig();

await Metro.runServer(config);
const fs = require('fs');

const config = await Metro.loadConfig();

await Metro.runServer(config, {
secureServerOptions: {
ca: fs.readFileSync('path/to/ca'),
cert: fs.readFileSync('path/to/cert'),
key: fs.readFileSync('path/to/key'),
}
});

Method runBuild(config, options)​

Given a configuration and a set of options that you would typically pass to a server, plus a set of options specific to the bundle itself, a bundle will be built. The return value is a Promise that resolves to an object with two properties, code and map. This is useful at build time.

Options​

  • dev (boolean): Create a development version of the build (process.env.NODE_ENV = 'development').
  • entry (string): Pointing to the entry file to bundle.
  • onBegin (Function): Called when the bundling starts.
  • onComplete (Function): Called when the bundling finishes.
  • onProgress (Function): Called during the bundle, every time there's new information available about the module count/progress.
  • minify (boolean): Whether Metro should minify the bundle.
  • out (string): Path to the output bundle.
  • platform ('web' | 'android' | 'ios'): Which platform to bundle for if a list of platforms is provided.
  • sourceMap (boolean): Whether Metro should generate source maps.
  • sourceMapUrl (string): URL where the source map can be found. It defaults to the same same URL as the bundle, but changing the extension from .bundle to .map. When inlineSourceMap is true, this property has no effect.
const config = await Metro.loadConfig();

await Metro.runBuild(config, {
entry: 'index.js',
platform: 'ios',
minify: true,
out: '/Users/Metro/metro-ios.js'
});

Method createConnectMiddleware(config)​

Instead of creating the full server, creates a Connect middleware that answers to bundle requests. This middleware can then be plugged into your own servers. The port parameter is optional and only used for logging purposes.

Options​

  • port (number): Port for the Connect middleware (only for logging purposes).
const Metro = require('metro');
const express = require('express');
const app = express();
const server = require('http').Server(app);

Metro.loadConfig().then(async config => {
const connectMiddleware = await Metro.createConnectMiddleware(config);
const {server: {port}} = config;

app.use(connectMiddleware.middleware);
server.listen(port);
connectMiddleware.attachHmrServer(server);
});

Available options​

Configuration​

Check Configuring Metro for details on configuration options.

URL and bundle request​

The server has the ability to serve assets, bundles and source maps for those bundles.

Assets​

In order to request an asset, you can freely use the require method as if it was another JS file. The server will treat this specific require calls and make them return the path to that file. When an asset is requested (an asset is recognized by its extension, which has to be on the assetExts array) it is generally served as-is.

However, the server is also able to serve specific assets depending on the platform and on the requested size (in the case of images). The way you specify the platform is via the dotted suffix (e.g. .ios) and the resolution via the at suffix (e.g. @2x). This is transparently handled for you when using require.

Bundle​

Any JS file can be used as the root for a bundle request. The file will be looked in the projectRoot. All files that are required by the root will be recursively included. In order to request a bundle, just change the extension from .js to .bundle. Options for building the bundle are passed as query parameters (all optional).

  • dev: build the bundle in development mode or not. Maps 1:1 to the dev setting of the bundles. Pass true or false as strings into the URL.
  • platform: platform requesting the bundle. Can be ios or android. Maps 1:1 to the platform setting of the bundles.
  • minify: whether code should be minified or not. Maps 1:1 to the minify setting of the bundles. Pass true or false as strings into the URL.
  • excludeSource: whether sources should be included in the source map or not. Pass true or false as strings into the URL.

For instance, requesting http://localhost:8081/foo/bar/baz.bundle?dev=true&platform=ios will create a bundle out of foo/bar/baz.js for iOS in development mode.

Source maps​

Source maps are built for each bundle by using the same URL as the bundle (thus, the same as the JS file acting as a root). This will only work when inlineSourceMap is set to false. All options you passed to the bundle will be added to the source map URL; otherwise, they wouldn't match.

JavaScript transformer​

The JavaScript transformer (babelTransformerPath) is the place where JS code will be manipulated; useful for calling Babel. The transformer can export two methods:

Method transform(module)​

Mandatory method that will transform code. The object received has information about the module being transformed (e.g its path, code...) and the returned object has to contain an ast key that is the AST representation of the transformed code. The default shipped transformer does the bare minimum amount of work by just parsing the code to AST:

const babylon = require('@babel/parser');

module.exports.transform = (file: {filename: string, src: string}) => {
const ast = babylon.parse(file.src, {sourceType: 'module'});

return {ast};
};

If you would like to plug-in Babel, you can simply do that by passing the code to it:

const {transformSync} = require('@babel/core');

module.exports.transform = file => {
return transformSync(file.src, {
// Babel options...
});
};

Method getCacheKey()​

Optional method that returns the cache key of the transformer. When using different transformers, this allows to correctly tie a transformed file to the transformer that converted it. The result of the method has to be a string.

+ \ No newline at end of file diff --git a/docs/local-development/index.html b/docs/local-development/index.html index b476375d9b..adbdaaf584 100644 --- a/docs/local-development/index.html +++ b/docs/local-development/index.html @@ -13,15 +13,15 @@ - +
-

Local Development Setup

This page includes tips for developers working on Metro itself, including how to test your changes within other local projects.

Testing Metro Changes inside a React Native Project​

When developing Metro, running your iterations against a local target project can be a great way to test the impact of your changes end-to-end.

Our recommended workflow is to use yarn link to register local metro packages within your development clone and then hot-switch to these versions in the consuming project. These instructions cover linking a local Metro clone with a bare workflow React Native app (i.e. having run npx react-native init MetroTestApp).

.
└── Development
β”œβ”€β”€ metro # metro clone
└── MetroTestApp # target project
  1. Use yarn link in your metro clone to register local packages

    From inside our metro clone, yarn link is responsible for registering local package folders to be linked to elsewhere.

    We recommend using npm exec --workspaces to register all packages in the metro repo β€” these can be individually linked into the target project later.

    npm exec --workspaces -- yarn link
  2. Use yarn link to replace Metro packages in your target project

    From inside our target project folder, yarn link <package-name> can be used to apply our registered metro packages for that project only.

    # Links 3 packages
    yarn link metro metro-config metro-runtime

    Note: At mininum, the metro and metro-runtime packages need to be linked.

  3. Configure Metro watchFolders to work with our linked packages

    Because yarn link has included files outside of the immediate React Native project folder, we need to inform Metro that this set of files exists (as it will not automatically follow the symlinks). Add the following to your metro.config.js:

    + const path = require('path');

    module.exports = {
    + watchFolders: [
    + path.resolve(__dirname, './node_modules'),
    + // Include necessary file paths for `yarn link`ed modules
    + path.resolve(__dirname, '../metro/packages'),
    + path.resolve(__dirname, '../metro/node_modules'),
    + ],
    ...
    };

    Run Metro

    Now we should be able to run Metro within our target project. Remember to restart this command after any code changes you make to metro or to the target project's metro.config.js file.

    yarn react-native start
  4. (Optional) Clean up with yarn unlink

    If you want to restore the remote (i.e. production npm) versions of metro packages in your target project, step 2 (and 1) can be repeated with yarn unlink.

Debug Logging​

Metro uses the debug package to write logs under named debug scopes (for example: Metro:WatchmanWatcher). Set the DEBUG environment variable before starting Metro to enable logs matching the supplied pattern.

The snippet below provides a pattern matching all Metro-defined messages.

DEBUG='Metro:*' yarn metro serve
- +

Local Development Setup

This page includes tips for developers working on Metro itself, including how to test your changes within other local projects.

Testing Metro Changes inside a React Native Project​

When developing Metro, running your iterations against a local target project can be a great way to test the impact of your changes end-to-end.

Our recommended workflow is to use yarn link to register local metro packages within your development clone and then hot-switch to these versions in the consuming project. These instructions cover linking a local Metro clone with a bare workflow React Native app (i.e. having run npx react-native init MetroTestApp).

.
└── Development
β”œβ”€β”€ metro # metro clone
└── MetroTestApp # target project
  1. Use yarn link in your metro clone to register local packages

    From inside our metro clone, yarn link is responsible for registering local package folders to be linked to elsewhere.

    We recommend using npm exec --workspaces to register all packages in the metro repo β€” these can be individually linked into the target project later.

    npm exec --workspaces -- yarn link
  2. Use yarn link to replace Metro packages in your target project

    From inside our target project folder, yarn link <package-name> can be used to apply our registered metro packages for that project only.

    # Links 3 packages
    yarn link metro metro-config metro-runtime

    Note: At mininum, the metro and metro-runtime packages need to be linked.

  3. Configure Metro watchFolders to work with our linked packages

    Because yarn link has included files outside of the immediate React Native project folder, we need to inform Metro that this set of files exists (as it will not automatically follow the symlinks). Add the following to your metro.config.js:

    + const path = require('path');

    module.exports = {
    + watchFolders: [
    + path.resolve(__dirname, './node_modules'),
    + // Include necessary file paths for `yarn link`ed modules
    + path.resolve(__dirname, '../metro/packages'),
    + path.resolve(__dirname, '../metro/node_modules'),
    + ],
    ...
    };

    Run Metro

    Now we should be able to run Metro within our target project. Remember to restart this command after any code changes you make to metro or to the target project's metro.config.js file.

    yarn react-native start
  4. (Optional) Clean up with yarn unlink

    If you want to restore the remote (i.e. production npm) versions of metro packages in your target project, step 2 (and 1) can be repeated with yarn unlink.

Debug Logging​

Metro uses the debug package to write logs under named debug scopes (for example: Metro:WatchmanWatcher). Set the DEBUG environment variable before starting Metro to enable logs matching the supplied pattern.

The snippet below provides a pattern matching all Metro-defined messages.

DEBUG='Metro:*' yarn metro serve
+ \ No newline at end of file diff --git a/docs/module-api/index.html b/docs/module-api/index.html index 85d1217623..171a581c4a 100644 --- a/docs/module-api/index.html +++ b/docs/module-api/index.html @@ -13,15 +13,15 @@ - +
-

Module API

Metro is designed to allow code written for Node (or for bundlers targeting the Web) to run mostly unmodified. The main APIs available to application code are listed below.

require()​

Similar to Node's require() function. require() takes a module name (or path) and returns the result of evaluating that module's code. Modules referenced by require() will be added to the bundle.

const localModule = require('./path/module');
const asset = require('./path/asset.png');
const jsonData = require('./path/data.json');
const {View} = require('react-native');

The argument to require() must be a compile-time constant. The dynamicDepsInPackages config option controls whether calling require() with a non-constant argument will fail at build time or at runtime.

Advanced usage: require at runtime​

At build time, Metro resolves module names to absolute paths and assigns an opaque module ID to each one.

At runtime, require refers to a function that takes an opaque module ID (not a name or path) and returns a module. This can be useful if you already have a module ID returned by another module API, such as require.resolveWeak.

const localModule = require('./path/module');
const id = require.resolveWeak('./path/module');
// Bypass the restriction on non-constant require() arguments
const dynamicRequire = require;
dynamicRequire(id) === localModule; // true

module.exports​

Similar to module.exports in Node. The module.exports property holds the value require() will return for the current module after it finishes evaluating.

ES Modules syntax (import and export)​

We currently recommend the use of @babel/plugin-transform-modules-commonjs in Metro projects to support import and export.

note

In React Native projects that use @react-native/babel-preset, import and export are supported out of the box.

import() (dynamic import)​

import() calls are supported out of the box. In React Native, using import() automatically splits your application code so that it loads faster during development, without affecting release builds.

info

For framework implementers:

  1. Enable lazy bundling by adding &lazy=true to the initial HTTP bundle URL your framework requests from Metro.
  2. At runtime, import() calls a framework-defined function to fetch and evaluate the split bundle. Your framework must implement this function if it uses the lazy=true parameter, or runtime errors will occur.

require.resolveWeak()​

Takes a module name (or path) and returns that module's opaque ID, without including it in the bundle. This is a specialised API intended to be used by frameworks; application code will rarely need to use it directly. See the section about using require at runtime.

- +

Module API

Metro is designed to allow code written for Node (or for bundlers targeting the Web) to run mostly unmodified. The main APIs available to application code are listed below.

require()​

Similar to Node's require() function. require() takes a module name (or path) and returns the result of evaluating that module's code. Modules referenced by require() will be added to the bundle.

const localModule = require('./path/module');
const asset = require('./path/asset.png');
const jsonData = require('./path/data.json');
const {View} = require('react-native');

The argument to require() must be a compile-time constant. The dynamicDepsInPackages config option controls whether calling require() with a non-constant argument will fail at build time or at runtime.

Advanced usage: require at runtime​

At build time, Metro resolves module names to absolute paths and assigns an opaque module ID to each one.

At runtime, require refers to a function that takes an opaque module ID (not a name or path) and returns a module. This can be useful if you already have a module ID returned by another module API, such as require.resolveWeak.

const localModule = require('./path/module');
const id = require.resolveWeak('./path/module');
// Bypass the restriction on non-constant require() arguments
const dynamicRequire = require;
dynamicRequire(id) === localModule; // true

module.exports​

Similar to module.exports in Node. The module.exports property holds the value require() will return for the current module after it finishes evaluating.

ES Modules syntax (import and export)​

We currently recommend the use of @babel/plugin-transform-modules-commonjs in Metro projects to support import and export.

note

In React Native projects that use @react-native/babel-preset, import and export are supported out of the box.

import() (dynamic import)​

import() calls are supported out of the box. In React Native, using import() automatically splits your application code so that it loads faster during development, without affecting release builds.

info

For framework implementers:

  1. Enable lazy bundling by adding &lazy=true to the initial HTTP bundle URL your framework requests from Metro.
  2. At runtime, import() calls a framework-defined function to fetch and evaluate the split bundle. Your framework must implement this function if it uses the lazy=true parameter, or runtime errors will occur.

require.resolveWeak()​

Takes a module name (or path) and returns that module's opaque ID, without including it in the bundle. This is a specialised API intended to be used by frameworks; application code will rarely need to use it directly. See the section about using require at runtime.

+ \ No newline at end of file diff --git a/docs/package-exports/index.html b/docs/package-exports/index.html index c7ca9f3fb7..756c844c09 100644 --- a/docs/package-exports/index.html +++ b/docs/package-exports/index.html @@ -13,15 +13,15 @@ - +
-

Package Exports Support (Experimental)

Background​

Introduced in Node.js 12.7.0, Package Exports is a modern approach for npm packages to specify entry points β€” the mapping of package subpaths which can be externally imported and which file(s) they should resolve to.

When Package Exports support is enabled via resolver.unstable_enablePackageExports, Metro's module resolution algorithm will consider the "exports" field in package.json files.

Configuration options​

OptionDescription
resolver.unstable_enablePackageExportsEnable Package Exports support.
resolver.unstable_conditionNamesThe set of condition names to assert when resolving conditional exports.
resolver.unstable_conditionsByPlatformThe additional condition names to assert when resolving for a given platform target.

Summary of breaking changes​

info

Package Exports resolution is available since Metro 0.76.1 and is disabled by default. We will provide the option to disable it for a long time yet, and have no plans to remove existing non-"exports" resolution behaviour.

Since Package Exports features overlap with existing React Native concepts (such as platform-specific extensions), and since "exports" had been live in the npm ecosystem for some time, we reached out to the React Native community to make sure our implementation would meet developers' needs (PR, final RFC).

This led us to create an implementation of Package Exports in Metro that is spec-compliant (necessitating some breaking changes), but backwards compatible otherwise (helping apps with existing imports to migrate gradually).

Breaking: Match "exports" first, then fall back to legacy resolution​

If present in a package.json file, "exports" will be the first field consulted when resolving a package.

  • "exports" will be used instead of any existing "react-native", "browser", or "main" field β€”Β or a file on disk at the same subpath (edge case).
  • Fallback: If the requested subpath is not matched in "exports", Metro will try to resolve it again, considering the above fields.

Subpaths matched in "exports" (including via subpath patterns) will use the exact target file path specified by a package.

Example​

For a package without an "exports" field, Metro tries multiple potential file locations based on the import specifier:

import FooComponent from 'some-pkg/FooComponent';
// Tries .[platform].js, .native.js, .js (+ TypeScript variants)

However, if "./FooComponent" is listed in "exports", Metro matches the import specifier to this subpath, and uses the target file specified by the package with no further rules:

import FooComponent from 'some-pkg/FooComponent';
// Resolves exact target from "exports" only
note

We have no plans to drop platform-specific extensions for packages not using "exports", or in app code.

Breaking: Import specifiers are matched exactly​

Previously, import specifiers (the string given to import or require()) could be defined using both extensioned or extensionless paths. This is no longer the case for subpath keys in the "exports" field.

Example​

{
"name": "some-pkg",
"exports": {
"./FooComponent": "./src/FooComponent.js"
}
}
import FooComponent from 'some-pkg/FooComponent.js';
// Inaccessible unless the package had also listed "./FooComponent.js"
// as an "exports" key

Note that this behaviour also applies for subpath patterns: "./*": "./src/*.js" is distinct from "./*.js": "./src/*.js".

Package encapsulation is lenient​

In Node.js, it is an error to import package subpaths that aren't explicitly listed in "exports". In Metro, we've decided to handle these errors leniently and resolve modules following the old behavior as necessary. This is intended to reduce user friction for previously allowed imports in existing Metro projects.

Instead of throwing an error, Metro will log a warning and fall back to file-based resolution.

warn: You have imported the module "foo/private/fn.js" which is not listed in
the "exports" of "foo". Consider updating your call site or asking the package
maintainer(s) to expose this API.
note

We plan to implement a strict mode for package encapsulation in future, to align with Node's default behavior. We recommend that all developers fix encapsulation warnings in their code.

Migration guide for package maintainers​

Adding an "exports" field to your package is entirely optional. Existing package resolution features will behave identically for packages which don't use "exports" β€” and we have no plans to remove this behaviour.

The Node.js spec gives guidance on migrating to "exports" in a non-breaking manner, however this is challenging in practice. For instance, if your React Native package uses platform-specific extensions on its public exports, this is a breaking change by default.

To make the introduction of "exports" non-breaking, ensure that every previously supported entry point is exported. It is best to explicitly specify entry points so that the package's public API is well-defined.

β€”Β https://nodejs.org/docs/latest-v19.x/api/packages.html#package-entry-points

Package subpaths​

caution

Please do not rely on lenient package encapsulation under Metro. While Metro does this for backwards compatibility, packages should follow how "exports" is documented in the spec and strictly implemented by other tools.

File extensions are important!​

Each subpath is an exact specifier (see section in RFC).

We recommend continuing to use extensionless specifiers for subpaths in packages targeting React Native β€”Β or defining both extensioned and extensionless specifiers. This will match matching existing user expectations.

  "exports": {
".": "./src/index.js",
"./FooComponent": "./src/FooComponent.js",
"./FooComponent.js": "./src/FooComponent.js"
}

Subpath patterns do not permit expansion​

Subpath patterns are a shorthand for mapping multiple subpaths β€”Β they do not permit path expansion (strictly a substring replacement), however will match nested directories (see section in RFC).

Only one * is permitted per side of a subpath pattern.

  "exports": {
".": "./index.js",
"./utils/*": "./utils/*.js"
}
  • 'pkg/utils/foo' matches 'pkg/utils/foo.js'.
  • 'pkg/utils/foo/bar' matches 'pkg/utils/foo/bar.js'.
  • 'pkg/utils/foo' does not match 'pkg/utils/foo.bar.js'.

Replacing "browser" and "react-native" fields​

We've introduced "react-native" as a community condition (for use with conditional exports). This represents React Native, the framework, sitting alongside other recognised runtimes such as "node" and "deno" (RFC).

Community Conditions Definitions β€”Β "react-native"

Will be matched by the React Native framework (all platforms). To target React Native for Web, "browser" should be specified before this condition.

This replaces the previous "react-native" root field. The priority order for how this was previously resolved was determined by projects, which created ambiguity when using React Native for Web. Under "exports", packages concretely define the resolution order for conditional entry points β€”Β removing this ambiguity.

Example: Use conditional exports to target web and React Native​

  "exports": {
"browser": "./dist/index-browser.js",
"react-native": "./dist/index-react-native.js",
"default": "./dist/index.js"
}
note

We chose not to introduce "android" and "ios" conditions, due to the prevalence of other existing platform selection methods, and the complexity of how this behavior might work across frameworks. We recommend the Platform.select() API instead.

Replacing platform-specific extensions​

Breaking change: Subpaths matched in "exports" (including via subpath patterns) will use the exact file path specified by a package, and will not attempt to expand sourceExts or platform-specific extensions.

Use Platform.select() (React Native)​

  "exports": {
"./FooComponent": "./src/FooComponent.js"
}
// src/FooComponent.js

const FooComponent = Platform.select({
android: require('./FooComponentAndroid.js'),
ios: require('FooComponentIOS.js'),
});

export default FooComponent;

Asset files​

As with source files, assets must be listed in "exports" to be imported without warnings. Asset files with multiple densities, e.g. icon.png and icon@2x.png, will continue to work without being listed individually.

Using subpath patterns can be a convenient method to export many assets. We recommend specifying asset subpaths with their file extension.

{
"exports": {
"./assets/*.png": "./dist/assets/*.png"
}
}
- +

Package Exports Support (Experimental)

Background​

Introduced in Node.js 12.7.0, Package Exports is a modern approach for npm packages to specify entry points β€” the mapping of package subpaths which can be externally imported and which file(s) they should resolve to.

When Package Exports support is enabled via resolver.unstable_enablePackageExports, Metro's module resolution algorithm will consider the "exports" field in package.json files.

Configuration options​

OptionDescription
resolver.unstable_enablePackageExportsEnable Package Exports support.
resolver.unstable_conditionNamesThe set of condition names to assert when resolving conditional exports.
resolver.unstable_conditionsByPlatformThe additional condition names to assert when resolving for a given platform target.

Summary of breaking changes​

info

Package Exports resolution is available since Metro 0.76.1 and is disabled by default. We will provide the option to disable it for a long time yet, and have no plans to remove existing non-"exports" resolution behaviour.

Since Package Exports features overlap with existing React Native concepts (such as platform-specific extensions), and since "exports" had been live in the npm ecosystem for some time, we reached out to the React Native community to make sure our implementation would meet developers' needs (PR, final RFC).

This led us to create an implementation of Package Exports in Metro that is spec-compliant (necessitating some breaking changes), but backwards compatible otherwise (helping apps with existing imports to migrate gradually).

Breaking: Match "exports" first, then fall back to legacy resolution​

If present in a package.json file, "exports" will be the first field consulted when resolving a package.

  • "exports" will be used instead of any existing "react-native", "browser", or "main" field β€”Β or a file on disk at the same subpath (edge case).
  • Fallback: If the requested subpath is not matched in "exports", Metro will try to resolve it again, considering the above fields.

Subpaths matched in "exports" (including via subpath patterns) will use the exact target file path specified by a package.

Example​

For a package without an "exports" field, Metro tries multiple potential file locations based on the import specifier:

import FooComponent from 'some-pkg/FooComponent';
// Tries .[platform].js, .native.js, .js (+ TypeScript variants)

However, if "./FooComponent" is listed in "exports", Metro matches the import specifier to this subpath, and uses the target file specified by the package with no further rules:

import FooComponent from 'some-pkg/FooComponent';
// Resolves exact target from "exports" only
note

We have no plans to drop platform-specific extensions for packages not using "exports", or in app code.

Breaking: Import specifiers are matched exactly​

Previously, import specifiers (the string given to import or require()) could be defined using both extensioned or extensionless paths. This is no longer the case for subpath keys in the "exports" field.

Example​

{
"name": "some-pkg",
"exports": {
"./FooComponent": "./src/FooComponent.js"
}
}
import FooComponent from 'some-pkg/FooComponent.js';
// Inaccessible unless the package had also listed "./FooComponent.js"
// as an "exports" key

Note that this behaviour also applies for subpath patterns: "./*": "./src/*.js" is distinct from "./*.js": "./src/*.js".

Package encapsulation is lenient​

In Node.js, it is an error to import package subpaths that aren't explicitly listed in "exports". In Metro, we've decided to handle these errors leniently and resolve modules following the old behavior as necessary. This is intended to reduce user friction for previously allowed imports in existing Metro projects.

Instead of throwing an error, Metro will log a warning and fall back to file-based resolution.

warn: You have imported the module "foo/private/fn.js" which is not listed in
the "exports" of "foo". Consider updating your call site or asking the package
maintainer(s) to expose this API.
note

We plan to implement a strict mode for package encapsulation in future, to align with Node's default behavior. We recommend that all developers fix encapsulation warnings in their code.

Migration guide for package maintainers​

Adding an "exports" field to your package is entirely optional. Existing package resolution features will behave identically for packages which don't use "exports" β€” and we have no plans to remove this behaviour.

The Node.js spec gives guidance on migrating to "exports" in a non-breaking manner, however this is challenging in practice. For instance, if your React Native package uses platform-specific extensions on its public exports, this is a breaking change by default.

To make the introduction of "exports" non-breaking, ensure that every previously supported entry point is exported. It is best to explicitly specify entry points so that the package's public API is well-defined.

β€”Β https://nodejs.org/docs/latest-v19.x/api/packages.html#package-entry-points

Package subpaths​

caution

Please do not rely on lenient package encapsulation under Metro. While Metro does this for backwards compatibility, packages should follow how "exports" is documented in the spec and strictly implemented by other tools.

File extensions are important!​

Each subpath is an exact specifier (see section in RFC).

We recommend continuing to use extensionless specifiers for subpaths in packages targeting React Native β€”Β or defining both extensioned and extensionless specifiers. This will match matching existing user expectations.

  "exports": {
".": "./src/index.js",
"./FooComponent": "./src/FooComponent.js",
"./FooComponent.js": "./src/FooComponent.js"
}

Subpath patterns do not permit expansion​

Subpath patterns are a shorthand for mapping multiple subpaths β€”Β they do not permit path expansion (strictly a substring replacement), however will match nested directories (see section in RFC).

Only one * is permitted per side of a subpath pattern.

  "exports": {
".": "./index.js",
"./utils/*": "./utils/*.js"
}
  • 'pkg/utils/foo' matches 'pkg/utils/foo.js'.
  • 'pkg/utils/foo/bar' matches 'pkg/utils/foo/bar.js'.
  • 'pkg/utils/foo' does not match 'pkg/utils/foo.bar.js'.

Replacing "browser" and "react-native" fields​

We've introduced "react-native" as a community condition (for use with conditional exports). This represents React Native, the framework, sitting alongside other recognised runtimes such as "node" and "deno" (RFC).

Community Conditions Definitions β€”Β "react-native"

Will be matched by the React Native framework (all platforms). To target React Native for Web, "browser" should be specified before this condition.

This replaces the previous "react-native" root field. The priority order for how this was previously resolved was determined by projects, which created ambiguity when using React Native for Web. Under "exports", packages concretely define the resolution order for conditional entry points β€”Β removing this ambiguity.

Example: Use conditional exports to target web and React Native​

  "exports": {
"browser": "./dist/index-browser.js",
"react-native": "./dist/index-react-native.js",
"default": "./dist/index.js"
}
note

We chose not to introduce "android" and "ios" conditions, due to the prevalence of other existing platform selection methods, and the complexity of how this behavior might work across frameworks. We recommend the Platform.select() API instead.

Replacing platform-specific extensions​

Breaking change: Subpaths matched in "exports" (including via subpath patterns) will use the exact file path specified by a package, and will not attempt to expand sourceExts or platform-specific extensions.

Use Platform.select() (React Native)​

  "exports": {
"./FooComponent": "./src/FooComponent.js"
}
// src/FooComponent.js

const FooComponent = Platform.select({
android: require('./FooComponentAndroid.js'),
ios: require('FooComponentIOS.js'),
});

export default FooComponent;

Asset files​

As with source files, assets must be listed in "exports" to be imported without warnings. Asset files with multiple densities, e.g. icon.png and icon@2x.png, will continue to work without being listed individually.

Using subpath patterns can be a convenient method to export many assets. We recommend specifying asset subpaths with their file extension.

{
"exports": {
"./assets/*.png": "./dist/assets/*.png"
}
}
+ \ No newline at end of file diff --git a/docs/resolution/index.html b/docs/resolution/index.html index c26acdfb70..aab3e18bd8 100644 --- a/docs/resolution/index.html +++ b/docs/resolution/index.html @@ -13,7 +13,7 @@ - + @@ -23,8 +23,8 @@

Module Resolution

Module resolution is the process of translating module names to module paths at build time. For example, if your project contains the code:

// src/App.js
import {View} from 'react-native';
// ...

Metro needs to know where in your project to load the react-native module from. This will typically resolve to something like node_modules/react-native/index.js.

Likewise, if your project contains the (similar) code:

// src/App.js
import Comp from './Component';
// ...

Metro needs to understand that you are referring to, say, src/Component.js, and not another file named Component that may also exist elsewhere.

Metro implements a version of Node's module resolution algorithm, augmented with additional Metro-specific features.

These Metro-specific features include:

  • Haste: An opt-in mechanism for importing modules by their globally-unique name anywhere in the project, e.g. import Foo from 'Foo'.
  • Platform extensions: Used by React Native to allow developers to write platform-specific versions of their JavaScript modules.
  • Asset extensions and image resolutions: Used by React Native to automatically select the best version of an image asset based on the device's screen density at runtime.
  • Custom resolvers: Metro integrators can provide their own resolver implementations to override almost everything about how modules are resolved.

Resolution algorithm​

Given a resolution context context, a module name moduleName, and an optional platform identifier platform, Metro's resolver performs RESOLVE(context, moduleName, platform), which either returns one of the resolution types, or throws an error.

Resolution types​

Source file​

The request is resolved to some absolute path representing a physical file on disk.

Asset files​

The request is resolved to one or more absolute paths representing physical files on disk.

Empty module​

The request is resolved to a built-in empty module, namely the one specified in resolver.emptyModulePath.

Algorithm​

note

These are the rules that Metro's default resolver follows. Refer to metro-resolver's source code for more details.

RESOLVE​

Parameters: (context, moduleName, platform)

  1. If a custom resolver is defined, then
    1. Return the result of the custom resolver.
  2. Otherwise, attempt to resolve moduleName as a path
    1. Let absoluteModuleName be the result of prepending the current directory (i.e. parent of context.originModulePath) with moduleName.
    2. Return the result of RESOLVE_MODULE(context, absoluteModuleName, platform), or continue.
  3. Apply redirections to moduleName. If this results in an empty module, then
    1. Return the empty module.
  4. If Haste resolutions are allowed, then
    1. Get the result of RESOLVE_HASTE(context, moduleName, platform).
    2. If resolved as a Haste package path, then
      1. Perform the algorithm for resolving a path (step 2 above). Throw an error if this resolution fails. For example, if the Haste package path for 'a/b' is foo/package.json, perform step 2 as if moduleName was foo/c.
  5. If context.disableHierarchicalLookup is not true, then
    1. Try resolving moduleName under node_modules from the current directory (i.e. parent of context.originModulePath) up to the root directory.
    2. Perform RESOLVE_PACKAGE(context, modulePath, platform) for each candidate path.
  6. For each element nodeModulesPath of context.nodeModulesPaths:
    1. Try resolving moduleName under nodeModulesPath as if the latter was another node_modules directory (similar to step 5 above).
    2. Perform RESOLVE_PACKAGE(context, modulePath, platform) for each candidate path.
  7. If context.extraNodeModules is set:
    1. Split moduleName into a package name (including an optional scope) and relative path.
    2. Look up the package name in context.extraNodeModules. If found, then
      1. Construct a path modulePath by replacing the package name part of moduleName with the value found in context.extraNodeModules
      2. Return the result of RESOLVE_PACKAGE(context, modulePath, platform).
  8. If no valid resolution has been found, throw a resolution failure error.

RESOLVE_MODULE​

Parameters: (context, moduleName, platform)

  1. Let filePath be the result of applying redirections to moduleName. This may locate a replacement subpath from a containing package.json file based on the browser field spec.
  2. Return the result of RESOLVE_FILE(context, filePath, platform), or continue.
  3. Otherwise, let dirPath be the directory path of filePath.
  4. If a file dirPath + 'package.json' exists, resolve based on the browser field spec:
    1. Let mainModulePath be the result of reading the package's entry path using context.mainFields.
    2. Return the result of RESOLVE_FILE(context, mainModulePath, platform), or continue.
    3. Return the result of RESOLVE_FILE(context, mainModulePath + '/index', platform).
    4. Throw an error if no resolution could be found.

RESOLVE_PACKAGE​

Parameters: (context, moduleName, platform)

  1. If context.enablePackageExports is enabled, and a containing package.json file contains the field "exports", get result of RESOLVE_PACKAGE_EXPORTS(context, packagePath, filePath, exportsField, platform).
    1. If resolved path exists, return result.
    2. Else, log either a package configuration or package encapsulation warning.
  2. Return the result of RESOLVE_MODULE(context, filePath, platform).

RESOLVE_PACKAGE_EXPORTS​

Parameters: (context, packagePath, filePath, exportsField, platform)

Resolves a package subpath based on the Package Entry Points spec (the "exports" field), when resolver.unstable_enablePackageExports is enabled.

  1. Let subpath be the relative path from packagePath to filePath, or '.'.
  2. If exportsField contains an invalid configuration or values, raise an InvalidPackageConfigurationError.
  3. If subpath is not defined by exportsField, raise a PackagePathNotExportedError.
  4. Let target be the result of matching subpath in exportsField after applying any conditional exports and/or substituting a subpath pattern match.
    1. Condition names will be asserted from the union of context.unstable_conditionNames and context.unstable_conditionNamesByPlatform for platform, in the order defined by exportsField.
  5. If target refers to an asset, then
    1. Return the result of RESOLVE_ASSET(context, target, platform).
  6. Return target as a source file resolution without applying redirections or trying any platform or extension variants.

RESOLVE_FILE​

Parameters: (context, filePath, platform)

  1. If the path refers to an asset, then
    1. Return the result of RESOLVE_ASSET(context, filePath, platform).
  2. Otherwise, if the path exists, then
    1. Try all platform and extension variants in sequence. Return a source file resolution for the first one that exists after applying redirections. For example, if platform is android and context.sourceExts is ['js', 'jsx'], try this sequence of potential file names:
      1. moduleName + '.android.js'
      2. moduleName + '.native.js' (if context.preferNativePlatform is true)
      3. moduleName + '.js'
      4. moduleName + '.android.jsx'
      5. moduleName + '.native.jsx' (if context.preferNativePlatform is true)
      6. moduleName + '.jsx'

RESOLVE_ASSET​

Parameters: (context, filePath, platform)

  1. Use context.resolveAsset to collect all asset variants.
  2. Return an asset resolution containing the collected asset paths.

RESOLVE_HASTE​

Parameters: (context, moduleName, platform)

  1. Try resolving moduleName as a Haste module. If found, then
    1. Return result as a source file resolution without applying redirections or trying any platform or extension variants.
  2. Try resolving moduleName as a Haste (global) package, or a path relative to a Haste package. -For example, if moduleName is 'a/b/c', try the following potential Haste package names:
    1. 'a/b/c', relative path ''
    2. 'a/b', relative path './c'
    3. 'a', with relative path './b/c'

Resolution context​

assetExts: $ReadOnlySet<string>​

The set of file extensions used to identify asset files. Defaults to resolver.assetExts.

dev: boolean​

true if the resolution is for a development bundle, or false otherwise.

doesFileExist: string => boolean​

Returns true if the file with the given path exists, or false otherwise.

By default, Metro implements this by consulting an in-memory map of the filesystem that has been prepared in advance. This approach avoids disk I/O during module resolution.

nodeModulesPaths: $ReadOnlyArray<string>​

A list of paths to check for modules after looking through all node_modules directories.

By default this is set to resolver.nodeModulesPaths

preferNativePlatform: boolean​

If true, try .native.${ext} before .${ext} and after .${platform}.${ext} during resolution. Metro sets this to true.

redirectModulePath: string => string | false​

Rewrites a module path, or returns false to redirect to the special empty module. In the default resolver, the resolution algorithm terminates with an empty module result if redirectModulePath returns false.

Metro uses this to implement the package.json browser field spec, particularly the ability to replace and ignore specific files.

The default implementation of this function respects resolver.resolverMainFields.

resolveAsset: (dirPath: string, assetName: string, extension: string) => ?$ReadOnlyArray<string>​

Given a directory path, the base asset name and an extension, returns a list of all the asset file names that match the given base name in that directory, or null if no such files are found. The default implementation considers each of resolver.assetResolutions and uses the ${assetName}@${resolution}${extension} format for asset variant file names.

See also Static Image Resources in the React Native docs.

sourceExts: $ReadOnlyArray<string>​

The list of file extensions to try, in order, when resolving a module path that does not exist on disk. Defaults to resolver.sourceExts.

mainFields: $ReadOnlyArray<string>​

The ordered list of fields in package.json that should be read to resolve a package's main entry point (and any subpath file replacements) per the "browser" field spec. Defaults to resolver.resolverMainFields.

getPackage: string => PackageJson​

Given the path to a package.json file, returns the parsed file contents.

getPackageForModule: (absoluteModulePath: string) => ?PackageInfo
Deprecated
​

Given a candidate absolute module path that may exist under a package, locates and returns the closest package root (working upwards from the given path, stopping at the nearest node_modules), parsed package.json contents, and the package-relative path of the given path.

resolveHasteModule: string => ?string​

Resolves a Haste module name to an absolute path. Returns null if no such module exists.

The default implementation of this function uses metro-file-map's getModule method.

resolveHastePackage: string => ?string​

Resolves a Haste (global) package name to an absolute package.json path. Returns null if no such package exists.

The default implementation of this function uses metro-file-map's getPackage method and can be turned on or off using resolver.enableGlobalPackages.

allowHaste: boolean​

true if Haste resolutions are allowed in the current context, false otherwise.

disableHierarchicalLookup: boolean​

If true, the resolver should not perform lookup in node_modules directories per the Node resolution algorithm. Defaults to resolver.disableHierarchicalLookup.

extraNodeModules: ?{[string]: string}​

A mapping of package names to directories that is consulted after the standard lookup through node_modules as well as any nodeModulesPaths.

originModulePath: string​

The path to the current module, e.g. the one containing the import we are currently resolving.

customResolverOptions: {[string]: mixed}​

Any custom options passed to the resolver. By default, Metro populates this based on URL parameters in the bundle request, e.g. http://localhost:8081/index.bundle?resolver.key=value becomes {key: 'value'}.

resolveRequest: CustomResolver​

A alternative resolver function to which the current request may be delegated. Defaults to resolver.resolveRequest.

Metro expects resolveRequest to have the following signature:

function resolveRequest(
context: ResolutionContext,
moduleName: string,
platform: string | null,
): Resolution {
// ...
}

type Resolution =
| {type: 'empty'}
| {type: 'sourceFile', filePath: string}
| {type: 'assetFiles', filePaths: $ReadOnlyArray<string>};

When calling the default resolver with a non-null resolveRequest function, it represents a custom resolver and will always be called, fully replacing the default resolution logic.

Inside a custom resolver, resolveRequest is set to the default resolver function, for easy chaining and customization.

dependency: ?Dependency​

A dependency descriptor corresponding to the current resolution request. This is provided for diagnostic purposes only and may not be used for semantic purposes. See the Caching section for more information.

type Dependency = {
// The literal name provided to a require or import call. For example 'foo' in
// case of `require('foo')`.
name: string,

data: {
// A locally unique key for this dependency within the origin module.
key: string,

// Source locations from the Babel AST, relative to the origin module, where
// this dependency was encountered. This may be an empty array.
locs: $ReadOnlyArray<BabelSourceLocation>,

asyncType: 'async' | 'prefetch' | 'weak' | null,

// Other properties are considered internal and may change in the future.
...
},
};

Caching​

Resolver results may be cached under the following conditions:

  1. For given origin module paths A and B and target module name M, the resolution for M may be reused if all of the following conditions hold:
    1. A and B are in the same directory.
    2. The contents of dev and customResolverOptions are equivalent ( = serialize to JSON the same) in both calls to the resolver.
  2. Any cache of resolutions must be invalidated if any file in the project has changed.

Custom resolvers must adhere to these assumptions, e.g. they may not return different resolutions for origin modules in the same directory under the same customResolverOptions.

- +For example, if moduleName is 'a/b/c', try the following potential Haste package names:
  1. 'a/b/c', relative path ''
  2. 'a/b', relative path './c'
  3. 'a', with relative path './b/c'

Resolution context​

assetExts: $ReadOnlySet<string>​

The set of file extensions used to identify asset files. Defaults to resolver.assetExts.

dev: boolean​

true if the resolution is for a development bundle, or false otherwise.

doesFileExist: string => boolean​

Returns true if the file with the given path exists, or false otherwise.

By default, Metro implements this by consulting an in-memory map of the filesystem that has been prepared in advance. This approach avoids disk I/O during module resolution.

nodeModulesPaths: $ReadOnlyArray<string>​

A list of paths to check for modules after looking through all node_modules directories.

By default this is set to resolver.nodeModulesPaths

preferNativePlatform: boolean​

If true, try .native.${ext} before .${ext} and after .${platform}.${ext} during resolution. Metro sets this to true.

redirectModulePath: string => string | false​

Rewrites a module path, or returns false to redirect to the special empty module. In the default resolver, the resolution algorithm terminates with an empty module result if redirectModulePath returns false.

Metro uses this to implement the package.json browser field spec, particularly the ability to replace and ignore specific files.

The default implementation of this function respects resolver.resolverMainFields.

resolveAsset: (dirPath: string, assetName: string, extension: string) => ?$ReadOnlyArray<string>​

Given a directory path, the base asset name and an extension, returns a list of all the asset file names that match the given base name in that directory, or null if no such files are found. The default implementation considers each of resolver.assetResolutions and uses the ${assetName}@${resolution}${extension} format for asset variant file names.

See also Static Image Resources in the React Native docs.

sourceExts: $ReadOnlyArray<string>​

The list of file extensions to try, in order, when resolving a module path that does not exist on disk. Defaults to resolver.sourceExts.

mainFields: $ReadOnlyArray<string>​

The ordered list of fields in package.json that should be read to resolve a package's main entry point (and any subpath file replacements) per the "browser" field spec. Defaults to resolver.resolverMainFields.

getPackage: string => PackageJson​

Given the path to a package.json file, returns the parsed file contents.

getPackageForModule: (absoluteModulePath: string) => ?PackageInfo
Deprecated
​

Given a candidate absolute module path that may exist under a package, locates and returns the closest package root (working upwards from the given path, stopping at the nearest node_modules), parsed package.json contents, and the package-relative path of the given path.

resolveHasteModule: string => ?string​

Resolves a Haste module name to an absolute path. Returns null if no such module exists.

The default implementation of this function uses metro-file-map's getModule method.

resolveHastePackage: string => ?string​

Resolves a Haste (global) package name to an absolute package.json path. Returns null if no such package exists.

The default implementation of this function uses metro-file-map's getPackage method and can be turned on or off using resolver.enableGlobalPackages.

allowHaste: boolean​

true if Haste resolutions are allowed in the current context, false otherwise.

disableHierarchicalLookup: boolean​

If true, the resolver should not perform lookup in node_modules directories per the Node resolution algorithm. Defaults to resolver.disableHierarchicalLookup.

extraNodeModules: ?{[string]: string}​

A mapping of package names to directories that is consulted after the standard lookup through node_modules as well as any nodeModulesPaths.

originModulePath: string​

The path to the current module, e.g. the one containing the import we are currently resolving.

customResolverOptions: {[string]: mixed}​

Any custom options passed to the resolver. By default, Metro populates this based on URL parameters in the bundle request, e.g. http://localhost:8081/index.bundle?resolver.key=value becomes {key: 'value'}.

resolveRequest: CustomResolver​

A alternative resolver function to which the current request may be delegated. Defaults to resolver.resolveRequest.

Metro expects resolveRequest to have the following signature:

function resolveRequest(
context: ResolutionContext,
moduleName: string,
platform: string | null,
): Resolution {
// ...
}

type Resolution =
| {type: 'empty'}
| {type: 'sourceFile', filePath: string}
| {type: 'assetFiles', filePaths: $ReadOnlyArray<string>};

When calling the default resolver with a non-null resolveRequest function, it represents a custom resolver and will always be called, fully replacing the default resolution logic.

Inside a custom resolver, resolveRequest is set to the default resolver function, for easy chaining and customization.

dependency: ?Dependency​

A dependency descriptor corresponding to the current resolution request. This is provided for diagnostic purposes only and may not be used for semantic purposes. See the Caching section for more information.

type Dependency = {
// The literal name provided to a require or import call. For example 'foo' in
// case of `require('foo')`.
name: string,

data: {
// A locally unique key for this dependency within the origin module.
key: string,

// Source locations from the Babel AST, relative to the origin module, where
// this dependency was encountered. This may be an empty array.
locs: $ReadOnlyArray<BabelSourceLocation>,

asyncType: 'async' | 'prefetch' | 'weak' | null,

// Other properties are considered internal and may change in the future.
...
},
};

Caching​

Resolver results may be cached under the following conditions:

  1. For given origin module paths A and B and target module name M, the resolution for M may be reused if all of the following conditions hold:
    1. A and B are in the same directory.
    2. The contents of dev and customResolverOptions are equivalent ( = serialize to JSON the same) in both calls to the resolver.
  2. Any cache of resolutions must be invalidated if any file in the project has changed.

Custom resolvers must adhere to these assumptions, e.g. they may not return different resolutions for origin modules in the same directory under the same customResolverOptions.

+ \ No newline at end of file diff --git a/docs/source-map-format/index.html b/docs/source-map-format/index.html index ef924c9ffe..ba5e12b8a6 100644 --- a/docs/source-map-format/index.html +++ b/docs/source-map-format/index.html @@ -13,15 +13,15 @@ - +
-

Source Map Format

Metro produces standard source maps along with its JavaScript bundle output. In addition to the standard information, Metro encodes extra information in vendor-specific fields within the source map. This page serves as a specification for this encoding.

note

The content on this page assumes familiarity with the source map specification. Check out Anatomy of source maps for a general introduction to source maps and how they work.

x_facebook_sources​

The x_facebook_sources field encodes metadata about source files in a source map. Each piece of metadata represents some attribute intrinsic to the source code of that particular file - for example, the result of running some analysis over the AST. This allows tools such as debuggers and JS engines to access such analyses efficiently, without needing to parse or even have access to the source code.

In the same way that the standard sources field is a list of source URLs and sourcesContent is a list of (optional) source code strings, x_facebook_sources is a list of optional metadata tuples. The i-th metadata tuple (x_facebook_sources[i]) corresponds to the source file whose URL is sources[i].

In nested (indexed) source maps, x_facebook_sources may appear as part of any nested source map in sections that itself has a sources field.

If present, x_facebook_sources may be a different length than sources (but usually shouldn't be). In particular, if it's shorter than sources, x_facebook_sources interpreted as if it were padded with null values to match the length of sources.

info

If you are writing a tool that processes source maps generated by Metro, and want to generate a new source map containing a valid x_facebook_sources field, you'll mainly need to ensure that x_facebook_sources[i] still corresponds to sources[i] in the output - even if your tool reorders, adds or deletes elements in sources. Notably, this can be done without parsing/decoding the metadata tuples: unless your tool actively needs to access the information within them, you can treat them as opaque blobs of JSON.

If a tool cannot guarantee that the sources and x_facebook_sources arrays will stay in sync, it should delete the x_facebook_sources field from its output.

Metadata tuple​

Each metadata tuple is encoded as an array of zero or more entries. Each entry may be null to signify that it's missing. A run of trailing nulls may be truncated from the end of the tuple with no change in meaning. The metadata tuple itself may also be null to signify that the source file has no associated metadata.

The indices in each metadata tuple are assigned as follows:

Function map​

A function map is encoded as an object with the following two fields:

  • names: An array of strings.
  • mappings: A string following the encoding described below.

When decoded, mappings represents a list of 3-tuples of integers: (column, nameIndex, line), (column, nameIndex, line), .... The list is ordered by line and then column.

The presence of a 3-tuple (column, nameIndex, line) means that the local function name in the code region beginning at line and column (in the source file described by the current metadata tuple) is names[nameIndex].

Function map mappings field encoding​

The value of the mappings field is described by the Mappings production of the grammar detailed below.

  1. Mappings = [ ";" ] LineMappings { ";" { ";" } LineMappings }
  2. LineMappings = FirstColumnMapping "," ColumnMapping
  3. FirstColumnMapping = VLQ VLQ VLQ
  4. ColumnMapping = VLQ VLQ [ VLQ ]
  5. VLQ = A single Base64-encoded variable-length quantity, as defined in the source map specification.
note

The above grammar uses the following BNF-like notation:

NotationMeaning
[ X ]X appears zero or 1 times.
{ X }X appears 0 or more times.
"foo"The literal characters foo.

The three VLQs in FirstColumnMapping or ColumnMapping represent, in this order:

  1. Column delta:
    • In FirstColumnMapping: The column offset from the beginning of the line. (0 = first column)
    • In ColumnMapping: The column offset from the last-encountered FirstColumnMapping or ColumnMapping.
  2. Name delta: The name index offset from the last-encountered FirstColumnMapping or ColumnMapping. This is not reset between lines.
  3. Line delta: The line offset from the last-encountered FirstColumnMapping or ColumnMapping. This is not reset between lines.
    • This MUST be 0 (Base64 VLQ: A) if it is part of a ColumnMapping.
    • Implementations SHOULD omit this field from the encoded form of ColumnMapping.

Example​

Given a single source file called file.js, a complete source map might look like this:

note

Comments are for illustrative purposes - the source map format does not allow comments.

{
"version": 3,
"sources": ["file.js"],
"sourcesContent": ["function a(){} function b(){}"],
"mappings": "AAAA", // NOTE: Simplified
"x_facebook_sources": [
// Metadata tuple for source #0 (file.js)
[
// Metadata item #0.0 = function map for source #0 (file.js)
{
// a from 1:0
// <global> from 1:14
// b from 1:15
// (See detailed decoding procedure below.)
"mappings": "AAA,cC,CC",
"names": [
"a",
"<global>",
"b",
]
}
]
]
}

The decoding procedure for the function map in the above example is illustrated by the following code:

const decoded = [];
const names = ['a', '<global>', 'b']; // From the function map

let column = 0, nameIndex = 0, line = 1;
column += 0 /* A */; nameIndex += 0 /* A */; line += 0 /* A */;
decoded.push({column, name: names[nameIndex] /* 'a' */, line});

column += 14 /* c */; nameIndex += 1 /* C */; // no line delta
decoded.push({column, name: names[nameIndex] /* '<global>' */, line});

column += 1 /* C */; nameIndex += 1 /* C */; // no line delta
decoded.push({column, name: names[nameIndex] /* 'b' */, line});

/*
decoded = [
{column: 0, name: 'a', line: 1},
{column: 14, name: '<global>', line: 1},
{column: 15, name: 'b', line: 1},
]
*/

x_google_ignoreList​

Metro's source maps include the x_google_ignoreList field by default. The serializer.isThirdPartyModule option can be used to control which modules are ignore-listed.

- +

Source Map Format

Metro produces standard source maps along with its JavaScript bundle output. In addition to the standard information, Metro encodes extra information in vendor-specific fields within the source map. This page serves as a specification for this encoding.

note

The content on this page assumes familiarity with the source map specification. Check out Anatomy of source maps for a general introduction to source maps and how they work.

x_facebook_sources​

The x_facebook_sources field encodes metadata about source files in a source map. Each piece of metadata represents some attribute intrinsic to the source code of that particular file - for example, the result of running some analysis over the AST. This allows tools such as debuggers and JS engines to access such analyses efficiently, without needing to parse or even have access to the source code.

In the same way that the standard sources field is a list of source URLs and sourcesContent is a list of (optional) source code strings, x_facebook_sources is a list of optional metadata tuples. The i-th metadata tuple (x_facebook_sources[i]) corresponds to the source file whose URL is sources[i].

In nested (indexed) source maps, x_facebook_sources may appear as part of any nested source map in sections that itself has a sources field.

If present, x_facebook_sources may be a different length than sources (but usually shouldn't be). In particular, if it's shorter than sources, x_facebook_sources interpreted as if it were padded with null values to match the length of sources.

info

If you are writing a tool that processes source maps generated by Metro, and want to generate a new source map containing a valid x_facebook_sources field, you'll mainly need to ensure that x_facebook_sources[i] still corresponds to sources[i] in the output - even if your tool reorders, adds or deletes elements in sources. Notably, this can be done without parsing/decoding the metadata tuples: unless your tool actively needs to access the information within them, you can treat them as opaque blobs of JSON.

If a tool cannot guarantee that the sources and x_facebook_sources arrays will stay in sync, it should delete the x_facebook_sources field from its output.

Metadata tuple​

Each metadata tuple is encoded as an array of zero or more entries. Each entry may be null to signify that it's missing. A run of trailing nulls may be truncated from the end of the tuple with no change in meaning. The metadata tuple itself may also be null to signify that the source file has no associated metadata.

The indices in each metadata tuple are assigned as follows:

Function map​

A function map is encoded as an object with the following two fields:

  • names: An array of strings.
  • mappings: A string following the encoding described below.

When decoded, mappings represents a list of 3-tuples of integers: (column, nameIndex, line), (column, nameIndex, line), .... The list is ordered by line and then column.

The presence of a 3-tuple (column, nameIndex, line) means that the local function name in the code region beginning at line and column (in the source file described by the current metadata tuple) is names[nameIndex].

Function map mappings field encoding​

The value of the mappings field is described by the Mappings production of the grammar detailed below.

  1. Mappings = [ ";" ] LineMappings { ";" { ";" } LineMappings }
  2. LineMappings = FirstColumnMapping "," ColumnMapping
  3. FirstColumnMapping = VLQ VLQ VLQ
  4. ColumnMapping = VLQ VLQ [ VLQ ]
  5. VLQ = A single Base64-encoded variable-length quantity, as defined in the source map specification.
note

The above grammar uses the following BNF-like notation:

NotationMeaning
[ X ]X appears zero or 1 times.
{ X }X appears 0 or more times.
"foo"The literal characters foo.

The three VLQs in FirstColumnMapping or ColumnMapping represent, in this order:

  1. Column delta:
    • In FirstColumnMapping: The column offset from the beginning of the line. (0 = first column)
    • In ColumnMapping: The column offset from the last-encountered FirstColumnMapping or ColumnMapping.
  2. Name delta: The name index offset from the last-encountered FirstColumnMapping or ColumnMapping. This is not reset between lines.
  3. Line delta: The line offset from the last-encountered FirstColumnMapping or ColumnMapping. This is not reset between lines.
    • This MUST be 0 (Base64 VLQ: A) if it is part of a ColumnMapping.
    • Implementations SHOULD omit this field from the encoded form of ColumnMapping.

Example​

Given a single source file called file.js, a complete source map might look like this:

note

Comments are for illustrative purposes - the source map format does not allow comments.

{
"version": 3,
"sources": ["file.js"],
"sourcesContent": ["function a(){} function b(){}"],
"mappings": "AAAA", // NOTE: Simplified
"x_facebook_sources": [
// Metadata tuple for source #0 (file.js)
[
// Metadata item #0.0 = function map for source #0 (file.js)
{
// a from 1:0
// <global> from 1:14
// b from 1:15
// (See detailed decoding procedure below.)
"mappings": "AAA,cC,CC",
"names": [
"a",
"<global>",
"b",
]
}
]
]
}

The decoding procedure for the function map in the above example is illustrated by the following code:

const decoded = [];
const names = ['a', '<global>', 'b']; // From the function map

let column = 0, nameIndex = 0, line = 1;
column += 0 /* A */; nameIndex += 0 /* A */; line += 0 /* A */;
decoded.push({column, name: names[nameIndex] /* 'a' */, line});

column += 14 /* c */; nameIndex += 1 /* C */; // no line delta
decoded.push({column, name: names[nameIndex] /* '<global>' */, line});

column += 1 /* C */; nameIndex += 1 /* C */; // no line delta
decoded.push({column, name: names[nameIndex] /* 'b' */, line});

/*
decoded = [
{column: 0, name: 'a', line: 1},
{column: 14, name: '<global>', line: 1},
{column: 15, name: 'b', line: 1},
]
*/

x_google_ignoreList​

Metro's source maps include the x_google_ignoreList field by default. The serializer.isThirdPartyModule option can be used to control which modules are ignore-listed.

+ \ No newline at end of file diff --git a/docs/troubleshooting/index.html b/docs/troubleshooting/index.html index 5c63625296..b1a3efd9f8 100644 --- a/docs/troubleshooting/index.html +++ b/docs/troubleshooting/index.html @@ -13,15 +13,15 @@ - +
-

Troubleshooting

Uh oh, something went wrong? Use this guide to resolve issues with Metro.

  1. Clear Watchman watches: watchman watch-del-all
  2. Delete node_modules and run yarn install
  3. Reset Metro's cache by passing the --reset-cache flag, or adding resetCache: true to your Metro configuration file.
  4. Remove the cache: rm -rf ${TMPDIR:-/tmp}/metro-*
  5. Update Metro to the latest version

Still unresolved?​

See the Help pages.

- +

Troubleshooting

Uh oh, something went wrong? Use this guide to resolve issues with Metro.

  1. Clear Watchman watches: watchman watch-del-all
  2. Delete node_modules and run yarn install
  3. Reset Metro's cache by passing the --reset-cache flag, or adding resetCache: true to your Metro configuration file.
  4. Remove the cache: rm -rf ${TMPDIR:-/tmp}/metro-*
  5. Update Metro to the latest version

Still unresolved?​

See the Help pages.

+ \ No newline at end of file diff --git a/help/index.html b/help/index.html index 439a03cb6c..1dcb33d380 100644 --- a/help/index.html +++ b/help/index.html @@ -13,7 +13,7 @@ - + @@ -21,7 +21,7 @@

Need help?

Metro is worked on by Facebook's React Native team. Team members are often around and available for questions.

Browse the docs

Find what you're looking for in our detailed documentation and guides

Join the community

Ask questions and find answers from other Metro users like you.

Stay up to date

Find out what's new with Metro.

- + \ No newline at end of file diff --git a/index.html b/index.html index 5560a65dc3..dc2843b792 100644 --- a/index.html +++ b/index.html @@ -13,7 +13,7 @@ - + @@ -21,7 +21,7 @@
Metro

Metro

πŸš‡ The JavaScript bundler for React Native

Star

Check it out in the intro video

Fast

Metro aims for sub-second reload cycles, fast startup and quick bundling speeds.

Scalable

Works with thousands of modules in a single application.

Integrated

Supports every React Native project out of the box.

- + \ No newline at end of file diff --git a/search/index.html b/search/index.html index 4f7f305f04..ab61db1ae0 100644 --- a/search/index.html +++ b/search/index.html @@ -13,7 +13,7 @@ - + @@ -21,7 +21,7 @@ - + \ No newline at end of file