diff --git a/bun.lockb b/bun.lockb index 3e59b1f..6fd1466 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 491bb0c..d26016d 100644 --- a/package.json +++ b/package.json @@ -4,17 +4,18 @@ "type": "module", "main": "src/index.ts", "devDependencies": { - "@types/bun": "latest" + "@types/bun": "^1.1.8" }, "peerDependencies": { "typescript": "^5.0.0" }, "dependencies": { "@types/mime-types": "^2.1.4", - "esbuild": "^0.23.0", - "marked": "^13.0.3", + "esbuild": "^0.23.1", + "marked": "^14.1.0", "mime-types": "^2.1.35", "postcss": "^8.4.41", - "preact": "^10.23.1" + "preact": "^10.23.2", + "sharp": "^0.33.5" } } \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index cb8e449..1a810e8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,11 +4,12 @@ export abstract class Plugin { renameTo?: string; abstract longLasting: boolean; abstract build?(): void; + isBinary = false; abstract rewriteFile( - file: string, + file: string | Buffer, filePath: string - ): Promise; + ): Promise; } import * as fs from "fs"; @@ -35,13 +36,14 @@ export default class SSSG { } async build() { - for(const plugin of this.plugins) { - if(plugin.build) { + console.time("Building.."); + for (const plugin of this.plugins) { + if (plugin.build) { plugin.build(); } } try { - fs.rmSync(this.outputFolder, {recursive: true}); + fs.rmSync(this.outputFolder, { recursive: true }); } catch {} const sourceFiles = fs.readdirSync(this.inputFolder, { @@ -66,7 +68,6 @@ export default class SSSG { z.rewriteTriggers.includes(type) ); - if (availablePlugins.length == 0) { const oldPath = path.join(file.parentPath, file.name); fs.cpSync( @@ -82,12 +83,20 @@ export default class SSSG { file.parentPath, shortname + "." + - (plugin.rewriteTriggers.includes("*") ? type : plugin.renameTo) + (plugin.rewriteTriggers.includes("*") || plugin.renameTo == "keep" + ? type + : plugin.renameTo) ) .replace(this.inputFolder, this.outputFolder); - let data = fs.readFileSync(oldPath).toString("utf8"); + let data: string | Buffer = fs.readFileSync(oldPath); + + if (!plugin.isBinary) { + data = data.toString("utf8"); + } for await (const globalPlugin of globalPlugins) { + if (!globalPlugin.isBinary && plugin.isBinary) continue; + const rewritten = await globalPlugin.rewriteFile(data, oldPath); if (!rewritten) continue; data = rewritten; @@ -98,8 +107,14 @@ export default class SSSG { if (!rewrite) continue; fs.mkdirSync(path.dirname(newPath), { recursive: true }); - fs.writeFileSync(newPath, rewrite); + fs.writeFileSync( + newPath, + plugin.isBinary + ? new Uint8Array(rewrite as Buffer) + : (rewrite as string) + ); } } + console.timeEnd("Building.."); } } diff --git a/src/plugins/compile-time-js.ts b/src/plugins/compile-time-js.ts index 0ffcbe3..962e3e6 100644 --- a/src/plugins/compile-time-js.ts +++ b/src/plugins/compile-time-js.ts @@ -5,7 +5,7 @@ export default class CompileTimeJS extends Plugin { rewriteTriggers = ["html", "*"] longLasting = false; build = undefined; - + async rewriteFile(file: string, filePath: string): Promise { let input = file; const regex = /{&(.+)&}/gms; diff --git a/src/plugins/dev.ts b/src/plugins/dev.ts index f027aba..d8eef98 100644 --- a/src/plugins/dev.ts +++ b/src/plugins/dev.ts @@ -3,7 +3,7 @@ import SSSG, { Plugin } from ".."; import * as fs from "fs"; import mime from "mime-types"; -const script = `let reconnectTimeout;function connect(){console.log("[--dev] connecting to dev server");let ws=new WebSocket("ws://localhost:8080");ws.addEventListener("message",message=>{if(message.data=="refresh"){location.reload()}});ws.addEventListener("open",()=>{console.log("[--dev] connected")});ws.addEventListener("close",()=>{console.log("[--dev] socket closed, restarting in 1s");clearTimeout(reconnectTimeout);reconnectTimeout=setTimeout(()=>{connect()},1e3)})}window.addEventListener("load",()=>connect());`; +const script = `let reconnectTimeout,waitingForReconnect=!1;function connect(){console.log("[dev] connecting to dev server");var e=new WebSocket("ws://localhost:8080");e.addEventListener("message",e=>{"refresh"==e.data&&location.reload()}),e.addEventListener("open",()=>{console.log("[dev] connected"),waitingForReconnect&&location.reload()}),e.addEventListener("close",()=>{console.log("[dev] socket closed, restarting in 1s"),clearTimeout(reconnectTimeout),reconnectTimeout=setTimeout(()=>{connect(),waitingForReconnect=!0},1e3)})}window.addEventListener("load",()=>connect());`; export default class DevPlugin extends Plugin { build: undefined; @@ -14,10 +14,9 @@ export default class DevPlugin extends Plugin { longLasting = true; server!: Server; allConnections: ServerWebSocket[] = []; - + constructor(sssg: SSSG) { super(); - if (!process.argv.includes("--dev")) return; fs.watch( sssg.inputFolder, diff --git a/src/plugins/image-optimization.ts b/src/plugins/image-optimization.ts new file mode 100644 index 0000000..d4fc641 --- /dev/null +++ b/src/plugins/image-optimization.ts @@ -0,0 +1,53 @@ +import { Plugin } from ".."; +import sharp from "sharp"; + +export default class ImageOptimization extends Plugin { + build: undefined; + + name = "image-optimization"; + rewriteTriggers = ["png", "gif", "jpg", "jpeg", "webp", "avif"]; + renameTo = "keep"; + isBinary = true; + longLasting = false; + + logging = true; + + constructor(logging = true) { + super(); + this.logging = logging; + } + + async rewriteFile(file: Buffer, filePath: string) { + const nonOptimized = file; + + const type = filePath.split(".").at(-1); + let data: Buffer = file; + if (type == "webp") { + data = await sharp(file).webp({ lossless: true }).toBuffer(); + } + if (type == "avif") { + data = await sharp(file).avif({ lossless: true }).toBuffer(); + } + if (type == "jpg" || type == "jpeg") { + data = await sharp(file).jpeg({ quality: 100 }).toBuffer(); + } + if (type == "png") { + data = await sharp(file).png({ quality: 100 }).toBuffer(); + } + if (type == "gif") { + // not heavy optimizations, not a fan tbf, but whatever :shrug: + data = await sharp(file, { animated: true }) + .gif({ colors: 16 }) + .toBuffer(); + } + + if(nonOptimized.byteLength < data.byteLength) { + if(this.logging) { + console.log("❌ " + filePath + "("+nonOptimized.byteLength+"B) optimized was " + data.byteLength + "B. It has been skipped."); + } + data = nonOptimized; + } + + return data; + } +} diff --git a/src/plugins/variables.ts b/src/plugins/variables.ts index f3f8334..bc940da 100644 --- a/src/plugins/variables.ts +++ b/src/plugins/variables.ts @@ -1,7 +1,6 @@ import { Plugin } from ".."; export default class Variables extends Plugin { - name = "variables"; rewriteTriggers = ["html", "*"]; renameTo = undefined;