Telegram Web Link
Announcing TypeScript 5.5 Beta

Выпущен Typescript 5.5 Beta. Ниже опишу основные изменения

Улучшение уточнения типов в предикатах. В канале уже был пост про это, но коротко повторюсь. Часто TypeScript-у нужно было явно указывать, что функция уточняет тип аргумента. Теперь же Typescript это будет понимать сам.

Пример кода, который ведет себя по-разному в 5.4 и 5.5

// 5.4 - nums: (number | null)[]
// 5.5 - nums: number[]
const nums = [1, 2, 3, null, 5].filter(x => x !== null);

nums.push(null); // ok in TS 5.4, error in TS 5.5


Улучшена проверка доступа к полю по индексу. Если TS уверен, что по текущим переменным там будет корректный тип, он не будет кидать ошибку

function f1(obj: Record<string, unknown>, key: string) {
if (typeof obj[key] === "string") {
// Now okay, previously was error
obj[key].toUpperCase();
}
}


Теперь можно импортировать типы в JSDoc через @import. Импортирование типов в JS файл для использования в JSDoc-е - не самая популярная фича (хотя я использую эту фичу несколько раз в год). Раньше необходимо было делать import внутри декларации типа, теперь же можно явно прописать импорт модуля. Легче показать на примера

// в 5.4
/**
* @param {import("./some-module").SomeType} myValue
*/
function doSomething(myValue) {
// ...
}

// в 5.5
/** @import * as someModule from "some-module" */

/**
* @param {someModule.SomeType} myValue
*/
function doSomething(myValue) {
// ...
}


Также теперь TS умеет проверять регулярки на валидность (но только те, что пишутся сразу в коде, а не те, которые создаются через new RegExp).

Изоляция деклараций типов. Библиотеки предоставляют свои типы в .d.ts файлах. Часто так бывает, что для проверки типов необходимо проходить по всем импортам внутри .d.ts, чтобы вывести все типы и убедиться в их корректности. Это может занимать много времени, особенно в больших проектах. Поэтому в TS ввели режим isolatedDeclarations, которые проверяет, что все типы .d.ts выводимы из файла без прыгания по импортам, что значительно ускоряет тайп-чек.

Опять же, разберем на примере
import { add } from "./add";

const x = add(); // ОК: тип не объявлен, но переменная не экспортируется

// ОШИБКА, экспортируется, но тип непонятен
export function foo() {
return x;
}

// OK, тип указан явно
export function foo(): string {
return x;
}

// OK, тип легко выводится
export let x = 10;
// ОК, тип легко выводится
export function y() { return 20; }

// OK, есть `as number`
export function z() { return Math.max(x, y()) as number; }


В tsconfig добавлена поддержка ${configDir} в описании путей. При использовании композиции конфигов была боль с описанием относительных путей т.к. ts обрабатывает их не так, как иногда хочется. Теперь же можно использовать ${configDir} , чтобы указывать пути относительно конфига. Пример использования

{
"compilerOptions": {
"typeRoots": [
"${configDir}/node_modules/@types"
"${configDir}/custom-types"
],
"outDir": "${configDir}/dist"
}
}


Также проведены оптимизации внутри TypeScript. TS стал быстрее собирать проекта на 5-8%, а language server стал работать на 10-20% быстрее. Кроме того размер пакета (архива) снизился 5.5МБ до 3.7МБ

Теперь нельзя переопределить undefined. До 5.5 нельзя было делать type null = any, type number = any и прочие переопределения. Но можно было переопределить type undefined = any. Теперь так нельзя

А также куча других изменений, но самые большие я коротко пересказал

https://devblogs.microsoft.com/typescript/announcing-typescript-5-5-beta/

#development #javascript #typescript
Дайджест за 2024-04-22 - 2024-05-03

ESLint v9.0.0 released
Вышел Eslint 9.0.0. Eslint начал активно избавляться от всякого legacy, концентрируясь на том, чтобы быть платформой для линтинга, а не супер-комбайном.

Основные изменения касаются отказа от старого конфига, отказа от nodejs < 18 и еще кучи отказов от всякого легаси, а также немного улучшили API для тестирования

Figma Plugins
Статья про особенности написания плагинов для Figma. Статья не очень сильно раскрывает как же писать эти самые плагины и с какими проблемами можно столкнуться при написании плагина. Но зато статья разбирает проблемы sandbox'инга плагинов.

