Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Force LF line endings for all text files
* text=auto eol=lf
63 changes: 63 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

permissions:
contents: read

jobs:
check:
runs-on: ubuntu-latest
name: Lint & Typecheck

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup pnpm
uses: pnpm/action-setup@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: package.json
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Typecheck
run: pnpm typecheck

- name: Lint
run: pnpm lint

- name: Format check
run: pnpm format:check

test:
runs-on: ubuntu-latest
name: Run Solutions

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup pnpm
uses: pnpm/action-setup@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: package.json
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Run all solutions
run: pnpm test
68 changes: 43 additions & 25 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -1,39 +1,57 @@
name: Leetcode Release CI / CD
name: Deploy to GitHub Pages

on:
push:
branches:
- master
branches: [main]

permissions:
contents: read
pages: write
id-token: write

concurrency:
group: pages-deploy
cancel-in-progress: true

jobs:
build-and-deploy:
build:
runs-on: ubuntu-latest
name: Leetcode Continuous Deploy

strategy:
matrix:
node-version: [18.x]
name: Build

steps:
- name: Actions Checkout 🛎️
uses: actions/checkout@v3
- name: Checkout
uses: actions/checkout@v4

- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- name: Setup pnpm
uses: pnpm/action-setup@v4

- name: Install pnpm
run: npm install -g pnpm@8
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: package.json
cache: pnpm

- name: Install dependencies 🔧
run: pnpm install
- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Build project 🔧
run: pnpm build
- name: Build docs
run: pnpm docs:build

- name: Deploy to Github Pages 🚀
uses: JamesIves/github-pages-deploy-action@v4
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
branch: gh-pages
folder: docs/.vuepress/dist
path: docs/.vitepress/dist

deploy:
needs: build
runs-on: ubuntu-latest
name: Deploy

environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}

steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
node_modules/
.DS_Store
docs/.vuepress/dist/
docs/.vitepress/dist/
docs/.vitepress/cache/
dist/
coverage/
*.log
Expand Down
2 changes: 2 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
node_modules
dist
docs/.vuepress/dist
docs/.vitepress/dist
docs/.vitepress/cache
docs/docs
.github
pnpm-lock.yaml
40 changes: 15 additions & 25 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -1,27 +1,17 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug Current TS File",
"type": "node",
"request": "launch",
"program": "${file}",
"runtimeArgs": [
"--nolazy",
"-r",
"ts-node/register"
],
"sourceMaps": true,
"cwd": "${workspaceFolder}",
"console": "integratedTerminal",
"skipFiles": [
"<node_internals>/**",
"node_modules/**"
],
"resolveSourceMapLocations": [
"${workspaceFolder}/**",
"!**/node_modules/**"
]
}
]
"version": "0.2.0",
"configurations": [
{
"name": "Debug Current TS File",
"type": "node",
"request": "launch",
"program": "${file}",
"runtimeArgs": ["--nolazy", "-r", "ts-node/register"],
"sourceMaps": true,
"cwd": "${workspaceFolder}",
"console": "integratedTerminal",
"skipFiles": ["<node_internals>/**", "node_modules/**"],
"resolveSourceMapLocations": ["${workspaceFolder}/**", "!**/node_modules/**"]
}
]
}
58 changes: 58 additions & 0 deletions docs/.vitepress/config.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { defineConfig } from 'vitepress';
import { generateSidebar } from './sidebar';
import { fileURLToPath } from 'url';
import { dirname, resolve } from 'path';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const docsRoot = resolve(__dirname, '..');

const sidebar = generateSidebar(docsRoot);

