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.
- package/marp-dev-preview.mjs +113 -110
- package/package.json +1 -1
package/marp-dev-preview.mjs
CHANGED
|
@@ -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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
|
|
42
|
-
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
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
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
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
|
-
|
|
331
|
-
|
|
332
|
-
|
|
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
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
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
|
-
|
|
345
|
-
|
|
347
|
+
console.error("Failed to initialize Marp:", error);
|
|
348
|
+
process.exit(1);
|
|
346
349
|
});
|