Skip to content

Commit e3c531c

Browse files
updates
1 parent d2e7e13 commit e3c531c

File tree

4 files changed

+246
-53
lines changed

4 files changed

+246
-53
lines changed

src/documentation/index.js

Lines changed: 227 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,121 @@ SOFTWARE.
2424
2525
*/
2626

27+
const template = {
28+
"charset": "utf-8",
29+
"title": "Documentation",
30+
"footer": `Made with ${link('_just', 'https://just.is-a.dev/')}.`,
31+
"viewport": "width=device-width, initial-scale=1.0",
32+
"twitter": "summary_large_image",
33+
"lang": "en",
34+
"headerTagIDStart": "hdr"
35+
}
2736
const fs = require('fs');
2837
const path = require('path');
29-
38+
const { JSDOM } = require('jsdom');
3039
const [HTML, CSS, JS] = process.argv.slice(2);
40+
const config = JSON.parse(fs.readFileSync('just.config.json', template.charset));
41+
const docsConfig = config.docs_config;
42+
43+
const charset = docsConfig ? docsConfig.charset || template.charset : template.charset;
44+
45+
const rootDirA = './';
46+
const extensions = ['.md', '.mdx', '.html'];
47+
48+
function getFiles(dir) {
49+
let results = [];
50+
const list = fs.readdirSync(dir);
51+
list.forEach(file => {
52+
const filePath = path.join(dir, file);
53+
const stat = fs.statSync(filePath);
54+
if (stat && stat.isDirectory()) {
55+
results = results.concat(getFiles(filePath));
56+
} else if (extensions.includes(path.extname(file))) {
57+
results.push(filePath);
58+
}
59+
});
60+
return results;
61+
}
62+
63+
function getTitleFromHtml(filePath) {
64+
const content = fs.readFileSync(filePath, charset);
65+
const dom = new JSDOM(content);
66+
const title = dom.window.document.querySelector('title');
67+
return title ? title.textContent : null;
68+
}
69+
70+
function getTitleFromMd(filePath) {
71+
const content = fs.readFileSync(filePath, charset).split('\n');
72+
if (content[0].startsWith('_just: title: ')) {
73+
return content[0].replace('_just: title: ', '').trim();
74+
}
75+
return null;
76+
}
77+
78+
function getPageList() {
79+
const files = getFiles(rootDirA);
80+
const pages = [];
81+
82+
files.forEach(file => {
83+
const extname = path.extname(file);
84+
let title;
85+
let pagePath = file.replace(rootDirA, '').replace(extname, '');
86+
87+
if (pagePath.endsWith('/index')) {
88+
pagePath = pagePath.split('').reverse().join('').replace('index'.split('').reverse().join(''), '').split('').reverse().join('');
89+
title = 'Home';
90+
} else {
91+
title = path.basename(pagePath);
92+
}
93+
94+
if (extname === '.html') {
95+
const htmlTitle = getTitleFromHtml(file);
96+
if (htmlTitle) title = htmlTitle;
97+
} else if (extname === '.md' || extname === '.mdx') {
98+
const mdTitle = getTitleFromMd(file);
99+
if (mdTitle) title = mdTitle;
100+
}
101+
102+
pages.push({ path: pagePath, title });
103+
});
104+
105+
return pages;
106+
}
107+
function addFolderToPageList(pageList) {
108+
return pageList.map(page => {
109+
const folderNameArray = page.path.split('/').filter(Boolean);
110+
const folderName = folderNameArray.length > 1 ? folderNameArray[folderNameArray.length - 2] : null;
111+
return { ...page, folder: folderName };
112+
});
113+
}
114+
const pageList = getPageList();
115+
116+
function generateListItems(PageList) {
117+
const folderMap = {};
118+
119+
PageList.forEach(page => {
120+
const folder = page.folder || '';
121+
if (!folderMap[folder]) {
122+
folderMap[folder] = [];
123+
}
124+
folderMap[folder].push(page);
125+
});
126+
127+
let listItemsHtml = '';
128+
129+
for (const [folderName, pages] of Object.entries(folderMap)) {
130+
listItemsHtml += `${ folderName != '' ? `<li>
131+
<span><strong>${folderName}</strong></span>
132+
<ul>` : '<li><ul>'}`;
133+
pages.forEach(page => {
134+
listItemsHtml += `<li><a href="${page.path}"><span>${page.title}</span></a></li>`;
135+
});
136+
listItemsHtml += ` </ul>
137+
</li>`;
138+
}
139+
140+
return listItemsHtml;
141+
}
31142

