需求来源
后端团队写了一套服务端的API文档,但由于技术选型上的原因,产生了如下结构:
- demo/
+ img/ # 文档图片目录
index.html # 总文档
app.html # 子文档1
bean.html # 子文档2
file.md # 子文档3
queryData.html # 子文档4
share.html # 子文档5
... # 子文档6,7,8,9,10,11.....
总文档和子文档中都有对应的目录和内容,需要做一个自动化的操作,具体如下:
- 将总文档的目录树和相应子文档的内容拼接,从新生成子文档的页面
- 处理目录树链接,使得文档页面可以互相跳转
最终生成如下目录:
- dist/
+ img/ # 文档图片目录
app.html # 子文档1
bean.html # 子文档2
file.md # 子文档3
queryData.html # 子文档4
share.html # 子文档5
... # 子文档6,7,8,9,10,11.....
最终代码
app.js
const path = require('path');
const fs = require('fs-extra');
const cheerio = require('cheerio');
const resourcePath = __dirname + '/demo/';
const buildPath = __dirname + '/dist/';
const mapPath = path.resolve('./reference.json');
const doc = {};
if(!fs.existsSync(buildPath)){
fs.mkdirSync(buildPath);
fs.mkdirSync(buildPath + 'images');
}
doc.map = JSON.parse(fs.readFileSync(mapPath).toString());
$ = cheerio.load(fs.readFileSync(resourcePath + 'index.html').toString());
$('.sectlevel1').children().each((i,el)=>{
let _val = $(el).children('a').text();
for (var key in doc.map) {
if(doc.map[key] == _val) {
$(el).children('a').attr('href',`${key}.html`);
$(el).children('ul').find('a').each((j,ele)=>{
let _hash = $(ele).attr('href');
$(ele).attr('href',`${key}.html${_hash}`);
});
}
}
})
fs.readdir(resourcePath,(err,files)=>{
if(err){
console.warn(err)
}else{
files.forEach(file=>{
if(fs.statSync(resourcePath + file).isFile()){
if(file !== 'index.html'){
let html = fs.readFileSync(resourcePath + file).toString();
_ = cheerio.load(html);
let content = _('#content');
$('#content').replaceWith(content);
$('#footer').remove();
$('#content').prepend('<h2>' + matchTitle(path.basename(file,".html")) + '</h2>');
fs.writeFile(buildPath + file, $.html(), function(err){
if(err) console.log(err)
else console.log(file + ' ------------------success');
})
}
}
})
}
})
fs.copySync(resourcePath + 'images', buildPath + 'images');
const matchTitle = (filename)=> {
return doc.map[filename];
}
代码分析
使用方法基本都是异步版本(Sync),避免回调地狱。
判断路径(文件/文件夹)是否存在
fs.existsSync(path) //true:存在;false:不存在;
创建文件夹
fs.mkdirSync(path)
读取文件
fs.readFileSync(path)
读取文件夹目录
fs.readdir(path,callback)
写入文件
fs.writeFile(path,content,callback)
判断是否文件
stat方法的参数是一个文件或目录,它产生一个对象,该对象包含了该文件或目录的具体信息。我们往往通过该方法,判断正在处理的到底是一个文件,还是一个目录。
fs.statSync(path).isFile()
拷贝目录
copySync需要插件 fs-extra 支持
fs.copySync(copyPath,distPath)
cheerio
需求中需要针对dom解析代码来匹配相应的href,所以这里强烈推荐 cheerio 插件,和JQuery一样的API,可以大大方便在node环境下针对dom的解析。
两个不同html的代码拼接片段:
const cheerio = require('cheerio');
$ = cheerio.load(fs.readFileSync('index.html').toString());
_ = cheerio.load(fs.readFileSync('app.html').toString());
let content = _('#content'); //获取app.html下的#content内容
$('#content').replaceWith(content); //index.html下的#content内容替换为app.html下的内容