host-mdx 2.0.0 → 2.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) Manas Ravindra Makde <manasmakde@gmail.com> (https://manasmakde.github.io/)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  A cli tool to create and serve a static html website from a given mdx directory
6
6
 
7
- ## Usage
7
+ ## 🛠️ Usage
8
8
 
9
9
  ```
10
10
  host-mdx [options]
@@ -18,12 +18,15 @@ Options:
18
18
  --track-changes, -t Tracks any changes made & auto reloads
19
19
  --verobse, -v Shows additional log messages
20
20
  ```
21
+
22
+ > If `--input-path` is not provided it will default to `./` i.e. current working directory
23
+ > If `--output-path` is not provided a temp folder will be created automatically & deleted upon exit
21
24
 
22
25
  Add a file by the name `.hostmdxignore` at the root of your project to filter out which files/folders to skip while generating html
23
26
  (similar to [.gitignore](https://git-scm.com/docs/gitignore))
24
27
 
25
28
 
26
- Add a file by the name `host-mdx.js` at the root of your project as a config file with the following:
29
+ Add a file by the name `host-mdx.js` at the root of your input folder as a config file with the following:
27
30
 
28
31
  ```js
29
32
  // Modify
@@ -32,19 +35,20 @@ modBundleMDXSettings(settings)
32
35
 
33
36
  // Hooks
34
37
  onSiteCreateStart(inputPath, outputPath)
35
- onSiteCreateEnd(inputPath, outputPath)
38
+ onSiteCreateEnd(inputPath, outputPath, wasInterrupted)
36
39
  onFileCreateStart(inputFilePath, outputFilePath)
37
40
  onFileCreateEnd(inputFilePath, outputFilePath)
38
41
  ```
39
42
  > Note: Any changes made to `host-mdx.js` require complete restart otherwise changes will not reflect
40
43
 
41
- ## Example
44
+ ## 📖 Example
42
45
 
43
46
  Command:
44
47
  ```bash
45
48
  npx host-mdx --input-path="path/to/my-website-template" --output-path="path/to/my-website" --port=3113 -t
46
49
  ```
47
50
 
51
+
48
52
  Input Directory:
49
53
  ```
50
54
  my-website-template/
@@ -82,7 +86,7 @@ export function onSiteCreateStart(inputPath, outputPath) {
82
86
  console.log("onSiteCreateStart", inputPath, outputPath)
83
87
  }
84
88
  export function onSiteCreateEnd(inputPath, outputPath, wasSuccessful){
85
- console.log("onSiteCreateEnd", inputPath, outputPath)
89
+ console.log("onSiteCreateEnd", inputPath, outputPath, wasSuccessful)
86
90
  }
87
91
  export function onFileCreateStart(inputFilePath, outputFilePath){
88
92
  console.log("onFileCreateStart", inputFilePath, outputFilePath)
@@ -120,4 +124,7 @@ my-website/
120
124
 
121
125
  The site will now be visible in the browser at `localhost:3113`
122
126
 
123
- > For a live example take a look at [sourcesnippet.github.io](https://sourcesnippet.github.io/)
127
+
128
+ ## 🔑 License
129
+
130
+ MIT © [Manas Ravindra Makde](https://manasmakde.github.io/)
package/index.js CHANGED
@@ -3,18 +3,22 @@
3
3
  import fs from 'fs'
4
4
  import os from 'os'
5
5
  import path from 'path'
6
- import http from 'http'
6
+ import polka from 'polka';
7
+ import sirv from 'sirv';
7
8
  import ignore from "ignore";
8
9
  import chokidar from 'chokidar';
9
10
  import * as readline from 'readline';
11
+ import { pathToFileURL } from 'url';
10
12
  import { mdxToHtml } from './mdx-to-html.js'
11
13
 
12
14
 
13
15
  // To-Set Properties
14
- const APP_NAME = "host-mdx"
15
- const DEFAULT_PORT = 3000
16
- const IGNORE_FILE_NAME = ".hostmdxignore"
17
- const CONFIG_FILE_NAME = "host-mdx.js"
16
+ const APP_NAME = "host-mdx";
17
+ const DEFAULT_PORT = 3000;
18
+ const IGNORE_FILE_NAME = ".hostmdxignore";
19
+ const CONFIG_FILE_NAME = "host-mdx.js";
20
+ const NOT_FOUND_404_FILE = "404.html"
21
+ const NOT_FOUND_404_MESSAGE = "404"
18
22
  const DEFAULT_IGNORES = `
19
23
  ${IGNORE_FILE_NAME}
20
24
  ${CONFIG_FILE_NAME}
@@ -22,21 +26,21 @@ node_modules
22
26
  package-lock.json
23
27
  package.json
24
28
  .git
25
- `
29
+ `;
26
30
 
27
31
 
28
32
  // Flags
29
- const CREATE_FLAG = "--create-only"
30
- const CREATE_SHORT_FLAG = "-c"
31
- const HELP_FLAG = "--help"
32
- const HELP_SHORT_FLAG = "-h"
33
- const INPUT_PATH_FLAG = "--input-path"
34
- const OUTPUT_PATH_FLAG = "--output-path"
35
- const PORT_FLAG = "--port"
36
- const VERBOSE_FLAG = "--verobse"
37
- const VERBOSE_SHORT_FLAG = "-v"
38
- const TRACK_CHANGES_FLAG = "--track-changes"
39
- const TRACK_CHANGES_SHORT_FLAG = "-t"
33
+ const CREATE_FLAG = "--create-only";
34
+ const CREATE_SHORT_FLAG = "-c";
35
+ const HELP_FLAG = "--help";
36
+ const HELP_SHORT_FLAG = "-h";
37
+ const INPUT_PATH_FLAG = "--input-path";
38
+ const OUTPUT_PATH_FLAG = "--output-path";
39
+ const PORT_FLAG = "--port";
40
+ const VERBOSE_FLAG = "--verbose";
41
+ const VERBOSE_SHORT_FLAG = "-v";
42
+ const TRACK_CHANGES_FLAG = "--track-changes";
43
+ const TRACK_CHANGES_SHORT_FLAG = "-t";
40
44
 
41
45
 
42
46
  // Messages & Errors
@@ -50,15 +54,16 @@ ${OUTPUT_PATH_FLAG}=... The path to which all html files will be generated
50
54
  ${PORT_FLAG}=... Localhost port number on which to host
51
55
  ${TRACK_CHANGES_FLAG}, ${TRACK_CHANGES_SHORT_FLAG} Tracks any changes made & auto reloads
52
56
  ${VERBOSE_FLAG}, ${VERBOSE_SHORT_FLAG} Shows additional log messages
53
- `
57
+ `;
54
58
 
55
59
 
56
60
  // Private Properties
57
- let isCreatingSite = false // Prevents site from being recreated if creation is already ongoing
58
- let isVerbose = false
59
- let configs
60
- let server
61
- const TEMP_HTML_DIR = path.join(os.tmpdir(), `${APP_NAME}`)
61
+ let isCreatingSite = false; // Prevents site from being recreated if creation is already ongoing
62
+ let isCreateSitePending = false // Keeps track if files have been modified and site needs to be recreated
63
+ let isVerbose = false;
64
+ let configs;
65
+ let app;
66
+ const TEMP_HTML_DIR = path.join(os.tmpdir(), `${APP_NAME}`);
62
67
  const TIME_OPTIONS = {
63
68
  year: 'numeric',
64
69
  month: 'long',
@@ -68,23 +73,7 @@ const TIME_OPTIONS = {
68
73
  second: 'numeric',
69
74
  hour12: false,
70
75
  fractionalSecondDigits: 3
71
- }
72
- const MIME_TYPE = { // Maps extensions to mime protocol
73
- '.html': 'text/html',
74
- '.css': 'text/css',
75
- '.js': 'text/javascript',
76
- '.json': 'application/json',
77
- '.png': 'image/png',
78
- '.jpg': 'image/jpeg',
79
- '.ico': 'image/x-icon',
80
- '.wav': 'audio/wav',
81
- '.webp': 'image/webp',
82
- '.mp3': 'audio/mpeg',
83
- '.mp4': 'video/mp4',
84
- '.svg': 'image/svg+xml',
85
- '.pdf': 'application/pdf',
86
- '.zip': 'application/zip',
87
- }
76
+ };
88
77
 
89
78
 
90
79
  // Utility Methods
@@ -97,18 +86,16 @@ function log(msg, checkVerbose = false) {
97
86
  console.log(`[${APP_NAME} ${timestamp}] ${msg}`)
98
87
  }
99
88
  function createTempDir() {
100
-
101
- // Delete existing temp dir
102
- if (fs.existsSync(TEMP_HTML_DIR)) {
103
- fs.rmSync(TEMP_HTML_DIR, { recursive: true, force: true })
104
- }
89
+ // Create default temp html dir
90
+ fs.mkdirSync(TEMP_HTML_DIR, { recursive: true });
105
91
 
106
92
 
107
- // Create default temp html dir
108
- fs.mkdirSync(TEMP_HTML_DIR, { recursive: true })
93
+ // Generate time stamp
94
+ const now = new Date()
95
+ const timestamp = `${now.getFullYear()}-${String(now.getMonth() + 1)}-${now.getDate()}T${now.getHours()}_${now.getMinutes()}_${now.getSeconds()}`
109
96
 
110
97
 
111
- return fs.mkdtempSync(path.join(TEMP_HTML_DIR, `/html-`));
98
+ return fs.mkdtempSync(path.join(TEMP_HTML_DIR, `html-${timestamp}-`));
112
99
  }
113
100
  function getIgnore(ignoreFilePath) {
114
101
  const ig = ignore();
@@ -137,19 +124,22 @@ function createFile(filePath, fileContent = "") {
137
124
  async function createSite(inputPath, outputPath) {
138
125
  // Exit if already creating
139
126
  if (isCreatingSite) {
140
- log("site creation already ongoing!")
127
+ log("Site creation already ongoing! Added to pending")
128
+ isCreateSitePending = true
141
129
  return
142
130
  }
143
131
 
144
132
 
145
133
  // Set creating status to ongoing
146
134
  isCreatingSite = true
135
+ isCreateSitePending = false
147
136
 
148
137
 
149
138
  // Get config properties
150
139
  let configFilePath = path.join(inputPath, `./${CONFIG_FILE_NAME}`)
151
140
  if (fs.existsSync(configFilePath)) {
152
- configs = await import(configFilePath);
141
+
142
+ configs = await import(pathToFileURL(configFilePath).href);
153
143
  }
154
144
 
155
145
 
@@ -171,13 +161,20 @@ async function createSite(inputPath, outputPath) {
171
161
 
172
162
  // Iterate through all folders & files
173
163
  const stack = [inputPath];
174
- while (stack.length > 0) {
164
+ while (stack.length > 0 && !isCreateSitePending) {
165
+ // Continue if path does not exist
175
166
  const currentPath = stack.pop()
167
+ if (!fs.existsSync(currentPath)) {
168
+ continue;
169
+ }
170
+
171
+
172
+ // Get essentials
176
173
  const relToInput = path.relative(inputPath, currentPath)
177
174
  const toIgnore = inputPath != currentPath && ig.ignores(relToInput)
178
175
  const absToOutput = path.join(outputPath, relToInput)
179
176
  const isDir = fs.statSync(currentPath).isDirectory()
180
- const isMdx = !isDir && absToOutput.endsWith(".mdx")
177
+ const isMdx = !isDir && currentPath.endsWith(".mdx")
181
178
 
182
179
 
183
180
  // Skip if to ignore this path
@@ -240,21 +237,87 @@ async function createSite(inputPath, outputPath) {
240
237
 
241
238
 
242
239
  // Broadcast site creation ended
243
- log("Created site")
244
- configs?.onSiteCreateEnd?.(inputPath, outputPath)
240
+ log(`Created site at ${outputPath}`)
241
+ configs?.onSiteCreateEnd?.(inputPath, outputPath, isCreateSitePending)
242
+
243
+
244
+ // Reinvoke creation
245
+ if(isCreateSitePending){
246
+ await createSite(inputPath, outputPath);
247
+ }
248
+ }
249
+ function filterArgs(rawArgs) {
250
+ // Assign to create
251
+ let toCreateOnly = rawArgs.includes(CREATE_FLAG) || rawArgs.includes(CREATE_SHORT_FLAG)
252
+
253
+
254
+ // Assign input path
255
+ let inputPath = rawArgs.find(val => val.startsWith(INPUT_PATH_FLAG));
256
+ let inputPathProvided = inputPath !== undefined;
257
+ inputPath = inputPathProvided ? inputPath.split('=')[1] : process.cwd();
258
+
259
+
260
+ // Check input path
261
+ if (!fs.existsSync(inputPath) || !fs.lstatSync(inputPath).isDirectory()) {
262
+ log(`Invalid input path "${inputPath}"`)
263
+ return null;
264
+ }
265
+ else {
266
+ inputPath = inputPath !== "" ? path.resolve(inputPath) : inputPath; // To ensure input path is absolute
267
+ }
268
+
269
+
270
+ // Assign output path
271
+ let outputPath = rawArgs.find(val => val.startsWith(OUTPUT_PATH_FLAG));
272
+ let outputPathProvided = outputPath !== undefined;
273
+ outputPath = outputPathProvided ? outputPath.split('=')[1] : createTempDir();
274
+
275
+
276
+ // Check output path
277
+ if (!fs.existsSync(outputPath) || !fs.lstatSync(outputPath).isDirectory()) {
278
+ log(`Invalid output path "${outputPath}"`)
279
+ return null;
280
+ }
281
+ else {
282
+ outputPath = outputPath !== "" ? path.resolve(outputPath) : outputPath; // To ensure output path is absolute
283
+ }
284
+
285
+
286
+ // Assign port
287
+ let port = rawArgs.find(val => val.startsWith(PORT_FLAG));
288
+ let portProvided = port !== undefined;
289
+ port = portProvided ? Number(port.split('=')[1]) : DEFAULT_PORT;
290
+
291
+
292
+ // Check port
293
+ if (!Number.isInteger(port)) {
294
+ log(`Invalid port`)
295
+ return null;
296
+ }
297
+
298
+
299
+ // Assign tracking changes
300
+ let toTrackChanges = rawArgs.includes(TRACK_CHANGES_FLAG) || rawArgs.includes(TRACK_CHANGES_SHORT_FLAG);
301
+
302
+
303
+ return { toCreateOnly, inputPath, inputPathProvided, outputPath, outputPathProvided, toTrackChanges, port, portProvided, };
245
304
  }
246
305
 
247
306
 
248
307
  // Main Methods
249
308
  async function createSiteSafe(...args) {
309
+
310
+ let success = true;
250
311
  try {
251
312
  await createSite(...args);
252
313
  }
253
314
  catch (err) {
254
- isCreatingSite = false
255
- console.log(err);
256
- log("Failed to create site!");
315
+ success = false;
316
+ isCreatingSite = false;
317
+ log(`Failed to create site!\n${err}`);
257
318
  }
319
+
320
+ return success;
258
321
  }
259
322
  async function listenForKey(createSiteCallback) {
260
323
 
@@ -269,7 +332,7 @@ async function listenForKey(createSiteCallback) {
269
332
  createSiteCallback();
270
333
  }
271
334
  else if (key && key.sequence == '\x03') {
272
- server.close((e) => { process.exit() })
335
+ app.server.close((e) => { process.exit() })
273
336
  }
274
337
  });
275
338
  }
@@ -280,128 +343,92 @@ function startServer(htmlDir, port) { // Starts server at given port
280
343
 
281
344
 
282
345
  // Start Server
283
- const newServer = http.createServer((req, res) => {
284
-
285
- // Parse & Sanitize URL
286
- let parsedUrl = new URL("http://" + req.headers.host + req.url)
287
- let sanitizedUrl = path.normalize(parsedUrl.pathname).replace(/^(\.\.[\/\\])+/, '')
288
- let isDirectory = !Boolean(path.parse(sanitizedUrl).ext)
289
- let relativeFilePath = path.normalize(sanitizedUrl + (isDirectory ? "/index.html" : ""))
290
- let absoluteFilePath = path.join(path.resolve(htmlDir), relativeFilePath)
291
- let pathExists = fs.existsSync(absoluteFilePath)
292
-
293
- // Respondes with content of file
294
- if (pathExists)
295
- // read file from file system
296
- fs.readFile(absoluteFilePath, function (err, data) {
297
- if (err) {
298
- res.statusCode = 500
299
- res.end(`Error getting the file: ${err}.`)
300
- }
301
- else {
302
- // Based on the URL path, extract the file extention. e.g. .js, .doc, ...
303
- const ext = path.parse(absoluteFilePath).ext
304
- res.setHeader('Content-type', MIME_TYPE[ext] || 'text/plain') // if the file is found, set Content-type and send data
305
- res.end(data)
306
- }
307
-
308
- })
309
- else { // Respondes with 404 if file not found
310
- res.statusCode = 404
311
- res.end(`404 Invalid url not found!`)
346
+ const assets = sirv(htmlDir, { dev: true });
347
+ const newApp = polka({
348
+ onNoMatch: (req, res) => {
349
+
350
+ // Set status code to 404
351
+ res.statusCode = 404;
352
+
353
+
354
+ // Send 404 file if found otherwise default not found message
355
+ const errorFile = path.join(htmlDir, NOT_FOUND_404_FILE);
356
+ if (fs.existsSync(errorFile)) {
357
+ res.setHeader('Content-Type', 'text/html');
358
+ res.end(fs.readFileSync(errorFile));
359
+ } else {
360
+ res.end(NOT_FOUND_404_MESSAGE);
361
+ }
312
362
  }
313
- })
314
- newServer.listen(port, () => { log(`Server listening at ${port} ... (Press 'r' to manually reload, Press 'Ctrl+c' to exit)`) })
315
- newServer.on("close", () => { configs?.onHostEnd?.(port) });
363
+ }).use(assets)
364
+
365
+ newApp.listen(port)
366
+ newApp.server.on("close", () => { configs?.onHostEnd?.(port) });
367
+ newApp.server.on("error", (e) => { log(`Failed to start server: ${e.message}`); throw e; });
368
+ log(`Server listening at ${port} ... (Press 'r' to manually reload, Press 'Ctrl+c' to exit)`)
369
+
316
370
 
317
- return newServer
371
+ return newApp
318
372
  }
319
373
  async function Main() {
320
374
  // Get all arguments
321
- const args = process.argv.slice(2)
375
+ const rawArgs = process.argv.slice(2);
376
+
377
+
378
+ // Check if verbose
379
+ isVerbose = rawArgs.includes(VERBOSE_FLAG) || rawArgs.includes(VERBOSE_SHORT_FLAG);
322
380
 
323
381
 
324
382
  // Check if asked for help
325
- if (args.includes(HELP_FLAG) || args.includes(HELP_SHORT_FLAG)) {
383
+ if (rawArgs.includes(HELP_FLAG) || rawArgs.includes(HELP_SHORT_FLAG)) {
326
384
  console.log(HELP_MESSAGE)
327
385
  return;
328
386
  }
329
387
 
330
388
 
331
- // Assign to create
332
- let toCreateOnly = args.includes(CREATE_FLAG) || args.includes(CREATE_SHORT_FLAG)
333
-
334
- // Assign input path
335
- let inputPath = args.find(val => val.startsWith(INPUT_PATH_FLAG))
336
- inputPath = inputPath !== undefined ? inputPath.split('=')[1] : process.cwd()
337
-
338
- // Assign output path
339
- let outputPath = args.find(val => val.startsWith(OUTPUT_PATH_FLAG))
340
- let outputPathProvided = outputPath !== undefined
341
- outputPath = outputPathProvided ? outputPath.split('=')[1] : createTempDir()
342
-
343
- // Assign tracking changes
344
- let toTrackChanges = args.includes(TRACK_CHANGES_FLAG) || args.includes(TRACK_CHANGES_SHORT_FLAG)
345
-
346
- // Assign port
347
- let port = args.find(val => val.startsWith(PORT_FLAG))
348
- port = port !== undefined ? Number(port.split('=')[1]) : DEFAULT_PORT
349
-
350
- // Assign verbose
351
- isVerbose = args.includes(VERBOSE_FLAG) || args.includes(VERBOSE_SHORT_FLAG)
352
-
353
-
354
- // Check input path
355
- if (!fs.existsSync(inputPath) || !fs.lstatSync(inputPath).isDirectory()) {
356
- log(`Invalid input path "${inputPath}"`)
357
- return
358
- }
359
-
360
- // Check output path
361
- if (!fs.existsSync(outputPath) || !fs.lstatSync(outputPath).isDirectory()) {
362
- log(`Invalid output path "${outputPath}"`)
363
- return
364
- }
365
-
366
- // Check port
367
- if (!Number.isInteger(port)) {
368
- log(`Invalid port`)
369
- return
389
+ // Filter arguments
390
+ let args = filterArgs(rawArgs);
391
+ if (args === null) {
392
+ return;
370
393
  }
371
394
 
372
395
 
373
396
  // Create site from mdx & return if only needed to create site
374
- await createSiteSafe(inputPath, outputPath)
375
- if (toCreateOnly) {
397
+ let wasCreated = await createSiteSafe(args.inputPath, args.outputPath);
398
+ if (args.toCreateOnly) {
399
+ process.exitCode = !wasCreated ? 1 : 0; // Exit with error code if not created successfully
376
400
  return;
377
401
  }
378
402
 
379
403
 
380
404
  // Watch for key presses
381
- listenForKey(() => createSiteSafe(inputPath, outputPath))
405
+ listenForKey(() => createSiteSafe(args.inputPath, args.outputPath));
382
406
 
383
407
 
384
408
  // Watch for changes
385
- if (toTrackChanges) {
386
- chokidar.watch(inputPath, { ignoreInitial: true }).on('all', (event, path) => {
387
- createSiteSafe(inputPath, outputPath)
409
+ if (args.toTrackChanges) {
410
+ chokidar.watch(args.inputPath, {
411
+ ignoreInitial: true
412
+ }).on('all', (event, path) => {
413
+ createSiteSafe(args.inputPath, args.outputPath)
388
414
  });
389
415
  }
390
416
 
391
417
 
392
418
  // Start server
393
- server = startServer(outputPath, port)
419
+ app = startServer(args.outputPath, args.port);
394
420
 
395
421
 
396
422
  // Handle quit
397
- process.on("exit", () => {
423
+ const cleanup = () => {
398
424
  // Remove html path
399
- if (!outputPathProvided && fs.existsSync(outputPath)) {
400
- fs.rmSync(outputPath, { recursive: true, force: true })
425
+ if (!args.outputPathProvided && fs.existsSync(args.outputPath)) {
426
+ fs.rmSync(args.outputPath, { recursive: true, force: true })
401
427
  }
402
-
403
- process.exit(0);
404
- });
428
+ }
429
+ process.on("exit", cleanup);
430
+ process.on("SIGINT", cleanup);
431
+ process.on("SIGTERM", cleanup);
405
432
  }
406
433
 
407
434
  Main()
package/mdx-to-html.js CHANGED
@@ -1,19 +1,43 @@
1
- import React from 'react';
2
- import rehypeHighlight from "rehype-highlight";
3
- import * as _jsx_runtime from 'react/jsx-runtime';
1
+ import * as Preact from "preact";
2
+ import * as PreactDOM from "preact/compat";
3
+ import * as _jsx_runtime from 'preact/jsx-runtime';
4
+ import { renderToString } from 'preact-render-to-string';
4
5
  import { common } from 'lowlight';
5
6
  import { bundleMDX } from 'mdx-bundler';
6
- import { getMDXComponent } from 'mdx-bundler/client/index.js'
7
- import { renderToString } from 'react-dom/server';
8
7
  import { createRequire } from 'module';
8
+ import rehypeHighlight from "rehype-highlight";
9
9
 
10
+
11
+ // Constants
10
12
  const nativeRequire = createRequire(import.meta.url);
13
+ const jsxBundlerConfig = {
14
+ jsxLib: {
15
+ varName: 'Preact',
16
+ package: 'preact',
17
+ },
18
+ jsxDom: {
19
+ varName: 'PreactDom',
20
+ package: 'preact/compat',
21
+ },
22
+ jsxRuntime: {
23
+ varName: '_jsx_runtime',
24
+ package: 'preact/jsx-runtime',
25
+ },
26
+ }
27
+
11
28
 
29
+ // Methods
30
+ function getMDXComponent(code, globals) {
31
+ const fn = new Function(...Object.keys(globals), code);
32
+ const mdxExport = fn(...Object.values(globals));
33
+ return mdxExport.default;
34
+ }
12
35
  export async function mdxToHtml(mdxCode, baseUrl, modSettingsCallback = undefined) {
13
-
36
+
14
37
  // Assign default settings
15
38
  let settings = {
16
39
  source: mdxCode,
40
+ jsxConfig: jsxBundlerConfig,
17
41
  cwd: baseUrl,
18
42
  esbuildOptions: (options) => {
19
43
  options.platform = 'node'
@@ -30,13 +54,15 @@ export async function mdxToHtml(mdxCode, baseUrl, modSettingsCallback = undefine
30
54
 
31
55
 
32
56
  // Modify settings
33
- if(modSettingsCallback !== undefined){
57
+ if (modSettingsCallback !== undefined) {
34
58
  settings = modSettingsCallback(settings)
35
59
  }
36
60
 
37
61
 
38
62
  // Generate html
39
63
  const { code } = await bundleMDX(settings);
40
- const Component = getMDXComponent(code, { require: nativeRequire, cwd: baseUrl })
41
- return renderToString(React.createElement(Component));
64
+ const Component = getMDXComponent(code, { Preact, PreactDOM, _jsx_runtime, require: nativeRequire, cwd: baseUrl })
65
+
66
+
67
+ return renderToString(Preact.h(Component, {}));
42
68
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "host-mdx",
3
- "version": "2.0.0",
4
- "description": "Creates and serves a github pages style html directory from a corresponding mdx directory",
3
+ "version": "2.1.0",
4
+ "description": "A cli tool to create and serve a static html website from a given mdx directory",
5
5
  "main": "index.js",
6
6
  "type": "module",
7
7
  "scripts": {
@@ -15,9 +15,11 @@
15
15
  "ignore": "^7.0.5",
16
16
  "lowlight": "^3.3.0",
17
17
  "mdx-bundler": "^10.1.1",
18
- "react": "^19.2.3",
19
- "react-dom": "^19.2.3",
20
- "rehype-highlight": "^7.0.2"
18
+ "polka": "^0.5.2",
19
+ "preact": "^10.28.2",
20
+ "preact-render-to-string": "^6.6.5",
21
+ "rehype-highlight": "^7.0.2",
22
+ "sirv": "^3.0.2"
21
23
  },
22
24
  "keywords": [
23
25
  "mdx"