Когда вы разрабатываете инструмент, похожий на Figma, и в которой можно встраивать плагины, у вас определенно возникнет проблема с тем, чтобы как-то ограничить возможности плагинов, чтобы они не могли конфликтовать друг с другом, ломать все приложение или влиять на безопасность.

DevTools Tips & Tricks
10 полезных фичей в DevTools. Некоторые вы уже могли видеть в тысяче других статей про devtools, но другие фичи выглядят достаточно интересно

Migrating 500+ tests from Mocha to Node.js
Хорошая статья про миграцию Astro c Mocha+chai на тест-раннер от ноды. У Astro 600+ тест-сьютов и 1600+ тестов. Большинство из этих тестов интеграционные. Команда Astro захотела переехать на нативное решение в node.js потому что верят в его развитие

Миграция была сделана в 3 этапа: перевели простой пакет, перевели сложный пакет, перевели все остальное

Node.js 22 is now available!
Вышла Nodejs 22, которая станет LTS-кой осенью.

Основные изменения:

Announcing TypeScript 5.5 Beta
Выпущен Typescript 5.5 Beta. Ниже опишу основные изменения

Улучшение уточнения типов в предикатах. В канале уже был пост про это, но коротко повторюсь. Часто TypeScript-у нужно было явно указывать, что функция уточняет тип аргумента. Теперь же Typescript это будет понимать сам.


——————————————

Спасибо что читаете, ставите реакции и отмечаетесь в комментариях. Если вы хотите помочь каналу - расскажите о нем своим коллегамдрузьям. Также оставляйте фидбек по формату, материалу и чему-угодно еще 🙂
React 19 Beta

Выпущен React 19 Beta! Обновляться пока рано, но уже можно готовиться к изменениям.

Первое большое изменение - Actions. Команда React не стала мудрить и взяла термин, который используется кучей тулов для менеджмента состояния и начала его использовать. В React-компонентах часто необходимо делать асинхронные действия, например, загружать данные из API. Сейчас в React это можно сделать композицией нескольких useState. А в React 19 можно использовать несколько новых хуков, которые упрощают работу с такими функциями

Пример использования нового API

const [name, setName] = useState("");  
const [error, setError] = useState(null);
const [isPending, startTransition] = useTransition();

const handleSubmit = () => {
startTransition(async () => {
const error = await updateName(name);
if (error) {
setError(error);
return;
}
redirect("/path");
})
};


Кроме useTransition появился еще useActionState, который упрощает работу с экшнами. Также в Form'ы можно прокидывать action-ы

function ChangeName({ name, setName }) {  
const [error, submitAction, isPending] = useActionState(
async (previousState, formData) => {
const error = await updateName(formData.get("name"));
if (error) {
return error;
}
redirect("/path");
return null;
},
null,
);
return (
<form action={submitAction}>
<input type="text" name="name" />
<button type="submit" disabled={isPending}>Update</button>
{error && <p>{error}</p>}
</form>
);
}


Также для удобства добавили хук useOptimistic, который позволяет упростить применение паттерна Optimistic UI. useOptimistic очень похож на useState, но отличается тем, ему на вход приходит эталонное значение и хук в state возвращает оптимистичное значение, пока action, в рамках котого произошел оптимистичный апдейт, не завершится.

function ChangeName({currentName, onUpdateName}) {  
const [optimisticName, setOptimisticName] = useOptimistic(currentName);
const submitAction = async formData => {
const newName = formData.get("name");
setOptimisticName(newName);
const updatedName = await updateName(newName);
onUpdateName(updatedName);
};
return (
<form action={submitAction}>
<p>Your name is: {optimisticName}</p>
<p>
<label>Change Name:</label>
<input
type="text"
name="name"
disabled={currentName !== optimisticName}
/>
</p>
</form>
);
}


Еще один новый хук use, позволяет дождаться промиса внутри компонента. Из особенностей - хук use может нарушать правила хуков и быть вызван внутри if-ов и других условных конструкций

import {use} from 'react';  

function Comments({commentsPromise}) {
// `use` will suspend until the promise resolves.
const comments = use(commentsPromise);
return comments.map(comment => <p key={comment.id}>{comment}</p>);
}

function Page({commentsPromise}) {
// When `use` suspends in Comments,
// this Suspense boundary will be shown.
return (
<Suspense fallback={<div>Click Me Load More...</div>}>
<Comments commentsPromise={commentsPromise} />
</Suspense>
)
}


