marp-dev-preview 0.0.4 → 0.0.5

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/marp-dev-preview.mjs +113 -110
  2. package/package.json +1 -1
@@ -12,54 +12,54 @@ import markdownItMark from 'markdown-it-mark';
12
12
  import markdownItContainer from 'markdown-it-container';
13
13
 
14
14
  const argv = yargs(hideBin(process.argv))
15
- .usage('Usage: $0 <markdown-file> [options]')
16
- .positional('markdown-file', {
17
- describe: 'Path to the markdown file to preview',
18
- type: 'string'
19
- })
20
- .option('theme-dir', {
21
- alias: 't',
22
- describe: 'Directory for custom themes',
23
- type: 'string'
24
- })
25
- .option('port', {
26
- alias: 'p',
27
- describe: 'Port to listen on',
28
- type: 'number',
29
- default: 8080
30
- })
31
- .config('config', 'Path to a JSON config file')
32
- .default('config', '.mp-config.json')
33
- .demandCommand(1, 'You must provide a markdown file.')
34
- .argv;
15
+ .usage('Usage: $0 <markdown-file> [options]')
16
+ .positional('markdown-file', {
17
+ describe: 'Path to the markdown file to preview',
18
+ type: 'string'
19
+ })
20
+ .option('theme-dir', {
21
+ alias: 't',
22
+ describe: 'Directory for custom themes',
23
+ type: 'string'
24
+ })
25
+ .option('port', {
26
+ alias: 'p',
27
+ describe: 'Port to listen on',
28
+ type: 'number',
29
+ default: 8080
30
+ })
31
+ .config('config', 'Path to a JSON config file')
32
+ .default('config', '.mp-config.json')
33
+ .demandCommand(1, 'You must provide a markdown file.')
34
+ .argv;
35
35
 
36
36
  const markdownFile = argv._[0]
37
37
  const themeDir = argv.themeDir;
38
38
  const port = argv.port;
39
39
 
40
40
  if (!markdownFile) {
41
- console.error('Error: You must provide a path to a markdown file.');
42
- process.exit(1);
41
+ console.error('Error: You must provide a path to a markdown file.');
42
+ process.exit(1);
43
43
  }
44
44
 
45
45
  const markdownDir = path.dirname(markdownFile);
46
46
 
47
47
  const mimeTypes = {
48
- '.html': 'text/html',
49
- '.js': 'text/javascript',
50
- '.css': 'text/css',
51
- '.json': 'application/json',
52
- '.png': 'image/png',
53
- '.jpg': 'image/jpeg',
54
- '.gif': 'image/gif',
55
- '.svg': 'image/svg+xml',
56
- '.wav': 'audio/wav',
57
- '.mp4': 'video/mp4',
58
- '.woff': 'application/font-woff',
59
- '.ttf': 'application/font-ttf',
60
- '.eot': 'application/vnd.ms-fontobject',
61
- '.otf': 'application/font-otf',
62
- '.wasm': 'application/wasm'
48
+ '.html': 'text/html',
49
+ '.js': 'text/javascript',
50
+ '.css': 'text/css',
51
+ '.json': 'application/json',
52
+ '.png': 'image/png',
53
+ '.jpg': 'image/jpeg',
54
+ '.gif': 'image/gif',
55
+ '.svg': 'image/svg+xml',
56
+ '.wav': 'audio/wav',
57
+ '.mp4': 'video/mp4',
58
+ '.woff': 'application/font-woff',
59
+ '.ttf': 'application/font-ttf',
60
+ '.eot': 'application/vnd.ms-fontobject',
61
+ '.otf': 'application/font-otf',
62
+ '.wasm': 'application/wasm'
63
63
  };
64
64
 
65
65
  const wss = new WebSocketServer({ port: port + 1 });
@@ -67,28 +67,28 @@ const wss = new WebSocketServer({ port: port + 1 });
67
67
  let marp;
68
68
 
69
69
  async function initializeMarp() {
70
- const options = { html: true, linkify: true, };
71
- marp = new Marp(options)
72
- .use(markdownItFootnote)
73
- .use(markdownItMark)
74
- .use(markdownItContainer, 'note');
70
+ const options = { html: true, linkify: true, };
71
+ marp = new Marp(options)
72
+ .use(markdownItFootnote)
73
+ .use(markdownItMark)
74
+ .use(markdownItContainer, 'note');
75
75
 
76
- if (themeDir) {
77
- const themeFiles = await fs.readdir(themeDir);
78
- for (const file of themeFiles) {
79
- if (path.extname(file) === '.css') {
80
- const css = await fs.readFile(path.join(themeDir, file), 'utf8');
81
- marp.themeSet.add(css);
82
- }
83
- }
84
- }
76
+ if (themeDir) {
77
+ const themeFiles = await fs.readdir(themeDir);
78
+ for (const file of themeFiles) {
79
+ if (path.extname(file) === '.css') {
80
+ const css = await fs.readFile(path.join(themeDir, file), 'utf8');
81
+ marp.themeSet.add(css);
82
+ }
83
+ }
84
+ }
85
85
  }
86
86
 
87
87
 
88
88
  async function renderMarp() {
89
- const md = await fs.readFile(markdownFile, 'utf8');
90
- const { html, css } = marp.render(md);
91
- return `
89
+ const md = await fs.readFile(markdownFile, 'utf8');
90
+ const { html, css } = marp.render(md);
91
+ return `
92
92
  <!DOCTYPE html>
93
93
  <html>
94
94
  <head>
@@ -158,7 +158,7 @@ async function renderMarp() {
158
158
  function findSlideByString(string) {
159
159
  const lowerString = string.toLowerCase();
160
160
  let found = false;
161
- for (let i = 0; i < slides.length; i++) {
161
+ for (let i = 0; i < slides.length && !found; i++) {
162
162
  if (slides[i].textContent.toLowerCase().includes(lowerString)) {
163
163
  slides[i].scrollIntoView({ behavior: 'smooth' });
164
164
  found = true;
@@ -166,7 +166,10 @@ async function renderMarp() {
166
166
  }
167
167
  if (!found) {
168
168
  console.error('No slide contains the string: ' + string);
169
+ return false;
169
170
  }
171
+
172
+ return true;
170
173
  }
171
174
 
172
175
  ws.onmessage = (event) => {
@@ -278,69 +281,69 @@ async function renderMarp() {
278
281
  }
279
282
 
280
283
  const server = http.createServer(async (req, res) => {
284
+ try {
285
+ if (req.url === '/') {
286
+ const html = await renderMarp();
287
+ res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
288
+ res.end(html);
289
+ } else if (req.url === '/api/command' && req.method === 'POST') {
290
+ let body = '';
291
+ req.on('data', chunk => {
292
+ body += chunk.toString();
293
+ });
294
+ req.on('end', () => {
281
295
  try {
282
- if (req.url === '/') {
283
- const html = await renderMarp();
284
- res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
285
- res.end(html);
286
- } else if (req.url === '/api/command' && req.method === 'POST') {
287
- let body = '';
288
- req.on('data', chunk => {
289
- body += chunk.toString();
290
- });
291
- req.on('end', () => {
292
- try {
293
- const command = JSON.parse(body);
294
- for (const ws of wss.clients) {
295
- ws.send(JSON.stringify(command));
296
- }
297
- res.writeHead(200, { 'Content-Type': 'application/json' });
298
- res.end(JSON.stringify({ status: 'ok', command }));
299
- } catch (e) {
300
- res.writeHead(400, { 'Content-Type': 'application/json' });
301
- res.end(JSON.stringify({ status: 'error', message: 'Invalid JSON' }));
302
- }
303
- });
304
- } else {
305
- const assetPath = path.join(markdownDir, req.url);
306
- const ext = path.extname(assetPath);
307
- const contentType = mimeTypes[ext] || 'application/octet-stream';
296
+ const command = JSON.parse(body);
297
+ for (const ws of wss.clients) {
298
+ ws.send(JSON.stringify(command));
299
+ }
300
+ res.writeHead(200, { 'Content-Type': 'application/json' });
301
+ res.end(JSON.stringify({ status: 'ok', command }));
302
+ } catch (e) {
303
+ res.writeHead(400, { 'Content-Type': 'application/json' });
304
+ res.end(JSON.stringify({ status: 'error', message: 'Invalid JSON' }));
305
+ }
306
+ });
307
+ } else {
308
+ const assetPath = path.join(markdownDir, req.url);
309
+ const ext = path.extname(assetPath);
310
+ const contentType = mimeTypes[ext] || 'application/octet-stream';
308
311
 
309
- try {
310
- const content = await fs.readFile(assetPath);
311
- res.writeHead(200, { 'Content-Type': contentType });
312
- res.end(content);
313
- } catch (error) {
314
- if (error.code === 'ENOENT') {
315
- res.writeHead(404);
316
- res.end('Not Found');
317
- } else {
318
- throw error;
319
- }
320
- }
321
- }
322
- } catch (error) {
323
- console.error(error);
324
- res.writeHead(500);
325
- res.end('Internal Server Error');
312
+ try {
313
+ const content = await fs.readFile(assetPath);
314
+ res.writeHead(200, { 'Content-Type': contentType });
315
+ res.end(content);
316
+ } catch (error) {
317
+ if (error.code === 'ENOENT') {
318
+ res.writeHead(404);
319
+ res.end('Not Found');
320
+ } else {
321
+ throw error;
326
322
  }
323
+ }
324
+ }
325
+ } catch (error) {
326
+ console.error(error);
327
+ res.writeHead(500);
328
+ res.end('Internal Server Error');
329
+ }
327
330
  });
328
331
 
329
332
  chokidar.watch(markdownFile).on('change', () => {
330
- console.log(`File ${markdownFile} changed, reloading...`);
331
- for (const ws of wss.clients) {
332
- ws.send('reload');
333
- }
333
+ console.log(`File ${markdownFile} changed, reloading...`);
334
+ for (const ws of wss.clients) {
335
+ ws.send('reload');
336
+ }
334
337
  });
335
338
 
336
339
  initializeMarp().then(() => {
337
- server.listen(port, () => {
338
- console.log(`Server listening on http://localhost:${port} for ${markdownFile}`);
339
- if (themeDir) {
340
- console.log(`Using custom themes from ${themeDir}`);
341
- }
342
- });
340
+ server.listen(port, () => {
341
+ console.log(`Server listening on http://localhost:${port} for ${markdownFile}`);
342
+ if (themeDir) {
343
+ console.log(`Using custom themes from ${themeDir}`);
344
+ }
345
+ });
343
346
  }).catch(error => {
344
- console.error("Failed to initialize Marp:", error);
345
- process.exit(1);
347
+ console.error("Failed to initialize Marp:", error);
348
+ process.exit(1);
346
349
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "marp-dev-preview",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "description": "A CLI tool to preview Marp markdown files.",
5
5
  "main": "marp-dev-preview.mjs",
6
6
  "type": "module",