Skip to content

Commit

Permalink
gatsby-image codemod (#28112)
Browse files Browse the repository at this point in the history
* saving initial AST exploration

* use the types to build replacement expression

* missed a line

* work off import parent

* graphql comment

* finish for the nigh

* Codemod for gatsby images

* use alias function to pass linter

* missed a check that isn't strictly necessary

* Update packages/gatsby-plugin-image/src/__tests__/options.json

* Update packages/gatsby-plugin-image/src/__tests__/fixtures/default/options.json

* handle call expression syntax

* small refactor

* function with recast, use replaceWith for more stability

* function with recast, use replaceWith for more stability

* use templates instead

* handle typescript in recast version

* add comments to test cases

* put recast and babel in jscodeshift wrapper

* Add tokens so we're parsing with babel through recast as well

* dont use a yaml query because tests attempt to access it

* don't update components when it isn't necessary

* we don't need to return and skip for this case

* add tests

* forgot to save and updated README

* shouldn't have saved it, oops

* ignore testfixtures

* integration should ignore fixtures, maybe

* windows fix, thanks ward

* fix syntax to fix tests

* adding some edge cases, need a real warn

* object expression case

* fix edge cases, still need to make imageImportName reset per file

* use path instead of name to ensure scope

* small refactor, small fix

* still run ts test

* use new localName references

* add mansion test now that it works

* forgot to save

* cleanup

* fix linter issue

* expressive warns

* remove false warning

* show recommendation if parser fails

* readme tweak

* recommend silent flag

* update README with src vs root directories

* object reference edge case and polyfill

* there is only one gatsby-image import extra, so let's not use includes

* Update packages/gatsby-plugin-image/src/transforms/gatsby-plugin-image.js

Co-authored-by: Kyle Gill <kylerobertgill@gmail.com>

* make jscodeshift a dev dependency

* Move to codemods package

* Add initial bin implementation

* update readme

* handle invalid cases

* ignore cache and public, use proper syntax

* support styled and other references but still warn

* remove redudant assignment

* use process.argv a bit more intelligently

* fix last failing test

* fix ignore logic for npx

* address comments

* extending use case

Co-authored-by: Kyle Gill <kylerobertgill@gmail.com>
Co-authored-by: gatsbybot <mathews.kyle+gatsbybot@gmail.com>
  • Loading branch information
3 people committed Dec 4, 2020
1 parent fbb2758 commit 726a5fe
Show file tree
Hide file tree
Showing 50 changed files with 1,494 additions and 28 deletions.
1 change: 1 addition & 0 deletions integration-tests/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ module.exports = {
`/dist/`,
`/node_modules/`,
`__tests__/fixtures`,
`__testfixtures__`,
`.cache`,
],
transform: { "^.+\\.js$": `<rootDir>/jest-transformer.js` },
Expand Down
1 change: 1 addition & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ module.exports = {
`<rootDir>/node_modules/`,
`<rootDir>/packages/gatsby-admin/.cache/`,
`__tests__/fixtures`,
`__testfixtures__/`,
],
transform: {
"^.+\\.[jt]sx?$": `<rootDir>/jest-transformer.js`,
Expand Down
1 change: 1 addition & 0 deletions packages/gatsby-codemods/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
!index.js
yarn.lock
transforms/*
bin/*
14 changes: 10 additions & 4 deletions packages/gatsby-codemods/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,22 @@
},
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.12.5"
"@babel/runtime": "^7.12.5",
"jscodeshift": "^0.11.0"
},
"devDependencies": {
"@babel/cli": "^7.12.1",
"@babel/core": "^7.12.3",
"babel-preset-gatsby-package": "^0.9.0-next.0",
"cross-env": "^7.0.2",
"jscodeshift": "^0.11.0"
"@babel/plugin-syntax-jsx": "^7.12.1",
"@babel/plugin-syntax-typescript": "^7.12.1",
"@babel/plugin-proposal-class-properties": "^7.12.1",
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
"recast": "^0.20.4",
"cross-env": "^7.0.2"
},
"engines": {
"node": ">=10.13.0"
}
},
"bin": "./bin/gatsby-codemods.js"
}
70 changes: 70 additions & 0 deletions packages/gatsby-codemods/src/bin/__tests__/gatsby-codemods-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
let execaReturnValue

jest.setMock('execa', {
sync: () => execaReturnValue
});

import process from 'process'
import path from 'path'
import fs from 'fs'

const {
runTransform,
run,
transformerDirectory,
jscodeshiftExecutable
} = require('../cli');

describe('transform', () => {

it('finds transformer directory', () => {
fs.lstatSync(transformerDirectory)
})

it('finds jscodeshift executable', () => {
fs.lstatSync(jscodeshiftExecutable)
})

it('runs jscodeshift for the given transformer', () => {
execaReturnValue = { error: null };
console.log = jest.fn();
runTransform(`gatsby-plugin-image`, `src`)

expect(console.log).toBeCalledWith(
`Executing command: jscodeshift --ignore-pattern=**/node_modules/** --ignore-pattern=**/.cache/** --ignore-pattern=**/public/** --extensions=jsx,js,ts,tsx --transform ${path.join(transformerDirectory, 'gatsby-plugin-image.js')} src`
)
})

it('warns when on missing transform', () => {
execaReturnValue = { error: null };
console.log = jest.fn();
process.argv = [`node`, `dir`]
run()

expect(console.log).toBeCalledWith(
`Be sure to pass in the name of the codemod you're attempting to run.`
)
})

it('warns when on missing target', () => {
execaReturnValue = { error: null };
console.log = jest.fn();
process.argv = [`node`, `dir`, `gatsby-plugin-image`]
run()

expect(console.log).toBeCalledWith(
`You have not provided a target directory to run the codemod against, will default to root.`
)
})

it('warns when invalid transform', () => {
execaReturnValue = { error: null };
console.log = jest.fn();
process.argv = [`node`, `dir`, `does-not-exist`]
run()

expect(console.log).toBeCalledWith(
`You have passed in invalid codemod name: does-not-exist. Please pass in one of the following: gatsby-plugin-image, global-graphql-calls, import-link, navigate-calls, rename-bound-action-creators.`
)
})
})
53 changes: 53 additions & 0 deletions packages/gatsby-codemods/src/bin/cli.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import path from 'path'
import execa from 'execa'

const codemods = [`gatsby-plugin-image`, `global-graphql-calls`, `import-link`, `navigate-calls`, `rename-bound-action-creators`]

export const transformerDirectory = path.join(__dirname, '../', 'transforms')
export const jscodeshiftExecutable = require.resolve('.bin/jscodeshift')

export function runTransform(transform, targetDir) {
const transformerPath = path.join(transformerDirectory, `${transform}.js`)

let args = []

args.push('--ignore-pattern=**/node_modules/**')
args.push('--ignore-pattern=**/.cache/**')
args.push('--ignore-pattern=**/public/**')

args.push('--extensions=jsx,js,ts,tsx')

args = args.concat(['--transform', transformerPath, targetDir])

console.log(`Executing command: jscodeshift ${args.join(' ')}`);

const result = execa.sync(jscodeshiftExecutable, args, {
stdio: 'inherit',
stripEof: false
})

if (result.error) {
throw result.error
}
}

export function run() {
let [transform, targetDir] = process.argv.slice(2)

if (!transform) {
console.log(`Be sure to pass in the name of the codemod you're attempting to run.`)
return
}

if (!codemods.includes(transform)) {
console.log(`You have passed in invalid codemod name: ${transform}. Please pass in one of the following: ${codemods.join(", ")}.`)
return
}

if(!targetDir) {
console.log(`You have not provided a target directory to run the codemod against, will default to root.`)
targetDir = `./`

}
runTransform(transform, targetDir)
}
3 changes: 3 additions & 0 deletions packages/gatsby-codemods/src/bin/gatsby-codemods.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env node

require('./cli').run();
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from "react"
import { graphql } from "gatsby"
import Img from "gatsby-image"

<Img fixed={data.file.childImageSharp.fixed} alt="headshot"/>

export const query = graphql`
{
file(relativePath: { eq: "headers/default.jpg" }) {
childImageSharp {
fixed(width: 125, height: 125) {
...GatsbyImageSharpFixed
}
}
}
}
`
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from "react"
import { graphql } from "gatsby"
import { GatsbyImage } from "gatsby-plugin-image";

<GatsbyImage image={data.file.childImageSharp.gatsbyImageData} alt="headshot" />

export const query = graphql`{
file(relativePath: {eq: "headers/default.jpg"}) {
childImageSharp {
gatsbyImageData(width: 125, height: 125, layout: FIXED)
}
}
}
`
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const result = graphql(`
query {
file(relativePath: { eq: "headers/headshot.jpg" }) {
childImageSharp {
fluid {
...GatsbyImageSharpFluid
}
}
}
}
`)
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const result = graphql(`{
file(relativePath: {eq: "headers/headshot.jpg"}) {
childImageSharp {
gatsbyImageData(layout: FLUID)
}
}
}
`)
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { GatsbyImage as Img } from "gatsby-plugin-image/compat"
// how do comments work?

<Img
fixed={data.file.childImageSharp.fixed}
alt="headshot"/>

//still doing ok?
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { GatsbyImage } from "gatsby-plugin-image";
// how do comments work?

<GatsbyImage image={data.file.childImageSharp.gatsbyImageData} alt="headshot" />

//still doing ok?
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React from "react"
import PropTypes from "prop-types"
import Image from "gatsby-image"

const svgAssetsContext = require.context(
`!file-loader!svgo-loader?{"plugins":[{"removeViewBox":false}]}!../assets`,
true,
/^\.\/.*\.svg$/
)

class SVGGatsbyImage extends Image {
render() {
const { fluid, alt, ...rest } = this.props

if (!this.state.isVisible) {
return <div ref={this.handleRef} />
}

return (
<img
src={svgAssetsContext(`./${fluid.src}`)}
alt={alt}
ref={this.imageRef}
{...rest}
/>
)
}
}

SVGGatsbyImage.propTypes = {
alt: PropTypes.string,
}

SVGGatsbyImage.defaultProps = {
alt: ``,
}

const LazySVGImg = ({ src, ...rest }) => (
<SVGGatsbyImage fluid={{ src }} {...rest} />
)

LazySVGImg.propTypes = {
src: PropTypes.string,
}

export default LazySVGImg
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import React from "react"
import PropTypes from "prop-types"
import { GatsbyImage } from "gatsby-plugin-image";

const svgAssetsContext = require.context(
`!file-loader!svgo-loader?{"plugins":[{"removeViewBox":false}]}!../assets`,
true,
/^\.\/.*\.svg$/
)

class SVGGatsbyImage extends GatsbyImage {
render() {
const { fluid, alt, ...rest } = this.props

if (!this.state.isVisible) {
return <div ref={this.handleRef} />
}

return (
<img
src={svgAssetsContext(`./${fluid.src}`)}
alt={alt}
ref={this.imageRef}
{...rest}
/>
)
}
}

SVGGatsbyImage.propTypes = {
alt: PropTypes.string,
}

SVGGatsbyImage.defaultProps = {
alt: ``,
}

const LazySVGImg = ({ src, ...rest }) => (
<SVGGatsbyImage fluid={{ src }} {...rest} />
)

LazySVGImg.propTypes = {
src: PropTypes.string,
}

export default LazySVGImg
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<GatsbyImage image={getImage(data.fitInside)} alt="chameleon" />
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<GatsbyImage image={getImage(data.fitInside)} alt="chameleon" />

0 comments on commit 726a5fe

Please sign in to comment.