mdsvr 1.0.2 → 2.0.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 +180 -4
- package/bin/mdsvr.js +0 -0
- package/dist/cli.js +73 -5
- package/dist/cli.js.map +1 -1
- package/dist/directory.d.ts.map +1 -1
- package/dist/directory.js +50 -21
- package/dist/directory.js.map +1 -1
- package/dist/generators/feed.d.ts +3 -0
- package/dist/generators/feed.d.ts.map +1 -0
- package/dist/generators/feed.js +110 -0
- package/dist/generators/feed.js.map +1 -0
- package/dist/generators/search-index.d.ts +10 -0
- package/dist/generators/search-index.d.ts.map +1 -0
- package/dist/generators/search-index.js +79 -0
- package/dist/generators/search-index.js.map +1 -0
- package/dist/generators/sitemap.d.ts +3 -0
- package/dist/generators/sitemap.d.ts.map +1 -0
- package/dist/generators/sitemap.js +90 -0
- package/dist/generators/sitemap.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/renderer/components.d.ts +60 -0
- package/dist/renderer/components.d.ts.map +1 -0
- package/dist/renderer/components.js +59 -0
- package/dist/renderer/components.js.map +1 -0
- package/dist/renderer/index.d.ts +7 -0
- package/dist/renderer/index.d.ts.map +1 -0
- package/dist/renderer/index.js +14 -0
- package/dist/renderer/index.js.map +1 -0
- package/dist/renderer/markdown.d.ts +14 -0
- package/dist/renderer/markdown.d.ts.map +1 -0
- package/dist/renderer/markdown.js +83 -0
- package/dist/renderer/markdown.js.map +1 -0
- package/dist/renderer/mdx.d.ts +13 -0
- package/dist/renderer/mdx.d.ts.map +1 -0
- package/dist/renderer/mdx.js +55 -0
- package/dist/renderer/mdx.js.map +1 -0
- package/dist/router.d.ts +2 -1
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +158 -45
- package/dist/router.js.map +1 -1
- package/dist/server.d.ts +5 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +43 -1
- package/dist/server.js.map +1 -1
- package/dist/settings/defaults.d.ts +5 -0
- package/dist/settings/defaults.d.ts.map +1 -0
- package/dist/settings/defaults.js +35 -0
- package/dist/settings/defaults.js.map +1 -0
- package/dist/settings/index.d.ts +11 -0
- package/dist/settings/index.d.ts.map +1 -0
- package/dist/settings/index.js +78 -0
- package/dist/settings/index.js.map +1 -0
- package/dist/settings/schema.d.ts +820 -0
- package/dist/settings/schema.d.ts.map +1 -0
- package/dist/settings/schema.js +117 -0
- package/dist/settings/schema.js.map +1 -0
- package/dist/template/index.d.ts +15 -0
- package/dist/template/index.d.ts.map +1 -0
- package/dist/template/index.js +851 -0
- package/dist/template/index.js.map +1 -0
- package/dist/template/search.d.ts +5 -0
- package/dist/template/search.d.ts.map +1 -0
- package/dist/template/search.js +241 -0
- package/dist/template/search.js.map +1 -0
- package/dist/template/seo.d.ts +13 -0
- package/dist/template/seo.d.ts.map +1 -0
- package/dist/template/seo.js +71 -0
- package/dist/template/seo.js.map +1 -0
- package/dist/template/sidebar.d.ts +14 -0
- package/dist/template/sidebar.d.ts.map +1 -0
- package/dist/template/sidebar.js +185 -0
- package/dist/template/sidebar.js.map +1 -0
- package/dist/types.d.ts +4 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +15 -3
package/README.md
CHANGED
|
@@ -1,6 +1,25 @@
|
|
|
1
1
|
# mdsvr
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> Full-featured documentation website server with **MDX support**, **site-wide settings**, **dark/light mode**, **SEO meta tags**, and **auto-generated navigation**.
|
|
4
|
+
|
|
5
|
+
Transform any folder of Markdown/Markdown files into a beautiful documentation website — zero config required.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx mdsvr ./docs
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## What's New in v2.0
|
|
12
|
+
|
|
13
|
+
| Feature | v1 | v2 |
|
|
14
|
+
| ------------------ | --- | ----------------------- |
|
|
15
|
+
| Markdown rendering | ✅ | ✅ Enhanced |
|
|
16
|
+
| MDX support | ❌ | ✅ Full pipeline |
|
|
17
|
+
| Site settings | ❌ | ✅ `settings.json` |
|
|
18
|
+
| Dark/Light mode | ❌ | ✅ Toggle + auto |
|
|
19
|
+
| SEO meta tags | ❌ | ✅ OG, Twitter, sitemap |
|
|
20
|
+
| Sidebar navigation | ❌ | ✅ Auto-generated |
|
|
21
|
+
| Search | ❌ | ✅ Full-text |
|
|
22
|
+
| Sitemap/RSS | ❌ | ✅ Auto-generated |
|
|
4
23
|
|
|
5
24
|
## Quick Start
|
|
6
25
|
|
|
@@ -8,6 +27,9 @@ Static file server with auto-render Markdown → HTML. Similar to Vercel's `serv
|
|
|
8
27
|
# One-shot, no install
|
|
9
28
|
npx mdsvr ./docs
|
|
10
29
|
|
|
30
|
+
# Create a starter settings.json
|
|
31
|
+
npx mdsvr ./docs --init
|
|
32
|
+
|
|
11
33
|
# With options
|
|
12
34
|
npx mdsvr ./notes --port 4000 --open
|
|
13
35
|
|
|
@@ -33,32 +55,186 @@ Options:
|
|
|
33
55
|
--host H Bind address (default: localhost)
|
|
34
56
|
-o, --open Auto-open browser
|
|
35
57
|
-s, --silent Suppress console output
|
|
58
|
+
--init Create a starter settings.json in [dir]
|
|
59
|
+
--validate Validate settings.json and exit
|
|
60
|
+
--no-watch Disable settings.json hot-reload
|
|
36
61
|
-v, --version Print version
|
|
37
62
|
-h, --help Print this help
|
|
38
63
|
```
|
|
39
64
|
|
|
65
|
+
## Configuration (`settings.json`)
|
|
66
|
+
|
|
67
|
+
Place `settings.json` in your docs root for full customization:
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"$schema": "https://mdsvr.dev/schema/v2.json",
|
|
72
|
+
"site": {
|
|
73
|
+
"title": "My Docs",
|
|
74
|
+
"description": "Project documentation",
|
|
75
|
+
"language": "en"
|
|
76
|
+
},
|
|
77
|
+
"appearance": {
|
|
78
|
+
"defaultTheme": "system",
|
|
79
|
+
"allowThemeToggle": true,
|
|
80
|
+
"accentColor": "#0969da"
|
|
81
|
+
},
|
|
82
|
+
"navigation": {
|
|
83
|
+
"sidebar": { "enabled": true },
|
|
84
|
+
"tocEnabled": true
|
|
85
|
+
},
|
|
86
|
+
"search": { "enabled": true },
|
|
87
|
+
"seo": {
|
|
88
|
+
"generateSitemap": true,
|
|
89
|
+
"generateRssFeed": false
|
|
90
|
+
},
|
|
91
|
+
"mdx": { "enabled": true }
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Create one with `npx mdsvr ./docs --init`.
|
|
96
|
+
|
|
97
|
+
## MDX Support
|
|
98
|
+
|
|
99
|
+
Write interactive documentation with MDX:
|
|
100
|
+
|
|
101
|
+
```mdx
|
|
102
|
+
---
|
|
103
|
+
title: Getting Started
|
|
104
|
+
description: How to install and configure
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
# Getting Started
|
|
108
|
+
|
|
109
|
+
<Callout type="warning">Make sure you have Node.js 18+ installed.</Callout>
|
|
110
|
+
|
|
111
|
+
<Steps>
|
|
112
|
+
### Install the package
|
|
113
|
+
Run `npm install mdsvr`
|
|
114
|
+
|
|
115
|
+
### Create your docs folder
|
|
116
|
+
|
|
117
|
+
Create a `./docs` directory
|
|
118
|
+
|
|
119
|
+
### Start the server
|
|
120
|
+
|
|
121
|
+
Run `npx mdsvr ./docs --open`
|
|
122
|
+
|
|
123
|
+
</Steps>
|
|
124
|
+
|
|
125
|
+
<CardGroup cols={2}>
|
|
126
|
+
<Card title="Quick Start" icon="⚡" href="/quickstart">
|
|
127
|
+
Get up and running in 5 minutes
|
|
128
|
+
</Card>
|
|
129
|
+
<Card title="Configuration" icon="⚙️" href="/config">
|
|
130
|
+
Full settings reference
|
|
131
|
+
</Card>
|
|
132
|
+
</CardGroup>
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Built-in MDX Components
|
|
136
|
+
|
|
137
|
+
- `<Callout type="info|warning|danger|success|tip">` - Highlighted boxes
|
|
138
|
+
- `<CodeGroup title="...">` - Tabbed code blocks
|
|
139
|
+
- `<Steps>` - Numbered step-by-step guide
|
|
140
|
+
- `<Card title="..." icon="..." href="...">` - Link cards
|
|
141
|
+
- `<CardGroup cols={2}>` - Grid layout for cards
|
|
142
|
+
- `<Tabs items={[...]}>` - Tabbed content
|
|
143
|
+
- `<Accordion title="...">` - Collapsible sections
|
|
144
|
+
- `<Badge color="green|orange|red|blue|purple|gray">` - Status badges
|
|
145
|
+
- `<Mermaid>` - Diagrams (client-side rendered)
|
|
146
|
+
|
|
40
147
|
## Programmatic API
|
|
41
148
|
|
|
42
149
|
```typescript
|
|
43
150
|
import { createServer } from "mdsvr";
|
|
44
151
|
|
|
45
|
-
const server = await createServer("./docs", {
|
|
152
|
+
const server = await createServer("./docs", {
|
|
153
|
+
port: 4000,
|
|
154
|
+
host: "localhost",
|
|
155
|
+
open: true,
|
|
156
|
+
silent: false,
|
|
157
|
+
watchSettings: true, // Auto-reload on settings.json changes
|
|
158
|
+
});
|
|
159
|
+
|
|
46
160
|
console.log(`Running at ${server.url}`);
|
|
161
|
+
console.log(`Settings: ${server.settings.site.title}`);
|
|
162
|
+
|
|
163
|
+
// Manual settings reload
|
|
164
|
+
await server.reloadSettings();
|
|
47
165
|
|
|
48
166
|
// Graceful shutdown
|
|
49
167
|
process.on("SIGINT", () => server.close());
|
|
50
168
|
```
|
|
51
169
|
|
|
170
|
+
### Accessing Settings
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
import { loadSettings, validateSettingsFile } from "mdsvr";
|
|
174
|
+
|
|
175
|
+
// Load settings from directory
|
|
176
|
+
const settings = await loadSettings("./docs");
|
|
177
|
+
|
|
178
|
+
// Validate settings.json
|
|
179
|
+
const result = await validateSettingsFile("./docs");
|
|
180
|
+
if (!result.valid) {
|
|
181
|
+
console.error(result.errors);
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Generated Endpoints
|
|
186
|
+
|
|
187
|
+
When enabled in `settings.json`:
|
|
188
|
+
|
|
189
|
+
| Endpoint | Description |
|
|
190
|
+
| -------------------- | ----------------------------------- |
|
|
191
|
+
| `/sitemap.xml` | XML sitemap for search engines |
|
|
192
|
+
| `/feed.xml` | RSS feed for blog posts |
|
|
193
|
+
| `/search-index.json` | Search index for client-side search |
|
|
194
|
+
|
|
52
195
|
## Features
|
|
53
196
|
|
|
54
|
-
- **Markdown rendering**: `.md` files automatically rendered as beautiful
|
|
197
|
+
- **Markdown rendering**: `.md` files automatically rendered as beautiful HTML
|
|
198
|
+
- **MDX support**: Interactive components in your docs
|
|
55
199
|
- **Directory listings**: Browse directories with clickable file links
|
|
56
200
|
- **Static files**: Serve any file type with correct MIME types
|
|
57
201
|
- **Syntax highlighting**: Code blocks highlighted with highlight.js
|
|
58
202
|
- **Anchor links**: Headers have clickable anchors
|
|
203
|
+
- **Sidebar navigation**: Auto-generated from your file structure
|
|
204
|
+
- **Table of contents**: Auto-generated from page headings
|
|
205
|
+
- **Dark/Light mode**: Toggle or auto-detect system preference
|
|
206
|
+
- **Full-text search**: Client-side search with keyboard shortcut (⌘K)
|
|
207
|
+
- **SEO optimized**: Open Graph, Twitter Cards, canonical URLs, sitemap
|
|
59
208
|
- **Mobile responsive**: Works on all devices
|
|
209
|
+
- **Hot reload**: Settings auto-reload on change
|
|
60
210
|
- **Path traversal protection**: Secure by default
|
|
61
|
-
- **Zero config**: Works out of the box
|
|
211
|
+
- **Zero config**: Works out of the box, fully customizable
|
|
212
|
+
|
|
213
|
+
## File Structure
|
|
214
|
+
|
|
215
|
+
```
|
|
216
|
+
docs/
|
|
217
|
+
├── settings.json # Site configuration (optional)
|
|
218
|
+
├── README.md # Homepage (auto-detected)
|
|
219
|
+
├── guide/
|
|
220
|
+
│ ├── getting-started.md
|
|
221
|
+
│ └── configuration.mdx
|
|
222
|
+
├── api/
|
|
223
|
+
│ └── reference.md
|
|
224
|
+
└── assets/
|
|
225
|
+
└── logo.svg
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Security
|
|
229
|
+
|
|
230
|
+
- **Path traversal**: Resolved paths are verified to be within root directory
|
|
231
|
+
- **Hidden files**: Files starting with `_` or listed in `settings.json` are hidden
|
|
232
|
+
- **Blocked extensions**: `.env`, `.key`, `.pem`, etc. are blocked by default
|
|
233
|
+
- **Read-only**: Server only serves files, no write operations
|
|
234
|
+
|
|
235
|
+
## Requirements
|
|
236
|
+
|
|
237
|
+
- Node.js 18+
|
|
62
238
|
|
|
63
239
|
## License
|
|
64
240
|
|
package/bin/mdsvr.js
CHANGED
|
File without changes
|
package/dist/cli.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createServer, getNetworkAddress } from "./server.js";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { readFile } from "node:fs/promises";
|
|
4
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { validateSettingsFile, generateDefaultSettings, } from "./settings/index.js";
|
|
6
7
|
function parseArgs(argv) {
|
|
7
8
|
const args = {
|
|
8
9
|
dir: ".",
|
|
@@ -12,6 +13,9 @@ function parseArgs(argv) {
|
|
|
12
13
|
silent: false,
|
|
13
14
|
version: false,
|
|
14
15
|
help: false,
|
|
16
|
+
init: false,
|
|
17
|
+
validate: false,
|
|
18
|
+
watchSettings: true,
|
|
15
19
|
};
|
|
16
20
|
for (let i = 0; i < argv.length; i++) {
|
|
17
21
|
const arg = argv[i];
|
|
@@ -37,6 +41,15 @@ function parseArgs(argv) {
|
|
|
37
41
|
else if (arg === "--silent" || arg === "-s") {
|
|
38
42
|
args.silent = true;
|
|
39
43
|
}
|
|
44
|
+
else if (arg === "--init") {
|
|
45
|
+
args.init = true;
|
|
46
|
+
}
|
|
47
|
+
else if (arg === "--validate") {
|
|
48
|
+
args.validate = true;
|
|
49
|
+
}
|
|
50
|
+
else if (arg === "--no-watch") {
|
|
51
|
+
args.watchSettings = false;
|
|
52
|
+
}
|
|
40
53
|
else if (!arg.startsWith("--") && !arg.startsWith("-")) {
|
|
41
54
|
args.dir = arg;
|
|
42
55
|
}
|
|
@@ -53,6 +66,9 @@ Options:
|
|
|
53
66
|
--host H Bind address (default: localhost)
|
|
54
67
|
-o, --open Auto-open browser
|
|
55
68
|
-s, --silent Suppress console output
|
|
69
|
+
--init Create a starter settings.json in [dir]
|
|
70
|
+
--validate Validate settings.json and exit
|
|
71
|
+
--no-watch Disable settings.json hot-reload
|
|
56
72
|
-v, --version Print version
|
|
57
73
|
-h, --help Print this help
|
|
58
74
|
|
|
@@ -60,6 +76,8 @@ Examples:
|
|
|
60
76
|
mdsvr ./docs
|
|
61
77
|
mdsvr ./notes --port 4000 --open
|
|
62
78
|
mdsvr . --host 0.0.0.0 --port 8080
|
|
79
|
+
mdsvr ./docs --init
|
|
80
|
+
mdsvr ./docs --validate
|
|
63
81
|
`);
|
|
64
82
|
}
|
|
65
83
|
async function getVersion() {
|
|
@@ -83,6 +101,11 @@ async function openBrowser(url) {
|
|
|
83
101
|
: `xdg-open`;
|
|
84
102
|
exec(`${cmd} "${url}"`);
|
|
85
103
|
}
|
|
104
|
+
async function initSettings(dir) {
|
|
105
|
+
const settingsPath = path.join(dir, "settings.json");
|
|
106
|
+
const defaultSettings = generateDefaultSettings();
|
|
107
|
+
await writeFile(settingsPath, JSON.stringify(defaultSettings, null, 2), "utf-8");
|
|
108
|
+
}
|
|
86
109
|
async function main() {
|
|
87
110
|
const args = parseArgs(process.argv.slice(2));
|
|
88
111
|
if (args.help) {
|
|
@@ -95,14 +118,49 @@ async function main() {
|
|
|
95
118
|
process.exit(0);
|
|
96
119
|
}
|
|
97
120
|
const absoluteDir = path.resolve(args.dir);
|
|
121
|
+
// Handle --init
|
|
122
|
+
if (args.init) {
|
|
123
|
+
try {
|
|
124
|
+
await initSettings(absoluteDir);
|
|
125
|
+
console.log(`\n ✔ Created ${args.dir}/settings.json with defaults`);
|
|
126
|
+
console.log(` ✔ Run \`npx mdsvr ${args.dir}\` to start\n`);
|
|
127
|
+
process.exit(0);
|
|
128
|
+
}
|
|
129
|
+
catch (err) {
|
|
130
|
+
console.error("Error:", err instanceof Error ? err.message : err);
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// Handle --validate
|
|
135
|
+
if (args.validate) {
|
|
136
|
+
try {
|
|
137
|
+
const result = await validateSettingsFile(absoluteDir);
|
|
138
|
+
if (result.valid) {
|
|
139
|
+
console.log(`\n ✔ settings.json is valid\n`);
|
|
140
|
+
process.exit(0);
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
console.log(`\n ✖ settings.json has errors:`);
|
|
144
|
+
result.errors?.forEach((e) => console.log(` - ${e}`));
|
|
145
|
+
console.log();
|
|
146
|
+
process.exit(1);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
catch (err) {
|
|
150
|
+
console.error("Error:", err instanceof Error ? err.message : err);
|
|
151
|
+
process.exit(1);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
98
154
|
try {
|
|
99
155
|
const server = await createServer(args.dir, {
|
|
100
156
|
port: args.port,
|
|
101
157
|
host: args.host,
|
|
102
158
|
open: args.open,
|
|
103
159
|
silent: args.silent,
|
|
160
|
+
watchSettings: args.watchSettings,
|
|
104
161
|
});
|
|
105
162
|
const version = await getVersion();
|
|
163
|
+
const settings = server.settings;
|
|
106
164
|
if (!args.silent) {
|
|
107
165
|
console.log(`\n mdsvr v${version}\n`);
|
|
108
166
|
console.log(` Local: ${server.url}`);
|
|
@@ -110,14 +168,24 @@ async function main() {
|
|
|
110
168
|
if (args.host !== "localhost" && networkAddr) {
|
|
111
169
|
console.log(` Network: http://${networkAddr}:${server.port}`);
|
|
112
170
|
}
|
|
113
|
-
console.log(`\n Serving
|
|
114
|
-
console.log(`
|
|
171
|
+
console.log(`\n Serving ${absoluteDir}`);
|
|
172
|
+
console.log(` Settings ${settings.site.title !== "mdsvr Docs" ? "loaded" : "using defaults"}`);
|
|
173
|
+
console.log(` MDX ${settings.mdx.enabled ? "enabled" : "disabled"}`);
|
|
174
|
+
console.log(` Search ${settings.search.enabled ? "enabled" : "disabled"}`);
|
|
175
|
+
if (settings.seo.generateSitemap) {
|
|
176
|
+
console.log(` Sitemap ${server.url}/sitemap.xml`);
|
|
177
|
+
}
|
|
178
|
+
console.log(`\n Hit Ctrl+C to stop.\n`);
|
|
115
179
|
}
|
|
116
180
|
if (args.open) {
|
|
117
181
|
await openBrowser(server.url);
|
|
118
182
|
}
|
|
119
|
-
// Graceful shutdown
|
|
120
|
-
|
|
183
|
+
// Graceful shutdown - use once() and flag to prevent multiple shutdowns
|
|
184
|
+
let isShuttingDown = false;
|
|
185
|
+
process.once("SIGINT", async () => {
|
|
186
|
+
if (isShuttingDown)
|
|
187
|
+
return;
|
|
188
|
+
isShuttingDown = true;
|
|
121
189
|
if (!args.silent) {
|
|
122
190
|
console.log("\nShutting down...");
|
|
123
191
|
}
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAC9D,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/directory.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"directory.d.ts","sourceRoot":"","sources":["../src/directory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEtC,wBAAgB,eAAe,CAAC,MAAM,EAAE;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"directory.d.ts","sourceRoot":"","sources":["../src/directory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEtC,wBAAgB,eAAe,CAAC,MAAM,EAAE;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,GAAG,MAAM,CA8IT"}
|
package/dist/directory.js
CHANGED
|
@@ -24,29 +24,63 @@ export function renderDirectory(params) {
|
|
|
24
24
|
})
|
|
25
25
|
.join("\n");
|
|
26
26
|
const breadcrumb = generateBreadcrumb(urlPath);
|
|
27
|
+
// Theme script for dark mode support
|
|
28
|
+
const themeScript = `
|
|
29
|
+
<script>
|
|
30
|
+
(function() {
|
|
31
|
+
var theme = localStorage.getItem('theme') || 'system';
|
|
32
|
+
if (theme === 'system') {
|
|
33
|
+
theme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
34
|
+
}
|
|
35
|
+
document.documentElement.setAttribute('data-theme', theme);
|
|
36
|
+
})();
|
|
37
|
+
</script>`;
|
|
27
38
|
return `<!DOCTYPE html>
|
|
28
|
-
<html lang="en">
|
|
39
|
+
<html lang="en" data-theme="light">
|
|
29
40
|
<head>
|
|
30
41
|
<meta charset="UTF-8">
|
|
31
42
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
32
43
|
<title>Index of ${escapeHtml(urlPath)}</title>
|
|
44
|
+
${themeScript}
|
|
33
45
|
<style>
|
|
46
|
+
:root {
|
|
47
|
+
--bg: #f6f8fa;
|
|
48
|
+
--bg-secondary: #ffffff;
|
|
49
|
+
--text: #24292f;
|
|
50
|
+
--text-muted: #656d76;
|
|
51
|
+
--border: #d0d7de;
|
|
52
|
+
--accent: #0969da;
|
|
53
|
+
--header-bg: #0d1117;
|
|
54
|
+
--header-text: #f0f6fc;
|
|
55
|
+
--header-border: #30363d;
|
|
56
|
+
}
|
|
57
|
+
:root[data-theme="dark"] {
|
|
58
|
+
--bg: #0d1117;
|
|
59
|
+
--bg-secondary: #161b22;
|
|
60
|
+
--text: #e6edf3;
|
|
61
|
+
--text-muted: #8d96a0;
|
|
62
|
+
--border: #30363d;
|
|
63
|
+
--accent: #58a6ff;
|
|
64
|
+
--header-bg: #161b22;
|
|
65
|
+
--header-text: #e6edf3;
|
|
66
|
+
--header-border: #30363d;
|
|
67
|
+
}
|
|
34
68
|
* { box-sizing: border-box; }
|
|
35
69
|
body {
|
|
36
70
|
margin: 0;
|
|
37
71
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans', Helvetica, Arial, sans-serif;
|
|
38
72
|
font-size: 16px;
|
|
39
73
|
line-height: 1.6;
|
|
40
|
-
color:
|
|
41
|
-
background:
|
|
74
|
+
color: var(--text);
|
|
75
|
+
background: var(--bg);
|
|
42
76
|
}
|
|
43
77
|
.header {
|
|
44
|
-
background:
|
|
78
|
+
background: var(--header-bg);
|
|
45
79
|
padding: 16px 24px;
|
|
46
|
-
border-bottom: 1px solid
|
|
80
|
+
border-bottom: 1px solid var(--header-border);
|
|
47
81
|
}
|
|
48
82
|
.header-logo {
|
|
49
|
-
color:
|
|
83
|
+
color: var(--header-text);
|
|
50
84
|
font-weight: 600;
|
|
51
85
|
font-size: 14px;
|
|
52
86
|
text-decoration: none;
|
|
@@ -55,22 +89,22 @@ export function renderDirectory(params) {
|
|
|
55
89
|
max-width: 860px;
|
|
56
90
|
margin: 32px auto;
|
|
57
91
|
padding: 32px;
|
|
58
|
-
background:
|
|
59
|
-
border: 1px solid
|
|
92
|
+
background: var(--bg-secondary);
|
|
93
|
+
border: 1px solid var(--border);
|
|
60
94
|
border-radius: 6px;
|
|
61
95
|
}
|
|
62
|
-
h1 { font-size: 20px; font-weight: 600; margin: 0 0 16px; color:
|
|
63
|
-
.breadcrumb { font-size: 14px; color:
|
|
64
|
-
.breadcrumb a { color:
|
|
96
|
+
h1 { font-size: 20px; font-weight: 600; margin: 0 0 16px; color: var(--text); }
|
|
97
|
+
.breadcrumb { font-size: 14px; color: var(--text-muted); margin-bottom: 20px; }
|
|
98
|
+
.breadcrumb a { color: var(--accent); text-decoration: none; }
|
|
65
99
|
.breadcrumb a:hover { text-decoration: underline; }
|
|
66
100
|
table { width: 100%; border-collapse: collapse; }
|
|
67
|
-
th, td { padding: 8px 12px; text-align: left; border-bottom: 1px solid
|
|
68
|
-
th { font-weight: 600; color:
|
|
101
|
+
th, td { padding: 8px 12px; text-align: left; border-bottom: 1px solid var(--border); }
|
|
102
|
+
th { font-weight: 600; color: var(--text-muted); font-size: 14px; }
|
|
69
103
|
td { font-size: 14px; }
|
|
70
|
-
td a { color:
|
|
104
|
+
td a { color: var(--accent); text-decoration: none; }
|
|
71
105
|
td a:hover { text-decoration: underline; }
|
|
72
|
-
.size { color:
|
|
73
|
-
.empty { color:
|
|
106
|
+
.size { color: var(--text-muted); text-align: right; width: 100px; }
|
|
107
|
+
.empty { color: var(--text-muted); font-style: italic; padding: 24px 0; }
|
|
74
108
|
@media (max-width: 900px) {
|
|
75
109
|
.container { margin: 16px; padding: 20px; }
|
|
76
110
|
.header { padding: 12px 16px; }
|
|
@@ -81,12 +115,7 @@ export function renderDirectory(params) {
|
|
|
81
115
|
</style>
|
|
82
116
|
</head>
|
|
83
117
|
<body>
|
|
84
|
-
<header class="header">
|
|
85
|
-
<a href="/" class="header-logo">mdsvr</a>
|
|
86
|
-
</header>
|
|
87
118
|
<div class="container">
|
|
88
|
-
<h1>Index of ${escapeHtml(urlPath) || "/"}</h1>
|
|
89
|
-
<div class="breadcrumb">${breadcrumb}</div>
|
|
90
119
|
<table>
|
|
91
120
|
<thead>
|
|
92
121
|
<tr><th>Name</th><th class="size">Size</th></tr>
|
package/dist/directory.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"directory.js","sourceRoot":"","sources":["../src/directory.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,eAAe,CAAC,MAG/B;IACC,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC;IAEpC,mDAAmD;IACnD,MAAM,MAAM,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACxC,IAAI,CAAC,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,CAAC,WAAW,EAAE;YAAE,OAAO,CAAC,CAAC,CAAC;QACnD,IAAI,CAAC,CAAC,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC,WAAW,EAAE;YAAE,OAAO,CAAC,CAAC;QAClD,OAAO,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,MAAM,IAAI,GAAG,MAAM;SAChB,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QACb,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;QACxB,MAAM,KAAK,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;QAClC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;QAC/D,MAAM,IAAI,GAAG,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC3D,MAAM,IAAI,GACR,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE;YACtB,CAAC,CAAC,UAAU,CAAE,KAAqC,CAAC,IAAI,IAAI,CAAC,CAAC;YAC9D,CAAC,CAAC,GAAG,CAAC;QAEV,OAAO;cACC,IAAI,aAAa,IAAI,KAAK,UAAU,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE;2BAChD,IAAI;YACnB,CAAC;IACT,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,MAAM,UAAU,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAE/C,
|
|
1
|
+
{"version":3,"file":"directory.js","sourceRoot":"","sources":["../src/directory.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feed.d.ts","sourceRoot":"","sources":["../../src/generators/feed.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAiGrD,wBAAsB,YAAY,CAChC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,QAAQ,GACjB,OAAO,CAAC,MAAM,CAAC,CAwCjB"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { promises as fs } from "node:fs";
|
|
3
|
+
import matter from "gray-matter";
|
|
4
|
+
function isHidden(filePath, settings) {
|
|
5
|
+
const basename = path.basename(filePath);
|
|
6
|
+
for (const pattern of settings.files.extensions.hidden) {
|
|
7
|
+
if (basename === pattern)
|
|
8
|
+
return true;
|
|
9
|
+
if (pattern.startsWith("*") && basename.endsWith(pattern.slice(1)))
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
if (basename.startsWith("_"))
|
|
13
|
+
return true;
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
function stripHtml(html) {
|
|
17
|
+
return html
|
|
18
|
+
.replace(/<[^>]+>/g, "")
|
|
19
|
+
.replace(/&/g, "&")
|
|
20
|
+
.replace(/</g, "<")
|
|
21
|
+
.replace(/>/g, ">")
|
|
22
|
+
.replace(/"/g, '"')
|
|
23
|
+
.replace(/'/g, "'")
|
|
24
|
+
.replace(/ /g, " ");
|
|
25
|
+
}
|
|
26
|
+
async function readDirRecursive(dirPath, rootDir, baseUrl, settings) {
|
|
27
|
+
const entries = [];
|
|
28
|
+
const items = await fs.readdir(dirPath, { withFileTypes: true });
|
|
29
|
+
for (const item of items) {
|
|
30
|
+
const fullPath = path.join(dirPath, item.name);
|
|
31
|
+
if (isHidden(fullPath, settings))
|
|
32
|
+
continue;
|
|
33
|
+
if (item.isDirectory()) {
|
|
34
|
+
const subEntries = await readDirRecursive(fullPath, rootDir, baseUrl, settings);
|
|
35
|
+
entries.push(...subEntries);
|
|
36
|
+
}
|
|
37
|
+
else if (item.name.endsWith(".md") ||
|
|
38
|
+
(item.name.endsWith(".mdx") && settings.mdx.enabled)) {
|
|
39
|
+
try {
|
|
40
|
+
const content = await fs.readFile(fullPath, "utf-8");
|
|
41
|
+
const parsed = matter(content);
|
|
42
|
+
// Only include files with a date in frontmatter (treat as blog posts)
|
|
43
|
+
if (!parsed.data.date)
|
|
44
|
+
continue;
|
|
45
|
+
const date = new Date(parsed.data.date);
|
|
46
|
+
if (isNaN(date.getTime()))
|
|
47
|
+
continue;
|
|
48
|
+
const relativePath = "/" + path.relative(rootDir, fullPath).replace(/\\/g, "/");
|
|
49
|
+
const urlPath = relativePath.replace(/\.(md|mdx)$/, "");
|
|
50
|
+
const fullUrl = `${baseUrl.replace(/\/$/, "")}${urlPath}`;
|
|
51
|
+
const title = parsed.data.title ||
|
|
52
|
+
parsed.content.match(/^#\s+(.+)$/m)?.[1] ||
|
|
53
|
+
item.name.replace(/\.\w+$/, "");
|
|
54
|
+
entries.push({
|
|
55
|
+
title,
|
|
56
|
+
url: fullUrl,
|
|
57
|
+
date,
|
|
58
|
+
description: parsed.data.description || "",
|
|
59
|
+
content: stripHtml(parsed.content.slice(0, 500)),
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// Skip files that can't be read
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return entries;
|
|
68
|
+
}
|
|
69
|
+
export async function generateFeed(rootDir, settings) {
|
|
70
|
+
const baseUrl = settings.site.baseUrl || "http://localhost:1900";
|
|
71
|
+
const feedUrl = settings.seo.rss?.feedUrl || "/feed.xml";
|
|
72
|
+
const siteUrl = settings.seo.rss?.siteUrl || baseUrl;
|
|
73
|
+
const entries = await readDirRecursive(rootDir, rootDir, baseUrl, settings);
|
|
74
|
+
// Sort by date, newest first
|
|
75
|
+
entries.sort((a, b) => b.date.getTime() - a.date.getTime());
|
|
76
|
+
// Take most recent 20
|
|
77
|
+
const recentEntries = entries.slice(0, 20);
|
|
78
|
+
const rssTitle = settings.seo.rss?.title || settings.site.title;
|
|
79
|
+
const rssDescription = settings.site.description || "RSS Feed";
|
|
80
|
+
const lastBuildDate = new Date().toUTCString();
|
|
81
|
+
const itemElements = recentEntries
|
|
82
|
+
.map((entry) => ` <item>
|
|
83
|
+
<title>${escapeXml(entry.title)}</title>
|
|
84
|
+
<link>${escapeXml(entry.url)}</link>
|
|
85
|
+
<pubDate>${entry.date.toUTCString()}</pubDate>
|
|
86
|
+
<guid>${escapeXml(entry.url)}</guid>
|
|
87
|
+
${entry.description ? `<description>${escapeXml(entry.description)}</description>` : ""}
|
|
88
|
+
</item>`)
|
|
89
|
+
.join("\n");
|
|
90
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
91
|
+
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
|
92
|
+
<channel>
|
|
93
|
+
<title>${escapeXml(rssTitle)}</title>
|
|
94
|
+
<link>${escapeXml(siteUrl)}</link>
|
|
95
|
+
<description>${escapeXml(rssDescription)}</description>
|
|
96
|
+
<lastBuildDate>${lastBuildDate}</lastBuildDate>
|
|
97
|
+
<atom:link href="${escapeXml(baseUrl + feedUrl)}" rel="self" type="application/rss+xml" />
|
|
98
|
+
${itemElements}
|
|
99
|
+
</channel>
|
|
100
|
+
</rss>`;
|
|
101
|
+
}
|
|
102
|
+
function escapeXml(text) {
|
|
103
|
+
return text
|
|
104
|
+
.replace(/&/g, "&")
|
|
105
|
+
.replace(/</g, "<")
|
|
106
|
+
.replace(/>/g, ">")
|
|
107
|
+
.replace(/"/g, """)
|
|
108
|
+
.replace(/'/g, "'");
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=feed.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feed.js","sourceRoot":"","sources":["../../src/generators/feed.ts"],"names":[],"mappings":"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"}
|