Bartosz Szewczyk
>5 @ Codete
2021
Me after half-marathon
Game I made in my free time
Rewriting code can be hard
import styled from '@emotion/styled'; import { typography, space, color, themeGet } from 'styled-system'; const Box = styled.div` ${typography} ${space} ${color} background-color: ${props => themeGet('colors.brand')} `;
import styled from '@emotion/styled'; import { typography, space, color, themeGet } from 'styled-system'; const Box = styled.div` ${typography} ${space} ${color} background-color: ${props => themeGet('colors.brand')} `;
import styled from '@emotion/styled'; import { typography, space, color, themeGet } from 'styled-system'; const Box = styled.div` ${typography} ${space} ${color} background-color: ${props => themeGet('colors.brand')} `;
import styled from '@emotion/styled'; import { typography, space, color, } from 'styled-system'; import { themeGet } from '@styled-system/theme-get'; const Box = styled.div` ${typography} ${space} ${color} background-color: ${props => themeGet('colors.brand')} `;
import styled from '@emotion/styled'; import { typography, space, color, } from 'styled-system'; import { themeGet } from '@styled-system/theme-get'; const Box = styled.div` ${typography} ${space} ${color} background-color: ${props => themeGet('colors.brand')} `;
import { themeGet } from 'styled-system';
import { typography, themeGet } from 'styled-system';
import { themeGet, space } from 'styled-system';
import { typography, themeGet, space } from 'styled-system';
import { color, typography, themeGet, space, layout } from 'styled-system';
import { themeGet, color, typography, space, layout } from 'styled-system';
import { color, typography, space, layout, themeGet } from 'styled-system';
Not covered
import { themeGet as styledSystemThemeGet } from 'styled-system';
import * as styledSystem from 'styled-system'; styledSystem.themeGet('color.brand');
String that specifies a search pattern
/(import {[\s\S]*)(themeGet,)([\s\S]*} from 'styled-system';)/g
(import {[\s\S]*)
(themeGet,?)
([\s\S]*} from 'styled-system';)
Your code editor/IDE
Perl interpreter 🤓
MATCH="\ (import {[\s\S]*)\ (themeGet,?)\ ([\s\S]*} from 'styled-system';)\ " REPLACE="\ \1\3 import { themeGet } from '\@styled-system/theme-get'\; " perl -i -0pe "s|$MATCH|$REPLACE|gm" src/**/*.js
MATCH="\ (import {[\s\S]*)\ (themeGet,?)\ ([\s\S]*} from 'styled-system';)\ " REPLACE="\ \1\3 import { themeGet } from '\@styled-system/theme-get'\; " perl -i -0pe "s|$MATCH|$REPLACE|gm" src/**/*.js
MATCH="\ (import {[\s\S]*)\ (themeGet,?)\ ([\s\S]*} from 'styled-system';)\ " REPLACE="\ \1\3 import { themeGet } from '\@styled-system/theme-get'\; " perl -i -0pe "s|$MATCH|$REPLACE|gm" src/**/*.js
MATCH="\ (import {[\s\S]*)\ (themeGet,?)\ ([\s\S]*} from 'styled-system';)\ " REPLACE="\ \1\3 import { themeGet } from '\@styled-system/theme-get'\; " perl -i -0pe "s|$MATCH|$REPLACE|gm" src/**/*.js
Just use any scripting language
const fs = require('fs') const glob = require('glob') const regexp = /.../g const replace = '...' glob('src/**/*.{ts,tsx}', (err, files) => { files.forEach(filename => { const contents = fs.readFileSync(filename, { encoding: 'utf-8' }) if (contents.indexOf('themeGet') !== -1) { fs.writeFileSync( filename, contents.replace(regexp, replace) ) } }) })
const fs = require('fs') const glob = require('glob') const regexp = /.../g const replace = '...' glob('src/**/*.{ts,tsx}', (err, files) => { files.forEach(filename => { const contents = fs.readFileSync(filename, { encoding: 'utf-8' }) if (contents.indexOf('themeGet') !== -1) { fs.writeFileSync( filename, contents.replace(regexp, replace) ) } }) })
const fs = require('fs') const glob = require('glob') const regexp = /.../g const replace = '...' glob('src/**/*.{ts,tsx}', (err, files) => { files.forEach(filename => { const contents = fs.readFileSync(filename, { encoding: 'utf-8' }) if (contents.indexOf('themeGet') !== -1) { fs.writeFileSync( filename, contents.replace(regexp, replace) ) } }) })
const fs = require('fs') const glob = require('glob') const regexp = /.../g const replace = '...' glob('src/**/*.{ts,tsx}', (err, files) => { files.forEach(filename => { const contents = fs.readFileSync(filename, { encoding: 'utf-8' }) if (contents.indexOf('themeGet') !== -1) { fs.writeFileSync( filename, contents.replace(regexp, replace) ) } }) })
import { themeGet } from 'styled-system';
🧙♀️ regexp magic 🧙
import { } from 'styled-system'; import { themeGet } from '@styled-system/theme-get';
Comby is a tool for searching and rewriting code
:[var]
:[var~regex]
:[~regex]
foo.bar(argument)
foobar(unwrap(argument))
themeGet
importCOMBY_M="$(cat <<"MATCH" import { :[before]themeGet:[~,?]:[after] } from 'styled-system' MATCH )" COMBY_R="$(cat <<"REWRITE" import {:[before]:[after]} from 'styled-system' import { themeGet } from '@styled-system/theme-get' REWRITE )" comby "$COMBY_M" "$COMBY_R" .js -i -d src
COMBY_M="$(cat <<"MATCH" import { :[before]themeGet:[~,?]:[after] } from 'styled-system' MATCH )" COMBY_R="$(cat <<"REWRITE" import {:[before]:[after]} from 'styled-system' import { themeGet } from '@styled-system/theme-get' REWRITE )" comby "$COMBY_M" "$COMBY_R" .js -i -d src
COMBY_M="$(cat <<"MATCH" import { :[before]themeGet:[~,?]:[after] } from 'styled-system' MATCH )" COMBY_R="$(cat <<"REWRITE" import {:[before]:[after]} from 'styled-system' import { themeGet } from '@styled-system/theme-get' REWRITE )" comby "$COMBY_M" "$COMBY_R" .js -i -d src
COMBY_M="$(cat <<"MATCH" import { :[before]themeGet:[~,?]:[after] } from 'styled-system' MATCH )" COMBY_R="$(cat <<"REWRITE" import {:[before]:[after]} from 'styled-system' import { themeGet } from '@styled-system/theme-get' REWRITE )" comby "$COMBY_M" "$COMBY_R" .js -i -d src
Code that rewrites code
Code that operate on code
Code that operate on code AST
light mode warning ⚠️
export const transform = (fileInfo, api) => { const { jscodeshift: j } = api; return j(fileInfo.source) .find(/* styled-system import with themeGet */) .replaceWith(/* import without themeGet */) .insertAfter(/* @styled-system/theme-get import */) .toSource(); };
export const transform = (fileInfo, api) => { const { jscodeshift: j } = api; return j(fileInfo.source) .find(/* styled-system import with themeGet */) .replaceWith(/* import without themeGet */) .insertAfter(/* @styled-system/theme-get import */) .toSource(); };
export const transform = (fileInfo, api) => { const { jscodeshift: j } = api; return j(fileInfo.source) .find(/* styled-system import with themeGet */) .replaceWith(/* import without themeGet */) .insertAfter(/* @styled-system/theme-get import */) .toSource(); };
export const transform = (fileInfo, api) => { const { jscodeshift: j } = api; return j(fileInfo.source) .find(/* styled-system import with themeGet */) .replaceWith(/* import without themeGet */) .insertAfter(/* @styled-system/theme-get import */) .toSource(); };
export const transform = (fileInfo, api) => { const { jscodeshift: j } = api; return j(fileInfo.source) .find(/* styled-system import with themeGet */) .replaceWith(/* import without themeGet */) .insertAfter(/* @styled-system/theme-get import */) .toSource(); };
export const transform = (fileInfo, api) => { const { jscodeshift: j } = api; return j(fileInfo.source) .find(/* styled-system import with themeGet */) .replaceWith(/* import without themeGet */) .insertAfter(/* @styled-system/theme-get import */) .toSource(); };
const transform: Transform = (fileInfo, api) => { const { jscodeshift: j } = api; const isThemeGet = (specifier) => specifier.type === 'ImportSpecifier' && specifier.imported.name === 'themeGet'; return j(fileInfo.source) .find(j.ImportDeclaration, { source: { value: 'styled-system' }, specifiers: (specifiers) => specifiers?.some(isThemeGet) ?? false, }) .replaceWith(({ node }) => { const specifiersWithoutThemeGet = node.specifiers?.filter( (specifier) => !isThemeGet(specifier) ) ?? []; return specifiersWithoutThemeGet.length > 0 ? j.importDeclaration( specifiersWithoutThemeGet, node.source, node.importKind ) : null; }) .insertAfter( j.template .statement` import { theme-get } from '@styled-system/theme-get' ` ) .toSource(); };
const transform: Transform = (fileInfo, api) => { const { jscodeshift: j } = api; const isThemeGet = (specifier) => specifier.type === 'ImportSpecifier' && specifier.imported.name === 'themeGet'; return j(fileInfo.source) .find(j.ImportDeclaration, { source: { value: 'styled-system' }, specifiers: (specifiers) => specifiers?.some(isThemeGet) ?? false, }) .replaceWith(({ node }) => { const specifiersWithoutThemeGet = node.specifiers?.filter( (specifier) => !isThemeGet(specifier) ) ?? []; return specifiersWithoutThemeGet.length > 0 ? j.importDeclaration( specifiersWithoutThemeGet, node.source, node.importKind ) : null; }) .insertAfter( j.template .statement` import { theme-get } from '@styled-system/theme-get' ` ) .toSource(); };
const transform: Transform = (fileInfo, api) => { const { jscodeshift: j } = api; const isThemeGet = (specifier) => specifier.type === 'ImportSpecifier' && specifier.imported.name === 'themeGet'; return j(fileInfo.source) .find(j.ImportDeclaration, { source: { value: 'styled-system' }, specifiers: (specifiers) => specifiers?.some(isThemeGet) ?? false, }) .replaceWith(({ node }) => { const specifiersWithoutThemeGet = node.specifiers?.filter( (specifier) => !isThemeGet(specifier) ) ?? []; return specifiersWithoutThemeGet.length > 0 ? j.importDeclaration( specifiersWithoutThemeGet, node.source, node.importKind ) : null; }) .insertAfter( j.template .statement` import { theme-get } from '@styled-system/theme-get' ` ) .toSource(); };
const transform: Transform = (fileInfo, api) => { const { jscodeshift: j } = api; const isThemeGet = (specifier) => specifier.type === 'ImportSpecifier' && specifier.imported.name === 'themeGet'; return j(fileInfo.source) .find(j.ImportDeclaration, { source: { value: 'styled-system' }, specifiers: (specifiers) => specifiers?.some(isThemeGet) ?? false, }) .replaceWith(({ node }) => { const specifiersWithoutThemeGet = node.specifiers?.filter( (specifier) => !isThemeGet(specifier) ) ?? []; return specifiersWithoutThemeGet.length > 0 ? j.importDeclaration( specifiersWithoutThemeGet, node.source, node.importKind ) : null; }) .insertAfter( j.template .statement` import { theme-get } from '@styled-system/theme-get' ` ) .toSource(); };
const transform: Transform = (fileInfo, api) => { const { jscodeshift: j } = api; const isThemeGet = (specifier) => specifier.type === 'ImportSpecifier' && specifier.imported.name === 'themeGet'; return j(fileInfo.source) .find(j.ImportDeclaration, { source: { value: 'styled-system' }, specifiers: (specifiers) => specifiers?.some(isThemeGet) ?? false, }) .replaceWith(({ node }) => { const specifiersWithoutThemeGet = node.specifiers?.filter( (specifier) => !isThemeGet(specifier) ) ?? []; return specifiersWithoutThemeGet.length > 0 ? j.importDeclaration( specifiersWithoutThemeGet, node.source, node.importKind ) : null; }) .insertAfter( j.template .statement` import { theme-get } from '@styled-system/theme-get' ` ) .toSource(); };
const transform: Transform = (fileInfo, api) => { const { jscodeshift: j } = api; const isThemeGet = (specifier) => specifier.type === 'ImportSpecifier' && specifier.imported.name === 'themeGet'; return j(fileInfo.source) .find(j.ImportDeclaration, { source: { value: 'styled-system' }, specifiers: (specifiers) => specifiers?.some(isThemeGet) ?? false, }) .replaceWith(({ node }) => { const specifiersWithoutThemeGet = node.specifiers?.filter( (specifier) => !isThemeGet(specifier) ) ?? []; return specifiersWithoutThemeGet.length > 0 ? j.importDeclaration( specifiersWithoutThemeGet, node.source, node.importKind ) : null; }) .insertAfter( j.template .statement` import { theme-get } from '@styled-system/theme-get' ` ) .toSource(); };
cli
jscodeshift -t theme-get.ts src/**/*.js
Code can be easily unit tested
defineInlineTest( transform, {}, ` import { a, themeGet, b } from 'styled-system'; `, ` import { a, b } from 'styled-system'; import { themeGet } from '@styled-system/theme-get'; ` )
defineInlineTest( transform, {}, ` import { a, themeGet, b } from 'styled-system'; `, ` import { a, b } from 'styled-system'; import { themeGet } from '@styled-system/theme-get'; ` )
defineInlineTest( transform, {}, ` import { a, themeGet, b } from 'styled-system'; `, ` import { a, b } from 'styled-system'; import { themeGet } from '@styled-system/theme-get'; ` )
Is there the best option?
Bartosz Szewczyk
👨🏼💻 Tech Lead @ Codete
@sztobar