All Projects → moroshko → React Scanner

moroshko / React Scanner

Licence: mit
Extract React components and props usage from code.

Programming Languages

javascript
184084 projects - #8 most used programming language

Projects that are alternatives of or similar to React Scanner

Redux React Starter
DEPRECATED use the new https://github.com/didierfranc/react-webpack-4
Stars: ✭ 137 (-22.16%)
Mutual labels:  components, jsx
Lucid
A UI component library from AppNexus.
Stars: ✭ 171 (-2.84%)
Mutual labels:  components, jsx
Crank
Write JSX-driven components with functions, promises and generators.
Stars: ✭ 2,487 (+1313.07%)
Mutual labels:  components, jsx
Bloomer
A set of React components for Bulma CSS Framework
Stars: ✭ 683 (+288.07%)
Mutual labels:  components, jsx
Kit
Tools for developing, documenting, and testing React component libraries
Stars: ✭ 1,201 (+582.39%)
Mutual labels:  components, jsx
Preact
⚛️ Fast 3kB React alternative with the same modern API. Components & Virtual DOM.
Stars: ✭ 30,527 (+17244.89%)
Mutual labels:  components, jsx
hsx
Static HTML sites with JSX and webpack (no React).
Stars: ✭ 15 (-91.48%)
Mutual labels:  components, jsx
React Workshop
⚒ 🚧 This is a workshop for learning how to build React Applications
Stars: ✭ 114 (-35.23%)
Mutual labels:  components, jsx
X0
Document & develop React components without breaking a sweat
Stars: ✭ 1,706 (+869.32%)
Mutual labels:  components, jsx
Html To React Components
Converts HTML pages into React components
Stars: ✭ 2,031 (+1053.98%)
Mutual labels:  jsx
Babel Plugin React Html Attrs
Babel plugin which transforms HTML and SVG attributes on JSX host elements into React-compatible attributes
Stars: ✭ 170 (-3.41%)
Mutual labels:  jsx
Terra Core
Terra offers a set of configurable React components designed to help build scalable and modular application UIs. This UI library was created to solve real-world issues in projects we work on day to day.
Stars: ✭ 167 (-5.11%)
Mutual labels:  components
Vue Design System
An open source tool for building UI Design Systems with Vue.js
Stars: ✭ 2,077 (+1080.11%)
Mutual labels:  components
Semantic Ui React
The official Semantic-UI-React integration
Stars: ✭ 12,561 (+7036.93%)
Mutual labels:  components
Preact Markup
⚡️ Render HTML5 as VDOM, with Components as Custom Elements!
Stars: ✭ 167 (-5.11%)
Mutual labels:  jsx
Zent
A collection of essential UI components written with React.
Stars: ✭ 2,133 (+1111.93%)
Mutual labels:  components
Opencensus Web
A stats collection and distributed tracing framework
Stars: ✭ 168 (-4.55%)
Mutual labels:  stats
Snap Shot
Jest-like snapshot feature for the rest of us, works magically by finding the right caller function
Stars: ✭ 170 (-3.41%)
Mutual labels:  jsx
React Messenger
Chat UX components built with React, inspired by Facebook Messenger
Stars: ✭ 167 (-5.11%)
Mutual labels:  components
Terraform Aws Components
Opinionated, self-contained Terraform root modules that each solve one, specific problem
Stars: ✭ 168 (-4.55%)
Mutual labels:  components

CI

react-scanner

react-scanner statically analyzes the given code (TypeScript supported) and extracts React components and props usage.

First, it crawls the given directory and compiles a list of files to be scanned. Then, it scans every file and extracts rendered components and their props into a JSON report.

For example, let's say we have the following index.js file:

import React from "react";
import ReactDOM from "react-dom";
import {
  BasisProvider,
  defaultTheme,
  Container,
  Text,
  Link as BasisLink,
} from "basis";

