UNPKG

@bufbuild/protoplugin

Version:

Helps to create your own Protocol Buffers code generators.

228 lines (227 loc) 8.12 kB
"use strict"; // Copyright 2021-2025 Buf Technologies, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. Object.defineProperty(exports, "__esModule", { value: true }); exports.generateFilePath = generateFilePath; exports.generatedDescName = generatedDescName; exports.generatedShapeName = generatedShapeName; exports.generatedJsonTypeName = generatedJsonTypeName; exports.generatedValidTypeName = generatedValidTypeName; const codegenv2_1 = require("@bufbuild/protobuf/codegenv2"); const reflect_1 = require("@bufbuild/protobuf/reflect"); const safe_identifier_js_1 = require("./safe-identifier.js"); /** * Return a file path for the give file descriptor. */ function generateFilePath(file, bootstrapWkt, filesToGenerate) { // Well-known types are published with the runtime package. We usually want // the generated code to import them from the runtime package, with the // following exceptions: // 1. We are bootstrapping the runtime package via the plugin option // "bootstrap_wkt". In that case, we cannot refer to the runtime package // itself. // 2. We were explicitly asked to generate the well-known type. const wktFrom = codegenv2_1.wktPublicImportPaths[file.proto.name]; if (wktFrom !== undefined && !bootstrapWkt && !filesToGenerate.find((f) => f.name === file.name)) { return wktFrom; } return "./" + file.name + "_pb.js"; } /** * Return a safe identifier for a generated descriptor. */ function generatedDescName(desc) { const file = desc.kind == "file" ? desc : desc.file; const { descNames } = allNames(file); const name = descNames.get(desc); if (name === undefined) { throw new Error(`unable to determine unique descriptor name for ${desc.toString()}`); } return name; } /** * Return a safe identifier for a generated shape. */ function generatedShapeName(desc) { const { shapeNames } = allNames(desc.file); const name = shapeNames.get(desc); if (name === undefined) { throw new Error(`unable to determine unique shape name for ${desc.toString()}`); } return name; } /** * Return a safe identifier for a generated JSON type. */ function generatedJsonTypeName(desc) { const { jsonTypeNames } = allNames(desc.file); const name = jsonTypeNames.get(desc); if (name === undefined) { throw new Error(`unable to determine unique json type name for ${desc.toString()}`); } return name; } /** * Return a safe identifier for a generated Valid type. */ function generatedValidTypeName(desc) { const { validTypeNames } = allNames(desc.file); const name = validTypeNames.get(desc); if (name === undefined) { throw new Error(`unable to determine unique valid type name for ${desc.toString()}`); } return name; } /** * Compute the ideal name for a generated descriptor. */ function idealDescName(desc, i) { const salt = i === 0 ? "" : i === 1 ? "$" : `$${i - 1}`; if (desc.kind == "file") { const name = "file_" + desc.name.replace(/[^a-zA-Z0-9_]+/g, "_"); return (0, safe_identifier_js_1.safeIdentifier)(name + salt); } switch (desc.kind) { case "enum": return (0, safe_identifier_js_1.safeIdentifier)(identifier(desc) + "Schema" + salt); case "message": return (0, safe_identifier_js_1.safeIdentifier)(identifier(desc) + "Schema" + salt); case "extension": return (0, safe_identifier_js_1.safeIdentifier)(identifier(desc) + salt); case "service": return (0, safe_identifier_js_1.safeIdentifier)(identifier(desc) + salt); } } /** * Compute the ideal name for a generated shape. */ function idealShapeName(desc, i) { const salt = i === 0 ? "" : i === 1 ? "$" : `$${i - 1}`; return (0, safe_identifier_js_1.safeIdentifier)(identifier(desc) + salt); } /** * Compute the ideal name for a generated JSON type. */ function idealJsonTypeName(desc, i) { const salt = i === 0 ? "" : i === 1 ? "$" : `$${i - 1}`; return (0, safe_identifier_js_1.safeIdentifier)(identifier(desc) + "Json" + salt); } /** * Compute the ideal name for a generated Valid type. */ function idealValidTypeName(desc, i) { const salt = i === 0 ? "" : i === 1 ? "$" : `$${i - 1}`; return (0, safe_identifier_js_1.safeIdentifier)(identifier(desc) + "Valid" + salt); } /** * Return an identifier for the given descriptor based on its type name. * * The type name for a protobuf message is the package name (if any), plus * the names of parent messages it is nested in (if any), plus the name of * the element, separated by dots. For example: foo.bar.ParentMsg.MyEnum. * * ECMAScript does not have packages or namespaces, so we need a single * identifier. Our convention is to drop the package name, and to join other * parts of the name with an underscore. For example: ParentMsg_MyEnum. */ function identifier(desc) { const pkg = desc.file.proto.package; const offset = pkg.length > 0 ? pkg.length + 1 : 0; const nameWithoutPkg = desc.typeName.substring(offset); return nameWithoutPkg.replace(/\./g, "_"); } /** * Compute all ideal names for the elements in the file, resolving name clashes. */ function allNames(file) { const taken = new Set(); // In the first pass, register shape names const shapeNames = new Map(); for (const desc of (0, reflect_1.nestedTypes)(file)) { if (desc.kind != "enum" && desc.kind != "message") { continue; } let name; for (let i = 0;; i++) { name = idealShapeName(desc, i); if (!taken.has(name)) { break; } } taken.add(name); shapeNames.set(desc, name); } // In the second pass, register desc names const descNames = new Map(); for (const desc of [file, ...(0, reflect_1.nestedTypes)(file)]) { let name; switch (desc.kind) { case "enum": case "message": { for (let i = 0;; i++) { name = idealDescName(desc, i); if (!taken.has(name)) { break; } } break; } default: { for (let i = 0;; i++) { name = idealDescName(desc, i); if (!taken.has(name)) { break; } } break; } } taken.add(name); descNames.set(desc, name); } // In the third pass, register json type names const jsonTypeNames = new Map(); for (const desc of (0, reflect_1.nestedTypes)(file)) { if (desc.kind != "enum" && desc.kind != "message") { continue; } let name; for (let i = 0;; i++) { name = idealJsonTypeName(desc, i); if (!taken.has(name)) { break; } } taken.add(name); jsonTypeNames.set(desc, name); } const validTypeNames = new Map(); for (const desc of (0, reflect_1.nestedTypes)(file)) { if (desc.kind != "enum" && desc.kind != "message") { continue; } let name; for (let i = 0;; i++) { name = idealValidTypeName(desc, i); if (!taken.has(name)) { break; } } taken.add(name); validTypeNames.set(desc, name); } return { shapeNames, jsonTypeNames, validTypeNames, descNames }; }