UNPKG

8.64 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const fs = require("fs");
4const path = require("path");
5const yaml = require("js-yaml");
6const dep_graph_1 = require("@snyk/dep-graph");
7const utils_1 = require("./utils");
8class LockfileParser {
9 constructor(hash, rootPkgInfo) {
10 this.rootPkgInfo = undefined;
11 this.rootPkgInfo = rootPkgInfo;
12 this.internalData = hash;
13 }
14 static async readFile(lockfilePath) {
15 const rootName = path.basename(path.dirname(path.resolve(lockfilePath)));
16 return new Promise((resolve, reject) => {
17 fs.readFile(lockfilePath, { encoding: 'utf8' }, (err, fileContents) => {
18 if (err) {
19 reject(err);
20 }
21 try {
22 const parser = this.readContents(fileContents, {
23 name: rootName,
24 version: '0.0.0',
25 });
26 resolve(parser);
27 }
28 catch (err) {
29 reject(err);
30 }
31 });
32 });
33 }
34 static readFileSync(lockfilePath) {
35 const fileContents = fs.readFileSync(lockfilePath, 'utf8');
36 const rootName = path.basename(path.dirname(path.resolve(lockfilePath)));
37 return this.readContents(fileContents, {
38 name: rootName,
39 version: '0.0.0',
40 });
41 }
42 static readContents(contents, rootPkgInfo) {
43 return new LockfileParser(yaml.safeLoad(contents), rootPkgInfo);
44 }
45 toDepGraph() {
46 const builder = new dep_graph_1.DepGraphBuilder(this.pkgManager, this.rootPkgInfo);
47 const allDeps = {};
48 // Add all package nodes first, but collect dependencies
49 this.internalData.PODS.forEach((elem) => {
50 let pkgInfo;
51 let pkgDeps;
52 if (typeof elem === 'string') {
53 // When there are NO dependencies. This equals in yaml e.g.
54 // - Expecta (1.0.5)
55 pkgInfo = utils_1.pkgInfoFromSpecificationString(elem);
56 pkgDeps = [];
57 }
58 else {
59 // When there are dependencies. This equals in yaml e.g.
60 // - React/Core (0.59.2):
61 // - yoga (= 0.59.2.React)
62 const objKey = Object.keys(elem)[0];
63 pkgInfo = utils_1.pkgInfoFromSpecificationString(objKey);
64 pkgDeps = elem[objKey].map(utils_1.pkgInfoFromDependencyString);
65 }
66 const nodeId = this.nodeIdForPkgInfo(pkgInfo);
67 builder.addPkgNode(pkgInfo, nodeId, {
68 labels: this.nodeInfoLabelsForPod(pkgInfo.name),
69 });
70 allDeps[nodeId] = pkgDeps;
71 });
72 // Connect explicitly in the manifest (`Podfile`)
73 // declared dependencies to the root node.
74 this.internalData.DEPENDENCIES.map(utils_1.pkgInfoFromDependencyString).forEach((pkgInfo) => {
75 builder.connectDep(builder.rootNodeId, this.nodeIdForPkgInfo(pkgInfo));
76 });
77 // Now we can start to connect dependencies
78 Object.entries(allDeps).forEach(([nodeId, pkgDeps]) => pkgDeps.forEach((pkgInfo) => {
79 const depNodeId = this.nodeIdForPkgInfo(pkgInfo);
80 if (!allDeps[depNodeId]) {
81 // The pod is not a direct dependency of any targets of the integration,
82 // which can happen for platform-specific transitives, when their platform
83 // is not used in any target. (e.g. PromiseKit/UIKit is iOS-specific and is
84 // a transitive of PromiseKit, but won't be included for a macOS project.)
85 return;
86 }
87 builder.connectDep(nodeId, depNodeId);
88 }));
89 return builder.build();
90 }
91 /// CocoaPods guarantees that every pod is only present in one version,
92 /// so we can use just the pod name as node ID.
93 nodeIdForPkgInfo(pkgInfo) {
94 return pkgInfo.name;
95 }
96 /// Gathers relevant info from the lockfile and transform
97 /// them into the expected labels data structure.
98 nodeInfoLabelsForPod(podName) {
99 let nodeInfoLabels = {
100 checksum: this.checksumForPod(podName),
101 };
102 const repository = this.repositoryForPod(podName);
103 if (repository) {
104 nodeInfoLabels = Object.assign(Object.assign({}, nodeInfoLabels), { repository });
105 }
106 const externalSourceInfo = this.externalSourceInfoForPod(podName);
107 if (externalSourceInfo) {
108 nodeInfoLabels = Object.assign(Object.assign({}, nodeInfoLabels), { externalSourcePodspec: externalSourceInfo[':podspec'], externalSourcePath: externalSourceInfo[':path'], externalSourceGit: externalSourceInfo[':git'], externalSourceTag: externalSourceInfo[':tag'], externalSourceCommit: externalSourceInfo[':commit'], externalSourceBranch: externalSourceInfo[':branch'] });
109 }
110 const checkoutOptions = this.checkoutOptionsForPod(podName);
111 if (checkoutOptions) {
112 nodeInfoLabels = Object.assign(Object.assign({}, nodeInfoLabels), { checkoutOptionsPodspec: checkoutOptions[':podspec'], checkoutOptionsPath: checkoutOptions[':path'], checkoutOptionsGit: checkoutOptions[':git'], checkoutOptionsTag: checkoutOptions[':tag'], checkoutOptionsCommit: checkoutOptions[':commit'], checkoutOptionsBranch: checkoutOptions[':branch'] });
113 }
114 // Sanitize labels by removing null fields
115 // (as they don't survive a serialization/parse cycle and break tests)
116 Object.entries(nodeInfoLabels).forEach(([key, value]) => {
117 if (value === null || value === undefined) {
118 delete nodeInfoLabels[key];
119 }
120 });
121 return nodeInfoLabels;
122 }
123 /// The checksum of the pod.
124 checksumForPod(podName) {
125 const rootName = utils_1.rootSpecName(podName);
126 return this.internalData['SPEC CHECKSUMS'][rootName];
127 }
128 /// This can be either an URL or the local repository name.
129 repositoryForPod(podName) {
130 // Older Podfile.lock might not have this section yet.
131 const specRepos = this.internalData['SPEC REPOS'];
132 if (!specRepos) {
133 return undefined;
134 }
135 const rootName = utils_1.rootSpecName(podName);
136 const specRepoEntry = Object.entries(specRepos).find(([, deps]) => deps.includes(rootName));
137 if (specRepoEntry) {
138 return specRepoEntry[0];
139 }
140 return undefined;
141 }
142 /// Extracts the external source info for a given pod, if there is any.
143 externalSourceInfoForPod(podName) {
144 // Older Podfile.lock might not have this section yet.
145 const externalSources = this.internalData['EXTERNAL SOURCES'];
146 if (!externalSources) {
147 return undefined;
148 }
149 const externalSourceEntry = externalSources[utils_1.rootSpecName(podName)];
150 if (externalSourceEntry) {
151 return externalSourceEntry;
152 }
153 return undefined;
154 }
155 /// Extracts the checkout options for a given pod, if there is any.
156 checkoutOptionsForPod(podName) {
157 // Older Podfile.lock might not have this section yet.
158 const checkoutOptions = this.internalData['CHECKOUT OPTIONS'];
159 if (!checkoutOptions) {
160 return undefined;
161 }
162 const checkoutOptionsEntry = checkoutOptions[utils_1.rootSpecName(podName)];
163 if (checkoutOptionsEntry) {
164 return checkoutOptionsEntry;
165 }
166 return undefined;
167 }
168 get repositories() {
169 // Older Podfile.lock might not have this section yet.
170 const specRepos = this.internalData['SPEC REPOS'];
171 if (!specRepos) {
172 return [];
173 }
174 return Object.keys(specRepos).map((nameOrUrl) => {
175 return { alias: nameOrUrl };
176 });
177 }
178 get pkgManager() {
179 return {
180 name: 'cocoapods',
181 version: this.cocoapodsVersion,
182 repositories: this.repositories,
183 };
184 }
185 /// The CocoaPods version encoded in the lockfile which was used to
186 /// create this resolution.
187 get cocoapodsVersion() {
188 return this.internalData.COCOAPODS || 'unknown';
189 }
190 /// The checksum of the Podfile, which was used when resolving this integration.
191 /// - Note: this was not tracked by earlier versions of CocoaPods.
192 get podfileChecksum() {
193 return this.internalData['PODFILE CHECKSUM'];
194 }
195}
196exports.default = LockfileParser;
197//# sourceMappingURL=lockfile-parser.js.map
\No newline at end of file