Стендап Сьогодні
📢 Канал в Telegram @stendap_sogodni
🦣 @stendap_sogodni@shevtsov.me в Федиверсі

🤖🚫 AI-free content. This post is 100% written by a human, as is everything on my blog. Enjoy!

Пости з тегом #JavaScript

06.04.2023

Транспайлери JavaScript та цільова версія мови

JavaScript - це, наскільки я знаю, єдина мова програмування, яка компілюється сама в себе. Це створює унікальні обставини. Ніхто вже багато років не хоче писати на тому JavaScript, який працюватиме в будь-якому браузері (хоча за останні десять років ця планка просто злетіла завдяки зникненню Internet Explorer.) Ні, ми пишемо сучасною мовою з усіма можливостями - async/await, null coalesce і так далі. А які середовища будемо підтримувати — залежить від нашого компілятора, точніше, транспайлера.

Зазвичай налаштування транспайлера за замовчуванням всіх влаштовують, але деколи доводиться робити зміни; так в сьогоднішній версії пакета mailtrap ми за недоглядом втратили підтримку старих версій Node.js.

В TypeScript є опція target, яка вказує, який саме стандарт JavaScript буде підтримувати скомпільований код. Який стандарт обрати — вгадайте сами. (Довідка радить ES6.) До того ж є окрема інструкція по вибору версії для Node.js - Node Target Mapping. От на неї ми й отримали проблеми, бо поміняли ціль з es5 на es2020 та втратили підтримку Node.js старіше за 14.

До речі, на сайті Node.js є сторінка з переліком версій, що ще підтримуються; підтримка версії 14 припиняється наприкінці цього місяця. Але, як бачите, це не заважає деяким командам продовжувати користуватись і старішими версіями.

А в Babel це налаштовується через @babel/preset-env. Це дуже зручно, бо дає вказати не тільки конкретні версії браузерів, які ми хочемо підтримувати, а й просто сказати: “99.9% ринку” чи щось схоже. Далі, чим старіше обрані браузери, тим більше нашого коду буде замінено на спрощений.

На останок, компіляція в іншу мову програмування — дуже поширений підхід. Так роблять майже всі компільовані мови програмування. А саме, компілюють вони в мову C, для якої вже є надійний та перевірений компілятор та пакет інструментів GCC. Генерація машинного коду — складна задача, та простіше перекласти її на GCC, ніж робити це наново.


25.02.2025

Express.js

Один з моїх улюблених засобів для створення простого вебсервера - Express. Для рубістів - Express дуже схожий за настроєм з Sinatra.

const app = express();
app.get("/", function (req, res) {
  res.send(`Hello, ${req.query.name}!`);
});

Раджу мати Express на озброєнні для маленьких вебсервісів — прототипів, однозадачних серверів, обгорток всіляких.


08.03.2025

Підписки з YouTube - до RSS

📺 Я якщо що великий прихильник не тільки RSS, а ще й читати їх раз на тиждень, щоб не витрачати час на вхідні потоки. От як раз нещодавно я повернувся на цей графік, бо він має схильність зʼїжджати: спочатку наздоганяєш щось що не встиг, а далі не встигнеш озирнутись, як заглядаєш в читач кожні 15 хвилин.

В цій схемі з RSS була одна діра: YouTube. Ніколи не міг знайти зручного способу передивлятися підписки. (Зручний — це коли я знаю, які нові відео зʼявилися, та можу їх відмітити як прочитані, щоб більше не бачити.)

В YouTube є RSS для кожного каналу, на щастя. Але немає RSS для всіх моїх підписок. Щоб не переносити вручну, знайшов оце скрипт, який генерує зі сторінки каналів файл OPML. Все б гарно, але в тому OPML є тільки адреси RSS, а назв не було.

Почав доробляти… та роздивився, що скрипт генерує з даних JSON, а потім парсить його регулярними виразами. Такого я ще не бачив! При тому, що дані для сторінки сидять в глобальній змінній (теж велика вдача.) Єдине, що там в змінній наче дані вже підготовані для компонент, тому знайти в структурі потрібні дані дійсно непросто, а скрипт з жорстко заданими шляхами буде надто крихким.

Отже, переробив скрипт. Мій варіант ходить по структурі даних та шукає обʼєкти, схожі на канали. Це працює, а головне — з обʼєкта каналу вже нескладно дістати його імʼя та адресу. А генерацію OPML залишив так саме як і було — через додавання рядків.

Ще мені в цьому скрипті сподобалося автоматичне направлення файлу на завантаження. В моїх попередніх рішеннях я просто виводив зміст в консоль для копіювання вручну, а тут значно зручніше.

