metro-bundler: remove hardcoded AssetRegistry path

Reviewed By: davidaurelio

Differential Revision: D5452553

fbshipit-source-id: e0a6f56d3bc03f4ba7f34fbee1ae418495dcc6cd
This commit is contained in:
Jean Lauliac 2017-07-24 17:04:38 -07:00 committed by Facebook Github Bot
parent 74e78ee43f
commit 5ba56250b5
10 changed files with 66 additions and 31 deletions

View File

@ -40,6 +40,7 @@ const {any, objectContaining} = expect;
var commonOptions = { var commonOptions = {
allowBundleUpdates: false, allowBundleUpdates: false,
assetExts: defaults.assetExts, assetExts: defaults.assetExts,
assetRegistryPath: '/AssetRegistry.js',
cacheVersion: 'smth', cacheVersion: 'smth',
enableBabelRCLookup: true, enableBabelRCLookup: true,
extraNodeModules: {}, extraNodeModules: {},

View File

@ -121,6 +121,7 @@ export type PostProcessBundleSourcemap = ({
type Options = {| type Options = {|
+allowBundleUpdates: boolean, +allowBundleUpdates: boolean,
+assetExts: Array<string>, +assetExts: Array<string>,
+assetRegistryPath: string,
+assetServer: AssetServer, +assetServer: AssetServer,
+blacklistRE?: RegExp, +blacklistRE?: RegExp,
+cacheVersion: string, +cacheVersion: string,
@ -213,6 +214,7 @@ class Bundler {
this._resolverPromise = Resolver.load({ this._resolverPromise = Resolver.load({
assetExts: opts.assetExts, assetExts: opts.assetExts,
assetRegistryPath: opts.assetRegistryPath,
blacklistRE: opts.blacklistRE, blacklistRE: opts.blacklistRE,
extraNodeModules: opts.extraNodeModules, extraNodeModules: opts.extraNodeModules,
getPolyfills: opts.getPolyfills, getPolyfills: opts.getPolyfills,
@ -740,7 +742,8 @@ class Bundler {
return this._applyAssetPlugins(assetPlugins, asset); return this._applyAssetPlugins(assetPlugins, asset);
}).then(asset => { }).then(asset => {
const {code, dependencies, dependencyOffsets} = generateAssetTransformResult(asset); const {code, dependencies, dependencyOffsets} =
generateAssetTransformResult(this._opts.assetRegistryPath, asset);
return { return {
asset, asset,
code, code,

View File

@ -29,15 +29,16 @@ const assetPropertyBlacklist = new Set([
'path', 'path',
]); ]);
const ASSET_REGISTRY_PATH = 'react-native/Libraries/Image/AssetRegistry'; function generateAssetCodeFileAst(
assetRegistryPath: string,
function generateAssetCodeFileAst(assetDescriptor: AssetDescriptor): Object { assetDescriptor: AssetDescriptor,
): Object {
const properDescriptor = filterObject(assetDescriptor, assetPropertyBlacklist); const properDescriptor = filterObject(assetDescriptor, assetPropertyBlacklist);
const descriptorAst = babylon.parseExpression(JSON.stringify(properDescriptor)); const descriptorAst = babylon.parseExpression(JSON.stringify(properDescriptor));
const t = babel.types; const t = babel.types;
const moduleExports = t.memberExpression(t.identifier('module'), t.identifier('exports')); const moduleExports = t.memberExpression(t.identifier('module'), t.identifier('exports'));
const requireCall = const requireCall =
t.callExpression(t.identifier('require'), [t.stringLiteral(ASSET_REGISTRY_PATH)]); t.callExpression(t.identifier('require'), [t.stringLiteral(assetRegistryPath)]);
const registerAssetFunction = t.memberExpression(requireCall, t.identifier('registerAsset')); const registerAssetFunction = t.memberExpression(requireCall, t.identifier('registerAsset'));
const registerAssetCall = t.callExpression(registerAssetFunction, [descriptorAst]); const registerAssetCall = t.callExpression(registerAssetFunction, [descriptorAst]);
return t.file(t.program([ return t.file(t.program([
@ -45,17 +46,20 @@ function generateAssetCodeFileAst(assetDescriptor: AssetDescriptor): Object {
])); ]));
} }
function generateAssetTransformResult(assetDescriptor: AssetDescriptor): {| function generateAssetTransformResult(
assetRegistryPath: string,
assetDescriptor: AssetDescriptor,
): {|
code: string, code: string,
dependencies: Array<string>, dependencies: Array<string>,
dependencyOffsets: Array<number>, dependencyOffsets: Array<number>,
|} { |} {
const {code} = babelGenerate( const {code} = babelGenerate(
generateAssetCodeFileAst(assetDescriptor), generateAssetCodeFileAst(assetRegistryPath, assetDescriptor),
{comments: false, compact: true}, {comments: false, compact: true},
); );
const dependencies = [ASSET_REGISTRY_PATH]; const dependencies = [assetRegistryPath];
const dependencyOffsets = [code.indexOf(ASSET_REGISTRY_PATH) - 1]; const dependencyOffsets = [code.indexOf(assetRegistryPath) - 1];
return {code, dependencies, dependencyOffsets}; return {code, dependencies, dependencyOffsets};
} }

View File

@ -32,6 +32,7 @@ type ContainsTransformerOptions = {+transformer: JSTransformerOptions}
type Options = {| type Options = {|
+assetExts: Array<string>, +assetExts: Array<string>,
+assetRegistryPath: string,
+blacklistRE?: RegExp, +blacklistRE?: RegExp,
+extraNodeModules: ?{}, +extraNodeModules: ?{},
+getPolyfills: ({platform: ?string}) => $ReadOnlyArray<string>, +getPolyfills: ({platform: ?string}) => $ReadOnlyArray<string>,
@ -71,7 +72,7 @@ class Resolver {
static async load(opts: Options): Promise<Resolver> { static async load(opts: Options): Promise<Resolver> {
const depGraphOpts = { const depGraphOpts = {
assetDependencies: ['react-native/Libraries/Image/AssetRegistry'], assetDependencies: [opts.assetRegistryPath],
assetExts: opts.assetExts, assetExts: opts.assetExts,
extraNodeModules: opts.extraNodeModules, extraNodeModules: opts.extraNodeModules,
forceNodeFilesystemAPI: false, forceNodeFilesystemAPI: false,

View File

@ -68,6 +68,7 @@ function debounceAndBatch(fn, delay) {
type Options = { type Options = {
assetExts?: Array<string>, assetExts?: Array<string>,
+assetRegistryPath: string,
blacklistRE?: RegExp, blacklistRE?: RegExp,
cacheVersion?: string, cacheVersion?: string,
enableBabelRCLookup?: boolean, enableBabelRCLookup?: boolean,
@ -176,6 +177,7 @@ class Server {
const maxWorkers = getMaxWorkers(options.maxWorkers); const maxWorkers = getMaxWorkers(options.maxWorkers);
this._opts = { this._opts = {
assetExts: options.assetExts || defaults.assetExts, assetExts: options.assetExts || defaults.assetExts,
assetRegistryPath: options.assetRegistryPath,
blacklistRE: options.blacklistRE, blacklistRE: options.blacklistRE,
cacheVersion: options.cacheVersion || '1.0', cacheVersion: options.cacheVersion || '1.0',
enableBabelRCLookup: enableBabelRCLookup:

View File

@ -33,6 +33,7 @@ exports.createServer = createServer;
exports.Logger = Logger; exports.Logger = Logger;
type Options = {| type Options = {|
+assetRegistryPath: string,
+sourceExts: ?Array<string>, +sourceExts: ?Array<string>,
+transformCache: TransformCache, +transformCache: TransformCache,
+transformModulePath: string, +transformModulePath: string,

View File

@ -237,8 +237,8 @@ __d(/* /TestBundle.js */function(global, require, module, exports) {
'use strict'; 'use strict';
var Bar = require(5 ); // 5 = ./Bar var Bar = require(5); // 5 = ./Bar
var Foo = require(6 ); // 6 = ./Foo var Foo = require(6); // 6 = ./Foo
module.exports = { Foo: Foo, Bar: Bar }; module.exports = { Foo: Foo, Bar: Bar };
}, 0); }, 0);
@ -246,7 +246,7 @@ __d(/* /Bar.js */function(global, require, module, exports) {
'use strict'; 'use strict';
var Foo = require(6 ); // 6 = ./Foo var Foo = require(6); // 6 = ./Foo
module.exports = { type: 'bar', foo: Foo.type }; module.exports = { type: 'bar', foo: Foo.type };
}, 5); }, 5);
@ -254,13 +254,15 @@ __d(/* /Foo.js */function(global, require, module, exports) {
'use strict'; 'use strict';
var asset = require(7 ); // 7 = ./test.png var asset = require(7); // 7 = ./test.png
module.exports = { type: 'foo', asset: asset }; module.exports = { type: 'foo', asset: asset };
}, 6); }, 6);
__d(/* /test.png */function(global, require, module, exports) {module.exports=require(8 ).registerAsset({\\"__packager_asset\\":true,\\"httpServerLocation\\":\\"/assets\\",\\"width\\":8,\\"height\\":8,\\"scales\\":[1],\\"hash\\":\\"77d45c1f7fa73c0f6c444a830dc42f67\\",\\"name\\":\\"test\\",\\"type\\":\\"png\\"}); // 8 = react-native/Libraries/Image/AssetRegistry __d(/* /test.png */function(global, require, module, exports) {module.exports=require(8).registerAsset({\\"__packager_asset\\":true,\\"httpServerLocation\\":\\"/assets\\",\\"width\\":8,\\"height\\":8,\\"scales\\":[1],\\"hash\\":\\"77d45c1f7fa73c0f6c444a830dc42f67\\",\\"name\\":\\"test\\",\\"type\\":\\"png\\"}); // 8 = /AssetRegistry
}, 7); }, 7);
__d(/* /node_modules/react-native/Libraries/Image/AssetRegistry.js */function(global, require, module, exports) {'use strict'; __d(/* /AssetRegistry.js */function(global, require, module, exports) {
'use strict';
}, 8); }, 8);
;require(0);" ;require(0);"
`; `;
@ -502,8 +504,8 @@ __d(/* /TestBundle.js */function(global, require, module, exports) {
'use strict'; 'use strict';
var Bar = require(5 ); // 5 = ./Bar var Bar = require(5); // 5 = ./Bar
var Foo = require(6 ); // 6 = ./Foo var Foo = require(6); // 6 = ./Foo
module.exports = { Foo: Foo, Bar: Bar }; module.exports = { Foo: Foo, Bar: Bar };
}, 0); }, 0);
@ -511,7 +513,7 @@ __d(/* /Bar.js */function(global, require, module, exports) {
'use strict'; 'use strict';
var Foo = require(6 ); // 6 = ./Foo var Foo = require(6); // 6 = ./Foo
module.exports = { type: 'bar', foo: Foo.type }; module.exports = { type: 'bar', foo: Foo.type };
}, 5); }, 5);
@ -519,13 +521,15 @@ __d(/* /Foo.js */function(global, require, module, exports) {
'use strict'; 'use strict';
var asset = require(7 ); // 7 = ./test.png var asset = require(7); // 7 = ./test.png
module.exports = { type: 'foo', asset: asset }; module.exports = { type: 'foo', asset: asset };
}, 6); }, 6);
__d(/* /test.png */function(global, require, module, exports) {module.exports=require(8 ).registerAsset({\\"__packager_asset\\":true,\\"httpServerLocation\\":\\"/assets\\",\\"width\\":8,\\"height\\":8,\\"scales\\":[1],\\"hash\\":\\"77d45c1f7fa73c0f6c444a830dc42f67\\",\\"name\\":\\"test\\",\\"type\\":\\"png\\"}); // 8 = react-native/Libraries/Image/AssetRegistry __d(/* /test.png */function(global, require, module, exports) {module.exports=require(8).registerAsset({\\"__packager_asset\\":true,\\"httpServerLocation\\":\\"/assets\\",\\"width\\":8,\\"height\\":8,\\"scales\\":[1],\\"hash\\":\\"77d45c1f7fa73c0f6c444a830dc42f67\\",\\"name\\":\\"test\\",\\"type\\":\\"png\\"}); // 8 = /AssetRegistry
}, 7); }, 7);
__d(/* /node_modules/react-native/Libraries/Image/AssetRegistry.js */function(global, require, module, exports) {'use strict'; __d(/* /AssetRegistry.js */function(global, require, module, exports) {
'use strict';
}, 8); }, 8);
;require(0);" ;require(0);"
`; `;

View File

@ -20,6 +20,10 @@ jasmine.DEFAULT_TIMEOUT_INTERVAL = 30 * 1000;
const INPUT_PATH = path.resolve(__dirname, '../basic_bundle'); const INPUT_PATH = path.resolve(__dirname, '../basic_bundle');
const POLYFILLS_PATH = path.resolve(__dirname, '../../Resolver/polyfills'); const POLYFILLS_PATH = path.resolve(__dirname, '../../Resolver/polyfills');
const ASSET_REGISTRY_PATH = path.resolve(
__dirname,
'../basic_bundle/AssetRegistry',
);
describe('basic_bundle', () => { describe('basic_bundle', () => {
const absPathRe = new RegExp(INPUT_PATH, 'g'); const absPathRe = new RegExp(INPUT_PATH, 'g');
@ -74,9 +78,24 @@ describe('basic_bundle', () => {
jest.resetModules(); jest.resetModules();
}); });
/**
* On different machines and local repos the absolute paths are different so
* we relativize them to the root of the test. We also trim the spacing inside
* ID-based `require()` calls because the way the bundle appears to do it is
* by replacing path strings directly by numbers in the code, not the AST.
*/
function verifyResultCode(code) {
expect(
code
.replace(absPathRe, '')
.replace(/require\(([0-9]*) *\)/g, 'require($1)'),
).toMatchSnapshot();
}
it('bundles package with polyfills as expected', async () => { it('bundles package with polyfills as expected', async () => {
const bundleWithPolyfills = await Packager.buildBundle( const bundleWithPolyfills = await Packager.buildBundle(
{ {
assetRegistryPath: ASSET_REGISTRY_PATH,
getPolyfills: () => [polyfill1, polyfill2], getPolyfills: () => [polyfill1, polyfill2],
projectRoots: [INPUT_PATH, POLYFILLS_PATH], projectRoots: [INPUT_PATH, POLYFILLS_PATH],
transformCache: Packager.TransformCaching.none(), transformCache: Packager.TransformCaching.none(),
@ -89,15 +108,13 @@ describe('basic_bundle', () => {
platform: 'ios', platform: 'ios',
}, },
); );
verifyResultCode(bundleWithPolyfills.getSource());
expect(
bundleWithPolyfills.getSource().replace(absPathRe, ''),
).toMatchSnapshot();
}); });
it('bundles package without polyfills as expected', async () => { it('bundles package without polyfills as expected', async () => {
const bundleWithoutPolyfills = await Packager.buildBundle( const bundleWithoutPolyfills = await Packager.buildBundle(
{ {
assetRegistryPath: ASSET_REGISTRY_PATH,
getPolyfills: () => [polyfill1, polyfill2], getPolyfills: () => [polyfill1, polyfill2],
projectRoots: [INPUT_PATH, POLYFILLS_PATH], projectRoots: [INPUT_PATH, POLYFILLS_PATH],
transformCache: Packager.TransformCaching.none(), transformCache: Packager.TransformCaching.none(),
@ -110,9 +127,6 @@ describe('basic_bundle', () => {
platform: 'ios', platform: 'ios',
}, },
); );
verifyResultCode(bundleWithoutPolyfills.getSource());
expect(
bundleWithoutPolyfills.getSource().replace(absPathRe, ''),
).toMatchSnapshot();
}); });
}); });

View File

@ -0,0 +1,6 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*/
'use strict';