markserv 1.17.3 → 1.19.1
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/README.md +33 -10
- package/lib/cli-defs.js +23 -8
- package/lib/cli-help.txt +6 -3
- package/lib/cli.js +1 -15
- package/lib/readme.js +2 -1
- package/lib/server.js +310 -83
- package/lib/templates/directory.html +158 -2
- package/lib/templates/error.html +159 -3
- package/lib/templates/github-markdown-dark.css +1124 -0
- package/lib/templates/github-markdown-light.css +1124 -0
- package/lib/templates/github-markdown-synthwave.css +987 -0
- package/lib/templates/highlight-js-github-gist.css +243 -1
- package/lib/templates/markdown.html +157 -1
- package/lib/templates/markserv.css +191 -519
- package/package.json +32 -30
- package/CHANGELOG.md +0 -145
package/lib/server.js
CHANGED
|
@@ -5,13 +5,13 @@ const path = require('path')
|
|
|
5
5
|
const fs = require('fs')
|
|
6
6
|
|
|
7
7
|
const chalk = require('chalk')
|
|
8
|
-
const opn = require('
|
|
8
|
+
const opn = require('open')
|
|
9
9
|
const Promise = require('bluebird')
|
|
10
10
|
const connect = require('connect')
|
|
11
11
|
const less = require('less')
|
|
12
12
|
const send = require('send')
|
|
13
|
-
const
|
|
14
|
-
const
|
|
13
|
+
const WebSocket = require('ws')
|
|
14
|
+
const getPort = require('get-port')
|
|
15
15
|
const implant = require('implant')
|
|
16
16
|
const deepmerge = require('deepmerge')
|
|
17
17
|
const handlebars = require('handlebars')
|
|
@@ -111,13 +111,12 @@ const log = (str, flags, err) => {
|
|
|
111
111
|
if (flags.silent) {
|
|
112
112
|
return
|
|
113
113
|
}
|
|
114
|
+
|
|
114
115
|
if (str) {
|
|
115
|
-
// eslint-disable-next-line no-console
|
|
116
116
|
console.log(str)
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
if (err) {
|
|
120
|
-
// eslint-disable-next-line no-console
|
|
121
120
|
console.error(err)
|
|
122
121
|
}
|
|
123
122
|
}
|
|
@@ -147,8 +146,8 @@ const markdownToHTML = markdownText => new Promise((resolve, reject) => {
|
|
|
147
146
|
|
|
148
147
|
try {
|
|
149
148
|
result = md.render(markdownText)
|
|
150
|
-
} catch (
|
|
151
|
-
return reject(
|
|
149
|
+
} catch (error) {
|
|
150
|
+
return reject(error)
|
|
152
151
|
}
|
|
153
152
|
|
|
154
153
|
resolve(result)
|
|
@@ -160,18 +159,19 @@ const getFile = path => new Promise((resolve, reject) => {
|
|
|
160
159
|
if (err) {
|
|
161
160
|
return reject(err)
|
|
162
161
|
}
|
|
162
|
+
|
|
163
163
|
resolve(data)
|
|
164
164
|
})
|
|
165
165
|
})
|
|
166
166
|
|
|
167
167
|
// Get Custom Less CSS to use in all Markdown files
|
|
168
168
|
const buildLessStyleSheet = cssPath =>
|
|
169
|
-
new Promise(resolve =>
|
|
169
|
+
new Promise((resolve, reject) =>
|
|
170
170
|
getFile(cssPath).then(data =>
|
|
171
171
|
less.render(data).then(data =>
|
|
172
172
|
resolve(data.css)
|
|
173
173
|
)
|
|
174
|
-
)
|
|
174
|
+
).catch(reject)
|
|
175
175
|
)
|
|
176
176
|
|
|
177
177
|
const baseTemplate = (templateUrl, handlebarData) => new Promise((resolve, reject) => {
|
|
@@ -221,8 +221,8 @@ const dirToHtml = filePath => {
|
|
|
221
221
|
prettyPath += '/'
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
-
if (prettyPath.
|
|
225
|
-
prettyPath = prettyPath.
|
|
224
|
+
if (prettyPath.slice(prettyPath.length - 2, 2) === '//') {
|
|
225
|
+
prettyPath = prettyPath.slice(0, prettyPath.length - 1)
|
|
226
226
|
}
|
|
227
227
|
|
|
228
228
|
urls.forEach(subPath => {
|
|
@@ -296,13 +296,37 @@ const createBreadcrumbs = path => {
|
|
|
296
296
|
}
|
|
297
297
|
|
|
298
298
|
// Http_request_handler: handles all the browser requests
|
|
299
|
+
const resolveTheme = flags => {
|
|
300
|
+
if (flags.light) {
|
|
301
|
+
return 'light'
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (flags.synthwave) {
|
|
305
|
+
return 'synthwave'
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (flags.theme && flags.theme !== 'dark') {
|
|
309
|
+
return flags.theme
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return 'dark'
|
|
313
|
+
}
|
|
314
|
+
|
|
299
315
|
const createRequestHandler = flags => {
|
|
300
|
-
let dir = flags
|
|
316
|
+
let {dir} = flags
|
|
301
317
|
const isDir = fs.statSync(dir).isDirectory()
|
|
302
318
|
if (!isDir) {
|
|
303
319
|
dir = path.parse(flags.dir).dir
|
|
304
320
|
}
|
|
321
|
+
|
|
305
322
|
flags.$openLocation = path.relative(dir, flags.dir)
|
|
323
|
+
const theme = resolveTheme(flags)
|
|
324
|
+
const themeFlags = {
|
|
325
|
+
themeDark: theme === 'dark',
|
|
326
|
+
themeLight: theme === 'light',
|
|
327
|
+
themeSynthwave: theme === 'synthwave',
|
|
328
|
+
themeSolarized: theme === 'solarized'
|
|
329
|
+
}
|
|
306
330
|
|
|
307
331
|
const implantOpts = {
|
|
308
332
|
maxDepth: 10
|
|
@@ -319,53 +343,69 @@ const createRequestHandler = flags => {
|
|
|
319
343
|
}),
|
|
320
344
|
|
|
321
345
|
file: (url, opts) => new Promise(resolve => {
|
|
346
|
+
if (typeof url !== 'string' || typeof (opts && opts.baseDir) !== 'string') {
|
|
347
|
+
return resolve(false)
|
|
348
|
+
}
|
|
349
|
+
|
|
322
350
|
const absUrl = path.join(opts.baseDir, url)
|
|
323
351
|
getFile(absUrl)
|
|
324
352
|
.then(data => {
|
|
325
353
|
msg('implant', style.link(absUrl), flags)
|
|
326
354
|
resolve(data)
|
|
327
355
|
})
|
|
328
|
-
.catch(
|
|
329
|
-
warnmsg('implant 404', style.link(absUrl), flags,
|
|
356
|
+
.catch(error => {
|
|
357
|
+
warnmsg('implant 404', style.link(absUrl), flags, error)
|
|
330
358
|
resolve(false)
|
|
331
359
|
})
|
|
332
360
|
}),
|
|
333
361
|
|
|
334
362
|
less: (url, opts) => new Promise(resolve => {
|
|
363
|
+
if (typeof url !== 'string' || typeof (opts && opts.baseDir) !== 'string') {
|
|
364
|
+
return resolve(false)
|
|
365
|
+
}
|
|
366
|
+
|
|
335
367
|
const absUrl = path.join(opts.baseDir, url)
|
|
336
368
|
buildLessStyleSheet(absUrl)
|
|
337
369
|
.then(data => {
|
|
338
370
|
msg('implant', style.link(absUrl), flags)
|
|
339
371
|
resolve(data)
|
|
340
372
|
})
|
|
341
|
-
.catch(
|
|
342
|
-
warnmsg('implant 404', style.link(absUrl), flags,
|
|
373
|
+
.catch(error => {
|
|
374
|
+
warnmsg('implant 404', style.link(absUrl), flags, error)
|
|
343
375
|
resolve(false)
|
|
344
376
|
})
|
|
345
377
|
}),
|
|
346
378
|
|
|
347
379
|
markdown: (url, opts) => new Promise(resolve => {
|
|
380
|
+
if (typeof url !== 'string' || typeof (opts && opts.baseDir) !== 'string') {
|
|
381
|
+
return resolve(false)
|
|
382
|
+
}
|
|
383
|
+
|
|
348
384
|
const absUrl = path.join(opts.baseDir, url)
|
|
349
385
|
getFile(absUrl).then(markdownToHTML)
|
|
350
386
|
.then(data => {
|
|
351
387
|
msg('implant', style.link(absUrl), flags)
|
|
352
388
|
resolve(data)
|
|
353
389
|
})
|
|
354
|
-
.catch(
|
|
355
|
-
warnmsg('implant 404', style.link(absUrl), flags,
|
|
390
|
+
.catch(error => {
|
|
391
|
+
warnmsg('implant 404', style.link(absUrl), flags, error)
|
|
356
392
|
resolve(false)
|
|
357
393
|
})
|
|
358
394
|
}),
|
|
359
395
|
|
|
360
396
|
html: (url, opts) => new Promise(resolve => {
|
|
397
|
+
if (typeof url !== 'string' || typeof (opts && opts.baseDir) !== 'string') {
|
|
398
|
+
return resolve(false)
|
|
399
|
+
}
|
|
400
|
+
|
|
361
401
|
const absUrl = path.join(opts.baseDir, url)
|
|
362
402
|
getFile(absUrl)
|
|
363
403
|
.then(data => {
|
|
364
404
|
msg('implant', style.link(absUrl), flags)
|
|
365
405
|
resolve(data)
|
|
366
406
|
})
|
|
367
|
-
.catch(
|
|
368
|
-
warnmsg('implant 404', style.link(absUrl), flags,
|
|
407
|
+
.catch(error => {
|
|
408
|
+
warnmsg('implant 404', style.link(absUrl), flags, error)
|
|
369
409
|
resolve(false)
|
|
370
410
|
})
|
|
371
411
|
})
|
|
@@ -395,7 +435,12 @@ const createRequestHandler = flags => {
|
|
|
395
435
|
filePath,
|
|
396
436
|
errorMsg,
|
|
397
437
|
errorStack,
|
|
398
|
-
referer
|
|
438
|
+
referer,
|
|
439
|
+
theme,
|
|
440
|
+
...themeFlags,
|
|
441
|
+
hotreload: flags.$hotreload,
|
|
442
|
+
wsPort: flags.$wsPort,
|
|
443
|
+
rootDir: flags.dir
|
|
399
444
|
}
|
|
400
445
|
|
|
401
446
|
return baseTemplate(templateUrl, handlebarData).then(final => {
|
|
@@ -417,6 +462,7 @@ const createRequestHandler = flags => {
|
|
|
417
462
|
if (flags.verbose) {
|
|
418
463
|
msg('{markserv url}', style.link(markservRelFilePath), flags)
|
|
419
464
|
}
|
|
465
|
+
|
|
420
466
|
send(req, markservRelFilePath).pipe(res)
|
|
421
467
|
return
|
|
422
468
|
}
|
|
@@ -435,7 +481,7 @@ const createRequestHandler = flags => {
|
|
|
435
481
|
isMarkdown = isType(fileTypes.markdown, filePath)
|
|
436
482
|
isHtml = isType(fileTypes.html, filePath)
|
|
437
483
|
}
|
|
438
|
-
} catch (
|
|
484
|
+
} catch (error) {
|
|
439
485
|
const fileName = path.parse(filePath).base
|
|
440
486
|
if (fileName === 'favicon.ico') {
|
|
441
487
|
res.writeHead(200, {'Content-Type': 'image/x-icon'})
|
|
@@ -444,8 +490,8 @@ const createRequestHandler = flags => {
|
|
|
444
490
|
return
|
|
445
491
|
}
|
|
446
492
|
|
|
447
|
-
errormsg('404', filePath, flags,
|
|
448
|
-
errorPage(404, filePath,
|
|
493
|
+
errormsg('404', filePath, flags, error)
|
|
494
|
+
errorPage(404, filePath, error)
|
|
449
495
|
return
|
|
450
496
|
}
|
|
451
497
|
|
|
@@ -453,44 +499,62 @@ const createRequestHandler = flags => {
|
|
|
453
499
|
if (isMarkdown) {
|
|
454
500
|
msg('markdown', style.link(prettyPath), flags)
|
|
455
501
|
getFile(filePath).then(markdownToHTML).then(filePath).then(html => {
|
|
456
|
-
|
|
502
|
+
const contentPromise = flags.templates ?
|
|
503
|
+
implant(html, implantHandlers, implantOpts) :
|
|
504
|
+
Promise.resolve(html)
|
|
505
|
+
|
|
506
|
+
return contentPromise.then(output => {
|
|
457
507
|
const templateUrl = path.join(__dirname, 'templates/markdown.html')
|
|
458
508
|
|
|
459
509
|
const handlebarData = {
|
|
460
510
|
title: path.parse(filePath).base,
|
|
461
511
|
content: output,
|
|
462
|
-
pid: process.pid | 'N/A'
|
|
512
|
+
pid: process.pid | 'N/A',
|
|
513
|
+
theme,
|
|
514
|
+
...themeFlags,
|
|
515
|
+
hotreload: flags.$hotreload,
|
|
516
|
+
wsPort: flags.$wsPort,
|
|
517
|
+
rootDir: flags.dir
|
|
463
518
|
}
|
|
464
519
|
|
|
465
520
|
return baseTemplate(templateUrl, handlebarData).then(final => {
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
521
|
+
if (flags.templates) {
|
|
522
|
+
const lvl2Dir = path.parse(templateUrl).dir
|
|
523
|
+
const lvl2Opts = deepmerge(implantOpts, {baseDir: lvl2Dir})
|
|
524
|
+
|
|
525
|
+
return implant(final, implantHandlers, lvl2Opts)
|
|
526
|
+
.then(output => {
|
|
527
|
+
res.writeHead(200, {
|
|
528
|
+
'content-type': 'text/html'
|
|
529
|
+
})
|
|
530
|
+
res.end(output)
|
|
473
531
|
})
|
|
474
|
-
|
|
475
|
-
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
res.writeHead(200, {
|
|
535
|
+
'content-type': 'text/html'
|
|
536
|
+
})
|
|
537
|
+
res.end(final)
|
|
476
538
|
})
|
|
477
539
|
})
|
|
478
|
-
}).catch(
|
|
479
|
-
|
|
480
|
-
console.error(err)
|
|
540
|
+
}).catch(error => {
|
|
541
|
+
console.error(error)
|
|
481
542
|
})
|
|
482
543
|
} else if (isHtml) {
|
|
483
544
|
msg('html', style.link(prettyPath), flags)
|
|
484
545
|
getFile(filePath).then(html => {
|
|
485
|
-
|
|
546
|
+
const contentPromise = flags.templates ?
|
|
547
|
+
implant(html, implantHandlers, implantOpts) :
|
|
548
|
+
Promise.resolve(html)
|
|
549
|
+
|
|
550
|
+
return contentPromise.then(output => {
|
|
486
551
|
res.writeHead(200, {
|
|
487
552
|
'content-type': 'text/html'
|
|
488
553
|
})
|
|
489
554
|
res.end(output)
|
|
490
555
|
})
|
|
491
|
-
}).catch(
|
|
492
|
-
|
|
493
|
-
console.error(err)
|
|
556
|
+
}).catch(error => {
|
|
557
|
+
console.error(error)
|
|
494
558
|
})
|
|
495
559
|
} else if (isDir) {
|
|
496
560
|
try {
|
|
@@ -504,24 +568,35 @@ const createRequestHandler = flags => {
|
|
|
504
568
|
content: dirToHtml(filePath),
|
|
505
569
|
title: path.parse(filePath).base,
|
|
506
570
|
pid: process.pid | 'N/A',
|
|
507
|
-
breadcrumbs: createBreadcrumbs(path.relative(dir, filePath))
|
|
571
|
+
breadcrumbs: createBreadcrumbs(path.relative(dir, filePath)),
|
|
572
|
+
theme,
|
|
573
|
+
...themeFlags,
|
|
574
|
+
hotreload: flags.$hotreload,
|
|
575
|
+
wsPort: flags.$wsPort,
|
|
576
|
+
rootDir: flags.dir
|
|
508
577
|
}
|
|
509
578
|
|
|
510
579
|
return baseTemplate(templateUrl, handlebarData).then(final => {
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
580
|
+
if (flags.templates) {
|
|
581
|
+
const lvl2Dir = path.parse(templateUrl).dir
|
|
582
|
+
const lvl2Opts = deepmerge(implantOpts, {baseDir: lvl2Dir})
|
|
583
|
+
return implant(final, implantHandlers, lvl2Opts).then(output => {
|
|
584
|
+
res.writeHead(200, {
|
|
585
|
+
'content-type': 'text/html'
|
|
586
|
+
})
|
|
587
|
+
res.end(output)
|
|
588
|
+
}).catch(error => {
|
|
589
|
+
console.error(error)
|
|
516
590
|
})
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
res.writeHead(200, {
|
|
594
|
+
'content-type': 'text/html'
|
|
521
595
|
})
|
|
596
|
+
res.end(final)
|
|
522
597
|
})
|
|
523
|
-
} catch (
|
|
524
|
-
errorPage(500, filePath,
|
|
598
|
+
} catch (error) {
|
|
599
|
+
errorPage(500, filePath, error)
|
|
525
600
|
}
|
|
526
601
|
} else {
|
|
527
602
|
// Other: Browser requests other MIME typed file (handled by 'send')
|
|
@@ -531,11 +606,8 @@ const createRequestHandler = flags => {
|
|
|
531
606
|
}
|
|
532
607
|
}
|
|
533
608
|
|
|
534
|
-
const startConnectApp =
|
|
609
|
+
const startConnectApp = httpRequestHandler => {
|
|
535
610
|
return connect()
|
|
536
|
-
.use(connectLiveReload({
|
|
537
|
-
port: liveReloadPort
|
|
538
|
-
}))
|
|
539
611
|
.use('/', httpRequestHandler)
|
|
540
612
|
}
|
|
541
613
|
|
|
@@ -552,33 +624,178 @@ const startHTTPServer = (connectApp, port, flags) => {
|
|
|
552
624
|
return httpServer
|
|
553
625
|
}
|
|
554
626
|
|
|
555
|
-
const
|
|
556
|
-
let dir = flags
|
|
627
|
+
const startHotReload = (wsPort, flags) => {
|
|
628
|
+
let {dir} = flags
|
|
557
629
|
const isDir = fs.statSync(dir).isDirectory()
|
|
558
630
|
if (!isDir) {
|
|
559
631
|
dir = path.parse(flags.dir).dir
|
|
560
632
|
}
|
|
561
633
|
|
|
562
|
-
const
|
|
563
|
-
const
|
|
564
|
-
|
|
634
|
+
const wss = new WebSocket.Server({port: wsPort})
|
|
635
|
+
const clients = new Map()
|
|
636
|
+
|
|
637
|
+
wss.on('connection', ws => {
|
|
638
|
+
ws.on('message', data => {
|
|
639
|
+
try {
|
|
640
|
+
const msg_ = JSON.parse(data)
|
|
641
|
+
if (msg_.path) {
|
|
642
|
+
clients.set(ws, msg_.path)
|
|
643
|
+
}
|
|
644
|
+
} catch (_) {}
|
|
645
|
+
})
|
|
646
|
+
|
|
647
|
+
ws.on('close', () => {
|
|
648
|
+
clients.delete(ws)
|
|
649
|
+
})
|
|
565
650
|
})
|
|
566
651
|
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
652
|
+
const sendToClients = (sockets, content) => {
|
|
653
|
+
for (const ws of sockets) {
|
|
654
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
655
|
+
ws.send(content)
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
// Debounced file watcher
|
|
661
|
+
let debounceTimer
|
|
662
|
+
const watchDir = path.resolve(dir)
|
|
663
|
+
|
|
664
|
+
const handleChange = () => {
|
|
665
|
+
// Group clients by path
|
|
666
|
+
const pathClients = new Map()
|
|
667
|
+
for (const [ws, clientPath] of clients) {
|
|
668
|
+
if (ws.readyState !== WebSocket.OPEN) {
|
|
669
|
+
continue
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
if (!pathClients.has(clientPath)) {
|
|
673
|
+
pathClients.set(clientPath, [])
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
pathClients.get(clientPath).push(ws)
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
const implantOpts = {maxDepth: 10}
|
|
680
|
+
|
|
681
|
+
const implantHandlers = {
|
|
682
|
+
markserv: () => new Promise(resolve => {
|
|
683
|
+
const value = path.relative(dir, __dirname)
|
|
684
|
+
resolve(value)
|
|
685
|
+
}),
|
|
686
|
+
|
|
687
|
+
file: (url, opts) => new Promise(resolve => {
|
|
688
|
+
const absUrl = path.join(opts.baseDir, url)
|
|
689
|
+
getFile(absUrl).then(data => resolve(data)).catch(() => resolve(false))
|
|
690
|
+
}),
|
|
691
|
+
|
|
692
|
+
less: (url, opts) => new Promise(resolve => {
|
|
693
|
+
const absUrl = path.join(opts.baseDir, url)
|
|
694
|
+
buildLessStyleSheet(absUrl).then(data => resolve(data)).catch(() => resolve(false))
|
|
695
|
+
}),
|
|
696
|
+
|
|
697
|
+
markdown: (url, opts) => new Promise(resolve => {
|
|
698
|
+
const absUrl = path.join(opts.baseDir, url)
|
|
699
|
+
getFile(absUrl).then(markdownToHTML).then(data => resolve(data)).catch(() => resolve(false))
|
|
700
|
+
}),
|
|
701
|
+
|
|
702
|
+
html: (url, opts) => new Promise(resolve => {
|
|
703
|
+
const absUrl = path.join(opts.baseDir, url)
|
|
704
|
+
getFile(absUrl).then(data => resolve(data)).catch(() => resolve(false))
|
|
705
|
+
})
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
for (const [clientPath, sockets] of pathClients) {
|
|
709
|
+
const decodedUrl = getPathFromUrl(decodeURIComponent(clientPath))
|
|
710
|
+
const filePath = path.normalize(unescape(dir) + unescape(decodedUrl))
|
|
711
|
+
const baseDir = path.parse(filePath).dir
|
|
712
|
+
implantOpts.baseDir = baseDir
|
|
713
|
+
|
|
714
|
+
let stat
|
|
715
|
+
let isDir_
|
|
716
|
+
let isMarkdown
|
|
717
|
+
|
|
718
|
+
try {
|
|
719
|
+
stat = fs.statSync(filePath)
|
|
720
|
+
isDir_ = stat.isDirectory()
|
|
721
|
+
if (!isDir_) {
|
|
722
|
+
isMarkdown = isType(fileTypes.markdown, filePath)
|
|
723
|
+
}
|
|
724
|
+
} catch (_) {
|
|
725
|
+
continue
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
if (isMarkdown) {
|
|
729
|
+
getFile(filePath)
|
|
730
|
+
.then(markdownToHTML)
|
|
731
|
+
.then(html => implant(html, implantHandlers, Object.assign({}, implantOpts, {baseDir})))
|
|
732
|
+
.then(output => {
|
|
733
|
+
sendToClients(sockets, output)
|
|
734
|
+
})
|
|
735
|
+
.catch(error => {
|
|
736
|
+
errormsg('hotreload', filePath, flags, error)
|
|
737
|
+
})
|
|
738
|
+
} else if (isDir_) {
|
|
739
|
+
try {
|
|
740
|
+
const content = dirToHtml(filePath)
|
|
741
|
+
const breadcrumbHtml = createBreadcrumbs(path.relative(dir, filePath))
|
|
742
|
+
let headerHtml = '<h1 class="icon folder isfolder">'
|
|
743
|
+
for (const crumb of breadcrumbHtml) {
|
|
744
|
+
headerHtml += `<a href="${crumb.href}">${crumb.text}</a>`
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
headerHtml += '</h1>\n'
|
|
748
|
+
const fullContent = headerHtml + content +
|
|
749
|
+
'<footer><sup><hr> Served by <a href="https://www.npmjs.com/package/markserv">MarkServ</a> | PID: ' + (process.pid || 'N/A') + '</sup></footer>'
|
|
750
|
+
|
|
751
|
+
sendToClients(sockets, fullContent)
|
|
752
|
+
} catch (error) {
|
|
753
|
+
errormsg('hotreload', filePath, flags, error)
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
try {
|
|
760
|
+
fs.watch(watchDir, {recursive: true}, (eventType, filename) => {
|
|
761
|
+
if (!filename) {
|
|
762
|
+
return
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// Check exclusions
|
|
766
|
+
for (const exclusion of fileTypes.exclusions) {
|
|
767
|
+
if (filename.includes(exclusion.replace(/\/$/, ''))) {
|
|
768
|
+
return
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
// Check if file extension is watched
|
|
773
|
+
const ext = path.extname(filename)
|
|
774
|
+
if (ext && !fileTypes.watch.includes(ext)) {
|
|
775
|
+
return
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
clearTimeout(debounceTimer)
|
|
779
|
+
debounceTimer = setTimeout(handleChange, 150)
|
|
780
|
+
})
|
|
781
|
+
} catch (error) {
|
|
782
|
+
errormsg('watch', watchDir, flags, error)
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
return wss
|
|
572
786
|
}
|
|
573
787
|
|
|
574
|
-
const logActiveServerInfo = async (serveURL, httpPort,
|
|
788
|
+
const logActiveServerInfo = async (serveURL, httpPort, wsPort, flags) => {
|
|
575
789
|
const dir = path.resolve(flags.dir)
|
|
576
790
|
|
|
577
791
|
const githubLink = 'github.com/markserv'
|
|
578
792
|
|
|
579
793
|
msg('address', style.address(serveURL), flags)
|
|
580
794
|
msg('path', chalk`{grey ${style.address(dir)}}`, flags)
|
|
581
|
-
|
|
795
|
+
|
|
796
|
+
if (wsPort) {
|
|
797
|
+
msg('hotreload', chalk`{grey ws://localhost:${style.port(wsPort)}}`, flags)
|
|
798
|
+
}
|
|
582
799
|
|
|
583
800
|
if (process.pid) {
|
|
584
801
|
msg('process', chalk`{grey your pid is: ${style.pid(process.pid)}}`, flags)
|
|
@@ -596,7 +813,7 @@ const checkForUpgrade = () => new Promise((resolve, reject) => {
|
|
|
596
813
|
}
|
|
597
814
|
|
|
598
815
|
analyzeDeps(packageJson).then(analysis => {
|
|
599
|
-
const latest = analysis.dependencies.markserv
|
|
816
|
+
const {latest} = analysis.dependencies.markserv
|
|
600
817
|
|
|
601
818
|
switch (analysis.dependencies.markserv.status) {
|
|
602
819
|
case 'error':
|
|
@@ -612,9 +829,9 @@ const checkForUpgrade = () => new Promise((resolve, reject) => {
|
|
|
612
829
|
resolve(false)
|
|
613
830
|
break
|
|
614
831
|
}
|
|
615
|
-
}).catch(
|
|
832
|
+
}).catch(error => {
|
|
616
833
|
console.log('err')
|
|
617
|
-
reject(
|
|
834
|
+
reject(error)
|
|
618
835
|
})
|
|
619
836
|
})
|
|
620
837
|
|
|
@@ -628,6 +845,7 @@ const doUpgrade = (newerVersion, flags) => {
|
|
|
628
845
|
if (code) {
|
|
629
846
|
return msg(chalk.bgRed('✨UPGRADE✨'), 'Markserv could not upgrade.', flags)
|
|
630
847
|
}
|
|
848
|
+
|
|
631
849
|
msg(chalk.bgRed('✨UPGRADE✨'), 'Upgrade finished!', flags)
|
|
632
850
|
})
|
|
633
851
|
}
|
|
@@ -660,28 +878,37 @@ const optionalUpgrade = async flags => {
|
|
|
660
878
|
}
|
|
661
879
|
|
|
662
880
|
logInstallNotes()
|
|
663
|
-
}).catch(
|
|
664
|
-
console.error(
|
|
881
|
+
}).catch(error => {
|
|
882
|
+
console.error(error)
|
|
665
883
|
})
|
|
666
884
|
}
|
|
667
885
|
|
|
668
886
|
const init = async flags => {
|
|
669
|
-
const
|
|
670
|
-
const httpPort = flags.port
|
|
887
|
+
const preferredPort = Number(flags.port) || 8642
|
|
888
|
+
const httpPort = flags.port ? preferredPort : await getPort({port: preferredPort})
|
|
889
|
+
|
|
890
|
+
let wsPort = null
|
|
891
|
+
const hotreloadEnabled = flags.hotreload !== false && flags.hotreload !== 'false'
|
|
892
|
+
if (hotreloadEnabled) {
|
|
893
|
+
wsPort = await getPort({port: httpPort + 1})
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
flags.$wsPort = wsPort
|
|
897
|
+
flags.$hotreload = hotreloadEnabled
|
|
671
898
|
|
|
672
899
|
const httpRequestHandler = createRequestHandler(flags)
|
|
673
|
-
const connectApp = startConnectApp(
|
|
900
|
+
const connectApp = startConnectApp(httpRequestHandler)
|
|
674
901
|
const httpServer = await startHTTPServer(connectApp, httpPort, flags)
|
|
675
902
|
|
|
676
|
-
let
|
|
677
|
-
if (
|
|
678
|
-
|
|
903
|
+
let hotReloadServer
|
|
904
|
+
if (hotreloadEnabled) {
|
|
905
|
+
hotReloadServer = startHotReload(wsPort, flags)
|
|
679
906
|
}
|
|
680
907
|
|
|
681
908
|
const serveURL = 'http://' + flags.address + ':' + httpPort
|
|
682
909
|
|
|
683
910
|
// Log server info to CLI
|
|
684
|
-
logActiveServerInfo(serveURL, httpPort,
|
|
911
|
+
logActiveServerInfo(serveURL, httpPort, wsPort, flags)
|
|
685
912
|
|
|
686
913
|
let launchUrl = false
|
|
687
914
|
if (flags.$openLocation || flags.$pathProvided) {
|
|
@@ -691,7 +918,7 @@ const init = async flags => {
|
|
|
691
918
|
const service = {
|
|
692
919
|
pid: process.pid,
|
|
693
920
|
httpServer,
|
|
694
|
-
|
|
921
|
+
hotReloadServer,
|
|
695
922
|
connectApp,
|
|
696
923
|
launchUrl
|
|
697
924
|
}
|