Sfoglia il codice sorgente

configure biome for presentation package

Futa Arai 9 mesi fa
parent
commit
dff86567e7

+ 0 - 1
biome.json

@@ -25,7 +25,6 @@
       "./packages/editor/**",
       "./packages/pdf-converter-client/**",
       "./packages/pluginkit/**",
-      "./packages/presentation/**",
       "./packages/remark-attachment-refs/**"
     ]
   },

+ 1 - 1
packages/presentation/.eslintignore

@@ -1 +1 @@
-/dist/**
+*

+ 0 - 5
packages/presentation/.eslintrc.cjs

@@ -1,5 +0,0 @@
-module.exports = {
-  extends: [
-    'weseek/react',
-  ],
-};

+ 1 - 1
packages/presentation/package.json

@@ -33,7 +33,7 @@
     "clean": "shx rm -rf dist",
     "dev": "vite build --mode dev",
     "watch": "pnpm run dev -w --emptyOutDir=false",
-    "lint:js": "eslint **/*.{js,jsx,ts,tsx}",
+    "lint:js": "biome check",
     "lint:styles": "stylelint --allow-empty-input \"src/**/*.scss\" \"src/**/*.css\"",
     "lint:typecheck": "vue-tsc --noEmit",
     "lint": "run-p lint:*"

+ 25 - 18
packages/presentation/src/client/components/GrowiSlides.tsx

@@ -4,27 +4,33 @@ import Head from 'next/head';
 import ReactMarkdown from 'react-markdown';
 
 import type { PresentationOptions } from '../consts';
-import { MARP_CONTAINER_CLASS_NAME, presentationMarpit, slideMarpit } from '../services/growi-marpit';
+import {
+  MARP_CONTAINER_CLASS_NAME,
+  presentationMarpit,
+  slideMarpit,
+} from '../services/growi-marpit';
 import * as extractSections from '../services/renderer/extract-sections';
 
-import { PresentationRichSlideSection, RichSlideSection } from './RichSlideSection';
-
+import {
+  PresentationRichSlideSection,
+  RichSlideSection,
+} from './RichSlideSection';
 
 type Props = {
-  options: PresentationOptions,
-  children?: string,
-  presentation?: boolean,
-}
+  options: PresentationOptions;
+  children?: string;
+  presentation?: boolean;
+};
 
 export const GrowiSlides = (props: Props): JSX.Element => {
-  const {
-    options, children, presentation,
-  } = props;
-  const {
-    rendererOptions, isDarkMode, disableSeparationByHeader,
-  } = options;
-
-  if (rendererOptions == null || rendererOptions.remarkPlugins == null || rendererOptions.components == null) {
+  const { options, children, presentation } = props;
+  const { rendererOptions, isDarkMode, disableSeparationByHeader } = options;
+
+  if (
+    rendererOptions == null ||
+    rendererOptions.remarkPlugins == null ||
+    rendererOptions.components == null
+  ) {
     return <></>;
   }
 
@@ -35,7 +41,9 @@ export const GrowiSlides = (props: Props): JSX.Element => {
       disableSeparationByHeader,
     },
   ]);
-  rendererOptions.components.section = presentation ? PresentationRichSlideSection : RichSlideSection;
+  rendererOptions.components.section = presentation
+    ? PresentationRichSlideSection
+    : RichSlideSection;
 
   const marpit = presentation ? presentationMarpit : slideMarpit;
   const { css } = marpit.render('');
@@ -46,10 +54,9 @@ export const GrowiSlides = (props: Props): JSX.Element => {
       </Head>
       <div className={`slides ${MARP_CONTAINER_CLASS_NAME}`}>
         <ReactMarkdown {...rendererOptions}>
-          { children ?? '## No Contents' }
+          {children ?? '## No Contents'}
         </ReactMarkdown>
       </div>
     </>
   );
-
 };

+ 3 - 3
packages/presentation/src/client/components/MarpSlides.tsx

