Jelajahi Sumber

add LinkerByRelativePathConfigurer

Yuki Takei 4 tahun lalu
induk
melakukan
dd9be9c503

+ 8 - 7
packages/app/src/client/util/GrowiRenderer.js

@@ -2,24 +2,24 @@ import MarkdownIt from 'markdown-it';
 
 import loggerFactory from '~/utils/logger';
 
-import Linker from './PreProcessor/Linker';
 import CsvToTable from './PreProcessor/CsvToTable';
 import EasyGrid from './PreProcessor/EasyGrid';
+import Linker from './PreProcessor/Linker';
 import XssFilter from './PreProcessor/XssFilter';
-
+import BlockdiagConfigurer from './markdown-it/blockdiag';
+import DrawioViewerConfigurer from './markdown-it/drawio-viewer';
 import EmojiConfigurer from './markdown-it/emoji';
 import FooternoteConfigurer from './markdown-it/footernote';
-import HeaderLineNumberConfigurer from './markdown-it/header-line-number';
 import HeaderConfigurer from './markdown-it/header';
+import HeaderLineNumberConfigurer from './markdown-it/header-line-number';
+import HeaderWithEditLinkConfigurer from './markdown-it/header-with-edit-link';
+import LinkerByRelativePathConfigurer from './markdown-it/link-by-relative-path';
 import MathJaxConfigurer from './markdown-it/mathjax';
 import PlantUMLConfigurer from './markdown-it/plantuml';
 import TableConfigurer from './markdown-it/table';
+import TableWithHandsontableButtonConfigurer from './markdown-it/table-with-handsontable-button';
 import TaskListsConfigurer from './markdown-it/task-lists';
 import TocAndAnchorConfigurer from './markdown-it/toc-and-anchor';
-import BlockdiagConfigurer from './markdown-it/blockdiag';
-import DrawioViewerConfigurer from './markdown-it/drawio-viewer';
-import TableWithHandsontableButtonConfigurer from './markdown-it/table-with-handsontable-button';
-import HeaderWithEditLinkConfigurer from './markdown-it/header-with-edit-link';
 
 const logger = loggerFactory('growi:util:GrowiRenderer');
 
@@ -68,6 +68,7 @@ export default class GrowiRenderer {
     this.isMarkdownItConfigured = false;
 
     this.markdownItConfigurers = [
+      new LinkerByRelativePathConfigurer(appContainer),
       new TaskListsConfigurer(appContainer),
       new HeaderConfigurer(appContainer),
       new EmojiConfigurer(appContainer),

+ 50 - 0
packages/app/src/client/util/markdown-it/link-by-relative-path.ts

@@ -0,0 +1,50 @@
+import path from 'path';
+
+// https://regex101.com/r/vV8LUe/1
+const PATTERN_RELATIVE_PATH = new RegExp(/^(\.{1,2})(\/.*)?$/);
+
+export default class LinkerByRelativePathConfigurer {
+
+  // eslint-disable-next-line @typescript-eslint/no-explicit-any
+  appContainer: any;
+
+  // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
+  constructor(appContainer) {
+    this.appContainer = appContainer;
+  }
+
+  // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
+  configure(md): void {
+    const pageContainer = this.appContainer.getContainer('PageContainer');
+
+    // Remember old renderer, if overridden, or proxy to default renderer
+    const defaultRender = md.renderer.rules.link_open || function(tokens, idx, options, env, self) {
+      return self.renderToken(tokens, idx, options);
+    };
+
+    md.renderer.rules.link_open = function(tokens, idx, options, env, self) {
+      if (tokens[idx] == null || (typeof tokens[idx].attrIndex !== 'function')) {
+        return defaultRender(tokens, idx, options, env, self);
+      }
+
+      // get href
+      const hrefIndex = tokens[idx].attrIndex('href');
+
+      if (hrefIndex != null && hrefIndex >= 0) {
+        const href: string = tokens[idx].attrs[hrefIndex][1];
+        const currentPath: string | null = pageContainer?.state.path;
+
+        // resolve relative path and replace
+        if (PATTERN_RELATIVE_PATH.test(href) && currentPath != null) {
+          const newHref = path.resolve(path.dirname(currentPath), href);
+          tokens[idx].attrs[hrefIndex][1] = newHref;
+        }
+      }
+
+      // pass token to default renderer.
+      return defaultRender(tokens, idx, options, env, self);
+    };
+
+  }
+
+}