diff --git a/integration/extension-import/base.proto b/integration/extension-import/base.proto new file mode 100644 index 000000000..1a7c6bdfa --- /dev/null +++ b/integration/extension-import/base.proto @@ -0,0 +1,9 @@ +syntax = "proto2"; + +package foo; + +message Extendable{ + required string field = 1; + + extensions 4 to max; +} diff --git a/integration/extension-import/extension-import-test.ts b/integration/extension-import/extension-import-test.ts new file mode 100644 index 000000000..1d19f2263 --- /dev/null +++ b/integration/extension-import/extension-import-test.ts @@ -0,0 +1,13 @@ +import * as path from "node:path"; +import * as fs from "node:fs"; +import * as ts from "typescript"; + +describe('extension-only-files', () => { + it("generate as external module", () => { + const generatedPath = path.join(__dirname, "extension.ts"); + const generatedCode = fs.readFileSync(generatedPath, "utf8"); + const source = ts.createSourceFile(generatedPath, generatedCode, ts.ScriptTarget.ES2018); + + expect(ts.isExternalModule(source)).toBe(true); + }) +}); diff --git a/integration/extension-import/extension.proto b/integration/extension-import/extension.proto new file mode 100644 index 000000000..9f5c31635 --- /dev/null +++ b/integration/extension-import/extension.proto @@ -0,0 +1,9 @@ +syntax = "proto2"; + +import "base.proto"; + +package foo; + +extend Extendable{ + optional int32 bar = 10; +} diff --git a/integration/extension-import/extension.ts b/integration/extension-import/extension.ts new file mode 100644 index 000000000..c71cf7316 --- /dev/null +++ b/integration/extension-import/extension.ts @@ -0,0 +1,3 @@ +/* eslint-disable */ + +export {}; diff --git a/integration/extension-import/parameters.txt b/integration/extension-import/parameters.txt new file mode 100644 index 000000000..91f448d9f --- /dev/null +++ b/integration/extension-import/parameters.txt @@ -0,0 +1 @@ +outputIndex=true,esModuleInterop=true diff --git a/integration/simple-esmodule-interop/empty-test.ts b/integration/simple-esmodule-interop/empty-test.ts new file mode 100644 index 000000000..8c70177cf --- /dev/null +++ b/integration/simple-esmodule-interop/empty-test.ts @@ -0,0 +1,7 @@ +import { } from './empty'; + +describe('empty', () => { + it('compiles', () => { + // if ./empty was not a module, this would not compile. + }); +}); diff --git a/integration/simple-esmodule-interop/empty.proto b/integration/simple-esmodule-interop/empty.proto new file mode 100644 index 000000000..9f5901bc7 --- /dev/null +++ b/integration/simple-esmodule-interop/empty.proto @@ -0,0 +1,2 @@ +syntax = "proto2"; +package empty; diff --git a/integration/simple-esmodule-interop/empty.ts b/integration/simple-esmodule-interop/empty.ts new file mode 100644 index 000000000..7bdf551c4 --- /dev/null +++ b/integration/simple-esmodule-interop/empty.ts @@ -0,0 +1,3 @@ +/* eslint-disable */ + +export const protobufPackage = "empty"; diff --git a/src/main.ts b/src/main.ts index e02f06e53..36f998e7e 100644 --- a/src/main.ts +++ b/src/main.ts @@ -382,6 +382,15 @@ export function generateFile(ctx: Context, fileDesc: FileDescriptorProto): [stri chunks.push(...generateSchema(ctx, fileDesc, sourceInfo)); } + // https://www.typescriptlang.org/docs/handbook/2/modules.html: + // > In TypeScript, just as in ECMAScript 2015, any file containing a top-level import or export is considered a module. + // > Conversely, a file without any top-level import or export declarations is treated as a script whose contents are available in the global scope (and therefore to modules as well). + // + // Thus, to mark an empty file a module, we need to add `export {}` to it. + if (options.esModuleInterop && chunks.length === 0) { + chunks.push(code`export {};`); + } + chunks.push( ...Object.values(utils).map((v) => { if (v instanceof ConditionalOutput) {