Thursday, February 22, 2024

4.0.0 - Spring Cleaning

What's Changed

This release is aimed at re-evaluating some of the core assumptions for the base of the framework.

The primary modules impacted in this endeavor are, Manifest, Compiler, Base, Pack, and Terminal. In this new world, Base and Terminal have had their scope's reduced, and some core functionality either removed, or migrated to a location that is closer to its primary use case.

NodeJS Alignment

In addition to shuffling code, there was a big push to lean into native NodeJS functionality when applicable. Some of the native functionality was 1-1 (or nearly so), while other areas required rethinking and reworking. As NodeJS has matured, there is more and more of an opportunity to shift ownership of some common patterns into core NodeJS libraries.

Terminal

In theory the role terminal had been playing was aimed at increasing the usability of the command line interface. The implementation relied on some of complexity of terminals that had issues in a cross-platform environment in addition to being really easy to get to an inconsistent state with SIGKILL occurring in the middle of an execution.

To that end terminal has been removed as a core module, but moved to a more specialized module, that has had a dramatic reduction in scope. This reduction comes in the form of only a few methods being available for rendering (and now constrained and better verified), along with outsourcing all color support to chalkjs. The code is smaller, more focused, more reliable, and now ultimately providing a superior user experience.

Manifest

The manifest structure has been overhauled to provide a more logical grouping of common values, and providing increased clarity of which fields belong together. This clarity, also coupled with some stronger assumptions about how the compiler should be working, allowed for a vast reduction in the number of times we need to produce a novel manifest context, which is the core unit of truth for every compilation/execution.

Beyond the context cleanup, internal work was done on module visiting/painting for determining runtime roles, and prod designations for all modules. This once nuanced and delicate piece of code is now far simplified and far more robust. The goal here is simplification of edge cases, and erring on the side of clarity over flexibility.

Compiler

The compiler has received an incredible amount of tlc in this release. Compiler client/server logic has been completely rewritten to centralize ownership to the compiler and to minimize the amount of information needed to integrate at runtime. Additionally types are now being shared appropriately which helps to mitigate the gap between client/server entirely.

The server has also been updated to control the logic around stop/restart/terminate/kill signals to have a repeatable and reliable method for handling shutdown cleanly and being able to recover cleanly when a process is killed immediately. The net result here is a more reliable process that is more resilient against unexpected behaviors.

File Watching

File watching, once again has been updated. Many odd bugs (some were logical, and some were tied to file write behavior of VS Code and how that translates into @parcel/watcher's event stream) have been fixed. Additionally in lieu of watching at each folder to mitigate the number of watchers (but also increased the watch complexity), all watching has been reduced to a single watcher at the repo root. This may have some impact for linux with the inotify limitations, a new field has been added to the `travetto.build` field of the package.json to allow for setting exclusions for watching.

The net-result here is a far more reliable watch experience, and the errant hangs on buffered file writes or git checkouts should be a thing of the past.

VSCode / Tooling Overhaul

In the course of stabilizing and ensuring consistency/repeatability/reliability of all the command line tools and the VS Code functionality, some of the patterns were entirely re-written. There was a non-trivial amount of logic geared towards restarting, checking for healthiness of the underlying tools, capturing odd error states, and the like.

This has all been rewritten to the point of pushing it all of the complexity into the command line tools. The net result here is that every tool invocation within VSCode is far simpler, and no longer has the possibility of runaway process spawning in edge cases. This also provides the benefit that other uses of the tools will now have access to a more robust functional interface allowing all callers to be simpler.

Base

This was a labor of love as base had grown into a hodgepodge of shared utilities that was a little too convenient. This is to say that more focused was needed here along with expectation of logic living where it is being used.

Process execution, environment variable interaction, time utilities, data utilities, stream interactions, shutdown behavior, and startup behavior were all on the chopping block. As stated in the first section, the primary goal here was alignment with standard NodeJS behavior along with migrating code to where it needed to be. The result was a significant change to the core functionality that represented simplicity and clarity, but is definitely not backwards compatible.

Out of all the changes, the Base module rewrite was the primary motivator for the major version release as this impacts far more than just the framework itself.

Pack

Last but not least, Pack was revisited with a unification between the CommonJS and ESM output formats, to allow for a consistent compilation experience, and help to resolve some unexpected bugs that rose during compilation in ESM. Everything is now consistent but this did introduce a behavior change in CommonJS around optional dependencies.

To that end, a new field in the package.json is exposed for defining ignorable modules during pack. This will help to minimize the need of creating a custom rollup entrypoint.

Whats Next?

The ecosystem is still waiting on ES Decorators being fully ratified (including parameter decorators), including support for standalone functions. Once all the necessary features are available within Typescript, the framework will overhaul decorator behavior, and allow for isolated functions where it make sense (e.g. DI factory functions, standalone rest routes, caching, etc).

No comments:

Post a Comment