Yuki Takei 3 лет назад
Родитель
Сommit
50fcc9fbb2

+ 13 - 1
packages/app/src/components/PageEditor.tsx

@@ -26,6 +26,7 @@ import {
   EditorMode,
   useEditorMode, useIsMobile, useSelectedGrant,
 } from '~/stores/ui';
+import { registerGrowiFacade } from '~/utils/growi-facade';
 import loggerFactory from '~/utils/logger';
 
 
@@ -68,7 +69,7 @@ const PageEditor = React.memo((): JSX.Element => {
   const { data: isUploadableFile } = useIsUploadableFile();
   const { data: isUploadableImage } = useIsUploadableImage();
 
-  const { data: rendererOptions } = usePreviewOptions();
+  const { data: rendererOptions, mutate: mutateRendererOptions } = usePreviewOptions();
 
   const currentRevisionId = currentPage?.revision?._id;
   const initialValue = editingMarkdown ?? '';
@@ -82,6 +83,17 @@ const PageEditor = React.memo((): JSX.Element => {
   const editorRef = useRef<IEditorMethods>(null);
   const previewRef = useRef<HTMLDivElement>(null);
 
+  // register to facade
+  useEffect(() => {
+    registerGrowiFacade({
+      markdownRenderer: {
+        optionsMutators: {
+          previewOptionsMutator: mutateRendererOptions,
+        },
+      },
+    });
+  }, [mutateRendererOptions]);
+
   const setMarkdownWithDebounce = useMemo(() => debounce(100, throttle(150, (value: string, isClean: boolean) => {
     markdownToSave.current = value;
     setMarkdownToPreview(value);

+ 3 - 0
packages/app/src/interfaces/global.ts

@@ -1,8 +1,11 @@
 import EventEmitter from 'events';
 
+import { GrowiFacade } from '@growi/core';
+
 import { IGraphViewer } from './graph-viewer';
 
 export type CustomWindow = Window
                          & typeof globalThis
+                         & { growiFacade: GrowiFacade }
                          & { globalEmitter: EventEmitter }
                          & { GraphViewer: IGraphViewer };

+ 14 - 0
packages/app/src/services/renderer/renderer.tsx

@@ -1,6 +1,7 @@
 // allow only types to import from react
 import { ComponentType } from 'react';
 
+import { isClient } from '@growi/core';
 import { Lsx } from '@growi/plugin-lsx/components';
 import * as lsxGrowiPlugin from '@growi/plugin-lsx/services/renderer';
 import growiPlugin from '@growi/remark-growi-plugin';
@@ -25,6 +26,7 @@ import { CodeBlock } from '~/components/ReactMarkdownComponents/CodeBlock';
 import { Header } from '~/components/ReactMarkdownComponents/Header';
 import { NextLink } from '~/components/ReactMarkdownComponents/NextLink';
 import { RendererConfig } from '~/interfaces/services/renderer';
+import { registerGrowiFacade } from '~/utils/growi-facade';
 import loggerFactory from '~/utils/logger';
 
 import * as addClass from './rehype-plugins/add-class';
@@ -480,3 +482,15 @@ export const generateOthersOptions = (config: RendererConfig): RendererOptions =
   verifySanitizePlugin(options);
   return options;
 };
+
+
+// register to facade
+if (isClient()) {
+  registerGrowiFacade({
+    markdownRenderer: {
+      optionsGenerators: {
+        generatePreviewOptions,
+      },
+    },
+  });
+}

+ 6 - 1
packages/app/src/stores/renderer.tsx

@@ -8,6 +8,7 @@ import {
   generateSimpleViewOptions, generatePreviewOptions, generateOthersOptions,
   generateViewOptions, generateTocOptions,
 } from '~/services/renderer/renderer';
+import { getGrowiFacade } from '~/utils/growi-facade';
 
 
 import {
@@ -88,7 +89,11 @@ export const usePreviewOptions = (): SWRResponse<RendererOptions, Error> => {
 
   return useSWRImmutable<RendererOptions, Error>(
     key,
-    (rendererId, rendererConfig, currentPagePath) => generatePreviewOptions(rendererConfig, currentPagePath),
+    (rendererId, rendererConfig, pagePath, highlightKeywords) => {
+      // determine options generator
+      const optionsGenerator = getGrowiFacade().markdownRenderer?.optionsGenerators?.customGeneratePreviewOptions ?? generatePreviewOptions;
+      return optionsGenerator(rendererConfig, pagePath, highlightKeywords);
+    },
     {
       fallbackData: isAllDataValid ? generatePreviewOptions(rendererConfig, currentPagePath) : undefined,
     },

+ 27 - 0
packages/app/src/utils/growi-facade.ts

@@ -0,0 +1,27 @@
+import { GrowiFacade, isServer } from '@growi/core';
+import deepmerge from 'ts-deepmerge';
+
+import { CustomWindow } from '~/interfaces/global';
+
+export const getGrowiFacade = (): GrowiFacade => {
+  if (isServer()) {
+    return {};
+  }
+
+  if ((window as CustomWindow).growiFacade == null) {
+    (window as CustomWindow).growiFacade = {};
+  }
+
+  return (window as CustomWindow).growiFacade;
+};
+
+export const registerGrowiFacade = (addedFacade: GrowiFacade): void => {
+  if (isServer()) {
+    throw new Error('This method is available only in client.');
+  }
+
+  (window as CustomWindow).growiFacade = deepmerge(
+    getGrowiFacade(),
+    addedFacade,
+  );
+};

+ 1 - 0
packages/core/src/index.ts

@@ -15,6 +15,7 @@ export * as pageUtils from './utils/page-utils';
 export * from './plugin/interfaces/option-parser';
 export * from './interfaces/attachment';
 export * from './interfaces/common';
+export * from './interfaces/growi-facade';
 export * from './interfaces/has-object-id';
 export * from './interfaces/lang';
 export * from './interfaces/page';

+ 9 - 0
packages/core/src/interfaces/growi-facade.ts

@@ -0,0 +1,9 @@
+export type GrowiFacade = {
+  markdownRenderer?: {
+    optionsGenerators?: {
+      generatePreviewOptions?: any;
+      customGeneratePreviewOptions?: any;
+    },
+    optionsMutators?: any,
+  }
+};