Defront — про фронтенд-разработку и не только
4.09K subscribers
21 photos
1.09K links
Ламповый канал про фронтенд и не только. Всё самое полезное для опытных web-разработчиков

Обсуждение постов @defrontchat

Также советую канал @webnya
Download Telegram
Майлз Боринз три дня назад открыл пулл-реквест в Node.js с обновлённой имплементацией поддержки ES2015 Modules.

Самое значимое изменение – возможность использовать новые модули в файлах с расширением *.js. Теперь в Node.js есть три возможности работать с новыми модулями:
1) используя расширение *.mjs;
2) используя расширение *.js и type: module в package.json;
3) используя расширение *.js и флаги командной строки --type=module / -m.

Причиной изменения подхода стала критика со стороны сообщества предыдущего механизма, когда работа с ES модулями была доступна только в файлах *.mjs. Для сохранения совместимости с CommonJS было добавлено специальное расширение *.cjs.

Разработчики Node.js пока не рекомендуют использовать ES модули в публикуемых пакетах. Релиз поддержки новых модулей без флага запланирован до выхода LTS версии Node.js 12 (октябрь 2019).

#nodejs #modules #esm #es2015

https://github.com/nodejs/node/pull/26745
Николас Закас – автор нескольких книг по JS и оригинальный автор eslint – в январе написал статью про то, почему он решил не использовать дефолтные экспорты в своих модулях. Статья называется "Why I've stopped exporting defaults from my JavaScript modules".

Для меня самое полезное в статье (как это ни странно) не причины, из-за которых автор отказался от дефолтных экспортов, а принципы, которыми он руководствуется при разработке и которые ценно вспоминать время от времени:
1. явное лучше неявного;
2. имена должны быть консистентны во всех файлах;
3. выкидывайте исключений как можно раньше и чаще;
4. меньшее количество решений - более быстрая разработка;
5. необходимость переключать контекст (side trips) замедляет разработку;
6. избыточная когнитивная нагрузка замедляет разработку.

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

#javascript #modules #esm #musings

https://humanwhocodes.com/blog/2019/01/stop-using-default-exports-javascript-module/
Надо уже написать что-нибудь про свои проекты. Недавно доделал бенчмарк скорости загрузки нативных JavaScript-модулей в браузерах — esmtest.

В бенчмарке измеряется скорость загрузки 100 модулей через HTTP/1.1 и HTTP/2. В каждом модуле находится код, который добавляет на страницу одну букву из первых предложений "Преступления и наказания" Ф.М. Достоевского. Скорость загрузки модулей через HTTP/2 в моих тестах в четыре-пять раз быстрее HTTP/1.1. Возникает резонный вопрос, достаточно ли этой разницы, для того чтобы полностью отказаться от бандлинга приложений? Всё зависит от ситуации. Если у вас Electron-приложение или приложение не очень большое, то от бандлинга можно отказаться. С другой стороны, если нужна максимальная скорость загрузки большого приложения, то тут надо действовать по обстоятельствам и делать эксперименты. В любом случае бандлинг можно оставить только для production-сборки и использовать нативную загрузку во время разработки, таким образом от source maps, с которыми иногда бывают проблемы, можно будет отказаться.

По каким-то причинам при троттлинге сети Chrome в пять раз уступает Firefox. Возможно, что на это влияют какие-то оптимизации в Firefox, или просто есть баг в одном из браузеров. В общем, всё это очень интересно, и пока непонятно из-за чего возникает проблема.

P.S. Сайт бенчмарка попал под ковровые бомбардировки Роскомнадзора, поэтому он может быть недоступен у некоторых российских интернет-провайдеров...

#js #modules #esm #performance

https://twitter.com/myshov/status/1139611892652105730
Пару дней назад Джейсон Миллер из Google написал статью про несколько стратегий загрузки js-бандлов с современным синтаксисом — "Modern Script Loading".

Отдача скриптов с современным синтаксисом для актуальных браузеров может очень положительно отразиться на производительности. При этом нельзя забывать о старых браузерах, которые могут не поддерживать новый синтаксис. Для условной загрузки скриптов в стандарте предусмотрен специальный аттрибут nomodule у тега script. Если вставить на страницу два тега скрипт один с современным синтаксисом, а другой со старым и с атрибутом nomodule, тогда последний не будет загружаться в современных браузерах. Но есть проблема — этот трюк будет вызывать лишний фетчинг файлов в Edge и Safari.

