dependency-radar 0.3.0 → 0.4.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.
@@ -5,11 +5,17 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.runNpmLs = runNpmLs;
7
7
  const path_1 = __importDefault(require("path"));
8
+ const fs_1 = __importDefault(require("fs"));
8
9
  const utils_1 = require("../utils");
10
+ const PNPM_DEPTH_ATTEMPTS = ['Infinity', '8', '4', '2', '1'];
11
+ const PNPM_MAX_OLD_SPACE_SIZE_MB = '8192';
9
12
  // Normalize package-manager-specific list output into a shared dependency tree.
10
- async function runNpmLs(projectPath, tempDir, tool = 'npm') {
13
+ async function runNpmLs(projectPath, tempDir, tool = 'npm', options = {}) {
11
14
  const targetFile = path_1.default.join(tempDir, `${tool}-ls.json`);
12
15
  try {
16
+ if (tool === 'pnpm') {
17
+ return await runPnpmLsWithFallback(projectPath, targetFile, options);
18
+ }
13
19
  const { args, normalize } = buildLsCommand(tool);
14
20
  const result = await (0, utils_1.runCommand)(tool, args, { cwd: projectPath });
15
21
  const parsed = parseJsonOutput(result.stdout);
@@ -19,9 +25,7 @@ async function runNpmLs(projectPath, tempDir, tool = 'npm') {
19
25
  return { ok: true, data: normalized, file: targetFile };
20
26
  }
21
27
  await (0, utils_1.writeJsonFile)(targetFile, { stdout: result.stdout, stderr: result.stderr, code: result.code });
22
- const error = result.code && result.code !== 0
23
- ? `${tool} ls exited with code ${result.code}`
24
- : `Failed to parse ${tool} ls output`;
28
+ const error = buildLsFailureMessage(tool, result.code, result.stderr);
25
29
  return { ok: false, error, file: targetFile };
26
30
  }
27
31
  catch (err) {
@@ -30,12 +34,6 @@ async function runNpmLs(projectPath, tempDir, tool = 'npm') {
30
34
  }
31
35
  }
32
36
  function buildLsCommand(tool) {
33
- if (tool === 'pnpm') {
34
- return {
35
- args: ['list', '--json', '--depth', 'Infinity'],
36
- normalize: normalizePnpmTree
37
- };
38
- }
39
37
  if (tool === 'yarn') {
40
38
  return {
41
39
  args: ['list', '--json', '--depth', 'Infinity'],
@@ -47,6 +45,115 @@ function buildLsCommand(tool) {
47
45
  normalize: normalizeNpmTree
48
46
  };
49
47
  }
48
+ async function runPnpmLsWithFallback(projectPath, targetFile, options) {
49
+ const installState = createPnpmInstallState(projectPath);
50
+ const attempts = [];
51
+ const env = {
52
+ NODE_OPTIONS: ensureNodeMaxOldSpaceSize(process.env.NODE_OPTIONS, PNPM_MAX_OLD_SPACE_SIZE_MB)
53
+ };
54
+ for (let index = 0; index < PNPM_DEPTH_ATTEMPTS.length; index++) {
55
+ const depth = PNPM_DEPTH_ATTEMPTS[index];
56
+ const result = await (0, utils_1.runCommand)('pnpm', ['list', '--json', '--depth', depth], {
57
+ cwd: projectPath,
58
+ env
59
+ });
60
+ const parsed = parseJsonOutput(result.stdout);
61
+ const normalized = normalizePnpmTree(parsed, installState);
62
+ const outOfMemory = isOutOfMemoryError(result.stderr);
63
+ attempts.push({
64
+ depth,
65
+ code: result.code,
66
+ stdoutBytes: Buffer.byteLength(result.stdout || '', 'utf8'),
67
+ stderrPreview: trimText(result.stderr, 1200),
68
+ outOfMemory
69
+ });
70
+ if (normalized) {
71
+ if (index > 0) {
72
+ progress(options, `✔ PNPM ls recovered for workspace: ${formatContextLabel(options)} (depth=${depth})`);
73
+ }
74
+ await (0, utils_1.writeJsonFile)(targetFile, normalized);
75
+ return { ok: true, data: normalized, file: targetFile };
76
+ }
77
+ const reason = describeAttemptFailure(result.code, result.stderr);
78
+ progress(options, `✖ Failed pnpm ls for workspace: ${formatContextLabel(options)} (depth=${depth}; ${reason})`);
79
+ const nextDepth = PNPM_DEPTH_ATTEMPTS[index + 1];
80
+ if (nextDepth) {
81
+ progress(options, `✔ Retrying pnpm ls for workspace: ${formatContextLabel(options)} (depth=${nextDepth})`);
82
+ }
83
+ }
84
+ await (0, utils_1.writeJsonFile)(targetFile, {
85
+ error: 'pnpm ls retries exhausted',
86
+ nodeOptions: env.NODE_OPTIONS,
87
+ attempts
88
+ });
89
+ const sawOom = attempts.some((attempt) => attempt.outOfMemory);
90
+ const lastAttempt = attempts[attempts.length - 1];
91
+ if (sawOom) {
92
+ return {
93
+ ok: false,
94
+ error: 'pnpm ls ran out of memory while building the dependency tree (retried with lower depths).',
95
+ file: targetFile
96
+ };
97
+ }
98
+ const suffix = lastAttempt && typeof lastAttempt.code === 'number'
99
+ ? ` Last exit code: ${lastAttempt.code}.`
100
+ : '';
101
+ return {
102
+ ok: false,
103
+ error: `Failed to parse pnpm ls output after retries.${suffix}`,
104
+ file: targetFile
105
+ };
106
+ }
107
+ function progress(options, line) {
108
+ if (typeof options.onProgress === 'function') {
109
+ options.onProgress(line);
110
+ }
111
+ }
112
+ function formatContextLabel(options) {
113
+ var _a;
114
+ const label = (_a = options.contextLabel) === null || _a === void 0 ? void 0 : _a.trim();
115
+ return label || '(unknown package)';
116
+ }
117
+ function describeAttemptFailure(code, stderr) {
118
+ if (isOutOfMemoryError(stderr))
119
+ return 'out of memory';
120
+ if (typeof code === 'number' && code !== 0)
121
+ return `exit code ${code}`;
122
+ if (code === null && stderr && stderr.trim())
123
+ return 'terminated before completion';
124
+ return 'no parseable JSON output';
125
+ }
126
+ function ensureNodeMaxOldSpaceSize(existing, megabytes) {
127
+ const token = '--max-old-space-size=';
128
+ if (typeof existing === 'string' && existing.includes(token)) {
129
+ return existing;
130
+ }
131
+ const option = `${token}${megabytes}`;
132
+ return existing && existing.trim() ? `${existing.trim()} ${option}` : option;
133
+ }
134
+ function isOutOfMemoryError(stderr) {
135
+ return /heap out of memory|Reached heap limit|Allocation failed - JavaScript heap out of memory/i.test(stderr || '');
136
+ }
137
+ function trimText(text, maxChars) {
138
+ if (!text)
139
+ return '';
140
+ const trimmed = text.trim();
141
+ if (trimmed.length <= maxChars)
142
+ return trimmed;
143
+ return trimmed.slice(trimmed.length - maxChars);
144
+ }
145
+ function buildLsFailureMessage(tool, code, stderr) {
146
+ if (isOutOfMemoryError(stderr)) {
147
+ return `${tool} ls ran out of memory while building the dependency tree`;
148
+ }
149
+ if (typeof code === 'number' && code !== 0) {
150
+ return `${tool} ls exited with code ${code}`;
151
+ }
152
+ if (code === null && stderr && stderr.trim()) {
153
+ return `${tool} ls failed before completion`;
154
+ }
155
+ return `Failed to parse ${tool} ls output`;
156
+ }
50
157
  function parseJsonOutput(raw) {
51
158
  if (!raw)
52
159
  return undefined;
@@ -106,15 +213,23 @@ function normalizeNpmNode(name, node) {
106
213
  }
107
214
  return out;
108
215
  }
109
- function normalizePnpmTree(data) {
110
- const roots = Array.isArray(data) ? data : [data];
111
- const root = roots.find((entry) => entry && typeof entry === 'object');
216
+ function normalizePnpmTree(data, installState) {
217
+ const roots = (Array.isArray(data) ? data : [data]).filter((entry) => entry && typeof entry === 'object');
218
+ const root = roots.find((entry) => !isPnpmErrorPayload(entry));
112
219
  if (!root || typeof root !== 'object')
113
220
  return undefined;
114
- const dependencies = collectPnpmDependencyMap(root);
221
+ const dependencies = collectPnpmDependencyMap(root, installState);
115
222
  return { dependencies };
116
223
  }
117
- function collectPnpmDependencyMap(node) {
224
+ function isPnpmErrorPayload(node) {
225
+ if (!node || typeof node !== 'object')
226
+ return false;
227
+ if (!('error' in node))
228
+ return false;
229
+ const error = node.error;
230
+ return typeof error === 'string' || (error !== null && typeof error === 'object');
231
+ }
232
+ function collectPnpmDependencyMap(node, installState) {
118
233
  const out = {};
119
234
  const groups = ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies'];
120
235
  for (const group of groups) {
@@ -128,7 +243,7 @@ function collectPnpmDependencyMap(node) {
128
243
  const name = typeof entry.name === 'string' ? entry.name : undefined;
129
244
  if (!name)
130
245
  continue;
131
- const normalized = normalizePnpmNode(name, entry);
246
+ const normalized = normalizePnpmNode(name, entry, installState);
132
247
  if (normalized)
133
248
  out[name] = normalized;
134
249
  }
@@ -138,7 +253,7 @@ function collectPnpmDependencyMap(node) {
138
253
  for (const [name, entry] of Object.entries(value)) {
139
254
  if (!entry || typeof entry !== 'object')
140
255
  continue;
141
- const normalized = normalizePnpmNode(name, entry);
256
+ const normalized = normalizePnpmNode(name, entry, installState);
142
257
  if (normalized)
143
258
  out[name] = normalized;
144
259
  }
@@ -148,19 +263,21 @@ function collectPnpmDependencyMap(node) {
148
263
  return out;
149
264
  if ((node === null || node === void 0 ? void 0 : node.dependencies) && typeof node.dependencies === 'object') {
150
265
  for (const [name, entry] of Object.entries(node.dependencies)) {
151
- const normalized = normalizePnpmNode(name, entry);
266
+ const normalized = normalizePnpmNode(name, entry, installState);
152
267
  if (normalized)
153
268
  out[name] = normalized;
154
269
  }
155
270
  }
156
271
  return out;
157
272
  }
158
- function normalizePnpmNode(name, node) {
273
+ function normalizePnpmNode(name, node, installState) {
159
274
  const version = typeof (node === null || node === void 0 ? void 0 : node.version) === 'string' ? node.version.trim() : '';
160
275
  if (!version || version === 'unknown' || version === 'missing' || version === 'invalid')
161
276
  return undefined;
277
+ if (!isPnpmPackageInstalled(name, version, installState))
278
+ return undefined;
162
279
  const out = { name, version, dependencies: {} };
163
- const childMap = collectPnpmDependencyMap(node);
280
+ const childMap = collectPnpmDependencyMap(node, installState);
164
281
  if (Object.keys(childMap).length > 0) {
165
282
  out.dependencies = childMap;
166
283
  }
@@ -171,6 +288,103 @@ function normalizePnpmNode(name, node) {
171
288
  out.dev = Boolean(node.dev);
172
289
  return out;
173
290
  }
291
+ function createPnpmInstallState(projectPath) {
292
+ const nodeModulesRoots = findNodeModulesRoots(projectPath);
293
+ const virtualStoreEntries = new Set();
294
+ for (const root of nodeModulesRoots) {
295
+ const virtualStoreDir = path_1.default.join(root, '.pnpm');
296
+ if (!safePathExists(virtualStoreDir))
297
+ continue;
298
+ for (const entry of safeReadDirNames(virtualStoreDir)) {
299
+ virtualStoreEntries.add(entry);
300
+ }
301
+ }
302
+ return {
303
+ enabled: virtualStoreEntries.size > 0 || nodeModulesRoots.length > 0,
304
+ virtualStoreEntries,
305
+ nodeModulesRoots,
306
+ installedCache: new Map()
307
+ };
308
+ }
309
+ function findNodeModulesRoots(startPath) {
310
+ const roots = [];
311
+ let current = path_1.default.resolve(startPath);
312
+ while (true) {
313
+ const candidate = path_1.default.join(current, 'node_modules');
314
+ if (safePathExists(candidate)) {
315
+ roots.push(candidate);
316
+ }
317
+ const parent = path_1.default.dirname(current);
318
+ if (parent === current)
319
+ break;
320
+ current = parent;
321
+ }
322
+ return roots;
323
+ }
324
+ function isPnpmPackageInstalled(name, version, installState) {
325
+ if (!installState.enabled)
326
+ return true;
327
+ const cacheKey = `${name}@${version}`;
328
+ const cached = installState.installedCache.get(cacheKey);
329
+ if (cached !== undefined)
330
+ return cached;
331
+ const normalizedName = normalizeScopedPackageNameForPnpmStore(name);
332
+ const storePrefix = `${normalizedName}@${version}`;
333
+ for (const entry of installState.virtualStoreEntries) {
334
+ if (entry === storePrefix || entry.startsWith(`${storePrefix}_`) || entry.startsWith(`${storePrefix}(`)) {
335
+ installState.installedCache.set(cacheKey, true);
336
+ return true;
337
+ }
338
+ }
339
+ // Workspace links may resolve outside the virtual store, but still exist in node_modules.
340
+ for (const nodeModulesRoot of installState.nodeModulesRoots) {
341
+ const packageDir = path_1.default.join(nodeModulesRoot, ...name.split('/'));
342
+ if (safePathExists(packageDir) && packageDirectoryMatchesVersion(packageDir, version)) {
343
+ installState.installedCache.set(cacheKey, true);
344
+ return true;
345
+ }
346
+ }
347
+ installState.installedCache.set(cacheKey, false);
348
+ return false;
349
+ }
350
+ function normalizeScopedPackageNameForPnpmStore(name) {
351
+ if (!name.startsWith('@'))
352
+ return name;
353
+ const slashIndex = name.indexOf('/');
354
+ if (slashIndex <= 0)
355
+ return name;
356
+ return `${name.slice(0, slashIndex)}+${name.slice(slashIndex + 1)}`;
357
+ }
358
+ function safePathExists(targetPath) {
359
+ try {
360
+ return fs_1.default.existsSync(targetPath);
361
+ }
362
+ catch {
363
+ return false;
364
+ }
365
+ }
366
+ function safeReadDirNames(dirPath) {
367
+ try {
368
+ return fs_1.default.readdirSync(dirPath);
369
+ }
370
+ catch {
371
+ return [];
372
+ }
373
+ }
374
+ function packageDirectoryMatchesVersion(packageDir, expectedVersion) {
375
+ const pkgJsonPath = path_1.default.join(packageDir, 'package.json');
376
+ if (!safePathExists(pkgJsonPath))
377
+ return true;
378
+ try {
379
+ const raw = fs_1.default.readFileSync(pkgJsonPath, 'utf8');
380
+ const parsed = JSON.parse(raw);
381
+ const version = typeof (parsed === null || parsed === void 0 ? void 0 : parsed.version) === 'string' ? parsed.version.trim() : '';
382
+ return !version || version === expectedVersion;
383
+ }
384
+ catch {
385
+ return true;
386
+ }
387
+ }
174
388
  function normalizeYarnTree(data) {
175
389
  const treePayload = resolveYarnTreePayload(data);
176
390
  if (!treePayload || !Array.isArray(treePayload.trees))
@@ -11,24 +11,7 @@ function normalizeOutdatedOutput(tool, data) {
11
11
  return undefined;
12
12
  if (typeof data === "object" && !Array.isArray(data) && !data.type)
13
13
  return data;
14
- if (Array.isArray(data)) {
15
- // pnpm often returns arrays of rows
16
- const out = {};
17
- for (const entry of data) {
18
- if (!entry || typeof entry !== "object")
19
- continue;
20
- const name = entry.name || entry.packageName;
21
- if (typeof name !== "string" || !name.trim())
22
- continue;
23
- out[name] = {
24
- current: entry.current || entry.currentVersion || entry.from,
25
- latest: entry.latest || entry.latestVersion || entry.to,
26
- wanted: entry.wanted,
27
- };
28
- }
29
- return Object.keys(out).length > 0 ? out : undefined;
30
- }
31
- // Yarn JSONL table output
14
+ // Yarn JSONL table output (classic) must be checked before generic array parsing.
32
15
  const entries = Array.isArray(data) ? data : [data];
33
16
  const tableEntry = entries.find((e) => { var _a; return e && typeof e === "object" && (e.type === "table" || ((_a = e.data) === null || _a === void 0 ? void 0 : _a.body)); });
34
17
  const table = (tableEntry === null || tableEntry === void 0 ? void 0 : tableEntry.data) || tableEntry;
@@ -55,10 +38,31 @@ function normalizeOutdatedOutput(tool, data) {
55
38
  }
56
39
  return Object.keys(out).length > 0 ? out : undefined;
57
40
  }
41
+ if (Array.isArray(data)) {
42
+ // pnpm often returns arrays of rows
43
+ const out = {};
44
+ for (const entry of data) {
45
+ if (!entry || typeof entry !== "object")
46
+ continue;
47
+ const name = entry.name || entry.packageName;
48
+ if (typeof name !== "string" || !name.trim())
49
+ continue;
50
+ out[name] = {
51
+ current: entry.current || entry.currentVersion || entry.from,
52
+ latest: entry.latest || entry.latestVersion || entry.to,
53
+ wanted: entry.wanted,
54
+ };
55
+ }
56
+ return Object.keys(out).length > 0 ? out : undefined;
57
+ }
58
58
  if (tool === "pnpm" && data.outdated)
59
59
  return data.outdated;
60
60
  return undefined;
61
61
  }
62
+ function isYarnOutdatedUnsupported(result) {
63
+ const output = `${result.stdout}\n${result.stderr}`;
64
+ return /Couldn't find a script named "outdated"/i.test(output);
65
+ }
62
66
  function buildOutdatedCommand(tool) {
63
67
  if (tool === "pnpm") {
64
68
  return {
@@ -93,6 +97,18 @@ async function runPackageOutdated(projectPath, tempDir, tool) {
93
97
  await (0, utils_1.writeJsonFile)(targetFile, normalized);
94
98
  return { ok: true, data: normalized, file: targetFile };
95
99
  }
100
+ if (tool === "yarn" && isYarnOutdatedUnsupported(result)) {
101
+ await (0, utils_1.writeJsonFile)(targetFile, {
102
+ stdout: result.stdout,
103
+ stderr: result.stderr,
104
+ code: result.code,
105
+ });
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
+ }
96
112
  await (0, utils_1.writeJsonFile)(targetFile, {
97
113
  stdout: result.stdout,
98
114
  stderr: result.stderr,
package/dist/utils.js CHANGED
@@ -27,7 +27,8 @@ function runCommand(command, args, options = {}) {
27
27
  return new Promise((resolve, reject) => {
28
28
  const child = (0, child_process_1.spawn)(command, args, {
29
29
  cwd: options.cwd,
30
- shell: false
30
+ shell: false,
31
+ env: options.env ? { ...process.env, ...options.env } : process.env
31
32
  });
32
33
  const stdoutChunks = [];
33
34
  const stderrChunks = [];
@@ -148,7 +149,7 @@ async function getPnpmStoreIndex(pnpmDir) {
148
149
  const entries = await getPnpmStoreEntries(pnpmDir);
149
150
  const index = new Map();
150
151
  for (const entry of entries) {
151
- const prefix = entry.split('(')[0];
152
+ const prefix = extractPnpmStoreEntryPrefix(entry);
152
153
  if (!prefix)
153
154
  continue;
154
155
  if (!index.has(prefix))
@@ -158,6 +159,20 @@ async function getPnpmStoreIndex(pnpmDir) {
158
159
  pnpmStoreIndexCache.set(pnpmDir, index);
159
160
  return index;
160
161
  }
162
+ function extractPnpmStoreEntryPrefix(entry) {
163
+ const atIndex = entry.startsWith('@') ? entry.indexOf('@', 1) : entry.indexOf('@');
164
+ if (atIndex <= 0)
165
+ return entry.split('(')[0];
166
+ const versionAndSuffix = entry.slice(atIndex + 1);
167
+ const underscoreIndex = versionAndSuffix.indexOf('_');
168
+ const parenIndex = versionAndSuffix.indexOf('(');
169
+ let cutoff = versionAndSuffix.length;
170
+ if (underscoreIndex >= 0)
171
+ cutoff = Math.min(cutoff, underscoreIndex);
172
+ if (parenIndex >= 0)
173
+ cutoff = Math.min(cutoff, parenIndex);
174
+ return entry.slice(0, atIndex + 1 + cutoff);
175
+ }
161
176
  async function resolvePnpmPackageJsonPath(pkgName, version, resolvePaths) {
162
177
  if (!version || version.startsWith('link:') || version.startsWith('workspace:') || version.startsWith('file:')) {
163
178
  return undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dependency-radar",
3
- "version": "0.3.0",
3
+ "version": "0.4.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": {
@@ -17,26 +17,16 @@
17
17
  "build": "npm run build:spdx && npm run build:report && tsc",
18
18
  "dev": "ts-node src/cli.ts scan",
19
19
  "scan": "node dist/cli.js scan",
20
- "fixtures:install:npm": "cd test-fixtures/npm-basic && npm install",
21
- "fixtures:install:npm-heavy": "cd test-fixtures/npm-heavy && npm install --force",
22
- "fixtures:install:pnpm": "cd test-fixtures/pnpm-workspace && pnpm install",
23
- "fixtures:install:pnpm-hoisted": "cd test-fixtures/pnpm-workspace-hoisted && pnpm install",
24
- "fixtures:install:yarn": "cd test-fixtures/yarn-workspace && yarn install",
25
- "fixtures:install:yarn-berry": "cd test-fixtures/yarn-berry-workspace && yarn install",
26
- "fixtures:install:optional": "cd test-fixtures/optional-deps && npm install",
27
- "fixtures:scan:npm": "npm run dev -- --project test-fixtures/npm-basic --out test-fixtures/npm-basic/dependency-radar.html --json --keep-temp",
28
- "fixtures:scan:npm-heavy": "npm run dev -- --project test-fixtures/npm-heavy --out test-fixtures/npm-heavy/dependency-radar.html --json --keep-temp",
29
- "fixtures:scan:pnpm": "npm run dev -- --project test-fixtures/pnpm-workspace --out test-fixtures/pnpm-workspace/dependency-radar.html --json --keep-temp",
30
- "fixtures:scan:pnpm-hoisted": "npm run dev -- --project test-fixtures/pnpm-workspace-hoisted --out test-fixtures/pnpm-workspace-hoisted/dependency-radar.html --json --keep-temp",
31
- "fixtures:scan:yarn": "npm run dev -- --project test-fixtures/yarn-workspace --out test-fixtures/yarn-workspace/dependency-radar.html --json --keep-temp",
32
- "fixtures:scan:yarn-berry": "npm run dev -- --project test-fixtures/yarn-berry-workspace --out test-fixtures/yarn-berry-workspace/dependency-radar.html --json --keep-temp",
33
- "fixtures:scan:optional": "npm run dev -- --project test-fixtures/optional-deps --out test-fixtures/optional-deps/dependency-radar.html --json --keep-temp",
34
- "fixtures:scan:no-node-modules": "npm run dev -- --project test-fixtures/no-node-modules --out test-fixtures/no-node-modules/dependency-radar.html --json --keep-temp",
35
- "fixtures:install": "npm run fixtures:install:npm && npm run fixtures:install:npm-heavy && npm run fixtures:install:pnpm && npm run fixtures:install:yarn",
36
- "fixtures:scan": "npm run fixtures:scan:npm && npm run fixtures:scan:npm-heavy && npm run fixtures:scan:pnpm && npm run fixtures:scan:pnpm-hoisted && npm run fixtures:scan:yarn && npm run fixtures:scan:yarn-berry && npm run fixtures:scan:optional",
37
- "fixtures:install:all": "npm run fixtures:install:npm && npm run fixtures:install:npm-heavy && npm run fixtures:install:pnpm && npm run fixtures:install:pnpm-hoisted && npm run fixtures:install:yarn && npm run fixtures:install:yarn-berry && npm run fixtures:install:optional",
38
- "prepublishOnly": "npm run build",
39
- "test": "echo \"Error: no test specified\" && exit 1"
20
+ "test": "npm run test:unit",
21
+ "test:unit": "vitest run",
22
+ "test:unit:watch": "vitest",
23
+ "test:fixtures": "npm --prefix test-fixtures run test",
24
+ "test:fixtures:online": "npm --prefix test-fixtures run test:online",
25
+ "test:fixtures:all": "npm --prefix test-fixtures run test:all",
26
+ "test:pack": "npm pack --dry-run",
27
+ "test:release:ok": "node -e \"console.log('\\n✅ test:release passed')\"",
28
+ "test:release": "npm run build && npm run test:unit && npm run test:fixtures:all && npm run test:pack && npm run test:release:ok",
29
+ "prepublishOnly": "npm run build"
40
30
  },
41
31
  "author": " ",
42
32
  "license": "MIT",
@@ -70,7 +60,8 @@
70
60
  "terser": "^5.27.0",
71
61
  "ts-node": "^10.9.2",
72
62
  "typescript": "^5.4.3",
73
- "vite": "^5.4.0"
63
+ "vite": "^5.4.0",
64
+ "vitest": "^2.1.8"
74
65
  },
75
66
  "packageManager": "pnpm@9.5.0+sha512.140036830124618d624a2187b50d04289d5a087f326c9edfc0ccd733d76c4f52c3a313d4fc148794a2a9d81553016004e6742e8cf850670268a7387fc220c903"
76
67
  }