mdts 0.3.2 → 0.4.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/bin/mdts +1 -1
- package/dist/cli.js +33 -19
- package/dist/frontend/bundle.js +1521 -1444
- package/{public → dist/frontend}/index.html +1 -1
- package/dist/index.js +4 -2
- package/dist/server/public/logo.svg +32 -0
- package/{public → dist/server/public}/welcome.md +1 -1
- package/dist/server/routes/filetree.js +14 -7
- package/dist/server/routes/outline.js +17 -13
- package/dist/server/server.js +49 -34
- package/package.json +21 -33
- /package/{public → dist/frontend}/favicon.ico +0 -0
- /package/{public → dist/frontend}/logo.svg +0 -0
- /package/{public → dist/frontend}/markdown.css +0 -0
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<link rel="stylesheet" href="/markdown.css" />
|
|
8
8
|
<title>mdts - Markdown file viewer</title>
|
|
9
|
-
|
|
9
|
+
<script defer src="bundle.js"></script></head>
|
|
10
10
|
<body>
|
|
11
11
|
<div id="root"></div>
|
|
12
12
|
<script defer src="/bundle.js"></script>
|
package/dist/index.js
CHANGED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<svg width="300" height="100" viewBox="0 0 180 100" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="grad" x1="0" y1="0" x2="1" y2="1">
|
|
4
|
+
<stop offset="0%" stop-color="#1b294a"/>
|
|
5
|
+
<stop offset="100%" stop-color="#1f43ab"/>
|
|
6
|
+
</linearGradient>
|
|
7
|
+
<style>
|
|
8
|
+
.text {
|
|
9
|
+
font-family: 'Segoe UI', 'Arial', sans-serif;
|
|
10
|
+
font-size: 42px;
|
|
11
|
+
font-weight: 700;
|
|
12
|
+
fill: url(#grad);
|
|
13
|
+
letter-spacing: 2px;
|
|
14
|
+
}
|
|
15
|
+
.icon {
|
|
16
|
+
fill: url(#grad);
|
|
17
|
+
}
|
|
18
|
+
</style>
|
|
19
|
+
</defs>
|
|
20
|
+
|
|
21
|
+
<!-- Icon (Abstract Folder Tree) -->
|
|
22
|
+
<g class="icon" transform="translate(10, 20)">
|
|
23
|
+
<path d="M5 25 L15 45" stroke="url(#grad)" stroke-width="2"/>
|
|
24
|
+
<path d="M25 25 L15 45" stroke="url(#grad)" stroke-width="2"/>
|
|
25
|
+
<rect x="0" y="20" width="10" height="10" rx="2"/>
|
|
26
|
+
<rect x="20" y="20" width="10" height="10" rx="2"/>
|
|
27
|
+
<rect x="10" y="40" width="10" height="10" rx="2"/>
|
|
28
|
+
</g>
|
|
29
|
+
|
|
30
|
+
<!-- Logotype -->
|
|
31
|
+
<text x="60" y="70" class="text">mdts</text>
|
|
32
|
+
</svg>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<h1 align="center">
|
|
2
|
-
<img src="logo.svg" alt="mdts" width="400">
|
|
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.
|
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.fileTreeRouter = void 0;
|
|
7
|
+
const express_1 = require("express");
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
4
10
|
const isDotFileOrDirectory = (entryName) => {
|
|
5
11
|
return entryName.startsWith('.');
|
|
6
12
|
};
|
|
@@ -9,11 +15,11 @@ const isLibraryDirectory = (entryName) => {
|
|
|
9
15
|
return libraryDirs.includes(entryName);
|
|
10
16
|
};
|
|
11
17
|
const getFileTree = (baseDirectory, currentRelativePath) => {
|
|
12
|
-
const entries =
|
|
18
|
+
const entries = fs_1.default.readdirSync(path_1.default.join(baseDirectory, currentRelativePath), { withFileTypes: true })
|
|
13
19
|
.filter(entry => !isDotFileOrDirectory(entry.name) && !isLibraryDirectory(entry.name));
|
|
14
20
|
const tree = [];
|
|
15
21
|
for (const entry of entries) {
|
|
16
|
-
const entryPath =
|
|
22
|
+
const entryPath = path_1.default.join(currentRelativePath, entry.name);
|
|
17
23
|
if (entry.isDirectory()) {
|
|
18
24
|
const subTree = getFileTree(baseDirectory, entryPath);
|
|
19
25
|
if (subTree.length > 0) { // Only include directory if it contains markdown files
|
|
@@ -26,10 +32,11 @@ const getFileTree = (baseDirectory, currentRelativePath) => {
|
|
|
26
32
|
}
|
|
27
33
|
return tree;
|
|
28
34
|
};
|
|
29
|
-
|
|
30
|
-
const router = Router();
|
|
35
|
+
const fileTreeRouter = (directory) => {
|
|
36
|
+
const router = (0, express_1.Router)();
|
|
31
37
|
router.get('/', (req, res) => {
|
|
32
38
|
res.json(getFileTree(directory, ''));
|
|
33
39
|
});
|
|
34
40
|
return router;
|
|
35
41
|
};
|
|
42
|
+
exports.fileTreeRouter = fileTreeRouter;
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
const
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.outlineRouter = void 0;
|
|
7
|
+
const express_1 = require("express");
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const markdown_it_1 = __importDefault(require("markdown-it"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const md = new markdown_it_1.default();
|
|
9
12
|
const slugify = (text) => {
|
|
10
13
|
return text
|
|
11
14
|
.toLowerCase()
|
|
@@ -15,7 +18,7 @@ const slugify = (text) => {
|
|
|
15
18
|
.replace(/^-+|-+$/g, ''); // Trim leading/trailing hyphens
|
|
16
19
|
};
|
|
17
20
|
const getMarkdownOutline = (filePath) => {
|
|
18
|
-
const fileContent =
|
|
21
|
+
const fileContent = fs_1.default.readFileSync(filePath, 'utf-8');
|
|
19
22
|
const tokens = md.parse(fileContent, {});
|
|
20
23
|
const outline = [];
|
|
21
24
|
for (const token of tokens) {
|
|
@@ -31,8 +34,8 @@ const getMarkdownOutline = (filePath) => {
|
|
|
31
34
|
}
|
|
32
35
|
return outline;
|
|
33
36
|
};
|
|
34
|
-
|
|
35
|
-
const router = Router();
|
|
37
|
+
const outlineRouter = (directory) => {
|
|
38
|
+
const router = (0, express_1.Router)();
|
|
36
39
|
router.get('/', (req, res) => {
|
|
37
40
|
const filePath = req.query.filePath;
|
|
38
41
|
if (!filePath) {
|
|
@@ -40,8 +43,8 @@ export const outlineRouter = (directory) => {
|
|
|
40
43
|
}
|
|
41
44
|
try {
|
|
42
45
|
const absolutePath = filePath === 'mdts-welcome-markdown.md'
|
|
43
|
-
?
|
|
44
|
-
:
|
|
46
|
+
? path_1.default.join(__dirname, '../../../public/welcome.md')
|
|
47
|
+
: path_1.default.join(directory, filePath);
|
|
45
48
|
const outline = getMarkdownOutline(absolutePath);
|
|
46
49
|
res.json(outline);
|
|
47
50
|
}
|
|
@@ -52,3 +55,4 @@ export const outlineRouter = (directory) => {
|
|
|
52
55
|
});
|
|
53
56
|
return router;
|
|
54
57
|
};
|
|
58
|
+
exports.outlineRouter = outlineRouter;
|
package/dist/server/server.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
3
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
4
|
return new (P || (P = Promise))(function (resolve, reject) {
|
|
@@ -7,51 +8,65 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
8
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
9
|
});
|
|
9
10
|
};
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.createApp = exports.serve = void 0;
|
|
16
|
+
const chokidar_1 = __importDefault(require("chokidar"));
|
|
17
|
+
const express_1 = __importDefault(require("express"));
|
|
18
|
+
const fs_1 = require("fs");
|
|
19
|
+
const path_1 = __importDefault(require("path"));
|
|
20
|
+
const ws_1 = require("ws");
|
|
21
|
+
const filetree_1 = require("./routes/filetree");
|
|
22
|
+
const outline_1 = require("./routes/outline");
|
|
23
|
+
const serve = (directory, port) => {
|
|
24
|
+
const app = (0, exports.createApp)(directory);
|
|
25
|
+
const server = app.listen(port, () => {
|
|
26
|
+
console.log(`🚀 Server listening at http://localhost:${port}`);
|
|
27
|
+
});
|
|
28
|
+
setupWatcher(directory, server);
|
|
29
|
+
return server;
|
|
30
|
+
};
|
|
31
|
+
exports.serve = serve;
|
|
32
|
+
const createApp = (directory, currentLocation = __dirname) => {
|
|
33
|
+
const app = (0, express_1.default)();
|
|
22
34
|
// Mount library static files
|
|
23
|
-
app.use(
|
|
24
|
-
app.use(
|
|
35
|
+
app.use(express_1.default.static(path_1.default.join(currentLocation, './public')));
|
|
36
|
+
app.use(express_1.default.static(path_1.default.join(currentLocation, '../frontend')));
|
|
25
37
|
// Define API
|
|
26
|
-
app.use('/api/filetree', fileTreeRouter(directory));
|
|
38
|
+
app.use('/api/filetree', (0, filetree_1.fileTreeRouter)(directory));
|
|
39
|
+
app.use('/api/outline', (0, outline_1.outlineRouter)(directory));
|
|
27
40
|
app.get('/api/markdown/mdts-welcome-markdown.md', (req, res) => {
|
|
28
41
|
res.setHeader('Content-Type', 'text/plain');
|
|
29
|
-
res.sendFile(
|
|
42
|
+
res.sendFile(path_1.default.join(currentLocation, './public/welcome.md'));
|
|
30
43
|
});
|
|
31
|
-
app.use('/api/markdown',
|
|
32
|
-
app.use('/api/outline', outlineRouter(directory));
|
|
44
|
+
app.use('/api/markdown', express_1.default.static(directory));
|
|
33
45
|
// Catch-all route to serve index.html for any other requests
|
|
34
46
|
app.get('*', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
35
|
-
const filePath =
|
|
47
|
+
const filePath = path_1.default.join(directory, req.path);
|
|
36
48
|
let isDirectory = false;
|
|
37
49
|
try {
|
|
38
|
-
const stats = yield
|
|
50
|
+
const stats = yield fs_1.promises.stat(filePath);
|
|
39
51
|
isDirectory = stats.isDirectory();
|
|
40
52
|
}
|
|
41
|
-
catch (
|
|
53
|
+
catch (_a) {
|
|
42
54
|
// File or directory does not exist, proceed as if it's a file
|
|
43
55
|
}
|
|
44
|
-
if (isDirectory ||
|
|
45
|
-
|
|
56
|
+
if (isDirectory ||
|
|
57
|
+
req.path.toLowerCase().endsWith('.md') ||
|
|
58
|
+
req.path.toLowerCase().endsWith('.markdown')) {
|
|
59
|
+
return res.sendFile(path_1.default.join(currentLocation, '../frontend/index.html'));
|
|
46
60
|
}
|
|
47
61
|
else {
|
|
48
62
|
return res.sendFile(req.path, { root: directory });
|
|
49
63
|
}
|
|
50
64
|
}));
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
65
|
+
return app;
|
|
66
|
+
};
|
|
67
|
+
exports.createApp = createApp;
|
|
68
|
+
const setupWatcher = (directory, server) => {
|
|
69
|
+
const wss = new ws_1.WebSocketServer({ server });
|
|
55
70
|
const isMarkdownOrSimpleAsset = (filePath) => {
|
|
56
71
|
const ext = filePath.toLowerCase().split('.').pop();
|
|
57
72
|
return (ext &&
|
|
@@ -65,18 +80,18 @@ export const serve = (directory, port) => {
|
|
|
65
80
|
};
|
|
66
81
|
let watcher;
|
|
67
82
|
try {
|
|
68
|
-
watcher =
|
|
69
|
-
ignored: (
|
|
70
|
-
if (
|
|
83
|
+
watcher = chokidar_1.default.watch(directory, {
|
|
84
|
+
ignored: (watchedFilePath) => {
|
|
85
|
+
if (watchedFilePath.includes('node_modules')) {
|
|
71
86
|
return true;
|
|
72
87
|
}
|
|
73
|
-
return !isMarkdownOrSimpleAsset(
|
|
88
|
+
return !isMarkdownOrSimpleAsset(watchedFilePath);
|
|
74
89
|
},
|
|
75
90
|
ignoreInitial: true,
|
|
76
91
|
});
|
|
77
92
|
}
|
|
78
|
-
catch (
|
|
79
|
-
console.error('🚫 Error watching directory:',
|
|
93
|
+
catch (e) {
|
|
94
|
+
console.error('🚫 Error watching directory:', e);
|
|
80
95
|
console.error('Livereload will be disabled');
|
|
81
96
|
}
|
|
82
97
|
watcher === null || watcher === void 0 ? void 0 : watcher.on('change', (filePath) => {
|
|
@@ -92,7 +107,7 @@ export const serve = (directory, port) => {
|
|
|
92
107
|
});
|
|
93
108
|
});
|
|
94
109
|
watcher === null || watcher === void 0 ? void 0 : watcher.on('unlink', (filePath) => {
|
|
95
|
-
console.log(
|
|
110
|
+
console.log(`🌲 File removed: ${filePath}, reloading tree...`);
|
|
96
111
|
wss.clients.forEach((client) => {
|
|
97
112
|
client.send(JSON.stringify({ type: 'reload-tree' }));
|
|
98
113
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mdts",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "A markdown preview server.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -8,14 +8,16 @@
|
|
|
8
8
|
"mdts": "./bin/mdts"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
|
-
"dist"
|
|
12
|
-
"public"
|
|
11
|
+
"dist"
|
|
13
12
|
],
|
|
14
13
|
"scripts": {
|
|
15
|
-
"
|
|
16
|
-
"build": "
|
|
17
|
-
"
|
|
18
|
-
"
|
|
14
|
+
"build": "tsc && cp -R public/. dist/server/public",
|
|
15
|
+
"build:frontend": "cd packages/frontend && npm run build && cp -R dist/. ../../dist/frontend",
|
|
16
|
+
"jest": "node --experimental-vm-modules node_modules/jest/bin/jest.js --config jest.config.js",
|
|
17
|
+
"watch": "npm run jest -- --watch",
|
|
18
|
+
"test": "npm run jest -- --selectProjects test",
|
|
19
|
+
"lint": "npm run jest -- --selectProjects lint",
|
|
20
|
+
"start": "npm run build && npm run build:frontend && node dist/index.js"
|
|
19
21
|
},
|
|
20
22
|
"repository": {
|
|
21
23
|
"type": "git",
|
|
@@ -30,49 +32,35 @@
|
|
|
30
32
|
],
|
|
31
33
|
"author": "unhappychoice",
|
|
32
34
|
"license": "MIT",
|
|
33
|
-
"type": "module",
|
|
34
35
|
"bugs": {
|
|
35
36
|
"url": "https://github.com/unhappychoice/mdts/issues"
|
|
36
37
|
},
|
|
37
38
|
"homepage": "https://github.com/unhappychoice/mdts#readme",
|
|
38
39
|
"devDependencies": {
|
|
39
|
-
"@babel/core": "^7.28.0",
|
|
40
|
-
"@babel/preset-env": "^7.28.0",
|
|
41
|
-
"@babel/preset-react": "^7.27.1",
|
|
42
|
-
"@babel/preset-typescript": "^7.27.1",
|
|
43
|
-
"@emotion/react": "^11.11.4",
|
|
44
|
-
"@emotion/styled": "^11.11.5",
|
|
45
|
-
"@mui/material": "^7.2.0",
|
|
46
40
|
"@types/commander": "^2.12.0",
|
|
47
41
|
"@types/express": "^4.17.21",
|
|
42
|
+
"@types/jest": "^30.0.0",
|
|
48
43
|
"@types/markdown-it": "^14.1.2",
|
|
49
|
-
"@types/
|
|
50
|
-
"@types/react-dom": "^19.1.6",
|
|
44
|
+
"@types/supertest": "^6.0.3",
|
|
51
45
|
"@types/ws": "^8.18.1",
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
46
|
+
"@typescript-eslint/eslint-plugin": "^8.37.0",
|
|
47
|
+
"@typescript-eslint/parser": "^8.37.0",
|
|
48
|
+
"axios": "^1.10.0",
|
|
49
|
+
"eslint": "^8.0.0",
|
|
50
|
+
"jest": "^30.0.4",
|
|
51
|
+
"jest-runner-eslint": "^2.3.0",
|
|
52
|
+
"open": "^10.2.0",
|
|
53
|
+
"supertest": "^7.1.3",
|
|
54
|
+
"ts-jest": "^29.4.0",
|
|
56
55
|
"ts-node": "^10.9.2",
|
|
57
56
|
"typescript": "^5.8.3",
|
|
58
|
-
"
|
|
59
|
-
"webpack-cli": "^6.0.1"
|
|
57
|
+
"typescript-eslint": "^8.37.0"
|
|
60
58
|
},
|
|
61
59
|
"dependencies": {
|
|
62
|
-
"@mui/icons-material": "^7.2.0",
|
|
63
|
-
"@mui/x-tree-view": "^8.8.0",
|
|
64
60
|
"chokidar": "^4.0.3",
|
|
65
61
|
"commander": "^14.0.0",
|
|
66
62
|
"express": "^4.19.2",
|
|
67
63
|
"markdown-it": "^14.1.0",
|
|
68
|
-
"open": "^10.1.2",
|
|
69
|
-
"react": "^19.1.0",
|
|
70
|
-
"react-dom": "^19.1.0",
|
|
71
|
-
"react-markdown": "^10.1.0",
|
|
72
|
-
"remark-gfm": "^4.0.1",
|
|
73
|
-
"remark-slug": "^7.0.1",
|
|
74
|
-
"rehype-raw": "^7.0.0",
|
|
75
|
-
"react-syntax-highlighter": "^15.6.1",
|
|
76
64
|
"ws": "^8.18.3"
|
|
77
65
|
}
|
|
78
66
|
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|