Frontend Den
1.63K subscribers
69 photos
11 videos
24 files
160 links
Все дял починаючих веб-розробників і не тільки. Зв'язок з адміном або запропонувати новину 👉 @denyspopov_web
@junnot_chat
#frontend #wordpress #html #css
Download Telegram
Привіт, 19 жовтня відбудеться React+ fwdays'24 — щорічна конференція для всіх, хто цікавиться розробкою на JavaScript 🤩

Основний фокус заходу — React. Проте, цього разу, Fwdays не обмежуються лише React та поговорять і про інші фреймворки, такі як Vue.js, Node.js, Angular, Svelte та інші.

Вас очікують:
📍Цікаві спікери та практичні доповіді про покращення React за допомогою TypeScript, мікро-фронтенд та монорепо, здоровий ґлузд та перфекціонізм під час рефакторингу та багато іншого.
📍Нетворкінг, Q&A зі спікерами, нові знайомства
📍Вайб Halloween під час офлайн частини заходу 👻
📍Розіграші та подарунки від партнерів

Формат: онлайн та офлайн у Києві

Використайте промокод POPOVREACT10 та отримайте знижку 10%, деталі за посиланням 👉 https://bit.ly/3TvYp6u

Приєднуйтесь до React+ fwdays'24!
Коротше, вирішив я проаналізувати архітектуру проєкту.
Часу, щоб самому її малювати, немає, та й лінь. Вирішив трохи пошукати інформацію, бо думаю, що таких, як я, багато, і рішення точно вже якесь є.
Коротше, по факту, все вийшло.
Результат на фото.

Він не ідеальний, але якщо трохи підкрутити, змінити порядок блоків і прибрати дублікати (так вийшло), то все буде взагалі дуже непогано.

Нижче декілька дописів із повним кодом і описом, що й як.
Частина 1

import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";
import { parse } from "@vue/compiler-sfc";
import { load } from "cheerio";
import glob from "glob";
import { exec } from "child_process";
import { promisify } from "util";

const execAsync = promisify(exec);
const globAsync = promisify(glob);

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const srcDir = path.join(__dirname, "src");

const componentsDTSPath = path.join("", "components.d.ts"); // Change the path if needed

const groupColors = ["red", "blue", "green", "orange", "purple", "yellow", "pink", "cyan"];

const sharedComponents = ["SVGIcons", "Button", "Input", "Tooltip", "Calendar", "ValidationErrorBlock"];

onst getGlobalComponents = (filePath) => {
const content = fs.readFileSync(filePath, "utf-8");
const globalComponents = [];
const regex = /(\w+): typeof import\(['"][^'"]+['"]\)\['default'\]/g;
let match;
while ((match = regex.exec(content)) !== null) {
globalComponents.push(match[1]);
}
return globalComponents;
};

const getUsedComponents = (templateContent, globalComponents) => {
const usedComponents = new Set();
const $ = load(templateContent, { xmlMode: true, decodeEntities: false });

const toKebabCase = (str) => str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();

$("*").each((_, elem) => {
const tagName = elem.tagName;
const possibleNames = [tagName, toKebabCase(tagName)];
possibleNames.forEach((name) => {
if (globalComponents.includes(name) || globalComponents.includes(toPascalCase(name))) {
usedComponents.add(name);
}
});
});

return Array.from(usedComponents).filter((comp) => !sharedComponents.includes(comp));
};

const toPascalCase = (str) =>
str
.split("-")
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join("");

const getComponentGroup = (relativePath) => {
const parts = relativePath.split(path.sep);
return parts.length > 1 ? parts[0] : "Others";
};
Частина 2

