Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 14 additions & 5 deletions packages/metro-transform-plugins/src/__mocks__/test-helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ const nullthrows = require('nullthrows');

function makeTransformOptions<OptionsT extends ?EntryOptions>(
plugins: ReadonlyArray<PluginEntry>,
options: OptionsT,
pluginOptions: OptionsT,
babelOptions?: BabelCoreOptions,
): BabelCoreOptions {
return {
ast: true,
Expand All @@ -30,9 +31,12 @@ function makeTransformOptions<OptionsT extends ?EntryOptions>(
compact: true,
configFile: false,
plugins: plugins.length
? plugins.map(plugin => [plugin, options])
? plugins.map(plugin => [plugin, pluginOptions])
: [() => ({visitor: {}})],
sourceType: 'module',
filename:
'/Users/test/app/node_modules/react-native/Libraries/Components/Pressable/useAndroidRippleForView.js',
...babelOptions,
};
}

Expand All @@ -55,10 +59,11 @@ function transformToAst<T extends ?EntryOptions>(
plugins: ReadonlyArray<PluginEntry>,
code: string,
options: T,
babelOptions?: BabelCoreOptions,
): BabelNodeFile {
const transformResult = transformSync(
code,
makeTransformOptions(plugins, options),
makeTransformOptions(plugins, options, babelOptions),
);
const ast = nullthrows(transformResult.ast);
validateOutputAst(ast);
Expand All @@ -69,17 +74,21 @@ function transform(
code: string,
plugins: ReadonlyArray<PluginEntry>,
options: ?EntryOptions,
babelOptions?: BabelCoreOptions,
) {
return generate(transformToAst(plugins, code, options)).code;
return generate(transformToAst(plugins, code, options, babelOptions)).code;
}

exports.compare = function (
plugins: ReadonlyArray<PluginEntry>,
code: string,
expected: string,
options: ?EntryOptions = {},
babelOptions?: BabelCoreOptions,
) {
expect(transform(code, plugins, options)).toBe(transform(expected, [], {}));
expect(transform(code, plugins, options, babelOptions)).toBe(
transform(expected, [], {}),
);
};

exports.transformToAst = transformToAst;
196 changes: 196 additions & 0 deletions packages/metro-transform-plugins/src/__tests__/inline-plugin-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -934,4 +934,200 @@ describe('inline constants', () => {

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

test('replaces Platform.OS in the code if Platform is a top level relative Node.js require()', () => {
// Source code before `@react-native/babel-preset`:
// ```
// const Platform = require('../../Utilities/Platform').default;
// ```
const code = `
var Platform = require('../../Utilities/Platform').default;
var test = Platform.OS === 'ios' ? 'ios' : 'not-ios';
`;

compare([inlinePlugin], code, code.replace('Platform.OS', '"android"'), {
inlinePlatform: true,
platform: 'android',
});
});

test('replaces Platform.OS in the code if Platform is a top level relative ES import', () => {
// Source code before `@react-native/babel-preset`:
// ```
// import Platform from '../../Utilities/Platform';
// ```
const code = `
var _Platform = _interopRequireDefault(require("../../Utilities/Platform"));
var test = _Platform.default.OS === 'ios' ? 'ios' : 'not-ios';
`;

compare(
[inlinePlugin],
code,
code.replace('_Platform.default.OS', '"android"'),
{
inlinePlatform: true,
platform: 'android',
},
);
});

test('should not replace Platform.OS in the code if Platform was exported from unexpected place', () => {
// Source code before `@react-native/babel-preset`:
// ```
// import Platform from '../../diff/lib/name.js';
// ```
const code = `
var _Platform = _interopRequireDefault(require("../../diff/lib/name.js"));
var test = _Platform.default.OS === 'ios' ? 'ios' : 'not-ios';
`;

compare([inlinePlugin], code, code, {
inlinePlatform: true,
platform: 'android',
});
});

test('replaces Platform.OS in the code if babel helper wrap require call', () => {
// Source code before `@react-native/babel-preset`:
// ```
// import * as RN from 'react-native';
// ```
const code = `
var RN = _interopRequireWildcard(require('react-native'));
var test = RN.Platform.OS === 'ios' ? 'ios' : 'not-ios';
`;

compare([inlinePlugin], code, code.replace('RN.Platform.OS', '"android"'), {
inlinePlatform: 'true',
platform: 'android',
});
});

test('replaces Platform.select in the code if Platform is a top level relative Node.js require()', () => {
// Source code before `@react-native/babel-preset`:
// ```
// const Platform = require('../../Utilities/Platform').default;
// ```
const code = `
var Platform = require('../../Utilities/Platform').default;

function a() {
Platform.select({ios: 1, android: 2});
var b = a.Platform.select({});
}
`;

compare([inlinePlugin], code, code.replace(/Platform\.select[^;]+/, '2'), {
inlinePlatform: 'true',
platform: 'android',
});
});

test('replaces Platform.select in the code if Platform is a top level relative ES import', () => {
// Source code before `@react-native/babel-preset`:
// ```
// import Platform from '../../Utilities/Platform';
// ```
const code = `
var _Platform = _interopRequireDefault(require("../../Utilities/Platform"));

function a() {
_Platform.default.select({ios: 1, android: 2});
var b = a.Platform.select({});
}
`;

compare(
[inlinePlugin],
code,
code.replace(/_Platform\.default\.select[^;]+/, '2'),
{
inlinePlatform: 'true',
platform: 'android',
},
);
});

test('should not replace Platform.select in the code if Platform was imported from non react-native package files', () => {
// Source code before `@react-native/babel-preset`:
// ```
// import Platform from '../../Utilities/Platform';
// ```
const code = `
var _Platform = _interopRequireDefault(require("../Platform"));

function a() {
_Platform.default.select({ios: 1, android: 2});
var b = a.Platform.select({});
}
`;

compare(
[inlinePlugin],
code,
code,
{
inlinePlatform: 'true',
platform: 'android',
},
{
filename: '/Users/test/app/src/myCode.js',
},
);
});

test('replaces Platform.select in the code if Platform is a top level relative ES import on Windows', () => {
// Source code before `@react-native/babel-preset`:
// ```
// import Platform from '../../Utilities/Platform';
// ```
const code = `
var _Platform = _interopRequireDefault(require("../../Utilities/Platform"));

function a() {
_Platform.default.select({ios: 1, android: 2});
var b = a.Platform.select({});
}
`;

compare(
[inlinePlugin],
code,
code.replace(/_Platform\.default\.select[^;]+/, '2'),
{
inlinePlatform: 'true',
platform: 'android',
},
{
filename:
'C:\\Users\\test\\app\\node_modules\\react-native\\Libraries\\Components\\Pressable\\useAndroidRippleForView.js',
},
);
});

test('replaces Platform.select in the code if babel helper wrap require call', () => {
// Source code before `@react-native/babel-preset`:
// ```
// import * as RN from 'react-native';
// ```
const code = `
var RN = _interopRequireWildcard(require('react-native'));

function a() {
RN.Platform.select({ios: 1, android: 2});
var b = a.Platform.select({});
}
`;

compare(
[inlinePlugin],
code,
code.replace(/RN\.Platform\.select[^;]+/, '2'),
{
inlinePlatform: 'true',
platform: 'android',
},
);
});
});
8 changes: 5 additions & 3 deletions packages/metro-transform-plugins/src/inline-plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export type Options = Readonly<{
platform: string,
}>;

type State = {opts: Options};
type State = {opts: Options, filename?: string};

const env = {name: 'env'};
const nodeEnv = {name: 'NODE_ENV'};
Expand Down Expand Up @@ -137,11 +137,12 @@ export default function inlinePlugin(
const node = path.node;
const scope = path.scope;
const opts = state.opts;
const filename = state.filename;

if (!isLeftHandSideOfAssignmentExpression(node, path.parent)) {
if (
opts.inlinePlatform &&
isPlatformNode(node, scope, !!opts.isWrapped)
isPlatformNode(node, scope, !!opts.isWrapped, filename)
) {
path.replaceWith(t.stringLiteral(opts.platform));
} else if (!opts.dev && isProcessEnvNodeEnv(node, scope)) {
Expand All @@ -156,10 +157,11 @@ export default function inlinePlugin(
const scope = path.scope;
const arg = node.arguments[0];
const opts = state.opts;
const filename = state.filename;

if (
opts.inlinePlatform &&
isPlatformSelectNode(node, scope, !!opts.isWrapped) &&
isPlatformSelectNode(node, scope, !!opts.isWrapped, filename) &&
isObjectExpression(arg)
) {
if (hasStaticProperties(arg)) {
Expand Down
Loading
Loading