Skip to content

Commit

Permalink
Allow to chain custom transformers when resolving asset sources
Browse files Browse the repository at this point in the history
Summary:
## Changelog:
[Internal]-

This adds an ability to have multiple custom source transformers when resolving asset sources, which is beneficial in some scenarios.

The transformers are chained, being executed in order they are registered, until one of them returns a non-null value.

If none does, then the default one is returned.

Differential Revision: D53472320
  • Loading branch information
rshest authored and facebook-github-bot committed Feb 6, 2024
1 parent 9ba56f6 commit fcfd82e
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ describe('resolveAssetSource', () => {
Platform.OS = 'android';
});

it('uses bundled source, event when js is sideloaded', () => {
it('uses bundled source, even when js is sideloaded', () => {
resolveAssetSource.setCustomSourceTransformer(resolver =>
resolver.resourceIdentifierWithoutScale(),
);
Expand All @@ -319,6 +319,64 @@ describe('resolveAssetSource', () => {
);
});

it('can chain multiple custom source transformers', () => {
resolveAssetSource.addCustomSourceTransformer(resolver => {
if (resolver.asset.type === 'gif') {
return resolver.fromSource(`my_gif_file`);
}
return null;
});

resolveAssetSource.addCustomSourceTransformer(resolver => {
if (resolver.asset.type === 'png') {
return resolver.fromSource(`my_png_file`);
}
return null;
});

const pngAsset = {
__packager_asset: true,
fileSystemLocation: '/root/app/module/a',
httpServerLocation: '/assets/AwesomeModule/Subdir',
width: 100,
height: 200,
scales: [1],
hash: '5b6f00f',
name: '!@Logo#1_\u20ac',
type: 'png',
};

expectResolvesAsset(pngAsset, {
__packager_asset: true,
width: 100,
height: 200,
uri: 'my_png_file',
scale: 1,
});

expectResolvesAsset(
{...pngAsset, type: 'gif'},
{
__packager_asset: true,
width: 100,
height: 200,
uri: 'my_gif_file',
scale: 1,
},
);

expectResolvesAsset(
{...pngAsset, type: 'jpg'},
{
__packager_asset: true,
width: 100,
height: 200,
uri: 'file:///sdcard/Path/To/Simulator/drawable-mdpi/awesomemodule_subdir_logo1_.jpg',
scale: 1,
},
);
});

it('allows any customization', () => {
resolveAssetSource.setCustomSourceTransformer(resolver =>
resolver.fromSource('TEST'),
Expand Down
53 changes: 40 additions & 13 deletions packages/react-native/Libraries/Image/resolveAssetSource.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,25 @@
* @flow strict-local
*/

// Resolves an asset into a `source` for `Image`.

'use strict';
// Utilities for resolving an asset into a `source` for e.g. `Image`

import type {ResolvedAssetSource} from './AssetSourceResolver';
import type {ImageSource} from './ImageSource';

import SourceCode from '../NativeModules/specs/NativeSourceCode';
import AssetSourceResolver from './AssetSourceResolver';
import {pickScale} from './AssetUtils';
import {getAssetByID} from '@react-native/assets-registry/registry';

const AssetSourceResolver = require('./AssetSourceResolver');
const {pickScale} = require('./AssetUtils');
const AssetRegistry = require('@react-native/assets-registry/registry');

let _customSourceTransformer, _serverURL, _scriptURL;
type CustomSourceTransformer = (
resolver: AssetSourceResolver,
) => ?ResolvedAssetSource;

let _customSourceTransformers: Array<CustomSourceTransformer> = [];
let _serverURL: ?string;
let _scriptURL: ?string;
let _sourceCodeScriptURL: ?string;

function getSourceCodeScriptURL(): ?string {
if (_sourceCodeScriptURL != null) {
return _sourceCodeScriptURL;
Expand Down Expand Up @@ -77,10 +80,25 @@ function getScriptURL(): ?string {
return _scriptURL;
}

/**
* `transformer` can optionally be used to apply a custom transformation when
* resolving an asset source. This methods overrides all other custom transformers
* that may have been previously registered.
*/
function setCustomSourceTransformer(
transformer: (resolver: AssetSourceResolver) => ResolvedAssetSource,
transformer: CustomSourceTransformer,
): void {
_customSourceTransformer = transformer;
_customSourceTransformers = [transformer];
}

/**
* Adds a `transformer` into the chain of custom source transformers, which will
* be applied in the order registered, until one returns a non-null value.
*/
function addCustomSourceTransformer(
transformer: CustomSourceTransformer,
): void {
_customSourceTransformers.push(transformer);
}

/**
Expand All @@ -94,7 +112,7 @@ function resolveAssetSource(source: ?ImageSource): ?ResolvedAssetSource {
return source;
}

const asset = AssetRegistry.getAssetByID(source);
const asset = getAssetByID(source);
if (!asset) {
return null;
}
Expand All @@ -104,12 +122,21 @@ function resolveAssetSource(source: ?ImageSource): ?ResolvedAssetSource {
getScriptURL(),
asset,
);
if (_customSourceTransformer) {
return _customSourceTransformer(resolver);

// Apply (chained) custom source transformers, if any
if (_customSourceTransformers) {
for (const customSourceTransformer of _customSourceTransformers) {
const transformedSource = customSourceTransformer(resolver);
if (transformedSource != null) {
return transformedSource;
}
}
}

return resolver.defaultAsset();
}

resolveAssetSource.pickScale = pickScale;
resolveAssetSource.setCustomSourceTransformer = setCustomSourceTransformer;
resolveAssetSource.addCustomSourceTransformer = addCustomSourceTransformer;
module.exports = resolveAssetSource;

0 comments on commit fcfd82e

Please sign in to comment.