@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 +90 -2
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +895 -0
- package/dist/cli.mjs.map +1 -0
- package/dist/index.d.mts +8 -0
- package/dist/index.mjs +8 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +52 -4
package/README.md
CHANGED
|
@@ -1,3 +1,91 @@
|
|
|
1
|
-
#
|
|
1
|
+
# neew
|
|
2
2
|
|
|
3
|
-
|
|
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
|
package/dist/cli.mjs.map
ADDED
|
@@ -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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"}
|
package/dist/index.d.mts
ADDED
|
@@ -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 @@
|
|
|
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": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
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
|
+
}
|