Repository: FHIR/sushi (ts)

Issue:
SUSHI should include R5 IG properties as extensions in R4 IGs
When SUSHI updated to support the new FHIR R5 ImplementationGuide resource, we added support for the new R5 ImplementationGuide resources and include those properties only in R5 IGs. This was originally based on guidance [here](https://chat.fhir.org/#narrow/stream/179252-IG-creation/topic/New.20IG.20Publisher/near/298399557).

However, we should now support including any _new_ properties that are defined in R5's ImplementationGuide resource as extensions in R4 IGs, based on guidance [here](https://chat.fhir.org/#narrow/stream/215610-shorthand/topic/Dependency.20reason/near/309636002). The extension should follow the pattern defined in the documentation for extensions for converting between versions [here](http://hl7.org/fhir/2022Sep/versions.html#extensions).

For example, `IG.dependsOn.reason` was added in R5. Currently, SUSHI will not include `reason` in R4 IGs. However, it should instead include an extension with `url` of `http://hl7.org/fhir/5.0/StructureDefinition/extension-ImplementationGuide.dependsOn.reason` and `valueMarkdown` of the specified reason in `sushi-config.yaml`.

Support for adding extensions should only be added for properties that are _new_ to R5 ImplementationGuides. Any property that was moved or renamed does _not_ need to have an extension defined for it.

Pull request:
Support IG R5 properties in R4 IGs
Fixes #1171.

This PR takes any properties that were in the R5 ImplementationGuide resource and adds them as extensions to R4 ImplementationGuide resources that SUSHI creates. To do this, I followed the documentation for converting between FHIR versions [here](http://hl7.org/fhir/2022Sep/versions.html#extensions). The properties that were changed in R5 are left as is in the R4 IG, and only new properties are added as extensions. The properties that are new can be seen in the function that is updated in the SUSHI code, as well as seen in the ImplementationGuide section of diff documentation [here](http://hl7.org/fhir/2022Sep/diff.html).

Note: Although I believe the extensions are created correctly, the IG Publisher does not recognize the extension URLs and produces errors if you create an R4 IG with these extensions. So if you add the new properties in your `sushi-config.yaml`, run SUSHI, and run the resulting IG through the IG Publisher, you will get errors in the QA report. This was brought up on Zulip [here](https://chat.fhir.org/#narrow/stream/179252-IG-creation/topic/Converting.20resources.20between.20FHIR.20versions) and seems to be an issue with the validator and the IGP. Until that is resolved, we don't want to merge this PR, but it can be reviewed.

Patch:
diff --git a/src/fhirtypes/ImplementationGuide.ts b/src/fhirtypes/ImplementationGuide.ts
index c23eceb..0968429 100644
--- a/src/fhirtypes/ImplementationGuide.ts
+++ b/src/fhirtypes/ImplementationGuide.ts
@@ -94,6 +94,7 @@ export type ImplementationGuideDefinitionPageGeneration = 'html' | 'markdown' |
 
 export type ImplementationGuideDefinitionParameter = {
   code: string | Coding; // code changed from a code to a Coding type in R5
+  extension?: Extension[];
   value: string;
 };
 
diff --git a/src/ig/IGExporter.ts b/src/ig/IGExporter.ts
index 038e619..b161cb9 100644
--- a/src/ig/IGExporter.ts
+++ b/src/ig/IGExporter.ts
@@ -109,7 +109,11 @@ export class IGExporter {
     this.addMenuXML(outPath);
     this.checkIgIni();
     this.checkPackageList();
-    this.updateForR5();
+    if (!isR4(this.config.fhirVersion)) {
+      this.updateForR5();
+    } else {
+      this.translateR5PropertiesToR4();
+    }
     this.addImplementationGuide(outPath);
   }
 
@@ -1353,106 +1357,219 @@ export class IGExporter {
     }
   }
 
+  // Supports necessary updates for R5 IGs.
+  // SUSHI only supports R4 or later, and as of now, we will just target R5.
   updateForR5(): void {
-    // If it isn't R4, we will update it. SUSHI only supports R4 or later, and as of now, we will just target R5.
-    if (!isR4(this.config.fhirVersion)) {
-      // Update IG.definition.resource
-      this.ig.definition.resource.forEach(resource => {
-        // Use IG.definition.resource.isExample
-        if (resource.exampleBoolean || resource.exampleCanonical) {
-          resource.isExample = true;
-          if (resource.exampleCanonical) {
-            resource.profile = [resource.exampleCanonical];
-          }
-          delete resource.exampleBoolean;
-          delete resource.exampleCanonical;
-        } else if (resource.exampleBoolean === false) {
-          resource.isExample = false;
-          delete resource.exampleBoolean;
+    // Update IG.definition.resource
+    this.ig.definition.resource.forEach(resource => {
+      // Use IG.definition.resource.isExample
+      if (resource.exampleBoolean || resource.exampleCanonical) {
+        resource.isExample = true;
+        if (resource.exampleCanonical) {
+          resource.profile = [resource.exampleCanonical];
         }
+        delete resource.exampleBoolean;
+        delete resource.exampleCanonical;
+      } else if (resource.exampleBoolean === false) {
+        resource.isExample = false;
+        delete resource.exampleBoolean;
+      }
 
-        // Assign IG.definition.resource.profile if provided
-        const configEntry = this.config.resources?.find(
-          r => r.reference?.reference === resource.reference?.reference
-        );
-        if (configEntry?.profile != null) {
-          resource.profile = configEntry.profile;
-        }
-        // Assign Ig.definition.resource.isExample if provided
-        if (configEntry?.isExample != null) {
-          resource.isExample = configEntry.isExample;
-        }
-      });
+      // Assign IG.definition.resource.profile if provided
+      const configEntry = this.config.resources?.find(
+        r => r.reference?.reference === resource.reference?.reference
+      );
+      if (configEntry?.profile != null) {
+        resource.profile = configEntry.profile;
+      }
+      // Assign IG.definition.resource.isExample if provided
+      if (configEntry?.isExample != null) {
+        resource.isExample = configEntry.isExample;
+      }
+    });
 
-      // Update IG.definition.page.name
-      this.updatePageNameForR5(this.ig.definition.page);
+    // Update IG.definition.page.name
+    this.ig.definition.page.name = this.ig.definition.page.nameUrl;
+    delete this.ig.definition.page.nameUrl;
+    this.ig.definition.page.page.forEach(page => {
+      this.updatePageNameForR5(page, this.config.pages);
+    });
 
-      // Add new IG.definition.page.source[x] property
-      this.ig.definition.page.page.forEach(page => {
-        // this.ig.definition.page is the toc.html page we create
-        // All configured pages are at the next level, so start at that level
-        this.addPageSourceForR5(page, this.config.pages);
-      });
-      // Default IG.definition.page.source[x] on every page if not set
-      this.defaultPageSourceUrlForR5(this.ig.definition.page);
-
-      // Update IG.definition.parameter
-      this.ig.definition.parameter.forEach(parameter => {
-        const guideParameterCodes: string[] =
-          this.fhirDefs
-            .fishForFHIR('http://hl7.org/fhir/guide-parameter-code', Type.CodeSystem)
-            ?.concept.map((concept: CodeSystemConcept) => concept.code) ?? [];
-
-        const code = parameter.code as string;
-        parameter.code = { code };
-        const parsedCode = parseCodeLexeme(code);
-
-        if (parsedCode.code && parsedCode.system) {
-          // If we can parse the code and we have a system and a code provided, we should use that.
-          parameter.code.code = parsedCode.code;
-          parameter.code.system = parsedCode.system;
-        } else if (guideParameterCodes.some(c => c === code)) {
-          // Otherwise, only a code was provided, so we check if it is in the bound VS
-          parameter.code.system = 'http://hl7.org/fhir/guide-parameter-code';
-        } else {
-          // If the code is not in the VS in the R5 IG resource, we default the system
-          // based on https://chat.fhir.org/#narrow/stream/179252-IG-creation/topic/Unknown.20FHIRVersion.20code.20'5.2E0.2E0-ballot'/near/298697304
-          parameter.code.system = 'http://hl7.org/fhir/tools/CodeSystem/ig-parameters';
-        }
-      });
+    // Add new IG.definition.page.source[x] property
+    // this.ig.definition.page is the toc.html page we create
+    // All configured pages are at the next level, so start at that level
+    this.ig.definition.page.page.forEach(page => {
+      this.addPageSourceForR5(page, this.config.pages);
+    });
+    // Default IG.definition.page.source[x] on every page if not set
+    this.defaultPageSourceUrlForR5(this.ig.definition.page);
+
+    // Update IG.definition.parameter
+    this.ig.definition.parameter.forEach(parameter => {
+      const guideParameterCodes: string[] =
+        this.fhirDefs
+          .fishForFHIR('http://hl7.org/fhir/guide-parameter-code', Type.CodeSystem)
+          ?.concept.map((concept: CodeSystemConcept) => concept.code) ?? [];
+
+      const code = parameter.code as string;
+      parameter.code = { code };
+      const parsedCode = parseCodeLexeme(code);
+
+      if (parsedCode.code && parsedCode.system) {
+        // If we can parse the code and we have a system and a code provided, we should use that.
+        parameter.code.code = parsedCode.code;
+        parameter.code.system = parsedCode.system;
+      } else if (guideParameterCodes.some(c => c === code)) {
+        // Otherwise, only a code was provided, so we check if it is in the bound VS
+        parameter.code.system = 'http://hl7.org/fhir/guide-parameter-code';
+      } else {
+        // If the code is not in the VS in the R5 IG resource, we default the system
+        // based on https://chat.fhir.org/#narrow/stream/179252-IG-creation/topic/Unknown.20FHIRVersion.20code.20'5.2E0.2E0-ballot'/near/298697304
+        parameter.code.system = 'http://hl7.org/fhir/tools/CodeSystem/ig-parameters';
+      }
+    });
 
-      // Add new copyrightLabel property
-      if (this.config.copyrightLabel) {
-        this.ig.copyrightLabel = this.config.copyrightLabel;
+    // Add new copyrightLabel property
+    if (this.config.copyrightLabel) {
+      this.ig.copyrightLabel = this.config.copyrightLabel;
+    }
+
+    // Add new versionAlgorithm property
+    if (this.config.versionAlgorithmString) {
+      this.ig.versionAlgorithmString = this.config.versionAlgorithmString;
+    } else if (this.config.versionAlgorithmCoding) {
+      this.ig.versionAlgorithmCoding = this.config.versionAlgorithmCoding;
+    }
+
+    // Add new dependsOn.reason property
+    this.ig.dependsOn?.forEach(dependency => {
+      const configDependency = this.config.dependencies?.find(
+        d => d.packageId === dependency.packageId
+      );
+      if (configDependency.reason) {
+        dependency.reason = configDependency.reason;
       }
+    });
+  }
 
-      // Add new versionAlgorithm property
-      if (this.config.versionAlgorithmString) {
-        this.ig.versionAlgorithmString = this.config.versionAlgorithmString;
-      } else if (this.config.versionAlgorithmCoding) {
-        this.ig.versionAlgorithmCoding = this.config.versionAlgorithmCoding;
+  // If an R4 IG uses any new properties from R5, they should be included as extensions.
+  // Converting between FHIR versions is documented here: http://hl7.org/fhir/2022Sep/versions.html#extensions
+  // If an R4 IG uses any R5 properties that have an equivalent R4 property, use the R4 property.
+  translateR5PropertiesToR4() {
+    this.ig.definition.resource.forEach(resource => {
+      // Assign IG.definition.resource.profile to exampleCanonical if it is not set, otherwise add an extension
+      const configEntry = this.config.resources?.find(
+        r => r.reference?.reference === resource.reference?.reference
+      );
+      if (resource.exampleCanonical == null && configEntry?.profile?.length) {
+        resource.exampleCanonical = configEntry.profile[0];
+        delete resource.exampleBoolean;
+      }
+      if (
+        resource.exampleCanonical != null &&
+        configEntry?.profile?.some(p => p !== resource.exampleCanonical)
+      ) {
+        resource.extension = (resource.extension ?? []).concat({
+          url: 'http://hl7.org/fhir/5.0/StructureDefinition/extension-ImplementationGuide.definition.resource.profile',
+          valueCanonical: configEntry.profile
+        });
       }
+      // Assign isExample to exampleBoolean if it is set and neither exampleCanonical or exampleBoolean are already set.
+      if (configEntry?.isExample != null && resource.exampleCanonical == null) {
+        resource.exampleBoolean = configEntry.isExample;
+      }
+    });
 
-      // Add new dependsOn.reason property
-      this.ig.dependsOn?.forEach(dependency => {
-        const configDependency = this.config.dependencies?.find(
-          d => d.packageId === dependency.packageId
-        );
-        if (configDependency.reason) {
-          dependency.reason = configDependency.reason;
-        }
+    // Add new IG.definition.page.source[x] property to an extension if it is provided. No need to set a default like R5 needs to.
+    // this.ig.definition.page is the toc.html page we create
+    // All configured pages are at the next level, so start at that level
+    this.ig.definition.page.page.forEach(page => {
+      this.addNameUrlAndPageSourceExtensionForR4(page, this.config.pages);
+    });
+
+    // Update IG.definition.parameter.code
+    this.ig.definition.parameter.forEach(parameter => {
+      const code = parameter.code as string;
+      const parsedCode = parseCodeLexeme(code);
+
+      // If the code is parsed, use just the code portion
+      if (parsedCode.code) {
+        parameter.code = parsedCode.code;
+      } else {
+        parameter.code = code;
+      }
+
+      // If the system is parsed, add an extension for the full Coding
+      if (parsedCode.system) {
+        parameter.extension = (parameter.extension ?? []).concat({
+          url: 'http://hl7.org/fhir/5.0/StructureDefinition/extension-ImplementationGuide.definition.resource.parameter.code',
+          valueCoding: {
+            code: parsedCode.code,
+            system: parsedCode.system
+          }
+        });
+      }
+    });
+
+    // Add new copyrightLabel property to an extension if provided
+    if (this.config.copyrightLabel) {
+      this.ig.extension = (this.ig.extension ?? []).concat({
+        url: 'http://hl7.org/fhir/5.0/StructureDefinition/extension-ImplementationGuide.copyrightLabel',
+        valueString: this.config.copyrightLabel
+      });
+    }
+
+    // Add new versionAlgorithm property to an extension if provided
+    if (this.config.versionAlgorithmString) {
+      this.ig.extension = (this.ig.extension ?? []).concat({
+        url: 'http://hl7.org/fhir/5.0/StructureDefinition/extension-ImplementationGuide.versionAlgorithm',
+        valueString: this.config.versionAlgorithmString
+      });
+    } else if (this.config.versionAlgorithmCoding) {
+      this.ig.extension = (this.ig.extension ?? []).concat({
+        url: 'http://hl7.org/fhir/5.0/StructureDefinition/extension-ImplementationGuide.versionAlgorithm',
+        valueCoding: this.config.versionAlgorithmCoding
       });
     }
+
+    // Add new dependsOn.reason property
+    this.ig.dependsOn?.forEach(dependency => {
+      const configDependency = this.config.dependencies?.find(
+        d => d.packageId === dependency.packageId
+      );
+      if (configDependency.reason) {
+        dependency.extension = (dependency.extension ?? []).concat({
+          url: 'http://hl7.org/fhir/5.0/StructureDefinition/extension-ImplementationGuide.dependsOn.reason',
+          valueMarkdown: configDependency.reason
+        });
+      }
+    });
   }
 
-  updatePageNameForR5(page: ImplementationGuideDefinitionPage): void {
-    if (page?.nameUrl) {
+  updatePageNameForR5(
+    page: ImplementationGuideDefinitionPage,
+    configPages: ImplementationGuideDefinitionPage[]
+  ): void {
+    const configPage = configPages?.find(
+      p => (p.nameUrl || p.name).replace(/\.[^.]+$/, '') === page.nameUrl.replace(/\.[^.]+$/, '')
+    );
+    if (configPage && configPage.name) {
+      page.name = configPage.name;
+      // Regenerate the title based on the updated name
+      page.title =
+        configPage.title ?? titleCase(words(page.name.replace(/\.[^.]+$/, '')).join(' '));
+      // The default of sourceUrl will be the old nameUrl (for now).
+      // If sourceUrl is included in the configuration, this value will be
+      // overwritten in addPageSourceForR5, which is called after this function in updateForR5.
+      page.sourceUrl = page.nameUrl;
+      delete page.nameUrl;
+    } else if (page.nameUrl) {
       page.name = page.nameUrl;
       delete page.nameUrl;
     }
     if (page.page?.length) {
       for (const subPage of page?.page) {
-        this.updatePageNameForR5(subPage);
+        this.updatePageNameForR5(subPage, configPage?.page ?? []);
       }
     }
   }
@@ -1462,9 +1579,7 @@ export class IGExporter {
     configPages: ImplementationGuideDefinitionPage[]
   ): void {
     const configPage = configPages?.find(
-      p =>
-        p.nameUrl.substring(0, p.nameUrl.lastIndexOf('.')) ===
-        page.name.substring(0, page.name.lastIndexOf('.'))
+      p => (p.nameUrl || p.name).replace(/\.[^.]+$/, '') === page.name.replace(/\.[^.]+$/, '')
     );
     if (configPage) {
       if (configPage.sourceUrl) {
@@ -1494,6 +1609,58 @@ export class IGExporter {
       }
     }
   }
+
+  // Add an extension for the R5 property IG.definition.page.source[x] if provided in configuration
+  addNameUrlAndPageSourceExtensionForR4(
+    page: ImplementationGuideDefinitionPage,
+    configPages: ImplementationGuideDefinitionPage[]
+  ): void {
+    const sourceExtensionUrl =
+      'http://hl7.org/fhir/5.0/StructureDefinition/extension-ImplementationGuide.definition.page.source';
+    const nameExtensionUrl =
+      'http://hl7.org/fhir/5.0/StructureDefinition/extension-ImplementationGuide.definition.page.name';
+
+    // Find the corresponding configuration page
+    // Configuration might use name or nameUrl and will have a filetype at the end that we want to ignore
+    const configPage = configPages?.find(
+      p => (p.nameUrl || p.name).replace(/\.[^.]+$/, '') === page.nameUrl.replace(/\.[^.]+$/, '')
+    );
+    if (configPage) {
+      // If nameUrl and name do not match, add the name value to an extension
+      if (configPage.name && configPage.nameUrl !== configPage.name) {
+        page.extension = (page.extension ?? []).concat({
+          url: nameExtensionUrl,
+          valueUrl: configPage.name
+        });
+      }
+
+      if (configPage.sourceUrl) {
+        // If nameUrl and sourceUrl do not match, add the sourceUrl value to an extension
+        if (configPage.nameUrl !== configPage.sourceUrl) {
+          page.extension = (page.extension ?? []).concat({
+            url: sourceExtensionUrl,
+            valueUrl: configPage.sourceUrl
+          });
+        }
+      } else if (configPage.sourceString) {
+        page.extension = (page.extension ?? []).concat({
+          url: sourceExtensionUrl,
+          valueString: configPage.sourceString
+        });
+      } else if (configPage.sourceMarkdown) {
+        page.extension = (page.extension ?? []).concat({
+          url: sourceExtensionUrl,
+          valueMarkdown: configPage.sourceMarkdown
+        });
+      }
+
+      if (page.page?.length) {
+        for (const subPage of page?.page) {
+          this.addNameUrlAndPageSourceExtensionForR4(subPage, configPage.page);
+        }
+      }
+    }
+  }
 }
 
 type PageMetadata = {
diff --git a/src/import/YAMLConfiguration.ts b/src/import/YAMLConfiguration.ts
index 4af0f68..4c2bf18 100644
--- a/src/import/YAMLConfiguration.ts
+++ b/src/import/YAMLConfiguration.ts
@@ -372,6 +372,7 @@ export type YAMLConfigurationPage = null | {
     | '#generated';
   extension?: Extension[];
   modifierExtension?: Extension[];
+  name?: string; // only supported in R5
   sourceUrl?: string; // only supported in R5
   sourceString?: string; // only supported in R5
   sourceMarkdown?: string; // only supported in R5
diff --git a/src/import/importConfiguration.ts b/src/import/importConfiguration.ts
index fa191c4..37e3837 100644
--- a/src/import/importConfiguration.ts
+++ b/src/import/importConfiguration.ts
@@ -675,6 +675,9 @@ function parsePage(
   if (details?.sourceMarkdown) {
     page.sourceMarkdown = details.sourceMarkdown;
   }
+  if (details?.name) {
+    page.name = details.name;
+  }
   if (details?.extension) {
     page.extension = details.extension;
   }


Test patch:
diff --git a/test/ig/IGExporter.IG.test.ts b/test/ig/IGExporter.IG.test.ts
index 715fcf4..a5ddd6e 100644
--- a/test/ig/IGExporter.IG.test.ts
+++ b/test/ig/IGExporter.IG.test.ts
@@ -2661,7 +2661,7 @@ describe('IGExporter', () => {
   describe('#r5-ig-format', () => {
     let tempOut: string;
 
-    beforeEach(() => {
+    beforeAll(() => {
       loggerSpy.reset();
       tempOut = temp.mkdirSync('sushi-test');
       const fixtures = path.join(__dirname, 'fixtures', 'simple-ig');
@@ -2716,7 +2716,8 @@ describe('IGExporter', () => {
               sourceString: 'source string for nested page'
             },
             {
-              nameUrl: 'second-nested-page.md'
+              nameUrl: 'second-nested-page-url.md',
+              name: 'second-nested-page-name.html'
             }
           ]
         }
@@ -2802,7 +2803,7 @@ describe('IGExporter', () => {
       exporter.export(tempOut);
     });
 
-    afterEach(() => {
+    afterAll(() => {
       temp.cleanupSync();
     });
 
@@ -2938,10 +2939,10 @@ describe('IGExporter', () => {
                 sourceString: 'source string for nested page' // Supports source[x]
               },
               {
-                name: 'second-nested-page.html',
-                title: 'Second Nested Page',
+                name: 'second-nested-page-name.html', // Configured name
+                title: 'Second Nested Page Name',
                 generation: 'markdown',
-                sourceUrl: 'second-nested-page.html'
+                sourceUrl: 'second-nested-page-url.html' // NameUrl used as sourceUrl
               }
             ]
           }
@@ -3057,6 +3058,7 @@ describe('IGExporter', () => {
       const defs = new FHIRDefinitions();
       const pkg = new Package(configWithVersionAlgorithm);
       const exporter = new IGExporter(pkg, defs, fixtures);
+      const tempOut = temp.mkdirSync('sushi-test-version-alg');
       exporter.export(tempOut);
       const igPath = path.join(
         tempOut,
@@ -3083,6 +3085,7 @@ describe('IGExporter', () => {
       const defs = new FHIRDefinitions();
       const pkg = new Package(configWithVersionAlgorithm);
       const exporter = new IGExporter(pkg, defs, fixtures);
+      const tempOut = temp.mkdirSync('sushi-test-version-alg');
       exporter.export(tempOut);
       const igPath = path.join(
         tempOut,
@@ -3119,4 +3122,767 @@ describe('IGExporter', () => {
       ]);
     });
   });
+
+  describe('#r5-properties-on-r4-igs', () => {
+    let tempOut: string;
+
+    beforeAll(() => {
+      loggerSpy.reset();
+      tempOut = temp.mkdirSync('sushi-test');
+      const fixtures = path.join(__dirname, 'fixtures', 'simple-ig');
+      const defs = new FHIRDefinitions();
+      // r5-definitions contains the guide-parameter-code CodeSystem, which was originally included in 5.0.0-ballot
+      loadFromPath(path.join(__dirname, '..', 'testhelpers', 'testdefs'), 'r5-definitions', defs);
+
+      const r4WithR5propsConfig = cloneDeep(minimalConfig);
+      r4WithR5propsConfig.copyrightLabel = 'Shorty Fsh 2022+';
+      r4WithR5propsConfig.dependencies = [
+        {
+          packageId: 'hl7.fhir.us.ocean',
+          uri: 'http://example.org/ocean',
+          id: 'ocean',
+          version: '1.0.0',
+          reason: 'The ocean is essential because it has fsh'
+        }
+      ];
+      r4WithR5propsConfig.resources = [
+        {
+          reference: { reference: 'Patient/patient-example' },
+          profile: ['http://example.org/StructureDefinition/solo']
+        },
+        {
+          reference: { reference: 'Patient/patient-example-two' },
+          profile: [
+            'http://example.org/StructureDefiniton/first',
+            'http://example.org/StructureDefiniton/second'
+          ]
+        },
+        {
+          reference: { reference: 'Patient/patient-example-three' },
+          isExample: false
+        },
+        {
+          reference: { reference: 'Patient/patient-example-four' },
+          exampleCanonical: 'http://hl7.org/fhir/sushi-test/StructureDefinition/sample-patient',
+          profile: ['http://example.org/StructureDefinition/solo']
+        },
+        {
+          reference: { reference: 'Patient/patient-example-five' },
+          exampleCanonical: 'http://example.org/StructureDefinition/solo',
+          profile: ['http://example.org/StructureDefinition/solo']
+        }
+      ];
+      r4WithR5propsConfig.parameters = [
+        {
+          code: 'generate-xml',
+          value: 'true'
+        },
+        {
+          code: 'http://example.org/parameters#special-code',
+          value: 'sparkles'
+        }
+      ];
+
+      const pkg = new Package(r4WithR5propsConfig);
+
+      // Example: Patient/patient-example
+      const instanceDef = InstanceDefinition.fromJSON(
+        fs.readJSONSync(path.join(fixtures, 'examples', 'Patient-example.json'))
+      );
+      instanceDef._instanceMeta.usage = 'Example';
+      pkg.instances.push(instanceDef);
+
+      // Example: Patient/patient-example-two
+      const instanceDef2 = InstanceDefinition.fromJSON(
+        fs.readJSONSync(path.join(fixtures, 'examples', 'Patient-example-two.json'))
+      );
+      instanceDef2._instanceMeta.usage = 'Example';
+      pkg.instances.push(instanceDef2);
+
+      // Example: Patient/patient-example-three
+      const instanceDef3 = InstanceDefinition.fromJSON(
+        fs.readJSONSync(path.join(fixtures, 'examples', 'Patient-example-three.json'))
+      );
+      instanceDef3._instanceMeta.usage = 'Example';
+      pkg.instances.push(instanceDef3);
+
+      // Example: Patient/patient-example-four
+      const instanceDef4 = InstanceDefinition.fromJSON(
+        fs.readJSONSync(path.join(fixtures, 'examples', 'Patient-example-three.json'))
+      );
+      instanceDef4.id = 'patient-example-four';
+      instanceDef4._instanceMeta.name = 'patient-example-four';
+      instanceDef4._instanceMeta.usage = 'Example';
+      pkg.instances.push(instanceDef4);
+
+      // Example: Patient/patient-example-five
+      const instanceDef5 = InstanceDefinition.fromJSON(
+        fs.readJSONSync(path.join(fixtures, 'examples', 'Patient-example-three.json'))
+      );
+      instanceDef5.id = 'patient-example-five';
+      instanceDef5._instanceMeta.name = 'patient-example-five';
+      instanceDef5._instanceMeta.usage = 'Example';
+      pkg.instances.push(instanceDef5);
+
+      const exporter = new IGExporter(pkg, defs, fixtures);
+      // No need to regenerate the IG on every test -- generate it once and inspect what you
+      // need to in the tests
+      exporter.export(tempOut);
+    });
+
+    afterAll(() => {
+      temp.cleanupSync();
+    });
+
+    describe('definition.resource.profile', () => {
+      describe('exampleCanonical is not set', () => {
+        it('should add configured profile array to exampleCanonical and no extension if length ===  1', () => {
+          const igPath = path.join(
+            tempOut,
+            'fsh-generated',
+            'resources',
+            'ImplementationGuide-fhir.us.minimal.json'
+          );
+          expect(fs.existsSync(igPath)).toBeTruthy();
+          const igContent = fs.readJSONSync(igPath);
+          expect(igContent.definition.resource).toContainEqual({
+            reference: { reference: 'Patient/patient-example' },
+            name: 'patient-example',
+            exampleCanonical: 'http://example.org/StructureDefinition/solo' // adds profile url
+            // no extension because only one profile was provided
+          });
+        });
+
+        it('should add configured profile array to exampleCanonical and add an extension if length > 1', () => {
+          const igPath = path.join(
+            tempOut,
+            'fsh-generated',
+            'resources',
+            'ImplementationGuide-fhir.us.minimal.json'
+          );
+          expect(fs.existsSync(igPath)).toBeTruthy();
+          const igContent = fs.readJSONSync(igPath);
+          expect(igContent.definition.resource).toContainEqual({
+            reference: {
+              reference: 'Patient/patient-example-two'
+            },
+            name: 'patient-example-two',
+            exampleCanonical: 'http://example.org/StructureDefiniton/first', // stays as exampleBoolean (not changed to isExample)
+            // Profile is added to an extension because it is provided and length > 1
+            extension: [
+              {
+                url: 'http://hl7.org/fhir/5.0/StructureDefinition/extension-ImplementationGuide.definition.resource.profile',
+                valueCanonical: [
+                  'http://example.org/StructureDefiniton/first',
+                  'http://example.org/StructureDefiniton/second'
+                ]
+              }
+            ]
+          });
+        });
+
+        it('should add exampleBoolean if isExample is set and no profile array configured', () => {
+          const igPath = path.join(
+            tempOut,
+            'fsh-generated',
+            'resources',
+            'ImplementationGuide-fhir.us.minimal.json'
+          );
+          expect(fs.existsSync(igPath)).toBeTruthy();
+          const igContent = fs.readJSONSync(igPath);
+          expect(igContent.definition.resource).toContainEqual({
+            reference: { reference: 'Patient/patient-example-three' },
+            name: 'patient-example-three',
+            exampleBoolean: false
+          });
+        });
+      });
+
+      describe('exampleCanonical is set', () => {
+        it('should add configured profile array to an extension and not change exampleCanonical if they differ', () => {
+          const igPath = path.join(
+            tempOut,
+            'fsh-generated',
+            'resources',
+            'ImplementationGuide-fhir.us.minimal.json'
+          );
+          expect(fs.existsSync(igPath)).toBeTruthy();
+          const igContent = fs.readJSONSync(igPath);
+          expect(igContent.definition.resource).toContainEqual({
+            reference: {
+              reference: 'Patient/patient-example-four'
+            },
+            name: 'patient-example-four',
+            exampleCanonical: 'http://hl7.org/fhir/sushi-test/StructureDefinition/sample-patient', // stays as configured
+            // extension added with configured profiles
+            extension: [
+              {
+                url: 'http://hl7.org/fhir/5.0/StructureDefinition/extension-ImplementationGuide.definition.resource.profile',
+                valueCanonical: ['http://example.org/StructureDefinition/solo']
+              }
+            ]
+          });
+        });
+
+        it('should not add any extension if the configured profile array matches the configured exampleCanonical', () => {
+          const igPath = path.join(
+            tempOut,
+            'fsh-generated',
+            'resources',
+            'ImplementationGuide-fhir.us.minimal.json'
+          );
+          expect(fs.existsSync(igPath)).toBeTruthy();
+          const igContent = fs.readJSONSync(igPath);
+          expect(igContent.definition.resource).toContainEqual({
+            reference: {
+              reference: 'Patient/patient-example-five'
+            },
+            name: 'patient-example-five',
+            exampleCanonical: 'http://example.org/StructureDefinition/solo' // stays as configured
+            // no extension added because profile array is the same as exampleCanonical
+          });
+        });
+      });
+    });
+
+    describe('definition.page.source[x] and definition.page.name', () => {
+      let tempOutPages: string;
+      let fixtures: string;
+      let defs: FHIRDefinitions;
+
+      beforeAll(() => {
+        loggerSpy.reset();
+        tempOutPages = temp.mkdirSync('sushi-test-pages');
+        fixtures = path.join(__dirname, 'fixtures', 'simple-ig');
+        defs = new FHIRDefinitions();
+        // r5-definitions contains the guide-parameter-code CodeSystem, which was originally included in 5.0.0-ballot
+        loadFromPath(path.join(__dirname, '..', 'testhelpers', 'testdefs'), 'r5-definitions', defs);
+      });
+
+      it('should set sourceUrl to extension if it differs from nameUrl', () => {
+        const r4WithR5pagesConfig = cloneDeep(minimalConfig);
+        r4WithR5pagesConfig.pages = [
+          {
+            nameUrl: 'nameUrl.md',
+            title: 'Name Url',
+            sourceUrl: 'different-sourceUrl.md',
+            generation: 'markdown',
+            page: [
+              {
+                nameUrl: 'nested-nameUrl.md',
+                title: 'Nested Name Url',
+                sourceUrl: 'nested-different-sourceUrl.md',
+                generation: 'markdown'
+              }
+            ]
+          }
+        ];
+        const pkg = new Package(r4WithR5pagesConfig);
+        const exporter = new IGExporter(pkg, defs, fixtures);
+        exporter.export(tempOutPages);
+
+        const igPath = path.join(
+          tempOutPages,
+          'fsh-generated',
+          'resources',
+          'ImplementationGuide-fhir.us.minimal.json'
+        );
+        expect(fs.existsSync(igPath)).toBeTruthy();
+        const igContent = fs.readJSONSync(igPath);
+        expect(igContent.definition.page.page).toEqual([
+          {
+            nameUrl: 'nameUrl.html',
+            generation: 'markdown',
+            title: 'Name Url',
+            extension: [
+              {
+                url: 'http://hl7.org/fhir/5.0/StructureDefinition/extension-ImplementationGuide.definition.page.source',
+                valueUrl: 'different-sourceUrl.md' // extension added for differing sourceUrl
+              }
+            ],
+            page: [
+              {
+                nameUrl: 'nested-nameUrl.html',
+                generation: 'markdown',
+                title: 'Nested Name Url',
+                extension: [
+                  {
+                    url: 'http://hl7.org/fhir/5.0/StructureDefinition/extension-ImplementationGuide.definition.page.source',
+                    valueUrl: 'nested-different-sourceUrl.md' // extension added for differing sourceUrl
+                  }
+                ]
+              }
+            ]
+          }
+        ]);
+      });
+
+      it('should not add an extension if sourceUrl and nameUrl match', () => {
+        const r4WithR5pagesConfig = cloneDeep(minimalConfig);
+        r4WithR5pagesConfig.pages = [
+          {
+            nameUrl: 'matching-nameUrl.md',
+            title: 'Name Url',
+            sourceUrl: 'matching-nameUrl.md',
+            generation: 'markdown',
+            page: [
+              {
+                nameUrl: 'nested-matching-nameUrl.md',
+                title: 'Nested Name Url',
+                sourceUrl: 'nested-matching-nameUrl.md',
+                generation: 'markdown'
+              }
+            ]
+          }
+        ];
+        const pkg = new Package(r4WithR5pagesConfig);
+        const exporter = new IGExporter(pkg, defs, fixtures);
+        exporter.export(tempOutPages);
+
+        const igPath = path.join(
+          tempOutPages,
+          'fsh-generated',
+          'resources',
+          'ImplementationGuide-fhir.us.minimal.json'
+        );
+        expect(fs.existsSync(igPath)).toBeTruthy();
+        const igContent = fs.readJSONSync(igPath);
+        expect(igContent.definition.page.page).toEqual([
+          {
+            nameUrl: 'matching-nameUrl.html',
+            generation: 'markdown',
+            title: 'Name Url',
+            // no extension because nameUrl and sourceUrl match
+            page: [
+              {
+                nameUrl: 'nested-matching-nameUrl.html',
+                generation: 'markdown',
+                title: 'Nested Name Url'
+                // no extension because nameUrl and sourceUrl match
+              }
+            ]
+          }
+        ]);
+      });
+
+      it('should add name to an extension if sourceUrl is defined', () => {
+        const r4WithR5pagesConfig = cloneDeep(minimalConfig);
+        r4WithR5pagesConfig.pages = [
+          {
+            nameUrl: 'name-and-sourceUrl.md',
+            title: 'Name and SourceUrl',
+            sourceUrl: 'sourceUrl.md',
+            name: 'name.md',
+            generation: 'markdown',
+            page: [
+              {
+                nameUrl: 'nested-name-and-sourceUrl.md',
+                title: 'Nested Name and SourceUrl',
+                sourceUrl: 'nested-sourceUrl.md',
+                name: 'nested-name.md',
+                generation: 'markdown'
+              }
+            ]
+          }
+        ];
+        const pkg = new Package(r4WithR5pagesConfig);
+        const exporter = new IGExporter(pkg, defs, fixtures);
+        exporter.export(tempOutPages);
+
+        const igPath = path.join(
+          tempOutPages,
+          'fsh-generated',
+          'resources',
+          'ImplementationGuide-fhir.us.minimal.json'
+        );
+        expect(fs.existsSync(igPath)).toBeTruthy();
+        const igContent = fs.readJSONSync(igPath);
+        expect(igContent.definition.page.page).toEqual([
+          {
+            nameUrl: 'name-and-sourceUrl.html',
+            title: 'Name and SourceUrl',
+            generation: 'markdown',
+            extension: [
+              {
+                url: 'http://hl7.org/fhir/5.0/StructureDefinition/extension-ImplementationGuide.definition.page.name',
+                valueUrl: 'name.md' // extension added for name
+              },
+              {
+                url: 'http://hl7.org/fhir/5.0/StructureDefinition/extension-ImplementationGuide.definition.page.source',
+                valueUrl: 'sourceUrl.md' // extension added for differing sourceUrl
+              }
+            ],
+            page: [
+              {
+                nameUrl: 'nested-name-and-sourceUrl.html',
+                title: 'Nested Name and SourceUrl',
+                generation: 'markdown',
+                extension: [
+                  {
+                    url: 'http://hl7.org/fhir/5.0/StructureDefinition/extension-ImplementationGuide.definition.page.name',
+                    valueUrl: 'nested-name.md' // extension added for name
+                  },
+                  {
+                    url: 'http://hl7.org/fhir/5.0/StructureDefinition/extension-ImplementationGuide.definition.page.source',
+                    valueUrl: 'nested-sourceUrl.md' // extension added for differing sourceUrl
+                  }
+                ]
+              }
+            ]
+          }
+        ]);
+      });
+
+      it('should add name to an extension if name differs from nameUrl when sourceUrl is not define', () => {
+        const r4WithR5pagesConfig = cloneDeep(minimalConfig);
+        r4WithR5pagesConfig.pages = [
+          {
+            nameUrl: 'name.md',
+            title: 'Name',
+            name: 'different-name.md',
+            generation: 'markdown',
+            page: [
+              {
+                nameUrl: 'nested-name.md',
+                title: 'Nested Name',
+                name: 'different-nested-name.md',
+                generation: 'markdown'
+              }
+            ]
+          }
+        ];
+        const pkg = new Package(r4WithR5pagesConfig);
+        const exporter = new IGExporter(pkg, defs, fixtures);
+        exporter.export(tempOutPages);
+
+        const igPath = path.join(
+          tempOutPages,
+          'fsh-generated',
+          'resources',
+          'ImplementationGuide-fhir.us.minimal.json'
+        );
+        expect(fs.existsSync(igPath)).toBeTruthy();
+        const igContent = fs.readJSONSync(igPath);
+        expect(igContent.definition.page.page).toEqual([
+          {
+            nameUrl: 'name.html',
+            title: 'Name',
+            generation: 'markdown',
+            extension: [
+              {
+                url: 'http://hl7.org/fhir/5.0/StructureDefinition/extension-ImplementationGuide.definition.page.name',
+                valueUrl: 'different-name.md' // extension added for name
+              }
+            ],
+            page: [
+              {
+                nameUrl: 'nested-name.html',
+                title: 'Nested Name',
+                generation: 'markdown',
+                extension: [
+                  {
+                    url: 'http://hl7.org/fhir/5.0/StructureDefinition/extension-ImplementationGuide.definition.page.name',
+                    valueUrl: 'different-nested-name.md' // extension added for name
+                  }
+                ]
+              }
+            ]
+          }
+        ]);
+      });
+
+      it('should not add name to an extension if name and nameUrl match', () => {
+        const r4WithR5pagesConfig = cloneDeep(minimalConfig);
+        r4WithR5pagesConfig.pages = [
+          {
+            nameUrl: 'matching-name.md',
+            title: 'Matching NameUrl and Name',
+            name: 'matching-name.md',
+            generation: 'markdown',
+            page: [
+              {
+                nameUrl: 'nested-matching-name.md',
+                title: 'Nested Matching NameUrl and Name',
+                name: 'nested-matching-name.md',
+                generation: 'markdown'
+              }
+            ]
+          }
+        ];
+        const pkg = new Package(r4WithR5pagesConfig);
+        const exporter = new IGExporter(pkg, defs, fixtures);
+        exporter.export(tempOutPages);
+
+        const igPath = path.join(
+          tempOutPages,
+          'fsh-generated',
+          'resources',
+          'ImplementationGuide-fhir.us.minimal.json'
+        );
+        expect(fs.existsSync(igPath)).toBeTruthy();
+        const igContent = fs.readJSONSync(igPath);
+        expect(igContent.definition.page.page).toEqual([
+          {
+            nameUrl: 'matching-name.html',
+            title: 'Matching NameUrl and Name',
+            generation: 'markdown',
+            // no extension because nameUrl and name match
+            page: [
+              {
+                nameUrl: 'nested-matching-name.html',
+                title: 'Nested Matching NameUrl and Name',
+                generation: 'markdown'
+                // no extension because nameUrl and name match
+              }
+            ]
+          }
+        ]);
+      });
+
+      it('should add sourceString to an extension', () => {
+        const r4WithR5pagesConfig = cloneDeep(minimalConfig);
+        r4WithR5pagesConfig.pages = [
+          {
+            nameUrl: 'sourceString.md',
+            title: 'SourceString',
+            sourceString: 'sourceString for page',
+            generation: 'markdown',
+            page: [
+              {
+                nameUrl: 'nested-sourceString.md',
+                title: 'Nested SourceString',
+                sourceString: 'nested sourceString for page',
+                generation: 'markdown'
+              }
+            ]
+          }
+        ];
+        const pkg = new Package(r4WithR5pagesConfig);
+        const exporter = new IGExporter(pkg, defs, fixtures);
+        exporter.export(tempOutPages);
+
+        const igPath = path.join(
+          tempOutPages,
+          'fsh-generated',
+          'resources',
+          'ImplementationGuide-fhir.us.minimal.json'
+        );
+        expect(fs.existsSync(igPath)).toBeTruthy();
+        const igContent = fs.readJSONSync(igPath);
+        expect(igContent.definition.page.page).toEqual([
+          {
+            nameUrl: 'sourceString.html',
+            title: 'SourceString',
+            generation: 'markdown',
+            extension: [
+              {
+                url: 'http://hl7.org/fhir/5.0/StructureDefinition/extension-ImplementationGuide.definition.page.source',
+                valueString: 'sourceString for page' // extension for source[x] string
+              }
+            ],
+            page: [
+              {
+                nameUrl: 'nested-sourceString.html',
+                title: 'Nested SourceString',
+                generation: 'markdown',
+                extension: [
+                  {
+                    url: 'http://hl7.org/fhir/5.0/StructureDefinition/extension-ImplementationGuide.definition.page.source',
+                    valueString: 'nested sourceString for page' // extension for source[x] string
+                  }
+                ]
+              }
+            ]
+          }
+        ]);
+      });
+
+      it('should add sourceMarkdown to an extension', () => {
+        const r4WithR5pagesConfig = cloneDeep(minimalConfig);
+        r4WithR5pagesConfig.pages = [
+          {
+            nameUrl: 'sourceMarkdown.md',
+            title: 'SourceMarkdown',
+            sourceMarkdown: 'sourceMarkdown for page',
+            generation: 'markdown',
+            page: [
+              {
+                nameUrl: 'nested-sourceMarkdown.md',
+                title: 'Nested SourceMarkdown',
+                sourceMarkdown: 'nested sourceMarkdown for page',
+                generation: 'markdown'
+              }
+            ]
+          }
+        ];
+        const pkg = new Package(r4WithR5pagesConfig);
+        const exporter = new IGExporter(pkg, defs, fixtures);
+        exporter.export(tempOutPages);
+
+        const igPath = path.join(
+          tempOutPages,
+          'fsh-generated',
+          'resources',
+          'ImplementationGuide-fhir.us.minimal.json'
+        );
+        expect(fs.existsSync(igPath)).toBeTruthy();
+        const igContent = fs.readJSONSync(igPath);
+        expect(igContent.definition.page.page).toEqual([
+          {
+            nameUrl: 'sourceMarkdown.html',
+            title: 'SourceMarkdown',
+            generation: 'markdown',
+            extension: [
+              {
+                url: 'http://hl7.org/fhir/5.0/StructureDefinition/extension-ImplementationGuide.definition.page.source',
+                valueMarkdown: 'sourceMarkdown for page' // extension for source[x] markdown
+              }
+            ],
+            page: [
+              {
+                nameUrl: 'nested-sourceMarkdown.html',
+                title: 'Nested SourceMarkdown',
+                generation: 'markdown',
+                extension: [
+                  {
+                    url: 'http://hl7.org/fhir/5.0/StructureDefinition/extension-ImplementationGuide.definition.page.source',
+                    valueMarkdown: 'nested sourceMarkdown for page' // extension for source[x] markdown
+                  }
+                ]
+              }
+            ]
+          }
+        ]);
+      });
+    });
+
+    it('should add definition.parameter.code system and code information to an extension if provided', () => {
+      const igPath = path.join(
+        tempOut,
+        'fsh-generated',
+        'resources',
+        'ImplementationGuide-fhir.us.minimal.json'
+      );
+      expect(fs.existsSync(igPath)).toBeTruthy();
+      const igContent = fs.readJSONSync(igPath);
+      expect(igContent.definition.parameter).toHaveLength(3); // Two configured + 1 default path-history
+      expect(igContent.definition.parameter[0]).toEqual({
+        code: 'generate-xml', // code configured without a system is left as is
+        value: 'true'
+      });
+      expect(igContent.definition.parameter[1]).toEqual({
+        code: 'special-code', // code configured with a system is parsed out from the system
+        value: 'sparkles',
+        extension: [
+          // extension added with the full Coding provided in configuration
+          {
+            url: 'http://hl7.org/fhir/5.0/StructureDefinition/extension-ImplementationGuide.definition.resource.parameter.code',
+            valueCoding: {
+              code: 'special-code',
+              system: 'http://example.org/parameters'
+            }
+          }
+        ]
+      });
+    });
+
+    it('should add copyrightLabel information to an extension if provided', () => {
+      const igPath = path.join(
+        tempOut,
+        'fsh-generated',
+        'resources',
+        'ImplementationGuide-fhir.us.minimal.json'
+      );
+      expect(fs.existsSync(igPath)).toBeTruthy();
+      const igContent = fs.readJSONSync(igPath);
+      expect(igContent.extension).toContainEqual({
+        url: 'http://hl7.org/fhir/5.0/StructureDefinition/extension-ImplementationGuide.copyrightLabel',
+        valueString: 'Shorty Fsh 2022+'
+      });
+    });
+
+    it('should add versionAlgorithmString to an extension if provided', () => {
+      // Export IG in this test so can test all variations of versionAlgorithm[x]
+      const configWithVersionAlgorithm = cloneDeep(minimalConfig);
+      configWithVersionAlgorithm.versionAlgorithmString = 'date';
+
+      const fixtures = path.join(__dirname, 'fixtures', 'simple-ig');
+      const defs = new FHIRDefinitions();
+      const pkg = new Package(configWithVersionAlgorithm);
+      const exporter = new IGExporter(pkg, defs, fixtures);
+      const tempOut = temp.mkdirSync('sushi-test-version-alg');
+      exporter.export(tempOut);
+      const igPath = path.join(
+        tempOut,
+        'fsh-generated',
+        'resources',
+        'ImplementationGuide-fhir.us.minimal.json'
+      );
+      expect(fs.existsSync(igPath)).toBeTruthy();
+      const igContent = fs.readJSONSync(igPath);
+
+      expect(igContent.extension).toEqual([
+        {
+          url: 'http://hl7.org/fhir/5.0/StructureDefinition/extension-ImplementationGuide.versionAlgorithm',
+          valueString: 'date'
+        }
+      ]);
+    });
+
+    it('should add versionAlgorithmCoding to an extension if provided', () => {
+      // Export IG in this test so can test all variations of versionAlgorithm[x]
+      const configWithVersionAlgorithm = cloneDeep(minimalConfig);
+      configWithVersionAlgorithm.versionAlgorithmCoding = {
+        system: 'http://example.org',
+        code: 'semver'
+      };
+
+      const fixtures = path.join(__dirname, 'fixtures', 'simple-ig');
+      const defs = new FHIRDefinitions();
+      const pkg = new Package(configWithVersionAlgorithm);
+      const exporter = new IGExporter(pkg, defs, fixtures);
+      const tempOut = temp.mkdirSync('sushi-test-version-alg');
+      exporter.export(tempOut);
+      const igPath = path.join(
+        tempOut,
+        'fsh-generated',
+        'resources',
+        'ImplementationGuide-fhir.us.minimal.json'
+      );
+      expect(fs.existsSync(igPath)).toBeTruthy();
+      const igContent = fs.readJSONSync(igPath);
+
+      expect(igContent.extension).toEqual([
+        {
+          url: 'http://hl7.org/fhir/5.0/StructureDefinition/extension-ImplementationGuide.versionAlgorithm',
+          valueCoding: { system: 'http://example.org', code: 'semver' }
+        }
+      ]);
+    });
+
+    it('should add dependsOn.reason to an extension if provided', () => {
+      const igPath = path.join(
+        tempOut,
+        'fsh-generated',
+        'resources',
+        'ImplementationGuide-fhir.us.minimal.json'
+      );
+      expect(fs.existsSync(igPath)).toBeTruthy();
+      const igContent = fs.readJSONSync(igPath);
+      expect(igContent.dependsOn).toEqual([
+        {
+          packageId: 'hl7.fhir.us.ocean',
+          uri: 'http://example.org/ocean',
+          id: 'ocean',
+          version: '1.0.0',
+          extension: [
+            {
+              url: 'http://hl7.org/fhir/5.0/StructureDefinition/extension-ImplementationGuide.dependsOn.reason',
+              valueMarkdown: 'The ocean is essential because it has fsh'
+            }
+          ]
+        }
+      ]);
+    });
+  });
 });