@trut/neew 0.0.0-stage → 1.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 CHANGED
@@ -1,3 +1,91 @@
1
- # Temporary Holding Version
1
+ # neew
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ `neew` turns remote generator repositories into reusable commands. Register a
4
+ repository once, invoke its generators by name from any directory, and let a
5
+ small fetch engine reuse a local cache until the remote ref actually moves.
6
+
7
+ ## Documentation
8
+
9
+ Start with [docs/getting-started.md](docs/getting-started.md), then read the module for the domain you need:
10
+
11
+ - [generator-repository.md](docs/generator-repository.md): the repository contract
12
+ - [repository-registry.md](docs/repository-registry.md): registering, removing, listing, collisions
13
+ - [source-fetching.md](docs/source-fetching.md): links, change detection, caching, offline use
14
+ - [generator-invocation.md](docs/generator-invocation.md): running generators, parameters, targets
15
+
16
+ ## Install
17
+
18
+ ```sh
19
+ pnpm add -g @trut/neew
20
+ ```
21
+
22
+ ## The generator repository contract
23
+
24
+ Any Git repository becomes a `neew` generator repository by adding a single
25
+ barrel file, `generators.ts`, at its root. The barrel default-exports a
26
+ `generators` array of paths or glob patterns resolved relative to the repository
27
+ root. Author it with the `defineGenerators` helper exported by this package:
28
+
29
+ <!-- example:barrel -->
30
+
31
+ ```ts
32
+ import { defineGenerators } from '@trut/neew';
33
+
34
+ export default defineGenerators({
35
+ generators: ['./generators/*.generator.ts'],
36
+ });
37
+ ```
38
+
39
+ Each pattern must match at least one generator module. A module declares a
40
+ `@trut/gen` generator with a unique `name`, an optional `description`, and the
41
+ prompts that become the parameters neew accepts. For example
42
+ `generators/component.generator.ts`:
43
+
44
+ <!-- example:generator -->
45
+
46
+ ```ts
47
+ import { addTemplates, defineGenerator, text } from '@trut/gen';
48
+
49
+ export default defineGenerator({
50
+ name: 'component',
51
+ description: 'Create a component',
52
+ basePath: 'src',
53
+ prompts: {
54
+ name: text({ message: 'Component name', placeholder: 'UserProfile' }),
55
+ },
56
+ actions: [
57
+ addTemplates({
58
+ from: 'templates/component',
59
+ to: 'components/<%= it.format.kebabCase(it.answers.name) %>',
60
+ }),
61
+ ],
62
+ });
63
+ ```
64
+
65
+ Generator names must be unique within the repository; two resolved generators
66
+ sharing a name fail resolution.
67
+
68
+ ## CLI usage
69
+
70
+ ```sh
71
+ neew add <link> # register a repository and index its generators
72
+ neew remove <link> # unregister a repository and drop its cache
73
+ neew list # list repositories and their generators (offline)
74
+ neew create <generator> [options] # run a generator
75
+ neew <generator> [options] # shortcut for create
76
+ neew <generator> --help # list a generator's parameters
77
+ neew # list the commands and available generators
78
+ ```
79
+
80
+ Options:
81
+
82
+ - `--target <dir>`: write generated files into `<dir>` instead of the current
83
+ directory.
84
+ - `--dry-run`: report the files that would be written without writing them.
85
+ - `--force`: replace existing files that collide with generated output.
86
+ - `-h, --help`, `-v, --version`.
87
+
88
+ Every generator prompt is exposed as a CLI flag (for example a `name` prompt
89
+ becomes `--name`). Missing values are prompted interactively on a TTY and fail
90
+ otherwise. The state lives in `~/.config/neew/neew.json` (registry) and
91
+ `~/.cache/neew/**` (fetched repositories).
package/dist/cli.d.mts ADDED
@@ -0,0 +1 @@
1
+ export {}
package/dist/cli.mjs ADDED
@@ -0,0 +1,895 @@
1
+ #!/usr/bin/env node
2
+ import { PromptCancelledError } from "@trut/gen";
3
+ import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
4
+ import { dirname, join, relative, resolve } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { discoverGenerators, flagNameFor, parseGlobalFlags, parsePromptValues, resolveGeneratorModules, resolvePrompts, runGenerator } from "@trut/gen/runtime";
7
+ import { homedir } from "node:os";
8
+ import { createJiti } from "jiti";
9
+ import { randomUUID } from "node:crypto";
10
+ import { Readable } from "node:stream";
11
+ import { pipeline } from "node:stream/promises";
12
+ import { createGunzip } from "node:zlib";
13
+ import * as tar from "tar";
14
+ import git from "isomorphic-git";
15
+ import http from "isomorphic-git/http/node";
16
+ import { confirm, isCancel, multiselect, password, select, text } from "@clack/prompts";
17
+ //#region src/config/paths.ts
18
+ function home(env) {
19
+ return env.HOME ?? env.USERPROFILE ?? homedir();
20
+ }
21
+ function resolveConfigRoot(env = process.env) {
22
+ if (env.NEEW_CONFIG_DIR) return env.NEEW_CONFIG_DIR;
23
+ if (env.XDG_CONFIG_HOME) return join(env.XDG_CONFIG_HOME, "neew");
24
+ if (process.platform === "darwin") return join(home(env), "Library", "Application Support", "neew");
25
+ if (process.platform === "win32") return join(env.APPDATA ?? join(home(env), "AppData", "Roaming"), "neew");
26
+ return join(home(env), ".config", "neew");
27
+ }
28
+ function resolveCacheRoot(env = process.env) {
29
+ if (env.NEEW_CACHE_DIR) return env.NEEW_CACHE_DIR;
30
+ if (env.XDG_CACHE_HOME) return join(env.XDG_CACHE_HOME, "neew");
31
+ if (process.platform === "darwin") return join(home(env), "Library", "Caches", "neew");
32
+ if (process.platform === "win32") return join(env.LOCALAPPDATA ?? join(home(env), "AppData", "Local"), "neew");
33
+ return join(home(env), ".cache", "neew");
34
+ }
35
+ //#endregion
36
+ //#region src/config/create-loader.ts
37
+ const FRAMEWORK_SPECIFIERS = [
38
+ "@trut/gen",
39
+ "@trut/gen/runtime",
40
+ "@trut/neew"
41
+ ];
42
+ function resolveSpecifier(specifier) {
43
+ try {
44
+ const url = import.meta.resolve(specifier);
45
+ return url.startsWith("file:") ? fileURLToPath(url) : url;
46
+ } catch {
47
+ return;
48
+ }
49
+ }
50
+ function createRepositoryJiti(baseDir) {
51
+ const alias = {};
52
+ for (const specifier of FRAMEWORK_SPECIFIERS) {
53
+ const resolved = resolveSpecifier(specifier);
54
+ if (resolved) alias[specifier] = resolved;
55
+ }
56
+ return createJiti(baseDir, {
57
+ interopDefault: true,
58
+ alias,
59
+ moduleCache: false,
60
+ fsCache: false
61
+ });
62
+ }
63
+ //#endregion
64
+ //#region src/config/load-barrel.ts
65
+ const BARREL_FILENAME = "generators.ts";
66
+ function pickConfig(moduleExports) {
67
+ if (moduleExports && typeof moduleExports === "object") {
68
+ const record = moduleExports;
69
+ if (Array.isArray(record.generators)) return moduleExports;
70
+ if (record.default && typeof record.default === "object") {
71
+ const fallback = record.default;
72
+ if (Array.isArray(fallback.generators)) return fallback;
73
+ }
74
+ }
75
+ return moduleExports;
76
+ }
77
+ async function loadBarrel(dir, options = {}) {
78
+ const path = join(dir, BARREL_FILENAME);
79
+ if (!existsSync(path)) throw new Error(`Repository contract violated: no ${BARREL_FILENAME} found at ${dir}. Every generator repository must provide a root ${BARREL_FILENAME} exporting a "generators" array.`);
80
+ const jiti = options.jiti ?? createRepositoryJiti(path);
81
+ let moduleExports;
82
+ try {
83
+ moduleExports = await jiti.import(path);
84
+ } catch (error) {
85
+ const message = error instanceof Error ? error.message : String(error);
86
+ throw new Error(`Failed to load ${BARREL_FILENAME} at ${path}: ${message}`);
87
+ }
88
+ const config = pickConfig(moduleExports);
89
+ if (!config || !Array.isArray(config.generators)) throw new Error(`Invalid ${BARREL_FILENAME} at ${path}: expected a default export whose "generators" is an array of glob patterns (strings).`);
90
+ if (config.generators.find((entry) => typeof entry !== "string") !== void 0) throw new Error(`Invalid ${BARREL_FILENAME} at ${path}: "generators" must be an array of glob patterns (strings).`);
91
+ return {
92
+ config,
93
+ path,
94
+ dir
95
+ };
96
+ }
97
+ //#endregion
98
+ //#region src/config/registry-file.ts
99
+ const REGISTRY_FILENAME = "neew.json";
100
+ function registryFilePath(env = process.env) {
101
+ return join(resolveConfigRoot(env), REGISTRY_FILENAME);
102
+ }
103
+ function emptyRegistry() {
104
+ return { repositories: {} };
105
+ }
106
+ function readRegistry(env = process.env) {
107
+ const path = registryFilePath(env);
108
+ if (!existsSync(path)) return emptyRegistry();
109
+ const text = readFileSync(path, "utf8");
110
+ let parsed;
111
+ try {
112
+ parsed = JSON.parse(text);
113
+ } catch (error) {
114
+ const message = error instanceof Error ? error.message : String(error);
115
+ throw new Error(`Failed to parse the neew registry at ${path}: ${message}. Fix or remove the file; neew will not overwrite it.`);
116
+ }
117
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error(`Invalid neew registry at ${path}: expected a JSON object.`);
118
+ const repositories = parsed.repositories;
119
+ if (!repositories || typeof repositories !== "object" || Array.isArray(repositories)) throw new Error(`Invalid neew registry at ${path}: expected a "repositories" object.`);
120
+ return { repositories };
121
+ }
122
+ function writeRegistry(file, env = process.env) {
123
+ const path = registryFilePath(env);
124
+ mkdirSync(dirname(path), { recursive: true });
125
+ const temp = `${path}.tmp-${randomUUID()}`;
126
+ writeFileSync(temp, `${JSON.stringify(file, null, 2)}\n`, "utf8");
127
+ renameSync(temp, path);
128
+ }
129
+ //#endregion
130
+ //#region src/engine/adapters/github.ts
131
+ function githubTarballUrl(source, ref) {
132
+ return `https://codeload.github.com/${source.owner}/${source.repo}/tar.gz/${encodeURIComponent(ref)}`;
133
+ }
134
+ function subdirFilter(subdir) {
135
+ return (path) => {
136
+ const relative = path.split("/").slice(1).join("/");
137
+ return relative === subdir || relative.startsWith(`${subdir}/`);
138
+ };
139
+ }
140
+ function tarballStrip(subdir) {
141
+ return subdir ? 1 + subdir.split("/").filter(Boolean).length : 1;
142
+ }
143
+ async function downloadGithub(source, dest, options, deps) {
144
+ const url = githubTarballUrl(source, options.ref);
145
+ const response = await (deps.fetch ?? globalThis.fetch)(url);
146
+ if (!response.ok) throw new Error(`Failed to download ${url}: ${response.status} ${response.statusText}`);
147
+ if (!response.body) throw new Error(`Failed to download ${url}: empty response body.`);
148
+ mkdirSync(dest, { recursive: true });
149
+ const filter = options.subdir ? subdirFilter(options.subdir) : void 0;
150
+ await pipeline(Readable.fromWeb(response.body), createGunzip(), tar.extract({
151
+ cwd: dest,
152
+ strip: tarballStrip(options.subdir),
153
+ ...filter ? { filter } : {}
154
+ }));
155
+ }
156
+ const githubAdapter = {
157
+ host: "github.com",
158
+ download: downloadGithub
159
+ };
160
+ //#endregion
161
+ //#region src/engine/adapters/index.ts
162
+ const SUPPORTED_HOSTS = ["github.com"];
163
+ var UnsupportedHostError = class extends Error {
164
+ host;
165
+ constructor(host) {
166
+ super(`Unsupported host "${host}". Supported hosts: ${SUPPORTED_HOSTS.join(", ")}.`);
167
+ this.name = "UnsupportedHostError";
168
+ this.host = host;
169
+ }
170
+ };
171
+ function getDownloadAdapter(host) {
172
+ if (host === githubAdapter.host || host === `www.${githubAdapter.host}`) return githubAdapter;
173
+ throw new UnsupportedHostError(host);
174
+ }
175
+ //#endregion
176
+ //#region src/engine/cache.ts
177
+ const METADATA_FILENAME = ".neew.json";
178
+ function sourceRefName(source) {
179
+ return source.ref ?? "HEAD";
180
+ }
181
+ function cacheSourcesDir(cacheRoot) {
182
+ return join(cacheRoot, "sources");
183
+ }
184
+ function cacheTmpDir(cacheRoot) {
185
+ return join(cacheRoot, "tmp");
186
+ }
187
+ function cacheEntryDir(cacheRoot, source) {
188
+ return join(cacheSourcesDir(cacheRoot), source.host, source.owner, `${source.repo}@${encodeURIComponent(sourceRefName(source))}`);
189
+ }
190
+ function readMetadata(entryDir) {
191
+ const path = join(entryDir, METADATA_FILENAME);
192
+ if (!existsSync(path)) return void 0;
193
+ try {
194
+ const parsed = JSON.parse(readFileSync(path, "utf8"));
195
+ if (!parsed || typeof parsed.commit !== "string") return void 0;
196
+ return parsed;
197
+ } catch {
198
+ return;
199
+ }
200
+ }
201
+ function writeMetadata(entryDir, metadata) {
202
+ mkdirSync(entryDir, { recursive: true });
203
+ writeFileSync(join(entryDir, METADATA_FILENAME), `${JSON.stringify(metadata, null, 2)}\n`, "utf8");
204
+ }
205
+ function removeCacheEntry(cacheRoot, source) {
206
+ rmSync(cacheEntryDir(cacheRoot, source), {
207
+ recursive: true,
208
+ force: true
209
+ });
210
+ }
211
+ //#endregion
212
+ //#region src/engine/resolve-ref.ts
213
+ function refCandidates(ref) {
214
+ if (ref.startsWith("refs/")) return [ref];
215
+ return [`refs/heads/${ref}`, `refs/tags/${ref}`];
216
+ }
217
+ function pickCommit(refs, source) {
218
+ if (source.ref) {
219
+ for (const candidate of refCandidates(source.ref)) {
220
+ const match = refs.find((entry) => entry.ref === candidate);
221
+ if (match) return match.peeled ?? match.oid;
222
+ }
223
+ const suffix = refs.find((entry) => entry.ref === source.ref || entry.ref.endsWith(`/${source.ref}`));
224
+ if (suffix) return suffix.peeled ?? suffix.oid;
225
+ throw new Error(`Ref "${source.ref}" was not found at ${source.url}.`);
226
+ }
227
+ const head = refs.find((entry) => entry.ref === "HEAD");
228
+ if (head?.target) {
229
+ const branch = refs.find((entry) => entry.ref === head.target);
230
+ if (branch) return branch.peeled ?? branch.oid;
231
+ }
232
+ const fallback = refs.find((entry) => entry.ref === "refs/heads/main" || entry.ref === "refs/heads/master");
233
+ if (fallback) return fallback.peeled ?? fallback.oid;
234
+ throw new Error(`Could not determine the default branch for ${source.url}.`);
235
+ }
236
+ async function resolveCommit(source, deps = {}) {
237
+ return pickCommit(await (deps.listServerRefs ?? git.listServerRefs)({
238
+ http: deps.http ?? http,
239
+ url: source.url,
240
+ symrefs: true,
241
+ peelTags: true,
242
+ ...deps.corsProxy ? { corsProxy: deps.corsProxy } : {}
243
+ }), source);
244
+ }
245
+ //#endregion
246
+ //#region src/engine/ensure.ts
247
+ function swapIntoPlace(entryDir, stagingDir) {
248
+ mkdirSync(dirname(entryDir), { recursive: true });
249
+ const backup = `${entryDir}.bak-${randomUUID()}`;
250
+ const hadPrevious = existsSync(entryDir);
251
+ if (hadPrevious) renameSync(entryDir, backup);
252
+ try {
253
+ renameSync(stagingDir, entryDir);
254
+ } catch (error) {
255
+ if (hadPrevious) renameSync(backup, entryDir);
256
+ throw error;
257
+ }
258
+ if (hadPrevious) rmSync(backup, {
259
+ recursive: true,
260
+ force: true
261
+ });
262
+ }
263
+ async function ensure(source, deps) {
264
+ const entryDir = cacheEntryDir(deps.cacheRoot, source);
265
+ const existing = existsSync(entryDir) ? readMetadata(entryDir) : void 0;
266
+ const resolve = deps.resolveCommit ?? ((target) => resolveCommit(target, deps.resolveDeps));
267
+ let commit;
268
+ try {
269
+ commit = await resolve(source);
270
+ } catch (error) {
271
+ if (existing) {
272
+ deps.warn?.(`Could not check ${source.url} for updates (${error instanceof Error ? error.message : String(error)}); using the cached copy.`);
273
+ return {
274
+ dir: entryDir,
275
+ metadata: existing,
276
+ changed: false,
277
+ offline: true
278
+ };
279
+ }
280
+ throw error;
281
+ }
282
+ if (existing && existing.commit === commit) return {
283
+ dir: entryDir,
284
+ metadata: existing,
285
+ changed: false,
286
+ offline: false
287
+ };
288
+ const staging = join(cacheTmpDir(deps.cacheRoot), `${source.repo}-${randomUUID()}`);
289
+ mkdirSync(staging, { recursive: true });
290
+ const download = deps.download ?? ((target, dest, options) => {
291
+ return getDownloadAdapter(target.host).download(target, dest, options, deps.downloadDeps ?? {});
292
+ });
293
+ try {
294
+ await download(source, staging, {
295
+ ref: commit,
296
+ ...source.subdir ? { subdir: source.subdir } : {}
297
+ });
298
+ const metadata = {
299
+ url: source.url,
300
+ ref: source.ref ?? null,
301
+ subdir: source.subdir ?? null,
302
+ commit,
303
+ fetchedAt: (/* @__PURE__ */ new Date()).toISOString()
304
+ };
305
+ writeMetadata(staging, metadata);
306
+ swapIntoPlace(entryDir, staging);
307
+ return {
308
+ dir: entryDir,
309
+ metadata,
310
+ changed: true,
311
+ offline: false
312
+ };
313
+ } catch (error) {
314
+ rmSync(staging, {
315
+ recursive: true,
316
+ force: true
317
+ });
318
+ throw error;
319
+ }
320
+ }
321
+ //#endregion
322
+ //#region src/engine/source.ts
323
+ const HOST_ALIASES = {
324
+ gh: "github.com",
325
+ github: "github.com",
326
+ gl: "gitlab.com",
327
+ gitlab: "gitlab.com",
328
+ bb: "bitbucket.org",
329
+ bitbucket: "bitbucket.org"
330
+ };
331
+ const SCHEME_PATTERN = /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//;
332
+ const PREFIX_PATTERN = /^([a-zA-Z][a-zA-Z0-9+.-]*):/;
333
+ function cleanSubdir(value) {
334
+ if (!value) return void 0;
335
+ const trimmed = value.replace(/^\/+/, "").replace(/\/+$/, "");
336
+ return trimmed === "" ? void 0 : trimmed;
337
+ }
338
+ function parseSource(input) {
339
+ const original = input.trim();
340
+ if (original === "") throw new Error("A repository link is required.");
341
+ let rest = original;
342
+ let ref;
343
+ const hash = rest.indexOf("#");
344
+ if (hash !== -1) {
345
+ ref = rest.slice(hash + 1).trim() || void 0;
346
+ rest = rest.slice(0, hash);
347
+ }
348
+ let subdir;
349
+ const separator = rest.indexOf("::");
350
+ if (separator !== -1) {
351
+ subdir = cleanSubdir(rest.slice(separator + 2));
352
+ rest = rest.slice(0, separator);
353
+ }
354
+ rest = rest.replace(SCHEME_PATTERN, "").replace(/^\/+/, "");
355
+ let host;
356
+ const prefix = PREFIX_PATTERN.exec(rest);
357
+ if (prefix) {
358
+ const alias = HOST_ALIASES[prefix[1].toLowerCase()];
359
+ if (alias) {
360
+ host = alias;
361
+ rest = rest.slice(prefix[0].length).replace(/^\/+/, "");
362
+ }
363
+ }
364
+ const segments = rest.split("/").map((segment) => segment.trim()).filter(Boolean);
365
+ if (segments.length === 0) throw new Error(`Could not parse repository link "${original}".`);
366
+ if (!host) {
367
+ if (segments[0].includes(".") && segments.length >= 3) host = segments.shift().toLowerCase();
368
+ else host = "github.com";
369
+ }
370
+ host = host.toLowerCase();
371
+ const owner = segments[0];
372
+ if (!owner) throw new Error(`Could not parse a repository owner from "${original}".`);
373
+ const rawRepo = segments[1];
374
+ if (!rawRepo) throw new Error(`Could not parse a repository name from "${original}".`);
375
+ const repo = rawRepo.replace(/\.git$/i, "");
376
+ if (!subdir && segments.length > 2) subdir = cleanSubdir(segments.slice(2).join("/"));
377
+ return {
378
+ host,
379
+ owner,
380
+ repo,
381
+ ...ref ? { ref } : {},
382
+ ...subdir ? { subdir } : {},
383
+ url: `https://${host}/${owner}/${repo}`
384
+ };
385
+ }
386
+ function canonicalSource(source) {
387
+ let identity = `${source.host}/${source.owner}/${source.repo}`;
388
+ if (source.subdir) identity += `::${source.subdir}`;
389
+ if (source.ref) identity += `#${source.ref}`;
390
+ return identity;
391
+ }
392
+ //#endregion
393
+ //#region src/registry/operations.ts
394
+ function sourceFromEntry(entry) {
395
+ return {
396
+ ...parseSource(entry.url),
397
+ ...entry.ref ? { ref: entry.ref } : {},
398
+ ...entry.subdir ? { subdir: entry.subdir } : {}
399
+ };
400
+ }
401
+ async function indexRepository(dir, patterns, jiti) {
402
+ for (const pattern of patterns) if ((await resolveGeneratorModules([pattern], dir)).length === 0) throw new Error(`Pattern "${pattern}" matched no files in ${dir}. Every pattern in the generators barrel must resolve to at least one generator module.`);
403
+ const discovered = await discoverGenerators({
404
+ configDir: dir,
405
+ patterns,
406
+ jiti
407
+ });
408
+ if (discovered.length === 0) throw new Error(`No generators were discovered in ${dir}.`);
409
+ return discovered.map((entry) => ({
410
+ name: entry.generator.name,
411
+ path: relative(dir, entry.modulePath),
412
+ ...entry.generator.description ? { description: entry.generator.description } : {}
413
+ }));
414
+ }
415
+ function collisionKey(sourceId, generatorPath) {
416
+ return `${sourceId}::${generatorPath}`;
417
+ }
418
+ function findCollisions(registry, canonical, indexed) {
419
+ const names = /* @__PURE__ */ new Map();
420
+ const paths = /* @__PURE__ */ new Map();
421
+ for (const [sourceId, entry] of Object.entries(registry.repositories)) {
422
+ if (sourceId === canonical) continue;
423
+ for (const generator of entry.generators) {
424
+ names.set(generator.name, {
425
+ entry,
426
+ source: sourceId
427
+ });
428
+ paths.set(collisionKey(sourceId, generator.path), { source: sourceId });
429
+ }
430
+ }
431
+ const conflicts = [];
432
+ for (const generator of indexed) {
433
+ const byName = names.get(generator.name);
434
+ const byPath = paths.get(collisionKey(canonical, generator.path));
435
+ const existing = byName ?? byPath;
436
+ if (!existing) continue;
437
+ conflicts.push({
438
+ name: generator.name,
439
+ path: generator.path,
440
+ ...generator.description ? { description: generator.description } : {},
441
+ existingSource: existing.source
442
+ });
443
+ }
444
+ return conflicts;
445
+ }
446
+ var CollisionError = class extends Error {
447
+ conflicts;
448
+ constructor(conflicts) {
449
+ const lines = conflicts.map((conflict) => ` - ${conflict.name}${conflict.description ? ` (${conflict.description})` : ""} conflicts with ${conflict.existingSource}`).join("\n");
450
+ super(`Repository cannot be added: generator name collision(s):\n${lines}`);
451
+ this.name = "CollisionError";
452
+ this.conflicts = conflicts;
453
+ }
454
+ };
455
+ async function addSource(link, deps) {
456
+ const source = parseSource(link);
457
+ const canonical = canonicalSource(source);
458
+ const registry = readRegistry(deps.env);
459
+ const ensured = await ensure(source, deps);
460
+ try {
461
+ const jiti = createRepositoryJiti(ensured.dir);
462
+ const barrel = await loadBarrel(ensured.dir, { jiti });
463
+ const generators = await indexRepository(ensured.dir, barrel.config.generators, jiti);
464
+ const conflicts = findCollisions(registry, canonical, generators);
465
+ if (conflicts.length > 0) throw new CollisionError(conflicts);
466
+ const entry = {
467
+ source: canonical,
468
+ url: source.url,
469
+ ...source.ref ? { ref: source.ref } : {},
470
+ ...source.subdir ? { subdir: source.subdir } : {},
471
+ generators
472
+ };
473
+ writeRegistry({ repositories: {
474
+ ...registry.repositories,
475
+ [canonical]: entry
476
+ } }, deps.env);
477
+ return entry;
478
+ } catch (error) {
479
+ if (ensured.changed) removeCacheEntry(deps.cacheRoot, source);
480
+ throw error;
481
+ }
482
+ }
483
+ function findEntryForLink(registry, source) {
484
+ const canonical = canonicalSource(source);
485
+ const exact = registry.repositories[canonical];
486
+ if (exact) return {
487
+ canonical,
488
+ entry: exact
489
+ };
490
+ for (const [sourceId, entry] of Object.entries(registry.repositories)) if (entry.url === source.url) return {
491
+ canonical: sourceId,
492
+ entry
493
+ };
494
+ }
495
+ function removeSource(link, deps) {
496
+ const source = parseSource(link);
497
+ const registry = readRegistry(deps.env);
498
+ const match = findEntryForLink(registry, source);
499
+ if (!match) throw new Error(`Repository "${canonicalSource(source)}" is not registered.`);
500
+ const repositories = { ...registry.repositories };
501
+ delete repositories[match.canonical];
502
+ writeRegistry({ repositories }, deps.env);
503
+ removeCacheEntry(deps.cacheRoot, sourceFromEntry(match.entry));
504
+ return match.entry;
505
+ }
506
+ function listRegistered(deps) {
507
+ return Object.values(readRegistry(deps.env).repositories);
508
+ }
509
+ function findGeneratorByName(entries, name) {
510
+ for (const entry of entries) {
511
+ const indexed = entry.generators.find((generator) => generator.name === name);
512
+ if (indexed) return {
513
+ entry,
514
+ indexed
515
+ };
516
+ }
517
+ }
518
+ //#endregion
519
+ //#region src/cli/prompt-adapter.ts
520
+ function unwrap(value) {
521
+ if (isCancel(value)) throw new PromptCancelledError();
522
+ return value;
523
+ }
524
+ function toValidate(validate) {
525
+ if (!validate) return void 0;
526
+ return (value) => {
527
+ if (value === void 0) return void 0;
528
+ const result = validate(value);
529
+ if (result === true) return void 0;
530
+ if (result === false) return "Invalid value";
531
+ return result;
532
+ };
533
+ }
534
+ function mapOptions(choices) {
535
+ return choices.map((choice) => ({
536
+ value: choice.value,
537
+ label: choice.label ?? choice.value,
538
+ ...choice.hint ? { hint: choice.hint } : {}
539
+ }));
540
+ }
541
+ function createClackAdapter(interactive) {
542
+ return {
543
+ interactive: interactive ?? Boolean(process.stdin.isTTY && process.stdout.isTTY),
544
+ async selectGenerator(choices) {
545
+ return unwrap(await select({
546
+ message: "Select a generator",
547
+ options: choices.map((choice) => ({
548
+ value: choice.name,
549
+ label: choice.name,
550
+ ...choice.description ? { hint: choice.description } : {}
551
+ }))
552
+ }));
553
+ },
554
+ async text(options) {
555
+ return unwrap(await text({
556
+ message: options.message,
557
+ placeholder: options.placeholder,
558
+ initialValue: options.default,
559
+ validate: toValidate(options.validate)
560
+ }));
561
+ },
562
+ async password(options) {
563
+ return unwrap(await password({ message: options.message }));
564
+ },
565
+ async number(options) {
566
+ const value = await text({
567
+ message: options.message,
568
+ initialValue: options.default === void 0 ? void 0 : String(options.default),
569
+ validate: (input) => {
570
+ const raw = input ?? "";
571
+ if (raw.trim() === "" || Number.isNaN(Number(raw))) return "Enter a number";
572
+ const parsed = Number(raw);
573
+ if (options.min !== void 0 && parsed < options.min) return `Must be at least ${options.min}`;
574
+ if (options.max !== void 0 && parsed > options.max) return `Must be at most ${options.max}`;
575
+ }
576
+ });
577
+ return Number(unwrap(value));
578
+ },
579
+ async confirm(options) {
580
+ return unwrap(await confirm({
581
+ message: options.message,
582
+ initialValue: options.default
583
+ }));
584
+ },
585
+ async select(options) {
586
+ return unwrap(await select({
587
+ message: options.message,
588
+ options: mapOptions(options.options),
589
+ initialValue: options.default
590
+ }));
591
+ },
592
+ async multiselect(options) {
593
+ return unwrap(await multiselect({
594
+ message: options.message,
595
+ options: mapOptions(options.options),
596
+ initialValues: options.default,
597
+ required: (options.min ?? 0) > 0
598
+ }));
599
+ }
600
+ };
601
+ }
602
+ //#endregion
603
+ //#region src/cli/run.ts
604
+ const GLOBAL_OPTIONS = [
605
+ ["--target <dir>", "Write generated files into <dir> instead of the current directory"],
606
+ ["--dry-run", "Report the files that would be written without writing them"],
607
+ ["--force", "Replace existing files that collide with generated output"],
608
+ ["-h, --help", "Show this help"],
609
+ ["-v, --version", "Show the version"]
610
+ ];
611
+ function usageText(entries) {
612
+ const lines = [];
613
+ lines.push("neew - manage and run remote generator repositories");
614
+ lines.push("");
615
+ lines.push("Usage:");
616
+ lines.push(" neew add <link>");
617
+ lines.push(" neew remove <link>");
618
+ lines.push(" neew list");
619
+ lines.push(" neew create <generator> [options]");
620
+ lines.push(" neew <generator> [options]");
621
+ lines.push("");
622
+ lines.push("Options:");
623
+ for (const [flag, description] of GLOBAL_OPTIONS) lines.push(` ${flag.padEnd(16)} ${description}`);
624
+ lines.push("");
625
+ lines.push("Generators:");
626
+ const generatorLines = entries.flatMap((entry) => entry.generators.map((generator) => ` ${generator.name}${generator.description ? ` ${generator.description}` : ""}`));
627
+ if (generatorLines.length === 0) lines.push(" (none registered)");
628
+ else lines.push(...generatorLines);
629
+ return lines.join("\n");
630
+ }
631
+ function parameterLines(generator) {
632
+ const prompts = generator.generator.prompts ?? {};
633
+ const keys = Object.keys(prompts);
634
+ if (keys.length === 0) return [" (no parameters)"];
635
+ return keys.map((key) => {
636
+ const prompt = prompts[key];
637
+ const flag = `--${flagNameFor(key)}`;
638
+ const value = prompt.type === "confirm" ? "" : " <value>";
639
+ const alias = prompt.alias ? `-${prompt.alias}, ` : "";
640
+ const required = prompt.default === void 0 ? " (required)" : "";
641
+ const fallback = prompt.default === void 0 ? "" : ` [default: ${prompt.default}]`;
642
+ return ` ${alias}${flag}${value}${prompt.message ? ` ${prompt.message}` : ""}${required}${fallback}`;
643
+ });
644
+ }
645
+ function generatorHelpText(generator) {
646
+ const name = generator.generator.name;
647
+ const lines = [];
648
+ lines.push(`neew ${name}${generator.generator.description ? ` - ${generator.generator.description}` : ""}`);
649
+ lines.push("");
650
+ lines.push("Usage:");
651
+ lines.push(` neew ${name} [options]`);
652
+ lines.push(` neew create ${name} [options]`);
653
+ lines.push("");
654
+ lines.push("Parameters:");
655
+ lines.push(...parameterLines(generator));
656
+ lines.push("");
657
+ lines.push("Options:");
658
+ for (const [flag, description] of GLOBAL_OPTIONS) if (flag === "--target <dir>") lines.push(` ${flag.padEnd(16)} ${description}`);
659
+ lines.push(" --dry-run Report the files that would be written without writing them");
660
+ lines.push(" --force Replace existing files that collide with generated output");
661
+ lines.push(" -h, --help Show this help");
662
+ lines.push(" -v, --version Show the version");
663
+ return lines.join("\n");
664
+ }
665
+ function readVersion() {
666
+ let dir = dirname(fileURLToPath(import.meta.url));
667
+ while (true) {
668
+ const packageJsonPath = join(dir, "package.json");
669
+ if (existsSync(packageJsonPath)) try {
670
+ const pkg = JSON.parse(readFileSync(packageJsonPath, "utf8"));
671
+ if (pkg.name === "@trut/neew") return pkg.version ?? "0.0.0";
672
+ } catch {}
673
+ const parent = dirname(dir);
674
+ if (parent === dir) return "0.0.0";
675
+ dir = parent;
676
+ }
677
+ }
678
+ function availableNames(entries) {
679
+ const names = entries.flatMap((entry) => entry.generators.map((generator) => generator.name));
680
+ return names.length === 0 ? "(none)" : names.join(", ");
681
+ }
682
+ function printUsage(context) {
683
+ let entries = [];
684
+ try {
685
+ entries = listRegistered(context.registryDeps);
686
+ } catch (error) {
687
+ context.stderr(`warning: ${error instanceof Error ? error.message : String(error)}`);
688
+ }
689
+ context.stdout(usageText(entries));
690
+ }
691
+ function report(stdout, generatorName, files, dryRun) {
692
+ if (dryRun) stdout(`Dry run for "${generatorName}": ${files.length} file(s) would be written.`);
693
+ else stdout(`Generator "${generatorName}" wrote ${files.length} file(s).`);
694
+ for (const file of files) stdout(` ${file}`);
695
+ }
696
+ function extractTarget(args, cwd) {
697
+ const rest = [];
698
+ let target;
699
+ for (let index = 0; index < args.length; index += 1) {
700
+ const arg = args[index];
701
+ if (arg === "--target") {
702
+ target = args[index + 1];
703
+ index += 1;
704
+ continue;
705
+ }
706
+ if (arg.startsWith("--target=")) {
707
+ target = arg.slice(9);
708
+ continue;
709
+ }
710
+ rest.push(arg);
711
+ }
712
+ return {
713
+ targetDir: target ? resolve(cwd, target) : cwd,
714
+ rest
715
+ };
716
+ }
717
+ async function addCommand(rest, context) {
718
+ const link = rest.find((arg) => !arg.startsWith("-"));
719
+ if (!link) throw new Error("Usage: neew add <link>");
720
+ const entry = await addSource(link, context.registryDeps);
721
+ context.stdout(`Added ${entry.source}`);
722
+ for (const generator of entry.generators) context.stdout(` ${generator.name}${generator.description ? ` ${generator.description}` : ""}`);
723
+ return {
724
+ kind: "added",
725
+ entry
726
+ };
727
+ }
728
+ function removeCommand(rest, context) {
729
+ const link = rest.find((arg) => !arg.startsWith("-"));
730
+ if (!link) throw new Error("Usage: neew remove <link>");
731
+ const entry = removeSource(link, context.registryDeps);
732
+ context.stdout(`Removed ${entry.source}`);
733
+ return {
734
+ kind: "removed",
735
+ entry
736
+ };
737
+ }
738
+ function listCommand(context) {
739
+ const entries = listRegistered(context.registryDeps);
740
+ if (entries.length === 0) {
741
+ context.stdout("No repositories registered.");
742
+ return {
743
+ kind: "list",
744
+ entries
745
+ };
746
+ }
747
+ for (const entry of entries) {
748
+ context.stdout(entry.source);
749
+ if (entry.generators.length === 0) context.stdout(" (no generators)");
750
+ for (const generator of entry.generators) context.stdout(` ${generator.name}${generator.description ? ` ${generator.description}` : ""}`);
751
+ }
752
+ return {
753
+ kind: "list",
754
+ entries
755
+ };
756
+ }
757
+ async function loadGenerator(name, context) {
758
+ const entries = listRegistered(context.registryDeps);
759
+ const located = findGeneratorByName(entries, name);
760
+ if (!located) throw new Error(`Unknown generator "${name}". Available generators: ${availableNames(entries)}.`);
761
+ const ensured = await ensure(sourceFromEntry(located.entry), context.registryDeps);
762
+ const jiti = createRepositoryJiti(ensured.dir);
763
+ const barrel = await loadBarrel(ensured.dir, { jiti });
764
+ const discovered = await discoverGenerators({
765
+ configDir: ensured.dir,
766
+ patterns: barrel.config.generators,
767
+ jiti
768
+ });
769
+ if (ensured.changed) {
770
+ const indexed = discovered.map((entry) => ({
771
+ name: entry.generator.name,
772
+ path: relative(ensured.dir, entry.modulePath),
773
+ ...entry.generator.description ? { description: entry.generator.description } : {}
774
+ }));
775
+ writeRegistry({ repositories: {
776
+ ...readRegistry(context.env).repositories,
777
+ [located.entry.source]: {
778
+ ...located.entry,
779
+ generators: indexed
780
+ }
781
+ } }, context.env);
782
+ }
783
+ const found = discovered.find((entry) => entry.generator.name === name);
784
+ if (!found) {
785
+ const names = discovered.map((entry) => entry.generator.name);
786
+ throw new Error(`Generator "${name}" is no longer provided by ${located.entry.source}. Available generators: ${names.length === 0 ? "(none)" : names.join(", ")}.`);
787
+ }
788
+ return {
789
+ found,
790
+ entry: located.entry,
791
+ repoDir: ensured.dir
792
+ };
793
+ }
794
+ async function invoke(argv, context, isCreate) {
795
+ const argOffset = isCreate ? 1 : 0;
796
+ const generatorArgv = argv.slice(argOffset);
797
+ const { global, generatorName } = parseGlobalFlags(generatorArgv);
798
+ if (global.version) {
799
+ const version = readVersion();
800
+ context.stdout(version);
801
+ return {
802
+ kind: "version",
803
+ version
804
+ };
805
+ }
806
+ if (!generatorName) {
807
+ printUsage(context);
808
+ return { kind: "help" };
809
+ }
810
+ const { found, repoDir } = await loadGenerator(generatorName, context);
811
+ if (global.help) {
812
+ context.stdout(generatorHelpText(found));
813
+ return {
814
+ kind: "generator-help",
815
+ generator: generatorName
816
+ };
817
+ }
818
+ const { targetDir, rest } = extractTarget(generatorArgv, context.cwd);
819
+ const values = parsePromptValues(rest, found.generator.prompts ?? {});
820
+ const answers = await resolvePrompts({
821
+ prompts: found.generator.prompts,
822
+ values,
823
+ adapter: context.adapter
824
+ });
825
+ const result = await runGenerator({
826
+ generator: found.generator,
827
+ configDir: targetDir,
828
+ moduleDir: found.moduleDir || repoDir,
829
+ answers,
830
+ dryRun: global.dryRun,
831
+ force: global.force
832
+ });
833
+ report(context.stdout, generatorName, result.files, global.dryRun);
834
+ return {
835
+ kind: "run",
836
+ generator: generatorName,
837
+ files: result.files,
838
+ written: result.written
839
+ };
840
+ }
841
+ async function runCli(argv, options = {}) {
842
+ const env = options.env ?? process.env;
843
+ const stdout = options.stdout ?? ((message) => process.stdout.write(`${message}\n`));
844
+ const stderr = options.stderr ?? ((message) => process.stderr.write(`${message}\n`));
845
+ const cacheRoot = resolveCacheRoot(env);
846
+ const context = {
847
+ env,
848
+ cwd: options.cwd ?? process.cwd(),
849
+ stdout,
850
+ stderr,
851
+ adapter: options.adapter ?? createClackAdapter(),
852
+ registryDeps: {
853
+ cacheRoot,
854
+ env,
855
+ ...options.resolveCommit ? { resolveCommit: options.resolveCommit } : {},
856
+ ...options.download ? { download: options.download } : {},
857
+ ...options.downloadDeps ? { downloadDeps: options.downloadDeps } : {},
858
+ ...options.resolveDeps ? { resolveDeps: options.resolveDeps } : {},
859
+ warn: (message) => stderr(`warning: ${message}`)
860
+ }
861
+ };
862
+ const [command, ...rest] = argv;
863
+ if (command === void 0) {
864
+ printUsage(context);
865
+ return { kind: "help" };
866
+ }
867
+ if (command === "-h" || command === "--help") {
868
+ printUsage(context);
869
+ return { kind: "help" };
870
+ }
871
+ if (command === "-v" || command === "--version") {
872
+ const version = readVersion();
873
+ context.stdout(version);
874
+ return {
875
+ kind: "version",
876
+ version
877
+ };
878
+ }
879
+ if (command === "add") return addCommand(rest, context);
880
+ if (command === "remove") return removeCommand(rest, context);
881
+ if (command === "list") return listCommand(context);
882
+ if (command === "create") return invoke(argv, context, true);
883
+ return invoke(argv, context, false);
884
+ }
885
+ //#endregion
886
+ //#region src/cli.ts
887
+ runCli(process.argv.slice(2)).catch((error) => {
888
+ const message = error instanceof Error ? error.message : String(error);
889
+ process.stderr.write(`\n${message}\n`);
890
+ process.exitCode = error instanceof PromptCancelledError ? 130 : 1;
891
+ });
892
+ //#endregion
893
+ export {};
894
+
895
+ //# sourceMappingURL=cli.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.mjs","names":[],"sources":["../src/config/paths.ts","../src/config/create-loader.ts","../src/config/load-barrel.ts","../src/config/registry-file.ts","../src/engine/adapters/github.ts","../src/engine/adapters/index.ts","../src/engine/cache.ts","../src/engine/resolve-ref.ts","../src/engine/ensure.ts","../src/engine/source.ts","../src/registry/operations.ts","../src/cli/prompt-adapter.ts","../src/cli/run.ts","../src/cli.ts"],"sourcesContent":["import { homedir } from 'node:os';\nimport { join } from 'node:path';\n\nexport type Environment = Record<string, string | undefined>;\n\nfunction home(env: Environment): string {\n return env.HOME ?? env.USERPROFILE ?? homedir();\n}\n\nexport function resolveConfigRoot(env: Environment = process.env): string {\n if (env.NEEW_CONFIG_DIR) return env.NEEW_CONFIG_DIR;\n if (env.XDG_CONFIG_HOME) return join(env.XDG_CONFIG_HOME, 'neew');\n if (process.platform === 'darwin') {\n return join(home(env), 'Library', 'Application Support', 'neew');\n }\n if (process.platform === 'win32') {\n return join(env.APPDATA ?? join(home(env), 'AppData', 'Roaming'), 'neew');\n }\n return join(home(env), '.config', 'neew');\n}\n\nexport function resolveCacheRoot(env: Environment = process.env): string {\n if (env.NEEW_CACHE_DIR) return env.NEEW_CACHE_DIR;\n if (env.XDG_CACHE_HOME) return join(env.XDG_CACHE_HOME, 'neew');\n if (process.platform === 'darwin') {\n return join(home(env), 'Library', 'Caches', 'neew');\n }\n if (process.platform === 'win32') {\n return join(env.LOCALAPPDATA ?? join(home(env), 'AppData', 'Local'), 'neew');\n }\n return join(home(env), '.cache', 'neew');\n}\n","import { fileURLToPath } from 'node:url';\nimport { createJiti, type Jiti } from 'jiti';\n\nconst FRAMEWORK_SPECIFIERS = ['@trut/gen', '@trut/gen/runtime', '@trut/neew'];\n\nfunction resolveSpecifier(specifier: string): string | undefined {\n try {\n const url = import.meta.resolve(specifier);\n return url.startsWith('file:') ? fileURLToPath(url) : url;\n } catch {\n return undefined;\n }\n}\n\nexport function createRepositoryJiti(baseDir: string): Jiti {\n const alias: Record<string, string> = {};\n for (const specifier of FRAMEWORK_SPECIFIERS) {\n const resolved = resolveSpecifier(specifier);\n if (resolved) alias[specifier] = resolved;\n }\n return createJiti(baseDir, {\n interopDefault: true,\n alias,\n moduleCache: false,\n fsCache: false,\n });\n}\n","import { existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { Jiti } from 'jiti';\nimport { createRepositoryJiti } from './create-loader';\nimport type { GeneratorsConfig } from './generators-config';\n\nexport const BARREL_FILENAME = 'generators.ts';\n\nexport interface LoadedBarrel {\n config: GeneratorsConfig;\n path: string;\n dir: string;\n}\n\nfunction pickConfig(moduleExports: unknown): unknown {\n if (moduleExports && typeof moduleExports === 'object') {\n const record = moduleExports as Record<string, unknown>;\n if (Array.isArray(record.generators)) return moduleExports;\n if (record.default && typeof record.default === 'object') {\n const fallback = record.default as Record<string, unknown>;\n if (Array.isArray(fallback.generators)) return fallback;\n }\n }\n return moduleExports;\n}\n\nexport interface LoadBarrelOptions {\n jiti?: Jiti;\n}\n\nexport async function loadBarrel(\n dir: string,\n options: LoadBarrelOptions = {},\n): Promise<LoadedBarrel> {\n const path = join(dir, BARREL_FILENAME);\n if (!existsSync(path)) {\n throw new Error(\n `Repository contract violated: no ${BARREL_FILENAME} found at ${dir}. ` +\n `Every generator repository must provide a root ${BARREL_FILENAME} exporting a \"generators\" array.`,\n );\n }\n const jiti = options.jiti ?? createRepositoryJiti(path);\n let moduleExports: unknown;\n try {\n moduleExports = await jiti.import(path);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(`Failed to load ${BARREL_FILENAME} at ${path}: ${message}`);\n }\n const config = pickConfig(moduleExports) as GeneratorsConfig | undefined;\n if (!config || !Array.isArray(config.generators)) {\n throw new Error(\n `Invalid ${BARREL_FILENAME} at ${path}: expected a default export whose \"generators\" is an array of glob patterns (strings).`,\n );\n }\n const invalid = config.generators.find((entry) => typeof entry !== 'string');\n if (invalid !== undefined) {\n throw new Error(\n `Invalid ${BARREL_FILENAME} at ${path}: \"generators\" must be an array of glob patterns (strings).`,\n );\n }\n return { config, path, dir };\n}\n","import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';\nimport { randomUUID } from 'node:crypto';\nimport { dirname, join } from 'node:path';\nimport { resolveConfigRoot, type Environment } from './paths';\n\nexport interface IndexedGenerator {\n name: string;\n path: string;\n description?: string;\n}\n\nexport interface RegistryEntry {\n source: string;\n url: string;\n ref?: string;\n subdir?: string;\n generators: IndexedGenerator[];\n}\n\nexport interface RegistryFile {\n repositories: Record<string, RegistryEntry>;\n}\n\nexport const REGISTRY_FILENAME = 'neew.json';\n\nexport function registryFilePath(env: Environment = process.env): string {\n return join(resolveConfigRoot(env), REGISTRY_FILENAME);\n}\n\nfunction emptyRegistry(): RegistryFile {\n return { repositories: {} };\n}\n\nexport function readRegistry(env: Environment = process.env): RegistryFile {\n const path = registryFilePath(env);\n if (!existsSync(path)) return emptyRegistry();\n const text = readFileSync(path, 'utf8');\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n throw new Error(\n `Failed to parse the neew registry at ${path}: ${message}. ` +\n `Fix or remove the file; neew will not overwrite it.`,\n );\n }\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new Error(`Invalid neew registry at ${path}: expected a JSON object.`);\n }\n const repositories = (parsed as RegistryFile).repositories;\n if (!repositories || typeof repositories !== 'object' || Array.isArray(repositories)) {\n throw new Error(`Invalid neew registry at ${path}: expected a \"repositories\" object.`);\n }\n return { repositories };\n}\n\nexport function writeRegistry(file: RegistryFile, env: Environment = process.env): void {\n const path = registryFilePath(env);\n mkdirSync(dirname(path), { recursive: true });\n const temp = `${path}.tmp-${randomUUID()}`;\n writeFileSync(temp, `${JSON.stringify(file, null, 2)}\\n`, 'utf8');\n renameSync(temp, path);\n}\n","import { mkdirSync } from 'node:fs';\nimport { Readable } from 'node:stream';\nimport { pipeline } from 'node:stream/promises';\nimport { createGunzip } from 'node:zlib';\nimport * as tar from 'tar';\nimport type { ParsedSource } from '../source';\nimport type { DownloadAdapter, DownloadDeps, DownloadOptions } from './types';\n\nexport function githubTarballUrl(source: ParsedSource, ref: string): string {\n return `https://codeload.github.com/${source.owner}/${source.repo}/tar.gz/${encodeURIComponent(ref)}`;\n}\n\nfunction subdirFilter(subdir: string): (path: string) => boolean {\n return (path) => {\n const relative = path.split('/').slice(1).join('/');\n return relative === subdir || relative.startsWith(`${subdir}/`);\n };\n}\n\nexport function tarballStrip(subdir: string | undefined): number {\n return subdir ? 1 + subdir.split('/').filter(Boolean).length : 1;\n}\n\nexport async function downloadGithub(\n source: ParsedSource,\n dest: string,\n options: DownloadOptions,\n deps: DownloadDeps,\n): Promise<void> {\n const url = githubTarballUrl(source, options.ref);\n const fetchImpl = deps.fetch ?? globalThis.fetch;\n const response = await fetchImpl(url);\n if (!response.ok) {\n throw new Error(`Failed to download ${url}: ${response.status} ${response.statusText}`);\n }\n if (!response.body) throw new Error(`Failed to download ${url}: empty response body.`);\n mkdirSync(dest, { recursive: true });\n const filter = options.subdir ? subdirFilter(options.subdir) : undefined;\n await pipeline(\n Readable.fromWeb(response.body as unknown as Parameters<typeof Readable.fromWeb>[0]),\n createGunzip(),\n tar.extract({\n cwd: dest,\n strip: tarballStrip(options.subdir),\n ...(filter ? { filter } : {}),\n }),\n );\n}\n\nexport const githubAdapter: DownloadAdapter = {\n host: 'github.com',\n download: downloadGithub,\n};\n","import { githubAdapter } from './github';\nimport type { DownloadAdapter } from './types';\n\nexport type { DownloadAdapter, DownloadDeps, DownloadOptions } from './types';\n\nconst SUPPORTED_HOSTS = ['github.com'];\n\nexport class UnsupportedHostError extends Error {\n readonly host: string;\n\n constructor(host: string) {\n super(`Unsupported host \"${host}\". Supported hosts: ${SUPPORTED_HOSTS.join(', ')}.`);\n this.name = 'UnsupportedHostError';\n this.host = host;\n }\n}\n\nexport function getDownloadAdapter(host: string): DownloadAdapter {\n if (host === githubAdapter.host || host === `www.${githubAdapter.host}`) {\n return githubAdapter;\n }\n throw new UnsupportedHostError(host);\n}\n","import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport type { ParsedSource } from './source';\n\nexport const METADATA_FILENAME = '.neew.json';\n\nexport interface CacheMetadata {\n url: string;\n ref: string | null;\n subdir: string | null;\n commit: string;\n fetchedAt: string;\n}\n\nexport function sourceRefName(source: Pick<ParsedSource, 'ref'>): string {\n return source.ref ?? 'HEAD';\n}\n\nexport function cacheSourcesDir(cacheRoot: string): string {\n return join(cacheRoot, 'sources');\n}\n\nexport function cacheTmpDir(cacheRoot: string): string {\n return join(cacheRoot, 'tmp');\n}\n\nexport function cacheEntryDir(cacheRoot: string, source: ParsedSource): string {\n return join(\n cacheSourcesDir(cacheRoot),\n source.host,\n source.owner,\n `${source.repo}@${encodeURIComponent(sourceRefName(source))}`,\n );\n}\n\nexport function readMetadata(entryDir: string): CacheMetadata | undefined {\n const path = join(entryDir, METADATA_FILENAME);\n if (!existsSync(path)) return undefined;\n try {\n const parsed = JSON.parse(readFileSync(path, 'utf8')) as CacheMetadata;\n if (!parsed || typeof parsed.commit !== 'string') return undefined;\n return parsed;\n } catch {\n return undefined;\n }\n}\n\nexport function writeMetadata(entryDir: string, metadata: CacheMetadata): void {\n mkdirSync(entryDir, { recursive: true });\n writeFileSync(\n join(entryDir, METADATA_FILENAME),\n `${JSON.stringify(metadata, null, 2)}\\n`,\n 'utf8',\n );\n}\n\nexport function removeCacheEntry(cacheRoot: string, source: ParsedSource): void {\n rmSync(cacheEntryDir(cacheRoot, source), { recursive: true, force: true });\n}\n","import git, { type ServerRef } from 'isomorphic-git';\nimport http, { type HttpClient } from 'isomorphic-git/http/node';\nimport type { ParsedSource } from './source';\n\nexport type ListServerRefs = (options: {\n http: HttpClient;\n url: string;\n symrefs?: boolean;\n peelTags?: boolean;\n corsProxy?: string;\n}) => Promise<ServerRef[]>;\n\nexport interface ResolveRefDeps {\n listServerRefs?: ListServerRefs;\n http?: HttpClient;\n corsProxy?: string;\n}\n\nfunction refCandidates(ref: string): string[] {\n if (ref.startsWith('refs/')) return [ref];\n return [`refs/heads/${ref}`, `refs/tags/${ref}`];\n}\n\nexport function pickCommit(refs: ServerRef[], source: ParsedSource): string {\n if (source.ref) {\n for (const candidate of refCandidates(source.ref)) {\n const match = refs.find((entry) => entry.ref === candidate);\n if (match) return match.peeled ?? match.oid;\n }\n const suffix = refs.find(\n (entry) => entry.ref === source.ref || entry.ref.endsWith(`/${source.ref}`),\n );\n if (suffix) return suffix.peeled ?? suffix.oid;\n throw new Error(`Ref \"${source.ref}\" was not found at ${source.url}.`);\n }\n const head = refs.find((entry) => entry.ref === 'HEAD');\n if (head?.target) {\n const branch = refs.find((entry) => entry.ref === head.target);\n if (branch) return branch.peeled ?? branch.oid;\n }\n const fallback = refs.find(\n (entry) => entry.ref === 'refs/heads/main' || entry.ref === 'refs/heads/master',\n );\n if (fallback) return fallback.peeled ?? fallback.oid;\n throw new Error(`Could not determine the default branch for ${source.url}.`);\n}\n\nexport async function resolveCommit(\n source: ParsedSource,\n deps: ResolveRefDeps = {},\n): Promise<string> {\n const listServerRefs = deps.listServerRefs ?? git.listServerRefs;\n const refs = await listServerRefs({\n http: deps.http ?? http,\n url: source.url,\n symrefs: true,\n peelTags: true,\n ...(deps.corsProxy ? { corsProxy: deps.corsProxy } : {}),\n });\n return pickCommit(refs, source);\n}\n","import { existsSync, mkdirSync, renameSync, rmSync } from 'node:fs';\nimport { randomUUID } from 'node:crypto';\nimport { dirname, join } from 'node:path';\nimport { getDownloadAdapter, type DownloadDeps, type DownloadOptions } from './adapters';\nimport {\n cacheEntryDir,\n cacheTmpDir,\n type CacheMetadata,\n readMetadata,\n writeMetadata,\n} from './cache';\nimport { resolveCommit, type ResolveRefDeps } from './resolve-ref';\nimport type { ParsedSource } from './source';\n\nexport interface EnsureDeps {\n cacheRoot: string;\n resolveCommit?: (source: ParsedSource) => Promise<string>;\n download?: (source: ParsedSource, dest: string, options: DownloadOptions) => Promise<void>;\n downloadDeps?: DownloadDeps;\n resolveDeps?: ResolveRefDeps;\n warn?: (message: string) => void;\n}\n\nexport interface EnsureResult {\n dir: string;\n metadata: CacheMetadata;\n changed: boolean;\n offline: boolean;\n}\n\nfunction swapIntoPlace(entryDir: string, stagingDir: string): void {\n mkdirSync(dirname(entryDir), { recursive: true });\n const backup = `${entryDir}.bak-${randomUUID()}`;\n const hadPrevious = existsSync(entryDir);\n if (hadPrevious) renameSync(entryDir, backup);\n try {\n renameSync(stagingDir, entryDir);\n } catch (error) {\n if (hadPrevious) renameSync(backup, entryDir);\n throw error;\n }\n if (hadPrevious) rmSync(backup, { recursive: true, force: true });\n}\n\nexport async function ensure(source: ParsedSource, deps: EnsureDeps): Promise<EnsureResult> {\n const entryDir = cacheEntryDir(deps.cacheRoot, source);\n const existing = existsSync(entryDir) ? readMetadata(entryDir) : undefined;\n const resolve = deps.resolveCommit ?? ((target) => resolveCommit(target, deps.resolveDeps));\n\n let commit: string;\n try {\n commit = await resolve(source);\n } catch (error) {\n if (existing) {\n deps.warn?.(\n `Could not check ${source.url} for updates (${\n error instanceof Error ? error.message : String(error)\n }); using the cached copy.`,\n );\n return { dir: entryDir, metadata: existing, changed: false, offline: true };\n }\n throw error;\n }\n\n if (existing && existing.commit === commit) {\n return { dir: entryDir, metadata: existing, changed: false, offline: false };\n }\n\n const staging = join(cacheTmpDir(deps.cacheRoot), `${source.repo}-${randomUUID()}`);\n mkdirSync(staging, { recursive: true });\n const download =\n deps.download ??\n ((target, dest, options) => {\n const adapter = getDownloadAdapter(target.host);\n return adapter.download(target, dest, options, deps.downloadDeps ?? {});\n });\n try {\n await download(source, staging, {\n ref: commit,\n ...(source.subdir ? { subdir: source.subdir } : {}),\n });\n const metadata: CacheMetadata = {\n url: source.url,\n ref: source.ref ?? null,\n subdir: source.subdir ?? null,\n commit,\n fetchedAt: new Date().toISOString(),\n };\n writeMetadata(staging, metadata);\n swapIntoPlace(entryDir, staging);\n return { dir: entryDir, metadata, changed: true, offline: false };\n } catch (error) {\n rmSync(staging, { recursive: true, force: true });\n throw error;\n }\n}\n","export interface ParsedSource {\n host: string;\n owner: string;\n repo: string;\n ref?: string;\n subdir?: string;\n url: string;\n}\n\nconst HOST_ALIASES: Record<string, string> = {\n gh: 'github.com',\n github: 'github.com',\n gl: 'gitlab.com',\n gitlab: 'gitlab.com',\n bb: 'bitbucket.org',\n bitbucket: 'bitbucket.org',\n};\n\nconst SCHEME_PATTERN = /^[a-zA-Z][a-zA-Z0-9+.-]*:\\/\\//;\nconst PREFIX_PATTERN = /^([a-zA-Z][a-zA-Z0-9+.-]*):/;\n\nfunction cleanSubdir(value: string | undefined): string | undefined {\n if (!value) return undefined;\n const trimmed = value.replace(/^\\/+/, '').replace(/\\/+$/, '');\n return trimmed === '' ? undefined : trimmed;\n}\n\nexport function parseSource(input: string): ParsedSource {\n const original = input.trim();\n if (original === '') throw new Error('A repository link is required.');\n\n let rest = original;\n let ref: string | undefined;\n const hash = rest.indexOf('#');\n if (hash !== -1) {\n ref = rest.slice(hash + 1).trim() || undefined;\n rest = rest.slice(0, hash);\n }\n\n let subdir: string | undefined;\n const separator = rest.indexOf('::');\n if (separator !== -1) {\n subdir = cleanSubdir(rest.slice(separator + 2));\n rest = rest.slice(0, separator);\n }\n\n rest = rest.replace(SCHEME_PATTERN, '').replace(/^\\/+/, '');\n\n let host: string | undefined;\n const prefix = PREFIX_PATTERN.exec(rest);\n if (prefix) {\n const alias = HOST_ALIASES[prefix[1].toLowerCase()];\n if (alias) {\n host = alias;\n rest = rest.slice(prefix[0].length).replace(/^\\/+/, '');\n }\n }\n\n const segments = rest\n .split('/')\n .map((segment) => segment.trim())\n .filter(Boolean);\n if (segments.length === 0) throw new Error(`Could not parse repository link \"${original}\".`);\n\n if (!host) {\n if (segments[0].includes('.') && segments.length >= 3) {\n host = segments.shift()!.toLowerCase();\n } else {\n host = 'github.com';\n }\n }\n host = host.toLowerCase();\n\n const owner = segments[0];\n if (!owner) throw new Error(`Could not parse a repository owner from \"${original}\".`);\n const rawRepo = segments[1];\n if (!rawRepo) throw new Error(`Could not parse a repository name from \"${original}\".`);\n const repo = rawRepo.replace(/\\.git$/i, '');\n if (!subdir && segments.length > 2) {\n subdir = cleanSubdir(segments.slice(2).join('/'));\n }\n\n return {\n host,\n owner,\n repo,\n ...(ref ? { ref } : {}),\n ...(subdir ? { subdir } : {}),\n url: `https://${host}/${owner}/${repo}`,\n };\n}\n\nexport function canonicalSource(source: ParsedSource): string {\n let identity = `${source.host}/${source.owner}/${source.repo}`;\n if (source.subdir) identity += `::${source.subdir}`;\n if (source.ref) identity += `#${source.ref}`;\n return identity;\n}\n\nexport function sourceDisplayName(source: ParsedSource): string {\n return canonicalSource(source);\n}\n","import { dirname, join, relative } from 'node:path';\nimport type { Jiti } from 'jiti';\nimport { discoverGenerators, resolveGeneratorModules } from '@trut/gen/runtime';\nimport type { Environment } from '../config/paths';\nimport { createRepositoryJiti } from '../config/create-loader';\nimport { loadBarrel } from '../config/load-barrel';\nimport {\n readRegistry,\n writeRegistry,\n type IndexedGenerator,\n type RegistryEntry,\n} from '../config/registry-file';\nimport { ensure, type EnsureDeps } from '../engine/ensure';\nimport { removeCacheEntry } from '../engine/cache';\nimport { parseSource, canonicalSource, type ParsedSource } from '../engine/source';\n\nexport interface RegistryDeps extends EnsureDeps {\n env?: Environment;\n}\n\nexport interface GeneratedIndexEntry {\n name: string;\n path: string;\n description?: string;\n moduleDir: string;\n modulePath: string;\n}\n\nexport function sourceFromEntry(entry: RegistryEntry): ParsedSource {\n return {\n ...parseSource(entry.url),\n ...(entry.ref ? { ref: entry.ref } : {}),\n ...(entry.subdir ? { subdir: entry.subdir } : {}),\n };\n}\n\nexport async function indexRepository(\n dir: string,\n patterns: string[],\n jiti: Jiti,\n): Promise<IndexedGenerator[]> {\n for (const pattern of patterns) {\n const matches = await resolveGeneratorModules([pattern], dir);\n if (matches.length === 0) {\n throw new Error(\n `Pattern \"${pattern}\" matched no files in ${dir}. Every pattern in the generators barrel must resolve to at least one generator module.`,\n );\n }\n }\n const discovered = await discoverGenerators({ configDir: dir, patterns, jiti });\n if (discovered.length === 0) {\n throw new Error(`No generators were discovered in ${dir}.`);\n }\n return discovered.map((entry) => ({\n name: entry.generator.name,\n path: relative(dir, entry.modulePath),\n ...(entry.generator.description ? { description: entry.generator.description } : {}),\n }));\n}\n\nexport interface Collision {\n name: string;\n path: string;\n description?: string;\n existingSource: string;\n}\n\nfunction collisionKey(sourceId: string, generatorPath: string): string {\n return `${sourceId}::${generatorPath}`;\n}\n\nexport function findCollisions(\n registry: { repositories: Record<string, RegistryEntry> },\n canonical: string,\n indexed: IndexedGenerator[],\n): Collision[] {\n const names = new Map<string, { entry: RegistryEntry; source: string }>();\n const paths = new Map<string, { source: string }>();\n for (const [sourceId, entry] of Object.entries(registry.repositories)) {\n if (sourceId === canonical) continue;\n for (const generator of entry.generators) {\n names.set(generator.name, { entry, source: sourceId });\n paths.set(collisionKey(sourceId, generator.path), { source: sourceId });\n }\n }\n const conflicts: Collision[] = [];\n for (const generator of indexed) {\n const byName = names.get(generator.name);\n const byPath = paths.get(collisionKey(canonical, generator.path));\n const existing = byName ?? byPath;\n if (!existing) continue;\n conflicts.push({\n name: generator.name,\n path: generator.path,\n ...(generator.description ? { description: generator.description } : {}),\n existingSource: existing.source,\n });\n }\n return conflicts;\n}\n\nexport class CollisionError extends Error {\n readonly conflicts: Collision[];\n\n constructor(conflicts: Collision[]) {\n const lines = conflicts\n .map(\n (conflict) =>\n ` - ${conflict.name}${\n conflict.description ? ` (${conflict.description})` : ''\n } conflicts with ${conflict.existingSource}`,\n )\n .join('\\n');\n super(`Repository cannot be added: generator name collision(s):\\n${lines}`);\n this.name = 'CollisionError';\n this.conflicts = conflicts;\n }\n}\n\nexport async function addSource(link: string, deps: RegistryDeps): Promise<RegistryEntry> {\n const source = parseSource(link);\n const canonical = canonicalSource(source);\n const registry = readRegistry(deps.env);\n const ensured = await ensure(source, deps);\n try {\n const jiti = createRepositoryJiti(ensured.dir);\n const barrel = await loadBarrel(ensured.dir, { jiti });\n const generators = await indexRepository(ensured.dir, barrel.config.generators, jiti);\n const conflicts = findCollisions(registry, canonical, generators);\n if (conflicts.length > 0) throw new CollisionError(conflicts);\n const entry: RegistryEntry = {\n source: canonical,\n url: source.url,\n ...(source.ref ? { ref: source.ref } : {}),\n ...(source.subdir ? { subdir: source.subdir } : {}),\n generators,\n };\n writeRegistry({ repositories: { ...registry.repositories, [canonical]: entry } }, deps.env);\n return entry;\n } catch (error) {\n if (ensured.changed) removeCacheEntry(deps.cacheRoot, source);\n throw error;\n }\n}\n\nexport function findEntryForLink(\n registry: { repositories: Record<string, RegistryEntry> },\n source: ParsedSource,\n): { canonical: string; entry: RegistryEntry } | undefined {\n const canonical = canonicalSource(source);\n const exact = registry.repositories[canonical];\n if (exact) return { canonical, entry: exact };\n for (const [sourceId, entry] of Object.entries(registry.repositories)) {\n if (entry.url === source.url) return { canonical: sourceId, entry };\n }\n return undefined;\n}\n\nexport function removeSource(link: string, deps: RegistryDeps): RegistryEntry {\n const source = parseSource(link);\n const registry = readRegistry(deps.env);\n const match = findEntryForLink(registry, source);\n if (!match) {\n throw new Error(`Repository \"${canonicalSource(source)}\" is not registered.`);\n }\n const repositories = { ...registry.repositories };\n delete repositories[match.canonical];\n writeRegistry({ repositories }, deps.env);\n removeCacheEntry(deps.cacheRoot, sourceFromEntry(match.entry));\n return match.entry;\n}\n\nexport function listRegistered(deps: { env?: Environment }): RegistryEntry[] {\n return Object.values(readRegistry(deps.env).repositories);\n}\n\nexport interface LocatedGenerator {\n entry: RegistryEntry;\n indexed: IndexedGenerator;\n}\n\nexport function findGeneratorByName(\n entries: RegistryEntry[],\n name: string,\n): LocatedGenerator | undefined {\n for (const entry of entries) {\n const indexed = entry.generators.find((generator) => generator.name === name);\n if (indexed) return { entry, indexed };\n }\n return undefined;\n}\n\nexport function generatorModuleDir(repositoryDir: string, indexed: IndexedGenerator): string {\n return dirname(join(repositoryDir, indexed.path));\n}\n","import { confirm, isCancel, multiselect, password, select, text } from '@clack/prompts';\nimport { PromptCancelledError, type PromptChoice } from '@trut/gen';\nimport type { PromptAdapter } from '@trut/gen/runtime';\n\nfunction unwrap<T>(value: T | symbol): T {\n if (isCancel(value)) throw new PromptCancelledError();\n return value as T;\n}\n\nfunction toValidate(\n validate: ((value: string) => boolean | string) | undefined,\n): ((value: string | undefined) => string | undefined) | undefined {\n if (!validate) return undefined;\n return (value) => {\n if (value === undefined) return undefined;\n const result = validate(value);\n if (result === true) return undefined;\n if (result === false) return 'Invalid value';\n return result;\n };\n}\n\nfunction mapOptions(choices: PromptChoice[]) {\n return choices.map((choice) => ({\n value: choice.value,\n label: choice.label ?? choice.value,\n ...(choice.hint ? { hint: choice.hint } : {}),\n }));\n}\n\nexport function createClackAdapter(interactive?: boolean): PromptAdapter {\n return {\n interactive: interactive ?? Boolean(process.stdin.isTTY && process.stdout.isTTY),\n async selectGenerator(choices) {\n return unwrap<string>(\n await select({\n message: 'Select a generator',\n options: choices.map((choice) => ({\n value: choice.name,\n label: choice.name,\n ...(choice.description ? { hint: choice.description } : {}),\n })),\n }),\n );\n },\n async text(options) {\n return unwrap<string>(\n await text({\n message: options.message,\n placeholder: options.placeholder,\n initialValue: options.default,\n validate: toValidate(options.validate),\n }),\n );\n },\n async password(options) {\n return unwrap<string>(await password({ message: options.message }));\n },\n async number(options) {\n const value = await text({\n message: options.message,\n initialValue: options.default === undefined ? undefined : String(options.default),\n validate: (input) => {\n const raw = input ?? '';\n if (raw.trim() === '' || Number.isNaN(Number(raw))) return 'Enter a number';\n const parsed = Number(raw);\n if (options.min !== undefined && parsed < options.min) {\n return `Must be at least ${options.min}`;\n }\n if (options.max !== undefined && parsed > options.max) {\n return `Must be at most ${options.max}`;\n }\n },\n });\n return Number(unwrap<string>(value));\n },\n async confirm(options) {\n return unwrap<boolean>(\n await confirm({ message: options.message, initialValue: options.default }),\n );\n },\n async select(options) {\n return unwrap<string>(\n await select({\n message: options.message,\n options: mapOptions(options.options),\n initialValue: options.default,\n }),\n );\n },\n async multiselect(options) {\n return unwrap<string[]>(\n await multiselect({\n message: options.message,\n options: mapOptions(options.options),\n initialValues: options.default,\n required: (options.min ?? 0) > 0,\n }),\n );\n },\n };\n}\n","import { existsSync, readFileSync } from 'node:fs';\nimport { dirname, join, relative, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport {\n discoverGenerators,\n parseGlobalFlags,\n parsePromptValues,\n resolvePrompts,\n runGenerator,\n flagNameFor,\n} from '@trut/gen/runtime';\nimport type { PromptAdapter, DiscoveredGenerator } from '@trut/gen/runtime';\nimport type { AnyPrompt } from '@trut/gen';\nimport type { Environment } from '../config/paths';\nimport { resolveCacheRoot } from '../config/paths';\nimport { createRepositoryJiti } from '../config/create-loader';\nimport { loadBarrel } from '../config/load-barrel';\nimport { readRegistry, writeRegistry, type RegistryEntry } from '../config/registry-file';\nimport type { DownloadDeps } from '../engine/adapters';\nimport type { EnsureDeps } from '../engine/ensure';\nimport type { ResolveRefDeps } from '../engine/resolve-ref';\nimport { ensure } from '../engine/ensure';\nimport {\n addSource,\n findGeneratorByName,\n listRegistered,\n removeSource,\n sourceFromEntry,\n type RegistryDeps,\n} from '../registry/operations';\nimport { createClackAdapter } from './prompt-adapter';\n\nexport interface CliOptions {\n env?: Environment;\n cwd?: string;\n stdout?: (message: string) => void;\n stderr?: (message: string) => void;\n adapter?: PromptAdapter;\n resolveCommit?: EnsureDeps['resolveCommit'];\n download?: EnsureDeps['download'];\n downloadDeps?: DownloadDeps;\n resolveDeps?: ResolveRefDeps;\n}\n\nexport type CliResult =\n | { kind: 'help' }\n | { kind: 'version'; version: string }\n | { kind: 'added'; entry: RegistryEntry }\n | { kind: 'removed'; entry: RegistryEntry }\n | { kind: 'list'; entries: RegistryEntry[] }\n | { kind: 'generator-help'; generator: string }\n | { kind: 'run'; generator: string; files: string[]; written: boolean };\n\ninterface Context {\n env: Environment;\n cwd: string;\n stdout: (message: string) => void;\n stderr: (message: string) => void;\n adapter: PromptAdapter;\n registryDeps: RegistryDeps;\n}\n\nconst GLOBAL_OPTIONS: Array<[string, string]> = [\n ['--target <dir>', 'Write generated files into <dir> instead of the current directory'],\n ['--dry-run', 'Report the files that would be written without writing them'],\n ['--force', 'Replace existing files that collide with generated output'],\n ['-h, --help', 'Show this help'],\n ['-v, --version', 'Show the version'],\n];\n\nfunction usageText(entries: RegistryEntry[]): string {\n const lines: string[] = [];\n lines.push('neew - manage and run remote generator repositories');\n lines.push('');\n lines.push('Usage:');\n lines.push(' neew add <link>');\n lines.push(' neew remove <link>');\n lines.push(' neew list');\n lines.push(' neew create <generator> [options]');\n lines.push(' neew <generator> [options]');\n lines.push('');\n lines.push('Options:');\n for (const [flag, description] of GLOBAL_OPTIONS) {\n lines.push(` ${flag.padEnd(16)} ${description}`);\n }\n lines.push('');\n lines.push('Generators:');\n const generatorLines = entries.flatMap((entry) =>\n entry.generators.map(\n (generator) =>\n ` ${generator.name}${generator.description ? ` ${generator.description}` : ''}`,\n ),\n );\n if (generatorLines.length === 0) {\n lines.push(' (none registered)');\n } else {\n lines.push(...generatorLines);\n }\n return lines.join('\\n');\n}\n\nfunction parameterLines(generator: DiscoveredGenerator): string[] {\n const prompts = (generator.generator.prompts ?? {}) as Record<string, AnyPrompt>;\n const keys = Object.keys(prompts);\n if (keys.length === 0) return [' (no parameters)'];\n return keys.map((key) => {\n const prompt = prompts[key];\n const flag = `--${flagNameFor(key)}`;\n const value = prompt.type === 'confirm' ? '' : ' <value>';\n const alias = prompt.alias ? `-${prompt.alias}, ` : '';\n const required = prompt.default === undefined ? ' (required)' : '';\n const fallback = prompt.default === undefined ? '' : ` [default: ${prompt.default}]`;\n const description = prompt.message ? ` ${prompt.message}` : '';\n return ` ${alias}${flag}${value}${description}${required}${fallback}`;\n });\n}\n\nfunction generatorHelpText(generator: DiscoveredGenerator): string {\n const name = generator.generator.name;\n const lines: string[] = [];\n lines.push(\n `neew ${name}${generator.generator.description ? ` - ${generator.generator.description}` : ''}`,\n );\n lines.push('');\n lines.push('Usage:');\n lines.push(` neew ${name} [options]`);\n lines.push(` neew create ${name} [options]`);\n lines.push('');\n lines.push('Parameters:');\n lines.push(...parameterLines(generator));\n lines.push('');\n lines.push('Options:');\n for (const [flag, description] of GLOBAL_OPTIONS) {\n if (flag === '--target <dir>') lines.push(` ${flag.padEnd(16)} ${description}`);\n }\n lines.push(' --dry-run Report the files that would be written without writing them');\n lines.push(' --force Replace existing files that collide with generated output');\n lines.push(' -h, --help Show this help');\n lines.push(' -v, --version Show the version');\n return lines.join('\\n');\n}\n\nfunction readVersion(): string {\n let dir = dirname(fileURLToPath(import.meta.url));\n while (true) {\n const packageJsonPath = join(dir, 'package.json');\n if (existsSync(packageJsonPath)) {\n try {\n const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf8')) as {\n name?: string;\n version?: string;\n };\n if (pkg.name === '@trut/neew') return pkg.version ?? '0.0.0';\n } catch {\n // ignore and keep walking up\n }\n }\n const parent = dirname(dir);\n if (parent === dir) return '0.0.0';\n dir = parent;\n }\n}\n\nfunction availableNames(entries: RegistryEntry[]): string {\n const names = entries.flatMap((entry) => entry.generators.map((generator) => generator.name));\n return names.length === 0 ? '(none)' : names.join(', ');\n}\n\nfunction printUsage(context: Context): void {\n let entries: RegistryEntry[] = [];\n try {\n entries = listRegistered(context.registryDeps);\n } catch (error) {\n context.stderr(`warning: ${error instanceof Error ? error.message : String(error)}`);\n }\n context.stdout(usageText(entries));\n}\n\nfunction report(\n stdout: (message: string) => void,\n generatorName: string,\n files: string[],\n dryRun: boolean,\n): void {\n if (dryRun) {\n stdout(`Dry run for \"${generatorName}\": ${files.length} file(s) would be written.`);\n } else {\n stdout(`Generator \"${generatorName}\" wrote ${files.length} file(s).`);\n }\n for (const file of files) stdout(` ${file}`);\n}\n\nfunction extractTarget(args: string[], cwd: string): { targetDir: string; rest: string[] } {\n const rest: string[] = [];\n let target: string | undefined;\n for (let index = 0; index < args.length; index += 1) {\n const arg = args[index];\n if (arg === '--target') {\n target = args[index + 1];\n index += 1;\n continue;\n }\n if (arg.startsWith('--target=')) {\n target = arg.slice('--target='.length);\n continue;\n }\n rest.push(arg);\n }\n return { targetDir: target ? resolve(cwd, target) : cwd, rest };\n}\n\nasync function addCommand(rest: string[], context: Context): Promise<CliResult> {\n const link = rest.find((arg) => !arg.startsWith('-'));\n if (!link) throw new Error('Usage: neew add <link>');\n const entry = await addSource(link, context.registryDeps);\n context.stdout(`Added ${entry.source}`);\n for (const generator of entry.generators) {\n context.stdout(\n ` ${generator.name}${generator.description ? ` ${generator.description}` : ''}`,\n );\n }\n return { kind: 'added', entry };\n}\n\nfunction removeCommand(rest: string[], context: Context): CliResult {\n const link = rest.find((arg) => !arg.startsWith('-'));\n if (!link) throw new Error('Usage: neew remove <link>');\n const entry = removeSource(link, context.registryDeps);\n context.stdout(`Removed ${entry.source}`);\n return { kind: 'removed', entry };\n}\n\nfunction listCommand(context: Context): CliResult {\n const entries = listRegistered(context.registryDeps);\n if (entries.length === 0) {\n context.stdout('No repositories registered.');\n return { kind: 'list', entries };\n }\n for (const entry of entries) {\n context.stdout(entry.source);\n if (entry.generators.length === 0) {\n context.stdout(' (no generators)');\n }\n for (const generator of entry.generators) {\n context.stdout(\n ` ${generator.name}${generator.description ? ` ${generator.description}` : ''}`,\n );\n }\n }\n return { kind: 'list', entries };\n}\n\nasync function loadGenerator(\n name: string,\n context: Context,\n): Promise<{ found: DiscoveredGenerator; entry: RegistryEntry; repoDir: string }> {\n const entries = listRegistered(context.registryDeps);\n const located = findGeneratorByName(entries, name);\n if (!located) {\n throw new Error(\n `Unknown generator \"${name}\". Available generators: ${availableNames(entries)}.`,\n );\n }\n const source = sourceFromEntry(located.entry);\n const ensured = await ensure(source, context.registryDeps);\n const jiti = createRepositoryJiti(ensured.dir);\n const barrel = await loadBarrel(ensured.dir, { jiti });\n const discovered = await discoverGenerators({\n configDir: ensured.dir,\n patterns: barrel.config.generators,\n jiti,\n });\n if (ensured.changed) {\n const indexed = discovered.map((entry) => ({\n name: entry.generator.name,\n path: relative(ensured.dir, entry.modulePath),\n ...(entry.generator.description ? { description: entry.generator.description } : {}),\n }));\n const registry = readRegistry(context.env);\n writeRegistry(\n {\n repositories: {\n ...registry.repositories,\n [located.entry.source]: { ...located.entry, generators: indexed },\n },\n },\n context.env,\n );\n }\n const found = discovered.find((entry) => entry.generator.name === name);\n if (!found) {\n const names = discovered.map((entry) => entry.generator.name);\n throw new Error(\n `Generator \"${name}\" is no longer provided by ${located.entry.source}. Available generators: ${\n names.length === 0 ? '(none)' : names.join(', ')\n }.`,\n );\n }\n return { found, entry: located.entry, repoDir: ensured.dir };\n}\n\nasync function invoke(argv: string[], context: Context, isCreate: boolean): Promise<CliResult> {\n const argOffset = isCreate ? 1 : 0;\n const generatorArgv = argv.slice(argOffset);\n const { global, generatorName } = parseGlobalFlags(generatorArgv);\n if (global.version) {\n const version = readVersion();\n context.stdout(version);\n return { kind: 'version', version };\n }\n if (!generatorName) {\n printUsage(context);\n return { kind: 'help' };\n }\n\n const { found, repoDir } = await loadGenerator(generatorName, context);\n\n if (global.help) {\n context.stdout(generatorHelpText(found));\n return { kind: 'generator-help', generator: generatorName };\n }\n\n const { targetDir, rest } = extractTarget(generatorArgv, context.cwd);\n const values = parsePromptValues(rest, found.generator.prompts ?? {});\n const answers = await resolvePrompts({\n prompts: found.generator.prompts,\n values,\n adapter: context.adapter,\n });\n const result = await runGenerator({\n generator: found.generator,\n configDir: targetDir,\n moduleDir: found.moduleDir || repoDir,\n answers,\n dryRun: global.dryRun,\n force: global.force,\n });\n report(context.stdout, generatorName, result.files, global.dryRun);\n return {\n kind: 'run',\n generator: generatorName,\n files: result.files,\n written: result.written,\n };\n}\n\nexport async function runCli(argv: string[], options: CliOptions = {}): Promise<CliResult> {\n const env = options.env ?? process.env;\n const stdout = options.stdout ?? ((message: string) => process.stdout.write(`${message}\\n`));\n const stderr = options.stderr ?? ((message: string) => process.stderr.write(`${message}\\n`));\n const cacheRoot = resolveCacheRoot(env);\n const context: Context = {\n env,\n cwd: options.cwd ?? process.cwd(),\n stdout,\n stderr,\n adapter: options.adapter ?? createClackAdapter(),\n registryDeps: {\n cacheRoot,\n env,\n ...(options.resolveCommit ? { resolveCommit: options.resolveCommit } : {}),\n ...(options.download ? { download: options.download } : {}),\n ...(options.downloadDeps ? { downloadDeps: options.downloadDeps } : {}),\n ...(options.resolveDeps ? { resolveDeps: options.resolveDeps } : {}),\n warn: (message) => stderr(`warning: ${message}`),\n },\n };\n\n const [command, ...rest] = argv;\n if (command === undefined) {\n printUsage(context);\n return { kind: 'help' };\n }\n if (command === '-h' || command === '--help') {\n printUsage(context);\n return { kind: 'help' };\n }\n if (command === '-v' || command === '--version') {\n const version = readVersion();\n context.stdout(version);\n return { kind: 'version', version };\n }\n if (command === 'add') return addCommand(rest, context);\n if (command === 'remove') return removeCommand(rest, context);\n if (command === 'list') return listCommand(context);\n if (command === 'create') return invoke(argv, context, true);\n return invoke(argv, context, false);\n}\n","#!/usr/bin/env node\nimport { PromptCancelledError } from '@trut/gen';\nimport { runCli } from './cli/run';\n\nrunCli(process.argv.slice(2)).catch((error: unknown) => {\n const message = error instanceof Error ? error.message : String(error);\n process.stderr.write(`\\n${message}\\n`);\n process.exitCode = error instanceof PromptCancelledError ? 130 : 1;\n});\n"],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ //#region src/config/generators-config.d.ts
2
+ interface GeneratorsConfig {
3
+ generators: string[];
4
+ }
5
+ export declare function defineGenerators<const TConfig extends GeneratorsConfig>(config: TConfig): TConfig;
6
+ //#endregion
7
+ export type { GeneratorsConfig };
8
+ //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs ADDED
@@ -0,0 +1,8 @@
1
+ //#region src/config/generators-config.ts
2
+ function defineGenerators(config) {
3
+ return config;
4
+ }
5
+ //#endregion
6
+ export { defineGenerators };
7
+
8
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/config/generators-config.ts"],"sourcesContent":["export interface GeneratorsConfig {\n generators: string[];\n}\n\nexport function defineGenerators<const TConfig extends GeneratorsConfig>(config: TConfig): TConfig {\n return config;\n}\n"],"mappings":";AAIA,SAAgB,iBAAyD,QAA0B;CACjG,OAAO;AACT"}
package/package.json CHANGED
@@ -1,6 +1,54 @@
1
1
  {
2
2
  "name": "@trut/neew",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.1.0",
4
+ "description": "",
5
+ "keywords": [],
6
+ "license": "ISC",
7
+ "author": {
8
+ "name": "Jakub Hajduk",
9
+ "email": "devhajduk@gmail.com"
10
+ },
11
+ "bin": {
12
+ "neew": "./dist/cli.mjs"
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "type": "module",
18
+ "exports": {
19
+ ".": {
20
+ "types": "./dist/index.d.mts",
21
+ "import": "./dist/index.mjs"
22
+ }
23
+ },
24
+ "publishConfig": {
25
+ "access": "public"
26
+ },
27
+ "scripts": {
28
+ "build": "tsdown",
29
+ "dev": "tsdown --watch",
30
+ "test": "node --import tsx --test",
31
+ "check": "tsc --noEmit && oxlint && oxfmt --check",
32
+ "fix": "oxlint --fix && oxfmt --write",
33
+ "release": "pnpm run build && release-it"
34
+ },
35
+ "dependencies": {
36
+ "@clack/prompts": "^1.8.1",
37
+ "@trut/gen": "^0.1.0",
38
+ "isomorphic-git": "^1.44.3",
39
+ "jiti": "^2.7.0",
40
+ "tar": "^7.5.22"
41
+ },
42
+ "devDependencies": {
43
+ "@arethetypeswrong/core": "^0.18.5",
44
+ "@types/node": "^26.6.5",
45
+ "oxfmt": "^0.73.0",
46
+ "oxlint": "^1.88.0",
47
+ "publint": "^0.3.25",
48
+ "release-it": "^21.1.1",
49
+ "tsdown": "^0.23.1",
50
+ "tsx": "^4.19.2",
51
+ "typescript": "^7.0.2"
52
+ },
53
+ "packageManager": "pnpm@12.14.0"
54
+ }