瀏覽代碼

Merge pull request #4094 from weseek/support/import-plugins

Support/import plugins
Yuki Takei 4 年之前
父節點
當前提交
b024700e54
共有 89 個文件被更改,包括 687 次插入560 次删除
  1. 10 1
      package.json
  2. 5 0
      packages/app/.eslintrc.js
  3. 2 0
      packages/app/bin/shrink-emojione-strategy.js
  4. 1 1
      packages/app/config/webpack.dev.dll.js
  5. 1 1
      packages/app/config/webpack.prod.js
  6. 14 12
      packages/app/package.json
  7. 1 1
      packages/app/src/client/hackmd-agent.js
  8. 8 8
      packages/app/src/client/legacy/crowi-presentation.js
  9. 2 2
      packages/app/src/client/legacy/crowi.js
  10. 1 0
      packages/app/src/client/plugin.js
  11. 1 1
      packages/app/src/client/services/AdminUsersContainer.js
  12. 1 1
      packages/app/src/client/services/AppContainer.js
  13. 1 0
      packages/app/src/client/services/PageContainer.js
  14. 15 15
      packages/app/src/components/Admin/App/FileUploadSetting.jsx
  15. 17 17
      packages/app/src/components/Admin/App/MailSetting.jsx
  16. 6 6
      packages/app/src/components/Admin/Common/AdminNavigation.jsx
  17. 1 1
      packages/app/src/components/Admin/Customize/CustomizeHeaderSetting.jsx
  18. 1 3
      packages/app/src/components/Admin/ImportData/GrowiArchive/ImportForm.jsx
  19. 5 5
      packages/app/src/components/Admin/ImportData/GrowiArchiveSection.jsx
  20. 6 6
      packages/app/src/components/Admin/ManageExternalAccount.jsx
  21. 1 1
      packages/app/src/components/Admin/MarkdownSetting/IndentForm.jsx
  22. 24 24
      packages/app/src/components/Admin/Notification/GlobalNotificationList.jsx
  23. 1 1
      packages/app/src/components/Admin/Notification/NotificationSetting.jsx
  24. 1 1
      packages/app/src/components/Admin/Notification/SlackIntegrationNotificationSetting.jsx
  25. 35 30
      packages/app/src/components/Admin/Security/BasicSecuritySettingContents.jsx
  26. 2 2
      packages/app/src/components/Admin/Security/LdapSecuritySettingContents.jsx
  27. 1 1
      packages/app/src/components/Admin/Security/ShareLinkSetting.jsx
  28. 9 9
      packages/app/src/components/Admin/SlackIntegration/BotTypeCard.jsx
  29. 1 1
      packages/app/src/components/Admin/SlackIntegration/CustomBotWithoutProxyConnectionStatus.jsx
  30. 1 1
      packages/app/src/components/Admin/SlackIntegration/OfficialBotSettings.jsx
  31. 5 5
      packages/app/src/components/Admin/SlackIntegration/WithProxyAccordions.jsx
  32. 5 5
      packages/app/src/components/Admin/UserManagement.jsx
  33. 3 3
      packages/app/src/components/Admin/Users/UserInviteModal.jsx
  34. 5 5
      packages/app/src/components/Admin/Users/UserMenu.jsx
  35. 3 3
      packages/app/src/components/BookmarkButton.jsx
  36. 1 1
      packages/app/src/components/Fab.jsx
  37. 6 6
      packages/app/src/components/InstallerForm.jsx
  38. 3 3
      packages/app/src/components/LikeButton.jsx
  39. 12 12
      packages/app/src/components/LoginForm.jsx
  40. 14 14
      packages/app/src/components/Me/ApiSettings.jsx
  41. 7 7
      packages/app/src/components/Me/ExternalAccountLinkedMe.jsx
  42. 4 4
      packages/app/src/components/Me/ImageCropModal.jsx
  43. 1 1
      packages/app/src/components/Me/PasswordSettings.jsx
  44. 1 1
      packages/app/src/components/Navbar/AuthorInfo.jsx
  45. 1 1
      packages/app/src/components/Navbar/GrowiSubNavigationSwitcher.jsx
  46. 4 4
      packages/app/src/components/Navbar/SubNavButtons.jsx
  47. 1 1
      packages/app/src/components/Page/ShareLinkAlert.jsx
  48. 0 1
      packages/app/src/components/PageAttachment.jsx
  49. 1 1
      packages/app/src/components/PageComment/Comment.jsx
  50. 27 27
      packages/app/src/components/PageComment/CommentEditor.jsx
  51. 3 3
      packages/app/src/components/PageComment/CommentPreview.jsx
  52. 5 5
      packages/app/src/components/PageComments.jsx
  53. 1 1
      packages/app/src/components/PageCreateModal.jsx
  54. 3 3
      packages/app/src/components/PageDeleteModal.jsx
  55. 30 30
      packages/app/src/components/PageDuplicateModal.jsx
  56. 1 1
      packages/app/src/components/PageEditor.jsx
  57. 16 16
      packages/app/src/components/PageEditor/Editor.jsx
  58. 1 1
      packages/app/src/components/PageEditor/EditorNavbarBottom.jsx
  59. 9 9
      packages/app/src/components/PageEditor/Preview.jsx
  60. 1 1
      packages/app/src/components/PageEditorByHackmd.jsx
  61. 2 1
      packages/app/src/components/PageEditorByHackmd/HackmdEditor.jsx
  62. 1 1
      packages/app/src/components/PageHistory/PageRevisionTable.jsx
  63. 16 16
      packages/app/src/components/PageRenameModal.jsx
  64. 9 9
      packages/app/src/components/SavePageControls.jsx
  65. 1 1
      packages/app/src/components/StaffCredit/StaffCredit.jsx
  66. 1 1
      packages/app/src/components/StickyStretchableScroller.jsx
  67. 17 17
      packages/app/src/components/TableOfContents.jsx
  68. 1 1
      packages/app/src/components/User/UserPicture.jsx
  69. 11 3
      packages/app/src/server/plugins/plugin-utils.js
  70. 2 1
      packages/app/src/server/routes/apiv3/slack-integration-settings.js
  71. 2 2
      packages/app/src/server/routes/apiv3/slack-integration.js
  72. 1 1
      packages/app/src/server/service/search-delegator/elasticsearch.js
  73. 1 1
      packages/app/src/test/util/path-utils.test.js
  74. 0 1
      packages/app/src/utils/object-utils.ts
  75. 3 3
      packages/app/src/utils/path-utils.ts
  76. 1 0
      packages/plugin-pukiwiki-like-linker/.eslintignore
  77. 50 0
      packages/plugin-pukiwiki-like-linker/README.md
  78. 49 0
      packages/plugin-pukiwiki-like-linker/package.json
  79. 6 0
      packages/plugin-pukiwiki-like-linker/src/client-entry.js
  80. 1 0
      packages/plugin-pukiwiki-like-linker/src/index.js
  81. 10 0
      packages/plugin-pukiwiki-like-linker/src/meta.js
  82. 21 0
      packages/plugin-pukiwiki-like-linker/src/resource/js/util/PreProcessor/PukiwikiLikeLinker.js
  83. 17 0
      packages/plugin-pukiwiki-like-linker/tsconfig.build.json
  84. 13 0
      packages/plugin-pukiwiki-like-linker/tsconfig.json
  85. 3 3
      packages/slack/package.json
  86. 3 3
      packages/slackbot-proxy/package.json
  87. 0 1
      packages/slackbot-proxy/src/controllers/privacy.ts
  88. 0 1
      packages/slackbot-proxy/src/controllers/term.ts
  89. 96 166
      yarn.lock

+ 10 - 1
package.json

@@ -34,7 +34,7 @@
     "build:dev:watch": "npm run build:dev:app:watch",
     "build:dev:watch:poll": "npm run build:dev:app:watch:poll",
     "build:dev": "yarn build:dev:app",
-    "build:prod": "yarn lerna run build --scope @growi/app --scope @growi/slack",
+    "build:prod": "yarn lerna run build --scope @growi/app --scope @growi/slack --scope @growi/plugin-pukiwiki-like-linker",
     "build:slack": "lerna run build --scope @growi/slack",
     "build": "npm run build:dev:watch",
     "build:poll": "npm run build:dev:watch:poll",
@@ -62,6 +62,15 @@
   "dependencies": {
   },
   "devDependencies": {
+    "@typescript-eslint/eslint-plugin": "^4.28.5",
+    "@typescript-eslint/parser": "^4.28.5",
+    "eslint": "^7.31.0",
+    "eslint-config-weseek": "^1.1.0",
+    "eslint-import-resolver-typescript": "^2.4.0",
+    "eslint-plugin-import": "^2.23.4",
+    "eslint-plugin-jest": "^24.3.2",
+    "eslint-plugin-react": "^7.24.0",
+    "eslint-plugin-react-hooks": "^4.2.0",
     "lerna": "^4.0.0"
   },
   "engines": {

+ 5 - 0
packages/app/.eslintrc.js

@@ -18,5 +18,10 @@ module.exports = {
       name: 'axios',
       message: 'Please use src/utils/axios instead.',
     }],
+    // set 'warn' temporarily -- 2021.08.02 Yuki Takei
+    '@typescript-eslint/explicit-module-boundary-types': ['warn'],
+    '@typescript-eslint/no-use-before-define': ['warn'],
+    '@typescript-eslint/no-this-alias': ['warn'],
+    '@typescript-eslint/no-var-requires': ['warn'],
   },
 };

+ 2 - 0
packages/app/bin/shrink-emojione-strategy.js

@@ -3,6 +3,7 @@
  *
  * @author Yuki Takei <yuki@weseek.co.jp>
  */
+/*
 require('module-alias/register');
 
 const fs = require('graceful-fs');
@@ -30,3 +31,4 @@ Object.keys(emojiStrategy).forEach((unicode) => {
 
 // write
 fs.writeFileSync(OUT, JSON.stringify(shrinkedMap));
+*/

+ 1 - 1
packages/app/config/webpack.dev.dll.js

@@ -1,7 +1,7 @@
 /**
  * @author: Yuki Takei <yuki@weseek.co.jp>
  */
- const path = require('path');
+const path = require('path');
 const webpack = require('webpack');
 
 

+ 1 - 1
packages/app/config/webpack.prod.js

