From 9f971dad0dfcd308a04c7c2b02e294f23e5d0689 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Tue, 11 May 2021 11:25:54 +0100 Subject: [PATCH 01/11] fix: move ipx middleware to module * add documentation for manual runtime configuration * add support for auto config with Nuxt 2.16+ and `.nuxtrc` --- docs/content/en/4.providers/ipx.md | 29 +++++++++++++++++++++++++ package.json | 1 + src/ipx.ts | 7 ------ src/module.ts | 35 ++++++++++++++++++++++-------- src/runtime/ipx.ts | 10 +++++++++ 5 files changed, 66 insertions(+), 16 deletions(-) delete mode 100644 src/ipx.ts create mode 100644 src/runtime/ipx.ts diff --git a/docs/content/en/4.providers/ipx.md b/docs/content/en/4.providers/ipx.md index ded524e2e..2d496f293 100644 --- a/docs/content/en/4.providers/ipx.md +++ b/docs/content/en/4.providers/ipx.md @@ -22,3 +22,32 @@ that are stored locally within your repo. ``` This will load the image in as `/static/logo.png` and apply the IPX optimizations if applicable. + + +### Using `ipx` in production + +**If you are using Nuxt 2.16+ this is done automatically for you.** + +However, if you are using Nuxt < 2.16, and you need to use the `ipx` provider at runtime, you will either need to add `@nuxt/image` to your _modules_ (instead of buildModules) or add the following code to your `nuxt.config`: + +```js +import path from 'path' +import { createIPX, createIPXMiddleware } from 'ipx' + +const ipx = createIPX({ + dir: path.join(__dirname, 'static'), + // https://image.nuxtjs.org/api/options#domains + domains: [], + // Any options you need to pass to sharp + sharp: {} +}) + +export default { + serverMiddleware: [ + { + path: '/_ipx', + handler: createIPXMiddleware(ipx) + } + ] +} +``` diff --git a/package.json b/package.json index 0981ec25a..8661631e1 100755 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "lru-cache": "^6.0.0", "node-fetch": "^2.6.1", "p-limit": "^3.1.0", + "rc9": "^1.2.0", "requrl": "^3.0.2", "ufo": "^0.7.2", "upath": "^2.0.1" diff --git a/src/ipx.ts b/src/ipx.ts deleted file mode 100644 index f7a643637..000000000 --- a/src/ipx.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { IPXOptions } from 'ipx' - -export function createIPXMiddleware (ipxOptions: IPXOptions) { - const { createIPX, createIPXMiddleware } = require('ipx') as typeof import('ipx') - const ipx = createIPX(ipxOptions) - return createIPXMiddleware(ipx) -} diff --git a/src/module.ts b/src/module.ts index 33c167785..6625be47c 100644 --- a/src/module.ts +++ b/src/module.ts @@ -1,16 +1,17 @@ import { resolve } from 'upath' +import { update as updaterc } from 'rc9' import type { Module } from '@nuxt/types' import defu from 'defu' +import { copyFile, mkdirp } from 'fs-extra' import { setupStaticGeneration } from './generate' -import { createIPXMiddleware } from './ipx' import { resolveProviders, detectProvider } from './provider' import type { ModuleOptions, CreateImageOptions } from './types' import { pick, pkg } from './utils' const imageModule: Module = async function imageModule (moduleOptions) { - const { nuxt, addPlugin, addServerMiddleware } = this + const { nuxt, addPlugin, addModule } = this const defaults: ModuleOptions = { staticFilename: '[publicPath]/image/[hash][ext]', @@ -75,13 +76,29 @@ const imageModule: Module = async function imageModule (moduleOpt } }) - addServerMiddleware({ - path: '/_ipx', - handle: createIPXMiddleware({ - dir: options.dir, - domains: options.domains, - sharp: options.sharp - }) + const ipxModuleDev = resolve(runtimeDir, 'ipx.js') + const ipxOptions = { + dir: options.dir, + domains: options.domains, + sharp: options.sharp + } + + addModule([ipxModuleDev, ipxOptions]) + + // In production, add IPX module to nuxt config for Nuxt 2.16+ + nuxt.hook('build:done', async () => { + if (nuxt.options.dev) { + return + } + + const distDir = resolve(this.options.buildDir, 'dist') + const apiDir = resolve(distDir, 'server', 'modules') + const apiModuleProd = resolve(apiDir, 'ipx.js') + + await mkdirp(apiDir) + await copyFile(ipxModuleDev, apiModuleProd) + + updaterc({ modules: [[apiModuleProd, ipxOptions]] }, { dir: distDir, name: '.nuxtrc' }) }) nuxt.options.build.loaders = defu({ diff --git a/src/runtime/ipx.ts b/src/runtime/ipx.ts new file mode 100644 index 000000000..aeba59160 --- /dev/null +++ b/src/runtime/ipx.ts @@ -0,0 +1,10 @@ +import type { Module } from '@nuxt/types/config/module' +import { createIPX, createIPXMiddleware, IPXOptions } from 'ipx' + +export default > function ipxModule (options) { + const ipx = createIPX(options) + this.addServerMiddleware({ + path: '/_ipx', + handle: createIPXMiddleware(ipx) + }) +} From a91483fafe8092fbff8b90f49e7b8763edb64304 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Tue, 11 May 2021 12:00:23 +0100 Subject: [PATCH 02/11] perf: don't add ipx middleware if ipx provider isn't used --- src/module.ts | 44 +++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/src/module.ts b/src/module.ts index 6625be47c..cab600224 100644 --- a/src/module.ts +++ b/src/module.ts @@ -76,30 +76,36 @@ const imageModule: Module = async function imageModule (moduleOpt } }) - const ipxModuleDev = resolve(runtimeDir, 'ipx.js') - const ipxOptions = { - dir: options.dir, - domains: options.domains, - sharp: options.sharp - } + // Only add IPX server middleware if the static/ipx provider is used + if ( + ['static', 'ipx'].includes(options.provider) || + ['static', 'ipx'].some(provider => Object.keys(options.providers).includes(provider)) + ) { + const ipxModuleDev = resolve(runtimeDir, 'ipx.js') + const ipxOptions = { + dir: options.dir, + domains: options.domains, + sharp: options.sharp + } - addModule([ipxModuleDev, ipxOptions]) + addModule([ipxModuleDev, ipxOptions]) - // In production, add IPX module to nuxt config for Nuxt 2.16+ - nuxt.hook('build:done', async () => { - if (nuxt.options.dev) { - return - } + // In production, add IPX module to .nuxtrc (used in Nuxt 2.16+) + nuxt.hook('build:done', async () => { + if (nuxt.options.dev) { + return + } - const distDir = resolve(this.options.buildDir, 'dist') - const apiDir = resolve(distDir, 'server', 'modules') - const apiModuleProd = resolve(apiDir, 'ipx.js') + const distDir = resolve(this.options.buildDir, 'dist') + const apiDir = resolve(distDir, 'server', 'modules') + const apiModuleProd = resolve(apiDir, 'ipx.js') - await mkdirp(apiDir) - await copyFile(ipxModuleDev, apiModuleProd) + await mkdirp(apiDir) + await copyFile(ipxModuleDev, apiModuleProd) - updaterc({ modules: [[apiModuleProd, ipxOptions]] }, { dir: distDir, name: '.nuxtrc' }) - }) + updaterc({ modules: [[apiModuleProd, ipxOptions]] }, { dir: distDir, name: '.nuxtrc' }) + }) + } nuxt.options.build.loaders = defu({ vue: { transformAssetUrls: { 'nuxt-img': 'src', 'nuxt-picture': 'src' } } From b329301bc834449216bf87abc4673730747a56fe Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Tue, 11 May 2021 14:00:05 +0100 Subject: [PATCH 03/11] fix: add serverMiddleware directly for nitro compat * use relative paths so not tied to build machine --- src/module.ts | 30 ++++++++++++++++++++---------- src/runtime/ipx.ts | 13 ++++--------- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/src/module.ts b/src/module.ts index cab600224..932682c73 100644 --- a/src/module.ts +++ b/src/module.ts @@ -1,17 +1,17 @@ -import { resolve } from 'upath' +import { relative, resolve } from 'upath' import { update as updaterc } from 'rc9' import type { Module } from '@nuxt/types' import defu from 'defu' -import { copyFile, mkdirp } from 'fs-extra' +import { mkdirp, readFile, writeFile } from 'fs-extra' import { setupStaticGeneration } from './generate' import { resolveProviders, detectProvider } from './provider' import type { ModuleOptions, CreateImageOptions } from './types' import { pick, pkg } from './utils' const imageModule: Module = async function imageModule (moduleOptions) { - const { nuxt, addPlugin, addModule } = this + const { nuxt, addPlugin, addServerMiddleware } = this const defaults: ModuleOptions = { staticFilename: '[publicPath]/image/[hash][ext]', @@ -81,14 +81,22 @@ const imageModule: Module = async function imageModule (moduleOpt ['static', 'ipx'].includes(options.provider) || ['static', 'ipx'].some(provider => Object.keys(options.providers).includes(provider)) ) { - const ipxModuleDev = resolve(runtimeDir, 'ipx.js') + const rootDir = this.options.rootDir const ipxOptions = { - dir: options.dir, + dir: relative(rootDir, options.dir), domains: options.domains, sharp: options.sharp } - addModule([ipxModuleDev, ipxOptions]) + // In development, add IPX middleware directly + + const { createIPX, createIPXMiddleware } = await import('ipx') + + const ipx = createIPX(ipxOptions) + addServerMiddleware({ + path: '/_ipx', + handle: createIPXMiddleware(ipx) + }) // In production, add IPX module to .nuxtrc (used in Nuxt 2.16+) nuxt.hook('build:done', async () => { @@ -96,14 +104,16 @@ const imageModule: Module = async function imageModule (moduleOpt return } + const handler = await readFile(resolve(runtimeDir, 'ipx.js'), 'utf-8') const distDir = resolve(this.options.buildDir, 'dist') - const apiDir = resolve(distDir, 'server', 'modules') - const apiModuleProd = resolve(apiDir, 'ipx.js') + const apiDir = resolve(distDir, 'api') + const apiFile = resolve(apiDir, 'ipx.js') + const relativeApiFile = '~/' + relative(rootDir, apiFile) await mkdirp(apiDir) - await copyFile(ipxModuleDev, apiModuleProd) + await writeFile(apiFile, handler.replace(/.__IPX_OPTIONS__./, JSON.stringify(ipxOptions))) - updaterc({ modules: [[apiModuleProd, ipxOptions]] }, { dir: distDir, name: '.nuxtrc' }) + updaterc({ serverMiddleware: [{ path: '/_ipx', handler: relativeApiFile }] }, { dir: distDir, name: '.nuxtrc' }) }) } diff --git a/src/runtime/ipx.ts b/src/runtime/ipx.ts index aeba59160..e73568c98 100644 --- a/src/runtime/ipx.ts +++ b/src/runtime/ipx.ts @@ -1,10 +1,5 @@ -import type { Module } from '@nuxt/types/config/module' -import { createIPX, createIPXMiddleware, IPXOptions } from 'ipx' +import { createIPX, createIPXMiddleware } from 'ipx' -export default > function ipxModule (options) { - const ipx = createIPX(options) - this.addServerMiddleware({ - path: '/_ipx', - handle: createIPXMiddleware(ipx) - }) -} +const ipx = createIPX('__IPX_OPTIONS__') + +export default createIPXMiddleware(ipx) From 93cca313d0e2bc9969f2eadc569670cbbcaf8ff0 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Tue, 11 May 2021 16:39:11 +0100 Subject: [PATCH 04/11] feat: warn if provider is `ipx` and Nuxt < 2.16 --- package.json | 2 ++ src/module.ts | 31 +++++++++++++++++++++---------- yarn.lock | 5 +++++ 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 8661631e1..494813675 100755 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "p-limit": "^3.1.0", "rc9": "^1.2.0", "requrl": "^3.0.2", + "semver": "^7.3.5", "ufo": "^0.7.2", "upath": "^2.0.1" }, @@ -52,6 +53,7 @@ "@types/jest": "latest", "@types/lru-cache": "latest", "@types/node-fetch": "latest", + "@types/semver": "^7.3.5", "@vue/test-utils": "latest", "babel-core": "^7.0.0-bridge.0", "babel-eslint": "latest", diff --git a/src/module.ts b/src/module.ts index 932682c73..30e1d6df0 100644 --- a/src/module.ts +++ b/src/module.ts @@ -5,6 +5,8 @@ import type { Module } from '@nuxt/types' import defu from 'defu' import { mkdirp, readFile, writeFile } from 'fs-extra' +import { lt } from 'semver' + import { setupStaticGeneration } from './generate' import { resolveProviders, detectProvider } from './provider' import type { ModuleOptions, CreateImageOptions } from './types' @@ -78,10 +80,11 @@ const imageModule: Module = async function imageModule (moduleOpt // Only add IPX server middleware if the static/ipx provider is used if ( - ['static', 'ipx'].includes(options.provider) || - ['static', 'ipx'].some(provider => Object.keys(options.providers).includes(provider)) + (options.provider === 'static' && nuxt.options.dev) || + options.provider === 'ipx' || + Object.keys(options.providers).includes('ipx') ) { - const rootDir = this.options.rootDir + const rootDir = nuxt.options.rootDir const ipxOptions = { dir: relative(rootDir, options.dir), domains: options.domains, @@ -90,13 +93,21 @@ const imageModule: Module = async function imageModule (moduleOpt // In development, add IPX middleware directly - const { createIPX, createIPXMiddleware } = await import('ipx') + const hasUserProvidedMiddleware = !!nuxt.options.serverMiddleware.find(mw => mw.path && mw.path.startsWith('/_ipx')) - const ipx = createIPX(ipxOptions) - addServerMiddleware({ - path: '/_ipx', - handle: createIPXMiddleware(ipx) - }) + if (!hasUserProvidedMiddleware) { + const { createIPX, createIPXMiddleware } = await import('ipx') + + const ipx = createIPX(ipxOptions) + addServerMiddleware({ + path: '/_ipx', + handle: createIPXMiddleware(ipx) + }) + } + + if (nuxt.options.dev && options.provider === 'ipx' && !hasUserProvidedMiddleware && lt(nuxt.constructor.version, '2.16.0')) { + console.warn('If you would like to use the `ipx` provider at runtime, make sure to follow the instructions at https://image.nuxtjs.org/providers/ipx.') + } // In production, add IPX module to .nuxtrc (used in Nuxt 2.16+) nuxt.hook('build:done', async () => { @@ -105,7 +116,7 @@ const imageModule: Module = async function imageModule (moduleOpt } const handler = await readFile(resolve(runtimeDir, 'ipx.js'), 'utf-8') - const distDir = resolve(this.options.buildDir, 'dist') + const distDir = resolve(nuxt.options.buildDir, 'dist') const apiDir = resolve(distDir, 'api') const apiFile = resolve(apiDir, 'ipx.js') const relativeApiFile = '~/' + relative(rootDir, apiFile) diff --git a/yarn.lock b/yarn.lock index 4ffff2db1..c57961ab2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2024,6 +2024,11 @@ dependencies: "@types/node" "*" +"@types/semver@^7.3.5": + version "7.3.5" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.5.tgz#74deebbbcb1e86634dbf10a5b5e8798626f5a597" + integrity sha512-iotVxtCCsPLRAvxMFFgxL8HD2l4mAZ2Oin7/VJ2ooWO0VOK4EGOGmZWZn1uCq7RofR3I/1IOSjCHlFT71eVK0Q== + "@types/serve-static@*", "@types/serve-static@^1.13.9": version "1.13.9" resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.9.tgz#aacf28a85a05ee29a11fb7c3ead935ac56f33e4e" From 85b0c9dc2ec9fc827af4d13e24bc3f406c5b82ed Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Tue, 11 May 2021 17:32:00 +0100 Subject: [PATCH 05/11] fix: add type for smw --- src/module.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/module.ts b/src/module.ts index 30e1d6df0..4bdaf6b58 100644 --- a/src/module.ts +++ b/src/module.ts @@ -93,7 +93,7 @@ const imageModule: Module = async function imageModule (moduleOpt // In development, add IPX middleware directly - const hasUserProvidedMiddleware = !!nuxt.options.serverMiddleware.find(mw => mw.path && mw.path.startsWith('/_ipx')) + const hasUserProvidedMiddleware = !!nuxt.options.serverMiddleware.find((mw: { path: string }) => mw.path && mw.path.startsWith('/_ipx')) if (!hasUserProvidedMiddleware) { const { createIPX, createIPXMiddleware } = await import('ipx') From 3de913000ca58d1e165fd41827e711b005c72d2f Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Tue, 11 May 2021 17:41:25 +0100 Subject: [PATCH 06/11] fix: use correct alias --- src/module.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/module.ts b/src/module.ts index 4bdaf6b58..870aa7240 100644 --- a/src/module.ts +++ b/src/module.ts @@ -119,7 +119,7 @@ const imageModule: Module = async function imageModule (moduleOpt const distDir = resolve(nuxt.options.buildDir, 'dist') const apiDir = resolve(distDir, 'api') const apiFile = resolve(apiDir, 'ipx.js') - const relativeApiFile = '~/' + relative(rootDir, apiFile) + const relativeApiFile = '~~/' + relative(rootDir, apiFile) await mkdirp(apiDir) await writeFile(apiFile, handler.replace(/.__IPX_OPTIONS__./, JSON.stringify(ipxOptions))) From 5e9d8489442e5b30bb608592f24208025f68dbeb Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Tue, 11 May 2021 17:57:23 +0100 Subject: [PATCH 07/11] docs: update `ipx` dependency instruction --- docs/content/en/4.providers/ipx.md | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/docs/content/en/4.providers/ipx.md b/docs/content/en/4.providers/ipx.md index 2d496f293..cd6065a29 100644 --- a/docs/content/en/4.providers/ipx.md +++ b/docs/content/en/4.providers/ipx.md @@ -26,11 +26,34 @@ This will load the image in as `/static/logo.png` and apply the IPX optimization ### Using `ipx` in production +#### Add `ipx` dependency + +You'll need to ensure that `ipx` is in your production dependencies. + + + + +```bash +yarn add ipx +``` + + + + +```bash +npm install ipx +``` + + + + +#### Add `serverMiddleware` handler + **If you are using Nuxt 2.16+ this is done automatically for you.** However, if you are using Nuxt < 2.16, and you need to use the `ipx` provider at runtime, you will either need to add `@nuxt/image` to your _modules_ (instead of buildModules) or add the following code to your `nuxt.config`: -```js +```js [nuxt.config.js] import path from 'path' import { createIPX, createIPXMiddleware } from 'ipx' From 7842c0a7836364ba7aead3ff44ca8f354ed0efb1 Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Thu, 27 May 2021 16:11:44 +0200 Subject: [PATCH 08/11] update nuxt.config.js --- playground/nuxt.config.ts | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/playground/nuxt.config.ts b/playground/nuxt.config.ts index acfe1532b..2b5901e29 100644 --- a/playground/nuxt.config.ts +++ b/playground/nuxt.config.ts @@ -3,21 +3,12 @@ import type { } from '../src/types' export default { components: true, - // target: 'static', + target: 'static', head: { meta: [ { name: 'viewport', content: 'width=device-width, initial-scale=1' } ] }, - // serverMiddleware: { - // '/_ipx': () => { - // // - // } - // }, - modules: [ - // '@nuxt/image' - // '../src/module.ts' - ], buildModules: [ '../src/module.ts', '@nuxt/typescript-build' From 8dde9c00908fc02b5184e3207b993140f1ae40de Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Thu, 27 May 2021 16:12:19 +0200 Subject: [PATCH 09/11] fix lint errors --- src/runtime/components/nuxt-img.vue | 2 +- src/runtime/components/nuxt-picture.vue | 2 +- src/runtime/image.ts | 2 +- src/runtime/providers/prismic.ts | 2 +- test/unit/image.test.ts | 2 +- test/unit/picture.test.ts | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/runtime/components/nuxt-img.vue b/src/runtime/components/nuxt-img.vue index c86184dda..eadb6c00c 100644 --- a/src/runtime/components/nuxt-img.vue +++ b/src/runtime/components/nuxt-img.vue @@ -7,9 +7,9 @@