mirror of
https://github.com/strapi/strapi.git
synced 2025-11-01 18:33:55 +00:00
Merge branch 'features/deits' into deits/provider-results
This commit is contained in:
commit
7cf2ae8d05
15
.eslintrc.back.typescript.js
Normal file
15
.eslintrc.back.typescript.js
Normal file
@ -0,0 +1,15 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: ['@typescript-eslint'],
|
||||
/**
|
||||
* TODO: this should extend @strapi/eslint-config/typescript but doing so requires configuring parserOption.project, which requires tsconfig.json configuration
|
||||
*/
|
||||
// extends: ['plugin:@typescript-eslint/recommended'],
|
||||
globals: {
|
||||
strapi: false,
|
||||
},
|
||||
// Instead of extending (which includes values that interfere with this configuration), only take the rules field
|
||||
rules: require('./.eslintrc.back.js').rules,
|
||||
};
|
||||
10
.eslintrc.js
10
.eslintrc.js
@ -13,10 +13,20 @@ module.exports = {
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
// Backend javascript
|
||||
files: ['packages/**/*.js', 'test/**/*.js', 'scripts/**/*.js'],
|
||||
excludedFiles: frontPaths,
|
||||
...require('./.eslintrc.back.js'),
|
||||
},
|
||||
|
||||
// Backend typescript
|
||||
{
|
||||
files: ['packages/**/*.ts', 'test/**/*.ts', 'scripts/**/*.ts'],
|
||||
excludedFiles: frontPaths,
|
||||
...require('./.eslintrc.back.typescript.js'),
|
||||
},
|
||||
|
||||
// Frontend
|
||||
{
|
||||
files: frontPaths,
|
||||
...require('./.eslintrc.front.js'),
|
||||
|
||||
17
package.json
17
package.json
@ -41,7 +41,7 @@
|
||||
"format": "npm-run-all -p format:*",
|
||||
"format:code": "npm run prettier:code -- --write",
|
||||
"format:other": "npm run prettier:other -- --write",
|
||||
"prettier:code": "prettier \"**/*.js\"",
|
||||
"prettier:code": "prettier \"**/*.{js,ts}\"",
|
||||
"prettier:other": "prettier \"**/*.{md,css,scss,yaml,yml}\"",
|
||||
"test:clean": "rimraf ./coverage",
|
||||
"test:front": "npm run test:clean && cross-env IS_EE=true jest --config ./jest.config.front.js",
|
||||
@ -56,10 +56,10 @@
|
||||
"doc:api": "node scripts/open-api/serve.js"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,md,css,scss,yaml,yml}": [
|
||||
"*.{js,ts,md,css,scss,yaml,yml}": [
|
||||
"prettier --write"
|
||||
],
|
||||
"*.js": [
|
||||
"*.{js,ts}": [
|
||||
"eslint --fix"
|
||||
]
|
||||
},
|
||||
@ -70,12 +70,22 @@
|
||||
"@strapi/eslint-config": "0.1.2",
|
||||
"@swc/core": "1.2.224",
|
||||
"@swc/jest": "0.2.22",
|
||||
"@typescript-eslint/eslint-plugin": "5.43.0",
|
||||
"@typescript-eslint/parser": "5.42.1",
|
||||
"babel-eslint": "10.1.0",
|
||||
"chalk": "4.1.2",
|
||||
"chokidar": "3.5.3",
|
||||
"cross-env": "7.0.3",
|
||||
"dotenv": "14.2.0",
|
||||
"eslint": "8.21.0",
|
||||
"eslint-config-prettier": "8.5.0",
|
||||
"eslint-plugin": "1.0.1",
|
||||
"eslint-plugin-import": "2.26.0",
|
||||
"eslint-plugin-jsx-a11y": "6.6.1",
|
||||
"eslint-plugin-node": "11.1.0",
|
||||
"eslint-plugin-prettier": "4.2.1",
|
||||
"eslint-plugin-react": "7.31.10",
|
||||
"eslint-plugin-react-hooks": "4.6.0",
|
||||
"execa": "1.0.0",
|
||||
"fs-extra": "10.1.0",
|
||||
"get-port": "5.1.1",
|
||||
@ -105,6 +115,7 @@
|
||||
"stylelint-processor-styled-components": "1.10.0",
|
||||
"supertest": "6.2.4",
|
||||
"ts-jest": "29.0.3",
|
||||
"typescript": "4.6.2",
|
||||
"yargs": "17.6.0"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@ -9,6 +9,7 @@ const FilterSelect = ({ message, value, onChange, possibleFilters, onClear, cust
|
||||
|
||||
return (
|
||||
<Select
|
||||
data-testid={`${message}-button`}
|
||||
aria-label={message}
|
||||
placeholder={message}
|
||||
size="M"
|
||||
@ -20,7 +21,7 @@ const FilterSelect = ({ message, value, onChange, possibleFilters, onClear, cust
|
||||
>
|
||||
{Object.entries(possibleFilters).map(([filterName, count]) => {
|
||||
return (
|
||||
<Option key={filterName} value={filterName}>
|
||||
<Option data-testid={`${filterName}-${count}`} key={filterName} value={filterName}>
|
||||
{computeFilterMessage(filterName, count)}
|
||||
</Option>
|
||||
);
|
||||
|
||||
@ -51,6 +51,7 @@ const NpmPackagesFilters = ({
|
||||
<ButtonToggle
|
||||
variant="tertiary"
|
||||
ref={buttonRef}
|
||||
data-testid="filters-button"
|
||||
startIcon={<Filter />}
|
||||
onClick={handleToggle}
|
||||
size="S"
|
||||
|
||||
@ -1378,6 +1378,7 @@ exports[`Marketplace page - layout renders the online layout 1`] = `
|
||||
<button
|
||||
aria-disabled="false"
|
||||
class="c52 c53 c54"
|
||||
data-testid="filters-button"
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
|
||||
@ -1378,6 +1378,7 @@ exports[`Marketplace page - plugins tab renders and matches the plugin tab snaps
|
||||
<button
|
||||
aria-disabled="false"
|
||||
class="c52 c53 c54"
|
||||
data-testid="filters-button"
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
|
||||
@ -1373,6 +1373,7 @@ exports[`Marketplace page - providers tab renders and matches the providers tab
|
||||
<button
|
||||
aria-disabled="false"
|
||||
class="c52 c53 c54"
|
||||
data-testid="filters-button"
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
|
||||
@ -66,7 +66,6 @@ const App = (
|
||||
|
||||
const waitForReload = async () => {
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByTestId('loader')).toBe(null);
|
||||
expect(screen.getByRole('heading', { name: /marketplace/i })).toBeInTheDocument();
|
||||
});
|
||||
};
|
||||
|
||||
@ -54,7 +54,6 @@ const waitForReload = async () => {
|
||||
await waitFor(
|
||||
() => {
|
||||
expect(screen.queryByTestId('loader')).toBe(null);
|
||||
expect(screen.getByRole('heading', { name: /marketplace/i })).toBeInTheDocument();
|
||||
},
|
||||
{ timeout: 5000 }
|
||||
);
|
||||
@ -151,21 +150,21 @@ describe('Marketplace page - plugins tab', () => {
|
||||
});
|
||||
|
||||
it('shows plugins filters popover', () => {
|
||||
const filtersButton = screen.getByRole('button', { name: /filters/i });
|
||||
const filtersButton = screen.getByTestId('filters-button');
|
||||
|
||||
userEvent.click(filtersButton);
|
||||
const collectionsButton = screen.getByRole('button', { name: 'No collections selected' });
|
||||
const categoriesButton = screen.getByRole('button', { name: 'No categories selected' });
|
||||
const collectionsButton = screen.getByTestId('Collections-button');
|
||||
const categoriesButton = screen.getByTestId('Categories-button');
|
||||
|
||||
expect(collectionsButton).toBeVisible();
|
||||
expect(categoriesButton).toBeVisible();
|
||||
});
|
||||
|
||||
it('shows the collections filter options', () => {
|
||||
const filtersButton = screen.getByRole('button', { name: /filters/i });
|
||||
const filtersButton = screen.getByTestId('filters-button');
|
||||
userEvent.click(filtersButton);
|
||||
|
||||
const collectionsButton = screen.getByRole('button', { name: 'No collections selected' });
|
||||
const collectionsButton = screen.getByTestId('Collections-button');
|
||||
|
||||
userEvent.click(collectionsButton);
|
||||
|
||||
@ -177,16 +176,16 @@ describe('Marketplace page - plugins tab', () => {
|
||||
};
|
||||
|
||||
Object.entries(mockedServerCollections).forEach(([collectionName, count]) => {
|
||||
const option = screen.getByRole('option', { name: `${collectionName} (${count})` });
|
||||
const option = screen.getByTestId(`${collectionName}-${count}`);
|
||||
expect(option).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
it('shows the categories filter options', () => {
|
||||
const filtersButton = screen.getByRole('button', { name: /filters/i });
|
||||
const filtersButton = screen.getByTestId('filters-button');
|
||||
userEvent.click(filtersButton);
|
||||
|
||||
const categoriesButton = screen.getByRole('button', { name: 'No categories selected' });
|
||||
const categoriesButton = screen.getByTestId('Categories-button');
|
||||
|
||||
userEvent.click(categoriesButton);
|
||||
|
||||
@ -197,16 +196,16 @@ describe('Marketplace page - plugins tab', () => {
|
||||
};
|
||||
|
||||
Object.entries(mockedServerCategories).forEach(([categoryName, count]) => {
|
||||
const option = screen.getByRole('option', { name: `${categoryName} (${count})` });
|
||||
const option = screen.getByTestId(`${categoryName}-${count}`);
|
||||
expect(option).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
it('filters a collection option', async () => {
|
||||
const filtersButton = screen.getByRole('button', { name: /filters/i });
|
||||
const filtersButton = screen.getByTestId('filters-button');
|
||||
userEvent.click(filtersButton);
|
||||
|
||||
const collectionsButton = screen.getByRole('button', { name: 'No collections selected' });
|
||||
const collectionsButton = screen.getByTestId('Collections-button');
|
||||
userEvent.click(collectionsButton);
|
||||
|
||||
const option = screen.getByRole('option', { name: `Made by Strapi (13)` });
|
||||
@ -227,10 +226,10 @@ describe('Marketplace page - plugins tab', () => {
|
||||
});
|
||||
|
||||
it('filters a category option', async () => {
|
||||
const filtersButton = screen.getByRole('button', { name: /filters/i });
|
||||
const filtersButton = screen.getByTestId('filters-button');
|
||||
userEvent.click(filtersButton);
|
||||
|
||||
const categoriesButton = screen.getByRole('button', { name: 'No categories selected' });
|
||||
const categoriesButton = screen.getByTestId('Categories-button');
|
||||
userEvent.click(categoriesButton);
|
||||
|
||||
const option = screen.getByRole('option', { name: `Custom fields (4)` });
|
||||
@ -252,9 +251,9 @@ describe('Marketplace page - plugins tab', () => {
|
||||
|
||||
it('filters a category and a collection option', async () => {
|
||||
// When a user clicks the filters button
|
||||
userEvent.click(screen.getByRole('button', { name: /filters/i }));
|
||||
userEvent.click(screen.getByTestId('filters-button'));
|
||||
// They should see a select button for collections with no options selected
|
||||
const collectionsButton = screen.getByRole('button', { name: 'No collections selected' });
|
||||
const collectionsButton = screen.getByTestId('Collections-button');
|
||||
// When they click the select button
|
||||
userEvent.click(collectionsButton);
|
||||
// They should see a Made by Strapi option
|
||||
@ -264,11 +263,11 @@ describe('Marketplace page - plugins tab', () => {
|
||||
// The page should reload
|
||||
await waitForReload();
|
||||
// When they click the filters button again
|
||||
userEvent.click(screen.getByRole('button', { name: 'Filters' }));
|
||||
userEvent.click(screen.getByTestId('filters-button'));
|
||||
// They should see the collections button indicating 1 option selected
|
||||
userEvent.click(screen.getByRole('button', { name: '1 collection selected Made by Strapi' }));
|
||||
// They should the categories button with no options selected
|
||||
const categoriesButton = screen.getByRole('button', { name: 'No categories selected' });
|
||||
const categoriesButton = screen.getByTestId('Categories-button');
|
||||
userEvent.click(categoriesButton);
|
||||
const categoryOption = screen.getByRole('option', { name: `Custom fields (4)` });
|
||||
userEvent.click(categoryOption);
|
||||
@ -295,13 +294,13 @@ describe('Marketplace page - plugins tab', () => {
|
||||
});
|
||||
|
||||
it('filters multiple collection options', async () => {
|
||||
userEvent.click(screen.getByRole('button', { name: /filters/i }));
|
||||
userEvent.click(screen.getByRole('button', { name: 'No collections selected' }));
|
||||
userEvent.click(screen.getByTestId('filters-button'));
|
||||
userEvent.click(screen.getByTestId('Collections-button'));
|
||||
userEvent.click(screen.getByRole('option', { name: `Made by Strapi (13)` }));
|
||||
|
||||
await waitForReload();
|
||||
|
||||
userEvent.click(screen.getByRole('button', { name: /filters/i }));
|
||||
userEvent.click(screen.getByTestId('filters-button'));
|
||||
userEvent.click(screen.getByRole('button', { name: `1 collection selected Made by Strapi` }));
|
||||
userEvent.click(screen.getByRole('option', { name: `Verified (29)` }));
|
||||
|
||||
@ -318,13 +317,13 @@ describe('Marketplace page - plugins tab', () => {
|
||||
});
|
||||
|
||||
it('filters multiple category options', async () => {
|
||||
userEvent.click(screen.getByRole('button', { name: /filters/i }));
|
||||
userEvent.click(screen.getByRole('button', { name: 'No categories selected' }));
|
||||
userEvent.click(screen.getByTestId('filters-button'));
|
||||
userEvent.click(screen.getByTestId('Categories-button'));
|
||||
userEvent.click(screen.getByRole('option', { name: `Custom fields (4)` }));
|
||||
|
||||
await waitForReload();
|
||||
|
||||
userEvent.click(screen.getByRole('button', { name: /filters/i }));
|
||||
userEvent.click(screen.getByTestId('filters-button'));
|
||||
userEvent.click(screen.getByRole('button', { name: `1 category selected Custom fields` }));
|
||||
userEvent.click(screen.getByRole('option', { name: `Monitoring (1)` }));
|
||||
|
||||
@ -341,10 +340,10 @@ describe('Marketplace page - plugins tab', () => {
|
||||
});
|
||||
|
||||
it('removes a filter option tag', async () => {
|
||||
const filtersButton = screen.getByRole('button', { name: /filters/i });
|
||||
const filtersButton = screen.getByTestId('filters-button');
|
||||
userEvent.click(filtersButton);
|
||||
|
||||
const collectionsButton = screen.getByRole('button', { name: 'No collections selected' });
|
||||
const collectionsButton = screen.getByTestId('Collections-button');
|
||||
userEvent.click(collectionsButton);
|
||||
|
||||
const option = screen.getByRole('option', { name: `Made by Strapi (13)` });
|
||||
@ -362,10 +361,10 @@ describe('Marketplace page - plugins tab', () => {
|
||||
});
|
||||
|
||||
it('only filters in the plugins tab', async () => {
|
||||
const filtersButton = screen.getByRole('button', { name: /filters/i });
|
||||
const filtersButton = screen.getByTestId('filters-button');
|
||||
userEvent.click(filtersButton);
|
||||
|
||||
const collectionsButton = screen.getByRole('button', { name: 'No collections selected' });
|
||||
const collectionsButton = screen.getByTestId('Collections-button');
|
||||
userEvent.click(collectionsButton);
|
||||
|
||||
const option = screen.getByRole('option', { name: `Made by Strapi (13)` });
|
||||
|
||||
@ -53,7 +53,6 @@ const waitForReload = async () => {
|
||||
await waitFor(
|
||||
() => {
|
||||
expect(screen.queryByTestId('loader')).toBe(null);
|
||||
expect(screen.getByRole('heading', { name: /marketplace/i })).toBeInTheDocument();
|
||||
},
|
||||
{ timeout: 5000 }
|
||||
);
|
||||
@ -158,7 +157,7 @@ describe('Marketplace page - providers tab', () => {
|
||||
});
|
||||
|
||||
it('shows providers filters popover', () => {
|
||||
const filtersButton = screen.getByRole('button', { name: /filters/i });
|
||||
const filtersButton = screen.getByTestId('filters-button');
|
||||
|
||||
// Only show collections filters on providers
|
||||
const providersTab = screen.getByRole('tab', { name: /providers/i });
|
||||
@ -168,10 +167,10 @@ describe('Marketplace page - providers tab', () => {
|
||||
});
|
||||
|
||||
it('shows the collections filter options', () => {
|
||||
const filtersButton = screen.getByRole('button', { name: /filters/i });
|
||||
const filtersButton = screen.getByTestId('filters-button');
|
||||
userEvent.click(filtersButton);
|
||||
|
||||
const collectionsButton = screen.getByRole('button', { name: 'No collections selected' });
|
||||
const collectionsButton = screen.getByTestId('Collections-button');
|
||||
|
||||
userEvent.click(collectionsButton);
|
||||
|
||||
@ -183,16 +182,16 @@ describe('Marketplace page - providers tab', () => {
|
||||
};
|
||||
|
||||
Object.entries(mockedServerCollections).forEach(([collectionName, count]) => {
|
||||
const option = screen.getByRole('option', { name: `${collectionName} (${count})` });
|
||||
const option = screen.getByTestId(`${collectionName}-${count}`);
|
||||
expect(option).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
it('filters a collection option', async () => {
|
||||
const filtersButton = screen.getByRole('button', { name: /filters/i });
|
||||
const filtersButton = screen.getByTestId('filters-button');
|
||||
userEvent.click(filtersButton);
|
||||
|
||||
const collectionsButton = screen.getByRole('button', { name: 'No collections selected' });
|
||||
const collectionsButton = screen.getByTestId('Collections-button');
|
||||
userEvent.click(collectionsButton);
|
||||
|
||||
const option = screen.getByRole('option', { name: `Made by Strapi (6)` });
|
||||
@ -213,13 +212,13 @@ describe('Marketplace page - providers tab', () => {
|
||||
});
|
||||
|
||||
it('filters multiple collection options', async () => {
|
||||
userEvent.click(screen.getByRole('button', { name: /filters/i }));
|
||||
userEvent.click(screen.getByRole('button', { name: 'No collections selected' }));
|
||||
userEvent.click(screen.getByTestId('filters-button'));
|
||||
userEvent.click(screen.getByTestId('Collections-button'));
|
||||
userEvent.click(screen.getByRole('option', { name: `Made by Strapi (6)` }));
|
||||
|
||||
await waitForReload();
|
||||
|
||||
userEvent.click(screen.getByRole('button', { name: /filters/i }));
|
||||
userEvent.click(screen.getByTestId('filters-button'));
|
||||
userEvent.click(screen.getByRole('button', { name: `1 collection selected Made by Strapi` }));
|
||||
userEvent.click(screen.getByRole('option', { name: `Verified (6)` }));
|
||||
|
||||
@ -236,10 +235,10 @@ describe('Marketplace page - providers tab', () => {
|
||||
});
|
||||
|
||||
it('removes a filter option tag', async () => {
|
||||
const filtersButton = screen.getByRole('button', { name: /filters/i });
|
||||
const filtersButton = screen.getByTestId('filters-button');
|
||||
userEvent.click(filtersButton);
|
||||
|
||||
const collectionsButton = screen.getByRole('button', { name: 'No collections selected' });
|
||||
const collectionsButton = screen.getByTestId('Collections-button');
|
||||
userEvent.click(collectionsButton);
|
||||
|
||||
const option = screen.getByRole('option', { name: `Made by Strapi (6)` });
|
||||
@ -257,10 +256,10 @@ describe('Marketplace page - providers tab', () => {
|
||||
});
|
||||
|
||||
it('only filters in the providers tab', async () => {
|
||||
const filtersButton = screen.getByRole('button', { name: /filters/i });
|
||||
const filtersButton = screen.getByTestId('filters-button');
|
||||
userEvent.click(filtersButton);
|
||||
|
||||
const collectionsButton = screen.getByRole('button', { name: 'No collections selected' });
|
||||
const collectionsButton = screen.getByTestId('Collections-button');
|
||||
userEvent.click(collectionsButton);
|
||||
|
||||
const option = screen.getByRole('option', { name: `Made by Strapi (6)` });
|
||||
|
||||
@ -1,75 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
// Helpers.
|
||||
const { createTestBuilder } = require('../../../../../../test/helpers/builder');
|
||||
const { createStrapiInstance } = require('../../../../../../test/helpers/strapi');
|
||||
const form = require('../../../../../../test/helpers/generators');
|
||||
const { createAuthRequest } = require('../../../../../../test/helpers/request');
|
||||
|
||||
const builder = createTestBuilder();
|
||||
let strapi;
|
||||
let rq;
|
||||
|
||||
const restart = async () => {
|
||||
await strapi.destroy();
|
||||
strapi = await createStrapiInstance();
|
||||
rq = await createAuthRequest({ strapi });
|
||||
};
|
||||
|
||||
describe('Content Manager - Hide relations', () => {
|
||||
beforeAll(async () => {
|
||||
await builder.addContentTypes([form.article]).build();
|
||||
|
||||
strapi = await createStrapiInstance();
|
||||
rq = await createAuthRequest({ strapi });
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await strapi.destroy();
|
||||
await builder.cleanup();
|
||||
});
|
||||
|
||||
test('Hide relations', async () => {
|
||||
await rq({
|
||||
url: '/content-manager/content-types/api::article.article/configuration',
|
||||
method: 'PUT',
|
||||
body: {
|
||||
layouts: {
|
||||
edit: [],
|
||||
editRelations: [],
|
||||
list: [],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const { body } = await rq({
|
||||
url: '/content-manager/content-types/api::article.article/configuration',
|
||||
method: 'GET',
|
||||
});
|
||||
|
||||
expect(body.data.contentType.layouts.editRelations).toStrictEqual([]);
|
||||
});
|
||||
|
||||
test('Hide relations after server restart', async () => {
|
||||
await rq({
|
||||
url: '/content-manager/content-types/api::article.article/configuration',
|
||||
method: 'PUT',
|
||||
body: {
|
||||
layouts: {
|
||||
edit: [],
|
||||
editRelations: [],
|
||||
list: [],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await restart();
|
||||
|
||||
const { body } = await rq({
|
||||
url: '/content-manager/content-types/api::article.article/configuration',
|
||||
method: 'GET',
|
||||
});
|
||||
|
||||
expect(body.data.contentType.layouts.editRelations).toStrictEqual([]);
|
||||
});
|
||||
});
|
||||
@ -1,196 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
// Test a simple default API with no relations
|
||||
|
||||
const { omit, pick } = require('lodash/fp');
|
||||
|
||||
const { createTestBuilder } = require('../../../../../test/helpers/builder');
|
||||
const { createStrapiInstance } = require('../../../../../test/helpers/strapi');
|
||||
const { createAuthRequest } = require('../../../../../test/helpers/request');
|
||||
|
||||
let strapi;
|
||||
let rq;
|
||||
const data = {
|
||||
products: [],
|
||||
shops: [],
|
||||
};
|
||||
|
||||
const productModel = {
|
||||
attributes: {
|
||||
name: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
displayName: 'Product',
|
||||
singularName: 'product',
|
||||
pluralName: 'products',
|
||||
description: '',
|
||||
collectionName: '',
|
||||
};
|
||||
|
||||
const productWithDPModel = {
|
||||
attributes: {
|
||||
name: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
displayName: 'Product',
|
||||
singularName: 'product',
|
||||
pluralName: 'products',
|
||||
draftAndPublish: true,
|
||||
description: '',
|
||||
collectionName: '',
|
||||
};
|
||||
|
||||
const shopModel = {
|
||||
attributes: {
|
||||
name: {
|
||||
type: 'string',
|
||||
},
|
||||
products: {
|
||||
type: 'relation',
|
||||
relation: 'manyToMany',
|
||||
target: 'api::product.product',
|
||||
targetAttribute: 'shops',
|
||||
},
|
||||
},
|
||||
displayName: 'Shop',
|
||||
singularName: 'shop',
|
||||
pluralName: 'shops',
|
||||
};
|
||||
|
||||
const shops = [
|
||||
{
|
||||
name: 'market',
|
||||
},
|
||||
];
|
||||
|
||||
const products =
|
||||
({ withPublished = false }) =>
|
||||
({ shop }) => {
|
||||
const shops = [shop[0].id];
|
||||
|
||||
const entries = [
|
||||
{
|
||||
name: 'tomato',
|
||||
shops,
|
||||
publishedAt: new Date(),
|
||||
},
|
||||
{
|
||||
name: 'apple',
|
||||
shops,
|
||||
publishedAt: null,
|
||||
},
|
||||
];
|
||||
|
||||
if (withPublished) {
|
||||
return entries;
|
||||
}
|
||||
|
||||
return entries.map(omit('publishedAt'));
|
||||
};
|
||||
|
||||
describe('Relation-list route', () => {
|
||||
describe('without draftAndPublish', () => {
|
||||
const builder = createTestBuilder();
|
||||
|
||||
beforeAll(async () => {
|
||||
await builder
|
||||
.addContentTypes([productModel, shopModel])
|
||||
.addFixtures(shopModel.singularName, shops)
|
||||
.addFixtures(productModel.singularName, products({ withPublished: false }))
|
||||
.build();
|
||||
|
||||
strapi = await createStrapiInstance();
|
||||
rq = await createAuthRequest({ strapi });
|
||||
|
||||
data.shops = await builder.sanitizedFixturesFor(shopModel.singularName, strapi);
|
||||
data.products = await builder.sanitizedFixturesFor(productModel.singularName, strapi);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await strapi.destroy();
|
||||
await builder.cleanup();
|
||||
});
|
||||
|
||||
test('Can get relation-list for products of a shop', async () => {
|
||||
const res = await rq({
|
||||
method: 'POST',
|
||||
url: '/content-manager/relations/api::shop.shop/products',
|
||||
});
|
||||
|
||||
expect(res.body).toHaveLength(data.products.length);
|
||||
data.products.forEach((product, index) => {
|
||||
expect(res.body[index]).toStrictEqual(pick(['_id', 'id', 'name'], product));
|
||||
});
|
||||
});
|
||||
|
||||
test('Can get relation-list for products of a shop and omit some results', async () => {
|
||||
const res = await rq({
|
||||
method: 'POST',
|
||||
url: '/content-manager/relations/api::shop.shop/products',
|
||||
body: {
|
||||
idsToOmit: [data.products[0].id],
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.body).toHaveLength(1);
|
||||
expect(res.body[0]).toStrictEqual(pick(['_id', 'id', 'name'], data.products[1]));
|
||||
});
|
||||
});
|
||||
|
||||
describe('with draftAndPublish', () => {
|
||||
const builder = createTestBuilder();
|
||||
|
||||
beforeAll(async () => {
|
||||
await builder
|
||||
.addContentTypes([productWithDPModel, shopModel])
|
||||
.addFixtures(shopModel.singularName, shops)
|
||||
.addFixtures(productWithDPModel.singularName, products({ withPublished: true }))
|
||||
.build();
|
||||
|
||||
strapi = await createStrapiInstance();
|
||||
rq = await createAuthRequest({ strapi });
|
||||
|
||||
data.shops = await builder.sanitizedFixturesFor(shopModel.singularName, strapi);
|
||||
data.products = await builder.sanitizedFixturesFor(productWithDPModel.singularName, strapi);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await strapi.destroy();
|
||||
await builder.cleanup();
|
||||
});
|
||||
|
||||
test('Can get relation-list for products of a shop', async () => {
|
||||
const res = await rq({
|
||||
method: 'POST',
|
||||
url: '/content-manager/relations/api::shop.shop/products',
|
||||
});
|
||||
|
||||
expect(res.body).toHaveLength(data.products.length);
|
||||
|
||||
const tomatoProductRes = res.body.find((p) => p.name === 'tomato');
|
||||
const appleProductRes = res.body.find((p) => p.name === 'apple');
|
||||
|
||||
expect(tomatoProductRes).toMatchObject(pick(['_id', 'id', 'name'], data.products[0]));
|
||||
expect(tomatoProductRes.publishedAt).toBeISODate();
|
||||
expect(appleProductRes).toStrictEqual({
|
||||
...pick(['_id', 'id', 'name'], data.products[1]),
|
||||
publishedAt: null,
|
||||
});
|
||||
});
|
||||
|
||||
test('Can get relation-list for products of a shop and omit some results', async () => {
|
||||
const res = await rq({
|
||||
method: 'POST',
|
||||
url: '/content-manager/relations/api::shop.shop/products',
|
||||
body: {
|
||||
idsToOmit: [data.products[1].id],
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.body).toHaveLength(1);
|
||||
expect(res.body[0]).toMatchObject(pick(['_id', 'id', 'name'], data.products[0]));
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -20,6 +20,11 @@ export interface ILocalFileDestinationProviderOptions {
|
||||
enabled: boolean;
|
||||
};
|
||||
|
||||
// Archive
|
||||
archive: {
|
||||
enabled: boolean;
|
||||
};
|
||||
|
||||
// File
|
||||
file: {
|
||||
path: string;
|
||||
|
||||
@ -38,7 +38,10 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@strapi/logger": "4.5.0",
|
||||
"@strapi/strapi": "4.5.0",
|
||||
"chalk": "4.1.2",
|
||||
"fs-extra": "10.0.0",
|
||||
"lodash": "4.17.21",
|
||||
"prettier": "2.7.1",
|
||||
"stream-chain": "2.2.5",
|
||||
"stream-json": "1.7.4",
|
||||
@ -51,9 +54,8 @@
|
||||
"@types/stream-chain": "2.0.1",
|
||||
"@types/stream-json": "1.7.2",
|
||||
"@types/tar": "6.1.3",
|
||||
"fs-extra": "10.0.0",
|
||||
"rimraf": "3.0.2",
|
||||
"typescript": "4.8.4"
|
||||
"typescript": "4.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.19.1 <=18.x.x",
|
||||
|
||||
@ -277,7 +277,15 @@ program
|
||||
.argParser(parseInputBool)
|
||||
)
|
||||
.addOption(
|
||||
new Option('--compress [boolean]', 'Compress output file using gz')
|
||||
new Option('--compress [boolean]', 'Compress output file using gzip compression')
|
||||
.default(true)
|
||||
.argParser(parseInputBool)
|
||||
)
|
||||
.addOption(
|
||||
new Option(
|
||||
'--archive [boolean]',
|
||||
'Export all backup files into a single tar archive instead of a folder'
|
||||
)
|
||||
.default(true)
|
||||
.argParser(parseInputBool)
|
||||
)
|
||||
@ -310,7 +318,7 @@ program
|
||||
.addOption(
|
||||
new Option(
|
||||
'--schemaComparison <schemaComparison>',
|
||||
'exact requires every field to match, strict requires Strapi version and schemas to match, subset requires source schema to exist in destination, bypass skips checks',
|
||||
'exact requires every field to match, strict requires Strapi version and content type schema fields do not break, subset requires source schema to exist in destination, bypass skips checks',
|
||||
parseInputList
|
||||
)
|
||||
.choices(['exact', 'strict', 'subset', 'bypass'])
|
||||
|
||||
@ -78,6 +78,9 @@ module.exports = async (filename, opts) => {
|
||||
compression: {
|
||||
enabled: opts.compress,
|
||||
},
|
||||
archive: {
|
||||
enabled: opts.archive,
|
||||
},
|
||||
};
|
||||
const destination = createLocalFileDestinationProvider(destinationOptions);
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@ import { Attribute, ConfigurableOption, PrivateOption } from './base';
|
||||
import { GetAttributesByType, GetAttributesValues } from './utils';
|
||||
|
||||
export type BasicRelationsType = 'oneToOne' | 'oneToMany' | 'manyToOne' | 'manyToMany';
|
||||
export type PolymorphicRelationsType = 'morphToOne' | 'morphToMany' | 'morphOne' | 'morphMany';
|
||||
export type PolymorphicRelationsType = 'morphToOne' | 'morphToMany' | 'morphOne' | 'morphMany';
|
||||
export type RelationsType = BasicRelationsType | PolymorphicRelationsType;
|
||||
|
||||
export interface BasicRelationAttributeProperties<
|
||||
@ -17,16 +17,14 @@ export interface BasicRelationAttributeProperties<
|
||||
mappedBy?: RelationsKeysFromTo<T, S>;
|
||||
}
|
||||
|
||||
export interface PolymorphicRelationAttributeProperties<
|
||||
R extends RelationsType,
|
||||
> {
|
||||
export interface PolymorphicRelationAttributeProperties<R extends RelationsType> {
|
||||
relation: R;
|
||||
}
|
||||
|
||||
export type RelationAttribute<
|
||||
S extends SchemaUID,
|
||||
R extends RelationsType,
|
||||
T extends R extends PolymorphicRelationsType ? never: SchemaUID = never
|
||||
T extends R extends PolymorphicRelationsType ? never : SchemaUID = never
|
||||
> = Attribute<'relation'> &
|
||||
// Properties
|
||||
(R extends BasicRelationsType
|
||||
@ -34,22 +32,21 @@ export type RelationAttribute<
|
||||
: PolymorphicRelationAttributeProperties<R>) &
|
||||
// Options
|
||||
ConfigurableOption &
|
||||
PrivateOption
|
||||
PrivateOption;
|
||||
|
||||
export type RelationsKeysFromTo<
|
||||
TTarget extends SchemaUID,
|
||||
TSource extends SchemaUID
|
||||
> = keyof PickRelationsFromTo<TTarget, TSource>;
|
||||
|
||||
export type PickRelationsFromTo<TTarget extends SchemaUID, TSource extends SchemaUID> = GetAttributesByType<
|
||||
TTarget,
|
||||
'relation',
|
||||
{ target: TSource }
|
||||
>;
|
||||
export type PickRelationsFromTo<
|
||||
TTarget extends SchemaUID,
|
||||
TSource extends SchemaUID
|
||||
> = GetAttributesByType<TTarget, 'relation', { target: TSource }>;
|
||||
|
||||
export type RelationPluralityModifier<
|
||||
TRelation extends RelationsType,
|
||||
TValue extends Object
|
||||
TValue extends Record<string, unknown>
|
||||
> = TRelation extends `${string}Many` ? TValue[] : TValue;
|
||||
|
||||
export type RelationValue<
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { Service,GenericService } from '../core-api/service';
|
||||
import { Service, GenericService } from '../core-api/service';
|
||||
import { Controller, GenericController } from '../core-api/controller';
|
||||
import { Middleware } from '../middlewares';
|
||||
import { Policy } from '../core/registries/policies';
|
||||
import { Strapi } from '@strapi/strapi';
|
||||
import { Strapi } from './core/strapi';
|
||||
|
||||
type ControllerConfig<T extends Controller = Controller> = T;
|
||||
|
||||
|
||||
@ -18,7 +18,7 @@ const createUserBodySchema = yup.object().shape({
|
||||
connect: yup
|
||||
.array()
|
||||
.of(yup.object().shape({ id: yup.strapiID().required() }))
|
||||
.min(1)
|
||||
.min(1, 'Users must have a role')
|
||||
.required(),
|
||||
})
|
||||
.required()
|
||||
@ -36,7 +36,16 @@ const updateUserBodySchema = yup.object().shape({
|
||||
connect: yup
|
||||
.array()
|
||||
.of(yup.object().shape({ id: yup.strapiID().required() }))
|
||||
.min(1)
|
||||
.required(),
|
||||
disconnect: yup
|
||||
.array()
|
||||
.test('CheckDisconnect', 'Cannot remove role', function test(disconnectValue) {
|
||||
if (value.connect.length === 0 && disconnectValue.length > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
.required(),
|
||||
})
|
||||
: yup.strapiID()
|
||||
|
||||
77
yarn.lock
77
yarn.lock
@ -6505,7 +6505,7 @@
|
||||
dependencies:
|
||||
"@types/yargs-parser" "*"
|
||||
|
||||
"@typescript-eslint/eslint-plugin@^5.14.0":
|
||||
"@typescript-eslint/eslint-plugin@5.43.0", "@typescript-eslint/eslint-plugin@^5.14.0":
|
||||
version "5.43.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.43.0.tgz#4a5248eb31b454715ddfbf8cfbf497529a0a78bc"
|
||||
integrity sha512-wNPzG+eDR6+hhW4yobEmpR36jrqqQv1vxBq5LJO3fBAktjkvekfr4BRl+3Fn1CM/A+s8/EiGUbOMDoYqWdbtXA==
|
||||
@ -6520,6 +6520,16 @@
|
||||
semver "^7.3.7"
|
||||
tsutils "^3.21.0"
|
||||
|
||||
"@typescript-eslint/parser@5.42.1":
|
||||
version "5.42.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.42.1.tgz#3e66156f2f74b11690b45950d8f5f28a62751d35"
|
||||
integrity sha512-kAV+NiNBWVQDY9gDJDToTE/NO8BHi4f6b7zTsVAJoTkmB/zlfOpiEVBzHOKtlgTndCKe8vj9F/PuolemZSh50Q==
|
||||
dependencies:
|
||||
"@typescript-eslint/scope-manager" "5.42.1"
|
||||
"@typescript-eslint/types" "5.42.1"
|
||||
"@typescript-eslint/typescript-estree" "5.42.1"
|
||||
debug "^4.3.4"
|
||||
|
||||
"@typescript-eslint/parser@^5.14.0":
|
||||
version "5.43.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.43.0.tgz#9c86581234b88f2ba406f0b99a274a91c11630fd"
|
||||
@ -6530,6 +6540,14 @@
|
||||
"@typescript-eslint/typescript-estree" "5.43.0"
|
||||
debug "^4.3.4"
|
||||
|
||||
"@typescript-eslint/scope-manager@5.42.1":
|
||||
version "5.42.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.42.1.tgz#05e5e1351485637d466464237e5259b49f609b18"
|
||||
integrity sha512-QAZY/CBP1Emx4rzxurgqj3rUinfsh/6mvuKbLNMfJMMKYLRBfweus8brgXF8f64ABkIZ3zdj2/rYYtF8eiuksQ==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "5.42.1"
|
||||
"@typescript-eslint/visitor-keys" "5.42.1"
|
||||
|
||||
"@typescript-eslint/scope-manager@5.43.0":
|
||||
version "5.43.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.43.0.tgz#566e46303392014d5d163704724872e1f2dd3c15"
|
||||
@ -6548,11 +6566,29 @@
|
||||
debug "^4.3.4"
|
||||
tsutils "^3.21.0"
|
||||
|
||||
"@typescript-eslint/types@5.42.1":
|
||||
version "5.42.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.42.1.tgz#0d4283c30e9b70d2aa2391c36294413de9106df2"
|
||||
integrity sha512-Qrco9dsFF5lhalz+lLFtxs3ui1/YfC6NdXu+RAGBa8uSfn01cjO7ssCsjIsUs484vny9Xm699FSKwpkCcqwWwA==
|
||||
|
||||
"@typescript-eslint/types@5.43.0":
|
||||
version "5.43.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.43.0.tgz#e4ddd7846fcbc074325293515fa98e844d8d2578"
|
||||
integrity sha512-jpsbcD0x6AUvV7tyOlyvon0aUsQpF8W+7TpJntfCUWU1qaIKu2K34pMwQKSzQH8ORgUrGYY6pVIh1Pi8TNeteg==
|
||||
|
||||
"@typescript-eslint/typescript-estree@5.42.1":
|
||||
version "5.42.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.42.1.tgz#f9a223ecb547a781d37e07a5ac6ba9ff681eaef0"
|
||||
integrity sha512-qElc0bDOuO0B8wDhhW4mYVgi/LZL+igPwXtV87n69/kYC/7NG3MES0jHxJNCr4EP7kY1XVsRy8C/u3DYeTKQmw==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "5.42.1"
|
||||
"@typescript-eslint/visitor-keys" "5.42.1"
|
||||
debug "^4.3.4"
|
||||
globby "^11.1.0"
|
||||
is-glob "^4.0.3"
|
||||
semver "^7.3.7"
|
||||
tsutils "^3.21.0"
|
||||
|
||||
"@typescript-eslint/typescript-estree@5.43.0":
|
||||
version "5.43.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.43.0.tgz#b6883e58ba236a602c334be116bfc00b58b3b9f2"
|
||||
@ -6580,6 +6616,14 @@
|
||||
eslint-utils "^3.0.0"
|
||||
semver "^7.3.7"
|
||||
|
||||
"@typescript-eslint/visitor-keys@5.42.1":
|
||||
version "5.42.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.42.1.tgz#df10839adf6605e1cdb79174cf21e46df9be4872"
|
||||
integrity sha512-LOQtSF4z+hejmpUvitPlc4hA7ERGoj2BVkesOcG91HCn8edLGUXbTrErmutmPbl8Bo9HjAvOO/zBKQHExXNA2A==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "5.42.1"
|
||||
eslint-visitor-keys "^3.3.0"
|
||||
|
||||
"@typescript-eslint/visitor-keys@5.43.0":
|
||||
version "5.43.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.43.0.tgz#cbbdadfdfea385310a20a962afda728ea106befa"
|
||||
@ -11072,7 +11116,7 @@ eslint-config-airbnb@^19.0.4:
|
||||
object.assign "^4.1.2"
|
||||
object.entries "^1.1.5"
|
||||
|
||||
eslint-config-prettier@^8.5.0:
|
||||
eslint-config-prettier@8.5.0, eslint-config-prettier@^8.5.0:
|
||||
version "8.5.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz#5a81680ec934beca02c7b1a61cf8ca34b66feab1"
|
||||
integrity sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==
|
||||
@ -11100,7 +11144,7 @@ eslint-plugin-es@^3.0.0:
|
||||
eslint-utils "^2.0.0"
|
||||
regexpp "^3.0.0"
|
||||
|
||||
eslint-plugin-import@^2.25.4:
|
||||
eslint-plugin-import@2.26.0, eslint-plugin-import@^2.25.4:
|
||||
version "2.26.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz#f812dc47be4f2b72b478a021605a59fc6fe8b88b"
|
||||
integrity sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==
|
||||
@ -11119,7 +11163,7 @@ eslint-plugin-import@^2.25.4:
|
||||
resolve "^1.22.0"
|
||||
tsconfig-paths "^3.14.1"
|
||||
|
||||
eslint-plugin-jsx-a11y@^6.5.1:
|
||||
eslint-plugin-jsx-a11y@6.6.1, eslint-plugin-jsx-a11y@^6.5.1:
|
||||
version "6.6.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.6.1.tgz#93736fc91b83fdc38cc8d115deedfc3091aef1ff"
|
||||
integrity sha512-sXgFVNHiWffBq23uiS/JaP6eVR622DqwB4yTzKvGZGcPq6/yZ3WmOZfuBks/vHWo9GaFOqC2ZK4i6+C35knx7Q==
|
||||
@ -11138,7 +11182,7 @@ eslint-plugin-jsx-a11y@^6.5.1:
|
||||
minimatch "^3.1.2"
|
||||
semver "^6.3.0"
|
||||
|
||||
eslint-plugin-node@^11.1.0:
|
||||
eslint-plugin-node@11.1.0, eslint-plugin-node@^11.1.0:
|
||||
version "11.1.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz#c95544416ee4ada26740a30474eefc5402dc671d"
|
||||
integrity sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==
|
||||
@ -11150,19 +11194,19 @@ eslint-plugin-node@^11.1.0:
|
||||
resolve "^1.10.1"
|
||||
semver "^6.1.0"
|
||||
|
||||
eslint-plugin-prettier@^4.0.0:
|
||||
eslint-plugin-prettier@4.2.1, eslint-plugin-prettier@^4.0.0:
|
||||
version "4.2.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz#651cbb88b1dab98bfd42f017a12fa6b2d993f94b"
|
||||
integrity sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==
|
||||
dependencies:
|
||||
prettier-linter-helpers "^1.0.0"
|
||||
|
||||
eslint-plugin-react-hooks@^4.3.0:
|
||||
eslint-plugin-react-hooks@4.6.0, eslint-plugin-react-hooks@^4.3.0:
|
||||
version "4.6.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz#4c3e697ad95b77e93f8646aaa1630c1ba607edd3"
|
||||
integrity sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==
|
||||
|
||||
eslint-plugin-react@^7.29.3:
|
||||
eslint-plugin-react@7.31.10, eslint-plugin-react@^7.29.3:
|
||||
version "7.31.10"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.31.10.tgz#6782c2c7fe91c09e715d536067644bbb9491419a"
|
||||
integrity sha512-e4N/nc6AAlg4UKW/mXeYWd3R++qUano5/o+t+wnWxIf+bLsOaH3a4q74kX3nDjYym3VBN4HyO9nEn1GcAqgQOA==
|
||||
@ -11182,6 +11226,13 @@ eslint-plugin-react@^7.29.3:
|
||||
semver "^6.3.0"
|
||||
string.prototype.matchall "^4.0.7"
|
||||
|
||||
eslint-plugin@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin/-/eslint-plugin-1.0.1.tgz#24bf3136d167c90ed0d4d5702d8b9b1ed2beef79"
|
||||
integrity sha512-ervp8C09On0fLA258TvE08AqAr/bhRYgHVZd3BrJjD4JfOA2JGANDLGs06j51oWqfPd7Feoo3OoqHD+fuI2sFQ==
|
||||
dependencies:
|
||||
requireindex "~1.1.0"
|
||||
|
||||
eslint-scope@5.1.1, eslint-scope@^5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
|
||||
@ -19891,6 +19942,11 @@ require-from-string@2.0.2, require-from-string@^2.0.2:
|
||||
resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
|
||||
integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
|
||||
|
||||
requireindex@~1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/requireindex/-/requireindex-1.1.0.tgz#e5404b81557ef75db6e49c5a72004893fe03e162"
|
||||
integrity sha512-LBnkqsDE7BZKvqylbmn7lTIVdpx4K/QCduRATpO5R+wtPmky/a8pN1bO2D6wXppn1497AJF9mNjqAXr6bdl9jg==
|
||||
|
||||
requires-port@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
|
||||
@ -22059,11 +22115,6 @@ typescript@4.6.2:
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.2.tgz#fe12d2727b708f4eef40f51598b3398baa9611d4"
|
||||
integrity sha512-HM/hFigTBHZhLXshn9sN37H085+hQGeJHJ/X7LpBWLID/fbc2acUMfU+lGD98X81sKP+pFa9f0DZmCwB9GnbAg==
|
||||
|
||||
typescript@4.8.4:
|
||||
version "4.8.4"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.4.tgz#c464abca159669597be5f96b8943500b238e60e6"
|
||||
integrity sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==
|
||||
|
||||
typescript@^4.6.2:
|
||||
version "4.9.3"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.3.tgz#3aea307c1746b8c384435d8ac36b8a2e580d85db"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user