md-lv 1.0.1 → 1.0.3

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 CHANGED
@@ -19,12 +19,12 @@ A lightweight local server that renders Markdown files as beautiful HTML pages,
19
19
  ## Installation
20
20
 
21
21
  ```bash
22
- npm install -g markdown-viewer
22
+ npm install -g md-lv
23
23
  ```
24
24
 
25
25
  Or use npx:
26
26
  ```bash
27
- npx markdown-viewer
27
+ npx md-lv
28
28
  ```
29
29
 
30
30
  ## Usage
@@ -33,16 +33,16 @@ npx markdown-viewer
33
33
 
34
34
  ```bash
35
35
  # Serve current directory
36
- mdv
36
+ mdlv
37
37
 
38
38
  # Serve specific directory
39
- mdv --dir /path/to/docs
39
+ mdlv --dir /path/to/docs
40
40
 
41
41
  # Serve on custom port
42
- mdv --port 8080
42
+ mdlv --port 8080
43
43
 
44
44
  # Open README.md automatically
45
- mdv readme
45
+ mdlv readme
46
46
  ```
47
47
 
48
48
  ### CLI Options
@@ -58,13 +58,13 @@ mdv readme
58
58
 
59
59
  ### Subcommands
60
60
 
61
- #### `mdv readme`
61
+ #### `mdlv readme`
62
62
 
63
63
  Find and display the nearest README.md file:
64
64
 
65
65
  ```bash
66
66
  cd /path/to/project
67
- mdv readme
67
+ mdlv readme
68
68
  ```
69
69
 
70
70
  This command searches up the directory tree to find README.md and opens it in your browser.
package/bin/mdlv.js CHANGED
@@ -1,5 +1,5 @@
1
- #!/usr/bin/env node
2
-
3
- import { run } from '../src/cli.js';
4
-
5
- run(process.argv);
1
+ #!/usr/bin/env node
2
+
3
+ import { run } from '../src/cli.js';
4
+
5
+ run(process.argv);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "md-lv",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Serve Markdown files as HTML with live features - syntax highlighting, Mermaid diagrams, and MathJax formulas",
5
5
  "type": "module",
