@udx/md2html 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/index.js +99 -8
  2. package/package.json +4 -2
package/index.js CHANGED
@@ -50,6 +50,9 @@ import { marked } from 'marked';
50
50
  import { program } from 'commander';
51
51
  import chokidar from 'chokidar';
52
52
  import Handlebars from 'handlebars';
53
+ import express from 'express';
54
+ import { createServer } from 'http';
55
+ import open from 'open';
53
56
 
54
57
  // Custom renderer for handling math blocks
55
58
  const renderer = new marked.Renderer();
@@ -86,24 +89,41 @@ const CHAPTER_NAV_STYLES_PATH = path.join(path.dirname(new URL(import.meta.url).
86
89
  const SCRIPTS_PATH = path.join(path.dirname(new URL(import.meta.url).pathname), 'static/scripts.js');
87
90
 
88
91
  program
89
- .version('1.1.0')
92
+ .version('1.2.0')
90
93
  .description('Convert markdown files to a single HTML document with Google Docs styling')
91
- .option('-s, --src <directory>', 'Source directory containing markdown files')
92
- .option('-o, --out <file>', 'Output HTML file path')
94
+ .option('-s, --src <file>', 'Source markdown file or directory')
95
+ .option('-o, --out <file>', 'Output HTML file path (optional - defaults to ./output.html)')
93
96
  .option('-w, --watch', 'Watch for changes and rebuild automatically', false)
94
97
  .option('-d, --debug', 'Enable debug logging', false)
98
+ .option('-p, --preview', 'Open generated HTML in browser with live preview server', false)
99
+ .option('--port <number>', 'Port for preview server (default: random)', parseInt)
100
+ .addHelpText('after', `
101
+ Examples:
102
+ Basic conversion:
103
+ md2html -s content/docs
104
+ md2html --src=content/docs --out=output.html
105
+
106
+ Watch mode:
107
+ md2html -s content/docs --watch
108
+
109
+ Browser preview:
110
+ md2html -s content/docs --preview
111
+ md2html -s content/docs --preview --port 3000
112
+
113
+ Single file:
114
+ md2html -s document.md --preview`)
95
115
  .parse(process.argv);
96
116
 
97
117
  const options = program.opts();
98
118
 
99
- if (!options.src || !options.out) {
100
- console.error('Error: Source directory and output file are required');
119
+ if (!options.src) {
120
+ console.error('Error: Source markdown file or directory is required');
101
121
  program.help();
102
122
  process.exit(1);
103
123
  }
104
124
 
105
125
  const srcPath = path.resolve(options.src);
106
- const outputFile = path.resolve(options.out);
126
+ const outputFile = options.out ? path.resolve(options.out) : path.resolve('./output.html');
107
127
 
108
128
  const debug = (message) => {
109
129
  if (options.debug) {
@@ -647,6 +667,57 @@ async function buildHtml(srcDir, outputFile) {
647
667
  }
648
668
  }
649
669
 
670
+ /**
671
+ * Gets a random port between 3000-9999
672
+ */
673
+ function getRandomPort() {
674
+ return Math.floor(Math.random() * (9999 - 3000 + 1)) + 3000;
675
+ }
676
+
677
+ /**
678
+ * Starts a preview server for the generated HTML
679
+ * @param {string} outputFile - Path to the HTML file to serve
680
+ * @param {number|null} port - Port to use (null for random)
681
+ * @returns {Promise<number>} The port the server is running on
682
+ */
683
+ function startPreviewServer(outputFile, port = null) {
684
+ return new Promise((resolve, reject) => {
685
+ const app = express();
686
+ const targetPort = port || getRandomPort();
687
+
688
+ // Serve the generated HTML file at root
689
+ app.get('/', (req, res) => {
690
+ if (!fs.existsSync(outputFile)) {
691
+ res.status(404).send('<h1>HTML file not found</h1><p>Try rebuilding first</p>');
692
+ return;
693
+ }
694
+ res.sendFile(path.resolve(outputFile));
695
+ });
696
+
697
+ // Serve any static assets if they exist
698
+ const staticDir = path.join(path.dirname(outputFile), 'assets');
699
+ if (fs.existsSync(staticDir)) {
700
+ app.use('/assets', express.static(staticDir));
701
+ }
702
+
703
+ const server = createServer(app);
704
+
705
+ server.listen(targetPort, () => {
706
+ console.log(`šŸš€ Preview server running at http://localhost:${targetPort}`);
707
+ resolve(targetPort);
708
+ });
709
+
710
+ server.on('error', (err) => {
711
+ if (err.code === 'EADDRINUSE' && !port) {
712
+ // If random port is in use, try another random port
713
+ startPreviewServer(outputFile, null).then(resolve).catch(reject);
714
+ } else {
715
+ reject(err);
716
+ }
717
+ });
718
+ });
719
+ }
720
+
650
721
  /**
651
722
  * Watches for changes in markdown files and rebuilds HTML
652
723
  * @param {string} srcDir - Source directory containing markdown files
@@ -730,8 +801,28 @@ function watchMarkdown(srcDir, outputFile) {
730
801
  });
731
802
  }
732
803
 
733
- // Watch for changes if enabled
734
- if (options.watch) {
804
+ // Handle different execution modes
805
+ if (options.preview) {
806
+ // Preview mode: build HTML, start server, and optionally watch
807
+ buildHtml(srcPath, outputFile).then((success) => {
808
+ if (success) {
809
+ startPreviewServer(outputFile, options.port).then((port) => {
810
+ // Open browser
811
+ open(`http://localhost:${port}`);
812
+
813
+ if (options.watch) {
814
+ console.log('\nšŸ‘€ Watching for file changes...');
815
+ watchMarkdown(srcPath, outputFile);
816
+ } else {
817
+ console.log('\nšŸ’” Use --watch flag to auto-rebuild on file changes');
818
+ console.log('Press Ctrl+C to stop the server');
819
+ }
820
+ }).catch((err) => {
821
+ console.error('Failed to start preview server:', err);
822
+ });
823
+ }
824
+ });
825
+ } else if (options.watch) {
735
826
  watchMarkdown(srcPath, outputFile);
736
827
  } else {
737
828
  buildHtml(srcPath, outputFile);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@udx/md2html",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Magazine-quality Markdown to HTML converter with professional styling",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -34,8 +34,10 @@
34
34
  "dependencies": {
35
35
  "chokidar": "^3.5.3",
36
36
  "commander": "^11.0.0",
37
+ "express": "^4.18.2",
37
38
  "handlebars": "^4.7.8",
38
- "marked": "^9.1.0"
39
+ "marked": "^9.1.0",
40
+ "open": "^9.1.0"
39
41
  },
40
42
  "engines": {
41
43
  "node": ">=14.16"