Skip to content

Commit 8df9b89

Browse files
committed
Refactor the build script.
1 parent 8ba8a48 commit 8df9b89

File tree

7 files changed

+698
-2166
lines changed

7 files changed

+698
-2166
lines changed

build/build.js

Lines changed: 306 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,306 @@
1+
const path = require('path');
2+
const ejs = require('ejs');
3+
const FS = require('fs-extra');
4+
const marked = require('marked');
5+
const stylus = require('stylus');
6+
const Prism = require('prismjs');
7+
const loadLanguages = require('prismjs/components/');
8+
const UglifyJS = require("uglify-js");
9+
const colors = require('colors-cli/toxic');
10+
11+
const renderer = new marked.Renderer();
12+
renderer.heading = (text, level) => {
13+
if (/[\u4E00-\u9FA5]/i.test(text)) {
14+
return '<h' + level + ' id="' + text.toLowerCase() + '">' + text + '</h' + level + '>';
15+
} else {
16+
var escapedText = text.toLowerCase().replace(/[^\w]+/g, '-');
17+
return '<h' + level + ' id="' + escapedText + '">' + text + '</h' + level + '>';
18+
}
19+
}
20+
21+
marked.setOptions({
22+
renderer: renderer,
23+
gfm: true,
24+
tables: true,
25+
breaks: false,
26+
pedantic: false,
27+
sanitize: false,
28+
smartLists: true,
29+
smartypants: false,
30+
highlight: (code, lang, callback) => {
31+
if (/(tex)$/.test(lang)) lang = 'latex';
32+
if (/(h)$/.test(lang)) lang = 'c';
33+
if (/(js)$/.test(lang)) lang = 'javascript';
34+
if (/(tsx)$/.test(lang)) lang = 'jsx';
35+
if (/(bat)$/.test(lang)) lang = 'batch';
36+
if (/(py)$/.test(lang)) lang = 'python';
37+
if (/(rb)$/.test(lang)) lang = 'ruby';
38+
if (/(gitconfig|editorconfig|gitmodules)$/.test(lang)) lang = 'ini';
39+
if (/(yml)$/.test(lang)) lang = 'yaml';
40+
if (/(styl)$/.test(lang)) lang = 'stylus';
41+
if (/(stylelintrc|postcssrc)$/.test(lang)) lang = 'json';
42+
if (/(sh|shell|bash|bats|cgi|command|fcgi|ksh|sh.in|tmux|tool|zsh|bash_history|bash_logout|bash_profile|bashrc|cshrc|login|profile|zlogin|zlogout|zprofile|zshenv|zshrc)$/.test(lang)) lang = 'bash';
43+
if (/(ps1|psm1)$/.test(lang)) lang = 'powershell';
44+
if (/^(html|htm|xml|ejs)/.test(lang)) lang = 'html';
45+
lang = lang ? lang : 'bash';
46+
loadLanguages([lang]);
47+
let html = code;
48+
if (Prism.languages[lang]) {
49+
html = Prism.highlight(code, Prism.languages[lang], lang);
50+
}
51+
return callback('', html);
52+
}
53+
});
54+
55+
const deployDir = path.resolve(process.cwd(), '.deploy');
56+
const faviconPath = path.resolve(process.cwd(), 'template', 'img', 'favicon.ico');
57+
const rootIndexJSPath = path.resolve(process.cwd(), 'template', 'js', 'index.js');
58+
const dataJsonPath = path.resolve(process.cwd(), 'dist', 'data.json');
59+
const cssPath = path.resolve(deployDir, 'css', 'index.css');
60+
61+
let markdownIndexData = [];
62+
63+
mkdirs(deployDir)
64+
.then(dir => emptyDir(dir))
65+
.then(dir => {
66+
ensureDir(path.resolve(dir, 'img'));
67+
ensureDir(path.resolve(dir, 'js'));
68+
ensureDir(path.resolve(dir, 'css'));
69+
ensureDir(path.resolve(dir, 'c'));
70+
})
71+
.then(() => FS.copySync(faviconPath, path.resolve(deployDir, 'img', 'favicon.ico')))
72+
.then(() => FS.readFileSync(rootIndexJSPath))
73+
.then((data) => {
74+
FS.outputFileSync(path.resolve(deployDir, 'js', 'index.js'), UglifyJS.minify(data.toString()).code)
75+
})
76+
.then(dir => readMarkdownPaths(path.resolve(process.cwd(), 'command')))
77+
.then(dirs => createDataJSON(dirs))
78+
.then(data => {
79+
FS.outputFileSync(dataJsonPath, JSON.stringify(data.json));
80+
FS.outputFileSync(path.resolve(deployDir, 'js', 'dt.js'), `var linux_commands=${JSON.stringify(data.data)}`);
81+
markdownIndexData = data.data;
82+
})
83+
.then(() => createTmpToHTML(
84+
path.resolve(process.cwd(), 'template', 'index.ejs'),
85+
path.resolve(deployDir, 'index.html'),
86+
{
87+
p: '/index.html',
88+
n: 'Linux命令搜索引擎',
89+
d: '最专业的Linux命令大全,内容包含Linux命令手册、详解、学习,值得收藏的Linux命令速查手册。',
90+
command_length: markdownIndexData.length
91+
}
92+
))
93+
.then(() => createTmpToHTML(
94+
path.resolve(process.cwd(), 'template', 'list.ejs'),
95+
path.resolve(deployDir, 'list.html'),
96+
{
97+
p: '/list.html',
98+
n: '搜索',
99+
d: '最专业的Linux命令大全,命令搜索引擎,内容包含Linux命令手册、详解、学习,值得收藏的Linux命令速查手册。',
100+
command_length: markdownIndexData.length
101+
}
102+
))
103+
.then(() => createTmpToHTML(
104+
path.resolve(process.cwd(), 'template', 'hot.ejs'),
105+
path.resolve(deployDir, 'hot.html'),
106+
{
107+
p: '/hot.html',
108+
n: '搜索',
109+
d: '最专业的Linux命令大全,命令搜索引擎,内容包含Linux命令手册、详解、学习,值得收藏的Linux命令速查手册。',
110+
arr: markdownIndexData,
111+
command_length: markdownIndexData.length
112+
}
113+
))
114+
.then(() => {
115+
markdownIndexData.forEach(async (item, idx) => {
116+
item.command_length = markdownIndexData.length;
117+
await createTmpToHTML(
118+
path.resolve(process.cwd(), 'template', 'details.ejs'),
119+
path.resolve(deployDir, 'c', `${item.n}.html`),
120+
item,
121+
path.resolve(process.cwd(), 'command'),
122+
);
123+
})
124+
})
125+
.then(() => {
126+
return createStylToCss(
127+
path.resolve(process.cwd(), 'template', 'styl', 'index.styl'),
128+
path.resolve(deployDir, 'css', 'index.css'),
129+
);
130+
})
131+
.then((css) => FS.outputFileSync(cssPath, css))
132+
.then(() => console.log(` ${'→'.green} ${markdownIndexData.length}`))
133+
.catch((err) => {
134+
if (err && err.message) {
135+
console.log(`\n ERROR :> ${err.message.red_bt}\n`)
136+
}
137+
});
138+
139+
/**
140+
* Create a directory
141+
* @param {String} dir
142+
*/
143+
function mkdirs(dir) {
144+
return new Promise((resolve, reject) => {
145+
FS.ensureDir(dir, err => {
146+
err ? reject(err) : resolve(dir);
147+
})
148+
});
149+
}
150+
151+
/**
152+
* Empty a directory
153+
* @param {String} dir
154+
*/
155+
function emptyDir(dir) {
156+
return new Promise((resolve, reject) => {
157+
FS.emptyDir(dir, err => {
158+
err ? reject(err) : resolve(dir);
159+
})
160+
});
161+
}
162+
163+
/**
164+
* Ensures that the directory exists.
165+
* @param {String} dir
166+
*/
167+
function ensureDir(dir) {
168+
return new Promise((resolve, reject) => {
169+
try {
170+
FS.ensureDirSync(dir);
171+
resolve(dir);
172+
} catch (err) {
173+
reject(err);
174+
}
175+
});
176+
}
177+
178+
/**
179+
* [createStylToCss 生成CSS]
180+
* @param {[type]} stylPath stylus path
181+
* @param {[type]} cssPath css path
182+
*/
183+
function createStylToCss(stylPath) {
184+
return new Promise((resolve, reject) => {
185+
try {
186+
const stylStr = FS.readFileSync(stylPath, 'utf8');
187+
stylus(stylStr.toString())
188+
.set('filename', stylPath)
189+
.set('compress', true)
190+
.render((err, css) => {
191+
if (err) throw err;
192+
resolve(css);
193+
});
194+
} catch (err) {
195+
reject(err);
196+
}
197+
});
198+
}
199+
200+
/**
201+
*
202+
* @param {String} fromPath ejs path
203+
* @param {String} toPath html path
204+
*/
205+
function createTmpToHTML(fromPath, toPath, desJson, mdPath) {
206+
return new Promise((resolve, reject) => {
207+
try {
208+
let relative_path = '';
209+
const current_path = toPath.replace(new RegExp(`${deployDir}`), '');
210+
const tmpStr = FS.readFileSync(fromPath);
211+
let mdPathName = '';
212+
if (mdPath) {
213+
// CSS/JS 引用相对地址
214+
relative_path = '../';
215+
mdPathName = `/command/${desJson.n}.md`;
216+
}
217+
// 生成 HTML
218+
let html = ejs.render(tmpStr.toString(), {
219+
filename: fromPath,
220+
relative_path, // 当前文件相对于根目录的相对路径
221+
md_path: mdPathName || '', // markdown 路径
222+
current_path, // 当前 html 路径
223+
describe: desJson ? desJson : {}, // 当前 md 的描述
224+
}, { filename: fromPath });
225+
226+
if (mdPath) {
227+
const READMESTR = FS.readFileSync(path.resolve(mdPath, `${desJson.n}.md`));
228+
marked(READMESTR.toString(), (err, mdhtml) => {
229+
if (err) return reject(err);
230+
html = html.replace(/{{content}}/, mdhtml);
231+
FS.outputFileSync(toPath, html);
232+
console.log(` ${'→'.green} ${toPath.replace(process.cwd(), '')}`);
233+
resolve(html);
234+
});
235+
} else {
236+
FS.outputFileSync(toPath, html);
237+
console.log(` ${'→'.green} ${toPath.replace(process.cwd(), '')}`);
238+
resolve(html);
239+
}
240+
} catch (err) {
241+
reject(err);
242+
}
243+
});
244+
}
245+
246+
/**
247+
* Ensures that the directory exists.
248+
* @param {String} pathArr
249+
*/
250+
function createDataJSON(pathArr) {
251+
return new Promise((resolve, reject) => {
252+
try {
253+
const commandData = {};
254+
const indexes = [];
255+
pathArr.forEach((mdPath, i) => {
256+
const json = {}
257+
const con = FS.readFileSync(mdPath);
258+
const str = con.toString();
259+
let title = str.match(/[^===]+(?=[===])/g);
260+
title = title[0] ? title[0].replace(/\n/g, '') : title[0];
261+
title = title.replace(/\r/, '')
262+
// 命令名称
263+
json["n"] = title;
264+
// 命令路径
265+
json["p"] = `/${path.basename(mdPath, '.md').replace(/\\/g, '/')}`;
266+
// 命令描述
267+
let des = str.match(/\n==={1,}([\s\S]*?)##/i);
268+
if (!des) {
269+
throw `格式错误: ${mdPath}`;
270+
}
271+
des = des[1] ? des[1].replace(/\n/g, '') : des[1];
272+
des = des.replace(/\r/g, '')
273+
json["d"] = des;
274+
indexes.push(json);
275+
commandData[title] = json;
276+
})
277+
resolve({
278+
json: commandData,
279+
data: indexes
280+
});
281+
} catch (err) {
282+
reject(err);
283+
}
284+
});
285+
}
286+
287+
/**
288+
* 返回 MD 所有路径的 Array
289+
* @param {String} filepath
290+
*/
291+
function readMarkdownPaths(filepath) {
292+
return new Promise((resolve, reject) => {
293+
try {
294+
let pathAll = [];
295+
const files = FS.readdirSync(filepath);
296+
for (let i = 0; i < files.length; i++) {
297+
if (/\.md$/.test(files[i])) {
298+
pathAll.push(path.join(filepath, files[i]));
299+
}
300+
}
301+
resolve(pathAll);
302+
} catch (err) {
303+
reject(err);
304+
}
305+
});
306+
}

0 commit comments

Comments
 (0)