veslx 0.0.21 → 0.0.22

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
@@ -1,3 +1,130 @@
1
-
2
1
  # veslx
3
2
 
3
+ A CLI tool for turning markdown directories into beautiful documentation sites and slide presentations.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ bun install -g veslx
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```bash
14
+ # Initialize in your content directory
15
+ veslx init
16
+
17
+ # Start development server
18
+ veslx serve
19
+
20
+ # Build for production
21
+ veslx build
22
+ ```
23
+
24
+ ## Commands
25
+
26
+ | Command | Description |
27
+ |---------|-------------|
28
+ | `veslx init` | Create a `veslx.config.ts` configuration file |
29
+ | `veslx serve` | Start development server with hot reload |
30
+ | `veslx build` | Build for production to `dist/` |
31
+ | `veslx start` | Run as a background daemon (PM2) |
32
+ | `veslx stop` | Stop the daemon |
33
+
34
+ ## Content Structure
35
+
36
+ veslx scans your content directory for markdown files and renders them as posts or slides:
37
+
38
+ ```
39
+ content/
40
+ ├── my-post/
41
+ │ ├── README.mdx # Rendered as a blog post
42
+ │ └── SLIDES.mdx # Rendered as a presentation
43
+ ├── another-post/
44
+ │ └── README.mdx
45
+ └── ...
46
+ ```
47
+
48
+ ### Frontmatter
49
+
50
+ Add YAML frontmatter to control metadata:
51
+
52
+ ```mdx
53
+ ---
54
+ title: My Post Title
55
+ date: 2025-01-15
56
+ description: A brief description of the post
57
+ visibility: public
58
+ ---
59
+
60
+ Your content here...
61
+ ```
62
+
63
+ | Field | Description |
64
+ |-------|-------------|
65
+ | `title` | Display title (falls back to directory name) |
66
+ | `date` | Publication date |
67
+ | `description` | Short description shown in listings |
68
+ | `visibility` | Set to `hidden` to hide from listings |
69
+
70
+ ### Slides
71
+
72
+ Create presentations in `SLIDES.mdx` files. Separate slides with `---`:
73
+
74
+ ```mdx
75
+ ---
76
+ title: My Presentation
77
+ ---
78
+
79
+ # Slide 1
80
+
81
+ Content for the first slide
82
+
83
+ ---
84
+
85
+ # Slide 2
86
+
87
+ Content for the second slide
88
+ ```
89
+
90
+ Navigate slides with arrow keys or `j`/`k`.
91
+
92
+ ## Configuration
93
+
94
+ The `veslx.config.ts` file specifies your content directory:
95
+
96
+ ```typescript
97
+ export default {
98
+ dir: './content',
99
+ }
100
+ ```
101
+
102
+ ## Features
103
+
104
+ - **MDX Support** - Write with React components in markdown
105
+ - **Math Rendering** - LaTeX equations via KaTeX
106
+ - **Syntax Highlighting** - Code blocks with Shiki
107
+ - **Hot Reload** - See changes instantly during development
108
+ - **Slide Presentations** - Keyboard-navigable slides from markdown
109
+ - **Dark Mode** - Automatic theme switching
110
+ - **Daemon Mode** - Run as a background service with PM2
111
+
112
+ ## Development
113
+
114
+ ```bash
115
+ # Install dependencies
116
+ bun install
117
+
118
+ # Run locally
119
+ bun run dev
120
+
121
+ # Type check
122
+ bun run typecheck
123
+
124
+ # Lint
125
+ bun run lint
126
+ ```
127
+
128
+ ## License
129
+
130
+ MIT
package/bin/lib/build.ts CHANGED
@@ -19,6 +19,11 @@ export default async function buildApp() {
19
19
  const veslxRoot = new URL('../..', import.meta.url).pathname;
20
20
  const outDir = path.join(cwd, 'dist')
21
21
 
22
+ // Resolve content directory relative to user's cwd (where config lives)
23
+ const contentDir = path.isAbsolute(config.dir)
24
+ ? config.dir
25
+ : path.resolve(cwd, config.dir);
26
+
22
27
  await build({
23
28
  root: veslxRoot,
24
29
  configFile: new URL('../../vite.config.ts', import.meta.url).pathname,
@@ -27,7 +32,7 @@ export default async function buildApp() {
27
32
  emptyOutDir: true,
28
33
  },
29
34
  plugins: [
30
- veslxPlugin(config.dir)
35
+ veslxPlugin(contentDir)
31
36
  ],
32
37
  })
33
38
 
package/bin/lib/serve.ts CHANGED
@@ -3,26 +3,6 @@ import { createServer, preview } from 'vite'
3
3
  import importConfig from "./import-config";
4
4
  import veslxPlugin from '../../plugin/src/plugin'
5
5
  import path from 'path'
6
- import fs from 'fs'
7
-
8
- // Find the node_modules directory that contains actual dependencies
9
- // This handles both local dev (node_modules in project root) and
10
- // bunx execution (node_modules in temp dir parent)
11
- function findNodeModulesDir(startDir: string): string {
12
- let dir = startDir
13
- while (dir !== '/' && dir !== '.') {
14
- const nm = path.join(dir, 'node_modules')
15
- // Check if node_modules exists and has real packages (not just .vite cache)
16
- if (fs.existsSync(nm)) {
17
- const contents = fs.readdirSync(nm)
18
- if (contents.some(f => !f.startsWith('.') && f !== 'veslx')) {
19
- return nm
20
- }
21
- }
22
- dir = path.dirname(dir)
23
- }
24
- return path.join(startDir, 'node_modules')
25
- }
26
6
 
27
7
  export default async function start() {
28
8
  const cwd = process.cwd()
@@ -37,28 +17,20 @@ export default async function start() {
37
17
  }
38
18
 
39
19
  const veslxRoot = new URL('../..', import.meta.url).pathname;
40
- const nodeModulesDir = findNodeModulesDir(veslxRoot)
20
+
21
+ // Resolve content directory relative to user's cwd (where config lives)
22
+ const contentDir = path.isAbsolute(config.dir)
23
+ ? config.dir
24
+ : path.resolve(cwd, config.dir);
41
25
 
42
26
  const server = await preview({
43
27
  root: veslxRoot,
44
28
  configFile: new URL('../../vite.config.ts', import.meta.url).pathname,
45
29
  plugins: [
46
- veslxPlugin(config.dir)
30
+ veslxPlugin(contentDir)
47
31
  ],
48
- // resolve: {
49
- // // Tell Vite to look for modules in the found node_modules directory
50
- // modules: [nodeModulesDir, 'node_modules'],
51
- // },
52
- // server: {
53
- // fs: {
54
- // // Allow serving from the parent node_modules
55
- // allow: [veslxRoot, nodeModulesDir, cwd],
56
- // },
57
- // },
58
32
  })
59
33
 
60
- // await server.listen()
61
-
62
34
  server.printUrls()
63
35
  server.bindCLIShortcuts({ print: true })
64
36
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "veslx",
3
- "version": "0.0.21",
3
+ "version": "0.0.22",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "veslx": "bin/veslx.ts"
@@ -2,7 +2,7 @@ import { type Plugin, type Connect } from 'vite'
2
2
  import path from 'path'
3
3
  import fs from 'fs'
4
4
  import { buildAll } from './lib'
5
- import chokidar from 'chokidar'
5
+ import chokidar, { type FSWatcher } from 'chokidar'
6
6
  import type { IncomingMessage, ServerResponse } from 'http'
7
7
 
8
8
  /**
@@ -24,18 +24,21 @@ function copyDirSync(src: string, dest: string) {
24
24
  }
25
25
  }
26
26
 
27
- export default function contentPlugin(inputDir: string): Plugin {
27
+ export default function contentPlugin(contentDir: string): Plugin {
28
28
 
29
- if (!inputDir) {
29
+ if (!contentDir) {
30
30
  throw new Error('Content directory must be specified.')
31
31
  }
32
32
 
33
- // Resolve dir to absolute path from user's cwd
34
- const dir = path.isAbsolute(inputDir) ? inputDir : path.resolve(process.cwd(), inputDir)
33
+ if (!path.isAbsolute(contentDir)) {
34
+ throw new Error(`Content directory must be an absolute path, got: ${contentDir}`)
35
+ }
36
+
37
+ const dir = contentDir
35
38
 
36
39
  const buildFn = () => buildAll([dir])
37
40
 
38
- let watchers: chokidar.FSWatcher[] = []
41
+ let watchers: FSWatcher[] = []
39
42
 
40
43
  // Server middleware for serving content files
41
44
  const urlToDir = new Map<string, string>()
package/vite.config.ts CHANGED
@@ -53,9 +53,6 @@ export default defineConfig({
53
53
  host: '0.0.0.0',
54
54
  port: process.env.PORT ? parseInt(process.env.PORT, 10) : 3000,
55
55
  strictPort: true,
56
- fs: {
57
- allow: ['..', '../..'],
58
- },
59
56
  allowedHosts: true,
60
57
  },
61
58
  build: {