Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The road to Fresh 2.0 #2363

Open
marvinhagemeister opened this issue Mar 14, 2024 · 45 comments
Open

The road to Fresh 2.0 #2363

marvinhagemeister opened this issue Mar 14, 2024 · 45 comments

Comments

@marvinhagemeister
Copy link
Collaborator

marvinhagemeister commented Mar 14, 2024

The road to Fresh 2.0

tl;dr: Fresh will become massively simpler and be distributed via JSR with the upcoming 2.0 release. Express/Hono-like API and true async components and much more.


Back to Fresh!

Since yesterday, I've been going back to working on Fresh again. During the past few months I helped with shipping https://jsr.io/ which meant that Fresh sat on the back burner for me. Admittedly, I needed a bit a break from Fresh as well, and JSR came along at the right time.

Working on JSR allowed me to put myself into the user's seat and get first hands experiences with Fresh as a user, which was nice change! It also uncovered a lot of things where I feel we can make Fresh much better.

With JSR beeing out of the door, the next task for me is to move Fresh over from deno.land/x to JSR. Given that this is a bit of a breaking change, @lucacasonato and I were wondering what a potential Fresh 2.0 release could look like.

We've never done a breaking Fresh release before, but now, after having worked for nearly a year on Fresh, the timing feels right. It allows us to revisit all the ideas in Fresh and part ways with those that didn't pan out.

We spent yesterday morning going through the most common problems people shared on Discord and in our issue tracker to get a good picture of where Fresh is at. The overall theme we want to have for Fresh 2, is to be massively simpler than before.

A new plugin API

It became pretty clear that many issues relate to the "clunkyness" of the current plugin API. It's the most common way to extend Fresh itself with capabilities that it doesn't include out of the box. Whilst it received many cool new features over time, it quite never felt elegant as it should be. This is no surprise given that nothing in Fresh itself makes use of it.

Back in July of last year there was some explorations on making Fresh's API more express/Hono-like #1487 and yesterday we realized that this is the perfect solution for nearly all the problems encountered with the current plugin API. Here is what we're picturing it to look like:

const app = new FreshApp();

// Custom middlewares
app.use(ctx => {
  console.log(`Here is a cool request: ${ctx.url}`)
  return ctx.next();
});

// Also can be branched on by HTTP method
app.get(ctx => {
  return new Response("it works!")
});

// Adding an island (exact function signature yet to be determined)
app.addIsland(...)

// Finally, start the app
await app.listen();

A Fresh plugin would change from the complex object it is today, to being nothing more than a standard JavaScript function:

function disallowFoo(app: App) {
  let counter = 0;

  // Redirect to / whenever a route contains
  // the word "foo".
  app.use((ctx) => {
    if (ctx.url.pathname.includes("foo")) {
      counter++;
      return ctx.redirect("/");
    }

    return ctx.next();
  });

  // Add a route that shows how many times we
  // redirected folks
  app.get("/counter", () => {
    const msg = `Foo redirect counter: ${counter}`;
    return new Response(msg);
  });
}

// Usage
const app = new FreshApp();

// Adding the plugin is a standard function call
disallowFoo(app);

await app.listen();

The beauty about this kind of API is that many features in Fresh are a just standard middleware, and the more complex ones like our file system router and middleware handler would just call methods on app. The internals of Fresh will be implemented the exact same way as any other plugin or middleware. This eliminates the mismatch of the current plugin API and the Fresh internals of today.

Simpler middleware signature

The current middleware signature in Fresh has two function arguments:

type Middleware = (req: Request, ctx: FreshContext) => Promise<Response>;

In most of our code we noticed that we rarely need to access the Request object. So most of our middlewares just skip over it:

// We don't need _req, but still need to define it...
const foo = (_req: Request, ctx: FreshContext) => {
  // Do something here
  const exists = doSomething();
  if (!exists) {
    return ctx.renderNotFound();
  }

  // Respond
  return ctx.next();
};

It's a minor thing, but it's a bit annoying that you always have to sorta step over the req argument. With Fresh 2.0 we're planning to move it onto the context object.

// Fresh 1.x
const foo = (req: Request, ctx: FreshContext) => new Response("hello");

// Fresh 2.0
const foo = (ctx: FreshContext) => new Response("hello");

Same arguments for sync and async routes

Orignally, I've modelled async routes after the middleware signature. That's why they require two arguments:

