@emeryld/rrroutes-export 1.0.9 → 1.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -72,11 +72,13 @@ Settings:
72
72
  Behavior summary:
73
73
 
74
74
  - Resolves commit path from `from..to` (chronological).
75
+ - Builds commit-aware module path candidates (current path + extension variants + rename history).
75
76
  - Filters commits to those touching module tree/relevant config files.
76
77
  - Ensures tip commit is always included.
77
78
  - Creates a temp git worktree per selected commit.
78
79
  - For TypeScript module entries, compiles the commit snapshot to JS and imports emitted output.
79
80
  - For JavaScript module entries, imports directly from worktree.
81
+ - If module entrypoint cannot be resolved for a commit, changelog uses an empty snapshot for that commit (timeline continuity).
80
82
  - Exports snapshots, then computes adjacent diffs:
81
83
  - route events (`route_added`, `route_removed`, `schema_changed`, `cfg_changed`)
82
84
  - source-object events (`source_object_added`, `source_object_removed`, `source_schema_changed`)
@@ -85,6 +87,7 @@ Runtime progress logs:
85
87
 
86
88
  - `rrroutes-export-changelog` now prints step-by-step progress (window resolution, commit filtering, per-commit snapshot progress, diff totals, output writing, cleanup).
87
89
  - Changelog logs include compile lifecycle details: selected tsconfig, cache hit/miss, emitted entry path.
90
+ - Changelog logs include per-commit module resolution and unresolved-entry fallback messages.
88
91
  - Bundle script prints stage logs for snapshot export + changelog export.
89
92
 
90
93
  Troubleshooting historical TS imports:
@@ -92,6 +95,7 @@ Troubleshooting historical TS imports:
92
95
  - Changelog execution no longer relies on direct Node runtime loading of TS sources.
93
96
  - Historical imports like `./x.js` pointing to `x.ts` are handled by snapshot compilation.
94
97
  - If tsconfig auto-discovery fails, pass `--tsconfig`.
98
+ - If entrypoint path moved across history, changelog resolves rename history automatically.
95
99
 
96
100
  ## Recommended end-to-end script
97
101
 
@@ -32,6 +32,7 @@ type CommitSnapshot = {
32
32
  commit: ChangelogCommit;
33
33
  routesByKey: Record<string, RouteSnapshot>;
34
34
  sourceObjectsById: Record<string, SourceObjectSnapshot>;
35
+ unresolvedModule?: boolean;
35
36
  };
36
37
  type DiffOp = 'added' | 'removed' | 'changed';
37
38
  export type SchemaPathDelta = {
@@ -96,6 +97,7 @@ export type FinalizedLeavesChangelogExport = {
96
97
  from: string;
97
98
  to: string;
98
99
  selectedCommitCount: number;
100
+ unresolvedModuleCommits?: string[];
99
101
  };
100
102
  commits: ChangelogCommit[];
101
103
  byRoute: Record<string, RouteChangeEvent[]>;
@@ -123,7 +125,9 @@ declare function resolveCommitWindow(cwd: string, from?: string, to?: string): P
123
125
  to: string;
124
126
  }>;
125
127
  declare function resolveCommitPath(cwd: string, fromCommit: string, toCommit: string): Promise<string[]>;
126
- declare function filterCommits(cwd: string, commits: string[], moduleRel: string, tsconfigRel?: string): Promise<string[]>;
128
+ declare function buildModulePathCandidates(repoRoot: string, toCommit: string, requestedModuleRel: string): Promise<string[]>;
129
+ declare function resolveModulePathForCommit(repoRoot: string, commitSha: string, candidates: readonly string[]): Promise<string | undefined>;
130
+ declare function filterCommits(cwd: string, commits: string[], moduleCandidates: readonly string[], tsconfigRel?: string): Promise<string[]>;
127
131
  declare function buildSourceIdentity(section: ChangelogSchemaSection, value: Record<string, unknown>): SourceIdentity;
128
132
  declare function toRouteSnapshots(payload: FinalizedLeavesExport): Record<string, RouteSnapshot>;
129
133
  declare function toSourceObjectSnapshots(routesByKey: Record<string, RouteSnapshot>): Record<string, SourceObjectSnapshot>;
