Skip to content

Instantly share code, notes, and snippets.

@timostamm
Created December 12, 2022 10:07
Show Gist options
  • Save timostamm/d36a6f15b010cbd8b0bf91e734df8cf3 to your computer and use it in GitHub Desktop.
Save timostamm/d36a6f15b010cbd8b0bf91e734df8cf3 to your computer and use it in GitHub Desktop.
Simple plugin to generate an enum object for every oneof
version: v1
plugins:
- name: oneofhelper
path: ./src/protoc-gen-oneofhelper.ts
opt: target=ts
out: src/gen
#!/usr/bin/env -S npx tsx
import { createEcmaScriptPlugin, runNodeJs } from "@bufbuild/protoplugin";
import { localName, GeneratedFile, Schema } from "@bufbuild/protoplugin/ecmascript";
import type { DescMessage } from "@bufbuild/protobuf";
const protocGenOneofhelper = createEcmaScriptPlugin({
name: "protoc-gen-oneofhelper",
version: `v1.0.0`,
generateTs (schema: Schema) {
for (const file of schema.files) {
const f = schema.generateFile(file.name + "_oneofhelper.ts");
f.preamble(file);
for (const message of file.messages) {
generateMessage(f, message);
}
}
}
});
// prettier-ignore
function generateMessage(f: GeneratedFile, message: DescMessage) {
for (const oo of message.oneofs) {
// Name of the enum we are about to generate
const name = localName(oo).replace(/^[a-z]/, v => v.toUpperCase()) + "Case";
f.print`export const ${name} = {`;
for (const field of oo.fields) {
f.print` ${field.name.toUpperCase()}: '${localName(field)}',`;
}
f.print`} as const;`;
}
for (const nestedMessage of message.nestedMessages) {
generateMessage(f, nestedMessage);
}
}
runNodeJs(protocGenOneofhelper);
@lwhiteley
Copy link

lwhiteley commented Dec 21, 2022

posting here to reference everything in on place

Notes:

  1. ensure to make file executable by running chmod +x protoc-gen-oneofhelper.ts
  2. had to modify the code a bit due to some conflicts with similar named oneof properties
    • This also puts a blank line between enums created
const titleCase = (value: string) => {
  return value.replace(/^[a-z]/, (v) => v.toUpperCase());
};

// prettier-ignore
function generateMessage(f: GeneratedFile, message: DescMessage) {
  for (const oo of message.oneofs) {
    // Name of the enum we are about to generate
    const name = titleCase(message.name) + '_' + titleCase(localName(oo)) + "Case";
    f.print`export const ${name} = {`;
    for (const field of oo.fields) {
      f.print`  ${field.name.toUpperCase()}: '${localName(field)}',`;
    }
    f.print`} as const;
`;
  }
  for (const nestedMessage of message.nestedMessages) {
    generateMessage(f, nestedMessage);
  }
}

Links:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment