Skip to content

Commit 1a8d73d

Browse files
committed
Add postinstall hook for fetching official protoc-gen-js binary
1 parent cbeded2 commit 1a8d73d

File tree

7 files changed

+203
-5
lines changed

7 files changed

+203
-5
lines changed

MODULE.bazel.lock

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

bin/protoc-gen-js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#!/usr/bin/env node
2+
3+
console.error(
4+
'The protoc-gen-js binary could not be found. This is likely because you ' +
5+
'installed google-protobuf with the --omit=optional flag.'
6+
);
7+
console.error(
8+
'To fix this, please reinstall google-protobuf without that flag.'
9+
);
10+
process.exit(1);

download-protoc-gen-js.js

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
const fs = require('fs');
2+
const os = require('os');
3+
const path = require('path');
4+
5+
const isGlobal = process.env.npm_config_global === 'true';
6+
7+
let AdmZip;
8+
try {
9+
AdmZip = require('adm-zip');
10+
} catch (e) {
11+
if (isGlobal) {
12+
console.warn(
13+
'Skipping protoc-gen-js download due to --omit=optional in global install.'
14+
);
15+
console.warn(
16+
'If you have a system-installed protoc-gen-js, it will be preserved.'
17+
);
18+
// On global installs, we refuse to overwrite a potentially real binary
19+
// with our placeholder. In this case, we must delete our placeholder.
20+
const placeholderPath = path.join(__dirname, 'bin', 'protoc-gen-js');
21+
if (fs.existsSync(placeholderPath)) {
22+
fs.unlinkSync(placeholderPath);
23+
}
24+
} else {
25+
// For local installs, the placeholder script is fine. It's sandboxed
26+
// to the project's node_modules/.bin and provides a helpful error.
27+
console.warn(
28+
'adm-zip not found, skipping binary download. The protoc-gen-js command will show a help message.'
29+
);
30+
}
31+
process.exit(0);
32+
}
33+
34+
const VERSION = '4.0.0';
35+
const BIN_DIR = path.join(__dirname, 'bin');
36+
37+
function getPlatform() {
38+
const platform = os.platform();
39+
if (platform === 'darwin') {
40+
return 'osx';
41+
}
42+
if (platform === 'win32') {
43+
return 'windows';
44+
}
45+
return platform;
46+
}
47+
48+
function getArch() {
49+
const arch = os.arch();
50+
if (arch === 'x64') {
51+
return 'x86_64';
52+
}
53+
if (arch === 'arm64') {
54+
return 'aarch64';
55+
}
56+
return arch;
57+
}
58+
59+
function getDownloadUrl() {
60+
const platform = getPlatform();
61+
const arch = getArch();
62+
const supportedPlatforms = {
63+
linux: ['x86_64', 'aarch64'],
64+
osx: ['x86_64', 'aarch64'],
65+
windows: ['x86_64'],
66+
};
67+
68+
if (
69+
!supportedPlatforms[platform] ||
70+
!supportedPlatforms[platform].includes(arch)
71+
) {
72+
console.error(`Unsupported platform: ${platform}-${arch}`);
73+
process.exit(1);
74+
}
75+
76+
return `https://github.com/protocolbuffers/protobuf-javascript/releases/download/v${VERSION}/protobuf-javascript-${VERSION}-${platform}-${arch}.zip`;
77+
}
78+
79+
function unzip(zipFile, destDir, binaryName) {
80+
return new Promise((resolve, reject) => {
81+
const binaryPathInZip = `bin/${binaryName}`;
82+
const zip = new AdmZip(zipFile);
83+
const zipEntries = zip.getEntries();
84+
let found = false;
85+
86+
for (const entry of zipEntries) {
87+
const entryFileName = entry.entryName.replace(/\\/g, '/');
88+
if (entryFileName.endsWith(binaryPathInZip)) {
89+
found = true;
90+
fs.writeFile(path.join(destDir, binaryName), entry.getData(), (err) => {
91+
if (err) {
92+
reject(err);
93+
} else {
94+
resolve();
95+
}
96+
});
97+
break;
98+
}
99+
}
100+
101+
if (!found) {
102+
reject(new Error(`Binary ${binaryPathInZip} not found in zip file.`));
103+
}
104+
});
105+
}
106+
107+
async function main() {
108+
try {
109+
const downloadUrl = getDownloadUrl();
110+
const zipFile = path.join(__dirname, 'google-protobuf.zip');
111+
const binaryName =
112+
os.platform() === 'win32' ? 'protoc-gen-js.exe' : 'protoc-gen-js';
113+
114+
await fs.promises.mkdir(BIN_DIR, {recursive: true});
115+
116+
console.log(`Downloading ${downloadUrl}`);
117+
const response = await fetch(downloadUrl);
118+
if (!response.ok) {
119+
throw new Error(
120+
`Failed to download: ${response.status} ${response.statusText}`
121+
);
122+
}
123+
const buffer = await response.arrayBuffer();
124+
await fs.promises.writeFile(zipFile, Buffer.from(buffer));
125+
126+
console.log('Unzipping...');
127+
await unzip(zipFile, BIN_DIR, binaryName);
128+
129+
await fs.promises.unlink(zipFile);
130+
131+
const binaryPath = path.join(BIN_DIR, binaryName);
132+
133+
console.log(`Making ${binaryPath} executable...`);
134+
await fs.promises.chmod(binaryPath, 0o755);
135+
136+
console.log('Done!');
137+
} catch (err) {
138+
console.error(`Failed to download protoc-gen-js: ${err.message}`);
139+
process.exit(1);
140+
}
141+
}
142+
143+
main();

package-lock.json

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

package.json

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,23 @@
33
"version": "4.0.0",
44
"description": "Protocol Buffers for JavaScript",
55
"main": "google-protobuf.js",
6+
"bin": {
7+
"protoc-gen-js": "bin/protoc-gen-js"
8+
},
69
"files": [
710
"google/protobuf/*_pb.js",
811
"google/protobuf/compiler/*_pb.js",
912
"google-protobuf.js",
1013
"LICENSE.md",
1114
"LICENSE-asserts.md",
1215
"package.json",
13-
"README.md"
16+
"README.md",
17+
"download-protoc-gen-js.js",
18+
"bin/protoc-gen-js"
1419
],
20+
"optionalDependencies": {
21+
"adm-zip": "^0.5.16"
22+
},
1523
"devDependencies": {
1624
"glob": "~7.1.4",
1725
"google-closure-compiler": "20230802.0.0",
@@ -23,7 +31,8 @@
2331
"scripts": {
2432
"build": "node ./node_modules/gulp/bin/gulp.js dist",
2533
"test": "node ./node_modules/gulp/bin/gulp.js test",
26-
"clean": "node ./node_modules/gulp/bin/gulp.js clean"
34+
"clean": "node ./node_modules/gulp/bin/gulp.js clean",
35+
"postinstall": "node download-protoc-gen-js.js"
2736
},
2837
"repository": {
2938
"type": "git",

postinstall.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
2+
const { spawn } = require('child_process');
3+
4+
const child = spawn('node', ['download-protoc-gen-js.js'], {
5+
stdio: 'inherit'
6+
});
7+
8+
child.on('close', (code) => {
9+
if (code !== 0) {
10+
console.error('Failed to download protoc-gen-js');
11+
process.exit(1);
12+
}
13+
});

yarn.lock

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@
1414
dependencies:
1515
is-negated-glob "^1.0.0"
1616

17+
adm-zip@^0.5.16:
18+
version "0.5.16"
19+
resolved "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz"
20+
integrity sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==
21+
1722
ansi-regex@^5.0.1:
1823
version "5.0.1"
1924
resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz"

0 commit comments

Comments
 (0)