create-icebreaker 1.1.0 → 1.1.2

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 CHANGED
@@ -1,50 +1,18 @@
1
1
  # create-icebreaker
2
2
 
3
- Interactive bootstrapper for the icebreaker monorepo template. It scaffolds from the npm package and trims templates based on your selections.
3
+ English | [简体中文](README.zh-CN.md)
4
4
 
5
- ## Usage
5
+ Compatibility create command for repoctl-managed workspaces.
6
6
 
7
- - `pnpm create icebreaker`
8
- - `npm create icebreaker@latest`
7
+ Existing automation may continue to use:
9
8
 
10
- By default this:
11
-
12
- - uses npm templates by default (no GitHub needed)
13
- - asks for the project directory (defaults to `icebreaker-monorepo`)
14
- - asks which templates to keep (default keeps none)
15
- - updates the root `package.json` name
16
-
17
- ## Next Steps
18
-
19
- After scaffolding finishes:
20
-
21
- ```sh
22
- cd <your-project>
23
- pnpm install
24
- pnpm setup
25
- pnpm new my-package
26
- pnpm check
9
+ ```bash
10
+ npm create icebreaker@latest
11
+ pnpm create icebreaker
27
12
  ```
28
13
 
29
- Recommended maintenance flow inside the generated workspace:
30
-
31
- - `pnpm setup`: bootstrap recommended workspace metadata and tooling
32
- - `pnpm new`: create the next package or app through the guided flow
33
- - `pnpm check`: run recommended local verification
34
- - `pnpm dlx repo@latest clean --yes`: optional zero-install cleanup on a fresh clone
35
-
36
- ## Templates
37
-
38
- Available template keys:
39
-
40
- - `tsdown`
41
- - `vue-lib`
42
- - `vue-hono`
43
- - `hono-server`
44
- - `vitepress`
45
- - `cli`
14
+ New projects should prefer `npm create repoctl@latest` or `pnpm create repoctl`. Both entrypoints use the same maintained scaffold engine and lead into the `repo init`, `repo doctor`, `repo new`, and `repo check` workflow.
46
15
 
47
- ## Flags
16
+ Output is English by default. Pass `--lang zh-CN` or set `REPOCTL_LANG=zh-CN` for Simplified Chinese.
48
17
 
49
- - `--templates <list>`: comma-separated template keys or indexes to keep (e.g. `tsdown,vue-hono` or `1,4`)
50
- - `--force`: overwrite a non-empty target directory
18
+ Documentation: https://repo.icebreaker.top
@@ -0,0 +1,18 @@
1
+ # create-icebreaker
2
+
3
+ [English](README.md) | 简体中文
4
+
5
+ repoctl 管理工作区的兼容 create 命令。
6
+
7
+ 已有自动化可以继续使用:
8
+
9
+ ```bash
10
+ npm create icebreaker@latest
11
+ pnpm create icebreaker
12
+ ```
13
+
14
+ 新项目推荐使用 `npm create repoctl@latest` 或 `pnpm create repoctl`。两个入口共享同一套维护中的 scaffold engine,并进入 `repo init`、`repo doctor`、`repo new` 和 `repo check` 工作流。
15
+
16
+ 默认输出英文。传入 `--lang zh-CN` 或设置 `REPOCTL_LANG=zh-CN` 可切换为简体中文。
17
+
18
+ 文档:https://repo.icebreaker.top
package/dist/index.mjs CHANGED
@@ -1,178 +1,10 @@
1
- import path from "node:path";
2
1
  import process from "node:process";
3
- import { Command, checkbox, input, scaffoldWorkspace, templateChoices } from "@icebreakers/monorepo-templates";
4
- import fs from "node:fs/promises";
5
- //#region src/constants.ts
6
- const DEFAULT_TARGET = "icebreaker-monorepo";
7
- //#endregion
8
- //#region src/next-steps.ts
9
- function formatNextSteps(targetDir) {
10
- return [
11
- "",
12
- "All set! Next steps:",
13
- ` cd ${path.relative(process.cwd(), targetDir) || "."}`,
14
- " pnpm install",
15
- " pnpm setup",
16
- " pnpm new my-package",
17
- " pnpm check",
18
- ""
19
- ].join("\n");
20
- }
21
- //#endregion
22
- //#region src/package-json.ts
23
- const repoctlPackageName = "repoctl";
24
- const legacyToolPackageName = "@icebreakers/monorepo";
25
- const publishedRepoctlVersion = "latest";
26
- async function updateRootPackageJson(targetDir, projectName) {
27
- const pkgPath = path.join(targetDir, "package.json");
28
- const raw = await fs.readFile(pkgPath, "utf8");
29
- const pkg = JSON.parse(raw);
30
- pkg.name = projectName;
31
- const devDependencies = pkg.devDependencies;
32
- if (devDependencies && devDependencies[legacyToolPackageName]) delete devDependencies[legacyToolPackageName];
33
- if (devDependencies && devDependencies[repoctlPackageName]?.startsWith("workspace:")) devDependencies[repoctlPackageName] = publishedRepoctlVersion;
34
- if (devDependencies && Object.keys(devDependencies).length === 0) delete pkg.devDependencies;
35
- const scripts = pkg.scripts;
36
- if (scripts) {
37
- const nextScripts = {};
38
- for (const [key, value] of Object.entries(scripts)) {
39
- if (typeof value === "string" && value.includes("monorepo")) continue;
40
- nextScripts[key] = value;
41
- }
42
- if (Object.keys(nextScripts).length) pkg.scripts = nextScripts;
43
- else delete pkg.scripts;
44
- }
45
- await fs.writeFile(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`, "utf8");
46
- }
47
- //#endregion
48
- //#region src/templates.ts
49
- const templateTokenSplitPattern = /[,\s]+/;
50
- const numericTemplatePattern = /^\d+$/;
51
- function parseTemplateInput(input) {
52
- const selections = /* @__PURE__ */ new Set();
53
- const unknown = [];
54
- const tokens = input.split(templateTokenSplitPattern).map((token) => token.trim()).filter(Boolean);
55
- for (const token of tokens) {
56
- const normalized = token.toLowerCase();
57
- if (numericTemplatePattern.test(normalized)) {
58
- const choice = templateChoices[Number(normalized) - 1];
59
- if (choice) selections.add(choice.key);
60
- else unknown.push(token);
61
- continue;
62
- }
63
- const match = templateChoices.find((item) => item.key === normalized);
64
- if (match) selections.add(match.key);
65
- else unknown.push(token);
66
- }
67
- return {
68
- selections: [...selections],
69
- unknown
70
- };
71
- }
72
- function resolveTemplateSelections(input) {
73
- if (!input) return {
74
- selections: [],
75
- unknown: []
76
- };
77
- return parseTemplateInput(input);
78
- }
79
- //#endregion
80
- //#region src/prompts.ts
81
- async function promptTargetDir(defaultDir) {
82
- return ((await input({
83
- message: `项目创建目录(默认 ${defaultDir}):`,
84
- default: defaultDir
85
- }))?.trim?.() ?? defaultDir) || defaultDir;
86
- }
87
- async function promptTemplates() {
88
- return await checkbox({
89
- message: "请选择要保留的模板(默认不选择):",
90
- choices: templateChoices.map((item) => ({
91
- name: `${item.key} - ${item.label}`,
92
- value: item.key
93
- }))
94
- });
95
- }
96
- //#endregion
97
- //#region src/source-npm.ts
98
- async function scaffoldFromNpm(targetDir, selectedTemplates, force = false) {
99
- await scaffoldWorkspace({
100
- targetDir,
101
- templateKeys: selectedTemplates,
102
- targetMode: "prepare",
103
- force
104
- });
105
- }
106
- //#endregion
107
- //#region src/tsconfig.ts
108
- const workspaceRoots = [
109
- "apps",
110
- "packages",
111
- "templates"
112
- ];
113
- async function pathExists(targetPath) {
114
- try {
115
- await fs.access(targetPath);
116
- return true;
117
- } catch {
118
- return false;
119
- }
120
- }
121
- async function readWorkspaceReferences(targetDir, workspaceRoot) {
122
- const rootDir = path.join(targetDir, workspaceRoot);
123
- if (!await pathExists(rootDir)) return [];
124
- const entries = await fs.readdir(rootDir, { withFileTypes: true });
125
- const references = [];
126
- for (const entry of entries) {
127
- if (!entry.isDirectory()) continue;
128
- const workspaceDir = path.join(rootDir, entry.name);
129
- const [hasPackageJson, hasTsconfig] = await Promise.all([pathExists(path.join(workspaceDir, "package.json")), pathExists(path.join(workspaceDir, "tsconfig.json"))]);
130
- if (hasPackageJson && hasTsconfig) references.push({ path: `./${workspaceRoot}/${entry.name}` });
131
- }
132
- return references;
133
- }
134
- async function updateRootTsconfigReferences(targetDir) {
135
- const tsconfigPath = path.join(targetDir, "tsconfig.json");
136
- const raw = await fs.readFile(tsconfigPath, "utf8");
137
- const tsconfig = JSON.parse(raw);
138
- const references = (await Promise.all(workspaceRoots.map((workspaceRoot) => readWorkspaceReferences(targetDir, workspaceRoot)))).flat();
139
- tsconfig.extends = tsconfig.extends ?? "repoctl/tsconfig.json";
140
- tsconfig.files = [];
141
- tsconfig.references = references;
142
- await fs.writeFile(tsconfigPath, `${JSON.stringify(tsconfig, null, 2)}\n`, "utf8");
143
- }
144
- //#endregion
2
+ import { createCreateRepoctlProgram } from "create-repoctl";
145
3
  //#region src/index.ts
