@chappibunny/repolens 1.15.5 → 1.16.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.
@@ -1,3 +1,5 @@
1
+ import { replaceBracketParams } from "../utils/route-params.js";
2
+
1
3
  function isRouteFile(file) {
2
4
  return (
3
5
  file.includes("/pages/api/") ||
@@ -15,10 +17,10 @@ function routePathFromFile(file) {
15
17
  const appIndex = file.indexOf("/app/");
16
18
  const relative = file.slice(appIndex + 5);
17
19
 
18
- return "/" + relative
20
+ const cleaned = relative
19
21
  .replace(/\/page\.(ts|tsx|js|jsx)$/, "")
20
- .replace(/\/route\.(ts|tsx|js|jsx)$/, "")
21
- .replace(/\[(.*?)\]/g, ":$1");
22
+ .replace(/\/route\.(ts|tsx|js|jsx)$/, "");
23
+ return `/${replaceBracketParams(cleaned)}`;
22
24
  }
23
25
 
24
26
  if (file.includes("/pages/api/")) {
@@ -1,13 +1,12 @@
1
+ import { replaceLeadingAppGroup, replaceSlashGroups } from "../utils/route-params.js";
2
+
1
3
  function sanitizeNodeId(value) {
2
4
  return value.replace(/[^a-zA-Z0-9_]/g, "_");
3
5
  }
4
6
 
5
7
  function normalizeLabel(value) {
6
- return value
7
- .replace(/^src\//, "")
8
- .replace(/^app\/\((.*?)\)/, "app/$1")
9
- .replace(/\/\((.*?)\)/g, "/$1")
10
- .replace(/\/+/g, "/");
8
+ const withoutSrc = value.replace(/^src\//, "");
9
+ return replaceSlashGroups(replaceLeadingAppGroup(withoutSrc)).replace(/\/+/g, "/");
11
10
  }
12
11
 
13
12
  function buildModuleGraph(modules, depGraph) {
@@ -96,9 +95,11 @@ function buildModuleGraph(modules, depGraph) {
96
95
  */
97
96
  function findModuleForFile(fileKey, modules) {
98
97
  const normalized = fileKey.replace(/\\/g, "/");
98
+ const flat = modules.find((mod) => mod.flat) || modules.find((mod) => mod.key === "root");
99
99
  let bestMatch = null;
100
100
  for (const mod of modules) {
101
101
  const modKey = mod.key;
102
+ if (flat && modKey === flat.key) continue;
102
103
  // Direct match (with or without extension)
103
104
  if (normalized === modKey || normalized === modKey.replace(/\.[^/.]+$/, "")) {
104
105
  return modKey;
@@ -110,6 +111,7 @@ function findModuleForFile(fileKey, modules) {
110
111
  }
111
112
  }
112
113
  }
114
+ if (!bestMatch && flat && !normalized.includes("/")) return flat.key;
113
115
  return bestMatch;
114
116
  }
115
117
 
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Linear replacements for route and label text.
3
+ *
4
+ * A lazy `.*?` between two markers is polynomial: on a long string with no
5
+ * closer, each start position scans to the end (CodeQL js/polynomial-redos).
6
+ * These walks stop at the first closer and never re-scan, which matches the
7
+ * old `String.prototype.replace` result.
8
+ */
9
+
10
+ function replaceMarked(input, open, close, mapInner) {
11
+ const value = String(input ?? "");
12
+ let out = "";
13
+ let cursor = 0;
14
+ while (cursor < value.length) {
15
+ const start = value.indexOf(open, cursor);
16
+ if (start === -1) {
17
+ out += value.slice(cursor);
18
+ break;
19
+ }
20
+ const end = value.indexOf(close, start + open.length);
21
+ if (end === -1) {
22
+ out += value.slice(cursor);
23
+ break;
24
+ }
25
+ out += value.slice(cursor, start);
26
+ out += mapInner(value.slice(start + open.length, end));
27
+ cursor = end + close.length;
28
+ }
29
+ return out;
30
+ }
31
+
32
+ /** `[param]` → `:param`, including the first `]` only. Global, non-overlapping. */
33
+ export function replaceBracketParams(value) {
34
+ return replaceMarked(value, "[", "]", (inner) => `:${inner}`);
35
+ }
36
+
37
+ /** `/(group)` → `/group`. Used for Next.js route groups in diagram labels. */
38
+ export function replaceSlashGroups(value) {
39
+ return replaceMarked(value, "/(", ")", (inner) => `/${inner}`);
40
+ }
41
+
42
+ /** A leading `app/(group)` → `app/group`. Later groups use {@link replaceSlashGroups}. */
43
+ export function replaceLeadingAppGroup(value) {
44
+ const input = String(value ?? "");
45
+ const prefix = "app/(";
46
+ if (!input.startsWith(prefix)) return input;
47
+ const end = input.indexOf(")", prefix.length);
48
+ if (end === -1) return input;
49
+ return `app/${input.slice(prefix.length, end)}${input.slice(end + 1)}`;
50
+ }
package/src/utils/yaml.js CHANGED
@@ -1,14 +1,15 @@
1
- import yaml from "js-yaml";
1
+ import { CORE_SCHEMA, load, mergeTag } from "js-yaml";
2
2
 
3
3
  const MAX_YAML_CHARS = 1_000_000;
4
4
  const MAX_YAML_ALIASES = 2000;
5
5
 
6
6
  /**
7
7
  * Parse YAML without executing custom tags.
8
- * js-yaml 4 `load` is already safe (no JS types). This adds a size cap and
9
- * an alias cap so a config cannot expand into a billion-laughs document.
10
- * The js-yaml 4 default schema is unchanged (`true`/`false` are booleans;
11
- * the YAML 1.1 spellings `yes` and `no` stay strings).
8
+ * js-yaml 5 `load` uses the YAML 1.2 Core schema (`true`/`false` are booleans;
9
+ * the YAML 1.1 spellings `yes` and `no` stay strings). The merge key (`<<`)
10
+ * is added back so configs that relied on js-yaml 4's merge still parse.
11
+ * A size cap and `maxAliases` stop a billion-laughs document.
12
+ * An empty file returns `undefined`, matching the previous loader.
12
13
  */
13
14
  export function loadYaml(raw) {
14
15
  if (typeof raw !== "string") {
@@ -17,11 +18,15 @@ export function loadYaml(raw) {
17
18
  if (raw.length > MAX_YAML_CHARS) {
18
19
  throw new Error(`YAML document exceeds ${MAX_YAML_CHARS} characters`);
19
20
  }
21
+ if (raw.trim() === "") return undefined;
20
22
 
21
23
  const aliasUses = raw.match(/(^|[\s\[\{,])\*[A-Za-z_][\w-]*/g);
22
24
  if (aliasUses && aliasUses.length > MAX_YAML_ALIASES) {
23
25
  throw new Error(`YAML document has too many aliases (${aliasUses.length})`);
24
26
  }
25
27
 
26
- return yaml.load(raw);
28
+ return load(raw, {
29
+ schema: CORE_SCHEMA.withTags(mergeTag),
30
+ maxAliases: MAX_YAML_ALIASES,
31
+ });
27
32
  }