Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: JSON doc generation #533

Merged
merged 27 commits into from
Jan 18, 2022
Merged

feat!: JSON doc generation #533

merged 27 commits into from
Jan 18, 2022

Conversation

Chriscbr
Copy link
Contributor

@Chriscbr Chriscbr commented Dec 24, 2021

Supersedes #481
Part of cdklabs/construct-hub-webapp#714 and cdklabs/construct-hub#142

Allows rendering language specific documentation to a JSON object in addition to a markdown string. This allows the consumer more flexibility to render the structure of the API reference as needed without regenerating markdown documents.

This PR builds upon #481 by cleaning up the JSON schema (in schema.ts) and also changing the markdown rendering logic to be based on the JSON output -- so the markdown rendering does not depend on any TranspiledXxx classes or interfaces.

All headers now have anchor tags which default to the unique language-agnostic FQNs (e.g. @aws-cdk/aws-s3.Bucket.parameter.scope). Based on my research, all characters used in JSII FQNs (A-Za-z0-9_-/.@) are allowed as URL fragments, and are also allowed as id's in HTML5, so this is a reasonable default.

This PR also extends the notion of fully qualified names to all "entities" in the document (methods, properties, enum members, and even method parameters) denoted by the "id" fields within the JSON schema, so that any section of a document can be uniquely linked - not just types. The JsiiEntity interface in schema.ts is intended to encapsulate any information that should be needed to render a link, including the package version it's from (this is necessary for Construct Hub to be able to link to the correct versions of packages).

Markdown generation can now be customized using three hooks:

  • anchorFormatter: (type: JsiiEntity) => string - customize the IDs given to anchors tags that are placed next to every header in the document, so that any part of the API reference can be directly linked
  • linkFormatter: (type: JsiiEntity, metadata: AssemblyMetadataSchema) => string - customize how links should be rendered when they appear in tables, descriptions, etc. -- e.g. to include logic to "link to another page"
  • typeFormatter?: (type: TypeSchema, linkFormatter) => string - customize how composite types should be rendered when they include nested types - this allows functionality like the screenshot below:

Screen Shot 2021-12-23 at 7 34 14 PM

See markdown.test.ts for an example of how the markdown rendering hooks would be used to format links for Construct Hub.

Examples:

@aws-cdk/aws-ecr Python API.json: https://gist.github.com/Chriscbr/731b315808faaf2a2161d686ce248368
@aws-cdk/aws-ecr Python API.md: https://gist.github.com/Chriscbr/d0833a9ecce258dead648a63253b8b0e

BREAKING CHANGE: Documentation.render is now named Documentation.toMarkdown. In addition, the options parameter for this method has changed. TranspiledType is no longer exported.

@Chriscbr
Copy link
Contributor Author

Chriscbr commented Dec 24, 2021

(moved comments to PR description)

test/docgen/view/parameter.test.ts Outdated Show resolved Hide resolved
@@ -33,16 +33,6 @@ describe('extractPackageName', () => {

});

