IT — Отладка NodeJS приложений при помощи ndb
Пост-заметка об ndb. Всем кто пишет для nodejs периодически приходится отлаживать своё приложение. Да даже тем, кто использует mocha или webpack бывает нет-нет да удобнее отладить по-человечески проблему, нежели тыкать повсюду console.log-и. NodeJS издавна предоставляет нам для этого браузерный инструмент.
Работает оно так:
- мы запускаем наше приложение из консоли с нужным флагом
- NodeJS в консоли нам сообщает ссылку с нужным портом
- Которую мы открываем в браузере и видим перед собой копию chrome-dev-tools-ов.
Если надо перезагрузить приложение ― повторяем всё с нуля. С одной стороны сами инструменты весьма удобные. С другой стороны вся эта мышиная возня с портами и перезапусками очень неудобна.
И тут на помощь к нам приходит ndb. Просто перед командой запуска приложения добавляем ndb. Dev-tools-ы открываются прямо в своём отдельном окне. Перезагрузить приложение можно нажав ctrl+R. Все breakpoint-ы и прочая муть при этом сохраняется.
Выглядит это всё примерно так:
IT — Webpack и SCSS импорты в купе с module-resolver
С приходоим ES7 в JS пришли import-ы и export-ы. Жить стало веселее, и... сложнее. Появилось много заморочек и вообще новых проблем. Одна из таких проблем выглядит так: `import some from ../../../../../some`. Знакомая ситуация?
Обычно её в случае webpack-а решают при помощи модуля module-resolver. Он позволяет в .babelrc (или где вы держите конфигурацию) указывать регулярные выражения для автозамены, а также добавляет поддержку root-записей. Скажем с ним можно указать `import actions from '@actions/header', где `@actions` будет алиасом к какому-нибудь пути в вашем проекте.
Дальше встаёт вопрос инструментов. А как на это должен реагировать редактор? Как он узнает, что теперь пути в import-ах устроены хитрее. Что делать с линтингом? К счастью, для eslint есть такой плагин: eslint-plugin-import. С редакторами ситуация может быть сложнее.
Хорошо, а что делать с SCSS\Sass? Нормальных рабочих решений с наскоку мне найти не удалось. Но как оказалось, всё можно решить относительно просто. В настройках webpack для sass-loader-а можно указать настройки. Они будут переданы как есть в пакет node-sass. А вот он, оказывается, достаточно гибкий. Если там указать метод importer, то можно навязать свою логику обработки @import-ов. Пример:
{
loader: 'sass-loader',
options:
{
sourceMap: true,
url: false,
importer: url => (
{
file: url
.replace(...)
.replace(...)
.replace(...),
}),
},
}
Либо даже выцепить ваши настройки прямо из настроек в .babelrc.
IT — Google Calendar и спам от Казино в мобильном приложении на Android
В который раз наткнулся на странную вещь. Телефон издаёт странный нестандартный непривычный звук. Подхожу - смотрю - реклама казино в google-календаре. В этот раз решил разобраться, как так. Стал рыться-копаться. Нет, таких записей в календаре у меня нет. Нет - к календарю ни у какого приложения доступа нет. WTF?
Разгадка - недоработка Gmail. Оказывается что если сформировать письмо правильным образом, то "умный" Google автоматически формирует в календаре Event под это письмо. И часть писем успевают сформировать такие спам-уведомления из календаря ещё до того момента, пока Google не определяет, что это спам и event-ы с письмами сносит. Однако диалог "пойду-не-пойду на мероприятие" на мобильнике при этом не тухнет.
Лечится принудительным отключением в настройках Google Calendar опции: Automatically add events from Gmail to my calendar. Да, она у всех включена по-умолчанию.
IT — Embedded webpack application
Возникла необходимость внедрять готовый SPA в другой проект на страницу. Проект сразу писался под это дело, но всё равно возник ряд проблем, которые нужно было решать. Напишу про 2 из них сюда как шпаргалку.
webpack & dynamic import
Что если ваш проект использует динамический импорт? Например System.import. В этом случае webpack сделает для подгружаемых файлов отдельный chunk. И грузить он его будет исходя из publicPath в настройках webpack. Но ведь embedded решения могут быть внедрены по любому пути. Как быть? Всё просто, пишем в точке входа что-нибудь типа: __webpack_public_path__ = window.myPredefinedPath и дело в шляпе. Магия. До загрузки приложения нужно определить этот window.myPredefinedPath, webpack его увидит и будет использовать. Сразу отмечу, что надо писать как есть, не window.__webpack...=, а как-будто эта переменная уже определена в коде. Если на неё ругается eslint не беда, пишем: // eslint-disable-line и он более не ругается.
webpack css & url
Возникла проблема с тем, что в стилях указаны пути к шрифтам так, что webpack их найти не может (отдельная тема почему так). Да и не должен, него его это забота. Однако он пытается от-revolve-ить все пути, что находит в css. Лечится установкой url: false в cssLoader-е. После этой настройки пути отдаются как есть без каких-либо проверок.
IT — React 16 & Context decorators
С обновлением React до 16 версии мы получили новый вид контекста, на замену старому. Теперь это не мутное API, как было раньше, а утверждённый вариант, с которым можно смело в бой. В чём же ключевые различия?
- Старый вариант был многословным. Новый вариант компактнее.
- Старый вариант не обновлял связанные с контекстом компоненты при изменении контекста. По сути контекст должен был быть имутабельным. Новый вариант обновляет связанные с ним компоненты.
- Старый вариант можно было использовать в любом методе компонента. Новый только в render-е.
- Старый вариант не имел пенальти по производительности и не требовал использования HOC-ов, не увеличивал иерархию древа. Новый вариант всё это делает. Скажем для connect-а из redux-а потребуется аж 2 дополнительных уровня иерархии.
- Старый вариант был оформлен в виде static полей класса, новый вариант в виде function as tag content. И то и другое слегка уродливо. Или не слегка. JSX в целом страшненький. А тут...
В итоге послевкусие специфическое. С одной стороны API хотя бы устаканили. С другой стороны сделали его каким-то очень неудобным и вообще кривоватым. Такое впечатление возникает в целом, глядя на новые возможности из 16+. Но тут я остановлюсь на работе с контекстом.
Писать вот так:
render()
{
return <Consumer>{arg1 =>
<Consumer2>{arg2 =>
<div>/* some code with them */</div>
}</Consumer2>
}</Consumer>
}
… невыносимо. Хочется удобства. В итоге это вылилось в такой вариант:
@renderContext('arg1', 'arg2')
render(arg1, arg2)
{
return <div>/* some code with them */</div>;
}
По сути я воспользовался декораторами из ES7, которые пока под некоторым вопросом. Но т.к. JSX в стандарте тоже нет, то плевать. Что делает данный декоратор? Он оборачивает метод render обёртками из кода выше. Но делает это автоматически. А в исходный render метод передаёт все контексты как аргументы функции в том же порядке.
Никакой магии в этом нет и все эти обёртки над обёртками никуда не испарились, но теперь хотя бы скрыты с глаз долой. Помимо прочего я написал ещё несколько декораторов. Один для <Provider/>-ов, один для HOC, один для удобтва i18n. Чувствую в декораторы затянет меня с головой. Ух злая эта штука. Это не к добру :)
Сам декоратор устроен вот так:
const renderContext = (...keys) =>
function(target, methodName, descriptor)
{
const render = descriptor.value;
descriptor.value = keys
.map(key => list[key].Consumer)
.reverse()
.reduce((inner, Consumer) =>
{
return function(...args)
{
return <Consumer>
{arg => inner.call(this, ...args, arg)}
</Consumer>;
};
}, render);
return descriptor;
};
Забирайте кому надо ;)