Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions packages/metro/src/HmrServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const getGraphId = require('./lib/getGraphId');
const hmrJSBundle = require('./DeltaBundler/Serializers/hmrJSBundle');
const nullthrows = require('nullthrows');
const parseOptionsFromUrl = require('./lib/parseOptionsFromUrl');
const transformHelpers = require('./lib/transformHelpers');
const splitBundleOptions = require('./lib/splitBundleOptions');
const url = require('url');

Expand Down Expand Up @@ -84,13 +85,24 @@ class HmrServer<TClient: Client> {

const {options} = parseOptionsFromUrl(
url.format(urlObj),
this._config.projectRoot,
new Set(this._config.resolver.platforms),
);

const {entryFile, transformOptions} = splitBundleOptions(options);

const graphId = getGraphId(entryFile, transformOptions);
/**
* `entryFile` is relative to projectRoot, we need to use resolution function
* to find the appropriate file with supported extensions.
*/
const resolutionFn = await transformHelpers.getResolveDependencyFn(
this._bundler.getBundler(),
transformOptions.platform,
);
const resolvedEntryFilePath = resolutionFn(
`${this._config.projectRoot}/.`,
entryFile,
);
const graphId = getGraphId(resolvedEntryFilePath, transformOptions);
revPromise = this._bundler.getRevisionByGraphId(graphId);

if (!revPromise) {
Expand Down
5 changes: 5 additions & 0 deletions packages/metro/src/HmrServer/__tests__/HmrServer-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
const HmrServer = require('..');

const getGraphId = require('../../lib/getGraphId');
jest.mock('../../lib/transformHelpers', () => ({
getResolveDependencyFn: () => (from, to) =>
`${from.replace(/\.$/, '')}${to}.js`,
}));

describe('HmrServer', () => {
let hmrServer;
Expand Down Expand Up @@ -74,6 +78,7 @@ describe('HmrServer', () => {
deleted: new Set(),
},
}),
getBundler() {},
};
createModuleIdMock = path => {
return path + '-id';
Expand Down
39 changes: 32 additions & 7 deletions packages/metro/src/Server.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const debug = require('debug')('Metro:Server');
const formatBundlingError = require('./lib/formatBundlingError');
const mime = require('mime-types');
const parseOptionsFromUrl = require('./lib/parseOptionsFromUrl');
const transformHelpers = require('./lib/transformHelpers');
const parsePlatformFilePath = require('./node-haste/lib/parsePlatformFilePath');
const path = require('path');
const serializeDeltaJSBundle = require('./DeltaBundler/Serializers/helpers/serializeDeltaJSBundle');
Expand Down Expand Up @@ -424,15 +425,27 @@ class Server {
protocol: 'http',
host: req.headers.host,
}),
this._config.projectRoot,
new Set(this._config.resolver.platforms),
);
const {
entryFile,
transformOptions,
serializerOptions,
} = splitBundleOptions(bundleOptions);
const graphId = getGraphId(entryFile, transformOptions);

/**
* `entryFile` is relative to projectRoot, we need to use resolution function
* to find the appropriate file with supported extensions.
*/
const resolutionFn = await transformHelpers.getResolveDependencyFn(
this._bundler.getBundler(),
transformOptions.platform,
);
const resolvedEntryFilePath = resolutionFn(
`${this._config.projectRoot}/.`,
entryFile,
);
const graphId = getGraphId(resolvedEntryFilePath, transformOptions);
const buildID = this.getNewBuildID();

let onProgress = null;
Expand All @@ -458,7 +471,7 @@ class Server {
this._reporter.update({
buildID,
bundleDetails: {
entryFile,
entryFile: resolvedEntryFilePath,
platform: transformOptions.platform,
dev: transformOptions.dev,
minify: transformOptions.minify,
Expand All @@ -473,7 +486,7 @@ class Server {
revisionId,
buildID,
bundleOptions,
entryFile,
entryFile: resolvedEntryFilePath,
transformOptions,
serializerOptions,
onProgress,
Expand Down Expand Up @@ -867,7 +880,6 @@ class Server {
async _sourceMapForURL(reqUrl: string): Promise<MetroSourceMap> {
const {options} = parseOptionsFromUrl(
reqUrl,
this._config.projectRoot,
new Set(this._config.resolver.platforms),
);

Expand All @@ -878,12 +890,25 @@ class Server {
onProgress,
} = splitBundleOptions(options);

const graphId = getGraphId(entryFile, transformOptions);
/**
* `entryFile` is relative to projectRoot, we need to use resolution function
* to find the appropriate file with supported extensions.
*/
const resolutionFn = await transformHelpers.getResolveDependencyFn(
this._bundler.getBundler(),
transformOptions.platform,
);
const resolvedEntryFilePath = resolutionFn(
`${this._config.projectRoot}/.`,
entryFile,
);

const graphId = getGraphId(resolvedEntryFilePath, transformOptions);
let revision;
const revPromise = this._bundler.getRevisionByGraphId(graphId);
if (revPromise == null) {
({revision} = await this._bundler.initializeGraph(
entryFile,
resolvedEntryFilePath,
transformOptions,
{onProgress},
));
Expand Down
33 changes: 2 additions & 31 deletions packages/metro/src/Server/__tests__/Server-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
'use strict';

const ResourceNotFoundError = require('../../IncrementalBundler/ResourceNotFoundError');
const path = require('path');

const {getDefaultValues} = require('metro-config/src/defaults');

Expand Down Expand Up @@ -209,8 +210,7 @@ describe('processRequest', () => {
transformHelpers.getTransformFn = jest.fn().mockReturnValue(() => {});
transformHelpers.getResolveDependencyFn = jest
.fn()
.mockReturnValue(() => {});

.mockReturnValue((a, b) => path.resolve(a, `${b}.js`));
let i = 0;
crypto.randomBytes.mockImplementation(() => `XXXXX-${i++}`);

Expand Down Expand Up @@ -412,26 +412,10 @@ describe('processRequest', () => {
});

it('does not rebuild the bundle when making concurrent requests', async () => {
let resolveBuildGraph;

// Delay the response of the buildGraph method.
transformHelpers.getResolveDependencyFn.mockImplementation(async () => {
return new Promise(res => (resolveBuildGraph = res));
});

const promise1 = makeRequest('index.bundle');
const promise2 = makeRequest('index.bundle');

// We must wait for all synchronous promises *before*
// `getResolveDependencyFn` to resolve before we can be sure that
// `resolveBuildGraph` has been defined.
process.nextTick(() => {
resolveBuildGraph({
entryPoints: ['/root/mybundle.js'],
dependencies,
});
});

const [result1, result2] = await Promise.all([promise1, promise2]);
expect(result1.body).toEqual(result2.body);
expect(result1.getHeader('X-Metro-Files-Changed-Count')).toEqual('3');
Expand Down Expand Up @@ -567,22 +551,9 @@ describe('processRequest', () => {
});

it('does return the same base bundle when making concurrent requests', async () => {
let resolveBuildGraph;

transformHelpers.getResolveDependencyFn.mockImplementation(async () => {
return new Promise(res => (resolveBuildGraph = res));
});

const promise1 = makeRequest('index.delta');
const promise2 = makeRequest('index.delta');

process.nextTick(() => {
resolveBuildGraph({
entryPoints: ['/root/mybundle.js'],
dependencies,
});
});

const [result1, result2] = await Promise.all([promise1, promise2]);
const {revisionId: id1, ...base1} = JSON.parse(result1.body);
const {revisionId: id2, ...base2} = JSON.parse(result2.body);
Expand Down
41 changes: 8 additions & 33 deletions packages/metro/src/lib/__tests__/parseOptionsFromUrl-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,39 +18,24 @@ jest.mock('../parseCustomTransformOptions', () => () => ({}));
describe('parseOptionsFromUrl', () => {
it.each([['map'], ['delta'], ['bundle']])('detects %s requests', type => {
expect(
parseOptionsFromUrl(
`http://localhost/my/bundle.${type}`,
'/',
new Set([]),
).options,
parseOptionsFromUrl(`http://localhost/my/bundle.${type}`, new Set([]))
.options,
).toMatchObject({bundleType: type});
});

it('resolves the entry file from the project root', () => {
expect(
parseOptionsFromUrl(
'http://localhost/my/bundle.bundle.includeRequire.runModule.assets',
'/static/bundles/',
new Set([]),
).options,
).toMatchObject({entryFile: '/static/bundles/my/bundle.js'});
});

it('removes extraneous options from the pathname', () => {
expect(
parseOptionsFromUrl(
'http://localhost/my/bundle.bundle.includeRequire.runModule.assets',
'/',
new Set([]),
).options,
).toMatchObject({entryFile: '/my/bundle.js'});
).toMatchObject({entryFile: './my/bundle'});
});

it('retrieves the platform from the query parameters', () => {
expect(
parseOptionsFromUrl(
'http://localhost/my/bundle.bundle?platform=ios',
'/',
new Set([]),
).options,
).toMatchObject({platform: 'ios'});
Expand All @@ -60,7 +45,6 @@ describe('parseOptionsFromUrl', () => {
expect(
parseOptionsFromUrl(
'http://localhost/my/bundle.test.bundle',
'/',
new Set(['test']),
).options,
).toMatchObject({platform: 'test'});
Expand All @@ -70,28 +54,26 @@ describe('parseOptionsFromUrl', () => {
expect(
parseOptionsFromUrl(
'http://localhost/my/bundle.delta?revisionId=XXX',
'/',
new Set([]),
),
).toMatchObject({revisionId: 'XXX'});

expect(
parseOptionsFromUrl(
'http://localhost/my/bundle.delta?deltaBundleId=XXX',
'/',
new Set([]),
),
).toMatchObject({revisionId: 'XXX'});
});

it('infers the source map url from the pathname', () => {
expect(
parseOptionsFromUrl('http://localhost/my/bundle.bundle', '/', new Set([]))
parseOptionsFromUrl('http://localhost/my/bundle.bundle', new Set([]))
.options,
).toMatchObject({sourceMapUrl: '//localhost/my/bundle.map'});

expect(
parseOptionsFromUrl('http://localhost/my/bundle.delta', '/', new Set([]))
parseOptionsFromUrl('http://localhost/my/bundle.delta', new Set([]))
.options,
).toMatchObject({sourceMapUrl: '//localhost/my/bundle.map'});
});
Expand All @@ -100,7 +82,6 @@ describe('parseOptionsFromUrl', () => {
expect(
parseOptionsFromUrl(
'http://localhost/my/bundle.bundle?platform=ios',
'/',
new Set(['ios']),
).options,
).toMatchObject({
Expand All @@ -110,7 +91,6 @@ describe('parseOptionsFromUrl', () => {
expect(
parseOptionsFromUrl(
'http://localhost/my/bundle.bundle?platform=android',
'/',
new Set(['android']),
).options,
).toMatchObject({
Expand All @@ -120,7 +100,7 @@ describe('parseOptionsFromUrl', () => {

it('always sets the `hot` option to `true`', () => {
expect(
parseOptionsFromUrl('http://localhost/my/bundle.bundle', '/', new Set([]))
parseOptionsFromUrl('http://localhost/my/bundle.bundle', new Set([]))
.options,
).toMatchObject({hot: true});
});
Expand All @@ -134,27 +114,22 @@ describe('parseOptionsFromUrl', () => {
])('boolean option `%s`', (optionName, defaultValue) => {
it(`defaults to \`${String(defaultValue)}\``, () => {
expect(
parseOptionsFromUrl(
'http://localhost/my/bundle.bundle',
'/',
new Set([]),
).options,
parseOptionsFromUrl('http://localhost/my/bundle.bundle', new Set([]))
.options,
).toMatchObject({[optionName]: defaultValue});
});

it('is retrieved from the url', () => {
expect(
parseOptionsFromUrl(
`http://localhost/my/bundle.bundle?${optionName}=true`,
'/',
new Set([]),
).options,
).toMatchObject({[optionName]: true});

expect(
parseOptionsFromUrl(
`http://localhost/my/bundle.bundle?${optionName}=false`,
'/',
new Set([]),
).options,
).toMatchObject({[optionName]: false});
Expand Down
Loading