feat(next): update payments-next build and restart

Because:

- Starting and restarting payments-next takes a long time due to always
  building the app first, which isn't necessary during development.
- When changes are made to NestApp or its dependencies, these are not
  hot reloaded by Next.js, requiring a full Next.js restart.

This commit:

- Adopt Nx plugin for Next.js which more closely aligns with Next.js
  standard dev commands.
- Retained Nx Next.js executors for build, since it more closely fits
  into the current FxA build and deploy CI logic.
- Added a watcher to NestApp which calls a NestApp restart api, only
  available in development, to restart the NestApp on any changes.
- Adds a function to get the NestApp, instead of a const, thus ensuring,
  in dev mode, the latest NestApp is always returned.

Closes #FXA-9706
This commit is contained in:
Reino Muhl 2024-06-07 15:39:06 -04:00
Родитель 50b07e32f7
Коммит ab8ae37229
Не найден ключ, соответствующий данной подписи
28 изменённых файлов: 497 добавлений и 125 удалений

2
.gitignore поставляемый
Просмотреть файл

@ -174,4 +174,4 @@ tmp
# shared-contentful
libs/shared/contentful/src/__generated__/graphql.d.ts
libs/shared/contentful/src/__generated__/graphql.js
libs/shared/contentful/src/__generated__/graphql.js

Просмотреть файл

@ -27,6 +27,14 @@ module.exports = function (grunt) {
dest: 'public/locales/en/payments.ftl',
},
},
http: {
// Call the Payments Next route to restart and reinitialize the Nest App.
nestapp: {
options: {
url: 'http://localhost:3035/api/dev/nestapp/restart'
}
}
},
watch: {
ftl: {
files: srcPaths,
@ -35,14 +43,35 @@ module.exports = function (grunt) {
interrupt: true,
},
},
nestapp: {
files: [
'../../../libs/payments/**/*.ts',
'../../../libs/shared/**/*.ts'
],
tasks: ['http:nestapp'],
options: {
interrupt: true,
},
}
},
// Allows for multiple watchers tasks to be run concurrently.
concurrent: {
options: {
logConcurrentOutput: true,
},
dev: {
tasks: ['watch:nestapp', 'watch:ftl']
}
}
});
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-http');
grunt.loadNpmTasks('grunt-concurrent');
grunt.registerTask('merge-ftl', ['copy:branding-ftl', 'concat:ftl']);
grunt.registerTask('watch-ftl', ['watch:ftl']);
grunt.registerTask('watchers', ['concurrent:dev']);
};

Просмотреть файл

