Skip to main content

module-loader-server

Загрузчик модулей для серверного окружения. Загружает файл по урлу, компилирует и выполняет модуль с помощью модуля vm, кэширует результат.

Установка

Для yarn:

yarn add @tinkoff/module-loader-server

Для npm:

npm install @tinkoff/module-loader-server

Подключение и использование

import { ServerLoader } from '@tinkoff/module-loader-server';

const loader = new ServerLoader();

loader.resolveByUrl('https://cdn.example.com/js/module.js').then((moduleExports) => {
// ...
});

В конструктор можно передать опции (см. интерфейс LoaderDeps), из важных это параметр request который по умолчанию определяется библиотекой request, и содержит только deduplicate плагин.

Если вам нужны другие плагины или поведение при запросе на сервер, переопределите параметр request в конструкторе лоадера.

Взаимодействие с кешем

Допустим, вам нужно синхронное апи для получения объекта, при условии, что он есть в кэше. Тогда есть два способа.

Первый, используем метод loadByUrl<R>(url: string, options: LoadOptions), который возвращает Promise<R> если объекта нет в кеше, и R если он там есть:

const result = loader.loadByUrl(url);
if (!isPromise(result)) {
syncOperation(result);
} else {
asyncOperation(result);
}

Второй, можно использовать метод getByUrl<R = any>(url: string, options: LoadOptions = {}): R | void, который возвращает объект, только если он присутствует в кеше:

const result = loader.getByUrl(url);
if (result !== void 0) {
syncOperation(result);
} else {
asyncOperation(loader.resolveByUrl(url));
}

Особенности

  • Circuit Breaker для запросов. На каждый урл будет создан отдельный Circuit Breaker, который будет отслеживать падения именно этого JS файла. Это нужно для быстрой отдачи ошибки вместо завершения запроса по таймаутам, что может ухудшить общее время ответа приложения.

Интерфейс и типы

type LogFunction = (...args: any[]) => void;

export interface Logger {
trace: LogFunction;
debug: LogFunction;
info: LogFunction;
warn: LogFunction;
error: LogFunction;
}

export type RequestFunc = (options: {
url: string;
responseType?: string;
timeout?: number;
}) => Promise<any>;

export interface Cache {
get: (key: string) => any;
set: (key: string, module: any) => void;
}

export interface RequestOptions {
circuitBreakerEnabled?: boolean;
}

export interface LoaderDeps {
log?: Logger;
request?: RequestFunc;
cache?: Cache;
externals?: Record<string, any>;
debug?: string[];
timeout?: number;
requestOptions?: RequestOptions;
}

export interface LoadOptions {
[key: string]: any;
// тип ресурса, по умолчанию 'module', используется при логировании
kind?: string;
// если задан, будет использоваться после kind при логировании
displayName?: string;
// если задан, ресурс будет проверен на соответствие заданному хешу
integrity?: string;

codePrefix?: string;

type?: 'js' | 'json';
}