Наконец-то можно прокидывать ref как prop компонету, без использования forwardRef.

Также добавлено много фичей для серверного рендера, в частности серверные компоненты, серверные экшны и улучшены удобство рендера meta-тегов, style, script async, preload.

Еще из знаковых изменений - корректная поддержка Custom Elements.

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

https://react.dev/blog/2024/04/25/react-19

#development #javascript #react #release
Великолепная детективная история
Vitest: invalid JSON syntax found at position -1.

Алло, это канал о витесте? Ну а если серьёзно, то пожалуй столкнулся с одним из самых интересных багов в своей жизни.

Я уже рассказывал, что витест для нас компромиссный опыт и породил экстравагантные практикие в духе «гоняем 100 пайплайнов и проверяем, что не флакает». В общем, задумал я обновить его немного, открыл пул-реквест, погонял те самые 100 пайплайнов — всё зеленое! Мёрджим!

Проходит 2 недели и 60% пайплайнов начинают падать… Разработчики ругаются, тимлид предлагает откатывать, я в замешательстве — работало же! Снова начинаю гонять пайплайны, находим 3 проблемных теста, которые никто не трогал уже полгода. Выключаем — всё снова стабильно и зелено. Начинаю разбираться…

Ошибка выглядит так — Error: Failed to parse JSON file, invalid JSON syntax found at position -1. Спасибо, очень информативно! Собираю такой же докер образ как на CI, тыкаю проблемные тест — ничего. Т.к. проблема воспроизводится только на CI, то решаю запатчить vite через yarn patch, добавив название файла в лог. Сработало — проблемный файл мы генерируем на CI и путь до него известен.

Проверяю содержимое файла до тестов — всё на месте. Проверяю после тестов — и снова всё на месте. Но в тестах почему-то всё так же ломается. И тут я замечаю, что у падающих шардов есть кое-что общее — падает именно первый тест. Смотрю как это работает внутри vite — обычный fs.readFile и JSON.parse. В голову закрадывается страшная идея — а что если это какя-то проблема с файловой системой и одновременным чтением одного файла из нескольких процессов. Ищу ишью в ноде, но ничего прям конкретного не вижу. Уже начинаю писать скрипт для стресс-теста одновременного чтения, чтобы проверить гипотезу и тут в голову приходит идея — а что если кто-то этот файл всё таки перезаписывает?

Проверяю файл через fs.stat сразу после записи и после тестов — даты создания и редактирования отличаются! Смотрю немного в пайплайн и понимаю, что внутри одной из команд мы запускаем генерацию этого файла. Мда! А ларчик просто открывался, как говорится. Убираю лишнюю генерацию и всё сразу же зеленеет.

Для меня было откровением, что при записи файл может принимать какое-то промежуточное состояние. Т.е. не before → after, а before → empty → after.

Рефлексируя о дебаге и что могло упростить поиск, пришёл к такому алгоритму:
1. Если ошибка указывает на неочевидное место, то её сразу же надо патчить, чтобы сузить радиус дебага (в очередной раз могу поругать бандлинг зависимостей в vite, кажется в новой версии зависимости ошибка уже содержит путь до файла).
2. В ошибках нужно искать общие признаки (в моём случае что падает только первый тест в каждом из шардов).
3. Проверять самые простые гипотезы в userland коде, а не искать какие-то баги в больших популярных библиотеках (vite и node в нашем случае)

Остаётся вопрос: а почему ошибка не сразу всплыла, а через 2 недели?
Ответ прост: у нас 4 шарда и не все тесты импортируют проблемный файл. С добавлением/удалением тестов порядок в шардах немного меняется и тесты, которые читают файл, становятся первыми в очереди и попадают в те самые первые 5 секунд, когда мы перезаписываем файл.

Такой вот детектив. А как вы провели вечер пятницы?
Deep Dive into Rspack & Webpack Tree Shaking

Крутой разбор механизма работу Tree-shaking в Webpack. Tree-shaking - это фича бандлеров, которая позволяет при сборке выкидывать код, который не нужен в конечном приложении. Решение о неиспользуемости кода принимается на разных уровнях анализа - как на основе анализа стейтментов в конкретном файле, так и на основе анализа импортов и экспортов

В Webpack tree-shaking реализуется тремя механизмами: оптимизация usedExports, оптимизация sideEffect и оптимизация dead code elimination. Оптимизации разбираются от простых к сложным.

