@emeryld/rrroutes-export 1.0.4 → 1.0.5
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 +132 -3
- package/bin/rrroutes-export-changelog.mjs +16 -0
- package/dist/defaultViewerTemplate.d.ts +1 -1
- package/dist/exportFinalizedLeaves.changelog.cli.d.ts +15 -0
- package/dist/exportFinalizedLeaves.changelog.d.ts +145 -0
- package/dist/index.cjs +882 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.mjs +877 -14
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -4
- package/tools/finalized-leaves-viewer.html +129 -1
package/dist/index.mjs
CHANGED
|
@@ -296,46 +296,46 @@ function normalizeType(schema) {
|
|
|
296
296
|
return "unknown";
|
|
297
297
|
}
|
|
298
298
|
}
|
|
299
|
-
function isNonEmptyPath(
|
|
300
|
-
return typeof
|
|
299
|
+
function isNonEmptyPath(path6) {
|
|
300
|
+
return typeof path6 === "string" && path6.length > 0;
|
|
301
301
|
}
|
|
302
|
-
function setNode(out,
|
|
303
|
-
if (!isNonEmptyPath(
|
|
304
|
-
out[
|
|
302
|
+
function setNode(out, path6, schema, inherited) {
|
|
303
|
+
if (!isNonEmptyPath(path6)) return;
|
|
304
|
+
out[path6] = {
|
|
305
305
|
type: normalizeType(schema),
|
|
306
306
|
nullable: inherited.nullable || Boolean(schema.nullable),
|
|
307
307
|
optional: inherited.optional || Boolean(schema.optional),
|
|
308
308
|
literal: schema.kind === "literal" ? schema.literal : void 0
|
|
309
309
|
};
|
|
310
310
|
}
|
|
311
|
-
function flattenInto(out, schema,
|
|
312
|
-
if (!schema || !isNonEmptyPath(
|
|
311
|
+
function flattenInto(out, schema, path6, inherited) {
|
|
312
|
+
if (!schema || !isNonEmptyPath(path6)) return;
|
|
313
313
|
const nextInherited = {
|
|
314
314
|
optional: inherited.optional || Boolean(schema.optional),
|
|
315
315
|
nullable: inherited.nullable || Boolean(schema.nullable)
|
|
316
316
|
};
|
|
317
317
|
if (schema.kind === "union" && Array.isArray(schema.union) && schema.union.length > 0) {
|
|
318
318
|
schema.union.forEach((option, index) => {
|
|
319
|
-
const optionPath = `${
|
|
319
|
+
const optionPath = `${path6}-${index + 1}`;
|
|
320
320
|
flattenInto(out, option, optionPath, inherited);
|
|
321
321
|
});
|
|
322
322
|
return;
|
|
323
323
|
}
|
|
324
|
-
setNode(out,
|
|
324
|
+
setNode(out, path6, schema, inherited);
|
|
325
325
|
if (schema.kind === "object" && schema.properties) {
|
|
326
326
|
for (const [key, child] of Object.entries(schema.properties)) {
|
|
327
|
-
const childPath = `${
|
|
327
|
+
const childPath = `${path6}.${key}`;
|
|
328
328
|
flattenInto(out, child, childPath, nextInherited);
|
|
329
329
|
}
|
|
330
330
|
return;
|
|
331
331
|
}
|
|
332
332
|
if (schema.kind === "array" && schema.element) {
|
|
333
|
-
flattenInto(out, schema.element, `${
|
|
333
|
+
flattenInto(out, schema.element, `${path6}[]`, nextInherited);
|
|
334
334
|
}
|
|
335
335
|
}
|
|
336
|
-
function flattenSerializableSchema(schema,
|
|
336
|
+
function flattenSerializableSchema(schema, path6) {
|
|
337
337
|
const out = {};
|
|
338
|
-
flattenInto(out, schema,
|
|
338
|
+
flattenInto(out, schema, path6, { optional: false, nullable: false });
|
|
339
339
|
return out;
|
|
340
340
|
}
|
|
341
341
|
function isSerializedLeaf(value) {
|
|
@@ -408,7 +408,27 @@ var DEFAULT_VIEWER_TEMPLATE = `<!doctype html>
|
|
|
408
408
|
const resultsEl = document.getElementById('results')
|
|
409
409
|
const payload = window.__FINALIZED_LEAVES_PAYLOAD
|
|
410
410
|
|
|
411
|
-
if (
|
|
411
|
+
if (payload && payload.kind === 'finalized-leaves-changelog') {
|
|
412
|
+
statusEl.textContent =
|
|
413
|
+
'Loaded changelog payload with ' + (payload.commits?.length || 0) + ' commits.'
|
|
414
|
+
|
|
415
|
+
const wrapSection = (title, value) => {
|
|
416
|
+
const card = document.createElement('details')
|
|
417
|
+
const summary = document.createElement('summary')
|
|
418
|
+
summary.textContent = title
|
|
419
|
+
const pre = document.createElement('pre')
|
|
420
|
+
pre.textContent = JSON.stringify(value, null, 2)
|
|
421
|
+
card.appendChild(summary)
|
|
422
|
+
card.appendChild(pre)
|
|
423
|
+
return card
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
resultsEl.appendChild(wrapSection('Commits', payload.commits || []))
|
|
427
|
+
resultsEl.appendChild(wrapSection('By Route', payload.byRoute || {}))
|
|
428
|
+
resultsEl.appendChild(
|
|
429
|
+
wrapSection('By Source Object', payload.bySourceObject || {}),
|
|
430
|
+
)
|
|
431
|
+
} else if (!payload || !Array.isArray(payload.leaves)) {
|
|
412
432
|
statusEl.textContent = 'No baked payload found in this HTML file.'
|
|
413
433
|
} else {
|
|
414
434
|
statusEl.textContent = 'Loaded baked payload with ' + payload.leaves.length + ' routes.'
|
|
@@ -1315,22 +1335,865 @@ async function runExportFinalizedLeavesCli(argv) {
|
|
|
1315
1335
|
outFile: path3.resolve(args.outFile)
|
|
1316
1336
|
};
|
|
1317
1337
|
}
|
|
1338
|
+
|
|
1339
|
+
// src/exportFinalizedLeaves.changelog.ts
|
|
1340
|
+
import fs2 from "fs/promises";
|
|
1341
|
+
import os from "os";
|
|
1342
|
+
import path4 from "path";
|
|
1343
|
+
import { execFile as execFileCallback, spawn as spawn2 } from "child_process";
|
|
1344
|
+
import { promisify } from "util";
|
|
1345
|
+
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
1346
|
+
var execFile = promisify(execFileCallback);
|
|
1347
|
+
var CHANGESET_CFG_FIELDS = [
|
|
1348
|
+
"summary",
|
|
1349
|
+
"description",
|
|
1350
|
+
"tags",
|
|
1351
|
+
"deprecated",
|
|
1352
|
+
"stability",
|
|
1353
|
+
"docsGroup",
|
|
1354
|
+
"docsHidden",
|
|
1355
|
+
"feed"
|
|
1356
|
+
];
|
|
1357
|
+
var SCHEMA_SECTIONS = ["params", "query", "body", "output"];
|
|
1358
|
+
var SOURCE_KEY_BY_SECTION = {
|
|
1359
|
+
params: "paramsSchema",
|
|
1360
|
+
query: "querySchema",
|
|
1361
|
+
body: "bodySchema",
|
|
1362
|
+
output: "outputSchema"
|
|
1363
|
+
};
|
|
1364
|
+
function normalizePath(p) {
|
|
1365
|
+
return p.replace(/\\/g, "/");
|
|
1366
|
+
}
|
|
1367
|
+
function stableStringify(value) {
|
|
1368
|
+
return JSON.stringify(value, (_key, v) => {
|
|
1369
|
+
if (v && typeof v === "object" && !Array.isArray(v)) {
|
|
1370
|
+
const sorted = Object.entries(v).sort(([a], [b]) => a.localeCompare(b)).reduce((acc, [k, inner]) => {
|
|
1371
|
+
acc[k] = inner;
|
|
1372
|
+
return acc;
|
|
1373
|
+
}, {});
|
|
1374
|
+
return sorted;
|
|
1375
|
+
}
|
|
1376
|
+
return v;
|
|
1377
|
+
});
|
|
1378
|
+
}
|
|
1379
|
+
function sanitizeCfgValue(value) {
|
|
1380
|
+
if (Array.isArray(value)) {
|
|
1381
|
+
return value.slice().sort((a, b) => String(a).localeCompare(String(b)));
|
|
1382
|
+
}
|
|
1383
|
+
return value;
|
|
1384
|
+
}
|
|
1385
|
+
function toCfgSubset(cfg) {
|
|
1386
|
+
const out = {};
|
|
1387
|
+
for (const field of CHANGESET_CFG_FIELDS) {
|
|
1388
|
+
const value = sanitizeCfgValue(cfg[field]);
|
|
1389
|
+
if (value !== void 0) {
|
|
1390
|
+
out[field] = value;
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
return out;
|
|
1394
|
+
}
|
|
1395
|
+
function entrySignature(entry) {
|
|
1396
|
+
return stableStringify({
|
|
1397
|
+
type: entry.type,
|
|
1398
|
+
nullable: Boolean(entry.nullable),
|
|
1399
|
+
optional: Boolean(entry.optional),
|
|
1400
|
+
literal: entry.literal
|
|
1401
|
+
});
|
|
1402
|
+
}
|
|
1403
|
+
function semverToTuple(tag) {
|
|
1404
|
+
const match = tag.match(/^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/);
|
|
1405
|
+
if (!match) return null;
|
|
1406
|
+
return {
|
|
1407
|
+
major: Number(match[1]),
|
|
1408
|
+
minor: Number(match[2]),
|
|
1409
|
+
patch: Number(match[3]),
|
|
1410
|
+
prerelease: match[4] ?? ""
|
|
1411
|
+
};
|
|
1412
|
+
}
|
|
1413
|
+
function compareSemverTags(a, b) {
|
|
1414
|
+
const av = semverToTuple(a);
|
|
1415
|
+
const bv = semverToTuple(b);
|
|
1416
|
+
if (!av && !bv) return 0;
|
|
1417
|
+
if (!av) return -1;
|
|
1418
|
+
if (!bv) return 1;
|
|
1419
|
+
if (av.major !== bv.major) return av.major - bv.major;
|
|
1420
|
+
if (av.minor !== bv.minor) return av.minor - bv.minor;
|
|
1421
|
+
if (av.patch !== bv.patch) return av.patch - bv.patch;
|
|
1422
|
+
if (!av.prerelease && bv.prerelease) return 1;
|
|
1423
|
+
if (av.prerelease && !bv.prerelease) return -1;
|
|
1424
|
+
return av.prerelease.localeCompare(bv.prerelease);
|
|
1425
|
+
}
|
|
1426
|
+
async function runGit(cwd, args) {
|
|
1427
|
+
const { stdout } = await execFile("git", args, { cwd });
|
|
1428
|
+
return stdout.trim();
|
|
1429
|
+
}
|
|
1430
|
+
async function resolveCommit(cwd, rev) {
|
|
1431
|
+
return runGit(cwd, ["rev-parse", "--verify", `${rev}^{commit}`]);
|
|
1432
|
+
}
|
|
1433
|
+
async function findLastReachableSemverTag(cwd, to) {
|
|
1434
|
+
const tagsRaw = await runGit(cwd, ["tag", "--merged", to]);
|
|
1435
|
+
const tags = tagsRaw.split("\n").map((item) => item.trim()).filter(Boolean).filter((tag) => semverToTuple(tag) !== null);
|
|
1436
|
+
if (tags.length === 0) {
|
|
1437
|
+
return void 0;
|
|
1438
|
+
}
|
|
1439
|
+
tags.sort(compareSemverTags);
|
|
1440
|
+
return tags[tags.length - 1];
|
|
1441
|
+
}
|
|
1442
|
+
async function findDefaultFromCommit(cwd, toCommit) {
|
|
1443
|
+
const tag = await findLastReachableSemverTag(cwd, toCommit);
|
|
1444
|
+
if (tag) {
|
|
1445
|
+
return resolveCommit(cwd, tag);
|
|
1446
|
+
}
|
|
1447
|
+
const root = await runGit(cwd, ["rev-list", "--max-parents=0", toCommit]);
|
|
1448
|
+
const rootCommit = root.split("\n").map((item) => item.trim()).filter(Boolean)[0];
|
|
1449
|
+
if (!rootCommit) {
|
|
1450
|
+
throw new Error("Failed to resolve a baseline commit for changelog generation.");
|
|
1451
|
+
}
|
|
1452
|
+
return rootCommit;
|
|
1453
|
+
}
|
|
1454
|
+
async function resolveCommitWindow(cwd, from, to) {
|
|
1455
|
+
const toCommit = await resolveCommit(cwd, to ?? "HEAD");
|
|
1456
|
+
const fromCommit = from ? await resolveCommit(cwd, from) : await findDefaultFromCommit(cwd, toCommit);
|
|
1457
|
+
return { from: fromCommit, to: toCommit };
|
|
1458
|
+
}
|
|
1459
|
+
async function resolveCommitPath(cwd, fromCommit, toCommit) {
|
|
1460
|
+
if (fromCommit === toCommit) {
|
|
1461
|
+
return [toCommit];
|
|
1462
|
+
}
|
|
1463
|
+
const pathRaw = await runGit(cwd, [
|
|
1464
|
+
"rev-list",
|
|
1465
|
+
"--reverse",
|
|
1466
|
+
"--ancestry-path",
|
|
1467
|
+
`${fromCommit}..${toCommit}`
|
|
1468
|
+
]);
|
|
1469
|
+
const commits = pathRaw.split("\n").map((item) => item.trim()).filter(Boolean);
|
|
1470
|
+
return [fromCommit, ...commits];
|
|
1471
|
+
}
|
|
1472
|
+
function parentDirectories(relativePath) {
|
|
1473
|
+
const clean = normalizePath(relativePath).replace(/^\/+/, "");
|
|
1474
|
+
const dir = path4.posix.dirname(clean);
|
|
1475
|
+
if (!dir || dir === ".") return [];
|
|
1476
|
+
const parts = dir.split("/");
|
|
1477
|
+
const out = [];
|
|
1478
|
+
for (let i = parts.length; i > 0; i -= 1) {
|
|
1479
|
+
out.push(parts.slice(0, i).join("/"));
|
|
1480
|
+
}
|
|
1481
|
+
return out;
|
|
1482
|
+
}
|
|
1483
|
+
function buildRelevantConfigFiles(moduleRel, tsconfigRel) {
|
|
1484
|
+
const dirs = new Set(parentDirectories(moduleRel));
|
|
1485
|
+
if (tsconfigRel) {
|
|
1486
|
+
parentDirectories(tsconfigRel).forEach((item) => dirs.add(item));
|
|
1487
|
+
dirs.add(path4.posix.dirname(normalizePath(tsconfigRel)));
|
|
1488
|
+
}
|
|
1489
|
+
const files = /* @__PURE__ */ new Set(["pnpm-workspace.yaml"]);
|
|
1490
|
+
for (const dir of dirs) {
|
|
1491
|
+
if (!dir || dir === ".") continue;
|
|
1492
|
+
files.add(`${dir}/package.json`);
|
|
1493
|
+
files.add(`${dir}/tsconfig.json`);
|
|
1494
|
+
}
|
|
1495
|
+
return files;
|
|
1496
|
+
}
|
|
1497
|
+
async function commitTouchedRelevantPaths(cwd, commit, moduleRel, tsconfigRel) {
|
|
1498
|
+
const changed = await runGit(cwd, [
|
|
1499
|
+
"show",
|
|
1500
|
+
"--pretty=format:",
|
|
1501
|
+
"--name-only",
|
|
1502
|
+
"--diff-filter=ACMRTUXB",
|
|
1503
|
+
commit
|
|
1504
|
+
]);
|
|
1505
|
+
const changedFiles = changed.split("\n").map((item) => normalizePath(item.trim()).replace(/^\/+/, "")).filter(Boolean);
|
|
1506
|
+
const moduleFile = normalizePath(moduleRel).replace(/^\/+/, "");
|
|
1507
|
+
const moduleDir = normalizePath(path4.posix.dirname(moduleFile)).replace(/^\/+/, "");
|
|
1508
|
+
const tsconfigFile = tsconfigRel ? normalizePath(tsconfigRel).replace(/^\/+/, "") : void 0;
|
|
1509
|
+
const configFiles = buildRelevantConfigFiles(moduleFile, tsconfigFile);
|
|
1510
|
+
return changedFiles.some((file) => {
|
|
1511
|
+
if (file === moduleFile) return true;
|
|
1512
|
+
if (moduleDir && moduleDir !== "." && (file === moduleDir || file.startsWith(`${moduleDir}/`))) {
|
|
1513
|
+
return true;
|
|
1514
|
+
}
|
|
1515
|
+
if (tsconfigFile && file === tsconfigFile) return true;
|
|
1516
|
+
return configFiles.has(file);
|
|
1517
|
+
});
|
|
1518
|
+
}
|
|
1519
|
+
async function filterCommits(cwd, commits, moduleRel, tsconfigRel) {
|
|
1520
|
+
if (commits.length <= 1) {
|
|
1521
|
+
return commits;
|
|
1522
|
+
}
|
|
1523
|
+
const keep = [commits[0]];
|
|
1524
|
+
for (let i = 1; i < commits.length; i += 1) {
|
|
1525
|
+
const commit = commits[i];
|
|
1526
|
+
if (await commitTouchedRelevantPaths(cwd, commit, moduleRel, tsconfigRel)) {
|
|
1527
|
+
keep.push(commit);
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
const tip = commits[commits.length - 1];
|
|
1531
|
+
if (!keep.includes(tip)) {
|
|
1532
|
+
keep.push(tip);
|
|
1533
|
+
}
|
|
1534
|
+
return keep;
|
|
1535
|
+
}
|
|
1536
|
+
async function getCommitMetadata(cwd, commit) {
|
|
1537
|
+
const out = await runGit(cwd, ["show", "-s", "--format=%H%x1f%aI%x1f%s", commit]);
|
|
1538
|
+
const [sha, authorDate, subject] = out.split("");
|
|
1539
|
+
if (!sha || !authorDate) {
|
|
1540
|
+
throw new Error(`Unable to read commit metadata for ${commit}`);
|
|
1541
|
+
}
|
|
1542
|
+
return {
|
|
1543
|
+
sha,
|
|
1544
|
+
authorDate,
|
|
1545
|
+
subject: subject ?? ""
|
|
1546
|
+
};
|
|
1547
|
+
}
|
|
1548
|
+
function buildSourceIdentity(section, value) {
|
|
1549
|
+
const sourceName = typeof value.sourceName === "string" ? value.sourceName : void 0;
|
|
1550
|
+
const tag = typeof value.tag === "string" ? value.tag : void 0;
|
|
1551
|
+
const file = typeof value.file === "string" ? value.file : void 0;
|
|
1552
|
+
const line = typeof value.line === "number" ? value.line : void 0;
|
|
1553
|
+
const column = typeof value.column === "number" ? value.column : void 0;
|
|
1554
|
+
const fallbackName = sourceName ?? tag ?? section;
|
|
1555
|
+
const suffix = file ? `${file}:${line ?? 1}:${column ?? 1}` : `${section}:unknown`;
|
|
1556
|
+
return {
|
|
1557
|
+
id: sourceName ?? `${fallbackName}@${suffix}`,
|
|
1558
|
+
displayName: sourceName ?? `${fallbackName} (${suffix})`,
|
|
1559
|
+
sourceName,
|
|
1560
|
+
tag,
|
|
1561
|
+
file,
|
|
1562
|
+
line,
|
|
1563
|
+
column
|
|
1564
|
+
};
|
|
1565
|
+
}
|
|
1566
|
+
function asRecord(value) {
|
|
1567
|
+
return value && typeof value === "object" ? value : {};
|
|
1568
|
+
}
|
|
1569
|
+
function toRouteSnapshots(payload) {
|
|
1570
|
+
const routes = {};
|
|
1571
|
+
for (const leaf of payload.leaves) {
|
|
1572
|
+
const flatSchema = payload.schemaFlatByLeaf[leaf.key] ?? {};
|
|
1573
|
+
const cfg = toCfgSubset(asRecord(leaf.cfg));
|
|
1574
|
+
const sourceLeaf = payload.sourceByLeaf?.[leaf.key];
|
|
1575
|
+
const sourceSchemas = sourceLeaf?.schemas;
|
|
1576
|
+
const sourceBySection = {};
|
|
1577
|
+
for (const section of SCHEMA_SECTIONS) {
|
|
1578
|
+
const schemaKey = SOURCE_KEY_BY_SECTION[section];
|
|
1579
|
+
const sectionSource = sourceSchemas?.[schemaKey];
|
|
1580
|
+
if (!sectionSource || typeof sectionSource !== "object") continue;
|
|
1581
|
+
sourceBySection[section] = buildSourceIdentity(
|
|
1582
|
+
section,
|
|
1583
|
+
sectionSource
|
|
1584
|
+
);
|
|
1585
|
+
}
|
|
1586
|
+
routes[leaf.key] = {
|
|
1587
|
+
key: leaf.key,
|
|
1588
|
+
method: String(leaf.method).toUpperCase(),
|
|
1589
|
+
path: leaf.path,
|
|
1590
|
+
cfg,
|
|
1591
|
+
flatSchema,
|
|
1592
|
+
sourceBySection
|
|
1593
|
+
};
|
|
1594
|
+
}
|
|
1595
|
+
return routes;
|
|
1596
|
+
}
|
|
1597
|
+
function sectionPathEntries(flatSchema, section) {
|
|
1598
|
+
return Object.entries(flatSchema).filter(
|
|
1599
|
+
([schemaPath]) => schemaPath === section || schemaPath.startsWith(`${section}.`) || schemaPath.startsWith(`${section}[]`)
|
|
1600
|
+
);
|
|
1601
|
+
}
|
|
1602
|
+
function toSourceObjectSnapshots(routesByKey) {
|
|
1603
|
+
const working = /* @__PURE__ */ new Map();
|
|
1604
|
+
for (const route of Object.values(routesByKey)) {
|
|
1605
|
+
for (const section of SCHEMA_SECTIONS) {
|
|
1606
|
+
const sourceIdentity = route.sourceBySection[section];
|
|
1607
|
+
if (!sourceIdentity) continue;
|
|
1608
|
+
const entries = sectionPathEntries(route.flatSchema, section);
|
|
1609
|
+
if (entries.length === 0) continue;
|
|
1610
|
+
const existing = working.get(sourceIdentity.id) ?? {
|
|
1611
|
+
id: sourceIdentity.id,
|
|
1612
|
+
displayName: sourceIdentity.displayName,
|
|
1613
|
+
routeKeys: /* @__PURE__ */ new Set(),
|
|
1614
|
+
sections: {
|
|
1615
|
+
params: /* @__PURE__ */ new Map(),
|
|
1616
|
+
query: /* @__PURE__ */ new Map(),
|
|
1617
|
+
body: /* @__PURE__ */ new Map(),
|
|
1618
|
+
output: /* @__PURE__ */ new Map()
|
|
1619
|
+
}
|
|
1620
|
+
};
|
|
1621
|
+
existing.routeKeys.add(route.key);
|
|
1622
|
+
for (const [schemaPath, schemaEntry] of entries) {
|
|
1623
|
+
const sectionMap = existing.sections[section];
|
|
1624
|
+
const signatures = sectionMap.get(schemaPath) ?? /* @__PURE__ */ new Set();
|
|
1625
|
+
signatures.add(entrySignature(schemaEntry));
|
|
1626
|
+
sectionMap.set(schemaPath, signatures);
|
|
1627
|
+
}
|
|
1628
|
+
working.set(sourceIdentity.id, existing);
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
const out = {};
|
|
1632
|
+
for (const [id, item] of working.entries()) {
|
|
1633
|
+
const sections = {
|
|
1634
|
+
params: {},
|
|
1635
|
+
query: {},
|
|
1636
|
+
body: {},
|
|
1637
|
+
output: {}
|
|
1638
|
+
};
|
|
1639
|
+
for (const section of SCHEMA_SECTIONS) {
|
|
1640
|
+
for (const [schemaPath, signatures] of item.sections[section].entries()) {
|
|
1641
|
+
sections[section][schemaPath] = Array.from(signatures).sort();
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
out[id] = {
|
|
1645
|
+
id,
|
|
1646
|
+
displayName: item.displayName,
|
|
1647
|
+
routeKeys: Array.from(item.routeKeys).sort(),
|
|
1648
|
+
sections
|
|
1649
|
+
};
|
|
1650
|
+
}
|
|
1651
|
+
return out;
|
|
1652
|
+
}
|
|
1653
|
+
function diffRouteSchemas(before, after) {
|
|
1654
|
+
const paths = /* @__PURE__ */ new Set([...Object.keys(before), ...Object.keys(after)]);
|
|
1655
|
+
const out = [];
|
|
1656
|
+
for (const schemaPath of Array.from(paths).sort()) {
|
|
1657
|
+
const prev = before[schemaPath];
|
|
1658
|
+
const next = after[schemaPath];
|
|
1659
|
+
if (!prev && next) {
|
|
1660
|
+
out.push({ path: schemaPath, op: "added", after: next });
|
|
1661
|
+
continue;
|
|
1662
|
+
}
|
|
1663
|
+
if (prev && !next) {
|
|
1664
|
+
out.push({ path: schemaPath, op: "removed", before: prev });
|
|
1665
|
+
continue;
|
|
1666
|
+
}
|
|
1667
|
+
if (!prev || !next) continue;
|
|
1668
|
+
if (entrySignature(prev) !== entrySignature(next)) {
|
|
1669
|
+
out.push({ path: schemaPath, op: "changed", before: prev, after: next });
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
return out;
|
|
1673
|
+
}
|
|
1674
|
+
function diffRouteCfg(before, after) {
|
|
1675
|
+
const out = [];
|
|
1676
|
+
for (const field of CHANGESET_CFG_FIELDS) {
|
|
1677
|
+
const prev = before[field];
|
|
1678
|
+
const next = after[field];
|
|
1679
|
+
if (stableStringify(prev) !== stableStringify(next)) {
|
|
1680
|
+
out.push({
|
|
1681
|
+
field,
|
|
1682
|
+
before: prev,
|
|
1683
|
+
after: next
|
|
1684
|
+
});
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
return out;
|
|
1688
|
+
}
|
|
1689
|
+
function flattenSourceSections(snapshot) {
|
|
1690
|
+
const map = /* @__PURE__ */ new Map();
|
|
1691
|
+
for (const section of SCHEMA_SECTIONS) {
|
|
1692
|
+
const entries = snapshot.sections[section];
|
|
1693
|
+
for (const [schemaPath, signatures] of Object.entries(entries)) {
|
|
1694
|
+
map.set(`${section}:${schemaPath}`, signatures);
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
return map;
|
|
1698
|
+
}
|
|
1699
|
+
function diffSourceSchemas(before, after) {
|
|
1700
|
+
const prev = flattenSourceSections(before);
|
|
1701
|
+
const next = flattenSourceSections(after);
|
|
1702
|
+
const keys = /* @__PURE__ */ new Set([...prev.keys(), ...next.keys()]);
|
|
1703
|
+
const out = [];
|
|
1704
|
+
for (const key of Array.from(keys).sort()) {
|
|
1705
|
+
const prevSignatures = prev.get(key);
|
|
1706
|
+
const nextSignatures = next.get(key);
|
|
1707
|
+
const [section, ...rest] = key.split(":");
|
|
1708
|
+
const schemaPath = rest.join(":");
|
|
1709
|
+
if (!prevSignatures && nextSignatures) {
|
|
1710
|
+
out.push({
|
|
1711
|
+
section,
|
|
1712
|
+
path: schemaPath,
|
|
1713
|
+
op: "added",
|
|
1714
|
+
after: nextSignatures
|
|
1715
|
+
});
|
|
1716
|
+
continue;
|
|
1717
|
+
}
|
|
1718
|
+
if (prevSignatures && !nextSignatures) {
|
|
1719
|
+
out.push({
|
|
1720
|
+
section,
|
|
1721
|
+
path: schemaPath,
|
|
1722
|
+
op: "removed",
|
|
1723
|
+
before: prevSignatures
|
|
1724
|
+
});
|
|
1725
|
+
continue;
|
|
1726
|
+
}
|
|
1727
|
+
if (!prevSignatures || !nextSignatures) continue;
|
|
1728
|
+
if (stableStringify(prevSignatures) !== stableStringify(nextSignatures)) {
|
|
1729
|
+
out.push({
|
|
1730
|
+
section,
|
|
1731
|
+
path: schemaPath,
|
|
1732
|
+
op: "changed",
|
|
1733
|
+
before: prevSignatures,
|
|
1734
|
+
after: nextSignatures
|
|
1735
|
+
});
|
|
1736
|
+
}
|
|
1737
|
+
}
|
|
1738
|
+
return out;
|
|
1739
|
+
}
|
|
1740
|
+
function routeRecordByKey(events) {
|
|
1741
|
+
var _a;
|
|
1742
|
+
const out = {};
|
|
1743
|
+
for (const event of events) {
|
|
1744
|
+
out[_a = event.routeKey] ?? (out[_a] = []);
|
|
1745
|
+
out[event.routeKey].push(event);
|
|
1746
|
+
}
|
|
1747
|
+
return out;
|
|
1748
|
+
}
|
|
1749
|
+
function sourceRecordById(events) {
|
|
1750
|
+
var _a;
|
|
1751
|
+
const out = {};
|
|
1752
|
+
for (const event of events) {
|
|
1753
|
+
out[_a = event.sourceObjectId] ?? (out[_a] = []);
|
|
1754
|
+
out[event.sourceObjectId].push(event);
|
|
1755
|
+
}
|
|
1756
|
+
return out;
|
|
1757
|
+
}
|
|
1758
|
+
function buildRollups(routeEvents, sourceEvents, commits) {
|
|
1759
|
+
var _a, _b;
|
|
1760
|
+
const route = {
|
|
1761
|
+
route_added: 0,
|
|
1762
|
+
route_removed: 0,
|
|
1763
|
+
schema_changed: 0,
|
|
1764
|
+
cfg_changed: 0
|
|
1765
|
+
};
|
|
1766
|
+
const sourceObject = {
|
|
1767
|
+
source_object_added: 0,
|
|
1768
|
+
source_object_removed: 0,
|
|
1769
|
+
source_schema_changed: 0
|
|
1770
|
+
};
|
|
1771
|
+
for (const event of routeEvents) {
|
|
1772
|
+
route[event.type] += 1;
|
|
1773
|
+
}
|
|
1774
|
+
for (const event of sourceEvents) {
|
|
1775
|
+
sourceObject[event.type] += 1;
|
|
1776
|
+
}
|
|
1777
|
+
const byCommit = {};
|
|
1778
|
+
for (const commit of commits) {
|
|
1779
|
+
byCommit[commit.sha] = { routeEvents: 0, sourceEvents: 0 };
|
|
1780
|
+
}
|
|
1781
|
+
for (const event of routeEvents) {
|
|
1782
|
+
byCommit[_a = event.commitSha] ?? (byCommit[_a] = { routeEvents: 0, sourceEvents: 0 });
|
|
1783
|
+
byCommit[event.commitSha].routeEvents += 1;
|
|
1784
|
+
}
|
|
1785
|
+
for (const event of sourceEvents) {
|
|
1786
|
+
byCommit[_b = event.commitSha] ?? (byCommit[_b] = { routeEvents: 0, sourceEvents: 0 });
|
|
1787
|
+
byCommit[event.commitSha].sourceEvents += 1;
|
|
1788
|
+
}
|
|
1789
|
+
return {
|
|
1790
|
+
route,
|
|
1791
|
+
sourceObject,
|
|
1792
|
+
byCommit
|
|
1793
|
+
};
|
|
1794
|
+
}
|
|
1795
|
+
async function loadChangelogInput(modulePath, exportName) {
|
|
1796
|
+
const mod = await import(`${pathToFileURL2(modulePath).href}?v=${Date.now()}`);
|
|
1797
|
+
const value = mod[exportName] ?? (mod.default && mod.default[exportName]);
|
|
1798
|
+
if (!value) {
|
|
1799
|
+
throw new Error(`Export "${exportName}" not found in module: ${modulePath}`);
|
|
1800
|
+
}
|
|
1801
|
+
return value;
|
|
1802
|
+
}
|
|
1803
|
+
async function createSnapshot(repoRoot, moduleRel, exportName, commit, tempRoot, tsconfigRel, exportOptions) {
|
|
1804
|
+
const worktreeDir = path4.join(tempRoot, `wt-${commit.sha.slice(0, 12)}`);
|
|
1805
|
+
await runGit(repoRoot, ["worktree", "add", "--detach", worktreeDir, commit.sha]);
|
|
1806
|
+
try {
|
|
1807
|
+
const modulePath = path4.resolve(worktreeDir, moduleRel);
|
|
1808
|
+
const tsconfigPath = tsconfigRel ? path4.resolve(worktreeDir, tsconfigRel) : void 0;
|
|
1809
|
+
const input = await loadChangelogInput(modulePath, exportName);
|
|
1810
|
+
const payload = await exportFinalizedLeaves(input, {
|
|
1811
|
+
includeSource: true,
|
|
1812
|
+
sourceModulePath: modulePath,
|
|
1813
|
+
sourceExportName: exportName,
|
|
1814
|
+
tsconfigPath,
|
|
1815
|
+
...exportOptions
|
|
1816
|
+
});
|
|
1817
|
+
const routesByKey = toRouteSnapshots(payload);
|
|
1818
|
+
const sourceObjectsById = toSourceObjectSnapshots(routesByKey);
|
|
1819
|
+
return {
|
|
1820
|
+
commit,
|
|
1821
|
+
routesByKey,
|
|
1822
|
+
sourceObjectsById
|
|
1823
|
+
};
|
|
1824
|
+
} finally {
|
|
1825
|
+
await runGit(repoRoot, ["worktree", "remove", "--force", worktreeDir]).catch(() => void 0);
|
|
1826
|
+
await fs2.rm(worktreeDir, { recursive: true, force: true }).catch(() => void 0);
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1829
|
+
function diffSnapshots(previous, current) {
|
|
1830
|
+
const routeEvents = [];
|
|
1831
|
+
const sourceEvents = [];
|
|
1832
|
+
const routeKeys = /* @__PURE__ */ new Set([
|
|
1833
|
+
...Object.keys(previous.routesByKey),
|
|
1834
|
+
...Object.keys(current.routesByKey)
|
|
1835
|
+
]);
|
|
1836
|
+
for (const routeKey of Array.from(routeKeys).sort()) {
|
|
1837
|
+
const prevRoute = previous.routesByKey[routeKey];
|
|
1838
|
+
const nextRoute = current.routesByKey[routeKey];
|
|
1839
|
+
if (!prevRoute && nextRoute) {
|
|
1840
|
+
routeEvents.push({
|
|
1841
|
+
type: "route_added",
|
|
1842
|
+
commitSha: current.commit.sha,
|
|
1843
|
+
routeKey,
|
|
1844
|
+
method: nextRoute.method,
|
|
1845
|
+
path: nextRoute.path
|
|
1846
|
+
});
|
|
1847
|
+
continue;
|
|
1848
|
+
}
|
|
1849
|
+
if (prevRoute && !nextRoute) {
|
|
1850
|
+
routeEvents.push({
|
|
1851
|
+
type: "route_removed",
|
|
1852
|
+
commitSha: current.commit.sha,
|
|
1853
|
+
routeKey,
|
|
1854
|
+
method: prevRoute.method,
|
|
1855
|
+
path: prevRoute.path
|
|
1856
|
+
});
|
|
1857
|
+
continue;
|
|
1858
|
+
}
|
|
1859
|
+
if (!prevRoute || !nextRoute) continue;
|
|
1860
|
+
const schemaDiff = diffRouteSchemas(prevRoute.flatSchema, nextRoute.flatSchema);
|
|
1861
|
+
if (schemaDiff.length > 0) {
|
|
1862
|
+
routeEvents.push({
|
|
1863
|
+
type: "schema_changed",
|
|
1864
|
+
commitSha: current.commit.sha,
|
|
1865
|
+
routeKey,
|
|
1866
|
+
method: nextRoute.method,
|
|
1867
|
+
path: nextRoute.path,
|
|
1868
|
+
schemaDiff
|
|
1869
|
+
});
|
|
1870
|
+
}
|
|
1871
|
+
const cfgDiff = diffRouteCfg(prevRoute.cfg, nextRoute.cfg);
|
|
1872
|
+
if (cfgDiff.length > 0) {
|
|
1873
|
+
routeEvents.push({
|
|
1874
|
+
type: "cfg_changed",
|
|
1875
|
+
commitSha: current.commit.sha,
|
|
1876
|
+
routeKey,
|
|
1877
|
+
method: nextRoute.method,
|
|
1878
|
+
path: nextRoute.path,
|
|
1879
|
+
cfgDiff
|
|
1880
|
+
});
|
|
1881
|
+
}
|
|
1882
|
+
}
|
|
1883
|
+
const sourceKeys = /* @__PURE__ */ new Set([
|
|
1884
|
+
...Object.keys(previous.sourceObjectsById),
|
|
1885
|
+
...Object.keys(current.sourceObjectsById)
|
|
1886
|
+
]);
|
|
1887
|
+
for (const sourceObjectId of Array.from(sourceKeys).sort()) {
|
|
1888
|
+
const prevSource = previous.sourceObjectsById[sourceObjectId];
|
|
1889
|
+
const nextSource = current.sourceObjectsById[sourceObjectId];
|
|
1890
|
+
if (!prevSource && nextSource) {
|
|
1891
|
+
sourceEvents.push({
|
|
1892
|
+
type: "source_object_added",
|
|
1893
|
+
commitSha: current.commit.sha,
|
|
1894
|
+
sourceObjectId,
|
|
1895
|
+
displayName: nextSource.displayName,
|
|
1896
|
+
impactedRoutes: nextSource.routeKeys
|
|
1897
|
+
});
|
|
1898
|
+
continue;
|
|
1899
|
+
}
|
|
1900
|
+
if (prevSource && !nextSource) {
|
|
1901
|
+
sourceEvents.push({
|
|
1902
|
+
type: "source_object_removed",
|
|
1903
|
+
commitSha: current.commit.sha,
|
|
1904
|
+
sourceObjectId,
|
|
1905
|
+
displayName: prevSource.displayName,
|
|
1906
|
+
impactedRoutes: prevSource.routeKeys
|
|
1907
|
+
});
|
|
1908
|
+
continue;
|
|
1909
|
+
}
|
|
1910
|
+
if (!prevSource || !nextSource) continue;
|
|
1911
|
+
const schemaDiff = diffSourceSchemas(prevSource, nextSource);
|
|
1912
|
+
const prevRoutes = new Set(prevSource.routeKeys);
|
|
1913
|
+
const nextRoutes = new Set(nextSource.routeKeys);
|
|
1914
|
+
const routeAdded = Array.from(nextRoutes).filter((routeKey) => !prevRoutes.has(routeKey));
|
|
1915
|
+
const routeRemoved = Array.from(prevRoutes).filter((routeKey) => !nextRoutes.has(routeKey));
|
|
1916
|
+
if (schemaDiff.length > 0 || routeAdded.length > 0 || routeRemoved.length > 0) {
|
|
1917
|
+
sourceEvents.push({
|
|
1918
|
+
type: "source_schema_changed",
|
|
1919
|
+
commitSha: current.commit.sha,
|
|
1920
|
+
sourceObjectId,
|
|
1921
|
+
displayName: nextSource.displayName,
|
|
1922
|
+
impactedRoutes: nextSource.routeKeys,
|
|
1923
|
+
schemaDiff: schemaDiff.length > 0 ? schemaDiff : void 0,
|
|
1924
|
+
routeAdded: routeAdded.length > 0 ? routeAdded : void 0,
|
|
1925
|
+
routeRemoved: routeRemoved.length > 0 ? routeRemoved : void 0
|
|
1926
|
+
});
|
|
1927
|
+
}
|
|
1928
|
+
}
|
|
1929
|
+
return {
|
|
1930
|
+
routeEvents,
|
|
1931
|
+
sourceEvents
|
|
1932
|
+
};
|
|
1933
|
+
}
|
|
1934
|
+
var BAKED_PAYLOAD_MARKER2 = "<!--__FINALIZED_LEAVES_BAKED_PAYLOAD__-->";
|
|
1935
|
+
function escapePayloadForInlineScript2(payload) {
|
|
1936
|
+
return JSON.stringify(payload).replace(/<\//g, "<\\/").replace(/<!--/g, "<\\!--");
|
|
1937
|
+
}
|
|
1938
|
+
function injectPayloadIntoViewerHtml2(htmlTemplate, payload) {
|
|
1939
|
+
const payloadScript = `${BAKED_PAYLOAD_MARKER2}
|
|
1940
|
+
<script id="finalized-leaves-baked-payload">window.__FINALIZED_LEAVES_PAYLOAD = ${escapePayloadForInlineScript2(
|
|
1941
|
+
payload
|
|
1942
|
+
)};</script>`;
|
|
1943
|
+
if (htmlTemplate.includes(BAKED_PAYLOAD_MARKER2)) {
|
|
1944
|
+
return htmlTemplate.replace(BAKED_PAYLOAD_MARKER2, payloadScript);
|
|
1945
|
+
}
|
|
1946
|
+
if (htmlTemplate.includes("</body>")) {
|
|
1947
|
+
return htmlTemplate.replace("</body>", `${payloadScript}
|
|
1948
|
+
</body>`);
|
|
1949
|
+
}
|
|
1950
|
+
return `${payloadScript}
|
|
1951
|
+
${htmlTemplate}`;
|
|
1952
|
+
}
|
|
1953
|
+
async function resolveViewerTemplatePath2(viewerTemplateFile) {
|
|
1954
|
+
if (viewerTemplateFile) {
|
|
1955
|
+
const resolved = path4.resolve(viewerTemplateFile);
|
|
1956
|
+
await fs2.access(resolved);
|
|
1957
|
+
return resolved;
|
|
1958
|
+
}
|
|
1959
|
+
const candidates = [
|
|
1960
|
+
path4.resolve(
|
|
1961
|
+
process.cwd(),
|
|
1962
|
+
"node_modules/@emeryld/rrroutes-export/tools/finalized-leaves-viewer.html"
|
|
1963
|
+
),
|
|
1964
|
+
path4.resolve(process.cwd(), "tools/finalized-leaves-viewer.html"),
|
|
1965
|
+
path4.resolve(process.cwd(), "packages/export/tools/finalized-leaves-viewer.html")
|
|
1966
|
+
];
|
|
1967
|
+
for (const candidate of candidates) {
|
|
1968
|
+
try {
|
|
1969
|
+
await fs2.access(candidate);
|
|
1970
|
+
return candidate;
|
|
1971
|
+
} catch {
|
|
1972
|
+
}
|
|
1973
|
+
}
|
|
1974
|
+
return void 0;
|
|
1975
|
+
}
|
|
1976
|
+
async function writeJsonExport2(payload, outFile) {
|
|
1977
|
+
const resolved = path4.resolve(outFile);
|
|
1978
|
+
await fs2.mkdir(path4.dirname(resolved), { recursive: true });
|
|
1979
|
+
await fs2.writeFile(resolved, `${JSON.stringify(payload, null, 2)}
|
|
1980
|
+
`, "utf8");
|
|
1981
|
+
return resolved;
|
|
1982
|
+
}
|
|
1983
|
+
async function writeBakedHtmlExport2(payload, htmlFile, viewerTemplateFile) {
|
|
1984
|
+
const templatePath = await resolveViewerTemplatePath2(viewerTemplateFile);
|
|
1985
|
+
const template = templatePath ? await fs2.readFile(templatePath, "utf8") : DEFAULT_VIEWER_TEMPLATE;
|
|
1986
|
+
const baked = injectPayloadIntoViewerHtml2(template, payload);
|
|
1987
|
+
const resolved = path4.resolve(htmlFile);
|
|
1988
|
+
await fs2.mkdir(path4.dirname(resolved), { recursive: true });
|
|
1989
|
+
await fs2.writeFile(resolved, baked, "utf8");
|
|
1990
|
+
return resolved;
|
|
1991
|
+
}
|
|
1992
|
+
async function openHtmlInBrowser2(filePath) {
|
|
1993
|
+
const resolved = path4.resolve(filePath);
|
|
1994
|
+
const platform = process.platform;
|
|
1995
|
+
if (platform === "darwin") {
|
|
1996
|
+
spawn2("open", [resolved], { detached: true, stdio: "ignore" }).unref();
|
|
1997
|
+
return;
|
|
1998
|
+
}
|
|
1999
|
+
if (platform === "win32") {
|
|
2000
|
+
spawn2("cmd", ["/c", "start", "", resolved], {
|
|
2001
|
+
detached: true,
|
|
2002
|
+
stdio: "ignore"
|
|
2003
|
+
}).unref();
|
|
2004
|
+
return;
|
|
2005
|
+
}
|
|
2006
|
+
spawn2("xdg-open", [resolved], { detached: true, stdio: "ignore" }).unref();
|
|
2007
|
+
}
|
|
2008
|
+
async function writeFinalizedLeavesChangelogExport(payload, outFileOrOptions) {
|
|
2009
|
+
const options = typeof outFileOrOptions === "string" ? { outFile: outFileOrOptions } : outFileOrOptions;
|
|
2010
|
+
const written = {};
|
|
2011
|
+
if (options.outFile) {
|
|
2012
|
+
written.outFile = await writeJsonExport2(payload, options.outFile);
|
|
2013
|
+
}
|
|
2014
|
+
if (options.htmlFile) {
|
|
2015
|
+
written.htmlFile = await writeBakedHtmlExport2(
|
|
2016
|
+
payload,
|
|
2017
|
+
options.htmlFile,
|
|
2018
|
+
options.viewerTemplateFile
|
|
2019
|
+
);
|
|
2020
|
+
}
|
|
2021
|
+
if (options.openOnFinish) {
|
|
2022
|
+
if (!written.htmlFile) {
|
|
2023
|
+
throw new Error(
|
|
2024
|
+
"openOnFinish requires htmlFile. Provide htmlFile to open the baked viewer."
|
|
2025
|
+
);
|
|
2026
|
+
}
|
|
2027
|
+
await openHtmlInBrowser2(written.htmlFile);
|
|
2028
|
+
}
|
|
2029
|
+
return written;
|
|
2030
|
+
}
|
|
2031
|
+
async function exportFinalizedLeavesChangelog(options) {
|
|
2032
|
+
const cwd = process.cwd();
|
|
2033
|
+
const repoRoot = await runGit(cwd, ["rev-parse", "--show-toplevel"]);
|
|
2034
|
+
const modulePathAbsolute = path4.resolve(cwd, options.modulePath);
|
|
2035
|
+
const modulePathRelative = normalizePath(path4.relative(repoRoot, modulePathAbsolute));
|
|
2036
|
+
if (modulePathRelative.startsWith("..")) {
|
|
2037
|
+
throw new Error("modulePath must resolve inside the git repository root.");
|
|
2038
|
+
}
|
|
2039
|
+
const tsconfigAbsolute = options.tsconfigPath ? path4.resolve(cwd, options.tsconfigPath) : void 0;
|
|
2040
|
+
const tsconfigRelative = tsconfigAbsolute ? normalizePath(path4.relative(repoRoot, tsconfigAbsolute)) : void 0;
|
|
2041
|
+
if (tsconfigRelative && tsconfigRelative.startsWith("..")) {
|
|
2042
|
+
throw new Error("tsconfigPath must resolve inside the git repository root.");
|
|
2043
|
+
}
|
|
2044
|
+
const exportName = options.exportName ?? "leaves";
|
|
2045
|
+
const window = await resolveCommitWindow(repoRoot, options.from, options.to);
|
|
2046
|
+
const allCommits = await resolveCommitPath(repoRoot, window.from, window.to);
|
|
2047
|
+
const selectedCommitShas = await filterCommits(
|
|
2048
|
+
repoRoot,
|
|
2049
|
+
allCommits,
|
|
2050
|
+
modulePathRelative,
|
|
2051
|
+
tsconfigRelative
|
|
2052
|
+
);
|
|
2053
|
+
if (selectedCommitShas.length === 0) {
|
|
2054
|
+
selectedCommitShas.push(window.to);
|
|
2055
|
+
}
|
|
2056
|
+
const commits = [];
|
|
2057
|
+
for (const commit of selectedCommitShas) {
|
|
2058
|
+
commits.push(await getCommitMetadata(repoRoot, commit));
|
|
2059
|
+
}
|
|
2060
|
+
const tempRoot = await fs2.mkdtemp(path4.join(os.tmpdir(), "rrroutes-export-changelog-"));
|
|
2061
|
+
try {
|
|
2062
|
+
const snapshots = [];
|
|
2063
|
+
for (const commit of commits) {
|
|
2064
|
+
snapshots.push(
|
|
2065
|
+
await createSnapshot(
|
|
2066
|
+
repoRoot,
|
|
2067
|
+
modulePathRelative,
|
|
2068
|
+
exportName,
|
|
2069
|
+
commit,
|
|
2070
|
+
tempRoot,
|
|
2071
|
+
tsconfigRelative,
|
|
2072
|
+
options
|
|
2073
|
+
)
|
|
2074
|
+
);
|
|
2075
|
+
}
|
|
2076
|
+
const routeEvents = [];
|
|
2077
|
+
const sourceEvents = [];
|
|
2078
|
+
for (let i = 1; i < snapshots.length; i += 1) {
|
|
2079
|
+
const { routeEvents: nextRouteEvents, sourceEvents: nextSourceEvents } = diffSnapshots(
|
|
2080
|
+
snapshots[i - 1],
|
|
2081
|
+
snapshots[i]
|
|
2082
|
+
);
|
|
2083
|
+
routeEvents.push(...nextRouteEvents);
|
|
2084
|
+
sourceEvents.push(...nextSourceEvents);
|
|
2085
|
+
}
|
|
2086
|
+
const payload = {
|
|
2087
|
+
kind: "finalized-leaves-changelog",
|
|
2088
|
+
_meta: {
|
|
2089
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2090
|
+
description: "Git-history changelog for RRRoutes finalized leaves with route-level and source-object schema diffs.",
|
|
2091
|
+
modulePath: options.modulePath,
|
|
2092
|
+
exportName,
|
|
2093
|
+
from: window.from,
|
|
2094
|
+
to: window.to,
|
|
2095
|
+
selectedCommitCount: commits.length
|
|
2096
|
+
},
|
|
2097
|
+
commits,
|
|
2098
|
+
byRoute: routeRecordByKey(routeEvents),
|
|
2099
|
+
bySourceObject: sourceRecordById(sourceEvents),
|
|
2100
|
+
rollups: buildRollups(routeEvents, sourceEvents, commits)
|
|
2101
|
+
};
|
|
2102
|
+
if (options.outFile || options.htmlFile) {
|
|
2103
|
+
await writeFinalizedLeavesChangelogExport(payload, {
|
|
2104
|
+
outFile: options.outFile,
|
|
2105
|
+
htmlFile: options.htmlFile,
|
|
2106
|
+
viewerTemplateFile: options.viewerTemplateFile,
|
|
2107
|
+
openOnFinish: options.openOnFinish
|
|
2108
|
+
});
|
|
2109
|
+
}
|
|
2110
|
+
return payload;
|
|
2111
|
+
} finally {
|
|
2112
|
+
await fs2.rm(tempRoot, { recursive: true, force: true }).catch(() => void 0);
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
var __private = {
|
|
2116
|
+
resolveCommitWindow,
|
|
2117
|
+
resolveCommitPath,
|
|
2118
|
+
filterCommits,
|
|
2119
|
+
toRouteSnapshots,
|
|
2120
|
+
toSourceObjectSnapshots,
|
|
2121
|
+
diffSnapshots,
|
|
2122
|
+
buildSourceIdentity
|
|
2123
|
+
};
|
|
2124
|
+
|
|
2125
|
+
// src/exportFinalizedLeaves.changelog.cli.ts
|
|
2126
|
+
import path5 from "path";
|
|
2127
|
+
function parseFinalizedLeavesChangelogCliArgs(argv) {
|
|
2128
|
+
const args = /* @__PURE__ */ new Map();
|
|
2129
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
2130
|
+
const key = argv[i];
|
|
2131
|
+
if (key === "--") continue;
|
|
2132
|
+
if (!key.startsWith("--")) continue;
|
|
2133
|
+
if (key === "--with-source") {
|
|
2134
|
+
continue;
|
|
2135
|
+
}
|
|
2136
|
+
const value = argv[i + 1];
|
|
2137
|
+
if (!value || value.startsWith("--")) {
|
|
2138
|
+
throw new Error(`Missing value for ${key}`);
|
|
2139
|
+
}
|
|
2140
|
+
args.set(key, value);
|
|
2141
|
+
i += 1;
|
|
2142
|
+
}
|
|
2143
|
+
const modulePath = args.get("--module");
|
|
2144
|
+
if (!modulePath) {
|
|
2145
|
+
throw new Error("Missing required --module argument");
|
|
2146
|
+
}
|
|
2147
|
+
return {
|
|
2148
|
+
modulePath,
|
|
2149
|
+
exportName: args.get("--export") ?? "leaves",
|
|
2150
|
+
outFile: args.get("--out") ?? "finalized-leaves.changelog.json",
|
|
2151
|
+
htmlFile: args.get("--html") ?? void 0,
|
|
2152
|
+
tsconfigPath: args.get("--tsconfig") ?? void 0,
|
|
2153
|
+
from: args.get("--from") ?? void 0,
|
|
2154
|
+
to: args.get("--to") ?? void 0
|
|
2155
|
+
};
|
|
2156
|
+
}
|
|
2157
|
+
async function runExportFinalizedLeavesChangelogCli(argv) {
|
|
2158
|
+
const args = parseFinalizedLeavesChangelogCliArgs(argv);
|
|
2159
|
+
const options = {
|
|
2160
|
+
modulePath: args.modulePath,
|
|
2161
|
+
exportName: args.exportName,
|
|
2162
|
+
outFile: args.outFile,
|
|
2163
|
+
htmlFile: args.htmlFile,
|
|
2164
|
+
tsconfigPath: args.tsconfigPath,
|
|
2165
|
+
from: args.from,
|
|
2166
|
+
to: args.to,
|
|
2167
|
+
includeSource: true
|
|
2168
|
+
};
|
|
2169
|
+
const payload = await exportFinalizedLeavesChangelog(options);
|
|
2170
|
+
return {
|
|
2171
|
+
payload,
|
|
2172
|
+
outFile: path5.resolve(args.outFile),
|
|
2173
|
+
htmlFile: args.htmlFile ? path5.resolve(args.htmlFile) : void 0
|
|
2174
|
+
};
|
|
2175
|
+
}
|
|
1318
2176
|
export {
|
|
1319
2177
|
DEFAULT_VIEWER_TEMPLATE,
|
|
2178
|
+
__private,
|
|
1320
2179
|
clearSchemaIntrospectionHandlers,
|
|
1321
2180
|
createSchemaIntrospector,
|
|
1322
2181
|
exportFinalizedLeaves,
|
|
2182
|
+
exportFinalizedLeavesChangelog,
|
|
1323
2183
|
extractLeafSourceByAst,
|
|
1324
2184
|
flattenLeafSchemas,
|
|
1325
2185
|
flattenSerializableSchema,
|
|
1326
2186
|
introspectSchema,
|
|
1327
2187
|
loadFinalizedLeavesInput,
|
|
2188
|
+
parseFinalizedLeavesChangelogCliArgs,
|
|
1328
2189
|
parseFinalizedLeavesCliArgs,
|
|
1329
2190
|
registerSchemaIntrospectionHandler,
|
|
2191
|
+
runExportFinalizedLeavesChangelogCli,
|
|
1330
2192
|
runExportFinalizedLeavesCli,
|
|
1331
2193
|
serializableSchemaKinds,
|
|
1332
2194
|
serializeLeafContract,
|
|
1333
2195
|
serializeLeavesContract,
|
|
2196
|
+
writeFinalizedLeavesChangelogExport,
|
|
1334
2197
|
writeFinalizedLeavesExport
|
|
1335
2198
|
};
|
|
1336
2199
|
//# sourceMappingURL=index.mjs.map
|