Files
Joey Perrott 2d8635d29d refactor(docs-infra): migrate @angular/docs from dev-infra into adev directory (#57132)
To increase the ease of development we are moving @angular/docs into the adev directory within this repo. While
we are doing this to improve our development experience in the short term, efforts are also in place
to maintain a division between this @angular/docs (shared) code and adev itself, so that it can be extracted
back out in the future when components is ready to leverage it as well.

PR Close #57132
2024-07-30 15:51:26 +00:00

123 lines
3.5 KiB
TypeScript

/*!
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.dev/license
*/
import {Tokens, Token, RendererThis, TokenizerThis} from 'marked';
import {anchorTarget} from '../../helpers';
import {loadWorkspaceRelativeFile} from '../../utils';
interface DocsCardToken extends Tokens.Generic {
type: 'docs-card';
title: string;
body: string;
link?: string;
href?: string;
imgSrc?: string;
tokens: Token[];
}
// Capture group 1: all attributes on the opening tag
// Capture group 2: all content between the open and close tags
const cardRule = /^[^<]*<docs-card\s([^>]*)>((?:.(?!\/docs-card))*)<\/docs-card>/s;
const titleRule = /title="([^"]*)"/;
const linkRule = /link="([^"]*)"/;
const hrefRule = /href="([^"]*)"/;
const imgSrcRule = /imgSrc="([^"]*)"/;
export const docsCardExtension = {
name: 'docs-card',
level: 'block' as const,
start(src: string) {
return src.match(/^\s*<docs-card\s*/m)?.index;
},
tokenizer(this: TokenizerThis, src: string): DocsCardToken | undefined {
const match = cardRule.exec(src);
if (match) {
const attr = match[1].trim();
const title = titleRule.exec(attr);
const link = linkRule.exec(attr);
const href = hrefRule.exec(attr);
const imgSrc = imgSrcRule.exec(attr);
const body = match[2].trim();
const token: DocsCardToken = {
type: 'docs-card',
raw: match[0],
title: title ? title[1] : '',
body: body ?? '',
href: href ? href[1] : undefined,
link: link ? link[1] : undefined,
imgSrc: imgSrc ? imgSrc[1] : undefined,
tokens: [],
};
this.lexer.blockTokens(token.body, token.tokens);
return token;
}
return undefined;
},
renderer(this: RendererThis, token: DocsCardToken) {
return token.imgSrc ? getCardWithSvgIllustration(this, token) : getStandardCard(this, token);
},
};
function getStandardCard(renderer: RendererThis, token: DocsCardToken) {
if (token.href) {
return `
<a href="${token.href}" ${anchorTarget(token.href)} class="docs-card">
<div>
<h3>${token.title}</h3>
${renderer.parser.parse(token.tokens)}
</div>
<span>${token.link ? token.link : 'Learn more'}</span>
</a>
`;
}
return `
<div class="docs-card">
<div>
<h3>${token.title}</h3>
${renderer.parser.parse(token.tokens)}
</div>
${token.link ? `<span>${token.link}</span>` : ''}
</div>
`;
}
function getCardWithSvgIllustration(renderer: RendererThis, token: DocsCardToken) {
// We can assume that all illustrations are svg files
// We need to read svg content, instead of renering svg with `img`,
// cause we would like to use CSS variables to support dark and light mode.
const illustration = loadWorkspaceRelativeFile(token.imgSrc!);
if (token.href) {
return `
<a href="${token.href}" ${anchorTarget(token.href)} class="docs-card docs-card-with-svg">
${illustration}
<div class="docs-card-text-content">
<div>
<h3>${token.title}</h3>
${renderer.parser.parse(token.tokens)}
</div>
<span>${token.link ? token.link : 'Learn more'}</span>
</div>
</a>
`;
}
return `
<div class="docs-card docs-card-with-svg">
${illustration}
<div class="docs-card-text-content">
<h3>${token.title}</h3>
${renderer.parser.parse(token.tokens)}
</div>
</div>
`;
}