Rafi
Rafi

Reputation: 824

How to add included Pug files to Vite module graph

I wrote a Rollup plugin to import Pug as an HTML string:

// Rollup plugin imported to Vite config

import { render } from 'pug';

export default function pug() {
  return {
    name: 'rollup-plugin-pug-html',

    transform(src, id) {
      if (id.endsWith('.pug')) {
        const html = render(src, { filename: id });
        const code = `export default ${JSON.stringify(html)};`;

        return { code };
      }
    },
  };
}

I'm using it in Vite to create templates for Vue components, as in this reduced example:

// ProofOfConceptSFC.vue

<script>
import { compile } from 'vue/dist/vue.esm-bundler.js';
import template from './template.pug';

export default {
  render: compile(template)
};
</script>

The HMR is working great when I edit template.pug. The new template appears and the latest reactive values persist.

My problem is that template.pug may depend on other Pug files with include:

//- template.pug

include ./header.pug
p Hello {{ name }}
include ./footer.pug

The Vite server doesn't know about those files, and nothing happens if I touch them. Ideally I could invalidate template.pug when any Pug file is changed.

I'm guessing I want my plugin to update the ViteDevServer's server.moduleGraph. Is there a supported way to do that?

Upvotes: 2

Views: 3343

Answers (1)

Rafi
Rafi

Reputation: 824

Huge thanks to the friendly Vite chat on Discord for setting me in the right direction.

The two keys I was missing:

  1. Use Pug compile to create a render method that has render.dependencies, as done by Parcel
  2. Use virtual import statements to attach the dependencies to the transform hook result, as done by vite-plugin-svelte.

Here is the working plugin:

import { compile } from 'pug';

export default function pluginPug() {
  return {
    name: 'vite-plugin-pug',

    transform(src, id) {
      if (id.endsWith('.pug')) {
        const render = compile(src, { filename: id });
        const html = render();
        let code = '';

        for (let dep of render.dependencies) {
          code += `import ${JSON.stringify(dep)};\n`;
        }

        code += `export default ${JSON.stringify(html)};`;

        return { code };
      }
    },
  };
}

Upvotes: 1

Related Questions