test('custom link formatter', async () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved this test to markdown.test.ts

remarks: docs.remarks.length > 0 ? docs.remarks : undefined,
see: docs.link.length > 0 ? docs.link : undefined,
deprecated: docs.deprecated === true ? true : undefined,
deprecationReason: docs.deprecationReason,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can add more fields from this at any later point as needed (e.g. stability, examples, or other custom @ tags)

parentType: property.parentType,
parentType: callable.parentType,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

small bug fix needed to get links working correctly

@Chriscbr Chriscbr marked this pull request as ready for review January 2, 2022 23:42
@Chriscbr Chriscbr requested review from iliapolo, rix0rrr and a team January 2, 2022 23:43
@rix0rrr
Copy link
Contributor

rix0rrr commented Jan 4, 2022

One opinionated change I made was to change how code is rendered by default, from the usual markdown syntax to using HTML pre (multi-line) and code (single-line) tags. Doing this allows us to include links within snippets even when they're rendered on GitHub, as shown above.

I want to say: what's the point of MarkDown anymore at this stage 😅 but I support the usability improvements

Copy link
Contributor

@rix0rrr rix0rrr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like what you're doing, and I appreciate the effort. I'd prefer to see the MarkDown renderer factored out into its own visitor class. Seems like a neater separation of concerns, makes it easier to add new types of renders, and more in line with the final processing pipeline.


export const defaultLinkFormatter = (type: JsiiEntity) => {
const name = type.fqn.split('.').pop()!;
return `<a href="#${type.id}">${name}</a>`;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the type comes from a different assembly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case we just shrug it off. This is just a default after all. The id is unique across assemblies, so in the worst case the link is invalid. You can see this in the example gist here, where if you click on a type named Construct, the link doesn't take you anywhere.

I think this is good enough for the default.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, I suppose we could display no link if the type isn't from the current assembly. I'll leave this as a future enhancement for now since it's not too critical

Copy link
Contributor

@rix0rrr rix0rrr Jan 12, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See I disagree with this.

In terms of user experience, a link that doesn't work seems worse than no link at all. How annoyed would you be if you clicked something that's obviously a link and nothing happened? You'd click a couple times more, and then start to distrust every other link you'd see.

I'd rather have that be in the initial implementation. Doesn't seem like a huge implementation stretch, but correct me if I'm wrong.

Copy link
Contributor Author

@Chriscbr Chriscbr Jan 12, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it, I see your point now - yeah dead links are a pretty bad experience. 😅

I've added another parameter to the linkFormatter API to enable this behavior -- as of right now, linkFormatter is expected to be a pure function, so the formatter needs extra info to make the determination of whether to create a link.

The formatting APIs are getting a bit heavy/awkward so I've added some more docs and marked them experimental for now. It might make sense eventually just require anyone that wants to customize the markdown linking and formatting etc. to just implement their own markdown renderer at the end of the day -- but for now I'd rather keep these (awkward) APIs just to avoid duplicating code between here and Construct Hub. But any/all suggestions are still welcome. 🙂

src/docgen/render/markdown.ts Outdated Show resolved Hide resolved
src/docgen/schema.ts Outdated Show resolved Hide resolved
src/docgen/schema.ts Outdated Show resolved Hide resolved
src/docgen/schema.ts Outdated Show resolved Hide resolved
src/docgen/view/class.ts Outdated Show resolved Hide resolved
@Chriscbr Chriscbr requested a review from rix0rrr January 10, 2022 16:29
@Chriscbr
Copy link
Contributor Author

@rix0rrr I've given a shot at refactoring all of the markdown-ification into a visitor class - definitely feels like an improvement. Let me know what you think.

@Chriscbr Chriscbr changed the title feat: JSON doc generation feat!: JSON doc generation Jan 10, 2022
@rix0rrr rix0rrr changed the title feat!: JSON doc generation feat: JSON doc generation Jan 12, 2022
Copy link
Contributor

@rix0rrr rix0rrr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'd like to see the links changed. Let's not generate links that don't go anywhere.

Other than that, <3 this and ready to ship it!

README.md Outdated Show resolved Hide resolved
@Chriscbr
Copy link
Contributor Author

I'm getting timed out trying to create or update gists on GitHub - but here's a screenshot of the "fixed" links - for types that are not in the current document, we just show the full language-specific fqn.

Screen Shot 2022-01-12 at 12 45 18 PM

@Chriscbr Chriscbr requested a review from rix0rrr January 12, 2022 20:48
Copy link
Contributor

@iliapolo iliapolo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great!

src/docgen/render/markdown-doc.ts Outdated Show resolved Hide resolved
src/docgen/render/markdown-doc.ts Show resolved Hide resolved
src/docgen/render/markdown-render.ts Outdated Show resolved Hide resolved
src/docgen/render/markdown-render.ts Outdated Show resolved Hide resolved
src/docgen/render/markdown-render.ts Show resolved Hide resolved
src/docgen/view/documentation.ts Outdated Show resolved Hide resolved
src/docgen/view/property.ts Outdated Show resolved Hide resolved
test/docgen/view/__snapshots__/class.test.ts.snap Outdated Show resolved Hide resolved
src/docgen/schema.ts Show resolved Hide resolved
@Chriscbr Chriscbr changed the title feat: JSON doc generation feat!: JSON doc generation Jan 13, 2022
"packageVersion": "3.3.187",
"packageVersion": "3.3.188",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hrmmmmm. It looks like what's happening is that since most packages we generate have caret dependencies, the version of the referenced type isn't necessarily fixed.

I've been thinking about this for a while, including whether there's some better way to model this so that the package version stays fixed given the current assembly (e.g. by including the semver string from package.json instead) -- but it turns out that forces a lot of additional complexity onto downstream users.

For now I'm going to simply add some extra code that will make the snapshots stable by always linking to the same package version within unit tests (e.g. "1.2.3")

// may belong to dependency packages, which could be specified with caret
// dependencies - this means the packageVersion of a type like
// `construct.Construct` will be set to whichever version is installed.
// This is okay in practice, but makes snapshot tests inconsistent.
Copy link
Contributor

@iliapolo iliapolo Jan 16, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this a problem though? Our snapshots will get updated during self-mutation

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scratch that, its inconsistent over time you mean, unrelated to dependency upgrades 👍

@mergify mergify bot merged commit 757aa06 into main Jan 18, 2022
@mergify mergify bot deleted the rybickic/json branch January 18, 2022 22:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants