diff --git a/Libraries/Image/__tests__/resolveAssetSource-test.js b/Libraries/Image/__tests__/resolveAssetSource-test.js index c5fc3bbe1..866cf0368 100644 --- a/Libraries/Image/__tests__/resolveAssetSource-test.js +++ b/Libraries/Image/__tests__/resolveAssetSource-test.js @@ -12,9 +12,10 @@ jest .dontMock('AssetRegistry') .dontMock('../resolveAssetSource'); -var resolveAssetSource; -var SourceCode; var AssetRegistry; +var Platform; +var SourceCode; +var resolveAssetSource; function expectResolvesAsset(input, expectedSource) { var assetId = AssetRegistry.registerAsset(input); @@ -24,8 +25,9 @@ function expectResolvesAsset(input, expectedSource) { describe('resolveAssetSource', () => { beforeEach(() => { jest.resetModuleRegistry(); - SourceCode = require('NativeModules').SourceCode; AssetRegistry = require('AssetRegistry'); + Platform = require('Platform'); + SourceCode = require('NativeModules').SourceCode; resolveAssetSource = require('../resolveAssetSource'); }); @@ -59,7 +61,7 @@ describe('resolveAssetSource', () => { expect(resolveAssetSource('nonsense')).toBe(null); }); - describe('bundle was loaded from network', () => { + describe('bundle was loaded from network (DEV)', () => { beforeEach(() => { SourceCode.scriptURL = 'http://10.0.0.1:8081/main.bundle'; }); @@ -104,9 +106,21 @@ describe('resolveAssetSource', () => { }); - describe('bundle was loaded from file', () => { + describe('bundle was loaded from file (PROD) on iOS', () => { + var originalDevMode; + var originalPlatform; + beforeEach(() => { SourceCode.scriptURL = 'file:///Path/To/Simulator/main.bundle'; + originalDevMode = __DEV__; + originalPlatform = Platform.OS; + __DEV__ = false; + Platform.OS = 'ios'; + }); + + afterEach(() => { + __DEV__ = originalDevMode; + Platform.OS = originalPlatform; }); it('uses pre-packed image', () => { @@ -129,6 +143,43 @@ describe('resolveAssetSource', () => { }); }); + describe('bundle was loaded from file (PROD) on Android', () => { + var originalDevMode; + var originalPlatform; + + beforeEach(() => { + SourceCode.scriptURL = 'file:///Path/To/Simulator/main.bundle'; + originalDevMode = __DEV__; + originalPlatform = Platform.OS; + __DEV__ = false; + Platform.OS = 'android'; + }); + + afterEach(() => { + __DEV__ = originalDevMode; + Platform.OS = originalPlatform; + }); + + it('uses pre-packed image', () => { + expectResolvesAsset({ + __packager_asset: true, + fileSystemLocation: '/root/app/module/a', + httpServerLocation: '/assets/AwesomeModule/Subdir', + width: 100, + height: 200, + scales: [1], + hash: '5b6f00f', + name: '!@Logo#1_€', // Invalid chars shouldn't get passed to native + type: 'png', + }, { + isStatic: true, + width: 100, + height: 200, + uri: 'assets_awesomemodule_subdir_logo1_', + }); + }); + }); + }); describe('resolveAssetSource.pickScale', () => { diff --git a/Libraries/Image/resolveAssetSource.js b/Libraries/Image/resolveAssetSource.js index 29d59a9a5..26592195d 100644 --- a/Libraries/Image/resolveAssetSource.js +++ b/Libraries/Image/resolveAssetSource.js @@ -7,22 +7,31 @@ * of patent rights can be found in the PATENTS file in the same directory. * * @providesModule resolveAssetSource + * + * Resolves an asset into a `source` for `Image`. */ 'use strict'; var AssetRegistry = require('AssetRegistry'); var PixelRatio = require('PixelRatio'); +var Platform = require('Platform'); var SourceCode = require('NativeModules').SourceCode; var _serverURL; -function getServerURL() { +function getDevServerURL() { + if (!__DEV__) { + // In prod we want assets to be loaded from the archive + return null; + } if (_serverURL === undefined) { var scriptURL = SourceCode.scriptURL; var match = scriptURL && scriptURL.match(/^https?:\/\/.*?\//); if (match) { + // Bundle was loaded from network _serverURL = match[0]; } else { + // Bundle was loaded from file _serverURL = null; } } @@ -30,6 +39,55 @@ function getServerURL() { return _serverURL; } +/** + * Returns the path at which the asset can be found in the archive + */ +function getPathInArchive(asset) { + if (Platform.OS === 'android') { + var assetDir = getBasePath(asset); + // E.g. 'assets_awesomemodule_icon' + // The Android resource system picks the correct scale. + return (assetDir + '/' + asset.name) + .toLowerCase() + .replace(/\//g, '_') // Encode folder structure in file name + .replace(/([^a-z0-9_])/g, ''); // Remove illegal chars + } else { + // E.g. 'assets/AwesomeModule/icon@2x.png' + return getScaledAssetPath(asset); + } +} + +/** + * Returns an absolute URL which can be used to fetch the asset + * from the devserver + */ +function getPathOnDevserver(devServerUrl, asset) { + return devServerUrl + getScaledAssetPath(asset) + '?hash=' + asset.hash; +} + +/** + * Returns a path like 'assets/AwesomeModule' + */ +function getBasePath(asset) { + // TODO(frantic): currently httpServerLocation is used both as + // path in http URL and path within IPA. Should we have zipArchiveLocation? + var path = asset.httpServerLocation; + if (path[0] === '/') { + path = path.substr(1); + } + return path; +} + +/** + * Returns a path like 'assets/AwesomeModule/icon@2x.png' + */ +function getScaledAssetPath(asset) { + var scale = pickScale(asset.scales, PixelRatio.get()); + var scaleSuffix = scale === 1 ? '' : '@' + scale + 'x'; + var assetDir = getBasePath(asset); + return assetDir + '/' + asset.name + scaleSuffix + '.' + asset.type; +} + function pickScale(scales, deviceScale) { // Packager guarantees that `scales` array is sorted for (var i = 0; i < scales.length; i++) { @@ -58,31 +116,19 @@ function resolveAssetSource(source) { } function assetToImageSource(asset) { - // TODO(frantic): currently httpServerLocation is used both as - // path in http URL and path within IPA. Should we have zipArchiveLocation? - var path = asset.httpServerLocation; - if (path[0] === '/') { - path = path.substr(1); - } - - var scale = pickScale(asset.scales, PixelRatio.get()); - var scaleSuffix = scale === 1 ? '' : '@' + scale + 'x'; - - var fileName = asset.name + scaleSuffix + '.' + asset.type; - var serverURL = getServerURL(); - if (serverURL) { + var devServerURL = getDevServerURL(); + if (devServerURL) { return { width: asset.width, height: asset.height, - uri: serverURL + path + '/' + fileName + - '?hash=' + asset.hash, + uri: getPathOnDevserver(devServerURL, asset), isStatic: false, }; } else { return { width: asset.width, height: asset.height, - uri: path + '/' + fileName, + uri: getPathInArchive(asset), isStatic: true, }; }