function App() {
  return (
    <BasisProvider theme={defaultTheme}>
      <Container margin="4" hasBreakpointWidth>
        <Text textStyle="subtitle2">
          Want to know how your design system components are being used?
        </Text>
        <Text margin="4 0 0 0">
          Try{" "}
          <BasisLink href="https://github.com/moroshko/react-scanner" newTab>
            react-scanner
          </BasisLink>
        </Text>
      </Container>
    </BasisProvider>
  );
}

ReactDOM.render(<App />, document.getElementById("root"));

Running react-scanner on it will create the following JSON report:

Click to see it
{
  "BasisProvider": {
    "instances": [
      {
        "importInfo": {
          "imported": "BasisProvider",
          "local": "BasisProvider",
          "moduleName": "basis"
        },
        "props": {
          "theme": "(Identifier)"
        },
        "propsSpread": false,
        "location": {
          "file": "/path/to/index.js",
          "start": {
            "line": 13,
            "column": 5
          }
        }
      }
    ]
  },
  "Container": {
    "instances": [
      {
        "importInfo": {
          "imported": "Container",
          "local": "Container",
          "moduleName": "basis"
        },
        "props": {
          "margin": "4",
          "hasBreakpointWidth": null
        },
        "propsSpread": false,
        "location": {
          "file": "/path/to/index.js",
          "start": {
            "line": 14,
            "column": 7
          }
        }
      }
    ]
  },
  "Text": {
    "instances": [
      {
        "importInfo": {
          "imported": "Text",
          "local": "Text",
          "moduleName": "basis"
        },
        "props": {
          "textStyle": "subtitle2"
        },
        "propsSpread": false,
        "location": {
          "file": "/path/to/index.js",
          "start": {
            "line": 15,
            "column": 9
          }
        }
      },
      {
        "importInfo": {
          "imported": "Text",
          "local": "Text",
          "moduleName": "basis"
        },
        "props": {
          "margin": "4 0 0 0"
        },
        "propsSpread": false,
        "location": {
          "file": "/path/to/index.js",
          "start": {
            "line": 18,
            "column": 9
          }
        }
      }
    ]
  },
  "Link": {
    "instances": [
      {
        "importInfo": {
          "imported": "Link",
          "local": "BasisLink",
          "moduleName": "basis"
        },
        "props": {
          "href": "https://github.com/moroshko/react-scanner",
          "newTab": null
        },
        "propsSpread": false,
        "location": {
          "file": "/path/to/index.js",
          "start": {
            "line": 20,
            "column": 11
          }
        }
      }
    ]
  }
}

This raw JSON report is used then to generate something that is useful to you. For example, you might want to know:

  • How often a cetrain component is used in your design system? (see count-components processor)
  • How often a certain prop in a given component is used? (see count-components-and-props processor)
  • Looking at some prop in a given component, what's the distribution of values used? (e.g. you might consider deprecating a certain value)

Once you have the result you are interested in, you can write it to a file or simply log it to the console.

Installation

npm install --save-dev react-scanner

Usage

npx react-scanner -c /path/to/react-scanner.config.js

Config file

Everything that react-scanner does is controlled by a config file.

The config file can be located anywhere and it must export an object like this:

module.exports = {
  crawlFrom: "./src",
  includeSubComponents: true,
  importedFrom: "basis",
};

Running react-scanner with this config would output something like this to the console:

{
  "Text": {
    "instances": 17,
    "props": {
      "margin": 6,
      "color": 4,
      "textStyle": 1
    }
  },
  "Button": {
    "instances": 10,
    "props": {
      "width": 10,
      "variant": 5,
      "type": 3
    }
  },
  "Footer": {
    "instances": 1,
    "props": {}
  }
}

Here are all the available config options:

