packager: MapWithDefaults: @flow

Summary:
It was hard to type the resolution main algo, I had to put type annotations explicitely everywhere, otherwise Flow would get in some kind of loop and do weird errors. I think it's because the algo is recursive and Flow tries to infer types too deeply because of the generics.

Anyway, apart from that it's good to get this extra type security in the few other places. We require Node v4 minimum, that according to the internets supports the `class` syntax without transform, and I verified that inheriting from `Map` actually works as expected.

Reviewed By: davidaurelio

Differential Revision: D5078023

fbshipit-source-id: 05dfc4acf5b07cdda8a7b36ec9cba216d1810643
This commit is contained in:
Jean Lauliac 2017-05-17 09:21:25 -07:00 коммит произвёл Facebook Github Bot
Родитель 0357303f5b
Коммит e6bc8ea10a
8 изменённых файлов: 60 добавлений и 30 удалений

Просмотреть файл

@ -642,7 +642,7 @@ class Bundler {
entryFilePath: string,
options: BundlingOptions,
getModuleId: () => number,
dependencyPairs: Array<[mixed, {path: string}]>,
dependencyPairs: Array<[string, Module]>,
assetPlugins: Array<string>,
}): Promise<ModuleTransport> {
let moduleTransport;

Просмотреть файл

@ -552,6 +552,7 @@ class Server {
changedModules.forEach(m => {
response.setResolvedDependencyPairs(
m,
/* $FlowFixMe: should be enforced not to be null. */
dependencyPairs.get(m.path),
{ignoreFinalized: true},
);

Просмотреть файл

@ -12,13 +12,14 @@
'use strict';
import type {RawMapping} from '../Bundler/source-map';
import type Module from '../node-haste/Module';
import type {SourceMap} from './SourceMap';
type SourceMapOrMappings = SourceMap | Array<RawMapping>;
type Metadata = {
dependencies?: ?Array<string>,
dependencyPairs?: Array<[mixed, {path: string}]>,
dependencyPairs?: Array<[string, Module]>,
preloaded: ?boolean,
};

Просмотреть файл

@ -214,9 +214,10 @@ class ResolutionRequest<TModule: Moduleish, TPackage: Packageish> {
);
});
const collectedDependencies = new MapWithDefaults(module =>
collect(module),
);
const collectedDependencies: MapWithDefaults<
TModule,
Promise<Array<TModule>>,
> = new MapWithDefaults(module => collect(module));
const crawlDependencies = (mod, [depNames, dependencies]) => {
const filteredPairs = [];

Просмотреть файл

@ -11,8 +11,6 @@
'use strict';
import type Module from '../Module';
const NO_OPTIONS = {};
class ResolutionResponse<TModule: {hash(): string}, TOptions> {
@ -26,7 +24,7 @@ class ResolutionResponse<TModule: {hash(): string}, TOptions> {
// This is monkey-patched from Resolver.
getModuleId: ?() => number;
_mappings: {};
_mappings: {[hash: string]: Array<[string, TModule]>};
_finalized: boolean;
_mainModule: ?TModule;
@ -104,8 +102,8 @@ class ResolutionResponse<TModule: {hash(): string}, TOptions> {
}
setResolvedDependencyPairs(
module: Module,
pairs: mixed,
module: TModule,
pairs: Array<[string, TModule]>,
options: {ignoreFinalized?: boolean} = NO_OPTIONS,
) {
if (!options.ignoreFinalized) {
@ -121,7 +119,7 @@ class ResolutionResponse<TModule: {hash(): string}, TOptions> {
this.mocks = mocks;
}
getResolvedDependencyPairs(module: TModule) {
getResolvedDependencyPairs(module: TModule): $ReadOnlyArray<[string, TModule]> {
this._assertFinalized();
return this._mappings[module.hash()];
}

Просмотреть файл

@ -1,30 +1,35 @@
/**
/**
* Copyright (c) 2016-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @flow
* @format
*/
'use strict';
module.exports = function MapWithDefaults(factory, iterable) {
// This can't be `MapWithDefaults extends Map`, b/c the way babel transforms
// super calls in constructors: Map.call(this, iterable) throws for native
// Map objects in node 4+.
// TODO(davidaurelio) switch to a transform that does not transform classes
// and super calls, and change this into a class
class MapWithDefaults<TK, TV> extends Map<TK, TV> {
_factory: TK => TV;
const map = iterable ? new Map(iterable) : new Map();
const {get} = map;
map.get = key => {
if (map.has(key)) {
return get.call(map, key);
constructor(factory: TK => TV, iterable?: Iterable<[TK, TV]>) {
super(iterable);
this._factory = factory;
}
get(key: TK): TV {
if (this.has(key)) {
/* $FlowFixMe: can never be `undefined` since we tested with `has`
* (except if `TV` includes `void` as subtype, ex. is nullable) */
return Map.prototype.get.call(this, key);
}
const value = factory(key);
map.set(key, value);
const value = this._factory(key);
this.set(key, value);
return value;
};
return map;
};
}
}
module.exports = MapWithDefaults;

Просмотреть файл

@ -0,0 +1,24 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @format
*/
'use strict';
jest.disableAutomock();
const MapWithDefaults = require('../MapWithDefaults');
describe('MapWithDefaults', function() {
it('works', () => {
const map = new MapWithDefaults(() => ['bar']);
map.get('foo').push('baz');
expect(map.get('foo')).toEqual(['bar', 'baz']);
});
});

Просмотреть файл

@ -17,7 +17,7 @@ import type ResolutionResponse from '../DependencyGraph/ResolutionResponse';
function resolveModuleRequires<TModule: {hash(): string}, TOptions>(
resolutionResponse: ResolutionResponse<TModule, TOptions>,
module: TModule,
) {
): Array<TModule> {
const pairs = resolutionResponse.getResolvedDependencyPairs(module);
return pairs ? pairs.map(([, dependencyModule]) => dependencyModule) : [];
}