npm package: @nsmp/js-api
Description: Types for jsApi
Author: vsklyar@naumen.ru
Latest version: 3.2.2

# jsApi

Пакет предназначен для разработчиков встроенных приложений и предоставляет инструменты для улучшения и оптимизации процесса разработки. Пакет предоставляет:
- Функцию `initializeJsApi` для подключения `jsApi` в разных режимах (`development`, `test`, `production`);
	- Данная функция содержит проксирование, которое в зависимости от окружения создает объект `jsApi`.
		- Каждое окружение содержит объект `jsApi` со своим интерфейсом, который преобразовывается в объект с общим интерфейсом, что позволяет при разработке ВП использовать единый интерфейс вне зависимости от окружения.
			- На текущий момент существует **3 окружения**:
				- `smp4` - ВП встраивается в страницу устаревшего `GWT`-интерфейса внутрь `iframe`,
				- `uiFrame` - ВП встраивается в страницу нового `react`-интерфейса внутрь `iframe`,
				- `ui` - ВП встраивается в страницу нового `react`-интерфейса как `microfrontend`.
		- Функция принимает объект настроек, который содержит свойства:
			- `mock` - для определения `mock` объекта `jsApi` для локальной разработки;
			- `getContext` - функция возвращает контекст приложения, если оно выведено как `microfrontend`;
- Функцию `getJsApi` для получения созданного объекта `jsApi`, если он создан, иначе выбрасывает ошибку, что объект не создан;
- Функцию `removeJsApi` для удаления созданного объекта `jsApi`;
- Хук `useInitializeJsApi` для подключения `jsApi` в `microfrontend` приложении, использующем `react`. При размонтировании приложения хук вызывает функцию `removeJsApi`. Это необходимо, чтобы удалился `singleton`-объект `jsApi` для экономии памяти и смены контекста.
- Файлы декларации типов `.d.ts` для `jsApi` при работе с `TypeScript`;
- `Mock`-функции `jsApi` для локальной разработки, а также инструменты для их переопределения и уточнения;
- Инструменты для локальной разработки с проксированием запросов к приложению.

## Содержание