export default defineConfig({
title: 'LeetCode 通关手册',
description: '框架通解 · 举一反三 —— LeetCode 题解与算法专题笔记',
lang: 'zh-CN',

head: [['link', { rel: 'icon', href: '/favicon.ico' }]],

themeConfig: {
nav: [
{ text: '首页', link: '/' },
{ text: '📖 题解', link: '/docs/list/array/1.two-sum' },
{ text: '📖 专题', link: '/docs/topic/0.introduction' }
],

sidebar,

socialLinks: [{ icon: 'github', link: 'https://github.com/realduang/leetcode-in-javascript' }],

outline: {
level: [2, 3],
label: '章节导航'
},

search: {
provider: 'local'
},

footer: {
message: 'MIT LICENSE',
copyright: 'Copyright © 2019-present by Duang'
},

docFooter: {
prev: '上一篇',
next: '下一篇'
},
returnToTopLabel: '返回顶部',
sidebarMenuLabel: '菜单',
darkModeSwitchLabel: '主题'
},

markdown: {
headers: {
level: [2, 3]
}
}
});
File renamed without changes
File renamed without changes.
113 changes: 113 additions & 0 deletions docs/.vitepress/sidebar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { readdirSync, readFileSync, statSync } from 'fs';
import { join, basename } from 'path';
import type { DefaultTheme } from 'vitepress';

const categoryTitleMap: Record<string, string> = {
docs: '题库',
list: '📖 题解',
topic: '📖 专题',
array: '数组',
backtracking: '回溯法',
'binary-search': '二分查找',
'bit-manipulation': '位运算',
'breadth-first-search': '广度优先搜索',
'depth-first-search': '深度优先搜索',
'divide-and-conquer': '分治法',
'dynamic-programming': '动态规划',
design: '设计题',
graph: '图',
greedy: '贪心法',
'hash-table': '哈希表',
heap: '堆',
'linked-list': '链表',
math: '数学',
'sliding-window': '滑动窗口',
sort: '排序',
stack: '栈',
string: '字符串',
tree: '树',
trie: '查找树',
'two-pointers': '双指针',
unknown: '未分类'
};

function getCategoryTitle(name: string): string {
return categoryTitleMap[name] || name;
}

/**
* Extract the first `# heading` from a markdown file as its display title.
* Falls back to the filename (without extension) if no heading is found.
*/
function extractTitleFromMd(filePath: string, fallback: string): string {
try {
const content = readFileSync(filePath, 'utf-8');
const match = content.match(/^#\s+(.+)$/m);
return match ? match[1].trim() : fallback;
} catch {
return fallback;
}
}

function getNumber(filename: string): number {
return Number(filename.split('.')[0]);
}

function getMarkdownFiles(dir: string): string[] {
return readdirSync(dir)
.filter(f => f.endsWith('.md') && f !== 'README.md' && f !== 'index.md')
.sort((a, b) => getNumber(a) - getNumber(b));
}

function generateSidebarForList(docsRoot: string): DefaultTheme.SidebarItem[] {
const listDir = join(docsRoot, 'docs', 'list');
const categories = readdirSync(listDir).filter(f => statSync(join(listDir, f)).isDirectory());

return categories.map(cat => {
const catDir = join(listDir, cat);
const files = getMarkdownFiles(catDir);
return {
text: getCategoryTitle(cat),
collapsed: true,
items: files.map(f => {
const filePath = join(catDir, f);
const slug = basename(f, '.md');
return {
text: extractTitleFromMd(filePath, slug),
link: `/docs/list/${cat}/${slug}`
};
})
};
});
}

function generateSidebarForTopic(docsRoot: string): DefaultTheme.SidebarItem[] {
const topicDir = join(docsRoot, 'docs', 'topic');
const files = getMarkdownFiles(topicDir);

return files.map(f => {
const filePath = join(topicDir, f);
const slug = basename(f, '.md');
return {
text: extractTitleFromMd(filePath, slug),
link: `/docs/topic/${slug}`
};
});
}

export function generateSidebar(docsRoot: string): DefaultTheme.Sidebar {
return {
'/docs/list/': [
{
text: '📖 题解',
items: generateSidebarForList(docsRoot)
}
],
'/docs/topic/': [
{
text: '📖 专题',
items: generateSidebarForTopic(docsRoot)
}
]
};
}
Loading