Support for remote assets

Reviewed By: davidaurelio

Differential Revision: D6030886

fbshipit-source-id: c188b39e104f5c3f65a98336fecbffc93091ff4c
This commit is contained in:
Miguel Jimenez Esun 2017-10-19 18:38:10 -07:00 committed by Facebook Github Bot
parent 1a2a46a9fb
commit 0fdf36d06d
5 changed files with 167 additions and 17 deletions

View File

@ -89,9 +89,9 @@ type __TransformOptions = {
type _TransformOptions = type _TransformOptions =
__TransformOptions & {env?: {[key: string]: __TransformOptions}}; __TransformOptions & {env?: {[key: string]: __TransformOptions}};
declare class _Ast {} declare class Ast {}
type TransformResult = { type TransformResult = {
ast: _Ast, ast: Ast,
code: ?string, code: ?string,
ignored: boolean, ignored: boolean,
map: ?_SourceMap, map: ?_SourceMap,
@ -101,14 +101,14 @@ type VisitFn = <State>(path: Object, state: State) => any;
declare module 'babel-core' { declare module 'babel-core' {
declare type Plugins = _Plugins; declare type Plugins = _Plugins;
declare type SourceMap = _SourceMap; declare type SourceMap = _SourceMap;
declare type Ast = _Ast; declare type Ast = Ast;
declare type TransformOptions = _TransformOptions; declare type TransformOptions = _TransformOptions;
declare function transform( declare function transform(
code: string, code: string,
options?: _TransformOptions, options?: _TransformOptions,
): TransformResult; ): TransformResult;
declare function traverse<State>( declare function traverse<State>(
ast: _Ast, ast: Ast,
visitor: {[key: string]: VisitFn<State> | visitor: {[key: string]: VisitFn<State> |
{enter?: VisitFn<State>, exit?: VisitFn<State>}}, {enter?: VisitFn<State>, exit?: VisitFn<State>}},
scope?: ?Object, scope?: ?Object,
@ -117,7 +117,7 @@ declare module 'babel-core' {
): void; ): void;
declare var types: {[key: string]: Function}; declare var types: {[key: string]: Function};
declare function transformFromAst( declare function transformFromAst(
ast: _Ast, ast: Ast,
code?: ?string, code?: ?string,
babelOptions?: _TransformOptions, babelOptions?: _TransformOptions,
): TransformResult; ): TransformResult;
@ -133,7 +133,7 @@ type RawMapping = {
declare module 'babel-generator' { declare module 'babel-generator' {
declare type RawMapping = RawMapping; declare type RawMapping = RawMapping;
declare function exports( declare function exports(
ast: _Ast, ast: Ast,
options?: GeneratorOptions, options?: GeneratorOptions,
): TransformResult & {rawMappings: ?Array<RawMapping>}; ): TransformResult & {rawMappings: ?Array<RawMapping>};
} }

View File

@ -0,0 +1,52 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails oncall+javascript_foundation
* @format
*/
'use strict';
const generator = require('babel-generator').default;
const util = require('../util');
it('generates the right AST for remote assets', () => {
const asset = {
__packager_asset: true,
fileSystemLocation: '/js/RKJSModules/Apps/Wilde/AdsPayments/images',
hash: '3e9b7b3c4d4fa37f9eb580dc426412dbde2925ff',
height: 48,
httpServerLocation: '/assets/RKJSModules/Apps/Wilde/AdsPayments/images',
name: 'pending',
scales: [1.5, 2, 3, 4],
type: 'png',
width: 48,
};
const map = {
'/js/RKJSModules/Apps/Wilde/AdsPayments/images': {
pending: {
'2': 'img2x',
'3': 'img3x',
},
},
};
const {ast} = util.generateRemoteAssetCodeFileAst(
'gen',
asset,
'https://remote.server.com/',
map,
);
const code = generator(ast, {minified: true}).code;
expect(code).toBe(
'module.exports={"uri":"https://remote.server.com/"+{"2":"img2x","3":"img3x"}[require("gen").pickScale([2,3])]};',
);
});

View File

@ -86,7 +86,6 @@ export type AssetDescriptor = {
export type ExtendedAssetDescriptor = AssetDescriptor & { export type ExtendedAssetDescriptor = AssetDescriptor & {
+fileSystemLocation: string, +fileSystemLocation: string,
+files: Array<string>,
}; };
const sizeOf = denodeify(imageSize); const sizeOf = denodeify(imageSize);

View File

@ -16,7 +16,7 @@ const babel = require('babel-core');
const babelGenerate = require('babel-generator').default; const babelGenerate = require('babel-generator').default;
const babylon = require('babylon'); const babylon = require('babylon');
import type {AssetDescriptor} from '.'; import type {AssetDescriptor, ExtendedAssetDescriptor} from '.';
import type {ModuleTransportLike} from '../shared/types.flow'; import type {ModuleTransportLike} from '../shared/types.flow';
type SubTree<T: ModuleTransportLike> = ( type SubTree<T: ModuleTransportLike> = (
@ -26,10 +26,104 @@ type SubTree<T: ModuleTransportLike> = (
const assetPropertyBlacklist = new Set(['files', 'fileSystemLocation', 'path']); const assetPropertyBlacklist = new Set(['files', 'fileSystemLocation', 'path']);
// Structure of the object: dir.name.scale = asset
export type RemoteFileMap = {
[string]: {
[string]: {
[number]: string,
},
},
};
// Structure of the object: platform.dir.name.scale = asset
export type PlatformRemoteFileMap = {
['ios' | 'android']: RemoteFileMap,
};
type AssetResult = {
remote: boolean,
ast: Ast,
};
/**
* Generates the code involved in requiring an asset, but to be loaded remotely.
* If the asset cannot be found within the map, then it falls back to the
* standard asset.
*/
function generateRemoteAssetCodeFileAst(
assetSourceResolverPath: string,
assetDescriptor: ExtendedAssetDescriptor,
remoteServer: string,
remoteFileMap: RemoteFileMap,
): ?AssetResult {
const t = babel.types;
const file = remoteFileMap[assetDescriptor.fileSystemLocation];
const descriptor = file && file[assetDescriptor.name];
if (!descriptor) {
return null;
}
// require('AssetSourceResolver')
const requireCall = t.callExpression(t.identifier('require'), [
t.stringLiteral(assetSourceResolverPath),
]);
// require('AssetSourceResolver').pickScale
const pickScale = t.memberExpression(requireCall, t.identifier('pickScale'));
// require('AssetSourceResolver').pickScale([2, 3, ...])
const call = t.callExpression(pickScale, [
t.arrayExpression(
Object.keys(descriptor)
.map(Number)
.sort((a, b) => a - b)
.map(scale => t.numericLiteral(scale)),
),
]);
// {2: 'path/to/image@2x', 3: 'path/to/image@3x', ...}
const data = babylon.parseExpression(JSON.stringify(descriptor));
// ({2: '...', 3: '...'})[require(...).pickScale(...)]
const handler = t.memberExpression(data, call, true);
// 'https://remote.server.com/' + ({2: ...})[require(...).pickScale(...)]
const result = t.binaryExpression(
'+',
t.stringLiteral(remoteServer),
handler,
);
// module.exports
const moduleExports = t.memberExpression(
t.identifier('module'),
t.identifier('exports'),
);
return {
remote: true,
ast: t.file(
t.program([
t.expressionStatement(
t.assignmentExpression(
'=',
moduleExports,
t.objectExpression([
t.objectProperty(t.stringLiteral('uri'), result),
]),
),
),
]),
),
};
}
function generateAssetCodeFileAst( function generateAssetCodeFileAst(
assetRegistryPath: string, assetRegistryPath: string,
assetDescriptor: AssetDescriptor, assetDescriptor: AssetDescriptor,
): Object { ): AssetResult {
const properDescriptor = filterObject( const properDescriptor = filterObject(
assetDescriptor, assetDescriptor,
assetPropertyBlacklist, assetPropertyBlacklist,
@ -52,13 +146,16 @@ function generateAssetCodeFileAst(
const registerAssetCall = t.callExpression(registerAssetFunction, [ const registerAssetCall = t.callExpression(registerAssetFunction, [
descriptorAst, descriptorAst,
]); ]);
return t.file( return {
remote: false,
ast: t.file(
t.program([ t.program([
t.expressionStatement( t.expressionStatement(
t.assignmentExpression('=', moduleExports, registerAssetCall), t.assignmentExpression('=', moduleExports, registerAssetCall),
), ),
]), ]),
); ),
};
} }
function generateAssetTransformResult( function generateAssetTransformResult(
@ -70,7 +167,7 @@ function generateAssetTransformResult(
dependencyOffsets: Array<number>, dependencyOffsets: Array<number>,
|} { |} {
const {code} = babelGenerate( const {code} = babelGenerate(
generateAssetCodeFileAst(assetRegistryPath, assetDescriptor), generateAssetCodeFileAst(assetRegistryPath, assetDescriptor).ast,
{comments: false, compact: true}, {comments: false, compact: true},
); );
const dependencies = [assetRegistryPath]; const dependencies = [assetRegistryPath];
@ -177,5 +274,6 @@ module.exports = {
createRamBundleGroups, createRamBundleGroups,
generateAssetCodeFileAst, generateAssetCodeFileAst,
generateAssetTransformResult, generateAssetTransformResult,
generateRemoteAssetCodeFileAst,
isAssetTypeAnImage, isAssetTypeAnImage,
}; };

View File

@ -157,6 +157,7 @@ export type TransformedCodeFile = {
+hasteID: ?string, +hasteID: ?string,
package?: PackageData, package?: PackageData,
+transformed: TransformResults, +transformed: TransformResults,
+remoteAsset?: boolean,
+type: CodeFileTypes, +type: CodeFileTypes,
}; };