Skip to content

Commit 96769c2

Browse files
committed
Fix alternative index.html path
1 parent 3b18dfb commit 96769c2

5 files changed

Lines changed: 74 additions & 10 deletions

File tree

src/plugins/prerender-plugin.js

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ export function prerenderPlugin({ prerenderScript, renderTarget, additionalPrere
7575
let viteConfig = {};
7676
let userEnabledSourceMaps;
7777
let ssrBuild = false;
78+
let prerenderEntryHtml;
7879

7980
/** @type {import('./types.d.ts').PrerenderedRoute[]} */
8081
let routes = [];
@@ -90,12 +91,9 @@ export function prerenderPlugin({ prerenderScript, renderTarget, additionalPrere
9091
const tmpDirId = 'headless-prerender';
9192

9293
/**
93-
* From the non-external scripts in entry HTML document, find the one (if any)
94-
* that provides a `prerender` export
95-
*
9694
* @param {import('vite').Rollup.InputOption} input
9795
*/
98-
const getPrerenderScriptFromHTML = async (input) => {
96+
const getPrerenderEntryHtml = (input) => {
9997
// prettier-ignore
10098
const entryHtml =
10199
typeof input === "string"
@@ -106,6 +104,17 @@ export function prerenderPlugin({ prerenderScript, renderTarget, additionalPrere
106104

107105
if (!entryHtml) throw new Error('Unable to detect entry HTML');
108106

107+
return path.resolve(viteConfig.root, entryHtml);
108+
};
109+
110+
/**
111+
* From the non-external scripts in entry HTML document, find the one (if any)
112+
* that provides a `prerender` export
113+
*
114+
* @param {import('vite').Rollup.InputOption} input
115+
*/
116+
const getPrerenderScriptFromHTML = async (input) => {
117+
const entryHtml = getPrerenderEntryHtml(input);
109118
const htmlDoc = htmlParse(await fs.readFile(entryHtml, 'utf-8'));
110119

111120
const entryScriptTag = htmlDoc
@@ -118,7 +127,9 @@ export function prerenderPlugin({ prerenderScript, renderTarget, additionalPrere
118127
if (!entrySrc || /^https:/.test(entrySrc))
119128
throw new Error('Prerender entry script must have a `src` attribute and be local');
120129

121-
return path.join(viteConfig.root, entrySrc);
130+
return entrySrc.startsWith('/')
131+
? path.join(viteConfig.root, entrySrc)
132+
: path.resolve(path.dirname(entryHtml), entrySrc);
122133
};
123134

124135
return {
@@ -194,6 +205,7 @@ export function prerenderPlugin({ prerenderScript, renderTarget, additionalPrere
194205
},
195206
async options(opts) {
196207
if (ssrBuild || !opts.input) return;
208+
prerenderEntryHtml = getPrerenderEntryHtml(opts.input);
197209
if (!prerenderScript) {
198210
prerenderScript = await getPrerenderScriptFromHTML(opts.input);
199211
}
@@ -288,9 +300,25 @@ export function prerenderPlugin({ prerenderScript, renderTarget, additionalPrere
288300
};
289301

290302
// Grab the generated HTML file, we'll use it as a template for all pages:
291-
const tpl = /** @type {string} */ (
292-
/** @type {OutputAsset} */ (bundle['index.html']).source
293-
);
303+
const entryHtmlAsset =
304+
(prerenderEntryHtml &&
305+
Object.values(bundle).find(
306+
(output) =>
307+
output.type === 'asset' &&
308+
output.fileName.endsWith('.html') &&
309+
(output.originalFileName === prerenderEntryHtml ||
310+
output.originalFileNames?.includes(prerenderEntryHtml)),
311+
)) ||
312+
bundle['index.html'] ||
313+
Object.values(bundle).find(
314+
(output) => output.type === 'asset' && output.fileName.endsWith('.html'),
315+
);
316+
317+
if (!entryHtmlAsset || entryHtmlAsset.type !== 'asset') {
318+
this.error('Unable to detect generated entry HTML asset');
319+
}
320+
321+
const tpl = /** @type {string} */ (entryHtmlAsset.source);
294322

295323
// Create a tmp dir to allow importing & consuming the built modules,
296324
// before Rollup writes them to the disk
@@ -506,8 +534,7 @@ export function prerenderPlugin({ prerenderScript, renderTarget, additionalPrere
506534

507535
// Add generated HTML to compilation:
508536
route.url == '/'
509-
? (/** @type {OutputAsset} */ (bundle['index.html']).source =
510-
htmlDoc.toString())
537+
? (entryHtmlAsset.source = htmlDoc.toString())
511538
: this.emitFile({
512539
type: 'asset',
513540
fileName: assetName,
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
</head>
6+
<body>
7+
<script prerender type="module" src="./main.js"></script>
8+
</body>
9+
</html>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export async function prerender() {
2+
return `<h1>Nested Entry Test Result</h1>`;
3+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { defineConfig } from 'vite';
2+
import path from 'node:path';
3+
import url from 'node:url';
4+
import { vitePrerenderPlugin } from 'vite-prerender-plugin';
5+
6+
const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
7+
8+
export default defineConfig({
9+
build: {
10+
rollupOptions: {
11+
input: {
12+
dashboard: path.resolve(__dirname, 'src/dashboard/index.html'),
13+
},
14+
},
15+
},
16+
plugins: [vitePrerenderPlugin()],
17+
});

tests/index.test.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,14 @@ test('Should merge preload and entry chunks', async () => {
3737
assert.equal((await fs.readdir(outDirAssets)).length, 1);
3838
});
3939

40+
test('Should support nested HTML entrypoints', async () => {
41+
await loadFixture('nested-entry', env);
42+
await viteBuild(env.tmp.path);
43+
44+
const prerenderedHtml = await getOutputFile(env.tmp.path, 'src/dashboard/index.html');
45+
assert.match(prerenderedHtml, '<h1>Nested Entry Test Result</h1>');
46+
});
47+
4048
test('Should bail on merging preload & entry chunks if user configures `manualChunks`', async () => {
4149
await loadFixture('simple', env);
4250
await writeConfig(env.tmp.path, `

0 commit comments

Comments
 (0)