OpenZeppelin / openzeppelin-sdk

OpenZeppelin SDK repository for CLI and upgrades.js. No longer actively developed.
MIT License
431 stars 200 forks source link

Lay out a general migration plan to typescript #434

Closed spalladino closed 5 years ago

spalladino commented 5 years ago

Consider the following tasks:

eternauta1337 commented 5 years ago

Typescript migration plan

This document outlines my proposed general plan for migrating ZeppelinOS to Typescript. The steps don't need to be followed to the letter, but they lay out a tempting path to use as a backbone for the migration.

This plan focuses on the complete port of the lib and cli packages. The docs and vouching packages are left out of the migration for now. The test folders within each package are left out, but some tests may be ported for future reference.

This comment lays out the plan for porting the lib package. A future comment will lay out the plan for migrating cli, using this plan as a reference.

Useful guidelines

File port order

Files that have less dependencies should be ported first. This defines a porting tree that is traversed from leaf to branch, to the main trunk of the tree. To help identify and visualize the dependency tree Webstorm's UML diagrams feature can be used, as well as the madge NPM package, which can be used as follows:

madge packages/lib/src --exclude ../lib --summary

Having said that, the priority is to port public facing elements first. I.e. public interfaces are more important than encapsulated private members. This will allow us to work on external type declarations as we port the packages.

Pull requests

In order to keep PRs in a size that is reviewable, files should be ported in batches of 3-4, depending on complexity and submitted for review via a pull request. Before pushing your changes, make sure tests in lib and cli pass locally, and then make sure that the CI tests pass after pushing the changes.

TS-TODOs

As we port the code, we may realize things that need to be done but can't be done at the moment, or fall outside of the scope of the current tast. In these cases, add comments with the following marker:

// TS-TODO: Your comment here.

Linter settings

For now, we are using TSLint's "recommended settings". If you see something that bothers you, go to root/tslint.json and edit the file. If someone has already created a setting and you are about to change it, consider leaving a note for discussion in slack. We will revisit the linter settings in phase 5. If you definitely want to change the setting now, you can simply adjust it, then run:

npm run lint to verify the files that become affected by the rule change, and npm run lintfix to implement the new rule in the code.

Should I create an interface, a class or just a typed object?

For now, just type as you see it naturally and don't worry too much about conventions. We will revisit architecture design decisions in phase 6. The idea in the first stages is to move away from Js in any reasonable way. Once we hace Ts syntax, typed dependencies, strict compiler settings, we will have much more info to make architectural design decisions. For now, don't be afraid to use any types, but if you see a structure, either local or from a dependency, that could have a complex type, just leave a TS-TODO comment.

Automation

There don't seem to be good tools around for automating at least part of the process of converting a Javascript file to Typescript. However, simply renaming the file the right way, and using TSLint to make some style changes on the file, before actually starting to type it is very helpful. This PR contains a script to save time with this, which needs to be run on single files, without extension:

./scripts/ts_prepare.sh src/utils/ABIs

The script will list the changes that will be made to the file and prompt the user for confirmation. After the changes are applied, you can jump into the file and manually add types without wasting time with style related stuff.

Phases

The migration is divided into the following set of phases. Tasks within a phase can be parallelized, but a new phase should not be started until the previous one is completed.

Lib/Cli Phase 0: Bootstrapping

This phase involves bootstrapping Typescript into the project, so that the migration process can begin.

Porting of the lib package

Lib Phase 1: Syntax port of lib

This phases involves porting all .js files to .ts files contained within the src folder of packages/lib, following the suggested file port order from the guidelines above. Note that in this phase we are not paying particular attention to OOP design patterns, but merely porting the Javascript syntax to Typescript syntax. This means that the port involves applying types as much as possible, but not worrying about perfect typing of all local objects, or using perfect typings for 3rd party dependencies, which will be addressed in following phases.

At the end of this phase, there should be no .js files in packages/lib/src. The following task list shows all files, ordered by number of dependencies.

Lib Phase 2: Export declaration files for lib

As soon as packages/lib fully uses types, we should generate declaration .d.ts files so that the package can be used by packages/cli with types.

Lib Phase 3: Partial syntax port of test folders

After Truffle has been removed from the testing pipeline, the same process applied to the src folders in phase 1 can be applied to the test folders. However, we only need some of the tests to be ported in the lib package, so that we can use it for future reference in porting the rest of the tests.

Lib Phase 4: Typed dependencies

This phase involves introducing typings to all the 3rd party dependencies used in the project. More precisely, this involves using Typescript 2.x's @types dependencies so that the compiler understands the types used by the dependencies, instead of it inferring any all over the place. This also involves adapting our code to use these types.

Important: We should also consider replacing existing dependencies with pure Typescript packages, especially for web3 related stuff.

Lib Phase 5: Strict compiler and linter settings

This phase involves enabling stricter Typescript compiler settings and making all the necessary changes in the Typescript files. The documentation for all compiler settings can be found at http://www.typescriptlang.org/docs/handbook/compiler-options.html. The settings should be set in the following order:

Compiler settings

Linter settings

Lib Phase 6: Architecture design

In this phase, we can begin focusing on refactors that make the code more OOP designed. This means not just complying to Typescript syntax, but actually architecturing the code around Classes, Interfases, Generics, etc. That is, making the most out of the OOP design patterns that Typescript enables.

*TBD: Define task list of refactors that enforce a more OOP-like architecture. Drawing an in depth UML diagram would help in identifying OOP structures that could be applied.

Lib Phase 7: Cleanup

spalladino commented 5 years ago

@ajsantander looks great! A few comments:

eternauta1337 commented 5 years ago

@spalladino I've updated my previous comment (the plan) with your suggestions. Thank you =D

eternauta1337 commented 5 years ago

Porting of the cli package

This is the continuation of the migration plan, now focused on the cli package. Please use the migration plan for lib as reference.

Cli Phase 0: Bootstrapping

Cli Phase 1: Syntax port of cli.

Idem Phase 1 for lib, but for packages/cli.

Cli Phase 2: Export declaration files for cli

Idem phase 2 for lib, but for the cli package.

Cli Phase 3: Partial syntax port of test folders

Cli Phase 4: Typed dependencies

Cli Phase 5: Strict compiler and linter settings

This phase involves enabling stricter Typescript compiler settings and making all the necessary changes in the Typescript files. The documentation for all compiler settings can be found at http://www.typescriptlang.org/docs/handbook/compiler-options.html. The settings should be set in the following order:

Compiler settings

Cli Phase 6: Architecture design

In this phase, we can begin focusing on refactors that make the code more OOP designed. This means not just complying to Typescript syntax, but actually architecturing the code around Classes, Interfases, Generics, etc. That is, making the most out of the OOP design patterns that Typescript enables.

*TBD: Define task list of refactors that enforce a more OOP-like architecture. Drawing an in depth UML diagram would help in identifying OOP structures that could be applied.

Cli Phase 7: Cleanup

Further Phases

At this point, most of Lib and Cli should be ported to Typescript. Next steps, which could be turned into independent issues, could be:

facuspagnuolo commented 5 years ago

Closing this issue, the migration plan was approved, @ajsantander please create small issues splitting the plan above so we can keep track of each step. Great job!