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

feat(cloudflare-pages): generate _routes.json #121

Merged
merged 6 commits into from
May 6, 2024

Conversation

yusukebe
Copy link
Member

Fixes #110, #116

With this PR, the Vite plugin detects static files in the public directory and adds paths for serveStatic() in worker.js. And, this will emit _routes.json as a default.

The function that detects static files automatically is heavily inspired by @yudai-nkt 's https://github.com/yudai-nkt/awesome-hono/blob/main/vite-plugins/auto-exclude-static-routes.ts. I'll make @yudai-nkt a co-author.

Copy link

changeset-bot bot commented Apr 14, 2024

🦋 Changeset detected

Latest commit: 84b90c4

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@hono/vite-cloudflare-pages Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@yusukebe
Copy link
Member Author

Hey @yudai-nkt, @nakasyou, @ryuapp!

I've created this PR to resolve both #110 and #116. With this PR, static file paths will automatically be added to worker.js and _routes.json without explicitly specifying paths.

I think this is great. Can you review it?

Copy link
Contributor

@yudai-nkt yudai-nkt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

basically LGTM but left one minor nitpicking. thanks for tackling this!

packages/cloudflare-pages/src/cloudflare-pages.ts Outdated Show resolved Hide resolved
Copy link
Contributor

@ryuapp ryuapp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, @yusukebe
Thank you for nice PR!
I would like to leave just a few comments.