- [Быстрый старт](#быстрый-старт)
- [Инициализация и использование `jsApi`](#инициализация-и-использование-jsApi)
- [Типизация `jsApi`](#типизация-jsapi)
- [Переопределение методов `jsApi`](#переопределение-методов-jsapi)
- [Конфигурации](#конфигурации)
- [Настройка проксирования запросов](#настройка-проксирования-запросов)

## Быстрый старт

1. Установить пакет: ```npm i @nsmp/js-api```
2. Инициализировать `jsApi` в проекте - [Инициализация и использование jsApi](#инициализация-и-использование-jsApi)
3. В режиме разработки (при необходимости):
	1. переопределить стандартные методы `jsApi` - [Переопределение методов jsApi](#переопределение-методов-jsapi)
	2. настроить проксирование запросов - [Настройка проксирования запросов](#настройка-проксирования-запросов)

[К содержанию](#содержание)

## Инициализация и использование `jsApi`

Инициализация `jsApi` зависит от того, в каком окружении будет использоваться ВП.

### ВП будет использоваться во всех трех окружениях

Пример для ВП с использованием контекста:

Необходимо в корневом компоненте, обычно это `App`, вызвать хук `useInitializeJsApi` и передать в него контекст приложения.
Приложение как `microfrontend` - это `react`-компонент, который принимает контекст через `props` компонента.

Импорт хука и функций для работы с `jsApi` для `react` приложений осуществляется по пути `@nsmp/js-api/react`.

```tsx
import {JsApiComponentProps, useInitializeJsApi, getJsApi} from '@nsmp/js-api/react';

const EmbeddedApp = () => {
	const jsApi = getJsApi();

	return (
		<div>{jsApi.findApplicationCode()}</div>
	)
};

const App = (props: JsApiComponentProps) => {
	const {context} = props;

	useInitializeJsApi({
		getContext: () => context
	});

	return (
		<EmbeddedApp />
	);
};
```

Пример использования с расширением методов `jsApi` через моки:

```tsx
import {deepMergeJsApi, getJsApi, IJsApi, JsApiComponentProps, jsApiMock, useInitializeJsApi} from '@nsmp/js-api/react';
// @nsmp/partial-js-api - используется в качестве примера, в реальности пакет не существует
import {IPartialJsApi, partialApiMock} from '@nsmp/partial-js-api';

const EmbeddedApp = () => {
	const jsApi = getJsApi<IJsApi & IPartialJsApi>();

	return (
		<div>{jsApi.findApplicationCode()}</div>
	)
};

const App = (props?: JsApiComponentProps) => {
	useInitializeJsApi<IPartialJsApi>({
		// В режиме разработки методы из переменной `partialApiMock` переопределят методы `jsApi`, которые есть в библиотеке
		mock: process.env.NODE_ENV !== 'production' ? partialApiMock : {}
	});

	return (
		<EmbeddedApp />
	);
};
```
Объект `mock` это объект с таким же интерфейсом, как и объект `jsApi` в режиме `production`, но у него своя реализация методов под нужды разработки.


### ВП будет использоваться в окружениях `uiFrame` и `smp4`

Пример:

```typescript
import {initializeJsApi, getJsApi} from '@nsmp/js-api';

// В режиме `development` будут использоваться методы `jsApi`, которые определены в пакете @nsmp/js-api с помощью `mock` объекта
// Контекст берется автоматически из окружения, можно не передавать
initializeJsApi();

const jsApi = getJsApi();
jsApi.findContentCode();
```

Или

```typescript
import {initializeJsApi, IJsApi} from '@nsmp/js-api';
import {IMockApi, mockJsApi} from 'mocks/mockJsApi';

// В режиме `development` методы из переменной mockJsApi переопределят методы `jsApi`, которые есть в библиотеке
initializeJsApi<IMockApi>({mock: mockJsApi});

const jsApi = getJsApi<IJsApi & IMockApi>();
jsApi.findContentCode();
```

[К содержанию](#содержание)

## Типизация `jsApi`

Пакет предоставляет типы для методов `jsApi` в TypeScript:
- `JsApi` - включает все методы `jsApi`. Требует указания всех методов пакета, заменяя их новыми
- `PartialJsApi` - также включает все методы, но позволяет переопределять только необходимые, оставляя остальные без изменений

### Типизация собственных моков

Если нужно создать моки с частичным переопределением функций, а затем передать их в `initializeJsApi`, следует использовать тип `PartialJsApi` при создании моков.

Пример:

```typescript
import {PartialJsApi} from '@nsmp/js-api';

const mock: PartialJsApi = {
	extractSubjectUuid () {
		return 'subjectUuid$123';
	},
	findContentCode () {
		return 'contentCode';
	},
}

initializeJsApi(mock)
```

[К содержанию](#содержание)

## Переопределение методов `jsApi`

В пакете есть возможность переопределить стандартные методы. Для этого необходимо создать переменную в проекте, которая будет хранить новые значения методов `jsApi`, например:

```typescript
import {PartialJsApi} from '@nsmp/js-api';

// При работе с TypeScript указан тип PartialJsApi
const mockJsApi: PartialJsApi = {
	extractSubjectUuid () {
		return 'subjectUuid$123';
	},
	findContentCode () {
		return 'contentCode';
	},
	getCurrentUser () {
		return {
			uuid: 'user$123'
		};
	},
	urls: {
		objectCard: (uuid: string) => `/${uuid}`
	}
};
```

При работе с TypeScript переменной с моковыми методами `jsApi` можно указать тип PartialJsApi из данного пакета - [Типизация `jsApi`](#типизация-jsapi)

[К содержанию](#содержание)

## Конфигурации

### typescript для работы ВП

Для корректной работы `react` приложений необходимо в файле `tsconfig.json` добавить настройку:

```json
{
	"compilerOptions": {
		"moduleResolution": "bundler"
	}
}
```

Это необходимо, чтобы путь `@nsmp/js-api/react` воспринимался компилятором

### typescript для jest

`moduleResolution: "bundler"` нельзя использовать с ts-jest — он не поддерживает этот режим. Для корректной работы `jest` необходимо создать новый файл конфигурации, например, `tsconfig.test.json`. В файле `tsconfig.test.json` добавить настройку:

```json
{
	"compilerOptions": {
		"moduleResolution": "node"
	}
}
```

### jest

Хук `useInitializeJsApi` использует подключение `react`. Т.к. `react` не устанавливается в этом пакете, то он использует пакет из внешнего приложения.
При работе с `jest` может возникать ошибка, что в этом пакете нет `react`.

Для исправления проблемы нужно добавить настройку в файл `jest.confing.js`:

```javascript
module.exports = {
	moduleNameMapper: {
		'^react$': require.resolve('react')
	}
}
```

Для подключения нового файла конфигурации, например, `tsconfig.test.json` нужно добавить настройку в файл `jest.confing.js`:

```javascript
module.exports = {
	transform: {
		'^.+\\.[t|j]sx?$': ['ts-jest', {
			tsconfig: 'tsconfig.jest.json'
		}]
	}
}
```

[К содержанию](#содержание)

## Настройка проксирования запросов

Пакет имеет возможность проксирования запросов в режиме локальной разработки приложения. Чтобы проксирование запросов работало правильно, необходимо создать файл `dev.env` и определить переменные окружения:
- `ACCESS_KEY` - ключ доступа для REST запросов
- `APP_URL` - URL стенда
- `APP_CODE` - код ВП
- `CONTENT_CODE` - код контента, на котором выведено ВП
- `REST_PATH` - путь REST запроса (rest или earest)
- `SUBJECT_UUID` - идентификатор объекта, на котором выведено ВП
- `USER_LOGIN` - логин пользователя
- `USER_UUID` - идентификатор пользователя

Пример:

```dotenv
ACCESS_KEY=your-access-key
APP_URL=https://your-domain.ru/
APP_CODE=app_code
CONTENT_CODE=content_code
REST_PATH=earest
SUBJECT_UUID=subject$123
USER_LOGIN=your_login
USER_UUID=user$123
```

Также необходимо изменить строку запуска dev режима приложения в файле package.json, например:
- Было - `"dev": "cross-env NODE_ENV=development webpack serve --mode=development --config ./webpack/config.js"`
- Стало - `"dev": "cross-env NODE_ENV=development start-webpack-server --mode=development --config ./webpack/config.js --env ./dev.env"`

`config` и `env` являются опциональными аргументами командной строки. Если они не указаны по стандарту будут браться пути:
- `config` - `./webpack/config.js`
- `env` - `./dev.env`

Команда `start-webpack-server` запускает webpack server с добавленным в него проксированием запросов и всеми переменными окружения из файла `dev.env`

[К содержанию](#содержание)