Skip to content

Commit

Permalink
Implement react-18 preset (#13)
Browse files Browse the repository at this point in the history
Co-authored-by: Sebastian Silbermann <sebastian.silbermann@klarna.com>
  • Loading branch information
eps1lon and Sebastian Silbermann committed Apr 4, 2022
1 parent ffafd3d commit 5da481a
Show file tree
Hide file tree
Showing 42 changed files with 395 additions and 19 deletions.
5 changes: 5 additions & 0 deletions .changeset/flat-llamas-raise.md
@@ -0,0 +1,5 @@
---
"types-react-codemod": minor
---

Add `preset-18` codemod
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
23 changes: 22 additions & 1 deletion bin/types-react-codemod.cjs
Expand Up @@ -2,6 +2,7 @@
// @ts-check
const childProcess = require("child_process");
const fs = require("fs");
const inquirer = require("inquirer");
const process = require("process");
const yargs = require("yargs/yargs");
const { hideBin } = require("yargs/helpers");
Expand Down Expand Up @@ -40,7 +41,7 @@ async function main() {
.option("verbose", { default: false, type: "boolean" })
.demandOption(["codemod", "paths"]);
},
(argv) => {
async (argv) => {
const { codemod, dry, paths, verbose } = argv;

// TODO: npx instead?
Expand All @@ -55,6 +56,26 @@ async function main() {
`--transform ${path.join(transformsRoot, `${codemod}.js`)}`,
];

if (codemod === "preset-18") {
const { presets } = await inquirer.prompt([
{
message: "Pick transforms to apply",
name: "presets",
type: "checkbox",
choices: [
{ checked: false, value: "context-any" },
{ checked: true, value: "deprecated-react-type" },
{ checked: true, value: "deprecated-sfc-element" },
{ checked: true, value: "deprecated-sfc" },
{ checked: true, value: "deprecated-stateless-component" },
{ checked: false, value: "implicit-children" },
{ checked: false, value: "useCallback-implicit-any" },
],
},
]);
args.push(`--preset18Transforms="${presets.join(",")}"`);
}

if (dry) {
args.push("--dry");
}
Expand Down
2 changes: 2 additions & 0 deletions package.json
Expand Up @@ -12,6 +12,7 @@
"devDependencies": {
"@changesets/changelog-github": "^0.4.4",
"@changesets/cli": "^2.22.0",
"@types/inquirer": "^8.2.1",
"@types/jscodeshift": "^0.11.3",
"@types/node": "^16.10.0",
"@types/yargs": "^17.0.10",
Expand All @@ -30,6 +31,7 @@
"dependencies": {
"@babel/core": "^7.17.8",
"@babel/preset-env": "^7.16.11",
"inquirer": "^8.2.2",
"jscodeshift": "^0.13.1",
"yargs": "^17.4.0"
}
Expand Down
4 changes: 2 additions & 2 deletions transforms/context-any.js
Expand Up @@ -12,7 +12,7 @@ const parseSync = require("./utils/parseSync");
* 2 false-positive due to `path['context']` access
* 1 false-positive due to `constructor(props, context)`
*/
const transformer = (file, api) => {
const contextAnyTransform = (file, api) => {
const j = api.jscodeshift;
const ast = parseSync(file);

Expand Down Expand Up @@ -109,4 +109,4 @@ const transformer = (file, api) => {
return file.source;
};

export default transformer;
export default contextAnyTransform;
4 changes: 2 additions & 2 deletions transforms/deprecated-react-type.js
Expand Up @@ -4,7 +4,7 @@ const parseSync = require("./utils/parseSync");
* @type {import('jscodeshift').Transform}
* test: https://astexplorer.net/#/gist/e961510c31d225c80bac9cb0a499b1b3/65af0f88291c72e1295a5d36e5d372bb5bf50e3c
*/
const transformer = (file, api) => {
const deprecatedReactTypeTransform = (file, api) => {
const j = api.jscodeshift;
const ast = parseSync(file);

Expand All @@ -23,4 +23,4 @@ const transformer = (file, api) => {
return file.source;
};

export default transformer;
export default deprecatedReactTypeTransform;
4 changes: 2 additions & 2 deletions transforms/deprecated-sfc-element.js
Expand Up @@ -4,7 +4,7 @@ const parseSync = require("./utils/parseSync");
* @type {import('jscodeshift').Transform}
* test: https://astexplorer.net/#/gist/e63cf2776df94790a14280da12128019/3e82bd47dc3df1bb555c595e6b4423855d3f3277
*/
const transformer = (file, api) => {
const deprecatedSFCElementTransform = (file, api) => {
const j = api.jscodeshift;
const ast = parseSync(file);

Expand All @@ -23,4 +23,4 @@ const transformer = (file, api) => {
return file.source;
};

export default transformer;
export default deprecatedSFCElementTransform;
4 changes: 2 additions & 2 deletions transforms/deprecated-sfc.js
Expand Up @@ -4,7 +4,7 @@ const parseSync = require("./utils/parseSync");
* @type {import('jscodeshift').Transform}
* test: https://astexplorer.net/#/gist/ea9d61a7e259eb28984136ba37d39922/8d0777b75f0651193991c1e05343de50b292d4aa
*/
const transformer = (file, api) => {
const deprecatedSFCTransform = (file, api) => {
const j = api.jscodeshift;
const ast = parseSync(file);

Expand All @@ -23,4 +23,4 @@ const transformer = (file, api) => {
return file.source;
};

export default transformer;
export default deprecatedSFCTransform;
4 changes: 2 additions & 2 deletions transforms/deprecated-stateless-component.js
Expand Up @@ -4,7 +4,7 @@ const parseSync = require("./utils/parseSync");
* @type {import('jscodeshift').Transform}
* test: https://astexplorer.net/#/gist/ebd4c5257e3b5385a860de26edab25a0/a9df97df215041311c96c309683bdb9cad5b7b01
*/
const transformer = (file, api) => {
const deprecatedStatelessComponentTransform = (file, api) => {
const j = api.jscodeshift;
const ast = parseSync(file);

Expand All @@ -23,4 +23,4 @@ const transformer = (file, api) => {
return file.source;
};

export default transformer;
export default deprecatedStatelessComponentTransform;
4 changes: 2 additions & 2 deletions transforms/implicit-children.js
Expand Up @@ -9,7 +9,7 @@ const parseSync = require("./utils/parseSync");
* 11 skipped
* 2459 Ok
*/
const transformer = (file, api) => {
const implicitChildrenTransform = (file, api) => {
const j = api.jscodeshift;
const ast = parseSync(file);

Expand Down Expand Up @@ -72,4 +72,4 @@ const transformer = (file, api) => {
return file.source;
};

export default transformer;
export default implicitChildrenTransform;
77 changes: 77 additions & 0 deletions transforms/preset-18.js
@@ -0,0 +1,77 @@
import contextAnyTransform from "./context-any";
import deprecatedReactTypeTransform from "./deprecated-react-type";
import deprecatedSFCTransform from "./deprecated-sfc";
import deprecatedSFCElementTransform from "./deprecated-sfc-element";
import deprecatedStatelessComponentTransform from "./deprecated-stateless-component";
import implicitChildrenTransform from "./implicit-children";
import implicitAnyTransform from "./useCallback-implicit-any";

/**
* @type {import('jscodeshift').Transform}
* test: https://astexplorer.net/#/gist/efb6993a6dda29edfa15087323d95d8b/649afe43341d87843b0e90d737ceefc8f90c321b
*
* Summary for Klarna's klapp@19fc4dafed84670398644298bf19c8c2a781dcf8/clients
* 28172 Files unmodified
* 11 skipped
* 5 Ok
* 1 false-positive due to `this.props.context` access
* 2 false-positive due to `path['context']` access
* 1 false-positive due to `constructor(props, context)`
*/
const transform = (file, api, options) => {
const { preset18Transforms } = options;

const transformNames = new Set(preset18Transforms.split(","));
/**
* @type {import('jscodeshift').Transform[]}
*/
const transforms = [];
if (transformNames.has("context-any")) {
transforms.push(contextAnyTransform);
}
if (transformNames.has("deprecated-react-type")) {
transforms.push(deprecatedReactTypeTransform);
}
if (transformNames.has("deprecated-sfc")) {
transforms.push(deprecatedSFCTransform);
}
if (transformNames.has("deprecated-sfc-element")) {
transforms.push(deprecatedSFCElementTransform);
}
if (transformNames.has("deprecated-stateless-component")) {
transforms.push(deprecatedStatelessComponentTransform);
}
if (transformNames.has("implicit-children")) {
transforms.push(implicitChildrenTransform);
}
if (transformNames.has("useCallback-implicit-any")) {
transforms.push(implicitAnyTransform);
}

console.log(transformNames, transforms);

let wasAlwaysSkipped = true;
const newSource = transforms.reduce((currentFileSource, transform) => {
// TODO: currently we parse -> transform -> print on every transform
// Instead, we could parse and prince once in the preset and the transformers just deal with an AST.
// That requires refactoring of every transform into source-transformer and ast-transformer
const transformResult = transform(
{ path: file.path, source: currentFileSource },
api,
options
);

if (transformResult == null) {
return currentFileSource;
} else {
wasAlwaysSkipped = false;
return transformResult;
}
}, file.source);

if (!wasAlwaysSkipped) {
return newSource;
}
};

export default transform;
4 changes: 2 additions & 2 deletions transforms/useCallback-implicit-any.js
Expand Up @@ -13,7 +13,7 @@ const parseSync = require("./utils/parseSync");
* `const foo: Type = () => useCallback(event => {})`
* BUT this gets increasingly complicated if this becomes `const foo = () => useCallback(event => {})`
*/
const transformer = (file, api) => {
const useCallbackImplicitAnyTransform = (file, api) => {
const fileSupportsTypeAnnotations =
file.path.endsWith(".ts") || file.path.endsWith(".tsx");
if (!fileSupportsTypeAnnotations) {
Expand Down Expand Up @@ -110,4 +110,4 @@ const transformer = (file, api) => {
return file.source;
};

export default transformer;
export default useCallbackImplicitAnyTransform;

0 comments on commit 5da481a

Please sign in to comment.