Analyzing JavaScript React Native: the Zooniverse case study

Zooniverse is a JavaScript / React/ React Native system. We first clone it:

rootDirectory := 'zooniverse' asFileReference.
rootDirectory ensureDeleteAll.
repository := IceRepositoryCreator
		fromUrl: ''
		to: rootDirectory

Reasoning About Package Dependencies

Let's start with a simple question: How many dependencies does this project have? And how many do not point to react-native libraries?

The answers can be found in package.json:

packageJson := rootDirectory / 'package.json'

Of course, you can find the answer by reading, but you are here to learn how not to read. So, how do we do it without reading? Well, JSON happens to be a reasonably structured piece of data. So, let's parse it:

dictionary := STONJSON fromString: packageJson contents.
dependencies := dictionary at: #dependencies

So, how many dependencies are there? It's a simple query:

dependencies size

What about the dependencies that do not point to react-native libraries? Another query:

dependencies associations
	reject: [ :each | each key includesSubstring: 'react-native' ]

This is a rather small problem. Yet, even here we can outcompete reading by means of custom tools.

Reasoning About React Dependencies

A more complicated problem is that of reasoning about dependencies between React components. For example, are there cycles between components?

To answer this question, we create a model:

model := GtReactImporter new
	parserClass: JSXParser;
	importDirectory: rootDirectory;

Now, inspect the components and pick the Dependencies view:

model allReactComponents

In this case, to answer the question effectively, we need an analysis at the level of React. To this end, we created a custom importer that understands React patterns (specifically, component definitions and dependencies), extended the base model with React specific entities, and created a custom visualization.