Why are JS sourcemaps typically at token granularity?

It is possible to use AST granularity, but usually to build an AST, you need before to tokenize the code anyway. For debugging purpose AST is an unneccessary step as the syntax analyzer must be fed with tokenized data, in order to work.

An interesting resource on topic

I suggest also to explore acornJS sourcecode and take a look how it produces AST


The TypeScript compiler actually only emits sourcemap locations on AST node bounds, with some exceptions to improve compatibility with certain tools that expect mappings for certain positions, so token-based maps actually aren't quite universal. In the example you give, TS's sourcemaps are for positions like so:

const a = function *() { return a + ++ b } /*
^     ^^  ^              ^      ^^  ^  ^^^
*/

Which are generally both the start and end of each Identifier AST node (plus starts otherwise).

The rationale for mapping both start and end positions for an Identifier AST node is pretty simple - when you rename an Identifier, you want a selection range on that renamed identifier to be able to map back to the original identifier, without necessarily relying on heuristics.