6
6
  "engines": {
@@ -0,0 +1,65 @@
1
+ import { Router } from 'express';
2
+ import fs from 'fs/promises';
3
+ import path from 'path';
4
+ import { validatePath } from '../utils/path.js';
5
+
6
+ const router = Router();
7
+
8
+ /**
9
+ * 画像ファイルの MIME タイプマップ
10
+ */
11
+ const IMAGE_MIME_TYPES = {
12
+ '.png': 'image/png',
13
+ '.jpg': 'image/jpeg',
14
+ '.jpeg': 'image/jpeg',
15
+ '.gif': 'image/gif',
16
+ '.svg': 'image/svg+xml',
17
+ '.webp': 'image/webp',
18
+ '.ico': 'image/x-icon',
19
+ '.bmp': 'image/bmp',
20
+ '.avif': 'image/avif'
21
+ };
22
+
23
+ /**
24
+ * 画像ファイル配信ルート
25
+ * Markdownから相対パスで参照される画像を配信
26
+ */
27
+ router.get(/^\/.*/, async (req, res, next) => {
28
+ try {
29
+ const requestPath = req.path;
30
+ const ext = path.extname(requestPath).toLowerCase();
31
+
32
+ // 画像ファイル以外は次のハンドラへ
33
+ const mimeType = IMAGE_MIME_TYPES[ext];
34
+ if (!mimeType) {
35
+ return next();
36
+ }
37
+
38
+ const docRoot = req.app.get('docRoot');
39
+ let filePath;
40
+
41
+ try {
42
+ filePath = validatePath(requestPath, docRoot);
43
+ } catch (error) {
44
+ if (error.code === 'ETRAVERSAL') {
45
+ return res.status(403).send('Access denied');
46
+ }
47
+ throw error;
48
+ }
49
+
50
+ // ファイル存在確認
51
+ try {
52
+ await fs.access(filePath);
53
+ } catch {
54
+ return next(); // 存在しない場合は次のハンドラへ
55
+ }
56
+
57
+ // 画像ファイルを配信
58
+ const imageData = await fs.readFile(filePath);
59
+ res.type(mimeType).send(imageData);
60
+ } catch (error) {
61
+ next(error);
62
+ }
63
+ });
64
+
65
+ export default router;
@@ -94,8 +94,9 @@ router.get(/^\/.*/, async (req, res, next) => {
94
94
  ? `<li class="folder parent"><a href="${path.dirname(requestPath)}/">..</a><span class="size">-</span></li>\n`
95
95
  : '';
96
96
 
97
+ const docRootName = req.app.get('docRootName');
97
98
  const html = renderTemplate('page', {
98
- title: `Index of ${requestPath}`,
99
+ title: `${requestPath === '/' ? docRootName : requestPath} - ${docRootName}`,
99
100
  content: `<h1>Index of ${escapeHtml(requestPath)}</h1>
100
101
  <ul class="directory-listing">${parentLink}${listHtml}</ul>`,
101
102
  breadcrumbs: generateBreadcrumbs(requestPath)
@@ -31,8 +31,9 @@ router.get(/.*\.md$/i, async (req, res, next) => {
31
31
  const content = await fs.readFile(filePath, 'utf-8');
32
32
 
33
33
  // テンプレートにMarkdownを埋め込み(クライアント側でレンダリング)
34
+ const docRootName = req.app.get('docRootName');
34
35
  const html = renderTemplate('page', {
35
- title: path.basename(filePath),
36
+ title: `${path.basename(filePath)} - ${docRootName}`,
36
37
  content: `<div id="markdown-source" style="display:none">${escapeHtml(content)}</div>
37
38
  <div id="markdown-rendered"></div>`,
38
39
  breadcrumbs: generateBreadcrumbs(requestPath)
package/src/routes/raw.js CHANGED
@@ -58,8 +58,9 @@ router.get(/^\/.*/, async (req, res, next) => {
58
58
  const language = getLanguageFromExtension(ext);
59
59
  const fileName = path.basename(filePath);
60
60
 
61
+ const docRootName = req.app.get('docRootName');
61
62
  const html = renderTemplate('page', {
62
- title: fileName,
63
+ title: `${fileName} - ${docRootName}`,
63
64
  content: `<h1>${escapeHtml(fileName)}</h1>
64
65
  <pre><code class="language-${language}">${escapeHtml(content)}</code></pre>`,
65
66
  breadcrumbs: generateBreadcrumbs(requestPath)
package/src/server.js CHANGED
@@ -3,6 +3,7 @@ import path from 'path';
3
3
  import { fileURLToPath } from 'url';
4
4
  import markdownRouter from './routes/markdown.js';
5
5
  import rawRouter from './routes/raw.js';
6
+ import assetsRouter from './routes/assets.js';
6
7
  import directoryRouter from './routes/directory.js';
7
8
  import apiRouter from './routes/api.js';
8
9
  import { securityHeaders, pathTraversalGuard } from './middleware/security.js';
@@ -23,6 +24,7 @@ export function createServer(options = {}) {
23
24
  // ドキュメントルートの設定
24
25
  const docRoot = path.resolve(options.dir || '.');
25
26
  app.set('docRoot', docRoot);
27
+ app.set('docRootName', path.basename(docRoot));
26
28
 
27
29
  // セキュリティミドルウェア(最初に登録)
28
30
  app.use(securityHeaders);
@@ -50,6 +52,9 @@ export function createServer(options = {}) {
50
52
  // Raw コード表示ルート(.js, .py 等)
51
53
  app.use(rawRouter);
52
54
 
55
+ // 画像ファイル配信ルート
56
+ app.use(assetsRouter);
57
+
53
58
  // ディレクトリ一覧ルート
54
59
  app.use(directoryRouter);
55
60