@kurotako/cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/LICENSE +21 -0
- package/README.md +44 -0
- package/dist/bin/tako.d.ts +1 -0
- package/dist/bin/tako.js +673 -0
- package/dist/bin/tako.js.map +1 -0
- package/dist/index.cjs +714 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +84 -0
- package/dist/index.d.ts +84 -0
- package/dist/index.js +675 -0
- package/dist/index.js.map +1 -0
- package/package.json +62 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,714 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
ConfigExistsError: () => ConfigExistsError,
|
|
34
|
+
ConsoleReporter: () => ConsoleReporter,
|
|
35
|
+
comparePlanToDisk: () => comparePlanToDisk,
|
|
36
|
+
renderError: () => renderError,
|
|
37
|
+
runCli: () => runCli
|
|
38
|
+
});
|
|
39
|
+
module.exports = __toCommonJS(index_exports);
|
|
40
|
+
|
|
41
|
+
// src/cli.ts
|
|
42
|
+
var import_core6 = require("@kurotako/core");
|
|
43
|
+
var import_citty5 = require("citty");
|
|
44
|
+
|
|
45
|
+
// src/commands/check.ts
|
|
46
|
+
var import_node_fs2 = require("fs");
|
|
47
|
+
var import_node_path2 = require("path");
|
|
48
|
+
var import_config = require("@kurotako/config");
|
|
49
|
+
var import_core2 = require("@kurotako/core");
|
|
50
|
+
var import_citty = require("citty");
|
|
51
|
+
|
|
52
|
+
// src/args.ts
|
|
53
|
+
var sharedArgs = {
|
|
54
|
+
config: {
|
|
55
|
+
type: "string",
|
|
56
|
+
description: "path to the tako config file (default: ./tako.config.ts)"
|
|
57
|
+
},
|
|
58
|
+
/** Hidden: not advertised, kept minimal. Enables `debug` reporter output. */
|
|
59
|
+
debug: {
|
|
60
|
+
type: "boolean",
|
|
61
|
+
default: false
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// src/diff.ts
|
|
66
|
+
var import_node_fs = require("fs");
|
|
67
|
+
var import_promises = require("fs/promises");
|
|
68
|
+
var import_node_path = __toESM(require("path"), 1);
|
|
69
|
+
function toPosix(rootDir, absPath) {
|
|
70
|
+
return import_node_path.default.relative(rootDir, absPath).split(import_node_path.default.sep).join("/");
|
|
71
|
+
}
|
|
72
|
+
async function comparePlanToDisk(args) {
|
|
73
|
+
const { plan, roots, ignore, rootDir } = args;
|
|
74
|
+
const plannedByPath = new Map(plan.map((f) => [import_node_path.default.resolve(f.path), f]));
|
|
75
|
+
const entries = [];
|
|
76
|
+
for (const [absPath, file] of plannedByPath) {
|
|
77
|
+
if (!(0, import_node_fs.existsSync)(absPath)) {
|
|
78
|
+
entries.push({ kind: "missing", path: toPosix(rootDir, absPath) });
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
const onDisk = await (0, import_promises.readFile)(absPath, "utf8");
|
|
82
|
+
if (onDisk !== file.content) {
|
|
83
|
+
entries.push({ kind: "modified", path: toPosix(rootDir, absPath) });
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const plannedPaths = [...plannedByPath.keys()];
|
|
87
|
+
const hasPlannedUnder = (dir) => {
|
|
88
|
+
const prefix = `${dir}${import_node_path.default.sep}`;
|
|
89
|
+
return plannedPaths.some((p) => p === dir || p.startsWith(prefix));
|
|
90
|
+
};
|
|
91
|
+
const seenRoots = /* @__PURE__ */ new Set();
|
|
92
|
+
for (const root of roots) {
|
|
93
|
+
const absRoot = import_node_path.default.resolve(root);
|
|
94
|
+
if (seenRoots.has(absRoot) || !(0, import_node_fs.existsSync)(absRoot)) {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
seenRoots.add(absRoot);
|
|
98
|
+
await scan(absRoot);
|
|
99
|
+
}
|
|
100
|
+
async function scan(dir) {
|
|
101
|
+
if (!hasPlannedUnder(dir)) {
|
|
102
|
+
if (await hasNonIgnoredFile(dir)) {
|
|
103
|
+
entries.push({ kind: "orphan", path: `${toPosix(rootDir, dir)}/` });
|
|
104
|
+
}
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
for (const dirent of await (0, import_promises.readdir)(dir, { withFileTypes: true })) {
|
|
108
|
+
const abs = import_node_path.default.join(dir, dirent.name);
|
|
109
|
+
if (ignore(abs)) {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if (dirent.isDirectory()) {
|
|
113
|
+
await scan(abs);
|
|
114
|
+
} else if (dirent.isFile() && !plannedByPath.has(abs)) {
|
|
115
|
+
entries.push({ kind: "orphan", path: toPosix(rootDir, abs) });
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
async function hasNonIgnoredFile(dir) {
|
|
120
|
+
for (const dirent of await (0, import_promises.readdir)(dir, { withFileTypes: true })) {
|
|
121
|
+
const abs = import_node_path.default.join(dir, dirent.name);
|
|
122
|
+
if (ignore(abs)) {
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if (dirent.isFile()) {
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
if (dirent.isDirectory() && await hasNonIgnoredFile(abs)) {
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
entries.sort(
|
|
135
|
+
(a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : a.kind < b.kind ? -1 : a.kind > b.kind ? 1 : 0
|
|
136
|
+
);
|
|
137
|
+
return entries;
|
|
138
|
+
}
|
|
139
|
+
function ignoreBuildArtifacts(absPath) {
|
|
140
|
+
return /(^|[/\\])(dist|node_modules)([/\\]|$)/.test(absPath);
|
|
141
|
+
}
|
|
142
|
+
async function hasGeneratedMarker(pkgDir) {
|
|
143
|
+
try {
|
|
144
|
+
const pkg = JSON.parse(
|
|
145
|
+
await (0, import_promises.readFile)(import_node_path.default.join(pkgDir, "package.json"), "utf8")
|
|
146
|
+
);
|
|
147
|
+
return typeof pkg["//"] === "string" && pkg["//"].startsWith("Generated by tako");
|
|
148
|
+
} catch {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
async function markedPackageDirs(packagesDir) {
|
|
153
|
+
if (!(0, import_node_fs.existsSync)(packagesDir)) {
|
|
154
|
+
return [];
|
|
155
|
+
}
|
|
156
|
+
const out = [];
|
|
157
|
+
for (const dirent of await (0, import_promises.readdir)(packagesDir, { withFileTypes: true })) {
|
|
158
|
+
if (!dirent.isDirectory()) {
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
const abs = import_node_path.default.join(packagesDir, dirent.name);
|
|
162
|
+
if (await hasGeneratedMarker(abs)) {
|
|
163
|
+
out.push(abs);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return out.sort();
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// src/reporter.ts
|
|
170
|
+
var import_core = require("@kurotako/core");
|
|
171
|
+
var ESC = String.fromCharCode(27);
|
|
172
|
+
var ANSI = {
|
|
173
|
+
debug: `${ESC}[2m`,
|
|
174
|
+
// dim
|
|
175
|
+
info: `${ESC}[36m`,
|
|
176
|
+
// cyan
|
|
177
|
+
warn: `${ESC}[33m`,
|
|
178
|
+
// yellow
|
|
179
|
+
error: `${ESC}[31m`
|
|
180
|
+
// red
|
|
181
|
+
};
|
|
182
|
+
var ANSI_RESET = `${ESC}[0m`;
|
|
183
|
+
var ConsoleReporter = class {
|
|
184
|
+
stream;
|
|
185
|
+
showDebug;
|
|
186
|
+
color;
|
|
187
|
+
constructor(options = {}) {
|
|
188
|
+
this.stream = options.stream ?? process.stderr;
|
|
189
|
+
this.showDebug = options.debug ?? Boolean(process.env.TAKO_DEBUG);
|
|
190
|
+
this.color = options.color ?? (Boolean(this.stream.isTTY) && process.env.NO_COLOR === void 0);
|
|
191
|
+
}
|
|
192
|
+
debug(msg, meta) {
|
|
193
|
+
if (this.showDebug) {
|
|
194
|
+
this.write("debug", msg, meta);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
info(msg, meta) {
|
|
198
|
+
this.write("info", msg, meta);
|
|
199
|
+
}
|
|
200
|
+
warn(msg, meta) {
|
|
201
|
+
this.write("warn", msg, meta);
|
|
202
|
+
}
|
|
203
|
+
error(msg, meta) {
|
|
204
|
+
this.write("error", msg, meta);
|
|
205
|
+
}
|
|
206
|
+
/** A child `Logger` whose calls carry `{ scope: tag }` in `meta`. */
|
|
207
|
+
child(tag) {
|
|
208
|
+
return (0, import_core.childLogger)(this, { scope: tag });
|
|
209
|
+
}
|
|
210
|
+
write(level, msg, meta) {
|
|
211
|
+
const prefix = this.color ? `${ANSI[level]}tako${ANSI_RESET}` : "tako";
|
|
212
|
+
const detail = meta === void 0 ? "" : ` ${formatMeta(meta)}`;
|
|
213
|
+
this.stream.write(`${prefix} ${msg}${detail}
|
|
214
|
+
`);
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
function formatMeta(meta) {
|
|
218
|
+
if (meta !== null && typeof meta === "object" && !Array.isArray(meta)) {
|
|
219
|
+
return Object.entries(meta).map(([k, v]) => `${k}=${stringify(v)}`).join(" ");
|
|
220
|
+
}
|
|
221
|
+
return stringify(meta);
|
|
222
|
+
}
|
|
223
|
+
function stringify(value) {
|
|
224
|
+
if (typeof value === "string") {
|
|
225
|
+
return value;
|
|
226
|
+
}
|
|
227
|
+
try {
|
|
228
|
+
return JSON.stringify(value) ?? String(value);
|
|
229
|
+
} catch {
|
|
230
|
+
return String(value);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// src/commands/check.ts
|
|
235
|
+
var checkCommand = (0, import_citty.defineCommand)({
|
|
236
|
+
meta: {
|
|
237
|
+
name: "check",
|
|
238
|
+
description: "check the generated output on disk matches a fresh generate (drift guard)"
|
|
239
|
+
},
|
|
240
|
+
args: { ...sharedArgs },
|
|
241
|
+
run: async ({ args }) => {
|
|
242
|
+
const reporter = new ConsoleReporter({ debug: Boolean(args.debug) });
|
|
243
|
+
const cwd = process.cwd();
|
|
244
|
+
const { config } = await (0, import_config.loadConfig)({
|
|
245
|
+
cwd,
|
|
246
|
+
configPath: args.config || void 0
|
|
247
|
+
});
|
|
248
|
+
const { plan = [] } = await (0, import_core2.run)(config, { logger: reporter, plan: true });
|
|
249
|
+
const roots = [];
|
|
250
|
+
const ignores = [];
|
|
251
|
+
for (const output of config.outputs) {
|
|
252
|
+
if (output.mode === "package") {
|
|
253
|
+
ignores.push(ignoreBuildArtifacts);
|
|
254
|
+
if (output.packagesDir) {
|
|
255
|
+
roots.push(...await markedPackageDirs(output.packagesDir));
|
|
256
|
+
}
|
|
257
|
+
} else {
|
|
258
|
+
const dir = output.dir;
|
|
259
|
+
if (!dir) {
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
if (!(0, import_node_fs2.existsSync)(dir)) {
|
|
263
|
+
reporter.info(
|
|
264
|
+
`output directory ${(0, import_node_path2.relative)(config.rootDir, dir)} does not exist \u2014 run tako generate`
|
|
265
|
+
);
|
|
266
|
+
process.exitCode = 1;
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
roots.push(dir);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
const ignore = (absPath) => ignores.some((fn) => fn(absPath));
|
|
273
|
+
const drift = await comparePlanToDisk({
|
|
274
|
+
plan,
|
|
275
|
+
roots,
|
|
276
|
+
ignore,
|
|
277
|
+
rootDir: config.rootDir
|
|
278
|
+
});
|
|
279
|
+
if (drift.length === 0) {
|
|
280
|
+
reporter.info("output is in sync");
|
|
281
|
+
process.exitCode ??= 0;
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
reporter.error(formatDrift(drift));
|
|
285
|
+
process.exitCode = 1;
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
function formatDrift(drift) {
|
|
289
|
+
const width = Math.max(...drift.map((entry) => entry.kind.length));
|
|
290
|
+
const lines = drift.map(
|
|
291
|
+
(entry) => ` ${entry.kind.padEnd(width)} ${entry.path}`
|
|
292
|
+
);
|
|
293
|
+
return [
|
|
294
|
+
"drift detected",
|
|
295
|
+
"",
|
|
296
|
+
...lines,
|
|
297
|
+
"",
|
|
298
|
+
`${drift.length} files differ from a fresh generate. Run \`tako generate\`.`
|
|
299
|
+
].join("\n");
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// src/commands/generate.ts
|
|
303
|
+
var import_node_path4 = require("path");
|
|
304
|
+
var import_config3 = require("@kurotako/config");
|
|
305
|
+
var import_core5 = require("@kurotako/core");
|
|
306
|
+
var import_citty2 = require("citty");
|
|
307
|
+
|
|
308
|
+
// src/watch.ts
|
|
309
|
+
var import_node_path3 = require("path");
|
|
310
|
+
var import_config2 = require("@kurotako/config");
|
|
311
|
+
var import_core4 = require("@kurotako/core");
|
|
312
|
+
var import_chokidar = __toESM(require("chokidar"), 1);
|
|
313
|
+
|
|
314
|
+
// src/errors.ts
|
|
315
|
+
var import_core3 = require("@kurotako/core");
|
|
316
|
+
var ConfigExistsError = class extends import_core3.TakoError {
|
|
317
|
+
path;
|
|
318
|
+
constructor(path2) {
|
|
319
|
+
super(
|
|
320
|
+
"config_exists",
|
|
321
|
+
`a config file already exists at '${path2}' (pass --force to overwrite)`
|
|
322
|
+
);
|
|
323
|
+
this.path = path2;
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
function renderError(error) {
|
|
327
|
+
const lines = [`error [${error.code}]: ${error.message}`];
|
|
328
|
+
const issues = error.issues;
|
|
329
|
+
if (Array.isArray(issues)) {
|
|
330
|
+
for (const issue of issues) {
|
|
331
|
+
const at = issue.path === void 0 || issue.path === "" ? "<root>" : issue.path;
|
|
332
|
+
lines.push(` - ${at}: ${issue.message}`);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
const cycle = error.cycle;
|
|
336
|
+
if (Array.isArray(cycle) && cycle.length > 0) {
|
|
337
|
+
lines.push(` cycle: ${cycle.join(" -> ")}`);
|
|
338
|
+
}
|
|
339
|
+
const driverName = error.driverName;
|
|
340
|
+
if (typeof driverName === "string") {
|
|
341
|
+
const role = error.role;
|
|
342
|
+
const namespace = error.namespace;
|
|
343
|
+
const roleLabel = typeof role === "string" ? role : "driver";
|
|
344
|
+
const where = typeof namespace === "string" ? ` (namespace '${namespace}')` : "";
|
|
345
|
+
lines.push(` ${roleLabel}: ${driverName}${where}`);
|
|
346
|
+
} else {
|
|
347
|
+
const generator = error.generator;
|
|
348
|
+
if (typeof generator === "string") {
|
|
349
|
+
const namespace = error.namespace;
|
|
350
|
+
const where = typeof namespace === "string" ? ` (namespace '${namespace}')` : "";
|
|
351
|
+
lines.push(` generator: ${generator}${where}`);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
const cause = error.cause;
|
|
355
|
+
if (cause !== void 0 && cause !== null) {
|
|
356
|
+
const message = cause instanceof Error ? cause.message : String(cause);
|
|
357
|
+
const code = cause instanceof import_core3.TakoError ? ` [${cause.code}]` : "";
|
|
358
|
+
lines.push(` cause${code}: ${message}`);
|
|
359
|
+
}
|
|
360
|
+
return lines.join("\n");
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// src/watch.ts
|
|
364
|
+
var DEBOUNCE_MS = 100;
|
|
365
|
+
function chokidarWatcher() {
|
|
366
|
+
const fsw = import_chokidar.default.watch([], {
|
|
367
|
+
ignoreInitial: true,
|
|
368
|
+
awaitWriteFinish: { stabilityThreshold: 150, pollInterval: 20 }
|
|
369
|
+
});
|
|
370
|
+
return {
|
|
371
|
+
add: (paths) => fsw.add(paths),
|
|
372
|
+
unwatch: (paths) => fsw.unwatch(paths),
|
|
373
|
+
on: (handler) => {
|
|
374
|
+
fsw.on("add", (p) => handler("add", p));
|
|
375
|
+
fsw.on("change", (p) => handler("change", p));
|
|
376
|
+
fsw.on("unlink", (p) => handler("unlink", p));
|
|
377
|
+
},
|
|
378
|
+
close: () => fsw.close()
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
function isAbortError(error) {
|
|
382
|
+
return error instanceof Error && error.name === "AbortError";
|
|
383
|
+
}
|
|
384
|
+
async function watchAndRun(opts, deps = {}) {
|
|
385
|
+
const { cwd, configPath, reporter } = opts;
|
|
386
|
+
const createWatcher = deps.createWatcher ?? chokidarWatcher;
|
|
387
|
+
const loadConfig3 = deps.loadConfig ?? import_config2.loadConfig;
|
|
388
|
+
const run3 = deps.run ?? import_core4.run;
|
|
389
|
+
const debounceMs = deps.debounceMs ?? DEBOUNCE_MS;
|
|
390
|
+
const watcher = createWatcher();
|
|
391
|
+
let watched = /* @__PURE__ */ new Set();
|
|
392
|
+
let inFlight = null;
|
|
393
|
+
let queued = false;
|
|
394
|
+
let closed = false;
|
|
395
|
+
let timer = null;
|
|
396
|
+
let reason = "initial build";
|
|
397
|
+
const guessedConfigFile = configPath ? (0, import_node_path3.resolve)(cwd, configPath) : (0, import_node_path3.resolve)(cwd, "tako.config.ts");
|
|
398
|
+
function applyWatchSet(desired) {
|
|
399
|
+
for (const path2 of desired) {
|
|
400
|
+
if (!watched.has(path2)) {
|
|
401
|
+
watcher.add(path2);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
for (const path2 of watched) {
|
|
405
|
+
if (!desired.has(path2)) {
|
|
406
|
+
watcher.unwatch(path2);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
watched = desired;
|
|
410
|
+
}
|
|
411
|
+
async function reconcileWatchSet(loaded) {
|
|
412
|
+
const desired = /* @__PURE__ */ new Set([loaded.configFile]);
|
|
413
|
+
for (const [namespace, source] of Object.entries(loaded.config.sources)) {
|
|
414
|
+
const paths = await source.parser.watchPaths?.({
|
|
415
|
+
namespace,
|
|
416
|
+
cwd: loaded.rootDir,
|
|
417
|
+
logger: reporter
|
|
418
|
+
}) ?? [];
|
|
419
|
+
for (const path2 of paths) {
|
|
420
|
+
desired.add((0, import_node_path3.resolve)(loaded.rootDir, path2));
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
applyWatchSet(desired);
|
|
424
|
+
}
|
|
425
|
+
async function cycle() {
|
|
426
|
+
const why = reason;
|
|
427
|
+
reporter.info(`--- rebuild (${why}) ---`);
|
|
428
|
+
const ac = new AbortController();
|
|
429
|
+
inFlight = ac;
|
|
430
|
+
try {
|
|
431
|
+
const loaded = await loadConfig3({ cwd, configPath });
|
|
432
|
+
await reconcileWatchSet(loaded);
|
|
433
|
+
const result = await run3(loaded.config, {
|
|
434
|
+
logger: reporter,
|
|
435
|
+
signal: ac.signal,
|
|
436
|
+
write: true
|
|
437
|
+
});
|
|
438
|
+
if (!ac.signal.aborted) {
|
|
439
|
+
reporter.info(`wrote ${result.files.length} files`);
|
|
440
|
+
}
|
|
441
|
+
} catch (error) {
|
|
442
|
+
if (isAbortError(error) || ac.signal.aborted) {
|
|
443
|
+
} else if (error instanceof import_core4.TakoError) {
|
|
444
|
+
reporter.error(renderError(error));
|
|
445
|
+
applyWatchSet(/* @__PURE__ */ new Set([...watched, guessedConfigFile]));
|
|
446
|
+
} else {
|
|
447
|
+
reporter.error("internal error (this is a bug):");
|
|
448
|
+
console.error(error);
|
|
449
|
+
}
|
|
450
|
+
} finally {
|
|
451
|
+
inFlight = null;
|
|
452
|
+
if (queued && !closed) {
|
|
453
|
+
queued = false;
|
|
454
|
+
await cycle();
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
function trigger() {
|
|
459
|
+
if (inFlight) {
|
|
460
|
+
queued = true;
|
|
461
|
+
inFlight.abort();
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
void cycle();
|
|
465
|
+
}
|
|
466
|
+
function schedule(event, path2) {
|
|
467
|
+
if (closed) {
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
reason = `${(0, import_node_path3.basename)(path2)} ${event === "unlink" ? "removed" : "changed"}`;
|
|
471
|
+
if (timer) {
|
|
472
|
+
clearTimeout(timer);
|
|
473
|
+
}
|
|
474
|
+
timer = setTimeout(() => {
|
|
475
|
+
timer = null;
|
|
476
|
+
trigger();
|
|
477
|
+
}, debounceMs);
|
|
478
|
+
}
|
|
479
|
+
watcher.on(schedule);
|
|
480
|
+
applyWatchSet(/* @__PURE__ */ new Set([guessedConfigFile]));
|
|
481
|
+
await cycle();
|
|
482
|
+
reporter.info(`watching ${watched.size} paths`);
|
|
483
|
+
await new Promise((resolvePromise) => {
|
|
484
|
+
const shutdown = () => {
|
|
485
|
+
closed = true;
|
|
486
|
+
if (timer) {
|
|
487
|
+
clearTimeout(timer);
|
|
488
|
+
}
|
|
489
|
+
inFlight?.abort();
|
|
490
|
+
process.off("SIGINT", shutdown);
|
|
491
|
+
process.off("SIGTERM", shutdown);
|
|
492
|
+
void watcher.close().finally(() => resolvePromise());
|
|
493
|
+
};
|
|
494
|
+
process.on("SIGINT", shutdown);
|
|
495
|
+
process.on("SIGTERM", shutdown);
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// src/commands/generate.ts
|
|
500
|
+
function outputSummaryDir(output, config) {
|
|
501
|
+
return output.mode === "package" ? output.packagesDir ?? config.rootDir : output.dir ?? config.rootDir;
|
|
502
|
+
}
|
|
503
|
+
async function loadAndRun(opts) {
|
|
504
|
+
const loaded = await (0, import_config3.loadConfig)({
|
|
505
|
+
cwd: opts.cwd,
|
|
506
|
+
configPath: opts.configPath
|
|
507
|
+
});
|
|
508
|
+
const result = await (0, import_core5.run)(loaded.config, {
|
|
509
|
+
logger: opts.reporter,
|
|
510
|
+
write: opts.write,
|
|
511
|
+
signal: opts.signal
|
|
512
|
+
});
|
|
513
|
+
return {
|
|
514
|
+
result,
|
|
515
|
+
config: loaded.config,
|
|
516
|
+
configFile: loaded.configFile,
|
|
517
|
+
rootDir: loaded.rootDir
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
var generateCommand = (0, import_citty2.defineCommand)({
|
|
521
|
+
meta: {
|
|
522
|
+
name: "generate",
|
|
523
|
+
description: "run the pipeline and write the generated code"
|
|
524
|
+
},
|
|
525
|
+
args: {
|
|
526
|
+
...sharedArgs,
|
|
527
|
+
watch: {
|
|
528
|
+
type: "boolean",
|
|
529
|
+
description: "rebuild on every schema / config change",
|
|
530
|
+
default: false
|
|
531
|
+
},
|
|
532
|
+
"dry-run": {
|
|
533
|
+
type: "boolean",
|
|
534
|
+
description: "run everything but write nothing",
|
|
535
|
+
default: false
|
|
536
|
+
}
|
|
537
|
+
},
|
|
538
|
+
run: async ({ args }) => {
|
|
539
|
+
const reporter = new ConsoleReporter({ debug: Boolean(args.debug) });
|
|
540
|
+
const cwd = process.cwd();
|
|
541
|
+
const configPath = args.config || void 0;
|
|
542
|
+
if (args.watch) {
|
|
543
|
+
await watchAndRun({ cwd, configPath, reporter });
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
const dryRun = Boolean(args["dry-run"]);
|
|
547
|
+
const { result, config } = await loadAndRun({
|
|
548
|
+
cwd,
|
|
549
|
+
configPath,
|
|
550
|
+
write: !dryRun,
|
|
551
|
+
reporter
|
|
552
|
+
});
|
|
553
|
+
if (dryRun) {
|
|
554
|
+
reporter.info(`dry run: ${result.files.length} files would be written`);
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
const dirs = config.outputs.map((output) => (0, import_node_path4.relative)(cwd, outputSummaryDir(output, config))).join(", ");
|
|
558
|
+
reporter.info(`wrote ${result.files.length} files -> ${dirs}`);
|
|
559
|
+
}
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
// src/commands/init.ts
|
|
563
|
+
var import_node_fs3 = require("fs");
|
|
564
|
+
var import_promises2 = require("fs/promises");
|
|
565
|
+
var import_node_path5 = require("path");
|
|
566
|
+
var import_config4 = require("@kurotako/config");
|
|
567
|
+
var import_citty3 = require("citty");
|
|
568
|
+
function detectMonorepo(startDir) {
|
|
569
|
+
let dir = (0, import_node_path5.resolve)(startDir);
|
|
570
|
+
while (true) {
|
|
571
|
+
const pkgPath = (0, import_node_path5.resolve)(dir, "package.json");
|
|
572
|
+
if ((0, import_node_fs3.existsSync)(pkgPath)) {
|
|
573
|
+
if ((0, import_node_fs3.existsSync)((0, import_node_path5.resolve)(dir, "pnpm-workspace.yaml"))) {
|
|
574
|
+
return true;
|
|
575
|
+
}
|
|
576
|
+
try {
|
|
577
|
+
const pkg = JSON.parse((0, import_node_fs3.readFileSync)(pkgPath, "utf8"));
|
|
578
|
+
const ws = pkg.workspaces;
|
|
579
|
+
if (Array.isArray(ws) || typeof ws === "object" && ws !== null && Array.isArray(ws.packages)) {
|
|
580
|
+
return true;
|
|
581
|
+
}
|
|
582
|
+
} catch {
|
|
583
|
+
}
|
|
584
|
+
return false;
|
|
585
|
+
}
|
|
586
|
+
const parent = (0, import_node_path5.dirname)(dir);
|
|
587
|
+
if (parent === dir) {
|
|
588
|
+
return false;
|
|
589
|
+
}
|
|
590
|
+
dir = parent;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
var initCommand = (0, import_citty3.defineCommand)({
|
|
594
|
+
meta: {
|
|
595
|
+
name: "init",
|
|
596
|
+
description: "create a tako.config.ts in the current directory"
|
|
597
|
+
},
|
|
598
|
+
args: {
|
|
599
|
+
...sharedArgs,
|
|
600
|
+
force: {
|
|
601
|
+
type: "boolean",
|
|
602
|
+
description: "overwrite an existing config file",
|
|
603
|
+
default: false
|
|
604
|
+
},
|
|
605
|
+
monorepo: {
|
|
606
|
+
type: "boolean",
|
|
607
|
+
description: "write the monorepo config layout (auto-detected from workspaces when unset)",
|
|
608
|
+
default: void 0
|
|
609
|
+
}
|
|
610
|
+
},
|
|
611
|
+
run: async ({ args }) => {
|
|
612
|
+
const reporter = new ConsoleReporter({ debug: Boolean(args.debug) });
|
|
613
|
+
const cwd = process.cwd();
|
|
614
|
+
const target = args.config ? (0, import_node_path5.resolve)(cwd, args.config) : (0, import_node_path5.resolve)(cwd, "tako.config.ts");
|
|
615
|
+
if ((0, import_node_fs3.existsSync)(target) && !args.force) {
|
|
616
|
+
throw new ConfigExistsError(target);
|
|
617
|
+
}
|
|
618
|
+
const monorepo = typeof args.monorepo === "boolean" ? args.monorepo : detectMonorepo(cwd);
|
|
619
|
+
await (0, import_promises2.mkdir)((0, import_node_path5.dirname)(target), { recursive: true });
|
|
620
|
+
await (0, import_promises2.writeFile)(
|
|
621
|
+
target,
|
|
622
|
+
monorepo ? import_config4.CONFIG_TEMPLATE_MONOREPO : import_config4.CONFIG_TEMPLATE,
|
|
623
|
+
"utf8"
|
|
624
|
+
);
|
|
625
|
+
const name = (0, import_node_path5.relative)(cwd, target) || "tako.config.ts";
|
|
626
|
+
reporter.info(
|
|
627
|
+
monorepo ? `created ${name} (monorepo layout)` : `created ${name}`
|
|
628
|
+
);
|
|
629
|
+
}
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
// src/commands/validate.ts
|
|
633
|
+
var import_citty4 = require("citty");
|
|
634
|
+
var validateCommand = (0, import_citty4.defineCommand)({
|
|
635
|
+
meta: {
|
|
636
|
+
name: "validate",
|
|
637
|
+
description: "check the config and schema without writing anything"
|
|
638
|
+
},
|
|
639
|
+
args: { ...sharedArgs },
|
|
640
|
+
run: async ({ args }) => {
|
|
641
|
+
const reporter = new ConsoleReporter({ debug: Boolean(args.debug) });
|
|
642
|
+
await loadAndRun({
|
|
643
|
+
cwd: process.cwd(),
|
|
644
|
+
configPath: args.config || void 0,
|
|
645
|
+
write: false,
|
|
646
|
+
reporter
|
|
647
|
+
});
|
|
648
|
+
reporter.info("config and schema are valid");
|
|
649
|
+
}
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
// src/cli.ts
|
|
653
|
+
var VERSION = true ? "0.1.0" : "0.0.0-dev";
|
|
654
|
+
var subCommands = {
|
|
655
|
+
init: initCommand,
|
|
656
|
+
generate: generateCommand,
|
|
657
|
+
validate: validateCommand,
|
|
658
|
+
check: checkCommand
|
|
659
|
+
};
|
|
660
|
+
var main = (0, import_citty5.defineCommand)({
|
|
661
|
+
meta: {
|
|
662
|
+
name: "tako",
|
|
663
|
+
version: VERSION,
|
|
664
|
+
description: "synchronise TypeScript schemas from data model to forms"
|
|
665
|
+
},
|
|
666
|
+
subCommands
|
|
667
|
+
});
|
|
668
|
+
async function runCli(argv) {
|
|
669
|
+
const reporter = new ConsoleReporter({
|
|
670
|
+
debug: argv.includes("--debug") || Boolean(process.env.TAKO_DEBUG)
|
|
671
|
+
});
|
|
672
|
+
if (argv.includes("--version") || argv.includes("-v")) {
|
|
673
|
+
process.stdout.write(`${VERSION}
|
|
674
|
+
`);
|
|
675
|
+
process.exitCode ??= 0;
|
|
676
|
+
return;
|
|
677
|
+
}
|
|
678
|
+
if (argv.length === 0 || argv.includes("--help") || argv.includes("-h")) {
|
|
679
|
+
const sub = argv.find((a) => !a.startsWith("-"));
|
|
680
|
+
const isSub = sub !== void 0 && sub in subCommands;
|
|
681
|
+
const cmd = isSub ? subCommands[sub] : main;
|
|
682
|
+
process.stdout.write(
|
|
683
|
+
`${await (0, import_citty5.renderUsage)(cmd, isSub ? main : void 0)}
|
|
684
|
+
`
|
|
685
|
+
);
|
|
686
|
+
process.exitCode ??= 0;
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
try {
|
|
690
|
+
await (0, import_citty5.runCommand)(main, { rawArgs: argv });
|
|
691
|
+
process.exitCode ??= 0;
|
|
692
|
+
} catch (error) {
|
|
693
|
+
if (error instanceof import_core6.TakoError) {
|
|
694
|
+
reporter.error(renderError(error));
|
|
695
|
+
process.exitCode = 1;
|
|
696
|
+
} else if (error instanceof Error && error.name === "CLIError") {
|
|
697
|
+
reporter.error(error.message);
|
|
698
|
+
process.exitCode = 1;
|
|
699
|
+
} else {
|
|
700
|
+
reporter.error("internal error (this is a bug):");
|
|
701
|
+
console.error(error);
|
|
702
|
+
process.exitCode = 1;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
707
|
+
0 && (module.exports = {
|
|
708
|
+
ConfigExistsError,
|
|
709
|
+
ConsoleReporter,
|
|
710
|
+
comparePlanToDisk,
|
|
711
|
+
renderError,
|
|
712
|
+
runCli
|
|
713
|
+
});
|
|
714
|
+
//# sourceMappingURL=index.cjs.map
|