single-file-node.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. /* global require, exports, Buffer */
  2. const fs = require("fs");
  3. const jsdom = require("jsdom");
  4. const dataUri = require("strong-data-uri");
  5. const iconv = require("iconv-lite");
  6. const request = require("request-promise-native");
  7. const { JSDOM } = jsdom;
  8. const USER_AGENT = "";
  9. const DocUtilCore = eval(fs.readFileSync("./lib/single-file/util/doc-util-core.js").toString());
  10. const SingleFileCore = eval(fs.readFileSync("./lib/single-file/single-file-core.js").toString());
  11. const cssTree = eval(fs.readFileSync("./lib/single-file/vendor/css-tree.js").toString());
  12. const docHelper = eval(fs.readFileSync("./lib/single-file/util/doc-helper.js").toString());
  13. const srcsetParser = eval(fs.readFileSync("./lib/single-file/vendor/html-srcset-parser.js").toString());
  14. const modules = {
  15. docHelper: docHelper,
  16. srcsetParser: srcsetParser,
  17. cssMinifier: eval(fs.readFileSync("./lib/single-file/vendor/css-minifier.js").toString()),
  18. htmlMinifier: eval(fs.readFileSync("./lib/single-file/modules/html-minifier.js").toString()),
  19. fontsMinifier: eval(fs.readFileSync("./lib/single-file/modules/css-fonts-minifier.js").toString()),
  20. fontsAltMinifier: eval(fs.readFileSync("./lib/single-file/modules/css-fonts-alt-minifier.js").toString()),
  21. cssRulesMinifier: eval(fs.readFileSync("./lib/single-file/modules/css-rules-minifier.js").toString()),
  22. matchedRules: eval(fs.readFileSync("./lib/single-file/modules/css-matched-rules.js").toString()),
  23. mediasMinifier: eval(fs.readFileSync("./lib/single-file/modules/css-medias-alt-minifier.js").toString()),
  24. imagesAltMinifier: eval(fs.readFileSync("./lib/single-file/modules/html-images-alt-minifier.js").toString()),
  25. serializer: eval(fs.readFileSync("./lib/single-file/modules/html-serializer.js").toString())
  26. };
  27. modules.fontsAltMinifier.cssTree = cssTree;
  28. modules.fontsMinifier.cssTree = cssTree;
  29. modules.fontsMinifier.fontPropertyParser = eval(fs.readFileSync("./lib/single-file/vendor/css-font-property-parser.js").toString());
  30. modules.fontsMinifier.docHelper = docHelper;
  31. modules.matchedRules.cssTree = cssTree;
  32. modules.mediasMinifier.cssTree = cssTree;
  33. modules.mediasMinifier.mediaQueryParser = eval(fs.readFileSync("./lib/single-file/vendor/css-media-query-parser.js").toString());
  34. modules.cssRulesMinifier.cssTree = cssTree;
  35. modules.imagesAltMinifier.srcsetParser = srcsetParser;
  36. const domUtil = {
  37. getContent, parseDocContent, parseSVGContent, isValidFontUrl, getContentSize, digestText
  38. };
  39. exports.getClass = () => {
  40. const DocUtil = DocUtilCore.getClass(modules, domUtil);
  41. return SingleFileCore.getClass(DocUtil, cssTree);
  42. };
  43. function parseDocContent(content, baseURI) {
  44. const doc = (new JSDOM(content, {
  45. contentType: "text/html"
  46. })).window.document;
  47. let baseElement = doc.querySelector("base");
  48. if (!baseElement || !baseElement.getAttribute("href")) {
  49. if (baseElement) {
  50. baseElement.remove();
  51. }
  52. baseElement = doc.createElement("base");
  53. baseElement.setAttribute("href", baseURI);
  54. doc.head.insertBefore(baseElement, doc.head.firstChild);
  55. }
  56. return doc;
  57. }
  58. function parseSVGContent(content) {
  59. return (new JSDOM(content, {
  60. contentType: "image/svg+xml"
  61. })).window.document;
  62. }
  63. async function digestText(/* algo, text */) {
  64. // TODO
  65. return 0;
  66. }
  67. function getContentSize(content) {
  68. // TODO: check
  69. return Buffer.byteLength(content, "utf-8");
  70. }
  71. function isValidFontUrl(/* urlFunction */) {
  72. // TODO?
  73. return true;
  74. }
  75. async function getContent(resourceURL, options) {
  76. const requestOptions = {
  77. method: "GET",
  78. uri: resourceURL,
  79. resolveWithFullResponse: true,
  80. encoding: null,
  81. headers: {
  82. "User-Agent": USER_AGENT
  83. }
  84. };
  85. let resourceContent;
  86. try {
  87. resourceContent = await request(requestOptions);
  88. } catch (e) {
  89. return options.asDataURI ? "data:base64," : "";
  90. }
  91. const contentType = resourceContent.headers["content-type"];
  92. if (options && options.asDataURI) {
  93. try {
  94. return { data: dataUri.encode(resourceContent.body, contentType), resourceURL };
  95. } catch (e) {
  96. return { data: "data:base64,", resourceURL };
  97. }
  98. } else {
  99. const matchCharset = contentType && contentType.match(/\s*;\s*charset\s*=\s*(.*)(;|$)/i);
  100. let charset = "utf-8";
  101. if (matchCharset && matchCharset[1]) {
  102. charset = matchCharset[1];
  103. try {
  104. return { data: iconv.decode(resourceContent.body, charset), charset };
  105. } catch (e) {
  106. return { data: resourceContent.body.toString("utf8"), charset: "utf8" };
  107. }
  108. } else {
  109. return { data: resourceContent.body.toString("utf8"), charset };
  110. }
  111. }
  112. }