Repository: nteract/nteract (language: python)

Issue:
Unable to load mythic-configuration in browser due to node package reference
**Application or Package Used**
mythic-configuration

**Describe the bug**
Importing mythic-configuration into a browser setting results in this stack trace:
```
ERROR in ./node_modules/mkdirp/index.js
Module not found: Error: Can't resolve 'fs' in '.../node_modules/mkdirp'
 @ ./node_modules/mkdirp/index.js 2:9-22
 @ ./node_modules/fs-observable/lib/index.js
 @ ./node_modules/@nteract/mythic-configuration/lib/backends/filesystem.js
 @ ./node_modules/@nteract/mythic-configuration/lib/index.js
 @ ./node_modules/@nteract/stateful-components/lib/config-options.js
 @ ./node_modules/@nteract/stateful-components/lib/index.js
```

**To Reproduce**
Steps to reproduce the behavior:

1. Start with a react application in the browser
2. Add `mythic-configuration` as a dependency
3. Attempt to import mythic-configuration
4. Note the stack trace

**Expected behavior**
I expect no stack trace. I expect mythic-configuration not to import any node-specific modules by default.

**Desktop (please complete the following information):**

- OS: macOS
- Browser: chrome
- Version: 88

**Additional context**
The stack trace tells the story, but essentially by the root cause is that `mythic-configuration/index.js` imports `backends/filesystem.js` which imports `fs-observable`, which is node-specific and therefore breaks in the browser.

If index.js did a dynamic import of filesystem only as requested it would probably fix this ticket.


Discussion / hints:

Fix patch:
diff --git a/changelogs/current_changelog.md b/changelogs/current_changelog.md
index 718cfabd80..97f68099c4 100644
--- a/changelogs/current_changelog.md
+++ b/changelogs/current_changelog.md
@@ -24,6 +24,8 @@ Provide a bulleted list of new features or improvements and a reference to the P
 
 Provide a bulleted list of bug fixes and a reference to the PR(s) containing the changes.
 
+- Fixed mythic-configuration, allowing it to be imported in a browser setting ([Issue #5445](https://github.com/nteract/nteract/issues/5445))
+
 ## Core SDK Packages
 
 ### @nteract/actions ([publish-version-here])
diff --git a/packages/mythic-configuration/src/index.ts b/packages/mythic-configuration/src/index.ts
index e9e0a3c2a9..c671aa7a2e 100644
--- a/packages/mythic-configuration/src/index.ts
+++ b/packages/mythic-configuration/src/index.ts
@@ -4,7 +4,6 @@ import { setConfigAtKey } from "./myths/set-config-at-key";
 import { configuration } from "./package";
 import { ConfigurationOption, ConfigurationOptionDefinition, ConfigurationOptionDeprecatedDefinition, HasPrivateConfigurationState } from "./types";
 
-export { setConfigFile } from "./backends/filesystem"
 export { configuration } from "./package";
 export { setConfigAtKey } from "./myths/set-config-at-key"
 export { toggleConfigAtKey } from "./myths/toggle-config-at-key"
@@ -125,3 +124,34 @@ export const allDeprecations = () => {
     changeTo,
   })));
 }
+
+// Export the filesystem backend's function for loading config. This export will
+// fail in the browser since it does not have the transitively depended 'fs'
+// package, so guard against that for our dear browser-bound friends.
+export const setConfigFile = (function () {
+  // Try to return the actually requested function (works on node & electron)
+  try {
+    // This import fails in the browser due to fs-observable dependencies.
+    // Attempt the import using a non-literal string to gently dissuade webpack
+    // and similar creatures from pre-processing the `require` (which fails in a
+    // non-catchable way).
+    const fsBackendPath = "./backends/filesystem";
+    const fsBackend = require(fsBackendPath);
+
+    // Some browsers define 'require', and maybe they will return something
+    // falsy instead of throwing so let's make sure that's not happening.
+    if (fsBackend === undefined || fsBackend.setConfigFile === undefined) {
+      throw new Error(`Looked for 'setConfigFile' but couldn't find it on imported mythic-configuration/backends/filesystem: ${JSON.stringify(fsBackend)}`);
+    }
+
+    return fsBackend.setConfigFile;
+  } catch (requireErr) {
+    // Ono the import failed! Not a showstopper for browser users. Return a
+    // version of the function that yells if you use it.
+    return (filename: string) => {
+      const err = Error("Failed to load mythic-configuration's filesystem backend. You're probably running in a browser, but this only works in a node runtime!");
+      err.stack += "\nCaused by " + requireErr.stack;
+      throw err;
+    }
+  }
+})();


Test patch:
diff --git a/packages/mythic-configuration/__tests__/configuration.spec.ts b/packages/mythic-configuration/__tests__/configuration.spec.ts
index bd2811c595..6ea650da97 100644
--- a/packages/mythic-configuration/__tests__/configuration.spec.ts
+++ b/packages/mythic-configuration/__tests__/configuration.spec.ts
@@ -111,4 +111,25 @@ describe("configuration", () => {
       })
     );
   });
+
+  describe('setConfigFile', () => {
+    test("in node environment index.ts exports the implementation from backends/filesystem", () => {
+      const indexVersion = require("../src").setConfigFile;
+      const fsBackendVersion = require("../src/backends/filesystem").setConfigFile;
+      expect(indexVersion).toBe(fsBackendVersion);
+    });
+
+    test("in non-node environment index.ts exports an implementation that throws exceptions [https://github.com/nteract/nteract/issues/5445]", () => {
+      jest.resetModules();
+      jest.mock("../src/backends/filesystem", () => {
+        throw new Error("Can't resolve 'fs' in '.../node_modules/mkdirp'");
+      });
+      const indexVersion = require("../src").setConfigFile;
+      expect(() => indexVersion("/any/path/will/throw")).toThrow("Failed to load mythic-configuration");
+    });
+
+    afterEach(() => {
+      jest.resetAllMocks();
+    });
+  });
 });