├── .gitignore
├── demo
├── 01.gif
├── 02.jpg
├── 03.gif
└── novel_sources.json
├── src
├── constants.ts
├── parser
│ ├── index.ts
│ ├── rule.ts
│ ├── general.ts
│ └── parser.ts
├── type.ts
├── parser.ts
├── cache.ts
├── db.ts
├── index.ts
├── api.ts
├── utils.ts
├── main.ts
└── reader.ts
├── SOURCE.md
├── test
├── ruleParser.js
└── parser.js
├── package.json
├── README.md
├── Algorithm.md
├── tsconfig.json
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
--------------------------------------------------------------------------------
/demo/01.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zt8989/novel-reader/HEAD/demo/01.gif
--------------------------------------------------------------------------------
/demo/02.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zt8989/novel-reader/HEAD/demo/02.jpg
--------------------------------------------------------------------------------
/demo/03.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/zt8989/novel-reader/HEAD/demo/03.gif
--------------------------------------------------------------------------------
/src/constants.ts:
--------------------------------------------------------------------------------
1 | export const scriptStartTag = ""
2 | export const scriptEndTag = ""
3 | export const newLineSplit = "\n"
--------------------------------------------------------------------------------
/src/parser/index.ts:
--------------------------------------------------------------------------------
1 | export type ParserReturnType = {
2 | index: { next?: string, prev?: string}
3 | content: string
4 | title: string
5 | }
6 |
7 | export interface IParser {
8 | parseNovel: (url: string) => Promise
9 | }
--------------------------------------------------------------------------------
/src/type.ts:
--------------------------------------------------------------------------------
1 | export type BookType = {
2 | name: string
3 | lastUrl: string
4 | }
5 |
6 | export type DataStoreDocumentType = {
7 | _id: string
8 | createdAt?: Date
9 | updatedAt?: Date
10 | }
11 |
12 | export type ConfigType = {
13 | lastUrl?: string
14 | lastLine?: number
15 | line?: number,
16 | baseUrl?: string,
17 | token?: string
18 | }
--------------------------------------------------------------------------------
/src/parser.ts:
--------------------------------------------------------------------------------
1 | import { GeneralParser } from './parser/general';
2 | import { readSourcesSync } from './utils';
3 | import { RuleParser } from './parser/rule';
4 | import { IParser } from './parser/index';
5 |
6 | export function parseNovel(url: string) {
7 | const sources = readSourcesSync()
8 | const source = sources.find(source => url.startsWith(source.bookSourceUrl))
9 | let parser: IParser
10 | if (source) {
11 | parser = new RuleParser(source)
12 | } else {
13 | parser = new GeneralParser()
14 | }
15 | return parser.parseNovel(url)
16 | }
17 |
--------------------------------------------------------------------------------
/src/cache.ts:
--------------------------------------------------------------------------------
1 | import { ParserReturnType } from "./parser/index";
2 |
3 | export default class CacheManager {
4 | private cache: { url: string, value: Promise }[] = []
5 |
6 | setItem(url: string, value: Promise){
7 | this.cache.push({ url, value })
8 | }
9 |
10 | getItem(url: string) {
11 | return this.cache.find(x => x.url === url)?.value || null
12 | }
13 |
14 | removeItem(url: string) {
15 | const index = this.cache.findIndex(x => x.url === url)
16 | if(index !== -1){
17 | this.cache.splice(index, 1)
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/demo/novel_sources.json:
--------------------------------------------------------------------------------
1 | [{
2 | "bookSourceUrl": "https://www.biquwu.cc/",
3 | "ruleBookContent": "next($('#content').text())",
4 | "rulePrevPage": "next($('a:contains(上一章)').attr('href'))",
5 | "ruleNextPage": "next($('a:contains(下一章)').attr('href'))",
6 | "ruleBookTitle": "next($('.content > h1').text())"
7 | },
8 | {
9 | "bookSourceUrl": "http://m.suixkan.com/",
10 | "ruleBookContent": "class.con.0@textNodes&&class.con.1@textNodes&&class.con.1@textNodes",
11 | "rulePrevPage": "class.prevUrl@text",
12 | "ruleNextPage": "class.nextUrl@text",
13 | "ruleBookTitle": "class.section@tag.h2@text##\\(.*\\)"
14 | }]
--------------------------------------------------------------------------------
/SOURCE.md:
--------------------------------------------------------------------------------
1 | # 源规则
2 |
3 | 目前支持两种采集源规则,一种简易规则,另一种js规则
4 |
5 | ## 源文件
6 |
7 | 将`novel_sources.json`文件放在home目录下的.nvrd目录,
8 | `novel_sources.json`是一个json数组
9 | 可以在`demo/novel_sources.json`下查看
10 |
11 | ### 规则字段说明
12 |
13 | * `bookSourceUrl` 书源地址
14 | * `ruleBookContent` 文本内容规则
15 | * `rulePrevPage` 上一章规则
16 | * `ruleNextPage` 下一章规则
17 | * `ruleBookTitle` 标题规则
18 |
19 | ## 简易规则
20 |
21 | 具体可以查看
22 | [阅读源规则](https://alanskycn.gitee.io/teachme/Rule/source.html)
23 |
24 | 只支持JSOUP之Default语法规则
25 |
26 | ## js规则
27 |
28 | 如果规则已``开头``结尾,则为js规则
29 |
30 | js接受三个参数`$`,`fetch`,`next`,返回结果必须调用`next`
31 |
32 | * `$` `cheerio`实例
33 | * `fetch` `node-fetch`实例
34 | * `next` 回调,进入下一步
35 |
36 | > 例如
37 | ```javascript
38 | next($('#content').text())
39 | ```
--------------------------------------------------------------------------------
/test/ruleParser.js:
--------------------------------------------------------------------------------
1 | const parser = require('../dist/parser');
2 | const { RuleParser } = require('../dist/parser/rule');
3 | const utils = require('../dist/utils');
4 |
5 | let urls = [
6 | 'https://h.630book.com/book_155359/49485601.html',
7 | ]
8 |
9 | ;(async () => {
10 | for (let url of urls) {
11 | const p = parser.getParser(url)
12 | console.assert(p instanceof RuleParser, "rule 查找失败")
13 | let res = await p.parseNovel(url)
14 | let content = res.content
15 | const limit = 40
16 | const lines = utils.wordWrap(content, limit)
17 | console.assert(lines.length > 10, "解析失败")
18 | for(let line of lines) {
19 | console.assert(line.length <= limit, "error")
20 | console.log(line.length, line)
21 | }
22 | console.info(res)
23 | }
24 | })()
25 |
26 |
--------------------------------------------------------------------------------
/src/db.ts:
--------------------------------------------------------------------------------
1 | import { join } from 'path'
2 | import Datastore from 'nedb-promises'
3 | import os from "os"
4 |
5 | function dbFactory(fileName: string){
6 | return Datastore.create({
7 | filename: join(os.homedir(), `/.nvrd/${fileName}.db`),
8 | timestampData: true,
9 | autoload: true
10 | })
11 | }
12 |
13 | let novelsIns: Datastore | null = null
14 |
15 | let sourcesIns: Datastore | null = null
16 |
17 | let usersIns: Datastore | null = null
18 |
19 | export default {
20 | sources: () => {
21 | if (sourcesIns === null) {
22 | sourcesIns = dbFactory('sources')
23 | }
24 | return sourcesIns
25 | },
26 | books: () => {
27 | if (novelsIns === null) {
28 | novelsIns = dbFactory('books')
29 | }
30 | return novelsIns
31 | },
32 | users: () => {
33 | if (usersIns === null) {
34 | usersIns = dbFactory('users')
35 | }
36 | return usersIns
37 | },
38 | }
39 |
--------------------------------------------------------------------------------
/test/parser.js:
--------------------------------------------------------------------------------
1 | const parser = require('../dist/parser')
2 | const utils = require('../dist/utils');
3 |
4 | let urls = [
5 | // 'https://www.daxuetian.com/xs/2/2511.html',
6 | // 'http://www.yunxs.com/xintengjiejie/252578.html',
7 | // 'http://www.xbiquge.la/26/26874/13244872.html',
8 | // 'https://www.oldtimescc.cc/go/37136/19989330.html',
9 | // 'https://www.sikushu8.com/5/5867/919735.html',
10 | // 'https://www.2kzw.com/42/42542/38451420.html',
11 | // 'https://www.hgq26.com/107/107229/64932812.html',
12 | // 'http://m.suixkan.com/r/218571/218572.html',
13 | 'https://h.630book.com/book_155359/49485601.html'
14 | ]
15 |
16 | ;(async () => {
17 | for (let url of urls) {
18 | let res = await parser.getParser(url).parseNovel(url)
19 | let content = res.content
20 | const limit = 40
21 | const lines = utils.wordWrap(content, limit)
22 | console.assert(lines.length > 10, "解析失败")
23 | for(let line of lines) {
24 | console.assert(line.length <= limit, "error")
25 | console.log(line.length, line)
26 | }
27 | console.info(res.index)
28 | }
29 | })()
30 |
31 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@zt8989/novel-reader",
3 | "version": "1.1.3",
4 | "description": "terminal novel reader",
5 | "main": "dist/main.js",
6 | "scripts": {
7 | "build": "tsc",
8 | "watch": "tsc -w"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "git@github.com:zt8989/novel-reader.git"
13 | },
14 | "keywords": [
15 | "terminal",
16 | "typescript",
17 | "novel reader"
18 | ],
19 | "author": "zhouteng",
20 | "license": "MIT",
21 | "bin": {
22 | "nvrd": "dist/index.js"
23 | },
24 | "files": [
25 | "dist"
26 | ],
27 | "dependencies": {
28 | "@types/cheerio": "^0.22.23",
29 | "@types/inquirer": "^7.3.1",
30 | "@types/mkdirp": "^1.0.1",
31 | "@types/node": "^10.12.10",
32 | "@types/node-fetch": "^2.5.7",
33 | "chalk": "^4.1.0",
34 | "cheerio": "^1.0.0-rc.3",
35 | "cli-cursor": "^3.1.0",
36 | "commander": "^2.19.0",
37 | "debug": "^4.1.0",
38 | "eol": "^0.9.1",
39 | "gbk.js": "^0.3.0",
40 | "inquirer": "^7.3.3",
41 | "mkdirp": "^1.0.4",
42 | "nedb-promises": "^4.1.0",
43 | "node-fetch": "^2.6.1",
44 | "rxjs": "^6.6.3",
45 | "typescript": "^4.1.3",
46 | "update-notifier": "^5.0.1"
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Novel Reader
2 |
3 | 一款基于命令行跨平台文本小说阅读工具,996 与 10107 程序员摸鱼划水必备神器。
4 |
5 | 
6 |
7 | > 添加书籍
8 |
9 | 
10 |
11 | > 实时更换书籍
12 |
13 | 
14 |
15 | ## 功能亮点
16 |
17 | - 使用 nodejs 开发
18 | - 软件运行于命令行,对 Vimer 友好,支持 Vim 方式的 Key Binding 进行翻页和跳转。
19 | - 支持 Boss Key,方便紧急情况下对界面隐藏和伪装。
20 |
21 | ## 安装步骤
22 |
23 | 其实,你和摸鱼之间,只有两步的距离:
24 |
25 | ```bash
26 | npm i -g @zt8989/novel-reader
27 | # -n 3表示显示3行,默认为1
28 | nvrd read <章节地址> -n 3
29 |
30 | nvrd book -l # 列出所有书籍
31 | nvrd book -a <书名> <章节地址> #添加书籍
32 | nvrd book -s <书名> <章节地址> #修改书籍地址
33 | nvrd book --remove <书名> #删除书籍
34 | nvrd book -r [书名] -n 3 # 如果不填写书名或者查找不到会出现下拉列表, -n 3表示显示3行,默认为1
35 | ```
36 |
37 | ## 支持平台
38 |
39 | - Mac OS
40 | - Linux
41 | - Windows
42 |
43 | ## 快捷键说明
44 |
45 | - `ctrl+c` 退出程序
46 | - `j`, `ctrl+n`, ``显示下一行内容
47 | - `k` 或者 `ctrl+p`, ``显示上一行内容
48 | - `b` Boss Key,隐藏当前内容并显示伪装 Shell 提示符
49 | - `r` 重新获取当前页面
50 | - `l` 展示书源列表
51 | - `n` 下一章节
52 | - `m` 上一章节
53 |
54 | ## 提示
55 |
56 | - 只测试过笔趣阁相关网址,采集规则不一定适用其他网站
57 |
58 | ## 书源规则
59 |
60 | [书源规则](./SOURCE.md)
61 |
62 | ## 小说内容采集算法解释
63 |
64 | [小说解析算法解释](./Algorithm.md)
65 |
66 | ## Issue 与 PR
67 |
68 | 欢迎提交 issue 与 merge request。
69 |
70 | ## 协议
71 |
72 | 本开源软件基于[MIT](#)。
73 |
74 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | import program from 'commander'
4 | import { readAction, bookAction, sourceAction, loginAction } from './main'
5 | const updateNotifier = require('update-notifier');
6 |
7 | const pkg = require('../package.json');
8 |
9 | updateNotifier({ pkg }).notify();
10 |
11 | program
12 | .version(pkg.version)
13 |
14 | program.command("read ")
15 | .description("read from url")
16 | .option("-n [line]", "set the line number")
17 | .action(readAction)
18 |
19 | program.command("book")
20 | .description("book manage")
21 | .option("-l, --list", "list books")
22 | .option("-r, --read [name]", "list books")
23 | .option("-a, --add ", "add a book")
24 | .option("--remove ", "remove a book")
25 | .option("-s, --set-url ")
26 | .option("-n [line]", "set the line number")
27 | .option("-u, --up", "将数据上传到服务器")
28 | .option("-d, --down", "将数据下载到本地")
29 | .action(bookAction)
30 |
31 | program.command("source")
32 | .description("source manage")
33 | .option("-u, --up", "将数据上传到服务器")
34 | .option("-d, --down", "将数据下载到本地")
35 | .action(sourceAction)
36 |
37 | program.command("login")
38 | .description("login")
39 | .option("-a, --api", "set api url")
40 | .action(loginAction)
41 |
42 | program.parse(process.argv)
--------------------------------------------------------------------------------
/src/parser/rule.ts:
--------------------------------------------------------------------------------
1 | import cheerio from 'cheerio';
2 | import { SourceType } from '../utils';
3 | import { parseRule } from './parser'
4 | import { GeneralParser } from './general';
5 |
6 | export class RuleParser extends GeneralParser {
7 | private source: SourceType
8 |
9 | constructor(source: SourceType) {
10 | super()
11 | this.source = source
12 | }
13 |
14 | /**
15 | * 解析内容
16 | * @param doc
17 | */
18 | protected async parseContent(doc: string) {
19 | if(this.source.ruleBookContent) {
20 | const $ = cheerio.load(doc, { decodeEntities: false })
21 | return parseRule(this.source.ruleBookContent, $('body'), $) as string
22 | } else {
23 | return super.parseContent(doc)
24 | }
25 | }
26 |
27 | protected async parsePrevPage($: cheerio.Root) {
28 | if(this.source.rulePrevPage) {
29 | return parseRule(this.source.rulePrevPage, $('body'), $) as string
30 | } else {
31 | return super.parsePrevPage($)
32 | }
33 | }
34 |
35 | protected async parseNextPage($: cheerio.Root){
36 | if(this.source.ruleNextPage) {
37 | return parseRule(this.source.ruleNextPage, $('body'), $) as string
38 | } else {
39 | return super.parseNextPage($)
40 | }
41 | }
42 |
43 | protected async parseTitle(doc: string) {
44 | if(this.source.ruleBookTitle) {
45 | const $ = cheerio.load(doc, { decodeEntities: false });
46 | return parseRule(this.source.ruleBookTitle, $('body'), $) as string
47 | } else {
48 | return super.parseTitle(doc)
49 | }
50 | }
51 |
52 | }
--------------------------------------------------------------------------------
/Algorithm.md:
--------------------------------------------------------------------------------
1 | ## 小说解析算法解释
2 |
3 | ```typescript
4 | protected async parseContent(doc: string) {
5 | const $ = cheerio.load(doc, { decodeEntities: false });
6 |
7 | function parseChildren(parent: cheerio.Cheerio, size: number, slope: number, variance: number): string {
8 | const children = parent.children()
9 | if(children.length === 1) {
10 | return parseChildren(children, size, slope, variance)
11 | }
12 | let maxChildren: { element: cheerio.Cheerio; size: number; slope: number; };
13 | const sizeList: number[] = [];
14 | children.each((index, element) => {
15 | const wrapperElement = $(element);
16 | const length = wrapperElement.text().length;
17 | sizeList.push(length);
18 | const tempSlop = (size - length) / size;
19 | if (!maxChildren) {
20 | maxChildren = {
21 | element: wrapperElement,
22 | size: length,
23 | slope: tempSlop
24 | };
25 | } else if (tempSlop < (maxChildren.slope)) { // 保存最小斜率
26 | maxChildren = {
27 | element: wrapperElement,
28 | size: length,
29 | slope: tempSlop
30 | };
31 | }
32 | });
33 | // debug(sizeList)
34 | let avg = sizeList.reduce((sum, x) => sum + x, 0) / sizeList.length;
35 | // 计算方差
36 | let tempVariance = Math.sqrt(sizeList.reduce((sum, x) => sum + (x - avg) ** 2, 0) / sizeList.length);
37 | // debug(avg, tempVariance)
38 | // @ts-ignore
39 | if (maxChildren) {
40 | // console.log('---', maxChildren.element.html(), maxChildren.element[0].name)
41 | debug(maxChildren.element.text(), maxChildren.slope, slope, tempVariance, variance)
42 | if (tempVariance > 100) {
43 | return parseChildren(maxChildren.element, maxChildren.size, maxChildren.slope, tempVariance);
44 | } else {
45 | return handleTextNodes(parent)
46 | }
47 | } else {
48 | return "";
49 | }
50 | }
51 |
52 | return parseChildren($('body'), $('body').text().length, 1, $('body').text().length);
53 | }
54 |
55 | ```
56 |
57 | 父元素的文字长度为`size`。
58 | 遍历每个子元素,取得文字长度`length`。
59 | 如果`(size - length) / size`值最小, 既斜率最小, 则当前子元素文字最多。
60 | 计算方差`tempVariance`, 如果方差值>`100`(经验值),则表示所有子元素的差异很大,取最大的子元素重新计算。
61 | 否则表示这个父元素所有的子元素差异很小(如果是小说的话,每个段落不会差很多),将父元素作为正文返回。
62 |
--------------------------------------------------------------------------------
/src/api.ts:
--------------------------------------------------------------------------------
1 | import fetch, { RequestInit } from 'node-fetch'
2 | import { BookType } from './type'
3 | import { readConfig, SourceType } from './utils'
4 |
5 | const request = async (url: string, options: RequestInit, auth = true) => {
6 | const config = await readConfig()
7 | if (!config.baseUrl || !config.baseUrl.startsWith("http")) {
8 | throw new Error("请使用nvrd login -a 登录")
9 | }
10 | if (auth && !config.token) {
11 | throw new Error("请使用nvrd login登录")
12 | }
13 | if(auth) {
14 | if (!options.headers) {
15 | options.headers = {}
16 | }
17 | // @ts-ignore
18 | options.headers['Authorization'] = 'Bearer ' + config.token
19 | }
20 | // console.log(options)
21 | // console.log(options.method || "get", url, options)
22 |
23 | const baseUrl = config.baseUrl
24 | const res = await fetch(baseUrl + url, options).then(res => res.json())
25 | if(res.code === 200) {
26 | return res.data
27 | } else {
28 | const error = new Error(res.message)
29 | // @ts-ignore
30 | error.code = res.code
31 | throw error
32 | }
33 | }
34 |
35 | export const login = (data: { username: string, password: string }) => {
36 | return request('/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data)}, false)
37 | }
38 |
39 | export const checkLogin = () => {
40 | return request('/status', {})
41 | }
42 |
43 | export const getBooks = (): Promise => {
44 | return request('/books/list', {})
45 | }
46 |
47 | export const setBooks = (data: BookType[]) => {
48 | return request('/books/list', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data)})
49 | }
50 |
51 | export const syncBooks = (data: BookType[]) => {
52 | return request('/books/sync', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data)})
53 | }
54 |
55 | export const setBook = (data: BookType) => {
56 | return request('/books', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data)})
57 | }
58 |
59 | export const removeBook = (data: { name: string }) => {
60 | return request('/books', { method: 'DELETE', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data)})
61 | }
62 |
63 | export const getSources = (): Promise => {
64 | return request('/sources', {} )
65 | }
66 |
67 | export const setSources = (data: SourceType[]) => {
68 | return request('/sources', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data)})
69 | }
--------------------------------------------------------------------------------
/src/utils.ts:
--------------------------------------------------------------------------------
1 | import os from "os"
2 | import path from "path"
3 | import fs from 'fs'
4 | import { promisify } from 'util'
5 | import { newLineSplit } from "./constants"
6 | import mkdirp from 'mkdirp'
7 | import { ConfigType } from "./type"
8 |
9 | const writeFile = promisify(fs.writeFile)
10 | const readFile = promisify(fs.readFile)
11 |
12 | const nvrdDir = path.resolve(os.homedir(), ".nvrd")
13 |
14 | const historyFile = path.resolve(nvrdDir, ".novel_history")
15 |
16 | const sourcesFile = path.resolve(nvrdDir, "novel_sources.json")
17 |
18 | export type SourceType = {
19 | bookSourceUrl: string,
20 | ruleBookContent: string,
21 | ruleNextPage: string
22 | rulePrevPage: string
23 | ruleBookTitle: string
24 | }
25 |
26 | export async function readConfig(): Promise {
27 | if (!fs.existsSync(historyFile)) {
28 | await mkdirp(nvrdDir)
29 | await writeConfig({})
30 | return {}
31 | } else {
32 | try {
33 | const json = await readFile(historyFile, { encoding: 'utf-8'})
34 | return JSON.parse(json)
35 | } catch (e){
36 | return {}
37 | }
38 | }
39 | }
40 |
41 | export async function writeConfig(config: ConfigType) {
42 | return await writeFile(historyFile, JSON.stringify(config), { encoding: 'utf-8'})
43 | }
44 |
45 | export function writeConfigSync(config: ConfigType) {
46 | return fs.writeFileSync(historyFile, JSON.stringify(config), { encoding: 'utf-8'})
47 | }
48 |
49 | export function readSourcesSync(): SourceType[] {
50 | if (!fs.existsSync(sourcesFile)) {
51 | return []
52 | } else {
53 | try {
54 | const json = fs.readFileSync(sourcesFile, { encoding: 'utf-8'})
55 | return JSON.parse(json)
56 | } catch (e){
57 | return []
58 | }
59 | }
60 | }
61 |
62 | export async function readSources(): Promise {
63 | if (!fs.existsSync(sourcesFile)) {
64 | return []
65 | } else {
66 | try {
67 | const json = await readFile(sourcesFile, { encoding: 'utf-8'})
68 | return JSON.parse(json)
69 | } catch (e){
70 | return []
71 | }
72 | }
73 | }
74 |
75 | export async function writeSources(sources: SourceType[]) {
76 | return await writeFile(sourcesFile, JSON.stringify(sources), { encoding: 'utf-8'})
77 | }
78 |
79 | export function wordWrap(str: string, maxWidth: number) {
80 | const lines = str.split(newLineSplit)
81 | const newLines: string[] = []
82 | for (let line of lines) {
83 | if(line.length <= maxWidth) {
84 | if(line.trim()){
85 | newLines.push(line)
86 | }
87 | } else {
88 | do {
89 | newLines.push(line.slice(0, maxWidth))
90 | line = line.slice(maxWidth)
91 | } while(line.length > 0)
92 | }
93 | }
94 |
95 | // console.log(newLines)
96 |
97 | return newLines
98 | }
--------------------------------------------------------------------------------
/src/parser/general.ts:
--------------------------------------------------------------------------------
1 | import { IParser } from './index';
2 | import fetch from 'node-fetch';
3 | import cheerio from 'cheerio';
4 | import { handleTextNodes } from './parser';
5 | const gbk = require('gbk.js');
6 | const debug = require('debug')('parser')
7 |
8 | export class GeneralParser implements IParser {
9 |
10 | protected async fetchUrl(url: string) {
11 | const headers = {
12 | 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36'
13 | };
14 |
15 | return fetch(url, {
16 | headers
17 | })
18 | .then(res => res.buffer())
19 | .then(res => {
20 | if (res.includes('gbk') || res.includes('gb2312')) {
21 | return gbk.decode(res);
22 | }
23 | return res.toString('utf-8');
24 | });
25 | }
26 | /**
27 | * 解析内容
28 | * @param doc
29 | */
30 | protected async parseContent(doc: string) {
31 | const $ = cheerio.load(doc, { decodeEntities: false });
32 |
33 | function parseChildren(parent: cheerio.Cheerio, size: number, slope: number, variance: number): string {
34 | const children = parent.children()
35 | if(children.length === 1) {
36 | return parseChildren(children, size, slope, variance)
37 | }
38 | let maxChildren: { element: cheerio.Cheerio; size: number; slope: number; };
39 | const sizeList: number[] = [];
40 | children.each((index, element) => {
41 | const wrapperElement = $(element);
42 | const length = wrapperElement.text().length;
43 | sizeList.push(length);
44 | const tempSlop = (size - length) / size;
45 | if (!maxChildren) {
46 | maxChildren = {
47 | element: wrapperElement,
48 | size: length,
49 | slope: tempSlop
50 | };
51 | } else if (tempSlop < (maxChildren.slope)) { // 保存最小斜率
52 | maxChildren = {
53 | element: wrapperElement,
54 | size: length,
55 | slope: tempSlop
56 | };
57 | }
58 | });
59 | // debug(sizeList)
60 | let avg = sizeList.reduce((sum, x) => sum + x, 0) / sizeList.length;
61 | // 计算方差
62 | let tempVariance = Math.sqrt(sizeList.reduce((sum, x) => sum + (x - avg) ** 2, 0) / sizeList.length);
63 | // debug(avg, tempVariance)
64 | // @ts-ignore
65 | if (maxChildren) {
66 | // console.log('---', maxChildren.element.html(), maxChildren.element[0].name)
67 | debug(maxChildren.element.text(), maxChildren.slope, slope, tempVariance, variance)
68 | if (tempVariance > 100) {
69 | return parseChildren(maxChildren.element, maxChildren.size, maxChildren.slope, tempVariance);
70 | } else {
71 | return handleTextNodes(parent)
72 | }
73 | } else {
74 | return "";
75 | }
76 | }
77 |
78 | return parseChildren($('body'), $('body').text().length, 1, $('body').text().length);
79 | }
80 |
81 | protected async parseIndexChapter(doc: string) {
82 | const $ = cheerio.load(doc, { decodeEntities: false });
83 |
84 | const nextHref = await this.parseNextPage($)
85 | const prevHref = await this.parsePrevPage($)
86 | return { next: nextHref, prev: prevHref };
87 | }
88 |
89 | protected async parsePrevPage($: cheerio.Root) {
90 | const prevHref = $('body').find('a:contains("上一章")').attr("href") || $('body').find('a:contains("上一页")').attr("href");
91 | return prevHref
92 | }
93 |
94 | protected async parseNextPage($: cheerio.Root){
95 | const nextHref = $('body').find('a:contains("下一章")').attr("href") || $('body').find('a:contains("下一页")').attr("href");
96 | return nextHref
97 | }
98 |
99 | protected async parseTitle(doc: string) {
100 | const $ = cheerio.load(doc, { decodeEntities: false });
101 |
102 | return $('title').text();
103 | }
104 |
105 | parseNovel = async (url: string) => {
106 | // console.log('fetching...', url)
107 | const doc = await this.fetchUrl(url);
108 | const content = await this.parseContent(doc);
109 | const index = await this.parseIndexChapter(doc);
110 | const title = await this.parseTitle(doc);
111 | return { index, content, title };
112 | }
113 |
114 | }
--------------------------------------------------------------------------------
/src/parser/parser.ts:
--------------------------------------------------------------------------------
1 | import { scriptEndTag, scriptStartTag, newLineSplit } from "../constants"
2 | import cheerio from 'cheerio'
3 | import fetch from 'node-fetch'
4 |
5 |
6 | function parseRule (rule: string, $: cheerio.Cheerio, root: cheerio.Root): cheerio.Cheerio | string | Promise {
7 | if (!rule) return ''
8 |
9 | if (rule.startsWith(scriptStartTag) && rule.endsWith(scriptEndTag)) {
10 | return parseScript(rule.slice(scriptStartTag.length, rule.length - scriptEndTag.length), root)
11 | }
12 |
13 | const index = rule.indexOf("##")
14 | const parseRules = index === - 1 ? rule : rule.slice(0, index)
15 | const regexRule = index === - 1 ? "": rule.slice(index + 2)
16 |
17 | if(parseRules.includes("||") && parseRules.includes("&&")) {
18 | throw new Error("不允许同时存在&&和||规则")
19 | }
20 |
21 | if (rule.includes("&&")) {
22 | const parseRuleListAnd = parseRules.split("&&")
23 | const temp: string[] = []
24 | for (let parseRule of parseRuleListAnd) {
25 | const rules = parseRule.split('@')
26 | let ret: ReturnType = $
27 | rules.forEach((r) => {
28 | if(ret && typeof ret !== "string") {
29 | ret = simpleParser(r, ret)
30 | }
31 | })
32 | if (regexRule) {
33 | const regexRules = regexRule.split('|')
34 | regexRules.forEach(r => {
35 | if(typeof ret === 'string'){
36 | ret = parseRegex(ret, r)
37 | }
38 | })
39 | }
40 | if(ret && ret.length > 0) {
41 | temp.push(ret as any as string)
42 | }
43 | }
44 | return temp.join(newLineSplit)
45 | } else {
46 | const parseRuleListOr = parseRules.split("||")
47 | for (let parseRule of parseRuleListOr) {
48 | const rules = parseRule.split('@')
49 | let ret: ReturnType = $
50 | rules.forEach((r) => {
51 | if(ret && typeof ret !== "string") {
52 | ret = simpleParser(r, ret)
53 | }
54 | })
55 | if (regexRule) {
56 | const regexRules = regexRule.split('|')
57 | regexRules.forEach(r => {
58 | if(typeof ret === 'string'){
59 | ret = parseRegex(ret, r)
60 | }
61 | })
62 | }
63 | if(ret && ret.length > 0) {
64 | return ret as any as cheerio.Cheerio | string
65 | }
66 | }
67 | }
68 |
69 | return ''
70 | }
71 |
72 | function parseScript(script: string, $: cheerio.Root): Promise {
73 | const func = new Function("$", "fetch", "next", script)
74 | return new Promise((resovle) => func($, fetch, resovle))
75 | }
76 |
77 | function parseRegex (text: string, regex: string) {
78 | const reg = new RegExp(regex, "g")
79 | return text.replace(reg, '')
80 | }
81 |
82 | function selectorParse (rule: string[], $: cheerio.Cheerio, prefix = '') {
83 | const [tag, indexs] = rule[1].split('!')
84 | let indexList: number[] = []
85 | if(indexs) {
86 | indexList = indexs.split(":").map(x => Number(x));
87 | }
88 |
89 | $ = $.find(prefix + tag)
90 | if (indexs) {
91 | indexList = indexList.map(val => {
92 | if(val >= 0) {
93 | return val
94 | } else {
95 | return $.length + val
96 | }
97 | })
98 | $ = $.filter(i => {
99 | return !indexList.includes(i)
100 | })
101 | } else if (rule[2]) {
102 | $ = $.eq(Number(rule[2]))
103 | }
104 | return $
105 | }
106 |
107 | function simpleParser (rule: string, $: cheerio.Cheerio) {
108 | const rules = rule.split('.')
109 | let ret: cheerio.Cheerio | string | undefined | null
110 | switch (rules[0]) {
111 | case 'class':
112 | ret = selectorParse(rules, $, '.')
113 | break
114 | case 'id':
115 | ret = selectorParse(rules, $, '#')
116 | break
117 | case 'tag':
118 | ret = selectorParse(rules, $, '')
119 | break
120 | }
121 | if('cheerio' in $) {
122 | switch (rules[0]) {
123 | case 'class':
124 | case 'id':
125 | case 'tag':
126 | break
127 | case 'text':
128 | ret = $.text()
129 | break
130 | case 'href':
131 | case 'src':
132 | ret = $.attr(rules[0])
133 | break
134 | case 'html':
135 | ret = $.html()
136 | break
137 | case 'textNodes':
138 | {
139 | ret = handleTextNodes($)
140 | }
141 | break
142 | default:
143 | throw new Error('unkonw rule: ' + rule)
144 | }
145 | }
146 | return ret
147 | }
148 |
149 | function handleTextNodes(parent: cheerio.Cheerio){
150 | const temp: string[] = [];
151 | parent.contents().each((index, element) => {
152 | if (element.type === 'text') {
153 | const content = String.prototype.trim.apply(element.data);
154 | content && (temp.push(' ' + content));
155 | } else if(element.type === 'tag' && ["p"].includes(element.name)) {
156 | const content = String.prototype.trim.apply(cheerio(element).text());
157 | content && (temp.push(' ' + content));
158 | }
159 | });
160 | return temp.join(newLineSplit);
161 | }
162 |
163 | export {
164 | handleTextNodes,
165 | parseRule
166 | }
167 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Basic Options */
4 | "target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
5 | "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
6 | // "lib": [], /* Specify library files to be included in the compilation. */
7 | // "allowJs": true, /* Allow javascript files to be compiled. */
8 | // "checkJs": true, /* Report errors in .js files. */
9 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
10 | // "declaration": true, /* Generates corresponding '.d.ts' file. */
11 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
12 | // "sourceMap": true, /* Generates corresponding '.map' file. */
13 | // "outFile": "./", /* Concatenate and emit output to single file. */
14 | "outDir": "./dist", /* Redirect output structure to the directory. */
15 | "rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
16 | // "composite": true, /* Enable project compilation */
17 | // "removeComments": true, /* Do not emit comments to output. */
18 | // "noEmit": true, /* Do not emit outputs. */
19 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */
20 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
21 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
22 |
23 | /* Strict Type-Checking Options */
24 | "strict": true, /* Enable all strict type-checking options. */
25 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
26 | // "strictNullChecks": true, /* Enable strict null checks. */
27 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */
28 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
29 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
30 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
31 |
32 | /* Additional Checks */
33 | "noUnusedLocals": true, /* Report errors on unused locals. */
34 | // "noUnusedParameters": true, /* Report errors on unused parameters. */
35 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
36 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
37 |
38 | /* Module Resolution Options */
39 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
40 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
41 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
42 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
43 | // "typeRoots": [], /* List of folders to include type definitions from. */
44 | "types": [], /* Type declaration files to be included in compilation. */
45 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
46 | "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
47 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
48 |
49 | /* Source Map Options */
50 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
51 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
52 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
53 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
54 |
55 | /* Experimental Options */
56 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
57 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
58 | "skipLibCheck": true
59 | },
60 | "include": [
61 | "src/*"
62 | ],
63 | "exclude": [
64 | "node_modules",
65 | "./node_modules",
66 | "./node_modules/*",
67 | "./node_modules/@types/node/index.d.ts",
68 | ]
69 | }
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import Reader from "./reader"
2 | import inquirer from "inquirer";
3 | import { readConfig, readSources, writeConfig, writeSources } from "./utils";
4 | import db from './db'
5 | import { BookType, ConfigType } from "./type";
6 | import { checkLogin, getBooks, getSources, login, removeBook, setBooks, setSources, syncBooks } from "./api";
7 |
8 |
9 |
10 | export async function readAction(...rest: any[]) {
11 | let argv = rest[rest.length - 1]
12 | const url = rest[0] || ""
13 | if(url.startsWith("http")) {
14 | let line = 1
15 | if(argv.N > 1) {
16 | line = Number(argv.N)
17 | }
18 |
19 | return await read(url, line)
20 | } else {
21 | console.error("请输入正确的网址")
22 | }
23 | }
24 |
25 | async function read(url: string, line: number = 1, skipConfig = false) {
26 | let config = await readConfig()
27 |
28 | const questions: any[] = []
29 |
30 | // if (config.lastUrl) {
31 | // questions.push({
32 | // type: "confirm",
33 | // name: "continue",
34 | // message: "是否继续上次的阅读?"
35 | // })
36 | // }
37 |
38 | questions.push({
39 | type: "novel",
40 | name: "read novel",
41 | url,
42 | config: config,
43 | line: line ? line : config.line ? config.line : 1
44 | })
45 |
46 | inquirer.registerPrompt("novel", Reader)
47 | return inquirer.prompt(questions)
48 | }
49 |
50 | async function readFromBook(config: ConfigType, book: BookType, line: number = 1) {
51 | const questions: any[] = []
52 |
53 | // if (config.lastUrl) {
54 | // questions.push({
55 | // type: "confirm",
56 | // name: "continue",
57 | // message: "是否继续上次的阅读?"
58 | // })
59 | // }
60 |
61 | const { lastUrl, ...rest } = config
62 | questions.push({
63 | type: "novel",
64 | name: "read novel",
65 | url: book ? book.lastUrl : "",
66 | config: rest,
67 | book: book,
68 | line: line ? line : config.line ? config.line : 1
69 | })
70 |
71 | inquirer.registerPrompt("novel", Reader)
72 | return inquirer.prompt(questions)
73 | }
74 |
75 | export async function bookAction(...rest: any[]){
76 | let argv = rest[rest.length - 1]
77 | if(argv.list){
78 | let list = await db.books().find({}, { name: 1, lastUrl: 1, updatedAt: 1 })
79 | let newList = list.map(x => {
80 | const { _id, ...rest } = x
81 | return rest
82 | })
83 | console.table(newList)
84 | return
85 | }
86 |
87 | if(argv.add || argv['setUrl']) {
88 | const name = argv.add || argv['setUrl']
89 | if(rest.length !== 2) {
90 | console.error("请输入正确的参数")
91 | return
92 | }
93 | const url: string = rest[0] || ""
94 | if(!url.startsWith("http")){
95 | console.error("请输入正确的网址")
96 | return
97 | }
98 | let result = await db.books().findOne({ name: name })
99 | if (result) {
100 | await db.books().update({ _id: result._id }, { $set: { lastUrl: url } })
101 | } else {
102 | await db.books().insert({ name: name, lastUrl: url })
103 | }
104 | console.log(argv.add ? "添加成功":"更新成功")
105 | return
106 | }
107 |
108 | if(argv.read) {
109 | let line = 1
110 | if(argv.N > 1) {
111 | line = Number(argv.N)
112 | }
113 | let config = await readConfig()
114 | let books = await db.books().find({})
115 | if (config.token) {
116 | await checkLogin().catch(e => {
117 | if(e.code === 401) {
118 | delete config.token
119 | e.message = "登录已失效"
120 | }
121 | throw e
122 | })
123 | let data = await syncBooks(books as any) as (BookType & { _id: string, deleted: boolean, updatedAt: string })[]
124 |
125 | // 过滤出所有已删除的
126 | let deleteds = data.filter(x => x.deleted)
127 |
128 | // 过滤出所有未删除的
129 | data = data.filter(x => !x.deleted)
130 |
131 | // 需要添加的
132 | const inserts = data.filter(x => !books.some(b => b.name === x.name))
133 |
134 | // 需要更新的
135 | const updates = data.filter(x => {
136 | const book = books.find(b => b.name === x.name)
137 | return book && (new Date(x.updatedAt).getTime()) > (book.updatedAt?.getTime() ?? 0)
138 | })
139 |
140 | // const deletes = books.filter(b => !data.some(x => x.name === b.name))
141 |
142 | for (let book of inserts) {
143 | const { name, lastUrl } = book
144 | await db.books().insert({ name, lastUrl })
145 | }
146 |
147 |
148 | for (let book of updates) {
149 | const { _id, name, updatedAt, ...rest } = book
150 | await db.books().update({ name }, { $set: rest })
151 | }
152 |
153 | for(let book of deleteds) {
154 | await db.books().remove({ name: book.name }, {})
155 | }
156 |
157 | }
158 |
159 | const result = books.find(x => x.name == argv.read) as BookType
160 |
161 | await readFromBook(config, result, line)
162 | return
163 | }
164 |
165 | if(argv.remove) {
166 | await db.books().remove({ name: argv.remove }, {})
167 | let config = await readConfig()
168 | if (config.token) {
169 | await checkLogin().catch(e => {
170 | if(e.code === 401) {
171 | delete config.token
172 | e.message = "登录已失效"
173 | }
174 | throw e
175 | })
176 | await removeBook({ name: argv.remove })
177 | }
178 | console.error("删除成功")
179 | return
180 | }
181 |
182 | if(argv.up) {
183 | const books = await db.books().find({}) as BookType[]
184 | await setBooks(books)
185 | console.log("上传成功")
186 | return
187 | }
188 |
189 | if(argv.down) {
190 | const books = await getBooks()
191 | await db.books().remove({}, { multi: true })
192 | for(let book of books) {
193 | await db.books().insert(book)
194 | }
195 | console.log("下载成功")
196 | return
197 | }
198 |
199 | console.error("请输入正确的参数")
200 | }
201 |
202 | export async function sourceAction(...rest: any[]){
203 | let argv = rest[rest.length - 1]
204 |
205 | if(argv.up) {
206 | const sources = await readSources()
207 | await setSources(sources)
208 | console.log("上传成功")
209 | return
210 | }
211 |
212 | if(argv.down) {
213 | const sources = await getSources()
214 | await writeSources(sources)
215 | console.log("下载成功")
216 | return
217 | }
218 |
219 | }
220 |
221 | export async function configAction(...rest: any[]) {
222 | let argv = rest[rest.length - 1]
223 | if(argv.api && rest[0] && rest[0].startsWith('http')) {
224 | const config = await readConfig()
225 | config.baseUrl = rest[0]
226 | await writeConfig(config)
227 | console.log("保存api成功", rest[0])
228 | return
229 | }
230 |
231 | if(argv.up) {
232 | const books = await db.books().find({}) as BookType[]
233 | await setBooks(books)
234 | const sources = await readSources()
235 | const res = await setSources(sources)
236 | console.log("上传成功", res)
237 | return
238 | }
239 |
240 | if(argv.down) {
241 | const books = await getBooks()
242 | await db.books().remove({}, { multi: true })
243 | for(let book of books) {
244 | await db.books().insert(book)
245 | }
246 | const sources = await getSources()
247 | await writeSources(sources)
248 | console.log("下载成功")
249 | return
250 | }
251 |
252 | }
253 |
254 | export async function loginAction(...rest: any[]) {
255 | let argv = rest[rest.length - 1]
256 | const config = await readConfig()
257 |
258 | if(argv.api && rest[0] && rest[0].startsWith('http')) {
259 | config.baseUrl = rest[0]
260 | await writeConfig(config)
261 | }
262 |
263 | if(!config.baseUrl) {
264 | console.error("请使用nvrd login -a 登录")
265 | return
266 | }
267 |
268 | inquirer.prompt([
269 | { type: 'input',
270 | name: 'username',
271 | validate: val => !!String.prototype.trim.apply(val)
272 | },
273 | { type: 'password',
274 | name: 'password',
275 | validate: val => !!String.prototype.trim.apply(val)
276 | },
277 | ]).then(async (answers: { username: string, password: string }) => {
278 | const token = await login(answers)
279 | const config = await readConfig()
280 | config.token = token
281 | await writeConfig(config)
282 | console.log("登录成功")
283 | }).catch(e => {
284 | console.error(e.message ? e.message : e)
285 | })
286 | }
--------------------------------------------------------------------------------
/src/reader.ts:
--------------------------------------------------------------------------------
1 | import { ReadLine } from "readline";
2 | // import eol from 'eol'
3 | import Base from "inquirer/lib/prompts/base";
4 | import inquirer from "inquirer";
5 | import observe from "inquirer/lib/utils/events";
6 | import cliCursor from 'cli-cursor'
7 | import chalk from 'chalk'
8 | import { filter, share } from 'rxjs/operators'
9 | import { parseNovel } from "./parser";
10 | import { writeConfigSync, wordWrap } from "./utils";
11 | import ConfirmPrompt from "inquirer/lib/prompts/confirm";
12 | import { newLineSplit } from "./constants";
13 | import { BookType, ConfigType, DataStoreDocumentType } from "./type";
14 | import db from "./db";
15 | import ListPrompt from "inquirer/lib/prompts/list";
16 | import { setBook } from "./api";
17 | import { ParserReturnType } from "./parser/index";
18 | import CacheManager from "./cache";
19 |
20 | const HISTORY_STACK = 5
21 | export default class Reader extends Base{
22 | /** 阅读滚动行数 */
23 | private count = 0
24 | /** 显示行数 */
25 | private line = 1
26 | private lineNumber = 40
27 | private lines: string[] = []
28 | private url: string = ""
29 | private title: string = ""
30 | private loading = true
31 | private boss = false
32 | private config: ConfigType
33 | // @ts-ignore
34 | private firstRun = true
35 | private book: BookType & DataStoreDocumentType
36 | private cacheManageer: CacheManager;
37 |
38 | private list: (string | undefined)[] = new Array(HISTORY_STACK)
39 | private currentPos = -1
40 |
41 | setNext(url?: string){
42 | this.list[(this.currentPos + 1) % HISTORY_STACK] = url
43 | }
44 |
45 | next(){
46 | this.currentPos+=1
47 | return this.list[this.currentPos % HISTORY_STACK]
48 | }
49 |
50 | prev(){
51 | if(this.currentPos > 0){
52 | this.currentPos -= 1
53 | return this.list[this.currentPos % HISTORY_STACK]
54 | }
55 | }
56 |
57 | constructor(question: any, readLine: ReadLine, answers: inquirer.Answers) {
58 | super(question, readLine, answers)
59 |
60 | this.url = question.url
61 | this.line = question.line
62 | this.config = question.config
63 | // console.log(this.config)
64 | this.book = question.book
65 |
66 | this.cacheManageer = new CacheManager()
67 |
68 | // if (answers.continue === true && this.config.lastUrl) {
69 | // this.url = this.config.lastUrl
70 | // }
71 | }
72 |
73 | getConfirmPrompt() {
74 | const prompt = new ConfirmPrompt({
75 | type: "confirm",
76 | name: "continue",
77 | message: "是否继续上次的阅读?"
78 | }, this.rl, {});
79 |
80 | (prompt as any).onEnd = function onEnd(this: any, input: string) {
81 | this.status = 'answered';
82 |
83 | var output = this.opt.filter(input);
84 | // this.render(output);
85 |
86 | this.done(output);
87 | };
88 |
89 | (prompt as any).screen = this.screen
90 |
91 | return prompt
92 | }
93 |
94 | async getListPrompt() {
95 | const list = await db.books().find({})
96 | const prompt = new ListPrompt({
97 | type: "list",
98 | name: "chooseBook",
99 | message: "选择你要阅读的书籍?",
100 | choices: list as any
101 | }, this.rl, {});
102 |
103 | (prompt as any).onSubmit = function onSubmit(this: any, value: any) {
104 | this.status = 'answered';
105 | this.done(value);
106 | };
107 |
108 | (prompt as any).screen = this.screen
109 |
110 | return prompt
111 | }
112 |
113 | /**
114 | * Start the Inquiry session
115 | * @param {Function} cb Callback when prompt is done
116 | * @return {this}
117 | */
118 | async _run(cb: Function) {
119 |
120 | var events = observe(this.rl);
121 |
122 | events.normalizedUpKey
123 | .forEach(this.onUpKey.bind(this));
124 | events.normalizedDownKey
125 | .forEach(this.onDownKey.bind(this));
126 |
127 | events.keypress.pipe(
128 | filter(
129 | ({ key }) =>
130 | key.name === 'n'
131 | ),
132 | share()
133 | ).forEach(this.onPageNext.bind(this))
134 |
135 | events.keypress.pipe(
136 | filter(
137 | ({ key }) =>
138 | key.name === 'm'
139 | ),
140 | share()
141 | ).forEach(this.onPageUp.bind(this))
142 |
143 | events.keypress.pipe(
144 | filter(({ key }) => key && key.name === 'b'),
145 | share()
146 | ).forEach(this.onBossKey.bind(this))
147 |
148 | events.keypress.pipe(
149 | filter(({ key }) => key && key.name === 'r'),
150 | share()
151 | ).forEach(this.onRefresh.bind(this))
152 |
153 | events.keypress.pipe(
154 | filter(({ key }) => key && key.name === 'l'),
155 | share()
156 | ).forEach(this.onListBook.bind(this))
157 |
158 | const next = (cont: Boolean = false) => {
159 | if(cont && this.config.lastUrl) {
160 | this.url = this.config.lastUrl
161 | }
162 | // Init the prompt
163 | cliCursor.hide();
164 | this.setNext(this.url)
165 | this._read(this.next()!)
166 | }
167 | if (this.firstRun && this.config.lastUrl) {
168 | this.getConfirmPrompt().run().then((res: boolean) => {
169 | // Init the prompt
170 | next(res)
171 | })
172 | } else if (this.book) {
173 | next()
174 | } else {
175 | this.onListBook()
176 | }
177 | this.firstRun = false
178 | return this;
179 | }
180 |
181 | onListBook(){
182 | this.getListPrompt().then(res => res.run()).then(res => {
183 | return db.books().findOne({ name: res })
184 | }).then((res: any) => {
185 | if(res) {
186 | this.book = res as any
187 | return this._read(res.lastUrl)
188 | } else {
189 | console.error("未找到对应的书")
190 | process.exit()
191 | }
192 | })
193 | }
194 |
195 | onPageUp(){
196 | const prev = this.prev()
197 | if(prev){
198 | if(prev.startsWith("http")) {
199 | this._read(prev)
200 | } else {
201 | const urlObj = new URL(prev, this.url)
202 | this._read(urlObj.href)
203 | }
204 | } else {
205 | this.title = '没有上一页了'
206 | this.render()
207 | }
208 | }
209 |
210 | onUpKey() {
211 | if(this.loading) return
212 | if(this.isHead()) {
213 | this.onPageUp()
214 | } else {
215 | if(this.count < this.line) {
216 | this.count = 0
217 | } else {
218 | this.count -= this.line
219 | }
220 | this.render()
221 | }
222 | }
223 |
224 | getAbsoluteUrl(url: string) {
225 | if(url.startsWith("http")) {
226 | return url
227 | } else {
228 | const urlObj = new URL(url, this.url)
229 | return urlObj.href
230 | }
231 | }
232 |
233 | onPageNext(){
234 | const next = this.next()
235 | if(next){
236 | this._read(this.getAbsoluteUrl(next))
237 | } else {
238 | this.title = '没有下一页了'
239 | this.render()
240 | }
241 | }
242 |
243 | onDownKey() {
244 | if(this.loading) return
245 | if(!this.isEnd()) {
246 | this.count += this.line
247 | this.render()
248 | } else {
249 | this.onPageNext()
250 | }
251 | }
252 |
253 | onBossKey() {
254 | this.boss = !this.boss
255 | if(this.boss) {
256 | cliCursor.show()
257 | } else {
258 | cliCursor.hide()
259 | }
260 | this.render()
261 | }
262 |
263 | onRefresh(){
264 | if(!this.loading) {
265 | this._read(this.url)
266 | }
267 | }
268 |
269 |
270 |
271 | private parseNovel(url: string): Promise{
272 | let item = this.cacheManageer.getItem(url)
273 | if(item) {
274 | return item
275 | } else {
276 | let value = parseNovel(url)
277 | this.cacheManageer.setItem(url, value)
278 | return value
279 | }
280 | }
281 |
282 | private async _read(url: string){
283 | this.url = url
284 | this.loading = true
285 | this.render();
286 | const res = await this.parseNovel(url)
287 | if(res.index.next) {
288 | this.parseNovel(this.getAbsoluteUrl(res.index.next))
289 | }
290 | if (this.book) {
291 | // console.log("save books")
292 | db.books().update({ _id: this.book._id }, { $set: { lastUrl: url }})
293 | this.config.token && setBook({ name: this.book.name, lastUrl: url }).catch(e => {
294 | if(e.code === 401) {
295 | delete this.config.token
296 | } else {
297 | throw e
298 | }
299 | })
300 | }
301 | this.lines = wordWrap(res.content, this.lineNumber)
302 | // if (this.firstRun && (this.config.lastLine || 0) < this.lines.length) {
303 | // this.count = this.config.lastLine || 0
304 | // } else {
305 | this.count = 0
306 | // }
307 | this.setNext(res.index.next)
308 | this.title = res.title
309 | this.loading = false
310 | this.render()
311 | }
312 |
313 | render() {
314 | if(this.boss) {
315 | this.screen.render("shell>", "")
316 | } else {
317 | const currentLine = this.getCurrentLine()
318 | const lines = this.getRenderLines(currentLine)
319 | const progress = Math.round(this.lines.length ? ((currentLine) / this.lines.length * 100) : 100)
320 | const content = lines.length > 0 ? lines.join(newLineSplit) : this.loading ? '' : "没有内容"
321 | const title = this.loading ? '加载中...' : chalk.gray(this.title)
322 | this.screen.render(content,
323 | progress
324 | + '%\t'
325 | + currentLine
326 | + '/'
327 | + this.lines.length
328 | + '\t'
329 | + title)
330 | }
331 | }
332 |
333 | getRenderLines(currentLine: number){
334 | const lines = new Array(this.line).fill(" ")
335 | this.lines.slice(this.count, currentLine).forEach((value, index) => {
336 | lines[index] = value
337 | })
338 | return lines
339 | }
340 |
341 | getCurrentLine(){
342 | let currentLine = this.count + this.line
343 | if(currentLine > this.lines.length) {
344 | currentLine = this.lines.length
345 | }
346 | return currentLine
347 | }
348 |
349 | close(){
350 | super.close()
351 | writeConfigSync({ ...this.config, lastUrl: this.url, lastLine: this.count })
352 | }
353 |
354 | isEnd() {
355 | return (this.count + this.line) >= this.lines.length
356 | }
357 |
358 | isHead() {
359 | return this.count === 0
360 | }
361 | }
--------------------------------------------------------------------------------
/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 | "@sindresorhus/is@^0.14.0":
6 | version "0.14.0"
7 | resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea"
8 | integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==
9 |
10 | "@szmarczak/http-timer@^1.1.2":
11 | version "1.1.2"
12 | resolved "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421"
13 | integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==
14 | dependencies:
15 | defer-to-connect "^1.0.1"
16 |
17 | "@types/cheerio@^0.22.23":
18 | version "0.22.23"
19 | resolved "https://registry.npm.taobao.org/@types/cheerio/download/@types/cheerio-0.22.23.tgz?cache=0&sync_timestamp=1607401292950&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fcheerio%2Fdownload%2F%40types%2Fcheerio-0.22.23.tgz#74bcfee9c5ee53f619711dca953a89fe5cfa4eb4"
20 | integrity sha1-dLz+6cXuU/YZcR3KlTqJ/lz6TrQ=
21 | dependencies:
22 | "@types/node" "*"
23 |
24 | "@types/inquirer@^7.3.1":
25 | version "7.3.1"
26 | resolved "https://registry.npm.taobao.org/@types/inquirer/download/@types/inquirer-7.3.1.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Finquirer%2Fdownload%2F%40types%2Finquirer-7.3.1.tgz#1f231224e7df11ccfaf4cf9acbcc3b935fea292d"
27 | integrity sha1-HyMSJOffEcz69M+ay8w7k1/qKS0=
28 | dependencies:
29 | "@types/through" "*"
30 | rxjs "^6.4.0"
31 |
32 | "@types/mkdirp@^1.0.1":
33 | version "1.0.1"
34 | resolved "https://registry.npmjs.org/@types/mkdirp/-/mkdirp-1.0.1.tgz#0930b948914a78587de35458b86c907b6e98bbf6"
35 | integrity sha512-HkGSK7CGAXncr8Qn/0VqNtExEE+PHMWb+qlR1faHMao7ng6P3tAaoWWBMdva0gL5h4zprjIO89GJOLXsMcDm1Q==
36 | dependencies:
37 | "@types/node" "*"
38 |
39 | "@types/node-fetch@^2.5.7":
40 | version "2.5.7"
41 | resolved "https://registry.npm.taobao.org/@types/node-fetch/download/@types/node-fetch-2.5.7.tgz?cache=0&sync_timestamp=1605054865534&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fnode-fetch%2Fdownload%2F%40types%2Fnode-fetch-2.5.7.tgz#20a2afffa882ab04d44ca786449a276f9f6bbf3c"
42 | integrity sha1-IKKv/6iCqwTUTKeGRJonb59rvzw=
43 | dependencies:
44 | "@types/node" "*"
45 | form-data "^3.0.0"
46 |
47 | "@types/node@*":
48 | version "14.14.14"
49 | resolved "https://registry.npm.taobao.org/@types/node/download/@types/node-14.14.14.tgz?cache=0&sync_timestamp=1608047873667&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fnode%2Fdownload%2F%40types%2Fnode-14.14.14.tgz#f7fd5f3cc8521301119f63910f0fb965c7d761ae"
50 | integrity sha1-9/1fPMhSEwERn2ORDw+5ZcfXYa4=
51 |
52 | "@types/node@^10.12.10":
53 | version "10.17.49"
54 | resolved "https://registry.npm.taobao.org/@types/node/download/@types/node-10.17.49.tgz?cache=0&sync_timestamp=1608047873667&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fnode%2Fdownload%2F%40types%2Fnode-10.17.49.tgz#ecf0b67bab4b84d0ec9b0709db4aac3824a51c4a"
55 | integrity sha1-7PC2e6tLhNDsmwcJ20qsOCSlHEo=
56 |
57 | "@types/through@*":
58 | version "0.0.30"
59 | resolved "https://registry.npm.taobao.org/@types/through/download/@types/through-0.0.30.tgz?cache=0&sync_timestamp=1605057449889&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Fthrough%2Fdownload%2F%40types%2Fthrough-0.0.30.tgz#e0e42ce77e897bd6aead6f6ea62aeb135b8a3895"
60 | integrity sha1-4OQs536Je9aurW9upirrE1uKOJU=
61 | dependencies:
62 | "@types/node" "*"
63 |
64 | ansi-align@^3.0.0:
65 | version "3.0.0"
66 | resolved "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz#b536b371cf687caaef236c18d3e21fe3797467cb"
67 | integrity sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==
68 | dependencies:
69 | string-width "^3.0.0"
70 |
71 | ansi-escapes@^4.2.1:
72 | version "4.3.1"
73 | resolved "https://registry.npm.taobao.org/ansi-escapes/download/ansi-escapes-4.3.1.tgz?cache=0&sync_timestamp=1600349127942&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fansi-escapes%2Fdownload%2Fansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61"
74 | integrity sha1-pcR8xDGB8fOP/XB2g3cA05VSKmE=
75 | dependencies:
76 | type-fest "^0.11.0"
77 |
78 | ansi-regex@^4.1.0:
79 | version "4.1.0"
80 | resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997"
81 | integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==
82 |
83 | ansi-regex@^5.0.0:
84 | version "5.0.0"
85 | resolved "https://registry.npm.taobao.org/ansi-regex/download/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75"
86 | integrity sha1-OIU59VF5vzkznIGvMKZU1p+Hy3U=
87 |
88 | ansi-styles@^4.1.0:
89 | version "4.3.0"
90 | resolved "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-4.3.0.tgz?cache=0&sync_timestamp=1606792371412&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fansi-styles%2Fdownload%2Fansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
91 | integrity sha1-7dgDYornHATIWuegkG7a00tkiTc=
92 | dependencies:
93 | color-convert "^2.0.1"
94 |
95 | async@0.2.10:
96 | version "0.2.10"
97 | resolved "https://registry.npmjs.org/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1"
98 | integrity sha1-trvgsGdLnXGXCMo43owjfLUmw9E=
99 |
100 | asynckit@^0.4.0:
101 | version "0.4.0"
102 | resolved "https://registry.npm.taobao.org/asynckit/download/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
103 | integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
104 |
105 | binary-search-tree@0.2.5:
106 | version "0.2.5"
107 | resolved "https://registry.npmjs.org/binary-search-tree/-/binary-search-tree-0.2.5.tgz#7dbb3b210fdca082450dad2334c304af39bdc784"
108 | integrity sha1-fbs7IQ/coIJFDa0jNMMErzm9x4Q=
109 | dependencies:
110 | underscore "~1.4.4"
111 |
112 | boolbase@~1.0.0:
113 | version "1.0.0"
114 | resolved "https://registry.npm.taobao.org/boolbase/download/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
115 | integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24=
116 |
117 | boxen@^4.2.0:
118 | version "4.2.0"
119 | resolved "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz#e411b62357d6d6d36587c8ac3d5d974daa070e64"
120 | integrity sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==
121 | dependencies:
122 | ansi-align "^3.0.0"
123 | camelcase "^5.3.1"
124 | chalk "^3.0.0"
125 | cli-boxes "^2.2.0"
126 | string-width "^4.1.0"
127 | term-size "^2.1.0"
128 | type-fest "^0.8.1"
129 | widest-line "^3.1.0"
130 |
131 | cacheable-request@^6.0.0:
132 | version "6.1.0"
133 | resolved "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912"
134 | integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==
135 | dependencies:
136 | clone-response "^1.0.2"
137 | get-stream "^5.1.0"
138 | http-cache-semantics "^4.0.0"
139 | keyv "^3.0.0"
140 | lowercase-keys "^2.0.0"
141 | normalize-url "^4.1.0"
142 | responselike "^1.0.2"
143 |
144 | camelcase@^5.3.1:
145 | version "5.3.1"
146 | resolved "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
147 | integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
148 |
149 | chalk@^3.0.0:
150 | version "3.0.0"
151 | resolved "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4"
152 | integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==
153 | dependencies:
154 | ansi-styles "^4.1.0"
155 | supports-color "^7.1.0"
156 |
157 | chalk@^4.1.0:
158 | version "4.1.0"
159 | resolved "https://registry.npm.taobao.org/chalk/download/chalk-4.1.0.tgz?cache=0&sync_timestamp=1592843133653&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fchalk%2Fdownload%2Fchalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a"
160 | integrity sha1-ThSHCmGNni7dl92DRf2dncMVZGo=
161 | dependencies:
162 | ansi-styles "^4.1.0"
163 | supports-color "^7.1.0"
164 |
165 | chardet@^0.7.0:
166 | version "0.7.0"
167 | resolved "https://registry.npm.taobao.org/chardet/download/chardet-0.7.0.tgz?cache=0&sync_timestamp=1601032529880&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fchardet%2Fdownload%2Fchardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
168 | integrity sha1-kAlISfCTfy7twkJdDSip5fDLrZ4=
169 |
170 | cheerio@^1.0.0-rc.3:
171 | version "1.0.0-rc.3"
172 | resolved "https://registry.npm.taobao.org/cheerio/download/cheerio-1.0.0-rc.3.tgz#094636d425b2e9c0f4eb91a46c05630c9a1a8bf6"
173 | integrity sha1-CUY21CWy6cD065GkbAVjDJoai/Y=
174 | dependencies:
175 | css-select "~1.2.0"
176 | dom-serializer "~0.1.1"
177 | entities "~1.1.1"
178 | htmlparser2 "^3.9.1"
179 | lodash "^4.15.0"
180 | parse5 "^3.0.1"
181 |
182 | ci-info@^2.0.0:
183 | version "2.0.0"
184 | resolved "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46"
185 | integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==
186 |
187 | cli-boxes@^2.2.0:
188 | version "2.2.1"
189 | resolved "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f"
190 | integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==
191 |
192 | cli-cursor@^3.1.0:
193 | version "3.1.0"
194 | resolved "https://registry.npm.taobao.org/cli-cursor/download/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307"
195 | integrity sha1-JkMFp65JDR0Dvwybp8kl0XU68wc=
196 | dependencies:
197 | restore-cursor "^3.1.0"
198 |
199 | cli-width@^3.0.0:
200 | version "3.0.0"
201 | resolved "https://registry.npm.taobao.org/cli-width/download/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6"
202 | integrity sha1-ovSEN6LKqaIkNueUvwceyeYc7fY=
203 |
204 | clone-response@^1.0.2:
205 | version "1.0.2"
206 | resolved "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b"
207 | integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=
208 | dependencies:
209 | mimic-response "^1.0.0"
210 |
211 | color-convert@^2.0.1:
212 | version "2.0.1"
213 | resolved "https://registry.npm.taobao.org/color-convert/download/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
214 | integrity sha1-ctOmjVmMm9s68q0ehPIdiWq9TeM=
215 | dependencies:
216 | color-name "~1.1.4"
217 |
218 | color-name@~1.1.4:
219 | version "1.1.4"
220 | resolved "https://registry.npm.taobao.org/color-name/download/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
221 | integrity sha1-wqCah6y95pVD3m9j+jmVyCbFNqI=
222 |
223 | combined-stream@^1.0.8:
224 | version "1.0.8"
225 | resolved "https://registry.npm.taobao.org/combined-stream/download/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
226 | integrity sha1-w9RaizT9cwYxoRCoolIGgrMdWn8=
227 | dependencies:
228 | delayed-stream "~1.0.0"
229 |
230 | commander@^2.19.0:
231 | version "2.20.3"
232 | resolved "https://registry.npm.taobao.org/commander/download/commander-2.20.3.tgz?cache=0&sync_timestamp=1607931342826&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcommander%2Fdownload%2Fcommander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
233 | integrity sha1-/UhehMA+tIgcIHIrpIA16FMa6zM=
234 |
235 | configstore@^5.0.1:
236 | version "5.0.1"
237 | resolved "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96"
238 | integrity sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==
239 | dependencies:
240 | dot-prop "^5.2.0"
241 | graceful-fs "^4.1.2"
242 | make-dir "^3.0.0"
243 | unique-string "^2.0.0"
244 | write-file-atomic "^3.0.0"
245 | xdg-basedir "^4.0.0"
246 |
247 | crypto-random-string@^2.0.0:
248 | version "2.0.0"
249 | resolved "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5"
250 | integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==
251 |
252 | css-select@~1.2.0:
253 | version "1.2.0"
254 | resolved "https://registry.npm.taobao.org/css-select/download/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858"
255 | integrity sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=
256 | dependencies:
257 | boolbase "~1.0.0"
258 | css-what "2.1"
259 | domutils "1.5.1"
260 | nth-check "~1.0.1"
261 |
262 | css-what@2.1:
263 | version "2.1.3"
264 | resolved "https://registry.npm.taobao.org/css-what/download/css-what-2.1.3.tgz?cache=0&sync_timestamp=1602570934118&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcss-what%2Fdownload%2Fcss-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2"
265 | integrity sha1-ptdgRXM2X+dGhsPzEcVlE9iChfI=
266 |
267 | debug@^4.1.0:
268 | version "4.3.1"
269 | resolved "https://registry.npm.taobao.org/debug/download/debug-4.3.1.tgz?cache=0&sync_timestamp=1607566537361&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee"
270 | integrity sha1-8NIpxQXgxtjEmsVT0bE9wYP2su4=
271 | dependencies:
272 | ms "2.1.2"
273 |
274 | decompress-response@^3.3.0:
275 | version "3.3.0"
276 | resolved "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3"
277 | integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=
278 | dependencies:
279 | mimic-response "^1.0.0"
280 |
281 | deep-extend@^0.6.0:
282 | version "0.6.0"
283 | resolved "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
284 | integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==
285 |
286 | defer-to-connect@^1.0.1:
287 | version "1.1.3"
288 | resolved "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591"
289 | integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==
290 |
291 | delayed-stream@~1.0.0:
292 | version "1.0.0"
293 | resolved "https://registry.npm.taobao.org/delayed-stream/download/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
294 | integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
295 |
296 | dom-serializer@0:
297 | version "0.2.2"
298 | resolved "https://registry.npm.taobao.org/dom-serializer/download/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51"
299 | integrity sha1-GvuB9TNxcXXUeGVd68XjMtn5u1E=
300 | dependencies:
301 | domelementtype "^2.0.1"
302 | entities "^2.0.0"
303 |
304 | dom-serializer@~0.1.1:
305 | version "0.1.1"
306 | resolved "https://registry.npm.taobao.org/dom-serializer/download/dom-serializer-0.1.1.tgz#1ec4059e284babed36eec2941d4a970a189ce7c0"
307 | integrity sha1-HsQFnihLq+027sKUHUqXChic58A=
308 | dependencies:
309 | domelementtype "^1.3.0"
310 | entities "^1.1.1"
311 |
312 | domelementtype@1, domelementtype@^1.3.0, domelementtype@^1.3.1:
313 | version "1.3.1"
314 | resolved "https://registry.npm.taobao.org/domelementtype/download/domelementtype-1.3.1.tgz?cache=0&sync_timestamp=1606866123758&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdomelementtype%2Fdownload%2Fdomelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f"
315 | integrity sha1-0EjESzew0Qp/Kj1f7j9DM9eQSB8=
316 |
317 | domelementtype@^2.0.1:
318 | version "2.1.0"
319 | resolved "https://registry.npm.taobao.org/domelementtype/download/domelementtype-2.1.0.tgz?cache=0&sync_timestamp=1606866123758&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdomelementtype%2Fdownload%2Fdomelementtype-2.1.0.tgz#a851c080a6d1c3d94344aed151d99f669edf585e"
320 | integrity sha1-qFHAgKbRw9lDRK7RUdmfZp7fWF4=
321 |
322 | domhandler@^2.3.0:
323 | version "2.4.2"
324 | resolved "https://registry.npm.taobao.org/domhandler/download/domhandler-2.4.2.tgz?cache=0&sync_timestamp=1606872288592&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdomhandler%2Fdownload%2Fdomhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803"
325 | integrity sha1-iAUJfpM9ZehVRvcm1g9euItE+AM=
326 | dependencies:
327 | domelementtype "1"
328 |
329 | domutils@1.5.1:
330 | version "1.5.1"
331 | resolved "https://registry.npm.taobao.org/domutils/download/domutils-1.5.1.tgz?cache=0&sync_timestamp=1607393088815&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdomutils%2Fdownload%2Fdomutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf"
332 | integrity sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=
333 | dependencies:
334 | dom-serializer "0"
335 | domelementtype "1"
336 |
337 | domutils@^1.5.1:
338 | version "1.7.0"
339 | resolved "https://registry.npm.taobao.org/domutils/download/domutils-1.7.0.tgz?cache=0&sync_timestamp=1607393088815&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdomutils%2Fdownload%2Fdomutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a"
340 | integrity sha1-Vuo0HoNOBuZ0ivehyyXaZ+qfjCo=
341 | dependencies:
342 | dom-serializer "0"
343 | domelementtype "1"
344 |
345 | dot-prop@^5.2.0:
346 | version "5.3.0"
347 | resolved "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88"
348 | integrity sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==
349 | dependencies:
350 | is-obj "^2.0.0"
351 |
352 | duplexer3@^0.1.4:
353 | version "0.1.4"
354 | resolved "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2"
355 | integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=
356 |
357 | emoji-regex@^7.0.1:
358 | version "7.0.3"
359 | resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156"
360 | integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==
361 |
362 | emoji-regex@^8.0.0:
363 | version "8.0.0"
364 | resolved "https://registry.npm.taobao.org/emoji-regex/download/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
365 | integrity sha1-6Bj9ac5cz8tARZT4QpY79TFkzDc=
366 |
367 | end-of-stream@^1.1.0:
368 | version "1.4.4"
369 | resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
370 | integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
371 | dependencies:
372 | once "^1.4.0"
373 |
374 | entities@^1.1.1, entities@~1.1.1:
375 | version "1.1.2"
376 | resolved "https://registry.npm.taobao.org/entities/download/entities-1.1.2.tgz?cache=0&sync_timestamp=1602897079266&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fentities%2Fdownload%2Fentities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56"
377 | integrity sha1-vfpzUplmTfr9NFKe1PhSKidf6lY=
378 |
379 | entities@^2.0.0:
380 | version "2.1.0"
381 | resolved "https://registry.npm.taobao.org/entities/download/entities-2.1.0.tgz?cache=0&sync_timestamp=1602897079266&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fentities%2Fdownload%2Fentities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5"
382 | integrity sha1-mS0xKc999ocLlsV4WMJJoSD4uLU=
383 |
384 | eol@^0.9.1:
385 | version "0.9.1"
386 | resolved "https://registry.npm.taobao.org/eol/download/eol-0.9.1.tgz#f701912f504074be35c6117a5c4ade49cd547acd"
387 | integrity sha1-9wGRL1BAdL41xhF6XEreSc1Ues0=
388 |
389 | escape-goat@^2.0.0:
390 | version "2.1.1"
391 | resolved "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675"
392 | integrity sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==
393 |
394 | escape-string-regexp@^1.0.5:
395 | version "1.0.5"
396 | resolved "https://registry.npm.taobao.org/escape-string-regexp/download/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
397 | integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
398 |
399 | external-editor@^3.0.3:
400 | version "3.1.0"
401 | resolved "https://registry.npm.taobao.org/external-editor/download/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495"
402 | integrity sha1-ywP3QL764D6k0oPK7SdBqD8zVJU=
403 | dependencies:
404 | chardet "^0.7.0"
405 | iconv-lite "^0.4.24"
406 | tmp "^0.0.33"
407 |
408 | figures@^3.0.0:
409 | version "3.2.0"
410 | resolved "https://registry.npm.taobao.org/figures/download/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af"
411 | integrity sha1-YlwYvSk8YE3EqN2y/r8MiDQXRq8=
412 | dependencies:
413 | escape-string-regexp "^1.0.5"
414 |
415 | form-data@^3.0.0:
416 | version "3.0.0"
417 | resolved "https://registry.npm.taobao.org/form-data/download/form-data-3.0.0.tgz#31b7e39c85f1355b7139ee0c647cf0de7f83c682"
418 | integrity sha1-MbfjnIXxNVtxOe4MZHzw3n+DxoI=
419 | dependencies:
420 | asynckit "^0.4.0"
421 | combined-stream "^1.0.8"
422 | mime-types "^2.1.12"
423 |
424 | gbk.js@^0.3.0:
425 | version "0.3.0"
426 | resolved "https://registry.npm.taobao.org/gbk.js/download/gbk.js-0.3.0.tgz#1b62a0a3f137081c213905bfaee1b6abdac861bd"
427 | integrity sha1-G2Kgo/E3CBwhOQW/ruG2q9rIYb0=
428 |
429 | get-stream@^4.1.0:
430 | version "4.1.0"
431 | resolved "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5"
432 | integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==
433 | dependencies:
434 | pump "^3.0.0"
435 |
436 | get-stream@^5.1.0:
437 | version "5.2.0"
438 | resolved "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3"
439 | integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==
440 | dependencies:
441 | pump "^3.0.0"
442 |
443 | global-dirs@^2.0.1:
444 | version "2.1.0"
445 | resolved "https://registry.npmjs.org/global-dirs/-/global-dirs-2.1.0.tgz#e9046a49c806ff04d6c1825e196c8f0091e8df4d"
446 | integrity sha512-MG6kdOUh/xBnyo9cJFeIKkLEc1AyFq42QTU4XiX51i2NEdxLxLWXIjEjmqKeSuKR7pAZjTqUVoT2b2huxVLgYQ==
447 | dependencies:
448 | ini "1.3.7"
449 |
450 | got@^9.6.0:
451 | version "9.6.0"
452 | resolved "https://registry.npmjs.org/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85"
453 | integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==
454 | dependencies:
455 | "@sindresorhus/is" "^0.14.0"
456 | "@szmarczak/http-timer" "^1.1.2"
457 | cacheable-request "^6.0.0"
458 | decompress-response "^3.3.0"
459 | duplexer3 "^0.1.4"
460 | get-stream "^4.1.0"
461 | lowercase-keys "^1.0.1"
462 | mimic-response "^1.0.1"
463 | p-cancelable "^1.0.0"
464 | to-readable-stream "^1.0.0"
465 | url-parse-lax "^3.0.0"
466 |
467 | graceful-fs@^4.1.2:
468 | version "4.2.4"
469 | resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb"
470 | integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==
471 |
472 | has-flag@^4.0.0:
473 | version "4.0.0"
474 | resolved "https://registry.npm.taobao.org/has-flag/download/has-flag-4.0.0.tgz?cache=0&sync_timestamp=1577797756584&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fhas-flag%2Fdownload%2Fhas-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
475 | integrity sha1-lEdx/ZyByBJlxNaUGGDaBrtZR5s=
476 |
477 | has-yarn@^2.1.0:
478 | version "2.1.0"
479 | resolved "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77"
480 | integrity sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==
481 |
482 | htmlparser2@^3.9.1:
483 | version "3.10.1"
484 | resolved "https://registry.npm.taobao.org/htmlparser2/download/htmlparser2-3.10.1.tgz?cache=0&sync_timestamp=1607394271903&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fhtmlparser2%2Fdownload%2Fhtmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f"
485 | integrity sha1-vWedw/WYl7ajS7EHSchVu1OpOS8=
486 | dependencies:
487 | domelementtype "^1.3.1"
488 | domhandler "^2.3.0"
489 | domutils "^1.5.1"
490 | entities "^1.1.1"
491 | inherits "^2.0.1"
492 | readable-stream "^3.1.1"
493 |
494 | http-cache-semantics@^4.0.0:
495 | version "4.1.0"
496 | resolved "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390"
497 | integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==
498 |
499 | iconv-lite@^0.4.24:
500 | version "0.4.24"
501 | resolved "https://registry.npm.taobao.org/iconv-lite/download/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
502 | integrity sha1-ICK0sl+93CHS9SSXSkdKr+czkIs=
503 | dependencies:
504 | safer-buffer ">= 2.1.2 < 3"
505 |
506 | immediate@~3.0.5:
507 | version "3.0.6"
508 | resolved "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
509 | integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=
510 |
511 | import-lazy@^2.1.0:
512 | version "2.1.0"
513 | resolved "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43"
514 | integrity sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=
515 |
516 | imurmurhash@^0.1.4:
517 | version "0.1.4"
518 | resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
519 | integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
520 |
521 | inherits@^2.0.1, inherits@^2.0.3:
522 | version "2.0.4"
523 | resolved "https://registry.npm.taobao.org/inherits/download/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
524 | integrity sha1-D6LGT5MpF8NDOg3tVTY6rjdBa3w=
525 |
526 | ini@1.3.7:
527 | version "1.3.7"
528 | resolved "https://registry.npmjs.org/ini/-/ini-1.3.7.tgz#a09363e1911972ea16d7a8851005d84cf09a9a84"
529 | integrity sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ==
530 |
531 | ini@~1.3.0:
532 | version "1.3.8"
533 | resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c"
534 | integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==
535 |
536 | inquirer@^7.3.3:
537 | version "7.3.3"
538 | resolved "https://registry.npm.taobao.org/inquirer/download/inquirer-7.3.3.tgz?cache=0&sync_timestamp=1595475980671&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Finquirer%2Fdownload%2Finquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003"
539 | integrity sha1-BNF2sq8Er8FXqD/XwQDpjuCq0AM=
540 | dependencies:
541 | ansi-escapes "^4.2.1"
542 | chalk "^4.1.0"
543 | cli-cursor "^3.1.0"
544 | cli-width "^3.0.0"
545 | external-editor "^3.0.3"
546 | figures "^3.0.0"
547 | lodash "^4.17.19"
548 | mute-stream "0.0.8"
549 | run-async "^2.4.0"
550 | rxjs "^6.6.0"
551 | string-width "^4.1.0"
552 | strip-ansi "^6.0.0"
553 | through "^2.3.6"
554 |
555 | is-ci@^2.0.0:
556 | version "2.0.0"
557 | resolved "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c"
558 | integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==
559 | dependencies:
560 | ci-info "^2.0.0"
561 |
562 | is-fullwidth-code-point@^2.0.0:
563 | version "2.0.0"
564 | resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
565 | integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=
566 |
567 | is-fullwidth-code-point@^3.0.0:
568 | version "3.0.0"
569 | resolved "https://registry.npm.taobao.org/is-fullwidth-code-point/download/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
570 | integrity sha1-8Rb4Bk/pCz94RKOJl8C3UFEmnx0=
571 |
572 | is-installed-globally@^0.3.2:
573 | version "0.3.2"
574 | resolved "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz#fd3efa79ee670d1187233182d5b0a1dd00313141"
575 | integrity sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==
576 | dependencies:
577 | global-dirs "^2.0.1"
578 | is-path-inside "^3.0.1"
579 |
580 | is-npm@^5.0.0:
581 | version "5.0.0"
582 | resolved "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz#43e8d65cc56e1b67f8d47262cf667099193f45a8"
583 | integrity sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==
584 |
585 | is-obj@^2.0.0:
586 | version "2.0.0"
587 | resolved "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982"
588 | integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==
589 |
590 | is-path-inside@^3.0.1:
591 | version "3.0.2"
592 | resolved "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz#f5220fc82a3e233757291dddc9c5877f2a1f3017"
593 | integrity sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==
594 |
595 | is-typedarray@^1.0.0:
596 | version "1.0.0"
597 | resolved "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
598 | integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
599 |
600 | is-yarn-global@^0.3.0:
601 | version "0.3.0"
602 | resolved "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232"
603 | integrity sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==
604 |
605 | json-buffer@3.0.0:
606 | version "3.0.0"
607 | resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898"
608 | integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=
609 |
610 | keyv@^3.0.0:
611 | version "3.1.0"
612 | resolved "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9"
613 | integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==
614 | dependencies:
615 | json-buffer "3.0.0"
616 |
617 | latest-version@^5.1.0:
618 | version "5.1.0"
619 | resolved "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face"
620 | integrity sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==
621 | dependencies:
622 | package-json "^6.3.0"
623 |
624 | lie@3.1.1:
625 | version "3.1.1"
626 | resolved "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e"
627 | integrity sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=
628 | dependencies:
629 | immediate "~3.0.5"
630 |
631 | localforage@^1.3.0:
632 | version "1.9.0"
633 | resolved "https://registry.npmjs.org/localforage/-/localforage-1.9.0.tgz#f3e4d32a8300b362b4634cc4e066d9d00d2f09d1"
634 | integrity sha512-rR1oyNrKulpe+VM9cYmcFn6tsHuokyVHFaCM3+osEmxaHTbEk8oQu6eGDfS6DQLWi/N67XRmB8ECG37OES368g==
635 | dependencies:
636 | lie "3.1.1"
637 |
638 | lodash@^4.15.0, lodash@^4.17.19:
639 | version "4.17.20"
640 | resolved "https://registry.npm.taobao.org/lodash/download/lodash-4.17.20.tgz?cache=0&sync_timestamp=1597336082988&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Flodash%2Fdownload%2Flodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52"
641 | integrity sha1-tEqbYpe8tpjxxRo1RaKzs2jVnFI=
642 |
643 | lowercase-keys@^1.0.0, lowercase-keys@^1.0.1:
644 | version "1.0.1"
645 | resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f"
646 | integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==
647 |
648 | lowercase-keys@^2.0.0:
649 | version "2.0.0"
650 | resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479"
651 | integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==
652 |
653 | lru-cache@^6.0.0:
654 | version "6.0.0"
655 | resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
656 | integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
657 | dependencies:
658 | yallist "^4.0.0"
659 |
660 | make-dir@^3.0.0:
661 | version "3.1.0"
662 | resolved "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
663 | integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==
664 | dependencies:
665 | semver "^6.0.0"
666 |
667 | mime-db@1.44.0:
668 | version "1.44.0"
669 | resolved "https://registry.npm.taobao.org/mime-db/download/mime-db-1.44.0.tgz?cache=0&sync_timestamp=1600831117178&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmime-db%2Fdownload%2Fmime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92"
670 | integrity sha1-+hHF6wrKEzS0Izy01S8QxaYnL5I=
671 |
672 | mime-types@^2.1.12:
673 | version "2.1.27"
674 | resolved "https://registry.npm.taobao.org/mime-types/download/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f"
675 | integrity sha1-R5SfmOJ56lMRn1ci4PNOUpvsAJ8=
676 | dependencies:
677 | mime-db "1.44.0"
678 |
679 | mimic-fn@^2.1.0:
680 | version "2.1.0"
681 | resolved "https://registry.npm.taobao.org/mimic-fn/download/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
682 | integrity sha1-ftLCzMyvhNP/y3pptXcR/CCDQBs=
683 |
684 | mimic-response@^1.0.0, mimic-response@^1.0.1:
685 | version "1.0.1"
686 | resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b"
687 | integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==
688 |
689 | minimist@^1.2.0, minimist@^1.2.5:
690 | version "1.2.5"
691 | resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
692 | integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
693 |
694 | mkdirp@^1.0.4:
695 | version "1.0.4"
696 | resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
697 | integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
698 |
699 | mkdirp@~0.5.1:
700 | version "0.5.5"
701 | resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
702 | integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
703 | dependencies:
704 | minimist "^1.2.5"
705 |
706 | ms@2.1.2:
707 | version "2.1.2"
708 | resolved "https://registry.npm.taobao.org/ms/download/ms-2.1.2.tgz?cache=0&sync_timestamp=1607433899126&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fms%2Fdownload%2Fms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
709 | integrity sha1-0J0fNXtEP0kzgqjrPM0YOHKuYAk=
710 |
711 | mute-stream@0.0.8:
712 | version "0.0.8"
713 | resolved "https://registry.npm.taobao.org/mute-stream/download/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d"
714 | integrity sha1-FjDEKyJR/4HiooPelqVJfqkuXg0=
715 |
716 | nedb-promises@^4.1.0:
717 | version "4.1.0"
718 | resolved "https://registry.npmjs.org/nedb-promises/-/nedb-promises-4.1.0.tgz#b0700aae6c72b7c1ba696da618cc140a1520c078"
719 | integrity sha512-nTdx7jX/Vu24L05Cy0ee7CL3L4SEHCb1jlLlegPl0VlE8jsUXgnSyNOjq3FEc3cdUSDK05X7hSzb3+a07PigmQ==
720 | dependencies:
721 | nedb "^1.8.0"
722 |
723 | nedb@^1.8.0:
724 | version "1.8.0"
725 | resolved "https://registry.npmjs.org/nedb/-/nedb-1.8.0.tgz#0e3502cd82c004d5355a43c9e55577bd7bd91d88"
726 | integrity sha1-DjUCzYLABNU1WkPJ5VV3vXvZHYg=
727 | dependencies:
728 | async "0.2.10"
729 | binary-search-tree "0.2.5"
730 | localforage "^1.3.0"
731 | mkdirp "~0.5.1"
732 | underscore "~1.4.4"
733 |
734 | node-fetch@^2.6.1:
735 | version "2.6.1"
736 | resolved "https://registry.npm.taobao.org/node-fetch/download/node-fetch-2.6.1.tgz?cache=0&sync_timestamp=1599309206591&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fnode-fetch%2Fdownload%2Fnode-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
737 | integrity sha1-BFvTI2Mfdu0uK1VXM5RBa2OaAFI=
738 |
739 | normalize-url@^4.1.0:
740 | version "4.5.0"
741 | resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129"
742 | integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==
743 |
744 | nth-check@~1.0.1:
745 | version "1.0.2"
746 | resolved "https://registry.npm.taobao.org/nth-check/download/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c"
747 | integrity sha1-sr0pXDfj3VijvwcAN2Zjuk2c8Fw=
748 | dependencies:
749 | boolbase "~1.0.0"
750 |
751 | once@^1.3.1, once@^1.4.0:
752 | version "1.4.0"
753 | resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
754 | integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
755 | dependencies:
756 | wrappy "1"
757 |
758 | onetime@^5.1.0:
759 | version "5.1.2"
760 | resolved "https://registry.npm.taobao.org/onetime/download/onetime-5.1.2.tgz?cache=0&sync_timestamp=1597003951681&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fonetime%2Fdownload%2Fonetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e"
761 | integrity sha1-0Oluu1awdHbfHdnEgG5SN5hcpF4=
762 | dependencies:
763 | mimic-fn "^2.1.0"
764 |
765 | os-tmpdir@~1.0.2:
766 | version "1.0.2"
767 | resolved "https://registry.npm.taobao.org/os-tmpdir/download/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
768 | integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=
769 |
770 | p-cancelable@^1.0.0:
771 | version "1.1.0"
772 | resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc"
773 | integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==
774 |
775 | package-json@^6.3.0:
776 | version "6.5.0"
777 | resolved "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0"
778 | integrity sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==
779 | dependencies:
780 | got "^9.6.0"
781 | registry-auth-token "^4.0.0"
782 | registry-url "^5.0.0"
783 | semver "^6.2.0"
784 |
785 | parse5@^3.0.1:
786 | version "3.0.3"
787 | resolved "https://registry.npm.taobao.org/parse5/download/parse5-3.0.3.tgz?cache=0&sync_timestamp=1595850971402&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fparse5%2Fdownload%2Fparse5-3.0.3.tgz#042f792ffdd36851551cf4e9e066b3874ab45b5c"
788 | integrity sha1-BC95L/3TaFFVHPTp4Gazh0q0W1w=
789 | dependencies:
790 | "@types/node" "*"
791 |
792 | prepend-http@^2.0.0:
793 | version "2.0.0"
794 | resolved "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
795 | integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=
796 |
797 | pump@^3.0.0:
798 | version "3.0.0"
799 | resolved "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
800 | integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==
801 | dependencies:
802 | end-of-stream "^1.1.0"
803 | once "^1.3.1"
804 |
805 | pupa@^2.1.1:
806 | version "2.1.1"
807 | resolved "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz#f5e8fd4afc2c5d97828faa523549ed8744a20d62"
808 | integrity sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==
809 | dependencies:
810 | escape-goat "^2.0.0"
811 |
812 | rc@^1.2.8:
813 | version "1.2.8"
814 | resolved "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
815 | integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==
816 | dependencies:
817 | deep-extend "^0.6.0"
818 | ini "~1.3.0"
819 | minimist "^1.2.0"
820 | strip-json-comments "~2.0.1"
821 |
822 | readable-stream@^3.1.1:
823 | version "3.6.0"
824 | resolved "https://registry.npm.taobao.org/readable-stream/download/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
825 | integrity sha1-M3u9o63AcGvT4CRCaihtS0sskZg=
826 | dependencies:
827 | inherits "^2.0.3"
828 | string_decoder "^1.1.1"
829 | util-deprecate "^1.0.1"
830 |
831 | registry-auth-token@^4.0.0:
832 | version "4.2.1"
833 | resolved "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz#6d7b4006441918972ccd5fedcd41dc322c79b250"
834 | integrity sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==
835 | dependencies:
836 | rc "^1.2.8"
837 |
838 | registry-url@^5.0.0:
839 | version "5.1.0"
840 | resolved "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz#e98334b50d5434b81136b44ec638d9c2009c5009"
841 | integrity sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==
842 | dependencies:
843 | rc "^1.2.8"
844 |
845 | responselike@^1.0.2:
846 | version "1.0.2"
847 | resolved "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7"
848 | integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=
849 | dependencies:
850 | lowercase-keys "^1.0.0"
851 |
852 | restore-cursor@^3.1.0:
853 | version "3.1.0"
854 | resolved "https://registry.npm.taobao.org/restore-cursor/download/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e"
855 | integrity sha1-OfZ8VLOnpYzqUjbZXPADQjljH34=
856 | dependencies:
857 | onetime "^5.1.0"
858 | signal-exit "^3.0.2"
859 |
860 | run-async@^2.4.0:
861 | version "2.4.1"
862 | resolved "https://registry.npm.taobao.org/run-async/download/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455"
863 | integrity sha1-hEDsz5nqPnC9QJ1JqriOEMGJpFU=
864 |
865 | rxjs@^6.4.0, rxjs@^6.6.0, rxjs@^6.6.3:
866 | version "6.6.3"
867 | resolved "https://registry.npm.taobao.org/rxjs/download/rxjs-6.6.3.tgz?cache=0&sync_timestamp=1607305607879&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Frxjs%2Fdownload%2Frxjs-6.6.3.tgz#8ca84635c4daa900c0d3967a6ee7ac60271ee552"
868 | integrity sha1-jKhGNcTaqQDA05Z6buesYCce5VI=
869 | dependencies:
870 | tslib "^1.9.0"
871 |
872 | safe-buffer@~5.2.0:
873 | version "5.2.1"
874 | resolved "https://registry.npm.taobao.org/safe-buffer/download/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
875 | integrity sha1-Hq+fqb2x/dTsdfWPnNtOa3gn7sY=
876 |
877 | "safer-buffer@>= 2.1.2 < 3":
878 | version "2.1.2"
879 | resolved "https://registry.npm.taobao.org/safer-buffer/download/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
880 | integrity sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=
881 |
882 | semver-diff@^3.1.1:
883 | version "3.1.1"
884 | resolved "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz#05f77ce59f325e00e2706afd67bb506ddb1ca32b"
885 | integrity sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==
886 | dependencies:
887 | semver "^6.3.0"
888 |
889 | semver@^6.0.0, semver@^6.2.0, semver@^6.3.0:
890 | version "6.3.0"
891 | resolved "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
892 | integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
893 |
894 | semver@^7.3.2:
895 | version "7.3.4"
896 | resolved "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97"
897 | integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==
898 | dependencies:
899 | lru-cache "^6.0.0"
900 |
901 | signal-exit@^3.0.2:
902 | version "3.0.3"
903 | resolved "https://registry.npm.taobao.org/signal-exit/download/signal-exit-3.0.3.tgz?cache=0&sync_timestamp=1592843131591&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsignal-exit%2Fdownload%2Fsignal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
904 | integrity sha1-oUEMLt2PB3sItOJTyOrPyvBXRhw=
905 |
906 | string-width@^3.0.0:
907 | version "3.1.0"
908 | resolved "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961"
909 | integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==
910 | dependencies:
911 | emoji-regex "^7.0.1"
912 | is-fullwidth-code-point "^2.0.0"
913 | strip-ansi "^5.1.0"
914 |
915 | string-width@^4.0.0, string-width@^4.1.0:
916 | version "4.2.0"
917 | resolved "https://registry.npm.taobao.org/string-width/download/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5"
918 | integrity sha1-lSGCxGzHssMT0VluYjmSvRY7crU=
919 | dependencies:
920 | emoji-regex "^8.0.0"
921 | is-fullwidth-code-point "^3.0.0"
922 | strip-ansi "^6.0.0"
923 |
924 | string_decoder@^1.1.1:
925 | version "1.3.0"
926 | resolved "https://registry.npm.taobao.org/string_decoder/download/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
927 | integrity sha1-QvEUWUpGzxqOMLCoT1bHjD7awh4=
928 | dependencies:
929 | safe-buffer "~5.2.0"
930 |
931 | strip-ansi@^5.1.0:
932 | version "5.2.0"
933 | resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae"
934 | integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==
935 | dependencies:
936 | ansi-regex "^4.1.0"
937 |
938 | strip-ansi@^6.0.0:
939 | version "6.0.0"
940 | resolved "https://registry.npm.taobao.org/strip-ansi/download/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532"
941 | integrity sha1-CxVx3XZpzNTz4G4U7x7tJiJa5TI=
942 | dependencies:
943 | ansi-regex "^5.0.0"
944 |
945 | strip-json-comments@~2.0.1:
946 | version "2.0.1"
947 | resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
948 | integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
949 |
950 | supports-color@^7.1.0:
951 | version "7.2.0"
952 | resolved "https://registry.npm.taobao.org/supports-color/download/supports-color-7.2.0.tgz?cache=0&sync_timestamp=1608033349725&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
953 | integrity sha1-G33NyzK4E4gBs+R4umpRyqiWSNo=
954 | dependencies:
955 | has-flag "^4.0.0"
956 |
957 | term-size@^2.1.0:
958 | version "2.2.1"
959 | resolved "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz#2a6a54840432c2fb6320fea0f415531e90189f54"
960 | integrity sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==
961 |
962 | through@^2.3.6:
963 | version "2.3.8"
964 | resolved "https://registry.npm.taobao.org/through/download/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
965 | integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
966 |
967 | tmp@^0.0.33:
968 | version "0.0.33"
969 | resolved "https://registry.npm.taobao.org/tmp/download/tmp-0.0.33.tgz?cache=0&sync_timestamp=1592843137359&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ftmp%2Fdownload%2Ftmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
970 | integrity sha1-bTQzWIl2jSGyvNoKonfO07G/rfk=
971 | dependencies:
972 | os-tmpdir "~1.0.2"
973 |
974 | to-readable-stream@^1.0.0:
975 | version "1.0.0"
976 | resolved "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771"
977 | integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==
978 |
979 | tslib@^1.9.0:
980 | version "1.14.1"
981 | resolved "https://registry.npm.taobao.org/tslib/download/tslib-1.14.1.tgz?cache=0&sync_timestamp=1602286854330&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ftslib%2Fdownload%2Ftslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
982 | integrity sha1-zy04vcNKE0vK8QkcQfZhni9nLQA=
983 |
984 | type-fest@^0.11.0:
985 | version "0.11.0"
986 | resolved "https://registry.npm.taobao.org/type-fest/download/type-fest-0.11.0.tgz?cache=0&sync_timestamp=1606468804579&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ftype-fest%2Fdownload%2Ftype-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1"
987 | integrity sha1-l6vwhyMQ/tiKXEZrJWgVdhReM/E=
988 |
989 | type-fest@^0.8.1:
990 | version "0.8.1"
991 | resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
992 | integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==
993 |
994 | typedarray-to-buffer@^3.1.5:
995 | version "3.1.5"
996 | resolved "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080"
997 | integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==
998 | dependencies:
999 | is-typedarray "^1.0.0"
1000 |
1001 | typescript@^4.1.3:
1002 | version "4.1.3"
1003 | resolved "https://registry.npm.taobao.org/typescript/download/typescript-4.1.3.tgz?cache=0&sync_timestamp=1607912478239&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Ftypescript%2Fdownload%2Ftypescript-4.1.3.tgz#519d582bd94cba0cf8934c7d8e8467e473f53bb7"
1004 | integrity sha1-UZ1YK9lMugz4k0x9joRn5HP1O7c=
1005 |
1006 | underscore@~1.4.4:
1007 | version "1.4.4"
1008 | resolved "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz#61a6a32010622afa07963bf325203cf12239d604"
1009 | integrity sha1-YaajIBBiKvoHljvzJSA88SI51gQ=
1010 |
1011 | unique-string@^2.0.0:
1012 | version "2.0.0"
1013 | resolved "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d"
1014 | integrity sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==
1015 | dependencies:
1016 | crypto-random-string "^2.0.0"
1017 |
1018 | update-notifier@^5.0.1:
1019 | version "5.0.1"
1020 | resolved "https://registry.npmjs.org/update-notifier/-/update-notifier-5.0.1.tgz#1f92d45fb1f70b9e33880a72dd262bc12d22c20d"
1021 | integrity sha512-BuVpRdlwxeIOvmc32AGYvO1KVdPlsmqSh8KDDBxS6kDE5VR7R8OMP1d8MdhaVBvxl4H3551k9akXr0Y1iIB2Wg==
1022 | dependencies:
1023 | boxen "^4.2.0"
1024 | chalk "^4.1.0"
1025 | configstore "^5.0.1"
1026 | has-yarn "^2.1.0"
1027 | import-lazy "^2.1.0"
1028 | is-ci "^2.0.0"
1029 | is-installed-globally "^0.3.2"
1030 | is-npm "^5.0.0"
1031 | is-yarn-global "^0.3.0"
1032 | latest-version "^5.1.0"
1033 | pupa "^2.1.1"
1034 | semver "^7.3.2"
1035 | semver-diff "^3.1.1"
1036 | xdg-basedir "^4.0.0"
1037 |
1038 | url-parse-lax@^3.0.0:
1039 | version "3.0.0"
1040 | resolved "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c"
1041 | integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=
1042 | dependencies:
1043 | prepend-http "^2.0.0"
1044 |
1045 | util-deprecate@^1.0.1:
1046 | version "1.0.2"
1047 | resolved "https://registry.npm.taobao.org/util-deprecate/download/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
1048 | integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
1049 |
1050 | widest-line@^3.1.0:
1051 | version "3.1.0"
1052 | resolved "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca"
1053 | integrity sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==
1054 | dependencies:
1055 | string-width "^4.0.0"
1056 |
1057 | wrappy@1:
1058 | version "1.0.2"
1059 | resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
1060 | integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
1061 |
1062 | write-file-atomic@^3.0.0:
1063 | version "3.0.3"
1064 | resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8"
1065 | integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==
1066 | dependencies:
1067 | imurmurhash "^0.1.4"
1068 | is-typedarray "^1.0.0"
1069 | signal-exit "^3.0.2"
1070 | typedarray-to-buffer "^3.1.5"
1071 |
1072 | xdg-basedir@^4.0.0:
1073 | version "4.0.0"
1074 | resolved "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13"
1075 | integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==
1076 |
1077 | yallist@^4.0.0:
1078 | version "4.0.0"
1079 | resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
1080 | integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
1081 |
--------------------------------------------------------------------------------