core-maugli 1.2.67 → 1.2.71
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/astro.config.mjs +2 -1
- package/package.json +1 -1
- package/public/blackbox-1200.webp +0 -0
- package/public/blackbox-400.webp +0 -0
- package/public/blackbox-800.webp +0 -0
- package/resize-all.cjs +17 -17
- package/scripts/check-version.js +4 -4
- package/scripts/copy-netlify-config.js +4 -4
- package/scripts/generate-previews-build.js +104 -0
- package/scripts/generate-previews.js +8 -8
- package/scripts/optimize-images.cjs +13 -13
- package/scripts/squoosh-optimize.js +12 -12
- package/scripts/update-all-blogs.js +15 -14
- package/scripts/update-components.js +7 -46
- package/src/pages/[...id].astro +1 -7
- package/src/utils/posts.ts +2 -2
- package/src/middleware.ts +0 -21
- package/src/pages/sw.js.astro +0 -8
package/astro.config.mjs
CHANGED
|
@@ -37,7 +37,7 @@ export const pwaOptions = {
|
|
|
37
37
|
workbox: {
|
|
38
38
|
navigateFallback: '/index.html',
|
|
39
39
|
cleanupOutdatedCaches: true,
|
|
40
|
-
navigateFallbackDenylist: [/^\/api
|
|
40
|
+
navigateFallbackDenylist: [/^\/api\//, /^\/sw\.js$/],
|
|
41
41
|
clientsClaim: true,
|
|
42
42
|
globPatterns: ['**/*.{js,css,html,png,jpg,jpeg,webp,svg}'],
|
|
43
43
|
runtimeCaching: [
|
|
@@ -95,6 +95,7 @@ export default defineConfig({
|
|
|
95
95
|
vite: {
|
|
96
96
|
plugins: [
|
|
97
97
|
tailwindcss(),
|
|
98
|
+
VitePWA(pwaOptions),
|
|
98
99
|
imagetools({
|
|
99
100
|
// Aggressive image optimization
|
|
100
101
|
defaultDirectives: () => {
|
package/package.json
CHANGED
|
Binary file
|
package/public/blackbox-400.webp
CHANGED
|
Binary file
|
package/public/blackbox-800.webp
CHANGED
|
Binary file
|
package/resize-all.cjs
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
// resize-all.cjs -
|
|
1
|
+
// resize-all.cjs - recursive image resizing
|
|
2
2
|
const fs = require('fs');
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const sharp = require('sharp');
|
|
5
5
|
|
|
6
|
-
//
|
|
6
|
+
// Sizes for generation
|
|
7
7
|
const sizes = [400, 800, 1200];
|
|
8
8
|
|
|
9
9
|
const inputDir = './public';
|
|
10
|
-
const processedFiles = new Set(); //
|
|
10
|
+
const processedFiles = new Set(); // Track processed files
|
|
11
11
|
|
|
12
|
-
//
|
|
12
|
+
// Recursive function for directory traversal
|
|
13
13
|
function processDirectory(dir) {
|
|
14
14
|
if (!fs.existsSync(dir)) {
|
|
15
|
-
console.log(
|
|
15
|
+
console.log(`Directory ${dir} does not exist`);
|
|
16
16
|
return;
|
|
17
17
|
}
|
|
18
18
|
|
|
@@ -23,15 +23,15 @@ function processDirectory(dir) {
|
|
|
23
23
|
const stat = fs.statSync(itemPath);
|
|
24
24
|
|
|
25
25
|
if (stat.isDirectory()) {
|
|
26
|
-
//
|
|
26
|
+
// Recursively process subdirectories
|
|
27
27
|
processDirectory(itemPath);
|
|
28
28
|
} else if (stat.isFile()) {
|
|
29
29
|
const ext = path.extname(item).toLowerCase();
|
|
30
30
|
const baseName = path.basename(item, ext);
|
|
31
31
|
|
|
32
|
-
//
|
|
32
|
+
// Check if it's an image and doesn't contain size in filename
|
|
33
33
|
if (['.jpg', '.jpeg', '.png', '.webp'].includes(ext)) {
|
|
34
|
-
//
|
|
34
|
+
// Exclude PWA icons and service files
|
|
35
35
|
const excludePatterns = [
|
|
36
36
|
'icon-192', 'icon-512', // PWA иконки
|
|
37
37
|
'favicon', // Фавиконки
|
|
@@ -41,31 +41,31 @@ function processDirectory(dir) {
|
|
|
41
41
|
|
|
42
42
|
const shouldExclude = excludePatterns.some(pattern => baseName.includes(pattern));
|
|
43
43
|
|
|
44
|
-
//
|
|
45
|
-
//
|
|
44
|
+
// Skip files that already contain size (e.g., image-400.webp, image-800-800.webp)
|
|
45
|
+
// Enhanced check: skip files with -400, -800, -1200 anywhere in the name
|
|
46
46
|
const hasResizeSuffix = sizes.some(size => baseName.includes(`-${size}`));
|
|
47
47
|
|
|
48
48
|
if (!hasResizeSuffix && !shouldExclude && !processedFiles.has(itemPath)) {
|
|
49
49
|
processedFiles.add(itemPath);
|
|
50
50
|
|
|
51
|
-
console.log(
|
|
51
|
+
console.log(`Processing: ${itemPath}`);
|
|
52
52
|
|
|
53
53
|
sizes.forEach(width => {
|
|
54
54
|
const outputPath = path.join(path.dirname(itemPath), `${baseName}-${width}${ext}`);
|
|
55
55
|
|
|
56
|
-
//
|
|
56
|
+
// Check if file doesn't already exist
|
|
57
57
|
if (!fs.existsSync(outputPath)) {
|
|
58
58
|
sharp(itemPath)
|
|
59
59
|
.resize(width)
|
|
60
60
|
.toFile(outputPath, (err) => {
|
|
61
61
|
if (err) {
|
|
62
|
-
console.error(
|
|
62
|
+
console.error(`Error creating ${outputPath}:`, err.message);
|
|
63
63
|
} else {
|
|
64
|
-
console.log(`✅
|
|
64
|
+
console.log(`✅ Created: ${path.relative('./public', outputPath)}`);
|
|
65
65
|
}
|
|
66
66
|
});
|
|
67
67
|
} else {
|
|
68
|
-
console.log(`⏭️
|
|
68
|
+
console.log(`⏭️ Skipping (already exists): ${path.relative('./public', outputPath)}`);
|
|
69
69
|
}
|
|
70
70
|
});
|
|
71
71
|
}
|
|
@@ -74,6 +74,6 @@ function processDirectory(dir) {
|
|
|
74
74
|
});
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
console.log('🔄
|
|
77
|
+
console.log('🔄 Starting image resize process...');
|
|
78
78
|
processDirectory(inputDir);
|
|
79
|
-
console.log('✅
|
|
79
|
+
console.log('✅ Image processing completed!');
|
package/scripts/check-version.js
CHANGED
|
@@ -45,10 +45,10 @@ async function getMaugliConfig() {
|
|
|
45
45
|
const configContent = fs.readFileSync(configPath, 'utf8');
|
|
46
46
|
console.log(colorize('🔍 Reading maugli.config.ts...', 'cyan'));
|
|
47
47
|
|
|
48
|
-
//
|
|
48
|
+
// Simple and reliable forceUpdate search
|
|
49
49
|
let forceUpdate = false;
|
|
50
50
|
|
|
51
|
-
//
|
|
51
|
+
// Search for all lines with forceUpdate
|
|
52
52
|
const lines = configContent.split('\n');
|
|
53
53
|
const forceUpdateLines = lines.filter(line => line.includes('forceUpdate'));
|
|
54
54
|
|
|
@@ -57,9 +57,9 @@ async function getMaugliConfig() {
|
|
|
57
57
|
for (const line of forceUpdateLines) {
|
|
58
58
|
console.log(colorize(` ${line.trim()}`, 'gray'));
|
|
59
59
|
|
|
60
|
-
//
|
|
60
|
+
// Check different formats
|
|
61
61
|
if (line.includes('forceUpdate') && line.includes('true')) {
|
|
62
|
-
//
|
|
62
|
+
// Check that this is not a comment
|
|
63
63
|
const trimmedLine = line.trim();
|
|
64
64
|
if (!trimmedLine.startsWith('//') && !trimmedLine.startsWith('*')) {
|
|
65
65
|
forceUpdate = true;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Copies netlify.toml only when initializing a new blog
|
|
5
|
+
* DOES NOT TOUCH existing netlify.toml files!
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import fs from 'fs';
|
|
@@ -23,7 +23,7 @@ function main() {
|
|
|
23
23
|
return;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
//
|
|
26
|
+
// Search for source file in package
|
|
27
27
|
let sourcePath;
|
|
28
28
|
const nodeModulesPath = path.join(process.cwd(), 'node_modules', 'core-maugli', 'netlify.toml');
|
|
29
29
|
if (fs.existsSync(nodeModulesPath)) {
|
|
@@ -37,7 +37,7 @@ function main() {
|
|
|
37
37
|
return;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
//
|
|
40
|
+
// Copy file only for new blog
|
|
41
41
|
fs.copyFileSync(sourcePath, targetPath);
|
|
42
42
|
console.log('✅ netlify.toml created for new blog');
|
|
43
43
|
console.log('');
|
|
@@ -0,0 +1,104 @@
|
|
|
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
|
+
// Universal project root detection
|
|
10
|
+
const rootDir = __dirname.includes('node_modules')
|
|
11
|
+
? path.join(__dirname, '../../..')
|
|
12
|
+
: path.join(__dirname, '..');
|
|
13
|
+
|
|
14
|
+
// Sizes for different content types
|
|
15
|
+
const blogPreviewWidth = 400;
|
|
16
|
+
const blogPreviewHeight = 210;
|
|
17
|
+
const rubricPreviewWidth = 210;
|
|
18
|
+
const rubricPreviewHeight = 214;
|
|
19
|
+
|
|
20
|
+
// Generate in dist instead of public
|
|
21
|
+
const outputDir = path.join(rootDir, 'dist');
|
|
22
|
+
|
|
23
|
+
// Function to create preview in dist
|
|
24
|
+
async function createPreviewForBuild(sourcePath, outputPath, width, height) {
|
|
25
|
+
const previewDir = path.dirname(outputPath);
|
|
26
|
+
if (!fs.existsSync(previewDir)) {
|
|
27
|
+
fs.mkdirSync(previewDir, { recursive: true });
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (fs.existsSync(outputPath)) {
|
|
31
|
+
return; // Preview already exists
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
await sharp(sourcePath)
|
|
36
|
+
.resize(width, height, { fit: 'cover' })
|
|
37
|
+
.toFile(outputPath);
|
|
38
|
+
console.log(`✅ Preview created: ${path.relative(rootDir, outputPath)}`);
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.error(`❌ Error creating preview ${outputPath}:`, error.message);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Function to process directory
|
|
45
|
+
async function processDirectory(sourceDir, outputSubDir) {
|
|
46
|
+
if (!fs.existsSync(sourceDir)) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const items = fs.readdirSync(sourceDir);
|
|
51
|
+
|
|
52
|
+
for (const item of items) {
|
|
53
|
+
const sourcePath = path.join(sourceDir, item);
|
|
54
|
+
const stat = fs.statSync(sourcePath);
|
|
55
|
+
|
|
56
|
+
if (stat.isDirectory()) {
|
|
57
|
+
if (item === 'previews') continue; // Skip preview folders
|
|
58
|
+
await processDirectory(sourcePath, path.join(outputSubDir, item));
|
|
59
|
+
} else if (item.match(/\.(webp|jpg|jpeg|png)$/i)) {
|
|
60
|
+
const ext = path.extname(item);
|
|
61
|
+
const name = path.basename(item, ext);
|
|
62
|
+
|
|
63
|
+
// Skip files that already contain size suffix
|
|
64
|
+
const hasResizeSuffix = [400, 800, 1200].some(size => name.includes(`-${size}`));
|
|
65
|
+
if (hasResizeSuffix) continue;
|
|
66
|
+
|
|
67
|
+
// Determine preview size
|
|
68
|
+
let previewWidth, previewHeight;
|
|
69
|
+
if (sourcePath.includes('/img/default/') && (name.includes('rubric') || name.includes('tag'))) {
|
|
70
|
+
previewWidth = rubricPreviewWidth;
|
|
71
|
+
previewHeight = rubricPreviewHeight;
|
|
72
|
+
} else {
|
|
73
|
+
previewWidth = blogPreviewWidth;
|
|
74
|
+
previewHeight = blogPreviewHeight;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Create preview
|
|
78
|
+
const outputDir = path.join(rootDir, 'dist', outputSubDir, 'previews');
|
|
79
|
+
const outputPath = path.join(outputDir, `${name}${ext}`);
|
|
80
|
+
|
|
81
|
+
console.log(`🎭 Creating preview for build: ${name}`);
|
|
82
|
+
await createPreviewForBuild(sourcePath, outputPath, previewWidth, previewHeight);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Main function
|
|
88
|
+
async function generatePreviewsForBuild() {
|
|
89
|
+
console.log('🚀 Starting preview generation for build...');
|
|
90
|
+
|
|
91
|
+
// Process system folders
|
|
92
|
+
await processDirectory(path.join(rootDir, 'public/img/default'), 'img/default');
|
|
93
|
+
await processDirectory(path.join(rootDir, 'public/img/examples'), 'img/examples');
|
|
94
|
+
|
|
95
|
+
// Process user images if they exist
|
|
96
|
+
const pageImagesDir = path.join(rootDir, 'public/img/page-images');
|
|
97
|
+
if (fs.existsSync(pageImagesDir)) {
|
|
98
|
+
await processDirectory(pageImagesDir, 'img/page-images');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
console.log('✅ Preview generation for build completed!');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
generatePreviewsForBuild().catch(console.error);
|
|
@@ -6,15 +6,15 @@ import { fileURLToPath } from 'url';
|
|
|
6
6
|
const __filename = fileURLToPath(import.meta.url);
|
|
7
7
|
const __dirname = path.dirname(__filename);
|
|
8
8
|
|
|
9
|
-
//
|
|
9
|
+
// Universal project root detection
|
|
10
10
|
const rootDir = __dirname.includes('node_modules')
|
|
11
11
|
? path.join(__dirname, '../../..')
|
|
12
12
|
: path.join(__dirname, '..');
|
|
13
13
|
|
|
14
|
-
//
|
|
14
|
+
// Sizes for different content types
|
|
15
15
|
const blogPreviewWidth = 400;
|
|
16
16
|
const blogPreviewHeight = 210;
|
|
17
|
-
const rubricPreviewWidth = 210; //
|
|
17
|
+
const rubricPreviewWidth = 210; // Increased size for retina display quality (105px * 2)
|
|
18
18
|
const rubricPreviewHeight = 214; // Увеличенный размер для качества на retina дисплеях (107px * 2)
|
|
19
19
|
const authorPreviewWidth = 192; // Квадратный размер для аватарок
|
|
20
20
|
const authorPreviewHeight = 192;
|
|
@@ -108,7 +108,7 @@ function extractImagePaths() {
|
|
|
108
108
|
return Array.from(imagePaths);
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
//
|
|
111
|
+
// Function to clean all existing preview images
|
|
112
112
|
function cleanupExistingPreviews() {
|
|
113
113
|
const publicDir = path.join(rootDir, 'public');
|
|
114
114
|
|
|
@@ -135,7 +135,7 @@ function cleanupExistingPreviews() {
|
|
|
135
135
|
removePreviewDirs(publicDir);
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
//
|
|
138
|
+
// Function to create preview image
|
|
139
139
|
async function createPreview(imagePath) {
|
|
140
140
|
const fullImagePath = path.join(rootDir, 'public', imagePath.replace(/^\//, ''));
|
|
141
141
|
|
|
@@ -149,7 +149,7 @@ async function createPreview(imagePath) {
|
|
|
149
149
|
const name = path.basename(fullImagePath, ext);
|
|
150
150
|
const previewPath = path.join(dir, 'previews', `${name}${ext}`);
|
|
151
151
|
|
|
152
|
-
//
|
|
152
|
+
// Determine preview size based on image type
|
|
153
153
|
let previewWidth, previewHeight;
|
|
154
154
|
if (imagePath.includes('/img/default/') && (name.includes('rubric') || name.includes('tag'))) {
|
|
155
155
|
previewWidth = rubricPreviewWidth;
|
|
@@ -171,7 +171,7 @@ async function createPreview(imagePath) {
|
|
|
171
171
|
fs.mkdirSync(previewDir, { recursive: true });
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
-
//
|
|
174
|
+
// If preview already exists - skip generation
|
|
175
175
|
if (fs.existsSync(previewPath)) {
|
|
176
176
|
// console.log(`Preview already exists: ${previewPath}`);
|
|
177
177
|
return;
|
|
@@ -188,7 +188,7 @@ async function createPreview(imagePath) {
|
|
|
188
188
|
|
|
189
189
|
// Основная функция
|
|
190
190
|
async function generatePreviews() {
|
|
191
|
-
//
|
|
191
|
+
// Clean previews only if explicitly specified (e.g., via environment variable)
|
|
192
192
|
const CLEAN_PREVIEWS = process.env.CLEAN_PREVIEWS === '1';
|
|
193
193
|
if (CLEAN_PREVIEWS) {
|
|
194
194
|
console.log('Cleaning up existing previews...');
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
// optimize-images.cjs -
|
|
1
|
+
// optimize-images.cjs - advanced image optimization with Sharp
|
|
2
2
|
const fs = require('fs');
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const sharp = require('sharp');
|
|
5
5
|
|
|
6
|
-
//
|
|
6
|
+
// Sizes for generation
|
|
7
7
|
const sizes = [400, 800, 1200];
|
|
8
8
|
|
|
9
|
-
//
|
|
9
|
+
// Optimization settings for different formats
|
|
10
10
|
const optimizationSettings = {
|
|
11
11
|
webp: {
|
|
12
12
|
quality: 80,
|
|
13
|
-
effort: 6, //
|
|
13
|
+
effort: 6, // Maximum compression (0-6)
|
|
14
14
|
lossless: false
|
|
15
15
|
},
|
|
16
16
|
jpeg: {
|
|
@@ -20,7 +20,7 @@ const optimizationSettings = {
|
|
|
20
20
|
},
|
|
21
21
|
png: {
|
|
22
22
|
quality: 90,
|
|
23
|
-
compressionLevel: 9, //
|
|
23
|
+
compressionLevel: 9, // Maximum compression
|
|
24
24
|
progressive: true
|
|
25
25
|
}
|
|
26
26
|
};
|
|
@@ -28,13 +28,13 @@ const optimizationSettings = {
|
|
|
28
28
|
const inputDir = './public';
|
|
29
29
|
const processedFiles = new Set();
|
|
30
30
|
|
|
31
|
-
//
|
|
31
|
+
// Function for image optimization
|
|
32
32
|
async function optimizeImage(inputPath, outputPath, width = null) {
|
|
33
33
|
try {
|
|
34
34
|
const ext = path.extname(outputPath).toLowerCase();
|
|
35
35
|
let sharpInstance = sharp(inputPath);
|
|
36
36
|
|
|
37
|
-
//
|
|
37
|
+
// Resize if width is specified
|
|
38
38
|
if (width) {
|
|
39
39
|
sharpInstance = sharpInstance.resize(width, null, {
|
|
40
40
|
withoutEnlargement: true,
|
|
@@ -42,7 +42,7 @@ async function optimizeImage(inputPath, outputPath, width = null) {
|
|
|
42
42
|
});
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
//
|
|
45
|
+
// Apply optimization based on format
|
|
46
46
|
switch (ext) {
|
|
47
47
|
case '.webp':
|
|
48
48
|
await sharpInstance
|
|
@@ -64,23 +64,23 @@ async function optimizeImage(inputPath, outputPath, width = null) {
|
|
|
64
64
|
break;
|
|
65
65
|
|
|
66
66
|
default:
|
|
67
|
-
//
|
|
67
|
+
// For other formats use WebP by default
|
|
68
68
|
const webpPath = outputPath.replace(/\.[^.]+$/, '.webp');
|
|
69
69
|
await sharpInstance
|
|
70
70
|
.webp(optimizationSettings.webp)
|
|
71
71
|
.toFile(webpPath);
|
|
72
|
-
console.log(`🔄
|
|
72
|
+
console.log(`🔄 Converted to WebP: ${path.relative('./public', webpPath)}`);
|
|
73
73
|
return;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
console.log(`✅
|
|
76
|
+
console.log(`✅ Optimized: ${path.relative('./public', outputPath)}`);
|
|
77
77
|
|
|
78
78
|
} catch (err) {
|
|
79
|
-
console.error(`❌
|
|
79
|
+
console.error(`❌ Optimization error ${outputPath}:`, err.message);
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
//
|
|
83
|
+
// Get file size statistics
|
|
84
84
|
function getFileSizeStats(originalPath, optimizedPath) {
|
|
85
85
|
if (!fs.existsSync(originalPath) || !fs.existsSync(optimizedPath)) {
|
|
86
86
|
return null;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
// squoosh-optimize.js -
|
|
3
|
+
// squoosh-optimize.js - automatic optimization through Squoosh CLI
|
|
4
4
|
import { execSync } from 'child_process';
|
|
5
5
|
import fs from 'fs';
|
|
6
6
|
import path from 'path';
|
|
@@ -12,7 +12,7 @@ const __dirname = path.dirname(__filename);
|
|
|
12
12
|
const projectRoot = path.resolve(__dirname, '..');
|
|
13
13
|
const publicDir = path.join(projectRoot, 'public');
|
|
14
14
|
|
|
15
|
-
//
|
|
15
|
+
// Function to get all images
|
|
16
16
|
function getAllImages(dir, images = []) {
|
|
17
17
|
const items = fs.readdirSync(dir);
|
|
18
18
|
|
|
@@ -25,7 +25,7 @@ function getAllImages(dir, images = []) {
|
|
|
25
25
|
} else if (stat.isFile()) {
|
|
26
26
|
const ext = path.extname(item).toLowerCase();
|
|
27
27
|
if (['.jpg', '.jpeg', '.png', '.webp'].includes(ext)) {
|
|
28
|
-
//
|
|
28
|
+
// Skip already processed resized images
|
|
29
29
|
const baseName = path.basename(item, ext);
|
|
30
30
|
if (!/-\d+$/.test(baseName)) {
|
|
31
31
|
images.push(itemPath);
|
|
@@ -37,7 +37,7 @@ function getAllImages(dir, images = []) {
|
|
|
37
37
|
return images;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
//
|
|
40
|
+
// Function to create temporary directory
|
|
41
41
|
function createTempDir() {
|
|
42
42
|
const tempDir = path.join(projectRoot, '.temp-optimization');
|
|
43
43
|
if (!fs.existsSync(tempDir)) {
|
|
@@ -107,29 +107,29 @@ async function optimizeWithSquoosh() {
|
|
|
107
107
|
const savings = originalSize - optimizedSize;
|
|
108
108
|
const savingsPercent = Math.round((savings / originalSize) * 100);
|
|
109
109
|
|
|
110
|
-
//
|
|
110
|
+
// Replace original with optimized version
|
|
111
111
|
fs.copyFileSync(optimizedPath, originalPath);
|
|
112
112
|
|
|
113
113
|
totalSavings += savings;
|
|
114
114
|
processedCount++;
|
|
115
115
|
|
|
116
|
-
console.log(`💾 ${path.relative(publicDir, originalPath)}: ${Math.round(savings/1024)}KB
|
|
116
|
+
console.log(`💾 ${path.relative(publicDir, originalPath)}: ${Math.round(savings/1024)}KB saved (${savingsPercent}%)`);
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
119
|
}
|
|
120
120
|
});
|
|
121
121
|
|
|
122
|
-
console.log(`\n🎉
|
|
123
|
-
console.log(`💰
|
|
122
|
+
console.log(`\n🎉 Processed ${processedCount} images`);
|
|
123
|
+
console.log(`💰 Total savings: ${Math.round(totalSavings/1024)}KB`);
|
|
124
124
|
|
|
125
125
|
} catch (squooshError) {
|
|
126
|
-
console.error('❌
|
|
127
|
-
console.log('🔄
|
|
126
|
+
console.error('❌ Squoosh CLI error:', squooshError.message);
|
|
127
|
+
console.log('🔄 Switching to Sharp optimization...');
|
|
128
128
|
return false;
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
} finally {
|
|
132
|
-
//
|
|
132
|
+
// Clean up temporary files
|
|
133
133
|
if (fs.existsSync(tempDir)) {
|
|
134
134
|
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
135
135
|
}
|
|
@@ -138,7 +138,7 @@ async function optimizeWithSquoosh() {
|
|
|
138
138
|
return true;
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
-
//
|
|
141
|
+
// Run if called directly
|
|
142
142
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
143
143
|
optimizeWithSquoosh().catch(console.error);
|
|
144
144
|
}
|
|
@@ -1,21 +1,22 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Script for centralized updating of all blogs to the latest core-maugli version
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* node scripts/update-all-blogs.js [
|
|
6
|
+
* Usage:
|
|
7
|
+
* node scripts/update-all-blogs.js [project_path]
|
|
8
8
|
*
|
|
9
|
-
*
|
|
9
|
+
* Or for multiple updates:
|
|
10
10
|
* node scripts/update-all-blogs.js /path/to/blogs/project1 /path/to/blogs/project2
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import fs from 'fs';
|
|
14
14
|
import path from 'path';
|
|
15
|
+
import { execSync } from 'child_process';
|
|
15
16
|
|
|
16
|
-
const CURRENT_VERSION = '1.2.
|
|
17
|
+
const CURRENT_VERSION = '1.2.70';
|
|
17
18
|
|
|
18
|
-
//
|
|
19
|
+
// Correct scripts for package.json
|
|
19
20
|
const CORRECT_SCRIPTS = {
|
|
20
21
|
"typograf": "node typograf-batch.js",
|
|
21
22
|
"dev": "node resize-all.cjs && node scripts/generate-previews.js && astro dev",
|
|
@@ -43,7 +44,7 @@ const CORRECT_SCRIPTS = {
|
|
|
43
44
|
"generate-previews": "node scripts/generate-previews.js"
|
|
44
45
|
};
|
|
45
46
|
|
|
46
|
-
//
|
|
47
|
+
// Script files that need to be copied
|
|
47
48
|
const REQUIRED_SCRIPTS = [
|
|
48
49
|
'scripts/flatten-images.cjs',
|
|
49
50
|
'scripts/optimize-images.cjs',
|
|
@@ -97,20 +98,20 @@ function updateBlogProject(projectPath) {
|
|
|
97
98
|
log(`Updating project: ${absolutePath}`, 'info');
|
|
98
99
|
|
|
99
100
|
try {
|
|
100
|
-
// 1.
|
|
101
|
+
// 1. Read package.json
|
|
101
102
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
102
103
|
|
|
103
|
-
// 2.
|
|
104
|
+
// 2. Check that this is a core-maugli project
|
|
104
105
|
if (packageJson.name !== 'core-maugli') {
|
|
105
106
|
log(`Skipping: not a core-maugli project (${packageJson.name})`, 'warning');
|
|
106
107
|
return false;
|
|
107
108
|
}
|
|
108
109
|
|
|
109
|
-
// 3.
|
|
110
|
+
// 3. Update version
|
|
110
111
|
const oldVersion = packageJson.version;
|
|
111
112
|
packageJson.version = CURRENT_VERSION;
|
|
112
113
|
|
|
113
|
-
// 4.
|
|
114
|
+
// 4. Update scripts
|
|
114
115
|
let scriptsUpdated = false;
|
|
115
116
|
for (const [scriptName, scriptValue] of Object.entries(CORRECT_SCRIPTS)) {
|
|
116
117
|
if (packageJson.scripts[scriptName] !== scriptValue) {
|
|
@@ -119,10 +120,10 @@ function updateBlogProject(projectPath) {
|
|
|
119
120
|
}
|
|
120
121
|
}
|
|
121
122
|
|
|
122
|
-
// 5.
|
|
123
|
+
// 5. Save package.json
|
|
123
124
|
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 4));
|
|
124
125
|
|
|
125
|
-
// 6.
|
|
126
|
+
// 6. Copy missing scripts
|
|
126
127
|
const scriptsDir = path.join(absolutePath, 'scripts');
|
|
127
128
|
if (!fs.existsSync(scriptsDir)) {
|
|
128
129
|
fs.mkdirSync(scriptsDir, { recursive: true });
|
|
@@ -7,7 +7,7 @@ import { fileURLToPath, pathToFileURL } from 'url';
|
|
|
7
7
|
const __filename = fileURLToPath(import.meta.url);
|
|
8
8
|
const __dirname = path.dirname(__filename);
|
|
9
9
|
|
|
10
|
-
//
|
|
10
|
+
// Define root directories
|
|
11
11
|
const isInNodeModules = __dirname.includes('node_modules');
|
|
12
12
|
const isSourceProject = !isInNodeModules && (
|
|
13
13
|
__dirname.includes('core-maugli-blog') ||
|
|
@@ -83,7 +83,7 @@ async function updateConfigVersion() {
|
|
|
83
83
|
|
|
84
84
|
let updated = false;
|
|
85
85
|
|
|
86
|
-
//
|
|
86
|
+
// Update dependency version
|
|
87
87
|
if (userPackageData.dependencies && userPackageData.dependencies['core-maugli']) {
|
|
88
88
|
const currentVersion = userPackageData.dependencies['core-maugli'];
|
|
89
89
|
if (currentVersion !== `^${newVersion}`) {
|
|
@@ -100,14 +100,14 @@ async function updateConfigVersion() {
|
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
//
|
|
103
|
+
// Update build script to include preview generation
|
|
104
104
|
if (userPackageData.scripts) {
|
|
105
105
|
const expectedBuildScript = "node typograf-batch.js && node scripts/generate-previews.js && node scripts/verify-assets.js && astro build";
|
|
106
106
|
const currentBuildScript = userPackageData.scripts.build;
|
|
107
107
|
|
|
108
|
-
//
|
|
108
|
+
// Check if build script contains preview generation
|
|
109
109
|
if (currentBuildScript && !currentBuildScript.includes('generate-previews.js')) {
|
|
110
|
-
//
|
|
110
|
+
// Add preview generation to build process
|
|
111
111
|
if (currentBuildScript.includes('astro build')) {
|
|
112
112
|
userPackageData.scripts.build = currentBuildScript.replace(
|
|
113
113
|
'astro build',
|
|
@@ -119,9 +119,6 @@ async function updateConfigVersion() {
|
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
// Создаем файл sw.js.astro для исправления PWA маршрутизации
|
|
123
|
-
await createPWAServiceWorkerFile();
|
|
124
|
-
|
|
125
122
|
if (updated) {
|
|
126
123
|
await fs.writeFile(userPackageJsonPath, JSON.stringify(userPackageData, null, 2) + '\n', 'utf-8');
|
|
127
124
|
console.log(`📦 Updated package.json with version ^${newVersion}`);
|
|
@@ -140,42 +137,6 @@ async function updateConfigVersion() {
|
|
|
140
137
|
}
|
|
141
138
|
}
|
|
142
139
|
|
|
143
|
-
async function createPWAServiceWorkerFile() {
|
|
144
|
-
try {
|
|
145
|
-
const swPath = path.join(userRoot, 'src/pages/sw.js.astro');
|
|
146
|
-
|
|
147
|
-
// Проверяем, существует ли уже файл
|
|
148
|
-
try {
|
|
149
|
-
await fs.stat(swPath);
|
|
150
|
-
console.log('🔧 PWA service worker file already exists, skipping...');
|
|
151
|
-
return;
|
|
152
|
-
} catch {
|
|
153
|
-
// Файл не существует, создаем его
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
const swContent = `---
|
|
157
|
-
// Astro page для обработки service worker запросов PWA
|
|
158
|
-
// Это исправляет предупреждения маршрутизации в dev режиме
|
|
159
|
-
---
|
|
160
|
-
|
|
161
|
-
<script>
|
|
162
|
-
// Перенаправляем на актуальный service worker
|
|
163
|
-
if (typeof window !== 'undefined') {
|
|
164
|
-
window.location.href = '/dev-dist/sw.js';
|
|
165
|
-
}
|
|
166
|
-
</script>`;
|
|
167
|
-
|
|
168
|
-
// Создаем папку если её нет
|
|
169
|
-
await fs.mkdir(path.dirname(swPath), { recursive: true });
|
|
170
|
-
|
|
171
|
-
// Создаем файл
|
|
172
|
-
await fs.writeFile(swPath, swContent, 'utf-8');
|
|
173
|
-
console.log('🔧 Created PWA service worker file: src/pages/sw.js.astro');
|
|
174
|
-
} catch (error) {
|
|
175
|
-
console.warn('Warning: Could not create PWA service worker file:', error.message);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
140
|
async function copyDirectory(src, dest) {
|
|
180
141
|
try {
|
|
181
142
|
await fs.mkdir(dest, { recursive: true });
|
|
@@ -243,7 +204,7 @@ async function updateComponents() {
|
|
|
243
204
|
return;
|
|
244
205
|
}
|
|
245
206
|
|
|
246
|
-
//
|
|
207
|
+
// Additional check
|
|
247
208
|
if (packageRoot === userRoot) {
|
|
248
209
|
console.log('⚠️ Skipping component update (packageRoot equals userRoot)');
|
|
249
210
|
return;
|
|
@@ -286,7 +247,7 @@ async function updateComponents() {
|
|
|
286
247
|
// Обрабатываем стили отдельно
|
|
287
248
|
await updateStyles();
|
|
288
249
|
|
|
289
|
-
//
|
|
250
|
+
// Update version in config
|
|
290
251
|
await updateConfigVersion();
|
|
291
252
|
|
|
292
253
|
console.log(`✅ Updated ${updatedCount} component directories/files`);
|
package/src/pages/[...id].astro
CHANGED
|
@@ -7,7 +7,7 @@ import BaseLayout from '../layouts/BaseLayout.astro';
|
|
|
7
7
|
|
|
8
8
|
export async function getStaticPaths() {
|
|
9
9
|
const pages = await getCollection('pages');
|
|
10
|
-
|
|
10
|
+
|
|
11
11
|
return pages.map((page) => {
|
|
12
12
|
return {
|
|
13
13
|
params: { id: page.id },
|
|
@@ -20,12 +20,6 @@ type Props = { page: CollectionEntry<'pages'> };
|
|
|
20
20
|
|
|
21
21
|
const { page } = Astro.props;
|
|
22
22
|
|
|
23
|
-
// Блокируем обработку системных файлов PWA
|
|
24
|
-
const pwaFiles = ['sw.js', 'manifest.json', 'workbox-', 'service-worker.js'];
|
|
25
|
-
if (pwaFiles.some(file => Astro.url.pathname.includes(file))) {
|
|
26
|
-
return new Response(null, { status: 404 });
|
|
27
|
-
}
|
|
28
|
-
|
|
29
23
|
const { title, seo } = page.data;
|
|
30
24
|
const { Content } = await render(page);
|
|
31
25
|
|
package/src/utils/posts.ts
CHANGED
|
@@ -11,7 +11,7 @@ export async function getFeaturedPosts() {
|
|
|
11
11
|
.sort((a, b) => b.data.publishDate.getTime() - a.data.publishDate.getTime())
|
|
12
12
|
.slice(0, 3); // Максимум 3 featured поста
|
|
13
13
|
} catch (error) {
|
|
14
|
-
console.error('
|
|
14
|
+
console.error('Error getting featured posts:', error);
|
|
15
15
|
return [];
|
|
16
16
|
}
|
|
17
17
|
}
|
|
@@ -24,7 +24,7 @@ export async function getAllPosts() {
|
|
|
24
24
|
const allPosts = await getFilteredCollection('blog');
|
|
25
25
|
return allPosts.sort((a, b) => b.data.publishDate.getTime() - a.data.publishDate.getTime());
|
|
26
26
|
} catch (error) {
|
|
27
|
-
console.error('
|
|
27
|
+
console.error('Error getting posts:', error);
|
|
28
28
|
return [];
|
|
29
29
|
}
|
|
30
30
|
}
|
package/src/middleware.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { defineMiddleware } from 'astro:middleware';
|
|
2
|
-
|
|
3
|
-
export const onRequest = defineMiddleware(async (context, next) => {
|
|
4
|
-
// Список системных файлов PWA, которые не должны обрабатываться динамическими маршрутами
|
|
5
|
-
const pwaFiles = [
|
|
6
|
-
'/sw.js',
|
|
7
|
-
'/manifest.json',
|
|
8
|
-
'/manifest.webmanifest',
|
|
9
|
-
'/workbox-',
|
|
10
|
-
'/service-worker.js'
|
|
11
|
-
];
|
|
12
|
-
|
|
13
|
-
// Проверяем, является ли запрос системным файлом PWA
|
|
14
|
-
if (pwaFiles.some(file => context.url.pathname.startsWith(file))) {
|
|
15
|
-
// Если это системный файл, пропускаем обработку Astro и позволяем Vite/PWA плагину обработать его
|
|
16
|
-
return next();
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// Для всех остальных запросов продолжаем обычную обработку
|
|
20
|
-
return next();
|
|
21
|
-
});
|
package/src/pages/sw.js.astro
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
// Этот файл блокирует обработку /sw.js через динамический маршрут [...]
|
|
3
|
-
// Фактический service worker генерируется VitePWA плагином
|
|
4
|
-
---
|
|
5
|
-
<script>
|
|
6
|
-
// Redirect to actual service worker generated by VitePWA
|
|
7
|
-
window.location.href = '/dev-dist/sw.js';
|
|
8
|
-
</script>
|