Джейсон предлагает четыре решения возникшей проблемы:
1. Определение поддержки браузером современных модулей и вставка на страницу нужного бандла в рантайме
2. При формировании страницы на сервере определять User-Agent и вставлять в код страницы нужный скрипт
3. Использовать описанный выше паттерн nomodule/module, понимая, что у какой-то части пользователей будет происходить лишняя загрузка кода
4. Использование полифиллов в теге с аттрибутом nomodule

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

#js #esm #modules #performance

https://jasonformat.com/modern-script-loading/
Филип Уолтон написал про свою идею использования нативных модулей в качестве выходного формата бандла — "Using Native JavaScript Modules in Production Today".

На данный момент самый популярный бандлер Webpack не поддерживает нативные модули в качестве выходного формата. Если вы посмотрите внутрь бандла, то увидите вместе с кодом модуля бойлерплейт-код для его инициализации. У менее популярного бандлера Rollup есть поддержка esm в качестве выходного формата. Использование нативных модулей даёт несколько преимуществ: выходной бандл получается меньше в объёме и нативные модули можно эффективно загружать с помощью хинта modulepreload (только в Chrome).

В статье подробно разбирается, как реализовать описанный подход на практике. Статью стоит почитать и взвесить все за и против использования описанного подхода в своём проекте.

#performance #rollup #esm

https://philipwalton.com/articles/using-native-javascript-modules-in-production-today/
В блоге Акселя Раушмайера недавно была опубликована новая статья — "Evaluating JavaScript code via import()".

Код, который использует модульную систему JS, не может быть выполнен с помощью eval(). Для того чтобы обойти это ограничение, можно использовать динамический импорт import() с закодированным кодом модуля в виде data URI.

Выглядит это так:
const js = `console.log('Hello everyone!');`;
const encodedJs = encodeURIComponent(js);
const dataUri = 'data:text/javascript;charset=utf-8,'
+ encodedJs;
import(dataUri);


При желании можно получить доступ ко всем экспортируемым объектам. Также можно вставлять data URI прямо в статический импорт, и он будет работать.

Маловероятно, что вы будете использовать этот трюк. Тем не менее статья интересная, почитать стоит.

#esm #trick

https://2ality.com/2019/10/eval-via-import.html
В последнем подкасте HTTP 203 Джек Арчибальд и Сурма рассказали, почему из спецификации HTTP Modules была удалена возможность импорта JSON-файлов.

В Node.js есть возможность зареквайрить json в JavaScript-файл. Это очень удобно, если нужно зафолбечиться на какие-то данные в случае проблем с API или просто для чтения конфигов. Похожий механизм импортов был предложен для добавления в web-платформу в спецификации HTTP Modules: import json from 'some.json'. Проблема в том, что в web расширение файла не влияет на интерпретацию загружаемых данных, тип ресурса определяется HTTP-заголовком content-type. Это означает, что если мы импортируем json с чужого домена import json from 'https://example.com/some.json' и если этот сайт будет компроментирован таким образом, что вместо application/json в заголовке будет отправляться application/javascript, это открывает дыру в безопасности, так как содержимое json-файла может быть заменено на любой JavaScript-код. Теперь разработчики спецификации думают над тем, как добавлять метаинформацию на уровне модульной системы, чтобы избавиться от таких случаев.

Webpack решает проблему с метаинформацией, добавляя свои расширения в спецификатор импорта. Разработчики Rollup и Parcel тоже размышляют над этой проблемой. В любом случае код может получиться непереносимым между бандлерами, если все будут работать над этой задачей независимо. Таким образом решение возникшей проблемы в импорте json на уровне спецификации, позволит унифицировать использование метаинформации и в бандлерах.

#esm #security #specification

https://www.youtube.com/watch?v=jG7VfbqqTGw
Недавно в Node.js 13.2.0 появилась стабильная поддержка ECMAScript modules. Это событие можно считать началом "Великого исхода с CommonJS на ESM". Шучу. CommonJS будет продолжать жить и здравствовать, пока вся экосистема JS не перейдёт на нативную модульную систему. Тем не менее разработчикам библиотек теперь надо иметь в виду, что пакеты могут быть импортированы в CommonJS- и ESM. Какими способами можно поддержать оба окружения, Аксель Раушмайер рассказал в статье "Hybrid npm packages (ESM and CommonJS)".

