grpc/grpc-web

ES Modules Support?

alangdm opened this issue Β· 24 comments

Hi, I'm new to grpc-web and I was checking your Web basics tutorial when I noticed that the client code generator only supports Closure and common.js

Now that ES Modules are supported on all modern browsers, is there a chance that you'll have a generator for them? That would allow the library to be used without any build process which would be great imho

This isn't a direct answer, but the TypeScript output does support modules. If you're not using TS in your project, a workaround would be to re-compile from the TS output of protoc down to ES6.

@jonahbron
I tried this, it was a bit annoying to configure but I managed to get it working

Here's a sample bash script for generating the ts files and the tsconfig.json to compile it to esm in case anyone has this same problem

protoc -I ./protos protos/*.proto --js_out=import_style=commonjs:./protos/ts --grpc-web_out=import_style=typescript,mode=grpcwebtext:./protos/ts
{
  "compilerOptions": {
    "target": "es2015", 
    "module": "es2015",
    "allowJs": true,
    "outDir": "../esm",
    "rootDir": "./",
    "strict": false,
    "moduleResolution": "node",
    "esModuleInterop": true
  }
}

Then again this only worked for the grpc-web_out files, the other files were still generated as commonjs modules, but I guess it's something

This actually seems to be a real problem when trying to use grpc-web with something like create-react-app or any Babel 7 Typescript. Since they don't support commonjs only ESModules. Currently the only solution seems to be to manually rollup the exports using something like this https://github.com/rollup/rollup-plugin-commonjs. Would love to find a more permanent solution though.

ESM modules would be really nice.

In the mean time, if you use grpc web with stencil and rollup, see this comment ionic-team/stencil#1343 (comment)

That would allow the library to be used without any build process which would be great imho

By the way, this is only true if both grpc-web AND the generated client-stubs has esm-support, so this issue has to be solved in two parts:

Part 1: Generating an esm-compatible grpc-web-client:

image

  • Part 1 is 99% solved already with the import_style=typescript-option πŸ‘
  • The only thing that is left for me to do after generating the EchoServiceClient.ts above, is to update the import paths to be esm-compatible.

Example:

Generated import paths Modified to be esm-compatible CDN alternative
image image image
  • FYI: Here is my protoc-gen-grpc-web-version:
$ brew info protoc-gen-grpc-web
protoc-gen-grpc-web: stable 1.2.1 (bottled)

Part 2: Convert the grpc-web-package to be esm-compatible.

  • After modifying the paths in Part 1, I will still get an error if I try to run my generated client directly in a browser, because the modules imported from ../node-modules/grpc-web/index.js and https://cdn.jsdelivr.net/npm/grpc-web@1.2.1/index.min.js are NOT esm-compatible.

  • Neither is './echo_pb.js', and it's depedency ../node-modules/google-protobuf/google-protobuf.js, for that matter, but that is out of scope of this repo.

  • This inspired me to explore how the grpc-web-package can be converted to be esm-compatible in a PR here --> Arxcis#1

  • I would love to discuss this more πŸš€

Part 2: Work in progress update

  • So I have been hacking along on my fork PR of grpc-web here --> Arxcis#2
  • I have put together a "transpiler" of sorts, which takes the exports.js-file as an input, and traverses all the dependencies, and outputs a es_modules-folder, with transpiled .js files.
  • I have gotten to a point where the browser is able to load all the modules - but not run. (see demo here --> Arxcis#2)
  • The main issue I am running into now, is that the closure-library the code I have generated has a few circular dependencies of top-level values, which the browser's ES modules loader is not too happy about. (details here --> Arxcis#2)
  • I honestly don't know how to proceed from here I think I would have to improve my generator to not generate top-level values, bu try to transform modules into ES classes instead, to solve the circular dependency issues πŸ™
  • Alternatively We could generate modules wrapped in an immediately invoked function closure, which returns the module.
  • Alterntive 2: We could just write code without circular deps. I am not holding my breath πŸ˜…

Any progress on this issue?

Any progress on this issue?

FYI: My experiment mentioned above, ended without any success. I have no affiliation with the maintainers of this repo. I don't know what the plan is. I am patiently waiting like you πŸ™

I am also waiting patiently. 😒

Are there any plans for this to be worked on? Currently having issues with using Vite alongside grpc-web.

Are there any plans for this to be worked on? Currently having issues with using Vite alongside grpc-web.

You can use this package https://github.com/timostamm/protobuf-ts which works great with Vite

Please πŸ™

is grpc supported es6?

Definitely would be nice to have for bundlers that don't support CommonJS. I'll try that rollup solution above, but first-party support would be much preferred.

Not sure if this will work for everyone, but it worked for me. I had this error when trying to import the generated client in an ESM project:

require is not defined in ES module scope, you can use import instead
This file is being treated as an ES module because it has a '.js' file extension and 'X\package.json' contains "type": "module".
To treat it as a CommonJS script, rename it to use the '.cjs' file extension

I fixed it by creating a package.json in the directory of my generated client (e.g. @/generated) with the following contents:

{
	"type": "commonjs",
	"main": "xyz_client_pb.js",
	"types": "xyz_client_pb.d.ts",
	"dependencies": {
		"google-protobuf": "^3.21.2"
	}
}

I needed to install google-protobuf from within the directory for this to work.

This essentially un-sets your "type": "module" setting for only those generated files. It seems you can pretty much use it as normal, for example:

import { XYZRequest } from "@/generated"
import { XYZClient } from "@/generated/xyz_client_grpc_pb"

We got this working by using moduleResolution: "bundler" and tsc-alias to rewrite imports missing .js extension.

I switched to @connectrpc/connect-web a couple of months ago because I ran into an issue with the client code that I couldn't hack my way out of anymore. The refactor was a headache, but the whole experience is definitely cleaner now. This issue really needs to take center stage if the gRPC-Web team doesn't want to lose more developers. The lack of modernity in the client code is baffling.

We also switched to connect-web for the very same reasons @PrestonMonteWest explained.

we need esm!

@sampajano It seems you are the active maintainer of this project, could you please take a look at this feature requestπŸ₯Ί

Hihi.. thanks for pinging on this FR.. Sorry for the inconveniences!

Lately we're working on migrating our codebase to Typescript, at which time i will need to rework all the toolchains we have. And i will ensure to take a look at improving our usability here as well!

@loeffel-io @ttc0419 FYI!