-
Notifications
You must be signed in to change notification settings - Fork 133
/
Copy pathprotoc.js
executable file
·137 lines (113 loc) · 4.75 KB
/
protoc.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
#!/usr/bin/env node
// Automatically installs protoc if not found on $PATH, then
// runs it transiently.
const {spawnSync} = require('child_process');
const path = require('path');
const fs = require('fs');
const os = require('os');
const {findProtocVersionConfig, listInstalled, findProtocPlugins, findProtobufTs, unzip, makeReleaseName, httpGetRedirect, httpDownload, mkDirRecursive, findProtocInPath, standardInstallDirectory} = require('./util');
main().catch(err => {
console.error((err instanceof Error) ? err.message : err);
process.exit(1);
});
async function main() {
// the full path to the protoc executable
let command;
// the full path to the include files of a protoc release (well-known-types)
let includePath;
// does the nearest package.json have a config.protocVersion?
const configuredVersion = findProtocVersionConfig(process.cwd());
if (configuredVersion) {
// we prefer the configured protoc version and install it
let release = await ensureInstalled(configuredVersion);
command = release.protocPath;
includePath = release.includePath;
} else {
// there is no configured protoc version. do we have protoc in the $PATH?
command = findProtocInPath(process.env.PATH)
if (!command) {
// no protoc in $PATH, install the latest version
let release = await ensureInstalled(configuredVersion);
command = release.protocPath;
includePath = release.includePath;
}
}
let args = [
// pass all arguments to the process
...process.argv.slice(2),
];
if (includePath) {
// add the "include" directory of the installed protoc to the proto path
// do this last, otherwise it can shadow a user input
args.push("--proto_path", includePath);
}
// search for @protobuf-ts/plugin in node_modules and add --proto_path argument
let protobufTs = findProtobufTs(process.cwd());
if (protobufTs) {
args.push("--proto_path", protobufTs);
}
// search for any protoc-gen-xxx plugins in .bin and add --plugin arguments for them
for (let plugin of findProtocPlugins(process.cwd())) {
args.unshift("--plugin", plugin);
}
let child = spawnSync(command, args, {
// protoc accepts stdin for some commands, pipe all IO
stdio: [process.stdin, process.stdout, process.stderr],
shell: false
});
if (child.error) {
throw new Error("@protobuf-ts/protoc was unable to spawn protoc. " + child.error);
}
process.exit(child.status);
}
async function ensureInstalled(version) {
// resolve the latest release version number if necessary
if (version === "latest" || version === undefined) {
let latestLocation;
try {
latestLocation = await httpGetRedirect("https://github.com/protocolbuffers/protobuf/releases/latest");
} catch (e) {
throw new Error(`@protobuf-ts/protoc failed to retrieve latest protoc version number: ${e}`);
}
version = latestLocation.split("/v").pop();
}
// make the release name for the current platform and the requested version number
let releaseName = makeReleaseName({
platform: os.platform(),
arch: os.arch(),
version: version
});
// if this release is already installed, we are done here
let alreadyInstalled = listInstalled().find(i => i.name === releaseName);
if (alreadyInstalled) {
return alreadyInstalled;
}
// download the release
let archive;
try {
archive = await httpDownload(`https://github.com/protocolbuffers/protobuf/releases/download/v${version}/${releaseName}.zip`);
} catch (e) {
throw new Error(`@protobuf-ts/protoc failed to download protoc v${version}. \nDid you misspell the version number? The version number must look like "3.0.12", without a leading "v".\n${e}`);
}
// unzip the archive
let archivePath = path.join(standardInstallDirectory, releaseName);
try {
unzip(archive, (data, header) => {
let filename = path.join(archivePath, header.filename);
mkDirRecursive(path.dirname(filename));
fs.writeFileSync(filename, data, {
mode: header.filename.includes("bin/") ? 0o755 : 0o666
});
});
} catch (e) {
throw new Error(`@protobuf-ts/protoc failed unzip the downloaded protoc release v${version}: ${e}`);
}
// sanity check
let installed = listInstalled().find(i => i.name === releaseName);
if (!installed) {
throw new Error(`@protobuf-ts/protoc failed to install protoc v${version}.`);
}
// finished
console.info(`@protobuf-ts/protoc installed protoc v${installed.version}.`);
return installed;
}