Your all (<script src='index.js'></script> tags replace from tags and append at </body> end tag
This is a vite plugin coded by Pkfan. This plugin work with follwoing steps:
- Get html compile result from vite
- Extract/Remove all ( <head>content</head> ) from html
- Extract/Remove all ( <script src=""></script> ) from ( <head>content</head> )
- Append all ( <script src=""></script> ) into ( </body> ) end tag
- return vite final ( <html> content </html>) result and write into target file
// appendHeadScriptsAtBody.js
function appendHeadScriptsAtBody() {
return {
name: "append-head-scripts-at-body",
transformIndexHtml(html, ctx) {
const replaceScriptFromHead = (htmlHead) => {
let script = "";
const regExpForScriptTagsSelection = new RegExp(
"<script[^<>]+[^<>]+></script>"
);
let htmlHeadResult = htmlHead.replace(
regExpForScriptTagsSelection,
(match) => {
script = `\n\t\t${match}`;
return "";
}
);
return { script, htmlHeadResult };
};
const replaceAllScriptFromHead = (htmlHead) => {
let scripts = "";
let htmlFinalHead = htmlHead;
while (true) {
let { script, htmlHeadResult } = replaceScriptFromHead(htmlFinalHead);
if (!(script && script.trim() != "")) {
break;
}
htmlFinalHead = htmlHeadResult;
scripts = scripts + script;
}
return { scripts, htmlFinalHead };
};
const extractHeadTagsFromHtml = (htmlInput) => {
const regExpForHeadTagsSelection = new RegExp("<head>(.|\n)*?</head>");
let htmlHead = "";
let htmlResultWithBodyTagsOnly = htmlInput.replace(
regExpForHeadTagsSelection,
(match) => {
htmlHead = match;
return "";
}
);
return { htmlHead, htmlResultWithBodyTagsOnly };
};
const htmlTagStartingLength = (htmlResultWithBodyTagsOnly) => {
// e.g [ ( <html lang="en"> ) of length 17 ] and [ ( <html> ) of length 6 ]
const htmlStartTag = new RegExp("<html(.|\n)*?>").exec(
htmlResultWithBodyTagsOnly
)[0];
const indexOfHtmlstartingTag =
htmlResultWithBodyTagsOnly.indexOf(htmlStartTag);
return htmlStartTag.length + indexOfHtmlstartingTag;
};
const concatHeadIntoHtml = ({
htmlFinalHead,
htmlResultWithBodyTagsOnly,
}) => {
const htmlStartTagLength = htmlTagStartingLength(
htmlResultWithBodyTagsOnly
);
const startSlice = htmlResultWithBodyTagsOnly.slice(
0,
htmlStartTagLength
);
const endSlice = htmlResultWithBodyTagsOnly.slice(htmlStartTagLength);
return `${startSlice}\n${htmlFinalHead}\n${endSlice}`;
};
const appendScriptsIntoHtmlEndBodyTag = ({ htmlResult, scripts }) =>
htmlResult.replace("</body>", `\t\t${scripts}\n\t</body>`);
if (ctx.chunk.isEntry) {
let { htmlHead, htmlResultWithBodyTagsOnly } =
extractHeadTagsFromHtml(html);
const { scripts, htmlFinalHead } = replaceAllScriptFromHead(htmlHead);
const htmlResult = concatHeadIntoHtml({
htmlFinalHead,
htmlResultWithBodyTagsOnly,
});
html = appendScriptsIntoHtmlEndBodyTag({ htmlResult, scripts });
}
return html;
},
};
}
export default appendHeadScriptsAtBody;
// vite.config.js
import { defineConfig } from "vite";
import appendHeadScriptsAtBody from "./appendHeadScriptsAtBody";
export default defineConfig({
plugins: [appendHeadScriptsAtBody()],
// other vite code here
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="../js/counter.js"></script>
<title>about App</title>
<script type="module" crossorigin src="/ass/main-26c2ddec.js"></script>
<link rel="stylesheet" href="/ass/main-d0964974.css">
</head>
<body>
<div id="app"></div>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>about App</title>
<link rel="stylesheet" href="/ass/main-d0964974.css">
</head>
<body>
<div id="app"></div>
<script src="../js/counter.js"></script>
<script type="module" crossorigin src="/ass/main-26c2ddec.js"></script>
</body>
</html>