mirror of https://github.com/status-im/metro.git
Use the new Graph object for generating HMR bundles
Reviewed By: jeanlauliac Differential Revision: D7275598 fbshipit-source-id: 912a60ebce7ccc291d138c6f1ef8b0fea2d5712b
This commit is contained in:
parent
395e0494a6
commit
9bae90b2b8
|
@ -0,0 +1,108 @@
|
||||||
|
/**
|
||||||
|
* 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 addParamsToDefineCall = require('../../lib/addParamsToDefineCall');
|
||||||
|
|
||||||
|
const {wrapModule} = require('./helpers/js');
|
||||||
|
|
||||||
|
import type {Delta, Graph} from '../';
|
||||||
|
import type {DependencyEdge} from '../traverseDependencies';
|
||||||
|
|
||||||
|
type Options = {
|
||||||
|
createModuleId: string => number,
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Result = {
|
||||||
|
type: string,
|
||||||
|
body: {
|
||||||
|
modules: $ReadOnlyArray<{|+id: number, +code: string|}>,
|
||||||
|
sourceURLs: {},
|
||||||
|
sourceMappingURLs: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function hmrJSBundle(delta: Delta, graph: Graph, options: Options): Result {
|
||||||
|
const modules = [];
|
||||||
|
|
||||||
|
for (const module of delta.modified.values()) {
|
||||||
|
modules.push(_prepareModule(module, graph, options));
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'update',
|
||||||
|
body: {
|
||||||
|
modules,
|
||||||
|
sourceURLs: {},
|
||||||
|
sourceMappingURLs: {}, // TODO: handle Source Maps
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function _prepareModule(
|
||||||
|
module: DependencyEdge,
|
||||||
|
graph: Graph,
|
||||||
|
options: Options,
|
||||||
|
): {|+id: number, +code: string|} {
|
||||||
|
const code = wrapModule(module, {
|
||||||
|
createModuleId: options.createModuleId,
|
||||||
|
dev: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const inverseDependencies = _getInverseDependencies(module.path, graph);
|
||||||
|
|
||||||
|
// Transform the inverse dependency paths to ids.
|
||||||
|
const inverseDependenciesById = Object.create(null);
|
||||||
|
Object.keys(inverseDependencies).forEach(path => {
|
||||||
|
inverseDependenciesById[options.createModuleId(path)] = inverseDependencies[
|
||||||
|
path
|
||||||
|
].map(options.createModuleId);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: options.createModuleId(module.path),
|
||||||
|
code: addParamsToDefineCall(code, inverseDependenciesById),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instead of adding the whole inverseDependncies object into each changed
|
||||||
|
* module (which can be really huge if the dependency graph is big), we only
|
||||||
|
* add the needed inverseDependencies for each changed module (we do this by
|
||||||
|
* traversing upwards the dependency graph).
|
||||||
|
*/
|
||||||
|
function _getInverseDependencies(
|
||||||
|
path: string,
|
||||||
|
graph: Graph,
|
||||||
|
inverseDependencies: {[key: string]: Array<string>} = {},
|
||||||
|
): {[key: string]: Array<string>} {
|
||||||
|
// Dependency alredy traversed.
|
||||||
|
if (path in inverseDependencies) {
|
||||||
|
return inverseDependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
const module = graph.dependencies.get(path);
|
||||||
|
if (!module) {
|
||||||
|
return inverseDependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
inverseDependencies[path] = [];
|
||||||
|
|
||||||
|
for (const inverse of module.inverseDependencies) {
|
||||||
|
inverseDependencies[path].push(inverse);
|
||||||
|
|
||||||
|
_getInverseDependencies(inverse, graph, inverseDependencies);
|
||||||
|
}
|
||||||
|
|
||||||
|
return inverseDependencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = hmrJSBundle;
|
|
@ -13,26 +13,29 @@ jest.mock('../../lib/getAbsolutePath');
|
||||||
|
|
||||||
const HmrServer = require('..');
|
const HmrServer = require('..');
|
||||||
|
|
||||||
const {EventEmitter} = require('events');
|
|
||||||
|
|
||||||
describe('HmrServer', () => {
|
describe('HmrServer', () => {
|
||||||
let hmrServer;
|
let hmrServer;
|
||||||
let serverMock;
|
let serverMock;
|
||||||
|
let buildGraphMock;
|
||||||
let deltaBundlerMock;
|
let deltaBundlerMock;
|
||||||
let deltaTransformerMock;
|
let callbacks;
|
||||||
let getDeltaTransformerMock;
|
let mockedGraph;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
deltaTransformerMock = new EventEmitter();
|
mockedGraph = {
|
||||||
deltaTransformerMock.getDelta = jest.fn().mockReturnValue({id: '1234'});
|
dependencies: new Map(),
|
||||||
deltaTransformerMock.getInverseDependencies = jest.fn();
|
entryPoint: 'EntryPoint.js',
|
||||||
|
};
|
||||||
|
|
||||||
getDeltaTransformerMock = jest
|
buildGraphMock = jest.fn().mockReturnValue(mockedGraph);
|
||||||
.fn()
|
|
||||||
.mockReturnValue(Promise.resolve(deltaTransformerMock));
|
callbacks = new Map();
|
||||||
|
|
||||||
deltaBundlerMock = {
|
deltaBundlerMock = {
|
||||||
getDeltaTransformer: getDeltaTransformerMock,
|
buildGraph: buildGraphMock,
|
||||||
|
listen: (graph, cb) => {
|
||||||
|
callbacks.set(graph, cb);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
serverMock = {
|
serverMock = {
|
||||||
getDeltaBundler() {
|
getDeltaBundler() {
|
||||||
|
@ -46,6 +49,11 @@ describe('HmrServer', () => {
|
||||||
getProjectRoots() {
|
getProjectRoots() {
|
||||||
return ['/root'];
|
return ['/root'];
|
||||||
},
|
},
|
||||||
|
_opts: {
|
||||||
|
createModuleId(path) {
|
||||||
|
return path + '-id';
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
hmrServer = new HmrServer(serverMock);
|
hmrServer = new HmrServer(serverMock);
|
||||||
|
@ -57,29 +65,17 @@ describe('HmrServer', () => {
|
||||||
jest.fn(),
|
jest.fn(),
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(getDeltaTransformerMock).toBeCalledWith(
|
expect(buildGraphMock).toBeCalledWith(
|
||||||
'/hot?bundleEntry=EntryPoint.js&platform=ios',
|
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
deltaBundleId: null,
|
|
||||||
dev: true,
|
dev: true,
|
||||||
entryFile: '/root/EntryPoint.js',
|
entryPoints: ['/root/EntryPoint.js'],
|
||||||
minify: false,
|
minify: false,
|
||||||
platform: 'ios',
|
platform: 'ios',
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should generate an initial delta when a client is connected', async () => {
|
it('should return the correctly formatted HMR message after a file change', async () => {
|
||||||
await hmrServer.onClientConnect(
|
|
||||||
'/hot?bundleEntry=EntryPoint.js&platform=ios',
|
|
||||||
jest.fn(),
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(deltaTransformerMock.getDelta).toBeCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return the correctly formatted HMR message after a file change', async done => {
|
|
||||||
jest.useRealTimers();
|
|
||||||
const sendMessage = jest.fn();
|
const sendMessage = jest.fn();
|
||||||
|
|
||||||
await hmrServer.onClientConnect(
|
await hmrServer.onClientConnect(
|
||||||
|
@ -87,51 +83,51 @@ describe('HmrServer', () => {
|
||||||
sendMessage,
|
sendMessage,
|
||||||
);
|
);
|
||||||
|
|
||||||
deltaTransformerMock.getDelta.mockReturnValue(
|
deltaBundlerMock.getDelta = jest.fn().mockReturnValue(
|
||||||
Promise.resolve({
|
Promise.resolve({
|
||||||
delta: new Map([[1, {code: '__d(function() { alert("hi"); });'}]]),
|
modified: new Map([
|
||||||
|
[
|
||||||
|
'/hi',
|
||||||
|
{
|
||||||
|
dependencies: new Map(),
|
||||||
|
inverseDependencies: new Set(),
|
||||||
|
path: '/hi',
|
||||||
|
output: {
|
||||||
|
code: '__d(function() { alert("hi"); });',
|
||||||
|
type: 'module',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
]),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
deltaTransformerMock.getInverseDependencies.mockReturnValue(
|
|
||||||
Promise.resolve(
|
|
||||||
new Map([
|
|
||||||
[1, [2, 3]],
|
|
||||||
[2, []],
|
|
||||||
[3, [4]],
|
|
||||||
[4, []],
|
|
||||||
[5, [1, 2, 3]], // this shouldn't be added to the response
|
|
||||||
]),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
deltaTransformerMock.emit('change');
|
await callbacks.get(mockedGraph)();
|
||||||
|
|
||||||
setTimeout(function() {
|
expect(sendMessage.mock.calls.map(call => JSON.parse(call[0]))).toEqual([
|
||||||
expect(JSON.parse(sendMessage.mock.calls[0][0])).toEqual({
|
{
|
||||||
type: 'update-start',
|
type: 'update-start',
|
||||||
});
|
},
|
||||||
expect(JSON.parse(sendMessage.mock.calls[1][0])).toEqual({
|
{
|
||||||
type: 'update',
|
type: 'update',
|
||||||
body: {
|
body: {
|
||||||
modules: [
|
modules: [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: '/hi-id',
|
||||||
code:
|
code: '__d(function() { alert("hi"); },"/hi-id",[],"hi",{});',
|
||||||
'__d(function() { alert("hi"); },{"1":[2,3],"2":[],"3":[4],"4":[]});',
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
sourceURLs: {},
|
sourceURLs: {},
|
||||||
sourceMappingURLs: {},
|
sourceMappingURLs: {},
|
||||||
},
|
},
|
||||||
});
|
},
|
||||||
expect(JSON.parse(sendMessage.mock.calls[2][0])).toEqual({
|
{
|
||||||
type: 'update-done',
|
type: 'update-done',
|
||||||
});
|
},
|
||||||
done();
|
]);
|
||||||
}, 30);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return error messages when there is a transform error', async done => {
|
it('should return error messages when there is a transform error', async () => {
|
||||||
jest.useRealTimers();
|
jest.useRealTimers();
|
||||||
const sendMessage = jest.fn();
|
const sendMessage = jest.fn();
|
||||||
|
|
||||||
|
@ -140,7 +136,7 @@ describe('HmrServer', () => {
|
||||||
sendMessage,
|
sendMessage,
|
||||||
);
|
);
|
||||||
|
|
||||||
deltaTransformerMock.getDelta.mockImplementation(async () => {
|
deltaBundlerMock.getDelta = jest.fn().mockImplementation(async () => {
|
||||||
const transformError = new SyntaxError('test syntax error');
|
const transformError = new SyntaxError('test syntax error');
|
||||||
transformError.type = 'TransformError';
|
transformError.type = 'TransformError';
|
||||||
transformError.filename = 'EntryPoint.js';
|
transformError.filename = 'EntryPoint.js';
|
||||||
|
@ -148,9 +144,8 @@ describe('HmrServer', () => {
|
||||||
throw transformError;
|
throw transformError;
|
||||||
});
|
});
|
||||||
|
|
||||||
deltaTransformerMock.emit('change');
|
await callbacks.get(mockedGraph)();
|
||||||
|
|
||||||
setTimeout(function() {
|
|
||||||
expect(JSON.parse(sendMessage.mock.calls[0][0])).toEqual({
|
expect(JSON.parse(sendMessage.mock.calls[0][0])).toEqual({
|
||||||
type: 'update-start',
|
type: 'update-start',
|
||||||
});
|
});
|
||||||
|
@ -170,7 +165,5 @@ describe('HmrServer', () => {
|
||||||
expect(JSON.parse(sendMessage.mock.calls[2][0])).toEqual({
|
expect(JSON.parse(sendMessage.mock.calls[2][0])).toEqual({
|
||||||
type: 'update-done',
|
type: 'update-done',
|
||||||
});
|
});
|
||||||
done();
|
|
||||||
}, 30);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,52 +0,0 @@
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
* @format
|
|
||||||
* @flow
|
|
||||||
*/
|
|
||||||
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
import type {CustomTransformOptions} from '../JSTransformer/worker';
|
|
||||||
import type {BundleOptions} from '../shared/types.flow';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Module to easily create the needed configuration parameters needed for the
|
|
||||||
* bundler for HMR (since a lot of params are not relevant in this use case).
|
|
||||||
*/
|
|
||||||
module.exports = function getBundlingOptionsForHmr(
|
|
||||||
entryFile: string,
|
|
||||||
platform: string,
|
|
||||||
customTransformOptions: CustomTransformOptions,
|
|
||||||
): BundleOptions {
|
|
||||||
// These are the really meaningful bundling options. The others below are
|
|
||||||
// not relevant for HMR.
|
|
||||||
const mainOptions = {
|
|
||||||
deltaBundleId: null,
|
|
||||||
entryFile,
|
|
||||||
hot: true,
|
|
||||||
minify: false,
|
|
||||||
platform,
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
...mainOptions,
|
|
||||||
assetPlugins: [],
|
|
||||||
bundleType: 'hmr',
|
|
||||||
customTransformOptions,
|
|
||||||
dev: true,
|
|
||||||
entryModuleOnly: false,
|
|
||||||
excludeSource: false,
|
|
||||||
inlineSourceMap: false,
|
|
||||||
isolateModuleIDs: false,
|
|
||||||
onProgress: null,
|
|
||||||
resolutionResponse: null,
|
|
||||||
runBeforeMainModule: [],
|
|
||||||
runModule: false,
|
|
||||||
sourceMapUrl: '',
|
|
||||||
unbundle: false,
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -10,10 +10,9 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const addParamsToDefineCall = require('../lib/addParamsToDefineCall');
|
|
||||||
const formatBundlingError = require('../lib/formatBundlingError');
|
const formatBundlingError = require('../lib/formatBundlingError');
|
||||||
const getAbsolutePath = require('../lib/getAbsolutePath');
|
const getAbsolutePath = require('../lib/getAbsolutePath');
|
||||||
const getBundlingOptionsForHmr = require('./getBundlingOptionsForHmr');
|
const hmrJSBundle = require('../DeltaBundler/Serializers/hmrJSBundle');
|
||||||
const nullthrows = require('fbjs/lib/nullthrows');
|
const nullthrows = require('fbjs/lib/nullthrows');
|
||||||
const parseCustomTransformOptions = require('../lib/parseCustomTransformOptions');
|
const parseCustomTransformOptions = require('../lib/parseCustomTransformOptions');
|
||||||
const url = require('url');
|
const url = require('url');
|
||||||
|
@ -22,13 +21,12 @@ const {
|
||||||
Logger: {createActionStartEntry, createActionEndEntry, log},
|
Logger: {createActionStartEntry, createActionEndEntry, log},
|
||||||
} = require('metro-core');
|
} = require('metro-core');
|
||||||
|
|
||||||
import type DeltaTransformer from '../DeltaBundler/DeltaTransformer';
|
import type {Graph} from '../DeltaBundler/DeltaCalculator';
|
||||||
import type PackagerServer from '../Server';
|
import type PackagerServer from '../Server';
|
||||||
import type {Reporter} from '../lib/reporting';
|
import type {Reporter} from '../lib/reporting';
|
||||||
|
|
||||||
type Client = {|
|
type Client = {|
|
||||||
clientId: string,
|
graph: Graph,
|
||||||
deltaTransformer: DeltaTransformer,
|
|
||||||
sendFn: (data: string) => mixed,
|
sendFn: (data: string) => mixed,
|
||||||
|};
|
|};
|
||||||
|
|
||||||
|
@ -44,7 +42,6 @@ type Client = {|
|
||||||
class HmrServer<TClient: Client> {
|
class HmrServer<TClient: Client> {
|
||||||
_packagerServer: PackagerServer;
|
_packagerServer: PackagerServer;
|
||||||
_reporter: Reporter;
|
_reporter: Reporter;
|
||||||
_lastSequenceId: ?string;
|
|
||||||
|
|
||||||
constructor(packagerServer: PackagerServer) {
|
constructor(packagerServer: PackagerServer) {
|
||||||
this._packagerServer = packagerServer;
|
this._packagerServer = packagerServer;
|
||||||
|
@ -65,23 +62,24 @@ class HmrServer<TClient: Client> {
|
||||||
// DeltaBundleId param through the WS connection and we'll be able to share
|
// DeltaBundleId param through the WS connection and we'll be able to share
|
||||||
// the same DeltaTransformer between the WS connection and the HTTP one.
|
// the same DeltaTransformer between the WS connection and the HTTP one.
|
||||||
const deltaBundler = this._packagerServer.getDeltaBundler();
|
const deltaBundler = this._packagerServer.getDeltaBundler();
|
||||||
const deltaTransformer = await deltaBundler.getDeltaTransformer(
|
const graph = await deltaBundler.buildGraph({
|
||||||
clientUrl,
|
assetPlugins: [],
|
||||||
getBundlingOptionsForHmr(
|
|
||||||
getAbsolutePath(bundleEntry, this._packagerServer.getProjectRoots()),
|
|
||||||
platform,
|
|
||||||
customTransformOptions,
|
customTransformOptions,
|
||||||
),
|
dev: true,
|
||||||
);
|
entryPoints: [
|
||||||
|
getAbsolutePath(bundleEntry, this._packagerServer.getProjectRoots()),
|
||||||
// Trigger an initial build to start up the DeltaTransformer.
|
],
|
||||||
const {id} = await deltaTransformer.getDelta();
|
hot: true,
|
||||||
|
minify: false,
|
||||||
this._lastSequenceId = id;
|
onProgress: null,
|
||||||
|
platform,
|
||||||
|
type: 'module',
|
||||||
|
});
|
||||||
|
|
||||||
// Listen to file changes.
|
// Listen to file changes.
|
||||||
const client = {clientId: clientUrl, deltaTransformer, sendFn};
|
const client = {sendFn, graph};
|
||||||
deltaTransformer.on('change', this._handleFileChange.bind(this, client));
|
|
||||||
|
deltaBundler.listen(graph, this._handleFileChange.bind(this, client));
|
||||||
|
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
@ -97,7 +95,7 @@ class HmrServer<TClient: Client> {
|
||||||
onClientDisconnect(client: TClient) {
|
onClientDisconnect(client: TClient) {
|
||||||
// We can safely stop the delta transformer since the
|
// We can safely stop the delta transformer since the
|
||||||
// transformer is not shared between clients.
|
// transformer is not shared between clients.
|
||||||
this._packagerServer.getDeltaBundler().endTransformer(client.clientId);
|
this._packagerServer.getDeltaBundler().endGraph(client.graph);
|
||||||
}
|
}
|
||||||
|
|
||||||
async _handleFileChange(client: Client) {
|
async _handleFileChange(client: Client) {
|
||||||
|
@ -122,11 +120,17 @@ class HmrServer<TClient: Client> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async _prepareResponse(client: Client): Promise<{type: string, body: {}}> {
|
async _prepareResponse(
|
||||||
let result;
|
client: Client,
|
||||||
|
): Promise<{type: string, body: Object}> {
|
||||||
|
const deltaBundler = this._packagerServer.getDeltaBundler();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
result = await client.deltaTransformer.getDelta(this._lastSequenceId);
|
const delta = await deltaBundler.getDelta(client.graph, {reset: false});
|
||||||
|
|
||||||
|
return hmrJSBundle(delta, client.graph, {
|
||||||
|
createModuleId: this._packagerServer._opts.createModuleId,
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const formattedError = formatBundlingError(error);
|
const formattedError = formatBundlingError(error);
|
||||||
|
|
||||||
|
@ -134,104 +138,6 @@ class HmrServer<TClient: Client> {
|
||||||
|
|
||||||
return {type: 'error', body: formattedError};
|
return {type: 'error', body: formattedError};
|
||||||
}
|
}
|
||||||
const modules = [];
|
|
||||||
|
|
||||||
const inverseDependencies = await client.deltaTransformer.getInverseDependencies();
|
|
||||||
|
|
||||||
for (const [id, module] of result.delta) {
|
|
||||||
// The Delta Bundle can have null objects: these correspond to deleted
|
|
||||||
// modules, which we don't need to send to the client.
|
|
||||||
if (module != null) {
|
|
||||||
// When there are new modules added on the dependency graph, the delta
|
|
||||||
// bundler returns them first, so the HMR logic does not need to worry
|
|
||||||
// about sorting modules when passing them to the client.
|
|
||||||
modules.push(this._prepareModule(id, module.code, inverseDependencies));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this._lastSequenceId = result.id;
|
|
||||||
|
|
||||||
return {
|
|
||||||
type: 'update',
|
|
||||||
body: {
|
|
||||||
modules,
|
|
||||||
sourceURLs: {},
|
|
||||||
sourceMappingURLs: {}, // TODO: handle Source Maps
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* We need to add the inverse dependencies of that specific module into
|
|
||||||
* the define() call, to make the HMR logic in the client able to propagate
|
|
||||||
* the changes to the module dependants, if needed.
|
|
||||||
*
|
|
||||||
* To do so, we need to append the inverse dependencies object as the last
|
|
||||||
* parameter to the __d() call from the code that we get from the bundler.
|
|
||||||
*
|
|
||||||
* So, we need to transform this:
|
|
||||||
*
|
|
||||||
* __d(
|
|
||||||
* function(global, ...) { (module transformed code) },
|
|
||||||
* moduleId,
|
|
||||||
* dependencyMap?,
|
|
||||||
* moduleName?
|
|
||||||
* );
|
|
||||||
*
|
|
||||||
* Into this:
|
|
||||||
*
|
|
||||||
* __d(
|
|
||||||
* function(global, ...) { (module transformed code) },
|
|
||||||
* moduleId,
|
|
||||||
* dependencyMap?,
|
|
||||||
* moduleName?,
|
|
||||||
* inverseDependencies,
|
|
||||||
* );
|
|
||||||
*/
|
|
||||||
_prepareModule(
|
|
||||||
id: number,
|
|
||||||
code: string,
|
|
||||||
inverseDependencies: Map<number, $ReadOnlyArray<number>>,
|
|
||||||
): {id: number, code: string} {
|
|
||||||
const moduleInverseDependencies = Object.create(null);
|
|
||||||
|
|
||||||
this._addInverseDep(id, inverseDependencies, moduleInverseDependencies);
|
|
||||||
|
|
||||||
return {
|
|
||||||
id,
|
|
||||||
code: addParamsToDefineCall(code, moduleInverseDependencies),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instead of adding the whole inverseDependncies object into each changed
|
|
||||||
* module (which can be really huge if the dependency graph is big), we only
|
|
||||||
* add the needed inverseDependencies for each changed module (we do this by
|
|
||||||
* traversing upwards the dependency graph).
|
|
||||||
*/
|
|
||||||
_addInverseDep(
|
|
||||||
module: number,
|
|
||||||
inverseDependencies: Map<number, $ReadOnlyArray<number>>,
|
|
||||||
moduleInverseDependencies: {
|
|
||||||
[key: number]: Array<number>,
|
|
||||||
__proto__: null,
|
|
||||||
},
|
|
||||||
) {
|
|
||||||
if (module in moduleInverseDependencies) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
moduleInverseDependencies[module] = [];
|
|
||||||
|
|
||||||
for (const inverse of inverseDependencies.get(module) || []) {
|
|
||||||
moduleInverseDependencies[module].push(inverse);
|
|
||||||
|
|
||||||
this._addInverseDep(
|
|
||||||
inverse,
|
|
||||||
inverseDependencies,
|
|
||||||
moduleInverseDependencies,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue