@icebreakers/monorepo-templates 2.0.7 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/assets/.agents/skills/repoctl/SKILL.md +2 -2
- package/assets/.agents/skills/repoctl/references/templates.md +3 -2
- package/assets/.github/workflows/ci.yml +0 -4
- package/assets/package.json +3 -7
- package/assets/pnpm-workspace.yaml +2 -0
- package/assets/turbo.json +11 -4
- package/dist/cli.mjs +1 -1
- package/dist/index.mjs +2 -2
- package/dist/{prepare-BapI8ioj.mjs → prepare-2VvFwYDz.mjs} +23 -3
- package/package.json +1 -1
- package/template-data.mjs +8 -0
- package/templates/client/package.json +1 -1
- package/templates/nimbus/AGENTS.md +12 -0
- package/templates/nimbus/LICENSE +21 -0
- package/templates/nimbus/README.md +17 -0
- package/templates/nimbus/UPSTREAM.md +16 -0
- package/templates/nimbus/astro.config.ts +28 -0
- package/templates/nimbus/eslint.config.js +25 -0
- package/templates/nimbus/gitignore +5 -0
- package/templates/nimbus/nimbus.json +4 -0
- package/templates/nimbus/package.json +34 -0
- package/templates/nimbus/public/favicon.svg +1 -0
- package/templates/nimbus/scripts/check-locales.mjs +11 -0
- package/templates/nimbus/src/components/aside.astro +9 -0
- package/templates/nimbus/src/components/head.astro +43 -0
- package/templates/nimbus/src/components/search/client.ts +62 -0
- package/templates/nimbus/src/components/search/index.astro +26 -0
- package/templates/nimbus/src/components/sidebar.astro +26 -0
- package/templates/nimbus/src/components.ts +3 -0
- package/templates/nimbus/src/content/docs/get-started.mdx +36 -0
- package/templates/nimbus/src/content/docs/index.mdx +16 -0
- package/templates/nimbus/src/content/docs/writing.mdx +35 -0
- package/templates/nimbus/src/content/docs/zh/get-started.mdx +36 -0
- package/templates/nimbus/src/content/docs/zh/index.mdx +16 -0
- package/templates/nimbus/src/content/docs/zh/writing.mdx +35 -0
- package/templates/nimbus/src/content.config.ts +6 -0
- package/templates/nimbus/src/env.d.ts +6 -0
- package/templates/nimbus/src/layouts/docs.astro +70 -0
- package/templates/nimbus/src/lib/locale.ts +50 -0
- package/templates/nimbus/src/pages/[...slug]/index.md.ts +4 -0
- package/templates/nimbus/src/pages/[...slug]/index.mdx.ts +4 -0
- package/templates/nimbus/src/pages/[...slug].astro +36 -0
- package/templates/nimbus/src/pages/[section]/llms.txt.ts +4 -0
- package/templates/nimbus/src/pages/llms-full.txt.ts +4 -0
- package/templates/nimbus/src/pages/llms.txt.ts +4 -0
- package/templates/nimbus/src/pages/robots.txt.ts +18 -0
- package/templates/nimbus/src/styles/base.css +121 -0
- package/templates/nimbus/src/styles/site.css +186 -0
- package/templates/nimbus/stylelint.config.js +3 -0
- package/templates/nimbus/tsconfig.json +10 -0
- package/templates/server/package.json +1 -1
- package/templates/vitepress/.vitepress/config.ts +4 -5
- package/templates/vitepress/.vitepress/home/content.ts +2 -2
- package/templates/vitepress/.vitepress/home/faq.ts +16 -0
- package/templates/vitepress/.vitepress/mermaid/markdown.ts +18 -0
- package/templates/vitepress/.vitepress/mermaid/mermaid.vue +66 -0
- package/templates/vitepress/.vitepress/mermaid/render.ts +37 -0
- package/templates/vitepress/.vitepress/seo.ts +12 -1
- package/templates/vitepress/.vitepress/theme/components/HomePage.vue +2 -16
- package/templates/vitepress/.vitepress/theme/index.ts +3 -1
- package/templates/vitepress/ai/create.md +1 -1
- package/templates/vitepress/package.json +4 -8
- package/templates/vitepress/reference/commands.md +3 -3
- package/templates/vitepress/reference/template-assets.md +1 -1
- package/templates/vitepress/reference/templates.md +13 -9
- package/templates/vitepress/tasks/index.md +1 -1
- package/templates/vitepress/test/mermaid.test.ts +39 -0
- package/templates/vitepress/test/render.test.ts +48 -0
- package/templates/vitepress/test/seo.test.ts +19 -0
- package/templates/vitepress/tsconfig.json +8 -8
- package/templates/vitepress/vitest.config.ts +8 -0
- package/templates/vitepress/zh/ai/create.md +1 -1
- package/templates/vitepress/zh/learn/monorepo/manage.md +1 -1
- package/templates/vitepress/zh/reference/commands.md +3 -3
- package/templates/vitepress/zh/reference/execution-model.md +1 -1
- package/templates/vitepress/zh/reference/template-assets.md +3 -2
- package/templates/vitepress/zh/reference/templates.md +13 -9
- package/templates/vitepress/zh/tasks/index.md +1 -1
- package/templates/vitepress/zh/tasks/reports.md +1 -1
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
@import './base.css';
|
|
2
|
+
|
|
3
|
+
.site-header {
|
|
4
|
+
display: flex;
|
|
5
|
+
align-items: center;
|
|
6
|
+
justify-content: space-between;
|
|
7
|
+
min-height: 4.5rem;
|
|
8
|
+
padding: 0.8rem max(1.5rem, calc((100vw - 90rem) / 2));
|
|
9
|
+
border-bottom: 1px solid var(--line);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.brand {
|
|
13
|
+
font-weight: 650;
|
|
14
|
+
color: var(--ink);
|
|
15
|
+
text-decoration: none;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.language-link {
|
|
19
|
+
font-size: 0.875rem;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.docs-grid {
|
|
23
|
+
display: grid;
|
|
24
|
+
grid-template-columns: 15rem minmax(0, 1fr) 12rem;
|
|
25
|
+
gap: 3rem;
|
|
26
|
+
max-width: 90rem;
|
|
27
|
+
padding: 3rem 1.5rem;
|
|
28
|
+
margin: auto;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.site-rail, .page-rail {
|
|
32
|
+
font-size: 0.875rem;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.rail-title {
|
|
36
|
+
font-size: 0.75rem;
|
|
37
|
+
font-weight: 650;
|
|
38
|
+
color: var(--muted);
|
|
39
|
+
letter-spacing: 0.04em;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.navigation-list {
|
|
43
|
+
padding: 0;
|
|
44
|
+
list-style: none;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.navigation-list ul {
|
|
48
|
+
padding-left: 1rem;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.navigation-list a {
|
|
52
|
+
display: block;
|
|
53
|
+
padding: 0.3rem 0;
|
|
54
|
+
color: var(--muted);
|
|
55
|
+
text-decoration: none;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.navigation-list a[aria-current="page"] {
|
|
59
|
+
font-weight: 650;
|
|
60
|
+
color: var(--accent);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.navigation-list a:hover {
|
|
64
|
+
color: var(--accent);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.index-link {
|
|
68
|
+
display: inline-block;
|
|
69
|
+
margin-top: 2rem;
|
|
70
|
+
font-family: ui-monospace, monospace;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
main {
|
|
74
|
+
min-width: 0;
|
|
75
|
+
max-width: 48rem;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.description {
|
|
79
|
+
margin-bottom: 2.5rem;
|
|
80
|
+
font-size: 1.15rem;
|
|
81
|
+
color: var(--muted);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.callout {
|
|
85
|
+
padding: 1rem 1.25rem;
|
|
86
|
+
margin: 1.5rem 0;
|
|
87
|
+
background: var(--soft);
|
|
88
|
+
border-left: 3px solid var(--accent);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.callout p:last-child {
|
|
92
|
+
margin-bottom: 0;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
footer {
|
|
96
|
+
padding-top: 1rem;
|
|
97
|
+
margin-top: 3rem;
|
|
98
|
+
font-size: 0.875rem;
|
|
99
|
+
border-top: 1px solid var(--line);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.search-panel {
|
|
103
|
+
margin-bottom: 2rem;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
.search-panel form {
|
|
107
|
+
margin-top: 1rem;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.search-controls {
|
|
111
|
+
display: flex;
|
|
112
|
+
gap: 0.3rem;
|
|
113
|
+
margin-top: 0.5rem;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.search-results {
|
|
117
|
+
padding-left: 1.2rem;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.search-results li {
|
|
121
|
+
margin-bottom: 0.6rem;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.skip-link {
|
|
125
|
+
position: absolute;
|
|
126
|
+
top: -5rem;
|
|
127
|
+
left: 1rem;
|
|
128
|
+
padding: 0.5rem;
|
|
129
|
+
background: var(--surface);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.skip-link:focus {
|
|
133
|
+
top: 1rem;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
@media (max-width: 72rem) {
|
|
137
|
+
.docs-grid {
|
|
138
|
+
grid-template-columns: 13rem minmax(0, 1fr);
|
|
139
|
+
gap: 2rem;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.page-rail {
|
|
143
|
+
display: none;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
@media (max-width: 48rem) {
|
|
148
|
+
.docs-grid {
|
|
149
|
+
grid-template-columns: minmax(0, 1fr);
|
|
150
|
+
padding-top: 1.5rem;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.site-rail {
|
|
154
|
+
padding-bottom: 1rem;
|
|
155
|
+
border-bottom: 1px solid var(--line);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.site-rail .navigation-list {
|
|
159
|
+
display: flex;
|
|
160
|
+
flex-wrap: wrap;
|
|
161
|
+
gap: 0.5rem 1rem;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.site-rail .navigation-list ul {
|
|
165
|
+
padding-left: 0;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.index-link {
|
|
169
|
+
margin-top: 0.5rem;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.search-panel {
|
|
173
|
+
margin-bottom: 1rem;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
@media (prefers-color-scheme: dark) {
|
|
178
|
+
:root {
|
|
179
|
+
--surface: #141c2b;
|
|
180
|
+
--ink: #e3eaf5;
|
|
181
|
+
--muted: #a8b6cc;
|
|
182
|
+
--line: #33415a;
|
|
183
|
+
--accent: #97baff;
|
|
184
|
+
--soft: #222f46;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import Tailwindcss from '@tailwindcss/vite'
|
|
2
2
|
import { defineConfig } from 'vitepress'
|
|
3
3
|
import llmstxt from 'vitepress-plugin-llms'
|
|
4
|
-
import {
|
|
4
|
+
import { mermaidMarkdown } from './mermaid/markdown'
|
|
5
5
|
import { createNav, createSidebars } from './navigation/routes'
|
|
6
6
|
import { createPageHead, siteOrigin } from './seo'
|
|
7
7
|
|
|
8
|
-
export default
|
|
8
|
+
export default defineConfig({
|
|
9
9
|
outDir: '.vitepress/dist',
|
|
10
10
|
srcExclude: ['CHANGELOG.md', 'learn/packages/examples/**', 'zh/learn/packages/examples/**'],
|
|
11
11
|
title: 'repoctl',
|
|
@@ -69,7 +69,6 @@ export default withMermaid(defineConfig({
|
|
|
69
69
|
},
|
|
70
70
|
vite: {
|
|
71
71
|
plugins: [
|
|
72
|
-
// @ts-ignore vitepress-plugin-llms currently exposes Vite 6 plugin types.
|
|
73
72
|
Tailwindcss(),
|
|
74
73
|
llmstxt({
|
|
75
74
|
title: 'repoctl',
|
|
@@ -89,5 +88,5 @@ export default withMermaid(defineConfig({
|
|
|
89
88
|
}),
|
|
90
89
|
],
|
|
91
90
|
},
|
|
92
|
-
|
|
93
|
-
})
|
|
91
|
+
markdown: { config: mermaidMarkdown },
|
|
92
|
+
})
|
|
@@ -32,7 +32,7 @@ const english = {
|
|
|
32
32
|
evidence: {
|
|
33
33
|
title: 'See the plan before the repository changes',
|
|
34
34
|
description: 'Preview work, redact local paths, and keep the exact command sequence with the report.',
|
|
35
|
-
code: 'pnpm exec repo new docs --template
|
|
35
|
+
code: 'pnpm exec repo new docs --template nimbus --dry-run\npnpm exec repo check --dry-run\npnpm exec repo doctor --markdown --redact',
|
|
36
36
|
link: { label: 'See how diagnosis works', href: '/start/diagnose' },
|
|
37
37
|
},
|
|
38
38
|
layers: {
|
|
@@ -77,7 +77,7 @@ const chinese = {
|
|
|
77
77
|
evidence: {
|
|
78
78
|
title: '仓库变化前,先看清执行计划',
|
|
79
79
|
description: '预览操作、脱敏本地路径,并让报告保留准确的命令顺序。',
|
|
80
|
-
code: 'pnpm exec repo new docs --template
|
|
80
|
+
code: 'pnpm exec repo new docs --template nimbus --dry-run\npnpm exec repo check --dry-run\npnpm exec repo doctor --markdown --redact',
|
|
81
81
|
link: { label: '了解诊断流程', href: '/zh/start/diagnose' },
|
|
82
82
|
},
|
|
83
83
|
layers: {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { DocsLocale } from '../navigation/routes'
|
|
2
|
+
|
|
3
|
+
export const faqContent = {
|
|
4
|
+
zh: [
|
|
5
|
+
['repoctl 是什么?', 'repoctl 是面向 pnpm 与 Turborepo monorepo 的任务型 CLI,用于初始化、诊断、创建、校验和发布工作区。'],
|
|
6
|
+
['如何安装 repoctl?', '在项目中运行 pnpm add -D repoctl,然后使用 pnpm exec repo init 和 pnpm exec repo doctor。'],
|
|
7
|
+
['repoctl 与 pnpm、Turborepo 是什么关系?', 'pnpm 和 Turborepo 负责包管理与任务编排,repoctl 负责把团队约定、检查和日常操作组织成可复用任务。'],
|
|
8
|
+
['repo doctor 与 repo check 有什么区别?', 'repo doctor 诊断仓库健康状况,repo check 规划并执行 lint、类型、构建和测试校验。'],
|
|
9
|
+
],
|
|
10
|
+
en: [
|
|
11
|
+
['What is repoctl?', 'repoctl is a task-first CLI for initializing, diagnosing, creating, checking, and releasing pnpm and Turborepo monorepos.'],
|
|
12
|
+
['How do I install repoctl?', 'Run pnpm add -D repoctl, then use pnpm exec repo init and pnpm exec repo doctor.'],
|
|
13
|
+
['How does repoctl relate to pnpm and Turborepo?', 'pnpm and Turborepo provide package management and task orchestration; repoctl turns repository conventions and checks into reusable tasks.'],
|
|
14
|
+
['What is the difference between repo doctor and repo check?', 'repo doctor diagnoses repository health, while repo check plans and runs lint, type, build, and test checks.'],
|
|
15
|
+
],
|
|
16
|
+
} satisfies Record<DocsLocale, [string, string][]>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { MarkdownRenderer } from 'vitepress'
|
|
2
|
+
|
|
3
|
+
export function mermaidMarkdown(md: MarkdownRenderer) {
|
|
4
|
+
const fence = md.renderer.rules.fence!
|
|
5
|
+
md.renderer.rules.fence = (tokens, index, options, env, renderer) => {
|
|
6
|
+
const token = tokens[index]!
|
|
7
|
+
const language = token.info.trim()
|
|
8
|
+
if (language === 'mermaid') {
|
|
9
|
+
const graph = md.utils.escapeHtml(encodeURIComponent(token.content))
|
|
10
|
+
return `<Mermaid graph="${graph}" />\n`
|
|
11
|
+
}
|
|
12
|
+
// Keep the old plugin's escape hatch for displaying Mermaid source code.
|
|
13
|
+
if (language === 'mmd') {
|
|
14
|
+
token.info = 'mermaid'
|
|
15
|
+
}
|
|
16
|
+
return fence(tokens, index, options, env, renderer)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { useData } from 'vitepress'
|
|
3
|
+
import { computed, onMounted, ref, watch } from 'vue'
|
|
4
|
+
import { renderMermaid, resolveMermaidTheme } from './render'
|
|
5
|
+
|
|
6
|
+
const props = defineProps<{ graph: string }>()
|
|
7
|
+
const { frontmatter, isDark, lang } = useData()
|
|
8
|
+
const mounted = ref(false)
|
|
9
|
+
const diagram = ref<HTMLElement>()
|
|
10
|
+
const error = ref('')
|
|
11
|
+
const loading = ref(true)
|
|
12
|
+
const theme = computed(() => resolveMermaidTheme(isDark.value, frontmatter.value.mermaidTheme))
|
|
13
|
+
|
|
14
|
+
onMounted(() => {
|
|
15
|
+
mounted.value = true
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
watch([mounted, () => props.graph, theme], async ([ready, graph, selectedTheme], _, onCleanup) => {
|
|
19
|
+
if (!ready || !diagram.value) {
|
|
20
|
+
return
|
|
21
|
+
}
|
|
22
|
+
let active = true
|
|
23
|
+
onCleanup(() => {
|
|
24
|
+
active = false
|
|
25
|
+
})
|
|
26
|
+
error.value = ''
|
|
27
|
+
loading.value = true
|
|
28
|
+
diagram.value.replaceChildren()
|
|
29
|
+
try {
|
|
30
|
+
const result = await renderMermaid(decodeURIComponent(graph), selectedTheme)
|
|
31
|
+
if (active && diagram.value) {
|
|
32
|
+
// Mermaid sanitizes SVG using its strict security mode.
|
|
33
|
+
diagram.value.innerHTML = result.svg
|
|
34
|
+
result.bindFunctions?.(diagram.value)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch (cause) {
|
|
38
|
+
if (active) {
|
|
39
|
+
error.value = cause instanceof Error ? cause.message : String(cause)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
if (active) {
|
|
44
|
+
loading.value = false
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}, { flush: 'post' })
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
<template>
|
|
51
|
+
<div class="mermaid" :aria-busy="loading">
|
|
52
|
+
<p v-if="loading" role="status">
|
|
53
|
+
{{ lang === 'zh-CN' ? '正在绘制图表…' : 'Rendering diagram…' }}
|
|
54
|
+
</p>
|
|
55
|
+
<p v-if="error" role="alert">
|
|
56
|
+
{{ lang === 'zh-CN' ? '图表绘制失败:' : 'Unable to render diagram: ' }}{{ error }}
|
|
57
|
+
</p>
|
|
58
|
+
<div ref="diagram" class="mermaid__diagram" />
|
|
59
|
+
</div>
|
|
60
|
+
</template>
|
|
61
|
+
|
|
62
|
+
<style scoped>
|
|
63
|
+
.mermaid__diagram {
|
|
64
|
+
overflow-x: auto;
|
|
65
|
+
}
|
|
66
|
+
</style>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { MermaidConfig, RenderResult } from 'mermaid'
|
|
2
|
+
|
|
3
|
+
let pending: Promise<unknown> = Promise.resolve()
|
|
4
|
+
let sequence = 0
|
|
5
|
+
|
|
6
|
+
export type MermaidTheme = NonNullable<MermaidConfig['theme']>
|
|
7
|
+
const themes: readonly MermaidTheme[] = ['base', 'dark', 'default', 'forest', 'neutral', 'neo', 'neo-dark', 'redux', 'redux-dark', 'redux-color', 'redux-dark-color', 'null']
|
|
8
|
+
|
|
9
|
+
export function resolveMermaidTheme(dark: boolean, theme: unknown): MermaidTheme {
|
|
10
|
+
if (dark) {
|
|
11
|
+
return 'dark'
|
|
12
|
+
}
|
|
13
|
+
return typeof theme === 'string' && themes.includes(theme as MermaidTheme)
|
|
14
|
+
? theme as MermaidTheme
|
|
15
|
+
: 'default'
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function renderMermaid(source: string, theme: MermaidTheme): Promise<RenderResult> {
|
|
19
|
+
// initialize() changes global state, so it must share the render queue.
|
|
20
|
+
const result = pending.then(async () => {
|
|
21
|
+
const { default: mermaid } = await import('mermaid')
|
|
22
|
+
const container = document.createElement('div')
|
|
23
|
+
container.setAttribute('aria-hidden', 'true')
|
|
24
|
+
container.style.cssText = 'position:absolute;left:-10000px;top:0;'
|
|
25
|
+
document.body.append(container)
|
|
26
|
+
try {
|
|
27
|
+
mermaid.initialize({ startOnLoad: false, securityLevel: 'strict', suppressErrorRendering: true, theme })
|
|
28
|
+
return await mermaid.render(`repoctl-mermaid-${++sequence}`, source, container)
|
|
29
|
+
}
|
|
30
|
+
finally {
|
|
31
|
+
container.remove()
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
// A malformed diagram must not block later diagrams.
|
|
35
|
+
pending = result.catch(() => {})
|
|
36
|
+
return result
|
|
37
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { HeadConfig, TransformContext } from 'vitepress'
|
|
2
|
+
import { faqContent } from './home/faq'
|
|
2
3
|
|
|
3
4
|
export const siteOrigin = 'https://repoctl.icebreaker.top'
|
|
4
5
|
|
|
@@ -35,6 +36,16 @@ export function createPageHead({ page, title, description }: TransformContext):
|
|
|
35
36
|
{ '@type': 'Organization', 'name': 'repoctl', 'url': siteOrigin, 'logo': `${siteOrigin}/brand/repoctl-mark.svg`, 'sameAs': ['https://github.com/icelib/repoctl', 'https://www.npmjs.com/package/repoctl'] },
|
|
36
37
|
{ '@type': 'SoftwareApplication', 'name': 'repoctl', 'applicationCategory': 'DeveloperApplication', 'operatingSystem': 'macOS, Linux, Windows', 'url': siteOrigin, description, 'sameAs': ['https://github.com/icelib/repoctl', 'https://www.npmjs.com/package/repoctl'], 'offers': { '@type': 'Offer', 'price': '0', 'priceCurrency': 'USD' } },
|
|
37
38
|
{ '@type': 'WebSite', 'name': 'repoctl', 'url': siteOrigin, 'potentialAction': { '@type': 'SearchAction', 'target': `${siteOrigin}/reference/commands?q={search_term_string}`, 'query-input': 'required name=search_term_string' } },
|
|
39
|
+
...(page === 'index.md' || page === 'zh/index.md'
|
|
40
|
+
? [{
|
|
41
|
+
'@type': 'FAQPage',
|
|
42
|
+
'mainEntity': faqContent[isChinese ? 'zh' : 'en'].map(([name, text]) => ({
|
|
43
|
+
'@type': 'Question',
|
|
44
|
+
name,
|
|
45
|
+
'acceptedAnswer': { '@type': 'Answer', text },
|
|
46
|
+
})),
|
|
47
|
+
}]
|
|
48
|
+
: []),
|
|
38
49
|
],
|
|
39
50
|
}
|
|
40
51
|
|
|
@@ -61,6 +72,6 @@ export function createPageHead({ page, title, description }: TransformContext):
|
|
|
61
72
|
['meta', { name: 'twitter:description', content: description }],
|
|
62
73
|
['meta', { name: 'twitter:image', content: socialImage }],
|
|
63
74
|
['meta', { name: 'twitter:image:alt', content: socialImageAlt }],
|
|
64
|
-
['script', { type: 'application/ld+json' }, JSON.stringify(jsonLd)],
|
|
75
|
+
['script', { type: 'application/ld+json' }, JSON.stringify(jsonLd).replace(/</g, '\\u003c')],
|
|
65
76
|
]
|
|
66
77
|
}
|
|
@@ -2,22 +2,11 @@
|
|
|
2
2
|
import type { DocsLocale } from '../../navigation/routes'
|
|
3
3
|
import { computed } from 'vue'
|
|
4
4
|
import { homeContent } from '../../home/content'
|
|
5
|
+
import { faqContent } from '../../home/faq'
|
|
5
6
|
|
|
6
7
|
const props = defineProps<{ locale: DocsLocale }>()
|
|
7
8
|
const content = computed(() => homeContent[props.locale])
|
|
8
|
-
const faq = computed(() => props.locale
|
|
9
|
-
? [
|
|
10
|
-
['repoctl 是什么?', 'repoctl 是面向 pnpm 与 Turborepo monorepo 的任务型 CLI,用于初始化、诊断、创建、校验和发布工作区。'],
|
|
11
|
-
['如何安装 repoctl?', '在项目中运行 pnpm add -D repoctl,然后使用 pnpm exec repo init 和 pnpm exec repo doctor。'],
|
|
12
|
-
['repoctl 与 pnpm、Turborepo 是什么关系?', 'pnpm 和 Turborepo 负责包管理与任务编排,repoctl 负责把团队约定、检查和日常操作组织成可复用任务。'],
|
|
13
|
-
['repo doctor 与 repo check 有什么区别?', 'repo doctor 诊断仓库健康状况,repo check 规划并执行 lint、类型、构建和测试校验。'],
|
|
14
|
-
]
|
|
15
|
-
: [
|
|
16
|
-
['What is repoctl?', 'repoctl is a task-first CLI for initializing, diagnosing, creating, checking, and releasing pnpm and Turborepo monorepos.'],
|
|
17
|
-
['How do I install repoctl?', 'Run pnpm add -D repoctl, then use pnpm exec repo init and pnpm exec repo doctor.'],
|
|
18
|
-
['How does repoctl relate to pnpm and Turborepo?', 'pnpm and Turborepo provide package management and task orchestration; repoctl turns repository conventions and checks into reusable tasks.'],
|
|
19
|
-
['What is the difference between repo doctor and repo check?', 'repo doctor diagnoses repository health, while repo check plans and runs lint, type, build, and test checks.'],
|
|
20
|
-
])
|
|
9
|
+
const faq = computed(() => faqContent[props.locale])
|
|
21
10
|
</script>
|
|
22
11
|
|
|
23
12
|
<template>
|
|
@@ -129,9 +118,6 @@ const faq = computed(() => props.locale === 'zh'
|
|
|
129
118
|
<summary>{{ item[0] }}</summary>
|
|
130
119
|
<p>{{ item[1] }}</p>
|
|
131
120
|
</details>
|
|
132
|
-
<script type="application/ld+json">
|
|
133
|
-
{{ JSON.stringify({ '@context': 'https://schema.org', '@type': 'FAQPage', "mainEntity": faq.map(item => ({ '@type': 'Question', "name": item[0], "acceptedAnswer": { '@type': 'Answer', "text": item[1] } })) }) }}
|
|
134
|
-
</script>
|
|
135
121
|
</section>
|
|
136
122
|
</main>
|
|
137
123
|
</template>
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import type { EnhanceAppContext, Theme } from 'vitepress'
|
|
2
2
|
import DefaultTheme from 'vitepress/theme'
|
|
3
|
+
import Mermaid from '../mermaid/mermaid.vue'
|
|
3
4
|
import HomePage from './components/HomePage.vue'
|
|
4
5
|
import Layout from './Layout.vue'
|
|
5
6
|
import './tailwind.css'
|
|
6
7
|
import './home/index.css'
|
|
7
8
|
|
|
8
9
|
export default {
|
|
9
|
-
|
|
10
|
+
extends: DefaultTheme,
|
|
10
11
|
Layout,
|
|
11
12
|
enhanceApp({ app }: EnhanceAppContext) {
|
|
12
13
|
app.component('HomePage', HomePage)
|
|
14
|
+
app.component('Mermaid', Mermaid)
|
|
13
15
|
},
|
|
14
16
|
} satisfies Theme
|
|
@@ -35,7 +35,7 @@ pnpm exec repo doctor
|
|
|
35
35
|
| API / Hono service | `hono-server` |
|
|
36
36
|
| TypeScript library / SDK | `tsdown` |
|
|
37
37
|
| Vue component library | `vue-lib` |
|
|
38
|
-
| Documentation site | `
|
|
38
|
+
| Documentation site | `nimbus` |
|
|
39
39
|
| CLI | `cli` |
|
|
40
40
|
|
|
41
41
|
Omit `--templates` when the intent is unclear. After init, create a package with:
|
|
@@ -34,23 +34,19 @@
|
|
|
34
34
|
"deploy:dry-run": "wrangler deploy --dry-run",
|
|
35
35
|
"lint": "eslint .",
|
|
36
36
|
"lint:fix": "eslint . --fix",
|
|
37
|
-
"typecheck": "vue-tsc -p tsconfig.json --noEmit"
|
|
37
|
+
"typecheck": "vue-tsc -p tsconfig.json --noEmit",
|
|
38
|
+
"test": "vitest run"
|
|
38
39
|
},
|
|
39
40
|
"dependencies": {
|
|
40
41
|
"vue": "^3.5.43"
|
|
41
42
|
},
|
|
42
43
|
"devDependencies": {
|
|
43
|
-
"@braintree/sanitize-url": "^7.1.2",
|
|
44
44
|
"@tailwindcss/vite": "^4.3.3",
|
|
45
|
-
"cytoscape": "^3.34.3",
|
|
46
|
-
"cytoscape-cose-bilkent": "^4.1.0",
|
|
47
|
-
"dayjs": "^1.11.23",
|
|
48
|
-
"debug": "^4.4.3",
|
|
49
45
|
"mermaid": "^12.0.0",
|
|
50
46
|
"tailwindcss": "^4.3.3",
|
|
51
|
-
"vitepress": "
|
|
47
|
+
"vitepress": "2.0.0-alpha.20",
|
|
52
48
|
"vitepress-plugin-llms": "^1.14.0",
|
|
53
|
-
"
|
|
49
|
+
"vitest": "~5.0.3",
|
|
54
50
|
"vue-tsc": "^3.3.11",
|
|
55
51
|
"wrangler": "^4.135.0"
|
|
56
52
|
}
|
|
@@ -64,9 +64,9 @@ Use it to discover built-in template keys, inspect metadata, verify template hea
|
|
|
64
64
|
```bash
|
|
65
65
|
repo new
|
|
66
66
|
repo new sdk --template tsdown
|
|
67
|
-
repo new docs --template
|
|
68
|
-
repo new docs --template
|
|
69
|
-
repo new docs --template
|
|
67
|
+
repo new docs --template nimbus
|
|
68
|
+
repo new docs --template nimbus --dry-run
|
|
69
|
+
repo new docs --template nimbus --json --out plans/docs.json
|
|
70
70
|
```
|
|
71
71
|
|
|
72
72
|
Use it to create packages and apps. Explicit template keys are validated first; invalid keys fail with suggestions instead of silently falling back.
|
|
@@ -20,7 +20,7 @@ repo templates --category library
|
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
22
|
repo new sdk --template tsdown --dry-run
|
|
23
|
-
repo new docs --template
|
|
23
|
+
repo new docs --template nimbus --json --out plans/docs.json
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
A plan records the selected template, source, destination, package name, workspace pattern, and whether fallback behavior was used. Unknown explicit keys fail with a suggestion.
|
|
@@ -4,14 +4,17 @@ repoctl templates are maintained by `@icebreakers/monorepo-templates`. The CLI,
|
|
|
4
4
|
|
|
5
5
|
## Built-In Templates
|
|
6
6
|
|
|
7
|
-
| Key | Category | Default target | Use case
|
|
8
|
-
| ------------- | -------- | ------------------ |
|
|
9
|
-
| `tsdown` | library | `packages/tsdown` | TypeScript library
|
|
10
|
-
| `vue-lib` | library | `packages/vue-lib` | Vue 3 component library
|
|
11
|
-
| `vue-hono` | app | `apps/client` | Vue 3 + Hono app
|
|
12
|
-
| `hono-server` | service | `apps/server` | Hono API service
|
|
13
|
-
| `vitepress` | docs | `apps/website` | VitePress docs site
|
|
14
|
-
| `
|
|
7
|
+
| Key | Category | Default target | Use case |
|
|
8
|
+
| ------------- | -------- | ------------------ | -------------------------------------- |
|
|
9
|
+
| `tsdown` | library | `packages/tsdown` | TypeScript library |
|
|
10
|
+
| `vue-lib` | library | `packages/vue-lib` | Vue 3 component library |
|
|
11
|
+
| `vue-hono` | app | `apps/client` | Vue 3 + Hono app |
|
|
12
|
+
| `hono-server` | service | `apps/server` | Hono API service |
|
|
13
|
+
| `vitepress` | docs | `apps/website` | VitePress docs site |
|
|
14
|
+
| `nimbus` | docs | `apps/docs` | Nimbus + Astro, default bilingual docs |
|
|
15
|
+
| `cli` | tool | `apps/cli` | TypeScript CLI |
|
|
16
|
+
|
|
17
|
+
Nimbus is the default for the interactive **Docs Site** goal. It includes English at `/`, Chinese at `/zh/`, search and AI documentation endpoints. VitePress remains an explicit alternative. Their default targets are `apps/docs` and `apps/website`, so both can coexist. General creation still defaults to `tsdown`; naming a project `docs` does not infer its template. In noninteractive workflows, pass `--template nimbus` explicitly.
|
|
15
18
|
|
|
16
19
|
## Discover Templates
|
|
17
20
|
|
|
@@ -29,6 +32,7 @@ repo templates --markdown --out docs/templates.md
|
|
|
29
32
|
repo new sdk --template tsdown
|
|
30
33
|
repo new ui --template vue-lib
|
|
31
34
|
repo new api --template hono-server
|
|
35
|
+
repo new docs --template nimbus
|
|
32
36
|
repo new website --template vitepress
|
|
33
37
|
repo new toolbox --template cli
|
|
34
38
|
```
|
|
@@ -85,7 +89,7 @@ For existing projects, update these scripts and the Turbo inputs/outputs, then r
|
|
|
85
89
|
### Create a docs site
|
|
86
90
|
|
|
87
91
|
```bash
|
|
88
|
-
repo new docs --template
|
|
92
|
+
repo new docs --template nimbus
|
|
89
93
|
```
|
|
90
94
|
|
|
91
95
|
Check first:
|
|
@@ -36,7 +36,7 @@ Continue with [Adopt an existing workspace](/tasks/adopt-existing).
|
|
|
36
36
|
```bash
|
|
37
37
|
pnpm exec repo templates
|
|
38
38
|
pnpm exec repo new sdk --template tsdown
|
|
39
|
-
pnpm exec repo new docs --template
|
|
39
|
+
pnpm exec repo new docs --template nimbus --dry-run
|
|
40
40
|
```
|
|
41
41
|
|
|
42
42
|
For daily development, root scripts also work:
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { createMarkdownRenderer } from 'vitepress'
|
|
2
|
+
import { describe, expect, it } from 'vitest'
|
|
3
|
+
import { mermaidMarkdown } from '../.vitepress/mermaid/markdown'
|
|
4
|
+
import { resolveMermaidTheme } from '../.vitepress/mermaid/render'
|
|
5
|
+
|
|
6
|
+
describe('Mermaid Markdown integration', () => {
|
|
7
|
+
it('round-trips diagram text without injecting attributes or Vue expressions', async () => {
|
|
8
|
+
const md = await createMarkdownRenderer(process.cwd(), { config: mermaidMarkdown })
|
|
9
|
+
const source = 'flowchart LR\n A["引号 & <tag> {{ value }}"] --> B["it\'s fine"]\n'
|
|
10
|
+
const html = await md.renderAsync(`\`\`\`mermaid\n${source}\`\`\``)
|
|
11
|
+
const graph = html.match(/graph="([^"]+)"/)![1]!
|
|
12
|
+
expect(decodeURIComponent(graph)).toBe(source)
|
|
13
|
+
expect(html).not.toContain('<tag>')
|
|
14
|
+
expect(html).not.toContain('{{ value }}')
|
|
15
|
+
expect(html).toContain('<Mermaid ')
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it('keeps normal fences and the mmd source-code escape hatch highlighted', async () => {
|
|
19
|
+
const md = await createMarkdownRenderer(process.cwd(), { config: mermaidMarkdown })
|
|
20
|
+
const html = await md.renderAsync('```ts\nconst answer = 42\n```\n\n```mmd\nflowchart LR\n A --> B\n```')
|
|
21
|
+
expect(html).toContain('language-ts')
|
|
22
|
+
expect(html).toContain('language-mermaid')
|
|
23
|
+
expect(html).not.toContain('<Mermaid ')
|
|
24
|
+
expect(html).toContain('shiki')
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it('renders multiple independent component instances', async () => {
|
|
28
|
+
const md = await createMarkdownRenderer(process.cwd(), { config: mermaidMarkdown })
|
|
29
|
+
const html = await md.renderAsync('```mermaid\nflowchart LR\n A --> B\n```\n\n```mermaid\nsequenceDiagram\n A->>B: Hello\n```')
|
|
30
|
+
expect(html.match(/<Mermaid /g)).toHaveLength(2)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('validates page themes and lets dark mode take precedence', () => {
|
|
34
|
+
expect(resolveMermaidTheme(false, 'forest')).toBe('forest')
|
|
35
|
+
expect(resolveMermaidTheme(true, 'forest')).toBe('dark')
|
|
36
|
+
expect(resolveMermaidTheme(false, { theme: 'forest' })).toBe('default')
|
|
37
|
+
expect(resolveMermaidTheme(false, undefined)).toBe('default')
|
|
38
|
+
})
|
|
39
|
+
})
|