Основным механизмом для создания гибридных пакетов будет новое поле exports в package.json, поддержка которого находится в экспериментальном режиме за флагом --experimental-conditional-exports. Благодаря ему один и тот же спецификатор импорта можно будет использовать как в CommonJS, так и в ESM. Логика работы exports похоже на switch-case. Символ точки используется для обозначения main:
{
"type": "commonjs",
"main": "./commonjs/entry.js",
"exports": {
".": {
"require": "./commonjs/entry.js",
"default": "./esm/entry.mjs"
}
}
}


Node.js поддерживает условия: require — для импортов из CommonJS, node — для Node.js, default — для всех импортов, не попавших под условие require и node. Другие платформы и инструменты могут поддерживать свои собственные условия, например: browser, electron, deno, react-native.

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

#esm #nodejs #npm

https://2ality.com/2019/10/hybrid-npm-packages.html
Web-worker — это механизм, который позволяет вынести выполнение кода из главного потока браузера. Он доступен в браузерах уже более 10 лет. Проблема в том, что загрузка дополнительного кода в воркерах застряла в 2009 году — там используется синхронная функция loadScripts(), которая загружает код в глобальную область видимости модуля. Джейсон Миллер из Google рассказал про новый подход, который позволяет устранить недостатки старого подхода работы с модульным кодом в веб-воркерах — "Threading the web with module workers".

Новый подход использует ESM ( import/export ), тем самым автоматически подхватывая все преимущества загрузчика новой модульной системы (параллельная загрузка кода, предварительная загрузка и парсинг кода с помощью хинта modulepreload ). Тип воркера необходимо указывать при его создании, передавая дополнительный аргумент {type: 'module'}:
const worker = new Worker('worker.js', {
type: 'module'
});


На данный момент поддержка module workers есть только в Chrome 80. Идёт работа над поддержкой ESM в service workers.

#esm #webworkers

https://web.dev/module-workers/
Сегодня столкнулся с циклическими зависимостями в своём проекте. Захотелось посмотреть, как эту проблему решают другие. Нашёл статью Мишеля Вестстрате (автор mobx) — "How to fix nasty circular dependency issues once and for all in JavaScript & TypeScript".

Суть подхода заключается в использовании двух файлов internal.js и index.js. Файл internal.js реэкспортит все локальные модули. Файл index.js (входная точка в библиотеку) реэкспортит содержимое internal.jsexport * from "./internal.js";. Локальные зависимости должны импортировать нужные сущности только из internal.js. В internal.js все реэкспорты размещаются в таком порядке, в котором все зависимости будут корректно разрешены. Благодаря такому подходу можно управлять порядком загрузки модулей.

В итоге пофиксил циклические зависимости по-другому, но взял на заметку подход Мишеля.

#js #esm #trick

https://medium.com/visual-development/how-to-fix-nasty-circular-dependency-issues-once-and-for-all-in-javascript-typescript-a04c987cf0de
25 апреля npm немного поштормило. Ошибка в пакете is-promise, привела к поломке трёх миллионов зависимых проектов. Форбс Линдсей — автор библиотеки — написал постмортем.

После добавления поддержки ESM новый файл index.mjs не был добавлен в секцию files в package.json. Также в секции exports идентификатор модуля был без префикса ./. Из-за опубликованного кода перестал работать require вида require('is-promise/index'), require('is-promise/index.js'), require('is-promise/package.json').

С момента публикации сломанной библиотеки до её полного фикса прошло четыре часа. Для предотвращения проблем в будущем был удалён .npmignore и добавлен прогон тестов для Node.js 12 и 14, также были добавлены тесты, использующие npm pack для проверки публикуемого API и настроена публикация пакетов из CI. Разработчики Node.js в свою очередь обновили документацию, уточнив, что package.exports должен явно включать в себя все необходимые точки входа.

#npm #postmortem #esm #nodejs

https://medium.com/@forbeslindesay/is-promise-post-mortem-cab807f18dcc
Микаэль Роджерс написал пост про организацию совместимости Node.js-модулей, использующих ESM, и require — "Native ESM in Node.js with require() fallbacks".

Последние версии Node.js поддерживают нативную модульную систему и позволяют импортировать CommonJS-модули внутри ESM-файлов. Но может возникнуть ситуация, когда нужно обеспечить импорт ESM-файлов в CommonJS. Node.js не поддерживает такое направление импорта. Для обхода этого ограничения можно использовать export maps в package.json. В нём каждому файлу сопоставляются соответствующие CommonJS- и ESM-версии:

