hadars 0.1.15 → 0.1.17

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/cli.js CHANGED
@@ -507,6 +507,9 @@ var buildCompilerConfig = (entry, opts, includeHotPlugin) => {
507
507
  clean: false
508
508
  },
509
509
  mode: opts.mode,
510
+ // Persist transformed modules to disk — subsequent starts only recompile
511
+ // changed files, making repeat dev starts significantly faster.
512
+ cache: true,
510
513
  externals,
511
514
  ...optimization !== void 0 ? { optimization } : {},
512
515
  plugins: [
@@ -794,6 +797,14 @@ async function processHtmlTemplate(templatePath) {
794
797
  }
795
798
  if (matches.length === 0)
796
799
  return templatePath;
800
+ await ensureHadarsTmpDir();
801
+ const sourceHash = crypto.createHash("md5").update(html).digest("hex").slice(0, 8);
802
+ const cachedPath = pathMod3.join(HADARS_TMP_DIR, `template-${sourceHash}.html`);
803
+ try {
804
+ await fs.access(cachedPath);
805
+ return cachedPath;
806
+ } catch {
807
+ }
797
808
  const { default: postcss } = await import("postcss");
798
809
  let plugins = [];
799
810
  try {
@@ -805,15 +816,14 @@ async function processHtmlTemplate(templatePath) {
805
816
  let processedHtml = html;
806
817
  for (const { full, attrs, css } of matches) {
807
818
  try {
808
- const result = await postcss(plugins).process(css, { from: void 0 });
819
+ const result = await postcss(plugins).process(css, { from: templatePath });
809
820
  processedHtml = processedHtml.replace(full, `<style${attrs}>${result.css}</style>`);
810
821
  } catch (err) {
811
822
  console.warn("[hadars] PostCSS error processing <style> block in HTML template:", err);
812
823
  }
813
824
  }
814
- const tmpPath = pathMod3.join(os.tmpdir(), `hadars-template-${Date.now()}.html`);
815
- await fs.writeFile(tmpPath, processedHtml);
816
- return tmpPath;
825
+ await fs.writeFile(cachedPath, processedHtml);
826
+ return cachedPath;
817
827
  }
818
828
  var HEAD_MARKER = '<meta name="HADARS_HEAD">';
819
829
  var BODY_MARKER = '<meta name="HADARS_BODY">';
@@ -1072,6 +1082,8 @@ var __dirname2 = process.cwd();
1072
1082
  var getSuffix = (mode) => mode === "development" ? `?v=${Date.now()}` : "";
1073
1083
  var HadarsFolder = "./.hadars";
1074
1084
  var StaticPath = `${HadarsFolder}/static`;
1085
+ var HADARS_TMP_DIR = pathMod3.join(os.tmpdir(), "hadars");
1086
+ var ensureHadarsTmpDir = () => fs.mkdir(HADARS_TMP_DIR, { recursive: true });
1075
1087
  var validateOptions = (options) => {
1076
1088
  if (!options.entry) {
1077
1089
  throw new Error("Entry file is required");
@@ -1127,7 +1139,8 @@ var dev = async (options) => {
1127
1139
  console.error("Failed to read client script from package dist, falling back to src", err);
1128
1140
  throw err;
1129
1141
  }
1130
- const tmpFilePath = pathMod3.join(os.tmpdir(), `hadars-client-${Date.now()}.tsx`);
1142
+ await ensureHadarsTmpDir();
1143
+ const tmpFilePath = pathMod3.join(HADARS_TMP_DIR, `client-${Date.now()}.tsx`);
1131
1144
  await fs.writeFile(tmpFilePath, clientScript);
1132
1145
  let ssrBuildId = crypto.randomBytes(4).toString("hex");
1133
1146
  const resolvedHtmlTemplate = options.htmlTemplate ? await processHtmlTemplate(pathMod3.resolve(__dirname2, options.htmlTemplate)) : void 0;
@@ -1234,29 +1247,31 @@ var dev = async (options) => {
1234
1247
  stdoutReader = null;
1235
1248
  }
1236
1249
  })();
1237
- await Promise.all([clientBuildDone, ssrBuildDone]);
1238
- if (stdoutReader) {
1239
- const reader = stdoutReader;
1240
- (async () => {
1241
- try {
1242
- while (true) {
1243
- const { done, value } = await reader.read();
1244
- if (done)
1245
- break;
1246
- const chunk = decoder.decode(value, { stream: true });
1247
- try {
1248
- process.stdout.write(chunk);
1249
- } catch (e) {
1250
- }
1251
- if (chunk.includes(rebuildMarker)) {
1252
- ssrBuildId = crypto.randomBytes(4).toString("hex");
1253
- console.log("[hadars] SSR bundle updated, build id:", ssrBuildId);
1250
+ const readyPromise = Promise.all([clientBuildDone, ssrBuildDone]);
1251
+ readyPromise.then(() => {
1252
+ if (stdoutReader) {
1253
+ const reader = stdoutReader;
1254
+ (async () => {
1255
+ try {
1256
+ while (true) {
1257
+ const { done, value } = await reader.read();
1258
+ if (done)
1259
+ break;
1260
+ const chunk = decoder.decode(value, { stream: true });
1261
+ try {
1262
+ process.stdout.write(chunk);
1263
+ } catch (e) {
1264
+ }
1265
+ if (chunk.includes(rebuildMarker)) {
1266
+ ssrBuildId = crypto.randomBytes(4).toString("hex");
1267
+ console.log("[hadars] SSR bundle updated, build id:", ssrBuildId);
1268
+ }
1254
1269
  }
1270
+ } catch (e) {
1255
1271
  }
1256
- } catch (e) {
1257
- }
1258
- })();
1259
- }
1272
+ })();
1273
+ }
1274
+ });
1260
1275
  (async () => {
1261
1276
  try {
1262
1277
  const r = stderrWebStream.getReader();
@@ -1273,9 +1288,10 @@ var dev = async (options) => {
1273
1288
  }
1274
1289
  })();
1275
1290
  const getPrecontentHtml = makePrecontentHtmlGetter(
1276
- fs.readFile(pathMod3.join(__dirname2, StaticPath, "out.html"), "utf-8")
1291
+ readyPromise.then(() => fs.readFile(pathMod3.join(__dirname2, StaticPath, "out.html"), "utf-8"))
1277
1292
  );
1278
1293
  await serve(port, async (req, ctx) => {
1294
+ await readyPromise;
1279
1295
  const request = parseRequest(req);
1280
1296
  if (handler) {
1281
1297
  const res = await handler(request);
@@ -1338,7 +1354,8 @@ var build = async (options) => {
1338
1354
  const srcClientPath = pathMod3.resolve(packageDir2, "utils", "clientScript.tsx");
1339
1355
  clientScript = (await fs.readFile(srcClientPath, "utf-8")).replace("$_MOD_PATH$", entry + `?v=${Date.now()}`);
1340
1356
  }
1341
- const tmpFilePath = pathMod3.join(os.tmpdir(), `hadars-client-${Date.now()}.tsx`);
1357
+ await ensureHadarsTmpDir();
1358
+ const tmpFilePath = pathMod3.join(HADARS_TMP_DIR, `client-${Date.now()}.tsx`);
1342
1359
  await fs.writeFile(tmpFilePath, clientScript);
1343
1360
  const resolvedHtmlTemplate = options.htmlTemplate ? await processHtmlTemplate(pathMod3.resolve(__dirname2, options.htmlTemplate)) : void 0;
1344
1361
  console.log("Building client and server bundles in parallel...");
package/dist/ssr-watch.js CHANGED
@@ -233,6 +233,9 @@ var buildCompilerConfig = (entry2, opts, includeHotPlugin) => {
233
233
  clean: false
234
234
  },
235
235
  mode: opts.mode,
236
+ // Persist transformed modules to disk — subsequent starts only recompile
237
+ // changed files, making repeat dev starts significantly faster.
238
+ cache: true,
236
239
  externals,
237
240
  ...optimization !== void 0 ? { optimization } : {},
238
241
  plugins: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hadars",
3
- "version": "0.1.15",
3
+ "version": "0.1.17",
4
4
  "description": "Minimal SSR framework for React — rspack, HMR, TypeScript, Bun/Node/Deno",
5
5
  "module": "./dist/index.js",
6
6
  "type": "module",
package/src/build.ts CHANGED
@@ -36,6 +36,16 @@ async function processHtmlTemplate(templatePath: string): Promise<string> {
36
36
  }
37
37
  if (matches.length === 0) return templatePath;
38
38
 
39
+ await ensureHadarsTmpDir();
40
+
41
+ // Cache by content hash — same template content → skip Tailwind re-scan on restart.
42
+ const sourceHash = crypto.createHash('md5').update(html).digest('hex').slice(0, 8);
43
+ const cachedPath = pathMod.join(HADARS_TMP_DIR, `template-${sourceHash}.html`);
44
+ try {
45
+ await fs.access(cachedPath);
46
+ return cachedPath; // cache hit
47
+ } catch { /* cache miss — process below */ }
48
+
39
49
  const { default: postcss } = await import('postcss');
40
50
  let plugins: any[] = [];
41
51
  try {
@@ -49,16 +59,15 @@ async function processHtmlTemplate(templatePath: string): Promise<string> {
49
59
  let processedHtml = html;
50
60
  for (const { full, attrs, css } of matches) {
51
61
  try {
52
- const result = await postcss(plugins).process(css, { from: undefined });
62
+ const result = await postcss(plugins).process(css, { from: templatePath });
53
63
  processedHtml = processedHtml.replace(full, `<style${attrs}>${result.css}</style>`);
54
64
  } catch (err) {
55
65
  console.warn('[hadars] PostCSS error processing <style> block in HTML template:', err);
56
66
  }
57
67
  }
58
68
 
59
- const tmpPath = pathMod.join(os.tmpdir(), `hadars-template-${Date.now()}.html`);
60
- await fs.writeFile(tmpPath, processedHtml);
61
- return tmpPath;
69
+ await fs.writeFile(cachedPath, processedHtml);
70
+ return cachedPath;
62
71
  }
63
72
 
64
73
  const HEAD_MARKER = '<meta name="HADARS_HEAD">';
@@ -395,6 +404,11 @@ const getSuffix = (mode: Mode) => mode === 'development' ? `?v=${Date.now()}` :
395
404
 
396
405
  const HadarsFolder = './.hadars';
397
406
  const StaticPath = `${HadarsFolder}/static`;
407
+ // Dedicated temp directory — keeps all hadars temp files out of the root of
408
+ // os.tmpdir() so rspack's file watcher doesn't traverse unrelated system files
409
+ // (e.g. Steam/Chrome shared-memory device files) in that directory.
410
+ const HADARS_TMP_DIR = pathMod.join(os.tmpdir(), 'hadars');
411
+ const ensureHadarsTmpDir = () => fs.mkdir(HADARS_TMP_DIR, { recursive: true });
398
412
 
399
413
  const validateOptions = (options: HadarsRuntimeOptions) => {
400
414
  if (!options.entry) {
@@ -481,7 +495,8 @@ export const dev = async (options: HadarsRuntimeOptions) => {
481
495
  throw err;
482
496
  }
483
497
 
484
- const tmpFilePath = pathMod.join(os.tmpdir(), `hadars-client-${Date.now()}.tsx`);
498
+ await ensureHadarsTmpDir();
499
+ const tmpFilePath = pathMod.join(HADARS_TMP_DIR, `client-${Date.now()}.tsx`);
485
500
  await fs.writeFile(tmpFilePath, clientScript);
486
501
 
487
502
  // SSR live-reload id to force re-import
@@ -601,27 +616,32 @@ export const dev = async (options: HadarsRuntimeOptions) => {
601
616
  }
602
617
  })();
603
618
 
604
- // Wait for both client and SSR builds to finish in parallel.
605
- await Promise.all([clientBuildDone, ssrBuildDone]);
606
-
607
- // Continue reading stdout to forward logs and pick up SSR rebuild signals.
608
- if (stdoutReader) {
609
- const reader = stdoutReader as ReadableStreamDefaultReader<Uint8Array>;
610
- (async () => {
611
- try {
612
- while (true) {
613
- const { done, value } = await reader.read();
614
- if (done) break;
615
- const chunk = decoder.decode(value, { stream: true });
616
- try { process.stdout.write(chunk); } catch (e) { }
617
- if (chunk.includes(rebuildMarker)) {
618
- ssrBuildId = crypto.randomBytes(4).toString('hex');
619
- console.log('[hadars] SSR bundle updated, build id:', ssrBuildId);
619
+ // Both builds run in parallel this promise resolves when they're both done.
620
+ // We do NOT await it here; the server starts immediately below so that the
621
+ // port is bound right away. Incoming requests await this promise before
622
+ // processing, so they hold in-flight and all resolve together once ready.
623
+ const readyPromise = Promise.all([clientBuildDone, ssrBuildDone]);
624
+
625
+ readyPromise.then(() => {
626
+ // Continue reading stdout to forward logs and pick up SSR rebuild signals.
627
+ if (stdoutReader) {
628
+ const reader = stdoutReader as ReadableStreamDefaultReader<Uint8Array>;
629
+ (async () => {
630
+ try {
631
+ while (true) {
632
+ const { done, value } = await reader.read();
633
+ if (done) break;
634
+ const chunk = decoder.decode(value, { stream: true });
635
+ try { process.stdout.write(chunk); } catch (e) { }
636
+ if (chunk.includes(rebuildMarker)) {
637
+ ssrBuildId = crypto.randomBytes(4).toString('hex');
638
+ console.log('[hadars] SSR bundle updated, build id:', ssrBuildId);
639
+ }
620
640
  }
621
- }
622
- } catch (e) { }
623
- })();
624
- }
641
+ } catch (e) { }
642
+ })();
643
+ }
644
+ });
625
645
 
626
646
  // Forward stderr asynchronously
627
647
  (async () => {
@@ -636,10 +656,12 @@ export const dev = async (options: HadarsRuntimeOptions) => {
636
656
  })();
637
657
 
638
658
  const getPrecontentHtml = makePrecontentHtmlGetter(
639
- fs.readFile(pathMod.join(__dirname, StaticPath, 'out.html'), 'utf-8')
659
+ readyPromise.then(() => fs.readFile(pathMod.join(__dirname, StaticPath, 'out.html'), 'utf-8'))
640
660
  );
641
661
 
642
662
  await serve(port, async (req, ctx) => {
663
+ // Hold requests until both builds are ready. Once resolved this is a no-op.
664
+ await readyPromise;
643
665
  const request = parseRequest(req);
644
666
  if (handler) {
645
667
  const res = await handler(request);
@@ -716,7 +738,8 @@ export const build = async (options: HadarsRuntimeOptions) => {
716
738
  .replace('$_MOD_PATH$', entry + `?v=${Date.now()}`);
717
739
  }
718
740
 
719
- const tmpFilePath = pathMod.join(os.tmpdir(), `hadars-client-${Date.now()}.tsx`);
741
+ await ensureHadarsTmpDir();
742
+ const tmpFilePath = pathMod.join(HADARS_TMP_DIR, `client-${Date.now()}.tsx`);
720
743
  await fs.writeFile(tmpFilePath, clientScript);
721
744
 
722
745
  // Pre-process the HTML template's <style> blocks through PostCSS (e.g. Tailwind).
@@ -288,6 +288,9 @@ const buildCompilerConfig = (
288
288
  clean: false,
289
289
  },
290
290
  mode: opts.mode,
291
+ // Persist transformed modules to disk — subsequent starts only recompile
292
+ // changed files, making repeat dev starts significantly faster.
293
+ cache: true,
291
294
  externals,
292
295
  ...(optimization !== undefined ? { optimization } : {}),
293
296
  plugins: [