Skip to content

Cloudflare adapter disables known cloudflare worker "compatible" dependencies #263

@patdx

Description

@patdx

Summary

The adapter solid-start-cloudflare-workers pulls in "node" exports of dependencies by default. This makes sense for solid-js framework and possibly precompiled Solid Components in node_modules. However, for many other third-party packages, this will pull in incompatible "node" versions of those libraries. Using browser/web exports when bundling for cloudflare workers (and similar edge runtimes) will probably lead to more success.

Background

I want to set up a graphql server as an API route.

What happened:

1. Plain cloudflare worker test

I tried to set up a graphql server using a package that claims to work with cloudflare.

Here is my implementation: https://github.com/patdx/solid-start-bundle-experiment-old/tree/main/plain-cloudflare-worker

It works great :)

2. Attempted integration into solid-start

I added it to an API route and In solid-start dev/node mode it worked fine.

https://github.com/patdx/solid-start-bundle-experiment-old/tree/main/solid-with-standard-adapter

I tried to build it with the solid-start-cloudflare-workers adapter.

But, I got a really big 2.5mb dist/server.js with a bunch of node imports

// dist/server.js
import manifestJSON from '__STATIC_CONTENT_MANIFEST';
import Url from 'url';
import require$$1$1 from 'fs';
import require$$6$1 from 'buffer';
import require$$0 from 'util';
import * as crypto__default from 'crypto';
import crypto__default__default from 'crypto';
import * as process$1 from 'process';
import Stream from 'stream';
import require$$0$1 from 'assert';
import require$$4 from 'net';
import http from 'http';
import require$$10 from 'stream/web';
import require$$1$2 from 'perf_hooks';
import require$$4$1 from 'util/types';
import require$$0$2 from 'events';
import require$$4$2 from 'tls';
import require$$3 from 'async_hooks';
import require$$1$3 from 'console';
import zlib from 'zlib';
import require$$0$3 from 'punycode';
import https from 'https';

Even if I turn on node_compat=true in my wrangler.toml it still does not work, wrangler fails with an error like:

✘ [ERROR] Build failed with 4 errors:

  dist/server.js:13:24: ERROR: Could not resolve "stream/web"
  dist/server.js:14:25: ERROR: Could not resolve "perf_hooks"
  dist/server.js:15:25: ERROR: Could not resolve "util/types"
  dist/server.js:18:23: ERROR: Could not resolve "async_hooks"

Not to mention there seems to be at least one or two fetch polyfills inside the bundle

3. My attempted fix

I noticed that cloudflare wrangler CLI is automatically bundling with conditions: ["worker", "browser"]

However, the solid-start-cloudflare-workers prebundles with ["node", "solid"]. This step is pulling in the (unwanted) Node.js version @graphql-yoga/common and all sorts of extra dependencies and Node polyfills.

I tried to give wrangler the .solid/sever.js directly and let wrangler do the only bundling instead.

This looked like it would work, until I went to try running the code and it errored out because that renderToStringAsync is not available in the browser. Oh, oops 😅

After some trial and error, I found I could set resolveOnly: "solid-js" inside of my fork of solid-start-cloudflare-workers. Then, this step bundled the node/server version of solid-js so renderToStringAsync can work. And then wrangler bundled everything else in the browser friendly version. It worked! :)

Conclusion

Edge runtime need the server version of solid-js.

However, for many other packages, this will pull in polyfills and node-native code that are very heavy or do not work in an edge runtime. Not to mention, I think it will be a bit confusing for developers to find that solid-start-cloudflare-workers only supports a subset of the packages supported by plain cloudflare workers.

I've tried to survey some other adapters and it seems most of the are leaning towards targeting web/webworker environments.

Additionally, some Vite-based frameworks do this inside Vite itself, saving a bundle step. For example, Shopify Hydrogen.

In a perfect world, I think we would try to find a way to get solid-js to expose its server exports to worker environments naturally. Perhaps using the worker export condition like emotion css. Then, solid-start-cloudflare-workers could target a worker/browser environment to be consistent with other cloudflare deploy pipelines while still getting the server exports from solid-js. This would also benefit non-solid-start based SSR environments that target cloudflare workers. However, the worker export is not really a standard, I don't know if this will be enough to support all edge runtimes.

I found this old discussion on the main repo: solidjs/solid#308 (reply in thread)

The only thing I'm wondering is do we need something other than node to indicate non-node server environments like Deno or Cloudflare. I am tempted to include server everywhere I include node. To be fair this is only a bundling concern but maybe node === all server isn't right.

Which is kind of related too. Exposing a parallel import "server" seems like it could be nice too (but would not be enough for compatibility with plain cloudflare wrangler API).

If it is preferred to solve by only changing the adapter, maybe we can add some configuration settings or consider using my hack of resolveOnly: "solid-js", so the adapter is only bundling solid-js and wrangler is handling the majority of the dependencies. Note: I guess any solution may need special logic to handle solid components imported from node_modules too. (Not sure how to do this...)

Related links

Metadata

Metadata

Assignees

No one assigned

    Labels

    upstreamIssue waiting on dependencies

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions