diff --git a/.github/ISSUE_TEMPLATE/BUG_REPORT.md b/.github/ISSUE_TEMPLATE/BUG_REPORT.md index 8abc249ecc..6549f17461 100644 --- a/.github/ISSUE_TEMPLATE/BUG_REPORT.md +++ b/.github/ISSUE_TEMPLATE/BUG_REPORT.md @@ -20,7 +20,7 @@ https://github.com/strapi/strapi/blob/main/CONTRIBUTING.md#reporting-an-issue ### Required System information - + diff --git a/.github/actions/check-pr-status/README.md b/.github/actions/check-pr-status/README.md index d1e428d4d8..0ef40e9fe7 100644 --- a/.github/actions/check-pr-status/README.md +++ b/.github/actions/check-pr-status/README.md @@ -19,7 +19,7 @@ This action checks a PR labels, milestone and status to validate it is ready for ### Requirements -- The code is compatible with Node 16, 18, and 20 +- The code is compatible with Node 18, and 20 ### Dependencies diff --git a/.github/actions/check-pr-status/action.yml b/.github/actions/check-pr-status/action.yml index 44327b5b23..3eb6dcdc4b 100644 --- a/.github/actions/check-pr-status/action.yml +++ b/.github/actions/check-pr-status/action.yml @@ -1,5 +1,5 @@ name: 'PR Checker' description: 'Check PR status for mergeability' runs: - using: 'node16' + using: 'node20' main: 'dist/index.js' diff --git a/.github/actions/run-api-tests/script.sh b/.github/actions/run-api-tests/script.sh index 476388e991..406f68c8c1 100755 --- a/.github/actions/run-api-tests/script.sh +++ b/.github/actions/run-api-tests/script.sh @@ -11,6 +11,5 @@ export JWT_SECRET="aSecret" opts=($DB_OPTIONS) jestOptions=($JEST_OPTIONS) -yarn nx run-many --target=build --nx-ignore-cycles --skip-nx-cache -yarn run test:generate-app --appPath=test-apps/api "${opts[@]}" +yarn run test:generate-app:no-build --appPath=test-apps/api "${opts[@]}" yarn run test:api --no-generate-app "${jestOptions[@]}" diff --git a/.github/actions/run-build/action.yml b/.github/actions/run-build/action.yml new file mode 100644 index 0000000000..fc3257e9e6 --- /dev/null +++ b/.github/actions/run-build/action.yml @@ -0,0 +1,19 @@ +name: 'Monorepo build (yarn)' +description: 'Run yarn build with cache enabled' + +runs: + using: 'composite' + + steps: + + - name: ♻️ Restore build cache + uses: actions/cache@v3 + id: yarn-build-cache + with: + path: packages/**/dist + key: yarn-build-cache-${{ github.sha }} + - if: ${{ steps.yarn-build-cache.outputs.cache-hit != 'true' }} + name: 📥 Run build + shell: bash + run: yarn nx run-many --target=build --nx-ignore-cycles --skip-nx-cache + \ No newline at end of file diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index a33dace4a1..dad2009465 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -18,7 +18,7 @@ jobs: run: echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > .npmrc - uses: actions/setup-node@v3 with: - node-version: 16 + node-version: 20 - run: yarn - run: ./scripts/pre-publish.sh --yes env: diff --git a/.github/workflows/publish-prerelease.yml b/.github/workflows/publish-prerelease.yml index 387f883a95..56f6a1b28a 100644 --- a/.github/workflows/publish-prerelease.yml +++ b/.github/workflows/publish-prerelease.yml @@ -21,7 +21,7 @@ jobs: run: echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > .npmrc - uses: actions/setup-node@v3 with: - node-version: 16 + node-version: 20 - run: yarn - run: ./scripts/pre-publish.sh --yes env: diff --git a/.github/workflows/remove-dist-tag.yml b/.github/workflows/remove-dist-tag.yml index 16319f93c6..aecf68080d 100644 --- a/.github/workflows/remove-dist-tag.yml +++ b/.github/workflows/remove-dist-tag.yml @@ -21,7 +21,7 @@ jobs: run: echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > .npmrc - uses: actions/setup-node@v3 with: - node-version: 16 + node-version: 20 - run: yarn - run: ./scripts/remove-dist-tag.sh env: diff --git a/.github/workflows/skipped_tests.yml b/.github/workflows/skipped_tests.yml index 358002ace7..9e52e6ce3d 100644 --- a/.github/workflows/skipped_tests.yml +++ b/.github/workflows/skipped_tests.yml @@ -42,7 +42,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node: [16, 18, 20] + node: [18, 20] steps: - run: echo "Skipped" @@ -62,7 +62,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - node: [16, 18, 20] + node: [18, 20] steps: - run: echo "Skipped" @@ -72,7 +72,7 @@ jobs: name: '[CE] API Integration (postgres, node: ${{ matrix.node }})' strategy: matrix: - node: [16, 18, 20] + node: [18, 20] steps: - run: echo "Skipped" @@ -82,7 +82,7 @@ jobs: name: '[CE] API Integration (mysql, node: ${{ matrix.node }})' strategy: matrix: - node: [16, 18, 20] + node: [18, 20] steps: - run: echo "Skipped" @@ -92,7 +92,7 @@ jobs: name: '[CE] API Integration (mysql:5 , node: ${{ matrix.node }})' strategy: matrix: - node: [16, 18, 20] + node: [18, 20] steps: - run: echo "Skipped" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5bb0f1653d..48d69213cc 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -35,30 +35,39 @@ jobs: filters: .github/filters.yaml lint: - name: 'lint (node: ${{ matrix.node }})' - needs: [changes] + name: 'lint (node: 20)' runs-on: ubuntu-latest - strategy: - matrix: - node: [18] steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: actions/setup-node@v3 with: - node-version: ${{ matrix.node }} + node-version: 20 - uses: nrwl/nx-set-shas@v3 - name: Monorepo install uses: ./.github/actions/yarn-nm-install - - name: Run build - run: yarn nx run-many --target=build --nx-ignore-cycles --skip-nx-cache + - name: Monorepo build + uses: ./.github/actions/run-build - name: Run lint run: yarn nx affected --target=lint --parallel --nx-ignore-cycles + build: + name: 'build (node: 20)' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v3 + with: + node-version: 20 + - name: Monorepo install + uses: ./.github/actions/yarn-nm-install + - name: Monorepo build + uses: ./.github/actions/run-build + typescript: name: 'typescript' - needs: [changes] + needs: [build] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -70,8 +79,8 @@ jobs: - uses: nrwl/nx-set-shas@v3 - name: Monorepo install uses: ./.github/actions/yarn-nm-install - - name: Run build - run: yarn nx run-many --target=build --nx-ignore-cycles --skip-nx-cache + - name: Monorepo build + uses: ./.github/actions/run-build - name: TSC for packages run: yarn nx affected --target=test:ts --nx-ignore-cycles - name: TSC for back @@ -81,11 +90,11 @@ jobs: unit_back: name: 'unit_back (node: ${{ matrix.node }})' - needs: [changes, lint, typescript] + needs: [changes, build] runs-on: ubuntu-latest strategy: matrix: - node: [16, 18, 20] + node: [18, 20] steps: - uses: actions/checkout@v4 with: @@ -96,14 +105,14 @@ jobs: - uses: nrwl/nx-set-shas@v3 - name: Monorepo install uses: ./.github/actions/yarn-nm-install - - name: Run build - run: yarn build --skip-nx-cache + - name: Monorepo build + uses: ./.github/actions/run-build - name: Run tests run: yarn nx affected --target=test:unit --nx-ignore-cycles unit_front: name: 'unit_front (node: ${{ matrix.node }})' - needs: [changes, lint, typescript] + needs: [changes, build] runs-on: ubuntu-latest strategy: matrix: @@ -118,31 +127,14 @@ jobs: - uses: nrwl/nx-set-shas@v3 - name: Monorepo install uses: ./.github/actions/yarn-nm-install - - name: Run build:ts for admin-test-utils & helper-plugin - run: yarn build --projects=@strapi/admin-test-utils,@strapi/helper-plugin --skip-nx-cache + - name: Monorepo build + uses: ./.github/actions/run-build - name: Run test run: yarn nx affected --target=test:front --nx-ignore-cycles - build: - name: 'build (node: ${{ matrix.node }})' - needs: [changes, lint, typescript, unit_front] - runs-on: ubuntu-latest - strategy: - matrix: - node: [16, 18, 20] - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.node }} - - name: Monorepo install - uses: ./.github/actions/yarn-nm-install - - name: Build - run: yarn build --projects=@strapi/admin,@strapi/helper-plugin - e2e: timeout-minutes: 60 - needs: [changes, lint, typescript, unit_front, build] + needs: [changes, build, typescript, unit_front] name: 'e2e (browser: ${{ matrix.project }})' runs-on: ubuntu-latest strategy: @@ -164,8 +156,8 @@ jobs: - name: Install Playwright Browsers run: npx playwright@1.38.1 install --with-deps - - name: Run build - run: yarn nx run-many --target=build --nx-ignore-cycles --skip-nx-cache + - name: Monorepo build + uses: ./.github/actions/run-build - name: Run E2E tests run: yarn test:e2e --setup --concurrency=1 --project=${{ matrix.project }} @@ -180,12 +172,12 @@ jobs: api_ce_pg: if: needs.changes.outputs.backend == 'true' runs-on: ubuntu-latest - needs: [changes, lint, typescript, unit_back, unit_front] + needs: [changes, build, typescript, unit_back, unit_front] name: '[CE] API Integration (postgres, node: ${{ matrix.node }}, shard: ${{ matrix.shard }})' strategy: matrix: - node: [16, 18, 20] - shard: [1/2, 2/2] + node: [18, 20] + shard: [1/5, 2/5, 3/5, 4/5, 5/5] services: postgres: # Docker Hub image @@ -212,6 +204,8 @@ jobs: node-version: ${{ matrix.node }} - name: Monorepo install uses: ./.github/actions/yarn-nm-install + - name: Monorepo build + uses: ./.github/actions/run-build - uses: ./.github/actions/run-api-tests with: dbOptions: '--dbclient=postgres --dbhost=localhost --dbport=5432 --dbname=strapi_test --dbusername=strapi --dbpassword=strapi' @@ -220,13 +214,13 @@ jobs: api_ce_mysql: if: needs.changes.outputs.backend == 'true' runs-on: ubuntu-latest - needs: [changes, lint, typescript, unit_back, unit_front] + needs: [changes, build, typescript, unit_back, unit_front] name: '[CE] API Integration (mysql:latest, client: ${{ matrix.db_client }}, node: ${{ matrix.node }}, shard: ${{ matrix.shard }})' strategy: matrix: - node: [16, 18, 20] + node: [18, 20] db_client: ['mysql', 'mysql2'] - shard: [1/2, 2/2] + shard: [1/5, 2/5, 3/5, 4/5, 5/5] services: mysql: image: bitnami/mysql:latest @@ -251,6 +245,8 @@ jobs: node-version: ${{ matrix.node }} - name: Monorepo install uses: ./.github/actions/yarn-nm-install + - name: Monorepo build + uses: ./.github/actions/run-build - uses: ./.github/actions/run-api-tests with: dbOptions: '--dbclient=${{ matrix.db_client }} --dbhost=localhost --dbport=3306 --dbname=strapi_test --dbusername=strapi --dbpassword=strapi' @@ -259,13 +255,13 @@ jobs: api_ce_mysql_5: if: needs.changes.outputs.backend == 'true' runs-on: ubuntu-latest - needs: [changes, lint, typescript, unit_back, unit_front] + needs: [changes, build, typescript, unit_back, unit_front] name: '[CE] API Integration (mysql:5, client: ${{ matrix.db_client }} , node: ${{ matrix.node }}, shard: ${{ matrix.shard }})' strategy: matrix: - node: [16, 18, 20] + node: [18, 20] db_client: ['mysql', 'mysql2'] - shard: [1/2, 2/2] + shard: [1/5, 2/5, 3/5, 4/5, 5/5] services: mysql: image: bitnami/mysql:5.7 @@ -289,6 +285,8 @@ jobs: node-version: ${{ matrix.node }} - name: Monorepo install uses: ./.github/actions/yarn-nm-install + - name: Monorepo build + uses: ./.github/actions/run-build - uses: ./.github/actions/run-api-tests with: dbOptions: '--dbclient=${{ matrix.db_client }} --dbhost=localhost --dbport=3306 --dbname=strapi_test --dbusername=strapi --dbpassword=strapi' @@ -297,13 +295,13 @@ jobs: api_ce_sqlite: if: needs.changes.outputs.backend == 'true' runs-on: ubuntu-latest - needs: [changes, lint, typescript, unit_back, unit_front] + needs: [changes, build, typescript, unit_back, unit_front] name: '[CE] API Integration (sqlite, client: ${{ matrix.sqlite_pkg }}, node: ${{ matrix.node }}, shard: ${{ matrix.shard }})' strategy: matrix: - node: [16, 18, 20] + node: [18, 20] sqlite_pkg: ['better-sqlite3', 'sqlite3'] - shard: [1/2, 2/2] + shard: [1/5, 2/5, 3/5, 4/5, 5/5] steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v3 @@ -311,6 +309,8 @@ jobs: node-version: ${{ matrix.node }} - name: Monorepo install uses: ./.github/actions/yarn-nm-install + - name: Monorepo build + uses: ./.github/actions/run-build - uses: ./.github/actions/run-api-tests env: SQLITE_PKG: ${{ matrix.sqlite_pkg }} @@ -321,15 +321,15 @@ jobs: # EE api_ee_pg: runs-on: ubuntu-latest - needs: [changes, lint, typescript, unit_back, unit_front] + needs: [changes, build, typescript, unit_back, unit_front] name: '[EE] API Integration (postgres, node: ${{ matrix.node }}, shard: ${{ matrix.shard }})' if: needs.changes.outputs.backend == 'true' && github.event.pull_request.head.repo.full_name == github.repository && !(github.actor == 'dependabot[bot]' || github.actor == 'dependabot-preview[bot]') env: STRAPI_LICENSE: ${{ secrets.strapiLicense }} strategy: matrix: - node: [16, 18, 20] - shard: [1/2, 2/2] + node: [18, 20] + shard: [1/5, 2/5, 3/5, 4/5, 5/5] services: postgres: # Docker Hub image @@ -356,6 +356,8 @@ jobs: node-version: ${{ matrix.node }} - name: Monorepo install uses: ./.github/actions/yarn-nm-install + - name: Monorepo build + uses: ./.github/actions/run-build - uses: ./.github/actions/run-api-tests with: dbOptions: '--dbclient=postgres --dbhost=localhost --dbport=5432 --dbname=strapi_test --dbusername=strapi --dbpassword=strapi' @@ -364,16 +366,16 @@ jobs: api_ee_mysql: runs-on: ubuntu-latest - needs: [changes, lint, typescript, unit_back, unit_front] + needs: [changes, build, typescript, unit_back, unit_front] name: '[EE] API Integration (mysql:latest, client: ${{ matrix.db_client }}, node: ${{ matrix.node }}, shard: ${{ matrix.shard }})' if: needs.changes.outputs.backend == 'true' && github.event.pull_request.head.repo.full_name == github.repository && !(github.actor == 'dependabot[bot]' || github.actor == 'dependabot-preview[bot]') env: STRAPI_LICENSE: ${{ secrets.strapiLicense }} strategy: matrix: - node: [16, 18, 20] + node: [18, 20] db_client: ['mysql', 'mysql2'] - shard: [1/2, 2/2] + shard: [1/5, 2/5, 3/5, 4/5, 5/5] services: mysql: image: bitnami/mysql:latest @@ -398,6 +400,8 @@ jobs: node-version: ${{ matrix.node }} - name: Monorepo install uses: ./.github/actions/yarn-nm-install + - name: Monorepo build + uses: ./.github/actions/run-build - uses: ./.github/actions/run-api-tests with: dbOptions: '--dbclient=${{ matrix.db_client }} --dbhost=localhost --dbport=3306 --dbname=strapi_test --dbusername=strapi --dbpassword=strapi' @@ -406,16 +410,16 @@ jobs: api_ee_sqlite: runs-on: ubuntu-latest - needs: [changes, lint, typescript, unit_back, unit_front] + needs: [changes, build, typescript, unit_back, unit_front] name: '[EE] API Integration (sqlite, client: ${{ matrix.sqlite_pkg }}, node: ${{ matrix.node }}, shard: ${{ matrix.shard }})' if: needs.changes.outputs.backend == 'true' && github.event.pull_request.head.repo.full_name == github.repository && !(github.actor == 'dependabot[bot]' || github.actor == 'dependabot-preview[bot]') env: STRAPI_LICENSE: ${{ secrets.strapiLicense }} strategy: matrix: - node: [16, 18, 20] + node: [18, 20] sqlite_pkg: ['better-sqlite3', 'sqlite3'] - shard: [1/2, 2/2] + shard: [1/5, 2/5, 3/5, 4/5, 5/5] steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v3 @@ -423,6 +427,8 @@ jobs: node-version: ${{ matrix.node }} - name: Monorepo install uses: ./.github/actions/yarn-nm-install + - name: Monorepo build + uses: ./.github/actions/run-build - uses: ./.github/actions/run-api-tests env: SQLITE_PKG: ${{ matrix.sqlite_pkg }} diff --git a/.nvmrc b/.nvmrc index b6a7d89c68..209e3ef4b6 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -16 +20 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 178eb526df..935057928d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -46,7 +46,7 @@ The Strapi core team will review your pull request and either merge it, request ## Contribution Prerequisites -- You have [Node.js](https://nodejs.org/en/) at version >= v16 and <= v20 and [Yarn](https://yarnpkg.com/en/) at v1.2.0+ installed. +- You have [Node.js](https://nodejs.org/en/) at version >= v18 and <= v20 and [Yarn](https://yarnpkg.com/en/) at v1.2.0+ installed. - You are familiar with [Git](https://git-scm.com). **Before submitting your pull request** make sure the following requirements are fulfilled: diff --git a/README.md b/README.md index 08d79b5eb4..f7be8430f0 100644 --- a/README.md +++ b/README.md @@ -94,6 +94,7 @@ Strapi only supports maintenance and LTS versions of Node.js. Please refer to th | Strapi Version | Recommended | Minimum | | --------------- | ----------- | ------- | +| 4.14.5 and up | 20.x | 18.x | | 4.11.0 and up | 18.x | 16.x | | 4.3.9 to 4.10.x | 18.x | 14.x | | 4.0.x to 4.3.8 | 16.x | 14.x | diff --git a/docs/docs/docs/01-core/strapi/commands/plugin/00-overview.md b/docs/docs/docs/01-core/strapi/commands/plugin/00-overview.md index 90abab0101..1452c7482c 100644 --- a/docs/docs/docs/01-core/strapi/commands/plugin/00-overview.md +++ b/docs/docs/docs/01-core/strapi/commands/plugin/00-overview.md @@ -14,3 +14,41 @@ This is an experimental API that is subject to change at any moment, hence why i ## Available Commands - [plugin:build](build) - Build a plugin for publishing +- [plugin:watch](watch) - Watch & compile a plugin in local development + +## Setting up your package + +In order to build/watch/check a plugin you need to have a `package.json` that must contain the following fields: + +- `name` +- `version` + +In regards to the export keys of your package.json because a plugin _typically_ has both a server and client +side output we recommend doing the following: + +```json +{ + "name": "@strapi/plugin", + "version": "1.0.0", + "exports": { + "./strapi-admin": { + "types": "./dist/admin/index.d.ts", + "source": "./admin/src/index.ts", + "import": "./dist/admin/index.mjs", + "require": "./dist/admin/index.js", + "default": "./dist/admin/index.js" + }, + "./strapi-server": { + "types": "./dist/server/index.d.ts", + "source": "./server/src/index.ts", + "import": "./dist/server/index.mjs", + "require": "./dist/server/index.js", + "default": "./dist/server/index.js" + }, + "./package.json": "./package.json" + } +} +``` + +We don't use `main`, `module` or `types` on the root level of the package.json because of the aforementioned reason (plugins don't have one entry). +If you've not written your plugin in typescript, you can omit the `types` value of an export map. This is the minimum setup required to build a plugin. diff --git a/docs/docs/docs/01-core/strapi/commands/plugin/01-build.md b/docs/docs/docs/01-core/strapi/commands/plugin/01-build.md index 6cee3e3928..d0b819e855 100644 --- a/docs/docs/docs/01-core/strapi/commands/plugin/01-build.md +++ b/docs/docs/docs/01-core/strapi/commands/plugin/01-build.md @@ -9,8 +9,7 @@ tags: --- The `plugin:build` command is used to build plugins in a CJS/ESM compatible format that can be instantly published to NPM. -This is done by looking at the export fields of a package.json e.g. `main`, `module`, `types` and `exports`. By using the -exports map specifically we can build dual plugins that support a server & client output. +This is done by using `pack-up` underneath and a specific configuration, for this command we _do not_ look for a `packup.config` file. ## Usage @@ -24,48 +23,14 @@ strapi plugin:build Bundle your strapi plugin for publishing. Options: - -y, --yes Skip all confirmation prompts (default: false) + --force Automatically answer "yes" to all prompts, including potentially destructive requests, and run non-interactively. -d, --debug Enable debugging mode with verbose logs (default: false) + --silent Don't log anything (default: false) + --sourcemap produce sourcemaps (default: false) + --minify minify the output (default: false) -h, --help Display help for command ``` -## Setting up your package - -In order to build a plugin you need to have a `package.json` that must contain the following fields: - -- `name` -- `version` - -In regards to the export keys of your package.json because a plugin _typically_ has both a server and client -side output we recommend doing the following: - -```json -{ - "name": "@strapi/plugin", - "version": "1.0.0", - "exports": { - "./strapi-admin": { - "types": "./dist/admin/index.d.ts", - "source": "./admin/src/index.ts", - "import": "./dist/admin/index.mjs", - "require": "./dist/admin/index.js", - "default": "./dist/admin/index.js" - }, - "./strapi-server": { - "types": "./dist/server/index.d.ts", - "source": "./server/src/index.ts", - "import": "./dist/server/index.mjs", - "require": "./dist/server/index.js", - "default": "./dist/server/index.js" - }, - "./package.json": "./package.json" - } -} -``` - -We don't use `main`, `module` or `types` on the root level of the package.json because of the aforementioned reason (plugins don't have one entry). -If you've not written your plugin in typescript, you can omit the `types` value of an export map. This is the minimum setup required to build a plugin. - ## How it works The command sequence can be visualised as follows: @@ -73,37 +38,6 @@ The command sequence can be visualised as follows: - Load package.json - Validate that package.json against a `yup` schema - Validate the ordering of an export map if `pkg.exports` is defined -- Create a build context, this holds information like: - - The transpilation target - - The external dependencies (that we don't want to bundle) - - Where the output should go e.g. `dist` - - The exports we're about to use to create build tasks -- Create a list of build tasks based on the `exports` from the build context, these can currently either be `"build:js"` or `"build:dts"` -- Pass the build task to a specific task handler e.g. `vite` or `tsc` +- Create a set of "bundles" to build ignoring the package.json exports map that is _specifically_ set up for strapi-plugins. +- Pass the created config to `pack-up`'s build API. - Finish - -## Transpilation target - -There are three different runtimes available for plugins: - -- `node` which equates to a `node16` target -- `web` which equates to a `esnext` target -- `*` (universal) which equates to `["last 3 major versions", "Firefox ESR", "last 2 Opera versions", "not dead", "node 16.0.0"]` - -The `node` and `web` targets are specifically used for the export maps with they keys `./strapi-server` and `./strapi-admin` respectively. -Any other export map values will be transpiled to the universal target. The universal target can be overwritten by adding the `browserslist` -key to your `package.json` (seen below): - -```json -{ - "name": "@strapi/plugin", - "version": "1.0.0", - "browserslist": [ - "last 3 major versions", - "Firefox ESR", - "last 2 Opera versions", - "not dead", - "node 16.0.0" - ] -} -``` diff --git a/docs/docs/docs/01-core/strapi/commands/plugin/02-watch.md b/docs/docs/docs/01-core/strapi/commands/plugin/02-watch.md new file mode 100644 index 0000000000..54401510f9 --- /dev/null +++ b/docs/docs/docs/01-core/strapi/commands/plugin/02-watch.md @@ -0,0 +1,40 @@ +--- +title: plugin:build +description: An in depth look at the plugin:build command of the Strapi CLI +tags: + - CLI + - commands + - plugins + - building +--- + +The `plugin:watch` command is used to watch plugin source files and compile them to production viable assets in real-time. +This is done by using `pack-up` underneath and a specific configuration, for this command we _do not_ look for a `packup.config` file. + +## Usage + +```bash +strapi plugin:watch +``` + +### Options + +```bash +Watch & compile your strapi plugin for local development. + +Options: + -d, --debug Enable debugging mode with verbose logs (default: false) + --silent Don't log anything (default: false) + -h, --help Display help for command +``` + +## How it works + +The command sequence can be visualised as follows: + +- Load package.json +- Validate that package.json against a `yup` schema +- Validate the ordering of an export map if `pkg.exports` is defined +- Create a set of "bundles" to build ignoring the package.json exports map that is _specifically_ set up for strapi-plugins. +- Pass the created config to `pack-up`'s watch API. +- Run's indefinitely diff --git a/docs/docs/docs/05-utils/pack-up/01-commands/02-build.mdx b/docs/docs/docs/05-utils/pack-up/01-commands/02-build.mdx index 0ee24eb43d..900c33729c 100644 --- a/docs/docs/docs/05-utils/pack-up/01-commands/02-build.mdx +++ b/docs/docs/docs/05-utils/pack-up/01-commands/02-build.mdx @@ -32,6 +32,8 @@ build(); ```ts interface BuildOptions { + configFile: false; + config?: Config; cwd?: string; debug?: boolean; minify?: boolean; diff --git a/docs/docs/docs/05-utils/pack-up/01-commands/04-watch.mdx b/docs/docs/docs/05-utils/pack-up/01-commands/04-watch.mdx index af2884e689..0f55407075 100644 --- a/docs/docs/docs/05-utils/pack-up/01-commands/04-watch.mdx +++ b/docs/docs/docs/05-utils/pack-up/01-commands/04-watch.mdx @@ -27,6 +27,8 @@ watch(); ```ts interface WatchOptions { + configFile: false; + config?: Config; cwd?: string; debug?: boolean; silent?: boolean; diff --git a/docs/docs/docs/05-utils/pack-up/02-config.mdx b/docs/docs/docs/05-utils/pack-up/02-config.mdx index 542e1b0684..10b8bb512c 100644 --- a/docs/docs/docs/05-utils/pack-up/02-config.mdx +++ b/docs/docs/docs/05-utils/pack-up/02-config.mdx @@ -47,6 +47,12 @@ interface Config { * Whether to minify the output or not. */ minify?: boolean; + /** + * Instead of creating as few chunks as possible, this mode + * will create separate chunks for all modules using the original module + * names as file names + */ + preserveModules?: boolean; /** * Whether to generate sourcemaps for the output or not. */ @@ -56,6 +62,10 @@ interface Config { * Node.js workers and you want them to be transpiled for the node environment. */ runtime?: Runtime; + /** + * path to the tsconfig file to use for the bundle. + */ + tsconfig?: string; } interface ConfigBundle { @@ -63,6 +73,8 @@ interface ConfigBundle { import?: string; require?: string; runtime?: Runtime; + tsconfig?: string; + types?: string; } type Runtime = '*' | 'node' | 'web'; diff --git a/e2e/app-template/package.json b/e2e/app-template/package.json index e5c4d88aca..e6898ebee6 100644 --- a/e2e/app-template/package.json +++ b/e2e/app-template/package.json @@ -16,7 +16,7 @@ "better-sqlite3": "8.6.0" }, "engines": { - "node": ">=16.0.0 <=20.x.x", + "node": ">=18.0.0 <=20.x.x", "npm": ">=6.0.0" }, "license": "MIT" diff --git a/examples/getstarted/package.json b/examples/getstarted/package.json index ec30a6a922..51ded56590 100644 --- a/examples/getstarted/package.json +++ b/examples/getstarted/package.json @@ -38,7 +38,7 @@ "styled-components": "5.3.3" }, "engines": { - "node": ">=16.0.0 <=20.x.x", + "node": ">=18.0.0 <=20.x.x", "npm": ">=6.0.0" }, "strapi": { diff --git a/examples/kitchensink-ts/package.json b/examples/kitchensink-ts/package.json index 0d9813b7e5..3181690488 100644 --- a/examples/kitchensink-ts/package.json +++ b/examples/kitchensink-ts/package.json @@ -24,7 +24,7 @@ "styled-components": "5.3.3" }, "engines": { - "node": ">=16.0.0 <=20.x.x", + "node": ">=18.0.0 <=20.x.x", "npm": ">=6.0.0" }, "strapi": { diff --git a/examples/kitchensink/package.json b/examples/kitchensink/package.json index 4769da211e..23ba9e04bb 100644 --- a/examples/kitchensink/package.json +++ b/examples/kitchensink/package.json @@ -29,7 +29,7 @@ "styled-components": "5.3.3" }, "engines": { - "node": ">=16.0.0 <=20.x.x", + "node": ">=18.0.0 <=20.x.x", "npm": ">=6.0.0" }, "strapi": { diff --git a/package.json b/package.json index b509aaab06..1609d8136f 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ "test:front:watch": "cross-env IS_EE=true run test:front --watch", "test:front:watch:ce": "cross-env IS_EE=false run test:front --watch", "test:generate-app": "yarn build:ts && node test/scripts/generate-test-app.js", + "test:generate-app:no-build": "node test/scripts/generate-test-app.js", "test:ts": "yarn test:ts:packages && yarn test:ts:front && yarn test:ts:back", "test:ts:back": "nx run-many --target=test:ts:back --nx-ignore-cycles", "test:ts:front": "nx run-many --target=test:ts:front --nx-ignore-cycles", @@ -137,7 +138,7 @@ }, "packageManager": "yarn@3.6.4", "engines": { - "node": ">=16.0.0 <=20.x.x", + "node": ">=18.0.0 <=20.x.x", "npm": ">=6.0.0" } } diff --git a/packages/admin-test-utils/package.json b/packages/admin-test-utils/package.json index 3e5dd40e49..c72b09d5a5 100644 --- a/packages/admin-test-utils/package.json +++ b/packages/admin-test-utils/package.json @@ -82,6 +82,6 @@ "@testing-library/jest-dom": "^5.16.5" }, "engines": { - "node": ">=16.0.0 <=20.x.x" + "node": ">=18.0.0 <=20.x.x" } } diff --git a/packages/cli/create-strapi-app/package.json b/packages/cli/create-strapi-app/package.json index d99ef86b7f..000b40f051 100644 --- a/packages/cli/create-strapi-app/package.json +++ b/packages/cli/create-strapi-app/package.json @@ -54,7 +54,7 @@ "tsconfig": "4.14.4" }, "engines": { - "node": ">=16.0.0 <=20.x.x", + "node": ">=18.0.0 <=20.x.x", "npm": ">=6.0.0" } } diff --git a/packages/cli/create-strapi-starter/package.json b/packages/cli/create-strapi-starter/package.json index 86bd552d87..fc4f4b4b10 100644 --- a/packages/cli/create-strapi-starter/package.json +++ b/packages/cli/create-strapi-starter/package.json @@ -59,7 +59,7 @@ "tsconfig": "4.14.4" }, "engines": { - "node": ">=16.0.0 <=20.x.x", + "node": ">=18.0.0 <=20.x.x", "npm": ">=6.0.0" } } diff --git a/packages/core/admin/admin/src/StrapiApp.js b/packages/core/admin/admin/src/StrapiApp.js index 6f63469018..9778c12bdf 100644 --- a/packages/core/admin/admin/src/StrapiApp.js +++ b/packages/core/admin/admin/src/StrapiApp.js @@ -10,23 +10,20 @@ import { BrowserRouter } from 'react-router-dom'; import Logo from './assets/images/logo-strapi-2022.svg'; import { LANGUAGE_LOCAL_STORAGE_KEY } from './components/LanguageProvider'; -import Providers from './components/Providers'; -import { - HOOKS, - INJECTION_ZONES -} from './constants'; +import { Providers } from './components/Providers'; +import { HOOKS, INJECTION_ZONES } from './constants'; import { customFields, Plugin, Reducers } from './core/apis'; -import { configureStore } from './core/store'; +import { configureStore } from './core/store/configure'; import { basename, createHook } from './core/utils'; import favicon from './favicon.png'; import App from './pages/App'; import languageNativeNames from './translations/languageNativeNames'; const { - INJECT_COLUMN_IN_TABLE, + INJECT_COLUMN_IN_TABLE, MUTATE_COLLECTION_TYPES_LINKS, MUTATE_EDIT_VIEW_LAYOUT, - MUTATE_SINGLE_TYPES_LINKS + MUTATE_SINGLE_TYPES_LINKS, } = HOOKS; class StrapiApp { diff --git a/packages/core/admin/admin/src/components/AuthenticatedApp.tsx b/packages/core/admin/admin/src/components/AuthenticatedApp.tsx new file mode 100644 index 0000000000..dde6f92e2a --- /dev/null +++ b/packages/core/admin/admin/src/components/AuthenticatedApp.tsx @@ -0,0 +1,184 @@ +import * as React from 'react'; + +import { + AppInfoContextValue, + AppInfoProvider, + auth, + LoadingIndicatorPage, + useFetchClient, + useGuidedTour, +} from '@strapi/helper-plugin'; +import lodashGet from 'lodash/get'; +import { useQueries } from 'react-query'; +import lt from 'semver/functions/lt'; +import valid from 'semver/functions/valid'; +// TODO: DS add loader + +import packageJSON from '../../../package.json'; +import { UserEntity } from '../../../shared/entities'; +import { useConfiguration } from '../hooks/useConfiguration'; +import { APIResponse, APIResponseUsersLegacy } from '../types/adminAPI'; +// @ts-expect-error - no types yet. +import { getFullName, hashAdminUserEmail } from '../utils'; + +import { NpsSurvey } from './NpsSurvey'; +import { PluginsInitializer } from './PluginsInitializer'; +import { RBACProvider, Permission } from './RBACProvider'; + +const strapiVersion = packageJSON.version; + +const AuthenticatedApp = () => { + const { setGuidedTourVisibility } = useGuidedTour(); + const userInfo = auth.get('userInfo'); + const userName = userInfo + ? lodashGet(userInfo, 'username') || getFullName(userInfo.firstname, userInfo.lastname) + : null; + const [userDisplayName, setUserDisplayName] = React.useState(userName); + const [userId, setUserId] = React.useState(); + const { showReleaseNotification } = useConfiguration(); + const { get } = useFetchClient(); + const [ + { data: appInfos, status }, + { data: tagName, isLoading }, + { data: permissions, status: fetchPermissionsStatus, refetch, isFetching }, + { data: userRoles }, + ] = useQueries([ + { + queryKey: 'app-infos', + async queryFn() { + const { data } = await get< + APIResponse< + Pick< + AppInfoContextValue, + | 'currentEnvironment' + | 'autoReload' + | 'communityEdition' + | 'dependencies' + | 'useYarn' + | 'projectId' + | 'strapiVersion' + | 'nodeVersion' + > + > + >('/admin/information'); + + return data.data; + }, + }, + { + queryKey: 'strapi-release', + async queryFn() { + try { + const res = await fetch('https://api.github.com/repos/strapi/strapi/releases/latest'); + + if (!res.ok) { + throw new Error(); + } + + const response = (await res.json()) as { tag_name: string | null | undefined }; + + if (!response.tag_name) { + throw new Error(); + } + + return response.tag_name; + } catch (err) { + // Don't throw an error + return strapiVersion; + } + }, + enabled: showReleaseNotification, + initialData: strapiVersion, + }, + { + queryKey: 'admin-users-permission', + async queryFn() { + const { data } = await get<{ data: Permission[] }>('/admin/users/me/permissions'); + + return data.data; + }, + initialData: [], + }, + { + queryKey: 'user-roles', + async queryFn() { + const { + data: { + data: { roles }, + }, + } = await get>('/admin/users/me'); + + return roles; + }, + }, + ]); + + const shouldUpdateStrapi = checkLatestStrapiVersion(strapiVersion, tagName); + + /** + * TODO: does this actually need to be an effect? + */ + React.useEffect(() => { + if (userRoles) { + const isUserSuperAdmin = userRoles.find(({ code }) => code === 'strapi-super-admin'); + + if (isUserSuperAdmin && appInfos?.autoReload) { + setGuidedTourVisibility(true); + } + } + }, [userRoles, appInfos, setGuidedTourVisibility]); + + React.useEffect(() => { + const getUserId = async () => { + const userId = await hashAdminUserEmail(userInfo); + setUserId(userId); + }; + + getUserId(); + }, [userInfo]); + + // We don't need to wait for the release query to be fetched before rendering the plugins + // however, we need the appInfos and the permissions + const shouldShowNotDependentQueriesLoader = + isFetching || status === 'loading' || fetchPermissionsStatus === 'loading'; + + const shouldShowLoader = isLoading || shouldShowNotDependentQueriesLoader; + + if (shouldShowLoader) { + return ; + } + + // TODO: add error state + if (status === 'error') { + return
error...
; + } + + return ( + + + + + + + ); +}; + +const checkLatestStrapiVersion = ( + currentPackageVersion: string, + latestPublishedVersion: string = '' +): boolean => { + if (!valid(currentPackageVersion) || !valid(latestPublishedVersion)) { + return false; + } + + return lt(currentPackageVersion, latestPublishedVersion); +}; + +export { AuthenticatedApp }; diff --git a/packages/core/admin/admin/src/components/AuthenticatedApp/index.js b/packages/core/admin/admin/src/components/AuthenticatedApp/index.js deleted file mode 100644 index bf9d59aac3..0000000000 --- a/packages/core/admin/admin/src/components/AuthenticatedApp/index.js +++ /dev/null @@ -1,116 +0,0 @@ -import React, { useEffect, useState } from 'react'; - -import { - AppInfoProvider, - auth, - LoadingIndicatorPage, - useGuidedTour, - useNotification, -} from '@strapi/helper-plugin'; -import get from 'lodash/get'; -import { useQueries } from 'react-query'; -// TODO: DS add loader - -import packageJSON from '../../../../package.json'; -import { useConfiguration } from '../../hooks/useConfiguration'; -import { getFullName, hashAdminUserEmail } from '../../utils'; -import { NpsSurvey } from '../NpsSurvey'; -import { PluginsInitializer } from '../PluginsInitializer'; -import RBACProvider from '../RBACProvider'; - -import { fetchAppInfo, fetchCurrentUserPermissions, fetchUserRoles } from './utils/api'; -import { checkLatestStrapiVersion } from './utils/checkLatestStrapiVersion'; -import { fetchStrapiLatestRelease } from './utils/fetchStrapiLatestRelease'; - -const strapiVersion = packageJSON.version; - -const AuthenticatedApp = () => { - const { setGuidedTourVisibility } = useGuidedTour(); - const toggleNotification = useNotification(); - const userInfo = auth.getUserInfo(); - const userName = get(userInfo, 'username') || getFullName(userInfo.firstname, userInfo.lastname); - const [userDisplayName, setUserDisplayName] = useState(userName); - const [userId, setUserId] = useState(null); - const { showReleaseNotification } = useConfiguration(); - const [ - { data: appInfos, status }, - { data: tagName, isLoading }, - { data: permissions, status: fetchPermissionsStatus, refetch, isFetching }, - { data: userRoles }, - ] = useQueries([ - { queryKey: 'app-infos', queryFn: fetchAppInfo }, - { - queryKey: 'strapi-release', - queryFn: () => fetchStrapiLatestRelease(toggleNotification), - enabled: showReleaseNotification, - initialData: strapiVersion, - }, - { - queryKey: 'admin-users-permission', - queryFn: fetchCurrentUserPermissions, - initialData: [], - }, - { - queryKey: 'user-roles', - queryFn: fetchUserRoles, - }, - ]); - - const shouldUpdateStrapi = checkLatestStrapiVersion(strapiVersion, tagName); - - /** - * TODO: does this actually need to be an effect? - */ - useEffect(() => { - if (userRoles) { - const isUserSuperAdmin = userRoles.find(({ code }) => code === 'strapi-super-admin'); - - if (isUserSuperAdmin && appInfos?.autoReload) { - setGuidedTourVisibility(true); - } - } - }, [userRoles, appInfos, setGuidedTourVisibility]); - - useEffect(() => { - const getUserId = async () => { - const userId = await hashAdminUserEmail(userInfo); - setUserId(userId); - }; - - getUserId(); - }, [userInfo]); - - // We don't need to wait for the release query to be fetched before rendering the plugins - // however, we need the appInfos and the permissions - const shouldShowNotDependentQueriesLoader = - isFetching || status === 'loading' || fetchPermissionsStatus === 'loading'; - - const shouldShowLoader = isLoading || shouldShowNotDependentQueriesLoader; - - if (shouldShowLoader) { - return ; - } - - // TODO: add error state - if (status === 'error') { - return
error...
; - } - - return ( - - - - - - - ); -}; - -export default AuthenticatedApp; diff --git a/packages/core/admin/admin/src/components/AuthenticatedApp/utils/api.js b/packages/core/admin/admin/src/components/AuthenticatedApp/utils/api.js deleted file mode 100644 index ad5a2883c2..0000000000 --- a/packages/core/admin/admin/src/components/AuthenticatedApp/utils/api.js +++ /dev/null @@ -1,47 +0,0 @@ -import { getFetchClient } from '@strapi/helper-plugin'; - -const { get } = getFetchClient(); - -const fetchAppInfo = async () => { - try { - const { data, headers } = await get('/admin/information'); - - if (!headers['content-type'].includes('application/json')) { - throw new Error('Not found'); - } - - return data.data; - } catch (error) { - throw new Error(error); - } -}; - -const fetchCurrentUserPermissions = async () => { - try { - const { data, headers } = await get('/admin/users/me/permissions'); - - if (!headers['content-type'].includes('application/json')) { - throw new Error('Not found'); - } - - return data.data; - } catch (err) { - throw new Error(err); - } -}; - -const fetchUserRoles = async () => { - try { - const { - data: { - data: { roles }, - }, - } = await get('/admin/users/me'); - - return roles; - } catch (err) { - throw new Error(err); - } -}; - -export { fetchAppInfo, fetchCurrentUserPermissions, fetchUserRoles }; diff --git a/packages/core/admin/admin/src/components/AuthenticatedApp/utils/checkLatestStrapiVersion.ts b/packages/core/admin/admin/src/components/AuthenticatedApp/utils/checkLatestStrapiVersion.ts deleted file mode 100644 index e8f13169c3..0000000000 --- a/packages/core/admin/admin/src/components/AuthenticatedApp/utils/checkLatestStrapiVersion.ts +++ /dev/null @@ -1,13 +0,0 @@ -import lt from 'semver/functions/lt'; -import valid from 'semver/functions/valid'; - -export const checkLatestStrapiVersion = ( - currentPackageVersion: string, - latestPublishedVersion: string -): boolean => { - if (!valid(currentPackageVersion) || !valid(latestPublishedVersion)) { - return false; - } - - return lt(currentPackageVersion, latestPublishedVersion); -}; diff --git a/packages/core/admin/admin/src/components/AuthenticatedApp/utils/fetchStrapiLatestRelease.ts b/packages/core/admin/admin/src/components/AuthenticatedApp/utils/fetchStrapiLatestRelease.ts deleted file mode 100644 index 3fe8edfa68..0000000000 --- a/packages/core/admin/admin/src/components/AuthenticatedApp/utils/fetchStrapiLatestRelease.ts +++ /dev/null @@ -1,19 +0,0 @@ -import packageJSON from '../../../../../package.json'; - -const strapiVersion = packageJSON.version; - -export const fetchStrapiLatestRelease = async () => { - try { - const res = await fetch('https://api.github.com/repos/strapi/strapi/releases/latest'); - - if (!res.ok) { - throw new Error('Failed to fetch latest Strapi version.'); - } - const { tag_name } = await res.json(); - - return tag_name; - } catch (err) { - // Don't throw an error - return strapiVersion; - } -}; diff --git a/packages/core/admin/admin/src/components/AuthenticatedApp/utils/tests/checkLatestStrapiVersion.test.ts b/packages/core/admin/admin/src/components/AuthenticatedApp/utils/tests/checkLatestStrapiVersion.test.ts deleted file mode 100644 index facdc53c8a..0000000000 --- a/packages/core/admin/admin/src/components/AuthenticatedApp/utils/tests/checkLatestStrapiVersion.test.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { checkLatestStrapiVersion } from '../checkLatestStrapiVersion'; - -describe('ADMIN | utils | checkLatestStrapiVersion', () => { - it('should return true if the current version is lower than the latest published version', () => { - expect(checkLatestStrapiVersion('v3.3.2', 'v3.3.4')).toBeTruthy(); - expect(checkLatestStrapiVersion('3.3.2', 'v3.3.4')).toBeTruthy(); - expect(checkLatestStrapiVersion('v3.3.2', '3.3.4')).toBeTruthy(); - expect(checkLatestStrapiVersion('3.3.2', '3.3.4')).toBeTruthy(); - }); - it('should return false if the current version is equal to the latest published version', () => { - expect(checkLatestStrapiVersion('3.3.4', 'v3.3.4')).toBeFalsy(); - expect(checkLatestStrapiVersion('v3.3.4', '3.3.4')).toBeFalsy(); - expect(checkLatestStrapiVersion('3.3.4', '3.3.4')).toBeFalsy(); - }); - it('should return false if the current version is a beta of the next release', () => { - expect(checkLatestStrapiVersion('3.4.0-beta.1', 'v3.3.4')).toBeFalsy(); - expect(checkLatestStrapiVersion('v3.4.0-beta.1', '3.3.4')).toBeFalsy(); - expect(checkLatestStrapiVersion('3.4.0-beta.1', '3.3.4')).toBeFalsy(); - }); -}); diff --git a/packages/core/admin/admin/src/components/ConfigurationProvider.tsx b/packages/core/admin/admin/src/components/ConfigurationProvider.tsx index 3d1cd15eeb..d0737b81a3 100644 --- a/packages/core/admin/admin/src/components/ConfigurationProvider.tsx +++ b/packages/core/admin/admin/src/components/ConfigurationProvider.tsx @@ -2,7 +2,7 @@ import * as React from 'react'; import { ConfigurationContext, ConfigurationContextValue } from '../contexts/configuration'; -export interface ConfigurationProviderProps { +interface ConfigurationProviderProps { children: React.ReactNode; authLogo: string; menuLogo: string; @@ -65,3 +65,4 @@ const ConfigurationProvider = ({ }; export { ConfigurationProvider }; +export type { ConfigurationProviderProps }; diff --git a/packages/core/admin/admin/src/components/LanguageProvider.tsx b/packages/core/admin/admin/src/components/LanguageProvider.tsx index d647e9fae0..08edc9852b 100644 --- a/packages/core/admin/admin/src/components/LanguageProvider.tsx +++ b/packages/core/admin/admin/src/components/LanguageProvider.tsx @@ -127,3 +127,4 @@ const reducer = (state = initialState, action: Action) => { }; export { LanguageProvider, useLocales, LANGUAGE_LOCAL_STORAGE_KEY }; +export type { LanguageProviderProps, LocalesContextValue }; diff --git a/packages/core/admin/admin/src/components/Providers.tsx b/packages/core/admin/admin/src/components/Providers.tsx new file mode 100644 index 0000000000..ed082ba14c --- /dev/null +++ b/packages/core/admin/admin/src/components/Providers.tsx @@ -0,0 +1,125 @@ +import * as React from 'react'; + +import { + AutoReloadOverlayBlockerProvider, + CustomFieldsProvider, + CustomFieldsProviderProps, + LibraryProvider, + LibraryProviderProps, + NotificationsProvider, + OverlayBlockerProvider, + StrapiAppProvider, + StrapiAppProviderProps, +} from '@strapi/helper-plugin'; +import { QueryClient, QueryClientProvider } from 'react-query'; +import { Provider } from 'react-redux'; + +import { AdminContext, AdminContextValue } from '../contexts/admin'; + +import { ConfigurationProvider, ConfigurationProviderProps } from './ConfigurationProvider'; +import { GuidedTourProvider } from './GuidedTour/Provider'; +import { LanguageProvider, LanguageProviderProps } from './LanguageProvider'; +import { Theme } from './Theme'; +import { ThemeToggleProvider, ThemeToggleProviderProps } from './ThemeToggleProvider'; + +import type { Store } from '../core/store/configure'; + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + refetchOnWindowFocus: false, + }, + }, +}); + +interface ProvidersProps + extends Pick, + Pick, + Pick< + ConfigurationProviderProps, + 'authLogo' | 'menuLogo' | 'showReleaseNotification' | 'showTutorials' + >, + Pick, + Pick, + Pick, + Pick< + StrapiAppProviderProps, + | 'getPlugin' + | 'menu' + | 'plugins' + | 'runHookParallel' + | 'runHookSeries' + | 'runHookWaterfall' + | 'settings' + > { + children: React.ReactNode; + store: Store; +} + +const Providers = ({ + authLogo, + children, + components, + customFields, + fields, + getAdminInjectedComponents, + getPlugin, + localeNames, + menu, + menuLogo, + messages, + plugins, + runHookParallel, + runHookSeries, + runHookWaterfall, + settings, + showReleaseNotification, + showTutorials, + store, + themes, +}: ProvidersProps) => { + return ( + + + + + + + + + + + + + + {children} + + + + + + + + + + + + + + ); +}; + +export { Providers }; diff --git a/packages/core/admin/admin/src/components/Providers/index.js b/packages/core/admin/admin/src/components/Providers/index.js deleted file mode 100644 index 97f89f044c..0000000000 --- a/packages/core/admin/admin/src/components/Providers/index.js +++ /dev/null @@ -1,156 +0,0 @@ -import React from 'react'; - -import { - AutoReloadOverlayBlockerProvider, - CustomFieldsProvider, - LibraryProvider, - NotificationsProvider, - OverlayBlockerProvider, - StrapiAppProvider, -} from '@strapi/helper-plugin'; -import PropTypes from 'prop-types'; -import { QueryClient, QueryClientProvider } from 'react-query'; -import { Provider } from 'react-redux'; - -import { AdminContext } from '../../contexts/admin'; -import { ConfigurationProvider } from '../ConfigurationProvider'; -import { GuidedTourProvider } from '../GuidedTour/Provider'; -import { LanguageProvider } from '../LanguageProvider'; -import { Theme } from '../Theme'; -import { ThemeToggleProvider } from '../ThemeToggleProvider'; - -const queryClient = new QueryClient({ - defaultOptions: { - queries: { - refetchOnWindowFocus: false, - }, - }, -}); - -const Providers = ({ - authLogo, - children, - components, - customFields, - fields, - getAdminInjectedComponents, - getPlugin, - localeNames, - menu, - menuLogo, - messages, - plugins, - runHookParallel, - runHookSeries, - runHookWaterfall, - settings, - showReleaseNotification, - showTutorials, - store, - themes, -}) => { - return ( - - - - - - - - - - - - - - {children} - - - - - - - - - - - - - - ); -}; - -Providers.propTypes = { - authLogo: PropTypes.oneOfType([PropTypes.string, PropTypes.any]).isRequired, - children: PropTypes.node.isRequired, - components: PropTypes.object.isRequired, - customFields: PropTypes.object.isRequired, - fields: PropTypes.object.isRequired, - getAdminInjectedComponents: PropTypes.func.isRequired, - getPlugin: PropTypes.func.isRequired, - localeNames: PropTypes.objectOf(PropTypes.string).isRequired, - menu: PropTypes.arrayOf( - PropTypes.shape({ - to: PropTypes.string.isRequired, - icon: PropTypes.func.isRequired, - intlLabel: PropTypes.shape({ - id: PropTypes.string.isRequired, - defaultMessage: PropTypes.string.isRequired, - }).isRequired, - permissions: PropTypes.array, - Component: PropTypes.func, - }) - ).isRequired, - menuLogo: PropTypes.oneOfType([PropTypes.string, PropTypes.any]).isRequired, - messages: PropTypes.object.isRequired, - plugins: PropTypes.object.isRequired, - runHookParallel: PropTypes.func.isRequired, - runHookWaterfall: PropTypes.func.isRequired, - runHookSeries: PropTypes.func.isRequired, - settings: PropTypes.object.isRequired, - showReleaseNotification: PropTypes.bool.isRequired, - showTutorials: PropTypes.bool.isRequired, - store: PropTypes.object.isRequired, - themes: PropTypes.shape({ - light: PropTypes.shape({ - colors: PropTypes.object.isRequired, - shadows: PropTypes.object.isRequired, - sizes: PropTypes.object.isRequired, - zIndices: PropTypes.array.isRequired, - spaces: PropTypes.array.isRequired, - borderRadius: PropTypes.string.isRequired, - mediaQueries: PropTypes.object.isRequired, - fontSizes: PropTypes.array.isRequired, - lineHeights: PropTypes.array.isRequired, - fontWeights: PropTypes.object.isRequired, - }).isRequired, - dark: PropTypes.shape({ - colors: PropTypes.object.isRequired, - shadows: PropTypes.object.isRequired, - sizes: PropTypes.object.isRequired, - zIndices: PropTypes.array.isRequired, - spaces: PropTypes.array.isRequired, - borderRadius: PropTypes.string.isRequired, - mediaQueries: PropTypes.object.isRequired, - fontSizes: PropTypes.array.isRequired, - lineHeights: PropTypes.array.isRequired, - fontWeights: PropTypes.object.isRequired, - }).isRequired, - custom: PropTypes.object, - }).isRequired, -}; - -export default Providers; diff --git a/packages/core/admin/admin/src/components/RBACProvider.tsx b/packages/core/admin/admin/src/components/RBACProvider.tsx new file mode 100644 index 0000000000..659fed05a2 --- /dev/null +++ b/packages/core/admin/admin/src/components/RBACProvider.tsx @@ -0,0 +1,124 @@ +import * as React from 'react'; + +import { + LoadingIndicatorPage, + Permission, + RBACContext, + RBACContextValue, +} from '@strapi/helper-plugin'; +import produce from 'immer'; + +import { useTypedSelector, useTypedDispatch } from '../core/store/hooks'; + +/* ------------------------------------------------------------------------------------------------- + * RBACProvider + * -----------------------------------------------------------------------------------------------*/ + +interface RBACProviderProps { + children: React.ReactNode; + permissions: Permission[]; + refetchPermissions: RBACContextValue['refetchPermissions']; +} + +const RBACProvider = ({ children, permissions, refetchPermissions }: RBACProviderProps) => { + const allPermissions = useTypedSelector((state) => state.rbacProvider.allPermissions); + + const dispatch = useTypedDispatch(); + + React.useEffect(() => { + dispatch(setPermissionsAction(permissions)); + + return () => { + dispatch(resetStoreAction()); + }; + }, [permissions, dispatch]); + + if (!allPermissions) { + return ; + } + + return ( + + {children} + + ); +}; + +/* ------------------------------------------------------------------------------------------------- + * RBACReducer + * -----------------------------------------------------------------------------------------------*/ + +interface RBACState { + allPermissions: null | Permission[]; + collectionTypesRelatedPermissions: Record>; +} + +const initialState = { + allPermissions: null, + collectionTypesRelatedPermissions: {}, +}; + +const RESET_STORE = 'StrapiAdmin/RBACProvider/RESET_STORE'; +const SET_PERMISSIONS = 'StrapiAdmin/RBACProvider/SET_PERMISSIONS'; + +interface ResetStoreAction { + type: typeof RESET_STORE; +} + +const resetStoreAction = (): ResetStoreAction => ({ type: RESET_STORE }); + +interface SetPermissionsAction { + type: typeof SET_PERMISSIONS; + permissions: Permission[]; +} + +const setPermissionsAction = ( + permissions: SetPermissionsAction['permissions'] +): SetPermissionsAction => ({ + type: SET_PERMISSIONS, + permissions, +}); + +type Actions = ResetStoreAction | SetPermissionsAction; + +const RBACReducer = (state: RBACState = initialState, action: Actions) => + produce(state, (draftState) => { + switch (action.type) { + case SET_PERMISSIONS: { + draftState.allPermissions = action.permissions; + draftState.collectionTypesRelatedPermissions = action.permissions + .filter((perm) => perm.subject) + .reduce>>((acc, current) => { + const { subject, action } = current; + + if (!subject) return acc; + + if (!acc[subject]) { + acc[subject] = {}; + } + + acc[subject] = acc[subject][action] + ? { ...acc[subject], [action]: [...acc[subject][action], current] } + : { ...acc[subject], [action]: [current] }; + + return acc; + }, {}); + break; + } + case RESET_STORE: { + return initialState; + } + default: + return state; + } + }); + +export { RBACProvider, RBACReducer, resetStoreAction, setPermissionsAction }; +export type { + RBACState, + Actions, + RBACProviderProps, + ResetStoreAction, + SetPermissionsAction, + Permission, +}; diff --git a/packages/core/admin/admin/src/components/RBACProvider/actions.js b/packages/core/admin/admin/src/components/RBACProvider/actions.js deleted file mode 100644 index dfaba17b30..0000000000 --- a/packages/core/admin/admin/src/components/RBACProvider/actions.js +++ /dev/null @@ -1,10 +0,0 @@ -import { RESET_STORE, SET_PERMISSIONS } from './constants'; - -const resetStore = () => ({ type: RESET_STORE }); - -const setPermissions = (permissions) => ({ - type: SET_PERMISSIONS, - permissions, -}); - -export { resetStore, setPermissions }; diff --git a/packages/core/admin/admin/src/components/RBACProvider/constants.js b/packages/core/admin/admin/src/components/RBACProvider/constants.js deleted file mode 100644 index e7942f3c3e..0000000000 --- a/packages/core/admin/admin/src/components/RBACProvider/constants.js +++ /dev/null @@ -1,2 +0,0 @@ -export const RESET_STORE = 'StrapiAdmin/RBACProvider/RESET_STORE'; -export const SET_PERMISSIONS = 'StrapiAdmin/RBACProvider/SET_PERMISSIONS'; diff --git a/packages/core/admin/admin/src/components/RBACProvider/index.js b/packages/core/admin/admin/src/components/RBACProvider/index.js deleted file mode 100644 index 38b3fe8794..0000000000 --- a/packages/core/admin/admin/src/components/RBACProvider/index.js +++ /dev/null @@ -1,39 +0,0 @@ -import React, { useEffect } from 'react'; - -import { LoadingIndicatorPage, RBACProviderContext } from '@strapi/helper-plugin'; -import PropTypes from 'prop-types'; -import { useDispatch, useSelector } from 'react-redux'; - -import { resetStore, setPermissions } from './actions'; - -const RBACProvider = ({ children, permissions, refetchPermissions }) => { - const { allPermissions } = useSelector((state) => state.rbacProvider); - - const dispatch = useDispatch(); - - useEffect(() => { - dispatch(setPermissions(permissions)); - - return () => { - dispatch(resetStore()); - }; - }, [permissions, dispatch]); - - if (!allPermissions) { - return ; - } - - return ( - - {children} - - ); -}; - -RBACProvider.propTypes = { - children: PropTypes.node.isRequired, - permissions: PropTypes.array.isRequired, - refetchPermissions: PropTypes.func.isRequired, -}; - -export default RBACProvider; diff --git a/packages/core/admin/admin/src/components/RBACProvider/reducer.js b/packages/core/admin/admin/src/components/RBACProvider/reducer.js deleted file mode 100644 index 1730c78751..0000000000 --- a/packages/core/admin/admin/src/components/RBACProvider/reducer.js +++ /dev/null @@ -1,51 +0,0 @@ -/* - * - * RBACProvider reducer - * The goal of this reducer is to provide - * the plugins with an access to the user's permissions - * in our middleware system - * - */ - -import produce from 'immer'; - -import { RESET_STORE, SET_PERMISSIONS } from './constants'; - -const initialState = { - allPermissions: null, - collectionTypesRelatedPermissions: {}, -}; - -const reducer = (state = initialState, action) => - // eslint-disable-next-line consistent-return - produce(state, (draftState) => { - switch (action.type) { - case SET_PERMISSIONS: { - draftState.allPermissions = action.permissions; - draftState.collectionTypesRelatedPermissions = action.permissions - .filter((perm) => perm.subject) - .reduce((acc, current) => { - const { subject, action } = current; - - if (!acc[subject]) { - acc[subject] = {}; - } - - acc[subject] = acc[subject][action] - ? { ...acc[subject], [action]: [...acc[subject][action], current] } - : { ...acc[subject], [action]: [current] }; - - return acc; - }, {}); - break; - } - case RESET_STORE: { - return initialState; - } - default: - return state; - } - }); - -export default reducer; -export { initialState }; diff --git a/packages/core/admin/admin/src/components/ThemeToggleProvider.tsx b/packages/core/admin/admin/src/components/ThemeToggleProvider.tsx index 54d024bbec..d937259c86 100644 --- a/packages/core/admin/admin/src/components/ThemeToggleProvider.tsx +++ b/packages/core/admin/admin/src/components/ThemeToggleProvider.tsx @@ -48,3 +48,4 @@ const ThemeToggleProvider = ({ children, themes }: ThemeToggleProviderProps) => }; export { ThemeToggleProvider }; +export type { ThemeToggleProviderProps }; diff --git a/packages/core/admin/admin/src/components/AuthenticatedApp/tests/index.test.js b/packages/core/admin/admin/src/components/tests/AuthenticatedApp.test.tsx similarity index 68% rename from packages/core/admin/admin/src/components/AuthenticatedApp/tests/index.test.js rename to packages/core/admin/admin/src/components/tests/AuthenticatedApp.test.tsx index cf80c1112c..93ac1038f2 100644 --- a/packages/core/admin/admin/src/components/AuthenticatedApp/tests/index.test.js +++ b/packages/core/admin/admin/src/components/tests/AuthenticatedApp.test.tsx @@ -1,8 +1,6 @@ -import React from 'react'; - import { render, waitFor } from '@tests/utils'; -import AuthenticatedApp from '../index'; +import { AuthenticatedApp } from '../AuthenticatedApp'; jest.mock('@strapi/helper-plugin', () => ({ ...jest.requireActual('@strapi/helper-plugin'), @@ -11,14 +9,14 @@ jest.mock('@strapi/helper-plugin', () => ({ */ usePersistentState: jest.fn().mockImplementation(() => [{ enabled: false }, jest.fn()]), auth: { - getUserInfo: () => ({ firstname: 'kai', lastname: 'doe', email: 'testemail@strapi.io' }), + get: () => ({ firstname: 'kai', lastname: 'doe', email: 'testemail@strapi.io' }), }, useGuidedTour: jest.fn(() => ({ setGuidedTourVisibility: jest.fn(), })), })); -jest.mock('../../PluginsInitializer', () => ({ +jest.mock('../PluginsInitializer', () => ({ PluginsInitializer() { return
PluginsInitializer
; }, @@ -34,10 +32,10 @@ describe('AuthenticatedApp', () => { }); it('should not crash', async () => { - const { queryByText } = render(); + const { queryByText, getByText } = render(); await waitFor(() => expect(queryByText(/Loading/)).not.toBeInTheDocument()); - expect(queryByText(/PluginsInitializer/)).toBeInTheDocument(); + expect(getByText(/PluginsInitializer/)).toBeInTheDocument(); }); }); diff --git a/packages/core/admin/admin/src/components/RBACProvider/tests/reducer.test.js b/packages/core/admin/admin/src/components/tests/RBACProvider.test.tsx similarity index 65% rename from packages/core/admin/admin/src/components/RBACProvider/tests/reducer.test.js rename to packages/core/admin/admin/src/components/tests/RBACProvider.test.tsx index bc50e42559..85a3556cb8 100644 --- a/packages/core/admin/admin/src/components/RBACProvider/tests/reducer.test.js +++ b/packages/core/admin/admin/src/components/tests/RBACProvider.test.tsx @@ -1,10 +1,15 @@ import { fixtures } from '@strapi/admin-test-utils'; -import { resetStore, setPermissions } from '../actions'; -import rbacProviderReducer, { initialState } from '../reducer'; +import { + Permission, + RBACReducer, + RBACState, + resetStoreAction, + setPermissionsAction, +} from '../RBACProvider'; -describe('rbacProviderReducer', () => { - let state; +describe('RBACReducer', () => { + let state: RBACState; beforeEach(() => { state = { @@ -16,24 +21,41 @@ describe('rbacProviderReducer', () => { it('returns the initial state', () => { const expected = state; - expect(rbacProviderReducer(undefined, {})).toEqual(expected); + // @ts-expect-error – testing the default case + expect(RBACReducer(undefined, {})).toEqual(expected); }); - describe('resetStore', () => { + describe('resetStoreAction', () => { it('should reset the state to its initial value', () => { - state.allPermissions = true; - state.collectionTypesRelatedPermissions = true; + state.allPermissions = []; + state.collectionTypesRelatedPermissions = { + apple: {}, + }; - expect(rbacProviderReducer(state, resetStore())).toEqual(initialState); + expect(RBACReducer(state, resetStoreAction())).toMatchInlineSnapshot(` + { + "allPermissions": null, + "collectionTypesRelatedPermissions": {}, + } + `); }); }); - describe('setPermissions', () => { + describe('setPermissionsAction', () => { it('should set the allPermissions value correctly', () => { - const permissions = [{ action: 'test', subject: null }]; + const permissions: Permission[] = [ + { + id: 0, + action: 'test', + subject: null, + conditions: [], + properties: {}, + actionParameters: {}, + }, + ]; const expected = { ...state, allPermissions: permissions }; - expect(rbacProviderReducer(state, setPermissions(permissions))).toEqual(expected); + expect(RBACReducer(state, setPermissionsAction(permissions))).toEqual(expected); }); it('should set the collectionTypesRelatedPermissions correctly', () => { @@ -89,7 +111,7 @@ describe('rbacProviderReducer', () => { }; expect( - rbacProviderReducer(state, setPermissions(fixtures.permissions.allPermissions)) + RBACReducer(state, setPermissionsAction(fixtures.permissions.contentManager)) .collectionTypesRelatedPermissions ).toEqual(expected); }); diff --git a/packages/core/admin/admin/src/content-manager/components/BlocksEditor/hooks/tests/useBlocksStore.test.js b/packages/core/admin/admin/src/content-manager/components/BlocksEditor/hooks/tests/useBlocksStore.test.js index 1298af830c..d01a438dfa 100644 --- a/packages/core/admin/admin/src/content-manager/components/BlocksEditor/hooks/tests/useBlocksStore.test.js +++ b/packages/core/admin/admin/src/content-manager/components/BlocksEditor/hooks/tests/useBlocksStore.test.js @@ -284,13 +284,13 @@ describe('useBlocksStore', () => { ); const link = screen.getByRole('link', 'Some link'); - expect(screen.queryByLabelText(/Delete/i, { selector: 'button' })).not.toBeInTheDocument(); - expect(screen.queryByLabelText(/Edit/i, { selector: 'button' })).not.toBeInTheDocument(); + expect(screen.queryByRole('button', { name: /Delete/i })).not.toBeInTheDocument(); + expect(screen.queryByRole('button', { name: /Edit/i })).not.toBeInTheDocument(); await user.click(link); - expect(screen.queryByLabelText(/Delete/i, { selector: 'button' })).toBeInTheDocument(); - expect(screen.queryByLabelText(/Edit/i, { selector: 'button' })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: /Delete/i })).toBeInTheDocument(); + expect(screen.getByRole('button', { name: /Edit/i })).toBeInTheDocument(); }); it('renders link fields to edit when user clicks the edit option and check save button disabled state', async () => { diff --git a/packages/core/admin/admin/src/content-manager/components/BlocksEditor/hooks/useBlocksStore.js b/packages/core/admin/admin/src/content-manager/components/BlocksEditor/hooks/useBlocksStore.js index 9858020808..43f57ff50f 100644 --- a/packages/core/admin/admin/src/content-manager/components/BlocksEditor/hooks/useBlocksStore.js +++ b/packages/core/admin/admin/src/content-manager/components/BlocksEditor/hooks/useBlocksStore.js @@ -2,15 +2,16 @@ import * as React from 'react'; import { Box, + Icon, Typography, BaseLink, Popover, - IconButton, Field, FieldLabel, FieldInput, Flex, Button, + Tooltip, } from '@strapi/design-system'; import { Code, @@ -255,6 +256,18 @@ Image.propTypes = { }).isRequired, }; +// Make sure the tooltip is above the popover +const TooltipCustom = styled(Tooltip)` + z-index: 6; +`; + +// Used for the Edit and Cancel buttons in the link popover +const CustomButton = styled(Button)` + & > span { + line-height: normal; + } +`; + const Link = React.forwardRef(({ element, children, ...attributes }, forwardedRef) => { const { formatMessage } = useIntl(); const editor = useSlate(); @@ -376,25 +389,49 @@ const Link = React.forwardRef(({ element, children, ...attributes }, forwardedRe - } - size="L" - variant="danger" - onClick={() => removeLink(editor)} - label={formatMessage({ + - } - size="L" - onClick={() => setIsEditing(true)} - label={formatMessage({ + > + removeLink(editor)} + aria-label={formatMessage({ + id: 'components.Blocks.popover.delete', + defaultMessage: 'Delete', + })} + type="button" + justifyContent="center" + > + + + + + + > + setIsEditing(true)} + aria-label={formatMessage({ + id: 'components.Blocks.popover.edit', + defaultMessage: 'Edit', + })} + type="button" + justifyContent="center" + > + + + )} diff --git a/packages/core/admin/admin/src/content-manager/pages/ListView/index.js b/packages/core/admin/admin/src/content-manager/pages/ListView/index.js index b463796cb1..df9d1c7ce8 100644 --- a/packages/core/admin/admin/src/content-manager/pages/ListView/index.js +++ b/packages/core/admin/admin/src/content-manager/pages/ListView/index.js @@ -44,7 +44,7 @@ import { useDispatch } from 'react-redux'; import { useHistory, useLocation, Link as ReactRouterLink } from 'react-router-dom'; import { HOOKS } from '../../../constants'; -import { useTypedSelector } from '../../../core/store'; +import { useTypedSelector } from '../../../core/store/hooks'; import { useAdminUsers } from '../../../hooks/useAdminUsers'; import { useEnterprise } from '../../../hooks/useEnterprise'; import { InjectionZone } from '../../../shared/components'; diff --git a/packages/core/admin/admin/src/contexts/ApiTokenPermissions/index.js b/packages/core/admin/admin/src/contexts/ApiTokenPermissions/index.js deleted file mode 100644 index e41506aae4..0000000000 --- a/packages/core/admin/admin/src/contexts/ApiTokenPermissions/index.js +++ /dev/null @@ -1,25 +0,0 @@ -import React, { createContext, useContext } from 'react'; - -import PropTypes from 'prop-types'; - -const ApiTokenPermissionsContext = createContext({}); - -const ApiTokenPermissionsContextProvider = ({ children, ...rest }) => { - return ( - - {children} - - ); -}; - -const useApiTokenPermissionsContext = () => useContext(ApiTokenPermissionsContext); - -ApiTokenPermissionsContextProvider.propTypes = { - children: PropTypes.node.isRequired, -}; - -export { - ApiTokenPermissionsContext, - ApiTokenPermissionsContextProvider, - useApiTokenPermissionsContext, -}; diff --git a/packages/core/admin/admin/src/contexts/admin.ts b/packages/core/admin/admin/src/contexts/admin.ts index 33667d2d65..b862b7f13f 100644 --- a/packages/core/admin/admin/src/contexts/admin.ts +++ b/packages/core/admin/admin/src/contexts/admin.ts @@ -16,3 +16,4 @@ const AdminContext = React.createContext({ const useAdmin = () => React.useContext(AdminContext); export { AdminContext, useAdmin }; +export type { AdminContextValue }; diff --git a/packages/core/admin/admin/src/contexts/apiTokenPermissions.tsx b/packages/core/admin/admin/src/contexts/apiTokenPermissions.tsx new file mode 100644 index 0000000000..4c74e4090b --- /dev/null +++ b/packages/core/admin/admin/src/contexts/apiTokenPermissions.tsx @@ -0,0 +1,62 @@ +/* eslint-disable check-file/filename-naming-convention */ + +import * as React from 'react'; + +import { Entity } from '@strapi/types'; + +interface PseudoEvent { + target: { value: string }; +} + +interface APITokenPermissionsContextProviderProps { + selectedAction: string[] | null; + routes: string[]; + selectedActions: string[]; + data: { + allActionsIds: Entity.ID[]; + permissions: { + apiId: string; + label: string; + controllers: { controller: string; actions: { actionId: string; action: string } }[]; + }[]; + }; + onChange: ({ target: { value } }: PseudoEvent) => void; + onChangeSelectAll: ({ target: { value } }: PseudoEvent) => void; + setSelectedAction: ({ target: { value } }: PseudoEvent) => void; +} + +interface ApiTokenPermissionsContextProviderProps extends APITokenPermissionsContextProviderProps { + children: React.ReactNode[]; +} + +const ApiTokenPermissionsContext = React.createContext({ + selectedAction: null, + routes: [], + selectedActions: [], + data: { + allActionsIds: [], + permissions: [], + }, + onChange: () => {}, + onChangeSelectAll: () => {}, + setSelectedAction: () => {}, +}); + +const ApiTokenPermissionsContextProvider = ({ + children, + ...rest +}: ApiTokenPermissionsContextProviderProps) => { + return ( + + {children} + + ); +}; + +const useApiTokenPermissionsContext = () => React.useContext(ApiTokenPermissionsContext); + +export { + ApiTokenPermissionsContext, + ApiTokenPermissionsContextProvider, + useApiTokenPermissionsContext, +}; diff --git a/packages/core/admin/admin/src/core/store.ts b/packages/core/admin/admin/src/core/store/configure.ts similarity index 55% rename from packages/core/admin/admin/src/core/store.ts rename to packages/core/admin/admin/src/core/store/configure.ts index d680181af8..54e77e6e14 100644 --- a/packages/core/admin/admin/src/core/store.ts +++ b/packages/core/admin/admin/src/core/store/configure.ts @@ -4,42 +4,28 @@ import { Middleware, Reducer, combineReducers, - createSelector, - Selector, } from '@reduxjs/toolkit'; -import { useDispatch, useStore, TypedUseSelectorHook, useSelector } from 'react-redux'; +import { RBACReducer } from '../../components/RBACProvider'; // @ts-expect-error no types, yet. -import rbacProviderReducer from '../components/RBACProvider/reducer'; +import rbacManagerReducer from '../../content-manager/hooks/useSyncRbac/reducer'; // @ts-expect-error no types, yet. -import rbacManagerReducer from '../content-manager/hooks/useSyncRbac/reducer'; +import cmAppReducer from '../../content-manager/pages/App/reducer'; // @ts-expect-error no types, yet. -import cmAppReducer from '../content-manager/pages/App/reducer'; +import editViewLayoutManagerReducer from '../../content-manager/pages/EditViewLayoutManager/reducer'; // @ts-expect-error no types, yet. -import editViewLayoutManagerReducer from '../content-manager/pages/EditViewLayoutManager/reducer'; +import listViewReducer from '../../content-manager/pages/ListView/reducer'; // @ts-expect-error no types, yet. -import listViewReducer from '../content-manager/pages/ListView/reducer'; +import editViewCrudReducer from '../../content-manager/sharedReducers/crudReducer/reducer'; // @ts-expect-error no types, yet. -import editViewCrudReducer from '../content-manager/sharedReducers/crudReducer/reducer'; -// @ts-expect-error no types, yet. -import appReducer from '../pages/App/reducer'; - -const createReducer = ( - appReducers: Record, - asyncReducers: Record -) => { - return combineReducers({ - ...appReducers, - ...asyncReducers, - }); -}; +import appReducer from '../../pages/App/reducer'; /** * @description Static reducers are ones we know, they live in the admin package. */ -const staticReducers: Record = { +const staticReducers = { admin_app: appReducer, - rbacProvider: rbacProviderReducer, + rbacProvider: RBACReducer, 'content-manager_app': cmAppReducer, 'content-manager_listView': listViewReducer, 'content-manager_rbacManager': rbacManagerReducer, @@ -60,8 +46,13 @@ const injectReducerStoreEnhancer: (appReducers: Record) => Stor asyncReducers, injectReducer: (key: string, asyncReducer: Reducer) => { asyncReducers[key] = asyncReducer; - // @ts-expect-error we dynamically add reducers which makes the types uncomfortable. - store.replaceReducer(createReducer(appReducers, asyncReducers)); + store.replaceReducer( + // @ts-expect-error we dynamically add reducers which makes the types uncomfortable. + combineReducers({ + ...appReducers, + ...asyncReducers, + }) + ); }, }; }; @@ -74,10 +65,10 @@ const configureStoreImpl = ( appMiddlewares: Array<() => Middleware> = [], injectedReducers: Record = {} ) => { - const coreReducers = { ...staticReducers, ...injectedReducers }; + const coreReducers = { ...staticReducers, ...injectedReducers } as const; const store = configureStore({ - reducer: createReducer(coreReducers, {}), + reducer: coreReducers, devTools: process.env.NODE_ENV !== 'production', middleware: (getDefaultMiddleware) => [ ...getDefaultMiddleware(), @@ -95,20 +86,6 @@ type Store = ReturnType & { }; type RootState = ReturnType; -type AppDispatch = Store['dispatch']; -const useTypedDispatch: () => AppDispatch = useDispatch; -const useTypedStore = useStore as () => Store; -const useTypedSelector: TypedUseSelectorHook = useSelector; - -const createTypedSelector = (selector: Selector) => - createSelector((state: RootState) => state, selector); - -export { - useTypedDispatch, - useTypedStore, - useTypedSelector, - configureStoreImpl as configureStore, - createTypedSelector, -}; -export type { RootState }; +export { configureStoreImpl as configureStore }; +export type { RootState, Store }; diff --git a/packages/core/admin/admin/src/core/store/hooks.ts b/packages/core/admin/admin/src/core/store/hooks.ts new file mode 100644 index 0000000000..7b2a9d6643 --- /dev/null +++ b/packages/core/admin/admin/src/core/store/hooks.ts @@ -0,0 +1,15 @@ +import { createSelector, Selector } from '@reduxjs/toolkit'; +import { useDispatch, useStore, TypedUseSelectorHook, useSelector } from 'react-redux'; + +import type { RootState, Store } from './configure'; + +type AppDispatch = Store['dispatch']; + +const useTypedDispatch: () => AppDispatch = useDispatch; +const useTypedStore = useStore as () => Store; +const useTypedSelector: TypedUseSelectorHook = useSelector; + +const createTypedSelector = (selector: Selector) => + createSelector((state: RootState) => state, selector); + +export { useTypedDispatch, useTypedStore, useTypedSelector, createTypedSelector }; diff --git a/packages/core/admin/admin/src/hooks/useInjectReducer/tests/useInjectReducer.test.js b/packages/core/admin/admin/src/hooks/useInjectReducer/tests/useInjectReducer.test.js index 19f1d8abb0..3e07be9a87 100644 --- a/packages/core/admin/admin/src/hooks/useInjectReducer/tests/useInjectReducer.test.js +++ b/packages/core/admin/admin/src/hooks/useInjectReducer/tests/useInjectReducer.test.js @@ -3,7 +3,7 @@ import React from 'react'; import { renderHook } from '@testing-library/react'; import { Provider } from 'react-redux'; -import { configureStore } from '../../../core/store'; +import { configureStore } from '../../../core/store/configure'; import { useInjectReducer } from '../useInjectReducer'; const store = configureStore(); diff --git a/packages/core/admin/admin/src/pages/Admin/index.js b/packages/core/admin/admin/src/pages/Admin/index.js index 50d93bf520..49d0564c6f 100644 --- a/packages/core/admin/admin/src/pages/Admin/index.js +++ b/packages/core/admin/admin/src/pages/Admin/index.js @@ -33,10 +33,12 @@ const MarketplacePage = lazy(() => import(/* webpackChunkName: "Admin_marketplace" */ '../MarketplacePage') ); const NotFoundPage = lazy(() => - import(/* webpackChunkName: "Admin_NotFoundPage" */ '../NotFoundPage') + import(/* webpackChunkName: "Admin_NotFoundPage" */ '../NotFoundPage').then(({ NotFoundPage }) => ({ default: NotFoundPage })) ); const InternalErrorPage = lazy(() => - import(/* webpackChunkName: "Admin_InternalErrorPage" */ '../InternalErrorPage') + import(/* webpackChunkName: "Admin_InternalErrorPage" */ '../InternalErrorPage').then(({ InternalErrorPage }) => ({ + default: InternalErrorPage + })) ); const ProfilePage = lazy(() => diff --git a/packages/core/admin/admin/src/pages/App/index.js b/packages/core/admin/admin/src/pages/App/index.js index 8009c739d8..b2621d6aaa 100644 --- a/packages/core/admin/admin/src/pages/App/index.js +++ b/packages/core/admin/admin/src/pages/App/index.js @@ -27,13 +27,15 @@ import { useConfiguration } from '../../hooks/useConfiguration'; import { useEnterprise } from '../../hooks/useEnterprise'; import { createRoute, makeUniqueRoutes } from '../../utils'; import AuthPage from '../AuthPage'; -import NotFoundPage from '../NotFoundPage'; +import { NotFoundPage } from '../NotFoundPage'; import UseCasePage from '../UseCasePage'; import { ROUTES_CE, SET_ADMIN_PERMISSIONS } from './constants'; const AuthenticatedApp = lazy(() => - import(/* webpackChunkName: "Admin-authenticatedApp" */ '../../components/AuthenticatedApp') + import(/* webpackChunkName: "Admin-authenticatedApp" */ '../../components/AuthenticatedApp').then( + (mod) => ({ default: mod.AuthenticatedApp }) + ) ); function App() { diff --git a/packages/core/admin/admin/src/pages/InternalErrorPage/index.js b/packages/core/admin/admin/src/pages/InternalErrorPage.tsx similarity index 80% rename from packages/core/admin/admin/src/pages/InternalErrorPage/index.js rename to packages/core/admin/admin/src/pages/InternalErrorPage.tsx index 030b408e49..5eb64117e9 100644 --- a/packages/core/admin/admin/src/pages/InternalErrorPage/index.js +++ b/packages/core/admin/admin/src/pages/InternalErrorPage.tsx @@ -4,15 +4,21 @@ * This is the page we show when the user gets a 500 error * */ -import React from 'react'; -import { ContentLayout, EmptyStateLayout, HeaderLayout, Main } from '@strapi/design-system'; -import { LinkButton, useFocusWhenNavigate } from '@strapi/helper-plugin'; +import { + ContentLayout, + EmptyStateLayout, + HeaderLayout, + LinkButton, + Main, +} from '@strapi/design-system'; +import { useFocusWhenNavigate } from '@strapi/helper-plugin'; import { ArrowRight, EmptyPictures } from '@strapi/icons'; import { useIntl } from 'react-intl'; -const InternalErrorPage = () => { +export const InternalErrorPage = () => { const { formatMessage } = useIntl(); + useFocusWhenNavigate(); return ( @@ -46,5 +52,3 @@ const InternalErrorPage = () => { ); }; - -export default InternalErrorPage; diff --git a/packages/core/admin/admin/src/pages/InternalErrorPage/tests/index.test.js b/packages/core/admin/admin/src/pages/InternalErrorPage/tests/index.test.js deleted file mode 100644 index f228d7b552..0000000000 --- a/packages/core/admin/admin/src/pages/InternalErrorPage/tests/index.test.js +++ /dev/null @@ -1,433 +0,0 @@ -import React from 'react'; - -import { lightTheme, ThemeProvider } from '@strapi/design-system'; -import { render } from '@testing-library/react'; -import { createMemoryHistory } from 'history'; -import { IntlProvider } from 'react-intl'; -import { Router } from 'react-router-dom'; - -import InternalErrorPage from '../index'; - -const history = createMemoryHistory(); - -const App = ( - - - - - - - -); - -describe('InternalErrorPage', () => { - it('renders and matches the snapshot', () => { - const { - container: { firstChild }, - } = render(App); - - expect(firstChild).toMatchInlineSnapshot(` - .c6 { - font-weight: 600; - font-size: 2rem; - line-height: 1.25; - color: #32324d; - } - - .c13 { - font-weight: 500; - font-size: 1rem; - line-height: 1.25; - color: #666687; - text-align: center; - } - - .c18 { - font-size: 0.75rem; - line-height: 1.33; - font-weight: 600; - color: #ffffff; - } - - .c1 { - background: #f6f6f9; - padding-top: 40px; - padding-right: 56px; - padding-bottom: 40px; - padding-left: 56px; - } - - .c3 { - min-width: 0; - } - - .c7 { - padding-right: 56px; - padding-left: 56px; - } - - .c8 { - background: #ffffff; - padding: 64px; - border-radius: 4px; - box-shadow: 0px 1px 4px rgba(33,33,52,0.1); - } - - .c10 { - padding-bottom: 24px; - } - - .c12 { - padding-bottom: 16px; - } - - .c14 { - background: #4945ff; - padding-top: 8px; - padding-right: 16px; - padding-bottom: 8px; - padding-left: 16px; - border-radius: 4px; - border-color: #4945ff; - border: 1px solid #4945ff; - } - - .c2 { - -webkit-align-items: center; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-flex-direction: row; - -ms-flex-direction: row; - flex-direction: row; - -webkit-box-pack: justify; - -webkit-justify-content: space-between; - -ms-flex-pack: justify; - justify-content: space-between; - } - - .c4 { - -webkit-align-items: center; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-flex-direction: row; - -ms-flex-direction: row; - flex-direction: row; - } - - .c9 { - -webkit-align-items: center; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-flex-direction: column; - -ms-flex-direction: column; - flex-direction: column; - } - - .c15 { - -webkit-align-items: center; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - display: -webkit-inline-box; - display: -webkit-inline-flex; - display: -ms-inline-flexbox; - display: inline-flex; - -webkit-flex-direction: row; - -ms-flex-direction: row; - flex-direction: row; - gap: 8px; - } - - .c16 { - position: relative; - outline: none; - } - - .c16 > svg { - height: 12px; - width: 12px; - } - - .c16 > svg > g, - .c16 > svg path { - fill: #ffffff; - } - - .c16[aria-disabled='true'] { - pointer-events: none; - } - - .c16:after { - -webkit-transition-property: all; - transition-property: all; - -webkit-transition-duration: 0.2s; - transition-duration: 0.2s; - border-radius: 8px; - content: ''; - position: absolute; - top: -4px; - bottom: -4px; - left: -4px; - right: -4px; - border: 2px solid transparent; - } - - .c16:focus-visible { - outline: none; - } - - .c16:focus-visible:after { - border-radius: 8px; - content: ''; - position: absolute; - top: -5px; - bottom: -5px; - left: -5px; - right: -5px; - border: 2px solid #4945ff; - } - - .c11 svg { - height: 5.5rem; - } - - .c0:focus-visible { - outline: none; - } - - .c17 { - -webkit-text-decoration: none; - text-decoration: none; - border: 1px solid #d9d8ff; - background: #f0f0ff; - } - - .c17[aria-disabled='true'] { - border: 1px solid #dcdce4; - background: #eaeaef; - } - - .c17[aria-disabled='true'] .c5 { - color: #666687; - } - - .c17[aria-disabled='true'] svg > g, - .c17[aria-disabled='true'] svg path { - fill: #666687; - } - - .c17[aria-disabled='true']:active { - border: 1px solid #dcdce4; - background: #eaeaef; - } - - .c17[aria-disabled='true']:active .c5 { - color: #666687; - } - - .c17[aria-disabled='true']:active svg > g, - .c17[aria-disabled='true']:active svg path { - fill: #666687; - } - - .c17:hover { - background-color: #ffffff; - } - - .c17:active { - background-color: #ffffff; - border: 1px solid #4945ff; - } - - .c17:active .c5 { - color: #4945ff; - } - - .c17:active svg > g, - .c17:active svg path { - fill: #4945ff; - } - - .c17 .c5 { - color: #271fe0; - } - - .c17 svg > g, - .c17 svg path { - fill: #271fe0; - } - -
-
-
-
-
-

- Page not found -

-
-
-
-
-
-
- -
-

- An error occured -

-
- - - Back to homepage - - - -
-
-
- `); - }); -}); diff --git a/packages/core/admin/admin/src/pages/NotFoundPage/index.js b/packages/core/admin/admin/src/pages/NotFoundPage.tsx similarity index 82% rename from packages/core/admin/admin/src/pages/NotFoundPage/index.js rename to packages/core/admin/admin/src/pages/NotFoundPage.tsx index a48d81b1b2..9ebb05c8c2 100644 --- a/packages/core/admin/admin/src/pages/NotFoundPage/index.js +++ b/packages/core/admin/admin/src/pages/NotFoundPage.tsx @@ -4,14 +4,18 @@ * This is the page we show when the user visits a url that doesn't have a route * */ -import React from 'react'; - -import { ContentLayout, EmptyStateLayout, HeaderLayout, Main } from '@strapi/design-system'; -import { LinkButton, useFocusWhenNavigate } from '@strapi/helper-plugin'; +import { + ContentLayout, + EmptyStateLayout, + HeaderLayout, + LinkButton, + Main, +} from '@strapi/design-system'; +import { useFocusWhenNavigate } from '@strapi/helper-plugin'; import { ArrowRight, EmptyPictures } from '@strapi/icons'; import { useIntl } from 'react-intl'; -const NoContentType = () => { +export const NotFoundPage = () => { const { formatMessage } = useIntl(); useFocusWhenNavigate(); @@ -46,5 +50,3 @@ const NoContentType = () => { ); }; - -export default NoContentType; diff --git a/packages/core/admin/admin/src/pages/NotFoundPage/tests/index.test.js b/packages/core/admin/admin/src/pages/NotFoundPage/tests/index.test.js deleted file mode 100644 index ee340a39de..0000000000 --- a/packages/core/admin/admin/src/pages/NotFoundPage/tests/index.test.js +++ /dev/null @@ -1,433 +0,0 @@ -import React from 'react'; - -import { lightTheme, ThemeProvider } from '@strapi/design-system'; -import { render } from '@testing-library/react'; -import { createMemoryHistory } from 'history'; -import { IntlProvider } from 'react-intl'; -import { Router } from 'react-router-dom'; - -import NotFoundPage from '../index'; - -const history = createMemoryHistory(); - -const App = ( - - - - - - - -); - -describe('NotFoundPage', () => { - it('renders and matches the snapshot', () => { - const { - container: { firstChild }, - } = render(App); - - expect(firstChild).toMatchInlineSnapshot(` - .c6 { - font-weight: 600; - font-size: 2rem; - line-height: 1.25; - color: #32324d; - } - - .c13 { - font-weight: 500; - font-size: 1rem; - line-height: 1.25; - color: #666687; - text-align: center; - } - - .c18 { - font-size: 0.75rem; - line-height: 1.33; - font-weight: 600; - color: #ffffff; - } - - .c1 { - background: #f6f6f9; - padding-top: 40px; - padding-right: 56px; - padding-bottom: 40px; - padding-left: 56px; - } - - .c3 { - min-width: 0; - } - - .c7 { - padding-right: 56px; - padding-left: 56px; - } - - .c8 { - background: #ffffff; - padding: 64px; - border-radius: 4px; - box-shadow: 0px 1px 4px rgba(33,33,52,0.1); - } - - .c10 { - padding-bottom: 24px; - } - - .c12 { - padding-bottom: 16px; - } - - .c14 { - background: #4945ff; - padding-top: 8px; - padding-right: 16px; - padding-bottom: 8px; - padding-left: 16px; - border-radius: 4px; - border-color: #4945ff; - border: 1px solid #4945ff; - } - - .c2 { - -webkit-align-items: center; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-flex-direction: row; - -ms-flex-direction: row; - flex-direction: row; - -webkit-box-pack: justify; - -webkit-justify-content: space-between; - -ms-flex-pack: justify; - justify-content: space-between; - } - - .c4 { - -webkit-align-items: center; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-flex-direction: row; - -ms-flex-direction: row; - flex-direction: row; - } - - .c9 { - -webkit-align-items: center; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - display: -webkit-box; - display: -webkit-flex; - display: -ms-flexbox; - display: flex; - -webkit-flex-direction: column; - -ms-flex-direction: column; - flex-direction: column; - } - - .c15 { - -webkit-align-items: center; - -webkit-box-align: center; - -ms-flex-align: center; - align-items: center; - display: -webkit-inline-box; - display: -webkit-inline-flex; - display: -ms-inline-flexbox; - display: inline-flex; - -webkit-flex-direction: row; - -ms-flex-direction: row; - flex-direction: row; - gap: 8px; - } - - .c16 { - position: relative; - outline: none; - } - - .c16 > svg { - height: 12px; - width: 12px; - } - - .c16 > svg > g, - .c16 > svg path { - fill: #ffffff; - } - - .c16[aria-disabled='true'] { - pointer-events: none; - } - - .c16:after { - -webkit-transition-property: all; - transition-property: all; - -webkit-transition-duration: 0.2s; - transition-duration: 0.2s; - border-radius: 8px; - content: ''; - position: absolute; - top: -4px; - bottom: -4px; - left: -4px; - right: -4px; - border: 2px solid transparent; - } - - .c16:focus-visible { - outline: none; - } - - .c16:focus-visible:after { - border-radius: 8px; - content: ''; - position: absolute; - top: -5px; - bottom: -5px; - left: -5px; - right: -5px; - border: 2px solid #4945ff; - } - - .c11 svg { - height: 5.5rem; - } - - .c0:focus-visible { - outline: none; - } - - .c17 { - -webkit-text-decoration: none; - text-decoration: none; - border: 1px solid #d9d8ff; - background: #f0f0ff; - } - - .c17[aria-disabled='true'] { - border: 1px solid #dcdce4; - background: #eaeaef; - } - - .c17[aria-disabled='true'] .c5 { - color: #666687; - } - - .c17[aria-disabled='true'] svg > g, - .c17[aria-disabled='true'] svg path { - fill: #666687; - } - - .c17[aria-disabled='true']:active { - border: 1px solid #dcdce4; - background: #eaeaef; - } - - .c17[aria-disabled='true']:active .c5 { - color: #666687; - } - - .c17[aria-disabled='true']:active svg > g, - .c17[aria-disabled='true']:active svg path { - fill: #666687; - } - - .c17:hover { - background-color: #ffffff; - } - - .c17:active { - background-color: #ffffff; - border: 1px solid #4945ff; - } - - .c17:active .c5 { - color: #4945ff; - } - - .c17:active svg > g, - .c17:active svg path { - fill: #4945ff; - } - - .c17 .c5 { - color: #271fe0; - } - - .c17 svg > g, - .c17 svg path { - fill: #271fe0; - } - -
-
-
-
-
-

- Page not found -

-
-
-
-
-
-
- -
-

- Oops! We can't seem to find the page you're looging for... -

-
- - - Back to homepage - - - -
-
-
- `); - }); -}); diff --git a/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/ActionBoundRoutes/index.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/ActionBoundRoutes/index.js index 75542b39ac..b5f661fa66 100644 --- a/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/ActionBoundRoutes/index.js +++ b/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/ActionBoundRoutes/index.js @@ -3,7 +3,7 @@ import React from 'react'; import { Flex, GridItem, Typography } from '@strapi/design-system'; import { useIntl } from 'react-intl'; -import { useApiTokenPermissionsContext } from '../../../../../../../contexts/ApiTokenPermissions'; +import { useApiTokenPermissionsContext } from '../../../../../../../contexts/apiTokenPermissions'; import BoundRoute from '../BoundRoute'; const ActionBoundRoutes = () => { diff --git a/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/CollapsableContentType/index.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/CollapsableContentType/index.js index 147720b84e..20b21ae175 100644 --- a/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/CollapsableContentType/index.js +++ b/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/CollapsableContentType/index.js @@ -17,7 +17,7 @@ import PropTypes from 'prop-types'; import { useIntl } from 'react-intl'; import styled from 'styled-components'; -import { useApiTokenPermissionsContext } from '../../../../../../../contexts/ApiTokenPermissions'; +import { useApiTokenPermissionsContext } from '../../../../../../../contexts/apiTokenPermissions'; import CheckboxWrapper from './CheckBoxWrapper'; diff --git a/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/Permissions/index.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/Permissions/index.js index 11bc80363a..20fa4f72f0 100644 --- a/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/Permissions/index.js +++ b/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/components/Permissions/index.js @@ -3,7 +3,7 @@ import React, { memo } from 'react'; import { Flex, Grid, GridItem, Typography } from '@strapi/design-system'; import { useIntl } from 'react-intl'; -import { useApiTokenPermissionsContext } from '../../../../../../../contexts/ApiTokenPermissions'; +import { useApiTokenPermissionsContext } from '../../../../../../../contexts/apiTokenPermissions'; import ActionBoundRoutes from '../ActionBoundRoutes'; import ContentTypesSection from '../ContenTypesSection'; diff --git a/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/index.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/index.js index 6d48006a71..9a433519fa 100644 --- a/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/index.js +++ b/packages/core/admin/admin/src/pages/SettingsPage/pages/ApiTokens/EditView/index.js @@ -18,7 +18,7 @@ import { useQuery } from 'react-query'; import { useSelector } from 'react-redux'; import { useHistory, useRouteMatch } from 'react-router-dom'; -import { ApiTokenPermissionsContextProvider } from '../../../../../contexts/ApiTokenPermissions'; +import { ApiTokenPermissionsContextProvider } from '../../../../../contexts/apiTokenPermissions'; import { formatAPIErrors } from '../../../../../utils'; import { selectAdminPermissions } from '../../../../App/selectors'; import { API_TOKEN_TYPE } from '../../../components/Tokens/constants'; diff --git a/packages/core/admin/package.json b/packages/core/admin/package.json index f972adf208..1f881f2cf2 100644 --- a/packages/core/admin/package.json +++ b/packages/core/admin/package.json @@ -148,7 +148,7 @@ "@strapi/strapi": "^4.3.4" }, "engines": { - "node": ">=16.0.0 <=20.x.x", + "node": ">=18.0.0 <=20.x.x", "npm": ">=6.0.0" }, "nx": { diff --git a/packages/core/content-manager/package.json b/packages/core/content-manager/package.json index c1f0bbe71a..c5463cce54 100644 --- a/packages/core/content-manager/package.json +++ b/packages/core/content-manager/package.json @@ -31,7 +31,7 @@ "qs": "6.11.1" }, "engines": { - "node": ">=16.0.0 <=20.x.x", + "node": ">=18.0.0 <=20.x.x", "npm": ">=6.0.0" }, "strapi": { diff --git a/packages/core/content-type-builder/package.json b/packages/core/content-type-builder/package.json index cb2227662e..2cc1ddbad5 100644 --- a/packages/core/content-type-builder/package.json +++ b/packages/core/content-type-builder/package.json @@ -63,7 +63,7 @@ "styled-components": "5.3.3" }, "engines": { - "node": ">=16.0.0 <=20.x.x", + "node": ">=18.0.0 <=20.x.x", "npm": ">=6.0.0" }, "strapi": { diff --git a/packages/core/data-transfer/package.json b/packages/core/data-transfer/package.json index 64b54fbc2f..06d9374baa 100644 --- a/packages/core/data-transfer/package.json +++ b/packages/core/data-transfer/package.json @@ -82,7 +82,7 @@ "@strapi/strapi": "^4.14.4" }, "engines": { - "node": ">=16.0.0 <=20.x.x", + "node": ">=18.0.0 <=20.x.x", "npm": ">=6.0.0" } } diff --git a/packages/core/database/package.json b/packages/core/database/package.json index 8ce54061b9..4d97fa64cc 100644 --- a/packages/core/database/package.json +++ b/packages/core/database/package.json @@ -55,7 +55,7 @@ "tsconfig": "4.14.4" }, "engines": { - "node": ">=16.0.0 <=20.x.x", + "node": ">=18.0.0 <=20.x.x", "npm": ">=6.0.0" } } diff --git a/packages/core/email/package.json b/packages/core/email/package.json index c00b5b8ffe..b76aa3441e 100644 --- a/packages/core/email/package.json +++ b/packages/core/email/package.json @@ -85,7 +85,7 @@ "styled-components": "5.3.3" }, "engines": { - "node": ">=16.0.0 <=20.x.x", + "node": ">=18.0.0 <=20.x.x", "npm": ">=6.0.0" }, "strapi": { diff --git a/packages/core/helper-plugin/package.json b/packages/core/helper-plugin/package.json index dcdeeecc44..f62d42c33d 100644 --- a/packages/core/helper-plugin/package.json +++ b/packages/core/helper-plugin/package.json @@ -104,7 +104,7 @@ "styled-components": "^5.3.3" }, "engines": { - "node": ">=16.0.0 <=20.x.x", + "node": ">=18.0.0 <=20.x.x", "npm": ">=6.0.0" }, "nx": { diff --git a/packages/core/helper-plugin/src/components/CheckPagePermissions.tsx b/packages/core/helper-plugin/src/components/CheckPagePermissions.tsx index 4c0f7ea417..a81d0e3b39 100644 --- a/packages/core/helper-plugin/src/components/CheckPagePermissions.tsx +++ b/packages/core/helper-plugin/src/components/CheckPagePermissions.tsx @@ -3,15 +3,11 @@ import * as React from 'react'; import { Redirect } from 'react-router-dom'; import { useNotification } from '../features/Notifications'; -import { useRBACProvider } from '../features/RBAC'; +import { Permission, useRBACProvider } from '../features/RBAC'; import { hasPermissions } from '../utils/hasPermissions'; import { LoadingIndicatorPage } from './LoadingIndicatorPage'; -import type { domain } from '@strapi/permissions'; - -type Permission = domain.permission.Permission; - export interface CheckPagePermissionsProps { children: React.ReactNode; permissions?: Permission[]; diff --git a/packages/core/helper-plugin/src/components/CheckPermissions.tsx b/packages/core/helper-plugin/src/components/CheckPermissions.tsx index f2d294237d..226e5fe004 100644 --- a/packages/core/helper-plugin/src/components/CheckPermissions.tsx +++ b/packages/core/helper-plugin/src/components/CheckPermissions.tsx @@ -1,13 +1,9 @@ import * as React from 'react'; import { useNotification } from '../features/Notifications'; -import { useRBACProvider } from '../features/RBAC'; +import { useRBACProvider, Permission } from '../features/RBAC'; import { hasPermissions } from '../utils/hasPermissions'; -import type { domain } from '@strapi/permissions'; - -type Permission = domain.permission.Permission; - // NOTE: this component is very similar to the CheckPagePermissions // except that it does not handle redirections nor loading state diff --git a/packages/core/helper-plugin/src/features/AppInfo.tsx b/packages/core/helper-plugin/src/features/AppInfo.tsx index b7c022c06b..e265b8e6a1 100644 --- a/packages/core/helper-plugin/src/features/AppInfo.tsx +++ b/packages/core/helper-plugin/src/features/AppInfo.tsx @@ -115,3 +115,5 @@ export { useAppInfo, useAppInfos, }; + +export type { AppInfoContextValue, AppInfoProviderProps }; diff --git a/packages/core/helper-plugin/src/features/CustomFields.tsx b/packages/core/helper-plugin/src/features/CustomFields.tsx index 0fd98a03da..10f1bf2864 100644 --- a/packages/core/helper-plugin/src/features/CustomFields.tsx +++ b/packages/core/helper-plugin/src/features/CustomFields.tsx @@ -128,3 +128,10 @@ const CustomFieldsProvider = ({ children, customFields }: CustomFieldsProviderPr const useCustomFields = () => React.useContext(CustomFieldsContext); export { CustomFieldsContext, CustomFieldsProvider, useCustomFields }; +export type { + CustomFieldsProviderProps, + CustomField, + CustomFieldComponents, + CustomFieldOption, + CustomFieldOptions, +}; diff --git a/packages/core/helper-plugin/src/features/Library.tsx b/packages/core/helper-plugin/src/features/Library.tsx index 0a9c86ab8d..3a47b325b4 100644 --- a/packages/core/helper-plugin/src/features/Library.tsx +++ b/packages/core/helper-plugin/src/features/Library.tsx @@ -4,7 +4,7 @@ import * as React from 'react'; * Context * -----------------------------------------------------------------------------------------------*/ -export interface LibraryContextValue { +interface LibraryContextValue { fields?: Record; components?: Record; } @@ -32,3 +32,4 @@ const LibraryProvider = ({ children, fields, components }: LibraryProviderProps) const useLibrary = () => React.useContext(LibraryContext); export { LibraryContext, LibraryProvider, useLibrary }; +export type { LibraryContextValue, LibraryProviderProps }; diff --git a/packages/core/helper-plugin/src/features/RBAC.tsx b/packages/core/helper-plugin/src/features/RBAC.tsx index 69a7e419aa..f67c386bde 100644 --- a/packages/core/helper-plugin/src/features/RBAC.tsx +++ b/packages/core/helper-plugin/src/features/RBAC.tsx @@ -1,9 +1,23 @@ import * as React from 'react'; -import type { domain } from '@strapi/permissions'; +import type { Entity } from '@strapi/types'; import type { QueryObserverBaseResult } from 'react-query'; -type Permission = domain.permission.Permission; +/** + * This is duplicated from the `@strapi/admin` package. + */ +type Permission = { + id?: Entity.ID; + action: string; + subject: string | null; + actionParameters?: object; + properties?: { + fields?: string[]; + locales?: string[]; + [key: string]: unknown; + }; + conditions?: string[]; +}; /* ------------------------------------------------------------------------------------------------- * Context diff --git a/packages/core/helper-plugin/src/features/StrapiApp.tsx b/packages/core/helper-plugin/src/features/StrapiApp.tsx index b7ac96dd82..5383b15fc9 100644 --- a/packages/core/helper-plugin/src/features/StrapiApp.tsx +++ b/packages/core/helper-plugin/src/features/StrapiApp.tsx @@ -4,10 +4,7 @@ import { LinkProps } from 'react-router-dom'; import { TranslationMessage } from '../types'; -import type { domain } from '@strapi/permissions'; - -type Permission = domain.permission.Permission; - +import type { Permission } from './RBAC'; interface MenuItem extends Pick { to: string; icon: React.ElementType; @@ -62,7 +59,7 @@ type RunHookWaterfall = ( store: Store ) => unknown | Promise; -export interface StrapiAppContextValue { +interface StrapiAppContextValue { menu: MenuItem[]; plugins: Record; settings: Record; @@ -124,3 +121,13 @@ const StrapiAppProvider = ({ const useStrapiApp = () => React.useContext(StrapiAppContext); export { StrapiAppContext, StrapiAppProvider, useStrapiApp }; +export type { + StrapiAppProviderProps, + StrapiAppContextValue, + MenuItem, + Plugin, + StrapiAppSettingLink, + StrapiAppSetting, + RunHookSeries, + RunHookWaterfall, +}; diff --git a/packages/core/helper-plugin/src/hooks/useRBAC.ts b/packages/core/helper-plugin/src/hooks/useRBAC.ts index e29dd1150d..e0dbf35dfb 100644 --- a/packages/core/helper-plugin/src/hooks/useRBAC.ts +++ b/packages/core/helper-plugin/src/hooks/useRBAC.ts @@ -2,15 +2,12 @@ import { useCallback, useMemo, useState } from 'react'; import { useQueries } from 'react-query'; -import { useRBACProvider } from '../features/RBAC'; +import { useRBACProvider, Permission } from '../features/RBAC'; import { useFetchClient } from './useFetchClient'; -import type { domain } from '@strapi/permissions'; import type { AxiosResponse } from 'axios'; -type Permission = domain.permission.Permission; - type AllowedActions = Record; export const useRBAC = ( @@ -71,8 +68,7 @@ export const useRBAC = ( data: { data }, } = await post< { data: { data: boolean[] } }, - AxiosResponse<{ data: { data: boolean[] } }>, - { permissions: Permission[] } + AxiosResponse<{ data: { data: boolean[] } }> >('/admin/permissions/check', { permissions: matchingPermissions.map(({ action, subject }) => ({ action, diff --git a/packages/core/helper-plugin/src/utils/auth.ts b/packages/core/helper-plugin/src/utils/auth.ts index a173ce8638..12097e0630 100644 --- a/packages/core/helper-plugin/src/utils/auth.ts +++ b/packages/core/helper-plugin/src/utils/auth.ts @@ -98,26 +98,16 @@ const auth = { sessionStorage.clear(); }, - get(key: T): StorageItems[T] | string | null { - const localStorageItem = localStorage.getItem(key); - if (localStorageItem) { + get(key: T): StorageItems[T] | null { + const item = localStorage.getItem(key) ?? sessionStorage.getItem(key); + if (item) { try { - const parsedItem = JSON.parse(localStorageItem); + const parsedItem = JSON.parse(item); return parsedItem; } catch (error) { // Failed to parse return the string value - return localStorageItem; - } - } - - const sessionStorageItem = sessionStorage.getItem(key); - if (sessionStorageItem) { - try { - const parsedItem = JSON.parse(sessionStorageItem); - return parsedItem; - } catch (error) { - // Failed to parse return the string value - return sessionStorageItem; + // @ts-expect-error - this is fine + return item; } } diff --git a/packages/core/helper-plugin/src/utils/hasPermissions.ts b/packages/core/helper-plugin/src/utils/hasPermissions.ts index ffd0f0300d..53cfb3bd71 100644 --- a/packages/core/helper-plugin/src/utils/hasPermissions.ts +++ b/packages/core/helper-plugin/src/utils/hasPermissions.ts @@ -1,10 +1,8 @@ import { getFetchClient } from './getFetchClient'; -import type { domain } from '@strapi/permissions'; +import type { Permission } from '../features/RBAC'; import type { GenericAbortSignal } from 'axios'; -type Permission = domain.permission.Permission; - const findMatchingPermissions = (userPermissions: Permission[], permissions: Permission[]) => userPermissions.reduce((acc, curr) => { const associatedPermission = permissions.find( @@ -24,7 +22,7 @@ const formatPermissionsForRequest = (permissions: Permission[]) => return {}; } - const returnedPermission: Permission = { + const returnedPermission: Partial = { action: permission.action, }; diff --git a/packages/core/helper-plugin/src/utils/tests/hasPermissions.test.ts b/packages/core/helper-plugin/src/utils/tests/hasPermissions.test.ts index 8c0a37c922..93131c6473 100644 --- a/packages/core/helper-plugin/src/utils/tests/hasPermissions.test.ts +++ b/packages/core/helper-plugin/src/utils/tests/hasPermissions.test.ts @@ -5,9 +5,7 @@ import { shouldCheckPermissions, } from '../hasPermissions'; -import type { domain } from '@strapi/permissions'; - -type Permission = domain.permission.Permission; +import type { Permission } from '../../features/RBAC'; const hasPermissionsTestData: Record> = { userPermissions: { diff --git a/packages/core/permissions/package.json b/packages/core/permissions/package.json index ba3fee2ce0..ef92ffff4c 100644 --- a/packages/core/permissions/package.json +++ b/packages/core/permissions/package.json @@ -49,7 +49,7 @@ "tsconfig": "4.14.4" }, "engines": { - "node": ">=16.0.0 <=20.x.x", + "node": ">=18.0.0 <=20.x.x", "npm": ">=6.0.0" } } diff --git a/packages/core/strapi/README.md b/packages/core/strapi/README.md index b0ec6a0168..2b9c5ed75d 100644 --- a/packages/core/strapi/README.md +++ b/packages/core/strapi/README.md @@ -94,6 +94,7 @@ Strapi only supports maintenance and LTS versions of Node.js. Please refer to th | Strapi Version | Recommended | Minimum | | --------------- | ----------- | ------- | +| 4.14.5 and up | 20.x | 18.x | | 4.11.0 and up | 18.x | 16.x | | 4.3.9 to 4.10.x | 18.x | 14.x | | 4.0.x to 4.3.8 | 16.x | 14.x | diff --git a/packages/core/strapi/package.json b/packages/core/strapi/package.json index bec3f27f68..b789861d47 100644 --- a/packages/core/strapi/package.json +++ b/packages/core/strapi/package.json @@ -171,7 +171,7 @@ "typescript": "5.2.2" }, "engines": { - "node": ">=16.0.0 <=20.x.x", + "node": ">=18.0.0 <=20.x.x", "npm": ">=6.0.0" }, "nx": { diff --git a/packages/core/strapi/src/commands/actions/plugin/build-command/action.ts b/packages/core/strapi/src/commands/actions/plugin/build-command/action.ts index f6e14632d3..99bd24cde9 100644 --- a/packages/core/strapi/src/commands/actions/plugin/build-command/action.ts +++ b/packages/core/strapi/src/commands/actions/plugin/build-command/action.ts @@ -16,7 +16,7 @@ export default async ({ force, ...opts }: ActionOptions) => { * Notify users this is an experimental command and get them to approve first * this can be opted out by setting the argument --yes */ - await notifyExperimentalCommand({ force }); + await notifyExperimentalCommand('plugin:build', { force }); const cwd = process.cwd(); diff --git a/packages/core/strapi/src/commands/actions/plugin/watch/action.ts b/packages/core/strapi/src/commands/actions/plugin/watch/action.ts new file mode 100644 index 0000000000..cf8d7e6eaf --- /dev/null +++ b/packages/core/strapi/src/commands/actions/plugin/watch/action.ts @@ -0,0 +1,103 @@ +import boxen from 'boxen'; +import chalk from 'chalk'; +import { ConfigBundle, WatchCLIOptions, watch } from '@strapi/pack-up'; +import { notifyExperimentalCommand } from '../../../utils/helpers'; +import { createLogger } from '../../../utils/logger'; +import { Export, loadPkg, validatePkg } from '../../../utils/pkg'; + +interface ActionOptions extends WatchCLIOptions { + force?: boolean; +} + +export default async ({ force, ...opts }: ActionOptions) => { + const logger = createLogger({ debug: opts.debug, silent: opts.silent, timestamp: false }); + try { + /** + * Notify users this is an experimental command and get them to approve first + * this can be opted out by setting the argument --yes + */ + await notifyExperimentalCommand('plugin:watch', { force }); + + const cwd = process.cwd(); + + const pkg = await loadPkg({ cwd, logger }); + const pkgJson = await validatePkg({ pkg }); + + if (!pkgJson.exports['./strapi-admin'] && !pkgJson.exports['./strapi-server']) { + throw new Error( + 'You need to have either a strapi-admin or strapi-server export in your package.json' + ); + } + + const bundles: ConfigBundle[] = []; + + if (pkgJson.exports['./strapi-admin']) { + const exp = pkgJson.exports['./strapi-admin'] as Export; + + const bundle: ConfigBundle = { + source: exp.source, + import: exp.import, + require: exp.require, + runtime: 'web', + }; + + if (exp.types) { + bundle.types = exp.types; + // TODO: should this be sliced from the source path...? + bundle.tsconfig = './admin/tsconfig.build.json'; + } + + bundles.push(bundle); + } + + if (pkgJson.exports['./strapi-server']) { + const exp = pkgJson.exports['./strapi-server'] as Export; + + const bundle: ConfigBundle = { + source: exp.source, + import: exp.import, + require: exp.require, + runtime: 'node', + }; + + if (exp.types) { + bundle.types = exp.types; + // TODO: should this be sliced from the source path...? + bundle.tsconfig = './server/tsconfig.build.json'; + } + + bundles.push(bundle); + } + + await watch({ + cwd, + configFile: false, + config: { + bundles, + dist: './dist', + /** + * ignore the exports map of a plugin, because we're streamlining the + * process and ensuring the server package and admin package are built + * with the correct runtime and their individual tsconfigs + */ + exports: {}, + }, + ...opts, + }); + } catch (err) { + logger.error( + 'There seems to be an unexpected error, try again with --debug for more information \n' + ); + if (err instanceof Error && err.stack) { + console.log( + chalk.red( + boxen(err.stack, { + padding: 1, + align: 'left', + }) + ) + ); + } + process.exit(1); + } +}; diff --git a/packages/core/strapi/src/commands/actions/plugin/watch/command.ts b/packages/core/strapi/src/commands/actions/plugin/watch/command.ts new file mode 100644 index 0000000000..4d0142a4f5 --- /dev/null +++ b/packages/core/strapi/src/commands/actions/plugin/watch/command.ts @@ -0,0 +1,17 @@ +import type { StrapiCommand } from '../../../types'; +import { runAction } from '../../../utils/helpers'; +import action from './action'; + +/** + * `$ strapi plugin:build` + */ +const command: StrapiCommand = ({ command }) => { + command + .command('plugin:watch') + .description('Watch & compile your strapi plugin for local development.') + .option('-d, --debug', 'Enable debugging mode with verbose logs', false) + .option('--silent', "Don't log anything", false) + .action(runAction('plugin:watch', action)); +}; + +export default command; diff --git a/packages/core/strapi/src/commands/index.ts b/packages/core/strapi/src/commands/index.ts index 94b6e2ab73..2c271e6dfc 100644 --- a/packages/core/strapi/src/commands/index.ts +++ b/packages/core/strapi/src/commands/index.ts @@ -28,6 +28,7 @@ import uninstallCommand from './actions/uninstall/command'; import versionCommand from './actions/version/command'; import watchAdminCommand from './actions/watch-admin/command'; import buildPluginCommand from './actions/plugin/build-command/command'; +import watchPluginCommand from './actions/plugin/watch/command'; const strapiCommands = { createAdminUser, @@ -58,6 +59,7 @@ const strapiCommands = { versionCommand, watchAdminCommand, buildPluginCommand, + watchPluginCommand, } as const; const buildStrapiCommand = (argv: string[], command = new Command()) => { diff --git a/packages/core/strapi/src/commands/utils/helpers.ts b/packages/core/strapi/src/commands/utils/helpers.ts index 9a7a57e690..cc993380f7 100644 --- a/packages/core/strapi/src/commands/utils/helpers.ts +++ b/packages/core/strapi/src/commands/utils/helpers.ts @@ -151,22 +151,20 @@ const runAction = * @description Notify users this is an experimental command and get them to approve first * this can be opted out by passing `yes` as a property of the args object. * - * @type {(args?: { force?: boolean }) => Promise} - * * @example * ```ts * const { notifyExperimentalCommand } = require('../utils/helpers'); * * const myCommand = async ({ force }) => { - * await notifyExperimentalCommand({ force }); + * await notifyExperimentalCommand('plugin:build', { force }); * } * ``` */ -const notifyExperimentalCommand = async ({ force }: { force?: boolean } = {}) => { +const notifyExperimentalCommand = async (name: string, { force }: { force?: boolean } = {}) => { console.log( boxen( `The ${chalk.bold( - chalk.underline('plugin:build') + chalk.underline(name) )} command is considered experimental, use at your own risk.`, { title: 'Warning', diff --git a/packages/core/types/package.json b/packages/core/types/package.json index 9f7debe5dc..9d92409e11 100644 --- a/packages/core/types/package.json +++ b/packages/core/types/package.json @@ -68,7 +68,7 @@ "typescript": "5.2.2" }, "engines": { - "node": ">=16.0.0 <=20.x.x", + "node": ">=18.0.0 <=20.x.x", "npm": ">=6.0.0" } } diff --git a/packages/core/upload/package.json b/packages/core/upload/package.json index 6c0700d2fd..24fe3a8354 100644 --- a/packages/core/upload/package.json +++ b/packages/core/upload/package.json @@ -69,7 +69,7 @@ "styled-components": "5.3.3" }, "engines": { - "node": ">=16.0.0 <=20.x.x", + "node": ">=18.0.0 <=20.x.x", "npm": ">=6.0.0" }, "strapi": { diff --git a/packages/core/utils/package.json b/packages/core/utils/package.json index 4caf2c109b..1f77fb2893 100644 --- a/packages/core/utils/package.json +++ b/packages/core/utils/package.json @@ -62,7 +62,7 @@ "tsconfig": "4.14.4" }, "engines": { - "node": ">=16.0.0 <=20.x.x", + "node": ">=18.0.0 <=20.x.x", "npm": ">=6.0.0" } } diff --git a/packages/generators/app/package.json b/packages/generators/app/package.json index 8ca8ebbeb8..f7b128a1cf 100644 --- a/packages/generators/app/package.json +++ b/packages/generators/app/package.json @@ -62,7 +62,7 @@ "copyfiles": "2.4.1" }, "engines": { - "node": ">=16.0.0 <=20.x.x", + "node": ">=18.0.0 <=20.x.x", "npm": ">=6.0.0" } } diff --git a/packages/generators/app/src/resources/json/common/engines.ts b/packages/generators/app/src/resources/json/common/engines.ts index b5a43d6eff..e325de00d2 100644 --- a/packages/generators/app/src/resources/json/common/engines.ts +++ b/packages/generators/app/src/resources/json/common/engines.ts @@ -1,4 +1,4 @@ export default { - node: '>=16.0.0 <=20.x.x', + node: '>=18.0.0 <=20.x.x', npm: '>=6.0.0', }; diff --git a/packages/generators/generators/package.json b/packages/generators/generators/package.json index cd58e62193..e15d1c3018 100644 --- a/packages/generators/generators/package.json +++ b/packages/generators/generators/package.json @@ -62,7 +62,7 @@ "tsconfig": "4.14.4" }, "engines": { - "node": ">=16.0.0 <=20.x.x", + "node": ">=18.0.0 <=20.x.x", "npm": ">=6.0.0" } } diff --git a/packages/generators/generators/src/templates/js/plugin-package.json.hbs b/packages/generators/generators/src/templates/js/plugin-package.json.hbs index 55591cde85..9815e69e51 100644 --- a/packages/generators/generators/src/templates/js/plugin-package.json.hbs +++ b/packages/generators/generators/src/templates/js/plugin-package.json.hbs @@ -35,7 +35,7 @@ } ], "engines": { - "node": ">=16.0.0 <=20.x.x", + "node": ">=18.0.0 <=20.x.x", "npm": ">=6.0.0" }, "license": "MIT" diff --git a/packages/generators/generators/src/templates/ts/plugin-package.json.hbs b/packages/generators/generators/src/templates/ts/plugin-package.json.hbs index 6a000a8471..4bf831f38f 100644 --- a/packages/generators/generators/src/templates/ts/plugin-package.json.hbs +++ b/packages/generators/generators/src/templates/ts/plugin-package.json.hbs @@ -40,7 +40,7 @@ } ], "engines": { - "node": ">=16.0.0 <=20.x.x", + "node": ">=18.0.0 <=20.x.x", "npm": ">=6.0.0" }, "scripts": { diff --git a/packages/plugins/color-picker/package.json b/packages/plugins/color-picker/package.json index 99dc87acb1..8d5ee0c1ba 100644 --- a/packages/plugins/color-picker/package.json +++ b/packages/plugins/color-picker/package.json @@ -42,7 +42,7 @@ "strapi-server.js" ], "scripts": { - "build": "NODE_ENV=production strapi plugin:build --force", + "build": "strapi plugin:build --force", "clean": "run -T rimraf ./dist", "lint": "yarn lint:project && yarn lint:back && yarn lint:front", "lint:back": "run -T eslint ./server -c ./server/.eslintrc.js", @@ -54,7 +54,7 @@ "test:front:watch": "run -T cross-env IS_EE=true jest --config ./jest.config.front.js --watchAll", "test:front:watch:ce": "run -T cross-env IS_EE=false jest --config ./jest.config.front.js --watchAll", "test:ts:front": "run -T tsc -p admin/tsconfig.json", - "watch": "run -T tsc -w --preserveWatchOutput" + "watch": "strapi plugin:watch" }, "dependencies": { "@strapi/design-system": "1.12.2", @@ -83,7 +83,7 @@ "styled-components": "5.3.3" }, "engines": { - "node": ">=16.0.0 <=20.x.x", + "node": ">=18.0.0 <=20.x.x", "npm": ">=6.0.0" }, "strapi": { diff --git a/packages/plugins/documentation/package.json b/packages/plugins/documentation/package.json index 28667fd561..6779a17838 100644 --- a/packages/plugins/documentation/package.json +++ b/packages/plugins/documentation/package.json @@ -65,7 +65,7 @@ "styled-components": "5.3.3" }, "engines": { - "node": ">=16.0.0 <=20.x.x", + "node": ">=18.0.0 <=20.x.x", "npm": ">=6.0.0" }, "strapi": { diff --git a/packages/plugins/graphql/package.json b/packages/plugins/graphql/package.json index b0cc82ea84..c5eab63154 100644 --- a/packages/plugins/graphql/package.json +++ b/packages/plugins/graphql/package.json @@ -59,7 +59,7 @@ "styled-components": "5.3.3" }, "engines": { - "node": ">=16.0.0 <=20.x.x", + "node": ">=18.0.0 <=20.x.x", "npm": ">=6.0.0" }, "strapi": { diff --git a/packages/plugins/graphql/server/services/constants.js b/packages/plugins/graphql/server/services/constants.js index ed4ffa763b..ce870fd77c 100644 --- a/packages/plugins/graphql/server/services/constants.js +++ b/packages/plugins/graphql/server/services/constants.js @@ -24,6 +24,7 @@ const STRAPI_SCALARS = [ 'integer', 'string', 'richtext', + 'blocks', 'enumeration', 'biginteger', 'float', @@ -49,6 +50,7 @@ const SCALARS_ASSOCIATIONS = { string: 'String', enumeration: 'String', richtext: 'String', + blocks: 'JSON', biginteger: 'Long', float: 'Float', decimal: 'Float', diff --git a/packages/plugins/i18n/package.json b/packages/plugins/i18n/package.json index 039566fe23..e72d0fa2d6 100644 --- a/packages/plugins/i18n/package.json +++ b/packages/plugins/i18n/package.json @@ -60,7 +60,7 @@ "styled-components": "5.3.3" }, "engines": { - "node": ">=16.0.0 <=20.x.x", + "node": ">=18.0.0 <=20.x.x", "npm": ">=6.0.0" }, "strapi": { diff --git a/packages/plugins/sentry/package.json b/packages/plugins/sentry/package.json index 6221ba29d4..20f51c7a42 100644 --- a/packages/plugins/sentry/package.json +++ b/packages/plugins/sentry/package.json @@ -45,7 +45,7 @@ "styled-components": "5.3.3" }, "engines": { - "node": ">=16.0.0 <=20.x.x", + "node": ">=18.0.0 <=20.x.x", "npm": ">=6.0.0" }, "strapi": { diff --git a/packages/plugins/users-permissions/package.json b/packages/plugins/users-permissions/package.json index 1d905f6dd7..8429f4e658 100644 --- a/packages/plugins/users-permissions/package.json +++ b/packages/plugins/users-permissions/package.json @@ -67,7 +67,7 @@ "styled-components": "5.3.3" }, "engines": { - "node": ">=16.0.0 <=20.x.x", + "node": ">=18.0.0 <=20.x.x", "npm": ">=6.0.0" }, "strapi": { diff --git a/packages/providers/audit-logs-local/package.json b/packages/providers/audit-logs-local/package.json index c668624786..5efaf9e003 100644 --- a/packages/providers/audit-logs-local/package.json +++ b/packages/providers/audit-logs-local/package.json @@ -48,7 +48,7 @@ "tsconfig": "4.14.4" }, "engines": { - "node": ">=16.0.0 <=20.x.x", + "node": ">=18.0.0 <=20.x.x", "npm": ">=6.0.0" } } diff --git a/packages/providers/email-amazon-ses/package.json b/packages/providers/email-amazon-ses/package.json index 7965135c8c..678a17ef4a 100644 --- a/packages/providers/email-amazon-ses/package.json +++ b/packages/providers/email-amazon-ses/package.json @@ -52,7 +52,7 @@ "tsconfig": "4.14.4" }, "engines": { - "node": ">=16.0.0 <=20.x.x", + "node": ">=18.0.0 <=20.x.x", "npm": ">=6.0.0" } } diff --git a/packages/providers/email-mailgun/package.json b/packages/providers/email-mailgun/package.json index cdc372d7d5..9fe2e45868 100644 --- a/packages/providers/email-mailgun/package.json +++ b/packages/providers/email-mailgun/package.json @@ -55,7 +55,7 @@ "tsconfig": "4.14.4" }, "engines": { - "node": ">=16.0.0 <=20.x.x", + "node": ">=18.0.0 <=20.x.x", "npm": ">=6.0.0" } } diff --git a/packages/providers/email-nodemailer/package.json b/packages/providers/email-nodemailer/package.json index 1e681286ec..98558d74f1 100644 --- a/packages/providers/email-nodemailer/package.json +++ b/packages/providers/email-nodemailer/package.json @@ -66,7 +66,7 @@ "tsconfig": "4.14.4" }, "engines": { - "node": ">=16.0.0 <=20.x.x", + "node": ">=18.0.0 <=20.x.x", "npm": ">=6.0.0" } } diff --git a/packages/providers/email-sendgrid/package.json b/packages/providers/email-sendgrid/package.json index ebc4fd9497..91c46a430b 100644 --- a/packages/providers/email-sendgrid/package.json +++ b/packages/providers/email-sendgrid/package.json @@ -52,7 +52,7 @@ "tsconfig": "4.14.4" }, "engines": { - "node": ">=16.0.0 <=20.x.x", + "node": ">=18.0.0 <=20.x.x", "npm": ">=6.0.0" } } diff --git a/packages/providers/email-sendmail/package.json b/packages/providers/email-sendmail/package.json index 81acb5d912..0c0e6706a2 100644 --- a/packages/providers/email-sendmail/package.json +++ b/packages/providers/email-sendmail/package.json @@ -52,7 +52,7 @@ "tsconfig": "4.14.4" }, "engines": { - "node": ">=16.0.0 <=20.x.x", + "node": ">=18.0.0 <=20.x.x", "npm": ">=6.0.0" } } diff --git a/packages/providers/upload-aws-s3/package.json b/packages/providers/upload-aws-s3/package.json index 1b9707669e..7632e7be25 100644 --- a/packages/providers/upload-aws-s3/package.json +++ b/packages/providers/upload-aws-s3/package.json @@ -56,7 +56,7 @@ "tsconfig": "4.14.4" }, "engines": { - "node": ">=16.0.0 <=20.x.x", + "node": ">=18.0.0 <=20.x.x", "npm": ">=6.0.0" } } diff --git a/packages/providers/upload-cloudinary/package.json b/packages/providers/upload-cloudinary/package.json index 58eefcf01f..6761a8d9a0 100644 --- a/packages/providers/upload-cloudinary/package.json +++ b/packages/providers/upload-cloudinary/package.json @@ -53,7 +53,7 @@ "tsconfig": "4.14.4" }, "engines": { - "node": ">=16.0.0 <=20.x.x", + "node": ">=18.0.0 <=20.x.x", "npm": ">=6.0.0" } } diff --git a/packages/providers/upload-local/package.json b/packages/providers/upload-local/package.json index 35612a5851..9e83688bf9 100644 --- a/packages/providers/upload-local/package.json +++ b/packages/providers/upload-local/package.json @@ -54,7 +54,7 @@ "tsconfig": "4.14.4" }, "engines": { - "node": ">=16.0.0 <=20.x.x", + "node": ">=18.0.0 <=20.x.x", "npm": ">=6.0.0" } } diff --git a/packages/utils/logger/package.json b/packages/utils/logger/package.json index ec2b1d1445..9527e9b5ac 100644 --- a/packages/utils/logger/package.json +++ b/packages/utils/logger/package.json @@ -48,7 +48,7 @@ "tsconfig": "4.14.4" }, "engines": { - "node": ">=16.0.0 <=20.x.x", + "node": ">=18.0.0 <=20.x.x", "npm": ">=6.0.0" } } diff --git a/packages/utils/pack-up/package.json b/packages/utils/pack-up/package.json index 120a4a46ef..cb6c4b8809 100644 --- a/packages/utils/pack-up/package.json +++ b/packages/utils/pack-up/package.json @@ -92,7 +92,7 @@ "rimraf": "3.0.2" }, "engines": { - "node": ">=16.0.0 <=20.x.x", + "node": ">=18.0.0 <=20.x.x", "npm": ">=6.0.0" } } diff --git a/packages/utils/pack-up/src/node/createBuildContext.ts b/packages/utils/pack-up/src/node/createBuildContext.ts index 04eb2ebbca..addfd26c83 100644 --- a/packages/utils/pack-up/src/node/createBuildContext.ts +++ b/packages/utils/pack-up/src/node/createBuildContext.ts @@ -48,7 +48,7 @@ const DEFAULT_BROWSERS_LIST_CONFIG = [ 'Firefox ESR', 'last 2 Opera versions', 'not dead', - 'node 16.0.0', + 'node 18.0.0', ]; /** @@ -71,7 +71,7 @@ const createBuildContext = async ({ const targets = { '*': browserslistToEsbuild(pkg.browserslist ?? DEFAULT_BROWSERS_LIST_CONFIG), - node: browserslistToEsbuild(['node 16.0.0']), + node: browserslistToEsbuild(['node 18.0.0']), web: ['esnext'], }; diff --git a/packages/utils/pack-up/src/node/createTasks.ts b/packages/utils/pack-up/src/node/createTasks.ts index 90911ddd83..6c63e4ad98 100644 --- a/packages/utils/pack-up/src/node/createTasks.ts +++ b/packages/utils/pack-up/src/node/createTasks.ts @@ -1,5 +1,6 @@ import path from 'path'; +import { DtsWatchTask } from './tasks/dts/watch'; import { ViteBaseTask, ViteTaskEntry } from './tasks/vite/types'; import { ViteWatchTask } from './tasks/vite/watch'; @@ -10,7 +11,7 @@ import type { DtsBaseTask } from './tasks/dts/types'; import type { ViteBuildTask } from './tasks/vite/build'; type BuildTask = DtsBuildTask | ViteBuildTask; -type WatchTask = ViteWatchTask; +type WatchTask = ViteWatchTask | DtsWatchTask; type BaseTask = ViteBaseTask | DtsBaseTask; diff --git a/packages/utils/pack-up/src/node/tasks/dts/watch.ts b/packages/utils/pack-up/src/node/tasks/dts/watch.ts index 5d04c3bff1..a745e999d3 100644 --- a/packages/utils/pack-up/src/node/tasks/dts/watch.ts +++ b/packages/utils/pack-up/src/node/tasks/dts/watch.ts @@ -57,7 +57,7 @@ const dtsWatchTask: TaskHandler = { } const compilerHost = ts.createWatchCompilerHost( - 'tsconfig.build.json', + tsconfig.path, tsconfig.config.options, ts.sys, ts.createEmitAndSemanticDiagnosticsBuilderProgram, diff --git a/packages/utils/tsconfig/client.json b/packages/utils/tsconfig/client.json index cea9f75090..9a79528013 100644 --- a/packages/utils/tsconfig/client.json +++ b/packages/utils/tsconfig/client.json @@ -14,6 +14,7 @@ "forceConsistentCasingInFileNames": true, "resolveJsonModule": true, "noEmit": true, + "noImplicitAny": true, "jsx": "react-jsx" } } diff --git a/packages/utils/typescript/package.json b/packages/utils/typescript/package.json index 16279bb0dc..ad39e146df 100644 --- a/packages/utils/typescript/package.json +++ b/packages/utils/typescript/package.json @@ -43,7 +43,7 @@ "typescript": "5.2.2" }, "engines": { - "node": ">=16.0.0 <=20.x.x", + "node": ">=18.0.0 <=20.x.x", "npm": ">=6.0.0" } }