export default async function Page(req: Request, ctx: RouteContext) {
  // ...
}

With the benefit of hindsight, I consider this a mistake. What naturally happens is that folks tend to start out with a synchronous route and add the async keyword to make it asynchronoues. But this doesn't work in Fresh 1.x.

// User starts out with a sync route
export default function Page(props: PageProps) {
  // ...
}

// Later they want to make it async, but this
// breaks in Fresh 1.x :S
export default async function Page(props: PageProps) {
  // ...
}

So yeah, we'll correct that. In Fresh 2.0 this will work as expected.

// Sync route same as in Fresh 1.x
export default function Page(props: PageProps) {
  // ...
}

// Async route in Fresh 2.0
export default async function Page(props: PageProps) {
  // ...
}

True async server-side components

In truth, the async route components in Fresh 1.x are a bit of a beautiful lie. Internally, they are not components, but a plain function that happens to return JSX. We take the returned JSX and pass it to Preact to render and that's all
there is to it. The downside of that approach is that hooks don't work inside the async funciton, because it's not executed in a component context. In fact async routes in Fresh 1.x are called way before the actual rendering starts.

// Fresh 1.x async route
export default async function Page(req: Request, ctx: RouteContext) {
  // This breaks, because this function is not a component in Fresh 1.x,
  // but rather treated as a function that happens to return JSX
  const value = useContext(MyContext);
  // ...
}

You might be wondering why we went with this approach and the main reason was that Preact didn't support rendering async components at that time. This was the quickest way to get something that gives you most of the benefits of async components with some tradeoffs into Fresh.

The good news is that Preact recently added support for rendering async components on the server itself. This means we can finally drop our workaround and support rendering async components natively.

// Fresh 2.x async components just work
export default async function Page(ctx: FreshContext) {
  // Works as expected in Fresh 2.0
  const value = useContext(MyContext);
  // ...
}

This isn't restricted to just route components either. With the exception of islands or components used inside islands, any component on the server can be async in Fresh 2. Components in islands cannot be async, because islands are also rendered in the browser and Preact does not support async components there. It's unlikely that it will in the near future either as rendering in the client is much more complex, given that it mostly has to deal with updates which are not a thing on the server.

Simpler error responses

Fresh 1.x allows you two define a _500.tsx and a _404.tsx template at the top of your routes folder. This allows you to render a different template when an error occurred. But this has a problem: What if you want to show an error page when a different status code other than 500 or 404 is thrown?

We could always add support for more error templates in Fresh, but ultimately, the heart of the issue is that Fresh does the branching instead of the developer using Fresh.

So in Fresh 2.0 both templates will be merged into one _error.tsx template.

# Fresh 1.x
routes/
   ├── _500.tsx
   ├── _404.tsx
   └── ...

# Fresh 2.0
routes/
   ├── _error.tsx
   └── ...

And then inside the _error.tsx template you can branch and render different content based on the error code if you desire:

export default function ErrorPage(ctx: FreshContext) {
  // Exact signature to be determined, but the status
  // code will live somewhere in "ctx"
  const status = ctx.error.status;

  if (status === 404) {
    return <h1>This is not the page you're looking for</h1>;
  } else {
    return <h1>Sorry - Some other error happend!</h1>;
  }
}

With only one error template to deal with, this makes it a lot easier to allow them to be put anywhere. So will be able to use different error templates for different parts of your app.

# Fresh 2.0
routes/
  ├── admin/
  │   ├── _error.tsx # different error page for admin routes
  │   └── ...
  │
  ├── _error.tsx # error template for everywhere else
  └── ...

Adding <head> elements from Handlers

Whilst not ready for the initial Fresh 2.0 release, we plan to get the guts of Fresh ready for streaming rendering where both the server and client can load data in parallel. Again, this will not be part of the initial 2.0 release, but might land later in the year. But before we can even explore that path, some changes in regards to how Fresh works are required.

With streaming the <head> portion of an HTML document will be flushed as early as possible to the browser. This breaks the current <Head> component which allows you to add additional elements into the <head>-tag from anywhere else in your component tree. By the team the <Head> component is called, the actual <head> of the document has long been flushed already to the browser. There is no remaining head on the server we could patch anymore.

So with Fresh 2.0 we'll remove the <Head> component in favour of a new way of adding elements from a route handler.

