Apex Stubs Generator
What stubs are and where they live
.cls stub files represent the Apex standard library (namespaces, classes, methods, enums).
They live in two places:
- This repo:
packages/apex-parser-ast/src/resources/StandardApexLibrary/ - jorje:
apex-jorje/apex-jorje-lsp/src/main/resources/StandardApexLibrary/
Both copies must be kept in sync. The authoritative source is this repo's generator pipeline.
API generation pipeline
The generator lives in packages/apex-parser-ast/scripts/ and is the sole supported
stub-generation process. It fetches Apex type definitions from the Salesforce Symbol
Table API, then converts the resulting JSON into .cls files.
Running the generator
Prerequisites:
- Authenticate the Salesforce CLI to an org that exposes the Symbol Table API.
- The API is available on a 264 org farm or later.
cd packages/apex-parser-ast
# Fetch all available type definitions from the default org (gus), then generate stubs.
npm run update:stubs
# Rebuild the protobuf cache after generation.
npm run compile
To use another authenticated org, API version, or one namespace:
npm run fetch:api-stubs -- --org myorg
npm run fetch:api-stubs -- --api-version v68.0
npm run fetch:api-stubs -- --namespace System
npm run generate:api-stubs
The pipeline:
fetch-api-stubs.mjsdiscovers API namespaces and writes JSON tobuild/api-stubs/.generate-api-stubs.mjsvalidates the captured input, replaces generated output for configured namespaces, and preserves hand-crafted builtins.generate-stdlib-cache.mjs, run bynpm run compile, parses the.clsfiles and writes the protobuf cache.
Key source files
| File | Purpose |
|---|---|
packages/apex-parser-ast/scripts/fetch-api-stubs.mjs |
Fetches Symbol Table API data |
packages/apex-parser-ast/scripts/generate-api-stubs.mjs |
Validates captures and emits .cls files |
packages/apex-parser-ast/scripts/apexStubGenerator.js |
Converts API JSON to Apex source |
packages/apex-parser-ast/scripts/api-stub-config.mjs |
Defines bundled namespaces |
packages/apex-parser-ast/scripts/generate-stdlib-cache.mjs |
Builds the protobuf cache |
Namespace name rules
Namespace names come from the API capture. TARGET_NAMESPACES in
api-stub-config.mjs defines which namespaces are emitted to
StandardApexLibrary/; remaining fetched namespaces remain available to the type registry
without producing bundled .cls files.
These names become the output folder names under StandardApexLibrary/.
Visibility rule
Hand-crafted builtins are preserved during generation. The authoritative skip lists are
BUILTIN_CLASSES and BUILTIN_NAMESPACED_CLASSES in generate-api-stubs.mjs.
macOS case-insensitive filesystem — critical pitfall
macOS has a case-insensitive filesystem. Git's index stores the real committed name, but
ls and file reads on macOS silently fold case. This means:
- A committed file
SoapType.clsand a file on disk namedSOAPType.clsappear identical locally. - On Linux CI (case-sensitive), they are different files — causing test failures that do not reproduce on macOS.
Rules for working with filenames
- Always use
git ls-tree -r HEADto get the true committed names — never trustls. - Renaming for case: always use a two-step
git mvthrough a temp name:git mv OldName.cls OldName_tmp.cls git mv OldName_tmp.cls NewName.cls - Auditing mismatches — find all
.clsfiles where committed name ≠ declared class name:git ls-tree -r HEAD --name-only "packages/apex-parser-ast/src/resources/StandardApexLibrary/" \ | while read f; do base=$(basename "$f" .cls) declared=$(grep -m1 -oP '(?<=global (class|interface|enum) )\S+' "$f" 2>/dev/null) [ -n "$declared" ] && [ "$declared" != "$base" ] && echo "MISMATCH: $f -> $declared" done - Auditing directory name mismatches — compare committed dirs against TOC + overrides:
use
git ls-tree HEAD --name-only <path>then compare againstNAMESPACE_NAME_OVERRIDESand the scraped TOC output.
Source of truth for casing
| Thing | Source of truth |
|---|---|
.cls filename |
The global class/interface/enum NAME declaration inside the file |
| Namespace folder name | TARGET_NAMESPACES and API namespace name |
Snapshot test
packages/apex-parser-ast/test/generator/emptyStubDetection.snapshot.test.ts
Tracks which stubs have no members (empty bodies). Fails when:
- New stubs are added that happen to be empty
- A rename changes the class name captured in the snapshot
Update after intentional changes:
cd packages/apex-parser-ast
npx jest emptyStubDetection --updateSnapshot
macOS caveat: --updateSnapshot may report "1 passed" instead of "1 updated" when the
local file read returns the wrong casing. Trust what CI sees — edit the snapshot directly if
needed to match the correct committed name.
Syncing stubs to jorje
After regenerating stubs in this repo, the same files must be copied to:
apex-jorje/apex-jorje-lsp/src/main/resources/StandardApexLibrary/
jorje's branch conventions and build process are separate — sync is a manual copy
of the StandardApexLibrary/ tree, then build/test in jorje.