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

SSG + Client script #48

Closed
berlysia opened this issue Feb 13, 2024 · 5 comments · Fixed by #70
Closed

SSG + Client script #48

berlysia opened this issue Feb 13, 2024 · 5 comments · Fixed by #70
Labels
enhancement New feature or request

Comments

@berlysia
Copy link
Contributor

berlysia commented Feb 13, 2024

What is the feature you are proposing?

(update) Does not work even with the steps written in the README , there is no client bundles!

Currently, @hono/vite-ssg and honox/vite/client combination cannot work in naive way.

  1. vite build --mode client && vite build results the latter build cleanups the former.
  2. Failed to vite build . A generated client script referenced by Script component cannot resolve by Vite.
current `vite.config.ts` file example
// vite.config.ts
import honox from "honox/vite";
import client from "honox/vite/client";
import { defineConfig } from "vite";
import ssg from "@hono/vite-ssg";

const entry = "./app/server.ts";

export default defineConfig(({ mode, command }) => {
  const plugins =
    mode === "client"
      ? [client()]
      : [honox(), ssg({ entry })];

  return {
    build: {
      rollupOptions: {
        input: ["./app/style.css"],
        output: {
          assetFileNames: "static/assets/[name].[ext]",
        },
      }
    },
    plugins,
  };
});
An error log of server build
Error: [vite]: Rollup failed to resolve import "/static/client-qvhPWvF1.js" from "(repo-root)/.hono/index.html".
This is most likely unintended because it can break your application at runtime.
If you do want to externalize this module explicitly add it to
`build.rollupOptions.external`
    at viteWarn (file://(repo-root)/node_modules/vite/dist/node/chunks/dep-94_H5fT6.js:67040:27)
    at onRollupWarning (file://(repo-root)/node_modules/vite/dist/node/chunks/dep-94_H5fT6.js:67068:9)
    at onwarn (file://(repo-root)/node_modules/vite/dist/node/chunks/dep-94_H5fT6.js:66777:13)
    at file://(repo-root)/node_modules/rollup/dist/es/shared/node-entry.js:17457:13
    at Object.logger [as onLog] (file://(repo-root)/node_modules/rollup/dist/es/shared/node-entry.js:19117:9)
    at ModuleLoader.handleInvalidResolvedId (file://(repo-root)/node_modules/rollup/dist/es/shared/node-entry.js:18061:26)
    at file://(repo-root)/node_modules/rollup/dist/es/shared/node-entry.js:18019:26
error: script "build" exited with code 1

Use of build.rollupOptions.external does not work. A generated script element will be removed in a generated HTML.

WORKAROUND for 1: Write a custom plugin that build.emptyOutDir set to false.
This derives from build.emptyOutDir set to true by @hono/vite-ssg .

WORKAROUND for 2: Use resolve.alias.

`vite.config.ts` file with workaround
// vite.config.ts
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import honox from "honox/vite";
import client from "honox/vite/client";
import { defineConfig } from "vite";
import ssg from "@hono/vite-ssg";

const entry = "./app/server.ts";

export default defineConfig(({ mode, command }) => {
  const plugins =
    mode === "client"
      ? [client()]
      : [
          honox({ entry }),
          ssg({ entry }),
          {
            config() {
              return { build: { emptyOutDir: false } };
            },
          },
        ];

  return {
    build: {
      rollupOptions: {
        input: ["./app/style.css"],
        output: {
          assetFileNames: "static/assets/[name].[ext]",
        },
      }
    },
    plugins,
    resolve: {
      alias: [
        {
          find: /^\/static\/(.*?)\.js/,
          replacement: resolve(
            // Node 18 support, for 20 or upper, `import.meta.dirname` also works
            dirname(fileURLToPath(import.meta.url)),
            "dist/static/$1.js"
          ),
        },
      ],
    },
  };
});
@berlysia berlysia added the enhancement New feature or request label Feb 13, 2024
@berlysia
Copy link
Contributor Author

berlysia commented Feb 14, 2024

(edited) I thought "it works!" but there is errors. I folded up what I was writing.

References which points to generated codes built by client build cause not found and fallback to index.html

And previous workarounds 1 and 2 are also have same problem, but there is no error because the files by client build is also in dist directory.

image

(folded)

(update) Just work with this configuration:

// vite.config.ts
import honox from "honox/vite";
import client from "honox/vite/client";
import { defineConfig } from "vite";
import ssg from "@hono/vite-ssg";

const entry = "./app/server.ts";

export default defineConfig(({ mode, command }) => {
  if (mode === "client") {
    return {
      plugins: [client()],
      build: {
        outDir: ".hono",
      },
    };
  }

  return {
    plugins: [honox(), ssg({ entry })],
  };
});

And fix manifest position for Script component.

// app/routes/_renderer.tsx
import { Style } from "hono/css";
import { jsxRenderer } from "hono/jsx-renderer";
import { Script } from "honox/server";
import { Manifest } from "vite";

// here
const manifestFile = Object.values(
  import.meta.glob<{ default: Manifest }>("/.hono/.vite/manifest.json", {
    eager: true,
  }),
)[0];
const manifest = manifestFile.default;

export default jsxRenderer(({ children, title }) => {
  return (
    <html lang="en">
      <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>{title}</title>
        <Script src="/app/client.ts" manifest={manifest} />
        <Style />
      </head>
      <body>{children}</body>
    </html>
  );
});

@yusukebe
Copy link
Member

@berlysia

Thanks! I'll check the details later!

@berlysia
Copy link
Contributor Author

berlysia commented Feb 15, 2024

Hi, looks like it's working this time, I believe.

in short: Just work with build client and toSSG into dist. For my usecase, @hono/vite-ssg is useless.

Sample repository is here (and deployed here).

◆If you have no time, you can skip to after <hr /> 😉◆

In my investigation, following config achieves the subject.

vite.config.ts
// vite.config.ts
import ssg from "@hono/vite-ssg";
import honox from "honox/vite";
import client from "honox/vite/client";
import { defineConfig } from "vite";
import devServer from "@hono/vite-dev-server";

const entry = "app/server.ts";

export default defineConfig(({ mode }) => {
  if (mode === "client") {
    // Client build
    return {
      plugins: [client()],
      build: {
        outDir: ".hono", // <--
        emptyOutDir: true,
      },
    };
  }

  {
    // SSG build
    return {
      plugins: [
        honox(),
        devServer({ entry }),
        ssg({ entry }),
      ],
    };
  }
});
build log
$ bun run build
$ vite build --mode client && vite build
vite v5.1.3 building for client...
✓ 23 modules transformed.
.hono/.vite/manifest.json         0.37 kB │ gzip: 0.19 kB
.hono/static/counter-CDwTLWFe.js  0.22 kB │ gzip: 0.20 kB
.hono/static/client-DnYkLGI4.js   6.54 kB │ gzip: 3.09 kB
✓ built in 99ms
GET  /
Build files into temp directory: .hono
Generated: .hono/index.html
vite v5.1.3 building for production...
✓ 6 modules transformed.
../dist/index.html                           0.51 kB │ gzip: 0.36 kB
../dist/assets/counter-CDwTLWFe-B0y06f1g.js  0.22 kB │ gzip: 0.20 kB
../dist/assets/index-D-Zp7aOz.js             8.22 kB │ gzip: 3.58 kB
✓ built in 63ms

Build takes following steps:

  1. vite build --mode client.
    • Clean up .hono directory and emit client bundles and manifest.json .
  2. vite build.
    1. @hono/vite-ssg generates pages by toSSG into .hono directory.
      • The pages have reference to the client bundles, thanks to manifest.json .
    2. Vite builds with pages generated in the former step in .hono directory.
    3. Emit final bundles and pages into dist directory.

toSSG -> vite build generates __vite__mapDeps.viteFileDeps twice . But there is no error because __vite__mapDeps.viteFileDeps is empty.
This is remote cause the weird errors described in #48 (comment) .

image


But, I think we don't need the step 2-ii, 2-iii . Just work with build client and toSSG into dist, right?
This fundamentally resolves the problem surrounding __vite__mapDeps.viteFileDeps .

So what I want is just like this:

  1. vite build with plugins: [client()]
  2. Run build.ts that contains https://github.com/honojs/vite-plugins/blob/55e3abfb8017c7ee9606458da665e3f09e0d428f/packages/ssg/src/ssg.ts#L34-L50 , with NODE_ENV=production

@yusukebe
Copy link
Member

Hi @berlysia

Sorry to be late!

But, I think we don't need the step 2-ii, 2-iii . Just work with build client and toSSG into dist, right?

Indeed, you are right. I've looked into your sample repository, that's simple and great! Perhaps, we can apply that way to the @hono/vite-ssg. Can you try to fix the @hono/vite-ssg to support it?

@berlysia
Copy link
Contributor Author

Great. I will try it.

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

Successfully merging a pull request may close this issue.

2 participants