@protobuf-ts/plugin
Version:
The protocol buffer compiler plugin "protobuf-ts" generates TypeScript, gRPC-web, Twirp, and more.
97 lines (96 loc) • 4.06 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.SymbolTable = void 0;
/**
* A table for unique symbols (for any DescriptorProto, EnumDescriptorProto
* or ServiceDescriptorProto) in files (GeneratedFile).
*/
class SymbolTable {
constructor(clashResolver) {
this.entries = [];
this.clashResolveMaxTries = 100;
this.hasNameInFile = (name, file) => this.entries.some(e => e.file === file && e.name === name);
this.clashResolver = clashResolver !== null && clashResolver !== void 0 ? clashResolver : SymbolTable.defaultClashResolver;
}
/**
* Register a symbol in the given file for the given descriptor.
*
* If the name is already taken in the file, an alternative name
* is automatically generated by appending '$' and a running
* number to the requested name. You can change the behaviour by
* providing your own `clashResolver`.
*
* Only one symbol per kind can be registered for a descriptor.
*
* If you want to generate an interface *and* a class for a
* message, use a different `kind` for each.
*
* Returns the actual name registered.
*/
register(requestedName, descType, file, kind = 'default') {
// Only one symbol per kind can be registered for a descriptor.
if (this.has(descType, kind)) {
let { file, name } = this.get(descType, kind);
let msg = `Cannot register name "${requestedName}" of kind "${kind}" for ${descType.toString()}. `
+ `The descriptor is already registered in file "${file.getFilename()}" with name "${name}". `
+ `Use a different 'kind' to register multiple symbols for a descriptor.`;
throw new Error(msg);
}
// find a free name within the file
let name = requestedName;
let count = 0;
while (this.hasNameInFile(name, file) && count < this.clashResolveMaxTries) {
name = this.clashResolver(descType, file, requestedName, kind, ++count, name);
}
if (this.hasNameInFile(name, file)) {
let msg = `Failed to register name "${requestedName}" for ${descType.toString()}. `
+ `Gave up finding alternative name after ${this.clashResolveMaxTries} tries. `
+ `There is something wrong with the clash resolver.`;
throw new Error(msg);
}
// add the entry and return name
this.entries.push({ file, descriptor: descType, kind, name });
return name;
}
/**
* Find a symbol (of the given kind) for the given descriptor.
* Return `undefined` if not found.
*/
find(descType, kind = 'default') {
return this.entries.find(e => e.descriptor.typeName === descType.typeName && e.kind === kind);
}
/**
* Find a symbol (of the given kind) for the given descriptor.
* Raises error if not found.
*/
get(descType, kind = 'default') {
const found = this.find(descType, kind);
if (!found) {
let files = this.entries.map(e => e.file)
.filter((value, index, array) => array.indexOf(value) === index);
let msg = `Failed to find name for ${descType.toString()} of kind "${kind}". `
+ `Searched in ${files.length} files.`;
throw new Error(msg);
}
return found;
}
/**
* Is a name (of the given kind) registered for the the given descriptor?
*/
has(descType, kind = 'default') {
return !!this.find(descType, kind);
}
list(file, kind) {
let matches = this.entries.filter(e => e.file === file);
if (kind !== undefined) {
matches = matches.filter(e => e.kind === kind);
}
return matches;
}
static defaultClashResolver(descriptor, file, requestedName, kind, tryCount) {
let n = requestedName;
n = n.endsWith('$') ? n.substring(1) : n;
return n + '$' + tryCount;
}
}
exports.SymbolTable = SymbolTable;