Repository: nuxt/image
Pull request #851 (2023-06-06 12:53:24)

chore(deps): update devdependency @nuxt-themes/docus to ^1.12.1
[![Mend Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)

This PR contains the following updates:

| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| @&#8203;nuxt-themes/docus | [`^1.12.0` -> `^1.12.1`](https://renovatebot.com/diffs/npm/@nuxt-themes%2fdocus/1.12.0/1.12.1) | [![age](https://badges.renovateapi.com/packages/npm/@nuxt-themes%2fdocus/1.12.1/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/npm/@nuxt-themes%2fdocus/1.12.1/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/npm/@nuxt-themes%2fdocus/1.12.1/compatibility-slim/1.12.0)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/npm/@nuxt-themes%2fdocus/1.12.1/confidence-slim/1.12.0)](https://docs.renovatebot.com/merge-confidence/) |

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://www.mend.io/free-developer-tools/renovate/). View repository job log [here](https://app.renovatebot.com/dashboard#github/nuxt/image).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNS4xMTAuMCIsInVwZGF0ZWRJblZlciI6IjM1LjExMC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiJ9-->


netlify[bot]: ### <span aria-hidden="true">👷</span> Deploy Preview for *nuxt-image-v1* processing.


|  Name | Link |
|:-:|------------------------|
|<span aria-hidden="true">🔨</span> Latest commit | b5e223eacafaf90b6f20bd27d1049c18adc9916a |
|<span aria-hidden="true">🔍</span> Latest deploy log | https://app.netlify.com/sites/nuxt-image-v1/deploys/647f2c4705f0570008b7dc06 |

Patch:
diff --git a/docs/package.json b/docs/package.json
index 26d9fd6..1143eeb 100755
--- a/docs/package.json
+++ b/docs/package.json
@@ -6,7 +6,7 @@
     "generate": "nuxi generate"
   },
   "devDependencies": {
-    "@nuxt-themes/docus": "^1.12.0",
+    "@nuxt-themes/docus": "^1.12.1",
     "@nuxtjs/plausible": "latest",
     "nuxt": "^3.5.3"
   }
diff --git a/package.json b/package.json
index bfea54a..c93d400 100755
--- a/package.json
+++ b/package.json
@@ -33,6 +33,7 @@
     "node-fetch-native": "^1.1.1",
     "ohash": "^1.1.2",
     "pathe": "^1.1.1",
