Full Code of MuYunyun/create-react-doc for AI

main 36c56cecb7ae cached
171 files
288.2 KB
92.1k tokens
29 symbols
1 requests
Download .txt
Showing preview only (337K chars total). Download the full file or copy to clipboard to get everything.
Repository: MuYunyun/create-react-doc
Branch: main
Commit: 36c56cecb7ae
Files: 171
Total size: 288.2 KB

Directory structure:
gitextract_uke23iaa/

├── .editorconfig
├── .eslintrc.js
├── .github/
│   ├── FUNDING.yml
│   ├── config.yml
│   ├── dependabot.yml
│   ├── stale.yml
│   └── workflows/
│       ├── gh-pages.yml
│       ├── greetings.yml
│       └── traffic2badge.yml
├── .gitignore
├── .npmignore
├── .npmrc
├── .yarnrc
├── CONTRIBUTING.md
├── README-en.md
├── README.md
├── components/
│   ├── Button/
│   │   ├── index.jsx
│   │   └── index.less
│   └── index.jsx
├── config.yml
├── docs/
│   ├── Front-matter.md
│   ├── 主题/
│   │   ├── 自定义主题.md
│   │   └── 默认主题.md
│   ├── 书写组件.md
│   ├── 其它工具.md
│   ├── 快速上手.md
│   ├── 数学公式.md
│   ├── 更新日志.md
│   ├── 测试/
│   │   ├── 测试标签.md
│   │   └── 测试路由.md
│   ├── 站点发布.md
│   └── 高阶用法.md
├── gh-pages.js
├── injectLogic/
│   └── index.js
├── lerna.json
├── package.json
├── packages/
│   ├── crd-client-utils/
│   │   ├── .npmrc
│   │   ├── README.md
│   │   ├── index.js
│   │   └── package.json
│   ├── crd-generator-sitemap/
│   │   ├── .npmrc
│   │   ├── README.md
│   │   ├── generate.js
│   │   ├── index.js
│   │   └── package.json
│   ├── crd-scripts/
│   │   ├── .npmrc
│   │   ├── README.md
│   │   ├── index.js
│   │   ├── package.json
│   │   └── src/
│   │       ├── build.js
│   │       ├── commands/
│   │       │   ├── initProject.js
│   │       │   └── initTheme.js
│   │       ├── conf/
│   │       │   ├── getDirTree.js
│   │       │   ├── getPrerenderRoutes.js
│   │       │   ├── node-directory-tree.js
│   │       │   ├── path.js
│   │       │   ├── rawTreeReplaceLoader.js
│   │       │   ├── webpack.config.dev.js
│   │       │   ├── webpack.config.js
│   │       │   ├── webpack.config.prod.js
│   │       │   └── webpack.config.server.js
│   │       ├── deploy.js
│   │       ├── generate.js
│   │       ├── server.js
│   │       ├── utils/
│   │       │   ├── index.js
│   │       │   └── initCache.js
│   │       └── web/
│   │           ├── Router.js
│   │           ├── crd.json
│   │           └── index.js
│   ├── crd-seed/
│   │   ├── .npmrc
│   │   ├── README.md
│   │   ├── component/
│   │   │   ├── Affix/
│   │   │   │   ├── affix.js
│   │   │   │   ├── index.js
│   │   │   │   └── utils/
│   │   │   │       └── index.js
│   │   │   ├── Footer/
│   │   │   │   ├── index.js
│   │   │   │   └── index.less
│   │   │   ├── Header/
│   │   │   │   ├── index.js
│   │   │   │   └── index.less
│   │   │   ├── Icon/
│   │   │   │   ├── Icon.js
│   │   │   │   ├── iconsInfo.js
│   │   │   │   ├── index.js
│   │   │   │   ├── loadSprite.js
│   │   │   │   └── style/
│   │   │   │       └── index.less
│   │   │   ├── Menu/
│   │   │   │   ├── Menu.js
│   │   │   │   ├── MenuItem.js
│   │   │   │   ├── SubMenu.js
│   │   │   │   ├── context.js
│   │   │   │   ├── index.js
│   │   │   │   ├── style/
│   │   │   │   │   ├── index.less
│   │   │   │   │   └── theme.less
│   │   │   │   ├── transition.js
│   │   │   │   └── util.js
│   │   │   ├── NoMatch/
│   │   │   │   ├── index.js
│   │   │   │   └── index.less
│   │   │   ├── Search/
│   │   │   │   ├── README.md
│   │   │   │   ├── index.js
│   │   │   │   └── index.less
│   │   │   └── Tags/
│   │   │       ├── index.js
│   │   │       └── index.less
│   │   ├── index.js
│   │   ├── index.less
│   │   ├── language/
│   │   │   └── index.js
│   │   ├── layout/
│   │   │   ├── index.js
│   │   │   ├── index.less
│   │   │   └── utils.js
│   │   ├── package.json
│   │   ├── style/
│   │   │   ├── base.less
│   │   │   └── mobile.less
│   │   └── utils/
│   │       └── index.js
│   ├── crd-templates/
│   │   ├── .npmrc
│   │   ├── README.md
│   │   ├── default/
│   │   │   ├── .github/
│   │   │   │   └── workflows/
│   │   │   │       └── gh-pages.yml
│   │   │   ├── .npmrc
│   │   │   ├── Introduction/
│   │   │   │   └── hello_world.md
│   │   │   ├── README.md
│   │   │   ├── _.gitignore
│   │   │   ├── _config.yml
│   │   │   └── _package.json
│   │   ├── package.json
│   │   └── theme/
│   │       └── default/
│   │           ├── Introduction/
│   │           │   └── hello_world.md
│   │           ├── README.md
│   │           ├── _.gitignore
│   │           ├── _.npmrc
│   │           ├── _config.yml
│   │           ├── _index.js
│   │           ├── _index.less
│   │           └── _package.json
│   ├── crd-theme/
│   │   ├── .npmrc
│   │   ├── README.md
│   │   ├── component/
│   │   │   └── Loading/
│   │   │       ├── index.js
│   │   │       └── index.less
│   │   ├── index.html
│   │   ├── index.js
│   │   ├── index.less
│   │   ├── markdown/
│   │   │   ├── Link.js
│   │   │   ├── Link.less
│   │   │   ├── codeBlock.js
│   │   │   ├── index.js
│   │   │   └── style/
│   │   │       ├── css.less
│   │   │       ├── default.less
│   │   │       ├── diff.less
│   │   │       ├── index.less
│   │   │       ├── javascript.less
│   │   │       ├── swift.less
│   │   │       └── xml.less
│   │   └── package.json
│   ├── crd-utils/
│   │   ├── .npmrc
│   │   ├── README.md
│   │   ├── index.js
│   │   ├── package.json
│   │   └── path.js
│   ├── create-react-doc/
│   │   ├── .npmignore
│   │   ├── .npmrc
│   │   ├── .yarnrc
│   │   ├── README.md
│   │   ├── index.js
│   │   └── package.json
│   └── leetcode-cli/
│       ├── .npmrc
│       ├── README.md
│       ├── leetcode-table.md
│       ├── package.json
│       ├── problems.json
│       └── src/
│           ├── cli.js
│           ├── download.js
│           ├── leetcode-table.md
│           ├── leetcode.js
│           ├── logout.js
│           ├── problems.json
│           └── utils.js
└── utils/
    ├── uppackage-dev.sh
    └── uppackage.sh

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

================================================
FILE: .editorconfig
================================================
# http://editorconfig.org
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

[*.less]
indent_style = space
indent_size = 2

[Makefile]
indent_style = tab


================================================
FILE: .eslintrc.js
================================================
const eslintrc = {
  "parser": "babel-eslint",
  "extends": "airbnb",
  "env": {
    "browser": true,
    "node": true,
    "es6": true,
    "mocha": true,
    "jest": true,
    "jasmine": true
  },
  "plugins": [
    "react",
    "import"
  ],
  "parserOptions": {
    parser: 'babel-eslint',
  },
  "rules": {
    "linebreak-style": 0,
    "func-names": 0,
    "sort-imports": 0,
    "arrow-body-style": 0,
    "prefer-destructuring": 0,
    "max-len": 0,
    "consistent-return": 0,
    "comma-dangle": [
      "error",
      "always-multiline"
    ],
    "function-paren-newline": 0,
    "class-methods-use-this": 0,
    "react/sort-comp": 0,
    "react/prop-types": 0,
    "react/jsx-first-prop-new-line": 0,
    "react/require-extension": 0,
    "react/jsx-filename-extension": [
      1,
      {
        "extensions": [
          ".js",
          ".jsx"
        ]
      }
    ],
    "import/extensions": 0,
    "import/no-unresolved": 0,
    "import/no-extraneous-dependencies": 0,
    "import/prefer-default-export": 0,
    "jsx-a11y/no-static-element-interactions": 0,
    "jsx-a11y/anchor-has-content": 0,
    "jsx-a11y/click-events-have-key-events": 0,
    "jsx-a11y/anchor-is-valid": 0,
    "jsx-a11y/label-has-for": 0,
    "jsx-a11y/no-noninteractive-element-interactions": 0,
    "jsx-a11y/mouse-events-have-key-events": 0,
    "react/no-danger": 0,
    "react/jsx-no-bind": 0,
    "react/forbid-prop-types": 0,
    "react/require-default-props": 0,
    "react/no-did-mount-set-state": 0,
    "react/no-array-index-key": 0,
    "react/no-find-dom-node": 0,
    "react/no-unused-state": 0,
    "react/no-unused-prop-types": 0,
    "react/default-props-match-prop-types": 0,
    "react/jsx-curly-spacing": 0,
    "react/no-render-return-value": 0,
    'react/jsx-uses-react': 0,
    'react/react-in-jsx-scope': 0,
    "object-curly-newline": 0,
    "no-param-reassign": 0,
    "no-return-assign": 0,
    "no-redeclare": 0,
    "no-restricted-globals": 0,
    "no-restricted-syntax": 0,
    "no-underscore-dangle": 0,
    "no-unused-expressions": 0,
    "no-use-before-define": 0,
    "semi": ["error", "never"],
    "quotes": 0,
    "no-plusplus": 0
  }
}

if (process.env.NODE_ENV === 'development') {
  Object.assign(eslintrc.rules,
  {
    'no-console': 0,
    'no-unused-vars': 0,
  });
}

module.exports = eslintrc


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

github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # 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
# Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
custom: ['http://muyunyun.cn/sponsor/']


================================================
FILE: .github/config.yml
================================================
todo:
  keyword: "@makeAnIssue"


================================================
FILE: .github/dependabot.yml
================================================
# see https://github.com/yi-Xu-0100/traffic-to-badge/blob/main/.github/dependabot.yml
version: 2
updates:
  # Enable version updates for npm
  - package-ecosystem: 'npm'
    directory: '/'
    schedule:
      interval: 'daily'
  # Maintain dependencies for GitHub Actions
  - package-ecosystem: 'github-actions'
    directory: '/'
    schedule:
      interval: 'daily'


================================================
FILE: .github/stale.yml
================================================
# Configuration for probot-stale - https://github.com/probot/stale

# Number of days of inactivity before an Issue or Pull Request becomes stale
daysUntilStale: 60

# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
daysUntilClose: 7

# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled)
onlyLabels: []

# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
exemptLabels:
  - pinned
  - security
  - "[Status] Maybe Later"

# Set to true to ignore issues in a project (defaults to false)
exemptProjects: false

# Set to true to ignore issues in a milestone (defaults to false)
exemptMilestones: false

# Set to true to ignore issues with an assignee (defaults to false)
exemptAssignees: false

# Label to use when marking as stale
staleLabel: wontfix

# Comment to post when marking as stale. Set to `false` to disable
markComment: >
  This issue has been automatically marked as stale because it has not had
  recent activity. It will be closed if no further activity occurs. Thank you
  for your contributions.

# Comment to post when removing the stale label.
# unmarkComment: >
#   Your comment here.

# Comment to post when closing a stale Issue or Pull Request.
# closeComment: >
#   Your comment here.

# Limit the number of actions per hour, from 1-30. Default is 30
limitPerRun: 30

# Limit to only `issues` or `pulls`
# only: issues

# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls':
# pulls:
#   daysUntilStale: 30
#   markComment: >
#     This pull request has been automatically marked as stale because it has not had
#     recent activity. It will be closed if no further activity occurs. Thank you
#     for your contributions.

# issues:
#   exemptLabels:
#     - confirmed

================================================
FILE: .github/workflows/gh-pages.yml
================================================
name: GitHub Pages

on:
  push:
    branches:
      - main
  pull_request:

jobs:
  deploy:
    runs-on: ubuntu-22.04
    permissions:
      contents: write
    concurrency:
      group: ${{ github.workflow }}-${{ github.ref }}
    steps:
      - uses: actions/checkout@v3

      - name: Setup Node
        uses: actions/setup-node@v3
        with:
          node-version: '14'

      - name: Get yarn cache
        id: yarn-cache
        run: echo "YARN_CACHE_DIR=$(yarn cache dir)" >> "${GITHUB_OUTPUT}"

      - name: Cache dependencies
        uses: actions/cache@v3
        with:
          path: ${{ steps.yarn-cache.outputs.YARN_CACHE_DIR }}
          key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
          restore-keys: |
            ${{ runner.os }}-yarn-

      - run: yarn install --frozen-lockfile
      - run: yarn build

      - name: Deploy
        uses: peaceiris/actions-gh-pages@v3
        if: ${{ github.ref == 'refs/heads/main' }}
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: .crd-dist


================================================
FILE: .github/workflows/greetings.yml
================================================
name: Greetings

on: [pull_request, issues]

jobs:
  greeting:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/first-interaction@v1
      with:
        repo-token: ${{ secrets.GITHUB_TOKEN }}
        issue-message: 'Welcome your first issue here, thanks'
        pr-message: 'Welcome your first pr here, thanks'


================================================
FILE: .github/workflows/traffic2badge.yml
================================================
name: traffic2badge
on:
  push:
    branches:
      - main
  schedule:
    - cron: '1 0 * * *' #UTC

jobs:
  run:
    name: Make GitHub Traffic to Badge
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Code
        uses: actions/checkout@v2.3.3

      - name: Get Commit Message
        id: message
        uses: actions/github-script@v3.0.0
        env:
          FULL_COMMIT_MESSAGE: '${{ github.event.head_commit.message }}'
        with:
          result-encoding: string
          script: |
            var message = `${process.env.FULL_COMMIT_MESSAGE}`;
            core.info(message);
            if (message != '') return message;
            var time = new Date(Date.now()).toISOString();
            core.info(time);
            return `Get traffic data at ${time}`;

      - name: Set Traffic
        id: traffic
        uses: yi-Xu-0100/traffic-to-badge@v1.4.0
        with:
          my_token: ${{ secrets.TRAFFIC_TOKEN }}
          #(default) static_list: ${{ github.repository }}
          #(default) traffic_branch: traffic
          #(default) views_color: brightgreen
          #(default) clones_color: brightgreen
          #(default) logo: github
          #(default) year:

      - name: Deploy
        uses: peaceiris/actions-gh-pages@v3.7.3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_branch: ${{ steps.traffic.outputs.traffic_branch }}
          publish_dir: ${{ steps.traffic.outputs.traffic_path }}
          user_name: 'github-actions[bot]'
          user_email: 'github-actions[bot]@users.noreply.github.com'
          full_commit_message: ${{ steps.message.outputs.result }}

      - name: Show Traffic Data
        run: |
          echo ${{ steps.traffic.outputs.traffic_branch }}
          echo ${{ steps.traffic.outputs.traffic_path }}
          cd ${{ steps.traffic.outputs.traffic_path }}
          ls -a

================================================
FILE: .gitignore
================================================
node_modules
.create-react-doc-dist
package-lock.json
.cache
.DS_Store
.crd-dist/

*.bak
*.tem
*.log
*.temp
#.swp
*.*~
~*.*

docs/忽略文件.md

================================================
FILE: .npmignore
================================================
.cache
.gitignore
.editorconfig
.create-react-doc-dist
node_modules
package-lock.json
dist

================================================
FILE: .npmrc
================================================
# .npmrc

registry=https://registry.npmjs.org/


================================================
FILE: .yarnrc
================================================
# .yarnrc
registry "https://registry.npmjs.org/"


================================================
FILE: CONTRIBUTING.md
================================================
# HOW TO CONTRIBUTE

