Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion goldens/ts/flatbuffers/goldens/universe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import * as flatbuffers from 'flatbuffers';

import { Galaxy } from '../../flatbuffers/goldens/galaxy.js';
import { Galaxy } from './galaxy.js';


export class Universe {
Expand Down
101 changes: 76 additions & 25 deletions src/idl_gen_ts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -318,9 +318,10 @@ class TsGenerator : public BaseGenerator {
export_counter++;
}

if (export_counter > 0)
if (export_counter > 0) {
parser_.opts.file_saver->SaveFile(it.second.filepath.c_str(), code,
false);
}
}
}

Expand Down Expand Up @@ -641,19 +642,20 @@ class TsGenerator : public BaseGenerator {
}

void GenStructArgs(import_set& imports, const StructDef& struct_def,
std::string* arguments, const std::string& nameprefix) {
const Definition& owner, std::string* arguments,
const std::string& nameprefix) {
for (auto it = struct_def.fields.vec.begin();
it != struct_def.fields.vec.end(); ++it) {
auto& field = **it;
if (IsStruct(field.value.type)) {
// Generate arguments for a struct inside a struct. To ensure names
// don't clash, and to make it obvious these arguments are constructing
// a nested struct, prefix the name with the field name.
GenStructArgs(imports, *field.value.type.struct_def, arguments,
GenStructArgs(imports, *field.value.type.struct_def, owner, arguments,
nameprefix + field.name + "_");
} else {
*arguments += ", " + nameprefix + field.name + ": " +
GenTypeName(imports, field, field.value.type, true,
GenTypeName(imports, owner, field.value.type, true,
field.IsOptional());
}
}
Expand Down Expand Up @@ -915,6 +917,48 @@ class TsGenerator : public BaseGenerator {
return symbols_expression;
}

std::vector<std::string> PathComponents(const std::string& path) const {
std::vector<std::string> components;
size_t start = 0;
while (start < path.size()) {
auto end = path.find(kPathSeparator, start);
if (end == std::string::npos) end = path.size();
if (end > start) {
components.emplace_back(path.substr(start, end - start));
}
if (end == path.size()) break;
start = end + 1;
}
return components;
}

std::string RelativeDirectory(const std::vector<std::string>& from,
const std::vector<std::string>& to) const {
size_t common = 0;
while (common < from.size() && common < to.size() &&
from[common] == to[common]) {
++common;
}

std::string rel;
const size_t ups = from.size() - common;
if (ups == 0) {
rel = ".";
} else {
for (size_t i = 0; i < ups; ++i) {
if (!rel.empty()) rel += kPathSeparator;
rel += "..";
}
}

for (size_t i = common; i < to.size(); ++i) {
if (!rel.empty()) rel += kPathSeparator;
rel += to[i];
}

return rel;
}

template <typename DefinitionT>
ImportDefinition AddImport(import_set& imports, const Definition& dependent,
const DefinitionT& dependency) {
Expand Down Expand Up @@ -942,26 +986,32 @@ class TsGenerator : public BaseGenerator {
const std::string symbols_expression = GenSymbolExpression(
dependency, has_name_clash, import_name, name, object_name);

std::string bare_file_path;
std::string rel_file_path;
if (dependent.defined_namespace) {
const auto& dep_comps = dependent.defined_namespace->components;
for (size_t i = 0; i < dep_comps.size(); i++) {
rel_file_path += i == 0 ? ".." : (kPathSeparator + std::string(".."));
}
if (dep_comps.size() == 0) {
rel_file_path += ".";
}
} else {
rel_file_path += "..";
}
const Namespace* dependent_ns = dependent.defined_namespace
? dependent.defined_namespace
: parser_.empty_namespace_;
const Namespace* dependency_ns = dependency.defined_namespace
? dependency.defined_namespace
: parser_.empty_namespace_;

bare_file_path +=
kPathSeparator +
namer_.Directories(dependency.defined_namespace->components,
SkipDir::OutputPath) +
const std::string dependent_dirs =
namer_.Directories(*dependent_ns, SkipDir::OutputPath);
const std::string dependency_dirs =
namer_.Directories(*dependency_ns, SkipDir::OutputPath);

const auto dependent_components = PathComponents(dependent_dirs);
const auto dependency_components = PathComponents(dependency_dirs);

std::string rel_dir =
RelativeDirectory(dependent_components, dependency_components);
if (rel_dir.empty()) rel_dir = ".";
if (!rel_dir.empty()) rel_dir += kPathSeparator;

std::string rel_file_path =
rel_dir + namer_.File(dependency, SkipFile::SuffixAndExtension);

std::string bare_file_path =
kPathSeparator + dependency_dirs +
namer_.File(dependency, SkipFile::SuffixAndExtension);
rel_file_path += bare_file_path;

ImportDefinition import;
import.name = name;
Expand Down Expand Up @@ -1624,11 +1674,12 @@ class TsGenerator : public BaseGenerator {
GenDocComment(struct_def.doc_comment, code_ptr);
code += "export class ";
code += object_name;
if (parser.opts.generate_object_based_api)
if (parser.opts.generate_object_based_api) {
code += " implements flatbuffers.IUnpackableObject<" + object_api_name +
"> {\n";
else
} else {
code += " {\n";
}
code += " bb: flatbuffers.ByteBuffer|null = null;\n";
code += " bb_pos = 0;\n";

Expand Down Expand Up @@ -2043,7 +2094,7 @@ class TsGenerator : public BaseGenerator {
// Emit a factory constructor
if (struct_def.fixed) {
std::string arguments;
GenStructArgs(imports, struct_def, &arguments, "");
GenStructArgs(imports, struct_def, struct_def, &arguments, "");
GenDocComment(code_ptr);

code += "static create" + GetPrefixedName(struct_def) +
Expand Down
28 changes: 28 additions & 0 deletions tests/ts/JavaScriptRelativeImportPathTest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
import { dirname, resolve } from "node:path";

const here = dirname(fileURLToPath(import.meta.url));
const headerPath = resolve(here, "relative_imports/transit/three/header.ts");

const contents = readFileSync(headerPath, "utf8");

const expectedImports = [
"from '../one/info.js';",
"from '../two/identity.js';",
];

for (const expected of expectedImports) {
if (!contents.includes(expected)) {
throw new Error(`Missing relative import "${expected}" in ${headerPath}`);
}
}

const forbidden = "../transit/";
if (contents.includes(forbidden)) {
throw new Error(
`Found unexpected namespace segment in import path within ${headerPath}`
);
}

console.log("JavaScriptRelativeImportPathTest: OK");
15 changes: 15 additions & 0 deletions tests/ts/TypeScriptTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,20 @@ def esbuild(input, output):
flatc(options=["--ts"], schema="../long_namespace.fbs")
flatc(options=["--ts"], schema="../longer_namespace.fbs")


flatc(
options=[
"--ts",
"--reflect-names",
"--gen-name-strings",
"--gen-object-api",
"--ts-entry-points",
"--ts-flat-files",
],
schema="relative_imports/relative_imports.fbs",
prefix="relative_imports",
)

print("Running TypeScript Compiler...")
check_call(["tsc"])
print(
Expand All @@ -201,6 +215,7 @@ def esbuild(input, output):
check_call(NODE_CMD + ["JavaScriptFlexBuffersTest"])
check_call(NODE_CMD + ["JavaScriptComplexArraysTest"])
check_call(NODE_CMD + ["JavaScriptUnionUnderlyingTypeTest"])
check_call(NODE_CMD + ["JavaScriptRelativeImportPathTest"])

print("Running old v1 TypeScript Tests...")
check_call(NODE_CMD + ["JavaScriptTestv1.cjs", "./monster_test_generated.cjs"])
2 changes: 1 addition & 1 deletion tests/ts/my-game/example/any-ambiguous-aliases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */

import { Monster, MonsterT } from '../../my-game/example/monster.js';
import { Monster, MonsterT } from './monster.js';


export enum AnyAmbiguousAliases {
Expand Down
6 changes: 3 additions & 3 deletions tests/ts/my-game/example/any-unique-aliases.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */

import { Monster as MyGame_Example2_Monster, MonsterT as MyGame_Example2_MonsterT } from '../../my-game/example2/monster.js';
import { Monster, MonsterT } from '../../my-game/example/monster.js';
import { TestSimpleTableWithEnum, TestSimpleTableWithEnumT } from '../../my-game/example/test-simple-table-with-enum.js';
import { Monster as MyGame_Example2_Monster, MonsterT as MyGame_Example2_MonsterT } from '../example2/monster.js';
import { Monster, MonsterT } from './monster.js';
import { TestSimpleTableWithEnum, TestSimpleTableWithEnumT } from './test-simple-table-with-enum.js';


export enum AnyUniqueAliases {
Expand Down
6 changes: 3 additions & 3 deletions tests/ts/my-game/example/any.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any, @typescript-eslint/no-non-null-assertion */

import { Monster as MyGame_Example2_Monster, MonsterT as MyGame_Example2_MonsterT } from '../../my-game/example2/monster.js';
import { Monster, MonsterT } from '../../my-game/example/monster.js';
import { TestSimpleTableWithEnum, TestSimpleTableWithEnumT } from '../../my-game/example/test-simple-table-with-enum.js';
import { Monster as MyGame_Example2_Monster, MonsterT as MyGame_Example2_MonsterT } from '../example2/monster.js';
import { Monster, MonsterT } from './monster.js';
import { TestSimpleTableWithEnum, TestSimpleTableWithEnumT } from './test-simple-table-with-enum.js';


export enum Any {
Expand Down
26 changes: 13 additions & 13 deletions tests/ts/my-game/example/monster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@

import * as flatbuffers from 'flatbuffers';

import { Monster as MyGame_Example2_Monster, MonsterT as MyGame_Example2_MonsterT } from '../../my-game/example2/monster.js';
import { Ability, AbilityT } from '../../my-game/example/ability.js';
import { Any, unionToAny, unionListToAny } from '../../my-game/example/any.js';
import { AnyAmbiguousAliases, unionToAnyAmbiguousAliases, unionListToAnyAmbiguousAliases } from '../../my-game/example/any-ambiguous-aliases.js';
import { AnyUniqueAliases, unionToAnyUniqueAliases, unionListToAnyUniqueAliases } from '../../my-game/example/any-unique-aliases.js';
import { Color } from '../../my-game/example/color.js';
import { Race } from '../../my-game/example/race.js';
import { Referrable, ReferrableT } from '../../my-game/example/referrable.js';
import { Stat, StatT } from '../../my-game/example/stat.js';
import { Test, TestT } from '../../my-game/example/test.js';
import { TestSimpleTableWithEnum, TestSimpleTableWithEnumT } from '../../my-game/example/test-simple-table-with-enum.js';
import { Vec3, Vec3T } from '../../my-game/example/vec3.js';
import { InParentNamespace, InParentNamespaceT } from '../../my-game/in-parent-namespace.js';
import { Monster as MyGame_Example2_Monster, MonsterT as MyGame_Example2_MonsterT } from '../example2/monster.js';
import { Ability, AbilityT } from './ability.js';
import { Any, unionToAny, unionListToAny } from './any.js';
import { AnyAmbiguousAliases, unionToAnyAmbiguousAliases, unionListToAnyAmbiguousAliases } from './any-ambiguous-aliases.js';
import { AnyUniqueAliases, unionToAnyUniqueAliases, unionListToAnyUniqueAliases } from './any-unique-aliases.js';
import { Color } from './color.js';
import { Race } from './race.js';
import { Referrable, ReferrableT } from './referrable.js';
import { Stat, StatT } from './stat.js';
import { Test, TestT } from './test.js';
import { TestSimpleTableWithEnum, TestSimpleTableWithEnumT } from './test-simple-table-with-enum.js';
import { Vec3, Vec3T } from './vec3.js';
import { InParentNamespace, InParentNamespaceT } from '../in-parent-namespace.js';


/**
Expand Down
2 changes: 1 addition & 1 deletion tests/ts/my-game/example/struct-of-structs-of-structs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import * as flatbuffers from 'flatbuffers';

import { StructOfStructs, StructOfStructsT } from '../../my-game/example/struct-of-structs.js';
import { StructOfStructs, StructOfStructsT } from './struct-of-structs.js';


export class StructOfStructsOfStructs implements flatbuffers.IUnpackableObject<StructOfStructsOfStructsT> {
Expand Down
4 changes: 2 additions & 2 deletions tests/ts/my-game/example/struct-of-structs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

import * as flatbuffers from 'flatbuffers';

import { Ability, AbilityT } from '../../my-game/example/ability.js';
import { Test, TestT } from '../../my-game/example/test.js';
import { Ability, AbilityT } from './ability.js';
import { Test, TestT } from './test.js';


export class StructOfStructs implements flatbuffers.IUnpackableObject<StructOfStructsT> {
Expand Down
2 changes: 1 addition & 1 deletion tests/ts/my-game/example/test-simple-table-with-enum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import * as flatbuffers from 'flatbuffers';

import { Color } from '../../my-game/example/color.js';
import { Color } from './color.js';


export class TestSimpleTableWithEnum implements flatbuffers.IUnpackableObject<TestSimpleTableWithEnumT> {
Expand Down
4 changes: 2 additions & 2 deletions tests/ts/my-game/example/vec3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

import * as flatbuffers from 'flatbuffers';

import { Color } from '../../my-game/example/color.js';
import { Test, TestT } from '../../my-game/example/test.js';
import { Color } from './color.js';
import { Test, TestT } from './test.js';


export class Vec3 implements flatbuffers.IUnpackableObject<Vec3T> {
Expand Down
2 changes: 1 addition & 1 deletion tests/ts/optional-scalars/scalar-stuff.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import * as flatbuffers from 'flatbuffers';

import { OptionalByte } from '../optional-scalars/optional-byte.js';
import { OptionalByte } from './optional-byte.js';


export class ScalarStuff {
Expand Down
20 changes: 20 additions & 0 deletions tests/ts/relative_imports/relative_imports.fbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace Transit.One;

table Info {
timestamp:ulong;
}

namespace Transit.Two;

table Identity {
id:uint;
}

namespace Transit.Three;

table Header {
info:Transit.One.Info;
id:Transit.Two.Identity;
}

root_type Header;
10 changes: 7 additions & 3 deletions tests/ts/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
{
"compilerOptions": {
"target": "ES2020",
"lib": ["ES2020", "DOM"],
"lib": [
"ES2020",
"DOM"
],
"module": "NodeNext",
"declaration": true,
"strict": true
Expand All @@ -17,6 +20,7 @@
"arrays_test_complex/**/*.ts",
"union_underlying_type_test.ts",
"long-namespace/**/*.ts",
"longer-namespace/**/*.ts"
"longer-namespace/**/*.ts",
"relative_imports/**/*.ts"
]
}
}
Loading