const generateArchitecture = async () => {
try {
if (!fs.existsSync(componentsDTSPath)) {
console.error(`File ${componentsDTSPath} not found.`);
return;
}

const globalComponents = getGlobalComponents(componentsDTSPath);
console.log("Globally registered components:", globalComponents);

const pattern = path.join(srcDir, "**/*.vue");
const vueFiles = await globAsync(pattern, { ignore: "**/node_modules/**" });

if (vueFiles.length === 0) {
console.warn("No Vue components found. Check the file path.");
return;
}

console.log(`Found ${vueFiles.length} Vue components.`);

const dependencyGraph = {};
const groups = {};

vueFiles.forEach((file) => {
const relativePath = path.relative(srcDir, file);
const componentName = path.basename(relativePath, ".vue");
dependencyGraph[componentName] = [];
const group = getComponentGroup(relativePath);
if (!groups[group]) {
groups[group] = [];
}
groups[group].push(componentName);
});

for (const file of vueFiles) {
const relativePath = path.relative(srcDir, file);
const componentName = path.basename(relativePath, ".vue");

const content = fs.readFileSync(file, "utf-8");
const { descriptor } = parse(content);

if (!descriptor.template) {
console.warn(`File ${file} does not contain a <template>.`);
continue;
}

const templateContent = descriptor.template.content;
const usedComponents = getUsedComponents(templateContent, globalComponents);

dependencyGraph[componentName] = usedComponents;
console.log(`Component: ${componentName}, Uses: ${usedComponents.join(", ")}`);
}

let dotContent = 'digraph G {\n node [shape=box];\n rankdir=LR;\n splines=true;\n';

Object.keys(groups).forEach((group, index) => {
const color = groupColors[index % groupColors.length];
dotContent += ` subgraph cluster_${index} {\n label="${group}";\n color="${color}";\n`;
groups[group].forEach((component) => {
dotContent += ` "${component}";\n`;
});
dotContent += " }\n";
});

Object.keys(dependencyGraph).forEach((component) => {
const dependencies = dependencyGraph[component];
if (dependencies.length > 0) {
dependencies.forEach((dep) => {
dotContent += ` "${component}" -> "${dep}";\n`;
});
}
});

dotContent += "}\n";

const dotDir = path.join(__dirname, "docs", "architecture");
const dotPath = path.join(dotDir, "architecture.dot");
fs.mkdirSync(dotDir, { recursive: true });
fs.writeFileSync(dotPath, dotContent, "utf-8");

console.log("\nFile architecture.dot successfully created.");

const outputPngPath = path.join(dotDir, "architecture.png");
const cmd = `dot -Tpng "${dotPath}" -o "${outputPngPath}"`;
const { stdout, stderr } = await execAsync(cmd);
if (stderr) {
console.error(`Graphviz stderr: ${stderr}`);
}

console.log(`Architecture diagram generated at: ${outputPngPath}`);
} catch (error) {
console.error(`Error generating architecture: ${error.message}`);
}
};

generateArchitecture();
Для роботи скрипта необхідно встановити наступні залежності:

npm install @vue/compiler-sfc cheerio glob js-cookie util


@vue/compiler-sfc — для парсингу .vue файлів
cheerio — для роботи з HTML (розбір шаблонів Vue)
glob — для пошуку файлів у файловій системі
js-cookie — для роботи з кукі (якщо використовується у застосунку)
util — для промисифікації функцій.

Для генерації графу потрібен Graphviz

На macOS

brew install graphviz


На Ubuntu/Debian/Windows треба пошукати 😅

У мене весь проект лежить в src
А ще у мене є components.d.ts для автоматичного підкючення компонентів

По суті той скрипт що вище сканує всі .vue файли в папці src, потім визначає залежності між компонентами. Створює файл architecture.dot, який використовується для генерації графу.

Приклад результату
Якщо структура вашого проєкту виглядає так:

src/
├── components/
│ ├── Button.vue
│ ├── Modal.vue
├── pages/
│ ├── Home.vue
│ ├── Dashboard.vue
├── shared/
│ ├── Header.vue
│ ├── Footer.vue


То граф архітектури виглядатиме приблизно так:

Групи:
components: Modal, Button.
pages: Home, Dashboard.
shared: Header, Footer.

Залежності:
Home → Header.
Dashboard → Header, Footer.
Modal → Button.

Короче у підсумку виходить дуже не поганий інструмент для візуалізації архітектури

