From 57c810ab9a7dd278425775f2e728f8e07ebcb632 Mon Sep 17 00:00:00 2001 From: James Brunton Date: Mon, 23 Mar 2026 10:16:52 +0000 Subject: [PATCH] Add frontend developer guide describing the path alias architecture (#5964) # Description of Changes Add frontend developer guide describing the path alias architecture. There's probably more needed in here which we should flesh out over time, but this is a start. --- AGENTS.md | 2 ++ frontend/DeveloperGuide.md | 68 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 frontend/DeveloperGuide.md diff --git a/AGENTS.md b/AGENTS.md index d67040db64..ea6f7e1f46 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -55,6 +55,8 @@ Development for the AI engine happens in the `engine/` folder. It's built with L #### Import Paths - CRITICAL **ALWAYS use `@app/*` for imports.** Do not use `@core/*` or `@proprietary/*` unless explicitly wrapping/extending a lower layer implementation. +For a broader explanation of the frontend layering and override architecture, see [frontend/DeveloperGuide.md](frontend/DeveloperGuide.md). + ```typescript // ✅ CORRECT - Use @app/* for all imports import { AppLayout } from "@app/components/AppLayout"; diff --git a/frontend/DeveloperGuide.md b/frontend/DeveloperGuide.md new file mode 100644 index 0000000000..f0ec58cbec --- /dev/null +++ b/frontend/DeveloperGuide.md @@ -0,0 +1,68 @@ +# Frontend Developer Guide + +This document is a guide to the main frontend architectural rules in Stirling-PDF. + +## Mode-Specific Code + +There are several different builds of the frontend, each with their own mode-specific code. +The frontend uses [TypeScript Path Aliases](https://www.typescriptlang.org/tsconfig/#paths) to ensure that only relevant code for the configured app version will be present in the build. +Refer to the various `tsconfig.*.json` files to see the specific path alias order. + +The vast majority of the code is in the `src/core` folder, which is the open-source app. +Other builds, such as the desktop app, use `src/core` as the base layer, and then override various files to change behaviour. +If an import is `from '@app/a/b'`, this will refer to `src/core/a/b.ts` in the core build of the app, but may refer to `src/desktop/a/b.ts` in the desktop app if that file exists. + +It is important to try to minimise the amount of overridden code in the app. +Often, just one function needs to behave differently in a specific mode. +For example: + +```ts +// core/file1.ts +function f1() { /* ... */ } +function f2() { /* ... */ } // Needs to be overridden in desktop +function f3() { /* ... */ } +``` + +In cases like this, instead of duplicating the entire file, create a new extension module for the core app and override _that_ in the desktop app. + +```ts +// core/file1.ts +import { f2 } from '@app/file1Extensions'; + +function f1() { /* ... */ } +function f3() { /* ... */ } +``` + +```ts +// core/file1Extensions.ts +export function f2() { /* ... */ } // Original core implementation +``` + +```ts +// desktop/file1Extensions.ts +export function f2() { /* ... */ } // Custom desktop implementation +``` + +Building with this pattern minimises the duplicated code in the system and greatly reduces the chances that changing the core app will break the desktop app. + +In general, all imports for app code should come via `@app` because it allows for other builds of the app to override behaviour if necessary. +The only time that it is beneficial to import via a specific folder (e.g. `@core`) is when you want to reduce duplication **in the file you are overriding**. For example: + +```ts +// core/file2.ts + +export interface MyProps { + // Lots of properties that we don't want to duplicate +} + +export function f1(props: MyProps) { /* ... */ } // Original core implementation +``` + +```ts +// desktop/file2.ts + +import { type MyProps } from '@core/file2'; +export { type MyProps }; // Re-export so anything importing file2 can still access MyProps + +export function f1(props: MyProps) { /* ... */ } // Custom desktop implementation +```