Convert entryPoint paths to absolute in the Server

Reviewed By: davidaurelio

Differential Revision: D7196392

fbshipit-source-id: a1927d66fd67e35382a5ac418896dc5a902a5a5c
This commit is contained in:
Rafael Oleza 2018-03-19 10:04:49 -07:00 committed by Facebook Github Bot
parent ab25e7c475
commit 40cce3b120
12 changed files with 179 additions and 73 deletions

View File

@ -11,6 +11,8 @@
'use strict';
const os = require('os');
const path = require('path');
const process = require('process');
const {EventEmitter} = require('events');
@ -33,6 +35,7 @@ export type LogEntry = {
action_name?: string,
action_phase?: string,
duration_ms?: number,
entry_point?: string,
log_entry_label: string,
log_session?: string,
start_timestamp?: [number, number],
@ -46,7 +49,13 @@ function on(event: string, handler: (logEntry: LogEntry) => void): void {
}
function createEntry(data: LogEntry | string): LogEntry {
const logEntry = typeof data === 'string' ? {log_entry_label: data} : data;
const logEntry: LogEntry =
typeof data === 'string' ? {log_entry_label: data} : data;
const entryPoint = logEntry.entry_point;
if (entryPoint) {
logEntry.entry_point = path.relative(process.cwd(), entryPoint);
}
return {
...logEntry,

View File

@ -73,13 +73,9 @@ class DeltaCalculator extends EventEmitter {
this._options = options;
this._dependencyGraph = dependencyGraph;
const entryPoints = this._options.entryPoints.map(entryPoint =>
this._dependencyGraph.getAbsolutePath(entryPoint),
);
this._graph = {
dependencies: new Map(),
entryPoints,
entryPoints: this._options.entryPoints,
};
this._dependencyGraph

View File

@ -356,19 +356,12 @@ class DeltaTransformer extends EventEmitter {
}
async _getAppend(dependencyEdges: DependencyEdges): Promise<DeltaEntries> {
// Get the absolute path of the entry file, in order to be able to get the
// actual correspondant module (and its moduleId) to be able to add the
// correct require(); call at the very end of the bundle.
const entryPointModulePath = this._dependencyGraph.getAbsolutePath(
this._bundleOptions.entryFile,
);
// First, get the modules correspondant to all the module names defined in
// the `runBeforeMainModule` config variable. Then, append the entry point
// module so the last thing that gets required is the entry point.
const append = new Map(
this._bundleOptions.runBeforeMainModule
.concat(entryPointModulePath)
.concat(this._bundleOptions.entryFile)
.filter(path => dependencyEdges.has(path))
.map(this._getModuleId)
.map(moduleId => {

View File

@ -9,6 +9,8 @@
*/
'use strict';
jest.mock('../../lib/getAbsolutePath');
const HmrServer = require('..');
const {EventEmitter} = require('events');
@ -41,6 +43,9 @@ describe('HmrServer', () => {
update: jest.fn(),
};
},
getProjectRoots() {
return ['/root'];
},
};
hmrServer = new HmrServer(serverMock);
@ -57,7 +62,7 @@ describe('HmrServer', () => {
expect.objectContaining({
deltaBundleId: null,
dev: true,
entryFile: 'EntryPoint.js',
entryFile: '/root/EntryPoint.js',
minify: false,
platform: 'ios',
}),

View File

@ -12,6 +12,7 @@
const addParamsToDefineCall = require('../lib/addParamsToDefineCall');
const formatBundlingError = require('../lib/formatBundlingError');
const getAbsolutePath = require('../lib/getAbsolutePath');
const getBundlingOptionsForHmr = require('./getBundlingOptionsForHmr');
const nullthrows = require('fbjs/lib/nullthrows');
const parseCustomTransformOptions = require('../lib/parseCustomTransformOptions');
@ -66,7 +67,11 @@ class HmrServer<TClient: Client> {
const deltaBundler = this._packagerServer.getDeltaBundler();
const deltaTransformer = await deltaBundler.getDeltaTransformer(
clientUrl,
getBundlingOptionsForHmr(bundleEntry, platform, customTransformOptions),
getBundlingOptionsForHmr(
getAbsolutePath(bundleEntry, this._packagerServer.getProjectRoots()),
platform,
customTransformOptions,
),
);
// Trigger an initial build to start up the DeltaTransformer.

View File

@ -21,6 +21,7 @@ jest
.mock('../../Assets')
.mock('../../node-haste/DependencyGraph')
.mock('metro-core/src/Logger')
.mock('../../lib/getAbsolutePath')
.mock('../../lib/GlobalTransformCache')
.mock('../../DeltaBundler/Serializers/Serializers');
@ -47,7 +48,7 @@ describe('processRequest', () => {
let server;
const options = {
projectRoots: ['root'],
projectRoots: ['/root'],
blacklistRE: null,
cacheVersion: null,
polyfillModuleNames: null,
@ -183,7 +184,7 @@ describe('processRequest', () => {
bundleType: 'bundle',
customTransformOptions: {},
dev: true,
entryFile: 'index.ios.js',
entryFile: '/root/index.ios.js',
entryModuleOnly: false,
excludeSource: false,
hot: true,
@ -214,7 +215,7 @@ describe('processRequest', () => {
bundleType: 'bundle',
customTransformOptions: {},
dev: true,
entryFile: 'index.js',
entryFile: '/root/index.js',
entryModuleOnly: false,
excludeSource: false,
hot: true,
@ -245,7 +246,7 @@ describe('processRequest', () => {
bundleType: 'bundle',
customTransformOptions: {},
dev: true,
entryFile: 'index.js',
entryFile: '/root/index.js',
entryModuleOnly: false,
excludeSource: false,
hot: true,
@ -391,7 +392,7 @@ describe('processRequest', () => {
server.processRequest(req, res);
res.end.mockImplementation(value => {
expect(getAsset).toBeCalledWith('imgs/a.png', ['root'], 'ios');
expect(getAsset).toBeCalledWith('imgs/a.png', ['/root'], 'ios');
expect(value).toBe('i am image');
done();
});
@ -409,7 +410,7 @@ describe('processRequest', () => {
server.processRequest(req, res);
res.end.mockImplementation(value => {
expect(getAsset).toBeCalledWith('imgs/a.png', ['root'], 'ios');
expect(getAsset).toBeCalledWith('imgs/a.png', ['/root'], 'ios');
expect(value).toBe(mockData.slice(0, 4));
done();
});
@ -427,7 +428,7 @@ describe('processRequest', () => {
res.end.mockImplementation(value => {
expect(getAsset).toBeCalledWith(
'imgs/\u{4E3B}\u{9875}/logo.png',
['root'],
['/root'],
undefined,
);
expect(value).toBe('i am image');
@ -450,7 +451,7 @@ describe('processRequest', () => {
assetPlugins: [],
customTransformOptions: {},
dev: true,
entryFile: 'foo file',
entryFile: '/root/foo file',
entryModuleOnly: false,
excludeSource: false,
hot: false,

View File

@ -1,3 +1,3 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`processRequest Generate delta bundle endpoint should send the correct deltaBundlerId to the bundler 1`] = `"{\\"sourceMapUrl\\":null,\\"bundleType\\":\\"delta\\",\\"customTransformOptions\\":{},\\"entryFile\\":\\"index.js\\",\\"deltaBundleId\\":null,\\"dev\\":true,\\"minify\\":false,\\"excludeSource\\":null,\\"hot\\":true,\\"runBeforeMainModule\\":[\\"InitializeCore\\"],\\"runModule\\":true,\\"inlineSourceMap\\":false,\\"isolateModuleIDs\\":false,\\"platform\\":\\"ios\\",\\"resolutionResponse\\":null,\\"entryModuleOnly\\":false,\\"assetPlugins\\":[],\\"onProgress\\":null,\\"unbundle\\":false}"`;
exports[`processRequest Generate delta bundle endpoint should send the correct deltaBundlerId to the bundler 1`] = `"{\\"sourceMapUrl\\":null,\\"bundleType\\":\\"delta\\",\\"customTransformOptions\\":{},\\"entryFile\\":\\"/root/index.js\\",\\"deltaBundleId\\":null,\\"dev\\":true,\\"minify\\":false,\\"excludeSource\\":null,\\"hot\\":true,\\"runBeforeMainModule\\":[\\"InitializeCore\\"],\\"runModule\\":true,\\"inlineSourceMap\\":false,\\"isolateModuleIDs\\":false,\\"platform\\":\\"ios\\",\\"resolutionResponse\\":null,\\"entryModuleOnly\\":false,\\"assetPlugins\\":[],\\"onProgress\\":null,\\"unbundle\\":false}"`;

View File

@ -17,6 +17,7 @@ const Serializers = require('../DeltaBundler/Serializers/Serializers');
const debug = require('debug')('Metro:Server');
const defaults = require('../defaults');
const formatBundlingError = require('../lib/formatBundlingError');
const getAbsolutePath = require('../lib/getAbsolutePath');
const getMaxWorkers = require('../lib/getMaxWorkers');
const getOrderedDependencyPaths = require('../lib/getOrderedDependencyPaths');
const mime = require('mime-types');
@ -234,6 +235,7 @@ class Server {
runBeforeMainModule: this._opts.getModulesRunBeforeMainModule(
options.entryFile,
),
entryFile: getAbsolutePath(options.entryFile, this._opts.projectRoots),
};
const fullBundle = await Serializers.fullBundle(
@ -255,7 +257,10 @@ class Server {
async getRamBundleInfo(options: BundleOptions): Promise<RamBundleInfo> {
return await Serializers.getRamBundleInfo(
this._deltaBundler,
options,
{
...options,
entryFile: getAbsolutePath(options.entryFile, this._opts.projectRoots),
},
this._opts.getTransformOptions,
);
}
@ -263,7 +268,10 @@ class Server {
async getAssets(options: BundleOptions): Promise<$ReadOnlyArray<AssetData>> {
return await Serializers.getAssets(
this._deltaBundler,
options,
{
...options,
entryFile: getAbsolutePath(options.entryFile, this._opts.projectRoots),
},
this._opts.projectRoots,
);
}
@ -277,6 +285,7 @@ class Server {
const bundleOptions = {
...Server.DEFAULT_BUNDLE_OPTIONS,
...options,
entryFile: getAbsolutePath(options.entryFile, this._opts.projectRoots),
bundleType: 'delta',
};
@ -813,6 +822,11 @@ class Server {
})
.join('.') + '.js';
const absoluteEntryFile = getAbsolutePath(
entryFile,
this._opts.projectRoots,
);
// try to get the platform from the url
const platform =
urlQuery.platform ||
@ -847,7 +861,7 @@ class Server {
}),
bundleType: isMap ? 'map' : deltaBundleId ? 'delta' : 'bundle',
customTransformOptions,
entryFile,
entryFile: absoluteEntryFile,
deltaBundleId,
dev,
minify,
@ -891,6 +905,10 @@ class Server {
return this._reporter;
}
getProjectRoots(): $ReadOnlyArray<string> {
return this._opts.projectRoots;
}
static DEFAULT_BUNDLE_OPTIONS = {
assetPlugins: [],
customTransformOptions: Object.create(null),

View File

@ -0,0 +1,16 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
'use strict';
const path = require('path');
module.exports = (file: string, roots: $ReadOnlyArray<string>): string =>
path.resolve(roots[0], file);

View File

@ -0,0 +1,57 @@
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @emails oncall+javascript_foundation
* @format
*/
'use strict';
jest.mock('fs', () => new (require('metro-memory-fs'))());
const fs = require('fs');
const getAbsolutePath = require('../getAbsolutePath');
const mkdirp = require('mkdirp');
beforeEach(() => {
fs.reset();
mkdirp.sync('/root/a');
mkdirp.sync('/root/b');
mkdirp.sync('/root/a/d');
});
it('should work for a simple case with a single project root', () => {
fs.writeFileSync('/root/a/entryPoint.js', '');
expect(getAbsolutePath('entryPoint.js', ['/root/a'])).toEqual(
'/root/a/entryPoint.js',
);
});
it('should resolve from the first defined project root', () => {
fs.writeFileSync('/root/a/entryPoint.js', '');
fs.writeFileSync('/root/b/entryPoint.js', '');
expect(getAbsolutePath('entryPoint.js', ['/root/a', '/root/c'])).toEqual(
'/root/a/entryPoint.js',
);
});
it('should resolve from sub-folders', () => {
fs.writeFileSync('/root/a/d/entryPoint.js', '');
fs.writeFileSync('/root/b/entryPoint.js', '');
expect(getAbsolutePath('d/entryPoint.js', ['/root/a', '/root/d'])).toEqual(
'/root/a/d/entryPoint.js',
);
});
it('should throw an error if not found', () => {
expect(() =>
getAbsolutePath('entryPoint.js', ['/root/a', '/root/d']),
).toThrow();
});

View File

@ -0,0 +1,51 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
'use strict';
const fs = require('fs');
const isAbsolutePath = require('absolute-path');
const path = require('path');
function getAbsolutePath(
filePath: string,
projectRoots: $ReadOnlyArray<string>,
): string {
if (isAbsolutePath(filePath)) {
return path.resolve(filePath);
}
for (let i = 0; i < projectRoots.length; i++) {
const potentialAbsPath = path.resolve(projectRoots[i], filePath);
if (fs.existsSync(potentialAbsPath)) {
return potentialAbsPath;
}
}
throw new NotFoundError(filePath, projectRoots);
}
class NotFoundError extends Error {
status: number;
type: string;
constructor(relativePath: string, projectRoots: $ReadOnlyArray<string>) {
super(
`File not found: ${relativePath} in any of the project roots (${projectRoots.join(
', ',
)})`,
);
this.type = 'NotFoundError';
this.status = 404;
}
}
module.exports = getAbsolutePath;

View File

@ -19,11 +19,9 @@ const ModuleCache = require('./ModuleCache');
const ResolutionRequest = require('./DependencyGraph/ResolutionRequest');
const fs = require('fs');
const isAbsolutePath = require('absolute-path');
const parsePlatformFilePath = require('./lib/parsePlatformFilePath');
const path = require('path');
const toLocalPath = require('../node-haste/lib/toLocalPath');
const util = require('util');
const {ModuleResolver} = require('./DependencyGraph/ModuleResolution');
const {EventEmitter} = require('events');
@ -288,52 +286,9 @@ class DependencyGraph extends EventEmitter {
return toLocalPath(this._opts.projectRoots, filePath);
}
getAbsolutePath(filePath: string) {
if (isAbsolutePath(filePath)) {
return path.resolve(filePath);
}
for (let i = 0; i < this._opts.projectRoots.length; i++) {
const root = this._opts.projectRoots[i];
const potentialAbsPath = path.join(root, filePath);
if (this._hasteFS.exists(potentialAbsPath)) {
return path.resolve(potentialAbsPath);
}
}
// If we failed to find a file, maybe this is just a Haste name so try that
// TODO: We should prefer Haste name resolution first ideally since it is faster
// TODO: Ideally, we should not do any `path.parse().name` here and just use the
// name, but in `metro/src/Server/index.js` we append `'.js'` to all names
// so until that changes, we have to do this.
const potentialPath = this._moduleMap.getModule(
path.parse(filePath).name,
null,
);
if (potentialPath) {
return potentialPath;
}
throw new NotFoundError(
'Cannot find entry file %s in any of the roots: %j',
filePath,
this._opts.projectRoots,
);
}
createPolyfill(options: {file: string}) {
return this._moduleCache.createPolyfill(options);
}
}
function NotFoundError(...args) {
Error.call(this);
Error.captureStackTrace(this, this.constructor);
var msg = util.format.apply(util, args);
this.message = msg;
this.type = this.name = 'NotFoundError';
this.status = 404;
}
util.inherits(NotFoundError, Error);
module.exports = DependencyGraph;