Він по суті може допогти
1. Швидко розуміти залежності між компонентами.
2. Визначати зайві зв'язки.
3. Покращувати модульність застосунку.
12 бібліотек, які вам можуть стати у нагоді 😉

AOS (Animate on Scroll) — це легка та налаштовувана бібліотека для додавання ефектних анімацій при прокручуванні ваших вебсторінок.
https://michalsnik.github.io/aos

Chart.js - дозволяє легко створювати різні типи графіків, включаючи лінійні, стовпчасті, радарні та кругові, з мінімальною конфігурацією.
https://www.chartjs.org

SweetAlert2 пропонує адаптивні, повністю налаштовувані спливаючі вікна, які легко інтегруються у ваші проєкти.
https://sweetalert2.github.io

SortableJS дозволяє легко реалізувати функцію перетягування для списків. Її гнучкість робить її ідеальною для створення інтерактивних інтерфейсів, таких як канбан-дошки.
https://sortablejs.github.io/Sortable

Floating UI дозволяє легко керувати складними компонентами інтерфейсу, такими як підказки, випадаючі меню та спливаючі вікна.
https://floating-ui.com

FullCalendar пропонує зручний, повнофункціональний інтерфейс календаря з налаштовуваним керуванням подіями, функцією перетягування та різними режимами перегляду.
https://fullcalendar.io

Animate.css - додавайте попередньо створені анімації до елементів вашого сайту за допомогою Animate.css.
https://animate.style

Lottie від Airbnb - Ця бібліотека дозволяє інтегрувати високоякісні анімації у ваші вебдодатки.
https://lottiefiles.com/free-animations/airbnb

Tippy.js - Легка, розширювана бібліотека для створення красивих, налаштовуваних підказок, спливаючих вікон і випадаючих меню.
https://atomiks.github.io/tippyjs

Day.js — це проста й швидка бібліотека, яка чудово підходить для проєктів, що працюють з датами, не займаючи багато місця та не уповільнюючи роботу.
https://day.js.org

Swiper — це безкоштовний і потужний інструмент для створення слайдерів і каруселей. Він забезпечує плавні переходи й добре працює на різних екранах, що робить його ідеальним для мобільних проєктів.
https://swiperjs.com

Vivus - Створюйте приголомшливі анімовані SVG-малюнки за допомогою Vivus. Ця легка JavaScript-бібліотека дозволяє анімувати SVG, імітуючи процес малювання, додаючи унікальний стиль вашим графікам.
https://maxwellito.github.io/vivus
Channel name was changed to «Frontend Den»
26 листопада був реліз Vite 6
Про нові зміни можна почитати тут
Якщо коротко, то:
- Деякі умови більше не додаються автоматично і мають бути вказані в конфігурації.
- JSON.stringify тепер за замовчуванням працює в режимі 'auto'
- За замовчуванням використовується сучасний API.
- У бібліотечному режимі ім'я CSS-файлу тепер залежить від назви пакету.
Якщо ви цікавитесь Vue як і я, ось вам цікавенька стаття де розповідають як побудувати веб апплікейшин який видаляє фон за допомогою Vue та Transformers.js
Зробив із ноутбука власний сервер.

Хотів я арендувати VPS для себе щоб на ньому розмістити свої проекти. Довго шукав, читав, питав і так і не зміг обрати найкращий. А потім я згадав що у мене є ноутбук, який лежить без діла. Тому склавши такі речі як: ноутбук без діла + бажання розібратись як підняті свій сервер, я почав його підіймати

1️⃣ Спочатку встановив Ubuntu Server ( останню версію Ubuntu Server можна завантажити з офіційного сайту )

При встановленні там все інтуітивно зрозуміло.
Єдино що: я обрав мінімальну конфігурацію, одразу під час встановлення додав OpenSSH Server.

2️⃣ Далі після встановлення я новоспечений сервер залишив на столі і пішов вже сів за свій ноут. До серверу підключивя через SSH через локальну мережу.

Як це зробити
Дізнайтеся локальну IP-адресу сервера
ip a

