Skip to content

Instantly share code, notes, and snippets.

@lelandrichardson
Last active May 12, 2017 15:58
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save lelandrichardson/c7a68950ec10576e174543413bb6e711 to your computer and use it in GitHub Desktop.
Save lelandrichardson/c7a68950ec10576e174543413bb6e711 to your computer and use it in GitHub Desktop.
Find RN Imports
const walk = require('babylon-walk');
const babylon = require('babylon');
const glob = require('glob');
const fs = require('fs');
const path = require('path');
const zip = (a, b, fn) => a.forEach((el, i) => fn(el, b[i], i));
const promisify = fn => new Promise((res, rej) => {
const done = (err, val) => (err ? rej(err) : res(val));
fn(done);
});
const getFile = fpath => promisify(cb => fs.readFile(fpath, 'utf8', cb));
const getFiles = fpath => promisify(cb => fs.readdir(fpath, cb));
const getGlobbedFiles = (g, opts) => promisify(cb => glob(g, opts, cb));
function getAST(data, file) {
let tree;
try {
tree = babylon.parse(data, {
sourceType: 'module',
plugins: [
'jsx',
'flow',
'objectRestSpread',
'exportExtensions',
'classProperties',
],
});
return tree;
} catch (e) {
console.error(file, e);
return null;
}
}
function getReactNativeImports(tree, collector) {
walk.simple(tree, {
ImportDeclaration: (node) => {
if (node.source.value === 'react-native') {
const names = node.specifiers
.filter(spec => spec.type === 'ImportSpecifier')
.map(spec => spec.imported.name);
collector.push(...names);
}
},
});
return collector;
}
function getReactNativeImportsFromPath(file, collector) {
return getFile(file)
.then(str => getAST(str, file))
.then(tree => getReactNativeImports(tree, collector));
}
const results = [];
getGlobbedFiles(process.argv[2], { dot: false, absolute: true })
.then(files => Promise.all(files.map(f => getReactNativeImportsFromPath(f, results))))
.then(() => {
const amounts = {};
results.forEach(name => {
if (!amounts[name]) {
amounts[name] = 0;
}
amounts[name] += 1;
});
process.stdout.write(`name\tcount\n`);
Object.keys(amounts).forEach(key => {
process.stdout.write(`${key}\t${amounts[key]}\n`);
});
});

To run this, you will first need to install the following npm packages:

npm i babylon babylon-walk glob

Then, with the script in the current directory in find-rn-imports.js, you would want to run it, passing in a glob pattern to hit all of the source code files in your repo. For instance I ran something like:

node ./find-rn-imports.js '!(node_modules|test)/**/*.js'

The output of that script will indicate which react native APIs you are importing and how many times in your code base (though this assumes you are using import and not require).

@CharlesMangwa
Copy link

CharlesMangwa commented Apr 30, 2017

So here are my outputs:

This one is from a recent app I've just started.

name	                    count
StatusBar	            5
View	                    30
Alert	                    3
StyleSheet	            27
Animated	            1
ScrollView	            1
I18nManager                 1
Platform	            10
Text	                    17
Image                       7
Dimensions                  2
TouchableNativeFeedback     1
TouchableOpacity            1
AsyncStorage	            1

And this one is from an older one.

name	                    count
StatusBar	            5
Text	                    21
View	                    24
StyleSheet	            28
TouchableWithoutFeedback    9
Alert	                    7
RefreshControl	            1
Perf	                    2
RenderingPerf	            2
Platform	            13
ScrollView	            5
ActivityIndicator	    2
Dimensions	            7
Image	                    6
Keyboard	            4
KeyboardAvoidingView	    2
TextInput	            2
NetInfo	                    1
Linking	                    2
Share	                    1
TouchableOpacity	    3
Animated	            1
TouchableNativeFeedback     1
NavigationExperimental      1
AsyncStorage                1

Hope it helps you!

@FiberJW
Copy link

FiberJW commented Apr 30, 2017

Output for https://github.com/datwheat/pul

name	count
AsyncStorage	7
Alert	5
View	29
Text	17
Dimensions	8
StyleSheet	34
TouchableOpacity	18
Platform	8
StatusBar	8
Linking	1
Image	6
Vibration	2
TextInput	6
ScrollView	3
LayoutAnimation	3
FlatList	6
Share	1
ActivityIndicator	9
Keyboard	5
Animated	1
Switch	1
WebView	1
AppState	1

@manosim
Copy link

manosim commented Apr 30, 2017

Results from a project - hope it helps!

name	count
BackAndroid	2
Alert	21
Linking	5
Keyboard	2
View	60
Navigator	3
Platform	8
StyleSheet	63
ScrollView	9
Text	48
TouchableOpacity	16
ActivityIndicator	2
TouchableHighlight	10
Image	14
Dimensions	7
TouchableWithoutFeedback	1
Slider	1
TextInput	5
ListView	2
Modal	1

