@pygmalionjs/pygmalion 0.2.23 → 0.2.25

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.
@@ -1,4 +1,4 @@
1
- import { F as s, N as a, e as r, s as i, a as t } from "./App-CkJW2APT.js";
1
+ import { F as s, N as a, e as r, s as i, a as t } from "./App-rBeENUlS.js";
2
2
  export {
3
3
  s as FrozenRoutePreviewView,
4
4
  a as NodeModel,
@@ -1,3 +1,4 @@
1
+ import { createHash } from 'node:crypto';
1
2
  import fs from 'node:fs/promises';
2
3
  import path from 'node:path';
3
4
  import ts from 'typescript';
@@ -1040,7 +1041,22 @@ function resolveSourceSpecifier(importer, specifier, files) {
1040
1041
  return candidates.find((candidate) => files.has(candidate)) ?? null;
1041
1042
  }
1042
1043
 
1043
- function associateRouteCandidates(manifest, graph) {
1044
+ /**
1045
+ * Content digest of everything a route renders from.
1046
+ *
1047
+ * A frame captured from these files stays valid until one of them changes, which
1048
+ * is what makes an unrelated commit stop invalidating it.
1049
+ */
1050
+ function closureDigest(reachable, fileDigests) {
1051
+ const parts = [...reachable]
1052
+ .filter((file) => fileDigests.has(file))
1053
+ .sort()
1054
+ .map((file) => `${file}:${fileDigests.get(file)}`);
1055
+ if (!parts.length) return null;
1056
+ return createHash('sha256').update(parts.join('\n')).digest('hex').slice(0, 32);
1057
+ }
1058
+
1059
+ function associateRouteCandidates(manifest, graph, fileDigests = new Map()) {
1044
1060
  const dependencies = {};
1045
1061
  for (const [file, values] of Object.entries(graph.dependencies)) {
1046
1062
  dependencies[stripSourceDirectory(file, graph.sourceDirectory)] = values.map(
@@ -1125,11 +1141,13 @@ function associateRouteCandidates(manifest, graph) {
1125
1141
  },
1126
1142
  });
1127
1143
  }
1144
+ const dependencyDigest = closureDigest(reachable, fileDigests);
1128
1145
  return {
1129
1146
  ...route,
1130
1147
  resolvedRoots: [...new Set(resolvedRoots)].sort(),
1131
1148
  evidenceIds,
1132
1149
  scenarioIds: [...scenarioIds].sort(),
1150
+ ...(dependencyDigest == null ? {} : { dependencyDigest }),
1133
1151
  };
1134
1152
  });
1135
1153
 
@@ -1174,6 +1192,7 @@ export async function scanStoryboardEnvironment(root, options = {}) {
1174
1192
  };
1175
1193
  const sources = [];
1176
1194
 
1195
+ const fileDigests = new Map();
1177
1196
  for (const file of files) {
1178
1197
  let stat;
1179
1198
  try {
@@ -1192,6 +1211,12 @@ export async function scanStoryboardEnvironment(root, options = {}) {
1192
1211
  source,
1193
1212
  sourcePath: normalizedRelativePath(sourceRoot, file),
1194
1213
  });
1214
+ // The scan already holds every file, so a content digest costs nothing extra
1215
+ // here and is what lets a frame survive a revision it does not depend on.
1216
+ fileDigests.set(
1217
+ normalizedRelativePath(sourceRoot, file),
1218
+ createHash('sha256').update(source).digest('hex').slice(0, 32),
1219
+ );
1195
1220
  }
1196
1221
 
1197
1222
  const sourceFiles = new Set(sources.map((entry) => entry.sourcePath));
@@ -1212,7 +1237,7 @@ export async function scanStoryboardEnvironment(root, options = {}) {
1212
1237
  const graph = await buildSourceGraph(path.resolve(root), {
1213
1238
  sourceDirectory: options.sourceDirectory ?? '.',
1214
1239
  });
1215
- return associateRouteCandidates(manifest, graph);
1240
+ return associateRouteCandidates(manifest, graph, fileDigests);
1216
1241
  }
1217
1242
 
1218
1243
  export function storyboardEnvironmentBootstrapSource() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pygmalionjs/pygmalion",
3
- "version": "0.2.23",
3
+ "version": "0.2.25",
4
4
  "description": "Code-backed DOM design sandbox and visual QA editor",
5
5
  "license": "UNLICENSED",
6
6
  "publishConfig": {