Auf dieser Seite finden Sie Codebeispiele, die in React und JavaScript für gängige Funktionen geschrieben werden, die Sie in Erweiterungen verwenden können.
Looker Extension SDK verwenden
Wenn Sie Funktionen aus dem Looker Extension SDK hinzufügen möchten, müssen Sie zuerst einen Verweis auf das SDK abrufen. Das kann entweder vom Anbieter oder global erfolgen. Anschließend können Sie SDK-Funktionen wie in jeder JavaScript-Anwendung aufrufen.
- So greifen Sie über den Anbieter auf das SDK zu:
import { ExtensionContext2 } from '@looker/extension-sdk-react'
export const Comp1 = () => {
const extensionContext = useContext(
ExtensionContext2
)
const { extensionSDK, coreSDK } = extensionContext
- Für den globalen Zugriff auf das SDK (die Erweiterung muss vor diesem Aufruf initialisiert werden):
const coreSDK = getCoreSDK2()
Jetzt können Sie das SDK wie jede andere JavaScript-Anwendung nutzen:
const GetLooks = async () => {
try {
const looks = await sdk.ok(sdk.all_looks('id'))
// process looks
. . .
} catch (error) {
// do error handling
. . .
}
}
An anderer Stelle in der Looker-Instanz navigieren
Da die Erweiterung in einem Sandbox-iFrame ausgeführt wird, können Sie an keiner anderen Stelle in der Looker-Instanz navigieren, indem Sie das window.location
-Objekt des übergeordneten Elements aktualisieren. Die Navigation kann mit dem Looker Extension SDK erfolgen.
Diese Funktion erfordert die Berechtigung navigation
.
import { ExtensionContext2 } from '@looker/extension-sdk-react'
. . .
const extensionContext = useContext(
ExtensionContext2
)
const { extensionSDK } = extensionContext
. . .
extensionSDK.updateLocation('/browse')
Öffnen eines neuen Browserfensters
Da die Erweiterung in einem Sandbox-iFrame ausgeführt wird, können Sie über das übergeordnete Fenster kein neues Browserfenster öffnen. Sie können ein Browserfenster mit dem Looker Extension SDK öffnen.
Für diese Funktion ist entweder die Berechtigung new_window
erforderlich, ein neues Fenster zu einem Standort in der aktuellen Looker-Instanz zu öffnen, oder die Berechtigung new_window_external_urls
, um ein neues Fenster auf einem anderen Host zu öffnen.
import { ExtensionContext2 } from '@looker/extension-sdk-react'
. . .
const extensionContext = useContext(
ExtensionContext2
)
const { extensionSDK } = extensionContext
. . .
extensionSDK.openBrowserWindow('/browse', '_blank')
. . .
extensionSDK.openBrowserWindow('https://docs.looker.com/reference/manifest-params/application#entitlements', '_blank')
Routing und Deeplinks
Folgendes gilt für react-basierte Erweiterungen:
Die Komponenten ExtensionProvider
und ExtensionProvider2
erstellen automatisch einen React-Router namens MemoryRouter
, den du verwenden kannst. Versuchen Sie nicht, ein BrowserRouter
-Objekt zu erstellen, da es in iFrames in Sandbox nicht funktioniert. Versuchen Sie nicht, ein HashRouter
-Objekt zu erstellen, da es in Sandbox-iFrames für die nicht Chromium-basierte Version des Microsoft Edge-Browsers nicht funktioniert.
Wenn das MemoryRouter
verwendet wird und Sie in Ihrer Erweiterung react-router
verwenden, synchronisiert das Framework der Erweiterung den Router Ihrer Erweiterung automatisch mit dem Looker-Host-Router. Das bedeutet, dass die Erweiterung beim erneuten Laden der Seite über Klicks auf die Schaltflächen „Zurück“ und „Vorwärts“ im Browser und über die aktuelle Route benachrichtigt wird. Dies bedeutet auch, dass die Erweiterung Deeplinks automatisch unterstützen sollte. Siehe die Beispiele für die Erweiterung, um zu sehen, wie react-router
verwendet wird.
Kontextdaten der Erweiterung
Die Daten des Erweiterungs-Frameworks dürfen nicht mit dem React-Kontext verwechselt werden.
Erweiterungen haben die Möglichkeit, Kontextdaten für alle Nutzer einer Erweiterung freizugeben. Die Kontextdaten können für Daten verwendet werden, die sich nicht häufig ändern und für die keine speziellen Sicherheitsanforderungen gelten. Beim Schreiben der Daten ist Vorsicht geboten, weil keine Datensperre erfolgt und der letzte Schreibvorgang gewinnt. Die Kontextdaten sind sofort nach dem Start für die Erweiterung verfügbar. Das Looker Extension SDK bietet Funktionen, mit denen sich die Kontextdaten aktualisieren und aktualisieren lassen.
Die maximale Größe der Kontextdaten beträgt etwa 16 MB. Kontextdaten werden in einen JSON-String serialisiert, sodass das auch berücksichtigt werden muss, wenn Sie Kontextdaten für Ihre Erweiterung verwenden.
import { ExtensionContext2 } from '@looker/extension-sdk-react'
. . .
const extensionContext = useContext(
ExtensionContext2
)
const { extensionSDK } = extensionContext
. . .
// Get loaded context data. This will reflect any updates that have
// been made by saveContextData.
let context = await extensionSDK.getContextData()
. . .
// Save context data to Looker server.
context = await extensionSDK.saveContextData(context)
. . .
// Refresh context data from Looker server.
context = await extensionSDK.refreshContextData()
Nutzerattribute
Das Looker Extension SDK bietet eine API für den Zugriff auf Looker-Nutzerattribute. Es gibt zwei Arten von Zugriff auf Nutzerattribute:
Untergeordnet – Mit der Erweiterung verknüpft. Ein auf Nutzer beschränktes Nutzerattribut wird per Namespace an die Erweiterung gesetzt. Das Nutzerattribut muss in der Looker-Instanz definiert werden, bevor es verwendet werden kann. Wenn Sie ein Nutzerattribut Namespace hinzufügen möchten, stellen Sie dem Attributnamen den Erweiterungsnamen voran. Bindestriche und Doppelpunkte im Erweiterungsnamen müssen durch einen Unterstrich ersetzt werden, da Bindestriche und Doppelpunkte nicht in Nutzerattributnamen verwendet werden können.
Beispiel: Für ein Nutzerattribut mit Umfang und dem Namen „
my_value
“ und der Erweiterungs-ID „my-extension::my-extension
“ muss der Name des Nutzerattributs „my_extension_my_extension_my_value
“ festgelegt werden. Nach der Definition kann das Nutzerattribut von der Erweiterung gelesen und aktualisiert werden.Global: Diese Attribute sind global und schreibgeschützt. Ein Beispiel hierfür ist das Nutzerattribut
locale
.
Im Folgenden finden Sie eine Liste der API-Aufrufe für Nutzerattribute:
userAttributeGetItem
: Liest ein Nutzerattribut. Es kann ein Standardwert definiert werden. Er wird verwendet, wenn für den Nutzer kein Wert für das Nutzerattribut vorhanden ist.userAttributeSetItem
: Speichert ein Nutzerattribut für den aktuellen Nutzer. Fehler bei globalen Nutzerattributen. Der gespeicherte Wert ist nur für den aktuellen Nutzer sichtbar.userAttributeResetItem
: Setzt ein Nutzerattribut für den aktuellen Nutzer auf den Standardwert zurück. Fehler bei globalen Nutzerattributen.
Für den Zugriff auf Nutzerattribute müssen Sie die Attributnamen in den Berechtigungen global_user_attributes
und/oder scoped_user_attributes
angeben. In der LookML-Projektmanifestdatei würden Sie beispielsweise Folgendes hinzufügen:
entitlements: {
scoped_user_attributes: ["my_value"]
global_user_attributes: ["locale"]
}
import { ExtensionContext2 } from '@looker/extension-sdk-react'
. . .
const extensionContext = useContext(
ExtensionContext2
)
const { extensionSDK } = extensionContext
// Read global user attribute
const locale = await extensionSDK.userAttributeGetItem('locale')
// Read scoped user attribute
const value = await extensionSDK.userAttributeGetItem('my_value')
// Update scoped user attribute
const value = await extensionSDK.userAttributeSetItem('my_value', 'abcd1234')
// Reset scoped user attribute
const value = await extensionSDK.userAttributeResetItem('my_value')
Lokale Speicherung
In Sandbox-iFrames ist kein Zugriff auf den lokalen Browserspeicher möglich. Mit dem Looker Extension SDK kann eine Erweiterung Daten im lokalen Speicher des übergeordneten Fensters lesen und schreiben. Der lokale Speicher wird als Namespace für die Erweiterung festgelegt. Er kann also keinen lokalen Speicher lesen, der vom übergeordneten Fenster oder von anderen Erweiterungen erstellt wurde.
Für die Verwendung des lokalen Speichers ist die Berechtigung local_storage
erforderlich.
Die localhost API der Erweiterung ist asynchron und nicht die synchrone Browserspeicher-API des Browsers.
import { ExtensionContext2 } from '@looker/extension-sdk-react'
. . .
const extensionContext = useContext(
ExtensionContext2
)
const { extensionSDK } = extensionContext
// Read from local storage
const value = await extensionSDK.localStorageGetItem('my_storage')
// Write to local storage
await extensionSDK.localStorageSetItem('my_storage', 'abcedefh')
// Delete item from local storage
await extensionSDK.localStorageRemoveItem('my_storage')
Der Seitentitel wird aktualisiert
Erweiterungen können den aktuellen Seitentitel aktualisieren. Für diese Aktion sind keine Berechtigungen erforderlich.
import { ExtensionContext2 } from '@looker/extension-sdk-react'
. . .
const extensionContext = useContext(
ExtensionContext2
)
const { extensionSDK } = extensionContext
extensionSDK.updateTitle('My Extension Title')
In die Systemzwischenablage schreiben
In einer Sandbox ausgeführte iFrames ermöglichen keinen Zugriff auf die Systemzwischenablage. Mit dem Looker Extension SDK kann eine Erweiterung Text in die Systemzwischenablage schreiben. Aus Sicherheitsgründen darf die Erweiterung nicht aus der Zwischenablage des Systems lesen.
Zum Schreiben in die Zwischenablage benötigen Sie die Berechtigung use_clipboard
.
import { ExtensionContext2 } from '@looker/extension-sdk-react'
. . .
const extensionContext = useContext(
ExtensionContext2
)
const { extensionSDK } = extensionContext
// Write to system clipboard
try {
await extensionSDK.clipboardWrite(
'My interesting information'
)
. . .
} catch (error) {
. . .
}
Dashboards, Looks und Entdeckungen einbetten
Das Framework der Erweiterung unterstützt das Einbetten von Dashboards, Looks und Explores.
Die Berechtigung use_embeds
ist erforderlich. Wir empfehlen, Inhalte mit dem Looker JavaScript Embed SDK einzubetten. Weitere Informationen finden Sie in der Dokumentation zum Einbetten von SDKs.
import { ExtensionContext2 } from '@looker/extension-sdk-react'
. . .
const extensionContext = useContext(
ExtensionContext2
)
const { extensionSDK } = extensionContext
. . .
const canceller = (event: any) => {
return { cancel: !event.modal }
}
const updateRunButton = (running: boolean) => {
setRunning(running)
}
const setupDashboard = (dashboard: LookerEmbedDashboard) => {
setDashboard(dashboard)
}
const embedCtrRef = useCallback(
(el) => {
const hostUrl = extensionContext?.extensionSDK?.lookerHostData?.hostUrl
if (el && hostUrl) {
el.innerHTML = ''
LookerEmbedSDK.init(hostUrl)
const db = LookerEmbedSDK.createDashboardWithId(id as number)
.withNext()
.appendTo(el)
.on('dashboard:loaded', updateRunButton.bind(null, false))
.on('dashboard:run:start', updateRunButton.bind(null, true))
.on('dashboard:run:complete', updateRunButton.bind(null, false))
.on('drillmenu:click', canceller)
.on('drillmodal:explore', canceller)
.on('dashboard:tile:explore', canceller)
.on('dashboard:tile:view', canceller)
.build()
.connect()
.then(setupDashboard)
.catch((error: Error) => {
console.error('Connection error', error)
})
}
},
[]
)
return (<EmbedContainer ref={embedCtrRef} />)
In den Erweiterungsbeispielen werden Komponenten mit benutzerdefinierten Stilen verwendet, um dem generierten iFrame eine einfache Gestaltung zu ermöglichen. Beispiel:
import styled from "styled-components"
export const EmbedContainer = styled.div`
width: 100%;
height: 95vh;
& > iframe {
width: 100%;
height: 100%;
}
Auf externe API-Endpunkte zugreifen
Das Framework für Erweiterungen bietet zwei Methoden für den Zugriff auf externe API-Endpunkte:
- Der Server-Proxy: Zugriff auf den Endpunkt über den Looker-Server. Mit diesem Mechanismus können Client-IDs und geheime Schlüssel sicher vom Looker-Server festgelegt werden.
- Der Proxy für den Abruf: Ruft den Endpunkt über den Browser des Nutzers auf. Der Proxy ist die Looker-UI.
In beiden Fällen musst du den externen API-Endpunkt in der Erweiterung external_api_urls
-Berechtigung angeben.
Server-Proxy
Im folgenden Beispiel wird die Verwendung des Server-Proxys zum Abrufen eines Zugriffstokens für den Abruf-Proxy veranschaulicht. Die Client-ID und das Secret müssen als Nutzerattribute für die Erweiterung definiert werden. Wenn das Nutzerattribut eingerichtet ist, wird der Standardwert in der Regel auf die Client-ID oder das Secret festgelegt.
import { ExtensionContext2 } from '@looker/extension-sdk-react'
. . .
const extensionContext = useContext(
ExtensionContext2
)
const { extensionSDK } = extensionContext
. . .
const requestBody = {
client_id: extensionSDK.createSecretKeyTag('my_client_id'),
client_secret: extensionSDK.createSecretKeyTag('my_client_secret'),
},
try {
const response = await extensionSDK.serverProxy(
'https://myaccesstokenserver.com/access_token',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(requestBody),
}
)
const { access_token, expiry_date } = response.body
. . .
} catch (error) {
// Error handling
. . .
}
Der Name des Nutzerattributs muss der Erweiterung zugeordnet werden. Bindestriche müssen durch Unterstriche ersetzt werden und die Zeichen ::
müssen durch einen einzelnen Unterstrich ersetzt werden.
Wenn Ihre Erweiterung beispielsweise den Namen my-extension::my-extension
hat, müssen die Nutzerattribute, die für das obige Beispiel definiert werden müssen, so aussehen:
my_extension_my_extension_my_client_id
my_extension_my_extension_'my_client_secret'
Proxy abrufen
Im folgenden Beispiel wird die Verwendung des Abruf-Proxys veranschaulicht. Dabei wird das Zugriffstoken aus dem vorherigen Server-Proxy-Beispiel verwendet.
import { ExtensionContext2 } from '@looker/extension-sdk-react'
. . .
const extensionContext = useContext(
ExtensionContext2
)
const { extensionSDK } = extensionContext
. . .
try {
const response = await extensionSDK.fetchProxy(
'https://myaccesstokenserver.com/myendpoint',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${accessToken}`,
},
body: JSON.stringify({
some_value: someValue,
another_value: anotherValue,
}),
}
)
// Handle success
. . .
} catch (error) {
// Handle failure
. . .
}
OAuth-Integration
Das Framework für Erweiterungen unterstützt die Integration mit OAuth-Anbietern. OAuth kann verwendet werden, um ein Zugriffstoken für einen Zugriff auf eine bestimmte Ressource abzurufen, z. B. ein Goorgle-Tabellendokument.
Du musst den OAuth-Serverendpunkt in der Berechtigung extension oauth2_urls
angeben. Außerdem musst du möglicherweise weitere URLs in der Berechtigung external_api_urls
angeben.
Das Framework für Erweiterungen unterstützt die folgenden Abläufe:
- Impliziter Ablauf
- Autorisierungstyp für Autorisierungscode mit geheimem Schlüssel
- PKCE-Code-Herausforderung und Bestätigung
Der allgemeine Ablauf besteht darin, dass ein untergeordnetes Fenster geöffnet wird, das eine OAuth-Serverseite lädt. Der OAuth-Server authentifiziert den Nutzer und leitet ihn zum Looker-Server zurück, wobei er zusätzliche Details enthält, mit denen ein Zugriffstoken abgerufen werden kann.
Impliziter Ablauf:
import { ExtensionContext2 } from '@looker/extension-sdk-react'
. . .
const extensionContext = useContext(
ExtensionContext2
)
const { extensionSDK } = extensionContext
. . .
const response = await extensionSDK.oauth2Authenticate(
'https://accounts.google.com/o/oauth2/v2/auth',
{
client_id: GOOGLE_CLIENT_ID!,
scope: GOOGLE_SCOPES,
response_type: 'token',
}
)
const { access_token, expires_in } = response
Autorisierungstyp für Autorisierungscode mit geheimem Schlüssel:
const authenticateParameters: Record<string, string> = {
client_id: GITHUB_CLIENT_ID!,
response_type: 'code',
}
const response = await extensionSDK.oauth2Authenticate(
'https://github.com/login/oauth/authorize',
authenticateParameters,
'GET'
)
const exchangeParameters: Record<string, string> = {
client_id: GITHUB_CLIENT_ID!,
code: response.code,
client_secret: extensionSDK.createSecretKeyTag('github_secret_key'),
}
const codeExchangeResponse = await extensionSDK.oauth2ExchangeCodeForToken(
'https://github.com/login/oauth/access_token',
exchangeParameters
)
const { access_token, error_description } = codeExchangeResponse
PKCE-Code-Herausforderung und Verifikation:
import { ExtensionContext2 } from '@looker/extension-sdk-react'
. . .
const extensionContext = useContext(
ExtensionContext2
)
const { extensionSDK } = extensionContext
. . .
const authRequest: Record<string, string> = {
client_id: AUTH0_CLIENT_ID!,
response_type: 'code',
scope: AUTH0_SCOPES,
code_challenge_method: 'S256',
}
const response = await extensionSDK.oauth2Authenticate(
'https://sampleoauthserver.com/authorize',
authRequest,
'GET'
)
const exchangeRequest: Record<string, string> = {
grant_type: 'authorization_code',
client_id: AUTH0_CLIENT_ID!,
code: response.code,
}
const codeExchangeResponse = await extensionSDK.oauth2ExchangeCodeForToken(
'https://sampleoauthserver.com/login/oauth/token',
exchangeRequest
)
const { access_token, expires_in } = codeExchangeResponse
Spartanisch
Spartan bezeichnet eine Methode, bei der Looker-Instanzen als Erweiterungen verwendet werden, um Erweiterungen nur für bestimmte Nutzer zugänglich zu machen. Ein spartanischer Nutzer, der zu einer Looker-Instanz geht, wird mit dem Anmeldevorgang angezeigt, den der Looker-Administrator konfiguriert hat. Nach der Authentifizierung wird dem Nutzer eine Erweiterung gemäß seinem Nutzerattribut landing_page
angezeigt (siehe unten). Der Nutzer kann nur auf Erweiterungen zugreifen, nicht auf andere Teile von Looker. Wenn der Nutzer Zugriff auf mehrere Erweiterungen hat, legen diese fest, dass der Nutzer mithilfe von extensionSDK.updateLocation
zu den anderen Erweiterungen wechseln kann. Es gibt eine bestimmte Looker Extension SDK-Methode, mit der sich Nutzer von der Looker-Instanz abmelden können.
import { ExtensionContext2 } from '@looker/extension-sdk-react'
. . .
const extensionContext = useContext(
ExtensionContext2
)
const { extensionSDK } = extensionContext
. . .
// Navigate to another extension
extensionSDK.updateLocation('/spartan/another::extension')
. . .
// Logout
extensionSDK.spartanLogout()
Spartanische Nutzer definieren
Um einen sparsamen Nutzer zu definieren, müssen Sie eine Gruppe namens „Nur Erweiterungen“ erstellen.
Nachdem die Gruppe „Nur Erweiterungen“ erstellt wurde, rufen Sie im Bereich Admin von Looker die Seite Nutzerattribute auf und bearbeiten Sie das Nutzerattribut landing_page
. Wählen Sie den Tab Gruppenwerte aus und fügen Sie die Gruppe "Nur Erweiterungen" hinzu. Der Wert sollte auf /spartan/my_extension::my_extension/
gesetzt werden, wobei my_extension::my_extension
die ID der Erweiterung ist. Wenn sich der Nutzer jetzt anmeldet, wird er zur vorgesehenen Erweiterung weitergeleitet.
Codeaufteilung
Bei der Codeaufteilung wird Code nur dann angefordert, wenn es erforderlich ist. In der Regel werden Code-Blöcke mit React-Routen verknüpft, bei denen jede Route einen eigenen Code-Abschnitt erhält. In React wird das mit den Komponenten Suspense
und React.lazy
ausgeführt. Die Komponente Suspense
zeigt eine Fallback-Komponente an, während der Codeblock geladen wird. React.lazy
ist für das Laden des Codeblocks verantwortlich.
Einrichtung der Codeaufteilung:
import { AsyncComp1 as Comp1 } from './Comp1.async'
import { AsyncComp1 as Comp2 } from './Comp2.async'
. . .
<Suspense fallback={<div>Loading...</div>}>
<Switch>
<Route path="/comp1">
<Comp1 />
</Route>
<Route path="/comp2">
<Comp2 />
</Route>
</Switch>
<Suspense>
Die Lazy-Load-Komponente wird so implementiert:
import { lazy } from 'react'
const Comp1 = lazy(
async () => import(/* webpackChunkName: "comp1" */ './Comp1')
)
export const AsyncComp1 = () => <Home />
Die Komponente wird so implementiert: Die Komponente muss als Standardkomponente exportiert werden:
const Comp1 = () => {
return (
<div>Hello World</div>
)
}
export default Comp1
Baumschütteln
Looker SDKs unterstützen das Baumschütteln, sind aber noch nicht perfekt. Wir arbeiten ständig an der Verbesserung unserer SDKs, um die Unterstützung für Baumschütteln zu verbessern. Für einige dieser Änderungen ist möglicherweise eine Refaktorierung des Codes erforderlich. In diesem Fall ist dies in den Versionshinweisen dokumentiert.
Wenn Sie das Baumschüttelmodul verwenden möchten, das Sie verwenden, muss es als Esmodule exportiert werden. Die importierten Funktionen müssen keine Nebeneffekte haben. Dazu gehören das Looker SDK für TypeScript/JavaScript, die Looker SDK-Laufzeitbibliothek, die Looker-UI-Komponenten, das Looker Extension SDK und das Extension SDK for React.
In einer Erweiterung sollten Sie eines der Looker SDKs 3.1 oder 4.0 auswählen und die Komponente ExtensionProvider2
aus dem Extension SDK for React verwenden. Falls Sie beide SDKs benötigen, verwenden Sie weiterhin die Komponente ExtensionProvider
. Die endgültige Paketgröße wird dann allerdings erhöht.
Mit dem folgenden Code wird der Erweiterungsanbieter eingerichtet. Sie müssen dem Anbieter mitteilen, welches SDK Sie verwenden möchten:
import { MyExtension } from './MyExtension'
import { ExtensionProvider2 } from '@looker/extension-sdk-react'
import { Looker40SDK } from '@looker/sdk/lib/4.0/methods'
import { hot } from 'react-hot-loader/root'
export const App = hot(() => {
return (
<ExtensionProvider2 type={Looker40SDK}>
<MyExtension />
</ExtensionProvider2>
)
})
Verwenden Sie in der Erweiterung nicht den folgenden Importstil:
import * as lookerComponents from `@looker/components`
Im Beispiel oben wird alles aus dem Modul übernommen. Importieren Sie stattdessen nur die Komponenten, die Sie wirklich benötigen. Beispiel:
import { Paragraph } from `@looker/components`
Glossar
- Codeaufteilung: Eine Technik zum Lazy Loading von JavaScript, bis sie benötigt wird. Idealerweise sollte das anfänglich geladene JavaScript-Bundle so klein wie möglich sein. Dazu können Sie die Codeaufteilung verwenden. Funktionen, die nicht sofort benötigt werden, werden erst geladen, wenn sie wirklich benötigt werden.
- IDE: Integrierte Entwicklungsumgebung Der Editor, der zum Erstellen und Ändern einer Erweiterung verwendet wird. Beispiele hierfür sind Visual Studio Code, Intellij und WebStorm.
- Szene: Im Allgemeinen ein Seitenaufruf in Looker. Die Szenen sind den wichtigsten Routen zugeordnet. Manchmal enthält eine Szene untergeordnete Szenen, die Unterrouten innerhalb der Hauptroute zugeordnet sind.
- Transpile: Der Quellcode wird in einer Sprache geschrieben und in eine andere Sprache mit einer ähnlichen Abstraktionsebene umgewandelt. Ein Beispiel dafür ist TypeScript für JavaScript.