Introduce `getOrderedDependencyPaths` that gets all concrete dependecy paths

Summary: @​public
Since we added packager-managed assets -- internally we still think of asset dependency as a single "module". In reality there are multiple files that represent this module. This becomes important with the `getDependencies` API which is used by Buck to inform it on what to rebuild. Since `getDependencies` deals with modules, and is more of an internal API, I've introduced a new one and would go on to deprecate this.

Reviewed By: @frantic

Differential Revision: D2487207
This commit is contained in:
Amjad Masad 2015-09-29 18:32:12 -07:00 committed by facebook-github-bot-3
parent 42b5cd59e4
commit 9211b9b3cf
6 changed files with 153 additions and 29 deletions

View File

@ -24,6 +24,27 @@ var sizeOf = require('image-size');
var fs = require('fs');
describe('Bundler', function() {
function createModule({
path,
id,
dependencies,
isAsset,
isAsset_DEPRECATED,
isJSON,
resolution,
}) {
return {
path,
resolution,
getDependencies() { return Promise.resolve(dependencies); },
getName() { return Promise.resolve(id); },
isJSON() { return isJSON; },
isAsset() { return isAsset; },
isAsset_DEPRECATED() { return isAsset_DEPRECATED; },
};
}
var getDependencies;
var wrapModule;
var bundler;
@ -59,27 +80,6 @@ describe('Bundler', function() {
assetServer: assetServer,
});
function createModule({
path,
id,
dependencies,
isAsset,
isAsset_DEPRECATED,
isJSON,
resolution,
}) {
return {
path,
resolution,
getDependencies() { return Promise.resolve(dependencies); },
getName() { return Promise.resolve(id); },
isJSON() { return isJSON; },
isAsset() { return isAsset; },
isAsset_DEPRECATED() { return isAsset_DEPRECATED; },
};
}
modules = [
createModule({id: 'foo', path: '/root/foo.js', dependencies: []}),
createModule({id: 'bar', path: '/root/bar.js', dependencies: []}),
@ -129,18 +129,23 @@ describe('Bundler', function() {
sizeOf.mockImpl(function(path, cb) {
cb(null, { width: 50, height: 100 });
});
});
assetServer.getAssetData.mockImpl(function() {
pit('create a bundle', function() {
assetServer.getAssetData.mockImpl(() => {
return {
scales: [1,2,3],
files: [
'/root/img/img.png',
'/root/img/img@2x.png',
'/root/img/img@3x.png',
],
hash: 'i am a hash',
name: 'img',
type: 'png',
};
});
});
pit('create a bundle', function() {
return bundler.bundle('/root/foo.js', true, 'source_map_url')
.then(function(p) {
expect(p.addModule.mock.calls[0][0]).toEqual({
@ -186,6 +191,11 @@ describe('Bundler', function() {
width: 25,
height: 50,
scales: [1, 2, 3],
files: [
'/root/img/img.png',
'/root/img/img@2x.png',
'/root/img/img@3x.png',
],
hash: 'i am a hash',
name: 'img',
type: 'png',
@ -235,4 +245,64 @@ describe('Bundler', function() {
.toBeCalledWith('/root/foo.js', { dev: true })
);
});
describe('getOrderedDependencyPaths', () => {
beforeEach(() => {
assetServer.getAssetData.mockImpl(function(relPath) {
if (relPath === 'img/new_image.png') {
return {
scales: [1,2,3],
files: [
'/root/img/new_image.png',
'/root/img/new_image@2x.png',
'/root/img/new_image@3x.png',
],
hash: 'i am a hash',
name: 'img',
type: 'png',
};
} else if (relPath === 'img/new_image2.png') {
return {
scales: [1,2,3],
files: [
'/root/img/new_image2.png',
'/root/img/new_image2@2x.png',
'/root/img/new_image2@3x.png',
],
hash: 'i am a hash',
name: 'img',
type: 'png',
};
}
throw new Error('unknown image ' + relPath);
});
});
pit('should get the concrete list of all dependency files', () => {
modules.push(
createModule({
id: 'new_image2.png',
path: '/root/img/new_image2.png',
isAsset: true,
resolution: 2,
dependencies: []
}),
);
return bundler.getOrderedDependencyPaths('/root/foo.js', true)
.then((paths) => expect(paths).toEqual([
'/root/foo.js',
'/root/bar.js',
'/root/img/img.png',
'/root/img/new_image.png',
'/root/img/new_image@2x.png',
'/root/img/new_image@3x.png',
'/root/file.json',
'/root/img/new_image2.png',
'/root/img/new_image2@2x.png',
'/root/img/new_image2@3x.png',
]));
});
});
});

View File

@ -187,6 +187,38 @@ class Bundler {
return this._resolver.getDependencies(main, { dev: isDev, platform });
}
getOrderedDependencyPaths({ entryFile, dev, platform }) {
return this.getDependencies(entryFile, dev, platform).then(
({ dependencies }) => {
const ret = [];
const promises = [];
const placeHolder = {};
dependencies.forEach(dep => {
if (dep.isAsset()) {
const relPath = getPathRelativeToRoot(
this._projectRoots,
dep.path
);
promises.push(
this._assetServer.getAssetData(relPath, platform)
);
ret.push(placeHolder);
} else {
ret.push(dep.path);
}
});
return Promise.all(promises).then(assetsData => {
assetsData.forEach(({ files }) => {
const index = ret.indexOf(placeHolder);
ret.splice(index, 1, ...files);
});
return ret;
});
}
);
}
_transformModule(bundle, response, module, platform = null) {
let transform;

View File

@ -200,6 +200,13 @@ class Server {
});
}
getOrderedDependencyPaths(options) {
return Promise.resolve().then(() => {
const opts = dependencyOpts(options);
return this._bundler.getOrderedDependencyPaths(opts);
});
}
_onFileChange(type, filepath, root) {
const absPath = path.join(root, filepath);
this._bundler.invalidateFile(absPath);

View File

@ -86,6 +86,13 @@ class SocketClient {
});
}
getOrderedDependencyPaths(main) {
return this._send({
type: 'getOrderedDependencyPaths',
data: main,
});
}
buildBundle(options) {
return this._send({
type: 'buildBundle',

View File

@ -121,6 +121,14 @@ class SocketServer {
);
break;
case 'getOrderedDependencyPaths':
this._jobs++;
this._packagerServer.getOrderedDependencyPaths(m.data).then(
(dependencies) => this._reply(sock, m.id, 'result', dependencies),
handleError,
);
break;
default:
this._reply(sock, m.id, 'error', 'Unknown message type: ' + m.type);
}

View File

@ -39,7 +39,7 @@ function _dependencies(argv, conf, resolve, reject) {
command: 'platform',
description: 'The platform extension used for selecting modules',
type: 'string',
},
}
], argv);
const rootModuleAbsolutePath = args['entry-file'];
@ -75,22 +75,22 @@ function _dependencies(argv, conf, resolve, reject) {
log('Waiting for the packager.');
resolve(ReactPackager.createClientFor(config).then(client => {
log('Packager client was created');
return client.getDependencies(options)
return client.getOrderedDependencyPaths(options)
.then(deps => {
log('Packager returned dependencies');
client.close();
deps.forEach(module => {
deps.forEach(modulePath => {
// Temporary hack to disable listing dependencies not under this directory.
// Long term, we need either
// (a) JS code to not depend on anything outside this directory, or
// (b) Come up with a way to declare this dependency in Buck.
const isInsideProjectRoots = config.projectRoots.filter(root =>
module.path.startsWith(root)
modulePath.startsWith(root)
).length > 0;
if (isInsideProjectRoots) {
outStream.write(module.path + '\n');
outStream.write(modulePath + '\n');
}
});
writeToFile && outStream.end();