Merge pull request #2912 from tenkirin/feat/file-extension-options

feat(proto-loader-gen-types): add options for specifying file extensions
This commit is contained in:
Michael Lumish 2025-03-17 10:15:08 -07:00 committed by GitHub
commit 86aa0f2f8b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 57 additions and 42 deletions

View File

@ -65,40 +65,49 @@ Options:
--version Show version number [boolean]
--keepCase Preserve the case of field names
[boolean] [default: false]
--longs The type that should be used to output 64 bit integer
values. Can be String, Number[string] [default: "Long"]
--enums The type that should be used to output enum fields. Can
be String [string] [default: "number"]
--bytes The type that should be used to output bytes fields.
Can be String, Array [string] [default: "Buffer"]
--longs The type that should be used to output 64 bit
integer values. Can be String, Number
[string] [default: "Long"]
--enums The type that should be used to output enum fields.
Can be String [string] [default: "number"]
--bytes The type that should be used to output bytes
fields. Can be String, Array
[string] [default: "Buffer"]
--defaults Output default values for omitted fields
[boolean] [default: false]
--arrays Output default values for omitted repeated fields even
if --defaults is not set [boolean] [default: false]
--objects Output default values for omitted message fields even
if --defaults is not set [boolean] [default: false]
--oneofs Output virtual oneof fields set to the present field's
name [boolean] [default: false]
--json Represent Infinity and NaN as strings in float fields.
Also decode google.protobuf.Any automatically
--arrays Output default values for omitted repeated fields
even if --defaults is not set
[boolean] [default: false]
--objects Output default values for omitted message fields
even if --defaults is not set
[boolean] [default: false]
--oneofs Output virtual oneof fields set to the present
field's name [boolean] [default: false]
--json Represent Infinity and NaN as strings in float
fields. Also decode google.protobuf.Any
automatically [boolean] [default: false]
--includeComments Generate doc comments from comments in the original
files [boolean] [default: false]
-I, --includeDirs Directories to search for included files [array]
-O, --outDir Directory in which to output files [string] [required]
--grpcLib The gRPC implementation library that these types will
be used with. If not provided, some types will not be
generated [string]
--inputTemplate Template for mapping input or "permissive" type names
[string] [default: "%s"]
--outputTemplate Template for mapping output or "restricted" type names
[string] [default: "%s__Output"]
-O, --outDir Directory in which to output files
[string] [required]
--grpcLib The gRPC implementation library that these types
will be used with. If not provided, some types will
not be generated [string]
--inputTemplate Template for mapping input or "permissive" type
names [string] [default: "%s"]
--outputTemplate Template for mapping output or "restricted" type
names [string] [default: "%s__Output"]
--inputBranded Output property for branded type for "permissive"
types with fullName of the Message as its value
[boolean] [default: false]
--outputBranded Output property for branded type for "restricted"
types with fullName of the Message as its value
[boolean] [default: false]
--targetFileExtension File extension for generated files.
[string] [default: ".ts"]
--importFileExtension File extension for import specifiers in generated
code. [string] [default: ""]
```
### Example Usage

View File

@ -47,6 +47,8 @@ type GeneratorOptions = Protobuf.IParseOptions & Protobuf.IConversionOptions & {
outputTemplate: string;
inputBranded: boolean;
outputBranded: boolean;
targetFileExtension?: string;
importFileExtension?: string;
}
class TextFormatter {
@ -105,8 +107,8 @@ function getImportPath(to: Protobuf.Type | Protobuf.Enum | Protobuf.Service): st
return stripLeadingPeriod(to.fullName).replace(/\./g, '/');
}
function getPath(to: Protobuf.Type | Protobuf.Enum | Protobuf.Service) {
return stripLeadingPeriod(to.fullName).replace(/\./g, '/') + '.ts';
function getPath(to: Protobuf.Type | Protobuf.Enum | Protobuf.Service, options: GeneratorOptions) {
return stripLeadingPeriod(to.fullName).replace(/\./g, '/') + options.targetFileExtension;
}
function getPathToRoot(from: Protobuf.NamespaceBase) {
@ -153,7 +155,7 @@ function getImportLine(dependency: Protobuf.Type | Protobuf.Enum | Protobuf.Serv
throw new Error('Invalid object passed to getImportLine');
}
}
return `import type { ${importedTypes} } from '${filePath}';`
return `import type { ${importedTypes} } from '${filePath}${options.importFileExtension}';`
}
function getChildMessagesAndEnums(namespace: Protobuf.NamespaceBase): (Protobuf.Type | Protobuf.Enum)[] {
@ -787,21 +789,21 @@ function generateFilesForNamespace(namespace: Protobuf.NamespaceBase, options: G
if (nested instanceof Protobuf.Type) {
generateMessageInterfaces(fileFormatter, nested, options);
if (options.verbose) {
console.log(`Writing ${options.outDir}/${getPath(nested)} from file ${nested.filename}`);
console.log(`Writing ${options.outDir}/${getPath(nested, options)} from file ${nested.filename}`);
}
filePromises.push(writeFile(`${options.outDir}/${getPath(nested)}`, fileFormatter.getFullText()));
filePromises.push(writeFile(`${options.outDir}/${getPath(nested, options)}`, fileFormatter.getFullText()));
} else if (nested instanceof Protobuf.Enum) {
generateEnumInterface(fileFormatter, nested, options);
if (options.verbose) {
console.log(`Writing ${options.outDir}/${getPath(nested)} from file ${nested.filename}`);
console.log(`Writing ${options.outDir}/${getPath(nested, options)} from file ${nested.filename}`);
}
filePromises.push(writeFile(`${options.outDir}/${getPath(nested)}`, fileFormatter.getFullText()));
filePromises.push(writeFile(`${options.outDir}/${getPath(nested, options)}`, fileFormatter.getFullText()));
} else if (nested instanceof Protobuf.Service) {
generateServiceInterfaces(fileFormatter, nested, options);
if (options.verbose) {
console.log(`Writing ${options.outDir}/${getPath(nested)} from file ${nested.filename}`);
console.log(`Writing ${options.outDir}/${getPath(nested, options)} from file ${nested.filename}`);
}
filePromises.push(writeFile(`${options.outDir}/${getPath(nested)}`, fileFormatter.getFullText()));
filePromises.push(writeFile(`${options.outDir}/${getPath(nested, options)}`, fileFormatter.getFullText()));
} else if (isNamespaceBase(nested)) {
filePromises.push(...generateFilesForNamespace(nested, options));
}
@ -877,6 +879,8 @@ async function runScript() {
.option('outputTemplate', { string: true, default: `${templateStr}__Output` })
.option('inputBranded', boolDefaultFalseOption)
.option('outputBranded', boolDefaultFalseOption)
.option('targetFileExtension', { string: true, default: '.ts' })
.option('importFileExtension', { string: true, default: '' })
.coerce('longs', value => {
switch (value) {
case 'String': return String;
@ -916,6 +920,8 @@ async function runScript() {
outputTemplate: 'Template for mapping output or "restricted" type names',
inputBranded: 'Output property for branded type for "permissive" types with fullName of the Message as its value',
outputBranded: 'Output property for branded type for "restricted" types with fullName of the Message as its value',
targetFileExtension: 'File extension for generated files.',
importFileExtension: 'File extension for import specifiers in generated code.'
}).demandOption(['outDir'])
.demand(1)
.usage('$0 [options] filenames...')