Comment on lines 61 to 65
staticRoutes = {
version: 1,
include: ['/*'],
exclude: staticPaths,
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This approach is fine for minimal projects, but we can't use no more than 100 include/exclude rules combined.
https://developers.cloudflare.com/pages/functions/routing/#limits

It will be required to optimized in the future.
Of course, we can set routesJson to false.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This approach is fine for minimal projects, but we can't use no more than 100 include/exclude rules combined.

You are right. One of the best practices is to create a directory like /static on the top level because it will be written as /static/*. We can write about it on README.

packages/cloudflare-pages/src/cloudflare-pages.ts Outdated Show resolved Hide resolved

return {
name: '@hono/vite-cloudflare-pages',
configResolved: async (resolvedConfig) => {
config = resolvedConfig
const paths = await readdir(config.publicDir, {
Copy link
Contributor

@ryuapp ryuapp Apr 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const paths = await readdir(config.publicDir, {
const paths = await readdir(config.build.outDir, {

I tried this plugin on local and found a bug.
JS and CSS files generated by vite are not included in exclude, so they can't be loaded.
We need to look at an output directory.

PS: This may not be enough and you may need to check after all files have been built.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! (in my prototype implementation mentioned above, Vite only generated server-side bundle so publicDir was enough)

IIUC, build.outDir will be empty on the configResolved hook of the initial vite build run, so I suppose readdir should be called in later hooks like writeBundle. Correct me if I'm wrong.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yudai-nkt
You are right!
Calling readdir on the writeBundle hook works by excluding _worker.js.

Codes I tried
  return {
    name: "@hono/vite-cloudflare-pages",
    configResolved: async (resolvedConfig) => {
      config = resolvedConfig;
-      const paths = await readdir(config.publicDir, {
-        withFileTypes: true
-      });
-      paths.forEach((p) => {
-        if (p.isDirectory()) {
-          staticPaths.push(`/${p.name}/*`);
-        } else if (p.name !== "_worker.js") {
-          staticPaths.push(`/${p.name}`);
-        }
      });
    },
    resolveId(id) {
      if (id === virtualEntryId) {
        return resolvedVirtualEntryId;
      }
    },
    async load(id) {
      if (id === resolvedVirtualEntryId) {
        return await getEntryContent({
          entry: options?.entry ? Array.isArray(options.entry) ? options.entry : [options.entry] : [...defaultOptions.entry],
          staticPaths
        });
      }
    },
    writeBundle: async () => {
+      const paths = await readdir(config.build.outDir, {
+        withFileTypes: true
+      });
+      paths.forEach((p) => {
+        if (p.isDirectory()) {
+          staticPaths.push(`/${p.name}/*`);
+        } else if (p.name !== "_worker.js") {
+          staticPaths.push(`/${p.name}`);
+        }
+      });
      staticRoutes = {
        version: 1,
        include: ["/*"],
        exclude: staticPaths
      };

However, HonoX generate .vite/manifest.json in out dir.
If it's static build, shouldn't it be output?
I think this needs to be fixed in HonoX.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As you said, you should specify config.build.outDir if you built the JS or CSS files before build server-side with this plugin. But, if you did not build some front files before executing this, the config.build.outDir does not exist, and it should be a different directory, e.g., public So, I've created the serveStaticDir option:

3d09e1c

The default value is config.build.outDir, but the user can specify it themselves. This is good. What do you think?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC, build.outDir will be empty on the configResolved hook of the initial vite build run, so I suppose readdir should be called in later hooks like writeBundle. Correct me if I'm wrong.

Indeed, it will be empty. But, this plugin for server-side. If we use it with client-side files, e.g., JS or CSS, we should build it in a separate build phase like the below:

vite build --mode client && vite build"

So, if we build client things first, config.build.outDir exists, and we don't have to write readdir in writeBundle. And if we call readdir in writeBundle, the test will fail because the staticPaths are empty when getEntryContent is executed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It appears that the css imported from 3d09e1c is no longer being loaded.
I'm trying with vite-plugin branch of the following repository:
https://github.com/ryuapp/honox-tailwind-template/tree/vite-plugin

"now"
{"version":1,"include":["/*"],"exclude":["/.vite/*","/humans.txt","/static/*"]}

"ideal"
{"version":1,"include":["/*"],"exclude":["/.vite/*","/humans.txt","/static/*","/style-BMTRAcdn.css"]}

As you said, you should specify config.build.outDir if you built the JS or CSS files before build server-side with this plugin. But, if you did not build some front files before executing this, the config.build.outDir does not exist, and it should be a different directory, e.g., public So, I've created the serveStaticDir option:

3d09e1c

The default value is config.build.outDir, but the user can specify it themselves. This is good. What do you think?

I'm sorry I didn't explain it well.
What I have a problem with is just .vite/manifest.json in an output directory.
However, I had misunderstood manifest.json; thus that was not an issue to discuss here...

Regarding the option, I don't think it' necessary.
This is because creating _routes.json from config.build.outDir is simpler and easier to understand.
If we want to handle _routes.json freely, I think that if we have _routes.json in public, we should give priority to that.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ryuapp

Thank you for your explanation! Understood well.

I've added the change: 84b90c4

  • Move readdir into writeBundle.
  • Remove serveStaticDir and routesJson options.
  • Use config.build.outDir.
  • Don't write serveStatic code on _worker.js.

The point is that it does not add serveStatic on _worker.js and should generate _routes.json. The serverStatic is not necessary if it has _routes.json, and it will be simpler rather than if both are set.

What do you think of this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yusukebe
Great! This PR works well in my project :)
I agree with all your opinions. I have no more questions.
Thank you very much for your great work!

@yusukebe yusukebe requested review from yudai-nkt and ryuapp May 4, 2024 15:10
@yusukebe
Copy link
Member Author

yusukebe commented May 4, 2024

@yudai-nkt @ryuapp

Sorry for being super late. I've updated it. Could you review this?

@yusukebe yusukebe changed the title feat(cloudflare-pages): detect static files automatically feat(cloudflare-pages): generate _routes.json May 6, 2024
Copy link
Contributor

@ryuapp ryuapp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM🎈

@yusukebe
Copy link
Member Author

yusukebe commented May 6, 2024

@ryuapp

Thank you! I'll merge this now. Let's go with it!

@yusukebe yusukebe merged commit cf3afbf into main May 6, 2024
2 checks passed
@yusukebe yusukebe deleted the feat/cloudflare-pages-auto-detect-staticfiles branch May 6, 2024 20:20
@github-actions github-actions bot mentioned this pull request May 6, 2024
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

Successfully merging this pull request may close these issues.

[@hono/vite-cloudflare-pages] Serve static assets using _routes.json
4 participants