Один из способов ускорения компиляции TypeScript
Исходные данные: компиляция NodeJS проекта съедает почти 2Гб памяти. На рабочем компьюторе это меня не беспокоило, но на ноутбуке периодически возникал неприятный OutOfMemory.
Я начал исследовать, зачем довольно небольшой проект так много жрет? Довольно быстро в гугле я нашел неизвестную мне ранее опцию компилятора tsc --listFiles
, которая выводит список используемых файлов: их оказалось 4500! Слишком много. Беглый просмотр списка показал, что в основном используются файлы из библиотек: googleapis
, @hubspot
, @redis-client
. Я сохранил список в файл npx tsc --listFiles > .files.ls
и начал измерения:
cat .files.ls | grep redis | wc -l 491
cat .files.ls | grep google | wc -l 907
cat .files.ls | grep hubspot | wc -l 1682
Hubspot
Библиотека предоставляет методы к апи этого сервиса. Методов очень много, а используем из них всего 6.
Создаю файл hubspot.js с одной строчкой: export {Client} from "@hubspot/api-client";
и переписываю импорты на этот файл. Отлично, я избавился от полторы тысячи файлов! Но без типизации можно сделать много ошибок. Поэтому добавляю рядом файл hubspot.d.ts
, в котором прописываю типы только для нужных апи:
import {IHttpOptions} from "@hubspot/api-client/lib/src/services/http/IHttpOptions";
import IConfiguration from "@hubspot/api-client/lib/src/configuration/IConfiguration";
import {PromisePipelinesApi} from "@hubspot/api-client/lib/codegen/crm/pipelines/types/PromiseAPI";
import {PromiseCoreApi} from "@hubspot/api-client/lib/codegen/crm/properties/types/PromiseAPI";
import {PromiseSearchApi} from "@hubspot/api-client/lib/codegen/crm/contacts/types/PromiseAPI";
export declare class Client {
constructor(config?: IConfiguration);
apiRequest(opts?: IHttpOptions): Promise;
crm: {
deals: {
searchApi: PromiseSearchApi
};
contacts: {
searchApi: PromiseSearchApi
};
properties: {
coreApi: PromiseCoreApi;
};
pipelines: {
pipelinesApi: PromisePipelinesApi;
};
}
}
Проверяем: npx tsc --listFiles | grep hubspot | wc -l 112
. Good enough.
Гугл также предоставляет апи к своим сервисам, которых, мне кажется, сильно больше, чем у Hubsot. Но у Google библиотека более продуманная, и позволяет импортировать каждый сервис отдельно:
Вместо
import { google } from 'googleapis';
google.cloudresourcemanager('v1')...
Нужно писать
import { cloudresourcemanager } from 'googleapis/build/src/apis/cloudresourcemanager'
cloudresourcemanager('v1')...
Вроде бы известный способ уменьшения размера бандла, но на бекенде этим чаще всего не заморачиваются, а на фронтенде сборщик может выкинуть из бандла неиспользуемые файлы.
Проверяем: npx tsc --listFiles | grep hubspot | wc -l 288
.
Redis
Почти 500 файлов, ничего себе! Наверное я много чего не знаю про невероятные возможности этой БД. Стал изучать типизацию в @redis/client
и быстро запутался, настолько она изощренная. Можно было бы взять другую библиотеку, но неизвестно, какие подводные камни она принесет. Вместо этого я просто скопировал типизацию используемых методов и немного упростил ее, убрав возможность использовать Buffer вместо string:
export type RedisClientType = {
on(type: 'error', cb: (err: Error) => void | any);
on(type: 'end', cb: (err: any) => void | any);
connect(): Promise;
set(key: string, value: string | number, options?: SetOptions): Promise;
del(keys: string | Array): Promise;
get(key: string): Promise;
publish(channel: string, message: string): Promise;
subscribe: (channels: string | Array, listener: (message: string) => unknown) => Promise;
}
export declare function createClient(config: {
url: string;
password: string;
}): RedisClientType;
declare type MaximumOneOf = K extends keyof T ? {
[P in K]?: T[K];
} & Partial, never>> : never;
declare type SetTTL = MaximumOneOf<{
EX: number;
PX: number;
EXAT: number;
PXAT: number;
KEEPTTL: true;
}>;
declare type SetGuards = MaximumOneOf<{
NX: true;
XX: true;
}>;
interface SetCommonOptions {
GET?: true;
}
export declare type SetOptions = SetTTL & SetGuards & SetCommonOptions;
Осталось только два файла из 491.
Итого
Количество используемых файлов сократилось с 4576 до 1397, потребление памяти упало в два раза до 1Гб, время компиляции на ноутбуке сократилось значительно. Изменения затронули всего 24 файла в проекте, так что code review будет простым.
Некоторые библиотеки предоставляют возможность импортировать отдельные свои части — этим стоит пользоваться, даже если размер бандла вам не важен. Для других библиотек можно использовать отдельные типы для ускорения компиляции. К сожалению, все еще существуют популярные библиотеки, в которых типизация настолько плохая, что лучше бы ее не было. При выборе библиотеки стоит обращать на это внимание.
Главным плюсом я вижу возможность не беспокоиться об OutOfMemory на ноутбуке, ну и пусть все машинки делают чуть меньше работы, надеюсь за это они меня пощядят при восстании.