Skip to content

Commit

Permalink
create cli to generate typing from protobuf file
Browse files Browse the repository at this point in the history
  • Loading branch information
zgid123 committed Apr 18, 2023
1 parent 2b4b418 commit b32950f
Show file tree
Hide file tree
Showing 19 changed files with 974 additions and 83 deletions.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,9 @@ yarn-error.log*
lib
.turbo
dist

debug
target
**/*.rs.bk
*.pdb
Cargo.lock
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[rust]": {
"editor.defaultFoldingRangeProvider": "rust-lang.rust-analyzer"
}
}
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@ gRPC Library for Javascript project.
# Usage

- [core](packages/core/README.md)

- [cli](packages/cli/README.md)

- [fastify-client](packages/fastify/client/README.md)

- [fastify-server](packages/fastify/server/README.md)

# TODO
- [nestjs-client](packages/nestjs/client/README.md)

- [ ] Write CLI to generate type for TypeScript based on proto files
- [nestjs-server](packages/nestjs/server/README.md)
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"grpc-javascript",
"grpc-nodejs",
"grpc-nestjs",
"grpc-fastify"
"grpc-fastify",
"grpc-cli"
],
"author": "Alpha",
"repository": {
Expand All @@ -39,8 +40,8 @@
"husky": "^8.0.3",
"lint-staged": "^13.2.1",
"prettier": "^2.8.7",
"rollup": "^3.20.4",
"turbo": "^1.9.2",
"rollup": "^3.20.5",
"turbo": "^1.9.3",
"typescript": "^5.0.4"
},
"dependencies": {
Expand Down
1 change: 1 addition & 0 deletions packages/cli/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
bin/index.js
4 changes: 4 additions & 0 deletions packages/cli/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.turbo
src
tsconfig.json
rollup.config.ts
46 changes: 46 additions & 0 deletions packages/cli/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
A CLI to generate TypeScript typings from protobuf for gRPC.

This package is for supporting `@grpc.ts` packages.

# Install

```sh
npm install --save-dev @grpc.ts/cli

# or

yarn add -D @grpc.ts/cli

# or

pnpm add -D @grpc.ts/cli
```

# Usage

- Create config file `grpc-cli.ts`

```ts
import type { IConfigProps } from '@grpc.ts/cli';

const config: IConfigProps = {
paths: ['../proto/*.proto'],
external: ['google.protobuf'],
};

export default config;
```

- Run script

```sh
pnpm gen-grpc-typing
```

Default output will be `protobufTypings`. Change `output` option to specify the folder you want to store the typing files.

# TODO

[ ] Support generate typing as monorepo project

[ ] Rewrite the parser in Rust
52 changes: 52 additions & 0 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"name": "@grpc.ts/cli",
"version": "1.0.0",
"license": "MIT",
"directories": {
"lib": "lib"
},
"author": "Alpha",
"description": "CLI package for @grpc.ts to generate typing for TypeScript",
"homepage": "https://github.com/zgid123/grpc-ts",
"keywords": [
"grpc",
"grpc-ts",
"grpc-js",
"grpc-typescript",
"grpc-javascript",
"grpc-cli"
],
"repository": {
"type": "git",
"url": "git+https://github.com/zgid123/grpc-ts"
},
"bin": {
"gen-grpc-typing": "lib/run.cjs"
},
"main": "./lib/index.cjs",
"module": "./lib/index.mjs",
"types": "./lib/index.d.ts",
"exports": {
".": {
"import": "./lib/index.mjs",
"require": "./lib/index.cjs",
"types": "./lib/index.d.ts"
}
},
"scripts": {
"prepublish": "pnpm build",
"build": "rollup --config rollup.config.ts --configPlugin typescript"
},
"dependencies": {
"@grpc.ts/core": "workspace:*",
"glob": "^10.2.1",
"prettier": "^2.8.7",
"protobufjs": "^7.2.3",
"ts-node": "^10.9.1"
},
"devDependencies": {
"@types/glob": "^8.1.0",
"@types/node": "^18.15.11",
"@types/prettier": "^2.7.2"
}
}
47 changes: 47 additions & 0 deletions packages/cli/rollup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { defineConfig } from 'rollup';
import json from '@rollup/plugin-json';
import commonjs from '@rollup/plugin-commonjs';
import resolve from '@rollup/plugin-node-resolve';
import typescript from '@rollup/plugin-typescript';

export default [
defineConfig({
input: 'src/index.ts',
plugins: [json(), resolve(), commonjs(), typescript()],
external: ['@grpc.ts/core', 'protobufjs'],
output: [
{
file: './lib/index.cjs',
format: 'cjs',
},
{
file: './lib/index.mjs',
format: 'es',
},
],
}),
defineConfig({
input: 'src/run.ts',
plugins: [json(), resolve(), commonjs(), typescript()],
external: [
'@grpc.ts/core',
'protobufjs',
'glob',
'prettier',
'ts-node',
'ts-node/register',
],
output: [
{
file: './lib/run.cjs',
format: 'cjs',
banner: '#!/usr/bin/env node\n',
},
{
file: './lib/run.mjs',
format: 'es',
banner: '#!/usr/bin/env node\n',
},
],
}),
];
1 change: 1 addition & 0 deletions packages/cli/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export type { IConfigProps } from './interface';
44 changes: 44 additions & 0 deletions packages/cli/src/interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import type { IEnum } from 'protobufjs';

export interface IConfigProps {
output?: string;
external?: string[];
paths?: string | string[];
}

export interface IOptionsProps {
external?: string[];
}

export interface IMessageProps {
name: string;
type: string;
rule?: 'repeated';
optional: boolean;
}

export type TNamespaceMessage = [string, IMessageProps[]];

export interface IServiceProps {
name: string;
requestType: string;
responseType: string;
}

export type TNamespaceService = [string, IServiceProps[]];

export type TEnum = IEnum['values'];

export type TNamespaceEnum = [string, TEnum[]];

export interface INamespaceDataProps {
enums: TNamespaceEnum[];
messages: TNamespaceMessage[];
services: TNamespaceService[];
}

export type TParseNamespaceReturn = [string, INamespaceDataProps];

export interface ICachedEnumsProps {
[key: string]: boolean;
}
106 changes: 106 additions & 0 deletions packages/cli/src/run.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { glob } from 'glob';
import { resolve } from 'path';
import { format } from 'prettier';
import { load } from 'protobufjs';
import { cwd } from 'node:process';
import { writeFile, mkdir } from 'node:fs/promises';

import { parse, wrapArray, loadConfig, combine } from './utils';
import {
createImportType,
createExportEnums,
createExportMessages,
createExportServices,
createExportPackageName,
} from './utils/contentHelper';

async function generate() {
const { external, output, paths } = await loadConfig();
const filePaths = await glob(
wrapArray(paths).map((path) => resolve(cwd(), path)),
);

Promise.all(
filePaths.map(async (filePath) => {
const root = await load(filePath);

const data = root.toJSON().nested;

if (!data) {
console.warn('No data from proto file');

return;
}

const result = parse(data, {
external,
});

let fileContent = createExportPackageName(result[0][0]);
let hasMetadata = false;
let hasGrpcTimestamp = false;
let hasService = false;

result.forEach(([_, packageData]) => {
const [enumsContent, cachedEnums] = createExportEnums(
packageData.enums,
);

const [messagesContent, containsGrpcTimestamp] = createExportMessages(
packageData.messages,
cachedEnums,
);

if (containsGrpcTimestamp) {
hasGrpcTimestamp = containsGrpcTimestamp;
}

if (packageData.services.length) {
hasService = true;
hasMetadata = true;
}

const servicesContent = createExportServices(packageData.services);

fileContent = combine(
{
joinWith: '\n',
},
fileContent,
enumsContent,
messagesContent,
servicesContent,
);
});

const fileName = filePath.slice(
filePath.lastIndexOf('/') + 1,
filePath.length - '.proto'.length,
);

fileContent = await createImportType(fileContent, {
hasService,
hasMetadata,
hasGrpcTimestamp,
});

await mkdir(output, { recursive: true });

writeFile(
`${output}/${fileName}.interface.ts`,
format(fileContent, {
singleQuote: true,
trailingComma: 'all',
jsxSingleQuote: true,
parser: 'typescript',
arrowParens: 'always',
}),
{
encoding: 'utf8',
},
);
}),
);
}

generate();
34 changes: 34 additions & 0 deletions packages/cli/src/utils/configuration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { register } from 'ts-node';
import { cwd } from 'node:process';

import type { IConfigProps } from '../interface';

export async function loadConfig(): Promise<Required<IConfigProps>> {
const service = register({
compilerOptions: {
module: 'CommonJS',
},
moduleTypes: {
'**': 'cjs',
},
});
service.enabled(true);

const path = `${cwd()}/grpc-cli.ts`;

let data: IConfigProps = {};

try {
data = require(path).default;
} catch {
// ignore error
}

service.enabled(false);

return {
paths: data.paths || [],
output: data.output || 'protobufTypings',
external: data.external || ['google.protobuf'],
};
}
Loading

0 comments on commit b32950f

Please sign in to comment.