@icebreakers/monorepo-templates 2.0.7 → 2.2.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 +4 -2
- package/assets/.agents/skills/repoctl/references/commands.md +224 -13
- package/assets/.agents/skills/repoctl/references/config.md +70 -6
- package/assets/.agents/skills/repoctl/references/dependencies.md +37 -0
- package/assets/.agents/skills/repoctl/references/knip.md +34 -0
- package/assets/.agents/skills/repoctl/references/templates.md +26 -2
- package/assets/.github/workflows/ci.yml +10 -14
- package/assets/package.json +8 -16
- package/assets/pnpm-workspace.yaml +4 -1
- package/assets/repoctl.config.ts +1 -1
- package/assets/turbo.json +12 -4
- package/dist/cli.mjs +1 -1
- package/dist/index.d.mts +281 -1
- package/dist/index.mjs +1138 -75
- package/dist/{prepare-BapI8ioj.mjs → prepare-BYNSxvoB.mjs} +96 -4
- package/package.json +2 -1
- package/template-data.mjs +32 -0
- package/templates/client/package.json +2 -2
- package/templates/next/README.md +31 -0
- package/templates/next/eslint.config.js +5 -0
- package/templates/next/gitignore +7 -0
- package/templates/next/next.config.ts +5 -0
- package/templates/next/package.json +36 -0
- package/templates/next/src/app/api/health/route.ts +5 -0
- package/templates/next/src/app/counter.tsx +19 -0
- package/templates/next/src/app/favicon.ico +0 -0
- package/templates/next/src/app/globals.css +109 -0
- package/templates/next/src/app/layout.tsx +17 -0
- package/templates/next/src/app/page.tsx +16 -0
- package/templates/next/stylelint.config.js +3 -0
- package/templates/next/test/counter.test.tsx +22 -0
- package/templates/next/tsconfig.json +40 -0
- package/templates/next/turbo.json +24 -0
- package/templates/next/vitest.config.js +9 -0
- 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/react-lib/LICENSE +21 -0
- package/templates/react-lib/README.md +52 -0
- package/templates/react-lib/eslint.config.js +5 -0
- package/templates/react-lib/gitignore +4 -0
- package/templates/react-lib/package.json +48 -0
- package/templates/react-lib/src/counter.tsx +28 -0
- package/templates/react-lib/src/index.ts +2 -0
- package/templates/react-lib/src/style.css +36 -0
- package/templates/react-lib/stylelint.config.js +3 -0
- package/templates/react-lib/test/counter.test.tsx +28 -0
- package/templates/react-lib/test-d/index.test-d.ts +11 -0
- package/templates/react-lib/tsconfig.app.json +15 -0
- package/templates/react-lib/tsconfig.build.json +12 -0
- package/templates/react-lib/tsconfig.json +4 -0
- package/templates/react-lib/tsconfig.node.json +11 -0
- package/templates/react-lib/vite.config.ts +22 -0
- package/templates/react-lib/vitest.config.ts +5 -0
- package/templates/react-vite/README.md +27 -0
- package/templates/react-vite/eslint.config.js +5 -0
- package/templates/react-vite/gitignore +6 -0
- package/templates/react-vite/index.html +13 -0
- package/templates/react-vite/package.json +36 -0
- package/templates/react-vite/src/app.tsx +22 -0
- package/templates/react-vite/src/main.tsx +15 -0
- package/templates/react-vite/src/style.css +95 -0
- package/templates/react-vite/stylelint.config.js +3 -0
- package/templates/react-vite/test/app.test.tsx +22 -0
- package/templates/react-vite/tsconfig.app.json +26 -0
- package/templates/react-vite/tsconfig.json +11 -0
- package/templates/react-vite/tsconfig.node.json +18 -0
- package/templates/react-vite/vite.config.ts +6 -0
- package/templates/react-vite/vitest.config.ts +9 -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/navigation/routes.ts +7 -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/artifacts.md +51 -0
- package/templates/vitepress/reference/commands.md +152 -3
- package/templates/vitepress/reference/config.md +101 -7
- package/templates/vitepress/reference/dependencies.md +116 -0
- package/templates/vitepress/reference/dependency-admission.md +67 -0
- package/templates/vitepress/reference/devcontainer.md +25 -0
- package/templates/vitepress/reference/index.md +1 -0
- package/templates/vitepress/reference/install-security.md +51 -0
- package/templates/vitepress/reference/knip.md +55 -0
- package/templates/vitepress/reference/move.md +39 -0
- package/templates/vitepress/reference/organization-presets.md +89 -0
- package/templates/vitepress/reference/removal.md +42 -0
- package/templates/vitepress/reference/template-assets.md +40 -1
- package/templates/vitepress/reference/templates.md +376 -9
- package/templates/vitepress/start/diagnose.md +19 -2
- package/templates/vitepress/tasks/checks.md +149 -1
- package/templates/vitepress/tasks/ci.md +93 -1
- package/templates/vitepress/tasks/index.md +1 -1
- package/templates/vitepress/tasks/release.md +72 -6
- 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/artifacts.md +51 -0
- package/templates/vitepress/zh/reference/commands.md +151 -3
- package/templates/vitepress/zh/reference/config.md +94 -2
- package/templates/vitepress/zh/reference/dependencies.md +116 -0
- package/templates/vitepress/zh/reference/dependency-admission.md +67 -0
- package/templates/vitepress/zh/reference/devcontainer.md +25 -0
- package/templates/vitepress/zh/reference/execution-model.md +1 -1
- package/templates/vitepress/zh/reference/index.md +1 -0
- package/templates/vitepress/zh/reference/install-security.md +51 -0
- package/templates/vitepress/zh/reference/knip.md +55 -0
- package/templates/vitepress/zh/reference/move.md +39 -0
- package/templates/vitepress/zh/reference/organization-presets.md +89 -0
- package/templates/vitepress/zh/reference/removal.md +42 -0
- package/templates/vitepress/zh/reference/template-assets.md +22 -6
- package/templates/vitepress/zh/reference/templates.md +356 -9
- package/templates/vitepress/zh/start/diagnose.md +24 -13
- package/templates/vitepress/zh/tasks/checks.md +150 -0
- package/templates/vitepress/zh/tasks/ci.md +93 -1
- package/templates/vitepress/zh/tasks/index.md +1 -1
- package/templates/vitepress/zh/tasks/release.md +72 -6
- package/templates/vitepress/zh/tasks/reports.md +1 -1
- package/templates/vue-lib/package.json +1 -1
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
|
5
|
+
"target": "ES2023",
|
|
6
|
+
"lib": ["ES2023"],
|
|
7
|
+
"types": ["node"],
|
|
8
|
+
"verbatimModuleSyntax": true
|
|
9
|
+
},
|
|
10
|
+
"include": ["vite.config.ts", "vitest.config.ts"]
|
|
11
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import path from 'node:path'
|
|
2
|
+
import react from '@vitejs/plugin-react'
|
|
3
|
+
import { defineConfig } from 'vite'
|
|
4
|
+
import dts from 'vite-plugin-dts'
|
|
5
|
+
|
|
6
|
+
export default defineConfig({
|
|
7
|
+
plugins: [react(), dts({ tsconfigPath: './tsconfig.build.json', entryRoot: './src', clearPureImport: true })],
|
|
8
|
+
build: {
|
|
9
|
+
lib: {
|
|
10
|
+
entry: path.resolve(import.meta.dirname, 'src/index.ts'),
|
|
11
|
+
formats: ['es'],
|
|
12
|
+
fileName: 'index',
|
|
13
|
+
cssFileName: 'style',
|
|
14
|
+
},
|
|
15
|
+
rolldownOptions: {
|
|
16
|
+
// Include JSX runtimes and every peer subpath in the external boundary.
|
|
17
|
+
external: id => /^(?:react|react-dom)(?:\/|$)/u.test(id),
|
|
18
|
+
// Counter is a client component; preserve its boundary in bundled RSC consumers.
|
|
19
|
+
output: { banner: '\'use client\';' },
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
})
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# React application
|
|
2
|
+
|
|
3
|
+
A small React and TypeScript SPA built with Vite. It includes an accessible counter, a Vitest interaction test, and the workspace's shared ESLint and Stylelint configuration.
|
|
4
|
+
|
|
5
|
+
From the workspace root, enable the declared package manager and install dependencies:
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
corepack enable
|
|
9
|
+
pnpm install
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Run these commands from this application directory:
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
pnpm dev
|
|
16
|
+
pnpm build
|
|
17
|
+
pnpm lint
|
|
18
|
+
pnpm typecheck
|
|
19
|
+
pnpm test
|
|
20
|
+
pnpm preview
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
`build` creates `dist/`; `preview` serves that production output locally. `lint` checks both code and CSS. `typecheck` uses TypeScript project references for the app, tests, and Vite configuration.
|
|
24
|
+
|
|
25
|
+
To consume another workspace package, add it as a `workspace:*` dependency, import its package name, and build its public output first. Running `pnpm build` at the workspace root lets Turbo order dependency builds. Do not import another package's `src/` directory.
|
|
26
|
+
|
|
27
|
+
Router, state management, backend, and CSS framework choices are left to your application.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<meta name="description" content="A small React application, ready to make your own." />
|
|
7
|
+
<title>React workspace</title>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div id="root"></div>
|
|
11
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@icebreakers/react-vite-template",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.0.0",
|
|
5
|
+
"private": true,
|
|
6
|
+
"description": "Internal React, Vite and TypeScript application template shipped with repoctl",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": ">=22.13.0"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"dev": "vite",
|
|
13
|
+
"build": "vite build",
|
|
14
|
+
"preview": "vite preview",
|
|
15
|
+
"lint": "eslint . && stylelint \"src/**/*.css\"",
|
|
16
|
+
"lint:fix": "eslint . --fix && stylelint \"src/**/*.css\" --fix",
|
|
17
|
+
"typecheck": "tsc -b",
|
|
18
|
+
"test": "vitest run",
|
|
19
|
+
"test:dev": "vitest"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"react": "^19.3.0",
|
|
23
|
+
"react-dom": "^19.3.0"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"@testing-library/dom": "^10.4.2",
|
|
27
|
+
"@testing-library/react": "^16.3.3",
|
|
28
|
+
"@types/react": "^19.3.0",
|
|
29
|
+
"@types/react-dom": "^19.3.0",
|
|
30
|
+
"@vitejs/plugin-react": "^6.1.1",
|
|
31
|
+
"jsdom": "^30.1.1",
|
|
32
|
+
"typescript": "~6.0.3",
|
|
33
|
+
"vite": "^8.3.2",
|
|
34
|
+
"vitest": "~5.0.3"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
|
|
3
|
+
export default function App() {
|
|
4
|
+
const [count, setCount] = useState(0)
|
|
5
|
+
|
|
6
|
+
return (
|
|
7
|
+
<main>
|
|
8
|
+
<p className="eyebrow">Your next idea starts here</p>
|
|
9
|
+
<h1>Make something useful.</h1>
|
|
10
|
+
<p className="intro">A small React workspace with room to grow.</p>
|
|
11
|
+
<section className="counter" aria-labelledby="counter-title">
|
|
12
|
+
<h2 id="counter-title">Try an interaction</h2>
|
|
13
|
+
<output aria-label="Count" aria-live="polite">{count}</output>
|
|
14
|
+
<div className="actions">
|
|
15
|
+
<button type="button" onClick={() => setCount(value => value + 1)}>Add one</button>
|
|
16
|
+
<button type="button" className="secondary" disabled={count === 0} onClick={() => setCount(0)}>Reset</button>
|
|
17
|
+
</div>
|
|
18
|
+
</section>
|
|
19
|
+
<p className="footnote">Built with React, TypeScript, and Vite.</p>
|
|
20
|
+
</main>
|
|
21
|
+
)
|
|
22
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { StrictMode } from 'react'
|
|
2
|
+
import { createRoot } from 'react-dom/client'
|
|
3
|
+
import App from './app'
|
|
4
|
+
import './style.css'
|
|
5
|
+
|
|
6
|
+
const container = document.getElementById('root')
|
|
7
|
+
if (!container) {
|
|
8
|
+
throw new Error('The application root element is missing.')
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
createRoot(container).render(
|
|
12
|
+
<StrictMode>
|
|
13
|
+
<App />
|
|
14
|
+
</StrictMode>,
|
|
15
|
+
)
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
font-family: system-ui, sans-serif;
|
|
3
|
+
font-synthesis: none;
|
|
4
|
+
color: #203834;
|
|
5
|
+
background: #f5f4ee;
|
|
6
|
+
text-rendering: optimizelegibility;
|
|
7
|
+
-webkit-font-smoothing: antialiased;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
* {
|
|
11
|
+
box-sizing: border-box;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
body {
|
|
15
|
+
min-width: 320px;
|
|
16
|
+
margin: 0;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
main {
|
|
20
|
+
max-width: 760px;
|
|
21
|
+
padding: clamp(32px, 10vw, 96px) 24px;
|
|
22
|
+
margin: 0 auto;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.eyebrow,
|
|
26
|
+
.footnote {
|
|
27
|
+
font-size: 14px;
|
|
28
|
+
color: #53655f;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
h1 {
|
|
32
|
+
max-width: 600px;
|
|
33
|
+
margin: 24px 0;
|
|
34
|
+
font-size: clamp(40px, 8vw, 72px);
|
|
35
|
+
line-height: 1.05;
|
|
36
|
+
letter-spacing: -0.045em;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.intro {
|
|
40
|
+
font-size: 18px;
|
|
41
|
+
line-height: 1.6;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.counter {
|
|
45
|
+
padding: 28px;
|
|
46
|
+
margin: 40px 0 28px;
|
|
47
|
+
background: #fffefa;
|
|
48
|
+
border: 1px solid #c9d3ca;
|
|
49
|
+
border-radius: 12px;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
h2 {
|
|
53
|
+
margin: 0;
|
|
54
|
+
font-size: 18px;
|
|
55
|
+
font-weight: 500;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
output {
|
|
59
|
+
display: block;
|
|
60
|
+
margin: 16px 0;
|
|
61
|
+
font-size: 64px;
|
|
62
|
+
font-variant-numeric: tabular-nums;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.actions {
|
|
66
|
+
display: flex;
|
|
67
|
+
flex-wrap: wrap;
|
|
68
|
+
gap: 12px;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
button {
|
|
72
|
+
min-height: 44px;
|
|
73
|
+
padding: 10px 20px;
|
|
74
|
+
font: inherit;
|
|
75
|
+
color: #fff;
|
|
76
|
+
cursor: pointer;
|
|
77
|
+
background: #245e50;
|
|
78
|
+
border: 1px solid #245e50;
|
|
79
|
+
border-radius: 6px;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
button.secondary {
|
|
83
|
+
color: #245e50;
|
|
84
|
+
background: transparent;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
button:focus-visible {
|
|
88
|
+
outline: 3px solid #ba5a20;
|
|
89
|
+
outline-offset: 4px;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
button:disabled {
|
|
93
|
+
cursor: default;
|
|
94
|
+
opacity: 0.5;
|
|
95
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { cleanup, fireEvent, render, screen } from '@testing-library/react'
|
|
2
|
+
import { afterEach, expect, it } from 'vitest'
|
|
3
|
+
import App from '../src/app'
|
|
4
|
+
|
|
5
|
+
afterEach(cleanup)
|
|
6
|
+
|
|
7
|
+
it('increments the count and resets it to zero', () => {
|
|
8
|
+
render(<App />)
|
|
9
|
+
const count = screen.getByLabelText('Count')
|
|
10
|
+
const reset = screen.getByRole('button', { name: 'Reset' })
|
|
11
|
+
expect(count.textContent).toBe('0')
|
|
12
|
+
expect(reset.hasAttribute('disabled')).toBe(true)
|
|
13
|
+
|
|
14
|
+
fireEvent.click(screen.getByRole('button', { name: 'Add one' }))
|
|
15
|
+
fireEvent.click(screen.getByRole('button', { name: 'Add one' }))
|
|
16
|
+
expect(count.textContent).toBe('2')
|
|
17
|
+
expect(reset.hasAttribute('disabled')).toBe(false)
|
|
18
|
+
|
|
19
|
+
fireEvent.click(reset)
|
|
20
|
+
expect(count.textContent).toBe('0')
|
|
21
|
+
expect(reset.hasAttribute('disabled')).toBe(true)
|
|
22
|
+
})
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
|
5
|
+
"target": "ES2022",
|
|
6
|
+
"jsx": "react-jsx",
|
|
7
|
+
"lib": [
|
|
8
|
+
"ES2022",
|
|
9
|
+
"DOM",
|
|
10
|
+
"DOM.Iterable"
|
|
11
|
+
],
|
|
12
|
+
"types": [
|
|
13
|
+
"vite/client"
|
|
14
|
+
],
|
|
15
|
+
"noFallthroughCasesInSwitch": true,
|
|
16
|
+
"noUnusedLocals": true,
|
|
17
|
+
"noUnusedParameters": true,
|
|
18
|
+
"verbatimModuleSyntax": true,
|
|
19
|
+
"erasableSyntaxOnly": true,
|
|
20
|
+
"noUncheckedSideEffectImports": true
|
|
21
|
+
},
|
|
22
|
+
"include": [
|
|
23
|
+
"src",
|
|
24
|
+
"test"
|
|
25
|
+
]
|
|
26
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
|
5
|
+
"target": "ES2023",
|
|
6
|
+
"lib": [
|
|
7
|
+
"ES2023"
|
|
8
|
+
],
|
|
9
|
+
"types": [
|
|
10
|
+
"node"
|
|
11
|
+
],
|
|
12
|
+
"verbatimModuleSyntax": true
|
|
13
|
+
},
|
|
14
|
+
"include": [
|
|
15
|
+
"vite.config.ts",
|
|
16
|
+
"vitest.config.ts"
|
|
17
|
+
]
|
|
18
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import react from '@vitejs/plugin-react'
|
|
2
|
+
import { defineVitestProjectConfig } from 'repoctl/tooling'
|
|
3
|
+
import { mergeConfig } from 'vitest/config'
|
|
4
|
+
|
|
5
|
+
export default mergeConfig(await defineVitestProjectConfig({
|
|
6
|
+
options: { environment: 'jsdom' },
|
|
7
|
+
}), {
|
|
8
|
+
plugins: [react()],
|
|
9
|
+
})
|
|
@@ -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
|
+
}
|
|
@@ -52,7 +52,14 @@ export const routeSections = [
|
|
|
52
52
|
items: [
|
|
53
53
|
item('', 'Reference overview', '参考总览'),
|
|
54
54
|
item('commands', 'Command reference', '命令参考'),
|
|
55
|
+
item('devcontainer', 'Dev Container preset', 'Dev Container 预设'),
|
|
55
56
|
item('config', 'Configuration', '配置文件'),
|
|
57
|
+
item('organization-presets', 'Organization presets', '组织工程预设'),
|
|
58
|
+
item('dependencies', 'Dependency consistency', '依赖版本一致性'),
|
|
59
|
+
item('knip', 'Unused code and dependencies', '闲置代码与依赖检查'),
|
|
60
|
+
item('artifacts', 'Build and production artifacts', '构建与生产产物'),
|
|
61
|
+
item('move', 'Move or rename a workspace', '移动或重命名工作区'),
|
|
62
|
+
item('removal', 'Remove a workspace safely', '安全移除工作区'),
|
|
56
63
|
item('execution-model', 'Execution model', '执行模型'),
|
|
57
64
|
item('output', 'Output formats', '输出格式'),
|
|
58
65
|
item('aliases', 'Command aliases', '命令别名'),
|
|
@@ -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
|
}
|