@ -11,7 +11,7 @@ import { DEFAULT_LOCALE } from '@fxa/shared/l10n';
import errorIcon from '@fxa/shared/assets/images/error.svg';
import {
SupportedPages,
app,
getApp,
getCartOrRedirectAction,
} from '@fxa/payments/ui/server';
import { CartErrorReasonId } from '@fxa/shared/db/mysql/account';
@ -64,8 +64,8 @@ export default async function CheckoutError({
params.cartId,
SupportedPages.ERROR
);
const l10nPromise = app.getL10n(locale);
const [cart, l10n] = await Promise.all([cartPromise, l10nPromise]);
const l10n = getApp().getL10n(locale);
const [cart] = await Promise.all([cartPromise]);
const errorReason = getErrorReason(cart.errorReasonId);

Просмотреть файл

@ -4,7 +4,7 @@
import { headers } from 'next/headers';
import {
app,
getApp,
fetchContentfulData,
getCartAction,
PurchaseDetails,
@ -48,11 +48,10 @@ export default async function RootLayout({
const locale = headers().get('accept-language') || DEFAULT_LOCALE;
const contentfulDataPromise = fetchContentfulData(params.offeringId, locale);
const cartDataPromise = getCartAction(params.cartId);
const l10nPromise = app.getL10n(locale);
const [contentful, cart, l10n] = await Promise.all([
const l10n = getApp().getL10n(locale);
const [contentful, cart] = await Promise.all([
contentfulDataPromise,
cartDataPromise,
l10nPromise,
]);
return (

Просмотреть файл

@ -6,7 +6,7 @@ import { revalidatePath } from 'next/cache';
import { headers } from 'next/headers';
import { PaymentSection } from '@fxa/payments/ui';
import {
app,
getApp,
getCartOrRedirectAction,
SupportedPages,
} from '@fxa/payments/ui/server';
@ -30,7 +30,7 @@ export default async function Checkout({ params }: { params: CheckoutParams }) {
//);
const locale = headers().get('accept-language') || DEFAULT_LOCALE;
const sessionPromise = auth();
const l10nPromise = app.getL10n(locale);
const l10n = getApp().getL10n(locale);
const cartPromise = getCartOrRedirectAction(
params.cartId,
SupportedPages.START
@ -38,9 +38,8 @@ export default async function Checkout({ params }: { params: CheckoutParams }) {
//TODO - Replace with cartPromise as part of FXA-8903
const fakeCartDataPromise = getFakeCartData(params.cartId);
const cmsPromise = getContentfulContent(params.offeringId, locale);
const [session, l10n, cart, fakeCart, cms] = await Promise.all([
const [session, cart, fakeCart, cms] = await Promise.all([
sessionPromise,
l10nPromise,
cartPromise,
fakeCartDataPromise,
cmsPromise,
@ -96,7 +95,7 @@ export default async function Checkout({ params }: { params: CheckoutParams }) {
'use server';
const email =
formData.get('email')?.toString() || 'test@example.com';
await app.getActionsService().updateCart({
await getApp().getActionsService().updateCart({
cartId: cart.id,
version: cart.version,
cartDetails: {

Просмотреть файл

@ -12,7 +12,7 @@ import { getFakeCartData } from 'apps/payments/next/app/_lib/apiClient';
import circledConfirm from '@fxa/shared/assets/images/circled-confirm.svg';
import {
SupportedPages,
app,
getApp,
fetchContentfulData,
getCartOrRedirectAction,
} from '@fxa/payments/ui/server';
@ -66,12 +66,11 @@ export default async function CheckoutSuccess({
params.cartId,
SupportedPages.SUCCESS
);
const l10nPromise = app.getL10n(locale);
const l10n = getApp().getL10n(locale);
const fakeCartDataPromise = getFakeCartData(params.cartId);
const [contentful, cart, l10n, fakeCart] = await Promise.all([
const [contentful, cart, fakeCart] = await Promise.all([
contentfulDataPromise,
cartDataPromise,
l10nPromise,
fakeCartDataPromise,
]);

Просмотреть файл

@ -1,7 +1,7 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import { app } from '@fxa/payments/ui/server';
import { getApp } from '@fxa/payments/ui/server';
import { headers } from 'next/headers';
import { DEFAULT_LOCALE } from '@fxa/shared/l10n';
import { Providers } from '@fxa/payments/ui';
@ -19,7 +19,7 @@ export default async function RootProviderLayout({
// headers().get('accept-language')
//);
const locale = headers().get('accept-language') || DEFAULT_LOCALE;
const fetchedMessages = app.getFetchedMessages(locale);
const fetchedMessages = getApp().getFetchedMessages(locale);
return (
<Providers

Просмотреть файл

@ -0,0 +1,25 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import { reinitializeNestApp } from '@fxa/payments/ui/server';
import { notFound } from 'next/navigation';
import { NextResponse } from 'next/server';
export const dynamic = 'force-dynamic';
/**
* Provides a route to reinitialize the standalone Nest.js app used by
* Payments Next. This ensures the latest code changes are available
* in the Nest.js App.
*
* NOTE: This route should only be used in a development environment.
*/
export async function GET() {
if (process.env.NODE_ENV === 'development') {
await reinitializeNestApp();
return NextResponse.json({});
} else {
notFound();
}
}

Просмотреть файл

@ -1,7 +1,7 @@
export async function register() {
if (process.env.NEXT_RUNTIME === 'nodejs') {
const { app } = await import('@fxa/payments/ui/server');
const { getApp } = await import('@fxa/payments/ui/server');
await app.initialize();
await getApp().initialize();
}
}

Просмотреть файл

@ -10,7 +10,7 @@ const apps = [];
apps.push({
name: 'payments-next',
script: 'nx serve payments-next',
script: 'nx dev payments-next',
max_restarts: '1',
min_uptime: '2m',
env: {
@ -21,8 +21,8 @@ apps.push({
if (process.env.CI !== 'true') {
apps.push({
name: 'payments-next-ftl',
script: 'nx watch-ftl payments-next',
name: 'payments-next-watchers',
script: 'nx watchers payments-next',
cwd: __dirname,
filter_env: ['npm_'],
max_restarts: '1',

Просмотреть файл

@ -12,31 +12,11 @@
"outputPath": "dist/apps/payments/next",
"postcssConfig": "apps/payments/next/postcss.config.js"
},
"configurations": {
"development": {
"outputPath": "apps/payments/next"
}
},
"dependsOn": ["l10n-bundle"]
},
"serve": {
"executor": "@nx/next:server",
"defaultConfiguration": "development",
"dev": {
"options": {
"buildTarget": "payments-next:build",
"dev": true,
"port": 3035,
"postcssConfig": "apps/payments/next/postcss.config.js"
},
"configurations": {
"development": {
"buildTarget": "payments-next:build:development",
"dev": true
},
"production": {
"buildTarget": "payments-next:build:production",
"dev": false
}
"port": 3035
}
},
"export": {
@ -66,7 +46,8 @@
"command": "pm2 stop apps/payments/next/pm2.config.js"
},
"restart": {
"command": "pm2 restart apps/payments/next/pm2.config.js"
"command": "pm2 restart apps/payments/next/pm2.config.js",
"dependsOn": [""]
},
"delete": {
"command": "pm2 delete apps/payments/next/pm2.config.js"
@ -82,16 +63,13 @@
"dependsOn": ["l10n-merge"],
"command": "./_scripts/l10n/bundle.sh apps/payments/next branding,react,payments"
},
"watch-ftl": {
"command": "yarn grunt --gruntfile='apps/payments/next/Gruntfile.js' watch-ftl"
"watchers": {
"command": "yarn grunt --gruntfile='apps/payments/next/Gruntfile.js' watchers"
},
"l10n-convert": {
"dependsOn": ["l10n-prime"],
"command": "node -r esbuild-register apps/payments/next/app/_lib/scripts/convert.ts"
},
"ztest": {
"command": "node -r esbuild-register apps/payments/next/config/index.ts"
},
"storybook": {
"executor": "@nx/storybook:storybook",
"options": {

Просмотреть файл

@ -6,3 +6,4 @@ export * from './lib/cart.factories';
export * from './lib/cart.manager';
export * from './lib/cart.service';
export * from './lib/cart.utils';
export * from './lib/checkout.service';

Просмотреть файл

@ -5,7 +5,7 @@
'use server';
import { plainToClass } from 'class-transformer';
import { app } from '../nestapp/app';
import { getApp } from '../nestapp/app';
import { CheckoutCartWithPaypalActionArgs } from '../nestapp/validators/CheckoutCartWithPaypalActionArgs';
export const checkoutCartWithPaypal = async (
@ -14,7 +14,7 @@ export const checkoutCartWithPaypal = async (
customerData: { locale: string; displayName: string },
token?: string
) => {
await app.getActionsService().checkoutCartWithPaypal(
await getApp().getActionsService().checkoutCartWithPaypal(
plainToClass(CheckoutCartWithPaypalActionArgs, {
cartId,
version,

Просмотреть файл

@ -5,7 +5,7 @@
'use server';
import { plainToClass } from 'class-transformer';
import { app } from '../nestapp/app';
import { getApp } from '../nestapp/app';
import {
CheckoutCartWithStripeActionArgs,
CheckoutCartWithStripeActionCustomerData,
@ -17,7 +17,7 @@ export const checkoutCartWithStripe = async (
paymentMethodId: string,
customerData: CheckoutCartWithStripeActionCustomerData
) => {
await app.getActionsService().checkoutCartWithStripe(
await getApp().getActionsService().checkoutCartWithStripe(
plainToClass(CheckoutCartWithStripeActionArgs, {
cartId,
version,

Просмотреть файл

@ -5,14 +5,14 @@
'use server';
import { plainToClass } from 'class-transformer';
import { app } from '../nestapp/app';
import { getApp } from '../nestapp/app';
import { FetchContentfulDataArgs } from '../nestapp/validators/FetchContentfulDataArgs';
export const fetchContentfulData = (
offeringId: string,
acceptLanguage: string
) => {
return app.getActionsService().fetchContentfulData(
return getApp().getActionsService().fetchContentfulData(
plainToClass(FetchContentfulDataArgs, {
offeringId,
acceptLanguage,

Просмотреть файл

@ -5,11 +5,11 @@
'use server';
import { plainToClass } from 'class-transformer';
import { app } from '../nestapp/app';
import { getApp } from '../nestapp/app';
import { GetCartActionArgs } from '../nestapp/validators/GetCartActionArgs';
export const getCartAction = async (cartId: string) => {
const cart = await app.getActionsService().getCart(
const cart = await getApp().getActionsService().getCart(
plainToClass(GetCartActionArgs, {
cartId,
})

Просмотреть файл

@ -6,7 +6,7 @@
import { plainToClass } from 'class-transformer';
import { redirect } from 'next/navigation';
import { app } from '../nestapp/app';
import { getApp } from '../nestapp/app';
import { GetCartActionArgs } from '../nestapp/validators/GetCartActionArgs';
import { getRedirect, validateCartState } from '../utils/get-cart';
import { SupportedPages } from '../utils/types';
@ -20,7 +20,7 @@ export const getCartOrRedirectAction = async (
cartId: string,
page: SupportedPages
) => {
const cart = await app.getActionsService().getCart(
const cart = await getApp().getActionsService().getCart(
plainToClass(GetCartActionArgs, {
cartId,
})

Просмотреть файл

@ -5,11 +5,11 @@
'use server';
import { plainToClass } from 'class-transformer';
import { app } from '../nestapp/app';
import { getApp } from '../nestapp/app';
import { GetPayPalCheckoutTokenArgs } from '../nestapp/validators/GetPayPalCheckoutTokenArgs';
export const getPayPalCheckoutToken = async (currencyCode: string) => {
const actionsService = app.getActionsService();
const actionsService = getApp().getActionsService();
const token = await actionsService.getPayPalCheckoutToken(
plainToClass(GetPayPalCheckoutTokenArgs, {

Просмотреть файл

@ -5,7 +5,7 @@
'use server';
import { StripeError } from '@stripe/stripe-js';
import { app } from '../nestapp/app';
import { getApp } from '../nestapp/app';
import { redirect } from 'next/navigation';
import { stripeErrorToErrorReasonId } from '@fxa/payments/cart';
@ -15,7 +15,7 @@ export const handleStripeErrorAction = async (
) => {
const errorReasonId = stripeErrorToErrorReasonId(stripeError);
await app.getActionsService().finalizeCartWithError({
await getApp().getActionsService().finalizeCartWithError({
cartId,
errorReasonId,
});

Просмотреть файл

@ -5,11 +5,11 @@
'use server';
import { plainToClass } from 'class-transformer';
import { app } from '../nestapp/app';
import { getApp } from '../nestapp/app';
import { RestartCartActionArgs } from '../nestapp/validators/RestartCartActionArgs';
export const restartCartAction = async (cartId: string) => {
const actionsService = app.getActionsService();
const actionsService = getApp().getActionsService();
const cart = await actionsService.restartCart(
plainToClass(RestartCartActionArgs, {

Просмотреть файл

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
'use server';
import { app } from '../nestapp/app';
import { getApp } from '../nestapp/app';
import { plainToClass } from 'class-transformer';
import { SetupCartActionArgs } from '../nestapp/validators/SetupCartActionArgs';
@ -15,7 +15,7 @@ export const setupCartAction = async (
uid?: string,
ip?: string
) => {
return app.getActionsService().setupCart(
return getApp().getActionsService().setupCart(
plainToClass(SetupCartActionArgs, {
interval,
offeringConfigId,

Просмотреть файл

@ -5,7 +5,7 @@
'use server';
import { UpdateCart } from '@fxa/payments/cart';
import { app } from '../nestapp/app';
import { getApp } from '../nestapp/app';
import { UpdateCartActionArgs } from '../nestapp/validators/UpdateCartActionArgs';
import { plainToClass } from 'class-transformer';
@ -14,7 +14,7 @@ export const updateCartAction = async (
version: number,
cartDetails: UpdateCart
) => {
const actionsService = await app.getActionsService();
const actionsService = getApp().getActionsService();
await actionsService.updateCart(
plainToClass(UpdateCartActionArgs, {

Просмотреть файл

@ -5,7 +5,7 @@
import { Module } from '@nestjs/common';
import { TypedConfigModule } from 'nest-typed-config';
import { CartManager, CartService } from '@fxa/payments/cart';
import { CartManager, CartService, CheckoutService } from '@fxa/payments/cart';
import {
EligibilityManager,
EligibilityService,
@ -39,7 +39,6 @@ import { StatsDProvider } from '@fxa/shared/metrics/statsd';
import { RootConfig } from './config';
import { NextJSActionsService } from './nextjs-actions.service';
import { validate } from '../config.utils';
import { CheckoutService } from 'libs/payments/cart/src/lib/checkout.service';
import { AccountManager } from '@fxa/shared/account/account';
@Module({

Просмотреть файл

@ -41,4 +41,17 @@ class AppSingleton {
}
}
export const app = singleton('nestApp', new AppSingleton()) as AppSingleton;
export async function reinitializeNestApp() {
return (
singleton('nestApp', new AppSingleton(), true) as AppSingleton
).initialize();
}
/**
* Returns an instance of AppSingleton.
*
* Note, exporting a function instead of a constant value, ensures that
* Next.js pages always fetch the latest AppSingleton
*/
export const getApp = () =>
singleton('nestApp', new AppSingleton()) as AppSingleton;

Просмотреть файл

@ -5,9 +5,12 @@ Permission to use, copy, modify, and/or distribute this software for any purpose
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
export function singleton<T>(name: string, value: T) {
export function singleton<T>(name: string, value: T, refresh = false) {
const yolo = global as any;
yolo.__singletons ??= {};
if (refresh) {
delete yolo.__singletons[name];
}
yolo.__singletons[name] ??= value;
return yolo.__singletons[name];
}

184
nx.json
Просмотреть файл

@ -2,8 +2,14 @@
"$schema": "./node_modules/nx/schemas/nx-schema.json",
"targetDefaults": {
"build": {
"dependsOn": ["prebuild", "^build"],
"inputs": ["production", "^production"],
"dependsOn": [
"prebuild",
"^build"
],
"inputs": [
"production",
"^production"
],
"outputs": [
"{projectRoot}/*.tsbuildinfo",
"{projectRoot}/*/.tmp",
@ -21,41 +27,75 @@
"cache": true
},
"build-storybook": {
"dependsOn": ["build"],
"inputs": ["production", "^production"],
"outputs": ["{projectRoot}/storybook-static"],
"dependsOn": [
"build"
],
"inputs": [
"production",
"^production"
],
"outputs": [
"{projectRoot}/storybook-static"
],
"cache": true
},
"compile": {
"dependsOn": ["^compile"],
"inputs": ["typescript", "^typescript"],
"outputs": ["{projectRoot}/build", "{projectRoot}/dist"],
"dependsOn": [
"^compile"
],
"inputs": [
"typescript",
"^typescript"
],
"outputs": [
"{projectRoot}/build",
"{projectRoot}/dist"
],
"cache": true
},
"gql-copy": {
"dependsOn": [
{
"projects": ["fxa-settings", "fxa-admin-panel"],
"projects": [
"fxa-settings",
"fxa-admin-panel"
],
"target": "gql-extract"
}
],
"inputs": ["typescript", "^typescript"],
"outputs": ["{projectRoot}/src/config/gql/allowlist"],
"inputs": [
"typescript",
"^typescript"
],
"outputs": [
"{projectRoot}/src/config/gql/allowlist"
],
"cache": true
},
"gql-extract": {
"dependsOn": [],
"inputs": ["typescript"],
"outputs": ["{workspaceRoot}/configs/gql/allowlist"],
"inputs": [
"typescript"
],
"outputs": [
"{workspaceRoot}/configs/gql/allowlist"
],
"cache": true
},
"lint": {
"inputs": ["lint", "{workspaceRoot}/.eslintrc.json"],
"outputs": ["{projectRoot}/.eslintcache"],
"inputs": [
"lint",
"{workspaceRoot}/.eslintrc.json"
],
"outputs": [
"{projectRoot}/.eslintcache"
],
"cache": true
},
"prebuild": {
"dependsOn": ["gql-copy"],
"dependsOn": [
"gql-copy"
],
"inputs": [],
"outputs": [
"{projectRoot}/public/locales",
@ -69,23 +109,50 @@
"cache": true
},
"restart": {
"dependsOn": ["build", "^restart"],
"inputs": ["production", "^production"],
"dependsOn": [
"build",
"^restart"
],
"inputs": [
"production",
"^production"
],
"outputs": []
},
"start": {
"dependsOn": ["build", "gen-keys", "^start"],
"inputs": ["production", "^production"],
"dependsOn": [
"build",
"gen-keys",
"^start"
],
"inputs": [
"production",
"^production"
],
"outputs": []
},
"storybook": {
"dependsOn": ["build"],
"inputs": ["production", "^production"],
"outputs": ["{projectRoot}/storybook-static"]
"dependsOn": [
"build"
],
"inputs": [
"production",
"^production"
],
"outputs": [
"{projectRoot}/storybook-static"
]
},
"test": {
"inputs": ["production", "^production"],
"dependsOn": ["test-unit", "test-integration", "test-e2e"],
"inputs": [
"production",
"^production"
],
"dependsOn": [
"test-unit",
"test-integration",
"test-e2e"
],
"outputs": [
"{projectRoot}/coverage",
"{projectRoot}/.nyc_output",
@ -94,8 +161,13 @@
"cache": true
},
"test-e2e": {
"dependsOn": ["build"],
"inputs": ["production", "^production"],
"dependsOn": [
"build"
],
"inputs": [
"production",
"^production"
],
"outputs": [
"{projectRoot}/coverage",
"{projectRoot}/.nyc_output",
@ -103,8 +175,14 @@
]
},
"test-integration": {
"dependsOn": ["build", "gen-keys"],
"inputs": ["test", "^test"],
"dependsOn": [
"build",
"gen-keys"
],
"inputs": [
"test",
"^test"
],
"outputs": [
"{projectRoot}/coverage",
"{projectRoot}/.nyc_output",
@ -114,8 +192,14 @@
"cache": true
},
"test-unit": {
"dependsOn": ["build", "gen-keys"],
"inputs": ["test", "^test"],
"dependsOn": [
"build",
"gen-keys"
],
"inputs": [
"test",
"^test"
],
"outputs": [
"{projectRoot}/coverage",
"{projectRoot}/.nyc_output",
@ -125,7 +209,11 @@
},
"@nx/jest:jest": {
"cache": true,
"inputs": ["default", "^production", "{workspaceRoot}/jest.preset.js"],
"inputs": [
"default",
"^production",
"{workspaceRoot}/jest.preset.js"
],
"options": {
"passWithNoTests": true
},
@ -138,13 +226,23 @@
},
"@nx/js:tsc": {
"cache": true,
"dependsOn": ["^build"],
"inputs": ["production", "^production"]
"dependsOn": [
"^build"
],
"inputs": [
"production",
"^production"
]
}
},
"namedInputs": {
"default": ["{projectRoot}/**/*.*", "sharedGlobals"],
"lint": ["{projectRoot}/**/*.@(js|jsx|ts|tsx)"],
"default": [
"{projectRoot}/**/*.*",
"sharedGlobals"
],
"lint": [
"{projectRoot}/**/*.@(js|jsx|ts|tsx)"
],
"production": [
"default",
"{workspaceRoot}/external/l10n/**/*.@(ftl|po)",
@ -166,7 +264,10 @@
"runtime": "tsc -v"
}
],
"test": ["default", "{workspaceRoot}/jest.preset.js"],
"test": [
"default",
"{workspaceRoot}/jest.preset.js"
],
"typescript": [
"{projectRoot}/**/*.@(ts|tsx)",
"{projectRoot}/package.json",
@ -204,6 +305,15 @@
"options": {
"targetName": "lint"
}
},
{
"plugin": "@nx/next/plugin",
"options": {
"startTargetName": "next:start",
"buildTargetName": "build",
"devTargetName": "dev",
"serveStaticTargetName": "serve-static"
}
}
]
}

Просмотреть файл

@ -212,9 +212,11 @@
"eslint-plugin-react-hooks": "4.6.0",
"grunt": "^1.6.1",
"grunt-cli": "^1.4.3",
"grunt-concurrent": "^3.0.0",
"grunt-contrib-concat": "^2.1.0",
"grunt-contrib-copy": "^1.0.0",
"grunt-contrib-watch": "^1.1.0",
"grunt-http": "^2.3.3",
"jest": "^29.5.0",
"jest-environment-jsdom": "^29.5.0",
"jest-environment-node": "^29.7.0",

237
yarn.lock
Просмотреть файл

@ -9925,6 +9925,15 @@ __metadata:
languageName: node
linkType: hard
"@hapi/b64@npm:4.x.x":
version: 4.2.1
resolution: "@hapi/b64@npm:4.2.1"
dependencies:
"@hapi/hoek": 8.x.x
checksum: 1220d67adbbb22cbd1b4dd1b2fce027e973721cddfab1a5bf26d1523c6e0995fb0f7e76bd35aa90079bbd5d373a4fc151f0d4023ae19b791cff450267b00690f
languageName: node
linkType: hard
"@hapi/b64@npm:5.x.x":
version: 5.0.0
resolution: "@hapi/b64@npm:5.0.0"
@ -9934,6 +9943,15 @@ __metadata:
languageName: node
linkType: hard
"@hapi/boom@npm:7.x.x":
version: 7.4.11
resolution: "@hapi/boom@npm:7.4.11"
dependencies:
"@hapi/hoek": 8.x.x
checksum: ef166e1a764a9574b084d258a315d83fd61070b3ba0f5c16a2a5d716fe3f3fc068ce3c600bcb8bf62c0bad0ab01ba505b81ff93e806091d17e17571bcb76c6d3
languageName: node
linkType: hard
"@hapi/boom@npm:9.x.x, @hapi/boom@npm:^9.0.0, @hapi/boom@npm:^9.1.0":
version: 9.1.4
resolution: "@hapi/boom@npm:9.1.4"
@ -9961,6 +9979,16 @@ __metadata:
languageName: node
linkType: hard
"@hapi/bounce@npm:1.x.x":
version: 1.3.2
resolution: "@hapi/bounce@npm:1.3.2"
dependencies:
"@hapi/boom": 7.x.x
"@hapi/hoek": ^8.3.1
checksum: 68ea13fafd762d29f4815e71a0dd928f98b1a3dd7808aacd1244a838b01d1713fd7dfaa68f4d2dcd48e2576c3cd508f1ddd4a7aa8991b7262bcae498c58a2ad0
languageName: node
linkType: hard
"@hapi/bounce@npm:2.x.x, @hapi/bounce@npm:^2.0.0":
version: 2.0.0
resolution: "@hapi/bounce@npm:2.0.0"
@ -10040,6 +10068,15 @@ __metadata:
languageName: node
linkType: hard
"@hapi/cryptiles@npm:4.x.x":
version: 4.2.1
resolution: "@hapi/cryptiles@npm:4.2.1"
dependencies:
"@hapi/boom": 7.x.x
checksum: 4d4941d7dd78ac47fe8f3e39abbf5064ca7cb099662fe3dbc7818a78053962349138ca8adb78f34b5f9d1d9fc920564e8fca3f4e7c1b83ed74d4277251feeb9c
languageName: node
linkType: hard
"@hapi/cryptiles@npm:5.x.x":
version: 5.0.0
resolution: "@hapi/cryptiles@npm:5.0.0"
@ -10100,6 +10137,13 @@ __metadata:
languageName: node
linkType: hard
"@hapi/hoek@npm:8.x.x, @hapi/hoek@npm:^8.3.1":
version: 8.5.1
resolution: "@hapi/hoek@npm:8.5.1"
checksum: 8f8ce36be4f5e5d7a712072d4a028a4a95beec7a7da3a7a6e9a0c07d697f04c19b37d93751db352c314ea831f7e2120569a035dc6a351ed8c0444f1d3b275621
languageName: node
linkType: hard
"@hapi/hoek@npm:9.x.x, @hapi/hoek@npm:^9.0.0, @hapi/hoek@npm:^9.0.4":
version: 9.2.0
resolution: "@hapi/hoek@npm:9.2.0"
@ -10206,6 +10250,18 @@ __metadata:
languageName: node
linkType: hard
"@hapi/sntp@npm:3.x.x":
version: 3.1.2
resolution: "@hapi/sntp@npm:3.1.2"
dependencies:
"@hapi/boom": 7.x.x
"@hapi/bounce": 1.x.x
"@hapi/hoek": 8.x.x
"@hapi/teamwork": 3.x.x
checksum: 531d1cd05de9e51ab97f36071932bedc4c1d6a7258281b88b82e7fe7b5e04289099f3e568e1e69fe85db969ce8cde06a9176bf1ce00dba843825550102ea55a4
languageName: node
linkType: hard
"@hapi/somever@npm:^3.0.0":
version: 3.0.1
resolution: "@hapi/somever@npm:3.0.1"
@ -10246,6 +10302,13 @@ __metadata:
languageName: node
linkType: hard
"@hapi/teamwork@npm:3.x.x":
version: 3.3.1
resolution: "@hapi/teamwork@npm:3.3.1"
checksum: 1cc2ab5b0c2b192e2432adfeeb1194b354503d92acd9e0104d6e6d00ecfe6c4002f11ee23fec077fb812f419cb0fb75b830eca12f1269dda226e3d246a58f623
languageName: node
linkType: hard
"@hapi/teamwork@npm:5.x.x, @hapi/teamwork@npm:^5.1.0":
version: 5.1.0
resolution: "@hapi/teamwork@npm:5.1.0"
@ -25998,6 +26061,13 @@ __metadata:
languageName: node
linkType: hard
"async@npm:^3.1.0":
version: 3.2.5
resolution: "async@npm:3.2.5"
checksum: 5ec77f1312301dee02d62140a6b1f7ee0edd2a0f983b6fd2b0849b969f245225b990b47b8243e7b9ad16451a53e7f68e753700385b706198ced888beedba3af4
languageName: node
linkType: hard
"async@npm:^3.2.3":
version: 3.2.3
resolution: "async@npm:3.2.3"
@ -26167,7 +26237,7 @@ __metadata:
languageName: node
linkType: hard
"aws-sign2@npm:~0.7.0":
"aws-sign2@npm:^0.7.0, aws-sign2@npm:~0.7.0":
version: 0.7.0
resolution: "aws-sign2@npm:0.7.0"
checksum: b148b0bb0778098ad8cf7e5fc619768bcb51236707ca1d3e5b49e41b171166d8be9fdc2ea2ae43d7decf02989d0aaa3a9c4caa6f320af95d684de9b548a71525
@ -37219,7 +37289,7 @@ __metadata:
languageName: node
linkType: hard
"form-data@npm:^2.5.0":
"form-data@npm:^2.3.2, form-data@npm:^2.5.0":
version: 2.5.1
resolution: "form-data@npm:2.5.1"
dependencies:
@ -38861,9 +38931,11 @@ fsevents@~2.1.1:
graphql-request: ^6.1.0
grunt: ^1.6.1
grunt-cli: ^1.4.3
grunt-concurrent: ^3.0.0
grunt-contrib-concat: ^2.1.0
grunt-contrib-copy: ^1.0.0
grunt-contrib-watch: ^1.1.0
grunt-http: ^2.3.3
hot-shots: ^10.0.0
husky: ^4.2.5
jest: ^29.5.0
@ -40191,6 +40263,20 @@ fsevents@~2.1.1:
languageName: node
linkType: hard
"grunt-concurrent@npm:^3.0.0":
version: 3.0.0
resolution: "grunt-concurrent@npm:3.0.0"
dependencies:
arrify: ^2.0.1
async: ^3.1.0
indent-string: ^4.0.0
pad-stream: ^2.0.0
peerDependencies:
grunt: ">=1"
checksum: 898c55bcf09382ac4a6cb6a431ec7dd6d685f87cff1ca2a51fd6e8363fc54d55bbd385586c369205dc73082eb61a378f0dfd9241a89e0978197cd1c87475e6bc
languageName: node
linkType: hard
"grunt-contrib-clean@npm:^2.0.1":
version: 2.0.1
resolution: "grunt-contrib-clean@npm:2.0.1"
@ -40298,6 +40384,40 @@ fsevents@~2.1.1:
languageName: node
linkType: hard
"grunt-http@npm:^2.3.3":
version: 2.3.3
resolution: "grunt-http@npm:2.3.3"
dependencies:
async: ^3.1.0
aws-sign2: ^0.7.0
form-data: ^2.3.2
hawk: ^7.0.9
http-signature: ^1.2.0
oauth-sign: ^0.9.0
request: ^2.88.0
tough-cookie: ^3.0.0
tunnel-agent: ^0.6.0
peerDependencies:
grunt: ">=0.4.0"
dependenciesMeta:
aws-sign2:
optional: true
form-data:
optional: true
hawk:
optional: true
http-signature:
optional: true
oauth-sign:
optional: true
tough-cookie:
optional: true
tunnel-agent:
optional: true
checksum: 7bc697b8891d7b40969b5f84a6aa25fdc9c2e8c9249e7b4ffcc0347bb9440aec53691f0efeefc33616e3cdd75bdaf5bbe88a30bc704aba82c9999013c5adb0bc
languageName: node
linkType: hard
"grunt-jsonlint@npm:2.1.3":
version: 2.1.3
resolution: "grunt-jsonlint@npm:2.1.3"
@ -40930,6 +41050,19 @@ fsevents@~2.1.1:
languageName: node
linkType: hard
"hawk@npm:^7.0.9":
version: 7.1.2
resolution: "hawk@npm:7.1.2"
dependencies:
"@hapi/b64": 4.x.x
"@hapi/boom": 7.x.x
"@hapi/cryptiles": 4.x.x
"@hapi/hoek": 8.x.x
"@hapi/sntp": 3.x.x
checksum: bdfe0ebe40ff2f0d9cf902c4d10efdc8cdc2f4cca7213fe1b8008c29e647bff34aae5ba1de25f480630f5b6b3a502a992f3714a648414dbae32d2d1689bb9b3c
languageName: node
linkType: hard
"he@npm:1.2.0, he@npm:^1.2.0":
version: 1.2.0
resolution: "he@npm:1.2.0"
@ -41568,6 +41701,17 @@ fsevents@~2.1.1:
languageName: node
linkType: hard
"http-signature@npm:^1.2.0":
version: 1.4.0
resolution: "http-signature@npm:1.4.0"
dependencies:
assert-plus: ^1.0.0
jsprim: ^2.0.2
sshpk: ^1.18.0
checksum: f07f4cc0481e4461c68b9b7d1a25bf2ec4cef8e0061812b989c1e64f504b4b11f75f88022102aea05d25d47a87789599f1a310b1f8a56945a50c93e54c7ee076
languageName: node
linkType: hard
"http-signature@npm:~1.2.0":
version: 1.2.0
resolution: "http-signature@npm:1.2.0"
@ -42482,6 +42626,13 @@ fsevents@~2.1.1:
languageName: node
linkType: hard
"ip-regex@npm:^2.1.0":
version: 2.1.0
resolution: "ip-regex@npm:2.1.0"
checksum: 331d95052aa53ce245745ea0fc3a6a1e2e3c8d6da65fa8ea52bf73768c1b22a9ac50629d1d2b08c04e7b3ac4c21b536693c149ce2c2615ee4796030e5b3e3cba
languageName: node
linkType: hard
"ip-reputation-js-client@npm:^6.0.4":
version: 6.0.4
resolution: "ip-reputation-js-client@npm:6.0.4"
@ -46349,7 +46500,7 @@ fsevents@~2.1.1:
languageName: node
linkType: hard
"json-schema@npm:^0.4.0":
"json-schema@npm:0.4.0, json-schema@npm:^0.4.0":
version: 0.4.0
resolution: "json-schema@npm:0.4.0"
checksum: 66389434c3469e698da0df2e7ac5a3281bcff75e797a5c127db7c5b56270e01ae13d9afa3c03344f76e32e81678337a8c912bdbb75101c62e487dc3778461d72
@ -46568,6 +46719,18 @@ fsevents@~2.1.1:
languageName: node
linkType: hard
"jsprim@npm:^2.0.2":
version: 2.0.2
resolution: "jsprim@npm:2.0.2"
dependencies:
assert-plus: 1.0.0
extsprintf: 1.3.0
json-schema: 0.4.0
verror: 1.10.0
checksum: d175f6b1991e160cb0aa39bc857da780e035611986b5492f32395411879fdaf4e513d98677f08f7352dac93a16b66b8361c674b86a3fa406e2e7af6b26321838
languageName: node
linkType: hard
"jsqr@npm:1.4.0, jsqr@npm:^1.4.0":
version: 1.4.0
resolution: "jsqr@npm:1.4.0"
@ -51446,6 +51609,13 @@ fsevents@~2.1.1:
languageName: node
linkType: hard
"oauth-sign@npm:^0.9.0, oauth-sign@npm:~0.9.0":
version: 0.9.0
resolution: "oauth-sign@npm:0.9.0"
checksum: 8f5497a127967866a3c67094c21efd295e46013a94e6e828573c62220e9af568cc1d2d04b16865ba583e430510fa168baf821ea78f355146d8ed7e350fc44c64
languageName: node
linkType: hard
"oauth-sign@npm:~0.8.2":
version: 0.8.2
resolution: "oauth-sign@npm:0.8.2"
@ -51453,13 +51623,6 @@ fsevents@~2.1.1:
languageName: node
linkType: hard
"oauth-sign@npm:~0.9.0":
version: 0.9.0
resolution: "oauth-sign@npm:0.9.0"
checksum: 8f5497a127967866a3c67094c21efd295e46013a94e6e828573c62220e9af568cc1d2d04b16865ba583e430510fa168baf821ea78f355146d8ed7e350fc44c64
languageName: node
linkType: hard
"oauth4webapi@npm:^2.4.0":
version: 2.10.3
resolution: "oauth4webapi@npm:2.10.3"
@ -52329,6 +52492,17 @@ fsevents@~2.1.1:
languageName: node
linkType: hard
"pad-stream@npm:^2.0.0":
version: 2.0.0
resolution: "pad-stream@npm:2.0.0"
dependencies:
pumpify: ^1.3.3
split2: ^2.1.1
through2: ^2.0.0
checksum: 19ddb1a6391462e23c473ff42b73b1c38fea95d42f0d9b76f39f5aa198a97fa9057519d769e16157f06df6646133807cdedb3fe04868e2cd50523db92f9bbb13
languageName: node
linkType: hard
"pako@npm:^0.2.5, pako@npm:~0.2.0":
version: 0.2.9
resolution: "pako@npm:0.2.9"
@ -60277,6 +60451,15 @@ resolve@1.1.7:
languageName: node
linkType: hard
"split2@npm:^2.1.1":
version: 2.2.0
resolution: "split2@npm:2.2.0"
dependencies:
through2: ^2.0.2
checksum: 06a9fe364f1c37098539e8d9b460c8c2e00320600a5e040dd3fa006f6bdabbbe6ee983568a62a585f41862c61b9672f59b93ef0accf4add346c716edcd6d21b7
languageName: node
linkType: hard
"split@npm:0.3":
version: 0.3.3
resolution: "split@npm:0.3.3"
@ -60349,6 +60532,27 @@ resolve@1.1.7:
languageName: node
linkType: hard
"sshpk@npm:^1.18.0":
version: 1.18.0
resolution: "sshpk@npm:1.18.0"
dependencies:
asn1: ~0.2.3
assert-plus: ^1.0.0
bcrypt-pbkdf: ^1.0.0
dashdash: ^1.12.0
ecc-jsbn: ~0.1.1
getpass: ^0.1.1
jsbn: ~0.1.0
safer-buffer: ^2.0.2
tweetnacl: ~0.14.0
bin:
sshpk-conv: bin/sshpk-conv
sshpk-sign: bin/sshpk-sign
sshpk-verify: bin/sshpk-verify
checksum: 01d43374eee3a7e37b3b82fdbecd5518cbb2e47ccbed27d2ae30f9753f22bd6ffad31225cb8ef013bc3fb7785e686cea619203ee1439a228f965558c367c3cfa
languageName: node
linkType: hard
"sshpk@npm:^1.7.0":
version: 1.16.1
resolution: "sshpk@npm:1.16.1"
@ -62540,7 +62744,7 @@ resolve@1.1.7:
languageName: node
linkType: hard
"through2@npm:^2.0.0, through2@npm:^2.0.3, through2@npm:~2.0.0":
"through2@npm:^2.0.0, through2@npm:^2.0.2, through2@npm:^2.0.3, through2@npm:~2.0.0":
version: 2.0.5
resolution: "through2@npm:2.0.5"
dependencies:
@ -62867,6 +63071,17 @@ resolve@1.1.7:
languageName: node
linkType: hard
"tough-cookie@npm:^3.0.0":
version: 3.0.1
resolution: "tough-cookie@npm:3.0.1"
dependencies:
ip-regex: ^2.1.0
psl: ^1.1.28
punycode: ^2.1.1
checksum: 796f6239bce5674a1267b19f41972a2602a2a23715817237b5922b0dc2343512512eea7d41d29210a4ec545f8ef32173bbbf01277dd8ec3ae3841b19cbe69f67
languageName: node
linkType: hard
"tough-cookie@npm:^4.0.0":
version: 4.0.0
resolution: "tough-cookie@npm:4.0.0"