Skip to content

Commit

Permalink
Add bundle command
Browse files Browse the repository at this point in the history
Summary: This adds the machinery necessary to create a bundling function, and adds different reusable parts around generating code and maps for bundles. Used for the new integration with Buck

Reviewed By: cpojer

Differential Revision: D4299272

fbshipit-source-id: 59ebe39a454ebf56c2159717c2881088d6d3308a
  • Loading branch information
davidaurelio authored and Facebook Github Bot committed Dec 13, 2016
1 parent a76547f commit f3a9dec
Show file tree
Hide file tree
Showing 5 changed files with 204 additions and 74 deletions.
102 changes: 102 additions & 0 deletions packager/react-packager/src/ModuleGraph/ModuleGraph.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/**
* 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
*/
'use strict';

const defaults = require('../../../defaults');
const nullthrows = require('fbjs/lib/nullthrows');
const parallel = require('async/parallel');
const seq = require('async/seq');

const {virtualModule} = require('./output/util');

import type {
Callback,
GraphFn,
GraphResult,
Module,
} from './types.flow';

type BuildFn = (
entryPoints: Iterable<string>,
options: BuildOptions,
callback: Callback<{modules: Iterable<Module>, entryModules: Iterable<Module>}>,
) => void;

type BuildOptions = {|
optimize?: boolean,
platform?: string,
|};

exports.createBuildSetup = (
graph: GraphFn,
translateDefaultsPath: string => string = x => x,
): BuildFn =>
(entryPoints, options, callback) => {
const {
optimize = false,
platform = defaults.platforms[0],
} = options;
const graphOptions = {optimize};

const graphWithOptions =
(entry, cb) => graph(entry, platform, graphOptions, cb);
const graphOnlyModules = seq(graphWithOptions, getModules);

parallel({
graph: cb => graphWithOptions(
concat(defaults.runBeforeMainModule, entryPoints),
cb,
),
moduleSystem: cb => graphOnlyModules(
[translateDefaultsPath(defaults.moduleSystem)],
cb,
),
polyfills: cb => graphOnlyModules(
defaults.polyfills.map(translateDefaultsPath),
cb,
),
}, (
error: ?Error,
result?: {graph: GraphResult, moduleSystem: Array<Module>, polyfills: Array<Module>},
) => {
if (error) {
callback(error);
return;
}


const {
graph: {modules, entryModules},
moduleSystem,
polyfills,
} = nullthrows(result);

callback(null, {
entryModules,
modules: concat([prelude(optimize)], moduleSystem, polyfills, modules),
});
});
};

const getModules = (x, cb) => cb(null, x.modules);

function* concat<T>(...iterables: Array<Iterable<T>>): Iterable<T> {
for (const it of iterables) {
yield* it;
}
}

function prelude(optimize) {
return virtualModule(
`var __DEV__= ${String(!optimize)
}, __BUNDLE_START_TIME__ = Date.now();`
);
}
44 changes: 20 additions & 24 deletions packager/react-packager/src/ModuleGraph/output/as-plain-bundle.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,36 +13,32 @@
const {createIndexMap} = require('./source-map');
const {addModuleIdsToModuleWrapper} = require('./util');

import type {Module} from '../types.flow';
import type {SourceMap} from './source-map';
import type {OutputFn} from '../types.flow';

module.exports = (
modules: Iterable<Module>,
filename?: string,
idForPath: {path: string} => number,
): {code: string, map: SourceMap} => {
let code = '';
let line = 0;
const sections = [];
(modules, filename, idForPath) => {
let code = '';
let line = 0;
const sections = [];

for (const module of modules) {
const {file} = module;
const moduleCode = file.type === 'module'
? addModuleIdsToModuleWrapper(module, idForPath)
: file.code;
for (const module of modules) {
const {file} = module;
const moduleCode = file.type === 'module'
? addModuleIdsToModuleWrapper(module, idForPath)
: file.code;

code += moduleCode + '\n';
if (file.map) {
sections.push({
map: file.map,
offset: {column: 0, line}
});
code += moduleCode + '\n';
if (file.map) {
sections.push({
map: file.map,
offset: {column: 0, line}
});
}
line += countLines(moduleCode);
}
line += countLines(moduleCode);
}

return {code, map: createIndexMap({file: filename, sections})};
};
return {code, map: createIndexMap({file: filename, sections})};
}: OutputFn);

