Design System and its code source migration: a brief logbook
The Confetti itself is an open-source product developed by Labcodes, and it has a test suite and its repository can be found at: https://github.com/labcodes/confetti-ds. And its documentation is accessible at: https://confetti.labcodes.com.br/.
Another important point is that this migration was already planned and some events have helped to advance this process and as good practices have helped to guide this development process.
While we were publishing the
0.1.0-alpha.11 version, the team has started to test the package into the RunKit (the Npm's integrated terminal), and we noticed that a project dependency
@babel/runtime was not found. Indeed, it was known by the time that Babel (ES6 transpiler to Common JS) was causing some memory leakages during the build process.
One of the probable causes of these problems was the direct call to the Babel CLI plus the packaging and dependency mishaps; the direct consequence was the larger size of the final bundle.
After attempting to remove the package and review the build process, the team noticed that it was the ideal timing to update the build process using tsup to prepare the ground when the code source migrated to Typescript.
As each component has its own file, it allowed the team to better control the functioning of each one and gradually test each change. The high test coverage contributed positively to the migration, since the project had good coverage and allowed each refactoring step to be verified.
How was the process?
Adjusting the build
We used tsup as a build tool with TS, which in turn uses ES Build under the hood. However, it allows the user to custom and control the steps and parameters of code formatting. The setup itself was written inside a new file named
tsup.config.ts, where we could select and filter the correct files for the build process (tsup can bundle any project with Node in its stack).
The result is a faster build process, as they describe in their official docs:
tsup automatically excludes packages specified in the dependencies and peerDependencies fields in the packages.json, but if it somehow doesn't exclude some packages, this library also has a special executable tsup-node that automatically skips bundling any Node.js package.
When we were testing the new build process, we noticed that the product has running smoothly and there were no major breaking changes, therefore the design system was stable.
After that overall analysis and the team's availability to perform a full source code migration plus the reasonably low cost of time, we certified that it was the perfect timing to start the process of migrating the components from JS to TS.
Getting down to the real deal
First, we started altering the file extensions from
.tsx. The Jest's VS Code extension started flagging errors in the test files - which was somehwat expected - and we also began to replace the file extensions to
.tsx. Some error messages originated from a more tsup strict nature.
We therefore resumed the migration so that we could move forward with the refactorings without so many warnings. To do this, we removed ESLint (because tsup was already performing this task) and at the same time we disabled tsconfig's
We also needed to export the component types via a type declaration file
types.d.ts. During compilation, the types are extracted and exported, making them available to the API of the developer who wants to use the design system library.
Each component had its interface created: in the case of the base components, we created the base interface to be used in the child components - we'll come back to this part later because we had to make some changes at this point too. During this process of declaring and structuring the variables, the references to PropTypes were also removed.
Points of interest (& some tips!)
Throughout the process we noticed that some tests and work tools helped us to locate some issues along the way. It is important to emphasize the developer's acquaintance level with the project might interfere more than seniority, because those tools and test files can help and engineer to model the
Here are some tips from the migration process's notes:
1. Pay close attention to the optional props
Several interfaces had optional props that were marked as mandatory by accident when we migrated from
PropTypes to types, and this caused several errors. Taking this into account is essential to validate the migration.
2. The preferred usage of
We prefer to use interfaces rather than types because they allow the user to customize components more safely, are easier to extend and are more rigorous in checking types.
3. Pay close attention when declaring types in components
As components are functions, the difference between the type of the arguments and the return type is a simple
:. At the beginning of the process, it was common to put the types of the props as the return of the components.
4. Focus on the tests and spend some time setting up those to run automatically
As we started using Turborepo in the project, the VSCode Jest extension didn't run the tests because it couldn't find the correct project path.
5. Using the correct extensions for your IDE can help you save much time
For VS Code, these were the ones that had helped us most:
Error Lens is a native Microsoft's extension that helps to check the syntax errors. Total Typescript was developed by Matt Pollock - he has some great courses about Typescript (the link is avaliable at References section). Another native extension that had helped us a lot was Jest. It provied us a automated JS's test suite with a complete interface that had allowed us to perform a double-check to validate if the props were being rendered and being used as expected.
6. Take a closer look at the tests and docs
It is worth analyzing if there are still mentions of JS Docs declarations (if you use them), checking if there are components without mentions of props in the documentation; in addition, analyze if `type-checking` is impacting the tests.
7. Export all the components to a single index file
Having just one output file helps when generating an
index.d.ts file and when importing the components into the project that will use the library. This allows another user to extend the types more easily.
8. For a component with multiple variants, create the base types and extend them; however, but only export the types for each variant
This topic is quite self-describing, as we can see in the following snippet: the base properties are not declared inside the abstract component (aka the base component); these are declared inside at a more generic interface.
However, the size of the package and the code itself have not canged significantly.
npm notice package size: 64.9 kB
npm notice unpacked size: 341.7 kB
npm notice package size: 78.5 kB (31kb from types, 47kb of code)
npm notice unpacked size: 365.5 kB
The migration itself, despite its mishaps, was greatly helped by the wide coverage of the tests; therefore, code following good TDD practices can help to better understand certain stages of the change process.
In addition, the initial process was carried out with
strict: False, i.e. we needed to set all static type validations to true. There are still parts of the code with an implicitly inferred `any` type, for example.
1. Always start tests from the inside out
Even if the tests have some implementation problems, they are the source of truth for your application and will immediately show up any serious problems during migration.
2. Have tests in your project, please 🙏
Projects with high test coverage are essential in DX to guarantee improvements and evolutions in the code, especially when it comes to such a large refactoring.
3. If in doubt, try to migrate to Typescript ASAP
The migration itself wasn't that complicated, but the effort would have increased greatly if the library had been larger, so this is the kind of decision that should be made as early as possible.
4. Remember to test in a real scenario (our case was an internal project)
The projects that already used the Confetti's design system did not need a lot of refactoring to update the code to receive the project updates.
I would like to thank my mentor Luciano Ratamero and one of the creators of the Confetti who agreed to help and without whom this migration would not have been possible and who co-authored this text.