@kurotako/core 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 +12 -0
- package/LICENSE +21 -0
- package/README.md +27 -0
- package/dist/index.cjs +1238 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +435 -0
- package/dist/index.d.ts +435 -0
- package/dist/index.js +1177 -0
- package/dist/index.js.map +1 -0
- package/package.json +53 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1238 @@
|
|
|
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
|
+
BANNER: () => BANNER,
|
|
34
|
+
DependencyCycleError: () => DependencyCycleError,
|
|
35
|
+
DriverError: () => DriverError,
|
|
36
|
+
DuplicateNamespaceError: () => DuplicateNamespaceError,
|
|
37
|
+
GITATTRIBUTES: () => GITATTRIBUTES,
|
|
38
|
+
HookError: () => HookError,
|
|
39
|
+
InvalidDependencyError: () => InvalidDependencyError,
|
|
40
|
+
InvalidOutputPathError: () => InvalidOutputPathError,
|
|
41
|
+
IrValidationError: () => IrValidationError,
|
|
42
|
+
MissingPackageWorkspaceFilesError: () => MissingPackageWorkspaceFilesError,
|
|
43
|
+
NamespaceMismatchError: () => NamespaceMismatchError,
|
|
44
|
+
OutputCollisionError: () => OutputCollisionError,
|
|
45
|
+
OutputNotGeneratedError: () => OutputNotGeneratedError,
|
|
46
|
+
OutputPeerConflictError: () => OutputPeerConflictError,
|
|
47
|
+
PackageBuildError: () => PackageBuildError,
|
|
48
|
+
PackageInstallError: () => PackageInstallError,
|
|
49
|
+
TakoError: () => TakoError,
|
|
50
|
+
UnknownDependencyError: () => UnknownDependencyError,
|
|
51
|
+
UnsupportedOutputModeError: () => UnsupportedOutputModeError,
|
|
52
|
+
applyBanner: () => applyBanner,
|
|
53
|
+
childLogger: () => childLogger,
|
|
54
|
+
collectPeerDependencies: () => collectPeerDependencies,
|
|
55
|
+
directoryWriter: () => directoryWriter,
|
|
56
|
+
noopLogger: () => noopLogger,
|
|
57
|
+
packageWriter: () => packageWriter,
|
|
58
|
+
resolvePackageManager: () => resolvePackageManager,
|
|
59
|
+
run: () => run,
|
|
60
|
+
runInstall: () => runInstall,
|
|
61
|
+
selectWriter: () => selectWriter,
|
|
62
|
+
synthesizeRootBarrels: () => synthesizeRootBarrels
|
|
63
|
+
});
|
|
64
|
+
module.exports = __toCommonJS(index_exports);
|
|
65
|
+
|
|
66
|
+
// src/errors.ts
|
|
67
|
+
var TakoError = class extends Error {
|
|
68
|
+
code;
|
|
69
|
+
constructor(code, message, options) {
|
|
70
|
+
super(message, options);
|
|
71
|
+
this.name = new.target.name;
|
|
72
|
+
this.code = code;
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
var NamespaceMismatchError = class extends TakoError {
|
|
76
|
+
namespace;
|
|
77
|
+
returned;
|
|
78
|
+
constructor(namespace, returned) {
|
|
79
|
+
super(
|
|
80
|
+
"namespace_mismatch",
|
|
81
|
+
`parser for namespace '${namespace}' returned a SourceIR with namespace '${returned}'`
|
|
82
|
+
);
|
|
83
|
+
this.namespace = namespace;
|
|
84
|
+
this.returned = returned;
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
var IrValidationError = class extends TakoError {
|
|
88
|
+
issues;
|
|
89
|
+
namespace;
|
|
90
|
+
constructor(issues, namespace) {
|
|
91
|
+
const detail = issues.map((i) => `${i.path === "" ? "<root>" : i.path}: ${i.message}`).join("; ");
|
|
92
|
+
const where = namespace ? ` in namespace '${namespace}'` : "";
|
|
93
|
+
super("ir_invalid", `invalid IR${where}: ${detail}`);
|
|
94
|
+
this.issues = issues;
|
|
95
|
+
this.namespace = namespace;
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
var DuplicateNamespaceError = class extends TakoError {
|
|
99
|
+
namespace;
|
|
100
|
+
constructor(namespace) {
|
|
101
|
+
super(
|
|
102
|
+
"duplicate_namespace",
|
|
103
|
+
`two sources claim the namespace '${namespace}'`
|
|
104
|
+
);
|
|
105
|
+
this.namespace = namespace;
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
var UnknownDependencyError = class extends TakoError {
|
|
109
|
+
generator;
|
|
110
|
+
missing;
|
|
111
|
+
constructor(generator, missing) {
|
|
112
|
+
super(
|
|
113
|
+
"unknown_dependency",
|
|
114
|
+
`generator '${generator}' declares a hard dependency on '${missing}', which is not in the config`
|
|
115
|
+
);
|
|
116
|
+
this.generator = generator;
|
|
117
|
+
this.missing = missing;
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
var InvalidDependencyError = class extends TakoError {
|
|
121
|
+
generator;
|
|
122
|
+
dependency;
|
|
123
|
+
constructor(generator, dependency) {
|
|
124
|
+
super(
|
|
125
|
+
"invalid_dependency",
|
|
126
|
+
`generator '${generator}' lists '${dependency}' in both dependsOn and optionalDependsOn`
|
|
127
|
+
);
|
|
128
|
+
this.generator = generator;
|
|
129
|
+
this.dependency = dependency;
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
var DependencyCycleError = class extends TakoError {
|
|
133
|
+
cycle;
|
|
134
|
+
constructor(cycle) {
|
|
135
|
+
super(
|
|
136
|
+
"dependency_cycle",
|
|
137
|
+
`the generator dependency graph has a cycle: ${cycle.join(" -> ")}`
|
|
138
|
+
);
|
|
139
|
+
this.cycle = cycle;
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
var OutputCollisionError = class extends TakoError {
|
|
143
|
+
path;
|
|
144
|
+
generators;
|
|
145
|
+
constructor(path5, generators, hint) {
|
|
146
|
+
super(
|
|
147
|
+
"output_collision",
|
|
148
|
+
`generators '${generators[0]}' and '${generators[1]}' both emit '${path5}'${hint ? `. ${hint}` : ""}`
|
|
149
|
+
);
|
|
150
|
+
this.path = path5;
|
|
151
|
+
this.generators = generators;
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
var InvalidOutputPathError = class extends TakoError {
|
|
155
|
+
path;
|
|
156
|
+
generator;
|
|
157
|
+
constructor(path5, generator) {
|
|
158
|
+
super(
|
|
159
|
+
"invalid_output_path",
|
|
160
|
+
`generator '${generator}' emits '${path5}', which escapes the output root`
|
|
161
|
+
);
|
|
162
|
+
this.path = path5;
|
|
163
|
+
this.generator = generator;
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
var UnsupportedOutputModeError = class extends TakoError {
|
|
167
|
+
mode;
|
|
168
|
+
constructor(mode) {
|
|
169
|
+
super(
|
|
170
|
+
"unsupported_output_mode",
|
|
171
|
+
`unsupported output mode '${mode}' (expected 'dir' or 'package')`
|
|
172
|
+
);
|
|
173
|
+
this.mode = mode;
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
var OutputPeerConflictError = class extends TakoError {
|
|
177
|
+
namespace;
|
|
178
|
+
package;
|
|
179
|
+
ranges;
|
|
180
|
+
generators;
|
|
181
|
+
constructor(namespace, pkg, ranges, generators) {
|
|
182
|
+
super(
|
|
183
|
+
"output_peer_conflict",
|
|
184
|
+
`namespace '${namespace}': generators [${generators.join(", ")}] declare peer '${pkg}' with conflicting ranges [${ranges.join(", ")}]`
|
|
185
|
+
);
|
|
186
|
+
this.namespace = namespace;
|
|
187
|
+
this.package = pkg;
|
|
188
|
+
this.ranges = ranges;
|
|
189
|
+
this.generators = generators;
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
var PackageBuildError = class extends TakoError {
|
|
193
|
+
namespace;
|
|
194
|
+
constructor(namespace, options) {
|
|
195
|
+
super(
|
|
196
|
+
"package_build_error",
|
|
197
|
+
`the build of the generated package for namespace '${namespace}' failed`,
|
|
198
|
+
options
|
|
199
|
+
);
|
|
200
|
+
this.namespace = namespace;
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
var MISSING_PACKAGE_WORKSPACE_FILE_GUIDANCE = {
|
|
204
|
+
"tsconfig.base.json": `Create '<workspaceRoot>/tsconfig.base.json':
|
|
205
|
+
{
|
|
206
|
+
"compilerOptions": {
|
|
207
|
+
"target": "ES2022",
|
|
208
|
+
"module": "ESNext",
|
|
209
|
+
"moduleResolution": "bundler",
|
|
210
|
+
"strict": true,
|
|
211
|
+
"skipLibCheck": true
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
Use these values as-is, no adjustment needed: in particular, keep
|
|
216
|
+
"moduleResolution": "bundler" \u2014 'node16'/'nodenext' would fail to compile
|
|
217
|
+
the extensionless \`export * from './zod';\` that tako's generated root
|
|
218
|
+
barrel always emits.`,
|
|
219
|
+
"tsup.config.base.{ts,js,mjs,cjs}": `Create '<workspaceRoot>/tsup.config.base.ts':
|
|
220
|
+
import type { Options } from 'tsup';
|
|
221
|
+
|
|
222
|
+
export const basePreset: Options = {
|
|
223
|
+
entry: ['src/index.ts'],
|
|
224
|
+
format: ['esm', 'cjs'],
|
|
225
|
+
dts: { compilerOptions: { composite: false, incremental: false } },
|
|
226
|
+
sourcemap: true,
|
|
227
|
+
clean: true,
|
|
228
|
+
target: 'node22',
|
|
229
|
+
outDir: 'dist',
|
|
230
|
+
};`,
|
|
231
|
+
"'typescript' (devDependency, needed for the .d.ts build)": `Run, from '<workspaceRoot>':
|
|
232
|
+
<your package manager> add -D typescript`
|
|
233
|
+
};
|
|
234
|
+
var MissingPackageWorkspaceFilesError = class extends TakoError {
|
|
235
|
+
workspaceRoot;
|
|
236
|
+
missing;
|
|
237
|
+
constructor(workspaceRoot, missing) {
|
|
238
|
+
const guidance = missing.map((item) => {
|
|
239
|
+
const template = MISSING_PACKAGE_WORKSPACE_FILE_GUIDANCE[item];
|
|
240
|
+
return template ? template.replaceAll("<workspaceRoot>", workspaceRoot) : item;
|
|
241
|
+
}).join("\n\n");
|
|
242
|
+
super(
|
|
243
|
+
"missing_package_workspace_files",
|
|
244
|
+
`mode 'package' requires 'tsconfig.base.json' and 'tsup.config.base.{ts,js,mjs,cjs}' in '${workspaceRoot}' (one directory above 'packagesDir'); missing: ${missing.join(", ")}
|
|
245
|
+
|
|
246
|
+
${guidance}`
|
|
247
|
+
);
|
|
248
|
+
this.workspaceRoot = workspaceRoot;
|
|
249
|
+
this.missing = missing;
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
var OutputNotGeneratedError = class extends TakoError {
|
|
253
|
+
path;
|
|
254
|
+
constructor(path5) {
|
|
255
|
+
super(
|
|
256
|
+
"output_not_generated",
|
|
257
|
+
`refusing to wipe '${path5}': it is non-empty and its package.json lacks the '"//": "Generated by tako\u2026"' marker`
|
|
258
|
+
);
|
|
259
|
+
this.path = path5;
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
var PackageInstallError = class extends TakoError {
|
|
263
|
+
pm;
|
|
264
|
+
constructor(pm, options) {
|
|
265
|
+
super(
|
|
266
|
+
"package_install_error",
|
|
267
|
+
`the '${pm} install' step for the generated packages exited non-zero`,
|
|
268
|
+
options
|
|
269
|
+
);
|
|
270
|
+
this.pm = pm;
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
var DriverError = class extends TakoError {
|
|
274
|
+
role;
|
|
275
|
+
driverName;
|
|
276
|
+
namespace;
|
|
277
|
+
constructor(role, driverName, options) {
|
|
278
|
+
const where = options?.namespace ? ` (namespace '${options.namespace}')` : "";
|
|
279
|
+
super(
|
|
280
|
+
"driver_error",
|
|
281
|
+
`${role} '${driverName}'${where} threw during ${role === "parser" ? "parse" : "generate"}`,
|
|
282
|
+
options
|
|
283
|
+
);
|
|
284
|
+
this.role = role;
|
|
285
|
+
this.driverName = driverName;
|
|
286
|
+
this.namespace = options?.namespace;
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
var HookError = class extends TakoError {
|
|
290
|
+
hook;
|
|
291
|
+
constructor(hook, options) {
|
|
292
|
+
super("hook_error", `the '${hook}' hook threw`, options);
|
|
293
|
+
this.hook = hook;
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
// src/logger.ts
|
|
298
|
+
var noopLogger = {
|
|
299
|
+
debug() {
|
|
300
|
+
},
|
|
301
|
+
info() {
|
|
302
|
+
},
|
|
303
|
+
warn() {
|
|
304
|
+
},
|
|
305
|
+
error() {
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
function mergeMeta(prefixMeta, meta) {
|
|
309
|
+
if (meta === void 0) {
|
|
310
|
+
return { ...prefixMeta };
|
|
311
|
+
}
|
|
312
|
+
if (meta !== null && typeof meta === "object" && !Array.isArray(meta)) {
|
|
313
|
+
return { ...prefixMeta, ...meta };
|
|
314
|
+
}
|
|
315
|
+
return { ...prefixMeta, value: meta };
|
|
316
|
+
}
|
|
317
|
+
function childLogger(base, prefixMeta) {
|
|
318
|
+
return {
|
|
319
|
+
debug: (msg, meta) => base.debug(msg, mergeMeta(prefixMeta, meta)),
|
|
320
|
+
info: (msg, meta) => base.info(msg, mergeMeta(prefixMeta, meta)),
|
|
321
|
+
warn: (msg, meta) => base.warn(msg, mergeMeta(prefixMeta, meta)),
|
|
322
|
+
error: (msg, meta) => base.error(msg, mergeMeta(prefixMeta, meta))
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// src/collect.ts
|
|
327
|
+
var import_node_path = __toESM(require("path"), 1);
|
|
328
|
+
function normalizePath(rawPath, generator) {
|
|
329
|
+
const posix = rawPath.replace(/\\/g, "/");
|
|
330
|
+
if (posix.startsWith("/")) {
|
|
331
|
+
throw new InvalidOutputPathError(rawPath, generator);
|
|
332
|
+
}
|
|
333
|
+
const normalized = import_node_path.default.posix.normalize(posix);
|
|
334
|
+
if (normalized === ".." || normalized.startsWith("../") || import_node_path.default.posix.isAbsolute(normalized)) {
|
|
335
|
+
throw new InvalidOutputPathError(rawPath, generator);
|
|
336
|
+
}
|
|
337
|
+
return normalized;
|
|
338
|
+
}
|
|
339
|
+
function mergeTrees(perGenerator, opts) {
|
|
340
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
341
|
+
for (const { generator, files } of perGenerator) {
|
|
342
|
+
for (const file of files) {
|
|
343
|
+
const normalized = normalizePath(file.path, generator);
|
|
344
|
+
const existing = byPath.get(normalized);
|
|
345
|
+
if (existing) {
|
|
346
|
+
throw new OutputCollisionError(
|
|
347
|
+
normalized,
|
|
348
|
+
[existing.generator, generator],
|
|
349
|
+
opts?.collisionHint
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
byPath.set(normalized, {
|
|
353
|
+
generator,
|
|
354
|
+
file: { path: normalized, content: file.content }
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return [...byPath.values()].map((entry) => entry.file).sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// src/filter.ts
|
|
362
|
+
function filterIR(ir, namespaces) {
|
|
363
|
+
const clone = structuredClone(ir);
|
|
364
|
+
if (namespaces === void 0) {
|
|
365
|
+
return clone;
|
|
366
|
+
}
|
|
367
|
+
const keep = new Set(namespaces);
|
|
368
|
+
const sources = {};
|
|
369
|
+
for (const [key, source] of Object.entries(clone.sources)) {
|
|
370
|
+
if (keep.has(key)) {
|
|
371
|
+
sources[key] = source;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
clone.sources = sources;
|
|
375
|
+
return clone;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// src/graph.ts
|
|
379
|
+
function generatorOrder(generators) {
|
|
380
|
+
const names = Object.keys(generators);
|
|
381
|
+
const index = new Map(names.map((name, i) => [name, i]));
|
|
382
|
+
const dependents = new Map(names.map((n) => [n, []]));
|
|
383
|
+
const indegree = new Map(names.map((n) => [n, 0]));
|
|
384
|
+
for (const name of names) {
|
|
385
|
+
const cfg = generators[name];
|
|
386
|
+
const hard = cfg?.generator.dependsOn ?? [];
|
|
387
|
+
const optional = cfg?.generator.optionalDependsOn ?? [];
|
|
388
|
+
const seen = /* @__PURE__ */ new Set();
|
|
389
|
+
const addEdge = (dep) => {
|
|
390
|
+
if (seen.has(dep)) {
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
seen.add(dep);
|
|
394
|
+
dependents.get(dep)?.push(name);
|
|
395
|
+
indegree.set(name, (indegree.get(name) ?? 0) + 1);
|
|
396
|
+
};
|
|
397
|
+
for (const dep of hard) {
|
|
398
|
+
if (optional.includes(dep)) {
|
|
399
|
+
throw new InvalidDependencyError(name, dep);
|
|
400
|
+
}
|
|
401
|
+
if (!index.has(dep)) {
|
|
402
|
+
throw new UnknownDependencyError(name, dep);
|
|
403
|
+
}
|
|
404
|
+
addEdge(dep);
|
|
405
|
+
}
|
|
406
|
+
for (const dep of optional) {
|
|
407
|
+
if (!index.has(dep)) {
|
|
408
|
+
continue;
|
|
409
|
+
}
|
|
410
|
+
addEdge(dep);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
const byConfigOrder = (a, b) => (index.get(a) ?? 0) - (index.get(b) ?? 0);
|
|
414
|
+
const ready = names.filter((n) => (indegree.get(n) ?? 0) === 0).sort(byConfigOrder);
|
|
415
|
+
const order = [];
|
|
416
|
+
while (ready.length > 0) {
|
|
417
|
+
const node = ready.shift();
|
|
418
|
+
order.push(node);
|
|
419
|
+
for (const dependent of dependents.get(node) ?? []) {
|
|
420
|
+
const next = (indegree.get(dependent) ?? 0) - 1;
|
|
421
|
+
indegree.set(dependent, next);
|
|
422
|
+
if (next === 0) {
|
|
423
|
+
ready.push(dependent);
|
|
424
|
+
ready.sort(byConfigOrder);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
if (order.length < names.length) {
|
|
429
|
+
const remaining = new Set(names.filter((n) => !order.includes(n)));
|
|
430
|
+
throw new DependencyCycleError(findCycle(remaining, dependents));
|
|
431
|
+
}
|
|
432
|
+
return order;
|
|
433
|
+
}
|
|
434
|
+
function findCycle(nodes, dependents) {
|
|
435
|
+
const stack = [];
|
|
436
|
+
const onStack = /* @__PURE__ */ new Set();
|
|
437
|
+
const visited = /* @__PURE__ */ new Set();
|
|
438
|
+
const walk = (node) => {
|
|
439
|
+
stack.push(node);
|
|
440
|
+
onStack.add(node);
|
|
441
|
+
for (const next of dependents.get(node) ?? []) {
|
|
442
|
+
if (!nodes.has(next)) {
|
|
443
|
+
continue;
|
|
444
|
+
}
|
|
445
|
+
if (onStack.has(next)) {
|
|
446
|
+
return [...stack.slice(stack.indexOf(next)), next];
|
|
447
|
+
}
|
|
448
|
+
if (!visited.has(next)) {
|
|
449
|
+
const found = walk(next);
|
|
450
|
+
if (found) {
|
|
451
|
+
return found;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
stack.pop();
|
|
456
|
+
onStack.delete(node);
|
|
457
|
+
visited.add(node);
|
|
458
|
+
return void 0;
|
|
459
|
+
};
|
|
460
|
+
for (const node of nodes) {
|
|
461
|
+
if (!visited.has(node)) {
|
|
462
|
+
const found = walk(node);
|
|
463
|
+
if (found) {
|
|
464
|
+
return found;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
return [...nodes];
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// src/merge.ts
|
|
472
|
+
var import_ir = require("@kurotako/ir");
|
|
473
|
+
function mergeSources(entries) {
|
|
474
|
+
const sources = {};
|
|
475
|
+
for (const { namespace, sourceIR } of entries) {
|
|
476
|
+
if (sourceIR.namespace !== namespace) {
|
|
477
|
+
throw new NamespaceMismatchError(namespace, sourceIR.namespace);
|
|
478
|
+
}
|
|
479
|
+
const validation = (0, import_ir.validateSourceIR)(sourceIR);
|
|
480
|
+
if (!validation.ok) {
|
|
481
|
+
throw new IrValidationError(validation.issues, namespace);
|
|
482
|
+
}
|
|
483
|
+
if (namespace in sources) {
|
|
484
|
+
throw new DuplicateNamespaceError(namespace);
|
|
485
|
+
}
|
|
486
|
+
sources[namespace] = validation.value;
|
|
487
|
+
}
|
|
488
|
+
const ir = { irVersion: import_ir.IR_VERSION, sources };
|
|
489
|
+
try {
|
|
490
|
+
(0, import_ir.assertIR)(ir);
|
|
491
|
+
} catch (error) {
|
|
492
|
+
if (error instanceof import_ir.IrValidationError) {
|
|
493
|
+
throw new IrValidationError(error.issues);
|
|
494
|
+
}
|
|
495
|
+
throw error;
|
|
496
|
+
}
|
|
497
|
+
return ir;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// src/writer/banner.ts
|
|
501
|
+
var BANNER = "// Generated by tako. Do not edit.\n";
|
|
502
|
+
var GITATTRIBUTES = "* linguist-generated=true\n";
|
|
503
|
+
var COMMENTABLE_BASENAMES = /* @__PURE__ */ new Set(["tsconfig.json"]);
|
|
504
|
+
function takesBanner(path5) {
|
|
505
|
+
if (path5.endsWith(".ts") || path5.endsWith(".tsx")) {
|
|
506
|
+
return true;
|
|
507
|
+
}
|
|
508
|
+
const basename = path5.slice(path5.lastIndexOf("/") + 1);
|
|
509
|
+
return COMMENTABLE_BASENAMES.has(basename);
|
|
510
|
+
}
|
|
511
|
+
function applyBanner(files) {
|
|
512
|
+
return files.map((file) => {
|
|
513
|
+
if (!takesBanner(file.path) || file.content.startsWith(BANNER)) {
|
|
514
|
+
return file;
|
|
515
|
+
}
|
|
516
|
+
return { path: file.path, content: BANNER + file.content };
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// src/writer/tree.ts
|
|
521
|
+
function contributingGenerators(files) {
|
|
522
|
+
const acc = /* @__PURE__ */ new Map();
|
|
523
|
+
for (const file of files) {
|
|
524
|
+
const parts = file.path.split("/");
|
|
525
|
+
if (parts.length < 3) {
|
|
526
|
+
continue;
|
|
527
|
+
}
|
|
528
|
+
const [namespace, generator] = parts;
|
|
529
|
+
if (!namespace || !generator) {
|
|
530
|
+
continue;
|
|
531
|
+
}
|
|
532
|
+
let set = acc.get(namespace);
|
|
533
|
+
if (!set) {
|
|
534
|
+
set = /* @__PURE__ */ new Set();
|
|
535
|
+
acc.set(namespace, set);
|
|
536
|
+
}
|
|
537
|
+
set.add(generator);
|
|
538
|
+
}
|
|
539
|
+
const out = /* @__PURE__ */ new Map();
|
|
540
|
+
for (const [namespace, set] of acc) {
|
|
541
|
+
out.set(namespace, [...set].sort());
|
|
542
|
+
}
|
|
543
|
+
return out;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// src/writer/barrel.ts
|
|
547
|
+
function synthesizeRootBarrels(files, artifactsByGenerator, logger) {
|
|
548
|
+
const contributors = contributingGenerators(files);
|
|
549
|
+
const barrels = [];
|
|
550
|
+
for (const namespace of [...contributors.keys()].sort()) {
|
|
551
|
+
const generators = contributors.get(namespace) ?? [];
|
|
552
|
+
if (artifactsByGenerator && logger) {
|
|
553
|
+
warnAmbiguousReExports(
|
|
554
|
+
namespace,
|
|
555
|
+
generators,
|
|
556
|
+
artifactsByGenerator,
|
|
557
|
+
logger
|
|
558
|
+
);
|
|
559
|
+
}
|
|
560
|
+
const content = generators.map((name) => `export * from './${name}';
|
|
561
|
+
`).join("");
|
|
562
|
+
barrels.push({ path: `${namespace}/index.ts`, content });
|
|
563
|
+
}
|
|
564
|
+
return barrels;
|
|
565
|
+
}
|
|
566
|
+
function warnAmbiguousReExports(namespace, generators, artifactsByGenerator, logger) {
|
|
567
|
+
const owners = /* @__PURE__ */ new Map();
|
|
568
|
+
for (const name of generators) {
|
|
569
|
+
const artifact = artifactsByGenerator[name];
|
|
570
|
+
if (!artifact) {
|
|
571
|
+
continue;
|
|
572
|
+
}
|
|
573
|
+
const identifiers = /* @__PURE__ */ new Set();
|
|
574
|
+
for (const [key, entity] of Object.entries(artifact.entities)) {
|
|
575
|
+
const dot = key.indexOf(".");
|
|
576
|
+
const entityNamespace = dot === -1 ? key : key.slice(0, dot);
|
|
577
|
+
if (entityNamespace !== namespace) {
|
|
578
|
+
continue;
|
|
579
|
+
}
|
|
580
|
+
for (const identifier of Object.values(entity.symbols)) {
|
|
581
|
+
identifiers.add(identifier);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
for (const identifier of identifiers) {
|
|
585
|
+
const list = owners.get(identifier) ?? [];
|
|
586
|
+
list.push(name);
|
|
587
|
+
owners.set(identifier, list);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
for (const [identifier, list] of owners) {
|
|
591
|
+
if (list.length > 1) {
|
|
592
|
+
const sorted = [...list].sort();
|
|
593
|
+
logger.warn(
|
|
594
|
+
`namespace '${namespace}': identifier '${identifier}' is re-exported by generators [${sorted.join(
|
|
595
|
+
", "
|
|
596
|
+
)}]; the ambiguous star re-export from '${namespace}/index.ts' will be dropped. Import it from a generator subpath instead.`,
|
|
597
|
+
{ namespace, identifier, generators: sorted }
|
|
598
|
+
);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// src/writer/directory.ts
|
|
604
|
+
var import_promises = __toESM(require("fs/promises"), 1);
|
|
605
|
+
var import_node_path2 = __toESM(require("path"), 1);
|
|
606
|
+
var GITATTRIBUTES2 = "* linguist-generated=true\n";
|
|
607
|
+
function sortByPath(entries) {
|
|
608
|
+
return [...entries].sort(
|
|
609
|
+
(a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0
|
|
610
|
+
);
|
|
611
|
+
}
|
|
612
|
+
var directoryWriter = {
|
|
613
|
+
async plan({ files, output }) {
|
|
614
|
+
if (!output.dir) {
|
|
615
|
+
throw new TakoError(
|
|
616
|
+
"invalid_output_config",
|
|
617
|
+
"mode 'dir' requires 'output.dir'"
|
|
618
|
+
);
|
|
619
|
+
}
|
|
620
|
+
const dir = import_node_path2.default.resolve(output.dir);
|
|
621
|
+
const planned = files.map((file) => ({
|
|
622
|
+
path: import_node_path2.default.join(dir, file.path),
|
|
623
|
+
content: file.content
|
|
624
|
+
}));
|
|
625
|
+
planned.push({
|
|
626
|
+
path: import_node_path2.default.join(dir, ".gitattributes"),
|
|
627
|
+
content: GITATTRIBUTES2
|
|
628
|
+
});
|
|
629
|
+
return sortByPath(planned);
|
|
630
|
+
},
|
|
631
|
+
async write(input) {
|
|
632
|
+
const planned = await this.plan(input);
|
|
633
|
+
const dir = import_node_path2.default.resolve(input.output.dir);
|
|
634
|
+
await import_promises.default.rm(dir, { recursive: true, force: true });
|
|
635
|
+
await import_promises.default.mkdir(dir, { recursive: true });
|
|
636
|
+
for (const file of planned) {
|
|
637
|
+
await import_promises.default.mkdir(import_node_path2.default.dirname(file.path), { recursive: true });
|
|
638
|
+
await import_promises.default.writeFile(file.path, file.content, "utf8");
|
|
639
|
+
}
|
|
640
|
+
return planned.map((file) => file.path);
|
|
641
|
+
}
|
|
642
|
+
};
|
|
643
|
+
|
|
644
|
+
// src/writer/package.ts
|
|
645
|
+
var import_node_fs2 = require("fs");
|
|
646
|
+
var import_promises2 = __toESM(require("fs/promises"), 1);
|
|
647
|
+
var import_node_module = require("module");
|
|
648
|
+
var import_node_path4 = __toESM(require("path"), 1);
|
|
649
|
+
|
|
650
|
+
// src/writer/peers.ts
|
|
651
|
+
function collectPeerDependencies(artifactsByGenerator, files) {
|
|
652
|
+
const contributors = contributingGenerators(files);
|
|
653
|
+
const result = {};
|
|
654
|
+
for (const namespace of [...contributors.keys()].sort()) {
|
|
655
|
+
const generators = contributors.get(namespace) ?? [];
|
|
656
|
+
const merged = /* @__PURE__ */ new Map();
|
|
657
|
+
for (const generator of generators) {
|
|
658
|
+
const peers = artifactsByGenerator[generator]?.peerDependencies;
|
|
659
|
+
if (!peers) {
|
|
660
|
+
continue;
|
|
661
|
+
}
|
|
662
|
+
for (const [pkg, range] of Object.entries(peers)) {
|
|
663
|
+
const existing = merged.get(pkg);
|
|
664
|
+
if (existing && existing.range !== range) {
|
|
665
|
+
throw new OutputPeerConflictError(
|
|
666
|
+
namespace,
|
|
667
|
+
pkg,
|
|
668
|
+
[existing.range, range],
|
|
669
|
+
[existing.generator, generator]
|
|
670
|
+
);
|
|
671
|
+
}
|
|
672
|
+
if (!existing) {
|
|
673
|
+
merged.set(pkg, { range, generator });
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
const sorted = {};
|
|
678
|
+
for (const pkg of [...merged.keys()].sort()) {
|
|
679
|
+
const entry = merged.get(pkg);
|
|
680
|
+
if (entry) {
|
|
681
|
+
sorted[pkg] = entry.range;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
result[namespace] = sorted;
|
|
685
|
+
}
|
|
686
|
+
return result;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
// src/writer/pm.ts
|
|
690
|
+
var import_node_child_process = require("child_process");
|
|
691
|
+
var import_node_fs = require("fs");
|
|
692
|
+
var import_node_path3 = __toESM(require("path"), 1);
|
|
693
|
+
var import_node_util = require("util");
|
|
694
|
+
var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
|
|
695
|
+
var PACKAGE_MANAGERS = [
|
|
696
|
+
"bun",
|
|
697
|
+
"pnpm",
|
|
698
|
+
"yarn",
|
|
699
|
+
"npm"
|
|
700
|
+
];
|
|
701
|
+
var LOCKFILES = [
|
|
702
|
+
["bun.lock", "bun"],
|
|
703
|
+
["bun.lockb", "bun"],
|
|
704
|
+
["pnpm-lock.yaml", "pnpm"],
|
|
705
|
+
["yarn.lock", "yarn"],
|
|
706
|
+
["package-lock.json", "npm"]
|
|
707
|
+
];
|
|
708
|
+
function isPackageManager(value) {
|
|
709
|
+
return PACKAGE_MANAGERS.includes(value);
|
|
710
|
+
}
|
|
711
|
+
function* ancestors(startDir) {
|
|
712
|
+
let dir = import_node_path3.default.resolve(startDir);
|
|
713
|
+
while (true) {
|
|
714
|
+
yield dir;
|
|
715
|
+
const parent = import_node_path3.default.dirname(dir);
|
|
716
|
+
if (parent === dir) {
|
|
717
|
+
return;
|
|
718
|
+
}
|
|
719
|
+
dir = parent;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
function resolvePackageManager(opts) {
|
|
723
|
+
if (opts.configured) {
|
|
724
|
+
return opts.configured;
|
|
725
|
+
}
|
|
726
|
+
for (const dir of ancestors(opts.startDir)) {
|
|
727
|
+
for (const [file, pm] of LOCKFILES) {
|
|
728
|
+
if ((0, import_node_fs.existsSync)(import_node_path3.default.join(dir, file))) {
|
|
729
|
+
return pm;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
if ((0, import_node_fs.existsSync)(import_node_path3.default.join(dir, ".git"))) {
|
|
733
|
+
break;
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
for (const dir of ancestors(opts.startDir)) {
|
|
737
|
+
const pkgPath = import_node_path3.default.join(dir, "package.json");
|
|
738
|
+
if ((0, import_node_fs.existsSync)(pkgPath)) {
|
|
739
|
+
try {
|
|
740
|
+
const pkg = JSON.parse((0, import_node_fs.readFileSync)(pkgPath, "utf8"));
|
|
741
|
+
if (typeof pkg.packageManager === "string") {
|
|
742
|
+
const name = pkg.packageManager.split("@")[0] ?? "";
|
|
743
|
+
if (isPackageManager(name)) {
|
|
744
|
+
return name;
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
} catch {
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
if ((0, import_node_fs.existsSync)(import_node_path3.default.join(dir, ".git"))) {
|
|
751
|
+
break;
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
return null;
|
|
755
|
+
}
|
|
756
|
+
async function runInstall(pm, cwd) {
|
|
757
|
+
try {
|
|
758
|
+
await execFileAsync(pm, ["install"], { cwd });
|
|
759
|
+
} catch (cause) {
|
|
760
|
+
throw new PackageInstallError(pm, { cause });
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
// src/writer/package.ts
|
|
765
|
+
var MARKER = "Generated by tako. Do not edit.";
|
|
766
|
+
function computePackageLayout({
|
|
767
|
+
files,
|
|
768
|
+
output,
|
|
769
|
+
artifacts
|
|
770
|
+
}) {
|
|
771
|
+
if (!output.packagesDir) {
|
|
772
|
+
throw new TakoError(
|
|
773
|
+
"invalid_output_config",
|
|
774
|
+
"mode 'package' requires 'output.packagesDir'"
|
|
775
|
+
);
|
|
776
|
+
}
|
|
777
|
+
if (!output.scope) {
|
|
778
|
+
throw new TakoError(
|
|
779
|
+
"invalid_output_config",
|
|
780
|
+
"mode 'package' requires 'output.scope'"
|
|
781
|
+
);
|
|
782
|
+
}
|
|
783
|
+
const packagesDir = import_node_path4.default.resolve(output.packagesDir);
|
|
784
|
+
const scope = output.scope;
|
|
785
|
+
const scopeSlug = scope.replace(/^@/, "");
|
|
786
|
+
const peersByNamespace = collectPeerDependencies(artifacts ?? {}, files);
|
|
787
|
+
const byNamespace = /* @__PURE__ */ new Map();
|
|
788
|
+
for (const file of files) {
|
|
789
|
+
const slash = file.path.indexOf("/");
|
|
790
|
+
if (slash === -1) {
|
|
791
|
+
continue;
|
|
792
|
+
}
|
|
793
|
+
const namespace = file.path.slice(0, slash);
|
|
794
|
+
const rest = file.path.slice(slash + 1);
|
|
795
|
+
const list = byNamespace.get(namespace) ?? [];
|
|
796
|
+
list.push({ path: rest, content: file.content });
|
|
797
|
+
byNamespace.set(namespace, list);
|
|
798
|
+
}
|
|
799
|
+
const namespaces = [...byNamespace.keys()].sort();
|
|
800
|
+
const entriesByNamespace = /* @__PURE__ */ new Map();
|
|
801
|
+
const planned = [];
|
|
802
|
+
for (const namespace of namespaces) {
|
|
803
|
+
const sources = [...byNamespace.get(namespace) ?? []].sort(
|
|
804
|
+
(a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0
|
|
805
|
+
);
|
|
806
|
+
const pkgDir = import_node_path4.default.join(packagesDir, `${scopeSlug}-${namespace}`);
|
|
807
|
+
const entries = [];
|
|
808
|
+
for (const src of sources) {
|
|
809
|
+
planned.push({
|
|
810
|
+
path: import_node_path4.default.join(pkgDir, "src", src.path),
|
|
811
|
+
content: src.content
|
|
812
|
+
});
|
|
813
|
+
entries.push(`src/${src.path}`);
|
|
814
|
+
}
|
|
815
|
+
planned.push({
|
|
816
|
+
path: import_node_path4.default.join(pkgDir, "package.json"),
|
|
817
|
+
content: buildPackageJson(
|
|
818
|
+
scope,
|
|
819
|
+
namespace,
|
|
820
|
+
peersByNamespace[namespace] ?? {}
|
|
821
|
+
)
|
|
822
|
+
});
|
|
823
|
+
planned.push({
|
|
824
|
+
path: import_node_path4.default.join(pkgDir, "tsconfig.json"),
|
|
825
|
+
content: buildTsconfig(namespace)
|
|
826
|
+
});
|
|
827
|
+
const sortedEntries = entries.sort();
|
|
828
|
+
entriesByNamespace.set(namespace, sortedEntries);
|
|
829
|
+
planned.push({
|
|
830
|
+
path: import_node_path4.default.join(pkgDir, "tsup.config.ts"),
|
|
831
|
+
content: buildTsupConfig(sortedEntries)
|
|
832
|
+
});
|
|
833
|
+
planned.push({
|
|
834
|
+
path: import_node_path4.default.join(pkgDir, ".gitattributes"),
|
|
835
|
+
content: GITATTRIBUTES
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
planned.push({
|
|
839
|
+
path: import_node_path4.default.join(packagesDir, ".gitattributes"),
|
|
840
|
+
content: `${scopeSlug}-*/** linguist-generated=true
|
|
841
|
+
`
|
|
842
|
+
});
|
|
843
|
+
planned.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
|
|
844
|
+
return { packagesDir, scopeSlug, namespaces, entriesByNamespace, planned };
|
|
845
|
+
}
|
|
846
|
+
var packageWriter = {
|
|
847
|
+
async plan(input) {
|
|
848
|
+
return computePackageLayout(input).planned;
|
|
849
|
+
},
|
|
850
|
+
async write({ files, output, artifacts, logger = noopLogger }) {
|
|
851
|
+
const { packagesDir, scopeSlug, namespaces, entriesByNamespace, planned } = computePackageLayout({ files, output, artifacts, logger });
|
|
852
|
+
assertWorkspaceBaseFiles(packagesDir);
|
|
853
|
+
await import_promises2.default.mkdir(packagesDir, { recursive: true });
|
|
854
|
+
for (const namespace of namespaces) {
|
|
855
|
+
await guardAndReset(import_node_path4.default.join(packagesDir, `${scopeSlug}-${namespace}`));
|
|
856
|
+
}
|
|
857
|
+
const written = [];
|
|
858
|
+
for (const file of planned) {
|
|
859
|
+
await import_promises2.default.mkdir(import_node_path4.default.dirname(file.path), { recursive: true });
|
|
860
|
+
await import_promises2.default.writeFile(file.path, file.content, "utf8");
|
|
861
|
+
written.push(file.path);
|
|
862
|
+
}
|
|
863
|
+
await buildPackages(packagesDir, scopeSlug, namespaces, entriesByNamespace);
|
|
864
|
+
const pm = resolvePackageManager({
|
|
865
|
+
configured: output.packageManager,
|
|
866
|
+
startDir: packagesDir
|
|
867
|
+
});
|
|
868
|
+
if (pm) {
|
|
869
|
+
await runInstall(pm, findWorkspaceRoot(packagesDir) ?? packagesDir);
|
|
870
|
+
} else {
|
|
871
|
+
logger.warn(
|
|
872
|
+
"could not resolve a package manager; run '<your package manager> install' in the workspace root to link the generated packages",
|
|
873
|
+
{ packagesDir }
|
|
874
|
+
);
|
|
875
|
+
}
|
|
876
|
+
return written.sort((a, b) => a < b ? -1 : a > b ? 1 : 0);
|
|
877
|
+
}
|
|
878
|
+
};
|
|
879
|
+
var TSUP_CONFIG_BASE_EXTENSIONS = [".ts", ".js", ".mjs", ".cjs"];
|
|
880
|
+
function assertWorkspaceBaseFiles(packagesDir) {
|
|
881
|
+
const workspaceRoot = import_node_path4.default.dirname(packagesDir);
|
|
882
|
+
const missing = [];
|
|
883
|
+
if (!(0, import_node_fs2.existsSync)(import_node_path4.default.join(workspaceRoot, "tsconfig.base.json"))) {
|
|
884
|
+
missing.push("tsconfig.base.json");
|
|
885
|
+
}
|
|
886
|
+
const hasTsupBase = TSUP_CONFIG_BASE_EXTENSIONS.some(
|
|
887
|
+
(ext) => (0, import_node_fs2.existsSync)(import_node_path4.default.join(workspaceRoot, `tsup.config.base${ext}`))
|
|
888
|
+
);
|
|
889
|
+
if (!hasTsupBase) {
|
|
890
|
+
missing.push("tsup.config.base.{ts,js,mjs,cjs}");
|
|
891
|
+
}
|
|
892
|
+
if (!isResolvableFrom("typescript", workspaceRoot)) {
|
|
893
|
+
missing.push("'typescript' (devDependency, needed for the .d.ts build)");
|
|
894
|
+
}
|
|
895
|
+
if (missing.length > 0) {
|
|
896
|
+
throw new MissingPackageWorkspaceFilesError(workspaceRoot, missing);
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
function isResolvableFrom(specifier, from) {
|
|
900
|
+
try {
|
|
901
|
+
(0, import_node_module.createRequire)(import_node_path4.default.join(from, "noop.js")).resolve(specifier);
|
|
902
|
+
return true;
|
|
903
|
+
} catch {
|
|
904
|
+
return false;
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
async function guardAndReset(pkgDir) {
|
|
908
|
+
let entries;
|
|
909
|
+
try {
|
|
910
|
+
entries = await import_promises2.default.readdir(pkgDir);
|
|
911
|
+
} catch {
|
|
912
|
+
entries = [];
|
|
913
|
+
}
|
|
914
|
+
if (entries.length > 0 && !hasGeneratedMarker(pkgDir)) {
|
|
915
|
+
throw new OutputNotGeneratedError(pkgDir);
|
|
916
|
+
}
|
|
917
|
+
await import_promises2.default.rm(pkgDir, { recursive: true, force: true });
|
|
918
|
+
await import_promises2.default.mkdir(pkgDir, { recursive: true });
|
|
919
|
+
}
|
|
920
|
+
function hasGeneratedMarker(pkgDir) {
|
|
921
|
+
try {
|
|
922
|
+
const pkg = JSON.parse(
|
|
923
|
+
(0, import_node_fs2.readFileSync)(import_node_path4.default.join(pkgDir, "package.json"), "utf8")
|
|
924
|
+
);
|
|
925
|
+
return typeof pkg["//"] === "string" && pkg["//"].startsWith("Generated by tako");
|
|
926
|
+
} catch {
|
|
927
|
+
return false;
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
async function buildPackages(packagesDir, scopeSlug, namespaces, entriesByNamespace) {
|
|
931
|
+
const { build } = await import("tsup");
|
|
932
|
+
const originalCwd = process.cwd();
|
|
933
|
+
try {
|
|
934
|
+
for (const namespace of namespaces) {
|
|
935
|
+
const pkgDir = import_node_path4.default.join(packagesDir, `${scopeSlug}-${namespace}`);
|
|
936
|
+
process.chdir(pkgDir);
|
|
937
|
+
try {
|
|
938
|
+
await build({
|
|
939
|
+
// Config auto-discovery (cosmiconfig) is disabled: the pkgDir's own
|
|
940
|
+
// tsup.config.ts exists for humans building the package standalone
|
|
941
|
+
// later, but resolving it here — a TS file re-exporting
|
|
942
|
+
// `../../tsup.config.base` — through tsup's config loader instead of
|
|
943
|
+
// this programmatic call caused spurious "Could not resolve" entry
|
|
944
|
+
// errors.
|
|
945
|
+
config: false,
|
|
946
|
+
// A relative `entry` array goes through tinyglobby, whose matches
|
|
947
|
+
// esbuild then resolves against the cwd esbuild's service captured
|
|
948
|
+
// at first use — *not* the cwd at this call, so it silently breaks
|
|
949
|
+
// after the first `process.chdir()` in this loop. An absolute-path
|
|
950
|
+
// entry *map* skips glob resolution entirely (tsup only runs
|
|
951
|
+
// `fs.existsSync` on it) and keeps `dist/` mirroring `src/`.
|
|
952
|
+
entry: Object.fromEntries(
|
|
953
|
+
(entriesByNamespace.get(namespace) ?? []).map((entry) => [
|
|
954
|
+
entry.replace(/^src\//, "").replace(/\.ts$/, ""),
|
|
955
|
+
import_node_path4.default.join(pkgDir, entry)
|
|
956
|
+
])
|
|
957
|
+
),
|
|
958
|
+
tsconfig: import_node_path4.default.join(pkgDir, "tsconfig.json"),
|
|
959
|
+
format: ["esm", "cjs"],
|
|
960
|
+
dts: { compilerOptions: { composite: false, incremental: false } },
|
|
961
|
+
outDir: import_node_path4.default.join(pkgDir, "dist"),
|
|
962
|
+
silent: true
|
|
963
|
+
});
|
|
964
|
+
} catch (cause) {
|
|
965
|
+
throw new PackageBuildError(namespace, { cause });
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
} finally {
|
|
969
|
+
process.chdir(originalCwd);
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
function findWorkspaceRoot(startDir) {
|
|
973
|
+
for (const dir of ancestors(startDir)) {
|
|
974
|
+
if ((0, import_node_fs2.existsSync)(import_node_path4.default.join(dir, "pnpm-workspace.yaml"))) {
|
|
975
|
+
return dir;
|
|
976
|
+
}
|
|
977
|
+
const pkgPath = import_node_path4.default.join(dir, "package.json");
|
|
978
|
+
if ((0, import_node_fs2.existsSync)(pkgPath)) {
|
|
979
|
+
try {
|
|
980
|
+
const pkg = JSON.parse((0, import_node_fs2.readFileSync)(pkgPath, "utf8"));
|
|
981
|
+
if (pkg.workspaces !== void 0) {
|
|
982
|
+
return dir;
|
|
983
|
+
}
|
|
984
|
+
} catch {
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
return null;
|
|
989
|
+
}
|
|
990
|
+
function buildPackageJson(scope, namespace, peerDependencies) {
|
|
991
|
+
const pkg = {
|
|
992
|
+
name: `${scope}/${namespace}`,
|
|
993
|
+
version: "0.0.0",
|
|
994
|
+
type: "module",
|
|
995
|
+
main: "./dist/index.cjs",
|
|
996
|
+
module: "./dist/index.js",
|
|
997
|
+
types: "./dist/index.d.ts",
|
|
998
|
+
exports: {
|
|
999
|
+
".": {
|
|
1000
|
+
types: "./dist/index.d.ts",
|
|
1001
|
+
import: "./dist/index.js",
|
|
1002
|
+
require: "./dist/index.cjs"
|
|
1003
|
+
},
|
|
1004
|
+
"./*": {
|
|
1005
|
+
types: "./dist/*.d.ts",
|
|
1006
|
+
import: "./dist/*.js",
|
|
1007
|
+
require: "./dist/*.cjs"
|
|
1008
|
+
}
|
|
1009
|
+
},
|
|
1010
|
+
files: ["dist", "src"],
|
|
1011
|
+
peerDependencies,
|
|
1012
|
+
sideEffects: false,
|
|
1013
|
+
scripts: { build: "tsup" },
|
|
1014
|
+
"//": MARKER
|
|
1015
|
+
};
|
|
1016
|
+
return `${JSON.stringify(pkg, null, 2)}
|
|
1017
|
+
`;
|
|
1018
|
+
}
|
|
1019
|
+
function buildTsconfig(namespace) {
|
|
1020
|
+
const body = JSON.stringify(
|
|
1021
|
+
{
|
|
1022
|
+
extends: "../../tsconfig.base.json",
|
|
1023
|
+
compilerOptions: {
|
|
1024
|
+
outDir: "dist",
|
|
1025
|
+
paths: { [`${namespace}/*`]: ["./src/*"] }
|
|
1026
|
+
},
|
|
1027
|
+
include: ["src"]
|
|
1028
|
+
},
|
|
1029
|
+
null,
|
|
1030
|
+
2
|
|
1031
|
+
);
|
|
1032
|
+
return `${BANNER}${body}
|
|
1033
|
+
`;
|
|
1034
|
+
}
|
|
1035
|
+
function buildTsupConfig(entries) {
|
|
1036
|
+
const list = entries.map((entry) => ` '${entry}',`).join("\n");
|
|
1037
|
+
return `${BANNER}import { basePreset } from '../../tsup.config.base';
|
|
1038
|
+
|
|
1039
|
+
export default {
|
|
1040
|
+
...basePreset,
|
|
1041
|
+
entry: [
|
|
1042
|
+
${list}
|
|
1043
|
+
],
|
|
1044
|
+
};
|
|
1045
|
+
`;
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
// src/writer/index.ts
|
|
1049
|
+
function selectWriter(output) {
|
|
1050
|
+
const mode = output.mode ?? "dir";
|
|
1051
|
+
if (mode === "dir") {
|
|
1052
|
+
return directoryWriter;
|
|
1053
|
+
}
|
|
1054
|
+
if (mode === "package") {
|
|
1055
|
+
return packageWriter;
|
|
1056
|
+
}
|
|
1057
|
+
throw new UnsupportedOutputModeError(mode);
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
// src/run.ts
|
|
1061
|
+
async function run(config, opts) {
|
|
1062
|
+
const logger = opts?.logger ?? noopLogger;
|
|
1063
|
+
const checkSignal = () => opts?.signal?.throwIfAborted();
|
|
1064
|
+
checkSignal();
|
|
1065
|
+
const entries = [];
|
|
1066
|
+
for (const namespace of Object.keys(config.sources).sort()) {
|
|
1067
|
+
checkSignal();
|
|
1068
|
+
const source = config.sources[namespace];
|
|
1069
|
+
if (!source) {
|
|
1070
|
+
continue;
|
|
1071
|
+
}
|
|
1072
|
+
const { parser } = source;
|
|
1073
|
+
const anchorDir = await parser.anchor?.(config.rootDir) ?? config.rootDir;
|
|
1074
|
+
const ctx = {
|
|
1075
|
+
namespace,
|
|
1076
|
+
cwd: config.rootDir,
|
|
1077
|
+
anchorDir,
|
|
1078
|
+
logger: childLogger(logger, { namespace })
|
|
1079
|
+
};
|
|
1080
|
+
try {
|
|
1081
|
+
const sourceIR = await parser.parse(ctx);
|
|
1082
|
+
entries.push({ namespace, sourceIR });
|
|
1083
|
+
} catch (error) {
|
|
1084
|
+
if (error instanceof DriverError) {
|
|
1085
|
+
throw error;
|
|
1086
|
+
}
|
|
1087
|
+
throw new DriverError("parser", parser.name, { cause: error, namespace });
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
checkSignal();
|
|
1091
|
+
const ir = mergeSources(entries);
|
|
1092
|
+
checkSignal();
|
|
1093
|
+
const order = generatorOrder(config.generators);
|
|
1094
|
+
const artifacts = {};
|
|
1095
|
+
const perGenerator = [];
|
|
1096
|
+
for (const name of order) {
|
|
1097
|
+
checkSignal();
|
|
1098
|
+
const cfg = config.generators[name];
|
|
1099
|
+
if (!cfg) {
|
|
1100
|
+
continue;
|
|
1101
|
+
}
|
|
1102
|
+
const { generator } = cfg;
|
|
1103
|
+
const view = filterIR(ir, cfg.namespaces);
|
|
1104
|
+
const declared = [
|
|
1105
|
+
...generator.dependsOn ?? [],
|
|
1106
|
+
...generator.optionalDependsOn ?? []
|
|
1107
|
+
];
|
|
1108
|
+
const dependencies = {};
|
|
1109
|
+
for (const dep of declared) {
|
|
1110
|
+
const artifact = artifacts[dep];
|
|
1111
|
+
if (artifact) {
|
|
1112
|
+
dependencies[dep] = artifact;
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
try {
|
|
1116
|
+
const out = await generator.generate({
|
|
1117
|
+
ir: view,
|
|
1118
|
+
dependencies,
|
|
1119
|
+
logger: childLogger(logger, { generator: name })
|
|
1120
|
+
});
|
|
1121
|
+
artifacts[name] = out.artifact;
|
|
1122
|
+
perGenerator.push({ generator: name, files: out.files });
|
|
1123
|
+
} catch (error) {
|
|
1124
|
+
if (error instanceof DriverError) {
|
|
1125
|
+
throw error;
|
|
1126
|
+
}
|
|
1127
|
+
throw new DriverError("generator", generator.name, { cause: error });
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
checkSignal();
|
|
1131
|
+
const collected = mergeTrees(perGenerator);
|
|
1132
|
+
checkSignal();
|
|
1133
|
+
const barrels = synthesizeRootBarrels(collected, artifacts, logger);
|
|
1134
|
+
const merged = mergeTrees(
|
|
1135
|
+
[
|
|
1136
|
+
...perGenerator,
|
|
1137
|
+
{ generator: "<synthesized root barrel>", files: barrels }
|
|
1138
|
+
],
|
|
1139
|
+
{
|
|
1140
|
+
collisionHint: "each generator must emit under its own '<namespace>/<generatorName>/' sub-tree; '<namespace>/index.ts' is synthesized by tako"
|
|
1141
|
+
}
|
|
1142
|
+
);
|
|
1143
|
+
const files = applyBanner(merged);
|
|
1144
|
+
const outputTree = (output) => {
|
|
1145
|
+
const names = new Set(output.generators ?? order);
|
|
1146
|
+
const filteredFiles = collected.filter(
|
|
1147
|
+
(file) => names.has(file.path.split("/")[1] ?? "")
|
|
1148
|
+
);
|
|
1149
|
+
const outputBarrels = synthesizeRootBarrels(
|
|
1150
|
+
filteredFiles,
|
|
1151
|
+
artifacts,
|
|
1152
|
+
logger
|
|
1153
|
+
);
|
|
1154
|
+
return applyBanner(
|
|
1155
|
+
mergeTrees([
|
|
1156
|
+
{ generator: "<filtered>", files: filteredFiles },
|
|
1157
|
+
{ generator: "<synthesized root barrel>", files: outputBarrels }
|
|
1158
|
+
])
|
|
1159
|
+
);
|
|
1160
|
+
};
|
|
1161
|
+
if (opts?.plan === true) {
|
|
1162
|
+
const planned = [];
|
|
1163
|
+
for (const output of config.outputs) {
|
|
1164
|
+
checkSignal();
|
|
1165
|
+
const writer = selectWriter(output);
|
|
1166
|
+
planned.push(
|
|
1167
|
+
...await writer.plan({
|
|
1168
|
+
files: outputTree(output),
|
|
1169
|
+
output,
|
|
1170
|
+
artifacts,
|
|
1171
|
+
logger
|
|
1172
|
+
})
|
|
1173
|
+
);
|
|
1174
|
+
}
|
|
1175
|
+
planned.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
|
|
1176
|
+
return { ir, order, files, artifacts, written: [], plan: planned };
|
|
1177
|
+
}
|
|
1178
|
+
const written = [];
|
|
1179
|
+
if (opts?.write !== false) {
|
|
1180
|
+
for (const output of config.outputs) {
|
|
1181
|
+
checkSignal();
|
|
1182
|
+
const writer = selectWriter(output);
|
|
1183
|
+
const writtenPaths = await writer.write({
|
|
1184
|
+
files: outputTree(output),
|
|
1185
|
+
output,
|
|
1186
|
+
artifacts,
|
|
1187
|
+
logger
|
|
1188
|
+
});
|
|
1189
|
+
written.push({ output, files: writtenPaths });
|
|
1190
|
+
checkSignal();
|
|
1191
|
+
const outputDir = (output.mode === "package" ? output.packagesDir : output.dir) ?? config.rootDir;
|
|
1192
|
+
try {
|
|
1193
|
+
await config.hooks?.afterEmit?.({
|
|
1194
|
+
outputDir,
|
|
1195
|
+
files: writtenPaths,
|
|
1196
|
+
logger
|
|
1197
|
+
});
|
|
1198
|
+
} catch (error) {
|
|
1199
|
+
throw new HookError("afterEmit", { cause: error });
|
|
1200
|
+
}
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
return { ir, order, files, artifacts, written };
|
|
1204
|
+
}
|
|
1205
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1206
|
+
0 && (module.exports = {
|
|
1207
|
+
BANNER,
|
|
1208
|
+
DependencyCycleError,
|
|
1209
|
+
DriverError,
|
|
1210
|
+
DuplicateNamespaceError,
|
|
1211
|
+
GITATTRIBUTES,
|
|
1212
|
+
HookError,
|
|
1213
|
+
InvalidDependencyError,
|
|
1214
|
+
InvalidOutputPathError,
|
|
1215
|
+
IrValidationError,
|
|
1216
|
+
MissingPackageWorkspaceFilesError,
|
|
1217
|
+
NamespaceMismatchError,
|
|
1218
|
+
OutputCollisionError,
|
|
1219
|
+
OutputNotGeneratedError,
|
|
1220
|
+
OutputPeerConflictError,
|
|
1221
|
+
PackageBuildError,
|
|
1222
|
+
PackageInstallError,
|
|
1223
|
+
TakoError,
|
|
1224
|
+
UnknownDependencyError,
|
|
1225
|
+
UnsupportedOutputModeError,
|
|
1226
|
+
applyBanner,
|
|
1227
|
+
childLogger,
|
|
1228
|
+
collectPeerDependencies,
|
|
1229
|
+
directoryWriter,
|
|
1230
|
+
noopLogger,
|
|
1231
|
+
packageWriter,
|
|
1232
|
+
resolvePackageManager,
|
|
1233
|
+
run,
|
|
1234
|
+
runInstall,
|
|
1235
|
+
selectWriter,
|
|
1236
|
+
synthesizeRootBarrels
|
|
1237
|
+
});
|
|
1238
|
+
//# sourceMappingURL=index.cjs.map
|