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

fix: codemod next/image within monorepo #46047

Merged
merged 11 commits into from
Feb 18, 2023
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
images: {
loader: "imgix",
path: "https://example.com/"
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
images: {
loader: "cloudinary",
path: "https://example.com/"
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const normalizeSrc = (src) => src[0] === '/' ? src.slice(1) : src
export default function imgixLoader({ src, width, quality }) {
const url = new URL('https://example.com/' + normalizeSrc(src))
const params = url.searchParams
params.set('auto', params.getAll('auto').join(',') || 'format')
params.set('fit', params.get('fit') || 'max')
params.set('w', params.get('w') || width.toString())
if (quality) { params.set('q', quality.toString()) }
return url.href
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
images: {
loader: "custom",
loaderFile: "./imgix-loader.js"
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const normalizeSrc = (src) => src[0] === '/' ? src.slice(1) : src
export default function cloudinaryLoader({ src, width, quality }) {
const params = ['f_auto', 'c_limit', 'w_' + width, 'q_' + (quality || 'auto')]
const paramsString = params.join(',') + '/'
return 'https://example.com/' + paramsString + normalizeSrc(src)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
images: {
loader: "custom",
loaderFile: "./cloudinary-loader.js"
},
}
Original file line number Diff line number Diff line change
@@ -1,36 +1,46 @@
/* global jest */
jest.autoMockOff()
const Runner = require('jscodeshift/dist/Runner');
const { cp, mkdir, rm, readdir, readFile } = require('fs/promises')
const { cp, mkdir, mkdtemp, rm, readdir, readFile, stat } = require('fs/promises')
const { readdirSync } = require('fs')
const { tmpdir } = require('os')
const { join } = require('path')

const fixtureDir = join(__dirname, '..', '__testfixtures__', 'next-image-experimental-loader')
const transform = join(__dirname, '..', 'next-image-experimental.js')
const opts = { recursive: true }
const opts = { recursive: true, force: true }

async function toObj(dir) {
const obj = {}
const files = await readdir(dir)
for (const file of files) {
obj[file] = await readFile(join(dir, file), 'utf8')
const filePath = join(dir, file)
const s = await stat(filePath)
if (s.isDirectory()) {
obj[file] = await toObj(filePath)
} else {
obj[file] = await readFile(filePath, 'utf8')
}
}
return obj
}

it.each(readdirSync(fixtureDir))('should transform loader %s', async (loader) => {
const tmp = await mkdtemp(join(tmpdir(), `next-image-experimental-${loader}-`))
const originalCwd = process.cwd()
try {
await mkdir(join(fixtureDir, 'tmp'), opts)
await cp(join(fixtureDir, loader, 'input'), join(fixtureDir, 'tmp'), opts)
process.chdir(join(fixtureDir, 'tmp'))
await mkdir(tmp, opts)
await cp(join(fixtureDir, loader, 'input'), tmp, opts)
process.chdir(tmp)
const result = await Runner.run(transform, [`.`], {})
expect(result.error).toBe(0)
expect(
await toObj(join(fixtureDir, 'tmp'))
await toObj(tmp)
).toStrictEqual(
await toObj(join(fixtureDir, loader, 'output'))
)
} finally {
await rm(join(fixtureDir, 'tmp'), opts)
await rm(tmp, opts)
process.chdir(originalCwd)
}
})
33 changes: 22 additions & 11 deletions packages/next-codemod/transforms/next-image-experimental.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { join, parse } from 'path'
import { writeFileSync } from 'fs'
import type {
API,
Expand Down Expand Up @@ -148,7 +149,11 @@ function findAndReplaceProps(
})
}

function nextConfigTransformer(j: JSCodeshift, root: Collection) {
function nextConfigTransformer(
j: JSCodeshift,
root: Collection,
appDir: string
) {
let pathPrefix = ''
let loaderType = ''
root.find(j.ObjectExpression).forEach((o) => {
Expand Down Expand Up @@ -188,15 +193,20 @@ function nextConfigTransformer(j: JSCodeshift, root: Collection) {
return true
})
if (loaderType && pathPrefix) {
let filename = `./${loaderType}-loader.js`
const importSpecifier = `./${loaderType}-loader.js`
const filePath = join(appDir, importSpecifier)
properties.push(
j.property('init', j.identifier('loaderFile'), j.literal(filename))
j.property(
'init',
j.identifier('loaderFile'),
j.literal(importSpecifier)
)
)
images.value.properties = properties
const normalizeSrc = `const normalizeSrc = (src) => src[0] === '/' ? src.slice(1) : src`
if (loaderType === 'imgix') {
writeFileSync(
filename,
filePath,
`${normalizeSrc}
export default function imgixLoader({ src, width, quality }) {
const url = new URL('${pathPrefix}' + normalizeSrc(src))
Expand All @@ -213,7 +223,7 @@ function nextConfigTransformer(j: JSCodeshift, root: Collection) {
)
} else if (loaderType === 'cloudinary') {
writeFileSync(
filename,
filePath,
`${normalizeSrc}
export default function cloudinaryLoader({ src, width, quality }) {
const params = ['f_auto', 'c_limit', 'w_' + width, 'q_' + (quality || 'auto')]
Expand All @@ -226,7 +236,7 @@ function nextConfigTransformer(j: JSCodeshift, root: Collection) {
)
} else if (loaderType === 'akamai') {
writeFileSync(
filename,
filePath,
`${normalizeSrc}
export default function akamaiLoader({ src, width, quality }) {
return '${pathPrefix}' + normalizeSrc(src) + '?imwidth=' + width
Expand All @@ -250,14 +260,15 @@ export default function transformer(
const j = api.jscodeshift.withParser('tsx')
const root = j(file.source)

const parsed = parse(file.path || '/')
const isConfig =
file.path === 'next.config.js' ||
file.path === 'next.config.ts' ||
file.path === 'next.config.mjs' ||
file.path === 'next.config.cjs'
parsed.base === 'next.config.js' ||
parsed.base === 'next.config.ts' ||
parsed.base === 'next.config.mjs' ||
parsed.base === 'next.config.cjs'

if (isConfig) {
const result = nextConfigTransformer(j, root)
const result = nextConfigTransformer(j, root, parsed.dir)
return result.toSource()
}

Expand Down