Author: Yao Zeyuan
Recently, I received a demand to count the popularity of self-developed NPM package & the amount of functions in the package in the front-end project of the company. The solution process is more interesting, here to share.
The basic idea of the project is relatively simple, as shown in the figure below.
For obtaining all front-end project problems, because our company has a set of self-built public front-end packaging platform, we can directly call the platform interface to pull the project source code.
So there is only one difficulty left: how to parse the JS file and get the usage times of exported objects in the target NPM package.
It was simple: we did what Babel did.
As anyone who has used Babel knows, it can read ES6 code, turn the entire JS file into an abstract syntax tree, walk through the tree, call plug-ins to tweak the content of the code, strip/transform the syntax structure, and finally output ES5 code. All we need to do is write a plug-in that identifies the target NPM package and counts the use of variables extracted from the package as Babel traverses the syntax tree. The process is as follows.
Then the rest is manual work: all the syntax tree node types that Babel parses are in the babel-types package, and all you need to do is target each syntax structure in the package (import/variable destruct/rename/function call /…). Write the handler function, and finally output all the results as a JSON.
The code is quite lengthy, so you can browse the Github project for the full text, and just show the data deconstruction for statistics
Summary of project data: SummaryCollection
Create a SummaryCollection object for each project. Call the Add method to register the parsing results for each file
Function signatures | function | note |
---|---|---|
constructor() | Initialize the summary class | Summarize analysis records of all files in the project |
add(target: UsedSummaryInFile) | Add file analysis data | |
toJson(): TypeUiLibReport[] | Output summary results |
File data summary: USedSummaryFile
For a single JS file, the usage record of the target NPM is counted
Function signatures | function | note |
---|---|---|
constructor(fileUri: string) | Initialize file analysis records | Log filesfileUri NPM package uses data in |
addLib(libName: string) | Once the target NPM is found, register the NPM package name | |
addLibAlias(libName: string, aliasName: string) | Registers the alias of the target NPM package | |
addComponent(libName: string, componentName: string) | Enlists components under the target NPM package | |
addComponentAlias(libName: string, componentName: string, componentNameAlias: string) | Registers the alias of the component under the target NPM package | |
incrComponentUseCount(libName: string, componentName: string) | Number of components used in NPM package +1 | |
incrLibUseCount(libName: string) | NPM package direct use times +1 | |
isRegistedLibName(targetName: string) | Check whether the NPM package has been registered | |
isRegistedComponentName(targetName: string) | Check whether the component is registered | |
getComponentNameBelongToLib(targetName: string) | Based on the component name, find the NPM package name to which it belongs |
NPM usage statistics: UsedLib
Records the usage of NPM packages and the usage of components within NPM packages
Function signatures | function | note |
---|---|---|
constructor(libName: string) | Initialize NPM record, NPM package namelibName |
Record NPM package usage data |
addComponent(componentName: string) | registrationlibName Components in packages |
– |
addComponentAlias(componentName: string, componentAliasName: string) | registrationlibName Alias of a component in a package |
– |
incrComponentUseCount(componentName: string, fileUri: string) | Component in filefileUri +1 times of use in |
– |
incrLibUseCount(fileUri: string) | NPM library in filefileUri +1 times of use in |
The NPM package may be a function in itself |
isRegistedComponentName(testComponentName: string) | Checking component namestestComponentName Whether inlibName Package registered |
– |
Component usage: UsedCompontent
Record the number of times a component is used
Function signatures | function | note |
---|---|---|
constructor(name: string) | Initialize the component record object. Name is the name of the component being counted | Record component usage data |
addAliasName(aliasName: string) | Register component alias | – |
incrUseCount(fileUri: string) | In the filefileUri +1 times of use in |
– |
The resources
👇 introduces Babel to the process of processing syntax tree (Visitor pattern), abstract syntax tree concept, how Babel works, must read
Go deep into Babel. This one will do
👇, also an introduction to Babel.
What front-end engineers need to know about Babel
👇 Babel parses JS code using @babel/babel-parser, while @babel/babel-parser is fork’s Acorn. When working with abstract syntax trees generated by Babel, it is necessary to understand the meaning of the type field of each syntax tree node (e.g., VariableDeclaration, ImportDefaultSpecifier).
Here is the standard document, but in English, below is a Chinese version, you can refer to the development process
Use Acorn to parse JavaScript
👇 convert JS code to AST online. A must-have when writing related code
Abstract syntax tree preview