mirror of https://github.com/status-im/metro.git
Improve constant inlining, add `process.platform`
Reviewed By: bestander Differential Revision: D3235716 fb-gh-sync-id: f9019ec0042827e409fa84ba74f4c426ccad1519 fbshipit-source-id: f9019ec0042827e409fa84ba74f4c426ccad1519
This commit is contained in:
parent
cc538b9b35
commit
17726e1ae6
|
@ -35,7 +35,7 @@ describe('inline constants', () => {
|
||||||
var a = __DEV__ ? 1 : 2;
|
var a = __DEV__ ? 1 : 2;
|
||||||
var b = a.__DEV__;
|
var b = a.__DEV__;
|
||||||
var c = function __DEV__(__DEV__) {};
|
var c = function __DEV__(__DEV__) {};
|
||||||
}`
|
}`;
|
||||||
const {ast} = inline('arbitrary.js', {code}, {dev: true});
|
const {ast} = inline('arbitrary.js', {code}, {dev: true});
|
||||||
expect(toString(ast)).toEqual(normalize(code.replace(/__DEV__/, 'true')));
|
expect(toString(ast)).toEqual(normalize(code.replace(/__DEV__/, 'true')));
|
||||||
});
|
});
|
||||||
|
@ -44,7 +44,7 @@ describe('inline constants', () => {
|
||||||
const code = `function a() {
|
const code = `function a() {
|
||||||
var a = Platform.OS;
|
var a = Platform.OS;
|
||||||
var b = a.Platform.OS;
|
var b = a.Platform.OS;
|
||||||
}`
|
}`;
|
||||||
const {ast} = inline('arbitrary.js', {code}, {platform: 'ios'});
|
const {ast} = inline('arbitrary.js', {code}, {platform: 'ios'});
|
||||||
expect(toString(ast)).toEqual(normalize(code.replace(/Platform\.OS/, '"ios"')));
|
expect(toString(ast)).toEqual(normalize(code.replace(/Platform\.OS/, '"ios"')));
|
||||||
});
|
});
|
||||||
|
@ -55,7 +55,18 @@ describe('inline constants', () => {
|
||||||
function a() {
|
function a() {
|
||||||
if (Platform.OS === 'android') a = function() {};
|
if (Platform.OS === 'android') a = function() {};
|
||||||
var b = a.Platform.OS;
|
var b = a.Platform.OS;
|
||||||
}`
|
}`;
|
||||||
|
const {ast} = inline('arbitrary.js', {code}, {platform: 'ios'});
|
||||||
|
expect(toString(ast)).toEqual(normalize(code.replace(/Platform\.OS/, '"ios"')));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('replaces Platform.OS in the code if Platform is a top level import from react-native', () => {
|
||||||
|
const code = `
|
||||||
|
var Platform = require('react-native').Platform;
|
||||||
|
function a() {
|
||||||
|
if (Platform.OS === 'android') a = function() {};
|
||||||
|
var b = a.Platform.OS;
|
||||||
|
}`;
|
||||||
const {ast} = inline('arbitrary.js', {code}, {platform: 'ios'});
|
const {ast} = inline('arbitrary.js', {code}, {platform: 'ios'});
|
||||||
expect(toString(ast)).toEqual(normalize(code.replace(/Platform\.OS/, '"ios"')));
|
expect(toString(ast)).toEqual(normalize(code.replace(/Platform\.OS/, '"ios"')));
|
||||||
});
|
});
|
||||||
|
@ -64,7 +75,7 @@ describe('inline constants', () => {
|
||||||
const code = `function a() {
|
const code = `function a() {
|
||||||
var a = require('Platform').OS;
|
var a = require('Platform').OS;
|
||||||
var b = a.require('Platform').OS;
|
var b = a.require('Platform').OS;
|
||||||
}`
|
}`;
|
||||||
const {ast} = inline('arbitrary.js', {code}, {platform: 'android'});
|
const {ast} = inline('arbitrary.js', {code}, {platform: 'android'});
|
||||||
expect(toString(ast)).toEqual(
|
expect(toString(ast)).toEqual(
|
||||||
normalize(code.replace(/require\('Platform'\)\.OS/, '"android"')));
|
normalize(code.replace(/require\('Platform'\)\.OS/, '"android"')));
|
||||||
|
@ -74,18 +85,27 @@ describe('inline constants', () => {
|
||||||
const code = `function a() {
|
const code = `function a() {
|
||||||
var a = React.Platform.OS;
|
var a = React.Platform.OS;
|
||||||
var b = a.React.Platform.OS;
|
var b = a.React.Platform.OS;
|
||||||
}`
|
}`;
|
||||||
const {ast} = inline('arbitrary.js', {code}, {platform: 'ios'});
|
const {ast} = inline('arbitrary.js', {code}, {platform: 'ios'});
|
||||||
expect(toString(ast)).toEqual(normalize(code.replace(/React\.Platform\.OS/, '"ios"')));
|
expect(toString(ast)).toEqual(normalize(code.replace(/React\.Platform\.OS/, '"ios"')));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('replaces ReactNative.Platform.OS in the code if ReactNative is a global', () => {
|
||||||
|
const code = `function a() {
|
||||||
|
var a = ReactNative.Platform.OS;
|
||||||
|
var b = a.ReactNative.Platform.OS;
|
||||||
|
}`;
|
||||||
|
const {ast} = inline('arbitrary.js', {code}, {platform: 'ios'});
|
||||||
|
expect(toString(ast)).toEqual(normalize(code.replace(/ReactNative\.Platform\.OS/, '"ios"')));
|
||||||
|
});
|
||||||
|
|
||||||
it('replaces React.Platform.OS in the code if React is a top level import', () => {
|
it('replaces React.Platform.OS in the code if React is a top level import', () => {
|
||||||
const code = `
|
const code = `
|
||||||
var React = require('React');
|
var React = require('React');
|
||||||
function a() {
|
function a() {
|
||||||
if (React.Platform.OS === 'android') a = function() {};
|
if (React.Platform.OS === 'android') a = function() {};
|
||||||
var b = a.React.Platform.OS;
|
var b = a.React.Platform.OS;
|
||||||
}`
|
}`;
|
||||||
const {ast} = inline('arbitrary.js', {code}, {platform: 'ios'});
|
const {ast} = inline('arbitrary.js', {code}, {platform: 'ios'});
|
||||||
expect(toString(ast)).toEqual(normalize(code.replace(/React.Platform\.OS/, '"ios"')));
|
expect(toString(ast)).toEqual(normalize(code.replace(/React.Platform\.OS/, '"ios"')));
|
||||||
});
|
});
|
||||||
|
@ -94,19 +114,40 @@ describe('inline constants', () => {
|
||||||
const code = `function a() {
|
const code = `function a() {
|
||||||
var a = require('React').Platform.OS;
|
var a = require('React').Platform.OS;
|
||||||
var b = a.require('React').Platform.OS;
|
var b = a.require('React').Platform.OS;
|
||||||
}`
|
}`;
|
||||||
const {ast} = inline('arbitrary.js', {code}, {platform: 'android'});
|
const {ast} = inline('arbitrary.js', {code}, {platform: 'android'});
|
||||||
expect(toString(ast)).toEqual(
|
expect(toString(ast)).toEqual(
|
||||||
normalize(code.replace(/require\('React'\)\.Platform\.OS/, '"android"')));
|
normalize(code.replace(/require\('React'\)\.Platform\.OS/, '"android"')));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('replaces ReactNative.Platform.OS in the code if ReactNative is a top level import', () => {
|
||||||
|
const code = `
|
||||||
|
var ReactNative = require('react-native');
|
||||||
|
function a() {
|
||||||
|
if (ReactNative.Platform.OS === 'android') a = function() {};
|
||||||
|
var b = a.ReactNative.Platform.OS;
|
||||||
|
}`;
|
||||||
|
const {ast} = inline('arbitrary.js', {code}, {platform: 'android'});
|
||||||
|
expect(toString(ast)).toEqual(normalize(code.replace(/ReactNative.Platform\.OS/, '"android"')));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('replaces require("react-native").Platform.OS in the code', () => {
|
||||||
|
const code = `function a() {
|
||||||
|
var a = require('react-native').Platform.OS;
|
||||||
|
var b = a.require('react-native').Platform.OS;
|
||||||
|
}`;
|
||||||
|
const {ast} = inline('arbitrary.js', {code}, {platform: 'android'});
|
||||||
|
expect(toString(ast)).toEqual(
|
||||||
|
normalize(code.replace(/require\('react-native'\)\.Platform\.OS/, '"android"')));
|
||||||
|
});
|
||||||
|
|
||||||
it('replaces process.env.NODE_ENV in the code', () => {
|
it('replaces process.env.NODE_ENV in the code', () => {
|
||||||
const code = `function a() {
|
const code = `function a() {
|
||||||
if (process.env.NODE_ENV === 'production') {
|
if (process.env.NODE_ENV === 'production') {
|
||||||
return require('Prod');
|
return require('Prod');
|
||||||
}
|
}
|
||||||
return require('Dev');
|
return require('Dev');
|
||||||
}`
|
}`;
|
||||||
const {ast} = inline('arbitrary.js', {code}, {dev: false});
|
const {ast} = inline('arbitrary.js', {code}, {dev: false});
|
||||||
expect(toString(ast)).toEqual(
|
expect(toString(ast)).toEqual(
|
||||||
normalize(code.replace(/process\.env\.NODE_ENV/, '"production"')));
|
normalize(code.replace(/process\.env\.NODE_ENV/, '"production"')));
|
||||||
|
@ -118,16 +159,28 @@ describe('inline constants', () => {
|
||||||
return require('Prod');
|
return require('Prod');
|
||||||
}
|
}
|
||||||
return require('Dev');
|
return require('Dev');
|
||||||
}`
|
}`;
|
||||||
const {ast} = inline('arbitrary.js', {code}, {dev: true});
|
const {ast} = inline('arbitrary.js', {code}, {dev: true});
|
||||||
expect(toString(ast)).toEqual(
|
expect(toString(ast)).toEqual(
|
||||||
normalize(code.replace(/process\.env\.NODE_ENV/, '"development"')));
|
normalize(code.replace(/process\.env\.NODE_ENV/, '"development"')));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('replaces process.platform in the code', () => {
|
||||||
|
const code = `function a() {
|
||||||
|
if (process.platform === 'android') {
|
||||||
|
return require('./android');
|
||||||
|
}
|
||||||
|
return require('./ios');
|
||||||
|
}`;
|
||||||
|
const {ast} = inline('arbitrary.js', {code}, {platform: 'ios'});
|
||||||
|
expect(toString(ast)).toEqual(
|
||||||
|
normalize(code.replace(/process\.platform\b/, '"ios"')));
|
||||||
|
});
|
||||||
|
|
||||||
it('accepts an AST as input', function() {
|
it('accepts an AST as input', function() {
|
||||||
const code = `function ifDev(a,b){return __DEV__?a:b;}`;
|
const code = 'function ifDev(a,b){return __DEV__?a:b;}';
|
||||||
const {ast} = inline('arbitrary.hs', {ast: toAst(code)}, {dev: false});
|
const {ast} = inline('arbitrary.hs', {ast: toAst(code)}, {dev: false});
|
||||||
expect(toString(ast)).toEqual(code.replace(/__DEV__/, 'false'))
|
expect(toString(ast)).toEqual(code.replace(/__DEV__/, 'false'));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,8 @@
|
||||||
const babel = require('babel-core');
|
const babel = require('babel-core');
|
||||||
const t = babel.types;
|
const t = babel.types;
|
||||||
|
|
||||||
const react = {name: 'React'};
|
const React = {name: 'React'};
|
||||||
|
const ReactNative = {name: 'ReactNative'};
|
||||||
const platform = {name: 'Platform'};
|
const platform = {name: 'Platform'};
|
||||||
const os = {name: 'OS'};
|
const os = {name: 'OS'};
|
||||||
const requirePattern = {name: 'require'};
|
const requirePattern = {name: 'require'};
|
||||||
|
@ -19,9 +20,12 @@ const requirePattern = {name: 'require'};
|
||||||
const env = {name: 'env'};
|
const env = {name: 'env'};
|
||||||
const nodeEnv = {name: 'NODE_ENV'};
|
const nodeEnv = {name: 'NODE_ENV'};
|
||||||
const processId = {name: 'process'};
|
const processId = {name: 'process'};
|
||||||
|
const platformId = {name: 'platform'};
|
||||||
|
|
||||||
const dev = {name: '__DEV__'};
|
const dev = {name: '__DEV__'};
|
||||||
|
|
||||||
|
const importMap = new Map([['ReactNative', 'react-native']]);
|
||||||
|
|
||||||
const isGlobal = (binding) => !binding;
|
const isGlobal = (binding) => !binding;
|
||||||
|
|
||||||
const isToplevelBinding = (binding) => isGlobal(binding) || !binding.scope.parent;
|
const isToplevelBinding = (binding) => isGlobal(binding) || !binding.scope.parent;
|
||||||
|
@ -31,20 +35,27 @@ const isRequireCall = (node, dependencyId, scope) =>
|
||||||
t.isIdentifier(node.callee, requirePattern) &&
|
t.isIdentifier(node.callee, requirePattern) &&
|
||||||
t.isStringLiteral(node.arguments[0], t.stringLiteral(dependencyId));
|
t.isStringLiteral(node.arguments[0], t.stringLiteral(dependencyId));
|
||||||
|
|
||||||
const isImport = (node, scope, pattern) =>
|
const isImport = (node, scope, patterns) =>
|
||||||
t.isIdentifier(node, pattern) &&
|
patterns.some(pattern => {
|
||||||
isToplevelBinding(scope.getBinding(pattern.name)) ||
|
const importName = importMap.get(pattern.name) || pattern.name;
|
||||||
isRequireCall(node, pattern.name, scope);
|
return isRequireCall(node, importName, scope);
|
||||||
|
});
|
||||||
|
|
||||||
|
function isImportOrGlobal(node, scope, patterns) {
|
||||||
|
const identifier = patterns.find(pattern => t.isIdentifier(node, pattern));
|
||||||
|
return identifier && isToplevelBinding(scope.getBinding(identifier.name)) ||
|
||||||
|
isImport(node, scope, patterns);
|
||||||
|
}
|
||||||
|
|
||||||
const isPlatformOS = (node, scope) =>
|
const isPlatformOS = (node, scope) =>
|
||||||
t.isIdentifier(node.property, os) &&
|
t.isIdentifier(node.property, os) &&
|
||||||
isImport(node.object, scope, platform);
|
isImportOrGlobal(node.object, scope, [platform]);
|
||||||
|
|
||||||
const isReactPlatformOS = (node, scope) =>
|
const isReactPlatformOS = (node, scope) =>
|
||||||
t.isIdentifier(node.property, os) &&
|
t.isIdentifier(node.property, os) &&
|
||||||
t.isMemberExpression(node.object) &&
|
t.isMemberExpression(node.object) &&
|
||||||
t.isIdentifier(node.object.property, platform) &&
|
t.isIdentifier(node.object.property, platform) &&
|
||||||
isImport(node.object.object, scope, react);
|
isImportOrGlobal(node.object.object, scope, [React, ReactNative]);
|
||||||
|
|
||||||
const isProcessEnvNodeEnv = (node, scope) =>
|
const isProcessEnvNodeEnv = (node, scope) =>
|
||||||
t.isIdentifier(node.property, nodeEnv) &&
|
t.isIdentifier(node.property, nodeEnv) &&
|
||||||
|
@ -53,6 +64,11 @@ const isProcessEnvNodeEnv = (node, scope) =>
|
||||||
t.isIdentifier(node.object.object, processId) &&
|
t.isIdentifier(node.object.object, processId) &&
|
||||||
isGlobal(scope.getBinding(processId.name));
|
isGlobal(scope.getBinding(processId.name));
|
||||||
|
|
||||||
|
const isProcessPlatform = (node, scope) =>
|
||||||
|
t.isIdentifier(node.property, platformId) &&
|
||||||
|
t.isIdentifier(node.object, processId) &&
|
||||||
|
isGlobal(scope.getBinding(processId.name));
|
||||||
|
|
||||||
const isDev = (node, parent, scope) =>
|
const isDev = (node, parent, scope) =>
|
||||||
t.isIdentifier(node, dev) &&
|
t.isIdentifier(node, dev) &&
|
||||||
isGlobal(scope.getBinding(dev.name)) &&
|
isGlobal(scope.getBinding(dev.name)) &&
|
||||||
|
@ -71,11 +87,12 @@ const inlinePlugin = {
|
||||||
|
|
||||||
if (isPlatformOS(node, scope) || isReactPlatformOS(node, scope)) {
|
if (isPlatformOS(node, scope) || isReactPlatformOS(node, scope)) {
|
||||||
path.replaceWith(t.stringLiteral(state.opts.platform));
|
path.replaceWith(t.stringLiteral(state.opts.platform));
|
||||||
}
|
} else if (isProcessEnvNodeEnv(node, scope)) {
|
||||||
|
|
||||||
if(isProcessEnvNodeEnv(node, scope)) {
|
|
||||||
path.replaceWith(
|
path.replaceWith(
|
||||||
t.stringLiteral(state.opts.dev ? 'development' : 'production'));
|
t.stringLiteral(state.opts.dev ? 'development' : 'production'));
|
||||||
|
} else if (isProcessPlatform(node, scope)) {
|
||||||
|
path.replaceWith(
|
||||||
|
t.stringLiteral(state.opts.platform));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue