mdToVue.js 9.9 KB

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