252 lines
8.5 KiB
JavaScript
252 lines
8.5 KiB
JavaScript
import FS from 'fs-extra';
|
||
import path from 'path';
|
||
import stylus from 'stylus';
|
||
import * as ejs from 'ejs';
|
||
import UglifyJS from 'uglify-js';
|
||
import { create } from 'markdown-to-html-cli';
|
||
import _ from 'colors-cli/toxic.js';
|
||
|
||
const deployDir = path.resolve(process.cwd(), '.deploy');
|
||
const faviconPath = path.resolve(process.cwd(), 'template', 'img', 'favicon.ico');
|
||
const rootIndexJSPath = path.resolve(process.cwd(), 'template', 'js', 'index.js');
|
||
const dataJsonPath = path.resolve(process.cwd(), 'dist', 'data.json');
|
||
const dataJsonMinPath = path.resolve(process.cwd(), 'dist', 'data.min.json');
|
||
const cssPath = path.resolve(deployDir, 'css', 'index.css');
|
||
const contributorsPath = path.resolve(process.cwd(), 'CONTRIBUTORS.svg');
|
||
|
||
;(async () => {
|
||
try {
|
||
await FS.ensureDir(deployDir);
|
||
await FS.emptyDir(deployDir);
|
||
await FS.ensureDir(path.resolve(deployDir, 'img'));
|
||
await FS.ensureDir(path.resolve(deployDir, 'js'));
|
||
await FS.ensureDir(path.resolve(deployDir, 'css'));
|
||
await FS.ensureDir(path.resolve(deployDir, 'c'));
|
||
await FS.copySync(faviconPath, path.resolve(deployDir, 'img', 'favicon.ico'));
|
||
|
||
await FS.copyFile(path.resolve(process.cwd(), 'template', 'js', 'copy-to-clipboard.js'), path.resolve(deployDir, 'js', 'copy-to-clipboard.js'));
|
||
await FS.copyFile(path.resolve(process.cwd(), 'node_modules/@wcj/dark-mode/main.js'), path.resolve(deployDir, 'js', 'dark-mode.min.js'));
|
||
await FS.copyFile(path.resolve(process.cwd(), 'node_modules/@uiw/github-corners/lib/index.js'), path.resolve(deployDir, 'js', 'github-corners.js'));
|
||
|
||
const jsData = await FS.readFileSync(rootIndexJSPath);
|
||
await FS.outputFile(path.resolve(deployDir, 'js', 'index.js'), UglifyJS.minify(jsData.toString()).code)
|
||
const files = await readMarkdownPaths(path.resolve(process.cwd(), 'command'));
|
||
const jsonData = await createDataJSON(files);
|
||
await FS.outputFile(dataJsonPath, JSON.stringify(jsonData.json, null, 2));
|
||
await FS.outputFile(dataJsonMinPath, JSON.stringify(jsonData.json));
|
||
await FS.outputFile(path.resolve(deployDir, 'js', 'dt.js'), `var linux_commands=${JSON.stringify(jsonData.data)}`);
|
||
|
||
const cssStr = await createStylToCss(
|
||
path.resolve(process.cwd(), 'template', 'styl', 'index.styl'),
|
||
path.resolve(deployDir, 'css', 'index.css'),
|
||
);
|
||
|
||
await FS.outputFileSync(cssPath, cssStr)
|
||
console.log(` ${'→'.green} ${jsonData.data.length}`)
|
||
|
||
await createTmpToHTML(
|
||
path.resolve(process.cwd(), 'template', 'index.ejs'),
|
||
path.resolve(deployDir, 'index.html'),
|
||
{
|
||
p: '/index.html',
|
||
n: 'Linux命令搜索引擎',
|
||
d: '最专业的Linux命令大全,内容包含Linux命令手册、详解、学习,值得收藏的Linux命令速查手册。',
|
||
command_length: jsonData.data.length
|
||
}
|
||
);
|
||
|
||
await createTmpToHTML(
|
||
path.resolve(process.cwd(), 'template', 'list.ejs'),
|
||
path.resolve(deployDir, 'list.html'),
|
||
{
|
||
p: '/list.html',
|
||
n: '搜索',
|
||
d: '最专业的Linux命令大全,命令搜索引擎,内容包含Linux命令手册、详解、学习,值得收藏的Linux命令速查手册。',
|
||
command_length: jsonData.data.length
|
||
}
|
||
);
|
||
|
||
await createTmpToHTML(
|
||
path.resolve(process.cwd(), 'template', 'hot.ejs'),
|
||
path.resolve(deployDir, 'hot.html'),
|
||
{
|
||
p: '/hot.html',
|
||
n: '搜索',
|
||
d: '最专业的Linux命令大全,命令搜索引擎,内容包含Linux命令手册、详解、学习,值得收藏的Linux命令速查手册。',
|
||
arr: jsonData.data,
|
||
command_length: jsonData.data.length
|
||
}
|
||
);
|
||
|
||
let svgStr = '';
|
||
if (FS.existsSync(contributorsPath)) {
|
||
svgStr = (await FS.readFile(contributorsPath)).toString();
|
||
}
|
||
|
||
await createTmpToHTML(
|
||
path.resolve(process.cwd(), 'template', 'contributors.ejs'),
|
||
path.resolve(deployDir, 'contributors.html'),
|
||
{
|
||
p: '/contributors.html',
|
||
n: '搜索',
|
||
d: '最专业的Linux命令大全,命令搜索引擎,内容包含Linux命令手册、详解、学习,值得收藏的Linux命令速查手册。',
|
||
arr: jsonData.data,
|
||
command_length: jsonData.data.length,
|
||
contributors: svgStr,
|
||
}
|
||
);
|
||
|
||
await Promise.all(jsonData.data.map(async (item, idx) => {
|
||
item.command_length = jsonData.data.length;
|
||
await createTmpToHTML(
|
||
path.resolve(process.cwd(), 'template', 'details.ejs'),
|
||
path.resolve(deployDir, 'c', `${item.n}.html`),
|
||
item,
|
||
path.resolve(process.cwd(), 'command'),
|
||
);
|
||
}));
|
||
|
||
} catch (err) {
|
||
if (err && err.message) {
|
||
console.log(`\n ERROR :> ${err.message.red_bt}\n`)
|
||
}
|
||
process.exit(1);
|
||
}
|
||
})();
|
||
|
||
/**
|
||
* 返回 MD 所有路径的 Array
|
||
* @param {String} filepath
|
||
*/
|
||
function readMarkdownPaths(filepath) {
|
||
return new Promise((resolve, reject) => {
|
||
try {
|
||
let pathAll = [];
|
||
const files = FS.readdirSync(filepath);
|
||
for (let i = 0; i < files.length; i++) {
|
||
if (/\.md$/.test(files[i])) {
|
||
pathAll.push(path.join(filepath, files[i]));
|
||
}
|
||
}
|
||
resolve(pathAll);
|
||
} catch (err) {
|
||
reject(err);
|
||
}
|
||
});
|
||
}
|
||
|
||
/**
|
||
* Ensures that the directory exists.
|
||
* @param {String} pathArr
|
||
*/
|
||
function createDataJSON(pathArr) {
|
||
return new Promise((resolve, reject) => {
|
||
try {
|
||
const commandData = {};
|
||
const indexes = [];
|
||
pathArr.forEach((mdPath, i) => {
|
||
const json = {}
|
||
const con = FS.readFileSync(mdPath);
|
||
const str = con.toString();
|
||
let title = str.match(/[^===]+(?=[===])/g);
|
||
title = title[0] ? title[0].replace(/\n/g, '') : title[0];
|
||
title = title.replace(/\r/, '')
|
||
// 命令名称
|
||
json["n"] = title;
|
||
// 命令路径
|
||
json["p"] = `/${path.basename(mdPath, '.md').replace(/\\/g, '/')}`;
|
||
// 命令描述
|
||
let des = str.match(/\n==={1,}([\s\S]*?)##/i);
|
||
if (!des) {
|
||
throw `格式错误: ${mdPath}`;
|
||
}
|
||
des = des[1] ? des[1].replace(/\n/g, '') : des[1];
|
||
des = des.replace(/\r/g, '')
|
||
json["d"] = des;
|
||
indexes.push(json);
|
||
commandData[title] = json;
|
||
})
|
||
resolve({
|
||
json: commandData,
|
||
data: indexes
|
||
});
|
||
} catch (err) {
|
||
reject(err);
|
||
}
|
||
});
|
||
}
|
||
|
||
/**
|
||
* @param {String} fromPath ejs path
|
||
* @param {String} toPath html path
|
||
*/
|
||
function createTmpToHTML(fromPath, toPath, desJson, mdPath) {
|
||
return new Promise(async (resolve, reject) => {
|
||
try {
|
||
const current_path = toPath.replace(new RegExp(`${deployDir}`), '');
|
||
const tmpStr = await FS.readFile(fromPath);
|
||
let mdPathName = '';
|
||
let mdhtml = '';
|
||
let relative_path = '';
|
||
if (mdPath) {
|
||
// CSS/JS 引用相对地址
|
||
relative_path = '../';
|
||
mdPathName = `/command/${desJson.n}.md`;
|
||
const READMESTR = await FS.readFile(path.resolve(mdPath, `${desJson.n}.md`));
|
||
mdhtml = await markdownToHTML(READMESTR.toString());
|
||
}
|
||
// 生成 HTML
|
||
let html = ejs.render(tmpStr.toString(), {
|
||
filename: fromPath,
|
||
relative_path, // 当前文件相对于根目录的相对路径
|
||
md_path: mdPathName || '', // markdown 路径
|
||
mdhtml: mdhtml || '',
|
||
current_path, // 当前 html 路径
|
||
describe: desJson ? desJson : {}, // 当前 md 的描述
|
||
}, {
|
||
filename: fromPath
|
||
});
|
||
|
||
await FS.outputFile(toPath, html);
|
||
console.log(` ${'♻️ →'.green} ${path.relative(process.cwd(), toPath)}`);
|
||
resolve();
|
||
} catch (err) {
|
||
reject(err);
|
||
}
|
||
});
|
||
}
|
||
|
||
function markdownToHTML(str) {
|
||
return create({
|
||
rewrite: (node) => {
|
||
if (node.type === 'element' && node.properties?.href && /.md/.test(node.properties.href) && !/^(https?:\/\/)/.test(node.properties.href)) {
|
||
let href = node.properties.href;
|
||
node.properties.href = href.replace(/([^\.\/\\]+)\.(md|markdown)/gi, '$1.html');
|
||
}
|
||
},
|
||
markdown: str, document: undefined, 'dark-mode': false
|
||
});
|
||
}
|
||
|
||
/**
|
||
* [createStylToCss 生成CSS]
|
||
* @param {[type]} stylPath stylus path
|
||
* @param {[type]} cssPath css path
|
||
*/
|
||
function createStylToCss(stylPath) {
|
||
return new Promise((resolve, reject) => {
|
||
try {
|
||
const stylStr = FS.readFileSync(stylPath, 'utf8');
|
||
stylus(stylStr.toString())
|
||
.set('filename', stylPath)
|
||
.set('compress', true)
|
||
.render((err, css) => {
|
||
if (err) throw err;
|
||
resolve(`${css}`);
|
||
});
|
||
} catch (err) {
|
||
reject(err);
|
||
}
|
||
});
|
||
}
|