diff --git a/packages/metro-transform-plugins/src/__tests__/inline-plugin-test.js b/packages/metro-transform-plugins/src/__tests__/inline-plugin-test.js index a1b1010354..711781d543 100644 --- a/packages/metro-transform-plugins/src/__tests__/inline-plugin-test.js +++ b/packages/metro-transform-plugins/src/__tests__/inline-plugin-test.js @@ -709,6 +709,37 @@ describe('inline constants', () => { }); }); + test("doesn't replace Platform.OS in other write targets", () => { + const code = ` + Platform.OS++; + delete Platform.OS; + [Platform.OS] = values; + ({os: Platform.OS} = value); + for (Platform.OS in object) {} + for ([Platform.OS] in nestedObject) {} + for (Platform.OS of values) {} + for ([Platform.OS] of nestedValues) {} + `; + + compare([inlinePlugin], code, code, { + inlinePlatform: true, + platform: 'ios', + }); + }); + + test('replaces Platform.OS when it is read inside a write target', () => { + const code = ` + target[Platform.OS] = value; + [target[Platform.OS]] = values; + ({[Platform.OS]: target} = value); + `; + + compare([inlinePlugin], code, code.replaceAll('Platform.OS', '"ios"'), { + inlinePlatform: true, + platform: 'ios', + }); + }); + test('replaces Platform.OS in the code if Platform is the right hand side of an assignment expression', () => { const code = ` function a() { diff --git a/packages/metro-transform-plugins/src/inline-plugin.js b/packages/metro-transform-plugins/src/inline-plugin.js index 2cc055d260..d548fcb1f5 100644 --- a/packages/metro-transform-plugins/src/inline-plugin.js +++ b/packages/metro-transform-plugins/src/inline-plugin.js @@ -44,7 +44,6 @@ export default function inlinePlugin( options: Options, ): PluginObj { const { - isAssignmentExpression, isIdentifier, isMemberExpression, isObjectExpression, @@ -69,10 +68,46 @@ export default function inlinePlugin( return !binding || isFlowDeclared(binding); } - const isLeftHandSideOfAssignmentExpression = ( - node: Node, - parent: Node, - ): boolean => isAssignmentExpression(parent) && parent.left === node; + function isWriteTarget(path: NodePath): boolean { + let child: Node = path.node; + let parentPath = path.parentPath; + + while (parentPath != null) { + const parent = parentPath.node; + if ( + (parent.type === 'AssignmentExpression' || + parent.type === 'ForInStatement' || + parent.type === 'ForOfStatement') && + parent.left === child + ) { + return true; + } + if (parent.type === 'UpdateExpression' && parent.argument === child) { + return true; + } + if ( + parent.type === 'UnaryExpression' && + parent.operator === 'delete' && + parent.argument === child + ) { + return true; + } + + const nestedWriteTarget = + parent.type === 'ArrayPattern' || + parent.type === 'ObjectPattern' || + (parent.type === 'ObjectProperty' && parent.value === child) || + (parent.type === 'RestElement' && parent.argument === child) || + (parent.type === 'AssignmentPattern' && parent.left === child); + if (!nestedWriteTarget) { + return false; + } + + child = parent; + parentPath = parentPath.parentPath; + } + return false; + } const isProcessEnvNodeEnv = (node: MemberExpression, scope: Scope): boolean => isIdentifier(node.property, nodeEnv) && @@ -138,7 +173,7 @@ export default function inlinePlugin( const scope = path.scope; const opts = state.opts; - if (!isLeftHandSideOfAssignmentExpression(node, path.parent)) { + if (!isWriteTarget(path)) { if ( opts.inlinePlatform && isPlatformNode(node, scope, !!opts.isWrapped)