nuxt / image

Plug-and-play image optimization for Nuxt applications.
https://image.nuxt.com
MIT License
1.35k stars 271 forks source link

Skipping sharp dependency? #379

Closed PatrickHeneise closed 1 year ago

PatrickHeneise commented 3 years ago

Hi there. When using external image optimization providers in a production environment, sharp is obsolete, correct? Is there a possibility to skip the installation? The binaries are causing a bit of a headache between various environments.

pi0 commented 3 years ago

Thanks for suggesting this. Does making sharp an optionalDependency makes sense to you?

PatrickHeneise commented 3 years ago

Yeah, ipx/sharp as optional dependency for local/development and opting out on production would be great. If you point me in the direction on how this can be implemented, I can try to provide a PR. I'm not sure how to make dependencies work with conditions.

pi0 commented 3 years ago

We need to move ipx from dependencies to optionalDependencies (perfection: in ipxSetup ensure dependency is installed without any errors and warn if not)

Also in your project, you can move @nuxt/image from dependencies to devDependencies this way it won't be installed in production.

PatrickHeneise commented 3 years ago

Thanks. I'll open a PR soon.

PatrickHeneise commented 3 years ago

Unfortunately this didn't fix the issue. As we're throwing an Error, the build fails when packages are installed with -no-optional

Step #1:  ERROR  [@nuxt/image] ipx is an optional dependency for local image optimization and is not properly installed. Please try npm install or yarn install again.
Step #1: 
Step #1: 
Step #1:  FATAL  Error: Cannot find module 'ipx'
pi0 commented 3 years ago

@PatrickHeneise Would you please share nuxt.config? This is odd that ipx provider is enabled since for vercel deployments, it has to auto switch to vercel.

PatrickHeneise commented 3 years ago

We're not using Vercel.

image: {
    domains: ['https://XYZ'],
    provider: 'cloudflare',
    providers: {
      cloudflare: {
        provider: '~/providers/cloudflareImageProvider',
        options: {
          baseURL: 'https://images.XYZ.workers.dev'
        }
      }
    }
  },

The error throws straight after nuxt build

pi0 commented 3 years ago

Thanks! I could reproduce and tracked down issue to resolveProvides that is always enabling static (which uses IPX). Looking for a fix :)

altryne commented 2 years ago

