@jet-w/astro-blog 0.2.3 → 0.2.5
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/dist/{chunk-Z3O3JK56.js → chunk-CEZBWSMU.js} +34 -5
- package/dist/index.js +1 -1
- package/dist/utils/i18n.d.ts +34 -2
- package/dist/utils/i18n.js +6 -2
- package/package.json +1 -1
- package/src/components/blog/Hero.astro +8 -4
- package/src/components/blog/PostCard.astro +8 -4
- package/src/components/home/FeaturedPostsSection.astro +2 -1
- package/src/components/home/QuickNavSection.astro +2 -1
- package/src/components/home/RecentPostsSection.astro +2 -1
- package/src/components/layout/Footer.astro +2 -1
- package/src/components/layout/Header.astro +8 -4
- package/src/components/layout/Sidebar.astro +2 -1
- package/src/layouts/BaseLayout.astro +8 -4
- package/src/pages/archives/[year]/[month].astro +2 -1
- package/src/pages/archives/index.astro +2 -1
- package/src/pages/categories/[category].astro +2 -1
- package/src/pages/categories/index.astro +2 -1
- package/src/pages/posts/[...slug].astro +2 -1
- package/src/pages/posts/index.astro +2 -1
- package/src/pages/posts/page/[page].astro +2 -1
- package/src/pages/slides/index.astro +2 -1
- package/src/pages/tags/[tag].astro +2 -1
- package/src/pages/tags/index.astro +2 -1
- package/src/plugins/remark-containers.mjs +235 -79
- package/src/utils/i18n.ts +83 -4
- package/templates/default/.claude/ralph-loop.local.md +34 -43
- package/templates/default/astro.config.mjs +2 -2
- package/templates/default/content/posts/blog_docs_en/01.get-started/03-create-post.md +124 -8
- package/templates/default/content/posts/blog_docs_zh/01.get-started/03-create-post.md +124 -9
- package/templates/default/package-lock.json +0 -9667
- package/templates/default/package.dev.json +0 -31
|
@@ -2,105 +2,223 @@ import { visit } from 'unist-util-visit';
|
|
|
2
2
|
|
|
3
3
|
export function remarkContainers() {
|
|
4
4
|
return (tree, file) => {
|
|
5
|
-
// Pre-process: Handle
|
|
5
|
+
// Pre-process: Handle containers that span across multiple sibling nodes
|
|
6
6
|
// This handles cases like:
|
|
7
|
-
// ::: tip
|
|
8
|
-
// Content
|
|
7
|
+
// ::: tip Title
|
|
8
|
+
// Content text here
|
|
9
|
+
// - list item 1
|
|
10
|
+
// - list item 2
|
|
9
11
|
// :::
|
|
10
|
-
// Where the
|
|
11
|
-
//
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const lastChild = node.children[node.children.length - 1];
|
|
12
|
+
// Where the container starts in one paragraph, has sibling nodes (lists, etc),
|
|
13
|
+
// and the closing ::: may be in a later text node
|
|
14
|
+
function processMultiNodeContainers(tree) {
|
|
15
|
+
visit(tree, 'paragraph', (node, index, parent) => {
|
|
16
|
+
if (!node.children || node.children.length === 0) return;
|
|
17
|
+
if (!parent || !parent.children) return;
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
// Last child must be text ending with :::
|
|
21
|
-
if (lastChild.type !== 'text') return;
|
|
19
|
+
const firstChild = node.children[0];
|
|
20
|
+
if (firstChild.type !== 'text') return;
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
const firstText = firstChild.value;
|
|
23
|
+
|
|
24
|
+
// Check if first text starts with ::: type [title]
|
|
25
|
+
// Allow content to follow on subsequent lines within this paragraph
|
|
26
|
+
const startMatch = firstText.match(/^(:{3,})\s+(tip|note|warning|danger|info|details)([ \t]+[^\n]*)?\n?/);
|
|
27
|
+
if (!startMatch) return;
|
|
28
|
+
|
|
29
|
+
const [fullMatch, openColons, type, titlePart] = startMatch;
|
|
30
|
+
const colonCount = openColons.length;
|
|
31
|
+
const customTitle = titlePart ? titlePart.trim() : '';
|
|
32
|
+
const title = customTitle || getDefaultTitle(type);
|
|
33
|
+
|
|
34
|
+
// Now we need to find the closing ::: which could be:
|
|
35
|
+
// 1. At the end of this same paragraph's text
|
|
36
|
+
// 2. In a sibling paragraph
|
|
37
|
+
// 3. Inside a text node in a list item (no blank line before :::)
|
|
38
|
+
|
|
39
|
+
const siblings = parent.children;
|
|
40
|
+
let endIndex = -1;
|
|
41
|
+
let endNodeInfo = null; // { siblingIndex, childPath, closeMatch }
|
|
42
|
+
|
|
43
|
+
// First check if closing is in the same paragraph
|
|
44
|
+
const lastChild = node.children[node.children.length - 1];
|
|
45
|
+
if (lastChild.type === 'text') {
|
|
46
|
+
const closeInSame = lastChild.value.match(/\n(:{3,})\s*$/);
|
|
47
|
+
if (closeInSame && closeInSame[1].length === colonCount) {
|
|
48
|
+
// Closing is in the same paragraph - handle as single paragraph container
|
|
49
|
+
endNodeInfo = { type: 'same-paragraph', closeMatch: closeInSame };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
25
52
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
53
|
+
if (!endNodeInfo) {
|
|
54
|
+
// Search through siblings for the closing :::
|
|
55
|
+
for (let i = index + 1; i < siblings.length; i++) {
|
|
56
|
+
const sibling = siblings[i];
|
|
29
57
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
58
|
+
// Check if sibling is a paragraph with just :::
|
|
59
|
+
if (sibling.type === 'paragraph' &&
|
|
60
|
+
sibling.children &&
|
|
61
|
+
sibling.children.length === 1 &&
|
|
62
|
+
sibling.children[0].type === 'text') {
|
|
63
|
+
const text = sibling.children[0].value.trim();
|
|
64
|
+
const closeMatch = text.match(/^(:{3,})$/);
|
|
65
|
+
if (closeMatch && closeMatch[1].length === colonCount) {
|
|
66
|
+
endIndex = i;
|
|
67
|
+
endNodeInfo = { type: 'sibling-paragraph', siblingIndex: i };
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
33
71
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
72
|
+
// Check if closing ::: is embedded in a text node (e.g., after a list item)
|
|
73
|
+
// This happens when there's no blank line before :::
|
|
74
|
+
if (sibling.type === 'list' && sibling.children) {
|
|
75
|
+
// Check the last item of the list
|
|
76
|
+
const lastItem = sibling.children[sibling.children.length - 1];
|
|
77
|
+
if (lastItem.children) {
|
|
78
|
+
const lastItemPara = lastItem.children[lastItem.children.length - 1];
|
|
79
|
+
if (lastItemPara.type === 'paragraph' && lastItemPara.children) {
|
|
80
|
+
const lastText = lastItemPara.children[lastItemPara.children.length - 1];
|
|
81
|
+
if (lastText.type === 'text') {
|
|
82
|
+
const closeMatch = lastText.value.match(/\n(:{3,})\s*$/);
|
|
83
|
+
if (closeMatch && closeMatch[1].length === colonCount) {
|
|
84
|
+
endIndex = i;
|
|
85
|
+
endNodeInfo = {
|
|
86
|
+
type: 'in-list',
|
|
87
|
+
siblingIndex: i,
|
|
88
|
+
lastText: lastText,
|
|
89
|
+
closeMatch: closeMatch
|
|
90
|
+
};
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
38
99
|
|
|
39
|
-
|
|
40
|
-
const title = customTitle || getDefaultTitle(type);
|
|
100
|
+
if (!endNodeInfo) return; // No closing found
|
|
41
101
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
102
|
+
// Create HTML wrapper
|
|
103
|
+
let openingHTML, closingHTML;
|
|
104
|
+
if (type === 'details') {
|
|
105
|
+
openingHTML = `<details class="container-details custom-container" data-container-type="details">
|
|
46
106
|
<summary class="container-title">${title}</summary>
|
|
47
107
|
<div class="container-content">`;
|
|
48
|
-
|
|
108
|
+
closingHTML = `</div>
|
|
49
109
|
</details>`;
|
|
50
|
-
|
|
51
|
-
|
|
110
|
+
} else {
|
|
111
|
+
openingHTML = `<div class="container-${type} custom-container" data-container-type="${type}">
|
|
52
112
|
<div class="container-title">${title}</div>
|
|
53
113
|
<div class="container-content">`;
|
|
54
|
-
|
|
114
|
+
closingHTML = `</div>
|
|
55
115
|
</div>`;
|
|
56
|
-
|
|
116
|
+
}
|
|
57
117
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
118
|
+
const htmlStartNode = { type: 'html', value: openingHTML };
|
|
119
|
+
const htmlEndNode = { type: 'html', value: closingHTML };
|
|
120
|
+
|
|
121
|
+
if (endNodeInfo.type === 'same-paragraph') {
|
|
122
|
+
// Handle single paragraph container
|
|
123
|
+
const lastChild = node.children[node.children.length - 1];
|
|
124
|
+
const newFirstText = firstText.slice(fullMatch.length);
|
|
125
|
+
const newLastText = lastChild.value.slice(0, endNodeInfo.closeMatch.index);
|
|
126
|
+
|
|
127
|
+
const contentChildren = [];
|
|
128
|
+
for (let i = 0; i < node.children.length; i++) {
|
|
129
|
+
const child = node.children[i];
|
|
130
|
+
if (i === 0) {
|
|
131
|
+
if (node.children.length === 1) {
|
|
132
|
+
// Single text node
|
|
133
|
+
const middleText = newFirstText.slice(0, newFirstText.length - (firstText.length - lastChild.value.length) - endNodeInfo.closeMatch[0].length);
|
|
134
|
+
if (middleText.trim()) {
|
|
135
|
+
contentChildren.push({ ...child, value: middleText.trim() });
|
|
136
|
+
}
|
|
137
|
+
} else if (newFirstText.trim()) {
|
|
138
|
+
contentChildren.push({ ...child, value: newFirstText });
|
|
139
|
+
}
|
|
140
|
+
} else if (i === node.children.length - 1) {
|
|
141
|
+
if (newLastText.trim()) {
|
|
142
|
+
contentChildren.push({ ...child, value: newLastText });
|
|
143
|
+
}
|
|
144
|
+
} else {
|
|
145
|
+
contentChildren.push({ ...child });
|
|
146
|
+
}
|
|
147
|
+
}
|
|
63
148
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
149
|
+
if (contentChildren.length === 0) return;
|
|
150
|
+
|
|
151
|
+
const contentParagraph = { type: 'paragraph', children: contentChildren };
|
|
152
|
+
parent.children.splice(index, 1, htmlStartNode, contentParagraph, htmlEndNode);
|
|
153
|
+
return index + 3;
|
|
154
|
+
|
|
155
|
+
} else if (endNodeInfo.type === 'sibling-paragraph') {
|
|
156
|
+
// Closing is in a sibling paragraph
|
|
157
|
+
// Extract content from opening paragraph (after the ::: line)
|
|
158
|
+
const newFirstText = firstText.slice(fullMatch.length);
|
|
159
|
+
const contentNodes = [];
|
|
160
|
+
|
|
161
|
+
// Add remaining content from opening paragraph if any
|
|
162
|
+
if (newFirstText.trim() || node.children.length > 1) {
|
|
163
|
+
const newParaChildren = [];
|
|
164
|
+
if (newFirstText.trim()) {
|
|
165
|
+
newParaChildren.push({ ...firstChild, value: newFirstText });
|
|
166
|
+
}
|
|
167
|
+
for (let i = 1; i < node.children.length; i++) {
|
|
168
|
+
newParaChildren.push({ ...node.children[i] });
|
|
169
|
+
}
|
|
170
|
+
if (newParaChildren.length > 0) {
|
|
171
|
+
contentNodes.push({ type: 'paragraph', children: newParaChildren });
|
|
75
172
|
}
|
|
76
|
-
} else if (newFirstText.trim()) {
|
|
77
|
-
contentChildren.push({ ...child, value: newFirstText });
|
|
78
173
|
}
|
|
79
|
-
|
|
80
|
-
//
|
|
81
|
-
|
|
82
|
-
|
|
174
|
+
|
|
175
|
+
// Add all siblings between opening and closing
|
|
176
|
+
for (let i = index + 1; i < endIndex; i++) {
|
|
177
|
+
contentNodes.push(siblings[i]);
|
|
83
178
|
}
|
|
84
|
-
} else {
|
|
85
|
-
// Middle children - keep as-is
|
|
86
|
-
contentChildren.push({ ...child });
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
179
|
|
|
90
|
-
|
|
91
|
-
|
|
180
|
+
const replaceCount = endIndex - index + 1;
|
|
181
|
+
const newNodes = [htmlStartNode, ...contentNodes, htmlEndNode];
|
|
182
|
+
parent.children.splice(index, replaceCount, ...newNodes);
|
|
183
|
+
return index + newNodes.length;
|
|
92
184
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
children: contentChildren
|
|
98
|
-
};
|
|
99
|
-
const htmlEndNode = { type: 'html', value: closingHTML };
|
|
185
|
+
} else if (endNodeInfo.type === 'in-list') {
|
|
186
|
+
// Closing ::: is inside the last list item
|
|
187
|
+
// Remove the closing from the text node
|
|
188
|
+
endNodeInfo.lastText.value = endNodeInfo.lastText.value.slice(0, endNodeInfo.closeMatch.index);
|
|
100
189
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
190
|
+
// Extract content from opening paragraph
|
|
191
|
+
const newFirstText = firstText.slice(fullMatch.length);
|
|
192
|
+
const contentNodes = [];
|
|
193
|
+
|
|
194
|
+
if (newFirstText.trim() || node.children.length > 1) {
|
|
195
|
+
const newParaChildren = [];
|
|
196
|
+
if (newFirstText.trim()) {
|
|
197
|
+
newParaChildren.push({ ...firstChild, value: newFirstText });
|
|
198
|
+
}
|
|
199
|
+
for (let i = 1; i < node.children.length; i++) {
|
|
200
|
+
newParaChildren.push({ ...node.children[i] });
|
|
201
|
+
}
|
|
202
|
+
if (newParaChildren.length > 0) {
|
|
203
|
+
contentNodes.push({ type: 'paragraph', children: newParaChildren });
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Add all siblings including the list (which now has the ::: removed)
|
|
208
|
+
for (let i = index + 1; i <= endIndex; i++) {
|
|
209
|
+
contentNodes.push(siblings[i]);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const replaceCount = endIndex - index + 1;
|
|
213
|
+
const newNodes = [htmlStartNode, ...contentNodes, htmlEndNode];
|
|
214
|
+
parent.children.splice(index, replaceCount, ...newNodes);
|
|
215
|
+
return index + newNodes.length;
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Run the multi-node container processor
|
|
221
|
+
processMultiNodeContainers(tree);
|
|
104
222
|
|
|
105
223
|
// Pre-process: Extract ::: from text nodes where it appears at the end
|
|
106
224
|
// This handles cases where ::: is on a new line but without a blank line separator
|
|
@@ -355,11 +473,35 @@ export function remarkContainers() {
|
|
|
355
473
|
console.log('DEBUG containerDirective:', type, 'children:', node.children?.length || 0, JSON.stringify(node.children?.map(c => c.type)));
|
|
356
474
|
}
|
|
357
475
|
|
|
358
|
-
// Get custom title from directive label
|
|
476
|
+
// Get custom title from directive label
|
|
477
|
+
// remark-directive v4 may store label in different places:
|
|
478
|
+
// 1. node.data.directiveLabel (standard location)
|
|
479
|
+
// 2. node.children[0] as a paragraph with the label text
|
|
359
480
|
let customTitle = '';
|
|
481
|
+
let contentChildren = node.children || [];
|
|
482
|
+
|
|
483
|
+
// Check node.data.directiveLabel first (standard remark-directive behavior)
|
|
360
484
|
if (node.data && node.data.directiveLabel) {
|
|
361
485
|
customTitle = node.data.directiveLabel;
|
|
362
486
|
}
|
|
487
|
+
// Check if first child is a paragraph that contains just the title text
|
|
488
|
+
// This happens when label is on same line: ::: warning Title Here
|
|
489
|
+
else if (contentChildren.length > 0) {
|
|
490
|
+
const firstChild = contentChildren[0];
|
|
491
|
+
// If first child is paragraph with single text node, it might be the title
|
|
492
|
+
if (firstChild.type === 'paragraph' &&
|
|
493
|
+
firstChild.children &&
|
|
494
|
+
firstChild.children.length === 1 &&
|
|
495
|
+
firstChild.children[0].type === 'text') {
|
|
496
|
+
const text = firstChild.children[0].value.trim();
|
|
497
|
+
// Check if this looks like a title (single line, no markdown formatting)
|
|
498
|
+
// and the directive has more children (actual content)
|
|
499
|
+
if (!text.includes('\n') && contentChildren.length > 1) {
|
|
500
|
+
customTitle = text;
|
|
501
|
+
contentChildren = contentChildren.slice(1); // Remove title from content
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
363
505
|
|
|
364
506
|
const title = customTitle || getDefaultTitle(type);
|
|
365
507
|
|
|
@@ -382,7 +524,7 @@ export function remarkContainers() {
|
|
|
382
524
|
const htmlStartNode = { type: 'html', value: openingHTML };
|
|
383
525
|
const htmlEndNode = { type: 'html', value: closingHTML };
|
|
384
526
|
|
|
385
|
-
const newNodes = [htmlStartNode, ...
|
|
527
|
+
const newNodes = [htmlStartNode, ...contentChildren, htmlEndNode];
|
|
386
528
|
parent.children.splice(index, 1, ...newNodes);
|
|
387
529
|
|
|
388
530
|
return index + newNodes.length;
|
|
@@ -395,10 +537,24 @@ export function remarkContainers() {
|
|
|
395
537
|
return;
|
|
396
538
|
}
|
|
397
539
|
|
|
540
|
+
// Get custom title from directive label
|
|
398
541
|
let customTitle = '';
|
|
542
|
+
let contentChildren = node.children || [];
|
|
543
|
+
|
|
399
544
|
if (node.data && node.data.directiveLabel) {
|
|
400
545
|
customTitle = node.data.directiveLabel;
|
|
401
546
|
}
|
|
547
|
+
// Check if first child is a text node that could be the title
|
|
548
|
+
else if (contentChildren.length > 0) {
|
|
549
|
+
const firstChild = contentChildren[0];
|
|
550
|
+
if (firstChild.type === 'text') {
|
|
551
|
+
const text = firstChild.value.trim();
|
|
552
|
+
if (!text.includes('\n') && contentChildren.length > 1) {
|
|
553
|
+
customTitle = text;
|
|
554
|
+
contentChildren = contentChildren.slice(1);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
}
|
|
402
558
|
|
|
403
559
|
const title = customTitle || getDefaultTitle(type);
|
|
404
560
|
|
|
@@ -421,7 +577,7 @@ export function remarkContainers() {
|
|
|
421
577
|
const htmlStartNode = { type: 'html', value: openingHTML };
|
|
422
578
|
const htmlEndNode = { type: 'html', value: closingHTML };
|
|
423
579
|
|
|
424
|
-
const newNodes = [htmlStartNode, ...
|
|
580
|
+
const newNodes = [htmlStartNode, ...contentChildren, htmlEndNode];
|
|
425
581
|
parent.children.splice(index, 1, ...newNodes);
|
|
426
582
|
|
|
427
583
|
return index + newNodes.length;
|
package/src/utils/i18n.ts
CHANGED
|
@@ -353,15 +353,38 @@ export function isMultiLanguageEnabled(
|
|
|
353
353
|
/**
|
|
354
354
|
* Get prefix for a locale in routes
|
|
355
355
|
* Returns empty string for default locale if prefixDefaultLocale is false
|
|
356
|
+
* If base is provided, it will be prepended to the locale prefix
|
|
357
|
+
*
|
|
358
|
+
* @example
|
|
359
|
+
* // Without base
|
|
360
|
+
* getLocalePrefix('en', config) // '' (for default locale)
|
|
361
|
+
* getLocalePrefix('zh-CN', config) // '/zh-CN'
|
|
362
|
+
*
|
|
363
|
+
* // With base '/my-blog'
|
|
364
|
+
* getLocalePrefix('en', config, '/my-blog') // '/my-blog'
|
|
365
|
+
* getLocalePrefix('zh-CN', config, '/my-blog') // '/my-blog/zh-CN'
|
|
356
366
|
*/
|
|
357
367
|
export function getLocalePrefix(
|
|
358
368
|
locale: string,
|
|
359
|
-
config: I18nConfig = defaultI18nConfig
|
|
369
|
+
config: I18nConfig = defaultI18nConfig,
|
|
370
|
+
base?: string
|
|
360
371
|
): string {
|
|
361
|
-
|
|
362
|
-
|
|
372
|
+
// Normalize base: remove trailing slash
|
|
373
|
+
const normalizedBase = base ? base.replace(/\/$/, '') : '';
|
|
374
|
+
|
|
375
|
+
// Get locale prefix
|
|
376
|
+
let localePrefix = '';
|
|
377
|
+
if (locale !== config.defaultLocale || config.routing.prefixDefaultLocale) {
|
|
378
|
+
localePrefix = `/${locale}`;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// If base is just '/' or empty, return locale prefix as before
|
|
382
|
+
if (!normalizedBase || normalizedBase === '') {
|
|
383
|
+
return localePrefix;
|
|
363
384
|
}
|
|
364
|
-
|
|
385
|
+
|
|
386
|
+
// Combine base and locale prefix
|
|
387
|
+
return `${normalizedBase}${localePrefix}`;
|
|
365
388
|
}
|
|
366
389
|
|
|
367
390
|
/**
|
|
@@ -406,6 +429,62 @@ export function filterPostsByLocale<T extends { id: string }>(
|
|
|
406
429
|
});
|
|
407
430
|
}
|
|
408
431
|
|
|
432
|
+
/**
|
|
433
|
+
* Add base URL prefix to a path
|
|
434
|
+
* This is used when the site is deployed to a subdirectory (e.g., /jet-w.astro-blog/)
|
|
435
|
+
*
|
|
436
|
+
* @example
|
|
437
|
+
* // If BASE_URL is '/jet-w.astro-blog'
|
|
438
|
+
* withBase('/posts') // '/jet-w.astro-blog/posts'
|
|
439
|
+
* withBase('/') // '/jet-w.astro-blog/'
|
|
440
|
+
*
|
|
441
|
+
* // If BASE_URL is '/'
|
|
442
|
+
* withBase('/posts') // '/posts'
|
|
443
|
+
*/
|
|
444
|
+
export function withBase(path: string, base?: string): string {
|
|
445
|
+
// Get base URL - in Astro components, pass import.meta.env.BASE_URL
|
|
446
|
+
// Remove trailing slash from base
|
|
447
|
+
const baseUrl = (base || '/').replace(/\/$/, '');
|
|
448
|
+
|
|
449
|
+
// If base is empty or just '/', return path as-is
|
|
450
|
+
if (!baseUrl || baseUrl === '') {
|
|
451
|
+
return path;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// Ensure path starts with /
|
|
455
|
+
const normalizedPath = path.startsWith('/') ? path : `/${path}`;
|
|
456
|
+
|
|
457
|
+
// If path is just '/', return base with trailing slash
|
|
458
|
+
if (normalizedPath === '/') {
|
|
459
|
+
return `${baseUrl}/`;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
return `${baseUrl}${normalizedPath}`;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Remove base URL prefix from a path
|
|
467
|
+
* Useful for getting the actual path without base prefix
|
|
468
|
+
*
|
|
469
|
+
* @example
|
|
470
|
+
* // If BASE_URL is '/jet-w.astro-blog'
|
|
471
|
+
* removeBase('/jet-w.astro-blog/posts', '/jet-w.astro-blog') // '/posts'
|
|
472
|
+
*/
|
|
473
|
+
export function removeBase(path: string, base?: string): string {
|
|
474
|
+
const baseUrl = (base || '/').replace(/\/$/, '');
|
|
475
|
+
|
|
476
|
+
if (!baseUrl || baseUrl === '') {
|
|
477
|
+
return path;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
if (path.startsWith(baseUrl)) {
|
|
481
|
+
const rest = path.slice(baseUrl.length);
|
|
482
|
+
return rest || '/';
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
return path;
|
|
486
|
+
}
|
|
487
|
+
|
|
409
488
|
// Re-export types and config functions
|
|
410
489
|
export type { I18nConfig, Locale, LocaleConfig, UITranslations } from '../config/i18n';
|
|
411
490
|
export {
|
|
@@ -1,48 +1,39 @@
|
|
|
1
1
|
---
|
|
2
|
-
active:
|
|
3
|
-
iteration:
|
|
2
|
+
active: true
|
|
3
|
+
iteration: 1
|
|
4
4
|
max_iterations: 20
|
|
5
5
|
completion_promise: "DONE"
|
|
6
|
-
started_at: "2026-01-
|
|
7
|
-
completed_at: "2026-01-29T01:48:00Z"
|
|
6
|
+
started_at: "2026-01-29T09:04:01Z"
|
|
8
7
|
---
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
### URL Behavior (Correct)
|
|
42
|
-
|
|
43
|
-
| Language | URL Pattern |
|
|
44
|
-
|----------|-------------|
|
|
45
|
-
| English (default) | `/posts/...`, `/slides`, `/about` |
|
|
46
|
-
| Chinese | `/zh-CN/posts/...`, `/zh-CN/slides`, `/zh-CN/about` |
|
|
47
|
-
|
|
48
|
-
This is the expected behavior with `prefixDefaultLocale: false`.
|
|
9
|
+
There is an error happened after running npm run build for default template in github action
|
|
10
|
+
8:27:27 AM [vite] (ssr) Error when evaluating SSR module /home/runner/work/jet-w.astro-blog/jet-w.astro-blog/templates/default/astro.config.mjs: Failed to resolve entry for package @jet-w/astro-blog. The package may have incorrect main/module/exports specified in its package.json.
|
|
11
|
+
Plugin: vite:import-analysis
|
|
12
|
+
File: /home/runner/work/jet-w.astro-blog/jet-w.astro-blog/templates/default/astro.config.mjs:11:46
|
|
13
|
+
9 |
|
|
14
|
+
10 | // Import plugins and integration from @jet-w/astro-blog
|
|
15
|
+
11 | import { astroBlog, defineI18nConfig } from '@jet-w/astro-blog';
|
|
16
|
+
| ^
|
|
17
|
+
12 | import { remarkProtectCode, rehypeRestoreCode } from '@jet-w/astro-blog/plugins/remark-protect-code.mjs';
|
|
18
|
+
13 | import { remarkContainers } from '@jet-w/astro-blog/plugins/remark-containers.mjs';
|
|
19
|
+
at packageEntryFailure (file:///home/runner/work/jet-w.astro-blog/jet-w.astro-blog/templates/default/node_modules/vite/dist/node/chunks/dep-D4NMHUTW.js:16198:15)
|
|
20
|
+
at resolvePackageEntry (file:///home/runner/work/jet-w.astro-blog/jet-w.astro-blog/templates/default/node_modules/vite/dist/node/chunks/dep-D4NMHUTW.js:16195:3)
|
|
21
|
+
at tryNodeResolve (file:///home/runner/work/jet-w.astro-blog/jet-w.astro-blog/templates/default/node_modules/vite/dist/node/chunks/dep-D4NMHUTW.js:16060:18)
|
|
22
|
+
at ResolveIdContext.resolveId (file:///home/runner/work/jet-w.astro-blog/jet-w.astro-blog/templates/default/node_modules/vite/dist/node/chunks/dep-D4NMHUTW.js:15831:19)
|
|
23
|
+
at EnvironmentPluginContainer.resolveId (file:///home/runner/work/jet-w.astro-blog/jet-w.astro-blog/templates/default/node_modules/vite/dist/node/chunks/dep-D4NMHUTW.js:42242:17)
|
|
24
|
+
at async TransformPluginContext.resolve (file:///home/runner/work/jet-w.astro-blog/jet-w.astro-blog/templates/default/node_modules/vite/dist/node/chunks/dep-D4NMHUTW.js:42449:15)
|
|
25
|
+
at async normalizeUrl (file:///home/runner/work/jet-w.astro-blog/jet-w.astro-blog/templates/default/node_modules/vite/dist/node/chunks/dep-D4NMHUTW.js:40492:26)
|
|
26
|
+
at async file:///home/runner/work/jet-w.astro-blog/jet-w.astro-blog/templates/default/node_modules/vite/dist/node/chunks/dep-D4NMHUTW.js:40623:37
|
|
27
|
+
at async Promise.all (index 8)
|
|
28
|
+
at async TransformPluginContext.transform (file:///home/runner/work/jet-w.astro-blog/jet-w.astro-blog/templates/default/node_modules/vite/dist/node/chunks/dep-D4NMHUTW.js:40550:7)
|
|
29
|
+
[astro] Unable to load your Astro config
|
|
30
|
+
Failed to resolve entry for package @jet-w/astro-blog. The package may have incorrect main/module/exports specified in its package.json.
|
|
31
|
+
Location:
|
|
32
|
+
/home/runner/work/jet-w.astro-blog/jet-w.astro-blog/templates/default/astro.config.mjs:11:46
|
|
33
|
+
Stack trace:
|
|
34
|
+
at packageEntryFailure (file:///home/runner/work/jet-w.astro-blog/jet-w.astro-blog/templates/default/node_modules/vite/dist/node/chunks/dep-D4NMHUTW.js:16198:15)
|
|
35
|
+
at tryNodeResolve (file:///home/runner/work/jet-w.astro-blog/jet-w.astro-blog/templates/default/node_modules/vite/dist/node/chunks/dep-D4NMHUTW.js:16060:18)
|
|
36
|
+
at EnvironmentPluginContainer.resolveId (file:///home/runner/work/jet-w.astro-blog/jet-w.astro-blog/templates/default/node_modules/vite/dist/node/chunks/dep-D4NMHUTW.js:42242:17)
|
|
37
|
+
at async normalizeUrl (file:///home/runner/work/jet-w.astro-blog/jet-w.astro-blog/templates/default/node_modules/vite/dist/node/chunks/dep-D4NMHUTW.js:40492:26)
|
|
38
|
+
at async Promise.all (index 8)
|
|
39
|
+
Error: Process completed with exit code 1.
|