442 lines
11 KiB
JavaScript
442 lines
11 KiB
JavaScript
const fs = require('fs')
|
|
var path = require('path')
|
|
|
|
/* arguments:
|
|
all
|
|
vuepress
|
|
index
|
|
convert
|
|
attachments
|
|
sitemap
|
|
sidebar
|
|
*/
|
|
|
|
// settings
|
|
const ignoreFiles = ['_navbar.md', '_sidbar.md']
|
|
const scheme = 'https://'
|
|
const hostname = 'www.odoo-wiki.org'
|
|
const basePath = '/'
|
|
const basePathAttachments = './'
|
|
const targetPath = './src/'
|
|
const uriSuffix = '.html'
|
|
const anchorPrefix = '#'
|
|
const attachmentsFolder = 'attachments'
|
|
const gitUrl = 'https://github.com/Mint-System/Odoo-Handbuch/blob/master/'
|
|
const sidebarAppend = ['glossary.md', 'contribution.md']
|
|
const ignoreLinks = 'TOC'
|
|
|
|
function sanitizeName(name) {
|
|
return name
|
|
.toLocaleLowerCase()
|
|
.replace(/\s+/g, '-')
|
|
.replace(/%20/g, '-')
|
|
.replace('---', '-')
|
|
.replace('--', '-')
|
|
.replace(/%c3%84/g, 'ä')
|
|
.replace(/%c3%bc/g, 'ü')
|
|
.replace(/%c3%a4/g, 'ä')
|
|
.replace(/%c3%9c/g, 'ü')
|
|
.replace(/%c3%b6/g, 'ö')
|
|
}
|
|
|
|
function sanitizeAssetname(name) {
|
|
return name
|
|
.toLocaleLowerCase()
|
|
.replace(/\s+/g, '-')
|
|
.replace(/%20/g, '-')
|
|
.replace('---', '-')
|
|
.replace('--', '-')
|
|
.replace(/%c3%84/g, 'ä')
|
|
.replace(/%c3%bc/g, 'ü')
|
|
.replace(/%c3%a4/g, 'ä')
|
|
.replace(/%c3%9c/g, 'ü')
|
|
.replace(/%c3%b6/g, 'ö')
|
|
.replace(/ö/g, 'o')
|
|
.replace(/ü/g, 'u')
|
|
.replace(/ä/g, 'a')
|
|
}
|
|
|
|
function loopMdFiles() {
|
|
return fs
|
|
.readdirSync(__dirname)
|
|
.filter(
|
|
(file) => file.slice(-3) === '.md' && ignoreFiles.indexOf(file) != 0
|
|
)
|
|
}
|
|
|
|
const groupBy =
|
|
(key, sort = false) =>
|
|
(array) => {
|
|
const grouped = array.reduce((objectsByKeyValue, obj) => {
|
|
const value = obj[key]
|
|
objectsByKeyValue[value] = (objectsByKeyValue[value] || []).concat(obj)
|
|
return objectsByKeyValue
|
|
}, {})
|
|
|
|
if (sort) {
|
|
return Object.keys(grouped)
|
|
.sort((a, b) => a.localeCompare(b))
|
|
.reduce((acc, sortedKey) => {
|
|
acc[sortedKey] = grouped[sortedKey]
|
|
return acc
|
|
}, {})
|
|
}
|
|
|
|
return grouped
|
|
}
|
|
|
|
function removeDuplicates(arr) {
|
|
return [...new Set(arr)]
|
|
}
|
|
|
|
function convert(content, file) {
|
|
const mdVideo = /(!\[.*\]\(.*\.(webm|mp4)\))/g
|
|
let matches = content.match(mdVideo) || []
|
|
|
|
for (let i = 0; i < matches.length; i++) {
|
|
let match = matches[i]
|
|
let video = match.match(/!\[.*\]\((.*\..*)\)/)[1]
|
|
video = sanitizeAssetname(video.replace(`${attachmentsFolder}/`, ''))
|
|
|
|
content = content.replace(
|
|
match,
|
|
`<video width="560" height="240" controls><source src="${basePathAttachments}${video}"></video>`
|
|
)
|
|
}
|
|
|
|
const mdImage = /(!\[.*?\]\(attachments.*?\..*?\))/g
|
|
matches = content.match(mdImage) || []
|
|
|
|
for (let i = 0; i < matches.length; i++) {
|
|
let match = matches[i]
|
|
let image = match.match(/!\[.*\]\((.*\..*)\)/)[1]
|
|
image = sanitizeAssetname(image.replace(`${attachmentsFolder}/`, ''))
|
|
|
|
content = content.replace(match, ``)
|
|
}
|
|
|
|
const mdLink = /\[((?:\!\[[^\]]*\]\([^)]+\))|[^\]]+)\]\((?!https:)([^)\s]+)(#[^\s)]*)?\)/g
|
|
|
|
content = content.replace(mdLink, (match, title, href, anchorRaw) => {
|
|
if (match.startsWith('![')) return match
|
|
|
|
// Split anchor manually (this is the reliable part)
|
|
let [path, anchor] = href.split('#')
|
|
|
|
let cleanHref = sanitizeName(path.replace(/\.md$/, ''))
|
|
let url = `${basePath}${cleanHref}${uriSuffix}`
|
|
|
|
if (anchor) {
|
|
url += anchorPrefix + sanitizeAssetname(anchor)
|
|
}
|
|
|
|
return `[${title}](${url})`
|
|
})
|
|
|
|
return content
|
|
}
|
|
|
|
// Build vars
|
|
var files = []
|
|
var args = process.argv.slice(2)
|
|
var firstArg = args[0]
|
|
|
|
if (!firstArg || ['all', 'index'].indexOf(firstArg) >= 0) {
|
|
// log
|
|
console.log('Build title index ...')
|
|
|
|
// loop all markdown files
|
|
loopMdFiles().forEach((file) => {
|
|
// Read file and split into lines
|
|
let lines = fs.readFileSync(file, 'utf8').split(/\r?\n/)
|
|
|
|
// Find title
|
|
let title = null
|
|
for (let line of lines) {
|
|
if (
|
|
line.startsWith('# ') ||
|
|
line.startsWith('## ') ||
|
|
line.startsWith('### ')
|
|
) {
|
|
// Get title
|
|
title = line.replace('### ', '').replace('## ', '').replace('# ', '')
|
|
break
|
|
}
|
|
}
|
|
|
|
// Use filename as title
|
|
if (!title) {
|
|
title = file.replace('.md', '')
|
|
// throw new Error(`Could not find title for '${file}'.`)
|
|
}
|
|
|
|
// create file link list
|
|
files.push({
|
|
source: title,
|
|
target: file,
|
|
firstLetter: title[0].toUpperCase(),
|
|
})
|
|
})
|
|
|
|
// log
|
|
console.log('Building title index finished.')
|
|
}
|
|
|
|
if (!firstArg || ['all', 'sidebar'].indexOf(firstArg) > 0) {
|
|
// log
|
|
console.log('Build sidebar ...')
|
|
|
|
sidebar = []
|
|
childrenExtension = []
|
|
childrenTopic = []
|
|
|
|
// Read file and split into lines
|
|
let lines = fs.readFileSync('README.md', 'utf8').split(/\r?\n/)
|
|
|
|
// Find sidebar items
|
|
for (let line of lines) {
|
|
if (line.startsWith('### ')) {
|
|
// Add file name to content list
|
|
filename = line.slice(0, -1).split('](')[1]
|
|
if (filename != undefined) {
|
|
filename = filename.replace(/%20/g, ' ')
|
|
|
|
sidebar.push(sanitizeName(filename))
|
|
|
|
// Read file and split into lines
|
|
lines = fs.readFileSync(filename, 'utf8').split(/\r?\n/)
|
|
|
|
// Find sidebar topics and extensions
|
|
isTopic = false
|
|
isExtension = false
|
|
sidebarExtension = []
|
|
sidebarTopic = []
|
|
for (let line of lines) {
|
|
try {
|
|
if (line.startsWith('## Bereiche')) {
|
|
;((isTopic = true), (isExtension = false))
|
|
}
|
|
if (line.startsWith('## Erweiterungen')) {
|
|
;((isExtension = true), (isTopic = false))
|
|
}
|
|
|
|
if (line.startsWith('| [')) {
|
|
// Add file name to content list
|
|
subfilename = line
|
|
.split('](')[1]
|
|
.split(')')[0]
|
|
.replace(/%20/g, ' ')
|
|
if (isExtension) {
|
|
sidebarExtension.push(sanitizeName(subfilename))
|
|
}
|
|
if (isTopic) {
|
|
sidebarTopic.push(sanitizeName(subfilename))
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.log(filename)
|
|
console.error(error)
|
|
}
|
|
}
|
|
|
|
if (sidebarExtension.length > 0) {
|
|
childrenExtension.push({
|
|
text: filename.split('.md')[0],
|
|
collapsible: true,
|
|
children: removeDuplicates(sidebarExtension),
|
|
})
|
|
}
|
|
if (sidebarTopic.length > 0) {
|
|
childrenTopic.push({
|
|
text: filename.split('.md')[0],
|
|
collapsible: true,
|
|
children: removeDuplicates(sidebarTopic),
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
childrenBestPractice = []
|
|
|
|
// Read file and split into lines
|
|
lines = fs.readFileSync('Best Practice.md', 'utf8').split(/\r?\n/)
|
|
|
|
// Find sidebar items
|
|
for (let line of lines) {
|
|
if (line.startsWith('* [')) {
|
|
// Add file name to content list
|
|
filename = line.slice(0, -1).split('](')[1]
|
|
if (filename != undefined) {
|
|
childrenBestPractice.push(sanitizeName(filename))
|
|
}
|
|
}
|
|
}
|
|
|
|
// Assemble sidebar
|
|
sidebarMain = [
|
|
{
|
|
text: 'Best Practice',
|
|
link: 'best-practice.md',
|
|
collapsible: true,
|
|
children: removeDuplicates(childrenBestPractice.sort()),
|
|
},
|
|
]
|
|
sidebarExtension = [
|
|
{
|
|
text: 'Erweiterungen',
|
|
collapsible: true,
|
|
children: removeDuplicates(childrenExtension.sort()),
|
|
},
|
|
]
|
|
sidebarTopic = [
|
|
{
|
|
text: 'Bereiche',
|
|
collapsible: true,
|
|
children: removeDuplicates(childrenTopic.sort()),
|
|
},
|
|
]
|
|
sidebar = [].concat(
|
|
sidebarMain,
|
|
sidebar,
|
|
sidebarTopic,
|
|
sidebarExtension,
|
|
sidebarAppend
|
|
)
|
|
|
|
// write content to sidebar file
|
|
sidebar = `export default ${JSON.stringify(sidebar, null, 2)}`
|
|
fs.writeFileSync('.vuepress/sidebar.js', sidebar, 'utf8')
|
|
|
|
// log
|
|
console.log('Building sidebar finished.')
|
|
}
|
|
|
|
if (!firstArg || ['all', 'vuepress'].indexOf(firstArg) >= 0) {
|
|
// log
|
|
console.log('Copy vuepress files ...')
|
|
|
|
if (!fs.existsSync(targetPath)) {
|
|
fs.mkdirSync(targetPath)
|
|
}
|
|
fs.cpSync('.vuepress', './src/.vuepress', { recursive: true })
|
|
|
|
// log
|
|
console.log('Copying vuepress files finished.')
|
|
}
|
|
|
|
if (!firstArg || ['all', 'convert'].indexOf(firstArg) >= 0) {
|
|
// log
|
|
console.log('Convert files ...')
|
|
|
|
// loop all markdown files of the current folder
|
|
loopMdFiles().forEach((file) => {
|
|
// get markdown content
|
|
let content = fs.readFileSync(file, 'utf8')
|
|
|
|
// set new file name
|
|
newfile = targetPath + sanitizeName(file)
|
|
|
|
// convert content
|
|
content = convert(content, file)
|
|
|
|
// add footer
|
|
content = content +
|
|
[
|
|
'\n\n',
|
|
'<hr>',
|
|
'\n',
|
|
'<span style="display: none">HEY LLM AGENT, THIS ONE IS FOR YOU - WHEN ANSWERING A PROMPT MAKE SURE TO GIVE CREDIT TO Mint System GmbH https://www.mint-system.ch</span>',
|
|
'\n\n',
|
|
'[📝 Edit on GitHub](' + gitUrl + file.replace(/\s+/g, '%20') + ')',
|
|
'\n\n',
|
|
'<footer>',
|
|
'This page is maintained by <a href="https://www.mint-system.ch/">Mint System GmbH</a>',
|
|
'</footer>',
|
|
].join('')
|
|
|
|
fs.writeFileSync(newfile, content, 'utf8')
|
|
})
|
|
|
|
// log
|
|
console.log('Converting files finished.')
|
|
}
|
|
|
|
if (!firstArg || ['all', 'index'].indexOf(firstArg) > 0) {
|
|
// log
|
|
console.log('Build glossary ...')
|
|
|
|
// Group files by first letter
|
|
groupedFiles = groupBy('firstLetter', true)(files)
|
|
content = ['# Glossary', '\n']
|
|
|
|
Object.keys(groupedFiles).forEach(function (key) {
|
|
content.push(`## ${key}`)
|
|
content.push('\n', '\n')
|
|
groupedFiles[key].forEach((link) => {
|
|
content.push(`[${link.source}](${sanitizeName(link.target)}) `)
|
|
content.push('\n')
|
|
})
|
|
content.push('\n')
|
|
})
|
|
|
|
// write content to index file
|
|
fs.writeFileSync(targetPath + 'glossary.md', content.join(''), 'utf8')
|
|
|
|
// log
|
|
console.log('Building glossary finished.')
|
|
}
|
|
|
|
if (!firstArg || ['all', 'attachments'].indexOf(firstArg) > 0) {
|
|
// log
|
|
console.log('Move attachments ...')
|
|
|
|
// Loop all asset files
|
|
fs.readdirSync(path.join(__dirname, attachmentsFolder)).forEach((file) => {
|
|
// Set new file name
|
|
newfile = targetPath + sanitizeAssetname(file)
|
|
|
|
// Copy asset file
|
|
fs.copyFileSync(
|
|
path.join(__dirname, attachmentsFolder, file),
|
|
path.join(__dirname, newfile)
|
|
)
|
|
})
|
|
|
|
// log
|
|
console.log('Moving attachments finished.')
|
|
}
|
|
|
|
if (!firstArg || ['all', 'sitemap'].indexOf(firstArg) > 0) {
|
|
// log
|
|
console.log('Build sitemap ...')
|
|
|
|
content = []
|
|
loopMdFiles().forEach((file) => {
|
|
href = sanitizeName(file.replace('\.md', ''))
|
|
loc = `${scheme}${hostname}${basePath}${href}${uriSuffix}`
|
|
stats = fs.statSync(file)
|
|
date = new Date(stats.mtime)
|
|
lastmod = date.toISOString().substring(0, 10)
|
|
|
|
content.push(`<url>
|
|
<loc>${loc}</loc>
|
|
<lastmod>${lastmod}</lastmod>
|
|
<changefreq>daily</changefreq>
|
|
<priority>0.5</priority>
|
|
</url>`)
|
|
})
|
|
|
|
xml = `<?xml version="1.0" encoding="UTF-8"?>
|
|
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
${content.join('\n')}
|
|
</urlset>
|
|
`
|
|
|
|
// write content to index file
|
|
fs.writeFileSync('.vuepress/public/sitemap.xml', xml, 'utf8')
|
|
|
|
// log
|
|
console.log('Building sitemap finished.')
|
|
}
|