Sfoglia il codice sorgente

Merge pull request #4084 from weseek/support/make-app-monorepo

Support/make app monorepo
Yuki Takei 4 anni fa
parent
commit
f4fc5ed1fa
100 ha cambiato i file con 542 aggiunte e 436 eliminazioni
  1. 1 9
      .eslintignore
  2. 8 15
      .eslintrc.js
  3. 5 8
      .github/workflows/ci-slackbot-proxy.yml
  4. 28 23
      .github/workflows/ci.yml
  5. 0 30
      .gitignore
  6. 0 4
      .stylelintrc.json
  7. 3 1
      CHANGES.md
  8. 5 7
      README.md
  9. 5 7
      README_JP.md
  10. 0 43
      app.json
  11. 0 39
      babel.config.js
  12. 0 26
      config/env.dev.js
  13. 0 4
      config/env.prod.js
  14. 0 13
      config/index.js
  15. 18 58
      package.json
  16. 27 0
      packages/app/.env.development
  17. 5 0
      packages/app/.env.production
  18. 5 0
      packages/app/.eslintignore
  19. 35 0
      packages/app/.eslintrc.js
  20. 21 0
      packages/app/.gitignore
  21. 0 0
      packages/app/.prettierignore
  22. 19 0
      packages/app/.stylelintrc.json
  23. 158 0
      packages/app/bin/cdn/cdn-resources-downloader.ts
  24. 6 8
      packages/app/bin/download-cdn-resources.ts
  25. 17 12
      packages/app/bin/generate-plugin-definitions-source.ts
  26. 0 0
      packages/app/bin/github-actions/update-readme.sh
  27. 2 0
      packages/app/bin/shrink-emojione-strategy.js
  28. 0 0
      packages/app/bin/templates/plugin-definitions.js.swig
  29. 8 0
      packages/app/config/cdn.js
  30. 2 0
      packages/app/config/ci/.env.local.for-ci
  31. 0 0
      packages/app/config/logger/config.dev.js
  32. 0 0
      packages/app/config/logger/config.prod.js
  33. 1 3
      packages/app/config/migrate.js
  34. 0 0
      packages/app/config/swagger-definition.js
  35. 54 53
      packages/app/config/webpack.common.js
  36. 4 4
      packages/app/config/webpack.dev.dll.js
  37. 7 7
      packages/app/config/webpack.dev.js
  38. 10 10
      packages/app/config/webpack.prod.js
  39. 0 0
      packages/app/docker/Dockerfile
  40. 0 0
      packages/app/docker/README.md
  41. 0 0
      packages/app/docker/docker-entrypoint.sh
  42. 0 0
      packages/app/docker/nocdn/env.prod.js
  43. 23 17
      packages/app/jest.config.js
  44. 65 35
      packages/app/package.json
  45. 0 0
      packages/app/public/favicon.ico
  46. 0 0
      packages/app/public/images/customize-settings/default-dark.svg
  47. 0 0
      packages/app/public/images/customize-settings/default-light.svg
  48. 0 0
      packages/app/public/images/customize-settings/fluid-dark.svg
  49. 0 0
      packages/app/public/images/customize-settings/fluid-light.svg
  50. 0 0
      packages/app/public/images/file-not-found.png
  51. 0 0
      packages/app/public/images/icons/editor/bold.svg
  52. 0 0
      packages/app/public/images/icons/editor/check.svg
  53. 0 0
      packages/app/public/images/icons/editor/code.svg
  54. 0 0
      packages/app/public/images/icons/editor/header.svg
  55. 0 0
      packages/app/public/images/icons/editor/italic.svg
  56. 0 0
      packages/app/public/images/icons/editor/link.svg
  57. 0 0
      packages/app/public/images/icons/editor/list-ol.svg
  58. 0 0
      packages/app/public/images/icons/editor/list-ul.svg
  59. 0 0
      packages/app/public/images/icons/editor/picture.svg
  60. 0 0
      packages/app/public/images/icons/editor/quote.svg
  61. 0 0
      packages/app/public/images/icons/editor/strikethrough.svg
  62. 0 0
      packages/app/public/images/icons/editor/table.svg
  63. 0 0
      packages/app/public/images/icons/emacs.png
  64. 0 0
      packages/app/public/images/icons/favicon/android-icon-144x144.png
  65. 0 0
      packages/app/public/images/icons/favicon/android-icon-192x192.png
  66. 0 0
      packages/app/public/images/icons/favicon/android-icon-36x36.png
  67. 0 0
      packages/app/public/images/icons/favicon/android-icon-48x48.png
  68. 0 0
      packages/app/public/images/icons/favicon/android-icon-72x72.png
  69. 0 0
      packages/app/public/images/icons/favicon/android-icon-96x96.png
  70. 0 0
      packages/app/public/images/icons/favicon/apple-icon-114x114.png
  71. 0 0
      packages/app/public/images/icons/favicon/apple-icon-120x120.png
  72. 0 0
      packages/app/public/images/icons/favicon/apple-icon-144x144.png
  73. 0 0
      packages/app/public/images/icons/favicon/apple-icon-152x152.png
  74. 0 0
      packages/app/public/images/icons/favicon/apple-icon-180x180.png
  75. 0 0
      packages/app/public/images/icons/favicon/apple-icon-57x57.png
  76. 0 0
      packages/app/public/images/icons/favicon/apple-icon-60x60.png
  77. 0 0
      packages/app/public/images/icons/favicon/apple-icon-72x72.png
  78. 0 0
      packages/app/public/images/icons/favicon/apple-icon-76x76.png
  79. 0 0
      packages/app/public/images/icons/favicon/apple-icon-precomposed.png
  80. 0 0
      packages/app/public/images/icons/favicon/apple-icon.png
  81. 0 0
      packages/app/public/images/icons/favicon/browserconfig.xml
  82. 0 0
      packages/app/public/images/icons/favicon/favicon-16x16.png
  83. 0 0
      packages/app/public/images/icons/favicon/favicon-32x32.png
  84. 0 0
      packages/app/public/images/icons/favicon/favicon-96x96.png
  85. 0 0
      packages/app/public/images/icons/favicon/manifest.json
  86. 0 0
      packages/app/public/images/icons/favicon/ms-icon-144x144.png
  87. 0 0
      packages/app/public/images/icons/favicon/ms-icon-150x150.png
  88. 0 0
      packages/app/public/images/icons/favicon/ms-icon-310x310.png
  89. 0 0
      packages/app/public/images/icons/favicon/ms-icon-70x70.png
  90. 0 0
      packages/app/public/images/icons/fx.svg
  91. 0 0
      packages/app/public/images/icons/slack/mark-color.svg
  92. 0 0
      packages/app/public/images/icons/slack/mark-monochrome_black.svg
  93. 0 0
      packages/app/public/images/icons/slack/mark-monochrome_white.svg
  94. 0 0
      packages/app/public/images/icons/slack/slack-logo-dark-off.svg
  95. 0 0
      packages/app/public/images/icons/slack/slack-logo-dark-on.svg
  96. 0 0
      packages/app/public/images/icons/slack/slack-logo-off.svg
  97. 0 0
      packages/app/public/images/icons/slack/slack-logo-on.svg
  98. 0 0
      packages/app/public/images/icons/sublime.png
  99. 0 0
      packages/app/public/images/icons/user.svg
  100. 0 0
      packages/app/public/images/icons/vim.png

+ 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/**

+ 8 - 15
.eslintrc.js

@@ -1,37 +1,30 @@
 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/..., ...
   },
 };

+ 5 - 8
.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:
 
@@ -55,9 +49,12 @@ 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
@@ -202,7 +199,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

+ 28 - 23
.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:
 
@@ -60,9 +49,12 @@ 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-pukiwiki-like-linker
+    - name: lerna run lint for app
+      run: |
+        yarn lerna run lint --scope @growi/app
 
     - name: Slack Notification
       uses: weseek/ghaction-slack-notification@master
@@ -126,11 +118,13 @@ jobs:
         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:
@@ -147,13 +141,19 @@ 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 }}
@@ -203,9 +203,13 @@ 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.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 +222,7 @@ jobs:
         url: ${{ secrets.SLACK_WEBHOOK_URL }}
 
 
-  build-prod:
+  launch-prod:
     runs-on: ubuntu-latest
 
     strategy:
@@ -277,8 +281,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/slack --scope @growi/app
     - name: lerna bootstrap --production
       run: |
         npx lerna bootstrap -- --production
@@ -291,14 +294,16 @@ 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
+        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
+        yarn server:ci
       env:
         MONGO_URI: mongodb://localhost:${{ job.services.mongodb36.ports['27017'] }}/growi-${{ steps.getdbname.outputs.suffix }}
     - name: Upload report as artifact

+ 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

+ 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",

+ 3 - 1
CHANGES.md

@@ -2,9 +2,11 @@
 
 ## v4.3.3-RC
 
-*
 * Support: Upgrade libs
+    * @slack/web-api
     * escape-string-regexp
+    * morgan
+
 ## v4.3.2
 
 * Feature: Hufflpuff theme

+ 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,
-  };
-};

+ 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()}`),
-};

+ 18 - 58
package.json

@@ -25,71 +25,31 @@
     "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 -- ",
+    "start": "yarn app:server",
+    "prestart": "yarn app:build",
+    "app:build": "yarn lerna run build --scope @growi/app --scope @growi/slack --scope @growi/plugin-pukiwiki-like-linker",
+    "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",
     "version": "node -p \"require('./package.json').version\"",
-    "webpack": "webpack"
+    "//// 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": {
   },
   "devDependencies": {
+    "@typescript-eslint/eslint-plugin": "^4.28.5",
+    "@typescript-eslint/parser": "^4.28.5",
+    "eslint": "^7.31.0",
+    "eslint-config-weseek": "^1.1.0",
+    "eslint-import-resolver-typescript": "^2.4.0",
+    "eslint-plugin-import": "^2.23.4",
+    "eslint-plugin-jest": "^24.3.2",
+    "eslint-plugin-react": "^7.24.0",
+    "eslint-plugin-react-hooks": "^4.2.0",
     "lerna": "^4.0.0"
   },
-  "_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"
-    }
-  },
   "engines": {
     "node": "^12 || ^14",
     "npm": ">=6.11.3 <7",

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

@@ -0,0 +1,27 @@
+##
+## 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 # 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

+ 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 # default: true

+ 5 - 0
packages/app/.eslintignore

@@ -0,0 +1,5 @@
+/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
+/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');
   })

+ 17 - 12
bin/generate-plugin-definitions-source.js → packages/app/bin/generate-plugin-definitions-source.ts

@@ -3,24 +3,24 @@
  *
  * @author Yuki Takei <yuki@weseek.co.jp>
  */
