diff --git a/packager/react-packager/src/DependencyResolver/DependencyGraph/__tests__/DependencyGraph-test.js b/packager/react-packager/src/DependencyResolver/DependencyGraph/__tests__/DependencyGraph-test.js index b8caec76a..a89ce97aa 100644 --- a/packager/react-packager/src/DependencyResolver/DependencyGraph/__tests__/DependencyGraph-test.js +++ b/packager/react-packager/src/DependencyResolver/DependencyGraph/__tests__/DependencyGraph-test.js @@ -1362,170 +1362,491 @@ describe('DependencyGraph', function() { }); }); - pit('should support simple browser field in packages', function() { - var root = '/root'; - fs.__setMockFilesystem({ - 'root': { - 'index.js': [ - '/**', - ' * @providesModule index', - ' */', - 'require("aPackage")', - ].join('\n'), - 'aPackage': { - 'package.json': JSON.stringify({ - name: 'aPackage', - main: 'main.js', - browser: 'client.js', - }), - 'main.js': 'some other code', - 'client.js': 'some code', - }, - }, - }); + testBrowserField('browser') + testBrowserField('react-native') - var dgraph = new DependencyGraph({ - ...defaults, - roots: [root], - }); - return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) { - expect(deps) - .toEqual([ - { - id: 'index', - path: '/root/index.js', - dependencies: ['aPackage'], - isAsset: false, - isAsset_DEPRECATED: false, - isJSON: false, - isPolyfill: false, - resolution: undefined, - resolveDependency: undefined, - }, - { - id: 'aPackage/client.js', - path: '/root/aPackage/client.js', - dependencies: [], - isAsset: false, - isAsset_DEPRECATED: false, - isJSON: false, - isPolyfill: false, - resolution: undefined, - resolveDependency: undefined, - }, - ]); - }); - }); + function replaceBrowserField (json, fieldName) { + if (fieldName !== 'browser') { + json[fieldName] = json.browser + delete json.browser + } - pit('should support browser field in packages w/o .js ext', function() { - var root = '/root'; - fs.__setMockFilesystem({ - 'root': { - 'index.js': [ - '/**', - ' * @providesModule index', - ' */', - 'require("aPackage")', - ].join('\n'), - 'aPackage': { - 'package.json': JSON.stringify({ - name: 'aPackage', - main: 'main.js', - browser: 'client', - }), - 'main.js': 'some other code', - 'client.js': 'some code', - }, - }, - }); + return json + } - var dgraph = new DependencyGraph({ - ...defaults, - roots: [root], - }); - return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) { - expect(deps) - .toEqual([ - { - id: 'index', - path: '/root/index.js', - dependencies: ['aPackage'], - isAsset: false, - isAsset_DEPRECATED: false, - isJSON: false, - isPolyfill: false, - resolution: undefined, - }, - { - id: 'aPackage/client.js', - path: '/root/aPackage/client.js', - dependencies: [], - isAsset: false, - isAsset_DEPRECATED: false, - isJSON: false, - isPolyfill: false, - resolution: undefined, - }, - ]); - }); - }); + function testBrowserField (fieldName) { + pit('should support simple browser field in packages ("' + fieldName + '")', function() { + var root = '/root'; + fs.__setMockFilesystem({ + 'root': { + 'index.js': [ + '/**', + ' * @providesModule index', + ' */', + 'require("aPackage")', + ].join('\n'), + 'aPackage': { + 'package.json': JSON.stringify(replaceBrowserField({ + name: 'aPackage', + main: 'main.js', + browser: 'client.js', + }, fieldName)), + 'main.js': 'some other code', + 'client.js': 'some code', + } + } + }); - pit('should support mapping main in browser field json', function() { - var root = '/root'; - fs.__setMockFilesystem({ - 'root': { - 'index.js': [ - '/**', - ' * @providesModule index', - ' */', - 'require("aPackage")', - ].join('\n'), - 'aPackage': { - 'package.json': JSON.stringify({ - name: 'aPackage', - main: './main.js', - browser: { - './main.js': './client.js', + var dgraph = new DependencyGraph({ + ...defaults, + roots: [root], + }); + return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) { + expect(deps) + .toEqual([ + { + id: 'index', + path: '/root/index.js', + dependencies: ['aPackage'], + isAsset: false, + isAsset_DEPRECATED: false, + isJSON: false, + isPolyfill: false, + resolution: undefined, + resolveDependency: undefined, }, - }), - 'main.js': 'some other code', - 'client.js': 'some code', - }, - }, + { + id: 'aPackage/client.js', + path: '/root/aPackage/client.js', + dependencies: [], + isAsset: false, + isAsset_DEPRECATED: false, + isJSON: false, + isPolyfill: false, + resolution: undefined, + resolveDependency: undefined, + }, + ]); + }); }); + pit('should support browser field in packages w/o .js ext ("' + fieldName + '")', function() { + var root = '/root'; + fs.__setMockFilesystem({ + 'root': { + 'index.js': [ + '/**', + ' * @providesModule index', + ' */', + 'require("aPackage")', + ].join('\n'), + 'aPackage': { + 'package.json': JSON.stringify(replaceBrowserField({ + name: 'aPackage', + main: 'main.js', + browser: 'client', + }, fieldName)), + 'main.js': 'some other code', + 'client.js': 'some code', + } + } + }); + + var dgraph = new DependencyGraph({ + ...defaults, + roots: [root], + }); + return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) { + expect(deps) + .toEqual([ + { + id: 'index', + path: '/root/index.js', + dependencies: ['aPackage'], + isAsset: false, + isAsset_DEPRECATED: false, + isJSON: false, + isPolyfill: false, + resolution: undefined, + }, + { + id: 'aPackage/client.js', + path: '/root/aPackage/client.js', + dependencies: [], + isAsset: false, + isAsset_DEPRECATED: false, + isJSON: false, + isPolyfill: false, + resolution: undefined, + }, + ]); + }); + }); + + pit('should support mapping main in browser field json ("' + fieldName + '")', function() { + var root = '/root'; + fs.__setMockFilesystem({ + 'root': { + 'index.js': [ + '/**', + ' * @providesModule index', + ' */', + 'require("aPackage")', + ].join('\n'), + 'aPackage': { + 'package.json': JSON.stringify(replaceBrowserField({ + name: 'aPackage', + main: './main.js', + browser: { + './main.js': './client.js', + }, + }, fieldName)), + 'main.js': 'some other code', + 'client.js': 'some code', + } + } + }); + + var dgraph = new DependencyGraph({ + ...defaults, + roots: [root], + assetExts: ['png', 'jpg'], + }); + return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) { + expect(deps) + .toEqual([ + { + id: 'index', + path: '/root/index.js', + dependencies: ['aPackage'], + isAsset: false, + isAsset_DEPRECATED: false, + isJSON: false, + isPolyfill: false, + resolution: undefined, + }, + { id: 'aPackage/client.js', + path: '/root/aPackage/client.js', + dependencies: [], + isAsset: false, + isAsset_DEPRECATED: false, + isJSON: false, + isPolyfill: false, + resolution: undefined, + resolveDependency: undefined, + }, + ]); + }); + }); + + pit('should work do correct browser mapping w/o js ext ("' + fieldName + '")', function() { + var root = '/root'; + fs.__setMockFilesystem({ + 'root': { + 'index.js': [ + '/**', + ' * @providesModule index', + ' */', + 'require("aPackage")', + ].join('\n'), + 'aPackage': { + 'package.json': JSON.stringify(replaceBrowserField({ + name: 'aPackage', + main: './main.js', + browser: { + './main': './client.js', + }, + }, fieldName)), + 'main.js': 'some other code', + 'client.js': 'some code', + } + } + }); + + var dgraph = new DependencyGraph({ + ...defaults, + roots: [root], + assetExts: ['png', 'jpg'], + }); + return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) { + expect(deps) + .toEqual([ + { + id: 'index', + path: '/root/index.js', + dependencies: ['aPackage'], + isAsset: false, + isAsset_DEPRECATED: false, + isJSON: false, + isPolyfill: false, + resolution: undefined, + resolveDependency: undefined, + }, + { + id: 'aPackage/client.js', + path: '/root/aPackage/client.js', + dependencies: [], + isAsset: false, + isAsset_DEPRECATED: false, + isJSON: false, + isPolyfill: false, + resolution: undefined, + resolveDependency: undefined, + }, + ]); + }); + }); + + pit('should support browser mapping of files ("' + fieldName + '")', function() { + var root = '/root'; + fs.__setMockFilesystem({ + 'root': { + 'index.js': [ + '/**', + ' * @providesModule index', + ' */', + 'require("aPackage")', + ].join('\n'), + 'aPackage': { + 'package.json': JSON.stringify(replaceBrowserField({ + name: 'aPackage', + main: './main.js', + browser: { + './main': './client.js', + './node.js': './not-node.js', + './not-browser': './browser.js', + './dir/server.js': './dir/client', + './hello.js': './bye.js', + }, + }, fieldName)), + 'main.js': 'some other code', + 'client.js': 'require("./node")\nrequire("./dir/server.js")', + 'not-node.js': 'require("./not-browser")', + 'not-browser.js': 'require("./dir/server")', + 'browser.js': 'some browser code', + 'dir': { + 'server.js': 'some node code', + 'client.js': 'require("../hello")', + }, + 'hello.js': 'hello', + 'bye.js': 'bye', + } + } + }); + var dgraph = new DependencyGraph({ ...defaults, roots: [root], }); return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) { - expect(deps) - .toEqual([ - { - id: 'index', - path: '/root/index.js', - dependencies: ['aPackage'], - isAsset: false, - isAsset_DEPRECATED: false, - isJSON: false, - isPolyfill: false, - resolution: undefined, - }, - { id: 'aPackage/client.js', - path: '/root/aPackage/client.js', - dependencies: [], - isAsset: false, - isAsset_DEPRECATED: false, - isJSON: false, - isPolyfill: false, - resolution: undefined, - resolveDependency: undefined, - }, - ]); + expect(deps) + .toEqual([ + { id: 'index', + path: '/root/index.js', + dependencies: ['aPackage'], + isAsset: false, + isAsset_DEPRECATED: false, + isJSON: false, + isPolyfill: false, + resolution: undefined, + }, + { id: 'aPackage/client.js', + path: '/root/aPackage/client.js', + dependencies: ['./node', './dir/server.js'], + isAsset: false, + isAsset_DEPRECATED: false, + isJSON: false, + isPolyfill: false, + resolution: undefined, + }, + { id: 'aPackage/not-node.js', + path: '/root/aPackage/not-node.js', + dependencies: ['./not-browser'], + isAsset: false, + isAsset_DEPRECATED: false, + isJSON: false, + isPolyfill: false, + resolution: undefined, + }, + { id: 'aPackage/browser.js', + path: '/root/aPackage/browser.js', + dependencies: [], + isAsset: false, + isAsset_DEPRECATED: false, + isJSON: false, + isPolyfill: false, + resolution: undefined, + }, + { + id: 'aPackage/dir/client.js', + path: '/root/aPackage/dir/client.js', + dependencies: ['../hello'], + isAsset: false, + isAsset_DEPRECATED: false, + isJSON: false, + isPolyfill: false, + resolution: undefined, + }, + { + id: 'aPackage/bye.js', + path: '/root/aPackage/bye.js', + dependencies: [], + isAsset: false, + isAsset_DEPRECATED: false, + isJSON: false, + isPolyfill: false, + resolution: undefined, + }, + ]); + }); }); - }); - pit('should work do correct browser mapping w/o js ext', function() { + pit('should support browser mapping for packages ("' + fieldName + '")', function() { + var root = '/root'; + fs.__setMockFilesystem({ + 'root': { + 'index.js': [ + '/**', + ' * @providesModule index', + ' */', + 'require("aPackage")', + ].join('\n'), + 'aPackage': { + 'package.json': JSON.stringify(replaceBrowserField({ + name: 'aPackage', + browser: { + 'node-package': 'browser-package', + } + }, fieldName)), + 'index.js': 'require("node-package")', + 'node-package': { + 'package.json': JSON.stringify({ + 'name': 'node-package', + }), + 'index.js': 'some node code', + }, + 'browser-package': { + 'package.json': JSON.stringify({ + 'name': 'browser-package', + }), + 'index.js': 'some browser code', + }, + } + } + }); + + var dgraph = new DependencyGraph({ + ...defaults, + roots: [root], + }); + return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) { + expect(deps) + .toEqual([ + { id: 'index', + path: '/root/index.js', + dependencies: ['aPackage'], + isAsset: false, + isAsset_DEPRECATED: false, + isJSON: false, + isPolyfill: false, + resolution: undefined, + }, + { id: 'aPackage/index.js', + path: '/root/aPackage/index.js', + dependencies: ['node-package'], + isAsset: false, + isAsset_DEPRECATED: false, + isJSON: false, + isPolyfill: false, + resolution: undefined, + }, + { id: 'browser-package/index.js', + path: '/root/aPackage/browser-package/index.js', + dependencies: [], + isAsset: false, + isAsset_DEPRECATED: false, + isJSON: false, + isPolyfill: false, + resolution: undefined, + }, + ]); + }); + }); + + pit('should support browser mapping for packages ("' + fieldName + '")', function() { + var root = '/root'; + fs.__setMockFilesystem({ + 'root': { + 'index.js': [ + '/**', + ' * @providesModule index', + ' */', + 'require("aPackage")', + ].join('\n'), + 'aPackage': { + 'package.json': JSON.stringify(replaceBrowserField({ + name: 'aPackage', + browser: { + 'node-package': 'browser-package', + } + }, fieldName)), + 'index.js': 'require("node-package")', + 'node-package': { + 'package.json': JSON.stringify({ + 'name': 'node-package', + }), + 'index.js': 'some node code', + }, + 'browser-package': { + 'package.json': JSON.stringify({ + 'name': 'browser-package', + }), + 'index.js': 'some browser code', + }, + } + } + }); + + var dgraph = new DependencyGraph({ + ...defaults, + roots: [root], + }); + return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) { + expect(deps) + .toEqual([ + { id: 'index', + path: '/root/index.js', + dependencies: ['aPackage'], + isAsset: false, + isAsset_DEPRECATED: false, + isJSON: false, + isPolyfill: false, + resolution: undefined, + }, + { id: 'aPackage/index.js', + path: '/root/aPackage/index.js', + dependencies: ['node-package'], + isAsset: false, + isAsset_DEPRECATED: false, + isJSON: false, + isPolyfill: false, + resolution: undefined, + }, + { id: 'browser-package/index.js', + path: '/root/aPackage/browser-package/index.js', + dependencies: [], + isAsset: false, + isAsset_DEPRECATED: false, + isJSON: false, + isPolyfill: false, + resolution: undefined, + }, + ]); + }); + }); + } + + pit('should fall back to browser mapping from react-native mapping', function() { var root = '/root'; fs.__setMockFilesystem({ 'root': { @@ -1538,186 +1859,36 @@ describe('DependencyGraph', function() { 'aPackage': { 'package.json': JSON.stringify({ name: 'aPackage', - main: './main.js', - browser: { - './main': './client.js', - }, - }), - 'main.js': 'some other code', - 'client.js': 'some code', - }, - }, - }); - - var dgraph = new DependencyGraph({ - ...defaults, - roots: [root], - }); - return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) { - expect(deps) - .toEqual([ - { - id: 'index', - path: '/root/index.js', - dependencies: ['aPackage'], - isAsset: false, - isAsset_DEPRECATED: false, - isJSON: false, - isPolyfill: false, - resolution: undefined, - resolveDependency: undefined, - }, - { - id: 'aPackage/client.js', - path: '/root/aPackage/client.js', - dependencies: [], - isAsset: false, - isAsset_DEPRECATED: false, - isJSON: false, - isPolyfill: false, - resolution: undefined, - resolveDependency: undefined, - }, - ]); - }); - }); - - pit('should support browser mapping of files', function() { - var root = '/root'; - fs.__setMockFilesystem({ - 'root': { - 'index.js': [ - '/**', - ' * @providesModule index', - ' */', - 'require("aPackage")', - ].join('\n'), - 'aPackage': { - 'package.json': JSON.stringify({ - name: 'aPackage', - main: './main.js', - browser: { - './main': './client.js', - './node.js': './not-node.js', - './not-browser': './browser.js', - './dir/server.js': './dir/client', - './hello.js': './bye.js', - }, - }), - 'main.js': 'some other code', - 'client.js': 'require("./node")\nrequire("./dir/server.js")', - 'not-node.js': 'require("./not-browser")', - 'not-browser.js': 'require("./dir/server")', - 'browser.js': 'some browser code', - 'dir': { - 'server.js': 'some node code', - 'client.js': 'require("../hello")', - }, - 'hello.js': 'hello', - 'bye.js': 'bye', - }, - }, - }); - - var dgraph = new DependencyGraph({ - ...defaults, - roots: [root], - }); - return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) { - expect(deps) - .toEqual([ - { id: 'index', - path: '/root/index.js', - dependencies: ['aPackage'], - isAsset: false, - isAsset_DEPRECATED: false, - isJSON: false, - isPolyfill: false, - resolution: undefined, - }, - { id: 'aPackage/client.js', - path: '/root/aPackage/client.js', - dependencies: ['./node', './dir/server.js'], - isAsset: false, - isAsset_DEPRECATED: false, - isJSON: false, - isPolyfill: false, - resolution: undefined, - }, - { id: 'aPackage/not-node.js', - path: '/root/aPackage/not-node.js', - dependencies: ['./not-browser'], - isAsset: false, - isAsset_DEPRECATED: false, - isJSON: false, - isPolyfill: false, - resolution: undefined, - }, - { id: 'aPackage/browser.js', - path: '/root/aPackage/browser.js', - dependencies: [], - isAsset: false, - isAsset_DEPRECATED: false, - isJSON: false, - isPolyfill: false, - resolution: undefined, - }, - { - id: 'aPackage/dir/client.js', - path: '/root/aPackage/dir/client.js', - dependencies: ['../hello'], - isAsset: false, - isAsset_DEPRECATED: false, - isJSON: false, - isPolyfill: false, - resolution: undefined, - }, - { - id: 'aPackage/bye.js', - path: '/root/aPackage/bye.js', - dependencies: [], - isAsset: false, - isAsset_DEPRECATED: false, - isJSON: false, - isPolyfill: false, - resolution: undefined, - }, - ]); - }); - }); - - pit('should support browser mapping for packages', function() { - var root = '/root'; - fs.__setMockFilesystem({ - 'root': { - 'index.js': [ - '/**', - ' * @providesModule index', - ' */', - 'require("aPackage")', - ].join('\n'), - 'aPackage': { - 'package.json': JSON.stringify({ - name: 'aPackage', - browser: { - 'node-package': 'browser-package', - }, + 'react-native': { + 'node-package': 'rn-package', + } }), 'index.js': 'require("node-package")', - 'node-package': { - 'package.json': JSON.stringify({ - 'name': 'node-package', - }), - 'index.js': 'some node code', - }, - 'browser-package': { - 'package.json': JSON.stringify({ - 'name': 'browser-package', - }), - 'index.js': 'some browser code', - }, - }, - }, + 'node_modules': { + 'node-package': { + 'package.json': JSON.stringify({ + 'name': 'node-package' + }), + 'index.js': 'some node code', + }, + 'rn-package': { + 'package.json': JSON.stringify({ + 'name': 'rn-package', + browser: { + 'nested-package': 'nested-browser-package' + } + }), + 'index.js': 'require("nested-package")', + }, + 'nested-browser-package': { + 'package.json': JSON.stringify({ + 'name': 'nested-browser-package', + }), + 'index.js': 'some code' + } + } + } + } }); var dgraph = new DependencyGraph({ @@ -1745,8 +1916,17 @@ describe('DependencyGraph', function() { isPolyfill: false, resolution: undefined, }, - { id: 'browser-package/index.js', - path: '/root/aPackage/browser-package/index.js', + { id: 'rn-package/index.js', + path: '/root/aPackage/node_modules/rn-package/index.js', + dependencies: ['nested-package'], + isAsset: false, + isAsset_DEPRECATED: false, + isJSON: false, + isPolyfill: false, + resolution: undefined, + }, + { id: 'nested-browser-package/index.js', + path: '/root/aPackage/node_modules/nested-browser-package/index.js', dependencies: [], isAsset: false, isAsset_DEPRECATED: false, diff --git a/packager/react-packager/src/DependencyResolver/Package.js b/packager/react-packager/src/DependencyResolver/Package.js index d45a8923e..2117e3f19 100644 --- a/packager/react-packager/src/DependencyResolver/Package.js +++ b/packager/react-packager/src/DependencyResolver/Package.js @@ -15,17 +15,18 @@ class Package { getMain() { return this._read().then(json => { - if (typeof json.browser === 'string') { - return path.join(this.root, json.browser); + var replacements = getReplacements(json) + if (typeof replacements === 'string') { + return path.join(this.root, replacements); } let main = json.main || 'index'; - if (json.browser && typeof json.browser === 'object') { - main = json.browser[main] || - json.browser[main + '.js'] || - json.browser[main + '.json'] || - json.browser[main.replace(/(\.js|\.json)$/, '')] || + if (replacements && typeof replacements === 'object') { + main = replacements[main] || + replacements[main + '.js'] || + replacements[main + '.json'] || + replacements[main.replace(/(\.js|\.json)$/, '')] || main; } @@ -51,14 +52,14 @@ class Package { redirectRequire(name) { return this._read().then(json => { - const {browser} = json; + var replacements = getReplacements(json); - if (!browser || typeof browser !== 'object') { + if (!replacements || typeof replacements !== 'object') { return name; } if (name[0] !== '/') { - return browser[name] || name; + return replacements[name] || name; } if (!isAbsolutePath(name)) { @@ -66,9 +67,9 @@ class Package { } const relPath = './' + path.relative(this.root, name); - const redirect = browser[relPath] || - browser[relPath + '.js'] || - browser[relPath + '.json']; + const redirect = replacements[relPath] || + replacements[relPath + '.js'] || + replacements[relPath + '.json']; if (redirect) { return path.join( this.root, @@ -90,4 +91,10 @@ class Package { } } +function getReplacements(pkg) { + return pkg['react-native'] == null + ? pkg.browser + : pkg['react-native']; +} + module.exports = Package;