vite icon indicating copy to clipboard operation
vite copied to clipboard

Built-in Multi-Page App Support on Build

Open Snugug opened this issue 3 years ago • 18 comments

Clear and concise description of the problem

As a developer of multi-page apps, I would like have all of the pages automatically included when running the build command instead of manually configuring each path. Given it works as expected during development, this feels more like a bug in build than a feature.

Suggested solution

Either automatically, or provide a config to automatically, include all globbed HTML files in the Vite root as rollup inputs.

Alternative

It can be done through globbing in Vite config, or using a plugin, although I've found the plugin route to be unreliable.

Additional context

A post on my current solution to this can be found here

Snugug avatar May 14 '21 18:05 Snugug

Have you tried https://github.com/IndexXuan/vite-plugin-mpa?

@IndexXuan this issue may be interesting to you.

patak-dev avatar May 15 '21 17:05 patak-dev

Yes I have and it didn't work for me, to a point where it was easier to write the globbing I needed than try and trace the problem in the plug-in

On May 15, 2021, at 1:40 PM, patak @.***> wrote:

 Have you tried https://github.com/IndexXuan/vite-plugin-mpa?

@IndexXuan this issue may be interesting to you.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or unsubscribe.

Snugug avatar May 15 '21 18:05 Snugug

@patak-js @Snugug I dont know why it didn't work for you. can you give more context ? this plugin in development: auto glob pages and configure vite options and rewrite url like vue-cli MPA. in build: auto glob pages and optimize structure like vue-cli MPA dist.

Also it support html template like vue-cli by https://github.com/IndexXuan/vite-plugin-html-template

IndexXuan avatar May 17 '21 09:05 IndexXuan

Simple workaround:

{
    build: {
        rollupOptions: {
            input: require('fast-glob').sync(['./**/*.html', '!dist']).map(entry => path.resolve(__dirname, entry))
        }
    }
}

geordie2020 avatar Oct 27 '21 09:10 geordie2020

@geordie2020 Normally you wouldn't traverse each subdirectory except for dist/. I'd suggest an allowlist rather than a denylist.

sanmai-NL avatar Jun 10 '22 20:06 sanmai-NL

// vite.config.js
import glob from 'fast-glob';

rollupOptions: {
    input: Object.fromEntries(
        glob.sync(['./*.html', './pages/**/*.html']).map(file => [
            // This remove `pages/` as well as the file extension from each
            // file, so e.g. pages/nested/foo.html becomes nested/foo
            path.relative(__dirname, file.slice(0, file.length - path.extname(file).length)),
            // This expands the relative paths to absolute paths, so e.g.
            // src/nested/foo becomes /project/src/nested/foo.js
            fileURLToPath(new URL(file, import.meta.url)),
        ]),
    ),
},

https://rollupjs.org/configuration-options/#input

kzamanbd avatar Mar 25 '23 19:03 kzamanbd

// vite.config.js
import glob from 'fast-glob';

rollupOptions: {
    input: Object.fromEntries(
        glob.sync(['./*.html', './pages/**/*.html']).map(file => [
            // This remove `pages/` as well as the file extension from each
            // file, so e.g. pages/nested/foo.html becomes nested/foo
            path.relative(__dirname, file.slice(0, file.length - path.extname(file).length)),
            // This expands the relative paths to absolute paths, so e.g.
            // src/nested/foo becomes /project/src/nested/foo.js
            fileURLToPath(new URL(file, import.meta.url)),
        ]),
    ),
},

https://rollupjs.org/configuration-options/#input

This helped me a lot, thanks friend!

Grinch3214 avatar Sep 24 '23 12:09 Grinch3214

// This remove `pages/` as well as the file extension from each
// file, so e.g. pages/nested/foo.html becomes nested/foo
path.relative(__dirname, file.slice(0, file.length - path.extname(file).length)),

☝️ This won't achieve the desired result, unfortunately, it will still get output as build/pages/nested/foo.html. All HTML entries are built relative to root directory, I didn't find a way to control this 😕

Even vite-plugin-mpa has this as an experimental feature:

Experimental: when building, organize the folder for you (like vue-cli) - e.g dist/src/pages/subpage/index.html will move to dist/subpage/index.html

silvenon avatar Jan 16 '24 00:01 silvenon

// This remove `pages/` as well as the file extension from each
// file, so e.g. pages/nested/foo.html becomes nested/foo
path.relative(__dirname, file.slice(0, file.length - path.extname(file).length)),

☝️ This won't achieve the desired result, unfortunately, it will still get output as build/pages/nested/foo.html. All HTML entries are built relative to root directory, I didn't find a way to control this 😕

