How to Ensure Codebase is Up to Date With Linting Rules
Implement a linting rule to prevent the code we just transformed from being reintroduced in the future
Get the project source code below, and follow along with the lesson material.
Download Project Source CodeTo set up the project on your local machine, please follow the directions provided in the README.md
file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.
Lesson Transcript
[00:00 - 00:09] The flash codebase now uses button components. To get to this point, we first performed an audit of the codebase to understand how button elements were being used today.
[00:10 - 00:18] Then we created a button component that was capable of handling all of those use cases. Finally, we performed a code mod to transform all of the button elements to the new button component.
[00:19 - 00:27] This completes the original refactor described in Module 3. However, an actual codebase will continue to evolve and have further changes made.
[00:28 - 00:36] How do we ensure the codebase remains up to date using the button component where appropriate? A common approach for this is to create a rule for that codebase that enforces this.
[00:37 - 00:43] Any time a change is made, the codebase can be checked against that rule. If there are any violations, the change can be blocked.
[00:44 - 00:55] For example, in a git workflow, a change is made in a branch off the main branch. Before that branch can be merged back into the main branch, this rule, in addition to other things like tests, need to pass.
[00:56 - 01:05] This way, the main branch should never violate this rule. This concept of applying rules to code is commonly referred to as linting.
[01:06 - 01:13] We can get started by creating another new directory called linter. And again, initialize a new package.json file.
[01:14 - 01:33] Finally, we can install the same dependencies as the original audit script along with one additional Babel package. And we can also install the type packages for these.
[01:34 - 01:43] Then create a new rule.ts file. We can start by copying all of the contents from the previous audit script.
[01:44 - 01:59] Again, we can remove all of the logging and tracking of the props and values. Since all the nodes that match these criteria were transformed in the previous module, no nodes will match these criteria in the codebase.
[02:00 - 02:11] To test this, let's intentionally add a new violation. Update the landing page component in the flash codebase, or any one of the other components, with the following button snippet anywhere in the component.
[02:12 - 02:26] Now that we know there is a violation, the linting rule can be updated to log the file in line that violates this rule, to make it easy to find and fix. Let's put the snippet of code into AST Explorer to see what kind of location data we have.
[02:27 - 02:40] First, make sure the "high-location data" option is disabled. Each node contains a location property that is an object with information about the starting and ending lines and the column numbers for this given node's code.
[02:41 - 02:53] With this in mind, this location information can be appended to the file name to make it easier to jump directly to the position in the code that the violation occurs. Let's get the current JSX opening element starting position.
[02:54 - 03:12] Then, let's print the full file path using node's path helper. We can see here that we're getting a type error.
[03:13 - 03:22] That start does not exist on type of null. For the purpose of this script, we're not expecting the location property to be null, so we can add an exclamation point to assert that it's not null.
[03:23 - 03:41] Finally, let's print a helpful message describing why this error is occurring. Now, let's switch back to the terminal and run this new linter rule.
[03:42 - 03:54] We can see that it prints the full error message here. Additionally, it provides the full file path and starting position of the line and column of where this violation occurred in the source code.
[03:55 - 04:01] This script provides the basics for a linting rule. However, copying and pasting the file path to see the code that is violating the rule is somewhat tedious.
[04:02 - 04:10] It would be nice to include the snippet of code that is causing the violation directly in the output. Fortunately, there's another Babel package that works great for this, Babel codeframe.
[04:11 - 04:20] This package allows printing a piece of code with an inline message or code frame. It was already installed earlier, so let's update the script to use this package instead of producing the console errors.
[04:21 - 04:33] We can start by importing the codeframe columns function from Babel codeframe. Then we can use this function below to print the code that violates this rule.
[04:34 - 04:37] It requires three arguments. First, the contents of the current file.
[04:38 - 04:44] Then the location of the exact code that is relevant. And finally, additional options.
[04:45 - 04:53] We already have the contents defined above, but we don't have the location or options defined yet. For the location, we can use the existing location data from the node.
[04:54 - 05:16] However, the columns are off by one, so we'll need to add one to each of those values. Now, we can define the options.
[05:17 - 05:23] There are two options we want. First, we want to enable syntax highlighting by setting the highlight code option to true.
[05:24 - 05:41] Second, we can move our error message to be inline. Finally we can print the result.
[05:42 - 05:57] Running the script will now highlight the same violation, but this time, the output provides contacts with the code that is violating this rule. This script is able to find new button elements added in the codebase and print helpful information to fix the issue.
[05:58 - 06:10] As mentioned earlier, this script will need to be run on every change and set an exit code to indicate if there are any violations or not. An exit code is a code return to indicate the status of a child process to a parent process.
[06:11 - 06:23] In this context, this is important for using the script in a continuous integration environment. Commonly, there are a number of build, test, linting and other steps in this environment to verify changes.
[06:24 - 06:45] Each step, in this case linting, can indicate whether it was successful or not via the exit code, and the CI environment can handle the failure appropriately, for example, marking the CI build as a failure and disallowing merging of the change. We can update our script to set the process exit code to one if there was a violation.
[06:46 - 06:53] In at the end, call the process.exit function. Calling process.exit will use the value of process.exit code.
[06:54 - 07:03] If it's not set, it will default to zero. An exit code of zero will indicate a success, while one indicates a failure.
[07:04 - 07:15] Let's run our script again to check the exit code. The latest exit code can be checked and show by echoing the value of $' question mark.
[07:16 - 07:24] We can also see the exit code printed here. Let's go back to the landing page component and remove this violation.
[07:25 - 07:43] Now if we run the script, it should print an exit code of zero. Setting an exit code like this isn't a requirement, but it is a helpful addition when it's a single step in a larger process like a CI environment.
[07:44 - 07:58] With minimal new code, it was possible to convert the same script used for a custom audit and a custom transform into a custom landing script. This demonstrates the versatility of ASTs and how many of the tools you may already use day to day rely on them.
[07:59 - 08:14] Different parsers can produce slightly different types of nodes, but otherwise they're ultimately a shared language, a tree data structure representing the underlying source code. This not only means it's easier to approach any AST-based tooling, but that much of the same tree traversal logic can be reused between different tools.
[08:15 - 08:27] The initial logic for finding button elements with the button class name is nearly identical across all these scripts. However, at this point, you might start having similar questions to the approach taken for creating a custom transform.
[08:28 - 08:41] We might imagine that a large code base will need many linting rules. In a similar way to reaching for JS code shift as a more all-in-one solution for doing transforms, the next lesson we'll cover using ES lint as an all-in-one solution for creating linting rules.
This lesson preview is part of the Practical Abstract Syntax Trees course and can be unlocked immediately with a \newline Pro subscription or a single-time purchase. Already have access to this course? Log in here.
Get unlimited access to Practical Abstract Syntax Trees, plus 70+ \newline books, guides and courses with the \newline Pro subscription.
![Thumbnail for the \newline course Practical Abstract Syntax Trees](/_next/image?url=https%3A%2F%2Fs3.amazonaws.com%2Fassets.fullstack.io%2Fn%2F20211005114103016_CoverImage_PracticalAbstractSyntaxTrees_v2.png&w=3840&q=75)