Improve your Code Quality with Nylas’ Custom Svelte Pre-Processor

Enforce your own HTML Standards in Svelte files with Nylas’ Custom Svelte Pre-Processor

Arjun Kalburgi | September 16, 2021

Linting is the most undervalued tool for developing with best practices. It brings a wonderful, in-editor developer experience by explaining what’s wrong and sometimes automating fixes. As the front-end team at Nylas grew, educating everyone on new code best practices and accessibility standards became difficult to maintain. We needed custom lint rules!

Want a PDF of this article?

Share it with a friend or save it for later reading.

Custom lint rules are built with ESLint and introduce an elegant way to maintain familiarity with the code-base. However, the current ESLint for the Svelte plugin (eslint-plugin-svelte3) does not fully support HTML linting in Svelte files – important for accessibility errors.

Understanding the ESLint Svelte Pre-Processor

When we discovered that the default ESLint parser for JavaScript already supports JSX, our next goal became to convert the Svelte HTML code into JSX compatible syntax. After some digging, it became clear that the pre-processor is the right place to do that; thus, we forked the current pre-processor.

An ESLint pre-processor is a function that compiles information about your code’s syntax tree, spits out an array of special objects (blocks) with your code, and offsets for mapping errors to the source code. ESLint runs on the pre-processed code and uses it to correctly map and highlight errors.

The pre-processor compiles the code into an AST (abstract syntax tree) with four blocks: 

  1. Module block: contains instance source code and references/reassignments of variables mentioned in other blocks.
<script type=”module”> … </script>

2. Instance block:  contains module source code.

<script type=”instance”> … </script>

3. HTML block: contains instance and module source code, all references/reassignments of variables in HTML part of the file. (Note each file’s HTML is not necessary for a body tag).

<body> … </body>

4. Style block: contains the style section of the file

<style> … </style>

After this, the Module and Instance blocks are passed along to ESLint while the HTML and Style blocks are stripped out. Our job is to further process the HTML block into JSX syntax and pass it to ESLint.

Transforming the HTML Block to JSX

The compiler makes walking and transforming the AST easy with methods for entering and leaving nodes. We made custom transformations for each node type (ASTExplorer.net was handy), using a switch case to split up the transformations.

See the example below:

switch (node.type) {
  case "Element": {...}
  case "IfBlock": {...}

Transformation to JSX is straightforward for node types like Element or Text (just preserve node positions) but more involved for Svelte-specific nodes like Each, Await, and If. Making the Svelte-specific nodes JSX required some math and smart text slicing while maintaining line number and the position of the node’s first character. We also added some extra JSX at the end of a line for matching closing tags and making sure white spaces are preserved. 

The result of the transformation looks something like this:

In the end, we have an in-memory JSX or TSX file that includes variable reassignments/references and a JSX code enclosed in JSX-fragment. This is exactly what we need to pass off to the default ESLint parser, which will produce a second-level AST that works with ESLint rules.

Helpful Tips

With HTML support now in the pre-processor, we can build custom HTML lint rules in our Svelte files! 

If you have a Svelte project and would like to build custom ESLint rules, use our Svelte Preprocessor so you can lint HTML. 

Use our parser to get HTML linting in your Svelte project.

Guide to create your own ESLint plugin + start creating custom rules.

More details on creating your own rule.

  1. Use one file
  • Modify the lint command in your package.json to point to just one file. Even better, create a new file just to test your new lint rule. Knowing the exact structure of the code being parsed makes a big difference.
  1. Use the VS Code Debugger 
  • In your Svelte app, open the Debug panel and select Node.js… in your run options. 
  • You’ll get a popup to run your npm scripts, filter for your lint script, and select it. Once you place a breakpoint in linting/rules/html-example.js, press the play button and wait for it to stop. 
  • When it stops, you’ll get something like this in the variables panel in VS Code. You can then add more if statements to narrow down to the element for which you’re looking. Use the node’s children, openingElement.name.name and openingElement.attributes.
  1. Restart VS Code’s Language Server

As you’re testing your rule, you’ll want to see if VS Code spots your errors. When you first set up a rule, you may need to restart VS Code. You can just run ESLint: Restart Language Server from the command palette as you modify your rule.

More to Come!

Our team is very excited to build lint rules into our code-base to improve accessibility, best practices, and documentation. As we continue to grow and make things more streamlined for our customers, we are passionate about introducing more tips and guidelines in the future. Keep an eye out for more information coming from our team!

Arjun Kalburgi

Arjun is a Software Engineer on the Front-end team at Nylas. He loves working on UX, bringing Nylas' premium brand to life, and his house plants.