vitejs / vite

Next generation frontend tooling. It's fast!
http://vite.dev
MIT License
68.58k stars 6.19k forks source link

Proposal to add `transformEntryToHtml` hook in plugins #8000

Open saurabhdaware opened 2 years ago

saurabhdaware commented 2 years ago

Clear and concise description of the problem

Think of this directory structure-

|- nested /
|--|- about.ejs
|- index.ejs
|- main.js 
|- vite.config.js

Currently, seems like there is no clear way to do the <-- template --> to HTML conversion during SSG. transformIndexHtml only runs for files with .html extension and transform can only output JavaScript and not HTML.

In the above example, I am guessing the implementation would look something like this-

- Turn EJS to temporary HTML files
- Set those newly created HTML files in the `input` option while calling vite.build 
- Delete the temporary HTML files (Since they are not going to have all the styles and scripts included)

This is exactly how I am doing things in the SSG that I am working on-

https://github.com/abelljs/abell/blob/387cf15dc81471a4f1e722fc9ad28541c64de2f5/packages/abell/src/cli/generate.ts#L53-L73

This seems to be missing for all template engines.

For example-

Suggested solution

A new hook called transformEntryToHtml.

Example-

// plugin code
export function ViteEjsPlugin(): Plugin {
  return {
    name: "vite-plugin-ejs",
    // Similar to `transformIndexHtml` except it runs for any entry file irrespective of the extension
    transformEntryToHtml(ejsCode: string): string {
      const html = ejs.render(ejsCode)
      return html   
    }
  };
}
// Plugin consumer's vite.config.ts
// vite.config.js
import { defineConfig } from 'vite';

export default defineConfig({
  build: {
    rollupOptions: {
      input: ['./index.ejs', './nested/about.ejs']
    }
  }
})

This will simplify the SSG flow a lot and will allow plugins like vite-plugin-ejs to have .ejs extension.

It will allow SSGs like Abell to build the output without generating temporary HTML files during the build.

Alternative

Alternative would be some way to call transformIndexHtml hook for files that are not HTML (don't have .html extension).

Additional context

Validations

sapphi-red commented 2 years ago

related: #2321

Pyrolistical commented 2 years ago

ditto for https://github.com/vbenjs/vite-plugin-html, it uses ejs as well but templates MUST end with .html

Pyrolistical commented 2 years ago

I think a simpler hook would be createIndexHtml. This would require less configuration and would allow us to write:

// vite.config.js

import {render} from 'ejs'
import {readFile, writeFile} from 'node:fs/promises'

// type createIndexHtml = {
//   dependencies: string[];
//   create: () => Promise<string>;
// }

const injectEjs = ({source, destination, data}) => {
  name: "inject-ejs",
  createIndexHtml: {
    dependencies: [source],
    async create() {
      const template = await readFile(source, 'utf8')
      const html = await render(template, data, {
        async: true
      })
      await writeFile(destination, html)
      return destination
    }
  }
};

export default () => {
  return {
    plugins: [
      injectEjs({
        source: 'index.html.ejs',
        destination: 'index.html',
        data: {
          title: 'some title'
        }
      })
    ]
  }
}

create returns a filename of a created html which is used like any other html entry point. createIndexHtml would need to be run before everything.

There is also a dependencies array which is all the source files the plugin will read. This would allow Vite to file watch the dependencies and only run the plugin when the contents have changed.

lubomirblazekcz commented 2 years ago

This hook (or similar) would actually solve my hacky solution in vituum. Where I'm using a workaround with renaming the files to .html before build to add support for various template engine ext-names, similiar to @saurabhdaware solution.

joshamaju commented 1 year ago

Any updates on this? seems to be a common point a lot of people have arrived at

lubomirblazekcz commented 1 year ago

Here is an updated vituum implementation which uses buildStart and buildEnd rollup hooks to rename files to .html for build and viteDevServer.middlewares for dev server support. If anyone wants you can use this workaround.

https://github.com/vituum/vituum/blob/3348010f053da61ee33c6bc5db120bd5a4b07e3b/src/utils/build.js#L31 https://github.com/vituum/vituum/blob/3348010f053da61ee33c6bc5db120bd5a4b07e3b/src/utils/common.js#L89

Here is a working example of vite plugin that supports .twig templates https://github.com/vituum/vite-plugin-twig/blob/3b91629f7b4a7a8588a8c7c8dc2e4c5859c9f23f/index.js#L151