А куда их выделять, в entry массив? Если так - то если в каждом чанке импортится скажем MUI или lodash, то они будут включаться в каждый чанк, и в итоге мы не сэкономим трафик пользователя, а значительно увеличим его, получив еще и проблемы коллизий.
Вручную такой механизм, не залезая во внутрянку esbuild, будет сложно сделать. Я тоже думал о такой схеме:
до билда смотрим все файлы на предмет асинхронных импортов и собираем новый entry points массив из них
продолжаем сборку со всеми этими entry points
смотрим в метафайле на общие зависимости всех entry points и выносим их в отдельный entry point и прописываем в externals весь этот список
пересобираем
Но как видно из схемы, общие зависимости придется грузить сразу вместе с базовым выходным файлом. Как ни кручу в голове разные сценарии, пока сам esbuild не сможет создавать чанки с соответствующими ссылками на используемые и пересекающиеся зависимости, этот функционал корректно не реализовать. Возможно получится сделать схему из 2-3 последовательных ребилдов, но это не то. Если получится - было бы интересно узнать, как
Я не сторонник подхода, когда для разработки используется одна экосистема, а для production-билда - другая, и легко можно поймать на проде ошибки, которых локально не было. В Vite это именно так, поэтому в коммерческих проектах его не использую. Также перфоманс production сборки через Rollup (его использует Vite) хуже, чем моя текущая связка Webpack+SWC. Конкретные цифры не приведу, но в районе 20%, как и при чистом Rollup. А в дев-режиме ускорение с 0.2с (Webpack+SWC) до 0.1с (Vite) совершенно недостаточно, чтобы сподвигнуть на переход.
К Vite я тоже делал 3 подхода, но каждый раз натыкался на недостаточный функционал и низкий перфоманс, если не использовать ESM-dev-схему, которая мне не подходит, как написал выше. Поэтому тщательно изучать Vite после перехода на esbuild не вижу пока необходимости - скорее более интересно будущее Turbopack.
либо использовать одну из многих либ для автобиндинга, если это не стрелочная функция - в mobx тоже есть autoBind: true параметр, если нужно. Но лучше приучаться писать в методах класса стрелочные функции, так не нужны дополнительные либы и обертки - чисто семантика языка
Спасибо за статью в популяризацию MobX, но многое не соответствует действительности.
MobX - не библиотека глобальных сторов, и не библиотека локальных. Это система реактивности, когда можно подписаться на изменение параметров объекта.
const data = observable({ test: 1 });
autorun(() => console.log(data.test));
data.test = 2;
// в консоли 1 и затем 2
Но использовать этот механизм во фреймворках можно, в том числе для менеджеринга состояния (и в многочисленных других кейсах).
"библиотека, использующая в своей реализации Context API" - нет, MobX не привязан к фреймворкам типа React и не тянет их зависимостью. Но может быть использован во фреймворках, в том числе для передачи его реактивных объектов в React Context. Можно и через другие провайдеры и инджекты.
"Mobx сторы хорошо масштабируются" - масштабируются архитектурные подходы, а MobX просто реактивная обвязка над объектами.
"Mobx стор состоит из двух частей — самого стора с данными и провайдера этого стора" - только первое) Провайдер к фреймворкам - это отдельная история. Лучше написать что "чтобы подключить к React через Context нужно завести кроме стора еще и провайдер".
"Стор в сущности функция‑генератор, которая возвращает объект" - нет, это может быть просто объект как в моем примере выше, но самый эффективный подход - оформлять сторы в виде классов
class Store {
constructor() { makeAutoObservable(this); }
data = 1;
toggle = () => { this.data = 2 }
}
const store = new Store();
Таким образом этот стор сам по себе реактивный и типизированный.
Если делать классами, то соответственно будет биндинг контекста и не придется писать
<button onClick={() => appStore.toggleTest()}>
вызывая лишние ререндеры button, потому что анонимная функция будет создаваться новая на каждый рендер. А нужно будет писать
<button onClick={appStore.toggleTest}>
сохраняя равенство по ссылкам и оптимизируя перфоманс процесса reconciliation Реакта.
Как правило, в проекте создается не только .env, но еще и example.dev.env и example.prod.env. Также нужно проверить, что в этих файлах есть все параметры, которые прописаны в ts-типах. Иначе будет частой ситуация, когда добавляется локальный ключ в .env, а в примерах не добавлен, и внезапно на компе другого разработчика проект не запустится (реальная и ооочень частая история). Вот пример, как можно это дело решить https://github.com/dkazakov8/dk-framework/tree/master/packages/compare-env
А для того, что описано в статье, обычно пишу простую утилиту. Можно и Zod прикрутить, но ни разу еще не было необходимости передавать сложные структуры.
Как параметры попадают из .env в process.env - это на усмотрение разработчика, самое простое решение - импортнуть dotenv. Также в моем решении предусмотрены типобезопасные дефолтные значения на случай, если в .env не выставлено значение типа "SENTRY_URL=". Но в отличие от статьи нет жестких проверок на типы и выдачи соответствующих ошибок если значение в .env указано не того типа - в целом фича хорошая, но малополезная, пока не встречал случая, чтобы кто-то смог сделать такую ошибку.
Спасибо за статью, возможно мои решения позволят в чем-то улучшить и ваш подход)
И у меня такой. Поставил на него Windows 95 - получился миниатиюрный псевдо-стационарный компьютер. Даже Fallout поставился. Подтормаживало, конечно, но было интересно
Мне казалось, что чтобы вырасти из миддла в синьоры нужны паттерны. Читал много книг, делал в разных паттернах решение одной и той же задачи, сравнивал перфоманс, масштрабируемость, DX и т.п. Но на протяжении последующих лет пригодились только в обсуждениях в чатах + для общего развития мышления. В реальности нужно совсем другое - абстракция сложности и понятность кода. Первое подразумевает, что можно в выделенной библиотеке писать так, чтобы тебе было понятно, а в коде проекта - чтобы было понятно джуну. Если проект долгоживущий - добавляешь к библиотекам документацию, чем руководствовался при выборе таких подходов, чтобы пришедшие на смену архитекторы могли дорабатывать либо переписывать по своему усмотрению, но код проекта оставался бы таким же простым.
Поэтому все же я думаю, что спрашивать про паттерны не надо ни джунов, ни синьоров, но миддлов погонять можно - это покажет кругозор
Спасибо за статью, прямо с языка все сняли. Сколько ни работал в небольших компаниях и стартапах - история везде одинаковая. Совмещение нескольких должностей, переработки, обещание золотых гор (вам повезло по поводу реальных выплат премий - на моей практике это исключительный случай), абсолютно нереалистичные ожидания с мотивацией "нам надо бежать еще быстрее, чтобы всех перегнать", малое внимание к качеству и стабильности продукта, "мнение начальства непререкаемо", нехватка специалистов, часто - отсутствие отпусков, высокая текучка. Это характерно не только для российских контор - в зарубежных часто не лучше, но как правило более демократично (более горизонтальные структуры).
Я тоже из-за внутреннего чувства ответственности и на ранних стадиях из-за обещаний "золотых гор" выжимался в каждой из компаний, да и сейчас продолжаю в точно такой же ситуации. Но внутренних ресурсов все меньше, а раздражения все больше, отчего иногда становлюсь "токсичным". Все еще надеюсь, что найду нормальную компанию (в крупных конторах действительно было намного спокойнее).
Вариант лучше, без дополнительных классов/data-theme/отдельных параметров --color-dark/--color-light, за которыми еще и нужно следить для избежания коллизий имен - это CSS variables записывать напрямую в <html style="--color-primary=#ccc"> . При смене темы соответственно менять их значения там же. И только для исключительных случаев (разная логика в разных темах) пригодится глобальный идентификатор в виде класса или data-theme. Таким образом значительно уменьшится количество кода и не нужно в стилях прописывать дополнительные служебные переменные
Надо упомянуть, что схема не будет работать с SSR - кука будет прикреплена к домену jsonplaceholder.typicode.com , а не домену фронта, так что node-сервер не сможет ее получить и зафорвардить в апи. Также и из локалстораджа он не получит токен. То есть для SSR юзер всегда будет неавторизован. Конечно, для авторизованных зон SSR зачастую избыточен, но ряд сайтов (маркетплейсы, к примеру) от этого пострадают, хотя и не слишком сильно.
На Хабре обсуждалось не раз предложение "сделать единое AST для TS, ESLint, Webpack и других сборщиков, переиспользовать в IDE", вроде вы тоже участвовали в обсуждениях. Но каждый раз приходим к тому, что есть разные лицензии, корпоративные политики (TS), ограничения операционных систем, разность механик работы разных инструментов. Я тоже хотел бы видеть "универсальный процесс, генерирующий универсальное дерево с адаптером ко всем языкам программирования", это был бы, наверное, лучший вклад в экосистему разработки за десятилетия, но остается только мечтать...
Только начал всю эту канитель, вроде как можно не 8 лет для подтверждения квалификации, а 6 + описание проектов на полкниги. Надеюсь на удачу, что 70-75 баллов каким-то образом стрельнут, также думаю проработать вариант с удаленной работой на австралийскую контору - может тоже зачтется. Спасибо за опыт, креплюсь)
Самое главное забыли - в ряде случаев (когда они появились - практически во всех браузерах так было) у стрелочной функции нет name, `const fn = () => false`, fn.name undefined|''. То есть в стек-трейсе будет anonymous, и если код минифицирован, найти причину бага очень сложно. Но в современных браузерах name назначается по имени константы. Это все еще приходится держать в голове. В определенных случаях и современные браузеры не назначат - `const fn = (() => () => { return false })()`.
Также стрелочные функции, назначенные как константы, нельзя использовать выше их объявления, в отличие от именованных. То есть в ряде случаев придется жонглировать местами объявления.
Еще натыкался на то, что стрелочные методы классов в определенных версиях транспиляторов не декорируются, так как на выходе код совершенно другой, чем у нестрелочного метода.
Фича в целом отличная, но об ограничениях нужно помнить
Чтобы написать достаточно полные описания, по которым ИИ сможет сгенерировать достаточно полноценный код, труда надо намного больше, потому что в каждой задаче нужно передавать весь контекст (подробнейшее описание того, как работает программа). У меня вот была задача по разработке формы, которая будет работать в нескольких режимах как минимум на 40 устройствах с учетом их особенностей. Описание было на 50 страниц, готовили его 3 месяца, и описана там была не вся система, и все равно приходилось писать кастомный код под каждое устройство. Программист будет всегда цениться за то, что сможет по минимальному описанию (бизнес-сценарий) продумать все технические кейсы и внедрить фичу в любую систему, это экономит бизнесу кучу денег и времени.
Хотя и можно для простейших задач создавать текстовые описания типа "создай компонент модалки", но даже в этом случае получишь как в статье - неоптимизированный, неподходящий для встраивания в систему код, который нужно ревьюить и переделывать. Куда проще и быстрее самому написать. Так что в рабочих сценариях пока не вижу применения для ИИ, только для обучения.
Эту технологию очень легко испортить - и то, что я писал из поверхностного, встречалось в каждом проекте, где использовалась css-in-js. И отсутствие корректного управления пропами, и стилизация чилдренов через звездочку, и отсутствие автоформатирования, и непонятные класснеймы либо инлайновые стили, и раздутое дерево компонентов, и смешение компонентов и стилевых компонентов, и деградация перфоманса. Если можно сделать столько ошибок в технологии - то вряд ли ее можно рекомендовать. Если для настройки и создания требований требуется опытный в этом senior - то популяризацией заниматься не нужно, кто умеет приготовить и проконтролировать корректное использование на протяжении всего цикла жизни проекта - тот будет использовать. Но статьи на хабре часто берут на вооружение молодые специалисты, и пока они соберут все грабли - проект может зарыться в багах и низкой скорости разработки. А "готовить" css-in-js очень сложно, требуется постоянный контроль, с этим вы скорее всего согласитесь
Проблем намного больше, это просто верхушка айсберга - я написал комментарий в поддержку статьи, так как сам столкнулся с огромным количеством проблем от разных библиотек css-in-js и полностью разочаровался в этом подходе, который только через несколько лет начал подбираться по удобству к написанию стилей в отдельных файлах с препроцессором. И все мои коллеги согласны с этим, все без исключения вернулись к CSS modules - поэтому IMHO css-in-js может использоваться только теми, кто недостаточно с ним поработал. Часто я говорю сторонникам "через какое-то время вы со мной согласитесь", и это оправдывалось в ряде случаев. Я вас понимаю, сам был оптимистичен и использовал многие подходы из вшивания стилей в разметку.
В CSS Modules нет проблемы передачи пропов стилизации. Есть компоненты, есть стили, которые прикрепляются через className - это единственная точка соприкосновения стилей и компонентов. Нет возни с TS, нет лишних оберток и компонентов, нет лишних импортов, нет лишнего кода в бандле, нет дополнительной ответственности разработчика, нет дополнительного времени на реализацию.
А куда их выделять, в entry массив? Если так - то если в каждом чанке импортится скажем MUI или lodash, то они будут включаться в каждый чанк, и в итоге мы не сэкономим трафик пользователя, а значительно увеличим его, получив еще и проблемы коллизий.
Вручную такой механизм, не залезая во внутрянку esbuild, будет сложно сделать. Я тоже думал о такой схеме:
до билда смотрим все файлы на предмет асинхронных импортов и собираем новый entry points массив из них
продолжаем сборку со всеми этими entry points
смотрим в метафайле на общие зависимости всех entry points и выносим их в отдельный entry point и прописываем в externals весь этот список
пересобираем
Но как видно из схемы, общие зависимости придется грузить сразу вместе с базовым выходным файлом. Как ни кручу в голове разные сценарии, пока сам esbuild не сможет создавать чанки с соответствующими ссылками на используемые и пересекающиеся зависимости, этот функционал корректно не реализовать. Возможно получится сделать схему из 2-3 последовательных ребилдов, но это не то. Если получится - было бы интересно узнать, как
Лучше бы esbuild и для продакшен-сборки использовали и добавили в него нормальное разделение на чанки, чем новый сборщик по факту делать...
Я не сторонник подхода, когда для разработки используется одна экосистема, а для production-билда - другая, и легко можно поймать на проде ошибки, которых локально не было. В Vite это именно так, поэтому в коммерческих проектах его не использую. Также перфоманс production сборки через Rollup (его использует Vite) хуже, чем моя текущая связка Webpack+SWC. Конкретные цифры не приведу, но в районе 20%, как и при чистом Rollup. А в дев-режиме ускорение с 0.2с (Webpack+SWC) до 0.1с (Vite) совершенно недостаточно, чтобы сподвигнуть на переход.
К Vite я тоже делал 3 подхода, но каждый раз натыкался на недостаточный функционал и низкий перфоманс, если не использовать ESM-dev-схему, которая мне не подходит, как написал выше. Поэтому тщательно изучать Vite после перехода на esbuild не вижу пока необходимости - скорее более интересно будущее Turbopack.
Думаю, имелось в виду именно в контексте Redux про миддлвары. Иначе это нападка на Promise, который имеет паттерн миддлвара, или Express.
либо использовать одну из многих либ для автобиндинга, если это не стрелочная функция - в mobx тоже есть autoBind: true параметр, если нужно. Но лучше приучаться писать в методах класса стрелочные функции, так не нужны дополнительные либы и обертки - чисто семантика языка
Спасибо за статью в популяризацию MobX, но многое не соответствует действительности.
MobX - не библиотека глобальных сторов, и не библиотека локальных. Это система реактивности, когда можно подписаться на изменение параметров объекта.
Но использовать этот механизм во фреймворках можно, в том числе для менеджеринга состояния (и в многочисленных других кейсах).
"библиотека, использующая в своей реализации Context API" - нет, MobX не привязан к фреймворкам типа React и не тянет их зависимостью. Но может быть использован во фреймворках, в том числе для передачи его реактивных объектов в React Context. Можно и через другие провайдеры и инджекты.
"Mobx сторы хорошо масштабируются" - масштабируются архитектурные подходы, а MobX просто реактивная обвязка над объектами.
"Mobx стор состоит из двух частей — самого стора с данными и провайдера этого стора" - только первое) Провайдер к фреймворкам - это отдельная история. Лучше написать что "чтобы подключить к React через Context нужно завести кроме стора еще и провайдер".
"Стор в сущности функция‑генератор, которая возвращает объект" - нет, это может быть просто объект как в моем примере выше, но самый эффективный подход - оформлять сторы в виде классов
Таким образом этот стор сам по себе реактивный и типизированный.
Если делать классами, то соответственно будет биндинг контекста и не придется писать
вызывая лишние ререндеры button, потому что анонимная функция будет создаваться новая на каждый рендер. А нужно будет писать
сохраняя равенство по ссылкам и оптимизируя перфоманс процесса reconciliation Реакта.
Как правило, в проекте создается не только .env, но еще и example.dev.env и example.prod.env. Также нужно проверить, что в этих файлах есть все параметры, которые прописаны в ts-типах. Иначе будет частой ситуация, когда добавляется локальный ключ в .env, а в примерах не добавлен, и внезапно на компе другого разработчика проект не запустится (реальная и ооочень частая история). Вот пример, как можно это дело решить https://github.com/dkazakov8/dk-framework/tree/master/packages/compare-env
А для того, что описано в статье, обычно пишу простую утилиту. Можно и Zod прикрутить, но ни разу еще не было необходимости передавать сложные структуры.
Как параметры попадают из .env в process.env - это на усмотрение разработчика, самое простое решение - импортнуть dotenv. Также в моем решении предусмотрены типобезопасные дефолтные значения на случай, если в .env не выставлено значение типа "SENTRY_URL=". Но в отличие от статьи нет жестких проверок на типы и выдачи соответствующих ошибок если значение в .env указано не того типа - в целом фича хорошая, но малополезная, пока не встречал случая, чтобы кто-то смог сделать такую ошибку.
Спасибо за статью, возможно мои решения позволят в чем-то улучшить и ваш подход)
И у меня такой. Поставил на него Windows 95 - получился миниатиюрный псевдо-стационарный компьютер. Даже Fallout поставился. Подтормаживало, конечно, но было интересно
Мне казалось, что чтобы вырасти из миддла в синьоры нужны паттерны. Читал много книг, делал в разных паттернах решение одной и той же задачи, сравнивал перфоманс, масштрабируемость, DX и т.п. Но на протяжении последующих лет пригодились только в обсуждениях в чатах + для общего развития мышления. В реальности нужно совсем другое - абстракция сложности и понятность кода. Первое подразумевает, что можно в выделенной библиотеке писать так, чтобы тебе было понятно, а в коде проекта - чтобы было понятно джуну. Если проект долгоживущий - добавляешь к библиотекам документацию, чем руководствовался при выборе таких подходов, чтобы пришедшие на смену архитекторы могли дорабатывать либо переписывать по своему усмотрению, но код проекта оставался бы таким же простым.
Поэтому все же я думаю, что спрашивать про паттерны не надо ни джунов, ни синьоров, но миддлов погонять можно - это покажет кругозор
Спасибо за статью, прямо с языка все сняли. Сколько ни работал в небольших компаниях и стартапах - история везде одинаковая. Совмещение нескольких должностей, переработки, обещание золотых гор (вам повезло по поводу реальных выплат премий - на моей практике это исключительный случай), абсолютно нереалистичные ожидания с мотивацией "нам надо бежать еще быстрее, чтобы всех перегнать", малое внимание к качеству и стабильности продукта, "мнение начальства непререкаемо", нехватка специалистов, часто - отсутствие отпусков, высокая текучка. Это характерно не только для российских контор - в зарубежных часто не лучше, но как правило более демократично (более горизонтальные структуры).
Я тоже из-за внутреннего чувства ответственности и на ранних стадиях из-за обещаний "золотых гор" выжимался в каждой из компаний, да и сейчас продолжаю в точно такой же ситуации. Но внутренних ресурсов все меньше, а раздражения все больше, отчего иногда становлюсь "токсичным". Все еще надеюсь, что найду нормальную компанию (в крупных конторах действительно было намного спокойнее).
Вариант лучше, без дополнительных классов/data-theme/отдельных параметров --color-dark/--color-light, за которыми еще и нужно следить для избежания коллизий имен - это CSS variables записывать напрямую в <html style="--color-primary=#ccc"> . При смене темы соответственно менять их значения там же. И только для исключительных случаев (разная логика в разных темах) пригодится глобальный идентификатор в виде класса или data-theme. Таким образом значительно уменьшится количество кода и не нужно в стилях прописывать дополнительные служебные переменные
Надо упомянуть, что схема не будет работать с SSR - кука будет прикреплена к домену jsonplaceholder.typicode.com , а не домену фронта, так что node-сервер не сможет ее получить и зафорвардить в апи. Также и из локалстораджа он не получит токен. То есть для SSR юзер всегда будет неавторизован. Конечно, для авторизованных зон SSR зачастую избыточен, но ряд сайтов (маркетплейсы, к примеру) от этого пострадают, хотя и не слишком сильно.
На Хабре обсуждалось не раз предложение "сделать единое AST для TS, ESLint, Webpack и других сборщиков, переиспользовать в IDE", вроде вы тоже участвовали в обсуждениях. Но каждый раз приходим к тому, что есть разные лицензии, корпоративные политики (TS), ограничения операционных систем, разность механик работы разных инструментов. Я тоже хотел бы видеть "универсальный процесс, генерирующий универсальное дерево с адаптером ко всем языкам программирования", это был бы, наверное, лучший вклад в экосистему разработки за десятилетия, но остается только мечтать...
Только начал всю эту канитель, вроде как можно не 8 лет для подтверждения квалификации, а 6 + описание проектов на полкниги. Надеюсь на удачу, что 70-75 баллов каким-то образом стрельнут, также думаю проработать вариант с удаленной работой на австралийскую контору - может тоже зачтется. Спасибо за опыт, креплюсь)
Самое главное забыли - в ряде случаев (когда они появились - практически во всех браузерах так было) у стрелочной функции нет name, `const fn = () => false`, fn.name undefined|''. То есть в стек-трейсе будет anonymous, и если код минифицирован, найти причину бага очень сложно. Но в современных браузерах name назначается по имени константы. Это все еще приходится держать в голове. В определенных случаях и современные браузеры не назначат - `const fn = (() => () => { return false })()`.
Также стрелочные функции, назначенные как константы, нельзя использовать выше их объявления, в отличие от именованных. То есть в ряде случаев придется жонглировать местами объявления.
Еще натыкался на то, что стрелочные методы классов в определенных версиях транспиляторов не декорируются, так как на выходе код совершенно другой, чем у нестрелочного метода.
Фича в целом отличная, но об ограничениях нужно помнить
Это рекламная статья "для галочки", что "популяризируют Сбер на Хабре", только тег реклама не указали. Тут не с кем обсуждать архитектуру.
Чтобы написать достаточно полные описания, по которым ИИ сможет сгенерировать достаточно полноценный код, труда надо намного больше, потому что в каждой задаче нужно передавать весь контекст (подробнейшее описание того, как работает программа). У меня вот была задача по разработке формы, которая будет работать в нескольких режимах как минимум на 40 устройствах с учетом их особенностей. Описание было на 50 страниц, готовили его 3 месяца, и описана там была не вся система, и все равно приходилось писать кастомный код под каждое устройство. Программист будет всегда цениться за то, что сможет по минимальному описанию (бизнес-сценарий) продумать все технические кейсы и внедрить фичу в любую систему, это экономит бизнесу кучу денег и времени.
Хотя и можно для простейших задач создавать текстовые описания типа "создай компонент модалки", но даже в этом случае получишь как в статье - неоптимизированный, неподходящий для встраивания в систему код, который нужно ревьюить и переделывать. Куда проще и быстрее самому написать. Так что в рабочих сценариях пока не вижу применения для ИИ, только для обучения.
Эту технологию очень легко испортить - и то, что я писал из поверхностного, встречалось в каждом проекте, где использовалась css-in-js. И отсутствие корректного управления пропами, и стилизация чилдренов через звездочку, и отсутствие автоформатирования, и непонятные класснеймы либо инлайновые стили, и раздутое дерево компонентов, и смешение компонентов и стилевых компонентов, и деградация перфоманса. Если можно сделать столько ошибок в технологии - то вряд ли ее можно рекомендовать. Если для настройки и создания требований требуется опытный в этом senior - то популяризацией заниматься не нужно, кто умеет приготовить и проконтролировать корректное использование на протяжении всего цикла жизни проекта - тот будет использовать. Но статьи на хабре часто берут на вооружение молодые специалисты, и пока они соберут все грабли - проект может зарыться в багах и низкой скорости разработки. А "готовить" css-in-js очень сложно, требуется постоянный контроль, с этим вы скорее всего согласитесь
Проблем намного больше, это просто верхушка айсберга - я написал комментарий в поддержку статьи, так как сам столкнулся с огромным количеством проблем от разных библиотек css-in-js и полностью разочаровался в этом подходе, который только через несколько лет начал подбираться по удобству к написанию стилей в отдельных файлах с препроцессором. И все мои коллеги согласны с этим, все без исключения вернулись к CSS modules - поэтому IMHO css-in-js может использоваться только теми, кто недостаточно с ним поработал. Часто я говорю сторонникам "через какое-то время вы со мной согласитесь", и это оправдывалось в ряде случаев. Я вас понимаю, сам был оптимистичен и использовал многие подходы из вшивания стилей в разметку.
В CSS Modules нет проблемы передачи пропов стилизации. Есть компоненты, есть стили, которые прикрепляются через className - это единственная точка соприкосновения стилей и компонентов. Нет возни с TS, нет лишних оберток и компонентов, нет лишних импортов, нет лишнего кода в бандле, нет дополнительной ответственности разработчика, нет дополнительного времени на реализацию.