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/page.js CHANGED
@@ -1,115 +1,119 @@
1
- const fm = require("front-matter");
2
- const fs = require("fs");
3
- const path = require("path");
4
- const marked = require("./marked");
5
- const PageBase = require("./page_base");
6
-
7
- module.exports = class Page extends PageBase {
8
- constructor(config) {
9
- super(config);
10
- this.srcFilePath = "";
11
- this.markdownFilePath = ""; // markdown file path
12
- this.contentPath = config.dev.content;
13
- }
14
-
15
- readSource(filePath) {
16
- if (filePath.indexOf(".md") === -1)
17
- this.srcFilePath = filePath + "/index.md";
18
- else this.srcFilePath = filePath;
19
-
20
- const mdContent = fs.readFileSync(this.srcFilePath, "utf8");
21
-
22
- if (path.extname(filePath) === ".md") {
23
- // If the file has a .md extension, extract the file name without the extension
24
- this.path = path.basename(filePath, ".md");
25
- } else {
26
- // If there's no .md extension, just get the last part of the path
27
- this.path = path.basename(filePath);
28
- }
29
- // parsed content by fields and body
30
- const content = fm(mdContent);
31
-
32
- this.title = `${content.attributes.title}`;
33
- this.date = content.attributes.date;
34
- this.url = `${this.config.blogsite}/${this.path}/`;
35
- this.image = content.attributes.image;
36
- this.description = content.attributes.description;
37
- // default image if no imageURL is specified
38
- this.imageURL = this.config.image;
39
-
40
- if (content.attributes.tags && content.attributes.tags.length > 0) {
41
- const tagArray = content.attributes.tags.split(",");
42
- this.tags = tagArray
43
- .map((tag) => tag.trim())
44
- .sort((a, b) => a.localeCompare(b));
45
- }
46
-
47
- // generated HTML from markdown
48
- this.body = marked.parse(content.body);
49
- // remove <p></p> and <p> </p> from the beginning and end of the content.body
50
- this.body = this.body.replace(/<p><\/p>/g, "").replace(/<p> <\/p>/g, "");
51
-
52
- // for generating the navigation link in the post.html template
53
- this.next = null;
54
- this.previous = null;
55
- }
56
-
57
- // about/index.html or about/hello.html
58
- generateContent(templateFile, outputPath) {
59
- // For a series of posts
60
- if (outputPath === undefined) outputPath = `${this.path}/index.html`;
61
- // if file name is not included in the path
62
- if (outputPath.indexOf(".htm") === -1) {
63
- if (outputPath[outputPath.length - 1] !== "/") outputPath += "/";
64
- outputPath += "index.html";
65
- }
66
-
67
- // if a directory path is included in the path
68
- if (outputPath.indexOf("/") !== -1) {
69
- if (this.path === "")
70
- this.path = outputPath.split("/").slice(0, -1).join("/");
71
-
72
- if (fs.existsSync(`${this.config.dev.outdir}/${this.path}`))
73
- fs.rmSync(`${this.config.dev.outdir}/${this.path}`, {
74
- recursive: true,
75
- });
76
-
77
- fs.mkdirSync(`${this.config.dev.outdir}/${this.path}`);
78
- } else {
79
- // remove the outputPath file if it exists
80
- if (fs.existsSync(`${this.config.dev.outdir}/${this.path}`))
81
- fs.unlinkSync(`${this.config.dev.outdir}/${this.path}`);
82
- }
83
-
84
- const layoutsPath = path.join(this.config.themePath, "layouts", templateFile);
85
- const postHTML = this.generateHTML(layoutsPath);
86
-
87
- fs.writeFileSync(
88
- `${this.config.dev.outdir}/${outputPath}`,
89
- postHTML,
90
- (e) => {
91
- if (e) throw e;
92
- console.log(`${outputPath}/index.html was created successfully`);
93
- },
94
- );
95
-
96
- // if there is the images folder in the output directory.
97
- if (
98
- fs.existsSync(`${this.config.dev.postsdir}/${this.path}/images`) &&
99
- this.path !== ""
100
- ) {
101
- // Copy images folder from postsdir to outdir
102
- if (!fs.existsSync(`${this.config.dev.outdir}/${this.path}/images`))
103
- fs.mkdirSync(`${this.config.dev.outdir}/${this.path}/images`);
104
-
105
- fs.readdirSync(`${this.config.dev.postsdir}/${this.path}/images`).forEach(
106
- (image) => {
107
- fs.copyFileSync(
108
- `${this.config.dev.postsdir}/${this.path}/images/${image}`,
109
- `${this.config.dev.outdir}/${this.path}/images/${image}`,
110
- );
111
- },
112
- );
113
- }
114
- }
115
- };
1
+ const fm = require("front-matter");
2
+ const fs = require("fs");
3
+ const path = require("path");
4
+ const marked = require("./marked");
5
+ const PageBase = require("./page_base");
6
+
7
+ module.exports = class Page extends PageBase {
8
+ constructor(config) {
9
+ super(config);
10
+ this.srcFilePath = "";
11
+ this.markdownFilePath = ""; // markdown file path
12
+ this.contentPath = config.dev.content;
13
+ }
14
+
15
+ readSource(filePath) {
16
+ if (filePath.indexOf(".md") === -1)
17
+ this.srcFilePath = filePath + "/index.md";
18
+ else this.srcFilePath = filePath;
19
+
20
+ const mdContent = fs.readFileSync(this.srcFilePath, "utf8");
21
+
22
+ if (path.extname(filePath) === ".md") {
23
+ // If the file has a .md extension, extract the file name without the extension
24
+ this.path = path.basename(filePath, ".md");
25
+ } else {
26
+ // If there's no .md extension, just get the last part of the path
27
+ this.path = path.basename(filePath);
28
+ }
29
+ // parsed content by fields and body
30
+ const content = fm(mdContent);
31
+
32
+ this.title = `${content.attributes.title}`;
33
+ this.date = content.attributes.date;
34
+ this.url = `${this.config.blogsite}/${this.path}/`;
35
+ this.image = content.attributes.image;
36
+ this.description = content.attributes.description;
37
+ // default image if no imageURL is specified
38
+ this.imageURL = this.config.image;
39
+
40
+ if (content.attributes.tags && content.attributes.tags.length > 0) {
41
+ const tagArray = content.attributes.tags.split(",");
42
+ this.tags = tagArray
43
+ .map((tag) => tag.trim())
44
+ .sort((a, b) => a.localeCompare(b));
45
+ }
46
+
47
+ // generated HTML from markdown
48
+ this.body = marked.parse(content.body);
49
+ // remove <p></p> and <p> </p> from the beginning and end of the content.body
50
+ this.body = this.body.replace(/<p><\/p>/g, "").replace(/<p> <\/p>/g, "");
51
+
52
+ // for generating the navigation link in the post.html template
53
+ this.next = null;
54
+ this.previous = null;
55
+ }
56
+
57
+ // about/index.html or about/hello.html
58
+ generateContent(templateFile, outputPath) {
59
+ // For a series of posts
60
+ if (outputPath === undefined) outputPath = path.join(this.path, "index.html");
61
+ // if file name is not included in the path
62
+ if (outputPath.indexOf(".htm") === -1) {
63
+ outputPath = path.join(outputPath, "index.html");
64
+ }
65
+
66
+ const outputDir = path.dirname(outputPath);
67
+ // if a directory path is included in the path
68
+ if (outputDir !== ".") {
69
+ if (this.path === "")
70
+ this.path = outputDir;
71
+
72
+ const outPath = path.join(this.config.dev.outdir, this.path);
73
+ if (fs.existsSync(outPath))
74
+ fs.rmSync(outPath, {
75
+ recursive: true,
76
+ });
77
+
78
+ fs.mkdirSync(outPath);
79
+ } else {
80
+ // remove the outputPath file if it exists
81
+ const outPath = path.join(this.config.dev.outdir, this.path);
82
+ if (fs.existsSync(outPath))
83
+ fs.unlinkSync(outPath);
84
+ }
85
+
86
+ const layoutsPath = path.join(this.config.themePath, "layouts", templateFile);
87
+ const postHTML = this.generateHTML(layoutsPath);
88
+
89
+ fs.writeFileSync(
90
+ path.join(this.config.dev.outdir, outputPath),
91
+ postHTML,
92
+ (e) => {
93
+ if (e) throw e;
94
+ console.log(`${outputPath}/index.html was created successfully`);
95
+ },
96
+ );
97
+
98
+ // if there is the images folder in the output directory.
99
+ const srcImagesDir = path.join(this.config.dev.postsdir, this.path, "images");
100
+ if (
101
+ fs.existsSync(srcImagesDir) &&
102
+ this.path !== ""
103
+ ) {
104
+ // Copy images folder from postsdir to outdir
105
+ const destImagesDir = path.join(this.config.dev.outdir, this.path, "images");
106
+ if (!fs.existsSync(destImagesDir))
107
+ fs.mkdirSync(destImagesDir);
108
+
109
+ fs.readdirSync(srcImagesDir).forEach(
110
+ (image) => {
111
+ fs.copyFileSync(
112
+ path.join(srcImagesDir, image),
113
+ path.join(destImagesDir, image),
114
+ );
115
+ },
116
+ );
117
+ }
118
+ }
119
+ };
@@ -1,97 +1,97 @@
1
- const fs = require("fs");
2
- const path = require("path");
3
-
4
- module.exports = class PageBase {
5
- constructor(config) {
6
- this.config = config;
7
- this.title = "";
8
- this.image = this.config.image;
9
- this.description = this.config.blogDescription;
10
- this.date = "";
11
- this.tags = [];
12
- this.content = "";
13
- this.url = "";
14
- this.theme = this.config.theme;
15
- }
16
-
17
- formatDate(date) {
18
- const options = { year: "numeric", month: "short", day: "numeric" };
19
- return date.toLocaleDateString("en-US", options);
20
- }
21
-
22
- googleAnalytics(trackingId) {
23
- return `<script async src="https://www.googletagmanager.com/gtag/js?id=${trackingId}"></script>
24
- <script>
25
- window.dataLayer = window.dataLayer || [];
26
- function gtag() {
27
- dataLayer.push(arguments);
28
- }
29
- gtag("js", new Date());
30
- gtag("config", "${trackingId}");
31
- </script>`;
32
- }
33
-
34
- // https://developer.twitter.com/en/docs/twitter-for-websites/cards/overview/abouts-cards
35
- twitterCard(card) {
36
- return `<meta name="twitter:card" content="${card}" />
37
- <meta name="twitter:site" content="${this.config.siteTwitter}" />
38
- <meta name="twitter:creator" content="${this.config.authorTwitter}" />
39
- <meta name="twitter:title" content="${this.title}" />
40
- <meta name="twitter:description" content="${this.description}" />
41
- <meta name="twitter:image" content="${this.imageURL}" />`;
42
- }
43
-
44
- // https://ogp.me/
45
- openGraph(type, articleObj) {
46
- const result = `<meta property="og:type" content="${type}" />
47
- <meta property="og:site_name" content="${this.config.blogName}" />
48
- <meta property="og:url" content="${this.url}" />
49
- <meta property="og:title" content="${this.title}" />
50
- <meta property="og:description" content="${this.description}" />
51
- <meta property="og:image" content="${this.imageURL}" />`;
52
-
53
- if (type === "article" && articleObj) {
54
- return (
55
- result +
56
- `
57
- <meta property="article:author" content="${articleObj.authorName}" />
58
- <meta property="article:published_time" content="${articleObj.publishedDate}" />
59
- ${articleObj.tags.map((tag) => `<meta property="article:tag" content="${tag}">`).join("\n ")}`
60
- );
61
- }
62
- return result;
63
- }
64
-
65
- footer() {
66
- const footerPath = path.join(this.config.themePath, "layouts", "partials", "footer.html");
67
- const footerTemplate = fs.readFileSync(footerPath, "utf-8");
68
-
69
- const funcFooter = new Function(
70
- "config",
71
- `return () => \`${footerTemplate}\`;`,
72
- );
73
- const result = funcFooter(this.config)();
74
- const footerHTML = result
75
- .split("\n")
76
- .map((line, index) => (index !== 0 ? ` ${line}` : line))
77
- .join("\n");
78
-
79
- return footerHTML;
80
- }
81
-
82
- generateHTML(templatePath, data) {
83
- const postTemplate = fs.readFileSync(templatePath, "utf-8");
84
-
85
- const funcPost = new Function(
86
- "page, data",
87
- `return () => \`${postTemplate}\`;`,
88
- );
89
- const result = funcPost(this, data)();
90
- const postHTML = result
91
- .split("\n")
92
- .map((line, index) => (index !== 0 ? `${line}` : line))
93
- .join("\n");
94
-
95
- return postHTML;
96
- }
97
- };
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+
4
+ module.exports = class PageBase {
5
+ constructor(config) {
6
+ this.config = config;
7
+ this.title = "";
8
+ this.image = this.config.image;
9
+ this.description = this.config.blogDescription;
10
+ this.date = "";
11
+ this.tags = [];
12
+ this.content = "";
13
+ this.url = "";
14
+ this.theme = this.config.theme;
15
+ }
16
+
17
+ formatDate(date) {
18
+ const options = { year: "numeric", month: "short", day: "numeric" };
19
+ return date.toLocaleDateString("en-US", options);
20
+ }
21
+
22
+ googleAnalytics(trackingId) {
23
+ return `<script async src="https://www.googletagmanager.com/gtag/js?id=${trackingId}"></script>
24
+ <script>
25
+ window.dataLayer = window.dataLayer || [];
26
+ function gtag() {
27
+ dataLayer.push(arguments);
28
+ }
29
+ gtag("js", new Date());
30
+ gtag("config", "${trackingId}");
31
+ </script>`;
32
+ }
33
+
34
+ // https://developer.twitter.com/en/docs/twitter-for-websites/cards/overview/abouts-cards
35
+ twitterCard(card) {
36
+ return `<meta name="twitter:card" content="${card}" />
37
+ <meta name="twitter:site" content="${this.config.siteTwitter}" />
38
+ <meta name="twitter:creator" content="${this.config.authorTwitter}" />
39
+ <meta name="twitter:title" content="${this.title}" />
40
+ <meta name="twitter:description" content="${this.description}" />
41
+ <meta name="twitter:image" content="${this.imageURL}" />`;
42
+ }
43
+
44
+ // https://ogp.me/
45
+ openGraph(type, articleObj) {
46
+ const result = `<meta property="og:type" content="${type}" />
47
+ <meta property="og:site_name" content="${this.config.blogName}" />
48
+ <meta property="og:url" content="${this.url}" />
49
+ <meta property="og:title" content="${this.title}" />
50
+ <meta property="og:description" content="${this.description}" />
51
+ <meta property="og:image" content="${this.imageURL}" />`;
52
+
53
+ if (type === "article" && articleObj) {
54
+ return (
55
+ result +
56
+ `
57
+ <meta property="article:author" content="${articleObj.authorName}" />
58
+ <meta property="article:published_time" content="${articleObj.publishedDate}" />
59
+ ${articleObj.tags.map((tag) => `<meta property="article:tag" content="${tag}">`).join("\n ")}`
60
+ );
61
+ }
62
+ return result;
63
+ }
64
+
65
+ footer() {
66
+ const footerPath = path.join(this.config.themePath, "layouts", "partials", "footer.html");
67
+ const footerTemplate = fs.readFileSync(footerPath, "utf-8");
68
+
69
+ const funcFooter = new Function(
70
+ "config",
71
+ `return () => \`${footerTemplate}\`;`,
72
+ );
73
+ const result = funcFooter(this.config)();
74
+ const footerHTML = result
75
+ .split("\n")
76
+ .map((line, index) => (index !== 0 ? ` ${line}` : line))
77
+ .join("\n");
78
+
79
+ return footerHTML;
80
+ }
81
+
82
+ generateHTML(templatePath, data) {
83
+ const postTemplate = fs.readFileSync(templatePath, "utf-8");
84
+
85
+ const funcPost = new Function(
86
+ "page, data",
87
+ `return () => \`${postTemplate}\`;`,
88
+ );
89
+ const result = funcPost(this, data)();
90
+ const postHTML = result
91
+ .split("\n")
92
+ .map((line, index) => (index !== 0 ? `${line}` : line))
93
+ .join("\n");
94
+
95
+ return postHTML;
96
+ }
97
+ };
package/lib/new.js CHANGED
@@ -1,48 +1,48 @@
1
- const fs = require("fs");
2
- const path = require("path");
3
-
4
- function createPost(config, title) {
5
- const postsDir = config.dev.postsdir;
6
- const postDir = path.join(postsDir, title);
7
-
8
- if (fs.existsSync(postDir)) {
9
- console.error(`Error: Post "${title}" already exists at ${postDir}`);
10
- process.exit(1);
11
- }
12
-
13
- // Ensure the posts directory exists
14
- fs.mkdirSync(postsDir, { recursive: true });
15
-
16
- // Create the post directory and images subdirectory
17
- fs.mkdirSync(postDir, { recursive: true });
18
- fs.mkdirSync(path.join(postDir, "images"), { recursive: true });
19
-
20
- // Generate today's date in YYYY-MM-DD format
21
- const today = new Date();
22
- const dateStr = today.toISOString().split("T")[0];
23
-
24
- // Copy placeholder image to the post's images directory
25
- const placeholderSrc = path.join(__dirname, "assets", "placeholder.svg");
26
- const placeholderDest = path.join(postDir, "images", "placeholder.svg");
27
- if (fs.existsSync(placeholderSrc)) {
28
- fs.copyFileSync(placeholderSrc, placeholderDest);
29
- }
30
-
31
- // Write index.md with front-matter
32
- const frontMatter = `---
33
- title: ${title}
34
- date: ${dateStr}
35
- description: ""
36
- image: "placeholder.svg"
37
- tags: ""
38
- ---
39
- `;
40
-
41
- fs.writeFileSync(path.join(postDir, "index.md"), frontMatter);
42
-
43
- console.log(`Created new post: ${postDir}`);
44
- console.log(` - ${path.join(postDir, "index.md")}`);
45
- console.log(` - ${path.join(postDir, "images/")}`);
46
- }
47
-
48
- module.exports = { createPost };
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+
4
+ function createPost(config, title) {
5
+ const postsDir = config.dev.postsdir;
6
+ const postDir = path.join(postsDir, title);
7
+
8
+ if (fs.existsSync(postDir)) {
9
+ console.error(`Error: Post "${title}" already exists at ${postDir}`);
10
+ process.exit(1);
11
+ }
12
+
13
+ // Ensure the posts directory exists
14
+ fs.mkdirSync(postsDir, { recursive: true });
15
+
16
+ // Create the post directory and images subdirectory
17
+ fs.mkdirSync(postDir, { recursive: true });
18
+ fs.mkdirSync(path.join(postDir, "images"), { recursive: true });
19
+
20
+ // Generate today's date in YYYY-MM-DD format
21
+ const today = new Date();
22
+ const dateStr = today.toISOString().split("T")[0];
23
+
24
+ // Copy placeholder image to the post's images directory
25
+ const placeholderSrc = path.join(__dirname, "assets", "placeholder.svg");
26
+ const placeholderDest = path.join(postDir, "images", "placeholder.svg");
27
+ if (fs.existsSync(placeholderSrc)) {
28
+ fs.copyFileSync(placeholderSrc, placeholderDest);
29
+ }
30
+
31
+ // Write index.md with front-matter
32
+ const frontMatter = `---
33
+ title: ${title}
34
+ date: ${dateStr}
35
+ description: ""
36
+ image: "placeholder.svg"
37
+ tags: ""
38
+ ---
39
+ `;
40
+
41
+ fs.writeFileSync(path.join(postDir, "index.md"), frontMatter);
42
+
43
+ console.log(`Created new post: ${postDir}`);
44
+ console.log(` - ${path.join(postDir, "index.md")}`);
45
+ console.log(` - ${path.join(postDir, "images/")}`);
46
+ }
47
+
48
+ module.exports = { createPost };
package/lib/posts.js CHANGED
@@ -1,42 +1,47 @@
1
- const fs = require("fs");
2
- const Page = require("./mod/page");
3
-
4
- module.exports = class Posts {
5
- constructor(config) {
6
- this.config = config;
7
- this.posts = [];
8
- }
9
-
10
- // Read all markdown articles from content/posts and sort them by date
11
- createPostObjects() {
12
- const postPaths = fs.readdirSync(this.config.dev.postsdir);
13
- postPaths.forEach((postPath) => {
14
- const indexPath = `${this.config.dev.postsdir}/${postPath}/index.md`;
15
- if (!fs.existsSync(indexPath)) {
16
- console.warn(`Skipping "${postPath}": no index.md found`);
17
- return;
18
- }
19
- const post = new Page(this.config);
20
- post.readSource(`${this.config.dev.postsdir}/${postPath}`);
21
- post.url = `${this.config.blogsite}/${post.path}/`;
22
- post.imageURL = `${this.config.blogsite}/${post.path}/images/${post.image}`;
23
- this.posts.push(post);
24
- });
25
- // sort by date
26
- this.posts.sort(function (a, b) {
27
- return new Date(b.date) - new Date(a.date);
28
- });
29
-
30
- // loop through posts and add previous and next post to each post
31
- for (let i = 0; i < this.posts.length; i++) {
32
- if (i > 0) {
33
- this.posts[i].next = this.posts[i - 1];
34
- }
35
- if (i < this.posts.length - 1) {
36
- this.posts[i].previous = this.posts[i + 1];
37
- }
38
- }
39
-
40
- return this.posts;
41
- }
42
- };
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ const Page = require("./mod/page");
4
+
5
+ module.exports = class Posts {
6
+ constructor(config) {
7
+ this.config = config;
8
+ this.posts = [];
9
+ }
10
+
11
+ // Read all markdown articles from content/posts and sort them by date
12
+ createPostObjects() {
13
+ if (!fs.existsSync(this.config.dev.postsdir)) {
14
+ console.warn(`No posts directory found at "${this.config.dev.postsdir}", skipping posts.`);
15
+ return;
16
+ }
17
+ const postPaths = fs.readdirSync(this.config.dev.postsdir);
18
+ postPaths.forEach((postPath) => {
19
+ const indexPath = path.join(this.config.dev.postsdir, postPath, "index.md");
20
+ if (!fs.existsSync(indexPath)) {
21
+ console.warn(`Skipping "${postPath}": no index.md found`);
22
+ return;
23
+ }
24
+ const post = new Page(this.config);
25
+ post.readSource(path.join(this.config.dev.postsdir, postPath));
26
+ post.url = `${this.config.blogsite}/${post.path}/`;
27
+ post.imageURL = `${this.config.blogsite}/${post.path}/images/${post.image}`;
28
+ this.posts.push(post);
29
+ });
30
+ // sort by date
31
+ this.posts.sort(function (a, b) {
32
+ return new Date(b.date) - new Date(a.date);
33
+ });
34
+
35
+ // loop through posts and add previous and next post to each post
36
+ for (let i = 0; i < this.posts.length; i++) {
37
+ if (i > 0) {
38
+ this.posts[i].next = this.posts[i - 1];
39
+ }
40
+ if (i < this.posts.length - 1) {
41
+ this.posts[i].previous = this.posts[i + 1];
42
+ }
43
+ }
44
+
45
+ return this.posts;
46
+ }
47
+ };
package/lib/server.js CHANGED
@@ -1,14 +1,20 @@
1
- const express = require("express");
2
-
3
- function serve(config, port) {
4
- const app = express();
5
- const PORT = port || process.env.PORT || 3000;
6
-
7
- app.use(express.static(config.dev.outdir));
8
-
9
- app.listen(PORT, () => {
10
- console.log(`fossbook dev server running at http://localhost:${PORT}`);
11
- });
12
- }
13
-
14
- module.exports = { serve };
1
+ const express = require("express");
2
+
3
+ function serve(config, port) {
4
+ const app = express();
5
+ const PORT = port || process.env.PORT || 3000;
6
+
7
+ const basePath = config.basePath || "/";
8
+ app.use(basePath, express.static(config.dev.outdir));
9
+
10
+ // Redirect root to basePath if basePath is not "/"
11
+ if (basePath !== "/") {
12
+ app.get("/", (req, res) => res.redirect(basePath));
13
+ }
14
+
15
+ app.listen(PORT, () => {
16
+ console.log(`fossbook dev server running at http://localhost:${PORT}`);
17
+ });
18
+ }
19
+
20
+ module.exports = { serve };