@icebreakers/monorepo-templates 2.0.6 → 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.
Files changed (85) hide show
  1. package/README.md +1 -1
  2. package/README.zh-CN.md +1 -1
  3. package/assets/.agents/skills/repoctl/SKILL.md +2 -2
  4. package/assets/.agents/skills/repoctl/references/templates.md +3 -2
  5. package/assets/.github/workflows/ci.yml +0 -4
  6. package/assets/.github/workflows/release.yml +5 -0
  7. package/assets/package.json +3 -7
  8. package/assets/pnpm-workspace.yaml +2 -0
  9. package/assets/turbo.json +11 -4
  10. package/dist/cli.mjs +1 -1
  11. package/dist/index.mjs +2 -2
  12. package/dist/{prepare-BapI8ioj.mjs → prepare-2VvFwYDz.mjs} +23 -3
  13. package/package.json +1 -1
  14. package/template-data.mjs +8 -0
  15. package/templates/client/package.json +1 -1
  16. package/templates/nimbus/AGENTS.md +12 -0
  17. package/templates/nimbus/LICENSE +21 -0
  18. package/templates/nimbus/README.md +17 -0
  19. package/templates/nimbus/UPSTREAM.md +16 -0
  20. package/templates/nimbus/astro.config.ts +28 -0
  21. package/templates/nimbus/eslint.config.js +25 -0
  22. package/templates/nimbus/gitignore +5 -0
  23. package/templates/nimbus/nimbus.json +4 -0
  24. package/templates/nimbus/package.json +34 -0
  25. package/templates/nimbus/public/favicon.svg +1 -0
  26. package/templates/nimbus/scripts/check-locales.mjs +11 -0
  27. package/templates/nimbus/src/components/aside.astro +9 -0
  28. package/templates/nimbus/src/components/head.astro +43 -0
  29. package/templates/nimbus/src/components/search/client.ts +62 -0
  30. package/templates/nimbus/src/components/search/index.astro +26 -0
  31. package/templates/nimbus/src/components/sidebar.astro +26 -0
  32. package/templates/nimbus/src/components.ts +3 -0
  33. package/templates/nimbus/src/content/docs/get-started.mdx +36 -0
  34. package/templates/nimbus/src/content/docs/index.mdx +16 -0
  35. package/templates/nimbus/src/content/docs/writing.mdx +35 -0
  36. package/templates/nimbus/src/content/docs/zh/get-started.mdx +36 -0
  37. package/templates/nimbus/src/content/docs/zh/index.mdx +16 -0
  38. package/templates/nimbus/src/content/docs/zh/writing.mdx +35 -0
  39. package/templates/nimbus/src/content.config.ts +6 -0
  40. package/templates/nimbus/src/env.d.ts +6 -0
  41. package/templates/nimbus/src/layouts/docs.astro +70 -0
  42. package/templates/nimbus/src/lib/locale.ts +50 -0
  43. package/templates/nimbus/src/pages/[...slug]/index.md.ts +4 -0
  44. package/templates/nimbus/src/pages/[...slug]/index.mdx.ts +4 -0
  45. package/templates/nimbus/src/pages/[...slug].astro +36 -0
  46. package/templates/nimbus/src/pages/[section]/llms.txt.ts +4 -0
  47. package/templates/nimbus/src/pages/llms-full.txt.ts +4 -0
  48. package/templates/nimbus/src/pages/llms.txt.ts +4 -0
  49. package/templates/nimbus/src/pages/robots.txt.ts +18 -0
  50. package/templates/nimbus/src/styles/base.css +121 -0
  51. package/templates/nimbus/src/styles/site.css +186 -0
  52. package/templates/nimbus/stylelint.config.js +3 -0
  53. package/templates/nimbus/tsconfig.json +10 -0
  54. package/templates/server/package.json +1 -1
  55. package/templates/vitepress/.vitepress/config.ts +4 -5
  56. package/templates/vitepress/.vitepress/home/content.ts +2 -2
  57. package/templates/vitepress/.vitepress/home/faq.ts +16 -0
  58. package/templates/vitepress/.vitepress/mermaid/markdown.ts +18 -0
  59. package/templates/vitepress/.vitepress/mermaid/mermaid.vue +66 -0
  60. package/templates/vitepress/.vitepress/mermaid/render.ts +37 -0
  61. package/templates/vitepress/.vitepress/seo.ts +12 -1
  62. package/templates/vitepress/.vitepress/theme/components/HomePage.vue +2 -16
  63. package/templates/vitepress/.vitepress/theme/index.ts +3 -1
  64. package/templates/vitepress/ai/create.md +1 -1
  65. package/templates/vitepress/package.json +4 -8
  66. package/templates/vitepress/reference/commands.md +3 -3
  67. package/templates/vitepress/reference/template-assets.md +1 -1
  68. package/templates/vitepress/reference/templates.md +13 -9
  69. package/templates/vitepress/tasks/index.md +1 -1
  70. package/templates/vitepress/tasks/release.md +18 -0
  71. package/templates/vitepress/test/mermaid.test.ts +39 -0
  72. package/templates/vitepress/test/render.test.ts +48 -0
  73. package/templates/vitepress/test/seo.test.ts +19 -0
  74. package/templates/vitepress/tsconfig.json +8 -8
  75. package/templates/vitepress/vitest.config.ts +8 -0
  76. package/templates/vitepress/zh/ai/create.md +1 -1
  77. package/templates/vitepress/zh/learn/monorepo/manage.md +1 -1
  78. package/templates/vitepress/zh/reference/commands.md +3 -3
  79. package/templates/vitepress/zh/reference/execution-model.md +1 -1
  80. package/templates/vitepress/zh/reference/template-assets.md +3 -2
  81. package/templates/vitepress/zh/reference/templates.md +13 -9
  82. package/templates/vitepress/zh/tasks/index.md +1 -1
  83. package/templates/vitepress/zh/tasks/release.md +18 -0
  84. package/templates/vitepress/zh/tasks/reports.md +1 -1
  85. package/assets/.changeset/wicked-ravens-wonder.md +0 -5
@@ -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
+ })
@@ -0,0 +1,48 @@
1
+ import { afterEach, describe, expect, it, vi } from 'vitest'
2
+
3
+ const engine = vi.hoisted(() => ({ initialize: vi.fn(), render: vi.fn() }))
4
+ vi.mock('mermaid', () => ({ default: engine }))
5
+
6
+ afterEach(() => {
7
+ vi.unstubAllGlobals()
8
+ vi.resetAllMocks()
9
+ })
10
+
11
+ describe('Mermaid rendering queue', () => {
12
+ it('serializes theme configuration with rendering and cleans up failed diagrams', async () => {
13
+ const containers: { remove: ReturnType<typeof vi.fn> }[] = []
14
+ vi.stubGlobal('document', {
15
+ createElement: () => {
16
+ const container = { style: {}, setAttribute: vi.fn(), remove: vi.fn() }
17
+ containers.push(container)
18
+ return container
19
+ },
20
+ body: { append: vi.fn() },
21
+ })
22
+ const { renderMermaid } = await import('../.vitepress/mermaid/render')
23
+ const first = Promise.withResolvers<{ svg: string }>()
24
+ const started = Promise.withResolvers<void>()
25
+ engine.render.mockImplementationOnce(() => {
26
+ started.resolve()
27
+ return first.promise
28
+ }).mockRejectedValueOnce(new Error('Invalid diagram')).mockResolvedValueOnce({ svg: '<svg />' })
29
+
30
+ const one = renderMermaid('flowchart LR; A-->B', 'forest')
31
+ const two = renderMermaid('invalid', 'dark')
32
+ const failure = expect(two).rejects.toThrow('Invalid diagram')
33
+ const three = renderMermaid('flowchart LR; B-->C', 'neutral')
34
+ await started.promise
35
+ expect(engine.initialize).toHaveBeenCalledTimes(1)
36
+ expect(engine.initialize).toHaveBeenLastCalledWith(expect.objectContaining({ theme: 'forest' }))
37
+ first.resolve({ svg: '<svg />' })
38
+ await one
39
+ await failure
40
+ await expect(three).resolves.toEqual({ svg: '<svg />' })
41
+ expect(engine.initialize.mock.calls.map(([config]) => config.theme)).toEqual(['forest', 'dark', 'neutral'])
42
+ expect(new Set(engine.render.mock.calls.map(([id]) => id)).size).toBe(3)
43
+ expect(containers).toHaveLength(3)
44
+ for (const container of containers) {
45
+ expect(container.remove).toHaveBeenCalledOnce()
46
+ }
47
+ })
48
+ })
@@ -0,0 +1,19 @@
1
+ import type { TransformContext } from 'vitepress'
2
+ import { expect, it } from 'vitest'
3
+ import { faqContent } from '../.vitepress/home/faq'
4
+ import { createPageHead } from '../.vitepress/seo'
5
+
6
+ it.each([['index.md', 'en'], ['zh/index.md', 'zh']] as const)('renders valid FAQ structured data for %s', (page, locale) => {
7
+ const head = createPageHead({ page, title: 'repoctl', description: 'A </script> description' } as TransformContext)
8
+ const script = head.find(([tag, attrs]) => tag === 'script' && attrs.type === 'application/ld+json')!
9
+ const content = script[2]!
10
+ expect(content).not.toContain('</script>')
11
+ const schema = JSON.parse(content)
12
+ const faq = schema['@graph'].find((item: { '@type': string }) => item['@type'] === 'FAQPage')
13
+ expect(faq.mainEntity.map((item: { name: string, acceptedAnswer: { text: string } }) => [item.name, item.acceptedAnswer.text])).toEqual(faqContent[locale])
14
+ })
15
+
16
+ it('keeps homepage FAQ data off documentation pages', () => {
17
+ const head = createPageHead({ page: 'reference/commands.md', title: 'Commands', description: 'Commands' } as TransformContext)
18
+ expect(head.find(([tag]) => tag === 'script')![2]).not.toContain('FAQPage')
19
+ })
@@ -10,14 +10,14 @@
10
10
  "skipLibCheck": true