Первая разбираемая оптимизация: Dead Code Elimination (уничтожение мертвого кода). Эта оптимизация работает на двух уровнях.

Первый уровень работает на уровне парсинга модуля - он определяет, какие ветви кода являются очевидно неиспользуемыми - например, вечно ложные ветви кода и неиспользуемые константы

Например

if(false){
console.log(a) // очевидно неиспользуемая ветвь кода, можно удалять
} else {
console.log(b);
}


Второй уровень работает уже на этапе предобработки итоговых файлов и удаляет тот код, который не может быть удален первой стадией обработки.
Например, второй уровень справится вот с этим кодом


function get_one(){
return 1;
}
let res = get_one() + get_one(); // функции заинлайнятся, а результат сложится

if(res != 2){ // сборщик понимает что тут 2 != 2, что неверно
console.log(c);
}


Оптимизация usedExports убирает из кода неиспользуемые экспорты. Бандлер, при сборке проекта, промечает все экспортируемые сущности как используемые и неиспользуемые. Все экспорты, которые в итоге оказались неиспользуемыми, можно смело удалять

Например

// index.js
import { a } from './util'
console.log(a)

// util.js
export const a = true

export const b = false // экспорт будет удален т.к. не используется


Как это работает: webpack помечаем неиспользуемые импорты особой инструкцией /* unused harmony export b */, которая обрабатывается механизмом, который отвечает за dead code elimination. Такие экспорты считаются неиспользуемым кодом, а значит его можно удалить

Поверх этих двух оптимизаций работает оптимизация sideEffects, она позволяет удалить модуль, если а) ни один его экспорт не используется б) он не имеет сайд-эффектов. Пункт а) мы уже разобрали - за это отвечает механизм usedExports, но как определить, есть ли в модуле сайд-эффекты?

Допустим, есть модуль, в котором обе экспортирующиеся переменные не используются, но есть вызов функции

export const c = 123;
export const d = test();
function test(){
/* some code */
}


Как определить, что функция test не создает сайд-эффектов (например, не добавляет что-то в window) без глубокого анализа (т.к. глубокий анализ - вещь сложная и замедлит сборку). Для этого есть 2 способа:

Первый - пометить вызов функции чистым

export const c = 123;
export const d = /*#__PURE__*/ test();
function test(){
/* some code */
}


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

Второй вариант - пометить весь модуль чистым. Делается это через поле в package.json

// package.json
{
"sideEffects": false
}


Бандлер также доверяет этому полю. Однако, здесь могут возникнуть сложные ситуации, когда модуль А имеет sideEffects: false, а он использует модуль Б, который имеет sideEffects: true. Есть еще всякие разные граничные случаи и каждый бандлер обрабатывает их по-своему.

В статье более подробно раскрываются механизмы работы этих оптимизаций, приводится в пример собранный webpack'ом код, а также рассматривается, когда эти механизмы работают плохо или не работают.

Рекомендую к прочтению, если вы периодически занимаетесь инфраструктурой сборки в проекте.


https://github.com/orgs/web-infra-dev/discussions/17

#development #javascript #webpack #treeShaking #performance
Извините, но сегодня день начнется с рекомендаций других каналов 🙃

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

Подборка реально крутая, смотрите что вам по душе и подписывайтесь!

🔗 https://www.tg-me.com/addlist/Z6Efi4jXwe9lODcy
Канал про решение алгоритмических задач и подготовку к собеседованиям - Algorithmics. Я сам не фанат алгоритмических секций на собеседованиях, но контент в канале действительно крутой контент по разбору задач. В канале постятся сами задачки и краткое решение, в блоге - детальное объяснение решения и решение задачи на нескольких языках.

Рекоменндую к подписке, если вам интересны алгоритмические задачки
Hello Bun: How Sveld now deploys 2x faster on GitHub and Render

Небольшая статья про перевод инфраструктуры сборки проекта для Svelte на Bun. В целом, миграция для такого небольшого проекта дело достаточно тривиальное - поменять несколько команд и потратить вечерок на замену небольших конструкций кода (для автора это cli-скрипт и код авто-тестов)

