0

I'm using filestack-js in a Rails project which is bundled with Vite. Everything works as expected until I include the ESM module for the filestack-js library, in this case in a StimulusJS controller:

import { Controller } from "stimulus";

import * as filestack from "filestack-js";

export default class extends Controller {
  // some irrelevant implementation code that calls filestack.init(...)
}

Loading the above controller file in the browser causes an error:

tslib.es6.js:25 Uncaught TypeError: Object prototype may only be an Object or null: undefined
    at setPrototypeOf (<anonymous>)
    at __extends (tslib.es6.js:25)
    at http.ts:43
    at node_modules/filestack-js/build/module/lib/request/adapters/http.js (http.ts:64)
    at __init (chunk-IHTDASF6.js?v=1616a449:14)
    at request_adapter.node.ts:17

This is an error produced by the browser while working in a development environment, using Vite to build and serve ES modules to the browser directly. It handles Typescript compilation. Removing the import * as filestack bit makes the error go away (but obviously breaks the class' functionality).

My google searches seem to suggest that this might be a circular dependency problem. The browser stack trace points towards a file in the filestack-js library:

// src/lib/request/adapters/http.ts

import * as url from 'url';
import * as zlib from 'zlib';
import Debug from 'debug';

import { AdapterInterface } from './interface';
import { getVersion } from '../../utils';
import * as Stream from 'stream'; // <---------- Stream imported here
import { FsRequestOptions, FsResponse } from '../types';
import * as utils from '../utils';
import { prepareData, parseResponse, combineURL, set as setHeader, normalizeHeaders } from './../helpers';
import { FsRequestErrorCode, FsRequestError } from '../error';
import { FsHttpMethod } from './../types';

const HTTPS_REGEXP = /https:?/;
const HTTP_CHUNK_SIZE = 16 * 1024;
const MAX_REDIRECTS = 10;
const CANCEL_CLEAR = `FsCleanMemory`;
const debug = Debug('fs:request:http');

class HttpWritableStream extends Stream.Writable {
  // omitted class definition
}

Where Stream.Writable is actually undefined due to a circular dependency problem. I have no idea how that would happen or seem to only affect me.

This is not an issue that has been reported on the filestack-js issue tracker.

Debugging in the browser and cloning/linking the repository locally have confirmed that Stream.Writable is returning undefined, but I don't know enough about JS to understand why. Supposedly this typically happens due to a circular dependency, but I'm not sure how the nodejs Stream module would have circular dependencies on a random library like filestack-js. I am also inexperienced enough in the JS world to understand exactly what it means to be using a nodeJS library like Stream in a browser module - filestack-js has both browser modules and commonJS/nodeJS modules so I'm not sure how/if they relate or interact.

Here's what the Stream object looks like when logged to a browser console. Clearly something has been imported but Writable is not a property of what was imported:

browser console

FWIW this happens on Chrome and Firefox, latest versions of each.

I also tried using dpdm to analyze the filestack-js project for circular dependencies. It did find some but it doesn't appear as if they are causing errors, and it does seem to explicitly be excluding node libraries and other dependency libraries.

taylorthurlow
  • 2,953
  • 3
  • 28
  • 42
  • Ok I'm seeing that it might be related to the fact that Vite uses rollup under the hood, and that rollup may not be able to handle certain circular dependencies that exist in the nodeJS libraries? [this repo](https://github.com/snowpackjs/rollup-plugin-polyfill-node) exists which seems like it might be designed to fix this sort of issue, but it doesn't seem to be working for me. – taylorthurlow Aug 26 '21 at 20:52
  • Is this Stimulus thing and your own class actually necessary to cause the problem, or does it happen already if all you do is to import the `filestack-js` library? – Bergi Aug 26 '21 at 22:40
  • @Bergi I can confirm that it is unrelated. It just happens to be where I’m using the library. Sorry that I didn’t make that clear. – taylorthurlow Aug 26 '21 at 22:41
  • 1
    In that case I'd definitely report it as a bug in their issue tracker. Especially as [the documentation](https://github.com/filestack/filestack-js#module-overview) claims to work with rollup out of the box. You should share your rollup config though – Bergi Aug 26 '21 at 22:45
  • Ah wow I missed that reference to rollup. I've so far hesitated to post an issue because I don't have an easy reproduction but I guess I might be able to spin up a semi-simple one. Thanks. – taylorthurlow Aug 26 '21 at 22:47

2 Answers2

0

Ok I think I've solved my issue but I'm not an expert so I'll try to explain what the problem was. Feel free to chime in with clarification if you know better.

This was caused by filestack-js's heavy usage of nodejs libraries. Historically, Webpack v4 has polyfilled a lot of common NodeJS libraries for usage in-browser, entirely transparent to most developers. This worked great but was complete magic.

Rollup, and incidentally, Webpack v5, do not do this polyfilling, so any nodeJS libraries used by "ESM" libraries from NPM that aren't directly compatible with modern browsers will just break. In order to polyfill this manually I had to instruct Vite & Rollup to alias the name of the nodejs stream module to something that is directly compatible with browsers, and install that. To do that, I:

yarn add --dev stream-browserify

And added the following to my vite.config.js:

// ...
resolve: {
  alias: {
    stream: "stream-browserify",
  },
},
// ...

There should be a very similar (but different) way of telling Rollup to do this, because here I do it though the Vite configuration.

For extra context, here is the GitHub issue I opened on the filestack-js repo: https://github.com/filestack/filestack-js/issues/458

taylorthurlow
  • 2,953
  • 3
  • 28
  • 42
0

Imported it directly as recommended in the link Taylor recommended.

import * as filestack from 'filestack-js/build/browser/filestack.esm';

https://github.com/filestack/filestack-js/issues/458#issuecomment-927373100

Chris
  • 644
  • 1
  • 12
  • 28