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

@astrojs/image SSR support for non-Node runtimes #4109

Closed
1 task
sandervspl opened this issue Aug 1, 2022 · 19 comments
Closed
1 task

@astrojs/image SSR support for non-Node runtimes #4109

sandervspl opened this issue Aug 1, 2022 · 19 comments
Assignees
Labels
- P2: has workaround Bug, but has workaround (priority) feat: assets Related to the Assets feature (scope) feat: ssr Related to SSR (scope) pkg: image Related to the `@astrojs/image` package (scope)

Comments

@sandervspl
Copy link

sandervspl commented Aug 1, 2022

What version of astro are you using?

1.0.0-rc.3

Are you using an SSR adapter? If so, which one?

Cloudflare

What package manager are you using?

pnpm (same happens on yarn and npm)

What operating system are you using?

Mac

Describe the Bug

When you combine the @astrojs/image integration with output: 'server' the build fails. It's failing to build the project because the image component is using multiple Node.js modules (fs, crypto, path, URL)

The error:

[commonjs--resolver] Cannot bundle Node.js built-in "node:fs/promises" imported from "node_modules/@astrojs/image/dist/utils/images.js". Consider disabling ssr.noExternal or remove the built-in dependency.
 error   Cannot bundle Node.js built-in "node:fs/promises" imported from "node_modules/@astrojs/image/dist/utils/images.js". Consider disabling ssr.noExternal or remove the built-in dependency.

Link to Minimal Reproducible Example

This example below does not run on Stackblitz. You will need to download the project, install it and then run npm run build. You will get the error above.

https://stackblitz.com/edit/github-i5zqrv?on=stackblitz

Participation

  • I am willing to submit a pull request for this issue.
@github-actions github-actions bot added this to Needs Triage in 🐛 Bug Tracker Aug 1, 2022
@sandervspl sandervspl changed the title 🐛 BUG: Build fails when combining the images component with SSR 🐛 BUG: Build fails when combining @astrojs/image with SSR Aug 1, 2022
@FredKSchott FredKSchott added the - P4: important Violate documented behavior or significantly impacts performance (priority) label Aug 1, 2022
@bluwy bluwy self-assigned this Aug 16, 2022
@bluwy
Copy link
Member

bluwy commented Aug 16, 2022

This only happens for the Cloudflare integration as we set vite.ssr.target: 'webworker' for it. Checking @astro/images which injects route endpoints for serving images, I'm not sure if this can work without a significant refactor. Perhaps this is out of scope for the Cloudflare integration? cc @tony-sull

@bluwy bluwy added the feat: ssr Related to SSR (scope) label Aug 16, 2022
@FredKSchott FredKSchott assigned bluwy and unassigned bluwy Sep 12, 2022
@tony-sull
Copy link
Contributor

#4738 gets us a lot closer to supporting non-Node environments but we aren't 100% there yet

The main task left to remove node dependencies in the SSR bundle is to have the integration build a manifest file at build time that includes file metadata for all local image files

@matthewp matthewp assigned matthewp and unassigned tony-sull Nov 4, 2022
@matthewp
Copy link
Contributor

matthewp commented Nov 7, 2022

Started implementing in this branch: https://github.com/withastro/astro/compare/image-non-node?expand=1 Current blocker is whether the vendored squoosh code can be edited or not, as it heavily relies on Node at the moment.

@matthewp matthewp changed the title 🐛 BUG: Build fails when combining @astrojs/image with SSR @astrojs/image support for non-Node environments Nov 8, 2022
@matthewp matthewp changed the title @astrojs/image support for non-Node environments @astrojs/image SSR support for non-Node runtimes Nov 8, 2022
@matthewp matthewp added - P2: has workaround Bug, but has workaround (priority) and removed - P4: important Violate documented behavior or significantly impacts performance (priority) labels Nov 8, 2022
@matthewp matthewp assigned Princesseuh and unassigned matthewp Jan 12, 2023
@matthewp
Copy link
Contributor

@Princesseuh This is something you could possibly work on in the future. For now I think it's blocked because the Squoosh library, the only one that uses wasm, has been deprecated. So I'm not sure we can take on this issue yet.

There probably needs to be some research as to what our options are. Do other similar projects like next/image work outside of Node.js? If so what are they using for image optimizations?

@elevatebart
Copy link
Contributor

Hello Y'all

Cool stuff toward a wasm sharp is being done here hopefully this will solve a thing or two.

Hehe

@Princesseuh Princesseuh added the feat: assets Related to the Assets feature (scope) label Mar 21, 2023
@Princesseuh
Copy link
Member

Hello, just a status update about this: We're currently waiting for Sharp to add WASM support, since libsquoosh is deprecated.

We won't fix this in @astrojs/image, as it is heading towards maintenance mode in favour of the new astro:assets feature, but we are looking into fixing this for astro:assets. The later has a much better warning for this being unsupported, which should at least clear confusion for users.

@Princesseuh Princesseuh removed the pkg: image Related to the `@astrojs/image` package (scope) label Apr 9, 2023
@schummar
Copy link
Contributor

