From b54b7db39c1e384c81cf10c9c5e0c25f10027e47 Mon Sep 17 00:00:00 2001 From: Stephanie DiBenedetto Date: Fri, 10 Oct 2025 14:39:59 -0700 Subject: [PATCH] add standlone protoc-gen-js npm package (@protocolbuffers/protoc-gen-js) for convenience --- MODULE.bazel.lock | 8 +- protoc_plugin/.gitignore | 3 + protoc_plugin/LICENSE.md | 29 +++++++ protoc_plugin/bin/.gitkeep | 0 protoc_plugin/cli.js | 8 ++ protoc_plugin/download-protoc-gen-js.js | 111 ++++++++++++++++++++++++ protoc_plugin/index.js | 5 ++ protoc_plugin/package-lock.json | 32 +++++++ protoc_plugin/package.json | 25 ++++++ 9 files changed, 218 insertions(+), 3 deletions(-) create mode 100644 protoc_plugin/.gitignore create mode 100644 protoc_plugin/LICENSE.md create mode 100644 protoc_plugin/bin/.gitkeep create mode 100644 protoc_plugin/cli.js create mode 100644 protoc_plugin/download-protoc-gen-js.js create mode 100644 protoc_plugin/index.js create mode 100644 protoc_plugin/package-lock.json create mode 100644 protoc_plugin/package.json diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index 86b517a..fc6a748 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -15,7 +15,8 @@ "https://bcr.bazel.build/modules/abseil-cpp/20250512.1/source.json": "d725d73707d01bb46ab3ca59ba408b8e9bd336642ca77a2269d4bfb8bbfd413d", "https://bcr.bazel.build/modules/apple_support/1.11.1/MODULE.bazel": "1843d7cd8a58369a444fc6000e7304425fba600ff641592161d9f15b179fb896", "https://bcr.bazel.build/modules/apple_support/1.15.1/MODULE.bazel": "a0556fefca0b1bb2de8567b8827518f94db6a6e7e7d632b4c48dc5f865bc7c85", - "https://bcr.bazel.build/modules/apple_support/1.15.1/source.json": "517f2b77430084c541bc9be2db63fdcbb7102938c5f64c17ee60ffda2e5cf07b", + "https://bcr.bazel.build/modules/apple_support/1.23.1/MODULE.bazel": "53763fed456a968cf919b3240427cf3a9d5481ec5466abc9d5dc51bc70087442", + "https://bcr.bazel.build/modules/apple_support/1.23.1/source.json": "d888b44312eb0ad2c21a91d026753f330caa48a25c9b2102fae75eb2b0dcfdd2", "https://bcr.bazel.build/modules/bazel_features/1.1.1/MODULE.bazel": "27b8c79ef57efe08efccbd9dd6ef70d61b4798320b8d3c134fd571f78963dbcd", "https://bcr.bazel.build/modules/bazel_features/1.11.0/MODULE.bazel": "f9382337dd5a474c3b7d334c2f83e50b6eaedc284253334cf823044a26de03e8", "https://bcr.bazel.build/modules/bazel_features/1.15.0/MODULE.bazel": "d38ff6e517149dc509406aca0db3ad1efdd890a85e049585b7234d04238e2a4d", @@ -24,6 +25,7 @@ "https://bcr.bazel.build/modules/bazel_features/1.19.0/MODULE.bazel": "59adcdf28230d220f0067b1f435b8537dd033bfff8db21335ef9217919c7fb58", "https://bcr.bazel.build/modules/bazel_features/1.21.0/MODULE.bazel": "675642261665d8eea09989aa3b8afb5c37627f1be178382c320d1b46afba5e3b", "https://bcr.bazel.build/modules/bazel_features/1.23.0/MODULE.bazel": "fd1ac84bc4e97a5a0816b7fd7d4d4f6d837b0047cf4cbd81652d616af3a6591a", + "https://bcr.bazel.build/modules/bazel_features/1.27.0/MODULE.bazel": "621eeee06c4458a9121d1f104efb80f39d34deff4984e778359c60eaf1a8cb65", "https://bcr.bazel.build/modules/bazel_features/1.3.0/MODULE.bazel": "cdcafe83ec318cda34e02948e81d790aab8df7a929cec6f6969f13a489ccecd9", "https://bcr.bazel.build/modules/bazel_features/1.30.0/MODULE.bazel": "a14b62d05969a293b80257e72e597c2da7f717e1e69fa8b339703ed6731bec87", "https://bcr.bazel.build/modules/bazel_features/1.30.0/source.json": "b07e17f067fe4f69f90b03b36ef1e08fe0d1f3cac254c1241a1818773e3423bc", @@ -111,8 +113,8 @@ "https://bcr.bazel.build/modules/rules_java/7.2.0/MODULE.bazel": "06c0334c9be61e6cef2c8c84a7800cef502063269a5af25ceb100b192453d4ab", "https://bcr.bazel.build/modules/rules_java/7.3.2/MODULE.bazel": "50dece891cfdf1741ea230d001aa9c14398062f2b7c066470accace78e412bc2", "https://bcr.bazel.build/modules/rules_java/7.6.1/MODULE.bazel": "2f14b7e8a1aa2f67ae92bc69d1ec0fa8d9f827c4e17ff5e5f02e91caa3b2d0fe", - "https://bcr.bazel.build/modules/rules_java/8.12.0/MODULE.bazel": "8e6590b961f2defdfc2811c089c75716cb2f06c8a4edeb9a8d85eaa64ee2a761", - "https://bcr.bazel.build/modules/rules_java/8.12.0/source.json": "cbd5d55d9d38d4008a7d00bee5b5a5a4b6031fcd4a56515c9accbcd42c7be2ba", + "https://bcr.bazel.build/modules/rules_java/8.14.0/MODULE.bazel": "717717ed40cc69994596a45aec6ea78135ea434b8402fb91b009b9151dd65615", + "https://bcr.bazel.build/modules/rules_java/8.14.0/source.json": "8a88c4ca9e8759da53cddc88123880565c520503321e2566b4e33d0287a3d4bc", "https://bcr.bazel.build/modules/rules_java/8.3.2/MODULE.bazel": "7336d5511ad5af0b8615fdc7477535a2e4e723a357b6713af439fe8cf0195017", "https://bcr.bazel.build/modules/rules_java/8.5.1/MODULE.bazel": "d8a9e38cc5228881f7055a6079f6f7821a073df3744d441978e7a43e20226939", "https://bcr.bazel.build/modules/rules_java/8.6.1/MODULE.bazel": "f4808e2ab5b0197f094cabce9f4b006a27766beb6a9975931da07099560ca9c2", diff --git a/protoc_plugin/.gitignore b/protoc_plugin/.gitignore new file mode 100644 index 0000000..19dc4e7 --- /dev/null +++ b/protoc_plugin/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +bin/protoc-gen-js* +protocolbuffers-protoc-gen-js-*.tgz diff --git a/protoc_plugin/LICENSE.md b/protoc_plugin/LICENSE.md new file mode 100644 index 0000000..2e5a388 --- /dev/null +++ b/protoc_plugin/LICENSE.md @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2025, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/protoc_plugin/bin/.gitkeep b/protoc_plugin/bin/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/protoc_plugin/cli.js b/protoc_plugin/cli.js new file mode 100644 index 0000000..004ec8e --- /dev/null +++ b/protoc_plugin/cli.js @@ -0,0 +1,8 @@ +#!/usr/bin/env node + +const child_process = require("child_process"); +const PLUGIN = require("./"); + +child_process + .spawn(PLUGIN, process.argv.slice(2), { stdio: "inherit" }) + .on("exit", (code) => process.exit(code)); diff --git a/protoc_plugin/download-protoc-gen-js.js b/protoc_plugin/download-protoc-gen-js.js new file mode 100644 index 0000000..2bb89cf --- /dev/null +++ b/protoc_plugin/download-protoc-gen-js.js @@ -0,0 +1,111 @@ +const fs = require('fs'); +const os = require('os'); +const path = require('path'); +const AdmZip = require('adm-zip'); + +const VERSION = '4.0.0'; +const BIN_DIR = path.join(__dirname, 'bin'); + +function getZipFilename(currentSystem) { + return `protobuf-javascript-${VERSION}-${currentSystem}.zip`; +} + +function getDownloadUrl(currentSystem) { + return `https://github.com/protocolbuffers/protobuf-javascript/releases/download/v${VERSION}/${getZipFilename(currentSystem)}`; +} + +function unzip(zipFile, destDir, binaryName) { + return new Promise((resolve, reject) => { + const zip = new AdmZip(zipFile); + + // Paths may be inconsistent between platforms. On MacOS and Windows, + // the path seems to include the archive name while it doesn't on linux. + // There's only one thing named protoc-gen-js(.exe), so just look for + // that instead of trying to get the exact "entryName". + const entries = zip.getEntries(); + let found = false; + for (const entry of entries) { + if (entry.name === binaryName) { + zip.extractEntryTo(entry, destDir, false, true); + found = true; + break; + } + } + + if (found) { + resolve(); + } else { + reject(new Error(`Binary ${binaryName} not found in zip file ${zipFile}`)); + } + }); +} + +function getCurrentSystem() { + const platform = os.platform(); + const arch = os.arch(); + + if (platform === 'darwin') { + if (arch === 'x64') { + return 'osx-x86_64'; + } + if (arch === 'arm64') { + return 'osx-aarch_64'; + } + } else if (platform === 'win32' && arch === 'x64') { + return 'win64'; + } else if (platform === 'linux') { + if (arch === 'x64') { + return 'linux-x86_64'; + } + if (arch === 'arm64') { + return 'linux-aarch_64'; + } + if (arch === 's390x') { + return 'linux-s390_64'; + } + } + + console.error(`Unsupported platform: ${platform} ${arch}`); + process.exit(1); +} + +async function main() { + try { + await fs.promises.mkdir(BIN_DIR, { recursive: true }); + const currentSystem = getCurrentSystem(); + + + const downloadUrl = getDownloadUrl(currentSystem); + const zipFile = path.join(__dirname, getZipFilename(currentSystem)); + const isWindows = os.platform() === 'win32'; + const binaryName = isWindows ? 'protoc-gen-js.exe' : 'protoc-gen-js'; + const binaryPath = path.join(BIN_DIR, binaryName); + + console.log(`Downloading ${downloadUrl}`); + const response = await fetch(downloadUrl); + if (!response.ok) { + throw new Error( + `Failed to download: ${response.status} ${response.statusText}` + ); + } + const buffer = await response.arrayBuffer(); + await fs.promises.writeFile(zipFile, Buffer.from(buffer)); + + console.log('Unzipping...'); + await unzip(zipFile, BIN_DIR, binaryName); + + await fs.promises.unlink(zipFile); + + console.log(`Making ${binaryPath} executable...`); + if (!isWindows) { + await fs.promises.chmod(binaryPath, 0o755); + } + + console.log('Done!'); + } catch (err) { + console.error(`Failed to install protoc-gen-js: ${err.message}`); + process.exit(1); + } +} + +main(); \ No newline at end of file diff --git a/protoc_plugin/index.js b/protoc_plugin/index.js new file mode 100644 index 0000000..663fbb6 --- /dev/null +++ b/protoc_plugin/index.js @@ -0,0 +1,5 @@ +const path = require("path"); +const BIN_DIR = path.resolve(__dirname, "bin"); +const EXT = process.platform === "win32" ? ".exe" : ""; + +module.exports = path.resolve(BIN_DIR, "protoc-gen-js" + EXT); diff --git a/protoc_plugin/package-lock.json b/protoc_plugin/package-lock.json new file mode 100644 index 0000000..22d820d --- /dev/null +++ b/protoc_plugin/package-lock.json @@ -0,0 +1,32 @@ +{ + "name": "@protocolbuffers/protoc-gen-js", + "version": "4.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@protocolbuffers/protoc-gen-js", + "version": "4.0.0", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "adm-zip": "^0.5.16" + }, + "bin": { + "protoc-gen-js": "cli.js" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/adm-zip": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz", + "integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==", + "license": "MIT", + "engines": { + "node": ">=12.0" + } + } + } +} diff --git a/protoc_plugin/package.json b/protoc_plugin/package.json new file mode 100644 index 0000000..460e538 --- /dev/null +++ b/protoc_plugin/package.json @@ -0,0 +1,25 @@ +{ + "name": "@protocolbuffers/protoc-gen-js", + "version": "4.0.0", + "description": "Standalone distribution of the protoc-gen-js plugin for Protocol Buffers", + "author": "Google Protocol Buffers Team", + "license": "BSD-3-Clause", + "repository": { + "type": "git", + "url": "https://github.com/protocolbuffers/protobuf-javascript.git" + }, + "type": "commonjs", + "main": "index.js", + "bin": { + "protoc-gen-js": "cli.js" + }, + "scripts": { + "postinstall": "node download-protoc-gen-js.js" + }, + "dependencies": { + "adm-zip": "^0.5.16" + }, + "engines": { + "node": ">=18.0.0" + } +}