javascripttypescriptwebpackvitebundler

How to build multiple HTML entry points with TypeScript injection and nested output folders in Vite?


Background

I'm migrating a complex multi-page application from Webpack to Vite. Each HTML file has a corresponding .ts script, and the files live in nested directories.

Note: These entry HTML and TS files do not refer to each other (Webpack automate this injection)

src/
  index.html
  index.ts
  user/login/index.html
  user/login/index.ts
  admin/dashboard.html
  admin/dashboard.ts

Goal:

release/
  index.html
  user/login/index.html
  admin/dashboard.html
  scripts/
    index.js
    user/login/index.js
    admin/dashboard.js

What I've tried:

I used vite-plugin-html and vite-plugin-html-template-mpa with a list of page definitions. I also defined the input entries using .ts files.

However:

This is my vite.config.js

import { defineConfig } from "vite";
import path from "path";
import htmlTemplateMpa from "vite-plugin-html-template-mpa";

const pages = [
  "index",
  "user/login/index",
  "admin/dashboard"
];

export default defineConfig(() => {
  const entries = pages.reduce((acc, page) => {
    acc[page] = path.resolve(__dirname, `src/${page}.ts`);
    return acc;
  }, {});

  return {
    build: {
      outDir: "release",
      rollupOptions: {
        input: entries,
        output: {
          entryFileNames: "scripts/[name].js",
          chunkFileNames: "scripts/chunks/[name].js",
          assetFileNames: "assets/[name].[ext]"
        }
      }
    },
    plugins: [
      htmlTemplateMpa({
        pages: pages.map((page) => ({
          template: `src/${page}.html`,
          entry: `${page}.ts`,
          filename: `${page}.html`
        })),
        minify: true
      })
    ]
  };
});

Problem

How can I configure Vite to:


Solution

  • Solution:

    1. Manually add a <script type="module" src="..."> tag to each entry .html file pointing to its corresponding .ts (built as .js):

      <!-- src/user/login/index.html -->
      <script type="module" src="/scripts/user/login/index.js"></script>
      
    2. Set rollupOptions.input in vite.config.js to your .html files:

      const entries = pages.reduce((acc, page) => {
        acc[page] = path.resolve(__dirname, `src/${page}.html`);
        return acc;
      }, {});
      
    3. Remove createHtmlPlugin or any similar HTML template plugins entirely.

    Final vite.config.js Output:

    import { defineConfig } from "vite";
    import path from "path";
    
    const pages = [
      "index",
      "user/login/index",
      "admin/dashboard"
    ];
    
    export default defineConfig(() => {
      const entries = pages.reduce((acc, page) => {
        acc[page] = path.resolve(__dirname, `src/${page}.html`);
        return acc;
      }, {});
    
      return {
        build: {
          outDir: "release",
          rollupOptions: {
            input: entries,
            output: {
              entryFileNames: "scripts/[name].js",
              chunkFileNames: "scripts/chunks/[name].js",
              assetFileNames: "assets/[name].[ext]"
            }
          }
        }
      };
    });