forked from meteor/meteor
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpackage-map.js
276 lines (249 loc) · 9.36 KB
/
package-map.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
var _ = require('underscore');
var packageVersionParser = require('./package-version-parser.js');
var utils = require('./utils.js');
// PackageMap: Represents the choices of package versions being used for a
// project. It knows all the packages that are used (direct and indirect
// dependencies), their versions, whether they are local or versioned packages,
// and the PackageSource object for any local packages. Prefer using this
// function over arbitrary JSON representations when possible. (A related class
// is projectContextModule.PackageMapFile which specifically represents the
// .meteor/packages file on disk.)
//
// It has a corresponding JSON format (used, eg, inside buildinfo files).
//
// If you specify the localCatalog option to the constructor, any package in
// that localCatalog will be considered to be local, and all others will be
// considered to be prebuilt versioned packages from troposphere. If you do not
// specify the localCatalog option, all packages will be considered to prebuilt
// versioned packages.
exports.PackageMap = function (versions, options) {
var self = this;
options = options || {};
self._map = {};
self._localCatalog = options.localCatalog || null;
_.each(versions, function (version, packageName) {
var packageSource = self._localCatalog &&
self._localCatalog.getPackageSource(packageName);
if (packageSource) {
self._map[packageName] =
{ kind: 'local', version: version, packageSource: packageSource };
} else {
self._map[packageName] =
{ kind: 'versioned', version: version, packageSource: null };
}
});
};
_.extend(exports.PackageMap.prototype, {
eachPackage: function (iterator) {
var self = this;
_.each(self._map, function (info, packageName) {
// For reasons that are super unclear, if this `_.clone` is inlined into
// the `iterator` call, the value produced can mysteriously turn into
// undefined on the way into `iterator`. Presumably some sort of memory
// corruption, maybe Fiber-related? Trying to minimize has been an
// exercise in nondeterminism. But this does seem to be a sure-fire way to
// fix it, for now. Who knows why, and who knows when it will recur again.
var infoClone = _.clone(info);
iterator(packageName, infoClone);
});
},
getInfo: function (packageName) {
var self = this;
if (_.has(self._map, packageName))
return self._map[packageName];
return null;
},
makeSubsetMap: function (packageNames) {
var self = this;
var subsetVersions = {};
_.each(packageNames, function (packageName) {
var info = self.getInfo(packageName);
if (!info)
throw Error("not a subset: " + packageName);
subsetVersions[packageName] = info.version;
});
return new exports.PackageMap(subsetVersions, {
localCatalog: self._localCatalog
});
},
toJSON: function () {
var self = this;
var ret = {};
_.each(self._map, function (info, packageName) {
if (info.kind === 'local') {
ret[packageName] = {
kind: 'local',
sourceRoot: info.packageSource.sourceRoot
};
} else {
ret[packageName] = {
kind: 'versioned',
version: info.version
};
}
});
return ret;
},
// Returns a map from package name to version. In most cases, this is a far
// worse representation than PackageMap... avoid using it!
toVersionMap: function () {
var self = this;
var ret = {};
_.each(self._map, function (info, packageName) {
ret[packageName] = info.version;
});
return ret;
},
// Given some JSON as returned from toJSON, returns true if every package in
// the JSON has the same mapping as in this map.
isSupersetOfJSON: function (mapJSON) {
var self = this;
return _.all(mapJSON, function (jsonInfo, packageName) {
var thisInfo = self.getInfo(packageName);
if (! thisInfo)
return false;
if (jsonInfo.kind !== thisInfo.kind)
return false;
if (thisInfo.kind === 'local') {
return thisInfo.packageSource.sourceRoot === jsonInfo.sourceRoot;
} else {
return thisInfo.version === jsonInfo.version;
}
});
}
});
// Static method: returns a PackageMap that represents a (catalog)
// ReleaseVersion entry (including its tool). Note that this function assumes
// that all packages will be prebuilt versioned, not local. This is mostly used
// to create PackageMaps to pass to tropohouse.downloadPackagesMissingFromMap;
// it should not be used as part of a ProjectContext because it does not allow
// you to override release packages with local packages.
exports.PackageMap.fromReleaseVersion = function (releaseVersion) {
var toolPackageVersion = releaseVersion.tool &&
utils.parsePackageAtVersion(releaseVersion.tool);
if (!toolPackageVersion)
throw new Error("bad tool in release: " + releaseVersion.tool);
var toolPackage = toolPackageVersion.package;
var toolVersion = toolPackageVersion.version;
var versionMap = _.clone(releaseVersion.packages || {});
versionMap[toolPackage] = toolVersion;
// As described in this function's description, all packages in this map are
// versioned, so we do not specify a localCatalog.
return new exports.PackageMap(versionMap);
};
// PackageMapDelta: represents the change in a PackageMap between two constraint
// solver runs.
exports.PackageMapDelta = function (options) {
var self = this;
self._changedPackages = {};
options.packageMap.eachPackage(function (packageName, info) {
var oldVersion = _.has(options.cachedVersions, packageName)
? options.cachedVersions[packageName] : null;
self._storeAddOrChange(
packageName, info, oldVersion, options.anticipatedPrereleases,
options.neededToUseUnanticipatedPrereleases);
});
_.each(options.cachedVersions, function (oldVersion, packageName) {
if (! options.packageMap.getInfo(packageName)) {
self._storeRemove(packageName, oldVersion);
}
});
};
_.extend(exports.PackageMapDelta.prototype, {
_storeAddOrChange: function (packageName, newInfo, oldVersion,
anticipatedPrereleases,
neededToUseUnanticipatedPrereleases) {
var self = this;
// Store nothing if nothing has changed.
if (newInfo.version === oldVersion)
return;
var backwardsIncompatible =
oldVersion !== null &&
(packageVersionParser.majorVersion(newInfo.version) !==
packageVersionParser.majorVersion(oldVersion));
var isPrerelease = /-/.test(newInfo.version);
var isAnticipatedPrerelease = _.has(anticipatedPrereleases, packageName) &&
_.has(anticipatedPrereleases[packageName], newInfo.version);
self._changedPackages[packageName] = {
oldVersion: oldVersion,
newVersion: newInfo.version,
isBackwardsIncompatible: backwardsIncompatible,
isUnanticipatedPrerelease: (neededToUseUnanticipatedPrereleases &&
isPrerelease && !isAnticipatedPrerelease)
};
},
_storeRemove: function (packageName, oldVersion) {
var self = this;
self._changedPackages[packageName] = {
oldVersion: oldVersion,
newVersion: null
};
},
eachChangedPackage: function (iterator) {
var self = this;
_.each(self._changedPackages, function (info, packageName) {
iterator(packageName, _.clone(info));
});
},
hasChanges: function () {
var self = this;
return ! _.isEmpty(self._changedPackages);
},
displayOnConsole: function (options) {
var self = this;
options = _.extend({
title: "Changes to your project's package version selections:"
}, options);
// Print nothing at all if nothing changed.
if (! self.hasChanges())
return;
var displayItems = [];
var anyBackwardsIncompatible = false;
var anyUnanticipatedPrerelease = false;
self.eachChangedPackage(function (packageName, info) {
if (info.newVersion === null) {
displayItems.push({
name: packageName,
description: "removed from your project"
});
return;
}
var name = packageName;
if (info.isBackwardsIncompatible) {
name += '*';
anyBackwardsIncompatible = true;
}
if (info.isUnanticipatedPrerelease) {
name += '+';
anyUnanticipatedPrerelease = true;
}
var description;
if (info.oldVersion === null) {
description = "added, version " + info.newVersion;
} else if (packageVersionParser.lessThan(info.oldVersion,
info.newVersion)) {
description =
"upgraded from " + info.oldVersion + " to " + info.newVersion;
} else {
description =
"downgraded from " + info.oldVersion + " to " + info.newVersion;
}
displayItems.push({ name: name, description: description });
});
var Console = require('./console.js').Console;
Console.info();
Console.info(options.title);
Console.info();
utils.printPackageList(displayItems);
if (anyBackwardsIncompatible) {
Console.info("\n" +
"* These packages have been updated to new versions that are not backwards\n" +
" compatible.");
}
if (anyUnanticipatedPrerelease) {
Console.info("\n" +
"+ In order to resolve constraints, we had to use experimental versions of these\n" +
" packages.");
}
}
});