From e3c7f0238d493b2daa61b40d6b279c9f42924138 Mon Sep 17 00:00:00 2001 From: Eric Rozell Date: Fri, 13 Oct 2023 20:42:20 -0400 Subject: [PATCH] Fixes issues with packaged asset resolution (#10621) * Fixes issues with packaged asset resolution `Image.resolveAssetSource` has two different code paths - one for resolving assets from the packager server when running in standard debug builds and one for resolving assets when the JS package is pre-bundled. Previously, we were not providing the bundle root path to the `SourceCodeModule` for pre-bundled apps, and it was up to each individual native module and view manager that depends on resolving asset paths with `Image.resolveAssetSource` to replace `file://` with the bundle root path pulled from the InstanceSettingsSnapshot. Even after fixing the issue with `SourceCodeModule`, there is still one more issue for unpackaged apps, whose asset paths may be Windows file paths (e.g., `C:\...`) rather than packaged asset paths (e.g., `ms-appx://`). This change ensures that SourceCodeModule provides the bundle root path as the `scriptURL` constant and patches the bug in the upstream `resolveAssetSource` module to skip coersion of the bundle root path to a form like `file://`. Resolves #10620 * Change files --- ...-06a66b00-cc43-451b-9087-1b0c52b44ab4.json | 7 ++ vnext/.flowconfig | 1 + .../Microsoft.ReactNative/Utils/ImageUtils.h | 1 - .../Views/Image/ImageViewManager.cpp | 5 - vnext/Shared/OInstance.cpp | 2 +- vnext/overrides.json | 7 ++ .../Image/resolveAssetSource.windows.js | 115 ++++++++++++++++++ 7 files changed, 131 insertions(+), 7 deletions(-) create mode 100644 change/react-native-windows-06a66b00-cc43-451b-9087-1b0c52b44ab4.json create mode 100644 vnext/src/Libraries/Image/resolveAssetSource.windows.js diff --git a/change/react-native-windows-06a66b00-cc43-451b-9087-1b0c52b44ab4.json b/change/react-native-windows-06a66b00-cc43-451b-9087-1b0c52b44ab4.json new file mode 100644 index 00000000000..a25bc127242 --- /dev/null +++ b/change/react-native-windows-06a66b00-cc43-451b-9087-1b0c52b44ab4.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "[PR] Fixes issues with packaged asset resolution (#10621)", + "packageName": "react-native-windows", + "email": "erozell@outlook.com", + "dependentChangeType": "patch" +} diff --git a/vnext/.flowconfig b/vnext/.flowconfig index 23d800ae08a..fefc1ef30db 100644 --- a/vnext/.flowconfig +++ b/vnext/.flowconfig @@ -28,6 +28,7 @@ /Libraries/Components/View/View.js /Libraries/DeprecatedPropTypes/DeprecatedViewAccessibility.js /Libraries/Image/Image.js +/Libraries/Image/resolveAssetSource.js /Libraries/Network/RCTNetworking.js /Libraries/NewAppScreen/components/DebugInstructions.js /Libraries/NewAppScreen/components/ReloadInstructions.js diff --git a/vnext/Microsoft.ReactNative/Utils/ImageUtils.h b/vnext/Microsoft.ReactNative/Utils/ImageUtils.h index e3b67e3cd0d..08a5cb1ae35 100644 --- a/vnext/Microsoft.ReactNative/Utils/ImageUtils.h +++ b/vnext/Microsoft.ReactNative/Utils/ImageUtils.h @@ -13,7 +13,6 @@ enum class ImageSourceFormat { Bitmap = 0, Svg = 1 }; struct ReactImageSource { std::string uri; std::string method; - std::string bundleRootPath; std::vector> headers; double width = 0; double height = 0; diff --git a/vnext/Microsoft.ReactNative/Views/Image/ImageViewManager.cpp b/vnext/Microsoft.ReactNative/Views/Image/ImageViewManager.cpp index a75037d6fd1..6aa6cceb0d5 100644 --- a/vnext/Microsoft.ReactNative/Views/Image/ImageViewManager.cpp +++ b/vnext/Microsoft.ReactNative/Views/Image/ImageViewManager.cpp @@ -185,11 +185,6 @@ void ImageViewManager::EmitImageEvent( void ImageViewManager::setSource(winrt::Grid grid, const winrt::Microsoft::ReactNative::JSValue &data) { auto sources{json_type_traits>::parseJson(data)}; - sources[0].bundleRootPath = GetReactContext().SettingsSnapshot().BundleRootPath(); - - if (sources[0].packagerAsset && sources[0].uri.find("file://") == 0) { - sources[0].uri.replace(0, 7, sources[0].bundleRootPath); - } auto reactImage{grid.as()}; diff --git a/vnext/Shared/OInstance.cpp b/vnext/Shared/OInstance.cpp index e146b72ff21..1ca6a99c528 100644 --- a/vnext/Shared/OInstance.cpp +++ b/vnext/Shared/OInstance.cpp @@ -597,7 +597,7 @@ std::vector> InstanceImpl::GetDefaultNativeModules m_devSettings->useFastRefresh, m_devSettings->inlineSourceMap, hermesBytecodeVersion) - : std::string(); + : m_devSettings->bundleRootPath; modules.push_back(std::make_unique( m_innerInstance, facebook::react::SourceCodeModule::Name, diff --git a/vnext/overrides.json b/vnext/overrides.json index 27ce337e8a7..dcd55a5c3af 100644 --- a/vnext/overrides.json +++ b/vnext/overrides.json @@ -440,6 +440,13 @@ "baseHash": "ead4aaa048a5ae0186d3f8ade054b970a2115a3a", "issue": 4590 }, + { + "type": "patch", + "file": "src/Libraries/Image/resolveAssetSource.windows.js", + "baseFile": "packages/react-native/Libraries/Image/resolveAssetSource.js", + "baseHash": "594d34126769664b671f742d0b73376de6507a04", + "issue": 10619 + }, { "type": "derived", "file": "src/Libraries/LogBox/UI/LogBoxInspectorCodeFrame.windows.js", diff --git a/vnext/src/Libraries/Image/resolveAssetSource.windows.js b/vnext/src/Libraries/Image/resolveAssetSource.windows.js new file mode 100644 index 00000000000..080dd9593fc --- /dev/null +++ b/vnext/src/Libraries/Image/resolveAssetSource.windows.js @@ -0,0 +1,115 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @flow + */ + +// Resolves an asset into a `source` for `Image`. + +'use strict'; + +import type {ResolvedAssetSource} from './AssetSourceResolver'; + +const AssetSourceResolver = require('./AssetSourceResolver'); +const Platform = require('../Utilities/Platform'); +const {pickScale} = require('./AssetUtils'); +const AssetRegistry = require('@react-native/assets-registry/registry'); + +let _customSourceTransformer, _serverURL, _scriptURL; + +let _sourceCodeScriptURL: ?string; +function getSourceCodeScriptURL(): ?string { + if (_sourceCodeScriptURL) { + return _sourceCodeScriptURL; + } + + let sourceCode = + global.nativeExtensions && global.nativeExtensions.SourceCode; + if (!sourceCode) { + sourceCode = require('../NativeModules/specs/NativeSourceCode').default; + } + _sourceCodeScriptURL = sourceCode.getConstants().scriptURL; + return _sourceCodeScriptURL; +} + +function getDevServerURL(): ?string { + if (_serverURL === undefined) { + const sourceCodeScriptURL = getSourceCodeScriptURL(); + const match = + sourceCodeScriptURL && sourceCodeScriptURL.match(/^https?:\/\/.*?\//); + if (match) { + // jsBundle was loaded from network + _serverURL = match[0]; + } else { + // jsBundle was loaded from file + _serverURL = null; + } + } + return _serverURL; +} + +function _coerceLocalScriptURL(scriptURL: ?string): ?string { + if (Platform.OS === 'windows') { + return scriptURL; + } + + if (scriptURL) { + if (scriptURL.startsWith('assets://')) { + // android: running from within assets, no offline path to use + return null; + } + scriptURL = scriptURL.substring(0, scriptURL.lastIndexOf('/') + 1); + if (!scriptURL.includes('://')) { + // Add file protocol in case we have an absolute file path and not a URL. + // This shouldn't really be necessary. scriptURL should be a URL. + scriptURL = 'file://' + scriptURL; + } + } + return scriptURL; +} + +function getScriptURL(): ?string { + if (_scriptURL === undefined) { + _scriptURL = _coerceLocalScriptURL(getSourceCodeScriptURL()); + } + return _scriptURL; +} + +function setCustomSourceTransformer( + transformer: (resolver: AssetSourceResolver) => ResolvedAssetSource, +): void { + _customSourceTransformer = transformer; +} + +/** + * `source` is either a number (opaque type returned by require('./foo.png')) + * or an `ImageSource` like { uri: '' } + */ +function resolveAssetSource(source: any): ?ResolvedAssetSource { + if (typeof source === 'object') { + return source; + } + + const asset = AssetRegistry.getAssetByID(source); + if (!asset) { + return null; + } + + const resolver = new AssetSourceResolver( + getDevServerURL(), + getScriptURL(), + asset, + ); + if (_customSourceTransformer) { + return _customSourceTransformer(resolver); + } + return resolver.defaultAsset(); +} + +resolveAssetSource.pickScale = pickScale; +resolveAssetSource.setCustomSourceTransformer = setCustomSourceTransformer; +module.exports = resolveAssetSource;