Стендап Сьогодні
📢
Канал в Telegram @stendap_sogodni
🦣
@stendap_sogodni@shevtsov.me в Федиверсі
Пости з тегом #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}!`);
});
-
JavaScript гарна мова з бібліотеками на всі випадки життя. TypeScript та Prettier роблять розробку зручною без обтяжування. Порівняно з тією ж Сінатрою, я можу впевнено досягати результатів. А порівняно з Go швидко. Для прототипів це золота середина.
-
Синтаксис та семантика Express прості та зрозумілі. Оголошувати маршрути, читати запити та генерувати відповіді можна з мінімумом рухів. Абстракція над HTTP прозора, завжди видно, що куди йде. Чомусь робота з тілом запита POST винесена в окремий пакет body-parser, але то дрібниці.
-
Nodemon та ts-node роблять локальний запуск та перезапуск простим та зрозумілим. Запускаєш
yarn run nodemon entry.ts
та все, можна працювати. -
Express легко (відносно…) розгорнути — або навіть перетворити застосунок на serverless. Відносно, бо не люблю технології, які залежать від вихідного коду бібліотек в продакшні — хай то node_modules, геми Ruby тощо. Втім, принаймні всі залежності для Node.js розташовуються в одній директорії з проєктом.
Раджу мати 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.