How to use transformIndexHtml to transform app.html
?
#8269
Replies: 9 comments 4 replies
-
transformIndexHtml isn't supported by sveletkit at the moment. You can do it at buildtime for prerendered pages via hooks.server.js |
Beta Was this translation helpful? Give feedback.
-
I'm curious what your usecase for it is? We've never bothered implementing it because there's not much you can do with it and it's difficult to use with SSR, but if there were a compelling usecase perhaps we could find a way to solve it |
Beta Was this translation helpful? Give feedback.
-
@benmccann another usecase with transformIndexHtml that I came across today: https://github.com/ZhongxuYang/vite-plugin-version-mark This can still be done with virtual module, but getting everything done within the plugin is obviously better. Hope this will be put under the roadmap in the future |
Beta Was this translation helpful? Give feedback.
-
@benmccann and another use case: The very useful (and popular) vite-plugin-webfont-dl, that will download and configure web fonts automatically and inject them as a style tag into the index.html using that hook: feat-agency/vite-plugin-webfont-dl#38. |
Beta Was this translation helpful? Give feedback.
-
We're developing SPA applications time to time, and the fact that
The headache:
Basically I want to do this: /** @returns {import('vite').Plugin} */
export default function myPlugin() {
return {
name: 'my-plugin',
buildStart: {
order: 'post',
handler() {
// Generate favicons
}
},
transformIndexHtml: {
order: 'post',
handler() {
// Return all favicons
return [
{
tag: 'link',
attrs: {
rel: 'icon',
type: 'image/png',
href: 'path-to-favicon.png'
}
}
];
}
}
};
} |
Beta Was this translation helpful? Give feedback.
-
I think this plugin: https://github.com/josh-hemphill/vite-plugin-favicon would work quite nicely if Looking at the competition: I believe Next.js is doing a great job by eliminating the tedious task of setting up proper favicons almost entirely through the use of filename conventions. However, I would be happy if everything could be resolved with a simple plugin like the one mentioned above. Considering favicons as a fundamental feature with a significant variance in implementation, I believe this is something that should be taken seriously by the maintainers, as it something virtually every user runs into sooner or later and it would be a great addition to improve everyone's dx. |
Beta Was this translation helpful? Give feedback.
-
I tried josh-hemphill/vite-plugin-favicon but not working with sveltekit. |
Beta Was this translation helpful? Give feedback.
-
I want to set the Google Tag Manager in the app.html file based on environment variable - for dev and production environments. The fact that transformIndexHtml doesn't work is preventing me from doing that. |
Beta Was this translation helpful? Give feedback.
-
I had this problem. This is how you solve it.
vite.config.ts import { sveltekit } from '@sveltejs/kit/vite'
import { defineConfig } from 'vite'
import { colorSuitePlugin } from 'tailwindcss-color-suite'
import { hookTransformIndexHtmlPlugin } from './hookTransformIndexHtmlPlugin'
export default defineConfig({
plugins: [
sveltekit(),
colorSuitePlugin(), // this is the plugin that uses transformIndexHtml, it was made with vue
hookTransformIndexHtmlPlugin(),
]
}); svelte.config.js import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
import hookHooks from './hookHookConfig'
/** @type {import('@sveltejs/kit').Config} */
const config = {
preprocess: vitePreprocess(),
kit: {
prerender: {
}
}
};
export default hookHooks(config); hookHookConfig.js import { mergeConfig } from 'vite';
/**
* @param {import('@sveltejs/kit').Config} [svelteKitConfig]
* @returns {import('@sveltejs/kit').Config}
*/
export function hookHooks(svelteKitConfig) {
const outDir = svelteKitConfig?.kit?.outDir ?? '.svelte-kit'
const currentHook = `${outDir}/generated/hooks.server.mjs`
const previousHook = svelteKitConfig?.kit?.files?.hooks?.server ?? 'src/hooks.server'
globalThis.__hook_hooks = {
currentHook,
previousHook,
}
/** @type {import('@sveltejs/kit').Config} */
const overrides = {
kit: {
files: {
hooks: {
server: currentHook
}
}
},
}
return mergeConfig(svelteKitConfig, overrides);
} hookTransformIndexHtmlPlugin.ts import type { Plugin } from 'vite'
import fs from 'fs'
import path from 'path'
// taken from /@sveltejs/kit/src/utils/filesystem.js
function resolve_entry(entry: string) {
if (fs.existsSync(entry)) {
const stats = fs.statSync(entry);
const index = path.join(entry, 'index');
if (stats.isDirectory() && fs.existsSync(index)) {
return resolve_entry(index);
}
return entry;
} else {
const dir = path.dirname(entry);
if (fs.existsSync(dir)) {
const base = path.basename(entry);
const files = fs.readdirSync(dir);
const found = files.find((file) => file.replace(/\.(js|ts)$/, '') === base);
if (found) return path.join(dir, found);
}
}
return null;
}
export function hookTransformIndexHtmlPlugin(): Plugin {
return {
name: 'hook-sveltekit-transformIndexHtml',
configureServer(server) {
const hookConfig = (globalThis as any).__hook_hooks
hookConfig.transformIndexHtml = server.transformIndexHtml
const handler =
`async ({ event, resolve }) => await resolve(event, {
transformPageChunk: async ({ html }) => {
const transform = globalThis.__hook_hooks.transformIndexHtml
if (!transform) return html
// vite has a lot of extra plugins we have to defeat them
// look at devHtmlTransformFn
let patchedHtml = await globalThis.__hook_hooks.transformIndexHtml('', '<html><head></head><body></body></html>', '');
patchedHtml = patchedHtml.replace('<script type="module" src="/@vite/client"></script>', '');
// now we patch the changes
return html
.replace('</head>', patchedHtml.match(/<head>([\\s\\S]*?)<\\/head>/)?.[0] || '</head>')
.replace('</body>', patchedHtml.match(/<body>([\\s\\S]*?)<\\/body>/)?.[0] || '</body>');
},
});
`
let combinedHooksContent
const currentHook = path.resolve(hookConfig.currentHook)
const previousHook = resolve_entry(hookConfig.previousHook)
if (previousHook && fs.existsSync(previousHook)) {
const currentDir = path.dirname(currentHook)
const hookPath = path.relative(currentDir, previousHook).replace(/\\/g, '/')
combinedHooksContent = `
import { sequence } from '@sveltejs/kit/hooks';
import * as userHooks from ${JSON.stringify(hookPath)};
const customHandle = ${handler}
export const handle = sequence(customHandle, userHooks.handle);
export default { ...userHooks, handle };
`
} else {
combinedHooksContent = `
export const handle = ${handler}
`;
}
combinedHooksContent = `
// this file is auto-generated
${combinedHooksContent}
`
fs.mkdirSync(path.dirname(hookConfig.currentHook), { recursive: true })
fs.writeFileSync(hookConfig.currentHook, combinedHooksContent)
},
};
} Please provide a way for plugins to add hooks, that would solve this problem on the plugins part. |
Beta Was this translation helpful? Give feedback.
-
I am writing a Vite plugin, and I notice that
transformIndexHtml
is never called when using that plugin with SvelteKit. Is there a hook in Vite plugin that I can use to transformapp.html
?I prefer not to do the transformation in
hooks.server.js
, as I want this to be done once in build time, but I cannot find the hook I need right now.Beta Was this translation helpful? Give feedback.
All reactions