// Fresh 2.0
export const handlers = defineHandlers({
  GET(ctx) {
    const user = await loadUser();

    // Handlers in Fresh 2.0 allow you to return either
    // a `Response` instance, or a plain object like here
    return {
      // Will be passed to the component's `props.data`
      data: { name: user.name },
      // Pass additional elements to `<head>`
      head: [
        { title: "My cool Page" },
        { name: "description", content: "this is a really cool page!" },
        // ...
      ],
    };
  },
});

export default defineRoute<typeof handlers>(async (props) => {
  return <h1>User name: {props.data.name}</h1>;
});

EDIT: This API is the least fleshed out of the ones listed here and might change. The main goal we have is to get rid of the <Head> component.

Appendix

These are the breaking changes we have planned for Fresh 2. Despite some of them being breaking changes updating a Fresh 1.x project to Fresh 2 should be fairly smooth. Although this is quite a long list of bigger features, I spent yesterday and today hacking on it and got way further than I anticipated. Most of the features are already implemented in a branch, but lots of things are still in flux. It's lacking quite a bit of polish too. There will likely be some bigger changes landed in the coming weeks in the main branch.

It's too early yet to be tried out, but there will be an update soon. Once the actual release date approaches we'll also work on adding an extensive migration document. Given that it's early days and I just started back working on Fresh
yesterday, these things don't exist yet. The details of some of the features listed here and how they will be implemented might change, but I think the rough outline is pretty solid already.

I'm pretty excited about this release because it fixes many long standing issues and massively improves the overall API. Playing around with early prototypes it makes it much more fun to use too. I hope you are similarly excited about this release.

@CAYdenberg
Copy link
Contributor

If head is specified as JSON, rather than JSX, will it be possible to create arbitrary tags? I gather from your example that title: string will create a <title> and name: string; content: string will create a <meta>, but this doesn't seem very generic.

@fazil47
Copy link

fazil47 commented Mar 15, 2024

Are there any plans to use vite? Would be nice to fix #978 before 2.0 as well.

@marvinhagemeister
Copy link
Collaborator Author

@CAYdenberg The head API is the least fleshed out from the points. It should definitely be possible to set any tag you want.

@fazil47 No plans to switch to vite at the moment. Good point, #978 will be resolved before 2.0 .

@BrunoBernardino
Copy link

Are there any plans/ideas to support functions as props in islands, even if with some limitations? I personally find that the hardest thing to live with, using Fresh. It limits code re-usability considerably.

For example, it's not uncommon to build something like:

<ComplexOrBigComponent onClick={onComponentClick} /> inside an island and have the ComplexOrBigComponent.tsx simply call onClick from its props, but this doesn't work with Fresh because of the serialization for SSR.

I understand why it's done (because of the SSR), but there should be some way around it, even if it's disabling SSR for those components automatically, I believe, and it would make it a lot more easy to migrate existing code and patterns.

@marvinhagemeister
Copy link
Collaborator Author

marvinhagemeister commented Mar 18, 2024

No, that doesn't fit into the 2.0 release schedule. Serializing functions would require a complex machinery of tracking all accessed variables which is very error prone. I don't have an idea of how to solve that at the moment, so this requires further research before we can think of tackling it.

@BrunoBernardino
Copy link

BrunoBernardino commented Mar 18, 2024

My suggestion would be to not serialize that component, just let it be completely rendered in the client, as I don't think it would be possible to serialize any function reliably.

@CAYdenberg
Copy link
Contributor

If I'm understanding @BrunoBernardino 's point, the limitation he's experiencing is around code reusability. In other words, the ability to use a component as an island or not-an-island, depending on the context. In other words, for components that accept a function as an optional prop, it would be nice if there were a way to declare them as an island (or not-an-island) other than what folder they are in.

@BrunoBernardino
Copy link

BrunoBernardino commented Mar 18, 2024

@CAYdenberg that's exactly it! I'd like to create "dumb" components in components/ that receive functions as props from some islands, and wouldn't mind losing the island's benefits in those situations.

@CAYdenberg
Copy link
Contributor

Well, if it helps, components that are children of islands can receive functions as props. So, your ComplexOrBigComponent could live in components/, receive onClick when used as a descendent of an island, and not receive it when used outside of an island. Just mark onClick as being optional in your props interface declaration (and handle the cases where it is undefined).

@BrunoBernardino
Copy link

@CAYdenberg I need to try that, as that should solve my problem! I'll report back once I've had the chance to do so.

@miguelrk
Copy link

