Skip to content

Commit fd27d68

Browse files
committed
Adds topcoder-lib-setup script
1 parent f1cd0ae commit fd27d68

File tree

4 files changed

+217
-14
lines changed

4 files changed

+217
-14
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Topcoder React Utils Changelog
22

3+
### v0.4.1
4+
Adds `topcoder-lib-setup` script, which should help to update our ReactJS
5+
libraries in the host packages. Not mentioned in the docs as it still pending
6+
to be tested in prod.
7+
38
### v0.4.0
49
- Updates many dependencies to the latest versions. Presumably, should not
510
introduce breaking changes, but, just in case, tagged as a minor update.

bin/topcoder-lib-setup

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
#!/usr/bin/env node
2+
3+
/* Auxiliary script that helps to install and upgrade Topcoder libraries
4+
* published to NPM. */
5+
/* eslint-disable import/no-extraneous-dependencies, no-console */
6+
7+
const commandLineArgs = require('command-line-args');
8+
const commandLineUsage = require('command-line-usage');
9+
const fs = require('fs');
10+
const path = require('path');
11+
const { spawnSync } = require('child_process');
12+
13+
/* Definition of command-line arguments. */
14+
const OPTIONS = [{
15+
name: 'help',
16+
alias: 'h',
17+
description: 'Shows usage instructions',
18+
type: Boolean,
19+
}, {
20+
name: 'libraries',
21+
defaultOption: true,
22+
multiple: true,
23+
type: String,
24+
}];
25+
26+
/* Definition of help information. */
27+
const HELP = [{
28+
content: 'Usage: {cyanBright topcoder-lib-setup [OPTION]... LIBRARY...}',
29+
}, {
30+
header: 'Description',
31+
content:
32+
`Installs or upgrades Topcoder's ReactJS libraries released to NPM.
33+
{cyanBright [OPTION]...} is the list of options (see below);
34+
{cyanBright LIBRARY...} is the list of libraries to install/upgrade`,
35+
}, {
36+
header: 'Options',
37+
optionList: OPTIONS.filter(x => !x.defaultOption),
38+
}];
39+
40+
/**
41+
* Generates a string containing name and version of the package to be
42+
* installed.
43+
* @param {Array} entry Array with package name as the first element, and
44+
* corresponding version or URI given in a `package.json`.
45+
* @return {String} Package name and version as a string that can be passed
46+
* into NPM's install command.
47+
*/
48+
function generateTargetPackage(entry) {
49+
if (entry[1].match(/^[^]/)) return `${entry[0]}@${entry[1].slice(1)}`;
50+
return `${entry[0]}@${entry[1]}`;
51+
}
52+
53+
/**
54+
* Adopts dev dependencies of the `donor` package into the `host` one.
55+
* @param {Object} donorData Data from donor's `package.json`.
56+
* @param {Object} hostData Data from host's `package.json`.
57+
*/
58+
function adoptDevDependencies(donorData) {
59+
let deps = Object.entries(donorData.devDependencies || {});
60+
deps = deps.map(generateTargetPackage);
61+
spawnSync('npm', ['install', '--save-dev'].concat(deps), {
62+
stdio: 'inherit',
63+
});
64+
}
65+
66+
/**
67+
* Locates and loads `package.json` of the host package (assumed to be inside
68+
* the current working directory).
69+
* @return {Object} Data from `package.json` parsed into JSON.
70+
*/
71+
function getHostPackageJson() {
72+
const url = path.resolve(process.cwd(), 'package.json');
73+
return JSON.parse(fs.readFileSync(url));
74+
}
75+
76+
/**
77+
* Locates and loads `package.json` file of the specified package.
78+
* @param {String} package Package name.
79+
* @return {Object} Data from `package.json` parsed into JSON.
80+
*/
81+
function getPackageJson(packageName = 'topcoder-react-utils') {
82+
let url = packageName === 'topcoder-react-utils' ? '..' : packageName;
83+
url = path.dirname(require.resolve(url));
84+
for (;;) {
85+
const files = fs.readdirSync(url);
86+
if (files.includes('package.json')) {
87+
url = path.resolve(url, 'package.json');
88+
break;
89+
}
90+
const up = path.resolve(url, '..');
91+
if (url === up) throw new Error(`Cannot find the package ${packageName}`);
92+
url = up;
93+
}
94+
return JSON.parse(fs.readFileSync(url));
95+
}
96+
97+
/**
98+
* Installs specified library.
99+
* @param {String} library Library name.
100+
*/
101+
function install(library) {
102+
let name = library;
103+
if (name.indexOf('@') <= 0) name += '@latest';
104+
spawnSync('npm', ['install', '--save', name], { stdio: 'inherit' });
105+
}
106+
107+
/**
108+
* Outputs help information to console.
109+
*/
110+
function showHelp() {
111+
console.log(commandLineUsage(HELP));
112+
}
113+
114+
/**
115+
* Updates prod dependencies of `host` package that are also prod dependencies
116+
* of `donor` to the same versions specified in the donor's `package.json`.
117+
* @param {Object} donorData Data from donor's `package.json`.
118+
* @param {Object} hostData Data from host's `package.json`.
119+
*/
120+
function updateProdDependencies(donorData, hostData) {
121+
let deps = Object.entries(donorData.dependencies || {});
122+
const hostDeps = hostData.dependencies || {};
123+
deps = deps.filter(x => hostDeps[x[0]]);
124+
deps = deps.map(generateTargetPackage);
125+
spawnSync('npm', ['install', '--save'].concat(deps), { stdio: 'inherit' });
126+
}
127+
128+
/* Entry point. */
129+
130+
const options = commandLineArgs(OPTIONS);
131+
if (!options.libraries) {
132+
options.libraries = ['topcoder-react-utils'];
133+
}
134+
135+
if (options.help) showHelp();
136+
else {
137+
const hostData = getHostPackageJson();
138+
options.libraries.forEach((library) => {
139+
install(library);
140+
const libData = getPackageJson(library);
141+
adoptDevDependencies(libData, hostData);
142+
updateProdDependencies(libData, hostData);
143+
});
144+
spawnSync('npm', ['install'], { stdio: 'inherit' });
145+
}

package-lock.json

Lines changed: 64 additions & 13 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@
5555
"babel-preset-env": "^1.7.0",
5656
"babel-preset-react": "^6.24.1",
5757
"babel-preset-stage-2": "^6.24.1",
58+
"command-line-args": "^5.0.2",
59+
"command-line-usage": "^5.0.5",
5860
"css-loader": "^0.28.11",
5961
"eslint": "^4.19.1",
6062
"eslint-config-airbnb": "^16.1.0",
@@ -115,5 +117,5 @@
115117
"prepublishOnly": "npm run build",
116118
"test": "npm run lint && npm run jest"
117119
},
118-
"version": "0.4.0"
120+
"version": "0.4.1"
119121
}

0 commit comments

Comments
 (0)