Full Code of ulivz/vuepress-plugin-yuque for AI

master 0333ed30aaa3 cached
28 files
38.8 KB
13.1k tokens
48 symbols
1 requests
Download .txt
Repository: ulivz/vuepress-plugin-yuque
Branch: master
Commit: 0333ed30aaa3
Files: 28
Total size: 38.8 KB

Directory structure:
gitextract__k5qwlck/

├── .editorconfig
├── .gitattributes
├── .github/
│   └── FUNDING.yml
├── .gitignore
├── .release-it.json
├── CHANGELOG.md
├── LICENSE
├── README.md
├── docs/
│   └── .vuepress/
│       ├── config.js
│       └── styles/
│           └── index.styl
├── example/
│   └── .vuepress/
│       └── config.js
├── lib/
│   ├── __test__/
│   │   ├── __snapshots__/
│   │   │   └── toc.test.js.snap
│   │   ├── toc.constant.js
│   │   └── toc.test.js
│   ├── client.js
│   ├── compose.js
│   ├── constant.js
│   ├── fetch.js
│   ├── html.js
│   ├── index.js
│   ├── spinner.js
│   ├── stack.js
│   ├── store.js
│   ├── style.styl
│   ├── toc.js
│   └── yuque.js
├── package.json
└── test/
    └── index.test.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .editorconfig
================================================
root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.md]
trim_trailing_whitespace = false

================================================
FILE: .gitattributes
================================================
* text=auto


================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms

github: ulivz # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: ulivz # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with a single custom sponsorship URL


================================================
FILE: .gitignore
================================================
node_modules
.temp*
docs2
dist


================================================
FILE: .release-it.json
================================================
{
  "src": {
    "tagName": "v%s",
    "commitMessage": "%s"
  },
  "increment": "conventional:angular",
  "beforeChangelogCommand": "conventional-changelog -p angular -i CHANGELOG.md -s",
  "changelogCommand": "conventional-changelog -p angular | tail -n +3",
  "safeBump": false
}


================================================
FILE: CHANGELOG.md
================================================
## [0.6.1](https://github.com/ulivz/vuepress-plugin-yuque/compare/v0.6.0...v0.6.1) (2020-01-15)


### Bug Fixes

