fossbook 0.1.3 → 0.2.0

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/README.md CHANGED
@@ -105,12 +105,7 @@ module.exports = {
105
105
  defaultLanguageInSubdir: false,
106
106
  languages: {
107
107
  en: { languageName: "English", locale: "en-US" },
108
- ko: {
109
- languageName: "한국어",
110
- locale: "ko-KR",
111
- blogName: "나의 블로그",
112
- blogDescription: "한국어 블로그",
113
- },
108
+ ko: { languageName: "한국어", locale: "ko-KR" },
114
109
  },
115
110
 
116
111
  // Optional comments (see "Comments" below)
@@ -132,6 +127,11 @@ module.exports = {
132
127
 
133
128
  ## Content Format
134
129
 
130
+ Fossbook supports GitHub Flavored Markdown plus extensions for image sizing
131
+ and alignment, captions, comic dialogue, aligned links, Mermaid diagrams, and
132
+ syntax-highlighted code. See [Fossbook Markdown](fossbook_markdown.md) for
133
+ syntax and examples.
134
+
135
135
  ### Post front-matter
136
136
 
137
137
  ```markdown
@@ -192,8 +192,6 @@ module.exports = {
192
192
  ko: {
193
193
  languageName: "한국어",
194
194
  locale: "ko-KR",
195
- blogName: "나의 블로그",
196
- blogDescription: "한국어 블로그",
197
195
  },
198
196
  ja: {
199
197
  languageName: "日本語",
@@ -203,14 +201,43 @@ module.exports = {
203
201
  };
204
202
  ```
205
203
 
204
+ Put localized blog information in a sibling config file for each language. For
205
+ example, `fossbook.config.en.js` contains:
206
+
207
+ ```js
208
+ module.exports = {
209
+ blogName: "My Blog",
210
+ authorName: "Jane Doe",
211
+ authorDescription: "Open source developer and writer",
212
+ blogDescription: "An English-language blog",
213
+ };
214
+ ```
215
+
216
+ And `fossbook.config.ko.js` contains:
217
+
218
+ ```js
219
+ module.exports = {
220
+ blogName: "나의 블로그",
221
+ authorName: "이수현",
222
+ authorDescription: "오픈 소스 개발자이자 작가",
223
+ blogDescription: "한국어 블로그",
224
+ homeLabel: "대문",
225
+ allPostsLabel: "모든 글",
226
+ aboutLabel: "소개",
227
+ tagsLabel: "태그",
228
+ allTagsLabel: "모든 태그",
229
+ };
230
+ ```
231
+
206
232
  - `defaultLanguage` identifies the language represented by `index.md` and
207
233
  `about.md`.
208
234
  - `defaultLanguageInSubdir: false` keeps the default language at the site root.
209
235
  Other languages are generated below their language code, such as `/ko/`.
210
236
  - Set `defaultLanguageInSubdir: true` to generate the default language below
211
237
  its code as well, such as `/en/`.
212
- - Each language can override site values such as `blogName` and
213
- `blogDescription`.
238
+ - Language config files use the main config filename with the language code
239
+ inserted before the extension: `fossbook.config.<language>.js`.
240
+ - Values omitted from a language config fall back to `fossbook.config.js`.
214
241
  - `locale` controls language-specific date formatting.
215
242
  - Language keys should use standard language tags such as `en`, `ko`, `ja`, or
216
243
  `zh-Hant`.
package/lib/all_posts.js CHANGED
@@ -9,15 +9,15 @@ module.exports = class AllPostsPage extends PageBase {
9
9
 
10
10
  // posts: array of Page objects
11
11
  generateContent(posts) {
12
- const pageTitle = "All posts";
12
+ const pageTitle = this.config.allPostsLabel || "All posts";
13
13
  const allPostsDir = path.join(this.config.dev.outdir, "all_posts");
14
14
  if (!fs.existsSync(allPostsDir))
15
15
  fs.mkdirSync(allPostsDir);
16
16
 
17
17
  const data = { posts: posts, pageTitle: pageTitle };
18
- this.url = `${this.config.blogsite}/posts.html`;
18
+ this.url = `${this.config.blogsite}/all_posts/`;
19
19
  this.title = `${this.config.blogName}: ${data.pageTitle}`;
20
- this.description = "All posts";
20
+ this.description = pageTitle;
21
21
  this.imageURL = this.config.image;
22
22
 
23
23
  const templatePath = path.join(this.config.themePath, "layouts", "all_posts.html");
package/lib/index.js CHANGED
@@ -75,6 +75,36 @@ function createAboutTranslations(languageConfigs) {
75
75
  });
76
76
  }
77
77
 
78
+ function createHomeTranslations(languageConfigs) {
79
+ return languageConfigs.map((config) => ({
80
+ language: config.language,
81
+ languageName: config.languageName,
82
+ path: config.basePath || "/",
83
+ url: `${config.blogsite}/`,
84
+ isDefault: config.language === config.defaultLanguage,
85
+ }));
86
+ }
87
+
88
+ function createAllPostsTranslations(languageConfigs) {
89
+ return languageConfigs.map((config) => ({
90
+ language: config.language,
91
+ languageName: config.languageName,
92
+ path: `${config.basePath || "/"}all_posts/`,
93
+ url: `${config.blogsite}/all_posts/`,
94
+ isDefault: config.language === config.defaultLanguage,
95
+ }));
96
+ }
97
+
98
+ function createTagListTranslations(languageConfigs) {
99
+ return languageConfigs.map((config) => ({
100
+ language: config.language,
101
+ languageName: config.languageName,
102
+ path: `${config.basePath || "/"}tags/`,
103
+ url: `${config.blogsite}/tags/`,
104
+ isDefault: config.language === config.defaultLanguage,
105
+ }));
106
+ }
107
+
78
108
  function buildLanguage(config) {
79
109
  fs.mkdirSync(config.dev.outdir, { recursive: true });
80
110
 
@@ -88,14 +118,17 @@ function buildLanguage(config) {
88
118
 
89
119
  // Create home page and pagination in output/page directory
90
120
  const homePagination = new HomePagination(config);
121
+ homePagination.translations = config.homeTranslations || [];
91
122
  homePagination.generateContent(postObjects);
92
123
 
93
124
  // Create all posts page in output/all_posts directory
94
125
  const allPostsPage = new AllPostsPage(config);
126
+ allPostsPage.translations = config.allPostsTranslations || [];
95
127
  allPostsPage.generateContent(postObjects);
96
128
 
97
129
  // Create tag pages in output/tags directory
98
130
  const tagPages = new TagPages(config);
131
+ tagPages.translations = config.tagListTranslations || [];
99
132
  tagPages.generateContent(postObjects);
100
133
 
101
134
  // Create about page in output/about directory
@@ -132,9 +165,15 @@ function build(config) {
132
165
  const languageConfigs = config.languageConfigs || [config];
133
166
  const postTranslationIndex = createPostTranslationIndex(languageConfigs);
134
167
  const aboutTranslations = createAboutTranslations(languageConfigs);
168
+ const homeTranslations = createHomeTranslations(languageConfigs);
169
+ const allPostsTranslations = createAllPostsTranslations(languageConfigs);
170
+ const tagListTranslations = createTagListTranslations(languageConfigs);
135
171
  languageConfigs.forEach((languageConfig) => {
136
172
  languageConfig.postTranslationIndex = postTranslationIndex;
137
173
  languageConfig.aboutTranslations = aboutTranslations;
174
+ languageConfig.homeTranslations = homeTranslations;
175
+ languageConfig.allPostsTranslations = allPostsTranslations;
176
+ languageConfig.tagListTranslations = tagListTranslations;
138
177
  });
139
178
  languageConfigs.forEach(buildLanguage);
140
179
 
@@ -150,4 +189,7 @@ module.exports = {
150
189
  buildLanguage,
151
190
  createPostTranslationIndex,
152
191
  createAboutTranslations,
192
+ createHomeTranslations,
193
+ createAllPostsTranslations,
194
+ createTagListTranslations,
153
195
  };
package/lib/init.js CHANGED
@@ -124,8 +124,11 @@ function initProject(options = {}) {
124
124
  defaultLanguageInSubdir: false,
125
125
  // languages: {
126
126
  // en: { languageName: "English", locale: "en-US" },
127
- // ko: { languageName: "한국어", locale: "ko-KR", blogName: "나의 블로그" },
127
+ // ko: { languageName: "한국어", locale: "ko-KR" },
128
128
  // },
129
+ // Add localized blog information to fossbook.config.en.js,
130
+ // fossbook.config.ko.js, and so on. Menu labels can also be localized with
131
+ // homeLabel, allPostsLabel, aboutLabel, tagsLabel, and allTagsLabel.
129
132
 
130
133
  // Comment system (optional)
131
134
  // comments: { provider: "utterances", repo: "user/repo", issueTerm: "pathname", theme: "github-light" },
package/lib/mod/config.js CHANGED
@@ -98,12 +98,12 @@ function loadConfig(configPath) {
98
98
  // Normalize postsPath into a bare segment without surrounding slashes
99
99
  merged.postsPath = (merged.postsPath || "").replace(/^\/+|\/+$/g, "");
100
100
 
101
- merged.languageConfigs = createLanguageConfigs(merged);
101
+ merged.languageConfigs = createLanguageConfigs(merged, resolvedConfigPath);
102
102
 
103
103
  return merged;
104
104
  }
105
105
 
106
- function createLanguageConfigs(config) {
106
+ function createLanguageConfigs(config, configPath) {
107
107
  const configuredLanguages = config.languages;
108
108
  const hasMultipleLanguages = configuredLanguages !== null && configuredLanguages !== undefined;
109
109
  const languages = hasMultipleLanguages
@@ -125,6 +125,10 @@ function createLanguageConfigs(config) {
125
125
  throw new Error(`languages.${language} must be an object`);
126
126
  }
127
127
 
128
+ const fileOverrides = configPath
129
+ ? loadLanguageConfig(configPath, language)
130
+ : {};
131
+
128
132
  const useSubdirectory = config.defaultLanguageInSubdir || language !== config.defaultLanguage;
129
133
  const languagePrefix = useSubdirectory ? language : "";
130
134
  const basePath = appendPathSegment(config.basePath, languagePrefix);
@@ -133,9 +137,12 @@ function createLanguageConfigs(config) {
133
137
  return {
134
138
  ...config,
135
139
  ...overrides,
140
+ ...fileOverrides,
141
+ defaultLanguage: config.defaultLanguage,
142
+ defaultLanguageInSubdir: config.defaultLanguageInSubdir,
136
143
  language,
137
- languageName: overrides.languageName || language,
138
- locale: overrides.locale || language,
144
+ languageName: fileOverrides.languageName || overrides.languageName || language,
145
+ locale: fileOverrides.locale || overrides.locale || language,
139
146
  languagePrefix,
140
147
  basePath,
141
148
  blogsite,
@@ -149,6 +156,22 @@ function createLanguageConfigs(config) {
149
156
  });
150
157
  }
151
158
 
159
+ function loadLanguageConfig(configPath, language) {
160
+ const extension = path.extname(configPath) || ".js";
161
+ const configStem = extension
162
+ ? configPath.slice(0, -extension.length)
163
+ : configPath;
164
+ const languageConfigPath = `${configStem}.${language}${extension}`;
165
+
166
+ if (!fs.existsSync(languageConfigPath)) return {};
167
+
168
+ const languageConfig = require(languageConfigPath);
169
+ if (!languageConfig || typeof languageConfig !== "object" || Array.isArray(languageConfig)) {
170
+ throw new Error(`${path.basename(languageConfigPath)} must export an object`);
171
+ }
172
+ return languageConfig;
173
+ }
174
+
152
175
  function appendPathSegment(basePath, segment) {
153
176
  const normalizedBase = `/${basePath || ""}`.replace(/\/{2,}/g, "/");
154
177
  const withTrailingSlash = normalizedBase.endsWith("/")
package/lib/tag/index.js CHANGED
@@ -46,10 +46,11 @@ module.exports = class TagList extends PageBase {
46
46
  if (!fs.existsSync(tagsDir))
47
47
  fs.mkdirSync(tagsDir);
48
48
 
49
+ const pageTitle = this.config.allTagsLabel || "All tags";
49
50
  this.imageURL = this.config.image;
50
- this.description = "Tag List";
51
- const data = { tags: tagArray, pageTitle: "All tags" };
52
- this.url = `${this.config.blogsite}/tag_list.html`;
51
+ this.description = pageTitle;
52
+ const data = { tags: tagArray, pageTitle };
53
+ this.url = `${this.config.blogsite}/tags/`;
53
54
  this.title = `${this.config.blogName}: ${data.pageTitle}`;
54
55
 
55
56
  const templatePath = path.join(this.config.themePath, "layouts", "tag_list.html");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fossbook",
3
- "version": "0.1.3",
3
+ "version": "0.2.0",
4
4
  "description": "A lightweight static blog site generator for GitHub Pages",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {
@@ -158,7 +158,7 @@
158
158
  }
159
159
 
160
160
  a {
161
- border-bottom: 3px solid var(--maincolor);
161
+ border-bottom: 2px solid var(--maincolor);
162
162
  color: inherit;
163
163
  text-decoration: none;
164
164
  }
@@ -243,11 +243,13 @@
243
243
  /* Code blocks */
244
244
  code {
245
245
  background-color: #f1f1f1;
246
+ font-family: 'Roboto Mono', monospace;
246
247
  padding: .1em .2em;
247
248
  }
248
249
 
249
250
  pre {
250
251
  background-color: #f6f8fa;
252
+ font-family: 'Roboto Mono', monospace;
251
253
  line-height: 1.4;
252
254
  overflow-x: auto;
253
255
  padding: 0;
@@ -278,6 +280,7 @@
278
280
  /* Header */
279
281
  header {
280
282
  display: flex;
283
+ font-family: 'Roboto Mono', monospace;
281
284
  flex-wrap: wrap;
282
285
  justify-content: space-between;
283
286
  margin: 1em 0;
@@ -288,16 +291,20 @@
288
291
  font-size: 1.5rem;
289
292
  }
290
293
  h1, h2, h3, h4, h5, h6 {
291
- font-size: 1.2rem;
294
+ line-height: 1.3;
295
+ margin-bottom: 0.75em;
292
296
  margin-top: 2em;
293
297
  }
294
-
295
- h1::before { color: var(--maincolor); content: '# '; }
296
- h2::before { color: var(--maincolor); content: '## '; }
297
- h3::before { color: var(--maincolor); content: '### '; }
298
- h4::before { color: var(--maincolor); content: '#### '; }
299
- h5::before { color: var(--maincolor); content: '##### '; }
300
- h6::before { color: var(--maincolor); content: '###### '; }
298
+
299
+ h1 { font-size: 1.4rem; }
300
+ h2 { font-size: 1.2rem; }
301
+ h3 { font-size: 1.1rem; }
302
+ h4 {
303
+ font-size: 1rem;
304
+ font-weight: 700;
305
+ }
306
+ h5 { font-size: 0.9rem; }
307
+ h6 { font-size: 0.8rem; }
301
308
 
302
309
  .meta {
303
310
  color: #999;
@@ -325,6 +332,13 @@
325
332
  h1.assistive-text {
326
333
  display: none;
327
334
  }
335
+ nav {
336
+ font-family: 'Roboto Mono', monospace;
337
+ }
338
+ html:lang(ko) header,
339
+ html:lang(ko) nav {
340
+ font-family: 'NanumSquareNeo', sans-serif;
341
+ }
328
342
  nav.post-navigation {
329
343
  display: flex;
330
344
  justify-content: end;
@@ -338,6 +352,7 @@
338
352
  display: flex;
339
353
  align-items: center;
340
354
  border-top: 0.4rem dotted var(--bordercl);
355
+ font-family: 'Roboto Mono', monospace;
341
356
  padding: 2rem 0rem;
342
357
  margin-top: 2rem;
343
358
  }
@@ -358,6 +373,7 @@
358
373
 
359
374
  /* Common */
360
375
  .title h1 {
376
+ font-size: 1.7rem;
361
377
  margin-bottom: 0;
362
378
  }
363
379
 
@@ -387,7 +403,7 @@
387
403
  }
388
404
 
389
405
  .callout a {
390
- border-bottom: 3px solid #fff;
406
+ border-bottom: 2px solid #fff;
391
407
  }
392
408
 
393
409
  .callout a:hover {
@@ -403,7 +419,7 @@
403
419
  content: "🏷 ";
404
420
  }
405
421
  .tags a{
406
- border-bottom: 3px solid var(--maincolor);
422
+ border-bottom: 2px solid var(--maincolor);
407
423
  }
408
424
  .tags a:hover{
409
425
  color:white;
@@ -10,6 +10,8 @@
10
10
  <!-- Google tag (gtag.js) -->
11
11
  ${page.config.googleAnalyticsID ? page.googleAnalytics(page.config.googleAnalyticsID) : ""}
12
12
  <title>${page.config.blogName}: ${data.pageTitle}</title>
13
+ <link rel="canonical" href="${page.url}" />
14
+ ${page.alternateLanguageLinks()}
13
15
  ${page.openGraph("website")}
14
16
  </head>
15
17
  <body>
@@ -17,13 +19,16 @@
17
19
  <header>
18
20
  <div class="main">${page.config.blogName}</div>
19
21
  <nav>
20
- <a href="${page.config.basePath}">Home</a>
22
+ <a href="${page.config.basePath}">${page.config.homeLabel || "Home"}</a>
21
23
  ${data.pageTitle}
22
- <a href="${page.config.basePath}about">About</a>
23
- <a href="${page.config.basePath}tags">Tags</a>
24
+ <a href="${page.config.basePath}about">${page.config.aboutLabel || "About"}</a>
25
+ <a href="${page.config.basePath}tags">${page.config.tagsLabel || "Tags"}</a>
24
26
  </nav>
25
27
  </header>
26
- <h1 class="page-title">${data.pageTitle}</h1>
28
+ <div class="title">
29
+ <h1 class="page-title">${data.pageTitle}</h1>
30
+ ${page.languageSwitcher()}
31
+ </div>
27
32
  <ul class="posts">
28
33
  ${data.posts
29
34
  .map(
@@ -10,6 +10,8 @@
10
10
  ${page.config.googleAnalyticsID ? page.googleAnalytics(page.config.googleAnalyticsID) : ""}
11
11
  <title>${page.config.blogName}</title>
12
12
  <meta name="description" content="${page.config.blogDescription}" />
13
+ <link rel="canonical" href="${page.url}" />
14
+ ${page.alternateLanguageLinks()}
13
15
  ${page.openGraph("website")}
14
16
  <!-- Twitter Card -->
15
17
  ${page.twitterCard("summary")}
@@ -17,12 +19,15 @@
17
19
  <body>
18
20
  <div class="content">
19
21
  <header>
20
- <div class="main">${page.config.blogName}</div>
22
+ <div>
23
+ <div class="main">${page.config.blogName}</div>
24
+ ${page.languageSwitcher()}
25
+ </div>
21
26
  <nav>
22
- Home
23
- <a href="${page.config.basePath}all_posts">All posts</a>
24
- <a href="${page.config.basePath}about">About</a>
25
- <a href="${page.config.basePath}tags">Tags</a>
27
+ ${page.config.homeLabel || "Home"}
28
+ <a href="${page.config.basePath}all_posts">${page.config.allPostsLabel || "All posts"}</a>
29
+ <a href="${page.config.basePath}about">${page.config.aboutLabel || "About"}</a>
30
+ <a href="${page.config.basePath}tags">${page.config.tagsLabel || "Tags"}</a>
26
31
  </nav>
27
32
  </header>
28
33
 
@@ -28,10 +28,10 @@
28
28
  ${page.config.blogName}
29
29
  </div>
30
30
  <nav class="site-navigation">
31
- <a href="${page.config.basePath}">Home</a>
32
- <a href="${page.config.basePath}all_posts">All posts</a>
33
- About
34
- <a href="${page.config.basePath}tags">Tags</a>
31
+ <a href="${page.config.basePath}">${page.config.homeLabel || "Home"}</a>
32
+ <a href="${page.config.basePath}all_posts">${page.config.allPostsLabel || "All posts"}</a>
33
+ ${page.config.aboutLabel || "About"}
34
+ <a href="${page.config.basePath}tags">${page.config.tagsLabel || "Tags"}</a>
35
35
  </nav>
36
36
  </header>
37
37
  <main>
@@ -42,10 +42,10 @@
42
42
  ${page.config.blogName}
43
43
  </div>
44
44
  <nav class="site-navigation">
45
- <a href="${page.config.basePath}">Home</a>
46
- <a href="${page.config.basePath}all_posts">All posts</a>
47
- <a href="${page.config.basePath}about">About</a>
48
- <a href="${page.config.basePath}tags">Tags</a>
45
+ <a href="${page.config.basePath}">${page.config.homeLabel || "Home"}</a>
46
+ <a href="${page.config.basePath}all_posts">${page.config.allPostsLabel || "All posts"}</a>
47
+ <a href="${page.config.basePath}about">${page.config.aboutLabel || "About"}</a>
48
+ <a href="${page.config.basePath}tags">${page.config.tagsLabel || "Tags"}</a>
49
49
  </nav>
50
50
  </header>
51
51
  <main>
@@ -17,10 +17,10 @@
17
17
  <header>
18
18
  <div class="main">${page.config.blogName}</div>
19
19
  <nav>
20
- <a href="${page.config.basePath}">Home</a>
21
- <a href="${page.config.basePath}all_posts">All posts</a>
22
- <a href="${page.config.basePath}about">About</a>
23
- <a href="${page.config.basePath}tags">Tags</a>
20
+ <a href="${page.config.basePath}">${page.config.homeLabel || "Home"}</a>
21
+ <a href="${page.config.basePath}all_posts">${page.config.allPostsLabel || "All posts"}</a>
22
+ <a href="${page.config.basePath}about">${page.config.aboutLabel || "About"}</a>
23
+ <a href="${page.config.basePath}tags">${page.config.tagsLabel || "Tags"}</a>
24
24
  </nav>
25
25
  </header>
26
26
  <h1 class="page-title">Entries tagged - "${data.tag}"</h1>
@@ -11,6 +11,8 @@
11
11
  <!-- Google tag (gtag.js) -->
12
12
  ${page.config.googleAnalyticsID ? page.googleAnalytics(page.config.googleAnalyticsID) : ""}
13
13
  <title>${page.config.blogName}: ${data.pageTitle}</title>
14
+ <link rel="canonical" href="${page.url}" />
15
+ ${page.alternateLanguageLinks()}
14
16
  ${page.openGraph("website")}
15
17
  </head>
16
18
  <body>
@@ -18,13 +20,16 @@
18
20
  <header>
19
21
  <div class="main">${page.config.blogName}</div>
20
22
  <nav>
21
- <a href="${page.config.basePath}">Home</a>
22
- <a href="${page.config.basePath}all_posts">All posts</a>
23
- <a href="${page.config.basePath}about">About</a>
24
- Tags
23
+ <a href="${page.config.basePath}">${page.config.homeLabel || "Home"}</a>
24
+ <a href="${page.config.basePath}all_posts">${page.config.allPostsLabel || "All posts"}</a>
25
+ <a href="${page.config.basePath}about">${page.config.aboutLabel || "About"}</a>
26
+ ${page.config.tagsLabel || "Tags"}
25
27
  </nav>
26
28
  </header>
27
- <h1 class="page-title">${data.pageTitle}</h1>
29
+ <div class="title">
30
+ <h1 class="page-title">${data.pageTitle}</h1>
31
+ ${page.languageSwitcher()}
32
+ </div>
28
33
  <div class="tag-cloud">
29
34
  <ul class="tags">
30
35
  ${data.tags