Просмотр исходного кода

update 'npm run package' task to also swap out dynamic imports for static

Hakim El Hattab 2 недель назад
Родитель
Сommit
2cba3fb7db
1 измененных файлов с 108 добавлено и 32 удалено
  1. 108 32
      zip.js

+ 108 - 32
zip.js

@@ -1,40 +1,116 @@
 import fs from 'node:fs';
 import path from 'node:path';
 import JSZip from 'jszip';
-import {glob} from 'glob';
+import { glob } from 'glob';
+
+function switchToStaticScripts(htmlContent) {
+	// Look for the module script block and capture indentation
+	const moduleScriptPattern = /(\s*)<script type="module">([\s\S]*?)<\/script>/;
+	const match = htmlContent.match(moduleScriptPattern);
+
+	if (!match) return htmlContent;
+
+	const indentation = match[1].replace(/\n/g, '');
+	let moduleCode = match[2];
+	let scriptPaths = [];
+
+	// Replace main reveal.js import
+	if (moduleCode.includes("import Reveal from 'reveal.js';")) {
+		scriptPaths.push('dist/reveal.js');
+		moduleCode = moduleCode.replace("import Reveal from 'reveal.js';", '');
+	}
+
+	// Replace plugin imports
+	moduleCode = moduleCode.replace(
+		/import\s+(\w+)\s+from\s+'reveal\.js\/plugin\/(\w+)';/g,
+		(match, pluginVar, pluginName) => {
+			scriptPaths.push(`dist/plugin/${pluginName}.js`);
+			return '';
+		}
+	);
+
+	// Clean up any remaining empty lines and trim
+	moduleCode = moduleCode.replace(/^\s*[\r\n]/gm, '').trim();
+
+	const scriptTags = scriptPaths.map((path) => `${indentation}<script src="${path}"></script>`);
+	const replacement =
+		'\n\n' +
+		scriptTags.join('\n') +
+		`\n${indentation}<script>\n${indentation}\t${moduleCode}\n${indentation}</script>`;
+
+	return htmlContent.replace(moduleScriptPattern, replacement);
+}
+
+/**
+ * Replace paths to dynamic CSS/SCSS files with static ones.
+ */
+function switchToStaticStyles(htmlContent) {
+	// Replace /css/* links with /dist/*
+	htmlContent = htmlContent.replace(/href="css\/([^"]+\.(css|scss))"/g, (match, filePath) => {
+		const cssPath = filePath.replace(/\.scss$/, '.css');
+		return `href="dist/${cssPath}"`;
+	});
+
+	// Replace /plugin/* links with /dist/plugin/*
+	htmlContent = htmlContent.replace(/href="plugin\/([^"]+\.(css|scss))"/g, (match, filePath) => {
+		const cssPath = filePath.replace(/\.scss$/, '.css');
+		return `href="dist/plugin/${cssPath}"`;
+	});
+
+	return htmlContent;
+}
 
 async function main() {
-  const zip = new JSZip();
-  const filesToInclude = [
-    './index.html',
-    './dist/**',
-    './*/*.md'
-  ];
-
-  if (fs.existsSync('./lib')) filesToInclude.push('./lib/**');
-  if (fs.existsSync('./images')) filesToInclude.push('./images/**');
-  if (fs.existsSync('./slides')) filesToInclude.push('./slides/**');
-
-  for (const pattern of filesToInclude) {
-    const files = glob.sync(pattern, {
-      nodir: true,
-      dot: true,
-      ignore: ['./examples/**', './test/**']
-    });
-    for (const file of files) {
-      const filePath = path.resolve(file);
-      const relativePath = path.relative(process.cwd(), filePath);
-      const fileData = fs.readFileSync(filePath);
-      zip.file(relativePath, fileData);
-    }
-  }
-
-  const content = await zip.generateAsync({ type: 'nodebuffer' });
-  fs.writeFileSync('reveal-js-presentation.zip', content);
-  console.log('Presentation packaged successfully: reveal-js-presentation.zip');
+	// Parse command line arguments for HTML file target
+	const args = process.argv.slice(2);
+	const htmlTarget = args.length > 0 ? args[0] : 'index.html';
+
+	// Ensure the target has ./ prefix if it's a relative path
+	const targetFile = htmlTarget.startsWith('./') ? htmlTarget : `./${htmlTarget}`;
+
+	console.log(`Packaging presentation with target file: ${targetFile}`);
+
+	// Read the HTML file
+	let htmlContent = fs.readFileSync(targetFile, 'utf8');
+
+	// Switch from Vite's dynamic imports to static ones so that
+	// this presentation can run anywhere (including offline via
+	// file:// protocol)
+	htmlContent = switchToStaticScripts(htmlContent);
+	htmlContent = switchToStaticStyles(htmlContent);
+
+	const zip = new JSZip();
+	const filesToInclude = ['./dist/**', './*/*.md'];
+
+	if (fs.existsSync('./lib')) filesToInclude.push('./lib/**');
+	if (fs.existsSync('./images')) filesToInclude.push('./images/**');
+	if (fs.existsSync('./slides')) filesToInclude.push('./slides/**');
+
+	// Add the modified HTML file first
+	const htmlFileName = htmlTarget.replace(/\.\//, '');
+	zip.file(htmlFileName, htmlContent);
+
+	for (const pattern of filesToInclude) {
+		const files = glob.sync(pattern, {
+			nodir: true,
+			dot: false,
+			ignore: ['./examples/**', './test/**'],
+		});
+		for (const file of files) {
+			const filePath = path.resolve(file);
+			const relativePath = path.relative(process.cwd(), filePath);
+			const fileData = fs.readFileSync(filePath);
+			zip.file(relativePath, fileData);
+		}
+	}
+
+	const content = await zip.generateAsync({ type: 'nodebuffer' });
+	const zipFileName = `presentation.zip`;
+	fs.writeFileSync(zipFileName, content);
+	console.log(`Presentation packaged successfully: ${zipFileName}`);
 }
 
-main().catch(error => {
-  console.error('Error packaging presentation:', error);
-  process.exit(1);
+main().catch((error) => {
+	console.error('Error packaging presentation:', error);
+	process.exit(1);
 });