* 1. fail to parse pre tag, may cause code loss issues; 2.transformheander should ignore empty string ([3726743](https://github.com/ulivz/vuepress-plugin-yuque/commit/3726743f97c722706e66d0a28048eda231102159))
* re-instantiate when repoId has been changed ([565a347](https://github.com/ulivz/vuepress-plugin-yuque/commit/565a347039cad6ab13d94ab069025de319e40da8))



<a name="0.6.0"></a>
# [0.6.0](https://github.com/ulivz/vuepress-plugin-yuque/compare/v0.5.5...v0.6.0) (2019-04-05)


### Features

* yuqueLink & yuqueLinkHtml option ([b3d7d06](https://github.com/ulivz/vuepress-plugin-yuque/commit/b3d7d06))



<a name="0.5.5"></a>
## [0.5.5](https://github.com/ulivz/vuepress-plugin-yuque/compare/v0.5.4...v0.5.5) (2019-04-05)


### Bug Fixes

* typo ([2a0b86b](https://github.com/ulivz/vuepress-plugin-yuque/commit/2a0b86b))



<a name="0.5.4"></a>
## [0.5.4](https://github.com/ulivz/vuepress-plugin-yuque/compare/v0.5.3...v0.5.4) (2019-04-05)


### Bug Fixes

* ## ... shouldn't be considered as title ([f30c893](https://github.com/ulivz/vuepress-plugin-yuque/commit/f30c893))



<a name="0.5.3"></a>
## [0.5.3](https://github.com/ulivz/vuepress-plugin-yuque/compare/v0.5.2...v0.5.3) (2019-04-05)


### Bug Fixes

* **html:** table's styles cannot be removed ([1035eb3](https://github.com/ulivz/vuepress-plugin-yuque/commit/1035eb3))



<a name="0.5.2"></a>
## [0.5.2](https://github.com/ulivz/vuepress-plugin-yuque/compare/v0.5.1...v0.5.2) (2019-04-05)


### Bug Fixes

* **html:** need to escape twice ([923b31a](https://github.com/ulivz/vuepress-plugin-yuque/commit/923b31a))



<a name="0.5.1"></a>
## [0.5.1](https://github.com/ulivz/vuepress-plugin-yuque/compare/v0.5.0...v0.5.1) (2019-04-05)


### Bug Fixes

* **html:** regression of escape html in code ([8f638e3](https://github.com/ulivz/vuepress-plugin-yuque/commit/8f638e3))



<a name="0.5.0"></a>
# [0.5.0](https://github.com/ulivz/vuepress-plugin-yuque/compare/v0.4.0...v0.5.0) (2019-04-05)


### Features

* **$core:** bump vuepress-plugin-medium ([cce0e73](https://github.com/ulivz/vuepress-plugin-yuque/commit/cce0e73))
* **html:** remove html tags in code and remove table styles ([fe75e46](https://github.com/ulivz/vuepress-plugin-yuque/commit/fe75e46))



<a name="0.4.0"></a>
# [0.4.0](https://github.com/ulivz/vuepress-plugin-yuque/compare/v0.3.1...v0.4.0) (2019-04-05)


### Features

* **$core:** functional source option (using markdown when return 'markdown') ([5ef63cd](https://github.com/ulivz/vuepress-plugin-yuque/commit/5ef63cd))



<a name="0.3.1"></a>
## [0.3.1](https://github.com/ulivz/vuepress-plugin-yuque/compare/v0.3.0...v0.3.1) (2019-02-14)


### Bug Fixes

* **$core:** cannot use correct api base url for custom yuque domain ([818343e](https://github.com/ulivz/vuepress-plugin-yuque/commit/818343e))



<a name="0.3.0"></a>
# [0.3.0](https://github.com/ulivz/vuepress-plugin-yuque/compare/v0.2.1...v0.3.0) (2019-02-11)


### Bug Fixes

* **$core:** throw error when toc's length is 1 ([df4e48c](https://github.com/ulivz/vuepress-plugin-yuque/commit/df4e48c))


### Features

* **$toc:** always set top-level node to branch node instead of leaf node ([44216dd](https://github.com/ulivz/vuepress-plugin-yuque/commit/44216dd))



<a name="0.2.1"></a>
## [0.2.1](https://github.com/ulivz/vuepress-plugin-yuque/compare/v0.2.0...v0.2.1) (2019-02-11)


### Bug Fixes

* **$core:** cannot fetch data from private repo (close: [#2](https://github.com/ulivz/vuepress-plugin-yuque/issues/2)) ([a59c420](https://github.com/ulivz/vuepress-plugin-yuque/commit/a59c420))
* **$core:** throw error since "prettify" doesn't check null (close: [#1](https://github.com/ulivz/vuepress-plugin-yuque/issues/1)) ([1110274](https://github.com/ulivz/vuepress-plugin-yuque/commit/1110274))



<a name="0.2.0"></a>
# [0.2.0](https://github.com/ulivz/vuepress-plugin-yuque/compare/v0.1.0...v0.2.0) (2019-02-08)


### Bug Fixes

* **$core:** changing repoUrl under dev doesn't take effect ([93e0c06](https://github.com/ulivz/vuepress-plugin-yuque/commit/93e0c06))
* **$core:** empty node: Cannot read property 'format' of undefined ([3c0c3bc](https://github.com/ulivz/vuepress-plugin-yuque/commit/3c0c3bc))
* **$toc:** using safe pop to avoid empty stack ([6eb19b4](https://github.com/ulivz/vuepress-plugin-yuque/commit/6eb19b4))


### Features

* **$core:** enable .html suffix by default ([9a5167f](https://github.com/ulivz/vuepress-plugin-yuque/commit/9a5167f))



<a name="0.1.0"></a>
# 0.1.0 (2019-02-08)


### Bug Fixes

* **$fetch:** cannot save cache properly ([4b21277](https://github.com/ulivz/vuepress-plugin-yuque/commit/4b21277))


### Features

* **$core:** `authToken` option ([e953197](https://github.com/ulivz/vuepress-plugin-yuque/commit/e953197))
* **$core:** `html` option ([185fcab](https://github.com/ulivz/vuepress-plugin-yuque/commit/185fcab))
* **$core:** `home` option and default title and description ([7b2505d](https://github.com/ulivz/vuepress-plugin-yuque/commit/7b2505d))
* **$core:** enable `medium-zoom` by default ([3538cc8](https://github.com/ulivz/vuepress-plugin-yuque/commit/3538cc8))
* **$core:** handle uncreated page ([5665483](https://github.com/ulivz/vuepress-plugin-yuque/commit/5665483))
* **$fetch:** env: `SKIP_CACHE` and `AUTH_TOKEN` ([c0530d9](https://github.com/ulivz/vuepress-plugin-yuque/commit/c0530d9))
* **$toc:** push branch node when the depth of last node is 1 ([8809986](https://github.com/ulivz/vuepress-plugin-yuque/commit/8809986))
* **$html:** extract headers from yuque's HTML markups ([88900ae](https://github.com/ulivz/vuepress-plugin-yuque/commit/88900ae))
* **$html:** highlight code fence block at build time ([475448b](https://github.com/ulivz/vuepress-plugin-yuque/commit/475448b))
* **$core:** leverage "html content" by default for now ([85801e7](https://github.com/ulivz/vuepress-plugin-yuque/commit/85801e7))
* **$fetch:** only enable cache under dev mode ([98cc5d1](https://github.com/ulivz/vuepress-plugin-yuque/commit/98cc5d1))
* **$core:** `repoUrl` option ([81d07dd](https://github.com/ulivz/vuepress-plugin-yuque/commit/81d07dd))
* **$core:** use page's default format ([7b41867](https://github.com/ulivz/vuepress-plugin-yuque/commit/7b41867))





================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) ULVIZ <chl814@foxmail.com> (https://github.com/ulivz)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.


================================================
FILE: README.md
================================================
# vuepress-plugin-yuque

[![NPM version](https://badgen.net/npm/v/vuepress-plugin-yuque)](https://npmjs.com/package/vuepress-plugin-yuque) [![NPM downloads](https://badgen.net/npm/dm/vuepress-plugin-yuque)](https://npmjs.com/package/vuepress-plugin-yuque) 

> 本插件需要 VuePress >= `1.0.0-alpha.37`。

<img src="./.media/hero.png">

## 特性

- **简单**:Zero-Markdown,只需配置你的语雀 repo 地址,就能获得一个 VuePress 站点;
- **高效**:自动生成的文档主页、侧边栏让你享受 “高效” 的文档生成体验;
- **缓存**:完美地解决语雀 Open API 调用次数超限的问题;

想了解更多,请移步:

- **案例**
  - [视频演示](https://player.youku.com/embed/XNDA1MzAwMDIzNg==)
  - [**Site**](https://antd-course.ulivz.com/)
  - [语雀](https://www.yuque.com/ant-design/course)
  - [源码](https://github.com/ulivz/vuepress-plugin-yuque/tree/master/example/.vuepress)
- **文档**
  - [**Site**](https://vuepress-plugin-yuque.ulivz.com/)
  - [语雀](https://www.yuque.com/vuepress/vuepress-plugin-yuque)
  - [源码](https://github.com/ulivz/vuepress-plugin-yuque/tree/master/docs/.vuepress)

## 快速上手

在 VuePress 中配置 `repoUrl`:

```js
// .vuepress/config.js
module.exports = {
  title: 'Ant Design 实战教程',
  description: '基于 umi 的 Ant Design 实战教程',
  plugins: [
    ['vuepress-plugin-yuque', {
      repoUrl: 'https://www.yuque.com/ant-design/course',
    }]
  ]
}
```

你就能获得这个[**Site**](https://antd-course.ulivz.com/),更多使用方法,可参见[文档](https://vuepress-plugin-yuque.ulivz.com/)。

## Contributing

1. Fork it!
2. Create your feature branch: `git checkout -b my-new-feature`
3. Commit your changes: `git commit -am 'Add some feature'`
4. Push to the branch: `git push origin my-new-feature`
5. Submit a pull request :D


## Author

**vuepress-plugin-yuque** © [ULVIZ](https://github.com/ulivz), Released under the [MIT](./LICENSE) License.<br>
Authored and maintained by ULVIZ with help from contributors ([list](https://github.com/ulivz/vuepress-plugin-yuque/contributors)).

> [github.com/ulivz](https://github.com/ulivz) · GitHub [@ULVIZ](https://github.com/ulivz) · Twitter [@_ulivz](https://twitter.com/_ulivz)


================================================
FILE: docs/.vuepress/config.js
================================================
module.exports = {
  head: [
    ['link', { rel: 'icon', href: `https://cdn.nlark.com/yuque/0/2019/png/242808/1549571925285-2372b0a0-0234-421c-a139-00ea16f1a106.png` }],
    ['meta', { name: 'theme-color', content: '#3eaf7c' }],
    ['meta', { name: 'apple-mobile-web-app-capable', content: 'yes' }],
    ['meta', { name: 'apple-mobile-web-app-status-bar-style', content: 'black' }],
    ['meta', { name: 'msapplication-TileColor', content: '#000000' }],
    ['meta', { name: 'referrer', content: 'no-referrer' }]
  ],
  themeConfig: {
    repo: 'ulivz/vuepress-plugin-yuque',
    nav: [
      { text: '指南', link: '/intro.html' },
      { text: '配置', link: '/config.html' },
      { text: 'CHANGELOG', link: '/changelog.html' },
    ]
  },
  async additionalPages () {
    const fetch = require('node-fetch')
    const response = await fetch('https://raw.githubusercontent.com/ulivz/vuepress-plugin-yuque/master/CHANGELOG.md')
    const content = await response.text()
    return [
      {
        path: '/changelog.html',
        content
      }
    ]
  },
  plugins: [
    [
      require('../../lib'),
      {
        html: true,
        yuqueLink: true,
        repoUrl: 'https://www.yuque.com/vuepress/vuepress-plugin-yuque',
        home: {
          actionText: 'Getting Started →',
          actionLink: '/intro.html',
          heroImage: 'https://cdn.nlark.com/yuque/0/2019/png/242808/1549571925285-2372b0a0-0234-421c-a139-00ea16f1a106.png',
          footer: `Copyright © ULIVZ`,
          features: [
            { title: '简单', details: 'Zero-Markdown,只需配置你的语雀 repo 地址,就能获得一个 VuePress 站点' },
            { title: '高效', details: '自动生成的文档主页、侧边栏让你享受 “高效” 的文档生成体验' },
            { title: '缓存', details: '完美地解决语雀 Open API 调用次数超限的问题' },
          ]
        }
      }
    ]
  ]
}


================================================
FILE: docs/.vuepress/styles/index.styl
================================================
.content
  img
    max-width 100% !important

.home .hero img
    max-height 250px!important


================================================
FILE: example/.vuepress/config.js
================================================
module.exports = {
  title: 'Ant Design 实战教程',
  description: '基于 umi 的 Ant Design 实战教程',
  head: [
    ['link', {
      rel: 'icon',
      href: `https://cdn.nlark.com/yuque/0/2019/png/242808/1549571925285-2372b0a0-0234-421c-a139-00ea16f1a106.png`
    }],
    ['meta', { name: 'theme-color', content: '#3eaf7c' }],
    ['meta', { name: 'apple-mobile-web-app-capable', content: 'yes' }],
    ['meta', { name: 'apple-mobile-web-app-status-bar-style', content: 'black' }],
    ['meta', { name: 'msapplication-TileColor', content: '#000000' }],
    ['meta', { name: 'referrer', content: 'no-referrer' }]
  ],
  plugins: [
    [
      require('../../lib'),
      {
        repoUrl: 'https://www.yuque.com/ant-design/course',
        home: {
          features: [
            { title: '循序渐进', details: '本教程的难度依次递进,为阅读者呈现舒适的学习曲线' },
            { title: '值得信赖', details: '由 antd 团队亲自打造,从技术栈、生态、研发流程等来为你提供系统化的学习体验' },
            { title: '最佳实践', details: '通过结合实际开发过程中的案例,来描述不同场景下的最佳实践' },
          ]
        }
      }
    ]
  ]
}


================================================
FILE: lib/__test__/__snapshots__/toc.test.js.snap
================================================
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`getSidebarByToc 1`] = `
Array [
  Object {
    "children": Array [],
    "collapsable": false,
    "path": "/intro.html",
    "title": "前言",
  },
  Object {
    "children": Array [
      Array [
        "/sc1lvc.html",
        "前端开发的演变",
      ],
      Array [
        "/wybhm9.html",
        "初始化项目",
      ],
      Array [
        "/fd5af7.html",
        "第一个组件",
      ],
      Array [
        "/agmsgo.html",
        "使用 Ant Design 组件",
      ],
      Array [
        "/goozth.html",
        "受控组件与非受控组件",
      ],
    ],
    "collapsable": false,
    "title": "第一章: 基础知识",
  },
  Object {
    "children": Array [
      Array [
        "/layout.html",
        "基本布局",
      ],
      Array [
        "/ghalng.html",
        "侧边导航",
      ],
      Array [
        "/ipsba8.html",
        "路由配置",
      ],
    ],
    "collapsable": false,
    "path": "/chapter2.html",
    "title": "第二章: 布局与路由",
  },
  Object {
    "children": Array [
      Array [
        "/abl3ad.html",
        "使用 model",
      ],
      Array [
        "/dsl8ee.html",
        "搭建基于 model 的卡片列表页面",
      ],
      Array [
        "/ig6mzb.html",
        "在 model 中请求服务端数据",
      ],
      Array [
        "/kmmq56.html",
        "模拟服务端数据",
      ],
    ],
    "collapsable": false,
    "path": "/chapter3.html",
    "title": "第三章: 小试牛刀",
  },
  Object {
    "children": Array [
      Array [
        "/ndfx96.html",
        "表格",
      ],
      Array [
        "/up1dn3.html",
        "表单",
      ],
      Array [
        "/dk8xsp.html",
        "图表",
      ],
    ],
    "collapsable": false,
    "path": "/chapter4.html",
    "title": "第四章: 复杂页面",
  },
  Object {
    "children": Array [
      Array [
        "/customized_styles.html",
        "自定义样式",
      ],
      Array [
        "/wvbsue.html",
        "上传与下载",
      ],
      Array [
        "/aut0sr.html",
        "国际化",
      ],
      Array [
        "/lifemethods.html",
        "React 的生命周期",
      ],
      Array [
        "/auth.html",
        "权限",
      ],
      Array [
        "/unittest.html",
        "单元测试",
      ],
      Array [
        "/typescript.html",
        "使用 TypeScript",
      ],
    ],
    "collapsable": false,
    "title": "第五章: 进阶功能",
  },
  Object {
    "children": Array [
      Array [
        "/byllph.html",
        "你需要了解的 ES6 语法",
      ],
      Array [
        "/tydf0a.html",
        "深入理解 umi",
      ],
      Array [
        "/about.html",
        "关于我们",
      ],
    ],
    "collapsable": false,
    "title": "附录",
  },
]
`;

exports[`getSidebarByToc 2`] = `
Array [
  Object {
    "children": Array [
      Array [
        "/intro.html",
        "介绍",
      ],
      Array [
        "/getting-started.html",
        "快速上手",
      ],
      Array [
        "/export-first-website.html",
        "导出第一个站点",
      ],
    ],
    "collapsable": false,
    "title": "指南",
  },
  Object {
    "children": Array [],
    "collapsable": false,
    "path": "/config.html",
    "title": "配置",
  },
]
`;

exports[`getSidebarByToc 3`] = `
Array [
  Object {
    "children": Array [
      Array [
        "/intro.html",
        "介绍",
      ],
      Array [
        "/getting-started.html",
        "快速上手",
      ],
      Array [
        "/generate-homepage.html",
        "生成主页",
      ],
      Array [
        "/cache.html",
        "缓存",
      ],
      Array [
        "/private-repo.html",
        "私有仓库",
      ],
    ],
    "collapsable": false,
    "title": "指南",
  },
  Object {
    "children": Array [],
    "collapsable": false,
    "path": "/config.html",
    "title": "配置",
  },
  Object {
    "children": Array [],
    "collapsable": false,
    "title": "进阶",
  },
  Object {
    "children": Array [],
    "collapsable": false,
    "path": "/a.html",
    "title": "A",
  },
  Object {
    "children": Array [
      Array [
        "/b-1.html",
        "B-1",
      ],
    ],
    "collapsable": false,
    "path": "/b.html",
    "title": "B",
  },
  Object {
    "children": Array [],
    "collapsable": false,
    "path": "/c.html",
    "title": "C",
  },
]
`;


================================================
FILE: lib/__test__/toc.constant.js
================================================
const TOC_1 = [
  { title: '前言', slug: 'intro', depth: 1 },
  { title: '第一章: 基础知识', slug: '#', depth: 1 },
  { title: '前端开发的演变', slug: 'sc1lvc', depth: 2 },
  { title: '初始化项目', slug: 'wybhm9', depth: 2 },
  { title: '第一个组件', slug: 'fd5af7', depth: 2 },
  { title: '使用 Ant Design 组件', slug: 'agmsgo', depth: 2 },
  { title: '受控组件与非受控组件', slug: 'goozth', depth: 2 },
  { title: '第二章: 布局与路由', slug: 'chapter2', depth: 1 },
  { title: '基本布局', slug: 'layout', depth: 2 },
  { title: '侧边导航', slug: 'ghalng', depth: 2 },
  { title: '路由配置', slug: 'ipsba8', depth: 2 },
  { title: '第三章: 小试牛刀', slug: 'chapter3', depth: 1 },
  { title: '使用 model', slug: 'abl3ad', depth: 2 },
  { title: '搭建基于 model 的卡片列表页面', slug: 'dsl8ee', depth: 2 },
  { title: '在 model 中请求服务端数据', slug: 'ig6mzb', depth: 2 },
  { title: '模拟服务端数据', slug: 'kmmq56', depth: 2 },
  { title: '第四章: 复杂页面', slug: 'chapter4', depth: 1 },
  { title: '表格', slug: 'ndfx96', depth: 2 },
  { title: '表单', slug: 'up1dn3', depth: 2 },
  { title: '图表', slug: 'dk8xsp', depth: 2 },
  { title: '第五章: 进阶功能', slug: '#', depth: 1 },
  { title: '自定义样式', slug: 'customized_styles', depth: 2 },
  { title: '上传与下载', slug: 'wvbsue', depth: 2 },
  { title: '国际化', slug: 'aut0sr', depth: 2 },
  { title: 'React 的生命周期', slug: 'lifemethods', depth: 2 },
  { title: '权限', slug: 'auth', depth: 2 },
  { title: '单元测试', slug: 'unittest', depth: 2 },
  { title: '使用 TypeScript', slug: 'typescript', depth: 2 },
  { title: '附录', slug: '#', depth: 1 },
  { title: '你需要了解的 ES6 语法', slug: 'byllph', depth: 2 },
  { title: '深入理解 umi', slug: 'tydf0a', depth: 2 },
  { title: '关于我们', slug: 'about', depth: 2 }
]

const TOC_2 = [
  { title: '指南', slug: '#', depth: 1 },
  { title: '介绍', slug: 'intro', depth: 2 },
  { title: '快速上手', slug: 'getting-started', depth: 2 },
  { title: '导出第一个站点', slug: 'export-first-website', depth: 2 },
  { title: '配置', slug: 'config', depth: 1 }
]

const TOC_3 = [
  { title: '指南', slug: '#', depth: 1 },
  { title: '介绍', slug: 'intro', depth: 2 },
  { title: '快速上手', slug: 'getting-started', depth: 2 },
  { title: '生成主页', slug: 'generate-homepage', depth: 2 },
  { title: '缓存', slug: 'cache', depth: 2 },
  { title: '私有仓库', slug: 'private-repo', depth: 2 },
  { title: '配置', slug: 'config', depth: 1 },
  { title: '进阶', slug: '#', depth: 1 },
  { title: 'A', slug: 'a', depth: 1 },
  { title: 'B', slug: 'b', depth: 1 },
  { title: 'B-1', slug: 'b-1', depth: 2 },
  { title: 'C', slug: 'c', depth: 1 },
]

module.exports = [
  TOC_1,
  TOC_2,
  TOC_3
]


================================================
FILE: lib/__test__/toc.test.js
================================================
const { getSidebarByToc } = require('../toc')
const tocs = require('./toc.constant')

test('getSidebarByToc', () => {
  tocs.forEach(toc => {
    const sidebar = getSidebarByToc(toc)
    expect(sidebar).toMatchSnapshot()
  })
})


================================================
FILE: lib/client.js
================================================
import './style.styl'


================================================
FILE: lib/compose.js
================================================
/**
 * Expose compose.
 */

module.exports = function compose(...processors) {
  if (processors.length === 0)
    return (input) => input
  if (processors.length === 1)
    return processors[0]
  return processors.reduce((prev, next) => {
    return (...args) => next(prev(...args))
  })
}


================================================
FILE: lib/constant.js
================================================
const pkg = require('./../package')

module.exports = {
  PACKAGE_NAME: pkg.name
}


================================================
FILE: lib/fetch.js
================================================
/**
 * Module dependencies.
 */

const fetch = require('node-fetch')

/**
 * Expose a wrapped fetch which handles network error.
 */

module.exports = function (url, options = {}) {
  return fetch(url, options)
    .then(handleResponse, handleNetworkError)
}

/**
 * Response handler
 *
 * @param {Response} response
 * @returns {Promise<any>>|never}
 */

function handleResponse(response) {
  if (response.ok) {
    return response.json()
  }
  return response.json().then(error => {
    throw error
  })
}

/**
 * Network error handler
 *
 * @param {Error} error
 */

function handleNetworkError(error) {
  throw {
    msg: error.message
  }
}


================================================
FILE: lib/html.js
================================================
/**
 * Module dependencies.
 */

const cheerio = require('cheerio')
const compose = require('./compose')
const escapeHtml = require('escape-html')

/**
 * Expose a object containing the utilities of handling markups.
 */

module.exports = {
  prettify
}

const transformHeaders = compose(
  ...[
    'h1',
    'h2',
    'h3',
    'h4',
    'h5',
    'h6',
  ].map((h, i) => $ => {
      $(h).replaceWith((_i, el) => {
        const text = $(el).text().trim()
        if(!text) {
          return
        }
        return `\n\n ${getHash(i + 1)} ${text} \n\n`
      })
      return $
    }
  ),
  highlight,
  escapeHTMLTagInCode,
  removeTableStyls,
)

function getHash(i) {
  return [...Array(i)].map(() => '#').join('')
}

function prettify(html) {
  html = prune(html)
  const $ = cheerio.load(html, {
    // ref: https://github.com/cheeriojs/cheerio/issues/565
    decodeEntities: false
  })
  transformHeaders($)
  return $('body').html()
}

function prune(html) {
  return html
    .replace(/<p><br\s\/><\/p>/g, '')
    .replace(/<p><span><br\s\/><\/span><\/p>/g, '')
}

const CODE_FENCE = '```'

function highlight($) {
  $('pre').replaceWith((i, el) => {
    const $el = $(el)
    const $code = $el.children('code')
    const lang = $el.attr('data-lang')
    const code = $code.length ? $code.text() : $el.text()
    return `\n\n ${CODE_FENCE} ${lang} \n ${code} \n ${CODE_FENCE} \n\n`
  })
  return $
}

function escapeHTMLTagInCode($) {
  $('code').replaceWith((i, el) => {
    const content = $(el).text()
    return escapeHtml(`<code>${escapeHtml(content)}</code>`)
  })
  return $
}

function removeTableStyls($) {
  $('table').css('width', '')
  return $
}


================================================
FILE: lib/index.js
================================================
/**
 * Module dependencies.
 */

const assert = require('assert')
const { chalk, parseFrontmatter, path } = require('@vuepress/shared-utils')
const Yuque = require('./yuque')
const { getSidebarByToc } = require('./toc')
const spinner = require('./spinner')
const { prettify } = require('./html')
const { PACKAGE_NAME } = require('./constant')
const debug = require('debug')(PACKAGE_NAME)

/**
 * Expose vuepress-plugin-yuque
 */

module.exports = (opts, ctx) => {
  return {
    name: PACKAGE_NAME,

    enhanceAppFiles: [
      path.join(__dirname, 'client.js')
    ],

    plugins: [
      ['@vuepress/medium-zoom', true],
    ],

    chainMarkdown(config) {
      if (opts.html) {
        return
      }
      // Disable `html` for now. Many yuque writers would write raw HTML in the markdown
      // But only these markups under inline code(`) will be transpiled successfully when
      // `html` is enabled.
      config
        .options
        .html(false)
    },

    async ready() {
      let { repoId, repoUrl, authToken, source } = opts

      if (repoId) {
        assert(
          typeof repoId === 'string' ||
          typeof repoId === 'number',
          `[${PACKAGE_NAME}] repoId should be string or number, but got ${repoId}`
        )
      } else if (repoUrl) {
        assert(
          typeof repoUrl === 'string',
          `[${PACKAGE_NAME}] repoUrl should be string, but got ${repoId}`
        )
      } else {
        throw new Error(`Expected repoId or repoUrl`)
      }

      Yuque.setAuthToken(authToken)

      if (repoUrl) {
        const targetRepo = await Yuque.inferRepoByUrl(repoUrl)
        repoId = targetRepo.id
      }

      Yuque.setRepoId(repoId)
      const yuque = Yuque.getInstance()

      spinner.start(`Fetching repo detail ...`)
      const repoDetail = await yuque.getRepoDetail()
      if (!(repoDetail && repoDetail.data && repoDetail.data.name)) {
        spinner.fail(
          `Please check your repoId: ${chalk.yellow(repoId)} ` +
          `response: ${JSON.stringify(repoDetail)}`
        )
        process.exit(1)
      }
      spinner.succeed(`Retrieved repo detail`)

      const { name, description, user } = repoDetail.data
      spinner.succeed(
        `Your Yuque website: ${chalk.green(name)} ` +
        `${chalk.gray(description)} ...`
      )
      spinner.start(`Fetching TOC ... `)

      const toc = await yuque.getToc()
      assert(
        toc && toc.data,
        `[${PACKAGE_NAME}] cannot get toc of ${repoId}`
      )

      debug('toc', toc.data)

      spinner.succeed(`Retrieved TOC`)

      // Apply sidebar config
      const sidebar = getSidebarByToc(toc.data)
      ctx.siteConfig.themeConfig = ctx.siteConfig.themeConfig || {}
      ctx.siteConfig.themeConfig.sidebar = sidebar
      if (!ctx.siteConfig.title) {
        ctx.siteConfig.title = name
      }
      if (!ctx.siteConfig.description) {
        ctx.siteConfig.description = description
      }

      let defaultActionLink

      // Apply pages
      for (const page of toc.data) {
        const { title, slug } = page
        // Once the slug is equal to '#', it means that current node
        // is an empty node.
        if (!slug || slug === '#') {
          continue
        }

        spinner.start(`Fetching ${chalk.cyan(title)} ... `)

        const { status, data } = await yuque.getPage(slug)
        debug('status = ', status)

        let postContent
        if (status && status === 404) {
          postContent = `# ${title}\n > 此文档尚未创建`
        } else {
          const useMarkdown = typeof source === 'string'
            ? source === 'markdown'
            : typeof source === 'function'
              ? source(page) === 'markdown'
              : false
          if (useMarkdown || data.format === 'markdown') {
            postContent = data.body || ''
          } else {
            postContent = prettify(data.body_html || '')
          }
        }

        const {
          data: frontmatter,
          content: strippedContent
        } = parseFrontmatter(postContent)
        const inferredTitle = inferTitle({}, strippedContent)

        let content = inferredTitle
          ? postContent
          : `# ${title} \n\n ${postContent}`

        if (opts.yuqueLink) {
          content += opts.yuqueLinkHtml || `<br><br><a class="yuque-link" target="_blank" href="${repoUrl}/${slug}"><svg viewBox="64 64 896 896" class="" data-icon="yuque" width="1em" height="1em" fill="currentColor" aria-hidden="true"><path d="M854.6 370.6c-9.9-39.4 9.9-102.2 73.4-124.4l-67.9-3.6s-25.7-90-143.6-98c-117.8-8.1-194.9-3-195-3 .1 0 87.4 55.6 52.4 154.7-25.6 52.5-65.8 95.6-108.8 144.7-1.3 1.3-2.5 2.6-3.5 3.7C319.4 605 96 860 96 860c245.9 64.4 410.7-6.3 508.2-91.1 20.5-.2 35.9-.3 46.3-.3 135.8 0 250.6-117.6 245.9-248.4-3.2-89.9-31.9-110.2-41.8-149.6zm-204.1 334c-10.6 0-26.2.1-46.8.3l-23.6.2-17.8 15.5c-47.1 41-104.4 71.5-171.4 87.6-52.5 12.6-110 16.2-172.7 9.6 18-20.5 36.5-41.6 55.4-63.1 92-104.6 173.8-197.5 236.9-268.5l1.4-1.4 1.3-1.5c4.1-4.6 20.6-23.3 24.7-28.1 9.7-11.1 17.3-19.9 24.5-28.6 30.7-36.7 52.2-67.8 69-102.2l1.6-3.3 1.2-3.4c13.7-38.8 15.4-76.9 6.2-112.8 22.5.7 46.5 1.9 71.7 3.6 33.3 2.3 55.5 12.9 71.1 29.2 5.8 6 10.2 12.5 13.4 18.7 1 2 1.7 3.6 2.3 5l5 17.7c-15.7 34.5-19.9 73.3-11.4 107.2 3 11.8 6.9 22.4 12.3 34.4 2.1 4.7 9.5 20.1 11 23.3 10.3 22.7 15.4 43 16.7 78.7 3.3 94.6-82.7 181.9-182 181.9z"></path></svg> 使用语雀查看</a>`
        }

        const permalink = `/${slug}.html`

        await ctx.addPage({
          content,
          frontmatter,
          permalink
        })

        if (!defaultActionLink) {
          defaultActionLink = permalink
        }

        spinner.succeed(`Retrieved ${chalk.cyan(title)} ... `)
      }

      if (ctx.pages.every(page => page.path !== '/')) {
        const { home = {} } = opts
        const { actionText, actionLink, heroImage, features, footer } = home

        spinner.info(`Apply default homepage`)
        await ctx.addPage({
          content: '',
          permalink: '/',
          frontmatter: {
            home: true,
            heroImage: heroImage || user.large_avatar_url,
            actionText: actionText || 'Getting Started →',
            actionLink: actionLink || defaultActionLink,
            features: features || undefined,
            footer: footer || `Copyright © ${user.name}`,
          }
        })
      }
    }
  }
}

/**
 * Infer title, forked from VuePress.
 */
function inferTitle(frontmatter, strippedContent) {
  if (frontmatter.home) {
    return 'Home';
  }
  if (frontmatter.title) {
    return frontmatter.title
  }
  const match = strippedContent.trim().match(/^#\s+(.*)/);
  if (match) {
    return match[1];
  }
};


================================================
FILE: lib/spinner.js
================================================
/**
 * Module dependencies.
 */

const ora = require('ora')
const { chalk } = require('@vuepress/shared-utils')

/**
 * Expose a singleton of spinner
 */

module.exports = !process.env.DEBUG
  ? ora()
  : [
    'frame',
    'clear',
    'render',
    'start',
    'stop',
    'succeed',
    'fail',
    'warn',
    'info',
    'stopAndPersist'
  ].reduce((memo, key) => {
    memo[key] = curry(key)
    return memo
  }, {})

function curry(name) {
  return (...args) => console.log(chalk.cyan(`spinner:${name}`), ...args)
}


================================================
FILE: lib/stack.js
================================================
/**
 * Expose a simple stack
 */

module.exports = class Stack {
  constructor() {
    this.elements = []
  }

  push(element) {
    this.elements.push(element)
  }

  pop() {
    return this.elements.pop()
  }

  peek() {
    return this.elements[this.size() - 1]
  }

  size() {
   return this.elements.length
  }
}


================================================
FILE: lib/store.js
================================================
/**
 * Module dependencies.
 */

const Conf = require('conf')

/**
 * Expose a object containing the utilities of cache.
 */

module.exports = {
  get,
  init
}

let store

function init(configName) {
  store = new Conf({
    configName: configName
  })
  return store
}

function get(configName) {
  if (store) {
    return store
  }
  return init(configName)
}




================================================
FILE: lib/style.styl
================================================
.yuque-link
  border: 1px solid #333;
  padding: 5px 10px;
  color: #333;
  border-radius: 3px;
  font-weight: 200;
  text-decoration none
  svg
    line-height: 20px;
    vertical-align: text-bottom;


================================================
FILE: lib/toc.js
================================================
/**
 * Module dependencies.
 */

const Stack = require('./stack')

/**
 * Expose a object containing the utilities of TOC.
 */
module.exports = {
  getSidebarByToc
}

/**
 * Transform yuque's toc to vuepress's sidebar config.
 *
 * @param {Array<TocNode>>} toc
 * @returns {Array<SidebarGroup>}
 */

function getSidebarByToc(toc) {
  if (toc.length === 1) {
    return [{
      path: toc[0].slug,
      title: toc[0].title,
      collapsable: false
    }]
  }

  const stack = new Stack()
  const sidebar = []
  stack.push({
    children: sidebar
  })

  for (let i = 0, l = toc.length; i < l; i++) {
    const prev = toc[i - 1]
    const cur = toc[i]
    const next = toc[i + 1]

    if (!prev) {
      if (next.depth >= cur.depth) {
        pushBranch(cur, stack)
      } else {
        pushLeaf(cur, stack)
      }

    } else if (!next) {
      if (cur.depth >= prev.depth) {
        pushLeaf(cur, stack)
      } else {
        stack.pop()
        if (cur.depth === 1) {
          pushBranch(cur, stack)
        } else {
          pushLeaf(cur, stack)
        }
      }

    } else {
      // Current node is the children node of previous node
      if (cur.depth > prev.depth) {
        if (next.depth > cur.depth) {
          pushBranch(cur, stack)
        } else {
          pushLeaf(cur, stack)
        }
        // Current node is the adjacent node of previous node
      } else if (cur.depth === prev.depth) {
        if (next.depth > cur.depth) {
          safePop(stack)
          pushBranch(cur, stack)
        } else if (next.depth === cur.depth) {
          if (cur.depth === 1) {
            safePop(stack)
            pushBranch(cur, stack)
          } else {
            pushLeaf(cur, stack)
          }
        } else {
          pushLeaf(cur, stack)
        }
        // Current node is the adjacent node previous node's parnent's node
      } else if (cur.depth < prev.depth) {
        if (next.depth > cur.depth) {
          safePop(stack)
          pushBranch(cur, stack)
        } else if (next.depth === cur.depth) {
          if (cur.depth === 1) {
            safePop(stack)
            pushBranch(cur, stack)
          } else {
            safePop(stack)
            pushLeaf(cur, stack)
          }
        } else {
          safePop(stack)
          pushLeaf(cur, stack)
        }
      }
    }
  }
  return sidebar
}

function safePop(stack) {
  if (stack.size() === 1) {
    return
  }
  stack.pop()
}

/**
 * Push branch node
 *
 * @param {TocNode} node
 * @param {Stack} stack
 */

function pushBranch(node, stack) {
  const group = getBranch(node)
  stack.peek().children.push(group)
  stack.push(group)
}

/**
 * Push leaf node
 *
 * @param {TocNode} node
 * @param {Stack} stack
 */

function pushLeaf(node, stack) {
  stack.peek().children.push(getLeaf(node))
}

/**
 * Get leaf node for vuepress's sidebar group
 *
 * @param {string} title
 * @param {string} slug
 * @returns {Array<string>}
 */

function getLeaf({
  title,
  slug
}) {
  return [`/${slug}.html`, title]
}

/**
 * Get branch node for vuepress's sidebar group
 *
 * @param {string} title
 * @param {string} slug
 * @returns {SidebarGroup}
 */

function getBranch({
  title,
  slug
}) {
  const group = {
    title,
    collapsable: false,
    children: []
  }
  if (slug && slug !== '#') {
    group.path = `/${slug}.html`
  }
  return group
}



================================================
FILE: lib/yuque.js
================================================
const assert = require('assert')
const url = require('url')
const wfetch = require('./fetch')
const store = require('./store')
const { hash, logger, chalk } = require('@vuepress/shared-utils')
const { PACKAGE_NAME } = require('./constant')
const debug = require('debug')(`${PACKAGE_NAME}:yuque`)

const AUTH_TOKEN = process.env.YUQUE_QUTH_TOKEN
const SKIP_CACHE = process.env.SKIP_CACHE
const isProduction = process.env.NODE_ENV === 'production'

let instance
let repoId
let authToken

module.exports = class Yuque {
  static setRepoId(id) {
    repoId = id
  }

  static setAuthToken(token) {
    authToken = token
  }

  static getInstance() {
    if (!instance) {
      instance = new Yuque(repoId)
    }
    if (instance.repoId !== repoId) {
      instance = new Yuque(repoId)
    }
    return instance
  }

  static async fetch(url, options = {}) {
    debug('fetch', url, 'with', JSON.stringify(options, null, 2))
    const { useCache = true } = options
    const key = hash(url + JSON.stringify(options))
    const cacheKey = `fetch.${key}`
    let yuque
    if (repoId) {
      yuque = Yuque.getInstance()
    }
    if (yuque && !isProduction && useCache && !SKIP_CACHE) {
      const cached = yuque.store.get(cacheKey)
      if (cached) {
        debug(`Using cache for ${chalk.yellow(url)}`)
        return cached
      }
    }

    let response
    const foptions = {}
    if (authToken || AUTH_TOKEN) {
      foptions.headers = {
        'X-Auth-Token': authToken || AUTH_TOKEN
      }
    }

    try {
      response = await wfetch(url, foptions)
      yuque && yuque.store.set(cacheKey, response)
      return response
    } catch (e) {
      return e
    }
  }

  static async get(base, path) {
    path = `${base}${path}`
    return Yuque.fetch(path)
  }

  static async getRepos(base, repoId) {
    return Yuque.get(base, `groups/${repoId}/repos`)
  }

  static async getRepoDetail(base, repoId) {
    return Yuque.get(base, `repos/${repoId}`)
  }

  static async getToc(base, repoId) {
    return this.get(base, `repos/${repoId}/toc`)
  }

  static async getPage(base, repoId, slug) {
    return this.get(base, `repos/${repoId}/docs/${slug}?raw=1`)
  }

  static async inferRepoByUrl(repoUrl) {
    const { protocol, host, pathname } = url.parse(repoUrl)
    assert(
      typeof pathname === 'string',
      `[CANNOT_RESOLVE_PATHNAME] Cannot infer repoId from repoUrl: ${repoUrl}`
    )

    const normalizedPathname = pathname.replace(/(^\/|\/$)/g, '')
    const [groupId, repoId] = normalizedPathname.split('/')
    assert(
      typeof groupId === 'string' &&
      typeof repoId === 'string',
      `[CANNOT_PARSE_PATHNAME] Cannot infer repoId from repoUrl: ${repoUrl}`
    )

    const base = `${protocol}//${host}/api/v2/`
    Yuque.base = base

    const { data: repos } = await Yuque.getRepos(base, groupId)
    assert(
      Array.isArray(repos),
      `[CANNOT_FIND_GROUP] Cannot infer repoId from repoUrl: ${repoUrl}`
    )

    const targetRepo = repos.find(repo => repo.namespace === normalizedPathname)
    assert(
      typeof targetRepo === 'object',
      `[CANNOT_FIND_REPO] Cannot infer repoId from repoUrl: ${repoUrl}`
    )

    return targetRepo
  }

  constructor(repoId) {
    this.repoId = repoId
    this.store = store.get(`yuque_repo_${repoId}`)
  }

  get base() {
    return Yuque.base || 'https://www.yuque.com/api/v2/'
  }

  async getRepos() {
    return Yuque.getRepos(this.base, this.repoId)
  }

  async getRepoDetail() {
    return Yuque.getRepoDetail(this.base, this.repoId)
  }

  async getToc() {
    return Yuque.getToc(this.base, this.repoId)
  }

  async getPage(slug) {
    return Yuque.getPage(this.base, this.repoId, slug)
  }
}


================================================
FILE: package.json
================================================
{
  "name": "vuepress-plugin-yuque",
  "version": "0.6.1",
  "description": "I: Yuque Repo, O: VuePress Site!",
  "main": "lib/index.js",
  "files": [
    "lib"
  ],
  "scripts": {
    "test": "jest",
    "lint": "xo",
    "dev": "vuepress dev docs --temp .temp",
    "build": "vuepress build docs --temp .temp",
    "dev:example": "vuepress dev example --temp .temp2",
    "build:example": "vuepress build example --temp .temp2",
    "release": "release-it"
  },
  "repository": {
    "url": "ulivz/vuepress-plugin-yuque",
    "type": "git"
  },
  "author": "ulivz<chl814@foxmail.com>",
  "license": "MIT",
  "dependencies": {
    "@vuepress/plugin-medium-zoom": "^1.0.0-alpha.44",
    "cheerio": "^1.0.0-rc.2",
    "conf": "^2.2.0",
    "debug": "^4.1.1",
    "node-fetch": "^2.3.0",
    "ora": "^3.0.0"
  },
  "devDependencies": {
    "conventional-changelog-cli": "^2.0.1",
    "eslint-config-sherry": "0.0.1",
    "husky": "1.2.0",
    "jest": "23.6.0",
    "lint-staged": "8.1.0",
    "release-it": "v7.4.8",
    "vuepress": "1.0.0-alpha.44",
    "xo": "0.23.0",
    "escape-html": "^1.0.3"
  },
  "jest": {
    "testEnvironment": "node"
  },
  "xo": {
    "extends": [
      "sherry"
    ],
    "envs": [
      "jest"
    ]
  },
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  },
  "lint-staged": {
    "*.{js}": [
      "xo --fix",
      "git add"
    ]
  }
}


================================================
FILE: test/index.test.js
================================================
const vuepressPluginYuque = require('../')

test('main', () => {
  expect(typeof vuepressPluginYuque).toBe('function')
})
Download .txt
gitextract__k5qwlck/

├── .editorconfig
├── .gitattributes
├── .github/
│   └── FUNDING.yml
├── .gitignore
├── .release-it.json
├── CHANGELOG.md
├── LICENSE
├── README.md
├── docs/
│   └── .vuepress/
│       ├── config.js
│       └── styles/
│           └── index.styl
├── example/
│   └── .vuepress/
│       └── config.js
├── lib/
│   ├── __test__/
│   │   ├── __snapshots__/
│   │   │   └── toc.test.js.snap
│   │   ├── toc.constant.js
│   │   └── toc.test.js
│   ├── client.js
│   ├── compose.js
│   ├── constant.js
│   ├── fetch.js
│   ├── html.js
│   ├── index.js
│   ├── spinner.js
│   ├── stack.js
│   ├── store.js
│   ├── style.styl
│   ├── toc.js
│   └── yuque.js
├── package.json
└── test/
    └── index.test.js
Download .txt
SYMBOL INDEX (48 symbols across 10 files)

FILE: docs/.vuepress/config.js
  method additionalPages (line 18) | async additionalPages () {

FILE: lib/__test__/toc.constant.js
  constant TOC_1 (line 1) | const TOC_1 = [
  constant TOC_2 (line 36) | const TOC_2 = [
  constant TOC_3 (line 44) | const TOC_3 = [

FILE: lib/fetch.js
  function handleResponse (line 23) | function handleResponse(response) {
  function handleNetworkError (line 38) | function handleNetworkError(error) {

FILE: lib/html.js
  function getHash (line 41) | function getHash(i) {
  function prettify (line 45) | function prettify(html) {
  function prune (line 55) | function prune(html) {
  constant CODE_FENCE (line 61) | const CODE_FENCE = '```'
  function highlight (line 63) | function highlight($) {
  function escapeHTMLTagInCode (line 74) | function escapeHTMLTagInCode($) {
  function removeTableStyls (line 82) | function removeTableStyls($) {

FILE: lib/index.js
  method chainMarkdown (line 30) | chainMarkdown(config) {
  method ready (line 42) | async ready() {
  function inferTitle (line 195) | function inferTitle(frontmatter, strippedContent) {

FILE: lib/spinner.js
  function curry (line 30) | function curry(name) {

FILE: lib/stack.js
  method constructor (line 6) | constructor() {
  method push (line 10) | push(element) {
  method pop (line 14) | pop() {
  method peek (line 18) | peek() {
  method size (line 22) | size() {

FILE: lib/store.js
  function init (line 18) | function init(configName) {
  function get (line 25) | function get(configName) {

FILE: lib/toc.js
  function getSidebarByToc (line 21) | function getSidebarByToc(toc) {
  function safePop (line 106) | function safePop(stack) {
  function pushBranch (line 120) | function pushBranch(node, stack) {
  function pushLeaf (line 133) | function pushLeaf(node, stack) {
  function getLeaf (line 145) | function getLeaf({
  function getBranch (line 160) | function getBranch({

FILE: lib/yuque.js
  constant AUTH_TOKEN (line 9) | const AUTH_TOKEN = process.env.YUQUE_QUTH_TOKEN
  constant SKIP_CACHE (line 10) | const SKIP_CACHE = process.env.SKIP_CACHE
  method setRepoId (line 18) | static setRepoId(id) {
  method setAuthToken (line 22) | static setAuthToken(token) {
  method getInstance (line 26) | static getInstance() {
  method fetch (line 36) | static async fetch(url, options = {}) {
  method get (line 70) | static async get(base, path) {
  method getRepos (line 75) | static async getRepos(base, repoId) {
  method getRepoDetail (line 79) | static async getRepoDetail(base, repoId) {
  method getToc (line 83) | static async getToc(base, repoId) {
  method getPage (line 87) | static async getPage(base, repoId, slug) {
  method inferRepoByUrl (line 91) | static async inferRepoByUrl(repoUrl) {
  method constructor (line 124) | constructor(repoId) {
  method base (line 129) | get base() {
  method getRepos (line 133) | async getRepos() {
  method getRepoDetail (line 137) | async getRepoDetail() {
  method getToc (line 141) | async getToc() {
  method getPage (line 145) | async getPage(slug) {
Condensed preview — 28 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (45K chars).
[
  {
    "path": ".editorconfig",
    "chars": 187,
    "preview": "root = true\n\n[*]\nindent_style = space\nindent_size = 2\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_whitespace = true\ni"
  },
  {
    "path": ".gitattributes",
    "chars": 12,
    "preview": "* text=auto\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "chars": 698,
    "preview": "# These are supported funding model platforms\n\ngithub: ulivz # Replace with up to 4 GitHub Sponsors-enabled usernames e."
  },
  {
    "path": ".gitignore",
    "chars": 31,
    "preview": "node_modules\n.temp*\ndocs2\ndist\n"
  },
  {
    "path": ".release-it.json",
    "chars": 283,
    "preview": "{\n  \"src\": {\n    \"tagName\": \"v%s\",\n    \"commitMessage\": \"%s\"\n  },\n  \"increment\": \"conventional:angular\",\n  \"beforeChange"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 6310,
    "preview": "## [0.6.1](https://github.com/ulivz/vuepress-plugin-yuque/compare/v0.6.0...v0.6.1) (2020-01-15)\n\n\n### Bug Fixes\n\n* 1. fa"
  },
  {
    "path": "LICENSE",
    "chars": 1115,
    "preview": "The MIT License (MIT)\n\nCopyright (c) ULVIZ <chl814@foxmail.com> (https://github.com/ulivz)\n\nPermission is hereby granted"
  },
  {
    "path": "README.md",
    "chars": 1973,
    "preview": "# vuepress-plugin-yuque\n\n[![NPM version](https://badgen.net/npm/v/vuepress-plugin-yuque)](https://npmjs.com/package/vuep"
  },
  {
    "path": "docs/.vuepress/config.js",
    "chars": 1786,
    "preview": "module.exports = {\n  head: [\n    ['link', { rel: 'icon', href: `https://cdn.nlark.com/yuque/0/2019/png/242808/1549571925"
  },
  {
    "path": "docs/.vuepress/styles/index.styl",
    "chars": 93,
    "preview": ".content\n  img\n    max-width 100% !important\n\n.home .hero img\n    max-height 250px!important\n"
  },
  {
    "path": "example/.vuepress/config.js",
    "chars": 1025,
    "preview": "module.exports = {\n  title: 'Ant Design 实战教程',\n  description: '基于 umi 的 Ant Design 实战教程',\n  head: [\n    ['link', {\n     "
  },
  {
    "path": "lib/__test__/__snapshots__/toc.test.js.snap",
    "chars": 4087,
    "preview": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`getSidebarByToc 1`] = `\nArray [\n  Object {\n    \"children\": Array []"
  },
  {
    "path": "lib/__test__/toc.constant.js",
    "chars": 2504,
    "preview": "const TOC_1 = [\n  { title: '前言', slug: 'intro', depth: 1 },\n  { title: '第一章: 基础知识', slug: '#', depth: 1 },\n  { title: '前"
  },
  {
    "path": "lib/__test__/toc.test.js",
    "chars": 229,
    "preview": "const { getSidebarByToc } = require('../toc')\nconst tocs = require('./toc.constant')\n\ntest('getSidebarByToc', () => {\n  "
  },
  {
    "path": "lib/client.js",
    "chars": 22,
    "preview": "import './style.styl'\n"
  },
  {
    "path": "lib/compose.js",
    "chars": 290,
    "preview": "/**\n * Expose compose.\n */\n\nmodule.exports = function compose(...processors) {\n  if (processors.length === 0)\n    return"
  },
  {
    "path": "lib/constant.js",
    "chars": 83,
    "preview": "const pkg = require('./../package')\n\nmodule.exports = {\n  PACKAGE_NAME: pkg.name\n}\n"
  },
  {
    "path": "lib/fetch.js",
    "chars": 646,
    "preview": "/**\n * Module dependencies.\n */\n\nconst fetch = require('node-fetch')\n\n/**\n * Expose a wrapped fetch which handles networ"
  },
  {
    "path": "lib/html.js",
    "chars": 1671,
    "preview": "/**\n * Module dependencies.\n */\n\nconst cheerio = require('cheerio')\nconst compose = require('./compose')\nconst escapeHtm"
  },
  {
    "path": "lib/index.js",
    "chars": 6694,
    "preview": "/**\n * Module dependencies.\n */\n\nconst assert = require('assert')\nconst { chalk, parseFrontmatter, path } = require('@vu"
  },
  {
    "path": "lib/spinner.js",
    "chars": 524,
    "preview": "/**\n * Module dependencies.\n */\n\nconst ora = require('ora')\nconst { chalk } = require('@vuepress/shared-utils')\n\n/**\n * "
  },
  {
    "path": "lib/stack.js",
    "chars": 318,
    "preview": "/**\n * Expose a simple stack\n */\n\nmodule.exports = class Stack {\n  constructor() {\n    this.elements = []\n  }\n\n  push(el"
  },
  {
    "path": "lib/store.js",
    "chars": 365,
    "preview": "/**\n * Module dependencies.\n */\n\nconst Conf = require('conf')\n\n/**\n * Expose a object containing the utilities of cache."
  },
  {
    "path": "lib/style.styl",
    "chars": 201,
    "preview": ".yuque-link\n  border: 1px solid #333;\n  padding: 5px 10px;\n  color: #333;\n  border-radius: 3px;\n  font-weight: 200;\n  te"
  },
  {
    "path": "lib/toc.js",
    "chars": 3350,
    "preview": "/**\n * Module dependencies.\n */\n\nconst Stack = require('./stack')\n\n/**\n * Expose a object containing the utilities of TO"
  },
  {
    "path": "lib/yuque.js",
    "chars": 3693,
    "preview": "const assert = require('assert')\nconst url = require('url')\nconst wfetch = require('./fetch')\nconst store = require('./s"
  },
  {
    "path": "package.json",
    "chars": 1390,
    "preview": "{\n  \"name\": \"vuepress-plugin-yuque\",\n  \"version\": \"0.6.1\",\n  \"description\": \"I: Yuque Repo, O: VuePress Site!\",\n  \"main\""
  },
  {
    "path": "test/index.test.js",
    "chars": 122,
    "preview": "const vuepressPluginYuque = require('../')\n\ntest('main', () => {\n  expect(typeof vuepressPluginYuque).toBe('function')\n}"
  }
]

About this extraction

This page contains the full source code of the ulivz/vuepress-plugin-yuque GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 28 files (38.8 KB), approximately 13.1k tokens, and a symbol index with 48 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!