Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix inlinePlugin replacing __DEV__ in invalid Babel AST locations #1195

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
125 changes: 93 additions & 32 deletions packages/metro-transform-plugins/src/__tests__/inline-plugin-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,45 +16,106 @@ const inlinePlugin = require('../inline-plugin');
const stripFlow = require('@babel/plugin-transform-flow-strip-types');

describe('inline constants', () => {
it('replaces __DEV__ in the code', () => {
const code = `
function a() {
var a = __DEV__ ? 1 : 2;
var b = a.__DEV__;
var c = function __DEV__(__DEV__) {};
}
`;
describe('__DEV__', () => {
it('replaces __DEV__ in the code', () => {
const code = `
function a() {
var a = __DEV__ ? 1 : 2;
var b = a.__DEV__;
var c = function __DEV__(__DEV__) {};
}
`;

compare([inlinePlugin], code, code.replace(/__DEV__/, 'false'), {
dev: false,
compare([inlinePlugin], code, code.replace(/__DEV__/, 'false'), {
dev: false,
});
});
});

it("doesn't replace a local __DEV__ variable", () => {
const code = `
function a() {
var __DEV__ = false;
var a = __DEV__ ? 1 : 2;
}
`;
it("doesn't replace a local __DEV__ variable", () => {
const code = `
function a() {
var __DEV__ = false;
var a = __DEV__ ? 1 : 2;
}
`;

compare([inlinePlugin], code, code, {dev: false});
});
compare([inlinePlugin], code, code, {dev: false});
});

it("doesn't replace __DEV__ in an object property key", () => {
const code = `
const x = {
__DEV__: __DEV__
};
`;
it("doesn't replace __DEV__ in an object property key", () => {
const code = `
const x = { __DEV__: __DEV__ };
`;

const expected = `
const x = {
__DEV__: false
};
`;
const expected = `
const x = { __DEV__: false };
`;

compare([inlinePlugin], code, expected, {dev: false});
});

it("doesn't replace __DEV__ in an object shorthand method name", () => {
const code = `
const x = {
__DEV__() { return __DEV__; },
};
`;

const expected = `
const x = {
__DEV__() { return false; },
};
`;

compare([inlinePlugin], code, expected, {dev: false});
});

it("doesn't replace __DEV__ as a label of a block statement", () => {
const code = `
__DEV__: {
break __DEV__;
};
`;

compare([inlinePlugin], code, code, {dev: false});
});

it("doesn't replace __DEV__ as the name of a function declaration or call", () => {
const code = `
function __DEV__() { return false; }
const isDev = __DEV__();
`;

compare([inlinePlugin], code, code, {dev: false});
});

it("doesn't replace __DEV__ as the name of a class method", () => {
const code = `
class Test {
__DEV__() {}
}
`;

compare([inlinePlugin], code, code, {dev: false});
});

compare([inlinePlugin], code, expected, {dev: false});
it("doesn't replace __DEV__ as the name of an export alias", () => {
const code = `
const dev = true;
export { dev as __DEV__ };
`;

compare([inlinePlugin], code, code, {dev: false});
});

it("doesn't replace __DEV__ as the name of an optional property access", () => {
const code = `
x?.__DEV__;
x?.__DEV__();
`;

compare([inlinePlugin], code, code, {dev: false});
});
});

it('replaces Platform.OS in the code if Platform is a global', () => {
Expand Down
10 changes: 3 additions & 7 deletions packages/metro-transform-plugins/src/inline-plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import type {PluginObj} from '@babel/core';
import type {Binding, NodePath, Scope} from '@babel/traverse';
import type {
CallExpression,
Identifier,
MemberExpression,
Node,
ObjectExpression,
Expand Down Expand Up @@ -86,12 +85,9 @@ function inlinePlugin(
isIdentifier(node.object.object, processId) &&
isGlobal(scope.getBinding(processId.name));

const isDev = (node: Identifier, parent: Node, scope: Scope): boolean =>
const isDev = (node: Node, parent: Node, scope: Scope): boolean =>
isIdentifier(node, dev) &&
isGlobalOrFlowDeclared(scope.getBinding(dev.name)) &&
!isMemberExpression(parent) &&
// not { __DEV__: 'value'}
(!isObjectProperty(parent) || parent.value === node);
isGlobalOrFlowDeclared(scope.getBinding(dev.name));

function findProperty(
objectExpression: ObjectExpression,
Expand Down Expand Up @@ -136,7 +132,7 @@ function inlinePlugin(

return {
visitor: {
Identifier(path: NodePath<Identifier>, state: State): void {
ReferencedIdentifier(path: NodePath<Node>, state: State): void {
if (!state.opts.dev && isDev(path.node, path.parent, path.scope)) {
path.replaceWith(t.booleanLiteral(state.opts.dev));
}
Expand Down