Skip to content

Commit df9c55f

Browse files
committed
restore defaults
1 parent b80ccd3 commit df9c55f

File tree

7 files changed

+54
-68
lines changed

7 files changed

+54
-68
lines changed

.changeset/busy-ads-rescue.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,18 @@
33
"@rnx-kit/metro-service": patch
44
---
55

6-
Fixed assets outside of project root not rendering
6+
Fixed assets outside of project root not rendering. This breaks release builds
7+
so it must be manually enabled in `metro.config.js`:
8+
9+
```diff
10+
diff --git a/metro.config.js b/metro.config.js
11+
index 000000000..000000000 100644
12+
--- a/metro.config.js
13+
+++ b/metro.config.js
14+
@@ -34,4 +34,5 @@ module.exports = makeMetroConfig({
15+
blacklistRE: blockList,
16+
blockList,
17+
},
18+
+ unstable_allowAssetsOutsideProjectRoot: true,
19+
});
20+
```
Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,46 @@
11
/**
22
* @typedef {import("metro").AssetData} AssetData;
3-
* @typedef {import("metro-config").ConfigT} ConfigT;
4-
* @typedef {import("metro-config").Middleware} Middleware;
5-
*
6-
* @typedef {import("metro/private/Server").default & {
7-
* _config?: ConfigT;
8-
* }} Server;
93
*/
104

5+
/**
6+
* @param {string} str
7+
* @param {string} searchValue
8+
* @param {string} replaceValue
9+
* @returns {string}
10+
*/
11+
function replaceString(str, searchValue, replaceValue) {
12+
return str.startsWith("/assets/")
13+
? str.replaceAll(searchValue, replaceValue)
14+
: str;
15+
}
16+
1117
/**
1218
* Metro doesn't support assets in a monorepo setup. When the app requests
1319
* assets at URLs such as `/assets/../../../node_modules/react-native/<...>`,
1420
* the URL will be resolved to `/node_modules/react-native/<...>` and Metro will
1521
* fail to resolve them. The workaround is to replace `..` with something else
1622
* so the URL doesn't collapse when resolved, then restore them in
17-
* `server.enhanceMiddleware`.
23+
* `server.rewriteRequestUrl`.
1824
*
1925
* For more details, see https://github.com/facebook/metro/issues/290.
2026
*
2127
* @param {import("type-fest").Writable<AssetData>} assetData
2228
* @returns {AssetData}
2329
*/
2430
function assetPlugin(assetData) {
25-
assetData.httpServerLocation = assetData.httpServerLocation.replaceAll(
26-
"../",
27-
"@@/"
28-
);
31+
const url = assetData.httpServerLocation;
32+
assetData.httpServerLocation = replaceString(url, "../", "@@/");
2933
return assetData;
3034
}
3135

3236
/**
33-
* This middleware restores `..` in asset URLs.
34-
*
35-
* @param {Middleware} middleware
36-
* @param {Server} _server
37-
* @returns {import("connect").NextHandleFunction}
37+
* Restores `..` in asset URLs.
38+
* @param {string} url
39+
* @returns {string}
3840
*/
39-
function enhanceMiddleware(middleware, _server) {
40-
return (req, res, next) => {
41-
const { url } = req;
42-
if (url && url.startsWith("/assets/")) {
43-
req.url = url.replaceAll("@@/", "../");
44-
}
45-
return middleware(req, res, next);
46-
};
41+
function restoreAssetURL(url) {
42+
return replaceString(url, "@@/", "../");
4743
}
4844

4945
module.exports = assetPlugin;
50-
module.exports.enhanceMiddleware = enhanceMiddleware;
46+
module.exports.restoreAssetURL = restoreAssetURL;

packages/metro-config/src/assetPlugins/rewriteAssetURLs.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
* fail to resolve them.
1010
*
1111
* In 0.67, they introduced a query parameter, `unstable_path`, specifically to
12-
* handle such cases.
12+
* handle such cases. However, this isn't properly fleshed out and only works in
13+
* server mode. Bundling with this plugin may cause issues with asset paths.
1314
*
1415
* For more details, see https://github.com/facebook/metro/issues/290.
1516
*

packages/metro-config/src/index.js

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
const {
55
findMetroPath,
6-
getMetroVersion,
76
requireModuleFromMetro,
87
} = require("@rnx-kit/tools-react-native/metro");
98
const fs = require("node:fs");
@@ -12,7 +11,10 @@ const { applyExpoWorkarounds, isExpoConfig } = require("./expoConfig");
1211

1312
/**
1413
* @typedef {import("metro-config").MetroConfig} MetroConfig;
15-
* @typedef {MetroConfig & { platform?: string; }} InputConfig;
14+
* @typedef {MetroConfig & {
15+
* platform?: string;
16+
* unstable_allowAssetsOutsideProjectRoot?: boolean;
17+
* }} InputConfig;
1618
*/
1719

1820
/** Packages that must be resolved to one specific copy. */
@@ -281,18 +283,6 @@ function exclusionList(additionalExclusions = [], projectRoot = process.cwd()) {
281283
];
282284
}
283285

