Fork of the excellent esp8266-react - https://github.com/rjwats/esp8266-react
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

122 lines
3.9 KiB

  1. const { resolve, relative, sep } = require('path');
  2. const { readdirSync, existsSync, unlinkSync, readFileSync, createWriteStream } = require('fs');
  3. var zlib = require('zlib');
  4. var mime = require('mime-types');
  5. const ARDUINO_INCLUDES = "#include <Arduino.h>\n\n";
  6. function getFilesSync(dir, files = []) {
  7. readdirSync(dir, { withFileTypes: true }).forEach(entry => {
  8. const entryPath = resolve(dir, entry.name);
  9. if (entry.isDirectory()) {
  10. getFilesSync(entryPath, files);
  11. } else {
  12. files.push(entryPath);
  13. }
  14. })
  15. return files;
  16. }
  17. function coherseToBuffer(input) {
  18. return Buffer.isBuffer(input) ? input : Buffer.from(input);
  19. }
  20. function cleanAndOpen(path) {
  21. if (existsSync(path)) {
  22. unlinkSync(path);
  23. }
  24. return createWriteStream(path, { flags: "w+" });
  25. }
  26. class ProgmemGenerator {
  27. constructor(options = {}) {
  28. const { outputPath, bytesPerLine = 20, indent = " ", includes = ARDUINO_INCLUDES } = options;
  29. this.options = { outputPath, bytesPerLine, indent, includes };
  30. }
  31. apply(compiler) {
  32. compiler.hooks.emit.tapAsync(
  33. { name: 'ProgmemGenerator' },
  34. (compilation, callback) => {
  35. const { outputPath, bytesPerLine, indent, includes } = this.options;
  36. const fileInfo = [];
  37. const writeStream = cleanAndOpen(resolve(compilation.options.context, outputPath));
  38. try {
  39. const writeIncludes = () => {
  40. writeStream.write(includes);
  41. }
  42. const writeFile = (relativeFilePath, buffer) => {
  43. const variable = "ESP_REACT_DATA_" + fileInfo.length;
  44. const mimeType = mime.lookup(relativeFilePath);
  45. var size = 0;
  46. writeStream.write("const uint8_t " + variable + "[] PROGMEM = {");
  47. const zipBuffer = zlib.gzipSync(buffer);
  48. zipBuffer.forEach((b) => {
  49. if (!(size % bytesPerLine)) {
  50. writeStream.write("\n");
  51. writeStream.write(indent);
  52. }
  53. writeStream.write("0x" + ("00" + b.toString(16).toUpperCase()).substr(-2) + ",");
  54. size++;
  55. });
  56. if (size % bytesPerLine) {
  57. writeStream.write("\n");
  58. }
  59. writeStream.write("};\n\n");
  60. fileInfo.push({
  61. uri: '/' + relativeFilePath.replace(sep, '/'),
  62. mimeType,
  63. variable,
  64. size
  65. });
  66. };
  67. const writeFiles = () => {
  68. // process static files
  69. const buildPath = compilation.options.output.path;
  70. for (const filePath of getFilesSync(buildPath)) {
  71. const readStream = readFileSync(filePath);
  72. const relativeFilePath = relative(buildPath, filePath);
  73. writeFile(relativeFilePath, readStream);
  74. }
  75. // process assets
  76. const { assets } = compilation;
  77. Object.keys(assets).forEach((relativeFilePath) => {
  78. writeFile(relativeFilePath, coherseToBuffer(assets[relativeFilePath].source()));
  79. });
  80. }
  81. const generateWWWClass = () => {
  82. return `typedef std::function<void(const String& uri, const String& contentType, const uint8_t * content, size_t len)> RouteRegistrationHandler;
  83. class WWWData {
  84. ${indent}public:
  85. ${indent.repeat(2)}static void registerRoutes(RouteRegistrationHandler handler) {
  86. ${fileInfo.map(file => `${indent.repeat(3)}handler("${file.uri}", "${file.mimeType}", ${file.variable}, ${file.size});`).join('\n')}
  87. ${indent.repeat(2)}}
  88. };
  89. `;
  90. }
  91. const writeWWWClass = () => {
  92. writeStream.write(generateWWWClass());
  93. }
  94. writeIncludes();
  95. writeFiles();
  96. writeWWWClass();
  97. writeStream.on('finish', () => {
  98. callback();
  99. });
  100. } finally {
  101. writeStream.end();
  102. }
  103. }
  104. );
  105. }
  106. }
  107. module.exports = ProgmemGenerator;