| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326 |
- import assert from 'assert';
- import { isClient } from '@growi/core/dist/utils/browser-utils';
- import * as presentation from '@growi/presentation/dist/client/services/sanitize-option';
- import * as refsGrowiDirective from '@growi/remark-attachment-refs/dist/client';
- import * as drawio from '@growi/remark-drawio';
- import * as lsxGrowiDirective from '@growi/remark-lsx/dist/client';
- import katex from 'rehype-katex';
- import sanitize from 'rehype-sanitize';
- import slug from 'rehype-slug';
- import type { HtmlElementNode } from 'rehype-toc';
- import breaks from 'remark-breaks';
- import remarkGithubAdmonitionsToDirectives from 'remark-github-admonitions-to-directives';
- import math from 'remark-math';
- import deepmerge from 'ts-deepmerge';
- import type { Pluggable } from 'unified';
- import { DrawioViewerWithEditButton } from '~/client/components/ReactMarkdownComponents/DrawioViewerWithEditButton';
- import { Header } from '~/client/components/ReactMarkdownComponents/Header';
- import { LightBox } from '~/client/components/ReactMarkdownComponents/LightBox';
- import { RichAttachment } from '~/client/components/ReactMarkdownComponents/RichAttachment';
- import { TableWithEditButton } from '~/client/components/ReactMarkdownComponents/TableWithEditButton';
- import * as callout from '~/features/callout';
- import * as mermaid from '~/features/mermaid';
- import * as plantuml from '~/features/plantuml';
- import type { RendererOptions } from '~/interfaces/renderer-options';
- import { type RendererConfigExt } from '~/interfaces/services/renderer';
- import * as addLineNumberAttribute from '~/services/renderer/rehype-plugins/add-line-number-attribute';
- import * as keywordHighlighter from '~/services/renderer/rehype-plugins/keyword-highlighter';
- import * as relocateToc from '~/services/renderer/rehype-plugins/relocate-toc';
- import * as attachment from '~/services/renderer/remark-plugins/attachment';
- import * as codeBlock from '~/services/renderer/remark-plugins/codeblock';
- import * as xsvToTable from '~/services/renderer/remark-plugins/xsv-to-table';
- import {
- getCommonSanitizeOption, generateCommonOptions, verifySanitizePlugin,
- } from '~/services/renderer/renderer';
- import loggerFactory from '~/utils/logger';
- // import EasyGrid from './PreProcessor/EasyGrid';
- import '@growi/remark-lsx/dist/client/style.css';
- import '@growi/remark-attachment-refs/dist/client/style.css';
- const logger = loggerFactory('growi:cli:services:renderer');
- assert(isClient(), 'This module must be loaded only from client modules.');
- export const generateViewOptions = (
- pagePath: string,
- config: RendererConfigExt,
- storeTocNode: (toc: HtmlElementNode) => void,
- ): RendererOptions => {
- const options = generateCommonOptions(pagePath);
- const { remarkPlugins, rehypePlugins, components } = options;
- // add remark plugins
- remarkPlugins.push(
- math,
- [plantuml.remarkPlugin, { plantumlUri: config.plantumlUri, isDarkMode: config.isDarkMode }],
- drawio.remarkPlugin,
- mermaid.remarkPlugin,
- xsvToTable.remarkPlugin,
- attachment.remarkPlugin,
- remarkGithubAdmonitionsToDirectives,
- callout.remarkPlugin,
- lsxGrowiDirective.remarkPlugin,
- refsGrowiDirective.remarkPlugin,
- );
- if (config.isEnabledLinebreaks) {
- remarkPlugins.push(breaks);
- }
- const rehypeSanitizePlugin: Pluggable | (() => void) = config.isEnabledXssPrevention
- ? [sanitize, deepmerge(
- getCommonSanitizeOption(config),
- presentation.sanitizeOption,
- drawio.sanitizeOption,
- mermaid.sanitizeOption,
- callout.sanitizeOption,
- attachment.sanitizeOption,
- lsxGrowiDirective.sanitizeOption,
- refsGrowiDirective.sanitizeOption,
- codeBlock.sanitizeOption,
- )]
- : () => {};
- // add rehype plugins
- rehypePlugins.push(
- slug,
- [lsxGrowiDirective.rehypePlugin, { pagePath, isSharedPage: config.isSharedPage }],
- [refsGrowiDirective.rehypePlugin, { pagePath }],
- rehypeSanitizePlugin,
- katex,
- [relocateToc.rehypePluginStore, { storeTocNode }],
- );
- // add components
- if (components != null) {
- components.h1 = Header;
- components.h2 = Header;
- components.h3 = Header;
- components.h4 = Header;
- components.h5 = Header;
- components.h6 = Header;
- components.lsx = lsxGrowiDirective.Lsx;
- components.ref = refsGrowiDirective.Ref;
- components.refs = refsGrowiDirective.Refs;
- components.refimg = refsGrowiDirective.RefImg;
- components.refsimg = refsGrowiDirective.RefsImg;
- components.gallery = refsGrowiDirective.Gallery;
- components.drawio = props => <DrawioViewerWithEditButton isDarkMode={config.isDarkMode} {...props} />;
- components.table = TableWithEditButton;
- components.mermaid = mermaid.MermaidViewer;
- components.callout = callout.CalloutViewer;
- components.attachment = RichAttachment;
- components.img = LightBox;
- }
- if (config.isEnabledXssPrevention) {
- verifySanitizePlugin(options, false);
- }
- return options;
- };
- export const generateTocOptions = (config: RendererConfigExt, tocNode: HtmlElementNode | undefined): RendererOptions => {
- const options = generateCommonOptions(undefined);
- const { rehypePlugins } = options;
- // add remark plugins
- // remarkPlugins.push();
- const rehypeSanitizePlugin: Pluggable | (() => void) = config.isEnabledXssPrevention
- ? [sanitize, deepmerge(
- getCommonSanitizeOption(config),
- codeBlock.sanitizeOption,
- )]
- : () => {};
- // add rehype plugins
- rehypePlugins.push(
- [relocateToc.rehypePluginRestore, { tocNode }],
- rehypeSanitizePlugin,
- );
- if (config.isEnabledXssPrevention) {
- verifySanitizePlugin(options);
- }
- return options;
- };
- export const generateSimpleViewOptions = (
- config: RendererConfigExt,
- pagePath: string,
- highlightKeywords?: string | string[],
- overrideIsEnabledLinebreaks?: boolean,
- ): RendererOptions => {
- const options = generateCommonOptions(pagePath);
- const { remarkPlugins, rehypePlugins, components } = options;
- // add remark plugins
- remarkPlugins.push(
- math,
- [plantuml.remarkPlugin, { plantumlUri: config.plantumlUri, isDarkMode: config.isDarkMode }],
- drawio.remarkPlugin,
- mermaid.remarkPlugin,
- xsvToTable.remarkPlugin,
- attachment.remarkPlugin,
- remarkGithubAdmonitionsToDirectives,
- callout.remarkPlugin,
- lsxGrowiDirective.remarkPlugin,
- refsGrowiDirective.remarkPlugin,
- );
- const isEnabledLinebreaks = overrideIsEnabledLinebreaks ?? config.isEnabledLinebreaks;
- if (isEnabledLinebreaks) {
- remarkPlugins.push(breaks);
- }
- const rehypeSanitizePlugin: Pluggable | (() => void) = config.isEnabledXssPrevention
- ? [sanitize, deepmerge(
- getCommonSanitizeOption(config),
- presentation.sanitizeOption,
- drawio.sanitizeOption,
- mermaid.sanitizeOption,
- callout.sanitizeOption,
- attachment.sanitizeOption,
- lsxGrowiDirective.sanitizeOption,
- refsGrowiDirective.sanitizeOption,
- codeBlock.sanitizeOption,
- )]
- : () => {};
- // add rehype plugins
- rehypePlugins.push(
- [lsxGrowiDirective.rehypePlugin, { pagePath, isSharedPage: config.isSharedPage }],
- [refsGrowiDirective.rehypePlugin, { pagePath }],
- [keywordHighlighter.rehypePlugin, { keywords: highlightKeywords }],
- rehypeSanitizePlugin,
- katex,
- );
- // add components
- if (components != null) {
- components.lsx = lsxGrowiDirective.LsxImmutable;
- components.ref = refsGrowiDirective.RefImmutable;
- components.refs = refsGrowiDirective.RefsImmutable;
- components.refimg = refsGrowiDirective.RefImgImmutable;
- components.refsimg = refsGrowiDirective.RefsImgImmutable;
- components.gallery = refsGrowiDirective.GalleryImmutable;
- components.drawio = props => <drawio.DrawioViewer {...props} isDarkMode={config.isDarkMode} />;
- components.mermaid = mermaid.MermaidViewer;
- components.callout = callout.CalloutViewer;
- components.attachment = RichAttachment;
- components.img = LightBox;
- }
- if (config.isEnabledXssPrevention) {
- verifySanitizePlugin(options, false);
- }
- return options;
- };
- export const generatePresentationViewOptions = (
- config: RendererConfigExt,
- pagePath: string,
- ): RendererOptions => {
- // based on simple view options
- const options = generateSimpleViewOptions(config, pagePath);
- const { rehypePlugins } = options;
- const rehypeSanitizePlugin: Pluggable | (() => void) = config.isEnabledXssPrevention
- ? [sanitize, deepmerge(
- addLineNumberAttribute.sanitizeOption,
- )]
- : () => {};
- // add rehype plugins
- rehypePlugins.push(
- addLineNumberAttribute.rehypePlugin,
- rehypeSanitizePlugin,
- );
- if (config.isEnabledXssPrevention) {
- verifySanitizePlugin(options, false);
- }
- return options;
- };
- export const generatePreviewOptions = (config: RendererConfigExt, pagePath: string): RendererOptions => {
- const options = generateCommonOptions(pagePath);
- const { remarkPlugins, rehypePlugins, components } = options;
- // add remark plugins
- remarkPlugins.push(
- math,
- [plantuml.remarkPlugin, { plantumlUri: config.plantumlUri, isDarkMode: config.isDarkMode }],
- drawio.remarkPlugin,
- mermaid.remarkPlugin,
- xsvToTable.remarkPlugin,
- attachment.remarkPlugin,
- remarkGithubAdmonitionsToDirectives,
- callout.remarkPlugin,
- lsxGrowiDirective.remarkPlugin,
- refsGrowiDirective.remarkPlugin,
- );
- if (config.isEnabledLinebreaks) {
- remarkPlugins.push(breaks);
- }
- const rehypeSanitizePlugin: Pluggable | (() => void) = config.isEnabledXssPrevention
- ? [sanitize, deepmerge(
- getCommonSanitizeOption(config),
- drawio.sanitizeOption,
- mermaid.sanitizeOption,
- callout.sanitizeOption,
- attachment.sanitizeOption,
- lsxGrowiDirective.sanitizeOption,
- refsGrowiDirective.sanitizeOption,
- addLineNumberAttribute.sanitizeOption,
- codeBlock.sanitizeOption,
- )]
- : () => {};
- // add rehype plugins
- rehypePlugins.push(
- [lsxGrowiDirective.rehypePlugin, { pagePath, isSharedPage: config.isSharedPage }],
- [refsGrowiDirective.rehypePlugin, { pagePath }],
- addLineNumberAttribute.rehypePlugin,
- rehypeSanitizePlugin,
- katex,
- );
- // add components
- if (components != null) {
- components.lsx = lsxGrowiDirective.LsxImmutable;
- components.ref = refsGrowiDirective.RefImmutable;
- components.refs = refsGrowiDirective.RefsImmutable;
- components.refimg = refsGrowiDirective.RefImgImmutable;
- components.refsimg = refsGrowiDirective.RefsImgImmutable;
- components.gallery = refsGrowiDirective.GalleryImmutable;
- components.drawio = props => <drawio.DrawioViewer {...props} isDarkMode={config.isDarkMode} />;
- components.mermaid = mermaid.MermaidViewer;
- components.callout = callout.CalloutViewer;
- components.attachment = RichAttachment;
- components.img = LightBox;
- }
- if (config.isEnabledXssPrevention) {
- verifySanitizePlugin(options, false);
- }
- return options;
- };
|