Does this effectively make the whole module non-working? Or is there a fix that will prevent the nuxt-image from trying to load ipx (which is not needed) My nuxt config is using this configuration per docs:


    provider: 'storyblok',
    storyblok: {
      baseURL: 'https://a.storyblok.com'
    },
    screens: {
      xs: 320,
      sm: 640,
      md: 768,
      lg: 1024,
      xl: 1280,
      xxl: 1536,
      '2xl': 1536
    },
  },```
and I'm getting the `ipx is an optional dependency for local image optimization` error as well
mittci commented 2 years ago

I also get that without stroyblock and other shit, on localhost

[@nuxt/image] ipx is an optional dependency for local image optimization and is not installed.

When I make en error in ohmyfetch from node_modules

  const $fetch = function $fetch2(request, opts) {
    console.log('10> ', request, opts)
    return $fetchRaw(request, opts).then((r) => r._data);
  };
  $fetch.raw = $fetchRaw;
  $fetch.create = (defaultOptions = {}) => createFetch({
    ...globalOptions,
    defaults: {
      ...globalOptions.defaults,
      ...defaultOptions
    }
  });
  console.log('11> ', { //// <------------------------ ADD THAT HERE
    ...globalOptions,
    defaults: {
      ...globalOptions.defaults,
      ...defaultOptions
    }
  })

  return $fetch;
}

but what image have ohmyfetch with fetch? image module use fetch to load make direct calls?

brunoenribeiro commented 2 years ago

Hi there. I'm getting this error too.

My env:

- Node 16.13.1
- Nuxt 2.15.7
- Yarn 1.22.15
- @nuxt/image ^0.7.1

I've installed @nuxt/image by running yarn add --dev --ignore-optional @nuxt/image, as pointed on https://github.com/nuxt/image/pull/380.

When I run yarn generate (alias for nuxt generate) I get this error:

yarn run v1.22.15
$ nuxt generate
ℹ Merging Tailwind config from ~/tailwind.config.js

 ERROR  [@nuxt/image] ipx is an optional dependency for local image optimization and is not properly installed. Please try npm install or yarn install again.

 FATAL  TypeError: Invalid host defined options

  at node_modules/@nuxt/image/dist/module.cjs:112:13
  at async Object.ipxSetup [as setup] (node_modules/@nuxt/image/dist/module.cjs:110:48)
  at async ModuleContainer.imageModule2 (node_modules/@nuxt/image/dist/module.cjs:262:7)
  at async ModuleContainer.addModule (node_modules/@nuxt/core/dist/core.js:239:20)

   ╭────────────────────────────────────────────────────╮
   │                                                    │
   │   ✖ Nuxt Fatal Error                               │
   │                                                    │
   │   Error: TypeError: Invalid host defined options   │
   │                                                    │
   ╰────────────────────────────────────────────────────╯

error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

My nuxt.config.js:

Note image property is set at the file's end. I get the same error with image: {}, and with no image property at all.

const fetch = require("node-fetch");

export default {
  head: {
    title: "Bored Teachers | Celebrating Educators Every Day.",
    htmlAttrs: {
      lang: "en"
    },
    meta: [
      { charset: "utf-8" },
      { name: "viewport", content: "width=device-width, initial-scale=1" },
      {
        hid: "description",
        name: "description",
        content: "Celebrating Educators Every Day."
      },
      // Twitter
      // Test on: https://cards-dev.twitter.com/validator
      {
        hid: "twitter:card",
        name: "twitter:card",
        content: "summary_large_image"
      },
      { hid: "twitter:site", name: "twitter:site", content: "@Bored_Teachers" },
      {
        hid: "twitter:url",
        name: "twitter:url",
        content: "https://boredteachers.com"
      },
      {
        hid: "twitter:title",
        name: "twitter:title",
        content: "Bored Teachers | Celebrating Educators Every Day."
      },
      {
        hid: "twitter:description",
        name: "twitter:description",
        content: "Celebrating Educators Every Day."
      },
      {
        hid: "twitter:image",
        name: "twitter:image",
        content:
          "https://new.boredteachers.com/wp-content/uploads/2021/06/BT-HI-RES-LOGO.png"
      },

      // Open Graph
      // Test on: https://developers.facebook.com/tools/debug/
      {
        hid: "og:site_name",
        property: "og:site_name",
        content: "Bored Teachers"
      },
      { hid: "og:type", property: "og:type", content: "website" },
      {
        hid: "og:url",
        property: "og:url",
        content: "https://boredteachers.com"
      },
      {
        hid: "og:title",
        property: "og:title",
        content: "Bored Teachers | Celebrating Educators Every Day."
      },
      {
        hid: "og:description",
        property: "og:description",
        content: "Celebrating Educators Every Day."
      },
      {
        hid: "og:image",
        property: "og:image",
        content:
          "https://new.boredteachers.com/wp-content/uploads/2021/06/BT-HI-RES-LOGO.png"
      },
      {
        hid: "og:image:secure_url",
        property: "og:image:secure_url",
        content:
          "https://new.boredteachers.com/wp-content/uploads/2021/06/BT-HI-RES-LOGO.png"
      },
      {
        hid: "og:image:alt",
        property: "og:image:alt",
        content: "BoredTeachers"
      },
      {
        hid: "fb:app_id",
        property: "fb:app_id",
        content: "355427452233649"
      }
    ],
    link: [
      { rel: "icon", type: "image/x-icon", href: "/favicon.ico" },
      {
        rel: "stylesheet",
        href:
          "https://fonts.googleapis.com/css2?family=Noto+Sans:ital,wght@0,400;0,700;1,400;1,700&family=Open+Sans:ital,wght@0,300;0,400;0,600;0,700;0,800;1,300;1,400;1,600;1,700;1,800&display=swap"
      },
      {
        rel: "stylesheet",
        href:
          "https://fonts.googleapis.com/css2?family=Sora:wght@100;200;300;400;500;600;700;800&display=swap"
      },
      {
        rel: "stylesheet",
        href:
          "https://fonts.googleapis.com/css2?family=Poppins:wght@100;200;300;400;500;600;700;800;900&display=swap"
      },
      {
        rel: "stylesheet",
        href: "https://fonts.googleapis.com/css2?family=Fugaz+One&display=swap"
      }
    ],
    script: [
      // { src: '/privy.js' },
      // { src: 'https://widget.privy.com/assets/widget.js', async: true },
    ]
  },

  loading: {
    color: "#25D6B2",
    height: "5px"
  },

  target: "static",

  router: {
    middleware: ["redirects"]
  },

  env: {
    YT_API: "", // hidden
    BASE_URL: "https://boredteachers.com"
  },

  privateRuntimeConfig: {
    ytApi: process.env.YT_API
  },

  publicRuntimeConfig: {
    baseUrl: process.env.BASE_URL || "https://boredteachers.com"
  },

  generate: {
    fallback: "404.html",
    // crawler: false,
    interval: 300,
    // exclude: [
    //   '/memes'
    // ],
    routes: function() {
      const uri = "https://new.boredteachers.com/index.php?graphql=query";
      const query = `{
        categories(first: 9999) {
          edges {
            node {
              slug
            }
          }
        }
        giveaways(first: 9999) {
          edges {
            node {
              slug
            }
          }
        }
        listicles(first: 9999) {
          edges {
            node {
              slug
            }
          }
        }
        memes(first: 9999) {
          edges {
            node {
              slug
            }
          }
        }
        posts(first: 9999) {
          edges {
            node {
              slug
            }
          }
        }
        printables(first: 9999) {
          edges {
            node {
              slug
            }
          }
        }
        shoppingLists(first: 9999) {
          edges {
            node {
              slug
            }
          }
        }
        videos(first: 9999) {
          edges {
            node {
              slug
            }
          }
        }
      }`;
      return fetch(uri, {
        method: "POST",
        headers: {
          "Content-Type": "application/json"
        },
        body: JSON.stringify({ query })
      })
        .then(result => result.json())
        .then(result => {
          const { data } = result;
          let routes = [];
          let tempArr = [];

          /* categories */
          tempArr = data.categories.edges.map(category => {
            return {
              route: `/category/${category.node.slug}`
            };
          });

          routes = tempArr.concat(routes);

          /* giveaways */
          tempArr = data.giveaways.edges.map(giveaway => {
            return {
              route: `/giveaway/${giveaway.node.slug}`
            };
          });

          routes = tempArr.concat(routes);

          /* listicles */
          tempArr = data.listicles.edges.map(listicle => {
            return {
              route: `/post/${listicle.node.slug}`
            };
          });

          routes = tempArr.concat(routes);

          /* memes */
          tempArr = data.memes.edges.map(meme => {
            return {
              route: `/meme/${meme.node.slug}`
            };
          });

          routes = tempArr.concat(routes);

          /* posts */
          tempArr = data.posts.edges.map(post => {
            return {
              route: `/post/${post.node.slug}`
            };
          });

          routes = tempArr.concat(routes);

          /* printables */
          tempArr = data.printables.edges.map(printable => {
            return {
              route: `/printable/${printable.node.slug}`
            };
          });

          routes = tempArr.concat(routes);

          /* shoppingLists */
          tempArr = data.shoppingLists.edges.map(shoppingList => {
            return {
              route: `/post/${shoppingList.node.slug}`
            };
          });

          routes = tempArr.concat(routes);

          /* videos */
          tempArr = data.videos.edges.map(video => {
            return {
              route: `/video/${video.node.slug}`
            };
          });

          routes = tempArr.concat(routes);

          return routes;
        })
        .catch(error => {
          console.error(error);
        });
    }
  },

  css: ["~/assets/main.css"],

  plugins: [
    "~/plugins/scripts.client",
    "~/plugins/vue-js-modal.js",
    "~/plugins/tooltips.js",
    {
      src: "@/plugins/slider",
      mode: "client",
      ssr: false
    },
    {
      src: "~/plugins/global-gutenberg-components-loader.js"
    },
    "~/plugins/axios.js",
    "~/plugins/podcasts.js",
    "~/plugins/freestarAds.js",
    "~/plugins/preview.client.js"
  ],

  // Auto import components: https://go.nuxtjs.dev/config-components
  components: true,

  buildModules: [
    "@nuxtjs/tailwindcss",
    "@nuxtjs/google-analytics",
    "@nuxt/image"
  ],

  modules: [
    "@nuxtjs/axios",
    "@nuxtjs/apollo",
    "nuxt-helmet",
    "vue-social-sharing/nuxt",
    "@nuxtjs/proxy",
    "nuxt-facebook-pixel-module"
  ],

  facebook: {
    track: "PageView",
    pixelId: "712475899386953",
    autoPageView: true,
    disabled: false
  },

  apollo: {
    includeNodeModules: true,
    clientConfigs: {
      default: "@/apollo/client-configs/default.js"
    },
    errorHandler: "~/plugins/apollo-error-handler.js"
  },

  helmet: {},

  googleAnalytics: {
    id: "UA-76795799-1"
  },

  // Build Configuration: https://go.nuxtjs.dev/config-build
  build: {
    extend(config, { isClient, isServer }) {
      config.node = {
        fs: "empty",
        child_process: "empty",
        tls: "empty",
        net: "empty"
      };
      config.resolve.alias["vue$"] = "vue/dist/vue.esm.js";

      if (isServer) {
        config.externals = [
          {
            canvas: "util"
          }
        ];
      }
    }
  },

  image: {
    domains: ["new.boredteachers.com"]
  }
};
maxtechera commented 2 years ago

Hi, I'm having the same issue! Works on my local computer and in GithubAction but fails during Heroku deployment. Is there a way to disable this dependency or some reason it wouldn't be installed in Heroku?

EhsanJamshidi commented 1 year ago

I have the same issue on Namecheap hosting, any fix?

dissy123 commented 1 year ago

hey I'm getting a issue when running my project with yarn start : Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'ipx' imported from /app/.output/server/index.mjs

When i build it for production with:

yarn install --ignore-optional

using this version in devDependencies: "@nuxt/image-edge": "^1.0.0-28059208.2abef1b"

ipx doubles my build size and i don't need it, it would be nice to have a flag to disable it.

olegdon commented 12 months ago

i assume there is no solution so far? that's pity

pi0 commented 12 months ago

@olegdon ipx (because of sharp) is now an optional dependency and also you can set provider to null if you are deploying to a target that has not support. If you encounter any issues feel free to open a new issue describing better of your project requirements and error you get 👍🏼