1 | ;
|
2 |
|
3 | // The adapter includes typings for the lowest supported NodeJS version.
|
4 | // This script updates them to the installed version
|
5 |
|
6 | const { spawn } = require('child_process');
|
7 | const semver = require('semver');
|
8 | const installedNodeVersion = semver.coerce(process.versions.node).major;
|
9 |
|
10 | function fail(reason) {
|
11 | console.error('Could not install NodeJS typings. This is not critical.');
|
12 | console.error('Reason: \n' + reason);
|
13 | // This is not critical!
|
14 | process.exit(0);
|
15 | }
|
16 |
|
17 | // Find latest version on npm
|
18 | console.log('Installing NodeJS typings...');
|
19 | npmCommand('view', ['@types/node', 'version'], {stdout: 'pipe', stderr: 'pipe'})
|
20 | .then(cmdResult => {
|
21 | if (cmdResult.exitCode !== 0) {
|
22 | return fail(cmdResult.stderr);
|
23 | }
|
24 | const latestVersion = semver.coerce(cmdResult.stdout).major;
|
25 | console.log(`latest @types: ${latestVersion}, installed node: ${installedNodeVersion}`);
|
26 | return semver.gt(`${installedNodeVersion}.0.0`, `${latestVersion}.0.0`)
|
27 | ? 'latest' // The installed version is too new, install latest
|
28 | : installedNodeVersion.toString()
|
29 | ;
|
30 | })
|
31 | .then(targetVersion => {
|
32 | // Install the desired version
|
33 | return npmCommand('i', [`@types/node@${targetVersion}`], {stdout: 'ignore', stderr: 'pipe'});
|
34 | })
|
35 | .then(cmdResult => {
|
36 | if (cmdResult.exitCode !== 0) {
|
37 | return fail(cmdResult.stderr);
|
38 | } else {
|
39 | process.exit(0);
|
40 | }
|
41 | });
|
42 |
|
43 | // TODO: the following code is copied from a js-controller fork
|
44 | // It should be moved to the core and referenced from there in a future version
|
45 |
|
46 | /**
|
47 | * @typedef {object} NpmCommandOptions
|
48 | * @property {string} cwd The directory to execute the command in
|
49 | * @property {NodeJS.ReadStream} stdin Where to redirect the stdin. Default: process.stdin
|
50 | * @property {NodeJS.WriteStream | "pipe" | "ignore"} stdout A write stream to redirect the stdout, "ignore" to ignore it or "pipe" to return it as a string. Default: process.stdout
|
51 | * @property {NodeJS.WriteStream | "pipe" | "ignore"} stderr A write stream to redirect the stderr, "ignore" to ignore it or "pipe" to return it as a string. Default: process.stderr
|
52 | */
|
53 |
|
54 | /**
|
55 | * @typedef {object} NpmCommandResult
|
56 | * @property {number} exitCode - The exit code of the spawned process
|
57 | * @property {string?} signal - The signal the process received before termination
|
58 | * @property {string?} stdout - If options.stdout was set to "buffer", this contains the stdout of the spawned process
|
59 | * @property {string?} stderr - If options.stderr was set to "buffer", this contains the stderr of the spawned process
|
60 | */
|
61 |
|
62 | /**
|
63 | * Executes an npm command (e.g. install) and returns the exit code and (if requested) the stdout
|
64 | * @param {string} command The npm command to execute
|
65 | * @param {string[]} [npmArgs] The command line arguments for the npm command
|
66 | * @param {Partial<NpmCommandOptions>} [options] (optional) Some options for the command execution
|
67 | * @returns {Promise<NpmCommandResult>}
|
68 | */
|
69 | function npmCommand(command, npmArgs, options) {
|
70 | if (typeof npmArgs === 'object' && !Array.isArray(npmArgs)) {
|
71 | // no args were given
|
72 | options = npmArgs;
|
73 | npmArgs = undefined;
|
74 | }
|
75 | if (options == null) options = {};
|
76 | if (npmArgs == null) npmArgs = [];
|
77 |
|
78 | const npmBinary = /^win/.test(process.platform) ? 'npm.cmd' : 'npm';
|
79 | /** @type {import("child_process").SpawnOptions} */
|
80 | const spawnOptions = {
|
81 | stdio: [
|
82 | options.stdin || process.stdin,
|
83 | options.stdout || process.stdout,
|
84 | options.stderr || process.stderr,
|
85 | ],
|
86 | // @ts-ignore This option exists starting with NodeJS 8
|
87 | windowsHide: true,
|
88 | };
|
89 | if (options.cwd != null) spawnOptions.cwd = options.cwd;
|
90 |
|
91 | // Now execute the npm process and avoid throwing errors
|
92 | return new Promise((resolve) => {
|
93 | try {
|
94 | /** @type {string} */
|
95 | let bufferedStdout;
|
96 | /** @type {string} */
|
97 | let bufferedStderr;
|
98 | const cmd = spawn(npmBinary, [command].concat(npmArgs), spawnOptions)
|
99 | .on('close', (code, signal) => {
|
100 | resolve({
|
101 | exitCode: code,
|
102 | signal,
|
103 | stdout: bufferedStdout,
|
104 | stderr: bufferedStderr
|
105 | });
|
106 | });
|
107 | // Capture stdout/stderr if requested
|
108 | if (options.stdout === 'pipe') {
|
109 | bufferedStdout = '';
|
110 | cmd.stdout.on('data', chunk => {
|
111 | const buffer = Buffer.isBuffer(chunk)
|
112 | ? chunk
|
113 | : new Buffer(chunk, 'utf8')
|
114 | ;
|
115 | bufferedStdout += buffer;
|
116 | });
|
117 | }
|
118 | if (options.stderr === 'pipe') {
|
119 | bufferedStderr = '';
|
120 | cmd.stderr.on('data', chunk => {
|
121 | const buffer = Buffer.isBuffer(chunk)
|
122 | ? chunk
|
123 | : new Buffer(chunk, 'utf8')
|
124 | ;
|
125 | bufferedStderr += buffer;
|
126 | });
|
127 | }
|
128 | } catch (e) {
|
129 | // doesn't matter, we return the exit code in the "close" handler
|
130 | }
|
131 | });
|
132 | }
|