core-maugli 1.2.3 → 1.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -0
- package/package.json +6 -13
- package/public/img/examples/blog/previews/post-1-avtomatizaciya-marketinga-kak-ii-osvobozhdaet-predprinimatelei-ot-cifrovogo-rabstva.webp +0 -0
- package/public/img/examples/blog/previews/post-2-avtomatizaciya-kontenta-kak-neiroseti-ubivayut-perfekcionizm-v-biznese.webp +0 -0
- package/public/img/examples/blog/previews/post-3-laik-ne-valyuta-kak-avtomatizaciya-marketinga-spasaet-ot-lozhnyh-metrik.webp +0 -0
- package/public/img/examples/blog/previews/post-5-5-fatalnyh-oshibok-marketinga-kotorye-ubivayut-startapy-na-starte.webp +0 -0
- package/public/img/examples/blog/previews/post-6-5-strategii-kontent-marketinga-dlya-startapov-avtomatizaciya-i-revolyuciya.webp +0 -0
- package/public/img/examples/blog/previews/post-7-viralnyi-kontent-ne-udacha-a-strategiya-avtomatizaciya-marketinga.webp +0 -0
- package/public/img/examples/blog/previews/post-agent-experience-mcp-biznes-v-epohu-ii-agentov.webp +0 -0
- package/public/img/examples/blog/previews/post_11.webp +0 -0
- package/public/img/examples/blog/previews/post_12.webp +0 -0
- package/public/img/examples/blog/previews/post_1_jsonld_guide.webp +0 -0
- package/public/img/examples/blog/previews/test-post.webp +0 -0
- package/public/img/examples/blog/previews/tr-post-1.webp +0 -0
- package/public/img/examples/products/previews/product_1.webp +0 -0
- package/public/img/examples/products/previews/product_2.webp +0 -0
- package/public/img/examples/projects/previews/project_1.webp +0 -0
- package/public/img/examples/projects/previews/project_2.webp +0 -0
- package/scripts/generate-previews.js +175 -0
- package/scripts/update-components.js +166 -0
- package/scripts/upgrade-config.js +23 -3
- package/src/components/LanguageSwitcher.astro +2 -16
- package/astro.config.mjs +0 -92
- package/bin/init.js +0 -201
- package/public/img/default/autor_default.webp +0 -0
- package/public/img/default/blog_default.webp +0 -0
- package/public/img/default/default.webp +0 -0
- package/public/img/default/product_default.webp +0 -0
- package/public/img/default/project_default.webp +0 -0
- package/public/img/default/rubric_default.webp +0 -0
- package/public/img/default/test.webp +0 -0
- package/public/img/default/test2.webp +0 -0
- package/resize-all.cjs +0 -29
- package/src/i18n/de.json +0 -126
- package/src/i18n/en.json +0 -126
- package/src/i18n/es.json +0 -126
- package/src/i18n/fr.json +0 -126
- package/src/i18n/index.ts +0 -10
- package/src/i18n/ja.json +0 -126
- package/src/i18n/languages.ts +0 -23
- package/src/i18n/pt.json +0 -126
- package/src/i18n/ru.json +0 -123
- package/src/i18n/zh.json +0 -126
- package/src/icons/ArrowLeft.astro +0 -13
- package/src/icons/ArrowRight.astro +0 -13
- package/src/icons/flags/brazil.svg +0 -14
- package/src/icons/flags/china.svg +0 -15
- package/src/icons/flags/france.svg +0 -12
- package/src/icons/flags/germany.svg +0 -12
- package/src/icons/flags/japan.svg +0 -11
- package/src/icons/flags/russia.svg +0 -12
- package/src/icons/flags/spain.svg +0 -12
- package/src/icons/flags/united arab emirates.svg +0 -13
- package/src/icons/flags/united states.svg +0 -15
- package/src/icons/socials/BlueskyIcon.astro +0 -9
- package/src/icons/socials/EmailIcon.astro +0 -8
- package/src/icons/socials/LinkedinIcon.astro +0 -9
- package/src/icons/socials/MastodonIcon.astro +0 -9
- package/src/icons/socials/MediumIcon.astro +0 -9
- package/src/icons/socials/RedditIcon.astro +0 -11
- package/src/icons/socials/TelegramIcon.astro +0 -11
- package/src/icons/socials/TwitterIcon.astro +0 -9
- package/src/layouts/BaseLayout.astro +0 -59
- package/src/pages/404.astro +0 -24
- package/src/pages/[...id].astro +0 -50
- package/src/pages/about.astro +0 -0
- package/src/pages/authors/[...page].astro +0 -105
- package/src/pages/authors/[id].astro +0 -175
- package/src/pages/blog/[...page].astro +0 -59
- package/src/pages/blog/[id].astro +0 -175
- package/src/pages/index.astro +0 -90
- package/src/pages/products/[...page].astro +0 -50
- package/src/pages/products/[id].astro +0 -221
- package/src/pages/projects/[...page].astro +0 -74
- package/src/pages/projects/[id].astro +0 -165
- package/src/pages/projects/tags/[id]/[...page].astro +0 -58
- package/src/pages/rss.xml.js +0 -5
- package/src/pages/tags/[id]/[...page].astro +0 -110
- package/src/pages/tags/index.astro +0 -124
- package/src/scripts/infoCardFadeIn.js +0 -22
- package/src/styles/global.css +0 -273
- package/src/utils/common-utils.ts +0 -0
- package/src/utils/content-loader.ts +0 -14
- package/src/utils/data-utils.ts +0 -49
- package/src/utils/featuredManager.ts +0 -118
- package/src/utils/posts.ts +0 -43
- package/src/utils/reading-time.ts +0 -28
- package/src/utils/remark-slugify.js +0 -8
- package/src/utils/rss.ts +0 -23
- package/tsconfig.json +0 -8
- package/typograf-batch.js +0 -49
- package/vite.config.js +0 -11
package/README.md
CHANGED
@@ -87,6 +87,19 @@ Your blog will be available at `http://localhost:4321/`
|
|
87
87
|
npm run build
|
88
88
|
```
|
89
89
|
|
90
|
+
## Component Updates & Customization
|
91
|
+
|
92
|
+
**Important**: Maugli Blog is designed for centralized component updates. All components (`src/components/`, `src/layouts/`, `src/pages/`, etc.) are automatically updated to the latest version when you update the package with `npm install --save core-maugli@latest`. This ensures you always receive the latest features, bug fixes, and improvements.
|
93
|
+
|
94
|
+
This centralized update approach **does not affect**:
|
95
|
+
|
96
|
+
- Your content (`src/content/`)
|
97
|
+
- Your Maugli configuration (`src/config/maugli.config.ts`)
|
98
|
+
- Your custom styles (`src/styles/global.css` - preserved if customized)
|
99
|
+
- Your project settings (`package.json`, `astro.config.mjs`, etc.)
|
100
|
+
|
101
|
+
Only the core blog components are updated, while your customizations and settings remain intact.
|
102
|
+
|
90
103
|
`npm run build` runs [`scripts/verify-assets.js`](scripts/verify-assets.js)
|
91
104
|
before the Astro build. This script checks the SHA-256 hashes of the
|
92
105
|
floating label component and footer badge to ensure they haven't been
|
package/package.json
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
"name": "core-maugli",
|
3
3
|
"description": "Astro & Tailwind CSS blog theme for Maugli.",
|
4
4
|
"type": "module",
|
5
|
-
"version": "1.2.
|
5
|
+
"version": "1.2.4",
|
6
6
|
"license": "GPL-3.0-or-later OR Commercial",
|
7
7
|
"repository": {
|
8
8
|
"type": "git",
|
@@ -21,15 +21,17 @@
|
|
21
21
|
"typograf": "node typograf-batch.js",
|
22
22
|
"dev": "astro dev",
|
23
23
|
"start": "astro dev",
|
24
|
-
"build": "node typograf-batch.js && node scripts/verify-assets.js && astro build",
|
24
|
+
"build": "node typograf-batch.js && node scripts/generate-previews.js && node scripts/verify-assets.js && astro build",
|
25
25
|
"test": "node tests/examplesFilter.test.ts",
|
26
26
|
"astro": "astro",
|
27
27
|
"featured:add": "node scripts/featured.js add",
|
28
28
|
"featured:remove": "node scripts/featured.js remove",
|
29
29
|
"featured:list": "node scripts/featured.js list",
|
30
30
|
"upgrade": "node scripts/upgrade-config.js",
|
31
|
+
"update-components": "node scripts/update-components.js",
|
31
32
|
"backup-update": "node scripts/update-with-backup.js",
|
32
|
-
"postinstall": "node scripts/upgrade-config.js"
|
33
|
+
"postinstall": "node scripts/upgrade-config.js",
|
34
|
+
"generate-previews": "node scripts/generate-previews.js"
|
33
35
|
},
|
34
36
|
"dependencies": {
|
35
37
|
"@astrojs/mdx": "^4.3.0",
|
@@ -61,17 +63,8 @@
|
|
61
63
|
"files": [
|
62
64
|
"src",
|
63
65
|
"public",
|
64
|
-
"scripts"
|
65
|
-
"typograf-batch.js",
|
66
|
-
"resize-all.cjs",
|
67
|
-
"bin",
|
68
|
-
"astro.config.mjs",
|
69
|
-
"tsconfig.json",
|
70
|
-
"vite.config.js",
|
71
|
-
"LICENSE",
|
72
|
-
"README.md"
|
66
|
+
"scripts"
|
73
67
|
],
|
74
|
-
"main": "bin/index.js",
|
75
68
|
"bin": {
|
76
69
|
"core-maugli": "bin/index.js"
|
77
70
|
}
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
package/public/img/examples/blog/previews/post-agent-experience-mcp-biznes-v-epohu-ii-agentov.webp
ADDED
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,175 @@
|
|
1
|
+
import fs from 'fs';
|
2
|
+
import path from 'path';
|
3
|
+
import sharp from 'sharp';
|
4
|
+
import { fileURLToPath } from 'url';
|
5
|
+
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
7
|
+
const __dirname = path.dirname(__filename);
|
8
|
+
|
9
|
+
// Универсальное определение корня проекта
|
10
|
+
const rootDir = __dirname.includes('node_modules')
|
11
|
+
? path.join(__dirname, '../../..')
|
12
|
+
: path.join(__dirname, '..');
|
13
|
+
|
14
|
+
const previewWidth = 400;
|
15
|
+
const previewHeight = 210;
|
16
|
+
|
17
|
+
// Функция для извлечения путей изображений из markdown файлов
|
18
|
+
function extractImagePaths() {
|
19
|
+
const imagePaths = new Set();
|
20
|
+
const contentDir = path.join(rootDir, 'src/content');
|
21
|
+
|
22
|
+
function scanDirectory(dir) {
|
23
|
+
if (!fs.existsSync(dir)) return;
|
24
|
+
|
25
|
+
const items = fs.readdirSync(dir);
|
26
|
+
|
27
|
+
for (const item of items) {
|
28
|
+
const itemPath = path.join(dir, item);
|
29
|
+
const stat = fs.statSync(itemPath);
|
30
|
+
|
31
|
+
if (stat.isDirectory()) {
|
32
|
+
scanDirectory(itemPath);
|
33
|
+
} else if (item.endsWith('.md') || item.endsWith('.mdx')) {
|
34
|
+
const content = fs.readFileSync(itemPath, 'utf-8');
|
35
|
+
|
36
|
+
// Извлекаем изображения из frontmatter (image: путь)
|
37
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
38
|
+
if (frontmatterMatch) {
|
39
|
+
const frontmatter = frontmatterMatch[1];
|
40
|
+
// Улучшенные регулярки для frontmatter
|
41
|
+
const imageMatches = frontmatter.match(/(?:^|\n)\s*(?:image|src):\s*['"]*([^'">\s\n]+)['"]*(?:\s|$)/g);
|
42
|
+
if (imageMatches) {
|
43
|
+
imageMatches.forEach(match => {
|
44
|
+
const imagePath = match.replace(/.*?(?:image|src):\s*['"]*/, '').replace(/['"]*\s*$/, '');
|
45
|
+
if (imagePath && !imagePath.startsWith('http') && imagePath.includes('/') && !imagePath.includes('authors/')) {
|
46
|
+
imagePaths.add(imagePath);
|
47
|
+
}
|
48
|
+
});
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
52
|
+
// Извлекаем изображения из содержимого markdown ( и <img src="путь">)
|
53
|
+
const imgMatches = content.match(/!\[.*?\]\(([^)]+)\)|<img[^>]+src\s*=\s*['"]*([^'">\s]+)['"]*[^>]*>/g);
|
54
|
+
if (imgMatches) {
|
55
|
+
imgMatches.forEach(match => {
|
56
|
+
let imagePath;
|
57
|
+
if (match.startsWith('![')) {
|
58
|
+
imagePath = match.match(/!\[.*?\]\(([^)]+)\)/)?.[1];
|
59
|
+
} else {
|
60
|
+
imagePath = match.match(/src\s*=\s*['"]*([^'">\s]+)['"]*/)?.[1];
|
61
|
+
}
|
62
|
+
if (imagePath && !imagePath.startsWith('http') && imagePath.includes('/') && !imagePath.includes('authors/')) {
|
63
|
+
imagePaths.add(imagePath);
|
64
|
+
}
|
65
|
+
});
|
66
|
+
}
|
67
|
+
}
|
68
|
+
}
|
69
|
+
}
|
70
|
+
|
71
|
+
scanDirectory(contentDir);
|
72
|
+
|
73
|
+
// Также добавляем изображения из public/img/examples/ кроме авторов
|
74
|
+
const examplesDir = path.join(rootDir, 'public/img/examples');
|
75
|
+
if (fs.existsSync(examplesDir)) {
|
76
|
+
function addExampleImages(dir, relativePath = '') {
|
77
|
+
// Пропускаем папку authors - аватары не нужно обрабатывать
|
78
|
+
if (relativePath.includes('authors/')) return;
|
79
|
+
|
80
|
+
const items = fs.readdirSync(dir);
|
81
|
+
for (const item of items) {
|
82
|
+
const itemPath = path.join(dir, item);
|
83
|
+
const stat = fs.statSync(itemPath);
|
84
|
+
|
85
|
+
if (stat.isDirectory()) {
|
86
|
+
// Пропускаем папку authors
|
87
|
+
if (item === 'authors') continue;
|
88
|
+
addExampleImages(itemPath, `${relativePath}${item}/`);
|
89
|
+
} else if (item.match(/\.(webp|jpg|jpeg|png)$/i) && !dir.includes('previews')) {
|
90
|
+
imagePaths.add(`/img/examples/${relativePath}${item}`);
|
91
|
+
}
|
92
|
+
}
|
93
|
+
}
|
94
|
+
addExampleImages(examplesDir);
|
95
|
+
}
|
96
|
+
|
97
|
+
return Array.from(imagePaths);
|
98
|
+
}
|
99
|
+
|
100
|
+
// Функция для очистки всех существующих превьюшек
|
101
|
+
function cleanupExistingPreviews() {
|
102
|
+
const publicDir = path.join(rootDir, 'public');
|
103
|
+
|
104
|
+
function removePreviewDirs(dir) {
|
105
|
+
if (!fs.existsSync(dir)) return;
|
106
|
+
|
107
|
+
const items = fs.readdirSync(dir);
|
108
|
+
|
109
|
+
for (const item of items) {
|
110
|
+
const itemPath = path.join(dir, item);
|
111
|
+
const stat = fs.statSync(itemPath);
|
112
|
+
|
113
|
+
if (stat.isDirectory()) {
|
114
|
+
if (item === 'previews') {
|
115
|
+
console.log(`Removing existing preview directory: ${itemPath}`);
|
116
|
+
fs.rmSync(itemPath, { recursive: true, force: true });
|
117
|
+
} else {
|
118
|
+
removePreviewDirs(itemPath);
|
119
|
+
}
|
120
|
+
}
|
121
|
+
}
|
122
|
+
}
|
123
|
+
|
124
|
+
removePreviewDirs(publicDir);
|
125
|
+
}
|
126
|
+
|
127
|
+
// Функция для создания превьюшки
|
128
|
+
async function createPreview(imagePath) {
|
129
|
+
const fullImagePath = path.join(rootDir, 'public', imagePath.replace(/^\//, ''));
|
130
|
+
|
131
|
+
if (!fs.existsSync(fullImagePath)) {
|
132
|
+
console.warn(`Image not found: ${fullImagePath}`);
|
133
|
+
return;
|
134
|
+
}
|
135
|
+
|
136
|
+
const dir = path.dirname(fullImagePath);
|
137
|
+
const ext = path.extname(fullImagePath);
|
138
|
+
const name = path.basename(fullImagePath, ext);
|
139
|
+
const previewPath = path.join(dir, 'previews', `${name}${ext}`);
|
140
|
+
|
141
|
+
// Создаем папку previews если её нет
|
142
|
+
const previewDir = path.dirname(previewPath);
|
143
|
+
if (!fs.existsSync(previewDir)) {
|
144
|
+
fs.mkdirSync(previewDir, { recursive: true });
|
145
|
+
}
|
146
|
+
|
147
|
+
try {
|
148
|
+
await sharp(fullImagePath)
|
149
|
+
.resize(previewWidth, previewHeight, { fit: 'cover' })
|
150
|
+
.toFile(previewPath);
|
151
|
+
|
152
|
+
console.log(`Preview created: ${previewPath}`);
|
153
|
+
} catch (error) {
|
154
|
+
console.error(`Error processing ${fullImagePath}:`, error.message);
|
155
|
+
}
|
156
|
+
}
|
157
|
+
|
158
|
+
// Основная функция
|
159
|
+
async function generatePreviews() {
|
160
|
+
console.log('Cleaning up existing previews...');
|
161
|
+
cleanupExistingPreviews();
|
162
|
+
|
163
|
+
console.log('Scanning content for images...');
|
164
|
+
const imagePaths = extractImagePaths();
|
165
|
+
|
166
|
+
console.log(`Found ${imagePaths.length} images to process`);
|
167
|
+
|
168
|
+
for (const imagePath of imagePaths) {
|
169
|
+
await createPreview(imagePath);
|
170
|
+
}
|
171
|
+
|
172
|
+
console.log('Preview generation completed!');
|
173
|
+
}
|
174
|
+
|
175
|
+
generatePreviews().catch(console.error);
|
@@ -0,0 +1,166 @@
|
|
1
|
+
#!/usr/bin/env node
|
2
|
+
|
3
|
+
import fs from 'fs/promises';
|
4
|
+
import path from 'path';
|
5
|
+
import { fileURLToPath, pathToFileURL } from 'url';
|
6
|
+
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
8
|
+
const __dirname = path.dirname(__filename);
|
9
|
+
|
10
|
+
// Определяем корневые папки
|
11
|
+
const packageRoot = __dirname.includes('node_modules')
|
12
|
+
? path.join(__dirname, '../../..', 'node_modules', 'core-maugli') // из node_modules
|
13
|
+
: path.join(__dirname, '..'); // из исходников (для разработки)
|
14
|
+
|
15
|
+
const userRoot = __dirname.includes('node_modules')
|
16
|
+
? path.join(__dirname, '../../..') // корень пользовательского проекта
|
17
|
+
: process.env.INIT_CWD || process.cwd(); // для разработки
|
18
|
+
|
19
|
+
// Список папок и файлов для полного обновления (перезаписи)
|
20
|
+
const FORCE_UPDATE_PATHS = [
|
21
|
+
'src/components',
|
22
|
+
'src/layouts',
|
23
|
+
'src/pages',
|
24
|
+
'src/utils',
|
25
|
+
'src/scripts',
|
26
|
+
'src/icons',
|
27
|
+
'src/i18n',
|
28
|
+
'public/flags',
|
29
|
+
'public/img/default'
|
30
|
+
// Исключили src/styles - может содержать пользовательские стили
|
31
|
+
];
|
32
|
+
|
33
|
+
// Список файлов, которые НЕ должны перезаписываться (пользовательские)
|
34
|
+
const PRESERVE_PATHS = [
|
35
|
+
'src/content',
|
36
|
+
'src/config/maugli.config.ts', // обновляется через upgrade-config.js
|
37
|
+
'src/styles/global.css', // может быть кастомизирован пользователем
|
38
|
+
'package.json',
|
39
|
+
'astro.config.mjs',
|
40
|
+
'tailwind.config.js',
|
41
|
+
'tsconfig.json'
|
42
|
+
];
|
43
|
+
|
44
|
+
async function copyDirectory(src, dest) {
|
45
|
+
try {
|
46
|
+
await fs.mkdir(dest, { recursive: true });
|
47
|
+
|
48
|
+
const entries = await fs.readdir(src, { withFileTypes: true });
|
49
|
+
|
50
|
+
for (const entry of entries) {
|
51
|
+
const srcPath = path.join(src, entry.name);
|
52
|
+
const destPath = path.join(dest, entry.name);
|
53
|
+
|
54
|
+
if (entry.isDirectory()) {
|
55
|
+
await copyDirectory(srcPath, destPath);
|
56
|
+
} else {
|
57
|
+
await fs.copyFile(srcPath, destPath);
|
58
|
+
console.log(`Updated: ${path.relative(userRoot, destPath)}`);
|
59
|
+
}
|
60
|
+
}
|
61
|
+
} catch (error) {
|
62
|
+
console.warn(`Warning: Could not copy ${src} to ${dest}:`, error.message);
|
63
|
+
}
|
64
|
+
}
|
65
|
+
|
66
|
+
async function updateStyles() {
|
67
|
+
const srcStylesPath = path.join(packageRoot, 'src/styles');
|
68
|
+
const destStylesPath = path.join(userRoot, 'src/styles');
|
69
|
+
|
70
|
+
try {
|
71
|
+
// Проверяем, существует ли папка styles в пакете
|
72
|
+
await fs.stat(srcStylesPath);
|
73
|
+
|
74
|
+
// Проверяем, есть ли уже пользовательские стили
|
75
|
+
try {
|
76
|
+
const userGlobalCss = path.join(destStylesPath, 'global.css');
|
77
|
+
await fs.stat(userGlobalCss);
|
78
|
+
console.log('📝 Preserving user styles (global.css exists)');
|
79
|
+
|
80
|
+
// Копируем только новые файлы стилей, не трогая global.css
|
81
|
+
const entries = await fs.readdir(srcStylesPath, { withFileTypes: true });
|
82
|
+
await fs.mkdir(destStylesPath, { recursive: true });
|
83
|
+
|
84
|
+
for (const entry of entries) {
|
85
|
+
if (entry.name !== 'global.css') {
|
86
|
+
const srcFile = path.join(srcStylesPath, entry.name);
|
87
|
+
const destFile = path.join(destStylesPath, entry.name);
|
88
|
+
await fs.copyFile(srcFile, destFile);
|
89
|
+
console.log(`Updated style: ${entry.name}`);
|
90
|
+
}
|
91
|
+
}
|
92
|
+
} catch {
|
93
|
+
// Пользовательских стилей нет, копируем все
|
94
|
+
await copyDirectory(srcStylesPath, destStylesPath);
|
95
|
+
console.log('📝 Copied default styles');
|
96
|
+
}
|
97
|
+
} catch (error) {
|
98
|
+
console.warn('Warning: Could not update styles:', error.message);
|
99
|
+
}
|
100
|
+
}
|
101
|
+
|
102
|
+
async function updateComponents() {
|
103
|
+
console.log('🔄 Updating Maugli components and assets...');
|
104
|
+
|
105
|
+
// Проверяем, что мы не в том же проекте (чтобы не удалить исходники)
|
106
|
+
if (packageRoot === userRoot) {
|
107
|
+
console.log('⚠️ Skipping component update (running in source project)');
|
108
|
+
return;
|
109
|
+
}
|
110
|
+
|
111
|
+
let updatedCount = 0;
|
112
|
+
|
113
|
+
for (const updatePath of FORCE_UPDATE_PATHS) {
|
114
|
+
const srcPath = path.join(packageRoot, updatePath);
|
115
|
+
const destPath = path.join(userRoot, updatePath);
|
116
|
+
|
117
|
+
try {
|
118
|
+
// Проверяем, существует ли исходная папка/файл
|
119
|
+
const stats = await fs.stat(srcPath);
|
120
|
+
|
121
|
+
if (stats.isDirectory()) {
|
122
|
+
// Удаляем существующую папку и копируем новую
|
123
|
+
try {
|
124
|
+
await fs.rm(destPath, { recursive: true, force: true });
|
125
|
+
} catch (e) {
|
126
|
+
// Папки может не быть - это нормально
|
127
|
+
}
|
128
|
+
|
129
|
+
await copyDirectory(srcPath, destPath);
|
130
|
+
updatedCount++;
|
131
|
+
} else if (stats.isFile()) {
|
132
|
+
// Копируем отдельный файл
|
133
|
+
await fs.mkdir(path.dirname(destPath), { recursive: true });
|
134
|
+
await fs.copyFile(srcPath, destPath);
|
135
|
+
console.log(`Updated: ${path.relative(userRoot, destPath)}`);
|
136
|
+
updatedCount++;
|
137
|
+
}
|
138
|
+
} catch (error) {
|
139
|
+
if (error.code !== 'ENOENT') {
|
140
|
+
console.warn(`Warning: Could not update ${updatePath}:`, error.message);
|
141
|
+
}
|
142
|
+
}
|
143
|
+
}
|
144
|
+
|
145
|
+
// Обрабатываем стили отдельно
|
146
|
+
await updateStyles();
|
147
|
+
|
148
|
+
console.log(`✅ Updated ${updatedCount} component directories/files`);
|
149
|
+
}
|
150
|
+
|
151
|
+
async function main() {
|
152
|
+
try {
|
153
|
+
await updateComponents();
|
154
|
+
console.log('🎉 Component update completed successfully!');
|
155
|
+
} catch (error) {
|
156
|
+
console.error('❌ Component update failed:', error);
|
157
|
+
process.exit(1);
|
158
|
+
}
|
159
|
+
}
|
160
|
+
|
161
|
+
// Запускаем только если вызывается напрямую
|
162
|
+
if (import.meta.url === pathToFileURL(process.argv[1]).href) {
|
163
|
+
main();
|
164
|
+
}
|
165
|
+
|
166
|
+
export { updateComponents };
|
@@ -1,14 +1,25 @@
|
|
1
1
|
#!/usr/bin/env node
|
2
2
|
|
3
3
|
import fs from 'fs/promises';
|
4
|
-
import path from 'path';
|
5
4
|
import os from 'os';
|
6
|
-
import
|
5
|
+
import path from 'path';
|
7
6
|
import ts from 'typescript';
|
7
|
+
import { fileURLToPath, pathToFileURL } from 'url';
|
8
8
|
|
9
9
|
const __filename = fileURLToPath(import.meta.url);
|
10
10
|
const __dirname = path.dirname(__filename);
|
11
11
|
|
12
|
+
// Импортируем функцию обновления компонентов
|
13
|
+
async function importUpdateComponents() {
|
14
|
+
try {
|
15
|
+
const { updateComponents } = await import('./update-components.js');
|
16
|
+
return updateComponents;
|
17
|
+
} catch (error) {
|
18
|
+
console.warn('Could not load update-components.js:', error.message);
|
19
|
+
return null;
|
20
|
+
}
|
21
|
+
}
|
22
|
+
|
12
23
|
const defaultConfigPath = path.join(__dirname, '../src/config/maugli.config.ts');
|
13
24
|
const userRoot = process.env.INIT_CWD || process.cwd();
|
14
25
|
const userConfigPath = path.join(userRoot, 'src/config/maugli.config.ts');
|
@@ -43,6 +54,15 @@ function mergeMissing(target, source) {
|
|
43
54
|
}
|
44
55
|
|
45
56
|
async function main() {
|
57
|
+
console.log('🔄 Starting Maugli upgrade process...');
|
58
|
+
|
59
|
+
// Сначала обновляем компоненты
|
60
|
+
const updateComponents = await importUpdateComponents();
|
61
|
+
if (updateComponents) {
|
62
|
+
await updateComponents();
|
63
|
+
}
|
64
|
+
|
65
|
+
// Затем обновляем конфиг
|
46
66
|
const pkg = await loadTsModule(defaultConfigPath);
|
47
67
|
const defCfg = pkg.maugliConfig;
|
48
68
|
const newVersion = pkg.MAUGLI_CONFIG_VERSION || defCfg.configVersion;
|
@@ -50,7 +70,7 @@ async function main() {
|
|
50
70
|
try {
|
51
71
|
await fs.access(userConfigPath);
|
52
72
|
} catch {
|
53
|
-
console.warn(`User config not found at ${userConfigPath}, skipping upgrade`);
|
73
|
+
console.warn(`User config not found at ${userConfigPath}, skipping config upgrade`);
|
54
74
|
return;
|
55
75
|
}
|
56
76
|
|
@@ -24,15 +24,7 @@ const current = availableLanguages.find((l) => l.code === currentLang) || availa
|
|
24
24
|
class="flag-box flex items-center justify-center w-7 h-7 rounded-[var(--radius-main)] shadow-[var(--shadow-main)]"
|
25
25
|
style="min-width:28px; min-height:28px; max-width:28px; max-height:28px;"
|
26
26
|
>
|
27
|
-
{current && (
|
28
|
-
<img
|
29
|
-
src={current.icon.replace(/^\/public\/|^\/|^flags\//, '/src/icons/flags/')}
|
30
|
-
alt={current.code.toUpperCase()}
|
31
|
-
width="28"
|
32
|
-
height="28"
|
33
|
-
style="pointer-events:none;"
|
34
|
-
/>
|
35
|
-
)}
|
27
|
+
{current && <img src={current.icon} alt={current.code.toUpperCase()} width="28" height="28" style="pointer-events:none;" />}
|
36
28
|
</span>
|
37
29
|
<svg class="w-4 h-4" viewBox="0 0 20 20" fill="currentColor">
|
38
30
|
<path
|
@@ -65,13 +57,7 @@ const current = availableLanguages.find((l) => l.code === currentLang) || availa
|
|
65
57
|
class="flag-box flex items-center justify-center w-7 h-7 rounded-[var(--radius-main)] shadow-[var(--shadow-main)]"
|
66
58
|
style="min-width:28px; min-height:28px; max-width:28px; max-height:28px;"
|
67
59
|
>
|
68
|
-
<img
|
69
|
-
src={lang.icon.replace(/^\/public\/|^\/|^flags\//, '/src/icons/flags/')}
|
70
|
-
alt={lang.code.toUpperCase()}
|
71
|
-
width="28"
|
72
|
-
height="28"
|
73
|
-
style="pointer-events:none;"
|
74
|
-
/>
|
60
|
+
<img src={lang.icon} alt={lang.code.toUpperCase()} width="28" height="28" style="pointer-events:none;" />
|
75
61
|
</span>
|
76
62
|
<span class="text-heading">{lang.label}</span>
|
77
63
|
</a>
|
package/astro.config.mjs
DELETED
@@ -1,92 +0,0 @@
|
|
1
|
-
import mdx from '@astrojs/mdx';
|
2
|
-
import sitemap from '@astrojs/sitemap';
|
3
|
-
import tailwindcss from '@tailwindcss/vite';
|
4
|
-
import { defineConfig } from 'astro/config';
|
5
|
-
import { imagetools } from 'vite-imagetools';
|
6
|
-
import { VitePWA } from 'vite-plugin-pwa';
|
7
|
-
import siteConfig from './src/data/site-config';
|
8
|
-
import remarkSlug from 'remark-slug';
|
9
|
-
import customSlugify from './src/utils/remark-slugify';
|
10
|
-
import { maugliConfig } from './src/config/maugli.config';
|
11
|
-
|
12
|
-
export const pwaOptions = {
|
13
|
-
registerType: 'autoUpdate',
|
14
|
-
includeAssets: ['favicon.svg', 'favicon.ico', 'robots.txt', 'apple-touch-icon.png'],
|
15
|
-
manifest: {
|
16
|
-
name: "Maugli Blog",
|
17
|
-
short_name: "Maugli",
|
18
|
-
start_url: "/",
|
19
|
-
display: "standalone",
|
20
|
-
background_color: maugliConfig.pwa?.backgroundColor ?? '#ffffff',
|
21
|
-
theme_color: maugliConfig.pwa?.themeColor ?? '#0cbf11',
|
22
|
-
icons: maugliConfig.pwa?.icons ?? [
|
23
|
-
{
|
24
|
-
src: "/icon-192.png",
|
25
|
-
sizes: "192x192",
|
26
|
-
type: "image/png",
|
27
|
-
purpose: "any maskable",
|
28
|
-
},
|
29
|
-
{
|
30
|
-
src: "/icon-512.png",
|
31
|
-
sizes: "512x512",
|
32
|
-
type: "image/png",
|
33
|
-
},
|
34
|
-
],
|
35
|
-
},
|
36
|
-
workbox: {
|
37
|
-
navigateFallback: '/index.html',
|
38
|
-
cleanupOutdatedCaches: true,
|
39
|
-
navigateFallbackDenylist: [/^\/api\//],
|
40
|
-
clientsClaim: true,
|
41
|
-
globPatterns: ['**/*.{js,css,html,png,jpg,jpeg,webp,svg}'],
|
42
|
-
runtimeCaching: [
|
43
|
-
{
|
44
|
-
urlPattern: ({ request }) => request.destination === 'image',
|
45
|
-
handler: 'CacheFirst',
|
46
|
-
options: {
|
47
|
-
cacheName: 'images-cache',
|
48
|
-
expiration: {
|
49
|
-
maxEntries: 50,
|
50
|
-
maxAgeSeconds: 30 * 24 * 60 * 60 // 30 дней
|
51
|
-
}
|
52
|
-
}
|
53
|
-
},
|
54
|
-
{
|
55
|
-
urlPattern: ({ request }) => request.destination === 'font',
|
56
|
-
handler: 'CacheFirst',
|
57
|
-
options: {
|
58
|
-
cacheName: 'fonts-cache',
|
59
|
-
expiration: {
|
60
|
-
maxEntries: 20,
|
61
|
-
maxAgeSeconds: 365 * 24 * 60 * 60 // 1 год
|
62
|
-
}
|
63
|
-
}
|
64
|
-
}
|
65
|
-
]
|
66
|
-
},
|
67
|
-
devOptions: {
|
68
|
-
enabled: true, // чтобы работал в деве
|
69
|
-
type: 'module',
|
70
|
-
}
|
71
|
-
};
|
72
|
-
|
73
|
-
// https://astro.build/config
|
74
|
-
export default defineConfig({
|
75
|
-
site: siteConfig.website,
|
76
|
-
integrations: [
|
77
|
-
mdx(),
|
78
|
-
sitemap()
|
79
|
-
],
|
80
|
-
vite: {
|
81
|
-
plugins: [
|
82
|
-
tailwindcss(),
|
83
|
-
imagetools(),
|
84
|
-
VitePWA(pwaOptions)
|
85
|
-
]
|
86
|
-
},
|
87
|
-
markdown: {
|
88
|
-
remarkPlugins: [
|
89
|
-
[remarkSlug, { slug: customSlugify }]
|
90
|
-
]
|
91
|
-
}
|
92
|
-
});
|