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/mod/config.js CHANGED
@@ -1,136 +1,136 @@
1
- const path = require("path");
2
- const fs = require("fs");
3
- const { execSync } = require("child_process");
4
-
5
- const defaults = {
6
- blogName: "My Blog",
7
- authorName: "",
8
- authorDescription: "",
9
- authorWebsite: "",
10
- blogDescription: "",
11
- blogsite: "http://localhost:3000",
12
- githubCNAME: "",
13
- googleAnalyticsID: "",
14
- authorTwitter: "",
15
- siteTwitter: "",
16
- githubRepository: "",
17
- image: "",
18
- theme: "archie",
19
- basePath: "", // auto-detected for GitHub Pages project sites, e.g. "/repo-name/"
20
- comments: null, // { provider: "utterances", repo: "user/repo", issueTerm: "pathname", theme: "github-light" }
21
- content: "./content",
22
- postsDir: "./content/posts",
23
- outputDir: "./public",
24
- staticDir: "./static",
25
- themesDir: "./themes",
26
- };
27
-
28
- /**
29
- * Resolve the theme path, checking the user's themesDir first,
30
- * then falling back to the built-in themes directory.
31
- */
32
- function resolveThemePath(config) {
33
- const themeName = config.theme || "archie";
34
- const userThemePath = path.resolve(config.themesDir || "./themes", themeName);
35
- const builtinThemePath = path.resolve(__dirname, "../../themes", themeName);
36
-
37
- if (fs.existsSync(userThemePath)) {
38
- return userThemePath;
39
- }
40
- if (fs.existsSync(builtinThemePath)) {
41
- return builtinThemePath;
42
- }
43
- console.error(`Theme "${themeName}" not found in ${userThemePath} or ${builtinThemePath}`);
44
- process.exit(1);
45
- }
46
-
47
- /**
48
- * Load configuration from the user's fossbook.config.js (or a custom path),
49
- * merge with defaults, and build the internal dev paths.
50
- */
51
- function loadConfig(configPath) {
52
- const resolvedConfigPath = path.resolve(configPath || "./fossbook.config.js");
53
-
54
- let userConfig = {};
55
- if (fs.existsSync(resolvedConfigPath)) {
56
- userConfig = require(resolvedConfigPath);
57
- } else if (configPath) {
58
- // Only error if user explicitly specified a config path
59
- console.error(`Config file not found: ${resolvedConfigPath}`);
60
- process.exit(1);
61
- } else {
62
- console.warn("No fossbook.config.js found, using defaults.");
63
- }
64
-
65
- const merged = { ...defaults, ...userConfig };
66
-
67
- // Build the internal dev paths object used by the generator modules
68
- const themePath = resolveThemePath(merged);
69
-
70
- merged.dev = {
71
- postsdir: path.resolve(merged.postsDir),
72
- content: path.resolve(merged.content),
73
- about: path.resolve(merged.content, "about.md"),
74
- outdir: path.resolve(merged.outputDir),
75
- themePath: path.dirname(themePath), // e.g. /path/to/themes
76
- staticDir: path.resolve(merged.staticDir),
77
- };
78
-
79
- merged.themePath = themePath; // full path to the active theme directory
80
-
81
- // Inject version and build date
82
- merged.version = require("../../package.json").version;
83
- merged.date_time = formatDate(new Date());
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
-
94
- return merged;
95
- }
96
-
97
- function formatDate(
98
- date,
99
- locale = "en-US",
100
- options = {
101
- year: "numeric",
102
- month: "short",
103
- day: "numeric",
104
- },
105
- ) {
106
- return date.toLocaleDateString(locale, options);
107
- }
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
-
136
- module.exports = { loadConfig, resolveThemePath, formatDate };
1
+ const path = require("path");
2
+ const fs = require("fs");
3
+ const { execSync } = require("child_process");
4
+
5
+ const defaults = {
6
+ blogName: "My Blog",
7
+ authorName: "",
8
+ authorDescription: "",
9
+ authorWebsite: "",
10
+ blogDescription: "",
11
+ blogsite: "http://localhost:3000",
12
+ githubCNAME: "",
13
+ googleAnalyticsID: "",
14
+ authorTwitter: "",
15
+ siteTwitter: "",
16
+ githubRepository: "",
17
+ image: "",
18
+ theme: "archie",
19
+ basePath: "", // auto-detected for GitHub Pages project sites, e.g. "/repo-name/"
20
+ comments: null, // { provider: "utterances", repo: "user/repo", issueTerm: "pathname", theme: "github-light" }
21
+ content: "./content",
22
+ postsDir: "./content/posts",
23
+ outputDir: "./public",
24
+ staticDir: "./static",
25
+ themesDir: "./themes",
26
+ };
27
+
28
+ /**
29
+ * Resolve the theme path, checking the user's themesDir first,
30
+ * then falling back to the built-in themes directory.
31
+ */
32
+ function resolveThemePath(config) {
33
+ const themeName = config.theme || "archie";
34
+ const userThemePath = path.resolve(config.themesDir || "./themes", themeName);
35
+ const builtinThemePath = path.resolve(__dirname, "../../themes", themeName);
36
+
37
+ if (fs.existsSync(userThemePath)) {
38
+ return userThemePath;
39
+ }
40
+ if (fs.existsSync(builtinThemePath)) {
41
+ return builtinThemePath;
42
+ }
43
+ console.error(`Theme "${themeName}" not found in ${userThemePath} or ${builtinThemePath}`);
44
+ process.exit(1);
45
+ }
46
+
47
+ /**
48
+ * Load configuration from the user's fossbook.config.js (or a custom path),
49
+ * merge with defaults, and build the internal dev paths.
50
+ */
51
+ function loadConfig(configPath) {
52
+ const resolvedConfigPath = path.resolve(configPath || "./fossbook.config.js");
53
+
54
+ let userConfig = {};
55
+ if (fs.existsSync(resolvedConfigPath)) {
56
+ userConfig = require(resolvedConfigPath);
57
+ } else if (configPath) {
58
+ // Only error if user explicitly specified a config path
59
+ console.error(`Config file not found: ${resolvedConfigPath}`);
60
+ process.exit(1);
61
+ } else {
62
+ console.warn("No fossbook.config.js found, using defaults.");
63
+ }
64
+
65
+ const merged = { ...defaults, ...userConfig };
66
+
67
+ // Build the internal dev paths object used by the generator modules
68
+ const themePath = resolveThemePath(merged);
69
+
70
+ merged.dev = {
71
+ postsdir: path.resolve(merged.postsDir),
72
+ content: path.resolve(merged.content),
73
+ about: path.resolve(merged.content, "about.md"),
74
+ outdir: path.resolve(merged.outputDir),
75
+ themePath: path.dirname(themePath), // e.g. /path/to/themes
76
+ staticDir: path.resolve(merged.staticDir),
77
+ };
78
+
79
+ merged.themePath = themePath; // full path to the active theme directory
80
+
81
+ // Inject version and build date
82
+ merged.version = require("../../package.json").version;
83
+ merged.date_time = formatDate(new Date());
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
+
94
+ return merged;
95
+ }
96
+
97
+ function formatDate(
98
+ date,
99
+ locale = "en-US",
100
+ options = {
101
+ year: "numeric",
102
+ month: "short",
103
+ day: "numeric",
104
+ },
105
+ ) {
106
+ return date.toLocaleDateString(locale, options);
107
+ }
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
+
136
+ module.exports = { loadConfig, resolveThemePath, formatDate };
package/lib/mod/marked.js CHANGED
@@ -1,95 +1,95 @@
1
- const marked = require("marked");
2
-
3
- marked.setOptions({
4
- renderer: new marked.Renderer(),
5
- highlight: function (code, language) {
6
- const hljs = require("highlight.js");
7
- const validLanguage = hljs.getLanguage(language) ? language : "plaintext";
8
- return hljs.highlight(validLanguage, code).value;
9
- },
10
- pedantic: false,
11
- gfm: true,
12
- breaks: false,
13
- sanitize: false,
14
- smartLists: true,
15
- smartypants: false,
16
- xhtml: false,
17
- });
18
-
19
- // Override function
20
- const renderer = {
21
- image(href, title, text) {
22
- let size = null;
23
- // Check if the title contains a size specification
24
- if (title && title.includes("size:")) {
25
- const sizeMatch = title.match(/size:(\d+%)/);
26
- if (sizeMatch && sizeMatch[1]) {
27
- size = sizeMatch[1];
28
- // Remove the size specification from the title
29
- title = title.replace(/size:\d+%/g, "").trim();
30
- }
31
- }
32
- let align = "center";
33
- if (title && title.includes("align:")) {
34
- // align: left, right, center
35
- const alignMatch = title.match(/align:(left|right|center)/);
36
- if (alignMatch && alignMatch[1]) {
37
- align = alignMatch[1];
38
- // Remove the alignment specification from the title
39
- title = title.replace(/align:(left|right|center)/g, "").trim();
40
- }
41
- }
42
-
43
- // Construct the image tag with optional size and title
44
- let imageTag = `<img src="${href}" alt="${text}"`;
45
- if (size) {
46
- imageTag += ` style="width: ${size};"`;
47
- }
48
- imageTag += ">";
49
-
50
- return `
51
- <div style="text-align: ${align};">
52
- <figure style="text-align: ${align};">
53
- ${imageTag}
54
- ${title ? `<figcaption>${title}</figcaption>` : ""}
55
- </figure>
56
- </div>
57
- `;
58
- },
59
- link(href, title, text) {
60
- let align = null;
61
- if (title && title.includes("align:")) {
62
- // align: left, right, center
63
- const alignMatch = title.match(/align:(left|right|center)/);
64
- if (alignMatch && alignMatch[1]) {
65
- align = alignMatch[1];
66
- // Remove the alignment specification from the title
67
- title = title.replace(/align:(left|right|center)/g, "").trim();
68
- }
69
- }
70
- // if align is not null, add div with text-align style
71
- if (align) {
72
- return `
73
- <div style="text-align: ${align};">
74
- <a href="${href} "${title ? `title="${title}"` : ""}>${text}</a>
75
- </div>`;
76
- } else {
77
- return `<a href="${href} "${title ? `title="${title}"` : ""}>${text}</a>`;
78
- }
79
- },
80
- blockquote(quote) {
81
- return `<div class="blockquote-container"><blockquote>${quote}</blockquote></div>`;
82
- },
83
- paragraph(text) {
84
- // remove <p> surrounding the image
85
- if (text.includes('<figure style="text-align: center;">')) {
86
- return text;
87
- } else {
88
- return `<p>${text}</p>`;
89
- }
90
- },
91
- };
92
-
93
- marked.use({ renderer });
94
-
95
- module.exports = marked;
1
+ const marked = require("marked");
2
+
3
+ marked.setOptions({
4
+ renderer: new marked.Renderer(),
5
+ highlight: function (code, language) {
6
+ const hljs = require("highlight.js");
7
+ const validLanguage = hljs.getLanguage(language) ? language : "plaintext";
8
+ return hljs.highlight(validLanguage, code).value;
9
+ },
10
+ pedantic: false,
11
+ gfm: true,
12
+ breaks: false,
13
+ sanitize: false,
14
+ smartLists: true,
15
+ smartypants: false,
16
+ xhtml: false,
17
+ });
18
+
19
+ // Override function
20
+ const renderer = {
21
+ image(href, title, text) {
22
+ let size = null;
23
+ // Check if the title contains a size specification
24
+ if (title && title.includes("size:")) {
25
+ const sizeMatch = title.match(/size:(\d+%)/);
26
+ if (sizeMatch && sizeMatch[1]) {
27
+ size = sizeMatch[1];
28
+ // Remove the size specification from the title
29
+ title = title.replace(/size:\d+%/g, "").trim();
30
+ }
31
+ }
32
+ let align = "center";
33
+ if (title && title.includes("align:")) {
34
+ // align: left, right, center
35
+ const alignMatch = title.match(/align:(left|right|center)/);
36
+ if (alignMatch && alignMatch[1]) {
37
+ align = alignMatch[1];
38
+ // Remove the alignment specification from the title
39
+ title = title.replace(/align:(left|right|center)/g, "").trim();
40
+ }
41
+ }
42
+
43
+ // Construct the image tag with optional size and title
44
+ let imageTag = `<img src="${href}" alt="${text}"`;
45
+ if (size) {
46
+ imageTag += ` style="width: ${size};"`;
47
+ }
48
+ imageTag += ">";
49
+
50
+ return `
51
+ <div style="text-align: ${align};">
52
+ <figure style="text-align: ${align};">
53
+ ${imageTag}
54
+ ${title ? `<figcaption>${title}</figcaption>` : ""}
55
+ </figure>
56
+ </div>
57
+ `;
58
+ },
59
+ link(href, title, text) {
60
+ let align = null;
61
+ if (title && title.includes("align:")) {
62
+ // align: left, right, center
63
+ const alignMatch = title.match(/align:(left|right|center)/);
64
+ if (alignMatch && alignMatch[1]) {
65
+ align = alignMatch[1];
66
+ // Remove the alignment specification from the title
67
+ title = title.replace(/align:(left|right|center)/g, "").trim();
68
+ }
69
+ }
70
+ // if align is not null, add div with text-align style
71
+ if (align) {
72
+ return `
73
+ <div style="text-align: ${align};">
74
+ <a href="${href} "${title ? `title="${title}"` : ""}>${text}</a>
75
+ </div>`;
76
+ } else {
77
+ return `<a href="${href} "${title ? `title="${title}"` : ""}>${text}</a>`;
78
+ }
79
+ },
80
+ blockquote(quote) {
81
+ return `<div class="blockquote-container"><blockquote>${quote}</blockquote></div>`;
82
+ },
83
+ paragraph(text) {
84
+ // remove <p> surrounding the image
85
+ if (text.includes('<figure style="text-align: center;">')) {
86
+ return text;
87
+ } else {
88
+ return `<p>${text}</p>`;
89
+ }
90
+ },
91
+ };
92
+
93
+ marked.use({ renderer });
94
+
95
+ module.exports = marked;