Skip to content

coderaiser/asyncify

ย 
ย 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

74 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

@codemodsquad/asyncify

CircleCI Coverage Status semantic-release Commitizen friendly npm version

Transforms promise chains into async/await. I wrote this to refactor the 5000+ .then/.catch/.finally calls in the sequelize codebase. This is slightly inspired by async-await-codemod, but written from scratch to guarantee the same behavior and tidy code as best as I can manage.

Usage

npx @codemodsquad/asyncify path/to/your/project/**/*.js

This command just forwards to jscodeshift, you can pass other jscodeshift CLI options.

Support table

asyncify
Converts .then โœ…
Converts .catch โœ…
Converts .finally โœ…
Renames identifiers in handlers that would conflict โœ…
Converts promise chains that aren't returned/awaited into IIAAFs โœ…
Converts return Promise.resolve()/return Promise.reject() โœ…
Removes unnecessary Promise.resolve() wrappers โœ…
Warns when the original function could return/throw a non-promise Planned
Refactoring/inlining handlers that contain conditional returns
All but one if/else/switch branch return โœ…
All branches return, even nested ones โœ…
All but one nested if/else/switch branch return ๐Ÿšซ
More than one if/else/switch branch doesn't return ๐Ÿšซ
Return inside loop ๐Ÿšซ

Warnings

Comments can sometimes get deleted due to an impedance mismatch between @babel and recast ASTs. If you use the --commentWorkarounds=true option it will try to prevent more comments from getting deleted but it sometimes causes an assertion to fail in recast.

There are a few edge cases where asyncify produces funky output. It's intended to not break any existing behavior (I know of no cases where it does, and I have fixed several such issues) but sometimes the output will be be semantically wrong even if it behaves correctly. For example, I've seen a case where doing an async operation several times in a row:

it('test', () => {
  const doSomething = () => {
    // ...
  }

  return doSomething()
    .then(doSomething)
    .then(doSomething)
}

Gets converted to:

it('test', async () => {
  const doSomething = () => {
    // ...
  }
  await doSomething(await doSomething(await doSomething()))
})

This works even though it initially seems like it wouldn't and is obviously not what you want:

it('test', async () => {
  const doSomething = () => {
    // ...
  }
  await doSomething()
  await doSomething()
  await doSomething()
})

Although I could possibly fix this for cases where it's easy to determine that the function has no parameters, there could be cases where it's impossible to determine whether the identifier doSomething is even a function or whether it has parameters.

About

Don't keep your promises ๐Ÿ˜‰

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • TypeScript 97.2%
  • JavaScript 2.8%