mdToVue.js 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. const fs = require('fs');
  2. var 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 + `</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. console.log(res)
  182. if(!res){
  183. fs.writeFile(cacheName,'','utf8',()=>{
  184. callback(cacheName)
  185. })
  186. }else{
  187. callback(cacheName)
  188. }
  189. })
  190. }
  191. /**
  192. * 执行文件缓存
  193. */
  194. let outhash = [];
  195. function pushHash(obj){
  196. //紧紧插入md文件hash
  197. if(/\.md$/.test(obj.name)&&obj['hash']){
  198. outhash.push(obj.hash);
  199. }
  200. if(obj['children']&&obj['children'].length>0){
  201. obj['children'].map(res=>{
  202. pushHash(res)
  203. })
  204. }
  205. }
  206. //hash 对比
  207. /**
  208. * 初始化检查是否有md文件 hash
  209. * 如果没有 hash 创建一个 json文件并把 md文件 hash进去 用来做缓存
  210. * 初始化获取所有md文件的 hash
  211. * @param {*} path
  212. */
  213. function comparehash(path,callback){
  214. checkIsexists(path,(cachePath)=>{
  215. //获取文件内容
  216. let fileText = fs.readFileSync(cachePath,'utf-8');
  217. //获取文件 hash
  218. hashElement(path, {
  219. folders: { exclude: ['.*', 'node_modules', 'test_coverage'] },
  220. files: { include: ['*.md'],exclude:['*.js','*.vue','*.scss','__test__'] }
  221. }).then(hash => {
  222. if(fileText){
  223. //如果有内容
  224. callback({
  225. fileText:fileText,
  226. cachePath:cachePath
  227. })
  228. }else{
  229. pushHash(hash)
  230. fs.writeFileSync(cachePath,outhash.join('|'),'utf-8');
  231. //如果没有内容
  232. callback({
  233. fileText:fileText,
  234. cachePath:cachePath
  235. })
  236. }
  237. })
  238. .catch(error => {
  239. return console.error('hashing failed:', error);
  240. });
  241. })
  242. }
  243. //文件监听
  244. function filelisten(param){
  245. let watcher = Chokidar.watch(param.entry,{
  246. persistent: true,
  247. usePolling: true,
  248. })
  249. let log = console.dir.bind(console);
  250. let watchAction = function({event, eventPath}){
  251. // 这里进行文件更改后的操作
  252. fileReadStar(eventPath,(res)=>{
  253. createdFile(param.output + res.mdName + '.vue', res.html, param.needCode)
  254. })
  255. }
  256. watcher.on('change', path => watchAction({event: 'change', eventPath: path}))
  257. .on('unlink', path => watchAction({event: 'remove', eventPath: path}));
  258. }
  259. /**
  260. * 文件转md
  261. * @param {obj}
  262. * @entry {string} 文件读取路径
  263. * @output {string} 文件输出路径
  264. * @needCode {boolen} 是否需要二维码 默认 true
  265. */
  266. function fileDisplay(param) {
  267. //检查文件是否第一次初始化并获取hash
  268. comparehash(param.entry,(hashMsgObj)=>{
  269. // 获取目录下所有文件
  270. readDirRecur(param.entry, function(filePath) {
  271. //文件列表
  272. fileList.map(item=>{
  273. ismd(item,hashMsgObj,res=>{
  274. //res md文件处理结果
  275. createdFile(param.output + res.mdName + '.vue', res.html, param.needCode)
  276. })
  277. })
  278. });
  279. });
  280. }
  281. /**
  282. * 输出的文件目录 是否存在
  283. * @outPath {String} 输出的文件目录
  284. */
  285. function ishasOutFile(outPath,callback){
  286. fs.stat(outPath,(err,res)=>{
  287. if(err){
  288. fs.mkdir(outPath,err=>{
  289. if(err){
  290. console.log(err)
  291. }else{
  292. callback()
  293. }
  294. })
  295. }else{
  296. callback()
  297. }
  298. })
  299. }
  300. /**
  301. *
  302. * @param {entry} 文件读取路径
  303. * @param {output} 文件输出路径
  304. * @param {needCode} 是否需要二维码 默认 true
  305. */
  306. function MdToHtml(commomOption) {
  307. // 默认参数就这么多了,暂时没想到
  308. let params = {
  309. entry:'',
  310. output:'',
  311. needCode:true
  312. }
  313. params = Object.assign(params,commomOption);
  314. //检查输出路径
  315. ishasOutFile(params.output,()=>{
  316. //获取所有的md 转html的结果
  317. fileDisplay(params);
  318. //文件监听
  319. filelisten(params);
  320. });
  321. }
  322. //用于后期的扩展暂时没想到
  323. MdToHtml.prototype.apply = function (compiler) {
  324. // console.log(compiler,'lls')
  325. };
  326. module.exports = MdToHtml;