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

Merge pull request #4260 from weseek/master

Release v4.4.0
Yuki Takei 4 лет назад
Родитель
Сommit
cc3f9dce96
100 измененных файлов с 1253 добавлено и 813 удалено
  1. 2 0
      .devcontainer/Dockerfile
  2. 1 0
      .devcontainer/devcontainer.json
  3. 4 1
      .devcontainer/docker-compose.yml
  4. 1 9
      .eslintignore
  5. 12 15
      .eslintrc.js
  6. 3 0
      .github/git-pr-release-template.erb
  7. 46 0
      .github/release-drafter.yml
  8. 26 81
      .github/workflows/ci-slackbot-proxy.yml
  9. 63 139
      .github/workflows/ci.yml
  10. 62 0
      .github/workflows/draft-release.yml
  11. 39 0
      .github/workflows/pr-to-master.yml
  12. 6 8
      .github/workflows/release-rc.yml
  13. 8 8
      .github/workflows/release-slackbot-proxy.yml
  14. 108 40
      .github/workflows/release.yml
  15. 0 30
      .gitignore
  16. 4 0
      .markdownlint.yml
  17. 0 4
      .stylelintrc.json
  18. 27 6
      .vscode/launch.json
  19. 0 19
      .vscode/tasks.json
  20. 39 2
      CHANGELOG.md
  21. 5 7
      README.md
  22. 5 7
      README_JP.md
  23. 0 43
      app.json
  24. 0 39
      babel.config.js
  25. 4 0
      bin/bump-versions/README.md
  26. 16 0
      bin/bump-versions/cli.js
  27. 71 0
      bin/bump-versions/flow/bump-versions.js
  28. 12 0
      bin/bump-versions/index.js
  29. 55 0
      bin/bump-versions/step/printHelp.js
  30. 0 43
      bin/generate-plugin-definitions-source.js
  31. 0 6
      bin/github-actions/update-readme.sh
  32. 12 0
      bump-versions.config.js
  33. 0 26
      config/env.dev.js
  34. 0 4
      config/env.prod.js
  35. 0 13
      config/index.js
  36. 0 14
      docker/docker-entrypoint.sh
  37. 41 63
      package.json
  38. 26 0
      packages/app/.env.development
  39. 5 0
      packages/app/.env.production
  40. 6 0
      packages/app/.eslintignore
  41. 35 0
      packages/app/.eslintrc.js
  42. 21 0
      packages/app/.gitignore
  43. 0 0
      packages/app/.prettierignore
  44. 19 0
      packages/app/.stylelintrc.json
  45. 158 0
      packages/app/bin/cdn/cdn-resources-downloader.ts
  46. 6 8
      packages/app/bin/download-cdn-resources.ts
  47. 56 0
      packages/app/bin/generate-plugin-definitions-source.ts
  48. 6 0
      packages/app/bin/github-actions/update-readme.sh
  49. 2 0
      packages/app/bin/shrink-emojione-strategy.js
  50. 0 0
      packages/app/bin/templates/plugin-definitions.js.swig
  51. 8 0
      packages/app/config/cdn.js
  52. 4 0
      packages/app/config/ci/.env.local.for-ci
  53. 0 0
      packages/app/config/logger/config.dev.js
  54. 0 0
      packages/app/config/logger/config.prod.js
  55. 5 7
      packages/app/config/migrate.js
  56. 0 0
      packages/app/config/swagger-definition.js
  57. 58 55
      packages/app/config/webpack.common.js
  58. 4 4
      packages/app/config/webpack.dev.dll.js
  59. 7 7
      packages/app/config/webpack.dev.js
  60. 10 10
      packages/app/config/webpack.prod.js
  61. 33 25
      packages/app/docker/Dockerfile
  62. 5 5
      packages/app/docker/README.md
  63. 14 0
      packages/app/docker/docker-entrypoint.sh
  64. 0 0
      packages/app/docker/nocdn/env.prod.js
  65. 24 17
      packages/app/jest.config.js
  66. 69 48
      packages/app/package.json
  67. 0 0
      packages/app/public/favicon.ico
  68. 0 0
      packages/app/public/images/customize-settings/default-dark.svg
  69. 0 0
      packages/app/public/images/customize-settings/default-light.svg
  70. 0 0
      packages/app/public/images/customize-settings/fluid-dark.svg
  71. 0 0
      packages/app/public/images/customize-settings/fluid-light.svg
  72. 0 0
      packages/app/public/images/file-not-found.png
  73. 0 0
      packages/app/public/images/icons/editor/bold.svg
  74. 0 0
      packages/app/public/images/icons/editor/check.svg
  75. 0 0
      packages/app/public/images/icons/editor/code.svg
  76. 0 0
      packages/app/public/images/icons/editor/header.svg
  77. 0 0
      packages/app/public/images/icons/editor/italic.svg
  78. 0 0
      packages/app/public/images/icons/editor/link.svg
  79. 0 0
      packages/app/public/images/icons/editor/list-ol.svg
  80. 0 0
      packages/app/public/images/icons/editor/list-ul.svg
  81. 0 0
      packages/app/public/images/icons/editor/picture.svg
  82. 0 0
      packages/app/public/images/icons/editor/quote.svg
  83. 0 0
      packages/app/public/images/icons/editor/strikethrough.svg
  84. 0 0
      packages/app/public/images/icons/editor/table.svg
  85. 0 0
      packages/app/public/images/icons/emacs.png
  86. 0 0
      packages/app/public/images/icons/favicon/android-icon-144x144.png
  87. 0 0
      packages/app/public/images/icons/favicon/android-icon-192x192.png
  88. 0 0
      packages/app/public/images/icons/favicon/android-icon-36x36.png
  89. 0 0
      packages/app/public/images/icons/favicon/android-icon-48x48.png
  90. 0 0
      packages/app/public/images/icons/favicon/android-icon-72x72.png
  91. 0 0
      packages/app/public/images/icons/favicon/android-icon-96x96.png
  92. 0 0
      packages/app/public/images/icons/favicon/apple-icon-114x114.png
  93. 0 0
      packages/app/public/images/icons/favicon/apple-icon-120x120.png
  94. 0 0
      packages/app/public/images/icons/favicon/apple-icon-144x144.png
  95. 0 0
      packages/app/public/images/icons/favicon/apple-icon-152x152.png
  96. 0 0
      packages/app/public/images/icons/favicon/apple-icon-180x180.png
  97. 0 0
      packages/app/public/images/icons/favicon/apple-icon-57x57.png
  98. 0 0
      packages/app/public/images/icons/favicon/apple-icon-60x60.png
  99. 0 0
      packages/app/public/images/icons/favicon/apple-icon-72x72.png
  100. 0 0
      packages/app/public/images/icons/favicon/apple-icon-76x76.png

+ 2 - 0
.devcontainer/Dockerfile

@@ -14,6 +14,8 @@ ARG USER_UID=1000
 ARG USER_GID=$USER_UID
 
 RUN mkdir -p /workspace/growi/node_modules
+RUN mkdir -p /workspace/growi/packages/app/node_modules
+RUN mkdir -p /workspace/growi/packages/slackbot-proxy/node_modules
 
 # [Optional] Update UID/GID if needed
 RUN if [ "$USER_GID" != "1000" ] || [ "$USER_UID" != "1000" ]; then \

+ 1 - 0
.devcontainer/devcontainer.json

@@ -16,6 +16,7 @@
 	"extensions": [
 		"dbaeumer.vscode-eslint",
 		"eamodio.gitlens",
+    "firsttris.vscode-jest-runner",
 		"msjsdiag.debugger-for-chrome",
 		"firefox-devtools.vscode-firefox-debug",
 		"editorconfig.editorconfig",

+ 4 - 1
.devcontainer/docker-compose.yml

@@ -24,8 +24,9 @@ services:
     volumes:
       - ..:/workspace/growi:delegated
       - node_modules:/workspace/growi/node_modules
+      - node_modules_app:/workspace/growi/packages/app/node_modules
+      - node_modules_slackbot-proxy:/workspace/growi/packages/slackbot-proxy/node_modules
       - ../../growi-docker-compose:/workspace/growi-docker-compose:delegated
-      - ../../node_modules:/workspace/node_modules:delegated
 
     tty: true
 
@@ -81,3 +82,5 @@ services:
       - /files/sqlite
 volumes:
   node_modules:
+  node_modules_app:
+  node_modules_slackbot-proxy:

+ 1 - 9
.eslintignore

@@ -1,9 +1 @@
-/.github/**
-/.vscode/**
-/node_modules/**
-/packages/**
-/public/**
-/src/client/js/legacy/thirdparty-js/**
-/src/client/js/util/reveal/plugins/markdown.js
-/src/linter-checker/**
-/tmp/**
+node_modules/**

+ 12 - 15
.eslintrc.js

@@ -1,37 +1,34 @@
 module.exports = {
+  root: true, // https://eslint.org/docs/user-guide/configuring/configuration-files#cascading-and-hierarchy
   extends: [
     'weseek',
-    'weseek/react',
-    "plugin:jest/recommended",
+    'weseek/typescript',
+    'plugin:jest/recommended',
   ],
   env: {
-    jquery: true,
-    "jest/globals": true,
+    'jest/globals': true,
   },
   globals: {
-    $: true,
-    jquery: true,
-    emojione: true,
-    hljs: true,
-    ScrollPosStyler: true,
-    window: true,
   },
   plugins: [
-    "jest",
+    'jest',
   ],
   rules: {
-    'indent': [
+    'import/prefer-default-export': 'off',
+    '@typescript-eslint/no-explicit-any': 'off',
+    indent: [
       'error',
       2,
       {
         SwitchCase: 1,
-        ignoredNodes: ['JSXElement *', 'JSXElement', 'JSXAttribute', 'JSXSpreadAttribute'],
         ArrayExpression: 'first',
         FunctionDeclaration: { body: 1, parameters: 2 },
         FunctionExpression: { body: 1, parameters: 2 },
       },
     ],
-    // eslint-plugin-import rules
-    'import/no-unresolved': [2, { ignore: ['^@'] }], // ignore @alias/..., @commons/..., ...
+    'jest/no-standalone-expect': [
+      'error',
+      { additionalTestBlockFunctions: ['each.test'] },
+    ],
   },
 };

+ 3 - 0
.github/git-pr-release-template.erb

@@ -0,0 +1,3 @@
+<%= ENV['GIT_PR_RELEASE_TITLE'] %>
+
+<%= ENV['GIT_PR_RELEASE_BODY'] %>

+ 46 - 0
.github/release-drafter.yml

@@ -0,0 +1,46 @@
+categories:
+  - title: 'BREAKING CHANGES'
+    labels:
+      - 'breaking'
+  - title: '💎 Features'
+    labels:
+      - 'feature'
+  - title: '🚀 Improvement'
+    labels:
+      - 'improvement'
+  - title: '🐛 Bug Fixes'
+    labels:
+      - 'bug'
+  - title: '🧰 Maintenance'
+    labels:
+      - 'support'
+      - 'dependencies'
+category-template: '### $TITLE'
+change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks.
+autolabeler:
+  - label: 'feature'
+    branch:
+      - '/feat\/.+/'
+  - label: 'improvement'
+    branch:
+      - '/imprv\/.+/'
+  - label: 'bug'
+    branch:
+      - '/fix\/.+/'
+    title:
+      - '/fix/i'
+  - label: 'support'
+    branch:
+      - '/support\/.+/'
+    title:
+      - '/chore/i'
+      - '/ci/i'
+      - '/docs/i'
+      - '/test/i'
+
+exclude-labels:
+  - 'exclude from changelog'
+template: |
+  ### Changes
+
+  $CHANGES

+ 26 - 81
.github/workflows/ci-slackbot-proxy.yml

@@ -6,12 +6,6 @@ on:
       - release/**
       - rc/**
       - tmp/**
-    paths:
-      - .github/workflows/ci-slackbot-proxy.yml
-      - packages/slack/**
-      - packages/slackbot-proxy/**
-      - package.json
-      - yarn.lock
 
 jobs:
 
@@ -24,30 +18,14 @@ jobs:
 
     steps:
     - uses: actions/checkout@v2
-    - name: Use Node.js ${{ matrix.node-version }}
-      uses: actions/setup-node@v1
+
+    - uses: actions/setup-node@v2
       with:
         node-version: ${{ matrix.node-version }}
-    - name: Cache/Restore node_modules
-      id: cache-dependencies
-      uses: actions/cache@v2
-      with:
-        path: '**/node_modules'
-        key: ${{ runner.OS }}-node_modules-${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}
-    - name: Get yarn cache dir
-      if: steps.cache-dependencies.outputs.cache-hit != 'true'
-      id: cache-yarn
-      run: echo "::set-output name=dir::$(yarn cache dir)"
-    - name: Cache/Restore yarn cache
-      if: steps.cache-dependencies.outputs.cache-hit != 'true'
-      uses: actions/cache@v2
-      with:
-        path: ${{ steps.cache-yarn.outputs.dir }}
-        key: ${{ runner.os }}-yarn-${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}
-        restore-keys: |
-          ${{ runner.os }}-yarn-${{ matrix.node-version }}-
-    - name: Install dependencies
-      if: steps.cache-dependencies.outputs.cache-hit != 'true'
+        cache: 'yarn'
+        cache-dependency-path: '**/yarn.lock'
+
+    - name: lerna bootstrap
       run: |
         npx lerna bootstrap
     - name: Print dependencies
@@ -55,9 +33,13 @@ jobs:
         echo -n "node " && node -v
         echo -n "npm " && npm -v
         yarn list --depth=0
+
+    - name: yarn lint
+      run: |
+        yarn lerna run lint --scope @growi/slack --scope @growi/slackbot-proxy
     - name: yarn test
       run: |
-        yarn lerna run test
+        yarn lerna run test --scope @growi/slack --scope @growi/slackbot-proxy
 
     - name: Slack Notification
       uses: weseek/ghaction-slack-notification@master
@@ -89,30 +71,14 @@ jobs:
 
     steps:
     - uses: actions/checkout@v2
-    - name: Use Node.js ${{ matrix.node-version }}
-      uses: actions/setup-node@v1
+
+    - uses: actions/setup-node@v2
       with:
         node-version: ${{ matrix.node-version }}
-    - name: Cache/Restore node_modules
-      id: cache-dependencies
-      uses: actions/cache@v2
-      with:
-        path: '**/node_modules'
-        key: ${{ runner.OS }}-node_modules_dev-${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}
-    - name: Get yarn cache dir
-      if: steps.cache-dependencies.outputs.cache-hit != 'true'
-      id: cache-yarn
-      run: echo "::set-output name=dir::$(yarn cache dir)"
-    - name: Cache/Restore yarn cache
-      if: steps.cache-dependencies.outputs.cache-hit != 'true'
-      uses: actions/cache@v2
-      with:
-        path: ${{ steps.cache-yarn.outputs.dir }}
-        key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
-        restore-keys: |
-          ${{ runner.os }}-yarn-
+        cache: 'yarn'
+        cache-dependency-path: '**/yarn.lock'
+
     - name: lerna bootstrap
-      if: steps.cache-dependencies.outputs.cache-hit != 'true'
       run: |
         npx lerna bootstrap
     - name: Print dependencies
@@ -120,10 +86,11 @@ jobs:
         echo -n "node " && node -v
         echo -n "npm " && npm -v
         yarn list --depth=0
+
     - name: yarn dev:ci
       working-directory: ./packages/slackbot-proxy
       run: |
-        cp config/ci/.env.local.for-ci .env.local
+        cp config/ci/.env.local.for-ci .env.development.local
         yarn dev:ci
       env:
         TYPEORM_CONNECTION: mysql
@@ -132,6 +99,7 @@ jobs:
         TYPEORM_DATABASE: growi-slackbot-proxy
         TYPEORM_USERNAME: root
         TYPEORM_PASSWORD:
+
     - name: Slack Notification
       uses: weseek/ghaction-slack-notification@master
       if: failure()
@@ -162,36 +130,13 @@ jobs:
 
     steps:
     - uses: actions/checkout@v2
-    - name: Use Node.js ${{ matrix.node-version }}
-      uses: actions/setup-node@v1
+
+    - uses: actions/setup-node@v2
       with:
         node-version: ${{ matrix.node-version }}
-    - name: Get Date
-      id: date
-      run: |
-        echo ::set-output name=YmdH::$(date '+%Y%m%d%H')
-        echo ::set-output name=Ymd::$(date '+%Y%m%d')
-        echo ::set-output name=Ym::$(date '+%Y%m')
-        echo ::set-output name=Y::$(date '+%Y')
-    - name: Cache/Restore node_modules
-      uses: actions/cache@v2
-      with:
-        path: '**/node_modules'
-        key: ${{ runner.OS }}-node_modules_prod-${{ matrix.node-version }}-${{ steps.date.outputs.YmdH }}
-        restore-keys: |
-          ${{ runner.os }}-node_modules_prod-${{ matrix.node-version }}-${{ steps.date.outputs.Ymd }}
-          ${{ runner.os }}-node_modules_prod-${{ matrix.node-version }}-${{ steps.date.outputs.Ym }}
-          ${{ runner.os }}-node_modules_prod-${{ matrix.node-version }}-${{ steps.date.outputs.Y }}
-    - name: Get yarn cache dir
-      id: cache-yarn
-      run: echo "::set-output name=dir::$(yarn cache dir)"
-    - name: Cache/Restore yarn cache
-      uses: actions/cache@v2
-      with:
-        path: ${{ steps.cache-yarn.outputs.dir }}
-        key: ${{ runner.os }}-yarn-${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}
-        restore-keys: |
-          ${{ runner.os }}-yarn-
+        cache: 'yarn'
+        cache-dependency-path: '**/yarn.lock'
+
     - name: lerna bootstrap
       run: |
         npx lerna bootstrap
@@ -202,7 +147,7 @@ jobs:
         yarn list --depth=0
     - name: lerna run build
       run: |
-        yarn lerna run build
+        yarn lerna run build --scope @growi/slack --scope @growi/slackbot-proxy
     - name: lerna bootstrap --production
       run: |
         npx lerna bootstrap -- --production
@@ -214,7 +159,7 @@ jobs:
     - name: yarn start:prod:ci
       working-directory: ./packages/slackbot-proxy
       run: |
-        cp config/ci/.env.local.for-ci .env.local
+        cp config/ci/.env.local.for-ci .env.production.local
         yarn start:prod:ci
       env:
         TYPEORM_CONNECTION: mysql

+ 63 - 139
.github/workflows/ci.yml

@@ -6,17 +6,6 @@ on:
       - release/**
       - rc/**
       - tmp/**
-    paths:
-      - .github/workflows/ci.yml
-      - packages/app/**
-      - .eslint*
-      - .prettier*
-      - .stylelint*
-      - config/**
-      - resource/**
-      - src/**
-      - package.json
-      - yarn.lock
 
 jobs:
 
@@ -29,30 +18,14 @@ jobs:
 
     steps:
     - uses: actions/checkout@v2
-    - name: Use Node.js ${{ matrix.node-version }}
-      uses: actions/setup-node@v1
+
+    - uses: actions/setup-node@v2
       with:
         node-version: ${{ matrix.node-version }}
-    - name: Cache/Restore node_modules
-      id: cache-dependencies
-      uses: actions/cache@v2
-      with:
-        path: '**/node_modules'
-        key: ${{ runner.OS }}-node_modules-${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}
-    - name: Get yarn cache dir
-      if: steps.cache-dependencies.outputs.cache-hit != 'true'
-      id: cache-yarn
-      run: echo "::set-output name=dir::$(yarn cache dir)"
-    - name: Cache/Restore yarn cache
-      if: steps.cache-dependencies.outputs.cache-hit != 'true'
-      uses: actions/cache@v2
-      with:
-        path: ${{ steps.cache-yarn.outputs.dir }}
-        key: ${{ runner.os }}-yarn-${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}
-        restore-keys: |
-          ${{ runner.os }}-yarn-${{ matrix.node-version }}-
-    - name: Install dependencies
-      if: steps.cache-dependencies.outputs.cache-hit != 'true'
+        cache: 'yarn'
+        cache-dependency-path: '**/yarn.lock'
+
+    - name: lerna bootstrap
       run: |
         npx lerna bootstrap
     - name: Print dependencies
@@ -60,9 +33,13 @@ jobs:
         echo -n "node " && node -v
         echo -n "npm " && npm -v
         yarn list --depth=0
-    - name: yarn lint
+
+    - name: lerna run lint for plugins
       run: |
-        yarn lint
+        yarn lerna run lint --scope @growi/plugin-*
+    - name: lerna run lint for app
+      run: |
+        yarn lerna run lint --scope @growi/app --scope @growi/core --scope @growi/ui
 
     - name: Slack Notification
       uses: weseek/ghaction-slack-notification@master
@@ -94,30 +71,14 @@ jobs:
 
     steps:
     - uses: actions/checkout@v2
-    - name: Use Node.js ${{ matrix.node-version }}
-      uses: actions/setup-node@v1
+
+    - uses: actions/setup-node@v2
       with:
         node-version: ${{ matrix.node-version }}
-    - name: Cache/Restore node_modules
-      id: cache-dependencies
-      uses: actions/cache@v2
-      with:
-        path: '**/node_modules'
-        key: ${{ runner.OS }}-node_modules-${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}
-    - name: Get yarn cache dir
-      if: steps.cache-dependencies.outputs.cache-hit != 'true'
-      id: cache-yarn
-      run: echo "::set-output name=dir::$(yarn cache dir)"
-    - name: Cache/Restore yarn cache
-      if: steps.cache-dependencies.outputs.cache-hit != 'true'
-      uses: actions/cache@v2
-      with:
-        path: ${{ steps.cache-yarn.outputs.dir }}
-        key: ${{ runner.os }}-yarn-${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}
-        restore-keys: |
-          ${{ runner.os }}-yarn-${{ matrix.node-version }}-
-    - name: Install dependencies
-      if: steps.cache-dependencies.outputs.cache-hit != 'true'
+        cache: 'yarn'
+        cache-dependency-path: '**/yarn.lock'
+
+    - name: lerna bootstrap
       run: |
         npx lerna bootstrap
     - name: Print dependencies
@@ -125,17 +86,26 @@ jobs:
         echo -n "node " && node -v
         echo -n "npm " && npm -v
         yarn list --depth=0
+
     - name: yarn test
+      working-directory: ./packages/app
       run: |
         yarn test
       env:
         MONGO_URI: mongodb://localhost:${{ job.services.mongodb.ports['27017'] }}/growi_test
     - name: yarn test with MongoDB 3.6
+      working-directory: ./packages/app
       run: |
         yarn test
       env:
         MONGO_URI: mongodb://localhost:${{ job.services.mongodb36.ports['27017'] }}/growi_test
 
+    - name: Upload coverage report as artifact
+      uses: actions/upload-artifact@v2
+      with:
+        name: Coverage Report
+        path: packages/app/coverage
+
     - name: Slack Notification
       uses: weseek/ghaction-slack-notification@master
       if: failure()
@@ -147,55 +117,29 @@ jobs:
         url: ${{ secrets.SLACK_WEBHOOK_URL }}
 
 
-  build-dev:
+  launch-dev:
     runs-on: ubuntu-latest
 
     strategy:
       matrix:
         node-version: [14.x]
 
+    services:
+      mongodb:
+        image: mongo:4.4
+        ports:
+        - 27017/tcp
+
     steps:
     - uses: actions/checkout@v2
-    - name: Use Node.js ${{ matrix.node-version }}
-      uses: actions/setup-node@v1
+
+    - uses: actions/setup-node@v2
       with:
         node-version: ${{ matrix.node-version }}
-    - name: Cache/Restore node_modules
-      id: cache-dependencies
-      uses: actions/cache@v2
-      with:
-        path: '**/node_modules'
-        key: ${{ runner.OS }}-node_modules_dev-${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}
-    - name: Get Date
-      id: date
-      run: |
-        echo ::set-output name=YmdH::$(date '+%Y%m%d%H')
-        echo ::set-output name=Ymd::$(date '+%Y%m%d')
-        echo ::set-output name=Ym::$(date '+%Y%m')
-        echo ::set-output name=Y::$(date '+%Y')
-    - name: Cache/Restore node_modules/.cache/hard-source
-      uses: actions/cache@v2
-      with:
-        path: node_modules/.cache
-        key: ${{ runner.OS }}-hard_source_webpack-${{ matrix.node-version }}-${{ steps.date.outputs.YmdH }}
-        restore-keys: |
-          ${{ runner.os }}-hard_source_webpack-${{ matrix.node-version }}-${{ steps.date.outputs.Ymd }}
-          ${{ runner.os }}-hard_source_webpack-${{ matrix.node-version }}-${{ steps.date.outputs.Ym }}
-          ${{ runner.os }}-hard_source_webpack-${{ matrix.node-version }}-${{ steps.date.outputs.Y }}
-    - name: Get yarn cache dir
-      if: steps.cache-dependencies.outputs.cache-hit != 'true'
-      id: cache-yarn
-      run: echo "::set-output name=dir::$(yarn cache dir)"
-    - name: Cache/Restore yarn cache
-      if: steps.cache-dependencies.outputs.cache-hit != 'true'
-      uses: actions/cache@v2
-      with:
-        path: ${{ steps.cache-yarn.outputs.dir }}
-        key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
-        restore-keys: |
-          ${{ runner.os }}-yarn-
-    - name: Install dependencies
-      if: steps.cache-dependencies.outputs.cache-hit != 'true'
+        cache: 'yarn'
+        cache-dependency-path: '**/yarn.lock'
+
+    - name: lerna bootstrap
       run: |
         npx lerna bootstrap
     - name: Print dependencies
@@ -203,9 +147,14 @@ jobs:
         echo -n "node " && node -v
         echo -n "npm " && npm -v
         yarn list --depth=0
-    - name: yarn build:dev
+
+    - name: yarn dev:ci
+      working-directory: ./packages/app
       run: |
-        yarn build:dev
+        cp config/ci/.env.local.for-ci .env.development.local
+        yarn dev:ci
+      env:
+        MONGO_URI: mongodb://localhost:${{ job.services.mongodb.ports['27017'] }}/growi_dev
 
     - name: Slack Notification
       uses: weseek/ghaction-slack-notification@master
@@ -218,7 +167,7 @@ jobs:
         url: ${{ secrets.SLACK_WEBHOOK_URL }}
 
 
-  build-prod:
+  launch-prod:
     runs-on: ubuntu-latest
 
     strategy:
@@ -237,37 +186,14 @@ jobs:
 
     steps:
     - uses: actions/checkout@v2
-    - name: Use Node.js ${{ matrix.node-version }}
-      uses: actions/setup-node@v1
+
+    - uses: actions/setup-node@v2
       with:
         node-version: ${{ matrix.node-version }}
-    - name: Get Date
-      id: date
-      run: |
-        echo ::set-output name=YmdH::$(date '+%Y%m%d%H')
-        echo ::set-output name=Ymd::$(date '+%Y%m%d')
-        echo ::set-output name=Ym::$(date '+%Y%m')
-        echo ::set-output name=Y::$(date '+%Y')
-    - name: Cache/Restore node_modules
-      uses: actions/cache@v2
-      with:
-        path: '**/node_modules'
-        key: ${{ runner.OS }}-node_modules_prod-${{ matrix.node-version }}-${{ steps.date.outputs.YmdH }}
-        restore-keys: |
-          ${{ runner.os }}-node_modules_prod-${{ matrix.node-version }}-${{ steps.date.outputs.Ymd }}
-          ${{ runner.os }}-node_modules_prod-${{ matrix.node-version }}-${{ steps.date.outputs.Ym }}
-          ${{ runner.os }}-node_modules_prod-${{ matrix.node-version }}-${{ steps.date.outputs.Y }}
-    - name: Get yarn cache dir
-      id: cache-yarn
-      run: echo "::set-output name=dir::$(yarn cache dir)"
-    - name: Cache/Restore yarn cache
-      uses: actions/cache@v2
-      with:
-        path: ${{ steps.cache-yarn.outputs.dir }}
-        key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
-        restore-keys: |
-          ${{ runner.os }}-yarn-
-    - name: Install dependencies
+        cache: 'yarn'
+        cache-dependency-path: '**/yarn.lock'
+
+    - name: lerna bootstrap
       run: |
         npx lerna bootstrap
     - name: Print dependencies
@@ -277,8 +203,7 @@ jobs:
         yarn list --depth=0
     - name: Build
       run: |
-        yarn lerna run build --scope @growi/slack
-        yarn lerna run build --scope @growi/app
+        yarn lerna run build --scope @growi/core --scope @growi/slack --scope @growi/plugin-* --scope @growi/app
     - name: lerna bootstrap --production
       run: |
         npx lerna bootstrap -- --production
@@ -291,21 +216,20 @@ jobs:
       id: getdbname
       run: |
         echo ::set-output name=suffix::$(echo '${{ matrix.node-version }}' | sed s/\\.//)
-    - name: yarn server:prod:ci
+    - name: yarn server:ci
+      working-directory: ./packages/app
       run: |
-        yarn server:prod:ci
+        cp config/ci/.env.local.for-ci .env.production.local
+        yarn server:ci
       env:
         MONGO_URI: mongodb://localhost:${{ job.services.mongodb.ports['27017'] }}/growi-${{ steps.getdbname.outputs.suffix }}
-    - name: yarn server:prod:ci with MongoDB 3.6
+    - name: yarn server:ci with MongoDB 3.6
+      working-directory: ./packages/app
       run: |
-        yarn server:prod:ci
+        cp config/ci/.env.local.for-ci .env.production.local
+        yarn server:ci
       env:
         MONGO_URI: mongodb://localhost:${{ job.services.mongodb36.ports['27017'] }}/growi-${{ steps.getdbname.outputs.suffix }}
-    - name: Upload report as artifact
-      uses: actions/upload-artifact@v2
-      with:
-        name: Report
-        path: report
 
     - name: Slack Notification
       uses: weseek/ghaction-slack-notification@master

+ 62 - 0
.github/workflows/draft-release.yml

@@ -0,0 +1,62 @@
+name: Draft Release
+
+on:
+  push:
+    branches:
+      - master
+
+jobs:
+
+  # Refs: https://github.com/release-drafter/release-drafter
+  update-release-draft:
+    runs-on: ubuntu-latest
+
+    outputs:
+      CURRENT_VERSION: ${{ steps.package-json.outputs.packageVersion }}
+      RELEASE_DRAFT_BODY: ${{ steps.release-drafter.outputs.body }}
+
+    steps:
+      - uses: actions/checkout@v2
+
+      - name: Retrieve information from package.json
+        uses: myrotvorets/info-from-package-json-action@0.0.2
+        id: package-json
+
+      # Drafts your next Release notes as Pull Requests are merged into "master"
+      - uses: release-drafter/release-drafter@v5
+        id: release-drafter
+        with:
+          name: v${{ steps.package-json.outputs.packageVersion }}
+          tag: v${{ steps.package-json.outputs.packageVersion }}
+          version: ${{ steps.package-json.outputs.packageVersion }}
+          disable-autolabeler: true
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+  # Refs: https://github.com/bakunyo/git-pr-release-action
+  update-release-pr:
+    needs: update-release-draft
+
+    runs-on: ubuntu-latest
+
+    steps:
+      - uses: actions/checkout@v2
+        with:
+          fetch-depth: 0
+
+      - name: Get release version
+        id: release-version
+        run: |
+          RELEASE_VERSION=`npx semver -i patch ${{ needs.update-release-draft.outputs.CURRENT_VERSION }}`
+          echo ::set-output name=RELEASE_VERSION::$RELEASE_VERSION
+
+      - name: Create/Update Pull Request
+        uses: bakunyo/git-pr-release-action@master
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+          GIT_PR_RELEASE_BRANCH_PRODUCTION: release/current
+          GIT_PR_RELEASE_BRANCH_STAGING: master
+          GIT_PR_RELEASE_TEMPLATE: .github/git-pr-release-template.erb
+          GIT_PR_RELEASE_TITLE: Release v${{ steps.release-version.outputs.RELEASE_VERSION }}
+          GIT_PR_RELEASE_BODY: ${{ needs.update-release-draft.outputs.RELEASE_DRAFT_BODY }}
+

+ 39 - 0
.github/workflows/pr-to-master.yml

@@ -0,0 +1,39 @@
+name: PR to master
+
+on:
+  pull_request:
+    branches:
+      - master
+    # Only following types are handled by the action, but one can default to all as well
+    types: [opened, reopened, edited, synchronize]
+
+jobs:
+
+  # Refs: https://github.com/release-drafter/release-drafter
+  auto-labeling:
+    runs-on: ubuntu-latest
+
+    steps:
+      - uses: release-drafter/release-drafter@v5
+        with:
+          disable-releaser: true
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+  check-title:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: amannn/action-semantic-pull-request@v3.4.2
+        with:
+          types: |
+            feat
+            imprv
+            fix
+            support
+            chore
+            ci
+            docs
+            test
+          requireScope: false
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

+ 6 - 8
.github/workflows/release-rc.yml

@@ -14,11 +14,9 @@ jobs:
     steps:
     - uses: actions/checkout@v2
 
-    - name: Setup semver
-      id: semver
-      run: |
-        semver=`npm run version --silent`
-        echo "::set-output name=SEMVER::$semver"
+    - name: Retrieve information from package.json
+      uses: myrotvorets/info-from-package-json-action@0.0.2
+      id: package-json
 
     - name: Docker meta
       id: meta
@@ -26,8 +24,8 @@ jobs:
       with:
         images: weseek/growi,ghcr.io/weseek/growi
         tags: |
-          type=raw,value=${{ steps.semver.outputs.SEMVER }}
-          type=raw,value=${{ steps.semver.outputs.SEMVER }}.{{sha}}
+          type=raw,value=${{ steps.package-json.outputs.packageVersion }}
+          type=raw,value=${{ steps.package-json.outputs.packageVersion }}.{{sha}}
 
     - name: Login to docker.io registry
       run: |
@@ -55,7 +53,7 @@ jobs:
       uses: docker/build-push-action@v2
       with:
         context: .
-        file: ./docker/Dockerfile
+        file: ./packages/app/docker/Dockerfile
         platforms: linux/amd64
         push: true
         cache-from: type=local,src=/tmp/.buildx-cache

+ 8 - 8
.github/workflows/release-slackbot-proxy.yml

@@ -1,9 +1,10 @@
 name: Release Docker Image for @growi/slackbot-proxy
 
 on:
-  push:
+  pull_request:
     branches:
       - release/slackbot-proxy/**
+    types: [closed]
 
 jobs:
 
@@ -12,13 +13,12 @@ jobs:
 
     steps:
     - uses: actions/checkout@v2
+      with:
+        ref: ${{ github.event.pull_request.base.ref }}
 
-    - name: Setup semver
-      id: semver
-      working-directory: ./packages/slackbot-proxy
-      run: |
-        semver=`npm run version --silent`
-        echo "::set-output name=SEMVER::$semver"
+    - name: Retrieve information from package.json
+      uses: myrotvorets/info-from-package-json-action@0.0.2
+      id: package-json
 
     - name: Docker meta
       id: meta
@@ -27,7 +27,7 @@ jobs:
         images: weseek/growi-slackbot-proxy,ghcr.io/weseek/growi-slackbot-proxy,asia.gcr.io/${{ secrets.GCP_PRJ_ID_SLACKBOT_PROXY }}/growi-slackbot-proxy
         tags: |
           type=raw,value=latest
-          type=raw,value=${{ steps.semver.outputs.SEMVER }}
+          type=raw,value=${{ steps.package-json.outputs.packageVersion }}
 
     - name: Login to docker.io registry
       run: |

+ 108 - 40
.github/workflows/release.yml

@@ -1,57 +1,127 @@
 name: Release
 
 on:
-  push:
+  pull_request:
     branches:
       - release/current
       - release/*.*.*
+    types: [closed]
 
 jobs:
-  github-release:
+  create-github-release:
 
     runs-on: ubuntu-latest
 
+    if: github.event.pull_request.merged == true
+
     outputs:
-      RELEASE_VERSION: ${{ steps.bump-version.outputs.RELEASE_VERSION }}
+      RELEASED_VERSION: ${{ steps.package-json.outputs.packageVersion }}
 
     steps:
     - uses: actions/checkout@v2
+      with:
+        ref: ${{ github.event.pull_request.base.ref }}
 
-    - name: Init Git
+    - uses: actions/setup-node@v2
+      with:
+        node-version: '14'
+        cache: 'yarn'
+        cache-dependency-path: '**/yarn.lock'
+
+    - name: Install dependencies
       run: |
-        git config --local user.name "GitHub Action"
-        git config --local user.email "info@weseek.co.jp"
-        git remote set-url origin "https://$GITHUB_ACTOR:${{ secrets.GITHUB_TOKEN }}@github.com/$GITHUB_REPOSITORY"
+        npx lerna bootstrap
 
-    - name: Bump version
-      id: bump-version
+    - name: Bump versions
       run: |
-        npm --no-git-tag-version version patch
-        export RELEASE_VERSION=`npm run version --silent`
-        sh ./bin/github-actions/update-readme.sh
-        echo "RELEASE_VERSION=$RELEASE_VERSION" >> $GITHUB_ENV
-        echo ::set-output name=RELEASE_VERSION::$RELEASE_VERSION
+        node ./bin/bump-versions -i patch
+        sh ./packages/app/bin/github-actions/update-readme.sh
 
-    - name: Checkout, Commit, Tag and Push
+    - name: Retrieve information from package.json
+      uses: myrotvorets/info-from-package-json-action@0.0.2
+      id: package-json
+
+    - name: Update Changelog
+      uses: stefanzweifel/changelog-updater-action@v1
+      with:
+        latest-version: v${{ steps.package-json.outputs.packageVersion }}
+        release-notes: ${{ github.event.pull_request.body }}
+
+    - name: Update README.md for docker image
+      working-directory: ./packages/app
       run: |
-        TMP_RELEASE_BRANCH=tmp/release-${{ env.RELEASE_VERSION }}
-        git checkout -B $TMP_RELEASE_BRANCH
-        git commit -am "Release v${{ env.RELEASE_VERSION }}"
-        git tag -a v${{ env.RELEASE_VERSION }} -m "v${{ env.RELEASE_VERSION }}"
-        git push --follow-tags origin $TMP_RELEASE_BRANCH
-        git push --delete origin $TMP_RELEASE_BRANCH
-
-    - name: Upload release notes
-      uses: Roang-zero1/github-create-release-action@master
-      with:
-        created_tag: v${{ env.RELEASE_VERSION }}
-        changelog_file: CHANGES.md
+        sh ./bin/github-actions/update-readme.sh
+      env:
+        RELEASED_VERSION: ${{ steps.package-json.outputs.packageVersion }}
+
+    - name: Commit, Tag and Push
+      uses: stefanzweifel/git-auto-commit-action@v4
+      with:
+        branch: ${{ github.event.pull_request.base.ref }}
+        commit_message: Release v${{ steps.package-json.outputs.packageVersion }}
+        tagging_message: v${{ steps.package-json.outputs.packageVersion }}
+
+    - uses: ncipollo/release-action@v1
+      with:
+        body: ${{ github.event.pull_request.body }}
+        tag: v${{ steps.package-json.outputs.packageVersion }}
+        token: ${{ secrets.GITHUB_TOKEN }}
+
+    - name: Delete drafts
+      uses: hugo19941994/delete-draft-releases@v1.0.0
       env:
         GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
 
 
+  create-pr-for-next-rc:
+    needs: create-github-release
+
+    runs-on: ubuntu-latest
+
+    steps:
+    - uses: actions/checkout@v2
+      with:
+        ref: v${{ needs.create-github-release.outputs.RELEASED_VERSION }}
+
+    - uses: actions/setup-node@v2
+      with:
+        node-version: '14'
+        cache: 'yarn'
+        cache-dependency-path: '**/yarn.lock'
+
+    - name: Install dependencies
+      run: |
+        npx lerna bootstrap
+
+    - name: Bump versions for next RC
+      run: |
+        node ./bin/bump-versions -i prerelease
+
+    - name: Retrieve information from package.json
+      uses: myrotvorets/info-from-package-json-action@0.0.2
+      id: package-json
+
+    - name: Commit
+      uses: github-actions-x/commit@v2.8
+      with:
+        github-token: ${{ secrets.GITHUB_TOKEN }}
+        push-branch: support/prepare-v${{ steps.package-json.outputs.packageVersion }}
+        commit-message: 'Bump version'
+        name: GitHub Action
+
+    - name: Create PR
+      uses: repo-sync/pull-request@v2
+      with:
+        source_branch: support/prepare-v${{ steps.package-json.outputs.packageVersion }}
+        destination_branch: master
+        pr_title: Prepare v${{ steps.package-json.outputs.packageVersion }}
+        pr_label: exclude from changelog
+        pr_body: "An automated PR generated by ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}"
+        github_token: ${{ secrets.GITHUB_TOKEN }}
+
+
   build-image:
-    needs: github-release
+    needs: create-github-release
 
     runs-on: ubuntu-latest
 
@@ -61,11 +131,8 @@ jobs:
 
     steps:
     - uses: actions/checkout@v2
-
-    - name: Checkout released tag
-      run: |
-        git fetch --tags
-        git checkout refs/tags/v${{ needs.github-release.outputs.RELEASE_VERSION }}
+      with:
+        ref: v${{ needs.create-github-release.outputs.RELEASED_VERSION }}
 
     - name: Setup suffix
       id: suffix
@@ -82,9 +149,9 @@ jobs:
           suffix=${{ steps.suffix.outputs.SUFFIX }}
         tags: |
           type=raw,value=latest
-          type=semver,value=${{ needs.github-release.outputs.RELEASE_VERSION }},pattern={{major}}
-          type=semver,value=${{ needs.github-release.outputs.RELEASE_VERSION }},pattern={{major}}.{{minor}}
-          type=semver,value=${{ needs.github-release.outputs.RELEASE_VERSION }},pattern={{major}}.{{minor}}.{{patch}}
+          type=semver,value=${{ needs.create-github-release.outputs.RELEASED_VERSION }},pattern={{major}}
+          type=semver,value=${{ needs.create-github-release.outputs.RELEASED_VERSION }},pattern={{major}}.{{minor}}
+          type=semver,value=${{ needs.create-github-release.outputs.RELEASED_VERSION }},pattern={{major}}.{{minor}}.{{patch}}
 
     - name: Login to docker.io registry
       run: |
@@ -104,15 +171,16 @@ jobs:
       uses: actions/cache@v2
       with:
         path: /tmp/.buildx-cache
-        key: ${{ runner.os }}-buildx-app-${{ github.sha }}
+        key: ${{ runner.os }}-buildx-app-${{ matrix.flavor }}-${{ github.sha }}
         restore-keys: |
+          ${{ runner.os }}-buildx-app-${{ matrix.flavor }}-
           ${{ runner.os }}-buildx-app-
 
     - name: Build and push
       uses: docker/build-push-action@v2
       with:
         context: .
-        file: ./docker/Dockerfile
+        file: ./packages/app/docker/Dockerfile
         platforms: linux/amd64
         push: true
         cache-from: type=local,src=/tmp/.buildx-cache
@@ -130,14 +198,14 @@ jobs:
         username: wsmoogle
         password: ${{ secrets.DOCKER_REGISTRY_PASSWORD }}
         repository: weseek/growi
-        readme-filepath: ./docker/README.md
+        readme-filepath: ./packages/app/docker/README.md
 
     - name: Slack Notification
       uses: weseek/ghaction-release-slack-notification@master
       with:
         channel: '#release'
         url: ${{ secrets.SLACK_WEBHOOK_URL }}
-        created_tag: 'v${{ needs.github-release.outputs.RELEASE_VERSION }}${{ env.SUFFIX }}'
+        created_tag: 'v${{ needs.create-github-release.outputs.RELEASED_VERSION }}${{ steps.suffix.outputs.SUFFIX }}'
 
     - name: Check whether workspace is clean
       run: |

+ 0 - 30
.gitignore

@@ -8,36 +8,6 @@ node_modules
 # testing
 coverage
 
-# next.js
-/.next/
-/out/
-
-# production
-/build
-
-# dist
-/packages/**/dist/
-/report/
-/public/static/js
-/public/static/styles
-/public/uploads
-/tmp/
-
-# dist (for GROWI v4.x and below)
-/public/*.chunk.js
-/public/*.chunk.js.LICENSE
-/public/*.bundle.js
-/public/manifest.json
-/public/dll
-/public/js
-/public/styles
-/src/*/__build__/
-/__build__/**
-/src/*/dist/
-/.awcache
-.webpack.json
-/compiled/
-
 # misc
 .DS_Store
 *.pem

+ 4 - 0
.markdownlint.yml

@@ -7,3 +7,7 @@ no-multiple-blanks: false
 no-duplicate-heading: false
 no-inline-html: false
 no-trailing-punctuation: false
+
+MD002: false
+MD012: false
+MD041: false

+ 0 - 4
.stylelintrc.json

@@ -2,10 +2,6 @@
   "extends": [
     "stylelint-config-recess-order"
   ],
-  "ignoreFiles": [
-    "src/client/styles/scss/_override-bootstrap-variables.scss",
-    "src/linter-checker/test.scss"
-  ],
   "rules": {
     "indentation": 2,
     "string-quotes": "single",

+ 27 - 6
.vscode/launch.json

@@ -14,10 +14,11 @@
         "type": "node",
         "request": "launch",
         "name": "Debug: Server",
+        "cwd": "${workspaceFolder}/packages/app",
         "runtimeExecutable": "npm",
         "runtimeArgs": [
           "run",
-          "server:nolazy"
+          "dev:server"
         ],
         "port": 9229,
         "restart": true,
@@ -30,9 +31,9 @@
         "name": "Debug: Chrome",
         "sourceMaps": true,
         "sourceMapPathOverrides": {
-          "webpack:///*": "${workspaceFolder}/*"
+          "webpack:///*": "${workspaceFolder}/packages/app/*"
         },
-        "webRoot": "${workspaceFolder}/public",
+        "webRoot": "${workspaceFolder}/packages/app/public",
         "url": "http://localhost:3000"
       },
       {
@@ -41,15 +42,35 @@
         "name": "Debug: Firefox",
         "reAttach": true,
         "url": "http://localhost:3000",
-        "webRoot": "${workspaceFolder}/public",
+        "webRoot": "${workspaceFolder}/packages/app/public",
         "pathMappings": [
+          {
+            "url": "webpack:///core",
+            "path": "${workspaceFolder}/packages/core"
+          },
+          {
+            "url": "webpack:///plugin-attachment-refs",
+            "path": "${workspaceFolder}/packages/plugin-attachment-refs"
+          },
+          {
+            "url": "webpack:///plugin-pukiwiki-like-linker",
+            "path": "${workspaceFolder}/packages/plugin-pukiwiki-like-linker"
+          },
+          {
+            "url": "webpack:///plugin-lsx",
+            "path": "${workspaceFolder}/packages/plugin-lsx"
+          },
+          {
+            "url": "webpack:///ui",
+            "path": "${workspaceFolder}/packages/ui"
+          },
           {
             "url": "webpack:///src",
-            "path": "${workspaceFolder}/src"
+            "path": "${workspaceFolder}/packages/app/src"
           },
           {
             "url": "http://localhost:3000",
-            "path": "${workspaceFolder}/public"
+            "path": "${workspaceFolder}/packages/app/public"
           }
         ]
       }

+ 0 - 19
.vscode/tasks.json

@@ -1,19 +0,0 @@
-{
-    // See https://go.microsoft.com/fwlink/?LinkId=733558
-    // for the documentation about the tasks.json format
-    "version": "2.0.0",
-    "tasks": [
-        {
-            "type": "npm",
-            "script": "build",
-            "problemMatcher": [
-                "$eslint-compact"
-            ]
-        },
-        {
-            "type": "npm",
-            "script": "server",
-            "problemMatcher": []
-        }
-    ]
-}

+ 39 - 2
CHANGES.md → CHANGELOG.md

@@ -1,6 +1,43 @@
-# CHANGES
+# Changelog
 
-## v4.3.2-RC
+## [Unreleased](https://github.com/weseek/growi/compare/v4.3.3...HEAD)
+
+*Please do not manually update this file. We've automated the process.*
+
+### BREAKING CHANGES
+
+* Official plugins are now preinstalled
+
+### Updates
+
+* Feature: Password resetting by user
+* Feature: User trigger notification and Global notification are available by new Slack integration
+* Improvement: Add attachment button in editor navbar
+* Fix: Recursive rename operation from `/parent` to `/parent/child` ([#4101](https://github.com/weseek/growi/pull/4101))
+* Fix: Encode spaces in page path in LinkEditModal
+* Support: Create @growi/core package
+* Support: Create @growi/ui package
+* Support: Improve error handling for @growi/slackbot-proxy
+* Support: Include official plugins as sub packages
+* Support: Upgrade libs
+    * @slack/web-api
+    * date-fns
+    * helmet
+    * morgan
+    * socket.io
+
+## v4.3.3
+
+* Improvement: Welcome page markdown
+* Fix: Some recursive operation exclude descendant pages that are restricted for groups
+    * Rename / Delete / Delete completely / Put back / Duplicate
+* Fix: Layout is broken when editing users page ([#4128](https://github.com/weseek/growi/issues/4128))
+* Support: Upgrade libs
+    * @slack/web-api
+    * date-fns
+    * escape-string-regexp
+
+## v4.3.2
 
 * Feature: Hufflpuff theme
 * Improvement: CodeMirror header styles

+ 5 - 7
README.md

@@ -97,13 +97,11 @@ See [GROWI Docs: Environment Variables](https://docs.growi.org/en/admin-guide/ad
 
 ## Command details
 
-| command                | desc                                                    |
-| ---------------------- | ------------------------------------------------------- |
-| `yarn run build:prod`  | Build the client                                        |
-| `yarn run server:prod` | Launch the server                                       |
-| `yarn start`           | Invoke `yarn run build:prod` and `yarn run server:prod` |
-
-<!-- The following links do not exist -->
+| command           | desc                                                    |
+| ------------------| ------------------------------------------------------- |
+| `yarn app:build`  | Build GROWI app client                                  |
+| `yarn app:server` | Launch GROWI app server                                 |
+| `yarn start`      | Invoke `yarn app:build` and `yarn app:server`           |
 
 For more info, see [GROWI Docs: List of npm Commands](https://docs.growi.org/en/dev/startup-v2/launch-system.html#list-of-npm-commands).
 

+ 5 - 7
README_JP.md

@@ -96,13 +96,11 @@ Crowi からの移行は **[こちら](https://docs.growi.org/en/admin-guide/mig
 
 ## コマンド詳細
 
-| コマンド               | 説明                                                             |
-| ---------------------- | ---------------------------------------------------------------- |
-| `yarn run build:prod`  | クライアントをビルドします。                                     |
-| `yarn run server:prod` | サーバーを起動します。                                           |
-| `yarn start`           | `yarn run build:prod` と `yarn run server:prod` を呼び出します。 |
-
-  <!-- 以下のリンクは存在しない (ja と en 両方) -->
+| コマンド          | 説明                                                    |
+| ------------------| ------------------------------------------------------- |
+| `yarn app:build`  | GROWI app クライアントをビルドします。                  |
+| `yarn app:server` | GROWI app サーバーを起動します。                        |
+| `yarn start`      | `yarn app:build` と `yarn app:server` を呼び出します。  |
 
 詳しくは [GROWI Docs: List of npm Commands](https://docs.growi.org/ja/dev/startup-v2/launch-system.html#npm-コマンドリスト)をご覧ください。
 

+ 0 - 43
app.json

@@ -1,43 +0,0 @@
-{
-  "name": "growi",
-  "description": "Team collaboration system with markdown",
-  "keywords": [
-    "wiki",
-    "communication"
-  ],
-  "repository": "https://github.com/weseek/growi",
-  "success_url": "/",
-  "env": {
-    "NODE_ENV": {
-      "description": "DO NOT CHANGE - 'yarn' needs this to install devDependencies",
-      "value": "development"
-    },
-    "FILE_UPLOAD": {
-      "description": "Attached files storage. - mongodb | aws | local | none",
-      "value": "mongodb",
-      "required": false
-    },
-    "SECRET_TOKEN": {
-      "description": "A secret key for verifying the integrity of signed cookies.",
-      "generator": "secret"
-    },
-    "PASSWORD_SEED": {
-      "description": "A password seed is used by password hash generator. ",
-      "generator": "secret"
-    },
-    "ADDITIONAL_PACKAGES": {
-      "description": "Space-separated list of npm package names to install.",
-      "value": "growi-plugin-lsx growi-plugin-pukiwiki-like-linker growi-plugin-attachment-refs react-images@1.0.0 react-motion",
-      "required": false
-    }
-  },
-  "addons": [
-    "mongolab",
-    {
-      "plan": "searchbox:starter",
-      "options": {
-        "es_version": "6"
-      }
-    }
-  ]
-}

+ 0 - 39
babel.config.js

@@ -1,39 +0,0 @@
-module.exports = function(api) {
-  api.cache(true);
-
-  const presets = [
-    [
-      '@babel/preset-env',
-      {
-        targets: {
-          node: 'current',
-        },
-      },
-    ],
-    '@babel/preset-react',
-  ];
-  const plugins = [
-    'lodash',
-    // transform
-    //  from `import { Button } from 'reactstrap';`
-    //  to   `import Row from 'reactstrap/Button';`
-    [
-      'transform-imports', {
-        reactstrap: {
-          // eslint-disable-next-line no-template-curly-in-string
-          transform: 'reactstrap/es/${member}',
-          preventFullImport: true,
-        },
-      },
-    ],
-    '@babel/plugin-proposal-optional-chaining',
-    [
-      '@babel/plugin-proposal-class-properties', { loose: true },
-    ],
-  ];
-
-  return {
-    presets,
-    plugins,
-  };
-};

+ 4 - 0
bin/bump-versions/README.md

@@ -0,0 +1,4 @@
+bump-versions.js
+==============
+
+Custom cli for bumping package versions based on [algolia/shipjs@0.23.3](https://github.com/algolia/shipjs/tree/v0.23.3/packages/shipjs)

+ 16 - 0
bin/bump-versions/cli.js

@@ -0,0 +1,16 @@
+import { print, parseArgs } from 'shipjs/src/util';
+import bumpVersions from './flow/bump-versions';
+
+export async function cli(argv) {
+  const { fn, arg: argSpec } = bumpVersions;
+  try {
+    const opts = parseArgs(argSpec, argv);
+    await fn(opts);
+  } catch (error) {
+    if (error.code === 'ARG_UNKNOWN_OPTION') {
+      print(error);
+    } else {
+      throw error;
+    }
+  }
+}

+ 71 - 0
bin/bump-versions/flow/bump-versions.js

@@ -0,0 +1,71 @@
+import semver from 'semver';
+import { loadConfig, getCurrentVersion, getReleaseType } from 'shipjs-lib';
+
+import printDryRunBanner from 'shipjs/src/step/printDryRunBanner';
+import confirmNextVersion from 'shipjs/src/step/prepare/confirmNextVersion';
+import updateVersion from 'shipjs/src/step/prepare/updateVersion';
+import updateVersionMonorepo from 'shipjs/src/step/prepare/updateVersionMonorepo';
+import installDependencies from 'shipjs/src/step/prepare/installDependencies';
+
+import printHelp from '../step/printHelp';
+
+async function bumpVersions({
+  help = false,
+  dir = '.',
+  dryRun = false,
+  increment = 'patch',
+  preid = 'RC',
+}) {
+  if (help) {
+    printHelp();
+    return;
+  }
+  if (dryRun) {
+    printDryRunBanner();
+  }
+
+  const config = await loadConfig(dir, 'bump-versions.config');
+  
+  // get current version
+  const { monorepo } = config;
+  const currentVersion =
+    monorepo && monorepo.mainVersionFile
+      ? getCurrentVersion(dir, monorepo.mainVersionFile)
+      : getCurrentVersion(dir);
+  
+  // determine next version
+  let nextVersion = semver.inc(currentVersion, increment, preid); // set preid if type is 'prerelease'
+  nextVersion = await confirmNextVersion({
+    yes: true,
+    currentVersion,
+    nextVersion,
+    dryRun,
+  });
+  const releaseType = getReleaseType(nextVersion);
+
+  // update
+  const updateVersionFn = monorepo
+    ? updateVersionMonorepo
+    : updateVersion;
+  await updateVersionFn({ config, nextVersion, releaseType, dir, dryRun });
+  installDependencies({ config, dir, dryRun });
+}
+
+const arg = {
+  '--dir': String,
+  '--help': Boolean,
+  '--dry-run': Boolean,
+  '--increment': String,
+  '--preid': String,
+
+  // Aliases
+  '-d': '--dir',
+  '-h': '--help',
+  '-D': '--dry-run',
+  '-i': '--increment',
+};
+
+export default {
+  arg,
+  fn: bumpVersions,
+};

+ 12 - 0
bin/bump-versions/index.js

@@ -0,0 +1,12 @@
+#!/usr/bin/env node
+
+require = require('esm')(module);
+(async function() {
+  try {
+    process.env.SHIPJS = true;
+    await require('./cli').cli(process.argv);
+  } catch (e) {
+    console.error(e);
+    process.exit(1);
+  }
+})();

+ 55 - 0
bin/bump-versions/step/printHelp.js

@@ -0,0 +1,55 @@
+import runStep from 'shipjs/src/step/runStep';
+import { print } from 'shipjs/src/util';
+import { bold, underline } from 'shipjs/src/color';
+
+export default () =>
+  runStep({}, () => {
+    const indent = (line) => `\t${line}`;
+
+    const help = `--help`;
+    const dir = `--dir ${underline('PATH')}`;
+    const increment = `--increment ${underline('LEVEL')}`;
+    const preId = `--preid ${underline('IDENTIFIER')}`;
+    const dryRun = `--dry-run`;
+    const all = [help, dir, increment, preId, dryRun]
+      .map((x) => `[${x}]`)
+      .join(' ');
+
+    const messages = [
+      bold('NAME'),
+      indent('bump-versions - Bump versions of packages.'),
+      '',
+      bold('USAGE'),
+      indent(`node ./bin/github-actions/bump-versions ${all}`),
+      '',
+      bold('OPTIONS'),
+      indent(`-h, ${help}`),
+      indent('  Print this help'),
+      '',
+      indent(`-d, ${dir}`),
+      indent(
+        `  Specify the ${underline(
+          'PATH'
+        )} of the repository (default: the current directory).`
+      ),
+      '',
+      indent(`-i, ${increment}`),
+      indent(
+        `  Specify the ${underline(
+          'LEVEL'
+        )} for semver.inc() to increment a version (default: 'patch').`
+      ),
+      '',
+      indent(`${preId}`),
+      indent(
+        `  Specify the ${underline(
+          'IDENTIFIER'
+        )} for semver.inc() with 'prerelease' type (default: 'RC').`
+      ),
+      '',
+      indent(`-D, ${dryRun}`),
+      indent('  Displays the steps without actually doing them.'),
+      '',
+    ];
+    print(messages.join('\n'));
+  });

+ 0 - 43
bin/generate-plugin-definitions-source.js

@@ -1,43 +0,0 @@
-/**
- * the tool for genetion of plugin definitions source code
- *
- * @author Yuki Takei <yuki@weseek.co.jp>
- */
-require('module-alias/register');
-const logger = require('@alias/logger')('growi:bin:generate-plugin-definitions-source');
-
-const fs = require('graceful-fs');
-const normalize = require('normalize-path');
-const swig = require('swig-templates');
-
-const helpers = require('@commons/util/helpers');
-const PluginUtils = require('@server/plugins/plugin-utils');
-
-const pluginUtils = new PluginUtils();
-
-const TEMPLATE = helpers.root('bin/templates/plugin-definitions.js.swig');
-const OUT = helpers.root('tmp/plugins/plugin-definitions.js');
-
-
-// list plugin names
-const pluginNames = pluginUtils.listPluginNames(helpers.root());
-logger.info('Detected plugins: ', pluginNames);
-
-// get definitions
-const definitions = pluginNames
-  .map((name) => {
-    return pluginUtils.generatePluginDefinition(name, true);
-  })
-  .map((definition) => {
-    // convert backslash to slash
-    definition.entries = definition.entries.map((entryPath) => {
-      return normalize(entryPath);
-    });
-    return definition;
-  });
-
-const compiledTemplate = swig.compileFile(TEMPLATE);
-const code = compiledTemplate({ definitions });
-
-// write
-fs.writeFileSync(OUT, code);

+ 0 - 6
bin/github-actions/update-readme.sh

@@ -1,6 +0,0 @@
-#!/bin/sh
-
-cd docker
-
-sed -i -e "s/^\([*] \[\`\)[^\`]\+\(\`, \`4\.3\`, .\+\]\)\(.\+\/blob\/v\).\+\(\/docker\/Dockerfile.\+\)$/\1${RELEASE_VERSION}\2\3${RELEASE_VERSION}\4/" README.md
-sed -i -e "s/^\([*] \[\`\)[^\`]\+\(\`, \`4\.3-nocdn\`, .\+\]\)\(.\+\/blob\/v\).\+\(\/docker\/Dockerfile.\+\)$/\1${RELEASE_VERSION}-nocdn\2\3${RELEASE_VERSION}\4/" README.md

+ 12 - 0
bump-versions.config.js

@@ -0,0 +1,12 @@
+module.exports = {
+  monorepo: {
+    mainVersionFile: 'package.json',
+    packagesToBump: [
+      'packages/app',
+      'packages/core',
+      'packages/slack',
+      'packages/ui',
+      'packages/plugin-*',
+    ],
+  },
+};

+ 0 - 26
config/env.dev.js

@@ -1,26 +0,0 @@
-module.exports = {
-  NODE_ENV: 'development',
-  FILE_UPLOAD: 'mongodb',
-  // MONGO_GRIDFS_TOTAL_LIMIT: 10485760,   // 10MB
-  MATHJAX: 1,
-  // NO_CDN: true,
-  MONGO_URI: 'mongodb://mongo:27017/growi',
-  // REDIS_URI: 'http://redis:6379',
-  // NCHAN_URI: 'http://nchan',
-  ELASTICSEARCH_URI: 'http://elasticsearch:9200/growi',
-  HACKMD_URI: 'http://localhost:3010',
-  HACKMD_URI_FOR_SERVER: 'http://hackmd:3000',
-  // DRAWIO_URI: 'http://localhost:8080/?offline=1&https=0',
-  // S2SMSG_PUBSUB_SERVER_TYPE: 'nchan',
-  // PUBLISH_OPEN_API: true,
-  // USER_UPPER_LIMIT: 0,
-  // DEV_HTTPS: true,
-  // FORCE_WIKI_MODE: 'private', // 'public', 'private', undefined
-  // PROMSTER_ENABLED: true,
-  // SLACK_SIGNING_SECRET: '',
-  // SLACK_BOT_TOKEN: '',
-  SALT_FOR_GTOP_TOKEN: 'proxy',
-  SALT_FOR_PTOG_TOKEN: 'growi',
-  // GROWI_CLOUD_URI: 'http://growi.cloud',
-  // GROWI_APP_ID_FOR_GROWI_CLOUD: '012345',
-};

+ 0 - 4
config/env.prod.js

@@ -1,4 +0,0 @@
-module.exports = {
-  NODE_ENV: 'production',
-  // FORMAT_NODE_LOG: false, // default: true
-};

+ 0 - 13
config/index.js

@@ -1,13 +0,0 @@
-function envShortName() {
-  switch (process.env.NODE_ENV) {
-    case 'production':
-      return 'prod';
-    default:
-      return 'dev';
-  }
-}
-
-module.exports = {
-  env: require(`./env.${envShortName()}`),
-  logger: require(`./logger/config.${envShortName()}`),
-};

+ 0 - 14
docker/docker-entrypoint.sh

@@ -1,14 +0,0 @@
-#!/bin/sh
-
-set -e
-
-# Support `FILE_UPLOAD=local`
-mkdir -p /data/uploads
-if [ ! -e "$appDir/public/uploads" ]; then
-  ln -s /data/uploads $appDir/public/uploads
-fi
-
-chown -R node:node /data/uploads
-chown -h node:node $appDir/public/uploads
-
-gosu node $@

+ 41 - 63
package.json

@@ -1,6 +1,6 @@
 {
   "name": "growi",
-  "version": "4.3.2-RC",
+  "version": "4.4.0-RC",
   "description": "Team collaboration software using markdown",
   "tags": [
     "wiki",
@@ -21,74 +21,52 @@
   },
   "private": true,
   "workspaces": {
-    "packages": ["packages/*"],
-    "nohoist": ["**/slackbot-proxy/bootstrap"]
+    "packages": [
+      "packages/*"
+    ],
+    "nohoist": [
+      "**/slackbot-proxy/bootstrap"
+    ]
   },
   "scripts": {
-    "build:api:jsdoc": "swagger-jsdoc -o tmp/swagger.json -d config/swagger-definition.js",
-    "build:apiv3:jsdoc": "cross-env API_VERSION=3 npm run build:api:jsdoc -- \"src/server/routes/apiv3/**/*.js\" \"src/server/models/**/*.js\"",
-    "build:apiv1:jsdoc": "cross-env API_VERSION=1 npm run build:api:jsdoc -- \"src/server/*/*.js\" \"src/server/models/**/*.js\"",
-    "build:dev:app:watch": "npm run build:dev:app -- --watch",
-    "build:dev:app:watch:poll": "npm run build:dev:app -- --watch --watch-poll",
-    "build:dev:app": "env-cmd -f config/env.dev.js webpack --config config/webpack.dev.js --progress",
-    "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:slack": "lerna run build --scope @growi/slack",
-    "build": "npm run build:dev:watch",
-    "build:poll": "npm run build:dev:watch:poll",
-    "clean:app": "rimraf -- public/js public/styles",
-    "clean:report": "rimraf -- report",
-    "clean": "npm-run-all -p clean:*",
-    "console": "env-cmd -f config/env.dev.js node --experimental-repl-await src/server/console.js",
-    "lint:js:fix": "eslint \"**/*.{js,jsx}\" --fix",
-    "lint:js": "eslint \"**/*.{js,jsx}\"",
-    "lint:styles:fix": "stylelint --fix src/client/styles/scss/**/*.scss",
-    "lint:styles": "stylelint src/client/styles/scss/**/*.scss",
-    "lint:swagger2openapi": "node node_modules/swagger2openapi/oas-validate tmp/swagger.json",
-    "lint": "npm-run-all -p lint:js lint:styles lint:swagger2openapi",
-    "migrate": "npm run migrate:up",
-    "migrate:create": "migrate-mongo create -f config/migrate.js -- ",
-    "migrate:status": "migrate-mongo status -f config/migrate.js",
-    "migrate:up": "migrate-mongo up -f config/migrate.js",
-    "migrate:down": "migrate-mongo down -f config/migrate.js",
-    "plugin:def": "node bin/generate-plugin-definitions-source.js",
-    "prebuild:dev:watch": "npm run prebuild:dev",
-    "prebuild:dev": "npm run clean:app && env-cmd -f config/env.dev.js npm run plugin:def && env-cmd -f config/env.dev.js npm run resource && yarn build:slack",
-    "prelint:swagger2openapi": "npm run build:apiv3:jsdoc",
-    "preserver:prod": "npm run migrate",
-    "prestart": "npm run build:prod",
-    "resource": "node bin/download-cdn-resources.js",
-    "i18n-json-merge:withTran": "rs-i18n -lan -- -t",
-    "i18n-json-merge:noTran": "rs-i18n -lan --",
-    "i18n-json-merge": "npm run i18n-json-merge:withTran --",
-    "server:nolazy": "env-cmd -f config/env.dev.js node-dev --nolazy --inspect src/server/app.js",
-    "server:dev": "env-cmd -f config/env.dev.js node-dev --expose_gc --inspect src/server/app.js",
-    "server:prod:ci": "npm run server:prod -- --ci",
-    "server:prod": "env-cmd -f config/env.prod.js node --expose_gc src/server/app.js",
-    "server": "npm run server:dev",
-    "start": "npm run server:prod",
-    "test": "jest --config=config/jest.config.js --passWithNoTests -- ",
-    "version": "node -p \"require('./package.json').version\"",
-    "webpack": "webpack"
+    "start": "yarn app:server",
+    "prestart": "yarn app:build",
+    "app:build": "yarn lerna run build",
+    "app:server": "yarn lerna run server --scope @growi/app",
+    "slackbot-proxy:build": "yarn lerna run build --scope @growi/slackbot-proxy --scope @growi/slack",
+    "slackbot-proxy:server": "yarn lerna run start:prod --scope @growi/slackbot-proxy",
+    "//// scripts for backward compatibility": "",
+    "build:prod": "echo !!! CAUTION !!! ==> The script 'build:prod' is deprecated. Use 'yarn app:build' instead. && yarn app:build",
+    "server:prod": "echo !!! CAUTION !!! ==> The script 'server:prod' is deprecated. Use 'yarn app:build' instead. && yarn app:server"
   },
   "dependencies": {
+    "cross-env": "^7.0.0",
+    "dotenv-flow": "^3.2.0",
+    "npm-run-all": "^4.1.5",
+    "ts-node": "^9.1.1",
+    "tsconfig-paths": "^3.9.0",
+    "typescript": "^4.2.3"
   },
   "devDependencies": {
-    "lerna": "^4.0.0"
-  },
-  "_moduleAliases": {
-    "@root": ".",
-    "@commons": "src/lib",
-    "@server": "src/server",
-    "@alias/logger": "src/lib/service/logger",
-    "debug": "src/lib/service/logger/alias-for-debug"
-  },
-  "jest": {
-    "moduleNameMapper": {
-      "@commons/(.*)": "<rootDir>/src/lib/$1"
-    }
+    "@types/jest": "^26.0.22",
+    "@types/node": "^14.14.35",
+    "@types/rewire": "^2.5.28",
+    "@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",
+    "jest": "^27.0.6",
+    "jest-date-mock": "^1.0.8",
+    "jest-localstorage-mock": "^2.4.14",
+    "lerna": "^4.0.0",
+    "rewire": "^5.0.0",
+    "shipjs": "^0.23.3",
+    "ts-jest": "^27.0.4"
   },
   "engines": {
     "node": "^12 || ^14",

+ 26 - 0
packages/app/.env.development

@@ -0,0 +1,26 @@
+##
+## Handled by Next.js with dotenv or dotenv-flow
+## https://nextjs.org/docs/basic-features/environment-variables
+##
+FILE_UPLOAD=mongodb
+# MONGO_GRIDFS_TOTAL_LIMIT=10485760
+MATHJAX=1
+# NO_CDN=true
+MONGO_URI="mongodb://mongo:27017/growi"
+# REDIS_URI="http://redis:6379"
+# NCHAN_URI="http://nchan"
+ELASTICSEARCH_URI="http://elasticsearch:9200/growi"
+ELASTICSEARCH_REQUEST_TIMEOUT=15000
+HACKMD_URI="http://localhost:3010"
+HACKMD_URI_FOR_SERVER="http://hackmd:3000"
+# DRAWIO_URI="http://localhost:8080/?offline=1&https=0"
+# S2SMSG_PUBSUB_SERVER_TYPE=nchan
+# PUBLISH_OPEN_API=true
+# USER_UPPER_LIMIT=0
+# DEV_HTTPS=true
+# FORCE_WIKI_MODE=private
+# PROMSTER_ENABLED=true
+# SLACKBOT_WITHOUT_PROXY_SIGNING_SECRET=''
+# SLACKBOT_WITHOUT_PROXY_BOT_TOKEN=''
+# GROWI_CLOUD_URI='http://growi.cloud'
+# GROWI_APP_ID_FOR_GROWI_CLOUD=012345

+ 5 - 0
packages/app/.env.production

@@ -0,0 +1,5 @@
+##
+## Handled by Next.js with dotenv or dotenv-flow
+## https://nextjs.org/docs/basic-features/environment-variables
+##
+FORMAT_NODE_LOG=false

+ 6 - 0
packages/app/.eslintignore

@@ -0,0 +1,6 @@
+/dist/**
+/public/**
+/src/client/legacy/thirdparty-js/**
+/src/client/util/reveal/plugins/markdown.js
+/src/linter-checker/**
+/tmp/**

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

@@ -0,0 +1,35 @@
+module.exports = {
+  extends: [
+    'weseek/react',
+    'weseek/typescript',
+  ],
+  env: {
+    jquery: true,
+  },
+  globals: {
+    $: true,
+    jquery: true,
+    emojione: true,
+    hljs: true,
+    ScrollPosStyler: true,
+    window: true,
+  },
+  settings: {
+    // resolve path aliases by eslint-import-resolver-typescript
+    'import/resolver': {
+      typescript: {},
+    },
+  },
+  rules: {
+    'no-restricted-imports': ['error', {
+      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'],
+    'jest/no-done-callback': ['warn'],
+  },
+};

+ 21 - 0
packages/app/.gitignore

@@ -0,0 +1,21 @@
+# next.js
+/.next/
+/out/
+
+# dist
+/dist/
+/transpiled/
+/public/static/js
+/public/static/styles
+/public/uploads
+/tmp/
+*.d.ts
+
+# dist (for GROWI v4.x and below)
+/public/*.chunk.js
+/public/*.chunk.js.LICENSE.txt
+/public/*.bundle.js
+/public/manifest.json
+/public/dll
+/public/js
+/public/styles

+ 0 - 0
.prettierignore → packages/app/.prettierignore


+ 19 - 0
packages/app/.stylelintrc.json

@@ -0,0 +1,19 @@
+{
+  "extends": [
+    "stylelint-config-recess-order"
+  ],
+  "ignoreFiles": [
+    "src/styles/_override-bootstrap-variables.scss",
+    "src/linter-checker/test.scss"
+  ],
+  "rules": {
+    "indentation": 2,
+    "string-quotes": "single",
+    "rule-empty-line-before": [ "always-multi-line", {
+      "except": ["after-single-line-comment", "first-nested"],
+      "ignore": ["after-comment", "inside-block"]
+    } ],
+    "selector-combinator-space-before": "always",
+    "selector-combinator-space-after": "always"
+  }
+}

+ 158 - 0
packages/app/bin/cdn/cdn-resources-downloader.ts

@@ -0,0 +1,158 @@
+import path from 'path';
+import { URL } from 'url';
+import urljoin from 'url-join';
+import { Transform } from 'stream';
+import replaceStream from 'replacestream';
+
+import { cdnLocalScriptRoot, cdnLocalStyleRoot, cdnLocalStyleWebRoot } from '^/config/cdn';
+import * as cdnManifests from '^/resource/cdn-manifests';
+
+import { CdnResource, CdnManifest } from '~/interfaces/cdn';
+import loggerFactory from '~/utils/logger';
+import { downloadTo } from '~/utils/download';
+
+const logger = loggerFactory('growi:service:CdnResourcesDownloader');
+
+export default class CdnResourcesDownloader {
+
+  async downloadAndWriteAll(): Promise<any> {
+    const cdnScriptResources: CdnResource[] = cdnManifests.js.map((manifest: CdnManifest) => {
+      return { manifest, outDir: cdnLocalScriptRoot };
+    });
+    const cdnStyleResources: CdnResource[] = cdnManifests.style.map((manifest) => {
+      return { manifest, outDir: cdnLocalStyleRoot };
+    });
+
+    const dlStylesOptions = {
+      replaceUrl: {
+        webroot: cdnLocalStyleWebRoot,
+      },
+    };
+
+    return Promise.all([
+      this.downloadScripts(cdnScriptResources),
+      this.downloadStyles(cdnStyleResources, dlStylesOptions),
+    ]);
+  }
+
+  /**
+   * Download script files from CDN
+   * @param cdnResources JavaScript resource data
+   * @param options
+   */
+  private async downloadScripts(cdnResources: CdnResource[], options?: any): Promise<any> {
+    logger.debug('Downloading scripts', cdnResources);
+
+    const opts = Object.assign({}, options);
+    const ext = opts.ext || 'js';
+
+    const promises = cdnResources.map((cdnResource) => {
+      const { manifest } = cdnResource;
+
+      logger.info(`Processing CdnResource '${manifest.name}'`);
+
+      return downloadTo(
+        manifest.url,
+        cdnResource.outDir,
+        `${manifest.name}.${ext}`,
+      );
+    });
+
+    return Promise.all(promises);
+  }
+
+  /**
+   * Download style sheet file from CDN
+   *  Assets in CSS is also downloaded
+   * @param cdnResources CSS resource data
+   * @param options
+   */
+  private async downloadStyles(cdnResources: CdnResource[], options?: any): Promise<any> {
+    logger.debug('Downloading styles', cdnResources);
+
+    const opts = Object.assign({}, options);
+    const ext = opts.ext || 'css';
+
+    // styles
+    const assetsResourcesStore: CdnResource[] = [];
+    const promisesForStyle = cdnResources.map((cdnResource) => {
+      const { manifest } = cdnResource;
+
+      logger.info(`Processing CdnResource '${manifest.name}'`);
+
+      let urlReplacer: Transform|null = null;
+
+      // generate replaceStream instance
+      if (opts.replaceUrl != null) {
+        urlReplacer = this.generateReplaceUrlInCssStream(cdnResource, assetsResourcesStore, opts.replaceUrl.webroot);
+      }
+
+      return downloadTo(
+        manifest.url,
+        cdnResource.outDir,
+        `${manifest.name}.${ext}`,
+        urlReplacer,
+      );
+    });
+
+    // wait until all styles are downloaded
+    await Promise.all(promisesForStyle);
+
+    logger.debug('Downloading assets', assetsResourcesStore);
+
+    // assets in css
+    const promisesForAssets = assetsResourcesStore.map((cdnResource) => {
+      const { manifest } = cdnResource;
+
+      logger.info(`Processing assts in css '${manifest.name}'`);
+
+      return downloadTo(
+        manifest.url,
+        cdnResource.outDir,
+        manifest.name,
+      );
+    });
+
+    return Promise.all(promisesForAssets);
+  }
+
+  /**
+   * Generate replaceStream instance to replace 'url(..)'
+   *
+   * e.g.
+   *  Before  : url(../images/logo.svg)
+   *  After   : url(/path/to/webroot/${cdnResources.name}/logo.svg)
+   *
+   * @param cdnResource CSS resource data
+   * @param assetsResourcesStore An array to store CdnResource that is detected by 'url()' in CSS
+   * @param webroot
+   */
+  private generateReplaceUrlInCssStream(cdnResource: CdnResource, assetsResourcesStore: CdnResource[], webroot: string): Transform {
+    return replaceStream(
+      /url\((?!['"]?data:)["']?(.+?)["']?\)/g, // https://regex101.com/r/Sds38A/3
+      (match, url) => {
+        // generate URL Object
+        const parsedUrl = url.startsWith('http')
+          ? new URL(url) // when url is fqcn
+          : new URL(url, cdnResource.manifest.url); // when url is relative
+        const basename = path.basename(parsedUrl.pathname);
+
+        logger.debug(`${cdnResource.manifest.name} has ${parsedUrl.toString()}`);
+
+        // add assets metadata to download later
+        const replacedCdnResource = {
+          manifest: {
+            name: basename,
+            url: parsedUrl.toString(),
+          },
+          outDir: path.join(cdnResource.outDir, cdnResource.manifest.name),
+        };
+        assetsResourcesStore.push(replacedCdnResource);
+
+        const replaceUrl = urljoin(webroot, cdnResource.manifest.name, basename);
+        return `url(${replaceUrl})`;
+      },
+    );
+  }
+
+}

+ 6 - 8
bin/download-cdn-resources.js → packages/app/bin/download-cdn-resources.ts

@@ -3,14 +3,15 @@
  *
  * @author Yuki Takei <yuki@weseek.co.jp>
  */
-require('module-alias/register');
+import { envUtils } from 'growi-commons';
 
-const logger = require('@alias/logger')('growi:bin:download-cdn-resources');
+import CdnResourcesDownloader from './cdn/cdn-resources-downloader';
+import loggerFactory from '../src/utils/logger';
 
-const { envUtils } = require('growi-commons');
+const logger = loggerFactory('growi:bin:download-cdn-resources');
 
 // check env var
-const noCdn = envUtils.toBoolean(process.env.NO_CDN);
+const noCdn: boolean = envUtils.toBoolean(process.env.NO_CDN);
 if (!noCdn) {
   logger.info('Using CDN. No resources are downloaded.');
   // exit
@@ -19,13 +20,10 @@ if (!noCdn) {
 
 logger.info('This is NO_CDN mode. Start to download resources.');
 
-const CdnResourcesDownloader = require('@commons/service/cdn-resources-downloader');
-const CdnResourcesService = require('@commons/service/cdn-resources-service');
 
 const downloader = new CdnResourcesDownloader();
-const service = new CdnResourcesService();
 
-service.downloadAndWriteAll(downloader)
+downloader.downloadAndWriteAll()
   .then(() => {
     logger.info('Download is completed successfully');
   })

+ 56 - 0
packages/app/bin/generate-plugin-definitions-source.ts

@@ -0,0 +1,56 @@
+/**
+ * the tool for genetion of plugin definitions source code
+ *
+ * @author Yuki Takei <yuki@weseek.co.jp>
+ */
+import fs from 'graceful-fs';
+import normalize from 'normalize-path';
+import swig from 'swig-templates';
+
+import { PluginDefinitionV4 } from '@growi/core';
+
+import PluginUtils from '../src/server/plugins/plugin-utils';
+import loggerFactory from '../src/utils/logger';
+import { resolveFromRoot } from '../src/utils/project-dir-utils';
+
+const logger = loggerFactory('growi:bin:generate-plugin-definitions-source');
+
+
+const pluginUtils = new PluginUtils();
+
+const TEMPLATE = resolveFromRoot('bin/templates/plugin-definitions.js.swig');
+const OUT = resolveFromRoot('tmp/plugins/plugin-definitions.js');
+
+// list plugin names
+const pluginNames: string[] = pluginUtils.listPluginNames();
+logger.info('Detected plugins: ', pluginNames);
+
+async function main(): Promise<void> {
+
+  // get definitions
+  const definitions: PluginDefinitionV4[] = [];
+  for (const pluginName of pluginNames) {
+    // eslint-disable-next-line no-await-in-loop
+    const definition = await pluginUtils.generatePluginDefinition(pluginName, true);
+    if (definition != null) {
+      definitions.push(definition);
+    }
+  }
+
+  definitions.map((definition) => {
+    // convert backslash to slash
+    definition.entries = definition.entries.map((entryPath) => {
+      return normalize(entryPath);
+    });
+    return definition;
+  });
+
+  const compiledTemplate = swig.compileFile(TEMPLATE);
+  const code = compiledTemplate({ definitions });
+
+  // write
+  fs.writeFileSync(OUT, code);
+
+}
+
+main();

+ 6 - 0
packages/app/bin/github-actions/update-readme.sh

@@ -0,0 +1,6 @@
+#!/bin/sh
+
+cd docker
+
+sed -i -e "s/^\([*] \[\`\)[^\`]\+\(\`, \`4\.4\`, .\+\]\)\(.\+\/blob\/v\).\+\(\/docker\/Dockerfile.\+\)$/\1${RELEASED_VERSION}\2\3${RELEASED_VERSION}\4/" README.md
+sed -i -e "s/^\([*] \[\`\)[^\`]\+\(\`, \`4\.4-nocdn\`, .\+\]\)\(.\+\/blob\/v\).\+\(\/docker\/Dockerfile.\+\)$/\1${RELEASED_VERSION}-nocdn\2\3${RELEASED_VERSION}\4/" README.md

+ 2 - 0
bin/shrink-emojione-strategy.js → 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));
+*/

+ 0 - 0
bin/templates/plugin-definitions.js.swig → packages/app/bin/templates/plugin-definitions.js.swig


+ 8 - 0
packages/app/config/cdn.js

@@ -0,0 +1,8 @@
+import path from 'path';
+
+import { projectRoot } from '~/utils/project-dir-utils';
+
+export const cdnLocalScriptRoot = path.join(projectRoot, 'public/static/js/cdn');
+export const cdnLocalScriptWebRoot = '/static/js/cdn';
+export const cdnLocalStyleRoot = path.join(projectRoot, 'public/static/styles/cdn');
+export const cdnLocalStyleWebRoot = '/static/styles/cdn';

+ 4 - 0
packages/app/config/ci/.env.local.for-ci

@@ -0,0 +1,4 @@
+FORMAT_NODE_LOG=true
+
+# disable Elasticsearch
+ELASTICSEARCH_URI=

+ 0 - 0
config/logger/config.dev.js → packages/app/config/logger/config.dev.js


+ 0 - 0
config/logger/config.prod.js → packages/app/config/logger/config.prod.js


+ 5 - 7
config/migrate.js → packages/app/config/migrate.js

@@ -5,11 +5,13 @@
  * @author Yuki Takei <yuki@weseek.co.jp>
  */
 
-require('module-alias/register');
+import mongoose from 'mongoose';
+
+import { initMongooseGlobalSettings, getMongoUri, mongoOptions } from '~/server/util/mongoose-utils';
 
 const { URL } = require('url');
 
-const { getMongoUri } = require('@commons/util/mongoose-utils');
+initMongooseGlobalSettings();
 
 const mongoUri = getMongoUri();
 
@@ -19,11 +21,7 @@ const url = new URL(mongoUri);
 const mongodb = {
   url: mongoUri,
   databaseName: url.pathname.substring(1), // omit heading slash
-  options: {
-    useNewUrlParser: true, // removes a deprecation warning when connecting
-    useUnifiedTopology: true,
-    useFindAndModify: false,
-  },
+  options: mongoOptions,
 };
 
 module.exports = {

+ 0 - 0
config/swagger-definition.js → packages/app/config/swagger-definition.js


+ 58 - 55
config/webpack.common.js → packages/app/config/webpack.common.js

@@ -1,56 +1,55 @@
 /**
  * @author: Yuki Takei <yuki@weseek.co.jp>
  */
+const path = require('path');
 const webpack = require('webpack');
 
 /*
- * Webpack Plugins
- */
+  * Webpack Plugins
+  */
 const WebpackAssetsManifest = require('webpack-assets-manifest');
 const LodashModuleReplacementPlugin = require('lodash-webpack-plugin');
-
-const helpers = require('../src/lib/util/helpers');
+const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
 
 /*
- * Webpack configuration
- *
- * See: http://webpack.github.io/docs/configuration.html#cli
- */
+  * Webpack configuration
+  *
+  * See: http://webpack.github.io/docs/configuration.html#cli
+  */
 module.exports = (options) => {
   return {
     mode: options.mode,
     entry: Object.assign({
-      'js/boot':                      './src/client/js/boot',
-      'js/app':                       './src/client/js/app',
-      'js/admin':                     './src/client/js/admin',
-      'js/nologin':                   './src/client/js/nologin',
-      'js/legacy':                    './src/client/js/legacy/crowi',
-      'js/legacy-presentation':       './src/client/js/legacy/crowi-presentation',
-      'js/plugin':                    './src/client/js/plugin',
-      'js/ie11-polyfill':             './src/client/js/ie11-polyfill',
-      'js/hackmd-agent':              './src/client/js/hackmd-agent',
-      'js/hackmd-styles':             './src/client/js/hackmd-styles',
+      'js/boot':                      './src/client/boot',
+      'js/app':                       './src/client/app',
+      'js/admin':                     './src/client/admin',
+      'js/nologin':                   './src/client/nologin',
+      'js/legacy':                    './src/client/legacy/crowi',
+      'js/legacy-presentation':       './src/client/legacy/crowi-presentation',
+      'js/plugin':                    './src/client/plugin',
+      'js/hackmd-agent':              './src/client/hackmd-agent',
+      'js/hackmd-styles':             './src/client/hackmd-styles',
       // styles
-      'styles/style-app':             './src/client/styles/scss/style-app.scss',
-      'styles/style-presentation':    './src/client/styles/scss/style-presentation.scss',
+      'styles/style-app':             './src/styles/style-app.scss',
+      'styles/style-presentation':    './src/styles/style-presentation.scss',
       // themes
-      'styles/theme-default':         './src/client/styles/scss/theme/default.scss',
-      'styles/theme-nature':          './src/client/styles/scss/theme/nature.scss',
-      'styles/theme-mono-blue':       './src/client/styles/scss/theme/mono-blue.scss',
-      'styles/theme-future':          './src/client/styles/scss/theme/future.scss',
-      'styles/theme-kibela':          './src/client/styles/scss/theme/kibela.scss',
-      'styles/theme-halloween':       './src/client/styles/scss/theme/halloween.scss',
-      'styles/theme-christmas':          './src/client/styles/scss/theme/christmas.scss',
-      'styles/theme-wood':          './src/client/styles/scss/theme/wood.scss',
-      'styles/theme-island':      './src/client/styles/scss/theme/island.scss',
-      'styles/theme-antarctic':      './src/client/styles/scss/theme/antarctic.scss',
-      'styles/theme-spring':         './src/client/styles/scss/theme/spring.scss',
-      'styles/theme-hufflepuff':         './src/client/styles/scss/theme/hufflepuff.scss',
+      'styles/theme-default':         './src/styles/theme/default.scss',
+      'styles/theme-nature':          './src/styles/theme/nature.scss',
+      'styles/theme-mono-blue':       './src/styles/theme/mono-blue.scss',
+      'styles/theme-future':          './src/styles/theme/future.scss',
+      'styles/theme-kibela':          './src/styles/theme/kibela.scss',
+      'styles/theme-halloween':       './src/styles/theme/halloween.scss',
+      'styles/theme-christmas':       './src/styles/theme/christmas.scss',
+      'styles/theme-wood':            './src/styles/theme/wood.scss',
+      'styles/theme-island':          './src/styles/theme/island.scss',
+      'styles/theme-antarctic':       './src/styles/theme/antarctic.scss',
+      'styles/theme-spring':          './src/styles/theme/spring.scss',
+      'styles/theme-hufflepuff':      './src/styles/theme/hufflepuff.scss',
       // styles for external services
-      'styles/style-hackmd':          './src/client/styles/hackmd/style.scss',
+      'styles/style-hackmd':          './src/styles-hackmd/style.scss',
     }, options.entry || {}), // Merge with env dependent settings
     output: Object.assign({
-      path: helpers.root('public'),
+      path: path.resolve(__dirname, '../public'),
       publicPath: '/',
       filename: '[name].bundle.js',
     }, options.output || {}), // Merge with env dependent settings
@@ -60,34 +59,38 @@ module.exports = (options) => {
       jquery: 'jQuery',
       emojione: 'emojione',
       hljs: 'hljs',
+      'dtrace-provider': 'dtrace-provider',
     },
     resolve: {
-      extensions: ['.js', '.jsx', '.json'],
-      modules: ((options.resolve && options.resolve.modules) || []).concat([helpers.root('node_modules')]),
-      alias: {
-        '@root': helpers.root('/'),
-        '@commons': helpers.root('src/lib'),
-        '@client': helpers.root('src/client'),
-        '@tmp': helpers.root('tmp'),
-        '@alias/logger': helpers.root('src/lib/service/logger'),
-        // replace bunyan
-        bunyan: 'browser-bunyan',
-      },
+      extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'],
+      plugins: [
+        new TsconfigPathsPlugin({
+          configFile: path.resolve(__dirname, '../tsconfig.build.client.json'),
+          extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'],
+        }),
+      ],
+    },
+    node: {
+      fs: 'empty',
     },
     module: {
       rules: options.module.rules.concat([
         {
-          test: /.jsx?$/,
+          test: /.(jsx?|tsx?)$/,
           exclude: {
-            test:    helpers.root('node_modules'),
+            test: /node_modules/,
             exclude: [ // include as a result
-              { test: helpers.root('node_modules', 'growi-plugin-') },
-              helpers.root('node_modules/growi-commons'),
-              helpers.root('node_modules/codemirror/src'),
+              { test: /node_modules\/growi-plugin-/ },
+              /node_modules\/growi-commons/,
+              /node_modules\/codemirror/,
             ],
           },
           use: [{
-            loader: 'babel-loader?cacheDirectory',
+            loader: 'ts-loader',
+            options: {
+              transpileOnly: true,
+              configFile: path.resolve(__dirname, '../tsconfig.build.client.json'),
+            },
           }],
         },
         {
@@ -98,14 +101,14 @@ module.exports = (options) => {
           },
         },
         /*
-         * File loader for supporting images, for example, in CSS files.
-         */
+          * File loader for supporting images, for example, in CSS files.
+          */
         {
           test: /\.(jpg|png|gif)$/,
           use: 'file-loader',
         },
         /* File loader for supporting fonts, for example, in CSS files.
-        */
+         */
         {
           test: /\.(eot|woff2?|svg|ttf)([?]?.*)$/,
           use: 'null-loader',
@@ -167,7 +170,7 @@ module.exports = (options) => {
             test: /node_modules[\\/].*\.(js|jsx|json)$/,
             chunks: (chunk) => {
               // ignore patterns
-              return chunk.name != null && !chunk.name.match(/boot|legacy-presentation|ie11-polyfill|hackmd-/);
+              return chunk.name != null && !chunk.name.match(/boot|legacy-presentation|hackmd-/);
             },
             name: 'js/vendors',
             minSize: 1,

+ 4 - 4
config/webpack.dev.dll.js → packages/app/config/webpack.dev.dll.js

@@ -1,8 +1,8 @@
 /**
  * @author: Yuki Takei <yuki@weseek.co.jp>
  */
+const path = require('path');
 const webpack = require('webpack');
-const helpers = require('../src/lib/util/helpers');
 
 
 module.exports = {
@@ -34,17 +34,17 @@ module.exports = {
     ],
   },
   output: {
-    path: helpers.root('public/dll'),
+    path: path.resolve(__dirname, '../public/dll'),
     filename: 'dll.js',
     library: 'growi_dlls',
   },
   resolve: {
     extensions: ['.js', '.json'],
-    modules: [helpers.root('src'), helpers.root('node_modules')],
+    modules: [path.resolve(__dirname, '../src'), path.resolve(__dirname, '../node_modules')],
   },
   plugins: [
     new webpack.DllPlugin({
-      path: helpers.root('public/dll/manifest.json'),
+      path: path.resolve(__dirname, '../public/dll/manifest.json'),
       name: 'growi_dlls',
     }),
   ],

+ 7 - 7
config/webpack.dev.js → packages/app/config/webpack.dev.js

@@ -2,6 +2,8 @@
  * @author: Yuki Takei <yuki@weseek.co.jp>
  */
 
+const path = require('path');
+
 /*
  * Webpack Plugins
  */
@@ -9,8 +11,6 @@ const MiniCssExtractPlugin = require('mini-css-extract-plugin');
 const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
 const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
 
-const helpers = require('../src/lib/util/helpers');
-
 /**
  * Webpack Constants
  */
@@ -20,7 +20,7 @@ module.exports = require('./webpack.common')({
   mode: 'development',
   devtool: 'cheap-module-eval-source-map',
   entry: {
-    'js/dev': './src/client/js/dev',
+    'js/dev': './src/client/dev',
   },
   resolve: {
     modules: ['../node_modules'],
@@ -35,8 +35,8 @@ module.exports = require('./webpack.common')({
           { loader: 'sass-loader', options: { sourceMap: true } },
         ],
         exclude: [
-          helpers.root('src/client/styles/hackmd'),
-          helpers.root('src/client/styles/scss/style-presentation.scss'),
+          path.resolve(__dirname, '../src/styles-hackmd'),
+          path.resolve(__dirname, '../src/styles/style-presentation.scss'),
         ],
       },
       { // Dump CSS for HackMD
@@ -47,8 +47,8 @@ module.exports = require('./webpack.common')({
           'sass-loader',
         ],
         include: [
-          helpers.root('src/client/styles/hackmd'),
-          helpers.root('src/client/styles/scss/style-presentation.scss'),
+          path.resolve(__dirname, '../src/styles-hackmd'),
+          path.resolve(__dirname, '../src/styles/style-presentation.scss'),
         ],
       },
     ],

+ 10 - 10
config/webpack.prod.js → packages/app/config/webpack.prod.js

@@ -2,19 +2,19 @@
  * @author: Yuki Takei <yuki@weseek.co.jp>
  */
 
+const path = require('path');
+
 /**
- * Webpack Plugins
- */
+  * Webpack Plugins
+  */
 const TerserPlugin = require('terser-webpack-plugin');
 const MiniCssExtractPlugin = require('mini-css-extract-plugin');
 const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
 const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
 
-const helpers = require('../src/lib/util/helpers');
-
 /**
- * Webpack Constants
- */
+  * Webpack Constants
+  */
 const { ANALYZE } = process.env;
 
 module.exports = require('./webpack.common')({
@@ -35,7 +35,7 @@ module.exports = require('./webpack.common')({
             loader: 'postcss-loader',
             options: {
               sourceMap: false,
-              plugins: (loader) => {
+              plugins: () => {
                 return [
                   require('autoprefixer')(),
                 ];
@@ -44,12 +44,12 @@ module.exports = require('./webpack.common')({
           },
           'sass-loader',
         ],
-        exclude: [helpers.root('src/client/js/legacy')],
+        exclude: [path.resolve(__dirname, '../src/client/legacy')],
       },
       {
         test: /\.(css|scss)$/,
         use: ['style-loader', 'css-loader', 'sass-loader'],
-        include: [helpers.root('src/client/js/legacy')],
+        include: [path.resolve(__dirname, '../src/client/legacy')],
       },
     ],
   },
@@ -61,7 +61,7 @@ module.exports = require('./webpack.common')({
 
     new BundleAnalyzerPlugin({
       analyzerMode: ANALYZE ? 'static' : 'disabled',
-      reportFilename: helpers.root('report/bundle-analyzer.html'),
+      reportFilename: path.resolve(__dirname, '../report/bundle-analyzer.html'),
       openAnalyzer: false,
     }),
 

+ 33 - 25
docker/Dockerfile → packages/app/docker/Dockerfile

@@ -17,16 +17,21 @@ COPY ./package.json .
 COPY ./yarn.lock .
 COPY ./lerna.json .
 COPY ./packages/app/package.json packages/app/
+COPY ./packages/core/package.json packages/core/
+COPY ./packages/plugin-attachment-refs/package.json packages/plugin-attachment-refs/
+COPY ./packages/plugin-lsx/package.json packages/plugin-lsx/
+COPY ./packages/plugin-pukiwiki-like-linker/package.json packages/plugin-pukiwiki-like-linker/
 COPY ./packages/slack/package.json packages/slack/
+COPY ./packages/ui/package.json packages/ui/
 
 # setup
 RUN yarn config set network-timeout 300000
 RUN npx lerna bootstrap
 
 # make artifacts
-RUN tar cf node_modules.tar node_modules \
-  packages/app/node_modules \
-  packages/slack/node_modules
+RUN tar cf node_modules.tar \
+  node_modules \
+  packages/*/node_modules
 
 
 
@@ -39,9 +44,9 @@ FROM deps-resolver AS deps-resolver-prod
 RUN yarn install --production
 
 # make artifacts
-RUN tar cf node_modules.tar node_modules \
-  packages/app/node_modules \
-  packages/slack/node_modules
+RUN tar cf node_modules.tar \
+  node_modules \
+  packages/*/node_modules
 
 
 
@@ -88,16 +93,14 @@ COPY ./package.json ./
 COPY ./yarn.lock ./
 COPY ./lerna.json ./
 COPY ./tsconfig.base.json ./
-COPY ./babel.config.js ./
-COPY ./bin ./bin
-COPY ./config ./config
-COPY ./public ./public
-COPY ./resource ./resource
-COPY ./src ./src
-COPY ./tmp ./tmp
 # copy all related packages
-COPY packages/slack packages/slack
 COPY packages/app packages/app
+COPY packages/core packages/core
+COPY packages/plugin-attachment-refs packages/plugin-attachment-refs
+COPY packages/plugin-lsx packages/plugin-lsx
+COPY packages/plugin-pukiwiki-like-linker packages/plugin-pukiwiki-like-linker
+COPY packages/slack packages/slack
+COPY packages/ui packages/ui
 
 # build
 RUN yarn lerna run build
@@ -106,15 +109,16 @@ RUN yarn lerna run build
 RUN tar cf packages.tar \
   package.json \
   yarn.lock \
-  config \
-  public \
-  resource \
-  src \
-  tmp \
-  packages/app/package.json \
-  packages/slack/package.json \
-  packages/slack/dist
-
+  tsconfig.base.json \
+  packages/app/config \
+  packages/app/public \
+  packages/app/resource \
+  packages/app/tmp \
+  packages/app/.env.production \
+  packages/app/tsconfig.base.json \
+  packages/app/tsconfig.json \
+  packages/*/package.json \
+  packages/*/dist
 
 
 
@@ -124,6 +128,8 @@ RUN tar cf packages.tar \
 FROM node:14-slim
 LABEL maintainer Yuki Takei <yuki@weseek.co.jp>
 
+ENV NODE_ENV production
+
 ENV appDir /opt/growi
 
 # Add gosu
@@ -154,12 +160,14 @@ RUN rm node_modules.tar packages.tar
 
 USER root
 
-COPY docker/docker-entrypoint.sh /
+COPY packages/app/docker/docker-entrypoint.sh /
 RUN chmod 700 /docker-entrypoint.sh
 RUN chown node:node ${appDir}
 
+WORKDIR ${appDir}/packages/app
+
 VOLUME /data
 EXPOSE 3000
 
 ENTRYPOINT ["/tini", "-e", "143", "--", "/docker-entrypoint.sh"]
-CMD ["yarn", "server:prod"]
+CMD ["node", "-r", "dotenv-flow/config", "--expose_gc", "dist/server/app.js"]

+ 5 - 5
docker/README.md → packages/app/docker/README.md

@@ -2,7 +2,7 @@
 GROWI Official docker image
 ========================
 
-[![Actions Status](https://github.com/weseek/growi/workflows/Release%20Docker%20Images/badge.svg)](https://github.com/weseek/growi/actions) [![docker-pulls](https://img.shields.io/docker/pulls/weseek/growi.svg)](https://hub.docker.com/r/weseek/growi/) [![](https://images.microbadger.com/badges/image/weseek/growi.svg)](https://microbadger.com/images/weseek/growi)
+[![Actions Status](https://github.com/weseek/growi/workflows/Release/badge.svg)](https://github.com/weseek/growi/actions) [![docker-pulls](https://img.shields.io/docker/pulls/weseek/growi.svg)](https://hub.docker.com/r/weseek/growi/) [![](https://images.microbadger.com/badges/image/weseek/growi.svg)](https://microbadger.com/images/weseek/growi)
 
 ![GROWI-x-docker](https://user-images.githubusercontent.com/1638767/38307565-105956e2-384f-11e8-8534-b1128522d68d.png)
 
@@ -10,10 +10,10 @@ GROWI Official docker image
 Supported tags and respective Dockerfile links
 ------------------------------------------------
 
-* [`4.3.0`, `4.3`, `4`, `latest` (Dockerfile)](https://github.com/weseek/growi/blob/v4.3.0/docker/Dockerfile)
-* [`4.3.0-nocdn`, `4.3-nocdn`, `4-nocdn`, `latest-nocdn` (Dockerfile)](https://github.com/weseek/growi/blob/v4.3.0/docker/Dockerfile)
-* [`4.2.0`, `4.2` (Dockerfile)](https://github.com/weseek/growi/blob/v4.2.0/docker/Dockerfile)
-* [`4.2.0-nocdn`, `4.2-nocdn` (Dockerfile)](https://github.com/weseek/growi/blob/v4.2.0/docker/Dockerfile)
+* [`4.4.0`, `4.4`, `4`, `latest` (Dockerfile)](https://github.com/weseek/growi/blob/v4.4.0/docker/Dockerfile)
+* [`4.4.0-nocdn`, `4.4-nocdn`, `4-nocdn`, `latest-nocdn` (Dockerfile)](https://github.com/weseek/growi/blob/v4.4.0/docker/Dockerfile)
+* [`4.3.3`, `4.3` (Dockerfile)](https://github.com/weseek/growi/blob/v4.3.3/docker/Dockerfile)
+* [`4.3.3-nocdn`, `4.3-nocdn` (Dockerfile)](https://github.com/weseek/growi/blob/v4.3.3/docker/Dockerfile)
 
 
 What is GROWI?

+ 14 - 0
packages/app/docker/docker-entrypoint.sh

@@ -0,0 +1,14 @@
+#!/bin/sh
+
+set -e
+
+# Support `FILE_UPLOAD=local`
+mkdir -p /data/uploads
+if [ ! -e "./public/uploads" ]; then
+  ln -s /data/uploads ./public/uploads
+fi
+
+chown -R node:node /data/uploads
+chown -h node:node ./public/uploads
+
+gosu node $@

+ 0 - 0
docker/nocdn/env.prod.js → packages/app/docker/nocdn/env.prod.js


+ 24 - 17
config/jest.config.js → packages/app/jest.config.js

@@ -1,33 +1,35 @@
 // For a detailed explanation regarding each configuration property, visit:
 // https://jestjs.io/docs/en/configuration.html
+// https://kulshekhar.github.io/ts-jest/user/config/
 
 const MODULE_NAME_MAPPING = {
-  '@root/(.+)': '<rootDir>/$1',
-  '@commons/(.+)': '<rootDir>/src/lib/$1',
-  '@server/(.+)': '<rootDir>/src/server/$1',
-  '@alias/logger': '<rootDir>/src/lib/service/logger',
-  // -- doesn't work with unknown error -- 2019.06.19 Yuki Takei
-  // debug: '<rootDir>/src/lib/service/logger/alias-for-debug',
+  '^\\^/(.+)$': '<rootDir>/$1',
+  '^~/(.+)$': '<rootDir>/src/$1',
+  '^@growi/(.+)$': '<rootDir>/../$1/src',
 };
 
 module.exports = {
   // Indicates whether each individual test should be reported during the run
   verbose: true,
 
-  rootDir: '../',
+  preset: 'ts-jest/presets/js-with-ts',
+
   globalSetup: '<rootDir>/src/test/global-setup.js',
   globalTeardown: '<rootDir>/src/test/global-teardown.js',
 
   projects: [
     {
       displayName: 'server',
-      testEnvironment: 'node',
+
+      preset: 'ts-jest/presets/js-with-ts',
+
       rootDir: '.',
+      roots: ['<rootDir>/src'],
+      testEnvironment: 'node',
       setupFilesAfterEnv: ['<rootDir>/src/test/setup.js'],
-      testMatch: ['<rootDir>/src/test/**/*.test.js'],
+      testMatch: ['<rootDir>/src/test/**/*.test.ts', '<rootDir>/src/test/**/*.test.js'],
       // Automatically clear mock calls and instances between every test
       clearMocks: true,
-      // A map from regular expressions to module names that allow to stub out resources with a single module
       moduleNameMapper: MODULE_NAME_MAPPING,
     },
     // {
@@ -37,19 +39,24 @@ module.exports = {
     // },
   ],
 
+  // Automatically clear mock calls and instances between every test
+  clearMocks: true,
 
   // Indicates whether the coverage information should be collected while executing the test
-  // collectCoverage: false,
+  collectCoverage: true,
 
   // An array of glob patterns indicating a set of files for which coverage information should be collected
-  collectCoverageFrom: [
-    'src/client/**/*.js',
-    'src/lib/**/*.js',
-    'src/migrations/**/*.js',
-    'src/server/**/*.js',
-  ],
+  // collectCoverageFrom: undefined,
 
   // The directory where Jest should output its coverage files
   coverageDirectory: 'coverage',
 
+  // An array of regexp pattern strings used to skip coverage collection
+  coveragePathIgnorePatterns: [
+    'index.ts',
+    '/config/',
+    '/resource/',
+    '/node_modules/',
+  ],
+
 };

+ 69 - 48
packages/app/package.json

@@ -1,23 +1,67 @@
 {
   "name": "@growi/app",
-  "version": "0.9.0-RC",
+  "version": "4.4.0-RC",
   "license": "MIT",
   "scripts": {
-    "build": "cd ../../ && env-cmd -f config/env.prod.js webpack --config config/webpack.prod.js --profile --bail",
-    "prebuild": "cd ../../ && yarn plugin:def && yarn resource"
+    "//// for production": "",
+    "start": "yarn build && yarn server",
+    "build": "run-p build:*",
+    "build:client": "yarn cross-env NODE_ENV=production webpack --config config/webpack.prod.js --profile --bail",
+    "build:server": "yarn cross-env NODE_ENV=production tsc -p tsconfig.build.server.json && tsc-alias -p tsconfig.build.server.json",
+    "clean": "npx shx rm -rf dist transpiled",
+    "prebuild": "yarn cross-env NODE_ENV=production run-p clean resources:*",
+    "postbuild": "npx shx mv transpiled/src dist && npx shx cp -r src/server/views dist/server/ && npx shx rm -rf transpiled",
+    "server": "yarn cross-env NODE_ENV=production node -r dotenv-flow/config --expose_gc dist/server/app.js",
+    "server:ci": "yarn server --ci",
+    "preserver": "yarn cross-env NODE_ENV=production yarn migrate",
+    "//// for development": "",
+    "dev": "run-p dev:client dev:server",
+    "dev:client": "yarn cross-env NODE_ENV=development webpack --config config/webpack.dev.js --progress --watch",
+    "dev:client:nowatch": "yarn cross-env NODE_ENV=development webpack --config config/webpack.dev.js",
+    "dev:server": "yarn cross-env NODE_ENV=development ts-node-dev --inspect --expose-gc -r tsconfig-paths/register -r dotenv-flow/config --transpile-only src/server/app.ts",
+    "predev:client": "yarn cross-env NODE_ENV=development run-p resources:*",
+    "predev:server": "yarn cross-env NODE_ENV=development yarn migrate",
+    "//// for CI": "",
+    "dev:ci": "yarn dev:client:nowatch && yarn dev:server --ci",
+    "predev:ci": "run-p resources:*",
+    "lint:typecheck": "npx tsc",
+    "lint:eslint": "eslint --quiet \"**/*.{js,jsx,ts,tsx}\"",
+    "lint:styles": "stylelint src/**/*.scss",
+    "lint:swagger2openapi": "node node_modules/.bin/oas-validate tmp/swagger.json",
+    "lint": "run-p lint:*",
+    "test": "cross-env NODE_ENV=test jest --passWithNoTests -- ",
+    "prelint:eslint": "yarn resources:plugin",
+    "prelint:swagger2openapi": "yarn openapi:v3",
+    "//// misc": "",
+    "console": "yarn cross-env NODE_ENV=development yarn ts-node --experimental-repl-await src/server/console.js",
+    "swagger-jsdoc": "swagger-jsdoc -o tmp/swagger.json -d config/swagger-definition.js",
+    "openapi:v3": "yarn cross-env API_VERSION=3 yarn swagger-jsdoc -- \"src/server/routes/apiv3/**/*.js\" \"src/server/models/**/*.js\"",
+    "openapi:v1": "yarn cross-env API_VERSION=1 yarn swagger-jsdoc -- \"src/server/*/*.js\" \"src/server/models/**/*.js\"",
+    "resources:plugin": "yarn ts-node bin/generate-plugin-definitions-source.ts",
+    "resources:dl-resources": "yarn ts-node bin/download-cdn-resources.ts",
+    "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",
+    "migrate:up": "yarn ts-node node_modules/.bin/migrate-mongo up -f config/migrate.js",
+    "migrate:down": "yarn ts-node node_modules/.bin/migrate-mongo down -f config/migrate.js",
+    "ts-node": "ts-node -r tsconfig-paths/register -r dotenv-flow/config --transpile-only"
   },
   "// comments for dependencies": {
     "openid-client": "Node.js 12 or higher is required for openid-client@3 and above.",
-    "string-width": "5.0.0 or above uses ESM."
+    "escape-string-regexp": "5.0.0 or above exports only ESM",
+    "string-width": "5.0.0 or above exports only ESM."
   },
   "dependencies": {
+    "@browser-bunyan/console-formatted-stream": "^1.6.2",
     "@google-cloud/storage": "^5.8.5",
-    "@growi/slack": "^0.9.0-RC",
-    "@kobalab/socket.io-session": "^1.0.3",
+    "@growi/plugin-attachment-refs": "^4.4.0-RC",
+    "@growi/plugin-pukiwiki-like-linker": "^4.4.0-RC",
+    "@growi/plugin-lsx": "^4.4.0-RC",
+    "@growi/slack": "^4.4.0-RC",
     "@promster/express": "^5.0.1",
     "@promster/server": "^6.0.0",
     "@slack/events-api": "^3.0.0",
-    "@slack/web-api": "^6.2.3",
+    "@slack/web-api": "^6.2.4",
     "@slack/webhook": "^6.0.0",
     "JSONStream": "^1.3.5",
     "archiver": "^5.3.0",
@@ -28,35 +72,31 @@
     "body-parser": "^1.18.2",
     "browser-bunyan": "^1.6.3",
     "bunyan": "^1.8.15",
-    "bunyan-format": "^0.2.1",
     "check-node-version": "^4.1.0",
     "connect-flash": "~0.1.1",
     "connect-mongo": "^4.4.1",
     "connect-redis": "^4.0.4",
     "cookie-parser": "^1.4.5",
-    "cross-env": "^7.0.0",
     "csrf": "^3.1.0",
-    "date-fns": "^2.0.0",
+    "date-fns": "^2.23.0",
     "detect-indent": "^6.0.0",
     "diff": "^5.0.0",
     "elasticsearch": "^16.0.0",
     "entities": "^2.0.0",
-    "env-cmd": "^10.0.1",
     "esa-nodejs": "^0.0.7",
-    "escape-string-regexp": "^2.0.0",
+    "escape-string-regexp": "=4.0.0",
     "express": "^4.16.1",
     "express-bunyan-logger": "^1.3.3",
     "express-form": "~0.12.0",
     "express-mongo-sanitize": "^2.1.0",
+    "express-rate-limit": "^5.3.0",
     "express-session": "^1.16.1",
     "express-validator": "^6.1.1",
     "express-webpack-assets": "^0.1.0",
     "graceful-fs": "^4.1.11",
     "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",
+    "helmet": "^4.6.0",
+    "nocache": "^3.0.1",
     "http-errors": "~1.6.2",
     "i18next": "^20.3.2",
     "i18next-express-middleware": "^2.0.0",
@@ -68,7 +108,6 @@
     "method-override": "^3.0.0",
     "migrate-mongo": "^8.2.2",
     "mkdirp": "^1.0.3",
-    "module-alias": "^2.0.6",
     "mongoose": "5.12.13",
     "mongoose-gridfs": "^1.2.42",
     "mongoose-paginate-v2": "^1.3.9",
@@ -77,9 +116,7 @@
     "multer-autoreap": "^1.0.3",
     "nodemailer": "^6.6.2",
     "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",
@@ -94,9 +131,10 @@
     "reconnecting-websocket": "^4.4.0",
     "redis": "^3.0.2",
     "rimraf": "^3.0.0",
-    "socket.io": "^2.3.0",
+    "socket.io": "^4.2.0",
     "stream-to-promise": "^3.0.0",
     "string-width": "=4.2.2",
+    "swagger-jsdoc": "^3.4.0",
     "swig-templates": "^2.0.2",
     "uglifycss": "^0.0.29",
     "universal-bunyan": "^0.9.2",
@@ -108,30 +146,21 @@
   },
   "// comments for defDependencies": {
     "@handsontable/react": "v3 requires handsontable >= 7.0.0.",
-    "handsontable": "v7.0.0 or above is no loger MIT lisence."
+    "handsontable": "v7.0.0 or above is no loger MIT lisence.",
+    "ts-loader": "v9 is not compatible with webpack@5"
   },
   "devDependencies": {
     "@alienfast/i18next-loader": "^1.0.16",
     "@atlaskit/drawer": "^5.3.7",
     "@atlaskit/navigation-next": "^8.0.5",
-    "@babel/core": "^7.4.5",
-    "@babel/plugin-proposal-class-properties": "^7.8.3",
-    "@babel/plugin-proposal-optional-chaining": "^7.9.0",
-    "@babel/polyfill": "^7.4.4",
-    "@babel/preset-env": "^7.4.5",
-    "@babel/preset-react": "^7.0.0",
+    "@growi/ui": "^4.4.0-RC",
     "@handsontable/react": "=2.1.0",
     "@types/compression": "^1.7.0",
     "@types/express": "^4.17.11",
     "@types/multer": "^1.4.5",
-    "@types/node": "^14.14.35",
+    "@types/react-dom": "^17.0.9",
     "autoprefixer": "^9.0.0",
-    "babel-eslint": "^10.0.1",
-    "babel-loader": "^8.0.6",
-    "babel-plugin-lodash": "^3.3.4",
-    "babel-plugin-transform-imports": "^2.0.0",
     "bootstrap": "^4.5.0",
-    "browser-bunyan": "^1.6.3",
     "browser-sync": "^2.26.3",
     "bunyan-debug": "^2.0.0",
     "cli": "~1.0.1",
@@ -143,19 +172,11 @@
     "csv-to-markdown-table": "^1.0.1",
     "diff2html": "^3.1.2",
     "eazy-logger": "^3.0.2",
-    "eslint": "^6.0.1",
-    "eslint-config-weseek": "^1.0.8",
-    "eslint-plugin-import": "^2.18.0",
-    "eslint-plugin-jest": "^23.0.3",
-    "eslint-plugin-react": "^7.14.2",
-    "eslint-plugin-react-hooks": "^4.0.4",
     "file-loader": "^5.0.2",
     "handsontable": "=6.2.2",
     "hard-source-webpack-plugin": "^0.13.1",
     "i18next-browser-languagedetector": "^4.0.1",
     "imports-loader": "^0.8.0",
-    "jest": "^25.1.0",
-    "jest-date-mock": "^1.0.8",
     "jquery-slimscroll": "^1.3.8",
     "jquery-ui": "^1.12.1",
     "jquery.cookie": "~1.4.1",
@@ -173,7 +194,7 @@
     "markdown-it-toc-and-anchor-with-slugid": "^1.1.4",
     "markdown-table": "^1.1.1",
     "mini-css-extract-plugin": "^0.9.0",
-    "morgan": "^1.9.0",
+    "morgan": "^1.10.0",
     "node-dev": "^4.0.0",
     "node-sass": "^4.14.1",
     "normalize-path": "^3.0.0",
@@ -193,26 +214,26 @@
     "react-frame-component": "^4.0.0",
     "react-hotkeys": "^2.0.0",
     "react-i18next": "^11.1.0",
-    "react-images": "1.0.0",
-    "react-motion": "^0.5.2",
-    "react-waypoint": "^9.0.0",
+    "react-waypoint": "^10.1.0",
     "reactstrap": "^8.9.0",
     "replacestream": "^4.0.3",
     "reveal.js": "^3.5.0",
-    "rs-i18n": "^0.0.9",
     "sass-loader": "^8.0.0",
     "simple-load-script": "^1.0.2",
-    "socket.io-client": "^2.3.0",
+    "socket.io-client": "^4.2.0",
     "sticky-events": "^3.1.3",
     "style-loader": "^1.0.0",
     "styled-components": "^5.0.1",
     "stylelint": "^13.2.0",
     "stylelint-config-recess-order": "^2.0.1",
-    "swagger-jsdoc": "^3.4.0",
     "swagger2openapi": "^5.3.1",
     "terser-webpack-plugin": "^4.1.0",
     "throttle-debounce": "^2.0.0",
     "toastr": "^2.1.2",
+    "ts-loader": "^8.3.0",
+    "ts-node-dev": "^1.1.6",
+    "tsc-alias": "^1.2.9",
+    "tsconfig-paths-webpack-plugin": "^3.5.1",
     "unstated": "^2.1.1",
     "webpack": "^4.39.3",
     "webpack-assets-manifest": "^3.1.1",

+ 0 - 0
public/favicon.ico → packages/app/public/favicon.ico


+ 0 - 0
public/images/customize-settings/default-dark.svg → packages/app/public/images/customize-settings/default-dark.svg


+ 0 - 0
public/images/customize-settings/default-light.svg → packages/app/public/images/customize-settings/default-light.svg


+ 0 - 0
public/images/customize-settings/fluid-dark.svg → packages/app/public/images/customize-settings/fluid-dark.svg


+ 0 - 0
public/images/customize-settings/fluid-light.svg → packages/app/public/images/customize-settings/fluid-light.svg


+ 0 - 0
public/images/file-not-found.png → packages/app/public/images/file-not-found.png


+ 0 - 0
public/images/icons/editor/bold.svg → packages/app/public/images/icons/editor/bold.svg


+ 0 - 0
public/images/icons/editor/check.svg → packages/app/public/images/icons/editor/check.svg


+ 0 - 0
public/images/icons/editor/code.svg → packages/app/public/images/icons/editor/code.svg


+ 0 - 0
public/images/icons/editor/header.svg → packages/app/public/images/icons/editor/header.svg


+ 0 - 0
public/images/icons/editor/italic.svg → packages/app/public/images/icons/editor/italic.svg


+ 0 - 0
public/images/icons/editor/link.svg → packages/app/public/images/icons/editor/link.svg


+ 0 - 0
public/images/icons/editor/list-ol.svg → packages/app/public/images/icons/editor/list-ol.svg


+ 0 - 0
public/images/icons/editor/list-ul.svg → packages/app/public/images/icons/editor/list-ul.svg


+ 0 - 0
public/images/icons/editor/picture.svg → packages/app/public/images/icons/editor/picture.svg


+ 0 - 0
public/images/icons/editor/quote.svg → packages/app/public/images/icons/editor/quote.svg


+ 0 - 0
public/images/icons/editor/strikethrough.svg → packages/app/public/images/icons/editor/strikethrough.svg


+ 0 - 0
public/images/icons/editor/table.svg → packages/app/public/images/icons/editor/table.svg


+ 0 - 0
public/images/icons/emacs.png → packages/app/public/images/icons/emacs.png


+ 0 - 0
public/images/icons/favicon/android-icon-144x144.png → packages/app/public/images/icons/favicon/android-icon-144x144.png


+ 0 - 0
public/images/icons/favicon/android-icon-192x192.png → packages/app/public/images/icons/favicon/android-icon-192x192.png


+ 0 - 0
public/images/icons/favicon/android-icon-36x36.png → packages/app/public/images/icons/favicon/android-icon-36x36.png


+ 0 - 0
public/images/icons/favicon/android-icon-48x48.png → packages/app/public/images/icons/favicon/android-icon-48x48.png


+ 0 - 0
public/images/icons/favicon/android-icon-72x72.png → packages/app/public/images/icons/favicon/android-icon-72x72.png


+ 0 - 0
public/images/icons/favicon/android-icon-96x96.png → packages/app/public/images/icons/favicon/android-icon-96x96.png


+ 0 - 0
public/images/icons/favicon/apple-icon-114x114.png → packages/app/public/images/icons/favicon/apple-icon-114x114.png


+ 0 - 0
public/images/icons/favicon/apple-icon-120x120.png → packages/app/public/images/icons/favicon/apple-icon-120x120.png


+ 0 - 0
public/images/icons/favicon/apple-icon-144x144.png → packages/app/public/images/icons/favicon/apple-icon-144x144.png


+ 0 - 0
public/images/icons/favicon/apple-icon-152x152.png → packages/app/public/images/icons/favicon/apple-icon-152x152.png


+ 0 - 0
public/images/icons/favicon/apple-icon-180x180.png → packages/app/public/images/icons/favicon/apple-icon-180x180.png


+ 0 - 0
public/images/icons/favicon/apple-icon-57x57.png → packages/app/public/images/icons/favicon/apple-icon-57x57.png


+ 0 - 0
public/images/icons/favicon/apple-icon-60x60.png → packages/app/public/images/icons/favicon/apple-icon-60x60.png


+ 0 - 0
public/images/icons/favicon/apple-icon-72x72.png → packages/app/public/images/icons/favicon/apple-icon-72x72.png


+ 0 - 0
public/images/icons/favicon/apple-icon-76x76.png → packages/app/public/images/icons/favicon/apple-icon-76x76.png


Некоторые файлы не были показаны из-за большого количества измененных файлов