Nice points @BrunoBernardino @CAYdenberg ... also, I'd like to bring up proper support for HMR to the table, would that fit into 2.0?

@marvinhagemeister
Copy link
Collaborator Author

Proper HMR support in Fresh requires some changes in Deno CLI. It won't land as part of Fresh 2.0 but rather in the following release 2.1. That's the current plan.

@BrunoBernardino
Copy link

@CAYdenberg that totally solved my problem, thank you so much!

@KnorpelSenf
Copy link

Regarding the middleware, I have done an innovative step when I invented middleware trees for my framework. It's a much smoother thing than what express did, as it lets you combine middleware (and thus, plugins) in a much more flexible way.

If you feel like reading more about it, https://grammy.dev/advanced/middleware is the place to go. If, however, you don't want to take inspiration from that, please ignore me. Keep up the great work!

@lionel-rowe
Copy link
Contributor

Exciting stuff! Any ideas on projected release timeline yet?

@marvinhagemeister
Copy link
Collaborator Author

@KnorpelSenf thanks for sharing, that was interesting read! In our case we take routing into account which allows us to effectively flatten the tree into a flat list of middlewares + routes. This saves us a bit of book keeping and seems to work very well so far.

@lionel-rowe I'm making good progress at the moment and the hope for it is to have it out in a couple of weeks, unless I run into unforeseen challenges. I definitely want to get this into user's hands as soon as possible.

@KnorpelSenf
Copy link

KnorpelSenf commented Mar 27, 2024

@marvinhagemeister there's no book-keeping required for such an implementation. It's surprisingly trivial to do this, i.e. 10-20 lines of code (depending on how you count). We did it like this: https://github.com/grammyjs/grammY/blob/38efc3c7729eef7133446451bdd7056ad4b09004/src/composer.ts#L196

What I'm rather concerned about is that a nested middleware system prevents some performance optimizations. While you could technically implement fast routing per node, it isn't really possible to directly jump to the right handler deep down in the middleware tree. In my case, the functionality is worth that, but HTTP routing can be optimised a lot better, so the opportunity cost is different for fresh. You might not want to go with middleware trees because of this. I'll leave it up to you :)

@marvinhagemeister
Copy link
Collaborator Author

@KnorpelSenf The implementation you have there is quite elegant! I like it!

Agree about the concerns of nested middlewares potentially preventing performance optimizations. The way it currently works is that internally middlewares and routes are stored like this:

[
  * [...middlewares]
  GET / [...routeMiddlewares]
  GET /foo/bar [...routeMiddlewares]
  POST /api/foobar [...routeMiddlewares]
]

This means that an incoming request needs to do two steps:

  1. Grab the middleware array at the first index in the array, because those apply to all routes, if one of them returns a response we can bail out early
  2. Match one of the next route entries in the array. Once a route matches you already have the correct list of next middlewares to run, so no further checks are required.

Apart from the route matching, this system conceptually just walks over a flat array until one of the middlewares returns a response.

@aakashsigdel
Copy link

Great work and exciting times ahead. Are there any plans on supporting css modules, css in js and such with 2.0?

@marvinhagemeister
Copy link
Collaborator Author

@aakashsigdel CSS modules require transpilation and I've been getting pushback when proposing to transpile the server code in Fresh or landing CSS modules right in Deno itself. So no progress on that front. When it comes to CSS-in-JS something like styled-components will never be a got fit with Fresh as it turns every component into a client component effectively.

The good news though is that with 2.0 we're in a much better position to potentially add all those transpilation stuff, even for server code.

@Twitch0125
Copy link

Would it be possible to extract template rendering/islands into a plugin? It'd be awesome if you could mix/match frameworks similar to Astro, but with the ergonomics of Fresh

@marvinhagemeister
Copy link
Collaborator Author

@Twitch0125 The new architecture certainly makes this easier as rendering isn't spread out throughout the entire code base like it is in Fresh 1.x . That said there are no plans to support multiple frameworks at the moment. The worry is that supporting more than one framework requires more maintenance effort, likely more than we have the bandwidth for at the moment.

@FabianMendoza7
Copy link

Fresh 2.0 promises to bring great improvements. When do you think it will be released?

@marvinhagemeister
Copy link
Collaborator Author

@FabianMendoza7 soon™

@janvhs
Copy link

janvhs commented Apr 8, 2024

