diff --git a/packages/migrate/commonsChunkPlugin/__snapshots__/commonsChunkPlugin.test.js.snap b/packages/migrate/commonsChunkPlugin/__snapshots__/commonsChunkPlugin.test.js.snap new file mode 100644 index 00000000000..edc282c874b --- /dev/null +++ b/packages/migrate/commonsChunkPlugin/__snapshots__/commonsChunkPlugin.test.js.snap @@ -0,0 +1,302 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`commonsChunkPlugin transforms correctly using "commonsChunkPlugin-0" data 1`] = ` +"module.exports = { + entry: { + app: './src/app.js', + vendor: './src/vendors.js', + }, + + optimizations: { + splitChunks: { + cacheGroups: { + app: { + name: 'app', + chunks: 'initial', + enforce: true, + test: 'app' + }, + + vendor: { + name: 'vendor', + chunks: 'initial', + enforce: true, + test: 'vendor' + } + } + } + } +} +" +`; + +exports[`commonsChunkPlugin transforms correctly using "commonsChunkPlugin-1" data 1`] = ` +"module.exports = { + entry: { + vendor: './src/vendors.js', + }, + + optimizations: { + splitChunks: { + cacheGroups: { + common: { + name: 'common', + chunks: 'initial', + enforce: true + }, + + vendor: { + name: 'vendor', + chunks: 'initial', + enforce: true, + test: 'vendor' + } + } + } + } +} +" +`; + +exports[`commonsChunkPlugin transforms correctly using "commonsChunkPlugin-2" data 1`] = ` +"module.exports = { + entry: { + vendor: './src/vendors.js', + }, + + optimizations: { + splitChunks: { + cacheGroups: { + vendor: { + name: 'vendor', + chunks: 'initial', + enforce: true, + test: 'vendor' + } + } + } + } +} +" +`; + +exports[`commonsChunkPlugin transforms correctly using "commonsChunkPlugin-3" data 1`] = ` +"module.exports = { + optimizations: { + splitChunks: { + cacheGroups: { + commons: { + name: 'commons', + chunks: 'initial', + enforce: true, + filename: 'commons.js' + } + } + } + } +} +" +`; + +exports[`commonsChunkPlugin transforms correctly using "commonsChunkPlugin-4" data 1`] = ` +"module.exports = { + entry: { + main: './src/index.js', + }, + + optimizations: { + splitChunks: { + cacheGroups: { + main: { + name: 'main', + enforce: true, + test: 'main', + chunks: 'async', + minSize: 2000 + } + } + } + } +} +" +`; + +exports[`commonsChunkPlugin transforms correctly using "commonsChunkPlugin-5" data 1`] = ` +"module.exports = { + entry: { + main: './src/index.js', + }, + + optimizations: { + splitChunks: { + cacheGroups: { + main: { + name: 'main', + chunks: 'initial', + enforce: true, + test: 'main' + } + } + }, + + runtimeChunk: { + name: 'runtime' + } + } +} +" +`; + +exports[`commonsChunkPlugin transforms correctly using "commonsChunkPlugin-6a" data 1`] = ` +"module.exports = { + entry: { + main: './src/index.js', + }, + + optimizations: { + splitChunks: { + cacheGroups: { + main: { + name: 'main', + chunks: 'initial', + enforce: true, + + test: module => { + if (module.getChunks().some(chunk => chunk.name === 'main')) + return true; + + const fn = ({ resource }) => /node_modules/.test(resource); + return fn(module); + } + } + } + } + } +} +" +`; + +exports[`commonsChunkPlugin transforms correctly using "commonsChunkPlugin-6b" data 1`] = ` +"module.exports = { + entry: { + main: './src/index.js', + }, + + optimizations: { + splitChunks: { + cacheGroups: { + main: { + name: 'main', + chunks: 'initial', + enforce: true, + + test: module => { + if (module.getChunks().some(chunk => chunk.name === 'main')) + return true; + + const fn = ({ resource }) => { + // some code + return /node_modules/.test(resource); + }; + + return fn(module); + } + } + } + } + } +} +" +`; + +exports[`commonsChunkPlugin transforms correctly using "commonsChunkPlugin-6c" data 1`] = ` +"module.exports = { + entry: { + main: './src/index.js', + }, + + optimizations: { + splitChunks: { + cacheGroups: { + main: { + name: 'main', + chunks: 'initial', + enforce: true, + + test: module => { + if (module.getChunks().some(chunk => chunk.name === 'main')) + return true; + + const fn = function ({ resource }) { + // some code + return /node_modules/.test(resource); + }; + + return fn(module); + } + } + } + } + } +} +" +`; + +exports[`commonsChunkPlugin transforms correctly using "commonsChunkPlugin-6d" data 1`] = ` +"module.exports = { + entry: { + main: './src/index.js', + }, + + optimizations: { + splitChunks: { + cacheGroups: { + main: { + name: 'main', + chunks: 'initial', + enforce: true, + + test: module => { + if (module.getChunks().some(chunk => chunk.name === 'main')) + return true; + + const fn = function ({ resource }) { + if (foo) { + return /node_modulesfoo/.test(resource); + } else { + return /node_modulesbaz/.test(resource); + } + }; + + return fn(module); + } + } + } + } + } +} +" +`; + +exports[`commonsChunkPlugin transforms correctly using "commonsChunkPlugin-7" data 1`] = ` +"module.exports = { + entry: { + main: './src/index.js', + }, + + optimizations: { + splitChunks: { + cacheGroups: { + main: { + name: 'main', + chunks: 'initial', + enforce: true, + test: 'main', + minSize: 3000 + } + } + } + } +} +" +`; diff --git a/packages/migrate/commonsChunkPlugin/__testfixtures__/commonsChunkPlugin-0.input.js b/packages/migrate/commonsChunkPlugin/__testfixtures__/commonsChunkPlugin-0.input.js new file mode 100644 index 00000000000..5174a051ee1 --- /dev/null +++ b/packages/migrate/commonsChunkPlugin/__testfixtures__/commonsChunkPlugin-0.input.js @@ -0,0 +1,12 @@ +module.exports = { + entry: { + app: './src/app.js', + vendor: './src/vendors.js', + }, + plugins: [ + new webpack.optimize.CommonsChunkPlugin({ + names: ["app", "vendor"], + minChunks: 2 + }) + ] +} diff --git a/packages/migrate/commonsChunkPlugin/__testfixtures__/commonsChunkPlugin-1.input.js b/packages/migrate/commonsChunkPlugin/__testfixtures__/commonsChunkPlugin-1.input.js new file mode 100644 index 00000000000..278b2581639 --- /dev/null +++ b/packages/migrate/commonsChunkPlugin/__testfixtures__/commonsChunkPlugin-1.input.js @@ -0,0 +1,11 @@ +module.exports = { + entry: { + vendor: './src/vendors.js', + }, + + plugins: [ + new webpack.optimize.CommonsChunkPlugin({ + names: ["common", "vendor"] + }) + ] +} diff --git a/packages/migrate/commonsChunkPlugin/__testfixtures__/commonsChunkPlugin-2.input.js b/packages/migrate/commonsChunkPlugin/__testfixtures__/commonsChunkPlugin-2.input.js new file mode 100644 index 00000000000..83a4fb6d3c5 --- /dev/null +++ b/packages/migrate/commonsChunkPlugin/__testfixtures__/commonsChunkPlugin-2.input.js @@ -0,0 +1,11 @@ +module.exports = { + entry: { + vendor: './src/vendors.js', + }, + + plugins: [ + new webpack.optimize.CommonsChunkPlugin({ + names: ["vendor"] + }) + ] +} diff --git a/packages/migrate/commonsChunkPlugin/__testfixtures__/commonsChunkPlugin-3.input.js b/packages/migrate/commonsChunkPlugin/__testfixtures__/commonsChunkPlugin-3.input.js new file mode 100644 index 00000000000..0c4289d6290 --- /dev/null +++ b/packages/migrate/commonsChunkPlugin/__testfixtures__/commonsChunkPlugin-3.input.js @@ -0,0 +1,8 @@ +module.exports = { + plugins: [ + new webpack.optimize.CommonsChunkPlugin({ + name: "commons", + filename: "commons.js", + }) + ] +} diff --git a/packages/migrate/commonsChunkPlugin/__testfixtures__/commonsChunkPlugin-4.input.js b/packages/migrate/commonsChunkPlugin/__testfixtures__/commonsChunkPlugin-4.input.js new file mode 100644 index 00000000000..973867a9866 --- /dev/null +++ b/packages/migrate/commonsChunkPlugin/__testfixtures__/commonsChunkPlugin-4.input.js @@ -0,0 +1,13 @@ +module.exports = { + entry: { + main: './src/index.js', + }, + + plugins: [ + new webpack.optimize.CommonsChunkPlugin({ + name: "main", + async: true, + minSize: 2000, + }) + ] +} diff --git a/packages/migrate/commonsChunkPlugin/__testfixtures__/commonsChunkPlugin-5.input.js b/packages/migrate/commonsChunkPlugin/__testfixtures__/commonsChunkPlugin-5.input.js new file mode 100644 index 00000000000..c7b2dc1c53d --- /dev/null +++ b/packages/migrate/commonsChunkPlugin/__testfixtures__/commonsChunkPlugin-5.input.js @@ -0,0 +1,11 @@ +module.exports = { + entry: { + main: './src/index.js', + }, + + plugins: [ + new webpack.optimize.CommonsChunkPlugin({ + names: ["main", "runtime"], + }) + ] +} diff --git a/packages/migrate/commonsChunkPlugin/__testfixtures__/commonsChunkPlugin-6a.input.js b/packages/migrate/commonsChunkPlugin/__testfixtures__/commonsChunkPlugin-6a.input.js new file mode 100644 index 00000000000..641965277fa --- /dev/null +++ b/packages/migrate/commonsChunkPlugin/__testfixtures__/commonsChunkPlugin-6a.input.js @@ -0,0 +1,12 @@ +module.exports = { + entry: { + main: './src/index.js', + }, + + plugins: [ + new webpack.optimize.CommonsChunkPlugin({ + name: "main", + minChunks: ({ resource }) => /node_modules/.test(resource), + }) + ] +} diff --git a/packages/migrate/commonsChunkPlugin/__testfixtures__/commonsChunkPlugin-6b.input.js b/packages/migrate/commonsChunkPlugin/__testfixtures__/commonsChunkPlugin-6b.input.js new file mode 100644 index 00000000000..fd48b62e34c --- /dev/null +++ b/packages/migrate/commonsChunkPlugin/__testfixtures__/commonsChunkPlugin-6b.input.js @@ -0,0 +1,15 @@ +module.exports = { + entry: { + main: './src/index.js', + }, + + plugins: [ + new webpack.optimize.CommonsChunkPlugin({ + name: "main", + minChunks: ({ resource }) => { + // some code + return /node_modules/.test(resource); + }, + }) + ] +} diff --git a/packages/migrate/commonsChunkPlugin/__testfixtures__/commonsChunkPlugin-6c.input.js b/packages/migrate/commonsChunkPlugin/__testfixtures__/commonsChunkPlugin-6c.input.js new file mode 100644 index 00000000000..9fe78cbc0fa --- /dev/null +++ b/packages/migrate/commonsChunkPlugin/__testfixtures__/commonsChunkPlugin-6c.input.js @@ -0,0 +1,15 @@ +module.exports = { + entry: { + main: './src/index.js', + }, + + plugins: [ + new webpack.optimize.CommonsChunkPlugin({ + name: "main", + minChunks: function ({ resource }) { + // some code + return /node_modules/.test(resource); + }, + }) + ] +} diff --git a/packages/migrate/commonsChunkPlugin/__testfixtures__/commonsChunkPlugin-6d.input.js b/packages/migrate/commonsChunkPlugin/__testfixtures__/commonsChunkPlugin-6d.input.js new file mode 100644 index 00000000000..bab003418a4 --- /dev/null +++ b/packages/migrate/commonsChunkPlugin/__testfixtures__/commonsChunkPlugin-6d.input.js @@ -0,0 +1,18 @@ +module.exports = { + entry: { + main: './src/index.js', + }, + + plugins: [ + new webpack.optimize.CommonsChunkPlugin({ + name: "main", + minChunks: function ({ resource }) { + if (foo) { + return /node_modulesfoo/.test(resource); + } else { + return /node_modulesbaz/.test(resource); + } + } + }) + ] +} diff --git a/packages/migrate/commonsChunkPlugin/__testfixtures__/commonsChunkPlugin-7.input.js b/packages/migrate/commonsChunkPlugin/__testfixtures__/commonsChunkPlugin-7.input.js new file mode 100644 index 00000000000..9999d11bd64 --- /dev/null +++ b/packages/migrate/commonsChunkPlugin/__testfixtures__/commonsChunkPlugin-7.input.js @@ -0,0 +1,12 @@ +module.exports = { + entry: { + main: './src/index.js', + }, + + plugins: [ + new webpack.optimize.CommonsChunkPlugin({ + name: "main", + minSize: 3000, + }), + ] +} diff --git a/packages/migrate/commonsChunkPlugin/commonsChunkPlugin.test.js b/packages/migrate/commonsChunkPlugin/commonsChunkPlugin.test.js new file mode 100644 index 00000000000..123b3ba81d8 --- /dev/null +++ b/packages/migrate/commonsChunkPlugin/commonsChunkPlugin.test.js @@ -0,0 +1,15 @@ +"use strict"; + +const defineTest = require("@webpack-cli/utils/defineTest").default; + +defineTest(__dirname, "commonsChunkPlugin", "commonsChunkPlugin-0"); +defineTest(__dirname, "commonsChunkPlugin", "commonsChunkPlugin-1"); +defineTest(__dirname, "commonsChunkPlugin", "commonsChunkPlugin-2"); +defineTest(__dirname, "commonsChunkPlugin", "commonsChunkPlugin-3"); +defineTest(__dirname, "commonsChunkPlugin", "commonsChunkPlugin-4"); +defineTest(__dirname, "commonsChunkPlugin", "commonsChunkPlugin-5"); +defineTest(__dirname, "commonsChunkPlugin", "commonsChunkPlugin-6a"); +defineTest(__dirname, "commonsChunkPlugin", "commonsChunkPlugin-6b"); +defineTest(__dirname, "commonsChunkPlugin", "commonsChunkPlugin-6c"); +defineTest(__dirname, "commonsChunkPlugin", "commonsChunkPlugin-6d"); +defineTest(__dirname, "commonsChunkPlugin", "commonsChunkPlugin-7"); diff --git a/packages/migrate/commonsChunkPlugin/commonsChunkPlugin.ts b/packages/migrate/commonsChunkPlugin/commonsChunkPlugin.ts new file mode 100644 index 00000000000..ea86fb371ba --- /dev/null +++ b/packages/migrate/commonsChunkPlugin/commonsChunkPlugin.ts @@ -0,0 +1,321 @@ +import { + addOrUpdateConfigObject, + createIdentifierOrLiteral, + createProperty, + findAndRemovePluginByName, + findPluginsByName, + findRootNodesByName, +} from "@webpack-cli/utils/ast-utils"; + +import { IJSCodeshift, INode } from "../types/NodePath"; + +/** + * + * Transform for CommonsChunkPlugin. If found, removes the + * plugin and sets optimizations.namedModules to true + * + * @param {Object} j - jscodeshift top-level import + * @param {Node} ast - jscodeshift ast to transform + * @returns {Node} ast - jscodeshift ast + */ +export default function(j: IJSCodeshift, ast: INode): INode { + const splitChunksProps: INode[] = []; + const cacheGroupsProps: INode[] = []; + const optimizationProps: object = {}; + + let commonCacheGroupsProps: INode[] = [ + createProperty(j, "chunks", "initial"), + createProperty(j, "enforce", true), + ]; + + // find old options + const CommonsChunkPlugin: INode = findPluginsByName(j, ast, [ + "webpack.optimize.CommonsChunkPlugin", + ]); + + if (!CommonsChunkPlugin.size()) { + return ast; + } + + // cache group options based on keys + let cacheGroup: object = {}; + let cacheGroups: INode[] = []; + + // iterate each CommonsChunkPlugin instance + CommonsChunkPlugin.forEach( + (path: INode): void => { + const CCPProps: INode[] = path.value.arguments[0].properties; + + // reset chunks from old props + cacheGroup = {}; + cacheGroups = []; + + commonCacheGroupsProps = [ + createProperty(j, "chunks", "initial"), + createProperty(j, "enforce", true), + ]; + + let chunkKey: string; + let chunkCount: number = 0; + + // iterate CCP props and map SCP props + CCPProps.forEach( + (p: INode): void => { + const propKey: string = p.key.name; + + switch (propKey) { + case "names": + p.value.elements.forEach(({ value: chunkValue }): void => { + if (chunkValue === "runtime") { + optimizationProps["runtimeChunk"] = j.objectExpression([ // tslint:disable-line + createProperty(j, "name", chunkValue), + ]); + } else { + if (!Array.isArray(cacheGroup[chunkValue])) { + cacheGroup[chunkValue] = []; + } + + findRootNodesByName(j, ast, "entry").forEach( + ({ value: { value: { properties: entries }} }, + ): void => { + chunkCount = entries.length; + entries.forEach(({ key: { name: entryName }}): void => { + if (entryName === chunkValue) { + cacheGroup[chunkValue].push( + createProperty(j, "test", entryName), + ); + } + }); + }); + } + }); + break; + + case "name": + const nameKey = p.value.value; + + if (nameKey === "runtime") { + optimizationProps["runtimeChunk"] = j.objectExpression([ // tslint:disable-line + createProperty(j, "name", nameKey), + ]); + } else { + chunkKey = nameKey; + + if (!Array.isArray(cacheGroup[nameKey])) { + cacheGroup[nameKey] = []; + } + + findRootNodesByName(j, ast, "entry").forEach( + ({ value: { value: { properties: entries }} }, + ): void => { + chunkCount = entries.length; + entries.forEach(({ key: { name: entryName }}): void => { + if (entryName === nameKey) { + cacheGroup[nameKey].push( + createProperty(j, "test", entryName), + ); + } + }); + }); + } + break; + + case "filename": + if (chunkKey) { + if (!Array.isArray(cacheGroup[chunkKey])) { + cacheGroup[chunkKey] = []; + } + cacheGroup[chunkKey].push( + createProperty(j, propKey, p.value.value), + ); + } + break; + + case "async": + if (!Array.isArray(cacheGroup[chunkKey])) { + cacheGroup[chunkKey] = []; + } + cacheGroup[chunkKey].push( + createProperty(j, "chunks", "async"), + ); + break; + + case "minSize": + if (!Array.isArray(cacheGroup[chunkKey])) { + cacheGroup[chunkKey] = []; + } + cacheGroup[chunkKey].push( + j.property("init", createIdentifierOrLiteral(j, propKey), p.value), + ); + break; + + case "minChunks" : + const { value: pathValue }: INode = p; + + // minChunk is a function + if ( + pathValue.type === "ArrowFunctionExpression" || + pathValue.type === "FunctionExpression" + ) { + if (!Array.isArray(cacheGroup[chunkKey])) { + cacheGroup[chunkKey] = []; + } + + cacheGroup[chunkKey] = cacheGroup[chunkKey].map((prop) => + prop.key.name === "test" ? mergeTestPropArrowFunction(j, chunkKey, pathValue) : prop); + } + break; + } + }, + ); + + Object.keys(cacheGroup).forEach((chunkName: string): void => { + let chunkProps: INode[] = [ + createProperty(j, "name", chunkName), + ]; + + const chunkPropsToAdd = cacheGroup[chunkName]; + const chunkPropsKeys = chunkPropsToAdd.map((prop) => prop.key.name); + + commonCacheGroupsProps = + commonCacheGroupsProps.filter((commonProp) => !chunkPropsKeys.includes(commonProp.key.name)); + + chunkProps.push(...commonCacheGroupsProps); + + if (chunkCount > 1) { + chunkProps.push( + j.property( + "init", + createIdentifierOrLiteral(j, "minChunks"), + createIdentifierOrLiteral(j, chunkCount), + ), + ); + } + + const chunkPropsContainTest = + chunkPropsToAdd.some((prop) => prop.key.name === "test" && prop.value.type === "Literal"); + + if (chunkPropsContainTest) { + chunkProps = chunkProps.filter((prop) => prop.key.name !== "minChunks"); + } + + if ( + chunkPropsToAdd && + Array.isArray(chunkPropsToAdd) && + chunkPropsToAdd.length > 0 + ) { + chunkProps.push(...chunkPropsToAdd); + } + + cacheGroups.push( + j.property( + "init", + createIdentifierOrLiteral(j, chunkName), + j.objectExpression([...chunkProps]), + ), + ); + }); + + if (cacheGroups.length > 0) { + cacheGroupsProps.push(...cacheGroups); + } + }, + ); + + // Remove old plugin + const root: INode = findAndRemovePluginByName( + j, + ast, + "webpack.optimize.CommonsChunkPlugin", + ); + + const rootProps: INode[] = [...splitChunksProps]; + + if (cacheGroupsProps.length > 0) { + rootProps.push( + j.property( + "init", + createIdentifierOrLiteral(j, "cacheGroups"), + j.objectExpression([...cacheGroupsProps]), + ), + ); + } + + // Add new optimizations splitChunks option + if (root) { + addOrUpdateConfigObject( + j, + root, + "optimizations", + "splitChunks", + j.objectExpression([...rootProps]), + ); + + Object.keys(optimizationProps).forEach((key: string): void => { + addOrUpdateConfigObject( + j, + root, + "optimizations", + key, + optimizationProps[key], + ); + }); + } + + return ast; +} + +// merge test entry prop and function expression. case 6[x] +const mergeTestPropArrowFunction = (j, chunkKey, testFunc) => { + return j.property( + "init", + createIdentifierOrLiteral(j, "test"), + j.arrowFunctionExpression( + [j.identifier("module")], + j.blockStatement([ + j.ifStatement( + j.callExpression( + j.memberExpression( + j.callExpression( + j.memberExpression( + j.identifier("module"), + j.identifier("getChunks"), + ), + [], + ), + j.identifier("some"), + false, + ), + [j.arrowFunctionExpression( + [j.identifier("chunk")], + j.binaryExpression( + "===", + j.memberExpression( + j.identifier("chunk"), + j.identifier("name"), + ), + j.literal(chunkKey), + ), + )], + ), + j.returnStatement( + j.literal(true), + ), + ), + j.variableDeclaration( + "const", + [j.variableDeclarator( + j.identifier("fn"), + testFunc, + )], + ), + j.returnStatement( + j.callExpression( + j.identifier("fn"), + [j.identifier("module")], + ), + ), + ]), + ), + ); +}; diff --git a/packages/migrate/migrate.ts b/packages/migrate/migrate.ts index f93cded0c94..6d3ad4ec6f3 100644 --- a/packages/migrate/migrate.ts +++ b/packages/migrate/migrate.ts @@ -3,6 +3,7 @@ import pEachSeries = require("p-each-series"); import pLazy = require("p-lazy"); import bannerPluginTransform from "./bannerPlugin/bannerPlugin"; +import commonsChunkPluginTransform from "./commonsChunkPlugin/commonsChunkPlugin"; import extractTextPluginTransform from "./extractTextPlugin/extractTextPlugin"; import loaderOptionsPluginTransform from "./loaderOptionsPlugin/loaderOptionsPlugin"; import loadersTransform from "./loaders/loaders"; @@ -15,6 +16,7 @@ import uglifyJsPluginTransform from "./uglifyJsPlugin/uglifyJsPlugin"; interface ITransformsObject { bannerPluginTransform: object; + commonsChunkPluginTransform?: object; extractTextPluginTransform: object; loaderOptionsPluginTransform: object; loadersTransform: object; @@ -36,6 +38,7 @@ const transformsObject: ITransformsObject = { extractTextPluginTransform, noEmitOnErrorsPluginTransform, removeDeprecatedPluginsTransform, + commonsChunkPluginTransform, }; /* tslint:enable object-literal-sort-keys */ diff --git a/packages/migrate/types/NodePath.ts b/packages/migrate/types/NodePath.ts index d082ddc6587..e40bdf3cba8 100644 --- a/packages/migrate/types/NodePath.ts +++ b/packages/migrate/types/NodePath.ts @@ -71,6 +71,12 @@ export interface IJSCodeshift extends Object { property?: (type: string, key: INode, value: INode) => INode; program?: (nodes: INode[]) => INode; booleanLiteral?: (bool: boolean) => INode; + arrowFunctionExpression?: (params: INode[], body: INode, exp: INode) => INode; + blockStatement?: (body: INode[]) => INode; + ifStatement?: (test: INode, consequent: INode, alternate?: INode) => INode; + returnStatement?: (arg: INode) => INode; + binaryExpression?: (operator: string, left: INode, right: INode) => INode; + Property?: IExpressionObject; NewExpression?: IExpressionObject; CallExpression?: IExpressionObject; @@ -83,6 +89,7 @@ export interface IJSCodeshift extends Object { ObjectExpression?: IExpressionObject; BlockStatement?: IExpressionObject; Program?: IExpressionObject; + ArrowFunctionExpression?: IExpressionObject; filters?: { VariableDeclarator: { requiresModule: Function,