@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
|
@@ -73,6 +73,14 @@ const templateChoices = [
|
|
|
73
73
|
category: "app",
|
|
74
74
|
description: "Vue 3 + Hono 的前后端一体应用模板"
|
|
75
75
|
},
|
|
76
|
+
{
|
|
77
|
+
key: "react-vite",
|
|
78
|
+
label: "React Vite 应用",
|
|
79
|
+
source: "react-vite",
|
|
80
|
+
target: "apps/react-vite",
|
|
81
|
+
category: "app",
|
|
82
|
+
description: "React + Vite + TypeScript 单页应用,包含交互测试与共享工程规范"
|
|
83
|
+
},
|
|
76
84
|
{
|
|
77
85
|
key: "hono-server",
|
|
78
86
|
label: "hono 模板",
|
|
@@ -96,6 +104,30 @@ const templateChoices = [
|
|
|
96
104
|
target: "apps/cli",
|
|
97
105
|
category: "tool",
|
|
98
106
|
description: "TypeScript 命令行工具模板"
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
key: "nimbus",
|
|
110
|
+
label: "Nimbus 文档(默认)",
|
|
111
|
+
source: "nimbus",
|
|
112
|
+
target: "apps/docs",
|
|
113
|
+
category: "docs",
|
|
114
|
+
description: "默认文档模板:Nimbus + Astro,中英双语与 AI 文档入口"
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
key: "react-lib",
|
|
118
|
+
label: "React 组件库",
|
|
119
|
+
source: "react-lib",
|
|
120
|
+
target: "packages/react-lib",
|
|
121
|
+
category: "library",
|
|
122
|
+
description: "带类型声明、显式 CSS 出口和发布消费验证的 React 组件库"
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
key: "next",
|
|
126
|
+
label: "Next.js App Router 应用",
|
|
127
|
+
source: "next",
|
|
128
|
+
target: "apps/next",
|
|
129
|
+
category: "app",
|
|
130
|
+
description: "Next.js + TypeScript SSR 应用,包含客户端交互、健康接口与工作区库集成"
|
|
99
131
|
}
|
|
100
132
|
];
|
|
101
133
|
//#endregion
|
|
@@ -144,13 +176,17 @@ const skipDirs = /* @__PURE__ */ new Set([
|
|
|
144
176
|
".vite",
|
|
145
177
|
".tmp",
|
|
146
178
|
".vue-global-types",
|
|
147
|
-
".wrangler"
|
|
179
|
+
".wrangler",
|
|
180
|
+
".astro",
|
|
181
|
+
".nimbus",
|
|
182
|
+
".next"
|
|
148
183
|
]);
|
|
149
184
|
const skipFiles = /* @__PURE__ */ new Set([
|
|
150
185
|
".DS_Store",
|
|
151
186
|
"CHANGELOG.md",
|
|
152
187
|
"route-map.d.ts",
|
|
153
|
-
"worker-configuration.d.ts"
|
|
188
|
+
"worker-configuration.d.ts",
|
|
189
|
+
"next-env.d.ts"
|
|
154
190
|
]);
|
|
155
191
|
function shouldSkipTemplatePath(rootDir, targetPath) {
|
|
156
192
|
const basename = path.basename(targetPath);
|
|
@@ -169,12 +205,33 @@ function createTemplateCopyFilter(rootDir) {
|
|
|
169
205
|
}
|
|
170
206
|
//#endregion
|
|
171
207
|
//#region src/prepare/published.ts
|
|
208
|
+
const sourceScriptNames = /* @__PURE__ */ new Set([
|
|
209
|
+
"tooling:build",
|
|
210
|
+
"check:no-tracked-build-artifacts",
|
|
211
|
+
"check:workflows",
|
|
212
|
+
"test:dev-scenarios",
|
|
213
|
+
"test:worker-types"
|
|
214
|
+
]);
|
|
215
|
+
const isSourceOnlyScript = (name) => name.startsWith("dev:") || sourceScriptNames.has(name) || name.startsWith("test:packaged-");
|
|
172
216
|
function sanitizePublishedManifestContent(content) {
|
|
173
217
|
const manifest = JSON.parse(content);
|
|
218
|
+
if (manifest.devDependencies) {
|
|
219
|
+
for (const name of [
|
|
220
|
+
"@icebreakers/commitlint-config",
|
|
221
|
+
"@icebreakers/eslint-config",
|
|
222
|
+
"@icebreakers/monorepo",
|
|
223
|
+
"@icebreakers/stylelint-config",
|
|
224
|
+
"make-fetch-happen",
|
|
225
|
+
"playwright"
|
|
226
|
+
]) delete manifest.devDependencies[name];
|
|
227
|
+
if (manifest.devDependencies["repoctl"]?.startsWith("workspace:")) manifest.devDependencies["repoctl"] = "latest";
|
|
228
|
+
}
|
|
174
229
|
if (manifest.scripts) {
|
|
175
|
-
for (const name of Object.keys(manifest.scripts)) if (
|
|
230
|
+
for (const name of Object.keys(manifest.scripts)) if (isSourceOnlyScript(name)) delete manifest.scripts[name];
|
|
176
231
|
manifest.scripts["dev"] = "turbo run dev --concurrency=20";
|
|
232
|
+
if (manifest.scripts["lint"]) manifest.scripts["lint"] = manifest.scripts["lint"].replace(/^pnpm run tooling:build && /u, "");
|
|
177
233
|
if (manifest.scripts["test"]) manifest.scripts["test"] = manifest.scripts["test"].replace(" && pnpm test:dev-scenarios", "");
|
|
234
|
+
for (const name of ["test", "test:dev"]) if (manifest.scripts[name]) manifest.scripts[name] = manifest.scripts[name].replace("pnpm check:no-tracked-build-artifacts && ", "");
|
|
178
235
|
}
|
|
179
236
|
return `${JSON.stringify(manifest, null, 2)}\n`;
|
|
180
237
|
}
|
|
@@ -236,6 +293,40 @@ function sanitizePublishedWorkspaceContent(content) {
|
|
|
236
293
|
return changed ? YAML.stringify(workspace) : content;
|
|
237
294
|
}
|
|
238
295
|
//#endregion
|
|
296
|
+
//#region src/prepare/workflows.ts
|
|
297
|
+
function sourceCommand(command) {
|
|
298
|
+
if (/[;&|`$<>]/u.test(command)) return false;
|
|
299
|
+
const script = /^pnpm\s+(?:--[\w-]+(?:=[\w./:-]+)?\s+)*(?:run\s+(?:--[\w-]+(?:=[\w./:-]+)?\s+)*)?([\w:-]+)(?:\s|$)/u.exec(command)?.[1];
|
|
300
|
+
return script !== void 0 && isSourceOnlyScript(script) || /^pnpm\s+exec\s+playwright\s+install(?:\s|$)/u.test(command);
|
|
301
|
+
}
|
|
302
|
+
function mentionsSourceCommand(command) {
|
|
303
|
+
return command.split(/[^\w:-]+/u).some(isSourceOnlyScript) || /\bplaywright\s+install\b/u.test(command);
|
|
304
|
+
}
|
|
305
|
+
/** Filter YAML steps, so neighboring steps and additional fields cannot escape publication rules. */
|
|
306
|
+
function sanitizePublishedCiWorkflowContent(content) {
|
|
307
|
+
const document = YAML.parseDocument(content);
|
|
308
|
+
if (document.errors.length) throw new Error("Cannot publish an invalid CI workflow.");
|
|
309
|
+
const jobs = document.get("jobs", true);
|
|
310
|
+
if (!YAML.isMap(jobs)) return content;
|
|
311
|
+
let changed = false;
|
|
312
|
+
for (const { value: job } of jobs.items) {
|
|
313
|
+
if (!YAML.isMap(job)) continue;
|
|
314
|
+
const steps = job.get("steps", true);
|
|
315
|
+
if (!YAML.isSeq(steps)) continue;
|
|
316
|
+
for (let index = steps.items.length - 1; index >= 0; index--) {
|
|
317
|
+
const step = steps.items[index];
|
|
318
|
+
const run = YAML.isMap(step) ? step.get("run") : void 0;
|
|
319
|
+
if (typeof run !== "string") continue;
|
|
320
|
+
const commands = run.split(/\r?\n/u).map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
|
|
321
|
+
if (!commands.some(mentionsSourceCommand)) continue;
|
|
322
|
+
if (!commands.every(sourceCommand)) throw new Error("Move source-only commands into separate CI steps before publishing templates.");
|
|
323
|
+
steps.delete(index);
|
|
324
|
+
changed = true;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return changed ? document.toString({ lineWidth: 0 }) : content;
|
|
328
|
+
}
|
|
329
|
+
//#endregion
|
|
239
330
|
//#region src/prepare/index.ts
|
|
240
331
|
const huskySkippedEntryPattern = /[\\/]_$/;
|
|
241
332
|
function toPublishedAssetPath(target) {
|
|
@@ -357,10 +448,11 @@ async function removeSourceRepoReleaseToolingBuildStep() {
|
|
|
357
448
|
if (nextContent !== content) await fs.writeFile(releaseWorkflowPath, nextContent, "utf8");
|
|
358
449
|
}
|
|
359
450
|
async function removeSourceRepoChecks() {
|
|
451
|
+
await fs.rm(path$1.join(assetsDir, ".github/workflows/devcontainer.yml"), { force: true });
|
|
360
452
|
const workflowPath = path$1.join(assetsDir, ".github/workflows/ci.yml");
|
|
361
453
|
if (await pathExists(workflowPath)) {
|
|
362
454
|
const workflow = await fs.readFile(workflowPath, "utf8");
|
|
363
|
-
await fs.writeFile(workflowPath, workflow
|
|
455
|
+
await fs.writeFile(workflowPath, sanitizePublishedCiWorkflowContent(workflow));
|
|
364
456
|
}
|
|
365
457
|
const manifestPath = path$1.join(assetsDir, "package.json");
|
|
366
458
|
if (await pathExists(manifestPath)) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@icebreakers/monorepo-templates",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "2.0
|
|
4
|
+
"version": "2.2.0",
|
|
5
5
|
"description": "Built-in project templates and managed workspace assets for repoctl",
|
|
6
6
|
"author": "ice breaker <hi@sonofmagic.top>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@inquirer/checkbox": "^5.2.5",
|
|
50
50
|
"@inquirer/input": "^5.1.6",
|
|
51
|
+
"@inquirer/password": "^5.2.2",
|
|
51
52
|
"@inquirer/select": "^5.2.5",
|
|
52
53
|
"commander": "^15.0.0",
|
|
53
54
|
"nanoexec": "^1.1.0",
|
package/template-data.mjs
CHANGED
|
@@ -23,6 +23,14 @@ export const templateChoices = [
|
|
|
23
23
|
category: 'app',
|
|
24
24
|
description: 'Vue 3 + Hono 的前后端一体应用模板',
|
|
25
25
|
},
|
|
26
|
+
{
|
|
27
|
+
key: 'react-vite',
|
|
28
|
+
label: 'React Vite 应用',
|
|
29
|
+
source: 'react-vite',
|
|
30
|
+
target: 'apps/react-vite',
|
|
31
|
+
category: 'app',
|
|
32
|
+
description: 'React + Vite + TypeScript 单页应用,包含交互测试与共享工程规范',
|
|
33
|
+
},
|
|
26
34
|
{
|
|
27
35
|
key: 'hono-server',
|
|
28
36
|
label: 'hono 模板',
|
|
@@ -47,4 +55,28 @@ export const templateChoices = [
|
|
|
47
55
|
category: 'tool',
|
|
48
56
|
description: 'TypeScript 命令行工具模板',
|
|
49
57
|
},
|
|
58
|
+
{
|
|
59
|
+
key: 'nimbus',
|
|
60
|
+
label: 'Nimbus 文档(默认)',
|
|
61
|
+
source: 'nimbus',
|
|
62
|
+
target: 'apps/docs',
|
|
63
|
+
category: 'docs',
|
|
64
|
+
description: '默认文档模板:Nimbus + Astro,中英双语与 AI 文档入口',
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
key: 'react-lib',
|
|
68
|
+
label: 'React 组件库',
|
|
69
|
+
source: 'react-lib',
|
|
70
|
+
target: 'packages/react-lib',
|
|
71
|
+
category: 'library',
|
|
72
|
+
description: '带类型声明、显式 CSS 出口和发布消费验证的 React 组件库',
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
key: 'next',
|
|
76
|
+
label: 'Next.js App Router 应用',
|
|
77
|
+
source: 'next',
|
|
78
|
+
target: 'apps/next',
|
|
79
|
+
category: 'app',
|
|
80
|
+
description: 'Next.js + TypeScript SSR 应用,包含客户端交互、健康接口与工作区库集成',
|
|
81
|
+
},
|
|
50
82
|
]
|
|
@@ -60,10 +60,10 @@
|
|
|
60
60
|
"@vitejs/plugin-vue-jsx": "^5.1.6",
|
|
61
61
|
"@vue/tsconfig": "^0.9.1",
|
|
62
62
|
"consola": "^3.4.2",
|
|
63
|
-
"hono": "^4.13.
|
|
63
|
+
"hono": "^4.13.12",
|
|
64
64
|
"tailwindcss": "^4.3.3",
|
|
65
65
|
"typescript": "~6.0.3",
|
|
66
|
-
"vite": "^8.3.
|
|
66
|
+
"vite": "^8.3.2",
|
|
67
67
|
"vite-plugin-vue-devtools": "^8.2.1",
|
|
68
68
|
"vite-tsconfig-paths": "^6.1.1",
|
|
69
69
|
"vue-tsc": "3.3.11",
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Next.js App Router workspace
|
|
2
|
+
|
|
3
|
+
This TypeScript template follows `create-next-app@16.3.8`'s App Router layout and uses the shared repoctl tooling. It includes a server page, a client counter and `GET /api/health`. It requires Node 22.13 or newer and shares the workspace's pnpm version and lockfile.
|
|
4
|
+
|
|
5
|
+
Run these from the generated workspace root:
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
corepack enable
|
|
9
|
+
pnpm install
|
|
10
|
+
pnpm build
|
|
11
|
+
pnpm lint
|
|
12
|
+
pnpm typecheck
|
|
13
|
+
pnpm test
|
|
14
|
+
pnpm --dir apps/next start
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
For development, run `pnpm --dir apps/next dev`. When created with `repo new portal --template next`, use `apps/portal` instead. Lint runs ESLint and Stylelint independently of Next build. `typecheck` runs `next typegen` before `tsc --noEmit`, so route types exist even before the first production build. Unit tests cover client state; the repoctl source repository also tests the built server and browser using actual published tarballs.
|
|
18
|
+
|
|
19
|
+
## Internal packages
|
|
20
|
+
|
|
21
|
+
For a compiled workspace library, add `"@acme/shared": "workspace:*"` to dependencies and import its public package name. The library should expose its built JavaScript and declaration files through `exports`. Turbo's inherited `^build` builds the library first; do not reach into another package's source via relative imports.
|
|
22
|
+
|
|
23
|
+
To consume an intentionally source-exporting TypeScript package, add its package name to `transpilePackages` in `next.config.ts`. Its exports and server/client compatibility must support the target environment. `transpilePackages` does not generate that library's standalone declaration files, and server-only code must not enter a client component. The packaged acceptance test covers both compiled and source-exporting internal packages.
|
|
24
|
+
|
|
25
|
+
## Cache and runtime boundaries
|
|
26
|
+
|
|
27
|
+
The local `turbo.json` includes all authored app inputs and caches `.next/**` except `.next/cache/**`. Development and production server tasks are persistent and uncached; type generation is uncached to keep Next's generated route declarations available. `.next`, `next-env.d.ts`, and TypeScript build info are generated locally and never shipped as template inputs.
|
|
28
|
+
|
|
29
|
+
No external font download is needed to build this starter. Configure environment values and production hosting for your application separately; no authentication, database or deployment platform is included.
|
|
30
|
+
|
|
31
|
+
References: [create-next-app](https://nextjs.org/docs/app/api-reference/cli/create-next-app), [Next CLI and typegen](https://nextjs.org/docs/app/api-reference/cli/next), [transpilePackages](https://nextjs.org/docs/app/api-reference/config/next-config-js/transpilePackages).
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@icebreakers/next-template",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.0.0",
|
|
5
|
+
"private": true,
|
|
6
|
+
"description": "Internal Next.js App Router and TypeScript application template shipped with repoctl",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": ">=22.13.0"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"dev": "next dev",
|
|
13
|
+
"build": "next build",
|
|
14
|
+
"start": "next start",
|
|
15
|
+
"lint": "eslint . && stylelint \"src/**/*.css\"",
|
|
16
|
+
"lint:fix": "eslint . --fix && stylelint \"src/**/*.css\" --fix",
|
|
17
|
+
"typecheck": "next typegen && tsc --noEmit",
|
|
18
|
+
"test": "vitest run",
|
|
19
|
+
"test:dev": "vitest"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"next": "16.3.8",
|
|
23
|
+
"react": "^19.3.0",
|
|
24
|
+
"react-dom": "^19.3.0"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@testing-library/dom": "^10.4.2",
|
|
28
|
+
"@testing-library/react": "^16.3.3",
|
|
29
|
+
"@types/react": "^19.3.0",
|
|
30
|
+
"@types/react-dom": "^19.3.0",
|
|
31
|
+
"@vitejs/plugin-react": "^6.1.1",
|
|
32
|
+
"jsdom": "^30.1.1",
|
|
33
|
+
"typescript": "~6.0.3",
|
|
34
|
+
"vitest": "~5.0.3"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useState } from 'react'
|
|
4
|
+
|
|
5
|
+
export default function Counter() {
|
|
6
|
+
const [count, setCount] = useState(0)
|
|
7
|
+
return (
|
|
8
|
+
<section className="counter" aria-label="Client interaction">
|
|
9
|
+
<div>
|
|
10
|
+
<p className="eyebrow">Client state</p>
|
|
11
|
+
<output aria-label="Count" aria-live="polite">{count}</output>
|
|
12
|
+
</div>
|
|
13
|
+
<div className="actions">
|
|
14
|
+
<button type="button" onClick={() => setCount(value => value + 1)}>Add one</button>
|
|
15
|
+
<button type="button" className="secondary" disabled={count === 0} onClick={() => setCount(0)}>Reset</button>
|
|
16
|
+
</div>
|
|
17
|
+
</section>
|
|
18
|
+
)
|
|
19
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
@charset "UTF-8";
|
|
2
|
+
|
|
3
|
+
:root {
|
|
4
|
+
font-family: Arial, Helvetica, sans-serif;
|
|
5
|
+
font-synthesis: none;
|
|
6
|
+
color: #1b263b;
|
|
7
|
+
background: #f5f3ee;
|
|
8
|
+
text-rendering: optimizelegibility;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
* {
|
|
12
|
+
box-sizing: border-box;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
body {
|
|
16
|
+
margin: 0;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
main {
|
|
20
|
+
width: min(100% - 3rem, 48rem);
|
|
21
|
+
margin: 12vh auto;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.eyebrow {
|
|
25
|
+
margin: 0 0 1.5rem;
|
|
26
|
+
font-size: 0.75rem;
|
|
27
|
+
font-weight: 700;
|
|
28
|
+
text-transform: uppercase;
|
|
29
|
+
letter-spacing: 0.12em;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
h1 {
|
|
33
|
+
margin: 0 0 1.5rem;
|
|
34
|
+
font-size: clamp(2.5rem, 7vw, 4.5rem);
|
|
35
|
+
line-height: 1.05;
|
|
36
|
+
letter-spacing: -0.05em;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.intro {
|
|
40
|
+
max-width: 35rem;
|
|
41
|
+
font-size: 1.125rem;
|
|
42
|
+
line-height: 1.7;
|
|
43
|
+
color: #536073;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.counter {
|
|
47
|
+
display: flex;
|
|
48
|
+
flex-wrap: wrap;
|
|
49
|
+
gap: 2rem;
|
|
50
|
+
align-items: center;
|
|
51
|
+
justify-content: space-between;
|
|
52
|
+
padding: 2rem 0;
|
|
53
|
+
margin: 3rem 0;
|
|
54
|
+
border-block: 1px solid #c7cbd1;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.counter .eyebrow {
|
|
58
|
+
margin-bottom: 0.75rem;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
output {
|
|
62
|
+
font-size: 3rem;
|
|
63
|
+
font-variant-numeric: tabular-nums;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.actions {
|
|
67
|
+
display: flex;
|
|
68
|
+
flex-wrap: wrap;
|
|
69
|
+
gap: 0.75rem;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
button {
|
|
73
|
+
min-height: 2.75rem;
|
|
74
|
+
padding: 0.75rem 1.125rem;
|
|
75
|
+
font: inherit;
|
|
76
|
+
color: #fff;
|
|
77
|
+
cursor: pointer;
|
|
78
|
+
background: #1b263b;
|
|
79
|
+
border: 1px solid #1b263b;
|
|
80
|
+
border-radius: 0.25rem;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.secondary {
|
|
84
|
+
color: #1b263b;
|
|
85
|
+
background: transparent;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
button:disabled {
|
|
89
|
+
cursor: default;
|
|
90
|
+
opacity: 0.45;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
button:focus-visible,
|
|
94
|
+
a:focus-visible {
|
|
95
|
+
outline: 3px solid #b95732;
|
|
96
|
+
outline-offset: 4px;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
footer {
|
|
100
|
+
display: flex;
|
|
101
|
+
flex-wrap: wrap;
|
|
102
|
+
gap: 1rem 2rem;
|
|
103
|
+
font-size: 0.875rem;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
a {
|
|
107
|
+
color: inherit;
|
|
108
|
+
text-underline-offset: 0.25em;
|
|
109
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/* eslint react-refresh/only-export-components: ["error", { "allowExportNames": ["metadata"] }] -- Next.js reads metadata from route layouts. */
|
|
2
|
+
import type { Metadata } from 'next'
|
|
3
|
+
import type { ReactNode } from 'react'
|
|
4
|
+
import './globals.css'
|
|
5
|
+
|
|
6
|
+
export const metadata: Metadata = {
|
|
7
|
+
title: 'Next.js workspace',
|
|
8
|
+
description: 'A Next.js App Router application in your pnpm workspace.',
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default function RootLayout({ children }: { children: ReactNode }) {
|
|
12
|
+
return (
|
|
13
|
+
<html lang="en">
|
|
14
|
+
<body>{children}</body>
|
|
15
|
+
</html>
|
|
16
|
+
)
|
|
17
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import Counter from './counter'
|
|
2
|
+
|
|
3
|
+
export default function HomePage() {
|
|
4
|
+
return (
|
|
5
|
+
<main>
|
|
6
|
+
<p className="eyebrow">repoctl / Next.js</p>
|
|
7
|
+
<h1>Next.js workspace</h1>
|
|
8
|
+
<p className="intro">A server-rendered page with a small client component. Ready for your next feature.</p>
|
|
9
|
+
<Counter />
|
|
10
|
+
<footer>
|
|
11
|
+
<a href="/api/health">Health endpoint</a>
|
|
12
|
+
<a href="https://nextjs.org/docs/app">App Router documentation</a>
|
|
13
|
+
</footer>
|
|
14
|
+
</main>
|
|
15
|
+
)
|
|
16
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { cleanup, fireEvent, render, screen } from '@testing-library/react'
|
|
2
|
+
import { afterEach, expect, it } from 'vitest'
|
|
3
|
+
import Counter from '../src/app/counter'
|
|
4
|
+
|
|
5
|
+
afterEach(cleanup)
|
|
6
|
+
|
|
7
|
+
it('increments the count and resets it to zero', () => {
|
|
8
|
+
render(<Counter />)
|
|
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,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"incremental": true,
|
|
5
|
+
"tsBuildInfoFile": "./node_modules/.tmp/next.tsbuildinfo",
|
|
6
|
+
"target": "ES2022",
|
|
7
|
+
"jsx": "react-jsx",
|
|
8
|
+
"lib": [
|
|
9
|
+
"DOM",
|
|
10
|
+
"DOM.Iterable",
|
|
11
|
+
"ESNext"
|
|
12
|
+
],
|
|
13
|
+
"paths": {
|
|
14
|
+
"@/*": [
|
|
15
|
+
"./src/*"
|
|
16
|
+
]
|
|
17
|
+
},
|
|
18
|
+
"types": [
|
|
19
|
+
"node"
|
|
20
|
+
],
|
|
21
|
+
"isolatedModules": true,
|
|
22
|
+
"plugins": [
|
|
23
|
+
{
|
|
24
|
+
"name": "next"
|
|
25
|
+
}
|
|
26
|
+
]
|
|
27
|
+
},
|
|
28
|
+
"include": [
|
|
29
|
+
"next-env.d.ts",
|
|
30
|
+
"src/**/*.ts",
|
|
31
|
+
"src/**/*.tsx",
|
|
32
|
+
"test/**/*.tsx",
|
|
33
|
+
"next.config.ts",
|
|
34
|
+
".next/types/**/*.ts",
|
|
35
|
+
".next/dev/types/**/*.ts"
|
|
36
|
+
],
|
|
37
|
+
"exclude": [
|
|
38
|
+
"node_modules"
|
|
39
|
+
]
|
|
40
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": [
|
|
3
|
+
"//"
|
|
4
|
+
],
|
|
5
|
+
"tasks": {
|
|
6
|
+
"build": {
|
|
7
|
+
"inputs": [
|
|
8
|
+
"$TURBO_DEFAULT$",
|
|
9
|
+
".env*"
|
|
10
|
+
],
|
|
11
|
+
"outputs": [
|
|
12
|
+
".next/**",
|
|
13
|
+
"!.next/cache/**"
|
|
14
|
+
]
|
|
15
|
+
},
|
|
16
|
+
"typecheck": {
|
|
17
|
+
"cache": false
|
|
18
|
+
},
|
|
19
|
+
"start": {
|
|
20
|
+
"cache": false,
|
|
21
|
+
"persistent": true
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -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
|
+
})
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Documentation workspace
|
|
2
|
+
|
|
3
|
+
This is a static Nimbus + Astro documentation site. Run commands in this workspace; install dependencies from the monorepo root after `corepack enable`.
|
|
4
|
+
|
|
5
|
+
- Content lives in `src/content/docs/`; English uses the root and Chinese uses `zh/` with matching relative filenames. Keep both translations and links in sync. The directory tree supplies routes and navigation.
|
|
6
|
+
- Layouts and components are project-owned. Keep modules small. Put styles in `src/styles/` so Stylelint checks every authored style.
|
|
7
|
+
- Register MDX components in `src/components.ts`. Add a text conversion in `astro.config.ts` when a custom component conveys information that would otherwise disappear from Markdown.
|
|
8
|
+
- Set the canonical `site` URL and project title in `astro.config.ts` before deployment. Static output is `dist/` and can be hosted anywhere.
|
|
9
|
+
- Run `pnpm build`, `pnpm lint`, and `pnpm typecheck`. `typecheck` combines Astro diagnostics with TypeScript checks. Search uses the production Pagefind index; verify it with `pnpm preview` after building.
|
|
10
|
+
- Verify `/llms.txt`, `/zh/llms.txt`, `/llms-full.txt`, and each page's `/index.md` and `/index.mdx`. Keep private content outside public content collections; `noindex` is not access control.
|
|
11
|
+
- `draft: true` excludes unfinished pages from production. When adding a page, create its translation before publishing so the language switch remains valid.
|
|
12
|
+
- Keep `nimbus.json` and `UPSTREAM.md` when upgrading. Review Nimbus migrations and project-owned changes before advancing the reviewed version.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License Copyright (c) 2025 Cloudflare, Inc.
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of
|
|
4
|
+
charge, to any person obtaining a copy of this software and associated
|
|
5
|
+
documentation files (the "Software"), to deal in the Software without
|
|
6
|
+
restriction, including without limitation the rights to use, copy, modify, merge,
|
|
7
|
+
publish, distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to the
|
|
9
|
+
following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice
|
|
12
|
+
(including the next paragraph) shall be included in all copies or substantial
|
|
13
|
+
portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
|
16
|
+
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
17
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
|
|
18
|
+
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
19
|
+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
20
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Project docs / 项目文档
|
|
2
|
+
|
|
3
|
+
A small bilingual Nimbus + Astro documentation site. 英文位于 `/`,中文位于 `/zh/`。
|
|
4
|
+
|
|
5
|
+
From the monorepo root, run `corepack enable` and `pnpm install`. Then run these commands in this directory:
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
pnpm dev
|
|
9
|
+
pnpm build
|
|
10
|
+
pnpm preview
|
|
11
|
+
pnpm lint
|
|
12
|
+
pnpm typecheck
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Edit `src/content/docs/` and its `zh/` translations. Set your project title and canonical site URL in `astro.config.ts` before publishing `dist/` to a static host. Search is available after a production build.
|
|
16
|
+
|
|
17
|
+
机器可读入口包括 `/llms.txt`、`/zh/llms.txt`、`/llms-full.txt`,以及页面的 `/index.md` 和 `/index.mdx`。详见 `AGENTS.md` 和 `UPSTREAM.md`。
|