fossbook 0.0.2 → 0.0.4

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/lib/deploy.js CHANGED
@@ -54,7 +54,10 @@ async function deploy(config, options = {}) {
54
54
  return;
55
55
  }
56
56
 
57
- // 8. Push
57
+ // 8. Ensure GitHub Pages is enabled (source: GitHub Actions)
58
+ ensureGitHubPages(cwd);
59
+
60
+ // 9. Push
58
61
  console.log(`\nPushing to ${remote}/${branch}...`);
59
62
  try {
60
63
  execSync(`git push ${remote} ${branch}`, { cwd, stdio: "inherit" });
@@ -64,7 +67,7 @@ async function deploy(config, options = {}) {
64
67
  process.exit(1);
65
68
  }
66
69
 
67
- // 9. Wait for CI/CD and show URL
70
+ // 10. Wait for CI/CD and show URL
68
71
  if (options.noWait) {
69
72
  console.log("\nPushed! Skipping CI/CD status check (--no-wait).");
70
73
  printSiteUrl(config);
@@ -199,4 +202,36 @@ function sleep(ms) {
199
202
  return new Promise((resolve) => setTimeout(resolve, ms));
200
203
  }
201
204
 
205
+ /**
206
+ * Ensure GitHub Pages is enabled with "GitHub Actions" as the build source.
207
+ * Uses the GitHub CLI (gh) to check and create the Pages site if needed.
208
+ */
209
+ function ensureGitHubPages(cwd) {
210
+ if (!isGhAvailable()) return;
211
+
212
+ try {
213
+ // Try to get the current Pages config
214
+ execSync("gh api repos/{owner}/{repo}/pages", {
215
+ cwd,
216
+ stdio: "ignore",
217
+ });
218
+ // Pages already enabled — nothing to do
219
+ } catch {
220
+ // Pages not enabled — try to create it with GitHub Actions source
221
+ try {
222
+ console.log("Enabling GitHub Pages (source: GitHub Actions)...");
223
+ execSync(
224
+ 'gh api repos/{owner}/{repo}/pages -X POST -f build_type=workflow',
225
+ { cwd, stdio: "ignore" }
226
+ );
227
+ console.log("GitHub Pages enabled successfully.");
228
+ } catch (e) {
229
+ console.warn(
230
+ "Warning: Could not auto-enable GitHub Pages.\n" +
231
+ "Please enable it manually: Settings → Pages → Source → GitHub Actions"
232
+ );
233
+ }
234
+ }
235
+ }
236
+
202
237
  module.exports = { deploy };
package/lib/mod/config.js CHANGED
@@ -1,5 +1,6 @@
1
1
  const path = require("path");
2
2
  const fs = require("fs");
3
+ const { execSync } = require("child_process");
3
4
 
4
5
  const defaults = {
5
6
  blogName: "My Blog",
@@ -15,6 +16,7 @@ const defaults = {
15
16
  githubRepository: "",
16
17
  image: "",
17
18
  theme: "archie",
19
+ basePath: "", // auto-detected for GitHub Pages project sites, e.g. "/repo-name/"
18
20
  comments: null, // { provider: "utterances", repo: "user/repo", issueTerm: "pathname", theme: "github-light" }
19
21
  content: "./content",
20
22
  postsDir: "./content/posts",
@@ -80,6 +82,15 @@ function loadConfig(configPath) {
80
82
  merged.version = require("../../package.json").version;
81
83
  merged.date_time = formatDate(new Date());
82
84
 
85
+ // Auto-detect basePath if not explicitly set
86
+ if (!merged.basePath) {
87
+ merged.basePath = detectBasePath(merged);
88
+ }
89
+ // Ensure basePath has trailing slash
90
+ if (!merged.basePath.endsWith("/")) {
91
+ merged.basePath += "/";
92
+ }
93
+
83
94
  return merged;
84
95
  }
85
96
 
@@ -95,4 +106,31 @@ function formatDate(
95
106
  return date.toLocaleDateString(locale, options);
96
107
  }
97
108
 
109
+ /**
110
+ * Auto-detect basePath for GitHub Pages project sites.
111
+ * Returns "/repo-name/" for project sites, or "/" for user sites / custom domains.
112
+ */
113
+ function detectBasePath(config) {
114
+ // If user has a custom domain, no prefix needed
115
+ if (config.githubCNAME) return "/";
116
+
117
+ try {
118
+ const remoteUrl = execSync("git remote get-url origin", {
119
+ encoding: "utf-8",
120
+ stdio: ["pipe", "pipe", "ignore"],
121
+ }).trim();
122
+ const match = remoteUrl.match(/github\.com[:/](.+?)(?:\.git)?$/);
123
+ if (match) {
124
+ const [owner, repo] = match[1].split("/");
125
+ // User/org sites (e.g. username.github.io) are served at root
126
+ if (repo === `${owner}.github.io`) return "/";
127
+ // Project sites need the repo name as prefix
128
+ return `/${repo}/`;
129
+ }
130
+ } catch {
131
+ // Not a git repo or no remote — fall back to root
132
+ }
133
+ return "/";
134
+ }
135
+
98
136
  module.exports = { loadConfig, resolveThemePath, formatDate };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fossbook",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "A lightweight static blog site generator for GitHub Pages",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {
@@ -4,9 +4,9 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <meta name="description" content="${page.config.blogDescription}" />
7
- <link rel="stylesheet" href="/styles/fonts.css">
8
- <link rel="stylesheet" href="/styles/main.css">
9
- <link rel="icon" type="image/png" href="/images/favicon.png">
7
+ <link rel="stylesheet" href="${page.config.basePath}styles/fonts.css">
8
+ <link rel="stylesheet" href="${page.config.basePath}styles/main.css">
9
+ <link rel="icon" type="image/png" href="${page.config.basePath}images/favicon.png">
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>
@@ -17,10 +17,10 @@
17
17
  <header>
18
18
  <div class="main">${page.config.blogName}</div>
19
19
  <nav>
20
- <a href="/">Home</a>
20
+ <a href="${page.config.basePath}">Home</a>
21
21
  ${data.pageTitle}
22
- <a href="/about">About</a>
23
- <a href="/tags">Tags</a>
22
+ <a href="${page.config.basePath}about">About</a>
23
+ <a href="${page.config.basePath}tags">Tags</a>
24
24
  </nav>
25
25
  </header>
26
26
  <h1 class="page-title">${data.pageTitle}</h1>
@@ -28,7 +28,7 @@
28
28
  ${data.posts
29
29
  .map(
30
30
  (post) => `<li class="post">
31
- <a href="/${post.path}">${post.title}</a><span class="meta">
31
+ <a href="${page.config.basePath}${post.path}">${post.title}</a><span class="meta">
32
32
  ${new Date(post.date).toDateString()}</span>
33
33
  </li>`,
34
34
  )
@@ -3,9 +3,9 @@
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <link rel="stylesheet" href="/styles/fonts.css">
7
- <link rel="stylesheet" href="/styles/main.css">
8
- <link rel="icon" type="image/png" href="/images/favicon.png">
6
+ <link rel="stylesheet" href="${page.config.basePath}styles/fonts.css">
7
+ <link rel="stylesheet" href="${page.config.basePath}styles/main.css">
8
+ <link rel="icon" type="image/png" href="${page.config.basePath}images/favicon.png">
9
9
  <!-- Google tag (gtag.js) -->
10
10
  ${page.config.googleAnalyticsID ? page.googleAnalytics(page.config.googleAnalyticsID) : ""}
11
11
  <title>${page.config.blogName}</title>
@@ -20,9 +20,9 @@
20
20
  <div class="main">${page.config.blogName}</div>
21
21
  <nav>
22
22
  Home
23
- <a href="/all_posts">All posts</a>
24
- <a href="/about">About</a>
25
- <a href="/tags">Tags</a>
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>
26
26
  </nav>
27
27
  </header>
28
28
 
@@ -35,22 +35,22 @@
35
35
  (
36
36
  post,
37
37
  ) => `<section class="list-item" aria-label="Summary of post">
38
- <h1><a href="/${post.path}">${post.title}</a></h1>
38
+ <h1><a href="${page.config.basePath}${post.path}">${post.title}</a></h1>
39
39
  <time>${page.formatDate(new Date(post.date))}</time>
40
40
  ${post.image ? `<div class="responsive-image">
41
- <img src="/${post.path}/images/${post.image}" alt="${post.title}">
41
+ <img src="${page.config.basePath}${post.path}/images/${post.image}" alt="${post.title}">
42
42
  </div>` : ''}
43
43
  <div class="description">${post.description || ''}</div>
44
- <a class="readmore" href="/${post.path}">Read more ⟶</a>
44
+ <a class="readmore" href="${page.config.basePath}${post.path}">Read more ⟶</a>
45
45
  </section>`,
46
46
  )
47
47
  .join("")}
48
48
  <ul class="pagination" role="navigation">
49
49
  <span class="page-item page-prev">
50
- ${data.prev ? `<a href="/page/${data.prev}.html" class="page-link" aria-label="Previous">← Prev</a>` : `<span class="page-link disable-link"><span aria-hidden="true">← Prev</span></span>`}
50
+ ${data.prev ? `<a href="${page.config.basePath}page/${data.prev}.html" class="page-link" aria-label="Previous">← Prev</a>` : `<span class="page-link disable-link"><span aria-hidden="true">← Prev</span></span>`}
51
51
  </span>
52
52
  <span class="page-item page-next">
53
- ${data.next ? `<a href="page/${data.next}.html" class="page-link" aria-label="Next">Next →</a>` : `<span class="page-link disable-link"><span aria-hidden="true">Next →</span></span>`}
53
+ ${data.next ? `<a href="${page.config.basePath}page/${data.next}.html" class="page-link" aria-label="Next">Next →</a>` : `<span class="page-link disable-link"><span aria-hidden="true">Next →</span></span>`}
54
54
  </span>
55
55
  </ul>
56
56
  </main>
@@ -3,9 +3,9 @@
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
- <link rel="stylesheet" href="../styles/fonts.css">
7
- <link rel="stylesheet" href="../styles/main.css">
8
- <link rel="icon" type="image/png" href="../images/favicon.png">
6
+ <link rel="stylesheet" href="${page.config.basePath}styles/fonts.css">
7
+ <link rel="stylesheet" href="${page.config.basePath}styles/main.css">
8
+ <link rel="icon" type="image/png" href="${page.config.basePath}images/favicon.png">
9
9
  <!-- Google tag (gtag.js) -->
10
10
  ${page.config.googleAnalyticsID ? page.googleAnalytics(page.config.googleAnalyticsID) : ""}
11
11
  <title>${page.config.blogName}: ${page.title}</title>
@@ -25,10 +25,10 @@
25
25
  ${page.config.blogName}
26
26
  </div>
27
27
  <nav class="site-navigation">
28
- <a href="/">Home</a>
29
- <a href="/all_posts">All posts</a>
28
+ <a href="${page.config.basePath}">Home</a>
29
+ <a href="${page.config.basePath}all_posts">All posts</a>
30
30
  About
31
- <a href="/tags">Tags</a>
31
+ <a href="${page.config.basePath}tags">Tags</a>
32
32
  </nav>
33
33
  </header>
34
34
  <main>
@@ -6,9 +6,9 @@
6
6
  <link rel="preconnect" href="https://fonts.googleapis.com">
7
7
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
8
8
  <link href="https://fonts.googleapis.com/css2?family=Comic+Neue:ital,wght@0,300;0,400;0,700;1,300;1,400;1,700&family=Nanum+Pen+Script&family=Playpen+Sans:wght@100..800&display=swap" rel="stylesheet">
9
- <link rel="stylesheet" href="../styles/fonts.css">
10
- <link rel="stylesheet" href="../styles/main.css">
11
- <link rel="icon" type="image/png" href="../images/favicon.png">
9
+ <link rel="stylesheet" href="${page.config.basePath}styles/fonts.css">
10
+ <link rel="stylesheet" href="${page.config.basePath}styles/main.css">
11
+ <link rel="icon" type="image/png" href="${page.config.basePath}images/favicon.png">
12
12
  <!-- Google tag (gtag.js) -->
13
13
  ${page.config.googleAnalyticsID ? page.googleAnalytics(page.config.googleAnalyticsID) : ""}
14
14
  <title>${page.title}</title>
@@ -32,10 +32,10 @@
32
32
  ${page.config.blogName}
33
33
  </div>
34
34
  <nav class="site-navigation">
35
- <a href="/">Home</a>
36
- <a href="/all_posts">All posts</a>
37
- <a href="/about">About</a>
38
- <a href="/tags">Tags</a>
35
+ <a href="${page.config.basePath}">Home</a>
36
+ <a href="${page.config.basePath}all_posts">All posts</a>
37
+ <a href="${page.config.basePath}about">About</a>
38
+ <a href="${page.config.basePath}tags">Tags</a>
39
39
  </nav>
40
40
  </header>
41
41
  <main>
@@ -50,17 +50,17 @@
50
50
  <div class="post-tags">
51
51
  <nav class="nav tags">
52
52
  <ul class="tags">
53
- ${page.tags.map((tag) => `<li><a href="/tags/${tag.replace(/\s+/g, "_")}">${tag}</a></li>`).join("")}
53
+ ${page.tags.map((tag) => `<li><a href="${page.config.basePath}tags/${tag.replace(/\s+/g, "_")}">${tag}</a></li>`).join("")}
54
54
  </ul>
55
55
  </nav>
56
56
  </div>
57
57
  </article>
58
58
  <ul class="pagination-post" role="navigation">
59
59
  <span class="page-item page-prev">
60
- ${page.previous ? `<a href="../${page.previous.path}" class="page-link" aria-label="Previous">← ${page.previous.title}</a>` : ""}
60
+ ${page.previous ? `<a href="${page.config.basePath}${page.previous.path}" class="page-link" aria-label="Previous">← ${page.previous.title}</a>` : ""}
61
61
  </span>
62
62
  <span class="page-item page-next">
63
- ${page.next ? `<a href="../${page.next.path}" class="page-link" aria-label="Next">${page.next.title} →</a>` : ""}
63
+ ${page.next ? `<a href="${page.config.basePath}${page.next.path}" class="page-link" aria-label="Next">${page.next.title} →</a>` : ""}
64
64
  </span>
65
65
  </ul>
66
66
  ${page.config.comments && page.config.comments.provider === 'utterances' ? `<div class="comments border">
@@ -4,9 +4,9 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <meta name="description" content="${page.config.blogDescription}" />
7
- <link rel="stylesheet" href="../../styles/fonts.css">
8
- <link rel="stylesheet" href="../../styles/main.css">
9
- <link rel="icon" type="image/png" href="../images/favicon.png">
7
+ <link rel="stylesheet" href="${page.config.basePath}styles/fonts.css">
8
+ <link rel="stylesheet" href="${page.config.basePath}styles/main.css">
9
+ <link rel="icon" type="image/png" href="${page.config.basePath}images/favicon.png">
10
10
  <!-- Google tag (gtag.js) -->
11
11
  ${page.config.googleAnalyticsID ? page.googleAnalytics(page.config.googleAnalyticsID) : ""}
12
12
  <title>${page.config.blogName}: Entries tagged - ${data.tag}</title>
@@ -17,10 +17,10 @@
17
17
  <header>
18
18
  <div class="main">${page.config.blogName}</div>
19
19
  <nav>
20
- <a href="/">Home</a>
21
- <a href="/all_posts">All posts</a>
22
- <a href="/about">About</a>
23
- <a href="/tags">Tags</a>
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>
24
24
  </nav>
25
25
  </header>
26
26
  <h1 class="page-title">Entries tagged - "${data.tag}"</h1>
@@ -29,7 +29,7 @@
29
29
  ${data.posts
30
30
  .map(
31
31
  (post) => `<li class="post">
32
- <a href="/${post.path}">${post.title}</a><span class="meta">
32
+ <a href="${page.config.basePath}${post.path}">${post.title}</a><span class="meta">
33
33
  ${new Date(post.date).toDateString()}</span>
34
34
  </li>`,
35
35
  )
@@ -4,9 +4,9 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <meta name="description" content="${page.config.blogDescription}" />
7
- <link rel="stylesheet" href="../styles/fonts.css">
8
- <link rel="stylesheet" href="../styles/main.css">
9
- <link rel="icon" type="image/png" href="../images/favicon.png">
7
+ <link rel="stylesheet" href="${page.config.basePath}styles/fonts.css">
8
+ <link rel="stylesheet" href="${page.config.basePath}styles/main.css">
9
+ <link rel="icon" type="image/png" href="${page.config.basePath}images/favicon.png">
10
10
 
11
11
  <!-- Google tag (gtag.js) -->
12
12
  ${page.config.googleAnalyticsID ? page.googleAnalytics(page.config.googleAnalyticsID) : ""}
@@ -18,9 +18,9 @@
18
18
  <header>
19
19
  <div class="main">${page.config.blogName}</div>
20
20
  <nav>
21
- <a href="/">Home</a>
22
- <a href="/all_posts">All posts</a>
23
- <a href="/about">About</a>
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
24
  Tags
25
25
  </nav>
26
26
  </header>