-require('module-alias/register');
-const logger = require('@alias/logger')('growi:bin:generate-plugin-definitions-source');
+import fs from 'graceful-fs';
+import normalize from 'normalize-path';
+import swig from 'swig-templates';
 
-const fs = require('graceful-fs');
-const normalize = require('normalize-path');
-const swig = require('swig-templates');
+import PluginUtils from '../src/server/plugins/plugin-utils';
+import loggerFactory from '../src/utils/logger';
+import { resolveFromRoot } from '../src/utils/project-dir-utils';
 
-const helpers = require('@commons/util/helpers');
-const PluginUtils = require('@server/plugins/plugin-utils');
+const logger = loggerFactory('growi:bin:generate-plugin-definitions-source');
 
-const pluginUtils = new PluginUtils();
 
-const TEMPLATE = helpers.root('bin/templates/plugin-definitions.js.swig');
-const OUT = helpers.root('tmp/plugins/plugin-definitions.js');
+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 = pluginUtils.listPluginNames(helpers.root());
+const pluginNames: string[] = pluginUtils.listPluginNames();
 logger.info('Detected plugins: ', pluginNames);
 
 // get definitions
@@ -29,12 +29,17 @@ const definitions = pluginNames
     return pluginUtils.generatePluginDefinition(name, true);
   })
   .map((definition) => {
+    if (definition == null) {
+      return null;
+    }
+
     // convert backslash to slash
     definition.entries = definition.entries.map((entryPath) => {
       return normalize(entryPath);
     });
     return definition;
-  });
+  })
+  .filter(definition => definition != null);
 
 const compiledTemplate = swig.compileFile(TEMPLATE);
 const code = compiledTemplate({ definitions });

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


+ 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';

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

@@ -0,0 +1,2 @@
+# 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


+ 1 - 3
config/migrate.js → packages/app/config/migrate.js

@@ -5,11 +5,9 @@
  * @author Yuki Takei <yuki@weseek.co.jp>
  */
 
-require('module-alias/register');
-
 const { URL } = require('url');
 
-const { getMongoUri } = require('@commons/util/mongoose-utils');
+const { getMongoUri } = require('~/server/util/mongoose-utils');
 
 const mongoUri = getMongoUri();
 

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


+ 54 - 53
config/webpack.common.js → packages/app/config/webpack.common.js

@@ -1,56 +1,54 @@
 /**
  * @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');
-
 /*
- * 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 +58,37 @@ 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')]),
+      extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'],
+      // modules: ((options.resolve && options.resolve.modules) || []).concat([path.resolve(__dirname, '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',
+        '~': path.resolve(__dirname, '../src'), // src
+        '^': path.resolve(__dirname, '../'), // project root
       },
     },
+    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 +99,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 +168,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,
     }),
 

+ 0 - 0
docker/Dockerfile → packages/app/docker/Dockerfile


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


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


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


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

@@ -1,33 +1,34 @@
 // 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',
 };
 
 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 +38,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/',
+  ],
+
 };

+ 65 - 35
packages/app/package.json

@@ -1,23 +1,66 @@
 {
   "name": "@growi/app",
-  "version": "0.9.0-RC",
+  "version": "4.3.3-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": "cross-env NODE_ENV=production webpack --config config/webpack.prod.js --profile --bail",
+    "build:server": "cross-env NODE_ENV=production tsc -p tsconfig.build.server.json && tsc-alias -p tsconfig.build.server.json",
+    "prebuild": "run-p resources:*",
+    "postbuild": "npx shx mv transpiled/src dist && npx shx rm -r transpiled",
+    "server": "cross-env NODE_ENV=production node -r dotenv-flow/config --expose_gc dist/server/app.js",
+    "server:ci": "yarn server --ci",
+    "preserver": "yarn migrate",
+    "//// for development": "",
+    "dev": "run-p dev:client dev:server",
+    "dev:client": "cross-env NODE_ENV=development webpack --config config/webpack.dev.js --progress --watch",
+    "dev:client:nowatch": "cross-env NODE_ENV=development webpack --config config/webpack.dev.js",
+    "dev:server": "cross-env NODE_ENV=development yarn ts-node-dev src/server/app.ts --expose_gc --inspect",
+    "predev:client": "run-p resources:*",
+    "predev:server": "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": "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": "cross-env API_VERSION=3 yarn swagger-jsdoc -- \"src/server/routes/apiv3/**/*.js\" \"src/server/models/**/*.js\"",
+    "openapi:v1": "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",
+    "ts-node-dev": "ts-node-dev -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",
+    "@growi/plugin-pukiwiki-like-linker": "^4.3.3-RC",
+    "@growi/slack": "^4.3.3-RC",
     "@kobalab/socket.io-session": "^1.0.3",
     "@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,7 +71,6 @@
     "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",
@@ -39,11 +81,11 @@
     "date-fns": "^2.0.0",
     "detect-indent": "^6.0.0",
     "diff": "^5.0.0",
+    "dotenv-flow": "^3.2.0",
     "elasticsearch": "^16.0.0",
     "entities": "^2.0.0",
-    "env-cmd": "^10.0.1",
     "esa-nodejs": "^0.0.7",
-    "escape-string-regexp": "^5.0.0",
+    "escape-string-regexp": "=4.0.0",
     "express": "^4.16.1",
     "express-bunyan-logger": "^1.3.3",
     "express-form": "~0.12.0",
@@ -55,7 +97,6 @@
     "growi-commons": "^5.0.4",
     "growi-plugin-attachment-refs": "^2.0.2",
     "growi-plugin-lsx": "^4.0.3",
-    "growi-plugin-pukiwiki-like-linker": "^3.1.0",
     "helmet": "^3.13.0",
     "http-errors": "~1.6.2",
     "i18next": "^20.3.2",
@@ -68,7 +109,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",
@@ -79,7 +119,6 @@
     "nodemailer-ses-transport": "~1.5.0",
     "npm-run-all": "^4.1.2",
     "openid-client": "=2.5.0",
-    "package-installed-version-sync": "^2.1.0",
     "passport": "^0.4.0",
     "passport-github": "^1.1.0",
     "passport-google-oauth20": "^2.0.0",
@@ -97,7 +136,11 @@
     "socket.io": "^2.3.0",
     "stream-to-promise": "^3.0.0",
     "string-width": "=4.2.2",
+    "swagger-jsdoc": "^3.4.0",
     "swig-templates": "^2.0.2",
+    "ts-node": "^9.1.1",
+    "tsconfig-paths": "^3.9.0",
+    "typescript": "^4.2.3",
     "uglifycss": "^0.0.29",
     "universal-bunyan": "^0.9.2",
     "unzipper": "^0.10.5",
@@ -108,30 +151,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",
     "@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,18 +177,12 @@
     "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": "^27.0.6",
     "jest-date-mock": "^1.0.8",
     "jquery-slimscroll": "^1.3.8",
     "jquery-ui": "^1.12.1",
@@ -173,7 +201,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",
@@ -195,11 +223,10 @@
     "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",
@@ -208,11 +235,14 @@
     "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-jest": "^27.0.4",
+    "ts-loader": "^8.3.0",
+    "ts-node-dev": "^1.1.6",
+    "tsc-alias": "^1.2.9",
     "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


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


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


+ 0 - 0
public/images/icons/favicon/browserconfig.xml → packages/app/public/images/icons/favicon/browserconfig.xml


+ 0 - 0
public/images/icons/favicon/favicon-16x16.png → packages/app/public/images/icons/favicon/favicon-16x16.png


+ 0 - 0
public/images/icons/favicon/favicon-32x32.png → packages/app/public/images/icons/favicon/favicon-32x32.png


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


+ 0 - 0
public/images/icons/favicon/manifest.json → packages/app/public/images/icons/favicon/manifest.json


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


+ 0 - 0
public/images/icons/favicon/ms-icon-150x150.png → packages/app/public/images/icons/favicon/ms-icon-150x150.png


+ 0 - 0
public/images/icons/favicon/ms-icon-310x310.png → packages/app/public/images/icons/favicon/ms-icon-310x310.png


+ 0 - 0
public/images/icons/favicon/ms-icon-70x70.png → packages/app/public/images/icons/favicon/ms-icon-70x70.png


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


+ 0 - 0
public/images/icons/slack/mark-color.svg → packages/app/public/images/icons/slack/mark-color.svg


+ 0 - 0
public/images/icons/slack/mark-monochrome_black.svg → packages/app/public/images/icons/slack/mark-monochrome_black.svg


+ 0 - 0
public/images/icons/slack/mark-monochrome_white.svg → packages/app/public/images/icons/slack/mark-monochrome_white.svg


+ 0 - 0
public/images/icons/slack/slack-logo-dark-off.svg → packages/app/public/images/icons/slack/slack-logo-dark-off.svg


+ 0 - 0
public/images/icons/slack/slack-logo-dark-on.svg → packages/app/public/images/icons/slack/slack-logo-dark-on.svg


+ 0 - 0
public/images/icons/slack/slack-logo-off.svg → packages/app/public/images/icons/slack/slack-logo-off.svg


+ 0 - 0
public/images/icons/slack/slack-logo-on.svg → packages/app/public/images/icons/slack/slack-logo-on.svg


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


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


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


Some files were not shown because too many files changed in this diff