angular / dgeni-packages

A collection of dgeni packages for generating documentation from source code.
MIT License
142 stars 106 forks source link

Regression: since 0.28.0, marked filter does not render markdown in HTML blocks #310

Closed Splaktar closed 3 years ago

Splaktar commented 3 years ago

I first hit this in https://github.com/angular/material/pull/11881, which ended up with the project pinning the dependency to 0.27.5 and not being able to upgrade since then. At that time, early 2020, I was quite unfamiliar with Marked and how it was used in dgeni-packages. So I was hoping that someone else would report this and it would get fixed. Unfortunately, that hasn't happened.

I have dug deeper recently in https://github.com/angular/angular.js/pull/17141 and this seems to be related to https://github.com/angular/dgeni-packages/pull/281/commits/16ceb9c9bc9aac168f82db977a9e380fb20b815a from PR https://github.com/angular/dgeni-packages/pull/281, released in 0.28.0.

There is a breaking changes note:

There are a few relevant breaking changes with this latest version of `marked`.
This only affects usage of the `renderMarkdown()` service and the `marked`
nunjucks filter. Take a look through the
[marked release notes](https://github.com/markedjs/marked/releases) and
check if this affects you.
  1. renderMarkdown() is not used by AngularJS or AngularJS Material.
  2. They both use require('dgeni-packages/nunjucks') and the marked filter, i.e. {$ doc.description | marked $}

I read through the Marked release notes, but being unfamiliar with the library, I didn't really see anything related or of value to fixing this.

I read through this project's README.md but didn't see any references on how to use the marked filter.

It looks like the code for the filter is here: https://github.com/angular/dgeni-packages/blob/0fe303e593fe04769d6cec958126082e6a541e55/nunjucks/rendering/filters/marked.js#L1-L13 I don't see how a change to any marked APIs could cause issues with this simple filter.

Except that it seems to call renderMarkdown() https://github.com/angular/dgeni-packages/blob/0fe303e593fe04769d6cec958126082e6a541e55/nunjucks/services/renderMarkdown.js#L1-L28 Which does import marked and use its APIs.

There is perhaps something that I'm missing here where this can be fixed in the AngularJS and AngularJS Material projects, but from what I can tell, with limited knowledge of this library, the fix needs to be made here.

Splaktar commented 3 years ago

I looked into the fact that the arguments for code() are (code, infostring, escaped) (where infostring has the lang and escaped is a boolean) but the renderMarkdown() arguments are (code, string, language). I tried

  renderer.code = (code, language, escaped) => {

    const trimmedCode = trimIndentation(code);
    let renderedCode = marked.Renderer.prototype.code.call(renderer, trimmedCode, language, escaped);

But it didn't help at all.

When I dug into this deeper, the | marked filters don't seem to be running at all. I put logs in here for code and renderedCode and some of the problematic text like [Find out more](misc/version-support-status) never came through at all.

Doh, because the problematic markdown is not code... 🤦🏼‍♂️

Splaktar commented 3 years ago

OK, so this doesn't parse properly:

<div class="alert alert-info">
**AngularJS Prefixes `$` and `$$`**:

To prevent accidental name collisions with your code,
AngularJS prefixes names of public objects with `$` and names of private objects with `$$`.
Please do not use the `$` or `$$` prefix in your code.
</div>

But this does

<div class="alert alert-info">

**AngularJS Prefixes `$` and `$$`**:

To prevent accidental name collisions with your code,
AngularJS prefixes names of public objects with `$` and names of private objects with `$$`.
Please do not use the `$` or `$$` prefix in your code.
</div>
Splaktar commented 3 years ago

Failing test

  it("should render markdown links in divs", () => {
    const html = renderMarkdown(
      '<div>\n' +
      '  some text with an important [link](https://angularjs.org)\n' +
      '</div>'
    );
    expect(html).toEqual(
      '<div>\n' +
      '  some text with an important <a href="https://angularjs.org">link</a>\n' +
      '</div>'
    );
  });

Passing test

  it("should render markdown links in divs with extra padding", () => {
    const html = renderMarkdown(
      '<div>\n\n' + // <-- this extra line feed is what makes it pass.
      '  some text with an important [link](https://angularjs.org)\n' +
      '</div>'
    );
    expect(html).toEqual(
      '<div>\n\n' +
      '<p>  some text with an important <a href="https://angularjs.org">link</a></p>\n' +
      '</div>'
    );
  });
Splaktar commented 3 years ago

OK, this is a bug or breaking change by design in marked. Here's a minimal demo with marked@2.0.7.

It works with marked@0.3.19 (demo) and breaks in 0.4.0.

Splaktar commented 3 years ago

I've opened https://github.com/markedjs/marked/issues/2085 for this. It looks like this breaking change might be Working as Intended, but I hope that there is some way that I can monkey patch the parser/renderer to fix this w/o having to add a ton of blank line feeds throughout the old documentation.

Splaktar commented 3 years ago

So the response from the marked team was extremely short, but it basically amounted to

This was broken until 0.4.0 when we aligned with the CommonMark Spec in https://github.com/markedjs/marked/pull/1135. You built all of your documentation for two major projects on top of broken functionality that we will never support. You might be able to go and write a custom renderer for markdown in block HTML elements, for which we have no real examples or guidance.

This kind of custom HTML block renderer could live in dgeni-packages, but it's not clear to me that it's viable or worth the effort. The Angular project has already moved on to remark, so there would be no benefit there.

That said, it's not ideal to leave the AngularJS and AngularJS Material projects on an old version of marked that has a huge list of major vulnerabilities. Though it could be argued that those vulnerabilities could only be triggered via commits to the repos, which are extremely limited at this time.

(╯°□°)╯︵ ┻━┻

petebacondarwin commented 3 years ago

The CommonMark specification is what we should be following in our use of markdown (IMO). If the only problems with upgrading to the new version of dgeni-packages is that angular.js docs need to be updated to include blank lines to trigger the switch from HTML mode to markdown mode then that is the most appropriate fix.

Note that remark (used in the Angular project) also conforms to this CommonMark specfiication.

Closing as this is working as expected in dgeni-packages.