From be5684b2421ab7c72bb9a2bdc2f40761e9c53c41 Mon Sep 17 00:00:00 2001 From: Adrien Denat Date: Mon, 1 Nov 2021 10:37:24 +0000 Subject: [PATCH] fix(undecorate): add support for default export classes (#3164) --- .changeset/dirty-ears-push.md | 5 ++ .../__tests__/undecorate.spec.ts | 36 ++++++++ packages/mobx-undecorate/src/undecorate.ts | 90 +++++++++++++++---- 3 files changed, 114 insertions(+), 17 deletions(-) create mode 100644 .changeset/dirty-ears-push.md diff --git a/.changeset/dirty-ears-push.md b/.changeset/dirty-ears-push.md new file mode 100644 index 000000000..d80a8e46f --- /dev/null +++ b/.changeset/dirty-ears-push.md @@ -0,0 +1,5 @@ +--- +"mobx-undecorate": minor +--- + +add support for default export classes + fix eslint-plugin-react compatibility diff --git a/packages/mobx-undecorate/__tests__/undecorate.spec.ts b/packages/mobx-undecorate/__tests__/undecorate.spec.ts index b7854d51d..76c4be717 100644 --- a/packages/mobx-undecorate/__tests__/undecorate.spec.ts +++ b/packages/mobx-undecorate/__tests__/undecorate.spec.ts @@ -1342,3 +1342,39 @@ test("makeObservable not added to imports #2540", () => { }" `) }) + +test("class default export comp with observer and inject", () => { + expect( + convert(` + import {observer, inject} from 'mobx-react' + + @inject("test") @observer export default class X extends React.Component {} + `) + ).toMatchInlineSnapshot(` + "import {observer, inject} from 'mobx-react' + + class X extends React.Component {} + export default inject(\\"test\\")(observer(X));" + `) +}) + +test("class default export comp with observer and inject", () => { + expect( + convert(` + import {observer, inject} from 'mobx-react' + import {withRouter} from 'react-router-dom' + + @inject("test") @observer class X extends React.Component {} + + export default withRouter(X) + + `) + ).toMatchInlineSnapshot(` + "import {observer, inject} from 'mobx-react' + import {withRouter} from 'react-router-dom' + + class X extends React.Component {} + + export default withRouter(inject(\\"test\\")(observer(X)))" + `) +}) diff --git a/packages/mobx-undecorate/src/undecorate.ts b/packages/mobx-undecorate/src/undecorate.ts index aba3bceb8..793427e32 100644 --- a/packages/mobx-undecorate/src/undecorate.ts +++ b/packages/mobx-undecorate/src/undecorate.ts @@ -7,7 +7,8 @@ import { Node, ClassDeclaration, ClassMethod, - ObjectExpression + ObjectExpression, + Identifier } from "jscodeshift" interface MobxUndecorateOptions { @@ -266,6 +267,7 @@ export default function transform( function handleObserverAndInject(clazzPath: ASTPath) { const clazz = clazzPath.value const decorators = (clazz as any).decorators ?? [] + const defaultExportPath = source.find(j.ExportDefaultDeclaration).paths()[0] const isObserver = dec => j.Decorator.check(dec) && @@ -281,22 +283,76 @@ export default function transform( const hasObserverOrInject = decorators.some(dec => isObserver(dec) || isInject(dec)) if (!hasObserverOrInject) return - // re-create the class - let newClassDefExpr: any = j.classExpression(clazz.id, clazz.body, clazz.superClass) - newClassDefExpr.superTypeParameters = clazz.superTypeParameters - newClassDefExpr.typeParameters = clazz.typeParameters - newClassDefExpr.implements = clazz.implements - // wrap with decorators - newClassDefExpr = decorators.reduceRight((newClassDefExpr, dec) => { - return j.callExpression(dec.expression, [newClassDefExpr]) - }, newClassDefExpr) - - changed = true - const decl = j.variableDeclaration("const", [ - j.variableDeclarator(j.identifier(clazz.id!.name), newClassDefExpr) - ]) - decl.comments = clazz.comments - clazzPath.replace(decl) + // If module uses default export + if (defaultExportPath && clazz.id) { + // If class is exported directly on the class declaration (`export default class ...`) + if (j.ClassDeclaration.check(defaultExportPath.node.declaration)) { + let newDefaultExportDefExpr = j.exportDefaultSpecifier(clazz.id) + + newDefaultExportDefExpr.exported = decorators.reduceRight( + (newDefaultExportId, dec) => { + return j.callExpression(dec.expression, [newDefaultExportId]) + }, + newDefaultExportDefExpr.exported + ) + + const exportDecl = j.exportDefaultDeclaration(newDefaultExportDefExpr.exported) + + // re-create the class + let newClassDefExpr = j.classExpression(clazz.id, clazz.body, clazz.superClass) + newClassDefExpr.superTypeParameters = clazz.superTypeParameters + newClassDefExpr.typeParameters = clazz.typeParameters + newClassDefExpr.implements = clazz.implements + + const newClassDefDecl = j.classDeclaration( + newClassDefExpr.id, + newClassDefExpr.body, + newClassDefExpr.superClass + ) + + // Insert module default export after class declaration + defaultExportPath.insertAfter(exportDecl) + // Replace old class with new class + defaultExportPath.replace(newClassDefDecl) + + changed = true + } else { + const newDefaultExportDefExpr = j.exportDefaultSpecifier(clazz.id!) + const decorators = (clazz as any).decorators ?? [] + const newClassExport = decorators.reduceRight((newDefaultExportId, dec) => { + return j.callExpression(dec.expression, [newDefaultExportId]) + }, newDefaultExportDefExpr.exported) + + source + .find(j.ExportDefaultDeclaration) + .find(j.Identifier, (value: Identifier) => value.name === clazz.id!.name) + .replaceWith(newClassExport) + + const newDecorators = decorators.some(dec => !isObserver(dec) && !isInject(dec)) + + ;(clazz as any).decorators = newDecorators + + changed = true + } + } else { + // re-create the class + let newClassDefExpr: any = j.classExpression(clazz.id, clazz.body, clazz.superClass) + newClassDefExpr.superTypeParameters = clazz.superTypeParameters + newClassDefExpr.typeParameters = clazz.typeParameters + newClassDefExpr.implements = clazz.implements + // wrap with decorators + newClassDefExpr = decorators.reduceRight((newClassDefExpr, dec) => { + return j.callExpression(dec.expression, [newClassDefExpr]) + }, newClassDefExpr) + + const decl = j.variableDeclaration("const", [ + j.variableDeclarator(j.identifier(clazz.id!.name), newClassDefExpr) + ]) + decl.comments = clazz.comments + clazzPath.replace(decl) + + changed = true + } } function handleProperty(