airbnb / ts-migrate

A tool to help migrate JavaScript code quickly and conveniently to TypeScript
MIT License
5.42k stars 218 forks source link

Expose as a NodeJS API #13

Open sumwatshade opened 4 years ago

sumwatshade commented 4 years ago

Hey team! Wanted to propose some stuff I would be willing to contribute, and thus wanted to see what your perspective was on it:

Proposal

Expose ts-migrate as a NodeJS API, such that teams could do something like:

const MigrationCLI = require('ts-migrate')

(async () => {
 const runner = new MigrationCLI();

 const FOLDER = process.cwd();
 const options = {
    // Configuration options go here
 }

 await runner.init(FOLDER, options)
 await runner.migrate(FOLDER, options)
})()

The async nature of this is similar to how ESLint handles their file manipulation. I think would open up the door for having parallel or threaded processes down the road in this case.

Motivation

I could see the case that many teams have specific project structure that may require additional setup. Having a code-mod that performs their setup, but also can migrate from JS to TS in the same tool, would be massively beneficial, and allows teams to become more opinionated on their own migration tool.

Thanks for your work as a team! Happy to get in contact for how I can help with design or contribution on this front 😄

NickHeiner commented 3 years ago

I strongly agree. I work in a codebase with 15k files and at that scale, one often encounters edge cases that are tough to handle with a CLI-only interface. This would be great!

sfrieson commented 3 years ago

I've started some work on something like this. My goal is to be able to supply custom plugins to the migrate command so it is a little more related to #41. If no work is being done on this, I'd definitely be willing to contribute.

Specific to the migrate command's API, I was thinking about giving the option to add custom plugins into the config as well as breaking up the current set of plugins into two sets: modifications and cleanup. Having these two sets would allow someone to insert their custom plugins before or after the core ts-migration modifications but before the cleanup so that someone wouldn't have to run reignore again after their custom modifications.

import migrate, { modificationSet, cleanupSet} from 'ts-migrate/commands/migrate';

(async () => {
  const directory = '...';
  const options = { ... };
  await  migrate(directory, options, (config) => {
    config.addPlugin(myPreRunPlugin, { foo: 'bar' })
      .addModificationPlugins()
      .addPlugin(myPreCleanupPlugin, { foo: 'bar' })
      .addCleanupPlugins()
      .addPlugin(myPostRunPlugin, { foo: 'bar' });
  }));
})();

Not supplying the config hook as the third argument would run the command as normal. Running with the config hook would allow you to order the plugins as you supply them, but then it is your responsibility to add the supplied plugins back into the config.

The .addModificationPlugins() would be a convenience to the custom plugin author to add the main plugins (stripTSIgnorePlugin, hoistClassStaticsPlugin, reactPropsPlugin, etc) in one command. It also allows them to get any updates to that list automatically.

The .addCleanupPlugins() would be a convenience to add the final eslintFixPlugin,tsIgnorePlugin,eslintFixPlugin sequence.

Feedback on approach, future compatibility, and naming are all appreciated.