import svelte from 'rollup-plugin-svelte'; import image from '@rollup/plugin-image'; import resolve from '@rollup/plugin-node-resolve'; import terser from "@rollup/plugin-terser"; import postcss from 'rollup-plugin-postcss' import htmlTemplate from 'rollup-plugin-generate-html-template'; import { readFile, writeFile } from 'fs'; import { basename } from 'path'; import { promisify } from 'util'; import zlib from 'node:zlib'; import { VERSION, } from 'rollup'; import { pascalCase } from "pascal-case"; import mime from 'mime'; function hexdump(buffer) { let lines = []; for (let i = 0; i < buffer.length; i += 16) { let block = buffer.slice(i, i + 16); // cut buffer into blocks of 16 let hexArray = []; for (let value of block) { hexArray.push("0x" + value.toString(16).padStart(2, "0")); } let hexString = hexArray.join(", "); let line = ` ${hexString}`; lines.push(line); } return lines.join(",\n"); } function cppCompressed(input, fileName, contentType) { return new Promise((resolve, reject) => zlib.gzip(input, { level: zlib.constants.Z_BEST_COMPRESSION }, function (error, result) { if (error) { reject(err); } console.info(fileName + " compressed " + result.length + " bytes"); const array = hexdump(result); const src = `/* * Binary array for the Web UI. * Gzip is used for smaller size and improved speeds. */ // Autogenerated do not edit!! const uint16_t ${fileName.replace(".","_").toUpperCase()}_L = ${result.length}; const uint8_t ${fileName.replace(".","_").toUpperCase()}[] PROGMEM = { ${array} }; void serve${pascalCase(fileName)}(AsyncWebServerRequest* request) { AsyncWebServerResponse *response = request->beginResponse_P(200, "${contentType || mime.getType(fileName)}", ${fileName.replace(".","_").toUpperCase()}, ${fileName.replace(".","_").toUpperCase()}_L); response->addHeader(F("Content-Encoding"), "gzip"); request->send(response); } `; resolve(src); })); } const isFunction = (arg) => typeof arg === 'function'; const isRegExp = (arg) => Object.prototype.toString.call(arg) === '[object RegExp]'; const readFilePromise = promisify(readFile); const writeFilePromise = promisify(writeFile); function isOutputChunk(file) { return typeof file.code === 'string'; } function getOutputFileContent(outputFileName, outputFile, outputOptions) { if (isOutputChunk(outputFile)) { let source; source = outputFile.code; if (outputOptions.sourcemap && outputFile.map) { const url = outputOptions.sourcemap === 'inline' ? outputFile.map.toUrl() : `${basename(outputFileName)}.map`; source += `//# source` + `MappingURL=${url}\n`; } return source; } else { return typeof outputFile.source === 'string' ? outputFile.source : // just to be sure, as it is typed string | Uint8Array in rollup 2.0.0 Buffer.from(outputFile.source); } } function cpp(options = {}) { const mapFileName = isFunction(options.fileName) ? options.fileName : (fileName) => fileName + (options.fileName || '.cpp'); const plugin = { name: 'cpp', generateBundle(outputOptions, bundle, isWrite) { if (!isWrite) return; return Promise.all(Object.keys(bundle) .map(fileName => { const fileEntry = bundle[fileName]; // file name filter option check const fileNameFilter = options.filter || /\.(js|mjs|json|css|html)$/; if (isRegExp(fileNameFilter) && !fileName.match(fileNameFilter)) { return Promise.resolve(); } if (isFunction(fileNameFilter) && !fileNameFilter(fileName)) { return Promise.resolve(); } const fileContent = getOutputFileContent(fileName, fileEntry, outputOptions); // minSize option check if (options.minSize && options.minSize > fileContent.length) { return Promise.resolve(); } return Promise.resolve(cppCompressed(fileContent, fileName)) .then(compressedContent => { writeFilePromise(mapFileName(fileName), compressedContent); return null; }) .catch((err) => { console.error(err); return Promise.reject('[rollup-plugin-cpp] Error compressing file ' + fileName); }); }) .concat([ (() => { if (!options.additionalFiles || !options.additionalFiles.length) return Promise.resolve(); const compressAdditionalFiles = () => Promise.all(options.additionalFiles.map(filePath => readFilePromise(filePath) .then(fileContent => cppCompressed(fileContent, basename(filePath))) .then(compressedContent => { return writeFilePromise(mapFileName(filePath), compressedContent); }) .catch((err) => { return Promise.reject('[rollup-plugin-cpp] Error compressing additional file ' + filePath + '\n' + err); }))); // additional files can be processed outside of rollup after a delay // for older plugins or plugins that write to disk (curcumventing rollup) without awaiting const additionalFilesDelay = options.additionalFilesDelay || (VERSION >= '2.0.0' ? 0 : 2000); if (additionalFilesDelay) { setTimeout(compressAdditionalFiles, additionalFilesDelay); return Promise.resolve(); } else { return compressAdditionalFiles(); } })(), ])); }, }; return plugin; } export default { input: 'src/main.js', output: { format: 'iife', file: './dist/index.js', name: 'app' }, plugins: [ svelte({}), resolve(), image(), postcss({ extract: 'bundle.css' }), htmlTemplate({ template: 'src/template.html', target: 'index.html', }), terser(), cpp({ additionalFiles: ['dist/index.html'], fileName: function(a) { return "../src/ui_" + basename(a).replace(".", "_") + ".h"; } }) ] };