mdts 0.4.4 → 0.5.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.
- package/README.md +31 -61
- package/dist/cli.js +4 -2
- package/dist/frontend/bundle.js +167 -68
- package/dist/frontend/markdown.css +72 -4
- package/dist/server/public/welcome.md +1 -1
- package/dist/server/routes/filetree.js +1 -1
- package/dist/server/routes/outline.js +38 -2
- package/dist/server/server.js +83 -14
- package/package.json +1 -1
|
@@ -54,22 +54,71 @@
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
hr {
|
|
57
|
-
|
|
57
|
+
margin: 48px 0;
|
|
58
|
+
border: 0;
|
|
59
|
+
border-bottom: 1px solid rgba(0, 0, 0, .12);
|
|
58
60
|
}
|
|
59
61
|
|
|
60
62
|
img {
|
|
61
63
|
max-width: 100%;
|
|
62
64
|
}
|
|
63
65
|
|
|
66
|
+
table {
|
|
67
|
+
display: block;
|
|
68
|
+
margin: 48px 0;
|
|
69
|
+
width: max-content;
|
|
70
|
+
overflow: auto;
|
|
71
|
+
border-spacing: 0;
|
|
72
|
+
border-collapse: collapse;
|
|
73
|
+
|
|
74
|
+
thead {
|
|
75
|
+
text-align: left;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
tr {
|
|
79
|
+
border-top: 1px solid rgba(0, 0, 0, .12);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
td, th {
|
|
83
|
+
padding: 6px 12px;
|
|
84
|
+
border: 1px solid rgba(0, 0, 0, .12);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
64
88
|
pre {
|
|
65
89
|
padding: 16px;
|
|
66
90
|
overflow: auto;
|
|
67
91
|
background-color: #f4f5f7;
|
|
92
|
+
|
|
93
|
+
code {
|
|
94
|
+
padding: 0;
|
|
95
|
+
font-size: 100%;
|
|
96
|
+
word-break: normal;
|
|
97
|
+
white-space: pre;
|
|
98
|
+
background: transparent;
|
|
99
|
+
border: 0;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
code {
|
|
104
|
+
display: inline-block;
|
|
105
|
+
padding: .1em .6em;
|
|
106
|
+
margin: 0;
|
|
107
|
+
font-size: 80%;
|
|
108
|
+
background-color: #f4f5f7;
|
|
109
|
+
border-radius: 2px;
|
|
68
110
|
}
|
|
69
111
|
|
|
70
112
|
blockquote {
|
|
113
|
+
margin: 24px 0;
|
|
71
114
|
padding: 5px 16px;
|
|
72
115
|
font-size: 14px;
|
|
116
|
+
color: rgba(0, 0, 0, .24);
|
|
117
|
+
border-left: 4px solid rgba(0, 0, 0, .12);
|
|
118
|
+
|
|
119
|
+
blockquote {
|
|
120
|
+
margin: 12px 0;
|
|
121
|
+
}
|
|
73
122
|
|
|
74
123
|
p {
|
|
75
124
|
margin: 0;
|
|
@@ -89,18 +138,37 @@
|
|
|
89
138
|
|
|
90
139
|
.markdown-body.dark {
|
|
91
140
|
h1 {
|
|
92
|
-
border-
|
|
141
|
+
border-color: rgba(255, 255, 255, .12);
|
|
93
142
|
}
|
|
94
143
|
|
|
95
144
|
h2 {
|
|
96
|
-
border-
|
|
145
|
+
border-color: rgba(255, 255, 255, .12);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
table {
|
|
149
|
+
tr {
|
|
150
|
+
border-color: rgba(255, 255, 255, .12);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
td, th {
|
|
154
|
+
border-color: rgba(255, 255, 255, .12);
|
|
155
|
+
}
|
|
97
156
|
}
|
|
98
157
|
|
|
99
158
|
pre {
|
|
100
159
|
background-color: #20252c;
|
|
101
160
|
}
|
|
102
161
|
|
|
162
|
+
code {
|
|
163
|
+
background-color: #20252c;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
blockquote {
|
|
167
|
+
color: rgba(255, 255, 255, .24);
|
|
168
|
+
border-color: rgba(255, 255, 255, .12);
|
|
169
|
+
}
|
|
170
|
+
|
|
103
171
|
hr {
|
|
104
|
-
color: rgba(255, 255, 255, .12);
|
|
172
|
+
border-color: rgba(255, 255, 255, .12);
|
|
105
173
|
}
|
|
106
174
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<h1 align="center">
|
|
2
|
-
<img src="
|
|
2
|
+
<img src="/logo.svg" alt="mdts" width="400">
|
|
3
3
|
</h1>
|
|
4
4
|
|
|
5
5
|
mdts (Markdown Tree Server) is a simple and efficient tool for serving and viewing markdown files with an interactive file tree and outline. It's designed to help you navigate and read your markdown-based documentation or notes with ease.
|
|
@@ -35,7 +35,7 @@ const getFileTree = (baseDirectory, currentRelativePath) => {
|
|
|
35
35
|
const fileTreeRouter = (directory) => {
|
|
36
36
|
const router = (0, express_1.Router)();
|
|
37
37
|
router.get('/', (req, res) => {
|
|
38
|
-
res.json(getFileTree(directory, ''));
|
|
38
|
+
res.json({ fileTree: getFileTree(directory, ''), mountedDirectoryPath: directory });
|
|
39
39
|
});
|
|
40
40
|
return router;
|
|
41
41
|
};
|
|
@@ -1,11 +1,44 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
37
|
};
|
|
5
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
39
|
exports.outlineRouter = void 0;
|
|
7
40
|
const express_1 = require("express");
|
|
8
|
-
const
|
|
41
|
+
const fs = __importStar(require("fs"));
|
|
9
42
|
const markdown_it_1 = __importDefault(require("markdown-it"));
|
|
10
43
|
const path_1 = __importDefault(require("path"));
|
|
11
44
|
const md = new markdown_it_1.default();
|
|
@@ -18,7 +51,10 @@ const slugify = (text) => {
|
|
|
18
51
|
.replace(/^-+|-+$/g, ''); // Trim leading/trailing hyphens
|
|
19
52
|
};
|
|
20
53
|
const getMarkdownOutline = (filePath) => {
|
|
21
|
-
|
|
54
|
+
if (!fs.existsSync(filePath)) {
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
const fileContent = fs.readFileSync(filePath, 'utf-8');
|
|
22
58
|
const tokens = md.parse(fileContent, {});
|
|
23
59
|
const outline = [];
|
|
24
60
|
for (const token of tokens) {
|
package/dist/server/server.js
CHANGED
|
@@ -1,4 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
36
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
37
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -15,7 +48,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
15
48
|
exports.createApp = exports.serve = void 0;
|
|
16
49
|
const chokidar_1 = __importDefault(require("chokidar"));
|
|
17
50
|
const express_1 = __importDefault(require("express"));
|
|
18
|
-
const
|
|
51
|
+
const fs = __importStar(require("fs"));
|
|
19
52
|
const path_1 = __importDefault(require("path"));
|
|
20
53
|
const ws_1 = require("ws");
|
|
21
54
|
const filetree_1 = require("./routes/filetree");
|
|
@@ -25,7 +58,7 @@ const serve = (directory, port) => {
|
|
|
25
58
|
const server = app.listen(port, () => {
|
|
26
59
|
console.log(`🚀 Server listening at http://localhost:${port}`);
|
|
27
60
|
});
|
|
28
|
-
setupWatcher(directory, server);
|
|
61
|
+
setupWatcher(directory, server, port);
|
|
29
62
|
return server;
|
|
30
63
|
};
|
|
31
64
|
exports.serve = serve;
|
|
@@ -41,13 +74,21 @@ const createApp = (directory, currentLocation = __dirname) => {
|
|
|
41
74
|
res.setHeader('Content-Type', 'text/plain');
|
|
42
75
|
res.sendFile(path_1.default.join(currentLocation, './public/welcome.md'));
|
|
43
76
|
});
|
|
77
|
+
app.use('/api/markdown', (req, res, next) => {
|
|
78
|
+
const filePath = path_1.default.join(directory, req.path);
|
|
79
|
+
if (!fs.existsSync(filePath)) {
|
|
80
|
+
console.error(`🚫 File not found: ${filePath}`);
|
|
81
|
+
return res.status(404).send('File not found');
|
|
82
|
+
}
|
|
83
|
+
next();
|
|
84
|
+
});
|
|
44
85
|
app.use('/api/markdown', express_1.default.static(directory));
|
|
45
86
|
// Catch-all route to serve index.html for any other requests
|
|
46
87
|
app.get('*', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
47
88
|
const filePath = path_1.default.join(directory, req.path);
|
|
48
89
|
let isDirectory = false;
|
|
49
90
|
try {
|
|
50
|
-
const stats =
|
|
91
|
+
const stats = fs.statSync(filePath);
|
|
51
92
|
isDirectory = stats.isDirectory();
|
|
52
93
|
}
|
|
53
94
|
catch (_a) {
|
|
@@ -59,32 +100,60 @@ const createApp = (directory, currentLocation = __dirname) => {
|
|
|
59
100
|
return res.sendFile(path_1.default.join(currentLocation, '../frontend/index.html'));
|
|
60
101
|
}
|
|
61
102
|
else {
|
|
62
|
-
return res.sendFile(req.path, { root: directory })
|
|
103
|
+
return res.sendFile(req.path, { root: directory }, (err) => {
|
|
104
|
+
if (err) {
|
|
105
|
+
if ('code' in err && err.code === 'ENOENT') {
|
|
106
|
+
console.error(`🚫 File not found: ${path_1.default.join(directory, req.path)}`);
|
|
107
|
+
res.status(404).send('File not found');
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
console.error(`🚫 Error serving file ${path_1.default.join(directory, req.path)}:`, err);
|
|
111
|
+
res.status(500).send('Internal Server Error');
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
});
|
|
63
115
|
}
|
|
64
116
|
}));
|
|
65
117
|
return app;
|
|
66
118
|
};
|
|
67
119
|
exports.createApp = createApp;
|
|
68
|
-
const setupWatcher = (directory, server) => {
|
|
120
|
+
const setupWatcher = (directory, server, port) => {
|
|
69
121
|
const wss = new ws_1.WebSocketServer({ server });
|
|
122
|
+
wss.on('listening', () => {
|
|
123
|
+
console.log(`🚀 WebSocket server listening at ws://localhost:${port}`);
|
|
124
|
+
});
|
|
125
|
+
wss.on('connection', () => {
|
|
126
|
+
console.log('🤝 Livereload Client connected');
|
|
127
|
+
wss.on('close', () => {
|
|
128
|
+
console.log('👋 Livereload Client closed');
|
|
129
|
+
});
|
|
130
|
+
wss.on('wsClientError', (e) => {
|
|
131
|
+
console.error('🚫 Error on WebSocket server:', e);
|
|
132
|
+
});
|
|
133
|
+
wss.on('error', (e) => {
|
|
134
|
+
console.error('🚫 Error on WebSocket server:', e);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
70
137
|
const isMarkdownOrSimpleAsset = (filePath) => {
|
|
71
138
|
const ext = filePath.toLowerCase().split('.').pop();
|
|
72
|
-
return (ext
|
|
73
|
-
(ext === 'md' ||
|
|
74
|
-
ext === 'markdown' ||
|
|
75
|
-
ext === 'png' ||
|
|
76
|
-
ext === 'jpg' ||
|
|
77
|
-
ext === 'jpeg' ||
|
|
78
|
-
ext === 'gif' ||
|
|
79
|
-
ext === 'svg'));
|
|
139
|
+
return ext && (ext === 'md' || ext === 'markdown');
|
|
80
140
|
};
|
|
81
|
-
let watcher;
|
|
141
|
+
let watcher = null;
|
|
82
142
|
try {
|
|
83
143
|
watcher = chokidar_1.default.watch(directory, {
|
|
84
144
|
ignored: (watchedFilePath) => {
|
|
85
145
|
if (watchedFilePath.includes('node_modules')) {
|
|
86
146
|
return true;
|
|
87
147
|
}
|
|
148
|
+
try {
|
|
149
|
+
if (fs.statSync(watchedFilePath).isDirectory()) {
|
|
150
|
+
return false; // Don't ignore directories
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
catch (_a) {
|
|
154
|
+
// On error (e.g., file not found), let chokidar handle it
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
88
157
|
return !isMarkdownOrSimpleAsset(watchedFilePath);
|
|
89
158
|
},
|
|
90
159
|
ignoreInitial: true,
|