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 +21 -0
- package/README.md +13 -6
- package/index.js +172 -145
- package/mdx-to-html.js +35 -9
- package/package.json +7 -5
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
|
|
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
|
-
|
|
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
|
|
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 = "--
|
|
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
|
|
59
|
-
let
|
|
60
|
-
let
|
|
61
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
108
|
-
|
|
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,
|
|
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("
|
|
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
|
-
|
|
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 &&
|
|
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(
|
|
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
|
-
|
|
255
|
-
|
|
256
|
-
log(
|
|
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
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
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
|
-
|
|
315
|
-
|
|
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
|
|
371
|
+
return newApp
|
|
318
372
|
}
|
|
319
373
|
async function Main() {
|
|
320
374
|
// Get all arguments
|
|
321
|
-
const
|
|
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 (
|
|
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
|
-
//
|
|
332
|
-
let
|
|
333
|
-
|
|
334
|
-
|
|
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, {
|
|
387
|
-
|
|
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
|
-
|
|
419
|
+
app = startServer(args.outputPath, args.port);
|
|
394
420
|
|
|
395
421
|
|
|
396
422
|
// Handle quit
|
|
397
|
-
|
|
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
|
-
|
|
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
|
|
2
|
-
import
|
|
3
|
-
import * as _jsx_runtime from '
|
|
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
|
-
|
|
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.
|
|
4
|
-
"description": "
|
|
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
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"
|
|
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"
|