packager: Resolver&co: @flow

Reviewed By: davidaurelio

Differential Revision: D4285448

fbshipit-source-id: ec9bc6fd3c6d6eae89347762bff8b5b24662394b
This commit is contained in:
Jean Lauliac 2016-12-08 05:10:37 -08:00 committed by Martin Konicek
parent 85478b56c5
commit b6478a3458
9 changed files with 122 additions and 55 deletions

View File

@ -52,7 +52,7 @@ type GeneratorOptions = {
type InlinePlugin = [string | {} | () => {}, any]; type InlinePlugin = [string | {} | () => {}, any];
// based on https://babeljs.io/docs/usage/options/ -- 2016-11-11 // based on https://babeljs.io/docs/usage/options/ -- 2016-11-11
type _TransformOptions = { type __TransformOptions = {
filename?: string, filename?: string,
filenameRelative?: string, filenameRelative?: string,
presets?: Array<string | Object>, presets?: Array<string | Object>,
@ -85,8 +85,8 @@ type _TransformOptions = {
extends?: string, extends?: string,
}; };
type TransformOptions = type _TransformOptions =
_TransformOptions & {env?: {[key: string]: TransformOptions}}; __TransformOptions & {env?: {[key: string]: __TransformOptions}};
declare class _Ast {}; declare class _Ast {};
type TransformResult = { type TransformResult = {
ast: _Ast, ast: _Ast,
@ -98,9 +98,10 @@ type VisitFn = <State>(path: Object, state: State) => any;
declare module 'babel-core' { declare module 'babel-core' {
declare type SourceMap = _SourceMap; declare type SourceMap = _SourceMap;
declare type Ast = _Ast; declare type Ast = _Ast;
declare type TransformOptions = _TransformOptions;
declare function transform( declare function transform(
code: string, code: string,
options?: TransformOptions, options?: _TransformOptions,
): TransformResult; ): TransformResult;
declare function traverse<State>( declare function traverse<State>(
ast: _Ast, ast: _Ast,
@ -114,7 +115,7 @@ declare module 'babel-core' {
declare function transformFromAst( declare function transformFromAst(
ast: _Ast, ast: _Ast,
code?: ?string, code?: ?string,
babelOptions?: TransformOptions, babelOptions?: _TransformOptions,
): TransformResult; ): TransformResult;
} }

View File

@ -35,9 +35,10 @@ const {
extname, extname,
} = require('path'); } = require('path');
import AssetServer from '../AssetServer'; import type AssetServer from '../AssetServer';
import Module from '../node-haste/Module'; import type Module from '../node-haste/Module';
import ResolutionResponse from '../node-haste/DependencyGraph/ResolutionResponse'; import type ResolutionResponse from '../node-haste/DependencyGraph/ResolutionResponse';
import type {Options as TransformOptions} from '../JSTransformer/worker/worker';
export type GetTransformOptions<T> = ( export type GetTransformOptions<T> = (
string, string,
@ -353,8 +354,6 @@ class Bundler {
? runBeforeMainModule ? runBeforeMainModule
.map(name => modulesByName[name]) .map(name => modulesByName[name])
.filter(Boolean) .filter(Boolean)
/* $FlowFixMe: looks like ResolutionResponse is monkey-patched
* with `getModuleId`. */
.map(response.getModuleId) .map(response.getModuleId)
: undefined; : undefined;
@ -450,7 +449,8 @@ class Bundler {
entryFilePath, entryFilePath,
assetPlugins, assetPlugins,
transformOptions: response.transformOptions, transformOptions: response.transformOptions,
getModuleId: response.getModuleId, /* $FlowFixMe: `getModuleId` is monkey-patched */
getModuleId: (response.getModuleId: () => number),
dependencyPairs: response.getResolvedDependencyPairs(module), dependencyPairs: response.getResolvedDependencyPairs(module),
}).then(transformed => { }).then(transformed => {
modulesByName[transformed.name] = module; modulesByName[transformed.name] = module;
@ -485,7 +485,7 @@ class Bundler {
generateSourceMaps = false, generateSourceMaps = false,
}: { }: {
entryFile: string, entryFile: string,
platform: ?string, platform: string,
dev?: boolean, dev?: boolean,
minify?: boolean, minify?: boolean,
hot?: boolean, hot?: boolean,
@ -528,14 +528,14 @@ class Bundler {
onProgress, onProgress,
}: { }: {
entryFile: string, entryFile: string,
platform: ?string, platform: string,
dev?: boolean, dev?: boolean,
minify?: boolean, minify?: boolean,
hot?: boolean, hot?: boolean,
recursive?: boolean, recursive?: boolean,
generateSourceMaps?: boolean, generateSourceMaps?: boolean,
isolateModuleIDs?: boolean, isolateModuleIDs?: boolean,
onProgress?: () => mixed, onProgress?: ?(finishedModules: number, totalModules: number) => mixed,
}) { }) {
return this.getTransformOptions( return this.getTransformOptions(
entryFile, entryFile,
@ -567,7 +567,7 @@ class Bundler {
getOrderedDependencyPaths({ entryFile, dev, platform }: { getOrderedDependencyPaths({ entryFile, dev, platform }: {
entryFile: string, entryFile: string,
dev: boolean, dev: boolean,
platform: ?string, platform: string,
}) { }) {
return this.getDependencies({entryFile, dev, platform}).then( return this.getDependencies({entryFile, dev, platform}).then(
({ dependencies }) => { ({ dependencies }) => {
@ -608,7 +608,15 @@ class Bundler {
getModuleId, getModuleId,
dependencyPairs, dependencyPairs,
assetPlugins, assetPlugins,
}) { }: {
module: Module,
bundle: Bundle,
entryFilePath: string,
transformOptions: TransformOptions,
getModuleId: () => number,
dependencyPairs: Array<[mixed, {path: string}]>,
assetPlugins: Array<string>,
}): Promise<ModuleTransport> {
let moduleTransport; let moduleTransport;
const moduleId = getModuleId(module); const moduleId = getModuleId(module);
@ -670,7 +678,9 @@ class Bundler {
__packager_asset: true, __packager_asset: true,
fileSystemLocation: pathDirname(module.path), fileSystemLocation: pathDirname(module.path),
httpServerLocation: assetUrlPath, httpServerLocation: assetUrlPath,
/* $FlowFixMe: `resolution` is assets-only */
width: dimensions ? dimensions.width / module.resolution : undefined, width: dimensions ? dimensions.width / module.resolution : undefined,
/* $FlowFixMe: `resolution` is assets-only */
height: dimensions ? dimensions.height / module.resolution : undefined, height: dimensions ? dimensions.height / module.resolution : undefined,
scales: assetData.scales, scales: assetData.scales,
files: assetData.files, files: assetData.files,
@ -718,9 +728,9 @@ class Bundler {
} }
_generateAssetModule( _generateAssetModule(
bundle, bundle: Bundle,
module, module: Module,
moduleId, moduleId: number,
assetPlugins: Array<string> = [], assetPlugins: Array<string> = [],
platform: ?string = null, platform: ?string = null,
) { ) {
@ -745,7 +755,7 @@ class Bundler {
mainModuleName: string, mainModuleName: string,
options: { options: {
dev?: boolean, dev?: boolean,
platform: ?string, platform: string,
hot?: boolean, hot?: boolean,
generateSourceMaps?: boolean, generateSourceMaps?: boolean,
}, },

View File

@ -18,7 +18,7 @@ const invariant = require('invariant');
const minify = require('./minify'); const minify = require('./minify');
import type {LogEntry} from '../../Logger/Types'; import type {LogEntry} from '../../Logger/Types';
import type {Ast, SourceMap} from 'babel-core'; import type {Ast, SourceMap, TransformOptions} from 'babel-core';
function makeTransformParams(filename, sourceCode, options) { function makeTransformParams(filename, sourceCode, options) {
if (filename.endsWith('.json')) { if (filename.endsWith('.json')) {
@ -31,7 +31,7 @@ export type TransformedCode = {
code: string, code: string,
dependencies: Array<string>, dependencies: Array<string>,
dependencyOffsets: Array<number>, dependencyOffsets: Array<number>,
map?: ?{}, map?: ?SourceMap,
}; };
type Transform = ( type Transform = (
@ -47,7 +47,13 @@ type Transform = (
) => void; ) => void;
export type Options = { export type Options = {
transform?: {projectRoots: Array<string>}, transform: {
projectRoots: Array<string>,
ramGroups: Array<string>,
platform: string,
preloadedModules: Array<string>,
} & TransformOptions,
platform: string,
}; };
export type Data = { export type Data = {
@ -132,7 +138,7 @@ exports.transformAndExtractDependencies = (
transform: string, transform: string,
filename: string, filename: string,
sourceCode: string, sourceCode: string,
options: ?Options, options: Options,
callback: Callback, callback: Callback,
) => { ) => {
/* $FlowFixMe: impossible to type a dynamic require */ /* $FlowFixMe: impossible to type a dynamic require */

View File

@ -5,9 +5,11 @@
* This source code is licensed under the BSD-style license found in the * 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 * 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. * of patent rights can be found in the PATENTS file in the same directory.
*
* @flow
*/ */
'use strict';
'use strict';
const DependencyGraph = require('../node-haste'); const DependencyGraph = require('../node-haste');
@ -15,6 +17,11 @@ const declareOpts = require('../lib/declareOpts');
const defaults = require('../../../defaults'); const defaults = require('../../../defaults');
const pathJoin = require('path').join; const pathJoin = require('path').join;
import type ResolutionResponse from '../node-haste/DependencyGraph/ResolutionResponse';
import type Module from '../node-haste/Module';
import type {SourceMap} from '../lib/SourceMap';
import type {Options as TransformOptions} from '../JSTransformer/worker/worker';
const validateOpts = declareOpts({ const validateOpts = declareOpts({
projectRoots: { projectRoots: {
type: 'array', type: 'array',
@ -87,7 +94,12 @@ const getDependenciesValidateOpts = declareOpts({
class Resolver { class Resolver {
constructor(options) { _depGraph: DependencyGraph;
_minifyCode: (filePath: string, code: string, map: SourceMap) =>
Promise<{code: string, map: SourceMap}>;
_polyfillModuleNames: Array<string>;
constructor(options: {resetCache: boolean}) {
const opts = validateOpts(options); const opts = validateOpts(options);
this._depGraph = new DependencyGraph({ this._depGraph = new DependencyGraph({
@ -122,15 +134,24 @@ class Resolver {
}); });
} }
getShallowDependencies(entryFile, transformOptions) { getShallowDependencies(
entryFile: string,
transformOptions: TransformOptions,
): Array<string> {
return this._depGraph.getShallowDependencies(entryFile, transformOptions); return this._depGraph.getShallowDependencies(entryFile, transformOptions);
} }
getModuleForPath(entryFile) { getModuleForPath(entryFile: string): Module {
return this._depGraph.getModuleForPath(entryFile); return this._depGraph.getModuleForPath(entryFile);
} }
getDependencies(entryPath, options, transformOptions, onProgress, getModuleId) { getDependencies(
entryPath: string,
options: {},
transformOptions: TransformOptions,
onProgress?: ?(finishedModules: number, totalModules: number) => mixed,
getModuleId: mixed,
): Promise<ResolutionResponse> {
const {platform, recursive} = getDependenciesValidateOpts(options); const {platform, recursive} = getDependenciesValidateOpts(options);
return this._depGraph.getDependencies({ return this._depGraph.getDependencies({
entryPath, entryPath,
@ -148,7 +169,7 @@ class Resolver {
}); });
} }
getModuleSystemDependencies(options) { getModuleSystemDependencies(options: {}): Array<Module> {
const opts = getDependenciesValidateOpts(options); const opts = getDependenciesValidateOpts(options);
const prelude = opts.dev const prelude = opts.dev
@ -167,7 +188,7 @@ class Resolver {
})); }));
} }
_getPolyfillDependencies() { _getPolyfillDependencies(): Array<Module> {
const polyfillModuleNames = defaults.polyfills.concat(this._polyfillModuleNames); const polyfillModuleNames = defaults.polyfills.concat(this._polyfillModuleNames);
return polyfillModuleNames.map( return polyfillModuleNames.map(
@ -179,7 +200,12 @@ class Resolver {
); );
} }
resolveRequires(resolutionResponse, module, code, dependencyOffsets = []) { resolveRequires(
resolutionResponse: ResolutionResponse,
module: Module,
code: string,
dependencyOffsets: Array<number> = [],
): string {
const resolvedDeps = Object.create(null); const resolvedDeps = Object.create(null);
// here, we build a map of all require strings (relative and absolute) // here, we build a map of all require strings (relative and absolute)
@ -187,6 +213,7 @@ class Resolver {
resolutionResponse.getResolvedDependencyPairs(module) resolutionResponse.getResolvedDependencyPairs(module)
.forEach(([depName, depModule]) => { .forEach(([depName, depModule]) => {
if (depModule) { if (depModule) {
/* $FlowFixMe: `getModuleId` is monkey-patched so may not exist */
resolvedDeps[depName] = resolutionResponse.getModuleId(depModule); resolvedDeps[depName] = resolutionResponse.getModuleId(depModule);
} }
}); });
@ -204,7 +231,7 @@ class Resolver {
? `${JSON.stringify(resolvedDeps[depName])} /* ${depName} */` ? `${JSON.stringify(resolvedDeps[depName])} /* ${depName} */`
: codeMatch; : codeMatch;
code = dependencyOffsets.reduceRight((codeBits, offset) => { const codeParts = dependencyOffsets.reduceRight((codeBits, offset) => {
const first = codeBits.shift(); const first = codeBits.shift();
codeBits.unshift( codeBits.unshift(
first.slice(0, offset), first.slice(0, offset),
@ -213,7 +240,7 @@ class Resolver {
return codeBits; return codeBits;
}, [code]); }, [code]);
return code.join(''); return codeParts.join('');
} }
wrapModule({ wrapModule({
@ -225,6 +252,17 @@ class Resolver {
meta = {}, meta = {},
dev = true, dev = true,
minify = false, minify = false,
}: {
resolutionResponse: ResolutionResponse,
module: Module,
name: string,
map: SourceMap,
code: string,
meta?: {
dependencyOffsets?: Array<number>,
},
dev?: boolean,
minify?: boolean,
}) { }) {
if (module.isJSON()) { if (module.isJSON()) {
code = `module.exports = ${code}`; code = `module.exports = ${code}`;
@ -233,6 +271,7 @@ class Resolver {
if (module.isPolyfill()) { if (module.isPolyfill()) {
code = definePolyfillCode(code); code = definePolyfillCode(code);
} else { } else {
/* $FlowFixMe: `getModuleId` is monkey-patched so may not exist */
const moduleId = resolutionResponse.getModuleId(module); const moduleId = resolutionResponse.getModuleId(module);
code = this.resolveRequires( code = this.resolveRequires(
resolutionResponse, resolutionResponse,
@ -243,17 +282,18 @@ class Resolver {
code = defineModuleCode(moduleId, code, name, dev); code = defineModuleCode(moduleId, code, name, dev);
} }
return minify return minify
? this._minifyCode(module.path, code, map) ? this._minifyCode(module.path, code, map)
: Promise.resolve({code, map}); : Promise.resolve({code, map});
} }
minifyModule({path, code, map}) { minifyModule(
{path, code, map}: {path: string, code: string, map: SourceMap},
): Promise<{code: string, map: SourceMap}> {
return this._minifyCode(path, code, map); return this._minifyCode(path, code, map);
} }
getDependencyGraph() { getDependencyGraph(): DependencyGraph {
return this._depGraph; return this._depGraph;
} }
} }

View File

@ -29,6 +29,7 @@ const CACHE_NAME = 'react-native-packager-cache';
type CacheFilePaths = {transformedCode: string, metadata: string}; type CacheFilePaths = {transformedCode: string, metadata: string};
import type {Options as TransformOptions} from '../JSTransformer/worker/worker'; import type {Options as TransformOptions} from '../JSTransformer/worker/worker';
import type {SourceMap} from './SourceMap';
/** /**
* If packager is running for two different directories, we don't want the * If packager is running for two different directories, we don't want the
@ -79,7 +80,7 @@ export type CachedResult = {
code: string, code: string,
dependencies: Array<string>, dependencies: Array<string>,
dependencyOffsets: Array<number>, dependencyOffsets: Array<number>,
map?: ?{}, map?: ?SourceMap,
}; };
/** /**
@ -227,7 +228,7 @@ function readMetadataFileSync(
cachedSourceHash: number, cachedSourceHash: number,
dependencies: Array<string>, dependencies: Array<string>,
dependencyOffsets: Array<number>, dependencyOffsets: Array<number>,
sourceMap: ?{}, sourceMap: ?SourceMap,
} { } {
const metadataStr = fs.readFileSync(metadataFilePath, 'utf8'); const metadataStr = fs.readFileSync(metadataFilePath, 'utf8');
let metadata; let metadata;

View File

@ -124,7 +124,7 @@ class ResolutionRequest {
}: { }: {
response: ResolutionResponse, response: ResolutionResponse,
transformOptions: Object, transformOptions: Object,
onProgress: () => void, onProgress?: ?(finishedModules: number, totalModules: number) => mixed,
recursive: boolean, recursive: boolean,
}) { }) {
const entry = this._moduleCache.getModule(this._entryPath); const entry = this._moduleCache.getModule(this._entryPath);

View File

@ -13,21 +13,26 @@
import Module from '../Module'; import Module from '../Module';
import type {Options as TransformOptions} from '../../JSTransformer/worker/worker';
const NO_OPTIONS = {}; const NO_OPTIONS = {};
class ResolutionResponse { class ResolutionResponse {
transformOptions: {}; transformOptions: TransformOptions;
dependencies: Array<Module>; dependencies: Array<Module>;
mainModuleId: ?(number | string); mainModuleId: ?(number | string);
mocks: mixed; mocks: mixed;
numPrependedDependencies: number; numPrependedDependencies: number;
// This is monkey-patched from Resolver.
getModuleId: ?() => number;
_mappings: {}; _mappings: {};
_finalized: boolean; _finalized: boolean;
_mainModule: ?Module; _mainModule: ?Module;
constructor({transformOptions}: {transformOptions: {}}) { constructor({transformOptions}: {transformOptions: TransformOptions}) {
this.transformOptions = transformOptions; this.transformOptions = transformOptions;
this.dependencies = []; this.dependencies = [];
this.mainModuleId = null; this.mainModuleId = null;
@ -76,7 +81,7 @@ class ResolutionResponse {
} }
} }
finalize() { finalize(): ResolutionResponse {
/* $FlowFixMe: _mainModule is not initialized in the constructor. */ /* $FlowFixMe: _mainModule is not initialized in the constructor. */
return this._mainModule.getName().then(id => { return this._mainModule.getName().then(id => {
this.mainModuleId = id; this.mainModuleId = id;

View File

@ -25,16 +25,18 @@ const jsonStableStringify = require('json-stable-stringify');
const {join: joinPath, relative: relativePath, extname} = require('path'); const {join: joinPath, relative: relativePath, extname} = require('path');
import type {TransformedCode, Options as TransformOptions} from '../JSTransformer/worker/worker'; import type {TransformedCode, Options as TransformOptions} from '../JSTransformer/worker/worker';
import type {SourceMap} from '../lib/SourceMap';
import type {ReadTransformProps} from '../lib/TransformCache'; import type {ReadTransformProps} from '../lib/TransformCache';
import type Cache from './Cache'; import type Cache from './Cache';
import type DependencyGraphHelpers from './DependencyGraph/DependencyGraphHelpers'; import type DependencyGraphHelpers from './DependencyGraph/DependencyGraphHelpers';
import type ModuleCache from './ModuleCache'; import type ModuleCache from './ModuleCache';
type ReadResult = { type ReadResult = {
code?: string, code: string,
dependencies?: ?Array<string>, dependencies?: ?Array<string>,
dependencyOffsets?: ?Array<number>, dependencyOffsets?: ?Array<number>,
map?: ?{}, map?: ?SourceMap,
source: string,
}; };
export type TransformCode = ( export type TransformCode = (
@ -122,7 +124,7 @@ class Module {
return this.read(transformOptions).then(({map}) => map); return this.read(transformOptions).then(({map}) => map);
} }
getName(): Promise<string | number> { getName(): Promise<string> {
return this._cache.get( return this._cache.get(
this.path, this.path,
'name', 'name',
@ -209,9 +211,10 @@ class Module {
id?: string, id?: string,
extern: boolean, extern: boolean,
result: TransformedCode, result: TransformedCode,
) { ): ReadResult {
if (this._options.cacheTransformResults === false) { if (this._options.cacheTransformResults === false) {
const {dependencies} = result; const {dependencies} = result;
/* $FlowFixMe: this code path is dead, remove. */
return {dependencies}; return {dependencies};
} }
return {...result, id, source}; return {...result, id, source};

View File

@ -38,6 +38,7 @@ const {
print, print,
} = require('../Logger'); } = require('../Logger');
import type {Options as TransformOptions} from '../JSTransformer/worker/worker';
import type { import type {
Options as ModuleOptions, Options as ModuleOptions,
TransformCode, TransformCode,
@ -53,7 +54,7 @@ class DependencyGraph {
extraNodeModules: Object, extraNodeModules: Object,
forceNodeFilesystemAPI: boolean, forceNodeFilesystemAPI: boolean,
ignoreFilePath: (filePath: string) => boolean, ignoreFilePath: (filePath: string) => boolean,
maxWorkers: number, maxWorkers: ?number,
mocksPattern: mixed, mocksPattern: mixed,
moduleOptions: ModuleOptions, moduleOptions: ModuleOptions,
platforms: Set<string>, platforms: Set<string>,
@ -103,22 +104,22 @@ class DependencyGraph {
assetDependencies: mixed, assetDependencies: mixed,
assetExts: Array<string>, assetExts: Array<string>,
cache: Cache, cache: Cache,
extensions: Array<string>, extensions?: ?Array<string>,
extraNodeModules: Object, extraNodeModules: Object,
forceNodeFilesystemAPI?: boolean, forceNodeFilesystemAPI?: boolean,
ignoreFilePath: (filePath: string) => boolean, ignoreFilePath: (filePath: string) => boolean,
maxWorkers: number, maxWorkers?: ?number,
mocksPattern: mixed, mocksPattern?: mixed,
moduleOptions: ?ModuleOptions, moduleOptions: ?ModuleOptions,
platforms: mixed, platforms: mixed,
preferNativePlatform: boolean, preferNativePlatform: boolean,
providesModuleNodeModules: Array<string>, providesModuleNodeModules: Array<string>,
resetCache: boolean, resetCache: boolean,
roots: Array<string>, roots: Array<string>,
shouldThrowOnUnresolvedErrors: () => boolean, shouldThrowOnUnresolvedErrors?: () => boolean,
transformCacheKey: string, transformCacheKey: string,
transformCode: TransformCode, transformCode: TransformCode,
useWatchman: boolean, useWatchman?: ?boolean,
watch: boolean, watch: boolean,
}) { }) {
this._opts = { this._opts = {
@ -272,8 +273,8 @@ class DependencyGraph {
}: { }: {
entryPath: string, entryPath: string,
platform: string, platform: string,
transformOptions: {}, transformOptions: TransformOptions,
onProgress: () => void, onProgress?: ?(finishedModules: number, totalModules: number) => mixed,
recursive: boolean, recursive: boolean,
}) { }) {
return this.load().then(() => { return this.load().then(() => {