dependency-radar 0.5.0 → 0.6.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.
@@ -84,7 +84,20 @@ function buildOutdatedCommand(tool) {
84
84
  lockFiles: ["package-lock.json", "npm-shrinkwrap.json"],
85
85
  };
86
86
  }
87
- async function runPackageOutdated(projectPath, tempDir, tool) {
87
+ /**
88
+ * Run the package manager's `outdated` command for a project, normalize the output, and optionally persist results to disk.
89
+ *
90
+ * Executes the appropriate `outdated` command for `npm`, `pnpm`, or `yarn` in the repository lockfile directory (if found) and normalizes the tool-specific output into a consistent map of package names to `{ current, latest, wanted }`.
91
+ *
92
+ * @param projectPath - Path to the project root where the command should be executed if no lockfile directory is found
93
+ * @param tempDir - Directory used to write the tool-specific output file when persistence is enabled
94
+ * @param tool - Package manager to run (`"npm" | "pnpm" | "yarn"`)
95
+ * @param options - Optional settings
96
+ * @param options.persistToDisk - When `false`, do not write any output file to `tempDir`; defaults to `true`
97
+ * @returns A ToolResult containing `data` with the normalized outdated mapping on success, or `error` on failure. When persistence is enabled the result includes `file` with the path to the written JSON file.
98
+ */
99
+ async function runPackageOutdated(projectPath, tempDir, tool, options = {}) {
100
+ const persistToDisk = options.persistToDisk !== false;
88
101
  const targetFile = path_1.default.join(tempDir, `${tool}-outdated.json`);
89
102
  try {
90
103
  const { cmd, args, lockFiles } = buildOutdatedCommand(tool);
@@ -94,38 +107,46 @@ async function runPackageOutdated(projectPath, tempDir, tool) {
94
107
  const parsed = (0, utils_1.parseJsonOutput)(result.stdout);
95
108
  const normalized = normalizeOutdatedOutput(tool, parsed);
96
109
  if (normalized && typeof normalized === "object") {
97
- await (0, utils_1.writeJsonFile)(targetFile, normalized);
98
- return { ok: true, data: normalized, file: targetFile };
110
+ if (persistToDisk) {
111
+ await (0, utils_1.writeJsonFile)(targetFile, normalized);
112
+ }
113
+ return { ok: true, data: normalized, ...(persistToDisk ? { file: targetFile } : {}) };
99
114
  }
100
115
  if (tool === "yarn" && isYarnOutdatedUnsupported(result)) {
116
+ if (persistToDisk) {
117
+ await (0, utils_1.writeJsonFile)(targetFile, {
118
+ stdout: result.stdout,
119
+ stderr: result.stderr,
120
+ code: result.code,
121
+ });
122
+ }
123
+ return {
124
+ ok: false,
125
+ error: 'Yarn outdated is not available in this Yarn release (common on Yarn Berry).',
126
+ ...(persistToDisk ? { file: targetFile } : {}),
127
+ };
128
+ }
129
+ if (persistToDisk) {
101
130
  await (0, utils_1.writeJsonFile)(targetFile, {
102
131
  stdout: result.stdout,
103
132
  stderr: result.stderr,
104
133
  code: result.code,
105
134
  });
106
- return {
107
- ok: false,
108
- error: 'Yarn outdated is not available in this Yarn release (common on Yarn Berry).',
109
- file: targetFile,
110
- };
111
135
  }
112
- await (0, utils_1.writeJsonFile)(targetFile, {
113
- stdout: result.stdout,
114
- stderr: result.stderr,
115
- code: result.code,
116
- });
117
136
  return {
118
137
  ok: false,
119
138
  error: `Failed to parse ${tool} outdated output`,
120
- file: targetFile,
139
+ ...(persistToDisk ? { file: targetFile } : {}),
121
140
  };
122
141
  }
123
142
  catch (err) {
124
- await (0, utils_1.writeJsonFile)(targetFile, { error: String(err) });
143
+ if (persistToDisk) {
144
+ await (0, utils_1.writeJsonFile)(targetFile, { error: String(err) });
145
+ }
125
146
  return {
126
147
  ok: false,
127
148
  error: `${tool} outdated failed: ${String(err)}`,
128
- file: targetFile,
149
+ ...(persistToDisk ? { file: targetFile } : {}),
129
150
  };
130
151
  }
131
152
  }
package/dist/utils.js CHANGED
@@ -58,11 +58,46 @@ function getDependencyRadarVersion() {
58
58
  async function ensureDir(dir) {
59
59
  await promises_1.default.mkdir(dir, { recursive: true });
60
60
  }
61
+ /**
62
+ * Write JSON data to a file, creating parent directories as needed.
63
+ *
64
+ * Attempts to write a pretty-printed JSON representation of `data` to `filePath`. If pretty-printing fails due to an "Invalid string length" RangeError, falls back to a compact JSON representation.
65
+ *
66
+ * @param filePath - The path of the file to write
67
+ * @param data - The value to serialize to JSON (typically JSON-serializable)
68
+ * @throws Rethrows errors from JSON serialization (except handled "Invalid string length" for pretty-printing) and filesystem write operations
69
+ */
61
70
  async function writeJsonFile(filePath, data) {
62
71
  await ensureDir(path_1.default.dirname(filePath));
63
- const content = JSON.stringify(data, null, 2);
72
+ let content;
73
+ try {
74
+ content = JSON.stringify(data, null, 2);
75
+ }
76
+ catch (err) {
77
+ // Large lockfile-derived trees can overflow string length when pretty-printing.
78
+ if (!isInvalidStringLengthError(err))
79
+ throw err;
80
+ content = JSON.stringify(data);
81
+ }
64
82
  await promises_1.default.writeFile(filePath, content, 'utf8');
65
83
  }
84
+ /**
85
+ * Determines whether a value is a RangeError whose message indicates an "Invalid string length".
86
+ *
87
+ * @param error - The value to inspect
88
+ * @returns `true` if `error` is a `RangeError` with a message matching "Invalid string length" (case-insensitive), `false` otherwise.
89
+ */
90
+ function isInvalidStringLengthError(error) {
91
+ if (!(error instanceof RangeError))
92
+ return false;
93
+ return /Invalid string length/i.test(error.message || '');
94
+ }
95
+ /**
96
+ * Check whether a filesystem path exists and is accessible.
97
+ *
98
+ * @param target - The path to check
99
+ * @returns `true` if the path exists and is accessible, `false` otherwise
100
+ */
66
101
  async function pathExists(target) {
67
102
  try {
68
103
  await promises_1.default.access(target);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dependency-radar",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Local-first dependency analysis tool that generates a single HTML report showing risk, size, usage, and structure of your project's dependencies.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -60,12 +60,8 @@
60
60
  "terser": "^5.27.0",
61
61
  "ts-node": "^10.9.2",
62
62
  "typescript": "^5.4.3",
63
- "vite": "^5.4.0",
64
- "vitest": "^2.1.8"
63
+ "vite": "^7.3.1",
64
+ "vitest": "^4.0.18"
65
65
  },
66
- "packageManager": "pnpm@9.5.0+sha512.140036830124618d624a2187b50d04289d5a087f326c9edfc0ccd733d76c4f52c3a313d4fc148794a2a9d81553016004e6742e8cf850670268a7387fc220c903",
67
- "dependencies": {
68
- "@yarnpkg/lockfile": "^1.1.0",
69
- "yaml": "^2.8.2"
70
- }
66
+ "packageManager": "npm@10.9.2"
71
67
  }