@lexho111/plainblog 0.3.2 → 0.3.4
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/Blog.js +18 -104
- package/blog.db +0 -0
- package/build-styles.js +61 -0
- package/gulp_frontend/package.json +1 -8
- package/package.json +2 -13
- package/styles.min.css +2 -2
- package/test_1767117403635.db +0 -0
- package/test_1767117781437.db +0 -0
- package/test_1767117944836.db +0 -0
package/Blog.js
CHANGED
|
@@ -2,10 +2,7 @@ import http from "http";
|
|
|
2
2
|
import crypto from "crypto";
|
|
3
3
|
import fs from "fs";
|
|
4
4
|
import { URLSearchParams } from "url";
|
|
5
|
-
import { Readable, Transform, Writable, pipeline } from "stream";
|
|
6
5
|
import { MapTransform } from "./streams.js";
|
|
7
|
-
import { fromEvent, firstValueFrom } from "rxjs";
|
|
8
|
-
import { map, scan, takeUntil, last, defaultIfEmpty } from "rxjs/operators";
|
|
9
6
|
import Article from "./Article.js";
|
|
10
7
|
import DatabaseModel from "./model/DatabaseModel.js";
|
|
11
8
|
import { fetchData, postData } from "./model/APIModel.js";
|
|
@@ -16,8 +13,7 @@ import path from "path";
|
|
|
16
13
|
import { fileURLToPath } from "url";
|
|
17
14
|
import { exec } from "child_process";
|
|
18
15
|
import { promisify } from "util";
|
|
19
|
-
import
|
|
20
|
-
import { compileStyles, compileScripts } from "./gulp_frontend/gulpfile.js";
|
|
16
|
+
import { compileStyles } from "./build-styles.js";
|
|
21
17
|
|
|
22
18
|
const execPromise = promisify(exec);
|
|
23
19
|
|
|
@@ -144,14 +140,12 @@ export default class Blog {
|
|
|
144
140
|
}
|
|
145
141
|
|
|
146
142
|
async handleLogin(req, res) {
|
|
147
|
-
const body
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
);
|
|
154
|
-
const body = await firstValueFrom(body$);
|
|
143
|
+
const body = await new Promise((resolve, reject) => {
|
|
144
|
+
let data = "";
|
|
145
|
+
req.on("data", (chunk) => (data += chunk.toString()));
|
|
146
|
+
req.on("end", () => resolve(data));
|
|
147
|
+
req.on("error", reject);
|
|
148
|
+
});
|
|
155
149
|
const params = new URLSearchParams(body);
|
|
156
150
|
|
|
157
151
|
if (params.get("password") === this.#password) {
|
|
@@ -219,22 +213,12 @@ export default class Blog {
|
|
|
219
213
|
|
|
220
214
|
/** post a blog article */
|
|
221
215
|
async postArticle(req, res) {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
// Create a stream of the body string
|
|
229
|
-
const body$ = fromEvent(req, "data").pipe(
|
|
230
|
-
map((chunk) => chunk.toString()),
|
|
231
|
-
scan((acc, chunk) => acc + chunk, ""), // Accumulate chunks
|
|
232
|
-
takeUntil(fromEvent(req, "end")), // Stop when 'end' emits
|
|
233
|
-
last(), // Emit only the final full string
|
|
234
|
-
defaultIfEmpty("") // Handle empty bodies
|
|
235
|
-
);
|
|
236
|
-
|
|
237
|
-
const body = await firstValueFrom(body$);
|
|
216
|
+
const body = await new Promise((resolve, reject) => {
|
|
217
|
+
let data = "";
|
|
218
|
+
req.on("data", (chunk) => (data += chunk.toString()));
|
|
219
|
+
req.on("end", () => resolve(data));
|
|
220
|
+
req.on("error", reject);
|
|
221
|
+
});
|
|
238
222
|
|
|
239
223
|
//req.on("end", async () => {
|
|
240
224
|
const params = new URLSearchParams(body);
|
|
@@ -499,7 +483,7 @@ export default class Blog {
|
|
|
499
483
|
const styleFiles = files.filter(
|
|
500
484
|
(f) => (f.endsWith(".scss") || f.endsWith(".css")) && !f.endsWith(".min.css")
|
|
501
485
|
);
|
|
502
|
-
const scriptFiles = files.filter((f) => f.endsWith(".js") && !f.endsWith(".min.js"));
|
|
486
|
+
//const scriptFiles = files.filter((f) => f.endsWith(".js") && !f.endsWith(".min.js"));
|
|
503
487
|
|
|
504
488
|
// --- Process Styles ---
|
|
505
489
|
if (styleFiles.length > 0) {
|
|
@@ -523,20 +507,10 @@ export default class Blog {
|
|
|
523
507
|
if (currentHash !== this.#stylesHash) {
|
|
524
508
|
console.log("Style assets have changed. Recompiling...");
|
|
525
509
|
this.#stylesHash = currentHash;
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
cwd: process.cwd(),
|
|
531
|
-
base: path.dirname(f.path),
|
|
532
|
-
path: f.path,
|
|
533
|
-
contents: f.content,
|
|
534
|
-
})
|
|
535
|
-
);
|
|
536
|
-
|
|
537
|
-
const stream = Readable.from(vinyls);
|
|
538
|
-
const outputStream = compileStyles(stream);
|
|
539
|
-
this.compiledStyles = await this.#streamToString(outputStream);
|
|
510
|
+
|
|
511
|
+
// Compile styles using the standalone script
|
|
512
|
+
this.compiledStyles = await compileStyles();
|
|
513
|
+
|
|
540
514
|
await fs.promises.writeFile(
|
|
541
515
|
path.join(__dirname, "styles.min.css"),
|
|
542
516
|
this.compiledStyles + `\n/* source-hash: ${currentHash} */`
|
|
@@ -545,66 +519,6 @@ export default class Blog {
|
|
|
545
519
|
console.log("styles are up-to-date")
|
|
546
520
|
}
|
|
547
521
|
}
|
|
548
|
-
|
|
549
|
-
// --- Process Scripts ---
|
|
550
|
-
if (scriptFiles.length > 0) {
|
|
551
|
-
const fileData = await Promise.all(
|
|
552
|
-
scriptFiles.sort().map(async (f) => {
|
|
553
|
-
const content = await fs.promises.readFile(f);
|
|
554
|
-
return { path: f, content };
|
|
555
|
-
})
|
|
556
|
-
);
|
|
557
|
-
|
|
558
|
-
const currentHash = crypto
|
|
559
|
-
.createHash("sha256")
|
|
560
|
-
.update(
|
|
561
|
-
fileData
|
|
562
|
-
.map((f) =>
|
|
563
|
-
crypto.createHash("sha256").update(f.content).digest("hex")
|
|
564
|
-
)
|
|
565
|
-
.join("")
|
|
566
|
-
)
|
|
567
|
-
.digest("hex");
|
|
568
|
-
|
|
569
|
-
if (currentHash !== this.#scriptsHash) {
|
|
570
|
-
console.log("Script assets have changed. Recompiling...");
|
|
571
|
-
this.#scriptsHash = currentHash;
|
|
572
|
-
|
|
573
|
-
const vinyls = fileData.map(
|
|
574
|
-
(f) =>
|
|
575
|
-
new Vinyl({
|
|
576
|
-
cwd: process.cwd(),
|
|
577
|
-
base: path.dirname(f.path),
|
|
578
|
-
path: f.path,
|
|
579
|
-
contents: f.content,
|
|
580
|
-
})
|
|
581
|
-
);
|
|
582
|
-
fileData.map(
|
|
583
|
-
(f) => {
|
|
584
|
-
console.log(`${f.path} ${f.content}`)})
|
|
585
|
-
|
|
586
|
-
const stream = Readable.from(vinyls);
|
|
587
|
-
const outputStream = compileScripts(stream);
|
|
588
|
-
this.compiledScripts = await this.#streamToString(outputStream);
|
|
589
|
-
await fs.promises.writeFile(
|
|
590
|
-
path.join(__dirname, "scripts.min.js"),
|
|
591
|
-
this.compiledScripts + `\n/* source-hash: ${currentHash} */`
|
|
592
|
-
);
|
|
593
|
-
} else {
|
|
594
|
-
console.log("scripts are up-to-date")
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
#streamToString(stream) {
|
|
600
|
-
return new Promise((resolve, reject) => {
|
|
601
|
-
let data = "";
|
|
602
|
-
stream.on("data", (file) => {
|
|
603
|
-
if (file.contents) data += file.contents.toString();
|
|
604
|
-
});
|
|
605
|
-
stream.on("end", () => resolve(data));
|
|
606
|
-
stream.on("error", reject);
|
|
607
|
-
});
|
|
608
522
|
}
|
|
609
523
|
|
|
610
524
|
async #findAssetFiles() {
|
package/blog.db
ADDED
|
Binary file
|
package/build-styles.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { promises as fs } from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import * as sass from "sass";
|
|
4
|
+
import postcss from "postcss";
|
|
5
|
+
import autoprefixer from "autoprefixer";
|
|
6
|
+
import cssnano from "cssnano";
|
|
7
|
+
|
|
8
|
+
// Configuration
|
|
9
|
+
const srcDir = path.join("gulp_frontend", "src");
|
|
10
|
+
|
|
11
|
+
// Helper to find files recursively
|
|
12
|
+
async function getFiles(dir) {
|
|
13
|
+
const dirents = await fs.readdir(dir, { withFileTypes: true });
|
|
14
|
+
const files = await Promise.all(
|
|
15
|
+
dirents.map((dirent) => {
|
|
16
|
+
const res = path.resolve(dir, dirent.name);
|
|
17
|
+
return dirent.isDirectory() ? getFiles(res) : res;
|
|
18
|
+
})
|
|
19
|
+
);
|
|
20
|
+
return Array.prototype.concat(...files);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function compileStyles() {
|
|
24
|
+
try {
|
|
25
|
+
const allFiles = await getFiles(srcDir);
|
|
26
|
+
|
|
27
|
+
// Filter for .scss files and exclude partials (files starting with _)
|
|
28
|
+
// Sort them to ensure consistent concatenation order
|
|
29
|
+
const scssFiles = allFiles
|
|
30
|
+
.filter((f) => f.endsWith(".scss") && !path.basename(f).startsWith("_"))
|
|
31
|
+
.sort();
|
|
32
|
+
|
|
33
|
+
let combinedCss = "";
|
|
34
|
+
|
|
35
|
+
// 1. Compile Sass
|
|
36
|
+
for (const file of scssFiles) {
|
|
37
|
+
try {
|
|
38
|
+
const result = sass.compile(file, {
|
|
39
|
+
loadPaths: [srcDir],
|
|
40
|
+
style: "expanded",
|
|
41
|
+
});
|
|
42
|
+
combinedCss += result.css + "\n";
|
|
43
|
+
} catch (err) {
|
|
44
|
+
console.error(`Error compiling ${path.basename(file)}:`, err.message);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// 2. PostCSS (Autoprefixer + CSSNano)
|
|
49
|
+
if (combinedCss) {
|
|
50
|
+
const plugins = [autoprefixer(), cssnano()];
|
|
51
|
+
const result = await postcss(plugins).process(combinedCss, {
|
|
52
|
+
from: undefined,
|
|
53
|
+
});
|
|
54
|
+
return result.css;
|
|
55
|
+
}
|
|
56
|
+
return "";
|
|
57
|
+
} catch (error) {
|
|
58
|
+
console.error("Build failed:", error);
|
|
59
|
+
return "";
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -15,14 +15,7 @@
|
|
|
15
15
|
"@babel/preset-env": "^7.28.5",
|
|
16
16
|
"autoprefixer": "^10.4.23",
|
|
17
17
|
"cssnano": "^7.1.2",
|
|
18
|
-
"
|
|
19
|
-
"gulp-babel": "^8.0.0",
|
|
20
|
-
"gulp-concat": "^2.6.1",
|
|
21
|
-
"gulp-postcss": "^10.0.0",
|
|
22
|
-
"gulp-rename": "^2.1.0",
|
|
23
|
-
"gulp-sass": "^6.0.1",
|
|
24
|
-
"gulp-sourcemaps": "^3.0.0",
|
|
25
|
-
"gulp-uglify": "^3.0.2",
|
|
18
|
+
"postcss": "^8.4.35",
|
|
26
19
|
"sass": "^1.97.1"
|
|
27
20
|
}
|
|
28
21
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lexho111/plainblog",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.4",
|
|
4
4
|
"description": "A tool for creating and serving a minimalist, single-page blog.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -19,22 +19,11 @@
|
|
|
19
19
|
"node-fetch": "^3.3.2",
|
|
20
20
|
"pg": "^8.16.3",
|
|
21
21
|
"pg-hstore": "^2.3.4",
|
|
22
|
-
"rxjs": "^7.8.2",
|
|
23
22
|
"sequelize": "^6.37.7",
|
|
24
23
|
"sqlite3": "^5.1.7",
|
|
25
|
-
"vinyl": "^3.0.1",
|
|
26
|
-
"@babel/core": "^7.28.5",
|
|
27
|
-
"@babel/preset-env": "^7.28.5",
|
|
28
24
|
"autoprefixer": "^10.4.23",
|
|
29
25
|
"cssnano": "^7.1.2",
|
|
30
|
-
"
|
|
31
|
-
"gulp-babel": "^8.0.0",
|
|
32
|
-
"gulp-concat": "^2.6.1",
|
|
33
|
-
"gulp-postcss": "^10.0.0",
|
|
34
|
-
"gulp-rename": "^2.1.0",
|
|
35
|
-
"gulp-sass": "^6.0.1",
|
|
36
|
-
"gulp-sourcemaps": "^3.0.0",
|
|
37
|
-
"gulp-uglify": "^3.0.2",
|
|
26
|
+
"postcss": "^8.4.35",
|
|
38
27
|
"sass": "^1.97.1"
|
|
39
28
|
},
|
|
40
29
|
"devDependencies": {
|
package/styles.min.css
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
.grid{border:0 solid #000;display:grid;gap:.25rem;grid-template-columns:1fr}.grid article{border:0 solid #ccc;border-radius:4px;min-width:0;overflow-wrap:break-word;padding:.25rem}
|
|
2
|
-
|
|
1
|
+
.grid{border:0 solid #000;display:grid;gap:.25rem;grid-template-columns:1fr}.grid article{border:0 solid #ccc;border-radius:4px;min-width:0;overflow-wrap:break-word;padding:.25rem}h1{color:#7f0000}nav a{color:#3b40c1;font-size:20px;text-decoration:underline}nav a:visited{color:#3b40c1;text-decoration-color:#3b40c1}
|
|
2
|
+
/* source-hash: 91ab1d754238759404d2b8becfe52372f6f7b9c0f136b8b125c69faadeacd9d7 */
|
|
Binary file
|
|
Binary file
|
|
Binary file
|