My Unpleasant First Encounter with Fresh: Creating @fedify/fresh - Part 1

개발곰 @gaebalgom@hackers.pub

Fresh?

Have you heard of the Deno Fresh framework? It's a framework developed and promoted by the Deno team. I encountered it for the first time while working on the Create @fedify/fresh package for Fresh 2.0 integration issue as part of the Fedify project.

Like its lemon logo, it looks pretty, but when I actually tried it out, I encountered several issues that I'd like to document here.

Project Background

There are two main reasons behind the Create @fedify/fresh package for Fresh 2.0 integration task:

  1. Fresh entered version 2.X with changes that aren't backward compatible with existing code.
  2. As Fedify packages were being reorganized, code under /x/ was removed, and separate integration packages were created instead.

Therefore, we decided to create a new package supporting Fresh 2.0 starting from Fedify 2.0.0.

When creating an integration package, we follow this process:

  1. Create and run a project using the package under the examples directory
  2. Create Federation-related files and middleware
  3. Once everything works properly, separate it into a standalone package
  4. Submit a PR, have the code reviewed, document it, add it to the init command...

Currently, we've completed steps 1 and 2 and are about to separate it into a standalone package. Before that, I thought it would be good to document the issues I encountered.

Issues Encountered

(Resolved) The project under the example folder doesn't work properly.

Fresh creates projects with the command deno run -Ar jsr:@fresh/init. I created a Fresh project under fedify/examples as usual, but when I tried to run the development server (deno task dev), it didn't work:

error: Config file must be a member of the workspace.
  Config: file:///home/dodok8/Development/fedify/examples/fresh/deno.json
  Workspace: file:///home/dodok8/Development/fedify/

Ah, it seems the workspace isn't specified. I added it to the workspace.

...
 Warning

  Ignored build scripts for packages:
  npm:workerd@1.20251011.0
  npm:sharp@0.33.5

  Lifecycle scripts are only supported when using a `node_modules` directory.
  Enable it in your deno config file:
  "nodeModulesDir": "auto"
╰─
Task dev vite
error: Uncaught Error: UNHANDLED PROMISE REJECTION
    at Process.<anonymous> (file:///home/dodok8/.cache/deno/npm/registry.npmjs.org/vite/7.1.12/bin/vite.js:12:11)
...

It's a long error, but the warning in the middle is helpfully emphasized. It says we need to change nodeModulesDir to auto. Looking at the Deno official documentation to understand this option, setting it to "auto" means packages will be fetched from node_modules instead of using deno cache. Fortunately, this didn't affect other build settings in fedify. My computer's node_modules just got bigger.

Frontend Not Showing! (Unresolved)

With everything set up, I eagerly ran deno task dev. This was my first encounter with Fresh and Preact.

But then...

Empty blank screen

Nothing appears? That's strange. When I followed the official documentation and tried in a separate repository, the counter example was visible.

I found someone with the same issue in the official Deno Discord. According to the developers, this can happen when multiple copies of Preact are installed. They said it was a bug fixed in version 2.5.4. But I'm using 2.5.6! Even after clearing the installation cache, I still see nothing. It seems like something is wrong with the node_modules, but I can't figure out what. Even manually removing Preact from node_modules results in a blank screen rather than import errors, which is puzzling.

If anyone can help me and the person I only know by their Discord nickname in the Deno server, I'd greatly appreciate it.

Fedify Middleware Prevents Server from Running (Resolved)

The middleware itself hadn't changed much except for types, so I borrowed a significant portion from the previous code and just adjusted the types and parameters to match Fresh 2.0. After resolving the type errors, I ran it. The server started normally. Now let's try connecting. I expected to see a blank screen.

Screen full of errors, with "varint.encode is not a function" error message at the top

But that's not what happened.

Fortunately, I solved this with Claude Code's help. Here's what was happening:

  • Fresh 2.0 uses Vite as its development server
  • Middleware is processed by Vite's SSR Module Runner, which doesn't support CommonJS (but comes with features like HMR)
  • However, Fedify has CommonJS dependencies
  • Boom!

Luckily, there's an option to run specific dependencies and their dependencies outside of Vite. This would disable HMR for those dependencies, but that's fine since I won't need to modify @fedify/fedify in real-time while creating @fedify/fresh.

With that fixed, the dev server now runs properly.

fedify nodeinfo command successfully retrieving nodeinfo from the development server

The middleware is working too.

Now let's try building and running deno task start:

error: Uncaught (in promise) Error: Cannot find module './llhttp/llhttp-wasm.js'
Require stack:
- /home/dodok8/Development/fedify/examples/fresh/_fresh/server/server-entry.mjs
    at Module._resolveFilename (node:module:633:15)
    at Module._load (node:module:496:27)
    at Module.require (node:module:694:19)
    at require (node:module:828:16)
    at lazyllhttp (file:///home/dodok8/Development/fedify/examples/fresh/_fresh/server/server-entry.mjs:14671:67)
    at file:///home/dodok8/Development/fedify/examples/fresh/_fresh/server/server-entry.mjs:14714:21

It's not working! The cause was again CommonJS. Adjusting build.rollupOptions's external fixed the issue.

Here's the final vite.config.ts:

export default defineConfig({
  plugins: [fresh()],
  ssr: {
    external: [
      "@fedify/fedify",
    ],
  },
  build: {
    rollupOptions: {
      external: [
        "@fedify/fedify",
      ],
    },
  },
});

Now I'm moving on to the package separation work. If I encounter more bugs during this process, or if there are issues when trying to use the published package externally, I'll be back with Part 2 of this creation story. Please wish me luck that I won't have to return!

4

1 comment

If you have a fediverse account, you can comment on this article from your own instance. Search https://hackers.pub/ap/articles/019a4dec-ba0c-7cca-81e0-c7eb302c75e2 on your instance and reply to it.

0