create-repoctl 0.0.13 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -4
- package/README.zh-CN.md +25 -0
- package/bin/create-repoctl.js +1 -1
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +9 -0
- package/dist/index.d.mts +16 -1
- package/dist/index.mjs +2 -2
- package/dist/src-B2gA_DOK.mjs +318 -0
- package/package.json +21 -6
package/README.md
CHANGED
|
@@ -1,13 +1,29 @@
|
|
|
1
1
|
# create-repoctl
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
English | [简体中文](README.zh-CN.md)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
The recommended create command for starting a repoctl-managed pnpm and Turborepo workspace.
|
|
6
6
|
|
|
7
|
-
```
|
|
7
|
+
```bash
|
|
8
8
|
npm create repoctl@latest
|
|
9
9
|
pnpm create repoctl
|
|
10
10
|
yarn create repoctl
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
The interactive flow selects a target directory and any built-in templates to include. After creation:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
cd <project>
|
|
17
|
+
pnpm install
|
|
18
|
+
pnpm exec repo init
|
|
19
|
+
pnpm exec repo doctor
|
|
20
|
+
pnpm exec repo check
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Output is English by default. Pass `--lang zh-CN` or set `REPOCTL_LANG=zh-CN` for Simplified Chinese.
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pnpm create repoctl -- --lang zh-CN
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Documentation: https://repo.icebreaker.top
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# create-repoctl
|
|
2
|
+
|
|
3
|
+
[English](README.md) | 简体中文
|
|
4
|
+
|
|
5
|
+
用于创建 repoctl 管理的 pnpm 与 Turborepo 工作区的推荐 create 命令。
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm create repoctl@latest
|
|
9
|
+
pnpm create repoctl
|
|
10
|
+
yarn create repoctl
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
交互流程会选择目标目录和需要包含的内置模板。创建完成后运行:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
cd <project>
|
|
17
|
+
pnpm install
|
|
18
|
+
pnpm exec repo init
|
|
19
|
+
pnpm exec repo doctor
|
|
20
|
+
pnpm exec repo check
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
默认输出英文。传入 `--lang zh-CN` 或设置 `REPOCTL_LANG=zh-CN` 可切换为简体中文。
|
|
24
|
+
|
|
25
|
+
文档:https://repo.icebreaker.top
|
package/bin/create-repoctl.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import '../dist/
|
|
2
|
+
import '../dist/cli.mjs'
|
package/dist/cli.d.mts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
package/dist/cli.mjs
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { t as createCreateRepoctlProgram } from "./src-B2gA_DOK.mjs";
|
|
2
|
+
import process from "node:process";
|
|
3
|
+
//#region src/cli.ts
|
|
4
|
+
createCreateRepoctlProgram().parseAsync(process.argv).catch((error) => {
|
|
5
|
+
process.stderr.write(`[create-repoctl] ${error instanceof Error ? error.message : error}\n`);
|
|
6
|
+
process.exitCode = 1;
|
|
7
|
+
});
|
|
8
|
+
//#endregion
|
|
9
|
+
export {};
|
package/dist/index.d.mts
CHANGED
|
@@ -1 +1,16 @@
|
|
|
1
|
-
import "
|
|
1
|
+
import { Command } from "@icebreakers/monorepo-templates";
|
|
2
|
+
//#region src/i18n/index.d.ts
|
|
3
|
+
type CreateRepoctlLocale = 'en' | 'zh-CN';
|
|
4
|
+
declare function resolveCreateRepoctlLocale(argv?: readonly string[], env?: Record<string, string | undefined>): CreateRepoctlLocale;
|
|
5
|
+
//#endregion
|
|
6
|
+
//#region src/index.d.ts
|
|
7
|
+
interface CreateRepoctlProgramOptions {
|
|
8
|
+
commandName?: string;
|
|
9
|
+
defaultTarget?: string;
|
|
10
|
+
cwd?: string;
|
|
11
|
+
argv?: readonly string[];
|
|
12
|
+
env?: Record<string, string | undefined>;
|
|
13
|
+
}
|
|
14
|
+
declare function createCreateRepoctlProgram(options?: CreateRepoctlProgramOptions): Command;
|
|
15
|
+
//#endregion
|
|
16
|
+
export { type CreateRepoctlLocale, CreateRepoctlProgramOptions, createCreateRepoctlProgram, resolveCreateRepoctlLocale };
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import "
|
|
2
|
-
export {};
|
|
1
|
+
import { n as resolveCreateRepoctlLocale, t as createCreateRepoctlProgram } from "./src-B2gA_DOK.mjs";
|
|
2
|
+
export { createCreateRepoctlProgram, resolveCreateRepoctlLocale };
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
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/i18n/locales/en/error.ts
|
|
6
|
+
const error$1 = {
|
|
7
|
+
commanderAllowedChoices: "Allowed choices are",
|
|
8
|
+
commanderArgumentMissing: "argument missing",
|
|
9
|
+
commanderDidYouMean: "Did you mean",
|
|
10
|
+
commanderDidYouMeanOneOf: "Did you mean one of",
|
|
11
|
+
commanderErrorPrefix: "error:",
|
|
12
|
+
commanderMissingRequiredArgument: "missing required argument",
|
|
13
|
+
commanderNotSpecified: "not specified",
|
|
14
|
+
commanderRequiredOption: "required option",
|
|
15
|
+
commanderTooManyArguments: "too many arguments",
|
|
16
|
+
commanderUnknownCommand: "unknown command",
|
|
17
|
+
commanderUnknownOption: "unknown option",
|
|
18
|
+
invalidLocale: "Unsupported locale: {locale}. Use en or zh-CN."
|
|
19
|
+
};
|
|
20
|
+
//#endregion
|
|
21
|
+
//#region src/i18n/locales/en/help.ts
|
|
22
|
+
const help$1 = {
|
|
23
|
+
argumentsTitle: "Arguments:",
|
|
24
|
+
choicesLabel: "choices:",
|
|
25
|
+
commandsTitle: "Commands:",
|
|
26
|
+
defaultLabel: "default:",
|
|
27
|
+
description: "Create a repoctl-managed pnpm and Turborepo workspace",
|
|
28
|
+
envLabel: "env:",
|
|
29
|
+
targetArgument: "Target directory",
|
|
30
|
+
templatesOption: "Comma-separated template keys or indexes to include",
|
|
31
|
+
forceOption: "Remove an existing target directory before creating the workspace",
|
|
32
|
+
globalOptionsTitle: "Global Options:",
|
|
33
|
+
helpOption: "Display help for command",
|
|
34
|
+
languageOption: "Output language: en or zh-CN",
|
|
35
|
+
optionsTitle: "Options:",
|
|
36
|
+
presetLabel: "preset:",
|
|
37
|
+
usageTitle: "Usage:"
|
|
38
|
+
};
|
|
39
|
+
//#endregion
|
|
40
|
+
//#region src/i18n/locales/en/prompt.ts
|
|
41
|
+
const prompt$1 = {
|
|
42
|
+
targetPrompt: "Project directory (default: {defaultDir})",
|
|
43
|
+
templatesPrompt: "Select templates to include (none by default)"
|
|
44
|
+
};
|
|
45
|
+
//#endregion
|
|
46
|
+
//#region src/i18n/locales/en/report.ts
|
|
47
|
+
const report$1 = {
|
|
48
|
+
unknownTemplates: "Ignoring unknown templates: {templates}",
|
|
49
|
+
complete: "Workspace created. Next steps:"
|
|
50
|
+
};
|
|
51
|
+
//#endregion
|
|
52
|
+
//#region src/i18n/locales/en/index.ts
|
|
53
|
+
const en = {
|
|
54
|
+
...help$1,
|
|
55
|
+
...prompt$1,
|
|
56
|
+
...report$1,
|
|
57
|
+
...error$1
|
|
58
|
+
};
|
|
59
|
+
//#endregion
|
|
60
|
+
//#region src/i18n/locales/zh-CN/error.ts
|
|
61
|
+
const error = {
|
|
62
|
+
commanderAllowedChoices: "可选值为",
|
|
63
|
+
commanderArgumentMissing: "缺少参数",
|
|
64
|
+
commanderDidYouMean: "你是否想输入",
|
|
65
|
+
commanderDidYouMeanOneOf: "你是否想输入以下之一",
|
|
66
|
+
commanderErrorPrefix: "错误:",
|
|
67
|
+
commanderMissingRequiredArgument: "缺少必填参数",
|
|
68
|
+
commanderNotSpecified: "未指定",
|
|
69
|
+
commanderRequiredOption: "必填选项",
|
|
70
|
+
commanderTooManyArguments: "参数过多",
|
|
71
|
+
commanderUnknownCommand: "未知命令",
|
|
72
|
+
commanderUnknownOption: "未知选项",
|
|
73
|
+
invalidLocale: "不支持的语言:{locale}。请使用 en 或 zh-CN。"
|
|
74
|
+
};
|
|
75
|
+
//#endregion
|
|
76
|
+
//#region src/i18n/locales/zh-CN/help.ts
|
|
77
|
+
const help = {
|
|
78
|
+
argumentsTitle: "参数:",
|
|
79
|
+
choicesLabel: "可选值:",
|
|
80
|
+
commandsTitle: "命令:",
|
|
81
|
+
defaultLabel: "默认值:",
|
|
82
|
+
description: "创建由 repoctl 管理的 pnpm 与 Turborepo 工作区",
|
|
83
|
+
envLabel: "环境变量:",
|
|
84
|
+
targetArgument: "目标目录",
|
|
85
|
+
templatesOption: "要包含的模板 key 或序号,以逗号分隔",
|
|
86
|
+
forceOption: "创建前删除已存在的目标目录",
|
|
87
|
+
globalOptionsTitle: "全局选项:",
|
|
88
|
+
helpOption: "显示命令帮助",
|
|
89
|
+
languageOption: "输出语言:en 或 zh-CN",
|
|
90
|
+
optionsTitle: "选项:",
|
|
91
|
+
presetLabel: "预设值:",
|
|
92
|
+
usageTitle: "用法:"
|
|
93
|
+
};
|
|
94
|
+
//#endregion
|
|
95
|
+
//#region src/i18n/locales/zh-CN/prompt.ts
|
|
96
|
+
const prompt = {
|
|
97
|
+
targetPrompt: "项目目录(默认:{defaultDir})",
|
|
98
|
+
templatesPrompt: "选择要包含的模板(默认不选择)"
|
|
99
|
+
};
|
|
100
|
+
//#endregion
|
|
101
|
+
//#region src/i18n/locales/zh-CN/report.ts
|
|
102
|
+
const report = {
|
|
103
|
+
unknownTemplates: "忽略未知模板:{templates}",
|
|
104
|
+
complete: "工作区创建完成。后续步骤:"
|
|
105
|
+
};
|
|
106
|
+
//#endregion
|
|
107
|
+
//#region src/i18n/locales/zh-CN/index.ts
|
|
108
|
+
const zhCN = {
|
|
109
|
+
...help,
|
|
110
|
+
...prompt,
|
|
111
|
+
...report,
|
|
112
|
+
...error
|
|
113
|
+
};
|
|
114
|
+
//#endregion
|
|
115
|
+
//#region src/i18n/index.ts
|
|
116
|
+
const supportedLocales = ["en", "zh-CN"];
|
|
117
|
+
function isCreateRepoctlLocale(value) {
|
|
118
|
+
return supportedLocales.includes(value);
|
|
119
|
+
}
|
|
120
|
+
function readArgvLocale(argv) {
|
|
121
|
+
const equalsArg = argv.find((arg) => arg.startsWith("--lang="));
|
|
122
|
+
if (equalsArg) return equalsArg.slice(7);
|
|
123
|
+
const index = argv.indexOf("--lang");
|
|
124
|
+
return index >= 0 ? argv[index + 1] : void 0;
|
|
125
|
+
}
|
|
126
|
+
function resolveCreateRepoctlLocale(argv = process.argv, env = process.env) {
|
|
127
|
+
const argvLocale = readArgvLocale(argv);
|
|
128
|
+
if (isCreateRepoctlLocale(argvLocale)) return argvLocale;
|
|
129
|
+
const envLocale = env["REPOCTL_LANG"];
|
|
130
|
+
return isCreateRepoctlLocale(envLocale) ? envLocale : "en";
|
|
131
|
+
}
|
|
132
|
+
function createTranslator(locale) {
|
|
133
|
+
const catalog = locale === "zh-CN" ? zhCN : en;
|
|
134
|
+
return (key, values = {}) => {
|
|
135
|
+
return Object.entries(values).reduce((result, [name, value]) => result.replaceAll(`{${name}}`, value), catalog[key]);
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
//#endregion
|
|
139
|
+
//#region src/next-steps.ts
|
|
140
|
+
function formatNextSteps(targetDir, cwd = process.cwd(), heading = "Workspace created. Next steps:") {
|
|
141
|
+
return [
|
|
142
|
+
"",
|
|
143
|
+
heading,
|
|
144
|
+
` cd ${path.relative(cwd, targetDir) || "."}`,
|
|
145
|
+
" pnpm install",
|
|
146
|
+
" pnpm exec repo init",
|
|
147
|
+
" pnpm exec repo doctor",
|
|
148
|
+
" pnpm exec repo check",
|
|
149
|
+
""
|
|
150
|
+
].join("\n");
|
|
151
|
+
}
|
|
152
|
+
//#endregion
|
|
153
|
+
//#region src/package-json.ts
|
|
154
|
+
const repoctlPackageName = "repoctl";
|
|
155
|
+
const legacyToolPackageName = "@icebreakers/monorepo";
|
|
156
|
+
const publishedRepoctlVersion = "latest";
|
|
157
|
+
async function updateRootPackageJson(targetDir, projectName) {
|
|
158
|
+
const pkgPath = path.join(targetDir, "package.json");
|
|
159
|
+
const raw = await fs.readFile(pkgPath, "utf8");
|
|
160
|
+
const pkg = JSON.parse(raw);
|
|
161
|
+
pkg.name = projectName;
|
|
162
|
+
const devDependencies = pkg.devDependencies;
|
|
163
|
+
if (devDependencies && devDependencies[legacyToolPackageName]) delete devDependencies[legacyToolPackageName];
|
|
164
|
+
if (devDependencies && devDependencies[repoctlPackageName]?.startsWith("workspace:")) devDependencies[repoctlPackageName] = publishedRepoctlVersion;
|
|
165
|
+
if (devDependencies && Object.keys(devDependencies).length === 0) delete pkg.devDependencies;
|
|
166
|
+
const scripts = pkg.scripts;
|
|
167
|
+
if (scripts) {
|
|
168
|
+
const nextScripts = {};
|
|
169
|
+
for (const [key, value] of Object.entries(scripts)) {
|
|
170
|
+
if (typeof value === "string" && value.includes("monorepo")) continue;
|
|
171
|
+
nextScripts[key] = value;
|
|
172
|
+
}
|
|
173
|
+
if (Object.keys(nextScripts).length) pkg.scripts = nextScripts;
|
|
174
|
+
else delete pkg.scripts;
|
|
175
|
+
}
|
|
176
|
+
await fs.writeFile(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`, "utf8");
|
|
177
|
+
}
|
|
178
|
+
//#endregion
|
|
179
|
+
//#region src/templates.ts
|
|
180
|
+
const templateTokenSplitPattern = /[,\s]+/;
|
|
181
|
+
const numericTemplatePattern = /^\d+$/;
|
|
182
|
+
function parseTemplateInput(input) {
|
|
183
|
+
const selections = /* @__PURE__ */ new Set();
|
|
184
|
+
const unknown = [];
|
|
185
|
+
const tokens = input.split(templateTokenSplitPattern).map((token) => token.trim()).filter(Boolean);
|
|
186
|
+
for (const token of tokens) {
|
|
187
|
+
const normalized = token.toLowerCase();
|
|
188
|
+
if (numericTemplatePattern.test(normalized)) {
|
|
189
|
+
const choice = templateChoices[Number(normalized) - 1];
|
|
190
|
+
if (choice) selections.add(choice.key);
|
|
191
|
+
else unknown.push(token);
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
const match = templateChoices.find((item) => item.key === normalized);
|
|
195
|
+
if (match) selections.add(match.key);
|
|
196
|
+
else unknown.push(token);
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
selections: [...selections],
|
|
200
|
+
unknown
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
function resolveTemplateSelections(input) {
|
|
204
|
+
if (!input) return {
|
|
205
|
+
selections: [],
|
|
206
|
+
unknown: []
|
|
207
|
+
};
|
|
208
|
+
return parseTemplateInput(input);
|
|
209
|
+
}
|
|
210
|
+
//#endregion
|
|
211
|
+
//#region src/prompts.ts
|
|
212
|
+
async function promptTargetDir(defaultDir, message) {
|
|
213
|
+
return ((await input({
|
|
214
|
+
message,
|
|
215
|
+
default: defaultDir
|
|
216
|
+
}))?.trim?.() ?? defaultDir) || defaultDir;
|
|
217
|
+
}
|
|
218
|
+
async function promptTemplates(message) {
|
|
219
|
+
return await checkbox({
|
|
220
|
+
message,
|
|
221
|
+
choices: templateChoices.map((item) => ({
|
|
222
|
+
name: `${item.key} - ${item.label}`,
|
|
223
|
+
value: item.key
|
|
224
|
+
}))
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
//#endregion
|
|
228
|
+
//#region src/source-npm.ts
|
|
229
|
+
async function scaffoldFromNpm(targetDir, selectedTemplates, force = false) {
|
|
230
|
+
await scaffoldWorkspace({
|
|
231
|
+
targetDir,
|
|
232
|
+
templateKeys: selectedTemplates,
|
|
233
|
+
targetMode: "prepare",
|
|
234
|
+
force
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
//#endregion
|
|
238
|
+
//#region src/tsconfig.ts
|
|
239
|
+
const workspaceRoots = [
|
|
240
|
+
"apps",
|
|
241
|
+
"packages",
|
|
242
|
+
"templates"
|
|
243
|
+
];
|
|
244
|
+
async function pathExists(targetPath) {
|
|
245
|
+
try {
|
|
246
|
+
await fs.access(targetPath);
|
|
247
|
+
return true;
|
|
248
|
+
} catch {
|
|
249
|
+
return false;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
async function readWorkspaceReferences(targetDir, workspaceRoot) {
|
|
253
|
+
const rootDir = path.join(targetDir, workspaceRoot);
|
|
254
|
+
if (!await pathExists(rootDir)) return [];
|
|
255
|
+
const entries = await fs.readdir(rootDir, { withFileTypes: true });
|
|
256
|
+
const references = [];
|
|
257
|
+
for (const entry of entries) {
|
|
258
|
+
if (!entry.isDirectory()) continue;
|
|
259
|
+
const workspaceDir = path.join(rootDir, entry.name);
|
|
260
|
+
const [hasPackageJson, hasTsconfig] = await Promise.all([pathExists(path.join(workspaceDir, "package.json")), pathExists(path.join(workspaceDir, "tsconfig.json"))]);
|
|
261
|
+
if (hasPackageJson && hasTsconfig) references.push({ path: `./${workspaceRoot}/${entry.name}` });
|
|
262
|
+
}
|
|
263
|
+
return references;
|
|
264
|
+
}
|
|
265
|
+
async function updateRootTsconfigReferences(targetDir) {
|
|
266
|
+
const tsconfigPath = path.join(targetDir, "tsconfig.json");
|
|
267
|
+
const raw = await fs.readFile(tsconfigPath, "utf8");
|
|
268
|
+
const tsconfig = JSON.parse(raw);
|
|
269
|
+
const references = (await Promise.all(workspaceRoots.map((workspaceRoot) => readWorkspaceReferences(targetDir, workspaceRoot)))).flat();
|
|
270
|
+
tsconfig.extends = tsconfig.extends ?? "repoctl/tsconfig.json";
|
|
271
|
+
tsconfig.files = [];
|
|
272
|
+
tsconfig.references = references;
|
|
273
|
+
await fs.writeFile(tsconfigPath, `${JSON.stringify(tsconfig, null, 2)}\n`, "utf8");
|
|
274
|
+
}
|
|
275
|
+
//#endregion
|
|
276
|
+
//#region src/index.ts
|
|
277
|
+
function createCreateRepoctlProgram(options = {}) {
|
|
278
|
+
const commandName = options.commandName ?? "create-repoctl";
|
|
279
|
+
const defaultTarget = options.defaultTarget ?? "repoctl-workspace";
|
|
280
|
+
const cwd = options.cwd ?? process.cwd();
|
|
281
|
+
const t = createTranslator(resolveCreateRepoctlLocale(options.argv ?? process.argv, options.env ?? process.env));
|
|
282
|
+
const program = new Command();
|
|
283
|
+
const defaultHelp = program.createHelp();
|
|
284
|
+
const titles = {
|
|
285
|
+
"Arguments:": t("argumentsTitle"),
|
|
286
|
+
"Commands:": t("commandsTitle"),
|
|
287
|
+
"Global Options:": t("globalOptionsTitle"),
|
|
288
|
+
"Options:": t("optionsTitle"),
|
|
289
|
+
"Usage:": t("usageTitle")
|
|
290
|
+
};
|
|
291
|
+
const localizeHelpMetadata = (value) => value.replaceAll("choices:", t("choicesLabel")).replaceAll("default:", t("defaultLabel")).replaceAll("preset:", t("presetLabel")).replaceAll("env:", t("envLabel"));
|
|
292
|
+
const localizeCommanderError = (value) => value.replaceAll("Did you mean one of", t("commanderDidYouMeanOneOf")).replaceAll("Did you mean", t("commanderDidYouMean")).replaceAll("Allowed choices are", t("commanderAllowedChoices")).replaceAll("missing required argument", t("commanderMissingRequiredArgument")).replaceAll("required option", t("commanderRequiredOption")).replaceAll("argument missing", t("commanderArgumentMissing")).replaceAll("not specified", t("commanderNotSpecified")).replaceAll("too many arguments", t("commanderTooManyArguments")).replaceAll("unknown command", t("commanderUnknownCommand")).replaceAll("unknown option", t("commanderUnknownOption")).replace(/^error:/, t("commanderErrorPrefix"));
|
|
293
|
+
program.name(commandName).description(t("description")).argument("[dir]", t("targetArgument"), defaultTarget).option("-t, --templates <list>", t("templatesOption")).option("-f, --force", t("forceOption"), false).option("--lang <locale>", t("languageOption")).helpOption("-h, --help", t("helpOption")).configureOutput({ outputError: (value, write) => write(localizeCommanderError(value)) }).configureHelp({
|
|
294
|
+
argumentDescription: (argument) => localizeHelpMetadata(defaultHelp.argumentDescription(argument)),
|
|
295
|
+
optionDescription: (option) => localizeHelpMetadata(defaultHelp.optionDescription(option)),
|
|
296
|
+
styleTitle: (title) => titles[title] ?? title
|
|
297
|
+
}).action(async (targetDirInput, createOptions) => {
|
|
298
|
+
if (createOptions.lang && !isCreateRepoctlLocale(createOptions.lang)) throw new Error(t("invalidLocale", { locale: createOptions.lang }));
|
|
299
|
+
const isInteractive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
300
|
+
let targetInput = targetDirInput || defaultTarget;
|
|
301
|
+
if (isInteractive) targetInput = await promptTargetDir(targetInput, t("targetPrompt", { defaultDir: targetInput }));
|
|
302
|
+
let selectedTemplates = [];
|
|
303
|
+
if (createOptions.templates) {
|
|
304
|
+
const { selections, unknown } = resolveTemplateSelections(createOptions.templates);
|
|
305
|
+
if (unknown.length) process.stderr.write(`${t("unknownTemplates", { templates: unknown.join(", ") })}\n`);
|
|
306
|
+
selectedTemplates = selections;
|
|
307
|
+
} else if (isInteractive) selectedTemplates = await promptTemplates(t("templatesPrompt"));
|
|
308
|
+
const targetDir = path.resolve(cwd, targetInput);
|
|
309
|
+
const projectName = path.basename(targetDir) || targetInput;
|
|
310
|
+
await scaffoldFromNpm(targetDir, selectedTemplates, Boolean(createOptions.force));
|
|
311
|
+
await updateRootPackageJson(targetDir, projectName);
|
|
312
|
+
await updateRootTsconfigReferences(targetDir);
|
|
313
|
+
process.stdout.write(formatNextSteps(targetDir, cwd, t("complete")));
|
|
314
|
+
});
|
|
315
|
+
return program;
|
|
316
|
+
}
|
|
317
|
+
//#endregion
|
|
318
|
+
export { resolveCreateRepoctlLocale as n, createCreateRepoctlProgram as t };
|
package/package.json
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-repoctl",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.0
|
|
5
|
-
"description": "
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"description": "Create a repoctl-managed pnpm and Turborepo workspace",
|
|
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/
|
|
11
|
+
"url": "git+https://github.com/sonofmagic/repoctl.git",
|
|
11
12
|
"directory": "packages/create-repoctl"
|
|
12
13
|
},
|
|
13
14
|
"bugs": {
|
|
14
|
-
"url": "https://github.com/sonofmagic/
|
|
15
|
+
"url": "https://github.com/sonofmagic/repoctl/issues"
|
|
15
16
|
},
|
|
16
17
|
"keywords": [
|
|
17
18
|
"repoctl",
|
|
@@ -21,6 +22,16 @@
|
|
|
21
22
|
"scaffold"
|
|
22
23
|
],
|
|
23
24
|
"sideEffects": false,
|
|
25
|
+
"exports": {
|
|
26
|
+
".": {
|
|
27
|
+
"types": "./dist/index.d.mts",
|
|
28
|
+
"default": "./dist/index.mjs"
|
|
29
|
+
},
|
|
30
|
+
"./package.json": "./package.json"
|
|
31
|
+
},
|
|
32
|
+
"main": "./dist/index.mjs",
|
|
33
|
+
"module": "./dist/index.mjs",
|
|
34
|
+
"types": "./dist/index.d.mts",
|
|
24
35
|
"bin": {
|
|
25
36
|
"create-repoctl": "bin/create-repoctl.js"
|
|
26
37
|
},
|
|
@@ -32,7 +43,10 @@
|
|
|
32
43
|
"node": ">=18.0.0"
|
|
33
44
|
},
|
|
34
45
|
"dependencies": {
|
|
35
|
-
"
|
|
46
|
+
"@icebreakers/monorepo-templates": "1.2.0"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"tsd": "^0.33.0"
|
|
36
50
|
},
|
|
37
51
|
"publishConfig": {
|
|
38
52
|
"access": "public"
|
|
@@ -40,7 +54,8 @@
|
|
|
40
54
|
"scripts": {
|
|
41
55
|
"dev": "tsdown --watch --sourcemap",
|
|
42
56
|
"build": "tsdown",
|
|
43
|
-
"typecheck": "pnpm --filter
|
|
57
|
+
"typecheck": "pnpm --filter @icebreakers/monorepo-templates build && node ../../node_modules/typescript/bin/tsc -p tsconfig.json",
|
|
58
|
+
"tsd": "pnpm build && tsd --files test-d/**/*.test-d.ts",
|
|
44
59
|
"lint": "eslint .",
|
|
45
60
|
"test": "vitest run --passWithNoTests"
|
|
46
61
|
}
|