parseComponent.js 3.9 KB

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