@lexho111/plainblog 0.7.2 → 0.7.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/Article.js +82 -90
- package/Article.ts +97 -0
- package/AssetManager.js +63 -116
- package/Auth.js +2 -2
- package/Blog.js +103 -82
- package/cluster-server.js +36 -31
- package/debug-loader.js +16 -1
- package/index.js +2 -0
- package/model/DataModel.js +11 -9
- package/model/SqliteAdapter.js +15 -7
- package/model/datastructures/BinarySearchTree.js +1 -1
- package/modules/csscompiler/compile-style-worker.js +72 -0
- package/modules/csscompiler/csscompiler.js +135 -0
- package/modules/jscompiler/compile-js-worker.js +34 -0
- package/{build-scripts.js → modules/jscompiler/jscompiler.js} +3 -1
- package/package.json +4 -7
- package/public/styles.min.css +8 -2
- package/router.js +60 -64
- package/server.js +190 -155
- package/src/styles.css +1 -1
- package/styles.hash +1 -0
- package/tsconfig.json +12 -0
- package/.dependency-cruiser.cjs +0 -382
- package/ArrayList.cpuprofile +0 -1
- package/BinarySearchTree.cpuprofile +0 -1
- package/FlameChartProfile.cpuprofile +0 -1
- package/FlameChartProfile2.cpuprofile +0 -1
- package/FlameChartProfile3.cpuprofile +0 -1
- package/StandaloneProfile.cpuprofile +0 -1
- package/blog_test_empty +0 -0
- package/blog_test_load +0 -0
- package/build-styles.js +0 -87
- package/workers/compiler-worker.js +0 -42
package/Blog.js
CHANGED
|
@@ -123,13 +123,13 @@ export default class Blog {
|
|
|
123
123
|
* @param {string} style - A string containing CSS rules.
|
|
124
124
|
*/
|
|
125
125
|
set style(style) {
|
|
126
|
-
debug("set style");
|
|
127
|
-
this.assetManager.
|
|
126
|
+
debug("set style %s", style);
|
|
127
|
+
this.assetManager.style = style;
|
|
128
128
|
this.assetManager.compilestyle = true;
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
get style() {
|
|
132
|
-
return this.assetManager.
|
|
132
|
+
return this.assetManager._styles;
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
/**
|
|
@@ -137,8 +137,8 @@ export default class Blog {
|
|
|
137
137
|
* @param {string|string[]} files - A single file path or an array of file paths.
|
|
138
138
|
*/
|
|
139
139
|
set stylesheetPath(files) {
|
|
140
|
+
debug(`set stylesheet path: ${files}`);
|
|
140
141
|
this.assetManager.stylesheetPath = files;
|
|
141
|
-
console.log(`this.assetManager.stylesheetPath: ${files}`);
|
|
142
142
|
this.assetManager.compilestyle = true;
|
|
143
143
|
}
|
|
144
144
|
|
|
@@ -160,77 +160,98 @@ export default class Blog {
|
|
|
160
160
|
|
|
161
161
|
/** initializes database */
|
|
162
162
|
async init() {
|
|
163
|
-
if (this.#initPromise) return this.#initPromise;
|
|
163
|
+
//if (this.#initPromise) return this.#initPromise;
|
|
164
164
|
|
|
165
165
|
this.#initPromise = (async () => {
|
|
166
|
-
|
|
166
|
+
try {
|
|
167
|
+
try {
|
|
168
|
+
await this.assetManager.init();
|
|
169
|
+
} catch (e) {
|
|
170
|
+
console.warn(
|
|
171
|
+
"Warning: AssetManager initialization failed. Styles may not be compiled.",
|
|
172
|
+
e.message,
|
|
173
|
+
);
|
|
174
|
+
}
|
|
167
175
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
176
|
+
if (this.#isExternalAPI) {
|
|
177
|
+
console.log("external API");
|
|
178
|
+
await this.#loadFromAPI();
|
|
179
|
+
} else {
|
|
180
|
+
debug(`database: ${this.database.type}`);
|
|
181
|
+
if (!this.#databaseModel) {
|
|
182
|
+
if (this.database.type === "file") {
|
|
183
|
+
this.#databaseModel = new DatabaseModel(
|
|
184
|
+
new WorkerAdapter(this.database),
|
|
185
|
+
);
|
|
186
|
+
} else if (this.database.type === "sqlite") {
|
|
187
|
+
this.#databaseModel = new DatabaseModel(
|
|
188
|
+
new SqliteAdapter(this.database),
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
// Timeout nach 60 Sekunden, um Hängenbleiben zu verhindern
|
|
193
|
+
let timeoutHandle;
|
|
194
|
+
const timeout = new Promise((_, reject) => {
|
|
195
|
+
timeoutHandle = setTimeout(
|
|
196
|
+
() => reject(new Error("Database initialization timed out")),
|
|
197
|
+
60000,
|
|
177
198
|
);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
|
|
199
|
+
});
|
|
200
|
+
try {
|
|
201
|
+
await Promise.race([this.#databaseModel.initialize(), timeout]);
|
|
202
|
+
} finally {
|
|
203
|
+
clearTimeout(timeoutHandle); // Timer aufräumen, sobald fertig
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (this.#databaseModel.isReady()) {
|
|
207
|
+
console.log(`connected to database`);
|
|
208
|
+
console.log(
|
|
209
|
+
`using ${this.#databaseModel.getType()} database '${this.#databaseModel.getDBName()}'`,
|
|
181
210
|
);
|
|
182
211
|
}
|
|
212
|
+
this.#articles.setDatabase(this.#databaseModel);
|
|
213
|
+
const [dbTitle, dbArticles] = await Promise.all([
|
|
214
|
+
this.#databaseModel.getBlogTitle(),
|
|
215
|
+
this.#databaseModel.findAll(-1),
|
|
216
|
+
]);
|
|
217
|
+
debug("dbArticles.size(): %d", dbArticles.length);
|
|
218
|
+
//log(filename, "dbArticles.size(): " + dbArticles.length);
|
|
219
|
+
//log(filename, "dbArticles.size(): " + dbArticles.length, "Blog.js");
|
|
220
|
+
debug("all articles in Blog after loading from db");
|
|
221
|
+
|
|
222
|
+
// Displays a beautiful table in the console
|
|
223
|
+
//table(dbArticles)
|
|
224
|
+
|
|
225
|
+
if (dbArticles.length == 0) {
|
|
226
|
+
dbArticles.push(
|
|
227
|
+
new Article(
|
|
228
|
+
1,
|
|
229
|
+
"Sample Entry #1",
|
|
230
|
+
"Prow scuttle parrel provost Sail ho shrouds spirits boom mizzenmast yardarm. Pinnace holystone mizzenmast quarter crow's nest nipperkin grog yardarm hempen halter furl. Swab barque interloper chantey doubloon starboard grog black jack gangway rutters.",
|
|
231
|
+
new Date(),
|
|
232
|
+
),
|
|
233
|
+
);
|
|
234
|
+
dbArticles.push(
|
|
235
|
+
new Article(
|
|
236
|
+
2,
|
|
237
|
+
"Sample Entry #2",
|
|
238
|
+
"Deadlights jack lad schooner scallywag dance the hempen jig carouser broadside cable strike colors. Bring a spring upon her cable holystone blow the man down spanker Shiver me timbers to go on account lookout wherry doubloon chase. Belay yo-ho-ho keelhaul squiffy black spot yardarm spyglass sheet transom heave to.",
|
|
239
|
+
new Date(),
|
|
240
|
+
),
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
if (this.assetManager.reloadStylesOnGET)
|
|
244
|
+
console.log("reload scripts and styles on get-request");
|
|
245
|
+
let title = "";
|
|
246
|
+
if (this.#title != null && this.#title.length > 0)
|
|
247
|
+
title = this.#title; // use blog title if set
|
|
248
|
+
else title = dbTitle; // use title from the database
|
|
249
|
+
const responseData = { title: title, articles: dbArticles };
|
|
250
|
+
this.#applyBlogData(responseData);
|
|
183
251
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
}
|
|
188
|
-
if (this.#databaseModel.isReady()) {
|
|
189
|
-
console.log(`connected to database`);
|
|
190
|
-
console.log(
|
|
191
|
-
`using ${this.#databaseModel.getType()} database '${this.#databaseModel.getDBName()}'`,
|
|
192
|
-
);
|
|
193
|
-
}
|
|
194
|
-
await p;
|
|
195
|
-
this.#articles.setDatabase(this.#databaseModel);
|
|
196
|
-
const [dbTitle, dbArticles] = await Promise.all([
|
|
197
|
-
this.#databaseModel.getBlogTitle(),
|
|
198
|
-
this.#databaseModel.findAll(-1),
|
|
199
|
-
]);
|
|
200
|
-
debug("dbArticles.size(): %d", dbArticles.length);
|
|
201
|
-
//log(filename, "dbArticles.size(): " + dbArticles.length);
|
|
202
|
-
//log(filename, "dbArticles.size(): " + dbArticles.length, "Blog.js");
|
|
203
|
-
debug("all articles in Blog after loading from db");
|
|
204
|
-
|
|
205
|
-
// Displays a beautiful table in the console
|
|
206
|
-
//table(dbArticles)
|
|
207
|
-
|
|
208
|
-
if (dbArticles.length == 0) {
|
|
209
|
-
dbArticles.push(
|
|
210
|
-
new Article(
|
|
211
|
-
1,
|
|
212
|
-
"Sample Entry #1",
|
|
213
|
-
"Prow scuttle parrel provost Sail ho shrouds spirits boom mizzenmast yardarm. Pinnace holystone mizzenmast quarter crow's nest nipperkin grog yardarm hempen halter furl. Swab barque interloper chantey doubloon starboard grog black jack gangway rutters.",
|
|
214
|
-
new Date(),
|
|
215
|
-
),
|
|
216
|
-
);
|
|
217
|
-
dbArticles.push(
|
|
218
|
-
new Article(
|
|
219
|
-
2,
|
|
220
|
-
"Sample Entry #2",
|
|
221
|
-
"Deadlights jack lad schooner scallywag dance the hempen jig carouser broadside cable strike colors. Bring a spring upon her cable holystone blow the man down spanker Shiver me timbers to go on account lookout wherry doubloon chase. Belay yo-ho-ho keelhaul squiffy black spot yardarm spyglass sheet transom heave to.",
|
|
222
|
-
new Date(),
|
|
223
|
-
),
|
|
224
|
-
);
|
|
225
|
-
}
|
|
226
|
-
if (this.assetManager.reloadStylesOnGET)
|
|
227
|
-
console.log("reload scripts and styles on get-request");
|
|
228
|
-
let title = "";
|
|
229
|
-
if (this.#title != null && this.#title.length > 0)
|
|
230
|
-
title = this.#title; // use blog title if set
|
|
231
|
-
else title = dbTitle; // use title from the database
|
|
232
|
-
const responseData = { title: title, articles: dbArticles };
|
|
233
|
-
this.#applyBlogData(responseData);
|
|
252
|
+
} catch (err) {
|
|
253
|
+
this.#initPromise = null; // Reset promise on error to allow retry
|
|
254
|
+
throw err;
|
|
234
255
|
}
|
|
235
256
|
})();
|
|
236
257
|
return this.#initPromise;
|
|
@@ -267,11 +288,6 @@ export default class Blog {
|
|
|
267
288
|
/** start a http server with default port 8080 */
|
|
268
289
|
async startServer(port = 8080) {
|
|
269
290
|
await this.init();
|
|
270
|
-
// is database ready
|
|
271
|
-
while (this.#databaseModel && !this.#databaseModel.isReady()) {
|
|
272
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
273
|
-
//console.log("is not ready;");
|
|
274
|
-
}
|
|
275
291
|
|
|
276
292
|
this.#server = await createServer(
|
|
277
293
|
this.postArticle.bind(this),
|
|
@@ -285,14 +301,16 @@ export default class Blog {
|
|
|
285
301
|
this.database,
|
|
286
302
|
);
|
|
287
303
|
|
|
288
|
-
await new Promise(
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
this.#server.
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
304
|
+
await new Promise(
|
|
305
|
+
function f1(resolve, reject) {
|
|
306
|
+
const errorHandler = (err) => reject(err);
|
|
307
|
+
this.#server.once("error", errorHandler);
|
|
308
|
+
this.#server.listen(port, "0.0.0.0", () => {
|
|
309
|
+
this.#server.removeListener("error", errorHandler);
|
|
310
|
+
resolve();
|
|
311
|
+
});
|
|
312
|
+
}.bind(this),
|
|
313
|
+
);
|
|
296
314
|
console.log(`server running at http://localhost:${port}/`);
|
|
297
315
|
}
|
|
298
316
|
|
|
@@ -363,7 +381,10 @@ export default class Blog {
|
|
|
363
381
|
|
|
364
382
|
/** render this blog content to valid html */
|
|
365
383
|
async toHTML(loggedin) {
|
|
366
|
-
|
|
384
|
+
let articles_after = this.#articles.findAll(null, null, 50);
|
|
385
|
+
if (articles_after instanceof Promise) {
|
|
386
|
+
articles_after = await articles_after;
|
|
387
|
+
}
|
|
367
388
|
// prettier-ignore
|
|
368
389
|
debug("fetched %d articles", articles_after.length);
|
|
369
390
|
const data = {
|
package/cluster-server.js
CHANGED
|
@@ -1,44 +1,49 @@
|
|
|
1
1
|
import cluster from "cluster";
|
|
2
2
|
import os from "os";
|
|
3
3
|
import process from "node:process";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
4
5
|
import Blog from "./Blog.js";
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
export function startCluster(port = process.env.PORT || 8080) {
|
|
8
|
+
const numCPUs = os.cpus().length;
|
|
7
9
|
|
|
8
|
-
if (cluster.isPrimary) {
|
|
9
|
-
|
|
10
|
+
if (cluster.isPrimary) {
|
|
11
|
+
console.log(`master ${process.pid} is running`);
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
// Fork workers (one per CPU core)
|
|
14
|
+
for (let i = 0; i < numCPUs; i++) {
|
|
15
|
+
cluster.fork();
|
|
16
|
+
}
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
} else {
|
|
23
|
-
|
|
24
|
-
|
|
18
|
+
cluster.on("exit", (worker, code, signal) => {
|
|
19
|
+
console.log(
|
|
20
|
+
`worker ${worker.process.pid} died (${signal || code}). restarting...`,
|
|
21
|
+
);
|
|
22
|
+
cluster.fork(); // Restart dead worker
|
|
23
|
+
});
|
|
24
|
+
} else {
|
|
25
|
+
// Worker process
|
|
26
|
+
const blog = new Blog();
|
|
25
27
|
|
|
26
|
-
|
|
28
|
+
blog
|
|
29
|
+
.startServer(port)
|
|
30
|
+
.then(() => {
|
|
31
|
+
console.log(`worker ${process.pid} started on port ${port}`);
|
|
32
|
+
})
|
|
33
|
+
.catch((err) => {
|
|
34
|
+
console.error(`Worker ${process.pid} failed to start:`, err);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
});
|
|
27
37
|
|
|
28
|
-
|
|
29
|
-
.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
.catch((err) => {
|
|
34
|
-
console.error(`Worker ${process.pid} failed to start:`, err);
|
|
35
|
-
process.exit(1);
|
|
38
|
+
// Graceful shutdown
|
|
39
|
+
process.on("SIGTERM", async () => {
|
|
40
|
+
console.log(`worker ${process.pid} shutting down...`);
|
|
41
|
+
await blog.closeServer();
|
|
42
|
+
process.exit(0);
|
|
36
43
|
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
37
46
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
console.log(`worker ${process.pid} shutting down...`);
|
|
41
|
-
await blog.closeServer();
|
|
42
|
-
process.exit(0);
|
|
43
|
-
});
|
|
47
|
+
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
48
|
+
startCluster();
|
|
44
49
|
}
|
package/debug-loader.js
CHANGED
|
@@ -27,14 +27,29 @@ export function createDebug(namespace) {
|
|
|
27
27
|
|
|
28
28
|
const debug_perf_ns = createDebug("plainblog:performance");
|
|
29
29
|
export function debug_perf(message, duration) {
|
|
30
|
+
let duration_rounded = duration.toFixed(2);
|
|
31
|
+
debug_perf_ns(message.padEnd(35) + " took " + duration_rounded + "ms");
|
|
30
32
|
const threshold = 25;
|
|
31
33
|
if (duration > threshold) {
|
|
32
34
|
debug_perf_ns(message + ` WARNING! threshold > ${threshold}ms reached!`);
|
|
33
35
|
}
|
|
34
36
|
}
|
|
35
37
|
|
|
38
|
+
export async function measure_perf(message, fn) {
|
|
39
|
+
const start = performance.now();
|
|
40
|
+
try {
|
|
41
|
+
return await fn();
|
|
42
|
+
} finally {
|
|
43
|
+
const duration = performance.now() - start;
|
|
44
|
+
debug_perf(message, duration);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
36
48
|
export function debug_perf1(message, duration, threshold) {
|
|
37
49
|
if (duration > threshold) {
|
|
38
|
-
debug_perf_ns(
|
|
50
|
+
debug_perf_ns(
|
|
51
|
+
message +
|
|
52
|
+
` took ${duration}ms. WARNING! threshold > ${threshold}ms reached!`,
|
|
53
|
+
);
|
|
39
54
|
}
|
|
40
55
|
}
|
package/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import FileAdapter from "./model/FileAdapter.js";
|
|
|
4
4
|
import PostgresAdapter from "./model/PostgresAdapter.js";
|
|
5
5
|
import SequelizeAdapter from "./model/SequelizeAdapter.js";
|
|
6
6
|
import SqliteAdapter from "./model/SqliteAdapter.js";
|
|
7
|
+
import { startCluster } from "./cluster-server.js";
|
|
7
8
|
|
|
8
9
|
export {
|
|
9
10
|
Blog,
|
|
@@ -12,5 +13,6 @@ export {
|
|
|
12
13
|
SequelizeAdapter,
|
|
13
14
|
FileAdapter,
|
|
14
15
|
SqliteAdapter,
|
|
16
|
+
startCluster,
|
|
15
17
|
};
|
|
16
18
|
export default Blog;
|
package/model/DataModel.js
CHANGED
|
@@ -56,7 +56,7 @@ export default class DataModel {
|
|
|
56
56
|
return this.storage.getAllArticles(order);
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
findAll(start, end, limit) {
|
|
60
60
|
debug("find all %d %d %d", start, end, limit);
|
|
61
61
|
if (start == null && end == null && limit == null)
|
|
62
62
|
return this.getAllArticles();
|
|
@@ -64,15 +64,17 @@ export default class DataModel {
|
|
|
64
64
|
let articles = this.storage.getRange(start, end, limit);
|
|
65
65
|
|
|
66
66
|
if (articles.length < limit && this.db) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
this.storage.
|
|
71
|
-
|
|
72
|
-
|
|
67
|
+
return (async () => {
|
|
68
|
+
const dbArticles = await this.db.findAll(limit, 0, start, end);
|
|
69
|
+
for (const data of dbArticles) {
|
|
70
|
+
if (!this.storage.contains(data.id)) {
|
|
71
|
+
this.storage.insert(
|
|
72
|
+
new Article(data.id, data.title, data.content, data.createdAt),
|
|
73
|
+
);
|
|
74
|
+
}
|
|
73
75
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
+
return this.storage.getRange(start, end, limit);
|
|
77
|
+
})();
|
|
76
78
|
}
|
|
77
79
|
return articles;
|
|
78
80
|
}
|
package/model/SqliteAdapter.js
CHANGED
|
@@ -13,6 +13,7 @@ export default class SqliteAdapter {
|
|
|
13
13
|
this.db = null;
|
|
14
14
|
this.initPromise = null;
|
|
15
15
|
this.ready = false;
|
|
16
|
+
this.stmts = {};
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
async initialize() {
|
|
@@ -31,7 +32,7 @@ export default class SqliteAdapter {
|
|
|
31
32
|
console.error(
|
|
32
33
|
"The 'better-sqlite3' package is not installed. Please install it by running: npm install better-sqlite3",
|
|
33
34
|
);
|
|
34
|
-
|
|
35
|
+
process.exit(1);
|
|
35
36
|
} else {
|
|
36
37
|
throw error;
|
|
37
38
|
}
|
|
@@ -86,16 +87,23 @@ export default class SqliteAdapter {
|
|
|
86
87
|
return this.dbname;
|
|
87
88
|
}
|
|
88
89
|
|
|
90
|
+
getStmt(sql) {
|
|
91
|
+
if (!this.stmts[sql]) {
|
|
92
|
+
this.stmts[sql] = this.db.prepare(sql);
|
|
93
|
+
}
|
|
94
|
+
return this.stmts[sql];
|
|
95
|
+
}
|
|
96
|
+
|
|
89
97
|
async getBlogTitle() {
|
|
90
98
|
debug("getBlogTitle dbname: %s", this.dbname);
|
|
91
99
|
if (!this.db) await this.initialize();
|
|
92
|
-
const row = this.
|
|
100
|
+
const row = this.getStmt("SELECT title FROM BlogInfos LIMIT 1").get();
|
|
93
101
|
return row ? row.title : "Blog";
|
|
94
102
|
}
|
|
95
103
|
|
|
96
104
|
async updateBlogTitle(newTitle) {
|
|
97
105
|
if (!this.db) await this.initialize();
|
|
98
|
-
this.
|
|
106
|
+
this.getStmt("UPDATE BlogInfos SET title = ?").run(newTitle);
|
|
99
107
|
}
|
|
100
108
|
|
|
101
109
|
async save(newArticle) {
|
|
@@ -106,7 +114,7 @@ export default class SqliteAdapter {
|
|
|
106
114
|
: new Date().toISOString();
|
|
107
115
|
const updatedAt = new Date().toISOString();
|
|
108
116
|
|
|
109
|
-
const stmt = this.
|
|
117
|
+
const stmt = this.getStmt(
|
|
110
118
|
`INSERT INTO Articles (id, title, content, createdAt, updatedAt)
|
|
111
119
|
VALUES (@id, @title, @content, @createdAt, @updatedAt)`,
|
|
112
120
|
);
|
|
@@ -150,13 +158,13 @@ export default class SqliteAdapter {
|
|
|
150
158
|
|
|
151
159
|
if (sets.length > 1) {
|
|
152
160
|
const sql = `UPDATE Articles SET ${sets.join(", ")} WHERE id = @id`;
|
|
153
|
-
this.
|
|
161
|
+
this.getStmt(sql).run(params);
|
|
154
162
|
}
|
|
155
163
|
}
|
|
156
164
|
|
|
157
165
|
async remove(id) {
|
|
158
166
|
if (!this.db) await this.initialize();
|
|
159
|
-
this.
|
|
167
|
+
this.getStmt("DELETE FROM Articles WHERE id = ?").run(id);
|
|
160
168
|
}
|
|
161
169
|
|
|
162
170
|
async findAll(
|
|
@@ -195,7 +203,7 @@ export default class SqliteAdapter {
|
|
|
195
203
|
params.push(offset);
|
|
196
204
|
}
|
|
197
205
|
|
|
198
|
-
const rows = this.
|
|
206
|
+
const rows = this.getStmt(query).all(...params);
|
|
199
207
|
return rows.map((row) => ({
|
|
200
208
|
...row,
|
|
201
209
|
createdAt: new Date(row.createdAt),
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { parentPort, workerData } from "worker_threads";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
|
|
4
|
+
export async function run(
|
|
5
|
+
baseContent,
|
|
6
|
+
extraContent = "",
|
|
7
|
+
inlineContent = "",
|
|
8
|
+
options = {},
|
|
9
|
+
) {
|
|
10
|
+
const { forceFallback = false, disableFallback = false } = options;
|
|
11
|
+
let finalStyles = baseContent + "\n" + extraContent + "\n" + inlineContent;
|
|
12
|
+
|
|
13
|
+
// postcss, autoprefixer, cssnano installed?
|
|
14
|
+
if (!forceFallback) {
|
|
15
|
+
try {
|
|
16
|
+
const { default: postcss } = await import("postcss");
|
|
17
|
+
const { default: autoprefixer } = await import("autoprefixer");
|
|
18
|
+
const { default: cssnano } = await import("cssnano");
|
|
19
|
+
|
|
20
|
+
const result = await postcss([autoprefixer, cssnano]).process(
|
|
21
|
+
finalStyles,
|
|
22
|
+
{
|
|
23
|
+
from: undefined,
|
|
24
|
+
},
|
|
25
|
+
);
|
|
26
|
+
return result.css;
|
|
27
|
+
} catch (e) {
|
|
28
|
+
// no --> print "please install postcss, autoprefixer, cssnano"
|
|
29
|
+
console.log(
|
|
30
|
+
"please run 'npm install postcss autoprefixer cssnano' for full feature support.",
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// fallback-minify
|
|
36
|
+
if (!disableFallback) {
|
|
37
|
+
finalStyles = finalStyles
|
|
38
|
+
.replace(/\/\*[\s\S]*?\*\//g, "")
|
|
39
|
+
.replace(/\s*([:;{}])\s*/g, "$1")
|
|
40
|
+
.replace(/}\s+/g, "}")
|
|
41
|
+
.replace(/;}/g, "}")
|
|
42
|
+
.trim();
|
|
43
|
+
}
|
|
44
|
+
return finalStyles;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// compile
|
|
48
|
+
if (parentPort && workerData && workerData.outputPath) {
|
|
49
|
+
const {
|
|
50
|
+
baseContent,
|
|
51
|
+
extraContent,
|
|
52
|
+
inlineContent,
|
|
53
|
+
outputPath,
|
|
54
|
+
forceFallback,
|
|
55
|
+
disableFallback,
|
|
56
|
+
} = workerData;
|
|
57
|
+
|
|
58
|
+
(async () => {
|
|
59
|
+
try {
|
|
60
|
+
const finalStyles = await run(baseContent, extraContent, inlineContent, {
|
|
61
|
+
forceFallback,
|
|
62
|
+
disableFallback,
|
|
63
|
+
});
|
|
64
|
+
// generate 'public/styles.min.css'
|
|
65
|
+
fs.writeFileSync(outputPath, finalStyles);
|
|
66
|
+
|
|
67
|
+
parentPort.postMessage("success");
|
|
68
|
+
} catch (error) {
|
|
69
|
+
parentPort.postMessage({ error: error.message });
|
|
70
|
+
}
|
|
71
|
+
})();
|
|
72
|
+
}
|