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 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
- # [[plugins]]
34
- # package = "netlify-plugin-bluesky"
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.48",
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
Binary file
Binary file
Binary file
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * Копирует netlify.toml из пакета core-maugli в проект
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
- function main() {
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
- // Проверяем, есть ли уже netlify.toml
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
- console.log('📋 netlify.toml already exists - skipping copy');
21
- return;
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
- let sourcePath;
204
+ // Мерджим плагины
205
+ const mergedPlugins = mergePlugins(existingPlugins, configPlugins);
26
206
 
27
- // Если запускаем из node_modules
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
- fs.copyFileSync(sourcePath, targetPath);
44
- console.log('✅ netlify.toml copied successfully');
45
- console.log('');
46
- console.log('📝 Manual setup required for:');
47
- console.log(' • Bluesky: https://app.netlify.com/extensions/bluesky-custom-domain');
48
- console.log(' • Supabase: https://app.netlify.com/extensions/supabase');
49
- console.log('');
50
- console.log('💡 Uncomment plugins in netlify.toml after setup');
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 copying netlify.toml:', error.message);
274
+ console.error('❌ Error updating netlify.toml:', error.message);
54
275
  }
55
276
  }
56
277
 
57
- main();
278
+ main().catch(console.error);