Skip to content

Commit 7680bde

Browse files
cipolleschifacebook-github-bot
authored andcommitted
Implement filtering for platform specific spec files
Summary: This diff helps the library maintainer to keep their spec file platform specific if some specs make no sense in one platform or in the other. We are filtering the spec files when we need to create the Schema. The diff modifies also the call sites in the `scripts` (for iOS) and in the `gradle-plugin` (for Android). It also adds tests for the new functions in the CLI. The change is completely additive and it should not change any pre-existing behaviour. ## Changelog [General][Added] - Add support for platform-specific specs Reviewed By: cortinico Differential Revision: D40008581 fbshipit-source-id: b7fcf6d38f85fe10e4e00002d3c6f2910abdbe35
1 parent 00b7956 commit 7680bde

File tree

10 files changed

+397
-27
lines changed

10 files changed

+397
-27
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow strict-local
8+
* @format
9+
* @oncall react_native
10+
*/
11+
12+
'use-strict';
13+
14+
const {parseArgs, filterJSFile} = require('../combine-utils.js');
15+
16+
describe('parseArgs', () => {
17+
const nodeBin = 'node';
18+
const combineApp = 'app';
19+
const schemaJson = 'schema.json';
20+
const specFile1 = 'NativeSpec.js';
21+
const specFile2 = 'SpecNativeComponent.js';
22+
23+
describe('when no platform provided', () => {
24+
it('returns null platform, schema and fileList', () => {
25+
const {platform, outfile, fileList} = parseArgs([
26+
nodeBin,
27+
combineApp,
28+
schemaJson,
29+
specFile1,
30+
specFile2,
31+
]);
32+
33+
expect(platform).toBeNull();
34+
expect(outfile).toBe(schemaJson);
35+
expect(fileList).toStrictEqual([specFile1, specFile2]);
36+
});
37+
});
38+
39+
describe('when platform passed with --platform', () => {
40+
it('returns the platform, the schema and the fileList', () => {
41+
const {platform, outfile, fileList} = parseArgs([
42+
nodeBin,
43+
combineApp,
44+
'--platform',
45+
'ios',
46+
schemaJson,
47+
specFile1,
48+
specFile2,
49+
]);
50+
51+
expect(platform).toBe('ios');
52+
expect(outfile).toBe(schemaJson);
53+
expect(fileList).toStrictEqual([specFile1, specFile2]);
54+
});
55+
});
56+
57+
describe('when platform passed with -p', () => {
58+
it('returns the platform, the schema and the fileList', () => {
59+
const {platform, outfile, fileList} = parseArgs([
60+
nodeBin,
61+
combineApp,
62+
'-p',
63+
'android',
64+
schemaJson,
65+
specFile1,
66+
specFile2,
67+
]);
68+
69+
expect(platform).toBe('android');
70+
expect(outfile).toBe(schemaJson);
71+
expect(fileList).toStrictEqual([specFile1, specFile2]);
72+
});
73+
});
74+
});
75+
76+
describe('filterJSFile', () => {
77+
describe('When the file is not a Spec file', () => {
78+
it('when no platform is passed, return false', () => {
79+
const file = 'anyJSFile.js';
80+
const result = filterJSFile(file);
81+
expect(result).toBeFalsy();
82+
});
83+
84+
it('when ios is passed and the file is iOS specific, return false', () => {
85+
const file = 'anyJSFile.ios.js';
86+
const result = filterJSFile(file);
87+
expect(result).toBeFalsy();
88+
});
89+
90+
it('when android is passed and the file is android specific, return false', () => {
91+
const file = 'anyJSFile.android.js';
92+
const result = filterJSFile(file);
93+
expect(result).toBeFalsy();
94+
});
95+
});
96+
97+
describe('When the file is NativeUIManager', () => {
98+
it('returns false', () => {
99+
const file = 'NativeUIManager.js';
100+
const result = filterJSFile(file);
101+
expect(result).toBeFalsy();
102+
});
103+
});
104+
105+
describe('When the file is NativeSampleTurboModule', () => {
106+
it('returns false', () => {
107+
const file = 'NativeSampleTurboModule.js';
108+
const result = filterJSFile(file);
109+
expect(result).toBeFalsy();
110+
});
111+
});
112+
113+
describe('When the file is a test file', () => {
114+
it('returns false', () => {
115+
const file = '__tests__/NativeModule-test.js';
116+
const result = filterJSFile(file);
117+
expect(result).toBeFalsy();
118+
});
119+
});
120+
121+
describe('When the file is a TS type def', () => {
122+
it('returns false', () => {
123+
const file = 'NativeModule.d.ts';
124+
const result = filterJSFile(file);
125+
expect(result).toBeFalsy();
126+
});
127+
});
128+
129+
describe('When the file is valid and it is platform agnostic', () => {
130+
const file = 'NativeModule.js';
131+
it('if the platform is null, returns true', () => {
132+
const result = filterJSFile(file);
133+
expect(result).toBeTruthy();
134+
});
135+
it('if the platform is ios, returns true', () => {
136+
const result = filterJSFile(file, 'ios');
137+
expect(result).toBeTruthy();
138+
});
139+
it('if the platform is android, returns true', () => {
140+
const result = filterJSFile(file, 'android');
141+
expect(result).toBeTruthy();
142+
});
143+
it('if the platform is windows, returns false', () => {
144+
const result = filterJSFile(file, 'windows');
145+
expect(result).toBeTruthy();
146+
});
147+
});
148+
149+
describe('When the file is valid and it is iOS specific', () => {
150+
const file = 'MySampleNativeComponent.ios.js';
151+
it('if the platform is null, returns false', () => {
152+
const result = filterJSFile(file);
153+
expect(result).toBeFalsy();
154+
});
155+
it('if the platform is ios, returns true', () => {
156+
const result = filterJSFile(file, 'ios');
157+
expect(result).toBeTruthy();
158+
});
159+
it('if the platform is android, returns false', () => {
160+
const result = filterJSFile(file, 'android');
161+
expect(result).toBeFalsy();
162+
});
163+
it('if the platform is windows, returns false', () => {
164+
const result = filterJSFile(file, 'windows');
165+
expect(result).toBeFalsy();
166+
});
167+
});
168+
169+
describe('When the file is valid and it is Android specific', () => {
170+
const file = 'MySampleNativeComponent.android.js';
171+
it('if the platform is null, returns false', () => {
172+
const result = filterJSFile(file);
173+
expect(result).toBeFalsy();
174+
});
175+
it('if the platform is ios, returns false', () => {
176+
const result = filterJSFile(file, 'ios');
177+
expect(result).toBeFalsy();
178+
});
179+
it('if the platform is android, returns true', () => {
180+
const result = filterJSFile(file, 'android');
181+
expect(result).toBeTruthy();
182+
});
183+
it('if the platform is windows, returns false', () => {
184+
const result = filterJSFile(file, 'windows');
185+
expect(result).toBeFalsy();
186+
});
187+
});
188+
189+
describe('When the file is valid and it is Windows specific', () => {
190+
const file = 'MySampleNativeComponent.windows.js';
191+
it('if the platform is null, returns false', () => {
192+
const result = filterJSFile(file);
193+
expect(result).toBeFalsy();
194+
});
195+
it('if the platform is ios, returns false', () => {
196+
const result = filterJSFile(file, 'ios');
197+
expect(result).toBeFalsy();
198+
});
199+
it('if the platform is android, returns false', () => {
200+
const result = filterJSFile(file, 'android');
201+
expect(result).toBeFalsy();
202+
});
203+
it('if the platform is windows, returns true', () => {
204+
const result = filterJSFile(file, 'windows');
205+
expect(result).toBeTruthy();
206+
});
207+
});
208+
});

