@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 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 Vinyl from "vinyl";
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$ = fromEvent(req, "data").pipe(
148
- map((chunk) => chunk.toString()),
149
- scan((acc, chunk) => acc + chunk, ""),
150
- takeUntil(fromEvent(req, "end")),
151
- last(),
152
- defaultIfEmpty("")
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
- // OLD CODE
223
- /*let body = "";
224
- req.on("data", (chunk) => {
225
- body += chunk.toString();
226
- });*/
227
- // NEW CODE
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
- const vinyls = fileData.map(
528
- (f) =>
529
- new Vinyl({
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
@@ -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
- "gulp": "^5.0.1",
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.2",
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
- "gulp": "^5.0.1",
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}nav a:visited{color:#3b40c1;text-decoration-color:#3b40c1}body{background-color:#fdfdfd;font-family:Arial}nav a{color:#3b40c1;font-size:20px;text-decoration:underline}.datetime{color:#434343;font-style:italic}h2{margin:0 0 5px}p{margin-top:10px}span{margin:0}
2
- /*# sourceMappingURL=styles.min.css.map */
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