From 14459d047f148051eadb88c6d9200241b92e1539 Mon Sep 17 00:00:00 2001 From: Peter Abbondanzo Date: Fri, 6 Sep 2024 11:05:17 -0700 Subject: [PATCH] Load XML files on disk by default (#46371) Summary: X-link: https://github.com/facebook/metro/pull/1348 Pull Request resolved: https://github.com/facebook/react-native/pull/46371 ## Internal Vector drawable image support was added in D59530172 but importing vector drawable asset types was not supported out of the box. It required custom source transformers like the one added in D60021474. This is because Android cannot load vector drawable XML over the network. Vector drawables are compiled by AAPT as part of the build process. Even though Metro can serve XML, it would never load. ## Summary This adds some minor checks in the `AssetSourceResolver` to only attempt loading XML asset types from disk on the Android platform. XML assets like vector drawables are precompiled and cannot be served over the network by Metro. ## Changelog [Android] [Added] - Adds support for importing XML assets as images Differential Revision: D62302929 --- packages/assets/path-support.js | 2 +- .../Libraries/Image/AssetSourceResolver.js | 14 ++++++++++- .../__tests__/resolveAssetSource-test.js | 23 ++++++++++++++++++ .../js/examples/Image/ImageExample.js | 24 +++++++++++-------- 4 files changed, 51 insertions(+), 12 deletions(-) diff --git a/packages/assets/path-support.js b/packages/assets/path-support.js index 7d9388ad12567b..4ea50760fcd070 100644 --- a/packages/assets/path-support.js +++ b/packages/assets/path-support.js @@ -78,7 +78,7 @@ function getAndroidResourceIdentifier(asset: PackagerAsset): string { .toLowerCase() .replace(/\//g, '_') // Encode folder structure in file name .replace(/([^a-z0-9_])/g, '') // Remove illegal chars - .replace(/^assets_/, ''); // Remove "assets_" prefix + .replace(/^(?:assets|assetsunstable_path)_/, ''); // Remove "assets_" or "assetsunstable_path_" prefix } function getBasePath(asset: PackagerAsset): string { diff --git a/packages/react-native/Libraries/Image/AssetSourceResolver.js b/packages/react-native/Libraries/Image/AssetSourceResolver.js index e279212b49e5e5..d3af5d5a64b313 100644 --- a/packages/react-native/Libraries/Image/AssetSourceResolver.js +++ b/packages/react-native/Libraries/Image/AssetSourceResolver.js @@ -53,6 +53,14 @@ function getAssetPathInDrawableFolder(asset: PackagerAsset): string { return drawableFolder + '/' + fileName + '.' + asset.type; } +/** + * Returns true if the asset should only be loaded from the file system since + * it cannot be served over the network. + */ +function shouldAssetOnlyLoadFromFileSystem(asset: PackagerAsset): boolean { + return asset.type === 'xml' && Platform.OS === 'android'; +} + class AssetSourceResolver { serverUrl: ?string; // where the jsbundle is being run from @@ -67,7 +75,11 @@ class AssetSourceResolver { } isLoadedFromServer(): boolean { - return !!this.serverUrl; + return ( + this.serverUrl != null && + this.serverUrl !== '' && + !shouldAssetOnlyLoadFromFileSystem(this.asset) + ); } isLoadedFromFileSystem(): boolean { diff --git a/packages/react-native/Libraries/Image/__tests__/resolveAssetSource-test.js b/packages/react-native/Libraries/Image/__tests__/resolveAssetSource-test.js index 3058f7154048ce..e924b3dc61c38f 100644 --- a/packages/react-native/Libraries/Image/__tests__/resolveAssetSource-test.js +++ b/packages/react-native/Libraries/Image/__tests__/resolveAssetSource-test.js @@ -109,6 +109,29 @@ describe('resolveAssetSource', () => { }, ); }); + + it('respects query parameters', () => { + expectResolvesAsset( + { + __packager_asset: true, + fileSystemLocation: '/root/app/assets/module/a', + httpServerLocation: '/assets?unstable_path=./module/a', + width: 100, + height: 200, + scales: [1, 2, 3], + hash: '5b6f00f', + name: 'logo', + type: 'png', + }, + { + __packager_asset: true, + width: 100, + height: 200, + uri: 'http://10.0.0.1:8081/assets?unstable_path=./module/a/logo@2x.png?platform=ios&hash=5b6f00f', + scale: 2, + }, + ); + }); }); describe('bundle was loaded from file on iOS', () => { diff --git a/packages/rn-tester/js/examples/Image/ImageExample.js b/packages/rn-tester/js/examples/Image/ImageExample.js index 610b84fc5b4385..13656e1aee8603 100644 --- a/packages/rn-tester/js/examples/Image/ImageExample.js +++ b/packages/rn-tester/js/examples/Image/ImageExample.js @@ -14,15 +14,9 @@ import type {LayoutEvent} from 'react-native/Libraries/Types/CoreEventTypes'; import * as ReactNativeFeatureFlags from 'react-native/src/private/featureflags/ReactNativeFeatureFlags'; -const ImageCapInsetsExample = require('./ImageCapInsetsExample'); -const React = require('react'); -const { - Image, - ImageBackground, - StyleSheet, - Text, - View, -} = require('react-native'); +import ImageCapInsetsExample from './ImageCapInsetsExample'; +import React from 'react'; +import {Image, ImageBackground, StyleSheet, Text, View} from 'react-native'; const IMAGE1 = 'https://www.facebook.com/assets/fb_lite_messaging/E2EE-settings@3x.png'; @@ -618,7 +612,9 @@ class VectorDrawableExample extends React.Component< return ( Enabled: {isEnabled ? 'true' : 'false'} - + + + ); } @@ -849,6 +845,14 @@ const styles = StyleSheet.create({ experimental_boxShadow: '80px 0px 10px 0px hotpink', transform: 'rotate(-15deg)', }, + vectorDrawableRow: { + flexDirection: 'row', + gap: 8, + }, + vectorDrawable: { + height: 64, + width: 64, + }, }); exports.displayName = (undefined: ?string);