mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 18:36:30 +01:00 
			
		
		
		
	script to build the docs website
This commit is contained in:
		
							
								
								
									
										123
									
								
								src/build_docs_website.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								src/build_docs_website.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,123 @@ | ||||
| const fs = require("fs-extra"); | ||||
| const utils = require("./services/utils.js"); | ||||
| const html = require("html"); | ||||
|  | ||||
| const USER_GUIDE_DIR = './docs/user_guide'; | ||||
| const META_PATH = USER_GUIDE_DIR + '/!!!meta.json'; | ||||
| const WEB_TMP_DIR = './tmp/user_guide_web'; | ||||
| fs.copySync(USER_GUIDE_DIR, WEB_TMP_DIR); | ||||
|  | ||||
| const meta = JSON.parse(fs.readFileSync(META_PATH).toString()); | ||||
| const rootNoteMeta = meta.files[0]; | ||||
| const noteIdToMeta = {}; | ||||
| createNoteIdToMetaMapping(rootNoteMeta); | ||||
|  | ||||
| addNavigationAndStyle(rootNoteMeta, WEB_TMP_DIR); | ||||
|  | ||||
| fs.writeFileSync(WEB_TMP_DIR + '/style.css', getCss()); | ||||
|  | ||||
| function getCss() { | ||||
|     return '* { color: red }'; | ||||
| } | ||||
|  | ||||
| function addNavigationAndStyle(noteMeta, parentDirPath) { | ||||
|     const nav = createNavigation(rootNoteMeta, noteMeta); | ||||
|  | ||||
|     if (noteMeta.dataFileName) { | ||||
|         const filePath = parentDirPath + "/" + noteMeta.dataFileName; | ||||
|  | ||||
|         console.log(`Adding nav to ${filePath}`); | ||||
|  | ||||
|         const content = fs.readFileSync(filePath).toString(); | ||||
|         const depth = noteMeta.notePath.length - 1; | ||||
|         const updatedContent = content | ||||
|             .replaceAll("</head>", `<link rel="stylesheet" href="${"../".repeat(depth)}styles.css">`) | ||||
|             .replaceAll("</body>", nav + "</body>"); | ||||
|         const prettified = html.prettyPrint(updatedContent, {indent_size: 2}); | ||||
|         fs.writeFileSync(filePath, prettified); | ||||
|     } | ||||
|  | ||||
|     for (const childNoteMeta of noteMeta.children || []) { | ||||
|         addNavigationAndStyle(childNoteMeta, parentDirPath + '/' + noteMeta.dirFileName); | ||||
|     } | ||||
| } | ||||
|  | ||||
| function createNavigation(rootMeta, sourceMeta) { | ||||
|     function saveNavigationInner(meta) { | ||||
|         let html = '<li>'; | ||||
|  | ||||
|         const escapedTitle = utils.escapeHtml(`${meta.prefix ? `${meta.prefix} - ` : ''}${meta.title}`); | ||||
|  | ||||
|         if (meta.dataFileName) { | ||||
|             const targetUrl = getTargetUrl(meta.noteId, sourceMeta); | ||||
|  | ||||
|             html += `<a href="${targetUrl}">${escapedTitle}</a>`; | ||||
|         } | ||||
|         else { | ||||
|             html += escapedTitle; | ||||
|         } | ||||
|  | ||||
|         if (meta.children && meta.children.length > 0) { | ||||
|             html += '<ul>'; | ||||
|  | ||||
|             for (const child of meta.children) { | ||||
|                 html += saveNavigationInner(child); | ||||
|             } | ||||
|  | ||||
|             html += '</ul>' | ||||
|         } | ||||
|  | ||||
|         return `${html}</li>`; | ||||
|     } | ||||
|  | ||||
|     return `<nav class="note-tree-nav"><ul>${saveNavigationInner(rootMeta)}</ul></nav>`; | ||||
| } | ||||
|  | ||||
| function createNoteIdToMetaMapping(noteMeta) { | ||||
|     noteIdToMeta[noteMeta.noteId] = noteMeta; | ||||
|  | ||||
|     for (const childNoteMeta of noteMeta.children || []) { | ||||
|         createNoteIdToMetaMapping(childNoteMeta); | ||||
|     } | ||||
| } | ||||
|  | ||||
| function getTargetUrl(targetNoteId, sourceMeta) { | ||||
|     const targetMeta = noteIdToMeta[targetNoteId]; | ||||
|  | ||||
|     if (!targetMeta) { | ||||
|         throw new Error(`Could not find note meta for noteId '${targetNoteId}'`); | ||||
|     } | ||||
|  | ||||
|     const targetPath = targetMeta.notePath.slice(); | ||||
|     const sourcePath = sourceMeta.notePath.slice(); | ||||
|  | ||||
|     // > 1 for edge case that targetPath and sourcePath are exact same (link to itself) | ||||
|     while (targetPath.length > 1 && sourcePath.length > 1 && targetPath[0] === sourcePath[0]) { | ||||
|         targetPath.shift(); | ||||
|         sourcePath.shift(); | ||||
|     } | ||||
|  | ||||
|     let url = "../".repeat(sourcePath.length - 1); | ||||
|  | ||||
|     for (let i = 0; i < targetPath.length - 1; i++) { | ||||
|         const meta = noteIdToMeta[targetPath[i]]; | ||||
|  | ||||
|         if (!meta) { | ||||
|             throw new Error(`Cannot resolve note '${targetPath[i]}' from path '${targetPath.toString()}'`); | ||||
|         } | ||||
|  | ||||
|         url += `${encodeURIComponent(meta.dirFileName)}/`; | ||||
|     } | ||||
|  | ||||
|     const targetPathNoteId = targetPath[targetPath.length - 1]; | ||||
|     const meta = noteIdToMeta[targetPathNoteId]; | ||||
|  | ||||
|     if (!meta) { | ||||
|         throw new Error(`Cannot resolve note '${targetPathNoteId}' from path '${targetPath.toString()}'`); | ||||
|     } | ||||
|  | ||||
|     // link can target note which is only "folder-note" and as such will not have a file in an export | ||||
|     url += encodeURIComponent(meta.dataFileName || meta.dirFileName); | ||||
|  | ||||
|     return url; | ||||
| } | ||||
| @@ -254,9 +254,10 @@ async function exportToZip(taskContext, branch, format, res, setHeaders = true) | ||||
|     <link rel="stylesheet" href="${cssUrl}"> | ||||
|     <base target="_parent"> | ||||
| </head> | ||||
| <body class="ck-content"> | ||||
| <body> | ||||
|   <h1>${utils.escapeHtml(title)}</h1> | ||||
| ${content} | ||||
|    | ||||
|   <div class="ck-content">${content}</div> | ||||
| </body> | ||||
| </html>`; | ||||
|             } | ||||
|   | ||||
| @@ -1,5 +1,101 @@ | ||||
| const sanitizeHtml = require('sanitize-html'); | ||||
|  | ||||
| const fs = require("fs"); | ||||
| const path = require("path"); | ||||
| const html = require("html"); | ||||
|  | ||||
| const TMP_API_DOCS = './tmp/api_docs'; | ||||
| const TMP_FE_DOCS = TMP_API_DOCS + '/frontend_api'; | ||||
| const TMP_BE_DOCS = TMP_API_DOCS + '/backend_api'; | ||||
|  | ||||
| const sourceFiles = getFilesRecursively(TMP_API_DOCS); | ||||
|  | ||||
| for (const sourcePath of sourceFiles) { | ||||
|     const content = fs.readFileSync(sourcePath).toString(); | ||||
|     const transformedContent = transform(content); | ||||
|     const prettifiedContent = html.prettyPrint(transformedContent, {indent_size: 2}); | ||||
|     const filteredContent = prettifiedContent | ||||
|         .replace(/<br \/>Documentation generated by <a href="https:\/\/github.com\/jsdoc\/jsdoc">[^<]+<\/a>/gi, '') | ||||
|         .replace(/JSDoc: (Class|Module): [a-z]+/gi, ''); | ||||
|  | ||||
|     const destPath = sourcePath.replaceAll("tmp", "docs"); | ||||
|  | ||||
|     fs.mkdirSync(path.dirname(destPath), {recursive: true}); | ||||
|     fs.writeFileSync(destPath, filteredContent.trim()); | ||||
|  | ||||
|     console.log(destPath); | ||||
| } | ||||
|  | ||||
| const USER_GUIDE_DIR = './docs/user_guide'; | ||||
| const META_PATH = USER_GUIDE_DIR + '/!!!meta.json'; | ||||
|  | ||||
| const meta = JSON.parse(fs.readFileSync(META_PATH).toString()); | ||||
| const rootNoteMeta = meta.files[0]; | ||||
|  | ||||
| const {noteMeta: scriptApiDocsRoot, filePath: scriptApiDocsRootFilePath, notePath: scriptApiDocsRootNotePath} = | ||||
|     findNoteMeta(rootNoteMeta, 'Script API', []); | ||||
| const BE_FILES = ['AbstractBeccaEntity', 'BAttribute', 'BBranch', 'BEtapiToken', 'BNote', 'BNoteRevision', 'BOption', 'BRecentNote', 'module-sql']; | ||||
|  | ||||
| const FE_FILES = ['FNote', 'FAttribute', 'FBranch', 'FNoteComplement']; | ||||
|  | ||||
| scriptApiDocsRoot.dirFileName = scriptApiDocsRoot.dataFileName.substr(0, scriptApiDocsRoot.dataFileName.length - 5); | ||||
| scriptApiDocsRoot.children = getScriptApiMeta(); | ||||
|  | ||||
| fs.writeFileSync(META_PATH, JSON.stringify(meta, null, 2)); | ||||
| const scriptApiDocsRootDir =  USER_GUIDE_DIR + scriptApiDocsRootFilePath.substr(0, scriptApiDocsRootFilePath.length - 5); | ||||
|  | ||||
| fs.mkdirSync(scriptApiDocsRootDir, {recursive: true}); | ||||
| fs.mkdirSync(scriptApiDocsRootDir + '/BackendScriptApi', {recursive: true}); | ||||
| fs.mkdirSync(scriptApiDocsRootDir + '/FrontendScriptApi', {recursive: true}); | ||||
|  | ||||
| const BE_ROOT = scriptApiDocsRootDir + '/BackendScriptApi.html'; | ||||
| const FE_ROOT = scriptApiDocsRootDir + '/FrontendScriptApi.html'; | ||||
|  | ||||
| fs.copyFileSync(TMP_BE_DOCS + '/BackendScriptApi.html', BE_ROOT); | ||||
| fs.copyFileSync(TMP_FE_DOCS + '/FrontendScriptApi.html', FE_ROOT); | ||||
|  | ||||
| for (const file of BE_FILES) { | ||||
|     fs.copyFileSync(TMP_BE_DOCS + '/' + file + '.html', scriptApiDocsRootDir + '/BackendScriptApi/' + file + '.html'); | ||||
| } | ||||
| rewriteLinks(BE_ROOT, BE_FILES, 'BackendScriptApi'); | ||||
|  | ||||
| for (const file of FE_FILES) { | ||||
|     fs.copyFileSync(TMP_FE_DOCS + '/' + file + '.html', scriptApiDocsRootDir + '/FrontendScriptApi/' + file + '.html'); | ||||
| } | ||||
| rewriteLinks(FE_ROOT, FE_FILES, 'FrontendScriptApi'); | ||||
|  | ||||
| fs.rmSync(USER_GUIDE_DIR + '/index.html', {force: true}); | ||||
| fs.rmSync(USER_GUIDE_DIR + '/navigation.html', {force: true}); | ||||
| fs.rmSync(USER_GUIDE_DIR + '/style.css', {force: true}); | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| function getFilesRecursively(directory) { | ||||
|     const files = []; | ||||
|  | ||||
|     function getFilesRecursivelyInner(directory) { | ||||
|         const filesInDirectory = fs.readdirSync(directory); | ||||
|         for (const file of filesInDirectory) { | ||||
|             const absolute = path.join(directory, file); | ||||
|             if (fs.statSync(absolute).isDirectory()) { | ||||
|                 getFilesRecursivelyInner(absolute); | ||||
|             } else if (file.endsWith('.html')) { | ||||
|                 files.push(absolute); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     getFilesRecursivelyInner(directory); | ||||
|  | ||||
|     return files; | ||||
| } | ||||
|  | ||||
| function transform(content) { | ||||
|     const result = sanitizeHtml(content, { | ||||
|         allowedTags: [ | ||||
| @@ -41,48 +137,6 @@ function transform(content) { | ||||
|         ; | ||||
| } | ||||
|  | ||||
| const fs = require("fs"); | ||||
| const path = require("path"); | ||||
| const html = require("html"); | ||||
| let sourceFiles = []; | ||||
|  | ||||
| const getFilesRecursively = (directory) => { | ||||
|     const filesInDirectory = fs.readdirSync(directory); | ||||
|     for (const file of filesInDirectory) { | ||||
|         const absolute = path.join(directory, file); | ||||
|         if (fs.statSync(absolute).isDirectory()) { | ||||
|             getFilesRecursively(absolute); | ||||
|         } else if (file.endsWith('.html')) { | ||||
|             sourceFiles.push(absolute); | ||||
|         } | ||||
|     } | ||||
| }; | ||||
|  | ||||
| const TMP_API_DOCS = './tmp/api_docs'; | ||||
| const TMP_FE_DOCS = TMP_API_DOCS + '/frontend_api'; | ||||
| const TMP_BE_DOCS = TMP_API_DOCS + '/backend_api'; | ||||
|  | ||||
| getFilesRecursively(TMP_API_DOCS); | ||||
|  | ||||
| for (const sourcePath of sourceFiles) { | ||||
|     const content = fs.readFileSync(sourcePath).toString(); | ||||
|     const transformedContent = transform(content); | ||||
|     const prettifiedContent = html.prettyPrint(transformedContent, {indent_size: 2}); | ||||
|     const filteredContent = prettifiedContent | ||||
|         .replace(/<br \/>Documentation generated by <a href="https:\/\/github.com\/jsdoc\/jsdoc">[^<]+<\/a>/gi, '') | ||||
|         .replace(/JSDoc: (Class|Module): [a-z]+/gi, ''); | ||||
|  | ||||
|     const destPath = sourcePath.replaceAll("tmp", "docs"); | ||||
|  | ||||
|     fs.mkdirSync(path.dirname(destPath), {recursive: true}); | ||||
|     fs.writeFileSync(destPath, filteredContent.trim()); | ||||
|  | ||||
|     console.log(destPath); | ||||
| } | ||||
|  | ||||
| const META_PATH = './docs/user_guide/!!!meta.json'; | ||||
| const meta = JSON.parse(fs.readFileSync(META_PATH).toString()); | ||||
|  | ||||
| function findNoteMeta(noteMeta, name, notePath) { | ||||
|     if (noteMeta.title === name) { | ||||
|         return { | ||||
| @@ -107,28 +161,6 @@ function findNoteMeta(noteMeta, name, notePath) { | ||||
|     return null; | ||||
| } | ||||
|  | ||||
| const {noteMeta: scriptApiDocsRoot, filePath: scriptApiDocsRootFilePath, notePath: scriptApiDocsRootNotePath} = | ||||
|     findNoteMeta(meta.files[0], 'Script API', ['_scriptApi']); | ||||
|  | ||||
| const BE_FILES = ['AbstractBeccaEntity', 'BAttribute', 'BBranch', 'BEtapiToken', 'BNote', 'BNoteRevision', 'BOption', 'BRecentNote', 'module-sql']; | ||||
| const FE_FILES = ['FNote', 'FAttribute', 'FBranch', 'FNoteComplement']; | ||||
|  | ||||
| scriptApiDocsRoot.children = getScriptApiMeta(); | ||||
|  | ||||
| fs.writeFileSync(META_PATH, JSON.stringify(meta, null, 2)); | ||||
|  | ||||
| const scriptApiDocsRootDir =  './docs/user_guide' + scriptApiDocsRootFilePath.substr(0, scriptApiDocsRootFilePath.length - 5); | ||||
|  | ||||
| fs.mkdirSync(scriptApiDocsRootDir, {recursive: true}); | ||||
| fs.mkdirSync(scriptApiDocsRootDir + '/BackendScriptApi', {recursive: true}); | ||||
| fs.mkdirSync(scriptApiDocsRootDir + '/FrontendScriptApi', {recursive: true}); | ||||
|  | ||||
| const BE_ROOT = scriptApiDocsRootDir + '/BackendScriptApi.html'; | ||||
| const FE_ROOT = scriptApiDocsRootDir + '/FrontendScriptApi.html'; | ||||
|  | ||||
| fs.copyFileSync(TMP_BE_DOCS + '/BackendScriptApi.html', BE_ROOT); | ||||
| fs.copyFileSync(TMP_FE_DOCS + '/FrontendScriptApi.html', FE_ROOT); | ||||
|  | ||||
| function rewriteLinks(rootFilePath, files, dir) { | ||||
|     let content = fs.readFileSync(rootFilePath).toString(); | ||||
|  | ||||
| @@ -139,27 +171,27 @@ function rewriteLinks(rootFilePath, files, dir) { | ||||
|     fs.writeFileSync(rootFilePath, content); | ||||
| } | ||||
|  | ||||
| for (const file of BE_FILES) { | ||||
|     fs.copyFileSync(TMP_BE_DOCS + '/' + file + '.html', scriptApiDocsRootDir + '/BackendScriptApi/' + file + '.html'); | ||||
| } | ||||
| rewriteLinks(BE_ROOT, BE_FILES, 'BackendScriptApi'); | ||||
|  | ||||
| for (const file of FE_FILES) { | ||||
|     fs.copyFileSync(TMP_FE_DOCS + '/' + file + '.html', scriptApiDocsRootDir + '/FrontendScriptApi/' + file + '.html'); | ||||
| } | ||||
| rewriteLinks(FE_ROOT, FE_FILES, 'FrontendScriptApi'); | ||||
|  | ||||
| function createChildren(files, notePath) { | ||||
|     let positionCounter = 0; | ||||
|  | ||||
|     const camelCase = name => name.charAt(0).toLowerCase() + name.substr(1); | ||||
|     const camelCase = name => { | ||||
|         if (name === 'module-sql') { | ||||
|             return 'moduleSql'; | ||||
|         } else if (/[^a-z]+/i.test(name)) { | ||||
|             throw new Error(`Bad name '${name}'`); | ||||
|         } | ||||
|  | ||||
|         return name.charAt(0).toLowerCase() + name.substr(1); | ||||
|     }; | ||||
|  | ||||
|     return files.map(file => { | ||||
|         positionCounter += 10; | ||||
|  | ||||
|         const noteId = "_" + camelCase(file); | ||||
|  | ||||
|         return { | ||||
|             "isClone": false, | ||||
|             "noteId": "_file", | ||||
|             "noteId": noteId, | ||||
|             "notePath": [ | ||||
|                 ...notePath, | ||||
|                 '_' + camelCase(file) | ||||
| @@ -195,6 +227,7 @@ function getScriptApiMeta() { | ||||
|             "attributes": [], | ||||
|             "format": "html", | ||||
|             "dataFileName": "FrontendScriptApi.html", | ||||
|             "dirFileName": "FrontendScriptApi", | ||||
|             "children": createChildren(FE_FILES, [ | ||||
|                 ...scriptApiDocsRootNotePath, | ||||
|                 "_frontendApi" | ||||
| @@ -216,6 +249,7 @@ function getScriptApiMeta() { | ||||
|             "attributes": [], | ||||
|             "format": "html", | ||||
|             "dataFileName": "BackendScriptApi.html", | ||||
|             "dirFileName": "BackendScriptApi", | ||||
|             "children": createChildren(BE_FILES, [ | ||||
|                 ...scriptApiDocsRootNotePath, | ||||
|                 "_backendApi" | ||||
|   | ||||
		Reference in New Issue
	
	Block a user