mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e1032e1d09 | ||
|
|
0c2f3fe145 | ||
|
|
329f059ba9 | ||
|
|
2d8ec5eca7 | ||
|
|
6edb671e2d |
+1
-1
@@ -1,6 +1,6 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "React"
|
||||
s.version = "0.8.0"
|
||||
s.version = "0.11.0-rc"
|
||||
s.summary = "Build high quality mobile apps using React."
|
||||
s.description = <<-DESC
|
||||
React Native apps are built using the React JS
|
||||
|
||||
+3
-3
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-native",
|
||||
"version": "0.8.0",
|
||||
"version": "0.11.0",
|
||||
"description": "A framework for building native apps using React",
|
||||
"license": "BSD-3-Clause",
|
||||
"repository": {
|
||||
@@ -69,12 +69,12 @@
|
||||
"sane": "^1.1.2",
|
||||
"semver": "^4.3.6",
|
||||
"source-map": "0.1.31",
|
||||
"stacktrace-parser": "frantic/stacktrace-parser#493c5e5638",
|
||||
"stacktrace-parser": "0.1.3",
|
||||
"uglify-js": "2.4.16",
|
||||
"underscore": "1.7.0",
|
||||
"wordwrap": "^1.0.0",
|
||||
"worker-farm": "^1.3.1",
|
||||
"ws": "^0.7.2",
|
||||
"ws": "0.8.0",
|
||||
"yargs": "1.3.2",
|
||||
"yeoman-environment": "^1.2.7",
|
||||
"yeoman-generator": "^0.20.2"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
jest
|
||||
.dontMock('../../lib/getPlatformExtension')
|
||||
.dontMock('../../lib/getAssetDataFromName')
|
||||
.dontMock('../');
|
||||
|
||||
@@ -47,6 +48,43 @@ describe('AssetServer', () => {
|
||||
);
|
||||
});
|
||||
|
||||
pit('should work for the simple case with platform ext', () => {
|
||||
const server = new AssetServer({
|
||||
projectRoots: ['/root'],
|
||||
assetExts: ['png'],
|
||||
});
|
||||
|
||||
fs.__setMockFilesystem({
|
||||
'root': {
|
||||
imgs: {
|
||||
'b.ios.png': 'b ios image',
|
||||
'b.android.png': 'b android image',
|
||||
'c.png': 'c general image',
|
||||
'c.android.png': 'c android image',
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return Promise.all([
|
||||
server.get('imgs/b.png', 'ios').then(
|
||||
data => expect(data).toBe('b ios image')
|
||||
),
|
||||
server.get('imgs/b.png', 'android').then(
|
||||
data => expect(data).toBe('b android image')
|
||||
),
|
||||
server.get('imgs/c.png', 'android').then(
|
||||
data => expect(data).toBe('c android image')
|
||||
),
|
||||
server.get('imgs/c.png', 'ios').then(
|
||||
data => expect(data).toBe('c general image')
|
||||
),
|
||||
server.get('imgs/c.png').then(
|
||||
data => expect(data).toBe('c general image')
|
||||
),
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
pit('should work for the simple case with jpg', () => {
|
||||
const server = new AssetServer({
|
||||
projectRoots: ['/root'],
|
||||
@@ -95,6 +133,37 @@ describe('AssetServer', () => {
|
||||
);
|
||||
});
|
||||
|
||||
pit('should pick the bigger one with platform ext', () => {
|
||||
const server = new AssetServer({
|
||||
projectRoots: ['/root'],
|
||||
assetExts: ['png'],
|
||||
});
|
||||
|
||||
fs.__setMockFilesystem({
|
||||
'root': {
|
||||
imgs: {
|
||||
'b@1x.png': 'b1 image',
|
||||
'b@2x.png': 'b2 image',
|
||||
'b@4x.png': 'b4 image',
|
||||
'b@4.5x.png': 'b4.5 image',
|
||||
'b@1x.ios.png': 'b1 ios image',
|
||||
'b@2x.ios.png': 'b2 ios image',
|
||||
'b@4x.ios.png': 'b4 ios image',
|
||||
'b@4.5x.ios.png': 'b4.5 ios image',
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return Promise.all([
|
||||
server.get('imgs/b@3x.png').then(data =>
|
||||
expect(data).toBe('b4 image')
|
||||
),
|
||||
server.get('imgs/b@3x.png', 'ios').then(data =>
|
||||
expect(data).toBe('b4 ios image')
|
||||
),
|
||||
]);
|
||||
});
|
||||
|
||||
pit('should support multiple project roots', () => {
|
||||
const server = new AssetServer({
|
||||
projectRoots: ['/root', '/root2'],
|
||||
|
||||
+29
-10
@@ -20,7 +20,6 @@ const stat = Promise.denodeify(fs.stat);
|
||||
const readDir = Promise.denodeify(fs.readdir);
|
||||
const readFile = Promise.denodeify(fs.readFile);
|
||||
|
||||
|
||||
const validateOpts = declareOpts({
|
||||
projectRoots: {
|
||||
type: 'array',
|
||||
@@ -39,9 +38,9 @@ class AssetServer {
|
||||
this._assetExts = opts.assetExts;
|
||||
}
|
||||
|
||||
get(assetPath) {
|
||||
get(assetPath, platform = null) {
|
||||
const assetData = getAssetDataFromName(assetPath);
|
||||
return this._getAssetRecord(assetPath).then(record => {
|
||||
return this._getAssetRecord(assetPath, platform).then(record => {
|
||||
for (let i = 0; i < record.scales.length; i++) {
|
||||
if (record.scales[i] >= assetData.resolution) {
|
||||
return readFile(record.files[i]);
|
||||
@@ -85,9 +84,10 @@ class AssetServer {
|
||||
* 1. We first parse the directory of the asset
|
||||
* 2. We check to find a matching directory in one of the project roots
|
||||
* 3. We then build a map of all assets and their scales in this directory
|
||||
* 4. Then pick the closest resolution (rounding up) to the requested one
|
||||
* 4. Then try to pick platform-specific asset records
|
||||
* 5. Then pick the closest resolution (rounding up) to the requested one
|
||||
*/
|
||||
_getAssetRecord(assetPath) {
|
||||
_getAssetRecord(assetPath, platform = null) {
|
||||
const filename = path.basename(assetPath);
|
||||
|
||||
return (
|
||||
@@ -104,11 +104,20 @@ class AssetServer {
|
||||
const files = res[1];
|
||||
const assetData = getAssetDataFromName(filename);
|
||||
|
||||
const map = this._buildAssetMap(dir, files);
|
||||
const record = map[assetData.assetName];
|
||||
const map = this._buildAssetMap(dir, files, platform);
|
||||
|
||||
let record;
|
||||
if (platform != null){
|
||||
record = map[getAssetKey(assetData.assetName, platform)] ||
|
||||
map[assetData.assetName];
|
||||
} else {
|
||||
record = map[assetData.assetName];
|
||||
}
|
||||
|
||||
if (!record) {
|
||||
throw new Error('Asset not found');
|
||||
throw new Error(
|
||||
`Asset not found: ${assetPath} for platform: ${platform}`
|
||||
);
|
||||
}
|
||||
|
||||
return record;
|
||||
@@ -141,9 +150,10 @@ class AssetServer {
|
||||
const map = Object.create(null);
|
||||
assets.forEach(function(asset, i) {
|
||||
const file = files[i];
|
||||
let record = map[asset.assetName];
|
||||
const assetKey = getAssetKey(asset.assetName, asset.platform);
|
||||
let record = map[assetKey];
|
||||
if (!record) {
|
||||
record = map[asset.assetName] = {
|
||||
record = map[assetKey] = {
|
||||
scales: [],
|
||||
files: [],
|
||||
};
|
||||
@@ -151,6 +161,7 @@ class AssetServer {
|
||||
|
||||
let insertIndex;
|
||||
const length = record.scales.length;
|
||||
|
||||
for (insertIndex = 0; insertIndex < length; insertIndex++) {
|
||||
if (asset.resolution < record.scales[insertIndex]) {
|
||||
break;
|
||||
@@ -164,4 +175,12 @@ class AssetServer {
|
||||
}
|
||||
}
|
||||
|
||||
function getAssetKey(assetName, platform) {
|
||||
if (platform != null) {
|
||||
return `${assetName} : ${platform}`;
|
||||
} else {
|
||||
return assetName;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AssetServer;
|
||||
|
||||
@@ -125,7 +125,7 @@ describe('Bundler', function() {
|
||||
});
|
||||
});
|
||||
|
||||
wrapModule.mockImpl(function(module, code) {
|
||||
wrapModule.mockImpl(function(response, module, code) {
|
||||
return Promise.resolve('lol ' + code + ' lol');
|
||||
});
|
||||
|
||||
|
||||
+16
-7
@@ -125,7 +125,7 @@ class Bundler {
|
||||
const findEventId = Activity.startEvent('find dependencies');
|
||||
let transformEventId;
|
||||
|
||||
return this.getDependencies(main, isDev, platform).then((result) => {
|
||||
return this.getDependencies(main, isDev, platform).then((response) => {
|
||||
Activity.endEvent(findEventId);
|
||||
transformEventId = Activity.startEvent('transform');
|
||||
|
||||
@@ -135,14 +135,19 @@ class Bundler {
|
||||
complete: '=',
|
||||
incomplete: ' ',
|
||||
width: 40,
|
||||
total: result.dependencies.length,
|
||||
total: response.dependencies.length,
|
||||
});
|
||||
}
|
||||
|
||||
bundle.setMainModuleId(result.mainModuleId);
|
||||
bundle.setMainModuleId(response.mainModuleId);
|
||||
return Promise.all(
|
||||
result.dependencies.map(
|
||||
module => this._transformModule(bundle, module).then(transformed => {
|
||||
response.dependencies.map(
|
||||
module => this._transformModule(
|
||||
bundle,
|
||||
response,
|
||||
module,
|
||||
platform
|
||||
).then(transformed => {
|
||||
if (bar) {
|
||||
bar.tick();
|
||||
}
|
||||
@@ -170,7 +175,7 @@ class Bundler {
|
||||
return this._resolver.getDependencies(main, { dev: isDev, platform });
|
||||
}
|
||||
|
||||
_transformModule(bundle, module) {
|
||||
_transformModule(bundle, response, module, platform = null) {
|
||||
let transform;
|
||||
|
||||
if (module.isAsset_DEPRECATED()) {
|
||||
@@ -187,7 +192,11 @@ class Bundler {
|
||||
|
||||
const resolver = this._resolver;
|
||||
return transform.then(
|
||||
transformed => resolver.wrapModule(module, transformed.code).then(
|
||||
transformed => resolver.wrapModule(
|
||||
response,
|
||||
module,
|
||||
transformed.code
|
||||
).then(
|
||||
code => new ModuleTransport({
|
||||
code: code,
|
||||
map: transformed.map,
|
||||
|
||||
+2
-19
@@ -9,25 +9,8 @@
|
||||
'use strict';
|
||||
|
||||
jest
|
||||
.dontMock('absolute-path')
|
||||
.dontMock('crypto')
|
||||
.dontMock('underscore')
|
||||
.dontMock('path')
|
||||
.dontMock('../index')
|
||||
.dontMock('../../lib/getAssetDataFromName')
|
||||
.dontMock('../../DependencyResolver/crawlers')
|
||||
.dontMock('../../DependencyResolver/crawlers/node')
|
||||
.dontMock('../../DependencyResolver/DependencyGraph/docblock')
|
||||
.dontMock('../../DependencyResolver/fastfs')
|
||||
.dontMock('../../DependencyResolver/replacePatterns')
|
||||
.dontMock('../../DependencyResolver')
|
||||
.dontMock('../../DependencyResolver/DependencyGraph')
|
||||
.dontMock('../../DependencyResolver/AssetModule_DEPRECATED')
|
||||
.dontMock('../../DependencyResolver/AssetModule')
|
||||
.dontMock('../../DependencyResolver/Module')
|
||||
.dontMock('../../DependencyResolver/Package')
|
||||
.dontMock('../../DependencyResolver/Polyfill')
|
||||
.dontMock('../../DependencyResolver/ModuleCache');
|
||||
.autoMockOff()
|
||||
.mock('../../Cache');
|
||||
|
||||
const Promise = require('promise');
|
||||
const path = require('path');
|
||||
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* 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 Activity = require('../../Activity');
|
||||
const AssetModule_DEPRECATED = require('../AssetModule_DEPRECATED');
|
||||
const Fastfs = require('../fastfs');
|
||||
const debug = require('debug')('ReactPackager:DependencyGraph');
|
||||
const path = require('path');
|
||||
|
||||
class DeprecatedAssetMap {
|
||||
constructor({ fsCrawl, roots, assetExts, fileWatcher, ignoreFilePath, helpers }) {
|
||||
if (roots == null || roots.length === 0) {
|
||||
this._disabled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
this._helpers = helpers;
|
||||
this._map = Object.create(null);
|
||||
this._assetExts = assetExts;
|
||||
this._fastfs = new Fastfs(
|
||||
'Assets',
|
||||
roots,
|
||||
fileWatcher,
|
||||
{ ignore: ignoreFilePath, crawling: fsCrawl }
|
||||
);
|
||||
|
||||
this._fastfs.on('change', this._processFileChange.bind(this));
|
||||
}
|
||||
|
||||
build() {
|
||||
if (this._disabled) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return this._fastfs.build().then(
|
||||
() => {
|
||||
const processAsset_DEPRECATEDActivity = Activity.startEvent(
|
||||
'Building (deprecated) Asset Map',
|
||||
);
|
||||
|
||||
this._fastfs.findFilesByExts(this._assetExts).forEach(
|
||||
file => this._processAsset(file)
|
||||
);
|
||||
|
||||
Activity.endEvent(processAsset_DEPRECATEDActivity);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
resolve(fromModule, toModuleName) {
|
||||
if (this._disabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const assetMatch = toModuleName.match(/^image!(.+)/);
|
||||
if (assetMatch && assetMatch[1]) {
|
||||
if (!this._map[assetMatch[1]]) {
|
||||
debug('WARINING: Cannot find asset:', assetMatch[1]);
|
||||
return null;
|
||||
}
|
||||
return this._map[assetMatch[1]];
|
||||
}
|
||||
}
|
||||
|
||||
_processAsset(file) {
|
||||
let ext = this._helpers.extname(file);
|
||||
if (this._assetExts.indexOf(ext) !== -1) {
|
||||
let name = assetName(file, ext);
|
||||
if (this._map[name] != null) {
|
||||
debug('Conflcting assets', name);
|
||||
}
|
||||
|
||||
this._map[name] = new AssetModule_DEPRECATED(file);
|
||||
}
|
||||
}
|
||||
|
||||
_processFileChange(type, filePath, root, fstat) {
|
||||
const name = assetName(filePath);
|
||||
if (type === 'change' || type === 'delete') {
|
||||
delete this._map[name];
|
||||
}
|
||||
|
||||
if (type === 'change' || type === 'add') {
|
||||
this._processAsset(path.join(root, filePath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function assetName(file, ext) {
|
||||
return path.basename(file, '.' + ext).replace(/@[\d\.]+x/, '');
|
||||
}
|
||||
|
||||
module.exports = DeprecatedAssetMap;
|
||||
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* 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 getPontentialPlatformExt = require('../../lib/getPlatformExtension');
|
||||
|
||||
class HasteMap {
|
||||
constructor({ fastfs, moduleCache, helpers }) {
|
||||
this._fastfs = fastfs;
|
||||
this._moduleCache = moduleCache;
|
||||
this._helpers = helpers;
|
||||
this._map = Object.create(null);
|
||||
}
|
||||
|
||||
build() {
|
||||
let promises = this._fastfs.findFilesByExt('js', {
|
||||
ignore: (file) => this._helpers.isNodeModulesDir(file)
|
||||
}).map(file => this._processHasteModule(file));
|
||||
|
||||
promises = promises.concat(
|
||||
this._fastfs.findFilesByName('package.json', {
|
||||
ignore: (file) => this._helpers.isNodeModulesDir(file)
|
||||
}).map(file => this._processHastePackage(file))
|
||||
);
|
||||
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
processFileChange(type, absPath) {
|
||||
return Promise.resolve().then(() => {
|
||||
/*eslint no-labels: 0 */
|
||||
if (type === 'delete' || type === 'change') {
|
||||
loop: for (let name in this._map) {
|
||||
let modules = this._map[name];
|
||||
for (var i = 0; i < modules.length; i++) {
|
||||
if (modules[i].path === absPath) {
|
||||
modules.splice(i, 1);
|
||||
break loop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (type === 'delete') {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (this._helpers.extname(absPath) === 'js' ||
|
||||
this._helpers.extname(absPath) === 'json') {
|
||||
if (path.basename(absPath) === 'package.json') {
|
||||
return this._processHastePackage(absPath);
|
||||
} else {
|
||||
return this._processHasteModule(absPath);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getModule(name, platform = null) {
|
||||
if (this._map[name]) {
|
||||
const modules = this._map[name];
|
||||
if (platform != null) {
|
||||
for (let i = 0; i < modules.length; i++) {
|
||||
if (getPontentialPlatformExt(modules[i].path) === platform) {
|
||||
return modules[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return modules[0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
_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._map[name] == null) {
|
||||
this._map[name] = [];
|
||||
}
|
||||
|
||||
if (mod.type === 'Module') {
|
||||
// Modules takes precendence over packages.
|
||||
this._map[name].unshift(mod);
|
||||
} else {
|
||||
this._map[name].push(mod);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = HasteMap;
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* 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');
|
||||
|
||||
class Helpers {
|
||||
constructor({ providesModuleNodeModules, assetExts }) {
|
||||
this._providesModuleNodeModules = providesModuleNodeModules;
|
||||
this._assetExts = assetExts;
|
||||
}
|
||||
|
||||
isNodeModulesDir(file) {
|
||||
let parts = path.normalize(file).split(path.sep);
|
||||
const indexOfNodeModules = parts.lastIndexOf('node_modules');
|
||||
|
||||
if (indexOfNodeModules === -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
parts = parts.slice(indexOfNodeModules + 1);
|
||||
|
||||
const dirs = this._providesModuleNodeModules;
|
||||
|
||||
for (let i = 0; i < dirs.length; i++) {
|
||||
if (parts.indexOf(dirs[i]) > -1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
isAssetFile(file) {
|
||||
return this._assetExts.indexOf(this.extname(file)) !== -1;
|
||||
}
|
||||
|
||||
extname(name) {
|
||||
return path.extname(name).replace(/^\./, '');
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Helpers;
|
||||
+347
@@ -0,0 +1,347 @@
|
||||
/**
|
||||
* 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 debug = require('debug')('ReactPackager:DependencyGraph');
|
||||
const util = require('util');
|
||||
const path = require('path');
|
||||
const isAbsolutePath = require('absolute-path');
|
||||
const getAssetDataFromName = require('../../lib/getAssetDataFromName');
|
||||
|
||||
class ResolutionRequest {
|
||||
constructor({
|
||||
platform,
|
||||
entryPath,
|
||||
hasteMap,
|
||||
deprecatedAssetMap,
|
||||
helpers,
|
||||
moduleCache,
|
||||
fastfs,
|
||||
}) {
|
||||
this._platform = platform;
|
||||
this._entryPath = entryPath;
|
||||
this._hasteMap = hasteMap;
|
||||
this._deprecatedAssetMap = deprecatedAssetMap;
|
||||
this._helpers = helpers;
|
||||
this._moduleCache = moduleCache;
|
||||
this._fastfs = fastfs;
|
||||
this._resetResolutionCache();
|
||||
}
|
||||
|
||||
_tryResolve(action, secondaryAction) {
|
||||
return action().catch((error) => {
|
||||
if (error.type !== 'UnableToResolveError') {
|
||||
throw error;
|
||||
}
|
||||
return secondaryAction();
|
||||
});
|
||||
}
|
||||
|
||||
resolveDependency(fromModule, toModuleName) {
|
||||
const resHash = resolutionHash(fromModule.path, toModuleName);
|
||||
|
||||
if (this._immediateResolutionCache[resHash]) {
|
||||
return Promise.resolve(this._immediateResolutionCache[resHash]);
|
||||
}
|
||||
|
||||
const asset_DEPRECATED = this._deprecatedAssetMap.resolve(
|
||||
fromModule,
|
||||
toModuleName
|
||||
);
|
||||
if (asset_DEPRECATED) {
|
||||
return Promise.resolve(asset_DEPRECATED);
|
||||
}
|
||||
|
||||
const cacheResult = (result) => {
|
||||
this._immediateResolutionCache[resHash] = result;
|
||||
return result;
|
||||
};
|
||||
|
||||
const forgive = (error) => {
|
||||
if (error.type !== 'UnableToResolveError') {
|
||||
throw error;
|
||||
}
|
||||
|
||||
console.warn(
|
||||
'Unable to resolve module %s from %s',
|
||||
toModuleName,
|
||||
fromModule.path
|
||||
);
|
||||
return null;
|
||||
};
|
||||
|
||||
if (!this._helpers.isNodeModulesDir(fromModule.path)
|
||||
&& toModuleName[0] !== '.' &&
|
||||
toModuleName[0] !== '/') {
|
||||
return this._tryResolve(
|
||||
() => this._resolveHasteDependency(fromModule, toModuleName),
|
||||
() => this._resolveNodeDependency(fromModule, toModuleName)
|
||||
).then(
|
||||
cacheResult,
|
||||
forgive,
|
||||
);
|
||||
}
|
||||
|
||||
return this._resolveNodeDependency(fromModule, toModuleName)
|
||||
.then(
|
||||
cacheResult,
|
||||
forgive
|
||||
);
|
||||
}
|
||||
|
||||
getOrderedDependencies(response) {
|
||||
return Promise.resolve().then(() => {
|
||||
const entry = this._moduleCache.getModule(this._entryPath);
|
||||
const visited = Object.create(null);
|
||||
visited[entry.hash()] = true;
|
||||
|
||||
const collect = (mod) => {
|
||||
response.pushDependency(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();
|
||||
|
||||
const filteredPairs = [];
|
||||
|
||||
dependencies.forEach((modDep, i) => {
|
||||
if (modDep == null) {
|
||||
debug(
|
||||
'WARNING: Cannot find required module `%s` from module `%s`',
|
||||
depNames[i],
|
||||
mod.path
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return filteredPairs.push([depNames[i], modDep]);
|
||||
});
|
||||
|
||||
response.setResolvedDependencyPairs(mod, filteredPairs);
|
||||
|
||||
filteredPairs.forEach(([depName, modDep]) => {
|
||||
p = p.then(() => {
|
||||
if (!visited[modDep.hash()]) {
|
||||
visited[modDep.hash()] = true;
|
||||
return collect(modDep);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
});
|
||||
|
||||
return p;
|
||||
});
|
||||
};
|
||||
|
||||
return collect(entry);
|
||||
});
|
||||
}
|
||||
|
||||
getAsyncDependencies(response) {
|
||||
return Promise.resolve().then(() => {
|
||||
const mod = this._moduleCache.getModule(this._entryPath);
|
||||
return mod.getAsyncDependencies().then(bundles =>
|
||||
Promise
|
||||
.all(bundles.map(bundle =>
|
||||
Promise.all(bundle.map(
|
||||
dep => this.resolveDependency(mod, dep)
|
||||
))
|
||||
))
|
||||
.then(bs => bs.map(bundle => bundle.map(dep => dep.path)))
|
||||
);
|
||||
}).then(asyncDependencies => asyncDependencies.forEach(
|
||||
(dependency) => response.pushAsyncDependency(dependency)
|
||||
));
|
||||
}
|
||||
|
||||
_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.getModule(realModuleName, this._platform);
|
||||
if (dep && dep.type === 'Module') {
|
||||
return dep;
|
||||
}
|
||||
|
||||
let packageName = realModuleName;
|
||||
while (packageName && packageName !== '.') {
|
||||
dep = this._hasteMap.getModule(packageName, this._platform);
|
||||
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._tryResolve(
|
||||
() => this._loadAsFile(potentialModulePath),
|
||||
() => this._loadAsDir(potentialModulePath),
|
||||
);
|
||||
}
|
||||
|
||||
throw new UnableToResolveError('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._tryResolve(
|
||||
() => this._loadAsFile(realModuleName),
|
||||
() => 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 UnableToResolveError('Node module not found'));
|
||||
searchQueue.forEach(potentialModulePath => {
|
||||
p = this._tryResolve(
|
||||
() => this._tryResolve(
|
||||
() => p,
|
||||
() => this._loadAsFile(potentialModulePath),
|
||||
),
|
||||
() => this._loadAsDir(potentialModulePath)
|
||||
);
|
||||
});
|
||||
|
||||
return p;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_loadAsFile(potentialModulePath) {
|
||||
return Promise.resolve().then(() => {
|
||||
if (this._helpers.isAssetFile(potentialModulePath)) {
|
||||
const {name, type} = getAssetDataFromName(potentialModulePath);
|
||||
|
||||
let pattern = '^' + name + '(@[\\d\\.]+x)?';
|
||||
if (this._platform != null) {
|
||||
pattern += '(\\.' + this._platform + ')?';
|
||||
}
|
||||
pattern += '\\.' + type;
|
||||
|
||||
// We arbitrarly grab the first one, because scale selection
|
||||
// will happen somewhere
|
||||
const [assetFile] = this._fastfs.matches(
|
||||
path.dirname(potentialModulePath),
|
||||
new RegExp(pattern)
|
||||
);
|
||||
|
||||
if (assetFile) {
|
||||
return this._moduleCache.getAssetModule(assetFile);
|
||||
}
|
||||
}
|
||||
|
||||
let file;
|
||||
if (this._fastfs.fileExists(potentialModulePath)) {
|
||||
file = potentialModulePath;
|
||||
} else if (this._platform != null &&
|
||||
this._fastfs.fileExists(potentialModulePath + '.' + this._platform + '.js')) {
|
||||
file = potentialModulePath + '.' + this._platform + '.js';
|
||||
} else if (this._fastfs.fileExists(potentialModulePath + '.js')) {
|
||||
file = potentialModulePath + '.js';
|
||||
} else if (this._fastfs.fileExists(potentialModulePath + '.json')) {
|
||||
file = potentialModulePath + '.json';
|
||||
} else {
|
||||
throw new UnableToResolveError(`File ${potentialModulePath} doesnt exist`);
|
||||
}
|
||||
|
||||
return this._moduleCache.getModule(file);
|
||||
});
|
||||
}
|
||||
|
||||
_loadAsDir(potentialDirPath) {
|
||||
return Promise.resolve().then(() => {
|
||||
if (!this._fastfs.dirExists(potentialDirPath)) {
|
||||
throw new UnableToResolveError(`Invalid directory ${potentialDirPath}`);
|
||||
}
|
||||
|
||||
const packageJsonPath = path.join(potentialDirPath, 'package.json');
|
||||
if (this._fastfs.fileExists(packageJsonPath)) {
|
||||
return this._moduleCache.getPackage(packageJsonPath)
|
||||
.getMain().then(
|
||||
(main) => this._tryResolve(
|
||||
() => this._loadAsFile(main),
|
||||
() => this._loadAsDir(main)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return this._loadAsFile(path.join(potentialDirPath, 'index'));
|
||||
});
|
||||
}
|
||||
|
||||
_resetResolutionCache() {
|
||||
this._immediateResolutionCache = Object.create(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function resolutionHash(modulePath, depName) {
|
||||
return `${path.resolve(modulePath)}:${depName}`;
|
||||
}
|
||||
|
||||
|
||||
function UnableToResolveError() {
|
||||
Error.call(this);
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
var msg = util.format.apply(util, arguments);
|
||||
this.message = msg;
|
||||
this.type = this.name = 'UnableToResolveError';
|
||||
}
|
||||
|
||||
util.inherits(UnableToResolveError, Error);
|
||||
|
||||
|
||||
function normalizePath(modulePath) {
|
||||
if (path.sep === '/') {
|
||||
modulePath = path.normalize(modulePath);
|
||||
} else if (path.posix) {
|
||||
modulePath = path.posix.normalize(modulePath);
|
||||
}
|
||||
|
||||
return modulePath.replace(/\/$/, '');
|
||||
}
|
||||
|
||||
|
||||
module.exports = ResolutionRequest;
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* 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';
|
||||
|
||||
class ResolutionResponse {
|
||||
constructor() {
|
||||
this.dependencies = [];
|
||||
this.asyncDependencies = [];
|
||||
this.mainModuleId = null;
|
||||
this._mappings = Object.create(null);
|
||||
this._finalized = false;
|
||||
}
|
||||
|
||||
_assertNotFinalized() {
|
||||
if (this._finalized) {
|
||||
throw new Error('Attempted to mutate finalized response.');
|
||||
}
|
||||
}
|
||||
|
||||
_assertFinalized() {
|
||||
if (!this._finalized) {
|
||||
throw new Error('Attempted to access unfinalized response.');
|
||||
}
|
||||
}
|
||||
|
||||
finalize() {
|
||||
return this._mainModule.getName().then(id => {
|
||||
this.mainModuleId = id;
|
||||
this._finalized = true;
|
||||
return this;
|
||||
});
|
||||
}
|
||||
|
||||
pushDependency(module) {
|
||||
this._assertNotFinalized();
|
||||
if (this.dependencies.length === 0) {
|
||||
this._mainModule = module;
|
||||
}
|
||||
|
||||
this.dependencies.push(module);
|
||||
}
|
||||
|
||||
prependDependency(module) {
|
||||
this._assertNotFinalized();
|
||||
this.dependencies.unshift(module);
|
||||
}
|
||||
|
||||
pushAsyncDependency(dependency){
|
||||
this._assertNotFinalized();
|
||||
this.asyncDependencies.push(dependency);
|
||||
}
|
||||
|
||||
setResolvedDependencyPairs(module, pairs) {
|
||||
this._assertNotFinalized();
|
||||
const hash = module.hash();
|
||||
if (this._mappings[hash] == null) {
|
||||
this._mappings[hash] = pairs;
|
||||
}
|
||||
}
|
||||
|
||||
getResolvedDependencyPairs(module) {
|
||||
this._assertFinalized();
|
||||
return this._mappings[module.hash()];
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ResolutionResponse;
|
||||
Vendored
+130
-24
@@ -8,26 +8,13 @@
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
jest
|
||||
.dontMock('../index')
|
||||
.dontMock('crypto')
|
||||
.dontMock('absolute-path')
|
||||
.dontMock('../docblock')
|
||||
.dontMock('../../crawlers')
|
||||
.dontMock('../../crawlers/node')
|
||||
.dontMock('../../replacePatterns')
|
||||
.dontMock('../../../lib/getAssetDataFromName')
|
||||
.dontMock('../../fastfs')
|
||||
.dontMock('../../AssetModule_DEPRECATED')
|
||||
.dontMock('../../AssetModule')
|
||||
.dontMock('../../Module')
|
||||
.dontMock('../../Package')
|
||||
.dontMock('../../ModuleCache')
|
||||
.setMock('chalk', { dim: function(s) { return s; } });
|
||||
jest.autoMockOff();
|
||||
|
||||
const Promise = require('promise');
|
||||
|
||||
jest.mock('fs');
|
||||
jest
|
||||
.mock('fs')
|
||||
.mock('../../../Cache');
|
||||
|
||||
describe('DependencyGraph', function() {
|
||||
var cache;
|
||||
@@ -36,12 +23,13 @@ describe('DependencyGraph', function() {
|
||||
var fileWatcher;
|
||||
var fs;
|
||||
|
||||
function getOrderedDependenciesAsJSON(dgraph, entry) {
|
||||
return dgraph.getOrderedDependencies(entry).then(
|
||||
deps => Promise.all(deps.map(dep => Promise.all([
|
||||
function getOrderedDependenciesAsJSON(dgraph, entry, platform) {
|
||||
return dgraph.getDependencies(entry, platform)
|
||||
.then(response => response.finalize())
|
||||
.then(({ dependencies }) => Promise.all(dependencies.map(dep => Promise.all([
|
||||
dep.getName(),
|
||||
dep.getDependencies(),
|
||||
]).then(([name, dependencies]) => ({
|
||||
]).then(([name, moduleDependencies]) => ({
|
||||
path: dep.path,
|
||||
isJSON: dep.isJSON(),
|
||||
isAsset: dep.isAsset(),
|
||||
@@ -49,7 +37,7 @@ describe('DependencyGraph', function() {
|
||||
isPolyfill: dep.isPolyfill(),
|
||||
resolution: dep.resolution,
|
||||
id: name,
|
||||
dependencies
|
||||
dependencies: moduleDependencies,
|
||||
})))
|
||||
));
|
||||
}
|
||||
@@ -66,10 +54,10 @@ describe('DependencyGraph', function() {
|
||||
isWatchman: () => Promise.resolve(false)
|
||||
};
|
||||
|
||||
cache = new Cache({});
|
||||
cache = new Cache();
|
||||
});
|
||||
|
||||
describe('getOrderedDependencies', function() {
|
||||
describe('get sync dependencies', function() {
|
||||
pit('should get dependencies', function() {
|
||||
var root = '/root';
|
||||
fs.__setMockFilesystem({
|
||||
@@ -424,6 +412,88 @@ describe('DependencyGraph', function() {
|
||||
});
|
||||
});
|
||||
|
||||
pit('should respect platform extension in assets', function() {
|
||||
var root = '/root';
|
||||
fs.__setMockFilesystem({
|
||||
'root': {
|
||||
'index.js': [
|
||||
'/**',
|
||||
' * @providesModule index',
|
||||
' */',
|
||||
'require("./imgs/a.png");',
|
||||
'require("./imgs/b.png");',
|
||||
'require("./imgs/c.png");',
|
||||
].join('\n'),
|
||||
'imgs': {
|
||||
'a@1.5x.ios.png': '',
|
||||
'b@.7x.ios.png': '',
|
||||
'c.ios.png': '',
|
||||
'c@2x.ios.png': '',
|
||||
},
|
||||
'package.json': JSON.stringify({
|
||||
name: 'rootPackage'
|
||||
}),
|
||||
}
|
||||
});
|
||||
|
||||
var dgraph = new DependencyGraph({
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
|
||||
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js', 'ios').then(function(deps) {
|
||||
expect(deps)
|
||||
.toEqual([
|
||||
{
|
||||
id: 'index',
|
||||
path: '/root/index.js',
|
||||
dependencies: [
|
||||
'./imgs/a.png',
|
||||
'./imgs/b.png',
|
||||
'./imgs/c.png',
|
||||
],
|
||||
isAsset: false,
|
||||
isAsset_DEPRECATED: false,
|
||||
isJSON: false,
|
||||
isPolyfill: false,
|
||||
resolution: undefined,
|
||||
},
|
||||
{
|
||||
id: 'rootPackage/imgs/a.png',
|
||||
path: '/root/imgs/a@1.5x.ios.png',
|
||||
resolution: 1.5,
|
||||
dependencies: [],
|
||||
isAsset: true,
|
||||
isAsset_DEPRECATED: false,
|
||||
isJSON: false,
|
||||
isPolyfill: false,
|
||||
},
|
||||
{
|
||||
id: 'rootPackage/imgs/b.png',
|
||||
path: '/root/imgs/b@.7x.ios.png',
|
||||
resolution: 0.7,
|
||||
dependencies: [],
|
||||
isAsset: true,
|
||||
isAsset_DEPRECATED: false,
|
||||
isJSON: false,
|
||||
isPolyfill: false,
|
||||
},
|
||||
{
|
||||
id: 'rootPackage/imgs/c.png',
|
||||
path: '/root/imgs/c.ios.png',
|
||||
resolution: 1,
|
||||
dependencies: [],
|
||||
isAsset: true,
|
||||
isAsset_DEPRECATED: false,
|
||||
isJSON: false,
|
||||
isPolyfill: false,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
pit('Deprecated and relative assets can live together', function() {
|
||||
var root = '/root';
|
||||
fs.__setMockFilesystem({
|
||||
@@ -3615,4 +3685,40 @@ describe('DependencyGraph', function() {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getAsyncDependencies', () => {
|
||||
pit('should get dependencies', function() {
|
||||
var root = '/root';
|
||||
fs.__setMockFilesystem({
|
||||
'root': {
|
||||
'index.js': [
|
||||
'/**',
|
||||
' * @providesModule index',
|
||||
' */',
|
||||
'System.import("a")'
|
||||
].join('\n'),
|
||||
'a.js': [
|
||||
'/**',
|
||||
' * @providesModule a',
|
||||
' */',
|
||||
].join('\n'),
|
||||
}
|
||||
});
|
||||
|
||||
var dgraph = new DependencyGraph({
|
||||
roots: [root],
|
||||
fileWatcher: fileWatcher,
|
||||
assetExts: ['png', 'jpg'],
|
||||
cache: cache,
|
||||
});
|
||||
|
||||
return dgraph.getDependencies('/root/index.js')
|
||||
.then(response => response.finalize())
|
||||
.then(({ asyncDependencies }) => {
|
||||
expect(asyncDependencies).toEqual([
|
||||
['/root/a.js']
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/**
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
@@ -9,17 +9,20 @@
|
||||
'use strict';
|
||||
|
||||
const Activity = require('../../Activity');
|
||||
const AssetModule_DEPRECATED = require('../AssetModule_DEPRECATED');
|
||||
const Fastfs = require('../fastfs');
|
||||
const ModuleCache = require('../ModuleCache');
|
||||
const Promise = require('promise');
|
||||
const crawl = require('../crawlers');
|
||||
const debug = require('debug')('DependencyGraph');
|
||||
const declareOpts = require('../../lib/declareOpts');
|
||||
const getAssetDataFromName = require('../../lib/getAssetDataFromName');
|
||||
const getPontentialPlatformExt = require('../../lib/getPlatformExtension');
|
||||
const isAbsolutePath = require('absolute-path');
|
||||
const path = require('path');
|
||||
const util = require('util');
|
||||
const Helpers = require('./Helpers');
|
||||
const ResolutionRequest = require('./ResolutionRequest');
|
||||
const ResolutionResponse = require('./ResolutionResponse');
|
||||
const HasteMap = require('./HasteMap');
|
||||
const DeprecatedAssetMap = require('./DeprecatedAssetMap');
|
||||
|
||||
const validateOpts = declareOpts({
|
||||
roots: {
|
||||
@@ -68,9 +71,8 @@ const validateOpts = declareOpts({
|
||||
class DependencyGraph {
|
||||
constructor(options) {
|
||||
this._opts = validateOpts(options);
|
||||
this._hasteMap = Object.create(null);
|
||||
this._resetResolutionCache();
|
||||
this._cache = this._opts.cache;
|
||||
this._helpers = new Helpers(this._opts);
|
||||
this.load();
|
||||
}
|
||||
|
||||
@@ -103,13 +105,29 @@ class DependencyGraph {
|
||||
|
||||
this._moduleCache = new ModuleCache(this._fastfs, this._cache);
|
||||
|
||||
this._hasteMap = new HasteMap({
|
||||
fastfs: this._fastfs,
|
||||
moduleCache: this._moduleCache,
|
||||
assetExts: this._opts.exts,
|
||||
helpers: this._helpers,
|
||||
});
|
||||
|
||||
this._deprecatedAssetMap = new DeprecatedAssetMap({
|
||||
fsCrawl: this._crawling,
|
||||
roots: this._opts.assetRoots_DEPRECATED,
|
||||
helpers: this._helpers,
|
||||
fileWatcher: this._opts.fileWatcher,
|
||||
ignoreFilePath: this._opts.ignoreFilePath,
|
||||
assetExts: this._opts.assetExts,
|
||||
});
|
||||
|
||||
this._loading = Promise.all([
|
||||
this._fastfs.build()
|
||||
.then(() => {
|
||||
const hasteActivity = Activity.startEvent('Building Haste Map');
|
||||
return this._buildHasteMap().then(() => Activity.endEvent(hasteActivity));
|
||||
return this._hasteMap.build().then(() => Activity.endEvent(hasteActivity));
|
||||
}),
|
||||
this._buildAssetMap_DEPRECATED(),
|
||||
this._deprecatedAssetMap.build(),
|
||||
]).then(() =>
|
||||
Activity.endEvent(depGraphActivity)
|
||||
);
|
||||
@@ -117,530 +135,73 @@ class DependencyGraph {
|
||||
return this._loading;
|
||||
}
|
||||
|
||||
setup({ platform }) {
|
||||
if (platform && this._opts.platforms.indexOf(platform) === -1) {
|
||||
getDependencies(entryPath, platform) {
|
||||
return this.load().then(() => {
|
||||
platform = this._getRequestPlatform(entryPath, platform);
|
||||
const absPath = this._getAbsolutePath(entryPath);
|
||||
const req = new ResolutionRequest({
|
||||
platform,
|
||||
entryPath: absPath,
|
||||
deprecatedAssetMap: this._deprecatedAssetMap,
|
||||
hasteMap: this._hasteMap,
|
||||
helpers: this._helpers,
|
||||
moduleCache: this._moduleCache,
|
||||
fastfs: this._fastfs,
|
||||
});
|
||||
|
||||
const response = new ResolutionResponse();
|
||||
|
||||
return Promise.all([
|
||||
req.getOrderedDependencies(response),
|
||||
req.getAsyncDependencies(response),
|
||||
]).then(() => response);
|
||||
});
|
||||
}
|
||||
|
||||
_getRequestPlatform(entryPath, platform) {
|
||||
if (platform == null) {
|
||||
platform = getPontentialPlatformExt(entryPath);
|
||||
if (platform == null || this._opts.platforms.indexOf(platform) === -1) {
|
||||
platform = null;
|
||||
}
|
||||
} else if (this._opts.platforms.indexOf(platform) === -1) {
|
||||
throw new Error('Unrecognized platform: ' + platform);
|
||||
}
|
||||
|
||||
// TODO(amasad): This is a potential race condition. Mutliple requests could
|
||||
// interfere with each other. This needs a refactor to fix -- which will
|
||||
// follow this diff.
|
||||
if (this._platformExt !== platform) {
|
||||
this._resetResolutionCache();
|
||||
}
|
||||
this._platformExt = platform;
|
||||
}
|
||||
|
||||
resolveDependency(fromModule, toModuleName) {
|
||||
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 entry = this._getModuleForEntryPath(entryPath);
|
||||
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(() => deps);
|
||||
});
|
||||
}
|
||||
|
||||
getAsyncDependencies(entryPath) {
|
||||
return this.load().then(() => {
|
||||
const mod = this._getModuleForEntryPath(entryPath);
|
||||
return mod.getAsyncDependencies().then(bundles =>
|
||||
Promise
|
||||
.all(bundles.map(bundle =>
|
||||
Promise.all(bundle.map(
|
||||
dep => this.resolveDependency(mod, dep)
|
||||
))
|
||||
))
|
||||
.then(bs => bs.map(bundle => bundle.map(dep => dep.path)))
|
||||
);
|
||||
});
|
||||
return platform;
|
||||
}
|
||||
|
||||
_getAbsolutePath(filePath) {
|
||||
if (isAbsolutePath(filePath)) {
|
||||
return filePath;
|
||||
return path.resolve(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;
|
||||
const potentialAbsPath = path.join(root, filePath);
|
||||
if (this._fastfs.fileExists(potentialAbsPath)) {
|
||||
return path.resolve(potentialAbsPath);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
_getModuleForEntryPath(entryPath) {
|
||||
const absPath = this._getAbsolutePath(entryPath);
|
||||
|
||||
if (absPath == null) {
|
||||
throw new NotFoundError(
|
||||
'Could not find source file at %s',
|
||||
entryPath
|
||||
);
|
||||
}
|
||||
|
||||
const absolutePath = path.resolve(absPath);
|
||||
|
||||
if (absolutePath == null) {
|
||||
throw new NotFoundError(
|
||||
'Cannot find entry file %s in any of the roots: %j',
|
||||
entryPath,
|
||||
this._opts.roots
|
||||
);
|
||||
}
|
||||
|
||||
// `platformExt` could be set in the `setup` method.
|
||||
if (!this._platformExt) {
|
||||
const platformExt = getPlatformExt(entryPath);
|
||||
if (platformExt && this._opts.platforms.indexOf(platformExt) > -1) {
|
||||
this._platformExt = platformExt;
|
||||
} else {
|
||||
this._platformExt = null;
|
||||
}
|
||||
}
|
||||
|
||||
return this._moduleCache.getModule(absolutePath);
|
||||
}
|
||||
|
||||
_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._getHasteModule(realModuleName);
|
||||
if (dep && dep.type === 'Module') {
|
||||
return dep;
|
||||
}
|
||||
|
||||
let packageName = realModuleName;
|
||||
while (packageName && packageName !== '.') {
|
||||
dep = this._getHasteModule(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._platformExt != null &&
|
||||
this._fastfs.fileExists(potentialModulePath + '.' + this._platformExt + '.js')) {
|
||||
file = potentialModulePath + '.' + this._platformExt + '.js';
|
||||
} 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))
|
||||
throw new NotFoundError(
|
||||
'Cannot find entry file %s in any of the roots: %j',
|
||||
filePath,
|
||||
this._opts.roots
|
||||
);
|
||||
|
||||
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] == null) {
|
||||
this._hasteMap[name] = [];
|
||||
}
|
||||
|
||||
if (mod.type === 'Module') {
|
||||
// Modules takes precendence over packages.
|
||||
this._hasteMap[name].unshift(mod);
|
||||
} else {
|
||||
this._hasteMap[name].push(mod);
|
||||
}
|
||||
}
|
||||
|
||||
_getHasteModule(name) {
|
||||
if (this._hasteMap[name]) {
|
||||
const modules = this._hasteMap[name];
|
||||
if (this._platformExt != null) {
|
||||
for (let i = 0; i < modules.length; i++) {
|
||||
if (getPlatformExt(modules[i].path) === this._platformExt) {
|
||||
return modules[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return modules[0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
_isNodeModulesDir(file) {
|
||||
let parts = path.normalize(file).split(path.sep);
|
||||
const indexOfNodeModules = parts.lastIndexOf('node_modules');
|
||||
|
||||
if (indexOfNodeModules === -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
parts = parts.slice(indexOfNodeModules + 1);
|
||||
|
||||
const dirs = this._opts.providesModuleNodeModules;
|
||||
|
||||
for (let i = 0; i < dirs.length; i++) {
|
||||
if (parts.indexOf(dirs[i]) > -1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
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 fastfs = new Fastfs(
|
||||
'Assets',
|
||||
this._opts.assetRoots_DEPRECATED,
|
||||
this._opts.fileWatcher,
|
||||
{ ignore: this._opts.ignoreFilePath, crawling: this._crawling }
|
||||
);
|
||||
|
||||
fastfs.on('change', this._processAssetChange_DEPRECATED.bind(this));
|
||||
|
||||
return fastfs.build().then(
|
||||
() => {
|
||||
const processAsset_DEPRECATEDActivity = Activity.startEvent(
|
||||
'Building (deprecated) Asset Map',
|
||||
);
|
||||
|
||||
const assets = fastfs.findFilesByExts(this._opts.assetExts).map(
|
||||
file => this._processAsset_DEPRECATED(file)
|
||||
);
|
||||
|
||||
Activity.endEvent(processAsset_DEPRECATEDActivity);
|
||||
return assets;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
_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._resetResolutionCache();
|
||||
|
||||
const absPath = path.join(root, filePath);
|
||||
if ((fstat && fstat.isDirectory()) ||
|
||||
if (fstat && fstat.isDirectory() ||
|
||||
this._opts.ignoreFilePath(absPath) ||
|
||||
this._isNodeModulesDir(absPath)) {
|
||||
this._helpers.isNodeModulesDir(absPath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*eslint no-labels: 0 */
|
||||
if (type === 'delete' || type === 'change') {
|
||||
loop: for (let name in this._hasteMap) {
|
||||
let modules = this._hasteMap[name];
|
||||
for (var i = 0; i < modules.length; i++) {
|
||||
if (modules[i].path === absPath) {
|
||||
modules.splice(i, 1);
|
||||
break loop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
this._loading = this._loading.then(
|
||||
() => this._hasteMap.processFileChange(type, absPath)
|
||||
);
|
||||
}
|
||||
|
||||
_resetResolutionCache() {
|
||||
this._immediateResolutionCache = Object.create(null);
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
@@ -651,26 +212,6 @@ function NotFoundError() {
|
||||
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(/\/$/, '');
|
||||
}
|
||||
|
||||
// Extract platform extension: index.ios.js -> ios
|
||||
function getPlatformExt(file) {
|
||||
const parts = path.basename(file).split('.');
|
||||
if (parts.length < 3) {
|
||||
return null;
|
||||
}
|
||||
return parts[parts.length - 2];
|
||||
}
|
||||
|
||||
util.inherits(NotFoundError, Error);
|
||||
|
||||
module.exports = DependencyGraph;
|
||||
|
||||
+48
-25
@@ -34,6 +34,22 @@ describe('HasteDependencyResolver', function() {
|
||||
HasteDependencyResolver = require('../');
|
||||
});
|
||||
|
||||
class ResolutionResponseMock {
|
||||
constructor({dependencies, mainModuleId, asyncDependencies}) {
|
||||
this.dependencies = dependencies;
|
||||
this.mainModuleId = mainModuleId;
|
||||
this.asyncDependencies = asyncDependencies;
|
||||
}
|
||||
|
||||
prependDependency(dependency) {
|
||||
this.dependencies.unshift(dependency);
|
||||
}
|
||||
|
||||
finalize() {
|
||||
return Promise.resolve(this);
|
||||
}
|
||||
}
|
||||
|
||||
function createModule(id, dependencies) {
|
||||
var module = new Module();
|
||||
module.getName.mockImpl(() => Promise.resolve(id));
|
||||
@@ -52,11 +68,12 @@ describe('HasteDependencyResolver', function() {
|
||||
|
||||
// Is there a better way? How can I mock the prototype instead?
|
||||
var depGraph = depResolver._depGraph;
|
||||
depGraph.getOrderedDependencies.mockImpl(function() {
|
||||
return Promise.resolve(deps);
|
||||
});
|
||||
depGraph.load.mockImpl(function() {
|
||||
return Promise.resolve();
|
||||
depGraph.getDependencies.mockImpl(function() {
|
||||
return Promise.resolve(new ResolutionResponseMock({
|
||||
dependencies: deps,
|
||||
mainModuleId: 'index',
|
||||
asyncDependencies: [],
|
||||
}));
|
||||
});
|
||||
|
||||
return depResolver.getDependencies('/root/index.js', { dev: false })
|
||||
@@ -133,19 +150,19 @@ describe('HasteDependencyResolver', function() {
|
||||
projectRoot: '/root',
|
||||
});
|
||||
|
||||
// Is there a better way? How can I mock the prototype instead?
|
||||
var depGraph = depResolver._depGraph;
|
||||
depGraph.getOrderedDependencies.mockImpl(function() {
|
||||
return Promise.resolve(deps);
|
||||
});
|
||||
depGraph.load.mockImpl(function() {
|
||||
return Promise.resolve();
|
||||
depGraph.getDependencies.mockImpl(function() {
|
||||
return Promise.resolve(new ResolutionResponseMock({
|
||||
dependencies: deps,
|
||||
mainModuleId: 'index',
|
||||
asyncDependencies: [],
|
||||
}));
|
||||
});
|
||||
|
||||
return depResolver.getDependencies('/root/index.js', { dev: true })
|
||||
.then(function(result) {
|
||||
expect(result.mainModuleId).toEqual('index');
|
||||
expect(depGraph.getOrderedDependencies).toBeCalledWith('/root/index.js');
|
||||
expect(depGraph.getDependencies).toBeCalledWith('/root/index.js', undefined);
|
||||
expect(result.dependencies[0]).toBe(Polyfill.mock.instances[0]);
|
||||
expect(result.dependencies[result.dependencies.length - 1])
|
||||
.toBe(module);
|
||||
@@ -161,13 +178,13 @@ describe('HasteDependencyResolver', function() {
|
||||
polyfillModuleNames: ['some module'],
|
||||
});
|
||||
|
||||
// Is there a better way? How can I mock the prototype instead?
|
||||
var depGraph = depResolver._depGraph;
|
||||
depGraph.getOrderedDependencies.mockImpl(function() {
|
||||
return Promise.resolve(deps);
|
||||
});
|
||||
depGraph.load.mockImpl(function() {
|
||||
return Promise.resolve();
|
||||
depGraph.getDependencies.mockImpl(function() {
|
||||
return Promise.resolve(new ResolutionResponseMock({
|
||||
dependencies: deps,
|
||||
mainModuleId: 'index',
|
||||
asyncDependencies: [],
|
||||
}));
|
||||
});
|
||||
|
||||
return depResolver.getDependencies('/root/index.js', { dev: false })
|
||||
@@ -343,17 +360,23 @@ describe('HasteDependencyResolver', function() {
|
||||
].join('\n');
|
||||
/*eslint-disable */
|
||||
|
||||
depGraph.resolveDependency.mockImpl(function(fromModule, toModuleName) {
|
||||
if (toModuleName === 'x') {
|
||||
return Promise.resolve(createModule('changed'));
|
||||
} else if (toModuleName === 'y') {
|
||||
return Promise.resolve(createModule('Y'));
|
||||
}
|
||||
const module = createModule('test module', ['x', 'y']);
|
||||
|
||||
return Promise.resolve(null);
|
||||
const resolutionResponse = new ResolutionResponseMock({
|
||||
dependencies: [module],
|
||||
mainModuleId: 'test module',
|
||||
asyncDependencies: [],
|
||||
});
|
||||
|
||||
resolutionResponse.getResolvedDependencyPairs = (module) => {
|
||||
return [
|
||||
['x', createModule('changed')],
|
||||
['y', createModule('Y')],
|
||||
];
|
||||
}
|
||||
|
||||
return depResolver.wrapModule(
|
||||
resolutionResponse,
|
||||
createModule('test module', ['x', 'y']),
|
||||
code
|
||||
).then(processedCode => {
|
||||
|
||||
+30
-4
@@ -13,6 +13,8 @@ const stat = Promise.denodeify(fs.stat);
|
||||
|
||||
const hasOwn = Object.prototype.hasOwnProperty;
|
||||
|
||||
const NOT_FOUND_IN_ROOTS = 'NotFoundInRootsError';
|
||||
|
||||
class Fastfs extends EventEmitter {
|
||||
constructor(name, roots, fileWatcher, {ignore, crawling}) {
|
||||
super();
|
||||
@@ -90,7 +92,11 @@ class Fastfs extends EventEmitter {
|
||||
}
|
||||
|
||||
readFile(filePath) {
|
||||
return this._getFile(filePath).read();
|
||||
const file = this._getFile(filePath);
|
||||
if (!file) {
|
||||
throw new Error(`Unable to find file with path: ${file}`);
|
||||
}
|
||||
return file.read();
|
||||
}
|
||||
|
||||
closest(filePath, name) {
|
||||
@@ -105,12 +111,30 @@ class Fastfs extends EventEmitter {
|
||||
}
|
||||
|
||||
fileExists(filePath) {
|
||||
const file = this._getFile(filePath);
|
||||
let file;
|
||||
try {
|
||||
file = this._getFile(filePath);
|
||||
} catch (e) {
|
||||
if (e.type === NOT_FOUND_IN_ROOTS) {
|
||||
return false;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
return file && !file.isDir;
|
||||
}
|
||||
|
||||
dirExists(filePath) {
|
||||
const file = this._getFile(filePath);
|
||||
let file;
|
||||
try {
|
||||
file = this._getFile(filePath);
|
||||
} catch (e) {
|
||||
if (e.type === NOT_FOUND_IN_ROOTS) {
|
||||
return false;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
|
||||
return file && file.isDir;
|
||||
}
|
||||
|
||||
@@ -138,7 +162,9 @@ class Fastfs extends EventEmitter {
|
||||
_getAndAssertRoot(filePath) {
|
||||
const root = this._getRoot(filePath);
|
||||
if (!root) {
|
||||
throw new Error(`File ${filePath} not found in any of the roots`);
|
||||
const error = new Error(`File ${filePath} not found in any of the roots`);
|
||||
error.type = NOT_FOUND_IN_ROOTS;
|
||||
throw error;
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
+44
-65
@@ -82,36 +82,18 @@ var getDependenciesValidateOpts = declareOpts({
|
||||
HasteDependencyResolver.prototype.getDependencies = function(main, options) {
|
||||
var opts = getDependenciesValidateOpts(options);
|
||||
|
||||
var depGraph = this._depGraph;
|
||||
var self = this;
|
||||
return this._depGraph.getDependencies(main, opts.platform).then(
|
||||
resolutionResponse => {
|
||||
this._getPolyfillDependencies(opts.dev).reverse().forEach(
|
||||
polyfill => resolutionResponse.prependDependency(polyfill)
|
||||
);
|
||||
|
||||
depGraph.setup({ platform: opts.platform });
|
||||
|
||||
return Promise.all([
|
||||
depGraph.getOrderedDependencies(main),
|
||||
depGraph.getAsyncDependencies(main),
|
||||
]).then(
|
||||
([dependencies, asyncDependencies]) => dependencies[0].getName().then(
|
||||
mainModuleId => {
|
||||
self._prependPolyfillDependencies(
|
||||
dependencies,
|
||||
opts.dev,
|
||||
);
|
||||
|
||||
return {
|
||||
mainModuleId,
|
||||
dependencies,
|
||||
asyncDependencies,
|
||||
};
|
||||
}
|
||||
)
|
||||
return resolutionResponse.finalize();
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
HasteDependencyResolver.prototype._prependPolyfillDependencies = function(
|
||||
dependencies,
|
||||
isDev
|
||||
) {
|
||||
HasteDependencyResolver.prototype._getPolyfillDependencies = function(isDev) {
|
||||
var polyfillModuleNames = [
|
||||
isDev
|
||||
? path.join(__dirname, 'polyfills/prelude_dev.js')
|
||||
@@ -124,7 +106,7 @@ HasteDependencyResolver.prototype._prependPolyfillDependencies = function(
|
||||
path.join(__dirname, 'polyfills/Array.prototype.es6.js'),
|
||||
].concat(this._polyfillModuleNames);
|
||||
|
||||
var polyfillModules = polyfillModuleNames.map(
|
||||
return polyfillModuleNames.map(
|
||||
(polyfillModuleName, idx) => new Polyfill({
|
||||
path: polyfillModuleName,
|
||||
id: polyfillModuleName,
|
||||
@@ -132,50 +114,47 @@ HasteDependencyResolver.prototype._prependPolyfillDependencies = function(
|
||||
isPolyfill: true,
|
||||
})
|
||||
);
|
||||
|
||||
dependencies.unshift.apply(dependencies, polyfillModules);
|
||||
};
|
||||
|
||||
HasteDependencyResolver.prototype.wrapModule = function(module, code) {
|
||||
if (module.isPolyfill()) {
|
||||
return Promise.resolve(code);
|
||||
}
|
||||
HasteDependencyResolver.prototype.wrapModule = function(resolutionResponse, module, code) {
|
||||
return Promise.resolve().then(() => {
|
||||
if (module.isPolyfill()) {
|
||||
return Promise.resolve(code);
|
||||
}
|
||||
|
||||
const resolvedDeps = Object.create(null);
|
||||
const resolvedDepsArr = [];
|
||||
const resolvedDeps = Object.create(null);
|
||||
const resolvedDepsArr = [];
|
||||
|
||||
return module.getDependencies().then(
|
||||
dependencies => Promise.all(dependencies.map(
|
||||
depName => this._depGraph.resolveDependency(module, depName)
|
||||
.then(depModule => {
|
||||
if (depModule) {
|
||||
return depModule.getName().then(name => {
|
||||
resolvedDeps[depName] = name;
|
||||
resolvedDepsArr.push(name);
|
||||
});
|
||||
}
|
||||
})
|
||||
return Promise.all(
|
||||
resolutionResponse.getResolvedDependencyPairs(module).map(
|
||||
([depName, depModule]) => {
|
||||
if (depModule) {
|
||||
return depModule.getName().then(name => {
|
||||
resolvedDeps[depName] = name;
|
||||
resolvedDepsArr.push(name);
|
||||
});
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
).then(() => {
|
||||
const relativizeCode = (codeMatch, pre, quot, depName, post) => {
|
||||
const depId = resolvedDeps[depName];
|
||||
if (depId) {
|
||||
return pre + quot + depId + post;
|
||||
} else {
|
||||
return codeMatch;
|
||||
}
|
||||
};
|
||||
).then(() => {
|
||||
const relativizeCode = (codeMatch, pre, quot, depName, post) => {
|
||||
const depId = resolvedDeps[depName];
|
||||
if (depId) {
|
||||
return pre + quot + depId + post;
|
||||
} else {
|
||||
return codeMatch;
|
||||
}
|
||||
};
|
||||
|
||||
return module.getName().then(
|
||||
name => defineModuleCode({
|
||||
code: code
|
||||
.replace(replacePatterns.IMPORT_RE, relativizeCode)
|
||||
.replace(replacePatterns.REQUIRE_RE, relativizeCode),
|
||||
deps: JSON.stringify(resolvedDepsArr),
|
||||
moduleName: name,
|
||||
})
|
||||
);
|
||||
return module.getName().then(
|
||||
name => defineModuleCode({
|
||||
code: code.replace(replacePatterns.IMPORT_RE, relativizeCode)
|
||||
.replace(replacePatterns.REQUIRE_RE, relativizeCode),
|
||||
deps: JSON.stringify(resolvedDepsArr),
|
||||
moduleName: name,
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -245,8 +245,16 @@ describe('processRequest', () => {
|
||||
expect(res.end).toBeCalledWith('i am image');
|
||||
});
|
||||
|
||||
it('should return 404', () => {
|
||||
it('should parse the platform option', () => {
|
||||
const req = {url: '/assets/imgs/a.png?platform=ios'};
|
||||
const res = {end: jest.genMockFn()};
|
||||
|
||||
AssetServer.prototype.get.mockImpl(() => Promise.resolve('i am image'));
|
||||
|
||||
server.processRequest(req, res);
|
||||
jest.runAllTimers();
|
||||
expect(AssetServer.prototype.get).toBeCalledWith('imgs/a.png', 'ios');
|
||||
expect(res.end).toBeCalledWith('i am image');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+3
-2
@@ -278,7 +278,8 @@ class Server {
|
||||
_processAssetsRequest(req, res) {
|
||||
const urlObj = url.parse(req.url, true);
|
||||
const assetPath = urlObj.pathname.match(/^\/assets\/(.+)$/);
|
||||
this._assetServer.get(assetPath[1])
|
||||
const assetEvent = Activity.startEvent(`processing asset request ${assetPath[1]}`);
|
||||
this._assetServer.get(assetPath[1], urlObj.query.platform)
|
||||
.then(
|
||||
data => res.end(data),
|
||||
error => {
|
||||
@@ -286,7 +287,7 @@ class Server {
|
||||
res.writeHead('404');
|
||||
res.end('Asset not found');
|
||||
}
|
||||
).done();
|
||||
).done(() => Activity.endEvent(assetEvent));
|
||||
}
|
||||
|
||||
_processProfile(req, res) {
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
jest.autoMockOff();
|
||||
var getAssetDataFromName = require('../getAssetDataFromName');
|
||||
|
||||
describe('getAssetDataFromName', function() {
|
||||
it('should extract resolution simple case', function() {
|
||||
var data = getAssetDataFromName('test@2x.png');
|
||||
expect(data).toEqual({
|
||||
assetName: 'test.png',
|
||||
resolution: 2,
|
||||
type: 'png',
|
||||
name: 'test',
|
||||
});
|
||||
});
|
||||
|
||||
it('should default resolution to 1', function() {
|
||||
var data = getAssetDataFromName('test.png');
|
||||
expect(data).toEqual({
|
||||
assetName: 'test.png',
|
||||
resolution: 1,
|
||||
type: 'png',
|
||||
name: 'test',
|
||||
});
|
||||
});
|
||||
|
||||
it('should support float', function() {
|
||||
var data = getAssetDataFromName('test@1.1x.png');
|
||||
expect(data).toEqual({
|
||||
assetName: 'test.png',
|
||||
resolution: 1.1,
|
||||
type: 'png',
|
||||
name: 'test',
|
||||
});
|
||||
|
||||
data = getAssetDataFromName('test@.1x.png');
|
||||
expect(data).toEqual({
|
||||
assetName: 'test.png',
|
||||
resolution: 0.1,
|
||||
type: 'png',
|
||||
name: 'test',
|
||||
});
|
||||
|
||||
data = getAssetDataFromName('test@0.2x.png');
|
||||
expect(data).toEqual({
|
||||
assetName: 'test.png',
|
||||
resolution: 0.2,
|
||||
type: 'png',
|
||||
name: 'test',
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,123 @@
|
||||
/**
|
||||
* 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';
|
||||
|
||||
jest.dontMock('../getPlatformExtension')
|
||||
.dontMock('../getAssetDataFromName');
|
||||
|
||||
describe('getAssetDataFromName', () => {
|
||||
let getAssetDataFromName;
|
||||
|
||||
beforeEach(() => {
|
||||
getAssetDataFromName = require('../getAssetDataFromName');
|
||||
});
|
||||
|
||||
it('should get data from name', () => {
|
||||
expect(getAssetDataFromName('a/b/c.png')).toEqual({
|
||||
resolution: 1,
|
||||
assetName: 'a/b/c.png',
|
||||
type: 'png',
|
||||
name: 'c',
|
||||
platform: null,
|
||||
});
|
||||
|
||||
expect(getAssetDataFromName('a/b/c@1x.png')).toEqual({
|
||||
resolution: 1,
|
||||
assetName: 'a/b/c.png',
|
||||
type: 'png',
|
||||
name: 'c',
|
||||
platform: null,
|
||||
});
|
||||
|
||||
expect(getAssetDataFromName('a/b/c@2.5x.png')).toEqual({
|
||||
resolution: 2.5,
|
||||
assetName: 'a/b/c.png',
|
||||
type: 'png',
|
||||
name: 'c',
|
||||
platform: null,
|
||||
});
|
||||
|
||||
expect(getAssetDataFromName('a/b/c.ios.png')).toEqual({
|
||||
resolution: 1,
|
||||
assetName: 'a/b/c.png',
|
||||
type: 'png',
|
||||
name: 'c',
|
||||
platform: 'ios',
|
||||
});
|
||||
|
||||
expect(getAssetDataFromName('a/b/c@1x.ios.png')).toEqual({
|
||||
resolution: 1,
|
||||
assetName: 'a/b/c.png',
|
||||
type: 'png',
|
||||
name: 'c',
|
||||
platform: 'ios',
|
||||
});
|
||||
|
||||
expect(getAssetDataFromName('a/b/c@2.5x.ios.png')).toEqual({
|
||||
resolution: 2.5,
|
||||
assetName: 'a/b/c.png',
|
||||
type: 'png',
|
||||
name: 'c',
|
||||
platform: 'ios',
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolution extraction', () => {
|
||||
it('should extract resolution simple case', () => {
|
||||
var data = getAssetDataFromName('test@2x.png');
|
||||
expect(data).toEqual({
|
||||
assetName: 'test.png',
|
||||
resolution: 2,
|
||||
type: 'png',
|
||||
name: 'test',
|
||||
platform: null,
|
||||
});
|
||||
});
|
||||
|
||||
it('should default resolution to 1', () => {
|
||||
var data = getAssetDataFromName('test.png');
|
||||
expect(data).toEqual({
|
||||
assetName: 'test.png',
|
||||
resolution: 1,
|
||||
type: 'png',
|
||||
name: 'test',
|
||||
platform: null,
|
||||
});
|
||||
});
|
||||
|
||||
it('should support float', () => {
|
||||
var data = getAssetDataFromName('test@1.1x.png');
|
||||
expect(data).toEqual({
|
||||
assetName: 'test.png',
|
||||
resolution: 1.1,
|
||||
type: 'png',
|
||||
name: 'test',
|
||||
platform: null,
|
||||
});
|
||||
|
||||
data = getAssetDataFromName('test@.1x.png');
|
||||
expect(data).toEqual({
|
||||
assetName: 'test.png',
|
||||
resolution: 0.1,
|
||||
type: 'png',
|
||||
name: 'test',
|
||||
platform: null,
|
||||
});
|
||||
|
||||
data = getAssetDataFromName('test@0.2x.png');
|
||||
expect(data).toEqual({
|
||||
assetName: 'test.png',
|
||||
resolution: 0.2,
|
||||
type: 'png',
|
||||
name: 'test',
|
||||
platform: null,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
jest.dontMock('../getPlatformExtension');
|
||||
|
||||
describe('getPlatformExtension', function() {
|
||||
it('should get platform ext', function() {
|
||||
var getPlatformExtension = require('../getPlatformExtension');
|
||||
expect(getPlatformExtension('a.ios.js')).toBe('ios');
|
||||
expect(getPlatformExtension('a.android.js')).toBe('android');
|
||||
expect(getPlatformExtension('/b/c/a.ios.js')).toBe('ios');
|
||||
expect(getPlatformExtension('/b/c.android/a.ios.js')).toBe('ios');
|
||||
expect(getPlatformExtension('/b/c/a@1.5x.ios.png')).toBe('ios');
|
||||
expect(getPlatformExtension('/b/c/a@1.5x.lol.png')).toBe(null);
|
||||
expect(getPlatformExtension('/b/c/a.lol.png')).toBe(null);
|
||||
});
|
||||
});
|
||||
+31
-7
@@ -1,14 +1,29 @@
|
||||
/**
|
||||
* 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');
|
||||
const path = require('path');
|
||||
const getPlatformExtension = require('./getPlatformExtension');
|
||||
|
||||
function getAssetDataFromName(filename) {
|
||||
var ext = path.extname(filename);
|
||||
const ext = path.extname(filename);
|
||||
const platformExt = getPlatformExtension(filename);
|
||||
|
||||
var re = new RegExp('@([\\d\\.]+)x\\' + ext + '$');
|
||||
let pattern = '@([\\d\\.]+)x';
|
||||
if (platformExt != null) {
|
||||
pattern += '(\\.' + platformExt + ')?';
|
||||
}
|
||||
pattern += '\\' + ext + '$';
|
||||
const re = new RegExp(pattern);
|
||||
|
||||
var match = filename.match(re);
|
||||
var resolution;
|
||||
const match = filename.match(re);
|
||||
let resolution;
|
||||
|
||||
if (!(match && match[1])) {
|
||||
resolution = 1;
|
||||
@@ -19,12 +34,21 @@ function getAssetDataFromName(filename) {
|
||||
}
|
||||
}
|
||||
|
||||
var assetName = match ? filename.replace(re, ext) : filename;
|
||||
let assetName;
|
||||
if (match) {
|
||||
assetName = filename.replace(re, ext);
|
||||
} else if (platformExt != null) {
|
||||
assetName = filename.replace(new RegExp(`\\.${platformExt}\\${ext}`), ext);
|
||||
} else {
|
||||
assetName = filename;
|
||||
}
|
||||
|
||||
return {
|
||||
resolution: resolution,
|
||||
assetName: assetName,
|
||||
type: ext.slice(1),
|
||||
name: path.basename(assetName, ext)
|
||||
name: path.basename(assetName, ext),
|
||||
platform: platformExt,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* 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 SUPPORTED_PLATFORM_EXTS = ['android', 'ios'];
|
||||
|
||||
const re = new RegExp(
|
||||
'[^\\.]+\\.(' + SUPPORTED_PLATFORM_EXTS.join('|') + ')\\.\\w+$'
|
||||
);
|
||||
|
||||
// Extract platform extension: index.ios.js -> ios
|
||||
function getPlatformExtension(file) {
|
||||
const match = file.match(re);
|
||||
if (match && match[1]) {
|
||||
return match[1];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
module.exports = getPlatformExtension;
|
||||
Reference in New Issue
Block a user