Приклад виводу: 192.168.1.100 — ваш локальний IP.

Щоб підключитися
ssh username@192.168.1.100


Далі вас попросе пароль.
Введіть його. Ну і якщо правльно ввели, то все, ви зайшли на свій сервер

3️⃣ Далі налаштування фаєрволу (UFW)

Налаштуйте захист сервера, дозволивши лише потрібні порти.

Встановіть UFW:
sudo apt update
sudo apt install ufw


Дозвольте доступ для SSH та веб-сервера:
sudo ufw allow OpenSSH
sudo ufw allow 80
sudo ufw allow 22


Увімкніть фаєрвол:
sudo ufw enable


Перевірка статусу
sudo ufw status


4️⃣ Встановлення PHP та MySQL

Встановіть PHP та необхідні модулі:
sudo apt install php php-fpm php-mysql


Встановіть MySQL
sudo apt install mysql-server


Налаштуйте MySQL для безпечної роботи (слідуйте інструкціям):
sudo mysql_secure_installation


sudo ufw status


5️⃣ Встановлення веб-сервера Nginx

sudo apt install nginx


У браузері на іншому пристрої введіть локальну IP-адресу сервера, наприклад http://192.168.1.100

6️⃣ Налаштування Nginx для роботи з PHP

sudo nano /etc/nginx/sites-available/example


Сам конфіг такий
server {
listen 80;
server_name <ваш_білий_IP>;

root /var/www/html;
index index.php index.html index.htm;

location / {
try_files $uri $uri/ =404;
}

location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.1-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}

location ~ /\.ht {
deny all;
}
}


Створюєм симлінк для активації конфігурації:
sudo ln -s /etc/nginx/sites-available/example /etc/nginx/sites-enabled/


Рестартім nginx
sudo nginx -t
sudo systemctl reload nginx


7️⃣ Потім щоб все працювало рівно мені довелось купити у свого провайдера білий айпі. Це коштує 30 грн/місяць. У вашого провайдера це може бути взашалі безкоштовно

І коли у вас є білий айпи, і якщо ви пидєднуєте ваш сервер до мережі інтернет через роутер, треба налаштувати проброс портів на ньому ( роутері ).

У мене в налаштуваннях роутера є така вкладка як Виртуальный сервер
і там вказую зовнішній порт ( 80 ), внутрішній порт ( 80 ), локальну айпі адресу ( 192.168.1.100 ) і протокол ( TCP )

і потім треба зробити ще одну таку запис для SSH
овнішній порт ( 20 ), внутрішній порт ( 20 ), локальну айпі адресу ( 192.168.1.100 ) і протокол ( ALL )

Сам зовнішній айпі можна дізнатися так
curl ifconfig.me


8️⃣ Розміщення файлів і все таке відбувається тут /var/www/html

Ту сторінку що ви бачите коли перйший раз заходите можете знайти тут
sudo nano /var/www/html/index.html


Це поки все що я зробив 🙂

Далі хочу розгорнути сайт на Wordpress
Купити якийсь дешевий домен і привязати його
А ще, Налаштування HTTPS

Поки все 🙃
#CSS шпаргалка яка може стати у нагоді, особливо тим хто тільки починає

#frontend #html #розробка #фронтенд
https://una.im/advanced-attr/ - Нові можливості attr()

Цікава стаття.
Точно вартої вашої уваги бо впевнений що рано чи пізно у вас буде кейс де attr() вас спасе 😉


https://css-tricks.com/positioning-text-around-elements-with-css-offset/ - Розташування тексту навколо елементів із зсувом CSS. Виглядає дуже гарно

https://css-tricks.com/some-things-you-might-not-know-about-custom-counter-styles/ - Деякі речі, які ви можете не знати про спеціальні стилі лічильників
Всім првиіт
Стартувало друге голосування на Премію Доу
Я увійшов у шорт ліст серед кандитатів в категоріі "Вони – надихають"
Тому, якщо у вас є трохи хвилинок, зайдіть сюди https://dou.ua/awards-2025/ і залиште будь ласка свій голос 😉