host-mdx 2.0.1 → 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,20 +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) {
175
165
  // Continue if path does not exist
176
166
  const currentPath = stack.pop()
177
- if(!fs.existsSync(currentPath)){
167
+ if (!fs.existsSync(currentPath)) {
178
168
  continue;
179
169
  }
180
170
 
181
-
171
+
182
172
  // Get essentials
183
173
  const relToInput = path.relative(inputPath, currentPath)
184
174
  const toIgnore = inputPath != currentPath && ig.ignores(relToInput)
185
175
  const absToOutput = path.join(outputPath, relToInput)
186
176
  const isDir = fs.statSync(currentPath).isDirectory()
187
- const isMdx = !isDir && absToOutput.endsWith(".mdx")
177
+ const isMdx = !isDir && currentPath.endsWith(".mdx")
188
178
 
189
179
 
190
180
  // Skip if to ignore this path
@@ -247,21 +237,87 @@ async function createSite(inputPath, outputPath) {
247
237
 
248
238
 
249
239
  // Broadcast site creation ended
250
- log("Created site")
251
- 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, };
252
304
  }
253
305
 
254
306
 
255
307
  // Main Methods
256
308
  async function createSiteSafe(...args) {
309
+
310
+ let success = true;
257
311
  try {
258
312
  await createSite(...args);
259
313
  }
260
314
  catch (err) {
261
- isCreatingSite = false
262
- console.log(err);
263
- log("Failed to create site!");
315
+ success = false;
316
+ isCreatingSite = false;
317
+ log(`Failed to create site!\n${err}`);
264
318
  }
319
+
320
+ return success;
265
321
  }
266
322
  async function listenForKey(createSiteCallback) {
267
323
 
@@ -276,144 +332,103 @@ async function listenForKey(createSiteCallback) {
276
332
  createSiteCallback();
277
333
  }
278
334
  else if (key && key.sequence == '\x03') {
279
- server.close((e) => { process.exit() })
335
+ app.server.close((e) => { process.exit() })
280
336
  }
281
337
  });
282
338
  }
