core-maugli 1.2.48 → 1.2.50
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/netlify.toml +19 -8
- package/package.json +2 -1
- package/public/blackbox-1200.webp +0 -0
- package/public/blackbox-400.webp +0 -0
- package/public/blackbox-800.webp +0 -0
- package/public/blackbox.webp +0 -0
- package/scripts/copy-netlify-config.js +243 -22
package/netlify.toml
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Netlify configuration for Maugli Blog
|
|
2
2
|
# Auto-copied from core-maugli package
|
|
3
|
+
#
|
|
4
|
+
# 🔧 CUSTOMIZATION GUIDE:
|
|
5
|
+
# - Add "# CUSTOMIZED" comment anywhere to prevent auto-updates
|
|
6
|
+
# - Uncomment Bluesky/Supabase plugins after setup in Netlify UI
|
|
7
|
+
# - Add your own plugins and they'll be preserved automatically
|
|
8
|
+
#
|
|
3
9
|
|
|
4
10
|
[build]
|
|
5
11
|
publish = "dist"
|
|
@@ -25,13 +31,18 @@
|
|
|
25
31
|
[[plugins]]
|
|
26
32
|
package = "netlify-plugin-submit-sitemap"
|
|
27
33
|
|
|
34
|
+
# Пользовательский плагин
|
|
35
|
+
[[plugins]]
|
|
36
|
+
package = "netlify-plugin-my-custom-analytics"
|
|
37
|
+
|
|
28
38
|
# Плагины для интеграций (требуют ручной настройки)
|
|
29
39
|
# Раскомментируйте после настройки в Netlify UI:
|
|
40
|
+
# ⚠️ После раскомментирования этот файл не будет автоматически обновляться
|
|
30
41
|
|
|
31
42
|
# Bluesky Custom Domain
|
|
32
43
|
# Инструкция: https://app.netlify.com/extensions/bluesky-custom-domain
|
|
33
|
-
|
|
34
|
-
|
|
44
|
+
[[plugins]]
|
|
45
|
+
package = "netlify-plugin-bluesky"
|
|
35
46
|
|
|
36
47
|
# Supabase Integration
|
|
37
48
|
# Инструкция: https://app.netlify.com/extensions/supabase
|
|
@@ -48,19 +59,19 @@
|
|
|
48
59
|
[[headers]]
|
|
49
60
|
for = "/*"
|
|
50
61
|
[headers.values]
|
|
51
|
-
X-Frame-Options = "DENY"
|
|
52
|
-
X-XSS-Protection = "1; mode=block"
|
|
53
|
-
X-Content-Type-Options = "nosniff"
|
|
54
|
-
Referrer-Policy = "strict-origin-when-cross-origin"
|
|
62
|
+
"X-Frame-Options" = "DENY"
|
|
63
|
+
"X-XSS-Protection" = "1; mode=block"
|
|
64
|
+
"X-Content-Type-Options" = "nosniff"
|
|
65
|
+
"Referrer-Policy" = "strict-origin-when-cross-origin"
|
|
55
66
|
|
|
56
67
|
# Кэширование изображений
|
|
57
68
|
[[headers]]
|
|
58
69
|
for = "/img/*"
|
|
59
70
|
[headers.values]
|
|
60
|
-
Cache-Control = "public, max-age=31536000, immutable"
|
|
71
|
+
"Cache-Control" = "public, max-age=31536000, immutable"
|
|
61
72
|
|
|
62
73
|
[[headers]]
|
|
63
74
|
for = "/*.webp"
|
|
64
75
|
[headers.values]
|
|
65
|
-
Cache-Control = "public, max-age=31536000, immutable"
|
|
76
|
+
"Cache-Control" = "public, max-age=31536000, immutable"
|
|
66
77
|
|
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.50",
|
|
6
6
|
"license": "GPL-3.0-or-later OR Commercial",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"build:no-check": "node scripts/flatten-images.cjs && node scripts/optimize-images.cjs && node typograf-batch.js && node scripts/verify-assets.js && node scripts/generate-previews.js && astro build",
|
|
28
28
|
"optimize": "node scripts/optimize-images.cjs",
|
|
29
29
|
"optimize:squoosh": "node scripts/squoosh-optimize.js",
|
|
30
|
+
"clean:resized": "node scripts/clean-resized-images.js",
|
|
30
31
|
"test": "node tests/examplesFilter.test.ts",
|
|
31
32
|
"astro": "astro",
|
|
32
33
|
"featured:add": "node scripts/featured.js add",
|
|
Binary file
|
package/public/blackbox-400.webp
CHANGED
|
Binary file
|
package/public/blackbox-800.webp
CHANGED
|
Binary file
|
package/public/blackbox.webp
CHANGED
|
Binary file
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Умно обновляет netlify.toml, мерджит плагины и сохраняет пользовательские настройки
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import fs from 'fs';
|
|
@@ -11,26 +11,213 @@ import { fileURLToPath } from 'url';
|
|
|
11
11
|
const __filename = fileURLToPath(import.meta.url);
|
|
12
12
|
const __dirname = path.dirname(__filename);
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
// Базовые плагины из core-maugli (будут обновляться)
|
|
15
|
+
const BASE_PLUGINS = [
|
|
16
|
+
'netlify-plugin-astro',
|
|
17
|
+
'@netlify/plugin-lighthouse',
|
|
18
|
+
'netlify-plugin-image-optim',
|
|
19
|
+
'netlify-plugin-minify-html',
|
|
20
|
+
'netlify-plugin-submit-sitemap',
|
|
21
|
+
'netlify-plugin-checklinks',
|
|
22
|
+
'netlify-plugin-inline-critical-css',
|
|
23
|
+
'netlify-plugin-hashfiles'
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
// Опциональные плагины (требуют ручной настройки)
|
|
27
|
+
const OPTIONAL_PLUGINS = [
|
|
28
|
+
'netlify-plugin-bluesky',
|
|
29
|
+
'@supabase/netlify-integration',
|
|
30
|
+
'netlify-plugin-bluesky-custom-domain'
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Парсит TOML файл и извлекает плагины
|
|
35
|
+
*/
|
|
36
|
+
function parseNetlifyToml(content) {
|
|
37
|
+
const plugins = [];
|
|
38
|
+
const lines = content.split('\n');
|
|
39
|
+
let currentPlugin = null;
|
|
40
|
+
|
|
41
|
+
for (let i = 0; i < lines.length; i++) {
|
|
42
|
+
const line = lines[i].trim();
|
|
43
|
+
|
|
44
|
+
// Проверяем начало секции плагина
|
|
45
|
+
if (line === '[[plugins]]') {
|
|
46
|
+
currentPlugin = {};
|
|
47
|
+
}
|
|
48
|
+
// Извлекаем package
|
|
49
|
+
else if (currentPlugin && line.match(/package\s*=\s*["']([^"']+)["']/)) {
|
|
50
|
+
const match = line.match(/package\s*=\s*["']([^"']+)["']/);
|
|
51
|
+
currentPlugin.package = match[1];
|
|
52
|
+
plugins.push(currentPlugin);
|
|
53
|
+
currentPlugin = null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return plugins;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Получает плагины из maugli.config.ts
|
|
62
|
+
*/
|
|
63
|
+
async function getMaugliConfigPlugins() {
|
|
64
|
+
try {
|
|
65
|
+
const configPath = path.join(process.cwd(), 'src', 'config', 'maugli.config.ts');
|
|
66
|
+
if (!fs.existsSync(configPath)) {
|
|
67
|
+
console.log('📝 maugli.config.ts not found, using default plugins');
|
|
68
|
+
return BASE_PLUGINS;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const configContent = fs.readFileSync(configPath, 'utf8');
|
|
72
|
+
|
|
73
|
+
// Ищем секцию netlify.plugins
|
|
74
|
+
const netlifyMatch = configContent.match(/netlify:\s*{[\s\S]*?plugins:\s*\[([\s\S]*?)\]/);
|
|
75
|
+
|
|
76
|
+
if (netlifyMatch) {
|
|
77
|
+
const pluginsString = netlifyMatch[1];
|
|
78
|
+
const plugins = [];
|
|
79
|
+
|
|
80
|
+
// Разбираем построчно, чтобы правильно обработать комментарии
|
|
81
|
+
const lines = pluginsString.split('\n');
|
|
82
|
+
for (const line of lines) {
|
|
83
|
+
const trimmed = line.trim();
|
|
84
|
+
// Пропускаем пустые строки и комментарии
|
|
85
|
+
if (!trimmed || trimmed.startsWith('//')) continue;
|
|
86
|
+
|
|
87
|
+
// Извлекаем строку плагина
|
|
88
|
+
const match = trimmed.match(/['"]([^'"]+)['"]/);
|
|
89
|
+
if (match) {
|
|
90
|
+
const pluginName = match[1].trim();
|
|
91
|
+
if (pluginName && !pluginName.includes('//')) {
|
|
92
|
+
plugins.push(pluginName);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
console.log(`📋 Found ${plugins.length} plugins in maugli.config.ts`);
|
|
98
|
+
return plugins;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
console.log('📝 No plugins found in maugli.config.ts, using defaults');
|
|
102
|
+
return BASE_PLUGINS;
|
|
103
|
+
} catch (error) {
|
|
104
|
+
console.log(`⚠️ Error reading maugli.config.ts: ${error.message}`);
|
|
105
|
+
return BASE_PLUGINS;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Мерджит плагины: базовые + из конфига + пользовательские
|
|
111
|
+
*/
|
|
112
|
+
function mergePlugins(existingPlugins, configPlugins) {
|
|
113
|
+
const merged = new Set();
|
|
114
|
+
const userPlugins = [];
|
|
115
|
+
|
|
116
|
+
// Добавляем все плагины из конфига
|
|
117
|
+
configPlugins.forEach(plugin => merged.add(plugin));
|
|
118
|
+
|
|
119
|
+
// Добавляем пользовательские плагины (не из базового списка)
|
|
120
|
+
existingPlugins.forEach(plugin => {
|
|
121
|
+
const packageName = plugin.package;
|
|
122
|
+
if (!BASE_PLUGINS.includes(packageName) && !configPlugins.includes(packageName)) {
|
|
123
|
+
userPlugins.push(packageName);
|
|
124
|
+
merged.add(packageName);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
all: Array.from(merged),
|
|
130
|
+
user: userPlugins,
|
|
131
|
+
config: configPlugins
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Генерирует TOML секцию плагинов
|
|
137
|
+
*/
|
|
138
|
+
function generatePluginsToml(plugins, userPlugins, optionalActive) {
|
|
139
|
+
let toml = '\n# Плагины (автоматически управляемые)\n';
|
|
140
|
+
|
|
141
|
+
plugins.forEach(plugin => {
|
|
142
|
+
const isOptional = OPTIONAL_PLUGINS.includes(plugin);
|
|
143
|
+
const isActive = optionalActive.includes(plugin);
|
|
144
|
+
const isUser = userPlugins.includes(plugin);
|
|
145
|
+
|
|
146
|
+
if (isOptional && !isActive) {
|
|
147
|
+
// Опциональный плагин, неактивный - комментируем
|
|
148
|
+
toml += `# [[plugins]]\n# package = "${plugin}"\n`;
|
|
149
|
+
} else {
|
|
150
|
+
// Активный плагин
|
|
151
|
+
toml += `[[plugins]]\n package = "${plugin}"`;
|
|
152
|
+
if (isUser) {
|
|
153
|
+
toml += ' # user-added';
|
|
154
|
+
}
|
|
155
|
+
toml += '\n';
|
|
156
|
+
}
|
|
157
|
+
toml += '\n';
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
return toml;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async function main() {
|
|
15
164
|
try {
|
|
16
165
|
const targetPath = path.join(process.cwd(), 'netlify.toml');
|
|
17
166
|
|
|
18
|
-
//
|
|
167
|
+
// Получаем плагины из конфига
|
|
168
|
+
const configPlugins = await getMaugliConfigPlugins();
|
|
169
|
+
|
|
170
|
+
let existingPlugins = [];
|
|
171
|
+
let existingContent = '';
|
|
172
|
+
let activeOptionalPlugins = [];
|
|
173
|
+
let shouldPreserve = false;
|
|
174
|
+
|
|
175
|
+
// Анализируем существующий файл
|
|
19
176
|
if (fs.existsSync(targetPath)) {
|
|
20
|
-
|
|
21
|
-
|
|
177
|
+
existingContent = fs.readFileSync(targetPath, 'utf8');
|
|
178
|
+
existingPlugins = parseNetlifyToml(existingContent);
|
|
179
|
+
|
|
180
|
+
// Определяем активные опциональные плагины
|
|
181
|
+
activeOptionalPlugins = existingPlugins
|
|
182
|
+
.map(p => p.package)
|
|
183
|
+
.filter(pkg => OPTIONAL_PLUGINS.includes(pkg));
|
|
184
|
+
|
|
185
|
+
// Проверяем, нужно ли сохранить файл
|
|
186
|
+
const hasCustomComment = existingContent.includes('# CUSTOMIZED') &&
|
|
187
|
+
!existingContent.includes('Add "# CUSTOMIZED" comment'); // Исключаем инструкцию
|
|
188
|
+
const hasUserModifications = !existingContent.includes('# Auto-copied from core-maugli package');
|
|
189
|
+
|
|
190
|
+
if (hasCustomComment) {
|
|
191
|
+
console.log('📋 Found "# CUSTOMIZED" marker - preserving entire file');
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (activeOptionalPlugins.length > 0) {
|
|
196
|
+
console.log('📋 Found active integrations:');
|
|
197
|
+
activeOptionalPlugins.forEach(plugin => {
|
|
198
|
+
console.log(` � ${plugin}`);
|
|
199
|
+
});
|
|
200
|
+
shouldPreserve = true;
|
|
201
|
+
}
|
|
22
202
|
}
|
|
23
203
|
|
|
24
|
-
//
|
|
25
|
-
|
|
204
|
+
// Мерджим плагины
|
|
205
|
+
const mergedPlugins = mergePlugins(existingPlugins, configPlugins);
|
|
26
206
|
|
|
27
|
-
|
|
207
|
+
if (mergedPlugins.user.length > 0) {
|
|
208
|
+
console.log('📦 Found user plugins:');
|
|
209
|
+
mergedPlugins.user.forEach(plugin => {
|
|
210
|
+
console.log(` ✨ ${plugin}`);
|
|
211
|
+
});
|
|
212
|
+
shouldPreserve = true;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Читаем базовый шаблон
|
|
216
|
+
let sourcePath;
|
|
28
217
|
const nodeModulesPath = path.join(process.cwd(), 'node_modules', 'core-maugli', 'netlify.toml');
|
|
29
218
|
if (fs.existsSync(nodeModulesPath)) {
|
|
30
219
|
sourcePath = nodeModulesPath;
|
|
31
|
-
}
|
|
32
|
-
// Если запускаем из самого пакета (для разработки)
|
|
33
|
-
else {
|
|
220
|
+
} else {
|
|
34
221
|
sourcePath = path.join(__dirname, '..', 'netlify.toml');
|
|
35
222
|
}
|
|
36
223
|
|
|
@@ -39,19 +226,53 @@ function main() {
|
|
|
39
226
|
return;
|
|
40
227
|
}
|
|
41
228
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
229
|
+
let templateContent = fs.readFileSync(sourcePath, 'utf8');
|
|
230
|
+
|
|
231
|
+
// Заменяем секцию плагинов
|
|
232
|
+
const pluginsToml = generatePluginsToml(
|
|
233
|
+
mergedPlugins.all,
|
|
234
|
+
mergedPlugins.user,
|
|
235
|
+
activeOptionalPlugins
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
// Удаляем старую секцию плагинов и вставляем новую
|
|
239
|
+
const beforePlugins = templateContent.split('# Обязательные плагины')[0];
|
|
240
|
+
const afterPlugins = templateContent.split('# Redirects')[1] || templateContent.split('[[redirects]]')[0];
|
|
241
|
+
|
|
242
|
+
const updatedContent = beforePlugins.trim() + '\n' + pluginsToml + '\n# Redirects' + (afterPlugins || '');
|
|
243
|
+
|
|
244
|
+
// Создаем бэкап если файл существует
|
|
245
|
+
if (fs.existsSync(targetPath) && shouldPreserve) {
|
|
246
|
+
fs.copyFileSync(targetPath, targetPath + '.backup');
|
|
247
|
+
console.log('📦 Created backup: netlify.toml.backup');
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Записываем обновленный файл
|
|
251
|
+
fs.writeFileSync(targetPath, updatedContent);
|
|
252
|
+
|
|
253
|
+
console.log('✅ netlify.toml updated successfully');
|
|
254
|
+
console.log(`📋 Total plugins: ${mergedPlugins.all.length}`);
|
|
255
|
+
console.log(` � From config: ${mergedPlugins.config.length}`);
|
|
256
|
+
console.log(` ✨ User added: ${mergedPlugins.user.length}`);
|
|
257
|
+
console.log(` 🔵 Active integrations: ${activeOptionalPlugins.length}`);
|
|
258
|
+
|
|
259
|
+
if (OPTIONAL_PLUGINS.some(p => !activeOptionalPlugins.includes(p))) {
|
|
260
|
+
console.log('\n📝 Available integrations (currently disabled):');
|
|
261
|
+
OPTIONAL_PLUGINS.forEach(plugin => {
|
|
262
|
+
if (!activeOptionalPlugins.includes(plugin)) {
|
|
263
|
+
const setupUrl = plugin.includes('bluesky')
|
|
264
|
+
? 'https://app.netlify.com/extensions/bluesky-custom-domain'
|
|
265
|
+
: plugin.includes('supabase')
|
|
266
|
+
? 'https://app.netlify.com/extensions/supabase'
|
|
267
|
+
: 'Netlify Extensions';
|
|
268
|
+
console.log(` � ${plugin}: ${setupUrl}`);
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
}
|
|
51
272
|
|
|
52
273
|
} catch (error) {
|
|
53
|
-
console.error('❌ Error
|
|
274
|
+
console.error('❌ Error updating netlify.toml:', error.message);
|
|
54
275
|
}
|
|
55
276
|
}
|
|
56
277
|
|
|
57
|
-
main();
|
|
278
|
+
main().catch(console.error);
|