core-maugli 1.2.15 → 1.2.17
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/bin/init.js +201 -0
- package/package.json +3 -2
- package/src/components/Card.astro +3 -3
- package/src/components/RubricCard.astro +11 -16
package/bin/init.js
ADDED
@@ -0,0 +1,201 @@
|
|
1
|
+
#!/usr/bin/env node
|
2
|
+
|
3
|
+
import { execSync } from 'child_process';
|
4
|
+
import { cpSync, existsSync, readFileSync, writeFileSync } from 'fs';
|
5
|
+
import path from 'path';
|
6
|
+
import readline from 'readline';
|
7
|
+
import { fileURLToPath } from 'url';
|
8
|
+
|
9
|
+
const __filename = fileURLToPath(import.meta.url);
|
10
|
+
const __dirname = path.dirname(__filename);
|
11
|
+
const templateRoot = path.join(__dirname, '..');
|
12
|
+
|
13
|
+
function getLanguageCodes() {
|
14
|
+
const file = readFileSync(path.join(templateRoot, 'src/i18n/languages.ts'), 'utf8');
|
15
|
+
const codes = [];
|
16
|
+
const regex = /{\s*code:\s*'([^']+)'/g;
|
17
|
+
let match;
|
18
|
+
while ((match = regex.exec(file)) !== null) {
|
19
|
+
codes.push(match[1]);
|
20
|
+
}
|
21
|
+
return codes;
|
22
|
+
}
|
23
|
+
|
24
|
+
function promptLang(codes) {
|
25
|
+
return new Promise(resolve => {
|
26
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
27
|
+
rl.question(`Choose language (${codes.join(', ')}): `, answer => {
|
28
|
+
rl.close();
|
29
|
+
resolve(codes.includes(answer) ? answer : codes[0]);
|
30
|
+
});
|
31
|
+
});
|
32
|
+
}
|
33
|
+
|
34
|
+
function promptRepo() {
|
35
|
+
return new Promise(resolve => {
|
36
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
37
|
+
rl.question('Repository URL: ', answer => {
|
38
|
+
rl.close();
|
39
|
+
resolve(answer.trim());
|
40
|
+
});
|
41
|
+
});
|
42
|
+
}
|
43
|
+
|
44
|
+
async function getRepoUrl(targetDir, repoOption) {
|
45
|
+
if (repoOption) return repoOption;
|
46
|
+
try {
|
47
|
+
const url = execSync('git remote get-url origin', {
|
48
|
+
cwd: targetDir,
|
49
|
+
stdio: ['ignore', 'pipe', 'ignore']
|
50
|
+
})
|
51
|
+
.toString()
|
52
|
+
.trim();
|
53
|
+
if (url) return url;
|
54
|
+
} catch {
|
55
|
+
// ignore
|
56
|
+
}
|
57
|
+
return await promptRepo();
|
58
|
+
}
|
59
|
+
|
60
|
+
function updateReadme(targetDir, repoUrl) {
|
61
|
+
if (!repoUrl) return;
|
62
|
+
const readmePath = path.join(targetDir, 'README.md');
|
63
|
+
if (!existsSync(readmePath)) return;
|
64
|
+
let content = readFileSync(readmePath, 'utf8');
|
65
|
+
const pattern = /https:\/\/app\.netlify\.com\/start\/deploy\?repository=[^\)\s]+/;
|
66
|
+
content = content.replace(
|
67
|
+
pattern,
|
68
|
+
`https://app.netlify.com/start/deploy?repository=${repoUrl}`
|
69
|
+
);
|
70
|
+
writeFileSync(readmePath, content);
|
71
|
+
console.log('Updated Netlify link in README.md');
|
72
|
+
}
|
73
|
+
|
74
|
+
function updateConfig(targetDir, lang, repoUrl) {
|
75
|
+
const configPath = path.join(targetDir, 'src', 'config', 'maugli.config.ts');
|
76
|
+
if (!existsSync(configPath)) return;
|
77
|
+
let content = readFileSync(configPath, 'utf8');
|
78
|
+
content = content.replace(/defaultLang:\s*'[^']*'/, `defaultLang: '${lang}'`);
|
79
|
+
const multiMatch = content.match(/enableMultiLang:\s*(true|false)/);
|
80
|
+
const multi = multiMatch ? multiMatch[1] === 'true' : false;
|
81
|
+
content = content.replace(/showLangSwitcher:\s*(true|false)/, `showLangSwitcher: ${multi}`);
|
82
|
+
|
83
|
+
// Update repository URL if provided
|
84
|
+
if (repoUrl) {
|
85
|
+
content = content.replace(
|
86
|
+
/repository:\s*{[^}]*url:\s*'[^']*'/,
|
87
|
+
`repository: {\n url: '${repoUrl}'`
|
88
|
+
);
|
89
|
+
}
|
90
|
+
|
91
|
+
writeFileSync(configPath, content);
|
92
|
+
console.log(`Configured default language to ${lang}`);
|
93
|
+
if (repoUrl) {
|
94
|
+
console.log(`Configured repository URL to ${repoUrl}`);
|
95
|
+
}
|
96
|
+
}
|
97
|
+
|
98
|
+
export default async function init(targetName, langOption, repoOption) {
|
99
|
+
const targetDir = targetName ? path.resolve(targetName) : process.cwd();
|
100
|
+
const codes = getLanguageCodes();
|
101
|
+
const lang = langOption && codes.includes(langOption) ? langOption : await promptLang(codes);
|
102
|
+
|
103
|
+
function copyItem(item) {
|
104
|
+
const src = path.join(templateRoot, item);
|
105
|
+
const dest = path.join(targetDir, item);
|
106
|
+
|
107
|
+
if (!existsSync(src)) {
|
108
|
+
console.log(`Skipped ${item} (not found)`);
|
109
|
+
return;
|
110
|
+
}
|
111
|
+
|
112
|
+
cpSync(src, dest, { recursive: true });
|
113
|
+
console.log(`Copied ${item}`);
|
114
|
+
}
|
115
|
+
|
116
|
+
// Copy package files first so npm install works correctly
|
117
|
+
['package.json', 'package-lock.json'].forEach(file => {
|
118
|
+
if (existsSync(path.join(templateRoot, file))) {
|
119
|
+
copyItem(file);
|
120
|
+
}
|
121
|
+
});
|
122
|
+
|
123
|
+
const items = [
|
124
|
+
'astro.config.mjs',
|
125
|
+
'tsconfig.json',
|
126
|
+
'vite.config.js',
|
127
|
+
'public',
|
128
|
+
'src',
|
129
|
+
'scripts',
|
130
|
+
'typograf-batch.js',
|
131
|
+
'resize-all.cjs',
|
132
|
+
'README.md',
|
133
|
+
'LICENSE'
|
134
|
+
];
|
135
|
+
items.forEach(copyItem);
|
136
|
+
|
137
|
+
const repoUrl = await getRepoUrl(targetDir, repoOption);
|
138
|
+
updateReadme(targetDir, repoUrl);
|
139
|
+
|
140
|
+
// Create essential config files
|
141
|
+
const gitignoreContent = `
|
142
|
+
# Dependencies
|
143
|
+
node_modules/
|
144
|
+
.pnpm-debug.log*
|
145
|
+
|
146
|
+
# Environment
|
147
|
+
.env
|
148
|
+
.env.local
|
149
|
+
.env.production
|
150
|
+
|
151
|
+
# Build outputs
|
152
|
+
dist/
|
153
|
+
.astro/
|
154
|
+
|
155
|
+
# Generated files
|
156
|
+
.DS_Store
|
157
|
+
.vscode/settings.json
|
158
|
+
|
159
|
+
# Cache
|
160
|
+
.typograf-cache.json
|
161
|
+
`;
|
162
|
+
|
163
|
+
const prettierrcContent = `{
|
164
|
+
"semi": true,
|
165
|
+
"singleQuote": true,
|
166
|
+
"tabWidth": 2,
|
167
|
+
"trailingComma": "es5",
|
168
|
+
"printWidth": 100,
|
169
|
+
"plugins": ["prettier-plugin-tailwindcss"]
|
170
|
+
}
|
171
|
+
`;
|
172
|
+
|
173
|
+
writeFileSync(path.join(targetDir, '.gitignore'), gitignoreContent.trim());
|
174
|
+
console.log('Created .gitignore');
|
175
|
+
|
176
|
+
writeFileSync(path.join(targetDir, '.prettierrc'), prettierrcContent);
|
177
|
+
console.log('Created .prettierrc');
|
178
|
+
|
179
|
+
execSync('npm install', { cwd: targetDir, stdio: 'inherit' });
|
180
|
+
updateConfig(targetDir, lang, repoUrl);
|
181
|
+
}
|
182
|
+
|
183
|
+
// Если скрипт запускается напрямую
|
184
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
185
|
+
const args = process.argv.slice(2);
|
186
|
+
let targetName;
|
187
|
+
let lang;
|
188
|
+
let repo;
|
189
|
+
for (let i = 0; i < args.length; i++) {
|
190
|
+
if (args[i] === '--lang' && i + 1 < args.length) {
|
191
|
+
lang = args[i + 1];
|
192
|
+
i++;
|
193
|
+
} else if (args[i] === '--repo' && i + 1 < args.length) {
|
194
|
+
repo = args[i + 1];
|
195
|
+
i++;
|
196
|
+
} else {
|
197
|
+
targetName = args[i];
|
198
|
+
}
|
199
|
+
}
|
200
|
+
await init(targetName, lang, repo);
|
201
|
+
}
|
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.17",
|
6
6
|
"license": "GPL-3.0-or-later OR Commercial",
|
7
7
|
"repository": {
|
8
8
|
"type": "git",
|
@@ -64,7 +64,8 @@
|
|
64
64
|
"files": [
|
65
65
|
"src",
|
66
66
|
"public",
|
67
|
-
"scripts"
|
67
|
+
"scripts",
|
68
|
+
"bin"
|
68
69
|
],
|
69
70
|
"bin": {
|
70
71
|
"core-maugli": "bin/index.js"
|
@@ -52,8 +52,8 @@ const cardImageAlt = seo?.image?.alt || image?.alt || title || 'Изображе
|
|
52
52
|
|
53
53
|
// Используем уменьшенное превью, если оно существует
|
54
54
|
let previewImage;
|
55
|
-
if (cardImage) {
|
56
|
-
previewImage = cardImage.replace(/\/([^\/]+)$/, '/previews/$1');
|
55
|
+
if (cardImage.src) {
|
56
|
+
previewImage = cardImage.src.replace(/\/([^\/]+)$/, '/previews/$1');
|
57
57
|
|
58
58
|
const __filename = fileURLToPath(import.meta.url);
|
59
59
|
const projectRoot = path.resolve(path.dirname(__filename), '../..');
|
@@ -64,7 +64,7 @@ if (cardImage) {
|
|
64
64
|
}
|
65
65
|
}
|
66
66
|
|
67
|
-
const finalImage = previewImage || cardImage;
|
67
|
+
const finalImage = previewImage || cardImage.src;
|
68
68
|
|
69
69
|
// Определяем контент для отображения
|
70
70
|
const content = excerpt || description;
|
@@ -194,23 +194,18 @@ if (import.meta.env.DEV) {
|
|
194
194
|
class={`w-full h-[195px] border border-[var(--border-main)] rounded-custom card-bg hover:card-shadow hover:-translate-y-1 transition-all duration-300 p-6 flex flex-row gap-4 items-start ${className}`}
|
195
195
|
>
|
196
196
|
<!-- Левая часть: картинка и дата -->
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
<span style="position:absolute;left:0;top:0;width:105px;height:107px;display:flex;align-items:center;justify-content:center;pointer-events:none;z-index:2;">
|
206
|
-
<span style="position:absolute;left:0;top:0;width:105px;height:107px;background:rgba(0,0,0,0.10);border-radius:inherit;z-index:1;" />
|
207
|
-
<span style="position:relative;z-index:2;font-size:48px;font-weight:700;font-family:inherit;color:var(--brand-color);letter-spacing:2px;">
|
208
|
-
{rubricInitials}
|
209
|
-
</span>
|
197
|
+
<div class="flex flex-col items-end gap-2 w-[105px] h-[147px] relative">
|
198
|
+
<img src={previewImageSrc} alt={title} class="w-[105px] h-[107px] object-cover rounded-custom" loading="lazy" />
|
199
|
+
{
|
200
|
+
rubricInitials && (
|
201
|
+
<span style="position:absolute;left:0;top:0;width:105px;height:107px;display:flex;align-items:center;justify-content:center;pointer-events:none;z-index:2;">
|
202
|
+
<span style="position:absolute;left:0;top:0;width:105px;height:107px;background:rgba(0,0,0,0.10);border-radius:inherit;z-index:1;" />
|
203
|
+
<span style="position:relative;z-index:2;font-size:48px;font-weight:700;font-family:inherit;color:var(--brand-color);letter-spacing:2px;">
|
204
|
+
{rubricInitials}
|
210
205
|
</span>
|
211
|
-
|
212
|
-
|
213
|
-
|
206
|
+
</span>
|
207
|
+
)
|
208
|
+
}
|
214
209
|
<div class="flex flex-col items-end gap-1 w-[74px] h-[32px]">
|
215
210
|
<span class={`flex items-center gap-1 text-[12px] text-right ${isBrandDate ? 'text-[var(--brand-color)]' : 'text-[var(--text-muted)]'}`}>
|
216
211
|
<svg
|