react-native: attachHMRServer: make it generic

Reviewed By: cpojer

Differential Revision: D5172344

fbshipit-source-id: ab8b39e1924d66d37da9734455ed9a72cf59906e
This commit is contained in:
Jean Lauliac 2017-06-02 09:23:52 -07:00 committed by Facebook Github Bot
parent ec030d14cd
commit a829d01bfc
4 changed files with 74 additions and 18 deletions

View File

@ -11,32 +11,73 @@
'use strict';
const getInverseDependencies = require('../../../packager/src//node-haste/lib/getInverseDependencies');
const getInverseDependencies = require('./getInverseDependencies');
const querystring = require('querystring');
const url = require('url');
import type HMRBundle from '../../../packager/src/Bundler/HMRBundle';
import type Server from '../../../packager/src/Server';
import type ResolutionResponse
from '../../../packager/src/node-haste/DependencyGraph/ResolutionResponse';
import type Module from '../../../packager/src/node-haste/Module';
import type {ResolutionResponse} from './getInverseDependencies';
import type {Server as HTTPServer} from 'http';
const blacklist = [
'Libraries/Utilities/HMRClient.js',
];
type HMROptions = {
type HMRBundle = {
getModulesIdsAndCode(): Array<{id: string, code: string}>,
getSourceMappingURLs(): Array<mixed>,
getSourceURLs(): Array<mixed>,
isEmpty(): boolean,
};
type DependencyOptions = {|
+dev: boolean,
+entryFile: string,
+hot: boolean,
+minify: boolean,
+platform: ?string,
+recursive: boolean,
|};
/**
* This is a subset of the actual `metro-bundler`'s `Server` class,
* without all the stuff we don't need to know about. This allows us to use
* `attachHMRServer` with different versions of `metro-bundler` as long as
* this specific contract is maintained.
*/
type PackagerServer<TModule> = {
buildBundleForHMR(
options: {platform: ?string},
host: string,
port: number,
): Promise<HMRBundle>,
getDependencies(options: DependencyOptions): Promise<ResolutionResponse<TModule>>,
getModuleForPath(entryFile: string): Promise<TModule>,
getShallowDependencies(options: DependencyOptions): Promise<Array<TModule>>,
setHMRFileChangeListener(listener: ?(type: string, filePath: string) => mixed): void,
};
type HMROptions<TModule> = {
httpServer: HTTPServer,
packagerServer: PackagerServer<TModule>,
path: string,
};
type Moduleish = {
getName(): Promise<string>,
isAsset(): boolean,
isJSON(): boolean,
path: string,
packagerServer: Server,
};
/**
* Attaches a WebSocket based connection to the Packager to expose
* Hot Module Replacement updates to the simulator.
*/
function attachHMRServer({httpServer, path, packagerServer}: HMROptions) {
function attachHMRServer<TModule: Moduleish>(
{httpServer, path, packagerServer}: HMROptions<TModule>,
) {
let client = null;
function disconnect() {
@ -50,10 +91,10 @@ function attachHMRServer({httpServer, path, packagerServer}: HMROptions) {
// - Inverse shallow dependencies map
function getDependencies(platform: string, bundleEntry: string): Promise<{
dependenciesCache: Array<string>,
dependenciesModulesCache: {[mixed]: Module},
shallowDependencies: {[string]: Array<Module>},
dependenciesModulesCache: {[mixed]: TModule},
shallowDependencies: {[string]: Array<TModule>},
inverseDependenciesCache: mixed,
resolutionResponse: ResolutionResponse<Module, *>,
resolutionResponse: ResolutionResponse<TModule>,
}> {
return packagerServer.getDependencies({
dev: true,
@ -68,7 +109,7 @@ function attachHMRServer({httpServer, path, packagerServer}: HMROptions) {
// for each dependency builds the object:
// `{path: '/a/b/c.js', deps: ['modA', 'modB', ...]}`
return Promise.all(response.dependencies.map((dep: Module) => {
return Promise.all(response.dependencies.map((dep: TModule) => {
return dep.getName().then(depName => {
if (dep.isAsset() || dep.isJSON()) {
return Promise.resolve({path: dep.path, deps: []});
@ -89,7 +130,7 @@ function attachHMRServer({httpServer, path, packagerServer}: HMROptions) {
});
});
}))
.then((deps: Array<{path: string, name?: string, deps: Array<Module>}>) => {
.then((deps: Array<{path: string, name?: string, deps: Array<TModule>}>) => {
// list with all the dependencies' filenames the bundle entry has
const dependenciesCache = response.dependencies.map(dep => dep.path);

View File

@ -12,10 +12,25 @@
'use strict';
import type ResolutionResponse from '../DependencyGraph/ResolutionResponse';
/**
* This is a subset of the actual `metro-bundler`'s `ResolutionResponse` class,
* without all the stuff we don't need to know about. This allows us to use
* `getInverseDependencies` with different versions of `metro-bundler`.
*/
export type ResolutionResponse<TModule> = {
copy(data: {
dependencies?: Array<TModule>,
mainModuleId?: number,
mocks?: mixed,
}): ResolutionResponse<TModule>,
dependencies: Array<TModule>,
getResolvedDependencyPairs(
module: TModule,
): $ReadOnlyArray<[string, TModule]>,
};
function resolveModuleRequires<TModule: {hash(): string}, TOptions>(
resolutionResponse: ResolutionResponse<TModule, TOptions>,
function resolveModuleRequires<TModule>(
resolutionResponse: ResolutionResponse<TModule>,
module: TModule,
): Array<TModule> {
const pairs = resolutionResponse.getResolvedDependencyPairs(module);
@ -37,8 +52,8 @@ function getModuleDependents<TModule>(
/**
* Returns an object that indicates in which module each module is required.
*/
function getInverseDependencies<TModule: {hash(): string}, TOptions>(
resolutionResponse: ResolutionResponse<TModule, TOptions>,
function getInverseDependencies<TModule>(
resolutionResponse: ResolutionResponse<TModule>,
): Map<TModule, Set<TModule>> {
const cache = new Map();

View File

@ -67,7 +67,7 @@ class HMRBundle extends BundleBase {
return (Promise.resolve(): any);
}
getModulesIdsAndCode() {
getModulesIdsAndCode(): Array<{id: string, code: string}> {
return this.__modules.map(module => {
return {
id: JSON.stringify(module.id),