Skip to content

Commit

Permalink
Make the packager work with babel strict mode transform
Browse files Browse the repository at this point in the history
Summary:
At the moment we have to disable strict mode for the transform-es2015-modules-commonjs because strict mode leaks to the global scope and breaks the bridge. It was due to the way the polyfills were bundled in the package. To fix it, I wrapped the polyfill modules in an IIFE. Then when strict mode was enabled some polyfills were broken due to strict mode errors so that was fixed too. Also removed the IIFE from the polyfills that included one.

This diff doesn't enable the strict mode transform since some internal facebook modules depend on it not being enabled. When #5214 lands we could make the default babel config shipped with OSS react-native use strict mode modules and facebook could just modify the babel config to disable it if needed.

This will allow removing `"strict": false` from https://github.com/facebook/react-native/blob/master/packager/react-packager/.babelrc#L16

Fixes #5316
Closes #5422

Reviewed By: svcscm

Differential Revision: D2846422

Pulled By: davidaurelio

fb-gh-sync-id: a3e2f8909aa87dabab2b872c61b887e80220fb56
  • Loading branch information
janicduplessis authored and facebook-github-bot-4 committed Jan 21, 2016
1 parent d806b75 commit d33b554
Show file tree
Hide file tree
Showing 17 changed files with 970 additions and 950 deletions.
Expand Up @@ -25,11 +25,11 @@
require('regenerator/runtime');

if (typeof GLOBAL === 'undefined') {
GLOBAL = this;
global.GLOBAL = this;
}

if (typeof window === 'undefined') {
window = GLOBAL;
global.window = GLOBAL;
}