283
339
  function startServer(htmlDir, port) { // Starts server at given port
284
-
340
+
285
341
  // Broadcast server starting
286
342
  configs?.onHostStart?.(port)
287
343
 
288
344
 
289
345
  // Start Server
290
- const newServer = http.createServer((req, res) => {
291
-
292
- // Parse & Sanitize URL
293
- let parsedUrl = new URL("http://" + req.headers.host + req.url)
294
- let sanitizedUrl = path.normalize(parsedUrl.pathname).replace(/^(\.\.[\/\\])+/, '')
295
- let isDirectory = !Boolean(path.parse(sanitizedUrl).ext)
296
- let relativeFilePath = path.normalize(sanitizedUrl + (isDirectory ? "/index.html" : ""))
297
- let absoluteFilePath = path.join(path.resolve(htmlDir), relativeFilePath)
298
- let pathExists = fs.existsSync(absoluteFilePath)
299
-
300
- // Respondes with content of file
301
- if (pathExists)
302
- // read file from file system
303
- fs.readFile(absoluteFilePath, function (err, data) {
304
- if (err) {
305
- res.statusCode = 500
306
- res.end(`Error getting the file: ${err}.`)
307
- }
308
- else {
309
- // Based on the URL path, extract the file extention. e.g. .js, .doc, ...
310
- const ext = path.parse(absoluteFilePath).ext
311
- res.setHeader('Content-type', MIME_TYPE[ext] || 'text/plain') // if the file is found, set Content-type and send data
312
- res.end(data)
313
- }
314
-
315
- })
316
- else { // Respondes with 404 if file not found
317
- res.statusCode = 404
318
- 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
+ }
319
362
  }
320
- })
321
- newServer.listen(port, () => { log(`Server listening at ${port} ... (Press 'r' to manually reload, Press 'Ctrl+c' to exit)`) })
322
- newServer.on("close", () => { configs?.onHostEnd?.(port) });
323
- newServer.on("error", (e) => { log(`Error Starting server ${e.message}`); throw e; });
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)`)
324
369
 
325
370
 
326
- return newServer
371
+ return newApp
327
372
  }
328
373
  async function Main() {
329
374
  // Get all arguments
330
- 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);
331
380
 
332
381
 
333
382
  // Check if asked for help
334
- if (args.includes(HELP_FLAG) || args.includes(HELP_SHORT_FLAG)) {
383
+ if (rawArgs.includes(HELP_FLAG) || rawArgs.includes(HELP_SHORT_FLAG)) {
335
384
  console.log(HELP_MESSAGE)
336
385
  return;
337
386
  }
338
387
 
339
388
 
340
- // Assign to create
341
- let toCreateOnly = args.includes(CREATE_FLAG) || args.includes(CREATE_SHORT_FLAG)
342
-
343
- // Assign input path
344
- let inputPath = args.find(val => val.startsWith(INPUT_PATH_FLAG))
345
- inputPath = inputPath !== undefined ? inputPath.split('=')[1] : process.cwd()
346
-
347
- // Assign output path
348
- let outputPath = args.find(val => val.startsWith(OUTPUT_PATH_FLAG))
349
- let outputPathProvided = outputPath !== undefined
350
- outputPath = outputPathProvided ? outputPath.split('=')[1] : createTempDir()
351
-
352
- // Assign tracking changes
353
- let toTrackChanges = args.includes(TRACK_CHANGES_FLAG) || args.includes(TRACK_CHANGES_SHORT_FLAG)
354
-
355
- // Assign port
356
- let port = args.find(val => val.startsWith(PORT_FLAG))
357
- port = port !== undefined ? Number(port.split('=')[1]) : DEFAULT_PORT
358
-
359
- // Assign verbose
360
- isVerbose = args.includes(VERBOSE_FLAG) || args.includes(VERBOSE_SHORT_FLAG)
361
-
362
-
363
- // Check input path
364
- if (!fs.existsSync(inputPath) || !fs.lstatSync(inputPath).isDirectory()) {
365
- log(`Invalid input path "${inputPath}"`)
366
- return
367
- }
368
-
369
- // Check output path
370
- if (!fs.existsSync(outputPath) || !fs.lstatSync(outputPath).isDirectory()) {
371
- log(`Invalid output path "${outputPath}"`)
372
- return
373
- }
374
-
375
- // Check port
376
- if (!Number.isInteger(port)) {
377
- log(`Invalid port`)
378
- return
389
+ // Filter arguments
390
+ let args = filterArgs(rawArgs);
391
+ if (args === null) {
392
+ return;
379
393
  }
380
394
 
381
395
 
382
396
  // Create site from mdx & return if only needed to create site
383
- await createSiteSafe(inputPath, outputPath)
384
- 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
385
400
  return;
386
401
  }
387
402
 
388
403
 
389
404
  // Watch for key presses
390
- listenForKey(() => createSiteSafe(inputPath, outputPath))
405
+ listenForKey(() => createSiteSafe(args.inputPath, args.outputPath));
391
406
 
392
407
 
393
408
  // Watch for changes
394
- if (toTrackChanges) {
395
- chokidar.watch(inputPath, {
396
- ignoreInitial: true,
397
- ignored: (path, stats) => isCreatingSite // Ignore if site creation is ongoing
409
+ if (args.toTrackChanges) {
410
+ chokidar.watch(args.inputPath, {
411
+ ignoreInitial: true
398
412
  }).on('all', (event, path) => {
399
- createSiteSafe(inputPath, outputPath)
413
+ createSiteSafe(args.inputPath, args.outputPath)
400
414
  });
401
415
  }
402
416
 
403
417
 
404
418
  // Start server
405
- server = startServer(outputPath, port)
419
+ app = startServer(args.outputPath, args.port);
406
420
 
407
421
 
408
422
  // Handle quit
409
- process.on("exit", () => {
423
+ const cleanup = () => {
410
424
  // Remove html path
411
- if (!outputPathProvided && fs.existsSync(outputPath)) {
412
- fs.rmSync(outputPath, { recursive: true, force: true })
425
+ if (!args.outputPathProvided && fs.existsSync(args.outputPath)) {
426
+ fs.rmSync(args.outputPath, { recursive: true, force: true })
413
427
  }
414
-
415
- process.exit(0);
416
- });
428
+ }
429
+ process.on("exit", cleanup);
430
+ process.on("SIGINT", cleanup);
431
+ process.on("SIGTERM", cleanup);
417
432
  }
418
433
 
419
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.1",
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"