const reLine = /^/gm;
function countLines(string: string): number {
Expand Down
32 changes: 24 additions & 8 deletions packager/react-packager/src/ModuleGraph/output/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
*/
'use strict';

import type {Module} from '../types.flow';
import type {IdForPathFn, Module} from '../types.flow';

// Transformed modules have the form
// __d(function(require, module, global, exports, dependencyMap) {
Expand Down Expand Up @@ -59,11 +59,27 @@ exports.createIdForPathFn = (): ({path: string} => number) => {
};
};

exports.virtualModule = (code: string): Module => ({
dependencies: [],
file: {
code,
path: '',
type: 'script',
// creates a series of virtual modules with require calls to the passed-in
// modules.
exports.requireCallsTo = function* (
modules: Iterable<Module>,
idForPath: IdForPathFn,
): Iterable<Module> {
for (const module of modules) {
yield virtualModule(`require(${idForPath(module.file)});`);
}
});
};

// creates a virtual module (i.e. not corresponding to a file on disk)
// with the given source code.
exports.virtualModule = virtualModule;
function virtualModule(code: string) {
return {
dependencies: [],
file: {
code,
path: '',
type: 'script',
}
};
}
1 change: 1 addition & 0 deletions packager/react-packager/src/ModuleGraph/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"main": "ModuleGraph.js"}
99 changes: 57 additions & 42 deletions packager/react-packager/src/ModuleGraph/types.flow.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,29 +10,13 @@
*/
'use strict';

import type {SourceMap} from './output/source-map';
import type {Console} from 'console';

export type Callback<A = void, B = void>
= (Error => mixed)
& ((null | void, A, B) => mixed);

type ResolveOptions = {
log?: Console,
};

type LoadOptions = {|
log?: Console,
optimize?: boolean,
platform?: string,
|};

type GraphOptions = {|
cwd?: string,
log?: Console,
optimize?: boolean,
skip?: Set<string>,
|};

type Dependency = {|
id: string,
path: string,
Expand All @@ -47,51 +31,53 @@ export type File = {|

type FileTypes = 'module' | 'script';

export type Module = {|
dependencies: Array<Dependency>,
file: File,
|};

export type GraphFn = (
entryPoints: Iterable<string>,
platform: string,
options?: ?GraphOptions,
callback?: Callback<GraphResult>,
) => void;

type GraphOptions = {|
cwd?: string,
log?: Console,
optimize?: boolean,
skip?: Set<string>,
|};

export type GraphResult = {
entryModules: Array<Module>,
modules: Array<Module>,
};

export type ResolveFn = (
id: string,
source: string,
platform: string,
options?: ResolveOptions,
callback: Callback<string>,
) => void;
export type IdForPathFn = {path: string} => number;

export type LoadFn = (
file: string,
options: LoadOptions,
callback: Callback<File, Array<string>>,
) => void;

export type TransformResult = {|
code: string,
dependencies: Array<string>,
dependencyMapName?: string,
map: ?Object,
type LoadOptions = {|
log?: Console,
optimize?: boolean,
platform?: string,
|};

export type TransformedFile = {
export type Module = {|
dependencies: Array<Dependency>,
file: File,
|};

export type OutputFn = (
modules: Iterable<Module>,
filename?: string,
idForPath: IdForPathFn,
) => OutputResult;

type OutputResult = {
code: string,
file: string,
hasteID: ?string,
package?: PackageData,
transformed: {[variant: string]: TransformResult},
type: FileTypes,
map: SourceMap,
};

export type PackageData = {|
Expand All @@ -101,8 +87,16 @@ export type PackageData = {|
'react-native'?: Object | string,
|};

export type TransformFnResult = {
ast: Object,
export type ResolveFn = (
id: string,
source: string,
platform: string,
options?: ResolveOptions,
callback: Callback<string>,
) => void;

type ResolveOptions = {
log?: Console,
};

export type TransformFn = (
Expand All @@ -114,3 +108,24 @@ export type TransformFn = (
|},
callback: Callback<TransformFnResult>
) => void;


export type TransformFnResult = {
ast: Object,
};

export type TransformResult = {|
code: string,
dependencies: Array<string>,
dependencyMapName?: string,
map: ?Object,
|};

export type TransformedFile = {
code: string,
file: string,
hasteID: ?string,
package?: PackageData,
transformed: {[variant: string]: TransformResult},
type: FileTypes,
};

0 comments on commit f3a9dec

Please sign in to comment.