Updates from Wed 24 Jun

This commit is contained in:
Alex Kotliarskyi 2015-06-24 10:49:09 -07:00
commit e316a17b14
43 changed files with 5004 additions and 2955 deletions

View File

@ -11,7 +11,6 @@
// Don't forget to everything listed here to `testConfig.json` // Don't forget to everything listed here to `testConfig.json`
// modulePathIgnorePatterns. // modulePathIgnorePatterns.
var sharedBlacklist = [ var sharedBlacklist = [
__dirname,
'website', 'website',
'node_modules/react-tools/src/utils/ImmutableObject.js', 'node_modules/react-tools/src/utils/ImmutableObject.js',
'node_modules/react-tools/src/core/ReactInstanceHandles.js', 'node_modules/react-tools/src/core/ReactInstanceHandles.js',

View File

@ -48,10 +48,11 @@ var messageHandlers = {
loadScript(message.url, sendReply.bind(null, null)); loadScript(message.url, sendReply.bind(null, null));
}, },
'executeJSCall': function(message, sendReply) { 'executeJSCall': function(message, sendReply) {
var returnValue = [[], [], [], [], []]; var returnValue = null;
try { try {
if (window && window.require) { if (window && window.require) {
returnValue = window.require(message.moduleName)[message.moduleMethod].apply(null, message.arguments); var module = window.require(message.moduleName);
returnValue = module[message.moduleMethod].apply(module, message.arguments);
} }
} finally { } finally {
sendReply(JSON.stringify(returnValue)); sendReply(JSON.stringify(returnValue));

View File

@ -11,7 +11,6 @@ on run argv
set theURL to item 1 of argv set theURL to item 1 of argv
tell application "Chrome" tell application "Chrome"
activate
if (count every window) = 0 then if (count every window) = 0 then
make new window make new window
@ -40,6 +39,7 @@ on run argv
set theWindow's active tab index to theTabIndex set theWindow's active tab index to theTabIndex
else else
tell window 1 tell window 1
activate
make new tab with properties {URL:theURL} make new tab with properties {URL:theURL}
end tell end tell
end if end if

View File

@ -66,6 +66,9 @@ if (options.projectRoots) {
if (__dirname.match(/node_modules\/react-native\/packager$/)) { if (__dirname.match(/node_modules\/react-native\/packager$/)) {
// packager is running from node_modules of another project // packager is running from node_modules of another project
options.projectRoots = [path.resolve(__dirname, '../../..')]; options.projectRoots = [path.resolve(__dirname, '../../..')];
} else if (__dirname.match(/Pods\/React\/packager$/)) {
// packager is running from node_modules of another project
options.projectRoots = [path.resolve(__dirname, '../../..')];
} else { } else {
options.projectRoots = [path.resolve(__dirname, '..')]; options.projectRoots = [path.resolve(__dirname, '..')];
} }
@ -83,11 +86,15 @@ if (options.root) {
if (options.assetRoots) { if (options.assetRoots) {
if (!Array.isArray(options.assetRoots)) { if (!Array.isArray(options.assetRoots)) {
options.assetRoots = options.assetRoots.split(','); options.assetRoots = options.assetRoots.split(',').map(function (dir) {
return path.resolve(process.cwd(), dir);
});
} }
} else { } else {
if (__dirname.match(/node_modules\/react-native\/packager$/)) { if (__dirname.match(/node_modules\/react-native\/packager$/)) {
options.assetRoots = [path.resolve(__dirname, '../../..')]; options.assetRoots = [path.resolve(__dirname, '../../..')];
} else if (__dirname.match(/Pods\/React\/packager$/)) {
options.assetRoots = [path.resolve(__dirname, '../../..')];
} else { } else {
options.assetRoots = [path.resolve(__dirname, '..')]; options.assetRoots = [path.resolve(__dirname, '..')];
} }

24
react-packager/.babelrc Normal file
View File

@ -0,0 +1,24 @@
// Keep in sync with packager/transformer.js
{
"retainLines": true,
"compact": true,
"comments": false,
"whitelist": [
"es6.arrowFunctions",
"es6.blockScoping",
// This is the only place where we differ from transformer.js
"es6.constants",
"es6.classes",
"es6.destructuring",
"es6.parameters.rest",
"es6.properties.computed",
"es6.properties.shorthand",
"es6.spread",
"es6.templateLiterals",
"es7.trailingFunctionCommas",
"es7.objectRestSpread",
"flow",
"react"
],
"sourceMaps": false
}

View File

@ -1,5 +0,0 @@
'use strict';
jest.autoMockOff();
module.exports = require.requireActual('bluebird');
jest.autoMockOn();

View File

@ -8,6 +8,10 @@
*/ */
'use strict'; 'use strict';
require('babel/register')({
only: /react-packager\/src/
});
useGracefulFs(); useGracefulFs();
var Activity = require('./src/Activity'); var Activity = require('./src/Activity');

View File

@ -8,7 +8,7 @@ jest
.mock('crypto') .mock('crypto')
.mock('fs'); .mock('fs');
var Promise = require('bluebird'); var Promise = require('promise');
describe('AssetServer', function() { describe('AssetServer', function() {
var AssetServer; var AssetServer;

View File

@ -11,13 +11,13 @@
var declareOpts = require('../lib/declareOpts'); var declareOpts = require('../lib/declareOpts');
var getAssetDataFromName = require('../lib/getAssetDataFromName'); var getAssetDataFromName = require('../lib/getAssetDataFromName');
var path = require('path'); var path = require('path');
var Promise = require('bluebird'); var Promise = require('promise');
var fs = require('fs'); var fs = require('fs');
var crypto = require('crypto'); var crypto = require('crypto');
var lstat = Promise.promisify(fs.lstat); var stat = Promise.denodeify(fs.stat);
var readDir = Promise.promisify(fs.readdir); var readDir = Promise.denodeify(fs.readdir);
var readFile = Promise.promisify(fs.readFile); var readFile = Promise.denodeify(fs.readFile);
module.exports = AssetServer; module.exports = AssetServer;
@ -56,12 +56,15 @@ AssetServer.prototype._getAssetRecord = function(assetPath) {
this._roots, this._roots,
path.dirname(assetPath) path.dirname(assetPath)
).then(function(dir) { ).then(function(dir) {
return [ return Promise.all([
dir, dir,
readDir(dir), readDir(dir),
]; ]);
}).spread(function(dir, files) { }).then(function(res) {
var dir = res[0];
var files = res[1];
var assetData = getAssetDataFromName(filename); var assetData = getAssetDataFromName(filename);
var map = buildAssetMap(dir, files); var map = buildAssetMap(dir, files);
var record = map[assetData.assetName]; var record = map[assetData.assetName];
@ -98,14 +101,14 @@ AssetServer.prototype.getAssetData = function(assetPath) {
return Promise.all( return Promise.all(
record.files.map(function(file) { record.files.map(function(file) {
return lstat(file); return stat(file);
}) })
); );
}).then(function(stats) { }).then(function(stats) {
var hash = crypto.createHash('md5'); var hash = crypto.createHash('md5');
stats.forEach(function(stat) { stats.forEach(function(fstat) {
hash.update(stat.mtime.getTime().toString()); hash.update(fstat.mtime.getTime().toString());
}); });
data.hash = hash.digest('hex'); data.hash = hash.digest('hex');
@ -114,21 +117,23 @@ AssetServer.prototype.getAssetData = function(assetPath) {
}; };
function findRoot(roots, dir) { function findRoot(roots, dir) {
return Promise.some( return Promise.all(
roots.map(function(root) { roots.map(function(root) {
var absPath = path.join(root, dir); var absPath = path.join(root, dir);
return lstat(absPath).then(function(stat) { return stat(absPath).then(function(fstat) {
if (!stat.isDirectory()) { return {path: absPath, isDirectory: fstat.isDirectory()};
throw new Error('Looking for dirs'); }, function (err) {
} return {path: absPath, isDirectory: false};
stat._path = absPath;
return stat;
}); });
}), })
1 ).then(
).spread( function(stats) {
function(stat) { for (var i = 0; i < stats.length; i++) {
return stat._path; if (stats[i].isDirectory) {
return stats[i].path;
}
}
throw new Error('Could not find any directories');
} }
); );
} }

View File

@ -0,0 +1,46 @@
'use strict';
const Module = require('./Module');
const Promise = require('promise');
const getAssetDataFromName = require('../lib/getAssetDataFromName');
class AssetModule extends Module {
isHaste() {
return Promise.resolve(false);
}
getDependencies() {
return Promise.resolve([]);
}
_read() {
return Promise.resolve({});
}
getName() {
return super.getName().then(id => {
const {name, type} = getAssetDataFromName(this.path);
return id.replace(/\/[^\/]+$/, `/${name}.${type}`);
});
}
getPlainObject() {
return this.getName().then(name => this.addReference({
path: this.path,
isJSON: false,
isAsset: true,
isAsset_DEPRECATED: false,
isPolyfill: false,
resolution: getAssetDataFromName(this.path).resolution,
id: name,
dependencies: [],
}));
}
hash() {
return `AssetModule : ${this.path}`;
}
}
module.exports = AssetModule;

View File

@ -0,0 +1,40 @@
'use strict';
const Module = require('./Module');
const Promise = require('promise');
const getAssetDataFromName = require('../lib/getAssetDataFromName');
class AssetModule_DEPRECATED extends Module {
isHaste() {
return Promise.resolve(false);
}
getName() {
return Promise.resolve(this.name);
}
getDependencies() {
return Promise.resolve([]);
}
getPlainObject() {
const {name, resolution} = getAssetDataFromName(this.path);
return Promise.resolve(this.addReference({
path: this.path,
id: `image!${name}`,
resolution,
isAsset_DEPRECATED: true,
dependencies: [],
isJSON: false,
isPolyfill: false,
isAsset: false,
}));
}
hash() {
return `AssetModule_DEPRECATED : ${this.path}`;
}
}
module.exports = AssetModule_DEPRECATED;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,556 @@
/**
* 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.
*/
'use strict';
const path = require('path');
const Fastfs = require('../fastfs');
const ModuleCache = require('../ModuleCache');
const AssetModule_DEPRECATED = require('../AssetModule_DEPRECATED');
const declareOpts = require('../../lib/declareOpts');
const isAbsolutePath = require('absolute-path');
const debug = require('debug')('DependencyGraph');
const getAssetDataFromName = require('../../lib/getAssetDataFromName');
const util = require('util');
const Promise = require('promise');
const _ = require('underscore');
const validateOpts = declareOpts({
roots: {
type: 'array',
required: true,
},
ignoreFilePath: {
type: 'function',
default: function(){}
},
fileWatcher: {
type: 'object',
required: true,
},
assetRoots_DEPRECATED: {
type: 'array',
default: [],
},
assetExts: {
type: 'array',
required: true,
},
providesModuleNodeModules: {
type: 'array',
default: [
'react-tools',
'react-native',
// Parse requires AsyncStorage. They will
// change that to require('react-native') which
// should work after this release and we can
// remove it from here.
'parse',
],
},
});
class DependencyGraph {
constructor(options) {
this._opts = validateOpts(options);
this._hasteMap = Object.create(null);
this._immediateResolutionCache = Object.create(null);
this.load();
}
load() {
if (this._loading) {
return this._loading;
}
const modulePattern = new RegExp(
'\.(' + ['js', 'json'].concat(this._assetExts).join('|') + ')$'
);
this._fastfs = new Fastfs(this._opts.roots,this._opts.fileWatcher, {
pattern: modulePattern,
ignore: this._opts.ignoreFilePath,
});
this._fastfs.on('change', this._processFileChange.bind(this));
this._moduleCache = new ModuleCache(this._fastfs);
this._loading = Promise.all([
this._fastfs.build().then(() => this._buildHasteMap()),
this._buildAssetMap_DEPRECATED(),
]);
return this._loading;
}
resolveDependency(fromModule, toModuleName) {
if (fromModule._ref) {
fromModule = fromModule._ref;
}
const resHash = resolutionHash(fromModule.path, toModuleName);
if (this._immediateResolutionCache[resHash]) {
return Promise.resolve(this._immediateResolutionCache[resHash]);
}
const asset_DEPRECATED = this._resolveAsset_DEPRECATED(
fromModule,
toModuleName
);
if (asset_DEPRECATED) {
return Promise.resolve(asset_DEPRECATED);
}
const cacheResult = (result) => {
this._immediateResolutionCache[resHash] = result;
return result;
};
const forgive = () => {
console.warn(
'Unable to resolve module %s from %s',
toModuleName,
fromModule.path
);
return null;
};
if (!this._isNodeModulesDir(fromModule.path)
&& toModuleName[0] !== '.' &&
toModuleName[0] !== '/') {
return this._resolveHasteDependency(fromModule, toModuleName).catch(
() => this._resolveNodeDependency(fromModule, toModuleName)
).then(
cacheResult,
forgive
);
}
return this._resolveNodeDependency(fromModule, toModuleName)
.then(
cacheResult,
forgive
);
}
getOrderedDependencies(entryPath) {
return this.load().then(() => {
const absolutePath = path.resolve(this._getAbsolutePath(entryPath));
if (absolutePath == null) {
throw new NotFoundError(
'Cannot find entry file %s in any of the roots: %j',
entryPath,
this._opts.roots
);
}
const entry = this._moduleCache.getModule(absolutePath);
const deps = [];
const visited = Object.create(null);
visited[entry.hash()] = true;
const collect = (mod) => {
deps.push(mod);
return mod.getDependencies().then(
depNames => Promise.all(
depNames.map(name => this.resolveDependency(mod, name))
).then((dependencies) => [depNames, dependencies])
).then(([depNames, dependencies]) => {
let p = Promise.resolve();
dependencies.forEach((modDep, i) => {
if (modDep == null) {
debug(
'WARNING: Cannot find required module `%s` from module `%s`',
depNames[i],
mod.path
);
return;
}
p = p.then(() => {
if (!visited[modDep.hash()]) {
visited[modDep.hash()] = true;
return collect(modDep);
}
return null;
});
});
return p;
});
};
return collect(entry)
.then(() => Promise.all(deps.map(dep => dep.getPlainObject())));
});
}
_getAbsolutePath(filePath) {
if (isAbsolutePath(filePath)) {
return filePath;
}
for (let i = 0; i < this._opts.roots.length; i++) {
const root = this._opts.roots[i];
const absPath = path.join(root, filePath);
if (this._fastfs.fileExists(absPath)) {
return absPath;
}
}
return null;
}
_resolveHasteDependency(fromModule, toModuleName) {
toModuleName = normalizePath(toModuleName);
let p = fromModule.getPackage();
if (p) {
p = p.redirectRequire(toModuleName);
} else {
p = Promise.resolve(toModuleName);
}
return p.then((realModuleName) => {
let dep = this._hasteMap[realModuleName];
if (dep && dep.type === 'Module') {
return dep;
}
let packageName = realModuleName;
while (packageName && packageName !== '.') {
dep = this._hasteMap[packageName];
if (dep && dep.type === 'Package') {
break;
}
packageName = path.dirname(packageName);
}
if (dep && dep.type === 'Package') {
const potentialModulePath = path.join(
dep.root,
path.relative(packageName, realModuleName)
);
return this._loadAsFile(potentialModulePath)
.catch(() => this._loadAsDir(potentialModulePath));
}
throw new Error('Unable to resolve dependency');
});
}
_redirectRequire(fromModule, modulePath) {
return Promise.resolve(fromModule.getPackage()).then(p => {
if (p) {
return p.redirectRequire(modulePath);
}
return modulePath;
});
}
_resolveNodeDependency(fromModule, toModuleName) {
if (toModuleName[0] === '.' || toModuleName[1] === '/') {
const potentialModulePath = isAbsolutePath(toModuleName) ?
toModuleName :
path.join(path.dirname(fromModule.path), toModuleName);
return this._redirectRequire(fromModule, potentialModulePath).then(
realModuleName => this._loadAsFile(realModuleName)
.catch(() => this._loadAsDir(realModuleName))
);
} else {
return this._redirectRequire(fromModule, toModuleName).then(
realModuleName => {
const searchQueue = [];
for (let currDir = path.dirname(fromModule.path);
currDir !== '/';
currDir = path.dirname(currDir)) {
searchQueue.push(
path.join(currDir, 'node_modules', realModuleName)
);
}
let p = Promise.reject(new Error('Node module not found'));
searchQueue.forEach(potentialModulePath => {
p = p.catch(
() => this._loadAsFile(potentialModulePath)
).catch(
() => this._loadAsDir(potentialModulePath)
);
});
return p;
});
}
}
_resolveAsset_DEPRECATED(fromModule, toModuleName) {
if (this._assetMap_DEPRECATED != null) {
const assetMatch = toModuleName.match(/^image!(.+)/);
// Process DEPRECATED global asset requires.
if (assetMatch && assetMatch[1]) {
if (!this._assetMap_DEPRECATED[assetMatch[1]]) {
debug('WARINING: Cannot find asset:', assetMatch[1]);
return null;
}
return this._assetMap_DEPRECATED[assetMatch[1]];
}
}
return null;
}
_isAssetFile(file) {
return this._opts.assetExts.indexOf(extname(file)) !== -1;
}
_loadAsFile(potentialModulePath) {
return Promise.resolve().then(() => {
if (this._isAssetFile(potentialModulePath)) {
const {name, type} = getAssetDataFromName(potentialModulePath);
const pattern = new RegExp('^' + name + '(@[\\d\\.]+x)?\\.' + type);
// We arbitrarly grab the first one, because scale selection
// will happen somewhere
const [assetFile] = this._fastfs.matches(
path.dirname(potentialModulePath),
pattern
);
if (assetFile) {
return this._moduleCache.getAssetModule(assetFile);
}
}
let file;
if (this._fastfs.fileExists(potentialModulePath)) {
file = potentialModulePath;
} else if (this._fastfs.fileExists(potentialModulePath + '.js')) {
file = potentialModulePath + '.js';
} else if (this._fastfs.fileExists(potentialModulePath + '.json')) {
file = potentialModulePath + '.json';
} else {
throw new Error(`File ${potentialModulePath} doesnt exist`);
}
return this._moduleCache.getModule(file);
});
}
_loadAsDir(potentialDirPath) {
return Promise.resolve().then(() => {
if (!this._fastfs.dirExists(potentialDirPath)) {
throw new Error(`Invalid directory ${potentialDirPath}`);
}
const packageJsonPath = path.join(potentialDirPath, 'package.json');
if (this._fastfs.fileExists(packageJsonPath)) {
return this._moduleCache.getPackage(packageJsonPath)
.getMain().then(
(main) => this._loadAsFile(main).catch(
() => this._loadAsDir(main)
)
);
}
return this._loadAsFile(path.join(potentialDirPath, 'index'));
});
}
_buildHasteMap() {
let promises = this._fastfs.findFilesByExt('js', {
ignore: (file) => this._isNodeModulesDir(file)
}).map(file => this._processHasteModule(file));
promises = promises.concat(
this._fastfs.findFilesByName('package.json', {
ignore: (file) => this._isNodeModulesDir(file)
}).map(file => this._processHastePackage(file))
);
return Promise.all(promises);
}
_processHasteModule(file) {
const module = this._moduleCache.getModule(file);
return module.isHaste().then(
isHaste => isHaste && module.getName()
.then(name => this._updateHasteMap(name, module))
);
}
_processHastePackage(file) {
file = path.resolve(file);
const p = this._moduleCache.getPackage(file, this._fastfs);
return p.isHaste()
.then(isHaste => isHaste && p.getName()
.then(name => this._updateHasteMap(name, p)))
.catch(e => {
if (e instanceof SyntaxError) {
// Malformed package.json.
return;
}
throw e;
});
}
_updateHasteMap(name, mod) {
if (this._hasteMap[name]) {
debug('WARNING: conflicting haste modules: ' + name);
if (mod.type === 'Package' &&
this._hasteMap[name].type === 'Module') {
// Modules takes precendence over packages.
return;
}
}
this._hasteMap[name] = mod;
}
_isNodeModulesDir(file) {
const inNodeModules = file.indexOf('/node_modules/') !== -1;
if (!inNodeModules) {
return false;
}
const dirs = this._opts.providesModuleNodeModules;
for (let i = 0; i < dirs.length; i++) {
const index = file.indexOf(dirs[i]);
if (index !== -1) {
return file.slice(index).indexOf('/node_modules/') !== -1;
}
}
return true;
}
_processAsset_DEPRECATED(file) {
let ext = extname(file);
if (this._opts.assetExts.indexOf(ext) !== -1) {
let name = assetName(file, ext);
if (this._assetMap_DEPRECATED[name] != null) {
debug('Conflcting assets', name);
}
this._assetMap_DEPRECATED[name] = new AssetModule_DEPRECATED(file);
}
}
_buildAssetMap_DEPRECATED() {
if (this._opts.assetRoots_DEPRECATED == null ||
this._opts.assetRoots_DEPRECATED.length === 0) {
return Promise.resolve();
}
this._assetMap_DEPRECATED = Object.create(null);
const pattern = new RegExp(
'\.(' + this._opts.assetExts.join('|') + ')$'
);
const fastfs = new Fastfs(
this._opts.assetRoots_DEPRECATED,
this._opts.fileWatcher,
{ pattern, ignore: this._opts.ignoreFilePath }
);
fastfs.on('change', this._processAssetChange_DEPRECATED.bind(this));
return fastfs.build().then(
() => fastfs.findFilesByExts(this._opts.assetExts).map(
file => this._processAsset_DEPRECATED(file)
)
);
}
_processAssetChange_DEPRECATED(type, filePath, root, fstat) {
const name = assetName(filePath);
if (type === 'change' || type === 'delete') {
delete this._assetMap_DEPRECATED[name];
}
if (type === 'change' || type === 'add') {
this._loading = this._loading.then(
() => this._processAsset_DEPRECATED(path.join(root, filePath))
);
}
}
_processFileChange(type, filePath, root, fstat) {
// It's really hard to invalidate the right module resolution cache
// so we just blow it up with every file change.
this._immediateResolutionCache = Object.create(null);
const absPath = path.join(root, filePath);
if ((fstat && fstat.isDirectory()) ||
this._opts.ignoreFilePath(absPath) ||
this._isNodeModulesDir(absPath)) {
return;
}
if (type === 'delete' || type === 'change') {
_.each(this._hasteMap, (mod, name) => {
if (mod.path === absPath) {
delete this._hasteMap[name];
}
});
if (type === 'delete') {
return;
}
}
if (extname(absPath) === 'js' || extname(absPath) === 'json') {
this._loading = this._loading.then(() => {
if (path.basename(filePath) === 'package.json') {
return this._processHastePackage(absPath);
} else {
return this._processHasteModule(absPath);
}
});
}
}
}
function assetName(file, ext) {
return path.basename(file, '.' + ext).replace(/@[\d\.]+x/, '');
}
function extname(name) {
return path.extname(name).replace(/^\./, '');
}
function resolutionHash(modulePath, depName) {
return `${path.resolve(modulePath)}:${depName}`;
}
function NotFoundError() {
Error.call(this);
Error.captureStackTrace(this, this.constructor);
var msg = util.format.apply(util, arguments);
this.message = msg;
this.type = this.name = 'NotFoundError';
this.status = 404;
}
function normalizePath(modulePath) {
if (path.sep === '/') {
modulePath = path.normalize(modulePath);
} else if (path.posix) {
modulePath = path.posix.normalize(modulePath);
}
return modulePath.replace(/\/$/, '');
}
util.inherits(NotFoundError, Error);
module.exports = DependencyGraph;

View File

@ -0,0 +1,133 @@
'use strict';
const Promise = require('promise');
const docblock = require('./DependencyGraph/docblock');
const isAbsolutePath = require('absolute-path');
const path = require('path');
const replacePatterns = require('./replacePatterns');
class Module {
constructor(file, fastfs, moduleCache) {
if (!isAbsolutePath(file)) {
throw new Error('Expected file to be absolute path but got ' + file);
}
this.path = path.resolve(file);
this.type = 'Module';
this._fastfs = fastfs;
this._moduleCache = moduleCache;
}
isHaste() {
return this._read().then(data => !!data.id);
}
getName() {
return this._read().then(data => {
if (data.id) {
return data.id;
}
const p = this.getPackage();
if (!p) {
// Name is full path
return this.path;
}
return p.getName()
.then(name => {
if (!name) {
return this.path;
}
return path.join(name, path.relative(p.root, this.path));
});
});
}
getPackage() {
return this._moduleCache.getPackageForModule(this);
}
getDependencies() {
return this._read().then(data => data.dependencies);
}
_read() {
if (!this._reading) {
this._reading = this._fastfs.readFile(this.path).then(content => {
const data = {};
const moduleDocBlock = docblock.parseAsObject(content);
if (moduleDocBlock.providesModule || moduleDocBlock.provides) {
data.id = /^(\S*)/.exec(
moduleDocBlock.providesModule || moduleDocBlock.provides
)[1];
}
// Ignore requires in generated code. An example of this is prebuilt
// files like the SourceMap library.
if ('extern' in moduleDocBlock) {
data.dependencies = [];
} else {
data.dependencies = extractRequires(content);
}
return data;
});
}
return this._reading;
}
getPlainObject() {
return Promise.all([
this.getName(),
this.getDependencies(),
]).then(([name, dependencies]) => this.addReference({
path: this.path,
isJSON: path.extname(this.path) === '.json',
isAsset: false,
isAsset_DEPRECATED: false,
isPolyfill: false,
resolution: undefined,
id: name,
dependencies
}));
}
hash() {
return `Module : ${this.path}`;
}
addReference(obj) {
Object.defineProperty(obj, '_ref', { value: this });
return obj;
}
}
/**
* Extract all required modules from a `code` string.
*/
var blockCommentRe = /\/\*(.|\n)*?\*\//g;
var lineCommentRe = /\/\/.+(\n|$)/g;
function extractRequires(code /*: string*/) /*: Array<string>*/ {
var deps = [];
code
.replace(blockCommentRe, '')
.replace(lineCommentRe, '')
.replace(replacePatterns.IMPORT_RE, (match, pre, quot, dep, post) => {
deps.push(dep);
return match;
})
.replace(replacePatterns.REQUIRE_RE, function(match, pre, quot, dep, post) {
deps.push(dep);
});
return deps;
}
module.exports = Module;

View File

@ -0,0 +1,72 @@
'use strict';
const AssetModule = require('./AssetModule');
const Package = require('./Package');
const Module = require('./Module');
const path = require('path');
class ModuleCache {
constructor(fastfs) {
this._moduleCache = Object.create(null);
this._packageCache = Object.create(null);
this._fastfs = fastfs;
fastfs.on('change', this._processFileChange.bind(this));
}
getModule(filePath) {
filePath = path.resolve(filePath);
if (!this._moduleCache[filePath]) {
this._moduleCache[filePath] = new Module(filePath, this._fastfs, this);
}
return this._moduleCache[filePath];
}
getAssetModule(filePath) {
filePath = path.resolve(filePath);
if (!this._moduleCache[filePath]) {
this._moduleCache[filePath] = new AssetModule(
filePath,
this._fastfs,
this
);
}
return this._moduleCache[filePath];
}
getPackage(filePath) {
filePath = path.resolve(filePath);
if (!this._packageCache[filePath]){
this._packageCache[filePath] = new Package(filePath, this._fastfs);
}
return this._packageCache[filePath];
}
getPackageForModule(module) {
// TODO(amasad): use ES6 Map.
if (module.__package) {
if (this._packageCache[module.__package]) {
return this._packageCache[module.__package];
} else {
delete module.__package;
}
}
const packagePath = this._fastfs.closest(module.path, 'package.json');
if (!packagePath) {
return null;
}
module.__package = packagePath;
return this.getPackage(packagePath);
}
_processFileChange(type, filePath, root) {
const absPath = path.join(root, filePath);
delete this._moduleCache[absPath];
delete this._packageCache[absPath];
}
}
module.exports = ModuleCache;

View File

@ -1,61 +0,0 @@
/**
* 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.
*/
'use strict';
function ModuleDescriptor(fields) {
if (!fields.id) {
throw new Error('Missing required fields id');
}
this.id = fields.id;
if (!fields.path) {
throw new Error('Missing required fields path');
}
this.path = fields.path;
if (!fields.dependencies) {
throw new Error('Missing required fields dependencies');
}
this.dependencies = fields.dependencies;
this.resolveDependency = fields.resolveDependency;
this.entry = fields.entry || false;
this.isPolyfill = fields.isPolyfill || false;
this.isAsset_DEPRECATED = fields.isAsset_DEPRECATED || false;
this.isAsset = fields.isAsset || false;
if (this.isAsset_DEPRECATED && this.isAsset) {
throw new Error('Cannot be an asset and a deprecated asset');
}
this.resolution = fields.resolution;
if (this.isAsset && isNaN(this.resolution)) {
throw new Error('Expected resolution to be a number for asset modules');
}
this.altId = fields.altId;
this.isJSON = fields.isJSON;
this._fields = fields;
}
ModuleDescriptor.prototype.toJSON = function() {
return {
id: this.id,
path: this.path,
dependencies: this.dependencies
};
};
module.exports = ModuleDescriptor;

View File

@ -0,0 +1,84 @@
'use strict';
const isAbsolutePath = require('absolute-path');
const path = require('path');
class Package {
constructor(file, fastfs) {
this.path = path.resolve(file);
this.root = path.dirname(this.path);
this._fastfs = fastfs;
this.type = 'Package';
}
getMain() {
return this._read().then(json => {
if (typeof json.browser === 'string') {
return path.join(this.root, json.browser);
}
let main = json.main || 'index';
if (json.browser && typeof json.browser === 'object') {
main = json.browser[main] ||
json.browser[main + '.js'] ||
json.browser[main + '.json'] ||
json.browser[main.replace(/(\.js|\.json)$/, '')] ||
main;
}
return path.join(this.root, main);
});
}
isHaste() {
return this._read().then(json => !!json.name);
}
getName() {
return this._read().then(json => json.name);
}
redirectRequire(name) {
return this._read().then(json => {
const {browser} = json;
if (!browser || typeof browser !== 'object') {
return name;
}
if (name[0] !== '/') {
return browser[name] || name;
}
if (!isAbsolutePath(name)) {
throw new Error(`Expected ${name} to be absolute path`);
}
const relPath = './' + path.relative(this.root, name);
const redirect = browser[relPath] ||
browser[relPath + '.js'] ||
browser[relPath + '.json'];
if (redirect) {
return path.join(
this.root,
redirect
);
}
return name;
});
}
_read() {
if (!this._reading) {
this._reading = this._fastfs.readFile(this.path)
.then(jsonStr => JSON.parse(jsonStr));
}
return this._reading;
}
}
module.exports = Package;

View File

@ -10,16 +10,20 @@
jest.dontMock('../') jest.dontMock('../')
.dontMock('q') .dontMock('q')
.dontMock('../replacePatterns') .dontMock('../replacePatterns');
.setMock('../../ModuleDescriptor', function(data) {return data;});
jest.mock('path'); jest.mock('path');
var Promise = require('bluebird'); var Promise = require('promise');
describe('HasteDependencyResolver', function() { describe('HasteDependencyResolver', function() {
var HasteDependencyResolver; var HasteDependencyResolver;
function createModule(o) {
o.getPlainObject = () => Promise.resolve(o);
return o;
}
beforeEach(function() { beforeEach(function() {
// For the polyfillDeps // For the polyfillDeps
require('path').join.mockImpl(function(a, b) { require('path').join.mockImpl(function(a, b) {
@ -30,7 +34,10 @@ describe('HasteDependencyResolver', function() {
describe('getDependencies', function() { describe('getDependencies', function() {
pit('should get dependencies with polyfills', function() { pit('should get dependencies with polyfills', function() {
var module = {id: 'index', path: '/root/index.js', dependencies: ['a']}; var module = createModule({
id: 'index',
path: '/root/index.js', dependencies: ['a']
});
var deps = [module]; var deps = [module];
var depResolver = new HasteDependencyResolver({ var depResolver = new HasteDependencyResolver({
@ -40,7 +47,7 @@ describe('HasteDependencyResolver', function() {
// Is there a better way? How can I mock the prototype instead? // Is there a better way? How can I mock the prototype instead?
var depGraph = depResolver._depGraph; var depGraph = depResolver._depGraph;
depGraph.getOrderedDependencies.mockImpl(function() { depGraph.getOrderedDependencies.mockImpl(function() {
return deps; return Promise.resolve(deps);
}); });
depGraph.load.mockImpl(function() { depGraph.load.mockImpl(function() {
return Promise.resolve(); return Promise.resolve();
@ -113,7 +120,12 @@ describe('HasteDependencyResolver', function() {
}); });
pit('should get dependencies with polyfills', function() { pit('should get dependencies with polyfills', function() {
var module = {id: 'index', path: '/root/index.js', dependencies: ['a']}; var module = createModule({
id: 'index',
path: '/root/index.js',
dependencies: ['a'],
});
var deps = [module]; var deps = [module];
var depResolver = new HasteDependencyResolver({ var depResolver = new HasteDependencyResolver({
@ -123,7 +135,7 @@ describe('HasteDependencyResolver', function() {
// Is there a better way? How can I mock the prototype instead? // Is there a better way? How can I mock the prototype instead?
var depGraph = depResolver._depGraph; var depGraph = depResolver._depGraph;
depGraph.getOrderedDependencies.mockImpl(function() { depGraph.getOrderedDependencies.mockImpl(function() {
return deps; return Promise.resolve(deps);
}); });
depGraph.load.mockImpl(function() { depGraph.load.mockImpl(function() {
return Promise.resolve(); return Promise.resolve();
@ -196,7 +208,11 @@ describe('HasteDependencyResolver', function() {
}); });
pit('should pass in more polyfills', function() { pit('should pass in more polyfills', function() {
var module = {id: 'index', path: '/root/index.js', dependencies: ['a']}; var module = createModule({
id: 'index',
path: '/root/index.js',
dependencies: ['a']
});
var deps = [module]; var deps = [module];
var depResolver = new HasteDependencyResolver({ var depResolver = new HasteDependencyResolver({
@ -207,7 +223,7 @@ describe('HasteDependencyResolver', function() {
// Is there a better way? How can I mock the prototype instead? // Is there a better way? How can I mock the prototype instead?
var depGraph = depResolver._depGraph; var depGraph = depResolver._depGraph;
depGraph.getOrderedDependencies.mockImpl(function() { depGraph.getOrderedDependencies.mockImpl(function() {
return deps; return Promise.resolve(deps);
}); });
depGraph.load.mockImpl(function() { depGraph.load.mockImpl(function() {
return Promise.resolve(); return Promise.resolve();
@ -294,7 +310,7 @@ describe('HasteDependencyResolver', function() {
}); });
describe('wrapModule', function() { describe('wrapModule', function() {
it('should resolve modules', function() { pit('should resolve modules', function() {
var depResolver = new HasteDependencyResolver({ var depResolver = new HasteDependencyResolver({
projectRoot: '/root', projectRoot: '/root',
}); });
@ -446,20 +462,21 @@ describe('HasteDependencyResolver', function() {
depGraph.resolveDependency.mockImpl(function(fromModule, toModuleName) { depGraph.resolveDependency.mockImpl(function(fromModule, toModuleName) {
if (toModuleName === 'x') { if (toModuleName === 'x') {
return { return Promise.resolve(createModule({
id: 'changed' id: 'changed'
}; }));
} else if (toModuleName === 'y') { } else if (toModuleName === 'y') {
return { id: 'Y' }; return Promise.resolve(createModule({ id: 'Y' }));
} }
return null;
return Promise.resolve(null);
}); });
var processedCode = depResolver.wrapModule({ return depResolver.wrapModule({
id: 'test module', id: 'test module',
path: '/root/test.js', path: '/root/test.js',
dependencies: dependencies dependencies: dependencies
}, code); }, code).then(processedCode => {
expect(processedCode).toEqual([ expect(processedCode).toEqual([
'__d(\'test module\',["changed","Y"],function(global,' + '__d(\'test module\',["changed","Y"],function(global,' +
@ -600,8 +617,10 @@ describe('HasteDependencyResolver', function() {
'require("Y")', 'require("Y")',
'require( \'z\' )', 'require( \'z\' )',
'require( "a")', 'require( "a")',
'require("b" )});', 'require("b" )',
'});',
].join('\n')); ].join('\n'));
}); });
}); });
});
}); });

View File

@ -0,0 +1,310 @@
'use strict';
const Promise = require('promise');
const {EventEmitter} = require('events');
const _ = require('underscore');
const debug = require('debug')('DependencyGraph');
const fs = require('fs');
const path = require('path');
const readDir = Promise.denodeify(fs.readdir);
const readFile = Promise.denodeify(fs.readFile);
const stat = Promise.denodeify(fs.stat);
const hasOwn = Object.prototype.hasOwnProperty;
class Fastfs extends EventEmitter {
constructor(roots, fileWatcher, {ignore, pattern}) {
super();
this._fileWatcher = fileWatcher;
this._ignore = ignore;
this._pattern = pattern;
this._roots = roots.map(root => new File(root, { isDir: true }));
this._fastPaths = Object.create(null);
}
build() {
const queue = this._roots.slice();
return this._search(queue).then(() => {
this._fileWatcher.on('all', this._processFileChange.bind(this));
});
}
stat(filePath) {
return Promise.resolve().then(() => {
const file = this._getFile(filePath);
return file.stat();
});
}
getAllFiles() {
return _.chain(this._roots)
.map(root => root.getFiles())
.flatten()
.value();
}
findFilesByExt(ext, { ignore }) {
return this.getAllFiles()
.filter(
file => file.ext() === ext && (!ignore || !ignore(file.path))
)
.map(file => file.path);
}
findFilesByExts(exts) {
return this.getAllFiles()
.filter(file => exts.indexOf(file.ext()) !== -1)
.map(file => file.path);
}
findFilesByName(name, { ignore }) {
return this.getAllFiles()
.filter(
file => path.basename(file.path) === name &&
(!ignore || !ignore(file.path))
)
.map(file => file.path);
}
readFile(filePath) {
return this._getFile(filePath).read();
}
closest(filePath, name) {
for (let file = this._getFile(filePath).parent;
file;
file = file.parent) {
if (file.children[name]) {
return file.children[name].path;
}
}
return null;
}
fileExists(filePath) {
const file = this._getFile(filePath);
return file && !file.isDir;
}
dirExists(filePath) {
const file = this._getFile(filePath);
return file && file.isDir;
}
matches(dir, pattern) {
let dirFile = this._getFile(dir);
if (!dirFile.isDir) {
throw new Error(`Expected file ${dirFile.path} to be a directory`);
}
return Object.keys(dirFile.children)
.filter(name => name.match(pattern))
.map(name => path.join(dirFile.path, name));
}
_getRoot(filePath) {
for (let i = 0; i < this._roots.length; i++) {
let possibleRoot = this._roots[i];
if (isDescendant(possibleRoot.path, filePath)) {
return possibleRoot;
}
}
return null;
}
_getAndAssertRoot(filePath) {
const root = this._getRoot(filePath);
if (!root) {
throw new Error(`File ${filePath} not found in any of the roots`);
}
return root;
}
_getFile(filePath) {
filePath = path.normalize(filePath);
if (!hasOwn.call(this._fastPaths, filePath)) {
this._fastPaths[filePath] = this._getAndAssertRoot(filePath).getFileFromPath(filePath);
}
return this._fastPaths[filePath];
}
_add(file) {
this._getAndAssertRoot(file.path).addChild(file);
}
_search(queue) {
const dir = queue.shift();
if (!dir) {
return Promise.resolve();
}
return readAndStatDir(dir.path).then(([filePaths, stats]) => {
filePaths.forEach((filePath, i) => {
if (this._ignore(filePath)) {
return;
}
if (stats[i].isDirectory()) {
queue.push(
new File(filePath, { isDir: true, fstat: stats[i] })
);
return;
}
if (filePath.match(this._pattern)) {
this._add(new File(filePath, { fstat: stats[i] }));
}
});
return this._search(queue);
});
}
_processFileChange(type, filePath, root, fstat) {
const absPath = path.join(root, filePath);
if (this._ignore(absPath) || (fstat && fstat.isDirectory())) {
return;
}
// Make sure this event belongs to one of our roots.
if (!this._getRoot(absPath)) {
return;
}
if (type === 'delete' || type === 'change') {
const file = this._getFile(absPath);
if (file) {
file.remove();
}
}
delete this._fastPaths[path.normalize(absPath)];
if (type !== 'delete') {
this._add(new File(absPath, {
isDir: false,
fstat
}));
}
this.emit('change', type, filePath, root, fstat);
}
}
class File {
constructor(filePath, {isDir, fstat}) {
this.path = filePath;
this.isDir = Boolean(isDir);
if (this.isDir) {
this.children = Object.create(null);
}
if (fstat) {
this._stat = Promise.resolve(fstat);
}
}
read() {
if (!this._read) {
this._read = readFile(this.path, 'utf8');
}
return this._read;
}
stat() {
if (!this._stat) {
this._stat = stat(this.path);
}
return this._stat;
}
addChild(file) {
const parts = path.relative(this.path, file.path).split(path.sep);
if (parts.length === 0) {
return;
}
if (parts.length === 1) {
this.children[parts[0]] = file;
file.parent = this;
} else if (this.children[parts[0]]) {
this.children[parts[0]].addChild(file);
} else {
const dir = new File(path.join(this.path, parts[0]), { isDir: true });
dir.parent = this;
this.children[parts[0]] = dir;
dir.addChild(file);
}
}
getFileFromPath(filePath) {
const parts = path.relative(this.path, filePath)
.split(path.sep);
/*eslint consistent-this:0*/
let file = this;
for (let i = 0; i < parts.length; i++) {
let fileName = parts[i];
if (!fileName) {
continue;
}
if (!file || !file.isDir) {
// File not found.
return null;
}
file = file.children[fileName];
}
return file;
}
getFiles() {
return _.flatten(_.values(this.children).map(file => {
if (file.isDir) {
return file.getFiles();
} else {
return file;
}
}));
}
ext() {
return path.extname(this.path).replace(/^\./, '');
}
remove() {
if (!this.parent) {
throw new Error(`No parent to delete ${this.path} from`);
}
delete this.parent.children[path.basename(this.path)];
}
}
function isDescendant(root, child) {
return path.relative(root, child).indexOf('..') !== 0;
}
function readAndStatDir(dir) {
return readDir(dir)
.then(files => Promise.all(files.map(f => path.join(dir, f))))
.then(files => Promise.all(
files.map(f => stat(f).catch(handleBrokenLink))
).then(stats => [
// Remove broken links.
files.filter((file, i ) => !!stats[i]),
stats.filter(Boolean),
]));
}
function handleBrokenLink(e) {
debug('WARNING: error stating, possibly broken symlink', e.message);
return Promise.resolve();
}
module.exports = Fastfs;

View File

@ -1,798 +0,0 @@
/**
* 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.
*/
'use strict';
var ModuleDescriptor = require('../../ModuleDescriptor');
var Promise = require('bluebird');
var fs = require('fs');
var docblock = require('./docblock');
var replacePatterns = require('../replacePatterns');
var path = require('path');
var isAbsolutePath = require('absolute-path');
var debug = require('debug')('DependecyGraph');
var util = require('util');
var declareOpts = require('../../../lib/declareOpts');
var getAssetDataFromName = require('../../../lib/getAssetDataFromName');
var readFile = Promise.promisify(fs.readFile);
var readDir = Promise.promisify(fs.readdir);
var lstat = Promise.promisify(fs.lstat);
var realpath = Promise.promisify(fs.realpath);
var validateOpts = declareOpts({
roots: {
type: 'array',
required: true,
},
ignoreFilePath: {
type: 'function',
default: function(){}
},
fileWatcher: {
type: 'object',
required: true,
},
assetRoots_DEPRECATED: {
type: 'array',
default: [],
},
assetExts: {
type: 'array',
required: true,
}
});
function DependecyGraph(options) {
var opts = validateOpts(options);
this._roots = opts.roots;
this._assetRoots_DEPRECATED = opts.assetRoots_DEPRECATED;
this._assetExts = opts.assetExts;
this._ignoreFilePath = opts.ignoreFilePath;
this._fileWatcher = options.fileWatcher;
this._loaded = false;
this._queue = this._roots.slice();
this._graph = Object.create(null);
this._packageByRoot = Object.create(null);
this._packagesById = Object.create(null);
this._moduleById = Object.create(null);
this._debugUpdateEvents = [];
this._moduleExtPattern = new RegExp(
'\.(' + ['js', 'json'].concat(this._assetExts).join('|') + ')$'
);
// Kick off the search process to precompute the dependency graph.
this._init();
}
DependecyGraph.prototype.load = function() {
if (this._loading != null) {
return this._loading;
}
this._loading = Promise.all([
this._search(),
this._buildAssetMap_DEPRECATED(),
]);
return this._loading;
};
/**
* Given an entry file return an array of all the dependent module descriptors.
*/
DependecyGraph.prototype.getOrderedDependencies = function(entryPath) {
var absolutePath = this._getAbsolutePath(entryPath);
if (absolutePath == null) {
throw new NotFoundError(
'Cannot find entry file %s in any of the roots: %j',
entryPath,
this._roots
);
}
var module = this._graph[absolutePath];
if (module == null) {
throw new Error('Module with path "' + entryPath + '" is not in graph');
}
var self = this;
var deps = [];
var visited = Object.create(null);
// Node haste sucks. Id's aren't unique. So to make sure our entry point
// is the thing that ends up in our dependency list.
var graphMap = Object.create(this._moduleById);
graphMap[module.id] = module;
// Recursively collect the dependency list.
function collect(module) {
deps.push(module);
module.dependencies.forEach(function(name) {
var id = sansExtJs(name);
var dep = self.resolveDependency(module, id);
if (dep == null) {
debug(
'WARNING: Cannot find required module `%s` from module `%s`.',
name,
module.id
);
return;
}
if (!visited[dep.id]) {
visited[dep.id] = true;
collect(dep);
}
});
}
visited[module.id] = true;
collect(module);
return deps;
};
/**
* Given a module descriptor `fromModule` return the module descriptor for
* the required module `depModuleId`. It could be top-level or relative,
* or both.
*/
DependecyGraph.prototype.resolveDependency = function(
fromModule,
depModuleId
) {
if (this._assetMap_DEPRECATED != null) {
var assetMatch = depModuleId.match(/^image!(.+)/);
// Process DEPRECATED global asset requires.
if (assetMatch && assetMatch[1]) {
if (!this._assetMap_DEPRECATED[assetMatch[1]]) {
debug('WARINING: Cannot find asset:', assetMatch[1]);
return null;
}
return this._assetMap_DEPRECATED[assetMatch[1]];
}
}
var packageJson, modulePath, dep;
// Package relative modules starts with '.' or '..'.
if (depModuleId[0] !== '.') {
// Check if we need to map the dependency to something else via the
// `browser` field in package.json
var fromPackageJson = this._lookupPackage(fromModule.path);
if (fromPackageJson && fromPackageJson.browser &&
fromPackageJson.browser[depModuleId]) {
depModuleId = fromPackageJson.browser[depModuleId];
}
// `depModuleId` is simply a top-level `providesModule`.
// `depModuleId` is a package module but given the full path from the
// package, i.e. package_name/module_name
if (this._moduleById[sansExtJs(depModuleId)]) {
return this._moduleById[sansExtJs(depModuleId)];
}
// `depModuleId` is a package and it's depending on the "main" resolution.
packageJson = this._packagesById[depModuleId];
// We are being forgiving here and raising an error because we could be
// processing a file that uses it's own require system.
if (packageJson == null) {
debug(
'WARNING: Cannot find required module `%s` from module `%s`.',
depModuleId,
fromModule.id
);
return null;
}
var main;
// We prioritize the `browser` field if it's a module path.
if (typeof packageJson.browser === 'string') {
main = packageJson.browser;
} else {
main = packageJson.main || 'index';
}
// If there is a mapping for main in the `browser` field.
if (packageJson.browser && typeof packageJson.browser === 'object') {
var tmpMain = packageJson.browser[main] ||
packageJson.browser[withExtJs(main)] ||
packageJson.browser[sansExtJs(main)];
if (tmpMain) {
main = tmpMain;
}
}
modulePath = withExtJs(path.join(packageJson._root, main));
dep = this._graph[modulePath];
// Some packages use just a dir and rely on an index.js inside that dir.
if (dep == null) {
dep = this._graph[path.join(packageJson._root, main, 'index.js')];
}
if (dep == null) {
throw new Error(
'Cannot find package main file for package: ' + packageJson._root
);
}
return dep;
} else {
// `depModuleId` is a module defined in a package relative to `fromModule`.
packageJson = this._lookupPackage(fromModule.path);
if (packageJson == null) {
throw new Error(
'Expected relative module lookup from ' + fromModule.id + ' to ' +
depModuleId + ' to be within a package but no package.json found.'
);
}
// Example: depModuleId: ../a/b
// fromModule.path: /x/y/z
// modulePath: /x/y/a/b
var dir = path.dirname(fromModule.path);
modulePath = path.join(dir, depModuleId);
if (packageJson.browser && typeof packageJson.browser === 'object') {
var relPath = './' + path.relative(packageJson._root, modulePath);
var tmpModulePath = packageJson.browser[withExtJs(relPath)] ||
packageJson.browser[sansExtJs(relPath)];
if (tmpModulePath) {
modulePath = path.join(packageJson._root, tmpModulePath);
}
}
// JS modules can be required without extensios.
if (!this._isFileAsset(modulePath) && !modulePath.match(/\.json$/)) {
modulePath = withExtJs(modulePath);
}
dep = this._graph[modulePath];
// Maybe the dependency is a directory and there is an index.js inside it.
if (dep == null) {
dep = this._graph[path.join(dir, depModuleId, 'index.js')];
}
// Maybe it's an asset with @n.nx resolution and the path doesn't map
// to the id
if (dep == null && this._isFileAsset(modulePath)) {
dep = this._moduleById[this._lookupName(modulePath)];
}
if (dep == null) {
debug(
'WARNING: Cannot find required module `%s` from module `%s`.' +
' Inferred required module path is %s',
depModuleId,
fromModule.id,
modulePath
);
return null;
}
return dep;
}
};
/**
* Intiates the filewatcher and kicks off the search process.
*/
DependecyGraph.prototype._init = function() {
var processChange = this._processFileChange.bind(this);
var watcher = this._fileWatcher;
this._loading = this.load().then(function() {
watcher.on('all', processChange);
});
};
/**
* Implements a DFS over the file system looking for modules and packages.
*/
DependecyGraph.prototype._search = function() {
var self = this;
var dir = this._queue.shift();
if (dir == null) {
return Promise.resolve(this._graph);
}
// Steps:
// 1. Read a dir and stat all the entries.
// 2. Filter the files and queue up the directories.
// 3. Process any package.json in the files
// 4. recur.
return readAndStatDir(dir)
.spread(function(files, stats) {
var modulePaths = files.filter(function(filePath, i) {
if (self._ignoreFilePath(filePath)) {
return false;
}
if (stats[i].isDirectory()) {
self._queue.push(filePath);
return false;
}
if (stats[i].isSymbolicLink()) {
return false;
}
return filePath.match(self._moduleExtPattern);
});
var processing = self._findAndProcessPackage(files, dir)
.then(function() {
return Promise.all(modulePaths.map(self._processModule.bind(self)));
});
return Promise.all([
processing,
self._search()
]);
})
.then(function() {
return self;
});
};
/**
* Given a list of files find a `package.json` file, and if found parse it
* and update indices.
*/
DependecyGraph.prototype._findAndProcessPackage = function(files, root) {
var self = this;
var packagePath;
for (var i = 0; i < files.length ; i++) {
var file = files[i];
if (path.basename(file) === 'package.json') {
packagePath = file;
break;
}
}
if (packagePath != null) {
return this._processPackage(packagePath);
} else {
return Promise.resolve();
}
};
DependecyGraph.prototype._processPackage = function(packagePath) {
var packageRoot = path.dirname(packagePath);
var self = this;
return readFile(packagePath, 'utf8')
.then(function(content) {
var packageJson;
try {
packageJson = JSON.parse(content);
} catch (e) {
debug('WARNING: malformed package.json: ', packagePath);
return Promise.resolve();
}
if (packageJson.name == null) {
debug(
'WARNING: package.json `%s` is missing a name field',
packagePath
);
return Promise.resolve();
}
packageJson._root = packageRoot;
self._addPackageToIndices(packageJson);
return packageJson;
});
};
DependecyGraph.prototype._addPackageToIndices = function(packageJson) {
this._packageByRoot[packageJson._root] = packageJson;
this._packagesById[packageJson.name] = packageJson;
};
DependecyGraph.prototype._removePackageFromIndices = function(packageJson) {
delete this._packageByRoot[packageJson._root];
delete this._packagesById[packageJson.name];
};
/**
* Parse a module and update indices.
*/
DependecyGraph.prototype._processModule = function(modulePath) {
var moduleData = { path: path.resolve(modulePath) };
var module;
if (this._assetExts.indexOf(extname(modulePath)) > -1) {
var assetData = getAssetDataFromName(this._lookupName(modulePath));
moduleData.id = assetData.assetName;
moduleData.resolution = assetData.resolution;
moduleData.isAsset = true;
moduleData.dependencies = [];
module = new ModuleDescriptor(moduleData);
this._updateGraphWithModule(module);
return Promise.resolve(module);
}
if (extname(modulePath) === 'json') {
moduleData.id = this._lookupName(modulePath);
moduleData.isJSON = true;
moduleData.dependencies = [];
module = new ModuleDescriptor(moduleData);
this._updateGraphWithModule(module);
return Promise.resolve(module);
}
var self = this;
return readFile(modulePath, 'utf8')
.then(function(content) {
var moduleDocBlock = docblock.parseAsObject(content);
if (moduleDocBlock.providesModule || moduleDocBlock.provides) {
moduleData.id = /^(\S*)/.exec(
moduleDocBlock.providesModule || moduleDocBlock.provides
)[1];
// Incase someone wants to require this module via
// packageName/path/to/module
moduleData.altId = self._lookupName(modulePath);
} else {
moduleData.id = self._lookupName(modulePath);
}
moduleData.dependencies = extractRequires(content);
module = new ModuleDescriptor(moduleData);
self._updateGraphWithModule(module);
return module;
});
};
/**
* Compute the name of module relative to a package it may belong to.
*/
DependecyGraph.prototype._lookupName = function(modulePath) {
var packageJson = this._lookupPackage(modulePath);
if (packageJson == null) {
return path.resolve(modulePath);
} else {
var relativePath =
sansExtJs(path.relative(packageJson._root, modulePath));
return path.join(packageJson.name, relativePath);
}
};
DependecyGraph.prototype._deleteModule = function(module) {
delete this._graph[module.path];
// Others may keep a reference so we mark it as deleted.
module.deleted = true;
// Haste allows different module to have the same id.
if (this._moduleById[module.id] === module) {
delete this._moduleById[module.id];
}
if (module.altId && this._moduleById[module.altId] === module) {
delete this._moduleById[module.altId];
}
};
/**
* Update the graph and indices with the module.
*/
DependecyGraph.prototype._updateGraphWithModule = function(module) {
if (this._graph[module.path]) {
this._deleteModule(this._graph[module.path]);
}
this._graph[module.path] = module;
if (this._moduleById[module.id]) {
debug(
'WARNING: Top-level module name conflict `%s`.\n' +
'module with path `%s` will replace `%s`',
module.id,
module.path,
this._moduleById[module.id].path
);
}
this._moduleById[module.id] = module;
// Some module maybe refrenced by both @providesModule and
// require(package/moduleName).
if (module.altId != null && this._moduleById[module.altId] == null) {
this._moduleById[module.altId] = module;
}
};
/**
* Find the nearest package to a module.
*/
DependecyGraph.prototype._lookupPackage = function(modulePath) {
var packageByRoot = this._packageByRoot;
/**
* Auxiliary function to recursively lookup a package.
*/
function lookupPackage(currDir) {
// ideally we stop once we're outside root and this can be a simple child
// dir check. However, we have to support modules that was symlinked inside
// our project root.
if (currDir === '/') {
return null;
} else {
var packageJson = packageByRoot[currDir];
if (packageJson) {
return packageJson;
} else {
return lookupPackage(path.dirname(currDir));
}
}
}
return lookupPackage(path.dirname(modulePath));
};
/**
* Process a filewatcher change event.
*/
DependecyGraph.prototype._processFileChange = function(
eventType,
filePath,
root,
stat
) {
var absPath = path.join(root, filePath);
if (this._ignoreFilePath(absPath)) {
return;
}
this._debugUpdateEvents.push({event: eventType, path: filePath});
if (this._assetExts.indexOf(extname(filePath)) > -1) {
this._processAssetChange_DEPRECATED(eventType, absPath);
// Fall through because new-style assets are actually modules.
}
var isPackage = path.basename(filePath) === 'package.json';
if (eventType === 'delete') {
if (isPackage) {
var packageJson = this._packageByRoot[path.dirname(absPath)];
if (packageJson) {
this._removePackageFromIndices(packageJson);
}
} else {
var module = this._graph[absPath];
if (module == null) {
return;
}
this._deleteModule(module);
}
} else if (!(stat && stat.isDirectory())) {
var self = this;
this._loading = this._loading.then(function() {
if (isPackage) {
return self._processPackage(absPath);
}
return self._processModule(absPath);
});
}
};
DependecyGraph.prototype.getDebugInfo = function() {
return '<h1>FileWatcher Update Events</h1>' +
'<pre>' + util.inspect(this._debugUpdateEvents) + '</pre>' +
'<h1> Graph dump </h1>' +
'<pre>' + util.inspect(this._graph) + '</pre>';
};
/**
* Searches all roots for the file and returns the first one that has file of
* the same path.
*/
DependecyGraph.prototype._getAbsolutePath = function(filePath) {
if (isAbsolutePath(filePath)) {
return filePath;
}
for (var i = 0; i < this._roots.length; i++) {
var root = this._roots[i];
var absPath = path.join(root, filePath);
if (this._graph[absPath]) {
return absPath;
}
}
return null;
};
DependecyGraph.prototype._buildAssetMap_DEPRECATED = function() {
if (this._assetRoots_DEPRECATED == null ||
this._assetRoots_DEPRECATED.length === 0) {
return Promise.resolve();
}
this._assetMap_DEPRECATED = Object.create(null);
return buildAssetMap_DEPRECATED(
this._assetRoots_DEPRECATED,
this._processAsset_DEPRECATED.bind(this)
);
};
DependecyGraph.prototype._processAsset_DEPRECATED = function(file) {
var ext = extname(file);
if (this._assetExts.indexOf(ext) !== -1) {
var name = assetName(file, ext);
if (this._assetMap_DEPRECATED[name] != null) {
debug('Conflcting assets', name);
}
this._assetMap_DEPRECATED[name] = new ModuleDescriptor({
id: 'image!' + name,
path: path.resolve(file),
isAsset_DEPRECATED: true,
dependencies: [],
resolution: getAssetDataFromName(file).resolution,
});
}
};
DependecyGraph.prototype._processAssetChange_DEPRECATED = function(eventType, file) {
if (this._assetMap_DEPRECATED == null) {
return;
}
var name = assetName(file, extname(file));
if (eventType === 'change' || eventType === 'delete') {
delete this._assetMap_DEPRECATED[name];
}
if (eventType === 'change' || eventType === 'add') {
this._processAsset_DEPRECATED(file);
}
};
DependecyGraph.prototype._isFileAsset = function(file) {
return this._assetExts.indexOf(extname(file)) !== -1;
};
/**
* Extract all required modules from a `code` string.
*/
var blockCommentRe = /\/\*(.|\n)*?\*\//g;
var lineCommentRe = /\/\/.+(\n|$)/g;
function extractRequires(code) {
var deps = [];
code
.replace(blockCommentRe, '')
.replace(lineCommentRe, '')
.replace(replacePatterns.IMPORT_RE, function(match, pre, quot, dep, post) {
deps.push(dep);
return match;
})
.replace(replacePatterns.REQUIRE_RE, function(match, pre, quot, dep, post) {
deps.push(dep);
});
return deps;
}
/**
* `file` without the .js extension.
*/
function sansExtJs(file) {
if (file.match(/\.js$/)) {
return file.slice(0, -3);
} else {
return file;
}
}
/**
* `file` with the .js extension.
*/
function withExtJs(file) {
if (file.match(/\.js$/)) {
return file;
} else {
return file + '.js';
}
}
function handleBrokenLink(e) {
debug('WARNING: error stating, possibly broken symlink', e.message);
return Promise.resolve();
}
function readAndStatDir(dir) {
return readDir(dir)
.then(function(files){
return Promise.all(files.map(function(filePath) {
return realpath(path.join(dir, filePath)).catch(handleBrokenLink);
}));
}).then(function(files) {
files = files.filter(function(f) {
return !!f;
});
var stats = files.map(function(filePath) {
return lstat(filePath).catch(handleBrokenLink);
});
return [
files,
Promise.all(stats),
];
});
}
/**
* Given a list of roots and list of extensions find all the files in
* the directory with that extension and build a map of those assets.
*/
function buildAssetMap_DEPRECATED(roots, processAsset) {
var queue = roots.slice(0);
function search() {
var root = queue.shift();
if (root == null) {
return Promise.resolve();
}
return readAndStatDir(root).spread(function(files, stats) {
files.forEach(function(file, i) {
if (stats[i].isDirectory()) {
queue.push(file);
} else {
processAsset(file);
}
});
return search();
});
}
return search();
}
function assetName(file, ext) {
return path.basename(file, '.' + ext).replace(/@[\d\.]+x/, '');
}
function extname(name) {
return path.extname(name).replace(/^\./, '');
}
function NotFoundError() {
Error.call(this);
Error.captureStackTrace(this, this.constructor);
var msg = util.format.apply(util, arguments);
this.message = msg;
this.type = this.name = 'NotFoundError';
this.status = 404;
}
util.inherits(NotFoundError, Error);
module.exports = DependecyGraph;

View File

@ -1,177 +0,0 @@
/**
* 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.
*/
'use strict';
var path = require('path');
var DependencyGraph = require('./DependencyGraph');
var replacePatterns = require('./replacePatterns');
var ModuleDescriptor = require('../ModuleDescriptor');
var declareOpts = require('../../lib/declareOpts');
var DEFINE_MODULE_CODE = [
'__d(',
'\'_moduleName_\',',
'_deps_,',
'function(global, require, requireDynamic, requireLazy, module, exports) {',
' _code_',
'}',
');',
].join('');
var DEFINE_MODULE_REPLACE_RE = /_moduleName_|_code_|_deps_/g;
var validateOpts = declareOpts({
projectRoots: {
type: 'array',
required: true,
},
blacklistRE: {
type: 'object', // typeof regex is object
},
polyfillModuleNames: {
type: 'array',
default: [],
},
nonPersistent: {
type: 'boolean',
default: false,
},
moduleFormat: {
type: 'string',
default: 'haste',
},
assetRoots: {
type: 'array',
default: [],
},
fileWatcher: {
type: 'object',
required: true,
},
assetExts: {
type: 'array',
required: true,
}
});
function HasteDependencyResolver(options) {
var opts = validateOpts(options);
this._depGraph = new DependencyGraph({
roots: opts.projectRoots,
assetRoots_DEPRECATED: opts.assetRoots,
assetExts: opts.assetExts,
ignoreFilePath: function(filepath) {
return filepath.indexOf('__tests__') !== -1 ||
(opts.blacklistRE && opts.blacklistRE.test(filepath));
},
fileWatcher: opts.fileWatcher,
});
this._polyfillModuleNames = opts.polyfillModuleNames || [];
}
var getDependenciesValidateOpts = declareOpts({
dev: {
type: 'boolean',
default: true,
},
});
HasteDependencyResolver.prototype.getDependencies = function(main, options) {
var opts = getDependenciesValidateOpts(options);
var depGraph = this._depGraph;
var self = this;
return depGraph.load()
.then(function() {
var dependencies = depGraph.getOrderedDependencies(main);
var mainModuleId = dependencies[0].id;
self._prependPolyfillDependencies(dependencies, opts.dev);
return {
mainModuleId: mainModuleId,
dependencies: dependencies
};
});
};
HasteDependencyResolver.prototype._prependPolyfillDependencies = function(
dependencies,
isDev
) {
var polyfillModuleNames = [
isDev
? path.join(__dirname, 'polyfills/prelude_dev.js')
: path.join(__dirname, 'polyfills/prelude.js'),
path.join(__dirname, 'polyfills/require.js'),
path.join(__dirname, 'polyfills/polyfills.js'),
path.join(__dirname, 'polyfills/console.js'),
path.join(__dirname, 'polyfills/error-guard.js'),
path.join(__dirname, 'polyfills/String.prototype.es6.js'),
path.join(__dirname, 'polyfills/Array.prototype.es6.js'),
].concat(this._polyfillModuleNames);
var polyfillModules = polyfillModuleNames.map(
function(polyfillModuleName, idx) {
return new ModuleDescriptor({
path: polyfillModuleName,
id: polyfillModuleName,
dependencies: polyfillModuleNames.slice(0, idx),
isPolyfill: true
});
}
);
dependencies.unshift.apply(dependencies, polyfillModules);
};
HasteDependencyResolver.prototype.wrapModule = function(module, code) {
if (module.isPolyfill) {
return code;
}
var resolvedDeps = Object.create(null);
var resolvedDepsArr = [];
for (var i = 0; i < module.dependencies.length; i++) {
var depName = module.dependencies[i];
var dep = this._depGraph.resolveDependency(module, depName);
if (dep) {
resolvedDeps[depName] = dep.id;
resolvedDepsArr.push(dep.id);
}
}
var relativizeCode = function(codeMatch, pre, quot, depName, post) {
var depId = resolvedDeps[depName];
if (depId) {
return pre + quot + depId + post;
} else {
return codeMatch;
}
};
return DEFINE_MODULE_CODE.replace(DEFINE_MODULE_REPLACE_RE, function(key) {
return {
'_moduleName_': module.id,
'_code_': code.replace(replacePatterns.IMPORT_RE, relativizeCode)
.replace(replacePatterns.REQUIRE_RE, relativizeCode),
'_deps_': JSON.stringify(resolvedDepsArr),
}[key];
});
};
HasteDependencyResolver.prototype.getDebugInfo = function() {
return this._depGraph.getDebugInfo();
};
module.exports = HasteDependencyResolver;

View File

@ -8,15 +8,174 @@
*/ */
'use strict'; 'use strict';
var HasteDependencyResolver = require('./haste'); var path = require('path');
var NodeDependencyResolver = require('./node'); var DependencyGraph = require('./DependencyGraph');
var replacePatterns = require('./replacePatterns');
var declareOpts = require('../lib/declareOpts');
var Promise = require('promise');
module.exports = function createDependencyResolver(options) { var validateOpts = declareOpts({
if (options.moduleFormat === 'haste') { projectRoots: {
return new HasteDependencyResolver(options); type: 'array',
} else if (options.moduleFormat === 'node') { required: true,
return new NodeDependencyResolver(options); },
} else { blacklistRE: {
throw new Error('unsupported'); type: 'object', // typeof regex is object
},
polyfillModuleNames: {
type: 'array',
default: [],
},
nonPersistent: {
type: 'boolean',
default: false,
},
moduleFormat: {
type: 'string',
default: 'haste',
},
assetRoots: {
type: 'array',
default: [],
},
fileWatcher: {
type: 'object',
required: true,
},
assetExts: {
type: 'array',
required: true,
} }
});
function HasteDependencyResolver(options) {
var opts = validateOpts(options);
this._depGraph = new DependencyGraph({
roots: opts.projectRoots,
assetRoots_DEPRECATED: opts.assetRoots,
assetExts: opts.assetExts,
ignoreFilePath: function(filepath) {
return filepath.indexOf('__tests__') !== -1 ||
(opts.blacklistRE && opts.blacklistRE.test(filepath));
},
fileWatcher: opts.fileWatcher,
});
this._polyfillModuleNames = opts.polyfillModuleNames || [];
}
var getDependenciesValidateOpts = declareOpts({
dev: {
type: 'boolean',
default: true,
},
});
HasteDependencyResolver.prototype.getDependencies = function(main, options) {
var opts = getDependenciesValidateOpts(options);
var depGraph = this._depGraph;
var self = this;
return depGraph.load().then(
() => depGraph.getOrderedDependencies(main).then(
dependencies => {
const mainModuleId = dependencies[0].id;
self._prependPolyfillDependencies(
dependencies,
opts.dev
);
return {
mainModuleId: mainModuleId,
dependencies: dependencies
};
}
)
);
}; };
HasteDependencyResolver.prototype._prependPolyfillDependencies = function(
dependencies,
isDev
) {
var polyfillModuleNames = [
isDev
? path.join(__dirname, 'polyfills/prelude_dev.js')
: path.join(__dirname, 'polyfills/prelude.js'),
path.join(__dirname, 'polyfills/require.js'),
path.join(__dirname, 'polyfills/polyfills.js'),
path.join(__dirname, 'polyfills/console.js'),
path.join(__dirname, 'polyfills/error-guard.js'),
path.join(__dirname, 'polyfills/String.prototype.es6.js'),
path.join(__dirname, 'polyfills/Array.prototype.es6.js'),
].concat(this._polyfillModuleNames);
var polyfillModules = polyfillModuleNames.map(
(polyfillModuleName, idx) => ({
path: polyfillModuleName,
id: polyfillModuleName,
dependencies: polyfillModuleNames.slice(0, idx),
isPolyfill: true,
})
);
dependencies.unshift.apply(dependencies, polyfillModules);
};
HasteDependencyResolver.prototype.wrapModule = function(module, code) {
if (module.isPolyfill) {
return Promise.resolve(code);
}
const resolvedDeps = Object.create(null);
const resolvedDepsArr = [];
return Promise.all(
module.dependencies.map(depName => {
return this._depGraph.resolveDependency(module, depName)
.then((dep) => dep && dep.getPlainObject().then(mod => {
if (mod) {
resolvedDeps[depName] = mod.id;
resolvedDepsArr.push(mod.id);
}
}));
})
).then(() => {
const relativizeCode = (codeMatch, pre, quot, depName, post) => {
const depId = resolvedDeps[depName];
if (depId) {
return pre + quot + depId + post;
} else {
return codeMatch;
}
};
return defineModuleCode({
code: code
.replace(replacePatterns.IMPORT_RE, relativizeCode)
.replace(replacePatterns.REQUIRE_RE, relativizeCode),
deps: JSON.stringify(resolvedDepsArr),
moduleName: module.id,
});
});
};
HasteDependencyResolver.prototype.getDebugInfo = function() {
return this._depGraph.getDebugInfo();
};
function defineModuleCode({moduleName, code, deps}) {
return [
`__d(`,
`'${moduleName}',`,
`${deps},`,
'function(global, require, ',
'requireDynamic, requireLazy, module, exports) {',
` ${code}`,
'\n});',
].join('');
}
module.exports = HasteDependencyResolver;

View File

@ -1,51 +0,0 @@
/**
* 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.
*/
'use strict';
var Promise = require('bluebird');
var ModuleDescriptor = require('../ModuleDescriptor');
var mdeps = require('module-deps');
var path = require('path');
exports.getRuntimeCode = function() {};
exports.wrapModule = function(id, source) {
return Promise.resolve(
'define(' + JSON.stringify(id) + ',' + ' function(exports, module) {\n'
+ source + '\n});'
);
};
exports.getDependencies = function(root, fileEntryPath) {
return new Promise(function(resolve) {
fileEntryPath = path.join(process.cwd(), root, fileEntryPath);
var md = mdeps();
md.end({file: fileEntryPath});
var deps = [];
md.on('data', function(data) {
deps.push(
new ModuleDescriptor({
id: data.id,
deps: data.deps,
path: data.file,
entry: data.entry
})
);
});
md.on('end', function() {
resolve(deps);
});
});
};

View File

@ -10,7 +10,7 @@
var EventEmitter = require('events').EventEmitter; var EventEmitter = require('events').EventEmitter;
var sane = require('sane'); var sane = require('sane');
var Promise = require('bluebird'); var Promise = require('promise');
var util = require('util'); var util = require('util');
var exec = require('child_process').exec; var exec = require('child_process').exec;
@ -57,7 +57,7 @@ util.inherits(FileWatcher, EventEmitter);
FileWatcher.prototype.end = function() { FileWatcher.prototype.end = function() {
return this._loading.then(function(watchers) { return this._loading.then(function(watchers) {
watchers.forEach(function(watcher) { watchers.forEach(function(watcher) {
return Promise.promisify(watcher.close, watcher)(); return Promise.denodeify(watcher.close).call(watcher);
}); });
}); });
}; };

View File

@ -14,7 +14,7 @@ var declareOpts = require('../lib/declareOpts');
var fs = require('fs'); var fs = require('fs');
var isAbsolutePath = require('absolute-path'); var isAbsolutePath = require('absolute-path');
var path = require('path'); var path = require('path');
var Promise = require('bluebird'); var Promise = require('promise');
var tmpdir = require('os').tmpDir(); var tmpdir = require('os').tmpDir();
var version = require('../../../../package.json').version; var version = require('../../../../package.json').version;
@ -74,11 +74,13 @@ Cache.prototype.get = function(filepath, loaderCb) {
Cache.prototype._set = function(filepath, loaderPromise) { Cache.prototype._set = function(filepath, loaderPromise) {
this._data[filepath] = loaderPromise.then(function(data) { this._data[filepath] = loaderPromise.then(function(data) {
return [ return Promise.all([
data, data,
Promise.promisify(fs.stat)(filepath) Promise.denodeify(fs.stat)(filepath)
]; ]);
}).spread(function(data, stat) { }).then(function(ref) {
var data = ref[0];
var stat = ref[1];
this._persistEventually(); this._persistEventually();
return { return {
data: data, data: data,
@ -113,7 +115,7 @@ Cache.prototype._persistCache = function() {
Object.keys(data).forEach(function(key, i) { Object.keys(data).forEach(function(key, i) {
json[key] = values[i]; json[key] = values[i];
}); });
return Promise.promisify(fs.writeFile)(cacheFilepath, JSON.stringify(json)); return Promise.denodeify(fs.writeFile)(cacheFilepath, JSON.stringify(json));
}) })
.then(function() { .then(function() {
this._persisting = null; this._persisting = null;

View File

@ -17,7 +17,7 @@ jest
.mock('os') .mock('os')
.mock('fs'); .mock('fs');
var Promise = require('bluebird'); var Promise = require('promise');
describe('JSTransformer Cache', function() { describe('JSTransformer Cache', function() {
var Cache; var Cache;

View File

@ -9,14 +9,14 @@
'use strict'; 'use strict';
var fs = require('fs'); var fs = require('fs');
var Promise = require('bluebird'); var Promise = require('promise');
var Cache = require('./Cache'); var Cache = require('./Cache');
var workerFarm = require('worker-farm'); var workerFarm = require('worker-farm');
var declareOpts = require('../lib/declareOpts'); var declareOpts = require('../lib/declareOpts');
var util = require('util'); var util = require('util');
var ModuleTransport = require('../lib/ModuleTransport'); var ModuleTransport = require('../lib/ModuleTransport');
var readFile = Promise.promisify(fs.readFile); var readFile = Promise.denodeify(fs.readFile);
module.exports = Transformer; module.exports = Transformer;
Transformer.TransformError = TransformError; Transformer.TransformError = TransformError;
@ -69,7 +69,7 @@ function Transformer(options) {
options.transformModulePath options.transformModulePath
); );
this._transform = Promise.promisify(this._workers); this._transform = Promise.denodeify(this._workers);
} }
} }

View File

@ -17,12 +17,15 @@ jest
jest.mock('fs'); jest.mock('fs');
var Promise = require('bluebird'); var Promise = require('promise');
describe('Packager', function() { describe('Packager', function() {
var getDependencies; var getDependencies;
var wrapModule; var wrapModule;
var Packager; var Packager;
var packager;
var assetServer;
var modules;
beforeEach(function() { beforeEach(function() {
getDependencies = jest.genMockFn(); getDependencies = jest.genMockFn();
@ -35,30 +38,27 @@ describe('Packager', function() {
}); });
Packager = require('../'); Packager = require('../');
});
pit('create a package', function() {
require('fs').statSync.mockImpl(function() { require('fs').statSync.mockImpl(function() {
return { return {
isDirectory: function() {return true;} isDirectory: () => true
}; };
}); });
require('fs').readFile.mockImpl(function(file, callback) { require('fs').readFile.mockImpl(function(file, callback) {
callback(null, '{"json":true}'); callback(null, '{"json":true}');
}); });
var assetServer = { assetServer = {
getAssetData: jest.genMockFn(), getAssetData: jest.genMockFn(),
}; };
var packager = new Packager({ packager = new Packager({
projectRoots: ['/root'], projectRoots: ['/root'],
assetServer: assetServer, assetServer: assetServer,
}); });
var modules = [ modules = [
{id: 'foo', path: '/root/foo.js', dependencies: []}, {id: 'foo', path: '/root/foo.js', dependencies: []},
{id: 'bar', path: '/root/bar.js', dependencies: []}, {id: 'bar', path: '/root/bar.js', dependencies: []},
{ {
@ -101,7 +101,7 @@ describe('Packager', function() {
}); });
wrapModule.mockImpl(function(module, code) { wrapModule.mockImpl(function(module, code) {
return 'lol ' + code + ' lol'; return Promise.resolve('lol ' + code + ' lol');
}); });
require('image-size').mockImpl(function(path, cb) { require('image-size').mockImpl(function(path, cb) {
@ -116,7 +116,9 @@ describe('Packager', function() {
type: 'png', type: 'png',
}; };
}); });
});
pit('create a package', function() {
return packager.package('/root/foo.js', true, 'source_map_url') return packager.package('/root/foo.js', true, 'source_map_url')
.then(function(p) { .then(function(p) {
expect(p.addModule.mock.calls[0][0]).toEqual({ expect(p.addModule.mock.calls[0][0]).toEqual({
@ -200,4 +202,42 @@ describe('Packager', function() {
]); ]);
}); });
}); });
pit('gets the list of dependencies', function() {
return packager.getDependencies('/root/foo.js', true)
.then(({dependencies}) => {
expect(dependencies).toEqual([
{
dependencies: [],
id: 'foo',
path: '/root/foo.js',
},
{
dependencies: [],
id: 'bar',
path: '/root/bar.js',
},
{
dependencies: [],
id: 'image!img',
isAsset_DEPRECATED: true,
path: '/root/img/img.png',
resolution: 2,
},
{
dependencies: [],
id: 'new_image.png',
isAsset: true,
path: '/root/img/new_image.png',
resolution: 2,
},
{
dependencies: [],
id: 'package/file.json',
isJSON: true,
path: '/root/file.json',
},
]);
});
});
}); });

View File

@ -11,7 +11,7 @@
var assert = require('assert'); var assert = require('assert');
var fs = require('fs'); var fs = require('fs');
var path = require('path'); var path = require('path');
var Promise = require('bluebird'); var Promise = require('promise');
var Transformer = require('../JSTransformer'); var Transformer = require('../JSTransformer');
var DependencyResolver = require('../DependencyResolver'); var DependencyResolver = require('../DependencyResolver');
var Package = require('./Package'); var Package = require('./Package');
@ -20,8 +20,8 @@ var ModuleTransport = require('../lib/ModuleTransport');
var declareOpts = require('../lib/declareOpts'); var declareOpts = require('../lib/declareOpts');
var imageSize = require('image-size'); var imageSize = require('image-size');
var sizeOf = Promise.promisify(imageSize); var sizeOf = Promise.denodeify(imageSize);
var readFile = Promise.promisify(fs.readFile); var readFile = Promise.denodeify(fs.readFile);
var validateOpts = declareOpts({ var validateOpts = declareOpts({
projectRoots: { projectRoots: {
@ -159,16 +159,17 @@ Packager.prototype._transformModule = function(ppackage, module) {
} }
var resolver = this._resolver; var resolver = this._resolver;
return transform.then(function(transformed) { return transform.then(
var code = resolver.wrapModule(module, transformed.code); transformed => resolver.wrapModule(module, transformed.code).then(
return new ModuleTransport({ code => new ModuleTransport({
code: code, code: code,
map: transformed.map, map: transformed.map,
sourceCode: transformed.sourceCode, sourceCode: transformed.sourceCode,
sourcePath: transformed.sourcePath, sourcePath: transformed.sourcePath,
virtual: transformed.virtual, virtual: transformed.virtual,
}); })
}); )
);
}; };
Packager.prototype.getGraphDebugInfo = function() { Packager.prototype.getGraphDebugInfo = function() {
@ -206,7 +207,9 @@ Packager.prototype.generateAssetModule = function(ppackage, module) {
return Promise.all([ return Promise.all([
sizeOf(module.path), sizeOf(module.path),
this._assetServer.getAssetData(relPath), this._assetServer.getAssetData(relPath),
]).spread(function(dimensions, assetData) { ]).then(function(res) {
var dimensions = res[0];
var assetData = res[1];
var img = { var img = {
__packager_asset: true, __packager_asset: true,
fileSystemLocation: path.dirname(module.path), fileSystemLocation: path.dirname(module.path),

View File

@ -20,7 +20,7 @@ jest.setMock('worker-farm', function() { return function() {}; })
.setMock('uglify-js') .setMock('uglify-js')
.dontMock('../'); .dontMock('../');
var Promise = require('bluebird'); var Promise = require('promise');
describe('processRequest', function() { describe('processRequest', function() {
var server; var server;

View File

@ -15,7 +15,7 @@ var FileWatcher = require('../FileWatcher');
var Packager = require('../Packager'); var Packager = require('../Packager');
var Activity = require('../Activity'); var Activity = require('../Activity');
var AssetServer = require('../AssetServer'); var AssetServer = require('../AssetServer');
var Promise = require('bluebird'); var Promise = require('promise');
var _ = require('underscore'); var _ = require('underscore');
var exec = require('child_process').exec; var exec = require('child_process').exec;
var fs = require('fs'); var fs = require('fs');
@ -131,13 +131,14 @@ Server.prototype._onFileChange = function(type, filepath, root) {
Server.prototype._rebuildPackages = function() { Server.prototype._rebuildPackages = function() {
var buildPackage = this.buildPackage.bind(this); var buildPackage = this.buildPackage.bind(this);
var packages = this._packages; var packages = this._packages;
Object.keys(packages).forEach(function(key) {
var options = getOptionsFromUrl(key); Object.keys(packages).forEach(function(optionsJson) {
var options = JSON.parse(optionsJson);
// Wait for a previous build (if exists) to finish. // Wait for a previous build (if exists) to finish.
packages[key] = (packages[key] || Promise.resolve()).finally(function() { packages[optionsJson] = (packages[optionsJson] || Promise.resolve()).finally(function() {
// With finally promise callback we can't change the state of the promise // With finally promise callback we can't change the state of the promise
// so we need to reassign the promise. // so we need to reassign the promise.
packages[key] = buildPackage(options).then(function(p) { packages[optionsJson] = buildPackage(options).then(function(p) {
// Make a throwaway call to getSource to cache the source string. // Make a throwaway call to getSource to cache the source string.
p.getSource({ p.getSource({
inlineSourceMap: options.inlineSourceMap, inlineSourceMap: options.inlineSourceMap,
@ -146,7 +147,7 @@ Server.prototype._rebuildPackages = function() {
return p; return p;
}); });
}); });
return packages[key]; return packages[optionsJson];
}); });
}; };
@ -228,15 +229,15 @@ Server.prototype._processDebugRequest = function(reqUrl, res) {
res.end(ret); res.end(ret);
} else if (parts[1] === 'packages') { } else if (parts[1] === 'packages') {
ret += '<h1> Cached Packages </h1>'; ret += '<h1> Cached Packages </h1>';
Promise.all(Object.keys(this._packages).map(function(url) { Promise.all(Object.keys(this._packages).map(function(optionsJson) {
return this._packages[url].then(function(p) { return this._packages[optionsJson].then(function(p) {
ret += '<div><h2>' + url + '</h2>'; ret += '<div><h2>' + optionsJson + '</h2>';
ret += p.getDebugInfo(); ret += p.getDebugInfo();
}); });
}, this)).then( }, this)).then(
function() { res.end(ret); }, function() { res.end(ret); },
function(e) { function(e) {
res.wrteHead(500); res.writeHead(500);
res.end('Internal Error'); res.end('Internal Error');
console.log(e.stack); console.log(e.stack);
} }
@ -350,9 +351,10 @@ Server.prototype.processRequest = function(req, res, next) {
var startReqEventId = Activity.startEvent('request:' + req.url); var startReqEventId = Activity.startEvent('request:' + req.url);
var options = getOptionsFromUrl(req.url); var options = getOptionsFromUrl(req.url);
var building = this._packages[req.url] || this.buildPackage(options); var optionsJson = JSON.stringify(options);
var building = this._packages[optionsJson] || this.buildPackage(options);
this._packages[req.url] = building; this._packages[optionsJson] = building;
building.then( building.then(
function(p) { function(p) {
if (requestType === 'bundle') { if (requestType === 'bundle') {

View File

@ -51,7 +51,7 @@ fs.readFile.mockImpl(function(filepath, encoding, callback) {
var node = getToNode(filepath); var node = getToNode(filepath);
// dir check // dir check
if (node && typeof node === 'object' && node.SYMLINK == null) { if (node && typeof node === 'object' && node.SYMLINK == null) {
callback(new Error('Trying to read a dir, ESIDR, or whatever')); callback(new Error('Error readFile a dir: ' + filepath));
} }
return callback(null, node); return callback(null, node);
} catch (e) { } catch (e) {
@ -59,12 +59,13 @@ fs.readFile.mockImpl(function(filepath, encoding, callback) {
} }
}); });
fs.lstat.mockImpl(function(filepath, callback) { fs.stat.mockImpl(function(filepath, callback) {
var node; var node;
try { try {
node = getToNode(filepath); node = getToNode(filepath);
} catch (e) { } catch (e) {
return callback(e); callback(e);
return;
} }
var mtime = { var mtime = {
@ -73,7 +74,12 @@ fs.lstat.mockImpl(function(filepath, callback) {
} }
}; };
if (node && typeof node === 'object' && node.SYMLINK == null) { if (node.SYMLINK) {
fs.stat(node.SYMLINK, callback);
return;
}
if (node && typeof node === 'object') {
callback(null, { callback(null, {
isDirectory: function() { isDirectory: function() {
return true; return true;
@ -89,9 +95,6 @@ fs.lstat.mockImpl(function(filepath, callback) {
return false; return false;
}, },
isSymbolicLink: function() { isSymbolicLink: function() {
if (typeof node === 'object' && node.SYMLINK) {
return true;
}
return false; return false;
}, },
mtime: mtime, mtime: mtime,
@ -113,6 +116,9 @@ function getToNode(filepath) {
} }
var node = filesystem; var node = filesystem;
parts.slice(1).forEach(function(part) { parts.slice(1).forEach(function(part) {
if (node && node.SYMLINK) {
node = getToNode(node.SYMLINK);
}
node = node[part]; node = node[part];
}); });