packages/react-native-codegen/src/cli/combine/combine-js-to-schema-cli.js

+3-18
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,9 @@
1414
const combine = require('./combine-js-to-schema');
1515
const fs = require('fs');
1616
const glob = require('glob');
17-
const path = require('path');
17+
const {parseArgs, filterJSFile} = require('./combine-utils');
1818

19-
const [outfile, ...fileList] = process.argv.slice(2);
20-
21-
function filterJSFile(file: string) {
22-
return (
23-
/^(Native.+|.+NativeComponent)/.test(path.basename(file)) &&
24-
// NativeUIManager will be deprecated by Fabric UIManager.
25-
// For now, ignore this spec completely because the types are not fully supported.
26-
!file.endsWith('NativeUIManager.js') &&
27-
// NativeSampleTurboModule is for demo purpose. It should be added manually to the
28-
// app for now.
29-
!file.endsWith('NativeSampleTurboModule.js') &&
30-
!file.includes('__tests') &&
31-
// Ignore TypeScript type declaration files.
32-
!file.endsWith('.d.ts')
33-
);
34-
}
19+
const {platform, outfile, fileList} = parseArgs(process.argv);
3520

3621
const allFiles = [];
3722
fileList.forEach(file => {
@@ -40,7 +25,7 @@ fileList.forEach(file => {
4025
.sync(`${file}/**/*.{js,ts,tsx}`, {
4126
nodir: true,
4227
})
43-
.filter(filterJSFile);
28+
.filter(element => filterJSFile(element, platform));
4429
allFiles.push(...dirFiles);
4530
} else if (filterJSFile(file)) {
4631
allFiles.push(file);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow strict-local
8+
* @format
9+
* @oncall react_native
10+
*/
11+
12+
'use strict';
13+
14+
const path = require('path');
15+
16+
function parseArgs(args: string[]): {
17+
platform: ?string,
18+
outfile: string,
19+
fileList: string[],
20+
} {
21+
if (args.length > 2 && ['-p', '--platform'].indexOf(args[2]) >= 0) {
22+
const [outfile, ...fileList] = args.slice(4);
23+
return {
24+
platform: args[3],
25+
outfile,
26+
fileList,
27+
};
28+
}
29+
30+
const [outfile, ...fileList] = args.slice(2);
31+
return {
32+
platform: null,
33+
outfile,
34+
fileList,
35+
};
36+
}
37+
38+
/**
39+
* This function is used by the CLI to decide whether a JS/TS file has to be processed or not by the Codegen.
40+
* Parameters:
41+
* - file: the path to the file
42+
* - currentPlatform: the current platform for which we are creating the specs
43+
* Returns: `true` if the file can be used to generate some code; `false` otherwise
44+
*
45+
*/
46+
function filterJSFile(file: string, currentPlatform: ?string): boolean {
47+
const isSpecFile = /^(Native.+|.+NativeComponent)/.test(path.basename(file));
48+
const isNotNativeUIManager = !file.endsWith('NativeUIManager.js');
49+
const isNotNativeSampleTurboModule = !file.endsWith(
50+
'NativeSampleTurboModule.js',
51+
);
52+
const isNotTest = !file.includes('__tests');
53+
const isNotTSTypeDefinition = !file.endsWith('.d.ts');
54+
55+
const isValidCandidate =
56+
isSpecFile &&
57+
isNotNativeUIManager &&
58+
isNotNativeSampleTurboModule &&
59+
isNotTest &&
60+
isNotTSTypeDefinition;
61+
62+
const filenameComponents = path.basename(file).split('.');
63+
const isPlatformAgnostic = filenameComponents.length === 2;
64+
65+
if (currentPlatform == null) {
66+
// need to accept only files that are platform agnostic
67+
return isValidCandidate && isPlatformAgnostic;
68+
}
69+
70+
// If a platform is passed, accept both platform agnostic specs...
71+
if (isPlatformAgnostic) {
72+
return isValidCandidate;
73+
}
74+
75+
// ...and specs that share the same platform as the one passed.
76+
// specfiles must follow the pattern: <filename>[.<platform>].(js|ts|tsx)
77+
const filePlatform =
78+
filenameComponents.length > 2 ? filenameComponents[1] : 'unknown';
79+
return isValidCandidate && currentPlatform === filePlatform;
80+
}
81+
82+
module.exports = {
83+
parseArgs,
84+
filterJSFile,
85+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @format
8+
* @oncall react_native
9+
*/
10+
11+
'use strict';
12+
const {extractNativeModuleName} = require('../utils.js');
13+
14+
describe('extractnativeModuleName', () => {
15+
it('return filename when it ends with .js', () => {
16+
const filename = '/some_folder/NativeModule.js';
17+
const nativeModuleName = extractNativeModuleName(filename);
18+
expect(nativeModuleName).toBe('NativeModule');
19+
});
20+
it('return filename when it ends with .ts', () => {
21+
const filename = '/some_folder/NativeModule.ts';
22+
const nativeModuleName = extractNativeModuleName(filename);
23+
expect(nativeModuleName).toBe('NativeModule');
24+
});
25+
it('return filename when it ends with .tsx', () => {
26+
const filename = '/some_folder/NativeModule.tsx';
27+
const nativeModuleName = extractNativeModuleName(filename);
28+
expect(nativeModuleName).toBe('NativeModule');
29+
});
30+
it('return filename when it ends with .android.js', () => {
31+
const filename = '/some_folder/NativeModule.android.js';
32+
const nativeModuleName = extractNativeModuleName(filename);
33+
expect(nativeModuleName).toBe('NativeModule');
34+
});
35+
it('return filename when it ends with .android.ts', () => {
36+
const filename = '/some_folder/NativeModule.android.ts';
37+
const nativeModuleName = extractNativeModuleName(filename);
38+
expect(nativeModuleName).toBe('NativeModule');
39+
});
40+
it('return filename when it ends with .android.tsx', () => {
41+
const filename = '/some_folder/NativeModule.android.tsx';
42+
const nativeModuleName = extractNativeModuleName(filename);
43+
expect(nativeModuleName).toBe('NativeModule');
44+
});
45+
it('return filename when it ends with .ios.js', () => {
46+
const filename = '/some_folder/NativeModule.ios.ts';
47+
const nativeModuleName = extractNativeModuleName(filename);
48+
expect(nativeModuleName).toBe('NativeModule');
49+
});
50+
it('return filename when it ends with .ios.ts', () => {
51+
const filename = '/some_folder/NativeModule.ios.ts';
52+
const nativeModuleName = extractNativeModuleName(filename);
53+
expect(nativeModuleName).toBe('NativeModule');
54+
});
55+
it('return filename when it ends with .ios.tsx', () => {
56+
const filename = '/some_folder/NativeModule.ios.tsx';
57+
const nativeModuleName = extractNativeModuleName(filename);
58+
expect(nativeModuleName).toBe('NativeModule');
59+
});
60+
it('return filename when it ends with .windows.js', () => {
61+
const filename = '/some_folder/NativeModule.windows.js';
62+
const nativeModuleName = extractNativeModuleName(filename);
63+
expect(nativeModuleName).toBe('NativeModule');
64+
});
65+
});

0 commit comments

Comments
 (0)