Even vite-plugin-mpa has this as an experimental feature:

Experimental: when building, organize the folder for you (like vue-cli) - e.g dist/src/pages/subpage/index.html will move to dist/subpage/index.html

Maybe we can only keep the last 2 directories, like that:

path.join(...file.split(path.sep).slice(-2))

HawtinZeng avatar Feb 26 '24 07:02 HawtinZeng

Not sure what you mean, could you be more specific? Vite doesn't allow changing output path of HTML files, I don't think they have plans to change this. #15612


Adding more various info in case it helps anyone reading this.

Configuring your static router to look for HTML files in pages/* as a fallback might be a viable solution as well, and possibly the easiest workaround, I haven't tried it yet.

What I did in my project is created the following directory structure:

src/
  index.html
  about.html
  blog/
    index.html
  ...assets

then I set Vite's root to src. That way output is as I want it to be. Everything that's being imported must be in src. But this setup leads to some annoying additional configs, like setting build.outDir to ../build because those paths are being computed relative to src.

Regardless of which solution you choose, as far as listing HTML files goes, you can pass input paths to as an array to rollupOptions.input and omit output paths altogether AFAIK.

silvenon avatar Feb 26 '24 16:02 silvenon

// This remove `pages/` as well as the file extension from each
// file, so e.g. pages/nested/foo.html becomes nested/foo
path.relative(__dirname, file.slice(0, file.length - path.extname(file).length)),

☝️ This won't achieve the desired result, unfortunately, it will still get output as build/pages/nested/foo.html. All HTML entries are built relative to root directory, I didn't find a way to control this 😕 Even vite-plugin-mpa has this as an experimental feature:

Experimental: when building, organize the folder for you (like vue-cli) - e.g dist/src/pages/subpage/index.html will move to dist/subpage/index.html

Maybe we can only keep the last 2 directories, like that:

path.join(...file.split(path.sep).slice(-2))

I known, rollupOptions.input won't change the output path after building.

HawtinZeng avatar Feb 27 '24 02:02 HawtinZeng

Is there a solution to this issue?

eunjin0212 avatar Apr 19 '24 15:04 eunjin0212

Multiple workarounds are mentioned throughout the conversation, depending on which kind you're looking for.

silvenon avatar Apr 19 '24 18:04 silvenon

In the last meeting, we decided to prefer an explicit approach instead because we don't want users to accidentally build HTML files in test-results or __tests__, or an any potential name that they may not want. To make sure this works well in dev and build, dev should warn instead if it's accessing a HTML page that's not in the build list.

What we didn't discuss though (an idea I just had), is maybe Vite could probably a utility function to easily generate the input with globs.

bluwy avatar May 18 '24 08:05 bluwy

Inputs with globs, and/or excludes with globs, and warning if something isn't covered sounds like a good path forward

Snugug avatar May 18 '24 13:05 Snugug

Multiple workarounds are mentioned throughout the conversation, depending on which kind you're looking for.

But sadly none of them really work.

It would be nice to for example have

src/
  pages/
     index.html
     about.html
     blog/
         index.html
     ...assets

being built into

dist/
  index.html
  about.html
  blog/
    index.html
  ...assets

Or using any input directory for the pages. But this does not work at all and none of the workarounds work properly.

There are two issues:

  • the lesser problem is that picking up the html pages as input requires fancy rollup configuration
  • the bigger problem is that the produced output will be like dist/src/pages/index.html instead of dist/index.html

The latter has no workaround besides setting Vite's root to the subdirectory, resulting in undesired side effects and breaking other things.

dagnelies avatar Jul 12 '24 09:07 dagnelies

@dagnelies exactly this supports vituum.dev as a Vite plugin :)

lubomirblazekcz avatar Jul 12 '24 10:07 lubomirblazekcz

@lubomirblazekcz I think going by the docs it still builds only the immediate index.html with its assets.

I would echo that with @dagnelies and add to the thread that the paths in meta tags (og: etc.) may have https:// paths, in which case it would be nice if they were transformed too. Also one may have inline JSON-LD tags that also contain https:// paths to to image assets, which would be nice to have transforme too. Or alternatively kept as-is and the file structure moved and optimized in-place (maybe with JS file locations aren't such an issue considering their usage).

It may be something like https://modern-web.dev/docs/building/rollup-plugin-html/ could help people who stumble up here like I did. I just want to a way to build fairly simple multi-page apps and retain structure so it works when deployed, with some optimizations. :)

veikkoeeva avatar Jul 30 '24 08:07 veikkoeeva