diff --git a/react-packager/src/DependencyResolver/DependencyGraph/__tests__/DependencyGraph-test.js b/react-packager/src/DependencyResolver/DependencyGraph/__tests__/DependencyGraph-test.js index 471e6069..e2f0fd98 100644 --- a/react-packager/src/DependencyResolver/DependencyGraph/__tests__/DependencyGraph-test.js +++ b/react-packager/src/DependencyResolver/DependencyGraph/__tests__/DependencyGraph-test.js @@ -1809,6 +1809,77 @@ describe('DependencyGraph', function() { }); }); + pit('platform should work with node_modules', function() { + var root = '/root'; + fs.__setMockFilesystem({ + 'root': { + 'index.ios.js': [ + '/**', + ' * @providesModule index', + ' */', + 'require("foo");', + 'require("bar");', + ].join('\n'), + 'node_modules': { + 'foo': { + 'package.json': JSON.stringify({ + name: 'foo', + }), + 'index.ios.js': '', + }, + 'bar': { + 'package.json': JSON.stringify({ + name: 'bar', + main: 'main' + }), + 'main.ios.js': '', + }, + }, + } + }); + + var dgraph = new DependencyGraph({ + roots: [root], + fileWatcher: fileWatcher, + assetExts: ['png', 'jpg'], + }); + return dgraph.getOrderedDependencies('/root/index.ios.js').then(function(deps) { + expect(deps) + .toEqual([ + { + id: 'index', + path: '/root/index.ios.js', + dependencies: ['foo', 'bar'], + isAsset: false, + isAsset_DEPRECATED: false, + isJSON: false, + isPolyfill: false, + resolution: undefined, + }, + { + id: 'foo/index.ios.js', + path: '/root/node_modules/foo/index.ios.js', + dependencies: [], + isAsset: false, + isAsset_DEPRECATED: false, + isJSON: false, + isPolyfill: false, + resolution: undefined, + }, + { + id: 'bar/main.ios.js', + path: '/root/node_modules/bar/main.ios.js', + dependencies: [], + isAsset: false, + isAsset_DEPRECATED: false, + isJSON: false, + isPolyfill: false, + resolution: undefined, + }, + ]); + }); + }); + pit('nested node_modules with specific paths', function() { var root = '/root'; fs.__setMockFilesystem({ @@ -2333,6 +2404,169 @@ describe('DependencyGraph', function() { ]); }); }); + + pit('should work with multiple platforms (haste)', function() { + var root = '/root'; + fs.__setMockFilesystem({ + 'root': { + 'index.ios.js': ` + /** + * @providesModule index + */ + require('a'); + `, + 'a.ios.js': ` + /** + * @providesModule a + */ + `, + 'a.android.js': ` + /** + * @providesModule a + */ + `, + 'a.js': ` + /** + * @providesModule a + */ + `, + } + }); + + var dgraph = new DependencyGraph({ + roots: [root], + fileWatcher: fileWatcher, + assetExts: ['png', 'jpg'], + }); + return dgraph.getOrderedDependencies('/root/index.ios.js').then(function(deps) { + expect(deps) + .toEqual([ + { + id: 'index', + path: '/root/index.ios.js', + dependencies: ['a'], + isAsset: false, + isAsset_DEPRECATED: false, + isJSON: false, + isPolyfill: false, + resolution: undefined, + }, + { + id: 'a', + path: '/root/a.ios.js', + dependencies: [], + isAsset: false, + isAsset_DEPRECATED: false, + isJSON: false, + isPolyfill: false, + resolution: undefined, + }, + ]); + }); + }); + + pit('should pick the generic file', function() { + var root = '/root'; + fs.__setMockFilesystem({ + 'root': { + 'index.ios.js': ` + /** + * @providesModule index + */ + require('a'); + `, + 'a.android.js': ` + /** + * @providesModule a + */ + `, + 'a.js': ` + /** + * @providesModule a + */ + `, + } + }); + + var dgraph = new DependencyGraph({ + roots: [root], + fileWatcher: fileWatcher, + assetExts: ['png', 'jpg'], + }); + return dgraph.getOrderedDependencies('/root/index.ios.js').then(function(deps) { + expect(deps) + .toEqual([ + { + id: 'index', + path: '/root/index.ios.js', + dependencies: ['a'], + isAsset: false, + isAsset_DEPRECATED: false, + isJSON: false, + isPolyfill: false, + resolution: undefined, + }, + { + id: 'a', + path: '/root/a.js', + dependencies: [], + isAsset: false, + isAsset_DEPRECATED: false, + isJSON: false, + isPolyfill: false, + resolution: undefined, + }, + ]); + }); + }); + + pit('should work with multiple platforms (node)', function() { + var root = '/root'; + fs.__setMockFilesystem({ + 'root': { + 'index.ios.js': ` + /** + * @providesModule index + */ + require('./a'); + `, + 'a.ios.js': '', + 'a.android.js': '', + 'a.js': '', + } + }); + + var dgraph = new DependencyGraph({ + roots: [root], + fileWatcher: fileWatcher, + assetExts: ['png', 'jpg'], + }); + return dgraph.getOrderedDependencies('/root/index.ios.js').then(function(deps) { + expect(deps) + .toEqual([ + { + id: 'index', + path: '/root/index.ios.js', + dependencies: ['./a'], + isAsset: false, + isAsset_DEPRECATED: false, + isJSON: false, + isPolyfill: false, + resolution: undefined, + }, + { + id: '/root/a.ios.js', + path: '/root/a.ios.js', + dependencies: [], + isAsset: false, + isAsset_DEPRECATED: false, + isJSON: false, + isPolyfill: false, + resolution: undefined, + }, + ]); + }); + }); }); describe('file watch updating', function() { diff --git a/react-packager/src/DependencyResolver/DependencyGraph/index.js b/react-packager/src/DependencyResolver/DependencyGraph/index.js index e05575db..0a8c0076 100644 --- a/react-packager/src/DependencyResolver/DependencyGraph/index.js +++ b/react-packager/src/DependencyResolver/DependencyGraph/index.js @@ -56,6 +56,10 @@ const validateOpts = declareOpts({ 'parse', ], }, + platforms: { + type: 'array', + default: ['ios', 'android'], + } }); class DependencyGraph { @@ -169,6 +173,13 @@ class DependencyGraph { ); } + const platformExt = getPlatformExt(entryPath); + if (platformExt && this._opts.platforms.indexOf(platformExt) > -1) { + this._platformExt = platformExt; + } else { + this._platformExt = null; + } + const entry = this._moduleCache.getModule(absolutePath); const deps = []; const visited = Object.create(null); @@ -237,16 +248,14 @@ class DependencyGraph { } return p.then((realModuleName) => { - let dep = this._hasteMap[realModuleName]; - + let dep = this._getHasteModule(realModuleName); if (dep && dep.type === 'Module') { return dep; } let packageName = realModuleName; - while (packageName && packageName !== '.') { - dep = this._hasteMap[packageName]; + dep = this._getHasteModule(packageName); if (dep && dep.type === 'Package') { break; } @@ -349,6 +358,9 @@ class DependencyGraph { 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')) { @@ -419,15 +431,32 @@ class DependencyGraph { } _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; - } + if (this._hasteMap[name] == null) { + this._hasteMap[name] = []; } - this._hasteMap[name] = mod; + + 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) { @@ -511,12 +540,17 @@ class DependencyGraph { return; } + /*eslint no-labels: 0 */ if (type === 'delete' || type === 'change') { - _.each(this._hasteMap, (mod, name) => { - if (mod.path === absPath) { - delete this._hasteMap[name]; + 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; @@ -566,6 +600,15 @@ function normalizePath(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;