11
11
  },
12
12
  "include": [
13
- ".vitepress/home/**/*.ts",
14
- ".vitepress/navigation/**/*.ts",
15
- ".vitepress/seo.ts",
16
- ".vitepress/theme/**/*.ts",
17
- ".vitepress/theme/**/*.vue",
18
- ".vitepress/env.d.ts",
13
+ ".vitepress/**/*.ts",
14
+ ".vitepress/**/*.vue",
19
15
  "scripts/**/*.ts",
20
- "why/examples/2.npm-basic-package/src/**/*.ts",
21
- "why/examples/3.npm-basic-package/src/**/*.ts"
16
+ "test/**/*.ts",
17
+ "vitest.config.ts"
18
+ ],
19
+ "exclude": [
20
+ ".vitepress/dist",
21
+ ".vitepress/cache"
22
22
  ]
23
23
  }
@@ -0,0 +1,8 @@
1
+ import { defineConfig } from 'vitest/config'
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ name: 'website',
6
+ include: ['test/*.test.ts'],
7
+ },
8
+ })
@@ -34,7 +34,7 @@ pnpm exec repo doctor
34
34
  | API / Hono 服务 | `hono-server` |
35
35
  | TypeScript 库 / SDK | `tsdown` |
36
36
  | Vue 组件库 | `vue-lib` |
37
- | 文档站 | `vitepress` |
37
+ | 文档站 | `nimbus` |
38
38
  | CLI | `cli` |
39
39
 
40
40
  意图不清时省略 `--templates`。初始化后再:
@@ -169,7 +169,7 @@ pnpm run repo:new
169
169
  ### 直接指定模板
170
170
 
171
171
  ```bash
172
- pnpm exec repo new docs --template vitepress
172
+ pnpm exec repo new docs --template nimbus
173
173
  pnpm exec repo new sdk --template tsdown
174
174
  ```
175
175
 
@@ -79,9 +79,9 @@ repo templates --markdown --out docs/templates.md
79
79
  ```bash
80
80
  repo new
81
81
  repo new sdk --template tsdown
82
- repo new docs --template vitepress
83
- repo new docs --template vitepress --dry-run
84
- repo new docs --template vitepress --json --out plans/docs.json
82
+ repo new docs --template nimbus
83
+ repo new docs --template nimbus --dry-run
84
+ repo new docs --template nimbus --json --out plans/docs.json
85
85
  ```
