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/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
- const DEPLOY_WORKFLOW = `name: Deploy to GitHub Pages
6
-
7
- on:
8
- push:
9
- branches: [main]
10
-
11
- permissions:
12
- contents: read
13
- pages: write
14
- id-token: write
15
-
16
- concurrency:
17
- group: "pages"
18
- cancel-in-progress: false
19
-
20
- jobs:
21
- build-and-deploy:
22
- runs-on: ubuntu-latest
23
- environment:
24
- name: github-pages
25
- url: \${{ steps.deployment.outputs.page_url }}
26
- steps:
27
- - uses: actions/checkout@v4
28
-
29
- - name: Setup Node.js
30
- uses: actions/setup-node@v4
31
- with:
32
- node-version: '20'
33
- cache: 'npm'
34
-
35
- - name: Install dependencies
36
- run: npm ci
37
-
38
- - name: Build site
39
- run: npx fossbook build
40
-
41
- - name: Setup Pages
42
- uses: actions/configure-pages@v4
43
-
44
- - name: Upload artifact
45
- uses: actions/upload-pages-artifact@v3
46
- with:
47
- path: './public'
48
-
49
- - name: Deploy to GitHub Pages
50
- id: deployment
51
- uses: actions/deploy-pages@v4
52
- `;
53
-
54
- function initProject(options = {}) {
55
- const cwd = process.cwd();
56
-
57
- // Create directories
58
- const dirs = [
59
- "content/posts",
60
- "static/images",
61
- ];
62
-
63
- dirs.forEach((dir) => {
64
- const dirPath = path.join(cwd, dir);
65
- if (!fs.existsSync(dirPath)) {
66
- fs.mkdirSync(dirPath, { recursive: true });
67
- console.log(`Created: ${dir}/`);
68
- }
69
- });
70
-
71
- // Create .github/workflows/deploy.yml
72
- const workflowDir = path.join(cwd, ".github", "workflows");
73
- const workflowPath = path.join(workflowDir, "deploy.yml");
74
- if (!fs.existsSync(workflowPath)) {
75
- fs.mkdirSync(workflowDir, { recursive: true });
76
- fs.writeFileSync(workflowPath, DEPLOY_WORKFLOW);
77
- console.log("Created: .github/workflows/deploy.yml");
78
- }
79
-
80
- // Create .gitignore
81
- const gitignorePath = path.join(cwd, ".gitignore");
82
- if (!fs.existsSync(gitignorePath)) {
83
- fs.writeFileSync(gitignorePath, "node_modules/\npublic/\n");
84
- console.log("Created: .gitignore");
85
- }
86
-
87
- // Create fossbook.config.js
88
- const configPath = path.join(cwd, "fossbook.config.js");
89
- if (!fs.existsSync(configPath)) {
90
- const configContent = `module.exports = {
91
- blogName: "My Blog",
92
- authorName: "",
93
- authorDescription: "",
94
- authorWebsite: "",
95
- blogDescription: "A blog powered by fossbook",
96
- blogsite: "http://localhost:3000",
97
-
98
- // Optional
99
- githubCNAME: "",
100
- googleAnalyticsID: "",
101
- authorTwitter: "",
102
- siteTwitter: "",
103
- githubRepository: "",
104
- image: "",
105
- theme: "archie",
106
-
107
- // Comment system (optional)
108
- // comments: { provider: "utterances", repo: "user/repo", issueTerm: "pathname", theme: "github-light" },
109
-
110
- // Deployment
111
- // deploy: { branch: "main", remote: "origin" },
112
-
113
- // Directory overrides (defaults shown)
114
- content: "./content",
115
- postsDir: "./content/posts",
116
- outputDir: "./public",
117
- staticDir: "./static",
118
- themesDir: "./themes",
119
- };
120
- `;
121
- fs.writeFileSync(configPath, configContent);
122
- console.log("Created: fossbook.config.js");
123
- }
124
-
125
- // Create content/about.md
126
- const aboutPath = path.join(cwd, "content", "about.md");
127
- if (!fs.existsSync(aboutPath)) {
128
- const aboutContent = `---
129
- title: About
130
- ---
131
-
132
- Welcome to my blog!
133
- `;
134
- fs.writeFileSync(aboutPath, aboutContent);
135
- console.log("Created: content/about.md");
136
- }
137
-
138
- // Create package.json if it doesn't exist
139
- const pkgPath = path.join(cwd, "package.json");
140
- const createdPkg = !fs.existsSync(pkgPath);
141
- if (createdPkg) {
142
- const pkg = {
143
- name: path.basename(cwd),
144
- version: "1.0.0",
145
- private: true,
146
- scripts: {
147
- build: "fossbook build",
148
- start: "fossbook serve",
149
- deploy: "fossbook deploy",
150
- },
151
- dependencies: {
152
- fossbook: "latest",
153
- },
154
- };
155
- fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
156
- console.log("Created: package.json");
157
- }
158
-
159
- // Generate package-lock.json (required by CI/CD npm ci + cache)
160
- const lockPath = path.join(cwd, "package-lock.json");
161
- if (!fs.existsSync(lockPath)) {
162
- console.log("Running npm install to generate package-lock.json...");
163
- try {
164
- execSync("npm install", { cwd, stdio: "inherit" });
165
- console.log("Created: package-lock.json");
166
- } catch {
167
- console.warn("Warning: npm install failed. You may need to run it manually.");
168
- }
169
- }
170
-
171
- // Handle --github flag: create repo and push
172
- if (options.github) {
173
- initGitHub(cwd);
174
- }
175
-
176
- console.log("\nSite initialized! Next steps:");
177
- if (!options.github) {
178
- console.log(' 1. Edit fossbook.config.js with your site settings');
179
- console.log(' 2. Run: fossbook new "My First Post"');
180
- console.log(" 3. Edit your post in content/posts/");
181
- console.log(" 4. Run: fossbook build");
182
- console.log(" 5. Run: fossbook serve");
183
- } else {
184
- console.log(' 1. Edit fossbook.config.js with your site settings');
185
- console.log(' 2. Run: fossbook new "My First Post"');
186
- console.log(" 3. Edit your post in content/posts/");
187
- console.log(" 4. Run: fossbook deploy");
188
- }
189
- }
190
-
191
- function initGitHub(cwd) {
192
- // Check if gh CLI is available
193
- try {
194
- execSync("gh --version", { stdio: "ignore" });
195
- } catch {
196
- console.error(
197
- "Error: GitHub CLI (gh) is not installed.\n" +
198
- "Install it: https://cli.github.com/\n" +
199
- " Linux: sudo apt install gh\n" +
200
- " macOS: brew install gh\n" +
201
- " Windows: winget install GitHub.cli"
202
- );
203
- process.exit(1);
204
- }
205
-
206
- // Check if gh is authenticated
207
- try {
208
- execSync("gh auth status", { stdio: "ignore" });
209
- } catch {
210
- console.error("Error: GitHub CLI is not authenticated. Run: gh auth login");
211
- process.exit(1);
212
- }
213
-
214
- // Initialize git repo if needed
215
- if (!fs.existsSync(path.join(cwd, ".git"))) {
216
- console.log("Initializing git repository...");
217
- execSync("git init", { cwd, stdio: "inherit" });
218
- execSync("git branch -M main", { cwd, stdio: "inherit" });
219
- }
220
-
221
- // Create GitHub repository
222
- const repoName = path.basename(cwd);
223
- console.log(`Creating GitHub repository: ${repoName}...`);
224
- try {
225
- execSync(`gh repo create ${repoName} --public --source=. --remote=origin`, {
226
- cwd,
227
- stdio: "inherit",
228
- });
229
- } catch {
230
- // Repo may already exist or remote may already be set
231
- console.warn("Note: GitHub repo creation skipped (may already exist).");
232
- }
233
-
234
- // Initial commit and push
235
- console.log("Committing and pushing to GitHub...");
236
- execSync("git add -A", { cwd, stdio: "inherit" });
237
- try {
238
- execSync('git commit -m "Initial fossbook site"', { cwd, stdio: "inherit" });
239
- } catch {
240
- // Nothing to commit
241
- }
242
- try {
243
- execSync("git push -u origin main", { cwd, stdio: "inherit" });
244
- } catch (e) {
245
- console.warn("Warning: Could not push to origin. You may need to push manually.");
246
- }
247
-
248
- console.log("\nGitHub repository created and pushed!");
249
- console.log("Enable GitHub Pages in your repo settings:");
250
- console.log(" Settings Pages Source → GitHub Actions");
251
- }
252
-
253
- module.exports = { initProject };
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 };