From 00eec2445b9f342949dc2b17b684a480543229b7 Mon Sep 17 00:00:00 2001 From: Truman Mulholland <59830782+trumully@users.noreply.github.com> Date: Wed, 2 Jul 2025 03:38:02 +1200 Subject: [PATCH] [TS] Fix relative paths for exports (#8517) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes an issue where exports were using incorrect relative paths for >=3 namespace levels. This is fixed by making the starting range of the namespace components relative to the amount of components. Co-authored-by: Björn Harrtell --- src/idl_gen_ts.cpp | 19 +++--- tests/long_namespace.fbs | 7 +++ tests/longer_namespace.fbs | 7 +++ tests/ts/TypeScriptTest.py | 3 + tests/ts/com/company/test.ts | 5 ++ tests/ts/com/company/test/person.ts | 70 +++++++++++++++++++++ tests/ts/longer-namespace/a/b/c.d.ts | 1 + tests/ts/longer-namespace/a/b/c.js | 3 + tests/ts/longer-namespace/a/b/c.ts | 5 ++ tests/ts/longer-namespace/a/b/c/person.d.ts | 18 ++++++ tests/ts/longer-namespace/a/b/c/person.js | 54 ++++++++++++++++ tests/ts/longer-namespace/a/b/c/person.ts | 70 +++++++++++++++++++++ tests/ts/tsconfig.json | 4 +- 13 files changed, 255 insertions(+), 11 deletions(-) create mode 100644 tests/long_namespace.fbs create mode 100644 tests/longer_namespace.fbs create mode 100644 tests/ts/com/company/test.ts create mode 100644 tests/ts/com/company/test/person.ts create mode 100644 tests/ts/longer-namespace/a/b/c.d.ts create mode 100644 tests/ts/longer-namespace/a/b/c.js create mode 100644 tests/ts/longer-namespace/a/b/c.ts create mode 100644 tests/ts/longer-namespace/a/b/c/person.d.ts create mode 100644 tests/ts/longer-namespace/a/b/c/person.js create mode 100644 tests/ts/longer-namespace/a/b/c/person.ts diff --git a/src/idl_gen_ts.cpp b/src/idl_gen_ts.cpp index a4cb119c0..234c2be6c 100644 --- a/src/idl_gen_ts.cpp +++ b/src/idl_gen_ts.cpp @@ -263,14 +263,9 @@ class TsGenerator : public BaseGenerator { for (const auto &def : it.second.definitions) { std::vector rel_components; // build path for root level vs child level - if (it.second.ns->components.size() > 1) - std::copy(it.second.ns->components.begin() + 1, - it.second.ns->components.end(), - std::back_inserter(rel_components)); - else - std::copy(it.second.ns->components.begin(), - it.second.ns->components.end(), - std::back_inserter(rel_components)); + if (it.second.ns->components.size() > 0) { + rel_components.push_back(it.second.ns->components.back()); + } auto base_file_name = namer_.File(*(def.second), SkipFile::SuffixAndExtension); auto base_name = @@ -303,8 +298,12 @@ class TsGenerator : public BaseGenerator { if (it2.second.ns->components.size() != child_ns_level) continue; auto ts_file_path = it2.second.path + ".ts"; code += "export * as " + it2.second.symbolic_name + " from './"; - std::string rel_path = it2.second.path; - code += rel_path + ".js';\n"; + int count = it2.second.ns->components.size() > 1 ? 2 : 1; + std::vector rel_path; + std::copy(it2.second.ns->components.end() - count, + it2.second.ns->components.end(), + std::back_inserter(rel_path)); + code += namer_.Directories(rel_path, SkipDir::OutputPathAndTrailingPathSeparator) + ".js';\n"; export_counter++; } diff --git a/tests/long_namespace.fbs b/tests/long_namespace.fbs new file mode 100644 index 000000000..08dc64b37 --- /dev/null +++ b/tests/long_namespace.fbs @@ -0,0 +1,7 @@ +namespace com.company.test; + +table Person { + name:string; + age:short; +} +root_type Person; \ No newline at end of file diff --git a/tests/longer_namespace.fbs b/tests/longer_namespace.fbs new file mode 100644 index 000000000..e1748dab2 --- /dev/null +++ b/tests/longer_namespace.fbs @@ -0,0 +1,7 @@ +namespace longer_namespace.a.b.c; + +table Person { + name:string; + age:short; +} +root_type Person; \ No newline at end of file diff --git a/tests/ts/TypeScriptTest.py b/tests/ts/TypeScriptTest.py index c6c7cb7f9..a9ffe952a 100755 --- a/tests/ts/TypeScriptTest.py +++ b/tests/ts/TypeScriptTest.py @@ -122,6 +122,9 @@ flatc( schema="../union_underlying_type_test.fbs" ) +flatc(options=["--ts"], schema="../long_namespace.fbs") +flatc(options=["--ts"], schema="../longer_namespace.fbs") + print("Running TypeScript Compiler...") check_call(["tsc"]) print("Running TypeScript Compiler in old node resolution mode for no_import_ext...") diff --git a/tests/ts/com/company/test.ts b/tests/ts/com/company/test.ts new file mode 100644 index 000000000..87f26d217 --- /dev/null +++ b/tests/ts/com/company/test.ts @@ -0,0 +1,5 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */ + +export { Person } from './test/person.js'; diff --git a/tests/ts/com/company/test/person.ts b/tests/ts/com/company/test/person.ts new file mode 100644 index 000000000..98ca2d59f --- /dev/null +++ b/tests/ts/com/company/test/person.ts @@ -0,0 +1,70 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */ + +import * as flatbuffers from 'flatbuffers'; + + + +export class Person { + bb: flatbuffers.ByteBuffer|null = null; + bb_pos = 0; + __init(i:number, bb:flatbuffers.ByteBuffer):Person { + this.bb_pos = i; + this.bb = bb; + return this; +} + +static getRootAsPerson(bb:flatbuffers.ByteBuffer, obj?:Person):Person { + return (obj || new Person()).__init(bb.readInt32(bb.position()) + bb.position(), bb); +} + +static getSizePrefixedRootAsPerson(bb:flatbuffers.ByteBuffer, obj?:Person):Person { + bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH); + return (obj || new Person()).__init(bb.readInt32(bb.position()) + bb.position(), bb); +} + +name():string|null +name(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null +name(optionalEncoding?:any):string|Uint8Array|null { + const offset = this.bb!.__offset(this.bb_pos, 4); + return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null; +} + +age():number { + const offset = this.bb!.__offset(this.bb_pos, 6); + return offset ? this.bb!.readInt16(this.bb_pos + offset) : 0; +} + +static startPerson(builder:flatbuffers.Builder) { + builder.startObject(2); +} + +static addName(builder:flatbuffers.Builder, nameOffset:flatbuffers.Offset) { + builder.addFieldOffset(0, nameOffset, 0); +} + +static addAge(builder:flatbuffers.Builder, age:number) { + builder.addFieldInt16(1, age, 0); +} + +static endPerson(builder:flatbuffers.Builder):flatbuffers.Offset { + const offset = builder.endObject(); + return offset; +} + +static finishPersonBuffer(builder:flatbuffers.Builder, offset:flatbuffers.Offset) { + builder.finish(offset); +} + +static finishSizePrefixedPersonBuffer(builder:flatbuffers.Builder, offset:flatbuffers.Offset) { + builder.finish(offset, undefined, true); +} + +static createPerson(builder:flatbuffers.Builder, nameOffset:flatbuffers.Offset, age:number):flatbuffers.Offset { + Person.startPerson(builder); + Person.addName(builder, nameOffset); + Person.addAge(builder, age); + return Person.endPerson(builder); +} +} diff --git a/tests/ts/longer-namespace/a/b/c.d.ts b/tests/ts/longer-namespace/a/b/c.d.ts new file mode 100644 index 000000000..e57007967 --- /dev/null +++ b/tests/ts/longer-namespace/a/b/c.d.ts @@ -0,0 +1 @@ +export { Person } from './c/person.js'; diff --git a/tests/ts/longer-namespace/a/b/c.js b/tests/ts/longer-namespace/a/b/c.js new file mode 100644 index 000000000..b448f9100 --- /dev/null +++ b/tests/ts/longer-namespace/a/b/c.js @@ -0,0 +1,3 @@ +// automatically generated by the FlatBuffers compiler, do not modify +/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */ +export { Person } from './c/person.js'; diff --git a/tests/ts/longer-namespace/a/b/c.ts b/tests/ts/longer-namespace/a/b/c.ts new file mode 100644 index 000000000..a30937e06 --- /dev/null +++ b/tests/ts/longer-namespace/a/b/c.ts @@ -0,0 +1,5 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */ + +export { Person } from './c/person.js'; diff --git a/tests/ts/longer-namespace/a/b/c/person.d.ts b/tests/ts/longer-namespace/a/b/c/person.d.ts new file mode 100644 index 000000000..cbd8fcb41 --- /dev/null +++ b/tests/ts/longer-namespace/a/b/c/person.d.ts @@ -0,0 +1,18 @@ +import * as flatbuffers from 'flatbuffers'; +export declare class Person { + bb: flatbuffers.ByteBuffer | null; + bb_pos: number; + __init(i: number, bb: flatbuffers.ByteBuffer): Person; + static getRootAsPerson(bb: flatbuffers.ByteBuffer, obj?: Person): Person; + static getSizePrefixedRootAsPerson(bb: flatbuffers.ByteBuffer, obj?: Person): Person; + name(): string | null; + name(optionalEncoding: flatbuffers.Encoding): string | Uint8Array | null; + age(): number; + static startPerson(builder: flatbuffers.Builder): void; + static addName(builder: flatbuffers.Builder, nameOffset: flatbuffers.Offset): void; + static addAge(builder: flatbuffers.Builder, age: number): void; + static endPerson(builder: flatbuffers.Builder): flatbuffers.Offset; + static finishPersonBuffer(builder: flatbuffers.Builder, offset: flatbuffers.Offset): void; + static finishSizePrefixedPersonBuffer(builder: flatbuffers.Builder, offset: flatbuffers.Offset): void; + static createPerson(builder: flatbuffers.Builder, nameOffset: flatbuffers.Offset, age: number): flatbuffers.Offset; +} diff --git a/tests/ts/longer-namespace/a/b/c/person.js b/tests/ts/longer-namespace/a/b/c/person.js new file mode 100644 index 000000000..2195fc45b --- /dev/null +++ b/tests/ts/longer-namespace/a/b/c/person.js @@ -0,0 +1,54 @@ +// automatically generated by the FlatBuffers compiler, do not modify +/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */ +import * as flatbuffers from 'flatbuffers'; +export class Person { + constructor() { + this.bb = null; + this.bb_pos = 0; + } + __init(i, bb) { + this.bb_pos = i; + this.bb = bb; + return this; + } + static getRootAsPerson(bb, obj) { + return (obj || new Person()).__init(bb.readInt32(bb.position()) + bb.position(), bb); + } + static getSizePrefixedRootAsPerson(bb, obj) { + bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH); + return (obj || new Person()).__init(bb.readInt32(bb.position()) + bb.position(), bb); + } + name(optionalEncoding) { + const offset = this.bb.__offset(this.bb_pos, 4); + return offset ? this.bb.__string(this.bb_pos + offset, optionalEncoding) : null; + } + age() { + const offset = this.bb.__offset(this.bb_pos, 6); + return offset ? this.bb.readInt16(this.bb_pos + offset) : 0; + } + static startPerson(builder) { + builder.startObject(2); + } + static addName(builder, nameOffset) { + builder.addFieldOffset(0, nameOffset, 0); + } + static addAge(builder, age) { + builder.addFieldInt16(1, age, 0); + } + static endPerson(builder) { + const offset = builder.endObject(); + return offset; + } + static finishPersonBuffer(builder, offset) { + builder.finish(offset); + } + static finishSizePrefixedPersonBuffer(builder, offset) { + builder.finish(offset, undefined, true); + } + static createPerson(builder, nameOffset, age) { + Person.startPerson(builder); + Person.addName(builder, nameOffset); + Person.addAge(builder, age); + return Person.endPerson(builder); + } +} diff --git a/tests/ts/longer-namespace/a/b/c/person.ts b/tests/ts/longer-namespace/a/b/c/person.ts new file mode 100644 index 000000000..98ca2d59f --- /dev/null +++ b/tests/ts/longer-namespace/a/b/c/person.ts @@ -0,0 +1,70 @@ +// automatically generated by the FlatBuffers compiler, do not modify + +/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */ + +import * as flatbuffers from 'flatbuffers'; + + + +export class Person { + bb: flatbuffers.ByteBuffer|null = null; + bb_pos = 0; + __init(i:number, bb:flatbuffers.ByteBuffer):Person { + this.bb_pos = i; + this.bb = bb; + return this; +} + +static getRootAsPerson(bb:flatbuffers.ByteBuffer, obj?:Person):Person { + return (obj || new Person()).__init(bb.readInt32(bb.position()) + bb.position(), bb); +} + +static getSizePrefixedRootAsPerson(bb:flatbuffers.ByteBuffer, obj?:Person):Person { + bb.setPosition(bb.position() + flatbuffers.SIZE_PREFIX_LENGTH); + return (obj || new Person()).__init(bb.readInt32(bb.position()) + bb.position(), bb); +} + +name():string|null +name(optionalEncoding:flatbuffers.Encoding):string|Uint8Array|null +name(optionalEncoding?:any):string|Uint8Array|null { + const offset = this.bb!.__offset(this.bb_pos, 4); + return offset ? this.bb!.__string(this.bb_pos + offset, optionalEncoding) : null; +} + +age():number { + const offset = this.bb!.__offset(this.bb_pos, 6); + return offset ? this.bb!.readInt16(this.bb_pos + offset) : 0; +} + +static startPerson(builder:flatbuffers.Builder) { + builder.startObject(2); +} + +static addName(builder:flatbuffers.Builder, nameOffset:flatbuffers.Offset) { + builder.addFieldOffset(0, nameOffset, 0); +} + +static addAge(builder:flatbuffers.Builder, age:number) { + builder.addFieldInt16(1, age, 0); +} + +static endPerson(builder:flatbuffers.Builder):flatbuffers.Offset { + const offset = builder.endObject(); + return offset; +} + +static finishPersonBuffer(builder:flatbuffers.Builder, offset:flatbuffers.Offset) { + builder.finish(offset); +} + +static finishSizePrefixedPersonBuffer(builder:flatbuffers.Builder, offset:flatbuffers.Offset) { + builder.finish(offset, undefined, true); +} + +static createPerson(builder:flatbuffers.Builder, nameOffset:flatbuffers.Offset, age:number):flatbuffers.Offset { + Person.startPerson(builder); + Person.addName(builder, nameOffset); + Person.addAge(builder, age); + return Person.endPerson(builder); +} +} diff --git a/tests/ts/tsconfig.json b/tests/ts/tsconfig.json index eb08992b4..62a31c438 100644 --- a/tests/ts/tsconfig.json +++ b/tests/ts/tsconfig.json @@ -15,6 +15,8 @@ "namespace_test/**/*.ts", "union_vector/**/*.ts", "arrays_test_complex/**/*.ts", - "union_underlying_type_test.ts" + "union_underlying_type_test.ts", + "long-namespace/**/*.ts", + "longer-namespace/**/*.ts" ] }