Exploring svelte preprocessor

2023-09-24

Challenge

I’ve noticed a few packages now using preprocessors to provide DX improvements for their packages and I’m curious on how exactly these preprocessor work, and how easy is it to write something of my own.

Inspiration

For a first quick pass, I’m reviewed what threlte is doing in their preprocessor to get a baseline of what actually happens. I also created a new Sveltekit project to use to explore this.

Prepared the preprocess source

I created a new file called preprocess.js .

I created a new exported function called kPreprocess to be used in the svelte.config.js .

import adapter from '@sveltejs/adapter-auto';
import { kPreprocess } from './src/lib/preprocess.js';

/** @type {import('@sveltejs/kit').Config} */
const config = {
	kit: {
		adapter: adapter(),
	},
	preprocess: kPreprocess()
};

export default config;

The processor returned an object with a markup property that is a function that receives a single param that contains the properties content and filename . As it processes the file, it will execute this function on each file (I think).

export function kPreprocess() {
  return {
    markup: ({ content, filename }) => {
      console.log({ filename })
      return {
        code: content
      }
    }
  }
}

You can also pass a script method that will run through all the script tags. I added some basic code into a dummy Component.svelte file to see what the method returns.

//Component.svelte
<script>
  // HEre is a comment

  let hello = 'world';
</script>
export function kPreprocess() {
  return {
    markup: ({ content, filename }) => {
      console.log({ filename })
      return {
        code: content
      }
    },
    script: (data) => {
      console.log('this should run after the markup')
      console.log({data})
    }
  }
}

The logged output is as follows:

{
  filename: '/home/kaashin/kaashin-labs/svelte-preprocess/src/lib/Component.svelte'
}
this should run after the markup
{
  data: {
    content: "\n\t// HEre is a comment\n\n\tlet hello = 'world';\n",
    attributes: {},
    markup: "<script>\n\t// HEre is a comment\n\n\tlet hello = 'world';\n</script>\n",
    filename: '/home/kaashin/kaashin-labs/svelte-preprocess/src/lib/Component.svelte'
  }
}

So it looks like you get the content of what is inside the script tags, attributes that are in the script tag and the full markup of the script tag.

The result is the same with the style tag.

What does walk do in svelte/compiler

The next thing I looked over is the walk function provided by the svelte compiler that is used by all the preprocessors. Based on the svelte docs , it looks like a handy utility to make it easier to step through the markup by creating nodes of the code.

Extracting data from processed files

I modified the Component.svelte to define three different props. One with a type definition and a default value assigned, another with a type but no default value assigned, and lastly, one that is just defining the name with no type or default value

<script>
	// Here is a comment

	/**
	 * @type {string}
	 */
	export let color = 'blue';

	/**
	 * @type {boolean}
	 */
	export let loading;

	export let noType;

	let hello = 'world';
</script>

When reviewing the parsed code from walk I determined the following

Example, the jsdoc for: