Просмотр исходного кода

Merge branch 'master' into fix/112734-delete-page-modal-label

yuken 3 лет назад
Родитель
Сommit
250ed81644
22 измененных файлов с 93 добавлено и 241 удалено
  1. 6 1
      packages/app/src/components/PageEditor.tsx
  2. 12 3
      packages/app/src/components/PageEditorByHackmd.tsx
  3. 1 1
      packages/app/src/components/PrivateLegacyPages.tsx
  4. 1 1
      packages/app/src/components/SearchPage.tsx
  5. 0 0
      packages/app/src/components/SearchPage/SearchPageBase.module.scss
  6. 3 2
      packages/app/src/components/SearchPage/SearchPageBase.tsx
  7. 2 63
      packages/remark-growi-directive/src/mdast-util-growi-directive/index.js
  8. 0 2
      packages/remark-growi-directive/src/micromark-extension-growi-directive/lib/directive-leaf.js
  9. 0 2
      packages/remark-growi-directive/src/micromark-extension-growi-directive/lib/directive-text.js
  10. 0 79
      packages/remark-growi-directive/src/micromark-extension-growi-directive/lib/factory-attributes.js
  11. 1 25
      packages/remark-growi-directive/src/micromark-extension-growi-directive/lib/html.js
  12. 1 1
      packages/remark-growi-directive/test/fixtures/leaf/input.md
  13. 1 1
      packages/remark-growi-directive/test/fixtures/leaf/output.md
  14. 5 5
      packages/remark-growi-directive/test/fixtures/leaf/tree.json
  15. 1 1
      packages/remark-growi-directive/test/fixtures/text/input.md
  16. 1 1
      packages/remark-growi-directive/test/fixtures/text/output.md
  17. 7 7
      packages/remark-growi-directive/test/fixtures/text/tree.json
  18. 18 8
      packages/remark-growi-directive/test/mdast-util-growi-directive.test.js
  19. 24 30
      packages/remark-growi-directive/test/micromark-extension-growi-directive.test.js
  20. 4 4
      packages/remark-lsx/src/components/Lsx.tsx
  21. 4 3
      packages/remark-lsx/src/server/routes/lsx.js
  22. 1 1
      packages/remark-lsx/src/services/renderer/lsx.ts

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

@@ -31,6 +31,7 @@ import {
 } from '~/stores/editor';
 import { useConflictDiffModal } from '~/stores/modal';
 import { useCurrentPagePath, useSWRxCurrentPage, useSWRxTagsInfo } from '~/stores/page';
