From e41008f208382fbf0173c4ab627e7b35e4065fbd Mon Sep 17 00:00:00 2001 From: Ben Alpert Date: Wed, 30 Dec 2015 11:38:44 -0800 Subject: [PATCH] Consume react, fbjs from npm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: We don't (yet) treat these the same as any other modules because we still have special resolution rules for them in the packager allowing the use of `providesModule`, but I believe this allows people to use npm react in their RN projects and not have duplicate copies of React. Fixes facebook/react-native#2985. This relies on fbjs 0.6, which includes `.flow` files alongside the `.js` files to allow them to be typechecked without additional configuration. This also uses react 0.14.5, which shims a couple of files (as `.native.js`) to avoid DOM-specific bits. Once we fix these in React, we will use the same code on web and native. Hopefully we can also remove the packager support I'm adding here for `.native.js`. This diff is not the desired end state for us – ideally the packager would know nothing of react or fbjs, and we'll get there eventually by not relying on `providesModule` in order to load react and fbjs modules. (fbjs change posted here but not merged yet: https://github.com/facebook/fbjs/pull/84.) This should also allow relay to work seamlessly with RN, but I haven't verified this. public Reviewed By: sebmarkbage Differential Revision: D2786197 fb-gh-sync-id: ff50f28445e949edc9501f4b599df7970813870d --- blacklist.js | 77 ++++++++++--------- .../DependencyGraph/HasteMap.js | 33 +++++--- .../DependencyGraph/ResolutionRequest.js | 5 ++ .../DependencyGraph/index.js | 5 +- react-packager/src/Resolver/index.js | 5 +- 5 files changed, 75 insertions(+), 50 deletions(-) diff --git a/blacklist.js b/blacklist.js index 863e2a9a..05973c28 100644 --- a/blacklist.js +++ b/blacklist.js @@ -13,36 +13,41 @@ var path = require('path'); // Don't forget to everything listed here to `testConfig.json` // modulePathIgnorePatterns. var sharedBlacklist = [ - 'node_modules/react-haste/renderers/shared/event/eventPlugins/ResponderEventPlugin.js', - 'node_modules/react-haste/React.js', - 'node_modules/react-haste/renderers/dom/ReactDOM.js', + /node_modules[/\\]react[/\\]dist[/\\].*/, + 'node_modules/react/lib/React.js', + 'node_modules/react/lib/ReactDOM.js', // For each of these fbjs files (especially the non-forks/stubs), we should // consider deleting the conflicting copy and just using the fbjs version. - 'node_modules/fbjs-haste/__forks__/Map.js', - 'node_modules/fbjs-haste/__forks__/Promise.js', - 'node_modules/fbjs-haste/__forks__/fetch.js', - 'node_modules/fbjs-haste/core/Deferred.js', - 'node_modules/fbjs-haste/core/PromiseMap.js', - 'node_modules/fbjs-haste/core/areEqual.js', - 'node_modules/fbjs-haste/core/flattenArray.js', - 'node_modules/fbjs-haste/core/isEmpty.js', - 'node_modules/fbjs-haste/core/removeFromArray.js', - 'node_modules/fbjs-haste/core/resolveImmediate.js', - 'node_modules/fbjs-haste/core/sprintf.js', - 'node_modules/fbjs-haste/crypto/crc32.js', - 'node_modules/fbjs-haste/fetch/fetchWithRetries.js', - 'node_modules/fbjs-haste/functional/everyObject.js', - 'node_modules/fbjs-haste/functional/filterObject.js', - 'node_modules/fbjs-haste/functional/forEachObject.js', - 'node_modules/fbjs-haste/functional/someObject.js', - 'node_modules/fbjs-haste/request/xhrSimpleDataSerializer.js', - 'node_modules/fbjs-haste/stubs/ErrorUtils.js', - 'node_modules/fbjs-haste/stubs/URI.js', - 'node_modules/fbjs-haste/useragent/UserAgent.js', - 'node_modules/fbjs-haste/utils/nullthrows.js', + // + // fbjs forks: + 'node_modules/fbjs/lib/Map.js', + 'node_modules/fbjs/lib/Promise.js', + 'node_modules/fbjs/lib/fetch.js', + // fbjs stubs: + 'node_modules/fbjs/lib/ErrorUtils.js', + 'node_modules/fbjs/lib/URI.js', + // fbjs modules: + 'node_modules/fbjs/lib/Deferred.js', + 'node_modules/fbjs/lib/PromiseMap.js', + 'node_modules/fbjs/lib/UserAgent.js', + 'node_modules/fbjs/lib/areEqual.js', + 'node_modules/fbjs/lib/base62.js', + 'node_modules/fbjs/lib/crc32.js', + 'node_modules/fbjs/lib/everyObject.js', + 'node_modules/fbjs/lib/fetchWithRetries.js', + 'node_modules/fbjs/lib/filterObject.js', + 'node_modules/fbjs/lib/flattenArray.js', + 'node_modules/fbjs/lib/forEachObject.js', + 'node_modules/fbjs/lib/isEmpty.js', + 'node_modules/fbjs/lib/nullthrows.js', + 'node_modules/fbjs/lib/removeFromArray.js', + 'node_modules/fbjs/lib/resolveImmediate.js', + 'node_modules/fbjs/lib/someObject.js', + 'node_modules/fbjs/lib/sprintf.js', + 'node_modules/fbjs/lib/xhrSimpleDataSerializer.js', - // Those conflicts with the ones in fbjs-haste/. We need to blacklist the + // Those conflicts with the ones in fbjs/. We need to blacklist the // internal version otherwise they won't work in open source. 'downstream/core/CSSCore.js', 'downstream/core/TouchEventUtils.js', @@ -63,11 +68,8 @@ var sharedBlacklist = [ 'downstream/core/invariant.js', 'downstream/core/nativeRequestAnimationFrame.js', 'downstream/core/toArray.js', -]; -// Raw unescaped patterns in case you need to use wildcards -var sharedBlacklistWildcards = [ - 'website\/node_modules\/.*', + /website\/node_modules\/.*/, ]; var platformBlacklists = { @@ -85,10 +87,16 @@ var platformBlacklists = { ], }; -function escapeRegExp(str) { - var escaped = str.replace(/[\-\[\]\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'); - // convert the '/' into an escaped local file separator - return escaped.replace(/\//g,'\\' + path.sep); +function escapeRegExp(pattern) { + if (Object.prototype.toString.call(pattern) === '[object RegExp]') { + return pattern.source; + } else if (typeof pattern === 'string') { + var escaped = pattern.replace(/[\-\[\]\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'); + // convert the '/' into an escaped local file separator + return escaped.replace(/\//g,'\\' + path.sep); + } else { + throw new Error('Unexpected packager blacklist pattern: ' + pattern); + } } function blacklist(platform, additionalBlacklist) { @@ -96,7 +104,6 @@ function blacklist(platform, additionalBlacklist) { (additionalBlacklist || []).concat(sharedBlacklist) .concat(platformBlacklists[platform] || []) .map(escapeRegExp) - .concat(sharedBlacklistWildcards) .join('|') + ')$' ); diff --git a/react-packager/src/DependencyResolver/DependencyGraph/HasteMap.js b/react-packager/src/DependencyResolver/DependencyGraph/HasteMap.js index 38d27e3e..2f0b26af 100644 --- a/react-packager/src/DependencyResolver/DependencyGraph/HasteMap.js +++ b/react-packager/src/DependencyResolver/DependencyGraph/HasteMap.js @@ -12,12 +12,20 @@ const getPlatformExtension = require('../lib/getPlatformExtension'); const Promise = require('promise'); const GENERIC_PLATFORM = 'generic'; +const NATIVE_PLATFORM = 'native'; class HasteMap { - constructor({ extensions, fastfs, moduleCache, helpers }) { + constructor({ + extensions, + fastfs, + moduleCache, + preferNativePlatform, + helpers, + }) { this._extensions = extensions; this._fastfs = fastfs; this._moduleCache = moduleCache; + this._preferNativePlatform = preferNativePlatform; this._helpers = helpers; } @@ -73,18 +81,19 @@ class HasteMap { return null; } - // If no platform is given we choose the generic platform module list. - // If a platform is given and no modules exist we fallback - // to the generic platform module list. - if (platform == null) { - return modulesMap[GENERIC_PLATFORM]; - } else { - let module = modulesMap[platform]; - if (module == null) { - module = modulesMap[GENERIC_PLATFORM]; - } - return module; + // If platform is 'ios', we prefer .ios.js to .native.js which we prefer to + // a plain .js file. + let module = undefined; + if (module == null && platform != null) { + module = modulesMap[platform]; } + if (module == null && this._preferNativePlatform) { + module = modulesMap[NATIVE_PLATFORM]; + } + if (module == null) { + module = modulesMap[GENERIC_PLATFORM]; + } + return module; } _processHasteModule(file) { diff --git a/react-packager/src/DependencyResolver/DependencyGraph/ResolutionRequest.js b/react-packager/src/DependencyResolver/DependencyGraph/ResolutionRequest.js index 2125b97f..f896de27 100644 --- a/react-packager/src/DependencyResolver/DependencyGraph/ResolutionRequest.js +++ b/react-packager/src/DependencyResolver/DependencyGraph/ResolutionRequest.js @@ -18,6 +18,7 @@ const Promise = require('promise'); class ResolutionRequest { constructor({ platform, + preferNativePlatform, entryPath, hasteMap, deprecatedAssetMap, @@ -26,6 +27,7 @@ class ResolutionRequest { fastfs, }) { this._platform = platform; + this._preferNativePlatform = preferNativePlatform; this._entryPath = entryPath; this._hasteMap = hasteMap; this._deprecatedAssetMap = deprecatedAssetMap; @@ -329,6 +331,9 @@ class ResolutionRequest { } else if (this._platform != null && this._fastfs.fileExists(potentialModulePath + '.' + this._platform + '.js')) { file = potentialModulePath + '.' + this._platform + '.js'; + } else if (this._preferNativePlatform && + this._fastfs.fileExists(potentialModulePath + '.native.js')) { + file = potentialModulePath + '.native.js'; } else if (this._fastfs.fileExists(potentialModulePath + '.js')) { file = potentialModulePath + '.js'; } else if (this._fastfs.fileExists(potentialModulePath + '.json')) { diff --git a/react-packager/src/DependencyResolver/DependencyGraph/index.js b/react-packager/src/DependencyResolver/DependencyGraph/index.js index ac88a2e3..c4e983ad 100644 --- a/react-packager/src/DependencyResolver/DependencyGraph/index.js +++ b/react-packager/src/DependencyResolver/DependencyGraph/index.js @@ -37,6 +37,7 @@ class DependencyGraph { assetExts, providesModuleNodeModules, platforms, + preferNativePlatform, cache, extensions, mocksPattern, @@ -51,6 +52,7 @@ class DependencyGraph { assetExts: assetExts || [], providesModuleNodeModules, platforms: platforms || [], + preferNativePlatform: preferNativePlatform || false, cache, extensions: extensions || ['js', 'json'], mocksPattern, @@ -105,7 +107,7 @@ class DependencyGraph { fastfs: this._fastfs, extensions: this._opts.extensions, moduleCache: this._moduleCache, - assetExts: this._opts.exts, + preferNativePlatform: this._opts.preferNativePlatform, helpers: this._helpers, }); @@ -147,6 +149,7 @@ class DependencyGraph { const absPath = this._getAbsolutePath(entryPath); const req = new ResolutionRequest({ platform, + preferNativePlatform: this._opts.preferNativePlatform, entryPath: absPath, deprecatedAssetMap: this._deprecatedAssetMap, hasteMap: this._hasteMap, diff --git a/react-packager/src/Resolver/index.js b/react-packager/src/Resolver/index.js index d0f92285..ecc9eeeb 100644 --- a/react-packager/src/Resolver/index.js +++ b/react-packager/src/Resolver/index.js @@ -81,8 +81,8 @@ class Resolver { (opts.blacklistRE && opts.blacklistRE.test(filepath)); }, providesModuleNodeModules: [ - 'fbjs-haste', - 'react-haste', + 'fbjs', + 'react', 'react-native', // Parse requires AsyncStorage. They will // change that to require('react-native') which @@ -92,6 +92,7 @@ class Resolver { 'react-transform-hmr', ], platforms: ['ios', 'android'], + preferNativePlatform: true, fileWatcher: opts.fileWatcher, cache: opts.cache, });