TYPO3 v15 dev-main snapshot ()

This commit is contained in:
2026-08-10 22:31:09 +02:00
commit af8cc155b5
6818 changed files with 642608 additions and 0 deletions
@@ -0,0 +1,84 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Core\TypoScript\IncludeTree\IncludeNode;
use TYPO3\CMS\Core\TypoScript\Tokenizer\Token\Token;
use TYPO3\CMS\Core\TypoScript\Tokenizer\Token\TokenType;
/**
* Base implementation of condition nodes.
*
* @internal: Internal tree structure.
*/
abstract class AbstractConditionInclude extends AbstractInclude implements IncludeConditionInterface
{
protected Token $conditionValueToken;
protected ?Token $originalConditionValueToken = null;
protected bool $verdict;
/**
* Add the condition token to cache when serialized. See __serialize() of AbstractInclude.
*/
protected function serialize(): array
{
$result = parent::serialize();
$result['conditionValueToken'] = $this->conditionValueToken;
return $result;
}
public function setConditionToken(Token $token): void
{
if ($token->getType() !== TokenType::T_VALUE) {
throw new \LogicException('Token must be of type T_VALUE', 1655977210);
}
$this->conditionValueToken = $token;
}
public function getConditionToken(): Token
{
return $this->conditionValueToken;
}
public function setOriginalConditionToken(Token $token): void
{
if ($token->getType() !== TokenType::T_VALUE) {
throw new \LogicException('Token must be of type T_VALUE', 1655977211);
}
$this->originalConditionValueToken = $token;
}
public function getOriginalConditionToken(): ?Token
{
return $this->originalConditionValueToken;
}
public function isConditionNegated(): bool
{
return false;
}
public function setConditionVerdict(bool $verdict): void
{
$this->verdict = $verdict;
}
public function getConditionVerdict(): bool
{
return $this->verdict;
}
}
@@ -0,0 +1,203 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Core\TypoScript\IncludeTree\IncludeNode;
use TYPO3\CMS\Core\TypoScript\Tokenizer\Line\LineInterface;
use TYPO3\CMS\Core\TypoScript\Tokenizer\Line\LineStream;
/**
* Base implementation of IncludeInterface.
*
* @internal: Internal tree structure.
*/
abstract class AbstractInclude implements IncludeInterface
{
private ?string $identifier = null;
protected string $name = '';
protected string $path = '';
/**
* @var array<int, IncludeInterface>
*/
protected array $children = [];
protected ?LineStream $lineStream = null;
protected ?LineInterface $originalTokenLine = null;
protected bool $isSplit = false;
protected bool $root = false;
protected bool $clear = false;
protected ?int $pid = null;
/**
* When storing to cache, we only store FE relevant properties and skip
* things like "name", "identifier" and friends. We also don't need the
* LineStream when a node is split.
*/
final public function __serialize(): array
{
return $this->serialize();
}
protected function serialize(): array
{
$result['children'] = $this->children;
if ($this->isSplit()) {
$result['isSplit'] = true;
}
if (!$this->isSplit()) {
$result['lineStream'] = $this->lineStream;
}
if ($this->isRoot()) {
$result['root'] = true;
}
if ($this->isClear()) {
$result['clear'] = true;
}
return $result;
}
public function getType(): string
{
$classWithNamespace = static::class;
$lastBackslash = strrpos($classWithNamespace, '\\');
return substr($classWithNamespace, $lastBackslash + 1, -7);
}
public function setIdentifier(string $identifier): void
{
$this->identifier = hash('xxh3', $identifier);
$childCounter = 0;
foreach ($this->getNextChild() as $child) {
$child->setIdentifier($this->identifier . $childCounter);
$childCounter++;
}
}
public function getIdentifier(): string
{
if ($this->identifier === null) {
throw new \RuntimeException(
'Identifier has not been initialized. This happens when getIdentifier() is called on'
. ' trees retrieved from cache. The identifier is not supposed to be used in this context.',
1673634853
);
}
return $this->identifier;
}
public function setName(string $name): void
{
$this->name = $name;
}
public function getName(): string
{
return $this->name;
}
public function setPath(string $path): void
{
$this->path = $path;
}
public function getPath(): string
{
return $this->path;
}
public function addChild(IncludeInterface $node): void
{
$this->children[] = $node;
}
public function hasChildren(): bool
{
return !empty($this->children);
}
public function getNextChild(): iterable
{
foreach ($this->children as $child) {
yield $child;
}
}
public function isSysTemplateRecord(): bool
{
return false;
}
public function setLineStream(?LineStream $lineStream): void
{
$this->lineStream = $lineStream;
}
public function getLineStream(): ?LineStream
{
return $this->lineStream;
}
public function setOriginalLine(LineInterface $line): void
{
$this->originalTokenLine = $line;
}
public function getOriginalLine(): ?LineInterface
{
return $this->originalTokenLine;
}
public function setSplit(): void
{
$this->isSplit = true;
}
public function isSplit(): bool
{
return $this->isSplit;
}
public function setRoot(bool $root): void
{
$this->root = $root;
}
public function isRoot(): bool
{
return $this->root;
}
public function setClear(bool $clear): void
{
$this->clear = $clear;
}
public function isClear(): bool
{
return $this->clear;
}
public function setPid(int $pid): void
{
$this->pid = $pid;
}
public function getPid(): ?int
{
return $this->pid;
}
}
@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Core\TypoScript\IncludeTree\IncludeNode;
/**
* A node representing an "@import" include. The LineStream is set
* to the content of the included source, which can be split again
* if that source contains further conditions or includes.
*
* @internal: Internal tree structure.
*/
final class AtImportInclude extends AbstractInclude {}
@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Core\TypoScript\IncludeTree\IncludeNode;
/**
* A node representing the [ELSE] body of a condition:
*
* [foo = bar]
* ...
* [ELSE]
* baz = bazValue
*
* The LineStream is the body of the else block, the condition token
* is set to the token of the condition "[foo = bar]".
*
* @internal: Internal tree structure.
*/
final class ConditionElseInclude extends AbstractConditionInclude
{
public function isConditionNegated(): bool
{
return true;
}
}
@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Core\TypoScript\IncludeTree\IncludeNode;
/**
* A node representing a condition and its body.
*
* [foo = bar]
* baz = bazValue
* [END]
*
* @internal: Internal tree structure.
*/
final class ConditionInclude extends AbstractConditionInclude {}
@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Core\TypoScript\IncludeTree\IncludeNode;
/**
* A simple include representing [END] and [GLOBAL] lines.
*
* @internal: Internal tree structure.
*/
final class ConditionStopInclude extends AbstractInclude
{
public function addChild(IncludeInterface $node): void
{
throw new \LogicException('ConditionStopInclude can not have children', 1717691734);
}
public function hasChildren(): bool
{
return false;
}
}
@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Core\TypoScript\IncludeTree\IncludeNode;
/**
* A node created for "default TypoScript" from globals, content from:
* $GLOBALS['TYPO3_CONF_VARS']['FE']['defaultTypoScript_[constants|setup]'].
*
* @internal: Internal tree structure.
*/
final class DefaultTypoScriptInclude extends AbstractInclude {}
@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Core\TypoScript\IncludeTree\IncludeNode;
/**
* A node created for "magic" include from globals, when processing
* $GLOBALS['TYPO3_CONF_VARS ']['FE']['defaultTypoScript_[constants|setup]']
*
* @internal: Internal tree structure.
*/
final class DefaultTypoScriptMagicKeyInclude extends AbstractInclude {}
@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Core\TypoScript\IncludeTree\IncludeNode;
/**
* A node created for "extension static" TypoScript auto-include files:
* EXT:my_extension/ext_typoscript_[constants|setup].typoscript
*
* @internal: Internal tree structure.
*/
final class ExtensionStaticInclude extends AbstractInclude {}
@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Core\TypoScript\IncludeTree\IncludeNode;
/**
* A classic include from sys_template "include_static_file":
* EXT:/My/Path/[constants|setup].[typoscript|ts|txt]
*
* This is always a child of an IncludeStaticFileDatabaseInclude.
*
* @internal: Internal tree structure.
*/
final class FileInclude extends AbstractInclude {}
@@ -0,0 +1,61 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Core\TypoScript\IncludeTree\IncludeNode;
use TYPO3\CMS\Core\TypoScript\Tokenizer\Token\Token;
/**
* Source streams that contain conditions are split smaller parts
* and each condition creates a Condition node.
*
* This interface is implemented by all conditions nodes. It allows
* "parking" the main condition token to be evaluated during AST building.
*
* @internal: Internal tree structure.
*/
interface IncludeConditionInterface
{
/**
* Set and get the condition token: "[foo = bar]"
*/
public function setConditionToken(Token $token): void;
public function getConditionToken(): Token;
/**
* Conditions may use constants: "[foo = {$bar}]". This getter/setter
* allows storing the original condition token string.
* This is set in backend only in case a constant substitution has taken
* place. Otherwise, the "vanilla" condition token is identical,
* getOriginalConditionToken() returns null and the condition token should
* be fetched from getConditionToken().
*/
public function setOriginalConditionToken(Token $token): void;
public function getOriginalConditionToken(): ?Token;
/**
* True for ConditionElseInclude: The [ELSE] node of a condition.
*/
public function isConditionNegated(): bool;
/**
* When a condition is evaluated, this is set to true of false
* depending on the condition result.
*/
public function setConditionVerdict(bool $verdict): void;
public function getConditionVerdict(): bool;
}
@@ -0,0 +1,138 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Core\TypoScript\IncludeTree\IncludeNode;
use TYPO3\CMS\Core\TypoScript\Tokenizer\Line\LineInterface;
use TYPO3\CMS\Core\TypoScript\Tokenizer\Line\LineStream;
/**
* General interface of IncludeTree tree nodes.
*
* The TreeBuilder classes return a tree of these nodes, with the root node being
* a RootInclude. Each "include type" is represented by an own class: There
* is for instance "SysTemplateInclude" for a node that represents a sys_template
* row, and DefaultTypoScriptInclude for the default TypoScript string included from
* TYPO3_CONF_VARS.
*
* Nodes may have children, and a single stream of lines from the tokenizer
* may be split into multiple children: Each @import creates an own child node,
* and conditions trigger splitting as well.
*
* @internal: Internal tree structure.
*/
interface IncludeInterface
{
/**
* A human-readable string derived from class name - Used in BE template analyzer
*/
public function getType(): string;
/**
* An identifier for this include. Typically, a hash of some kind. This identifier
* is unique within the tree, by being created from the parent identifier plus
* something unique for this level like a counter. This identifier is used in the backend,
* when referencing single includes to be rendered.
* Calculating identifiers is initiated by calling setIdentifier() on RootNode, which
* will recurse the tree. Call this on the final tree, after include calculation finished,
* so include building itself does not need to fiddle with identifier updates.
* Note this value is skipped when persisting to caches since it's a Backend related
* thing that does not use cached context: When retrieving includes from cache
* (e.g. in Frontend), the identifier is null and calling the getter will throw an exception.
*/
public function setIdentifier(string $identifier): void;
public function getIdentifier(): string;
/**
* A human-readable version of the identifier: Used in backend tree rendering.
*/
public function setName(string $name): void;
public function getName(): string;
/**
* This is set to a non-empty string for includes that represent files. The file location
* is stored here, typically something like "EXT:my_extension/path/to/foo.typoscript".
* This is used when resolving file includes relative to a parent include, so a
* potential child node knows where to look relative to its parent path.
* Note this value is skipped when persisting to caches: The parent path
* information is no longer needed when a tree is fetched from cache since
* all children were attached already and don't need to be recalculated
* depending on their parent path value.
*/
public function setPath(string $path): void;
public function getPath(): string;
/**
* Child maintenance methods.
*/
public function addChild(IncludeInterface $node): void;
public function hasChildren(): bool;
/**
* @return iterable<IncludeInterface>
*/
public function getNextChild(): iterable;
/**
* True for IncludeTypoScriptInclude - this node represents a sys_template record.
* When true, methods like isRoot() and isClear() are relevant.
*/
public function isSysTemplateRecord(): bool;
/**
* The source split into single lines by a tokenizer.
*/
public function setLineStream(?LineStream $lineStream): void;
public function getLineStream(): ?LineStream;
/**
* When an imports are handled, such a line is substituted by the included
* content. To be able to still output the original line, it is parked here.
* Relevant in backend tree and source display only.
*/
public function setOriginalLine(LineInterface $line): void;
public function getOriginalLine(): ?LineInterface;
/**
* When included line streams contain conditions or imports, the node is split into
* children that contain single segments of the source. The node itself is then just
* a container and the LineStream attached is irrelevant for further processing.
* This flag is set when a line stream is split and the children fully represent the source.
*/
public function setSplit(): void;
public function isSplit(): bool;
/**
* Set to true for IncludeTypoScriptInclude's (sys_template records) when "root" flag is set.
*/
public function setRoot(bool $root): void;
public function isRoot(): bool;
/**
* Set to true for IncludeTypoScriptInclude's (sys_template records) when "clear constants"
* or "clear setup" is set. Depends on context if currently constants or setup are parsed.
*/
public function setClear(bool $clear): void;
public function isClear(): bool;
/**
* Set to the pid of IncludeTypoScriptInclude's (sys_template records). Relevant in backend
* tree rendering only.
*/
public function setPid(int $pid): void;
public function getPid(): ?int;
}
@@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Core\TypoScript\IncludeTree\IncludeNode;
/**
* Main node created for sys_template "include_static_file":
* This has FileInclude or IncludeStaticFileFileInclude children,
* depending on specific string.
*
* @internal: Internal tree structure.
*/
final class IncludeStaticFileDatabaseInclude extends AbstractInclude {}
@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Core\TypoScript\IncludeTree\IncludeNode;
/**
* Created when a sys_template "static_file_include" includes "include_static_file.txt" files:
* EXT:/My/Path/include_static_file.txt
*
* @internal: Internal tree structure.
*/
final class IncludeStaticFileFileInclude extends AbstractInclude {}
@@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Core\TypoScript\IncludeTree\IncludeNode;
/**
* Root of the IncludeTree. Does not contain LineStreams itself,
* only children do.
*
* @internal: Internal tree structure.
*/
final class RootInclude extends AbstractInclude
{
protected string $name = 'ROOT';
public function setName(string $name): void
{
throw new \LogicException('Can not set name on RootNode', 1656668001);
}
}
@@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Core\TypoScript\IncludeTree\IncludeNode;
/**
* When a source stream is split into children because the LineStream contains
* conditions or imports, this node represents TypoScript that is not within
* condition or import context, the "baz = bazValue" part in the example below:
*
* [foo=bar]
* ...
* [END]
* baz = bazValue
*
* @internal: Internal tree structure.
*/
final class SegmentInclude extends AbstractInclude {}
@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Core\TypoScript\IncludeTree\IncludeNode;
/**
* Include node created for includes from Site objects. Only relevant for constants.
*
* @internal: Internal tree structure.
*/
final class SiteInclude extends AbstractInclude {}
@@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Core\TypoScript\IncludeTree\IncludeNode;
/**
* The main node created for TypoScript from site sets
* and %configPath/sites/{constants,setup}.typoscript.
*
* @internal: Internal tree structure.
*/
final class SiteTemplateInclude extends AbstractInclude
{
protected bool $root = true;
protected bool $clear = true;
public function isRoot(): bool
{
return true;
}
public function isClear(): bool
{
return true;
}
}
@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Core\TypoScript\IncludeTree\IncludeNode;
/**
* A simple include type used by StringTreeBuilder when a single entry
* TypoScript snipped is parsed.
*
* @internal: Internal tree structure.
*/
final class StringInclude extends AbstractInclude {}
@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Core\TypoScript\IncludeTree\IncludeNode;
/**
* The main node created for sys_template rows.
*
* @internal: Internal tree structure.
*/
final class SysTemplateInclude extends AbstractInclude
{
public function isSysTemplateRecord(): bool
{
return true;
}
}
@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Core\TypoScript\IncludeTree\IncludeNode;
/**
* An include type used by user and pages TsConfig for single TsConfig snippets.
*
* @internal: Internal tree structure.
*/
final class TsConfigInclude extends AbstractInclude {}