Какие результаты:
- Сборка пакета в github-actions проходит в 2-3 раза быстрее
- Пакеты устанавливаются в 2-20 раз быстрее. При этом bun без кешей не сильно отстает от Yarn с кэшами.
- Юнит-тесты бегут в 2 раза быстрее
- В 2 раза ускорился деплой в Render (это платформа для деплоя статичных сайтов

https://render.com/blog/hello-bun-deploy-2x-faster-on-github-render

#development #javascript #bun #migration #svelte
The Front End Developer (Engineer) Handbook 2024

Невероятно огромный handbook про профессию фронтенд инженера в 2024 году. В хендбуке описано все - начиная от определения профессии, переходя через базовые знания (работы сети например) и заканчивая обзором современных инструментов и практик.

Рекомендую к просмотру

https://frontendmasters.com/guides/front-end-handbook/2024/

#development #frontend
HTML attributes vs DOM properties

Джейк Арчибальд написал хорошую статью про разницу между HTML Атрибутами и свойствами DOM-элемента. Я, честно говоря, никогда даже об этом не задумывался - работает и работает. В статье хорошо объясняется разница между ними, а также показываются подводные камни при работе с ними

Сначала стоит отметить базовую разницу между свойствами и атрибутами - атрибуты сериализуются в HTML, могут быть только строковыми и не зависят от регистра

const div = document.createElement('div');

div.setAttribute('foo', 'bar');
div.hello = 'world';

console.log(div.outerHTML); // '<div foo="bar"></div>'

const obj = { foo: 'bar' }
div.setAttribute('foo', obj);
console.log(typeof div.getAttribute('foo')); // 'string'
console.log(div.getAttribute('foo')); // '[object Object]'

// <div id="test" HeLlO="world"></div>
const div = document.querySelector('#test'); console.log(div.getAttributeNames()); // ['id', 'hello']


При этом атрибуты и свойства друг с другом не связаны - можно установить одноименные свойство и атрибут в разные значения

const  div = document.createElement('div')

div.setAttribute('kek', 'true')
div.kek = false

console.log(div.getAttribute('kek')) // true


Но, для многих атрибутов из спеки есть явная связь между атрибутом и свойством. Например, изменение свойства или атрибута id влияет на оба сразу.

const div = document.querySelector('#foo'); console.log(div.getAttribute('id')); // 'foo'
console.log(div.id); // 'foo'

div.id = 'bar';
console.log(div.getAttribute('id')); // 'bar'
console.log(div.id); // 'bar'


Все эти связи описаны в спеке и описаны немного по-разному. Например, свойство crossOrigin и атрибут crossorigin, но свойство ariaLabel и атрибут aria-label

Также есть приколы, связанные с приведением типов, валидацией и дефолтами

const input = document.createElement('input'); 
console.log(input.getAttribute('type')); // null
console.log(input.type); // 'text'

input.type = 'number';
console.log(input.getAttribute('type')); // 'number'
console.log(input.type); // 'number'

input.type = 'foo';
console.log(input.getAttribute('type')); // 'foo'
console.log(input.type); // 'text'


https://jakearchibald.com/2024/attributes-vs-properties/

#development #dom #domProperties #domAttributes
cpupro — лучший cpu профайлер

Не так давно я искал узкие места в витесте и вебпаке и внезапно наткнулся на cpupro. Когда я увидел, кто автор, то понял что инструмент плохим быть не может в принципе: Рома Дворнов — знак качества!

Традиционно, это самый быстрый аналайзер из существующих, способный переваривать огромные (2гб) профайлы.
Ну и по функциональности он впереди всех существуюших профайлеров — куча разных вьюшек, сортировки по пакетам/функциям и так далее. В общем, очень рекомендую!

https://github.com/lahmatiy/cpupro/releases/tag/v0.5.0
Дайджест за 2024-05-06 - 2024-05-10


React 19 Beta
Выпущен React 19 Beta! Обновляться пока рано, но уже можно готовиться к изменениям.

Первое большое изменение - Actions. Команда React не стала мудрить и взяла термин, который используется кучей тулов для менеджмента состояния и начала его использовать. В React-компонентах часто необходимо делать асинхронные действия, например, загружать данные из API. Сейчас в React это можно сделать композицией нескольких useState. А в React 19 можно использовать несколько новых хуков, которые упрощают работу с такими функциями

Deep Dive into Rspack & Webpack Tree Shaking
Крутой разбор механизма работу Tree-shaking в Webpack. Tree-shaking - это фича бандлеров, которая позволяет при сборке выкидывать код, который не нужен в конечном приложении. Решение о неиспользуемости кода принимается на разных уровнях анализа - как на основе анализа стейтментов в конкретном файле, так и на основе анализа импортов и экспортов

В Webpack tree-shaking реализуется тремя механизмами: оптимизация usedExports, оптимизация sideEffect и оптимизация dead code elimination. Оптимизации разбираются от простых к сложным.

Подборка каналов про веб-разработку с авторскими постами.
Подборка реально крутая, смотрите что вам по душе и подписывайтесь!

Канал про митапы\конференции и другие IT-движухи в России.
В канале постится достаточно много событий разной тематики и в разных городах.

Канал про решение алгоритмических задач и подготовку к собеседованиям - Algorithmics.
Я сам не фанат алгоритмических секций на собеседованиях, но контент в канале действительно крутой контент по разбору задач. В канале постятся сами задачки и краткое решение, в блоге - детальное объяснение решения и решение задачи на нескольких языках.

Рекоменндую к подписке, если вам интересны алгоритмические задачки

Hello Bun: How Sveld now deploys 2x faster on GitHub and Render
Небольшая статья про перевод инфраструктуры сборки для Svelte на Bun. В целом, миграция небольшого проекта дело достаточно тривиальное - поменять несколько команд и потратить вечерок на замену небольших конструкций кода (для автора это cli-скрипт и код авто-тестов)

The Front End Developer (Engineer) Handbook 2024
Невероятно огромный handbook про профессию фронтенд инженера в 2024 году. В хендбуке описано все - начиная от определения профессии, переходя через базовые знания (работы сети например) и заканчивая обзором современных инструментов и практик.

Рекомендую к просмотру

HTML attributes vs DOM properties
Джейк Арчибальд написал хорошую статью про разницу между HTML Атрибутами и свойствами DOM-элемента. Я, честно говоря, никогда даже об этом не задумывался - работает и работает. В статье хорошо объясняется разница между ними, а также показываются подводные камни при работе с ними

Репост cpupro — лучший cpu профайлер
это самый быстрый аналайзер из существующих, способный переваривать огромные (2гб) профайлы.

——————————————

Спасибо что читаете, ставите реакции и отмечаетесь в комментариях. Если вы хотите помочь каналу - расскажите о нем своим коллегамдрузьям. Также оставляйте фидбек по формату, материалу и чему-угодно еще 🙂
Development notes from xkcd's "Machine"

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

В статье описывает, как разрабатывалась эта очень красивая и интересная штука. В целом очень мало про код (хотя есть небольшой раздел с React), и очень много про основные принципы, которым следовали создатели игры

Так, относительно игроков принципы были следующие:
- Дать больше возможностей игрокам, даже ценой корректности создаваемых машин. Можно было бы сделать так, чтобы все было строго детерминировано и шары все летели в одной карте только по одному маршруту. Но это а) не так весело б) ограничивает игроков
- Т.к. все отдельные песочницы должны склеиваться в одно огромное поле, то необходимо зафиксировать "интерфейс" - откуда приходят шары и куда должны уходить. Что нужны шары уходят в нужные дырки даже проверяется автоматикой.
- Сколько нужно ждать, чтоб убедиться, что шары действительно летят туда, куда нужно? Ведь можно построить весьма заковыристый механизм, который будет очень долго жонглировать шарами. Разработчики приняли решение, что шары должны достигать своей цели за 30 секунд