+    "std-env": "^3.3.3",
     "ufo": "^1.1.2"
   },
   "devDependencies": {
diff --git a/playground/nuxt.config.ts b/playground/nuxt.config.ts
index 5382f54..065beb2 100644
--- a/playground/nuxt.config.ts
+++ b/playground/nuxt.config.ts
@@ -11,6 +11,7 @@ export default defineNuxtConfig({
     screens: {
       750: 750
     },
+    none: {},
     alias: {
       unsplash: 'https://images.unsplash.com', // ipx
       blog: '/remote/nuxt-org/blog' // cloudinary
diff --git a/playground/providers.ts b/playground/providers.ts
index dbe5bd8..cc30b08 100644
--- a/playground/providers.ts
+++ b/playground/providers.ts
@@ -11,6 +11,40 @@ export interface Provider {
 }
 
 export const providers: Provider[] = [
+  // null provider (for non-node environments)
+  {
+    name: 'none',
+    samples: [
+      {
+        src: '/images/colors.jpg',
+        from: 'Jeremy Thomas',
+        width: 300,
+        height: 300,
+        link: 'https://unsplash.com/@jeremythomasphoto?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText'
+      },
+      {
+        src: '/images/everest.jpg',
+        from: 'Mount Everest Wikipedia page (alias)',
+        width: 300,
+        height: 300,
+        link: 'https://en.wikipedia.org/wiki/Mount_Everest'
+      },
+      {
+        src: '/images/tacos.svg',
+        from: 'Illustration from Icons8',
+        width: 300,
+        height: 300,
+        link: 'https://icons8.com/illustrations/illustration/abstract-1419'
+      },
+      {
+        src: '/unsplash/photo-1606112219348-204d7d8b94ee',
+        from: 'Photo by Omid Armin',
+        width: 300,
+        height: 300,
+        link: 'https://unsplash.com/@omidarmin?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText'
+      }
+    ]
+  },
   // IPX
   {
     name: 'ipx',
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index eb5410b..dc9608d 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -36,6 +36,9 @@ importers:
       pathe:
         specifier: ^1.1.1
         version: 1.1.1
+      std-env:
+        specifier: ^3.3.3
+        version: 3.3.3
       ufo:
         specifier: ^1.1.2
         version: 1.1.2
@@ -99,8 +102,8 @@ importers:
   docs:
     devDependencies:
       '@nuxt-themes/docus':
-        specifier: ^1.12.0
-        version: 1.12.0(nuxt@3.5.3)(postcss@8.4.24)(rollup@3.23.0)(vue@3.3.4)
+        specifier: ^1.12.1
+        version: 1.12.1(nuxt@3.5.3)(postcss@8.4.24)(rollup@3.23.0)(vue@3.3.4)
       '@nuxtjs/plausible':
         specifier: latest
         version: 0.2.1(rollup@3.23.0)
@@ -809,8 +812,8 @@ packages:
       '@nodelib/fs.scandir': 2.1.5
       fastq: 1.15.0
 
-  /@nuxt-themes/docus@1.12.0(nuxt@3.5.3)(postcss@8.4.24)(rollup@3.23.0)(vue@3.3.4):
-    resolution: {integrity: sha512-/YAlsKbyyjxIflFOIO3bKdM/QNsWLNRBXK11CTXod8zBlJe7lL/XqtNmbtqoNyQ+WumnkfVRA4UyKnu4W/Ya9w==}
+  /@nuxt-themes/docus@1.12.1(nuxt@3.5.3)(postcss@8.4.24)(rollup@3.23.0)(vue@3.3.4):
+    resolution: {integrity: sha512-exSHLVzhhIuT0FdJFLClXcPyKuvqHC6diAYS6OEtggu8TQzbj9Y9idewCtr/Rsv3UEYM4S6Xj5+dqi86WugHMg==}
     dependencies:
       '@nuxt-themes/elements': 0.9.4(postcss@8.4.24)(rollup@3.23.0)(vue@3.3.4)
       '@nuxt-themes/tokens': 1.9.1(postcss@8.4.24)(rollup@3.23.0)(vue@3.3.4)
diff --git a/src/ipx.ts b/src/ipx.ts
index 0112698..4b774f9 100644
--- a/src/ipx.ts
+++ b/src/ipx.ts
@@ -1,10 +1,12 @@
 import { relative, resolve } from 'pathe'
 import { eventHandler } from 'h3'
-import { useNuxt, createResolver } from '@nuxt/kit'
+import { useNuxt, createResolver, useNitro } from '@nuxt/kit'
+import type { NitroEventHandler, NitroDevEventHandler } from 'nitropack'
 
 import type { ProviderSetup, ImageProviders } from './types'
 
 export const ipxSetup: ProviderSetup = async (providerOptions, moduleOptions) => {
+  const nitro = useNitro()
   const nuxt = useNuxt()
 
   // Add IPX middleware unless nuxtrc or user added a custom middleware
@@ -28,20 +30,22 @@ export const ipxSetup: ProviderSetup = async (providerOptions, moduleOptions) =>
   if (!nuxt.options.dev) {
     // TODO: Avoid adding for non-Node.js environments with a warning
     const resolver = createResolver(import.meta.url)
-    nuxt.hook('nitro:init', (nitro) => {
-      // Use absolute path for prerenderer
-      // TODO: Workaround for prerender support
-      // https://github.com/nuxt/image/pull/784
-      nitro.options._config.runtimeConfig = nitro.options._config.runtimeConfig || {}
-      nitro.options._config.runtimeConfig.ipx = { ...ipxOptions }
-      // Use relative path for built app
-      ipxOptions.dir = relative(nitro.options.output.serverDir, nitro.options.output.publicDir)
-      nitro.options.runtimeConfig.ipx = ipxOptions
-    })
-    nuxt.options.serverHandlers.push({
+    // Use absolute path for prerenderer
+    // TODO: Workaround for prerender support
+    // https://github.com/nuxt/image/pull/784
+    nitro.options._config.runtimeConfig = nitro.options._config.runtimeConfig || {}
+    nitro.options._config.runtimeConfig.ipx = { ...ipxOptions }
+    // Use relative path for built app
+    ipxOptions.dir = relative(nitro.options.output.serverDir, nitro.options.output.publicDir)
+    nitro.options.runtimeConfig.ipx = ipxOptions
+
+    const handler: NitroEventHandler = {
       route: '/_ipx/**',
       handler: resolver.resolve('./runtime/ipx')
-    })
+    }
+    nitro.options.handlers.push(handler)
+    // TODO: Workaround for prerender support
+    nitro.options._config.handlers!.push(handler)
     return
   }
 
@@ -54,10 +58,13 @@ export const ipxSetup: ProviderSetup = async (providerOptions, moduleOptions) =>
     })
   const ipx = createIPX(ipxOptions)
   const middleware = createIPXMiddleware(ipx)
-  nuxt.options.devServerHandlers.push({
+  const devHandler: NitroDevEventHandler = {
     route: '/_ipx',
     handler: eventHandler(async (event) => {
       await middleware(event.node.req, event.node.res)
     })
-  })
+  }
+  nitro.options.devHandlers.push(devHandler)
+  // TODO: Workaround for prerender support
+  nitro.options._config.devHandlers!.push(devHandler)
 }
diff --git a/src/module.ts b/src/module.ts
index 13d0787..68c8f11 100644
--- a/src/module.ts
+++ b/src/module.ts
@@ -1,7 +1,7 @@
 import { parseURL, withLeadingSlash } from 'ufo'
 import { defineNuxtModule, addTemplate, addImports, createResolver, addComponent, addPlugin } from '@nuxt/kit'
 import { resolve } from 'pathe'
-import { resolveProviders, detectProvider } from './provider'
+import { resolveProviders, detectProvider, resolveProvider } from './provider'
 import type { ImageProviders, ImageOptions, InputProvider, CreateImageOptions } from './types'
 
 export interface ModuleOptions extends ImageProviders {
@@ -64,8 +64,10 @@ export default defineNuxtModule<ModuleOptions>({
     // Normalize alias to start with leading slash
     options.alias = Object.fromEntries(Object.entries(options.alias).map(e => [withLeadingSlash(e[0]), e[1]]))
 
-    options.provider = detectProvider(options.provider)
-    options[options.provider] = options[options.provider] || {}
+    options.provider = detectProvider(options.provider)!
+    if (options.provider) {
+      options[options.provider] = options[options.provider] || {}
+    }
 
     const imageOptions: Omit<CreateImageOptions, 'providers' | 'nuxt'> = pick(options, [
       'screens',
@@ -79,7 +81,7 @@ export default defineNuxtModule<ModuleOptions>({
 
     // Run setup
     for (const p of providers) {
-      if (typeof p.setup === 'function') {
+      if (typeof p.setup === 'function' && p.name !== 'ipx') {
         await p.setup(p, options, nuxt)
       }
     }
@@ -120,6 +122,23 @@ ${providers.map(p => `  ['${p.name}']: { provider: ${p.importName}, defaults: ${
       }
     })
 
+    nuxt.hook('nitro:init', async (nitro) => {
+      if (!options.provider || options.provider === 'ipx') {
+        imageOptions.provider = options.provider = nitro.options.node ? 'ipx' : 'none'
+        options[options.provider] = options[options.provider] || {}
+
+        if (options.provider === 'none') { return }
+
+        const p = await resolveProvider(nuxt, 'ipx', options.ipx as {})
+        if (!providers.some(p => p.name === 'ipx')) {
+          providers.push(p)
+        }
+        if (typeof p.setup === 'function') {
+          await p.setup(p, options, nuxt)
+        }
+      }
+    })
+
     // Add runtime plugin
     addPlugin({ src: resolver.resolve('./runtime/plugin') })
 
diff --git a/src/provider.ts b/src/provider.ts
index cfa655e..2cb8221 100644
--- a/src/provider.ts
+++ b/src/provider.ts
@@ -4,6 +4,7 @@ import type { Nuxt } from '@nuxt/schema'
 import type { NitroConfig } from 'nitropack'
 import { createResolver, resolvePath } from '@nuxt/kit'
 import { hash } from 'ohash'
+import { provider } from 'std-env'
 import type { InputProvider, ImageModuleProvider, ProviderSetup } from './types'
 import type { ModuleOptions } from './module'
 import { ipxSetup } from './ipx'
@@ -24,6 +25,7 @@ const BuiltInProviders = [
   'ipx',
   'layer0',
   'netlify',
+  'none',
   'prismic',
   'sanity',
   'storyblok',
@@ -108,9 +110,7 @@ export function detectProvider (userInput?: string) {
     return userInput
   }
 
-  if (process.env.VERCEL || process.env.VERCEL_ENV || process.env.NOW_BUILDER) {
+  if (provider === 'vercel') {
     return 'vercel'
   }
-
-  return 'ipx'
 }
diff --git a/src/runtime/providers/none.ts b/src/runtime/providers/none.ts
new file mode 100644
index 0000000..f0ed771
--- /dev/null
+++ b/src/runtime/providers/none.ts
@@ -0,0 +1,3 @@
+import type { ProviderGetImage } from '../../types'
+
+export const getImage: ProviderGetImage = url => ({ url })


Test patch:
diff --git a/test/e2e/__snapshots__/no-ssr.test.ts.snap b/test/e2e/__snapshots__/no-ssr.test.ts.snap
index 05ce120..4df2025 100644
--- a/test/e2e/__snapshots__/no-ssr.test.ts.snap
+++ b/test/e2e/__snapshots__/no-ssr.test.ts.snap
@@ -206,6 +206,24 @@ exports[`browser (ssr: false) > netlify should render images 2`] = `
 ]
 `;
 
+exports[`browser (ssr: false) > none should render images 1`] = `
+[
+  "/images/colors.jpg",
+  "/images/everest.jpg",
+  "/images/tacos.svg",
+  "https://images.unsplash.com/photo-1606112219348-204d7d8b94ee",
+]
+`;
+
+exports[`browser (ssr: false) > none should render images 2`] = `
+[
+  "/images/colors.jpg",
+  "/images/everest.jpg",
+  "/images/tacos.svg",
+  "https://images.unsplash.com/photo-1606112219348-204d7d8b94ee",
+]
+`;
+
 exports[`browser (ssr: false) > prismic should render images 1`] = `
 [
   "https://images.prismic.io/200629-sms-hoy/f596a543-d593-4296-9abd-3d3ac15f1e39_ray-hennessy-mpw37yXc_WQ-unsplash.jpg?auto=compress,format&w=600&h=900",
diff --git a/test/e2e/__snapshots__/ssr.test.ts.snap b/test/e2e/__snapshots__/ssr.test.ts.snap
index 69c5d5f..ac87b1f 100644
--- a/test/e2e/__snapshots__/ssr.test.ts.snap
+++ b/test/e2e/__snapshots__/ssr.test.ts.snap
@@ -206,6 +206,24 @@ exports[`browser (ssr: true) > netlify should render images 2`] = `
 ]
 `;
 
+exports[`browser (ssr: true) > none should render images 1`] = `
+[
+  "/images/colors.jpg",
+  "/images/everest.jpg",
+  "/images/tacos.svg",
+  "https://images.unsplash.com/photo-1606112219348-204d7d8b94ee",
+]
+`;
+
+exports[`browser (ssr: true) > none should render images 2`] = `
+[
+  "/images/colors.jpg",
+  "/images/everest.jpg",
+  "/images/tacos.svg",
+  "https://images.unsplash.com/photo-1606112219348-204d7d8b94ee",
+]
+`;
+
 exports[`browser (ssr: true) > prismic should render images 1`] = `
 [
   "https://images.prismic.io/200629-sms-hoy/f596a543-d593-4296-9abd-3d3ac15f1e39_ray-hennessy-mpw37yXc_WQ-unsplash.jpg?auto=compress,format&w=600&h=900",
diff --git a/test/providers.ts b/test/providers.ts
index 1eb819a..f7b368a 100644
--- a/test/providers.ts
+++ b/test/providers.ts
@@ -1,6 +1,7 @@
 export const images = [
   {
     args: ['/test.png', {}],
+    none: { url: '/test.png' },
     ipx: { url: '/_ipx/_/test.png' },
     cloudflare: { url: '/test.png' },
     cloudinary: { url: '/f_auto,q_auto/test' },
@@ -23,6 +24,7 @@ export const images = [
   },
   {
     args: ['/test.png', { width: 200 }],
+    none: { url: '/test.png' },
     ipx: { url: '/_ipx/w_200/test.png' },
     cloudflare: { url: '/cdn-cgi/image/w=200/test.png' },
     cloudinary: { url: '/f_auto,q_auto,w_200/test' },
@@ -45,6 +47,7 @@ export const images = [
   },
   {
     args: ['/test.png', { height: 200 }],
+    none: { url: '/test.png' },
     ipx: { url: '/_ipx/h_200/test.png' },
     cloudflare: { url: '/cdn-cgi/image/h=200/test.png' },
     cloudinary: { url: '/f_auto,q_auto,h_200/test' },
@@ -67,6 +70,7 @@ export const images = [
   },
   {
     args: ['/test.png', { width: 200, height: 200 }],
+    none: { url: '/test.png' },
     ipx: { url: '/_ipx/s_200x200/test.png' },
     cloudflare: { url: '/cdn-cgi/image/w=200,h=200/test.png' },
     cloudinary: { url: '/f_auto,q_auto,w_200,h_200/test' },
@@ -89,6 +93,7 @@ export const images = [
   },
   {
     args: ['/test.png', { width: 200, height: 200, fit: 'contain' }],
+    none: { url: '/test.png' },
     ipx: { url: '/_ipx/fit_contain&s_200x200/test.png' },
     cloudflare: { url: '/cdn-cgi/image/w=200,h=200,fit=contain/test.png' },
     cloudinary: { url: '/f_auto,q_auto,w_200,h_200,c_scale/test' },
@@ -111,6 +116,7 @@ export const images = [
   },
   {
     args: ['/test.png', { width: 200, height: 200, fit: 'contain', format: 'jpeg' }],
+    none: { url: '/test.png' },
     ipx: { url: '/_ipx/fit_contain&f_jpeg&s_200x200/test.png' },
     cloudflare: { url: '/cdn-cgi/image/w=200,h=200,fit=contain,f=jpeg/test.png' },
     cloudinary: { url: '/f_jpg,q_auto,w_200,h_200,c_scale/test' },
diff --git a/test/unit/providers.test.ts b/test/unit/providers.test.ts
index 18197aa..9a751bc 100644
--- a/test/unit/providers.test.ts
+++ b/test/unit/providers.test.ts
@@ -6,6 +6,7 @@ import { images } from '../providers'
 
 import { cleanDoubleSlashes } from '#image/utils'
 import * as ipx from '#image/providers/ipx'
+import * as none from '~/src/runtime/providers/none'
 import * as cloudflare from '#image/providers/cloudflare'
 import * as cloudinary from '#image/providers/cloudinary'
 import * as twicpics from '#image/providers/twicpics'
@@ -351,4 +352,15 @@ describe('Providers', () => {
       expect(generated).toMatchObject(image.vercel)
     }
   })
+
+  it('none', () => {
+    const providerOptions = {
+    }
+
+    for (const image of images) {
+      const [src, modifiers] = image.args
+      const generated = none.getImage(src, { modifiers, ...providerOptions }, emptyContext)
+      expect(generated).toMatchObject(image.none)
+    }
+  })
 })