Осьо мій скрипт. До речі, раніше я викладав схожі скрипти на GitHub Gist, але цього разу думаю — а чому не можу просто до себе на сайт? Тільки голий .js на сайті не дуже зручно, а от як стаття разом зі стислою документацією — чудово.


08.05.2025

Повторювані залежності Yarn зі станом — обережно!

Припустимо, у вас в package.json підключена інтеграція… ну скажімо @some/core та @some/angular. Раптом приходить Mend, та каже — в @some/core вразливість, оновлюємо її! А з @some/angular все добре, її залишаємо. Дякуємо Mend, оновлюємо, живемо далі.

Проходить час та виявляється, що пакет неініціалізований та інтеграція тихо не працює. Ми потрапили в одну з відомих пасток Yarn. Принаймні, відомих після першого такого випадку — в мене, на жаль, навіть вже не перший.

Річ у тім, що в кожного пакета (та в нашого застосунку) свої вимоги до версій залежностей. Та якщо вони не збігаються, Yarn надає кожному пакету власні екземпляри залежності потрібної версії. Тобто застосунок отримує оновлену версію @some/core@1.2 та @some/angular@1.1, а останній, якщо в нього, як часто буває, жорстко задані версії внутрішніх залежностей, в собі може приховувати @some/core@1.1.

В простому випадку це проблема тільки для розміру збірки. (А хто на нього дивиться?) Але, якщо залежність має внутрішній стан, то ми тепер маємо два екземпляри цього стану. Бо кожна версія — це абсолютно окремий пакет! Також нас очікує халепа, якщо пакети містять ООП та перевіряють сумісність прототипів — бо прототипи теж будуть у двох екземплярах. В таке я колись вляпався із pouchdb.

А тепер, що з цим робити? Знайшов плагін для Webpack - DuplicatePackageCheckerPlugin. Він принаймні повідомить, або навіть видасть помилку. Попереджаю, що у вас гарантовано є дублікати, та майже гарантовано від всіх не позбутися — але проблемні (чи навпаки — безпроблемні?) можна ігнорувати.

Також корисною буде команда yarn dedupe - вона намагається оптимізувати залежності, якщо вони роз`їхалися з часом. Але суттєво вона ситуацію не змінить без корекції версій.

Інша корисна команда - yarn why -R packagename - покаже, чому транзитивна залежність є у вашому застосунку. (-R це як раз для транзитивних, щоб дивитися глибше одного рівня.) Я нею користуюся ще й коли хочу зрозуміти, чи можна швидко позбавитися якогось негарного пакета.

Але головне, що ви тепер знаєте, що таке буває, та роздвоєння особистості в пакета не стане сюрпризом.


14.07.2025

Чому в JavaScript такий хаотичний парсер дат?

Поділилися на роботі тестом “на знання парсера дат JavaScript” jsdate.wtf. Так, там багато дичини, так, я навіть на 50% не вгадав, але це всі й так знають. Краще з’ясуймо, чому?

Спочатку можна зайти в специфікацію ECMAScript. Тут все цілком притомно: є один-єдиний формат YYYY-MM-DDTHH:mm:ss.sssZ з невеличкими варіаціями. Ну й хіба те, що аргумент обовʼязково перетворюється в рядок. Все решта… на розсуд реалізації.

Тепер до реалізації. Тест написаний для Node.js. Втім, варто знати, що Node.js побудований на рушії V8, який також використовується в Chrome (та інших місцях.) Тож шукати подробиці будемо в коді V8, який, до речі, написаний на C++. Знаходимо там метод ParseDateTimeString, а з нього - DateParser::Parse. Тут з першого ж погляду бачимо коментар, який пояснює наявність “legacy dates”. В ньому перелічені чи не всі приклади з jsdate.wtf, хоча, на жаль, нема пояснень, звідки саме взялася спадщина.

Можливо, ще цікавіше подивитися на юніт-тест парсера, бо в ньому перелічено багато прикладів. З прикладів можна побачити, чому дивно інтерпретуються короткі значення років та чому має сенс пропускати рядки на початку (як-от день тижня.) Єдине, що мені досі незрозуміле, це пропуск змісту (дужок).

Оцю всю кухню доводиться підтримувати досі, бо браузери хочуть мати найбільшу сумісність, а ніяких версій в JavaScript немає. Отак і живемо, і далі жити будемо, аж поки JavaScript не переродиться чи не помре. (Як гадаєте, побачимо таке?)

У власних проєктах я б радив уникати таких вбудованих методів та використовувати бібліотеку, як-от dayjs, де ви самі контролюєте версію та формат. Та точно не використовувати Date.parse() для валідації! А від хиб на кшталт Date.parse(-[]) захистить, як завжди, TypeScript.

Так що, як бачите, річ не у тім, що парсер погано написаний, та не в тому, що специфікація погана. Спадщина — ось найбільший тягар JavaScript.