+import { usePageTreeTermManager } from '~/stores/page-listing';
 import { useSetRemoteLatestPageData } from '~/stores/remote-latest-page';
 import { usePreviewOptions } from '~/stores/renderer';
 import {
@@ -88,6 +89,7 @@ const PageEditor = React.memo((): JSX.Element => {
   const { data: isUploadableFile } = useIsUploadableFile();
   const { data: isUploadableImage } = useIsUploadableImage();
   const { data: conflictDiffModalStatus, close: closeConflictDiffModal } = useConflictDiffModal();
+  const { advance: advancePt } = usePageTreeTermManager();
 
   const { data: rendererOptions, mutate: mutateRendererOptions } = usePreviewOptions();
   const { mutate: mutateIsEnabledUnsavedWarning } = useIsEnabledUnsavedWarning();
@@ -204,6 +206,9 @@ const PageEditor = React.memo((): JSX.Element => {
         options,
       );
 
+      // to sync revision id with page tree: https://github.com/weseek/growi/pull/7227
+      advancePt();
+
       return page;
     }
     catch (error) {
@@ -221,7 +226,7 @@ const PageEditor = React.memo((): JSX.Element => {
     }
 
   // eslint-disable-next-line max-len
-  }, [currentPathname, optionsToSave, grantData, isSlackEnabled, saveOrUpdate, pageId, currentPagePath, currentRevisionId]);
+  }, [currentPathname, optionsToSave, grantData, isSlackEnabled, saveOrUpdate, pageId, currentPagePath, currentRevisionId, advancePt]);
 
   const saveAndReturnToViewHandler = useCallback(async(opts: {slackChannels: string, overwriteScopesOfDescendants?: boolean}) => {
     if (editorMode !== EditorMode.Editor) {

+ 12 - 3
packages/app/src/components/PageEditorByHackmd.tsx

@@ -25,6 +25,7 @@ import {
   usePageIdOnHackmd, useHasDraftOnHackmd, useRevisionIdHackmdSynced, useIsHackmdDraftUpdatingInRealtime,
 } from '~/stores/hackmd';
 import { useCurrentPagePath, useSWRxCurrentPage, useSWRxTagsInfo } from '~/stores/page';
+import { usePageTreeTermManager } from '~/stores/page-listing';
 import { useRemoteRevisionId } from '~/stores/remote-latest-page';
 import {
   EditorMode,
@@ -63,6 +64,7 @@ export const PageEditorByHackmd = (): JSX.Element => {
   const { data: grantData } = useSelectedGrant();
   const { data: hackmdUri } = useHackmdUri();
   const saveOrUpdate = useSaveOrUpdate();
+  const { advance: advancePt } = usePageTreeTermManager();
 
   const { returnPathForURL } = pathUtils;
 
@@ -127,6 +129,9 @@ export const PageEditorByHackmd = (): JSX.Element => {
       else {
         updateStateAfterSave?.();
         mutateIsHackmdDraftUpdatingInRealtime(false);
+
+        // to sync revision id with page tree: https://github.com/weseek/growi/pull/7227
+        advancePt();
       }
       setIsInitialized(false);
       mutateEditorMode(EditorMode.View);
@@ -136,7 +141,7 @@ export const PageEditorByHackmd = (): JSX.Element => {
       toastError(error.message);
     }
   // eslint-disable-next-line max-len
-  }, [editorMode, currentPathname, revision, revisionIdHackmdSynced, optionsToSave, saveOrUpdate, pageId, currentPagePath, isNotFound, mutateEditorMode, router, updateStateAfterSave, mutateIsHackmdDraftUpdatingInRealtime]);
+  }, [editorMode, currentPathname, revision, revisionIdHackmdSynced, optionsToSave, saveOrUpdate, pageId, currentPagePath, isNotFound, mutateEditorMode, router, updateStateAfterSave, mutateIsHackmdDraftUpdatingInRealtime, advancePt]);
 
   // set handler to save and reload Page
   useEffect(() => {
@@ -258,6 +263,9 @@ export const PageEditorByHackmd = (): JSX.Element => {
       updateStateAfterSave?.();
       mutateTagsInfo();
 
+      // to sync revision id with page tree: https://github.com/weseek/growi/pull/7227
+      advancePt();
+
       mutateIsEnabledUnsavedWarning(false);
 
       logger.debug('success to save');
@@ -268,8 +276,9 @@ export const PageEditorByHackmd = (): JSX.Element => {
       logger.error('failed to save', error);
       toastError(error.message);
     }
-  }, [currentPagePath, currentPathname, pageId, revisionIdHackmdSynced, optionsToSave,
-      saveOrUpdate, mutatePageData, updateStateAfterSave, mutateTagsInfo, mutateIsEnabledUnsavedWarning, t]);
+  }, [
+    currentPagePath, currentPathname, pageId, revisionIdHackmdSynced, optionsToSave,
+    saveOrUpdate, mutatePageData, updateStateAfterSave, mutateTagsInfo, advancePt, mutateIsEnabledUnsavedWarning, t]);
 
   /**
    * onChange event of HackmdEditor handler

+ 1 - 1
packages/app/src/components/PrivateLegacyPages.tsx

@@ -29,7 +29,7 @@ import PaginationWrapper from './PaginationWrapper';
 import { PrivateLegacyPagesMigrationModal } from './PrivateLegacyPagesMigrationModal';
 import { OperateAllControl } from './SearchPage/OperateAllControl';
 import SearchControl from './SearchPage/SearchControl';
-import { IReturnSelectedPageIds, SearchPageBase, usePageDeleteModalForBulkDeletion } from './SearchPage2/SearchPageBase';
+import { IReturnSelectedPageIds, SearchPageBase, usePageDeleteModalForBulkDeletion } from './SearchPage/SearchPageBase';
 
 
 // TODO: replace with "customize:showPageLimitationS"

+ 1 - 1
packages/app/src/components/SearchPage.tsx

@@ -16,7 +16,7 @@ import { NotAvailableForGuest } from './NotAvailableForGuest';
 import PaginationWrapper from './PaginationWrapper';
 import { OperateAllControl } from './SearchPage/OperateAllControl';
 import SearchControl from './SearchPage/SearchControl';
-import { IReturnSelectedPageIds, SearchPageBase, usePageDeleteModalForBulkDeletion } from './SearchPage2/SearchPageBase';
+import { IReturnSelectedPageIds, SearchPageBase, usePageDeleteModalForBulkDeletion } from './SearchPage/SearchPageBase';
 
 
 // TODO: replace with "customize:showPageLimitationS"

+ 0 - 0
packages/app/src/components/SearchPage2/SearchPageBase.module.scss → packages/app/src/components/SearchPage/SearchPageBase.module.scss


+ 3 - 2
packages/app/src/components/SearchPage2/SearchPageBase.tsx → packages/app/src/components/SearchPage/SearchPageBase.tsx

@@ -14,7 +14,8 @@ import { usePageDeleteModal } from '~/stores/modal';
 import { usePageTreeTermManager } from '~/stores/page-listing';
 
 import { ForceHideMenuItems } from '../Common/Dropdown/PageItemControl';
-import { SearchResultList } from '../SearchPage/SearchResultList';
+
+import { SearchResultList } from './SearchResultList';
 
 import styles from './SearchPageBase.module.scss';
 
@@ -41,7 +42,7 @@ type Props = {
 }
 
 const SearchPageBaseSubstance: ForwardRefRenderFunction<ISelectableAll & IReturnSelectedPageIds, Props> = (props:Props, ref) => {
-  const SearchResultContent = dynamic(import('../SearchPage/SearchResultContent').then(mod => mod.SearchResultContent), { ssr: false });
+  const SearchResultContent = dynamic(import('./SearchResultContent').then(mod => mod.SearchResultContent), { ssr: false });
   const {
     pages,
     searchingKeyword,

+ 2 - 63
packages/remark-growi-directive/src/mdast-util-growi-directive/index.js

@@ -40,16 +40,12 @@ export const directiveFromMarkdown = {
   },
   exit: {
     directiveLeaf: exit,
-    directiveLeafAttributeClassValue: exitAttributeClassValue,
-    directiveLeafAttributeIdValue: exitAttributeIdValue,
     directiveLeafAttributeName: exitAttributeName,
     directiveLeafAttributeValue: exitAttributeValue,
     directiveLeafAttributes: exitAttributes,
     directiveLeafName: exitName,
 
     directiveText: exit,
-    directiveTextAttributeClassValue: exitAttributeClassValue,
-    directiveTextAttributeIdValue: exitAttributeIdValue,
     directiveTextAttributeName: exitAttributeName,
     directiveTextAttributeValue: exitAttributeValue,
     directiveTextAttributes: exitAttributes,
@@ -118,22 +114,6 @@ function enterAttributes() {
   this.buffer(); // Capture EOLs
 }
 
-/** @type {FromMarkdownHandle} */
-function exitAttributeIdValue(token) {
-  const list = /** @type {Array.<[string, string]>} */ (
-    this.getData('directiveAttributes')
-  );
-  list.push(['id', parseEntities(this.sliceSerialize(token))]);
-}
-
-/** @type {FromMarkdownHandle} */
-function exitAttributeClassValue(token) {
-  const list = /** @type {Array.<[string, string]>} */ (
-    this.getData('directiveAttributes')
-  );
-  list.push(['class', parseEntities(this.sliceSerialize(token))]);
-}
-
 /** @type {FromMarkdownHandle} */
 function exitAttributeValue(token) {
   const list = /** @type {Array.<[string, string]>} */ (
@@ -165,12 +145,7 @@ function exitAttributes() {
   while (++index < list.length) {
     const attribute = list[index];
 
-    if (attribute[0] === 'class' && cleaned.class) {
-      cleaned.class += ` ${attribute[1]}`;
-    }
-    else {
-      cleaned[attribute[0]] = attribute[1];
-    }
+    cleaned[attribute[0]] = attribute[1];
   }
 
   this.setData('directiveAttributes');
@@ -252,46 +227,10 @@ function attributes(node, context) {
     ) {
       const value = String(attrs[key]);
 
-      if (key === 'id') {
-        id = shortcut.test(value) ? `#${value}` : quoted('id', value);
-      }
-      else if (key === 'class') {
-        const list = value.split(/[\t\n\r ]+/g);
-        /** @type {Array.<string>} */
-        const classesFullList = [];
-        /** @type {Array.<string>} */
-        const classesList = [];
-        let index = -1;
-
-        while (++index < list.length) {
-          (shortcut.test(list[index]) ? classesList : classesFullList).push(
-            list[index],
-          );
-        }
-
-        classesFull = classesFullList.length > 0
-          ? quoted('class', classesFullList.join(' '))
-          : '';
-        classes = classesList.length > 0 ? `.${classesList.join('.')}` : '';
-      }
-      else {
-        values.push(quoted(key, value));
-      }
+      values.push(quoted(key, value));
     }
   }
 
-  if (classesFull) {
-    values.unshift(classesFull);
-  }
-
-  if (classes) {
-    values.unshift(classes);
-  }
-
-  if (id) {
-    values.unshift(id);
-  }
-
   return values.length > 0 ? `(${values.join(' ')})` : '';
 
   /**

+ 0 - 2
packages/remark-growi-directive/src/micromark-extension-growi-directive/lib/directive-leaf.js

@@ -125,8 +125,6 @@ function tokenizeAttributes(effects, ok, nok) {
     'directiveLeafAttributes',
     'directiveLeafAttributesMarker',
     'directiveLeafAttribute',
-    'directiveLeafAttributeId',
-    'directiveLeafAttributeClass',
     'directiveLeafAttributeName',
     'directiveLeafAttributeInitializerMarker',
     'directiveLeafAttributeValueLiteral',

+ 0 - 2
packages/remark-growi-directive/src/micromark-extension-growi-directive/lib/directive-text.js

@@ -96,8 +96,6 @@ function tokenizeAttributes(effects, ok, nok) {
     'directiveTextAttributes',
     'directiveTextAttributesMarker',
     'directiveTextAttribute',
-    'directiveTextAttributeId',
-    'directiveTextAttributeClass',
     'directiveTextAttributeName',
     'directiveTextAttributeInitializerMarker',
     'directiveTextAttributeValueLiteral',

+ 0 - 79
packages/remark-growi-directive/src/micromark-extension-growi-directive/lib/factory-attributes.js

@@ -24,8 +24,6 @@ import { markdownLineEndingOrSpaceOrComma, factoryAttributesDevider } from '../.
  * @param {string} attributesType
  * @param {string} attributesMarkerType
  * @param {string} attributeType
- * @param {string} attributeIdType
- * @param {string} attributeClassType
  * @param {string} attributeNameType
  * @param {string} attributeInitializerType
  * @param {string} attributeValueLiteralType
@@ -42,8 +40,6 @@ export function factoryAttributes(
     attributesType,
     attributesMarkerType,
     attributeType,
-    attributeIdType,
-    attributeClassType,
     attributeNameType,
     attributeInitializerType,
     attributeValueLiteralType,
@@ -71,16 +67,6 @@ export function factoryAttributes(
 
   /** @type {State} */
   function between(code) {
-    if (code === codes.numberSign) {
-      type = attributeIdType;
-      return shortcutStart(code);
-    }
-
-    if (code === codes.dot) {
-      type = attributeClassType;
-      return shortcutStart(code);
-    }
-
     if (disallowEol) {
       if (markdownSpace(code)) {
         return factorySpace(effects, between, types.whitespace)(code);
@@ -110,69 +96,6 @@ export function factoryAttributes(
     return end(code);
   }
 
-  /** @type {State} */
-  function shortcutStart(code) {
-    effects.enter(attributeType);
-    effects.enter(type);
-    effects.enter(`${type}Marker`);
-    effects.consume(code);
-    effects.exit(`${type}Marker`);
-    return shortcutStartAfter;
-  }
-
-  /** @type {State} */
-  function shortcutStartAfter(code) {
-    if (
-      code === codes.eof
-      || code === codes.quotationMark
-      || code === codes.numberSign
-      || code === codes.apostrophe
-      || code === codes.dot
-      || code === codes.lessThan
-      || code === codes.equalsTo
-      || code === codes.greaterThan
-      || code === codes.graveAccent
-      || code === codes.rightParenthesis
-      || code === codes.comma
-    ) {
-      return nok(code);
-    }
-
-    effects.enter(`${type}Value`);
-    effects.consume(code);
-    return shortcut;
-  }
-
-  /** @type {State} */
-  function shortcut(code) {
-    if (
-      code === codes.eof
-      || code === codes.quotationMark
-      || code === codes.apostrophe
-      || code === codes.lessThan
-      || code === codes.equalsTo
-      || code === codes.greaterThan
-      || code === codes.graveAccent
-    ) {
-      return nok(code);
-    }
-
-    if (
-      code === codes.numberSign
-      || code === codes.dot
-      || code === codes.rightParenthesis
-      || markdownLineEndingOrSpaceOrComma(code)
-    ) {
-      effects.exit(`${type}Value`);
-      effects.exit(type);
-      effects.exit(attributeType);
-      return between(code);
-    }
-
-    effects.consume(code);
-    return shortcut;
-  }
-
   /** @type {State} */
   function name(code) {
     if (
@@ -181,9 +104,7 @@ export function factoryAttributes(
         && code !== codes.lineFeed
         && code !== codes.carriageReturnLineFeed
         && code !== codes.quotationMark
-        && code !== codes.numberSign
         && code !== codes.apostrophe
-        && code !== codes.dot
         && code !== codes.lessThan
         && code !== codes.equalsTo
         && code !== codes.greaterThan

+ 1 - 25
packages/remark-growi-directive/src/micromark-extension-growi-directive/lib/html.js

@@ -49,8 +49,6 @@ export function directiveHtml(options = {}) {
     },
     exit: {
       directiveLeaf: exit,
-      directiveLeafAttributeClassValue: exitAttributeClassValue,
-      directiveLeafAttributeIdValue: exitAttributeIdValue,
       directiveLeafAttributeName: exitAttributeName,
       directiveLeafAttributeValue: exitAttributeValue,
       directiveLeafAttributes: exitAttributes,
@@ -58,8 +56,6 @@ export function directiveHtml(options = {}) {
       directiveLeafName: exitName,
 
       directiveText: exit,
-      directiveTextAttributeClassValue: exitAttributeClassValue,
-      directiveTextAttributeIdValue: exitAttributeIdValue,
       directiveTextAttributeName: exitAttributeName,
       directiveTextAttributeValue: exitAttributeValue,
       directiveTextAttributes: exitAttributes,
@@ -105,21 +101,6 @@ export function directiveHtml(options = {}) {
     this.setData('directiveAttributes', []);
   }
 
-  /** @type {_Handle} */
-  function exitAttributeIdValue(token) {
-    /** @type {Attribute[]} */
-    const attributes = this.getData('directiveAttributes');
-    attributes.push(['id', parseEntities(this.sliceSerialize(token))]);
-  }
-
-  /** @type {_Handle} */
-  function exitAttributeClassValue(token) {
-    /** @type {Attribute[]} */
-    const attributes = this.getData('directiveAttributes');
-
-    attributes.push(['class', parseEntities(this.sliceSerialize(token))]);
-  }
-
   /** @type {_Handle} */
   function exitAttributeName(token) {
     // Attribute names in CommonMark are significantly limited, so character
@@ -154,12 +135,7 @@ export function directiveHtml(options = {}) {
     while (++index < attributes.length) {
       attribute = attributes[index];
 
-      if (attribute[0] === 'class' && cleaned.class) {
-        cleaned.class += ` ${attribute[1]}`;
-      }
-      else {
-        cleaned[attribute[0]] = attribute[1];
-      }
+      cleaned[attribute[0]] = attribute[1];
     }
 
     this.resume();

+ 1 - 1
packages/remark-growi-directive/test/fixtures/leaf/input.md

@@ -8,4 +8,4 @@ $a[b](c)
 
 $a[b *c* d **e**]
 
-$a(#b.c.d id=e class="f g" h="i &amp; j k")
+$a(#b.c.d .f.g h="i &amp; j k")

+ 1 - 1
packages/remark-growi-directive/test/fixtures/leaf/output.md

@@ -8,4 +8,4 @@ $a[b](c)
 
 $a[b *c* d **e**]
 
-$a(#e .c.d.f.g h="i & j k")
+$a(#b.c.d .f.g h="i & j k")

+ 5 - 5
packages/remark-growi-directive/test/fixtures/leaf/tree.json

@@ -232,8 +232,8 @@
       "type": "leafGrowiPluginDirective",
       "name": "a",
       "attributes": {
-        "id": "e",
-        "class": "c d f g",
+        "#b.c.d": "",
+        ".f.g": "",
         "h": "i & j k"
       },
       "children": [],
@@ -245,8 +245,8 @@
         },
         "end": {
           "line": 11,
-          "column": 44,
-          "offset": 90
+          "column": 32,
+          "offset": 78
         }
       }
     }
@@ -260,7 +260,7 @@
     "end": {
       "line": 12,
       "column": 1,
-      "offset": 91
+      "offset": 79
     }
   }
 }

+ 1 - 1
packages/remark-growi-directive/test/fixtures/text/input.md

@@ -3,5 +3,5 @@ One $a, two $a[b], three $a(b), four $a[b](c).
 $a[b *c*
 d **e**].
 
-$a(#b.c.d id=e class="f g" h="i &amp; j
+$a(#b.c.d .f.g h="i &amp; j
 k").

+ 1 - 1
packages/remark-growi-directive/test/fixtures/text/output.md

@@ -3,5 +3,5 @@ One $a, two $a[b], three $a(b), four $a[b](c).
 $a[b *c*
 d **e**].
 
-$a(#e .c.d.f.g h="i & j
+$a(#b.c.d .f.g h="i & j
 k").

+ 7 - 7
packages/remark-growi-directive/test/fixtures/text/tree.json

@@ -365,8 +365,8 @@
           "type": "textGrowiPluginDirective",
           "name": "a",
           "attributes": {
-            "id": "e",
-            "class": "c d f g",
+            "#b.c.d": "",
+            ".f.g": "",
             "h": "i & j\nk"
           },
           "children": [],
@@ -379,7 +379,7 @@
             "end": {
               "line": 7,
               "column": 4,
-              "offset": 111
+              "offset": 99
             }
           }
         },
@@ -390,12 +390,12 @@
             "start": {
               "line": 7,
               "column": 4,
-              "offset": 111
+              "offset": 99
             },
             "end": {
               "line": 7,
               "column": 5,
-              "offset": 112
+              "offset": 100
             }
           }
         }
@@ -409,7 +409,7 @@
         "end": {
           "line": 7,
           "column": 5,
-          "offset": 112
+          "offset": 100
         }
       }
     }
@@ -423,7 +423,7 @@
     "end": {
       "line": 8,
       "column": 1,
-      "offset": 113
+      "offset": 101
     }
   }
 }

+ 18 - 8
packages/remark-growi-directive/test/mdast-util-growi-directive.test.js

@@ -119,6 +119,14 @@ test('markdown -> mdast', (t) => {
     'should support content in a label',
   );
 
+  const hoge = removePosition(
+    fromMarkdown('x $a(#b.c.d e=f g="h&amp;i&unknown;j")', {
+      extensions: [directive()],
+      mdastExtensions: [directiveFromMarkdown],
+    }),
+    true,
+  );
+
   t.deepEqual(
     removePosition(
       fromMarkdown('x $a(#b.c.d e=f g="h&amp;i&unknown;j")', {
@@ -138,7 +146,7 @@ test('markdown -> mdast', (t) => {
               type: DirectiveType.Text,
               name: 'a',
               attributes: {
-                id: 'b', class: 'c d', e: 'f', g: 'h&i&unknown;j',
+                '#b.c.d': '', e: 'f', g: 'h&i&unknown;j',
               },
               children: [],
             },
@@ -307,7 +315,7 @@ test('mdast -> markdown', (t) => {
           {
             type: DirectiveType.Text,
             name: 'b',
-            attributes: { class: 'a b\nc', id: 'd', key: 'value' },
+            attributes: { '#d': '', '.a.b.c': '', key: 'value' },
             children: [],
           },
           { type: 'text', value: ' k.' },
@@ -316,7 +324,7 @@ test('mdast -> markdown', (t) => {
       { extensions: [directiveToMarkdown] },
     ),
     'a $b(#d .a.b.c key="value") k.\n',
-    'should serialize a directive (text) w/ `id`, `class` attributes',
+    'should serialize a directive (text) w/ hash, dot notation attributes',
   );
 
   t.deepEqual(
@@ -391,7 +399,7 @@ test('mdast -> markdown', (t) => {
           {
             type: DirectiveType.Text,
             name: 'b',
-            attributes: { class: 'c.d e<f' },
+            attributes: { 'c.d': '', 'e<f': '' },
             children: [],
           },
           { type: 'text', value: ' g.' },
@@ -399,7 +407,7 @@ test('mdast -> markdown', (t) => {
       },
       { extensions: [directiveToMarkdown] },
     ),
-    'a $b(class="c.d e<f") g.\n',
+    'a $b(c.d e<f) g.\n',
     'should not use the `class` shortcut if impossible characters exist',
   );
 
@@ -412,7 +420,9 @@ test('mdast -> markdown', (t) => {
           {
             type: DirectiveType.Text,
             name: 'b',
-            attributes: { class: 'c.d e f<g hij' },
+            attributes: {
+              'c.d': '', e: '', 'f<g': '', hij: '',
+            },
             children: [],
           },
           { type: 'text', value: ' k.' },
@@ -420,7 +430,7 @@ test('mdast -> markdown', (t) => {
       },
       { extensions: [directiveToMarkdown] },
     ),
-    'a $b(.e.hij class="c.d f<g") k.\n',
+    'a $b(c.d e f<g hij) k.\n',
     'should not use the `class` shortcut if impossible characters exist (but should use it for classes that don’t)',
   );
 
@@ -485,7 +495,7 @@ test('mdast -> markdown', (t) => {
       {
         type: DirectiveType.Leaf,
         name: 'a',
-        attributes: { id: 'b', class: 'c d', key: 'e\nf' },
+        attributes: { '#b': '', '.c.d': '', key: 'e\nf' },
         children: [],
       },
       { extensions: [directiveToMarkdown] },

+ 24 - 30
packages/remark-growi-directive/test/micromark-extension-growi-directive.test.js

@@ -231,39 +231,39 @@ test('micromark-extension-directive (syntax)', (t) => {
     );
 
     t.equal(
-      micromark('$a(..b)', options()),
-      '<p>(..b)</p>',
-      'should not support an empty shortcut (`.`)',
+      micromark('a $a(..b)', options()),
+      '<p>a </p>',
+      'should support attrs which starts w/ continuous dots',
     );
 
     t.equal(
-      micromark('$a(.#b)', options()),
-      '<p>(.#b)</p>',
-      'should not support an empty shortcut (`#`)',
+      micromark('a $a(.#b)', options()),
+      '<p>a </p>',
+      'should support attrs which start w/ `#`',
     );
 
     t.equal(
-      micromark('$a(.)', options()),
-      '<p>(.)</p>',
-      'should not support an empty shortcut (`}`)',
+      micromark('a $a(.)', options()),
+      '<p>a </p>',
+      'should support attrs w/ (`.`)',
     );
 
     t.equal(
-      micromark('$a(.a=b)', options()),
-      '<p>(.a=b)</p>',
-      'should not support certain characters in shortcuts (`=`)',
+      micromark('a $a(.a=b)', options()),
+      '<p>a </p>',
+      'should support with the attr `(.a=b)`',
     );
 
     t.equal(
-      micromark('$a(.a"b)', options()),
-      '<p>(.a&quot;b)</p>',
-      'should not support certain characters in shortcuts (`"`)',
+      micromark('a $a(.a"b)', options()),
+      '<p>a </p>',
+      'should support with the attr `(.a"b)`',
     );
 
     t.equal(
-      micromark('$a(.a<b)', options()),
-      '<p>(.a&lt;b)</p>',
-      'should not support certain characters in shortcuts (`<`)',
+      micromark('a $a(.a<b)', options()),
+      '<p>a </p>',
+      'should support with the attr `(.a<b)`',
     );
 
     t.equal(
@@ -1002,26 +1002,20 @@ test('content', (t) => {
 
   t.equal(
     micromark('a $span(#a#b)', options({ '*': h })),
-    '<p>a <span id="b"></span></p>',
-    'should support `id` shortcuts',
+    '<p>a <span #a#b=""></span></p>',
+    'should support attrs which contains `#` (1)',
   );
 
   t.equal(
     micromark('a $span(id=a id="b" #c#d)', options({ '*': h })),
-    '<p>a <span id="d"></span></p>',
-    'should support `id` shortcuts after `id` attributes',
+    '<p>a <span id="b" #c#d=""></span></p>',
+    'should support attrs which contains `#` (2)',
   );
 
   t.equal(
     micromark('a $span(.a.b)', options({ '*': h })),
-    '<p>a <span class="a b"></span></p>',
-    'should support `class` shortcuts',
-  );
-
-  t.equal(
-    micromark('a $span(class=a class="b c" .d.e)', options({ '*': h })),
-    '<p>a <span class="a b c d e"></span></p>',
-    'should support `class` shortcuts after `class` attributes',
+    '<p>a <span .a.b=""></span></p>',
+    'should support attrs with dot notation',
   );
 
   t.test('spec for growi plugin', (t) => {

+ 4 - 4
packages/remark-lsx/src/components/Lsx.tsx

@@ -19,23 +19,23 @@ type Props = {
   sort?: string,
   reverse?: string,
   filter?: string,
+  except?: string,
 
   isImmutable?: boolean,
 };
 
 export const Lsx = React.memo(({
   prefix,
-  num, depth, sort, reverse, filter,
+  num, depth, sort, reverse, filter, except,
   isImmutable,
-  ...props
 }: Props): JSX.Element => {
 
   const lsxContext = useMemo(() => {
     const options = {
-      num, depth, sort, reverse, filter,
+      num, depth, sort, reverse, filter, except,
     };
     return new LsxContext(prefix, options);
-  }, [depth, filter, num, prefix, reverse, sort]);
+  }, [depth, filter, num, prefix, reverse, sort, except]);
 
   const { data, error } = useSWRxNodeTree(lsxContext, isImmutable);
 

+ 4 - 3
packages/remark-lsx/src/server/routes/lsx.js

@@ -1,6 +1,6 @@
 import createError, { isHttpError } from 'http-errors';
 
-const { pagePathUtils, customTagUtils } = require('@growi/core');
+const { pathUtils, pagePathUtils, customTagUtils } = require('@growi/core');
 
 const { OptionParser } = customTagUtils;
 
@@ -8,6 +8,7 @@ const { OptionParser } = customTagUtils;
 const DEFAULT_PAGES_NUM = 50;
 
 
+const { addTrailingSlash } = pathUtils;
 const { isTopPage } = pagePathUtils;
 
 class Lsx {
@@ -105,10 +106,10 @@ class Lsx {
     let filterPath = '';
     if (optionsFilter.charAt(0) === '^') {
       // move '^' to the first of path
-      filterPath = new RegExp(`^${pagePath}${optionsFilter.slice(1, optionsFilter.length)}`);
+      filterPath = new RegExp(`^${addTrailingSlash(pagePath)}${optionsFilter.slice(1, optionsFilter.length)}`);
     }
     else {
-      filterPath = new RegExp(`^${pagePath}.*${optionsFilter}`);
+      filterPath = new RegExp(`^${addTrailingSlash(pagePath)}.*${optionsFilter}`);
     }
 
     if (isExceptFilter) {

+ 1 - 1
packages/remark-lsx/src/services/renderer/lsx.ts

@@ -8,7 +8,7 @@ import { Plugin } from 'unified';
 import { visit } from 'unist-util-visit';
 
 const NODE_NAME_PATTERN = new RegExp(/ls|lsx/);
-const SUPPORTED_ATTRIBUTES = ['prefix', 'num', 'depth', 'sort', 'reverse', 'filter'];
+const SUPPORTED_ATTRIBUTES = ['prefix', 'num', 'depth', 'sort', 'reverse', 'filter', 'except'];
 
 const { hasHeadingSlash } = pathUtils;