Отдельно много внимания в статье уделяется модерации. Был необходим отдельный UI для модерации, где можно быстро проверить, что текущая песочница соответствует правилам хорошей песочницы (все шары попадают за 30 секунд туда, куда нужно)

Про техническую сторону написано не очень много: как движок физики был взят Rapier (написан на Rust, запускается в WASM). Над Rapier был написан UI на React с кастомным контекстом, который управляет объектами Rapier. Использование React позволило легко создавать виджеты, которые влияли на физику (например, Вентилятор). Также удобным оказалась цикл жизни React-компонентов - при скроле холста с механизмами, те механизмы, которые пропали из вида анмаунтились в React, что убирало объекты из Rapier. Получается, что нет на экране - нет и в симуляции физики, что звучит как хороший паттерн. В статье есть ссылки на репозиторий с реализацией и код там выглядит внушительно.

В общем, рекомендую посмотреть статью хотя бы из-за залипательных видосиков, а также поиграться с Machine и построить свой механизм.


https://chromakode.com/post/xkcd-machine/

#development #javascript #react #xkcd
The evolution of Figma’s mobile engine: Compiling away our custom programming language

Статья от команды Figma про то, как они мигрировали с языка Skew на Typescript. Если я правильно понял, Skew - это сайд-проект с ранних дней Figma, цель которого - предоставить язык, который быстро работает и компилируется.