Proper HMR support in Fresh requires some changes in Deno CLI. It won't land as part of Fresh 2.0 but rather in the following release 2.1. That's the current plan.

Without having read the Fresh source, yet. Would be great if you could somehow make this loosely connected to Fresh, as it could be really useful for developing SPAs with Preact, Deno and Esbuild.

@marvinhagemeister
Copy link
Collaborator Author

Proper HMR support in Fresh requires some changes in Deno CLI. It won't land as part of Fresh 2.0 but rather in the following release 2.1. That's the current plan.

Without having read the Fresh source, yet. Would be great if you could somehow make this loosely connected to Fresh, as it could be really useful for developing SPAs with Preact, Deno and Esbuild.

Agree, I've been pushing for solutions that are not specific to Fresh for that internally. Something that any developer using Deno would profit off, not just Fresh.

@MMMartt
Copy link

MMMartt commented Apr 29, 2024

@FabianMendoza7 soon™

Thank you for the update! I'm really excited to hear that Fresh 2.0 is on the horizon. Could you clarify what "soon™" means in this context? Are we looking at a timeline of a few weeks, or is it more in the range of months? Any specifics you could share would be greatly appreciated. Thanks for your time and effort!

@dev-nicolaos
Copy link

dev-nicolaos commented May 1, 2024

If head is specified as JSON, rather than JSX, will it be possible to create arbitrary tags? I gather from your example that title: string will create a <title> and name: string; content: string will create a , but this doesn't seem very generic.

Not sure if a path has been chosen for this yet, but just a friendly reminder to tread carefully when trying to recreate all the nuances of <head> with a JS API. Jim Nielsen recently published a great post on this topic.

@Grawl
Copy link

Grawl commented May 6, 2024

HMR is the only thing I miss for now in Fresh

@lucacasonato
Copy link
Member

Not sure if a path has been chosen for this yet, but just a friendly reminder to tread carefully when trying to recreate all the nuances of <head> with a JS API. Jim Nielsen recently published a great post on this topic.

We came up with a new plan for head elements yesterday that is very HTML-first 😀

@guy-borderless
Copy link

HMR is the only thing I miss for now in Fresh

tailwind has a recommended class order which is really helpful so automatic class sorting is also missing

@guifromrio
Copy link

HMR is what's holding back our online editor from being seamless with fresh, so we're using an HTMX-only template in the meantime :'(

@marvinhagemeister
Copy link
Collaborator Author

Yeah, we hear you all. HMR will be the main focus once Fresh 2.0 is out.

@tlgimenes
Copy link
Contributor

Are there any plans/ideas to support functions as props in islands, even if with some limitations? I personally find that the hardest thing to live with, using Fresh. It limits code re-usability considerably.

For example, it's not uncommon to build something like:

<ComplexOrBigComponent onClick={onComponentClick} /> inside an island and have the ComplexOrBigComponent.tsx simply call onClick from its props, but this doesn't work with Fresh because of the serialization for SSR.

I understand why it's done (because of the SSR), but there should be some way around it, even if it's disabling SSR for those components automatically, I believe, and it would make it a lot more easy to migrate existing code and patterns.

I'm also facing similar issues. I usually have a simple function that I'd like to be called on the frontend.
Before some changes made last year in Preact, I could write something like

<MyComponent onclick="console.log('click')" />

Writing functions as strings should be more than enough for these simple, DOM manipulations. It would be nice to have this feature back.

@Grawl
Copy link

Grawl commented May 8, 2024

Yeah, we hear you all. HMR will be the main focus once Fresh 2.0 is out.

Partial powered HMR for non-island code?

@Grawl
Copy link

Grawl commented May 8, 2024

<MyComponent onclick="console.log('click')" />

Looks like Vue

@mayank99
Copy link

CSS modules require transpilation and I've been getting pushback when proposing to transpile the server code in Fresh or landing CSS modules right in Deno itself. So no progress on that front.

@marvinhagemeister Wouldn't the first step be to support CSS imports? Today, there is no way to import a CSS file in Deno, let alone transpile it.

I'm thinking it would be very useful to be able to import a CSS file and reference its contents or its url. For example:

import stylesheetContents from "../styles.css" with { type: "asset-raw" };

<style>{stylesheetContents}</style>
import stylesheetUrl from "../styles.css" with { type: "asset-url" };

<link rel="stylesheet" href={stylesheetUrl} />

When it comes to CSS-in-JS something like styled-components will never be a got fit with Fresh as it turns every component into a client component effectively.