@brunolemos
Copy link

I got an error:

brunolemos@brunolemos-macpro devhub (master) $ node ./find-rn-imports.js '!(node_modules|test)/**/*.js'
net.js:10
const cares = process.binding('cares_wrap');
                      ^

Error: EFILE
    at net.js:10:23
    at NativeModule.compile (bootstrap_node.js:526:7)
    at NativeModule.require (bootstrap_node.js:467:18)
    at tty.js:4:13
    at NativeModule.compile (bootstrap_node.js:526:7)
    at NativeModule.require (bootstrap_node.js:467:18)
    at createWritableStdioStream (internal/process/stdio.js:142:19)
    at process.getStdout [as stdout] (internal/process/stdio.js:10:14)
    at console.js:100:37
    at NativeModule.compile (bootstrap_node.js:526:7)
brunolemos@brunolemos-macpro devhub (master) $ 

@lelandrichardson
Copy link
Author

@brunolemos that's a strange error. It looks like something in node is crashing trying to log to stdout?

@iZaL
Copy link

iZaL commented Apr 30, 2017

output for https://github.com/iZaL/real-estate-app-ui

name    count
View    57
Animated        8
Dimensions      16
Easing  2
StyleSheet      57
Text    42
TouchableHighlight      36
StatusBar       2
Image   15
TextInput       7
AsyncStorage    2
ActivityIndicator       1
Alert   4
ListView        5
KeyboardAvoidingView    1
Picker  1
ScrollView      7
Modal   1
TouchableWithoutFeedback        2
FlatList        3
ActionSheetIOS  1
Linking 2
SwipeableListView       1

@lelandrichardson
Copy link
Author

In the interest of sharing, i'll also post the results for the airbnb codebase if anyone is interested in how they compare!

image

@lelandrichardson
Copy link
Author

If you thought the numbers might be bigger... it's because most of our code actually doesn't pull in RN directly, but instead is using components from our UI component library! (these counts do include imports from inside the component library though)

@javiercr
Copy link

That's super interesting! Thanks for sharing @lelandrichardson!

Here are our numbers (we've just implemented around 60% of the planned features):

screen shot 2017-05-01 at 00 40 48

@mini-eggs
Copy link

mini-eggs commented Apr 30, 2017

The script doesn't work with legacy decorators, here's what it picked up from the non-decorated files
InteractionManager 1
AsyncStorage 3
NativeModules 1
Platform 7
Dimensions 5
View 3
Animated 4
Easing 1
BackAndroid 1
PixelRatio 1

With decorators working (thanks @ lelandrichardson!):

name    count
InteractionManager      1
AsyncStorage    3
NativeModules   1
Platform        8
View    5
Modal   3
Dimensions      7
Animated        6
Easing  1
StatusBar       1
BackAndroid     1
Alert   1
PixelRatio      1

I have some abstractions over the Animated API, feel like these numbers don't show my appreciation for the API

@lelandrichardson
Copy link
Author

@mini-eggs you can add decorator support by adding 'decorators' to the list of plugins in the babylon.parse call

@dabit3
Copy link

dabit3 commented Apr 30, 2017

name	count
View	59
StyleSheet	35
Platform	20
Image	12
Dimensions	15
TouchableOpacity	7
Text	56
TouchableWithoutFeedback	9
ScrollView	6
Navigator	4
TouchableHighlight	8
Animated	5
Easing	3
StatusBar	3
TextInput	3
TouchableNativeFeedback	5
ActivityIndicator	5
DatePickerAndroid	1
DatePickerIOS	1
Keyboard	4
TimePickerAndroid	1
Picker	1
Switch	1
Modal	1
ListView	10
WebView	2
PanResponder	1
Alert	5
Linking	2
CameraRoll	3
InteractionManager	7
KeyboardAvoidingView	3

@justinobney
Copy link

@lelandrichardson curious what airbnb is using for routing?

@bleonard
Copy link

bleonard commented May 1, 2017

name count
View 129
TouchableWithoutFeedback 7
Image 9
PixelRatio 46
NativeModules 11
Vibration 2
TouchableOpacity 16
StyleSheet 114
PanResponder 5
Animated 6
Easing 4
Text 8
ListView 9
Dimensions 22
Keyboard 8
Platform 16
StatusBar 3
InteractionManager 2
Modal 1
ScrollView 16
Linking 7
ActivityIndicator 4
Switch 1
TextInput 1
Clipboard 6
TouchableHighlight 2
AppState 2
Navigator 1
Alert 1
AlertIOS 1
PushNotificationIOS 2
BackAndroid 2
RefreshControl 2
WebView 4
findNodeHandle 1
Picker 3
Share 1
AsyncStorage 1
ActionSheetIOS 1

@bleonard
Copy link

bleonard commented May 1, 2017

We wrap many of the components, so I assume this wouldn't catch them. For example we have out own <Text> that sets various defaults and such on the built-in one.
The platform stuff as well is wrapped with one file included in android and one in iOS. So there are several alerts and/or action sheets, but we only include it in Platform/ActionSheet.ios.js similar to the sample project

@sahrens
Copy link

sahrens commented May 1, 2017

@mini-eggs - what kind of abstractions around Animated have you found valuable?

@bmeck
Copy link

bmeck commented May 1, 2017

from an isomorphic UI lib in use in several apps:

name	count
View	11
TouchableWithoutFeedback	4
Text	12
StyleSheet	7
Platform	3
TouchableOpacity	1
TouchableHighlight	1
Modal	1
TextInput	1
NativeModules	1
Switch	2

@lelandrichardson
Copy link
Author

@justinobney there's a lot more info about that here: https://www.youtube.com/watch?v=tWitQoPgs8w

@fabriziomoscon
Copy link

my list

name	count
NativeModules	1
NativeEventEmitter	1
Platform	20
StyleSheet	68
Text	64
TextInput	8
View	68
Animated	2
Easing	2
Image	16
ActivityIndicator	14
Dimensions	3
StatusBar	3
TouchableOpacity	20
TouchableNativeFeedback	12
NavigationExperimental	4
Alert	10
Linking	1
ScrollView	16
Modal	4
ListView	2
RefreshControl	2
Switch	1
AppState	3
Share	2
Picker	1
NetInfo	1
WebView	1
AsyncStorage	2

@timbielawski
Copy link

I got

name count
View 98
Text 87
Linking 4
TouchableOpacity 46
Alert 5
Animated 18
ListView 3
StyleSheet 88
TextInput 8
TouchableWithoutFeedback 6
PanResponder 2
AppRegistry 1
Image 32
ScrollView 35
Dimensions 5
TouchableHighlight 6
LayoutAnimation 7
ActivityIndicator 32
Platform 29
ToastAndroid 2
TouchableNativeFeedback 1
BackAndroid 2
PermissionsAndroid 1
AlertIOS 1
Picker 1
AsyncStorage 2
InteractionManager 10
KeyboardAvoidingView 2
RecyclerViewBackedScrollView 2
Modal 3
Keyboard 1
WebView 1
Slider 1
StatusBar 2
AppState 1
NetInfo 1
Navigator 1

@tijs
Copy link

tijs commented May 2, 2017

For a recent RN project of ours:

name count
Alert 16
Platform 20
Text 47
View 65
StyleSheet 62
TextInput 7
Dimensions 31
TouchableOpacity 28
Image 30
Animated 14
TouchableHighlight 10
TouchableWithoutFeedback 9
StatusBar 5
Easing 9
ScrollView 6
ListView 7
UIManager 5
LayoutAnimation 5
PermissionsAndroid 1
Linking 8
NetInfo 1
AppState 3
NativeModules 4
InteractionManager 2
RefreshControl 2
ActionSheetIOS 1
Navigator 1
AsyncStorage 1

@kelset
Copy link

kelset commented May 3, 2017

Output for our production app:
screen shot 2017-05-03 at 15 00 07

@sahrens
Copy link

sahrens commented May 8, 2017

I had to break up the Promise.all into multiple batches to get it to finish, but here are our numbers:

 { globFiles: 11540, filesProcessed: 11540 }
name	count
StyleSheet	3110
View	3045
Text	1507
Image	962
Platform	725
NativeModules	543
TouchableOpacity	431
TouchableHighlight	314
ScrollView	284
Dimensions	200
ListView	184
TouchableWithoutFeedback	181
Animated	163
PixelRatio	153
TextInput	137
Alert	89
LayoutAnimation	87
Modal	81
Linking	80
AppRegistry	67
Navigator	53
UIManager	44
ActivityIndicator	34
StatusBar	34
AsyncStorage	32
AlertIOS	31
TouchableNativeFeedback	31
FlatList	28
KeyboardAvoidingView	28
Keyboard	28
WebView	26
Easing	26
Switch	25
PanResponder	22
ToastAndroid	19
ActionSheetIOS	17
InteractionManager	15
RefreshControl	15
AppState	15
SectionList	14
Clipboard	14
PushNotificationIOS	12
Picker	12
PermissionsAndroid	9
NetInfo	7
NativeEventEmitter	7
Slider	4
RecyclerViewBackedScrollView	2

@sahrens
Copy link

sahrens commented May 8, 2017

I also had to change the matcher to handle our requires since we still haven't switched, so I used a whitelist on the module names, so might have missed some.

@sahrens
Copy link

sahrens commented May 12, 2017

Also note with our numbers up there we have some different internal implementations, e.g. for Image.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment