Skip to content

Commit

Permalink
Fixes issues with packaged asset resolution (#10621)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
rozele committed Oct 14, 2023
1 parent a974fa7 commit e3c7f02
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 7 deletions.
Original file line number Diff line number Diff line change
@@ -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"
}
1 change: 1 addition & 0 deletions vnext/.flowconfig
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
<PROJECT_ROOT>/Libraries/Components/View/View.js
<PROJECT_ROOT>/Libraries/DeprecatedPropTypes/DeprecatedViewAccessibility.js
<PROJECT_ROOT>/Libraries/Image/Image.js
<PROJECT_ROOT>/Libraries/Image/resolveAssetSource.js
<PROJECT_ROOT>/Libraries/Network/RCTNetworking.js
<PROJECT_ROOT>/Libraries/NewAppScreen/components/DebugInstructions.js
<PROJECT_ROOT>/Libraries/NewAppScreen/components/ReloadInstructions.js
Expand Down
1 change: 0 additions & 1 deletion vnext/Microsoft.ReactNative/Utils/ImageUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ enum class ImageSourceFormat { Bitmap = 0, Svg = 1 };
struct ReactImageSource {
std::string uri;
std::string method;
std::string bundleRootPath;
std::vector<std::pair<std::string, std::string>> headers;
double width = 0;
double height = 0;
Expand Down
5 changes: 0 additions & 5 deletions vnext/Microsoft.ReactNative/Views/Image/ImageViewManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -185,11 +185,6 @@ void ImageViewManager::EmitImageEvent(

void ImageViewManager::setSource(winrt::Grid grid, const winrt::Microsoft::ReactNative::JSValue &data) {
auto sources{json_type_traits<std::vector<ReactImageSource>>::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<ReactImage>()};

Expand Down
2 changes: 1 addition & 1 deletion vnext/Shared/OInstance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -597,7 +597,7 @@ std::vector<std::unique_ptr<NativeModule>> InstanceImpl::GetDefaultNativeModules
m_devSettings->useFastRefresh,
m_devSettings->inlineSourceMap,
hermesBytecodeVersion)
: std::string();
: m_devSettings->bundleRootPath;
modules.push_back(std::make_unique<CxxNativeModule>(
m_innerInstance,
facebook::react::SourceCodeModule::Name,
Expand Down
7 changes: 7 additions & 0 deletions vnext/overrides.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
115 changes: 115 additions & 0 deletions vnext/src/Libraries/Image/resolveAssetSource.windows.js
Original file line number Diff line number Diff line change
@@ -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: '<http location || file path>' }
*/
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;

0 comments on commit e3c7f02

Please sign in to comment.