stack-tools-js / stack-tools

Utilites for printing and parsing errors and their stacks
5 stars 1 forks source link

Feature: chalk package #7

Open conartist6 opened 2 years ago

conartist6 commented 2 years ago

I'd like to have a @stack-tools/chalk-tools package that offers users the ability for format error syntax with chalk.

This is for the most part straightforward thanks to the most recent work I've done (monorepo and visitor pattern). The goal is to provide syntax hightlighting for terminal environments with chalk, and also to put the AST and visitor systems through their first real test.

conartist6 commented 2 years ago

The first question is, at what level should this package operate?

My instinct is to try to make a single package that is agnostic to the precise text formatting of the error, i.e. the chalk package would work equally well with errors parsed from v8 syntax as it would with errors parsed from SpiderMonkey syntax. Otherwise I'll start to get a proliferation of packages like chalk-tools-v8 and chalk-tools-spidermonkey and maybe eventually chalk-tools-node-spidermonkey and this would just get to be absurd.

So how should I make the code reusable?

My first idea was to make chalk could operate on ASTs, e.g. highlight(ast) => ast, but it doesn't take much thinking to see how that approach is awful. Worst it forces syntax highlighting to take place only at the level of literals, which means there's no way to highlight an entire higher order construct like Site, which is our most common use case.

I think the I need to take another page out of babel's book and create APIs that permit plugins, where a plugin defines a set (or subset) of visitors. Babel has definitely seen their plugin system stretch to the breaking point and are currently working on explicit dependency management for plugins, but they're not done yet (that I know of) and their current system has gotten them very far indeed so I'm inclined to treat it as sound.

conartist6 commented 2 years ago

On second though though maybe Babel isn't as much of an analog as I'm imagining. It's plugin system definitely is ast->ast, so their printing code is never mixed with anything else. Essentially they do a complete pass of the AST for plugins to do transformations, then do a single complete pass to print. I'm talking about doing a pass that mixes different kinds of printing visitors. I think I just need to start building and see what happens.

conartist6 commented 2 years ago

I'm going to need some way of doing the chaining, e.g. (node, next) => chalk.red`${next(node)}`. This would permit the chalk code to function purely as a decorator without knowing exactly what it is decorating.

conartist6 commented 2 years ago

A better name for next might be print as we need to enforce the basic contract that the thing going in is a node and the thing coming out is a string.

conartist6 commented 2 years ago

The rock bottom simplest solution I could provide is to expand the definition of a visitor from (node, visit) => {} to (node, visit, context) => {}. This would have an impact in a few places where I manually forward the call, as I do for the header visitor, but it shouldn't be a big deal as this is a pretty standard set of arguments and shouldn't need to grow further. Then I could add context.defaultVisitors and it would then be possible to do context.defaultVisitors.overriddenVisitor(node, visit, context), though it would obviously be desirable to create some sugar for that expression.

conartist6 commented 2 years ago

I like that this solution gives you the complete set of visitors you are overriding, which could be useful when extending from visitors that are already themselves extended, something which I haven't created an API for yet. I think the class paradigm is pretty natural to describe this as it offers extension, super calling, and stored state. If I were to use the class paradigm then actually chalk would be a visitor mixin, which sounds great to me.

conartist6 commented 2 years ago

OK so I have this built and can see it working -- a chalk visitor expressed in a way that is fundamentally agnostic to the underlying syntax. But after all that it's not clear to me if I want a chalk package. Maybe just having a way to build this easily is enough? It's essentially just a styling system, but is applying some default styles worth a whole package? And especially a package that seems like it's going to have a pretty ugly API? And chalk itself is a bit finnicky when it comes to exactly how you express certain things, so extending from a chalk printing just doesn't seem like a better idea than making your own.

conartist6 commented 2 years ago

You can see what I have working in this comment.