fossbook 0.0.4 → 0.0.6
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 +246 -246
- package/bin/fossbook.js +120 -119
- package/lib/all_posts.js +33 -32
- package/lib/assets/placeholder.svg +3 -3
- package/lib/deploy.js +257 -237
- package/lib/home.js +60 -59
- package/lib/index.js +79 -79
- package/lib/init.js +311 -253
- package/lib/mod/config.js +136 -136
- package/lib/mod/marked.js +95 -95
- package/lib/mod/page.js +119 -115
- package/lib/mod/page_base.js +97 -97
- package/lib/new.js +48 -48
- package/lib/posts.js +47 -42
- package/lib/server.js +20 -14
- package/lib/tag/index.js +70 -69
- package/lib/tag/tag.js +30 -29
- package/package.json +45 -45
- package/themes/archie/assets/fonts/fira-sans-v10-latin-regular.svg +330 -330
- package/themes/archie/assets/fonts/ibm-plex-mono-v6-latin-500italic.svg +365 -365
- package/themes/archie/assets/fonts/roboto-mono-v12-latin-regular.svg +405 -405
- package/themes/archie/assets/styles/fonts.css +51 -51
- package/themes/archie/assets/styles/highlights.css +75 -75
- package/themes/archie/assets/styles/main.css +402 -402
- package/themes/archie/layouts/all_posts.html +41 -41
- package/themes/archie/layouts/home.html +61 -61
- package/themes/archie/layouts/page.html +50 -50
- package/themes/archie/layouts/partials/footer.html +9 -9
- package/themes/archie/layouts/post.html +81 -81
- package/themes/archie/layouts/tag.html +42 -42
- package/themes/archie/layouts/tag_list.html +43 -43
package/lib/init.js
CHANGED
|
@@ -1,253 +1,311 @@
|
|
|
1
|
-
const fs = require("fs");
|
|
2
|
-
const path = require("path");
|
|
3
|
-
const { execSync } = require("child_process");
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
//
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
`;
|
|
134
|
-
fs.writeFileSync(
|
|
135
|
-
console.log("Created:
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// Create
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
//
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
try {
|
|
238
|
-
execSync(
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const { execSync } = require("child_process");
|
|
4
|
+
const readline = require("readline");
|
|
5
|
+
|
|
6
|
+
const DEPLOY_WORKFLOW = `name: Deploy to GitHub Pages
|
|
7
|
+
|
|
8
|
+
on:
|
|
9
|
+
push:
|
|
10
|
+
branches: [main]
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
pages: write
|
|
15
|
+
id-token: write
|
|
16
|
+
|
|
17
|
+
concurrency:
|
|
18
|
+
group: "pages"
|
|
19
|
+
cancel-in-progress: false
|
|
20
|
+
|
|
21
|
+
jobs:
|
|
22
|
+
build-and-deploy:
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
environment:
|
|
25
|
+
name: github-pages
|
|
26
|
+
url: \${{ steps.deployment.outputs.page_url }}
|
|
27
|
+
steps:
|
|
28
|
+
- uses: actions/checkout@v4
|
|
29
|
+
|
|
30
|
+
- name: Setup Node.js
|
|
31
|
+
uses: actions/setup-node@v4
|
|
32
|
+
with:
|
|
33
|
+
node-version: '20'
|
|
34
|
+
cache: 'npm'
|
|
35
|
+
|
|
36
|
+
- name: Install dependencies
|
|
37
|
+
run: npm ci
|
|
38
|
+
|
|
39
|
+
- name: Build site
|
|
40
|
+
run: npx fossbook build
|
|
41
|
+
|
|
42
|
+
- name: Setup Pages
|
|
43
|
+
uses: actions/configure-pages@v4
|
|
44
|
+
|
|
45
|
+
- name: Upload artifact
|
|
46
|
+
uses: actions/upload-pages-artifact@v3
|
|
47
|
+
with:
|
|
48
|
+
path: './public'
|
|
49
|
+
|
|
50
|
+
- name: Deploy to GitHub Pages
|
|
51
|
+
id: deployment
|
|
52
|
+
uses: actions/deploy-pages@v4
|
|
53
|
+
`;
|
|
54
|
+
|
|
55
|
+
function initProject(options = {}) {
|
|
56
|
+
let cwd = process.cwd();
|
|
57
|
+
|
|
58
|
+
// If a project name is given, create the directory and work inside it
|
|
59
|
+
if (options.name) {
|
|
60
|
+
const projectDir = path.join(cwd, options.name);
|
|
61
|
+
if (fs.existsSync(projectDir)) {
|
|
62
|
+
console.error(`Error: Directory "${options.name}" already exists.`);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
fs.mkdirSync(projectDir, { recursive: true });
|
|
66
|
+
cwd = projectDir;
|
|
67
|
+
console.log(`Creating new site in ${cwd}\n`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Create directories
|
|
71
|
+
const dirs = [
|
|
72
|
+
"content/posts",
|
|
73
|
+
"static/images",
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
dirs.forEach((dir) => {
|
|
77
|
+
const dirPath = path.join(cwd, dir);
|
|
78
|
+
if (!fs.existsSync(dirPath)) {
|
|
79
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
80
|
+
console.log(`Created: ${dir}/`);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Create .github/workflows/deploy.yml
|
|
85
|
+
const workflowDir = path.join(cwd, ".github", "workflows");
|
|
86
|
+
const workflowPath = path.join(workflowDir, "deploy.yml");
|
|
87
|
+
if (!fs.existsSync(workflowPath)) {
|
|
88
|
+
fs.mkdirSync(workflowDir, { recursive: true });
|
|
89
|
+
fs.writeFileSync(workflowPath, DEPLOY_WORKFLOW);
|
|
90
|
+
console.log("Created: .github/workflows/deploy.yml");
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Create .gitignore
|
|
94
|
+
const gitignorePath = path.join(cwd, ".gitignore");
|
|
95
|
+
if (!fs.existsSync(gitignorePath)) {
|
|
96
|
+
fs.writeFileSync(gitignorePath, "node_modules/\npublic/\n");
|
|
97
|
+
console.log("Created: .gitignore");
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Create fossbook.config.js
|
|
101
|
+
const configPath = path.join(cwd, "fossbook.config.js");
|
|
102
|
+
if (!fs.existsSync(configPath)) {
|
|
103
|
+
const configContent = `module.exports = {
|
|
104
|
+
blogName: "My Blog",
|
|
105
|
+
authorName: "",
|
|
106
|
+
authorDescription: "",
|
|
107
|
+
authorWebsite: "",
|
|
108
|
+
blogDescription: "A blog powered by fossbook",
|
|
109
|
+
blogsite: "http://localhost:3000",
|
|
110
|
+
|
|
111
|
+
// Optional
|
|
112
|
+
githubCNAME: "",
|
|
113
|
+
googleAnalyticsID: "",
|
|
114
|
+
authorTwitter: "",
|
|
115
|
+
siteTwitter: "",
|
|
116
|
+
githubRepository: "",
|
|
117
|
+
image: "",
|
|
118
|
+
theme: "archie",
|
|
119
|
+
|
|
120
|
+
// Comment system (optional)
|
|
121
|
+
// comments: { provider: "utterances", repo: "user/repo", issueTerm: "pathname", theme: "github-light" },
|
|
122
|
+
|
|
123
|
+
// Deployment
|
|
124
|
+
// deploy: { branch: "main", remote: "origin" },
|
|
125
|
+
|
|
126
|
+
// Directory overrides (defaults shown)
|
|
127
|
+
content: "./content",
|
|
128
|
+
postsDir: "./content/posts",
|
|
129
|
+
outputDir: "./public",
|
|
130
|
+
staticDir: "./static",
|
|
131
|
+
themesDir: "./themes",
|
|
132
|
+
};
|
|
133
|
+
`;
|
|
134
|
+
fs.writeFileSync(configPath, configContent);
|
|
135
|
+
console.log("Created: fossbook.config.js");
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Create content/about.md
|
|
139
|
+
const aboutPath = path.join(cwd, "content", "about.md");
|
|
140
|
+
if (!fs.existsSync(aboutPath)) {
|
|
141
|
+
const aboutContent = `---
|
|
142
|
+
title: About
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
Welcome to my blog!
|
|
146
|
+
`;
|
|
147
|
+
fs.writeFileSync(aboutPath, aboutContent);
|
|
148
|
+
console.log("Created: content/about.md");
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Create package.json if it doesn't exist
|
|
152
|
+
const pkgPath = path.join(cwd, "package.json");
|
|
153
|
+
const createdPkg = !fs.existsSync(pkgPath);
|
|
154
|
+
if (createdPkg) {
|
|
155
|
+
const pkg = {
|
|
156
|
+
name: path.basename(cwd),
|
|
157
|
+
version: "1.0.0",
|
|
158
|
+
private: true,
|
|
159
|
+
scripts: {
|
|
160
|
+
build: "fossbook build",
|
|
161
|
+
start: "fossbook serve",
|
|
162
|
+
deploy: "fossbook deploy",
|
|
163
|
+
},
|
|
164
|
+
dependencies: {
|
|
165
|
+
fossbook: "latest",
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
169
|
+
console.log("Created: package.json");
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Generate package-lock.json (required by CI/CD npm ci + cache)
|
|
173
|
+
const lockPath = path.join(cwd, "package-lock.json");
|
|
174
|
+
if (!fs.existsSync(lockPath)) {
|
|
175
|
+
console.log("Running npm install to generate package-lock.json...");
|
|
176
|
+
try {
|
|
177
|
+
execSync("npm install", { cwd, stdio: "inherit" });
|
|
178
|
+
console.log("Created: package-lock.json");
|
|
179
|
+
} catch {
|
|
180
|
+
console.warn("Warning: npm install failed. You may need to run it manually.");
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Handle --github flag: create repo and push
|
|
185
|
+
if (options.github) {
|
|
186
|
+
initGitHub(cwd);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
console.log("\nSite initialized! Next steps:");
|
|
190
|
+
if (!options.github) {
|
|
191
|
+
console.log(' 1. Edit fossbook.config.js with your site settings');
|
|
192
|
+
console.log(' 2. Run: fossbook new "My First Post"');
|
|
193
|
+
console.log(" 3. Edit your post in content/posts/");
|
|
194
|
+
console.log(" 4. Run: fossbook build");
|
|
195
|
+
console.log(" 5. Run: fossbook serve");
|
|
196
|
+
} else {
|
|
197
|
+
console.log(' 1. Edit fossbook.config.js with your site settings');
|
|
198
|
+
console.log(' 2. Run: fossbook new "My First Post"');
|
|
199
|
+
console.log(" 3. Edit your post in content/posts/");
|
|
200
|
+
console.log(" 4. Run: fossbook deploy");
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function initGitHub(cwd) {
|
|
205
|
+
// Check if gh CLI is available
|
|
206
|
+
try {
|
|
207
|
+
execSync("gh --version", { stdio: "ignore" });
|
|
208
|
+
} catch {
|
|
209
|
+
console.error(
|
|
210
|
+
"Error: GitHub CLI (gh) is not installed.\n" +
|
|
211
|
+
"Install it: https://cli.github.com/\n" +
|
|
212
|
+
" Linux: sudo apt install gh\n" +
|
|
213
|
+
" macOS: brew install gh\n" +
|
|
214
|
+
" Windows: winget install GitHub.cli"
|
|
215
|
+
);
|
|
216
|
+
process.exit(1);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Check if gh is authenticated
|
|
220
|
+
try {
|
|
221
|
+
execSync("gh auth status", { stdio: "ignore" });
|
|
222
|
+
} catch {
|
|
223
|
+
console.error("Error: GitHub CLI is not authenticated. Run: gh auth login");
|
|
224
|
+
process.exit(1);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Initialize git repo if needed
|
|
228
|
+
if (!fs.existsSync(path.join(cwd, ".git"))) {
|
|
229
|
+
console.log("Initializing git repository...");
|
|
230
|
+
execSync("git init", { cwd, stdio: "inherit" });
|
|
231
|
+
execSync("git branch -M main", { cwd, stdio: "inherit" });
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Create GitHub repository
|
|
235
|
+
const repoName = path.basename(cwd);
|
|
236
|
+
console.log(`Creating GitHub repository: ${repoName}...`);
|
|
237
|
+
try {
|
|
238
|
+
execSync(`gh repo create ${repoName} --public --source=. --remote=origin`, {
|
|
239
|
+
cwd,
|
|
240
|
+
stdio: "inherit",
|
|
241
|
+
});
|
|
242
|
+
} catch {
|
|
243
|
+
// Repo may already exist — ask user what to do
|
|
244
|
+
console.warn(`\nA GitHub repository named "${repoName}" already exists on your account.`);
|
|
245
|
+
const answer = promptSync("Push to the existing repo? (y/n): ");
|
|
246
|
+
if (answer.toLowerCase() !== "y") {
|
|
247
|
+
console.log("Cancelled. Your local site files are still available.");
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
// Ensure the remote is set
|
|
251
|
+
try {
|
|
252
|
+
// Check if origin remote already exists
|
|
253
|
+
execSync("git remote get-url origin", { cwd, stdio: "ignore" });
|
|
254
|
+
} catch {
|
|
255
|
+
// Add origin remote using the authenticated user's repo URL
|
|
256
|
+
try {
|
|
257
|
+
const ghUser = execSync("gh api user --jq .login", {
|
|
258
|
+
cwd,
|
|
259
|
+
encoding: "utf-8",
|
|
260
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
261
|
+
}).trim();
|
|
262
|
+
const remoteUrl = `git@github.com:${ghUser}/${repoName}.git`;
|
|
263
|
+
execSync(`git remote add origin ${remoteUrl}`, { cwd, stdio: "inherit" });
|
|
264
|
+
console.log(`Added remote: ${remoteUrl}`);
|
|
265
|
+
} catch {
|
|
266
|
+
console.warn("Warning: Could not add git remote. Add it manually with:");
|
|
267
|
+
console.warn(` git remote add origin git@github.com:<user>/${repoName}.git`);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Initial commit and push
|
|
273
|
+
console.log("Committing and pushing to GitHub...");
|
|
274
|
+
execSync("git add -A", { cwd, stdio: "inherit" });
|
|
275
|
+
try {
|
|
276
|
+
execSync('git commit -m "Initial fossbook site"', { cwd, stdio: "inherit" });
|
|
277
|
+
} catch {
|
|
278
|
+
// Nothing to commit
|
|
279
|
+
}
|
|
280
|
+
try {
|
|
281
|
+
execSync("git push -u origin main", { cwd, stdio: "inherit" });
|
|
282
|
+
} catch (e) {
|
|
283
|
+
console.warn("Warning: Could not push to origin. You may need to push manually.");
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
console.log("\nGitHub repository created and pushed!");
|
|
287
|
+
console.log("Enable GitHub Pages in your repo settings:");
|
|
288
|
+
console.log(" Settings → Pages → Source → GitHub Actions");
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/**
|
|
292
|
+
* Synchronous prompt that reads a single line from stdin.
|
|
293
|
+
*/
|
|
294
|
+
function promptSync(question) {
|
|
295
|
+
const buf = Buffer.alloc(256);
|
|
296
|
+
process.stdout.write(question);
|
|
297
|
+
const fd = fs.openSync("/dev/tty", "r");
|
|
298
|
+
let str = "";
|
|
299
|
+
// Read one byte at a time until newline
|
|
300
|
+
while (true) {
|
|
301
|
+
const bytesRead = fs.readSync(fd, buf, 0, 1);
|
|
302
|
+
if (bytesRead === 0) break;
|
|
303
|
+
const char = buf.toString("utf-8", 0, 1);
|
|
304
|
+
if (char === "\n") break;
|
|
305
|
+
str += char;
|
|
306
|
+
}
|
|
307
|
+
fs.closeSync(fd);
|
|
308
|
+
return str.trim();
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
module.exports = { initProject };
|