284-
/**
285-
* Returns whether Metro supports the `unstable_path` query parameter.
286-
* @param {string} projectRoot
287-
* @returns {boolean}
288-
*/
289-
function supportsAssetPathQueryParam(projectRoot) {
290-
// https://github.com/facebook/metro/commit/f3d1157bacc341dff82efea2f70b634141105fc0
291-
const version = getMetroVersion(projectRoot) || "0.0.0";
292-
const [major = 0, minor = 0] = version.split(".");
293-
return Number(major) * 1000 + Number(minor) >= 67;
294-
}
295-
296286
/**
297287
* @param {string} projectRoot
298288
* @param {InputConfig} inputConfig
@@ -322,13 +312,16 @@ function additionalConfig(projectRoot, inputConfig) {
322312
watchFolders: inputConfig.watchFolders ?? defaultWatchFolders(),
323313
};
324314

325-
if (supportsAssetPathQueryParam(projectRoot)) {
315+
if (inputConfig.unstable_allowAssetsOutsideProjectRoot) {
316+
// We currently cannot enable this by default because it affects the
317+
// structure of the assets folder, breaking release builds.
326318
transformer.assetPlugins = [
327319
require.resolve("./assetPlugins/rewriteAssetURLs.js"),
328320
];
329321
} else {
330-
const { enhanceMiddleware } = require("./assetPlugins/escapeAssetURLs.js");
331-
config.server = { enhanceMiddleware };
322+
const { restoreAssetURL } = require("./assetPlugins/escapeAssetURLs.js");
323+
config.server = { rewriteRequestUrl: restoreAssetURL };
324+
332325
transformer.assetPlugins = [
333326
require.resolve("./assetPlugins/escapeAssetURLs.js"),
334327
];
Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
import type { AssetData } from "metro";
2-
import type { Middleware } from "metro-config";
3-
import type Server from "metro/private/Server";
42
import { equal } from "node:assert/strict";
5-
import type { IncomingMessage, ServerResponse } from "node:http";
63
import { describe, it } from "node:test";
74
import {
8-
enhanceMiddleware,
95
default as escapeAssetURL,
6+
restoreAssetURL,
107
} from "../../src/assetPlugins/escapeAssetURLs.js";
118

12-
describe("assetPluginForMonorepos/escapeRelativePaths", () => {
9+
describe("assetPlugins/escapeRelativePaths", () => {
1310
const cases = [
1411
["/assets/./node_modules", "/assets/./node_modules"],
1512
["/assets/../node_modules", "/assets/@@/node_modules"],
@@ -29,22 +26,7 @@ describe("assetPluginForMonorepos/escapeRelativePaths", () => {
2926

3027
it("unescapes `..` in URLs", () => {
3128
for (const [output, input] of cases) {
32-
const middleware: Middleware = (req: Middleware) => {
33-
equal("url" in req && req.url, output);
34-
return middleware;
35-
};
36-
const server = {
37-
_config: { transformer: { assetPlugins: [] } },
38-
} as unknown as Server;
39-
40-
const incoming = { url: input } as IncomingMessage;
41-
const response = {} as ServerResponse;
42-
43-
enhanceMiddleware(middleware, server)(
44-
incoming,
45-
response,
46-
() => undefined
47-
);
29+
equal(restoreAssetURL(input), output);
4830
}
4931
});
5032
});

packages/metro-config/test/assetPlugins/rewriteAssetURLs.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { deepEqual } from "node:assert/strict";
33
import { describe, it } from "node:test";
44
import rewriteRequest from "../../src/assetPlugins/rewriteAssetURLs.js";
55

6-
describe("assetPluginForMonorepos/rewriteRequestWithQueryParam", () => {
6+
describe("assetPlugins/rewriteRequestWithQueryParam", () => {
77
const cases = [
88
["/assets/./node_modules", "/assets/./node_modules"],
99
["/assets/../node_modules", "/assets?unstable_path=../node_modules"],

packages/metro-config/test/index.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,7 @@ describe("makeMetroConfig()", () => {
420420
deepEqual(config.resolver.blockList, blockList);
421421

422422
deepEqual(config.transformer.assetPlugins, [
423-
require.resolve("../src/assetPlugins/rewriteAssetURLs.js"),
423+
require.resolve("../src/assetPlugins/escapeAssetURLs.js"),
424424
]);
425425

426426
const opts = { dev: false, hot: true, platform: undefined } as const;
@@ -494,7 +494,7 @@ describe("makeMetroConfig()", () => {
494494
deepEqual(config.resolver.blockList, blockList);
495495

496496
deepEqual(config.transformer.assetPlugins, [
497-
require.resolve("../src/assetPlugins/rewriteAssetURLs.js"),
497+
require.resolve("../src/assetPlugins/escapeAssetURLs.js"),
498498
]);
499499

500500
const opts = { dev: false, hot: true, platform: undefined } as const;

0 commit comments

Comments
 (0)