Skip to content

Commit

Permalink
fix(undecorate): add support for default export classes (#3164)
Browse files Browse the repository at this point in the history
  • Loading branch information
Grsmto authored Nov 1, 2021
1 parent 17997c2 commit be5684b
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 17 deletions.
5 changes: 5 additions & 0 deletions .changeset/dirty-ears-push.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"mobx-undecorate": minor
---

add support for default export classes + fix eslint-plugin-react compatibility
36 changes: 36 additions & 0 deletions packages/mobx-undecorate/__tests__/undecorate.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)))"
`)
})
90 changes: 73 additions & 17 deletions packages/mobx-undecorate/src/undecorate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import {
Node,
ClassDeclaration,
ClassMethod,
ObjectExpression
ObjectExpression,
Identifier
} from "jscodeshift"

interface MobxUndecorateOptions {
Expand Down Expand Up @@ -266,6 +267,7 @@ export default function transform(
function handleObserverAndInject(clazzPath: ASTPath<ClassDeclaration>) {
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) &&
Expand All @@ -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(
Expand Down

0 comments on commit be5684b

Please sign in to comment.