SUSHI ("SUSHI Unshortens Short Hand Inputs") is a TypeScript CLI compiler and library that converts FHIR Shorthand (FSH) source files into FHIR R4/R4B/R5 JSON artifacts (StructureDefinitions, CodeSystems, ValueSets, Instances, etc.) for use in FHIR Implementation Guides (IGs).
The public npm package is fsh-sushi. The CLI binary is sushi. There is also a programmatic API (fshToFhir) exported from src/run/FshToFhir.ts.
src/
app.ts CLI entry point (commander-based)
index.ts Public library exports
fshtypes/ FSH AST node classes (Profile, Extension, Instance, etc.)
rules/ Rule classes (CardRule, AssignmentRule, BindingRule, etc.)
import/ FSH parser (ANTLR4-generated) + config loading
export/ FHIR JSON exporters (one per artifact type)
fhirdefs/ FHIR definition loader and cache
fhirtypes/ TypeScript models for FHIR types (StructureDefinition, ElementDefinition, etc.)
ig/ ImplementationGuide (IG) exporter
run/ fshToFhir() programmatic API
utils/ Logging, fishing, path utilities, processing helpers
antlr/ ANTLR4 grammar files (.g4) and Gradle build
test/ Jest tests mirroring src/ structure
testhelpers/ Shared test utilities (loggerSpy, TestFisher, getTestFHIRDefinitions)
regression/ Regression test runner against real-world IG repos
dist/ Compiled output (do not edit)
| Command | What it does |
|---|---|
npm run build |
Compile TypeScript to dist/ |
npm test |
Run full Jest test suite |
npm run lint |
Run ESLint + tsc |
npm run lint:fix |
Auto-fix ESLint issues |
npm run prettier |
Check formatting |
npm run prettier:fix |
Auto-fix formatting |
npm run check |
test + lint + prettier (pre-PR gate) |
npm run regression |
Run regression tests against real-world IGs |
Always run npm run check before submitting a PR.
FSH source → import → tank → export → FHIR JSON
-
Import (
src/import/FSHImporter.ts): ANTLR4-generated parser visits FSH grammar, populatingFSHDocumentobjects with typed FSH entities and rules. Multiple documents are collected into anFSHTank. -
Tank (
src/import/FSHTank.ts): an in-memory store of all parsed FSH definitions. Implements theFishableinterface — anything that can be "fished" for by name/id/url. -
Export (
src/export/): exporter classes read from the tank and produce FHIR JSON. Resolution order viaMasterFisher: local package output first → tank → externalFHIRDefinitions. -
FHIRDefinitions (
src/fhirdefs/): loads and caches FHIR core packages from disk or the FHIR registry. -
IG Export (
src/ig/IGExporter.ts): assembles all artifacts into an IG project directory.
- Fishable / fishing: the uniform interface for resolving FHIR artifacts by name, id, or canonical URL.
MasterFisheraggregatesFSHTank,FHIRDefinitions, and the in-progressPackage. - FSH types (
src/fshtypes/): typed AST nodes for each FSH keyword (Profile, Extension, Instance, Invariant, RuleSet, etc.). - Rules (
src/fshtypes/rules/): each rule class corresponds to a FSH rule syntax (e.g.,CardRuleforN..M,AssignmentRulefor= value,BindingRuleforfrom ValueSet). - ElementDefinition (
src/fhirtypes/ElementDefinition.ts): the largest and most complex class in the codebase. Handles path navigation, slicing, type constraints, pattern/fixed value assignment, etc. - StructureDefinitionExporter (
src/export/StructureDefinitionExporter.ts): the most complex exporter; handles Profiles, Extensions, Logicals, and Resources.
Tests live in test/ and mirror src/. The test framework is Jest with ts-jest.
getTestFHIRDefinitions(includeR4, ...paths)— load FHIR definitions fromtest/testhelpers/testdefs/; passtruefor R4, or usetestDefsPath('r4-definitions')/testDefsPath('r5-definitions').TestFisher— aMasterFisherwrapper suitable for tests; takes(FSHTank, FHIRDefinitions, Package).loggerSpy— captures log output; callloggerSpy.reset()inbeforeEachand assert withloggerSpy.getMessageAtIndex(...)orloggerSpy.getAllMessages(...).importSingleText(fsh, filename)— parse a single FSH string into aFSHDocument.minimalConfig(test/utils/minimalConfig.ts) — a minimalConfigurationobject for tests.
beforeAll(async () => {
defs = await getTestFHIRDefinitions(true, testDefsPath('r4-definitions'));
});
beforeEach(() => {
loggerSpy.reset();
doc = new FSHDocument('fileName');
const input = new FSHTank([doc], minimalConfig);
pkg = new Package(input.config);
const fisher = new TestFisher(input, defs, pkg);
exporter = new StructureDefinitionExporter(input, pkg, fisher);
});- New FSH syntax: add grammar to
antlr/src/main/antlr4/FSH.g4, regenerate withnpm run build:grammar(requires Java/Gradle), add an AST node insrc/fshtypes/, updateFSHImporter.tsvisitor, update the relevant exporter. - New rule type: add a class under
src/fshtypes/rules/, export fromsrc/fshtypes/rules/index.ts, handle it in the relevant exporter and inAllowedRules.ts. - New exporter: follow the pattern of existing exporters; implement the
Fishableinterface if needed; integrate intoexportFHIR.ts.
Several packages are intentionally held back from major upgrades (see DEPENDENCY-NOTES.md):
chalk,https-proxy-agent,junk,title-case— held at major versions that use CommonJS (not ESM), because SUSHI uses"module": "commonjs".html-minifier-terser— held at v5; v6 made APIs async which would require pervasive refactoring.commander,del-cli,ini— held back to maintain Node 18 support.
Do not bump these without reading DEPENDENCY-NOTES.md and understanding the constraint.
- TypeScript with
noImplicitAny; strict null checks are not enabled. - ESLint + Prettier enforce formatting. Run
npm run lint:fix && npm run prettier:fixafter changes. - Prefer self-explanatory code; add comments only for non-obvious constraints or workarounds.
- Do not use ESM-only packages (the build target is CommonJS).
- FSH supports FHIR R4, R4B, and R5. Version-specific handling lives in
src/utils/FHIRVersionUtils.tsandsrc/fhirdefs/R5DefsForR4/. - FHIR canonical URLs uniquely identify artifacts;
valid-urlis used to validate them. - "Slicing" is a core FHIR concept for constraining arrays of elements; its complexity is concentrated in
ElementDefinition.tsandStructureDefinitionExporter.ts. - The
Fishableinterface (src/utils/Fishable.ts) and itsTypeenum are the primary lookup mechanism throughout the codebase.