@openrewrite/rewrite 8.68.0-20251202-044649 → 8.68.0-20251202-154952

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.
Files changed (45) hide show
  1. package/dist/javascript/assertions.d.ts +1 -0
  2. package/dist/javascript/assertions.d.ts.map +1 -1
  3. package/dist/javascript/assertions.js +82 -11
  4. package/dist/javascript/assertions.js.map +1 -1
  5. package/dist/javascript/dependency-workspace.d.ts +46 -5
  6. package/dist/javascript/dependency-workspace.d.ts.map +1 -1
  7. package/dist/javascript/dependency-workspace.js +70 -35
  8. package/dist/javascript/dependency-workspace.js.map +1 -1
  9. package/dist/javascript/index.d.ts +2 -0
  10. package/dist/javascript/index.d.ts.map +1 -1
  11. package/dist/javascript/index.js +2 -0
  12. package/dist/javascript/index.js.map +1 -1
  13. package/dist/javascript/node-resolution-result.d.ts +204 -0
  14. package/dist/javascript/node-resolution-result.d.ts.map +1 -0
  15. package/dist/javascript/node-resolution-result.js +723 -0
  16. package/dist/javascript/node-resolution-result.js.map +1 -0
  17. package/dist/javascript/package-json-parser.d.ts +143 -0
  18. package/dist/javascript/package-json-parser.d.ts.map +1 -0
  19. package/dist/javascript/package-json-parser.js +773 -0
  20. package/dist/javascript/package-json-parser.js.map +1 -0
  21. package/dist/javascript/templating/engine.js +1 -1
  22. package/dist/javascript/templating/engine.js.map +1 -1
  23. package/dist/json/parser.js +10 -1
  24. package/dist/json/parser.js.map +1 -1
  25. package/dist/json/tree.d.ts +1 -1
  26. package/dist/json/tree.js +1 -1
  27. package/dist/json/tree.js.map +1 -1
  28. package/dist/parser.d.ts +1 -1
  29. package/dist/parser.d.ts.map +1 -1
  30. package/dist/rpc/request/parse.d.ts +4 -0
  31. package/dist/rpc/request/parse.d.ts.map +1 -1
  32. package/dist/rpc/request/parse.js +17 -1
  33. package/dist/rpc/request/parse.js.map +1 -1
  34. package/dist/version.txt +1 -1
  35. package/package.json +5 -2
  36. package/src/javascript/assertions.ts +73 -15
  37. package/src/javascript/dependency-workspace.ts +124 -46
  38. package/src/javascript/index.ts +2 -0
  39. package/src/javascript/node-resolution-result.ts +905 -0
  40. package/src/javascript/package-json-parser.ts +845 -0
  41. package/src/javascript/templating/engine.ts +1 -1
  42. package/src/json/parser.ts +18 -1
  43. package/src/json/tree.ts +1 -1
  44. package/src/parser.ts +1 -1
  45. package/src/rpc/request/parse.ts +20 -2