function setUpConsole() {
Expand Down Expand Up @@ -70,6 +70,15 @@ function polyfillGlobal(name, newValue, scope = GLOBAL) {
Object.defineProperty(scope, name, {...descriptor, value: newValue});
}

/**
* Polyfill a module if it is not already defined in `scope`.
*/
function polyfillIfNeeded(name, polyfill, scope = GLOBAL, descriptor = {}) {
if (scope[name] === undefined) {
Object.defineProperty(scope, name, {...descriptor, value: polyfill});
}
}

function setUpErrorHandler() {
if (global.__fbDisableExceptionsManager) {
return;
Expand All @@ -78,7 +87,7 @@ function setUpErrorHandler() {
function handleError(e, isFatal) {
try {
require('ExceptionsManager').handleException(e, isFatal);
} catch(ee) {
} catch (ee) {
console.log('Failed to print error: ', ee.message);
}
}
Expand Down Expand Up @@ -146,7 +155,11 @@ function setUpXHR() {
}

function setUpGeolocation() {
GLOBAL.navigator = GLOBAL.navigator || {};
polyfillIfNeeded('navigator', {}, GLOBAL, {
writable: true,
enumerable: true,
configurable: true,
});
polyfillGlobal('geolocation', require('Geolocation'), GLOBAL.navigator);
}

Expand Down Expand Up @@ -179,9 +192,9 @@ function setUpProcessEnv() {
}

function setUpNumber() {
Number.EPSILON = Number.EPSILON || Math.pow(2, -52);
Number.MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || Math.pow(2, 53) - 1;
Number.MIN_SAFE_INTEGER = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
polyfillIfNeeded('EPSILON', Math.pow(2, -52), Number);
polyfillIfNeeded('MAX_SAFE_INTEGER', Math.pow(2, 53) - 1, Number);
polyfillIfNeeded('MIN_SAFE_INTEGER', -(Math.pow(2, 53) - 1), Number);
}

function setUpDevTools() {
Expand Down
56 changes: 26 additions & 30 deletions Libraries/JavaScriptAppEngine/polyfills/document.js
@@ -1,34 +1,30 @@
/* eslint global-strict: 0 */
(function(GLOBAL) {
/**
* The document must be shimmed before anything else that might define the
* `ExecutionEnvironment` module (which checks for `document.createElement`).
*/
/* eslint strict: 0 */

// The browser defines Text and Image globals by default. If you forget to
// require them, then the error message is very confusing.
function getInvalidGlobalUseError(name) {
return new Error(
'You are trying to render the global ' + name + ' variable as a ' +
'React element. You probably forgot to require ' + name + '.'
);
// TODO: Remove document polyfill now that chrome debugging is in a web worker.

// The browser defines Text and Image globals by default. If you forget to
// require them, then the error message is very confusing.
function getInvalidGlobalUseError(name) {
return new Error(
'You are trying to render the global ' + name + ' variable as a ' +
'React element. You probably forgot to require ' + name + '.'
);
}
global.Text = {
get defaultProps() {
throw getInvalidGlobalUseError('Text');
}
GLOBAL.Text = {
get defaultProps() {
throw getInvalidGlobalUseError('Text');
}
};
GLOBAL.Image = {
get defaultProps() {
throw getInvalidGlobalUseError('Image');
}
};
// Force `ExecutionEnvironment.canUseDOM` to be false.
if (GLOBAL.document) {
GLOBAL.document.createElement = null;
};
global.Image = {
get defaultProps() {
throw getInvalidGlobalUseError('Image');
}
};
// Force `ExecutionEnvironment.canUseDOM` to be false.
if (global.document) {
global.document.createElement = null;
}

// There is no DOM so MutationObserver doesn't make sense. It is used
// as feature detection in Bluebird Promise implementation
GLOBAL.MutationObserver = undefined;
})(this);
// There is no DOM so MutationObserver doesn't make sense. It is used
// as feature detection in Bluebird Promise implementation
global.MutationObserver = undefined;
3 changes: 2 additions & 1 deletion Libraries/vendor/emitter/mixInEventEmitter.js
Expand Up @@ -49,12 +49,13 @@ var TYPES_KEY = keyOf({__types: true});
*/
function mixInEventEmitter(klass, types) {
invariant(types, 'Must supply set of valid event types');
invariant(!this.__eventEmitter, 'An active emitter is already mixed in');

// If this is a constructor, write to the prototype, otherwise write to the
// singleton object.
var target = klass.prototype || klass;

invariant(!target.__eventEmitter, 'An active emitter is already mixed in');

var ctor = klass.constructor;
if (ctor) {
invariant(
Expand Down
29 changes: 29 additions & 0 deletions packager/react-packager/src/Resolver/__tests__/Resolver-test.js
Expand Up @@ -58,6 +58,14 @@ describe('Resolver', function() {
return module;
}

function createPolyfill(id, dependencies) {
var polyfill = new Polyfill({});
polyfill.getName.mockImpl(() => Promise.resolve(id));
polyfill.getDependencies.mockImpl(() => Promise.resolve(dependencies));
polyfill.isPolyfill.mockReturnValue(true);
return polyfill;
}

describe('getDependencies', function() {
pit('should get dependencies with polyfills', function() {
var module = createModule('index');
Expand Down Expand Up @@ -1020,5 +1028,26 @@ describe('Resolver', function() {
].join('\n'));
});
});

pit('should resolve polyfills', function () {
const depResolver = new Resolver({
projectRoot: '/root',
});
const polyfill = createPolyfill('test polyfill', []);
const code = [
'global.fetch = () => 1;',
].join('');
return depResolver.wrapModule(
null,
polyfill,
code
).then(processedCode => {
expect(processedCode.code).toEqual([
'(function(global) {',
'global.fetch = () => 1;',
"\n})(typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : this);",
].join(''));
});
});
});
});
12 changes: 11 additions & 1 deletion packager/react-packager/src/Resolver/index.js
Expand Up @@ -214,7 +214,9 @@ class Resolver {

wrapModule(resolutionResponse, module, code) {
if (module.isPolyfill()) {
return Promise.resolve({code});
return Promise.resolve({
code: definePolyfillCode(code),
});
}

return this.resolveRequires(resolutionResponse, module, code).then(
Expand All @@ -239,4 +241,12 @@ function defineModuleCode(moduleName, code) {
].join('');
}

function definePolyfillCode(code) {
return [
'(function(global) {',
code,
`\n})(typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : this);`,
].join('');
}

module.exports = Resolver;
Expand Up @@ -5,54 +5,51 @@
* @polyfill
*/

/*eslint-disable */
/*jslint bitwise: true */
/* eslint-disable */

(function(undefined) {
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex
function findIndex(predicate, context) {
if (this == null) {
throw new TypeError(
'Array.prototype.findIndex called on null or undefined'
);
}
if (typeof predicate !== 'function') {
throw new TypeError('predicate must be a function');
}
var list = Object(this);
var length = list.length >>> 0;
for (var i = 0; i < length; i++) {
if (predicate.call(context, list[i], i, list)) {
return i;
}
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex
function findIndex(predicate, context) {
if (this == null) {
throw new TypeError(
'Array.prototype.findIndex called on null or undefined'
);
}
if (typeof predicate !== 'function') {
throw new TypeError('predicate must be a function');
}
var list = Object(this);
var length = list.length >>> 0;
for (var i = 0; i < length; i++) {
if (predicate.call(context, list[i], i, list)) {
return i;
}
return -1;
}
return -1;
}

if (!Array.prototype.findIndex) {
Object.defineProperty(Array.prototype, 'findIndex', {
enumerable: false,
writable: true,
configurable: true,
value: findIndex
});
}
if (!Array.prototype.findIndex) {
Object.defineProperty(Array.prototype, 'findIndex', {
enumerable: false,
writable: true,
configurable: true,
value: findIndex
});
}

// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
if (!Array.prototype.find) {
Object.defineProperty(Array.prototype, 'find', {
enumerable: false,
writable: true,
configurable: true,
value: function(predicate, context) {
if (this == null) {
throw new TypeError(
'Array.prototype.find called on null or undefined'
);
}
var index = findIndex.call(this, predicate, context);
return index === -1 ? undefined : this[index];
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
if (!Array.prototype.find) {
Object.defineProperty(Array.prototype, 'find', {
enumerable: false,
writable: true,
configurable: true,
value: function(predicate, context) {
if (this == null) {
throw new TypeError(
'Array.prototype.find called on null or undefined'
);
}
});
}
})();
var index = findIndex.call(this, predicate, context);
return index === -1 ? undefined : this[index];
}
});
}

0 comments on commit d33b554

Please sign in to comment.