Основные причины, по которым решили отказаться от Skew в пользу Typescript'а заключаются в следующем:
- Тяжело масштабировать разработку т.к. специалистов по Skew нет на рынке и никто его учить не хочет
- Typescript стал де-факто стандартом в вебе и заимел много полезных фичей (особенно по сравнению с временами, когда Figma только стартовала) и имеет прекрасный тулинг вокруг
- Мобильные браузеры стали поддерживать WASM, что позволяет перевести наиболее требовательные к производительности части кода в WASM

Т.е. в целом 2 основных причины: WASM позволяет портировать наиболее "горячий" код без потери производительности (а по замерам команды, потеря производительности при компиляции в JS составляет в 2 раза по сравнению с WASM), а Typescript позволяет разрабатывать на современном популярном языке остальные части.

Первый прототип миграции ребята делали еще в 2020 год и убедились, что это в целом реальная история и поставили себе цель - полностью уйти со Skew. Но т.к. нельзя переписать такой большой проект разом на другой язык, то миграция реализовывалась в 3 фазы

Первая фаза: разработали транспайлер Skew => Typescript. Конечно же не без проблем и их приходилось фиксить.
Вторая фаза: когда генерируемый код стал похож на работающий, начали отдавать его пользователям
Третья фаза: когда убедились, что весь код работает хорошо, просто запустили транспиляцию Skew => Typescript и удалили все Skew исходники

Отдельного внимания заслуживает транспилятор Skew => Typescript. В целом Skew уже умел билдится в Javascript, поэтому транспиляция в Typescript делалась не с нуля. Тем не менее, во время создания транспилятора нашлись интересные нюансы

Например, использование деструктуризации замедляет код на 25%, поэтому при обращении к arguments Skew генерирует код, в котором доступ к элементам массива осуществляется по индексу.

Другая проблема оказалась связана с интересным механизмом в Skew, который называется devirtualization. Суть его в том, что метод класса отвязывается от класса и становится обособленной функцией
myObject.myFunc(a, b)
// becomes...
myFunc(myObject, a, b)

Эта оптимизация как-то ускоряет код и она была в Skew, но её нет в Typescript. Я так понял, что ребята решили отказаться от нее и на основе логов искали код, который рассчитывал на эту фичу Skew

Еще одна прикольная штука связанная с компилятором - это то что ребята сделали мапинг сорс мапов javascript => typescript => skew. Первую часть (ts => js) давал esbuld из коробки. Вторую часть (ts => skew) ребята написали сами.



https://www.figma.com/blog/figmas-journey-to-typescript-compiling-away-our-custom-programming-language/

#development #typescript #figma #migration #skew
How to document your JavaScript package

Хорошая статья от команды Deno про то, как писать документацию к пакету в виде JSDoc. В рамках данной статьи рассказывается, как пользоваться JSDoc и как JSR использует JSDoc для автоматического создания документации к пакету.

Отдельно замечу, что реализация автодокументации от JSR очень хороша. Идея проста - пишите документацию к коду, а инструмент это все распарсит и опубликует в удобном для людей виде. Но, не смотря на простоту этой идеи, хороший реализаций не так много. Если грамотно писать JSDoc к пакету и публиковать его в JSR, то JSR сделает реальную крутую автодоку к пакету.

Что умеет делать JSR с JSdoc:
- Красиво выводит форматирование доки. Можно использовать markdown внутри JSDoc и быть уверенным, что форматирование будет корректно отображено
- Резолвит ссылки на другие сущности внутри пакета
- Объединяет типы TS и jsdoc
- Красиво выводит примеры использования кода
- Автоматически проверяет корректность примеров использования кода

Последняя фича отлично реализована в Rust. Её лучше всего показывать на примере.

Допустим, у вас есть функция sum и вы к ней в JSDoc приводите пример запуска кода и его результат
/**
* Adds two values and returns the sum.
*
* @example
* \`\`\`ts
* import { sum } from "jsr:@deno/sum";
* const finalValue = sum(1, "this is a string"); // 3
* \`\`\`
*/
export function sum(value1: number, value2: number): number {
return value1 + value2;
}


В данном случае в JSDoc ошибка, ведь нельзя прокинуть "this is a string" как число. Deno умеет тестировать доки и найдет ошибку

