JS F/k
6 subscribers
7 photos
42 links
HTML/TS/Vue — с примерами, по делу, без воды

https://js-f-k.netlify.app

#html #vue #typescript #npm
Download Telegram
Channel created
Автообновление npm зависимостей

Многие до сих пор проверяют зависимости package.json вручную или через npm outdated.
Но есть инструмент, который сделает это удобнее и проще: npm-check-updates (ncu).

Установка:
npm install --global npm-check-updates

ncu          # показать обновления
ncu -u # обновить package.json
npm install # установить зависимости


Показать обновления до определенного уровня:
ncu --target patch
ncu --target minor
ncu --target latest


Показать обновления определенных пакетов:
ncu -f webpack  # только webpack
ncu -x webpack # всё, кроме webpack


Проверка обновлений глобальных зависимостей:
ncu -g


Игнорирование пакетов:
// .ncurc
{
"reject": ["webpack", "eslint", "@types/*"]
}


⚠️ Пока нет возможности ограничить пакеты только определенным интервалом версий.

npm-check-updates даёт полный контроль, гибкость и удобство в настройке, лучше подходит для автоматизации процесса.

#npm
Улучшение дефолтного поведения TypeScript

ts-reset — утилита, которая расширяет стандартную типизацию в TypeScript, устраняя устаревшие и нестрогие участки в базовых API.
Подключается на уровне проекта и повышает строгость типизации в ряде API.

Установка:
npm install --save-dev ts-reset

// reset.d.ts
import "@total-typescript/ts-reset";


Также возможно установить отдельные правила:
// reset.d.ts
import "@total-typescript/ts-reset/json-parse";
import "@total-typescript/ts-reset/fetch";


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

JSON.parse, .json(), localStorage, sessionStorage теперь возвращают unknown, а не any
.filter(Boolean) корректно удаляет falsy-значения
.includes(), .indexOf(), Set.has(), Map.has() не требуют точного сравнения (чего и пытаемся добиться, вызывая их)
Array.isArray() больше не считает any[] безопасным

⚠️ Не рекомендуется использовать в библиотеках, так как изменения глобальных типов могут повлиять на конечные проекты, в которые библиотека будет установлена.

Внутри существующих проектов подключение ts-reset, как правило, не вызывает проблем: типы становятся строже, но остаются совместимыми с корректным кодом.
Если после подключения появляются ошибки — скорее всего, это участки, где типизация и так была небезопасной.

#npm #typescript
Типизация querySelector

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

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
Channel photo updated
Переход с CommonJS на ESM

Модули на основе 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-синтаксис:
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
Pinia: запрет неявной мутации

Одна из главных причин использовать 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.
Они часто попадают в проект как зависимости — например, 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
Spellcheck для любого текста

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

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-сервис.
Вот и мы написали свой. Но хотя бы типизированный.

Мы используем 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-утилита, которая ищет каталоги node_modules и позволяет удалять их одним нажатием клавиши.

Запуск:
npx npkill


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

⚠️ npkill удаляет каталоги без подтверждения. Убедитесь, что у вас остались lock файлы, чтобы при необходимости быстро восстановить зависимости.

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

#npm