Типизация querySelector
typed-query-selector — улучшение типизации методов
⚠️ Требуется TypeScript версии 4.1 или выше.
Установка:
Результат:
#npm #typescript
typed-query-selector — улучшение типизации методов
querySelector и querySelectorAll с выводом типов на основе CSS-селекторов.⚠️ Требуется TypeScript версии 4.1 или выше.
Установка:
npm install --save-dev typed-query-selector
// typed-query-selector.d.ts
import "typed-query-selector";
Результат:
document.querySelector("div#app"); // HTMLDivElement
document.querySelector("div#app > form#login"); // HTMLFormElement
document.querySelectorAll("span.badge"); // NodeListOf<HTMLSpanElement>
document.querySelector("button#submit"); // HTMLButtonElement#npm #typescript
Аннотации над текстом с тегом ruby
Часто используется для японского и китайского, но работает и для любых других языков.
Элементы
Пример:
Зачем `<rp>`?
Обычно содержит скобки вокруг
#html
ruby (MDN) — HTML-элемент для аннотирования слов: переводы, транскрипции, пояснения.Часто используется для японского и китайского, но работает и для любых других языков.
Элементы
<ruby>, <rt> и <rp> входят в Baseline: поддерживаются всеми современными браузерами.Пример:
we usually say <ruby>привет<rp>(</rp><rt>hi</rt><rp>)</rp></ruby> as a greeting
Зачем `<rp>`?
<rp> (ruby parentheses) отображается только в браузерах без поддержки <ruby>.Обычно содержит скобки вокруг
<rt>, чтобы сохранить читаемость: привет (hi)#html
Переход с CommonJS на ESM
Модули на основе
Современный стек (включая Node.js) постепенно переходит на ESM (ECMAScript Modules),
который стал официальным стандартом.
Зачем рассматривать переход:
- ESM поддерживается во всех современных сборщиках
- даёт доступ к
- новые API Node.js, например
- всё больше библиотек публикуются только в ESM-формате
Подробнее: antfu.me/posts/move-on-to-esm-only
Подробная инструкция по миграции от sindresorhus
Для постепенного перехода можно включить правило prefer-module из
⚠️ Переход на ESM может быть затруднён, особенно в CLI-приложениях и проектах где используются зависимости, которые давно не обновлялись.
В таких случаях лучше придерживаться CommonJS до появления стабильной поддержки.
ESM — направление развития платформы. Если проект позволяет — стоит начинать миграцию. Но для некоторых типов приложений переход пока может быть преждевременным.
#build
Модули на основе
require() и module.exports (CommonJS) — устаревающий формат.Современный стек (включая Node.js) постепенно переходит на ESM (ECMAScript Modules),
который стал официальным стандартом.
Зачем рассматривать переход:
- ESM поддерживается во всех современных сборщиках
- даёт доступ к
import/export, top-level await, tree-shaking- новые API Node.js, например
import.meta, ориентированы на ESM- всё больше библиотек публикуются только в ESM-формате
Подробнее: antfu.me/posts/move-on-to-esm-only
Подробная инструкция по миграции от sindresorhus
Для постепенного перехода можно включить правило prefer-module из
eslint-plugin-unicorn.⚠️ Переход на ESM может быть затруднён, особенно в CLI-приложениях и проектах где используются зависимости, которые давно не обновлялись.
В таких случаях лучше придерживаться CommonJS до появления стабильной поддержки.
ESM — направление развития платформы. Если проект позволяет — стоит начинать миграцию. Но для некоторых типов приложений переход пока может быть преждевременным.
#build
Pinia: стоит ли использовать setup store
Документация Pinia описывает два способа создания стора: setup store и option-store.
Option-синтаксис:
Setup-синтаксис:
Дополнительно: приватное состояние возможно только через отдельные store-инстансы — например, так:
Подробнее:
– Pinia docs: Setup Stores
– Mastering Pinia: Private State
Таким образом, можно использовать
В большинстве проектов предпочтительнее продолжать использовать
#pinia
Документация Pinia описывает два способа создания стора: setup store и option-store.
Option-синтаксис:
export const useCounter = defineStore("counter", {
state: () => ({ count: 0 }),
actions: {
increment() { this.count++; }
}
});Setup-синтаксис:
export const useCounter = defineStore("counter", () => {
const count = ref(0);
function increment() { count.value++; }
return { count, increment }
});Setup store позволяет использовать Composition API внутри стора, но имеет ряд ограничений:Необходимо вручную вернуть все переменные и методы из setup().
Пропущенные значения приведут к проблемам с SSR, Vue Devtools и сторонними плагинами.
Типизация и линтинг работают ограниченно, проверки на уровне TS/ESLint отсутствуют.
Дополнительно: приватное состояние возможно только через отдельные store-инстансы — например, так:
const usePrivateState = defineStore("store-private", () => {
const secret = ref('Never seen outside');
return { secret }
});
export const useStore = defineStore("store", () => {
const privateState = usePrivateState();
const censoredSecret = computed(() => '*'.repeat(privateState.secret.length));
return { censoredSecret }
});Подробнее:
– Pinia docs: Setup Stores
– Mastering Pinia: Private State
Таким образом, можно использовать
setup store, однако мы не рекомендуем это делать, пока его архитектура остаётся неочевидной, а ошибки не выявляются инструментами.В большинстве проектов предпочтительнее продолжать использовать
option store — он безопаснее, прозрачнее и лучше поддерживается.#pinia
Агрегатор информации по доменным зонам
Если вы когда-либо покупали домен, то знаете, как это выглядит: нужно открыть десяток сайтов регистраторов, вручную вбивать доменные имена, разбираться в условиях, ждать загрузки, обходить рекламу…
А потом выясняется, что продление стоит в 3 раза дороже, а приватный WHOIS — платная опция.
tld-list решает все вопросы одной страницей.
Это агрегатор всей информации по доменным зонам:
- показывает цену регистрации и продления у популярных регистраторов (включая скрытые комиссии)
- отображает, какие зоны поддерживают WHOIS privacy
- позволяет отфильтровать домены по длине, символам, доступности
- сортирует по цене и особенностям (например, можно исключить зоны с премиум-ценами)
Никаких устаревших подборок, перебора регистраторов вручную и вопросов "а сколько .dev стоит у Namecheap?".
tld-list делает это за вас.
#service
Если вы когда-либо покупали домен, то знаете, как это выглядит: нужно открыть десяток сайтов регистраторов, вручную вбивать доменные имена, разбираться в условиях, ждать загрузки, обходить рекламу…
А потом выясняется, что продление стоит в 3 раза дороже, а приватный WHOIS — платная опция.
tld-list решает все вопросы одной страницей.
Это агрегатор всей информации по доменным зонам:
- показывает цену регистрации и продления у популярных регистраторов (включая скрытые комиссии)
- отображает, какие зоны поддерживают WHOIS privacy
- позволяет отфильтровать домены по длине, символам, доступности
- сортирует по цене и особенностям (например, можно исключить зоны с премиум-ценами)
Никаких устаревших подборок, перебора регистраторов вручную и вопросов "а сколько .dev стоит у Namecheap?".
tld-list делает это за вас.
#service
Tld-List
Compare Prices of All Top-Level Domains | TLD-List
Compare the prices of 3,495 domain extensions from 54 registrars. Check domain availability, discover free features, and find the best domain registrar.
Pinia: запрет неявной мутации
Одна из главных причин использовать TypeScript — это предсказуемость.
Любое изменение данных должно быть намеренным: мы либо вызываем функцию, либо обновляем
Но в случае с Pinia возможен такой код:
Явная vs неявная мутация
Что здесь не так?
- ❌
- ✅
Такое поведение ближе к
Именно эта намеренность и отделяет хорошую архитектуру от хрупкой.
Pinia и неявные мутации
В отличие от Vuex, Pinia по умолчанию позволяет напрямую менять
Нет ни
strict режим обсуждался ещё в 2020, но так и остался не реализован.
Как запретить неявные мутации
Мы можем вернуть контроль сами через типизацию. Делаем
Теперь:
Pinia предлагает гибкость, но вместе с ней — уязвимость.
Если вы хотите, чтобы изменения
Мы не ограничиваем возможности Pinia, мы лишь возвращаем понятную дисциплину, которая особенно нужна в команде или на долгосрочном проекте.
#pinia #typescript
Одна из главных причин использовать TypeScript — это предсказуемость.
Любое изменение данных должно быть намеренным: мы либо вызываем функцию, либо обновляем
ref.Но в случае с Pinia возможен такой код:
const store = useDummyStore()
store.stateValue = null // ❌ прямая мутация state, но нет ошибки
Явная vs неявная мутация
Что здесь не так?
- ❌
store.stateValue = null — выглядит как поле объекта, но на самом деле мутирует глобальное хранилище напрямую.- ✅
storeToRefs(store).stateValue.value = null — требует предварительного шага, вызов storeToRefs, и работы с .value, что делает намерение очевидным.Такое поведение ближе к
commit() из Vuex: мы не просто что-то присваиваем, мы просим хранилище редактировать свой state.Именно эта намеренность и отделяет хорошую архитектуру от хрупкой.
Pinia и неявные мутации
В отличие от Vuex, Pinia по умолчанию позволяет напрямую менять
state.Нет ни
mutations, ни встроенного запрета.strict режим обсуждался ещё в 2020, но так и остался не реализован.
Как запретить неявные мутации
Мы можем вернуть контроль сами через типизацию. Делаем
state доступным только для чтения:// env.d.ts
declare module 'pinia' {
export interface StoreDefinition<
Id extends string = string,
S extends StateTree = StateTree,
G = _GettersTree<S>,
A = _ActionsTree
> {
(pinia?: Pinia | null | undefined, hot?: StoreGeneric): Store<Id, Readonly<S>, G, A>
}
}
Теперь:
// Прямое присваивание
const store = useDummyStore()
store.stateValue = null // ❌ Error: Cannot assign to 'stateValue' because it is a read-only property
// Посредством storeToRefs
const { stateValue } = storeToRefs(useDummyStore())
stateValue.value = null // ✅
// Посредством action
store.setValue(null) // ✅
Pinia предлагает гибкость, но вместе с ней — уязвимость.
Если вы хотите, чтобы изменения
state всегда были преднамеренными и контролируемыми, настройка Readonly<StateTree> — хорошая отправная точка.Мы не ограничиваем возможности Pinia, мы лишь возвращаем понятную дисциплину, которая особенно нужна в команде или на долгосрочном проекте.
#pinia #typescript
Очистка node_modules от устаревших полифилов
Даже если вы используете Current или LTS версию Node.js, многие популярные пакеты по-прежнему тянут за собой устаревшие полифилы — вплоть до Node.js 4.
Они часто попадают в проект как зависимости — например,
Это увеличивает размер node_modules, замедляет установку и даже выполнение кода.
Всё потому, что некоторые из этих полифилов используются вместо нативных API, даже если они уже доступны в среде выполнения, что снижает производительность, хотя в этом нет нужды.
nolyfill — это CLI-инструмент, который автоматически находит и заменяет устаревшие полифилы на безопасные заглушки.
⚠️ Он вам не подойдет, если ваш проект запускается на версии Node.js ниже 12.4.0 или вы разрабатываете под среду, которая не поддерживает ECMAScript2019.
Использование:
Если вы часто меняете зависимости, чтобы не забывать запускать
Современный стек требует современных решений.
#npm
Даже если вы используете Current или LTS версию Node.js, многие популярные пакеты по-прежнему тянут за собой устаревшие полифилы — вплоть до Node.js 4.
Они часто попадают в проект как зависимости — например,
eslint-plugin-import, eslint-plugin-jsx-a11y, eslint-plugin-react.Это увеличивает размер node_modules, замедляет установку и даже выполнение кода.
Всё потому, что некоторые из этих полифилов используются вместо нативных API, даже если они уже доступны в среде выполнения, что снижает производительность, хотя в этом нет нужды.
nolyfill — это CLI-инструмент, который автоматически находит и заменяет устаревшие полифилы на безопасные заглушки.
⚠️ Он вам не подойдет, если ваш проект запускается на версии Node.js ниже 12.4.0 или вы разрабатываете под среду, которая не поддерживает ECMAScript2019.
Использование:
npx nolyfill // Найдёт полифилы в текущем проекте
npx nolyfill install // Заменит их на заглушки
Если вы часто меняете зависимости, чтобы не забывать запускать
nolyfill — добавьте вызов в postinstall:// package.json
{
"scripts": {
"postinstall": "npx nolyfill"
}
}
Современный стек требует современных решений.
nolyfill — это простой способ освободить проект от наследия старых зависимостей и сделать шаг к современному, быстрому и чистому JavaScript.#npm
npm
npm: nolyfill
Speed up your package installation process, reduce your disk usage, extend the lifespan of your precious SSD by reducing your node_modules size.. Latest version: 1.0.44, last published: 3 months ago. Start using nolyfill in your project by running `npm i…
Spellcheck для любого текста
Ошибки бывают не только в логике, но и в словах.
Особенно если проект мультиязычный или с большим количеством переменных, комментариев и документации.
cspell — это CLI-инструмент, который проверяет орфографию прямо в коде: в идентификаторах, строках, комментариях и текстовых ресурсах.
Работает быстро, легко настраивается и поддерживает словари для десятков языков.
Пример базовой настройки:
Так получаем проверку орфографии русских и английских слов за один проход по проекту.
Выше представлена базовая конфигурация. Ознакомившись с документацией, вы сможете:
- подключать словари технических терминов, определенных фреймворков и библиотек
- добавлять собственные словари
- создавать отдельные настройки под каждый язык программирования
- тонко настраивать правила, используя маску для разрешения или запрета определенных слов
Если вы хотите поддерживать порядок не только в коде, но и в текстах — cspell поможет избежать опечаток и сэкономит время ревьюеров.
#linters #npm
Ошибки бывают не только в логике, но и в словах.
Особенно если проект мультиязычный или с большим количеством переменных, комментариев и документации.
cspell — это CLI-инструмент, который проверяет орфографию прямо в коде: в идентификаторах, строках, комментариях и текстовых ресурсах.
Работает быстро, легко настраивается и поддерживает словари для десятков языков.
Пример базовой настройки:
npm install --save-dev cspell @cspell/dict-ru_ru
// package.json
{
"scripts": {
"lint:cspell": "cspell **"
}
}
// .cspell.json
{
"$schema": "https://raw.githubusercontent.com/streetsidesoftware/cspell/main/cspell.schema.json",
"version": "0.2",
"language": "en,ru",
"import": ["@cspell/dict-ru_ru/cspell-ext.json"]
}
Так получаем проверку орфографии русских и английских слов за один проход по проекту.
Выше представлена базовая конфигурация. Ознакомившись с документацией, вы сможете:
- подключать словари технических терминов, определенных фреймворков и библиотек
- добавлять собственные словари
- создавать отдельные настройки под каждый язык программирования
- тонко настраивать правила, используя маску для разрешения или запрета определенных слов
Если вы хотите поддерживать порядок не только в коде, но и в текстах — cspell поможет избежать опечаток и сэкономит время ревьюеров.
#linters #npm
Типизированные customEvents
Наверно каждый разработчик хоть раз писал свой CustomEvent-сервис.
Вот и мы написали свой. Но хотя бы типизированный.
Мы используем
- обеспечить строгую типизацию названий событий и их
- иметь автодополнение при вызове событий
- заранее знать структуру
Как использовать:
Реализация:
Если вы обходитесь без глобального стора, не хотите лишних зависимостей и при этом цените строгую типизацию — возможно, вам подойдёт именно такой вариант.
#typescript #vue #vueuse
Наверно каждый разработчик хоть раз писал свой CustomEvent-сервис.
Вот и мы написали свой. Но хотя бы типизированный.
Мы используем
window.dispatchEvent() и useEventListener() из VueUse, но оборачиваем их, чтобы:- обеспечить строгую типизацию названий событий и их
payload- иметь автодополнение при вызове событий
- заранее знать структуру
event.detail без явных проверок и anyКак использовать:
dispatchCustomDashboardEvent("user-created", user.id);
useDashboardEventListener("user-created", ({ detail }) => {
state = detail; // string, не any
});Реализация:
import { useEventListener, type Arrayable } from "@vueuse/core";
// Доступные события и payload описываются через интерфейс
export interface CustomDashboardEvent {
"user-created": User["id"]
"user-deleted": never
}
type DashboardEventName = keyof CustomDashboardEvent;
// Создание типизированного события
function createCustomDashboardEvent<EventName extends DashboardEventName>(
eventName: EventName,
payload?: CustomDashboardEvent[EventName]
): CustomEvent<CustomDashboardEvent[EventName]> {
return new CustomEvent<CustomDashboardEvent[EventName]>(eventName, { detail: payload });
}
// Вызов события
export function dispatchCustomDashboardEvent<EventName extends DashboardEventName>(
eventName: EventName,
...[payload]: CustomDashboardEvent[EventName] extends never ? [] : [CustomDashboardEvent[EventName]]
): void {
window.dispatchEvent(createCustomDashboardEvent(eventName, payload));
}
// Прослушивание событий
export function useDashboardEventListener<EventName extends DashboardEventName>(
eventName: Arrayable<EventName>,
callback: (eventData: CustomEventInit<CustomDashboardEvent[EventName]>) => void,
options?: AddEventListenerOptions
): void {
useEventListener(window, eventName, callback, options);
}Если вы обходитесь без глобального стора, не хотите лишних зависимостей и при этом цените строгую типизацию — возможно, вам подойдёт именно такой вариант.
#typescript #vue #vueuse
This media is not supported in your browser
VIEW IN TELEGRAM
Быстрая очистка node_modules
npkill — это CLI-утилита, которая ищет каталоги
Запуск:
По умолчанию утилита просканирует текущую папку и все вложенные каталоги на наличие
Перемещаясь стрелками и нажимая
⚠️
Периодическая очистка зависимостей в неактуальных или забытых проектах может освободить десятки гигабайт.
#npm
npkill — это CLI-утилита, которая ищет каталоги
node_modules и позволяет удалять их одним нажатием клавиши.Запуск:
npx npkill
По умолчанию утилита просканирует текущую папку и все вложенные каталоги на наличие
node_modules.Перемещаясь стрелками и нажимая
Space, можно тут же удалить любую найденную папку с пакетами.⚠️
npkill удаляет каталоги без подтверждения. Убедитесь, что у вас остались lock файлы, чтобы при необходимости быстро восстановить зависимости.Периодическая очистка зависимостей в неактуальных или забытых проектах может освободить десятки гигабайт.
npkill позволит сделать это быстро и просто.#npm
Добавление иконки перед ссылкой
Чтобы ссылка на внешний ресурс выглядела более узнаваемо, можно добавить к ней иконку, используя только CSS:
Работает для всех ссылок, в
Отображение разных иконок для одного домена:
⚠️ Безопасность
Можно указать ссылку на
Но есть нюансы:
- такие запросы подчиняются CORS-политике, и браузер может заблокировать загрузку;
- запросы к
Поэтому рекомендуется хранить иконки на сервере, либо вставлять их в формате
#css
Чтобы ссылка на внешний ресурс выглядела более узнаваемо, можно добавить к ней иконку, используя только CSS:
a[href*="github.com"]::before {
display: inline-block;
width: 1em;
height: 1em;
margin-right: 0.3em;
content: "";
background-image: url('/assets/icons/github.svg');
background-repeat: no-repeat;
background-size: contain;
}
a[href*="t.me"]::before {
display: inline-block;
width: 1em;
height: 1em;
margin-right: 0.3em;
content: "";
background-image: url('/assets/icons/telegram.svg');
background-repeat: no-repeat;
background-size: contain;
}Работает для всех ссылок, в
href которых встречается нужный домен.Отображение разных иконок для одного домена:
a[href^="https://github.com"] {
--icon: url('/assets/icons/github-icon.svg');
&[href*="/pulls"] {
--icon: url('/assets/icons/github-pull-icon.svg');
}
&::before {
display: inline-block;
width: 1em;
height: 1em;
margin-right: 0.3em;
content: "";
background-image: var(--icon);
background-repeat: no-repeat;
background-size: contain;
}
}⚠️ Безопасность
Можно указать ссылку на
favicon напрямую:a[href*="github.com"]::before {
background-image: url("https://github.com/favicon.ico");
}Но есть нюансы:
- такие запросы подчиняются CORS-политике, и браузер может заблокировать загрузку;
- запросы к
favicon являются внешними — они могут обмениваться куками и оставлять след в логах целевого сайта.Поэтому рекомендуется хранить иконки на сервере, либо вставлять их в формате
base64.#css
Forwarded from mefody.dev
llms.txt
Если готовить сайты для поисковиков мы уже давно научились (SSR, sitemap.xml, robots.txt и прочее), то для LLM пока только учимся.
Полина Гуртовая поделилась интересной ссылкой на пропозал 2024 года. Если сайт хочет быть обработанным LLM-краулером, то достаточно в корень сайта положить файл
Пока что это пропозал от отдельного комьюнити, формально в процесс стандартизации от W3C или WHATWG его даже тяжело поместить, так как сфера AI ещё слишком юная, стандарты придумываются на ходу. Но тем не менее даже без стандарта наличие такого файла не повредит. Скоро SEO превратится в LLMEO, можно начать готовиться.
https://llmstxt.org/
Если готовить сайты для поисковиков мы уже давно научились (SSR, sitemap.xml, robots.txt и прочее), то для LLM пока только учимся.
Полина Гуртовая поделилась интересной ссылкой на пропозал 2024 года. Если сайт хочет быть обработанным LLM-краулером, то достаточно в корень сайта положить файл
/llms.txt с текстовым представлением всего, что нужно знать о сайте. Оформлять можно в Markdown, роботы с ним очень хорошо работают.Пока что это пропозал от отдельного комьюнити, формально в процесс стандартизации от W3C или WHATWG его даже тяжело поместить, так как сфера AI ещё слишком юная, стандарты придумываются на ходу. Но тем не менее даже без стандарта наличие такого файла не повредит. Скоро SEO превратится в LLMEO, можно начать готовиться.
https://llmstxt.org/
llms-txt
The /llms.txt file – llms-txt
A proposal to standardise on using an /llms.txt file to provide information to help LLMs use a website at inference time.
Поиск влитых git-веток и связанных задач
В крупном репозитории, где одновременно работают несколько разработчиков, может быть до 30 активных веток, часть из которых на паузе, часть в работе, а часть размечены в предстоящий релиз.
После каждого релиза такие ветки нужно удалять, делать это вручную — занятие сомнительное.
Чтобы не пропустить ветку или не удалить нужную, можно воспользоваться
git-merged-branches — это CLI-утилита, которая показывает ветки, влитые в основную (master или main).
Работает локально, проста в использовании, не имеет дочерних зависимостей и доступна также под коротким алиасом
Главная фича — возможность просматривать ссылки на задачи из трекера прямо в списке веток, если в названии используются идентификаторы задач, такие как JIRA-123, BUG-42 и т.п.
Использование
Установить глобально:
Или запустить через
Вызвав
Настройка идентификаторов задач
Если вы используете префиксы задач в названиях веток, например TOKEN-123_fix-layout — можно настроить автоматическое добавление ссылок на трекер задач.
Что изменит вывод на такой:
#npm
В крупном репозитории, где одновременно работают несколько разработчиков, может быть до 30 активных веток, часть из которых на паузе, часть в работе, а часть размечены в предстоящий релиз.
После каждого релиза такие ветки нужно удалять, делать это вручную — занятие сомнительное.
Чтобы не пропустить ветку или не удалить нужную, можно воспользоваться
git-merged-branches.git-merged-branches — это CLI-утилита, которая показывает ветки, влитые в основную (master или main).
Работает локально, проста в использовании, не имеет дочерних зависимостей и доступна также под коротким алиасом
gmb.Главная фича — возможность просматривать ссылки на задачи из трекера прямо в списке веток, если в названии используются идентификаторы задач, такие как JIRA-123, BUG-42 и т.п.
Использование
Установить глобально:
npm install --global git-merged-branches
Или запустить через
npx:npx git-merged-branches
Вызвав
git-merged-branches, или алиас gmb, утилита определит базовую ветку (master или main) и выведет список веток, которые уже были в неё влиты:$ gmb
Branches merged into 'master':
bugfix/fix-crash-on-start
feature/add-new-feature
hotfix/urgent-fix
Настройка идентификаторов задач
Если вы используете префиксы задач в названиях веток, например TOKEN-123_fix-layout — можно настроить автоматическое добавление ссылок на трекер задач.
// package.json
{
"git-merged-branches": {
"issueUrlFormat": "https://jira.my-company.com/browse/{{prefix}}{{id}}",
"issueUrlPrefix": ["TOKEN-", "BUG-"]
}
}
Что изменит вывод на такой:
TOKEN-123_fix-layout <https://jira.my-company.com/browse/TOKEN-123>
BUG-56_add-tests <https://jira.my-company.com/browse/BUG-56>
git-merged-branches — минималистичная, но полезная утилита, которая позволит быстро почистить ветки, заодно проверив их статус в трекере.#npm
Скачиваем приложение с RuStore без регистрации
RuStore — российский магазин приложений, который требует обязательной регистрации для скачивания приложений. Однако у RuStore есть публичное API, с помощью которого можно получить данные о приложении и скачать APK даже без аккаунта.
Получение информации о приложении
Получаем в ответе подробные данные о приложении и нужный нам id в
Получение ссылки для скачивания APK
Ответ будет содержать URL в
Скачивание APK
Перейдя по
Подробные данные об эндпоинтах можно найти на gist.github.
Итог
С помощью пары запросов к API RuStore можно обойти требование регистрации и получить APK-файл напрямую. Это пригодится для автоматической загрузки обновлений, резервного копирования или анализа приложений. Всё что вам нужно — знать
#service
RuStore — российский магазин приложений, который требует обязательной регистрации для скачивания приложений. Однако у RuStore есть публичное API, с помощью которого можно получить данные о приложении и скачать APK даже без аккаунта.
Получение информации о приложении
GET https://backapi.rustore.ru/applicationData/overallInfo/{packageName}Получаем в ответе подробные данные о приложении и нужный нам id в
body.appId.Получение ссылки для скачивания APK
POST https://backapi.rustore.ru/applicationData/download-link{
"appId": "body.appId",
"firstInstall": true
}Ответ будет содержать URL в
body.apkUrl.Скачивание APK
Перейдя по
body.apkUrl будет загружен .zip файл с приложением, внутри которого будет нужный нам .apk файл.Подробные данные об эндпоинтах можно найти на gist.github.
Итог
С помощью пары запросов к API RuStore можно обойти требование регистрации и получить APK-файл напрямую. Это пригодится для автоматической загрузки обновлений, резервного копирования или анализа приложений. Всё что вам нужно — знать
packageName приложения.#service
Альтернатива Postman и Insomnia
Postman уже давно больше, чем простой REST-клиент: он оброс авторизацией, облаками и платными фичами. Когда-то легковесной альтернативой ему была Insomnia, но и она постепенно ушла в сторону усложнения и ограничения бесплатного доступа. Теперь для базовых задач это уже не тот инструмент.
Yaak новая попытка вернуться к простоте. Он работает локально, не требует регистрации и предлагает только базовый функционал. Если нужен простой и понятный REST-клиент без отвлекающих надстроек — Yaak может подойти. Больше контекста — в посте автора.
#app
Postman уже давно больше, чем простой REST-клиент: он оброс авторизацией, облаками и платными фичами. Когда-то легковесной альтернативой ему была Insomnia, но и она постепенно ушла в сторону усложнения и ограничения бесплатного доступа. Теперь для базовых задач это уже не тот инструмент.
Yaak новая попытка вернуться к простоте. Он работает локально, не требует регистрации и предлагает только базовый функционал. Если нужен простой и понятный REST-клиент без отвлекающих надстроек — Yaak может подойти. Больше контекста — в посте автора.
#app
Vue, TypeScript и импортированные типы: что может пойти не так?
Когда вы используете
Компилятор постарается сделать всё возможное, чтобы вывести эквивалентные параметры времени выполнения, основанные на аргументах типа. Но сработает это не всегда.
Проблема: неработающий Boolean при импорте
На момент
Пример
Результат
Как видно из примера — если не передавать в проп
Решение
Если поменять тип на локально объявленный, всё работает верно.
В таком случае TypeScript передаёт в Vue AST более полную информацию о локальных типах, и Vue корректно использует конструктор
Проверить самостоятельно
Если хотите поэкспериментировать: Открыть демо на Vue Playground
Итог
Если вы столкнулись с тем, что
Если вам известно другое решение — пишите, будет интересно разобраться!
#typescript #vue #unresolved
Когда вы используете
defineProps в Vue с TypeScript, компилятор Vue запускает обратную генерацию типов: из интерфейсов, аннотаций и значений по умолчанию (default) создаются JS-конструкторы — Boolean, String, Object и другие. Именно они потом используются в скомпилированном компоненте в рантайме.Компилятор постарается сделать всё возможное, чтобы вывести эквивалентные параметры времени выполнения, основанные на аргументах типа. Но сработает это не всегда.
Проблема: неработающий Boolean при импорте
На момент
vue@3.5.*, если типы пропов импортируются, а не определяются прямо в компоненте — Vue может некорректно вывести тип Boolean при использовании shorthand-синтаксиса.Пример
<!-- Comp.vue -->
<script setup lang="ts">
import { Props } from "./types";
withDefaults(defineProps<Props>(), {
disabled: false,
sampleBooleanProp: false
});
</script>
// types.ts
import { InputHTMLAttributes } from "vue";
export interface Props {
disabled?: InputHTMLAttributes["disabled"] // импортированный тип
sampleBooleanProp?: boolean
}
Результат
<!-- Пропы не указаны. Должны примениться значения по умолчанию -->
<Comp/>
<!-- ✅ Работает верно. disabled: false, sampleBooleanProp: false -->
<!-- Пропы указаны со значениями. Должны примениться значения true -->
<Comp :disabled="true" :sampleBooleanProp="true"/>
<!-- ✅ Работает верно. disabled: true, sampleBooleanProp: true -->
<!-- Пропы указаны со значениями. Должны примениться значения false -->
<Comp disabled="false" sampleBooleanProp="false"/>
<!-- ✅ Работает верно. disabled: false, sampleBooleanProp: false -->
<!-- Пропы указаны в shorthand-формате. Должны примениться значения true -->
<Comp disabled sampleBooleanProp/>
<!-- ❌ Ошибка. disabled: '' (пустая строка), sampleBooleanProp: true -->
Как видно из примера — если не передавать в проп
disabled значение, то вместо ожидаемого true, проп равен пустой строке. То есть Vue не сгенерировал конструктор Boolean для disabled — он не смог вывести правильный тип из импортированного интерфейса.Решение
Если поменять тип на локально объявленный, всё работает верно.
// types.ts
// локальный интерфейс, полностью идентичен InputHTMLAttributes
interface InputHTMLAttributes_Local { disabled: boolean | "true" | "false" }
export interface Props {
disabled?: InputHTMLAttributes_Local["disabled"] // локальный аналог
sampleBooleanProp?: boolean
}
В таком случае TypeScript передаёт в Vue AST более полную информацию о локальных типах, и Vue корректно использует конструктор
Boolean.Проверить самостоятельно
Если хотите поэкспериментировать: Открыть демо на Vue Playground
Итог
Если вы столкнулись с тем, что
shorthand-пропы ведут себя странно — вероятно, проблема в неправильно выведенном типе из импортированного интерфейса. Пока единственное решение — объявить типы локально или отказаться от shorthand-синтаксиса.Если вам известно другое решение — пишите, будет интересно разобраться!
#typescript #vue #unresolved
Как я написал свой Stylelint-плагин
На первый взгляд сделать это несложно, но на практике есть свои нюансы и подводные камни. В подробном разборе я рассказал о своём опыте, с примерами и ключевыми моментами реализации: от базового API до поддержки
https://js-f-k.netlify.app/articles/custom-stylelint-plugin
#linters
На первый взгляд сделать это несложно, но на практике есть свои нюансы и подводные камни. В подробном разборе я рассказал о своём опыте, с примерами и ключевыми моментами реализации: от базового API до поддержки
autofix.https://js-f-k.netlify.app/articles/custom-stylelint-plugin
#linters
Почему можно (и нужно) отказаться от Prettier
Когда-то Prettier был обязательным выбором при старте нового проекта. Но времена меняются — и теперь он скорее мешает, чем помогает.
Anthony Fu (core-member Vue, Nuxt, Vite) ещё в 2022 написал убедительную статью о том, почему больше не использует Prettier. Ниже — краткое изложение ключевых аргументов:
- Opinionated. У создателей Prettier своё мнение, как должен выглядеть код — и почти никаких настроек.
- Минимум гибкости. Помимо отсутствия возможности конфигурации правил, большинство из них нельзя даже отключить.
- Конфликты с ESLint. Prettier навязывает стиль, ESLint проверяет логику. Чтобы они работали вместе приходится ставить обвязки, вроде eslint-plugin-prettier.
- ESLint — современная альтернатива. В текущих реалиях сам ESLint справляется с форматированием ничуть не хуже, но при этом даёт полный контроль в одной экосистеме.
Мнение
Я сам начал использовать Prettier году в 2018, и уже тогда он вызывал неудобства. Ранее не было достойных альтернатив, и он действительно скорее экономил время. Но сейчас его время прошло. Если вам всё ещё приходится объяснять, что не так с Prettier — просто дайте ссылку на эту статью.
#linters
Когда-то Prettier был обязательным выбором при старте нового проекта. Но времена меняются — и теперь он скорее мешает, чем помогает.
Anthony Fu (core-member Vue, Nuxt, Vite) ещё в 2022 написал убедительную статью о том, почему больше не использует Prettier. Ниже — краткое изложение ключевых аргументов:
- Opinionated. У создателей Prettier своё мнение, как должен выглядеть код — и почти никаких настроек.
- Минимум гибкости. Помимо отсутствия возможности конфигурации правил, большинство из них нельзя даже отключить.
- Конфликты с ESLint. Prettier навязывает стиль, ESLint проверяет логику. Чтобы они работали вместе приходится ставить обвязки, вроде eslint-plugin-prettier.
- ESLint — современная альтернатива. В текущих реалиях сам ESLint справляется с форматированием ничуть не хуже, но при этом даёт полный контроль в одной экосистеме.
Мнение
Я сам начал использовать Prettier году в 2018, и уже тогда он вызывал неудобства. Ранее не было достойных альтернатив, и он действительно скорее экономил время. Но сейчас его время прошло. Если вам всё ещё приходится объяснять, что не так с Prettier — просто дайте ссылку на эту статью.
#linters
Автоматические size-метки для Merge Request в GitLab CI
В больших проектах удобно сортировать Merge Request по размеру: мелкие правки можно рассмотреть быстро, а крупные требуют отдельного внимания. Чтобы не выставлять метки вручную, можно добавить скрипт в GitLab CI, который автоматически будет присваивать их ориентируясь на количество строк в diff.
https://js-f-k.netlify.app/articles/gitlab-size-labels
#ci #git
В больших проектах удобно сортировать Merge Request по размеру: мелкие правки можно рассмотреть быстро, а крупные требуют отдельного внимания. Чтобы не выставлять метки вручную, можно добавить скрипт в GitLab CI, который автоматически будет присваивать их ориентируясь на количество строк в diff.
https://js-f-k.netlify.app/articles/gitlab-size-labels
#ci #git
ESLint плагины: compat
С постоянным развитием стандарта ECMAScript и Web API вы наверняка сталкивались с ситуацией, когда хочется использовать новую возможность языка или API, но возникает вопрос — заработает ли это у пользователей. Возможно, вы даже добавляли полифиллы пару лет назад, но теперь уже не помните какие из них всё ещё нужны.
Чтобы писать код и не задумываться об этом, можно воспользоваться плагином eslint-plugin-compat — он автоматически проверит, поддерживаются ли используемые функции в целевых браузерах.
Установка
Поддержка полифиллов
Если вы используете полифиллы, их нужно явно указать в настройках — плагин не будет реагировать на такие API:
Итог
⚠️ Не забывайте обновлять
#linters #npm
С постоянным развитием стандарта ECMAScript и Web API вы наверняка сталкивались с ситуацией, когда хочется использовать новую возможность языка или API, но возникает вопрос — заработает ли это у пользователей. Возможно, вы даже добавляли полифиллы пару лет назад, но теперь уже не помните какие из них всё ещё нужны.
Чтобы писать код и не задумываться об этом, можно воспользоваться плагином eslint-plugin-compat — он автоматически проверит, поддерживаются ли используемые функции в целевых браузерах.
Установка
npm install --save-dev eslint-plugin-compat
// eslint.config.js
import compat from "eslint-plugin-compat";
export default [
// ...
compat.configs["flat/recommended"]
];
// package.json / .browserslistrc
{
"browserslist": [
"last 3 Chrome versions",
"Firefox ESR"
]
}
Поддержка полифиллов
Если вы используете полифиллы, их нужно явно указать в настройках — плагин не будет реагировать на такие API:
// eslint.config.js
{
"settings": {
"polyfills": [
"Promise",
"WebAssembly.compile",
"fetch",
"Array.prototype.push"
]
}
}
Итог
eslint-plugin-compat поможет предотвратить ситуацию, когда в прод попадает код, не работающий у части ваших пользователей. Если вы знаете, какие браузеры поддерживаете — подключите этот плагин, и ESLint сам предупредит, если появятся проблемы.⚠️ Не забывайте обновлять
caniuse зависимости, чтобы browserslist всегда соответствовал актуальным данным.#linters #npm
