@rollup/plugin-wasm

WebAssembly (WASM) используется в JavaScript-проектах для выполнения высокопроизводительного кода, написанного на Rust, C/C++ и других языках, компилируемых в бинарный формат. В сборщике Rollup поддержка WASM реализуется через плагин @rollup/plugin-wasm, который обеспечивает корректную интеграцию .wasm модулей в итоговый бандл и управление их загрузкой.

Основная задача плагина заключается в преобразовании импортов WebAssembly-файлов в JavaScript-код, который может корректно загружать, компилировать и инициализировать WASM-модуль во время выполнения.

Общая архитектура работы плагина

Плагин @rollup/plugin-wasm работает на этапе трансформации модулей Rollup. При обнаружении импорта .wasm файла он:

  • перехватывает импортируемый ресурс
  • читает бинарный WASM-файл
  • преобразует его в JavaScript-совместимое представление
  • генерирует код загрузчика (inline или через URL)
  • интегрирует результат в граф зависимостей Rollup

Ключевая особенность заключается в выборе стратегии включения WASM:

  • инлайн бинарных данных в JS
  • загрузка через отдельный файл
  • использование fetch и WebAssembly.instantiateStreaming

Установка и подключение плагина

Плагин устанавливается как обычная зависимость проекта:

npm install @rollup/plugin-wasm --save-dev

Подключение в конфигурации Rollup:

import wasm from '@rollup/plugin-wasm';

export default {
  input: 'src/index.js',
  output: {
    format: 'esm',
    dir: 'dist'
  },
  plugins: [
    wasm()
  ]
};

После подключения Rollup начинает распознавать .wasm импорты как модули.

Базовый пример использования WASM

Структура проекта:

src/
  index.js
  math.wasm

Импорт WASM в Jav * aScript:

import initWasm from './math.wasm';

const wasmModule = await initWasm();

console.log(wasmModule.exports.add(2, 3));

Плагин генерирует функцию инициализации, которая возвращает объект exports, содержащий экспортированные функции WASM-модуля.

Режимы загрузки WebAssembly

Плагин поддерживает несколько стратегий интеграции WASM.

Inline режим

WASM-код встраивается напрямую в JavaScript-бандл в виде бинарного массива.

wasm({
  mode: 'inline'
});

Характеристики:

  • отсутствие дополнительных HTTP-запросов
  • увеличение размера JS-бандла
  • удобен для небольших модулей

URL режим

WASM выносится в отдельный файл, который загружается динамически.

wasm({
  mode: 'url'
});

Поведение:

  • создается отдельный .wasm файл в output директории
  • загрузка происходит через fetch
  • уменьшается размер основного бандла

Fetch режим (по умолчанию в современных сборках)

wasm({
  mode: 'fetch'
});

Особенности:

  • использование WebAssembly.instantiateStreaming
  • оптимальная производительность загрузки
  • требует корректной серверной конфигурации MIME-типа application/wasm

Поведение в разных форматах сборки Rollup

ES Modules (esm)

Наиболее корректный сценарий использования WASM. Поддерживается динамическая загрузка через import() и await.

export default async function load() {
  const wasm = await import('./module.wasm');
  return wasm.default();
}

CommonJS

В CommonJS форматах плагин эмулирует асинхронную загрузку:

const wasm = require('./module.wasm');

wasm().then(instance => {
  console.log(instance.exports);
});

Такой режим может приводить к усложнению управления асинхронностью.

Работа с экспортами и импортами WASM

WebAssembly-модули могут экспортировать функции, память и таблицы.

Пример Rust-кода:

#[no_mangle]
pub fn multiply(a: i32, b: i32) -> i32 {
    a * b
}

В JavaScript после подключения через Rollup:

import init from './math.wasm';

const wasm = await init();

console.log(wasm.exports.multiply(4, 5));

Экспортируемая структура зависит от компилятора и упаковщика WASM.

Передача памяти и взаимодействие с буферами

WASM использует линейную память, которая может быть доступна в JavaScript через WebAssembly.Memory.

Пример работы с буфером:

const wasm = await init();

const memory = wasm.exports.memory;
const view = new Uint8Array(memory.buffer);

view[0] = 42;

Rollup не изменяет модель памяти, но обеспечивает корректную упаковку и загрузку модуля.

Оптимизация размера и производительности

При использовании @rollup/plugin-wasm важны следующие аспекты:

1. Размер бинарника

Inline режим увеличивает размер JavaScript-бандла, поэтому используется для:

  • небольших утилитарных функций
  • криптографических операций
  • вспомогательных вычислений

2. Кэширование

URL режим позволяет браузеру кэшировать .wasm файл отдельно от JS.

3. Lazy loading

Использование динамического import() позволяет загружать WASM только при необходимости:

button.addEventListener('click', async () => {
  const wasm = await import('./heavy.wasm');
  wasm.default();
});

Ограничения и особенности интеграции

1. Асинхронная инициализация

WASM почти всегда требует async-обёртки, что влияет на архитектуру приложения.

2. MIME-типы

Для корректной работы instantiateStreaming сервер должен отдавать:

Content-Type: application/wasm

3. Ограничения tree-shaking

WASM-модули не поддаются классическому tree-shaking, так как являются бинарными артефактами.

4. Поддержка старых окружений

В старых браузерах может отсутствовать:

  • WebAssembly API
  • streaming compilation

В таких случаях используется fallback через ArrayBuffer.

Интеграция с TypeScript

При использовании TypeScript требуется декларация модуля:

declare module '*.wasm' {
  const initWasm: () => Promise<any>;
  export default initWasm;
}

Rollup не генерирует типы автоматически, поэтому типизация задается вручную.

Сочетание с другими плагинами Rollup

@rollup/plugin-wasm часто используется совместно с:

  • @rollup/plugin-node-resolve — для резолва зависимостей
  • @rollup/plugin-commonjs — для поддержки CJS окружений
  • rollup-plugin-terser — для минификации итогового JS

Порядок подключения плагинов влияет на поведение загрузки WASM:

plugins: [
  nodeResolve(),
  wasm(),
  terser()
]

Отладка WebAssembly в Rollup-сборке

При диагностике проблем важно учитывать:

  • корректность пути к .wasm
  • наличие файла в output директории
  • правильность async-инициализации
  • ошибки декодирования бинарного файла

Часто используемый подход — временное переключение режима:

wasm({
  mode: 'url'
});

Это позволяет изолировать проблемы загрузки от проблем инлайна.

Производственные сценарии использования

WASM в Rollup чаще всего применяется в следующих областях:

  • криптография (AES, RSA, SHA)
  • обработка изображений и видео
  • физические симуляции
  • математические вычисления
  • парсинг больших объемов данных

Rollup обеспечивает минимальную обвязку вокруг WASM, сохраняя производительность близкой к нативной.

Структура итогового кода после сборки

После сборки Rollup с @rollup/plugin-wasm итоговый код обычно включает:

  • JS-функцию загрузки WASM
  • либо base64/Uint8Array представление
  • либо ссылку на отдельный .wasm файл
  • обёртку instantiate или instantiateStreaming

Это позволяет интегрировать WASM в любой модульный JavaScript-код без ручной настройки низкоуровневых API WebAssembly