typedoc icon indicating copy to clipboard operation
typedoc copied to clipboard

[RFC] Consider extracting `toString` functionality to standalone module

Open haltcase opened this issue 4 years ago • 3 comments

Problem

It's not currently possible to use typedoc's type to string rendering alone — you get the CLI, the file/filesystem handling, markdown rendering & syntax highlighting, etc. Extracting string rendering to a separate module/package would enable:

  • using typedoc in a more modular fashion, i.e. to parse and render independent of one another
  • use of parts of typedoc's capabilities in more areas, such as browser-like environments or other site generators
  • rendering of type classes as is possible now, but also of serialized objects like the ones in typedoc JSON outputs

My driving use case is to render types to documentation in Docusaurus v2, which uses React & MDX. While there is an existing plugin for outputting markdown and targeting Docusaurus v2, it doesn't allow using the full power of the React & MDX ecosystems (it just outputs static markdown).

Suggested Solution

I'd be willing to work on this if accepted. I recommend extracting the toString methods that currently live in each of typedoc's various type classes into a separate module that can then be used internally as well as exposed to allow the above mentioned features. The new API would act on objects provided as arguments which should allow it to support both the existing functionality and also operate on data such as from typedoc's serialized JSON output.

I imagine at a high level that the API could look something like this:

// specialized functions for each type
export const stringifyArray = (node: ArrayType): string => {
    // implementation
};

export const stringifyUnion = (node: UnionType): string => {
    // implementation
};

const stringifiers = {
    array: stringifyArray,
    union: stringifyUnion
  // ...
};

// generic type-to-string renderer that defers to
// the appropriate specialized functions above
export const stringifyType = (node: Type | SerializedType): string => {
    return stringifiers[node.type](node);
};

Then the existing classes could likely be refactored to use these by implementing toString in the base class instead of overriding it in every subclass:

// src/lib/models/types/abstract.ts

import { stringifyType } from '../path/to/stringify-module'

export abstract class Type {
	// ...

    /**
     * Return a string representation of this type.
     */
    toString(): string {
        return stringifyType(this);
    }
}

haltcase avatar Jan 12 '20 05:01 haltcase

Thanks for taking the time to write this out for discussion.

I think factoring out the conversion from a TypeScript project to models into a new module that TypeDoc consumes would be beneficial in forcing us to design an API that is actually usable for consumers. #439 was an attempt at this several years ago, but ended up just getting neglected since there wasn't enough time to get it reviewed + merged.

The toString functionality is currently somewhat of an afterthought. When outputting a type it isn't very useful for generating HTML or JSON to just output the type as a string. For HTML, the parts of the type that refer to other reflections should be hyperlinked, and for JSON it should be broken into parts so that the consumer can do the same when consuming the JSON...

Right now we offer very little support for consumers of the JSON format. There are several interfaces that describe the resulting type, but no easy way to consume it. This would be a step in the right direction, and the described solution makes sense to me.


(not to put a damper on this effort but...)

If you want toString functionality, why consume the JSON in the first place? It is fairly straightforward to get a ProjectReflection describing the converted project, which provides the toString functionality in addition to several other helper methods. Is there some reason to use the JSON instead of this?

Gerrit0 avatar Jan 13 '20 03:01 Gerrit0

@Gerrit0 For my Docusaurus use case, I wasn't able to figure out a way to use the API where it could process the files (server side) and also pass that data to the renderer (client side). The client is also unable to use things like Node's fs module.

haltcase avatar Jan 13 '20 03:01 haltcase

Ah, that makes sense. :+1:

Gerrit0 avatar Jan 13 '20 03:01 Gerrit0

Eventually I want to make it possible to use TypeDoc from a browser, that's so far away, and so low down on the priority, list, however, that it's likely not going to happen...

Gerrit0 avatar Sep 04 '23 18:09 Gerrit0