It might be worth looking into CSS-in-JS that executes server-side (like Hono) or even at build-time (like vanilla-extract). CSS-in-JS is only bad when executed client-side (I've written about this topic at length).

@Grawl
Copy link

Grawl commented May 14, 2024

@mayank99 I solved this problem using a custom Fresh plugin:

import { Plugin } from '$fresh/server.ts'
import { ensureDir } from '$std/fs/mod.ts'

type NPMPackage = {
    name?: string
    files?: string[]
}

/** @example
1. Enable `"nodeModulesDir": true` in `deno.json`

2. Add plugin to `fresh.config.ts`

import normalize from 'npm:@csstools/normalize.css/package.json' with { type: 'json' }
export default defineConfig({
    plugins: [
        npmToStatic([normalize]),
    ],
})
*/
export const npmToStatic: (packages: NPMPackage[]) => Plugin = (packages) => ({
    name: 'npm_to_static',
    async configResolved({ staticDir }) {
        for (const { name, files } of packages) {
            if (!name) throw new Error('package does not have a name')
            if (!files) throw new Error(`package "${name}" does not have a "files" array`)
            const packageDist = `${staticDir}/dist/${name}`
            await ensureDir(packageDist)
            for (const file of files) {
                await Deno.copyFile(`./node_modules/${name}/${file}`, `${packageDist}/${file}`)
            }
        }
    },
})

@guy-borderless
Copy link

CSS modules require transpilation and I've been getting pushback when proposing to transpile the server code in Fresh or landing CSS modules right in Deno itself. So no progress on that front.

@marvinhagemeister Wouldn't the first step be to support CSS imports? Today, there is no way to import a CSS file in Deno, let alone transpile it.

I'm thinking it would be very useful to be able to import a CSS file and reference its contents or its url. For example:

import stylesheetContents from "../styles.css" with { type: "asset-raw" };

<style>{stylesheetContents}</style>
import stylesheetUrl from "../styles.css" with { type: "asset-url" };

<link rel="stylesheet" href={stylesheetUrl} />

When it comes to CSS-in-JS something like styled-components will never be a got fit with Fresh as it turns every component into a client component effectively.

It might be worth looking into CSS-in-JS that executes server-side (like Hono) or even at build-time (like vanilla-extract). CSS-in-JS is only bad when executed client-side (I've written about this topic at length).
Coloca

With tailwind and preact I personally think CSS classes are best avoided when tailwind, components, and CSS variables can solve the problem. I still find myself reaching for CSS for things like a keyframe, using a specific media query to change a CSS variable. Other than that, and rarely for 3rd party integrations, I wonder is the use case here just another way to style. Maybe it's enough to add LSP support for writing CSS inside a <style> tag.

@felix-schindler

This comment was marked as off-topic.

@roj1512
Copy link
Contributor

roj1512 commented May 30, 2024

@marvinhagemeister Why is fsRoutes() not a middleware? Like, why is it applied like fsRoutes(app) and not app.use(fsRoutes()).

fresh/www/main.ts

Lines 7 to 10 in dc283a1

await fsRoutes(app, {
loadIsland: (path) => import(`./islands/${path}`),
loadRoute: (path) => import(`./routes/${path}`),
});

@marvinhagemeister
Copy link
Collaborator Author

marvinhagemeister commented May 30, 2024

@roj1512 because it needs to register routes for the router to be aware of. It basically calls app.get/post/patch/delete() under the hood.

That said it's an interesting idea trying to make everything a middleware.

@roj1512
Copy link
Contributor

roj1512 commented May 30, 2024

I see nothing wrong with

function fsRoutes(ctx) {
    if (filter(ctx)) {
      //
    } else {
      ctx.next();
    }
}

@roj1512
Copy link
Contributor

roj1512 commented May 30, 2024

Also, for the sake of simplicity, what do you think if this option is set as default so the user could just do fsRoutes()?

{ 
   loadIsland: (path) => import(`./islands/${path}`), 
   loadRoute: (path) => import(`./routes/${path}`), 
}

@marvinhagemeister
Copy link
Collaborator Author

That won't work, because the dynamic imports are relative to the file they are in. For Deno Deploy to statically analyze them and include the referenced imported files it needs to be in the proper file. It's definitely not as nice as not having them, but then again I feel like these 2 lines are nicer than having fresh.gen.ts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests