@preship/core 2.0.1 → 2.1.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.
- package/dist/index.d.ts +127 -3
- package/dist/index.js +781 -21
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
type ProjectType = 'npm' | 'yarn' | 'pnpm';
|
|
1
|
+
type ProjectType = 'npm' | 'yarn' | 'pnpm' | 'flutter' | 'maven' | 'gradle' | 'cocoapods' | 'spm';
|
|
2
2
|
interface DetectedProject {
|
|
3
3
|
type: ProjectType;
|
|
4
4
|
path: string;
|
|
@@ -14,7 +14,7 @@ interface Dependency {
|
|
|
14
14
|
isDirect: boolean;
|
|
15
15
|
isDevDependency: boolean;
|
|
16
16
|
}
|
|
17
|
-
type LicenseSource = 'package-json-license-field' | 'license-file' | 'package-json-licenses-field' | 'npm-registry' | 'github-api' | 'cache' | 'unknown';
|
|
17
|
+
type LicenseSource = 'package-json-license-field' | 'license-file' | 'package-json-licenses-field' | 'npm-registry' | 'pub-registry' | 'maven-central' | 'cocoapods-cdn' | 'github-api' | 'cache' | 'unknown';
|
|
18
18
|
interface ParsedDependency {
|
|
19
19
|
name: string;
|
|
20
20
|
version: string;
|
|
@@ -205,6 +205,130 @@ declare function parseYarnLockfile(lockfilePath: string, packageJsonPath: string
|
|
|
205
205
|
|
|
206
206
|
declare function parsePnpmLockfile(lockfilePath: string, packageJsonPath: string): ParsedDependency[];
|
|
207
207
|
|
|
208
|
+
/**
|
|
209
|
+
* Parse a Flutter/Dart pubspec.lock file and return all dependencies.
|
|
210
|
+
*
|
|
211
|
+
* pubspec.lock uses a well-defined YAML structure:
|
|
212
|
+
* packages:
|
|
213
|
+
* <name>:
|
|
214
|
+
* dependency: "direct main"
|
|
215
|
+
* description:
|
|
216
|
+
* name: <name>
|
|
217
|
+
* ...
|
|
218
|
+
* source: hosted
|
|
219
|
+
* version: "1.2.3"
|
|
220
|
+
*
|
|
221
|
+
* We use a simple line-based parser (no js-yaml dependency needed)
|
|
222
|
+
* because pubspec.lock has a consistent, predictable structure.
|
|
223
|
+
*
|
|
224
|
+
* SDK packages (source: sdk) are skipped — these are Flutter/Dart
|
|
225
|
+
* built-ins like `flutter`, `sky_engine`, `flutter_test`.
|
|
226
|
+
*
|
|
227
|
+
* @param lockfilePath - Absolute path to pubspec.lock
|
|
228
|
+
* @param pubspecYamlPath - Absolute path to pubspec.yaml
|
|
229
|
+
* @returns Array of ParsedDependency
|
|
230
|
+
*/
|
|
231
|
+
declare function parsePubspecLockfile(lockfilePath: string, pubspecYamlPath: string): ParsedDependency[];
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Parse a Maven pom.xml file and return all dependencies.
|
|
235
|
+
*
|
|
236
|
+
* Maven doesn't have a lockfile, so we parse pom.xml directly.
|
|
237
|
+
* Extracts <dependency> blocks from the <dependencies> section,
|
|
238
|
+
* resolving ${property} version references from <properties>.
|
|
239
|
+
*
|
|
240
|
+
* Skips:
|
|
241
|
+
* - <scope>provided</scope> and <scope>system</scope> (always)
|
|
242
|
+
* - <scope>test</scope> (marked as dev dependency)
|
|
243
|
+
*
|
|
244
|
+
* Package name format: groupId:artifactId
|
|
245
|
+
* (e.g., "org.springframework.boot:spring-boot-starter-web")
|
|
246
|
+
*
|
|
247
|
+
* @param pomPath - Absolute path to pom.xml
|
|
248
|
+
* @param _manifestPath - Same as pomPath (Maven uses pom.xml as both)
|
|
249
|
+
* @returns Array of ParsedDependency
|
|
250
|
+
*/
|
|
251
|
+
declare function parseMavenPom(pomPath: string, _manifestPath: string): ParsedDependency[];
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Parse a Gradle project's dependencies.
|
|
255
|
+
*
|
|
256
|
+
* Tries sources in priority order:
|
|
257
|
+
* 1. gradle.lockfile (opt-in, most reliable)
|
|
258
|
+
* 2. gradle/libs.versions.toml (version catalog)
|
|
259
|
+
* 3. build.gradle or build.gradle.kts (regex fallback)
|
|
260
|
+
*
|
|
261
|
+
* Package name format: groupId:artifactId
|
|
262
|
+
* (e.g., "org.springframework.boot:spring-boot-starter-web")
|
|
263
|
+
*
|
|
264
|
+
* @param lockfilePath - Path to gradle.lockfile or build.gradle(.kts)
|
|
265
|
+
* @param manifestPath - Path to build.gradle or build.gradle.kts
|
|
266
|
+
* @returns Array of ParsedDependency
|
|
267
|
+
*/
|
|
268
|
+
declare function parseGradleLockfile(lockfilePath: string, manifestPath: string): ParsedDependency[];
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Parse a CocoaPods Podfile.lock and return all dependencies.
|
|
272
|
+
*
|
|
273
|
+
* Podfile.lock is a YAML-like format with consistent structure:
|
|
274
|
+
*
|
|
275
|
+
* PODS:
|
|
276
|
+
* - AFNetworking (4.0.1):
|
|
277
|
+
* - AFNetworking/NSURLSession (= 4.0.1)
|
|
278
|
+
* - AFNetworking/Reachability (= 4.0.1)
|
|
279
|
+
* - Alamofire (5.9.1)
|
|
280
|
+
*
|
|
281
|
+
* DEPENDENCIES:
|
|
282
|
+
* - AFNetworking (~> 4.0)
|
|
283
|
+
* - Alamofire
|
|
284
|
+
*
|
|
285
|
+
* SPEC REPOS:
|
|
286
|
+
* ...
|
|
287
|
+
*
|
|
288
|
+
* We use a line-based parser (no YAML library needed) since the
|
|
289
|
+
* format is predictable.
|
|
290
|
+
*
|
|
291
|
+
* Subspecs (e.g., AFNetworking/NSURLSession) are skipped — only
|
|
292
|
+
* top-level pods are returned.
|
|
293
|
+
*
|
|
294
|
+
* @param lockfilePath - Absolute path to Podfile.lock
|
|
295
|
+
* @param _manifestPath - Absolute path to Podfile (unused, for API consistency)
|
|
296
|
+
* @returns Array of ParsedDependency
|
|
297
|
+
*/
|
|
298
|
+
declare function parsePodfileLock(lockfilePath: string, _manifestPath: string): ParsedDependency[];
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Parse a Swift Package Manager Package.resolved file.
|
|
302
|
+
*
|
|
303
|
+
* Supports:
|
|
304
|
+
* - v2 format (Swift 5.6+): { "pins": [...] }
|
|
305
|
+
* - v3 format: same structure with "originHash"
|
|
306
|
+
* - v1 format (legacy): { "object": { "pins": [...] } }
|
|
307
|
+
*
|
|
308
|
+
* Skips:
|
|
309
|
+
* - Branch-based pins (no version)
|
|
310
|
+
* - Local packages (kind: "localSourceControl")
|
|
311
|
+
*
|
|
312
|
+
* All SPM deps are treated as direct (Package.resolved doesn't
|
|
313
|
+
* distinguish direct vs transitive).
|
|
314
|
+
*
|
|
315
|
+
* The `location` field contains the Git URL, useful for
|
|
316
|
+
* GitHub-based license resolution.
|
|
317
|
+
*
|
|
318
|
+
* @param resolvedPath - Absolute path to Package.resolved
|
|
319
|
+
* @param _manifestPath - Absolute path to Package.swift (unused, for API consistency)
|
|
320
|
+
* @returns Array of ParsedDependency
|
|
321
|
+
*/
|
|
322
|
+
declare function parsePackageResolved(resolvedPath: string, _manifestPath: string): ParsedDependency[];
|
|
323
|
+
/**
|
|
324
|
+
* Extract the Git location URL for a pin.
|
|
325
|
+
* Used by the license resolver to find GitHub repos.
|
|
326
|
+
*
|
|
327
|
+
* @param resolvedPath - Absolute path to Package.resolved
|
|
328
|
+
* @returns Map of package identity → location URL
|
|
329
|
+
*/
|
|
330
|
+
declare function extractSpmLocations(resolvedPath: string): Map<string, string>;
|
|
331
|
+
|
|
208
332
|
/**
|
|
209
333
|
* Encrypted Project-Level License Cache
|
|
210
334
|
*
|
|
@@ -360,4 +484,4 @@ interface ScanTimeoutController {
|
|
|
360
484
|
*/
|
|
361
485
|
declare function createScanTimeoutController(timeout?: number): ScanTimeoutController;
|
|
362
486
|
|
|
363
|
-
export { AdaptiveRateLimiter, type CacheEntry, type CacheFile, type Dependency, type DetectedProject, type ExceptionEntry, type LicenseSource, type ModuleConfig, type PackageHealth, type ParsedDependency, type PolicyResult, type PolicyTemplate, type PolicyVerdict, type PreshipConfig, type ProjectType, type ResolverMode, type ScanResult, type ScanTimeoutController, type SecretFinding, type SecretRule, type SecretSeverity, type SecretsConfig, type SecretsResult, type SecurityConfig, type SecurityFinding, type SecurityPolicyLevel, type SecurityResult, type SecuritySeverity, type SecurityVulnerability, type UnifiedScanResult, cacheKey, createEmptyCache, createScanTimeoutController, detectProjects, getCacheEntry, getDefaultConfig, getOrCreateKey, loadCache, loadConfig, parseNpmLockfile, parsePnpmLockfile, parseYarnLockfile, saveCache, setCacheEntry };
|
|
487
|
+
export { AdaptiveRateLimiter, type CacheEntry, type CacheFile, type Dependency, type DetectedProject, type ExceptionEntry, type LicenseSource, type ModuleConfig, type PackageHealth, type ParsedDependency, type PolicyResult, type PolicyTemplate, type PolicyVerdict, type PreshipConfig, type ProjectType, type ResolverMode, type ScanResult, type ScanTimeoutController, type SecretFinding, type SecretRule, type SecretSeverity, type SecretsConfig, type SecretsResult, type SecurityConfig, type SecurityFinding, type SecurityPolicyLevel, type SecurityResult, type SecuritySeverity, type SecurityVulnerability, type UnifiedScanResult, cacheKey, createEmptyCache, createScanTimeoutController, detectProjects, extractSpmLocations, getCacheEntry, getDefaultConfig, getOrCreateKey, loadCache, loadConfig, parseGradleLockfile, parseMavenPom, parseNpmLockfile, parsePackageResolved, parsePnpmLockfile, parsePodfileLock, parsePubspecLockfile, parseYarnLockfile, saveCache, setCacheEntry };
|
package/dist/index.js
CHANGED
|
@@ -35,13 +35,19 @@ __export(index_exports, {
|
|
|
35
35
|
createEmptyCache: () => createEmptyCache,
|
|
36
36
|
createScanTimeoutController: () => createScanTimeoutController,
|
|
37
37
|
detectProjects: () => detectProjects,
|
|
38
|
+
extractSpmLocations: () => extractSpmLocations,
|
|
38
39
|
getCacheEntry: () => getCacheEntry,
|
|
39
40
|
getDefaultConfig: () => getDefaultConfig,
|
|
40
41
|
getOrCreateKey: () => getOrCreateKey,
|
|
41
42
|
loadCache: () => loadCache,
|
|
42
43
|
loadConfig: () => loadConfig,
|
|
44
|
+
parseGradleLockfile: () => parseGradleLockfile,
|
|
45
|
+
parseMavenPom: () => parseMavenPom,
|
|
43
46
|
parseNpmLockfile: () => parseNpmLockfile,
|
|
47
|
+
parsePackageResolved: () => parsePackageResolved,
|
|
44
48
|
parsePnpmLockfile: () => parsePnpmLockfile,
|
|
49
|
+
parsePodfileLock: () => parsePodfileLock,
|
|
50
|
+
parsePubspecLockfile: () => parsePubspecLockfile,
|
|
45
51
|
parseYarnLockfile: () => parseYarnLockfile,
|
|
46
52
|
saveCache: () => saveCache,
|
|
47
53
|
setCacheEntry: () => setCacheEntry
|
|
@@ -378,10 +384,30 @@ function loadConfig(projectPath) {
|
|
|
378
384
|
// src/detector.ts
|
|
379
385
|
var fs2 = __toESM(require("fs"));
|
|
380
386
|
var path2 = __toESM(require("path"));
|
|
387
|
+
var NODE_LOCK_FILES = [
|
|
388
|
+
{ filename: "package-lock.json", type: "npm", manifest: "package.json" },
|
|
389
|
+
{ filename: "yarn.lock", type: "yarn", manifest: "package.json" },
|
|
390
|
+
{ filename: "pnpm-lock.yaml", type: "pnpm", manifest: "package.json" }
|
|
391
|
+
];
|
|
392
|
+
var FLUTTER_LOCK_FILES = [
|
|
393
|
+
{ filename: "pubspec.lock", type: "flutter", manifest: "pubspec.yaml" }
|
|
394
|
+
];
|
|
395
|
+
var JAVA_LOCK_FILES = [
|
|
396
|
+
{ filename: "gradle.lockfile", type: "gradle", manifest: "build.gradle" },
|
|
397
|
+
{ filename: "gradle.lockfile", type: "gradle", manifest: "build.gradle.kts" },
|
|
398
|
+
{ filename: "build.gradle", type: "gradle", manifest: "build.gradle" },
|
|
399
|
+
{ filename: "build.gradle.kts", type: "gradle", manifest: "build.gradle.kts" },
|
|
400
|
+
{ filename: "pom.xml", type: "maven", manifest: "pom.xml" }
|
|
401
|
+
];
|
|
402
|
+
var IOS_LOCK_FILES = [
|
|
403
|
+
{ filename: "Podfile.lock", type: "cocoapods", manifest: "Podfile" },
|
|
404
|
+
{ filename: "Package.resolved", type: "spm", manifest: "Package.swift" }
|
|
405
|
+
];
|
|
381
406
|
var LOCK_FILES = [
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
407
|
+
...NODE_LOCK_FILES,
|
|
408
|
+
...FLUTTER_LOCK_FILES,
|
|
409
|
+
...JAVA_LOCK_FILES,
|
|
410
|
+
...IOS_LOCK_FILES
|
|
385
411
|
];
|
|
386
412
|
function detectFramework(packageJsonPath) {
|
|
387
413
|
try {
|
|
@@ -391,30 +417,129 @@ function detectFramework(packageJsonPath) {
|
|
|
391
417
|
...pkg.dependencies || {},
|
|
392
418
|
...pkg.devDependencies || {}
|
|
393
419
|
};
|
|
420
|
+
if (allDeps["expo"]) return "expo";
|
|
421
|
+
if (allDeps["react-native"]) return "react-native";
|
|
422
|
+
if (allDeps["electron"]) return "electron";
|
|
423
|
+
if (allDeps["@tauri-apps/api"] || allDeps["tauri"]) return "tauri";
|
|
394
424
|
if (allDeps["next"]) return "nextjs";
|
|
425
|
+
if (allDeps["nuxt"]) return "nuxtjs";
|
|
426
|
+
if (allDeps["@remix-run/react"] || allDeps["remix"]) return "remix";
|
|
427
|
+
if (allDeps["astro"]) return "astro";
|
|
428
|
+
if (allDeps["gatsby"]) return "gatsby";
|
|
395
429
|
if (allDeps["react"]) return "react";
|
|
430
|
+
if (allDeps["vue"]) return "vue";
|
|
431
|
+
if (allDeps["@angular/core"]) return "angular";
|
|
432
|
+
if (allDeps["svelte"] || allDeps["@sveltejs/kit"]) return "svelte";
|
|
396
433
|
if (allDeps["express"]) return "express";
|
|
397
434
|
if (allDeps["fastify"]) return "fastify";
|
|
398
435
|
if (allDeps["@nestjs/core"] || allDeps["nestjs"]) return "nestjs";
|
|
436
|
+
if (allDeps["@hapi/hapi"]) return "hapi";
|
|
437
|
+
if (allDeps["koa"]) return "koa";
|
|
399
438
|
return void 0;
|
|
400
439
|
} catch {
|
|
401
440
|
return void 0;
|
|
402
441
|
}
|
|
403
442
|
}
|
|
443
|
+
function detectFlutterFramework(pubspecYamlPath, projectRoot) {
|
|
444
|
+
try {
|
|
445
|
+
const content = fs2.readFileSync(pubspecYamlPath, "utf-8");
|
|
446
|
+
const hasFlutterSdk = /^\s{2}flutter:\s*$/m.test(content) && /sdk:\s*flutter/m.test(content);
|
|
447
|
+
if (hasFlutterSdk) {
|
|
448
|
+
const hasWebDir = fs2.existsSync(path2.join(projectRoot, "web"));
|
|
449
|
+
if (hasWebDir) return "flutter-web";
|
|
450
|
+
const hasDesktopDir = fs2.existsSync(path2.join(projectRoot, "linux")) || fs2.existsSync(path2.join(projectRoot, "windows")) || fs2.existsSync(path2.join(projectRoot, "macos"));
|
|
451
|
+
if (hasDesktopDir) return "flutter-desktop";
|
|
452
|
+
return "flutter";
|
|
453
|
+
}
|
|
454
|
+
const hasShelf = /shelf/m.test(content);
|
|
455
|
+
const hasDartFrog = /dart_frog/m.test(content);
|
|
456
|
+
if (hasShelf || hasDartFrog) return "dart-server";
|
|
457
|
+
return "dart-cli";
|
|
458
|
+
} catch {
|
|
459
|
+
return void 0;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
function detectJavaFramework(manifestPath, _projectRoot) {
|
|
463
|
+
try {
|
|
464
|
+
const content = fs2.readFileSync(manifestPath, "utf-8");
|
|
465
|
+
if (/com\.android\.(application|library)/m.test(content)) return "android-java";
|
|
466
|
+
if (/spring-boot-starter/m.test(content)) return "spring-boot";
|
|
467
|
+
if (/org\.springframework/m.test(content)) return "spring";
|
|
468
|
+
if (/io\.quarkus/m.test(content)) return "quarkus";
|
|
469
|
+
if (/io\.micronaut/m.test(content)) return "micronaut";
|
|
470
|
+
return "java";
|
|
471
|
+
} catch {
|
|
472
|
+
return "java";
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
function detectIosFramework(manifestPath, projectType) {
|
|
476
|
+
if (projectType === "spm") return "ios-spm";
|
|
477
|
+
try {
|
|
478
|
+
const content = fs2.readFileSync(manifestPath, "utf-8");
|
|
479
|
+
if (/platform\s*:tvos/m.test(content)) return "tvos-native";
|
|
480
|
+
if (/platform\s*:osx/m.test(content) || /platform\s*:macos/m.test(content)) return "macos-native";
|
|
481
|
+
return "ios-native";
|
|
482
|
+
} catch {
|
|
483
|
+
return "ios-native";
|
|
484
|
+
}
|
|
485
|
+
}
|
|
404
486
|
function detectProjects(rootPath) {
|
|
405
487
|
const absRoot = path2.resolve(rootPath);
|
|
406
488
|
const packageJsonPath = path2.join(absRoot, "package.json");
|
|
407
|
-
|
|
489
|
+
const pubspecYamlPath = path2.join(absRoot, "pubspec.yaml");
|
|
490
|
+
const pomXmlPath = path2.join(absRoot, "pom.xml");
|
|
491
|
+
const buildGradlePath = path2.join(absRoot, "build.gradle");
|
|
492
|
+
const buildGradleKtsPath = path2.join(absRoot, "build.gradle.kts");
|
|
493
|
+
const podfilePath = path2.join(absRoot, "Podfile");
|
|
494
|
+
const packageSwiftPath = path2.join(absRoot, "Package.swift");
|
|
495
|
+
const hasPackageJson = fs2.existsSync(packageJsonPath);
|
|
496
|
+
const hasPubspecYaml = fs2.existsSync(pubspecYamlPath);
|
|
497
|
+
const hasPomXml = fs2.existsSync(pomXmlPath);
|
|
498
|
+
const hasBuildGradle = fs2.existsSync(buildGradlePath) || fs2.existsSync(buildGradleKtsPath);
|
|
499
|
+
const hasPodfile = fs2.existsSync(podfilePath);
|
|
500
|
+
const hasPackageSwift = fs2.existsSync(packageSwiftPath);
|
|
501
|
+
if (!hasPackageJson && !hasPubspecYaml && !hasPomXml && !hasBuildGradle && !hasPodfile && !hasPackageSwift) {
|
|
408
502
|
throw new Error(
|
|
409
|
-
`No
|
|
410
|
-
PreShip
|
|
503
|
+
`No supported project manifest found in ${absRoot}.
|
|
504
|
+
PreShip supports:
|
|
505
|
+
- Node.js (package.json)
|
|
506
|
+
- Flutter/Dart (pubspec.yaml)
|
|
507
|
+
- Maven (pom.xml)
|
|
508
|
+
- Gradle (build.gradle / build.gradle.kts)
|
|
509
|
+
- CocoaPods (Podfile)
|
|
510
|
+
- Swift Package Manager (Package.swift)
|
|
411
511
|
Make sure you're running from the project root.`
|
|
412
512
|
);
|
|
413
513
|
}
|
|
414
514
|
const detected = [];
|
|
415
515
|
for (const entry of LOCK_FILES) {
|
|
416
516
|
const lockFilePath = path2.join(absRoot, entry.filename);
|
|
417
|
-
|
|
517
|
+
const manifestPath = path2.join(absRoot, entry.manifest);
|
|
518
|
+
if (!fs2.existsSync(lockFilePath) || !fs2.existsSync(manifestPath)) {
|
|
519
|
+
continue;
|
|
520
|
+
}
|
|
521
|
+
if (entry.type === "flutter") {
|
|
522
|
+
detected.push({
|
|
523
|
+
type: entry.type,
|
|
524
|
+
path: absRoot,
|
|
525
|
+
lockFile: lockFilePath,
|
|
526
|
+
framework: detectFlutterFramework(pubspecYamlPath, absRoot)
|
|
527
|
+
});
|
|
528
|
+
} else if (entry.type === "maven" || entry.type === "gradle") {
|
|
529
|
+
detected.push({
|
|
530
|
+
type: entry.type,
|
|
531
|
+
path: absRoot,
|
|
532
|
+
lockFile: lockFilePath,
|
|
533
|
+
framework: detectJavaFramework(manifestPath, absRoot)
|
|
534
|
+
});
|
|
535
|
+
} else if (entry.type === "cocoapods" || entry.type === "spm") {
|
|
536
|
+
detected.push({
|
|
537
|
+
type: entry.type,
|
|
538
|
+
path: absRoot,
|
|
539
|
+
lockFile: lockFilePath,
|
|
540
|
+
framework: detectIosFramework(manifestPath, entry.type)
|
|
541
|
+
});
|
|
542
|
+
} else {
|
|
418
543
|
detected.push({
|
|
419
544
|
type: entry.type,
|
|
420
545
|
path: absRoot,
|
|
@@ -424,11 +549,42 @@ function detectProjects(rootPath) {
|
|
|
424
549
|
}
|
|
425
550
|
}
|
|
426
551
|
if (detected.length === 0) {
|
|
427
|
-
|
|
428
|
-
|
|
552
|
+
if (hasPackageJson) {
|
|
553
|
+
throw new Error(
|
|
554
|
+
`No supported lock file found in ${absRoot}.
|
|
429
555
|
PreShip needs a lock file to scan dependencies.
|
|
430
556
|
Run 'npm install', 'yarn install', or 'pnpm install' first.`
|
|
431
|
-
|
|
557
|
+
);
|
|
558
|
+
} else if (hasPubspecYaml) {
|
|
559
|
+
throw new Error(
|
|
560
|
+
`No pubspec.lock found in ${absRoot}.
|
|
561
|
+
PreShip needs a lock file to scan dependencies.
|
|
562
|
+
Run 'flutter pub get' or 'dart pub get' first.`
|
|
563
|
+
);
|
|
564
|
+
} else if (hasPomXml) {
|
|
565
|
+
throw new Error(
|
|
566
|
+
`Maven project detected in ${absRoot} but scanning is ready.
|
|
567
|
+
PreShip will parse pom.xml directly (no lockfile needed).`
|
|
568
|
+
);
|
|
569
|
+
} else if (hasBuildGradle) {
|
|
570
|
+
throw new Error(
|
|
571
|
+
`No gradle.lockfile found in ${absRoot}.
|
|
572
|
+
PreShip needs a lockfile or build file to scan dependencies.
|
|
573
|
+
For best results, enable lockfile: add 'dependencyLocking { lockAllConfigurations() }' to build.gradle.`
|
|
574
|
+
);
|
|
575
|
+
} else if (hasPodfile) {
|
|
576
|
+
throw new Error(
|
|
577
|
+
`No Podfile.lock found in ${absRoot}.
|
|
578
|
+
PreShip needs a lock file to scan dependencies.
|
|
579
|
+
Run 'pod install' first.`
|
|
580
|
+
);
|
|
581
|
+
} else {
|
|
582
|
+
throw new Error(
|
|
583
|
+
`No Package.resolved found in ${absRoot}.
|
|
584
|
+
PreShip needs a resolved file to scan dependencies.
|
|
585
|
+
Run 'swift package resolve' first.`
|
|
586
|
+
);
|
|
587
|
+
}
|
|
432
588
|
}
|
|
433
589
|
return [detected[0]];
|
|
434
590
|
}
|
|
@@ -481,7 +637,13 @@ function parseV2Packages(packages, directDeps, directDevDeps) {
|
|
|
481
637
|
if (key === "") continue;
|
|
482
638
|
if (!key.startsWith("node_modules/")) continue;
|
|
483
639
|
const parts = key.split("node_modules/");
|
|
484
|
-
|
|
640
|
+
let name = parts[parts.length - 1];
|
|
641
|
+
if (entry.resolved && typeof entry.resolved === "string") {
|
|
642
|
+
const realName = extractPackageNameFromResolved(entry.resolved);
|
|
643
|
+
if (realName && realName !== name) {
|
|
644
|
+
name = realName;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
485
647
|
if (!name || !entry.version) continue;
|
|
486
648
|
const dedupKey = `${name}@${entry.version}`;
|
|
487
649
|
if (seen.has(dedupKey)) continue;
|
|
@@ -497,6 +659,21 @@ function parseV2Packages(packages, directDeps, directDevDeps) {
|
|
|
497
659
|
}
|
|
498
660
|
return results;
|
|
499
661
|
}
|
|
662
|
+
function extractPackageNameFromResolved(resolved) {
|
|
663
|
+
const scopedMatch = resolved.match(
|
|
664
|
+
/registry\.npmjs\.org\/(@[^/]+\/[^/]+)\/-\//
|
|
665
|
+
);
|
|
666
|
+
if (scopedMatch) {
|
|
667
|
+
return scopedMatch[1];
|
|
668
|
+
}
|
|
669
|
+
const regularMatch = resolved.match(
|
|
670
|
+
/registry\.npmjs\.org\/([^@/][^/]*)\/-\//
|
|
671
|
+
);
|
|
672
|
+
if (regularMatch) {
|
|
673
|
+
return regularMatch[1];
|
|
674
|
+
}
|
|
675
|
+
return null;
|
|
676
|
+
}
|
|
500
677
|
function parseV1Dependencies(dependencies, directDeps, directDevDeps) {
|
|
501
678
|
const results = [];
|
|
502
679
|
function walk(deps) {
|
|
@@ -757,18 +934,595 @@ function parseV5Key(key) {
|
|
|
757
934
|
return { name, pkgVersion };
|
|
758
935
|
}
|
|
759
936
|
|
|
760
|
-
// src/
|
|
761
|
-
var crypto = __toESM(require("crypto"));
|
|
937
|
+
// src/parsers/flutter.ts
|
|
762
938
|
var fs6 = __toESM(require("fs"));
|
|
939
|
+
function parsePubspecLockfile(lockfilePath, pubspecYamlPath) {
|
|
940
|
+
let lockContent;
|
|
941
|
+
try {
|
|
942
|
+
lockContent = fs6.readFileSync(lockfilePath, "utf-8");
|
|
943
|
+
} catch {
|
|
944
|
+
throw new Error(
|
|
945
|
+
`Failed to read ${lockfilePath}.
|
|
946
|
+
The lock file may be missing or corrupted. Try running 'flutter pub get'.`
|
|
947
|
+
);
|
|
948
|
+
}
|
|
949
|
+
let pubspecContent;
|
|
950
|
+
try {
|
|
951
|
+
pubspecContent = fs6.readFileSync(pubspecYamlPath, "utf-8");
|
|
952
|
+
} catch {
|
|
953
|
+
throw new Error(
|
|
954
|
+
`Failed to read ${pubspecYamlPath}.
|
|
955
|
+
Make sure pubspec.yaml exists and is readable.`
|
|
956
|
+
);
|
|
957
|
+
}
|
|
958
|
+
const { directDeps, devDeps } = parsePubspecYamlDeps(pubspecContent);
|
|
959
|
+
const packages = parsePubspecLockPackages(lockContent);
|
|
960
|
+
const results = [];
|
|
961
|
+
const seen = /* @__PURE__ */ new Set();
|
|
962
|
+
for (const [name, entry] of packages) {
|
|
963
|
+
if (entry.source === "sdk") {
|
|
964
|
+
continue;
|
|
965
|
+
}
|
|
966
|
+
if (!entry.version) {
|
|
967
|
+
continue;
|
|
968
|
+
}
|
|
969
|
+
const dedupKey = `${name}@${entry.version}`;
|
|
970
|
+
if (seen.has(dedupKey)) {
|
|
971
|
+
continue;
|
|
972
|
+
}
|
|
973
|
+
seen.add(dedupKey);
|
|
974
|
+
const isDirect = entry.dependency === "direct main" || entry.dependency === "direct dev" || entry.dependency === "direct overridden";
|
|
975
|
+
const isDevDependency = entry.dependency === "direct dev" || !isDirect && devDeps.has(name) && !directDeps.has(name);
|
|
976
|
+
results.push({
|
|
977
|
+
name,
|
|
978
|
+
version: entry.version,
|
|
979
|
+
isDirect,
|
|
980
|
+
isDevDependency
|
|
981
|
+
});
|
|
982
|
+
}
|
|
983
|
+
return results;
|
|
984
|
+
}
|
|
985
|
+
function parsePubspecYamlDeps(content) {
|
|
986
|
+
const directDeps = /* @__PURE__ */ new Set();
|
|
987
|
+
const devDeps = /* @__PURE__ */ new Set();
|
|
988
|
+
const lines = content.split("\n");
|
|
989
|
+
let currentSection = null;
|
|
990
|
+
for (const line of lines) {
|
|
991
|
+
const trimmed = line.trimEnd();
|
|
992
|
+
if (trimmed === "" || trimmed.startsWith("#")) {
|
|
993
|
+
continue;
|
|
994
|
+
}
|
|
995
|
+
if (!trimmed.startsWith(" ") && !trimmed.startsWith(" ")) {
|
|
996
|
+
if (trimmed === "dependencies:" || trimmed.startsWith("dependencies:")) {
|
|
997
|
+
currentSection = "dependencies";
|
|
998
|
+
continue;
|
|
999
|
+
}
|
|
1000
|
+
if (trimmed === "dev_dependencies:" || trimmed.startsWith("dev_dependencies:")) {
|
|
1001
|
+
currentSection = "dev_dependencies";
|
|
1002
|
+
continue;
|
|
1003
|
+
}
|
|
1004
|
+
currentSection = null;
|
|
1005
|
+
continue;
|
|
1006
|
+
}
|
|
1007
|
+
if (currentSection) {
|
|
1008
|
+
const depMatch = trimmed.match(/^ ([a-zA-Z_][a-zA-Z0-9_]*):/);
|
|
1009
|
+
if (depMatch) {
|
|
1010
|
+
const depName = depMatch[1];
|
|
1011
|
+
if (currentSection === "dependencies") {
|
|
1012
|
+
directDeps.add(depName);
|
|
1013
|
+
} else {
|
|
1014
|
+
devDeps.add(depName);
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
return { directDeps, devDeps };
|
|
1020
|
+
}
|
|
1021
|
+
function parsePubspecLockPackages(content) {
|
|
1022
|
+
const packages = /* @__PURE__ */ new Map();
|
|
1023
|
+
const lines = content.split("\n");
|
|
1024
|
+
let inPackages = false;
|
|
1025
|
+
let currentPackage = null;
|
|
1026
|
+
let currentEntry = {};
|
|
1027
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1028
|
+
const line = lines[i];
|
|
1029
|
+
const trimmedEnd = line.trimEnd();
|
|
1030
|
+
if (trimmedEnd === "packages:") {
|
|
1031
|
+
inPackages = true;
|
|
1032
|
+
continue;
|
|
1033
|
+
}
|
|
1034
|
+
if (!inPackages) {
|
|
1035
|
+
continue;
|
|
1036
|
+
}
|
|
1037
|
+
if (trimmedEnd !== "" && !trimmedEnd.startsWith(" ") && !trimmedEnd.startsWith(" ")) {
|
|
1038
|
+
if (currentPackage && currentEntry.dependency && currentEntry.source && currentEntry.version) {
|
|
1039
|
+
packages.set(currentPackage, currentEntry);
|
|
1040
|
+
}
|
|
1041
|
+
break;
|
|
1042
|
+
}
|
|
1043
|
+
if (trimmedEnd === "") {
|
|
1044
|
+
continue;
|
|
1045
|
+
}
|
|
1046
|
+
const packageNameMatch = line.match(/^ ([a-zA-Z_][a-zA-Z0-9_-]*):\s*$/);
|
|
1047
|
+
if (packageNameMatch) {
|
|
1048
|
+
if (currentPackage && currentEntry.dependency && currentEntry.source && currentEntry.version) {
|
|
1049
|
+
packages.set(currentPackage, currentEntry);
|
|
1050
|
+
}
|
|
1051
|
+
currentPackage = packageNameMatch[1];
|
|
1052
|
+
currentEntry = {};
|
|
1053
|
+
continue;
|
|
1054
|
+
}
|
|
1055
|
+
if (currentPackage && line.startsWith(" ") && !line.startsWith(" ")) {
|
|
1056
|
+
const propMatch = line.match(/^ (\w+):\s*"?([^"]*)"?\s*$/);
|
|
1057
|
+
if (propMatch) {
|
|
1058
|
+
const [, key, value] = propMatch;
|
|
1059
|
+
if (key === "dependency") {
|
|
1060
|
+
currentEntry.dependency = value;
|
|
1061
|
+
} else if (key === "source") {
|
|
1062
|
+
currentEntry.source = value;
|
|
1063
|
+
} else if (key === "version") {
|
|
1064
|
+
currentEntry.version = value;
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
if (currentPackage && currentEntry.dependency && currentEntry.source && currentEntry.version) {
|
|
1070
|
+
packages.set(currentPackage, currentEntry);
|
|
1071
|
+
}
|
|
1072
|
+
return packages;
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
// src/parsers/maven.ts
|
|
1076
|
+
var fs7 = __toESM(require("fs"));
|
|
1077
|
+
function parseMavenPom(pomPath, _manifestPath) {
|
|
1078
|
+
let content;
|
|
1079
|
+
try {
|
|
1080
|
+
content = fs7.readFileSync(pomPath, "utf-8");
|
|
1081
|
+
} catch {
|
|
1082
|
+
throw new Error(
|
|
1083
|
+
`Failed to read ${pomPath}.
|
|
1084
|
+
The pom.xml file may be missing or corrupted.`
|
|
1085
|
+
);
|
|
1086
|
+
}
|
|
1087
|
+
const properties = parseProperties(content);
|
|
1088
|
+
const dependencies = parseDependencies(content, properties);
|
|
1089
|
+
return dependencies;
|
|
1090
|
+
}
|
|
1091
|
+
function parseProperties(content) {
|
|
1092
|
+
const props = /* @__PURE__ */ new Map();
|
|
1093
|
+
const propsMatch = content.match(/<properties>([\s\S]*?)<\/properties>/);
|
|
1094
|
+
if (!propsMatch) return props;
|
|
1095
|
+
const propsBlock = propsMatch[1];
|
|
1096
|
+
const propRegex = /<([a-zA-Z0-9._-]+)>(.*?)<\/\1>/g;
|
|
1097
|
+
let match;
|
|
1098
|
+
while ((match = propRegex.exec(propsBlock)) !== null) {
|
|
1099
|
+
props.set(match[1], match[2].trim());
|
|
1100
|
+
}
|
|
1101
|
+
return props;
|
|
1102
|
+
}
|
|
1103
|
+
function resolveVersion(version, properties) {
|
|
1104
|
+
if (!version) return void 0;
|
|
1105
|
+
return version.replace(/\$\{([^}]+)\}/g, (_, propName) => {
|
|
1106
|
+
return properties.get(propName) ?? `\${${propName}}`;
|
|
1107
|
+
});
|
|
1108
|
+
}
|
|
1109
|
+
function parseDependencies(content, properties) {
|
|
1110
|
+
const results = [];
|
|
1111
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1112
|
+
const withoutMgmt = content.replace(
|
|
1113
|
+
/<dependencyManagement>[\s\S]*?<\/dependencyManagement>/g,
|
|
1114
|
+
""
|
|
1115
|
+
);
|
|
1116
|
+
const depsBlockRegex = /<dependencies>([\s\S]*?)<\/dependencies>/g;
|
|
1117
|
+
let depsMatch;
|
|
1118
|
+
while ((depsMatch = depsBlockRegex.exec(withoutMgmt)) !== null) {
|
|
1119
|
+
const depsBlock = depsMatch[1];
|
|
1120
|
+
const depRegex = /<dependency>([\s\S]*?)<\/dependency>/g;
|
|
1121
|
+
let depMatch;
|
|
1122
|
+
while ((depMatch = depRegex.exec(depsBlock)) !== null) {
|
|
1123
|
+
const depBlock = depMatch[1];
|
|
1124
|
+
const groupId = extractXmlValue(depBlock, "groupId");
|
|
1125
|
+
const artifactId = extractXmlValue(depBlock, "artifactId");
|
|
1126
|
+
const rawVersion = extractXmlValue(depBlock, "version");
|
|
1127
|
+
const scope = extractXmlValue(depBlock, "scope");
|
|
1128
|
+
if (!groupId || !artifactId) continue;
|
|
1129
|
+
if (scope === "provided" || scope === "system") continue;
|
|
1130
|
+
const version = resolveVersion(rawVersion, properties) ?? "UNKNOWN";
|
|
1131
|
+
const name = `${groupId}:${artifactId}`;
|
|
1132
|
+
const isDevDependency = scope === "test";
|
|
1133
|
+
const dedupKey = `${name}@${version}`;
|
|
1134
|
+
if (seen.has(dedupKey)) continue;
|
|
1135
|
+
seen.add(dedupKey);
|
|
1136
|
+
results.push({
|
|
1137
|
+
name,
|
|
1138
|
+
version,
|
|
1139
|
+
isDirect: true,
|
|
1140
|
+
// pom.xml only lists direct dependencies
|
|
1141
|
+
isDevDependency
|
|
1142
|
+
});
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
return results;
|
|
1146
|
+
}
|
|
1147
|
+
function extractXmlValue(block, tag) {
|
|
1148
|
+
const match = block.match(new RegExp(`<${tag}>\\s*(.*?)\\s*</${tag}>`));
|
|
1149
|
+
return match ? match[1].trim() : void 0;
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
// src/parsers/gradle.ts
|
|
1153
|
+
var fs8 = __toESM(require("fs"));
|
|
763
1154
|
var path3 = __toESM(require("path"));
|
|
1155
|
+
function parseGradleLockfile(lockfilePath, manifestPath) {
|
|
1156
|
+
const lockFileName = path3.basename(lockfilePath);
|
|
1157
|
+
if (lockFileName === "gradle.lockfile") {
|
|
1158
|
+
return parseGradleLockfileFormat(lockfilePath);
|
|
1159
|
+
}
|
|
1160
|
+
const projectDir = path3.dirname(manifestPath);
|
|
1161
|
+
const versionCatalogPath = path3.join(projectDir, "gradle", "libs.versions.toml");
|
|
1162
|
+
if (fs8.existsSync(versionCatalogPath)) {
|
|
1163
|
+
const catalogDeps = parseVersionCatalog(versionCatalogPath);
|
|
1164
|
+
if (catalogDeps.length > 0) return catalogDeps;
|
|
1165
|
+
}
|
|
1166
|
+
return parseBuildGradleFile(manifestPath);
|
|
1167
|
+
}
|
|
1168
|
+
function parseGradleLockfileFormat(lockfilePath) {
|
|
1169
|
+
let content;
|
|
1170
|
+
try {
|
|
1171
|
+
content = fs8.readFileSync(lockfilePath, "utf-8");
|
|
1172
|
+
} catch {
|
|
1173
|
+
throw new Error(
|
|
1174
|
+
`Failed to read ${lockfilePath}.
|
|
1175
|
+
The gradle.lockfile may be missing or corrupted.`
|
|
1176
|
+
);
|
|
1177
|
+
}
|
|
1178
|
+
const results = [];
|
|
1179
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1180
|
+
for (const line of content.split("\n")) {
|
|
1181
|
+
const trimmed = line.trim();
|
|
1182
|
+
if (!trimmed || trimmed.startsWith("#") || trimmed === "empty=") continue;
|
|
1183
|
+
const eqIdx = trimmed.indexOf("=");
|
|
1184
|
+
if (eqIdx === -1) continue;
|
|
1185
|
+
const coordPart = trimmed.slice(0, eqIdx);
|
|
1186
|
+
const configPart = trimmed.slice(eqIdx + 1);
|
|
1187
|
+
const parts = coordPart.split(":");
|
|
1188
|
+
if (parts.length < 3) continue;
|
|
1189
|
+
const groupId = parts[0];
|
|
1190
|
+
const artifactId = parts[1];
|
|
1191
|
+
const version = parts[2];
|
|
1192
|
+
const name = `${groupId}:${artifactId}`;
|
|
1193
|
+
const dedupKey = `${name}@${version}`;
|
|
1194
|
+
if (seen.has(dedupKey)) continue;
|
|
1195
|
+
seen.add(dedupKey);
|
|
1196
|
+
const configs = configPart.split(",").map((c) => c.trim().toLowerCase());
|
|
1197
|
+
const isTestOnly = configs.length > 0 && configs.every(
|
|
1198
|
+
(c) => c.includes("test") || c.includes("androidtest") || c.includes("unittest")
|
|
1199
|
+
);
|
|
1200
|
+
results.push({
|
|
1201
|
+
name,
|
|
1202
|
+
version,
|
|
1203
|
+
isDirect: true,
|
|
1204
|
+
// Lockfile doesn't distinguish, assume direct
|
|
1205
|
+
isDevDependency: isTestOnly
|
|
1206
|
+
});
|
|
1207
|
+
}
|
|
1208
|
+
return results;
|
|
1209
|
+
}
|
|
1210
|
+
function parseVersionCatalog(catalogPath) {
|
|
1211
|
+
let content;
|
|
1212
|
+
try {
|
|
1213
|
+
content = fs8.readFileSync(catalogPath, "utf-8");
|
|
1214
|
+
} catch {
|
|
1215
|
+
return [];
|
|
1216
|
+
}
|
|
1217
|
+
const versions = /* @__PURE__ */ new Map();
|
|
1218
|
+
const versionsSection = extractTomlSection(content, "versions");
|
|
1219
|
+
if (versionsSection) {
|
|
1220
|
+
for (const line of versionsSection.split("\n")) {
|
|
1221
|
+
const match = line.match(/^\s*([a-zA-Z0-9_-]+)\s*=\s*"([^"]+)"/);
|
|
1222
|
+
if (match) {
|
|
1223
|
+
versions.set(match[1], match[2]);
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
const results = [];
|
|
1228
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1229
|
+
const librariesSection = extractTomlSection(content, "libraries");
|
|
1230
|
+
if (!librariesSection) return results;
|
|
1231
|
+
for (const line of librariesSection.split("\n")) {
|
|
1232
|
+
const trimmed = line.trim();
|
|
1233
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
1234
|
+
const moduleMatch = trimmed.match(/module\s*=\s*"([^"]+)"/);
|
|
1235
|
+
if (!moduleMatch) {
|
|
1236
|
+
const groupMatch = trimmed.match(/group\s*=\s*"([^"]+)"/);
|
|
1237
|
+
const nameMatch = trimmed.match(/name\s*=\s*"([^"]+)"/);
|
|
1238
|
+
if (!groupMatch || !nameMatch) continue;
|
|
1239
|
+
const name2 = `${groupMatch[1]}:${nameMatch[1]}`;
|
|
1240
|
+
const version2 = resolveTomlVersion(trimmed, versions);
|
|
1241
|
+
if (!version2) continue;
|
|
1242
|
+
const dedupKey2 = `${name2}@${version2}`;
|
|
1243
|
+
if (seen.has(dedupKey2)) continue;
|
|
1244
|
+
seen.add(dedupKey2);
|
|
1245
|
+
results.push({ name: name2, version: version2, isDirect: true, isDevDependency: false });
|
|
1246
|
+
continue;
|
|
1247
|
+
}
|
|
1248
|
+
const name = moduleMatch[1];
|
|
1249
|
+
const version = resolveTomlVersion(trimmed, versions);
|
|
1250
|
+
if (!version) continue;
|
|
1251
|
+
const dedupKey = `${name}@${version}`;
|
|
1252
|
+
if (seen.has(dedupKey)) continue;
|
|
1253
|
+
seen.add(dedupKey);
|
|
1254
|
+
results.push({ name, version, isDirect: true, isDevDependency: false });
|
|
1255
|
+
}
|
|
1256
|
+
return results;
|
|
1257
|
+
}
|
|
1258
|
+
function extractTomlSection(content, sectionName) {
|
|
1259
|
+
const sectionRegex = new RegExp(`^\\[${sectionName}\\]\\s*$`, "m");
|
|
1260
|
+
const match = content.match(sectionRegex);
|
|
1261
|
+
if (!match || match.index === void 0) return null;
|
|
1262
|
+
const start = match.index + match[0].length;
|
|
1263
|
+
const nextSection = content.slice(start).match(/^\[/m);
|
|
1264
|
+
const end = nextSection?.index !== void 0 ? start + nextSection.index : content.length;
|
|
1265
|
+
return content.slice(start, end);
|
|
1266
|
+
}
|
|
1267
|
+
function resolveTomlVersion(line, versions) {
|
|
1268
|
+
const refMatch = line.match(/version\.ref\s*=\s*"([^"]+)"/);
|
|
1269
|
+
if (refMatch) {
|
|
1270
|
+
return versions.get(refMatch[1]);
|
|
1271
|
+
}
|
|
1272
|
+
const versionMatch = line.match(/version\s*=\s*"([^"]+)"/);
|
|
1273
|
+
if (versionMatch) {
|
|
1274
|
+
return versionMatch[1];
|
|
1275
|
+
}
|
|
1276
|
+
return void 0;
|
|
1277
|
+
}
|
|
1278
|
+
function parseBuildGradleFile(buildFilePath) {
|
|
1279
|
+
let content;
|
|
1280
|
+
try {
|
|
1281
|
+
content = fs8.readFileSync(buildFilePath, "utf-8");
|
|
1282
|
+
} catch {
|
|
1283
|
+
throw new Error(
|
|
1284
|
+
`Failed to read ${buildFilePath}.
|
|
1285
|
+
The build file may be missing or corrupted.`
|
|
1286
|
+
);
|
|
1287
|
+
}
|
|
1288
|
+
const results = [];
|
|
1289
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1290
|
+
const depConfigs = [
|
|
1291
|
+
"implementation",
|
|
1292
|
+
"api",
|
|
1293
|
+
"compileOnly",
|
|
1294
|
+
"runtimeOnly",
|
|
1295
|
+
"testImplementation",
|
|
1296
|
+
"testCompileOnly",
|
|
1297
|
+
"testRuntimeOnly",
|
|
1298
|
+
"androidTestImplementation",
|
|
1299
|
+
"debugImplementation",
|
|
1300
|
+
"releaseImplementation"
|
|
1301
|
+
];
|
|
1302
|
+
for (const config of depConfigs) {
|
|
1303
|
+
const patterns = [
|
|
1304
|
+
new RegExp(`${config}\\s*\\(\\s*["']([^"']+)["']\\s*\\)`, "g"),
|
|
1305
|
+
new RegExp(`${config}\\s+["']([^"']+)["']`, "g")
|
|
1306
|
+
];
|
|
1307
|
+
for (const pattern of patterns) {
|
|
1308
|
+
let match;
|
|
1309
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
1310
|
+
const dep = match[1];
|
|
1311
|
+
const parts = dep.split(":");
|
|
1312
|
+
if (parts.length < 3) continue;
|
|
1313
|
+
const name = `${parts[0]}:${parts[1]}`;
|
|
1314
|
+
const version = parts[2];
|
|
1315
|
+
const dedupKey = `${name}@${version}`;
|
|
1316
|
+
if (seen.has(dedupKey)) continue;
|
|
1317
|
+
seen.add(dedupKey);
|
|
1318
|
+
const isTest = config.toLowerCase().includes("test");
|
|
1319
|
+
results.push({
|
|
1320
|
+
name,
|
|
1321
|
+
version,
|
|
1322
|
+
isDirect: true,
|
|
1323
|
+
isDevDependency: isTest
|
|
1324
|
+
});
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
const mapPattern = new RegExp(
|
|
1328
|
+
`${config}\\s+group:\\s*["']([^"']+)["']\\s*,\\s*name:\\s*["']([^"']+)["']\\s*,\\s*version:\\s*["']([^"']+)["']`,
|
|
1329
|
+
"g"
|
|
1330
|
+
);
|
|
1331
|
+
let mapMatch;
|
|
1332
|
+
while ((mapMatch = mapPattern.exec(content)) !== null) {
|
|
1333
|
+
const name = `${mapMatch[1]}:${mapMatch[2]}`;
|
|
1334
|
+
const version = mapMatch[3];
|
|
1335
|
+
const dedupKey = `${name}@${version}`;
|
|
1336
|
+
if (seen.has(dedupKey)) continue;
|
|
1337
|
+
seen.add(dedupKey);
|
|
1338
|
+
const isTest = config.toLowerCase().includes("test");
|
|
1339
|
+
results.push({
|
|
1340
|
+
name,
|
|
1341
|
+
version,
|
|
1342
|
+
isDirect: true,
|
|
1343
|
+
isDevDependency: isTest
|
|
1344
|
+
});
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
return results;
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
// src/parsers/cocoapods.ts
|
|
1351
|
+
var fs9 = __toESM(require("fs"));
|
|
1352
|
+
function parsePodfileLock(lockfilePath, _manifestPath) {
|
|
1353
|
+
let content;
|
|
1354
|
+
try {
|
|
1355
|
+
content = fs9.readFileSync(lockfilePath, "utf-8");
|
|
1356
|
+
} catch {
|
|
1357
|
+
throw new Error(
|
|
1358
|
+
`Failed to read ${lockfilePath}.
|
|
1359
|
+
The Podfile.lock may be missing or corrupted. Try running 'pod install'.`
|
|
1360
|
+
);
|
|
1361
|
+
}
|
|
1362
|
+
const pods = parsePodsSection(content);
|
|
1363
|
+
const directDeps = parseDependenciesSection(content);
|
|
1364
|
+
const results = [];
|
|
1365
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1366
|
+
for (const [name, version] of pods) {
|
|
1367
|
+
if (name.includes("/")) continue;
|
|
1368
|
+
const dedupKey = `${name}@${version}`;
|
|
1369
|
+
if (seen.has(dedupKey)) continue;
|
|
1370
|
+
seen.add(dedupKey);
|
|
1371
|
+
const isDirect = directDeps.has(name);
|
|
1372
|
+
results.push({
|
|
1373
|
+
name,
|
|
1374
|
+
version,
|
|
1375
|
+
isDirect,
|
|
1376
|
+
isDevDependency: false
|
|
1377
|
+
// CocoaPods doesn't have dev dependency concept
|
|
1378
|
+
});
|
|
1379
|
+
}
|
|
1380
|
+
return results;
|
|
1381
|
+
}
|
|
1382
|
+
function parsePodsSection(content) {
|
|
1383
|
+
const pods = /* @__PURE__ */ new Map();
|
|
1384
|
+
const lines = content.split("\n");
|
|
1385
|
+
let inPodsSection = false;
|
|
1386
|
+
for (const line of lines) {
|
|
1387
|
+
const trimmed = line.trimEnd();
|
|
1388
|
+
if (trimmed === "PODS:") {
|
|
1389
|
+
inPodsSection = true;
|
|
1390
|
+
continue;
|
|
1391
|
+
}
|
|
1392
|
+
if (inPodsSection && trimmed !== "" && !trimmed.startsWith(" ") && !trimmed.startsWith(" ")) {
|
|
1393
|
+
break;
|
|
1394
|
+
}
|
|
1395
|
+
if (!inPodsSection) continue;
|
|
1396
|
+
if (trimmed === "") continue;
|
|
1397
|
+
const podMatch = trimmed.match(/^ - ([^\s(/]+(?:\/[^\s(]+)?)\s+\(([^)]+)\)/);
|
|
1398
|
+
if (podMatch) {
|
|
1399
|
+
pods.set(podMatch[1], podMatch[2]);
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
return pods;
|
|
1403
|
+
}
|
|
1404
|
+
function parseDependenciesSection(content) {
|
|
1405
|
+
const directDeps = /* @__PURE__ */ new Set();
|
|
1406
|
+
const lines = content.split("\n");
|
|
1407
|
+
let inDepsSection = false;
|
|
1408
|
+
for (const line of lines) {
|
|
1409
|
+
const trimmed = line.trimEnd();
|
|
1410
|
+
if (trimmed === "DEPENDENCIES:") {
|
|
1411
|
+
inDepsSection = true;
|
|
1412
|
+
continue;
|
|
1413
|
+
}
|
|
1414
|
+
if (inDepsSection && trimmed !== "" && !trimmed.startsWith(" ") && !trimmed.startsWith(" ")) {
|
|
1415
|
+
break;
|
|
1416
|
+
}
|
|
1417
|
+
if (!inDepsSection) continue;
|
|
1418
|
+
if (trimmed === "") continue;
|
|
1419
|
+
const depMatch = trimmed.match(/^ - ([^\s(/]+)/);
|
|
1420
|
+
if (depMatch) {
|
|
1421
|
+
directDeps.add(depMatch[1]);
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
return directDeps;
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
// src/parsers/spm.ts
|
|
1428
|
+
var fs10 = __toESM(require("fs"));
|
|
1429
|
+
function parsePackageResolved(resolvedPath, _manifestPath) {
|
|
1430
|
+
let content;
|
|
1431
|
+
try {
|
|
1432
|
+
content = fs10.readFileSync(resolvedPath, "utf-8");
|
|
1433
|
+
} catch {
|
|
1434
|
+
throw new Error(
|
|
1435
|
+
`Failed to read ${resolvedPath}.
|
|
1436
|
+
The Package.resolved file may be missing or corrupted.
|
|
1437
|
+
Run 'swift package resolve' to regenerate it.`
|
|
1438
|
+
);
|
|
1439
|
+
}
|
|
1440
|
+
let parsed;
|
|
1441
|
+
try {
|
|
1442
|
+
parsed = JSON.parse(content);
|
|
1443
|
+
} catch {
|
|
1444
|
+
throw new Error(
|
|
1445
|
+
`Failed to parse ${resolvedPath}: Invalid JSON.
|
|
1446
|
+
The Package.resolved file may be corrupted.`
|
|
1447
|
+
);
|
|
1448
|
+
}
|
|
1449
|
+
const pins = extractPins(parsed);
|
|
1450
|
+
if (!pins) return [];
|
|
1451
|
+
const results = [];
|
|
1452
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1453
|
+
for (const pin of pins) {
|
|
1454
|
+
if (pin.kind === "localSourceControl") continue;
|
|
1455
|
+
const version = pin.state?.version;
|
|
1456
|
+
if (!version) continue;
|
|
1457
|
+
const name = pin.identity;
|
|
1458
|
+
if (!name) continue;
|
|
1459
|
+
const dedupKey = `${name}@${version}`;
|
|
1460
|
+
if (seen.has(dedupKey)) continue;
|
|
1461
|
+
seen.add(dedupKey);
|
|
1462
|
+
results.push({
|
|
1463
|
+
name,
|
|
1464
|
+
version,
|
|
1465
|
+
isDirect: true,
|
|
1466
|
+
// Package.resolved doesn't distinguish
|
|
1467
|
+
isDevDependency: false
|
|
1468
|
+
});
|
|
1469
|
+
}
|
|
1470
|
+
return results;
|
|
1471
|
+
}
|
|
1472
|
+
function extractPins(parsed) {
|
|
1473
|
+
if (Array.isArray(parsed.pins)) {
|
|
1474
|
+
return parsed.pins;
|
|
1475
|
+
}
|
|
1476
|
+
if (parsed.object && Array.isArray(parsed.object.pins)) {
|
|
1477
|
+
return parsed.object.pins.map((pin) => ({
|
|
1478
|
+
identity: pin.package,
|
|
1479
|
+
kind: "remoteSourceControl",
|
|
1480
|
+
location: pin.repositoryURL || "",
|
|
1481
|
+
state: {
|
|
1482
|
+
revision: pin.state?.revision,
|
|
1483
|
+
version: pin.state?.version,
|
|
1484
|
+
branch: pin.state?.branch
|
|
1485
|
+
}
|
|
1486
|
+
}));
|
|
1487
|
+
}
|
|
1488
|
+
return null;
|
|
1489
|
+
}
|
|
1490
|
+
function extractSpmLocations(resolvedPath) {
|
|
1491
|
+
const locations = /* @__PURE__ */ new Map();
|
|
1492
|
+
let content;
|
|
1493
|
+
try {
|
|
1494
|
+
content = fs10.readFileSync(resolvedPath, "utf-8");
|
|
1495
|
+
} catch {
|
|
1496
|
+
return locations;
|
|
1497
|
+
}
|
|
1498
|
+
let parsed;
|
|
1499
|
+
try {
|
|
1500
|
+
parsed = JSON.parse(content);
|
|
1501
|
+
} catch {
|
|
1502
|
+
return locations;
|
|
1503
|
+
}
|
|
1504
|
+
const pins = extractPins(parsed);
|
|
1505
|
+
if (!pins) return locations;
|
|
1506
|
+
for (const pin of pins) {
|
|
1507
|
+
if (pin.identity && pin.location) {
|
|
1508
|
+
locations.set(pin.identity, pin.location);
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
return locations;
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
// src/cache.ts
|
|
1515
|
+
var crypto = __toESM(require("crypto"));
|
|
1516
|
+
var fs11 = __toESM(require("fs"));
|
|
1517
|
+
var path4 = __toESM(require("path"));
|
|
764
1518
|
var KEY_FILENAME = ".preship-key";
|
|
765
1519
|
var ALGORITHM = "aes-256-cbc";
|
|
766
1520
|
var IV_LENGTH = 16;
|
|
767
1521
|
function getOrCreateKey(projectPath) {
|
|
768
|
-
const keyPath =
|
|
1522
|
+
const keyPath = path4.join(projectPath, KEY_FILENAME);
|
|
769
1523
|
try {
|
|
770
|
-
if (
|
|
771
|
-
const hex =
|
|
1524
|
+
if (fs11.existsSync(keyPath)) {
|
|
1525
|
+
const hex = fs11.readFileSync(keyPath, "utf-8").trim();
|
|
772
1526
|
if (hex.length === 64) {
|
|
773
1527
|
return Buffer.from(hex, "hex");
|
|
774
1528
|
}
|
|
@@ -777,7 +1531,7 @@ function getOrCreateKey(projectPath) {
|
|
|
777
1531
|
}
|
|
778
1532
|
const key = crypto.randomBytes(32);
|
|
779
1533
|
try {
|
|
780
|
-
|
|
1534
|
+
fs11.writeFileSync(keyPath, key.toString("hex"), { mode: 384 });
|
|
781
1535
|
} catch {
|
|
782
1536
|
}
|
|
783
1537
|
return key;
|
|
@@ -813,10 +1567,10 @@ function createEmptyCache() {
|
|
|
813
1567
|
}
|
|
814
1568
|
function loadCache(cacheFilePath, key) {
|
|
815
1569
|
try {
|
|
816
|
-
if (!
|
|
1570
|
+
if (!fs11.existsSync(cacheFilePath)) {
|
|
817
1571
|
return createEmptyCache();
|
|
818
1572
|
}
|
|
819
|
-
const encryptedContent =
|
|
1573
|
+
const encryptedContent = fs11.readFileSync(cacheFilePath, "utf-8").trim();
|
|
820
1574
|
if (!encryptedContent) {
|
|
821
1575
|
return createEmptyCache();
|
|
822
1576
|
}
|
|
@@ -835,8 +1589,8 @@ function saveCache(cacheFilePath, cache, key) {
|
|
|
835
1589
|
const jsonStr = JSON.stringify(cache);
|
|
836
1590
|
const encrypted = encrypt(jsonStr, key);
|
|
837
1591
|
const tmpPath = cacheFilePath + ".tmp";
|
|
838
|
-
|
|
839
|
-
|
|
1592
|
+
fs11.writeFileSync(tmpPath, encrypted, "utf-8");
|
|
1593
|
+
fs11.renameSync(tmpPath, cacheFilePath);
|
|
840
1594
|
} catch {
|
|
841
1595
|
}
|
|
842
1596
|
}
|
|
@@ -1025,13 +1779,19 @@ function createScanTimeoutController(timeout) {
|
|
|
1025
1779
|
createEmptyCache,
|
|
1026
1780
|
createScanTimeoutController,
|
|
1027
1781
|
detectProjects,
|
|
1782
|
+
extractSpmLocations,
|
|
1028
1783
|
getCacheEntry,
|
|
1029
1784
|
getDefaultConfig,
|
|
1030
1785
|
getOrCreateKey,
|
|
1031
1786
|
loadCache,
|
|
1032
1787
|
loadConfig,
|
|
1788
|
+
parseGradleLockfile,
|
|
1789
|
+
parseMavenPom,
|
|
1033
1790
|
parseNpmLockfile,
|
|
1791
|
+
parsePackageResolved,
|
|
1034
1792
|
parsePnpmLockfile,
|
|
1793
|
+
parsePodfileLock,
|
|
1794
|
+
parsePubspecLockfile,
|
|
1035
1795
|
parseYarnLockfile,
|
|
1036
1796
|
saveCache,
|
|
1037
1797
|
setCacheEntry
|
package/package.json
CHANGED