@@ -160,6 +164,8 @@ export declare const __private: {
160
164
  resolveCommitWindow: typeof resolveCommitWindow;
161
165
  resolveCommitPath: typeof resolveCommitPath;
162
166
  filterCommits: typeof filterCommits;
167
+ buildModulePathCandidates: typeof buildModulePathCandidates;
168
+ resolveModulePathForCommit: typeof resolveModulePathForCommit;
163
169
  resolveSnapshotTsconfig: typeof resolveSnapshotTsconfig;
164
170
  toEmittedModulePath: typeof toEmittedModulePath;
165
171
  buildCompileCacheKey: typeof buildCompileCacheKey;
package/dist/index.cjs CHANGED
@@ -1411,6 +1411,7 @@ var CHANGESET_CFG_FIELDS = [
1411
1411
  "feed"
1412
1412
  ];
1413
1413
  var SCHEMA_SECTIONS = ["params", "query", "body", "output"];
1414
+ var MODULE_EXTENSIONS = [".ts", ".tsx", ".mts", ".cts", ".js", ".mjs", ".cjs"];
1414
1415
  var SOURCE_KEY_BY_SECTION = {
1415
1416
  params: "paramsSchema",
1416
1417
  query: "querySchema",
@@ -1525,6 +1526,65 @@ async function resolveCommitPath(cwd, fromCommit, toCommit) {
1525
1526
  const commits = pathRaw.split("\n").map((item) => item.trim()).filter(Boolean);
1526
1527
  return [fromCommit, ...commits];
1527
1528
  }
1529
+ function modulePathStem(modulePath) {
1530
+ const normalized = normalizePath(modulePath).replace(/^\/+/, "");
1531
+ for (const ext of MODULE_EXTENSIONS) {
1532
+ if (normalized.endsWith(ext)) {
1533
+ return normalized.slice(0, -ext.length);
1534
+ }
1535
+ }
1536
+ return normalized;
1537
+ }
1538
+ function extensionVariants(pathLike) {
1539
+ const stem = modulePathStem(pathLike);
1540
+ return MODULE_EXTENSIONS.map((ext) => `${stem}${ext}`);
1541
+ }
1542
+ async function buildModulePathCandidates(repoRoot, toCommit, requestedModuleRel) {
1543
+ const requested = normalizePath(requestedModuleRel).replace(/^\/+/, "");
1544
+ const out = [];
1545
+ const seen = /* @__PURE__ */ new Set();
1546
+ const pushCandidate = (value) => {
1547
+ const normalized = normalizePath(value).replace(/^\/+/, "");
1548
+ if (!normalized) return;
1549
+ if (seen.has(normalized)) return;
1550
+ seen.add(normalized);
1551
+ out.push(normalized);
1552
+ };
1553
+ pushCandidate(requested);
1554
+ extensionVariants(requested).forEach(pushCandidate);
1555
+ const history = await runGit(repoRoot, [
1556
+ "log",
1557
+ "--follow",
1558
+ "--name-only",
1559
+ "--format=",
1560
+ toCommit,
1561
+ "--",
1562
+ requested
1563
+ ]);
1564
+ history.split("\n").map((item) => item.trim()).filter(Boolean).forEach((candidate) => {
1565
+ pushCandidate(candidate);
1566
+ extensionVariants(candidate).forEach(pushCandidate);
1567
+ });
1568
+ return out;
1569
+ }
1570
+ async function commitHasPath(repoRoot, commitSha, candidatePath) {
1571
+ try {
1572
+ await execFile("git", ["cat-file", "-e", `${commitSha}:${candidatePath}`], {
1573
+ cwd: repoRoot
1574
+ });
1575
+ return true;
1576
+ } catch {
1577
+ return false;
1578
+ }
1579
+ }
1580
+ async function resolveModulePathForCommit(repoRoot, commitSha, candidates) {
1581
+ for (const candidate of candidates) {
1582
+ if (await commitHasPath(repoRoot, commitSha, candidate)) {
1583
+ return candidate;
1584
+ }
1585
+ }
1586
+ return void 0;
1587
+ }
1528
1588
  function parentDirectories(relativePath) {
1529
1589
  const clean = normalizePath(relativePath).replace(/^\/+/, "");
1530
1590
  const dir = import_node_path4.default.posix.dirname(clean);
@@ -1536,8 +1596,11 @@ function parentDirectories(relativePath) {
1536
1596
  }
1537
1597
  return out;
1538
1598
  }
1539
- function buildRelevantConfigFiles(moduleRel, tsconfigRel) {
1540
- const dirs = new Set(parentDirectories(moduleRel));
1599
+ function buildRelevantConfigFiles(moduleCandidates, tsconfigRel) {
1600
+ const dirs = /* @__PURE__ */ new Set();
1601
+ for (const moduleRel of moduleCandidates) {
1602
+ parentDirectories(moduleRel).forEach((item) => dirs.add(item));
1603
+ }
1541
1604
  if (tsconfigRel) {
1542
1605
  parentDirectories(tsconfigRel).forEach((item) => dirs.add(item));
1543
1606
  dirs.add(import_node_path4.default.posix.dirname(normalizePath(tsconfigRel)));
@@ -1550,7 +1613,7 @@ function buildRelevantConfigFiles(moduleRel, tsconfigRel) {
1550
1613
  }
1551
1614
  return files;
1552
1615
  }
1553
- async function commitTouchedRelevantPaths(cwd, commit, moduleRel, tsconfigRel) {
1616
+ async function commitTouchedRelevantPaths(cwd, commit, moduleCandidates, tsconfigRel) {
1554
1617
  const changed = await runGit(cwd, [
1555
1618
  "show",
1556
1619
  "--pretty=format:",
@@ -1559,27 +1622,35 @@ async function commitTouchedRelevantPaths(cwd, commit, moduleRel, tsconfigRel) {
1559
1622
  commit
1560
1623
  ]);
1561
1624
  const changedFiles = changed.split("\n").map((item) => normalizePath(item.trim()).replace(/^\/+/, "")).filter(Boolean);
1562
- const moduleFile = normalizePath(moduleRel).replace(/^\/+/, "");
1563
- const moduleDir = normalizePath(import_node_path4.default.posix.dirname(moduleFile)).replace(/^\/+/, "");
1625
+ const moduleFiles = Array.from(
1626
+ new Set(moduleCandidates.map((item) => normalizePath(item).replace(/^\/+/, "")))
1627
+ );
1628
+ const moduleDirs = Array.from(
1629
+ new Set(
1630
+ moduleFiles.map((item) => normalizePath(import_node_path4.default.posix.dirname(item)).replace(/^\/+/, "")).filter((item) => item && item !== ".")
1631
+ )
1632
+ );
1564
1633
  const tsconfigFile = tsconfigRel ? normalizePath(tsconfigRel).replace(/^\/+/, "") : void 0;
1565
- const configFiles = buildRelevantConfigFiles(moduleFile, tsconfigFile);
1634
+ const configFiles = buildRelevantConfigFiles(moduleFiles, tsconfigFile);
1566
1635
  return changedFiles.some((file) => {
1567
- if (file === moduleFile) return true;
1568
- if (moduleDir && moduleDir !== "." && (file === moduleDir || file.startsWith(`${moduleDir}/`))) {
1569
- return true;
1636
+ if (moduleFiles.includes(file)) return true;
1637
+ for (const moduleDir of moduleDirs) {
1638
+ if (file === moduleDir || file.startsWith(`${moduleDir}/`)) {
1639
+ return true;
1640
+ }
1570
1641
  }
1571
1642
  if (tsconfigFile && file === tsconfigFile) return true;
1572
1643
  return configFiles.has(file);
1573
1644
  });
1574
1645
  }
1575
- async function filterCommits(cwd, commits, moduleRel, tsconfigRel) {
1646
+ async function filterCommits(cwd, commits, moduleCandidates, tsconfigRel) {
1576
1647
  if (commits.length <= 1) {
1577
1648
  return commits;
1578
1649
  }
1579
1650
  const keep = [commits[0]];
1580
1651
  for (let i = 1; i < commits.length; i += 1) {
1581
1652
  const commit = commits[i];
1582
- if (await commitTouchedRelevantPaths(cwd, commit, moduleRel, tsconfigRel)) {
1653
+ if (await commitTouchedRelevantPaths(cwd, commit, moduleCandidates, tsconfigRel)) {
1583
1654
  keep.push(commit);
1584
1655
  }
1585
1656
  }
@@ -2013,6 +2084,7 @@ async function compileSnapshotEntry(options) {
2013
2084
  declarationMap: false,
2014
2085
  sourceMap: false,
2015
2086
  inlineSourceMap: false,
2087
+ inlineSources: false,
2016
2088
  composite: false,
2017
2089
  incremental: false,
2018
2090
  tsBuildInfoFile: void 0,
@@ -2045,12 +2117,31 @@ ${message}`
2045
2117
  async function loadSnapshotExport(options) {
2046
2118
  return loadChangelogInput(options.entryPath, options.exportName);
2047
2119
  }
2048
- async function createSnapshot(repoRoot, moduleRel, exportName, commit, tempRoot, tsconfigRel, exportOptions, log) {
2120
+ async function createSnapshot(repoRoot, moduleCandidates, exportName, commit, tempRoot, tsconfigRel, exportOptions, log) {
2049
2121
  const worktreeDir = import_node_path4.default.join(tempRoot, `wt-${commit.sha.slice(0, 12)}`);
2050
2122
  await runGit(repoRoot, ["worktree", "add", "--detach", worktreeDir, commit.sha]);
2051
2123
  try {
2052
2124
  await ensureWorktreeDependencies(repoRoot, worktreeDir);
2053
- const modulePath = import_node_path4.default.resolve(worktreeDir, moduleRel);
2125
+ const resolvedModuleRel = await resolveModulePathForCommit(
2126
+ repoRoot,
2127
+ commit.sha,
2128
+ moduleCandidates
2129
+ );
2130
+ if (!resolvedModuleRel) {
2131
+ log?.(
2132
+ `[changelog] unresolved module for commit ${commit.sha.slice(0, 12)}; using empty snapshot`
2133
+ );
2134
+ return {
2135
+ commit,
2136
+ routesByKey: {},
2137
+ sourceObjectsById: {},
2138
+ unresolvedModule: true
2139
+ };
2140
+ }
2141
+ log?.(
2142
+ `[changelog] resolved module for commit ${commit.sha.slice(0, 12)}: ${resolvedModuleRel}`
2143
+ );
2144
+ const modulePath = import_node_path4.default.resolve(worktreeDir, resolvedModuleRel);
2054
2145
  let snapshotRuntimePath = modulePath;
2055
2146
  let resolvedTsconfigPath;
2056
2147
  if (isTypeScriptModule(modulePath)) {
@@ -2065,7 +2156,7 @@ async function createSnapshot(repoRoot, moduleRel, exportName, commit, tempRoot,
2065
2156
  );
2066
2157
  snapshotRuntimePath = await compileSnapshotEntry({
2067
2158
  worktreeDir,
2068
- moduleRel,
2159
+ moduleRel: resolvedModuleRel,
2069
2160
  modulePath,
2070
2161
  tsconfigPath: resolvedTsconfigPath,
2071
2162
  commitSha: commit.sha,
@@ -2322,12 +2413,18 @@ async function exportFinalizedLeavesChangelog(options) {
2322
2413
  log("[changelog] resolving commit window");
2323
2414
  const window = await resolveCommitWindow(repoRoot, options.from, options.to);
2324
2415
  log(`[changelog] window: ${window.from.slice(0, 12)}..${window.to.slice(0, 12)}`);
2416
+ const moduleCandidates = await buildModulePathCandidates(
2417
+ repoRoot,
2418
+ window.to,
2419
+ modulePathRelative
2420
+ );
2421
+ log(`[changelog] module candidates discovered: ${moduleCandidates.length}`);
2325
2422
  const allCommits = await resolveCommitPath(repoRoot, window.from, window.to);
2326
2423
  log(`[changelog] commits in ancestry path: ${allCommits.length}`);
2327
2424
  const selectedCommitShas = await filterCommits(
2328
2425
  repoRoot,
2329
2426
  allCommits,
2330
- modulePathRelative,
2427
+ moduleCandidates,
2331
2428
  tsconfigRelative
2332
2429
  );
2333
2430
  log(`[changelog] commits after path filter: ${selectedCommitShas.length}`);
@@ -2344,6 +2441,7 @@ async function exportFinalizedLeavesChangelog(options) {
2344
2441
  log(`[changelog] temp workspace: ${tempRoot}`);
2345
2442
  try {
2346
2443
  const snapshots = [];
2444
+ const unresolvedModuleCommits = [];
2347
2445
  for (let index = 0; index < commits.length; index += 1) {
2348
2446
  const commit = commits[index];
2349
2447
  log(
@@ -2352,7 +2450,7 @@ async function exportFinalizedLeavesChangelog(options) {
2352
2450
  snapshots.push(
2353
2451
  await createSnapshot(
2354
2452
  repoRoot,
2355
- modulePathRelative,
2453
+ moduleCandidates,
2356
2454
  exportName,
2357
2455
  commit,
2358
2456
  tempRoot,
@@ -2361,6 +2459,9 @@ async function exportFinalizedLeavesChangelog(options) {
2361
2459
  log
2362
2460
  )
2363
2461
  );
2462
+ if (snapshots[snapshots.length - 1]?.unresolvedModule) {
2463
+ unresolvedModuleCommits.push(commit.sha);
2464
+ }
2364
2465
  }
2365
2466
  const routeEvents = [];
2366
2467
  const sourceEvents = [];
@@ -2385,7 +2486,8 @@ async function exportFinalizedLeavesChangelog(options) {
2385
2486
  exportName,
2386
2487
  from: window.from,
2387
2488
  to: window.to,
2388
- selectedCommitCount: commits.length
2489
+ selectedCommitCount: commits.length,
2490
+ unresolvedModuleCommits: unresolvedModuleCommits.length > 0 ? unresolvedModuleCommits : void 0
2389
2491
  },
2390
2492
  commits,
2391
2493
  byRoute: routeRecordByKey(routeEvents),
@@ -2412,6 +2514,8 @@ var __private = {
2412
2514
  resolveCommitWindow,
2413
2515
  resolveCommitPath,
2414
2516
  filterCommits,
2517
+ buildModulePathCandidates,
2518
+ resolveModulePathForCommit,
2415
2519
  resolveSnapshotTsconfig,
2416
2520
  toEmittedModulePath,
2417
2521
  buildCompileCacheKey,