@emeryld/manager 0.4.0 → 0.4.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 +39 -55
- package/dist/create-package/index.js +177 -10
- package/dist/create-package/shared.js +1 -1
- package/dist/create-package/variants/client.js +19 -12
- package/dist/create-package/variants/contract.js +18 -12
- package/dist/create-package/variants/docker.js +32 -20
- package/dist/create-package/variants/empty.js +16 -12
- package/dist/create-package/variants/fullstack.js +34 -0
- package/dist/create-package/variants/server.js +20 -13
- package/dist/publish.js +9 -1
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -1,58 +1,42 @@
|
|
|
1
1
|
# @emeryld/manager
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"manager-cli": "manager-cli"
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
```
|
|
20
|
-
4. Run `pnpm install` to fetch `ts-node`, `semver`, and other runtime deps.
|
|
21
|
-
|
|
22
|
-
## Running the CLI
|
|
23
|
-
|
|
24
|
-
- Always run `pnpm manager-cli` (or `pnpm run manager-cli`) from the **workspace root** where your `packages/` folder lives. The manager operates against the workspace you are in, not the package that ships the tool.
|
|
25
|
-
- Each TypeScript helper script registers the bundled `ts-node/esm` loader with the exact script path, so the CLI works even from pnpm workspaces that hoist dependencies.
|
|
26
|
-
- To avoid the warning/error you saw earlier, make sure:
|
|
27
|
-
- the workspace has `packages/` (or a configured manifest) so `loadPackages()` can find targets,
|
|
28
|
-
- `pnpm install` has already written `ts-node` into `node_modules`,
|
|
29
|
-
- you execute the CLI from the workspace that owns those packages.
|
|
30
|
-
|
|
31
|
-
## Testing the loader registration
|
|
32
|
-
|
|
33
|
-
Run `pnpm test` to ensure the helper CLI always generates a `--import data:text...` snippet that registers `ts-node/esm.mjs` with **the actual script file path**. This guards against regressions that would make Node reject the loader and crash before the interactive menu appears.
|
|
34
|
-
|
|
35
|
-
## Package creator overview
|
|
36
|
-
|
|
37
|
-
Use `Create package` inside the CLI to scaffold starter packages. The flow will prompt for a variant, target path, and package name, then generate the files and attempt `pnpm install` and `pnpm run build` when a build script exists.
|
|
38
|
-
|
|
39
|
-
### How to scaffold
|
|
40
|
-
- From the workspace root, run `pnpm manager-cli` → choose `Create package`.
|
|
41
|
-
- Pick a variant, confirm the target directory, and override the package name if desired.
|
|
42
|
-
- After scaffolding, the new package’s README covers variant-specific details and scripts.
|
|
43
|
-
|
|
44
|
-
### Variant quick view
|
|
45
|
-
|
|
46
|
-
| Variant | Default dir | Purpose | Key scripts |
|
|
3
|
+
Dev dependency built for Codex agents: scaffold RRRoutes packages, read the generated structure, and ship changes without rebuilding boilerplate. Install it in a pnpm workspace and call `pnpm manager-cli` from the workspace root.
|
|
4
|
+
|
|
5
|
+
## Quick start (agents)
|
|
6
|
+
- Install: `pnpm add -D @emeryld/manager`
|
|
7
|
+
- Add a script: `"manager-cli": "manager-cli"` in the workspace `package.json`
|
|
8
|
+
- Discover templates fast: `pnpm manager-cli templates` (alias `pnpm manager-cli create --list`)
|
|
9
|
+
- Scaffold without prompts:
|
|
10
|
+
`pnpm manager-cli create --variant rrr-server --dir packages/api --name @scope/api --contract @scope/contract --skip-install`
|
|
11
|
+
- Prefer prompts: `pnpm manager-cli create` and follow the menu
|
|
12
|
+
- After scaffolding, open the new package `README.md` for variant-specific scripts and usage
|
|
13
|
+
|
|
14
|
+
## Templates at a glance
|
|
15
|
+
| id | default dir | summary | key files |
|
|
47
16
|
| --- | --- | --- | --- |
|
|
48
|
-
| rrr
|
|
49
|
-
| rrr
|
|
50
|
-
| rrr
|
|
51
|
-
| empty
|
|
52
|
-
|
|
|
53
|
-
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
-
|
|
17
|
+
| rrr-contract | `packages/rrr-contract` | Shared RRRoutes contract + sockets for clients/servers | `src/index.ts`, `README.md` |
|
|
18
|
+
| rrr-server | `packages/rrr-server` | Express + RRRoutes API wired to a contract import | `src/index.ts`, `.env.example`, `README.md` |
|
|
19
|
+
| rrr-client | `packages/rrr-client` | React Query-ready RRRoutes client bound to a contract import | `src/index.ts`, `README.md` |
|
|
20
|
+
| rrr-empty | `packages/rrr-empty` | Minimal TypeScript package with lint/format/test wiring | `src/index.ts`, `README.md` |
|
|
21
|
+
| rrr-docker | `packages/rrr-docker` | Express service with Dockerfile + helper CLI | `src/index.ts`, `scripts/docker.ts`, `Dockerfile`, `README.md` |
|
|
22
|
+
| rrr-fullstack | `rrrfull-stack` | pnpm workspace with contract, server, client, and docker helper | root `package.json`, `pnpm-workspace.yaml`, `packages/*` |
|
|
23
|
+
|
|
24
|
+
Use `pnpm manager-cli create --describe <variant>` to print the scripts, key files, and notes for any template before scaffolding.
|
|
25
|
+
|
|
26
|
+
## Create command reference
|
|
27
|
+
- `pnpm manager-cli create --list` — show templates
|
|
28
|
+
- `pnpm manager-cli create --describe rrr-server` — see scripts/files/notes for one template
|
|
29
|
+
- `pnpm manager-cli create --variant rrr-client --dir packages/web-client --name @scope/web-client` — scaffold directly (adds `--contract <id>` for server/client)
|
|
30
|
+
- `--skip-install` / `--skip-build` — bypass post-create steps when you want to install/build later
|
|
31
|
+
|
|
32
|
+
## Release helper (existing packages)
|
|
33
|
+
- Run from the workspace root where `packages/` lives.
|
|
34
|
+
- `pnpm manager-cli` launches an interactive menu: pick packages, then run update → test → build → publish (or any single step).
|
|
35
|
+
- Non-interactive publish: `pnpm manager-cli <pkg|all> --non-interactive --bump patch` (see CLI prompts for flags like `--sync` and `--tag`).
|
|
36
|
+
|
|
37
|
+
## Notes
|
|
38
|
+
- Each helper script registers the bundled `ts-node/esm` loader with the correct path so the CLI works even when dependencies are hoisted.
|
|
39
|
+
- The create flow runs `pnpm install` and `pnpm run build` when a build script exists; skip with `--skip-install`/`--skip-build` if you want to control timing.
|
|
40
|
+
|
|
41
|
+
## Tests
|
|
42
|
+
- `pnpm test` verifies the helper CLI loader registration.
|
|
@@ -4,7 +4,7 @@ import path from 'node:path';
|
|
|
4
4
|
import { stdin as input } from 'node:process';
|
|
5
5
|
import { askLine, promptSingleKey } from '../prompts.js';
|
|
6
6
|
import { colors, logGlobal } from '../utils/log.js';
|
|
7
|
-
import { workspaceRoot } from './shared.js';
|
|
7
|
+
import { SCRIPT_DESCRIPTIONS, workspaceRoot, } from './shared.js';
|
|
8
8
|
import { clientVariant } from './variants/client.js';
|
|
9
9
|
import { contractVariant } from './variants/contract.js';
|
|
10
10
|
import { dockerVariant } from './variants/docker.js';
|
|
@@ -19,10 +19,122 @@ const VARIANTS = [
|
|
|
19
19
|
dockerVariant,
|
|
20
20
|
fullstackVariant,
|
|
21
21
|
];
|
|
22
|
+
const VARIANT_LOOKUP = new Map(VARIANTS.map((variant) => [variant.id, variant]));
|
|
22
23
|
function derivePackageName(targetDir) {
|
|
23
24
|
const base = path.basename(targetDir) || 'rrr-package';
|
|
24
25
|
return base;
|
|
25
26
|
}
|
|
27
|
+
function resolveVariant(key) {
|
|
28
|
+
if (!key)
|
|
29
|
+
return undefined;
|
|
30
|
+
const normalized = key.toLowerCase();
|
|
31
|
+
return (VARIANT_LOOKUP.get(normalized) ??
|
|
32
|
+
VARIANTS.find((variant) => {
|
|
33
|
+
const label = variant.label.toLowerCase();
|
|
34
|
+
const id = variant.id.toLowerCase();
|
|
35
|
+
return (id === normalized ||
|
|
36
|
+
id.replace(/^rrr-/, '') === normalized ||
|
|
37
|
+
label === normalized ||
|
|
38
|
+
label.includes(normalized));
|
|
39
|
+
}));
|
|
40
|
+
}
|
|
41
|
+
function printVariantList() {
|
|
42
|
+
logGlobal('Available templates', colors.magenta);
|
|
43
|
+
VARIANTS.forEach((variant) => {
|
|
44
|
+
const summary = variant.summary ? ` ${colors.dim(`– ${variant.summary}`)}` : '';
|
|
45
|
+
console.log(`- ${colors.green(variant.label)} ${colors.dim(`(${variant.id})`)} → ${colors.cyan(variant.defaultDir)}${summary}`);
|
|
46
|
+
});
|
|
47
|
+
console.log(colors.dim('Use "--describe <variant>" for details or "--variant <id> --dir <path>" to scaffold without prompts.'));
|
|
48
|
+
}
|
|
49
|
+
function printVariantDetails(variant) {
|
|
50
|
+
logGlobal(`${variant.label} (${variant.id})`, colors.green);
|
|
51
|
+
console.log(` default dir: ${colors.cyan(variant.defaultDir)}`);
|
|
52
|
+
if (variant.summary) {
|
|
53
|
+
console.log(` ${variant.summary}`);
|
|
54
|
+
}
|
|
55
|
+
if (variant.keyFiles?.length) {
|
|
56
|
+
console.log(' key files:');
|
|
57
|
+
variant.keyFiles.forEach((file) => console.log(` - ${file}`));
|
|
58
|
+
}
|
|
59
|
+
if (variant.scripts?.length) {
|
|
60
|
+
console.log(' scripts:');
|
|
61
|
+
variant.scripts.forEach((script) => {
|
|
62
|
+
const desc = SCRIPT_DESCRIPTIONS[script];
|
|
63
|
+
const detail = desc ? colors.dim(` – ${desc}`) : '';
|
|
64
|
+
console.log(` - ${script}${detail}`);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
if (variant.notes?.length) {
|
|
68
|
+
console.log(' notes:');
|
|
69
|
+
variant.notes.forEach((note) => console.log(` - ${note}`));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function parseCreateCliArgs(argv) {
|
|
73
|
+
const options = {};
|
|
74
|
+
for (let i = 0; i < argv.length; i++) {
|
|
75
|
+
const arg = argv[i];
|
|
76
|
+
if (arg === '--list' || arg === '-l' || arg === 'list' || arg === 'ls') {
|
|
77
|
+
options.list = true;
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (arg === '--describe' || arg === '-d' || arg === 'describe') {
|
|
81
|
+
options.describe = argv[++i];
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (arg === '--variant' || arg === '-v') {
|
|
85
|
+
options.variant = argv[++i];
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (arg === '--dir' || arg === '--path' || arg === '-p') {
|
|
89
|
+
options.targetDir = argv[++i];
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (arg === '--name' || arg === '-n') {
|
|
93
|
+
options.pkgName = argv[++i];
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (arg === '--contract') {
|
|
97
|
+
options.contractName = argv[++i];
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (arg === '--skip-install') {
|
|
101
|
+
options.skipInstall = true;
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (arg === '--skip-build') {
|
|
105
|
+
options.skipBuild = true;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (arg === '--help' || arg === '-h') {
|
|
109
|
+
options.help = true;
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if (!arg.startsWith('-') && !options.variant) {
|
|
113
|
+
options.variant = arg;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return options;
|
|
117
|
+
}
|
|
118
|
+
function printCreateHelp() {
|
|
119
|
+
logGlobal('Create package help', colors.magenta);
|
|
120
|
+
console.log('Usage:');
|
|
121
|
+
console.log(' pnpm manager-cli create # interactive prompts');
|
|
122
|
+
console.log(' pnpm manager-cli create --list # list templates');
|
|
123
|
+
console.log(' pnpm manager-cli create --describe rrr-server');
|
|
124
|
+
console.log(' pnpm manager-cli create --variant rrr-client --dir packages/rrr-client --name @scope/client');
|
|
125
|
+
console.log(' pnpm manager-cli create --variant rrr-server --contract @scope/contract --skip-install');
|
|
126
|
+
console.log('');
|
|
127
|
+
console.log('Flags:');
|
|
128
|
+
console.log(' --list, -l Show available templates');
|
|
129
|
+
console.log(' --describe, -d Print details for a template');
|
|
130
|
+
console.log(' --variant, -v Pick a template by id/label (skips variant prompt)');
|
|
131
|
+
console.log(' --dir, --path, -p Target directory (skips path prompt)');
|
|
132
|
+
console.log(' --name, -n Package name (skips name prompt)');
|
|
133
|
+
console.log(' --contract Contract import to inject (server/client variants)');
|
|
134
|
+
console.log(' --skip-install Do not run pnpm install after scaffolding');
|
|
135
|
+
console.log(' --skip-build Skip build after scaffolding');
|
|
136
|
+
console.log(' --help, -h Show this help');
|
|
137
|
+
}
|
|
26
138
|
async function ensureTargetDir(targetDir) {
|
|
27
139
|
try {
|
|
28
140
|
const stats = await stat(targetDir);
|
|
@@ -177,7 +289,11 @@ async function promptForTargetDir(fallback) {
|
|
|
177
289
|
const normalized = answer || fallback;
|
|
178
290
|
return path.resolve(workspaceRoot, normalized);
|
|
179
291
|
}
|
|
180
|
-
async function postCreateTasks(targetDir) {
|
|
292
|
+
async function postCreateTasks(targetDir, options) {
|
|
293
|
+
if (options?.skipInstall) {
|
|
294
|
+
logGlobal('Skipping pnpm install (flag).', colors.dim);
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
181
297
|
try {
|
|
182
298
|
logGlobal('Running pnpm install…', colors.cyan);
|
|
183
299
|
await runCommand('pnpm', ['install'], workspaceRoot);
|
|
@@ -186,6 +302,10 @@ async function postCreateTasks(targetDir) {
|
|
|
186
302
|
logGlobal(`pnpm install failed: ${error instanceof Error ? error.message : String(error)}`, colors.yellow);
|
|
187
303
|
return;
|
|
188
304
|
}
|
|
305
|
+
if (options?.skipBuild) {
|
|
306
|
+
logGlobal('Skipping build (flag).', colors.dim);
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
189
309
|
try {
|
|
190
310
|
const pkgJsonPath = path.join(targetDir, 'package.json');
|
|
191
311
|
const pkgRaw = await readFile(pkgJsonPath, 'utf8');
|
|
@@ -199,22 +319,69 @@ async function postCreateTasks(targetDir) {
|
|
|
199
319
|
logGlobal(`Skipping build (could not read package.json): ${error instanceof Error ? error.message : String(error)}`, colors.yellow);
|
|
200
320
|
}
|
|
201
321
|
}
|
|
202
|
-
async function gatherTarget() {
|
|
203
|
-
const variant = await promptForVariant();
|
|
204
|
-
const targetDir =
|
|
322
|
+
async function gatherTarget(initial = {}) {
|
|
323
|
+
const variant = initial.variant ?? (await promptForVariant());
|
|
324
|
+
const targetDir = initial.targetDir !== undefined
|
|
325
|
+
? path.resolve(workspaceRoot, initial.targetDir)
|
|
326
|
+
: await promptForTargetDir(variant.defaultDir);
|
|
205
327
|
const fallbackName = derivePackageName(targetDir);
|
|
206
|
-
const nameAnswer =
|
|
328
|
+
const nameAnswer = initial.pkgName === undefined
|
|
329
|
+
? await askLine(`Package name? (${fallbackName}): `)
|
|
330
|
+
: initial.pkgName;
|
|
207
331
|
const pkgName = (nameAnswer || fallbackName).trim() || fallbackName;
|
|
208
332
|
await ensureTargetDir(targetDir);
|
|
209
|
-
return {
|
|
333
|
+
return {
|
|
334
|
+
variant,
|
|
335
|
+
targetDir,
|
|
336
|
+
pkgName,
|
|
337
|
+
contractName: initial.contractName,
|
|
338
|
+
};
|
|
210
339
|
}
|
|
211
|
-
export async function createRrrPackage() {
|
|
212
|
-
const target = await gatherTarget();
|
|
340
|
+
export async function createRrrPackage(options = {}) {
|
|
341
|
+
const target = await gatherTarget(options);
|
|
213
342
|
logGlobal(`Creating ${target.variant.label} in ${path.relative(workspaceRoot, target.targetDir) || '.'}`, colors.green);
|
|
214
343
|
await target.variant.scaffold({
|
|
215
344
|
targetDir: target.targetDir,
|
|
216
345
|
pkgName: target.pkgName,
|
|
346
|
+
contractName: target.contractName ?? options.contractName,
|
|
347
|
+
});
|
|
348
|
+
await postCreateTasks(target.targetDir, {
|
|
349
|
+
skipInstall: options.skipInstall,
|
|
350
|
+
skipBuild: options.skipBuild ?? options.skipInstall,
|
|
217
351
|
});
|
|
218
|
-
await postCreateTasks(target.targetDir);
|
|
219
352
|
logGlobal('Scaffold complete. Install/build steps were attempted; ready to run!', colors.green);
|
|
220
353
|
}
|
|
354
|
+
export async function runCreatePackageCli(argv) {
|
|
355
|
+
const parsed = parseCreateCliArgs(argv);
|
|
356
|
+
if (parsed.help) {
|
|
357
|
+
printCreateHelp();
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
if (parsed.list) {
|
|
361
|
+
printVariantList();
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
if (parsed.describe) {
|
|
365
|
+
const variant = resolveVariant(parsed.describe);
|
|
366
|
+
if (!variant) {
|
|
367
|
+
throw new Error(`Unknown variant "${parsed.describe}". Use --list to see available templates.`);
|
|
368
|
+
}
|
|
369
|
+
printVariantDetails(variant);
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
const variant = resolveVariant(parsed.variant);
|
|
373
|
+
if (parsed.variant && !variant) {
|
|
374
|
+
throw new Error(`Unknown variant "${parsed.variant}". Use --list to see available templates.`);
|
|
375
|
+
}
|
|
376
|
+
const targetDir = parsed.targetDir
|
|
377
|
+
? path.resolve(workspaceRoot, parsed.targetDir)
|
|
378
|
+
: undefined;
|
|
379
|
+
await createRrrPackage({
|
|
380
|
+
variant,
|
|
381
|
+
targetDir,
|
|
382
|
+
pkgName: parsed.pkgName,
|
|
383
|
+
contractName: parsed.contractName,
|
|
384
|
+
skipInstall: parsed.skipInstall,
|
|
385
|
+
skipBuild: parsed.skipBuild ?? parsed.skipInstall,
|
|
386
|
+
});
|
|
387
|
+
}
|
|
@@ -155,7 +155,7 @@ export function basePackageFiles(options) {
|
|
|
155
155
|
'.gitignore': gitignoreFrom(options?.gitignoreEntries),
|
|
156
156
|
};
|
|
157
157
|
}
|
|
158
|
-
const SCRIPT_DESCRIPTIONS = {
|
|
158
|
+
export const SCRIPT_DESCRIPTIONS = {
|
|
159
159
|
dev: 'Watch and rebuild on change',
|
|
160
160
|
build: 'Type-check and emit to dist/',
|
|
161
161
|
typecheck: 'Type-check only (no emit)',
|
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
import { BASE_LINT_DEV_DEPENDENCIES, basePackageFiles, basePackageJson, buildReadme, baseScripts, baseTsConfig, writeFileIfMissing, } from '../shared.js';
|
|
2
|
+
const CLIENT_SCRIPTS = [
|
|
3
|
+
'dev',
|
|
4
|
+
'build',
|
|
5
|
+
'typecheck',
|
|
6
|
+
'lint',
|
|
7
|
+
'lint:fix',
|
|
8
|
+
'format',
|
|
9
|
+
'format:check',
|
|
10
|
+
'clean',
|
|
11
|
+
'test',
|
|
12
|
+
];
|
|
2
13
|
const CONTRACT_IMPORT_PLACEHOLDER = '@your-scope/contract';
|
|
3
14
|
export function clientIndexTs(contractImport) {
|
|
4
15
|
return `import { QueryClient } from '@tanstack/react-query'
|
|
@@ -35,17 +46,6 @@ export function clientPackageJson(name, contractName = CONTRACT_IMPORT_PLACEHOLD
|
|
|
35
46
|
});
|
|
36
47
|
}
|
|
37
48
|
function clientFiles(pkgName, contractImport) {
|
|
38
|
-
const scriptsForReadme = [
|
|
39
|
-
'dev',
|
|
40
|
-
'build',
|
|
41
|
-
'typecheck',
|
|
42
|
-
'lint',
|
|
43
|
-
'lint:fix',
|
|
44
|
-
'format',
|
|
45
|
-
'format:check',
|
|
46
|
-
'clean',
|
|
47
|
-
'test',
|
|
48
|
-
];
|
|
49
49
|
return {
|
|
50
50
|
'package.json': clientPackageJson(pkgName, contractImport),
|
|
51
51
|
'tsconfig.json': baseTsConfig({ lib: ['ES2020', 'DOM'], types: ['node'] }),
|
|
@@ -54,7 +54,7 @@ function clientFiles(pkgName, contractImport) {
|
|
|
54
54
|
'README.md': buildReadme({
|
|
55
55
|
name: pkgName,
|
|
56
56
|
description: 'Starter RRRoutes client scaffold.',
|
|
57
|
-
scripts:
|
|
57
|
+
scripts: CLIENT_SCRIPTS,
|
|
58
58
|
sections: [
|
|
59
59
|
{
|
|
60
60
|
title: 'Getting Started',
|
|
@@ -83,6 +83,13 @@ export const clientVariant = {
|
|
|
83
83
|
id: 'rrr-client',
|
|
84
84
|
label: 'rrr client',
|
|
85
85
|
defaultDir: 'packages/rrr-client',
|
|
86
|
+
summary: 'React Query-ready RRRoutes client bound to a shared contract import.',
|
|
87
|
+
keyFiles: ['src/index.ts', 'README.md'],
|
|
88
|
+
scripts: CLIENT_SCRIPTS,
|
|
89
|
+
notes: [
|
|
90
|
+
'Set the contract import via --contract or by editing src/index.ts.',
|
|
91
|
+
'Exports query client + typed route builders to plug into React apps.',
|
|
92
|
+
],
|
|
86
93
|
async scaffold(ctx) {
|
|
87
94
|
const contractImport = ctx.contractName ?? CONTRACT_IMPORT_PLACEHOLDER;
|
|
88
95
|
const files = clientFiles(ctx.pkgName, contractImport);
|
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
import { BASE_LINT_DEV_DEPENDENCIES, basePackageFiles, basePackageJson, buildReadme, baseScripts, baseTsConfig, writeFileIfMissing, } from '../shared.js';
|
|
2
|
+
const CONTRACT_SCRIPTS = [
|
|
3
|
+
'dev',
|
|
4
|
+
'build',
|
|
5
|
+
'typecheck',
|
|
6
|
+
'lint',
|
|
7
|
+
'lint:fix',
|
|
8
|
+
'format',
|
|
9
|
+
'format:check',
|
|
10
|
+
'clean',
|
|
11
|
+
'test',
|
|
12
|
+
];
|
|
2
13
|
export const CONTRACT_TS = `import { defineSocketEvents, finalize, resource } from '@emeryld/rrroutes-contract'
|
|
3
14
|
import { z } from 'zod'
|
|
4
15
|
|
|
@@ -90,17 +101,6 @@ function contractPackageJson(name) {
|
|
|
90
101
|
});
|
|
91
102
|
}
|
|
92
103
|
function contractFiles(pkgName) {
|
|
93
|
-
const scriptsForReadme = [
|
|
94
|
-
'dev',
|
|
95
|
-
'build',
|
|
96
|
-
'typecheck',
|
|
97
|
-
'lint',
|
|
98
|
-
'lint:fix',
|
|
99
|
-
'format',
|
|
100
|
-
'format:check',
|
|
101
|
-
'clean',
|
|
102
|
-
'test',
|
|
103
|
-
];
|
|
104
104
|
return {
|
|
105
105
|
'package.json': contractPackageJson(pkgName),
|
|
106
106
|
'tsconfig.json': baseTsConfig(),
|
|
@@ -109,7 +109,7 @@ function contractFiles(pkgName) {
|
|
|
109
109
|
'README.md': buildReadme({
|
|
110
110
|
name: pkgName,
|
|
111
111
|
description: 'Contract package scaffolded by manager-cli.',
|
|
112
|
-
scripts:
|
|
112
|
+
scripts: CONTRACT_SCRIPTS,
|
|
113
113
|
sections: [
|
|
114
114
|
{
|
|
115
115
|
title: 'Getting Started',
|
|
@@ -134,6 +134,12 @@ export const contractVariant = {
|
|
|
134
134
|
id: 'rrr-contract',
|
|
135
135
|
label: 'rrr contract',
|
|
136
136
|
defaultDir: 'packages/rrr-contract',
|
|
137
|
+
summary: 'Shared RRRoutes registry + sockets to be consumed by server/client packages.',
|
|
138
|
+
keyFiles: ['src/index.ts', 'README.md'],
|
|
139
|
+
scripts: CONTRACT_SCRIPTS,
|
|
140
|
+
notes: [
|
|
141
|
+
'Edit src/index.ts to define routes and socket events; exports registry/socket config.',
|
|
142
|
+
],
|
|
137
143
|
async scaffold(ctx) {
|
|
138
144
|
const files = contractFiles(ctx.pkgName);
|
|
139
145
|
for (const [relative, contents] of Object.entries(files)) {
|
|
@@ -1,4 +1,23 @@
|
|
|
1
1
|
import { BASE_LINT_DEV_DEPENDENCIES, basePackageFiles, basePackageJson, buildReadme, baseScripts, baseTsConfig, writeFileIfMissing, } from '../shared.js';
|
|
2
|
+
const DOCKER_SCRIPTS = [
|
|
3
|
+
'dev',
|
|
4
|
+
'build',
|
|
5
|
+
'typecheck',
|
|
6
|
+
'lint',
|
|
7
|
+
'lint:fix',
|
|
8
|
+
'format',
|
|
9
|
+
'format:check',
|
|
10
|
+
'clean',
|
|
11
|
+
'test',
|
|
12
|
+
'start',
|
|
13
|
+
'docker:build',
|
|
14
|
+
'docker:up',
|
|
15
|
+
'docker:dev',
|
|
16
|
+
'docker:logs',
|
|
17
|
+
'docker:stop',
|
|
18
|
+
'docker:clean',
|
|
19
|
+
'docker:reset',
|
|
20
|
+
];
|
|
2
21
|
function dockerPackageJson(name) {
|
|
3
22
|
return basePackageJson({
|
|
4
23
|
name,
|
|
@@ -77,25 +96,6 @@ CMD ["node", "dist/index.js"]
|
|
|
77
96
|
`;
|
|
78
97
|
}
|
|
79
98
|
function dockerFiles(pkgName) {
|
|
80
|
-
const scriptsForReadme = [
|
|
81
|
-
'dev',
|
|
82
|
-
'build',
|
|
83
|
-
'typecheck',
|
|
84
|
-
'lint',
|
|
85
|
-
'lint:fix',
|
|
86
|
-
'format',
|
|
87
|
-
'format:check',
|
|
88
|
-
'clean',
|
|
89
|
-
'test',
|
|
90
|
-
'start',
|
|
91
|
-
'docker:build',
|
|
92
|
-
'docker:up',
|
|
93
|
-
'docker:dev',
|
|
94
|
-
'docker:logs',
|
|
95
|
-
'docker:stop',
|
|
96
|
-
'docker:clean',
|
|
97
|
-
'docker:reset',
|
|
98
|
-
];
|
|
99
99
|
return {
|
|
100
100
|
'package.json': dockerPackageJson(pkgName),
|
|
101
101
|
'tsconfig.json': baseTsConfig({ types: ['node'] }),
|
|
@@ -107,7 +107,7 @@ function dockerFiles(pkgName) {
|
|
|
107
107
|
'README.md': buildReadme({
|
|
108
108
|
name: pkgName,
|
|
109
109
|
description: 'Dockerized service scaffolded by manager-cli.',
|
|
110
|
-
scripts:
|
|
110
|
+
scripts: DOCKER_SCRIPTS,
|
|
111
111
|
sections: [
|
|
112
112
|
{
|
|
113
113
|
title: 'Getting Started',
|
|
@@ -221,6 +221,18 @@ export const dockerVariant = {
|
|
|
221
221
|
id: 'rrr-docker',
|
|
222
222
|
label: 'dockerized service',
|
|
223
223
|
defaultDir: 'packages/rrr-docker',
|
|
224
|
+
summary: 'Express service plus Dockerfile and a helper CLI for local runs.',
|
|
225
|
+
keyFiles: [
|
|
226
|
+
'src/index.ts',
|
|
227
|
+
'scripts/docker.ts',
|
|
228
|
+
'Dockerfile',
|
|
229
|
+
'README.md',
|
|
230
|
+
],
|
|
231
|
+
scripts: DOCKER_SCRIPTS,
|
|
232
|
+
notes: [
|
|
233
|
+
'Use docker:dev or docker:up to build and run the container quickly.',
|
|
234
|
+
'scripts/docker.ts wraps common docker commands with consistent naming.',
|
|
235
|
+
],
|
|
224
236
|
async scaffold(ctx) {
|
|
225
237
|
const files = dockerFiles(ctx.pkgName);
|
|
226
238
|
for (const [relative, contents] of Object.entries(files)) {
|
|
@@ -1,4 +1,15 @@
|
|
|
1
1
|
import { BASE_LINT_DEV_DEPENDENCIES, basePackageFiles, basePackageJson, buildReadme, baseScripts, baseTsConfig, writeFileIfMissing, } from '../shared.js';
|
|
2
|
+
const EMPTY_SCRIPTS = [
|
|
3
|
+
'dev',
|
|
4
|
+
'build',
|
|
5
|
+
'typecheck',
|
|
6
|
+
'lint',
|
|
7
|
+
'lint:fix',
|
|
8
|
+
'format',
|
|
9
|
+
'format:check',
|
|
10
|
+
'clean',
|
|
11
|
+
'test',
|
|
12
|
+
];
|
|
2
13
|
function emptyPackageJson(name) {
|
|
3
14
|
return basePackageJson({
|
|
4
15
|
name,
|
|
@@ -9,17 +20,6 @@ function emptyPackageJson(name) {
|
|
|
9
20
|
});
|
|
10
21
|
}
|
|
11
22
|
function emptyFiles(pkgName) {
|
|
12
|
-
const scriptsForReadme = [
|
|
13
|
-
'dev',
|
|
14
|
-
'build',
|
|
15
|
-
'typecheck',
|
|
16
|
-
'lint',
|
|
17
|
-
'lint:fix',
|
|
18
|
-
'format',
|
|
19
|
-
'format:check',
|
|
20
|
-
'clean',
|
|
21
|
-
'test',
|
|
22
|
-
];
|
|
23
23
|
return {
|
|
24
24
|
'package.json': emptyPackageJson(pkgName),
|
|
25
25
|
'tsconfig.json': baseTsConfig({ types: ['node'] }),
|
|
@@ -28,7 +28,7 @@ function emptyFiles(pkgName) {
|
|
|
28
28
|
'README.md': buildReadme({
|
|
29
29
|
name: pkgName,
|
|
30
30
|
description: 'Empty package scaffolded by manager-cli.',
|
|
31
|
-
scripts:
|
|
31
|
+
scripts: EMPTY_SCRIPTS,
|
|
32
32
|
sections: [
|
|
33
33
|
{
|
|
34
34
|
title: 'Getting Started',
|
|
@@ -50,6 +50,10 @@ export const emptyVariant = {
|
|
|
50
50
|
id: 'rrr-empty',
|
|
51
51
|
label: 'empty package',
|
|
52
52
|
defaultDir: 'packages/rrr-empty',
|
|
53
|
+
summary: 'Minimal TypeScript package with lint/format/test scaffolding.',
|
|
54
|
+
keyFiles: ['src/index.ts', 'README.md'],
|
|
55
|
+
scripts: EMPTY_SCRIPTS,
|
|
56
|
+
notes: ['Start coding in src/index.ts; everything else is wired up.'],
|
|
53
57
|
async scaffold(ctx) {
|
|
54
58
|
const files = emptyFiles(ctx.pkgName);
|
|
55
59
|
for (const [relative, contents] of Object.entries(files)) {
|
|
@@ -4,6 +4,25 @@ import { clientVariant } from './client.js';
|
|
|
4
4
|
import { serverVariant } from './server.js';
|
|
5
5
|
import { dockerVariant } from './docker.js';
|
|
6
6
|
import { contractVariant } from './contract.js';
|
|
7
|
+
const FULLSTACK_SCRIPTS = [
|
|
8
|
+
'setup',
|
|
9
|
+
'dev',
|
|
10
|
+
'build',
|
|
11
|
+
'typecheck',
|
|
12
|
+
'lint',
|
|
13
|
+
'lint:fix',
|
|
14
|
+
'lint-staged',
|
|
15
|
+
'format',
|
|
16
|
+
'format:check',
|
|
17
|
+
'test',
|
|
18
|
+
'clean',
|
|
19
|
+
'prepare',
|
|
20
|
+
'docker:up',
|
|
21
|
+
'docker:dev',
|
|
22
|
+
'docker:logs',
|
|
23
|
+
'docker:stop',
|
|
24
|
+
'docker:reset',
|
|
25
|
+
];
|
|
7
26
|
function deriveNames(baseName) {
|
|
8
27
|
const normalized = baseName.trim();
|
|
9
28
|
return {
|
|
@@ -97,6 +116,21 @@ export const fullstackVariant = {
|
|
|
97
116
|
id: 'rrr-fullstack',
|
|
98
117
|
label: 'rrr fullstack (contract + server + client + docker)',
|
|
99
118
|
defaultDir: 'rrrfull-stack',
|
|
119
|
+
summary: 'End-to-end RRRoutes stack: pnpm workspace with contract, server, client, and docker helper packages.',
|
|
120
|
+
keyFiles: [
|
|
121
|
+
'package.json',
|
|
122
|
+
'pnpm-workspace.yaml',
|
|
123
|
+
'docker-compose.yml',
|
|
124
|
+
'packages/<name>-contract',
|
|
125
|
+
'packages/<name>-server',
|
|
126
|
+
'packages/<name>-client',
|
|
127
|
+
'packages/<name>-docker',
|
|
128
|
+
],
|
|
129
|
+
scripts: FULLSTACK_SCRIPTS,
|
|
130
|
+
notes: [
|
|
131
|
+
'Generates four packages and a workspace root; great starting point when you need the whole stack.',
|
|
132
|
+
'Use --name to control the workspace prefix (default is the target folder name).',
|
|
133
|
+
],
|
|
100
134
|
async scaffold(ctx) {
|
|
101
135
|
const baseName = ctx.pkgName;
|
|
102
136
|
const names = deriveNames(baseName);
|
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
import { BASE_LINT_DEV_DEPENDENCIES, basePackageFiles, basePackageJson, buildReadme, baseScripts, baseTsConfig, writeFileIfMissing, } from '../shared.js';
|
|
2
|
+
const SERVER_SCRIPTS = [
|
|
3
|
+
'dev',
|
|
4
|
+
'build',
|
|
5
|
+
'typecheck',
|
|
6
|
+
'lint',
|
|
7
|
+
'lint:fix',
|
|
8
|
+
'format',
|
|
9
|
+
'format:check',
|
|
10
|
+
'clean',
|
|
11
|
+
'test',
|
|
12
|
+
'start',
|
|
13
|
+
];
|
|
2
14
|
const CONTRACT_IMPORT_PLACEHOLDER = '@your-scope/contract';
|
|
3
15
|
export function serverIndexTs(contractImport) {
|
|
4
16
|
return `import 'dotenv/config'
|
|
@@ -70,18 +82,6 @@ export function serverPackageJson(name, contractName = CONTRACT_IMPORT_PLACEHOLD
|
|
|
70
82
|
});
|
|
71
83
|
}
|
|
72
84
|
function serverFiles(pkgName, contractImport) {
|
|
73
|
-
const scriptsForReadme = [
|
|
74
|
-
'dev',
|
|
75
|
-
'build',
|
|
76
|
-
'typecheck',
|
|
77
|
-
'lint',
|
|
78
|
-
'lint:fix',
|
|
79
|
-
'format',
|
|
80
|
-
'format:check',
|
|
81
|
-
'clean',
|
|
82
|
-
'test',
|
|
83
|
-
'start',
|
|
84
|
-
];
|
|
85
85
|
return {
|
|
86
86
|
'package.json': serverPackageJson(pkgName, contractImport),
|
|
87
87
|
'tsconfig.json': baseTsConfig({ types: ['node'] }),
|
|
@@ -91,7 +91,7 @@ function serverFiles(pkgName, contractImport) {
|
|
|
91
91
|
'README.md': buildReadme({
|
|
92
92
|
name: pkgName,
|
|
93
93
|
description: 'Starter RRRoutes server scaffold.',
|
|
94
|
-
scripts:
|
|
94
|
+
scripts: SERVER_SCRIPTS,
|
|
95
95
|
sections: [
|
|
96
96
|
{
|
|
97
97
|
title: 'Getting Started',
|
|
@@ -121,6 +121,13 @@ export const serverVariant = {
|
|
|
121
121
|
id: 'rrr-server',
|
|
122
122
|
label: 'rrr server',
|
|
123
123
|
defaultDir: 'packages/rrr-server',
|
|
124
|
+
summary: 'Express + RRRoutes server wired to a contract import; ships a health route.',
|
|
125
|
+
keyFiles: ['src/index.ts', '.env.example', 'README.md'],
|
|
126
|
+
scripts: SERVER_SCRIPTS,
|
|
127
|
+
notes: [
|
|
128
|
+
'Set the contract import via --contract or by editing src/index.ts.',
|
|
129
|
+
'Includes start script for compiled output and dotenv-ready dev script.',
|
|
130
|
+
],
|
|
124
131
|
async scaffold(ctx) {
|
|
125
132
|
const contractImport = ctx.contractName ?? CONTRACT_IMPORT_PLACEHOLDER;
|
|
126
133
|
const files = serverFiles(ctx.pkgName, contractImport);
|
package/dist/publish.js
CHANGED
|
@@ -5,7 +5,7 @@ import { getOrderedPackages, loadPackages, resolvePackage } from './packages.js'
|
|
|
5
5
|
import { releaseMultiple, releaseSingle, } from './release.js';
|
|
6
6
|
import { ensureWorkingTreeCommitted } from './preflight.js';
|
|
7
7
|
import { publishCliState } from './prompts.js';
|
|
8
|
-
import { createRrrPackage } from './create-package/index.js';
|
|
8
|
+
import { createRrrPackage, runCreatePackageCli, } from './create-package/index.js';
|
|
9
9
|
import { colors, logGlobal } from './utils/log.js';
|
|
10
10
|
function resolveTargetsFromArg(packages, arg) {
|
|
11
11
|
if (arg.toLowerCase() === 'all')
|
|
@@ -121,6 +121,14 @@ async function runPackageSelectionLoop(packages, helperArgs) {
|
|
|
121
121
|
}
|
|
122
122
|
async function main() {
|
|
123
123
|
const cliArgs = process.argv.slice(2);
|
|
124
|
+
if (cliArgs[0] === 'create') {
|
|
125
|
+
await runCreatePackageCli(cliArgs.slice(1));
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
if (cliArgs[0] === 'templates') {
|
|
129
|
+
await runCreatePackageCli(['--list']);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
124
132
|
const parsed = parseCliArgs(cliArgs);
|
|
125
133
|
const packages = await loadPackages();
|
|
126
134
|
// If user provided non-interactive flags, run headless path
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@emeryld/manager",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "Interactive manager for pnpm monorepos (update/test/build/publish).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -30,7 +30,8 @@
|
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@types/node": "^20.17.0",
|
|
32
32
|
"@types/semver": "^7.7.1",
|
|
33
|
-
"cross-env": "^7.0.3"
|
|
33
|
+
"cross-env": "^7.0.3",
|
|
34
|
+
"@emeryld/manager": "^0.4.1"
|
|
34
35
|
},
|
|
35
36
|
"ts-node": {
|
|
36
37
|
"esm": true,
|
|
@@ -41,6 +42,6 @@
|
|
|
41
42
|
"scripts": {
|
|
42
43
|
"build": "tsc -p tsconfig.base.json && node scripts/copy-manager-cli.mjs",
|
|
43
44
|
"typecheck": "tsc -p tsconfig.base.json --noEmit",
|
|
44
|
-
"test": "cross-env TS_NODE_PROJECT=tsconfig.test.json node --loader ts-node/esm test
|
|
45
|
+
"test": "cross-env TS_NODE_PROJECT=tsconfig.test.json node --loader ts-node/esm --test test/*.test.ts"
|
|
45
46
|
}
|
|
46
47
|
}
|