diff --git a/packager/src/node-haste/lib/__tests__/getInverseDependencies-test.js b/local-cli/server/util/__tests__/getInverseDependencies-test.js similarity index 100% rename from packager/src/node-haste/lib/__tests__/getInverseDependencies-test.js rename to local-cli/server/util/__tests__/getInverseDependencies-test.js diff --git a/local-cli/server/util/attachHMRServer.js b/local-cli/server/util/attachHMRServer.js index ff35f4231..e27b3c182 100644 --- a/local-cli/server/util/attachHMRServer.js +++ b/local-cli/server/util/attachHMRServer.js @@ -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, + getSourceURLs(): Array, + 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 = { + buildBundleForHMR( + options: {platform: ?string}, + host: string, + port: number, + ): Promise, + getDependencies(options: DependencyOptions): Promise>, + getModuleForPath(entryFile: string): Promise, + getShallowDependencies(options: DependencyOptions): Promise>, + setHMRFileChangeListener(listener: ?(type: string, filePath: string) => mixed): void, +}; + +type HMROptions = { httpServer: HTTPServer, + packagerServer: PackagerServer, + path: string, +}; + +type Moduleish = { + getName(): Promise, + 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( + {httpServer, path, packagerServer}: HMROptions, +) { 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, - dependenciesModulesCache: {[mixed]: Module}, - shallowDependencies: {[string]: Array}, + dependenciesModulesCache: {[mixed]: TModule}, + shallowDependencies: {[string]: Array}, inverseDependenciesCache: mixed, - resolutionResponse: ResolutionResponse, + resolutionResponse: ResolutionResponse, }> { 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}>) => { + .then((deps: Array<{path: string, name?: string, deps: Array}>) => { // list with all the dependencies' filenames the bundle entry has const dependenciesCache = response.dependencies.map(dep => dep.path); diff --git a/packager/src/node-haste/lib/getInverseDependencies.js b/local-cli/server/util/getInverseDependencies.js similarity index 62% rename from packager/src/node-haste/lib/getInverseDependencies.js rename to local-cli/server/util/getInverseDependencies.js index 663208ee7..2c568cb4a 100644 --- a/packager/src/node-haste/lib/getInverseDependencies.js +++ b/local-cli/server/util/getInverseDependencies.js @@ -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 = { + copy(data: { + dependencies?: Array, + mainModuleId?: number, + mocks?: mixed, + }): ResolutionResponse, + dependencies: Array, + getResolvedDependencyPairs( + module: TModule, + ): $ReadOnlyArray<[string, TModule]>, +}; -function resolveModuleRequires( - resolutionResponse: ResolutionResponse, +function resolveModuleRequires( + resolutionResponse: ResolutionResponse, module: TModule, ): Array { const pairs = resolutionResponse.getResolvedDependencyPairs(module); @@ -37,8 +52,8 @@ function getModuleDependents( /** * Returns an object that indicates in which module each module is required. */ -function getInverseDependencies( - resolutionResponse: ResolutionResponse, +function getInverseDependencies( + resolutionResponse: ResolutionResponse, ): Map> { const cache = new Map(); diff --git a/packager/src/Bundler/HMRBundle.js b/packager/src/Bundler/HMRBundle.js index 5953f7f71..db3b0ac3a 100644 --- a/packager/src/Bundler/HMRBundle.js +++ b/packager/src/Bundler/HMRBundle.js @@ -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),