-
Notifications
You must be signed in to change notification settings - Fork 24.4k
/
Copy pathget-and-update-packages.js
170 lines (148 loc) · 5.15 KB
/
get-and-update-packages.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
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
* @oncall react_native
*/
const {publishPackage} = require('../npm-utils');
const {getPackageVersionStrByTag} = require('../npm-utils');
const forEachPackage = require('./for-each-package');
const {writeFileSync} = require('fs');
const path = require('path');
/**
* Get the latest version of each monorepo package and publishes a new package if there have been updates.
* Returns a map of monorepo packages and their latest version.
*
* This is called by `react-native`'s nightly and prealpha job.
* Note: This does not publish `package/react-native`'s nightly/prealpha. That is handled in `publish-npm`.
*/
function getAndUpdatePackages(
version /*: string */,
tag /*: 'nightly' | 'prealpha' */,
) /*: {| [string]: string|}*/ {
const packages = getPackagesToPublish();
updateDependencies(packages, version);
publishPackages(packages, tag);
return reducePackagesToNameVersion(packages);
}
/*::
type PackageMap = {|[string]: PackageMetadata |}
type PackageMetadata = {|
// The absolute path to the package
path: string,
// The parsed package.json contents
packageJson: Object,
|};
*/
/**
* Extract package metadata from the monorepo
* It skips react-native, the packages marked as private and packages not already published on NPM.
*
* @return Dictionary<String, PackageMetadata> where the string is the name of the package and the PackageMetadata
* is an object that contains the absolute path to the package and the packageJson.
*/
function getPackagesToPublish() /*: PackageMap */ {
let packages /*: PackageMap */ = {};
forEachPackage(
(packageAbsolutePath, packageRelativePathFromRoot, packageManifest) => {
if (packageManifest.private || !isPublishedOnNPM(packageManifest.name)) {
console.log(
`\u23F1 Skipping publishing for ${packageManifest.name}. It is either pivate or unpublished`,
);
return;
}
packages[packageManifest.name] = {
path: packageAbsolutePath,
packageJson: packageManifest,
};
},
);
return packages;
}
function isPublishedOnNPM(packageName /*: string */) /*: boolean */ {
try {
getPackageVersionStrByTag(packageName);
return true;
} catch (e) {
return false;
}
}
/**
* Update the dependencies in the packages to the passed version.
* The function update the packages dependencies and devDepenencies if they contains other packages in the monorepo.
* The function also writes the updated package.json to disk.
*/
function updateDependencies(packages /*: PackageMap */, version /*: string */) {
Object.keys(packages).forEach(packageName => {
const package = packages[packageName];
const packageManifest = package.packageJson;
if (packageManifest.dependencies) {
for (const dependency of Object.keys(packageManifest.dependencies)) {
if (packages[dependency]) {
// the dependency is in the monorepo
packages[packageName].packageJson.dependencies[dependency] = version;
}
}
}
if (packageManifest.devDependencies) {
for (const dependency of Object.keys(packageManifest.devDependencies)) {
if (packages[dependency]) {
// the dependency is in the monorepo
packages[packageName].packageJson.devDependencies[dependency] =
version;
}
}
}
packages[packageName].packageJson.version = version;
writeFileSync(
path.join(packages[packageName].path, 'package.json'),
JSON.stringify(packages[packageName].packageJson, null, 2) + '\n',
'utf-8',
);
});
}
/**
* Publish the passed set of packages to npm with the passed tag.
* In case a package fails to be published, it throws an error, stopping the nightly publishing completely
*/
function publishPackages(
packages /*: PackageMap */,
tag /*: 'nightly' | 'prealpha' */,
) {
for (const [packageName, packageMetadata] of Object.entries(packages)) {
const packageAbsolutePath = packageMetadata.path;
const version = packageMetadata.packageJson.version;
const result = publishPackage(packageAbsolutePath, {
tags: [tag],
otp: process.env.NPM_CONFIG_OTP,
});
if (result.code !== 0) {
throw new Error(
`\u274c Failed to publish version ${version} of ${packageName}. npm publish exited with code ${result.code}:`,
);
}
console.log(`\u2705 Successfully published new version of ${packageName}`);
}
}
/**
* Reduces the Dictionary<String, PackageMetadata> to a Dictionary<String, String>.
* where the key is the name of the package and the value is the version number.
*
* @return Dictionary<String, String> with package name and the new version number.
*/
function reducePackagesToNameVersion(
packages /*: PackageMap */,
) /*: {| [string]: string |} */ {
return Object.keys(packages).reduce(
(result /*: {| [string]: string |} */, name /*: string */) => {
result[name] = packages[name].packageJson.version;
return result;
},
{},
);
}
module.exports = getAndUpdatePackages;