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/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 =
|
|
61
|
-
// if file name is not included in the path
|
|
62
|
-
if (outputPath.indexOf(".htm") === -1) {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
// if a directory path is included in the path
|
|
68
|
-
if (
|
|
69
|
-
if (this.path === "")
|
|
70
|
-
this.path =
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
if
|
|
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
|
-
}
|
|
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
|
+
};
|
package/lib/mod/page_base.js
CHANGED
|
@@ -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
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
this.
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
//
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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 };
|