"exports": {
".": {
"import": "./index.js",
"require": "./dist/index.cjs"
},
"./basics.js": {
"import": "./basics.js",
"require": "./dist/basics.cjs"
},
...
}


Для автоматического создания cjs-файлов из esm-файлов Микаэль воспользовался Rollup и небольшим конфигом.

Статья будет полезна тем, кто хотел использовать в своих пакетах ESM, но не делал этого из-за отсутствия совместимости с CommonJS.

#nodejs #esm

https://dev.to/mikeal_2/native-esm-in-node-js-w-require-fallbacks-and-support-for-all-front-end-compilers-2ded
Лия Веру написала небольшую статью про то как подружить между собой ESM и библиотеки, использующие другие подходы, — "Import non-ESM libraries in ES Modules, with client-side vanilla JS".

Все популярные браузеры поддерживают нативную модульную систему (ESM). Проблема в том, что есть очень много CommonJS-библиотек, которые были написаны давно и которые просто так нельзя взять и подключить к себе с помощью import. Для обхода этой проблемы Лия предлагает использовать небольшой трюк, суть которого заключается в использовании динамического импорта с предварительно застабленной переменной module:

async function require(path) {
let _module = window.module;
window.module = {};
await import(path);
let exports = module.exports;
window.module = _module;
return exports;
}


У такого подхода много ограничений, но он может пригодиться, если надо по-быстрому поэкспериментировать с библиотеками, которые распространяются как CommonJS/AMD-модули (для AMD надо стабить define).

#esm #trick #js

https://lea.verou.me/2020/07/import-non-esm-libraries-in-es-modules-with-client-side-vanilla-js/
Тим Ван Дер Лип из команды разработки Chrome написал статью о миграции кодовой базы девтулзов на ECMAScript Modules — "DevTools architecture refresh: Migrating to JavaScript modules".

Chrome DevTools — это большое приложение, написанное на стандартных web-технологиях: HTML, CSS, JS. Оно было спроектировано более 10 лет назад до массового распространения модульных систем. Для организации модульности до 2020 года в Dev Tools использовался паттерн Externally Defined Dependencies. В этом паттерне весь граф зависимостей описывается в независимом файле или файлах. Специальная утилита (в случае Dev Tools, написанная на python) считывает этот файл и собирает из большого количества js-файлов один бандл.

Такой подход с течением времени стал приносить проблемы. Не было интеграции с ide, были проблемы с утилитой сборки, в которой не учитывались краевые случаи, необходимо было использовать глобальную область видимости для общего кода, список зависимостей в файле нужно было фиксировать в правильном порядке.

Чтобы избавиться от этих проблем, ребята решили перенести всю кодовую базу на ESM. Начальная оценка была 2-4 недели, но весь процесс миграции занял 7 месяцев, так как по ходу дела возникло много проблем, например, с интеграцией со старой системой и с тестами, которые работали в sloppy режиме (то есть без "use strict"; ). Миграция включала в себя два этапа: в первом были добавлены новые export'ы, во втором — import'ы с удалением устаревшего кода.

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

#esm #migration

https://developers.google.com/web/updates/2020/09/migrating-to-js-modules
Якуб Джейерлюк поделился шпаргалкой со всеми способами загрузки JavaScript-кода, их преимуществами и недостатками.

Примеры использования:

— "script src" — самый распространённый вариант загрузки, используется для загрузки кода стандарта ES5 и ниже
— "script src defer" — для отложенного выполнения кода с сохранением порядка выполнения скриптов
— "script src async" — для отложенного выполнения кода без сохранения порядка выполнения
— "script src async defer" — тоже самое, что и предыдущий вариант, но с поддержкой IE9
— "script inline" — для внедрения на страницу небольших сниппетов кода, которые нужно выполнить как можно быстрее
— "script src module" — для загрузки кода в современных браузерах
— "script src module async" — для загрузки кода для прогрессивного улучшения страницы в современных браузерах
— "script inline module" — небольшой сниппет кода, который не нужно кэшировать
— "script inline module async" — небольшой сниппет кода для прогрессивного улучшения страницы, который не нужно кэшировать
— "script nomodule" — фоллбек для старых браузеров, не поддерживающих модульную систему ECMAScript 2015

#js #esm

https://gist.github.com/jakub-g/385ee6b41085303a53ad92c7c8afd7a6