@@ -5,9 +5,9 @@ import Head from 'next/head';
 import { presentationMarpit, slideMarpit } from '../services/growi-marpit';
 
 type Props = {
-  children?: string,
-  presentation?: boolean,
-}
+  children?: string;
+  presentation?: boolean;
+};
 
 export const MarpSlides = (props: Props): JSX.Element => {
   const { children, presentation } = props;

+ 10 - 10
packages/presentation/src/client/components/Presentation.tsx

@@ -1,4 +1,4 @@
-import { useEffect, type JSX } from 'react';
+import { type JSX, useEffect } from 'react';
 
 import Reveal from 'reveal.js';
 
@@ -10,7 +10,6 @@ import styles from './Presentation.module.scss';
 
 const moduleClass = styles['grw-presentation'] ?? '';
 
-
 const baseRevealOptions: Reveal.Options = {
   // adjust size to the marp preset size
   width: 1280,
@@ -27,14 +26,14 @@ const baseRevealOptions: Reveal.Options = {
  */
 const removeAllHiddenElements = () => {
   const sections = document.querySelectorAll(`${moduleClass} section`);
-  sections.forEach(section => section.removeAttribute('hidden'));
+  sections.forEach((section) => section.removeAttribute('hidden'));
 };
 
 export type PresentationProps = {
-  options: PresentationOptions,
-  marp?: boolean,
-  children?: string,
-}
+  options: PresentationOptions;
+  marp?: boolean;
+  children?: string;
+};
 
 export const Presentation = (props: PresentationProps): JSX.Element => {
   const { options, marp, children } = props;
@@ -45,8 +44,7 @@ export const Presentation = (props: PresentationProps): JSX.Element => {
       return;
     }
     const deck = new Reveal({ ...baseRevealOptions, ...revealOptions });
-    deck.initialize()
-      .then(() => deck.slide(0)); // navigate to the first slide
+    deck.initialize().then(() => deck.slide(0)); // navigate to the first slide
 
     deck.on('ready', removeAllHiddenElements);
     deck.on('slidechanged', removeAllHiddenElements);
@@ -59,7 +57,9 @@ export const Presentation = (props: PresentationProps): JSX.Element => {
 
   return (
     <div className={`${moduleClass} reveal`}>
-      <Slides options={options} hasMarpFlag={marp} presentation>{children}</Slides>
+      <Slides options={options} hasMarpFlag={marp} presentation>
+        {children}
+      </Slides>
     </div>
   );
 };

+ 34 - 34
packages/presentation/src/client/components/RichSlideSection.tsx

@@ -1,47 +1,47 @@
-import type { ReactNode, JSX } from 'react';
+import type { JSX, ReactNode } from 'react';
 import React from 'react';
 
 type RichSlideSectionProps = {
-  children?: ReactNode,
-  presentation?: boolean,
-}
-
-const OriginalRichSlideSection = React.memo((props: RichSlideSectionProps): JSX.Element => {
-  const { children, presentation } = props;
-
-  return (
-    <section className={presentation ? 'm-2' : 'shadow rounded m-2'}>
-      <svg data-marpit-svg="" viewBox="0 0 1280 720">
-        <foreignObject width="1280" height="720">
-          <section>
-            {children ?? <></>}
-          </section>
-        </foreignObject>
-      </svg>
-    </section>
-  );
-});
-
+  children?: ReactNode;
+  presentation?: boolean;
+};
 
-const RichSlideSectionNoMemorized = (props: RichSlideSectionProps): JSX.Element => {
+const OriginalRichSlideSection = React.memo(
+  (props: RichSlideSectionProps): JSX.Element => {
+    const { children, presentation } = props;
+
+    return (
+      <section className={presentation ? 'm-2' : 'shadow rounded m-2'}>
+        <svg data-marpit-svg="" viewBox="0 0 1280 720">
+          <foreignObject width="1280" height="720">
+            <section>{children ?? <></>}</section>
+          </foreignObject>
+        </svg>
+      </section>
+    );
+  },
+);
+
+const RichSlideSectionNoMemorized = (
+  props: RichSlideSectionProps,
+): JSX.Element => {
   const { children } = props;
 
-  return (
-    <OriginalRichSlideSection>
-      {children}
-    </OriginalRichSlideSection>
-  );
+  return <OriginalRichSlideSection>{children}</OriginalRichSlideSection>;
 };
-export const RichSlideSection = React.memo(RichSlideSectionNoMemorized) as typeof RichSlideSectionNoMemorized;
-
+export const RichSlideSection = React.memo(
+  RichSlideSectionNoMemorized,
+) as typeof RichSlideSectionNoMemorized;
 
-const PresentationRichSlideSectionNoMemorized = (props: RichSlideSectionProps): JSX.Element => {
+const PresentationRichSlideSectionNoMemorized = (
+  props: RichSlideSectionProps,
+): JSX.Element => {
   const { children } = props;
 
   return (
-    <OriginalRichSlideSection presentation>
-      {children}
-    </OriginalRichSlideSection>
+    <OriginalRichSlideSection presentation>{children}</OriginalRichSlideSection>
   );
 };
-export const PresentationRichSlideSection = React.memo(PresentationRichSlideSectionNoMemorized) as typeof PresentationRichSlideSectionNoMemorized;
+export const PresentationRichSlideSection = React.memo(
+  PresentationRichSlideSectionNoMemorized,
+) as typeof PresentationRichSlideSectionNoMemorized;

+ 13 - 13
packages/presentation/src/client/components/Slides.tsx

@@ -8,24 +8,24 @@ import { MarpSlides } from './MarpSlides';
 import styles from './Slides.module.scss';
 
 export type SlidesProps = {
-  options: PresentationOptions,
-  children?: string,
-  hasMarpFlag?: boolean,
-  presentation?: boolean,
-}
+  options: PresentationOptions;
+  children?: string;
+  hasMarpFlag?: boolean;
+  presentation?: boolean;
+};
 
 export const Slides = (props: SlidesProps): JSX.Element => {
-  const {
-    options, children, hasMarpFlag, presentation,
-  } = props;
+  const { options, children, hasMarpFlag, presentation } = props;
 
   return (
     <div className={`${styles['slides-styles']}`}>
-      {
-        hasMarpFlag
-          ? <MarpSlides presentation={presentation}>{children}</MarpSlides>
-          : <GrowiSlides options={options} presentation={presentation}>{children}</GrowiSlides>
-      }
+      {hasMarpFlag ? (
+        <MarpSlides presentation={presentation}>{children}</MarpSlides>
+      ) : (
+        <GrowiSlides options={options} presentation={presentation}>
+          {children}
+        </GrowiSlides>
+      )}
     </div>
   );
 };

+ 5 - 5
packages/presentation/src/client/consts/index.ts

@@ -2,8 +2,8 @@ import type { Options as ReactMarkdownOptions } from 'react-markdown';
 import type { Options as RevealOptions } from 'reveal.js';
 
 export type PresentationOptions = {
-  rendererOptions: ReactMarkdownOptions,
-  revealOptions?: RevealOptions,
-  isDarkMode?: boolean,
-  disableSeparationByHeader?: boolean,
-}
+  rendererOptions: ReactMarkdownOptions;
+  revealOptions?: RevealOptions;
+  isDarkMode?: boolean;
+  disableSeparationByHeader?: boolean;
+};

+ 11 - 11
packages/presentation/src/client/services/growi-marpit.ts

@@ -8,12 +8,12 @@ export const MARP_CONTAINER_CLASS_NAME = 'marpit';
 // https://github.com/marp-team/marp-vscode/blob/d9af184ed12b65bb28c0f328e250955d548ac1d1/src/plugins/line-number.ts
 const sourceMapIgnoredTypesForElements = ['inline', 'marpit_slide_open'];
 const lineNumber = (md) => {
-
-  const { marpit_slide_containers_open: marpitSlideContainersOpen } = md.renderer.rules;
+  const { marpit_slide_containers_open: marpitSlideContainersOpen } =
+    md.renderer.rules;
 
   // Enable line sync by per slides
   md.renderer.rules.marpit_slide_containers_open = (tks, i, opts, env, slf) => {
-    const slide = tks.slice(i + 1).find(t => t.type === 'marpit_slide_open');
+    const slide = tks.slice(i + 1).find((t) => t.type === 'marpit_slide_open');
 
     if (slide?.map?.length) {
       tks[i].attrJoin('class', 'has-data-line');
@@ -27,8 +27,8 @@ const lineNumber = (md) => {
   md.core.ruler.push('marp_growi_source_map_attr', (state) => {
     for (const token of state.tokens) {
       if (
-        token.map?.length
-        && !sourceMapIgnoredTypesForElements.includes(token.type)
+        token.map?.length &&
+        !sourceMapIgnoredTypesForElements.includes(token.type)
       ) {
         token.attrJoin('class', 'has-data-line');
         token.attrSet('data-line', token.map[0]);
@@ -48,15 +48,15 @@ const marpitOption: MarpOptions = {
 };
 
 const slideMarpitOption = marpitOption;
-slideMarpitOption.slideContainer = (
-  [new Element('section', { class: 'shadow rounded m-2' })]
-);
+slideMarpitOption.slideContainer = [
+  new Element('section', { class: 'shadow rounded m-2' }),
+];
 
 export const slideMarpit = new Marp(slideMarpitOption).use(lineNumber);
 
 const presentationMarpitOption = marpitOption;
-presentationMarpitOption.slideContainer = (
-  [new Element('section', { class: 'm-2' })]
-);
+presentationMarpitOption.slideContainer = [
+  new Element('section', { class: 'm-2' }),
+];
 
 export const presentationMarpit = new Marp(presentationMarpitOption);

+ 26 - 29
packages/presentation/src/client/services/renderer/extract-sections.ts

@@ -1,20 +1,21 @@
 import type { Schema as SanitizeOption } from 'hast-util-sanitize';
 import type { Plugin } from 'unified';
-import type { Parent, Node } from 'unist';
+import type { Node, Parent } from 'unist';
 import { findAfter } from 'unist-util-find-after';
 import { visit } from 'unist-util-visit';
 
-
-function wrapWithSection(parentNode: Parent, startElem: Node, endElem?: Node | null, isDarkMode?: boolean): void {
+function wrapWithSection(
+  parentNode: Parent,
+  startElem: Node,
+  endElem?: Node | null,
+  isDarkMode?: boolean,
+): void {
   const siblings = parentNode.children;
 
   const startIndex = siblings.indexOf(startElem);
   const endIndex = endElem != null ? siblings.indexOf(endElem) : undefined;
 
-  const between = siblings.slice(
-    startIndex,
-    endIndex,
-  );
+  const between = siblings.slice(startIndex, endIndex);
 
   const section = {
     type: 'section',
@@ -36,11 +37,13 @@ function removeElement(parentNode: Parent, elem: Node): void {
 }
 
 export type ExtractSectionsPluginParams = {
-  isDarkMode?: boolean,
-  disableSeparationByHeader?: boolean,
-}
+  isDarkMode?: boolean;
+  disableSeparationByHeader?: boolean;
+};
 
-export const remarkPlugin: Plugin<[ExtractSectionsPluginParams]> = (options) => {
+export const remarkPlugin: Plugin<[ExtractSectionsPluginParams]> = (
+  options,
+) => {
   const { isDarkMode, disableSeparationByHeader } = options;
 
   const startCondition = (node: Node) => {
@@ -58,30 +61,24 @@ export const remarkPlugin: Plugin<[ExtractSectionsPluginParams]> = (options) =>
 
   return (tree) => {
     // wrap with <section>
-    visit(
-      tree,
-      startCondition,
-      (node, index, parent: Parent) => {
-        if (parent == null || parent.type !== 'root' || node.type === 'yaml') {
-          return;
-        }
+    visit(tree, startCondition, (node, index, parent: Parent) => {
+      if (parent == null || parent.type !== 'root' || node.type === 'yaml') {
+        return;
+      }
 
-        const startElem = node;
-        const endElem = findAfter(parent, startElem, endCondition);
+      const startElem = node;
+      const endElem = findAfter(parent, startElem, endCondition);
 
-        wrapWithSection(parent, startElem, endElem, isDarkMode);
+      wrapWithSection(parent, startElem, endElem, isDarkMode);
 
-        // remove <hr>
-        if (endElem != null && endElem.type === 'thematicBreak') {
-          removeElement(parent, endElem);
-        }
-      },
-    );
+      // remove <hr>
+      if (endElem != null && endElem.type === 'thematicBreak') {
+        removeElement(parent, endElem);
+      }
+    });
   };
-
 };
 
-
 export const sanitizeOption: SanitizeOption = {
   tagNames: ['section'],
 };

+ 24 - 24
packages/presentation/src/services/use-slides-by-frontmatter.ts

@@ -4,18 +4,17 @@ import type { Parent, Root } from 'mdast';
 import type { Processor } from 'unified';
 
 type ParseResult = {
-  marp: boolean | undefined,
-  slide: boolean | undefined,
-}
+  marp: boolean | undefined;
+  slide: boolean | undefined;
+};
 
 const parseSlideFrontmatter = (frontmatter: string): ParseResult => {
-
   let marp;
   let slide;
 
   const lines = frontmatter.split('\n');
   lines.forEach((line) => {
-    const [key, value] = line.split(':').map(part => part.trim());
+    const [key, value] = line.split(':').map((part) => part.trim());
     if (key === 'marp' && value === 'true') {
       marp = true;
     }
@@ -27,56 +26,58 @@ const parseSlideFrontmatter = (frontmatter: string): ParseResult => {
   return { marp, slide };
 };
 
-
 type ProcessorOpts = {
-  onParsed?: (result: ParseResult) => void,
-  onSkipped?: () => void,
+  onParsed?: (result: ParseResult) => void;
+  onSkipped?: () => void;
 };
 
-const generateFrontmatterProcessor = async(opts?: ProcessorOpts) => {
-
+const generateFrontmatterProcessor = async (opts?: ProcessorOpts) => {
   const remarkFrontmatter = (await import('remark-frontmatter')).default;
   const remarkParse = (await import('remark-parse')).default;
   const remarkStringify = (await import('remark-stringify')).default;
   const unified = (await import('unified')).unified;
 
-  return (unified()
+  return unified()
     .use(remarkParse)
     .use(remarkStringify)
     .use(remarkFrontmatter, ['yaml'])
-    .use(() => ((obj: Parent) => {
+    .use(() => (obj: Parent) => {
       if (obj.children[0]?.type === 'yaml') {
         const result = parseSlideFrontmatter(obj.children[0]?.value);
         opts?.onParsed?.(result);
-      }
-      else {
+      } else {
         opts?.onSkipped?.();
       }
-    })));
+    });
 };
 
 export type UseSlide = {
-  marp?: boolean,
-}
+  marp?: boolean;
+};
 
 /**
  * Frontmatter parser for slide
  * @param markdown Markdwon document
  * @returns An UseSlide instance. If the markdown does not contain neither "marp" or "slide" attribute in frontmatter, it returns undefined.
  */
-export const useSlidesByFrontmatter = (markdown?: string, isEnabledMarp?: boolean): UseSlide | undefined => {
-
-  const [processor, setProcessor] = useState<Processor<Root, undefined, undefined, Root, string>|undefined>();
-  const [parseResult, setParseResult] = useState<UseSlide|undefined>();
+export const useSlidesByFrontmatter = (
+  markdown?: string,
+  isEnabledMarp?: boolean,
+): UseSlide | undefined => {
+  const [processor, setProcessor] = useState<
+    Processor<Root, undefined, undefined, Root, string> | undefined
+  >();
+  const [parseResult, setParseResult] = useState<UseSlide | undefined>();
 
   useEffect(() => {
     if (processor != null) {
       return;
     }
 
-    (async() => {
+    (async () => {
       const p = await generateFrontmatterProcessor({
-        onParsed: result => setParseResult(result.marp || result.slide ? result : undefined),
+        onParsed: (result) =>
+          setParseResult(result.marp || result.slide ? result : undefined),
         onSkipped: () => setParseResult(undefined),
       });
       setProcessor(p);
@@ -94,5 +95,4 @@ export const useSlidesByFrontmatter = (markdown?: string, isEnabledMarp?: boolea
   return parseResult != null
     ? { marp: isEnabledMarp && parseResult?.marp }
     : undefined;
-
 };

+ 2 - 5
packages/presentation/tsconfig.json

@@ -5,13 +5,10 @@
     "jsx": "react-jsx",
 
     "baseUrl": ".",
-    "paths": {
-    },
+    "paths": {},
 
     /* TODO: remove below flags for strict checking */
     "noImplicitAny": false
   },
-  "include": [
-    "src"
-  ]
+  "include": ["src"]
 }