Skip to content

Commit 9ce55c3

Browse files
committed
Implement importing modules with multi-module setup
1 parent e79921b commit 9ce55c3

29 files changed

+432
-118
lines changed

src/Actions/PersistTypesCollectionAction.php

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,24 @@ public function __construct(TypeScriptTransformerConfig $config, protected strin
1414
$this->config = $config;
1515
}
1616

17-
public function execute(TypesCollection $collection): void
17+
public function execute(TypesCollection $moduleCollection, ?TypesCollection $totalCollection = null): void
1818
{
19+
if ($totalCollection === null) {
20+
$totalCollection = $moduleCollection;
21+
}
1922
$this->ensureOutputFileExists();
2023

2124
$writer = $this->config->getWriter();
2225

23-
(new ReplaceSymbolsInCollectionAction())->execute(
24-
$collection,
26+
(new ReplaceIntermoduleSymbolsInCollectionAction())->execute(
27+
$moduleCollection,
28+
$totalCollection,
2529
$writer->replacesSymbolsWithFullyQualifiedIdentifiers()
2630
);
2731

2832
file_put_contents(
2933
$this->outputFile,
30-
$writer->format($collection)
34+
$writer->format($moduleCollection)
3135
);
3236
}
3337

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace Spatie\TypeScriptTransformer\Actions;
4+
5+
use Spatie\TypeScriptTransformer\Structures\TranspilationResult;
6+
use Spatie\TypeScriptTransformer\Structures\TypesCollection;
7+
8+
class ReplaceIntermoduleSymbolsInCollectionAction
9+
{
10+
public function execute(
11+
TypesCollection $moduleCollection,
12+
TypesCollection $totalCollection,
13+
$withFullyQualifiedNames = true
14+
): TypesCollection
15+
{
16+
$replaceSymbolsInTypeAction = new ReplaceSymbolsInTypeAction($totalCollection, $withFullyQualifiedNames);
17+
18+
foreach ($moduleCollection as $type) {
19+
$type->transformed = $replaceSymbolsInTypeAction->execute($type);
20+
}
21+
22+
return $moduleCollection;
23+
}
24+
}

src/Actions/ReplaceSymbolsInTypeAction.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Spatie\TypeScriptTransformer\Exceptions\CircularDependencyChain;
66
use Spatie\TypeScriptTransformer\Structures\TransformedType;
7+
use Spatie\TypeScriptTransformer\Structures\TranspilationResult;
78
use Spatie\TypeScriptTransformer\Structures\TypesCollection;
89

910
class ReplaceSymbolsInTypeAction
@@ -18,7 +19,7 @@ public function __construct(TypesCollection $collection, $withFullyQualifiedName
1819
$this->withFullyQualifiedNames = $withFullyQualifiedNames;
1920
}
2021

21-
public function execute(TransformedType $type, array $chain = []): string
22+
public function execute(TransformedType $type, array $chain = []): TranspilationResult
2223
{
2324
if (in_array($type->getTypeScriptName(), $chain)) {
2425
$chain = array_merge($chain, [$type->getTypeScriptName()]);

src/Actions/TranspileTypeToTypeScriptAction.php

Lines changed: 92 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@
2121
use phpDocumentor\Reflection\Types\String_;
2222
use phpDocumentor\Reflection\Types\This;
2323
use phpDocumentor\Reflection\Types\Void_;
24+
use SebastianBergmann\ObjectReflector\ObjectReflector;
2425
use Spatie\TypeScriptTransformer\Structures\MissingSymbolsCollection;
26+
use Spatie\TypeScriptTransformer\Structures\TranspilationResult;
2527
use Spatie\TypeScriptTransformer\Types\RecordType;
2628
use Spatie\TypeScriptTransformer\Types\StructType;
2729
use Spatie\TypeScriptTransformer\Types\TypeScriptType;
@@ -44,104 +46,152 @@ public function __construct(
4446
$this->currentClass = $currentClass;
4547
}
4648

47-
public function execute(Type $type): string
48-
{
49-
return match (true) {
49+
public function execute(Type $type): TranspilationResult {
50+
$result = match (true) {
5051
$type instanceof Compound => $this->resolveCompoundType($type),
5152
$type instanceof AbstractList => $this->resolveListType($type),
5253
$type instanceof Nullable => $this->resolveNullableType($type),
5354
$type instanceof Object_ => $this->resolveObjectType($type),
5455
$type instanceof StructType => $this->resolveStructType($type),
5556
$type instanceof RecordType => $this->resolveRecordType($type),
56-
$type instanceof TypeScriptType => (string) $type,
57-
$type instanceof Boolean => 'boolean',
58-
$type instanceof Float_, $type instanceof Integer => 'number',
59-
$type instanceof String_, $type instanceof ClassString => 'string',
60-
$type instanceof Null_ => 'null',
57+
$type instanceof TypeScriptType => TranspilationResult::noDeps((string)$type),
58+
$type instanceof Boolean => TranspilationResult::noDeps('boolean'),
59+
$type instanceof Float_, $type instanceof Integer => TranspilationResult::noDeps('number'),
60+
$type instanceof String_, $type instanceof ClassString => TranspilationResult::noDeps('string'),
61+
$type instanceof Null_ => TranspilationResult::noDeps('null'),
6162
$type instanceof Self_, $type instanceof Static_, $type instanceof This => $this->resolveSelfReferenceType(),
62-
$type instanceof Scalar => 'string|number|boolean',
63-
$type instanceof Mixed_ => 'any',
64-
$type instanceof Void_ => 'void',
63+
$type instanceof Scalar => TranspilationResult::noDeps('string|number|boolean'),
64+
$type instanceof Mixed_ => TranspilationResult::noDeps('any'),
65+
$type instanceof Void_ => TranspilationResult::noDeps('void'),
6566
$type instanceof PseudoType => $this->execute($type->underlyingType()),
6667
default => throw new Exception("Could not transform type: {$type}")
6768
};
69+
return new TranspilationResult(
70+
array_merge(
71+
(
72+
$type instanceof Object_
73+
&& $type->__toString() !== 'object'
74+
)
75+
? [$type]
76+
: [],
77+
$result->dependencies
78+
),
79+
$result->typescript
80+
);
6881
}
6982

70-
private function resolveCompoundType(Compound $compound): string
71-
{
83+
private function resolveCompoundType(Compound $compound): TranspilationResult {
7284
$transformed = array_map(
73-
fn (Type $type) => $this->execute($type),
85+
fn(Type $type) => $this->execute($type),
7486
iterator_to_array($compound->getIterator())
7587
);
7688

77-
return join(' | ', array_unique($transformed));
89+
return new TranspilationResult(
90+
array_reduce(
91+
$transformed,
92+
fn(array $carry, TranspilationResult $item) => array_merge(
93+
$carry,
94+
$item->dependencies
95+
),
96+
[]
97+
),
98+
join(
99+
' | ',
100+
array_unique(
101+
array_map(
102+
fn(TranspilationResult $result) => $result->typescript,
103+
$transformed
104+
)
105+
)
106+
)
107+
);
78108
}
79109

80-
private function resolveListType(AbstractList $list): string
81-
{
110+
private function resolveListType(AbstractList $list): TranspilationResult {
111+
$valueTransResult = $this->execute($list->getValueType());
82112
if ($this->isTypeScriptArray($list->getKeyType())) {
83-
return "Array<{$this->execute($list->getValueType())}>";
113+
return new TranspilationResult(
114+
$valueTransResult->dependencies,
115+
"Array<$valueTransResult->typescript>"
116+
);
84117
}
85118

86-
return "{ [key: {$this->execute($list->getKeyType())}]: {$this->execute($list->getValueType())} }";
119+
$keyTransResult = $this->execute($list->getKeyType());
120+
$typescript = "{ [key: $keyTransResult->typescript]: {$valueTransResult->typescript} }";
121+
return new TranspilationResult(
122+
array_merge($valueTransResult->dependencies, $keyTransResult->dependencies),
123+
$typescript
124+
);
87125
}
88126

89-
private function resolveNullableType(Nullable $nullable): string
90-
{
127+
private function resolveNullableType(Nullable $nullable): TranspilationResult {
91128
if ($this->nullablesAreOptional) {
92129
return $this->execute($nullable->getActualType());
93130
}
94131

95-
return "{$this->execute($nullable->getActualType())} | null";
132+
$transpilationResult = $this->execute($nullable->getActualType());
133+
return new TranspilationResult(
134+
$transpilationResult->dependencies,
135+
"$transpilationResult->typescript | null"
136+
);
96137
}
97138

98-
private function resolveObjectType(Object_ $object): string
99-
{
139+
private function resolveObjectType(Object_ $object): TranspilationResult {
100140
if ($object->getFqsen() === null) {
101-
return 'object';
141+
return TranspilationResult::noDeps('object');
102142
}
103143

104-
return $this->missingSymbolsCollection->add(
105-
(string) $object->getFqsen()
144+
return TranspilationResult::noDeps(
145+
$this->missingSymbolsCollection->add(
146+
(string)$object->getFqsen()
147+
)
106148
);
107149
}
108150

109-
private function resolveStructType(StructType $type): string
110-
{
151+
private function resolveStructType(StructType $type): TranspilationResult {
111152
$transformed = "{";
112153

154+
$dependencies = [];
113155
foreach ($type->getTypes() as $name => $type) {
114-
$transformed .= "{$name}:{$this->execute($type)};";
156+
$trRes = $this->execute($type);
157+
$transformed .= "{$name}:{$trRes->typescript};";
158+
foreach ($trRes->dependencies as $dependency) {
159+
$dependencies[] = $dependency;
160+
}
115161
}
116162

117-
return "{$transformed}}";
163+
return new TranspilationResult($dependencies, "{$transformed}}");
118164
}
119165

120-
private function resolveRecordType(RecordType $type): string
121-
{
122-
return "Record<{$this->execute($type->getKeyType())}, {$this->execute($type->getValueType())}>";
166+
private function resolveRecordType(RecordType $type): TranspilationResult {
167+
$keyTr = $this->execute($type->getKeyType());
168+
$valueTr = $this->execute($type->getValueType());
169+
return new TranspilationResult(
170+
array_merge($keyTr->dependencies, $valueTr->dependencies),
171+
"Record<{$keyTr->typescript}, {$valueTr->typescript}>"
172+
);
123173
}
124174

125-
private function resolveSelfReferenceType(): string
126-
{
175+
private function resolveSelfReferenceType(): TranspilationResult {
127176
if ($this->currentClass === null) {
128-
return 'any';
177+
return TranspilationResult::noDeps('any');
129178
}
130179

131-
return $this->missingSymbolsCollection->add($this->currentClass);
180+
return TranspilationResult::noDeps(
181+
$this->missingSymbolsCollection->add($this->currentClass)
182+
);
132183
}
133184

134-
private function isTypeScriptArray(Type $keyType): bool
135-
{
136-
if (! $keyType instanceof Compound) {
185+
private function isTypeScriptArray(Type $keyType): bool {
186+
if (!$keyType instanceof Compound) {
137187
return false;
138188
}
139189

140190
if ($keyType->getIterator()->count() !== 2) {
141191
return false;
142192
}
143193

144-
if (! $keyType->contains(new String_()) || ! $keyType->contains(new Integer())) {
194+
if (!$keyType->contains(new String_()) || !$keyType->contains(new Integer())) {
145195
return false;
146196
}
147197

src/Structures/NamespacedType.php

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
3+
namespace Spatie\TypeScriptTransformer\Structures;
4+
5+
use ReflectionClass;
6+
7+
class NamespacedType
8+
{
9+
10+
public readonly string $namespace;
11+
12+
public readonly string $shortName;
13+
14+
public function __construct(
15+
string $type
16+
) {
17+
$this->namespace = self::namespace($type);
18+
$this->shortName = self::shortName($type);
19+
}
20+
21+
22+
public static function namespace(string|ReflectionClass $type): string {
23+
if ($type instanceof ReflectionClass) {
24+
$type = $type->getName();
25+
}
26+
return substr($type, 0, strrpos($type, '\\'));
27+
}
28+
29+
public static function commonPrefix(string $a, string $b): string {
30+
$length = min(strlen($a), strlen($b));
31+
$i = 0;
32+
33+
while ($i < $length && $a[$i] === $b[$i]) {
34+
$i++;
35+
}
36+
37+
return substr($a, 0, $i);
38+
}
39+
40+
public static function shortName(string|ReflectionClass $type): string {
41+
if ($type instanceof ReflectionClass) {
42+
$type = $type->getName();
43+
}
44+
45+
$pos = strrpos($type, '\\');
46+
47+
return $pos !== false ? substr($type, $pos + 1) : $type;
48+
}
49+
50+
}

src/Structures/TransformedType.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class TransformedType
1010

1111
public ?string $name = null;
1212

13-
public string $transformed;
13+
public TranspilationResult $transformed;
1414

1515
public MissingSymbolsCollection $missingSymbols;
1616

@@ -23,7 +23,7 @@ class TransformedType
2323
public static function create(
2424
ReflectionClass $class,
2525
string $name,
26-
string $transformed,
26+
TranspilationResult $transformed,
2727
?MissingSymbolsCollection $missingSymbols = null,
2828
bool $inline = false,
2929
string $keyword = 'type',
@@ -34,7 +34,7 @@ public static function create(
3434

3535
public static function createInline(
3636
ReflectionClass $class,
37-
string $transformed,
37+
TranspilationResult $transformed,
3838
?MissingSymbolsCollection $missingSymbols = null
3939
): self {
4040
return new self($class, null, $transformed, $missingSymbols ?? new MissingSymbolsCollection(), true);
@@ -43,7 +43,7 @@ public static function createInline(
4343
public function __construct(
4444
ReflectionClass $class,
4545
?string $name,
46-
string $transformed,
46+
TranspilationResult $transformed,
4747
MissingSymbolsCollection $missingSymbols,
4848
bool $isInline,
4949
string $keyword = 'type',
@@ -91,10 +91,10 @@ public function replaceSymbol(string $class, string $replacement): void
9191
{
9292
$this->missingSymbols->remove($class);
9393

94-
$this->transformed = str_replace(
94+
$this->transformed->typescript = str_replace(
9595
"{%{$class}%}",
9696
$replacement,
97-
$this->transformed
97+
$this->transformed->typescript
9898
);
9999
}
100100

0 commit comments

Comments
 (0)