mdToVue.js 11 KB

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