搭建 type-challenges 题目解答网站

159 阅读2分钟

获取type-challenges

使用git,拉取type-challenges代码

git clone https://github.com/type-challenges/type-challenges

生成playground

npm run generate

将playground中的ts文件转成markdown

cd playground

npm init -y

npm install -D gulp gulp-rename through2

在package.json中的scripts添加

"build": "gulp"

在playground根目录下新建gulpfile.js。将下面内容复制到文件中:

const { src, dest } = require('gulp')
const rename = require('gulp-rename')
const through2 = require('through2')

exports.default = function () {
    return src('./**/*.ts', { ignore: 'node_modules/**' })
        .pipe(through2.obj(function (file, _, cb) {
            if (file.isBuffer()) {
                let content = file.contents.toString()
                content = content.replace(/\/\*|\*\//g, '')
                content = content.split(/_*\s*(?:Your Code Here|Test Cases|Further Steps)\s*_*/g)
                let str = '#  ' + content[0].replace(/(Parameters|Intersection|MutableKeys|PercentageParser|Unique|DeepMutable)<T/g, '$1&lt;T').replace(/^[\s\r\n]+/, '')
                str += '\r\n\r\n### Answer\r\n' + ('```ts\r\n' + content[1] + '\r\n```').replace(/^\s*$(?:\n|\r\n)/mg, '')
                file.contents = Buffer.from(str)
            }
            cb(null, file)
        }))
        .pipe(rename({ extname: '.md' }))
        .pipe(dest('../dist/docs'))
}

运行命令在上级目录生成dist文件夹。

生成网站

切换到dist目录

npm init -y

npm install -D vitepress

npx vitepress init

// Theme 选择 Default Theme + Customization

npm install -D vite-plugin-vitepress-auto-sidebar vue

在.vitepress/theme新建compponents/HeroPage.vue。文件内容:

<script setup lang="ts">
import { defineOptions } from "vue";
defineOptions({ name: "HeroPage" });
</script>

<template>
  <main class="hero-page-wrapper">
    <div class="content">
      <a href="https://github.com/type-challenges/type-challenges">
        <img src="/logo.svg" width="500" alt="type-challenges" />
      </a>
      <div>
        <h1>Collection of TypeScript type challenges with online judge</h1>
        <p>
          <a href="/warm/00013-warm-hello-world.html" class="button"> 快速开始 </a>
        </p>
      </div>
    </div>
  </main>
</template>

<style scoped>
.hero-page-wrapper {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

.content {
  justify-content: center;
  align-items: center;
  width: 100%;
  margin-bottom: 5rem;
  text-align: center;
}

div {
  text-align: center;
}

h1 {
  font-weight: 400;
  margin: 0;
  font-size: 1.5em;
  color: #273849;
  opacity: 0.7;
}

a img {
  margin: 0 auto;
}

a.button {
  padding: 0.75em 2em;
  border-radius: 2em;
  display: inline-block;
  color: #fff;
  background-color: #304455;
  transition: all 0.15s ease;
  box-sizing: border-box;
  border: 1px solid #304455;
  text-decoration: none;

  margin: 2em 0;
  font-size: 0.9em;
  font-weight: 600;
  letter-spacing: 0.1em;
  min-width: 8em;
  text-align: center;

  text-transform: uppercase;
}
</style>

修改theme/index.ts,注册HeroPage组件:

enhanceApp({ app, router, siteData }) {
    app.component(HeroPage.name, HeroPage);
}

修改.vitepress/config.mts:

import { defineConfig } from 'vitepress'
// @ts-ignore
import AutoSidebar from 'vite-plugin-vitepress-auto-sidebar'

// https://vitepress.dev/reference/site-config
export default defineConfig({
  base: '/',
  srcDir: './docs',
  outDir: './.vitepress/tsch',
  title: 'Tsch',
  lang: 'zh-CN',
  description: "type-challenges ",
  lastUpdated: true,
  cleanUrls: false,
  metaChunk: true,
  head: [
    ['meta', { name: 'theme-color', content: '#5f67ee' }],
    ['meta', { property: 'og:type', content: 'website' }],
    ['meta', { property: 'og:locale', content: 'zh' }]
  ],
  themeConfig: {
    // https://vitepress.dev/reference/default-theme-config
    nav: [
      { text: 'Home', link: '/' },
      { text: 'Warm', link: '/warm/00013-warm-hello-world' },
      { text: 'Easy', link: '/easy/00004-easy-pick' },
      { text: 'Medium', link: '/medium/00002-medium-get-return-type' },
      { text: 'Hard', link: '/hard/00006-hard-simple-vue' },
      { text: 'Extreme', link: '/extreme/00005-extreme-get-readonly-keys' }
    ],

    sidebar: [
      {
        text: 'Examples',
        items: [
          { text: 'Markdown Examples', link: '/markdown-examples' },
          { text: 'Runtime API Examples', link: '/api-examples' }
        ]
      }
    ],

    socialLinks: [
      { icon: 'github', link: 'https://github.com/vuejs/vitepress' }
    ],

    outline: false,

    search: {
        provider: 'local'
    }
  },
  vite: {
    plugins: [
      // add plugin
      AutoSidebar({
        path: '/docs',
        ignoreList: ['node_modules', 'public'],
        sideBarResolved: (data) => Object.entries(data).reduce((p, [k, v]) => {
          return {
            ...p,
            // @ts-ignore
            [k]: v.map((s) => ({
              items: [...s.items]
                .sort((a, b) => a.text.split('-')[0] - (b.text).split('-')[0])
                .map(s => ({ text: s.text.replace(new RegExp('\\d+\\-[A-Za-z]+\\-', 'g'), ''), link: s.link }))
            }))
          }
        }, {})
      })
    ]
  }
})

新建或者修改docs/index.md:

---
layout: page
---

<HeroPage />

最后,将type-challenges的logo.svg复制到docs/public。