Option Type Description
crawlFrom string The path of the directory to start crawling from.
Absolute or relative to the config file location.
exclude array or function Each array item should be a string or a regex. When crawling, if directory name matches exactly the string item or matches the regex item, it will be excluded from crawling.
For more complex scenarios, exclude can be a a function that accepts a directory name and should return true if the directory should be excluded from crawling.
globs array Only files matching these globs will be scanned. See here for glob syntax.
Default: ["**/!(*.test|*.spec)[email protected](js|ts)?(x)"]
components object Components to report. Omit to report all components.
includeSubComponents boolean Whether to report subcomponents or not.
When false, Footer will be reported, but Footer.Content will not.
When true, Footer.Content will be reported, as well as Footer.Content.Legal, etc.
Default: false
importedFrom string or regex Before reporting a component, we'll check if it's imported from a module name matching importedFrom and, only if there is a match, the component will be reported.
When omitted, this check is bypassed.
getComponentName function This function is called to determine the component name to be used in the report based on the import declaration.
Default: ({ imported, local, moduleName, importType }) => imported || local
processors array See Processors.
Default: ["count-components-and-props"]

Processors

Scanning the files results in a JSON report. Add processors to tell react-scanner what to do with this report.

Built-in processors

react-scanner comes with some ready to use processors.

To use a built-in processor, simply specify its name as a string, e.g.:

processors: ["count-components"]

You can also use a tuple form to pass options to a built-in processor, e.g.:

processors: [
  ["count-components", { outputTo: "/path/to/my-report.json" }]
]

All the built-in processors support the following options:

Option Type Description
outputTo string Where to output the result.
Absolute or relative to the config file location.
When omitted, the result is printed out to the console.

Here are the built-in processors that react-scanner comes with:

count-components

Example output:

{
  "Text": 10,
  "Button": 5,
  "Link": 3
}

count-components-and-props

Example output:

{
  "Text": {
    "instances": 17,
    "props": {
      "margin": 6,
      "color": 4,
      "textStyle": 1
    }
  },
  "Button": {
    "instances": 10,
    "props": {
      "width": 10,
      "variant": 4,
      "type": 2
    }
  },
  "Footer": {
    "instances": 1,
    "props": {}
  }
}

raw-report

Example output:

{
  "Text": {
    "instances": [
      {
        "props": {
          "textStyle": "subtitle2"
        },
        "propsSpread": false,
        "location": {
          "file": "/path/to/file",
          "start": {
            "line": 9,
            "column": 9
          }
        }
      },
      {
        "props": {
          "margin": "4 0 0 0"
        },
        "propsSpread": false,
        "location": {
          "file": "/path/to/file",
          "start": {
            "line": 12,
            "column": 9
          }
        }
      }
    ]
  },
  "Link": {
    "instances": [
      {
        "props": {
          "href": "https://github.com/moroshko/react-scanner",
          "newTab": null
        },
        "propsSpread": false,
        "location": {
          "file": "/path/to/file",
          "start": {
            "line": 14,
            "column": 11
          }
        }
      }
    ]
  },
  "Container": {
    "instances": [
      {
        "props": {
          "margin": "4",
          "hasBreakpointWidth": null
        },
        "propsSpread": false,
        "location": {
          "file": "/path/to/file",
          "start": {
            "line": 8,
            "column": 7
          }
        }
      }
    ]
  }
}

Custom processors

We saw above that built-in processors come in the form of a string or a tuple.

Custom processors are functions, and can be asynchronous!

If the processor function returns a Promise, it will be awaited before the next processor kicks in. This way, you can use previous processors results in your processor function.

Here is an example of taking the output of the built-in count-components-and-props processor and sending it to your storage solution.

processors: [
  "count-components-and-props",
  ({ prevResult }) => {
    return axios.post("/my/storage/solution", prevResult);
  }
]

Processor functions receive an object with the following keys in it:

Key Type Description
report object The raw JSON report.
prevResults array Previous processors results.
prevResult any The last item in prevResults. Just for convenience.
forEachComponent function Helper function to recursively traverse the raw JSON report. The function you pass in is called for every component in the report, and it gets an object with componentName and component in it. Check the implementation of count-components-and-props for a usage example.
sortObjectKeysByValue function Helper function that sorts object keys by some function of the value. Check the implementation of count-components-and-props for a usage example.
output function Helper function that outputs the given data. Its first parameter is the data you want to output. The second parameter is the destination. When the second parameter is omitted, it outputs to the console. To output to the file system, pass an absolute path or a relative path to the config file location.

License

MIT

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].