From c24399496d98a1c0ee597569f2e7b00b77cd963b Mon Sep 17 00:00:00 2001 From: Brie <723547+brieb@users.noreply.github.com> Date: Wed, 9 Jan 2019 15:15:47 -0800 Subject: [PATCH 01/15] [jscodeshift] Run `npx dts-gen --dt --name jscodeshift --template module` --- types/jscodeshift/index.d.ts | 39 ++++++++++++++++++++++++++ types/jscodeshift/jscodeshift-tests.ts | 0 types/jscodeshift/tsconfig.json | 22 +++++++++++++++ types/jscodeshift/tslint.json | 1 + 4 files changed, 62 insertions(+) create mode 100644 types/jscodeshift/index.d.ts create mode 100644 types/jscodeshift/jscodeshift-tests.ts create mode 100644 types/jscodeshift/tsconfig.json create mode 100644 types/jscodeshift/tslint.json diff --git a/types/jscodeshift/index.d.ts b/types/jscodeshift/index.d.ts new file mode 100644 index 0000000000..7d1fe12f72 --- /dev/null +++ b/types/jscodeshift/index.d.ts @@ -0,0 +1,39 @@ +// Type definitions for jscodeshift 0.6 +// Project: https://github.com/facebook/jscodeshift#readme +// Definitions by: My Self +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped + +/*~ If this module is a UMD module that exposes a global variable 'myLib' when + *~ loaded outside a module loader environment, declare that global here. + *~ Otherwise, delete this declaration. + */ +export as namespace myLib; + +/*~ If this module has methods, declare them as functions like so. + */ +export function myMethod(a: string): string; +export function myOtherMethod(a: number): number; + +/*~ You can declare types that are available via importing the module */ +export interface someType { + name: string; + length: number; + extras?: string[]; +} + +/*~ You can declare properties of the module using const, let, or var */ +export const myField: number; + +/*~ If there are types, properties, or methods inside dotted names + *~ of the module, declare them inside a 'namespace'. + */ +export namespace subProp { + /*~ For example, given this definition, someone could write: + *~ import { subProp } from 'yourModule'; + *~ subProp.foo(); + *~ or + *~ import * as yourMod from 'yourModule'; + *~ yourMod.subProp.foo(); + */ + export function foo(): void; +} \ No newline at end of file diff --git a/types/jscodeshift/jscodeshift-tests.ts b/types/jscodeshift/jscodeshift-tests.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/types/jscodeshift/tsconfig.json b/types/jscodeshift/tsconfig.json new file mode 100644 index 0000000000..726b5b889f --- /dev/null +++ b/types/jscodeshift/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "module": "commonjs", + "lib": [ + "es6" + ], + "noImplicitAny": true, + "noImplicitThis": true, + "strictNullChecks": true, + "baseUrl": "../", + "typeRoots": [ + "../" + ], + "types": [], + "noEmit": true, + "forceConsistentCasingInFileNames": true + }, + "files": [ + "index.d.ts", + "jscodeshift-tests.ts" + ] +} diff --git a/types/jscodeshift/tslint.json b/types/jscodeshift/tslint.json new file mode 100644 index 0000000000..3db14f85ea --- /dev/null +++ b/types/jscodeshift/tslint.json @@ -0,0 +1 @@ +{ "extends": "dtslint/dt.json" } From 3d12a04ac4f3591d580eccbcb4e1b6bcbb14f8e1 Mon Sep 17 00:00:00 2001 From: Brie <723547+brieb@users.noreply.github.com> Date: Wed, 9 Jan 2019 15:39:35 -0800 Subject: [PATCH 02/15] [jscodeshift] Add types for jscodeshift 0.6 --- types/jscodeshift/index.d.ts | 448 +++++++++++++++++++++++-- types/jscodeshift/jscodeshift-tests.ts | 50 +++ types/jscodeshift/package.json | 7 + types/jscodeshift/tsconfig.json | 1 + 4 files changed, 477 insertions(+), 29 deletions(-) create mode 100644 types/jscodeshift/package.json diff --git a/types/jscodeshift/index.d.ts b/types/jscodeshift/index.d.ts index 7d1fe12f72..3e91d382ba 100644 --- a/types/jscodeshift/index.d.ts +++ b/types/jscodeshift/index.d.ts @@ -1,39 +1,429 @@ // Type definitions for jscodeshift 0.6 // Project: https://github.com/facebook/jscodeshift#readme -// Definitions by: My Self +// Definitions by: Brie Bunge // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// TypeScript Version: 2.9 -/*~ If this module is a UMD module that exposes a global variable 'myLib' when - *~ loaded outside a module loader environment, declare that global here. - *~ Otherwise, delete this declaration. - */ -export as namespace myLib; +declare module "jscodeshift" { + import { Collection, registerMethods } from "jscodeshift/src/Collection"; + import * as JSXElement from "jscodeshift/src/collections/JSXElement"; + import * as VariableDeclarator from "jscodeshift/src/collections/VariableDeclarator"; + import { Template } from "jscodeshift/src/template"; + import recast, { Builders, NamedTypes, NodePath, Options, Parser } from "recast"; + import { ASTNode } from "ast-types/gen/nodes"; -/*~ If this module has methods, declare them as functions like so. - */ -export function myMethod(a: string): string; -export function myOtherMethod(a: number): number; + export type ASTPath = NodePath; -/*~ You can declare types that are available via importing the module */ -export interface someType { - name: string; - length: number; - extras?: string[]; + export interface Filters { + JSXElement: JSXElement.FilterMethods; + VariableDeclarator: VariableDeclarator.FilterMethods; + } + + export interface Mappings { + JSXElement: JSXElement.MappingMethods; + } + + export interface Plugin { + (core: Core): void; + } + + export interface FileInfo { + /** The absolute path to the current file. */ + path: string; + /** The source code of the current file. */ + source: string; + } + + export interface Stats { + /** + * Helper function to collect data during --dry runs. + * This function keeps a counter for how often it was called with a specific argument. + * The result is shown in the console. Useful for finding out how many files match a criterion. + */ + (name: string, quantity?: number): void; + } + + interface Core { + (source: string, options?: Options): Collection; + (source: ASTNode | ASTNode[] | ASTPath | ASTPath[]): Collection; + + registerMethods: typeof registerMethods; + + types: typeof recast.types; + + match(path: ASTNode | ASTPath, filter: ((path: ASTNode) => boolean) | ASTNode): boolean; + + /** template, bound to default parser */ + template: Template; + + filters: Filters; + + mappings: Mappings; + + /** + * Utility function for registering plugins. + * + * Plugins are simple functions that are passed the core jscodeshift instance. + * They should extend jscodeshift by calling `registerMethods`, etc. + * This method guards against repeated registrations (the plugin callback will only be called once). + */ + use(plugin: Plugin): void; + + /** + * Returns a version of the core jscodeshift function "bound" to a specific + * parser. + */ + withParser(parser: string | Parser): JSCodeshift; + } + + type JSCodeshift = Core & NamedTypes & Builders; + + const core: JSCodeshift; + export default core; + + export interface API { + j: JSCodeshift; + jscodeshift: JSCodeshift; + stats: Stats; + report: (msg: string) => void; + } + + export interface Options { + [option: string]: any; + } + + export interface Transform { + /** + * If a string is returned and it is different from passed source, the transform is considered to be successful. + * If a string is returned but it's the same as the source, the transform is considered to be unsuccessful. + * If nothing is returned, the file is not supposed to be transformed (which is ok). + */ + (file: FileInfo, api: API, options: Options): string | null | undefined | void; + } + + export * from "ast-types/gen/nodes"; + export { Collection, Parser }; } -/*~ You can declare properties of the module using const, let, or var */ -export const myField: number; +declare module "jscodeshift/src/template" { + import { Parser } from "recast"; -/*~ If there are types, properties, or methods inside dotted names - *~ of the module, declare them inside a 'namespace'. - */ -export namespace subProp { - /*~ For example, given this definition, someone could write: - *~ import { subProp } from 'yourModule'; - *~ subProp.foo(); - *~ or - *~ import * as yourMod from 'yourModule'; - *~ yourMod.subProp.foo(); + export interface Template { + /** Tagged template function. Parses the string as source and returns an array of Statement AST nodes. */ + statements(...args: any[]): any; + /** Tagged template function. Parses the string as source and returns an Statement AST node. */ + statement(...args: any[]): any; + /** Tagged template function. Parses the string as source and returns an Expression AST node. */ + expression(...args: any[]): any; + } + + export default function withParser(parser: Parser): Template; + + export {}; // force module +} + +declare module "jscodeshift/src/Collection" { + import * as JSXElement from "jscodeshift/src/collections/JSXElement"; + import * as NodeCollection from "jscodeshift/src/collections/Node"; + import * as VariableDeclarator from "jscodeshift/src/collections/VariableDeclarator"; + import recast, { ASTNode, NodePath, Options } from "recast"; + + type Type = typeof recast.types.Type; + type ASTPath = NodePath; + + interface Collection + extends NodeCollection.TraversalMethods, + NodeCollection.MutationMethods, + VariableDeclarator.GlobalMethods, + VariableDeclarator.TransformMethods, + JSXElement.GlobalMethods, + JSXElement.TraversalMethods { + /** + * @param paths An array of AST paths + * @param parent A parent collection + * @param types An array of types all the paths in the collection + * have in common. If not passed, it will be inferred from the paths. + */ + new (paths: ASTPath[], parent: Collection, types?: Type[]): this; + + /** + * Returns a new collection containing the nodes for which the callback returns true. + */ + filter( + callback: (path: ASTPath, i: number, paths: ASTPath[]) => path is ASTPath + ): Collection; + filter( + callback: (path: ASTPath, i: number, paths: ASTPath[]) => boolean + ): Collection; + + /** + * Executes callback for each node/path in the collection. + */ + forEach(callback: (path: ASTPath, i: number, paths: ASTPath[]) => void): this; + + /** + * Tests whether at-least one path passes the test implemented by the provided callback. + */ + some(callback: (path: ASTPath, i: number, paths: ASTPath[]) => boolean): boolean; + + /** + * Tests whether all paths pass the test implemented by the provided callback. + */ + every(callback: (path: ASTPath, i: number, paths: ASTPath[]) => boolean): boolean; + + /** + * Executes the callback for every path in the collection and returns a new + * collection from the return values (which must be paths). + * + * The callback can return null to indicate to exclude the element from the + * new collection. + * + * If an array is returned, the array will be flattened into the result + * collection. + * + * @param callback + * @param type Force the new collection to be of a specific type + */ + map( + callback: ( + path: ASTPath, + i: number, + paths: ASTPath[] + ) => ASTPath | ASTPath[] | null | undefined, + type: Type + ): Collection; + + /** Returns the number of elements in this collection. */ + size(): number; + + /** Returns the number of elements in this collection. */ + length: number; + + /** Returns an array of AST nodes in this collection. */ + nodes(): N[]; + + /** Returns an array of ASTPaths in this this collection. */ + paths(): ASTPath[]; + + getAST(): ASTPath[]; + + /** + * Converts the AST back to a string, using recast. + * @param options directly passed to recast's printer + */ + toSource(options?: Options): string; + + /** + * Returns a new collection containing only the element at position index. + * In case of a negative index, the element is taken from the end: + * .at(0) - first element + * .at(-1) - last element + */ + at(index: number): Collection; + + /** Calls "get" on the first path (same as "collection.paths(0).get(...)"). */ + get(...fields: (string | number)[]): T; + + /** + * Returns the type(s) of the collection. This is only used for unit tests, + * don't think other consumers would need it. + */ + getTypes(): string[]; + + /** + * Returns true if this collection has the type 'type'. + */ + isOfType(type: Type): boolean; + } + + /** + * This function adds the provided methods to the prototype of the corresponding + * typed collection. If no type is passed, the methods are added to + * Collection.prototype and are available for all collections. + * + * @param methods Methods to add to the prototype + * @param type Optional type to add the methods to */ - export function foo(): void; -} \ No newline at end of file + export function registerMethods(methods: object, type?: Type): void; +} + +declare module "jscodeshift/src/collections/Node" { + import { Collection } from "jscodeshift/src/Collection"; + import { ASTNode, Type } from "recast"; + + export interface TraversalMethods { + /** + * Find nodes of a specific type within the nodes of this collection. + */ + find(type: Type): Collection; + find(type: Type, filter: (value: any) => boolean): Collection; + find(type: Type, filter: object): Collection; + + /** + * Returns a collection containing the paths that create the scope of the + * currently selected paths. Dedupes the paths. + */ + closestScope(): Collection; + + /** + * Traverse the AST up and finds the closest node of the provided type. + */ + closest(type: Type, filter?: any): Collection; + + /** + * Finds the declaration for each selected path. Useful for member expressions + * or JSXElements. Expects a callback function that maps each path to the name + * to look for. + * + * If the callback returns a falsey value, the element is skipped. + */ + getVariableDeclarators(nameGetter: Function): Collection; + } + + export interface MutationMethods { + /** + * Simply replaces the selected nodes with the provided node. If a function + * is provided it is executed for every node and the node is replaced with the + * functions return value. + * + * @param {Node|Array|function} nodes + */ + replaceWith(nodes: T | T[] | ((path: any, i: number) => T)): this; + + /** + * Inserts a new node before the current one. + * + * @param {Node|Array|function} insert + */ + insertBefore(insert: any): Collection; + + /** + * Inserts a new node after the current one. + * + * @param {Node|Array|function} insert + */ + insertAfter(insert: any): Collection; + + remove(): Collection; + } + + export function register(): void; + + export {}; // force module +} + +declare module "jscodeshift/src/collections/VariableDeclarator" { + import { VariableDeclarator } from "ast-types/gen/nodes"; + import { Collection } from "jscodeshift/src/Collection"; + import recast, { NodePath } from "recast"; + + type Node = typeof recast.types.namedTypes.Node; + type ASTPath = NodePath; + + export interface GlobalMethods { + /** + * Finds all variable declarators, optionally filtered by name. + */ + findVariableDeclarators(name?: string): Collection; + } + + export interface TransformMethods { + /** + * Renames a variable and all its occurrences. + * This method only applies to VariableDeclarator typed collections. + */ + renameTo(newName: string): Collection; + } + + interface Filter { + (path: ASTPath): boolean; + } + + export interface FilterMethods { + /** + * Returns a function that returns true if the provided path is a variable + * declarator and requires one of the specified module names. + * + * @param names A module name or an array of module names + */ + requiresModule(names: string | string[]): Filter; + } + + export function register(): void; + export const filters: FilterMethods; + + export {}; // force module +} + +declare module "jscodeshift/src/collections/JSXElement" { + import { JSXElement } from "ast-types/gen/nodes"; + import { Collection } from "jscodeshift/src/Collection"; + import { NodePath } from "recast"; + + type ASTPath = NodePath; + + export interface GlobalMethods { + /** + * Finds all JSXElements optionally filtered by name + */ + findJSXElements(name?: string): Collection; + + /** + * Finds all JSXElements by module name. Given + * + * var Bar = require('Foo'); + * + * + * findJSXElementsByModuleName('Foo') will find , without having to + * know the variable name. + */ + findJSXElementsByModuleName(moduleName: string): Collection; + } + + type Defined = T extends undefined ? never : T; + type JSXElementChild = Defined[0]; + + export interface TraversalMethods { + /** + * Returns all child nodes, including literals and expressions. + * This method only applies to JSXElement typed collections. + */ + childNodes(): Collection; + + /** + * Returns all children that are JSXElements. + * This method only applies to JSXElement typed collections. + */ + childElements(): Collection; + } + + interface Filter { + (path: ASTPath): boolean; + } + + export interface FilterMethods { + /** + * Filter method for attributes. + */ + hasAttributes(attributeFilter: { [attributeName: string]: any }): Filter; + + /** + * Filter elements which contain a specific child type + */ + hasChildren(name: string): Filter; + } + + export interface MappingMethods { + /** + * Given a JSXElement, returns its "root" name. E.g. it would return "Foo" for + * both and . + */ + getRootName(path: ASTPath): string; + } + + export function register(): void; + export const filters: FilterMethods; + export const mappings: MappingMethods; + + export {}; // force module +} diff --git a/types/jscodeshift/jscodeshift-tests.ts b/types/jscodeshift/jscodeshift-tests.ts index e69de29bb2..8631e400c4 100644 --- a/types/jscodeshift/jscodeshift-tests.ts +++ b/types/jscodeshift/jscodeshift-tests.ts @@ -0,0 +1,50 @@ +import { ASTNode, FileInfo, API, Transform } from "jscodeshift"; + +// Can define transform with `function`. +function replaceWithFooTransform(fileInfo: FileInfo, api: API) { + return api + .jscodeshift(fileInfo.source) + .findVariableDeclarators("foo") + .renameTo("bar") + .toSource(); +} + +// Can define transform with arrow function, using `Transform` type. +const reverseIdentifiersTransform: Transform = (file, api) => { + const j = api.jscodeshift; + + return j(file.source) + .find(j.Identifier) + .forEach(path => { + j(path).replaceWith( + j.identifier( + path.node.name + .split("") + .reverse() + .join("") + ) + ); + }) + .toSource(); +}; + +// `ASTNode` supports type narrowing. +{ + const node = ({} as any) as ASTNode; + if (node.type === "CatchClause") { + // `node` is narrowed to `CatchClause` here + if (node.param && node.param.type === "Identifier") { + // `node.param` is narrowed to `Identifier` here + if ( + node.param.typeAnnotation && + node.param.typeAnnotation.type === "TSTypeAnnotation" + ) { + // `node.param.typeAnnotation` is narrowed to `TSTypeAnnotation` here + if (node.param.typeAnnotation.typeAnnotation.type === "TSArrayType") { + // `node.param.typeAnnotation.typeAnnotation` is narrowed to `TSArrayType` here + node.param.typeAnnotation.typeAnnotation.elementType; + } + } + } + } +} diff --git a/types/jscodeshift/package.json b/types/jscodeshift/package.json new file mode 100644 index 0000000000..65d03a5a7f --- /dev/null +++ b/types/jscodeshift/package.json @@ -0,0 +1,7 @@ +{ + "private": true, + "dependencies": { + "ast-types": "^0.12.0", + "recast": "^0.17.0" + } +} diff --git a/types/jscodeshift/tsconfig.json b/types/jscodeshift/tsconfig.json index 726b5b889f..f1ad7409d3 100644 --- a/types/jscodeshift/tsconfig.json +++ b/types/jscodeshift/tsconfig.json @@ -7,6 +7,7 @@ "noImplicitAny": true, "noImplicitThis": true, "strictNullChecks": true, + "strictFunctionTypes": true, "baseUrl": "../", "typeRoots": [ "../" From 3e712a34cb4fca323d48ca5d627fa846947cacde Mon Sep 17 00:00:00 2001 From: Brie <723547+brieb@users.noreply.github.com> Date: Wed, 9 Jan 2019 15:51:32 -0800 Subject: [PATCH 03/15] [jscodeshift] Fix lints --- types/jscodeshift/index.d.ts | 46 +++++++++++++++-------------------- types/jscodeshift/tslint.json | 9 ++++++- 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/types/jscodeshift/index.d.ts b/types/jscodeshift/index.d.ts index 3e91d382ba..391b4a4f13 100644 --- a/types/jscodeshift/index.d.ts +++ b/types/jscodeshift/index.d.ts @@ -119,7 +119,7 @@ declare module "jscodeshift/src/template" { export default function withParser(parser: Parser): Template; - export {}; // force module + export {}; // to shut off automatic exporting } declare module "jscodeshift/src/Collection" { @@ -131,7 +131,7 @@ declare module "jscodeshift/src/Collection" { type Type = typeof recast.types.Type; type ASTPath = NodePath; - interface Collection + export interface Collection extends NodeCollection.TraversalMethods, NodeCollection.MutationMethods, VariableDeclarator.GlobalMethods, @@ -144,32 +144,32 @@ declare module "jscodeshift/src/Collection" { * @param types An array of types all the paths in the collection * have in common. If not passed, it will be inferred from the paths. */ - new (paths: ASTPath[], parent: Collection, types?: Type[]): this; + new (paths: Array>, parent: Collection, types?: Type[]): this; /** * Returns a new collection containing the nodes for which the callback returns true. */ filter( - callback: (path: ASTPath, i: number, paths: ASTPath[]) => path is ASTPath + callback: (path: ASTPath, i: number, paths: Array>) => path is ASTPath ): Collection; filter( - callback: (path: ASTPath, i: number, paths: ASTPath[]) => boolean + callback: (path: ASTPath, i: number, paths: Array>) => boolean ): Collection; /** * Executes callback for each node/path in the collection. */ - forEach(callback: (path: ASTPath, i: number, paths: ASTPath[]) => void): this; + forEach(callback: (path: ASTPath, i: number, paths: Array>) => void): this; /** * Tests whether at-least one path passes the test implemented by the provided callback. */ - some(callback: (path: ASTPath, i: number, paths: ASTPath[]) => boolean): boolean; + some(callback: (path: ASTPath, i: number, paths: Array>) => boolean): boolean; /** * Tests whether all paths pass the test implemented by the provided callback. */ - every(callback: (path: ASTPath, i: number, paths: ASTPath[]) => boolean): boolean; + every(callback: (path: ASTPath, i: number, paths: Array>) => boolean): boolean; /** * Executes the callback for every path in the collection and returns a new @@ -188,8 +188,8 @@ declare module "jscodeshift/src/Collection" { callback: ( path: ASTPath, i: number, - paths: ASTPath[] - ) => ASTPath | ASTPath[] | null | undefined, + paths: Array> + ) => ASTPath | Array> | null | undefined, type: Type ): Collection; @@ -203,9 +203,9 @@ declare module "jscodeshift/src/Collection" { nodes(): N[]; /** Returns an array of ASTPaths in this this collection. */ - paths(): ASTPath[]; + paths(): Array>; - getAST(): ASTPath[]; + getAST(): Array>; /** * Converts the AST back to a string, using recast. @@ -222,7 +222,7 @@ declare module "jscodeshift/src/Collection" { at(index: number): Collection; /** Calls "get" on the first path (same as "collection.paths(0).get(...)"). */ - get(...fields: (string | number)[]): T; + get(...fields: Array): any; /** * Returns the type(s) of the collection. This is only used for unit tests, @@ -245,6 +245,8 @@ declare module "jscodeshift/src/Collection" { * @param type Optional type to add the methods to */ export function registerMethods(methods: object, type?: Type): void; + + export {}; // to shut off automatic exporting } declare module "jscodeshift/src/collections/Node" { @@ -255,9 +257,7 @@ declare module "jscodeshift/src/collections/Node" { /** * Find nodes of a specific type within the nodes of this collection. */ - find(type: Type): Collection; - find(type: Type, filter: (value: any) => boolean): Collection; - find(type: Type, filter: object): Collection; + find(type: Type, filter?: ((value: any) => boolean) | object): Collection; /** * Returns a collection containing the paths that create the scope of the @@ -277,7 +277,7 @@ declare module "jscodeshift/src/collections/Node" { * * If the callback returns a falsey value, the element is skipped. */ - getVariableDeclarators(nameGetter: Function): Collection; + getVariableDeclarators(nameGetter: (...args: any[]) => any): Collection; } export interface MutationMethods { @@ -285,22 +285,16 @@ declare module "jscodeshift/src/collections/Node" { * Simply replaces the selected nodes with the provided node. If a function * is provided it is executed for every node and the node is replaced with the * functions return value. - * - * @param {Node|Array|function} nodes */ replaceWith(nodes: T | T[] | ((path: any, i: number) => T)): this; /** * Inserts a new node before the current one. - * - * @param {Node|Array|function} insert */ insertBefore(insert: any): Collection; /** * Inserts a new node after the current one. - * - * @param {Node|Array|function} insert */ insertAfter(insert: any): Collection; @@ -309,7 +303,7 @@ declare module "jscodeshift/src/collections/Node" { export function register(): void; - export {}; // force module + export {}; // to shut off automatic exporting } declare module "jscodeshift/src/collections/VariableDeclarator" { @@ -352,7 +346,7 @@ declare module "jscodeshift/src/collections/VariableDeclarator" { export function register(): void; export const filters: FilterMethods; - export {}; // force module + export {}; // to shut off automatic exporting } declare module "jscodeshift/src/collections/JSXElement" { @@ -425,5 +419,5 @@ declare module "jscodeshift/src/collections/JSXElement" { export const filters: FilterMethods; export const mappings: MappingMethods; - export {}; // force module + export {}; // to shut off automatic exporting } diff --git a/types/jscodeshift/tslint.json b/types/jscodeshift/tslint.json index 3db14f85ea..94991f356f 100644 --- a/types/jscodeshift/tslint.json +++ b/types/jscodeshift/tslint.json @@ -1 +1,8 @@ -{ "extends": "dtslint/dt.json" } +{ + "extends": "dtslint/dt.json", + "rules": { + "no-declare-current-package": false, + "no-self-import": false, + "no-unnecessary-generics": false + } +} From 0cc491505e8284969ef2720aa10bf42c20b31ac6 Mon Sep 17 00:00:00 2001 From: Brie <723547+brieb@users.noreply.github.com> Date: Wed, 9 Jan 2019 16:16:12 -0800 Subject: [PATCH 04/15] [jscodeshift] Additional test cases --- types/jscodeshift/index.d.ts | 12 ++++----- types/jscodeshift/jscodeshift-tests.ts | 37 +++++++++++++++++++++----- 2 files changed, 36 insertions(+), 13 deletions(-) diff --git a/types/jscodeshift/index.d.ts b/types/jscodeshift/index.d.ts index 391b4a4f13..b9b15be825 100644 --- a/types/jscodeshift/index.d.ts +++ b/types/jscodeshift/index.d.ts @@ -126,9 +126,8 @@ declare module "jscodeshift/src/Collection" { import * as JSXElement from "jscodeshift/src/collections/JSXElement"; import * as NodeCollection from "jscodeshift/src/collections/Node"; import * as VariableDeclarator from "jscodeshift/src/collections/VariableDeclarator"; - import recast, { ASTNode, NodePath, Options } from "recast"; + import recast, { ASTNode, NodePath, Options, Type } from "recast"; - type Type = typeof recast.types.Type; type ASTPath = NodePath; export interface Collection @@ -144,7 +143,7 @@ declare module "jscodeshift/src/Collection" { * @param types An array of types all the paths in the collection * have in common. If not passed, it will be inferred from the paths. */ - new (paths: Array>, parent: Collection, types?: Type[]): this; + new (paths: Array>, parent: Collection, types?: Array>): this; /** * Returns a new collection containing the nodes for which the callback returns true. @@ -190,7 +189,7 @@ declare module "jscodeshift/src/Collection" { i: number, paths: Array> ) => ASTPath | Array> | null | undefined, - type: Type + type: Type ): Collection; /** Returns the number of elements in this collection. */ @@ -233,7 +232,7 @@ declare module "jscodeshift/src/Collection" { /** * Returns true if this collection has the type 'type'. */ - isOfType(type: Type): boolean; + isOfType(type: Type): boolean; } /** @@ -244,7 +243,7 @@ declare module "jscodeshift/src/Collection" { * @param methods Methods to add to the prototype * @param type Optional type to add the methods to */ - export function registerMethods(methods: object, type?: Type): void; + export function registerMethods(methods: object, type?: Type): void; export {}; // to shut off automatic exporting } @@ -311,7 +310,6 @@ declare module "jscodeshift/src/collections/VariableDeclarator" { import { Collection } from "jscodeshift/src/Collection"; import recast, { NodePath } from "recast"; - type Node = typeof recast.types.namedTypes.Node; type ASTPath = NodePath; export interface GlobalMethods { diff --git a/types/jscodeshift/jscodeshift-tests.ts b/types/jscodeshift/jscodeshift-tests.ts index 8631e400c4..f426eb37b7 100644 --- a/types/jscodeshift/jscodeshift-tests.ts +++ b/types/jscodeshift/jscodeshift-tests.ts @@ -1,4 +1,4 @@ -import { ASTNode, FileInfo, API, Transform } from "jscodeshift"; +import { ASTNode, FileInfo, API, Transform, Parser } from "jscodeshift"; // Can define transform with `function`. function replaceWithFooTransform(fileInfo: FileInfo, api: API) { @@ -28,21 +28,46 @@ const reverseIdentifiersTransform: Transform = (file, api) => { .toSource(); }; +// Can define a custom parser. +const parser: Parser = { + parse(source, options) { + // return estree compatible AST + return { type: "root" }; + } +}; + +// Can pass options to recast +const transformWithRecastFormattingOptions: Transform = (file, { j }) => { + return j(file.source).toSource({ quote: "single" }); +}; + +const transformWithRecastParseOptions: Transform = (file, { j }) => { + return j(file.source, { + /* ...passes options to recast's parse method... */ + }).toSource(); +}; + // `ASTNode` supports type narrowing. { const node = ({} as any) as ASTNode; if (node.type === "CatchClause") { - // `node` is narrowed to `CatchClause` here + // $ExpectType CatchClause + node; + if (node.param && node.param.type === "Identifier") { - // `node.param` is narrowed to `Identifier` here + // $ExpectType Identifier + node.param; + if ( node.param.typeAnnotation && node.param.typeAnnotation.type === "TSTypeAnnotation" ) { - // `node.param.typeAnnotation` is narrowed to `TSTypeAnnotation` here + // $ExpectType TSTypeAnnotation + node.param.typeAnnotation; + if (node.param.typeAnnotation.typeAnnotation.type === "TSArrayType") { - // `node.param.typeAnnotation.typeAnnotation` is narrowed to `TSArrayType` here - node.param.typeAnnotation.typeAnnotation.elementType; + // $ExpectType TSArrayType + node.param.typeAnnotation.typeAnnotation; } } } From abfdaa340f1fb458f7b76c587a46c90c418ddaae Mon Sep 17 00:00:00 2001 From: Brie <723547+brieb@users.noreply.github.com> Date: Wed, 9 Jan 2019 16:17:22 -0800 Subject: [PATCH 05/15] [jscodeshift] Move tests to /test subfolder --- types/jscodeshift/{ => test}/jscodeshift-tests.ts | 0 types/jscodeshift/tsconfig.json | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) rename types/jscodeshift/{ => test}/jscodeshift-tests.ts (100%) diff --git a/types/jscodeshift/jscodeshift-tests.ts b/types/jscodeshift/test/jscodeshift-tests.ts similarity index 100% rename from types/jscodeshift/jscodeshift-tests.ts rename to types/jscodeshift/test/jscodeshift-tests.ts diff --git a/types/jscodeshift/tsconfig.json b/types/jscodeshift/tsconfig.json index f1ad7409d3..c5f4e65e59 100644 --- a/types/jscodeshift/tsconfig.json +++ b/types/jscodeshift/tsconfig.json @@ -16,8 +16,8 @@ "noEmit": true, "forceConsistentCasingInFileNames": true }, - "files": [ + "include": [ "index.d.ts", - "jscodeshift-tests.ts" + "test" ] } From 277387c0b621e630dafb1a269009a7e9c253c037 Mon Sep 17 00:00:00 2001 From: Brie <723547+brieb@users.noreply.github.com> Date: Wed, 9 Jan 2019 16:50:27 -0800 Subject: [PATCH 06/15] [jscodeshift] Copy js-transforms for use as real-world test cases https://github.com/jhgg/js-transforms/tree/bfdc97240bfc07e30453a81407f98820d67439a8 --- .../bind-this-to-bind-expression.js | 22 +++ ...-bind-this-to-arrow-function-expression.js | 50 +++++++ ...expression-to-arrow-function-expression.js | 46 ++++++ .../js-transforms/props-to-destructuring.js | 137 ++++++++++++++++++ .../pure-to-composite-component.js | 61 ++++++++ 5 files changed, 316 insertions(+) create mode 100755 types/jscodeshift/test/js-transforms/bind-this-to-bind-expression.js create mode 100755 types/jscodeshift/test/js-transforms/call-expression-bind-this-to-arrow-function-expression.js create mode 100755 types/jscodeshift/test/js-transforms/function-expression-to-arrow-function-expression.js create mode 100755 types/jscodeshift/test/js-transforms/props-to-destructuring.js create mode 100755 types/jscodeshift/test/js-transforms/pure-to-composite-component.js diff --git a/types/jscodeshift/test/js-transforms/bind-this-to-bind-expression.js b/types/jscodeshift/test/js-transforms/bind-this-to-bind-expression.js new file mode 100755 index 0000000000..97248a7e35 --- /dev/null +++ b/types/jscodeshift/test/js-transforms/bind-this-to-bind-expression.js @@ -0,0 +1,22 @@ +/** + * This transform converts stuff like: + * + * let x = this.foo.bind(this); + * + * to + * + * let x = ::this.foo; + * + */ + +module.exports = function (file, api) { + const j = api.jscodeshift; + return j(file.source) + // Find stuff that looks like this.xyz.bind(this) + .find(j.CallExpression, {callee: {object: {object: j.ThisExpression}, property: {name: 'bind'}}}) + // Ensure that .bind() is being called with only one argument, and that argument is "this". + .filter(p => p.value.arguments.length == 1 && p.value.arguments[0].type == "ThisExpression") + // We can now replace it with ::this.xyz + .replaceWith(p => j.bindExpression(null, p.value.callee.object)) + .toSource(); +}; \ No newline at end of file diff --git a/types/jscodeshift/test/js-transforms/call-expression-bind-this-to-arrow-function-expression.js b/types/jscodeshift/test/js-transforms/call-expression-bind-this-to-arrow-function-expression.js new file mode 100755 index 0000000000..727f2e950d --- /dev/null +++ b/types/jscodeshift/test/js-transforms/call-expression-bind-this-to-arrow-function-expression.js @@ -0,0 +1,50 @@ +/** Converts + * onClick(function(a, b) { + * return a + b; + * }.bind(this), + * function(b, c) { + * return 1; + * }.bind(this)); + * + * onClick(function(a) { + * var a = 1; + * return a; + * }.bind(this)); + * + * var a = function(c) { return c; }.bind(this); + * + ** to + * onClick((a, b) => a + b, + * (b, c) => 1); + * + * onClick(a => { + * var a = 1; + * return a; + * }); + * + * var a = c => c; + * + */ + +module.exports = function(file, api) { + const j = api.jscodeshift; + + return j(file.source) + // We're looking for a CallExpression that's calling .bind() onto a FunctionExpression. + .find(j.CallExpression, {callee: {property: {name: 'bind'}, object: {type: 'FunctionExpression'}}}) + // Verify that .bind() is only being called with `this` as it's sole arguments. + .filter(p => p.value.arguments.length == 1 && p.value.arguments[0].type == "ThisExpression") + .replaceWith(p => { + // Grab the function body. Since we looked for the CallExpression originally, the "callee.object" would refer + // to the FunctionExpression that's being called .bind(this) on. We need the body of that function + // to transform into an ArrowFunctionExpression. + var body = p.value.callee.object.body; + // We can get a bit clever here. If we have a function that consists of a single return statement in it's body, + // we can transform it to the more compact arrowFunctionExpression (a, b) => a + b, vs (a + b) => { return a + b } + var useExpression = body.type == 'BlockStatement' && body.body.length == 1 && body.body[0].type == "ReturnStatement"; + body = useExpression ? body.body[0].argument : body; + + return j.arrowFunctionExpression(p.value.callee.object.params, body, useExpression); + }) + .toSource(); +}; \ No newline at end of file diff --git a/types/jscodeshift/test/js-transforms/function-expression-to-arrow-function-expression.js b/types/jscodeshift/test/js-transforms/function-expression-to-arrow-function-expression.js new file mode 100755 index 0000000000..fdee1a994b --- /dev/null +++ b/types/jscodeshift/test/js-transforms/function-expression-to-arrow-function-expression.js @@ -0,0 +1,46 @@ +/** + * Converts a FunctionExpression to an ArrowFunctionExpression when safe to do so. + * + * var a = function(a, b) { + * return a + b; + * } + * + * var b = function(a, b) { + * var c = 0; + * return a + b + c; + * } + * + * var a = function(a, b) { + * return a + b + this.c; + * } + ** + * var a = (a, b) => a + b + * + * var b = (a, b) => { + * var c = 0; + * return a + b + c; + * } + * + * var a = function(a, b) { + * return a + b + this.c; + * } + */ + +module.exports = function (file, api) { + const j = api.jscodeshift; + + return j(file.source) + .find(j.FunctionExpression) + // We check for this expression, as if it's in a function expression, we don't want to re-bind "this" by + // using the arrowFunctionExpression. As that could potentially have some unintended consequences. + .filter(p => j(p).find(j.ThisExpression).size() == 0) + .replaceWith(p => { + var body = p.value.body; + // We can get a bit clever here. If we have a function that consists of a single return statement in it's body, + // we can transform it to the more compact arrowFunctionExpression (a, b) => a + b, vs (a + b) => { return a + b } + var useExpression = body.type == 'BlockStatement' && body.body.length == 1 && body.body[0].type == "ReturnStatement"; + body = useExpression ? body.body[0].argument : body; + return j.arrowFunctionExpression(p.value.params, body, useExpression); + }) + .toSource(); +}; diff --git a/types/jscodeshift/test/js-transforms/props-to-destructuring.js b/types/jscodeshift/test/js-transforms/props-to-destructuring.js new file mode 100755 index 0000000000..87def91d52 --- /dev/null +++ b/types/jscodeshift/test/js-transforms/props-to-destructuring.js @@ -0,0 +1,137 @@ +/** + * Transforms: + * class C extends React.Component() { + * render() { + * return
+ * } + * } + *** To: + * + * class C extends React.Component() { + * render() { + * const { + * foo, + * bar + * } = this.props; + * + * return
+ * } + * } + * + */ + +let keywords = 'this function if return var else for new in typeof while case break try catch delete throw switch continue default instanceof do void finally with debugger implements interface package private protected public static class enum export extends import super true false null abstract boolean byte char const double final float goto int long native short synchronized throws transient volatile'; +keywords = keywords.split(' ').reduce((f, k) => { + f[k] = true; + return f; +}, {}); +const isKeyword = k => keywords.hasOwnProperty(k); + +module.exports = function (file, api) { + const j = api.jscodeshift; + const {statement} = j.template; + + return j(file.source) + .find(j.FunctionExpression) + .replaceWith(p => { + console.log(p); + const root = j(p.value); + const variablesToReplace = {}; + + // Figure out if the variable was defined from props, so that we can re-use that definition. + const isFromProps = (name, resolvedScope) => { + return resolvedScope.getBindings()[name].every( + p => { + const decl = j(p).closest(j.VariableDeclarator); + // What happens when our VariableDeclarator is too high up the parent AST? + + if (!decl.size()) return false; + const node = decl.nodes()[0]; + + if (!(node.init.type == 'MemberExpression' && + node.init.object.type == 'ThisExpression' && + node.init.property.name == 'props')) + return false; + + // Check for the case where it could be aliased (i.e.) { baz: foo } = this.props; + // In this case, we won't do a substitution. + if (p.parentPath.value.type == 'Property' && p.parentPath.value.key.name !== name) + return false; + + return true; + } + ); + }; + + // Transform "this.props.xyz" to "xyz", and record what we've transformed. + // Transform as long as we don't have "xyz" already defined in the scope. + root + .find(j.MemberExpression, { + object: { + type: 'MemberExpression', + object: {type: 'ThisExpression'}, + property: {name: 'props'} + } + }) + .filter(e => { + const resolvedScope = e.scope.lookup(e.value.property.name); + // If the scope is null, that means that this property isn't defined in the scope yet, + // and we can use it. Otherwise, if it is defined, we should see if it was defined from `this.props` + // if none of these cases are true, we can't do substitution. + return resolvedScope == null || isFromProps(e.value.property.name, resolvedScope); + }) + // Ensure that our substitution won't cause us to define a keyword, i.e. `this.props.while` won't + // get converted into `while`. + .filter(p => !isKeyword(p.value.property.name)) + // Now, do the replacement, `this.props.xyz` => `xyz`. + .replaceWith(p => p.value.property) + // Finally, mark the variable as something we will need to define earlier in the function, + // if it's not already defined. + .forEach(p => { + // Is this prop already defined somewhere else. + if (!p.scope.lookup(p.value.name)) + variablesToReplace[p.value.name] = true; + }); + + + // Create property definitions for variables that we've replaced. + const properties = Object.keys(variablesToReplace) + .sort() + .map(k => { + const prop = j.property('init', j.identifier(k), j.identifier(k)); + prop.shorthand = true; + return prop; + }); + + // We have no properties to inject, so we can bail here. + if (!properties.length) + return p.value; + + // See if we already have a VariableDeclarator like { a, b, c } = this.props; + const propDefinitions = root + .find(j.VariableDeclarator, { + id: {type: 'ObjectPattern'}, + init: {type: 'MemberExpression', object: {type: 'ThisExpression'}, property: {name: 'props'}} + }); + + if (propDefinitions.size()) { + const nodePath = propDefinitions.paths()[0]; + const node = nodePath.value; + const newPattern = j.objectPattern(node.id.properties.concat(properties)); + nodePath.replace(j.variableDeclarator(newPattern, node.init)); + return p.value; + } + + // Otherwise, we'll have to create our own, as none were suitable for use. + // Create the variable definition `const { xyz } = this.props;` + const decl = statement`const { ${properties} } = this.props;`; + + // Add the variable definition to the top of the function expression body. + return j.functionExpression( + p.value.id, + p.value.params, + j.blockStatement([decl].concat(p.value.body.body)) + ); + } + ).toSource(); +}; diff --git a/types/jscodeshift/test/js-transforms/pure-to-composite-component.js b/types/jscodeshift/test/js-transforms/pure-to-composite-component.js new file mode 100755 index 0000000000..dd4b3bf5c2 --- /dev/null +++ b/types/jscodeshift/test/js-transforms/pure-to-composite-component.js @@ -0,0 +1,61 @@ +/** For when you've gone too pure and want to go back. **/ +/** Converts + * let HistoryItem = (props) => { + * const { + * item + * } = props; + * return
  • {item}
  • ; + * }; + * + * let X = (props) =>
    foo
    ; + * + * to + * + * class HistoryItem extends Component { + * render() { + * const { + * item + * } = this.props; + * return
  • {item}
  • ; + * } + * } + * + * class X extends Component { + * render() { + * return
    foo
    ; + * } + * } + */ + + +module.exports = function (file, api) { + const j = api.jscodeshift; + const {statement} = j.template; + + function hasJSXElement(ast) { + return j(ast).find(j.JSXElement).size() > 0; + } + + return j(file.source) + .find(j.VariableDeclaration) + .filter(p => p.value.declarations.length == 1) + .replaceWith(p => { + const decl = p.value.declarations[0]; + if (decl.init.type !== 'ArrowFunctionExpression' || + (!hasJSXElement(decl.init.body) && decl.init.body.type !== "JSXElement")) + return p.value; + + let body = decl.init.body; + body = body.type == "JSXElement" ? j.returnStatement(body) : body = body.body; + + j(body) + .find(j.Identifier, {name: 'props'}) + .replaceWith(p => j.memberExpression(j.thisExpression(), j.identifier('props'))); + + return statement`class ${decl.id} extends Component { + render() { ${body} } + }`; + + }) + .toSource(); +}; From 81cf624ae165314859216122efedd926d40e6fa5 Mon Sep 17 00:00:00 2001 From: Brie <723547+brieb@users.noreply.github.com> Date: Wed, 9 Jan 2019 16:52:05 -0800 Subject: [PATCH 07/15] [jscodeshift] Rename js-transforms from JS to TS --- ...this-to-bind-expression.js => bind-this-to-bind-expression.ts} | 0 ... => call-expression-bind-this-to-arrow-function-expression.ts} | 0 ...ion.js => function-expression-to-arrow-function-expression.ts} | 0 .../{props-to-destructuring.js => props-to-destructuring.ts} | 0 ...e-to-composite-component.js => pure-to-composite-component.ts} | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename types/jscodeshift/test/js-transforms/{bind-this-to-bind-expression.js => bind-this-to-bind-expression.ts} (100%) rename types/jscodeshift/test/js-transforms/{call-expression-bind-this-to-arrow-function-expression.js => call-expression-bind-this-to-arrow-function-expression.ts} (100%) rename types/jscodeshift/test/js-transforms/{function-expression-to-arrow-function-expression.js => function-expression-to-arrow-function-expression.ts} (100%) rename types/jscodeshift/test/js-transforms/{props-to-destructuring.js => props-to-destructuring.ts} (100%) rename types/jscodeshift/test/js-transforms/{pure-to-composite-component.js => pure-to-composite-component.ts} (100%) diff --git a/types/jscodeshift/test/js-transforms/bind-this-to-bind-expression.js b/types/jscodeshift/test/js-transforms/bind-this-to-bind-expression.ts similarity index 100% rename from types/jscodeshift/test/js-transforms/bind-this-to-bind-expression.js rename to types/jscodeshift/test/js-transforms/bind-this-to-bind-expression.ts diff --git a/types/jscodeshift/test/js-transforms/call-expression-bind-this-to-arrow-function-expression.js b/types/jscodeshift/test/js-transforms/call-expression-bind-this-to-arrow-function-expression.ts similarity index 100% rename from types/jscodeshift/test/js-transforms/call-expression-bind-this-to-arrow-function-expression.js rename to types/jscodeshift/test/js-transforms/call-expression-bind-this-to-arrow-function-expression.ts diff --git a/types/jscodeshift/test/js-transforms/function-expression-to-arrow-function-expression.js b/types/jscodeshift/test/js-transforms/function-expression-to-arrow-function-expression.ts similarity index 100% rename from types/jscodeshift/test/js-transforms/function-expression-to-arrow-function-expression.js rename to types/jscodeshift/test/js-transforms/function-expression-to-arrow-function-expression.ts diff --git a/types/jscodeshift/test/js-transforms/props-to-destructuring.js b/types/jscodeshift/test/js-transforms/props-to-destructuring.ts similarity index 100% rename from types/jscodeshift/test/js-transforms/props-to-destructuring.js rename to types/jscodeshift/test/js-transforms/props-to-destructuring.ts diff --git a/types/jscodeshift/test/js-transforms/pure-to-composite-component.js b/types/jscodeshift/test/js-transforms/pure-to-composite-component.ts similarity index 100% rename from types/jscodeshift/test/js-transforms/pure-to-composite-component.js rename to types/jscodeshift/test/js-transforms/pure-to-composite-component.ts From 17c8595115965054a9b57670f164d8a4bfe8a9e5 Mon Sep 17 00:00:00 2001 From: Brie <723547+brieb@users.noreply.github.com> Date: Wed, 9 Jan 2019 17:09:54 -0800 Subject: [PATCH 08/15] [jscodeshift] Fix compiler errors in test/js-transforms --- types/jscodeshift/index.d.ts | 6 ++-- .../jscodeshift/test/js-transforms/README.md | 2 ++ .../bind-this-to-bind-expression.ts | 5 +-- ...-bind-this-to-arrow-function-expression.ts | 19 +++++++--- ...expression-to-arrow-function-expression.ts | 13 +++++-- .../js-transforms/props-to-destructuring.ts | 36 ++++++++++--------- .../pure-to-composite-component.ts | 30 ++++++++-------- 7 files changed, 69 insertions(+), 42 deletions(-) create mode 100644 types/jscodeshift/test/js-transforms/README.md mode change 100755 => 100644 types/jscodeshift/test/js-transforms/bind-this-to-bind-expression.ts diff --git a/types/jscodeshift/index.d.ts b/types/jscodeshift/index.d.ts index b9b15be825..95a26cd0a4 100644 --- a/types/jscodeshift/index.d.ts +++ b/types/jscodeshift/index.d.ts @@ -250,7 +250,9 @@ declare module "jscodeshift/src/Collection" { declare module "jscodeshift/src/collections/Node" { import { Collection } from "jscodeshift/src/Collection"; - import { ASTNode, Type } from "recast"; + import { ASTNode, Type, NodePath } from "recast"; + + type ASTPath = NodePath; export interface TraversalMethods { /** @@ -285,7 +287,7 @@ declare module "jscodeshift/src/collections/Node" { * is provided it is executed for every node and the node is replaced with the * functions return value. */ - replaceWith(nodes: T | T[] | ((path: any, i: number) => T)): this; + replaceWith(nodes: T | T[] | ((path: ASTPath, i: number) => T)): Collection; /** * Inserts a new node before the current one. diff --git a/types/jscodeshift/test/js-transforms/README.md b/types/jscodeshift/test/js-transforms/README.md new file mode 100644 index 0000000000..f314daaa44 --- /dev/null +++ b/types/jscodeshift/test/js-transforms/README.md @@ -0,0 +1,2 @@ +Sample jscodeshift transforms taken from https://github.com/jhgg/js-transforms +and converted to TypeScript, for use as real-world test cases. diff --git a/types/jscodeshift/test/js-transforms/bind-this-to-bind-expression.ts b/types/jscodeshift/test/js-transforms/bind-this-to-bind-expression.ts old mode 100755 new mode 100644 index 97248a7e35..65161f8957 --- a/types/jscodeshift/test/js-transforms/bind-this-to-bind-expression.ts +++ b/types/jscodeshift/test/js-transforms/bind-this-to-bind-expression.ts @@ -8,8 +8,9 @@ * let x = ::this.foo; * */ +import { Transform, MemberExpression } from "jscodeshift"; -module.exports = function (file, api) { +const transform: Transform = function (file, api) { const j = api.jscodeshift; return j(file.source) // Find stuff that looks like this.xyz.bind(this) @@ -17,6 +18,6 @@ module.exports = function (file, api) { // Ensure that .bind() is being called with only one argument, and that argument is "this". .filter(p => p.value.arguments.length == 1 && p.value.arguments[0].type == "ThisExpression") // We can now replace it with ::this.xyz - .replaceWith(p => j.bindExpression(null, p.value.callee.object)) + .replaceWith(p => j.bindExpression(null, (p.value.callee as MemberExpression).object)) .toSource(); }; \ No newline at end of file diff --git a/types/jscodeshift/test/js-transforms/call-expression-bind-this-to-arrow-function-expression.ts b/types/jscodeshift/test/js-transforms/call-expression-bind-this-to-arrow-function-expression.ts index 727f2e950d..08bf424ae8 100755 --- a/types/jscodeshift/test/js-transforms/call-expression-bind-this-to-arrow-function-expression.ts +++ b/types/jscodeshift/test/js-transforms/call-expression-bind-this-to-arrow-function-expression.ts @@ -25,8 +25,9 @@ * var a = c => c; * */ +import { Transform, MemberExpression, FunctionExpression } from "jscodeshift"; -module.exports = function(file, api) { +const transform: Transform = function(file, api) { const j = api.jscodeshift; return j(file.source) @@ -35,16 +36,24 @@ module.exports = function(file, api) { // Verify that .bind() is only being called with `this` as it's sole arguments. .filter(p => p.value.arguments.length == 1 && p.value.arguments[0].type == "ThisExpression") .replaceWith(p => { + var callee = p.value.callee as MemberExpression; + var object = callee.object as FunctionExpression; // Grab the function body. Since we looked for the CallExpression originally, the "callee.object" would refer // to the FunctionExpression that's being called .bind(this) on. We need the body of that function // to transform into an ArrowFunctionExpression. - var body = p.value.callee.object.body; + var body = object.body; // We can get a bit clever here. If we have a function that consists of a single return statement in it's body, // we can transform it to the more compact arrowFunctionExpression (a, b) => a + b, vs (a + b) => { return a + b } - var useExpression = body.type == 'BlockStatement' && body.body.length == 1 && body.body[0].type == "ReturnStatement"; - body = useExpression ? body.body[0].argument : body; + var useExpression = false; + if (body.type == 'BlockStatement' && body.body.length == 1) { + const first = body.body[0]; + if (first.type == "ReturnStatement") { + useExpression = true; + body = first.argument!; + } + } - return j.arrowFunctionExpression(p.value.callee.object.params, body, useExpression); + return j.arrowFunctionExpression(object.params, body, useExpression); }) .toSource(); }; \ No newline at end of file diff --git a/types/jscodeshift/test/js-transforms/function-expression-to-arrow-function-expression.ts b/types/jscodeshift/test/js-transforms/function-expression-to-arrow-function-expression.ts index fdee1a994b..ed633bc462 100755 --- a/types/jscodeshift/test/js-transforms/function-expression-to-arrow-function-expression.ts +++ b/types/jscodeshift/test/js-transforms/function-expression-to-arrow-function-expression.ts @@ -25,8 +25,9 @@ * return a + b + this.c; * } */ +import { Transform } from "jscodeshift"; -module.exports = function (file, api) { +const transform: Transform = function (file, api) { const j = api.jscodeshift; return j(file.source) @@ -38,8 +39,14 @@ module.exports = function (file, api) { var body = p.value.body; // We can get a bit clever here. If we have a function that consists of a single return statement in it's body, // we can transform it to the more compact arrowFunctionExpression (a, b) => a + b, vs (a + b) => { return a + b } - var useExpression = body.type == 'BlockStatement' && body.body.length == 1 && body.body[0].type == "ReturnStatement"; - body = useExpression ? body.body[0].argument : body; + var useExpression = false; + if (body.type == 'BlockStatement' && body.body.length == 1) { + const first = body.body[0]; + if (first.type == "ReturnStatement") { + useExpression = true; + body = first.argument!; + } + } return j.arrowFunctionExpression(p.value.params, body, useExpression); }) .toSource(); diff --git a/types/jscodeshift/test/js-transforms/props-to-destructuring.ts b/types/jscodeshift/test/js-transforms/props-to-destructuring.ts index 87def91d52..81f58c5bb7 100755 --- a/types/jscodeshift/test/js-transforms/props-to-destructuring.ts +++ b/types/jscodeshift/test/js-transforms/props-to-destructuring.ts @@ -19,37 +19,40 @@ * } * */ +import { Transform, VariableDeclarator, ObjectPattern, Identifier, BlockStatement } from "jscodeshift"; -let keywords = 'this function if return var else for new in typeof while case break try catch delete throw switch continue default instanceof do void finally with debugger implements interface package private protected public static class enum export extends import super true false null abstract boolean byte char const double final float goto int long native short synchronized throws transient volatile'; -keywords = keywords.split(' ').reduce((f, k) => { +const keywordsStr = 'this function if return var else for new in typeof while case break try catch delete throw switch continue default instanceof do void finally with debugger implements interface package private protected public static class enum export extends import super true false null abstract boolean byte char const double final float goto int long native short synchronized throws transient volatile'; +let keywords = keywordsStr.split(' ').reduce<{ [key: string]: boolean }>((f, k) => { f[k] = true; return f; }, {}); -const isKeyword = k => keywords.hasOwnProperty(k); +const isKeyword = (k: string) => keywords.hasOwnProperty(k); -module.exports = function (file, api) { +const transform: Transform = function (file, api) { const j = api.jscodeshift; const {statement} = j.template; return j(file.source) .find(j.FunctionExpression) .replaceWith(p => { - console.log(p); + // console.log(p); const root = j(p.value); - const variablesToReplace = {}; + const variablesToReplace: { [name: string]: boolean } = {}; // Figure out if the variable was defined from props, so that we can re-use that definition. - const isFromProps = (name, resolvedScope) => { + const isFromProps = (name: string, resolvedScope: any) => { return resolvedScope.getBindings()[name].every( - p => { + (p: any) => { const decl = j(p).closest(j.VariableDeclarator); // What happens when our VariableDeclarator is too high up the parent AST? if (!decl.size()) return false; - const node = decl.nodes()[0]; + const node: VariableDeclarator = decl.nodes()[0]; - if (!(node.init.type == 'MemberExpression' && + if (!(node.init != null && + node.init.type == 'MemberExpression' && node.init.object.type == 'ThisExpression' && + node.init.property.type == 'Identifier' && node.init.property.name == 'props')) return false; @@ -74,17 +77,17 @@ module.exports = function (file, api) { } }) .filter(e => { - const resolvedScope = e.scope.lookup(e.value.property.name); + const resolvedScope = e.scope.lookup((e.value.property as Identifier).name); // If the scope is null, that means that this property isn't defined in the scope yet, // and we can use it. Otherwise, if it is defined, we should see if it was defined from `this.props` // if none of these cases are true, we can't do substitution. - return resolvedScope == null || isFromProps(e.value.property.name, resolvedScope); + return resolvedScope == null || isFromProps((e.value.property as Identifier).name, resolvedScope); }) // Ensure that our substitution won't cause us to define a keyword, i.e. `this.props.while` won't // get converted into `while`. - .filter(p => !isKeyword(p.value.property.name)) + .filter(p => !isKeyword((p.value.property as Identifier).name)) // Now, do the replacement, `this.props.xyz` => `xyz`. - .replaceWith(p => p.value.property) + .replaceWith(p => p.value.property as Identifier) // Finally, mark the variable as something we will need to define earlier in the function, // if it's not already defined. .forEach(p => { @@ -117,7 +120,8 @@ module.exports = function (file, api) { if (propDefinitions.size()) { const nodePath = propDefinitions.paths()[0]; const node = nodePath.value; - const newPattern = j.objectPattern(node.id.properties.concat(properties)); + // TODO(brieb): support narrowing based on second argument to `find` + const newPattern = j.objectPattern((node.id as ObjectPattern).properties.concat(properties)); nodePath.replace(j.variableDeclarator(newPattern, node.init)); return p.value; } @@ -130,7 +134,7 @@ module.exports = function (file, api) { return j.functionExpression( p.value.id, p.value.params, - j.blockStatement([decl].concat(p.value.body.body)) + j.blockStatement([decl].concat((p.value.body as BlockStatement).body)) ); } ).toSource(); diff --git a/types/jscodeshift/test/js-transforms/pure-to-composite-component.ts b/types/jscodeshift/test/js-transforms/pure-to-composite-component.ts index dd4b3bf5c2..a0ea249746 100755 --- a/types/jscodeshift/test/js-transforms/pure-to-composite-component.ts +++ b/types/jscodeshift/test/js-transforms/pure-to-composite-component.ts @@ -27,12 +27,13 @@ * } */ +import { Transform, ASTNode } from "jscodeshift"; -module.exports = function (file, api) { +const transform: Transform = function (file, api) { const j = api.jscodeshift; const {statement} = j.template; - function hasJSXElement(ast) { + function hasJSXElement(ast: ASTNode) { return j(ast).find(j.JSXElement).size() > 0; } @@ -41,21 +42,22 @@ module.exports = function (file, api) { .filter(p => p.value.declarations.length == 1) .replaceWith(p => { const decl = p.value.declarations[0]; - if (decl.init.type !== 'ArrowFunctionExpression' || - (!hasJSXElement(decl.init.body) && decl.init.body.type !== "JSXElement")) - return p.value; + if (decl.type === "VariableDeclarator" && decl.init != null) { + if (decl.init.type !== 'ArrowFunctionExpression' || + (!hasJSXElement(decl.init.body) && decl.init.body.type !== "JSXElement")) + return p.value; - let body = decl.init.body; - body = body.type == "JSXElement" ? j.returnStatement(body) : body = body.body; + let body: any = decl.init.body; + body = body.type == "JSXElement" ? j.returnStatement(body) : body = body.body; - j(body) - .find(j.Identifier, {name: 'props'}) - .replaceWith(p => j.memberExpression(j.thisExpression(), j.identifier('props'))); - - return statement`class ${decl.id} extends Component { - render() { ${body} } - }`; + j(body) + .find(j.Identifier, {name: 'props'}) + .replaceWith(p => j.memberExpression(j.thisExpression(), j.identifier('props'))); + return statement`class ${decl.id} extends Component { + render() { ${body} } + }`; + } }) .toSource(); }; From 60cec9121ecac6b8cc705f51056076c01980ae90 Mon Sep 17 00:00:00 2001 From: Brie <723547+brieb@users.noreply.github.com> Date: Wed, 9 Jan 2019 17:15:44 -0800 Subject: [PATCH 09/15] [jscodeshift] Fix lint violations in test/js-transforms --- .../bind-this-to-bind-expression.ts | 6 ++--- ...-bind-this-to-arrow-function-expression.ts | 23 ++++++++++--------- ...expression-to-arrow-function-expression.ts | 14 +++++------ .../js-transforms/props-to-destructuring.ts | 23 ++++++++++--------- .../pure-to-composite-component.ts | 11 +++++---- 5 files changed, 40 insertions(+), 37 deletions(-) diff --git a/types/jscodeshift/test/js-transforms/bind-this-to-bind-expression.ts b/types/jscodeshift/test/js-transforms/bind-this-to-bind-expression.ts index 65161f8957..6bd9be1fc8 100644 --- a/types/jscodeshift/test/js-transforms/bind-this-to-bind-expression.ts +++ b/types/jscodeshift/test/js-transforms/bind-this-to-bind-expression.ts @@ -10,14 +10,14 @@ */ import { Transform, MemberExpression } from "jscodeshift"; -const transform: Transform = function (file, api) { +const transform: Transform = (file, api) => { const j = api.jscodeshift; return j(file.source) // Find stuff that looks like this.xyz.bind(this) .find(j.CallExpression, {callee: {object: {object: j.ThisExpression}, property: {name: 'bind'}}}) // Ensure that .bind() is being called with only one argument, and that argument is "this". - .filter(p => p.value.arguments.length == 1 && p.value.arguments[0].type == "ThisExpression") + .filter(p => p.value.arguments.length === 1 && p.value.arguments[0].type === "ThisExpression") // We can now replace it with ::this.xyz .replaceWith(p => j.bindExpression(null, (p.value.callee as MemberExpression).object)) .toSource(); -}; \ No newline at end of file +}; diff --git a/types/jscodeshift/test/js-transforms/call-expression-bind-this-to-arrow-function-expression.ts b/types/jscodeshift/test/js-transforms/call-expression-bind-this-to-arrow-function-expression.ts index 08bf424ae8..cfe672436e 100755 --- a/types/jscodeshift/test/js-transforms/call-expression-bind-this-to-arrow-function-expression.ts +++ b/types/jscodeshift/test/js-transforms/call-expression-bind-this-to-arrow-function-expression.ts @@ -1,4 +1,5 @@ -/** Converts +/** + * Converts * onClick(function(a, b) { * return a + b; * }.bind(this), @@ -13,7 +14,7 @@ * * var a = function(c) { return c; }.bind(this); * - ** to + * to * onClick((a, b) => a + b, * (b, c) => 1); * @@ -27,27 +28,27 @@ */ import { Transform, MemberExpression, FunctionExpression } from "jscodeshift"; -const transform: Transform = function(file, api) { +const transform: Transform = (file, api) => { const j = api.jscodeshift; return j(file.source) // We're looking for a CallExpression that's calling .bind() onto a FunctionExpression. .find(j.CallExpression, {callee: {property: {name: 'bind'}, object: {type: 'FunctionExpression'}}}) // Verify that .bind() is only being called with `this` as it's sole arguments. - .filter(p => p.value.arguments.length == 1 && p.value.arguments[0].type == "ThisExpression") + .filter(p => p.value.arguments.length === 1 && p.value.arguments[0].type === "ThisExpression") .replaceWith(p => { - var callee = p.value.callee as MemberExpression; - var object = callee.object as FunctionExpression; + const callee = p.value.callee as MemberExpression; + const object = callee.object as FunctionExpression; // Grab the function body. Since we looked for the CallExpression originally, the "callee.object" would refer // to the FunctionExpression that's being called .bind(this) on. We need the body of that function // to transform into an ArrowFunctionExpression. - var body = object.body; + let body = object.body; // We can get a bit clever here. If we have a function that consists of a single return statement in it's body, // we can transform it to the more compact arrowFunctionExpression (a, b) => a + b, vs (a + b) => { return a + b } - var useExpression = false; - if (body.type == 'BlockStatement' && body.body.length == 1) { + let useExpression = false; + if (body.type === 'BlockStatement' && body.body.length === 1) { const first = body.body[0]; - if (first.type == "ReturnStatement") { + if (first.type === "ReturnStatement") { useExpression = true; body = first.argument!; } @@ -56,4 +57,4 @@ const transform: Transform = function(file, api) { return j.arrowFunctionExpression(object.params, body, useExpression); }) .toSource(); -}; \ No newline at end of file +}; diff --git a/types/jscodeshift/test/js-transforms/function-expression-to-arrow-function-expression.ts b/types/jscodeshift/test/js-transforms/function-expression-to-arrow-function-expression.ts index ed633bc462..5a1b50dc6b 100755 --- a/types/jscodeshift/test/js-transforms/function-expression-to-arrow-function-expression.ts +++ b/types/jscodeshift/test/js-transforms/function-expression-to-arrow-function-expression.ts @@ -13,7 +13,7 @@ * var a = function(a, b) { * return a + b + this.c; * } - ** + * * var a = (a, b) => a + b * * var b = (a, b) => { @@ -27,22 +27,22 @@ */ import { Transform } from "jscodeshift"; -const transform: Transform = function (file, api) { +const transform: Transform = (file, api) => { const j = api.jscodeshift; return j(file.source) .find(j.FunctionExpression) // We check for this expression, as if it's in a function expression, we don't want to re-bind "this" by // using the arrowFunctionExpression. As that could potentially have some unintended consequences. - .filter(p => j(p).find(j.ThisExpression).size() == 0) + .filter(p => j(p).find(j.ThisExpression).size() === 0) .replaceWith(p => { - var body = p.value.body; + let body = p.value.body; // We can get a bit clever here. If we have a function that consists of a single return statement in it's body, // we can transform it to the more compact arrowFunctionExpression (a, b) => a + b, vs (a + b) => { return a + b } - var useExpression = false; - if (body.type == 'BlockStatement' && body.body.length == 1) { + let useExpression = false; + if (body.type === 'BlockStatement' && body.body.length === 1) { const first = body.body[0]; - if (first.type == "ReturnStatement") { + if (first.type === "ReturnStatement") { useExpression = true; body = first.argument!; } diff --git a/types/jscodeshift/test/js-transforms/props-to-destructuring.ts b/types/jscodeshift/test/js-transforms/props-to-destructuring.ts index 81f58c5bb7..6db8fe49dd 100755 --- a/types/jscodeshift/test/js-transforms/props-to-destructuring.ts +++ b/types/jscodeshift/test/js-transforms/props-to-destructuring.ts @@ -5,7 +5,8 @@ * return
    * } * } - *** To: + * + * To: * * class C extends React.Component() { * render() { @@ -21,14 +22,15 @@ */ import { Transform, VariableDeclarator, ObjectPattern, Identifier, BlockStatement } from "jscodeshift"; +// tslint:disable-next-line max-line-length const keywordsStr = 'this function if return var else for new in typeof while case break try catch delete throw switch continue default instanceof do void finally with debugger implements interface package private protected public static class enum export extends import super true false null abstract boolean byte char const double final float goto int long native short synchronized throws transient volatile'; -let keywords = keywordsStr.split(' ').reduce<{ [key: string]: boolean }>((f, k) => { +const keywords = keywordsStr.split(' ').reduce<{ [key: string]: boolean }>((f, k) => { f[k] = true; return f; }, {}); const isKeyword = (k: string) => keywords.hasOwnProperty(k); -const transform: Transform = function (file, api) { +const transform: Transform = (file, api) => { const j = api.jscodeshift; const {statement} = j.template; @@ -50,15 +52,15 @@ const transform: Transform = function (file, api) { const node: VariableDeclarator = decl.nodes()[0]; if (!(node.init != null && - node.init.type == 'MemberExpression' && - node.init.object.type == 'ThisExpression' && - node.init.property.type == 'Identifier' && - node.init.property.name == 'props')) + node.init.type === 'MemberExpression' && + node.init.object.type === 'ThisExpression' && + node.init.property.type === 'Identifier' && + node.init.property.name === 'props')) return false; // Check for the case where it could be aliased (i.e.) { baz: foo } = this.props; // In this case, we won't do a substitution. - if (p.parentPath.value.type == 'Property' && p.parentPath.value.key.name !== name) + if (p.parentPath.value.type === 'Property' && p.parentPath.value.key.name !== name) return false; return true; @@ -81,10 +83,10 @@ const transform: Transform = function (file, api) { // If the scope is null, that means that this property isn't defined in the scope yet, // and we can use it. Otherwise, if it is defined, we should see if it was defined from `this.props` // if none of these cases are true, we can't do substitution. - return resolvedScope == null || isFromProps((e.value.property as Identifier).name, resolvedScope); + return resolvedScope === null || isFromProps((e.value.property as Identifier).name, resolvedScope); }) // Ensure that our substitution won't cause us to define a keyword, i.e. `this.props.while` won't - // get converted into `while`. + // get converted into `while`. .filter(p => !isKeyword((p.value.property as Identifier).name)) // Now, do the replacement, `this.props.xyz` => `xyz`. .replaceWith(p => p.value.property as Identifier) @@ -96,7 +98,6 @@ const transform: Transform = function (file, api) { variablesToReplace[p.value.name] = true; }); - // Create property definitions for variables that we've replaced. const properties = Object.keys(variablesToReplace) .sort() diff --git a/types/jscodeshift/test/js-transforms/pure-to-composite-component.ts b/types/jscodeshift/test/js-transforms/pure-to-composite-component.ts index a0ea249746..6d1a30c378 100755 --- a/types/jscodeshift/test/js-transforms/pure-to-composite-component.ts +++ b/types/jscodeshift/test/js-transforms/pure-to-composite-component.ts @@ -1,5 +1,6 @@ -/** For when you've gone too pure and want to go back. **/ -/** Converts +/** For when you've gone too pure and want to go back. */ +/** + * Converts * let HistoryItem = (props) => { * const { * item @@ -29,7 +30,7 @@ import { Transform, ASTNode } from "jscodeshift"; -const transform: Transform = function (file, api) { +const transform: Transform = (file, api) => { const j = api.jscodeshift; const {statement} = j.template; @@ -39,7 +40,7 @@ const transform: Transform = function (file, api) { return j(file.source) .find(j.VariableDeclaration) - .filter(p => p.value.declarations.length == 1) + .filter(p => p.value.declarations.length === 1) .replaceWith(p => { const decl = p.value.declarations[0]; if (decl.type === "VariableDeclarator" && decl.init != null) { @@ -48,7 +49,7 @@ const transform: Transform = function (file, api) { return p.value; let body: any = decl.init.body; - body = body.type == "JSXElement" ? j.returnStatement(body) : body = body.body; + body = body.type === "JSXElement" ? j.returnStatement(body) : body = body.body; j(body) .find(j.Identifier, {name: 'props'}) From 4162211dcc844212a7b7a4282a602b1f6fab0f32 Mon Sep 17 00:00:00 2001 From: Brie <723547+brieb@users.noreply.github.com> Date: Wed, 9 Jan 2019 17:47:26 -0800 Subject: [PATCH 10/15] [jscodeshift] In tsconfig, don't use "include", must use "files" --- ...-expression-bind-this-to-arrow-function-expression.ts | 0 .../function-expression-to-arrow-function-expression.ts | 0 .../test/js-transforms/props-to-destructuring.ts | 0 .../test/js-transforms/pure-to-composite-component.ts | 0 types/jscodeshift/tsconfig.json | 9 +++++++-- 5 files changed, 7 insertions(+), 2 deletions(-) mode change 100755 => 100644 types/jscodeshift/test/js-transforms/call-expression-bind-this-to-arrow-function-expression.ts mode change 100755 => 100644 types/jscodeshift/test/js-transforms/function-expression-to-arrow-function-expression.ts mode change 100755 => 100644 types/jscodeshift/test/js-transforms/props-to-destructuring.ts mode change 100755 => 100644 types/jscodeshift/test/js-transforms/pure-to-composite-component.ts diff --git a/types/jscodeshift/test/js-transforms/call-expression-bind-this-to-arrow-function-expression.ts b/types/jscodeshift/test/js-transforms/call-expression-bind-this-to-arrow-function-expression.ts old mode 100755 new mode 100644 diff --git a/types/jscodeshift/test/js-transforms/function-expression-to-arrow-function-expression.ts b/types/jscodeshift/test/js-transforms/function-expression-to-arrow-function-expression.ts old mode 100755 new mode 100644 diff --git a/types/jscodeshift/test/js-transforms/props-to-destructuring.ts b/types/jscodeshift/test/js-transforms/props-to-destructuring.ts old mode 100755 new mode 100644 diff --git a/types/jscodeshift/test/js-transforms/pure-to-composite-component.ts b/types/jscodeshift/test/js-transforms/pure-to-composite-component.ts old mode 100755 new mode 100644 diff --git a/types/jscodeshift/tsconfig.json b/types/jscodeshift/tsconfig.json index c5f4e65e59..8ad162be32 100644 --- a/types/jscodeshift/tsconfig.json +++ b/types/jscodeshift/tsconfig.json @@ -16,8 +16,13 @@ "noEmit": true, "forceConsistentCasingInFileNames": true }, - "include": [ + "files": [ "index.d.ts", - "test" + "test/jscodeshift-tests.ts", + "test/js-transforms/bind-this-to-bind-expression.ts", + "test/js-transforms/call-expression-bind-this-to-arrow-function-expression.ts", + "test/js-transforms/function-expression-to-arrow-function-expression.ts", + "test/js-transforms/props-to-destructuring.ts", + "test/js-transforms/pure-to-composite-component.ts" ] } From 5a750fd73fbc5f673054c25a2615fdbbf072db45 Mon Sep 17 00:00:00 2001 From: Brie <723547+brieb@users.noreply.github.com> Date: Mon, 28 Jan 2019 21:44:19 -0800 Subject: [PATCH 11/15] [jscodeshift] Update ast-types to 0.12.1 and recast to 0.17.2 --- types/jscodeshift/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/types/jscodeshift/package.json b/types/jscodeshift/package.json index 65d03a5a7f..6fd3d8b70f 100644 --- a/types/jscodeshift/package.json +++ b/types/jscodeshift/package.json @@ -1,7 +1,7 @@ { "private": true, "dependencies": { - "ast-types": "^0.12.0", - "recast": "^0.17.0" + "ast-types": "^0.12.1", + "recast": "^0.17.2" } } From f3e79d7ac831cf8826a541b12f72025dd097ac77 Mon Sep 17 00:00:00 2001 From: Brie <723547+brieb@users.noreply.github.com> Date: Mon, 28 Jan 2019 21:46:09 -0800 Subject: [PATCH 12/15] [jscodeshift] Fix TSC errors and DTS lints --- .../call-expression-bind-this-to-arrow-function-expression.ts | 4 ++-- .../function-expression-to-arrow-function-expression.ts | 4 ++-- .../jscodeshift/test/js-transforms/props-to-destructuring.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/types/jscodeshift/test/js-transforms/call-expression-bind-this-to-arrow-function-expression.ts b/types/jscodeshift/test/js-transforms/call-expression-bind-this-to-arrow-function-expression.ts index cfe672436e..09cdf3f29e 100644 --- a/types/jscodeshift/test/js-transforms/call-expression-bind-this-to-arrow-function-expression.ts +++ b/types/jscodeshift/test/js-transforms/call-expression-bind-this-to-arrow-function-expression.ts @@ -26,7 +26,7 @@ * var a = c => c; * */ -import { Transform, MemberExpression, FunctionExpression } from "jscodeshift"; +import { Transform, MemberExpression, FunctionExpression, ArrowFunctionExpression } from "jscodeshift"; const transform: Transform = (file, api) => { const j = api.jscodeshift; @@ -42,7 +42,7 @@ const transform: Transform = (file, api) => { // Grab the function body. Since we looked for the CallExpression originally, the "callee.object" would refer // to the FunctionExpression that's being called .bind(this) on. We need the body of that function // to transform into an ArrowFunctionExpression. - let body = object.body; + let body: ArrowFunctionExpression['body'] = object.body; // We can get a bit clever here. If we have a function that consists of a single return statement in it's body, // we can transform it to the more compact arrowFunctionExpression (a, b) => a + b, vs (a + b) => { return a + b } let useExpression = false; diff --git a/types/jscodeshift/test/js-transforms/function-expression-to-arrow-function-expression.ts b/types/jscodeshift/test/js-transforms/function-expression-to-arrow-function-expression.ts index 5a1b50dc6b..b960c920cd 100644 --- a/types/jscodeshift/test/js-transforms/function-expression-to-arrow-function-expression.ts +++ b/types/jscodeshift/test/js-transforms/function-expression-to-arrow-function-expression.ts @@ -25,7 +25,7 @@ * return a + b + this.c; * } */ -import { Transform } from "jscodeshift"; +import { Transform, ArrowFunctionExpression } from "jscodeshift"; const transform: Transform = (file, api) => { const j = api.jscodeshift; @@ -36,7 +36,7 @@ const transform: Transform = (file, api) => { // using the arrowFunctionExpression. As that could potentially have some unintended consequences. .filter(p => j(p).find(j.ThisExpression).size() === 0) .replaceWith(p => { - let body = p.value.body; + let body: ArrowFunctionExpression['body'] = p.value.body; // We can get a bit clever here. If we have a function that consists of a single return statement in it's body, // we can transform it to the more compact arrowFunctionExpression (a, b) => a + b, vs (a + b) => { return a + b } let useExpression = false; diff --git a/types/jscodeshift/test/js-transforms/props-to-destructuring.ts b/types/jscodeshift/test/js-transforms/props-to-destructuring.ts index 6db8fe49dd..138d61a620 100644 --- a/types/jscodeshift/test/js-transforms/props-to-destructuring.ts +++ b/types/jscodeshift/test/js-transforms/props-to-destructuring.ts @@ -135,7 +135,7 @@ const transform: Transform = (file, api) => { return j.functionExpression( p.value.id, p.value.params, - j.blockStatement([decl].concat((p.value.body as BlockStatement).body)) + j.blockStatement([decl].concat(p.value.body.body)) ); } ).toSource(); From b20f6fa61f901ad3c78af6b44e33b7b776c0fa88 Mon Sep 17 00:00:00 2001 From: Brie <723547+brieb@users.noreply.github.com> Date: Sat, 2 Feb 2019 18:56:12 -0800 Subject: [PATCH 13/15] [jscodeshift] Split into multiple files --- types/jscodeshift/index.d.ts | 457 +++--------------- types/jscodeshift/src/Collection.d.ts | 123 +++++ .../src/collections/JSXElement.d.ts | 70 +++ types/jscodeshift/src/collections/Node.d.ts | 57 +++ .../src/collections/VariableDeclarator.d.ts | 39 ++ types/jscodeshift/src/template.d.ts | 14 + types/jscodeshift/tsconfig.json | 5 + types/jscodeshift/tslint.json | 7 +- 8 files changed, 378 insertions(+), 394 deletions(-) create mode 100644 types/jscodeshift/src/Collection.d.ts create mode 100644 types/jscodeshift/src/collections/JSXElement.d.ts create mode 100644 types/jscodeshift/src/collections/Node.d.ts create mode 100644 types/jscodeshift/src/collections/VariableDeclarator.d.ts create mode 100644 types/jscodeshift/src/template.d.ts diff --git a/types/jscodeshift/index.d.ts b/types/jscodeshift/index.d.ts index 95a26cd0a4..f4cee2db55 100644 --- a/types/jscodeshift/index.d.ts +++ b/types/jscodeshift/index.d.ts @@ -4,420 +4,101 @@ // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped // TypeScript Version: 2.9 -declare module "jscodeshift" { - import { Collection, registerMethods } from "jscodeshift/src/Collection"; - import * as JSXElement from "jscodeshift/src/collections/JSXElement"; - import * as VariableDeclarator from "jscodeshift/src/collections/VariableDeclarator"; - import { Template } from "jscodeshift/src/template"; - import recast, { Builders, NamedTypes, NodePath, Options, Parser } from "recast"; - import { ASTNode } from "ast-types/gen/nodes"; +import { ASTNode } from "ast-types/gen/nodes"; +import recast, { Builders, NamedTypes, NodePath, Options, Parser } from "recast"; +import { Collection, registerMethods } from "./src/Collection"; +import * as JSXElement from "./src/collections/JSXElement"; +import * as VariableDeclarator from "./src/collections/VariableDeclarator"; +import { Template } from "./src/template"; - export type ASTPath = NodePath; +export type ASTPath = NodePath; - export interface Filters { - JSXElement: JSXElement.FilterMethods; - VariableDeclarator: VariableDeclarator.FilterMethods; - } - - export interface Mappings { - JSXElement: JSXElement.MappingMethods; - } - - export interface Plugin { - (core: Core): void; - } - - export interface FileInfo { - /** The absolute path to the current file. */ - path: string; - /** The source code of the current file. */ - source: string; - } - - export interface Stats { - /** - * Helper function to collect data during --dry runs. - * This function keeps a counter for how often it was called with a specific argument. - * The result is shown in the console. Useful for finding out how many files match a criterion. - */ - (name: string, quantity?: number): void; - } - - interface Core { - (source: string, options?: Options): Collection; - (source: ASTNode | ASTNode[] | ASTPath | ASTPath[]): Collection; - - registerMethods: typeof registerMethods; - - types: typeof recast.types; - - match(path: ASTNode | ASTPath, filter: ((path: ASTNode) => boolean) | ASTNode): boolean; - - /** template, bound to default parser */ - template: Template; - - filters: Filters; - - mappings: Mappings; - - /** - * Utility function for registering plugins. - * - * Plugins are simple functions that are passed the core jscodeshift instance. - * They should extend jscodeshift by calling `registerMethods`, etc. - * This method guards against repeated registrations (the plugin callback will only be called once). - */ - use(plugin: Plugin): void; - - /** - * Returns a version of the core jscodeshift function "bound" to a specific - * parser. - */ - withParser(parser: string | Parser): JSCodeshift; - } - - type JSCodeshift = Core & NamedTypes & Builders; - - const core: JSCodeshift; - export default core; - - export interface API { - j: JSCodeshift; - jscodeshift: JSCodeshift; - stats: Stats; - report: (msg: string) => void; - } - - export interface Options { - [option: string]: any; - } - - export interface Transform { - /** - * If a string is returned and it is different from passed source, the transform is considered to be successful. - * If a string is returned but it's the same as the source, the transform is considered to be unsuccessful. - * If nothing is returned, the file is not supposed to be transformed (which is ok). - */ - (file: FileInfo, api: API, options: Options): string | null | undefined | void; - } - - export * from "ast-types/gen/nodes"; - export { Collection, Parser }; +export interface Filters { + JSXElement: JSXElement.FilterMethods; + VariableDeclarator: VariableDeclarator.FilterMethods; } -declare module "jscodeshift/src/template" { - import { Parser } from "recast"; - - export interface Template { - /** Tagged template function. Parses the string as source and returns an array of Statement AST nodes. */ - statements(...args: any[]): any; - /** Tagged template function. Parses the string as source and returns an Statement AST node. */ - statement(...args: any[]): any; - /** Tagged template function. Parses the string as source and returns an Expression AST node. */ - expression(...args: any[]): any; - } - - export default function withParser(parser: Parser): Template; - - export {}; // to shut off automatic exporting +export interface Mappings { + JSXElement: JSXElement.MappingMethods; } -declare module "jscodeshift/src/Collection" { - import * as JSXElement from "jscodeshift/src/collections/JSXElement"; - import * as NodeCollection from "jscodeshift/src/collections/Node"; - import * as VariableDeclarator from "jscodeshift/src/collections/VariableDeclarator"; - import recast, { ASTNode, NodePath, Options, Type } from "recast"; +export interface Plugin { + (core: Core): void; +} - type ASTPath = NodePath; +export interface FileInfo { + /** The absolute path to the current file. */ + path: string; + /** The source code of the current file. */ + source: string; +} - export interface Collection - extends NodeCollection.TraversalMethods, - NodeCollection.MutationMethods, - VariableDeclarator.GlobalMethods, - VariableDeclarator.TransformMethods, - JSXElement.GlobalMethods, - JSXElement.TraversalMethods { - /** - * @param paths An array of AST paths - * @param parent A parent collection - * @param types An array of types all the paths in the collection - * have in common. If not passed, it will be inferred from the paths. - */ - new (paths: Array>, parent: Collection, types?: Array>): this; +export interface Stats { + /** + * Helper function to collect data during --dry runs. + * This function keeps a counter for how often it was called with a specific argument. + * The result is shown in the console. Useful for finding out how many files match a criterion. + */ + (name: string, quantity?: number): void; +} - /** - * Returns a new collection containing the nodes for which the callback returns true. - */ - filter( - callback: (path: ASTPath, i: number, paths: Array>) => path is ASTPath - ): Collection; - filter( - callback: (path: ASTPath, i: number, paths: Array>) => boolean - ): Collection; +interface Core { + (source: string, options?: Options): Collection; + (source: ASTNode | ASTNode[] | ASTPath | ASTPath[]): Collection; - /** - * Executes callback for each node/path in the collection. - */ - forEach(callback: (path: ASTPath, i: number, paths: Array>) => void): this; + registerMethods: typeof registerMethods; - /** - * Tests whether at-least one path passes the test implemented by the provided callback. - */ - some(callback: (path: ASTPath, i: number, paths: Array>) => boolean): boolean; + types: typeof recast.types; - /** - * Tests whether all paths pass the test implemented by the provided callback. - */ - every(callback: (path: ASTPath, i: number, paths: Array>) => boolean): boolean; + match(path: ASTNode | ASTPath, filter: ((path: ASTNode) => boolean) | ASTNode): boolean; - /** - * Executes the callback for every path in the collection and returns a new - * collection from the return values (which must be paths). - * - * The callback can return null to indicate to exclude the element from the - * new collection. - * - * If an array is returned, the array will be flattened into the result - * collection. - * - * @param callback - * @param type Force the new collection to be of a specific type - */ - map( - callback: ( - path: ASTPath, - i: number, - paths: Array> - ) => ASTPath | Array> | null | undefined, - type: Type - ): Collection; + /** template, bound to default parser */ + template: Template; - /** Returns the number of elements in this collection. */ - size(): number; + filters: Filters; - /** Returns the number of elements in this collection. */ - length: number; - - /** Returns an array of AST nodes in this collection. */ - nodes(): N[]; - - /** Returns an array of ASTPaths in this this collection. */ - paths(): Array>; - - getAST(): Array>; - - /** - * Converts the AST back to a string, using recast. - * @param options directly passed to recast's printer - */ - toSource(options?: Options): string; - - /** - * Returns a new collection containing only the element at position index. - * In case of a negative index, the element is taken from the end: - * .at(0) - first element - * .at(-1) - last element - */ - at(index: number): Collection; - - /** Calls "get" on the first path (same as "collection.paths(0).get(...)"). */ - get(...fields: Array): any; - - /** - * Returns the type(s) of the collection. This is only used for unit tests, - * don't think other consumers would need it. - */ - getTypes(): string[]; - - /** - * Returns true if this collection has the type 'type'. - */ - isOfType(type: Type): boolean; - } + mappings: Mappings; /** - * This function adds the provided methods to the prototype of the corresponding - * typed collection. If no type is passed, the methods are added to - * Collection.prototype and are available for all collections. + * Utility function for registering plugins. * - * @param methods Methods to add to the prototype - * @param type Optional type to add the methods to + * Plugins are simple functions that are passed the core jscodeshift instance. + * They should extend jscodeshift by calling `registerMethods`, etc. + * This method guards against repeated registrations (the plugin callback will only be called once). */ - export function registerMethods(methods: object, type?: Type): void; + use(plugin: Plugin): void; - export {}; // to shut off automatic exporting + /** + * Returns a version of the core jscodeshift function "bound" to a specific + * parser. + */ + withParser(parser: string | Parser): JSCodeshift; } -declare module "jscodeshift/src/collections/Node" { - import { Collection } from "jscodeshift/src/Collection"; - import { ASTNode, Type, NodePath } from "recast"; +type JSCodeshift = Core & NamedTypes & Builders; - type ASTPath = NodePath; +declare const core: JSCodeshift; +export default core; - export interface TraversalMethods { - /** - * Find nodes of a specific type within the nodes of this collection. - */ - find(type: Type, filter?: ((value: any) => boolean) | object): Collection; - - /** - * Returns a collection containing the paths that create the scope of the - * currently selected paths. Dedupes the paths. - */ - closestScope(): Collection; - - /** - * Traverse the AST up and finds the closest node of the provided type. - */ - closest(type: Type, filter?: any): Collection; - - /** - * Finds the declaration for each selected path. Useful for member expressions - * or JSXElements. Expects a callback function that maps each path to the name - * to look for. - * - * If the callback returns a falsey value, the element is skipped. - */ - getVariableDeclarators(nameGetter: (...args: any[]) => any): Collection; - } - - export interface MutationMethods { - /** - * Simply replaces the selected nodes with the provided node. If a function - * is provided it is executed for every node and the node is replaced with the - * functions return value. - */ - replaceWith(nodes: T | T[] | ((path: ASTPath, i: number) => T)): Collection; - - /** - * Inserts a new node before the current one. - */ - insertBefore(insert: any): Collection; - - /** - * Inserts a new node after the current one. - */ - insertAfter(insert: any): Collection; - - remove(): Collection; - } - - export function register(): void; - - export {}; // to shut off automatic exporting +export interface API { + j: JSCodeshift; + jscodeshift: JSCodeshift; + stats: Stats; + report: (msg: string) => void; } -declare module "jscodeshift/src/collections/VariableDeclarator" { - import { VariableDeclarator } from "ast-types/gen/nodes"; - import { Collection } from "jscodeshift/src/Collection"; - import recast, { NodePath } from "recast"; - - type ASTPath = NodePath; - - export interface GlobalMethods { - /** - * Finds all variable declarators, optionally filtered by name. - */ - findVariableDeclarators(name?: string): Collection; - } - - export interface TransformMethods { - /** - * Renames a variable and all its occurrences. - * This method only applies to VariableDeclarator typed collections. - */ - renameTo(newName: string): Collection; - } - - interface Filter { - (path: ASTPath): boolean; - } - - export interface FilterMethods { - /** - * Returns a function that returns true if the provided path is a variable - * declarator and requires one of the specified module names. - * - * @param names A module name or an array of module names - */ - requiresModule(names: string | string[]): Filter; - } - - export function register(): void; - export const filters: FilterMethods; - - export {}; // to shut off automatic exporting +export interface Options { + [option: string]: any; } -declare module "jscodeshift/src/collections/JSXElement" { - import { JSXElement } from "ast-types/gen/nodes"; - import { Collection } from "jscodeshift/src/Collection"; - import { NodePath } from "recast"; - - type ASTPath = NodePath; - - export interface GlobalMethods { - /** - * Finds all JSXElements optionally filtered by name - */ - findJSXElements(name?: string): Collection; - - /** - * Finds all JSXElements by module name. Given - * - * var Bar = require('Foo'); - * - * - * findJSXElementsByModuleName('Foo') will find , without having to - * know the variable name. - */ - findJSXElementsByModuleName(moduleName: string): Collection; - } - - type Defined = T extends undefined ? never : T; - type JSXElementChild = Defined[0]; - - export interface TraversalMethods { - /** - * Returns all child nodes, including literals and expressions. - * This method only applies to JSXElement typed collections. - */ - childNodes(): Collection; - - /** - * Returns all children that are JSXElements. - * This method only applies to JSXElement typed collections. - */ - childElements(): Collection; - } - - interface Filter { - (path: ASTPath): boolean; - } - - export interface FilterMethods { - /** - * Filter method for attributes. - */ - hasAttributes(attributeFilter: { [attributeName: string]: any }): Filter; - - /** - * Filter elements which contain a specific child type - */ - hasChildren(name: string): Filter; - } - - export interface MappingMethods { - /** - * Given a JSXElement, returns its "root" name. E.g. it would return "Foo" for - * both and . - */ - getRootName(path: ASTPath): string; - } - - export function register(): void; - export const filters: FilterMethods; - export const mappings: MappingMethods; - - export {}; // to shut off automatic exporting +export interface Transform { + /** + * If a string is returned and it is different from passed source, the transform is considered to be successful. + * If a string is returned but it's the same as the source, the transform is considered to be unsuccessful. + * If nothing is returned, the file is not supposed to be transformed (which is ok). + */ + (file: FileInfo, api: API, options: Options): string | null | undefined | void; } + +export * from "ast-types/gen/nodes"; +export { Collection, Parser }; diff --git a/types/jscodeshift/src/Collection.d.ts b/types/jscodeshift/src/Collection.d.ts new file mode 100644 index 0000000000..5ddf7b2bdc --- /dev/null +++ b/types/jscodeshift/src/Collection.d.ts @@ -0,0 +1,123 @@ +import { ASTNode, NodePath, Options, Type } from "recast"; +import * as JSXElement from "./collections/JSXElement"; +import * as NodeCollection from "./collections/Node"; +import * as VariableDeclarator from "./collections/VariableDeclarator"; + +type ASTPath = NodePath; + +export interface Collection + extends NodeCollection.TraversalMethods, + NodeCollection.MutationMethods, + VariableDeclarator.GlobalMethods, + VariableDeclarator.TransformMethods, + JSXElement.GlobalMethods, + JSXElement.TraversalMethods { + /** + * @param paths An array of AST paths + * @param parent A parent collection + * @param types An array of types all the paths in the collection + * have in common. If not passed, it will be inferred from the paths. + */ + new (paths: Array>, parent: Collection, types?: Array>): this; + + /** + * Returns a new collection containing the nodes for which the callback returns true. + */ + filter( + callback: (path: ASTPath, i: number, paths: Array>) => path is ASTPath + ): Collection; + filter( + callback: (path: ASTPath, i: number, paths: Array>) => boolean + ): Collection; + + /** + * Executes callback for each node/path in the collection. + */ + forEach(callback: (path: ASTPath, i: number, paths: Array>) => void): this; + + /** + * Tests whether at-least one path passes the test implemented by the provided callback. + */ + some(callback: (path: ASTPath, i: number, paths: Array>) => boolean): boolean; + + /** + * Tests whether all paths pass the test implemented by the provided callback. + */ + every(callback: (path: ASTPath, i: number, paths: Array>) => boolean): boolean; + + /** + * Executes the callback for every path in the collection and returns a new + * collection from the return values (which must be paths). + * + * The callback can return null to indicate to exclude the element from the + * new collection. + * + * If an array is returned, the array will be flattened into the result + * collection. + * + * @param callback + * @param type Force the new collection to be of a specific type + */ + map( + callback: ( + path: ASTPath, + i: number, + paths: Array> + ) => ASTPath | Array> | null | undefined, + type: Type + ): Collection; + + /** Returns the number of elements in this collection. */ + size(): number; + + /** Returns the number of elements in this collection. */ + length: number; + + /** Returns an array of AST nodes in this collection. */ + nodes(): N[]; + + /** Returns an array of ASTPaths in this this collection. */ + paths(): Array>; + + getAST(): Array>; + + /** + * Converts the AST back to a string, using recast. + * @param options directly passed to recast's printer + */ + toSource(options?: Options): string; + + /** + * Returns a new collection containing only the element at position index. + * In case of a negative index, the element is taken from the end: + * .at(0) - first element + * .at(-1) - last element + */ + at(index: number): Collection; + + /** Calls "get" on the first path (same as "collection.paths(0).get(...)"). */ + get(...fields: Array): any; + + /** + * Returns the type(s) of the collection. This is only used for unit tests, + * don't think other consumers would need it. + */ + getTypes(): string[]; + + /** + * Returns true if this collection has the type 'type'. + */ + isOfType(type: Type): boolean; +} + +/** + * This function adds the provided methods to the prototype of the corresponding + * typed collection. If no type is passed, the methods are added to + * Collection.prototype and are available for all collections. + * + * @param methods Methods to add to the prototype + * @param type Optional type to add the methods to + */ +export function registerMethods(methods: object, type?: Type): void; + +export { }; // to shut off automatic exporting diff --git a/types/jscodeshift/src/collections/JSXElement.d.ts b/types/jscodeshift/src/collections/JSXElement.d.ts new file mode 100644 index 0000000000..730c33ad90 --- /dev/null +++ b/types/jscodeshift/src/collections/JSXElement.d.ts @@ -0,0 +1,70 @@ +import { JSXElement } from "ast-types/gen/nodes"; +import { NodePath } from "recast"; +import { Collection } from "../Collection"; + +type ASTPath = NodePath; + +export interface GlobalMethods { + /** + * Finds all JSXElements optionally filtered by name + */ + findJSXElements(name?: string): Collection; + + /** + * Finds all JSXElements by module name. Given + * + * var Bar = require('Foo'); + * + * + * findJSXElementsByModuleName('Foo') will find , without having to + * know the variable name. + */ + findJSXElementsByModuleName(moduleName: string): Collection; +} + +type Defined = T extends undefined ? never : T; +type JSXElementChild = Defined[0]; + +export interface TraversalMethods { + /** + * Returns all child nodes, including literals and expressions. + * This method only applies to JSXElement typed collections. + */ + childNodes(): Collection; + + /** + * Returns all children that are JSXElements. + * This method only applies to JSXElement typed collections. + */ + childElements(): Collection; +} + +interface Filter { + (path: ASTPath): boolean; +} + +export interface FilterMethods { + /** + * Filter method for attributes. + */ + hasAttributes(attributeFilter: { [attributeName: string]: any }): Filter; + + /** + * Filter elements which contain a specific child type + */ + hasChildren(name: string): Filter; +} + +export interface MappingMethods { + /** + * Given a JSXElement, returns its "root" name. E.g. it would return "Foo" for + * both and . + */ + getRootName(path: ASTPath): string; +} + +export function register(): void; +export const filters: FilterMethods; +export const mappings: MappingMethods; + +export { }; // to shut off automatic exporting diff --git a/types/jscodeshift/src/collections/Node.d.ts b/types/jscodeshift/src/collections/Node.d.ts new file mode 100644 index 0000000000..e596c8a8ab --- /dev/null +++ b/types/jscodeshift/src/collections/Node.d.ts @@ -0,0 +1,57 @@ +import { VariableDeclarator } from "ast-types/gen/nodes"; +import { ASTNode, NodePath, Type } from "recast"; +import { Collection } from "../Collection"; + +type ASTPath = NodePath; + +export interface TraversalMethods { + /** + * Find nodes of a specific type within the nodes of this collection. + */ + find(type: Type, filter?: ((value: any) => boolean) | object): Collection; + + /** + * Returns a collection containing the paths that create the scope of the + * currently selected paths. Dedupes the paths. + */ + closestScope(): Collection; + + /** + * Traverse the AST up and finds the closest node of the provided type. + */ + closest(type: Type, filter?: any): Collection; + + /** + * Finds the declaration for each selected path. Useful for member expressions + * or JSXElements. Expects a callback function that maps each path to the name + * to look for. + * + * If the callback returns a falsey value, the element is skipped. + */ + getVariableDeclarators(nameGetter: (...args: any[]) => any): Collection; +} + +export interface MutationMethods { + /** + * Simply replaces the selected nodes with the provided node. If a function + * is provided it is executed for every node and the node is replaced with the + * functions return value. + */ + replaceWith(nodes: T | T[] | ((path: ASTPath, i: number) => T)): Collection; + + /** + * Inserts a new node before the current one. + */ + insertBefore(insert: any): Collection; + + /** + * Inserts a new node after the current one. + */ + insertAfter(insert: any): Collection; + + remove(): Collection; +} + +export function register(): void; + +export { }; // to shut off automatic exporting diff --git a/types/jscodeshift/src/collections/VariableDeclarator.d.ts b/types/jscodeshift/src/collections/VariableDeclarator.d.ts new file mode 100644 index 0000000000..8627b316db --- /dev/null +++ b/types/jscodeshift/src/collections/VariableDeclarator.d.ts @@ -0,0 +1,39 @@ +import { VariableDeclarator } from "ast-types/gen/nodes"; +import { NodePath } from "recast"; +import { Collection } from "../Collection"; + +type ASTPath = NodePath; + +export interface GlobalMethods { + /** + * Finds all variable declarators, optionally filtered by name. + */ + findVariableDeclarators(name?: string): Collection; +} + +export interface TransformMethods { + /** + * Renames a variable and all its occurrences. + * This method only applies to VariableDeclarator typed collections. + */ + renameTo(newName: string): Collection; +} + +interface Filter { + (path: ASTPath): boolean; +} + +export interface FilterMethods { + /** + * Returns a function that returns true if the provided path is a variable + * declarator and requires one of the specified module names. + * + * @param names A module name or an array of module names + */ + requiresModule(names: string | string[]): Filter; +} + +export function register(): void; +export const filters: FilterMethods; + +export { }; // to shut off automatic exporting diff --git a/types/jscodeshift/src/template.d.ts b/types/jscodeshift/src/template.d.ts new file mode 100644 index 0000000000..44bfab9be3 --- /dev/null +++ b/types/jscodeshift/src/template.d.ts @@ -0,0 +1,14 @@ +import { Parser } from "recast"; + +export interface Template { + /** Tagged template function. Parses the string as source and returns an array of Statement AST nodes. */ + statements(...args: any[]): any; + /** Tagged template function. Parses the string as source and returns an Statement AST node. */ + statement(...args: any[]): any; + /** Tagged template function. Parses the string as source and returns an Expression AST node. */ + expression(...args: any[]): any; +} + +export default function withParser(parser: Parser): Template; + +export { }; // to shut off automatic exporting diff --git a/types/jscodeshift/tsconfig.json b/types/jscodeshift/tsconfig.json index 8ad162be32..a2c90ce266 100644 --- a/types/jscodeshift/tsconfig.json +++ b/types/jscodeshift/tsconfig.json @@ -18,6 +18,11 @@ }, "files": [ "index.d.ts", + "src/Collection.d.ts", + "src/collections/JSXElement.d.ts", + "src/collections/Node.d.ts", + "src/collections/VariableDeclarator.d.ts", + "src/template.d.ts", "test/jscodeshift-tests.ts", "test/js-transforms/bind-this-to-bind-expression.ts", "test/js-transforms/call-expression-bind-this-to-arrow-function-expression.ts", diff --git a/types/jscodeshift/tslint.json b/types/jscodeshift/tslint.json index 94991f356f..f93cf8562a 100644 --- a/types/jscodeshift/tslint.json +++ b/types/jscodeshift/tslint.json @@ -1,8 +1,3 @@ { - "extends": "dtslint/dt.json", - "rules": { - "no-declare-current-package": false, - "no-self-import": false, - "no-unnecessary-generics": false - } + "extends": "dtslint/dt.json" } From 9e0b1db4347a4357e7e3f11e06e744c93cf32219 Mon Sep 17 00:00:00 2001 From: Brie <723547+brieb@users.noreply.github.com> Date: Wed, 6 Feb 2019 13:35:10 -0800 Subject: [PATCH 14/15] [jscodeshift] Use 'export =' and 'require' to be consistent with JS implementation --- types/jscodeshift/index.d.ts | 99 +---- types/jscodeshift/src/Collection.d.ts | 32 +- .../src/collections/JSXElement.d.ts | 19 +- types/jscodeshift/src/collections/Node.d.ts | 28 +- .../src/collections/VariableDeclarator.d.ts | 14 +- types/jscodeshift/src/core.d.ts | 363 ++++++++++++++++++ types/jscodeshift/src/template.d.ts | 28 +- .../bind-this-to-bind-expression.ts | 8 +- .../js-transforms/props-to-destructuring.ts | 2 +- types/jscodeshift/tsconfig.json | 1 + 10 files changed, 439 insertions(+), 155 deletions(-) create mode 100644 types/jscodeshift/src/core.d.ts diff --git a/types/jscodeshift/index.d.ts b/types/jscodeshift/index.d.ts index f4cee2db55..e19423d928 100644 --- a/types/jscodeshift/index.d.ts +++ b/types/jscodeshift/index.d.ts @@ -4,101 +4,6 @@ // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped // TypeScript Version: 2.9 -import { ASTNode } from "ast-types/gen/nodes"; -import recast, { Builders, NamedTypes, NodePath, Options, Parser } from "recast"; -import { Collection, registerMethods } from "./src/Collection"; -import * as JSXElement from "./src/collections/JSXElement"; -import * as VariableDeclarator from "./src/collections/VariableDeclarator"; -import { Template } from "./src/template"; +import core = require("./src/core"); -export type ASTPath = NodePath; - -export interface Filters { - JSXElement: JSXElement.FilterMethods; - VariableDeclarator: VariableDeclarator.FilterMethods; -} - -export interface Mappings { - JSXElement: JSXElement.MappingMethods; -} - -export interface Plugin { - (core: Core): void; -} - -export interface FileInfo { - /** The absolute path to the current file. */ - path: string; - /** The source code of the current file. */ - source: string; -} - -export interface Stats { - /** - * Helper function to collect data during --dry runs. - * This function keeps a counter for how often it was called with a specific argument. - * The result is shown in the console. Useful for finding out how many files match a criterion. - */ - (name: string, quantity?: number): void; -} - -interface Core { - (source: string, options?: Options): Collection; - (source: ASTNode | ASTNode[] | ASTPath | ASTPath[]): Collection; - - registerMethods: typeof registerMethods; - - types: typeof recast.types; - - match(path: ASTNode | ASTPath, filter: ((path: ASTNode) => boolean) | ASTNode): boolean; - - /** template, bound to default parser */ - template: Template; - - filters: Filters; - - mappings: Mappings; - - /** - * Utility function for registering plugins. - * - * Plugins are simple functions that are passed the core jscodeshift instance. - * They should extend jscodeshift by calling `registerMethods`, etc. - * This method guards against repeated registrations (the plugin callback will only be called once). - */ - use(plugin: Plugin): void; - - /** - * Returns a version of the core jscodeshift function "bound" to a specific - * parser. - */ - withParser(parser: string | Parser): JSCodeshift; -} - -type JSCodeshift = Core & NamedTypes & Builders; - -declare const core: JSCodeshift; -export default core; - -export interface API { - j: JSCodeshift; - jscodeshift: JSCodeshift; - stats: Stats; - report: (msg: string) => void; -} - -export interface Options { - [option: string]: any; -} - -export interface Transform { - /** - * If a string is returned and it is different from passed source, the transform is considered to be successful. - * If a string is returned but it's the same as the source, the transform is considered to be unsuccessful. - * If nothing is returned, the file is not supposed to be transformed (which is ok). - */ - (file: FileInfo, api: API, options: Options): string | null | undefined | void; -} - -export * from "ast-types/gen/nodes"; -export { Collection, Parser }; +export = core; diff --git a/types/jscodeshift/src/Collection.d.ts b/types/jscodeshift/src/Collection.d.ts index 5ddf7b2bdc..2cab87b153 100644 --- a/types/jscodeshift/src/Collection.d.ts +++ b/types/jscodeshift/src/Collection.d.ts @@ -1,9 +1,9 @@ -import { ASTNode, NodePath, Options, Type } from "recast"; -import * as JSXElement from "./collections/JSXElement"; -import * as NodeCollection from "./collections/Node"; -import * as VariableDeclarator from "./collections/VariableDeclarator"; +import recast = require("recast"); +import JSXElement = require("./collections/JSXElement"); +import NodeCollection = require("./collections/Node"); +import VariableDeclarator = require("./collections/VariableDeclarator"); -type ASTPath = NodePath; +type ASTPath = recast.NodePath; export interface Collection extends NodeCollection.TraversalMethods, @@ -18,7 +18,7 @@ export interface Collection * @param types An array of types all the paths in the collection * have in common. If not passed, it will be inferred from the paths. */ - new (paths: Array>, parent: Collection, types?: Array>): this; + new (paths: Array>, parent: Collection, types?: Array>): this; /** * Returns a new collection containing the nodes for which the callback returns true. @@ -58,13 +58,13 @@ export interface Collection * @param callback * @param type Force the new collection to be of a specific type */ - map( + map( callback: ( path: ASTPath, i: number, paths: Array> ) => ASTPath | Array> | null | undefined, - type: Type + type: recast.Type ): Collection; /** Returns the number of elements in this collection. */ @@ -85,7 +85,7 @@ export interface Collection * Converts the AST back to a string, using recast. * @param options directly passed to recast's printer */ - toSource(options?: Options): string; + toSource(options?: recast.Options): string; /** * Returns a new collection containing only the element at position index. @@ -107,9 +107,13 @@ export interface Collection /** * Returns true if this collection has the type 'type'. */ - isOfType(type: Type): boolean; + isOfType(type: recast.Type): boolean; } +export function fromPaths(...args: any[]): any; + +export function fromNodes(...args: any[]): any; + /** * This function adds the provided methods to the prototype of the corresponding * typed collection. If no type is passed, the methods are added to @@ -118,6 +122,10 @@ export interface Collection * @param methods Methods to add to the prototype * @param type Optional type to add the methods to */ -export function registerMethods(methods: object, type?: Type): void; +export function registerMethods(methods: object, type?: recast.Type): void; -export { }; // to shut off automatic exporting +export function hasConflictingRegistration(...args: any[]): any; + +export function setDefaultCollectionType(...args: any[]): any; + +export {}; // shut off automatic exporting diff --git a/types/jscodeshift/src/collections/JSXElement.d.ts b/types/jscodeshift/src/collections/JSXElement.d.ts index 730c33ad90..0490e40f76 100644 --- a/types/jscodeshift/src/collections/JSXElement.d.ts +++ b/types/jscodeshift/src/collections/JSXElement.d.ts @@ -1,14 +1,15 @@ -import { JSXElement } from "ast-types/gen/nodes"; -import { NodePath } from "recast"; -import { Collection } from "../Collection"; +import nodes = require("ast-types/gen/nodes"); +import recast = require("recast"); +import Collection = require("../Collection"); -type ASTPath = NodePath; +type ASTPath = recast.NodePath; +type JSXElement = nodes.JSXElement; export interface GlobalMethods { /** * Finds all JSXElements optionally filtered by name */ - findJSXElements(name?: string): Collection; + findJSXElements(name?: string): Collection.Collection; /** * Finds all JSXElements by module name. Given @@ -19,7 +20,7 @@ export interface GlobalMethods { * findJSXElementsByModuleName('Foo') will find , without having to * know the variable name. */ - findJSXElementsByModuleName(moduleName: string): Collection; + findJSXElementsByModuleName(moduleName: string): Collection.Collection; } type Defined = T extends undefined ? never : T; @@ -30,13 +31,13 @@ export interface TraversalMethods { * Returns all child nodes, including literals and expressions. * This method only applies to JSXElement typed collections. */ - childNodes(): Collection; + childNodes(): Collection.Collection; /** * Returns all children that are JSXElements. * This method only applies to JSXElement typed collections. */ - childElements(): Collection; + childElements(): Collection.Collection; } interface Filter { @@ -67,4 +68,4 @@ export function register(): void; export const filters: FilterMethods; export const mappings: MappingMethods; -export { }; // to shut off automatic exporting +export {}; // shut off automatic exporting diff --git a/types/jscodeshift/src/collections/Node.d.ts b/types/jscodeshift/src/collections/Node.d.ts index e596c8a8ab..d1e74f314d 100644 --- a/types/jscodeshift/src/collections/Node.d.ts +++ b/types/jscodeshift/src/collections/Node.d.ts @@ -1,34 +1,34 @@ -import { VariableDeclarator } from "ast-types/gen/nodes"; -import { ASTNode, NodePath, Type } from "recast"; -import { Collection } from "../Collection"; +import nodes = require("ast-types/gen/nodes"); +import recast = require("recast"); +import Collection = require("../Collection"); -type ASTPath = NodePath; +type ASTPath = recast.NodePath; export interface TraversalMethods { /** * Find nodes of a specific type within the nodes of this collection. */ - find(type: Type, filter?: ((value: any) => boolean) | object): Collection; + find(type: recast.Type, filter?: ((value: any) => boolean) | object): Collection.Collection; /** * Returns a collection containing the paths that create the scope of the * currently selected paths. Dedupes the paths. */ - closestScope(): Collection; + closestScope(): Collection.Collection; /** * Traverse the AST up and finds the closest node of the provided type. */ - closest(type: Type, filter?: any): Collection; + closest(type: recast.Type, filter?: any): Collection.Collection; /** * Finds the declaration for each selected path. Useful for member expressions * or JSXElements. Expects a callback function that maps each path to the name * to look for. * - * If the callback returns a falsey value, the element is skipped. + * If the callback returns a falsy value, the element is skipped. */ - getVariableDeclarators(nameGetter: (...args: any[]) => any): Collection; + getVariableDeclarators(nameGetter: (...args: any[]) => any): Collection.Collection; } export interface MutationMethods { @@ -37,21 +37,21 @@ export interface MutationMethods { * is provided it is executed for every node and the node is replaced with the * functions return value. */ - replaceWith(nodes: T | T[] | ((path: ASTPath, i: number) => T)): Collection; + replaceWith(nodes: T | T[] | ((path: ASTPath, i: number) => T)): Collection.Collection; /** * Inserts a new node before the current one. */ - insertBefore(insert: any): Collection; + insertBefore(insert: any): Collection.Collection; /** * Inserts a new node after the current one. */ - insertAfter(insert: any): Collection; + insertAfter(insert: any): Collection.Collection; - remove(): Collection; + remove(): Collection.Collection; } export function register(): void; -export { }; // to shut off automatic exporting +export {}; // shut off automatic exporting diff --git a/types/jscodeshift/src/collections/VariableDeclarator.d.ts b/types/jscodeshift/src/collections/VariableDeclarator.d.ts index 8627b316db..eb7c8bcdfd 100644 --- a/types/jscodeshift/src/collections/VariableDeclarator.d.ts +++ b/types/jscodeshift/src/collections/VariableDeclarator.d.ts @@ -1,14 +1,14 @@ -import { VariableDeclarator } from "ast-types/gen/nodes"; -import { NodePath } from "recast"; -import { Collection } from "../Collection"; +import nodes = require("ast-types/gen/nodes"); +import recast = require("recast"); +import Collection = require("../Collection"); -type ASTPath = NodePath; +type ASTPath = recast.NodePath; export interface GlobalMethods { /** * Finds all variable declarators, optionally filtered by name. */ - findVariableDeclarators(name?: string): Collection; + findVariableDeclarators(name?: string): Collection.Collection; } export interface TransformMethods { @@ -16,7 +16,7 @@ export interface TransformMethods { * Renames a variable and all its occurrences. * This method only applies to VariableDeclarator typed collections. */ - renameTo(newName: string): Collection; + renameTo(newName: string): Collection.Collection; } interface Filter { @@ -36,4 +36,4 @@ export interface FilterMethods { export function register(): void; export const filters: FilterMethods; -export { }; // to shut off automatic exporting +export {}; // shut off automatic exporting diff --git a/types/jscodeshift/src/core.d.ts b/types/jscodeshift/src/core.d.ts new file mode 100644 index 0000000000..9b9f48305d --- /dev/null +++ b/types/jscodeshift/src/core.d.ts @@ -0,0 +1,363 @@ +import nodes = require("ast-types/gen/nodes"); +import recast = require("recast"); +import Collection = require("./Collection"); +import template = require("./template"); +import VariableDeclarator = require("./collections/VariableDeclarator"); +import JSXElement = require("./collections/JSXElement"); + +declare namespace core { + type Parser = recast.Parser; + + interface Filters { + JSXElement: JSXElement.FilterMethods; + VariableDeclarator: VariableDeclarator.FilterMethods; + } + + interface Mappings { + JSXElement: JSXElement.MappingMethods; + } + + interface Plugin { + (core: Core): void; + } + + interface FileInfo { + /** The absolute path to the current file. */ + path: string; + /** The source code of the current file. */ + source: string; + } + + interface Stats { + /** + * Helper function to collect data during --dry runs. + * This function keeps a counter for how often it was called with a specific argument. + * The result is shown in the console. Useful for finding out how many files match a criterion. + */ + (name: string, quantity?: number): void; + } + + type ASTPath = recast.NodePath; + + interface Core { + (source: string, options?: Options): Collection.Collection; + (source: ASTNode | ASTNode[] | ASTPath | ASTPath[]): Collection.Collection; + + registerMethods: typeof Collection.registerMethods; + + types: typeof recast.default.types; + + match( + path: ASTNode | ASTPath, + filter: ((path: ASTNode) => boolean) | ASTNode + ): boolean; + + /** template, bound to default parser */ + template: template.Template; + + filters: Filters; + + mappings: Mappings; + + /** + * Utility function for registering plugins. + * + * Plugins are simple functions that are passed the core jscodeshift instance. + * They should extend jscodeshift by calling `registerMethods`, etc. + * This method guards against repeated registrations (the plugin callback will only be called once). + */ + use(plugin: Plugin): void; + + /** + * Returns a version of the core jscodeshift function "bound" to a specific + * parser. + */ + withParser(parser: string | Parser): JSCodeshift; + } + + type JSCodeshift = Core & recast.NamedTypes & recast.Builders; + + interface API { + j: JSCodeshift; + jscodeshift: JSCodeshift; + stats: Stats; + report: (msg: string) => void; + } + + interface Options { + [option: string]: any; + } + + interface Transform { + /** + * If a string is returned and it is different from passed source, the transform is considered to be successful. + * If a string is returned but it's the same as the source, the transform is considered to be unsuccessful. + * If nothing is returned, the file is not supposed to be transformed (which is ok). + */ + (file: FileInfo, api: API, options: Options): string | null | undefined | void; + } + + type ASTNode = nodes.ASTNode; + + type AnyTypeAnnotation = nodes.AnyTypeAnnotation; + type ArrayExpression = nodes.ArrayExpression; + type ArrayPattern = nodes.ArrayPattern; + type ArrayTypeAnnotation = nodes.ArrayTypeAnnotation; + type ArrowFunctionExpression = nodes.ArrowFunctionExpression; + type AssignmentExpression = nodes.AssignmentExpression; + type AssignmentPattern = nodes.AssignmentPattern; + type AwaitExpression = nodes.AwaitExpression; + type BigIntLiteral = nodes.BigIntLiteral; + type BinaryExpression = nodes.BinaryExpression; + type BindExpression = nodes.BindExpression; + type Block = nodes.Block; + type BlockStatement = nodes.BlockStatement; + type BooleanLiteral = nodes.BooleanLiteral; + type BooleanLiteralTypeAnnotation = nodes.BooleanLiteralTypeAnnotation; + type BooleanTypeAnnotation = nodes.BooleanTypeAnnotation; + type BreakStatement = nodes.BreakStatement; + type CallExpression = nodes.CallExpression; + type CatchClause = nodes.CatchClause; + type ClassBody = nodes.ClassBody; + type ClassDeclaration = nodes.ClassDeclaration; + type ClassExpression = nodes.ClassExpression; + type ClassImplements = nodes.ClassImplements; + type ClassMethod = nodes.ClassMethod; + type ClassPrivateMethod = nodes.ClassPrivateMethod; + type ClassPrivateProperty = nodes.ClassPrivateProperty; + type ClassProperty = nodes.ClassProperty; + type ClassPropertyDefinition = nodes.ClassPropertyDefinition; + type Comment = nodes.Comment; + type CommentBlock = nodes.CommentBlock; + type CommentLine = nodes.CommentLine; + type ComprehensionBlock = nodes.ComprehensionBlock; + type ComprehensionExpression = nodes.ComprehensionExpression; + type ConditionalExpression = nodes.ConditionalExpression; + type ContinueStatement = nodes.ContinueStatement; + type DebuggerStatement = nodes.DebuggerStatement; + type Declaration = nodes.Declaration; + type DeclareClass = nodes.DeclareClass; + type DeclaredPredicate = nodes.DeclaredPredicate; + type DeclareExportAllDeclaration = nodes.DeclareExportAllDeclaration; + type DeclareExportDeclaration = nodes.DeclareExportDeclaration; + type DeclareFunction = nodes.DeclareFunction; + type DeclareInterface = nodes.DeclareInterface; + type DeclareModule = nodes.DeclareModule; + type DeclareModuleExports = nodes.DeclareModuleExports; + type DeclareOpaqueType = nodes.DeclareOpaqueType; + type DeclareTypeAlias = nodes.DeclareTypeAlias; + type DeclareVariable = nodes.DeclareVariable; + type Decorator = nodes.Decorator; + type Directive = nodes.Directive; + type DirectiveLiteral = nodes.DirectiveLiteral; + type DoExpression = nodes.DoExpression; + type DoWhileStatement = nodes.DoWhileStatement; + type EmptyStatement = nodes.EmptyStatement; + type EmptyTypeAnnotation = nodes.EmptyTypeAnnotation; + type ExistentialTypeParam = nodes.ExistentialTypeParam; + type ExistsTypeAnnotation = nodes.ExistsTypeAnnotation; + type ExportAllDeclaration = nodes.ExportAllDeclaration; + type ExportBatchSpecifier = nodes.ExportBatchSpecifier; + type ExportDeclaration = nodes.ExportDeclaration; + type ExportDefaultDeclaration = nodes.ExportDefaultDeclaration; + type ExportDefaultSpecifier = nodes.ExportDefaultSpecifier; + type ExportNamedDeclaration = nodes.ExportNamedDeclaration; + type ExportNamespaceSpecifier = nodes.ExportNamespaceSpecifier; + type ExportSpecifier = nodes.ExportSpecifier; + type Expression = nodes.Expression; + type ExpressionStatement = nodes.ExpressionStatement; + type File = nodes.File; + type Flow = nodes.Flow; + type FlowPredicate = nodes.FlowPredicate; + type FlowType = nodes.FlowType; + type ForAwaitStatement = nodes.ForAwaitStatement; + type ForInStatement = nodes.ForInStatement; + type ForOfStatement = nodes.ForOfStatement; + type ForStatement = nodes.ForStatement; + type Function = nodes.Function; + type FunctionDeclaration = nodes.FunctionDeclaration; + type FunctionExpression = nodes.FunctionExpression; + type FunctionTypeAnnotation = nodes.FunctionTypeAnnotation; + type FunctionTypeParam = nodes.FunctionTypeParam; + type GeneratorExpression = nodes.GeneratorExpression; + type GenericTypeAnnotation = nodes.GenericTypeAnnotation; + type Identifier = nodes.Identifier; + type IfStatement = nodes.IfStatement; + type Import = nodes.Import; + type ImportDeclaration = nodes.ImportDeclaration; + type ImportDefaultSpecifier = nodes.ImportDefaultSpecifier; + type ImportNamespaceSpecifier = nodes.ImportNamespaceSpecifier; + type ImportSpecifier = nodes.ImportSpecifier; + type InferredPredicate = nodes.InferredPredicate; + type InterfaceDeclaration = nodes.InterfaceDeclaration; + type InterfaceExtends = nodes.InterfaceExtends; + type InterfaceTypeAnnotation = nodes.InterfaceTypeAnnotation; + type InterpreterDirective = nodes.InterpreterDirective; + type IntersectionTypeAnnotation = nodes.IntersectionTypeAnnotation; + type JSXAttribute = nodes.JSXAttribute; + type JSXClosingElement = nodes.JSXClosingElement; + type JSXClosingFragment = nodes.JSXClosingFragment; + type JSXElement = nodes.JSXElement; + type JSXEmptyExpression = nodes.JSXEmptyExpression; + type JSXExpressionContainer = nodes.JSXExpressionContainer; + type JSXFragment = nodes.JSXFragment; + type JSXIdentifier = nodes.JSXIdentifier; + type JSXMemberExpression = nodes.JSXMemberExpression; + type JSXNamespacedName = nodes.JSXNamespacedName; + type JSXOpeningElement = nodes.JSXOpeningElement; + type JSXOpeningFragment = nodes.JSXOpeningFragment; + type JSXSpreadAttribute = nodes.JSXSpreadAttribute; + type JSXSpreadChild = nodes.JSXSpreadChild; + type JSXText = nodes.JSXText; + type LabeledStatement = nodes.LabeledStatement; + type Line = nodes.Line; + type Literal = nodes.Literal; + type LogicalExpression = nodes.LogicalExpression; + type MemberExpression = nodes.MemberExpression; + type MemberTypeAnnotation = nodes.MemberTypeAnnotation; + type MetaProperty = nodes.MetaProperty; + type MethodDefinition = nodes.MethodDefinition; + type MixedTypeAnnotation = nodes.MixedTypeAnnotation; + type ModuleSpecifier = nodes.ModuleSpecifier; + type NewExpression = nodes.NewExpression; + type Node = nodes.Node; + type Noop = nodes.Noop; + type NullableTypeAnnotation = nodes.NullableTypeAnnotation; + type NullLiteral = nodes.NullLiteral; + type NullLiteralTypeAnnotation = nodes.NullLiteralTypeAnnotation; + type NullTypeAnnotation = nodes.NullTypeAnnotation; + type NumberLiteralTypeAnnotation = nodes.NumberLiteralTypeAnnotation; + type NumberTypeAnnotation = nodes.NumberTypeAnnotation; + type NumericLiteral = nodes.NumericLiteral; + type NumericLiteralTypeAnnotation = nodes.NumericLiteralTypeAnnotation; + type ObjectExpression = nodes.ObjectExpression; + type ObjectMethod = nodes.ObjectMethod; + type ObjectPattern = nodes.ObjectPattern; + type ObjectProperty = nodes.ObjectProperty; + type ObjectTypeAnnotation = nodes.ObjectTypeAnnotation; + type ObjectTypeCallProperty = nodes.ObjectTypeCallProperty; + type ObjectTypeIndexer = nodes.ObjectTypeIndexer; + type ObjectTypeInternalSlot = nodes.ObjectTypeInternalSlot; + type ObjectTypeProperty = nodes.ObjectTypeProperty; + type ObjectTypeSpreadProperty = nodes.ObjectTypeSpreadProperty; + type OpaqueType = nodes.OpaqueType; + type OptionalCallExpression = nodes.OptionalCallExpression; + type OptionalMemberExpression = nodes.OptionalMemberExpression; + type ParenthesizedExpression = nodes.ParenthesizedExpression; + type Pattern = nodes.Pattern; + type Position = nodes.Position; + type Printable = nodes.Printable; + type PrivateName = nodes.PrivateName; + type Program = nodes.Program; + type Property = nodes.Property; + type PropertyPattern = nodes.PropertyPattern; + type QualifiedTypeIdentifier = nodes.QualifiedTypeIdentifier; + type RegExpLiteral = nodes.RegExpLiteral; + type RestElement = nodes.RestElement; + type RestProperty = nodes.RestProperty; + type ReturnStatement = nodes.ReturnStatement; + type SequenceExpression = nodes.SequenceExpression; + type SourceLocation = nodes.SourceLocation; + type Specifier = nodes.Specifier; + type SpreadElement = nodes.SpreadElement; + type SpreadElementPattern = nodes.SpreadElementPattern; + type SpreadProperty = nodes.SpreadProperty; + type SpreadPropertyPattern = nodes.SpreadPropertyPattern; + type Statement = nodes.Statement; + type StringLiteral = nodes.StringLiteral; + type StringLiteralTypeAnnotation = nodes.StringLiteralTypeAnnotation; + type StringTypeAnnotation = nodes.StringTypeAnnotation; + type Super = nodes.Super; + type SwitchCase = nodes.SwitchCase; + type SwitchStatement = nodes.SwitchStatement; + type TaggedTemplateExpression = nodes.TaggedTemplateExpression; + type TemplateElement = nodes.TemplateElement; + type TemplateLiteral = nodes.TemplateLiteral; + type ThisExpression = nodes.ThisExpression; + type ThisTypeAnnotation = nodes.ThisTypeAnnotation; + type ThrowStatement = nodes.ThrowStatement; + type TryStatement = nodes.TryStatement; + type TSAnyKeyword = nodes.TSAnyKeyword; + type TSArrayType = nodes.TSArrayType; + type TSAsExpression = nodes.TSAsExpression; + type TSBooleanKeyword = nodes.TSBooleanKeyword; + type TSCallSignatureDeclaration = nodes.TSCallSignatureDeclaration; + type TSConditionalType = nodes.TSConditionalType; + type TSConstructorType = nodes.TSConstructorType; + type TSConstructSignatureDeclaration = nodes.TSConstructSignatureDeclaration; + type TSDeclareFunction = nodes.TSDeclareFunction; + type TSDeclareMethod = nodes.TSDeclareMethod; + type TSEnumDeclaration = nodes.TSEnumDeclaration; + type TSEnumMember = nodes.TSEnumMember; + type TSExportAssignment = nodes.TSExportAssignment; + type TSExpressionWithTypeArguments = nodes.TSExpressionWithTypeArguments; + type TSExternalModuleReference = nodes.TSExternalModuleReference; + type TSFunctionType = nodes.TSFunctionType; + type TSHasOptionalTypeAnnotation = nodes.TSHasOptionalTypeAnnotation; + type TSHasOptionalTypeParameters = nodes.TSHasOptionalTypeParameters; + type TSImportEqualsDeclaration = nodes.TSImportEqualsDeclaration; + type TSIndexedAccessType = nodes.TSIndexedAccessType; + type TSIndexSignature = nodes.TSIndexSignature; + type TSInferType = nodes.TSInferType; + type TSInterfaceBody = nodes.TSInterfaceBody; + type TSInterfaceDeclaration = nodes.TSInterfaceDeclaration; + type TSIntersectionType = nodes.TSIntersectionType; + type TSLiteralType = nodes.TSLiteralType; + type TSMappedType = nodes.TSMappedType; + type TSMethodSignature = nodes.TSMethodSignature; + type TSModuleBlock = nodes.TSModuleBlock; + type TSModuleDeclaration = nodes.TSModuleDeclaration; + type TSNamespaceExportDeclaration = nodes.TSNamespaceExportDeclaration; + type TSNeverKeyword = nodes.TSNeverKeyword; + type TSNonNullExpression = nodes.TSNonNullExpression; + type TSNullKeyword = nodes.TSNullKeyword; + type TSNumberKeyword = nodes.TSNumberKeyword; + type TSObjectKeyword = nodes.TSObjectKeyword; + type TSOptionalType = nodes.TSOptionalType; + type TSParameterProperty = nodes.TSParameterProperty; + type TSParenthesizedType = nodes.TSParenthesizedType; + type TSPropertySignature = nodes.TSPropertySignature; + type TSQualifiedName = nodes.TSQualifiedName; + type TSRestType = nodes.TSRestType; + type TSStringKeyword = nodes.TSStringKeyword; + type TSSymbolKeyword = nodes.TSSymbolKeyword; + type TSThisType = nodes.TSThisType; + type TSTupleType = nodes.TSTupleType; + type TSType = nodes.TSType; + type TSTypeAliasDeclaration = nodes.TSTypeAliasDeclaration; + type TSTypeAnnotation = nodes.TSTypeAnnotation; + type TSTypeAssertion = nodes.TSTypeAssertion; + type TSTypeLiteral = nodes.TSTypeLiteral; + type TSTypeOperator = nodes.TSTypeOperator; + type TSTypeParameter = nodes.TSTypeParameter; + type TSTypeParameterDeclaration = nodes.TSTypeParameterDeclaration; + type TSTypeParameterInstantiation = nodes.TSTypeParameterInstantiation; + type TSTypePredicate = nodes.TSTypePredicate; + type TSTypeQuery = nodes.TSTypeQuery; + type TSTypeReference = nodes.TSTypeReference; + type TSUndefinedKeyword = nodes.TSUndefinedKeyword; + type TSUnionType = nodes.TSUnionType; + type TSUnknownKeyword = nodes.TSUnknownKeyword; + type TSVoidKeyword = nodes.TSVoidKeyword; + type TupleTypeAnnotation = nodes.TupleTypeAnnotation; + type TypeAlias = nodes.TypeAlias; + type TypeAnnotation = nodes.TypeAnnotation; + type TypeCastExpression = nodes.TypeCastExpression; + type TypeofTypeAnnotation = nodes.TypeofTypeAnnotation; + type TypeParameter = nodes.TypeParameter; + type TypeParameterDeclaration = nodes.TypeParameterDeclaration; + type TypeParameterInstantiation = nodes.TypeParameterInstantiation; + type UnaryExpression = nodes.UnaryExpression; + type UnionTypeAnnotation = nodes.UnionTypeAnnotation; + type UpdateExpression = nodes.UpdateExpression; + type VariableDeclaration = nodes.VariableDeclaration; + type VariableDeclarator = nodes.VariableDeclarator; + type Variance = nodes.Variance; + type VoidTypeAnnotation = nodes.VoidTypeAnnotation; + type WhileStatement = nodes.WhileStatement; + type WithStatement = nodes.WithStatement; + type YieldExpression = nodes.YieldExpression; +} + +declare const core: core.JSCodeshift; +export = core; diff --git a/types/jscodeshift/src/template.d.ts b/types/jscodeshift/src/template.d.ts index 44bfab9be3..cfc0a3c543 100644 --- a/types/jscodeshift/src/template.d.ts +++ b/types/jscodeshift/src/template.d.ts @@ -1,14 +1,18 @@ -import { Parser } from "recast"; +import recast = require("recast"); -export interface Template { - /** Tagged template function. Parses the string as source and returns an array of Statement AST nodes. */ - statements(...args: any[]): any; - /** Tagged template function. Parses the string as source and returns an Statement AST node. */ - statement(...args: any[]): any; - /** Tagged template function. Parses the string as source and returns an Expression AST node. */ - expression(...args: any[]): any; +export = template; + +declare const template: typeof withParser; + +declare function withParser(parser: recast.Parser): template.Template; + +declare namespace template { + interface Template { + /** Tagged template function. Parses the string as source and returns an array of Statement AST nodes. */ + statements(...args: any[]): any; + /** Tagged template function. Parses the string as source and returns an Statement AST node. */ + statement(...args: any[]): any; + /** Tagged template function. Parses the string as source and returns an Expression AST node. */ + expression(...args: any[]): any; + } } - -export default function withParser(parser: Parser): Template; - -export { }; // to shut off automatic exporting diff --git a/types/jscodeshift/test/js-transforms/bind-this-to-bind-expression.ts b/types/jscodeshift/test/js-transforms/bind-this-to-bind-expression.ts index 6bd9be1fc8..1c6d8cda56 100644 --- a/types/jscodeshift/test/js-transforms/bind-this-to-bind-expression.ts +++ b/types/jscodeshift/test/js-transforms/bind-this-to-bind-expression.ts @@ -8,9 +8,9 @@ * let x = ::this.foo; * */ -import { Transform, MemberExpression } from "jscodeshift"; +import j = require("jscodeshift"); -const transform: Transform = (file, api) => { +const transform: j.Transform = (file, api) => { const j = api.jscodeshift; return j(file.source) // Find stuff that looks like this.xyz.bind(this) @@ -18,6 +18,8 @@ const transform: Transform = (file, api) => { // Ensure that .bind() is being called with only one argument, and that argument is "this". .filter(p => p.value.arguments.length === 1 && p.value.arguments[0].type === "ThisExpression") // We can now replace it with ::this.xyz - .replaceWith(p => j.bindExpression(null, (p.value.callee as MemberExpression).object)) + .replaceWith(p => j.bindExpression(null, (p.value.callee as j.MemberExpression).object)) .toSource(); }; + +export = transform; diff --git a/types/jscodeshift/test/js-transforms/props-to-destructuring.ts b/types/jscodeshift/test/js-transforms/props-to-destructuring.ts index 138d61a620..db4caff58c 100644 --- a/types/jscodeshift/test/js-transforms/props-to-destructuring.ts +++ b/types/jscodeshift/test/js-transforms/props-to-destructuring.ts @@ -20,7 +20,7 @@ * } * */ -import { Transform, VariableDeclarator, ObjectPattern, Identifier, BlockStatement } from "jscodeshift"; +import { Transform, VariableDeclarator, ObjectPattern, Identifier } from "jscodeshift"; // tslint:disable-next-line max-line-length const keywordsStr = 'this function if return var else for new in typeof while case break try catch delete throw switch continue default instanceof do void finally with debugger implements interface package private protected public static class enum export extends import super true false null abstract boolean byte char const double final float goto int long native short synchronized throws transient volatile'; diff --git a/types/jscodeshift/tsconfig.json b/types/jscodeshift/tsconfig.json index a2c90ce266..5937bd483a 100644 --- a/types/jscodeshift/tsconfig.json +++ b/types/jscodeshift/tsconfig.json @@ -18,6 +18,7 @@ }, "files": [ "index.d.ts", + "src/core.d.ts", "src/Collection.d.ts", "src/collections/JSXElement.d.ts", "src/collections/Node.d.ts", From 47ffe1a4ba623bc5c363aee2ddb725ecadbfb361 Mon Sep 17 00:00:00 2001 From: Brie <723547+brieb@users.noreply.github.com> Date: Wed, 6 Feb 2019 13:41:12 -0800 Subject: [PATCH 15/15] [jscodeshift] Pin ast-types and recast --- types/jscodeshift/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/types/jscodeshift/package.json b/types/jscodeshift/package.json index 6fd3d8b70f..a501c0f28d 100644 --- a/types/jscodeshift/package.json +++ b/types/jscodeshift/package.json @@ -1,7 +1,7 @@ { "private": true, "dependencies": { - "ast-types": "^0.12.1", - "recast": "^0.17.2" + "ast-types": "0.12.1", + "recast": "0.17.2" } }