@emeryld/rrroutes-export 1.0.7 → 1.0.9

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/dist/index.mjs CHANGED
@@ -1340,9 +1340,11 @@ async function runExportFinalizedLeavesCli(argv) {
1340
1340
  import fs2 from "fs/promises";
1341
1341
  import os from "os";
1342
1342
  import path4 from "path";
1343
+ import crypto from "crypto";
1343
1344
  import { execFile as execFileCallback, spawn as spawn2 } from "child_process";
1344
1345
  import { promisify } from "util";
1345
1346
  import { pathToFileURL as pathToFileURL2 } from "url";
1347
+ import ts2 from "typescript";
1346
1348
  var execFile = promisify(execFileCallback);
1347
1349
  var CHANGESET_CFG_FIELDS = [
1348
1350
  "summary",
@@ -1817,20 +1819,221 @@ async function ensureWorktreeDependencies(repoRoot, worktreeDir) {
1817
1819
  }
1818
1820
  await fs2.symlink(rootNodeModules, worktreeNodeModules, "junction");
1819
1821
  }
1820
- async function createSnapshot(repoRoot, moduleRel, exportName, commit, tempRoot, tsconfigRel, exportOptions) {
1822
+ async function pathExists(filePath) {
1823
+ try {
1824
+ await fs2.access(filePath);
1825
+ return true;
1826
+ } catch {
1827
+ return false;
1828
+ }
1829
+ }
1830
+ function isTypeScriptModule(modulePath) {
1831
+ const ext = path4.extname(modulePath).toLowerCase();
1832
+ return ext === ".ts" || ext === ".tsx" || ext === ".mts" || ext === ".cts";
1833
+ }
1834
+ function isJavaScriptModule(modulePath) {
1835
+ const ext = path4.extname(modulePath).toLowerCase();
1836
+ return ext === ".js" || ext === ".mjs" || ext === ".cjs";
1837
+ }
1838
+ async function resolveSnapshotTsconfig(worktreeDir, modulePath, tsconfigOverride) {
1839
+ if (tsconfigOverride) {
1840
+ const resolved = path4.resolve(worktreeDir, tsconfigOverride);
1841
+ if (!await pathExists(resolved)) {
1842
+ throw new Error(`tsconfig not found: ${resolved}`);
1843
+ }
1844
+ return resolved;
1845
+ }
1846
+ let current = path4.dirname(modulePath);
1847
+ const normalizedRoot = normalizePath(path4.resolve(worktreeDir));
1848
+ while (true) {
1849
+ const candidate = path4.join(current, "tsconfig.json");
1850
+ if (await pathExists(candidate)) {
1851
+ return candidate;
1852
+ }
1853
+ const parent = path4.dirname(current);
1854
+ if (parent === current) break;
1855
+ if (!normalizePath(parent).startsWith(normalizedRoot)) break;
1856
+ current = parent;
1857
+ }
1858
+ throw new Error(
1859
+ `Unable to resolve tsconfig for TypeScript module: ${modulePath}. Pass --tsconfig to override.`
1860
+ );
1861
+ }
1862
+ function toEmittedModulePath(moduleRel) {
1863
+ const normalized = normalizePath(moduleRel);
1864
+ if (normalized.endsWith(".mts")) return normalized.slice(0, -4) + ".mjs";
1865
+ if (normalized.endsWith(".cts")) return normalized.slice(0, -4) + ".cjs";
1866
+ if (normalized.endsWith(".tsx")) return normalized.slice(0, -4) + ".js";
1867
+ if (normalized.endsWith(".ts")) return normalized.slice(0, -3) + ".js";
1868
+ return normalized;
1869
+ }
1870
+ function buildCompileCacheKey(commitSha, moduleRel, tsconfigPath, tsconfigStat) {
1871
+ return crypto.createHash("sha1").update(commitSha).update("\n").update(normalizePath(moduleRel)).update("\n").update(normalizePath(tsconfigPath)).update("\n").update(String(tsconfigStat.mtimeMs)).update("\n").update(String(tsconfigStat.size)).digest("hex");
1872
+ }
1873
+ async function collectFilesRecursive(rootDir) {
1874
+ const files = [];
1875
+ const walk = async (dir) => {
1876
+ const entries = await fs2.readdir(dir, { withFileTypes: true });
1877
+ for (const entry of entries) {
1878
+ const target = path4.join(dir, entry.name);
1879
+ if (entry.isDirectory()) {
1880
+ await walk(target);
1881
+ continue;
1882
+ }
1883
+ if (entry.isFile()) {
1884
+ files.push(target);
1885
+ }
1886
+ }
1887
+ };
1888
+ if (await pathExists(rootDir)) {
1889
+ await walk(rootDir);
1890
+ }
1891
+ return files;
1892
+ }
1893
+ function suffixMatchScore(candidate, wanted) {
1894
+ const left = normalizePath(candidate).split("/");
1895
+ const right = normalizePath(wanted).split("/");
1896
+ let score = 0;
1897
+ while (score < left.length && score < right.length) {
1898
+ const a = left[left.length - 1 - score];
1899
+ const b = right[right.length - 1 - score];
1900
+ if (a !== b) break;
1901
+ score += 1;
1902
+ }
1903
+ return score;
1904
+ }
1905
+ async function resolveEmittedEntryPath(emitRoot, moduleRel) {
1906
+ const expected = path4.resolve(emitRoot, toEmittedModulePath(moduleRel));
1907
+ if (await pathExists(expected)) {
1908
+ return expected;
1909
+ }
1910
+ const wantedRel = toEmittedModulePath(moduleRel);
1911
+ const wantedBase = path4.posix.basename(normalizePath(wantedRel));
1912
+ const files = await collectFilesRecursive(emitRoot);
1913
+ const candidates = files.filter(
1914
+ (file) => path4.posix.basename(normalizePath(file)) === wantedBase
1915
+ );
1916
+ if (candidates.length === 0) {
1917
+ return void 0;
1918
+ }
1919
+ candidates.sort(
1920
+ (a, b) => suffixMatchScore(b, wantedRel) - suffixMatchScore(a, wantedRel)
1921
+ );
1922
+ return candidates[0];
1923
+ }
1924
+ async function compileSnapshotEntry(options) {
1925
+ const log = options.log ?? (() => void 0);
1926
+ const tsconfigStat = await fs2.stat(options.tsconfigPath);
1927
+ const cacheKey = buildCompileCacheKey(
1928
+ options.commitSha,
1929
+ options.moduleRel,
1930
+ options.tsconfigPath,
1931
+ { mtimeMs: tsconfigStat.mtimeMs, size: tsconfigStat.size }
1932
+ );
1933
+ const emitRoot = path4.join(options.cacheRoot, cacheKey, "dist");
1934
+ let emittedEntry = path4.resolve(emitRoot, toEmittedModulePath(options.moduleRel));
1935
+ if (!options.noCache && await pathExists(emittedEntry)) {
1936
+ log(`[changelog] compile cache hit: ${options.commitSha.slice(0, 12)}`);
1937
+ return emittedEntry;
1938
+ }
1939
+ log(
1940
+ `[changelog] compile cache ${options.noCache ? "disabled" : "miss"}: ${options.commitSha.slice(0, 12)}`
1941
+ );
1942
+ await fs2.rm(path4.join(options.cacheRoot, cacheKey), { recursive: true, force: true }).catch(
1943
+ () => void 0
1944
+ );
1945
+ await fs2.mkdir(emitRoot, { recursive: true });
1946
+ const readResult = ts2.readConfigFile(options.tsconfigPath, ts2.sys.readFile);
1947
+ if (readResult.error) {
1948
+ throw new Error(ts2.flattenDiagnosticMessageText(readResult.error.messageText, "\n"));
1949
+ }
1950
+ const parsed = ts2.parseJsonConfigFileContent(
1951
+ readResult.config,
1952
+ ts2.sys,
1953
+ path4.dirname(options.tsconfigPath)
1954
+ );
1955
+ const compilerOptions = {
1956
+ ...parsed.options,
1957
+ noEmit: false,
1958
+ declaration: false,
1959
+ declarationMap: false,
1960
+ sourceMap: false,
1961
+ inlineSourceMap: false,
1962
+ composite: false,
1963
+ incremental: false,
1964
+ tsBuildInfoFile: void 0,
1965
+ outDir: emitRoot
1966
+ };
1967
+ const program = ts2.createProgram([options.modulePath], compilerOptions);
1968
+ const emitResult = program.emit();
1969
+ const diagnostics = ts2.getPreEmitDiagnostics(program).concat(emitResult.diagnostics).filter((diagnostic) => diagnostic.category === ts2.DiagnosticCategory.Error);
1970
+ if (diagnostics.length > 0) {
1971
+ const host = {
1972
+ getCanonicalFileName: (fileName) => fileName,
1973
+ getCurrentDirectory: () => options.worktreeDir,
1974
+ getNewLine: () => "\n"
1975
+ };
1976
+ const message = ts2.formatDiagnosticsWithColorAndContext(diagnostics.slice(0, 20), host);
1977
+ throw new Error(
1978
+ `TypeScript snapshot compile failed for ${options.moduleRel} at ${options.commitSha.slice(0, 12)}:
1979
+ ${message}`
1980
+ );
1981
+ }
1982
+ emittedEntry = await resolveEmittedEntryPath(emitRoot, options.moduleRel) ?? emittedEntry;
1983
+ if (!await pathExists(emittedEntry)) {
1984
+ throw new Error(
1985
+ `Compiled snapshot entry was not emitted: ${emittedEntry}. Check module/tsconfig compatibility.`
1986
+ );
1987
+ }
1988
+ log(`[changelog] compiled snapshot entry: ${emittedEntry}`);
1989
+ return emittedEntry;
1990
+ }
1991
+ async function loadSnapshotExport(options) {
1992
+ return loadChangelogInput(options.entryPath, options.exportName);
1993
+ }
1994
+ async function createSnapshot(repoRoot, moduleRel, exportName, commit, tempRoot, tsconfigRel, exportOptions, log) {
1821
1995
  const worktreeDir = path4.join(tempRoot, `wt-${commit.sha.slice(0, 12)}`);
1822
1996
  await runGit(repoRoot, ["worktree", "add", "--detach", worktreeDir, commit.sha]);
1823
1997
  try {
1824
1998
  await ensureWorktreeDependencies(repoRoot, worktreeDir);
1825
1999
  const modulePath = path4.resolve(worktreeDir, moduleRel);
1826
- const tsconfigPath = tsconfigRel ? path4.resolve(worktreeDir, tsconfigRel) : void 0;
1827
- const input = await loadChangelogInput(modulePath, exportName);
2000
+ let snapshotRuntimePath = modulePath;
2001
+ let resolvedTsconfigPath;
2002
+ if (isTypeScriptModule(modulePath)) {
2003
+ resolvedTsconfigPath = await resolveSnapshotTsconfig(
2004
+ worktreeDir,
2005
+ modulePath,
2006
+ tsconfigRel
2007
+ );
2008
+ log?.(`[changelog] using tsconfig: ${resolvedTsconfigPath}`);
2009
+ const compileCacheRoot = path4.resolve(
2010
+ exportOptions?.cacheDir ?? path4.join(os.tmpdir(), "rrroutes-export-changelog-cache")
2011
+ );
2012
+ snapshotRuntimePath = await compileSnapshotEntry({
2013
+ worktreeDir,
2014
+ moduleRel,
2015
+ modulePath,
2016
+ tsconfigPath: resolvedTsconfigPath,
2017
+ commitSha: commit.sha,
2018
+ cacheRoot: compileCacheRoot,
2019
+ noCache: exportOptions?.noCache,
2020
+ log
2021
+ });
2022
+ } else if (!isJavaScriptModule(modulePath)) {
2023
+ throw new Error(
2024
+ `Unsupported module extension for changelog snapshots: ${modulePath}`
2025
+ );
2026
+ }
2027
+ const input = await loadSnapshotExport({
2028
+ entryPath: snapshotRuntimePath,
2029
+ exportName
2030
+ });
1828
2031
  const payload = await exportFinalizedLeaves(input, {
1829
2032
  includeSource: true,
1830
2033
  sourceModulePath: modulePath,
1831
2034
  sourceExportName: exportName,
1832
- tsconfigPath,
1833
- ...exportOptions
2035
+ tsconfigPath: resolvedTsconfigPath ?? (tsconfigRel ? path4.resolve(worktreeDir, tsconfigRel) : void 0),
2036
+ handlers: exportOptions?.handlers
1834
2037
  });
1835
2038
  const routesByKey = toRouteSnapshots(payload);
1836
2039
  const sourceObjectsById = toSourceObjectSnapshots(routesByKey);
@@ -2100,7 +2303,8 @@ async function exportFinalizedLeavesChangelog(options) {
2100
2303
  commit,
2101
2304
  tempRoot,
2102
2305
  tsconfigRelative,
2103
- options
2306
+ options,
2307
+ log
2104
2308
  )
2105
2309
  );
2106
2310
  }
@@ -2154,6 +2358,11 @@ var __private = {
2154
2358
  resolveCommitWindow,
2155
2359
  resolveCommitPath,
2156
2360
  filterCommits,
2361
+ resolveSnapshotTsconfig,
2362
+ toEmittedModulePath,
2363
+ buildCompileCacheKey,
2364
+ compileSnapshotEntry,
2365
+ loadSnapshotExport,
2157
2366
  toRouteSnapshots,
2158
2367
  toSourceObjectSnapshots,
2159
2368
  diffSnapshots,
@@ -2164,6 +2373,7 @@ var __private = {
2164
2373
  import path5 from "path";
2165
2374
  function parseFinalizedLeavesChangelogCliArgs(argv) {
2166
2375
  const args = /* @__PURE__ */ new Map();
2376
+ let noCache = false;
2167
2377
  for (let i = 0; i < argv.length; i += 1) {
2168
2378
  const key = argv[i];
2169
2379
  if (key === "--") continue;
@@ -2171,6 +2381,10 @@ function parseFinalizedLeavesChangelogCliArgs(argv) {
2171
2381
  if (key === "--with-source") {
2172
2382
  continue;
2173
2383
  }
2384
+ if (key === "--no-cache") {
2385
+ noCache = true;
2386
+ continue;
2387
+ }
2174
2388
  const value = argv[i + 1];
2175
2389
  if (!value || value.startsWith("--")) {
2176
2390
  throw new Error(`Missing value for ${key}`);
@@ -2189,7 +2403,9 @@ function parseFinalizedLeavesChangelogCliArgs(argv) {
2189
2403
  htmlFile: args.get("--html") ?? void 0,
2190
2404
  tsconfigPath: args.get("--tsconfig") ?? void 0,
2191
2405
  from: args.get("--from") ?? void 0,
2192
- to: args.get("--to") ?? void 0
2406
+ to: args.get("--to") ?? void 0,
2407
+ cacheDir: args.get("--cache-dir") ?? void 0,
2408
+ noCache
2193
2409
  };
2194
2410
  }
2195
2411
  async function runExportFinalizedLeavesChangelogCli(argv) {
@@ -2207,6 +2423,8 @@ async function runExportFinalizedLeavesChangelogCli(argv) {
2207
2423
  from: args.from,
2208
2424
  to: args.to,
2209
2425
  includeSource: true,
2426
+ cacheDir: args.cacheDir,
2427
+ noCache: args.noCache,
2210
2428
  log
2211
2429
  };
2212
2430
  log("[changelog-cli] starting changelog export");