Skip to content

Commit

Permalink
fix(codemod): Fix replace-component-svgs codemod to deal with re-expo…
Browse files Browse the repository at this point in the history
…rted (#9053)
  • Loading branch information
dac09 authored and jtoar committed Sep 2, 2023
1 parent ed7e295 commit f8c4548
Show file tree
Hide file tree
Showing 22 changed files with 188 additions and 37 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# This file contains the configuration settings for your Redwood app.
# This file is also what makes your Redwood app a Redwood app.
# If you remove it and try to run `yarn rw dev`, you'll get an error.
#
# For the full list of options, see the "App Configuration: redwood.toml" doc:
# https://redwoodjs.com/docs/app-configuration-redwood-toml

[web]
title = "Redwood App"
port = 8910
apiUrl = "/.redwood/functions" # you can customise graphql and dbauth urls individually too: see https://redwoodjs.com/docs/app-configuration-redwood-toml#api-paths
includeEnvironmentVariables = [] # any ENV vars that should be available to the web side, see https://redwoodjs.com/docs/environment-variables#web
[api]
port = 8911
[browser]
open = true
[notifications]
versionUpdates = ["latest"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import CurrencyYen from './currency-yen.svg'
import Database from './database.svg'
import Desktop from 'src/components/other/desktop-computer.svg'
import DeviceMobile from './device-mobile.svg'

export { CurrencyYen, Database as DBIcon, Desktop, DeviceMobile as Mobile }

export { default as CursorClick } from './cursor-click.svg'
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# This file contains the configuration settings for your Redwood app.
# This file is also what makes your Redwood app a Redwood app.
# If you remove it and try to run `yarn rw dev`, you'll get an error.
#
# For the full list of options, see the "App Configuration: redwood.toml" doc:
# https://redwoodjs.com/docs/app-configuration-redwood-toml

[web]
title = "Redwood App"
port = 8910
apiUrl = "/.redwood/functions" # you can customise graphql and dbauth urls individually too: see https://redwoodjs.com/docs/app-configuration-redwood-toml#api-paths
includeEnvironmentVariables = [] # any ENV vars that should be available to the web side, see https://redwoodjs.com/docs/environment-variables#web
[api]
port = 8911
[browser]
open = true
[notifications]
versionUpdates = ["latest"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import type { SVGProps } from "react";
const CurrencyYen = (props: SVGProps<SVGSVGElement>) => <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" {...props}><path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM7.858 5.485a1 1 0 00-1.715 1.03L7.633 9H7a1 1 0 100 2h1.834l.166.277V12H7a1 1 0 100 2h2v1a1 1 0 102 0v-1h2a1 1 0 100-2h-2v-.723l.166-.277H13a1 1 0 100-2h-.634l1.492-2.486a1 1 0 10-1.716-1.029L10.034 9h-.068L7.858 5.485z" clipRule="evenodd" /></svg>;
export default CurrencyYen;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import type { SVGProps } from "react";
const CursorClick = (props: SVGProps<SVGSVGElement>) => <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" {...props}><path fillRule="evenodd" d="M6.672 1.911a1 1 0 10-1.932.518l.259.966a1 1 0 001.932-.518l-.26-.966zM2.429 4.74a1 1 0 10-.517 1.932l.966.259a1 1 0 00.517-1.932l-.966-.26zm8.814-.569a1 1 0 00-1.415-1.414l-.707.707a1 1 0 101.415 1.415l.707-.708zm-7.071 7.072l.707-.707A1 1 0 003.465 9.12l-.708.707a1 1 0 001.415 1.415zm3.2-5.171a1 1 0 00-1.3 1.3l4 10a1 1 0 001.823.075l1.38-2.759 3.018 3.02a1 1 0 001.414-1.415l-3.019-3.02 2.76-1.379a1 1 0 00-.076-1.822l-10-4z" clipRule="evenodd" /></svg>;
export default CursorClick;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import type { SVGProps } from "react";
const Database = (props: SVGProps<SVGSVGElement>) => <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" {...props}><path d="M3 12v3c0 1.657 3.134 3 7 3s7-1.343 7-3v-3c0 1.657-3.134 3-7 3s-7-1.343-7-3z" /><path d="M3 7v3c0 1.657 3.134 3 7 3s7-1.343 7-3V7c0 1.657-3.134 3-7 3S3 8.657 3 7z" /><path d="M17 5c0 1.657-3.134 3-7 3S3 6.657 3 5s3.134-3 7-3 7 1.343 7 3z" /></svg>;
export default Database;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import type { SVGProps } from "react";
const DeviceMobile = (props: SVGProps<SVGSVGElement>) => <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" {...props}><path fillRule="evenodd" d="M7 2a2 2 0 00-2 2v12a2 2 0 002 2h6a2 2 0 002-2V4a2 2 0 00-2-2H7zm3 14a1 1 0 100-2 1 1 0 000 2z" clipRule="evenodd" /></svg>;
export default DeviceMobile;
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import CurrencyYen from "./CurrencyYenSVG"
import Database from "./DatabaseSVG"
import Desktop from "src/components/other/DesktopComputerSVG"
import DeviceMobile from "./DeviceMobileSVG"

export { CurrencyYen, Database as DBIcon, Desktop, DeviceMobile as Mobile}

export { default as CursorClick } from "./CursorClickSVG"
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import type { SVGProps } from "react";
const DesktopComputer = (props: SVGProps<SVGSVGElement>) => <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" {...props}><path fillRule="evenodd" d="M3 5a2 2 0 012-2h10a2 2 0 012 2v8a2 2 0 01-2 2h-2.22l.123.489.804.804A1 1 0 0113 18H7a1 1 0 01-.707-1.707l.804-.804L7.22 15H5a2 2 0 01-2-2V5zm5.771 7H5V5h10v7H8.771z" clipRule="evenodd" /></svg>;
export default DesktopComputer;
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,12 @@ describe('replaceComponentSvgs', () => {
removeWhitespace: true, // needed for matching
})
})

it('Deals with when SVGs are rexported', async () => {
await matchFolderTransform('replaceComponentSvgs', 'reExported', {
useJsCodeshift: true,
targetPathsGlob: '**/*.{js,jsx,tsx}',
removeWhitespace: true, // needed for matching
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -49,61 +49,101 @@ export default async function transform(file: FileInfo, api: API) {
// Find all import declarations with "*.svg" import
const svgImports = root.find(j.ImportDeclaration).filter((path) => {
const importPath = path.node.source.value as string
return importPath.includes('.svg')
return importPath.endsWith('.svg')
})

// This is if you directly export from svg:
// e.g. export { default as X } from './X.svg'
const svgNamedExports = root.find(j.ExportNamedDeclaration).filter((path) => {
const source = path.value.source
return Boolean(
source &&
typeof source.value === 'string' &&
source.value.endsWith('.svg')
)
})

const svgsToConvert: Array<{
filePath: string
importSourcePath: StringLiteral
}> = []

const importOrExportStatementsWithSvg = [
...svgImports.paths(),
...svgNamedExports.paths(),
]
// Process each import declaration
svgImports.forEach((importDeclaration) => {
const importSpecifiers = importDeclaration.node.specifiers
importOrExportStatementsWithSvg.forEach((declaration) => {
const specifiers = declaration.node.specifiers

// Process each import specifier
importSpecifiers?.forEach((importSpecifier) => {
if (importSpecifier.type === 'ImportDefaultSpecifier') {
if (!importSpecifier.local) {
specifiers?.forEach((specifier) => {
// The name of the improted SVG, assigned based on whether you are
// importing or exporting directly
let svgName = ''

if (specifier.type === 'ExportSpecifier') {
svgName = specifier.exported.name
} else if (specifier.type === 'ImportDefaultSpecifier') {
if (!specifier.local) {
// Un-freaking-likely, skip if it happens
return
}

const importName = importSpecifier.local.name
svgName = specifier.local.name
}

const importPath = importDeclaration.node.source.value as string
const currentFolder = path.dirname(file.path)
const sourcePath = declaration.node.source?.value as string

let pathToSvgFile = path.resolve(currentFolder, importPath)
if (!sourcePath) {
// Note sure how this is possible.... but TS tells me to do this
// I guess because most export statements don't have a source?
return
}

if (importPath.startsWith('src/')) {
pathToSvgFile = importPath.replace('src/', getPaths().web.src + '/')
}
const currentFolder = path.dirname(file.path)

// Find the JSX elements that use the default import specifier
const svgsUsedAsComponent = root.findJSXElements(importName)
let pathToSvgFile = path.resolve(currentFolder, sourcePath)

svgsUsedAsComponent.forEach(() => {
svgsToConvert.push({
filePath: pathToSvgFile,
importSourcePath: importDeclaration.node.source as StringLiteral, // imports are all strings in this case
})
})
if (sourcePath.startsWith('src/')) {
pathToSvgFile = sourcePath.replace('src/', getPaths().web.src + '/')
}

const svgsUsedAsRenderProp = root.find(j.JSXExpressionContainer, {
expression: {
type: 'Identifier',
name: importName,
},
})
// Find the JSX elements that use the default import specifier
// e,g, <MySvg />
const svgsUsedAsComponent = root.findJSXElements(svgName)

// Used as a render prop
// <Component icon={MySvg} />
const svgsUsedAsRenderProp = root.find(j.JSXExpressionContainer, {
expression: {
type: 'Identifier',
name: svgName,
},
})

svgsUsedAsRenderProp.forEach(() => {
svgsToConvert.push({
filePath: pathToSvgFile,
importSourcePath: importDeclaration.node.source as StringLiteral, // imports are all strings in this case
})
// a) exported from another file e.g. export { default as MySvg } from './X.svg'
// b) imported from another file e.g. import MySvg from './X.svg', then exported export { MySvg }
const svgsReexported = root.find(j.ExportSpecifier).filter((path) => {
return (
path.value.local?.name === svgName ||
path.value.exported.name === svgName
)
})

// Concat all of them, and loop over once
const selectedSvgs = [
...svgsUsedAsComponent.paths(),
...svgsUsedAsRenderProp.paths(),
...svgsReexported.paths(),
]

selectedSvgs.forEach(() => {
svgsToConvert.push({
filePath: pathToSvgFile,
importSourcePath: declaration.node.source as StringLiteral, // imports are all strings in this case
})
}
})
})
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ export const description =

export const handler = () => {
task('Replace Component Svgs', async ({ setOutput }: TaskInnerAPI) => {
const targetPaths = fg.sync('**/*.{js,jsx,tsx}', {
cwd: getPaths().web.src,
absolute: true,
})

await runTransform({
transformPath: path.join(__dirname, 'replaceComponentSvgs.js'),
targetPaths: fg.sync('**/*.{js,jsx,tsx}', {
cwd: getPaths().web.src,
absolute: true,
}),
targetPaths,
})

setOutput('All done! Run `yarn rw lint --fix` to prettify your code')
Expand Down

0 comments on commit f8c4548

Please sign in to comment.