webpb 0.0.26 → 0.0.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -1
- package/cli/bin/webpb.js +70 -0
- package/cli/generate.js +58 -0
- package/cli/package-root.js +50 -0
- package/cli/parse-args.js +75 -0
- package/cli/platform.js +39 -0
- package/cli/resolve-protoc-plugin.js +114 -0
- package/cli/resolve-protoc.js +81 -0
- package/cli/versions.js +21 -0
- package/package.json +27 -49
- package/proto/google/protobuf/any.proto +158 -0
- package/proto/google/protobuf/api.proto +208 -0
- package/proto/google/protobuf/descriptor.proto +911 -0
- package/proto/google/protobuf/duration.proto +116 -0
- package/proto/google/protobuf/empty.proto +52 -0
- package/proto/google/protobuf/field_mask.proto +245 -0
- package/proto/google/protobuf/source_context.proto +48 -0
- package/proto/google/protobuf/struct.proto +95 -0
- package/proto/google/protobuf/timestamp.proto +147 -0
- package/proto/google/protobuf/type.proto +187 -0
- package/proto/google/protobuf/wrappers.proto +123 -0
- package/proto/webpb/WebpbExtend.proto +141 -0
- package/webpb-version.properties +2 -0
package/README.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# webpb
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
TypeScript runtime and protoc code generation CLI for webpb.
|
|
4
|
+
|
|
5
|
+
```shell
|
|
6
|
+
npm install -D webpb
|
|
7
|
+
npx webpb generate -o src/generated -I protos protos/*.proto
|
|
8
|
+
```
|
|
4
9
|
|
|
5
10
|
To view more, please visit: https://github.com/jinganix/webpb
|
package/cli/bin/webpb.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { realpathSync } from "node:fs";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { runGenerate } from "../generate.js";
|
|
5
|
+
export const HELP = `webpb — TypeScript runtime and protoc tooling for webpb
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
webpb generate [options] -- <files.proto...>
|
|
9
|
+
|
|
10
|
+
Run "webpb generate --help" for generation options.
|
|
11
|
+
`;
|
|
12
|
+
export async function runCli(argv) {
|
|
13
|
+
const [command, ...rest] = argv;
|
|
14
|
+
if (!command || command === "-h" || command === "--help") {
|
|
15
|
+
process.stdout.write(HELP);
|
|
16
|
+
return 0;
|
|
17
|
+
}
|
|
18
|
+
if (command === "generate") {
|
|
19
|
+
return runGenerate(rest);
|
|
20
|
+
}
|
|
21
|
+
console.error(`Unknown command: ${command}`);
|
|
22
|
+
process.stdout.write(HELP);
|
|
23
|
+
return 1;
|
|
24
|
+
}
|
|
25
|
+
function normalizeComparablePath(path) {
|
|
26
|
+
if (process.platform !== "win32") {
|
|
27
|
+
return path;
|
|
28
|
+
}
|
|
29
|
+
return path.replace(/^\\\\\?\\/, "").toLowerCase();
|
|
30
|
+
}
|
|
31
|
+
function resolveArgvEntryPath(argv) {
|
|
32
|
+
const entry = argv[1];
|
|
33
|
+
if (!entry) {
|
|
34
|
+
return "";
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
return realpathSync(entry);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return entry;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function resolveModulePath(moduleUrl) {
|
|
44
|
+
try {
|
|
45
|
+
return realpathSync(fileURLToPath(moduleUrl));
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return fileURLToPath(moduleUrl);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export function isMainModule(moduleUrl, argv = process.argv) {
|
|
52
|
+
const modulePath = normalizeComparablePath(resolveModulePath(moduleUrl));
|
|
53
|
+
const entryPath = normalizeComparablePath(resolveArgvEntryPath(argv));
|
|
54
|
+
return entryPath === modulePath;
|
|
55
|
+
}
|
|
56
|
+
export async function runMainEntrypoint(argv = process.argv, moduleUrl = import.meta.url) {
|
|
57
|
+
if (!isMainModule(moduleUrl, argv)) {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
return runCli(argv.slice(2));
|
|
61
|
+
}
|
|
62
|
+
export async function bootstrapMain(argv = process.argv, moduleUrl = import.meta.url) {
|
|
63
|
+
if (!isMainModule(moduleUrl, argv)) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
process.exit((await runMainEntrypoint(argv, moduleUrl)) ?? 0);
|
|
67
|
+
}
|
|
68
|
+
if (process.env.VITEST !== "true") {
|
|
69
|
+
await bootstrapMain();
|
|
70
|
+
}
|
package/cli/generate.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { existsSync, mkdirSync } from "node:fs";
|
|
3
|
+
import { bundledProtoInclude, packageRoot } from "./package-root.js";
|
|
4
|
+
import { GENERATE_HELP, parseGenerateArgs } from "./parse-args.js";
|
|
5
|
+
import { resolveProtocPlugin } from "./resolve-protoc-plugin.js";
|
|
6
|
+
import { resolveProtoc } from "./resolve-protoc.js";
|
|
7
|
+
function protocPath(filePath) {
|
|
8
|
+
return process.platform === "win32" ? filePath.replace(/\\/g, "/") : filePath;
|
|
9
|
+
}
|
|
10
|
+
export async function runGenerate(argv, rootOverride) {
|
|
11
|
+
const parsed = parseGenerateArgs(argv);
|
|
12
|
+
if (parsed.help) {
|
|
13
|
+
process.stdout.write(GENERATE_HELP);
|
|
14
|
+
return 0;
|
|
15
|
+
}
|
|
16
|
+
if (!parsed.out) {
|
|
17
|
+
console.error("Missing required option: --out");
|
|
18
|
+
process.stdout.write(GENERATE_HELP);
|
|
19
|
+
return 1;
|
|
20
|
+
}
|
|
21
|
+
if (parsed.protoFiles.length === 0) {
|
|
22
|
+
console.error("No .proto files specified");
|
|
23
|
+
process.stdout.write(GENERATE_HELP);
|
|
24
|
+
return 1;
|
|
25
|
+
}
|
|
26
|
+
for (const protoFile of parsed.protoFiles) {
|
|
27
|
+
if (!existsSync(protoFile)) {
|
|
28
|
+
console.error(`Proto file not found: ${protoFile}`);
|
|
29
|
+
return 1;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const root = rootOverride ?? packageRoot(import.meta.url);
|
|
33
|
+
const includeDirs = [...parsed.includes];
|
|
34
|
+
if (parsed.webpbProto) {
|
|
35
|
+
includeDirs.unshift(bundledProtoInclude(root));
|
|
36
|
+
}
|
|
37
|
+
mkdirSync(parsed.out, { recursive: true });
|
|
38
|
+
const protoc = await resolveProtoc(root, {
|
|
39
|
+
protobufVersion: parsed.protobufVersion,
|
|
40
|
+
protocPath: parsed.protoc,
|
|
41
|
+
});
|
|
42
|
+
const pluginBin = await resolveProtocPlugin(root, {
|
|
43
|
+
pluginPath: parsed.plugin,
|
|
44
|
+
webpbVersion: parsed.webpbVersion,
|
|
45
|
+
});
|
|
46
|
+
const args = [
|
|
47
|
+
...includeDirs.flatMap((dir) => ["-I", protocPath(dir)]),
|
|
48
|
+
`--plugin=protoc-gen-ts=${protocPath(pluginBin)}`,
|
|
49
|
+
`--ts_out=${protocPath(parsed.out)}`,
|
|
50
|
+
...parsed.protoFiles.map(protocPath),
|
|
51
|
+
];
|
|
52
|
+
const result = spawnSync(protoc, args, { stdio: "inherit" });
|
|
53
|
+
if (result.error) {
|
|
54
|
+
console.error(`Failed to run ${protoc}. Install protoc, set PROTOC, or retry to auto-download.`);
|
|
55
|
+
return 1;
|
|
56
|
+
}
|
|
57
|
+
return result.status ?? 1;
|
|
58
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { executableExtension } from "./platform.js";
|
|
5
|
+
export function packageRoot(moduleUrl) {
|
|
6
|
+
let dir = dirname(fileURLToPath(moduleUrl));
|
|
7
|
+
while (dir !== dirname(dir)) {
|
|
8
|
+
if (existsSync(join(dir, "package.json"))) {
|
|
9
|
+
return dir;
|
|
10
|
+
}
|
|
11
|
+
dir = dirname(dir);
|
|
12
|
+
}
|
|
13
|
+
throw new Error("webpb package root not found");
|
|
14
|
+
}
|
|
15
|
+
export function findMonorepoRoot(root) {
|
|
16
|
+
let dir = root;
|
|
17
|
+
while (dir !== dirname(dir)) {
|
|
18
|
+
if (existsSync(join(dir, "plugin", "go.mod"))) {
|
|
19
|
+
return dir;
|
|
20
|
+
}
|
|
21
|
+
dir = dirname(dir);
|
|
22
|
+
}
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
export function bundledProtoInclude(root) {
|
|
26
|
+
const packaged = join(root, "proto");
|
|
27
|
+
if (existsSync(packaged)) {
|
|
28
|
+
return packaged;
|
|
29
|
+
}
|
|
30
|
+
const monorepoRoot = findMonorepoRoot(root);
|
|
31
|
+
if (monorepoRoot) {
|
|
32
|
+
const monorepo = join(monorepoRoot, "lib/proto/src/main/resources");
|
|
33
|
+
if (existsSync(monorepo)) {
|
|
34
|
+
return monorepo;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
throw new Error("webpb proto includes not found; reinstall the webpb npm package");
|
|
38
|
+
}
|
|
39
|
+
export function monorepoPluginPath(root) {
|
|
40
|
+
const monorepoRoot = findMonorepoRoot(root);
|
|
41
|
+
if (!monorepoRoot) {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
const ext = executableExtension();
|
|
45
|
+
return join(monorepoRoot, "plugin/bin", `webpb-protoc-ts${ext}`);
|
|
46
|
+
}
|
|
47
|
+
export function readPackageVersion(root) {
|
|
48
|
+
const pkg = JSON.parse(readFileSync(join(root, "package.json"), "utf8"));
|
|
49
|
+
return pkg.version;
|
|
50
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
export const GENERATE_HELP = `Usage: webpb generate [options] -- <files.proto...>
|
|
2
|
+
|
|
3
|
+
Generate TypeScript from protobuf definitions using the webpb protoc plugin.
|
|
4
|
+
|
|
5
|
+
Options:
|
|
6
|
+
-o, --out <dir> Output directory (--ts_out), required
|
|
7
|
+
-I, --include <dir> Additional proto include path (repeatable)
|
|
8
|
+
--no-webpb-proto Skip bundled webpb/WebpbExtend.proto includes
|
|
9
|
+
--protoc <path> Path to protoc (default: PATH, then auto-download)
|
|
10
|
+
--plugin <path> Path to webpb-protoc-ts (default: auto-download)
|
|
11
|
+
--webpb-version <ver> webpb release version for plugin download
|
|
12
|
+
--protobuf-version <ver> protoc version for auto-download (default: bundled)
|
|
13
|
+
-h, --help Show this help
|
|
14
|
+
|
|
15
|
+
Environment variables:
|
|
16
|
+
PROTOC Path to protoc
|
|
17
|
+
WEBPB_PROTOC_TS Path to webpb-protoc-ts
|
|
18
|
+
WEBPB_VERSION webpb release version for plugin download
|
|
19
|
+
WEBPB_PROTOBUF_VERSION protoc version for auto-download
|
|
20
|
+
|
|
21
|
+
Examples:
|
|
22
|
+
webpb generate -o src/generated -I protos protos/*.proto
|
|
23
|
+
webpb generate --out generated/proto --include ../proto src/*.proto
|
|
24
|
+
`;
|
|
25
|
+
export function parseGenerateArgs(argv) {
|
|
26
|
+
const result = {
|
|
27
|
+
includes: [],
|
|
28
|
+
protoFiles: [],
|
|
29
|
+
webpbProto: true,
|
|
30
|
+
};
|
|
31
|
+
let index = 0;
|
|
32
|
+
while (index < argv.length) {
|
|
33
|
+
const arg = argv[index];
|
|
34
|
+
if (arg === "--") {
|
|
35
|
+
result.protoFiles.push(...argv.slice(index + 1));
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
switch (arg) {
|
|
39
|
+
case "-o":
|
|
40
|
+
case "--out":
|
|
41
|
+
result.out = argv[++index];
|
|
42
|
+
break;
|
|
43
|
+
case "-I":
|
|
44
|
+
case "--include":
|
|
45
|
+
result.includes.push(argv[++index]);
|
|
46
|
+
break;
|
|
47
|
+
case "--no-webpb-proto":
|
|
48
|
+
result.webpbProto = false;
|
|
49
|
+
break;
|
|
50
|
+
case "--protoc":
|
|
51
|
+
result.protoc = argv[++index];
|
|
52
|
+
break;
|
|
53
|
+
case "--plugin":
|
|
54
|
+
result.plugin = argv[++index];
|
|
55
|
+
break;
|
|
56
|
+
case "--webpb-version":
|
|
57
|
+
result.webpbVersion = argv[++index];
|
|
58
|
+
break;
|
|
59
|
+
case "--protobuf-version":
|
|
60
|
+
result.protobufVersion = argv[++index];
|
|
61
|
+
break;
|
|
62
|
+
case "-h":
|
|
63
|
+
case "--help":
|
|
64
|
+
return { help: true, includes: [], protoFiles: [], webpbProto: true };
|
|
65
|
+
default:
|
|
66
|
+
if (arg.startsWith("-")) {
|
|
67
|
+
throw new Error(`Unknown option: ${arg}`);
|
|
68
|
+
}
|
|
69
|
+
result.protoFiles.push(arg);
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
index += 1;
|
|
73
|
+
}
|
|
74
|
+
return result;
|
|
75
|
+
}
|
package/cli/platform.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export function webpbReleasePlatform(platform = process.platform, arch = process.arch) {
|
|
2
|
+
if (platform === "darwin") {
|
|
3
|
+
return arch === "arm64" ? "darwin-arm64" : "darwin-amd64";
|
|
4
|
+
}
|
|
5
|
+
if (platform === "linux") {
|
|
6
|
+
return arch === "arm64" ? "linux-arm64" : "linux-amd64";
|
|
7
|
+
}
|
|
8
|
+
if (platform === "win32") {
|
|
9
|
+
return arch === "ia32" ? "windows-386" : "windows-amd64";
|
|
10
|
+
}
|
|
11
|
+
throw new Error(`Unsupported platform: ${platform} ${arch}`);
|
|
12
|
+
}
|
|
13
|
+
export function protocMavenClassifier(platform = process.platform, arch = process.arch) {
|
|
14
|
+
if (platform === "darwin") {
|
|
15
|
+
return arch === "arm64" ? "osx-aarch_64" : "osx-x86_64";
|
|
16
|
+
}
|
|
17
|
+
if (platform === "linux") {
|
|
18
|
+
if (arch === "arm64") {
|
|
19
|
+
return "linux-aarch_64";
|
|
20
|
+
}
|
|
21
|
+
if (arch === "ppc64") {
|
|
22
|
+
return "linux-ppcle_64";
|
|
23
|
+
}
|
|
24
|
+
if (arch === "s390x") {
|
|
25
|
+
return "linux-s390_64";
|
|
26
|
+
}
|
|
27
|
+
if (arch === "ia32") {
|
|
28
|
+
return "linux-x86_32";
|
|
29
|
+
}
|
|
30
|
+
return "linux-x86_64";
|
|
31
|
+
}
|
|
32
|
+
if (platform === "win32") {
|
|
33
|
+
return arch === "ia32" ? "windows-x86_32" : "windows-x86_64";
|
|
34
|
+
}
|
|
35
|
+
throw new Error(`Unsupported platform: ${platform} ${arch}`);
|
|
36
|
+
}
|
|
37
|
+
export function executableExtension(platform = process.platform) {
|
|
38
|
+
return platform === "win32" ? ".exe" : "";
|
|
39
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { chmodSync, createWriteStream, existsSync, mkdirSync } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { pipeline } from "node:stream/promises";
|
|
6
|
+
import { monorepoPluginPath, readPackageVersion } from "./package-root.js";
|
|
7
|
+
import { executableExtension, webpbReleasePlatform } from "./platform.js";
|
|
8
|
+
const defaultExit = (code) => {
|
|
9
|
+
process.exit(code);
|
|
10
|
+
};
|
|
11
|
+
function defaultWebpbVersion(root, versionOverride) {
|
|
12
|
+
if (process.env.WEBPB_VERSION) {
|
|
13
|
+
return process.env.WEBPB_VERSION;
|
|
14
|
+
}
|
|
15
|
+
if (versionOverride) {
|
|
16
|
+
return versionOverride;
|
|
17
|
+
}
|
|
18
|
+
return readPackageVersion(root);
|
|
19
|
+
}
|
|
20
|
+
export function cachePluginPath(version) {
|
|
21
|
+
const ext = executableExtension();
|
|
22
|
+
return join(homedir(), ".cache", "webpb", version, `webpb-protoc-ts-${webpbReleasePlatform()}${ext}`);
|
|
23
|
+
}
|
|
24
|
+
export async function downloadReleaseBinary(version, destination, platform = process.platform) {
|
|
25
|
+
const ext = executableExtension(platform);
|
|
26
|
+
const releasePlatform = webpbReleasePlatform(platform);
|
|
27
|
+
const assetName = `webpb-protoc-ts-${releasePlatform}${ext}`;
|
|
28
|
+
const url = `https://github.com/jinganix/webpb/releases/download/v${version}/${assetName}`;
|
|
29
|
+
mkdirSync(dirname(destination), { recursive: true });
|
|
30
|
+
const response = await fetch(url);
|
|
31
|
+
if (!response.ok) {
|
|
32
|
+
throw new Error(`Failed to download ${url}: HTTP ${response.status}`);
|
|
33
|
+
}
|
|
34
|
+
if (!response.body) {
|
|
35
|
+
throw new Error(`Failed to download ${url}: empty response body`);
|
|
36
|
+
}
|
|
37
|
+
await pipeline(response.body, createWriteStream(destination));
|
|
38
|
+
if (platform !== "win32") {
|
|
39
|
+
chmodSync(destination, 0o755);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
export function findPluginDir(root) {
|
|
43
|
+
let dir = root;
|
|
44
|
+
while (dir !== dirname(dir)) {
|
|
45
|
+
if (existsSync(join(dir, "plugin", "go.mod"))) {
|
|
46
|
+
return join(dir, "plugin");
|
|
47
|
+
}
|
|
48
|
+
dir = dirname(dir);
|
|
49
|
+
}
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
export function buildFromSource(root, exit) {
|
|
53
|
+
const ext = executableExtension();
|
|
54
|
+
const binaryName = `webpb-protoc-ts${ext}`;
|
|
55
|
+
const pluginDir = findPluginDir(root);
|
|
56
|
+
if (!pluginDir) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
console.log("Building webpb-protoc-ts from source...");
|
|
60
|
+
const result = spawnSync("go", ["build", "-o", join("bin", binaryName), "./cmd/webpb-ts"], { cwd: pluginDir, stdio: "inherit" });
|
|
61
|
+
if (result.status !== 0) {
|
|
62
|
+
exit(result.status ?? 1);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
export function resolvePluginAfterFailedDownload(root, exit) {
|
|
66
|
+
if (findPluginDir(root)) {
|
|
67
|
+
buildFromSource(root, exit);
|
|
68
|
+
const rebuiltPath = monorepoPluginPath(root);
|
|
69
|
+
if (rebuiltPath && existsSync(rebuiltPath)) {
|
|
70
|
+
return rebuiltPath;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
export async function resolveProtocPlugin(root, options = {}) {
|
|
76
|
+
const exit = options.exit ?? defaultExit;
|
|
77
|
+
if (options.pluginPath) {
|
|
78
|
+
return options.pluginPath;
|
|
79
|
+
}
|
|
80
|
+
if (process.env.WEBPB_PROTOC_TS) {
|
|
81
|
+
return process.env.WEBPB_PROTOC_TS;
|
|
82
|
+
}
|
|
83
|
+
const localPath = monorepoPluginPath(root);
|
|
84
|
+
if (localPath && existsSync(localPath)) {
|
|
85
|
+
return localPath;
|
|
86
|
+
}
|
|
87
|
+
const version = defaultWebpbVersion(root, options.webpbVersion);
|
|
88
|
+
const cachedPath = cachePluginPath(version);
|
|
89
|
+
if (existsSync(cachedPath)) {
|
|
90
|
+
return cachedPath;
|
|
91
|
+
}
|
|
92
|
+
try {
|
|
93
|
+
console.log(`Downloading webpb-protoc-ts ${version} for ${webpbReleasePlatform()}...`);
|
|
94
|
+
await downloadReleaseBinary(version, cachedPath);
|
|
95
|
+
return cachedPath;
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
99
|
+
console.warn(`Release download failed: ${message}`);
|
|
100
|
+
}
|
|
101
|
+
const rebuilt = resolvePluginAfterFailedDownload(root, exit);
|
|
102
|
+
if (rebuilt) {
|
|
103
|
+
return rebuilt;
|
|
104
|
+
}
|
|
105
|
+
console.error([
|
|
106
|
+
"webpb-protoc-ts not found.",
|
|
107
|
+
"Install options:",
|
|
108
|
+
" - set WEBPB_PROTOC_TS to the plugin binary path",
|
|
109
|
+
" - download from https://github.com/jinganix/webpb/releases",
|
|
110
|
+
" - set WEBPB_VERSION and retry (auto-download)",
|
|
111
|
+
" - build from source: cd plugin && make build",
|
|
112
|
+
].join("\n"));
|
|
113
|
+
return exit(1);
|
|
114
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { chmodSync, createWriteStream, existsSync, mkdirSync } from "node:fs";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { pipeline } from "node:stream/promises";
|
|
6
|
+
import { executableExtension, protocMavenClassifier } from "./platform.js";
|
|
7
|
+
import { readBundledVersions } from "./versions.js";
|
|
8
|
+
const defaultExit = (code) => {
|
|
9
|
+
process.exit(code);
|
|
10
|
+
};
|
|
11
|
+
export function cacheProtocPath(protobufVersion) {
|
|
12
|
+
const ext = executableExtension();
|
|
13
|
+
return join(homedir(), ".cache", "webpb", "protoc", protobufVersion, `protoc${ext}`);
|
|
14
|
+
}
|
|
15
|
+
export function resolveOnPath(command) {
|
|
16
|
+
if (!command) {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
const lookup = process.platform === "win32" ? "where" : "which";
|
|
20
|
+
const result = spawnSync(lookup, [command], { encoding: "utf8" });
|
|
21
|
+
if (result.status !== 0) {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
const resolved = result.stdout.trim().split(/\r?\n/)[0];
|
|
25
|
+
return resolved || undefined;
|
|
26
|
+
}
|
|
27
|
+
export async function downloadProtocFromMaven(protobufVersion, destination, platform = process.platform) {
|
|
28
|
+
const classifier = protocMavenClassifier();
|
|
29
|
+
const fileName = `protoc-${protobufVersion}-${classifier}.exe`;
|
|
30
|
+
const url = `https://repo1.maven.org/maven2/com/google/protobuf/protoc/${protobufVersion}/${fileName}`;
|
|
31
|
+
mkdirSync(dirname(destination), { recursive: true });
|
|
32
|
+
const response = await fetch(url);
|
|
33
|
+
if (!response.ok) {
|
|
34
|
+
throw new Error(`Failed to download ${url}: HTTP ${response.status}`);
|
|
35
|
+
}
|
|
36
|
+
if (!response.body) {
|
|
37
|
+
throw new Error(`Failed to download ${url}: empty response body`);
|
|
38
|
+
}
|
|
39
|
+
await pipeline(response.body, createWriteStream(destination));
|
|
40
|
+
if (platform !== "win32") {
|
|
41
|
+
chmodSync(destination, 0o755);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export async function resolveProtoc(root, options = {}) {
|
|
45
|
+
const exit = options.exit ?? defaultExit;
|
|
46
|
+
if (options.protocPath) {
|
|
47
|
+
return options.protocPath;
|
|
48
|
+
}
|
|
49
|
+
if (process.env.PROTOC) {
|
|
50
|
+
return process.env.PROTOC;
|
|
51
|
+
}
|
|
52
|
+
const onPath = resolveOnPath("protoc");
|
|
53
|
+
if (onPath) {
|
|
54
|
+
return onPath;
|
|
55
|
+
}
|
|
56
|
+
const bundled = readBundledVersions(root);
|
|
57
|
+
const protobufVersion = options.protobufVersion ??
|
|
58
|
+
process.env.WEBPB_PROTOBUF_VERSION ??
|
|
59
|
+
bundled.protobufVersion;
|
|
60
|
+
const cachedPath = cacheProtocPath(protobufVersion);
|
|
61
|
+
if (existsSync(cachedPath)) {
|
|
62
|
+
return cachedPath;
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
console.log(`Downloading protoc ${protobufVersion} for ${protocMavenClassifier()}...`);
|
|
66
|
+
await downloadProtocFromMaven(protobufVersion, cachedPath);
|
|
67
|
+
return cachedPath;
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
71
|
+
console.error(`Failed to download protoc: ${message}`);
|
|
72
|
+
console.error([
|
|
73
|
+
"protoc not found.",
|
|
74
|
+
"Install options:",
|
|
75
|
+
" - add protoc to PATH",
|
|
76
|
+
" - set PROTOC to the protoc binary path",
|
|
77
|
+
" - set WEBPB_PROTOBUF_VERSION and retry (auto-download from Maven Central)",
|
|
78
|
+
].join("\n"));
|
|
79
|
+
return exit(1);
|
|
80
|
+
}
|
|
81
|
+
}
|
package/cli/versions.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
export function readBundledVersions(root) {
|
|
4
|
+
const path = join(root, "webpb-version.properties");
|
|
5
|
+
const text = readFileSync(path, "utf8");
|
|
6
|
+
const props = Object.fromEntries(text
|
|
7
|
+
.split("\n")
|
|
8
|
+
.map((line) => line.trim())
|
|
9
|
+
.filter((line) => line && !line.startsWith("#"))
|
|
10
|
+
.map((line) => {
|
|
11
|
+
const separator = line.indexOf("=");
|
|
12
|
+
return [line.slice(0, separator), line.slice(separator + 1)];
|
|
13
|
+
}));
|
|
14
|
+
if (!props.webpbVersion || !props.protobufVersion) {
|
|
15
|
+
throw new Error(`Invalid webpb-version.properties at ${path}`);
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
protobufVersion: props.protobufVersion,
|
|
19
|
+
webpbVersion: props.webpbVersion,
|
|
20
|
+
};
|
|
21
|
+
}
|
package/package.json
CHANGED
|
@@ -1,62 +1,40 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "webpb",
|
|
3
|
-
"version": "0.0.26",
|
|
4
|
-
"description": "The webpb runtime library",
|
|
5
|
-
"scripts": {
|
|
6
|
-
"build": "rimraf build/**/* && tsc && vite build && dts-bundle-generator --config ./dts-bundle-generator.config.cjs && copyfiles ./README.md ./package.json build",
|
|
7
|
-
"check": "npm run lint && npm run test",
|
|
8
|
-
"lint": "eslint --quiet src/**/*.ts",
|
|
9
|
-
"lint:fix": "eslint --fix src/**/*.ts",
|
|
10
|
-
"deploy": "npm run check && npm run build && npm publish ./build --access public",
|
|
11
|
-
"publish": "npm run sync-version && npm run build && npm publish ./build --access public",
|
|
12
|
-
"sync-version": "node scripts/sync-version.mjs",
|
|
13
|
-
"test": "vitest --coverage"
|
|
14
|
-
},
|
|
15
2
|
"author": "jinganix@gmail.com",
|
|
16
|
-
"
|
|
3
|
+
"bin": {
|
|
4
|
+
"webpb": "./cli/bin/webpb.js"
|
|
5
|
+
},
|
|
6
|
+
"description": "webpb TypeScript runtime and protoc code generation CLI",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.mjs",
|
|
11
|
+
"require": "./dist/index.cjs"
|
|
12
|
+
},
|
|
13
|
+
"./proto": "./proto"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"cli",
|
|
17
|
+
"dist",
|
|
18
|
+
"proto",
|
|
19
|
+
"webpb-version.properties",
|
|
20
|
+
"README.md"
|
|
21
|
+
],
|
|
17
22
|
"keywords": [
|
|
18
23
|
"webpb",
|
|
24
|
+
"protobuf",
|
|
25
|
+
"protoc",
|
|
19
26
|
"runtime"
|
|
20
27
|
],
|
|
28
|
+
"license": "Apache-2.0",
|
|
29
|
+
"main": "./dist/index.cjs",
|
|
30
|
+
"module": "./dist/index.mjs",
|
|
31
|
+
"name": "webpb",
|
|
21
32
|
"repository": {
|
|
22
33
|
"type": "git",
|
|
23
34
|
"url": "https://github.com/jinganix/webpb"
|
|
24
35
|
},
|
|
25
|
-
"type": "module",
|
|
26
36
|
"sideEffects": false,
|
|
27
|
-
"
|
|
28
|
-
"module": "./dist/index.mjs",
|
|
37
|
+
"type": "module",
|
|
29
38
|
"types": "./dist/index.d.ts",
|
|
30
|
-
"
|
|
31
|
-
".": {
|
|
32
|
-
"types": "./dist/index.d.ts",
|
|
33
|
-
"import": "./dist/index.mjs",
|
|
34
|
-
"require": "./dist/index.cjs"
|
|
35
|
-
}
|
|
36
|
-
},
|
|
37
|
-
"devDependencies": {
|
|
38
|
-
"@eslint/js": "^10.0.1",
|
|
39
|
-
"@types/node": "^25.9.3",
|
|
40
|
-
"@typescript-eslint/eslint-plugin": "^8.59.4",
|
|
41
|
-
"@typescript-eslint/parser": "^8.59.4",
|
|
42
|
-
"@vitest/coverage-v8": "^4.1.7",
|
|
43
|
-
"copyfiles": "^2.4.1",
|
|
44
|
-
"dts-bundle-generator": "^9.5.1",
|
|
45
|
-
"eslint": "^10.4.1",
|
|
46
|
-
"eslint-config-prettier": "^10.1.8",
|
|
47
|
-
"eslint-plugin-import-quotes": "^0.0.1",
|
|
48
|
-
"eslint-plugin-import-x": "^4.16.2",
|
|
49
|
-
"eslint-plugin-no-null": "^1.0.2",
|
|
50
|
-
"eslint-plugin-perfectionist": "^4.15.0",
|
|
51
|
-
"eslint-plugin-prettier": "^5.5.5",
|
|
52
|
-
"globals": "^14.0.0",
|
|
53
|
-
"prettier": "^3.8.4",
|
|
54
|
-
"pretty-quick": "^4.2.2",
|
|
55
|
-
"rimraf": "^6.1.3",
|
|
56
|
-
"ts-node": "^10.9.2",
|
|
57
|
-
"typescript": "^6.0.3",
|
|
58
|
-
"typescript-eslint": "^8.59.4",
|
|
59
|
-
"vite": "^8.0.14",
|
|
60
|
-
"vitest": "^4.1.7"
|
|
61
|
-
}
|
|
39
|
+
"version": "0.0.28"
|
|
62
40
|
}
|