zip.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. import fs from 'node:fs';
  2. import path from 'node:path';
  3. import JSZip from 'jszip';
  4. import { glob } from 'glob';
  5. function switchToStaticScripts(htmlContent) {
  6. // Look for the module script block and capture indentation
  7. const moduleScriptPattern = /(\s*)<script type="module">([\s\S]*?)<\/script>/;
  8. const match = htmlContent.match(moduleScriptPattern);
  9. if (!match) return htmlContent;
  10. const indentation = match[1].replace(/\n/g, '');
  11. let moduleCode = match[2];
  12. let scriptPaths = [];
  13. // Replace main reveal.js import
  14. if (moduleCode.includes("import Reveal from 'reveal.js';")) {
  15. scriptPaths.push('dist/reveal.js');
  16. moduleCode = moduleCode.replace("import Reveal from 'reveal.js';", '');
  17. }
  18. // Replace plugin imports
  19. moduleCode = moduleCode.replace(
  20. /import\s+(\w+)\s+from\s+'reveal\.js\/plugin\/(\w+)';/g,
  21. (match, pluginVar, pluginName) => {
  22. scriptPaths.push(`dist/plugin/${pluginName}.js`);
  23. return '';
  24. }
  25. );
  26. // Clean up any remaining empty lines and trim
  27. moduleCode = moduleCode.replace(/^\s*[\r\n]/gm, '').trim();
  28. const scriptTags = scriptPaths.map((path) => `${indentation}<script src="${path}"></script>`);
  29. const replacement =
  30. '\n\n' +
  31. scriptTags.join('\n') +
  32. `\n${indentation}<script>\n${indentation}\t${moduleCode}\n${indentation}</script>`;
  33. return htmlContent.replace(moduleScriptPattern, replacement);
  34. }
  35. /**
  36. * Replace paths to dynamic CSS/SCSS files with static ones.
  37. */
  38. function switchToStaticStyles(htmlContent) {
  39. // Replace /css/* links with /dist/*
  40. htmlContent = htmlContent.replace(/href="css\/([^"]+\.(css|scss))"/g, (match, filePath) => {
  41. const cssPath = filePath.replace(/\.scss$/, '.css');
  42. return `href="dist/${cssPath}"`;
  43. });
  44. // Replace /plugin/* links with /dist/plugin/*
  45. htmlContent = htmlContent.replace(/href="plugin\/([^"]+\.(css|scss))"/g, (match, filePath) => {
  46. const cssPath = filePath.replace(/\.scss$/, '.css');
  47. return `href="dist/plugin/${cssPath}"`;
  48. });
  49. return htmlContent;
  50. }
  51. async function main() {
  52. // Parse command line arguments for HTML file target
  53. const args = process.argv.slice(2);
  54. const htmlTarget = args.length > 0 ? args[0] : 'index.html';
  55. // Ensure the target has ./ prefix if it's a relative path
  56. const targetFile = htmlTarget.startsWith('./') ? htmlTarget : `./${htmlTarget}`;
  57. console.log(`Packaging presentation with target file: ${targetFile}`);
  58. // Read the HTML file
  59. let htmlContent = fs.readFileSync(targetFile, 'utf8');
  60. // Switch from Vite's dynamic imports to static ones so that
  61. // this presentation can run anywhere (including offline via
  62. // file:// protocol)
  63. htmlContent = switchToStaticScripts(htmlContent);
  64. htmlContent = switchToStaticStyles(htmlContent);
  65. const zip = new JSZip();
  66. const filesToInclude = ['./dist/**', './*/*.md'];
  67. if (fs.existsSync('./lib')) filesToInclude.push('./lib/**');
  68. if (fs.existsSync('./images')) filesToInclude.push('./images/**');
  69. if (fs.existsSync('./slides')) filesToInclude.push('./slides/**');
  70. // Add the modified HTML file first
  71. const htmlFileName = htmlTarget.replace(/\.\//, '');
  72. zip.file(htmlFileName, htmlContent);
  73. for (const pattern of filesToInclude) {
  74. const files = glob.sync(pattern, {
  75. nodir: true,
  76. dot: false,
  77. ignore: ['./examples/**', './test/**'],
  78. });
  79. for (const file of files) {
  80. const filePath = path.resolve(file);
  81. const relativePath = path.relative(process.cwd(), filePath);
  82. const fileData = fs.readFileSync(filePath);
  83. zip.file(relativePath, fileData);
  84. }
  85. }
  86. const content = await zip.generateAsync({ type: 'nodebuffer' });
  87. const zipFileName = `presentation.zip`;
  88. fs.writeFileSync(zipFileName, content);
  89. console.log(`Presentation packaged successfully: ${zipFileName}`);
  90. }
  91. main().catch((error) => {
  92. console.error('Error packaging presentation:', error);
  93. process.exit(1);
  94. });