Skip to content

Commit 0ca3946

Browse files
Merge branch 'feature/LF-2295/option-to-export-as-ES-modules' into 'master'
Add ES module support alongside CommonJS See merge request lfor/fhirpath.js!66
2 parents aca79b3 + c968728 commit 0ca3946

30 files changed

Lines changed: 1879 additions & 90 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ node_modules
33
downloads
44
src/scratch.js
55
build
6+
/esm/
67
.DS_Store
78
.editorconfig
89
.codex

CHANGELOG.md

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
This log documents significant changes for each release. This project follows
44
[Semantic Versioning](http://semver.org/).
55

6-
## [5.0.0] - 2026-06-18
6+
## [5.0.0] - 2026-07-13
77
### Added
88
- Added support for FHIRPath Instance Selector/Object Creation syntax for
99
constructing FHIR instances, including nested objects, primitive values,
@@ -13,6 +13,31 @@ This log documents significant changes for each release. This project follows
1313
`ResourceNode` and `FP_Type` results.
1414
- Added `path2Repeating` to FHIR model contexts for repeatability-aware object
1515
construction.
16+
- ES module entry points so `fhirpath` and its `fhir-context/*` models load
17+
with `import` (default and named exports) as well as `require`; a
18+
`package.json` `exports` map, `module` field, and `.d.mts` declarations
19+
route ES module and CommonJS consumers to the matching code and types.
20+
- Documented entry points (`fhirpath`, `fhirpath/fhir-context/<version>`,
21+
and `fhirpath/package.json`) are unchanged, and a wildcard fallback keeps
22+
other deep file-path imports working. Because the package now declares
23+
`exports`, those deep imports must include the file extension (e.g.
24+
`fhirpath/fhir-context/r4/index.js`); extensionless deep paths that
25+
previously resolved via Node's directory/extension lookup no longer do.
26+
- The `import` build is a pre-bundled, self-contained ES module
27+
(`esm/fhirpath.mjs`) that inlines the CommonJS dependencies (`antlr4`,
28+
`date-fns`, `decimal.js`, `@lhncbc/ucum-lhc`, and `@loxjs/url-join`), so
29+
esbuild-based bundlers (e.g. the Angular CLI) no longer emit
30+
"optimization bailout" warnings; the `require` entry point is unchanged.
31+
- `import` and `require` load separate copies of the library, so an
32+
application should not mix them for the same package — values from one
33+
are not `instanceof`-compatible with the other.
34+
- Type declarations for the exported `util` helpers and the `ucumUtils`
35+
instance, so their named (and default-export) usages are typed for both ES
36+
module and CommonJS consumers.
37+
- Explicit type exports for `Model`, `ResourceNode`, `Options`,
38+
`OptionVariants`, `Path`, and `UserInvocationTable`, so consumers can import
39+
and reference these types by name (e.g.
40+
`import type { OptionVariants } from 'fhirpath'`).
1641

1742
### Changed
1843
- Raw internal `ResourceNode` output now represents absent `_data` as `null` and

README.md

Lines changed: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,42 @@ Try it out on the [demo page](https://hl7.github.io/fhirpath.js/).
3030
npm install --save fhirpath
3131
```
3232

33+
The library ships both CommonJS and ES module entry points.
34+
35+
For CommonJS:
3336
```js
3437
const fhirpath = require('fhirpath');
3538
// For FHIR model data (choice type support) pull in the model file:
3639
const fhirpath_r4_model = require('fhirpath/fhir-context/r4');
3740
```
3841

42+
For ESM:
43+
```js
44+
import fhirpath from 'fhirpath';
45+
// Named exports are also available, e.g. `import { evaluate } from 'fhirpath'`.
46+
// For FHIR model data (choice type support) pull in the model file:
47+
import fhirpath_r4_model from 'fhirpath/fhir-context/r4';
48+
```
49+
50+
The `import` entry point resolves to a pre-bundled, self-contained ES module
51+
that inlines fhirpath's CommonJS dependencies. This means bundlers such as
52+
esbuild (used by the Angular CLI) will not emit "CommonJS or AMD dependencies
53+
can cause optimization bailouts" warnings for fhirpath.
54+
55+
Do not mix `require('fhirpath')` and `import 'fhirpath'` for the same package
56+
within a single application. The `import` entry point is a self-contained
57+
bundle with its own copy of the library and its dependencies, so values created
58+
via one entry point (e.g. `FP_Decimal` or other internal type instances) are
59+
not `instanceof`-compatible with the other, and helpers such as `ucumUtils`
60+
would be duplicated. Pick one module system per application.
61+
62+
The `esm/fhirpath.mjs` bundle is generated by the package's `prepare` lifecycle
63+
script (`npm run build:esm`) and is included in the published npm package, so
64+
installing fhirpath from the npm registry needs no extra step. If you instead
65+
install it from a git reference with lifecycle scripts disabled (e.g.
66+
`npm install --ignore-scripts`), run `npm run build:esm` afterward so the
67+
`import` entry point (`esm/fhirpath.mjs`) exists.
68+
3969
### Web-browser:
4070

4171
Download the zip file from the [releases
@@ -715,14 +745,22 @@ open browser on localhost:8080
715745
```
716746
* Compare the output files in the new folder to those of the last release
717747
(looking for issues that might be due to changes in the StructureDefinition format)
718-
* Copy the `index.js` file from the last release into the new folder
748+
* Copy the CommonJS and ES module entry files from the last release into the
749+
new folder
719750
```
720-
> cp ../r5/index.js r6
751+
> cp ../r5/index.js ../r5/index.mjs ../r5/index.d.ts ../r5/index.d.mts r6
721752
```
722-
* Update the `/index.d.ts` file to include the new module as an export (can copy from previous version)
723-
``` js
724-
declare module "fhirpath/fhir-context/r6" {
725-
export const {
753+
* Update the header comment (version name) in `r6/index.mjs` and
754+
`r6/index.d.mts`. If the new version has no `score` property, also remove
755+
`score` from the named-export lists in `r6/index.mjs` and `r6/index.d.ts`.
756+
* `r6/index.d.ts` declares the model's named exports for CommonJS consumers
757+
(copy from the previous version, shown below); `r6/index.d.mts` re-exports
758+
them for ES module consumers via `export * from "./index.js"` and needs no
759+
per-version changes.
760+
``` ts
761+
import {Model} from "../../src/fhirpath";
762+
763+
export const {
726764
version,
727765
score,
728766
choiceTypePaths,
@@ -733,8 +771,22 @@ open browser on localhost:8080
733771
resourcesWithUrlParam,
734772
path2TypeWithoutElements,
735773
path2RefType
736-
}: Model;
737-
}
774+
}: Model;
775+
```
776+
* Register the new version in the `exports` map in `package.json` so it can be
777+
imported by name through both `import` and `require` (copy an existing
778+
`./fhir-context/<version>` entry and change the version)
779+
``` json
780+
"./fhir-context/r6": {
781+
"import": {
782+
"types": "./fhir-context/r6/index.d.mts",
783+
"default": "./fhir-context/r6/index.mjs"
784+
},
785+
"require": {
786+
"types": "./fhir-context/r6/index.d.ts",
787+
"default": "./fhir-context/r6/index.js"
788+
}
789+
},
738790
```
739791
740792
## Credits

demo/package-lock.json

Lines changed: 27 additions & 17 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

demo/public/app.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,8 +271,12 @@ getVariableLabels().forEach((item, index) => {
271271
function addVariable(name = null, val = null) {
272272
const newItem = document.createElement("label");
273273
newItem.innerHTML =
274-
`<li>%<input type="text" value="${name || ''}"></li><button type="button">` +
274+
`<li>%<input type="text"></li><button type="button">` +
275275
"Remove variable</button>";
276+
// Assign the variable name via the value property instead of interpolating
277+
// it into innerHTML. The name can come from the untrusted "p" URL parameter,
278+
// so interpolating it would allow DOM-based XSS.
279+
newItem.querySelector('input').value = name || '';
276280
if (val) {
277281
newItem.setAttribute("data-json", fhirpath.util.toJSON(val, 2));
278282
}

eslint.config.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ module.exports = [
1212

1313
{
1414
// Apply this configuration to source files and the converter module
15-
files: ['src/**/*.js', 'src/parser/index.js', 'converter/**/*.js'],
15+
files: [
16+
'src/**/*.js', 'src/**/*.mjs', 'src/parser/index.js',
17+
'converter/**/*.js', 'fhir-context/**/*.mjs'
18+
],
1619
languageOptions: {
1720
ecmaVersion: 2020,
1821
sourceType: 'module',

fhir-context/dstu2/index.d.mts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Type declarations for the ES module wrapper (index.mjs) of the DSTU2 model.
2+
3+
import type { Model } from "../../src/fhirpath.js";
4+
5+
export * from "./index.js";
6+
7+
8+
declare const model: Model;
9+
10+
export default model;

fhir-context/dstu2/index.mjs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// ES module wrapper around the CommonJS FHIR DSTU2 model (index.js).
2+
//
3+
// Re-exposes the model object as the default export and its fields as named
4+
// exports so ES module consumers can use either
5+
// `import model from 'fhirpath/fhir-context/dstu2'` or named imports.
6+
7+
8+
import model from './index.js';
9+
10+
11+
export default model;
12+
13+
export const {
14+
version,
15+
choiceTypePaths,
16+
pathsDefinedElsewhere,
17+
type2Parent,
18+
path2Type,
19+
path2Repeating,
20+
resourcesWithUrlParam,
21+
path2TypeWithoutElements,
22+
path2RefType
23+
} = model;

fhir-context/r4/index.d.mts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Type declarations for the ES module wrapper (index.mjs) of the R4 model.
2+
3+
import type { Model } from "../../src/fhirpath.js";
4+
5+
export * from "./index.js";
6+
7+
8+
declare const model: Model;
9+
10+
export default model;

fhir-context/r4/index.mjs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// ES module wrapper around the CommonJS FHIR R4 model (index.js).
2+
//
3+
// Re-exposes the model object as the default export and its fields as named
4+
// exports so ES module consumers can use either
5+
// `import model from 'fhirpath/fhir-context/r4'` or named imports.
6+
7+
8+
import model from './index.js';
9+
10+
11+
export default model;
12+
13+
export const {
14+
version,
15+
score,
16+
choiceTypePaths,
17+
pathsDefinedElsewhere,
18+
type2Parent,
19+
path2Type,
20+
path2Repeating,
21+
resourcesWithUrlParam,
22+
path2TypeWithoutElements,
23+
path2RefType
24+
} = model;

0 commit comments

Comments
 (0)