DevGang
Авторизоваться

Hyperimport — импорт файлов c, Rust, Zig и т. д. в TypeScript.

— Что? Я правильно прочитал заголовок?

Да! Давайте сразу перейдем к примеру, почему бы и нет?

Ты сможешь это сделать,

index.ts

import { add } from "./add.rs";
console.log(add(5, 5)); // 10

add.rs

#[no_mangle]
pub extern "C" fn add(a: isize, b: isize) -> isize {
    a + b
}

И даже больше, например, импорт собственных функций C из libc в машинописный текст. Ознакомьтесь с руководством.

«Подожди! Что?? Как это вообще возможно!?»

Давным-давно, когда я работал над проектом webview-bun, который по сути является FFI-оболочкой API-интерфейсов библиотеки веб-просмотра для Bun. Мне случайно пришло в голову, почему я не могу импортировать исходный файл C веб-просмотра непосредственно в машинописный текст, если через FFI API мы можем импортировать функции из общих библиотек, которые по сути скомпилированы из исходного файла, такого как c, rust, zig и т. д., как насчет того, чтобы создать способ связать функции импорта в машинописном тексте с исходным файлом и автоматизировать промежуточные шаги таким образом, чтобы конечный пользователь видел, что они напрямую импортируют из исходного файла, в то время как все сложные части управляются автоматически внутри.

Я написал простую функцию Calc в Zig, чтобы складывать два числа. В машинописном скрипте я написал функцию импорта, которая будет определять путь к этому файлу zig, запускать дочерний процесс для вызова компилятора zig для компиляции этого файла в файл общей библиотеки, затем открывать эту общую библиотеку с помощью API FFI и возвращать символы, которые, по сути, содержит функцию расчета. Поэтому, когда я использовал функцию для импорта ZIG-файла, эти внутренние шаги происходили незаметно, и функция Calc работала. Затем, когда я изменил операцию внутри функции zig с сложения на вычитание и выполнил файл typescript, эти шаги повторились, по сути, перекомпилировав файл, и новый результат отразил изменения. Вот как выглядел файл typescript.

const { calc } = $import("./calc.zig");
console.log(calc(1, 2));

Казалось, что в этой функции используется черная магия: функция импортируется непосредственно из Zig, но внутри файл компилируется в общую библиотеку и импортируется из библиотеки, а не из самого исходного файла. Но синтаксис выглядел очень чистым и простым для понимания. Я записал свой экран, демонстрирующий этот экспериментальный прототип, и разместил его на дискорд-сервере Бана.

Вскоре Джаред наткнулся на сообщение и ответил, что я могу использовать Buntime (мы будем называть время выполнения Buntime, почему бы и нет) Plugin API и реализовать свою логику в виде плагина, который позволит мне использовать импорт ES6 вместо эта странная функция импорта.

Честно говоря, до этого я никогда не использовал API плагинов, поэтому начал погружаться в него. С некоторыми изрядными сложностями и некоторыми переписываниями мне наконец удалось перенести эту логику для использования API плагинов. Теперь я мог легко импортировать ZIG-файл, используя синтаксис импорта ES6. Несмотря на то, что Typescript все еще кричал на меня, потому что он не знает, что такое Calc.zig и что такое функция Calc, он все равно поразил меня, потому что выглядел устрашающе.

import { calc } from "./calc.zig";
console.log(calc(1, 2));

Поэтому я решил сделать его еще более ужасающим. Я добавил типы.

Используя функцию объявления модуля с подстановочными знаками в typescript, я создал файл typescript.d.ts, в котором объявил путь к файлу zig как модуль, внутри которого я добавил определения типов для функции Calc. Теперь машинописный текст доволен, и когда я навел курсор на функцию Calc, типы работали отлично, как и ожидалось. Вся комбинация выглядела идеально, но это все еще был статический прототип, и люди не могли использовать его в своих проектах. Я записал свой экран, демонстрирующий этот страшный синтаксис с черной магией, происходящей в фоновом режиме, и даже показал, что когда я менял операцию с сложения на вычитание, изменения все равно отражались. Я записал файлы как для Zig, так и для Rust и снова разместил их на сервере. Вскоре Джаред разместил здесь в Твиттере оба видео, демонстрирующие силу булочки. Все в комментариях к твиту сошли с ума, увидев, что такое вообще возможно.

Праймаген прокомментировал: «Это очень круто. Есть ли у вас какие-нибудь статьи или что-нибудь, что я могу прочитать по этому поводу?», поэтому я решил написать эту статью для него :)

Люди продолжали раздувать мой дискорд, спрашивая, когда я его выпущу. Прошу прощения, что заставил их ждать, но он наконец-то вышел!

Самая сложная часть заключалась в том, чтобы сделать вещи динамичными, потому что в этом прототипе мне пришлось вручную объявлять определения символов FFI, такие как типы аргументов и возвращаемых значений для функции Calc, а также вручную писать определения типов. Я хотел, чтобы все было максимально автоматизировано, и чтобы пользователь имел полный контроль над каждым аспектом, когда он будет использовать его в своем проекте. Мне нужна была гибкость, которая заставила меня задуматься о том, как лучше всего превратить этот прототип в настоящую вещь, которую люди будут использовать в своих проектах. Я экспериментировал с различными подходами, но все они в чем-то потерпели неудачу, что снизило мою мотивацию к дальнейшей работе над этим проектом. Я забросил это на долгое время, а между тем у меня были университетские дела.

Моя цель заключалась в том, чтобы пользователи могли

  • Добавить поддержку любого другого языка, который недоступен по умолчанию.
  • Иметь возможность менять реализации со своей собственной логикой.
  • Импортировать любые другие плагины, а не ограничиваться загрузкой других языковых файлов.

Три месяца спустя, на прошлой неделе, я начал с нуля. Это была моя четвертая или пятая попытка переписать книгу с нуля. Но каждый раз, когда я начинал все заново, у меня был опыт старых неудачных идей. В последний раз я решил подойти к этому, воспользовавшись преимуществами наследования, в основном классов. Где я разделил всю логику на их функции, позволяя пользователю расширять и переопределять их, по сути, заменяя реализации своими собственными. Мне понадобилась неделя, чтобы все заработало так, как я хотел. Мои самые важные цели достигнуты.

  • Класс Loader может быть расширен пользователем, а функции могут быть переопределены с помощью пользовательских реализаций для настройки поведения.
  • Любой тип плагина можно импортировать через hyperimport.

Не говоря уже о том, что эта идея также была представлена ​​в официальном видеоролике о запуске Bun 1.0. Смотрите здесь.

«Могу ли я использовать его? Это на GitHub??»

Абсолютно! Посетите репозиторий Hyperimport и просмотрите wiki подробную документацию с руководствами.

Ознакомьтесь с разделом Импорт файла ржавчины, чтобы получить пошаговое руководство по настройке базового проекта гиперимпорта.

Не стесняйтесь присоединяться к серверу Discord, если у вас есть какие-либо вопросы.

Если вы зашли так далеко, большое спасибо за прочтение статьи. История сумасшедшей экспериментальной идеи, воплотившейся в реальность. Я очень взволнован и буду с нетерпением ждать, какие умопомрачительные идеи люди смогут использовать и расширить его возможности.

Источник:

#JavaScript #TypeScript
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться

Присоединяйся в тусовку

В этом месте могла бы быть ваша реклама

Разместить рекламу