On this page
Wall
This page covers wall, the opt-in command for narrowing the API surface of a
source directory to a single script kind. To add a surface for native-extension
namespaces instead of narrowing one, see resolve.
Full surface by default
Defold scopes two namespaces to a script kind: gui.* resolves only inside a
.gui_script, and render.* only inside a .render_script. Every other
namespace (go, msg, vmath, sys, physics, …) is available in every kind.
By default defold-typescript gives you the full @defold-typescript/types
surface everywhere. init, build, and watch never change this: they scaffold
and build against whatever entrypoint your tsconfig names and never add, remove,
or prune a wall. The full surface never rejects a call the engine would allow, but
it also can't catch a gui.* use in a plain .script. To get the engine's wall
at compile time, opt in with wall.
What a wall is
A wall is a composite tsconfig.json written into a single-kind source directory
that narrows compilerOptions.types to that kind's subpath:
| Script kind | types entrypoint |
Namespaces |
|---|---|---|
.script |
@defold-typescript/types/script |
universal only |
.gui_script |
@defold-typescript/types/gui-script |
universal + gui |
.render_script |
@defold-typescript/types/render-script |
universal + render |
The root tsconfig.json references each walled directory and excludes it from the
root program, so tsc -b --noEmit builds every walled directory against only its
narrowed surface — a render.* use inside a gui-walled directory becomes a compile
error, while the rest of the project stays full-surface.
A directory is eligible to be walled only when every .ts source in it is one
kind; a directory mixing kinds cannot be walled, because no single narrowing
applies. build and watch never touch walls — they are entirely yours to
manage.
Interactive
Run wall with no arguments in a terminal:
bunx @defold-typescript/cli wall
You get a checkbox of every eligible source directory, pre-checked to the directories already walled. Checking an unwalled directory walls it; unchecking a walled directory removes its wall. Mixed-kind directories appear disabled, with their competing kinds shown as the reason. The final selection is the desired wall set — the command reconciles the project on disk to exactly what you checked.
Flags
For agents, CI, and scripted use, pass directories explicitly (a bare wall with
no TTY errors rather than hanging on an unrenderable prompt):
# Wall these directories (added to any already walled)
bunx @defold-typescript/cli wall src/ui src/rendering
# Remove a wall
bunx @defold-typescript/cli wall --remove src/ui
# List current and eligible walls (writes nothing)
bunx @defold-typescript/cli wall --list
bunx @defold-typescript/cli wall --list --json
--json emits the resulting directoryWalls (and, for --list, the eligible
set) for machine consumption. --json is machine-driven intent, so a bare
wall --json never opens the interactive menu — pass directories or --list.
Import the factory from the kind subpath
A wall only holds if a walled source imports its lifecycle factory from the kind subpath, not the main entry:
// src/ui/hud.ts — inside a gui wall
import { defineGuiScript } from "@defold-typescript/types/gui-script"; // correct
Importing the same factory from the main @defold-typescript/types entry pulls
every declare global namespace (including render) into the wall's program and
silently defeats the narrowing — render.* would type-check inside a gui wall:
// Wrong: re-introduces the full surface, defeating the wall
import { defineGuiScript } from "@defold-typescript/types";
The interactive snippets scaffolded by init already import from the kind
subpaths. build enforces this: a walled source that imports a lifecycle factory
from the main entry fails the build before transpile, naming the file and the kind
subpath it should import from instead. (watch does not enforce it yet — its
session path is a separate slice.)