mdToVue.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. const fs = require('fs');
  2. const path = require('path');
  3. //hash获取工具
  4. let { hashElement } = require('folder-hash');
  5. //marked转换工具
  6. let marked = require('marked');
  7. if (!marked) {
  8. console.log('you need npm i marked -D!');
  9. }
  10. //文件监听
  11. let Chokidar = require('chokidar');
  12. // 基本配置文件信息
  13. let {version} = require("../package.json");
  14. //vue js脚本
  15. //获取所有文件列表
  16. let fileList = [];
  17. let Articlehead = '';
  18. let Articleheadcount = 0;
  19. // maked文件配置
  20. var rendererMd = new marked.Renderer();
  21. //maked文件规则
  22. rendererMd.code = function (code, infostring, escaped) {
  23. var lang = (infostring || '').match(/\S*/)[0];
  24. if (this.options.highlight) {
  25. var out = this.options.highlight(code, lang);
  26. if (out != null && out !== code) {
  27. escaped = true;
  28. code = out;
  29. }
  30. }
  31. if (!lang) {
  32. return '<pre><code>'
  33. + (escaped ? code : escape(code, true))
  34. + '</code></pre>';
  35. }
  36. if (lang === 'html') {
  37. code = code.replace(/@latest/g, '@' + version)
  38. }
  39. return '<hide><pre class="prettyprint"><span class="lang">' + lang + '</span><div class="code-wrapper"><code class="'
  40. + this.options.langPrefix
  41. + escape(lang, true)
  42. + '">'
  43. + (escaped ? code : escape(code, true))
  44. + '</code></div><i class="copy" copy="copy" data-clipboard-action="copy" data-clipboard-target="code" title="复制代码"></i><i toast="toast" title="全屏"></i></pre></hide>\n';
  45. };
  46. rendererMd.heading = function (text,level){
  47. // 如果需要 marklist;
  48. if(level==2){
  49. Articleheadcount++;
  50. Articlehead +=`<li @click="leavelchose(`+Articleheadcount+`)" :class="levalcur==`+Articleheadcount+`?'cur':''" class="level`+Articleheadcount+'"><a href="#head'+Articleheadcount+'">'+text.substr(0,10)+'</a></li>';
  51. }
  52. return "<h"+level+" class='visibility' id='head"+Articleheadcount+"'>"+ text +"</h"+level+">";
  53. };
  54. marked.setOptions({
  55. renderer: rendererMd,
  56. highlight: function (code) {
  57. return require('highlight.js').highlightAuto(code).value;
  58. },
  59. tables: true
  60. }, res => {
  61. })
  62. /**
  63. * 是否需要单独处理头部信息
  64. * @param {text} sorce 替换 头部信息
  65. */
  66. function insert(sorce) {
  67. var insert = sorce.indexOf('</h1>');
  68. if (insert > -1) {
  69. return sorce.substring(0, insert) + '<i class="qrcode"><a :href="demourl"><span>请使用手机扫码体验</span><img :src="codeurl" alt=""></a></i>' + sorce.substring(insert, sorce.length);
  70. } else {
  71. return sorce
  72. }
  73. }
  74. ///创建一个空文件
  75. /**
  76. * 修复中
  77. * @param {string} output 输出路径
  78. * @param {string} sorce 文件源
  79. * @param {boole} ishasCode 是否需要二维码
  80. */
  81. function createdFile(output, sorce, param) {
  82. var pathSrc = output;
  83. if (param.needCode) {
  84. var res = insert(sorce);
  85. } else {
  86. var res = sorce;
  87. }
  88. var bufs = `<template><div @click="dsCode">
  89. <div v-if="content" class="layer">
  90. <pre><span class="close-box" @click="closelayer"></span><div v-html="content"></div></pre>
  91. </div>`+ res + (param.hasMarkList?'<ul class="markList">' +Articlehead + '</ul>':'')+
  92. `<nut-backtop :right="50" :bottom="50"></nut-backtop></div></template><script>import root from '../root.js';
  93. export default {
  94. mixins:[root]
  95. }</script>`;
  96. Articlehead = '';
  97. Articleheadcount = 0;
  98. fs.writeFile(pathSrc,bufs,'utf8',(err)=>{ })
  99. }
  100. /**
  101. * 目录读取,找到跟文件
  102. * @fileSrc {string} 打开文件路径
  103. * @callback {fn} 结束后回调函数
  104. */
  105. function readDirRecur(fileSrc, callback) {
  106. fs.readdir(fileSrc, function(err, files) {
  107. var count = 0
  108. var checkEnd = function() {
  109. ++count == files.length && callback()
  110. }
  111. files.forEach(function(file) {
  112. var fullPath = fileSrc + '/' + file;
  113. fs.stat(fullPath, function(err, stats) {
  114. if (stats.isDirectory()) {
  115. return readDirRecur(fullPath, checkEnd);
  116. } else {
  117. /*not use ignore files*/
  118. if(file[0] == '.') {
  119. } else {
  120. fileList.push(fullPath)
  121. }
  122. checkEnd()
  123. }
  124. })
  125. })
  126. //为空时直接回调
  127. files.length === 0 && callback()
  128. })
  129. }
  130. function fileReadStar(filedir,callback){
  131. fs.readFile(filedir, 'utf-8', (err, data) => {
  132. let html = marked(data);
  133. let mdName = "";
  134. let opensName = filedir.replace(/(^.*\/|.md)/g,"");
  135. //如果是doc文件以前缀 为
  136. if (opensName === 'doc') {
  137. mdName = filedir.replace(/(^.*packages\/|\/doc\.md)/g,'');
  138. } else {
  139. //如果不是doc命名的文件
  140. mdName = opensName;
  141. }
  142. callback({
  143. mdName:mdName,
  144. html:html
  145. })
  146. });
  147. }
  148. /**
  149. * 判断是否位md文件 并进行操作
  150. * 判断文件是否有改动
  151. * @src {string} 打开的文件目录
  152. * @hasobj {obj} hash对象
  153. * @callback {fn} 回调函数
  154. */
  155. function ismd(src,hasobj,callback){
  156. //判断文件类型是否是md文件
  157. let filedir = src;
  158. //return new Promise((resolve,reject)=>{
  159. if (/.md$/.test(filedir)) {
  160. if(hasobj.fileText){
  161. let hasHObjs = hasobj;
  162. hashElement(filedir).then(res=>{
  163. if(hasHObjs.fileText.indexOf(res.hash)==-1){
  164. //执行写入
  165. //同时更新缓存
  166. fs.writeFileSync(hasHObjs.cachePath,
  167. hasHObjs.fileText+'|'+res.hash
  168. ,'utf-8');
  169. fileReadStar(filedir,(obj)=>{
  170. callback(obj)
  171. })
  172. }
  173. })
  174. }else{
  175. //如果没有hash 直接做下一部
  176. fileReadStar(filedir,(obj)=>{
  177. callback(obj)
  178. })
  179. }
  180. //对md文件存储 hash
  181. //文件读取
  182. }
  183. }
  184. /**
  185. * 检查文件是否存折
  186. * @param {*} path
  187. * @param {*} callback
  188. */
  189. function checkIsexists (path,callback){
  190. let pathFileName = path.replace(/[^a-zA-Z]/g,'');
  191. let cacheName = './scripts/local'+pathFileName+'.cache';
  192. fs.exists(cacheName, res=>{
  193. if(!res){
  194. fs.writeFile(cacheName,'','utf8',()=>{
  195. callback(cacheName)
  196. })
  197. }else{
  198. callback(cacheName)
  199. }
  200. })
  201. }
  202. /**
  203. * 执行文件缓存
  204. */
  205. let outhash = [];
  206. function pushHash(obj){
  207. //紧紧插入md文件hash
  208. if(/\.md$/.test(obj.name)&&obj['hash']){
  209. outhash.push(obj.hash);
  210. }
  211. if(obj['children']&&obj['children'].length>0){
  212. obj['children'].map(res=>{
  213. pushHash(res)
  214. })
  215. }
  216. }
  217. //hash 对比
  218. /**
  219. * 初始化检查是否有md文件 hash
  220. * 如果没有 hash 创建一个 json文件并把 md文件 hash进去 用来做缓存
  221. * 初始化获取所有md文件的 hash
  222. * @param {*} path
  223. */
  224. function comparehash(path,callback){
  225. checkIsexists(path,(cachePath)=>{
  226. //获取文件内容
  227. let fileText = fs.readFileSync(cachePath,'utf-8');
  228. //获取文件 hash
  229. hashElement(path, {
  230. folders: { exclude: ['.*', 'node_modules', 'test_coverage'] },
  231. files: { include: ['*.md'],exclude:['*.js','*.vue','*.scss','__test__'] }
  232. }).then(hash => {
  233. if(fileText){
  234. //如果有内容
  235. callback({
  236. fileText:fileText,
  237. cachePath:cachePath
  238. })
  239. }else{
  240. pushHash(hash)
  241. fs.writeFileSync(cachePath,outhash.join('|'),'utf-8');
  242. //如果没有内容
  243. callback({
  244. fileText:fileText,
  245. cachePath:cachePath
  246. })
  247. }
  248. })
  249. .catch(error => {
  250. return console.error('hashing failed:', error);
  251. });
  252. })
  253. }
  254. //文件监听
  255. function filelisten(param){
  256. let watcher = Chokidar.watch(param.entry,{
  257. persistent: true,
  258. usePolling: true,
  259. })
  260. let log = console.dir.bind(console);
  261. let watchAction = function({event, eventPath}){
  262. // 这里进行文件更改后的操作
  263. fileReadStar(eventPath,(res)=>{
  264. createdFile(param.output + res.mdName + '.vue', res.html, param)
  265. })
  266. }
  267. watcher.on('change', path => watchAction({event: 'change', eventPath: path}))
  268. .on('unlink', path => watchAction({event: 'remove', eventPath: path}));
  269. }
  270. /**
  271. * 文件转md
  272. * @param {obj}
  273. * @entry {string} 文件读取路径
  274. * @output {string} 文件输出路径
  275. * @needCode {boolen} 是否需要二维码 默认 true
  276. */
  277. function fileDisplay(param) {
  278. //检查文件是否第一次初始化并获取hash
  279. comparehash(param.entry,(hashMsgObj)=>{
  280. // 获取目录下所有文件
  281. readDirRecur(param.entry, function(filePath) {
  282. //文件列表
  283. fileList.map(item=>{
  284. ismd(item,hashMsgObj,res=>{
  285. //res md文件处理结果
  286. createdFile(param.output + res.mdName + '.vue', res.html, param)
  287. })
  288. })
  289. });
  290. });
  291. }
  292. /**
  293. * 输出的文件目录 是否存在
  294. * @outPath {String} 输出的文件目录
  295. */
  296. function ishasOutFile(outPath,callback){
  297. fs.stat(outPath,(err,res)=>{
  298. if(err){
  299. fs.mkdir(outPath,err=>{
  300. if(err){
  301. console.log(err)
  302. }else{
  303. callback()
  304. }
  305. })
  306. }else{
  307. callback()
  308. }
  309. })
  310. }
  311. // function checkLoacl(){
  312. // var selfFile = './scripts/mdToVue.js';
  313. // //获取文件内容
  314. // checkIsexists(selfFile,(fileName)=>{
  315. // let fileText = fs.readFileSync(fileName,'utf-8');
  316. // })
  317. // //检查本文件是否被改动
  318. // hashElement('./scripts/mdToVue.js').then(hash => {
  319. // if(fileText){
  320. // //如果有内容
  321. // callback({
  322. // fileText:fileText,
  323. // cachePath:cachePath
  324. // })
  325. // }else{
  326. // pushHash(hash)
  327. // fs.writeFileSync(cachePath,outhash.join('|'),'utf-8');
  328. // //如果没有内容
  329. // callback({
  330. // fileText:fileText,
  331. // cachePath:cachePath
  332. // })
  333. // }
  334. // })
  335. // .catch(error => {
  336. // return console.error('hashing failed:', error);
  337. // });
  338. // }
  339. /**
  340. *
  341. * @param {entry} 文件读取路径
  342. * @param {output} 文件输出路径
  343. * @param {needCode} 是否需要二维码 默认 true
  344. */
  345. function MdToHtml(commomOption) {
  346. // 默认参数就这么多了,暂时没想到
  347. let params = {
  348. entry:'',
  349. output:'',
  350. needCode:true,
  351. isbuild:true,
  352. hasMarkList:true
  353. }
  354. params = Object.assign(params,commomOption);
  355. //检查输出路径
  356. ishasOutFile(params.output,()=>{
  357. //获取所有的md 转html的结果
  358. fileDisplay(params);
  359. //文件监听
  360. if(params.isbuild){
  361. filelisten(params);
  362. }
  363. });
  364. }
  365. //用于后期的扩展暂时没想到
  366. MdToHtml.prototype.apply = function (compiler) {
  367. // console.log(compiler,'lls')
  368. };
  369. module.exports = MdToHtml;