32143
const biMDtoHTML = (input) => {
33144
let text = input;
@@ -120,6 +231,9 @@ function hbuoclpMDtoHTML(text, maxBlockquoteLevel = 4) {
120231
return resultTextArray.join('');
121232
}
122233

234+
const link = (text, link_, ext = false) => `<a href="${link_}"${ext ? ' id="ext"' : ''}>${text}</a>`;
235+
const span = (text) => `<span>${text}</span>`;
236+
123237
function findMarkdownFiles(dir) {
124238
let results = [];
125239
const list = fs.readdirSync(dir);
@@ -135,17 +249,122 @@ function findMarkdownFiles(dir) {
135249
return results;
136250
}
137251

138-
const rootDir = process.cwd();
139-
const markdownFiles = findMarkdownFiles(rootDir);
252+
const rootDirB = process.cwd();
253+
const markdownFiles = findMarkdownFiles(rootDirB);
254+
255+
const title = docsConfig ? docsConfig.title || template.title : template.title;
256+
const metatitle = docsConfig ? docsConfig.metatitle || title : title;
257+
const ogtitle = docsConfig && docsConfig.og ? docsConfig.og.title || metatitle : metatitle;
258+
const description = docsConfig ? docsConfig.description || undefined : undefined;
259+
const ogdescription = docsConfig && docsConfig.og ? docsConfig.og.description || description : description;
260+
const viewport = docsConfig ? docsConfig.viewport || template.viewport : template.viewport;
261+
const twitter = docsConfig && docsConfig.twitter ? docsConfig.twitter.card || template.twitter : template.twitter;
262+
const metaKeywords = docsConfig ? docsConfig.keywords || undefined : undefined;
263+
const lang = docsConfig ? docsConfig.htmlLang || template.lang : template.lang;
264+
const yandexVerification = docsConfig ? docsConfig.yandex || undefined : undefined;
265+
const googleAnalytics = docsConfig ? docsConfig.googleAnalytics || undefined : undefined;
266+
const googleVerification = docsConfig ? docsConfig.google || undefined : undefined;
267+
const logoPath = docsConfig ? docsConfig.logo || undefined : undefined;
268+
269+
const insertHTMLinHead = docsConfig ? docsConfig.insertInHTMLHead || '' : '';
270+
271+
const keywords = metaKeywords ? `<meta name="keywords" content="${metaKeywords}"/>` : '';
272+
const desc = description ? `<meta name="description" content="${description}"/>` : '';
273+
const ogdesc = ogdescription ? `<meta property="og:description" content="${ogdescription}"/>` : '';
274+
const ogtitl = ogtitle ? `<meta property="og:title" content="${ogtitle}"/>` : '';
275+
const logo = logoPath ? `<img src="${logoPath}" width="35px" height="auto" alt="Logo">` : '';
276+
const name = docsConfig && docsConfig.title ? span(title) : logoPath ? '' : span(title);
277+
const htmlLang = lang ? ` lang="${`${lang}`.toLowerCase()}"` : '';
278+
const htmlhead = () => {
279+
let output = `
280+
${keywords}
281+
${desc}
282+
${ogtitl}
283+
${ogdesc}
284+
<meta property="og:type" content="website"/>`;
285+
if (twitter) {
286+
output += `<meta property="twitter:card" content="${twitter}"/>`
287+
}
288+
if (yandexVerification) {
289+
output += `\n<meta name="yandex-verification" content="${yandexVerification}"/>`;
290+
}
291+
if (googleVerification) {
292+
output += `\n<meta name="google-site-verification" content="${googleVerification}" />`;
293+
}
294+
if (googleAnalytics) {
295+
output += `\n<script async src="https://www.googletagmanager.com/gtag/js?id=${googleAnalytics}"></script>
296+
<script>
297+
window.dataLayer = window.dataLayer || [];
298+
function gtag() {
299+
dataLayer.push(arguments);
300+
}
301+
gtag('js', new Date());
302+
gtag('config', '${googleAnalytics}');
303+
</script>`
304+
}
305+
return output;
306+
}
307+
308+
filterText = (text) => text
309+
.replaceAll('_', `&#${'_'.charCodeAt(0)}`)
310+
.replaceAll('<script>', `&#${'<'.charCodeAt(0)}script&#${'>'.charCodeAt(0)}`)
311+
.replaceAll('</script>', `&#${'<'.charCodeAt(0)}&#${'/'.charCodeAt(0)}script&#${'>'.charCodeAt(0)}`);
312+
function makeJSDOM(data) {
313+
return `<!DOCTYPE html>
314+
<html>
315+
<head>
316+
<meta charset="${charset}">
317+
<title>hello, world!</title>
318+
</head>
319+
<body>
320+
${data}
321+
</body>
322+
</html>`;
323+
}
140324

141325
markdownFiles.forEach(file => {
142-
const content = fs.readFileSync(file, 'utf-8');
326+
const content = fs.readFileSync(file, charset);
143327
const fileNameWithoutExt = path.basename(file, path.extname(file));
144328
const outFilePath = (ext) => path.join(path.dirname(file), `${fileNameWithoutExt}.${ext}`);
145329

146-
const toHTML = hbuoclpMDtoHTML(content);
330+
const toHTML = hbuoclpMDtoHTML(content).replace(/<h1>(.*?)<\/h1>/g, (match, p1) => {
331+
return `<h1 id="${headerTagIDStart}${index++}">${p1}</h1>`;
332+
}).replace(/<h2>(.*?)<\/h2>/g, (match, p1) => {
333+
return `<h2 id="${headerTagIDStart}${index++}">${p1}</h2>`;
334+
}).replace(/<h3>(.*?)<\/h3>/g, (match, p1) => {
335+
return `<h3 id="${headerTagIDStart}${index++}">${p1}</h3>`;
336+
});
337+
const dom = new JSDOM(makeJSDOM(toHTML));
338+
const document = dom.window.document;
339+
const h1 = Array.from(document.querySelectorAll('h1')).map(h => [h.textContent, h.id]);
340+
const hT = Array.from(document.querySelectorAll('h2, h3')).map(h => [h.textContent, h.id]);
341+
const contents = [
342+
...h1.map(item => ({ ...item, first: true })),
343+
...hT.map(item => ({ ...item, first: false }))
344+
];
345+
let pageHeaders = '';
346+
for (const [text, id, first] of Object.entries(contents)) {
347+
pageHeaders += `<li${ first ? ' class="secondary"' : '' }>
348+
<a href="#${id}">
349+
<span>${text}</span>
350+
</a>
351+
</li>`;
352+
}
353+
354+
const pages = generateListItems(addFolderToPageList(pageList));
355+
let outHTML = HTML
356+
.replace('<html>', `<html lang="${htmlLang}">`)
357+
.replace('REPLACE_CHARSET', charset)
358+
.replace('REPLACE_VIEWPORT', viewport)
359+
.replace('REPLACE_TITLE', metatitle)
360+
.replace('REPLACE_DATA', htmlhead())
361+
.replace('REPLACE_CUSTOM', insertHTMLinHead)
362+
.replace('REPLACE_LOGO', logo)
363+
.replace('REPLACE_NAME', filterText(name))
364+
.replace('REPLACE_PAGES', filterText(pages))
365+
.replace('REPLACE_CONTENTS', filterText(pageHeaders));
147366

148-
fs.writeFileSync(outFilePath('html'), HTML.replace('REPLACE_CONTENT', toHTML), 'utf-8');
149-
fs.writeFileSync(outFilePath('css'), CSS, 'utf-8');
150-
fs.writeFileSync(outFilePath('js'), JS, 'utf-8');
367+
fs.writeFileSync(outFilePath('html'), outHTML.replace('REPLACE_CONTENT', toHTML), charset);
368+
fs.writeFileSync(outFilePath('css'), CSS, template.charset);
369+
fs.writeFileSync(outFilePath('js'), JS, template.charset);
151370
});

src/documentation/templates/page.html

Lines changed: 10 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
<!DOCTYPE html>
22
<html>
33
<head>
4-
<title>Just an Ultimate Site Tool - Documentation</title>
5-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6-
<meta charset="utf-8">
4+
<meta charset="REPLACE_CHARSET">
5+
<meta name="viewport" content="REPLACE_VIEWPORT">
6+
<title>REPLACE_TITLE</title>
77
<link href="/index.css" rel="stylesheet">
88
<script src="/index.js"></script>
9-
9+
REPLACE_DATA
1010
<link rel="preconnect" href="https://fonts.googleapis.com">
1111
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
1212
<link href="https://fonts.googleapis.com/css2?family=Murecho:[email protected]&family=Rubik:ital,wght@0,300..900;1,300..900&display=swap" rel="stylesheet">
13-
13+
REPLACE_CUSTOM
1414
</head>
1515
<body style="--hc: 0">
1616
<header>
1717
<nav class="navbar">
1818
<div class="heading">
19-
<img src="img/just.png" width="35px" height="auto" alt="Logo">
20-
<span>Documentation</span>
19+
REPLACE_LOGO
20+
REPLACE_NAME
2121
</div>
2222
<div class="links">
2323
<a>Link 1</a>
@@ -34,43 +34,14 @@
3434
<div id="main">
3535
<nav class="left">
3636
<ul>
37-
<li>
38-
<span><strong>Folder 1</strong></span>
39-
<ul>
40-
<li><a><span>Page 1</span></a></li>
41-
<li><a><span>Page 2</span></a></li>
42-
<li><a><span>Page 3</span></a></li>
43-
</ul>
44-
</li>
45-
<li>
46-
<span><strong>Folder 2</strong></span>
47-
<ul>
48-
<li><a><span>Page 1</span></a></li>
49-
<li><a><span>Page 2</span></a></li>
50-
<li><a><span>Page 3</span></a></li>
51-
</ul>
52-
</li>
37+
REPLACE_PAGES
5338
</ul>
5439
</nav>
5540
<nav class="right">
5641
<div>
5742
<span>On this page</span>
5843
<ul>
59-
<li>
60-
<a>
61-
<span>Page 1</span>
62-
</a>
63-
</li>
64-
<li class="secondary">
65-
<a>
66-
<span>Page 2</span>
67-
</a>
68-
</li>
69-
<li>
70-
<a>
71-
<span>Page 3</span>
72-
</a>
73-
</li>
44+
REPLACE_CONTENTS
7445
</ul>
7546
<div class="slider"></div>
7647
</div>
@@ -102,7 +73,7 @@
10273
</button>
10374
<button id="a" alt="Switch to Dynamic Theme">A</button>
10475
</div>
105-
<span>© 2025 JustDeveloper</span>
76+
<span>REPLACE_FOOTER</span>
10677
</footer>
10778
</main>
10879
</body>

src/redirect/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ const generatePage = (url, params, path_) => {
105105
if (robots) {
106106
output += `\n<meta name="robots" content="${robots}" />`
107107
}
108+
return output;
108109
}
109110

110111
const link = `<a href="${URL}" target="_self">`;

0 commit comments

Comments
 (0)