86
86
 
87
87
  用途:
@@ -61,7 +61,7 @@ repoctl 的失败状态遵循阻塞优先:
61
61
  如果只是想知道会发生什么,先加 `--dry-run` 或输出计划:
62
62
 
63
63
  ```bash
64
- repo new docs --template vitepress --json --out plans/docs.json
64
+ repo new docs --template nimbus --json --out plans/docs.json
65
65
  repo check --markdown --out reports/check-plan.md
66
66
  ```
67
67
 
@@ -15,6 +15,7 @@ repoctl 的模板能力不只是复制目录。它会把模板元数据、默认
15
15
  | `hono-server` | `templates/server` | `apps/server` | Hono service |
16
16
  | `vue-hono` | `templates/client` | `apps/client` | Vue + Hono app |
17
17
  | `vitepress` | `templates/vitepress` | `apps/website` | docs site |
18
+ | `nimbus` | `templates/nimbus` | `apps/docs` | default docs site |
18
19
  | `cli` | `templates/cli` | `apps/cli` | command line tool |
19
20
 
20
21
  查看实际可用模板:
@@ -42,8 +43,8 @@ repo templates --markdown --out docs/templates.md
42
43
  预览创建:
43
44
 
44
45
  ```bash
45
- repo new docs --template vitepress --dry-run
46
- repo new docs --template vitepress --json --out plans/docs.json
46
+ repo new docs --template nimbus --dry-run
47
+ repo new docs --template nimbus --json --out plans/docs.json
47
48
  ```
48
49
 
49
50
  `--json` 和 `--out` 都隐含 `--dry-run`,不会写入文件。
@@ -6,14 +6,17 @@ repoctl 的模板由 `@icebreakers/monorepo-templates` 维护。CLI、脚手架
6
6
 
7
7
  ## 内置模板
8
8
 
9
- | Key | Category | 默认目录 | 适合场景 |
10
- | ------------- | -------- | ------------------ | --------------------------- |
11
- | `tsdown` | library | `packages/tsdown` | TypeScript 库包 |
12
- | `vue-lib` | library | `packages/vue-lib` | Vue 3 组件库 |
13
- | `vue-hono` | app | `apps/client` | Vue 3 + Hono 前后端一体应用 |
14
- | `hono-server` | service | `apps/server` | Hono API 服务 |
15
- | `vitepress` | docs | `apps/website` | VitePress 文档站 |
16
- | `cli` | tool | `apps/cli` | TypeScript 命令行工具 |
9
+ | Key | Category | 默认目录 | 适合场景 |
10
+ | ------------- | -------- | ------------------ | -------------------------------- |
11
+ | `tsdown` | library | `packages/tsdown` | TypeScript 库包 |
12
+ | `vue-lib` | library | `packages/vue-lib` | Vue 3 组件库 |
13
+ | `vue-hono` | app | `apps/client` | Vue 3 + Hono 前后端一体应用 |
14
+ | `hono-server` | service | `apps/server` | Hono API 服务 |
15
+ | `vitepress` | docs | `apps/website` | VitePress 文档站 |
16
+ | `nimbus` | docs | `apps/docs` | Nimbus + Astro,默认中英双语文档 |
17
+ | `cli` | tool | `apps/cli` | TypeScript 命令行工具 |
18
+
19
+ Nimbus 是新建文档站点(`Docs Site`)的默认模板,提供英文 `/`、中文 `/zh/`、搜索和 AI 文档入口。VitePress 仍可显式选择,两者分别生成到 `apps/docs` 和 `apps/website`,可以同时使用。通用创建命令仍默认使用 `tsdown`;项目名称 `docs` 不会隐式改变模板。非交互调用请显式指定 `--template nimbus`。
17
20
 
18
21
  ## 查看模板
19
22
 
@@ -31,6 +34,7 @@ repo templates --markdown --out docs/templates.md
31
34
  repo new sdk --template tsdown
32
35
  repo new ui --template vue-lib
33
36
  repo new api --template hono-server
