Ласкаво просимо до "Frontend Den" - каналу у Telegram для початківців веб-розробників та всіх, хто цікавиться цією сферою. Якщо ви шукаєте цікаву та корисну інформацію про frontend, WordPress, HTML, CSS, то ви потрапили за адресою. Канал створено спеціально для навчання та обміну досвідом в галузі веб-розробки. Тут ви знайдете багато корисних порад, новин та можливість зв'язатися з адміністраторами для отримання консультації чи запропонувати цікавий контент. Присоеднуйтесь до нас, щоб бути в курсі всіх оновлень та розвивати свої навички разом з іншими учасниками каналу. Долучайтеся до обговорень та спілкуйтесь з однодумцями у чаті @junnot_chat. Для зв'язку з адміністратором або надсилання новин звертайтеся за контактами @denyspopov_web. Приєднуйтесь до нас, вивчайте нове та розвивайтесь у сфері веб-розробки разом з "Frontend Den"!
15 Jan, 13:04
09 Jan, 09:25
12 Dec, 17:42
ip a
ssh [email protected]
sudo apt update
sudo apt install ufw
sudo ufw allow OpenSSH
sudo ufw allow 80
sudo ufw allow 22
sudo ufw enable
sudo ufw status
sudo apt install php php-fpm php-mysql
sudo apt install mysql-server
sudo mysql_secure_installation
sudo ufw status
sudo apt install nginx
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/
sudo nginx -t
sudo systemctl reload nginx
curl ifconfig.me
sudo nano /var/www/html/index.html
10 Dec, 08:35
09 Dec, 12:23
05 Dec, 11:43
04 Dec, 11:11
npm install @vue/compiler-sfc cheerio glob js-cookie util
brew install graphviz
src/
├── components/
│ ├── Button.vue
│ ├── Modal.vue
├── pages/
│ ├── Home.vue
│ ├── Dashboard.vue
├── shared/
│ ├── Header.vue
│ ├── Footer.vue
04 Dec, 10:59
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();
04 Dec, 10:56
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";
};
04 Dec, 10:54
17 Sep, 07:34
02 Jul, 10:53
01 Jul, 10:53
30 Jun, 10:52
29 Jun, 10:51
28 Jun, 15:16
27 Jun, 15:16
27 Jun, 10:40
10 Jun, 12:21
06 Jun, 14:15