Compare commits

...

2 Commits

Author SHA1 Message Date
Thomas Kaul ffa21edd8e
Feature/upgrade prisma to version 6.2.1 (#4184) 1 week ago
Szymon Łągiewka ca45098de3
Feature/refactor various lodash functions with native JavaScript equivalents (#4170) 1 week ago
  1. 5
      CHANGELOG.md
  2. 4
      apps/api/src/app/benchmark/benchmark.service.ts
  3. 8
      apps/api/src/app/portfolio/calculator/portfolio-calculator.ts
  4. 3
      apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-baln-buy-and-sell-in-two-activities.spec.ts
  5. 3
      apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-baln-buy-and-sell.spec.ts
  6. 3
      apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-baln-buy.spec.ts
  7. 3
      apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts
  8. 3
      apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-fee.spec.ts
  9. 3
      apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-googl-buy.spec.ts
  10. 3
      apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-item.spec.ts
  11. 3
      apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-msft-buy-with-dividend.spec.ts
  12. 3
      apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-novn-buy-and-sell-partially.spec.ts
  13. 3
      apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-novn-buy-and-sell.spec.ts
  14. 6
      apps/api/src/app/portfolio/calculator/twr/portfolio-calculator.ts
  15. 6
      apps/api/src/app/portfolio/current-rate.service.ts
  16. 23
      apps/api/src/app/portfolio/portfolio.service.ts
  17. 64
      package-lock.json
  18. 4
      package.json

5
CHANGELOG.md

@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased
### Changed
- Refactored various `lodash` functions with native JavaScript equivalents
- Upgraded `prisma` from version `6.1.0` to `6.2.1`
### Fixed
- Fixed an issue with the renaming of activities with type `FEE`, `INTEREST`, `ITEM` or `LIABILITY`

4
apps/api/src/app/benchmark/benchmark.service.ts

@ -38,7 +38,7 @@ import {
isSameDay,
subDays
} from 'date-fns';
import { isNumber, last, uniqBy } from 'lodash';
import { isNumber, uniqBy } from 'lodash';
import ms from 'ms';
import { BenchmarkValue } from './interfaces/benchmark-value.interface';
@ -258,7 +258,7 @@ export class BenchmarkService {
}
const includesEndDate = isSameDay(
parseDate(last(marketData).date),
parseDate(marketData.at(-1).date),
endDate
);

8
apps/api/src/app/portfolio/calculator/portfolio-calculator.ts

@ -49,7 +49,7 @@ import {
min,
subDays
} from 'date-fns';
import { first, isNumber, last, sortBy, sum, uniq, uniqBy } from 'lodash';
import { isNumber, sortBy, sum, uniq, uniqBy } from 'lodash';
export abstract class PortfolioCalculator {
protected static readonly ENABLE_LOGGING = false;
@ -167,7 +167,7 @@ export abstract class PortfolioCalculator {
@LogPerformance
public async computeSnapshot(): Promise<PortfolioSnapshot> {
const lastTransactionPoint = last(this.transactionPoints);
const lastTransactionPoint = this.transactionPoints.at(-1);
const transactionPoints = this.transactionPoints?.filter(({ date }) => {
return isBefore(parseDate(date), this.endDate);
@ -772,9 +772,7 @@ export abstract class PortfolioCalculator {
let firstActivityDate: Date;
try {
const firstAccountBalanceDateString = first(
this.accountBalanceItems
)?.date;
const firstAccountBalanceDateString = this.accountBalanceItems[0]?.date;
firstAccountBalanceDate = firstAccountBalanceDateString
? parseDate(firstAccountBalanceDateString)
: new Date();

3
apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-baln-buy-and-sell-in-two-activities.spec.ts

@ -19,7 +19,6 @@ import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/po
import { parseDate } from '@ghostfolio/common/helper';
import { Big } from 'big.js';
import { last } from 'lodash';
jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => {
return {
@ -201,7 +200,7 @@ describe('PortfolioCalculator', () => {
totalValuablesWithCurrencyEffect: new Big('0')
});
expect(last(portfolioSnapshot.historicalData)).toMatchObject(
expect(portfolioSnapshot.historicalData.at(-1)).toMatchObject(
expect.objectContaining({
netPerformance: -15.8,
netPerformanceInPercentage: -0.05528341497550734703,

3
apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-baln-buy-and-sell.spec.ts

@ -19,7 +19,6 @@ import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/po
import { parseDate } from '@ghostfolio/common/helper';
import { Big } from 'big.js';
import { last } from 'lodash';
jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => {
return {
@ -186,7 +185,7 @@ describe('PortfolioCalculator', () => {
totalValuablesWithCurrencyEffect: new Big('0')
});
expect(last(portfolioSnapshot.historicalData)).toMatchObject(
expect(portfolioSnapshot.historicalData.at(-1)).toMatchObject(
expect.objectContaining({
netPerformance: -15.8,
netPerformanceInPercentage: -0.05528341497550734703,

3
apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-baln-buy.spec.ts

@ -19,7 +19,6 @@ import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/po
import { parseDate } from '@ghostfolio/common/helper';
import { Big } from 'big.js';
import { last } from 'lodash';
jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => {
return {
@ -177,7 +176,7 @@ describe('PortfolioCalculator', () => {
totalValuablesWithCurrencyEffect: new Big('0')
});
expect(last(portfolioSnapshot.historicalData)).toMatchObject(
expect(portfolioSnapshot.historicalData.at(-1)).toMatchObject(
expect.objectContaining({
netPerformance: 23.05,
netPerformanceInPercentage: 0.08437042459736457,

3
apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-btcusd-buy-and-sell-partially.spec.ts

@ -20,7 +20,6 @@ import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/po
import { parseDate } from '@ghostfolio/common/helper';
import { Big } from 'big.js';
import { last } from 'lodash';
jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => {
return {
@ -205,7 +204,7 @@ describe('PortfolioCalculator', () => {
totalValuablesWithCurrencyEffect: new Big('0')
});
expect(last(portfolioSnapshot.historicalData)).toMatchObject(
expect(portfolioSnapshot.historicalData.at(-1)).toMatchObject(
expect.objectContaining({
netPerformance: new Big('27172.74').mul(0.97373).toNumber(),
netPerformanceInPercentage: 42.41983590271396609433,

3
apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-fee.spec.ts

@ -19,7 +19,6 @@ import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/po
import { parseDate } from '@ghostfolio/common/helper';
import { Big } from 'big.js';
import { last } from 'lodash';
jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => {
return {
@ -158,7 +157,7 @@ describe('PortfolioCalculator', () => {
totalValuablesWithCurrencyEffect: new Big('0')
});
expect(last(portfolioSnapshot.historicalData)).toMatchObject(
expect(portfolioSnapshot.historicalData.at(-1)).toMatchObject(
expect.objectContaining({
netPerformance: 0,
netPerformanceInPercentage: 0,

3
apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-googl-buy.spec.ts

@ -20,7 +20,6 @@ import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/po
import { parseDate } from '@ghostfolio/common/helper';
import { Big } from 'big.js';
import { last } from 'lodash';
jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => {
return {
@ -184,7 +183,7 @@ describe('PortfolioCalculator', () => {
totalValuablesWithCurrencyEffect: new Big('0')
});
expect(last(portfolioSnapshot.historicalData)).toMatchObject(
expect(portfolioSnapshot.historicalData.at(-1)).toMatchObject(
expect.objectContaining({
netPerformance: new Big('26.33').mul(0.8854).toNumber(),
netPerformanceInPercentage: 0.29544434470377019749,

3
apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-item.spec.ts

@ -19,7 +19,6 @@ import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/po
import { parseDate } from '@ghostfolio/common/helper';
import { Big } from 'big.js';
import { last } from 'lodash';
jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => {
return {
@ -158,7 +157,7 @@ describe('PortfolioCalculator', () => {
totalValuablesWithCurrencyEffect: new Big('0')
});
expect(last(portfolioSnapshot.historicalData)).toMatchObject(
expect(portfolioSnapshot.historicalData.at(-1)).toMatchObject(
expect.objectContaining({
netPerformance: 0,
netPerformanceInPercentage: 0,

3
apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-msft-buy-with-dividend.spec.ts

@ -20,7 +20,6 @@ import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/po
import { parseDate } from '@ghostfolio/common/helper';
import { Big } from 'big.js';
import { last } from 'lodash';
jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => {
return {
@ -190,7 +189,7 @@ describe('PortfolioCalculator', () => {
totalValuablesWithCurrencyEffect: new Big('0')
});
expect(last(portfolioSnapshot.historicalData)).toMatchObject(
expect(portfolioSnapshot.historicalData.at(-1)).toMatchObject(
expect.objectContaining({
totalInvestmentValueWithCurrencyEffect: 298.58
})

3
apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-novn-buy-and-sell-partially.spec.ts

@ -21,7 +21,6 @@ import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/po
import { parseDate } from '@ghostfolio/common/helper';
import { Big } from 'big.js';
import { last } from 'lodash';
import { join } from 'path';
jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => {
@ -182,7 +181,7 @@ describe('PortfolioCalculator', () => {
totalValuablesWithCurrencyEffect: new Big('0')
});
expect(last(portfolioSnapshot.historicalData)).toMatchObject(
expect(portfolioSnapshot.historicalData.at(-1)).toMatchObject(
expect.objectContaining({
netPerformance: 17.68,
netPerformanceInPercentage: 0.12184460284330327256,

3
apps/api/src/app/portfolio/calculator/twr/portfolio-calculator-novn-buy-and-sell.spec.ts

@ -21,7 +21,6 @@ import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/po
import { parseDate } from '@ghostfolio/common/helper';
import { Big } from 'big.js';
import { last } from 'lodash';
import { join } from 'path';
jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => {
@ -229,7 +228,7 @@ describe('PortfolioCalculator', () => {
totalValuablesWithCurrencyEffect: new Big('0')
});
expect(last(portfolioSnapshot.historicalData)).toMatchObject(
expect(portfolioSnapshot.historicalData.at(-1)).toMatchObject(
expect.objectContaining({
netPerformance: 19.86,
netPerformanceInPercentage: 0.13100263852242744063,

6
apps/api/src/app/portfolio/calculator/twr/portfolio-calculator.ts

@ -13,7 +13,7 @@ import { DateRange } from '@ghostfolio/common/types';
import { Logger } from '@nestjs/common';
import { Big } from 'big.js';
import { addMilliseconds, differenceInDays, format, isBefore } from 'date-fns';
import { cloneDeep, first, last, sortBy } from 'lodash';
import { cloneDeep, sortBy } from 'lodash';
export class TWRPortfolioCalculator extends PortfolioCalculator {
private chartDates: string[];
@ -221,7 +221,7 @@ export class TWRPortfolioCalculator extends PortfolioCalculator {
};
}
const dateOfFirstTransaction = new Date(first(orders).date);
const dateOfFirstTransaction = new Date(orders[0].date);
const endDateString = format(end, DATE_FORMAT);
const startDateString = format(start, DATE_FORMAT);
@ -342,7 +342,7 @@ export class TWRPortfolioCalculator extends PortfolioCalculator {
});
}
const lastOrder = last(orders);
const lastOrder = orders.at(-1);
lastUnitPrice = lastOrder.unitPriceFromMarketData ?? lastOrder.unitPrice;
}

6
apps/api/src/app/portfolio/current-rate.service.ts

@ -13,7 +13,7 @@ import type { RequestWithUser } from '@ghostfolio/common/types';
import { Inject, Injectable } from '@nestjs/common';
import { REQUEST } from '@nestjs/core';
import { isBefore, isToday } from 'date-fns';
import { flatten, isEmpty, uniqBy } from 'lodash';
import { isEmpty, uniqBy } from 'lodash';
import { GetValueObject } from './interfaces/get-value-object.interface';
import { GetValuesObject } from './interfaces/get-values-object.interface';
@ -102,7 +102,9 @@ export class CurrentRateService {
})
);
const values = flatten(await Promise.all(promises));
const values = await Promise.all(promises).then((array) => {
return array.flat();
});
const response: GetValuesObject = {
dataProviderInfos,

23
apps/api/src/app/portfolio/portfolio.service.ts

@ -77,7 +77,7 @@ import {
parseISO,
set
} from 'date-fns';
import { isEmpty, last, uniq } from 'lodash';
import { isEmpty, uniq } from 'lodash';
import { PortfolioCalculator } from './calculator/portfolio-calculator';
import {
@ -1133,18 +1133,15 @@ export class PortfolioService {
netWorth,
totalInvestment,
valueWithCurrencyEffect
} =
chart?.length > 0
? last(chart)
: {
netPerformance: 0,
netPerformanceInPercentage: 0,
netPerformanceInPercentageWithCurrencyEffect: 0,
netPerformanceWithCurrencyEffect: 0,
netWorth: 0,
totalInvestment: 0,
valueWithCurrencyEffect: 0
};
} = chart?.at(-1) ?? {
netPerformance: 0,
netPerformanceInPercentage: 0,
netPerformanceInPercentageWithCurrencyEffect: 0,
netPerformanceWithCurrencyEffect: 0,
netWorth: 0,
totalInvestment: 0,
valueWithCurrencyEffect: 0
};
return {
chart,

64
package-lock.json

@ -40,7 +40,7 @@
"@nestjs/platform-express": "10.1.3",
"@nestjs/schedule": "3.0.2",
"@nestjs/serve-static": "4.0.0",
"@prisma/client": "6.1.0",
"@prisma/client": "6.2.1",
"@simplewebauthn/browser": "9.0.1",
"@simplewebauthn/server": "9.0.3",
"@stripe/stripe-js": "5.4.0",
@ -149,7 +149,7 @@
"nx": "20.3.0",
"prettier": "3.4.2",
"prettier-plugin-organize-attributes": "1.0.0",
"prisma": "6.1.0",
"prisma": "6.2.1",
"react": "18.2.0",
"react-dom": "18.2.0",
"replace-in-file": "8.3.0",
@ -9142,9 +9142,9 @@
"license": "MIT"
},
"node_modules/@prisma/client": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.1.0.tgz",
"integrity": "sha512-AbQYc5+EJKm1Ydfq3KxwcGiy7wIbm4/QbjCKWWoNROtvy7d6a3gmAGkKjK0iUCzh+rHV8xDhD5Cge8ke/kiy5Q==",
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.2.1.tgz",
"integrity": "sha512-msKY2iRLISN8t5X0Tj7hU0UWet1u0KuxSPHWuf3IRkB4J95mCvGpyQBfQ6ufcmvKNOMQSq90O2iUmJEN2e5fiA==",
"hasInstallScript": true,
"license": "Apache-2.0",
"engines": {
@ -9160,53 +9160,53 @@
}
},
"node_modules/@prisma/debug": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.1.0.tgz",
"integrity": "sha512-0himsvcM4DGBTtvXkd2Tggv6sl2JyUYLzEGXXleFY+7Kp6rZeSS3hiTW9mwtUlXrwYbJP6pwlVNB7jYElrjWUg==",
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.2.1.tgz",
"integrity": "sha512-0KItvt39CmQxWkEw6oW+RQMD6RZ43SJWgEUnzxN8VC9ixMysa7MzZCZf22LCK5DSooiLNf8vM3LHZm/I/Ni7bQ==",
"devOptional": true,
"license": "Apache-2.0"
},
"node_modules/@prisma/engines": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.1.0.tgz",
"integrity": "sha512-GnYJbCiep3Vyr1P/415ReYrgJUjP79fBNc1wCo7NP6Eia0CzL2Ot9vK7Infczv3oK7JLrCcawOSAxFxNFsAERQ==",
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.2.1.tgz",
"integrity": "sha512-lTBNLJBCxVT9iP5I7Mn6GlwqAxTpS5qMERrhebkUhtXpGVkBNd/jHnNJBZQW4kGDCKaQg/r2vlJYkzOHnAb7ZQ==",
"devOptional": true,
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
"@prisma/debug": "6.1.0",
"@prisma/engines-version": "6.1.0-21.11f085a2012c0f4778414c8db2651556ee0ef959",
"@prisma/fetch-engine": "6.1.0",
"@prisma/get-platform": "6.1.0"
"@prisma/debug": "6.2.1",
"@prisma/engines-version": "6.2.0-14.4123509d24aa4dede1e864b46351bf2790323b69",
"@prisma/fetch-engine": "6.2.1",
"@prisma/get-platform": "6.2.1"
}
},
"node_modules/@prisma/engines-version": {
"version": "6.1.0-21.11f085a2012c0f4778414c8db2651556ee0ef959",
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.1.0-21.11f085a2012c0f4778414c8db2651556ee0ef959.tgz",
"integrity": "sha512-PdJqmYM2Fd8K0weOOtQThWylwjsDlTig+8Pcg47/jszMuLL9iLIaygC3cjWJLda69siRW4STlCTMSgOjZzvKPQ==",
"version": "6.2.0-14.4123509d24aa4dede1e864b46351bf2790323b69",
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.2.0-14.4123509d24aa4dede1e864b46351bf2790323b69.tgz",
"integrity": "sha512-7tw1qs/9GWSX6qbZs4He09TOTg1ff3gYsB3ubaVNN0Pp1zLm9NC5C5MZShtkz7TyQjx7blhpknB7HwEhlG+PrQ==",
"devOptional": true,
"license": "Apache-2.0"
},
"node_modules/@prisma/fetch-engine": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.1.0.tgz",
"integrity": "sha512-asdFi7TvPlEZ8CzSZ/+Du5wZ27q6OJbRSXh+S8ISZguu+S9KtS/gP7NeXceZyb1Jv1SM1S5YfiCv+STDsG6rrg==",
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.2.1.tgz",
"integrity": "sha512-OO7O9d6Mrx2F9i+Gu1LW+DGXXyUFkP7OE5aj9iBfA/2jjDXEJjqa9X0ZmM9NZNo8Uo7ql6zKm6yjDcbAcRrw1A==",
"devOptional": true,
"license": "Apache-2.0",
"dependencies": {
"@prisma/debug": "6.1.0",
"@prisma/engines-version": "6.1.0-21.11f085a2012c0f4778414c8db2651556ee0ef959",
"@prisma/get-platform": "6.1.0"
"@prisma/debug": "6.2.1",
"@prisma/engines-version": "6.2.0-14.4123509d24aa4dede1e864b46351bf2790323b69",
"@prisma/get-platform": "6.2.1"
}
},
"node_modules/@prisma/get-platform": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.1.0.tgz",
"integrity": "sha512-ia8bNjboBoHkmKGGaWtqtlgQOhCi7+f85aOkPJKgNwWvYrT6l78KgojLekE8zMhVk0R9lWcifV0Pf8l3/15V0Q==",
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.2.1.tgz",
"integrity": "sha512-zp53yvroPl5m5/gXYLz7tGCNG33bhG+JYCm74ohxOq1pPnrL47VQYFfF3RbTZ7TzGWCrR3EtoiYMywUBw7UK6Q==",
"devOptional": true,
"license": "Apache-2.0",
"dependencies": {
"@prisma/debug": "6.1.0"
"@prisma/debug": "6.2.1"
}
},
"node_modules/@redis/bloom": {
@ -28249,14 +28249,14 @@
}
},
"node_modules/prisma": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/prisma/-/prisma-6.1.0.tgz",
"integrity": "sha512-aFI3Yi+ApUxkwCJJwyQSwpyzUX7YX3ihzuHNHOyv4GJg3X5tQsmRaJEnZ+ZyfHpMtnyahhmXVfbTZ+lS8ZtfKw==",
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/prisma/-/prisma-6.2.1.tgz",
"integrity": "sha512-hhyM0H13pQleQ+br4CkzGizS5I0oInoeTw3JfLw1BRZduBSQxPILlJLwi+46wZzj9Je7ndyQEMGw/n5cN2fknA==",
"devOptional": true,
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
"@prisma/engines": "6.1.0"
"@prisma/engines": "6.2.1"
},
"bin": {
"prisma": "build/index.js"

4
package.json

@ -86,7 +86,7 @@
"@nestjs/platform-express": "10.1.3",
"@nestjs/schedule": "3.0.2",
"@nestjs/serve-static": "4.0.0",
"@prisma/client": "6.1.0",
"@prisma/client": "6.2.1",
"@simplewebauthn/browser": "9.0.1",
"@simplewebauthn/server": "9.0.3",
"@stripe/stripe-js": "5.4.0",
@ -195,7 +195,7 @@
"nx": "20.3.0",
"prettier": "3.4.2",
"prettier-plugin-organize-attributes": "1.0.0",
"prisma": "6.1.0",
"prisma": "6.2.1",
"react": "18.2.0",
"react-dom": "18.2.0",
"replace-in-file": "8.3.0",

Loading…
Cancel
Save