The UpgradeJS Blog

Introducing Plugin Preloader

As the upgrade team, one of our most significant challenges is the initial setup of each project, as many of the tools we utilize for inspection need distinct and customized configurations. This is due to the countless methods in which JavaScript/TypeScript projects can be constructed.

In today’s demonstration, we will introduce you to our new open source project called plugin-preloader opens a new window and show you how you can use it to simplify your workflow if you encounter similar issues as we do.

plugin-preloader opens a new window is a powerful and flexible JavaScript library written in TypeScript and designed to streamline the development workflow by automating the preloading and processing of the code.

With its ability to integrate various plugins, plugin-preloader provides an efficient and customizable solution for managing Babel opens a new window and ESLint opens a new window plugins.

Use case

We have a blog post that explains how we use es6-plato opens a new window to create a comprehensive static code analysis report for each project. The report is produced by examining the project’s source code and offering a visual representation of code complexity along with various other metrics.

In order to generate the report, es6-plato must parse the project’s source code. This is where the plugin-preloader becomes valuable. It allows us to install all the necessary Babel and ESLint plugins by simply specifying the paths to their respective configurations, eliminating the need for manual installation.

This feature is implemented in our fork opens a new window of the es6-plato library. As we deal with multiple projects, this approach can save us a lot of time and effort.

Installation

To install the library, run the following command:

npm install plugin-preloader

Usage example

Let’s take a look at a project whose primary objective is to analyze other projects. To accomplish this, it must process the source code of the project being examined.

An example without usage of plugin-preloader

Let’s consider the following project structure:

Main executable file:

const babel = require("@babel/core");
const fs = require("fs");

async function getAST(filePath, config) {
  const fileContent = (await fs.promises.readFile(filePath)).toString();

  return await babel.parse(fileContent, config);
}

async function main(pathToTargedProject, configPath) {
  const config = require(configPath);

  // For the purpose of this example, we can skip the implementation of the getTargetProjectFiles function
  const targetProjectFiles = await getTargetProjectFiles(pathToTargedProject);

  const asts = await Promise.all(
    targetProjectFiles.map((filePath) => getAST(filePath, config))
  );

  console.log(asts);
}

module.exports = main;

In order to be able to inspect TypeScript projects, we need to install the @babel/plugin-transform-typescript plugin. We need to be sure that it was added to the package.json file:

{
  "dependencies": {
    "@babel/core": "7.21.8",
    "@babel/plugin-transform-typescript": "7.21.3"
  }
}

And add it to the Babel configuration:

module.exports = {
  plugins: ["@babel/plugin-transform-typescript"]
};

With that new setup ready, we can now move on and try to run this tool on a React project. Once again, we need to install the @babel/preset-react plugin and ensure that it was stored in the package.json file:

{
  "dependencies": {
    "@babel/core": "7.21.8",
    "@babel/plugin-transform-typescript": "7.21.3",
    "@babel/preset-react": "7.18.6"
  }
}

We have to also modify the Babel configuration. As we no longer need the @babel/plugin-transform-typescript plugin, we can remove it from the configuration:

module.exports = {
  presets: ["@babel/preset-react"]
};

From the previous code samples, you can see the pattern: in order to inspect a project, we need to install the required plugins and modify the Babel configuration.

In some cases we don’t need to use some installed Babel plugins and just modify the configuration.

An example using plugin-preloader

Now let’s take a look at how plugin-preloader can save us time and effort.

Changes we need to make to the main executable file:

const babel = require("@babel/core");
const fs = require("fs");

// add the plugin-preloader library
const preload = require("plugin-preloader");

// rest of the code

async function main(pathToTargedProject, configPath) {
  const config = require(configPath);
  
  // add the preload function call with the desired configuration, it can be babel and/or eslint
  preload({ babel: config });

  // rest of the code
}

module.exports = main;

Changes we need to make to the Babel configuration file:

module.exports = {
  presets: [["@babel/preset-react", undefined, undefined, "7.18.6"]]
};

And changes we need to make to the package.json file:

{
  "dependencies": {
    "@babel/core": "7.21.8",
    "plugin-preloader": "7.18.6"
  }
}

As you can see from these snippets, the project requires Babel plugins to parse the code. This is where the plugin-preloader proves useful.

By specifying the path to the plugin’s configuration, plugin-preloader allows you to install and use all necessary plugins dynamically and removes the need for manual installation. It offers the preload function that works seamlessly with standard Babel and ESLint configurations, ensuring that each plugin, preset, parser, or extend is processed and installed with the latest version.

To use a specific version for Babel plugins and presets, add a fourth element in the plugin/preset definition array item, bypassing the second and third elements (plugin configuration arguments) - ["@babel/preset-react", undefined, undefined, "7.18.6"]. For ESLint plugins, extends, and parsers, use an array instead of a string, and the desired version is specified in the second element of the array - ["plugin:@typescript-eslint/recommended", "3.0.0"]. From this point on, we can modify our Babel configuration based on the target project and not worry about installing the required pluginsm, plugin-preloader will take care of that for us.

To more effectively demonstrate the usage of this library, we have created an example project. You can find it in the example opens a new window folder.

Output

After successfully executing the preload function, the desired plugins, presets, parsers, and extends will be installed in the node_modules folder. It is important to note that these installed packages will not be added to the package.json or package-lock.json files. This is by design, as plugin-preloader aims to provide a temporary installation of the required plugins for the current session without affecting the project’s main dependencies. This approach ensures that the installed packages are solely used for the purpose they were preloaded for, such as code analysis or linting, without cluttering your project’s dependencies.

Once you are done with the specific task, the installed packages can be easily removed by running a clean-up process, such as deleting the node_modules folder and reinstalling the project dependencies using npm prune or yarn install. This keeps your project’s dependency list clean and focused on its core requirements.

Known issues

The library is still in its early stages of development, and we are working on improving it. We are aware of the following issues opens a new window , and we are working on resolving them.

Conclusion

In conclusion, the plugin-preloader opens a new window library offers a useful solution for handling Babel and ESLint plugins in JavaScript and TypeScript projects. By automating the installation of plugins, presets, parsers, and extends, it streamlines the development workflow, saving you time and effort.

By integrating this library into your projects, you can eliminate manual installation steps and focus on what truly matters — creating software.