Skip to content

Commit

Permalink
fix(content): fix deprecation warnings for marked package (#487)
Browse files Browse the repository at this point in the history
  • Loading branch information
luishcastroc authored Jun 29, 2023
1 parent a49376c commit 87a978b
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 68 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@
"h3": "^1.6.6",
"isomorphic-fetch": "^3.0.0",
"marked": "^5.0.2",
"marked-gfm-heading-id": "^3.0.4",
"marked-highlight": "^2.0.1",
"react": "^18.2.0",
"react-dom": "^18.0.0",
"rxjs": "7.8.0",
Expand Down
2 changes: 2 additions & 0 deletions packages/content/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
"@angular/router": "^15.0.0 || ^16.0.0",
"rxjs": "^6.5.0 || ^7.5.0",
"marked": "^5.0.2",
"marked-gfm-heading-id": "^3.0.4",
"marked-highlight": "^2.0.1",
"prismjs": "^1.29.0",
"front-matter": "^4.0.2"
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { MarkdownContentRendererService } from './markdown-content-renderer.service';
import { TestBed } from '@angular/core/testing';
import { MarkedSetupService } from './marked-setup.service';

describe('MarkdownContentRendererService', () => {
function setup() {
TestBed.configureTestingModule({
providers: [MarkdownContentRendererService],
providers: [MarkdownContentRendererService, MarkedSetupService],
});
return { service: TestBed.inject(MarkdownContentRendererService) };
}
Expand All @@ -14,4 +15,21 @@ describe('MarkdownContentRendererService', () => {
const result = await service.render('# Hello World');
expect(result).toMatch('<h1 id="hello-world">Hello World</h1>\n');
});

it('render should correctly highlight code blocks', async () => {
const { service } = setup();
let testCode = "```javascript\nconsole.log('Hello, world!');\n```";
let result = await service.render(testCode);

expect(result).toContain(
'<pre class="language-javascript"><code class="language-javascript">'
);

testCode = "```typescript\nconsole.log('Hello, world!');\n```";
result = await service.render(testCode);

expect(result).toContain(
'<pre class="language-typescript"><code class="language-typescript">'
);
});
});
75 changes: 9 additions & 66 deletions packages/content/src/lib/markdown-content-renderer.service.ts
Original file line number Diff line number Diff line change
@@ -1,86 +1,29 @@
/**
* Credit goes to Scully for original implementation
* https://github.com/scullyio/scully/blob/main/libs/scully/src/lib/fileHanderPlugins/markdown.ts
*/
import { inject, Injectable, PLATFORM_ID, Provider } from '@angular/core';
import { marked } from 'marked';

import 'prismjs';
import 'prismjs/plugins/toolbar/prism-toolbar';
import 'prismjs/plugins/copy-to-clipboard/prism-copy-to-clipboard';
import 'prismjs/components/prism-bash';
import 'prismjs/components/prism-css';
import 'prismjs/components/prism-javascript';
import 'prismjs/components/prism-json';
import 'prismjs/components/prism-markup';
import 'prismjs/components/prism-typescript';

import { ContentRenderer } from './content-renderer';

declare const Prism: typeof import('prismjs');

const renderer = new marked.Renderer();
// wrap code block the way Prism.js expects it
renderer.code = function (this: any, code, lang) {
// eslint-disable-next-line
code = this.options.highlight(code, lang);
if (!lang) {
return '<pre><code>' + code + '</code></pre>';
}
// e.g. "language-js"
const langClass = 'language-' + lang;
return (
'<pre class="' +
langClass +
'"><code class="' +
langClass +
'">' +
code +
'</code></pre>'
);
};
// ------------------------------
import { MarkedSetupService } from './marked-setup.service';

@Injectable()
export class MarkdownContentRendererService implements ContentRenderer {
platformId = inject(PLATFORM_ID);
#marked = inject(MarkedSetupService, { self: true });

async render(content: string) {
marked.setOptions({
renderer,
highlight: (code, lang) => {
lang = lang || 'typescript';
if (!Prism.languages[lang]) {
console.warn(`Notice:
---------------------------------------------------------------------------------------
The requested language '${lang}' is not available with the provided setup.
To enable, import your main.ts as:
import 'prismjs/components/prism-${lang}';
---------------------------------------------------------------------------------------
`);
return code;
}
return Prism.highlight(code, Prism.languages[lang], lang);
},
pedantic: false,
gfm: true,
breaks: false,
sanitize: false,
smartypants: false,
xhtml: false,
});

return marked(content);
return this.#marked.getMarkedInstance().parse(content);
}

// eslint-disable-next-line
enhance() {}
}

export function withMarkdownRenderer(): Provider {
return { provide: ContentRenderer, useClass: MarkdownContentRendererService };
return {
provide: ContentRenderer,
useClass: MarkdownContentRendererService,
deps: [MarkedSetupService],
};
}

export function provideContent(...features: Provider[]) {
return [...features];
return [...features, MarkedSetupService];
}
80 changes: 80 additions & 0 deletions packages/content/src/lib/marked-setup.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/**
* Credit goes to Scully for original implementation
* https://github.com/scullyio/scully/blob/main/libs/scully/src/lib/fileHanderPlugins/markdown.ts
*/
import { Injectable } from '@angular/core';
import { marked } from 'marked';
import { gfmHeadingId } from 'marked-gfm-heading-id';
import { markedHighlight } from 'marked-highlight';

import 'prismjs';
import 'prismjs/plugins/toolbar/prism-toolbar';
import 'prismjs/plugins/copy-to-clipboard/prism-copy-to-clipboard';
import 'prismjs/components/prism-bash';
import 'prismjs/components/prism-css';
import 'prismjs/components/prism-javascript';
import 'prismjs/components/prism-json';
import 'prismjs/components/prism-markup';
import 'prismjs/components/prism-typescript';

declare const Prism: typeof import('prismjs');

@Injectable()
export class MarkedSetupService {
private readonly marked: typeof marked;

constructor() {
const renderer = new marked.Renderer();
renderer.code = (code, lang) => {
if (!lang) {
return '<pre><code>' + code + '</code></pre>';
}
const langClass = 'language-' + lang;
const html =
'<pre class="' +
langClass +
'"><code class="' +
langClass +
'">' +
code +
'</code></pre>';
return html;
};

marked.use(
gfmHeadingId(),
markedHighlight({
highlight: (code, lang) => {
lang = lang || 'typescript';
if (!Prism.languages[lang]) {
console.warn(`Notice:
---------------------------------------------------------------------------------------
The requested language '${lang}' is not available with the provided setup.
To enable, import your main.ts as:
import 'prismjs/components/prism-${lang}';
---------------------------------------------------------------------------------------
`);
return code;
}
return Prism.highlight(code, Prism.languages[lang], lang);
},
}),
{
renderer,
pedantic: false,
gfm: true,
breaks: false,
sanitize: false,
smartypants: false,
xhtml: false,
mangle: false,
}
);

this.marked = marked;
}

getMarkedInstance(): typeof marked {
return this.marked;
}
}
24 changes: 23 additions & 1 deletion pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 87a978b

Please sign in to comment.