1. Welcome your pr! Before pr, talk about situations in the [issue](https://github.com/MuYunyun/create-react-doc/issues/new) firstly. If the situation is reasonable, go to the next step;
2. Switch to the new branch based main, submit the pr to branch `qa/latest` after finishing development.

## DEV

Run these bash command firstly.

```bash
$ git clone https://github.com/MuYunyun/create-react-doc
$ cd create-react-doc
$ yarn && yarn bootstrap && yarn start
```

And now you can see the document is running at http://localhost:3000.

## Test

After merging pr to qa/latest and publish beta package. You should verify the feature/bugfix with following bash:

```js
yarn add create-react-doc@beta
```

================================================
FILE: README-en.md
================================================
                                                     _.-"\
                                                _.-"      \
                                              ,-"          \
                                              \    create    \
                                              \ \    react    \
                                              \ \      doc     \
                                                \ \         _.-;
                                                \ \    _.-"   :
                                                  \ \,-"    _.-"
                                                  \(   _.-"
                                                    `--"

[![npm version](https://img.shields.io/npm/v/create-react-doc)](https://badge.fury.io/js/create-react-doc)
[![week download](https://img.shields.io/npm/dw/create-react-doc.svg)](https://www.npmjs.com/package/create-react-doc)
![views](https://raw.githubusercontent.com/MuYunyun/create-react-doc/traffic/traffic-create-react-doc/views.svg)
![views](https://raw.githubusercontent.com/MuYunyun/create-react-doc/traffic/traffic-create-react-doc/views_per_week.svg)
![clones](https://raw.githubusercontent.com/MuYunyun/create-react-doc/traffic/traffic-create-react-doc/clones_per_week.svg)
![LICENSE MIT](https://img.shields.io/npm/l/create-react-doc.svg)

English | [简体中文](./README.md)

# Create React Doc

[Create React Doc](https://github.com/MuYunyun/create-react-doc) is a markdown document site generation tool using React just like [create-react-app](https://github.com/facebook/create-react-app), developers can use Create React Doc to develop, deploy documents or blog sites without worrying about additional environment configuration information.

## Features

* The idea of ​​building a site: Just write markdown files as a blog site [like me](https://github.com/MuYunyun/blog).
* Out of box: One-click generation of documents and blog sites by specifying directories or documents, no need to care about site environment configuration information.
* Performance: greatly improve site loading speed through pre-rendering and lazy loading.
* Based on mdx: Support writing React components, mathematical formulas, etc. in markdown.
* Search engine optimization: Support SEO, making documents easier to search.
* Personalization: Support [custom theme](https://muyunyun.cn/create-react-doc/9f41fc98).
* Workflow: Integrate Github actions, support automated packaging and publishing sites.

> [Quick Start](https://muyunyun.cn/create-react-doc/290a4219)

## Subject

Create React Doc provides the official default theme [crd-seed](https://github.com/MuYunyun/create-react-doc/tree/main/packages/crd-seed). The theme supports the following features:

* Adapt to mobile and PC multi-terminal display.
* Support dark mode.
* The document supports embedded codepen, codesandbox.
* GitHub linkage.
* Support using tags to customize aggregate article content.

[my blog](http://muyunyun.cn/blog) is based [crd-seed](https://github.com/MuYunyun/create-react-doc/tree/main/packages/crd-seed) theme to build。

![](http://with.muyunyun.cn/90d3e357a31649b9466a828a92b6d88d.jpg)
![](http://with.muyunyun.cn/2e7440e4256debda2d73a4e6392c7146.jpg-300)

If you want to customize or share personal themes, you can refer to the [Custom Theme](https://muyunyun.cn/create-react-doc/9f41fc98) chapter.

## Get started quickly

**Create React Doc** is very easy to use. Developers don't need to install or configure additional tools such as webpack or Babel, they are built-in and hidden in the scaffolding, so developers can concentrate on document writing.

If you want to create a site file `doc` under the current file, here are three ways to quickly build a site:

### npx

```bash
npx create-react-doc doc
```

### npm

```bash
npm init create-react-doc doc
```

### yarn

```bash
yarn create react-doc doc
```

![](http://with.muyunyun.cn/0f0cf6e8cb68b18399eac2927f74b063.jpg)

> If you want to pull the content of the template to the current folder, you can replace the `doc` of the above command with `.`, such as executing `npx create-react-doc .`.

Then execute `cd doc && yarn && yarn start`, you can preview the site at `localhost: 3000`, if the site document changes, the site will automatically reload.

<img src="http://with.muyunyun.cn/2bbd4d8da3165e1a09a88f5e6a114009.jpg" width="900" />

## Site release

In the [Quick Start](http://muyunyun.cn/create-react-doc/QuickStart) section, it introduces how to quickly build a site. This section will introduce how to package and publish the built site to gh-pages.

### Automatically package and publish to gh-pages (recommended)

The initialized template project integrates the [ci configuration](https://github.com/MuYunyun/create-react-doc/blob/main/packages/templates/default/.github/workflows/gh-pages.yml) of `Github action`, the user only needs to execute `git push` on the main branch to complete the automatic deployment of the site.

![](http://with.muyunyun.cn/ea24d511f76efe5ba5d13bb6b1609aac.jpg)

If it is the first deployment, after performing the following operations, you need to select Github Pages as gh-pages in the setting tab of the project. (See [First Deployment with GITHUB_TOKEN](https://github.com/peaceiris/actions-gh-pages#%EF%B8%8F-first-deployment-with-github_token) for details)

```bash
git init
git add.
git commit -m "first commit"
git branch -M main
git remote add origin https://github.com/user or organization name/project name.git
git push -u origin main
```

> For more content, please visit [Site Release](http://muyunyun.cn/create-react-doc/SiteRelease), [Advanced Usage](http://muyunyun.cn/create-react-doc/HighOrderusage), [other tools](http://muyunyun.cn/create-react-doc/othertools) and other chapters.

## Practice Sharing

* [基于 SSR 的预渲染首屏直出方案](http://muyunyun.cn/blog/g3v1c5bq)
* [SEO 在 SPA 站点中的实践](http://muyunyun.cn/blog/ettzfags)


================================================
FILE: README.md
================================================
<!--
abbrlink: s31w9gd1
-->
                                                     _.-"\
                                                _.-"      \
                                              ,-"          \
                                              \    create    \
                                              \ \    react    \
                                              \ \      doc     \
                                                \ \         _.-;
                                                \ \    _.-"   :
                                                  \ \,-"    _.-"
                                                  \(   _.-"
                                                    `--"

[![npm version](https://img.shields.io/npm/v/create-react-doc)](https://badge.fury.io/js/create-react-doc)
[![week download](https://img.shields.io/npm/dw/create-react-doc.svg)](https://www.npmjs.com/package/create-react-doc)
![views](https://raw.githubusercontent.com/MuYunyun/create-react-doc/traffic/traffic-create-react-doc/views.svg)
![views](https://raw.githubusercontent.com/MuYunyun/create-react-doc/traffic/traffic-create-react-doc/views_per_week.svg)
![clones](https://raw.githubusercontent.com/MuYunyun/create-react-doc/traffic/traffic-create-react-doc/clones_per_week.svg)
![LICENSE MIT](https://img.shields.io/npm/l/create-react-doc.svg)

[English](./README-en.md) | 简体中文

# Create React Doc

[Create React Doc](https://github.com/MuYunyun/create-react-doc) 是一个使用 React 的 markdown 文档站点生成工具。就像 [create-react-app](https://github.com/facebook/create-react-app) 一样,开发者可以使用 Create React Doc 来开发、部署文档或者博客站点而无需关心额外的环境配置信息。

## 特性

* 建站理念: `文件即站点` (Files as a Site)。
* 开箱即用: 通过指定目录或文档, 一键生成文档、博客站点, 无需关心站点环境配置信息。
* 流畅的用户体验: 内置 SSR 首屏直出方案(基于 gp-pages 服务),以提升用户体验。
* 基于 mdx: 支持在 markdown 中`书写 React 组件`、数学公式等。
* 搜索引擎优化: 支持 SEO, 让文档更易被搜索。
* 个性化: 支持[自定义主题](https://muyunyun.cn/create-react-doc/9f41fc98)。
* 工作流: 集成 Github action, 支持自动化打包、发布站点。

> [快速上手](https://muyunyun.cn/create-react-doc/290a4219)

## 主题

create-react-doc 提供了默认主题 [crd-seed](https://github.com/MuYunyun/create-react-doc/tree/main/packages/crd-seed)。

该主题支持以下特性:

- [x] 适配网页/移动端展示。
- [x] 支持暗黑模式。
- [x] 支持标签页自定义聚合文章内容。
- [x] 内置评论模块。
- [x] 支持内嵌展示 codepen、codesandbox 案例。
- [x] 支持从文档页快速跳转到对应的 Github 文档页进行在线编辑。

该主题效果可以参考[笔者博客](http://muyunyun.cn/blog)。

![](http://with.muyunyun.cn/90d3e357a31649b9466a828a92b6d88d.jpg)
![](http://with.muyunyun.cn/2e7440e4256debda2d73a4e6392c7146.jpg-300)

如果您想定制化或者分享个人主题,可以参考[自定义主题](https://muyunyun.cn/create-react-doc/9f41fc98)章节。

## 快速上手

**create-react-doc** 非常容易上手。开发者不需要额外安装或配置 webpack 或者 Babel 等工具,它们被内置隐藏在脚手架中,因此开发者可以专心于文档的书写。

如果你想在当前文件下建立站点文件 `doc`, 这里提供如下三种方式快速建站:

### npx

```bash
npx create-react-doc doc
```

### npm

```bash
npm init create-react-doc doc
```

### yarn

```bash
yarn create react-doc doc
```

![](http://with.muyunyun.cn/0f0cf6e8cb68b18399eac2927f74b063.jpg)

> 如果想把模板内容内容拉取到当前文件夹, 则可以将如上命令的 `doc` 替换为 `.`, 比如执行 `npx create-react-doc .`。

接着执行 `cd doc && yarn && yarn start`, 可以在 `localhost: 3000` 预览站点, 如果站点文档发生改变, 站点将自动重新加载。

<img src="http://with.muyunyun.cn/2bbd4d8da3165e1a09a88f5e6a114009.jpg" width="900" />

## 站点发布

在 [快速上手](https://muyunyun.cn/create-react-doc/290a4219) 一节中介绍了如何快速搭建站点, 本节将介绍如何将搭建好的站点打包、发布到 gh-pages。

### 自动打包发布到 gh-pages (推荐)

初始化的模板项目集成了 `Github action` 的 [ci 配置](https://github.com/MuYunyun/create-react-doc/blob/main/packages/templates/default/.github/workflows/gh-pages.yml), 使用方只需在 main 分支执行 `git push` 即可以完成站点的自动部署。

![](http://with.muyunyun.cn/ea24d511f76efe5ba5d13bb6b1609aac.jpg)

如果是第一次部署, 在执行以下操作后, 需要在项目的 setting 选项卡中将 Github Pages 选择为 gh-pages。(详情见 [First Deployment with GITHUB_TOKEN](https://github.com/peaceiris/actions-gh-pages#%EF%B8%8F-first-deployment-with-github_token))

```bash
git init
git add .
git commit -m "first commit"
git branch -M main
git remote add origin https://github.com/用户或组织名/项目名.git
git push -u origin main
```

## 更多内容

* [站点发布](http://muyunyun.cn/create-react-doc/ude9296y)
* [高阶用法](http://muyunyun.cn/create-react-doc/9v9ug9h8)
* [其它工具](http://muyunyun.cn/create-react-doc/292h2c5k)
* [Front-matter](http://muyunyun.cn/create-react-doc/49g6b239)

## 实践分享

* [基于 SSR 的预渲染首屏直出方案](http://muyunyun.cn/blog/g3v1c5bq)
* [SEO 在 SPA 站点中的实践](http://muyunyun.cn/blog/ettzfags)


================================================
FILE: components/Button/index.jsx
================================================
import styles from './index.less'

const Button = ({
  children,
}) => {
  return <button className={styles.btn}>{children}</button>
}

export default Button


================================================
FILE: components/Button/index.less
================================================
.btn {
  color: #fff;
  background: #1890ff;
  border-color: #1890ff;
  text-shadow: 0 -1px 0 rgb(0 0 0 / 12%);
  box-shadow: 0 2px #0000000b;
  height: 40px;
  padding: 6.4px 15px;
  font-size: 16px;
  border-radius: 2px;
  line-height: 1.5715;
  position: relative;
  display: inline-block;
  font-weight: 400;
  white-space: nowrap;
  text-align: center;
}

================================================
FILE: components/index.jsx
================================================
import Button from './Button/index.jsx'

export {
  Button,
}


================================================
FILE: config.yml
================================================
# create-react-doc configuration.

# Site
title: Create React Doc

# Menu dir
## you can also set detailed dir, such as BasicSkill/css
## todo: auto menu
menu: [
  docs/快速上手.md,
  docs/更新日志.md,
  docs/主题,
  docs/站点发布.md,
  docs/Front-matter.md,
  docs/数学公式.md,
  docs/书写组件.md,
  docs/高阶用法.md,
  docs/其它工具.md,
  docs/测试
]
## set init open menu keys
# menuOpenKeys:

# site theme
devTheme: packages/crd-seed/index
# theme: crd-seed

# Github
## if you want to editing pages on github, you should config these arguments.
user: MuYunyun
repo: create-react-doc
github-ribbons: true
domain: https://muyunyun.cn
seo:
  google: true

# Available values: en | zh-cn
language: en

# Inject Custom Logic
inject: injectLogic/index.js

# Use abbrlink
abbrlink: true

# Show Tags in head
tags: true

# Config comment section
comment:
  # Giscus Config, The config parameter that's supported can be seen in [giscus-component](https://github.com/giscus/giscus-component#documentation).
  GiscusConfig:
    repoId: MDEwOlJlcG9zaXRvcnkyNjgwMTE4MzA=
    categoryId: DIC_kwDOD_mJNs4CSd1W


================================================
FILE: docs/Front-matter.md
================================================
<!--
abbrlink: 49g6b239
-->

## Front-matter

Front-matter 是文件最上方包裹在 `<!--` 与 `-->` 之间的区域,用于指定个别文件的变量,举例来说:

```md
<!--
abbrlink: 290a4219
tags: ['customTag1', 'customTag2']
-->
```

以下是预先定义的参数,您可在模板中使用这些参数值并加以利用。

| 参数     | 描述                                                            | 类型     |
| :------- | :-------------------------------------------------------------- | :------- |
| abbrlink | 短链。用于指定页面路由展示为指定短链,使用短链有助于 SEO 搜索。 |          |
| tags     | 自定义标签                                                      | string[] |

## 链接持久化

在以下场景需求场合中,可以展示短链以优化 URL 的显示。

* SEO 场景下需要链接持久化。
* URL 链接中存在中文会被转码展示。
* 文档的路径与文件名经常变更。

### 如何使用短链

在 `config.yml` 增加配置 `abbrlink: true`

```diff
+ abbrlink: true
```

做好上述配置后,接着在控制台执行 `react-doc generate` 即可给 menu 配置属性中的所有文章目录文件加上短链资源。

```bash
react-doc generate // 一键给所有文章加上短链
```


================================================
FILE: docs/主题/自定义主题.md
================================================
<!--
title: 自定义主题
abbrlink: 9f41fc98
date: 2019-05-12 13:23:44
tags: ['主题', '自定义标签1']
-->

## 使用自定义主题

切换主题非常简单, 只需要将根目录文件 `config.yml` 中的 `theme` 更改为您想使用的主题即可。

```diff
+ theme: custom-theme
```

### 如何开发自定义主题包

create-react-doc 脚手架提供了脚本命令 `react-doc theme` 用来一键创建主题包开发环境。

![](http://with.muyunyun.cn/2e4a4b11f96c0d38759700c05fe96267.gif)

```js
// 安装 create-react-doc
yarn add create-react-doc -g
// 执行 react-doc theme 并输入主题包名字
react-doc theme
```

进入到所创建主题目录, 执行 `yarn && yarn start`, 此时会自动打开浏览器, 并在屏幕中央显示 `Write docs happily now.`。如下图所示:

![](http://with.muyunyun.cn/1a2bf34700afd77a95014d2d5f359ffa.jpg)

恭喜你, 此时你已经将主题开发环境配置完成。接着便可以开始愉快地定制个人主题了。

在所创建的主题项目中使用了 `react v18`,`react-router-dom v6`,项目支持使用 `less` 语法。

```js
import { Switch, Route } from 'react-router-dom'
import styles from './index.less'

const CustomTheme = (props: CustomThemeProps) => {
  return (
    <Routes>
      <Route path="/">
        <div className={styles.center}>Welcome to your own theme</div>
      </Route>
    </Routes>
  )
}

export default CustomTheme
```

CustomThemeProps 的接口类型暴露了菜单资源 `menuSource` 与路由资源 `routeData`, 自定义主题时可以按需使用它们。

```js
interface CustomThemeProps {
  /** 菜单资源 */
  menuSource: {
    /** 文件名称 eg: '快速上手.md' */
    name: string
    /** 文件扩展名 eg: '.md' */
    extension: string
    /** 文件路径 eg: '/docs/快速上手.md' */
    path: string
    /** 路由路径 eg: ‘/快速上手’ */
    routePath: string
    /** 文件大小 eg: 924 */
    size: number
    /** 文件类型 eg: 'file' */
    type: string
    /** 文件创建日期 eg: '2020-11-11' */
    birthtime: string
    /** 文件修改日期 eg: '2021-01-14' */
    mtime: string
  }[]
  /** 路由资源 */
  routeData: {
    /** 文件名称 eg: '快速上手.md' */
    article: string
    /** 异步加载 markdown 组件函数 */
    component: AsyncRouteComponent(props)
    /** markdown 文章信息对象。若为文件则有 title 字段, 若为文件夹则无 title 字段 */
    mdconf: { title?: string }
    /** 文件路径 eg: '/docs/快速上手' */
    path: string
  }[]
}
```

此外在自定义主题文件中可以自由使用由 webpack 注入的 `DOCSCONFIG` 对象中的变量, DOCSCONFIG 中的变量与项目根目录中的 `config.yml` 文件变量一一对应。

比如 `config.yml` 配置如下所示:

```bash
menu: ['Introduction']
theme: crd-seed
user: muyunyun
repo: https://github.com/MuYunyun/create-react-doc
language: en
```

则主题项目中可以通过如下方式获取到 `config.yml` 配置属性。

```js
const { menu, theme, user } = DOCSCONFIG || {}
```


================================================
FILE: docs/主题/默认主题.md
================================================
<!--
abbrlink: 85li8wdd
-->

## 默认主题

create-react-doc 的默认主题为 [crd-seed](https://github.com/MuYunyun/create-react-doc/tree/main/packages/crd-seed)。

该主题支持以下特性:

- [x] 适配网页/移动端展示。
- [x] 支持暗黑模式。
- [x] 支持标签页自定义聚合文章内容。
- [x] 内置评论模块。
- [x] 支持内嵌展示 codepen、codesandbox 案例。
- [x] 支持从文档页快速跳转到对应的 Github 文档页进行在线编辑。

使用该主题搭建的项目有:

* [blog](https://github.com/MuYunyun/blog), [站点](http://muyunyun.cn/blog)
  * ![](http://with.muyunyun.cn/90d3e357a31649b9466a828a92b6d88d.jpg)
  * ![](http://with.muyunyun.cn/2e7440e4256debda2d73a4e6392c7146.jpg-300)
* [diana](https://github.com/MuYunyun/diana), [站点](https://muyunyun.cn/diana/)

> 如果您有其它的改进优化想法, 欢迎<a href="https://github.com/MuYunyun/create-react-doc/issues/new" target="_blank">留言补充</a>

## config.yml

[config.yml](https://github.com/MuYunyun/create-react-doc/blob/main/packages/templates/default/_config.yml) 文件是配置站点主题功能的地方。

它支持配置的属性如下:

|     属性名     |                 作用                 |                                        类型                                         |   默认   |
| :------------: | :----------------------------------: | :---------------------------------------------------------------------------------: | :------: |
|     title      |                站点名                |                                       string                                        |          |
|      menu      |    作为站点菜单的文件/文件夹路径     |                                      string[]                                       |          |
|  menuOpenKeys  |       默认展开菜单的文件夹路径       |                                       string                                        |          |
|      user      |            Github 用户名             |                                       string                                        |          |
|      repo      |            Github 项目名             |                                       string                                        |          |
|    language    |               站点语言               |                                     en \| zh-cn                                     |    en    |
| github-ribbons |     是否在右上角显示 github 丝带     |                                       boolean                                       |  false   |
|     theme      |               使用主题               |                                       string                                        | crd-seed |
|    devTheme    |  开发自定义主题时, 需设置其为 true   |                                       string                                        | ./index  |
|      seo       |          是否开启 SEO 优化           |                                { google?: boolean }                                 |          |
|     domain     | SEO 优化的站点域名, 用于生成 sitemap |                                       string                                        |          |
|    comment     |      开启评论区,并进行相关配置      | { GiscusConfig: [Props](https://github.com/giscus/giscus-component#documentation) } |          |

详细用法可以参考 [config.yml](https://github.com/MuYunyun/blog/blob/main/config.yml)。

================================================
FILE: docs/书写组件.md
================================================
<!--
abbrlink: lx1euo1b
tags: ['组件']
-->

import { Button } from '../components/index.jsx'

## 书写组件

[create-react-doc](https://github.com/MuYunyun/create-react-doc) 内置了 [mdx](https://github.com/mdx-js/mdx), 你可以在 .md 文件中书写 React 组件, 因此你可以选择 create-react-doc 来快速搭建组件站点。

## 例子
### Button 组件

```js
import { Button } from '../components/index.jsx'

<Button>Primary</Button>
```

<Button>Primary</Button>

================================================
FILE: docs/其它工具.md
================================================
<!--
abbrlink: 292h2c5k
-->

## 其它工具

### crd-leetcode-cli

#### 背景

当新增 LeetCode 题解时需要[手动更新表格](https://github.com/MuYunyun/blog/blob/main/LeetCode/README.md), 较为不便。[crd-leetcode-cli](https://github.com/MuYunyun/create-react-doc/tree/main/packages/leetcode-cli) 提供了更新 leetcode 站点中已 ac 题解的能力。

#### 安装

执行 `yarn add crd-leetcode-cli -g`, 国内用户可以执行 `cnpm install crd-leetcode-cli -g`

#### 使用

```bash
leetcode download       // 增量拉取 AC 题目(若无登录, 则会先执行登录逻辑)
leetcode download -a    // 全量拉取 AC 题目
leetcode login          // 登录
leetcode logout         // 登出
```

#### 自定义渲染表格

插件提供了自定义渲染 markdown table 的能力。

在项目根目录创建 [config.js](https://github.com/MuYunyun/blog/blob/main/config.js) 文件。

在 config.js 内自定义生成 markdown 的 [transform_markdown_table 函数](https://github.com/MuYunyun/blog/blob/main/config.js#L5-L22)。

```js
const transform_markdown_table = (dataArr: QuestionProps[]): string => {}
module.exports = { transform_markdown_table }
```

QuestionProps 接口定义如下:

|    名称    |       含义       |  例子   |
| :--------: | :--------------: | :-----: |
| questionId |       题号       |         |
|   title    |       标题       | Two Sum |
| titleSlug  | 标题的另一种模式 | two-sum |
| difficulty |       难度       |         |
| topicTags  |   题目所属标签   |         |

通过自定义 transform_markdown_table 函数, 便可得到如下 markdown table:

![](http://with.muyunyun.cn/1938e43a45410090e8486e495e6d9fee.jpg)

#### 技术细节

* 使用 puppeteer 登录 leetcode 获取 cookie 信息。
* 获取 cookie 后, 使用 graphql-request 调用 graphql 接口获取题目详情信息。
* 自定义生成 markdown table。

================================================
FILE: docs/快速上手.md
================================================
<!--
title: 快速上手
abbrlink: 290a4219
date: 2019-05-12 13:23:44
-->

## 快速上手

**create-react-doc** 非常容易上手。开发者不需要额外安装或配置 webpack 或者 Babel 等工具,它们被内置隐藏在脚手架中,因此开发者可以专心于文档的书写。

如果你想在当前文件下建立站点文件 `doc`, 这里提供如下三种方式快速建站:

### npx

```bash
npx create-react-doc doc
```

### npm

```bash
npm init create-react-doc doc
```

### yarn

```bash
yarn create react-doc doc
```

![](http://with.muyunyun.cn/0f0cf6e8cb68b18399eac2927f74b063.jpg)

> 如果想把模板内容内容拉取到当前文件夹, 则可以将如上命令的 `doc` 替换为 `.`, 比如执行 `npx create-react-doc .`。

接着执行 `cd doc && yarn && yarn start`, 可以在 `localhost: 3000` 预览站点, 如果站点文档发生改变, 站点将自动重新加载。

<img src="http://with.muyunyun.cn/2bbd4d8da3165e1a09a88f5e6a114009.jpg" width="900" />


================================================
FILE: docs/数学公式.md
================================================
<!--
abbrlink: slkczvi1
-->

## 数学公式

支持在 `$$` 与 `$$` 之间书写 latex 数学公式即能显示在网页上。

```js
$$
z = \frac{x}{y}
$$
```

被转化为:

$$
z = \frac{x}{y}
$$

```js
$$
C_1 \quad= \quad c_2 + c_4^3
$$
```

被转化为:

$$
C_1 \quad= \quad c_2 + c_4^3
$$

```js
$$
y = \sqrt {x_1^2 + x_2^2 + x_3^2 + x_4^2}
$$
```

被转化为:

$$
y = \sqrt {x_1^2 + x_2^2 + x_3^2 + x_4^2}
$$

## 参考链接

* [latex 数学公式示例汇总](https://zhuanlan.zhihu.com/p/34799800)


================================================
FILE: docs/更新日志.md
================================================
<!--
abbrlink: 179nqpxt
-->

# CHANGELOG

`create-react-doc` 严格遵循 [Semantic Versioning 2.0.0](http://semver.org/lang/zh-CN/) 语义化版本规范。

### 1.10.3

`2022-11-26`

- **Enhancement**

  - 🎈 更新主题字体样式为手写体风格。

### 1.10.2

`2022-11-19`

- **Fix**

  - 🐞 修复 @giscus/react 包结构变更导致站点崩溃的问题。[issue](https://github.com/giscus/giscus-component/issues/783)

- **Enhancement**

  - 🎈 使用 esbuild 代替 babel-loader 进行打包构建。[issue](https://github.com/MuYunyun/create-react-doc/issues/337)

### 1.10.0

`2022-11-10`

- **Feature**

  - 🚀 主题内置评论模块,支持在 [config.yml](https://muyunyun.cn/create-react-doc/85li8wdd) 配置开启评论模块。

### 1.9.2

`2022-04-09`

- **Fix**

  - 🐞 修复标签页包含重复标签归档的问题。[issue](https://github.com/MuYunyun/create-react-doc/issues/286)

### 1.9.1

`2022-04-05`

- **Enhancement**

  - 🎈 升级基础包版本。[issue](https://github.com/MuYunyun/create-react-doc/issues/278)
    - 更新 react 版本从 v17 至 v18
    - 更新 react-router-dom 版本从 v4 至 v6。

### 1.9.0

`2022-04-01`

- **Feature**

  - 🚀 支持配置展示标签页以自定义聚合文章内容。[issue](https://github.com/MuYunyun/create-react-doc/issues/264)

### 1.8.2

`2022-02-02`

- **Enhancement**

  - 🎈 支持在本地环境调试项目源代码。[mr](https://github.com/MuYunyun/create-react-doc/pull/249)

### 1.8.1

`2022-01-17`

- **Fix**

  - 🐞 修复 crd-scripts、crd-seed 遗漏指定安装 crd-client-utils 的问题。

### 1.8.0

`2022-01-16`

- **Feature**

  - 🚀 支持 SSR 首屏直出方案(基于 gp-pages 服务)以避免预渲染带来的二次刷新产生页面抖动的问题。[issue](https://github.com/MuYunyun/create-react-doc/issues/103)
  - 🚀 新增 crd-client-utils 包以收录公用方法。

### 1.7.0

`2022-01-02`

- **Feature**

  - 🚀 访问内容页路由时,收起菜单侧边栏以提高首屏加载体验。[issue](https://github.com/MuYunyun/create-react-doc/issues/219)
    - 优势一: 用户可以聚焦访问内容区,提升阅读体验。
    - 优势二: 菜单区域渲染内容复杂,隐藏其加载过程,提升首屏体验。
  - 🚀 菜单侧边栏展开后,选中项自动滚动到视口内。

### 1.6.1

`2021-12-27`

- **Fix**

  - 🐞 修复访问多层级子菜单目录对应的路由时,对应菜单未被展开的问题。[issue](https://github.com/MuYunyun/create-react-doc/issues/222)。

### 1.6.0

`2021-10-30`

- **Feature**

  - 🚀 SEO 搜索标题优化,优化 document.title 与 meta 标签。[issue](https://github.com/MuYunyun/create-react-doc/issues/203)

### 1.5.3

`2021-10-25`

- **Fix**

  - 🐞 修复在使用短链时,站点 sitemap.xml 文件生成丢失的问题。[issue](https://github.com/MuYunyun/create-react-doc/issues/206)。

### 1.5.2

`2021-10-19`

- **Fix**

  - 🐞 修复点击右上角 `Edit in GitHub` 链接跳转错误的问题。[issue](https://github.com/MuYunyun/create-react-doc/issues/205)。

### 1.5.1

`2021-10-19`

- **Fix**

  - 🐞 修复页面首屏菜单栏未高亮选中的问题。[issue](https://github.com/MuYunyun/create-react-doc/issues/195)。

### 1.5.0

`2021-10-13`

- **Feature**

  - 🚀 支持 react-doc generate 命令,给 md 文件自动补全短链。[issue](https://github.com/MuYunyun/create-react-doc/issues/87)、[mr](https://github.com/MuYunyun/create-react-doc/pull/194)

### 1.4.0

`2021-10-08`

- **Feature**

  - 🚀 支持展示短链以让文章链接持久化。[issue](https://github.com/MuYunyun/create-react-doc/issues/87)、[mr](https://github.com/MuYunyun/create-react-doc/pull/193)
    - 🚀 支持在 Front-matter 区域中书写个别文件的变量。

### 1.3.5

`2021-09-24`

- **Fix**

  - 🐞 修复路由过多时导致预渲染编译超时的问题。[issue](https://github.com/MuYunyun/create-react-doc/issues/182)
  - 🐞 修复初始化模板 menu 参数类型错误的问题。[mr](https://github.com/MuYunyun/create-react-doc/pull/181)

### 1.3.4

`2021-06-27`

- **Fix**

  - 🐞 修复 npx create-react-doc doc 初始化生成文档项目报错的问题。[issue](https://github.com/MuYunyun/create-react-doc/issues/157)。

### 1.3.3

`2021-06-24`

- **Fix**

  - 🐞 修复编译预渲染时, 缺少多层级目录文件生成的问题。[issue](https://github.com/MuYunyun/create-react-doc/issues/147)。

### 1.3.0

`2021-06-09`

- **Feature**

  - 🚀 create-react-doc 集成 MDX。[issue](https://github.com/MuYunyun/create-react-doc/issues/138)、[mr](https://github.com/MuYunyun/create-react-doc/pull/143)
    - 🚀 支持在 markdown 文件中`书写 React 组件`。
    - 🚀 支持在 markdown 文件中`书写数学公式`。

- **Enhancement**

  - 🎈 支持 yarn up、yarn up:dev 在 lerna 项目中快速安装包。[mr](https://github.com/MuYunyun/create-react-doc/pull/143/files?file-filters%5B%5D=.html&file-filters%5B%5D=.js&file-filters%5B%5D=.json&file-filters%5B%5D=.less&file-filters%5B%5D=.lock&file-filters%5B%5D=.sh)

### 1.2.0

- **Fix**

  - 🐞 修复路由数量过多, puppeteer 运行超时的问题。[issue](https://github.com/MuYunyun/blog/issues/115)。

### 1.1.4

- **Fix**

  - 🐞 修复点击 Edit In Github 失效的问题。[issue](https://github.com/MuYunyun/create-react-doc/issues/86)。

### 1.1.0

- **Feature**

  - 🚀 新增 crd-generator-sitemap 包, 用于生成 sitemap。
  - 🚀 配置文件中新增 domain 字段用于生成 sitemap。

### 1.0.0

- **Feature**

  - 🚀 文档支持预渲染。[pr](https://github.com/MuYunyun/create-react-doc/pull/95/files)
    - 🚀 路由由 hash 路由调整为 browser 路由。
    🎈 站点 SEO 优化。[doc](https://github.com/MuYunyun/blog/issues/84#issuecomment-786418891)
  - 🚀 配置文件中 menu 字段类型从 string 调整为 array。

### 0.3.30

- **Feature**

  - 🚀 支持显示展示 pv、uv。[pr](https://github.com/MuYunyun/create-react-doc/pull/85)

### 0.2.29

- **Fix**

  - 🐞 修复首次进入菜单未高亮的问题。[issue](https://github.com/MuYunyun/create-react-doc/issues/78)。
### 0.2.28

- **Feature**

  - 🚀 支持自定义主题 ci。[pr](https://github.com/MuYunyun/create-react-doc/pull/80)

### 0.2.27

- **Feature**

  - 🚀 支持自定义主题。[pr](https://github.com/MuYunyun/create-react-doc/pull/77)

### 0.2.22

- **Feature**

  - 🚀 升级 React 16 至 17。[pr](https://github.com/MuYunyun/create-react-doc/pull/71)

### 0.2.21

- **Feature**

  - 🚀 升级 webpack4 至 webpack5。[pr](https://github.com/MuYunyun/create-react-doc/pull/65)

### 0.2.14

- **Feature**

  - 🚀 支持 inject 与 injectWithPathname 注入自定义逻辑。[pr](https://github.com/MuYunyun/create-react-doc/pull/65)

### 0.2.14

- **Feature**

  - 🚀 集成 Github action, 支持自动化打包、发布站点。

### 0.2.7

`2020-09-25`

- **Feature**

  - 🚀 提供 @crd/leetcode-cli 提供将 leetcode 已 AC 的题目转化为 markdown table 的能力。[pr](https://github.com/MuYunyun/create-react-doc/pull/22)

### 0.2.0

`2020-08-02`

- **Feature**

  - 🚀 站点支持全局搜索菜单标题与文件内容。[pr](https://github.com/MuYunyun/create-react-doc/pull/22)

### 0.1.20

`2020-07-13`

- **Fix**

  - 🐞 fix [20](https://github.com/MuYunyun/create-react-doc/issues/20)。
  - 🐞 fix [17](https://github.com/MuYunyun/create-react-doc/issues/17)。

- **Enhancement**

  - 🎈 项目结构重构为 monorepo。[pr](https://github.com/MuYunyun/create-react-doc/pull/16)

### 0.1.0

- **Feature**

  - 🚀 详细内容见 [issue](https://github.com/MuYunyun/create-react-doc/issues/2)

================================================
FILE: docs/测试/测试标签.md
================================================
<!--
abbrlink: a80c431d
tags: ['自定义标签1', '自定义标签2', '自定义标签1']
-->

该页面用来测试自定义标签。


================================================
FILE: docs/测试/测试路由.md
================================================
<!--
tags: ['自定义标签1']
-->

* 该页面用来测试未使用 abbrlink 的中文路径。


================================================
FILE: docs/站点发布.md
================================================
<!--
abbrlink: ude9296y
-->

## 站点发布

在 [快速上手](http://muyunyun.cn/create-react-doc/%E5%BF%AB%E9%80%9F%E4%B8%8A%E6%89%8B) 一节中介绍了如何快速搭建站点, 本节将介绍如何将搭建好的站点打包、发布到 gh-pages。

### 自动打包发布到 gh-pages (推荐)

初始化的模板项目集成了 `Github action` 的 [ci 配置](https://github.com/MuYunyun/create-react-doc/blob/main/packages/templates/default/.github/workflows/gh-pages.yml), 使用方只需在 main 分支执行 `git push` 即可以完成站点的自动部署。

![](http://with.muyunyun.cn/ea24d511f76efe5ba5d13bb6b1609aac.jpg)

如果是第一次部署, 在执行以下操作后, 需要在项目的 setting 选项卡中将 Github Pages 选择为 gh-pages。(详情见 [First Deployment with GITHUB_TOKEN](https://github.com/peaceiris/actions-gh-pages#%EF%B8%8F-first-deployment-with-github_token))

```bash
git init
git add .
git commit -m "first commit"
git branch -M main
git remote add origin https://github.com/用户或组织名/项目名.git
git push -u origin main
```

### 手动打包发布

如果需要发布到自定义服务器, 可以执行 `npm run build` 或者 `yarn build` 对将要发布的文档站点进行打包构建, 此时的文档网站已准备好进行部署。

接着将打包出的 `.crd-dist` 的文件资源同步到目标服务器即可。

================================================
FILE: docs/高阶用法.md
================================================
<!--
abbrlink: 9v9ug9h8
-->

## 高阶用法

与 git 文件结构类似, 如果在展示的文件夹中有私有文件不方便展示在文档站点, 可以在 `.gitignore` 文件中设置过滤文件, 这样它们就不会展示在文档站点中了。eg: [.gitignore](https://github.com/MuYunyun/blog/blob/main/.gitignore)

### 插入自定义脚本

在 `config.yml` 文件中加入 `inject` 字段。

```diff
+ inject: injectLogic/index.js
```

然后在根目录新建与 `inject` 字段相对应的文件, 声明 `injectWithPathname` 函数, 写入[自定义逻辑](https://github.com/MuYunyun/create-react-doc/injectLogic/index.js)。

```js
// perf injectWithPathname logic every pathname changes
const injectWithPathname = (pathname) => {}

module.exports = { injectWithPathname }
```

================================================
FILE: gh-pages.js
================================================
const ghPages = require('gh-pages')

ghPages.publish(
  '.crd-dist',
  {
    branch: 'gh-pages',
    repo: 'https://github.com/MuYunyun/create-react-doc.git',
  },
  (error) => {
    if (error) {
      console.error(error)
    } else {
      console.log('docs sync success')
    }
  }
)


================================================
FILE: injectLogic/index.js
================================================
/* eslint-disable no-empty */
// seo: perf inject logic only once
const inject = () => {
  // SEO for Google through https://search.google.com/search-console/welcome
  const meta = document.createElement('meta')
  meta.name = 'google-site-verification'
  meta.content = '7fyp1NuvXSRLM9KpMq5_YNE_0zFZkPnuV-SbVVFgWbI'
  document.head.appendChild(meta)
}

// perf injectWithPathname logic every pathname changes
const injectWithPathname = (pathname) => {}

module.exports = { inject, injectWithPathname }


================================================
FILE: lerna.json
================================================
{
  "version": "1.10.3",
  "command": {
    "bootstrap": {
      "npmClientArgs": [
        "--no-package-lock"
      ]
    },
    "publish": {
      "allowBranch": [
        "main",
        "qa/latest"
      ],
      "message": "chore: publish"
    }
  },
  "//": "set yarn workspaces in root",
  "useWorkspaces": true,
  "npmClient": "yarn"
}


================================================
FILE: package.json
================================================
{
  "name": "create-react-doc",
  "private": true,
  "workspaces": [
    "packages/*"
  ],
  "description": "Fast static generated site. Just write markdown file.",
  "homepage": "http://muyunyun.cn/create-react-doc",
  "bin": {
    "react-doc": "bin/react-doc.js"
  },
  "scripts": {
    "bootstrap": "lerna bootstrap",
    "bootstrap --hoist": "lerna bootstrap --hoist",
    "clean": "lerna clean",
    "start": "yarn bootstrap && node packages/create-react-doc/index.js start",
    "build": "node packages/create-react-doc/index.js build",
    "deploy": "node packages/create-react-doc/index.js deploy",
    "release": "lerna publish",
    "release-qa": "lerna publish --npm-tag=beta",
    "cleanup": "rm -rf node_modules/gh-pages/.cache",
    "deploy:site": "npm run cleanup && node gh-pages",
    "up:dev": "sh utils/uppackage-dev.sh",
    "up": "sh utils/uppackage.sh"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/MuYunyun/create-react-doc"
  },
  "keywords": [
    "react",
    "create-react-doc",
    "blog",
    "markdown"
  ],
  "author": "muyunyun",
  "license": "MIT",
  "devDependencies": {
    "eslint": "^4.19.1",
    "eslint-config-airbnb": "^16.1.0",
    "eslint-plugin-import": "^2.11.0",
    "eslint-plugin-jsx-a11y": "^6.0.3",
    "eslint-plugin-react": "^7.7.0",
    "gh-pages": "^3.1.0",
    "lerna": "^3.22.1"
  }
}


================================================
FILE: packages/crd-client-utils/.npmrc
================================================
# .npmrc

registry=https://registry.npmjs.org/

# https://github.com/sass/node-sass#binary-configuration-parameters
sass_binary_site=https://npm.taobao.org/mirrors/node-sass/

# https://github.com/Medium/phantomjs#deciding-where-to-get-phantomjs
# phantomjs_cdnurl=http://cnpmjs.org/downloads
phantomjs_cdnurl=https://npm.taobao.org/dist/phantomjs


================================================
FILE: packages/crd-client-utils/README.md
================================================
### crd-client-utils

[create-react-doc](https://github.com/MuYunyun/create-react-doc) 的工具包。

================================================
FILE: packages/crd-client-utils/index.js
================================================
import { useLayoutEffect, useEffect } from 'react'

const ifDev = env === 'dev'
const ifProd = env === 'prod'
const ifPrerender = window.__PRERENDER_INJECTED && window.__PRERENDER_INJECTED.prerender

// Not only Prod env but also [some part of Dev env](https://github.com/MuYunyun/create-react-doc/blob/main/packages/crd-scripts/src/web/index.js#L10-L13)
// need using useLayoutEffect. If meeting this case in the future, thinking about passing extra tag from <RouterRoot />.
const useEnhancedEffect = ifProd
  ? useLayoutEffect
  : useEffect

export {
  ifDev,
  ifProd,
  ifPrerender,
  useEnhancedEffect
}


================================================
FILE: packages/crd-client-utils/package.json
================================================
{
  "name": "crd-client-utils",
  "version": "1.8.2",
  "description": "Utils with create react doc",
  "main": "index.js",
  "repository": {
    "type": "git",
    "url": "https://github.com/MuYunyun/create-react-doc",
    "directory": "packages/utils"
  },
  "keywords": [],
  "publishConfig": {
    "access": "public"
  },
  "author": "muyunyun",
  "license": "MIT",
  "gitHead": "ffc5e4cbc94a7356da558c2dbf46e2f39bb8b199"
}


================================================
FILE: packages/crd-generator-sitemap/.npmrc
================================================
# .npmrc

registry=https://registry.npmjs.org/

# https://github.com/sass/node-sass#binary-configuration-parameters
sass_binary_site=https://npm.taobao.org/mirrors/node-sass/

# https://github.com/Medium/phantomjs#deciding-where-to-get-phantomjs
# phantomjs_cdnurl=http://cnpmjs.org/downloads
phantomjs_cdnurl=https://npm.taobao.org/dist/phantomjs


================================================
FILE: packages/crd-generator-sitemap/README.md
================================================
### crd-generator-sitemap

[create-react-doc](https://github.com/MuYunyun/create-react-doc) SEO 插件, 用于生成站点地图。

================================================
FILE: packages/crd-generator-sitemap/generate.js
================================================
const { getDocsConfig } = require('crd-utils')

// template for google SEO
// <url>
//   <loc>muyunyun.cn/blog/xxx</loc>
//   <lastmod>{{ sNow | formatDate }}</lastmod>
//   <changefreq>daily</changefreq>
//   <priority>1.0</priority>
// </url>

const docsConfig = getDocsConfig()

const template = content =>
  `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  ${content}
</urlset>
`

/**
 * generate sitemap for google.
 */
const generateSiteMap = (routes) => {
  const domain = docsConfig && docsConfig.domain
  const repo = docsConfig && docsConfig.repo
  let content = ''
  for (let i = 0; i < routes.length; i++) {
    if (i === routes.length - 1) {
      content +=
`  <url>
    <loc>${domain}/${repo}${routes[i]}</loc>
  </url>`
    } else {
      content +=
`  <url>
    <loc>${domain}/${repo}${routes[i]}</loc>
  </url>\n`
    }
  }
  return template(content)
}

module.exports = {
  generateSiteMap,
}


================================================
FILE: packages/crd-generator-sitemap/index.js
================================================
const { generateSiteMap } = require('./generate')

module.exports = {
  generateSiteMap,
}


================================================
FILE: packages/crd-generator-sitemap/package.json
================================================
{
  "name": "crd-generator-sitemap",
  "version": "1.3.4",
  "description": "generator sitemap for create-react-doc",
  "main": "index.js",
  "repository": {
    "type": "git",
    "url": "https://github.com/MuYunyun/create-react-doc",
    "directory": "packages/crd-generator-sitemap"
  },
  "keywords": [],
  "publishConfig": {
    "access": "public"
  },
  "author": "muyunyun",
  "license": "MIT",
  "gitHead": "ffc5e4cbc94a7356da558c2dbf46e2f39bb8b199"
}


================================================
FILE: packages/crd-scripts/.npmrc
================================================
# .npmrc

registry=https://registry.npmjs.org/

# https://github.com/sass/node-sass#binary-configuration-parameters
sass_binary_site=https://npm.taobao.org/mirrors/node-sass/

# https://github.com/Medium/phantomjs#deciding-where-to-get-phantomjs
# phantomjs_cdnurl=http://cnpmjs.org/downloads
phantomjs_cdnurl=https://npm.taobao.org/dist/phantomjs


================================================
FILE: packages/crd-scripts/README.md
================================================
                                                     _.-"\
                                                _.-"      \
                                              ,-"          \
                                              \    create    \
                                              \ \    react    \
                                              \ \      doc     \
                                                \ \         _.-;
                                                \ \    _.-"   :
                                                  \ \,-"    _.-"
                                                  \(   _.-"
                                                    `--"

# crd-scripts

crd-scripts 封装提供了一系列构建、发布脚本。


================================================
FILE: packages/crd-scripts/index.js
================================================
const initProject = require('./src/commands/initProject')
const initTheme = require('./src/commands/initTheme')
const initCache = require('./src/utils/initCache')
const Servers = require('./src/server')
const Build = require('./src/build')
const Deploy = require('./src/deploy')
const Generate = require('./src/generate')
const paths = require('./src/conf/path')

module.exports = {
  initProject,
  initTheme,
  initCache,
  Servers,
  Build,
  Deploy,
  Generate,
  paths,
}


================================================
FILE: packages/crd-scripts/package.json
================================================
{
  "name": "crd-scripts",
  "version": "1.10.3",
  "description": "Scripts using with Create React Doc",
  "main": "index.js",
  "dependencies": {
    "@mdx-js/loader": "^1.6.22",
    "@nuxtjs/friendly-errors-webpack-plugin": "^2.0.2",
    "babel-eslint": "^8.0.1",
    "chalk": "^4.1.0",
    "colors-cli": "^1.0.13",
    "copy-markdown-image-webpack-plugin": "^2.0.0",
    "copy-template-dir": "^1.3.0",
    "crd-client-utils": "^1.8.2",
    "crd-generator-sitemap": "^1.3.4",
    "crd-prerender-spa-plugin": "^0.2.0",
    "crd-theme": "^1.10.3",
    "crd-utils": "^1.5.0",
    "css-loader": "^0.28.7",
    "css-minimizer-webpack-plugin": "^1.2.0",
    "detect-port": "^1.2.2",
    "esbuild-loader": "^2.20.0",
    "eslint": "^7.11.0",
    "eslint-loader": "^4.0.2",
    "file-loader": "^1.1.11",
    "fs-extra": "^5.0.0",
    "gh-pages": "^1.2.0",
    "html-webpack-plugin": "^4.5.1",
    "less-loader": "^7.2.1",
    "loading-cli": "^1.0.6",
    "local-ip-url": "^1.0.1",
    "mini-css-extract-plugin": "^0.4.0",
    "open-browsers": "^1.1.1",
    "path-browserify": "^1.0.1",
    "postcss-flexbugs-fixes": "^3.3.1",
    "postcss-loader": "^2.0.9",
    "process": "^0.11.10",
    "rehype-katex": "^5.0.0",
    "remark-math": "^3.0.1",
    "rimraf": "^2.6.2",
    "string-replace-loader": "^3.0.1",
    "style-loader": "^0.19.1",
    "upath": "^1.0.2",
    "url-replace-loader": "^1.0.0",
    "webpack": "^5.12.2",
    "webpack-dev-middleware": "^3.7.1",
    "webpack-dev-server": "^3.8.1",
    "webpack-hot-dev-clients": "^1.0.4",
    "webpackbar": "^4.0.0",
    "write": "^1.0.3",
    "yamljs": "^0.3.0"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/MuYunyun/create-react-doc",
    "directory": "packages/scripts"
  },
  "keywords": [],
  "publishConfig": {
    "access": "public"
  },
  "author": "muyunyun",
  "license": "MIT",
  "gitHead": "ffc5e4cbc94a7356da558c2dbf46e2f39bb8b199"
}


================================================
FILE: packages/crd-scripts/src/build.js
================================================
const webpack = require('webpack')
const fs = require('fs')
const { docsConfig } = require('crd-utils')
const conf = require('./conf/webpack.config.prod')
require('colors-cli/toxic')

module.exports = function serve(program) {
  if (!fs.existsSync(docsConfig)) {
    console.log('please check config.yml in root dir!\n')
    return
  }
  const webpackConf = conf(program)
  const compiler = webpack(webpackConf)
  compiler.run((err, stats) => {
    if (err) { throw err }
    // 官方输出参数
    // https://webpack.js.org/configuration/stats/
    // https://github.com/webpack/webpack/issues/538#issuecomment-59586196
    /* eslint-disable */
    console.log(stats.toString({
      colors: true,
      children: false,
      chunks: false,
      modules: false,
      errors: true,
      errorDetails: true,
      errorStack: true,
      warningsFilter: (warning) => {
        return true
      }
    }))
  })
}


================================================
FILE: packages/crd-scripts/src/commands/initProject.js
================================================
const path = require('path')
const { execSync } = require('child_process')
const fs = require('fs-extra')
const { templatePath, resolveApp } = require('crd-utils')
const copyTemplate = require('copy-template-dir')
const paths = require('../conf/path')

const log = console.log; // eslint-disable-line

// eslint-disable-next-line no-unused-vars
module.exports = function (params) {
  // process.argv[2] means the first argument after react-doc, eg react doc abc, process.argv[2] is abc
  const outDir = path.join(paths.projectPath, process.argv[2])
  const projectName = path.basename(outDir)

  const crdpkg = require(paths.crdPackage); // eslint-disable-line
  // replace the last vertion with x, so it'll install autoly when the last vertion changes.
  const CRD_VERSION = crdpkg.version.split('.').slice(0, 2).concat('x').join('.')

  // clear output dir
  if (!fs.pathExistsSync(outDir)) {
    fs.ensureDirSync(outDir)
  }

  const defaultTemplatePath = `${templatePath}/default`

  // copy template, see https://github.com/MuYunyun/create-react-doc/issues/50
  if (!fs.pathExistsSync(defaultTemplatePath)) {
    execSync('mkdir temp && cd temp && yarn add crd-templates -D')
  }
  const templatePathInTemp = resolveApp('temp/node_modules/crd-templates/default')
  if (fs.pathExistsSync(templatePathInTemp)) {
    copyTemplate(templatePathInTemp, outDir, {
      name: projectName,
      crdVersion: CRD_VERSION,
    }, (err, createdFiles) => {
      if (err) return log(`Copy Tamplate Error: ${err} !!!`.red)
      createdFiles.sort().forEach((createdFile) => {
        log(`  ${'create'.green} ${createdFile.replace(paths.projectPath, '')}`)
      })
      // to hack https://github.com/yoshuawuyts/copy-template-dir/issues/16
      execSync(`cp -r ${templatePathInTemp}/.github ${outDir}`)
      execSync('rm -rf temp')
      log('\n  initialization finished!\n'.green)
      const cmdstr = `cd ${projectName} && yarn && yarn start`.cyan
      log(`  Run the ${cmdstr} to start the website.\n\n`)
    })
  } else {
    log(` ❎ crd-templates install fail.\n\n`)
  }
}


================================================
FILE: packages/crd-scripts/src/commands/initTheme.js
================================================
/**
 * This file is to init theme quickly.
 */
const path = require('path')
const { execSync } = require('child_process')
const fs = require('fs-extra')
const { templatePath } = require('crd-utils')
const copyTemplate = require('copy-template-dir')
const paths = require('../conf/path')

const log = console.log; // eslint-disable-line

// eslint-disable-next-line no-unused-vars
module.exports = function (themeName) {
  const outDir = path.join(paths.projectPath, themeName)

  const crdpkg = require(paths.crdPackage); // eslint-disable-line
  // replace the last vertion with x, so it'll install autoly when the last vertion changes.
  const CRD_VERSION = crdpkg.version.split('.').slice(0, 2).concat('x').join('.')

  // clear output dir
  if (!fs.pathExistsSync(outDir)) {
    fs.ensureDirSync(outDir)
  }

  const defaultTemplatePath = `${templatePath}/theme/default`

  if (!fs.pathExistsSync(defaultTemplatePath)) {
    execSync('mkdir temp && cd temp && yarn add crd-templates -D')
  }
  copyTemplate(defaultTemplatePath, outDir, {
    name: themeName,
    crdVersion: CRD_VERSION,
  }, (err, createdFiles) => {
    if (err) return log(`Copy Tamplate Error: ${err} !!!`.red)
    createdFiles.sort().forEach((createdFile) => {
      log(`  ${'create'.green} ${createdFile.replace(paths.projectPath, '')}`)
    })
    execSync('rm -rf temp')
    log('\n  initialization finished!\n'.green)
    const cmdstr = `cd ${themeName} && yarn && yarn start`.cyan
    log(`  Run the ${cmdstr} to start the website.\n\n`)
  })
}


================================================
FILE: packages/crd-scripts/src/conf/getDirTree.js
================================================
const { directoryTree } = require('./node-directory-tree')

const getDirTree = (cmd) => {
  const dir = cmd.markdownPaths
  const dirs = Array.isArray(dir) ? dir : [dir]
  const otherProps = {
    mdconf: true,
    extensions: /\.md/,
    prerender: true,
  }
  const mapTagsWithArticle = []
  const dirTree = dirs.map(path => directoryTree({
    path,
    options: otherProps,
    mapTagsWithArticle
  }))
  return {
    dirTree,
    // map tags with path. [{ tagName: 'custom Tag 1', mapArticle: [{ path, name }]}]
    mapTagsWithArticle
  }
}

module.exports = {
  getDirTree,
}


================================================
FILE: packages/crd-scripts/src/conf/getPrerenderRoutes.js
================================================
// eg: ['docs/quick_start.md', 'a']
// output: ['/quick_start', '/a/b', '/a/b/c']
const getPrerenderRoutes = (dirTree) => {
  const dpCloneDirTree = JSON.parse(JSON.stringify(dirTree))
  const result = recursiveDirTree(dpCloneDirTree)
  result.push('/404')
  result.push('/tags')
  return result
}

function recursiveDirTree(data) {
  return recursive(
    data,
    '',
    []
  )
}

function recursive(
  data,
  routePath,
  prerenderRouteArr,
) {
  data.forEach((item) => {
    const { mdconf } = item || {}
    const { abbrlink, tags } = mdconf || {}
    const composeRouteName = `${routePath}/${item.name}`.replace(/.md$/, '')

    if (item.type === 'directory') {
      if (item.children && item.children.length > 0) {
        item.children = recursive(
          item.children,
          composeRouteName,
          prerenderRouteArr,
        )
      } else {
        item.children = []
      }
    } else if (item.type === 'file') {
      const prerenderRouteName = abbrlink
        ? `/${abbrlink}`
        : composeRouteName
      prerenderRouteArr.push(prerenderRouteName)
    }
  })
  return prerenderRouteArr
}

module.exports = {
  getPrerenderRoutes
}


================================================
FILE: packages/crd-scripts/src/conf/node-directory-tree.js
================================================
/* eslint-disable no-undef */
const fs = require('fs')
const PATH = require('path')
const YAML = require('yamljs')
const { execSync } = require('child_process')
const {
  replaceForFrontMatter,
  generateRandomId,
} = require('crd-utils')
const { getDigitFromDir, timeFormat } = require('../utils')

const constants = {
  DIRECTORY: 'directory',
  FILE: 'file',
}

function safeReadDirSync(path) {
  let dirData = {}
  try {
    /**
     * to make sure it's ordered like such rules, link issue: https://github.com/nodejs/node/issues/3232
     * 1.x                  1.x
     * 2.x    instead of   10.x
     * 10.x                 2.x
     */
    dirData = fs.readdirSync(path).sort((dir1, dir2) => {
      const dir1Digit = getDigitFromDir(dir1)
      const dir2Digit = getDigitFromDir(dir2)
      if (dir1Digit && dir2Digit) {
        return dir1Digit - dir2Digit
      }
      return 0
    })
  } catch (ex) {
    if (ex.code === 'EACCES')
    // User does not have permissions, ignore directory
    // eslint-disable-next-line brace-style
    { return null }
    throw ex
  }
  return dirData
}

// collect unique tags from all articles.
const tagsArr = []

/** build directory Tree, fork from https://github.com/mihneadb/node-directory-tree
 * path: path for file
 * options: {
 *   exclude: RegExp|RegExp[] - A RegExp or an array of RegExp to test for exclusion of directories.
 *   extensions : RegExp - A RegExp to test for exclusion of files with the matching extension.
 *   mdconf: Boolean.
 *   prerender: Boolean. Used for prerender.
 *   generate: Boolean. Used for generating info in front-matter.
 * }
 * mapTagsWithArticle: [{
 *  tagName: 'customTag1',    // tag type name
 *  mapArticle: [{
 *    path,                   // click tag to jump route such as /tags/customTag1
 *    title                   // the name of article
 *  }]
 * }]
 */
function directoryTree({
  path,
  options,
  routePath = '',
  mapTagsWithArticle
}) {
  const name = PATH.basename(path, '.md')
  const item = { name }
  const routePropsCurrent = `${routePath}/${name}`
  if (!options.prerender) {
    item.path = path
  }
  let stats
  try {
    stats = fs.statSync(path)
  } catch (e) {
    return null
  }

  // Skip if it matches the exclude regex
  if (options && options.exclude && options.exclude.test(path)) return null

  if (stats.isFile()) {
    const ext = PATH.extname(path).toLowerCase()

    // Skip if it does not match the extension regex
    if (options && options.extensions && !options.extensions.test(ext)) { return null }

    if (options && options.mdconf) {
      item.type = constants.FILE
      const contentStr = fs.readFileSync(path).toString()
      if (!contentStr) return
      const contentMatch = contentStr.match(/^<!--([^>]*)-->/)
      /** generate abbrlink in FrontMatter */
      if (options.generate) {
        const randomId = generateRandomId(8)
        if (!contentMatch) {
          replaceForFrontMatter({
            path,
            target: `<!--\nabbrlink: ${randomId}\n-->\n`
          })
        }
        if (contentMatch && contentMatch[1].indexOf('abbrlink') === -1) {
          replaceForFrontMatter({
            path,
            source: contentMatch[1],
            target: `\nabbrlink: ${randomId}${contentMatch[1]}`
          })
          console.log('✅ replaceForFrontMatter success')
        }
      }

      const yamlParse = contentMatch ? YAML.parse(contentMatch[1]) : {}
      const { tags: articleTags, abbrlink } = yamlParse
      if (Array.isArray(articleTags) && Array.isArray(mapTagsWithArticle)) {
        const cpArticleTags = Array.from(new Set(articleTags))
        for (let i = 0; i < cpArticleTags.length; i++) {
          const articleTag = cpArticleTags[i]
          const articleTagIndex = tagsArr.indexOf(articleTag)
          if (articleTagIndex > -1) {
            mapTagsWithArticle[articleTagIndex]['mapArticle'].push({
              path: abbrlink ? `/${abbrlink}` : routePropsCurrent,
              title: name
            })
          } else {
            tagsArr.push(cpArticleTags[i])
            mapTagsWithArticle.push({
              tagName: cpArticleTags[i],
              mapArticle: [{
                path: abbrlink ? `/${abbrlink}` : routePropsCurrent,
                title: name
              }]
            })
          }
        }
      }

      item.mdconf = yamlParse
      try {
        // see https://stackoverflow.com/questions/2390199/finding-the-date-time-a-file-was-first-added-to-a-git-repository/2390382#2390382
        const result = execSync(`git log --format=%aD ${path} | tail -1`)
        item.birthtime =
          Buffer.isBuffer(result) && timeFormat(new Date(result))
      } catch (error) {
        console.log(`❎ error: ${error.message}`)
      }
      try {
        // see https://stackoverflow.com/questions/22497597/get-the-last-modification-data-of-a-file-in-git-repo
        const result = execSync(`git log -1 --pretty="format:%ci" ${path}`)
        item.mtime = Buffer.isBuffer(result) && timeFormat(new Date(result))
      } catch (error) {
        console.log(`❎ error: ${error.message}`)
      }
      item.size = stats.size // File size in bytes
      item.extension = ext
      if (!options.prerender) {
        item.relative = item.path.replace(process.cwd(), '')
        item.isEmpty = contentMatch
          ? !String.prototype.trim.call(contentStr.replace(contentMatch[0], ''))
          : true
        const uglifyContent = contentStr.replace(/\s/g, '')
        item.content = uglifyContent
      }
    }
  } else if (stats.isDirectory()) {
    const dirData = safeReadDirSync(path)
    if (dirData === null) return null
    item.children = dirData
      .map(child =>
        directoryTree({
          path: PATH.join(path, child),
          options,
          routePath: routePropsCurrent,
          mapTagsWithArticle
        }),
      )
      .filter(e => !!e)
    item.type = constants.DIRECTORY
    if (!options.prerender) {
      item.size = item.children.reduce((prev, cur) => prev + cur.size, 0)
    }
  } else {
    return null // Or set item.size = 0 for devices, FIFO and sockets ?
  }
  return item
}

module.exports = {
  directoryTree
}


================================================
FILE: packages/crd-scripts/src/conf/path.js
================================================
const path = require('path')
const fs = require('fs')
const { execSync } = require('child_process')
const { resolveApp, docsConfig, getDocsConfig } = require('crd-utils')
const chalk = require('chalk')

// handle the problem of symbol in any platform
const appDirectory = fs.realpathSync(process.cwd())

const modPath = resolveApp('node_modules')
// get config crd from package.json
function getCrdConf() {
  const packagePath = resolveApp('./package.json')
  let conf = {}
  if (fs.existsSync(packagePath)) {
    const confPkg = require(packagePath); // eslint-disable-line
    conf = confPkg.crd
  }
  return conf
}

function getConfigFilePath(fileName, type) {
  const conf = getCrdConf()
  // read config
  if (conf && conf[type]) {
    // load theme dir
    if (type === 'theme') {
      if (!conf[type]) conf[type] = fileName
      const _path = path.resolve(appDirectory, 'theme', conf[type])
      const _NodeModulesPath = path.resolve(
        appDirectory,
        'node_modules',
        conf[type],
      )
      if (fs.existsSync(_path)) {
        return fs.realpathSync(_path)
      } else if (fs.existsSync(_NodeModulesPath)) {
        return fs.realpathSync(_NodeModulesPath)
      }
      return false
    }
    if (/^(favicon|logo)$/.test(type)) {
      return path.resolve(appDirectory, conf[type])
    }
  }
  const _filepath = path.resolve(appDirectory, fileName)
  if (fs.existsSync(_filepath)) {
    // favicon|logo in default root dir.
    return _filepath
  }
  return false
}

// Get favicon path
const faviconPath = () => {
  const _path = getConfigFilePath('./favicon.ico', 'favicon')
  if (_path) return _path
  // the path'll be writen dynamiclly in the future
  return resolveApp('node_modules/crd-theme/favicon.ico')
}

// Get logo path
const logoPath = () => {
  const _path = getConfigFilePath('./logo.svg', 'logo')
  if (_path) return _path
  return false
}

let theme = ''
// theme in develop mode.
let devTheme = false

const getTheme = () => {
  if (docsConfig) {
    const docsConfigObj = getDocsConfig()
    if (!docsConfigObj) return
    if (docsConfigObj.devTheme) {
      devTheme = docsConfigObj.devTheme
      theme = docsConfigObj.devTheme
    } else {
      theme = docsConfigObj.theme

      // install custom theme
      if (!fs.existsSync(resolveApp(`node_modules/${theme}`))) {
      // todo: chalkblue(xxx) not show in the terminal
        chalk.blue(`Install theme ${theme}`)
        // -W means ignore-workspace-root-check
        execSync(`yarn add ${theme} -D -W`)
        chalk.blue(`Install theme ${theme} done`)
      } else {
        chalk.blue(`Upgrade theme ${theme}`)
        // -W means ignore-workspace-root-check
        execSync(`yarn upgrade ${theme}`)
        chalk.blue(`Upgrade theme ${theme} done`)
      }
    }
  }
}

getTheme()

// get exclude folders
function getExcludeFoldersRegExp() {
  if (!fs.existsSync(modPath)) return []
  let regexp = fs.readdirSync(modPath)
  /** whitelist to include */
  const whiteListRegExp = new RegExp(`create-react-doc(.*)|crd-scripts|crd-theme|${theme}`)
  regexp = regexp.filter(
    item => !whiteListRegExp.test(item),
    // item => !/create-react-doc(.*)|crd-scripts|crd-theme/.test(item),
  )
  regexp = regexp.map((item) => {
    let rgxPath = `node_modules${path.sep}${item}`
    if (path.sep === '\\') {
      // to watch: is '\\' needful?
      rgxPath = `node_modules\\${path.sep}${item}`
    }
    return new RegExp(rgxPath)
  })
  return regexp
}

// crd tool dir
const toolDirectory = fs.realpathSync(__dirname)
const resolveTool = relativePath => path.resolve(toolDirectory, relativePath)

module.exports = {
  // markdown dir
  crdConf: getCrdConf(),
  defaultTheme: devTheme
    ? resolveApp(`${devTheme}`)
    : resolveApp(`node_modules/${theme}`),
  defaultNodeModules: modPath,
  projectPath: appDirectory,
  publicPath: '',
  logoPath: logoPath(),
  // crd tool dir
  getExcludeFoldersRegExp: getExcludeFoldersRegExp(),
  crdPackage: resolveTool('../../package.json'),
  defaultFaviconPath: faviconPath(),
  appIndexJs: resolveTool('../web/index.js'),
  appDir: resolveTool('../web'),
}


================================================
FILE: packages/crd-scripts/src/conf/rawTreeReplaceLoader.js
================================================
// A variation of https://github.com/react-doc/node-directory-tree-md/blob/master/lib/directory-tree-md.js
const { directoryTree } = require('./node-directory-tree')
const PATH = require('path')
const { ifInGitIgnore } = require('../utils/index')

function getAllWatchPath(arr, pathArr = []) {
  arr.forEach((item) => {
    const mdfilePathInProject = item.path.replace(
      process.cwd() + PATH.sep,
      '',
    )
    if (!ifInGitIgnore(mdfilePathInProject) && item.type === 'file') {
      pathArr.push(item.path)
    }
    if (item.children && item.children.length > 0) {
      pathArr.concat(getAllWatchPath(item.children, pathArr))
    }
  })
  return pathArr
}

function replacePath(dirs, path) {
  for (let i = 0; i < dirs.length; i += 1) {
    const element = PATH.dirname(dirs[i])
    const reg = new RegExp(`^${element}`, 'gi')
    if (reg.test(path)) {
      path = path.replace(reg, '')
      break
    }
  }
  return path
}

function getRelativePath(arr, relativePath, dirs) {
  const pathArr = []
  arr.forEach((item) => {
    if (relativePath && item.path) {
      item.path = replacePath(dirs, item.path)
    }
    if (item.children && item.children.length > 0) {
      item.children = getRelativePath(item.children, relativePath, dirs)
    }
    const notInGitIgnore = !ifInGitIgnore(item.path.replace(PATH.sep, ''))
    if (notInGitIgnore) {
      pathArr.push(item)
    }
  })
  return pathArr
}

module.exports = function (source) {
  // get option config from webpack loader, here is https://github.com/MuYunyun/create-react-doc/blob/main/packages/scripts/src/conf/webpack.config.prod.js#L61-L70
  const options = this.getOptions() || {}
  const { include, directoryTrees } = options
  const { dir, relativePath, ...otherProps } = directoryTrees
  let content = typeof source === 'string' ? JSON.parse(source) : source
  // It's said loader resuls are flagged as cacheable. See https://webpack.js.org/api/loaders/#thiscacheable.
  // if (this.cacheable) this.cacheable()
  if (directoryTrees && (!include || include.test(this.resourcePath))) {
    const dirs = Array.isArray(dir) ? dir : [dir]
    const dirTree = dirs.map(path => directoryTree({
      path,
      options: otherProps,
    }))

    const filemd = getAllWatchPath(dirTree)
    filemd.forEach((fileItem) => {
      this.addDependency(fileItem)
    })
    // replace full file path with relative path
    content = getRelativePath(
      dirTree,
      relativePath,
      dirs,
    )
  }
  content = JSON.stringify(content)
    .replace(/\u2028/g, '\\u2028')
    .replace(/\u2029/g, '\\u2029')

  return `module.exports = ${content}`
}


================================================
FILE: packages/crd-scripts/src/conf/webpack.config.dev.js
================================================
const autoprefixer = require('autoprefixer')
const webpack = require('webpack')
const path = require('path')
const upath = require('upath')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { defaultHTMLPath } = require('crd-utils')
const FriendlyErrorsWebpackPlugin = require('@nuxtjs/friendly-errors-webpack-plugin')
const { getDocsConfig } = require('crd-utils')
const { getDirTree } = require('./getDirTree')
const config = require('./webpack.config')
const paths = require('./path')

module.exports = function (cmd) {
  const docsConfig = getDocsConfig()
  const { mapTagsWithArticle } = getDirTree(cmd)
  config.mode = 'development'
  config.devtool = 'eval-source-map'
  config.entry = [
    require.resolve('react-hot-loader/patch'),
    require.resolve('webpack-hot-dev-clients/webpackHotDevClient'),
    paths.appIndexJs,
  ]
  config.output.publicPath = '/'
  config.module.rules = config.module.rules.map((item) => {
    if (item.oneOf) {
      const loaders = []
      loaders.push({
        // Process JS with Babel.
        test: /\.(js|jsx)$/,
        exclude: paths.getExcludeFoldersRegExp.concat(/\.(cache)/),
        use: [
          {
            loader: require.resolve('string-replace-loader'),
            options: {
              multiple: [
                { search: '__project_root__', replace: upath.normalizeSafe(paths.projectPath), flags: 'ig' },
                { search: '__project_theme__', replace: upath.normalizeSafe(paths.defaultTheme), flags: 'ig' },
              ],
            },
          },
          {
            loader: 'esbuild-loader',
            options: {
              loader: 'jsx',
              target: 'es2015',
              // This will make esbuild automatically generate import statements,
              // making the ProviderPlugin unnecesary if used only for "react".
              // Note that this option makes sense only when used in conjuction
              // with React >16.40.0 || >17
              // https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html
              jsx: 'automatic',
            }
          },
        ],
      })
      // https://ilikekillnerds.com/2018/03/disable-webpack-4-native-json-loader/
      loaders.push({
        test: /crd\.json$/,
        // 禁用 Webpack 4 本身的 JSON 加载程序
        type: 'javascript/auto',
        use: [
          {
            loader: `${path.join(__dirname, './rawTreeReplaceLoader.js')}`,
            options: {
              include: /crd\.json$/,
              directoryTrees: {
                dir: cmd.markdownPaths,
                mdconf: true,
                extensions: /\.md/,
                relativePath: true,
              },
            },
          },
        ],
      })

      loaders.push({
        test: /\.(css|less)$/,
        use: [
          require.resolve('style-loader'),
          {
            loader: require.resolve('css-loader'),
            options: {
              modules: true,
              localIdentName: '[local]--[hash:base64:5]',
              importLoaders: 1,
            },
          },
          {
            loader: require.resolve('postcss-loader'),
            options: {
              // Necessary for external CSS imports to work
              // https://github.com/facebookincubator/create-react-app/issues/2677
              ident: 'postcss',
              plugins: () => [
                require("postcss-flexbugs-fixes"), // eslint-disable-line
                autoprefixer({
                  browsers: [
                    '>1%',
                    'last 4 versions',
                    'Firefox ESR',
                    'not ie < 9', // React doesn't support IE8 anyway
                  ],
                  flexbox: 'no-2009',
                }),
              ],
            },
          },
          require.resolve('less-loader'),
        ],
      })

      item.oneOf = loaders.concat(item.oneOf)
    }
    return item
  })

  config.optimization = {
    // 将模块名称添加到工厂功能,以便它们显示在浏览器分析器中。
    // 当接收到热更新信号时,在浏览器 console 控制台打印更多可读性高的模块名称等信息
    moduleIds: 'named',
  }

  config.plugins = config.plugins.concat([
    new webpack.DefinePlugin({
      env: JSON.stringify('dev'),
      mapTagsWithArticle: JSON.stringify(mapTagsWithArticle)
    }),
    new webpack.HotModuleReplacementPlugin(),
    new HtmlWebpackPlugin({
      inject: true,
      favicon: paths.defaultFaviconPath,
      template: defaultHTMLPath,
      title: docsConfig && docsConfig.title ? docsConfig.title : 'Create React Doc',
    }),
    new FriendlyErrorsWebpackPlugin({
      clearConsole: true,
    }),
  ])
  return config
}


================================================
FILE: packages/crd-scripts/src/conf/webpack.config.js
================================================
/* eslint-disable global-require */
/* eslint-disable import/no-dynamic-require */
const webpack = require('webpack')
const webpackbar = require('webpackbar')
const fs = require('fs')
const { resolveApp, docsConfig, docsBuildDist } = require('crd-utils')
const { getDocsConfig } = require('crd-utils')
// const { getSearchContent } = require('../utils');
const remarkMath = require('remark-math')
const rehypeKatex = require('rehype-katex')
const paths = require('./path')
const pkg = require('../../package.json')

const define = {
  FOOTER: null,
  DOCSCONFIG: null,
  INJECT: null,
}
if (paths.crdConf && paths.crdConf.footer && typeof paths.crdConf.footer === 'string') {
  define.FOOTER = JSON.stringify(paths.crdConf.footer)
}
/* custom define docs config */
if (docsConfig) {
  // const searchContent = getSearchContent();
  const docsConfigObj = getDocsConfig()
  define.DOCSCONFIG = JSON.stringify(docsConfigObj)
  // todo: searchContent affects the performance, so take annotation here templately.
  // define.SEARCHCONTENT = searchContent && searchContent.toString();

  // if there is inject logic in docsConfigObj
  if (docsConfigObj && docsConfigObj.inject && fs.existsSync(resolveApp(docsConfigObj.inject))) {
    define.INJECT = require(resolveApp(docsConfigObj.inject))
  }
}

module.exports = {
  entry: {},
  output: {
    path: docsBuildDist,
    publicPath: paths.publicPath,
    filename: 'js/[name].[hash:8].js',
    chunkFilename: 'js/[name].[hash:8].js',
  },
  module: {
    rules: [
      {
        // “oneOf”将遍历所有以下加载程序,直到一个符合要求。
        // 当没有加载器匹配时,它将返回到加载程序列表末尾的“file”加载器。
        oneOf: [
          {
            test: /\.(svg|png|bmp|jpg|jpeg|gif)$/,
            loader: require.resolve('url-replace-loader'),
            options: {
              limit: 10000,
              name: 'img/[name].[hash:8].[ext]',
              replace: [
                {
                  test: /crd\.logo\.svg$/,
                  path: paths.logoPath,
                },
              ],
            },
          },
          {
            test: /\.md$/,
            use: [
              {
                loader: 'esbuild-loader',
                options: {
                  loader: 'jsx',
                  target: 'es2015',
                  // This will make esbuild automatically generate import statements,
                  // making the ProviderPlugin unnecesary if used only for "react".
                  // Note that this option makes sense only when used in conjuction
                  // with React >16.40.0 || >17
                  // https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html
                  jsx: 'automatic',
                }
              },
              {
                loader: require.resolve('@mdx-js/loader'),
                options: {
                  remarkPlugins: [
                    [
                      remarkMath,
                      {
                        /* options */
                      },
                    ],
                  ],
                  rehypePlugins: [
                    [
                      rehypeKatex,
                      {
                        /* options */
                      },
                    ],
                  ],
                },
              },
            ],
            exclude: /(node_modules)/,
          },
          // “file-loader”确保这些资源由WebpackDevServer服务。
          // 当您导入资源时,您将获得(虚拟)文件名。
          // 在生产中,它们将被复制到`build`文件夹。
          // 此加载程序不使用“test”,因此它将捕获所有模块
          {
            // 排除`js`文件以保持“css”加载器工作,因为它注入它的运行时,否则将通过“文件”加载器处理。
            // 还可以排除“html”和“json”扩展名,以便它们被webpacks内部加载器处理。
            exclude: [/\.js$/, /\.html$/, /\.json$/],
            loader: require.resolve('file-loader'),
            options: {
              name: 'static/[name].[hash:8].[ext]',
            },
          },
        ],
      },
    ],
  },
  plugins: [
    // eslint-disable-next-line new-cap
    new webpackbar({ name: pkg.name }),
    new webpack.DefinePlugin({
      VERSION: JSON.stringify(pkg.version),
      ...define,
    }),
    // fix "process is not defined" error:
    new webpack.ProvidePlugin({
      process: 'process/browser',
    }),
  ],
  resolve: {
    fallback: {
      path: require.resolve('path-browserify'),
    },
  },
}


================================================
FILE: packages/crd-scripts/src/conf/webpack.config.prod.js
================================================
const autoprefixer = require('autoprefixer')
const path = require('path')
const upath = require('upath')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const CopyMarkdownImageWebpackPlugin = require('copy-markdown-image-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const PrerenderSPAPlugin = require('crd-prerender-spa-plugin')
const { generateSiteMap } = require('crd-generator-sitemap')
const fs = require('fs-extra')
const { defaultHTMLPath, docsBuildDist } = require('crd-utils')
const { getDocsConfig } = require('crd-utils')
const config = require('./webpack.config')
const paths = require('./path')
const { getPrerenderRoutes } = require('./getPrerenderRoutes')
const { getDirTree } = require('./getDirTree')

const Renderer = PrerenderSPAPlugin.PuppeteerRenderer

module.exports = function (cmd) {
  const docsConfig = getDocsConfig()
  const { dirTree, mapTagsWithArticle } = getDirTree(cmd)
  const routes = getPrerenderRoutes(dirTree)

  config.mode = 'production'
  config.entry = [paths.appIndexJs]
  // config.output.filename = 'js/[hash:8].js'
  config.output.chunkFilename = 'js/[name].[hash:8].js'
  config.output.publicPath = docsConfig.repo ? `/${docsConfig.repo}/` : '/'
  config.output.path = docsConfig.repo ? `${docsBuildDist}/${docsConfig.repo}` : docsBuildDist
  config.module.rules = config.module.rules.map((item) => {
    if (item.oneOf) {
      const loaders = []
      loaders.push({
        // Process JS with Babel.
        test: /\.(js|jsx)$/,
        exclude: paths.getExcludeFoldersRegExp.concat(/\.(cache)/),
        use: [
          {
            loader: require.resolve('string-replace-loader'),
            options: {
              multiple: [
                { search: '__project_root__', replace: upath.normalizeSafe(paths.projectPath), flags: 'ig' },
                { search: '__project_theme__', replace: upath.normalizeSafe(paths.defaultTheme), flags: 'ig' },
              ],
            },
          },
          {
            loader: 'esbuild-loader',
            options: {
              loader: 'jsx',
              target: 'es2015',
              // This will make esbuild automatically generate import statements,
              // making the ProviderPlugin unnecesary if used only for "react".
              // Note that this option makes sense only when used in conjuction
              // with React >16.40.0 || >17
              // https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html
              jsx: 'automatic',
            }
          },
        ],
      })
      // https://ilikekillnerds.com/2018/03/disable-webpack-4-native-json-loader/
      loaders.push({
        test: /crd\.json$/,
        // 禁用 Webpack 4 本身的 JSON 加载程序
        type: 'javascript/auto',
        use: [
          {
            loader: `${path.join(__dirname, './rawTreeReplaceLoader.js')}`,
            options: {
              include: /crd\.json$/, // 检查包含的文件名字
              directoryTrees: {
                // 指定目录生成目录树,json
                dir: cmd.markdownPaths,
                mdconf: true,
                extensions: /\.md/,
                relativePath: true,
              },
            },
          },
        ],
      })

      loaders.push({
        test: /\.(css|less)$/,
        use: [
          MiniCssExtractPlugin.loader,
          {
            loader: require.resolve('css-loader'),
            options: {
              modules: true,
              localIdentName: '[local]-[hash:base64:5]',
              importLoaders: 1,
            },
          },
          {
            loader: require.resolve('postcss-loader'),
            options: {
              // Necessary for external CSS imports to work
              // https://github.com/facebookincubator/create-react-app/issues/2677
              ident: 'postcss',
              plugins: () => [
                require('postcss-flexbugs-fixes'), // eslint-disable-line
                autoprefixer({
                  browsers: [
                    '>1%',
                    'last 4 versions',
                    'Firefox ESR',
                    'not ie < 9', // React doesn't support IE8 anyway
                  ],
                  flexbox: 'no-2009',
                }),
              ],
            },
          },
          require.resolve('less-loader'),
        ],
      })

      item.oneOf = loaders.concat(item.oneOf)
    }
    return item
  })
  config.optimization = {
    // minimize: true,
    // minimizer: [
    //   // For webpack@5 you can use the `...` syntax to extend existing minimizers (i.e. `terser-webpack-plugin`), uncomment the next line
    //   // `...`,
    //   new CssMinimizerPlugin(),
    // ],
  }

  config.plugins = config.plugins.concat([
    new webpack.DefinePlugin({
      env: JSON.stringify('prod'),
      mapTagsWithArticle: JSON.stringify(mapTagsWithArticle)
    }),
    new HtmlWebpackPlugin({
      inject: true,
      favicon: paths.defaultFaviconPath,
      template: defaultHTMLPath,
      title:
        docsConfig && docsConfig.title ? docsConfig.title : 'Create React Doc',
      minify: {
        removeAttributeQuotes: true,
        collapseWhitespace: true,
        html5: true,
        minifyCSS: true,
        removeComments: true,
        removeEmptyAttributes: true,
      },
    }),
    new CopyMarkdownImageWebpackPlugin({
      dir: cmd.markdownPaths,
      toDir: config.output.path,
    }),
    // new webpack.optimize.DedupePlugin(),
    new MiniCssExtractPlugin({
      // Options similar to the same options in webpackOptions.output
      // both options are optional
      filename: 'css/[contenthash].css',
      chunkFilename: 'css/[id].css',
    }),
    new PrerenderSPAPlugin({
      // Required - The path to the webpack-outputted app to prerender.
      staticDir: docsBuildDist,
      outputDir: docsConfig.repo
        ? `${docsBuildDist}/${docsConfig.repo}`
        : docsBuildDist,
      indexPath: docsConfig.repo
        ? `${docsBuildDist}/${docsConfig.repo}/index.html`
        : `${docsBuildDist}/index.html`,
      // Required - Routes to render.
      routes,
      successCb: async () => {
        if (docsConfig.repo) {
          // not use fs.move here or it'll throw error in github action
          await fs.copy(`${docsBuildDist}/${docsConfig.repo}`, docsBuildDist)
          await fs.remove(`${docsBuildDist}/${docsConfig.repo}`)
          const defaultPath = (dirTree.find(data => data.name === 'README.md')
            && dirTree.find(data => data.name === 'README.md').mdconf
            && dirTree.find(data => data.name === 'README.md').mdconf.abbrlink) || 'README'
          // move README as root index.html
          await fs.copy(`${docsBuildDist}/${defaultPath}/index.html`, `${docsBuildDist}/index.html`)
          console.log('✅ generate prerender file success!')
          if (docsConfig.seo) {
            if (docsConfig.seo.google) {
              fs.writeFileSync(`${docsBuildDist}/sitemap.xml`, generateSiteMap(routes))
            }
          }
          console.log('✅ generate sitemap file success!')
        }
      },
      // The actual renderer to use. (Feel free to write your own)
      // Available renderers: https://github.com/Tribex/prerenderer/tree/master/renderers
      renderer: new Renderer({
        // Optional - The name of the property to add to the window object with the contents of `inject`.
        injectProperty: '__PRERENDER_INJECTED',
        // Optional - Any values you'd like your app to have access to via `window.injectProperty`.
        inject: {
          prerender: true,
        },
        // Optional - defaults to 0, no limit.
        // Routes are rendered asynchronously.
        // Use this to limit the number of routes rendered in parallel.
        maxConcurrentRoutes: 4,
        // https://pptr.dev/#?product=Puppeteer&version=v5.5.0&show=api-pagegotourl-options
        navigationOptions: {
          timeout: 0
        }
      }),
    }),
  ])
  return config
}


================================================
FILE: packages/crd-scripts/src/conf/webpack.config.server.js
================================================
const protocol = process.env.HTTPS === 'true' ? 'https' : 'http'

module.exports = (cmd, webpackConf) => {
  return {
    // 启用生成文件的gzip压缩。
    compress: true,
    // 沉默WebpackDevServer自己的日志,因为它们通常没有用处。
    // 这个设置仍然会显示编译警告和错误。
    clientLogLevel: 'none',
    // contentBase: conf.output.appPublic,
    publicPath: webpackConf.output.publicPath,
    hot: true,
    historyApiFallback: {
      // 带点的路径仍应使用历史回退。
      // See https://github.com/facebookincubator/create-react-app/issues/387.
      disableDotRule: true,
    },
    // historyApiFallback: true,
    // WebpackDevServer默认是嘈杂的,所以我们发出自定义消息
    // 通过上面的`compiler.plugin`调用来监听编译器事件。
    quiet: true,
    // 如果HTTPS环境变量设置为“true”,则启用HTTPS
    https: protocol === 'https',
    // 告诉服务器从哪里提供内容。提供静态文件,这只是必要的。
    contentBase: cmd.markdownPaths,
    // 通知服务器观察由devServer.contentBase选项提供的文件。
    // it'll reload page when file change.
    watchContentBase: true,
    // avoid cpu overload in some case
    watchOptions: {
      ignored: /node_modules/,
    },
  }
}


================================================
FILE: packages/crd-scripts/src/deploy.js
================================================
const ghpages = require('gh-pages')
const loading = require('loading-cli')

const log = console.log; // eslint-disable-line

module.exports = function server(cmd, docsConfig) {
  if (!docsConfig) {
    console.log('please check config.yml in root dir!\n')
    return
  }
  if (!docsConfig.user || !docsConfig.repo) {
    console.log('please check user and repo in config.yml!\n')
    return
  }
  const { user, repo, publish } = docsConfig
  log('  Start deploy to your git repo'.green)
  const load = loading({
    text: 'Please wait ...'.blue,
    color: 'blue',
    interval: 100,
    stream: process.stdout,
  }).start()

  ghpages.publish(
    cmd.output,
    {
      branch: cmd.branch,
      repo: publish || `https://github.com/${user}/${repo}.git`,
      message: `Update website, ${new Date()}!`,
    },
    (err) => {
      load.stop()
      if (err) {
        return log(err)
      }
      log(`\n  Push to ${cmd.branch} success!\n`.green.bold)
    },
  )
}



================================================
FILE: packages/crd-scripts/src/generate.js
================================================
const fs = require('fs')
const { docsConfig } = require('crd-utils')
const { directoryTree } = require('./conf/node-directory-tree')

module.exports = function generate(program) {
  if (!fs.existsSync(docsConfig)) {
    console.log('❎ please check config.yml in root dir!\n')
    return
  }

  const dir = program.markdownPaths
  const dirs = Array.isArray(dir) ? dir : [dir]
  const otherProps = {
    mdconf: true,
    extensions: /\.md/,
    generate: true
  }
  dirs.map(path => directoryTree({
    path,
    options: otherProps,
  }))
  console.log('✅ generate success!')
}


================================================
FILE: packages/crd-scripts/src/server.js
================================================
const webpack = require('webpack')
const WebpackDevServer = require('webpack-dev-server')
const openBrowsers = require('open-browsers')
const detect = require('detect-port')
const fs = require('fs')
const { docsConfig } = require('crd-utils')
const prepareUrls = require('local-ip-url/prepareUrls')
const conf = require('./conf/webpack.config.dev')
const createDevServerConfig = require('./conf/webpack.config.server')
require('colors-cli/toxic')

function clearConsole() {
  // process.stdout.write(process.platform === 'win32' ? '\x1B[2J\x1B[0f' : '\x1B[2J\x1B[3J\x1B[H');
}

module.exports = function server(cmd) {
  if (!fs.existsSync(docsConfig)) {
    console.log('please check config.yml in root dir!\n')
    return
  }
  const HOST = cmd.host
  let DEFAULT_PORT = cmd.port
  const webpackConf = conf(cmd)
  const compiler = webpack(webpackConf)

  detect(DEFAULT_PORT).then((_port) => {
    if (DEFAULT_PORT !== _port) DEFAULT_PORT = _port

    const protocol = process.env.HTTPS === 'true' ? 'https' : 'http'
    const urls = prepareUrls({ protocol, host: HOST, port: DEFAULT_PORT })
    // https://webpack.js.org/api/compiler-hooks/#aftercompile
    // print log after being compiled
    compiler.hooks.done.tap('done', () => {
      /* eslint-disable */
      console.log(`Dev Server Listening at Local: ${urls.localUrl.green}`);
      console.log(`              On Your Network: ${urls.lanUrl.green}`);
      console.log(`\nTo create a production build, use ${'npm run build'.blue_bt}.`);
      /* eslint-enable */
    })

    new WebpackDevServer(compiler, createDevServerConfig(cmd, webpackConf)).listen(DEFAULT_PORT, HOST, (err) => {
      if (err) {
        return console.log(err); // eslint-disable-line
      }
      clearConsole()
      // open browser
      openBrowsers(urls.localUrl)
    })
  }).catch((err) => {
    console.log(err); // eslint-disable-line
  })
}


================================================
FILE: packages/crd-scripts/src/utils/index.js
================================================
const fs = require('fs')
const { docsGitIgnore, searchFilePath } = require('crd-utils')

/**
 * judege cur file if in git ignore.
 */
exports.ifInGitIgnore = (mdfilePathInProject) => {
  let gitIgnoreContentArr = []
  if (fs.existsSync(docsGitIgnore)) {
    const gitIgnoreContent = fs.readFileSync(docsGitIgnore)
    gitIgnoreContentArr = gitIgnoreContent.toString().split('\n')
  }
  return gitIgnoreContentArr.indexOf(mdfilePathInProject) > -1
}

/**
 * to get dight from cur dir.
 * If there are order && unorder file in one same folder, the unorder file'll be in front of order file.
 * eg:
 *  '1.xx' => 1
 *  'xx' => 0
 */
exports.getDigitFromDir = (dir) => {
  const matchedResult = dir.match(/^((\d)*)\.(\s|\S)*$/)
  if (matchedResult && matchedResult[1]) {
    return parseInt(matchedResult[1], 10)
  }
  return 0
}

function paddingTwoDigits(digit) {
  return digit < 10 ? `0${digit}` : digit
}

/**
 * format time
 */
exports.timeFormat = (date) => {
  if (isNaN(date.getFullYear()) || isNaN(date.getMonth()) || isNaN(date.getDate())) return null
  return `${date.getFullYear()}-${paddingTwoDigits(
    date.getMonth() + 1,
  )}-${paddingTwoDigits(date.getDate())}`
}

exports.getSearchContent = () => {
  if (!fs.existsSync(searchFilePath)) {
    console.log('there is no find .cache/search.js in root dir!\n')
    return null
  }
  return fs.readFileSync(searchFilePath)
}



================================================
FILE: packages/crd-scripts/src/utils/initCache.js
================================================
const write = require('write')
const path = require('path')
const { cacheDirPath, getDocsConfig } = require('crd-utils')
const { directoryTree } = require('../conf/node-directory-tree')

module.exports = function (program, cb) {
  const treeData = program.markdownPaths.map((markdownPath) => {
    return directoryTree({
      path: markdownPath,
      options: {
        mdconf: true, // Markdown config for exsiting file.
        extensions: /\.md/,
      },
    })
  })
  // to collect search data
  const searchData = []
  const docsConfig = getDocsConfig()
  const useSearchPlugin = docsConfig.search && docsConfig.host

  function dfsMap(data) {
    // eslint-disable-next-line no-plusplus
    for (let i = 0; i < data.length; i++) {
      if (data[i].children) {
        dfsMap(data[i].children)
      } else {
        const searchMapKeys = docsConfig.search_map ? Object.keys(docsConfig.search_map) : []
        // eslint-disable-next-line no-plusplus
        for (let x = 0; x < searchMapKeys.length; x++) {
          if (data[i].relative) {
            const searchMapIndex = data[i].relative.indexOf(searchMapKeys[x])
            if (searchMapIndex !== -1 && typeof searchMapIndex === 'number') {
              const effectedPath = data[i].relative.replace(
                searchMapKeys[x],
                docsConfig.search_map[searchMapKeys[x]],
              )
              searchData.push({
                title: data[i].name,
                url: `${effectedPath.replace(/.md/g, '')}`,
                content: data[i].content,
              })
              break
            }
          }
        }
      }
    }
  }
  if (useSearchPlugin) {
    // README
    searchData.push({
      title: 'README',
      url: 'README',
      content: treeData[0].content,
    })
    // map treeData to generate search data source
    dfsMap(treeData)
    const writeSearchPath = path.resolve(
      process.cwd(),
      cacheDirPath,
      'search.js',
    )
    write.sync(
      writeSearchPath,
      `${JSON.stringify(
        searchData,
      )}`,
    )
  }
  cb()
}


================================================
FILE: packages/crd-scripts/src/web/Router.js
================================================
import { BrowserRouter, Route, Routes } from 'react-router-dom'
import theme from 'crd-theme'
import menuSource from './crd.json'

/**
 * serialize router data
 */
function routeData(data, arrayRoute = [], routePath = '/', article) {
  data.forEach((item) => {
    const routePropsCurrent = `${routePath}${item.name}`.replace(/.md$/, '')
    const { mdconf, ...otherItem } = item
    arrayRoute.push({
      path: routePropsCurrent,
      mdconf: mdconf || { title: item.name },
      props: { ...otherItem },
      article: article || item.name,
    })
    if (item.children && item.children.length > 0) {
      arrayRoute.concat(routeData(item.children, arrayRoute, `${routePropsCurrent}/`, article || item.name))
    }
  })
  return arrayRoute
}

function menuSourceFormat(data, routePath, article) {
  const arr = []
  data.forEach((item) => {
    const routePropsCurrent = `${routePath || ''}/${item.name}`.replace(/.md$/, '')
    if (item.type === 'directory') {
      if (item.children && item.children.length > 0) {
        item.title = item.name.replace(item.extension, '')
        item.mdconf = {}
        item.props = { isEmpty: true }
        item.children = menuSourceFormat(item.children, routePropsCurrent, article || item.name)
      } else {
        item.title = item.name.replace(item.extension, '')
        item.mdconf = { title: item.name }
        item.props = { isEmpty: true }
        item.children = []
      }
    } else {
      item.title = item.mdconf && item.mdconf.title ? item.mdconf.title : item.name.replace(item.extension, '')
      if (!item.mdconf) {
        item.props = { isEmpty: true }
      }
    }
    item.routePath = routePropsCurrent
    item.article = article || item.name
    arr.push(item)
  })
  return arr
}

const RoutersContainer = ({ ...props }) => {
  return (
    <Routes>
      <Route
        path="/*"
        element={theme({
          routeData: routeData(menuSource),
          menuSource: menuSourceFormat(menuSource),
          ...props,
        })}
      />
    </Routes>
  )
}

export default function RouterRoot() {
  return (
    <BrowserRouter>
      <RoutersContainer />
    </BrowserRouter>
  )
}


================================================
FILE: packages/crd-scripts/src/web/crd.json
================================================
[]


================================================
FILE: packages/crd-scripts/src/web/index.js
================================================
import { hydrate } from 'react-dom'
import { renderToString } from 'react-dom/server';
// import { hydrateRoot } from 'react-dom/client'
import { ifDev, ifPrerender } from 'crd-client-utils'
import RouterRoot from './Router'

if (ifDev) {
  // dev render
  document.getElementById('root').innerHTML = renderToString(<RouterRoot />)
  hydrate(
    <RouterRoot />,
    document.getElementById('root'),
  )
  // hydrateRoot(
  //   document.getElementById('root'),
  //   <RouterRoot />,
  // )
} else if (ifPrerender) {
  // prerender
  document.getElementById('root').innerHTML = renderToString(<RouterRoot />)
} else {
  // prod render:
  // It'll cause some [unkown error](https://github.com/MuYunyun/create-react-doc/issues/278) using hydrateRoot here.
  // So still using hydrate temporarily.
  hydrate(
    <RouterRoot />,
    document.getElementById('root'),
  )
  // hydrateRoot(
  //   document.getElementById('root'),
  //   <RouterRoot />,
  // )
}


================================================
FILE: packages/crd-seed/.npmrc
================================================
# .npmrc

registry=https://registry.npmjs.org/

# https://github.com/sass/node-sass#binary-configuration-parameters
sass_binary_site=https://npm.taobao.org/mirrors/node-sass/

# https://github.com/Medium/phantomjs#deciding-where-to-get-phantomjs
# phantomjs_cdnurl=http://cnpmjs.org/downloads
phantomjs_cdnurl=https://npm.taobao.org/dist/phantomjs


================================================
FILE: packages/crd-seed/README.md
================================================
## 主题

create-react-doc 提供了官方默认主题 [crd-seed](https://github.com/MuYunyun/create-react-doc/tree/main/packages/crd-seed)。该主题支持以下特性:

* 适配移动、PC 多端展示。
* 支持暗黑模式。
* 文档支持内嵌 codepen、codesandbox。
* GitHub 联动。

使用该主题搭建的项目有:

* [blog](https://github.com/MuYunyun/blog), [站点](http://muyunyun.cn/blog)
  * ![](http://with.muyunyun.cn/90d3e357a31649b9466a828a92b6d88d.jpg)
  * ![](http://with.muyunyun.cn/2e7440e4256debda2d73a4e6392c7146.jpg-300)
* [diana](https://github.com/MuYunyun/diana), [站点](https://muyunyun.cn/diana/)

如果您想定制化或者分享个人主题, 可以参考[自定义主题](http://muyunyun.cn/create-react-doc/自定义主题)章节。

================================================
FILE: packages/crd-seed/component/Affix/affix.js
================================================
import { useState, useEffect, useRef } from 'react'
import { throttle } from './utils'

const Affix = ({
  offsetTop,
  offsetBottom,
  children,
  target,
  onChange,
  className,
  wrapperClassName,
  style,
  width,
  affixStyle,
}) => {
  const placeholderRef = useRef(null)
  const wrapperRef = useRef(null)
  const widthRef = useRef(width)
  const [positionStyle, setPositionStyle] = useState({})
  // 滚动元素
  let scrollElm = window
  // 是否是绝对布局模式
  const fixedRef = useRef(false)
  const [fixed, setFixed] = useState(fixedRef.current)

  useEffect(() => {
    widthRef.current = width
  }, [width])

  useEffect(() => {
    // 在子节点移开父节点后保持原来占位
    setWrapperDimension()
  }, [fixed, width])

  useEffect(() => {
    if (target) scrollElm = target()
    scrollElm.addEventListener('scroll', scroll)
    return () => {
      if (target) scrollElm = target()
      scrollElm.removeEventListener('scroll', scroll)
    }
  }, [offsetTop, offsetBottom])

  const validValue = (value) => {
    return typeof value === 'number'
  }
  const setWrapperDimension = () => {
    const { width: wrapperRefWidth, height: wrapperRefHeight } = wrapperRef.current
      ? wrapperRef.current.getBoundingClientRect()
      : {}
    placeholderRef.current &&
      (placeholderRef.current.style.height = `${wrapperRefHeight}px`)
    placeholderRef.current &&
      (placeholderRef.current.style.width =
        typeof width === 'number' ? `${width}px` : `${wrapperRefWidth}px`)
    wrapperRef.current &&
      (wrapperRef.current.style.width =
        typeof width === 'number' ? `${width}px` : `${wrapperRefWidth}px`)
  }
  const updateFixed = () => {
    fixedRef.current = !fixedRef.current
    setFixed(fixedRef.current)
  }
  const handleScroll = () => {
    const rect =
      placeholderRef.current && placeholderRef.current.getBoundingClientRect()
    if (!rect) return
    let { top, bottom } = rect
    const updatePositionStyle = {
      width:
        typeof widthRef.current === 'number'
          ? widthRef.current
          : placeholderRef.current &&
            placeholderRef.current.getBoundingClientRect().width,
      zIndex: 999,
    }
    let containerTop = 0 // 容器距离视口上侧的距离
    let containerBottom = 0 // 容器距离视口下侧的距离

    if (scrollElm === window) {
      bottom = window.innerHeight - bottom
    } else {
      const containerRect = scrollElm && scrollElm.getBoundingClientRect()
      containerTop = containerRect && containerRect.top
      containerBottom = containerRect && containerRect.bottom
      top -= containerTop // 距离容器顶部的距离
      bottom = containerBottom - bottom // 距离容器底部的距离
    }

    if (
      (validValue(offsetTop) && top <= offsetTop) ||
      (validValue(offsetBottom) && bottom <= offsetBottom)
    ) {
      if (!fixedRef.current) {
        updatePositionStyle.position = 'fixed'
        validValue(offsetTop) && (updatePositionStyle.top = offsetTop + containerTop)
        validValue(offsetBottom) &&
          (updatePositionStyle.bottom =
            scrollElm === window
              ? bottom
              : window.innerHeight - (containerBottom - offsetBottom))
        onChange && onChange(true)
        updateFixed()
        setPositionStyle(updatePositionStyle)
      }
    } else if (fixedRef.current) {
      updatePositionStyle.position = 'relative'
      onChange && onChange(false)
      updateFixed()
      setPositionStyle(updatePositionStyle)
    }
  }

  const scroll = throttle(handleScroll, 20)

  return (
    <div ref={placeholderRef} style={style} className={className}>
      <div
        ref={wrapperRef}
        className={wrapperClassName}
        style={{ ...{ position: 'relative' }, ...positionStyle, ...affixStyle }}
      >
        {children}
      </div>
    </div>
  )
}

export default Affix


================================================
FILE: packages/crd-seed/component/Affix/index.js
================================================
import Affix from './affix'

export default Affix


================================================
FILE: packages/crd-seed/component/Affix/utils/index.js
================================================
const throttle = (fn, wait) => {
  let inThrottle
  let lastFn
  let lastTime
  return function () {
    const context = this
    // eslint-disable-next-line prefer-rest-params
    const args = arguments
    if (!inThrottle) {
      fn.apply(context, args)
      lastTime = Date.now()
      inThrottle = true
    } else {
      clearTimeout(lastFn)
      lastFn = setTimeout(() => {
        if (wait - (Date.now() - lastTime) <= 0) {
          fn.apply(context, args)
          lastTime = Date.now()
        }
      }, Math.max(wait - (Date.now() - lastTime), 0))
    }
  }
}

export { throttle }


================================================
FILE: packages/crd-seed/component/Footer/index.js
================================================
import cx from 'classnames'
import styles from './index.less'

const version = VERSION; // eslint-disable-line
const footer = FOOTER; // eslint-disable-line

const FooterView = ({ inlineCollapsed }) => {
  return (
    <div
      className={cx(styles.footer, {
        [`${styles['footer-inlineCollapsed']}`]: inlineCollapsed,
      })}
    >
      {footer ? (
        <div dangerouslySetInnerHTML={{ __html: footer }} />
      ) : (
        <>
          <div className={styles.powered_by}>
            {`Powered by${' '}`}
            <a
              target="_blank"
              rel="noopener noreferrer"
              href="https://github.com/MuYunyun/create-react-doc"
            >
              Create React Doc
            </a>
            .
          </div>
          <div>
            <i className="fa fa-user" /><span className={styles.uv_count} id="busuanzi_value_site_uv" />
            <span className={styles.split}>|</span>
            <i className="fa fa-eye" /><span className={styles.pv_count} id="busuanzi_value_site_pv" />
          </div>
        </>

      )}
    </div>
  )
}

export default FooterView


================================================
FILE: packages/crd-seed/component/Footer/index.less
================================================
.footer {
  font-size: 14px;
  text-align: center;
  border-top: 1px solid #e9e9e9;
  margin: 50px 0 0 240px;
  padding: 20px 0 50px 0;
  clear: both;
  color: #999;
  transition: margin .2s ease-in-out;

  a {
    color: #758AC5;

    &:hover {
      color: #0800ff;
    }
  }

  .powered_by {
    margin-bottom: 8px;
  }

  &-inlineCollapsed {
    margin: 50px 0 0 0;
  }

  .uv_count, .pv_count {
    margin-left: 5px;
  }

  .split {
    margin: 0 5px;
  }
}

================================================
FILE: packages/crd-seed/component/Header/index.js
================================================
import { useState } from 'react'
import cx from 'classnames'
import { Link } from 'react-router-dom'
import Switch from 'react-switch'
import { ifProd } from 'crd-client-utils'
import { isMobile } from '../../utils'
import Search from '../Search'
import styles from './index.less'

const Header = ({
  className,
  logo,
}) => {
  // eslint-disable-next-line no-undef
  const { user, repo, tags } = DOCSCONFIG || {}
  const [checked, setChecked] = useState(false)
  const handleChange = (value) => {
    value
      ? document.body.classList.add(styles.darkMode)
      : document.body.classList.remove(styles.darkMode)
    setChecked(value)
  }
  return (
    <div className={cx(styles.header, className)}>
      <div className={styles.wrapper}>
        <Link to="/" replace className={styles.titleLink}>
          <div className={styles.logo}>
            {logo && <img alt="logo" src={logo} />}
            {!isMobile && (
              <span>
                {(DOCSCONFIG && DOCSCONFIG.title) || 'Create React Doc'}
              </span>
            )}
          </div>
        </Link>
        {DOCSCONFIG && DOCSCONFIG.search ? <Search className={styles.search} /> : null}
      </div>
      <div className={styles.rightArea}>
        {
          tags
            ? <Link
              className={styles['tags']}
              to={ifProd ? `/${repo}/tags` : '/tags'}
            >
              标签
            </Link>
            : null
        }
        <Switch
          className={styles['no-dark-mode']}
          onChange={handleChange}
          checked={checked}
          width={50}
          height={24}
          offColor="#0f1114"
          onColor="#0f1114"
          uncheckedIcon={
            <img
              className={styles.sun}
              src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAVlpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDUuNC4wIj4KICAgPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iPgogICAgICAgICA8dGlmZjpPcmllbnRhdGlvbj4xPC90aWZmOk9yaWVudGF0aW9uPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KTMInWQAABwNJREFUWAmtV1tsFFUY/s6Z2d22zLYlZakUCRVaQcqlWIiCiS1gTEB9UAO+GR9En3iQGI0xJiSiRB98MjEq8cEQTSBeHhQM0V7whtEGDWC90BYitxahtNtu25058/v/ZzvLbilawJNM5+yZ89+//1LgJhYRNLW1uDfBAvpGiIk2O5auvfFxqIH3ZJ8/u06GN6Z9+wVl5SjcD1IbZa/UPkPyYl2uR4dreoD2bnbYxTlBBRytkHXtAREphP5KuH4lddx9h70yxX05t7yYXwGb6W8nx1jibpl2rFlGBxcG9M18okOrn7Bnk/BAO/4bI0UeEE1zjBp3UmvjOxJXJdaKN/ZiIu4tOZrAb4aTdZAZArKmWeiiJZ6jt5tiagdCS9+6cgO1Ne6Mvhe+ixTIfyDVhipnK9p+P0Edqx9RW/YZtQVGmOLChRxNNlyPsTEgPQKMB3dbEHa0h1awYmQ83enTd2vmUtvKd1Glv2RkzBb+kZGRrKtjzG60Wguhd/lJZBingbcfWWe72vjT75bJDrhYtvA0hrurETDr5HyF2Knb1MM4ab//xIoOqueA0edRnkkinTyJdYvqLFDZO4zUPFCvVoDjJq4T7TE61IWh4x5KqxX5KVKkX8WZ/t2ov2cb3MHt4dhIyOxIJxJOOF6xRx/99BksXLoecWcXytILMNBDqKpnGZWPquYfPxY8iXGR9fK+SgFrgcRPXPjVqhehL+3EmZ5RGJQi1QBU8TPThQnOQzm+5UXGIcetUeEAfP13VwzpI+w1jGJWdSliNfvVhiMPiOsllJag4M/UGHiqM6dlBb2OTLKHHV6KkvogrJ4XhBWniWK/Gp1MQyf93FOeUXKmKk/FzJxbQtKLjFXYT4USupy8fQVir2ynVEBiZMG0qtOHMS/AW4Gwrk7BG3C1F0B5nqNKE0CME4MfVRLPnXkBKe+ipvoFhNQywOhdghvLi0F8ReyVXV4BKTBRbbe5f64zR/DHsdZw1hJfeWlHl/GNRJzDxrd5m192z78TMaVnKELZoINZS4BzQ7vtnZljSnha/pPCbkuxzXcupYwI5tIeCpGc0Yp9tWHZQy/rmYhRfNgg4bHJBYLzGkxsRJF4XKlE2jBOHNSv3kY7Tj6vthzPFl61BrYwqFlmEQhtSVXmLiksxLmtRgYXI1ULU61JJ4eVKmG3/5sCVgpbMT6OMJ2E08/29Xf3w6v4FnHdCjfWgXu/O8Z5mLdCkeRs2khHe1DqOtQwbHWTAnM5S2HNmhALYo5KjkPFrMMKjZl6HxhWIAb0BqE+/73GrBRQUsKYiBu4JX8ycI6wtw+i5ef3NZpsrKVSHYCP37jwGDgeE1SA0S/xtl5SU2fs1ApEp0qTLVRjgyycDSsLHMSwmFltZMStR3uLLg6BdLhDa5dC6ryU2pHBe1BVO9tUcwfitJt2CLJZUHoG6T7Op75u0IyK31TCPcwFqgPk/KCaD3dFOuZBCO7xvCT/j048b3I3c7F2+WuOW7qdgkucFYlcQ4qop3yzTX7WaKfOCccye3Ts1Etq0+a/BHCF1yPgF3tAUkR6OrtGmo6gl94qqcXKh3rDyrOkPa58URoWcov2Mo6M+0QjrqKB+b7++oMa9Sz+ZkM0mie6aAtnGUvhmxaI+TogPOSQedgWioGSHFLn3v4kLh4HRspNmOGv41k+55siLFp2z6xYeJjhljFcbmxJlr4ga06TbevSByz/glQq4BJx46/c+237PbBqEYKxX3HpmKZEnQnr65X20hqJYaNcLoFOLiJk2LuBbyg7Q0OEn+hm0P3honxFD6rdxYorKpeIoi4YSSvyQHQIbM5t4+YNxLj/OxhVOOE4585qGpjnq+wSx6Q9CtNxTjd5klB+g6Mv36r0+b9cZFi44WYkHdG2ZWb3TtOUOXyVAlKlpGvJIAJ3eBMyfYS5C0qRZGtC85j+4sOasDe9xznPYezhhO/2Q6eP2fSOvYHOjtuQ1a9Q1VKynVDaMc8E0tptdxUsTFpFIYjcZKcbnoaQTNdiqCwNlL4G7oziSqGnT1ALf34vhk4R5zU3qYV9ONp9K88RtouShE68JwaU8dFw5W617shWa9ykeaBIn2hcsvPgL00k45QdTCZuSVcTRNs+8fnyLvooQfR5iujAnR9bxfY2xOVOxFS8SK3Le0l48VyYu1M8HRe5JD8wKPTjYnifaK3Wfn/GChYQ8ZAi6WRzWgqLV5YrsVLnZaVSoXU1g9gOIDwFySiGi+Zdrnzr7J3r+SMuszlcQCRn8lNGcTuSy2jOI7o9mxjZo+vR3ej3tN+ifRSOyUTS0+VMOid93cCubeiy/6TImS0QxRSCq2vxKr45zV+FQnjWH6D2xg+E9EatLcLAdHTgtGGD80D6jM0+aOl4wJgO/f96R2aJKCQ3yvgftRhdFMOpd6oAAAAASUVORK5CYII="
              width="16"
              height="16"
              role="presentation"
              alt="sun"
            />
          }
          checkedIcon={
            <img
              className={styles.moon}
              src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAVlpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDUuNC4wIj4KICAgPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iPgogICAgICAgICA8dGlmZjpPcmllbnRhdGlvbj4xPC90aWZmOk9yaWVudGF0aW9uPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KTMInWQAABlJJREFUWAm1V3tsFEUcntnXvXu0tBWo1ZZHihBjCEWqkHiNaMLDRKOtQSKaiCFKQtS/SbxiFCHGCIkmkBSMwZhQNTFoQZD0DFiwtCDFAkdDqBBBKFj63rvdnfH7zfVo5aFBj0l2Z/dm5vd98/0es8dYjlpr62azufnDQNZcU1PciMfjWvb9rvZSMk4Ayfb36pLH13189GC8LAtIRLLPt+pzwrCuLq4ISEv/gHmitrAwfPbEkXc/ad4dL6iujrvyX0jcitgd/yZlZqftP6995Mr5TVLa22Tn8XVX2g/XLSRjUu7Q79jonS7I7hS7/0oOb5VyqF52n98oj7esXX07EjlxwXWisRmSnm3b29TTM8iYrjmFBWExubxwY/uhNas4r/WySl1fc5cetDMd7ydl+lMJJRw5WC8ud62Xx5rfepzwxgZmbhUYNS5Stvsj4yo2GXJEFBVHWDBkfdbR9HpYBaaUajDnBLKKpl1xRKYcgGtMCqEzTaSnThk/SQT0uJqTqFNBmXMCsZE48DzRZRMBRjv1GHNdk3HBImF9ZUvTyxM40pMKVc4JZBXQOLOFoDeKSxdp6HIQcO4rjYT9fn0pjbz9GLt7BAAODmjSVReXUMFzNW5x5vfxp2mIxZjIuQKJxAmFa+is2DQJJQ0JyBVExNOYcJnPxx/6/utnijmP555ALEagKAGGnGn64QORBjARcIA/yJk7JMJBLRrNtybTvH88KGjCf2jK86bhzmMcwDKFZEQvbIhxFYhChoMWMzU2iWznlIBEVJOsP+1bdX/ALx9l7jApADeDAEcMkE90JnUmmGl4USKQ0xhoW3JB5XY0YrxYWhLwMZZypUyjDGH35AbNwgUGiFBPpuGbHCpAOV1ZGXf2f/taftAv31DyeymN2d1IhAFAwTOmnzF/kKcdh3me7CYCOVNgycju84u8DeVlwfFq9/ZlTfldYrMUjOlrkjkD+rU+WzCROkcEchIDHR011syZW9JHD7y07N6JvhWMpz3pugaTkB6lWFVCKkhck0zzeMp2utq+uHrmfxOgoCO/Z8CXPlEQ1bdH8wgvhSIkEG0ICcQeExIFGdimjvKka7btJFZuaXOammIGKUCFQ53j9EN1dYKWqHf0t2w407W2tgs6h89ZnImjB55flh81tt9XirjjDuSl+oIPRQ0iWPgNZ5GqTqbBe3vSzEl5n5PhWKwocyR2HlqYN61qV18WjYjE8JLARZPQsUSim8foIRYTlGr02Ly7piASFRtKJ4VfieYhxdS2JcDVMN6xVOKZyrCGm8b108lrLRVzvptLH7IoEFLFANes6KnDi+uxfmvFnF17oALq5u1agu3/YfHkcSFzeSggV5eXRfIB7CHNcO5SUI+Ih5Ir7f4MAV9IqdFzdZgNpZw1Gcs1mNvgGbTbqQ9/cz7ZuuhgyYRQ49ljTyWHhr2DwpNHHFf+5gnWZ3Bharo+0TD5dNMw5vv9RlVpSRDHK4TlnoukhtYApuOHejSZQuo5g/A9BysdKRCyLl6062fN37OXMDlvUJtUrtmxo0avrW3wTrYs3jJ9RvRVChrmSmanPMpX2OXMsmDGh6AiEIwBAlvkOqIdBy+8JyAz8pz7QxiDth4KDy5uAlwzrWTnwC8Vc4KVAMZ3YUZ+IqoIjP3h5KFFX1ZMy3uW+7RhEDHgTi0zC9rS7uhPCDiNrGFyqBeERtKN/B0YlyFCkw0NJ5C0Ojv7zvT1a1WV1TuvZDdL4NTgB7CASYpsen6gqvG5jmTf5qHedADgkBl3D0nkSgNhZACDyi0FUKZRr3IdRjgN4WPPoFMIIegIK3mqd38fS80mcJKelM4szNyzZtQbkchGePuBRS8Eg9pHU8ojRQpSqs+ajAIwTjjUMQ/nvTNM0kicwYxZIYMh/891DYi+fvedB+c1xsm4lDU6ya+Axtz+RiAzEVYbajQOpq17F0R9QevNcEhfcU+xvyQQUalGJBSesqOkgPQ4YNyUZL9fSvUPDjoNAwN8/dwFjaczNkc3ptaMud1EIDtGcmXTcefO2cGSvKIFfp/2JIJxlq7xEl3nVPM4fDeIbPkD16/ptNc0bDu7qxbsu0R2JGywWMIjF2ft3tjfloAyQAGXiOn8hrqwbVvMXzaO+QeHXP6nF0wvX74Hf4NGG5GPjSlYoyM3P/0FbCT6zvM/yYoAAAAASUVORK5CYII="
              width="16"
              height="16"
              role="presentation"
              alt="moon"
            />
          }
        />
        <a href={`https://github.com/${user}/${repo}`} className={styles['github-corner']} title="Star me on GitHub" aria-label="Follow me on GitHub" rel="noopener" target="_blank">
          <svg width="60" height="60" viewBox="0 0 250 250" aria-hidden="true">
            <path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z" />
            <path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="white" style={{ transformOrigin: '130px 106px' }} className={styles['octo-arm']} />
            <path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="white" />
          </svg>
        </a>
      </div>
    </div>
  )
}

export default Header


================================================
FILE: packages/crd-seed/component/Header/index.less
================================================
@import '../../style/base.less';

.logo {
  float: left;
  padding: 0 0 0 15px;

  img {
    height: 28px;
    vertical-align: middle;
  }

  span {
    vertical-align: middle;
  }

  img+span {
    margin-left: 10px;
  }
}

.header {
  box-shadow: 0 2px 8px #f0f1f2;
  line-height: 60px;
  height: 60px;
  position: relative;
  width: 100%;
  top: 0;
  z-index: @header-zIndex;
  display: flex;

  .wrapper {
    flex: 1;
    display: flex;
    align-items: center;

    .titleLink {
      display: inline-block;
    }

    .search {
      margin-left: 40px;
      max-width: 200px;
      display: flex;
      align-items: center;
    }

    .select {
      width: 200px;
    }
  }

  .rightArea {
    display: flex;
    align-items: center;

    .sun {
      position: absolute;
      left: 5px;
      top: 4px;
    }

    .moon {
      position: absolute;
      right: 5px;
      top: 4px;
    }

    .github-corner {
      display: block;

      &:hover {
        .octo-arm {
          animation: octocat-wave 560ms ease-in-out;
        }
      }
    }

    svg {
      vertical-align: bottom;
    }
  }
}

.tags {
  font-size: 15px;
  margin-right: 16px;
}

@keyframes octocat-wave {
  0%, 100% {
    transform: rotate(0);
  }
  20%, 60% {
    transform: rotate(-25deg);
  }
  40%, 80% {
    transform: rotate(100deg);
  }
}

.pageTitle {
  font-size: 30px;
  line-height: 38px;
  color: #0d1a26;
  font-weight: 500;
  margin-bottom: 20px;
  margin-top: 8px;
  padding-left: 20px;
}

.darkMode {
  filter: invert(100%) hue-rotate(180deg);

  .no-dark-mode {
    filter: invert(100%) hue-rotate(180deg);
  }
}

================================================
FILE: packages/crd-seed/component/Icon/Icon.js
================================================
import { useEffect } from 'react'
import cx from 'classnames'
import loadSprite from './loadSprite'
import styles from './style/index.less'

/* omit some props depends on arr */
const omit = (props, arr) =>
  Object.keys(props)
    .filter(k => arr.indexOf(k) === -1)
    // eslint-disable-next-line no-sequences
    .reduce((acc, key) => ((acc[key] = props[key]), acc), {})

function Icon(props) {
  const { type, color, prefixCls = 'icon', size, style, className, ...rest } = props

  useEffect(() => {
    loadSprite(props)
  }, [type])

  const newClassName = cx(styles[prefixCls], className)
  const cloneStyle = { ...style }
  if (color) {
    cloneStyle.color = color
  }
  if (size) {
    cloneStyle.fontSize = size
  }

  const restProps = omit(rest, ['svgContent'])

  return (
    <svg className={newClassName} style={cloneStyle} {...restProps}>
      <use xlinkHref={`#${type}`} />
    </svg>
  )
}

export default Icon


================================================
FILE: packages/crd-seed/component/Icon/iconsInfo.js
================================================
export const IconsInfo = {
  folder:
    '<svg viewBox="0 0 1024 1024"><defs/><path d="M838 151H450a94 94 0 00-94-91H184c-52 0-94 42-94 94v595c0 51 42 93 94 93h654c52 0 94-42 94-93V244c0-51-42-93-94-93zm-654-31h172c19 0 35 15 35 34v26c0 17 13 30 29 30h418c19 0 34 15 34 34v27H150V154c0-19 15-34 34-34zm654 663H184a34 34 0 01-34-34V330h722v419c0 19-15 34-34 34z"/></svg>',
  file:
    '<svg viewBox="0 0 1024 1024"><defs/><path d="M512 320H256v-64h256v64zM256 512h448v-64H256v64zm0 128h448v-64H256v64zm0 128h448v-64H256v64zm640-480v608c0 35-29 64-64 64H192c-35 0-64-29-64-64V128c0-35 29-64 64-64h480l224 224zm-64 32L640 128H192v768h640V320z"/></svg>',
  edit:
    '<svg viewBox="0 0 1026 1024"><defs/><path d="M308 709l56-122 70 68-126 54zm96-171l366-353 82 80-366 352-82-79zm550-371l-51 48-82-80 50-48c8-8 24-6 32 2l49 47c5 5 8 11 8 18 0 5-3 9-6 13zM314 530l-2 3-52 184c-3 11 0 23 8 31 6 5 14 9 23 9l8-2 190-49h1a8 8 0 006-3l508-489c15-14 23-34 23-55 0-25-11-49-29-67l-49-47c-19-18-43-28-69-28-23 0-43 8-58 23L315 528l-1 2zM101 0C45 0 0 45 0 100v824c0 54 45 99 101 99h806c56 0 101-45 101-99V398h-86v451c0 49-41 88-90 88H176c-49 0-90-39-90-88V173c0-48 41-88 90-88h457V0H101z"/></svg>',
  'update-time':
    '<svg viewBox="0 0 1024 1024"><defs/><path d="M649 649c-4 0-7 0-11-4L502 543c-4-4-7-7-7-14V222c0-10 7-17 17-17s17 7 17 17v300l130 96c7 7 10 17 3 24-3 3-7 7-13 7z"/><path d="M512 922a409 409 0 01-382-270c-4-10 0-17 10-21 10-3 17 0 20 11a376 376 0 00352 245c205 0 375-167 375-375 0-10 7-17 18-17s17 7 17 17c0 225-185 410-410 410zM119 529c-6 0-17-7-17-17a409 409 0 01792-140c4 10 0 17-10 21-10 3-17 0-20-11a376 376 0 00-352-245 377 377 0 00-375 375c0 10-7 17-18 17z"/><path d="M119 563c-3 0-10 0-13-3l-86-86c-6-6-6-17 0-23s18-7 24 0l72 71 72-71c7-7 17-7 24 0s6 17 0 23l-86 86c4 3-3 3-7 3zM990 580c-4 0-10 0-14-3l-71-72-72 72c-7 7-17 7-24 0s-7-17 0-24l85-85c7-7 17-7 24 0l86 85c6 7 6 17 0 24-4 3-11 3-14 3z"/></svg>',
  'create-time':
    '<svg viewBox="0 0 1024 1024"><defs/><path d="M512 67a445 445 0 100 890 445 445 0 000-890zm0 849a404 404 0 110-808 404 404 0 010 808zm222-404H532V249a20 20 0 00-40 0v283c0 11 9 20 20 20h222a20 20 0 000-40z"/></svg>',
  search:
    '<svg viewBox="0 0 1024 1024"><defs/><path d="M401 801a401 401 0 110-801 401 401 0 010 801zm0-89a312 312 0 100-623 312 312 0 000 623z"/><path d="M668 602l315 315-63 63-315-315z"/></svg>',
}


================================================
FILE: packages/crd-seed/component/Icon/index.js
================================================
import Icon from './Icon'

export default Icon


================================================
FILE: packages/crd-seed/component/Icon/loadSprite.js
================================================
import { IconsInfo } from './iconsInfo'

/* tslint:disable:max-line-length */
// inspried by https://github.com/kisenka/svg-sprite-loader/blob/master/runtime/browser-sprite.js
// Much simplified, do make sure run this after document ready
const svgSprite = contents => `
  <svg
    xmlns="http://www.w3.org/2000/svg"
    xmlns:xlink="http://www.w3.org/1999/xlink"
    id="__CRD_SVG_SPRITE_NODE__"
    style="display:none;overflow:hidden;width:0;height:0"
  >
    <defs>
      ${contents}
    </defs>
  </svg>
`
/**
 * '<svg viewBox="0 0 1024 1024"><path ..."/></svg>' =>
 * ' viewBox="0 0 1024 1024"><path ..."/></'
 */
const handleSvgContent = (svgContent) => {
  return svgContent.split('svg')[1]
}

const renderSvgSprite = (props) => {
  const { svgContent, type } = props
  const customIconsInfo = svgContent
    ? {
      [`${type}`]: svgContent,
    }
    : {}
  const mergeSvgInfo = { ...IconsInfo, ...customIconsInfo }
  const symbols = Object.keys(mergeSvgInfo)
    .map((iconName) => {
      const getContent = handleSvgContent(mergeSvgInfo[iconName])
      return `<symbol id=${iconName}${getContent}symbol>`
    })
    .join('')
  return svgSprite(symbols)
}

const loadSprite = (props) => {
  const { type, svgContent } = props
  if (!document) {
    return
  }
  const existing = document.getElementById('__CRD_SVG_SPRITE_NODE__')
  const mountNode = document.body

  if (!existing) {
    mountNode &&
      typeof mountNode.insertAdjacentHTML === 'function' &&
      mountNode.insertAdjacentHTML('afterbegin', renderSvgSprite(props))
  } else if (svgContent) {
    const defs = existing.children[0]
    const svgChildren = defs.children
    const svgChildrenIds = svgChildren ? [].slice.call(svgChildren).map(r => (r).id) : []
    if (svgChildrenIds.indexOf(type) !== -1) return
    defs.innerHTML += `<symbol id=${type}${handleSvgContent(svgContent)}symbol>`
  }
}

export default loadSprite


================================================
FILE: packages/crd-seed/component/Icon/style/index.less
================================================
.icon {
  font-size: 24px;
  vertical-align: middle;
  fill: currentColor;
  background-size: cover;
  width: 1em;
  height: 1em;
}


================================================
FILE: packages/crd-seed/component/Menu/Menu.js
================================================
import { useState } from 'react'
import cx from 'classnames'
import MenuItem from './MenuItem'
import { SubMenu } from './SubMenu'
import { MenuProvider } from './context'
import styles from './style/index.less'

const Menu = ({
  theme = 'light',
  children,
  selectedKey,
  onSelect = () => {},
  inlineCollapsed = false,
  defaultOpenKeys = [],
  menuStyle,
  toggle,
}) => {
  /* 存储 hover 状态的 key 值, 在垂直模式中需要根据 hover 的 key 值高亮父节点 */
  const [hoverKey, setHoverKey] = useState('')
  const MenuContext = {
    theme,
    mode: 'inline',
    inlineCollapsed,
    defaultOpenKeys,
    selectedKey,
    onSelect,
    hoverKey,
    onHoverKey: setHoverKey,
  }
  const renderToggle = () => {
    return (
      <div
        className={cx(styles.toggle, {
          [`${styles['toggle-collapsed']}`]: inlineCollapsed,
        })}
        onClick={toggle}
      >
        <i
          className={cx(styles['toggle-icon'], {
            [`${styles['toggle-icon-close']}`]: inlineCollapsed,
          })}
        />
      </div>
    )
  }
  const renderMenu = () => {
    return (
      <ul
        className={cx(
          styles.menu,
          styles[`menu-${theme}`],
          styles['menu-inline'],
          {
            [styles['menu-inline-collapsed']]: inlineCollapsed,
          },
        )}
        style={menuStyle}
      >
        {children}
      </ul>
    )
  }

  return (
    <MenuProvider value={MenuContext}>
      {renderToggle()}
      {renderMenu()}
    </MenuProvider>
  )
}

Menu.Item = MenuItem
Menu.SubMenu = SubMenu

export default Menu


================================================
FILE: packages/crd-seed/component/Menu/MenuItem.js
================================================
import { useEffect, useRef } from 'react'
import cx from 'classnames'
import { getMenuStyle } from './util'
import { useMenuContext } from './context'
import styles from './style/index.less'

function MenuItem({
  title = '',
  icon,
  keyValue = '',
  level = 0,
}) {
  const {
    theme,
    selectedKey,
    onSelect,
    onHoverKey,
    inlineCollapsed
  } = useMenuContext()
  const menuItemRef = useRef(null)
  const menuItemselected = keyValue.indexOf(selectedKey) > -1
  const menuUnFoldDelayTime = 300

  useEffect(() => {
    if (menuItemselected && (inlineCollapsed === false)) {
      setTimeout(() => {
        menuItemRef.current.scrollIntoView({
          block: 'center',
          behavior: 'smooth'
        })
      }, menuUnFoldDelayTime)
    }
  }, [keyValue, selectedKey, inlineCollapsed])

  const handleOnClick = () => {
    onSelect(keyValue)
  }

  const renderMenuItem = () => {
    return (
      <li
        className={cx(styles['menu-item'], styles[`menu-${theme}`], {
          [styles['menu-item-selected']]: menuItemselected,
        })}
        onMouseEnter={() => {
          onHoverKey(keyValue)
        }}
        onMouseLeave={() => onHoverKey('')}
        onClick={handleOnClick}
        style={getMenuStyle(level, 'menuItem')}
        ref={menuItemRef}
      >
        {icon ? <span className={cx(styles['menu-icon'])}>{icon}</span> : null}
        <span className={cx(styles['menu-item-title'])}>{title}</span>
      </li>
    )
  }

  return renderMenuItem()
}

export default MenuItem


================================================
FILE: packages/crd-seed/component/Menu/SubMenu.js
================================================
import { useState, useRef, Fragment, Children, cloneElement } from 'react'
import cx from 'classnames'
import { useEnhancedEffect } from 'crd-client-utils'
import Transition from './transition'
import { getMenuStyle } from './util'
import { useMenuContext } from './context'
import styles from './style/index.less'

function useCurrent(
  initialValue
) {
  const currentRef = useRef(initialValue)
  const [state, setState] = useState(initialValue)
  currentRef.current = state
  const set = (value) => {
    currentRef.current = value
    setState(value)
  }
  const get = () => currentRef.current
  return [get, set]
}

function SubMenu({
  children,
  title,
  icon,
  level = 0,
  keyValue = '',
  onTitleClick = () => {},
}) {
  const {
    selectedKey,
    mode,
    hoverKey,
    onHoverKey,
    defaultOpenKeys = [],
  } = useMenuContext()
  const [menuOpen, setMenuOpen] = useState(defaultOpenKeys.indexOf(keyValue) !== -1)
  const curSubmenu = useRef(null)
  const popupSubMenu = useRef(null)

  const [getParentMenuHover, setParentMenuHover] = useCurrent(false)

  const gapDistance = 4

  useEnhancedEffect(() => {
    if (popupSubMenu.current && curSubmenu.current) {
      popupSubMenu.current.style.left = `${curSubmenu.current.getBoundingClientRect().right +
        gapDistance}px`
      popupSubMenu.current.style.top = `${curSubmenu.current.getBoundingClientRect().top}px`
    }
  }, [getParentMenuHover()])

  /**
   * judege if is React Fragment.
   */
  function isReactFragment(variableToInspect) {
    if (variableToInspect.type) {
      return variableToInspect.type === Fragment
    }
    return variableToInspect === Fragment
  }

  /* 行内模式下, 渲染子节点 */
  const renderChild = (child) => {
    return (
      // eslint-disable-next-line quotes
      <>
        {Children.map(child || children, (reactNode) => {
          if (!reactNode || typeof reactNode !== 'object') {
            return null
          }
          const childElement = reactNode
          if (
            isReactFragment(childElement) &&
                  childElement.props.children
          ) {
            return renderChild(childElement.props.children)
          }
          return cloneElement(childElement, {
            level: level + 1,
            ...childElement.props,
          })
        // eslint-disable-next-line quotes
        })}
      </>
    )
  }

  const handleParentMouseEnter = () => {
    setParentMenuHover(true)
    onHoverKey(keyValue)
  }

  const handleParentMouseLeave = () => {
    onHoverKey('')
  }

  /* 处理 menu 开闭状态 */
  const handleMenuStatus = () => {
    onTitleClick(keyValue)
    mode === 'inline' && setMenuOpen(!menuOpen)
  }

  /* 判断 subMenu 是否被选中, 当子节点被选中时, 父节点也会被高亮;
    同时在 vertical 模式时, 当子节点被 hover 时, 父节点也会被高亮; */
  const judgeSubmenuSelect = (reactChildren) => {
    const result = Children.toArray(reactChildren).some((reactNode) => {
      if (!reactNode || typeof reactNode !== 'object') {
        return false
      }

      const childElement = reactNode
      // eslint-disable-next-line no-shadow
      const { keyValue } = childElement.props
      const originKey = keyValue ? String(keyValue) : ''
      if (childElement.type.name === 'MenuItem') {
        return selectedKey.split('').indexOf(originKey) !== -1 || hoverKey === originKey
      }
      if (childElement.type.name === 'SubMenu') {
        return judgeSubmenuSelect(childElement.props.children) || hoverKey === originKey
      }
      return false
    })
    return result
  }

  return (
    <li
      className={cx(styles.menu, styles.submenu, styles[`submenu-${mode}`], {
        [styles['submenu-selected']]: judgeSubmenuSelect(children),
      })}
      onMouseEnter={() => onHoverKey(keyValue)}
      ref={curSubmenu}
    >
      <div
        className={cx(styles['submenu-title'])}
        style={getMenuStyle(level, 'subMenu')}
        onClick={handleMenuStatus}
        onMouseEnter={handleParentMouseEnter}
        onMouseLeave={handleParentMouseLeave}
      >
        <i
          className={cx(styles['submenu-arrow'], {
            [styles['submenu-arrow-open']]: mode === 'inline' && menuOpen,
          })}
        />
        {icon ? <span className={cx(styles['menu-icon'])}>{icon}</span> : null}
        <span className={cx(styles['submenu-title-field'])}>{title}</span>
      </div>
      {mode === 'inline' ? (
        <Transition isShow={menuOpen}>
          <ul className={cx(styles.menu, styles.submenu)}>{renderChild()}</ul>
        </Transition>
      ) : null}
    </li>
  )
}

export { SubMenu }


================================================
FILE: packages/crd-seed/component/Menu/context.js
================================================
import { createContext, useContext } from 'react'

const MenuContext = createContext(undefined)

export const MenuProvider = ({ children, value }) => (
  <MenuContext.Provider value={value}>{children}</MenuContext.Provider>
)

export const useMenuContext = () => useContext(MenuContext)


================================================
FILE: packages/crd-seed/component/Menu/index.js
================================================
import Menu from './Menu'

export default Menu


================================================
FILE: packages/crd-seed/component/Menu/style/index.less
================================================
@import './theme.less';

.menu {
  box-sizing: border-box;
  list-style: none;
  margin: 0;
  padding: 0;
  color: @menu-color;
  background: @menu-background;
  user-select: none;
  transition: background .2s ease-in-out, width .2s ease-in-out;
  overflow: auto;
  user-select: none;
  font-weight: 600;

  &-item {
    // margin: 0 16px 0 15px;
    white-space: nowrap;
    cursor: pointer;
    height: 40px;
    line-height: 40px;
    padding: 0 16px;
    color: @menu-color;
    position: relative;
    cursor: pointer;
    // overflow: hidden;
    // white-space: nowrap;
    // text-overflow: ellipsis;

    &-selected {
      position: relative;
      background: @menu-background-selected;
      color: @menu-color-selected;

      a {
        color: @menu-color-selected!important;
      }
    }

    /* icon */
    i {
      margin-right: 10px;
    }

    a {
      color: rgba(0, 0, 0, 0.8);
      cursor: pointer;
      display: inline-block;
      text-decoration: none;

      &::before {
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        content: '';
        position: absolute;
        background-color: transparent;
      }
    }

    &-title {
      vertical-align: middle;
    }
  }

  &-item:hover {
    color: @menu-color-hover;

    & .menu-icon {
      color: @menu-color-hover;
    }
  }

  .submenu {
    line-height: 40px;
    cursor: pointer;
    overflow: hidden;

    &-title {
      position: relative;
      padding: 0 28px 0 0;
      font-size: 14px;
      cursor: pointer;
      margin-left: 20px;
      // overflow: hidden;
      // white-space: nowrap;
      // text-overflow: ellipsis;
    }

    &-title:hover {
      color: @menu-color-hover;

      .submenu-arrow {

        &::before,
        &::after {
          background: @menu-color-hover;
        }
      }
    }

    &-arrow {
      display: inline-block;
      width: 10px;
      flex-shrink: 0;
      // margin: 0 3px 0 1px;
      margin: 0 3px 0 -12px;
      position: relative;
      top: -5px;

      &::before,
      &::after {
        content: '';
        position: absolute;
        width: 6px;
        height: 1.5px;
        background: @arrow-background;
        transition: background .2s cubic-bezier(0.645, 0.045, 0.355, 1),
          transform .2s cubic-bezier(0.645, 0.045, 0.355, 1),
          top .2s cubic-bezier(0.645, 0.045, 0.355, 1),
          -webkit-transform .2s cubic-bezier(0.645, 0.045, 0.355, 1);
      }

      &::before {
        transform: rotate(45deg) translateY(-2px);
      }

      &::after {
        transform: rotate(-45deg) translateY(2px);
      }

      &-open {
        &::before {
          transform: rotate(-45deg) translateX(2px);
        }

        &::after {
          transform: rotate(45deg) translateX(-2px);
        }
      }
    }

    &-selected {
      color: @menu-color-selected;
    }
  }

  &-inline {
    width: 100%;

    .menu-item {
      &-selected {
        &::after {
          content: '';
          position: absolute;
          width: 3px;
          background: @menu-inline-selected;
          top: 0;
          bottom: 0;
          right: 0;
        }
      }
    }

    .submenu {
      &-arrow {
        &::before {
          transform: rotate(45deg) translateY(-2px);
        }

        &::after {
          transform: rotate(-45deg) translateY(2px);
        }

        &-open {
          &::before {
            transform: rotate(-45deg) translateX(2px);
          }

          &::after {
            transform: rotate(45deg) translateX(-2px);
          }
        }
      }
    }

    /* fold */
    &-collapsed {
      width: 0px;

      .menu-item {
        padding: 0 24px !important;

        &-title {
          opacity: 0;
        }
      }

      .submenu {
        &-title {
          padding: 0 24px !important;

          &-field {
            opacity: 0;
          }

          .submenu-arrow {
            display: none;
          }
        }
      }
    }
  }

  &-icon {
    margin-right: 4px;
  }
}

/* submenu fold/unfold animation */
.collapse-transition {
  overflow: hidden;
  transition: height .2s ease-in-out;
}

.toggle {
  position: absolute;
  width: 30px;
  height: 30px;
  left: 240px;
  top: 200px;
  transition: left .2s linear;
  box-shadow: 2px 0 8px rgba(0, 0, 0, .15);
  border-radius: 0 4px 4px 0;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #fff;

  &-icon {
    position: relative;
    background: 0 0;
    width: 12px;
    height: 2px;
    background: #333;
    transition: background .2s cubic-bezier(.78, .14, .15, .86);

    &::before,
    &::after {
      content: '';
      display: block;
      position: absolute;
      background: #333;
      width: 50%;
      height: 2px;
    }

    &::before {
      transform: translateY(-2px) rotate(-45deg);
    }

    &::after {
      transform: translateY(2px) rotate(45deg);
    }

    &-close {
      background: #333;

      &::before,
      &::after {
        content: '';
        display: block;
        position: absolute;
        background: #333;
        width: 100%;
        height: 2px;
        transform: rotate(0deg);
      }

      &::before {
        top: -4px;
      }

      &::after {
        top: 4px;
      }
    }
  }

  &-collapsed {
    left: 0px;
  }
}

================================================
FILE: packages/crd-seed/component/Menu/style/theme.less
================================================
/* light */
@menu-color: rgba(0, 0, 0, 0.65);
@menu-background: #fff;
@menu-color-hover: #1890ff;
@menu-color-selected: #1890ff;
@menu-inline-selected: #1199ee;
@menu-background-selected: #e6f7ff;
@arrow-background: #333;

================================================
FILE: packages/crd-seed/component/Menu/transition.js
================================================
import { useEffect, useRef, useCallback } from 'react'
import styles from './style/index.less'

const ANIMATION_DURATION = 200

export default function Transition({
  isShow,
  children,
}) {
  const mounted = useRef(false)
  const collapseRef = useRef(null)
  const timer = useRef({})

  // prepare
  const beforeEnter = () => {
    const el = collapseRef.current
    el.style.height = '0px'
  }

  const afterEnter = useCallback(() => {
    const el = collapseRef.current
    el.style.display = 'block'
    el.style.height = ''
  }, [])

  // start
  const enter = useCallback(() => {
    const el = collapseRef.current
    el.style.display = 'block'
    if (el.scrollHeight !== 0) {
      el.style.height = `${el.scrollHeight}px`
    }

    timer.current.enterTimer = setTimeout(() => afterEnter(), ANIMATION_DURATION)
  }, [afterEnter])

  const beforeLeave = useCallback(() => {
    const el = collapseRef.current

    el.style.display = 'block'
    if (el.scrollHeight !== 0) {
      el.style.height = `${el.scrollHeight}px`
    }
  }, [])

  const afterLeave = useCallback(() => {
    const el = collapseRef.current

    el.style.display = 'none'
    el.style.height = ''
  }, [])

  const leave = useCallback(() => {
    const el = collapseRef.current
    if (el.scrollHeight !== 0) {
      el.style.height = '0px'
    }
    timer.current.leaveTimer = setTimeout(() => afterLeave(), ANIMATION_DURATION)
  }, [afterLeave])

  const triggerChange = useCallback(
    (isShow) => {
      clearTimeout(timer.current.enterTimer)
      clearTimeout(timer.current.leaveTimer)
      if (isShow) {
        beforeEnter()
        enter()
      } else {
        beforeLeave()
        leave()
      }
    },
    [beforeLeave, enter, leave]
  )

  useEffect(() => {
    if (!mounted.current) {
      mounted.current = true
      beforeEnter()
      if (isShow) {
        enter()
      }
    } else {
      triggerChange(isShow)
    }
  }, [enter, isShow, triggerChange])

  return (
    <div className={styles['collapse-transition']} ref={collapseRef}>
      {children}
    </div>
  )
}


================================================
FILE: packages/crd-seed/component/Menu/util.js
================================================
/* 获取 menu 样式
  level: 层级
*/
const getMenuStyle = (level, type) => {
  const basicStyle = {
    fontSize: level === 0 ? '14px' : '12px',
  }
  if (type === 'menuItem') {
    return {
      ...basicStyle,
      paddingLeft: `${21 + (level * 16)}px`,
    }
  }
  return {
    ...basicStyle,
    paddingLeft: `${level * 16}px`,
  }
}

export { getMenuStyle }


================================================
FILE: packages/crd-seed/component/NoMatch/index.js
================================================
import styles from './index.less'

const NoMatch = () => {
  // eslint-disable-next-line no-undef
  const { user, repo } = DOCSCONFIG || {}
  return (
    <table className={styles.noMatch}>
      <tbody>
        <tr>
          <td>
            <h1>404</h1>
            <div>你似乎来到了没有知识存在的荒原...</div>
            <section>在 github 访问<a href={`https://github.com/${user}/${repo}`}>该项目</a></section>
          </td>
        </tr>
      </tbody>
    </table>
  )
}

export default NoMatch


================================================
FILE: packages/crd-seed/component/NoMatch/index.less
================================================
.noMatch {
  position: relative;
  width: 100%;
  height: 80%;
  text-align: center;
  border-spacing: 0;
  border-collapse: collapse;
  color: #a2a2a2;

  h1 {
    color: #717171;
    font-size: 68px;
    line-height: 54px;
    font-weight: 500;
  }
}

================================================
FILE: packages/crd-seed/component/Search/README.md
================================================
### API

|    props    | description |  type  | default  |
| :---------: | :---------: | :----: | :------: |
| placeholder | placeholder | string | 'Search' |
|  className  |     css     | string |    --    |


================================================
FILE: packages/crd-seed/component/Search/index.js
================================================
import { useState, useEffect } from 'react'
import cx from 'classnames'
import Icon from '../Icon'
import styles from './index.less'

const Search = ({
  placeholder = 'Search',
  className,
}) => {
  const [value, setValue] = useState('')
  // const [searchContent, setSearchContent] = useState([]);
  // const showSearchContent = value.length > 0 && searchContent.length > 0;
  useEffect(() => {
    /* eslint-disable-next-line no-undef */
    // if (SEARCHCONTENT) {
    //   /* eslint-disable-next-line no-undef */
    //   const filterSearch = SEARCHCONTENT.filter((r) => {
    //     return r.title.includes(value) || r.content.includes(value);
    //   });
    //   setSearchContent(filterSearch);
    // }
  }, [value])
  return (
    <div className={cx(styles.search, className)}>
      <Icon type="search" size="14" />
      <input
        placeholder={placeholder}
        value={value}
        onChange={(e) => {
          setValue(e.target.value)
        }}
      />
      {/* {showSearchContent ? (
        <ul className={styles.panel}>
          {searchContent.map((search) => {
            return (
              <li
                className={styles.searchItem}
                onClick={() => {
                  location.hash = search.url;
                }}
                key={search.url}
              >
                <span className={styles.title}>{search.title}</span>
                <span className={styles.content}>{search.content}</span>
              </li>
            );
          })}
        </ul>
      ) : null} */}
    </div>
  )
}

export default Search


================================================
FILE: packages/crd-seed/component/Search/index.less
================================================
.search {
  position: relative;

  input {
    line-height: 1.5;
    font-size: 14px;
    color: #999;
    outline: 0;
    border-radius: 4px;
    border: 0;
    width: 200px;
  }

  .panel {
    position: absolute;
    width: 350px;
    top: 50px;
    left: 0;
    background: rgba(255, 255, 255);
    max-height: 400px;
    box-shadow: rgba(101, 119, 134, 0.2) 0px 0px 15px, rgba(101, 119, 134, 0.15) 0px 0px 3px 1px;
    overflow-y: auto;

    li {
      padding: 0 5px;
      list-style: none;
      border: 1px solid rgb(230, 236, 240);
    }
  }

  .searchItem {
    display: flex;
  }

  .title {
    display: inline-block;
    width: 90px;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
  }

  .content {
    display: inline-block;
    width: 245px;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
  }
}

================================================
FILE: packages/crd-seed/component/Tags/index.js
================================================
import { Link, useMatch } from 'react-router-dom'
import { ifProd } from 'crd-client-utils'
import { ifAddPrefix } from '../../utils'
import styles from './index.less'

/**
 * name: the name of tag category.
 */
const Tags = () => {
  const { user, repo } = DOCSCONFIG || {}
  const path = ifAddPrefix ? `/${repo}/tags/:name` : '/tags/:name'
  const routeMatch = useMatch(path) || {}
  const { name } = routeMatch.params || {}

  return (
    <div className={styles.tags}>
      <div className={styles['tags-title']}>{name || 'Tags'}</div>
      <div className={styles['tags-content']}>
        {
          name
            ? mapTagsWithArticle.find(({ tagName }) => tagName === name)?.mapArticle.map(({ path, title }) => {
              return <Link
                className={styles['tags-text']}
                to={ifProd ? `/${repo}${path}` : `${path}`}
                key={path}
              >
                {title}
              </Link>
            })
            : mapTagsWithArticle.map(({ tagName }) => {
              return <Link
                className={styles['tags-text']}
                to={ifProd ? `/${repo}/tags/${tagName}` : `/tags/${tagName}`}
                key={tagName}
              >
                {tagName}
              </Link>
            })
        }
      </div>
    </div>
  )
}

export default Tags


================================================
FILE: packages/crd-seed/component/Tags/index.less
================================================
.tags {
  width: 100%;
  padding: 0 20px;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;

  &-title {
    font-size: 24px;
    font-weight: 400;
    text-align: center;
  }

  &-content {
    width: 100%;
    margin-top: 20px;
  }

  &-text {
    color: #6f6f6f;
    padding: 10px;
    display: inline-flex;
  }
}

================================================
FILE: packages/crd-seed/index.js
================================================
import { Routes, Route, Navigate } from 'react-router-dom'
import BasicLayout from './layout'
import NoMatch from './component/NoMatch'
import './index.less'

// run in the Web/Router.js
const ThemeSeed = (props) => {
  return (
    <Routes>
      <Route path="/*" element={<BasicLayout {...props} />} />
      <Route path="/404" element={<NoMatch />} />
    </Routes>
  )
}

export default ThemeSeed


================================================
FILE: packages/crd-seed/index.less
================================================
body {
  font-family: cursive, sans-serif;
}

body,
html {
  position: relative;
}

body,
html,
ul,
li {
  margin: 0;
  padding: 0;
}

a {
  color: #314659;
  text-decoration: none;
  transition: color .3s;

  &:hover {
    color: #1890ff;
  }
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

:global {
  #root {
    height: 100%;
    background: white;
  }
}

================================================
FILE: packages/crd-seed/language/index.js
================================================
const languageMap = {
  en: {
    create_tm: 'create',
    modify_tm: 'modify',
  },
  'zh-cn': {
    create_tm: '创建',
    modify_tm: '修改',
  },
}

export default languageMap


================================================
FILE: packages/crd-seed/layout/index.js
================================================
import * as React from 'react'
import { Routes, Link, Route, Navigate, useLocation } from 'react-router-dom'
import cx from 'classnames'
import { ifDev, ifProd, ifPrerender } from 'crd-client-utils'
const Giscus = require('@giscus/react')
import Menu from '../component/Menu'
import Icon from '../component/Icon'
import Affix from '../component/Affix'
import Header from '../component/Header'
import Footer from '../component/Footer'
import Tags from '../component/Tags'
import languageMap from '../language'
import { isMobile, ifAddPrefix } from '../utils'
import { getOpenSubMenuKeys } from './utils'
import logo from '../crd.logo.svg'
import styles from './index.less'
import '../style/mobile.less'

const { useState, useEffect, useMemo } = React
const SubMenu = Menu.SubMenu

function BasicLayout({
  routeData,
  menuSource
}) {
  const location = useLocation()
  const { pathname } = location
  const {
    user,
    repo,
    branch = 'main',
    language = 'en',
    menuOpenKeys,
    tags,
    comment
  } = DOCSCONFIG || {}

  const [inlineCollapsed, setInlineCollapsed] = useState(true)
  const [selectedKey, setSelectedKey] = useState('')
  const curOpenKeys = getOpenSubMenuKeys({
    pathname,
    menuSource,
    menuOpenKeys
  })
  const defaultPath = (routeData.find(data => data.path === '/README')
    && routeData.find(data => data.path === '/README').mdconf
    && routeData.find(data => data.path === '/README').mdconf.abbrlink) || 'README'

  useEffect(() => {
    if (ifPrerender) {
      scrollToTop()
      INJECT?.inject?.()
    }
  }, [])

  useEffect(() => {
    INJECT?.injectWithPathname?.(pathname)
  }, [pathname])

  useEffect(() => {
    const { pathname } = location
    let newPathName = pathname
    // fix https://github.com/MuYunyun/create-react-doc/issues/195
    if (newPathName.endsWith('/')) {
      newPathName = newPathName.slice(0, newPathName.length - 1)
    }
    if (newPathName.startsWith(`/${repo}`)) {
      newPathName = newPathName.slice(`/${repo}`.length, newPathName.length)
    }
    setSelectedKey(newPathName || defaultPath)
  }, location.pathname)

  const scrollToTop = () => {
    document.body.scrollTop = 0
    document.documentElement.scrollTop = 0
    window.scrollTo(0, 0)
  }
  const renderSubMenuItem = (menus) => {
    return (
      <>
        {menus.map((item, index) => {
          const { mdconf, routePath } = item || {}
          const { abbrlink } = mdconf || {}
          const path = abbrlink ? `/${abbrlink}` : routePath
          // item.path carrys .md here.
          return item.children && item.children.length > 0 ? (
            <SubMenu
              key={index}
              keyValue={item.path}
              title={item.name}
              icon={<Icon type="folder" size={16} />}
            >
              {renderSubMenuItem(item.children)}
            </SubMenu>
          ) : (
            <Menu.Item
              key={index}
              icon={<Icon type="file" size={16} />}
              keyValue={abbrlink ? `/${abbrlink}` : item.path}
              title={
                item &&
                item.type === "directory" &&
                item.props &&
                item.props.isEmpty ? (
                  <span>
                    {(item.mdconf && item.mdconf.title) || item.name}
                  </span>
                ) : (
                  <Link
                    to={ifProd ? `/${repo}${path}` : `${path}`}
                    replace={pathname.indexOf(path) > -1}
                  >
                    {item && item.mdconf && item.mdconf.title
                      ? item.mdconf.title
                      : item.title}
                  </Link>
                )
              }
            />
          )
        })}
      </>
    )
  }
  const renderMenu = (menus) => {
    if (menus.length < 1) return null

    return (
      <Affix
        offsetTop={0}
        className={styles.affixPlaceholder}
        wrapperClassName={styles.affixWrapper}
        width={inlineCollapsed ? 0 : 240}
      >
        <Menu
          inlineCollapsed={inlineCollapsed}
          toggle={() => {
            setInlineCollapsed(!inlineCollapsed)
          }}
          menuStyle={{
            height: "100vh",
            overflow: "auto",
          }}
          selectedKey={selectedKey}
          onSelect={(keyValue) => {
            setSelectedKey(keyValue)
          }}
          defaultOpenKeys={curOpenKeys}
        >
          {renderSubMenuItem(menus || [])}
        </Menu>
      </Affix>
    )
  }
  /**
   * This section is to show article's relevant information
   * such as edit in github and so on.
   */
  const renderPageHeader = () => {
    const curMenuSource = routeData.filter(r => {
      if (r.props.type === 'directory') return false
      return pathname.indexOf(r.mdconf.abbrlink) > -1 || decodeURIComponent(pathname).indexOf(r.path) > -1
    })
    const editPathName = curMenuSource[0] && curMenuSource[0].props.path
    const isNotTagPage = location.pathname.indexOf('/tags') === -1
    return (
      <div className={cx(styles.pageHeader)}>
        {user && repo && isNotTagPage ? (
          <a
            href={`https://github.com/${user}/${
              repo
            }/edit/${branch}${editPathName}`}
            target="_blank"
          >
            <Icon className={cx(styles.icon)} type="edit" size={13} />
            <span>Edit in GitHub</span>
          </a>
        ) : null}
      </div>
    )
  }

  /**
   * This section is to render comment area.
   * Every pathname should has its own comment module.
   */
  const renderComment = useMemo(() => {
    return <Giscus
      key={pathname}
      id="comments"
      repo={`${user}/${repo}`}
      category="General"
      mapping="pathname"
      strict="0"
      reactionsEnabled="1"
      emitMetadata="0"
      inputPosition="top"
      theme="preferred_color_scheme"
      lang="en"
      loading="lazy"
      crossorigin="anonymous"
      async
      {...comment?.GiscusConfig}
    />
  }, [pathname])

  /**
   * This section is to show article's relevant information
   * such as edit in created time、edited time and so on.
   */
  const renderPageFooter = () => {
    // in local env, data.path is to be /READEME, however pathname may be /Users/mac/.../.crd-dist/READEME/index.html
    const matchData = routeData.find((data) => pathname.indexOf(data.path) > -1)
    const matchProps = matchData && matchData.props
    return (
      <div className={cx(styles.pageFooter)}>
        {matchProps && matchProps.birthtime ? (
          <span className={cx(styles.position)}>
            <Icon className={cx(styles.icon)} type="create-time" size={13} />
            {languageMap[language].create_tm}:
            <span>{matchProps.birthtime}</span>
          </span>
        ) : null}
        {matchProps && matchProps.mtime ? (
          <span className={cx(styles.position)}>
            <Icon className={cx(styles.icon)} type="update-time" size={13} />
            {languageMap[language].modify_tm}:
            <span>
              {routeData.find((data) => pathname.indexOf(data.path) > -1).props.mtime}
            </span>
          </span>
        ): null}
      </div>
    )
  }
  const isCurentChildren = () => {
    const getRoute = routeData.filter((data) => pathname.indexOf(data.path) > -1)
    const article = getRoute.length > 0 ? getRoute[0].article : null
    const childs = menuSource.filter(
      (data) =>
        article === data.article && data.children && data.children.length > 1
    )
    return childs.length > 0
  }
  const isChild = isCurentChildren()
  const renderMenuContainer = () => {
    return (
      <>
        <nav
          className={cx(styles.menuWrapper, {
            [`${styles['menuWrapper-inlineCollapsed']}`]: inlineCollapsed,
          })}
        >
          {renderMenu(menuSource)}
        </nav>
        <div
          className={cx({
            [`${styles.menuMask}`]: isMobile && !inlineCollapsed,
          })}
          onClick={(e) => {
            e.stopPropagation()
            setInlineCollapsed(true)
          }}
        />
      </>
    )
  }

  const renderContent = () => {
    return (
      <div
        className={cx(`${styles.content}`, {
          [`${styles["content-fullpage"]}`]: inlineCollapsed || isMobile,
        })}
      >
        <Routes>
          {/* see https://reacttraining.com/react-router/web/api/Redirect/exact-bool */}
          <Route
            path={ifAddPrefix ? `/${repo}` : `/`}
            element={<Navigate to={ifAddPrefix ? `/${repo}/${defaultPath}` : `/${defaultPath}`} replace />}
          />
          {routeData.map((item) => {
            const { path, mdconf, component } = item
            const { abbrlink } = mdconf
            const enhancePath = abbrlink ? `/${abbrlink}` : path
            const Comp = component
            return (
              <Route
                key={enhancePath}
                path={ifAddPrefix ? `/${repo}${enhancePath}` : enhancePath}
                element={<Comp {...item} />}
              />
            )
          })}
          {
            tags
              ? <>
                <Route
                  key='/tags'
                  path={ifAddPrefix ? `/${repo}/tags` : '/tags'}
                  element={<Tags />}
                />
                <Route
                  key='/tags/:name'
                  path={ifAddPrefix ? `/${repo}/tags/:name` : '/tags/:name'}
                  element={<Tags />}
                />
              </>
              : null
          }
          {/* Todo: follow up how to use Redirect to back up the rest of route. */}
          {/* <Redirect path='/' to={ifAddPrefix ? `/${repo}/404` : `/404`} /> */}
        </Routes>
        {comment?.GiscusConfig ? renderComment : null}
        {renderPageFooter()}
      </div>
    )
  }

  return (
    <div className={styles.wrapper}>
      <Header logo={logo} />
      <div
        className={cx(styles.wrapperContent, {
          [styles.wrapperMobile]: isMobile,
        })}
      >
        {renderPageHeader()}
        {renderMenuContainer()}
        {renderContent()}
        <Footer inlineCollapsed={inlineCollapsed} />
      </div>
    </div>
  )
}

export default BasicLayout


================================================
FILE: packages/crd-seed/layout/index.less
================================================
@import '../style/base.less';

.wrapper {
  &::after {
    content: '';
    display: block;
    clear: both;
  }
}

.wrapperContent {
  padding: 40px 0 0 0;
  margin: 0px auto 0;
  position: relative;
  min-height: 100vh;

  .pageHeader,
  .pageFooter {
    position: absolute;
    right: 40px;
    font-size: 13px;
    display: flex;
    align-items: center;
  }

  .pageHeader {
    top: 20px;
  }

  .pageFooter {
    bottom: -23px;

    .position {
      margin-right: 6px;
      display: flex;
      align-items: center;
    }
  }

  .icon {
    margin-right: 3px;
  }
}

.wrapperMobile {
  // padding-top: 0;
}

.menuWrapper {
  width: 240px;
  float: left;
  position: absolute;
  font-size: 14px;
  color: #6d6d6d;
  transition: width .2s linear;
  z-index: @menu-zIndex;

  ul li {
    list-style: none;
  }

  ul ul {
    padding: 0 0 0 0;
  }

  li {
    // padding: 0 0 0 21px;
    line-height: 40px;
  }

  li li {
    padding: 0 0 0 0;
  }

  // :global {
  //   .active a {
  //     color: #1890ff;
  //   }
  // }

  &-inlineCollapsed {
    width: 0;
  }

  .affixPlaceholder {
    transition: width .2s linear;
  }

  .affixWrapper {
    border-right: 1px solid rgb(233, 233, 233);
    transition: width .2s linear;
    width: 0px;
  }
}

.menuMask {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  background: rgba(0, 0, 0, .3);
  z-index: @menu-mask-zIndex;
  transition: opacity .3s cubic-bezier(.78, .14, .15, .86)
}

.content {
  margin-left: 240px;
  min-height: 300px;
  transition: margin-left .2s ease-in-out;
  position: relative;

  &-fullpage {
    margin-left: 0px;
  }
}

.contentNoMenu {
  min-height: 400px;
}

giscus-widget {
  display: flex;
  margin: auto;
  max-width: 940px;
}


================================================
FILE: packages/crd-seed/layout/utils.js
================================================
/**
 * get keys of open sub menu from pathname
 *  {
 *    pathname: pathname of location,
 *      when pathname is /9f41fc98, the result is ['/docs/主题'].
 *      when pathname is /测试/测试路由, the result is ['/docs/测试']
 *    menuOpenKeys: means extra show open keys in config.yml
 *  }
 */
function getOpenSubMenuKeys({
  pathname,
  menuSource,
  menuOpenKeys
}) {
  const result = []
  getOpenSubMenuKeysForAbbrLink(
    menuSource,
    decodeURI(pathname),
    result
  )

  /** default open menu from config.yml */
  if (menuOpenKeys) {
    result.push(...menuOpenKeys.split(','))
  }

  return result
}

function getOpenSubMenuKeysForAbbrLink(source, pathname, result) {
  for (let i = 0; i < source.length; i++) {
    const { type, path, mdconf } = source[i]
    if (type === 'directory') {
      result.push(path)
      const ifFind = getOpenSubMenuKeysForAbbrLink(source[i].children, pathname, result)
      if (ifFind) return true
      result.pop()
    } else {
      if (
        pathname.indexOf(mdconf.abbrlink) > -1           // used with abbrlink
        || (pathname.indexOf(source[i].routePath) > -1)  // used not with abbrlink
      ) {
        return true
      }
    }
  }
}

export { getOpenSubMenuKeys }


================================================
FILE: packages/crd-seed/package.json
================================================
{
  "name": "crd-seed",
  "version": "1.10.3",
  "description": "Default Theme with Create React Doc",
  "main": "index.js",
  "dependencies": {
    "@giscus/react": "2.2.2",
    "crd-client-utils": "^1.8.2",
    "react-router-dom": "^6.3.0",
    "react-switch": "^5.0.1"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/MuYunyun/create-react-doc",
    "directory": "packages/theme-seed"
  },
  "keywords": [],
  "publishConfig": {
    "access": "public"
  },
  "author": "muyunyun",
  "license": "MIT",
  "gitHead": "ffc5e4cbc94a7356da558c2dbf46e2f39bb8b199"
}


================================================
FILE: packages/crd-seed/style/base.less
================================================
// z-index
@menu-zIndex: 99;
@menu-mask-zIndex: 90;
@header-zIndex: 100;

================================================
FILE: packages/crd-seed/style/mobile.less
================================================
// remove the blue area in the mobile
:global {
  div,
  input,
  textarea,
  button,
  select,
  a,
  li {
    -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
  }
}

================================================
FILE: packages/crd-seed/utils/index.js
================================================
import isClient from 'diana/lib/isClient'
import { ifProd, ifPrerender } from 'crd-client-utils'

/** judge if is in mobile */
const isMobile = isClient() ? 'ontouchend' in window : false

// decide to if add prefix for path, eg: '/' or '/${repo}'
const ifAddPrefix = ifProd && !ifPrerender

export { isMobile, ifAddPrefix }


================================================
FILE: packages/crd-templates/.npmrc
================================================
# .npmrc

registry=https://registry.npmjs.org/


================================================
FILE: packages/crd-templates/README.md
================================================
                                                     _.-"\
                                                _.-"      \
                                              ,-"          \
                                              \    create    \
                                              \ \    react    \
                                              \ \      doc     \
                                                \ \         _.-;
                                                \ \    _.-"   :
                                                  \ \,-"    _.-"
                                                  \(   _.-"
                                                    `--"

# crd-templates

crd-templates 集成了 create-react-doc 中相关模板文件。


================================================
FILE: packages/crd-templates/default/.github/workflows/gh-pages.yml
================================================
name: github pages
on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-18.04
    steps:
      - uses: actions/checkout@v2

      - name: Setup Node
        uses: actions/setup-node@v2.1.3
        with:
          node-version: '12.x'

      - name: Get yarn cache
        id: yarn-cache
        run: echo "::set-output name=dir::$(yarn cache dir)"

      - name: Cache dependencies
        uses: actions/cache@v2
        with:
          path: ${{ steps.yarn-cache.outputs.dir }}
          key: ${{ runner.os }}-website-${{ hashFiles('**/yarn.lock') }}
          restore-keys: |
            ${{ runner.os }}-website-

      - run: yarn install --frozen-lockfile
      - run: yarn build

      - name: Deploy
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: .crd-dist

================================================
FILE: packages/crd-templates/default/.npmrc
================================================
# .npmrc
registry=https://registry.npmjs.org/


================================================
FILE: packages/crd-templates/default/Introduction/hello_world.md
================================================
Write docs happily now.

If there is any problem, welcome give issue [there](https://github.com/MuYunyun/create-react-doc/issues).


================================================
FILE: packages/crd-templates/default/README.md
================================================
                                                     _.-"\
                                                _.-"      \
                                              ,-"          \
                                              \    create    \
                                              \ \    react    \
                                              \ \      doc     \
                                                \ \         _.-;
                                                \ \    _.-"   :
                                                  \ \,-"    _.-"
                                                  \(   _.-"
                                                    `--"

[![npm version](https://img.shields.io/npm/v/create-react-doc)](https://badge.fury.io/js/create-react-doc)
[![week download](https://img.shields.io/npm/dw/create-react-doc.svg)](https://www.npmjs.com/package/create-react-doc)
![views](https://raw.githubusercontent.com/MuYunyun/create-react-doc/traffic/traffic-create-react-doc/views.svg)
![views](https://raw.githubusercontent.com/MuYunyun/create-react-doc/traffic/traffic-create-react-doc/views_per_week.svg)
![clones](https://raw.githubusercontent.com/MuYunyun/create-react-doc/traffic/traffic-create-react-doc/clones_per_week.svg)
![LICENSE MIT](https://img.shields.io/npm/l/create-react-doc.svg)

# Create React Doc

Create React Doc 是一个使用 React 的 markdown 文档站点生成工具。就像 [create-react-app](https://github.com/facebook/create-react-app) 一样,开发者可以使用 Create React Doc 来开发、部署 markdown 站点或者博客而无需关心站点环境配置信息。

## 特性

* 建站理念: 文件即站点 (Files as a site)。
* 开箱即用: 一键生成可运行文档站点, 无需关心站点环境配置信息。
* 自定义展示目录: 天然适合搭建 monorepo 文档、博客等站点。
* 性能: 文档支持懒加载提升站点加载速度。
* 工作流: 集成 Github action, 自动化打包、发布站点。

> [快速上手](http://muyunyun.cn/create-react-doc/%E5%BF%AB%E9%80%9F%E4%B8%8A%E6%89%8B)

## 主题

当前默认使用的主题是 [crd-seed](https://github.com/MuYunyun/create-react-doc/tree/main/packages/crd-seed)。

使用该主题搭建的站点

* [blog](http://muyunyun.cn/blog)
  * ![](http://with.muyunyun.cn/90d3e357a31649b9466a828a92b6d88d.jpg)
  * ![](http://with.muyunyun.cn/2e7440e4256debda2d73a4e6392c7146.jpg-300)
* [diana](https://muyunyun.cn/diana/)

> 如果你的产品从中受益,欢迎<a href="https://github.com/MuYunyun/create-react-doc/issues/new" target="_blank">留言补充</a>

## 快速上手

**create-react-doc** 非常容易上手。开发者不需要额外安装或配置 webpack 或者 Babel 等工具,它们被内置隐藏在脚手架中,因此开发者可以专心于文档的书写。

如果你想在当前文件下建立站点文件 `doc`, 这里提供如下三种方式快速建站:

### npx

```bash
npx create-react-doc doc
```

### npm

```bash
npm init create-react-doc doc
```

### yarn

```bash
yarn create react-doc doc
```

![](http://with.muyunyun.cn/0f0cf6e8cb68b18399eac2927f74b063.jpg)

> 如果想把模板内容内容拉取到当前文件夹, 则可以将如上命令的 `doc` 替换为 `.`, 比如执行 `npx create-react-doc .`。

接着执行 `cd doc && yarn && yarn start`, 可以在 `localhost: 3000` 预览站点, 如果站点文档发生改变, 站点将自动重新加载。

<img src="http://with.muyunyun.cn/2bbd4d8da3165e1a09a88f5e6a114009.jpg" width="900" />

## 高阶用法

与 git 文件结构类似, 如果在展示的文件夹中有私有文件不方便展示在文档站点, 可以在 `.gitignore` 文件中设置过滤文件, 这样它们就不会展示在文档站点中了。eg: [.gitignore](https://github.com/MuYunyun/blog/blob/main/.gitignore)

## 其它工具

* [crd-leetcode-cli](https://github.com/MuYunyun/create-react-doc/tree/main/packages/leetcode-cli): 提供将 [leetcode](https://leetcode-cn.com/) 中已 AC 的题目转化为 markdown 表格的能力。

================================================
FILE: packages/crd-templates/default/_.gitignore
================================================
node_modules
package-lock.json
.DS_Store
.cache
.crd-dist


================================================
FILE: packages/crd-templates/default/_config.yml
================================================
# details see http://muyunyun.cn/create-react-doc/默认主题

# Site
# title:

# Menu dir
## you can also set detailed dir, such as BasicSkill/css
menu: ['Introduction']
## set init open menu keys
# menuOpenKeys:

# site theme
theme: crd-seed

# Github
## if you want to editing pages on github, you should config these arguments.
# user:
# repo:

# Available values: en | zh-cn
language: en

================================================
FILE: packages/crd-templates/default/_package.json
================================================
{
  "name": "{{name}}",
  "version": "1.0.0",
  "description": "Describe {{name}} here",
  "scripts": {
    "start": "react-doc start",
    "build": "react-doc build",
    "deploy": "react-doc deploy"
  },
  "keywords": [
    "{{name}}",
    "react-doc",
    "react"
  ],
  "devDependencies": {
    "create-react-doc": "{{crdVersion}}"
  },
  "author": "",
  "license": "MIT"
}


================================================
FILE: packages/crd-templates/package.json
================================================
{
  "name": "crd-templates",
  "version": "1.10.0",
  "description": "Default Templates with Create React Doc",
  "repository": {
    "type": "git",
    "url": "https://github.com/MuYunyun/create-react-doc",
    "directory": "packages/templates"
  },
  "keywords": [],
  "publishConfig": {
    "access": "public"
  },
  "author": "muyunyun",
  "license": "MIT",
  "gitHead": ""
}


================================================
FILE: packages/crd-templates/theme/default/Introduction/hello_world.md
================================================
Write docs happily now.

If there is any problem, welcome give issue [here](https://github.com/MuYunyun/create-react-doc/issues).


================================================
FILE: packages/crd-templates/theme/default/README.md
================================================
                                                     _.-"\
                                                _.-"      \
                                              ,-"          \
                                              \    create    \
                                              \ \    react    \
                                              \ \      doc     \
                                                \ \         _.-;
                                                \ \    _.-"   :
                                                  \ \,-"    _.-"
                                                  \(   _.-"
                                                    `--"

### {{name}}

This is {{name}} theme for [create-react-doc](https://github.com/MuYunyun/create-react-doc).

================================================
FILE: packages/crd-templates/theme/default/_.gitignore
================================================
node_modules
package-lock.json
.DS_Store
.cache
.crd-dist
config.yml
Introduction

================================================
FILE: packages/crd-templates/theme/default/_.npmrc
================================================
# .npmrc
registry=https://registry.npmjs.org/


================================================
FILE: packages/crd-templates/theme/default/_config.yml
================================================
# details see http://muyunyun.cn/create-react-doc/默认主题

# Site
# title:

# Menu dir
## you can also set detailed dir, such as BasicSkill/css
menu: ['Introduction']
## set init open menu keys
# menuOpenKeys:

# site theme
devTheme: ./index

# Github
## if you want to editing pages on github, you should config these arguments.
# user:
# repo:

# Available values: en | zh-cn
language: en

================================================
FILE: packages/crd-templates/theme/default/_index.js
================================================
// The position of current index.js should be kept.
import { Routes, Route, Navigate } from 'react-router-dom'
import styles from './index.less'

const {{name}} = ({routeData, menuSource}) => {
  return (
    <div className={styles.center}>
      <Routes>
        <Route
          path='/'
          element={<Navigate to="/Introduction/hello_world" replace />}
        />
        {routeData.map((item) => {
          const Comp = item.component
          return (
            <Route
              key={item.path}
              path={item.path}
              element={<Comp {...item} />}
            />
          )
        })}
      </Routes>
    </div>
  )
}

export default {{name}}


================================================
FILE: packages/crd-templates/theme/default/_index.less
================================================
.center {
  height: 100vh;
  width: 100vw;
  display: flex;
  align-items: center;
  justify-content: center;
}

================================================
FILE: packages/crd-templates/theme/default/_package.json
================================================
{
  "name": "{{name}}",
  "version": "0.1.0",
  "description": "{{name}} theme for create-react-doc",
  "main": "index.js",
  "scripts": {
    "start": "react-doc start"
  },
  "keywords": [
    "{{name}}",
    "react-doc",
    "react",
    "create-react-doc",
    "theme"
  ],
  "devDependencies": {
    "create-react-doc": "{{crdVersion}}"
  },
  "dependencies": {
    "react-router-dom": "^6.3.0"
  },
  "author": "",
  "license": "MIT"
}


================================================
FILE: packages/crd-theme/.npmrc
================================================
# .npmrc

registry=https://registry.npmjs.org/

# https://github.com/sass/node-sass#binary-configuration-parameters
sass_binary_site=https://npm.taobao.org/mirrors/node-sass/

# https://github.com/Medium/phantomjs#deciding-where-to-get-phantomjs
# phantomjs_cdnurl=http://cnpmjs.org/downloads
phantomjs_cdnurl=https://npm.taobao.org/dist/phantomjs


================================================
FILE: packages/crd-theme/README.md
================================================
### crd-theme

[create-react-doc](https://github.com/MuYunyun/create-react-doc) 的主题加载包。提供了懒加载, 文件内容解析等能力。

================================================
FILE: packages/crd-theme/component/Loading/index.js
================================================
import styles from './index.less'

const Loading = () => {
  return (
    <div className={styles.loading}>
      正在加载中....
    </div>
  )
}

export default Loading


================================================
FILE: packages/crd-theme/component/Loading/index.less
================================================
.loading {
  text-align: center;
  min-height: 450px;
  padding: 30px 0;
}


================================================
FILE: packages/crd-theme/index.html
================================================
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">

<head>
  <title><%= htmlWebpackPlugin.options.title %></title>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <!-- todo: move them into injectLogic, and https://search.google.com/search-console/welcome -->
  <script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js">
  </script>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.11.0/dist/katex.min.css"
    integrity="sha384-BdGj8xC2eZkQaxoQ8nSLefg4AV4/AwB3Fj+8SUSo7pnKP6Eoy18liIKTPn9oBYNG" crossOrigin="anonymous" />
</head>

<body>
  <div id="root"></div>
</body>

</html>



================================================
FILE: packages/crd-theme/index.js
================================================
import * as React from 'react'
import Markdown from './markdown'
import './index.less'

export default function (props) {
  // routing load component
  if (props.routeData && props.routeData.length > 0) {
    props.routeData.map((item) => {
      item.component = Markdown
      return item
    })
  }

  // support for custom theme.
  const CustomTheme = require('__project_theme__').default

  // use custom theme here.
  return (
    <CustomTheme {...props} />
  )
}


================================================
FILE: packages/crd-theme/index.less
================================================
body,
html {
  position: relative;
}

body,
html,
ul,
li {
  margin: 0;
  padding: 0;
}

a {
  color: #314659;
  text-decoration: none;
  transition: color .3s;

  &:hover {
    color: #1890ff;
  }
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

:global {
  #root {
    height: 100%;
    background: white;
  }
}

================================================
FILE: packages/crd-theme/markdown/Link.js
================================================
import styles from './Link.less'

export default ({ title, href, children }) => {
  let link = href.replace(/(\/|\/show\/|\/show)$/g, '')
  if (
    /^(http(?:|s):)\/\/(jsfiddle.net|runjs.cn|codepen.io|codesandbox.io)/.test(link) &&
    !/^(https|http):\/\/(jsfiddle.net|runjs.cn|codepen.io|codesandbox.io)(?:|\/)$/.test(link)
  ) {
    const regexRunjs = /(https|http):\/\/runjs.cn\/code\/(.*)/gi
    const regexCodepen = /(https|http):\/\/codepen.io\/(.*)\/pen\/(.*)/gi
    const regexCodesandbox = /(https|http):\/\/codesandbox.io\/(s|embed)\/(.*)/gi
    const runjs = regexRunjs.exec(link)
    const codepen = regexCodepen.exec(link)
    const codesandbox = regexCodesandbox.exec(link)
    if (runjs && runjs.length > 2) {
      link = `http://sandbox.runjs.cn/show/${runjs[2]}`
    } else if (codepen && codepen.length === 4) {
      link = `https://codepen.io/${codepen[2]}/embed/${codepen[3]}?height=400`
    } else if (codesandbox && codesandbox.length === 4) {
      link = `https://codesandbox.io/embed/${codesandbox[3]}`
    } else {
      link = `${link}/show/`
    }
    return (
      <iframe frameBorder={0} allowFullScreen="allowfullscreen" className={styles.frame} src={link} title={link} />
    )
  }
  return (
    <a href={href} title={title}>
      {children}
    </a>
  )
}


================================================
FILE: packages/crd-theme/markdown/Link.less
================================================
.frame {
  width: 100%;
  min-height: 400px;
  border: 1px solid #e9e9e9;
  border-radius: 3px;
  background: #fff;
}


================================================
FILE: packages/crd-theme/markdown/codeBlock.js
================================================
/** @jsxRuntime classic /
/* @jsx jsx */
import Highlight, { defaultProps } from 'prism-react-renderer'
import nightOwlLight from 'prism-react-renderer/themes/nightOwlLight'
import { mdx } from '@mdx-js/react'
// import {LiveProvider, LiveEditor, LiveError, LivePreview} from 'react-live'

export default ({ children, className, live, render }) => {
  // const language = className.replace(/language-/, '')

  // if (live) {
  //   return (
  //     <div style={{marginTop: '40px', backgroundColor: 'black'}}>
  //       <LiveProvider
  //         code={children.trim()}
  //         transformCode={code => '/** @jsx mdx */' + code}
  //         scope={{mdx}}
  //       >
  //         <LivePreview />
  //         <LiveEditor />
  //         <LiveError />
  //       </LiveProvider>
  //     </div>
  //   )
  // }

  // if (render) {
  //   return (
  //     <div style={{ marginTop: '40px' }}>
  //       <LiveProvider code={children}>
  //         <LivePreview />
  //       </LiveProvider>
  //     </div>
  //   )
  // }

  return (
    <Highlight {...defaultProps} theme={nightOwlLight} code={children.trim()} language="javascript">
      {({ className, style, tokens, getLineProps, getTokenProps }) => (
        <pre className={className} style={{ ...style, padding: '0 15px' }}>
          {tokens.map((line, i) => (
            <div key={i} {...getLineProps({ line, key: i })}>
              {line.map((token, key) => (
                <span key={key} {...getTokenProps({ token, key })} />
              ))}
            </div>
          ))}
        </pre>
      )}
    </Highlight>
  )
}


================================================
FILE: packages/crd-theme/markdown/index.js
================================================
import * as React from 'react'
import cx from 'classnames'
import { MDXProvider } from '@mdx-js/react'
import { Helmet } from 'react-helmet'
import CodeBlock from './codeBlock'
import Link from './Link'
import styles from './style/index.less'

const { useState, useEffect, useRef } = React

const components = {
  code: CodeBlock,
  link: Link,
}

function Markdown(markdownProps) {
  const { props } = markdownProps
  const { relative, name } = props

  const getRmFirstSlashMarkdownName = () => {
    const relativeMd = relative
    if (!relativeMd) return null
    return relative.slice(1, relative.length - 3)
  }

  const getInitMarkdownCP = () => {
    const markdownName = getRmFirstSlashMarkdownName()
    if (!markdownName) return
    return () => require(`__project_root__/${markdownName}.md`).default
  }

  const [MarkdownCP, setMarkdownCP] = useState(getInitMarkdownCP())
  const markdownWrapperRef = useRef(null)

  const renderMarkdown = () => {
    const markdownName = getRmFirstSlashMarkdownName()
    if (!markdownName) return
    // it must be writen with / & .md in dynamic import
    import(`__project_root__/${markdownName}.md`).then((data) => {
      // data.default is a function, so we should write () => data.default in setState here.
      setMarkdownCP(() => (data.default || data))
    })
  }

  useEffect(() => {
    renderMarkdown()
  }, [getRmFirstSlashMarkdownName()])

  const getName = () => {
    return name ? name.replace('.md', '') : ''
  }

  return (
    <>
      <Helmet>
        <title>{getName()}</title>
        <meta name={getName()} content={getName()} />
      </Helmet>
      {
        MarkdownCP
          ? <div
            className={cx('markdown', styles.markdown, styles.markdownwrapper)}
            ref={markdownWrapperRef}
          >
            <MDXProvider
              components={components}
            >
              <MarkdownCP />
            </MDXProvider>
          </div>
          : null
      }
    </>
  )
}

export default Markdown


================================================
FILE: packages/crd-theme/markdown/style/css.less
================================================
.language-css, .language-scss, .language-stylus, .language-less {
  .keyword, .selector-tag, .subst {
    color: #d73a49;
    font-weight: normal;
  }
  .tag, .name, .attribute {
    color: #005cc5;
    font-weight: normal;
  }
  .selector-pseudo {
    color: #6f42c1;
  }
  .selector-attr {
    color: #008c41;
  }
  .selector-class {
    color: #6f42c1;
  }
  .selector-id {
    font-weight: normal;
  }
}

================================================
FILE: packages/crd-theme/markdown/style/default.less
================================================
code {
  word-wrap: normal;
}

pre {
  max-height: 35em;
  position: relative;
  overflow: auto;
  background-color: rgb(251, 251, 251);
  border-radius: 3px;
}

pre code {
  background: none;
  font-size: 1em;
  overflow-wrap: normal;
  white-space: inherit;
}

ul,
ol {
  padding-left: 2em;
}

dl {
  padding: 0;

  dt {
    padding: 0;
    margin-top: 16px;
    font-size: 14px;
    font-style: italic;
    font-weight: 600;
  }
}

li+li {
  margin-top: 3px;
}

a {
  color: #0366d6;
}

p {
  margin-bottom: 16px;
}

blockquote {
  margin: 0;
  padding: 0 1em;
  margin: 16px 0 16px 0;
  color: #6a737d;
  border-left: 0.25em solid #dfe2e5;

  &>:first-child {
    margin-top: 0;
  }

  &>:last-child {
    margin-bottom: 0;
  }
}

hr {
  height: 0.25em;
  padding: 0;
  margin: 24px 0;
  background-color: #e1e4e8;
  border: 0;
}

h1 tt,
h1 code,
h2 tt,
h2 code,
h3 tt,
h3 code,
h4 tt,
h4 code,
h5 tt,
h5 code,
h6 tt,
h6 code {
  font-size: inherit
}

h1 {
  padding-bottom: 0.3em;
  font-size: 2em;
  border-bottom: 1px solid #eaecef
}

h2 {
  padding-bottom: 0.3em;
  font-size: 1.5em;
  border-bottom: 1px solid #eaecef
}

h3 {
  font-size: 1.25em
}

h4 {
  font-size: 1em
}

h5 {
  font-size: 0.875em
}

h6 {
  font-size: 0.85em;
  color: #6a737d
}

table {
  border-collapse: collapse;
  border-spacing: 0;
  width: 100%;

  &+table {
    margin-top: 16px;
  }
}

table th {
  font-weight: 600;
  white-space: nowrap;
  color: #5c6b77;
  background: rgba(0, 0, 0, .02);
}

table th,
table td {
  padding: 8px 13px;
  border: 1px solid #dfe2e5;
}

table tr {
  background-color: #fff;
  border-top: 1px solid #c6cbd1;
}

table tr:nth-child(2n) {
  background-color: #f6f8fa;
}

table img {
  background-color: transparent;
}

img {
  max-width: 100%;
  box-sizing: content-box;
  background-color: #fff;
  vertical-align: middle;
}

img[align=right] {
  padding-left: 20px;
}

img[align=left] {
  padding-right: 20px;
}

.comment,
.quote {
  color: #998;
}

.keyword,
.selector-tag,
.subst {
  color: #333;
  font-weight: bold;
}

.number,
.literal,
.variable,
.template-variable,
.tag .attr {
  color: #008080;
}

.string,
.doctag {
  color: #d14;
}

.title,
.section,
.selector-id {
  color: #900;
  font-weight: bold;
}

.subst {
  font-weight: normal;
}

.type,
.class .title {
  color: #458;
  font-weight: bold;
}

.tag,
.name,
.attribute {
  color: #000098;
  font-weight: normal;
}

.regexp,
.link {
  color: #009926;
}

.symbol,
.bullet {
  color: #990073;
}

.built_in,
.builtin-name {
  color: #0086b3;
}

.meta {
  color: #999;
  font-weight: bold;
}

.deletion {
  background: #fdd;
}

.addition {
  background: #dfd;
}

.emphasis {
  font-style: italic;
}

.strong {
  font-weight: bold;
}

================================================
FILE: packages/crd-theme/markdown/style/diff.less
================================================
.language-diff {

}

================================================
FILE: packages/crd-theme/markdown/style/index.less
================================================
.markdown {
  padding: 0 40px 20px 40px;
  margin: 0 auto;
  font-size: 14px;
  line-height: 1.5;
  word-wrap: break-word;
  color: #314659;
  & >*:first-child {
    margin-top: 0 !important
  }

  & >*:last-child {
    margin-bottom: 0 !important
  }
  :global {
    @import "./default.less";
    @import "./xml.less";
    @import "./swift.less";
    @import "./diff.less";
    @import "./css.less";
    @import "./javascript.less";
  }
}

.pageTitle {
  font-size: 30px;
  line-height: 38px;
  color: #0d1a26;
  font-weight: 500;
  margin-bottom: 20px;
  margin-top: 8px;
  padding-left: 20px;
}


================================================
FILE: packages/crd-theme/markdown/style/javascript.less
================================================
.language-jsx, .language-js,.language-javascript{
  .keyword, .selector-tag {
    color: #b111bf;
    font-weight: 600;
  }
  .subst {
    color: #0000ca;
  }
  .title, .section {
    color: #4e00dc;
    font-weight: normal;
  }
  .string, .doctag {
    color: #d73a49;
  }
  .attr {
    color: #0000ca;
  }
  .class .keyword{
    color: #008cd4;
  }
  .function {
    color: #0000ca;
    .params {
      color: #008cd4;
    }
  }
}
.language-json {
  color: #444;
  .attr {
    color: #0000ca;
  }
}

================================================
FILE: packages/crd-theme/markdown/style/swift.less
================================================
.language-swift {
  .keyword, .selector-tag, .subst {
    color: #C600AA;
    font-weight: normal;
  }
  .meta {
    color: #C526D0;
    font-weight: normal;
  }
  .type, .class .title {
    color: #7C10B2;
    font-weight: normal;
  }
}

================================================
FILE: packages/crd-theme/markdown/style/xml.less
================================================
.language-html, .xml{

}


================================================
FILE: packages/crd-theme/package.json
================================================
{
  "name": "crd-theme",
  "version": "1.10.3",
  "description": "Default Theme with Create React Doc",
  "main": "index.js",
  "dependencies": {
    "@mdx-js/react": "^1.6.22",
    "classnames": "^2.2.6",
    "diana": "^1.0.2",
    "less": "^3.0.2",
    "prism-react-renderer": "^1.2.1",
    "react": "^18.0.0",
    "react-dom": "^18.0.0",
    "react-helmet": "^6.1.0",
    "react-hot-loader": "^4.1.1",
    "react-markdown": "^3.3.0"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/MuYunyun/create-react-doc",
    "directory": "packages/theme"
  },
  "keywords": [],
  "publishConfig": {
    "access": "public"
  },
  "author": "muyunyun",
  "license": "MIT",
  "gitHead": "ffc5e4cbc94a7356da558c2dbf46e2f39bb8b199",
  "devDependencies": {
    "@types/react": "^17.0.43",
    "@types/react-dom": "^17.0.14"
  }
}


================================================
FILE: packages/crd-utils/.npmrc
================================================
# .npmrc

registry=https://registry.npmjs.org/

# https://github.com/sass/node-sass#binary-configuration-parameters
sass_binary_site=https://npm.taobao.org/mirrors/node-sass/

# https://github.com/Medium/phantomjs#deciding-where-to-get-phantomjs
# phantomjs_cdnurl=http://cnpmjs.org/downloads
phantomjs_cdnurl=https://npm.taobao.org/dist/phantomjs


================================================
FILE: packages/crd-utils/README.md
================================================
### crd-utils

[create-react-doc](https://github.com/MuYunyun/create-react-doc) 的工具包。

================================================
FILE: packages/crd-utils/index.js
================================================
const fs = require('fs')
const yaml = require('js-yaml')
const { resolveApp, resolveTool } = require('./path')

/* avoid reference loop, so use resolveApp('config.yml') instead of refrence from paths. */
const docsConfig = resolveApp('config.yml')

/**
 * get docs config, see https://github.com/nodeca/js-yaml/blob/2d1fbed8f3a76ff93cccb9a8a418b4c4a482d3d9/lib/js-yaml/loader.js#L1590-L1592
 */
const getDocsConfig = () => {
  if (!fs.existsSync(docsConfig)) {
    return null
  }
  return yaml.safeLoad(fs.readFileSync(docsConfig))
}

/**
 * replace file content for Front-matter
 * path: file path
 * source?: source content
 * target: target content
 */
const replaceForFrontMatter = ({
  path,
  source,
  target
}) => {
  fs.readFile(path, (err, data) => {
    if (err) {
      console.log(`❎ readFileContent error in ${path}`)
      return
    }
    console.log(`✅ readFileContent success in ${path}`)
    const replaceResult = source
      ? data.toString().replace(source, target)
      : `${target}\n${data.toString()}`
    fs.writeFile(path, replaceResult, (err) => {
      if (err) {
        console.log(`❎ writeFileContent error in ${path}`)
        return
      }
    })
    console.log(`✅ writeFileContent success in ${path}`)
  })
}

// generate a random string, length of it is n.
const generateRandomId = (n) => {
  const str = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
  let res = ""
  for (let i = 0; i < n; i++) {
    const id = Math.ceil(Math.random() * 35)
    res += str[id]
  }
  return res
}

module.exports = {
  resolveApp,
  resolveTool,
  getDocsConfig,
  replaceForFrontMatter,
  generateRandomId,
  // common paths
  docsGitIgnore: resolveApp('.gitignore'),
  docsBase: resolveApp(''),
  docsConfig,
  docsReadme: resolveApp('README.md'),
  docsBuildDist: resolveApp('.crd-dist'),
  cacheDirPath: resolveApp('.cache'),
  searchFilePath: resolveApp('.cache/search.js'),
  templatePath: resolveApp('node_modules/crd-templates'),
  defaultHTMLPath: resolveApp('node_modules/crd-theme/ind
Download .txt
gitextract_uke23iaa/

├── .editorconfig
├── .eslintrc.js
├── .github/
│   ├── FUNDING.yml
│   ├── config.yml
│   ├── dependabot.yml
│   ├── stale.yml
│   └── workflows/
│       ├── gh-pages.yml
│       ├── greetings.yml
│       └── traffic2badge.yml
├── .gitignore
├── .npmignore
├── .npmrc
├── .yarnrc
├── CONTRIBUTING.md
├── README-en.md
├── README.md
├── components/
│   ├── Button/
│   │   ├── index.jsx
│   │   └── index.less
│   └── index.jsx
├── config.yml
├── docs/
│   ├── Front-matter.md
│   ├── 主题/
│   │   ├── 自定义主题.md
│   │   └── 默认主题.md
│   ├── 书写组件.md
│   ├── 其它工具.md
│   ├── 快速上手.md
│   ├── 数学公式.md
│   ├── 更新日志.md
│   ├── 测试/
│   │   ├── 测试标签.md
│   │   └── 测试路由.md
│   ├── 站点发布.md
│   └── 高阶用法.md
├── gh-pages.js
├── injectLogic/
│   └── index.js
├── lerna.json
├── package.json
├── packages/
│   ├── crd-client-utils/
│   │   ├── .npmrc
│   │   ├── README.md
│   │   ├── index.js
│   │   └── package.json
│   ├── crd-generator-sitemap/
│   │   ├── .npmrc
│   │   ├── README.md
│   │   ├── generate.js
│   │   ├── index.js
│   │   └── package.json
│   ├── crd-scripts/
│   │   ├── .npmrc
│   │   ├── README.md
│   │   ├── index.js
│   │   ├── package.json
│   │   └── src/
│   │       ├── build.js
│   │       ├── commands/
│   │       │   ├── initProject.js
│   │       │   └── initTheme.js
│   │       ├── conf/
│   │       │   ├── getDirTree.js
│   │       │   ├── getPrerenderRoutes.js
│   │       │   ├── node-directory-tree.js
│   │       │   ├── path.js
│   │       │   ├── rawTreeReplaceLoader.js
│   │       │   ├── webpack.config.dev.js
│   │       │   ├── webpack.config.js
│   │       │   ├── webpack.config.prod.js
│   │       │   └── webpack.config.server.js
│   │       ├── deploy.js
│   │       ├── generate.js
│   │       ├── server.js
│   │       ├── utils/
│   │       │   ├── index.js
│   │       │   └── initCache.js
│   │       └── web/
│   │           ├── Router.js
│   │           ├── crd.json
│   │           └── index.js
│   ├── crd-seed/
│   │   ├── .npmrc
│   │   ├── README.md
│   │   ├── component/
│   │   │   ├── Affix/
│   │   │   │   ├── affix.js
│   │   │   │   ├── index.js
│   │   │   │   └── utils/
│   │   │   │       └── index.js
│   │   │   ├── Footer/
│   │   │   │   ├── index.js
│   │   │   │   └── index.less
│   │   │   ├── Header/
│   │   │   │   ├── index.js
│   │   │   │   └── index.less
│   │   │   ├── Icon/
│   │   │   │   ├── Icon.js
│   │   │   │   ├── iconsInfo.js
│   │   │   │   ├── index.js
│   │   │   │   ├── loadSprite.js
│   │   │   │   └── style/
│   │   │   │       └── index.less
│   │   │   ├── Menu/
│   │   │   │   ├── Menu.js
│   │   │   │   ├── MenuItem.js
│   │   │   │   ├── SubMenu.js
│   │   │   │   ├── context.js
│   │   │   │   ├── index.js
│   │   │   │   ├── style/
│   │   │   │   │   ├── index.less
│   │   │   │   │   └── theme.less
│   │   │   │   ├── transition.js
│   │   │   │   └── util.js
│   │   │   ├── NoMatch/
│   │   │   │   ├── index.js
│   │   │   │   └── index.less
│   │   │   ├── Search/
│   │   │   │   ├── README.md
│   │   │   │   ├── index.js
│   │   │   │   └── index.less
│   │   │   └── Tags/
│   │   │       ├── index.js
│   │   │       └── index.less
│   │   ├── index.js
│   │   ├── index.less
│   │   ├── language/
│   │   │   └── index.js
│   │   ├── layout/
│   │   │   ├── index.js
│   │   │   ├── index.less
│   │   │   └── utils.js
│   │   ├── package.json
│   │   ├── style/
│   │   │   ├── base.less
│   │   │   └── mobile.less
│   │   └── utils/
│   │       └── index.js
│   ├── crd-templates/
│   │   ├── .npmrc
│   │   ├── README.md
│   │   ├── default/
│   │   │   ├── .github/
│   │   │   │   └── workflows/
│   │   │   │       └── gh-pages.yml
│   │   │   ├── .npmrc
│   │   │   ├── Introduction/
│   │   │   │   └── hello_world.md
│   │   │   ├── README.md
│   │   │   ├── _.gitignore
│   │   │   ├── _config.yml
│   │   │   └── _package.json
│   │   ├── package.json
│   │   └── theme/
│   │       └── default/
│   │           ├── Introduction/
│   │           │   └── hello_world.md
│   │           ├── README.md
│   │           ├── _.gitignore
│   │           ├── _.npmrc
│   │           ├── _config.yml
│   │           ├── _index.js
│   │           ├── _index.less
│   │           └── _package.json
│   ├── crd-theme/
│   │   ├── .npmrc
│   │   ├── README.md
│   │   ├── component/
│   │   │   └── Loading/
│   │   │       ├── index.js
│   │   │       └── index.less
│   │   ├── index.html
│   │   ├── index.js
│   │   ├── index.less
│   │   ├── markdown/
│   │   │   ├── Link.js
│   │   │   ├── Link.less
│   │   │   ├── codeBlock.js
│   │   │   ├── index.js
│   │   │   └── style/
│   │   │       ├── css.less
│   │   │       ├── default.less
│   │   │       ├── diff.less
│   │   │       ├── index.less
│   │   │       ├── javascript.less
│   │   │       ├── swift.less
│   │   │       └── xml.less
│   │   └── package.json
│   ├── crd-utils/
│   │   ├── .npmrc
│   │   ├── README.md
│   │   ├── index.js
│   │   ├── package.json
│   │   └── path.js
│   ├── create-react-doc/
│   │   ├── .npmignore
│   │   ├── .npmrc
│   │   ├── .yarnrc
│   │   ├── README.md
│   │   ├── index.js
│   │   └── package.json
│   └── leetcode-cli/
│       ├── .npmrc
│       ├── README.md
│       ├── leetcode-table.md
│       ├── package.json
│       ├── problems.json
│       └── src/
│           ├── cli.js
│           ├── download.js
│           ├── leetcode-table.md
│           ├── leetcode.js
│           ├── logout.js
│           ├── problems.json
│           └── utils.js
└── utils/
    ├── uppackage-dev.sh
    └── uppackage.sh
Download .txt
SYMBOL INDEX (29 symbols across 15 files)

FILE: packages/crd-scripts/src/conf/getPrerenderRoutes.js
  function recursiveDirTree (line 11) | function recursiveDirTree(data) {
  function recursive (line 19) | function recursive(

FILE: packages/crd-scripts/src/conf/node-directory-tree.js
  constant PATH (line 3) | const PATH = require('path')
  constant YAML (line 4) | const YAML = require('yamljs')
  function safeReadDirSync (line 17) | function safeReadDirSync(path) {
  function directoryTree (line 64) | function directoryTree({

FILE: packages/crd-scripts/src/conf/path.js
  function getCrdConf (line 12) | function getCrdConf() {
  function getConfigFilePath (line 22) | function getConfigFilePath(fileName, type) {
  function getExcludeFoldersRegExp (line 103) | function getExcludeFoldersRegExp() {

FILE: packages/crd-scripts/src/conf/rawTreeReplaceLoader.js
  constant PATH (line 3) | const PATH = require('path')
  function getAllWatchPath (line 6) | function getAllWatchPath(arr, pathArr = []) {
  function replacePath (line 22) | function replacePath(dirs, path) {
  function getRelativePath (line 34) | function getRelativePath(arr, relativePath, dirs) {

FILE: packages/crd-scripts/src/server.js
  function clearConsole (line 12) | function clearConsole() {

FILE: packages/crd-scripts/src/utils/index.js
  function paddingTwoDigits (line 31) | function paddingTwoDigits(digit) {

FILE: packages/crd-scripts/src/utils/initCache.js
  function dfsMap (line 21) | function dfsMap(data) {

FILE: packages/crd-scripts/src/web/Router.js
  function routeData (line 8) | function routeData(data, arrayRoute = [], routePath = '/', article) {
  function menuSourceFormat (line 25) | function menuSourceFormat(data, routePath, article) {
  function RouterRoot (line 69) | function RouterRoot() {

FILE: packages/crd-seed/component/Icon/Icon.js
  function Icon (line 13) | function Icon(props) {

FILE: packages/crd-seed/component/Menu/MenuItem.js
  function MenuItem (line 7) | function MenuItem({

FILE: packages/crd-seed/component/Menu/SubMenu.js
  function useCurrent (line 9) | function useCurrent(
  function SubMenu (line 23) | function SubMenu({

FILE: packages/crd-seed/component/Menu/transition.js
  constant ANIMATION_DURATION (line 4) | const ANIMATION_DURATION = 200
  function Transition (line 6) | function Transition({

FILE: packages/crd-seed/layout/index.js
  function BasicLayout (line 22) | function BasicLayout({

FILE: packages/crd-seed/layout/utils.js
  function getOpenSubMenuKeys (line 10) | function getOpenSubMenuKeys({
  function getOpenSubMenuKeysForAbbrLink (line 30) | function getOpenSubMenuKeysForAbbrLink(source, pathname, result) {

FILE: packages/crd-theme/markdown/index.js
  function Markdown (line 16) | function Markdown(markdownProps) {
Condensed preview — 171 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (333K chars).
[
  {
    "path": ".editorconfig",
    "chars": 292,
    "preview": "# http://editorconfig.org\nroot = true\n\n[*]\nindent_style = space\nindent_size = 2\nend_of_line = lf\ncharset = utf-8\ntrim_tr"
  },
  {
    "path": ".eslintrc.js",
    "chars": 2332,
    "preview": "const eslintrc = {\n  \"parser\": \"babel-eslint\",\n  \"extends\": \"airbnb\",\n  \"env\": {\n    \"browser\": true,\n    \"node\": true,\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "chars": 743,
    "preview": "# These are supported funding model platforms\n\ngithub: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [u"
  },
  {
    "path": ".github/config.yml",
    "chars": 32,
    "preview": "todo:\n  keyword: \"@makeAnIssue\"\n"
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 369,
    "preview": "# see https://github.com/yi-Xu-0100/traffic-to-badge/blob/main/.github/dependabot.yml\nversion: 2\nupdates:\n  # Enable ver"
  },
  {
    "path": ".github/stale.yml",
    "chars": 1985,
    "preview": "# Configuration for probot-stale - https://github.com/probot/stale\n\n# Number of days of inactivity before an Issue or Pu"
  },
  {
    "path": ".github/workflows/gh-pages.yml",
    "chars": 1064,
    "preview": "name: GitHub Pages\n\non:\n  push:\n    branches:\n      - main\n  pull_request:\n\njobs:\n  deploy:\n    runs-on: ubuntu-22.04\n  "
  },
  {
    "path": ".github/workflows/greetings.yml",
    "chars": 322,
    "preview": "name: Greetings\n\non: [pull_request, issues]\n\njobs:\n  greeting:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions"
  },
  {
    "path": ".github/workflows/traffic2badge.yml",
    "chars": 1890,
    "preview": "name: traffic2badge\non:\n  push:\n    branches:\n      - main\n  schedule:\n    - cron: '1 0 * * *' #UTC\n\njobs:\n  run:\n    na"
  },
  {
    "path": ".gitignore",
    "chars": 137,
    "preview": "node_modules\n.create-react-doc-dist\npackage-lock.json\n.cache\n.DS_Store\n.crd-dist/\n\n*.bak\n*.tem\n*.log\n*.temp\n#.swp\n*.*~\n~"
  },
  {
    "path": ".npmignore",
    "chars": 90,
    "preview": ".cache\n.gitignore\n.editorconfig\n.create-react-doc-dist\nnode_modules\npackage-lock.json\ndist"
  },
  {
    "path": ".npmrc",
    "chars": 47,
    "preview": "# .npmrc\n\nregistry=https://registry.npmjs.org/\n"
  },
  {
    "path": ".yarnrc",
    "chars": 49,
    "preview": "# .yarnrc\nregistry \"https://registry.npmjs.org/\"\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 721,
    "preview": "# HOW TO CONTRIBUTE\n\n1. Welcome your pr! Before pr, talk about situations in the [issue](https://github.com/MuYunyun/cre"
  },
  {
    "path": "README-en.md",
    "chars": 5908,
    "preview": "                                                     _.-\"\\\n                                                _.-\"      \\\n "
  },
  {
    "path": "README.md",
    "chars": 4289,
    "preview": "<!--\nabbrlink: s31w9gd1\n-->\n                                                     _.-\"\\\n                                 "
  },
  {
    "path": "components/Button/index.jsx",
    "chars": 158,
    "preview": "import styles from './index.less'\n\nconst Button = ({\n  children,\n}) => {\n  return <button className={styles.btn}>{childr"
  },
  {
    "path": "components/Button/index.less",
    "chars": 359,
    "preview": ".btn {\n  color: #fff;\n  background: #1890ff;\n  border-color: #1890ff;\n  text-shadow: 0 -1px 0 rgb(0 0 0 / 12%);\n  box-sh"
  },
  {
    "path": "components/index.jsx",
    "chars": 62,
    "preview": "import Button from './Button/index.jsx'\n\nexport {\n  Button,\n}\n"
  },
  {
    "path": "config.yml",
    "chars": 1068,
    "preview": "# create-react-doc configuration.\n\n# Site\ntitle: Create React Doc\n\n# Menu dir\n## you can also set detailed dir, such as "
  },
  {
    "path": "docs/Front-matter.md",
    "chars": 830,
    "preview": "<!--\nabbrlink: 49g6b239\n-->\n\n## Front-matter\n\nFront-matter 是文件最上方包裹在 `<!--` 与 `-->` 之间的区域,用于指定个别文件的变量,举例来说:\n\n```md\n<!--\n"
  },
  {
    "path": "docs/主题/自定义主题.md",
    "chars": 2248,
    "preview": "<!--\ntitle: 自定义主题\nabbrlink: 9f41fc98\ndate: 2019-05-12 13:23:44\ntags: ['主题', '自定义标签1']\n-->\n\n## 使用自定义主题\n\n切换主题非常简单, 只需要将根目录"
  },
  {
    "path": "docs/主题/默认主题.md",
    "chars": 3025,
    "preview": "<!--\nabbrlink: 85li8wdd\n-->\n\n## 默认主题\n\ncreate-react-doc 的默认主题为 [crd-seed](https://github.com/MuYunyun/create-react-doc/tr"
  },
  {
    "path": "docs/书写组件.md",
    "chars": 401,
    "preview": "<!--\nabbrlink: lx1euo1b\ntags: ['组件']\n-->\n\nimport { Button } from '../components/index.jsx'\n\n## 书写组件\n\n[create-react-doc]("
  },
  {
    "path": "docs/其它工具.md",
    "chars": 1501,
    "preview": "<!--\nabbrlink: 292h2c5k\n-->\n\n## 其它工具\n\n### crd-leetcode-cli\n\n#### 背景\n\n当新增 LeetCode 题解时需要[手动更新表格](https://github.com/MuYun"
  },
  {
    "path": "docs/快速上手.md",
    "chars": 681,
    "preview": "<!--\ntitle: 快速上手\nabbrlink: 290a4219\ndate: 2019-05-12 13:23:44\n-->\n\n## 快速上手\n\n**create-react-doc** 非常容易上手。开发者不需要额外安装或配置 we"
  },
  {
    "path": "docs/数学公式.md",
    "chars": 414,
    "preview": "<!--\nabbrlink: slkczvi1\n-->\n\n## 数学公式\n\n支持在 `$$` 与 `$$` 之间书写 latex 数学公式即能显示在网页上。\n\n```js\n$$\nz = \\frac{x}{y}\n$$\n```\n\n被转化为:\n\n"
  },
  {
    "path": "docs/更新日志.md",
    "chars": 6032,
    "preview": "<!--\nabbrlink: 179nqpxt\n-->\n\n# CHANGELOG\n\n`create-react-doc` 严格遵循 [Semantic Versioning 2.0.0](http://semver.org/lang/zh-"
  },
  {
    "path": "docs/测试/测试标签.md",
    "chars": 80,
    "preview": "<!--\nabbrlink: a80c431d\ntags: ['自定义标签1', '自定义标签2', '自定义标签1']\n-->\n\n该页面用来测试自定义标签。\n"
  },
  {
    "path": "docs/测试/测试路由.md",
    "chars": 56,
    "preview": "<!--\ntags: ['自定义标签1']\n-->\n\n* 该页面用来测试未使用 abbrlink 的中文路径。\n"
  },
  {
    "path": "docs/站点发布.md",
    "chars": 958,
    "preview": "<!--\nabbrlink: ude9296y\n-->\n\n## 站点发布\n\n在 [快速上手](http://muyunyun.cn/create-react-doc/%E5%BF%AB%E9%80%9F%E4%B8%8A%E6%89%8B)"
  },
  {
    "path": "docs/高阶用法.md",
    "chars": 575,
    "preview": "<!--\nabbrlink: 9v9ug9h8\n-->\n\n## 高阶用法\n\n与 git 文件结构类似, 如果在展示的文件夹中有私有文件不方便展示在文档站点, 可以在 `.gitignore` 文件中设置过滤文件, 这样它们就不会展示在文档站"
  },
  {
    "path": "gh-pages.js",
    "chars": 287,
    "preview": "const ghPages = require('gh-pages')\n\nghPages.publish(\n  '.crd-dist',\n  {\n    branch: 'gh-pages',\n    repo: 'https://gith"
  },
  {
    "path": "injectLogic/index.js",
    "chars": 502,
    "preview": "/* eslint-disable no-empty */\n// seo: perf inject logic only once\nconst inject = () => {\n  // SEO for Google through htt"
  },
  {
    "path": "lerna.json",
    "chars": 345,
    "preview": "{\n  \"version\": \"1.10.3\",\n  \"command\": {\n    \"bootstrap\": {\n      \"npmClientArgs\": [\n        \"--no-package-lock\"\n      ]\n"
  },
  {
    "path": "package.json",
    "chars": 1367,
    "preview": "{\n  \"name\": \"create-react-doc\",\n  \"private\": true,\n  \"workspaces\": [\n    \"packages/*\"\n  ],\n  \"description\": \"Fast static"
  },
  {
    "path": "packages/crd-client-utils/.npmrc",
    "chars": 348,
    "preview": "# .npmrc\n\nregistry=https://registry.npmjs.org/\n\n# https://github.com/sass/node-sass#binary-configuration-parameters\nsass"
  },
  {
    "path": "packages/crd-client-utils/README.md",
    "chars": 92,
    "preview": "### crd-client-utils\n\n[create-react-doc](https://github.com/MuYunyun/create-react-doc) 的工具包。"
  },
  {
    "path": "packages/crd-client-utils/index.js",
    "chars": 609,
    "preview": "import { useLayoutEffect, useEffect } from 'react'\n\nconst ifDev = env === 'dev'\nconst ifProd = env === 'prod'\nconst ifPr"
  },
  {
    "path": "packages/crd-client-utils/package.json",
    "chars": 428,
    "preview": "{\n  \"name\": \"crd-client-utils\",\n  \"version\": \"1.8.2\",\n  \"description\": \"Utils with create react doc\",\n  \"main\": \"index.j"
  },
  {
    "path": "packages/crd-generator-sitemap/.npmrc",
    "chars": 348,
    "preview": "# .npmrc\n\nregistry=https://registry.npmjs.org/\n\n# https://github.com/sass/node-sass#binary-configuration-parameters\nsass"
  },
  {
    "path": "packages/crd-generator-sitemap/README.md",
    "chars": 109,
    "preview": "### crd-generator-sitemap\n\n[create-react-doc](https://github.com/MuYunyun/create-react-doc) SEO 插件, 用于生成站点地图。"
  },
  {
    "path": "packages/crd-generator-sitemap/generate.js",
    "chars": 966,
    "preview": "const { getDocsConfig } = require('crd-utils')\n\n// template for google SEO\n// <url>\n//   <loc>muyunyun.cn/blog/xxx</loc>"
  },
  {
    "path": "packages/crd-generator-sitemap/index.js",
    "chars": 91,
    "preview": "const { generateSiteMap } = require('./generate')\n\nmodule.exports = {\n  generateSiteMap,\n}\n"
  },
  {
    "path": "packages/crd-generator-sitemap/package.json",
    "chars": 460,
    "preview": "{\n  \"name\": \"crd-generator-sitemap\",\n  \"version\": \"1.3.4\",\n  \"description\": \"generator sitemap for create-react-doc\",\n  "
  },
  {
    "path": "packages/crd-scripts/.npmrc",
    "chars": 348,
    "preview": "# .npmrc\n\nregistry=https://registry.npmjs.org/\n\n# https://github.com/sass/node-sass#binary-configuration-parameters\nsass"
  },
  {
    "path": "packages/crd-scripts/README.md",
    "chars": 728,
    "preview": "                                                     _.-\"\\\n                                                _.-\"      \\\n "
  },
  {
    "path": "packages/crd-scripts/index.js",
    "chars": 477,
    "preview": "const initProject = require('./src/commands/initProject')\nconst initTheme = require('./src/commands/initTheme')\nconst in"
  },
  {
    "path": "packages/crd-scripts/package.json",
    "chars": 1920,
    "preview": "{\n  \"name\": \"crd-scripts\",\n  \"version\": \"1.10.3\",\n  \"description\": \"Scripts using with Create React Doc\",\n  \"main\": \"ind"
  },
  {
    "path": "packages/crd-scripts/src/build.js",
    "chars": 906,
    "preview": "const webpack = require('webpack')\nconst fs = require('fs')\nconst { docsConfig } = require('crd-utils')\nconst conf = req"
  },
  {
    "path": "packages/crd-scripts/src/commands/initProject.js",
    "chars": 2075,
    "preview": "const path = require('path')\nconst { execSync } = require('child_process')\nconst fs = require('fs-extra')\nconst { templa"
  },
  {
    "path": "packages/crd-scripts/src/commands/initTheme.js",
    "chars": 1526,
    "preview": "/**\n * This file is to init theme quickly.\n */\nconst path = require('path')\nconst { execSync } = require('child_process'"
  },
  {
    "path": "packages/crd-scripts/src/conf/getDirTree.js",
    "chars": 582,
    "preview": "const { directoryTree } = require('./node-directory-tree')\n\nconst getDirTree = (cmd) => {\n  const dir = cmd.markdownPath"
  },
  {
    "path": "packages/crd-scripts/src/conf/getPrerenderRoutes.js",
    "chars": 1168,
    "preview": "// eg: ['docs/quick_start.md', 'a']\n// output: ['/quick_start', '/a/b', '/a/b/c']\nconst getPrerenderRoutes = (dirTree) ="
  },
  {
    "path": "packages/crd-scripts/src/conf/node-directory-tree.js",
    "chars": 6188,
    "preview": "/* eslint-disable no-undef */\nconst fs = require('fs')\nconst PATH = require('path')\nconst YAML = require('yamljs')\nconst"
  },
  {
    "path": "packages/crd-scripts/src/conf/path.js",
    "chars": 4119,
    "preview": "const path = require('path')\nconst fs = require('fs')\nconst { execSync } = require('child_process')\nconst { resolveApp, "
  },
  {
    "path": "packages/crd-scripts/src/conf/rawTreeReplaceLoader.js",
    "chars": 2626,
    "preview": "// A variation of https://github.com/react-doc/node-directory-tree-md/blob/master/lib/directory-tree-md.js\nconst { direc"
  },
  {
    "path": "packages/crd-scripts/src/conf/webpack.config.dev.js",
    "chars": 4623,
    "preview": "const autoprefixer = require('autoprefixer')\nconst webpack = require('webpack')\nconst path = require('path')\nconst upath"
  },
  {
    "path": "packages/crd-scripts/src/conf/webpack.config.js",
    "chars": 4309,
    "preview": "/* eslint-disable global-require */\n/* eslint-disable import/no-dynamic-require */\nconst webpack = require('webpack')\nco"
  },
  {
    "path": "packages/crd-scripts/src/conf/webpack.config.prod.js",
    "chars": 8041,
    "preview": "const autoprefixer = require('autoprefixer')\nconst path = require('path')\nconst upath = require('upath')\nconst webpack ="
  },
  {
    "path": "packages/crd-scripts/src/conf/webpack.config.server.js",
    "chars": 1018,
    "preview": "const protocol = process.env.HTTPS === 'true' ? 'https' : 'http'\n\nmodule.exports = (cmd, webpackConf) => {\n  return {\n  "
  },
  {
    "path": "packages/crd-scripts/src/deploy.js",
    "chars": 971,
    "preview": "const ghpages = require('gh-pages')\nconst loading = require('loading-cli')\n\nconst log = console.log; // eslint-disable-l"
  },
  {
    "path": "packages/crd-scripts/src/generate.js",
    "chars": 579,
    "preview": "const fs = require('fs')\nconst { docsConfig } = require('crd-utils')\nconst { directoryTree } = require('./conf/node-dire"
  },
  {
    "path": "packages/crd-scripts/src/server.js",
    "chars": 1888,
    "preview": "const webpack = require('webpack')\nconst WebpackDevServer = require('webpack-dev-server')\nconst openBrowsers = require('"
  },
  {
    "path": "packages/crd-scripts/src/utils/index.js",
    "chars": 1388,
    "preview": "const fs = require('fs')\nconst { docsGitIgnore, searchFilePath } = require('crd-utils')\n\n/**\n * judege cur file if in gi"
  },
  {
    "path": "packages/crd-scripts/src/utils/initCache.js",
    "chars": 2080,
    "preview": "const write = require('write')\nconst path = require('path')\nconst { cacheDirPath, getDocsConfig } = require('crd-utils')"
  },
  {
    "path": "packages/crd-scripts/src/web/Router.js",
    "chars": 2165,
    "preview": "import { BrowserRouter, Route, Routes } from 'react-router-dom'\nimport theme from 'crd-theme'\nimport menuSource from './"
  },
  {
    "path": "packages/crd-scripts/src/web/crd.json",
    "chars": 3,
    "preview": "[]\n"
  },
  {
    "path": "packages/crd-scripts/src/web/index.js",
    "chars": 958,
    "preview": "import { hydrate } from 'react-dom'\nimport { renderToString } from 'react-dom/server';\n// import { hydrateRoot } from 'r"
  },
  {
    "path": "packages/crd-seed/.npmrc",
    "chars": 348,
    "preview": "# .npmrc\n\nregistry=https://registry.npmjs.org/\n\n# https://github.com/sass/node-sass#binary-configuration-parameters\nsass"
  },
  {
    "path": "packages/crd-seed/README.md",
    "chars": 587,
    "preview": "## 主题\n\ncreate-react-doc 提供了官方默认主题 [crd-seed](https://github.com/MuYunyun/create-react-doc/tree/main/packages/crd-seed)。该"
  },
  {
    "path": "packages/crd-seed/component/Affix/affix.js",
    "chars": 3763,
    "preview": "import { useState, useEffect, useRef } from 'react'\nimport { throttle } from './utils'\n\nconst Affix = ({\n  offsetTop,\n  "
  },
  {
    "path": "packages/crd-seed/component/Affix/index.js",
    "chars": 50,
    "preview": "import Affix from './affix'\n\nexport default Affix\n"
  },
  {
    "path": "packages/crd-seed/component/Affix/utils/index.js",
    "chars": 597,
    "preview": "const throttle = (fn, wait) => {\n  let inThrottle\n  let lastFn\n  let lastTime\n  return function () {\n    const context ="
  },
  {
    "path": "packages/crd-seed/component/Footer/index.js",
    "chars": 1128,
    "preview": "import cx from 'classnames'\nimport styles from './index.less'\n\nconst version = VERSION; // eslint-disable-line\nconst foo"
  },
  {
    "path": "packages/crd-seed/component/Footer/index.less",
    "chars": 462,
    "preview": ".footer {\n  font-size: 14px;\n  text-align: center;\n  border-top: 1px solid #e9e9e9;\n  margin: 50px 0 0 240px;\n  padding:"
  },
  {
    "path": "packages/crd-seed/component/Header/index.js",
    "chars": 9238,
    "preview": "import { useState } from 'react'\nimport cx from 'classnames'\nimport { Link } from 'react-router-dom'\nimport Switch from "
  },
  {
    "path": "packages/crd-seed/component/Header/index.less",
    "chars": 1613,
    "preview": "@import '../../style/base.less';\n\n.logo {\n  float: left;\n  padding: 0 0 0 15px;\n\n  img {\n    height: 28px;\n    vertical-"
  },
  {
    "path": "packages/crd-seed/component/Icon/Icon.js",
    "chars": 932,
    "preview": "import { useEffect } from 'react'\nimport cx from 'classnames'\nimport loadSprite from './loadSprite'\nimport styles from '"
  },
  {
    "path": "packages/crd-seed/component/Icon/iconsInfo.js",
    "chars": 2362,
    "preview": "export const IconsInfo = {\n  folder:\n    '<svg viewBox=\"0 0 1024 1024\"><defs/><path d=\"M838 151H450a94 94 0 00-94-91H184"
  },
  {
    "path": "packages/crd-seed/component/Icon/index.js",
    "chars": 47,
    "preview": "import Icon from './Icon'\n\nexport default Icon\n"
  },
  {
    "path": "packages/crd-seed/component/Icon/loadSprite.js",
    "chars": 1908,
    "preview": "import { IconsInfo } from './iconsInfo'\n\n/* tslint:disable:max-line-length */\n// inspried by https://github.com/kisenka/"
  },
  {
    "path": "packages/crd-seed/component/Icon/style/index.less",
    "chars": 132,
    "preview": ".icon {\n  font-size: 24px;\n  vertical-align: middle;\n  fill: currentColor;\n  background-size: cover;\n  width: 1em;\n  hei"
  },
  {
    "path": "packages/crd-seed/component/Menu/Menu.js",
    "chars": 1562,
    "preview": "import { useState } from 'react'\nimport cx from 'classnames'\nimport MenuItem from './MenuItem'\nimport { SubMenu } from '"
  },
  {
    "path": "packages/crd-seed/component/Menu/MenuItem.js",
    "chars": 1527,
    "preview": "import { useEffect, useRef } from 'react'\nimport cx from 'classnames'\nimport { getMenuStyle } from './util'\nimport { use"
  },
  {
    "path": "packages/crd-seed/component/Menu/SubMenu.js",
    "chars": 4541,
    "preview": "import { useState, useRef, Fragment, Children, cloneElement } from 'react'\nimport cx from 'classnames'\nimport { useEnhan"
  },
  {
    "path": "packages/crd-seed/component/Menu/context.js",
    "chars": 287,
    "preview": "import { createContext, useContext } from 'react'\n\nconst MenuContext = createContext(undefined)\n\nexport const MenuProvid"
  },
  {
    "path": "packages/crd-seed/component/Menu/index.js",
    "chars": 47,
    "preview": "import Menu from './Menu'\n\nexport default Menu\n"
  },
  {
    "path": "packages/crd-seed/component/Menu/style/index.less",
    "chars": 5318,
    "preview": "@import './theme.less';\n\n.menu {\n  box-sizing: border-box;\n  list-style: none;\n  margin: 0;\n  padding: 0;\n  color: @menu"
  },
  {
    "path": "packages/crd-seed/component/Menu/style/theme.less",
    "chars": 221,
    "preview": "/* light */\n@menu-color: rgba(0, 0, 0, 0.65);\n@menu-background: #fff;\n@menu-color-hover: #1890ff;\n@menu-color-selected: "
  },
  {
    "path": "packages/crd-seed/component/Menu/transition.js",
    "chars": 2080,
    "preview": "import { useEffect, useRef, useCallback } from 'react'\nimport styles from './style/index.less'\n\nconst ANIMATION_DURATION"
  },
  {
    "path": "packages/crd-seed/component/Menu/util.js",
    "chars": 356,
    "preview": "/* 获取 menu 样式\n  level: 层级\n*/\nconst getMenuStyle = (level, type) => {\n  const basicStyle = {\n    fontSize: level === 0 ? "
  },
  {
    "path": "packages/crd-seed/component/NoMatch/index.js",
    "chars": 484,
    "preview": "import styles from './index.less'\n\nconst NoMatch = () => {\n  // eslint-disable-next-line no-undef\n  const { user, repo }"
  },
  {
    "path": "packages/crd-seed/component/NoMatch/index.less",
    "chars": 252,
    "preview": ".noMatch {\n  position: relative;\n  width: 100%;\n  height: 80%;\n  text-align: center;\n  border-spacing: 0;\n  border-colla"
  },
  {
    "path": "packages/crd-seed/component/Search/README.md",
    "chars": 209,
    "preview": "### API\n\n|    props    | description |  type  | default  |\n| :---------: | :---------: | :----: | :------: |\n| placehold"
  },
  {
    "path": "packages/crd-seed/component/Search/index.js",
    "chars": 1591,
    "preview": "import { useState, useEffect } from 'react'\nimport cx from 'classnames'\nimport Icon from '../Icon'\nimport styles from '."
  },
  {
    "path": "packages/crd-seed/component/Search/index.less",
    "chars": 868,
    "preview": ".search {\n  position: relative;\n\n  input {\n    line-height: 1.5;\n    font-size: 14px;\n    color: #999;\n    outline: 0;\n "
  },
  {
    "path": "packages/crd-seed/component/Tags/index.js",
    "chars": 1342,
    "preview": "import { Link, useMatch } from 'react-router-dom'\nimport { ifProd } from 'crd-client-utils'\nimport { ifAddPrefix } from "
  },
  {
    "path": "packages/crd-seed/component/Tags/index.less",
    "chars": 375,
    "preview": ".tags {\n  width: 100%;\n  padding: 0 20px;\n  height: 100%;\n  display: flex;\n  align-items: center;\n  justify-content: cen"
  },
  {
    "path": "packages/crd-seed/index.js",
    "chars": 401,
    "preview": "import { Routes, Route, Navigate } from 'react-router-dom'\nimport BasicLayout from './layout'\nimport NoMatch from './com"
  },
  {
    "path": "packages/crd-seed/index.less",
    "chars": 367,
    "preview": "body {\n  font-family: cursive, sans-serif;\n}\n\nbody,\nhtml {\n  position: relative;\n}\n\nbody,\nhtml,\nul,\nli {\n  margin: 0;\n  "
  },
  {
    "path": "packages/crd-seed/language/index.js",
    "chars": 175,
    "preview": "const languageMap = {\n  en: {\n    create_tm: 'create',\n    modify_tm: 'modify',\n  },\n  'zh-cn': {\n    create_tm: '创建',\n "
  },
  {
    "path": "packages/crd-seed/layout/index.js",
    "chars": 10246,
    "preview": "import * as React from 'react'\nimport { Routes, Link, Route, Navigate, useLocation } from 'react-router-dom'\nimport cx f"
  },
  {
    "path": "packages/crd-seed/layout/index.less",
    "chars": 1737,
    "preview": "@import '../style/base.less';\n\n.wrapper {\n  &::after {\n    content: '';\n    display: block;\n    clear: both;\n  }\n}\n\n.wra"
  },
  {
    "path": "packages/crd-seed/layout/utils.js",
    "chars": 1224,
    "preview": "/**\n * get keys of open sub menu from pathname\n *  {\n *    pathname: pathname of location,\n *      when pathname is /9f4"
  },
  {
    "path": "packages/crd-seed/package.json",
    "chars": 586,
    "preview": "{\n  \"name\": \"crd-seed\",\n  \"version\": \"1.10.3\",\n  \"description\": \"Default Theme with Create React Doc\",\n  \"main\": \"index."
  },
  {
    "path": "packages/crd-seed/style/base.less",
    "chars": 72,
    "preview": "// z-index\n@menu-zIndex: 99;\n@menu-mask-zIndex: 90;\n@header-zIndex: 100;"
  },
  {
    "path": "packages/crd-seed/style/mobile.less",
    "chars": 164,
    "preview": "// remove the blue area in the mobile\n:global {\n  div,\n  input,\n  textarea,\n  button,\n  select,\n  a,\n  li {\n    -webkit-"
  },
  {
    "path": "packages/crd-seed/utils/index.js",
    "chars": 325,
    "preview": "import isClient from 'diana/lib/isClient'\nimport { ifProd, ifPrerender } from 'crd-client-utils'\n\n/** judge if is in mob"
  },
  {
    "path": "packages/crd-templates/.npmrc",
    "chars": 47,
    "preview": "# .npmrc\n\nregistry=https://registry.npmjs.org/\n"
  },
  {
    "path": "packages/crd-templates/README.md",
    "chars": 745,
    "preview": "                                                     _.-\"\\\n                                                _.-\"      \\\n "
  },
  {
    "path": "packages/crd-templates/default/.github/workflows/gh-pages.yml",
    "chars": 872,
    "preview": "name: github pages\non:\n  push:\n    branches:\n      - main\n\njobs:\n  deploy:\n    runs-on: ubuntu-18.04\n    steps:\n      - "
  },
  {
    "path": "packages/crd-templates/default/.npmrc",
    "chars": 46,
    "preview": "# .npmrc\nregistry=https://registry.npmjs.org/\n"
  },
  {
    "path": "packages/crd-templates/default/Introduction/hello_world.md",
    "chars": 131,
    "preview": "Write docs happily now.\n\nIf there is any problem, welcome give issue [there](https://github.com/MuYunyun/create-react-do"
  },
  {
    "path": "packages/crd-templates/default/README.md",
    "chars": 3198,
    "preview": "                                                     _.-\"\\\n                                                _.-\"      \\\n "
  },
  {
    "path": "packages/crd-templates/default/_.gitignore",
    "chars": 58,
    "preview": "node_modules\npackage-lock.json\n.DS_Store\n.cache\n.crd-dist\n"
  },
  {
    "path": "packages/crd-templates/default/_config.yml",
    "chars": 385,
    "preview": "# details see http://muyunyun.cn/create-react-doc/默认主题\n\n# Site\n# title:\n\n# Menu dir\n## you can also set detailed dir, su"
  },
  {
    "path": "packages/crd-templates/default/_package.json",
    "chars": 378,
    "preview": "{\n  \"name\": \"{{name}}\",\n  \"version\": \"1.0.0\",\n  \"description\": \"Describe {{name}} here\",\n  \"scripts\": {\n    \"start\": \"re"
  },
  {
    "path": "packages/crd-templates/package.json",
    "chars": 380,
    "preview": "{\n  \"name\": \"crd-templates\",\n  \"version\": \"1.10.0\",\n  \"description\": \"Default Templates with Create React Doc\",\n  \"repos"
  },
  {
    "path": "packages/crd-templates/theme/default/Introduction/hello_world.md",
    "chars": 130,
    "preview": "Write docs happily now.\n\nIf there is any problem, welcome give issue [here](https://github.com/MuYunyun/create-react-doc"
  },
  {
    "path": "packages/crd-templates/theme/default/README.md",
    "chars": 790,
    "preview": "                                                     _.-\"\\\n                                                _.-\"      \\\n "
  },
  {
    "path": "packages/crd-templates/theme/default/_.gitignore",
    "chars": 81,
    "preview": "node_modules\npackage-lock.json\n.DS_Store\n.cache\n.crd-dist\nconfig.yml\nIntroduction"
  },
  {
    "path": "packages/crd-templates/theme/default/_.npmrc",
    "chars": 46,
    "preview": "# .npmrc\nregistry=https://registry.npmjs.org/\n"
  },
  {
    "path": "packages/crd-templates/theme/default/_config.yml",
    "chars": 387,
    "preview": "# details see http://muyunyun.cn/create-react-doc/默认主题\n\n# Site\n# title:\n\n# Menu dir\n## you can also set detailed dir, su"
  },
  {
    "path": "packages/crd-templates/theme/default/_index.js",
    "chars": 685,
    "preview": "// The position of current index.js should be kept.\nimport { Routes, Route, Navigate } from 'react-router-dom'\nimport st"
  },
  {
    "path": "packages/crd-templates/theme/default/_index.less",
    "chars": 111,
    "preview": ".center {\n  height: 100vh;\n  width: 100vw;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n}"
  },
  {
    "path": "packages/crd-templates/theme/default/_package.json",
    "chars": 442,
    "preview": "{\n  \"name\": \"{{name}}\",\n  \"version\": \"0.1.0\",\n  \"description\": \"{{name}} theme for create-react-doc\",\n  \"main\": \"index.j"
  },
  {
    "path": "packages/crd-theme/.npmrc",
    "chars": 348,
    "preview": "# .npmrc\n\nregistry=https://registry.npmjs.org/\n\n# https://github.com/sass/node-sass#binary-configuration-parameters\nsass"
  },
  {
    "path": "packages/crd-theme/README.md",
    "chars": 105,
    "preview": "### crd-theme\n\n[create-react-doc](https://github.com/MuYunyun/create-react-doc) 的主题加载包。提供了懒加载, 文件内容解析等能力。"
  },
  {
    "path": "packages/crd-theme/component/Loading/index.js",
    "chars": 164,
    "preview": "import styles from './index.less'\n\nconst Loading = () => {\n  return (\n    <div className={styles.loading}>\n      正在加载中.."
  },
  {
    "path": "packages/crd-theme/component/Loading/index.less",
    "chars": 75,
    "preview": ".loading {\n  text-align: center;\n  min-height: 450px;\n  padding: 30px 0;\n}\n"
  },
  {
    "path": "packages/crd-theme/index.html",
    "chars": 735,
    "preview": "<!DOCTYPE html>\n<html xmlns=\"http://www.w3.org/1999/xhtml\">\n\n<head>\n  <title><%= htmlWebpackPlugin.options.title %></tit"
  },
  {
    "path": "packages/crd-theme/index.js",
    "chars": 470,
    "preview": "import * as React from 'react'\nimport Markdown from './markdown'\nimport './index.less'\n\nexport default function (props) "
  },
  {
    "path": "packages/crd-theme/index.less",
    "chars": 321,
    "preview": "body,\nhtml {\n  position: relative;\n}\n\nbody,\nhtml,\nul,\nli {\n  margin: 0;\n  padding: 0;\n}\n\na {\n  color: #314659;\n  text-de"
  },
  {
    "path": "packages/crd-theme/markdown/Link.js",
    "chars": 1296,
    "preview": "import styles from './Link.less'\n\nexport default ({ title, href, children }) => {\n  let link = href.replace(/(\\/|\\/show\\"
  },
  {
    "path": "packages/crd-theme/markdown/Link.less",
    "chars": 118,
    "preview": ".frame {\n  width: 100%;\n  min-height: 400px;\n  border: 1px solid #e9e9e9;\n  border-radius: 3px;\n  background: #fff;\n}\n"
  },
  {
    "path": "packages/crd-theme/markdown/codeBlock.js",
    "chars": 1597,
    "preview": "/** @jsxRuntime classic /\n/* @jsx jsx */\nimport Highlight, { defaultProps } from 'prism-react-renderer'\nimport nightOwlL"
  },
  {
    "path": "packages/crd-theme/markdown/index.js",
    "chars": 2007,
    "preview": "import * as React from 'react'\nimport cx from 'classnames'\nimport { MDXProvider } from '@mdx-js/react'\nimport { Helmet }"
  },
  {
    "path": "packages/crd-theme/markdown/style/css.less",
    "chars": 407,
    "preview": ".language-css, .language-scss, .language-stylus, .language-less {\n  .keyword, .selector-tag, .subst {\n    color: #d73a49"
  },
  {
    "path": "packages/crd-theme/markdown/style/default.less",
    "chars": 2711,
    "preview": "code {\n  word-wrap: normal;\n}\n\npre {\n  max-height: 35em;\n  position: relative;\n  overflow: auto;\n  background-color: rgb"
  },
  {
    "path": "packages/crd-theme/markdown/style/diff.less",
    "chars": 19,
    "preview": ".language-diff {\n\n}"
  },
  {
    "path": "packages/crd-theme/markdown/style/index.less",
    "chars": 598,
    "preview": ".markdown {\n  padding: 0 40px 20px 40px;\n  margin: 0 auto;\n  font-size: 14px;\n  line-height: 1.5;\n  word-wrap: break-wor"
  },
  {
    "path": "packages/crd-theme/markdown/style/javascript.less",
    "chars": 500,
    "preview": ".language-jsx, .language-js,.language-javascript{\n  .keyword, .selector-tag {\n    color: #b111bf;\n    font-weight: 600;\n"
  },
  {
    "path": "packages/crd-theme/markdown/style/swift.less",
    "chars": 237,
    "preview": ".language-swift {\n  .keyword, .selector-tag, .subst {\n    color: #C600AA;\n    font-weight: normal;\n  }\n  .meta {\n    col"
  },
  {
    "path": "packages/crd-theme/markdown/style/xml.less",
    "chars": 25,
    "preview": ".language-html, .xml{\n\n}\n"
  },
  {
    "path": "packages/crd-theme/package.json",
    "chars": 840,
    "preview": "{\n  \"name\": \"crd-theme\",\n  \"version\": \"1.10.3\",\n  \"description\": \"Default Theme with Create React Doc\",\n  \"main\": \"index"
  },
  {
    "path": "packages/crd-utils/.npmrc",
    "chars": 348,
    "preview": "# .npmrc\n\nregistry=https://registry.npmjs.org/\n\n# https://github.com/sass/node-sass#binary-configuration-parameters\nsass"
  },
  {
    "path": "packages/crd-utils/README.md",
    "chars": 85,
    "preview": "### crd-utils\n\n[create-react-doc](https://github.com/MuYunyun/create-react-doc) 的工具包。"
  },
  {
    "path": "packages/crd-utils/index.js",
    "chars": 2167,
    "preview": "const fs = require('fs')\nconst yaml = require('js-yaml')\nconst { resolveApp, resolveTool } = require('./path')\n\n/* avoid"
  },
  {
    "path": "packages/crd-utils/package.json",
    "chars": 471,
    "preview": "{\n  \"name\": \"crd-utils\",\n  \"version\": \"1.5.0\",\n  \"description\": \"Utils with create react doc\",\n  \"main\": \"index.js\",\n  \""
  },
  {
    "path": "packages/crd-utils/path.js",
    "chars": 283,
    "preview": "const path = require('path')\nconst fs = require('fs')\n\n// handle the problem of symbol in any platform\nconst appDirector"
  },
  {
    "path": "packages/create-react-doc/.npmignore",
    "chars": 90,
    "preview": ".cache\n.gitignore\n.editorconfig\n.create-react-doc-dist\nnode_modules\npackage-lock.json\ndist"
  },
  {
    "path": "packages/create-react-doc/.npmrc",
    "chars": 348,
    "preview": "# .npmrc\n\nregistry=https://registry.npmjs.org/\n\n# https://github.com/sass/node-sass#binary-configuration-parameters\nsass"
  },
  {
    "path": "packages/create-react-doc/.yarnrc",
    "chars": 357,
    "preview": "# .yarnrc\n\nregistry \"https://registry.npmjs.org/\"\n\n# https://github.com/sass/node-sass#binary-configuration-parameters\ns"
  },
  {
    "path": "packages/create-react-doc/README.md",
    "chars": 1097,
    "preview": "                                                     _.-\"\\\n                                                _.-\"      \\\n "
  },
  {
    "path": "packages/create-react-doc/index.js",
    "chars": 3161,
    "preview": "#!/usr/bin/env node\nconst fs = require('fs-extra')\nconst path = require('path')\nconst program = require('commander')\ncon"
  },
  {
    "path": "packages/create-react-doc/package.json",
    "chars": 802,
    "preview": "{\n  \"name\": \"create-react-doc\",\n  \"version\": \"1.10.3\",\n  \"description\": \"Fast static generated site. Just write markdown"
  },
  {
    "path": "packages/leetcode-cli/.npmrc",
    "chars": 348,
    "preview": "# .npmrc\n\nregistry=https://registry.npmjs.org/\n\n# https://github.com/sass/node-sass#binary-configuration-parameters\nsass"
  },
  {
    "path": "packages/leetcode-cli/README.md",
    "chars": 2336,
    "preview": "### crd-leetcode-cli\n\ncrd-leetcode-cli 提供将 [leetcode](https://leetcode-cn.com/) 中已 AC 的题目转化为 markdown 表格的能力。\n\n### Instal"
  },
  {
    "path": "packages/leetcode-cli/leetcode-table.md",
    "chars": 26948,
    "preview": "| # | Title | Explanation | Difficulty | Type |\n|:---:|:---:|:---:|:---:|:---:|\n| 1 | [Two Sum](https://leetcode.com/pro"
  },
  {
    "path": "packages/leetcode-cli/package.json",
    "chars": 860,
    "preview": "{\n  \"name\": \"crd-leetcode-cli\",\n  \"version\": \"1.10.2\",\n  \"description\": \"Generate leetcode table autoly\",\n  \"bin\": {\n   "
  },
  {
    "path": "packages/leetcode-cli/problems.json",
    "chars": 25532,
    "preview": "[\n  {\n    \"title\": \"Two Sum\",\n    \"titleSlug\": \"two-sum\",\n    \"status\": \"ac\",\n    \"difficulty\": \"Easy\",\n    \"questionId\""
  },
  {
    "path": "packages/leetcode-cli/src/cli.js",
    "chars": 728,
    "preview": "#!/usr/bin/env node\nconst commander = require('commander')\nconst packageJson = require('../package.json')\nconst download"
  },
  {
    "path": "packages/leetcode-cli/src/download.js",
    "chars": 2265,
    "preview": "/* eslint-disable import/no-dynamic-require */\n/* eslint-disable global-require */\nconst fs = require('fs')\nconst ora = "
  },
  {
    "path": "packages/leetcode-cli/src/leetcode-table.md",
    "chars": 22782,
    "preview": "| # | Title | Explanation | Difficulty | Type |\n|:---:|:---:|:---:|:---:|:---:|\n| 1 | [Two Sum](https://leetcode.com/pro"
  },
  {
    "path": "packages/leetcode-cli/src/leetcode.js",
    "chars": 3957,
    "preview": "const { GraphQLClient } = require('graphql-request')\nconst ora = require('ora')\nconst inquirer = require('inquirer')\ncon"
  },
  {
    "path": "packages/leetcode-cli/src/logout.js",
    "chars": 121,
    "preview": "const { removeConfig } = require('./utils')\n\nconst logOut = () => {\n  removeConfig('cookies')\n}\n\nmodule.exports = logOut"
  },
  {
    "path": "packages/leetcode-cli/src/problems.json",
    "chars": 21367,
    "preview": "[\n  {\n    \"title\": \"Two Sum\",\n    \"titleSlug\": \"two-sum\",\n    \"status\": \"ac\",\n    \"difficulty\": \"Easy\",\n    \"questionId\""
  },
  {
    "path": "packages/leetcode-cli/src/utils.js",
    "chars": 2257,
    "preview": "/* eslint-disable no-plusplus */\nconst { promisify } = require('util')\nconst homedir = require('os').homedir()\nconst fs "
  },
  {
    "path": "utils/uppackage-dev.sh",
    "chars": 29,
    "preview": "lerna add $1 --scope=$2 --dev"
  },
  {
    "path": "utils/uppackage.sh",
    "chars": 23,
    "preview": "lerna add $1 --scope=$2"
  }
]

About this extraction

This page contains the full source code of the MuYunyun/create-react-doc GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 171 files (288.2 KB), approximately 92.1k tokens, and a symbol index with 29 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!