grpc / grpc-node

gRPC for Node.js
https://grpc.io
Apache License 2.0
4.45k stars 645 forks source link

Generated proto types are not valid for ES modules #2401

Open joaofranciscosantos opened 1 year ago

joaofranciscosantos commented 1 year ago

Environment

Problem description

The generated file(s) are not compatible with ES modules.

Reproduction steps

How to Fix

The generated import should end with ".js": (See image below)

import type { BookItem as _bookStorePackage_BookItem, BookItem__Output as _bookStorePackage_BookItem__Output } from '../bookStorePackage/BookItem.js';

Additional context

Current error:

error TS2835: Relative import paths need explicit file extensions in EcmaScript imports when '--moduleResolution' is 'node16' or 'nodenext'. Did you mean './bookStorePackage/Book.js'?

Proto file:

syntax = 'proto3';

package bookStorePackage;

service Book {
    rpc createBook (BookItem) returns (BookItem);
}

message BookItem {
    int32 id = 1;
    string book = 2;
}

Image with error:

Screenshot 2023-03-29 at 00 24 13
DokiDoki1103 commented 1 year ago

好像不能用import都要用require

joaofranciscosantos commented 1 year ago

Note that the file is auto-generated. It should not be modified manually

atjn commented 6 months ago

I am having the same issue in Deno. It is very problematic because the ESM module specification explicitly disallows imports without the file extension, so there are a lot of environments that do not support these imports.

meros commented 6 months ago

This should be reopened as the fix was reverted. This needs to be adressed somehow as esm won't go anywhere :)

meros commented 6 months ago

Thanks, I'll take a look at the reasons for the revert as soon as I have a chance and see if I can help out somehow.

chernodub commented 4 months ago

Meanwhile, I figured a simple codemod that would make the generated files to be ESM-compatible. Hopefully, some would find this useful:

  1. npx proto-loader-gen-types --longs=String --enums=String --defaults --oneofs --grpcLib=@grpc/grpc-js --outDir=src/protos/generated/ src/protos/*.proto

  2. Use codemod

proto-codemod.js

import { readFileSync, readdirSync, writeFileSync } from 'fs';
import { join } from 'path';

const relativePathToDirArg = process.argv[2];

const absolutePathToDir = join(import.meta.dirname, relativePathToDirArg);

readdirSync(absolutePathToDir, { recursive: true })
    .filter((/** @type {string} */ filename) => filename.endsWith('.ts'))
    .forEach((filename) => {
        const pathToFile = join(absolutePathToDir, filename);

        const file = readFileSync(pathToFile, { encoding: 'utf-8' });

        // Looking for all relative import paths and appending ".js" to the end of the import path
        const modifiedFile = file.replaceAll(/(import .* '\.+\/.*)';/g, "$1.js';");

        writeFileSync(pathToFile, modifiedFile);
    });

Run with node proto-codemod.js ./src/protos/generated

💡 make sure to replace the paths to generated files with yours