yuque-dl 1.0.10 → 1.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,30 @@
1
1
  # yuque-dl
2
2
 
3
- yuque 知识库下载
3
+ 语雀知识库下载为本地markdown
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm i -g yuque-dl
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```bash
14
+ $ yuque-dl --help
15
+
16
+ Usage:
17
+ $ yuque-dl <url>
18
+
19
+ Commands:
20
+ <url> request url
21
+
22
+ For more info, run any command with the `--help` flag:
23
+ $ yuque-dl --help
24
+
25
+ Options:
26
+ -d, --distDir <dir> 下载的目录 eg: -d download (default: download)
27
+ -i, --ignoreImg 忽略图片不下载 (default: false)
28
+ -h, --help Display this message
29
+ -v, --version Display version number
30
+ ```
package/dist/cjs/cli.js CHANGED
@@ -1 +1 @@
1
- "use strict";var e=require("node:fs"),r=require("cac"),n=require("./index-b758b4ba.js");require("node:fs/promises"),require("node:path"),require("axios"),require("log4js"),require("progress"),require("rand-user-agent"),require("pull-md-img");const i=r.cac("yuque-dl"),{version:o}=JSON.parse(e.readFileSync(new URL("../../package.json","undefined"==typeof document?require("url").pathToFileURL(__filename).href:document.currentScript&&document.currentScript.src||new URL("cli.js",document.baseURI).href)).toString());i.command("<url>","request url").option("-d, --distDir <dir>","下载的目录 eg: -d download",{default:"download"}).action((async(e,r)=>{try{await n.main(e,r)}catch(e){n.logger.error(e.message||"unknown exception")}})),i.help(),i.version(o);try{i.parse()}catch(e){n.logger.error(e.message||"unknown exception"),process.exit(1)}
1
+ "use strict";var e=require("node:fs"),r=require("cac"),n=require("./index-f7dedfcf.js");require("node:fs/promises"),require("axios"),require("log4js"),require("progress"),require("rand-user-agent"),require("pull-md-img");const i=r.cac("yuque-dl"),{version:o}=JSON.parse(e.readFileSync(new URL("../../package.json","undefined"==typeof document?require("url").pathToFileURL(__filename).href:document.currentScript&&document.currentScript.src||new URL("cli.js",document.baseURI).href)).toString());i.command("<url>","request url").option("-d, --distDir <dir>","下载的目录 eg: -d download",{default:"download"}).option("-i, --ignoreImg","忽略图片不下载",{default:!1}).action((async(e,r)=>{try{await n.main(e,r)}catch(e){n.logger.error(e.message||"unknown exception")}})),i.help(),i.version(o);try{i.parse()}catch(e){n.logger.error(e.message||"unknown exception"),process.exit(1)}
@@ -0,0 +1 @@
1
+ "use strict";var e=require("node:fs/promises"),t=require("axios"),r=require("log4js"),o=require("progress"),i=require("rand-user-agent"),s=require("pull-md-img");var n=(r.configure({appenders:{cheese:{type:"console",layout:{type:"pattern",pattern:"%[%c [%p]:%] %m%n"}}},categories:{default:{appenders:["cheese"],level:"trace"}}}),r.getLogger("yuque-dl"));class a{constructor(e,t){this.bookPath="",this.progressFilePath="",this.progressInfo=[],this.curr=0,this.total=0,this.isDownloadInterrupted=!1,this.bar=null,this.completePromise=null,this.bookPath=e,this.progressFilePath=`${e}/progress.json`,this.total=t}async init(){let e;this.progressInfo=await this.getProgress(),this.curr=this.progressInfo.length,this.completePromise=new Promise((t=>{e=t})),this.curr>0&&this.curr!==this.total&&(this.isDownloadInterrupted=!0,n.info("断点下载")),this.bar=new o("downloading [:bar] :rate/bps :percent :etas",{width:80,total:this.total,curr:0,callback(){e()}}),this.curr>0&&this.bar.tick(this.curr)}async getProgress(){let t=[];try{const r=await e.readFile(this.progressFilePath,{encoding:"utf8"});t=JSON.parse(r)}catch(r){r&&"ENOENT"===r.code&&await e.writeFile(this.progressFilePath,JSON.stringify(t),{encoding:"utf8"})}return t}async updateProgress(t,r){r&&(this.progressInfo.push(t),await e.writeFile(this.progressFilePath,JSON.stringify(this.progressInfo),{encoding:"utf8"})),this.curr=this.curr+1,this.bar&&this.bar?.tick()}}function l({browser:e="chrome",os:t="mac os",device:r="desktop"}){r=r.toLowerCase(),e=e.toLowerCase(),t=t.toLowerCase();let o=i(r,e,t);if("chrome"===e)for(;o.includes("Chrome-Lighthouse")||o.includes("Gener8")||o.includes("HeadlessChrome")||o.includes("SMTBot");)o=i(r,e,t);if("safari"===e)for(;o.includes("Applebot");)o=i(r,e,t);return o}class c{constructor(e){this.summaryInfo=e}async genFile(){const{bookName:t,bookDesc:r,bookPath:o,uuidMap:i}=this.summaryInfo;let s=`# ${t}\n\n`;r&&(s+=`> ${r}\n\n`);let a=s;const l=[];i.forEach((e=>{const t=e.toc,r=t.parent_uuid,o=this.findTree(l,r),i={text:t.title.replace(/[\\\/:\*\?"<>\|\n\r]/g,"_").replace(/\s/,""),id:t.uuid,level:1,type:""};"title"===t.type.toLocaleLowerCase()||""!==t.child_uuid?(i.type="title","boolean"!=typeof o?(Array.isArray(o.children)||(o.children=[]),i.level=o.level+1,o.children.push(i)):(i.level=1,l.push(i))):(i.type="link",i.link=e.path,"boolean"!=typeof o?(Array.isArray(o.children)||(o.children=[]),i.level=o.level+1,o.children.push(i)):(i.level=1,l.push(i)))})),a+=this.genSummaryContent(l,"");try{await e.writeFile(`${o}/SUMMARY.md`,a)}catch(e){n.error("Generate Summary Error")}}genSummaryContent(e,t){for(let r=0;r<e.length;r++){const o=e[r];if("title"===o.type)t+=`\n${"".padStart(o.level+1,"#")} ${o.text}\n\n`;else if("link"===o.type){const e=o.link?o.link.replace(/\s/g,"%20"):o.link;t+=`- [${o.text}](${e})\n`}Array.isArray(o.children)&&(t+=this.genSummaryContent(o.children,""))}return t}findIdItem(e,t){if(e.id===t)return e;if(e.children){const r=this.findTree(e.children,t);if(r)return r}return!1}findTree(e,t){if(!t)return!1;for(let r=0;r<e.length;r++){const o=this.findIdItem(e[r],t);if(o)return o}return!1}}function u(e){const r=/decodeURIComponent\(\"(.+)\"\)\);/m;return t.get(e,{headers:{"user-agent":l({browser:"chrome",device:"desktop"})}}).then((({data:e="",status:t})=>200===t?e:"")).then((e=>{const t=e.match(r)||"";if(!t[1])return{};const o=JSON.parse(decodeURIComponent(t[1]));if(!o.book)return{};return{bookId:o.book.id,bookSlug:o.book.slug,tocList:o.book.toc||[],bookName:o.book.name||"",bookDesc:o.book.description||""}}))}async function d(r){const{bookId:o,itemUrl:i,savePath:n,saveFilePath:a,uuid:c,articleUrl:u,articleTitle:d,ignoreImg:h}=r;let p=`https://www.yuque.com/api/docs/${i}`;const g={book_id:String(o),merge_dynamic_data:String(!1),mode:"markdown"},f=new URLSearchParams(g).toString();p=`${p}?${f}`;let m=(await t.get(p,{headers:{"user-agent":l({browser:"chrome",device:"desktop"})}}).then((({data:e,status:t})=>{if(200===t){const t=e;if(!t||!t.data||!t.data.sourcecode)throw new Error(`download article Error: ${p}`);return t}throw new Error(`download article Error: ${p} http status ${t}`)})).catch((e=>{throw new Error(`download article Error: ${p} ${e.message}`)}))).data.sourcecode;if(!h)try{m=await s.run(m,{dist:n,imgDir:`img/${c}`,isIgnoreConsole:!0})}catch(e){let t=`download article image Error: ${e.message}`;throw e.error&&e.url&&(t=`download article image Error ${e.url}: ${e.error?.message}`),new Error(t)}m=m.replace(/<br(\s?)\/>/gm,"\n"),d&&(m=`# ${d}\n\x3c!--page header--\x3e\n\n${m}\n\n`),u&&(m+=`\x3c!--page footer--\x3e\n- 原文: <${u}>`);try{return await e.writeFile(a,m),!0}catch(e){throw new Error(`download article Error ${u}: ${e.message}`)}}exports.downloadArticle=d,exports.getKnowledgeBaseInfo=u,exports.logger=n,exports.main=async function(t,r){const{bookId:o,tocList:i,bookName:s,bookDesc:l,bookSlug:h}=await u(t);if(!o)throw new Error("No found book id");if(!i||0===i.length)throw new Error("No found toc list");const p=`${r.distDir}/${o}`;await e.mkdir(p,{recursive:!0});const g=i.length,f=new a(p,g);if(await f.init(),f.curr===g)return void n.info("√ 已完成");const m=new Map;f.isDownloadInterrupted&&f.progressInfo.forEach((e=>{m.set(e.toc.uuid,e)}));const w=t.replace(new RegExp(`(.*?/${h}).*`),"$1");let y=0,b=0,$=[];for(let t=0;t<g;t++){const s=i[t];if("string"!=typeof s.type)continue;if(m.get(s.uuid))continue;const n=/[\\\/:\*\?"<>\|\n\r]/g;if("title"===s.type.toLocaleLowerCase()||""!==s.child_uuid){let t=s,r=[],o=[];for(;t;)r.unshift(t.title.replace(n,"_").replace(/\s/,"")),o.unshift(t.uuid),t=m.get(t.parent_uuid)?m.get(t.parent_uuid).toc:void 0;const i={path:r.join("/"),pathTitleList:r,pathIdList:o,toc:s};await e.mkdir(`${p}/${r.join("/")}`,{recursive:!0}),m.set(s.uuid,i),f.updateProgress(i,!0)}else if(s.url){b+=1;let e={path:"",pathTitleList:[],pathIdList:[]};m.get(s.parent_uuid)&&(e=m.get(s.parent_uuid));const t=s.title.replace(n,"_").replace(/\s/,""),i=[...e.pathTitleList,`${t}.md`],a=[...e.pathIdList,s.uuid],l={path:i.join("/"),pathTitleList:i,pathIdList:a,toc:s};let c=!0;try{await d({bookId:o,itemUrl:s.url,savePath:`${p}/${e.path}`,saveFilePath:`${p}/${l.path}`,uuid:s.uuid,articleUrl:`${w}/${s.url}`,articleTitle:s.title,ignoreImg:r.ignoreImg})}catch(e){c=!1,y+=1,$.push(l),f.bar?.interrupt(`✕ ${e.message}`)}m.set(s.uuid,l),f.updateProgress(l,c)}}if(await(f.completePromise?.finally((()=>console.log("")))),y>0){n.info(`本次执行总数${b}篇,✕ 失败${y}篇`);for(let e=0;e<$.length;e++)n.error(`✕ ${$[e].path}`);n.info("o(╥﹏╥)o 由于网络波动以上下载失败,请重新执行命令")}const k=new c({bookPath:p,bookName:s,bookDesc:l,uuidMap:m});await k.genFile(),n.info("√ 生成目录 SUMMARY.md"),f.curr!==g||n.info("√ 已完成")};
package/dist/cjs/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";require("node:fs/promises"),require("node:path"),require("axios");var e=require("./index-b758b4ba.js");require("pull-md-img"),require("log4js"),require("progress"),require("rand-user-agent"),exports.getKnowledgeBaseInfo=e.getKnowledgeBaseInfo,exports.main=e.main;
1
+ "use strict";require("node:fs/promises"),require("axios");var e=require("./index-f7dedfcf.js");require("pull-md-img"),require("log4js"),require("progress"),require("rand-user-agent"),exports.downloadArticle=e.downloadArticle,exports.getKnowledgeBaseInfo=e.getKnowledgeBaseInfo,exports.main=e.main;
package/dist/es/cli.js CHANGED
@@ -1 +1 @@
1
- import{readFileSync as o}from"node:fs";import{cac as r}from"cac";import{m as e,l as t}from"./index-7580521b.js";import"node:fs/promises";import"node:path";import"axios";import"log4js";import"progress";import"rand-user-agent";import"pull-md-img";const i=r("yuque-dl"),{version:s}=JSON.parse(o(new URL("../../package.json",import.meta.url)).toString());i.command("<url>","request url").option("-d, --distDir <dir>","下载的目录 eg: -d download",{default:"download"}).action((async(o,r)=>{try{await e(o,r)}catch(o){t.error(o.message||"unknown exception")}})),i.help(),i.version(s);try{i.parse()}catch(o){t.error(o.message||"unknown exception"),process.exit(1)}
1
+ import{readFileSync as o}from"node:fs";import{cac as r}from"cac";import{m as e,l as t}from"./index-ec0d8de6.js";import"node:fs/promises";import"axios";import"log4js";import"progress";import"rand-user-agent";import"pull-md-img";const i=r("yuque-dl"),{version:n}=JSON.parse(o(new URL("../../package.json",import.meta.url)).toString());i.command("<url>","request url").option("-d, --distDir <dir>","下载的目录 eg: -d download",{default:"download"}).option("-i, --ignoreImg","忽略图片不下载",{default:!1}).action((async(o,r)=>{try{await e(o,r)}catch(o){t.error(o.message||"unknown exception")}})),i.help(),i.version(n);try{i.parse()}catch(o){t.error(o.message||"unknown exception"),process.exit(1)}
@@ -0,0 +1 @@
1
+ import e from"node:fs/promises";import t from"axios";import r from"log4js";import o from"progress";import i from"rand-user-agent";import s from"pull-md-img";var n=(r.configure({appenders:{cheese:{type:"console",layout:{type:"pattern",pattern:"%[%c [%p]:%] %m%n"}}},categories:{default:{appenders:["cheese"],level:"trace"}}}),r.getLogger("yuque-dl"));class a{constructor(e,t){this.bookPath="",this.progressFilePath="",this.progressInfo=[],this.curr=0,this.total=0,this.isDownloadInterrupted=!1,this.bar=null,this.completePromise=null,this.bookPath=e,this.progressFilePath=`${e}/progress.json`,this.total=t}async init(){let e;this.progressInfo=await this.getProgress(),this.curr=this.progressInfo.length,this.completePromise=new Promise((t=>{e=t})),this.curr>0&&this.curr!==this.total&&(this.isDownloadInterrupted=!0,n.info("断点下载")),this.bar=new o("downloading [:bar] :rate/bps :percent :etas",{width:80,total:this.total,curr:0,callback(){e()}}),this.curr>0&&this.bar.tick(this.curr)}async getProgress(){let t=[];try{const r=await e.readFile(this.progressFilePath,{encoding:"utf8"});t=JSON.parse(r)}catch(r){r&&"ENOENT"===r.code&&await e.writeFile(this.progressFilePath,JSON.stringify(t),{encoding:"utf8"})}return t}async updateProgress(t,r){r&&(this.progressInfo.push(t),await e.writeFile(this.progressFilePath,JSON.stringify(this.progressInfo),{encoding:"utf8"})),this.curr=this.curr+1,this.bar&&this.bar?.tick()}}function l({browser:e="chrome",os:t="mac os",device:r="desktop"}){r=r.toLowerCase(),e=e.toLowerCase(),t=t.toLowerCase();let o=i(r,e,t);if("chrome"===e)for(;o.includes("Chrome-Lighthouse")||o.includes("Gener8")||o.includes("HeadlessChrome")||o.includes("SMTBot");)o=i(r,e,t);if("safari"===e)for(;o.includes("Applebot");)o=i(r,e,t);return o}class c{constructor(e){this.summaryInfo=e}async genFile(){const{bookName:t,bookDesc:r,bookPath:o,uuidMap:i}=this.summaryInfo;let s=`# ${t}\n\n`;r&&(s+=`> ${r}\n\n`);let a=s;const l=[];i.forEach((e=>{const t=e.toc,r=t.parent_uuid,o=this.findTree(l,r),i={text:t.title.replace(/[\\\/:\*\?"<>\|\n\r]/g,"_").replace(/\s/,""),id:t.uuid,level:1,type:""};"title"===t.type.toLocaleLowerCase()||""!==t.child_uuid?(i.type="title","boolean"!=typeof o?(Array.isArray(o.children)||(o.children=[]),i.level=o.level+1,o.children.push(i)):(i.level=1,l.push(i))):(i.type="link",i.link=e.path,"boolean"!=typeof o?(Array.isArray(o.children)||(o.children=[]),i.level=o.level+1,o.children.push(i)):(i.level=1,l.push(i)))})),a+=this.genSummaryContent(l,"");try{await e.writeFile(`${o}/SUMMARY.md`,a)}catch(e){n.error("Generate Summary Error")}}genSummaryContent(e,t){for(let r=0;r<e.length;r++){const o=e[r];if("title"===o.type)t+=`\n${"".padStart(o.level+1,"#")} ${o.text}\n\n`;else if("link"===o.type){const e=o.link?o.link.replace(/\s/g,"%20"):o.link;t+=`- [${o.text}](${e})\n`}Array.isArray(o.children)&&(t+=this.genSummaryContent(o.children,""))}return t}findIdItem(e,t){if(e.id===t)return e;if(e.children){const r=this.findTree(e.children,t);if(r)return r}return!1}findTree(e,t){if(!t)return!1;for(let r=0;r<e.length;r++){const o=this.findIdItem(e[r],t);if(o)return o}return!1}}function u(e){const r=/decodeURIComponent\(\"(.+)\"\)\);/m;return t.get(e,{headers:{"user-agent":l({browser:"chrome",device:"desktop"})}}).then((({data:e="",status:t})=>200===t?e:"")).then((e=>{const t=e.match(r)||"";if(!t[1])return{};const o=JSON.parse(decodeURIComponent(t[1]));if(!o.book)return{};return{bookId:o.book.id,bookSlug:o.book.slug,tocList:o.book.toc||[],bookName:o.book.name||"",bookDesc:o.book.description||""}}))}async function d(r){const{bookId:o,itemUrl:i,savePath:n,saveFilePath:a,uuid:c,articleUrl:u,articleTitle:d,ignoreImg:h}=r;let p=`https://www.yuque.com/api/docs/${i}`;const g={book_id:String(o),merge_dynamic_data:String(!1),mode:"markdown"},m=new URLSearchParams(g).toString();p=`${p}?${m}`;let f=(await t.get(p,{headers:{"user-agent":l({browser:"chrome",device:"desktop"})}}).then((({data:e,status:t})=>{if(200===t){const t=e;if(!t||!t.data||!t.data.sourcecode)throw new Error(`download article Error: ${p}`);return t}throw new Error(`download article Error: ${p} http status ${t}`)})).catch((e=>{throw new Error(`download article Error: ${p} ${e.message}`)}))).data.sourcecode;if(!h)try{f=await s.run(f,{dist:n,imgDir:`img/${c}`,isIgnoreConsole:!0})}catch(e){let t=`download article image Error: ${e.message}`;throw e.error&&e.url&&(t=`download article image Error ${e.url}: ${e.error?.message}`),new Error(t)}f=f.replace(/<br(\s?)\/>/gm,"\n"),d&&(f=`# ${d}\n\x3c!--page header--\x3e\n\n${f}\n\n`),u&&(f+=`\x3c!--page footer--\x3e\n- 原文: <${u}>`);try{return await e.writeFile(a,f),!0}catch(e){throw new Error(`download article Error ${u}: ${e.message}`)}}async function h(t,r){const{bookId:o,tocList:i,bookName:s,bookDesc:l,bookSlug:h}=await u(t);if(!o)throw new Error("No found book id");if(!i||0===i.length)throw new Error("No found toc list");const p=`${r.distDir}/${o}`;await e.mkdir(p,{recursive:!0});const g=i.length,m=new a(p,g);if(await m.init(),m.curr===g)return void n.info("√ 已完成");const f=new Map;m.isDownloadInterrupted&&m.progressInfo.forEach((e=>{f.set(e.toc.uuid,e)}));const w=t.replace(new RegExp(`(.*?/${h}).*`),"$1");let y=0,b=0,$=[];for(let t=0;t<g;t++){const s=i[t];if("string"!=typeof s.type)continue;if(f.get(s.uuid))continue;const n=/[\\\/:\*\?"<>\|\n\r]/g;if("title"===s.type.toLocaleLowerCase()||""!==s.child_uuid){let t=s,r=[],o=[];for(;t;)r.unshift(t.title.replace(n,"_").replace(/\s/,"")),o.unshift(t.uuid),t=f.get(t.parent_uuid)?f.get(t.parent_uuid).toc:void 0;const i={path:r.join("/"),pathTitleList:r,pathIdList:o,toc:s};await e.mkdir(`${p}/${r.join("/")}`,{recursive:!0}),f.set(s.uuid,i),m.updateProgress(i,!0)}else if(s.url){b+=1;let e={path:"",pathTitleList:[],pathIdList:[]};f.get(s.parent_uuid)&&(e=f.get(s.parent_uuid));const t=s.title.replace(n,"_").replace(/\s/,""),i=[...e.pathTitleList,`${t}.md`],a=[...e.pathIdList,s.uuid],l={path:i.join("/"),pathTitleList:i,pathIdList:a,toc:s};let c=!0;try{await d({bookId:o,itemUrl:s.url,savePath:`${p}/${e.path}`,saveFilePath:`${p}/${l.path}`,uuid:s.uuid,articleUrl:`${w}/${s.url}`,articleTitle:s.title,ignoreImg:r.ignoreImg})}catch(e){c=!1,y+=1,$.push(l),m.bar?.interrupt(`✕ ${e.message}`)}f.set(s.uuid,l),m.updateProgress(l,c)}}if(await(m.completePromise?.finally((()=>console.log("")))),y>0){n.info(`本次执行总数${b}篇,✕ 失败${y}篇`);for(let e=0;e<$.length;e++)n.error(`✕ ${$[e].path}`);n.info("o(╥﹏╥)o 由于网络波动以上下载失败,请重新执行命令")}const k=new c({bookPath:p,bookName:s,bookDesc:l,uuidMap:f});await k.genFile(),n.info("√ 生成目录 SUMMARY.md"),m.curr!==g||n.info("√ 已完成")}export{d,u as g,n as l,h as m};
package/dist/es/index.js CHANGED
@@ -1 +1 @@
1
- import"node:fs/promises";import"node:path";import"axios";export{g as getKnowledgeBaseInfo,m as main}from"./index-7580521b.js";import"pull-md-img";import"log4js";import"progress";import"rand-user-agent";
1
+ import"node:fs/promises";import"axios";export{d as downloadArticle,g as getKnowledgeBaseInfo,m as main}from"./index-ec0d8de6.js";import"pull-md-img";import"log4js";import"progress";import"rand-user-agent";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yuque-dl",
3
- "version": "1.0.10",
3
+ "version": "1.0.12",
4
4
  "description": "yuque 知识库下载",
5
5
  "type": "module",
6
6
  "bin": {
@@ -58,7 +58,7 @@
58
58
  "cac": "^6.7.14",
59
59
  "log4js": "^6.9.1",
60
60
  "progress": "^2.0.3",
61
- "pull-md-img": "^0.0.20",
61
+ "pull-md-img": "^0.0.22",
62
62
  "rand-user-agent": "1.0.109"
63
63
  },
64
64
  "engines": {
@@ -19,5 +19,5 @@ export default class ProgressBar {
19
19
  constructor(bookPath: string, total: number);
20
20
  init(): Promise<void>;
21
21
  getProgress(): Promise<IProgress>;
22
- updateProgress(progressItem: IProgressItem): Promise<void>;
22
+ updateProgress(progressItem: IProgressItem, isSuccess: boolean): Promise<void>;
23
23
  }
@@ -0,0 +1,24 @@
1
+ import type { IProgressItem } from './ProgressBar';
2
+ interface IGenSummaryFile {
3
+ bookPath: string;
4
+ bookName?: string;
5
+ bookDesc?: string;
6
+ uuidMap: Map<string, IProgressItem>;
7
+ }
8
+ interface SummaryItem {
9
+ id: string;
10
+ children?: SummaryItem[];
11
+ type: string;
12
+ text: string;
13
+ level: number;
14
+ link?: string;
15
+ }
16
+ export default class Summary {
17
+ summaryInfo: IGenSummaryFile;
18
+ constructor(summaryInfo: IGenSummaryFile);
19
+ genFile(): Promise<void>;
20
+ genSummaryContent(summary: SummaryItem[], summaryContent: string): string;
21
+ findIdItem(node: SummaryItem, id: string): boolean | SummaryItem;
22
+ findTree(tree: SummaryItem[], id: string): SummaryItem | boolean;
23
+ }
24
+ export {};
package/types/cli.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export interface IOptions {
2
2
  distDir: string;
3
+ ignoreImg: boolean;
3
4
  }
package/types/index.d.ts CHANGED
@@ -2,10 +2,24 @@ import type { IOptions } from './cli';
2
2
  import type { KnowledgeBase } from './types/KnowledgeBaseResponse';
3
3
  interface IKnowledgeBaseInfo {
4
4
  bookId?: number;
5
+ bookSlug?: string;
5
6
  tocList?: KnowledgeBase.Toc[];
6
7
  bookName?: string;
7
8
  bookDesc?: string;
8
9
  }
10
+ interface IDownloadArticleParams {
11
+ bookId: number;
12
+ itemUrl: string;
13
+ savePath: string;
14
+ saveFilePath: string;
15
+ uuid: string;
16
+ articleTitle: string;
17
+ articleUrl: string;
18
+ ignoreImg: boolean;
19
+ }
20
+ /** 获取知识库数据信息 */
9
21
  declare function getKnowledgeBaseInfo(url: string): Promise<IKnowledgeBaseInfo>;
22
+ /** 下载单篇文章 */
23
+ declare function downloadArticle(params: IDownloadArticleParams): Promise<boolean>;
10
24
  declare function main(url: string, options: IOptions): Promise<void>;
11
- export { getKnowledgeBaseInfo, main };
25
+ export { getKnowledgeBaseInfo, downloadArticle, main };
@@ -1 +0,0 @@
1
- "use strict";var e=require("node:fs/promises"),t=require("node:path"),r=require("axios"),i=require("log4js"),o=require("progress"),s=require("rand-user-agent"),n=require("pull-md-img"),a={knowledgeBaseReg:/decodeURIComponent\(\"(.+)\"\)\);/m,dist:"./download"};var l=(i.configure({appenders:{cheese:{type:"console",layout:{type:"pattern",pattern:"%[%c [%p]:%] %m%n"}}},categories:{default:{appenders:["cheese"],level:"trace"}}}),i.getLogger("yuque-dl"));class c{constructor(e,t){this.bookPath="",this.progressFilePath="",this.progressInfo=[],this.curr=0,this.total=0,this.isDownloadInterrupted=!1,this.bar=null,this.completePromise=null,this.bookPath=e,this.progressFilePath=`${e}/progress.json`,this.total=t}async init(){let e;this.progressInfo=await this.getProgress(),this.curr=this.progressInfo.length,this.completePromise=new Promise((t=>{e=t})),this.curr>0&&this.curr!==this.total&&(this.isDownloadInterrupted=!0,l.info("断点下载")),this.bar=new o("[:bar] :rate/bps :percent :etas",{total:this.total,curr:this.curr,callback(){e()}})}async getProgress(){let t=[];try{const r=await e.readFile(this.progressFilePath,{encoding:"utf8"});t=JSON.parse(r)}catch(r){r&&"ENOENT"===r.code&&await e.writeFile(this.progressFilePath,JSON.stringify(t),{encoding:"utf8"})}return t}async updateProgress(t){this.progressInfo.push(t),await e.writeFile(this.progressFilePath,JSON.stringify(this.progressInfo),{encoding:"utf8"}),this.curr=this.curr+1,this.bar&&this.bar?.tick()}}function u({browser:e="chrome",os:t="mac os",device:r="desktop"}){r=r.toLowerCase(),e=e.toLowerCase(),t=t.toLowerCase();let i=s(r,e,t);if("chrome"===e)for(;i.includes("Chrome-Lighthouse")||i.includes("Gener8")||i.includes("HeadlessChrome")||i.includes("SMTBot");)i=s(r,e,t);if("safari"===e)for(;i.includes("Applebot");)i=s(r,e,t);return i}function d(e){return r.get(e,{headers:{"user-agent":u({browser:"chrome",device:"desktop"})}}).then((({data:e="",status:t})=>200===t?e:"")).then((e=>{const t=e.match(a.knowledgeBaseReg)||"";if(!t[1])return{};const r=JSON.parse(decodeURIComponent(t[1]));return{bookId:r.book.id,tocList:r.book.toc||[],bookName:r.book.name||"",bookDesc:r.book.description||""}}))}async function h(t){const{bookId:i,itemUrl:o,savePath:s,saveFilePath:a,uuid:c,articleUrl:d,articleTitle:h}=t,p=`https://www.yuque.com/api/docs/${o}`,g=await r.get(p,{headers:{"user-agent":u({browser:"chrome",device:"desktop"})},params:{book_id:i,merge_dynamic_data:!1,mode:"markdown"}}).then((({data:e,status:t})=>200===t?e:(l.error(`download article error: ${p} http status ${t}`),null)));if(!g||!g.data||!g.data.sourcecode)return!1;let f=g.data.sourcecode;try{f=await n.run(f,{dist:s,imgDir:`img/${c}`,isIgnoreConsole:!0})}catch(e){return l.error(`download article img Error (api: ${p})`),!1}f=f.replace(/<br(\s?)\/>/gm,"\n"),h&&(f=`# ${h}\n\x3c!--page header--\x3e\n\n${f}\n\n`),d&&(f+=`\x3c!--page footer--\x3e\n- 原文: <${d}>`);try{return await e.writeFile(a,f),!0}catch(e){return l.error(`download article write file Error (api: ${p})`),!1}}function p(e,t){for(let r=0;r<e.length;r++){const i=e[r];if("title"===i.type)t+=`\n${"".padStart(i.level+1,"#")} ${i.text}\n\n`;else if("link"===i.type){const e=i.link?i.link.replace(/\s/g,"%20"):i.link;t+=`- [${i.text}](${e})\n`}Array.isArray(i.children)&&(t+=p(i.children,""))}return t}function g(e,t){if(e.id===t)return e;if(e.children){const r=f(e.children,t);if(r)return r}return!1}function f(e,t){if(!t)return!1;for(let r=0;r<e.length;r++){const i=g(e[r],t);if(i)return i}return!1}exports.getKnowledgeBaseInfo=d,exports.logger=l,exports.main=async function(r,i){const{bookId:o,tocList:s,bookName:n,bookDesc:a}=await d(r);if(!o)throw new Error("No found book id");if(!s||0===s.length)throw new Error("No found toc list");const u=`${i.distDir}/${o}`;await e.mkdir(u,{recursive:!0});const g=s.length,w=new c(u,g);if(await w.init(),w.curr===g)return void l.info("√ 已完成");const m=new Map;w.isDownloadInterrupted&&w.progressInfo.forEach((e=>{m.set(e.toc.uuid,e)}));const k=t.parse(r).dir;let y=0,b=0;for(let t=0;t<g;t++){const r=s[t];if("string"!=typeof r.type)continue;if(m.get(r.uuid))continue;const i=/[\\\/:\*\?"<>\|\n\r]/g;if("title"===r.type.toLocaleLowerCase()||""!==r.child_uuid){let t=r,o=[],s=[];for(;t;)o.unshift(t.title.replace(i,"_")),s.unshift(t.uuid),t=m.get(t.parent_uuid)?m.get(t.parent_uuid).toc:void 0;const n={path:o.join("/"),pathTitleList:o,pathIdList:s,toc:r};await e.mkdir(`${u}/${o.join("/")}`,{recursive:!0}),m.set(r.uuid,n),w.updateProgress(n)}else if(r.url){b+=1;let e={path:"",pathTitleList:[],pathIdList:[]};m.get(r.parent_uuid)&&(e=m.get(r.parent_uuid));const t=r.title.replace(i,"_"),s=[...e.pathTitleList,`${t}.md`],n=[...e.pathIdList,r.uuid],a={path:s.join("/"),pathTitleList:s,pathIdList:n,toc:r};await h({bookId:o,itemUrl:r.url,savePath:`${u}/${e.path}`,saveFilePath:`${u}/${a.path}`,uuid:r.uuid,articleUrl:`${k}/${r.url}`,articleTitle:r.title})?(m.set(r.uuid,a),w.updateProgress(a)):y+=1}}await w.completePromise,l.info(`本次执行总数${b}篇,✕ 失败${y}篇`),l.info("生成目录 SUMMARY.md"),await async function(t){const{bookName:r,bookDesc:i,bookPath:o,uuidMap:s}=t,n=`# ${r}\n\n > ${i}\n\n`;let a=n;const c=[];s.forEach((e=>{const t=e.toc,r=t.parent_uuid,i=f(c,r);"title"===t.type.toLocaleLowerCase()||""!==t.child_uuid?"boolean"!=typeof i?(Array.isArray(i.children)||(i.children=[]),i.children.push({id:t.uuid,type:"title",level:i.level+1,text:t.title})):c.push({id:t.uuid,type:"title",text:t.title,level:1}):"boolean"!=typeof i?(Array.isArray(i.children)||(i.children=[]),i.children.push({id:t.uuid,type:"link",level:i.level+1,link:e.path,text:t.title})):c.push({id:t.uuid,type:"link",text:t.title,link:e.path,level:1})}));let u=p(c,"");a+=u;try{await e.writeFile(`${o}/SUMMARY.md`,a)}catch(e){l.error("Generate Summary Error")}}({bookPath:u,bookName:n,bookDesc:a,uuidMap:m}),w.curr!==g||l.info("√ 已完成")};
@@ -1 +0,0 @@
1
- import t from"node:fs/promises";import e from"node:path";import r from"axios";import i from"log4js";import o from"progress";import s from"rand-user-agent";import a from"pull-md-img";var n={knowledgeBaseReg:/decodeURIComponent\(\"(.+)\"\)\);/m,dist:"./download"};var l=(i.configure({appenders:{cheese:{type:"console",layout:{type:"pattern",pattern:"%[%c [%p]:%] %m%n"}}},categories:{default:{appenders:["cheese"],level:"trace"}}}),i.getLogger("yuque-dl"));class c{constructor(t,e){this.bookPath="",this.progressFilePath="",this.progressInfo=[],this.curr=0,this.total=0,this.isDownloadInterrupted=!1,this.bar=null,this.completePromise=null,this.bookPath=t,this.progressFilePath=`${t}/progress.json`,this.total=e}async init(){let t;this.progressInfo=await this.getProgress(),this.curr=this.progressInfo.length,this.completePromise=new Promise((e=>{t=e})),this.curr>0&&this.curr!==this.total&&(this.isDownloadInterrupted=!0,l.info("断点下载")),this.bar=new o("[:bar] :rate/bps :percent :etas",{total:this.total,curr:this.curr,callback(){t()}})}async getProgress(){let e=[];try{const r=await t.readFile(this.progressFilePath,{encoding:"utf8"});e=JSON.parse(r)}catch(r){r&&"ENOENT"===r.code&&await t.writeFile(this.progressFilePath,JSON.stringify(e),{encoding:"utf8"})}return e}async updateProgress(e){this.progressInfo.push(e),await t.writeFile(this.progressFilePath,JSON.stringify(this.progressInfo),{encoding:"utf8"}),this.curr=this.curr+1,this.bar&&this.bar?.tick()}}function u({browser:t="chrome",os:e="mac os",device:r="desktop"}){r=r.toLowerCase(),t=t.toLowerCase(),e=e.toLowerCase();let i=s(r,t,e);if("chrome"===t)for(;i.includes("Chrome-Lighthouse")||i.includes("Gener8")||i.includes("HeadlessChrome")||i.includes("SMTBot");)i=s(r,t,e);if("safari"===t)for(;i.includes("Applebot");)i=s(r,t,e);return i}function d(t){return r.get(t,{headers:{"user-agent":u({browser:"chrome",device:"desktop"})}}).then((({data:t="",status:e})=>200===e?t:"")).then((t=>{const e=t.match(n.knowledgeBaseReg)||"";if(!e[1])return{};const r=JSON.parse(decodeURIComponent(e[1]));return{bookId:r.book.id,tocList:r.book.toc||[],bookName:r.book.name||"",bookDesc:r.book.description||""}}))}async function h(r,i){const{bookId:o,tocList:s,bookName:a,bookDesc:n}=await d(r);if(!o)throw new Error("No found book id");if(!s||0===s.length)throw new Error("No found toc list");const u=`${i.distDir}/${o}`;await t.mkdir(u,{recursive:!0});const h=s.length,g=new c(u,h);if(await g.init(),g.curr===h)return void l.info("√ 已完成");const w=new Map;g.isDownloadInterrupted&&g.progressInfo.forEach((t=>{w.set(t.toc.uuid,t)}));const k=e.parse(r).dir;let y=0,b=0;for(let e=0;e<h;e++){const r=s[e];if("string"!=typeof r.type)continue;if(w.get(r.uuid))continue;const i=/[\\\/:\*\?"<>\|\n\r]/g;if("title"===r.type.toLocaleLowerCase()||""!==r.child_uuid){let e=r,o=[],s=[];for(;e;)o.unshift(e.title.replace(i,"_")),s.unshift(e.uuid),e=w.get(e.parent_uuid)?w.get(e.parent_uuid).toc:void 0;const a={path:o.join("/"),pathTitleList:o,pathIdList:s,toc:r};await t.mkdir(`${u}/${o.join("/")}`,{recursive:!0}),w.set(r.uuid,a),g.updateProgress(a)}else if(r.url){b+=1;let t={path:"",pathTitleList:[],pathIdList:[]};w.get(r.parent_uuid)&&(t=w.get(r.parent_uuid));const e=r.title.replace(i,"_"),s=[...t.pathTitleList,`${e}.md`],a=[...t.pathIdList,r.uuid],n={path:s.join("/"),pathTitleList:s,pathIdList:a,toc:r};await p({bookId:o,itemUrl:r.url,savePath:`${u}/${t.path}`,saveFilePath:`${u}/${n.path}`,uuid:r.uuid,articleUrl:`${k}/${r.url}`,articleTitle:r.title})?(w.set(r.uuid,n),g.updateProgress(n)):y+=1}}await g.completePromise,l.info(`本次执行总数${b}篇,✕ 失败${y}篇`),l.info("生成目录 SUMMARY.md"),await async function(e){const{bookName:r,bookDesc:i,bookPath:o,uuidMap:s}=e,a=`# ${r}\n\n > ${i}\n\n`;let n=a;const c=[];s.forEach((t=>{const e=t.toc,r=e.parent_uuid,i=m(c,r);"title"===e.type.toLocaleLowerCase()||""!==e.child_uuid?"boolean"!=typeof i?(Array.isArray(i.children)||(i.children=[]),i.children.push({id:e.uuid,type:"title",level:i.level+1,text:e.title})):c.push({id:e.uuid,type:"title",text:e.title,level:1}):"boolean"!=typeof i?(Array.isArray(i.children)||(i.children=[]),i.children.push({id:e.uuid,type:"link",level:i.level+1,link:t.path,text:e.title})):c.push({id:e.uuid,type:"link",text:e.title,link:t.path,level:1})}));let u=f(c,"");n+=u;try{await t.writeFile(`${o}/SUMMARY.md`,n)}catch(t){l.error("Generate Summary Error")}}({bookPath:u,bookName:a,bookDesc:n,uuidMap:w}),g.curr!==h||l.info("√ 已完成")}async function p(e){const{bookId:i,itemUrl:o,savePath:s,saveFilePath:n,uuid:c,articleUrl:d,articleTitle:h}=e,p=`https://www.yuque.com/api/docs/${o}`,f=await r.get(p,{headers:{"user-agent":u({browser:"chrome",device:"desktop"})},params:{book_id:i,merge_dynamic_data:!1,mode:"markdown"}}).then((({data:t,status:e})=>200===e?t:(l.error(`download article error: ${p} http status ${e}`),null)));if(!f||!f.data||!f.data.sourcecode)return!1;let g=f.data.sourcecode;try{g=await a.run(g,{dist:s,imgDir:`img/${c}`,isIgnoreConsole:!0})}catch(t){return l.error(`download article img Error (api: ${p})`),!1}g=g.replace(/<br(\s?)\/>/gm,"\n"),h&&(g=`# ${h}\n\x3c!--page header--\x3e\n\n${g}\n\n`),d&&(g+=`\x3c!--page footer--\x3e\n- 原文: <${d}>`);try{return await t.writeFile(n,g),!0}catch(t){return l.error(`download article write file Error (api: ${p})`),!1}}function f(t,e){for(let r=0;r<t.length;r++){const i=t[r];if("title"===i.type)e+=`\n${"".padStart(i.level+1,"#")} ${i.text}\n\n`;else if("link"===i.type){const t=i.link?i.link.replace(/\s/g,"%20"):i.link;e+=`- [${i.text}](${t})\n`}Array.isArray(i.children)&&(e+=f(i.children,""))}return e}function g(t,e){if(t.id===e)return t;if(t.children){const r=m(t.children,e);if(r)return r}return!1}function m(t,e){if(!e)return!1;for(let r=0;r<t.length;r++){const i=g(t[r],e);if(i)return i}return!1}export{d as g,l,h as m};
package/types/config.d.ts DELETED
@@ -1,5 +0,0 @@
1
- declare const _default: {
2
- knowledgeBaseReg: RegExp;
3
- dist: string;
4
- };
5
- export default _default;