37
+ repo new docs --template nimbus
34
38
  repo new website --template vitepress
35
39
  repo new toolbox --template cli
36
40
  ```
@@ -87,7 +91,7 @@ repo new api --template hono-server
87
91
  ### 要创建文档站
88
92
 
89
93
  ```bash
90
- repo new docs --template vitepress
94
+ repo new docs --template nimbus
91
95
  ```
92
96
 
93
97
  生成后先检查:
@@ -36,7 +36,7 @@ pnpm exec repo doctor --markdown --redact --out reports/doctor-after.md
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 vitepress --dry-run
39
+ pnpm exec repo new docs --template nimbus --dry-run
40
40
  ```
41
41
 
42
42
  如果只是日常开发,根脚本也可以:
@@ -70,6 +70,24 @@ const hook = { script: 'publish:extension', idempotent: true }
70
70
 
71
71
  程序化注入的旧 `GitHubOperations` adapter 保留原行为;要启用跨 runner 恢复,需要实现 `readReleaseState`、`writeReleaseState`、`listReleases`、`ensureTag`,并建议提供 `readTagTarget`。写入必须校验 revision,查询错误必须抛出。仅有本地 summary 不能代替远端检查点。
72
72
 
73
+ ## 无需发布的记录与原提交恢复
74
+
75
+ 仅包含 `none` 或空 frontmatter 的 intent 表示无需发布。它不会创建版本 PR,也不阻断已经准备好的版本发布。合并后重新出现、但 ledger 已记录消费的 intent 不会再次升级版本;pnpm 会在下一次有效版本操作中清理这些文件。
76
+
77
+ 版本 PR 以 pnpm 返回的实际发布包列表为依据,不再把任意 Git 改动视为版本变化。新包首次发布可以保留初始版本号,但仍会列入发布说明。仅清理记录不会创建发布 PR。
78
+
79
+ 已有版本尚未完成发布时,新的版本准备会在消费 intent 前失败,并给出原始提交和恢复命令。先恢复旧发布,再准备新版本:
80
+
81
+ ```bash
82
+ repo release ci --mode publish --source-sha <完整的-main-提交-SHA> --dry-run
83
+ repo release ci --mode publish --source-sha <完整的-main-提交-SHA>
84
+ repo release ci --mode prepare
85
+ ```
86
+
87
+ 受管 Release 工作流为 `publish` 和 `publish-unpublished` 提供相同的 `source-sha` 输入。当前工具会在独立目录检出原提交,按照其锁文件安装、构建和验证,恢复该提交 manifest 或 ledger 新增的整批版本,包括依赖传播升级的包。package/version 输入只校验是否属于该批发布,不拆分原发布批次。SHA 必须属于 `origin/main` 历史。dry-run 只检查源码与远端状态,不安装、不上传、不运行 hook、不写检查点。后续变更保留到下一版本;恢复过程中不修改版本号。
88
+
89
+ 恢复保留 GitHub 工作流环境,供可信发布和 provenance 使用:签名身份对应运行工作流的提交;独立检出的源码、发布检查点、Git tag 和 Release 目标则对应 `source-sha`。不要通过覆盖 `GITHUB_SHA` 恢复旧源码,npm 会拒绝与工作流签名身份不一致的 provenance。
90
+
73
91
  ## 下一步
74
92
 
75
93
  阅读[发包与变更日志](/zh/learn/monorepo/publish),再查看[报告与输出](/zh/tasks/reports)了解 CI 产物。
@@ -94,7 +94,7 @@ JSON 适合机器判断,不适合人工长时间阅读:
94
94
  ```bash
95
95
  repo doctor --json --out reports/doctor.json
96
96
  repo templates --check --json --out reports/templates.json
97
- repo new docs --template vitepress --json --out plans/docs.json
97
+ repo new docs --template nimbus --json --out plans/docs.json
98
98
  ```
99
99
 
100
100
  常见消费方式:
@@ -1,5 +0,0 @@
1
- ---
2
- "@icebreakers/monorepo-templates": none
3
- ---
4
-
5
- 优化构建产物测试夹具的依赖复用,避免 Windows CI 因反复复制依赖树而超时;仅影响测试,无需发布。