parseDir.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. import fs from "node:fs/promises";
  2. import {constants} from "node:fs";
  3. import path from "path";
  4. import htmlMinifier from "html-minifier-terser";
  5. import esbuild from "esbuild";
  6. export default async (dir)=>{
  7. const indexFile = await findIndex(dir);
  8. if(!indexFile) return;
  9. let data = {};
  10. if(path.extname(indexFile) === ".neovan"){
  11. data = await getNeovanData(indexFile);
  12. }else{
  13. data = await parseHtml(indexFile);
  14. }
  15. const bundle = await createBundle(data);
  16. writeFile(dir, bundle);
  17. fs.rm(path.join(dir, "tmp/"), {recursive: true, force: true});
  18. }
  19. const findIndex = async (dir)=>{
  20. const neovanPath = path.join(dir, "index.neovan");
  21. const htmlPath = path.join(dir, "index.html");
  22. const [neovan, html] = await Promise.allSettled([
  23. fs.access(path.join(dir, "index.neovan"), constants.F_OK),
  24. fs.access(path.join(dir, "index.html"), constants.F_OK)
  25. ]);
  26. if(neovan.status === "fulfilled") return neovanPath;
  27. if(html.status === "fulfilled") return htmlPath;
  28. return null;
  29. }
  30. const getNeovanData = async (index)=>{
  31. const neovan = await fs.readFile(index, "utf-8");
  32. const parentPath = path.dirname(index);
  33. const html = neovan.slice(neovan.indexOf("<contents>") + 10, neovan.indexOf("</contents>"));
  34. const css = neovan.slice(neovan.indexOf("<style>") + 7, neovan.indexOf("</style>"));
  35. const js = neovan.slice(neovan.indexOf("<script>") + 8, neovan.indexOf("</script>"));
  36. const cssFile = path.join(parentPath, `tmp/${path.basename(index, ".neovan")}.css`);
  37. const jsFile = path.join(parentPath, `tmp/${path.basename(index, ".neovan")}.js`);
  38. await fs.mkdir(path.join(parentPath, "tmp/"));
  39. await Promise.all([
  40. css === "" ? null : fs.writeFile(cssFile, css),
  41. js === "" ? null : fs.writeFile(jsFile, js)
  42. ]);
  43. return {
  44. html: html,
  45. css: cssFile,
  46. js: jsFile
  47. };
  48. }
  49. const parseHtml = async (index)=>{
  50. const parentPath = path.dirname(index);
  51. const basename = path.basename(index, ".html");
  52. return {
  53. html: await fs.readFile(index, "utf-8"),
  54. css: path.join(parentPath, `${basename}.css`),
  55. js: path.join(parentPath, `${basename}.js`)
  56. };
  57. }
  58. const createBundle = async (data)=>{
  59. const entryPoints = [];
  60. if(data.css) entryPoints.push(data.css);
  61. if(data.js) entryPoints.push(data.js);
  62. const esbuildProm = esbuild.build({
  63. entryPoints: entryPoints,
  64. bundle: true,
  65. minify: true,
  66. write: false,
  67. outdir: "/"
  68. });
  69. const htmlProm = htmlMinifier.minify(data.html, {
  70. collapseBooleanAttributes: true,
  71. collapseInlineTagWhitespace: true,
  72. collapseWhitespace: true,
  73. decodeEntities: true,
  74. html5: true,
  75. includeAutoGeneratedTags: false,
  76. noNewlinesBeforeTagClose: true,
  77. removeComments: true,
  78. useShortDoctype: true
  79. });
  80. const [buildData, html] = await Promise.all([esbuildProm, htmlProm]);
  81. const comps = {html: html};
  82. for(let i = 0; i < buildData.outputFiles.length; i++){
  83. const ext = path.extname(buildData.outputFiles[i].path).replace(".", "");
  84. comps[ext] = buildData.outputFiles[i].text;
  85. }
  86. return mergeFiles(comps);
  87. }
  88. const mergeFiles = (comps)=>{
  89. const cssIndex = comps.html.indexOf("</head>");
  90. const html = `${comps.html.slice(0, cssIndex)}<style>${comps.css}</style>${comps.html.slice(cssIndex)}`;
  91. const jsIndex = html.indexOf("</body>");
  92. return `${html.slice(0, jsIndex)}<script>${comps.js}</script>${html.slice(jsIndex)}`;
  93. }
  94. const writeFile = async (dir, bundle)=>{
  95. const writeDir = dir.replace("routes", ".build");
  96. await fs.mkdir(writeDir, {recursive: true});
  97. await fs.writeFile(`${writeDir}/index.html`, bundle);
  98. }