@@ -35,7 +35,7 @@ module.exports = require('./webpack.common')({
             loader: 'postcss-loader',
             options: {
               sourceMap: false,
-              plugins: (loader) => {
+              plugins: () => {
                 return [
                   require('autoprefixer')(),
                 ];

+ 14 - 12
packages/app/package.json

@@ -4,16 +4,19 @@
   "license": "MIT",
   "scripts": {
     "build": "env-cmd -f config/env.prod.js webpack --config config/webpack.prod.js --profile --bail",
-    "dev:build": "env-cmd -f config/env.dev.js webpack --config config/webpack.dev.js --progress --watch",
+    "prebuild:plugin": "yarn ts-node bin/generate-plugin-definitions-source.ts",
+    "prebuild:dl-resources": "yarn ts-node bin/download-cdn-resources.ts",
+    "prebuild": "run-p prebuild:*",
+    "dev:client": "env-cmd -f config/env.dev.js webpack --config config/webpack.dev.js --progress --watch",
     "dev:server": "env-cmd -f config/env.dev.js yarn ts-node-dev src/server/app.ts --expose_gc --inspect",
-    "dev": "npm-run-all -p dev:*",
-    "prebuild": "yarn ts-node bin/generate-plugin-definitions-source.ts && yarn ts-node bin/download-cdn-resources.ts",
-    "lint:js:fix": "eslint \"**/*.{js,jsx}\" --fix",
-    "lint:js": "eslint \"**/*.{js,jsx}\"",
+    "dev": "run-p dev:*",
+    "predev:client": "yarn prebuild",
+    "lint:js:fix": "eslint \"**/*.{js,jsx,ts,tsx}\" --fix",
+    "lint:js": "eslint \"**/*.{js,jsx,ts,tsx}\"",
     "lint:styles:fix": "stylelint --fix src/**/*.scss",
     "lint:styles": "stylelint src/**/*.scss",
     "lint:swagger2openapi": "node node_modules/swagger2openapi/oas-validate tmp/swagger.json",
-    "lint": "npm-run-all -p lint:js lint:styles lint:swagger2openapi",
+    "lint": "run-p lint:js lint:styles lint:swagger2openapi",
     "migrate": "yarn migrate:up",
     "migrate:create": "yarn ts-node node_modules/.bin/migrate-mongo create -f config/migrate.js -- ",
     "migrate:status": "yarn ts-node node_modules/.bin/migrate-mongo status -f config/migrate.js",
@@ -31,6 +34,7 @@
     "@browser-bunyan/console-formatted-stream": "^1.6.2",
     "@google-cloud/storage": "^5.8.5",
     "@growi/slack": "^4.3.3-RC",
+    "@growi/plugin-pukiwiki-like-linker": "^4.3.3-RC",
     "@kobalab/socket.io-session": "^1.0.3",
     "@promster/express": "^5.0.1",
     "@promster/server": "^6.0.0",
@@ -72,7 +76,6 @@
     "growi-commons": "^5.0.4",
     "growi-plugin-attachment-refs": "^2.0.2",
     "growi-plugin-lsx": "^4.0.3",
-    "growi-plugin-pukiwiki-like-linker": "^3.1.0",
     "helmet": "^3.13.0",
     "http-errors": "~1.6.2",
     "i18next": "^20.3.2",
@@ -95,7 +98,6 @@
     "nodemailer-ses-transport": "~1.5.0",
     "npm-run-all": "^4.1.2",
     "openid-client": "=2.5.0",
-    "package-installed-version-sync": "^2.1.0",
     "passport": "^0.4.0",
     "passport-github": "^1.1.0",
     "passport-google-oauth20": "^2.0.0",
@@ -153,12 +155,12 @@
     "diff2html": "^3.1.2",
     "eazy-logger": "^3.0.2",
     "eslint": "^7.31.0",
-    "eslint-config-weseek": "^1.0.11",
+    "eslint-config-weseek": "^1.1.0",
     "eslint-import-resolver-typescript": "^2.4.0",
     "eslint-plugin-import": "^2.23.4",
-    "eslint-plugin-jest": "^23.0.3",
-    "eslint-plugin-react": "^7.14.2",
-    "eslint-plugin-react-hooks": "^4.0.4",
+    "eslint-plugin-jest": "^24.3.2",
+    "eslint-plugin-react": "^7.24.0",
+    "eslint-plugin-react-hooks": "^4.2.0",
     "file-loader": "^5.0.2",
     "handsontable": "=6.2.2",
     "hard-source-webpack-plugin": "^0.13.1",

+ 1 - 1
packages/app/src/client/hackmd-agent.js

@@ -142,7 +142,7 @@ function connectToParentWithPenpal() {
 
   console.log('[HackMD] Loading GROWI agent for HackMD...');
 
-  window.addEventListener('load', (event) => {
+  window.addEventListener('load', () => {
     addEventListenersToCodemirror();
   });
 

+ 8 - 8
packages/app/src/client/legacy/crowi-presentation.js

@@ -1,7 +1,7 @@
 const Reveal = require('reveal.js');
 
-require('reveal.js/lib/js/head.min.js');
-require('reveal.js/lib/js/html5shiv.js');
+require('reveal.js/lib/js/head.min');
+require('reveal.js/lib/js/html5shiv');
 
 if (!window) {
   window = {};
@@ -30,19 +30,19 @@ Reveal.initialize({
 });
 
 require.ensure([], () => {
-  require('reveal.js/lib/js/classList.js');
-  require('reveal.js/plugin/zoom-js/zoom.js');
-  require('reveal.js/plugin/notes/notes.js');
-  require('../util/reveal/plugins/growi-renderer.js');
+  require('reveal.js/lib/js/classList');
+  require('reveal.js/plugin/zoom-js/zoom');
+  require('reveal.js/plugin/notes/notes');
+  require('../util/reveal/plugins/growi-renderer');
 
   // fix https://github.com/weseek/crowi-plus/issues/96
   Reveal.slide(0, 0);
   Reveal.sync();
 });
 
-Reveal.addEventListener('ready', (event) => {
+Reveal.addEventListener('ready', () => {
   // event.currentSlide, event.indexh, event.indexv
-  $('.reveal section').each(function(e) {
+  $('.reveal section').each(function() {
     const $self = $(this);
     if ($self.children().length !== 1) {
       $self.addClass('only');

+ 2 - 2
packages/app/src/client/legacy/crowi.js

@@ -154,7 +154,7 @@ Crowi.highlightSelectedSection = function(hash) {
   }
 };
 
-window.addEventListener('load', (e) => {
+window.addEventListener('load', () => {
   const { appContainer } = window;
   const pageContainer = appContainer.getContainer('PageContainer');
 
@@ -181,7 +181,7 @@ window.addEventListener('load', (e) => {
   }
 });
 
-window.addEventListener('load', (e) => {
+window.addEventListener('load', () => {
   const crowi = window.crowi;
   if (crowi && crowi.users && crowi.users.length !== 0) {
     const totalUsers = crowi.users.length;

+ 1 - 0
packages/app/src/client/plugin.js

@@ -12,6 +12,7 @@ export default class GrowiPlugin {
    *
    * @memberof CrowiPlugin
    */
+  // eslint-disable-next-line @typescript-eslint/no-unused-vars
   installAll(appContainer, originRenderer) {
     // import plugin definitions
     let definitions = [];

+ 1 - 1
packages/app/src/client/services/AdminUsersContainer.js

@@ -1,6 +1,6 @@
 import { Container } from 'unstated';
-import loggerFactory from '~/utils/logger';
 import { debounce } from 'throttle-debounce';
+import loggerFactory from '~/utils/logger';
 
 // eslint-disable-next-line no-unused-vars
 const logger = loggerFactory('growi:services:AdminUserGroupDetailContainer');

+ 1 - 1
packages/app/src/client/services/AppContainer.js

@@ -1,8 +1,8 @@
 import { Container } from 'unstated';
 
-import axios from 'axios';
 import urljoin from 'url-join';
 
+import axios from '~/utils/axios';
 import InterceptorManager from '~/services/interceptor-manager';
 
 import emojiStrategy from '../util/emojione/emoji_strategy_shrinked.json';

+ 1 - 0
packages/app/src/client/services/PageContainer.js

@@ -564,6 +564,7 @@ export default class PageContainer extends Container {
   }
 
   addWebSocketEventHandlers() {
+    // eslint-disable-next-line @typescript-eslint/no-this-alias
     const pageContainer = this;
     const socketIoContainer = this.appContainer.getContainer('SocketIoContainer');
     const socket = socketIoContainer.getSocket();

+ 15 - 15
packages/app/src/components/Admin/App/FileUploadSetting.jsx

@@ -49,21 +49,21 @@ function FileUploadSetting(props) {
 
         <div className="col-md-6 py-2">
           {fileUploadTypes.map((type) => {
-              return (
-                <div key={type} className="custom-control custom-radio custom-control-inline">
-                  <input
-                    type="radio"
-                    className="custom-control-input"
-                    name="file-upload-type"
-                    id={`file-upload-type-radio-${type}`}
-                    checked={adminAppContainer.state.fileUploadType === type}
-                    disabled={adminAppContainer.state.isFixedFileUploadByEnvVar}
-                    onChange={() => { adminAppContainer.changeFileUploadType(type) }}
-                  />
-                  <label className="custom-control-label" htmlFor={`file-upload-type-radio-${type}`}>{t(`admin:app_setting.${type}_label`)}</label>
-                </div>
-              );
-            })}
+            return (
+              <div key={type} className="custom-control custom-radio custom-control-inline">
+                <input
+                  type="radio"
+                  className="custom-control-input"
+                  name="file-upload-type"
+                  id={`file-upload-type-radio-${type}`}
+                  checked={adminAppContainer.state.fileUploadType === type}
+                  disabled={adminAppContainer.state.isFixedFileUploadByEnvVar}
+                  onChange={() => { adminAppContainer.changeFileUploadType(type) }}
+                />
+                <label className="custom-control-label" htmlFor={`file-upload-type-radio-${type}`}>{t(`admin:app_setting.${type}_label`)}</label>
+              </div>
+            );
+          })}
         </div>
         {adminAppContainer.state.isFixedFileUploadByEnvVar && (
           <p className="alert alert-warning mt-2 text-left offset-3 col-6">

+ 17 - 17
packages/app/src/components/Admin/App/MailSetting.jsx

@@ -64,22 +64,22 @@ function MailSetting(props) {
         </label>
         <div className="col-md-6 py-2">
           {transmissionMethods.map((method) => {
-              return (
-                <div key={method} className="custom-control custom-radio custom-control-inline">
-                  <input
-                    type="radio"
-                    className="custom-control-input"
-                    name="transmission-method"
-                    id={`transmission-method-radio-${method}`}
-                    checked={adminAppContainer.state.transmissionMethod === method}
-                    onChange={(e) => {
+            return (
+              <div key={method} className="custom-control custom-radio custom-control-inline">
+                <input
+                  type="radio"
+                  className="custom-control-input"
+                  name="transmission-method"
+                  id={`transmission-method-radio-${method}`}
+                  checked={adminAppContainer.state.transmissionMethod === method}
+                  onChange={(e) => {
                     adminAppContainer.changeTransmissionMethod(method);
                   }}
-                  />
-                  <label className="custom-control-label" htmlFor={`transmission-method-radio-${method}`}>{t(`admin:app_setting.${method}_label`)}</label>
-                </div>
-              );
-            })}
+                />
+                <label className="custom-control-label" htmlFor={`transmission-method-radio-${method}`}>{t(`admin:app_setting.${method}_label`)}</label>
+              </div>
+            );
+          })}
         </div>
       </div>
 
@@ -92,9 +92,9 @@ function MailSetting(props) {
             { t('Update') }
           </button>
           {adminAppContainer.state.transmissionMethod === 'smtp' && (
-          <button type="button" className="btn btn-secondary ml-4" onClick={sendTestEmailHandler}>
-            {t('admin:app_setting.send_test_email')}
-          </button>
+            <button type="button" className="btn btn-secondary ml-4" onClick={sendTestEmailHandler}>
+              {t('admin:app_setting.send_test_email')}
+            </button>
           )}
         </div>
       </div>

+ 6 - 6
packages/app/src/components/Admin/Common/AdminNavigation.jsx

@@ -84,12 +84,12 @@ const AdminNavigation = (props) => {
         <MenuLink menu="search"       isListGroupItems isActive={isActiveMenu('/search')} />
         {growiCloudUri != null && growiAppIdForGrowiCloud != null
           && (
-          <a
-            href={`${growiCloudUri}/my/apps/${growiAppIdForGrowiCloud}`}
-            className="list-group-item list-group-item-action border-0 round-corner"
-          >
-            <MenuLabel menu="cloud" />
-          </a>
+            <a
+              href={`${growiCloudUri}/my/apps/${growiAppIdForGrowiCloud}`}
+              className="list-group-item list-group-item-action border-0 round-corner"
+            >
+              <MenuLabel menu="cloud" />
+            </a>
           )
         }
       </>

+ 1 - 1
packages/app/src/components/Admin/Customize/CustomizeHeaderSetting.jsx

@@ -54,7 +54,7 @@ class CustomizeHeaderSetting extends React.Component {
               <pre className="hljs">
                 {/* eslint-disable-next-line react/no-unescaped-entities */}
                 <code className="text-wrap">&lt;script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@9.13.0/build/languages/yaml.min.js"
-                defer&gt;&lt;/script&gt;
+                  defer&gt;&lt;/script&gt;
                 </code>
               </pre>
             </div>

+ 1 - 3
packages/app/src/components/Admin/ImportData/GrowiArchive/ImportForm.jsx

@@ -385,13 +385,11 @@ class ImportForm extends React.Component {
                 insertedCount={collectionProgress ? collectionProgress.insertedCount : 0}
                 modifiedCount={collectionProgress ? collectionProgress.modifiedCount : 0}
                 errorsCount={errors ? errors.length : 0}
-
                 collectionName={collectionName}
                 isSelected={selectedCollections.has(collectionName)}
                 option={optionsMap[collectionName]}
-
                 isConfigButtonAvailable={isConfigButtonAvailable}
-
+                // events
                 onChange={this.toggleCheckbox}
                 onOptionChange={this.updateOption}
                 onConfigButtonClicked={this.openConfigurationModal}

+ 5 - 5
packages/app/src/components/Admin/ImportData/GrowiArchiveSection.jsx

@@ -135,11 +135,11 @@ class GrowiArchiveSection extends React.Component {
             />
           </div>
         )
-        : (
-          <UploadForm
-            onUpload={this.handleUpload}
-            onVersionMismatch={this.handleMismatchedVersions}
-          />
+          : (
+            <UploadForm
+              onUpload={this.handleUpload}
+              onVersionMismatch={this.handleMismatchedVersions}
+            />
           )}
       </Fragment>
     );

+ 6 - 6
packages/app/src/components/Admin/ManageExternalAccount.jsx

@@ -63,12 +63,12 @@ class ManageExternalAccount extends React.Component {
             <ExternalAccountTable />
             {pager}
           </>
-         )
-         : (
-           <>
-             {t('admin:user_management.external_account_none')}
-           </>
-)}
+        )
+          : (
+            <>
+              {t('admin:user_management.external_account_none')}
+            </>
+          )}
 
       </Fragment>
     );

+ 1 - 1
packages/app/src/components/Admin/MarkdownSetting/IndentForm.jsx

@@ -3,10 +3,10 @@ import React from 'react';
 
 import PropTypes from 'prop-types';
 import { withTranslation } from 'react-i18next';
-import loggerFactory from '~/utils/logger';
 import {
   UncontrolledDropdown, DropdownToggle, DropdownMenu, DropdownItem,
 } from 'reactstrap';
+import loggerFactory from '~/utils/logger';
 
 import { withUnstatedContainers } from '../../UnstatedUtils';
 import { toastSuccess, toastError } from '~/client/util/apiNotification';

+ 24 - 24
packages/app/src/components/Admin/Notification/GlobalNotificationList.jsx

@@ -95,35 +95,35 @@ class GlobalNotificationList extends React.Component {
               <td>
                 <ul className="list-inline mb-0">
                   {notification.triggerEvents.includes('pageCreate') && (
-                  <li className="list-inline-item badge badge-pill badge-success">
-                    <i className="icon-doc"></i> CREATE
-                  </li>
-                )}
+                    <li className="list-inline-item badge badge-pill badge-success">
+                      <i className="icon-doc"></i> CREATE
+                    </li>
+                  )}
                   {notification.triggerEvents.includes('pageEdit') && (
-                  <li className="list-inline-item badge badge-pill badge-warning">
-                    <i className="icon-pencil"></i> EDIT
-                  </li>
-                )}
+                    <li className="list-inline-item badge badge-pill badge-warning">
+                      <i className="icon-pencil"></i> EDIT
+                    </li>
+                  )}
                   {notification.triggerEvents.includes('pageMove') && (
-                  <li className="list-inline-item badge badge-pill badge-pink">
-                    <i className="icon-action-redo"></i> MOVE
-                  </li>
-                )}
+                    <li className="list-inline-item badge badge-pill badge-pink">
+                      <i className="icon-action-redo"></i> MOVE
+                    </li>
+                  )}
                   {notification.triggerEvents.includes('pageDelete') && (
-                  <li className="list-inline-item badge badge-pill badge-danger">
-                    <i className="icon-fire"></i> DELETE
-                  </li>
-                )}
+                    <li className="list-inline-item badge badge-pill badge-danger">
+                      <i className="icon-fire"></i> DELETE
+                    </li>
+                  )}
                   {notification.triggerEvents.includes('pageLike') && (
-                  <li className="list-inline-item badge badge-pill badge-info">
-                    <i className="icon-like"></i> LIKE
-                  </li>
-                )}
+                    <li className="list-inline-item badge badge-pill badge-info">
+                      <i className="icon-like"></i> LIKE
+                    </li>
+                  )}
                   {notification.triggerEvents.includes('comment') && (
-                  <li className="list-inline-item badge badge-pill badge-secondary">
-                    <i className="icon-fw icon-bubble"></i> POST
-                  </li>
-                )}
+                    <li className="list-inline-item badge badge-pill badge-secondary">
+                      <i className="icon-fw icon-bubble"></i> POST
+                    </li>
+                  )}
                 </ul>
               </td>
               <td>

+ 1 - 1
packages/app/src/components/Admin/Notification/NotificationSetting.jsx

@@ -1,9 +1,9 @@
 import React, { useMemo, useState } from 'react';
 import PropTypes from 'prop-types';
 
+import { TabContent, TabPane } from 'reactstrap';
 import loggerFactory from '~/utils/logger';
 
-import { TabContent, TabPane } from 'reactstrap';
 import { withUnstatedContainers } from '../../UnstatedUtils';
 import { toastError } from '~/client/util/apiNotification';
 import { toArrayIfNot } from '~/utils/array-utils';

+ 1 - 1
packages/app/src/components/Admin/Notification/SlackIntegrationNotificationSetting.jsx

@@ -1,9 +1,9 @@
 import React, { useMemo, useState } from 'react';
 import PropTypes from 'prop-types';
 
+import { TabContent, TabPane } from 'reactstrap';
 import loggerFactory from '~/utils/logger';
 
-import { TabContent, TabPane } from 'reactstrap';
 import { withUnstatedContainers } from '../../UnstatedUtils';
 import { toastError } from '~/client/util/apiNotification';
 import { toArrayIfNot } from '~/utils/array-utils';

+ 35 - 30
packages/app/src/components/Admin/Security/BasicSecuritySettingContents.jsx

@@ -42,9 +42,9 @@ class BasicSecurityManagementContents extends React.Component {
         </h2>
 
         {adminBasicSecurityContainer.state.retrieveError != null && (
-        <div className="alert alert-danger">
-          <p>{t('Error occurred')} : {adminBasicSecurityContainer.state.retrieveError}</p>
-        </div>
+          <div className="alert alert-danger">
+            <p>{t('Error occurred')} : {adminBasicSecurityContainer.state.retrieveError}</p>
+          </div>
         )}
 
         <div className="form-group row">
@@ -73,38 +73,43 @@ class BasicSecurityManagementContents extends React.Component {
         </div>
 
         {isBasicEnabled && (
-        <React.Fragment>
-          <div className="row mb-5">
-            <div className="offset-md-3 col-md-6">
-              <div className="custom-control custom-checkbox custom-checkbox-success">
-                <input
-                  id="bindByEmail-basic"
-                  className="custom-control-input"
-                  type="checkbox"
-                  checked={adminBasicSecurityContainer.state.isSameUsernameTreatedAsIdenticalUser || false}
-                  onChange={() => { adminBasicSecurityContainer.switchIsSameUsernameTreatedAsIdenticalUser() }}
-                />
-                <label
-                  className="custom-control-label"
-                  htmlFor="bindByEmail-basic"
-                  dangerouslySetInnerHTML={{ __html: t('security_setting.Treat username matching as identical', 'username') }}
-                />
+          <React.Fragment>
+            <div className="row mb-5">
+              <div className="offset-md-3 col-md-6">
+                <div className="custom-control custom-checkbox custom-checkbox-success">
+                  <input
+                    id="bindByEmail-basic"
+                    className="custom-control-input"
+                    type="checkbox"
+                    checked={adminBasicSecurityContainer.state.isSameUsernameTreatedAsIdenticalUser || false}
+                    onChange={() => { adminBasicSecurityContainer.switchIsSameUsernameTreatedAsIdenticalUser() }}
+                  />
+                  <label
+                    className="custom-control-label"
+                    htmlFor="bindByEmail-basic"
+                    dangerouslySetInnerHTML={{ __html: t('security_setting.Treat username matching as identical', 'username') }}
+                  />
+                </div>
+                <p className="form-text text-muted">
+                  <small dangerouslySetInnerHTML={{ __html: t('security_setting.Treat username matching as identical_warn', 'username') }} />
+                </p>
               </div>
-              <p className="form-text text-muted">
-                <small dangerouslySetInnerHTML={{ __html: t('security_setting.Treat username matching as identical_warn', 'username') }} />
-              </p>
             </div>
-          </div>
 
-          <div className="row my-3">
-            <div className="offset-4 col-5">
-              <button type="button" className="btn btn-primary" disabled={adminBasicSecurityContainer.state.retrieveError != null} onClick={this.onClickSubmit}>
-                {t('Update')}
-              </button>
+            <div className="row my-3">
+              <div className="offset-4 col-5">
+                <button
+                  type="button"
+                  className="btn btn-primary"
+                  disabled={adminBasicSecurityContainer.state.retrieveError != null}
+                  onClick={this.onClickSubmit}
+                >
+                  {t('Update')}
+                </button>
+              </div>
             </div>
-          </div>
 
-        </React.Fragment>
+          </React.Fragment>
         )}
 
       </React.Fragment>

+ 2 - 2
packages/app/src/components/Admin/Security/LdapSecuritySettingContents.jsx

@@ -120,8 +120,8 @@ class LdapSecuritySettingContents extends React.Component {
                     aria-expanded="true"
                   >
                     {adminLdapSecurityContainer.state.isUserBind
-                        ? <span className="pull-left">{t('security_setting.ldap.bind_user')}</span>
-                        : <span className="pull-left">{t('security_setting.ldap.bind_manager')}</span>}
+                      ? <span className="pull-left">{t('security_setting.ldap.bind_user')}</span>
+                      : <span className="pull-left">{t('security_setting.ldap.bind_manager')}</span>}
                   </button>
                   <div className="dropdown-menu" aria-labelledby="dropdownMenuButton">
                     <button className="dropdown-item" type="button" onClick={() => { adminLdapSecurityContainer.changeLdapBindMode(true) }}>

+ 1 - 1
packages/app/src/components/Admin/Security/ShareLinkSetting.jsx

@@ -172,7 +172,7 @@ class ShareLinkSetting extends React.Component {
             onClickDeleteButton={this.deleteLinkById}
             isAdmin
           />
-          )
+        )
           : (<p className="text-center">{t('share_links.No_share_links')}</p>
           )
         }

+ 9 - 9
packages/app/src/components/Admin/SlackIntegration/BotTypeCard.jsx

@@ -50,15 +50,15 @@ const BotTypeCard = (props) => {
 
           {/*  A recommended badge is shown on official bot card, supplementary names are shown on Custom bot cards   */}
           {props.botType === 'officialBot'
-          ? (
-            <span className="badge badge-info mr-2">
-              {t('admin:slack_integration.selecting_bot_types.recommended')}
-            </span>
-          ) : (
-            <span className="supplementary-bot-name mr-2">
-              {t(`admin:slack_integration.selecting_bot_types.${botDetails[props.botType].supplementaryBotName}`)}
-            </span>
-          )}
+            ? (
+              <span className="badge badge-info mr-2">
+                {t('admin:slack_integration.selecting_bot_types.recommended')}
+              </span>
+            ) : (
+              <span className="supplementary-bot-name mr-2">
+                {t(`admin:slack_integration.selecting_bot_types.${botDetails[props.botType].supplementaryBotName}`)}
+              </span>
+            )}
 
           <i className={props.isActive ? 'grw-botcard-title-active' : ''} aria-hidden="true"></i>
         </h3>

+ 1 - 1
packages/app/src/components/Admin/SlackIntegration/CustomBotWithoutProxyConnectionStatus.jsx

@@ -29,7 +29,7 @@ const CustomBotWithoutProxyConnectionStatus = (props) => {
                 <img width={20} height={20} src="/images/slack-integration/growi-bot-kun-icon.png" />
               </div>
             </div>
-         ) : ''}
+          ) : ''}
         </div>
       </div>
 

+ 1 - 1
packages/app/src/components/Admin/SlackIntegration/OfficialBotSettings.jsx

@@ -1,7 +1,7 @@
 import React, { useState, useEffect } from 'react';
 import PropTypes from 'prop-types';
-import loggerFactory from '~/utils/logger';
 import { useTranslation } from 'react-i18next';
+import loggerFactory from '~/utils/logger';
 import AppContainer from '~/client/services/AppContainer';
 import { withUnstatedContainers } from '../../UnstatedUtils';
 import { toastSuccess, toastError } from '~/client/util/apiNotification';

+ 5 - 5
packages/app/src/components/Admin/SlackIntegration/WithProxyAccordions.jsx

@@ -163,16 +163,16 @@ const GeneratingTokensAndRegisteringProxyServiceProcess = withUnstatedContainers
           <li>
             <p
               className="ml-2"
-                // eslint-disable-next-line react/no-danger
+              // eslint-disable-next-line react/no-danger
               dangerouslySetInnerHTML={{ __html: t('admin:slack_integration.accordion.enter_growi_register_on_slack') }}
             />
           </li>
           <li>
             <p
               className="ml-2"
-                // TODO: Add dynamic link
-                // TODO: Add logo
-                // eslint-disable-next-line react/no-danger
+              // TODO: Add dynamic link
+              // TODO: Add logo
+              // eslint-disable-next-line react/no-danger
               dangerouslySetInnerHTML={{ __html: t('admin:slack_integration.accordion.paste_growi_url') }}
             />
             <div className="input-group align-items-center pl-2 mb-3">
@@ -370,7 +370,7 @@ const WithProxyAccordions = (props) => {
                 {t(`admin:slack_integration.accordion.${value.title}`)}
                 {value.title === 'test_connection' && isLatestConnectionSuccess && <i className="ml-3 text-success fa fa-check"></i>}
               </>
-)}
+            )}
             key={key}
           >
             {value.content}

+ 5 - 5
packages/app/src/components/Admin/UserManagement.jsx

@@ -144,11 +144,11 @@ class UserManagement extends React.Component {
       <Fragment>
         {adminUsersContainer.state.userForPasswordResetModal != null
         && (
-        <PasswordResetModal
-          isOpen={adminUsersContainer.state.isPasswordResetModalShown}
-          onClose={adminUsersContainer.hidePasswordResetModal}
-          userForPasswordResetModal={adminUsersContainer.state.userForPasswordResetModal}
-        />
+          <PasswordResetModal
+            isOpen={adminUsersContainer.state.isPasswordResetModalShown}
+            onClose={adminUsersContainer.hidePasswordResetModal}
+            userForPasswordResetModal={adminUsersContainer.state.userForPasswordResetModal}
+          />
         )}
         <p>
           <InviteUserControl />

+ 3 - 3
packages/app/src/components/Admin/Users/UserInviteModal.jsx

@@ -176,7 +176,7 @@ class UserInviteModal extends React.Component {
             <div className="my-1" key={user.email}>
               <CopyToClipboard text={copyText} onCopy={this.showToaster}>
                 <li className="btn btn-outline-secondary">
-                Email: <strong className="mr-3">{user.email}</strong> Password: <strong>{user.password}</strong>
+                  Email: <strong className="mr-3">{user.email}</strong> Password: <strong>{user.password}</strong>
                 </li>
               </CopyToClipboard>
             </div>
@@ -269,11 +269,11 @@ class UserInviteModal extends React.Component {
         </ModalHeader>
         <ModalBody>
           {invitedEmailList == null ? this.renderModalBody()
-           : this.renderCreatedModalBody()}
+            : this.renderCreatedModalBody()}
         </ModalBody>
         <ModalFooter className="d-flex">
           {invitedEmailList == null ? this.renderModalFooter()
-           : this.renderCreatedModalFooter()}
+            : this.renderCreatedModalFooter()}
         </ModalFooter>
       </Modal>
     );

+ 5 - 5
packages/app/src/components/Admin/Users/UserMenu.jsx

@@ -65,11 +65,11 @@ class UserMenu extends React.Component {
           {(user.status === 1 || user.status === 3) && <StatusActivateButton user={user} />}
           {user.status === 2 && <StatusSuspendedButton user={user} />}
           {user.status === 5 && (
-          <SendInvitationEmailButton
-            user={user}
-            isInvitationEmailSended={isInvitationEmailSended}
-            onSuccessfullySentInvitationEmail={this.onSuccessfullySentInvitationEmail}
-          />
+            <SendInvitationEmailButton
+              user={user}
+              isInvitationEmailSended={isInvitationEmailSended}
+              onSuccessfullySentInvitationEmail={this.onSuccessfullySentInvitationEmail}
+            />
           )}
           {(user.status === 1 || user.status === 3 || user.status === 5) && <UserRemoveButton user={user} />}
         </li>

+ 3 - 3
packages/app/src/components/BookmarkButton.jsx

@@ -54,9 +54,9 @@ class BookmarkButton extends React.Component {
         </button>
 
         {isGuestUser && (
-        <UncontrolledTooltip placement="top" target="bookmark-button" fade={false}>
-          {t('Not available for guest')}
-        </UncontrolledTooltip>
+          <UncontrolledTooltip placement="top" target="bookmark-button" fade={false}>
+            {t('Not available for guest')}
+          </UncontrolledTooltip>
         )}
       </div>
     );

+ 1 - 1
packages/app/src/components/Fab.jsx

@@ -1,8 +1,8 @@
 import React, { useState, useCallback, useEffect } from 'react';
 import PropTypes from 'prop-types';
+import StickyEvents from 'sticky-events';
 import loggerFactory from '~/utils/logger';
 
-import StickyEvents from 'sticky-events';
 
 import AppContainer from '~/client/services/AppContainer';
 import NavigationContainer from '~/client/services/NavigationContainer';

+ 6 - 6
packages/app/src/components/InstallerForm.jsx

@@ -99,12 +99,12 @@ class InstallerForm extends React.Component {
                 />
                 <div className="dropdown-menu" aria-labelledby="dropdownLanguage">
                   {
-                  localeMetadatas.map(meta => (
-                    <button key={meta.id} className="dropdown-item" type="button" onClick={() => { this.changeLanguage(meta) }}>
-                      {meta.displayName}
-                    </button>
-                  ))
-                }
+                    localeMetadatas.map(meta => (
+                      <button key={meta.id} className="dropdown-item" type="button" onClick={() => { this.changeLanguage(meta) }}>
+                        {meta.displayName}
+                      </button>
+                    ))
+                  }
                 </div>
               </div>
             </div>

+ 3 - 3
packages/app/src/components/LikeButton.jsx

@@ -54,9 +54,9 @@ class LikeButton extends React.Component {
         </button>
 
         {isGuestUser && (
-        <UncontrolledTooltip placement="top" target="like-button" fade={false}>
-          {t('Not available for guest')}
-        </UncontrolledTooltip>
+          <UncontrolledTooltip placement="top" target="like-button" fade={false}>
+            {t('Not available for guest')}
+          </UncontrolledTooltip>
         )}
       </div>
     );

+ 12 - 12
packages/app/src/components/LoginForm.jsx

@@ -158,11 +158,11 @@ class LoginForm extends React.Component {
     return (
       <React.Fragment>
         {registrationMode === 'Restricted' && (
-        <p className="alert alert-warning">
-          {t('page_register.notice.restricted')}
-          <br />
-          {t('page_register.notice.restricted_defail')}
-        </p>
+          <p className="alert alert-warning">
+            {t('page_register.notice.restricted')}
+            <br />
+            {t('page_register.notice.restricted_defail')}
+          </p>
         )}
         <form role="form" action="/register" method="post" id="register-form">
           <div className="input-group" id="input-group-username">
@@ -196,18 +196,18 @@ class LoginForm extends React.Component {
           </div>
 
           {registrationWhiteList.length > 0 && (
-          <>
-            <p className="form-text">{t('page_register.form_help.email')}</p>
-            <ul>
-              {registrationWhiteList.map((elem) => {
+            <>
+              <p className="form-text">{t('page_register.form_help.email')}</p>
+              <ul>
+                {registrationWhiteList.map((elem) => {
                   return (
                     <li key={elem}>
                       <code>{elem}</code>
                     </li>
                   );
                 })}
-            </ul>
-          </>
+              </ul>
+            </>
           )}
 
           <div className="input-group">
@@ -273,7 +273,7 @@ class LoginForm extends React.Component {
                       </a>
                     </div>
                   </div>
-              )}
+                )}
               </div>
               <div className="back">
                 {isRegistrationEnabled && this.renderRegisterForm()}

+ 14 - 14
packages/app/src/components/Me/ApiSettings.jsx

@@ -44,20 +44,20 @@ class ApiSettings extends React.Component {
           <label htmlFor="apiToken" className="col-md-3 text-md-right">{t('Current API Token')}</label>
           <div className="col-md-6">
             {personalContainer.state.apiToken != null
-            ? (
-              <input
-                className="form-control"
-                type="text"
-                name="apiToken"
-                value={personalContainer.state.apiToken}
-                readOnly
-              />
-            )
-            : (
-              <p>
-                { t('page_me_apitoken.notice.apitoken_issued') }
-              </p>
-            )}
+              ? (
+                <input
+                  className="form-control"
+                  type="text"
+                  name="apiToken"
+                  value={personalContainer.state.apiToken}
+                  readOnly
+                />
+              )
+              : (
+                <p>
+                  { t('page_me_apitoken.notice.apitoken_issued') }
+                </p>
+              )}
           </div>
         </div>
 

+ 7 - 7
packages/app/src/components/Me/ExternalAccountLinkedMe.jsx

@@ -70,7 +70,7 @@ class ExternalAccountLinkedMe extends React.Component {
         <h2 className="border-bottom my-4">
           <button type="button" className="btn btn-outline-secondary btn-sm pull-right" onClick={this.openAssociateModal}>
             <i className="icon-plus" aria-hidden="true" />
-          Add
+            Add
           </button>
           { t('admin:user_management.external_accounts') }
         </h2>
@@ -93,7 +93,7 @@ class ExternalAccountLinkedMe extends React.Component {
                 key={account._id}
                 openDisassociateModal={this.openDisassociateModal}
               />
-                ))}
+            ))}
           </tbody>
         </table>
 
@@ -104,11 +104,11 @@ class ExternalAccountLinkedMe extends React.Component {
 
         {this.state.accountForDisassociate != null
         && (
-        <DisassociateModal
-          isOpen={this.state.isDisassociateModalOpen}
-          onClose={this.closeDisassociateModal}
-          accountForDisassociate={this.state.accountForDisassociate}
-        />
+          <DisassociateModal
+            isOpen={this.state.isDisassociateModalOpen}
+            onClose={this.closeDisassociateModal}
+            accountForDisassociate={this.state.accountForDisassociate}
+          />
         )}
 
       </Fragment>

+ 4 - 4
packages/app/src/components/Me/ImageCropModal.jsx

@@ -1,6 +1,5 @@
 import React from 'react';
 import PropTypes from 'prop-types';
-import loggerFactory from '~/utils/logger';
 import canvasToBlob from 'async-canvas-to-blob';
 
 import {
@@ -11,6 +10,7 @@ import {
 } from 'reactstrap';
 import { withTranslation } from 'react-i18next';
 import ReactCrop from 'react-image-crop';
+import loggerFactory from '~/utils/logger';
 import AppContainer from '~/client/services/AppContainer';
 import { withUnstatedContainers } from '../UnstatedUtils';
 import 'react-image-crop/dist/ReactCrop.css';
@@ -95,13 +95,13 @@ class ImageCropModal extends React.Component {
         </ModalBody>
         <ModalFooter>
           <button type="button" className="btn btn-outline-danger rounded-pill mr-auto" onClick={this.reset}>
-              Reset
+            Reset
           </button>
           <button type="button" className="btn btn-outline-secondary rounded-pill mr-2" onClick={this.props.onModalClose}>
-                  Cancel
+            Cancel
           </button>
           <button type="button" className="btn btn-outline-primary rounded-pill" onClick={this.crop}>
-                  Crop
+            Crop
           </button>
         </ModalFooter>
       </Modal>

+ 1 - 1
packages/app/src/components/Me/PasswordSettings.jsx

@@ -92,7 +92,7 @@ class PasswordSettings extends React.Component {
 
         {(this.state.isPasswordSet)
           ? <h2 className="border-bottom my-4">{t('personal_settings.update_password')}</h2>
-        : <h2 className="border-bottom my-4">{t('personal_settings.set_new_password')}</h2>}
+          : <h2 className="border-bottom my-4">{t('personal_settings.set_new_password')}</h2>}
         {(this.state.isPasswordSet)
         && (
           <div className="row mb-3">

+ 1 - 1
packages/app/src/components/Navbar/AuthorInfo.jsx

@@ -1,7 +1,7 @@
 import React from 'react';
 import PropTypes from 'prop-types';
-import { userPageRoot } from '~/utils/path-utils';
 import { format } from 'date-fns';
+import { userPageRoot } from '~/utils/path-utils';
 
 import UserPicture from '../User/UserPicture';
 

+ 1 - 1
packages/app/src/components/Navbar/GrowiSubNavigationSwitcher.jsx

@@ -1,9 +1,9 @@
 import React, { useState, useEffect, useCallback } from 'react';
 // import PropTypes from 'prop-types';
-import loggerFactory from '~/utils/logger';
 
 import StickyEvents from 'sticky-events';
 import { debounce } from 'throttle-debounce';
+import loggerFactory from '~/utils/logger';
 
 import GrowiSubNavigation from './GrowiSubNavigation';
 

+ 4 - 4
packages/app/src/components/Navbar/SubNavButtons.jsx

@@ -41,10 +41,10 @@ const SubnavButtons = (props) => {
   return (
     <>
       {isViewMode && (
-      <>
-        { pageContainer.isAbleToShowPageReactionButtons && <PageReactionButtons appContainer={appContainer} pageContainer={pageContainer} /> }
-        { pageContainer.isAbleToShowPageManagement && <PageManagement isCompactMode={isCompactMode} /> }
-      </>
+        <>
+          { pageContainer.isAbleToShowPageReactionButtons && <PageReactionButtons appContainer={appContainer} pageContainer={pageContainer} /> }
+          { pageContainer.isAbleToShowPageManagement && <PageManagement isCompactMode={isCompactMode} /> }
+        </>
       )}
     </>
   );

+ 1 - 1
packages/app/src/components/Page/ShareLinkAlert.jsx

@@ -45,7 +45,7 @@ const ShareLinkAlert = (props) => {
       <i className="icon-fw icon-link"></i>
       {(expiredAt === '' ? <span>{t('page_page.notice.no_deadline')}</span>
       // eslint-disable-next-line react/no-danger
-      : <span dangerouslySetInnerHTML={{ __html: t('page_page.notice.expiration', { expiredAt }) }} />
+        : <span dangerouslySetInnerHTML={{ __html: t('page_page.notice.expiration', { expiredAt }) }} />
       )}
     </p>
   );

+ 0 - 1
packages/app/src/components/PageAttachment.jsx

@@ -134,7 +134,6 @@ class PageAttachment extends React.Component {
           isOpen={showModal}
           animation="false"
           toggle={deleteModalClose}
-
           attachmentToDelete={attachmentToDelete}
           inUse={deleteInUse}
           deleting={this.state.deleting}

+ 1 - 1
packages/app/src/components/PageComment/Comment.jsx

@@ -219,7 +219,7 @@ class Comment extends React.PureComponent {
             </div>
           </div>
         )
-      }
+        }
       </React.Fragment>
     );
   }

+ 27 - 27
packages/app/src/components/PageComment/CommentEditor.jsx

@@ -332,39 +332,39 @@ class CommentEditor extends React.Component {
           <div className="d-flex">
             <label className="mr-2">
               {activeTab === 'comment_editor' && (
-              <span className="custom-control custom-checkbox">
-                <input
-                  type="checkbox"
-                  className="custom-control-input"
-                  id="comment-form-is-markdown"
-                  name="isMarkdown"
-                  checked={this.state.isMarkdown}
-                  value="1"
-                  onChange={this.updateStateCheckbox}
-                />
-                <label
-                  className="ml-2 custom-control-label"
-                  htmlFor="comment-form-is-markdown"
-                >
-                  Markdown
-                </label>
-              </span>
-                  ) }
+                <span className="custom-control custom-checkbox">
+                  <input
+                    type="checkbox"
+                    className="custom-control-input"
+                    id="comment-form-is-markdown"
+                    name="isMarkdown"
+                    checked={this.state.isMarkdown}
+                    value="1"
+                    onChange={this.updateStateCheckbox}
+                  />
+                  <label
+                    className="ml-2 custom-control-label"
+                    htmlFor="comment-form-is-markdown"
+                  >
+                    Markdown
+                  </label>
+                </span>
+              ) }
             </label>
             <span className="flex-grow-1" />
             <span className="d-none d-sm-inline">{ this.state.errorMessage && errorMessage }</span>
 
             { this.state.hasSlackConfig
               && (
-              <div className="form-inline align-self-center mr-md-2">
-                <SlackNotification
-                  isSlackEnabled={commentContainer.state.isSlackEnabled}
-                  slackChannels={commentContainer.state.slackChannels}
-                  onEnabledFlagChange={this.onSlackEnabledFlagChange}
-                  onChannelChange={this.onSlackChannelsChange}
-                  id="idForComment"
-                />
-              </div>
+                <div className="form-inline align-self-center mr-md-2">
+                  <SlackNotification
+                    isSlackEnabled={commentContainer.state.isSlackEnabled}
+                    slackChannels={commentContainer.state.slackChannels}
+                    onEnabledFlagChange={this.onSlackEnabledFlagChange}
+                    onChannelChange={this.onSlackChannelsChange}
+                    id="idForComment"
+                  />
+                </div>
               )
             }
             <div className="d-none d-sm-block">

+ 3 - 3
packages/app/src/components/PageComment/CommentPreview.jsx

@@ -13,9 +13,9 @@ export default class CommentPreview extends React.Component {
       <div
         className="page-comment-preview-body"
         ref={(elm) => {
-            this.previewElement = elm;
-            this.props.inputRef(elm);
-          }}
+          this.previewElement = elm;
+          this.props.inputRef(elm);
+        }}
       >
 
         <RevisionBody

+ 5 - 5
packages/app/src/components/PageComments.jsx

@@ -155,11 +155,11 @@ class PageComments extends React.Component {
           growiRenderer={this.growiRenderer}
         />
         {replies.length !== 0 && (
-        <ReplayComments
-          replyList={replies}
-          deleteBtnClicked={this.confirmToDeleteComment}
-          growiRenderer={this.growiRenderer}
-        />
+          <ReplayComments
+            replyList={replies}
+            deleteBtnClicked={this.confirmToDeleteComment}
+            growiRenderer={this.growiRenderer}
+          />
         )}
         { !showEditor && isLoggedIn && (
           <div className="text-right">

+ 1 - 1
packages/app/src/components/PageCreateModal.jsx

@@ -7,10 +7,10 @@ import { Modal, ModalHeader, ModalBody } from 'reactstrap';
 import { withTranslation } from 'react-i18next';
 import { format } from 'date-fns';
 
+import { pathUtils } from 'growi-commons';
 import {
   userPageRoot, isCreatablePage, generateEditorPath,
 } from '~/utils/path-utils';
-import { pathUtils } from 'growi-commons';
 
 import AppContainer from '~/client/services/AppContainer';
 import NavigationContainer from '~/client/services/NavigationContainer';

+ 3 - 3
packages/app/src/components/PageDeleteModal.jsx

@@ -99,9 +99,9 @@ const PageDeleteModal = (props) => {
         </label>
         {!isAbleToDeleteCompletely
         && (
-        <p className="alert alert-warning p-2 my-0">
-          <i className="icon-ban icon-fw"></i>{ t('modal_delete.delete_completely_restriction') }
-        </p>
+          <p className="alert alert-warning p-2 my-0">
+            <i className="icon-ban icon-fw"></i>{ t('modal_delete.delete_completely_restriction') }
+          </p>
         )}
       </div>
     );

+ 30 - 30
packages/app/src/components/PageDuplicateModal.jsx

@@ -136,23 +136,23 @@ const PageDuplicateModal = (props) => {
             </div>
             <div className="flex-fill">
               {isReachable
-              ? (
-                <PagePathAutoComplete
-                  initializedPath={path}
-                  onSubmit={ppacSubmitHandler}
-                  onInputChange={ppacInputChangeHandler}
-                  autoFocus
-                />
-              )
-              : (
-                <input
-                  type="text"
-                  value={pageNameInput}
-                  className="form-control"
-                  onChange={e => inputChangeHandler(e.target.value)}
-                  required
-                />
-              )}
+                ? (
+                  <PagePathAutoComplete
+                    initializedPath={path}
+                    onSubmit={ppacSubmitHandler}
+                    onInputChange={ppacInputChangeHandler}
+                    autoFocus
+                  />
+                )
+                : (
+                  <input
+                    type="text"
+                    value={pageNameInput}
+                    className="form-control"
+                    onChange={e => inputChangeHandler(e.target.value)}
+                    required
+                  />
+                )}
             </div>
           </div>
         </div>
@@ -172,19 +172,19 @@ const PageDuplicateModal = (props) => {
 
           <div>
             {isDuplicateRecursively && existingPaths.length !== 0 && (
-            <div className="custom-control custom-checkbox custom-checkbox-warning">
-              <input
-                className="custom-control-input"
-                name="withoutExistRecursively"
-                id="cbDuplicatewithoutExistRecursively"
-                type="checkbox"
-                checked={isDuplicateRecursivelyWithoutExistPath}
-                onChange={changeIsDuplicateRecursivelyWithoutExistPathHandler}
-              />
-              <label className="custom-control-label" htmlFor="cbDuplicatewithoutExistRecursively">
-                { t('modal_duplicate.label.Duplicate without exist path') }
-              </label>
-            </div>
+              <div className="custom-control custom-checkbox custom-checkbox-warning">
+                <input
+                  className="custom-control-input"
+                  name="withoutExistRecursively"
+                  id="cbDuplicatewithoutExistRecursively"
+                  type="checkbox"
+                  checked={isDuplicateRecursivelyWithoutExistPath}
+                  onChange={changeIsDuplicateRecursivelyWithoutExistPathHandler}
+                />
+                <label className="custom-control-label" htmlFor="cbDuplicatewithoutExistRecursively">
+                  { t('modal_duplicate.label.Duplicate without exist path') }
+                </label>
+              </div>
             )}
           </div>
           <div>

+ 1 - 1
packages/app/src/components/PageEditor.jsx

@@ -1,10 +1,10 @@
 import React from 'react';
 import PropTypes from 'prop-types';
-import loggerFactory from '~/utils/logger';
 import detectIndent from 'detect-indent';
 
 import { throttle, debounce } from 'throttle-debounce';
 import { envUtils } from 'growi-commons';
+import loggerFactory from '~/utils/logger';
 
 import AppContainer from '~/client/services/AppContainer';
 import PageContainer from '~/client/services/PageContainer';

+ 16 - 16
packages/app/src/components/PageEditor/Editor.jsx

@@ -221,10 +221,10 @@ export default class Editor extends AbstractEditor {
       <div className="overlay overlay-dropzone-active">
         {this.state.isUploading
           && (
-          <span className="overlay-content">
-            <div className="speeding-wheel d-inline-block"></div>
-            <span className="sr-only">Uploading...</span>
-          </span>
+            <span className="overlay-content">
+              <div className="speeding-wheel d-inline-block"></div>
+              <span className="sr-only">Uploading...</span>
+            </span>
           )
         }
         {!this.state.isUploading && <span className="overlay-content"></span>}
@@ -338,20 +338,20 @@ export default class Editor extends AbstractEditor {
 
         { this.props.isUploadable
           && (
-          <button
-            type="button"
-            className="btn btn-outline-secondary btn-block btn-open-dropzone"
-            onClick={() => { this.dropzone.open() }}
-          >
-            <i className="icon-paper-clip" aria-hidden="true"></i>&nbsp;
-            Attach files
-            <span className="d-none d-sm-inline">
+            <button
+              type="button"
+              className="btn btn-outline-secondary btn-block btn-open-dropzone"
+              onClick={() => { this.dropzone.open() }}
+            >
+              <i className="icon-paper-clip" aria-hidden="true"></i>&nbsp;
+              Attach files
+              <span className="d-none d-sm-inline">
               &nbsp;by dragging &amp; dropping,&nbsp;
-              <span className="btn-link">selecting them</span>,&nbsp;
-              or pasting from the clipboard.
-            </span>
+                <span className="btn-link">selecting them</span>,&nbsp;
+                or pasting from the clipboard.
+              </span>
 
-          </button>
+            </button>
           )
         }
 

+ 1 - 1
packages/app/src/components/PageEditor/EditorNavbarBottom.jsx

@@ -74,7 +74,7 @@ const EditorNavbarBottom = (props) => {
             />
           </nav>
         </Collapse>
-        )
+      )
       }
       <div className={`navbar navbar-expand border-top px-2 px-md-3 ${additionalClasses.join(' ')}`}>
         <form className="form-inline">

+ 9 - 9
packages/app/src/components/PageEditor/Preview.jsx

@@ -81,16 +81,16 @@ class Preview extends React.PureComponent {
           <div
             className="page-editor-preview-body"
             ref={(elm) => {
-                this.previewElement = elm;
-                if (this.props.inputRef != null) {
-                  this.props.inputRef(elm);
-                }
-              }}
+              this.previewElement = elm;
+              if (this.props.inputRef != null) {
+                this.props.inputRef(elm);
+              }
+            }}
             onScroll={(event) => {
-                if (this.props.onScroll != null) {
-                  this.props.onScroll(event.target.scrollTop);
-                }
-              }}
+              if (this.props.onScroll != null) {
+                this.props.onScroll(event.target.scrollTop);
+              }
+            }}
           >
             <RevisionBody
               {...this.props}

+ 1 - 1
packages/app/src/components/PageEditorByHackmd.jsx

@@ -1,8 +1,8 @@
 import React from 'react';
 import PropTypes from 'prop-types';
+import { withTranslation } from 'react-i18next';
 import loggerFactory from '~/utils/logger';
 
-import { withTranslation } from 'react-i18next';
 
 import AppContainer from '~/client/services/AppContainer';
 import PageContainer from '~/client/services/PageContainer';

+ 2 - 1
packages/app/src/components/PageEditorByHackmd/HackmdEditor.jsx

@@ -1,8 +1,8 @@
 import React from 'react';
 import PropTypes from 'prop-types';
+import connectToChild from 'penpal/lib/connectToChild';
 import loggerFactory from '~/utils/logger';
 
-import connectToChild from 'penpal/lib/connectToChild';
 
 const DEBUG_PENPAL = false;
 
@@ -27,6 +27,7 @@ export default class HackmdEditor extends React.PureComponent {
   }
 
   async initHackmdWithPenpal() {
+    // eslint-disable-next-line @typescript-eslint/no-this-alias
     const _this = this; // for in methods scope
 
     const iframe = document.createElement('iframe');

+ 1 - 1
packages/app/src/components/PageHistory/PageRevisionTable.jsx

@@ -65,7 +65,7 @@ class PageRevisionTable extends React.Component {
                   </button>
                 </div>
               </div>
-           )}
+            )}
           </div>
         </td>
         <td className="col-1">

+ 16 - 16
packages/app/src/components/PageRenameModal.jsx

@@ -175,22 +175,22 @@ const PageRenameModal = (props) => {
             <p className="form-text text-muted mt-0">{ t('modal_rename.help.recursive') }</p>
           </label>
           {existingPaths.length !== 0 && (
-          <div
-            className="custom-control custom-checkbox custom-checkbox-warning"
-            style={{ display: isRenameRecursively ? '' : 'none' }}
-          >
-            <input
-              className="custom-control-input"
-              name="withoutExistRecursively"
-              id="cbRenamewithoutExistRecursively"
-              type="checkbox"
-              checked={isRenameRecursivelyWithoutExistPath}
-              onChange={changeIsRenameRecursivelyWithoutExistPathHandler}
-            />
-            <label className="custom-control-label" htmlFor="cbRenamewithoutExistRecursively">
-              { t('modal_rename.label.Rename without exist path') }
-            </label>
-          </div>
+            <div
+              className="custom-control custom-checkbox custom-checkbox-warning"
+              style={{ display: isRenameRecursively ? '' : 'none' }}
+            >
+              <input
+                className="custom-control-input"
+                name="withoutExistRecursively"
+                id="cbRenamewithoutExistRecursively"
+                type="checkbox"
+                checked={isRenameRecursivelyWithoutExistPath}
+                onChange={changeIsRenameRecursivelyWithoutExistPathHandler}
+              />
+              <label className="custom-control-label" htmlFor="cbRenamewithoutExistRecursively">
+                { t('modal_rename.label.Rename without exist path') }
+              </label>
+            </div>
           )}
           {isRenameRecursively && <ComparePathsTable subordinatedPages={subordinatedPages} newPagePath={pageNameInput} />}
           {isRenameRecursively && existingPaths.length !== 0 && <DuplicatedPathsTable existingPaths={existingPaths} oldPagePath={pageNameInput} />}

+ 9 - 9
packages/app/src/components/SavePageControls.jsx

@@ -76,15 +76,15 @@ class SavePageControls extends React.Component {
 
         {this.isAclEnabled
           && (
-          <div className="mr-2">
-            <GrantSelector
-              disabled={isRootPage}
-              grant={editorContainer.state.grant}
-              grantGroupId={editorContainer.state.grantGroupId}
-              grantGroupName={editorContainer.state.grantGroupName}
-              onUpdateGrant={this.updateGrantHandler}
-            />
-          </div>
+            <div className="mr-2">
+              <GrantSelector
+                disabled={isRootPage}
+                grant={editorContainer.state.grant}
+                grantGroupId={editorContainer.state.grantGroupId}
+                grantGroupName={editorContainer.state.grantGroupName}
+                onUpdateGrant={this.updateGrantHandler}
+              />
+            </div>
           )
         }
 

+ 1 - 1
packages/app/src/components/StaffCredit/StaffCredit.jsx

@@ -1,9 +1,9 @@
 import React from 'react';
 import PropTypes from 'prop-types';
-import loggerFactory from '~/utils/logger';
 import {
   Modal, ModalBody,
 } from 'reactstrap';
+import loggerFactory from '~/utils/logger';
 import AppContainer from '~/client/services/AppContainer';
 import { withUnstatedContainers } from '../UnstatedUtils';
 

+ 1 - 1
packages/app/src/components/StickyStretchableScroller.jsx

@@ -1,9 +1,9 @@
 import React, { useEffect, useCallback } from 'react';
 import PropTypes from 'prop-types';
-import loggerFactory from '~/utils/logger';
 
 import { debounce } from 'throttle-debounce';
 import StickyEvents from 'sticky-events';
+import loggerFactory from '~/utils/logger';
 
 import NavigationContainer from '~/client/services/NavigationContainer';
 import { withUnstatedContainers } from './UnstatedUtils';

+ 17 - 17
packages/app/src/components/TableOfContents.jsx

@@ -1,8 +1,8 @@
 import React, { useCallback, useEffect } from 'react';
 import PropTypes from 'prop-types';
+import { withTranslation } from 'react-i18next';
 import loggerFactory from '~/utils/logger';
 
-import { withTranslation } from 'react-i18next';
 
 import PageContainer from '~/client/services/PageContainer';
 import NavigationContainer from '~/client/services/NavigationContainer';
@@ -60,22 +60,22 @@ const TableOfContents = (props) => {
       calcViewHeightFunc={calcViewHeight}
     >
       { tocHtml !== ''
-      ? (
-        <div
-          id="revision-toc-content"
-          className="revision-toc-content mb-3"
-          // eslint-disable-next-line react/no-danger
-          dangerouslySetInnerHTML={{ __html: tocHtml }}
-        />
-      )
-      : (
-        <div
-          id="revision-toc-content"
-          className="revision-toc-content mb-2"
-        >
-          <span className="text-muted">({t('page_table_of_contents.empty')})</span>
-        </div>
-      ) }
+        ? (
+          <div
+            id="revision-toc-content"
+            className="revision-toc-content mb-3"
+            // eslint-disable-next-line react/no-danger
+            dangerouslySetInnerHTML={{ __html: tocHtml }}
+          />
+        )
+        : (
+          <div
+            id="revision-toc-content"
+            className="revision-toc-content mb-2"
+          >
+            <span className="text-muted">({t('page_table_of_contents.empty')})</span>
+          </div>
+        ) }
 
     </StickyStretchableScroller>
   );

+ 1 - 1
packages/app/src/components/User/UserPicture.jsx

@@ -1,9 +1,9 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 
+import { UncontrolledTooltip } from 'reactstrap';
 import { userPageRoot } from '~/utils/path-utils';
 
-import { UncontrolledTooltip } from 'reactstrap';
 
 const DEFAULT_IMAGE = '/images/icons/user.svg';
 

+ 11 - 3
packages/app/src/server/plugins/plugin-utils.js

@@ -2,7 +2,6 @@ import loggerFactory from '~/utils/logger';
 import { resolveFromRoot } from '~/utils/project-dir-utils';
 
 const fs = require('graceful-fs');
-const packageInstalledVersionSync = require('package-installed-version-sync');
 
 const PluginUtilsV2 = require('./plugin-utils-v2');
 
@@ -73,14 +72,14 @@ class PluginUtils {
     const deps = json.dependencies || {};
 
     const pluginNames = Object.keys(deps).filter((name) => {
-      return /^(crowi|growi)-plugin-/.test(name);
+      return /^@growi\/plugin-/.test(name);
     });
 
     return pluginNames.map((name) => {
       return {
         name,
         requiredVersion: deps[name],
-        installedVersion: packageInstalledVersionSync(name),
+        installedVersion: this.getVersion(name),
       };
     });
   }
@@ -97,6 +96,15 @@ class PluginUtils {
     return plugins.map((plugin) => { return plugin.name });
   }
 
+  getVersion(packageName) {
+    const packagePath = resolveFromRoot(`../../node_modules/${packageName}/package.json`);
+
+    // Read package.json and find version
+    const content = fs.readFileSync(packagePath);
+    const json = JSON.parse(content);
+    return json.version || '';
+  }
+
 }
 
 module.exports = PluginUtils;

+ 2 - 1
packages/app/src/server/routes/apiv3/slack-integration-settings.js

@@ -1,9 +1,10 @@
+import loggerFactory from '~/utils/logger';
+
 const mongoose = require('mongoose');
 const express = require('express');
 const { body, query } = require('express-validator');
 const axios = require('axios');
 const urljoin = require('url-join');
-import loggerFactory from '~/utils/logger';
 
 const { getConnectionStatus, getConnectionStatuses, sendSuccessMessage } = require('@growi/slack');
 

+ 2 - 2
packages/app/src/server/routes/apiv3/slack-integration.js

@@ -1,9 +1,9 @@
+import loggerFactory from '~/utils/logger';
+
 const express = require('express');
 const mongoose = require('mongoose');
 const urljoin = require('url-join');
 
-import loggerFactory from '~/utils/logger';
-
 const { verifySlackRequest, generateWebClient } = require('@growi/slack');
 
 const logger = loggerFactory('growi:routes:apiv3:slack-integration');

+ 1 - 1
packages/app/src/server/service/search-delegator/elasticsearch.js

@@ -266,7 +266,7 @@ class ElasticsearchDelegator {
   }
 
   async createIndex(index) {
-    const body = require('@root/resource/search/mappings.json');
+    const body = require('^/resource/search/mappings.json');
     return this.client.indices.create({ index, body });
   }
 

+ 1 - 1
packages/app/src/test/util/path-utils.test.js

@@ -6,7 +6,7 @@ describe('TopPage Path test', () => {
     const result = isTopPage('/');
     expect(result).toBe(true);
   });
-  test('Path is not match string ', () => {
+  test('Path is not match string', () => {
     const result = isTopPage('/test');
     expect(result).toBe(false);
   });

+ 0 - 1
packages/app/src/utils/object-utils.ts

@@ -1,6 +1,5 @@
 // remove property if value is null
 
-// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
 export const removeNullPropertyFromObject = <T>(object: T): T => {
 
   for (const [key, value] of Object.entries(object)) {

+ 3 - 3
packages/app/src/utils/path-utils.ts

@@ -109,14 +109,14 @@ export const encodeSpaces = (path?:string): string | undefined => {
 
   // Encode SPACE and IDEOGRAPHIC SPACE
   return path.replace(/ /g, '%20').replace(/\u3000/g, '%E3%80%80');
-}
+};
 
 /**
  * Generate editor path
  * @param {string} paths
  * @returns {string}
  */
-export const generateEditorPath=(...paths)=> {
+export const generateEditorPath = (...paths) => {
   const joinedPath = [...paths].join('/');
 
   if (!isCreatablePage(joinedPath)) {
@@ -130,4 +130,4 @@ export const generateEditorPath=(...paths)=> {
   catch (err) {
     throw new Error('Invalid path format');
   }
-}
+};

+ 1 - 0
packages/plugin-pukiwiki-like-linker/.eslintignore

@@ -0,0 +1 @@
+/dist/**

+ 50 - 0
packages/plugin-pukiwiki-like-linker/README.md

@@ -0,0 +1,50 @@
+# growi-plugin-pukiwiki-like-linker
+[GROWI][growi] Plugin to add PukiwikiLikeLinker
+
+Overview
+----------
+
+Add the feature to use `[[alias>./relative/path]]` expression in markdown.
+
+### Replacement examples
+
+When you write at `/Level1/Level2` page:
+
+```html
+<!-- Markdown -->
+[[./Level3]]
+<!-- HTML -->
+<a href="/Level1/Level2/Level3">./Level3</a>
+
+
+<!-- Markdown -->
+[[../AnotherLevel2]]
+<!-- HTML -->
+<a href="/Level1/AnotherLevel2">../AnotherLevel2</a>
+
+
+<!-- Markdown -->
+Level 3 page is [[here>./Level3]]
+<!-- HTML -->
+Level 3 page is <a href="/Level1/Level2/Level3">here</a>
+
+
+<!-- Markdown -->
+[[example.com>https://example.com/]]
+<!-- HTML -->
+<a href="https://example.com/">example.com</a>
+```
+
+Install
+--------
+
+1. install plugin
+
+    ```
+    $ npm install --save growi-plugin-pukiwiki-like-linker
+    ```
+
+1. build client app (see [GROWI][growi])
+
+
+[growi]: https://github.com/weseek/growi

+ 49 - 0
packages/plugin-pukiwiki-like-linker/package.json

@@ -0,0 +1,49 @@
+{
+  "name": "@growi/plugin-pukiwiki-like-linker",
+  "version": "4.3.3-RC",
+  "description": "GROWI plugin to add PukiwikiLikeLinker",
+  "keywords": [
+    "growi",
+    "growi-plugin"
+  ],
+  "main": "src/index.js",
+  "files": [
+    "src"
+  ],
+  "scripts": {
+    "build": "yarn tsc && tsc-alias -p tsconfig.build.json",
+    "tsc": "tsc -p tsconfig.build.json",
+    "tsc:w": "yarn tsc -w",
+    "lint:js:fix": "eslint **/*.{js,jsx} --fix",
+    "lint:js": "eslint **/*.{js,jsx}",
+    "lint:styles:fix": "stylelint --fix src/**/*.scss",
+    "lint:styles": "stylelint src/**/*.scss",
+    "lint": "npm-run-all -p lint:js lint:styles",
+    "test": ""
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/weseek/growi-plugin-pukiwiki-like-linker.git"
+  },
+  "author": "Yuki Takei <yuki@weseek.co.jp>",
+  "dependencies": {
+  },
+  "devDependencies": {
+    "@types/jest": "^26.0.22",
+    "@typescript-eslint/eslint-plugin": "^4.28.5",
+    "@typescript-eslint/parser": "^4.28.5",
+    "browser-bunyan": "^1.6.3",
+    "cross-env": "^7.0.0",
+    "eslint": "^7.31.0",
+    "eslint-config-weseek": "^1.1.0",
+    "eslint-import-resolver-typescript": "^2.4.0",
+    "eslint-plugin-import": "^2.23.4",
+    "eslint-plugin-jest": "^24.3.2",
+    "npm-run-all": "^4.1.2",
+    "stylelint": "^13.2.0",
+    "stylelint-config-recess-order": "^2.0.1",
+    "ts-jest": "^26.5.4",
+    "tsc-alias": "^1.2.9",
+    "typescript": "^4.2.3"
+  }
+}

+ 6 - 0
packages/plugin-pukiwiki-like-linker/src/client-entry.js

@@ -0,0 +1,6 @@
+import PukiwikiLikeLinker from './resource/js/util/PreProcessor/PukiwikiLikeLinker';
+
+export default (appContainer) => {
+  // add preprocessor to head of array
+  appContainer.originRenderer.preProcessors.unshift(new PukiwikiLikeLinker());
+};

+ 1 - 0
packages/plugin-pukiwiki-like-linker/src/index.js

@@ -0,0 +1 @@
+module.exports = require('./meta');

+ 10 - 0
packages/plugin-pukiwiki-like-linker/src/meta.js

@@ -0,0 +1,10 @@
+const path = require('path');
+
+module.exports = {
+  pluginSchemaVersion: 3,
+  serverEntries: [
+  ],
+  clientEntries: [
+    path.join(__dirname, 'client-entry.js'),
+  ],
+};

+ 21 - 0
packages/plugin-pukiwiki-like-linker/src/resource/js/util/PreProcessor/PukiwikiLikeLinker.js

@@ -0,0 +1,21 @@
+const path = require('path');
+
+export default class PukiwikiLikeLinker {
+
+  process(markdown) {
+
+    return markdown
+      // see: https://regex101.com/r/k2dwz3/3
+      .replace(/\[\[(([^(\]\])]+)>)?(.+?)\]\]/g, (all, group1, group2, group3) => {
+        // create url
+        // use 'group3' as is if starts from 'http(s)', otherwise join to 'window.location.pathname'
+        const url = (group3.match(/^(\/|https?:\/\/)/)) ? group3 : path.join(window.location.pathname, group3);
+        // determine alias string
+        // if 'group2' is undefined, use group3
+        const alias = group2 || group3;
+
+        return `<a href="${url}">${alias}</a>`;
+      });
+  }
+
+}

+ 17 - 0
packages/plugin-pukiwiki-like-linker/tsconfig.build.json

@@ -0,0 +1,17 @@
+{
+  "extends": "./tsconfig.json",
+  "compilerOptions": {
+    "rootDir": "./src",
+    "outDir": "dist",
+    "declaration": true,
+    "noResolve": false,
+    "preserveConstEnums": true,
+    "sourceMap": true,
+    "noEmit": false,
+    "inlineSources": true,
+
+    "baseUrl": ".",
+    "paths": {
+    }
+  }
+}

+ 13 - 0
packages/plugin-pukiwiki-like-linker/tsconfig.json

@@ -0,0 +1,13 @@
+{
+  "extends": "../../tsconfig.base.json",
+  "compilerOptions": {
+    "baseUrl": ".",
+    "paths": {
+    }
+  },
+  "exclude": [
+    "node_modules",
+    "dist",
+    "**/*.test.ts"
+  ]
+}

+ 3 - 3
packages/slack/package.json

@@ -26,12 +26,12 @@
   "devDependencies": {
     "@types/express": "^4.17.11",
     "@types/jest": "^26.0.22",
-    "@typescript-eslint/eslint-plugin": "^4.18.0",
-    "@typescript-eslint/parser": "^4.18.0",
+    "@typescript-eslint/eslint-plugin": "^4.28.5",
+    "@typescript-eslint/parser": "^4.28.5",
     "browser-bunyan": "^1.6.3",
     "cross-env": "^7.0.0",
     "eslint": "^7.31.0",
-    "eslint-config-weseek": "^1.0.11",
+    "eslint-config-weseek": "^1.1.0",
     "eslint-import-resolver-typescript": "^2.4.0",
     "eslint-plugin-import": "^2.23.4",
     "eslint-plugin-jest": "^24.3.2",

+ 3 - 3
packages/slackbot-proxy/package.json

@@ -56,12 +56,12 @@
     "@tsed/exceptions": "^6.43.0",
     "@tsed/json-mapper": "^6.43.0",
     "@tsed/schema": "^6.43.0",
-    "@typescript-eslint/eslint-plugin": "^4.18.0",
-    "@typescript-eslint/parser": "^4.18.0",
+    "@typescript-eslint/eslint-plugin": "^4.28.5",
+    "@typescript-eslint/parser": "^4.28.5",
     "bootstrap": "^5.0.2",
     "browser-bunyan": "^1.6.3",
     "eslint": "^7.31.0",
-    "eslint-config-weseek": "^1.0.11",
+    "eslint-config-weseek": "^1.1.0",
     "eslint-import-resolver-typescript": "^2.4.0",
     "eslint-plugin-import": "^2.23.4",
     "morgan": "^1.10.0",

+ 0 - 1
packages/slackbot-proxy/src/controllers/privacy.ts

@@ -12,7 +12,6 @@ export class PrivacyCtrl {
     }
   }
 
-  // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
   getPrivacy(req: Request, res: Response): string|void {
     res.render('privacy.ejs');
   }

+ 0 - 1
packages/slackbot-proxy/src/controllers/term.ts

@@ -12,7 +12,6 @@ export class TermCtrl {
     }
   }
 
-  // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
   getTerm(req: Request, res: Response): string|void {
     res.render('term.ejs');
   }

+ 96 - 166
yarn.lock

@@ -2661,20 +2661,6 @@
   resolved "https://registry.yarnpkg.com/@types/zen-observable/-/zen-observable-0.8.2.tgz#808c9fa7e4517274ed555fa158f2de4b4f468e71"
   integrity sha512-HrCIVMLjE1MOozVoD86622S7aunluLb2PJdPfb3nYiEtohm8mIB/vyv0Fd37AdeMFrTUQXEunw78YloMA3Qilg==
 
-"@typescript-eslint/eslint-plugin@^4.18.0":
-  version "4.21.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.21.0.tgz#3fce2bfa76d95c00ac4f33dff369cb593aab8878"
-  integrity sha512-FPUyCPKZbVGexmbCFI3EQHzCZdy2/5f+jv6k2EDljGdXSRc0cKvbndd2nHZkSLqCNOPk0jB6lGzwIkglXcYVsQ==
-  dependencies:
-    "@typescript-eslint/experimental-utils" "4.21.0"
-    "@typescript-eslint/scope-manager" "4.21.0"
-    debug "^4.1.1"
-    functional-red-black-tree "^1.0.1"
-    lodash "^4.17.15"
-    regexpp "^3.0.0"
-    semver "^7.3.2"
-    tsutils "^3.17.1"
-
 "@typescript-eslint/eslint-plugin@^4.28.5":
   version "4.28.5"
   resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.5.tgz#8197f1473e7da8218c6a37ff308d695707835684"
@@ -2688,18 +2674,6 @@
     semver "^7.3.5"
     tsutils "^3.21.0"
 
-"@typescript-eslint/experimental-utils@4.21.0", "@typescript-eslint/experimental-utils@^4.0.1":
-  version "4.21.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.21.0.tgz#0b0bb7c15d379140a660c003bdbafa71ae9134b6"
-  integrity sha512-cEbgosW/tUFvKmkg3cU7LBoZhvUs+ZPVM9alb25XvR0dal4qHL3SiUqHNrzoWSxaXA9gsifrYrS1xdDV6w/gIA==
-  dependencies:
-    "@types/json-schema" "^7.0.3"
-    "@typescript-eslint/scope-manager" "4.21.0"
-    "@typescript-eslint/types" "4.21.0"
-    "@typescript-eslint/typescript-estree" "4.21.0"
-    eslint-scope "^5.0.0"
-    eslint-utils "^2.0.0"
-
 "@typescript-eslint/experimental-utils@4.28.5":
   version "4.28.5"
   resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.5.tgz#66c28bef115b417cf9d80812a713e0e46bb42a64"
@@ -2712,24 +2686,17 @@
     eslint-scope "^5.1.1"
     eslint-utils "^3.0.0"
 
-"@typescript-eslint/experimental-utils@^2.5.0":
-  version "2.7.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.7.0.tgz#58d790a3884df3041b5a5e08f9e5e6b7c41864b5"
-  integrity sha512-9/L/OJh2a5G2ltgBWJpHRfGnt61AgDeH6rsdg59BH0naQseSwR7abwHq3D5/op0KYD/zFT4LS5gGvWcMmegTEg==
-  dependencies:
-    "@types/json-schema" "^7.0.3"
-    "@typescript-eslint/typescript-estree" "2.7.0"
-    eslint-scope "^5.0.0"
-
-"@typescript-eslint/parser@^4.18.0":
+"@typescript-eslint/experimental-utils@^4.0.1":
   version "4.21.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.21.0.tgz#a227fc2af4001668c3e3f7415d4feee5093894c1"
-  integrity sha512-eyNf7QmE5O/l1smaQgN0Lj2M/1jOuNg2NrBm1dqqQN0sVngTLyw8tdCbih96ixlhbF1oINoN8fDCyEH9SjLeIA==
+  resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.21.0.tgz#0b0bb7c15d379140a660c003bdbafa71ae9134b6"
+  integrity sha512-cEbgosW/tUFvKmkg3cU7LBoZhvUs+ZPVM9alb25XvR0dal4qHL3SiUqHNrzoWSxaXA9gsifrYrS1xdDV6w/gIA==
   dependencies:
+    "@types/json-schema" "^7.0.3"
     "@typescript-eslint/scope-manager" "4.21.0"
     "@typescript-eslint/types" "4.21.0"
     "@typescript-eslint/typescript-estree" "4.21.0"
-    debug "^4.1.1"
+    eslint-scope "^5.0.0"
+    eslint-utils "^2.0.0"
 
 "@typescript-eslint/parser@^4.28.5":
   version "4.28.5"
@@ -2767,18 +2734,6 @@
   resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.28.5.tgz#d33edf8e429f0c0930a7c3d44e9b010354c422e9"
   integrity sha512-MruOu4ZaDOLOhw4f/6iudyks/obuvvZUAHBDSW80Trnc5+ovmViLT2ZMDXhUV66ozcl6z0LJfKs1Usldgi/WCA==
 
-"@typescript-eslint/typescript-estree@2.7.0":
-  version "2.7.0"
-  resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.7.0.tgz#34fd98c77a07b40d04d5b4203eddd3abeab909f4"
-  integrity sha512-vVCE/DY72N4RiJ/2f10PTyYekX2OLaltuSIBqeHYI44GQ940VCYioInIb8jKMrK9u855OEJdFC+HmWAZTnC+Ag==
-  dependencies:
-    debug "^4.1.1"
-    glob "^7.1.4"
-    is-glob "^4.0.1"
-    lodash.unescape "4.0.1"
-    semver "^6.3.0"
-    tsutils "^3.17.1"
-
 "@typescript-eslint/typescript-estree@4.21.0":
   version "4.21.0"
   resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.21.0.tgz#3817bd91857beeaeff90f69f1f112ea58d350b0a"
@@ -2976,11 +2931,6 @@
   resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d"
   integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==
 
-"@yarnpkg/lockfile@^1.1.0":
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31"
-  integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==
-
 JSONStream@^1.0.4, JSONStream@^1.3.5:
   version "1.3.5"
   resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0"
@@ -3446,14 +3396,7 @@ array-ify@^1.0.0:
   resolved "https://registry.yarnpkg.com/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece"
   integrity sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4=
 
-array-includes@^3.0.3:
-  version "3.0.3"
-  resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.0.3.tgz#184b48f62d92d7452bb31b323165c7f8bd02266d"
-  dependencies:
-    define-properties "^1.1.2"
-    es-abstract "^1.7.0"
-
-array-includes@^3.1.3:
+array-includes@^3.1.2, array-includes@^3.1.3:
   version "3.1.3"
   resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.3.tgz#c7f619b382ad2afaf5326cddfdc0afc61af7690a"
   integrity sha512-gcem1KlBU7c9rB+Rq8/3PPKsK2kjqeEBa3bD5kkQo4nYlOHQCJqIJFqBXDEfwaRuYTT4E+FxA9xez7Gf/e3Q7A==
@@ -3508,6 +3451,16 @@ array.prototype.flatmap@^1.2.2:
     es-abstract "^1.15.0"
     function-bind "^1.1.1"
 
+array.prototype.flatmap@^1.2.4:
+  version "1.2.4"
+  resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.2.4.tgz#94cfd47cc1556ec0747d97f7c7738c58122004c9"
+  integrity sha512-r9Z0zYoxqHz60vvQbWEdXIEtCwHF0yxaWfno9qzXeNHvfyl3BZqygmGzb84dsubyaXLH4husF+NFgMSdpZhk2Q==
+  dependencies:
+    call-bind "^1.0.0"
+    define-properties "^1.1.3"
+    es-abstract "^1.18.0-next.1"
+    function-bind "^1.1.1"
+
 arraybuffer.slice@~0.0.7:
   version "0.0.7"
   resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675"
@@ -6832,7 +6785,7 @@ error-ex@^1.3.1:
   dependencies:
     is-arrayish "^0.2.1"
 
-es-abstract@^1.11.0, es-abstract@^1.12.0:
+es-abstract@^1.12.0:
   version "1.13.0"
   resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.13.0.tgz#ac86145fdd5099d8dd49558ccba2eaf9b88e24e9"
   dependencies:
@@ -6902,16 +6855,6 @@ es-abstract@^1.5.1, es-abstract@^1.6.1:
     is-callable "^1.1.3"
     is-regex "^1.0.4"
 
-es-abstract@^1.7.0:
-  version "1.10.0"
-  resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.10.0.tgz#1ecb36c197842a00d8ee4c2dfd8646bb97d60864"
-  dependencies:
-    es-to-primitive "^1.1.1"
-    function-bind "^1.1.1"
-    has "^1.0.1"
-    is-callable "^1.1.3"
-    is-regex "^1.0.4"
-
 es-to-primitive@^1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.1.1.tgz#45355248a88979034b6792e19bb81f2b7975dd0d"
@@ -7005,10 +6948,10 @@ eslint-config-airbnb@^17.1.0:
     object.assign "^4.1.0"
     object.entries "^1.0.4"
 
-eslint-config-weseek@^1.0.11:
-  version "1.0.11"
-  resolved "https://registry.yarnpkg.com/eslint-config-weseek/-/eslint-config-weseek-1.0.11.tgz#f2daa61c4f8391551889c91a44fd358e6697374a"
-  integrity sha512-arLqn9IQ69WaswEj/q9lNVowXnXu2bx7AyvV31Z7g1uottEhPVv7E8iKIuVtZXersMIO46qP1gVGbGCmHHIAQw==
+eslint-config-weseek@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/eslint-config-weseek/-/eslint-config-weseek-1.1.0.tgz#ebdd2e9ae6c1c7190cedf0bc7975d8f155d171f4"
+  integrity sha512-h1J0G502+8v56uKdjQtwtrl1k/Tv5v++cWyu07MTyKqkGO/XgYtXfLRKTiVBEVC8RnrbitD04L8lHJ0JSR8mig==
   dependencies:
     eslint-config-airbnb "^17.1.0"
 
@@ -7060,13 +7003,6 @@ eslint-plugin-import@^2.23.4:
     resolve "^1.20.0"
     tsconfig-paths "^3.9.0"
 
-eslint-plugin-jest@^23.0.3:
-  version "23.0.3"
-  resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-23.0.3.tgz#d3f157f7791f97713372c13259ba1dfc436eb4c1"
-  integrity sha512-9cNxr66zeOyz1S9AkQL4/ouilR6QHpYj8vKOQZ60fu9hAt5PJWS4KqWqfr1aqN5NFEZSPjFOla2Azn+KTWiGwg==
-  dependencies:
-    "@typescript-eslint/experimental-utils" "^2.5.0"
-
 eslint-plugin-jest@^24.3.2:
   version "24.3.5"
   resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-24.3.5.tgz#71f0b580f87915695c286c3f0eb88cf23664d044"
@@ -7074,25 +7010,28 @@ eslint-plugin-jest@^24.3.2:
   dependencies:
     "@typescript-eslint/experimental-utils" "^4.0.1"
 
-eslint-plugin-react-hooks@^4.0.4:
-  version "4.0.4"
-  resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.0.4.tgz#aed33b4254a41b045818cacb047b81e6df27fa58"
-  integrity sha512-equAdEIsUETLFNCmmCkiCGq6rkSK5MoJhXFPFYeUebcjKgBmWWcgVOqZyQC8Bv1BwVCnTq9tBxgJFgAJTWoJtA==
+eslint-plugin-react-hooks@^4.2.0:
+  version "4.2.0"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.2.0.tgz#8c229c268d468956334c943bb45fc860280f5556"
+  integrity sha512-623WEiZJqxR7VdxFCKLI6d6LLpwJkGPYKODnkH3D7WpOG5KM8yWueBd8TLsNAetEJNF5iJmolaAKO3F8yzyVBQ==
 
-eslint-plugin-react@^7.14.2:
-  version "7.14.2"
-  resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.14.2.tgz#94c193cc77a899ac0ecbb2766fbef88685b7ecc1"
-  integrity sha512-jZdnKe3ip7FQOdjxks9XPN0pjUKZYq48OggNMd16Sk+8VXx6JOvXmlElxROCgp7tiUsTsze3jd78s/9AFJP2mA==
+eslint-plugin-react@^7.24.0:
+  version "7.24.0"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.24.0.tgz#eadedfa351a6f36b490aa17f4fa9b14e842b9eb4"
+  integrity sha512-KJJIx2SYx7PBx3ONe/mEeMz4YE0Lcr7feJTCMyyKb/341NcjuAgim3Acgan89GfPv7nxXK2+0slu0CWXYM4x+Q==
   dependencies:
-    array-includes "^3.0.3"
+    array-includes "^3.1.3"
+    array.prototype.flatmap "^1.2.4"
     doctrine "^2.1.0"
     has "^1.0.3"
-    jsx-ast-utils "^2.1.0"
-    object.entries "^1.1.0"
-    object.fromentries "^2.0.0"
-    object.values "^1.1.0"
+    jsx-ast-utils "^2.4.1 || ^3.0.0"
+    minimatch "^3.0.4"
+    object.entries "^1.1.4"
+    object.fromentries "^2.0.4"
+    object.values "^1.1.4"
     prop-types "^15.7.2"
-    resolve "^1.10.1"
+    resolve "^2.0.0-next.3"
+    string.prototype.matchall "^4.0.5"
 
 eslint-restricted-globals@^0.1.1:
   version "0.1.1"
@@ -7890,14 +7829,6 @@ find-root@^1.1.0:
   resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4"
   integrity sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==
 
-find-up@4.1.0, find-up@^4.0.0, find-up@^4.1.0:
-  version "4.1.0"
-  resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
-  integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
-  dependencies:
-    locate-path "^5.0.0"
-    path-exists "^4.0.0"
-
 find-up@^1.0.0:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f"
@@ -7918,6 +7849,14 @@ find-up@^3.0.0:
   dependencies:
     locate-path "^3.0.0"
 
+find-up@^4.0.0, find-up@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
+  integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
+  dependencies:
+    locate-path "^5.0.0"
+    path-exists "^4.0.0"
+
 findup-sync@3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-3.0.0.tgz#17b108f9ee512dfb7a5c7f3c8b27ea9e1a9c08d1"
@@ -8734,11 +8673,6 @@ growi-plugin-lsx@^4.0.3:
     growi-commons "^5.0.4"
     url "^0.11.0"
 
-growi-plugin-pukiwiki-like-linker@^3.1.0:
-  version "3.1.0"
-  resolved "https://registry.yarnpkg.com/growi-plugin-pukiwiki-like-linker/-/growi-plugin-pukiwiki-like-linker-3.1.0.tgz#e76981d0b3c6ca963950c6ca2a7cd2b520186523"
-  integrity sha512-DCthMiv8fugimqTHnRCer7qhkZIGdXr+A2YAzJFmpRocSC05ujnBS8T23ebomN0f9Hvz1pCMQ9fYFrn6b8JbIQ==
-
 growly@^1.3.0:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
@@ -10814,13 +10748,13 @@ jsprim@^1.2.2:
     json-schema "0.2.3"
     verror "1.10.0"
 
-jsx-ast-utils@^2.1.0:
-  version "2.2.1"
-  resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.2.1.tgz#4d4973ebf8b9d2837ee91a8208cc66f3a2776cfb"
-  integrity sha512-v3FxCcAf20DayI+uxnCuw795+oOIkVu6EnJ1+kSzhqqTZHNkTZ7B66ZgLp4oLJ/gbA64cI0B7WRoHZMSRdyVRQ==
+"jsx-ast-utils@^2.4.1 || ^3.0.0":
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.2.0.tgz#41108d2cec408c3453c1bbe8a4aae9e1e2bd8f82"
+  integrity sha512-EIsmt3O3ljsU6sot/J4E1zDRxfBNrhjyf/OKjlydwgEimQuznlM4Wv7U+ueONJMyEn1WRE0K8dhi3dVAXYT24Q==
   dependencies:
-    array-includes "^3.0.3"
-    object.assign "^4.1.0"
+    array-includes "^3.1.2"
+    object.assign "^4.1.2"
 
 jwa@^1.4.1:
   version "1.4.1"
@@ -11298,11 +11232,6 @@ lodash.truncate@^4.4.2:
   resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193"
   integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=
 
-lodash.unescape@4.0.1:
-  version "4.0.1"
-  resolved "https://registry.yarnpkg.com/lodash.unescape/-/lodash.unescape-4.0.1.tgz#bf2249886ce514cda112fae9218cdc065211fc9c"
-  integrity sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw=
-
 lodash.union@^4.6.0:
   version "4.6.0"
   resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88"
@@ -13175,7 +13104,7 @@ object.assign@^4.1.2:
     has-symbols "^1.0.1"
     object-keys "^1.1.1"
 
-object.entries@^1.0.4, object.entries@^1.1.0:
+object.entries@^1.0.4:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.0.tgz#2024fc6d6ba246aee38bdb0ffd5cfbcf371b7519"
   dependencies:
@@ -13184,14 +13113,24 @@ object.entries@^1.0.4, object.entries@^1.1.0:
     function-bind "^1.1.1"
     has "^1.0.3"
 
-object.fromentries@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.0.tgz#49a543d92151f8277b3ac9600f1e930b189d30ab"
+object.entries@^1.1.4:
+  version "1.1.4"
+  resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.4.tgz#43ccf9a50bc5fd5b649d45ab1a579f24e088cafd"
+  integrity sha512-h4LWKWE+wKQGhtMjZEBud7uLGhqyLwj8fpHOarZhD2uY3C9cRtk57VQ89ke3moByLXMedqs3XCHzyb4AmA2DjA==
   dependencies:
-    define-properties "^1.1.2"
-    es-abstract "^1.11.0"
-    function-bind "^1.1.1"
-    has "^1.0.1"
+    call-bind "^1.0.2"
+    define-properties "^1.1.3"
+    es-abstract "^1.18.2"
+
+object.fromentries@^2.0.4:
+  version "2.0.4"
+  resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.4.tgz#26e1ba5c4571c5c6f0890cef4473066456a120b8"
+  integrity sha512-EsFBshs5RUUpQEY1D4q/m59kMfz4YJvxuNCJcv/jWwOJr34EaVnG11ZrZa0UHB3wnzV1wx8m58T4hQL8IuNXlQ==
+  dependencies:
+    call-bind "^1.0.2"
+    define-properties "^1.1.3"
+    es-abstract "^1.18.0-next.2"
+    has "^1.0.3"
 
 object.getownpropertydescriptors@^2.0.3:
   version "2.0.3"
@@ -13222,17 +13161,7 @@ object.values@^1.0.4:
     function-bind "^1.1.0"
     has "^1.0.1"
 
-object.values@^1.1.0:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.0.tgz#bf6810ef5da3e5325790eaaa2be213ea84624da9"
-  integrity sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg==
-  dependencies:
-    define-properties "^1.1.3"
-    es-abstract "^1.12.0"
-    function-bind "^1.1.1"
-    has "^1.0.3"
-
-object.values@^1.1.3:
+object.values@^1.1.3, object.values@^1.1.4:
   version "1.1.4"
   resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.4.tgz#0d273762833e816b693a637d30073e7051535b30"
   integrity sha512-TnGo7j4XSnKQoK3MfvkzqKCi0nVe/D9I9IjwTNYdb/fxYHpjrluHVOgw0AF6jrRFGMPHdfuidR09tIDiIvnaSg==
@@ -13577,15 +13506,6 @@ p-waterfall@^2.1.1:
   dependencies:
     p-reduce "^2.0.0"
 
-package-installed-version-sync@^2.1.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/package-installed-version-sync/-/package-installed-version-sync-2.1.0.tgz#db8d2cbee32bc91a36e100da9bda6743f956ac93"
-  integrity sha512-rhREjEXIJ0IurYS23PGmlL1T+6/wJL9Oev2WYztN+MYze6xpsFxUL3DaixlZglpHoYCPxu3tdCUO/AMoIVrCVg==
-  dependencies:
-    "@yarnpkg/lockfile" "^1.1.0"
-    find-up "4.1.0"
-    semver "^6.2.0"
-
 pacote@^11.2.6:
   version "11.3.1"
   resolved "https://registry.yarnpkg.com/pacote/-/pacote-11.3.1.tgz#6ce95dd230db475cbd8789fd1f986bec51b4bf7c"
@@ -15738,7 +15658,7 @@ regexp-clone@1.0.0, regexp-clone@^1.0.0:
   resolved "https://registry.yarnpkg.com/regexp-clone/-/regexp-clone-1.0.0.tgz#222db967623277056260b992626354a04ce9bf63"
   integrity sha512-TuAasHQNamyyJ2hb97IuBEif4qBHGjPHBS64sZwytpLEqtBQ1gPJTnOaQ6qmpET16cK14kkjbazl6+p0RRv0yw==
 
-regexp.prototype.flags@^1.2.0:
+regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.3.1:
   version "1.3.1"
   resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz#7ef352ae8d159e758c0eadca6f8fcb4eef07be26"
   integrity sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==
@@ -15746,11 +15666,6 @@ regexp.prototype.flags@^1.2.0:
     call-bind "^1.0.2"
     define-properties "^1.1.3"
 
-regexpp@^3.0.0:
-  version "3.1.0"
-  resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2"
-  integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==
-
 regexpp@^3.1.0:
   version "3.2.0"
   resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2"
@@ -15982,13 +15897,6 @@ resolve@^1.10.0, resolve@^1.12.0:
   dependencies:
     path-parse "^1.0.6"
 
-resolve@^1.10.1:
-  version "1.11.1"
-  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.11.1.tgz#ea10d8110376982fef578df8fc30b9ac30a07a3e"
-  integrity sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw==
-  dependencies:
-    path-parse "^1.0.6"
-
 resolve@^1.3.2:
   version "1.11.0"
   resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.11.0.tgz#4014870ba296176b86343d50b60f3b50609ce232"
@@ -15996,6 +15904,14 @@ resolve@^1.3.2:
   dependencies:
     path-parse "^1.0.6"
 
+resolve@^2.0.0-next.3:
+  version "2.0.0-next.3"
+  resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.3.tgz#d41016293d4a8586a39ca5d9b5f15cbea1f55e46"
+  integrity sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==
+  dependencies:
+    is-core-module "^2.2.0"
+    path-parse "^1.0.6"
+
 resp-modifier@6.0.2:
   version "6.0.2"
   resolved "https://registry.yarnpkg.com/resp-modifier/-/resp-modifier-6.0.2.tgz#b124de5c4fbafcba541f48ffa73970f4aa456b4f"
@@ -16340,7 +16256,7 @@ semver@^6.0.0:
   resolved "https://registry.yarnpkg.com/semver/-/semver-6.1.1.tgz#53f53da9b30b2103cd4f15eab3a18ecbcb210c9b"
   integrity sha512-rWYq2e5iYW+fFe/oPPtYJxYgjBm8sC4rmoGdUOgBB7VnwKt6HrL793l2voH1UlsyYZpJ4g0wfjnTEO1s1NP2eQ==
 
-semver@^6.2.0, semver@^6.3.0:
+semver@^6.3.0:
   version "6.3.0"
   resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
   integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
@@ -17323,6 +17239,20 @@ string-width@^4.2.0:
     is-fullwidth-code-point "^3.0.0"
     strip-ansi "^6.0.0"
 
+string.prototype.matchall@^4.0.5:
+  version "4.0.5"
+  resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.5.tgz#59370644e1db7e4c0c045277690cf7b01203c4da"
+  integrity sha512-Z5ZaXO0svs0M2xd/6By3qpeKpLKd9mO4v4q3oMEQrk8Ck4xOD5d5XeBOOjGrmVZZ/AHB1S0CgG4N5r1G9N3E2Q==
+  dependencies:
+    call-bind "^1.0.2"
+    define-properties "^1.1.3"
+    es-abstract "^1.18.2"
+    get-intrinsic "^1.1.1"
+    has-symbols "^1.0.2"
+    internal-slot "^1.0.3"
+    regexp.prototype.flags "^1.3.1"
+    side-channel "^1.0.4"
+
 string.prototype.padend@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/string.prototype.padend/-/string.prototype.padend-3.0.0.tgz#f3aaef7c1719f170c5eab1c32bf780d96e21f2f0"