@synchjs/ewb 1.0.0 → 1.0.1
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/dist/Components/ServeMemoryStore.d.ts +10 -0
- package/dist/Components/ServeMemoryStore.d.ts.map +1 -1
- package/dist/Components/ServeMemoryStore.js +143 -25
- package/dist/Components/Server.d.ts +4 -0
- package/dist/Components/Server.d.ts.map +1 -1
- package/dist/Components/Server.js +33 -0
- package/dist/Decorations/Serve.d.ts +1 -0
- package/dist/Decorations/Serve.d.ts.map +1 -1
- package/dist/Decorations/Serve.js +3 -0
- package/package.json +1 -1
|
@@ -3,12 +3,22 @@ export declare class ServeMemoryStore {
|
|
|
3
3
|
private static _instance;
|
|
4
4
|
private _assets;
|
|
5
5
|
private _htmlCache;
|
|
6
|
+
private _cacheDir;
|
|
7
|
+
private _devMode;
|
|
8
|
+
private _watchers;
|
|
9
|
+
private _listeners;
|
|
6
10
|
private constructor();
|
|
7
11
|
static get instance(): ServeMemoryStore;
|
|
12
|
+
setDevMode(enabled: boolean): void;
|
|
13
|
+
onRebuild(listener: () => void): void;
|
|
14
|
+
private notify;
|
|
8
15
|
getAsset(path: string): {
|
|
9
16
|
type: string;
|
|
10
17
|
content: Uint8Array;
|
|
11
18
|
};
|
|
19
|
+
private getCacheKey;
|
|
20
|
+
private loadFromDisk;
|
|
21
|
+
private setupWatcher;
|
|
12
22
|
buildAndCache(htmlPath: string, options?: TailwindOptions): Promise<string>;
|
|
13
23
|
}
|
|
14
24
|
//# sourceMappingURL=ServeMemoryStore.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ServeMemoryStore.d.ts","sourceRoot":"","sources":["../../src/Components/ServeMemoryStore.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"ServeMemoryStore.d.ts","sourceRoot":"","sources":["../../src/Components/ServeMemoryStore.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAI/D,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAC,SAAS,CAAmB;IAC3C,OAAO,CAAC,OAAO,CACH;IACZ,OAAO,CAAC,UAAU,CAAkC;IACpD,OAAO,CAAC,SAAS,CAA0C;IAC3D,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,SAAS,CAAwC;IACzD,OAAO,CAAC,UAAU,CAAsB;IAExC,OAAO;IAUP,WAAkB,QAAQ,IAAI,gBAAgB,CAK7C;IAEM,UAAU,CAAC,OAAO,EAAE,OAAO;IAI3B,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI;IAIrC,OAAO,CAAC,MAAM;IAIP,QAAQ,CAAC,IAAI,EAAE,MAAM;cArCS,MAAM;iBAAW,UAAU;;IAyChE,OAAO,CAAC,WAAW;YAQL,YAAY;IAuB1B,OAAO,CAAC,YAAY;IA4CP,aAAa,CACxB,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,eAAgD,GACxD,OAAO,CAAC,MAAM,CAAC;CAqHnB"}
|
|
@@ -1,27 +1,117 @@
|
|
|
1
1
|
import tailwindPlugin from "bun-plugin-tailwind";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
2
4
|
export class ServeMemoryStore {
|
|
3
5
|
static _instance;
|
|
4
6
|
_assets = new Map();
|
|
5
7
|
_htmlCache = new Map();
|
|
6
|
-
|
|
8
|
+
_cacheDir = path.join(process.cwd(), ".ebw-cache");
|
|
9
|
+
_devMode = false;
|
|
10
|
+
_watchers = new Map();
|
|
11
|
+
_listeners = [];
|
|
12
|
+
constructor() {
|
|
13
|
+
if (!fs.existsSync(this._cacheDir)) {
|
|
14
|
+
try {
|
|
15
|
+
fs.mkdirSync(this._cacheDir, { recursive: true });
|
|
16
|
+
}
|
|
17
|
+
catch (e) {
|
|
18
|
+
// Fallback if we can't create directory
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
7
22
|
static get instance() {
|
|
8
23
|
if (!ServeMemoryStore._instance) {
|
|
9
24
|
ServeMemoryStore._instance = new ServeMemoryStore();
|
|
10
25
|
}
|
|
11
26
|
return ServeMemoryStore._instance;
|
|
12
27
|
}
|
|
28
|
+
setDevMode(enabled) {
|
|
29
|
+
this._devMode = enabled;
|
|
30
|
+
}
|
|
31
|
+
onRebuild(listener) {
|
|
32
|
+
this._listeners.push(listener);
|
|
33
|
+
}
|
|
34
|
+
notify() {
|
|
35
|
+
this._listeners.forEach((l) => l());
|
|
36
|
+
}
|
|
13
37
|
getAsset(path) {
|
|
14
|
-
// Remove leading slash for matching if stored without it, or normalize.
|
|
15
|
-
// We will store with leading slash.
|
|
16
38
|
return this._assets.get(path);
|
|
17
39
|
}
|
|
18
|
-
|
|
19
|
-
|
|
40
|
+
getCacheKey(htmlPath, options) {
|
|
41
|
+
return (htmlPath +
|
|
20
42
|
(options.enable ? ":tw" : "") +
|
|
21
|
-
(options.plugins ? ":" + options.plugins.length : "");
|
|
43
|
+
(options.plugins ? ":" + options.plugins.length : ""));
|
|
44
|
+
}
|
|
45
|
+
async loadFromDisk(cacheKey) {
|
|
46
|
+
const hash = Bun.hash(cacheKey).toString(16);
|
|
47
|
+
const cacheFile = path.join(this._cacheDir, `${hash}.json`);
|
|
48
|
+
if (fs.existsSync(cacheFile)) {
|
|
49
|
+
try {
|
|
50
|
+
const data = JSON.parse(fs.readFileSync(cacheFile, "utf-8"));
|
|
51
|
+
this._htmlCache.set(cacheKey, data.html);
|
|
52
|
+
for (const [webPath, asset] of Object.entries(data.assets)) {
|
|
53
|
+
this._assets.set(webPath, {
|
|
54
|
+
type: asset.type,
|
|
55
|
+
content: Buffer.from(asset.content, "base64"),
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
return data.html;
|
|
59
|
+
}
|
|
60
|
+
catch (e) {
|
|
61
|
+
console.error("Error loading cache from disk:", e);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
setupWatcher(htmlPath, options) {
|
|
67
|
+
const cacheKey = this.getCacheKey(htmlPath, options);
|
|
68
|
+
if (this._watchers.has(cacheKey))
|
|
69
|
+
return;
|
|
70
|
+
const absolutePath = path.resolve(process.cwd(), htmlPath);
|
|
71
|
+
const watchDir = path.dirname(absolutePath);
|
|
72
|
+
const watcher = fs.watch(watchDir, { recursive: true }, async (event, filename) => {
|
|
73
|
+
if (!filename)
|
|
74
|
+
return;
|
|
75
|
+
// Ignore common noise
|
|
76
|
+
if (filename.includes("node_modules") ||
|
|
77
|
+
filename.includes(".git") ||
|
|
78
|
+
filename.includes(".ebw-cache"))
|
|
79
|
+
return;
|
|
80
|
+
console.log(`[HMR] Change detected in ${filename}. Rebuilding...`);
|
|
81
|
+
// Clear cache for this entry
|
|
82
|
+
this._htmlCache.delete(cacheKey);
|
|
83
|
+
// Clear disk cache by deleting file
|
|
84
|
+
const hash = Bun.hash(cacheKey).toString(16);
|
|
85
|
+
const cacheFile = path.join(this._cacheDir, `${hash}.json`);
|
|
86
|
+
if (fs.existsSync(cacheFile)) {
|
|
87
|
+
fs.unlinkSync(cacheFile);
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
await this.buildAndCache(htmlPath, options);
|
|
91
|
+
this.notify();
|
|
92
|
+
}
|
|
93
|
+
catch (e) {
|
|
94
|
+
console.error("[HMR] Rebuild failed:", e);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
this._watchers.set(cacheKey, watcher);
|
|
98
|
+
}
|
|
99
|
+
async buildAndCache(htmlPath, options = { enable: false, plugins: [] }) {
|
|
100
|
+
const cacheKey = this.getCacheKey(htmlPath, options);
|
|
101
|
+
// 1. Check Memory Cache
|
|
22
102
|
if (this._htmlCache.has(cacheKey)) {
|
|
23
103
|
return this._htmlCache.get(cacheKey);
|
|
24
104
|
}
|
|
105
|
+
// 2. Check Disk Cache (unless in dev mode and we want to ensure fresh start,
|
|
106
|
+
// but usually disk cache is fine as watcher will invalidate it)
|
|
107
|
+
const diskHtml = await this.loadFromDisk(cacheKey);
|
|
108
|
+
if (diskHtml) {
|
|
109
|
+
if (this._devMode) {
|
|
110
|
+
this.setupWatcher(htmlPath, options);
|
|
111
|
+
}
|
|
112
|
+
return diskHtml;
|
|
113
|
+
}
|
|
114
|
+
// 3. Perform Build
|
|
25
115
|
try {
|
|
26
116
|
const plugins = [...(options.plugins || [])];
|
|
27
117
|
if (options.enable) {
|
|
@@ -30,9 +120,9 @@ export class ServeMemoryStore {
|
|
|
30
120
|
const build = await Bun.build({
|
|
31
121
|
entrypoints: [htmlPath],
|
|
32
122
|
target: "browser",
|
|
33
|
-
minify:
|
|
34
|
-
naming: "[name]-[hash].[ext]",
|
|
35
|
-
publicPath: "/",
|
|
123
|
+
minify: !this._devMode,
|
|
124
|
+
naming: "[name]-[hash].[ext]",
|
|
125
|
+
publicPath: "/",
|
|
36
126
|
plugins: plugins,
|
|
37
127
|
});
|
|
38
128
|
if (!build.success) {
|
|
@@ -40,33 +130,61 @@ export class ServeMemoryStore {
|
|
|
40
130
|
throw new Error("Build failed");
|
|
41
131
|
}
|
|
42
132
|
let htmlContent = "";
|
|
133
|
+
const currentBuildAssets = {};
|
|
43
134
|
for (const output of build.outputs) {
|
|
44
135
|
const content = await output.arrayBuffer();
|
|
45
|
-
|
|
46
|
-
// output.path is the absolute path if we were writing, or the name.
|
|
47
|
-
// With naming option, output.path usually contains the generated name.
|
|
48
|
-
// For artifacts, we need to map the requested URL to this content.
|
|
49
|
-
// output.kind gives us hint.
|
|
50
|
-
// Parse the relative path (URL) from the output.
|
|
51
|
-
// Since we didn't specify outdir, Bun might give us just the name or path relative to cwd.
|
|
52
|
-
// However, with `publicPath: "/"`, the HTML imports will look like `/foo-hash.js`.
|
|
53
|
-
// We need to store keys as `/foo-hash.js`.
|
|
54
|
-
// Let's rely on the fact that `output.path` usually returns what would be written to disk.
|
|
55
|
-
// We need the filename part.
|
|
136
|
+
const uint8 = new Uint8Array(content);
|
|
56
137
|
const filename = output.path.split(/[/\\]/).pop();
|
|
57
138
|
const webPath = "/" + filename;
|
|
58
139
|
if (output.type === "text/html" || filename?.endsWith(".html")) {
|
|
59
|
-
htmlContent = text;
|
|
140
|
+
htmlContent = await output.text();
|
|
60
141
|
}
|
|
61
142
|
else {
|
|
62
|
-
|
|
63
|
-
this._assets.set(webPath, {
|
|
143
|
+
const asset = {
|
|
64
144
|
type: output.type,
|
|
65
|
-
content:
|
|
66
|
-
}
|
|
145
|
+
content: uint8,
|
|
146
|
+
};
|
|
147
|
+
this._assets.set(webPath, asset);
|
|
148
|
+
currentBuildAssets[webPath] = {
|
|
149
|
+
type: output.type,
|
|
150
|
+
content: Buffer.from(uint8).toString("base64"),
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// Inject HMR/Reload script in dev mode
|
|
155
|
+
if (this._devMode) {
|
|
156
|
+
const reloadScript = `
|
|
157
|
+
<script id="ebw-hmr-script">
|
|
158
|
+
(function() {
|
|
159
|
+
const sse = new EventSource('/ebw-hmr');
|
|
160
|
+
sse.onmessage = (e) => {
|
|
161
|
+
if (e.data === 'reload') {
|
|
162
|
+
console.log('[HMR] Reloading page...');
|
|
163
|
+
location.reload();
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
sse.onerror = () => {
|
|
167
|
+
console.warn('[HMR] Connection lost. Attempting to reconnect...');
|
|
168
|
+
};
|
|
169
|
+
})();
|
|
170
|
+
</script>
|
|
171
|
+
`;
|
|
172
|
+
if (htmlContent.includes("</body>")) {
|
|
173
|
+
htmlContent = htmlContent.replace("</body>", `${reloadScript}</body>`);
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
htmlContent += reloadScript;
|
|
67
177
|
}
|
|
178
|
+
this.setupWatcher(htmlPath, options);
|
|
68
179
|
}
|
|
180
|
+
// 4. Save to Memory and Disk
|
|
69
181
|
this._htmlCache.set(cacheKey, htmlContent);
|
|
182
|
+
const hash = Bun.hash(cacheKey).toString(16);
|
|
183
|
+
const cacheFile = path.join(this._cacheDir, `${hash}.json`);
|
|
184
|
+
fs.writeFileSync(cacheFile, JSON.stringify({
|
|
185
|
+
html: htmlContent,
|
|
186
|
+
assets: currentBuildAssets,
|
|
187
|
+
}));
|
|
70
188
|
return htmlContent;
|
|
71
189
|
}
|
|
72
190
|
catch (error) {
|
|
@@ -18,7 +18,10 @@ export declare class Server {
|
|
|
18
18
|
private _controllerCount;
|
|
19
19
|
private _routeCount;
|
|
20
20
|
private _tailwindEnabled;
|
|
21
|
+
private _devMode;
|
|
22
|
+
private _sseClients;
|
|
21
23
|
constructor(options: ServerOptions);
|
|
24
|
+
private setupHmr;
|
|
22
25
|
private log;
|
|
23
26
|
init(): Promise<void>;
|
|
24
27
|
private printStartupMessage;
|
|
@@ -36,6 +39,7 @@ export interface ServerOptions {
|
|
|
36
39
|
logging?: boolean;
|
|
37
40
|
id: string;
|
|
38
41
|
controllersDir?: string;
|
|
42
|
+
devMode?: boolean;
|
|
39
43
|
corsOptions?: cors.CorsOptions;
|
|
40
44
|
helmetOptions?: HelmetOptions;
|
|
41
45
|
rateLimitOptions?: Partial<RateLimitOptions>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Server.d.ts","sourceRoot":"","sources":["../../src/Components/Server.ts"],"names":[],"mappings":"AAAA,OAAgB,EAEd,KAAK,OAAO,EACZ,KAAK,QAAQ,EACb,KAAK,YAAY,EAClB,MAAM,SAAS,CAAC;AAKjB,OAAe,EAAE,KAAK,aAAa,EAAE,MAAM,QAAQ,CAAC;AACpD,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAkB,EAChB,KAAK,OAAO,IAAI,gBAAgB,EACjC,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"Server.d.ts","sourceRoot":"","sources":["../../src/Components/Server.ts"],"names":[],"mappings":"AAAA,OAAgB,EAEd,KAAK,OAAO,EACZ,KAAK,QAAQ,EACb,KAAK,YAAY,EAClB,MAAM,SAAS,CAAC;AAKjB,OAAe,EAAE,KAAK,aAAa,EAAE,MAAM,QAAQ,CAAC;AACpD,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAkB,EAChB,KAAK,OAAO,IAAI,gBAAgB,EACjC,MAAM,oBAAoB,CAAC;AAwB5B,qBAAa,MAAM;IACjB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAU;IAC/B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;IACzC,SAAgB,GAAG,EAAE,MAAM,CAAC;IAC5B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAU;IACzC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAU;IACzC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAM;IACxC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAM;IAC3B,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAGhC;IACF,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAgC;IAC5D,OAAO,CAAC,eAAe,CAAC,CAAa;IAErC,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,WAAW,CAAkB;gBAEzB,OAAO,EAAE,aAAa;IAuClC,OAAO,CAAC,QAAQ;IAyBhB,OAAO,CAAC,GAAG;IAME,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAqClC,OAAO,CAAC,mBAAmB;IAsCpB,KAAK,IAAI,IAAI;YASN,eAAe;IAkQ7B,OAAO,CAAC,mBAAmB;IAoB3B,OAAO,CAAC,oBAAoB;IAyD5B,OAAO,CAAC,0BAA0B;IAclC,OAAO,CAAC,YAAY;CAmErB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,IAAI,CAAC,WAAW,CAAC;IAC/B,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,gBAAgB,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAC7C,gBAAgB,CAAC,EAAE,MAAM,CACvB,MAAM,EACN,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,KAAK,IAAI,CAC1D,CAAC;IACF,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACtC,SAAS,CAAC,EAAE;QAAE,GAAG,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,GAAG,CAAA;KAAE,CAAC;CAC3C"}
|
|
@@ -13,6 +13,7 @@ import * as jwt from "jsonwebtoken";
|
|
|
13
13
|
import { AUTH_METADATA_KEY, PUBLIC_METADATA_KEY, } from "../Decorations/Authorized";
|
|
14
14
|
import { SECURITY_METADATA_KEY, } from "../Decorations/Security";
|
|
15
15
|
import { TAILWIND_METADATA_KEY, } from "../Decorations/Tailwind";
|
|
16
|
+
import { SERVE_HTML_METADATA_KEY } from "../Decorations/Serve";
|
|
16
17
|
import { ServeMemoryStore } from "./ServeMemoryStore";
|
|
17
18
|
import boxen from "boxen";
|
|
18
19
|
import pc from "picocolors";
|
|
@@ -33,6 +34,8 @@ export class Server {
|
|
|
33
34
|
_controllerCount = 0;
|
|
34
35
|
_routeCount = 0;
|
|
35
36
|
_tailwindEnabled = false;
|
|
37
|
+
_devMode = false;
|
|
38
|
+
_sseClients = [];
|
|
36
39
|
constructor(options) {
|
|
37
40
|
this._port = options.port;
|
|
38
41
|
this._app = express();
|
|
@@ -56,8 +59,31 @@ export class Server {
|
|
|
56
59
|
max: 100,
|
|
57
60
|
}));
|
|
58
61
|
this._app.use(express.json());
|
|
62
|
+
this._devMode = options.devMode ?? process.env.NODE_ENV === "development";
|
|
63
|
+
if (this._devMode) {
|
|
64
|
+
this.setupHmr();
|
|
65
|
+
}
|
|
59
66
|
global.servers.set(this._id, this._app);
|
|
60
67
|
}
|
|
68
|
+
setupHmr() {
|
|
69
|
+
ServeMemoryStore.instance.setDevMode(true);
|
|
70
|
+
ServeMemoryStore.instance.onRebuild(() => {
|
|
71
|
+
this.log(`[${this._id}] Sending reload signal to ${this._sseClients.length} clients`);
|
|
72
|
+
this._sseClients.forEach((res) => {
|
|
73
|
+
res.write("data: reload\n\n");
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
this._app.get("/ebw-hmr", (req, res) => {
|
|
77
|
+
res.setHeader("Content-Type", "text/event-stream");
|
|
78
|
+
res.setHeader("Cache-Control", "no-cache");
|
|
79
|
+
res.setHeader("Connection", "keep-alive");
|
|
80
|
+
res.flushHeaders();
|
|
81
|
+
this._sseClients.push(res);
|
|
82
|
+
req.on("close", () => {
|
|
83
|
+
this._sseClients = this._sseClients.filter((c) => c !== res);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
}
|
|
61
87
|
log(message) {
|
|
62
88
|
if (this._enableLogging) {
|
|
63
89
|
console.log(message);
|
|
@@ -103,6 +129,7 @@ export class Server {
|
|
|
103
129
|
`${pc.bold(pad("Controllers:"))}${this._controllerCount}`,
|
|
104
130
|
`${pc.bold(pad("Routes:"))}${this._routeCount}`,
|
|
105
131
|
`${pc.bold(pad("Tailwind:"))}${this._tailwindEnabled ? pc.blue("Enabled") : pc.dim("Disabled")}`,
|
|
132
|
+
`${pc.bold(pad("HMR:"))}${this._devMode ? pc.cyan("Active") : pc.dim("Inactive")}`,
|
|
106
133
|
];
|
|
107
134
|
if (this._enableSwagger) {
|
|
108
135
|
lines.push(`${pc.bold(pad("Swagger:"))}http://localhost:${this._port}${this._swaggerPath}`);
|
|
@@ -300,6 +327,12 @@ export class Server {
|
|
|
300
327
|
};
|
|
301
328
|
this._app[route.method](fullPath, ...middlewares, handler);
|
|
302
329
|
this._routeCount++;
|
|
330
|
+
// Pre-build if @Serve is used
|
|
331
|
+
const htmlPath = Reflect.getMetadata(SERVE_HTML_METADATA_KEY, controller.target.prototype, route.handlerName);
|
|
332
|
+
if (htmlPath) {
|
|
333
|
+
this.log(`[${this._id}] Pre-building HTML: ${htmlPath}`);
|
|
334
|
+
await ServeMemoryStore.instance.buildAndCache(htmlPath, tailwindOptions);
|
|
335
|
+
}
|
|
303
336
|
}
|
|
304
337
|
}
|
|
305
338
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Serve.d.ts","sourceRoot":"","sources":["../../src/Decorations/Serve.ts"],"names":[],"mappings":"AAIA,wBAAgB,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe,
|
|
1
|
+
{"version":3,"file":"Serve.d.ts","sourceRoot":"","sources":["../../src/Decorations/Serve.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,uBAAuB,eAAe,CAAC;AAEpD,wBAAgB,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe,CAmDvD"}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { ServeMemoryStore } from "../Components/ServeMemoryStore";
|
|
2
2
|
import { TAILWIND_METADATA_KEY } from "./Tailwind";
|
|
3
|
+
export const SERVE_HTML_METADATA_KEY = "serve:html";
|
|
3
4
|
export function Serve(htmlPath) {
|
|
4
5
|
return function (target, propertyKey, descriptor) {
|
|
6
|
+
// Store the HTML path in metadata for pre-building
|
|
7
|
+
Reflect.defineMetadata(SERVE_HTML_METADATA_KEY, htmlPath, target, propertyKey);
|
|
5
8
|
const originalMethod = descriptor.value;
|
|
6
9
|
descriptor.value = async function (req, res, next) {
|
|
7
10
|
try {
|