deno test --doc
Check file:///Users/sum.ts$8-13.ts
error: TS2345 [ERROR]: Argument of type 'string' is not assignable to parameter of type 'number'.
const finalValue = sum(1, "this is a string");


Выглядит интересно. Правда в статье не до конца раскрывается, проходит ли только typecheck, или же сниппеты прям запускаются, как в Rust? Предполагаю, что пока только typecheck, но проверять что примеры из JSDoc запускаются и делают то, что от них ожидается. было бы супер круто.

В статье также описывают бест-практисы по ведению JSDoc в проекте.

https://deno.com/blog/document-javascript-package

#development #javascript #deno #jsr #jsdoc
Дайджест за 2024-05-13 - 2024-05-17


Development notes from xkcd's "Machine"
Достаточно знаменитый сайт с узнаваемыми комиксами xkcd в апреле запускал Machine - игру-песочницу, в которой игроку необходимо расставить различные механизмы так, чтобы входящие в поля шарики вылетали в нужном направлении. Наверное, лучше увидеть это глазами прямо в статье, потому что словами описать сложно. При этом песочницы разных игроков склеиваются в огромную безразмерную Machine, в которой шарики циркулируют бесконечно

В статье описывается, как разрабатывалась эта очень красивая и интересная штука. В целом очень мало про код (хотя есть небольшой раздел с React), и очень много про основные принципы, которым следовали создатели игры

The evolution of Figma’s mobile engine: Compiling away our custom programming language
Статья от команды Figma про то, как они мигрировали с языка Skew на Typescript. Если я правильно понял, Skew - это сайд-проект с ранних дней Figma, цель которого - предоставить язык, который быстро работает и компилируется.


How to document your JavaScript package
Хорошая статья от команды Deno про то, как писать документацию к пакету в виде JSDoc. В рамках данной статьи рассказывается, как пользоваться JSDoc и как JSR использует JSDoc для автоматического создания документации к пакету.

Отдельно замечу, что реализация автодокументации от JSR очень хороша. Идея проста - пишите документацию к коду, а инструмент это все распарсит и опубликует в удобном для людей виде. Но, не смотря на простоту этой идеи, хороший реализаций не так много. Если грамотно писать JSDoc к пакету и публиковать его в JSR, то JSR сделает реальную крутую автодоку к пакету.

——————————————

Спасибо что читаете, ставите реакции и отмечаетесь в комментариях. Если вы хотите помочь каналу - расскажите о нем своим коллегамдрузьям. Также оставляйте фидбек по формату, материалу и чему-угодно еще 🙂
ECMAScript proposal: Promise.withResolvers

Недавно в стандарт языка попала фича, которая немного упрощает кейсы, когда нужно создать промис и управлять резолвом промиса снаружи. Новое API: Promise.withResolvers. В статье разбирается основная проблема, из-за которое появилось API, само API, а также приводится несколько примеров использования

В чем суть и что вообще за проблема. Достаточно часто (по крайней мере в моей практике), нужно создать промис, резолвом которого нужно управлять снаружи


const tasks = []
function someTask(id) {
let _resolve;
const promise = new Promise(resolve => { _resolve = resolve})
// Сохраняем в очередь данные и функцию для резолва
tasks.push({resolve:_resolve, id})
// Возвращаем promise
return promise;
}

// setInterval - как показатель, что бизнес-логика где-то в другом месте
// и соответственно резолвить надо тоже в другом месте
setInternval(()=>{
for(let task of tasks) {
// Выполнили всю логики, резолвим
task.resolve(task.id)
}
}, 10000)


Кейс выше может казаться надуманным, но это буквально последний мой кейс использования такого паттерна (да я пишу наколеночные велосипеды в пет-проектах и не стесняюсь 🙃).

В целом, чтобы создавать такой промис, можно сделать свою утилку
function getPromiseWithResolvers() {
let _resolve, _reject
const promise = new Promise((res, rej)=>{
_resolve = res;
_reject = rej;
})
return {promise, resolve: _resolve, reject: _reject}
}


И именно это и делает новое API

 const { promise, resolve, reject } = Promise.withResolvers();


Вот такая вот небольшая фича, которая стандартизирует популярный в определенных кругах паттерн.

В комментариях можете поделиться своими кейсами, когда вам необходимо создать управляемый снаружи промис.

https://2ality.com/2024/05/proposal-promise-with-resolvers.html

#development #javascript #Promise
2024/05/20 18:07:21
Back to Top
HTML Embed Code: