/*
function formatting:
* for your static HTML constructor function,
* return the pure HTML that you'd like to add
export function createHeader () {
return `
This is my header!
`
}
* if generating an element using DOM manipulations such as document.createElement
* you must pass "document" as a parameter
export function createToolbar(document) {
const wrapper = document.createElement("nav");
const navbarLinks = ["/index.html", "/about.html", "/etc.html"]
for (const link of navbarLinks) {
const element = document.createElement("a")
element.innerHTML = link
element.href = link.substring(1, link.length - 5);
wrapper.appendChild("element");
}
return wrapper.outerHTML
}
* NOTE - you cant return a document fragment or a div! it has to be a string,
* so use .innerHTML or .outerHTML when you return the object!
*/
/*
options formatting:
[DIRECTORY] - file path to the static return function
[FUNCTION NAME] - if multiple functions present in one file, the name of the specific function
[PLACEHOLDER] - where do you want the HTML to appear? ex. "#toolbar-placeholder"
plugins: [viteVanillaPrerenderer({
moduleGroups: [
// if there are multiple "export function functionName() {}"s in one file
{
path: [DIRECTORY],
modules: [
{
fname: [FUNCTION NAME],
selector: [PLACEHOLDER]
},
{
fname: [FUNCTION NAME],
selector: [PLACEHOLDER]
},
]
},
// if there's only one function in the file path
{
path: [DIRECTORY],
selector: [PLACEHOLDER],
fname: [FUNCTION NAME]
},
]
})
]
additional functionality:
* for any given fname, you can set "outer: true" if you'd like to replace the outerHTML instead of the innerHTML
* if your js file has export ***default*** function fName() {...}, then you don't need an fname
* if you have multiple placeholders you want to fill, you can make [PLACEHOLDER] an array!
- first, your input function must return an array of strings of the same length as your [PLACEHOLDER] array
- then, you map them in parallel with each other!
- index[0] of your returned array will be appended to index[0] of your placeholder array!
* any questions or anything feel free to email me askdasybells@protonmail.com!
* hopefully one day i can make proper documentation for this and actually publish it or something :)
*/
// THE ACTUAL CODE:
import { JSDOM } from "jsdom";
import { resolve } from "path";
export default function viteVanillaPrerenderer(options) {
const moduleGroups = options?.moduleGroups || [];
return {
name: "vite-vanilla-prerenderer",
async transformIndexHtml(html, ctx) {
if (moduleGroups.length === 0) {
return html;
}
const dom = new JSDOM(html);
const document = dom.window.document;
function appendToSelectors(module, renderFunction, groupPath) {
if (Array.isArray(module.selector)) {
const renderedArray = renderFunction(document);
if (!Array.isArray(renderedArray)) {
console.error(
`[Vite-Vanilla-Prerenderer] Error: function from ${groupPath} did not return an array.`
);
return;
}
module.selector.forEach((selector, index) => {
if (index < renderedArray.length) {
const element = document.querySelector(`${selector}`);
if (!element) {
return;
}
element.innerHTML = renderedArray[index];
}
});
} else if (module.selector) {
const element = document.querySelector(module.selector);
if (!element) {
return;
}
const renderedHtml = renderFunction(document);
if (module.outer) {
element.outerHTML = renderedHtml;
} else {
element.innerHTML = renderedHtml;
}
}
}
for (const group of moduleGroups) {
try {
if (!group.path) {
console.warn(
`[Vite-Vanilla-Prerenderer] Skipping a group in config because it's missing 'path'.`
);
continue;
}
const scriptPath = resolve(process.cwd(), group.path);
const loadedModule = await import(scriptPath);
if (group.modules) {
group.modules.forEach((module) => {
const moduleName = module.fname || "default";
const renderFunction = loadedModule[moduleName];
if (typeof renderFunction === "function") {
appendToSelectors(module, renderFunction, group.path);
} else {
console.warn(
`[Vite-Vanilla-Prerenderer] Export ${moduleName} not found in ${group.path}.`
);
}
});
} else if (group.selector) {
const moduleName = group.fname || "default";
const renderFunction = loadedModule[moduleName];
if (typeof renderFunction === "function") {
appendToSelectors(group, renderFunction, group.path);
} else {
console.warn(
`[Vite-Vanilla-Prerenderer] Export ${moduleName} not found in ${group.path}.`
);
}
}
} catch (error) {
console.error(
`[Vite-Vanilla-Prerenderer] Failed to process module group for path '${group.path}'. Error: ${error.message}`
);
}
}
return dom.serialize();
},
};
}