I use hybrid mode and need astro:assets only at build time. I have a few SSR components and endpoints but they do not use it, but with the Cloudflare adapter the build still fails as soon as experimental: { assets: true } is enabled.

Until proper non-Node support is ready, here is my little hack to allow this.

Create a custom image service at "src/image-service.ts":

import type { LocalImageService } from 'astro';
import { baseService } from 'astro/assets';

const service: LocalImageService = {
    ...baseService,

    async transform(inputBuffer, transform, serviceConfig) {
        // Purposefully obfuscate the import to prevent bundling => will only work at build time!
        const imageService = (
            await new Function(
                `return import('astro/assets/services/squoosh')`
            )()
        ).default;

        return await imageService.transform(
            inputBuffer,
            transform,
            serviceConfig
        );
    },
};

export default service;

and add it to the config. E.g.:

import cloudflare from '@astrojs/cloudflare';
import { defineConfig } from 'astro/config';

// https://astro.build/config
export default defineConfig({
    output: 'hybrid',
    adapter: cloudflare({
        mode: 'directory',
    }),
    experimental: {
        assets: true,
    },
    image: {
        service: {
            entrypoint: './src/image-service.ts',
        },
    },
});

Just keep in mind that this will throw runtime errors, if you try to use astro:assets in any server rendered component!

@natemoo-re natemoo-re added the pkg: image Related to the `@astrojs/image` package (scope) label Aug 8, 2023
@matthewp
Copy link
Contributor

Closing as we have removed @astrojs/image from the repo an won't be fixing bugs for it going forward. In the future the solution for non-Node runtimes with astro:assets will be to use a wasm version of Sharp (when that is released).

@Princesseuh
Copy link
Member

Just for info, we're aware this also affects astro:assets, however we consider this to be closer to a feature request than an issue. So we'd like for discussion to continue on our roadmap repo instead.

As Matthew said, the ultimate solution for this is for Sharp, the upstream dependency, to support both WASM and non-Node environments.

@dfbaskin
Copy link

While we wait for a non-Node (Cloudflare) option, temporarily maybe we can just do a pass-through image (passThroughImageService.ts):

import type { LocalImageService } from "astro";
import { baseService } from "astro/assets";

// Image service ("sharp") not supported by Cloudflare pages
// https://github.com/withastro/astro/issues/4109
const service: LocalImageService = {
  ...baseService,
  getURL({ src }) {
    const path = typeof src === "string" ? src : src.src;
    return path;
  },
  async transform(buffer, transform) {
    return {
      data: buffer,
      format: transform.format,
    };
  },
};

export default service;

and registration (astro.config.mjs):

import { defineConfig } from "astro/config";
import mdx from "@astrojs/mdx";
import sitemap from "@astrojs/sitemap";
import cloudflare from "@astrojs/cloudflare";

// https://astro.build/config
export default defineConfig({
  site: "https://...",
  integrations: [mdx(), sitemap()],
  output: "server",
  adapter: cloudflare(),
  image: {
    service: {
      entrypoint: "./src/services/passThroughImageService",
    },
  },
});

@Princesseuh
Copy link
Member

That already exists:

import { defineConfig, passthroughImageService } from "astro/config";

export default defineConfig({
 image: {
   service: passthroughImageService(),
 },
});

It's also automatically configured for you when using an adapter that doesn't support Sharp and Squoosh.

@dfbaskin
Copy link

dfbaskin commented Oct 11, 2023

Missed that, Thanks!

Update: Tried the above and it didn't work, I think because the default noop service still routes to the /_image endpoint instead of directly to the image file.

@cptchloroplast
Copy link

That already exists:

import { defineConfig, passthroughImageService } from "astro/config";

export default defineConfig({
 image: {
   service: passthroughImageService(),
 },
});

It's also automatically configured for you when using an adapter that doesn't support Sharp and Squoosh.

FYI - This does not automatically configure when using the Cloudflare adapter. You have to explicitly tell it to use the pass through service. The docs are not very helpful in this regard either

@Princesseuh
Copy link
Member

That already exists:

import { defineConfig, passthroughImageService } from "astro/config";

export default defineConfig({
 image: {
   service: passthroughImageService(),
 },
});

It's also automatically configured for you when using an adapter that doesn't support Sharp and Squoosh.

FYI - This does not automatically configure when using the Cloudflare adapter. You have to explicitly tell it to use the pass through service. The docs are not very helpful in this regard either

Your CloudFlare integration might be outdated, the latest version should do it for you

@bstro
Copy link

bstro commented Dec 6, 2023

I'm using the vercel serverless adapter and it doesn't appear to automatically configure the passthrough image service. Getting the telltale errors indicating that the adapter I'm using doesn't support astro:assets (i.e. dozens of errors like Could not resolve "fs")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
- P2: has workaround Bug, but has workaround (priority) feat: assets Related to the Assets feature (scope) feat: ssr Related to SSR (scope) pkg: image Related to the `@astrojs/image` package (scope)
Projects
No open projects
🐛 Bug Tracker
Needs Triage
Development

No branches or pull requests