An 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 firsthand, I encountered several issues that I'd like to document here.

Work 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 packages were created for integration purposes.

Therefore, from Fedify 2.0.0 onwards, we decided to create a new package that supports Fresh 2.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 confirmed working 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 up to step 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 this value 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)

Anyway, I started deno task dev with high hopes. This was my first encounter with Fresh and Preact.

But...

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 phenomenon can occur 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 node_modules, but I can't figure out the cause. Even manually removing Preact from node_modules results in a blank screen rather than an import error, which is quite puzzling.

If anyone can help me and that foreign person I only know by their Discord nickname, I would greatly appreciate it.

Fedify Middleware Prevents Server from Starting (Resolved)

The middleware itself hadn't changed much except for the 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. It executed normally. Now let's connect. I expected to be greeted by a blank screen.

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

But that wasn't the case.

Fortunately, I solved it with the help of Claude Code. The principle was as follows:

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

Fortunately, there was an option to run specific dependencies and their dependencies externally without going through Vite. HMR won't work, but I won't need to modify @fedify/fedify in real-time while creating @fedify/fresh.

Anyway, the Dev Server now runs well.

fedify nodeinfo command successfully fetching nodeinfo from the development server

The middleware is working properly 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 doesn't work! The cause was again CJS. Adjusting build.rollupOptions's external solved the issue.

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

This is the final vite.config.ts.

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 return with Part 2 of this creation story. Please hope that I don't have to come back!

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