@openrewrite/recipes-react 0.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +130 -0
- package/dist/index.js.map +1 -0
- package/dist/migration/change-component-prop-value.d.ts +20 -0
- package/dist/migration/change-component-prop-value.d.ts.map +1 -0
- package/dist/migration/change-component-prop-value.js +217 -0
- package/dist/migration/change-component-prop-value.js.map +1 -0
- package/dist/react-native/view-prop-types.d.ts +8 -0
- package/dist/react-native/view-prop-types.d.ts.map +1 -0
- package/dist/react-native/view-prop-types.js +65 -0
- package/dist/react-native/view-prop-types.js.map +1 -0
- package/dist/react16/error-boundaries.d.ts +8 -0
- package/dist/react16/error-boundaries.d.ts.map +1 -0
- package/dist/react16/error-boundaries.js +42 -0
- package/dist/react16/error-boundaries.js.map +1 -0
- package/dist/react16/find-dom-node.d.ts +8 -0
- package/dist/react16/find-dom-node.d.ts.map +1 -0
- package/dist/react16/find-dom-node.js +48 -0
- package/dist/react16/find-dom-node.js.map +1 -0
- package/dist/react16/react-dom-factories.d.ts +8 -0
- package/dist/react16/react-dom-factories.d.ts.map +1 -0
- package/dist/react16/react-dom-factories.js +72 -0
- package/dist/react16/react-dom-factories.js.map +1 -0
- package/dist/react16/react-prop-types.d.ts +8 -0
- package/dist/react16/react-prop-types.d.ts.map +1 -0
- package/dist/react16/react-prop-types.js +69 -0
- package/dist/react16/react-prop-types.js.map +1 -0
- package/dist/react16/react-to-react-dom.d.ts +8 -0
- package/dist/react16/react-to-react-dom.d.ts.map +1 -0
- package/dist/react16/react-to-react-dom.js +97 -0
- package/dist/react16/react-to-react-dom.js.map +1 -0
- package/dist/react16/replace-create-factory.d.ts +8 -0
- package/dist/react16/replace-create-factory.d.ts.map +1 -0
- package/dist/react16/replace-create-factory.js +69 -0
- package/dist/react16/replace-create-factory.js.map +1 -0
- package/dist/react16/upgrade-to-react-16.d.ts +8 -0
- package/dist/react16/upgrade-to-react-16.d.ts.map +1 -0
- package/dist/react16/upgrade-to-react-16.js +41 -0
- package/dist/react16/upgrade-to-react-16.js.map +1 -0
- package/dist/react17/remove-event-persist.d.ts +8 -0
- package/dist/react17/remove-event-persist.d.ts.map +1 -0
- package/dist/react17/remove-event-persist.js +114 -0
- package/dist/react17/remove-event-persist.js.map +1 -0
- package/dist/react17/rename-unsafe-lifecycles.d.ts +8 -0
- package/dist/react17/rename-unsafe-lifecycles.d.ts.map +1 -0
- package/dist/react17/rename-unsafe-lifecycles.js +48 -0
- package/dist/react17/rename-unsafe-lifecycles.js.map +1 -0
- package/dist/react17/update-react-imports.d.ts +8 -0
- package/dist/react17/update-react-imports.d.ts.map +1 -0
- package/dist/react17/update-react-imports.js +40 -0
- package/dist/react17/update-react-imports.js.map +1 -0
- package/dist/react17/upgrade-to-react-17.d.ts +8 -0
- package/dist/react17/upgrade-to-react-17.d.ts.map +1 -0
- package/dist/react17/upgrade-to-react-17.js +37 -0
- package/dist/react17/upgrade-to-react-17.js.map +1 -0
- package/dist/react18/remove-unstable-batched-updates.d.ts +8 -0
- package/dist/react18/remove-unstable-batched-updates.d.ts.map +1 -0
- package/dist/react18/remove-unstable-batched-updates.js +170 -0
- package/dist/react18/remove-unstable-batched-updates.js.map +1 -0
- package/dist/react18/replace-reactdom-render.d.ts +8 -0
- package/dist/react18/replace-reactdom-render.d.ts.map +1 -0
- package/dist/react18/replace-reactdom-render.js +55 -0
- package/dist/react18/replace-reactdom-render.js.map +1 -0
- package/dist/react18/replace-render-callback.d.ts +8 -0
- package/dist/react18/replace-render-callback.d.ts.map +1 -0
- package/dist/react18/replace-render-callback.js +60 -0
- package/dist/react18/replace-render-callback.js.map +1 -0
- package/dist/react18/replace-unmount-component-at-node.d.ts +8 -0
- package/dist/react18/replace-unmount-component-at-node.d.ts.map +1 -0
- package/dist/react18/replace-unmount-component-at-node.js +54 -0
- package/dist/react18/replace-unmount-component-at-node.js.map +1 -0
- package/dist/react18/upgrade-to-react-18.d.ts +8 -0
- package/dist/react18/upgrade-to-react-18.d.ts.map +1 -0
- package/dist/react18/upgrade-to-react-18.js +39 -0
- package/dist/react18/upgrade-to-react-18.js.map +1 -0
- package/dist/react19/deprecated-react-types.d.ts +8 -0
- package/dist/react19/deprecated-react-types.d.ts.map +1 -0
- package/dist/react19/deprecated-react-types.js +135 -0
- package/dist/react19/deprecated-react-types.js.map +1 -0
- package/dist/react19/find-context-consumer.d.ts +9 -0
- package/dist/react19/find-context-consumer.d.ts.map +1 -0
- package/dist/react19/find-context-consumer.js +128 -0
- package/dist/react19/find-context-consumer.js.map +1 -0
- package/dist/react19/find-deprecated-reactdom-apis.d.ts +9 -0
- package/dist/react19/find-deprecated-reactdom-apis.d.ts.map +1 -0
- package/dist/react19/find-deprecated-reactdom-apis.js +132 -0
- package/dist/react19/find-deprecated-reactdom-apis.js.map +1 -0
- package/dist/react19/find-element-ref.d.ts +9 -0
- package/dist/react19/find-element-ref.d.ts.map +1 -0
- package/dist/react19/find-element-ref.js +88 -0
- package/dist/react19/find-element-ref.js.map +1 -0
- package/dist/react19/find-legacy-context-api.d.ts +9 -0
- package/dist/react19/find-legacy-context-api.d.ts.map +1 -0
- package/dist/react19/find-legacy-context-api.js +163 -0
- package/dist/react19/find-legacy-context-api.js.map +1 -0
- package/dist/react19/no-implicit-ref-callback-return.d.ts +8 -0
- package/dist/react19/no-implicit-ref-callback-return.d.ts.map +1 -0
- package/dist/react19/no-implicit-ref-callback-return.js +107 -0
- package/dist/react19/no-implicit-ref-callback-return.js.map +1 -0
- package/dist/react19/remove-context-provider.d.ts +8 -0
- package/dist/react19/remove-context-provider.d.ts.map +1 -0
- package/dist/react19/remove-context-provider.js +59 -0
- package/dist/react19/remove-context-provider.js.map +1 -0
- package/dist/react19/remove-forward-ref.d.ts +8 -0
- package/dist/react19/remove-forward-ref.d.ts.map +1 -0
- package/dist/react19/remove-forward-ref.js +73 -0
- package/dist/react19/remove-forward-ref.js.map +1 -0
- package/dist/react19/remove-prop-types.d.ts +8 -0
- package/dist/react19/remove-prop-types.d.ts.map +1 -0
- package/dist/react19/remove-prop-types.js +76 -0
- package/dist/react19/remove-prop-types.js.map +1 -0
- package/dist/react19/remove-react-fc.d.ts +8 -0
- package/dist/react19/remove-react-fc.d.ts.map +1 -0
- package/dist/react19/remove-react-fc.js +149 -0
- package/dist/react19/remove-react-fc.js.map +1 -0
- package/dist/react19/replace-act-import.d.ts +9 -0
- package/dist/react19/replace-act-import.d.ts.map +1 -0
- package/dist/react19/replace-act-import.js +34 -0
- package/dist/react19/replace-act-import.js.map +1 -0
- package/dist/react19/replace-default-props.d.ts +8 -0
- package/dist/react19/replace-default-props.d.ts.map +1 -0
- package/dist/react19/replace-default-props.js +195 -0
- package/dist/react19/replace-default-props.js.map +1 -0
- package/dist/react19/replace-react-shallow-renderer.d.ts +8 -0
- package/dist/react19/replace-react-shallow-renderer.d.ts.map +1 -0
- package/dist/react19/replace-react-shallow-renderer.js +69 -0
- package/dist/react19/replace-react-shallow-renderer.js.map +1 -0
- package/dist/react19/replace-reactdom-hydrate.d.ts +8 -0
- package/dist/react19/replace-reactdom-hydrate.d.ts.map +1 -0
- package/dist/react19/replace-reactdom-hydrate.js +55 -0
- package/dist/react19/replace-reactdom-hydrate.js.map +1 -0
- package/dist/react19/replace-string-ref.d.ts +8 -0
- package/dist/react19/replace-string-ref.d.ts.map +1 -0
- package/dist/react19/replace-string-ref.js +75 -0
- package/dist/react19/replace-string-ref.js.map +1 -0
- package/dist/react19/replace-use-form-state.d.ts +8 -0
- package/dist/react19/replace-use-form-state.d.ts.map +1 -0
- package/dist/react19/replace-use-form-state.js +54 -0
- package/dist/react19/replace-use-form-state.js.map +1 -0
- package/dist/react19/upgrade-to-react-19.d.ts +8 -0
- package/dist/react19/upgrade-to-react-19.d.ts.map +1 -0
- package/dist/react19/upgrade-to-react-19.js +59 -0
- package/dist/react19/upgrade-to-react-19.js.map +1 -0
- package/dist/react19/use-context-hook.d.ts +8 -0
- package/dist/react19/use-context-hook.d.ts.map +1 -0
- package/dist/react19/use-context-hook.js +54 -0
- package/dist/react19/use-context-hook.js.map +1 -0
- package/dist/react19/use-ref-required-initial.d.ts +8 -0
- package/dist/react19/use-ref-required-initial.d.ts.map +1 -0
- package/dist/react19/use-ref-required-initial.js +74 -0
- package/dist/react19/use-ref-required-initial.js.map +1 -0
- package/dist/refactoring/class-to-functional.d.ts +8 -0
- package/dist/refactoring/class-to-functional.d.ts.map +1 -0
- package/dist/refactoring/class-to-functional.js +205 -0
- package/dist/refactoring/class-to-functional.js.map +1 -0
- package/dist/refactoring/create-class-to-es6.d.ts +8 -0
- package/dist/refactoring/create-class-to-es6.d.ts.map +1 -0
- package/dist/refactoring/create-class-to-es6.js +289 -0
- package/dist/refactoring/create-class-to-es6.js.map +1 -0
- package/dist/refactoring/create-element-to-jsx.d.ts +8 -0
- package/dist/refactoring/create-element-to-jsx.d.ts.map +1 -0
- package/dist/refactoring/create-element-to-jsx.js +167 -0
- package/dist/refactoring/create-element-to-jsx.js.map +1 -0
- package/dist/refactoring/manual-bind-to-arrow.d.ts +8 -0
- package/dist/refactoring/manual-bind-to-arrow.d.ts.map +1 -0
- package/dist/refactoring/manual-bind-to-arrow.js +134 -0
- package/dist/refactoring/manual-bind-to-arrow.js.map +1 -0
- package/dist/refactoring/pure-render-mixin.d.ts +8 -0
- package/dist/refactoring/pure-render-mixin.d.ts.map +1 -0
- package/dist/refactoring/pure-render-mixin.js +253 -0
- package/dist/refactoring/pure-render-mixin.js.map +1 -0
- package/dist/refactoring/sort-comp.d.ts +8 -0
- package/dist/refactoring/sort-comp.d.ts.map +1 -0
- package/dist/refactoring/sort-comp.js +128 -0
- package/dist/refactoring/sort-comp.js.map +1 -0
- package/dist/search/find-hook-usage.d.ts +9 -0
- package/dist/search/find-hook-usage.d.ts.map +1 -0
- package/dist/search/find-hook-usage.js +262 -0
- package/dist/search/find-hook-usage.js.map +1 -0
- package/dist/search/find-prop-usage.d.ts +15 -0
- package/dist/search/find-prop-usage.d.ts.map +1 -0
- package/dist/search/find-prop-usage.js +177 -0
- package/dist/search/find-prop-usage.js.map +1 -0
- package/dist/search/find-react-component.d.ts +15 -0
- package/dist/search/find-react-component.d.ts.map +1 -0
- package/dist/search/find-react-component.js +260 -0
- package/dist/search/find-react-component.js.map +1 -0
- package/dist/search/find-server-rendering-usage.d.ts +9 -0
- package/dist/search/find-server-rendering-usage.d.ts.map +1 -0
- package/dist/search/find-server-rendering-usage.js +131 -0
- package/dist/search/find-server-rendering-usage.js.map +1 -0
- package/dist/simplify-object-pattern-property.d.ts +8 -0
- package/dist/simplify-object-pattern-property.d.ts.map +1 -0
- package/dist/simplify-object-pattern-property.js +59 -0
- package/dist/simplify-object-pattern-property.js.map +1 -0
- package/dist/simplify-react-imports.d.ts +8 -0
- package/dist/simplify-react-imports.d.ts.map +1 -0
- package/dist/simplify-react-imports.js +199 -0
- package/dist/simplify-react-imports.js.map +1 -0
- package/package.json +39 -0
- package/src/index.ts +149 -0
- package/src/migration/change-component-prop-value.ts +268 -0
- package/src/react-native/view-prop-types.ts +63 -0
- package/src/react16/error-boundaries.ts +46 -0
- package/src/react16/find-dom-node.ts +55 -0
- package/src/react16/react-dom-factories.ts +99 -0
- package/src/react16/react-prop-types.ts +71 -0
- package/src/react16/react-to-react-dom.ts +104 -0
- package/src/react16/replace-create-factory.ts +96 -0
- package/src/react16/upgrade-to-react-16.ts +37 -0
- package/src/react17/remove-event-persist.ts +121 -0
- package/src/react17/rename-unsafe-lifecycles.ts +57 -0
- package/src/react17/update-react-imports.ts +50 -0
- package/src/react17/upgrade-to-react-17.ts +30 -0
- package/src/react18/remove-unstable-batched-updates.ts +192 -0
- package/src/react18/replace-reactdom-render.ts +68 -0
- package/src/react18/replace-render-callback.ts +66 -0
- package/src/react18/replace-unmount-component-at-node.ts +66 -0
- package/src/react18/upgrade-to-react-18.ts +33 -0
- package/src/react19/deprecated-react-types.ts +120 -0
- package/src/react19/find-context-consumer.ts +127 -0
- package/src/react19/find-deprecated-reactdom-apis.ts +125 -0
- package/src/react19/find-element-ref.ts +86 -0
- package/src/react19/find-legacy-context-api.ts +157 -0
- package/src/react19/no-implicit-ref-callback-return.ts +123 -0
- package/src/react19/remove-context-provider.ts +87 -0
- package/src/react19/remove-forward-ref.ts +69 -0
- package/src/react19/remove-prop-types.ts +86 -0
- package/src/react19/remove-react-fc.ts +247 -0
- package/src/react19/replace-act-import.ts +36 -0
- package/src/react19/replace-default-props.ts +220 -0
- package/src/react19/replace-react-shallow-renderer.ts +75 -0
- package/src/react19/replace-reactdom-hydrate.ts +67 -0
- package/src/react19/replace-string-ref.ts +89 -0
- package/src/react19/replace-use-form-state.ts +66 -0
- package/src/react19/upgrade-to-react-19.ts +66 -0
- package/src/react19/use-context-hook.ts +67 -0
- package/src/react19/use-ref-required-initial.ts +75 -0
- package/src/refactoring/class-to-functional.ts +229 -0
- package/src/refactoring/create-class-to-es6.ts +309 -0
- package/src/refactoring/create-element-to-jsx.ts +200 -0
- package/src/refactoring/manual-bind-to-arrow.ts +139 -0
- package/src/refactoring/pure-render-mixin.ts +346 -0
- package/src/refactoring/sort-comp.ts +135 -0
- package/src/search/find-hook-usage.ts +226 -0
- package/src/search/find-prop-usage.ts +176 -0
- package/src/search/find-react-component.ts +254 -0
- package/src/search/find-server-rendering-usage.ts +120 -0
- package/src/simplify-object-pattern-property.ts +71 -0
- package/src/simplify-react-imports.ts +241 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import {ExecutionContext, Recipe, TreeVisitor} from "@openrewrite/rewrite";
|
|
2
|
+
import {JavaScriptVisitor} from "@openrewrite/rewrite/javascript";
|
|
3
|
+
import {isIdentifier, J} from "@openrewrite/rewrite/java";
|
|
4
|
+
import {create} from "mutative";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Adds `undefined` argument to `useRef()` calls with no arguments.
|
|
8
|
+
*
|
|
9
|
+
* In `@types/react` 19, `useRef()` with no arguments is no longer valid.
|
|
10
|
+
* You must pass an explicit initial value, typically `undefined` or `null`.
|
|
11
|
+
*
|
|
12
|
+
* Before:
|
|
13
|
+
* ```tsx
|
|
14
|
+
* const ref = useRef();
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* After:
|
|
18
|
+
* ```tsx
|
|
19
|
+
* const ref = useRef(undefined);
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* @see https://react.dev/blog/2024/04/25/react-19-upgrade-guide#different-types
|
|
23
|
+
*/
|
|
24
|
+
export class UseRefRequiredInitial extends Recipe {
|
|
25
|
+
readonly name = "org.openrewrite.react.19.use-ref-required-initial";
|
|
26
|
+
readonly displayName: string = "Add initial value to `useRef()` calls";
|
|
27
|
+
readonly description: string = "Adds `undefined` as initial argument to `useRef()` calls with no arguments. Required by `@types/react` 19.";
|
|
28
|
+
|
|
29
|
+
async editor(): Promise<TreeVisitor<any, ExecutionContext>> {
|
|
30
|
+
return new class extends JavaScriptVisitor<ExecutionContext> {
|
|
31
|
+
override async visitMethodInvocation(method: J.MethodInvocation, ctx: ExecutionContext): Promise<J | undefined> {
|
|
32
|
+
let m = await super.visitMethodInvocation(method, ctx) as J.MethodInvocation;
|
|
33
|
+
|
|
34
|
+
// Match useRef() or React.useRef() with no arguments
|
|
35
|
+
if (m.name.simpleName !== 'useRef') return m;
|
|
36
|
+
|
|
37
|
+
// Check it has no arguments (empty args list)
|
|
38
|
+
const args = m.arguments;
|
|
39
|
+
if (!args) return m;
|
|
40
|
+
|
|
41
|
+
const argElements = args.elements;
|
|
42
|
+
if (!argElements) return m;
|
|
43
|
+
|
|
44
|
+
// Empty args means a single J.Empty element
|
|
45
|
+
if (argElements.length === 1) {
|
|
46
|
+
const firstArg = argElements[0];
|
|
47
|
+
const argNode = (firstArg as any).element ?? firstArg;
|
|
48
|
+
if (argNode.kind === J.Kind.Empty) {
|
|
49
|
+
// Replace the empty arg with `undefined`
|
|
50
|
+
const undefinedIdent: J.Identifier = {
|
|
51
|
+
kind: J.Kind.Identifier,
|
|
52
|
+
id: argNode.id,
|
|
53
|
+
prefix: argNode.prefix,
|
|
54
|
+
markers: argNode.markers,
|
|
55
|
+
annotations: [],
|
|
56
|
+
simpleName: 'undefined'
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
return create(m, draft => {
|
|
60
|
+
const draftArg = draft.arguments!.elements[0];
|
|
61
|
+
const draftArgPadded = draftArg as any;
|
|
62
|
+
if (draftArgPadded.element) {
|
|
63
|
+
draftArgPadded.element = undefinedIdent;
|
|
64
|
+
} else {
|
|
65
|
+
draft.arguments!.elements[0] = undefinedIdent as any;
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return m;
|
|
72
|
+
}
|
|
73
|
+
}();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import {ExecutionContext, Recipe, TreeVisitor} from "@openrewrite/rewrite";
|
|
2
|
+
import {capture, JavaScriptVisitor, pattern, raw, rewrite, template} from "@openrewrite/rewrite/javascript";
|
|
3
|
+
import {J} from "@openrewrite/rewrite/java";
|
|
4
|
+
|
|
5
|
+
const LIFECYCLE_METHODS = new Set([
|
|
6
|
+
'componentWillMount', 'UNSAFE_componentWillMount',
|
|
7
|
+
'componentDidMount', 'componentWillReceiveProps',
|
|
8
|
+
'UNSAFE_componentWillReceiveProps', 'shouldComponentUpdate',
|
|
9
|
+
'componentWillUpdate', 'UNSAFE_componentWillUpdate',
|
|
10
|
+
'getSnapshotBeforeUpdate', 'componentDidUpdate',
|
|
11
|
+
'componentDidCatch', 'componentWillUnmount',
|
|
12
|
+
'getDerivedStateFromProps', 'getDerivedStateFromError',
|
|
13
|
+
'getDefaultProps', 'getInitialState',
|
|
14
|
+
]);
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Converts simple class components (render-only) to functional components.
|
|
18
|
+
*
|
|
19
|
+
* Only transforms class components that:
|
|
20
|
+
* - Have only a `render` method (no other instance methods)
|
|
21
|
+
* - Have no state
|
|
22
|
+
* - Have no lifecycle methods
|
|
23
|
+
* - Have no refs
|
|
24
|
+
*
|
|
25
|
+
* Before:
|
|
26
|
+
* ```tsx
|
|
27
|
+
* class MyComponent extends React.Component {
|
|
28
|
+
* render() {
|
|
29
|
+
* return <div>{this.props.name}</div>;
|
|
30
|
+
* }
|
|
31
|
+
* }
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* After:
|
|
35
|
+
* ```tsx
|
|
36
|
+
* function MyComponent(props) {
|
|
37
|
+
* return <div>{props.name}</div>;
|
|
38
|
+
* }
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* @see https://github.com/reactjs/react-codemod#pure-component
|
|
42
|
+
*/
|
|
43
|
+
export class ClassToFunctional extends Recipe {
|
|
44
|
+
readonly name = "org.openrewrite.react.refactoring.class-to-functional";
|
|
45
|
+
readonly displayName: string = "Convert class components to functional components";
|
|
46
|
+
readonly description: string = "Converts simple render-only class components to functional components.";
|
|
47
|
+
|
|
48
|
+
async editor(): Promise<TreeVisitor<any, ExecutionContext>> {
|
|
49
|
+
// Rewrite rules for replacing this.props references
|
|
50
|
+
const field = capture();
|
|
51
|
+
const thisPropsFieldRule = rewrite(() => ({
|
|
52
|
+
before: pattern`this.props.${field}`,
|
|
53
|
+
after: template`props.${field}`
|
|
54
|
+
}));
|
|
55
|
+
const thisPropsRule = rewrite(() => ({
|
|
56
|
+
before: pattern`this.props`,
|
|
57
|
+
after: template`props`
|
|
58
|
+
}));
|
|
59
|
+
|
|
60
|
+
return new class extends JavaScriptVisitor<ExecutionContext> {
|
|
61
|
+
override async visitClassDeclaration(classDecl: J.ClassDeclaration, ctx: ExecutionContext): Promise<J | undefined> {
|
|
62
|
+
let cd = await super.visitClassDeclaration(classDecl, ctx) as J.ClassDeclaration;
|
|
63
|
+
|
|
64
|
+
if (!this.extendsReactComponent(cd)) {
|
|
65
|
+
return cd;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const analysis = this.analyzeClassBody(cd);
|
|
69
|
+
if (!analysis.canConvert) {
|
|
70
|
+
return cd;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const renderMethod = analysis.renderMethod!;
|
|
74
|
+
if (!renderMethod.body) {
|
|
75
|
+
return cd;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Replace this.props references with props in the render body
|
|
79
|
+
const transformedBody = await this.replaceThisProps(renderMethod.body, ctx);
|
|
80
|
+
|
|
81
|
+
// Use template to construct the function declaration
|
|
82
|
+
const className = cd.name.simpleName;
|
|
83
|
+
const funcDecl = await template`function ${raw(className)}(props) {}`.apply(cd, this.cursor);
|
|
84
|
+
if (!funcDecl) return cd;
|
|
85
|
+
|
|
86
|
+
// Adjust render body indentation to match function (not class) nesting
|
|
87
|
+
const classBodyIndent = this.getIndent(renderMethod.prefix);
|
|
88
|
+
const adjustedBody = this.adjustIndentation(transformedBody as J.Block, classBodyIndent);
|
|
89
|
+
|
|
90
|
+
// Splice the transformed render body into the template-constructed function
|
|
91
|
+
const func = funcDecl as J.MethodDeclaration;
|
|
92
|
+
return {...func, body: adjustedBody} as any as J.MethodDeclaration;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
private extendsReactComponent(cd: J.ClassDeclaration): boolean {
|
|
96
|
+
if (!cd.extends) return false;
|
|
97
|
+
const extendsType = cd.extends.element;
|
|
98
|
+
|
|
99
|
+
if (extendsType.kind === J.Kind.FieldAccess) {
|
|
100
|
+
const fa = extendsType as J.FieldAccess;
|
|
101
|
+
const name = fa.name.element.simpleName;
|
|
102
|
+
if (name !== 'Component' && name !== 'PureComponent') return false;
|
|
103
|
+
if (fa.target.kind !== J.Kind.Identifier) return false;
|
|
104
|
+
return (fa.target as J.Identifier).simpleName === 'React';
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (extendsType.kind === J.Kind.Identifier) {
|
|
108
|
+
const name = (extendsType as J.Identifier).simpleName;
|
|
109
|
+
return name === 'Component' || name === 'PureComponent';
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
private analyzeClassBody(cd: J.ClassDeclaration): {
|
|
116
|
+
canConvert: boolean;
|
|
117
|
+
renderMethod?: J.MethodDeclaration;
|
|
118
|
+
} {
|
|
119
|
+
let renderMethod: J.MethodDeclaration | undefined;
|
|
120
|
+
let hasDisqualifyingMember = false;
|
|
121
|
+
|
|
122
|
+
for (const stmt of cd.body.statements) {
|
|
123
|
+
const s = stmt.element;
|
|
124
|
+
if (!s) continue;
|
|
125
|
+
|
|
126
|
+
if (s.kind === J.Kind.MethodDeclaration) {
|
|
127
|
+
const method = s as J.MethodDeclaration;
|
|
128
|
+
const name = method.name.simpleName;
|
|
129
|
+
|
|
130
|
+
if (name === 'render') {
|
|
131
|
+
renderMethod = method;
|
|
132
|
+
} else if (name === 'constructor') {
|
|
133
|
+
if (!this.isSimpleConstructor(method)) {
|
|
134
|
+
hasDisqualifyingMember = true;
|
|
135
|
+
}
|
|
136
|
+
} else if (LIFECYCLE_METHODS.has(name)) {
|
|
137
|
+
hasDisqualifyingMember = true;
|
|
138
|
+
} else {
|
|
139
|
+
const isStatic = method.modifiers?.some(m => m.keyword === 'static');
|
|
140
|
+
if (!isStatic) {
|
|
141
|
+
hasDisqualifyingMember = true;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
} else if (s.kind === J.Kind.VariableDeclarations) {
|
|
145
|
+
const vars = s as J.VariableDeclarations;
|
|
146
|
+
const isStatic = vars.modifiers?.some(m => m.keyword === 'static');
|
|
147
|
+
if (!isStatic) {
|
|
148
|
+
hasDisqualifyingMember = true;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
canConvert: !!renderMethod && !hasDisqualifyingMember,
|
|
155
|
+
renderMethod
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
private isSimpleConstructor(method: J.MethodDeclaration): boolean {
|
|
160
|
+
if (!method.body) return true;
|
|
161
|
+
const stmts = method.body.statements;
|
|
162
|
+
if (stmts.length === 0) return true;
|
|
163
|
+
if (stmts.length > 1) return false;
|
|
164
|
+
|
|
165
|
+
const firstStmt = stmts[0].element;
|
|
166
|
+
if (!firstStmt) return true;
|
|
167
|
+
|
|
168
|
+
if (firstStmt.kind === J.Kind.MethodInvocation) {
|
|
169
|
+
const call = firstStmt as J.MethodInvocation;
|
|
170
|
+
return call.name.simpleName === 'super';
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
private async replaceThisProps(body: J.Block, ctx: ExecutionContext): Promise<J.Block> {
|
|
177
|
+
const replacer = new class extends JavaScriptVisitor<ExecutionContext> {
|
|
178
|
+
override async visitFieldAccess(fieldAccess: J.FieldAccess, ctx: ExecutionContext): Promise<J | undefined> {
|
|
179
|
+
let fa = await super.visitFieldAccess(fieldAccess, ctx) as J.FieldAccess;
|
|
180
|
+
|
|
181
|
+
let result = await thisPropsFieldRule.tryOn(this.cursor, fa);
|
|
182
|
+
if (result) return result;
|
|
183
|
+
|
|
184
|
+
result = await thisPropsRule.tryOn(this.cursor, fa);
|
|
185
|
+
if (result) return result;
|
|
186
|
+
|
|
187
|
+
return fa;
|
|
188
|
+
}
|
|
189
|
+
}();
|
|
190
|
+
|
|
191
|
+
return await replacer.visit(body, ctx) as J.Block;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
private getIndent(space: any): string {
|
|
195
|
+
if (!space || typeof space.whitespace !== 'string') return '';
|
|
196
|
+
const ws = space.whitespace as string;
|
|
197
|
+
const lastNewline = ws.lastIndexOf('\n');
|
|
198
|
+
if (lastNewline === -1) return '';
|
|
199
|
+
return ws.substring(lastNewline + 1);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
private adjustIndentation(body: J.Block, indentToRemove: string): J.Block {
|
|
203
|
+
if (!indentToRemove) return body;
|
|
204
|
+
return {
|
|
205
|
+
...body,
|
|
206
|
+
statements: body.statements.map(stmt => ({
|
|
207
|
+
...stmt,
|
|
208
|
+
element: stmt.element ? {
|
|
209
|
+
...stmt.element,
|
|
210
|
+
prefix: this.removeIndent(stmt.element.prefix, indentToRemove)
|
|
211
|
+
} : stmt.element
|
|
212
|
+
})) as any,
|
|
213
|
+
end: this.removeIndent(body.end, indentToRemove)
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
private removeIndent(space: any, indent: string): any {
|
|
218
|
+
if (!space || typeof space.whitespace !== 'string') return space;
|
|
219
|
+
const ws = space.whitespace as string;
|
|
220
|
+
const adjusted = ws.split('\n').map((line: string, i: number) => {
|
|
221
|
+
if (i === 0) return line;
|
|
222
|
+
if (line.startsWith(indent)) return line.substring(indent.length);
|
|
223
|
+
return line;
|
|
224
|
+
}).join('\n');
|
|
225
|
+
return adjusted !== ws ? {...space, whitespace: adjusted} : space;
|
|
226
|
+
}
|
|
227
|
+
}();
|
|
228
|
+
}
|
|
229
|
+
}
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
import {ExecutionContext, printer, Recipe, TreeVisitor} from "@openrewrite/rewrite";
|
|
2
|
+
import {JavaScriptVisitor, JS, raw, template} from "@openrewrite/rewrite/javascript";
|
|
3
|
+
import {J} from "@openrewrite/rewrite/java";
|
|
4
|
+
|
|
5
|
+
const LIFECYCLE_METHODS = new Set([
|
|
6
|
+
'render',
|
|
7
|
+
'componentWillMount', 'UNSAFE_componentWillMount',
|
|
8
|
+
'componentDidMount', 'componentWillReceiveProps',
|
|
9
|
+
'UNSAFE_componentWillReceiveProps', 'shouldComponentUpdate',
|
|
10
|
+
'componentWillUpdate', 'UNSAFE_componentWillUpdate',
|
|
11
|
+
'getSnapshotBeforeUpdate', 'componentDidUpdate',
|
|
12
|
+
'componentDidCatch', 'componentWillUnmount',
|
|
13
|
+
'getDerivedStateFromProps', 'getDerivedStateFromError',
|
|
14
|
+
]);
|
|
15
|
+
|
|
16
|
+
const SEMICOLON_KIND = "org.openrewrite.java.marker.Semicolon";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Converts `React.createClass()` to ES6 class syntax.
|
|
20
|
+
*
|
|
21
|
+
* Handles:
|
|
22
|
+
* - `getInitialState()` → class property `state = {...}`
|
|
23
|
+
* - `getDefaultProps()` → `static defaultProps = {...}`
|
|
24
|
+
* - Static properties (propTypes, contextTypes) → class statics
|
|
25
|
+
* - Auto-binding of methods → arrow function class fields
|
|
26
|
+
* - Lifecycle methods and render → regular class methods
|
|
27
|
+
* - Skips conversion if mixins are present
|
|
28
|
+
*
|
|
29
|
+
* @see https://github.com/reactjs/react-codemod#class
|
|
30
|
+
*/
|
|
31
|
+
export class CreateClassToES6 extends Recipe {
|
|
32
|
+
readonly name = "org.openrewrite.react.refactoring.create-class-to-es6";
|
|
33
|
+
readonly displayName: string = "Convert `createClass` to ES6 class";
|
|
34
|
+
readonly description: string = "Converts `React.createClass()` and `createReactClass()` calls to ES6 class syntax.";
|
|
35
|
+
|
|
36
|
+
async editor(): Promise<TreeVisitor<any, ExecutionContext>> {
|
|
37
|
+
return new class extends JavaScriptVisitor<ExecutionContext> {
|
|
38
|
+
private convertedClassIds = new Set<string>();
|
|
39
|
+
|
|
40
|
+
private stripSemicolonFromConverted(statements: any[]): { modified: boolean, statements: any[] } {
|
|
41
|
+
let modified = false;
|
|
42
|
+
const newStatements = statements.map((stmt: any) => {
|
|
43
|
+
if (stmt.element && this.convertedClassIds.has(stmt.element.id)) {
|
|
44
|
+
const hasSemicolon = stmt.markers?.markers?.some(
|
|
45
|
+
(m: any) => m.kind === SEMICOLON_KIND
|
|
46
|
+
);
|
|
47
|
+
if (hasSemicolon) {
|
|
48
|
+
modified = true;
|
|
49
|
+
return {
|
|
50
|
+
...stmt,
|
|
51
|
+
markers: {
|
|
52
|
+
...stmt.markers,
|
|
53
|
+
markers: stmt.markers.markers.filter(
|
|
54
|
+
(m: any) => m.kind !== SEMICOLON_KIND
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return stmt;
|
|
61
|
+
});
|
|
62
|
+
return { modified, statements: newStatements };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
override async visitJsCompilationUnit(cu: JS.CompilationUnit, ctx: ExecutionContext): Promise<J | undefined> {
|
|
66
|
+
let c = await super.visitJsCompilationUnit(cu, ctx) as JS.CompilationUnit;
|
|
67
|
+
if (this.convertedClassIds.size === 0) return c;
|
|
68
|
+
|
|
69
|
+
const result = this.stripSemicolonFromConverted(c.statements as any);
|
|
70
|
+
return result.modified ? {...c, statements: result.statements} as any : c;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
override async visitBlock(block: J.Block, ctx: ExecutionContext): Promise<J | undefined> {
|
|
74
|
+
let b = await super.visitBlock(block, ctx) as J.Block;
|
|
75
|
+
if (this.convertedClassIds.size === 0) return b;
|
|
76
|
+
|
|
77
|
+
const result = this.stripSemicolonFromConverted(b.statements as any);
|
|
78
|
+
return result.modified ? {...b, statements: result.statements} as any : b;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
override async visitVariableDeclarations(varDecl: J.VariableDeclarations, ctx: ExecutionContext): Promise<J | undefined> {
|
|
82
|
+
let vd = await super.visitVariableDeclarations(varDecl, ctx) as J.VariableDeclarations;
|
|
83
|
+
|
|
84
|
+
if (!vd.variables || vd.variables.length !== 1) return vd;
|
|
85
|
+
|
|
86
|
+
const namedVar = vd.variables[0].element as J.VariableDeclarations.NamedVariable;
|
|
87
|
+
if (!namedVar.initializer) return vd;
|
|
88
|
+
|
|
89
|
+
const init = namedVar.initializer.element;
|
|
90
|
+
if (init.kind !== J.Kind.MethodInvocation) return vd;
|
|
91
|
+
|
|
92
|
+
const methodInv = init as J.MethodInvocation;
|
|
93
|
+
if (!this.isCreateClassCall(methodInv)) return vd;
|
|
94
|
+
|
|
95
|
+
const componentName = (namedVar.name as J.Identifier).simpleName;
|
|
96
|
+
|
|
97
|
+
// Get spec object (first argument)
|
|
98
|
+
const args = methodInv.arguments.elements;
|
|
99
|
+
if (args.length === 0) return vd;
|
|
100
|
+
|
|
101
|
+
const specArg = args[0].element;
|
|
102
|
+
if (specArg.kind !== J.Kind.NewClass) return vd;
|
|
103
|
+
|
|
104
|
+
const specObj = specArg as J.NewClass;
|
|
105
|
+
if (!specObj.body) return vd;
|
|
106
|
+
|
|
107
|
+
// Check for mixins — skip conversion if present
|
|
108
|
+
if (this.hasMixins(specObj.body)) return vd;
|
|
109
|
+
|
|
110
|
+
// Build transformed class members
|
|
111
|
+
const memberIndent = this.getMemberIndent(specObj.body);
|
|
112
|
+
const members = await this.transformMembers(specObj.body, memberIndent, ctx);
|
|
113
|
+
|
|
114
|
+
// Construct class declaration using template
|
|
115
|
+
const classDecl = await template`class ${raw(componentName)} extends React.Component {}`.apply(vd, this.cursor);
|
|
116
|
+
if (!classDecl) return vd;
|
|
117
|
+
|
|
118
|
+
// Splice transformed members into class body
|
|
119
|
+
const cd = classDecl as J.ClassDeclaration;
|
|
120
|
+
const result = {
|
|
121
|
+
...cd,
|
|
122
|
+
body: {
|
|
123
|
+
...cd.body,
|
|
124
|
+
statements: members
|
|
125
|
+
}
|
|
126
|
+
} as any as J.ClassDeclaration;
|
|
127
|
+
|
|
128
|
+
// Track converted node ID so visitBlock can strip the trailing semicolon
|
|
129
|
+
this.convertedClassIds.add(result.id);
|
|
130
|
+
|
|
131
|
+
return result;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private isCreateClassCall(method: J.MethodInvocation): boolean {
|
|
135
|
+
if (method.name.simpleName === 'createClass' && method.select) {
|
|
136
|
+
if (method.select.element.kind === J.Kind.Identifier) {
|
|
137
|
+
return (method.select.element as J.Identifier).simpleName === 'React';
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (method.name.simpleName === 'createReactClass' && !method.select) {
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
private hasMixins(body: J.Block): boolean {
|
|
147
|
+
for (const stmt of body.statements) {
|
|
148
|
+
const s = stmt.element;
|
|
149
|
+
if (s.kind === JS.Kind.PropertyAssignment) {
|
|
150
|
+
const prop = s as JS.PropertyAssignment;
|
|
151
|
+
if (this.getPropertyKeyName(prop) === 'mixins') return true;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
private getPropertyKeyName(prop: JS.PropertyAssignment): string | undefined {
|
|
158
|
+
const keyExpr = prop.name.element;
|
|
159
|
+
if (keyExpr.kind === J.Kind.Identifier) {
|
|
160
|
+
return (keyExpr as J.Identifier).simpleName;
|
|
161
|
+
}
|
|
162
|
+
return undefined;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
private getMemberIndent(body: J.Block): any {
|
|
166
|
+
// Get the prefix of the first member to use as indentation reference
|
|
167
|
+
for (const stmt of body.statements) {
|
|
168
|
+
if (stmt.element?.prefix) return stmt.element.prefix;
|
|
169
|
+
}
|
|
170
|
+
return undefined;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
private async transformMembers(body: J.Block, memberIndent: any, ctx: ExecutionContext): Promise<any[]> {
|
|
174
|
+
const members: any[] = [];
|
|
175
|
+
|
|
176
|
+
for (const stmt of body.statements) {
|
|
177
|
+
const s = stmt.element;
|
|
178
|
+
|
|
179
|
+
if (s.kind === J.Kind.MethodDeclaration) {
|
|
180
|
+
const method = s as J.MethodDeclaration;
|
|
181
|
+
const name = method.name.simpleName;
|
|
182
|
+
|
|
183
|
+
if (name === 'getInitialState') {
|
|
184
|
+
const stateInit = await this.convertGetInitialState(method, memberIndent, ctx);
|
|
185
|
+
if (stateInit) members.push({...stmt, element: stateInit});
|
|
186
|
+
} else if (name === 'getDefaultProps') {
|
|
187
|
+
const defaultProps = await this.convertGetDefaultProps(method, memberIndent, ctx);
|
|
188
|
+
if (defaultProps) members.push({...stmt, element: defaultProps});
|
|
189
|
+
} else if (LIFECYCLE_METHODS.has(name)) {
|
|
190
|
+
members.push(stmt);
|
|
191
|
+
} else {
|
|
192
|
+
const arrowProp = await this.convertToArrowProperty(method, memberIndent, ctx);
|
|
193
|
+
if (arrowProp) {
|
|
194
|
+
members.push({...stmt, element: arrowProp});
|
|
195
|
+
} else {
|
|
196
|
+
members.push(stmt);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
} else if (s.kind === JS.Kind.PropertyAssignment) {
|
|
200
|
+
const prop = s as JS.PropertyAssignment;
|
|
201
|
+
const keyName = this.getPropertyKeyName(prop);
|
|
202
|
+
|
|
203
|
+
if (keyName === 'propTypes' || keyName === 'contextTypes' || keyName === 'childContextTypes') {
|
|
204
|
+
const staticProp = await this.convertToStaticProperty(prop, keyName, memberIndent, ctx);
|
|
205
|
+
if (staticProp) {
|
|
206
|
+
members.push({...stmt, element: staticProp});
|
|
207
|
+
} else {
|
|
208
|
+
members.push(stmt);
|
|
209
|
+
}
|
|
210
|
+
} else if (keyName === 'displayName' || keyName === 'statics' || keyName === 'mixins') {
|
|
211
|
+
// Skip displayName (inferred from class name), statics, mixins
|
|
212
|
+
} else {
|
|
213
|
+
members.push(stmt);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return members;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
private async convertGetInitialState(method: J.MethodDeclaration, indent: any, ctx: ExecutionContext): Promise<J | undefined> {
|
|
222
|
+
if (!method.body) return undefined;
|
|
223
|
+
const stmts = method.body.statements;
|
|
224
|
+
if (stmts.length !== 1) return undefined;
|
|
225
|
+
|
|
226
|
+
const firstStmt = stmts[0].element;
|
|
227
|
+
if (firstStmt.kind !== J.Kind.Return) return undefined;
|
|
228
|
+
|
|
229
|
+
const returnStmt = firstStmt as J.Return;
|
|
230
|
+
if (!returnStmt.expression) return undefined;
|
|
231
|
+
|
|
232
|
+
const result = await template`state = ${returnStmt.expression}`.apply(method, this.cursor);
|
|
233
|
+
if (result && indent) {
|
|
234
|
+
return {...(result as any), prefix: indent};
|
|
235
|
+
}
|
|
236
|
+
return result;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
private async convertGetDefaultProps(method: J.MethodDeclaration, indent: any, ctx: ExecutionContext): Promise<J | undefined> {
|
|
240
|
+
if (!method.body) return undefined;
|
|
241
|
+
const stmts = method.body.statements;
|
|
242
|
+
if (stmts.length !== 1) return undefined;
|
|
243
|
+
|
|
244
|
+
const firstStmt = stmts[0].element;
|
|
245
|
+
if (firstStmt.kind !== J.Kind.Return) return undefined;
|
|
246
|
+
|
|
247
|
+
const returnStmt = firstStmt as J.Return;
|
|
248
|
+
if (!returnStmt.expression) return undefined;
|
|
249
|
+
|
|
250
|
+
const result = await template`static defaultProps = ${returnStmt.expression}`.apply(method, this.cursor);
|
|
251
|
+
if (result && indent) {
|
|
252
|
+
return {...(result as any), prefix: indent};
|
|
253
|
+
}
|
|
254
|
+
return result;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
private async convertToArrowProperty(method: J.MethodDeclaration, indent: any, ctx: ExecutionContext): Promise<J | undefined> {
|
|
258
|
+
if (!method.body) return undefined;
|
|
259
|
+
|
|
260
|
+
const name = method.name.simpleName;
|
|
261
|
+
|
|
262
|
+
const params = method.parameters?.elements ?? [];
|
|
263
|
+
let paramsSource = '';
|
|
264
|
+
if (params.length > 0) {
|
|
265
|
+
const paramStrs = await Promise.all(params.map(async (p: any) =>
|
|
266
|
+
(await printer(this.cursor).print(p.element)).trim()
|
|
267
|
+
));
|
|
268
|
+
paramsSource = paramStrs.join(', ');
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Create stub with empty body, then splice in original body to preserve whitespace
|
|
272
|
+
const stub = await template`${raw(name)} = (${raw(paramsSource)}) => {}`.apply(method, this.cursor);
|
|
273
|
+
if (!stub) return undefined;
|
|
274
|
+
|
|
275
|
+
// Navigate into the J.Assignment -> JS.ArrowFunction -> J.Lambda to replace the empty body
|
|
276
|
+
const s = stub as any;
|
|
277
|
+
const result = {
|
|
278
|
+
...s,
|
|
279
|
+
assignment: {
|
|
280
|
+
...s.assignment,
|
|
281
|
+
element: {
|
|
282
|
+
...s.assignment.element,
|
|
283
|
+
lambda: {
|
|
284
|
+
...s.assignment.element.lambda,
|
|
285
|
+
body: method.body
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
if (result && indent) {
|
|
291
|
+
return {...(result as any), prefix: indent};
|
|
292
|
+
}
|
|
293
|
+
return result;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
private async convertToStaticProperty(prop: JS.PropertyAssignment, name: string, indent: any, ctx: ExecutionContext): Promise<J | undefined> {
|
|
299
|
+
if (!prop.initializer) return undefined;
|
|
300
|
+
|
|
301
|
+
const result = await template`static ${raw(name)} = ${prop.initializer}`.apply(prop, this.cursor);
|
|
302
|
+
if (result && indent) {
|
|
303
|
+
return {...(result as any), prefix: indent};
|
|
304
|
+
}
|
|
305
|
+
return result;
|
|
306
|
+
}
|
|
307
|
+
}();
|
|
308
|
+
}
|
|
309
|
+
}
|