146
- function printNextSteps(targetDir) {
147
- process.stdout.write(formatNextSteps(targetDir));
148
- }
149
- async function runCreate(targetDirInput, options) {
150
- const isInteractive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
151
- let targetInput = targetDirInput || "icebreaker-monorepo";
152
- if (isInteractive) targetInput = await promptTargetDir(targetInput);
153
- let selectedTemplates = [];
154
- if (options.templates) {
155
- const { selections, unknown } = resolveTemplateSelections(options.templates);
156
- if (unknown.length) process.stderr.write(`忽略未知模板:${unknown.join(", ")}\n`);
157
- selectedTemplates = selections;
158
- } else if (isInteractive) selectedTemplates = await promptTemplates();
159
- const targetDir = path.resolve(process.cwd(), targetInput);
160
- const projectName = path.basename(targetDir) || targetInput;
161
- await scaffoldFromNpm(targetDir, selectedTemplates, Boolean(options.force));
162
- await updateRootPackageJson(targetDir, projectName);
163
- await updateRootTsconfigReferences(targetDir);
164
- printNextSteps(targetDir);
165
- }
166
- const program = new Command();
167
- program.name("create-icebreaker").description("Bootstrap the icebreaker monorepo template").argument("[dir]", "Target directory", DEFAULT_TARGET).option("-t, --templates <list>", "Comma-separated template keys or indexes to keep").option("-f, --force", "Remove existing target directory before scaffolding", false).action(async (dir, options) => {
168
- try {
169
- await runCreate(dir, options);
170
- } catch (error) {
171
- process.stderr.write(`[create-icebreaker] ${error instanceof Error ? error.message : error}\n`);
172
- process.exitCode = 1;
173
- }
174
- });
175
- program.parseAsync(process.argv).catch((error) => {
4
+ createCreateRepoctlProgram({
5
+ commandName: "create-icebreaker",
6
+ defaultTarget: "icebreaker-monorepo"
7
+ }).parseAsync(process.argv).catch((error) => {
176
8
  process.stderr.write(`[create-icebreaker] ${error instanceof Error ? error.message : error}\n`);
177
9
  process.exitCode = 1;
178
10
  });
package/package.json CHANGED
@@ -1,19 +1,21 @@
1
1
  {
2
2
  "name": "create-icebreaker",
3
3
  "type": "module",
4
- "version": "1.1.0",
5
- "description": "Bootstrap the icebreaker monorepo template via npm create/pnpm create with optional cleanup",
4
+ "version": "1.1.2",
5
+ "description": "Compatibility create command for repoctl-managed workspaces",
6
6
  "author": "ice breaker <hi@sonofmagic.top>",
7
7
  "license": "MIT",
8
+ "homepage": "https://repo.icebreaker.top",
8
9
  "repository": {
9
10
  "type": "git",
10
- "url": "git+https://github.com/sonofmagic/monorepo-template.git",
11
+ "url": "git+https://github.com/sonofmagic/repoctl.git",
11
12
  "directory": "packages/create-icebreaker"
12
13
  },
13
14
  "bugs": {
14
- "url": "https://github.com/sonofmagic/monorepo-template/issues"
15
+ "url": "https://github.com/sonofmagic/repoctl/issues"
15
16
  },
16
17
  "keywords": [
18
+ "repoctl",
17
19
  "monorepo",
18
20
  "create-app",
19
21
  "template",
@@ -41,7 +43,7 @@
41
43
  "node": ">=18.0.0"
42
44
  },
43
45
  "dependencies": {
44
- "@icebreakers/monorepo-templates": "1.1.0"
46
+ "create-repoctl": "0.1.1"
45
47
  },
46
48
  "publishConfig": {
47
49
  "access": "public"
@@ -49,7 +51,7 @@
49
51
  "scripts": {
50
52
  "dev": "tsdown --watch --sourcemap",
51
53
  "build": "tsdown",
52
- "typecheck": "pnpm --filter @icebreakers/monorepo-templates build && node ../../node_modules/typescript/bin/tsc -p tsconfig.json",
54
+ "typecheck": "pnpm --filter create-repoctl build && node ../../node_modules/typescript/bin/tsc -p tsconfig.json",
53
55
  "lint": "eslint .",
54
56
  "test": "vitest run --passWithNoTests"
55
57
  }