-
Notifications
You must be signed in to change notification settings - Fork 24.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
- Loading branch information
1 parent
00b7956
commit 7680bde
Showing
10 changed files
with
397 additions
and
27 deletions.
There are no files selected for viewing
208 changes: 208 additions & 0 deletions
208
packages/react-native-codegen/src/cli/combine/__tests__/combine-utils-test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,208 @@ | ||
/** | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
* | ||
* @flow strict-local | ||
* @format | ||
* @oncall react_native | ||
*/ | ||
|
||
'use-strict'; | ||
|
||
const {parseArgs, filterJSFile} = require('../combine-utils.js'); | ||
|
||
describe('parseArgs', () => { | ||
const nodeBin = 'node'; | ||
const combineApp = 'app'; | ||
const schemaJson = 'schema.json'; | ||
const specFile1 = 'NativeSpec.js'; | ||
const specFile2 = 'SpecNativeComponent.js'; | ||
|
||
describe('when no platform provided', () => { | ||
it('returns null platform, schema and fileList', () => { | ||
const {platform, outfile, fileList} = parseArgs([ | ||
nodeBin, | ||
combineApp, | ||
schemaJson, | ||
specFile1, | ||
specFile2, | ||
]); | ||
|
||
expect(platform).toBeNull(); | ||
expect(outfile).toBe(schemaJson); | ||
expect(fileList).toStrictEqual([specFile1, specFile2]); | ||
}); | ||
}); | ||
|
||
describe('when platform passed with --platform', () => { | ||
it('returns the platform, the schema and the fileList', () => { | ||
const {platform, outfile, fileList} = parseArgs([ | ||
nodeBin, | ||
combineApp, | ||
'--platform', | ||
'ios', | ||
schemaJson, | ||
specFile1, | ||
specFile2, | ||
]); | ||
|
||
expect(platform).toBe('ios'); | ||
expect(outfile).toBe(schemaJson); | ||
expect(fileList).toStrictEqual([specFile1, specFile2]); | ||
}); | ||
}); | ||
|
||
describe('when platform passed with -p', () => { | ||
it('returns the platform, the schema and the fileList', () => { | ||
const {platform, outfile, fileList} = parseArgs([ | ||
nodeBin, | ||
combineApp, | ||
'-p', | ||
'android', | ||
schemaJson, | ||
specFile1, | ||
specFile2, | ||
]); | ||
|
||
expect(platform).toBe('android'); | ||
expect(outfile).toBe(schemaJson); | ||
expect(fileList).toStrictEqual([specFile1, specFile2]); | ||
}); | ||
}); | ||
}); | ||
|
||
describe('filterJSFile', () => { | ||
describe('When the file is not a Spec file', () => { | ||
it('when no platform is passed, return false', () => { | ||
const file = 'anyJSFile.js'; | ||
const result = filterJSFile(file); | ||
expect(result).toBeFalsy(); | ||
}); | ||
|
||
it('when ios is passed and the file is iOS specific, return false', () => { | ||
const file = 'anyJSFile.ios.js'; | ||
const result = filterJSFile(file); | ||
expect(result).toBeFalsy(); | ||
}); | ||
|
||
it('when android is passed and the file is android specific, return false', () => { | ||
const file = 'anyJSFile.android.js'; | ||
const result = filterJSFile(file); | ||
expect(result).toBeFalsy(); | ||
}); | ||
}); | ||
|
||
describe('When the file is NativeUIManager', () => { | ||
it('returns false', () => { | ||
const file = 'NativeUIManager.js'; | ||
const result = filterJSFile(file); | ||
expect(result).toBeFalsy(); | ||
}); | ||
}); | ||
|
||
describe('When the file is NativeSampleTurboModule', () => { | ||
it('returns false', () => { | ||
const file = 'NativeSampleTurboModule.js'; | ||
const result = filterJSFile(file); | ||
expect(result).toBeFalsy(); | ||
}); | ||
}); | ||
|
||
describe('When the file is a test file', () => { | ||
it('returns false', () => { | ||
const file = '__tests__/NativeModule-test.js'; | ||
const result = filterJSFile(file); | ||
expect(result).toBeFalsy(); | ||
}); | ||
}); | ||
|
||
describe('When the file is a TS type def', () => { | ||
it('returns false', () => { | ||
const file = 'NativeModule.d.ts'; | ||
const result = filterJSFile(file); | ||
expect(result).toBeFalsy(); | ||
}); | ||
}); | ||
|
||
describe('When the file is valid and it is platform agnostic', () => { | ||
const file = 'NativeModule.js'; | ||
it('if the platform is null, returns true', () => { | ||
const result = filterJSFile(file); | ||
expect(result).toBeTruthy(); | ||
}); | ||
it('if the platform is ios, returns true', () => { | ||
const result = filterJSFile(file, 'ios'); | ||
expect(result).toBeTruthy(); | ||
}); | ||
it('if the platform is android, returns true', () => { | ||
const result = filterJSFile(file, 'android'); | ||
expect(result).toBeTruthy(); | ||
}); | ||
it('if the platform is windows, returns false', () => { | ||
const result = filterJSFile(file, 'windows'); | ||
expect(result).toBeTruthy(); | ||
}); | ||
}); | ||
|
||
describe('When the file is valid and it is iOS specific', () => { | ||
const file = 'MySampleNativeComponent.ios.js'; | ||
it('if the platform is null, returns false', () => { | ||
const result = filterJSFile(file); | ||
expect(result).toBeFalsy(); | ||
}); | ||
it('if the platform is ios, returns true', () => { | ||
const result = filterJSFile(file, 'ios'); | ||
expect(result).toBeTruthy(); | ||
}); | ||
it('if the platform is android, returns false', () => { | ||
const result = filterJSFile(file, 'android'); | ||
expect(result).toBeFalsy(); | ||
}); | ||
it('if the platform is windows, returns false', () => { | ||
const result = filterJSFile(file, 'windows'); | ||
expect(result).toBeFalsy(); | ||
}); | ||
}); | ||
|
||
describe('When the file is valid and it is Android specific', () => { | ||
const file = 'MySampleNativeComponent.android.js'; | ||
it('if the platform is null, returns false', () => { | ||
const result = filterJSFile(file); | ||
expect(result).toBeFalsy(); | ||
}); | ||
it('if the platform is ios, returns false', () => { | ||
const result = filterJSFile(file, 'ios'); | ||
expect(result).toBeFalsy(); | ||
}); | ||
it('if the platform is android, returns true', () => { | ||
const result = filterJSFile(file, 'android'); | ||
expect(result).toBeTruthy(); | ||
}); | ||
it('if the platform is windows, returns false', () => { | ||
const result = filterJSFile(file, 'windows'); | ||
expect(result).toBeFalsy(); | ||
}); | ||
}); | ||
|
||
describe('When the file is valid and it is Windows specific', () => { | ||
const file = 'MySampleNativeComponent.windows.js'; | ||
it('if the platform is null, returns false', () => { | ||
const result = filterJSFile(file); | ||
expect(result).toBeFalsy(); | ||
}); | ||
it('if the platform is ios, returns false', () => { | ||
const result = filterJSFile(file, 'ios'); | ||
expect(result).toBeFalsy(); | ||
}); | ||
it('if the platform is android, returns false', () => { | ||
const result = filterJSFile(file, 'android'); | ||
expect(result).toBeFalsy(); | ||
}); | ||
it('if the platform is windows, returns true', () => { | ||
const result = filterJSFile(file, 'windows'); | ||
expect(result).toBeTruthy(); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
85 changes: 85 additions & 0 deletions
85
packages/react-native-codegen/src/cli/combine/combine-utils.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
/** | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
* | ||
* @flow strict-local | ||
* @format | ||
* @oncall react_native | ||
*/ | ||
|
||
'use strict'; | ||
|
||
const path = require('path'); | ||
|
||
function parseArgs(args: string[]): { | ||
platform: ?string, | ||
outfile: string, | ||
fileList: string[], | ||
} { | ||
if (args.length > 2 && ['-p', '--platform'].indexOf(args[2]) >= 0) { | ||
const [outfile, ...fileList] = args.slice(4); | ||
return { | ||
platform: args[3], | ||
outfile, | ||
fileList, | ||
}; | ||
} | ||
|
||
const [outfile, ...fileList] = args.slice(2); | ||
return { | ||
platform: null, | ||
outfile, | ||
fileList, | ||
}; | ||
} | ||
|
||
/** | ||
* This function is used by the CLI to decide whether a JS/TS file has to be processed or not by the Codegen. | ||
* Parameters: | ||
* - file: the path to the file | ||
* - currentPlatform: the current platform for which we are creating the specs | ||
* Returns: `true` if the file can be used to generate some code; `false` otherwise | ||
* | ||
*/ | ||
function filterJSFile(file: string, currentPlatform: ?string): boolean { | ||
const isSpecFile = /^(Native.+|.+NativeComponent)/.test(path.basename(file)); | ||
const isNotNativeUIManager = !file.endsWith('NativeUIManager.js'); | ||
const isNotNativeSampleTurboModule = !file.endsWith( | ||
'NativeSampleTurboModule.js', | ||
); | ||
const isNotTest = !file.includes('__tests'); | ||
const isNotTSTypeDefinition = !file.endsWith('.d.ts'); | ||
|
||
const isValidCandidate = | ||
isSpecFile && | ||
isNotNativeUIManager && | ||
isNotNativeSampleTurboModule && | ||
isNotTest && | ||
isNotTSTypeDefinition; | ||
|
||
const filenameComponents = path.basename(file).split('.'); | ||
const isPlatformAgnostic = filenameComponents.length === 2; | ||
|
||
if (currentPlatform == null) { | ||
// need to accept only files that are platform agnostic | ||
return isValidCandidate && isPlatformAgnostic; | ||
} | ||
|
||
// If a platform is passed, accept both platform agnostic specs... | ||
if (isPlatformAgnostic) { | ||
return isValidCandidate; | ||
} | ||
|
||
// ...and specs that share the same platform as the one passed. | ||
// specfiles must follow the pattern: <filename>[.<platform>].(js|ts|tsx) | ||
const filePlatform = | ||
filenameComponents.length > 2 ? filenameComponents[1] : 'unknown'; | ||
return isValidCandidate && currentPlatform === filePlatform; | ||
} | ||
|
||
module.exports = { | ||
parseArgs, | ||
filterJSFile, | ||
}; |
65 changes: 65 additions & 0 deletions
65
packages/react-native-codegen/src/parsers/__tests__/utils-test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
/** | ||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
* | ||
* @format | ||
* @oncall react_native | ||
*/ | ||
|
||
'use strict'; | ||
const {extractNativeModuleName} = require('../utils.js'); | ||
|
||
describe('extractnativeModuleName', () => { | ||
it('return filename when it ends with .js', () => { | ||
const filename = '/some_folder/NativeModule.js'; | ||
const nativeModuleName = extractNativeModuleName(filename); | ||
expect(nativeModuleName).toBe('NativeModule'); | ||
}); | ||
it('return filename when it ends with .ts', () => { | ||
const filename = '/some_folder/NativeModule.ts'; | ||
const nativeModuleName = extractNativeModuleName(filename); | ||
expect(nativeModuleName).toBe('NativeModule'); | ||
}); | ||
it('return filename when it ends with .tsx', () => { | ||
const filename = '/some_folder/NativeModule.tsx'; | ||
const nativeModuleName = extractNativeModuleName(filename); | ||
expect(nativeModuleName).toBe('NativeModule'); | ||
}); | ||
it('return filename when it ends with .android.js', () => { | ||
const filename = '/some_folder/NativeModule.android.js'; | ||
const nativeModuleName = extractNativeModuleName(filename); | ||
expect(nativeModuleName).toBe('NativeModule'); | ||
}); | ||
it('return filename when it ends with .android.ts', () => { | ||
const filename = '/some_folder/NativeModule.android.ts'; | ||
const nativeModuleName = extractNativeModuleName(filename); | ||
expect(nativeModuleName).toBe('NativeModule'); | ||
}); | ||
it('return filename when it ends with .android.tsx', () => { | ||
const filename = '/some_folder/NativeModule.android.tsx'; | ||
const nativeModuleName = extractNativeModuleName(filename); | ||
expect(nativeModuleName).toBe('NativeModule'); | ||
}); | ||
it('return filename when it ends with .ios.js', () => { | ||
const filename = '/some_folder/NativeModule.ios.ts'; | ||
const nativeModuleName = extractNativeModuleName(filename); | ||
expect(nativeModuleName).toBe('NativeModule'); | ||
}); | ||
it('return filename when it ends with .ios.ts', () => { | ||
const filename = '/some_folder/NativeModule.ios.ts'; | ||
const nativeModuleName = extractNativeModuleName(filename); | ||
expect(nativeModuleName).toBe('NativeModule'); | ||
}); | ||
it('return filename when it ends with .ios.tsx', () => { | ||
const filename = '/some_folder/NativeModule.ios.tsx'; | ||
const nativeModuleName = extractNativeModuleName(filename); | ||
expect(nativeModuleName).toBe('NativeModule'); | ||
}); | ||
it('return filename when it ends with .windows.js', () => { | ||
const filename = '/some_folder/NativeModule.windows.js'; | ||
const nativeModuleName = extractNativeModuleName(filename); | ||
expect(nativeModuleName).toBe('NativeModule'); | ||
}); | ||
}); |
Oops, something went wrong.