diff --git a/.gitignore b/.gitignore index 1cab7f703..3e935fda9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /.idea +/.vscode .DS_Store /node_modules diff --git a/packages/plugin-framework/src/typescript-imports.ts b/packages/plugin-framework/src/typescript-imports.ts index a9cc9040b..b4acac7e5 100644 --- a/packages/plugin-framework/src/typescript-imports.ts +++ b/packages/plugin-framework/src/typescript-imports.ts @@ -9,10 +9,11 @@ import {TypescriptFile} from "./typescript-file"; export class TypeScriptImports { private readonly symbols: SymbolTable; + private readonly enable_import_extensions: boolean; - - constructor(symbols: SymbolTable) { + constructor(symbols: SymbolTable, enable_import_extensions: boolean) { this.symbols = symbols; + this.enable_import_extensions = enable_import_extensions; } @@ -76,7 +77,8 @@ export class TypeScriptImports { // add an import statement const importPath = createRelativeImportPath( source.getSourceFile().fileName, - symbolReg.file.getFilename() + symbolReg.file.getFilename(), + this.enable_import_extensions ); const blackListedNames = this.symbols.list(source).map(e => e.name); return ensureNamedImportPresent( @@ -285,7 +287,7 @@ export function findNamedImports(sourceFile: ts.SourceFile): { name: string, as: * Create a relative path for an import statement like * `import {Foo} from "./foo"` */ -function createRelativeImportPath(currentPath: string, pathToImportFrom: string): string { +function createRelativeImportPath(currentPath: string, pathToImportFrom: string, addExtension: boolean): string { // create relative path to the file to import let fromPath = path.relative(path.dirname(currentPath), pathToImportFrom); @@ -302,5 +304,10 @@ function createRelativeImportPath(currentPath: string, pathToImportFrom: string) if (!fromPath.startsWith('../') && !fromPath.startsWith('./')) { fromPath = './' + fromPath; } + + // add .js extensions on import statements for ESM compatibility with typescript and nodejs + if (addExtension === true) { + fromPath = fromPath + ".js"; + } return fromPath; } diff --git a/packages/plugin/src/our-options.ts b/packages/plugin/src/our-options.ts index fca43b1df..0f3b35c3c 100644 --- a/packages/plugin/src/our-options.ts +++ b/packages/plugin/src/our-options.ts @@ -200,6 +200,7 @@ export interface InternalOptions { readonly transpileTarget: ts.ScriptTarget | undefined, readonly transpileModule: ts.ModuleKind, readonly addPbSuffix: boolean; + readonly enable_import_extensions: boolean; } export function makeInternalOptions( @@ -232,6 +233,7 @@ export function makeInternalOptions( output_javascript_es2019: boolean, output_javascript_es2020: boolean, output_legacy_commonjs: boolean, + enable_import_extensions: boolean, }, pluginCredit?: string, ): InternalOptions { @@ -260,6 +262,7 @@ export function makeInternalOptions( transpileTarget: undefined, transpileModule: ts.ModuleKind.ES2015, addPbSuffix: false, + enable_import_extensions: true, }, ) as Writeable; if (pluginCredit) { @@ -418,4 +421,3 @@ export class OptionResolver { } } - diff --git a/packages/plugin/src/protobufts-plugin.ts b/packages/plugin/src/protobufts-plugin.ts index 1724cec36..9f10f35bc 100644 --- a/packages/plugin/src/protobufts-plugin.ts +++ b/packages/plugin/src/protobufts-plugin.ts @@ -124,6 +124,11 @@ export class ProtobuftsPlugin extends PluginBase { excludes: ["output_typescript"] }, + // Add file extension for ESM compatibility with node + enable_import_extensions: { + description: "Use .js extension for ESM import statements", + }, + // client client_none: { description: "Do not generate rpc clients. \n" + @@ -216,7 +221,7 @@ export class ProtobuftsPlugin extends PluginBase { registry = DescriptorRegistry.createFrom(request), symbols = new SymbolTable(), fileTable = new FileTable(), - imports = new TypeScriptImports(symbols), + imports = new TypeScriptImports(symbols, options.enable_import_extensions), comments = new CommentGenerator(registry), interpreter = new Interpreter(registry, options), optionResolver = new OptionResolver(interpreter, registry, options),