@@ -0,0 +1,773 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
36
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
37
+ return new (P || (P = Promise))(function (resolve, reject) {
38
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
39
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
40
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
41
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
42
+ });
43
+ };
44
+ var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); }
45
+ var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
46
+ if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
47
+ var g = generator.apply(thisArg, _arguments || []), i, q = [];
48
+ return i = Object.create((typeof AsyncIterator === "function" ? AsyncIterator : Object).prototype), verb("next"), verb("throw"), verb("return", awaitReturn), i[Symbol.asyncIterator] = function () { return this; }, i;
49
+ function awaitReturn(f) { return function (v) { return Promise.resolve(v).then(f, reject); }; }
50
+ function verb(n, f) { if (g[n]) { i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; if (f) i[n] = f(i[n]); } }
51
+ function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
52
+ function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
53
+ function fulfill(value) { resume("next", value); }
54
+ function reject(value) { resume("throw", value); }
55
+ function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
56
+ };
57
+ Object.defineProperty(exports, "__esModule", { value: true });
58
+ exports.PackageJsonParser = void 0;
59
+ /*
60
+ * Copyright 2025 the original author or authors.
61
+ * <p>
62
+ * Licensed under the Moderne Source Available License (the "License");
63
+ * you may not use this file except in compliance with the License.
64
+ * You may obtain a copy of the License at
65
+ * <p>
66
+ * https://docs.moderne.io/licensing/moderne-source-available-license
67
+ * <p>
68
+ * Unless required by applicable law or agreed to in writing, software
69
+ * distributed under the License is distributed on an "AS IS" BASIS,
70
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
71
+ * See the License for the specific language governing permissions and
72
+ * limitations under the License.
73
+ */
74
+ const parser_1 = require("../parser");
75
+ const json_1 = require("../json");
76
+ const node_resolution_result_1 = require("./node-resolution-result");
77
+ const fs = __importStar(require("fs"));
78
+ const fsp = __importStar(require("fs/promises"));
79
+ const path = __importStar(require("path"));
80
+ const YAML = __importStar(require("yaml"));
81
+ const child_process_1 = require("child_process");
82
+ /**
83
+ * A parser for package.json files that wraps the JsonParser.
84
+ *
85
+ * Similar to how MavenParser wraps XmlParser in Java, this parser:
86
+ * - Parses package.json files as JSON documents
87
+ * - Attaches NodeResolutionResult markers with dependency information
88
+ * - Optionally reads corresponding lock files (package-lock.json, yarn.lock, etc.)
89
+ * to provide resolved dependency versions
90
+ */
91
+ class PackageJsonParser extends parser_1.Parser {
92
+ constructor(options = {}) {
93
+ var _a;
94
+ super(options);
95
+ this.jsonParser = new json_1.JsonParser(options);
96
+ this.skipDependencyResolution = (_a = options.skipDependencyResolution) !== null && _a !== void 0 ? _a : false;
97
+ }
98
+ /**
99
+ * Extracts package metadata from a package.json object into a lock file entry format.
100
+ * Copies version, dependency fields, engines, and license.
101
+ */
102
+ static extractPackageMetadata(pkgJson, fallbackVersion) {
103
+ const entry = {
104
+ version: pkgJson.version || fallbackVersion,
105
+ };
106
+ for (const field of PackageJsonParser.DEPENDENCY_FIELDS) {
107
+ if (pkgJson[field] && Object.keys(pkgJson[field]).length > 0) {
108
+ entry[field] = pkgJson[field];
109
+ }
110
+ }
111
+ if (pkgJson.engines) {
112
+ entry.engines = pkgJson.engines;
113
+ }
114
+ if (pkgJson.license) {
115
+ entry.license = pkgJson.license;
116
+ }
117
+ return entry;
118
+ }
119
+ /**
120
+ * Accepts package.json files.
121
+ */
122
+ accept(sourcePath) {
123
+ const fileName = path.basename(sourcePath);
124
+ return fileName === 'package.json';
125
+ }
126
+ parse(...inputs) {
127
+ return __asyncGenerator(this, arguments, function* parse_1() {
128
+ // Group inputs by directory to share NodeResolutionResult markers
129
+ const inputsByDir = new Map();
130
+ for (const input of inputs) {
131
+ const filePath = (0, parser_1.parserInputFile)(input);
132
+ const dir = path.dirname(filePath);
133
+ if (!inputsByDir.has(dir)) {
134
+ inputsByDir.set(dir, []);
135
+ }
136
+ inputsByDir.get(dir).push(input);
137
+ }
138
+ // Process each directory's package.json files
139
+ for (const [dir, dirInputs] of inputsByDir) {
140
+ // Create a shared marker for this directory
141
+ let marker = null;
142
+ for (const input of dirInputs) {
143
+ // Parse as JSON first
144
+ const jsonGenerator = this.jsonParser.parse(input);
145
+ const jsonResult = yield __await(jsonGenerator.next());
146
+ if (jsonResult.done || !jsonResult.value) {
147
+ continue;
148
+ }
149
+ const jsonDoc = jsonResult.value;
150
+ // Create NodeResolutionResult marker if not already created for this directory
151
+ if (!marker) {
152
+ marker = yield __await(this.createMarker(input, dir));
153
+ }
154
+ // Attach the marker to the JSON document
155
+ if (marker) {
156
+ yield yield __await(Object.assign(Object.assign({}, jsonDoc), { markers: Object.assign(Object.assign({}, jsonDoc.markers), { markers: [...jsonDoc.markers.markers, marker] }) }));
157
+ }
158
+ else {
159
+ yield yield __await(jsonDoc);
160
+ }
161
+ }
162
+ }
163
+ });
164
+ }
165
+ /**
166
+ * Creates a NodeResolutionResult marker from the package.json content and optional lock file.
167
+ */
168
+ createMarker(input, dir) {
169
+ return __awaiter(this, void 0, void 0, function* () {
170
+ try {
171
+ const content = (0, parser_1.parserInputRead)(input);
172
+ const packageJson = JSON.parse(content);
173
+ // Determine the relative path for the marker
174
+ const filePath = (0, parser_1.parserInputFile)(input);
175
+ const relativePath = this.relativeTo
176
+ ? path.relative(this.relativeTo, filePath)
177
+ : filePath;
178
+ // Try to read lock file if dependency resolution is not skipped
179
+ // Use relativeTo directory if available (for tests), otherwise use the directory from input path
180
+ let lockContent = undefined;
181
+ let packageManager = undefined;
182
+ if (!this.skipDependencyResolution) {
183
+ const lockDir = this.relativeTo || dir;
184
+ const lockResult = yield this.tryReadLockFile(lockDir);
185
+ lockContent = lockResult === null || lockResult === void 0 ? void 0 : lockResult.content;
186
+ packageManager = lockResult === null || lockResult === void 0 ? void 0 : lockResult.packageManager;
187
+ }
188
+ // Read .npmrc configurations from all scopes
189
+ const projectDir = this.relativeTo || dir;
190
+ const npmrcConfigs = yield (0, node_resolution_result_1.readNpmrcConfigs)(projectDir);
191
+ return (0, node_resolution_result_1.createNodeResolutionResultMarker)(relativePath, packageJson, lockContent, undefined, packageManager, npmrcConfigs.length > 0 ? npmrcConfigs : undefined);
192
+ }
193
+ catch (error) {
194
+ console.warn(`Failed to create NodeResolutionResult marker: ${error}`);
195
+ return null;
196
+ }
197
+ });
198
+ }
199
+ /**
200
+ * Attempts to read and parse a lock file from the given directory.
201
+ * Supports npm (package-lock.json), bun (bun.lock), pnpm, and yarn.
202
+ *
203
+ * @returns Object with parsed lock file content and detected package manager, or undefined if no lock file found
204
+ */
205
+ tryReadLockFile(dir) {
206
+ return __awaiter(this, void 0, void 0, function* () {
207
+ var _a;
208
+ // Detect which lock file exists (first match wins based on priority)
209
+ for (const config of PackageJsonParser.LOCK_FILE_CONFIG) {
210
+ const lockPath = path.join(dir, config.filename);
211
+ if (!fs.existsSync(lockPath)) {
212
+ continue;
213
+ }
214
+ try {
215
+ const fileContent = yield fsp.readFile(lockPath, 'utf-8');
216
+ const packageManager = typeof config.packageManager === 'function'
217
+ ? config.packageManager(fileContent)
218
+ : config.packageManager;
219
+ // For package managers where lock file omits details, prefer node_modules
220
+ if (config.preferNodeModules) {
221
+ const parsed = yield this.walkNodeModules(dir);
222
+ if (parsed) {
223
+ return { content: parsed, packageManager };
224
+ }
225
+ }
226
+ // Parse lock file based on package manager
227
+ const content = yield this.parseLockFileContent(config.filename, fileContent, dir);
228
+ if (content) {
229
+ return { content, packageManager };
230
+ }
231
+ }
232
+ catch (error) {
233
+ (_a = console.debug) === null || _a === void 0 ? void 0 : _a.call(console, `Failed to parse ${config.filename}: ${error}`);
234
+ }
235
+ }
236
+ return undefined;
237
+ });
238
+ }
239
+ /**
240
+ * Parses lock file content based on the lock file type.
241
+ */
242
+ parseLockFileContent(filename, content, dir) {
243
+ return __awaiter(this, void 0, void 0, function* () {
244
+ switch (filename) {
245
+ case 'package-lock.json':
246
+ return JSON.parse(content);
247
+ case 'bun.lock':
248
+ return this.convertBunLockToNpmFormat(this.parseJsonc(content));
249
+ case 'pnpm-lock.yaml':
250
+ // Fall back to pnpm CLI when node_modules unavailable
251
+ return this.getPnpmDependencies(dir);
252
+ case 'yarn.lock':
253
+ return this.parseYarnLock(content);
254
+ default:
255
+ return undefined;
256
+ }
257
+ });
258
+ }
259
+ /**
260
+ * Parses JSONC (JSON with Comments and trailing commas) content.
261
+ *
262
+ * Note: This is a simple regex-based approach that works for bun.lock files but doesn't
263
+ * handle edge cases like comment-like sequences inside strings (e.g., "// not a comment").
264
+ * For lock files this is acceptable since they don't contain such patterns. If broader
265
+ * JSONC support is needed, consider using a proper parser like `jsonc-parser`.
266
+ */
267
+ parseJsonc(content) {
268
+ // Remove single-line comments (// ...)
269
+ let stripped = content.replace(/\/\/.*$/gm, '');
270
+ // Remove multi-line comments (/* ... */)
271
+ stripped = stripped.replace(/\/\*[\s\S]*?\*\//g, '');
272
+ // Remove trailing commas before ] or }
273
+ stripped = stripped.replace(/,(\s*[}\]])/g, '$1');
274
+ return JSON.parse(stripped);
275
+ }
276
+ /**
277
+ * Walks the node_modules directory to build an npm-format packages structure.
278
+ * This provides 100% accurate resolution for all package managers since it reads
279
+ * the actual installed packages rather than trying to interpret lock file formats.
280
+ *
281
+ * @param dir The project directory containing node_modules
282
+ * @returns npm package-lock.json format with packages map, or undefined if node_modules doesn't exist
283
+ */
284
+ walkNodeModules(dir) {
285
+ return __awaiter(this, void 0, void 0, function* () {
286
+ const nodeModulesPath = path.join(dir, 'node_modules');
287
+ if (!fs.existsSync(nodeModulesPath)) {
288
+ return undefined;
289
+ }
290
+ const packages = {
291
+ "": {} // Root package placeholder
292
+ };
293
+ // Check if this is a pnpm project (has .pnpm directory)
294
+ const pnpmPath = path.join(nodeModulesPath, '.pnpm');
295
+ if (fs.existsSync(pnpmPath)) {
296
+ yield this.walkPnpmNodeModules(pnpmPath, packages);
297
+ }
298
+ else {
299
+ yield this.walkNodeModulesRecursive(nodeModulesPath, 'node_modules', packages);
300
+ }
301
+ return Object.keys(packages).length > 1 ? {
302
+ lockfileVersion: 3,
303
+ packages
304
+ } : undefined;
305
+ });
306
+ }
307
+ /**
308
+ * Walks pnpm's .pnpm directory structure to build packages map.
309
+ * pnpm stores packages in .pnpm/<name>@<version>/node_modules/<name>/
310
+ */
311
+ walkPnpmNodeModules(pnpmPath, packages) {
312
+ return __awaiter(this, void 0, void 0, function* () {
313
+ let entries;
314
+ try {
315
+ entries = yield fsp.readdir(pnpmPath, { withFileTypes: true });
316
+ }
317
+ catch (_a) {
318
+ return;
319
+ }
320
+ // Process entries in parallel for better performance
321
+ yield Promise.all(entries.map((entry) => __awaiter(this, void 0, void 0, function* () {
322
+ // Skip non-directories and special files
323
+ if (!entry.isDirectory() || entry.name === 'node_modules') {
324
+ return;
325
+ }
326
+ // Parse name@version from directory name
327
+ // Handle scoped packages: @scope+name@version
328
+ const atIndex = entry.name.lastIndexOf('@');
329
+ if (atIndex <= 0)
330
+ return;
331
+ let name = entry.name.substring(0, atIndex);
332
+ const version = entry.name.substring(atIndex + 1);
333
+ // pnpm encodes @ as + in scoped packages: @scope+name -> @scope/name
334
+ if (name.startsWith('@') && name.includes('+')) {
335
+ name = name.replace('+', '/');
336
+ }
337
+ // The actual package is at .pnpm/<name>@<version>/node_modules/<name>/
338
+ const pkgDir = path.join(pnpmPath, entry.name, 'node_modules', name.replace('/', path.sep));
339
+ const packageJsonPath = path.join(pkgDir, 'package.json');
340
+ let pkgJson;
341
+ try {
342
+ const content = yield fsp.readFile(packageJsonPath, 'utf-8');
343
+ pkgJson = JSON.parse(content);
344
+ }
345
+ catch (_a) {
346
+ return;
347
+ }
348
+ // Use name@version as the key for pnpm (flat structure with version)
349
+ const pkgKey = `node_modules/${name}@${version}`;
350
+ packages[pkgKey] = PackageJsonParser.extractPackageMetadata(pkgJson, version);
351
+ })));
352
+ });
353
+ }
354
+ /**
355
+ * Recursively walks a node_modules directory, reading package.json files
356
+ * and building the packages map.
357
+ *
358
+ * @param nodeModulesPath Absolute path to the node_modules directory
359
+ * @param relativePath Relative path from project root (e.g., "node_modules" or "node_modules/foo/node_modules")
360
+ * @param packages The packages map to populate
361
+ */
362
+ walkNodeModulesRecursive(nodeModulesPath, relativePath, packages) {
363
+ return __awaiter(this, void 0, void 0, function* () {
364
+ let entries;
365
+ try {
366
+ entries = yield fsp.readdir(nodeModulesPath, { withFileTypes: true });
367
+ }
368
+ catch (_a) {
369
+ return; // Directory not readable
370
+ }
371
+ // Process entries in parallel for better performance
372
+ yield Promise.all(entries.map((entry) => __awaiter(this, void 0, void 0, function* () {
373
+ // Skip hidden files
374
+ if (entry.name.startsWith('.')) {
375
+ return;
376
+ }
377
+ // Accept directories and symlinks (pnpm uses symlinks)
378
+ const isDirectoryOrSymlink = entry.isDirectory() || entry.isSymbolicLink();
379
+ if (!isDirectoryOrSymlink) {
380
+ return;
381
+ }
382
+ // Handle scoped packages (@scope/name)
383
+ if (entry.name.startsWith('@')) {
384
+ const scopePath = path.join(nodeModulesPath, entry.name);
385
+ let scopeEntries;
386
+ try {
387
+ scopeEntries = yield fsp.readdir(scopePath, { withFileTypes: true });
388
+ }
389
+ catch (_a) {
390
+ return;
391
+ }
392
+ yield Promise.all(scopeEntries.map((scopeEntry) => __awaiter(this, void 0, void 0, function* () {
393
+ // Accept directories and symlinks for scoped packages too
394
+ if (!scopeEntry.isDirectory() && !scopeEntry.isSymbolicLink())
395
+ return;
396
+ const scopedName = `${entry.name}/${scopeEntry.name}`;
397
+ const pkgPath = path.join(scopePath, scopeEntry.name);
398
+ yield this.processPackage(pkgPath, `${relativePath}/${scopedName}`, packages);
399
+ })));
400
+ }
401
+ else {
402
+ const pkgPath = path.join(nodeModulesPath, entry.name);
403
+ yield this.processPackage(pkgPath, `${relativePath}/${entry.name}`, packages);
404
+ }
405
+ })));
406
+ });
407
+ }
408
+ /**
409
+ * Processes a single package directory, reading its package.json and
410
+ * recursively processing nested node_modules.
411
+ */
412
+ processPackage(pkgPath, relativePath, packages) {
413
+ return __awaiter(this, void 0, void 0, function* () {
414
+ const packageJsonPath = path.join(pkgPath, 'package.json');
415
+ // Read and parse the package's package.json
416
+ let pkgJson;
417
+ try {
418
+ const content = yield fsp.readFile(packageJsonPath, 'utf-8');
419
+ pkgJson = JSON.parse(content);
420
+ }
421
+ catch (_a) {
422
+ return; // Not a valid package
423
+ }
424
+ packages[relativePath] = PackageJsonParser.extractPackageMetadata(pkgJson);
425
+ // Recursively process nested node_modules
426
+ const nestedNodeModules = path.join(pkgPath, 'node_modules');
427
+ try {
428
+ yield fsp.access(nestedNodeModules);
429
+ yield this.walkNodeModulesRecursive(nestedNodeModules, `${relativePath}/node_modules`, packages);
430
+ }
431
+ catch (_b) {
432
+ // No nested node_modules, that's fine
433
+ }
434
+ });
435
+ }
436
+ /**
437
+ * Converts bun.lock format to npm package-lock.json format for unified processing.
438
+ *
439
+ * bun.lock format (v1):
440
+ * - Keys are package names or paths like "is-even/is-odd" for nested deps
441
+ * - Values are arrays: [name@version, url, metadata, integrity]
442
+ * - metadata can have: { dependencies: {...}, devDependencies: {...}, ... }
443
+ */
444
+ convertBunLockToNpmFormat(bunLock) {
445
+ if (!bunLock.packages) {
446
+ return undefined;
447
+ }
448
+ const packages = {
449
+ "": {} // Root package placeholder
450
+ };
451
+ for (const [key, value] of Object.entries(bunLock.packages)) {
452
+ // bun.lock array format: [name@version, url, metadata, integrity]
453
+ const [nameAtVersion, , metadata] = value;
454
+ if (typeof nameAtVersion !== 'string')
455
+ continue;
456
+ // Parse name@version from first element
457
+ const atIndex = nameAtVersion.lastIndexOf('@');
458
+ if (atIndex <= 0)
459
+ continue;
460
+ const name = nameAtVersion.substring(0, atIndex);
461
+ const version = nameAtVersion.substring(atIndex + 1);
462
+ const pkgEntry = {
463
+ version,
464
+ dependencies: (metadata === null || metadata === void 0 ? void 0 : metadata.dependencies) && Object.keys(metadata.dependencies).length > 0
465
+ ? metadata.dependencies : undefined,
466
+ devDependencies: (metadata === null || metadata === void 0 ? void 0 : metadata.devDependencies) && Object.keys(metadata.devDependencies).length > 0
467
+ ? metadata.devDependencies : undefined,
468
+ peerDependencies: (metadata === null || metadata === void 0 ? void 0 : metadata.peerDependencies) && Object.keys(metadata.peerDependencies).length > 0
469
+ ? metadata.peerDependencies : undefined,
470
+ optionalDependencies: (metadata === null || metadata === void 0 ? void 0 : metadata.optionalDependencies) && Object.keys(metadata.optionalDependencies).length > 0
471
+ ? metadata.optionalDependencies : undefined,
472
+ };
473
+ // Convert bun's path format to npm's node_modules format
474
+ // bun uses "parent/child" for nested deps, npm uses "node_modules/parent/node_modules/child"
475
+ let pkgPath;
476
+ if (key.includes('/')) {
477
+ // Nested dependency - convert "is-even/is-odd" to "node_modules/is-even/node_modules/is-odd"
478
+ const parts = key.split('/');
479
+ pkgPath = parts.map(p => `node_modules/${p}`).join('/');
480
+ }
481
+ else {
482
+ pkgPath = `node_modules/${name}`;
483
+ }
484
+ packages[pkgPath] = pkgEntry;
485
+ }
486
+ return {
487
+ lockfileVersion: 3,
488
+ packages
489
+ };
490
+ }
491
+ /**
492
+ * Gets dependency information from pnpm using its CLI.
493
+ * Uses `pnpm list --json --depth=Infinity` to get the full dependency tree.
494
+ */
495
+ getPnpmDependencies(dir) {
496
+ // Use spawnSync with array args to avoid shell injection risks
497
+ const result = (0, child_process_1.spawnSync)('pnpm', ['list', '--json', '--depth=Infinity'], {
498
+ cwd: dir,
499
+ encoding: 'utf-8',
500
+ stdio: ['pipe', 'pipe', 'pipe'],
501
+ timeout: 30000
502
+ });
503
+ if (result.error || result.status !== 0) {
504
+ return undefined;
505
+ }
506
+ const pnpmList = JSON.parse(result.stdout);
507
+ return this.convertPnpmListToNpmFormat(pnpmList);
508
+ }
509
+ /**
510
+ * Converts pnpm list --json output to npm package-lock.json format.
511
+ */
512
+ convertPnpmListToNpmFormat(pnpmList) {
513
+ const packages = {
514
+ "": {} // Root package placeholder
515
+ };
516
+ // pnpm list returns an array of projects (for workspaces) or a single object
517
+ const projects = Array.isArray(pnpmList) ? pnpmList : [pnpmList];
518
+ for (const project of projects) {
519
+ this.extractPnpmDependencies(project.dependencies, packages);
520
+ this.extractPnpmDependencies(project.devDependencies, packages);
521
+ this.extractPnpmDependencies(project.optionalDependencies, packages);
522
+ }
523
+ return Object.keys(packages).length > 1 ? {
524
+ lockfileVersion: 3,
525
+ packages
526
+ } : undefined;
527
+ }
528
+ /**
529
+ * Recursively extracts dependencies from pnpm list output.
530
+ * Uses name@version as key to handle multiple versions of the same package.
531
+ */
532
+ extractPnpmDependencies(deps, packages) {
533
+ if (!deps)
534
+ return;
535
+ for (const [name, info] of Object.entries(deps)) {
536
+ const version = info.version;
537
+ if (!version)
538
+ continue;
539
+ const pkgKey = `node_modules/${name}@${version}`;
540
+ if (!packages[pkgKey]) {
541
+ const pkgEntry = { version };
542
+ // Extract nested dependency version constraints
543
+ if (info.dependencies) {
544
+ const nestedDeps = {};
545
+ for (const [depName, depInfo] of Object.entries(info.dependencies)) {
546
+ nestedDeps[depName] = depInfo.version || '*';
547
+ }
548
+ if (Object.keys(nestedDeps).length > 0) {
549
+ pkgEntry.dependencies = nestedDeps;
550
+ }
551
+ }
552
+ packages[pkgKey] = pkgEntry;
553
+ }
554
+ // Recursively process nested dependencies
555
+ if (info.dependencies) {
556
+ this.extractPnpmDependencies(info.dependencies, packages);
557
+ }
558
+ }
559
+ }
560
+ /**
561
+ * Parses yarn.lock file and returns npm-format content.
562
+ * Detects whether it's Yarn Classic (v1) or Yarn Berry (v2+) format.
563
+ */
564
+ parseYarnLock(content) {
565
+ // Yarn Berry (v2+) has __metadata section at the start
566
+ if (content.includes('__metadata:')) {
567
+ return this.parseYarnBerryLock(content);
568
+ }
569
+ // Yarn Classic (v1) starts with "# yarn lockfile v1"
570
+ if (content.includes('# yarn lockfile v1')) {
571
+ return this.parseYarnClassicLock(content);
572
+ }
573
+ return undefined;
574
+ }
575
+ /**
576
+ * Parses Yarn Berry (v2+) yarn.lock file directly.
577
+ * Format is standard YAML with package entries like:
578
+ * "is-odd@npm:^3.0.1":
579
+ * version: 3.0.1
580
+ * resolution: "is-odd@npm:3.0.1"
581
+ * dependencies:
582
+ * is-number: "npm:^6.0.0"
583
+ */
584
+ parseYarnBerryLock(content) {
585
+ const lock = YAML.parse(content);
586
+ if (!lock)
587
+ return undefined;
588
+ const packages = {
589
+ "": {} // Root package placeholder
590
+ };
591
+ for (const [key, entry] of Object.entries(lock)) {
592
+ // Skip metadata and workspace entries
593
+ if (key === '__metadata' || key.includes('@workspace:'))
594
+ continue;
595
+ if (!entry || typeof entry !== 'object')
596
+ continue;
597
+ const version = entry.version;
598
+ if (!version)
599
+ continue;
600
+ // Extract package name from resolution like "is-odd@npm:3.0.1"
601
+ let name;
602
+ if (entry.resolution) {
603
+ const npmIndex = entry.resolution.indexOf('@npm:');
604
+ if (npmIndex > 0) {
605
+ name = entry.resolution.substring(0, npmIndex);
606
+ }
607
+ else {
608
+ continue;
609
+ }
610
+ }
611
+ else {
612
+ // Fallback: parse from key like "is-odd@npm:^3.0.1"
613
+ const npmIndex = key.indexOf('@npm:');
614
+ if (npmIndex > 0) {
615
+ name = key.substring(0, npmIndex);
616
+ }
617
+ else {
618
+ continue;
619
+ }
620
+ }
621
+ const pkgKey = `node_modules/${name}@${version}`;
622
+ // Skip if already processed (multiple version constraints can resolve to same version)
623
+ if (packages[pkgKey])
624
+ continue;
625
+ const pkgEntry = { version };
626
+ // Parse dependencies
627
+ if (entry.dependencies && typeof entry.dependencies === 'object') {
628
+ const deps = {};
629
+ for (const [depName, depConstraint] of Object.entries(entry.dependencies)) {
630
+ // Constraint is like "npm:^6.0.0" - strip the "npm:" prefix
631
+ deps[depName] = depConstraint.startsWith('npm:')
632
+ ? depConstraint.substring(4)
633
+ : depConstraint;
634
+ }
635
+ if (Object.keys(deps).length > 0) {
636
+ pkgEntry.dependencies = deps;
637
+ }
638
+ }
639
+ packages[pkgKey] = pkgEntry;
640
+ }
641
+ return Object.keys(packages).length > 1 ? {
642
+ lockfileVersion: 3,
643
+ packages
644
+ } : undefined;
645
+ }
646
+ /**
647
+ * Parses Yarn Classic (v1) yarn.lock file directly.
648
+ * Format is a custom format (not standard YAML):
649
+ *
650
+ * is-odd@^3.0.1:
651
+ * version "3.0.1"
652
+ * resolved "https://..."
653
+ * integrity sha512-...
654
+ * dependencies:
655
+ * is-number "^6.0.0"
656
+ */
657
+ parseYarnClassicLock(content) {
658
+ const packages = {
659
+ "": {} // Root package placeholder
660
+ };
661
+ // Split into package blocks - each block starts with an unindented line ending with ":"
662
+ // and may span multiple version constraints (e.g., "pkg@^1.0.0, pkg@^1.2.0:")
663
+ const lines = content.split('\n');
664
+ let currentNames = [];
665
+ let currentVersion = null;
666
+ let currentDeps = {};
667
+ let inDependencies = false;
668
+ for (const line of lines) {
669
+ // Skip comments and empty lines
670
+ if (line.startsWith('#') || line.trim() === '') {
671
+ continue;
672
+ }
673
+ // New package block (unindented line ending with ":")
674
+ if (!line.startsWith(' ') && line.endsWith(':')) {
675
+ // Save previous package if exists
676
+ if (currentNames.length > 0 && currentVersion) {
677
+ const pkgKey = `node_modules/${currentNames[0]}@${currentVersion}`;
678
+ if (!packages[pkgKey]) {
679
+ const pkgEntry = { version: currentVersion };
680
+ if (Object.keys(currentDeps).length > 0) {
681
+ pkgEntry.dependencies = currentDeps;
682
+ }
683
+ packages[pkgKey] = pkgEntry;
684
+ }
685
+ }
686
+ // Parse new package names from line like 'is-odd@^3.0.1, is-odd@^3.0.0:'
687
+ // or '"@babel/core@^7.0.0":'
688
+ const namesStr = line.slice(0, -1); // Remove trailing ":"
689
+ currentNames = [];
690
+ // Split by ", " but handle quoted strings
691
+ const parts = namesStr.split(/,\s*(?=(?:[^"]*"[^"]*")*[^"]*$)/);
692
+ for (const part of parts) {
693
+ // Remove surrounding quotes if present
694
+ let cleaned = part.trim();
695
+ if (cleaned.startsWith('"') && cleaned.endsWith('"')) {
696
+ cleaned = cleaned.slice(1, -1);
697
+ }
698
+ // Extract package name (everything before last @)
699
+ const atIndex = cleaned.lastIndexOf('@');
700
+ if (atIndex > 0) {
701
+ currentNames.push(cleaned.substring(0, atIndex));
702
+ }
703
+ }
704
+ currentVersion = null;
705
+ currentDeps = {};
706
+ inDependencies = false;
707
+ continue;
708
+ }
709
+ // Version line: ' version "3.0.1"'
710
+ const versionMatch = line.match(/^\s+version\s+"([^"]+)"/);
711
+ if (versionMatch) {
712
+ currentVersion = versionMatch[1];
713
+ continue;
714
+ }
715
+ // Dependencies section start
716
+ if (line.match(/^\s+dependencies:\s*$/)) {
717
+ inDependencies = true;
718
+ continue;
719
+ }
720
+ // Other section (resolved, integrity, etc.) - ends dependencies section
721
+ if (line.match(/^\s+\w+:/) && !line.match(/^\s{4}/)) {
722
+ inDependencies = false;
723
+ continue;
724
+ }
725
+ // Dependency entry: ' is-number "^6.0.0"'
726
+ if (inDependencies) {
727
+ const depMatch = line.match(/^\s{4}(.+?)\s+"([^"]+)"/);
728
+ if (depMatch) {
729
+ currentDeps[depMatch[1]] = depMatch[2];
730
+ }
731
+ }
732
+ }
733
+ // Save last package
734
+ if (currentNames.length > 0 && currentVersion) {
735
+ const pkgKey = `node_modules/${currentNames[0]}@${currentVersion}`;
736
+ if (!packages[pkgKey]) {
737
+ const pkgEntry = { version: currentVersion };
738
+ if (Object.keys(currentDeps).length > 0) {
739
+ pkgEntry.dependencies = currentDeps;
740
+ }
741
+ packages[pkgKey] = pkgEntry;
742
+ }
743
+ }
744
+ return Object.keys(packages).length > 1 ? {
745
+ lockfileVersion: 3,
746
+ packages
747
+ } : undefined;
748
+ }
749
+ }
750
+ exports.PackageJsonParser = PackageJsonParser;
751
+ /** Fields to copy from package.json that contain dependency maps */
752
+ PackageJsonParser.DEPENDENCY_FIELDS = [
753
+ 'dependencies',
754
+ 'devDependencies',
755
+ 'peerDependencies',
756
+ 'optionalDependencies'
757
+ ];
758
+ /**
759
+ * Lock file detection configuration.
760
+ * Priority order determines which package manager is detected when multiple lock files exist.
761
+ */
762
+ PackageJsonParser.LOCK_FILE_CONFIG = [
763
+ { filename: 'package-lock.json', packageManager: node_resolution_result_1.PackageManager.Npm },
764
+ { filename: 'bun.lock', packageManager: node_resolution_result_1.PackageManager.Bun },
765
+ { filename: 'pnpm-lock.yaml', packageManager: node_resolution_result_1.PackageManager.Pnpm, preferNodeModules: true },
766
+ // yarn.lock omits transitive dependency details (engines/license), so prefer node_modules
767
+ { filename: 'yarn.lock', packageManager: (content) => content.includes('__metadata:') ? node_resolution_result_1.PackageManager.YarnBerry : node_resolution_result_1.PackageManager.YarnClassic,
768
+ preferNodeModules: true
769
+ },
770
+ ];
771
+ // Register with the Parsers registry for RPC support
772
+ parser_1.Parsers.registerParser("packageJson", PackageJsonParser);
773
+ //# sourceMappingURL=package-json-parser.js.map