@absolutejs/absolute 0.19.0-beta.317 → 0.19.0-beta.318

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/index.js CHANGED
@@ -203,1389 +203,9 @@ var defineIslandRegistry = (registry) => registry, defineIslandComponent = (comp
203
203
  };
204
204
  var init_islands = () => {};
205
205
 
206
- // src/core/islandPageContext.ts
207
- var BOOTSTRAP_MANIFEST_KEY = "BootstrapClient", ISLAND_MARKER = 'data-island="true"', MANIFEST_MARKER = "__ABSOLUTE_MANIFEST__", ISLAND_STATE_MARKER = "__ABS_ISLAND_STATE__", buildIslandsHeadMarkup = (manifest) => {
208
- const manifestScript = `<script>window.__ABSOLUTE_MANIFEST__ = ${JSON.stringify(manifest)}</script>`;
209
- const islandStateScript = "<script>window.__ABS_ISLAND_STATE__ = window.__ABS_ISLAND_STATE__ ?? {}</script>";
210
- const bootstrapPath = manifest[BOOTSTRAP_MANIFEST_KEY];
211
- const bootstrapScript = bootstrapPath ? `<script type="module" src="${bootstrapPath}"></script>` : "";
212
- return `${manifestScript}${islandStateScript}${bootstrapScript}`;
213
- }, injectHeadMarkup = (html, markup) => {
214
- const closingHeadIndex = html.indexOf("</head>");
215
- if (closingHeadIndex >= 0) {
216
- return `${html.slice(0, closingHeadIndex)}${markup}${html.slice(closingHeadIndex)}`;
217
- }
218
- const openingBodyIndex = html.indexOf("<body");
219
- if (openingBodyIndex >= 0) {
220
- const bodyStart = html.indexOf(">", openingBodyIndex);
221
- if (bodyStart >= 0) {
222
- return `${html.slice(0, openingBodyIndex)}<head>${markup}</head>${html.slice(openingBodyIndex)}`;
223
- }
224
- }
225
- return `<!DOCTYPE html><html><head>${markup}</head><body>${html}</body></html>`;
226
- }, setCurrentIslandManifest = (manifest) => {
227
- globalThis.__absoluteManifest = manifest;
228
- }, htmlContainsIslands = (html) => html.includes(ISLAND_MARKER), injectIslandPageContext = (html, options) => {
229
- const manifest = globalThis.__absoluteManifest;
230
- const hasIslands = options?.hasIslands ?? htmlContainsIslands(html);
231
- if (!manifest || !hasIslands) {
232
- return html;
233
- }
234
- if (html.includes(MANIFEST_MARKER) || html.includes(ISLAND_STATE_MARKER)) {
235
- return html;
236
- }
237
- return injectHeadMarkup(html, buildIslandsHeadMarkup(manifest));
238
- };
239
-
240
- // src/utils/ssrErrorPage.ts
241
- var ssrErrorPage = (framework, error) => {
242
- const frameworkColors = {
243
- angular: "#dd0031",
244
- html: "#e34c26",
245
- htmx: "#1a365d",
246
- react: "#61dafb",
247
- svelte: "#ff3e00",
248
- vue: "#42b883"
249
- };
250
- const accent = frameworkColors[framework] ?? "#94a3b8";
251
- const label = framework.charAt(0).toUpperCase() + framework.slice(1);
252
- const message = error instanceof Error ? error.message : String(error);
253
- return `<!DOCTYPE html>
254
- <html>
255
- <head>
256
- <meta charset="utf-8">
257
- <meta name="viewport" content="width=device-width, initial-scale=1">
258
- <title>SSR Error - AbsoluteJS</title>
259
- <style>
260
- *{margin:0;padding:0;box-sizing:border-box}
261
- body{min-height:100vh;background:linear-gradient(135deg,rgba(15,23,42,0.98) 0%,rgba(30,41,59,0.98) 100%);color:#e2e8f0;font-family:"JetBrains Mono","Fira Code",ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:14px;line-height:1.6;display:flex;align-items:flex-start;justify-content:center;padding:32px}
262
- .card{max-width:720px;width:100%;background:rgba(30,41,59,0.6);border:1px solid rgba(71,85,105,0.5);border-radius:16px;box-shadow:0 25px 50px -12px rgba(0,0,0,0.5),0 0 0 1px rgba(255,255,255,0.05);overflow:hidden}
263
- .header{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:20px 24px;background:rgba(15,23,42,0.5);border-bottom:1px solid rgba(71,85,105,0.4)}
264
- .brand{font-weight:700;font-size:20px;color:#fff;letter-spacing:-0.02em}
265
- .badge{padding:5px 10px;border-radius:8px;font-size:12px;font-weight:600;background:${accent};color:#fff;opacity:0.95;box-shadow:0 2px 4px rgba(0,0,0,0.2)}
266
- .kind{color:#94a3b8;font-size:13px;font-weight:500}
267
- .content{padding:24px}
268
- .label{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:0.08em;color:#94a3b8;margin-bottom:8px}
269
- .message{margin:0;padding:16px 20px;background:rgba(239,68,68,0.12);border:1px solid rgba(239,68,68,0.25);border-radius:10px;overflow-x:auto;white-space:pre-wrap;word-break:break-word;color:#fca5a5;font-size:13px;line-height:1.5}
270
- .hint{margin-top:20px;padding:12px 20px;background:rgba(71,85,105,0.3);border-radius:10px;border:1px solid rgba(71,85,105,0.4);color:#cbd5e1;font-size:13px}
271
- </style>
272
- </head>
273
- <body>
274
- <div class="card">
275
- <div class="header">
276
- <div style="display:flex;align-items:center;gap:12px">
277
- <span class="brand">AbsoluteJS</span>
278
- <span class="badge">${label}</span>
279
- </div>
280
- <span class="kind">Server Render Error</span>
281
- </div>
282
- <div class="content">
283
- <div class="label">What went wrong</div>
284
- <pre class="message">${message.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")}</pre>
285
- <div class="hint">A component threw during server-side rendering. Check the terminal for the full stack trace.</div>
286
- </div>
287
- </div>
288
- </body>
289
- </html>`;
290
- };
291
-
292
- // src/utils/stringModifiers.ts
293
- var normalizeSlug = (str) => str.trim().replace(/\s+/g, "-").replace(/[^A-Za-z0-9\-_]+/g, "").replace(/[-_]{2,}/g, "-"), toKebab = (str) => normalizeSlug(str).replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase(), toPascal = (str) => {
294
- if (!str.includes("-") && !str.includes("_")) {
295
- return str.charAt(0).toUpperCase() + str.slice(1);
296
- }
297
- return normalizeSlug(str).split(/[-_]/).filter(Boolean).map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1).toLowerCase()).join("");
298
- }, toScreamingSnake = (str) => str.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toUpperCase();
299
-
300
- // src/utils/resolveConvention.ts
301
- import { basename } from "path";
302
- var CONVENTIONS_KEY = "__absoluteConventions", isConventionsMap = (value) => Boolean(value) && typeof value === "object", getMap = () => {
303
- const value = Reflect.get(globalThis, CONVENTIONS_KEY);
304
- if (isConventionsMap(value))
305
- return value;
306
- const empty = {};
307
- return empty;
308
- }, derivePageName = (pagePath) => {
309
- const base = basename(pagePath);
310
- const dotIndex = base.indexOf(".");
311
- const name = dotIndex > 0 ? base.slice(0, dotIndex) : base;
312
- return toPascal(name);
313
- }, resolveErrorConventionPath = (framework, pageName) => {
314
- const conventions2 = getMap()[framework];
315
- if (!conventions2)
316
- return;
317
- return conventions2.pages?.[pageName]?.error ?? conventions2.defaults?.error;
318
- }, resolveNotFoundConventionPath = (framework) => getMap()[framework]?.defaults?.notFound, setConventions = (map) => {
319
- Reflect.set(globalThis, CONVENTIONS_KEY, map);
320
- }, isDev = () => true, buildErrorProps = (error) => {
321
- const message = error instanceof Error ? error.message : String(error);
322
- const stack = isDev() && error instanceof Error ? error.stack : undefined;
323
- return { error: { message, stack } };
324
- }, renderReactError = async (conventionPath, errorProps) => {
325
- const { createElement } = await import("react");
326
- const { renderToReadableStream } = await import("react-dom/server");
327
- const mod = await import(conventionPath);
328
- const [firstKey] = Object.keys(mod);
329
- const ErrorComponent = mod.default ?? (firstKey ? mod[firstKey] : undefined);
330
- const element = createElement(ErrorComponent, errorProps);
331
- const stream = await renderToReadableStream(element);
332
- return new Response(stream, {
333
- headers: { "Content-Type": "text/html" },
334
- status: 500
335
- });
336
- }, renderSvelteError = async (conventionPath, errorProps) => {
337
- const { render } = await import("svelte/server");
338
- const mod = await import(conventionPath);
339
- const ErrorComponent = mod.default;
340
- const { head, body } = render(ErrorComponent, {
341
- props: errorProps
342
- });
343
- const html = `<!DOCTYPE html><html><head>${head}</head><body>${body}</body></html>`;
344
- return new Response(html, {
345
- headers: { "Content-Type": "text/html" },
346
- status: 500
347
- });
348
- }, unescapeVueStyles = (ssrBody) => {
349
- let styles = "";
350
- const body = ssrBody.replace(/<style>([\s\S]*?)<\/style>/g, (_, css) => {
351
- styles += `<style>${css.replace(/&quot;/g, '"').replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">")}</style>`;
352
- return "";
353
- });
354
- return { body, styles };
355
- }, renderVueError = async (conventionPath, errorProps) => {
356
- const { createSSRApp, h } = await import("vue");
357
- const { renderToString } = await import("vue/server-renderer");
358
- const mod = await import(conventionPath);
359
- const ErrorComponent = mod.default;
360
- const app = createSSRApp({
361
- render: () => h(ErrorComponent, errorProps)
362
- });
363
- const rawBody = await renderToString(app);
364
- const { styles, body } = unescapeVueStyles(rawBody);
365
- const html = `<!DOCTYPE html><html><head>${styles}</head><body><div id="root">${body}</div></body></html>`;
366
- return new Response(html, {
367
- headers: { "Content-Type": "text/html" },
368
- status: 500
369
- });
370
- }, renderAngularError = async (conventionPath, errorProps) => {
371
- const mod = await import(conventionPath);
372
- const renderError = mod.default ?? mod.renderError;
373
- if (typeof renderError !== "function")
374
- return null;
375
- const html = renderError(errorProps);
376
- return new Response(html, {
377
- headers: { "Content-Type": "text/html" },
378
- status: 500
379
- });
380
- }, logConventionRenderError = (framework, label, renderError) => {
381
- const message = renderError instanceof Error ? renderError.message : "";
382
- if (message.includes("Cannot find module") || message.includes("Cannot find package") || message.includes("not found in module")) {
383
- console.error(`[SSR] Convention ${label} page for ${framework} failed: missing framework package. Ensure the ${framework} runtime is installed (e.g. bun add ${framework === "react" ? "react react-dom" : framework}).`);
384
- return;
385
- }
386
- console.error(`[SSR] Failed to render ${framework} convention ${label} page:`, renderError);
387
- }, ERROR_RENDERERS, renderConventionError = async (framework, pageName, error) => {
388
- const conventionPath = resolveErrorConventionPath(framework, pageName);
389
- if (!conventionPath)
390
- return null;
391
- const errorProps = buildErrorProps(error);
392
- const renderer = ERROR_RENDERERS[framework];
393
- if (!renderer)
394
- return null;
395
- try {
396
- return await renderer(conventionPath, errorProps);
397
- } catch (renderError) {
398
- logConventionRenderError(framework, "error", renderError);
399
- }
400
- return null;
401
- }, renderReactNotFound = async (conventionPath) => {
402
- const { createElement } = await import("react");
403
- const { renderToReadableStream } = await import("react-dom/server");
404
- const mod = await import(conventionPath);
405
- const [nfKey] = Object.keys(mod);
406
- const NotFoundComponent = mod.default ?? (nfKey ? mod[nfKey] : undefined);
407
- const element = createElement(NotFoundComponent);
408
- const stream = await renderToReadableStream(element);
409
- return new Response(stream, {
410
- headers: { "Content-Type": "text/html" },
411
- status: 404
412
- });
413
- }, renderSvelteNotFound = async (conventionPath) => {
414
- const { render } = await import("svelte/server");
415
- const mod = await import(conventionPath);
416
- const NotFoundComponent = mod.default;
417
- const { head, body } = render(NotFoundComponent);
418
- const html = `<!DOCTYPE html><html><head>${head}</head><body>${body}</body></html>`;
419
- return new Response(html, {
420
- headers: { "Content-Type": "text/html" },
421
- status: 404
422
- });
423
- }, renderVueNotFound = async (conventionPath) => {
424
- const { createSSRApp, h } = await import("vue");
425
- const { renderToString } = await import("vue/server-renderer");
426
- const mod = await import(conventionPath);
427
- const NotFoundComponent = mod.default;
428
- const app = createSSRApp({
429
- render: () => h(NotFoundComponent)
430
- });
431
- const rawBody = await renderToString(app);
432
- const { styles, body } = unescapeVueStyles(rawBody);
433
- const html = `<!DOCTYPE html><html><head>${styles}</head><body><div id="root">${body}</div></body></html>`;
434
- return new Response(html, {
435
- headers: { "Content-Type": "text/html" },
436
- status: 404
437
- });
438
- }, renderAngularNotFound = async (conventionPath) => {
439
- const mod = await import(conventionPath);
440
- const renderNotFound = mod.default ?? mod.renderNotFound;
441
- if (typeof renderNotFound !== "function")
442
- return null;
443
- const html = renderNotFound();
444
- return new Response(html, {
445
- headers: { "Content-Type": "text/html" },
446
- status: 404
447
- });
448
- }, NOT_FOUND_RENDERERS, renderConventionNotFound = async (framework) => {
449
- const conventionPath = resolveNotFoundConventionPath(framework);
450
- if (!conventionPath)
451
- return null;
452
- const renderer = NOT_FOUND_RENDERERS[framework];
453
- if (!renderer)
454
- return null;
455
- try {
456
- return await renderer(conventionPath);
457
- } catch (renderError) {
458
- logConventionRenderError(framework, "not-found", renderError);
459
- }
460
- return null;
461
- }, NOT_FOUND_PRIORITY, renderFirstNotFound = async () => {
462
- for (const framework of NOT_FOUND_PRIORITY) {
463
- if (!getMap()[framework]?.defaults?.notFound)
464
- continue;
465
- const response = await renderConventionNotFound(framework);
466
- if (response)
467
- return response;
468
- }
469
- return null;
470
- };
471
- var init_resolveConvention = __esm(() => {
472
- ERROR_RENDERERS = {
473
- angular: renderAngularError,
474
- react: renderReactError,
475
- svelte: renderSvelteError,
476
- vue: renderVueError
477
- };
478
- NOT_FOUND_RENDERERS = {
479
- angular: renderAngularNotFound,
480
- react: renderReactNotFound,
481
- svelte: renderSvelteNotFound,
482
- vue: renderVueNotFound
483
- };
484
- NOT_FOUND_PRIORITY = [
485
- "react",
486
- "svelte",
487
- "vue",
488
- "angular"
489
- ];
490
- });
491
-
492
- // src/react/pageHandler.ts
493
- var ssrDirty = false, buildDirtyResponse = (index, maybeProps) => {
494
- const propsScript = maybeProps ? `window.__INITIAL_PROPS__=${JSON.stringify(maybeProps)};` : "";
495
- const dirtyFlag = "window.__SSR_DIRTY__=true;";
496
- const refreshSetup = "window.__REFRESH_BUFFER__=[];" + "window.$RefreshReg$=function(t,i){window.__REFRESH_BUFFER__.push([t,i])};" + "window.$RefreshSig$=function(){return function(t){return t}};";
497
- const inlineScript = `${propsScript}${dirtyFlag}${refreshSetup}`;
498
- const html = `<!DOCTYPE html><html><head></head><body>` + `<script>${inlineScript}</script>` + `<script type="module" src="${index}"></script>` + `</body></html>`;
499
- return new Response(html, {
500
- headers: { "Content-Type": "text/html" }
501
- });
502
- }, handleReactPageRequest = async (PageComponent, index, ...props) => {
503
- const [maybeProps] = props;
504
- if (ssrDirty) {
505
- return buildDirtyResponse(index, maybeProps);
506
- }
507
- try {
508
- const { createElement } = await import("react");
509
- const { renderToReadableStream } = await import("react-dom/server");
510
- const element = maybeProps !== undefined ? createElement(PageComponent, maybeProps) : createElement(PageComponent);
511
- const propsScript = maybeProps ? `window.__INITIAL_PROPS__=${JSON.stringify(maybeProps)};` : "";
512
- const refreshSetup = "window.__REFRESH_BUFFER__=[];window.$RefreshReg$=function(t,i){window.__REFRESH_BUFFER__.push([t,i])};window.$RefreshSig$=function(){return function(t){return t}};";
513
- const stream = await renderToReadableStream(element, {
514
- bootstrapModules: [index],
515
- bootstrapScriptContent: propsScript + refreshSetup || undefined,
516
- onError(error) {
517
- console.error("[SSR] React streaming error:", error);
518
- }
519
- });
520
- const html = injectIslandPageContext(await new Response(stream).text());
521
- return new Response(html, {
522
- headers: { "Content-Type": "text/html" }
523
- });
524
- } catch (error) {
525
- console.error("[SSR] React render error:", error);
526
- const pageName = PageComponent.name || PageComponent.displayName || "";
527
- const conventionResponse = await renderConventionError("react", pageName, error);
528
- if (conventionResponse)
529
- return conventionResponse;
530
- return new Response(ssrErrorPage("react", error), {
531
- headers: { "Content-Type": "text/html" },
532
- status: 500
533
- });
534
- }
535
- }, invalidateReactSsrCache = () => {
536
- ssrDirty = true;
537
- };
538
- var init_pageHandler = __esm(() => {
539
- init_resolveConvention();
540
- });
541
-
542
- // src/core/currentIslandRegistry.ts
543
- var setCurrentIslandRegistry = (registry) => {
544
- globalThis.__absoluteIslandRegistry = registry;
545
- }, requireCurrentIslandRegistry = () => {
546
- const registry = globalThis.__absoluteIslandRegistry;
547
- if (!registry) {
548
- throw new Error("No island registry is active. Configure `islands.registry` in absolute.config.ts before rendering <Island />.");
549
- }
550
- return registry;
551
- };
552
-
553
- // src/build/islandEntries.ts
554
- import {
555
- mkdirSync,
556
- rmSync,
557
- writeFileSync
558
- } from "fs";
559
- import { dirname, extname, join, relative, resolve as resolve3 } from "path";
560
- var frameworks, isRecord3 = (value) => typeof value === "object" && value !== null, resolveRegistryExport2 = (mod) => {
561
- if (isRecord3(mod.islandRegistry))
562
- return mod.islandRegistry;
563
- if (isRecord3(mod.default))
564
- return mod.default;
565
- throw new Error("Island registry module must export `islandRegistry` or a default registry object.");
566
- }, normalizeImportPath = (wrapperPath, targetPath) => {
567
- const importPath = relative(dirname(wrapperPath), targetPath).replace(/\\/g, "/");
568
- return importPath.startsWith(".") ? importPath : `./${importPath}`;
569
- }, isIdentifier = (value) => /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(value), resolveIslandSourcePath = (registryPath, sourcePath) => {
570
- if (sourcePath.startsWith("file://")) {
571
- return new URL(sourcePath).pathname;
572
- }
573
- return resolve3(dirname(registryPath), sourcePath);
574
- }, createRegistryImportCode = (wrapperPath, registryPath, hasNamedExport) => {
575
- const normalizedPath = normalizeImportPath(wrapperPath, registryPath);
576
- if (hasNamedExport) {
577
- return {
578
- importStatement: `import { islandRegistry as __absoluteIslandRegistry } from ${JSON.stringify(normalizedPath)};`,
579
- registryReference: "__absoluteIslandRegistry"
580
- };
581
- }
582
- return {
583
- importStatement: `import __absoluteIslandRegistry from ${JSON.stringify(normalizedPath)};`,
584
- registryReference: "__absoluteIslandRegistry"
585
- };
586
- }, createDirectEntrySource = (wrapperPath, importPath, exportName) => {
587
- const normalizedImportPath = normalizeImportPath(wrapperPath, importPath);
588
- if (!exportName || exportName === "default") {
589
- return `export { default } from ${JSON.stringify(normalizedImportPath)};
590
- `;
591
- }
592
- return `export { ${exportName} as default } from ${JSON.stringify(normalizedImportPath)};
593
- `;
594
- }, createRegistryEntrySource = (wrapperPath, registryPath, hasNamedExport, framework, component) => {
595
- const { importStatement, registryReference } = createRegistryImportCode(wrapperPath, registryPath, hasNamedExport);
596
- const frameworkAccess = isIdentifier(framework) ? `${registryReference}.${framework}` : `${registryReference}[${JSON.stringify(framework)}]`;
597
- const componentAccess = isIdentifier(component) ? `${frameworkAccess}.${component}` : `${frameworkAccess}[${JSON.stringify(component)}]`;
598
- return `${importStatement}
599
-
600
- const component = ${componentAccess};
601
-
602
- export default component;
603
- `;
604
- }, shouldUseCompiledClientPath = (framework, sourcePath) => {
605
- if (framework === "svelte") {
606
- return /\.svelte(?:\.(?:ts|js))?$/.test(sourcePath);
607
- }
608
- if (framework === "vue") {
609
- return extname(sourcePath) === ".vue";
610
- }
611
- return false;
612
- }, loadIslandRegistryBuildInfo = async (registryPath) => {
613
- const resolvedRegistryPath = resolve3(registryPath);
614
- const registryModule = await import(resolvedRegistryPath);
615
- const registry = resolveRegistryExport2(registryModule);
616
- const definitions = [];
617
- for (const framework of frameworks) {
618
- const frameworkRegistry = registry[framework];
619
- if (!isRecord3(frameworkRegistry))
620
- continue;
621
- for (const [component, value] of Object.entries(frameworkRegistry)) {
622
- definitions.push({
623
- buildReference: getIslandBuildReference(value),
624
- component,
625
- framework
626
- });
627
- }
628
- }
629
- return {
630
- definitions,
631
- hasNamedExport: isRecord3(registryModule.islandRegistry),
632
- registry,
633
- resolvedRegistryPath
634
- };
635
- }, collectIslandFrameworkSources = (buildInfo) => {
636
- const sources = {};
637
- for (const definition of buildInfo.definitions) {
638
- const buildReference = definition.buildReference;
639
- if (!buildReference)
640
- continue;
641
- const resolvedSourcePath = resolveIslandSourcePath(buildInfo.resolvedRegistryPath, buildReference.source);
642
- if (!shouldUseCompiledClientPath(definition.framework, resolvedSourcePath)) {
643
- continue;
644
- }
645
- const frameworkSources = sources[definition.framework] ?? [];
646
- if (!frameworkSources.includes(resolvedSourcePath)) {
647
- frameworkSources.push(resolvedSourcePath);
648
- }
649
- sources[definition.framework] = frameworkSources;
650
- }
651
- return sources;
652
- }, generateIslandEntryPoints = async ({
653
- buildInfo,
654
- buildPath,
655
- clientPathMaps = {}
656
- }) => {
657
- const generatedRoot = join(buildPath, "_island_entries");
658
- rmSync(generatedRoot, { force: true, recursive: true });
659
- const entries = [];
660
- for (const definition of buildInfo.definitions) {
661
- const entryPath = join(generatedRoot, "islands", definition.framework, `${definition.component}.ts`);
662
- const buildReference = definition.buildReference;
663
- const source = buildReference ? resolveIslandSourcePath(buildInfo.resolvedRegistryPath, buildReference.source) : null;
664
- const compiledSourcePath = source && shouldUseCompiledClientPath(definition.framework, source) ? clientPathMaps[definition.framework]?.get(source) : undefined;
665
- const entrySource = source && (compiledSourcePath || !shouldUseCompiledClientPath(definition.framework, source)) ? createDirectEntrySource(entryPath, compiledSourcePath ?? source, compiledSourcePath ? undefined : buildReference?.export) : createRegistryEntrySource(entryPath, buildInfo.resolvedRegistryPath, buildInfo.hasNamedExport, definition.framework, definition.component);
666
- mkdirSync(dirname(entryPath), { recursive: true });
667
- writeFileSync(entryPath, entrySource);
668
- entries.push({
669
- component: definition.component,
670
- entryPath,
671
- framework: definition.framework
672
- });
673
- }
674
- return {
675
- entries,
676
- generatedRoot
677
- };
678
- };
679
- var init_islandEntries = __esm(() => {
680
- init_islands();
681
- frameworks = [
682
- "react",
683
- "svelte",
684
- "vue",
685
- "angular"
686
- ];
687
- });
688
-
689
- // src/build/scanEntryPoints.ts
690
- import { existsSync } from "fs";
691
- var {Glob } = globalThis.Bun;
692
- var scanEntryPoints = async (dir, pattern) => {
693
- if (!existsSync(dir))
694
- return [];
695
- const entryPaths = [];
696
- const glob = new Glob(pattern);
697
- for await (const file2 of glob.scan({ absolute: true, cwd: dir })) {
698
- entryPaths.push(file2);
699
- }
700
- return entryPaths;
701
- };
702
- var init_scanEntryPoints = () => {};
703
-
704
- // src/islands/sourceMetadata.ts
705
- var islandFrameworks, islandHydrationModes, isIslandFramework = (value) => islandFrameworks.some((framework) => framework === value), isIslandHydrate = (value) => islandHydrationModes.some((hydrate) => hydrate === value), parseIslandTagAttributes = (attributeString) => {
706
- const frameworkMatch = attributeString.match(/\bframework\s*=\s*["']([^"']+)["']/);
707
- const componentMatch = attributeString.match(/\bcomponent\s*=\s*["']([^"']+)["']/);
708
- const hydrateMatch = attributeString.match(/\bhydrate\s*=\s*["']([^"']+)["']/);
709
- const framework = frameworkMatch?.[1];
710
- const component = componentMatch?.[1];
711
- if (!framework || !component) {
712
- return null;
713
- }
714
- if (!isIslandFramework(framework)) {
715
- return null;
716
- }
717
- const hydrateCandidate = hydrateMatch?.[1];
718
- return {
719
- component,
720
- framework,
721
- hydrate: hydrateCandidate && isIslandHydrate(hydrateCandidate) ? hydrateCandidate : undefined
722
- };
723
- }, normalizeUsage = (usage) => `${usage.framework}:${usage.component}:${usage.hydrate ?? ""}`, addUsage = (usageMap, usage) => {
724
- if (!usage)
725
- return;
726
- usageMap.set(normalizeUsage(usage), usage);
727
- }, extractIslandUsagesFromSource = (source) => {
728
- const usageMap = new Map;
729
- const islandTagRegex = /<Island\b([\s\S]*?)(?:\/>|>(?:[\s\S]*?)<\/Island>)/g;
730
- let islandTagMatch = islandTagRegex.exec(source);
731
- while (islandTagMatch) {
732
- addUsage(usageMap, parseIslandTagAttributes(islandTagMatch[1] ?? ""));
733
- islandTagMatch = islandTagRegex.exec(source);
734
- }
735
- const staticRenderCallRegex = /renderIsland\s*\(\s*\{[\s\S]*?\bframework\s*:\s*['"]([^'"]+)['"][\s\S]*?\bcomponent\s*:\s*['"]([^'"]+)['"](?:[\s\S]*?\bhydrate\s*:\s*['"]([^'"]+)['"])?[\s\S]*?\}\s*\)/g;
736
- let renderMatch = staticRenderCallRegex.exec(source);
737
- while (renderMatch) {
738
- const framework = renderMatch[1];
739
- const component = renderMatch[2];
740
- const hydrate = renderMatch[3];
741
- if (!framework || !component || !isIslandFramework(framework)) {
742
- renderMatch = staticRenderCallRegex.exec(source);
743
- continue;
744
- }
745
- addUsage(usageMap, {
746
- component,
747
- framework,
748
- hydrate: hydrate && isIslandHydrate(hydrate) ? hydrate : undefined
749
- });
750
- renderMatch = staticRenderCallRegex.exec(source);
751
- }
752
- return [...usageMap.values()];
753
- }, buildIslandMetadataExports = (source) => {
754
- const usages = extractIslandUsagesFromSource(source);
755
- const serialized = JSON.stringify(usages);
756
- return `
757
- export const __ABSOLUTE_PAGE_ISLANDS__ = ${serialized};
758
- export const __ABSOLUTE_PAGE_HAS_ISLANDS__ = ${usages.length > 0};
759
- `;
760
- };
761
- var init_sourceMetadata = __esm(() => {
762
- islandFrameworks = [
763
- "react",
764
- "svelte",
765
- "vue",
766
- "angular"
767
- ];
768
- islandHydrationModes = [
769
- "load",
770
- "idle",
771
- "visible",
772
- "none"
773
- ];
774
- });
775
-
776
- // src/islands/pageMetadata.ts
777
- import { readFileSync } from "fs";
778
- import { dirname as dirname2, resolve as resolve4 } from "path";
779
- var pagePatterns, getPageDirs = (config) => [
780
- { dir: config.angularDirectory, framework: "angular" },
781
- { dir: config.reactDirectory, framework: "react" },
782
- { dir: config.svelteDirectory, framework: "svelte" },
783
- { dir: config.vueDirectory, framework: "vue" },
784
- { dir: config.htmlDirectory, framework: "html" },
785
- { dir: config.htmxDirectory, framework: "htmx" }
786
- ].filter((entry) => typeof entry.dir === "string" && entry.dir.length > 0), buildIslandSourceLookup = async (config) => {
787
- const registryPath = config.islands?.registry;
788
- if (!registryPath) {
789
- return new Map;
790
- }
791
- const buildInfo = await loadIslandRegistryBuildInfo(registryPath);
792
- const lookup = new Map;
793
- for (const definition of buildInfo.definitions) {
794
- const source = definition.buildReference?.source;
795
- if (!source)
796
- continue;
797
- const resolvedSource = source.startsWith("file://") ? new URL(source).pathname : resolve4(dirname2(buildInfo.resolvedRegistryPath), source);
798
- lookup.set(`${definition.framework}:${definition.component}`, resolve4(resolvedSource));
799
- }
800
- return lookup;
801
- }, loadPageIslandMetadata = async (config) => {
802
- const pageMetadata = new Map;
803
- const islandSourceLookup = await buildIslandSourceLookup(config);
804
- for (const entry of getPageDirs(config)) {
805
- const pattern = pagePatterns[entry.framework];
806
- if (!pattern)
807
- continue;
808
- const files = await scanEntryPoints(resolve4(entry.dir), pattern);
809
- for (const filePath of files) {
810
- const source = readFileSync(filePath, "utf-8");
811
- const islands = extractIslandUsagesFromSource(source);
812
- pageMetadata.set(resolve4(filePath), {
813
- islands: islands.map((usage) => {
814
- const sourcePath = islandSourceLookup.get(`${usage.framework}:${usage.component}`);
815
- return sourcePath ? {
816
- ...usage,
817
- source: sourcePath
818
- } : usage;
819
- }),
820
- pagePath: resolve4(filePath)
821
- });
822
- }
823
- }
824
- return pageMetadata;
825
- }, setCurrentPageIslandMetadata = (metadata2) => {
826
- globalThis.__absolutePageIslandMetadata = metadata2;
827
- }, getCurrentPageIslandMetadata = () => globalThis.__absolutePageIslandMetadata ?? new Map, getPagesUsingIslandSource = (sourcePath) => {
828
- const target = resolve4(sourcePath);
829
- const matches = [];
830
- for (const metadata2 of getCurrentPageIslandMetadata().values()) {
831
- const usesTarget = metadata2.islands.some((usage) => {
832
- const candidate = usage.source;
833
- return candidate ? resolve4(candidate) === target : false;
834
- });
835
- if (usesTarget) {
836
- matches.push(metadata2.pagePath);
837
- }
838
- }
839
- return matches;
840
- };
841
- var init_pageMetadata = __esm(() => {
842
- init_islandEntries();
843
- init_scanEntryPoints();
844
- init_sourceMetadata();
845
- pagePatterns = {
846
- angular: "pages/**/*.{ts,js}",
847
- html: "pages/**/*.html",
848
- htmx: "pages/**/*.html",
849
- react: "pages/**/*.{ts,tsx,js,jsx}",
850
- svelte: "pages/**/*.svelte",
851
- vue: "pages/**/*.vue"
852
- };
853
- });
854
-
855
- // src/build/generateIslandBindings.ts
856
- var exports_generateIslandBindings = {};
857
- __export(exports_generateIslandBindings, {
858
- generateIslandBindings: () => generateIslandBindings
859
- });
860
- import { existsSync as existsSync2, mkdirSync as mkdirSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
861
- import { dirname as dirname3, resolve as resolve5 } from "path";
862
- var ensureDir = (filePath) => {
863
- mkdirSync2(dirname3(filePath), { recursive: true });
864
- }, writeIfChanged = (filePath, content) => {
865
- ensureDir(filePath);
866
- writeFileSync2(filePath, content);
867
- }, removeIfExists = (filePath) => {
868
- if (existsSync2(filePath)) {
869
- rmSync2(filePath, { force: true });
870
- }
871
- }, generateIslandBindings = (projectRoot, config) => {
872
- const registryPath = config.islands?.registry;
873
- if (!registryPath) {
874
- return;
875
- }
876
- const resolvedRegistryPath = resolve5(projectRoot, registryPath);
877
- removeIfExists(resolve5(dirname3(resolvedRegistryPath), "absolute-islands.d.ts"));
878
- if (config.reactDirectory) {
879
- const compatTarget = resolve5(config.reactDirectory, "generated", "absolute-react.ts");
880
- removeIfExists(resolve5(config.reactDirectory, "generated", "Island.tsx"));
881
- removeIfExists(resolve5(config.reactDirectory, "generated", "absolute-react.d.ts"));
882
- writeIfChanged(compatTarget, `export * from "@absolutejs/absolute/react";
883
- `);
884
- }
885
- if (config.vueDirectory) {
886
- const compatTarget = resolve5(config.vueDirectory, "generated", "absolute-vue.ts");
887
- removeIfExists(resolve5(config.vueDirectory, "generated", "Island.ts"));
888
- removeIfExists(resolve5(config.vueDirectory, "generated", "absolute-vue.d.ts"));
889
- writeIfChanged(compatTarget, `export * from "@absolutejs/absolute/vue";
890
- `);
891
- }
892
- if (config.svelteDirectory) {
893
- const compatTarget = resolve5(config.svelteDirectory, "generated", "absolute-svelte.ts");
894
- removeIfExists(resolve5(config.svelteDirectory, "generated", "islands.ts"));
895
- removeIfExists(resolve5(config.svelteDirectory, "generated", "absolute-svelte.d.ts"));
896
- writeIfChanged(compatTarget, `export * from "@absolutejs/absolute/svelte";
897
- `);
898
- }
899
- if (config.angularDirectory) {
900
- const compatTarget = resolve5(config.angularDirectory, "generated", "absolute-angular.ts");
901
- removeIfExists(resolve5(config.angularDirectory, "generated", "islands.ts"));
902
- removeIfExists(resolve5(config.angularDirectory, "generated", "absolute-angular.d.ts"));
903
- writeIfChanged(compatTarget, `export * from "@absolutejs/absolute/angular";
904
- `);
905
- }
906
- };
907
- var init_generateIslandBindings = () => {};
908
-
909
- // src/utils/getDurationString.ts
910
- var getDurationString = (duration) => {
911
- let durationString;
912
- if (duration < MILLISECONDS_IN_A_SECOND) {
913
- durationString = `${duration.toFixed(TIME_PRECISION)}ms`;
914
- } else if (duration < MILLISECONDS_IN_A_MINUTE) {
915
- durationString = `${(duration / MILLISECONDS_IN_A_SECOND).toFixed(TIME_PRECISION)}s`;
916
- } else {
917
- durationString = `${(duration / MILLISECONDS_IN_A_MINUTE).toFixed(TIME_PRECISION)}m`;
918
- }
919
- return durationString;
920
- };
921
- var init_getDurationString = __esm(() => {
922
- init_constants();
923
- });
924
-
925
- // src/utils/startupBanner.ts
926
- var colors, MONTHS, formatTimestamp = () => {
927
- const now = new Date;
928
- const month = MONTHS[now.getMonth()];
929
- const day = now.getDate().toString().padStart(2, "0");
930
- let hours = now.getHours();
931
- const minutes = now.getMinutes().toString().padStart(2, "0");
932
- const seconds = now.getSeconds().toString().padStart(2, "0");
933
- const ampm = hours >= HOURS_IN_HALF_DAY ? "PM" : "AM";
934
- hours = hours % HOURS_IN_HALF_DAY || HOURS_IN_HALF_DAY;
935
- return `${month} ${day} ${hours}:${minutes}:${seconds} ${ampm}`;
936
- }, startupBanner = (options) => {
937
- const {
938
- version,
939
- duration,
940
- port,
941
- host,
942
- networkUrl,
943
- protocol = "http"
944
- } = options;
945
- const name = `${colors.cyan}${colors.bold}ABSOLUTEJS${colors.reset}`;
946
- const ver = `${colors.dim}v${version}${colors.reset}`;
947
- const time = `${colors.dim}ready in${colors.reset} ${colors.bold}${getDurationString(duration)}${colors.reset}`;
948
- console.log("");
949
- console.log(` ${name} ${ver} ${time}`);
950
- console.log("");
951
- console.log(` ${colors.green}\u279C${colors.reset} ${colors.bold}Local:${colors.reset} ${protocol}://${host === "0.0.0.0" ? "localhost" : host}:${port}/`);
952
- if (networkUrl) {
953
- console.log(` ${colors.green}\u279C${colors.reset} ${colors.bold}Network:${colors.reset} ${networkUrl}`);
954
- }
955
- console.log("");
956
- };
957
- var init_startupBanner = __esm(() => {
958
- init_constants();
959
- init_getDurationString();
960
- colors = {
961
- bold: "\x1B[1m",
962
- cyan: "\x1B[36m",
963
- dim: "\x1B[2m",
964
- green: "\x1B[32m",
965
- reset: "\x1B[0m"
966
- };
967
- MONTHS = [
968
- "Jan",
969
- "Feb",
970
- "Mar",
971
- "Apr",
972
- "May",
973
- "Jun",
974
- "Jul",
975
- "Aug",
976
- "Sep",
977
- "Oct",
978
- "Nov",
979
- "Dec"
980
- ];
981
- });
982
-
983
- // src/utils/logger.ts
984
- var colors2, frameworkColors, formatPath = (filePath) => {
985
- const cwd = process.cwd();
986
- let relative2 = filePath.startsWith(cwd) ? filePath.slice(cwd.length + 1) : filePath;
987
- relative2 = relative2.replace(/\\/g, "/");
988
- if (!relative2.startsWith("/")) {
989
- relative2 = `/${relative2}`;
990
- }
991
- return relative2;
992
- }, getFrameworkColor = (framework) => frameworkColors[framework] || colors2.white, log = (action, options) => {
993
- const timestamp = `${colors2.dim}${formatTimestamp()}${colors2.reset}`;
994
- const tag = `${colors2.cyan}[hmr]${colors2.reset}`;
995
- let message = action;
996
- if (options?.path) {
997
- const pathColor = options.framework ? getFrameworkColor(options.framework) : colors2.white;
998
- message += ` ${pathColor}${formatPath(options.path)}${colors2.reset}`;
999
- }
1000
- if (options?.duration !== undefined) {
1001
- message += ` ${colors2.dim}(${options.duration}ms)${colors2.reset}`;
1002
- }
1003
- console.log(`${timestamp} ${tag} ${message}`);
1004
- }, logCssUpdate = (path, framework, duration) => {
1005
- log("css update", { duration, framework: framework ?? "css", path });
1006
- }, logError = (message, error) => {
1007
- const timestamp = `${colors2.dim}${formatTimestamp()}${colors2.reset}`;
1008
- const tag = `${colors2.red}[hmr]${colors2.reset}`;
1009
- const errorMsg = error instanceof Error ? error.message : error;
1010
- const fullMessage = `${colors2.red}error${colors2.reset} ${message}${errorMsg ? `: ${errorMsg}` : ""}`;
1011
- console.error(`${timestamp} ${tag} ${fullMessage}`);
1012
- }, logHmrUpdate = (path, framework, duration) => {
1013
- log("hmr update", { duration, framework, path });
1014
- }, logScriptUpdate = (path, framework, duration) => {
1015
- log("script update", { duration, framework, path });
1016
- }, logServerReload = () => {
1017
- log(`${colors2.cyan}server module reloaded${colors2.reset}`);
1018
- }, logWarn = (message) => {
1019
- const timestamp = `${colors2.dim}${formatTimestamp()}${colors2.reset}`;
1020
- const tag = `${colors2.yellow}[hmr]${colors2.reset}`;
1021
- console.warn(`${timestamp} ${tag} ${colors2.yellow}warning${colors2.reset} ${message}`);
1022
- };
1023
- var init_logger = __esm(() => {
1024
- init_startupBanner();
1025
- colors2 = {
1026
- blue: "\x1B[34m",
1027
- bold: "\x1B[1m",
1028
- cyan: "\x1B[36m",
1029
- dim: "\x1B[2m",
1030
- green: "\x1B[32m",
1031
- magenta: "\x1B[35m",
1032
- red: "\x1B[31m",
1033
- reset: "\x1B[0m",
1034
- white: "\x1B[37m",
1035
- yellow: "\x1B[33m"
1036
- };
1037
- frameworkColors = {
1038
- angular: colors2.magenta,
1039
- assets: colors2.dim,
1040
- css: colors2.cyan,
1041
- html: colors2.white,
1042
- htmx: colors2.white,
1043
- react: colors2.blue,
1044
- svelte: colors2.yellow,
1045
- vue: colors2.green
1046
- };
1047
- });
1048
-
1049
- // src/utils/normalizePath.ts
1050
- var normalizePath = (path) => path.replace(/\\/g, "/");
1051
-
1052
- // src/build/generateManifest.ts
1053
- var exports_generateManifest = {};
1054
- __export(exports_generateManifest, {
1055
- generateManifest: () => generateManifest
1056
- });
1057
- import { extname as extname2 } from "path";
1058
- var getManifestKey = (folder, pascalName, isClientComponent, isReact, isVue, isSvelte, isAngular) => {
1059
- if (folder === "indexes")
1060
- return `${pascalName}Index`;
1061
- if (isClientComponent)
1062
- return `${pascalName}Client`;
1063
- if (folder !== "pages")
1064
- return pascalName;
1065
- if (isReact)
1066
- return `${pascalName}Page`;
1067
- if (isVue || isSvelte || isAngular)
1068
- return pascalName;
1069
- return `${pascalName}Page`;
1070
- }, getCssKey = (pascalName, segments) => {
1071
- const isFromVue = segments.some((seg) => seg === "vue");
1072
- if (isFromVue && segments.includes("css"))
1073
- return `${pascalName}CompiledCSS`;
1074
- const isFromReact = segments.some((seg) => seg === "react");
1075
- const isFromSvelte = segments.some((seg) => seg === "svelte");
1076
- const isFromAngular = segments.some((seg) => seg === "angular");
1077
- if (isFromReact || isFromVue || isFromSvelte || isFromAngular)
1078
- return `${pascalName}BundledCSS`;
1079
- return `${pascalName}CSS`;
1080
- }, generateManifest = (outputs, buildPath) => outputs.reduce((manifest, artifact) => {
1081
- const normalizedArtifactPath = normalizePath(artifact.path);
1082
- const normalizedBuildPath = normalizePath(buildPath);
1083
- let relative2 = normalizedArtifactPath.startsWith(normalizedBuildPath) ? normalizedArtifactPath.slice(normalizedBuildPath.length) : normalizedArtifactPath;
1084
- relative2 = relative2.replace(/^\/+/, "");
1085
- const segments = relative2.split("/");
1086
- const fileWithHash = segments.pop();
1087
- if (!fileWithHash)
1088
- return manifest;
1089
- const [baseName] = fileWithHash.split(`.${artifact.hash}.`);
1090
- if (!baseName)
1091
- return manifest;
1092
- const pascalName = toPascal(baseName);
1093
- const ext = extname2(fileWithHash);
1094
- const islandIndex = segments.findIndex((seg) => seg === "islands");
1095
- if (ext === ".css") {
1096
- const cssKey = getCssKey(pascalName, segments);
1097
- if (manifest[cssKey] && manifest[cssKey] !== `/${relative2}`)
1098
- logWarn(`Duplicate manifest key "${cssKey}" \u2014 "${manifest[cssKey]}" will be overwritten by "/${relative2}". Use unique page names across frameworks.`);
1099
- manifest[cssKey] = `/${relative2}`;
1100
- return manifest;
1101
- }
1102
- if (islandIndex > UNFOUND_INDEX) {
1103
- const frameworkSegment = segments[islandIndex + 1];
1104
- if (frameworkSegment === "react" || frameworkSegment === "svelte" || frameworkSegment === "vue" || frameworkSegment === "angular") {
1105
- const manifestKey2 = getIslandManifestKey(frameworkSegment, pascalName);
1106
- manifest[manifestKey2] = `/${relative2}`;
1107
- return manifest;
1108
- }
1109
- }
1110
- const idx = segments.findIndex((seg) => seg === "indexes" || seg === "pages" || seg === "client");
1111
- const folder = idx > UNFOUND_INDEX ? segments[idx] : segments[0];
1112
- const isReact = segments.some((seg) => seg === "react");
1113
- const isVue = segments.some((seg) => seg === "vue");
1114
- const isSvelte = segments.some((seg) => seg === "svelte");
1115
- const isAngular = segments.some((seg) => seg === "angular");
1116
- const isClientComponent = segments.includes("client");
1117
- const manifestKey = getManifestKey(folder, pascalName, isClientComponent, isReact, isVue, isSvelte, isAngular);
1118
- if (manifest[manifestKey] && manifest[manifestKey] !== `/${relative2}`) {
1119
- logWarn(`Duplicate manifest key "${manifestKey}" \u2014 "${manifest[manifestKey]}" will be overwritten by "/${relative2}". Use unique page names across frameworks.`);
1120
- }
1121
- manifest[manifestKey] = `/${relative2}`;
1122
- return manifest;
1123
- }, {});
1124
- var init_generateManifest = __esm(() => {
1125
- init_constants();
1126
- init_logger();
1127
- });
1128
-
1129
- // src/build/generateReactIndexes.ts
1130
- var exports_generateReactIndexes = {};
1131
- __export(exports_generateReactIndexes, {
1132
- generateReactIndexFiles: () => generateReactIndexFiles
1133
- });
1134
- import { existsSync as existsSync3, mkdirSync as mkdirSync3 } from "fs";
1135
- import { readdir, rm, writeFile } from "fs/promises";
1136
- import { basename as basename2, join as join2, relative as relative2, resolve as resolve6, sep } from "path";
1137
- var {Glob: Glob2 } = globalThis.Bun;
1138
- var indexContentCache, resolveDevClientDir = () => {
1139
- const projectRoot = process.cwd();
1140
- const fromSource = resolve6(import.meta.dir, "../dev/client");
1141
- if (existsSync3(fromSource) && fromSource.startsWith(projectRoot)) {
1142
- return fromSource;
1143
- }
1144
- const fromNodeModules = resolve6(projectRoot, "node_modules/@absolutejs/absolute/dist/dev/client");
1145
- if (existsSync3(fromNodeModules))
1146
- return fromNodeModules;
1147
- return resolve6(import.meta.dir, "./dev/client");
1148
- }, devClientDir, errorOverlayPath, hmrClientPath, refreshSetupPath, generateReactIndexFiles = async (reactPagesDirectory, reactIndexesDirectory, isDev2 = false) => {
1149
- if (!existsSync3(reactIndexesDirectory)) {
1150
- mkdirSync3(reactIndexesDirectory, { recursive: true });
1151
- }
1152
- const CONVENTION_RE = /^(?:(.+)\.)?(error|loading|not-found)\.[^.]+$/;
1153
- const pagesGlob = new Glob2("*.*");
1154
- const files = [];
1155
- for await (const file2 of pagesGlob.scan({ cwd: reactPagesDirectory })) {
1156
- if (CONVENTION_RE.test(file2))
1157
- continue;
1158
- files.push(file2);
1159
- }
1160
- const currentPageNames = new Set(files.map((file2) => basename2(file2).split(".")[0]));
1161
- const emptyStringArray = [];
1162
- const existingIndexes = await readdir(reactIndexesDirectory).catch(() => emptyStringArray);
1163
- const staleIndexes = existingIndexes.filter((indexFile) => {
1164
- const indexName = indexFile.replace(/\.tsx$/, "");
1165
- return indexName !== "_refresh" && !currentPageNames.has(indexName);
1166
- });
1167
- if (staleIndexes.length > 0) {
1168
- await Promise.all(staleIndexes.map((indexFile) => {
1169
- indexContentCache.delete(join2(reactIndexesDirectory, indexFile));
1170
- return rm(join2(reactIndexesDirectory, indexFile), {
1171
- force: true
1172
- });
1173
- }));
1174
- }
1175
- const pagesRelPath = relative2(resolve6(reactIndexesDirectory), resolve6(reactPagesDirectory)).split(sep).join("/");
1176
- const promises = files.map(async (file2) => {
1177
- const fileName = basename2(file2);
1178
- const [componentName] = fileName.split(".");
1179
- const hmrPreamble = isDev2 ? [
1180
- `window.__HMR_FRAMEWORK__ = "react";`,
1181
- `window.__REACT_COMPONENT_KEY__ = "${componentName}Index";`,
1182
- `import '${refreshSetupPath}';`,
1183
- `import '${hmrClientPath}';`,
1184
- `import { showErrorOverlay, hideErrorOverlay } from '${errorOverlayPath}';
1185
- `
1186
- ] : [];
1187
- const reactImports = isDev2 ? [
1188
- `import { hydrateRoot, createRoot } from 'react-dom/client';`,
1189
- `import { createElement, Component } from 'react';`
1190
- ] : [
1191
- `import { hydrateRoot, createRoot } from 'react-dom/client';`,
1192
- `import { createElement } from 'react';`
1193
- ];
1194
- const errorBoundaryDef = isDev2 ? [
1195
- `
1196
- // Dev-only Error Boundary to catch React render errors`,
1197
- `class ErrorBoundary extends Component {`,
1198
- ` constructor(props) {`,
1199
- ` super(props);`,
1200
- ` this.state = { hasError: false };`,
1201
- ` window.__ERROR_BOUNDARY__ = this;`,
1202
- ` }`,
1203
- ` static getDerivedStateFromError() {`,
1204
- ` return { hasError: true };`,
1205
- ` }`,
1206
- ` componentDidCatch(error) {`,
1207
- ` showErrorOverlay({`,
1208
- ` framework: 'react',`,
1209
- ` kind: 'runtime',`,
1210
- ` message: error && error.stack ? error.stack : String(error)`,
1211
- ` });`,
1212
- ` }`,
1213
- ` componentDidUpdate(prevProps, prevState) {`,
1214
- ` if (prevState.hasError && !this.state.hasError) {`,
1215
- ` hideErrorOverlay();`,
1216
- ` }`,
1217
- ` }`,
1218
- ` reset() {`,
1219
- ` this.setState({ hasError: false });`,
1220
- ` }`,
1221
- ` render() {`,
1222
- ` if (this.state.hasError) return null;`,
1223
- ``,
1224
- ` return this.props.children;`,
1225
- ` }`,
1226
- `}
1227
- `
1228
- ] : [];
1229
- const content = [
1230
- ...hmrPreamble,
1231
- ...reactImports,
1232
- `import type { ComponentType } from 'react'`,
1233
- `import { ${componentName} } from '${pagesRelPath}/${componentName}';
1234
- `,
1235
- `type PropsOf<C> = C extends ComponentType<infer P> ? P : never;
1236
- `,
1237
- `declare global {`,
1238
- ` interface Window {`,
1239
- ` __INITIAL_PROPS__?: PropsOf<typeof ${componentName}>`,
1240
- ` __REACT_ROOT__?: ReturnType<typeof hydrateRoot | typeof createRoot>`,
1241
- ` __HMR_CLIENT_ONLY_MODE__?: boolean`,
1242
- ` }`,
1243
- `}
1244
- `,
1245
- ...errorBoundaryDef,
1246
- `// Hydration with error handling and fallback`,
1247
- `const isDev = ${isDev2};`,
1248
- `const componentPath = '${pagesRelPath}/${componentName}';
1249
- `,
1250
- `function isHydrationError(error) {`,
1251
- ` if (!error) return false;`,
1252
- ` const errorMessage = error instanceof Error ? error.message : String(error);`,
1253
- ` const errorString = String(error);`,
1254
- ` const fullMessage = errorMessage + ' ' + errorString;`,
1255
- ` const hydrationKeywords = ['hydration', 'Hydration', 'mismatch', 'Mismatch', 'did not match', 'server rendered HTML', 'server HTML', 'client HTML', 'Hydration failed'];`,
1256
- ` const isHydration = hydrationKeywords.some(keyword => fullMessage.includes(keyword));`,
1257
- ` `,
1258
- ` // Ignore whitespace-only mismatches in <head> - these are harmless formatting differences`,
1259
- ` // The error often shows: + <link...> vs - {"\\n "} which is just formatting`,
1260
- ` if (isHydration) {`,
1261
- ` // Check if this is a head/link/stylesheet related mismatch`,
1262
- ` const isHeadRelated = fullMessage.includes('<head') || fullMessage.includes('</head>') || fullMessage.includes('head>') || fullMessage.includes('<link') || fullMessage.includes('link>') || fullMessage.includes('stylesheet') || fullMessage.includes('fonts.googleapis') || fullMessage.includes('rel="stylesheet"');`,
1263
- ` `,
1264
- ` // Check if the mismatch involves only whitespace/newlines`,
1265
- ` // Pattern: looks for {"\\n"} or {"\\n "} or similar whitespace-only content`,
1266
- ` // Also check for patterns like: - {"\\n "} or + <link...>`,
1267
- ` const hasWhitespacePattern = /\\{\\s*["']\\\\n[^"']*["']\\s*\\}/.test(fullMessage) || /\\{\\s*["'][\\\\n\\\\r\\\\s]+["']\\s*\\}/.test(fullMessage) || /-\\s*\\{\\s*["'][\\\\n\\\\r\\\\s]+["']\\s*\\}/.test(fullMessage);`,
1268
- ` const isWhitespaceOnly = /^[\\s\\n\\r]*$/.test(errorString) || /^[\\s\\n\\r]*$/.test(errorMessage);`,
1269
- ` const hasNewlinePattern = fullMessage.includes('\\\\n') || fullMessage.includes('\\\\r') || fullMessage.includes('\\n') || fullMessage.includes('\\r');`,
1270
- ` `,
1271
- ` // If it's head-related and involves whitespace/newlines, ignore it`,
1272
- ` if (isHeadRelated && (hasWhitespacePattern || isWhitespaceOnly || hasNewlinePattern)) {`,
1273
- ` return false; // Don't treat whitespace-only head mismatches as errors`,
1274
- ` }`,
1275
- ` }`,
1276
- ` return isHydration;`,
1277
- `}
1278
- `,
1279
- `function logHydrationError(error, componentName) {`,
1280
- ` if (!isDev) return;`,
1281
- ` if (window.__HMR_WS__ && window.__HMR_WS__.readyState === WebSocket.OPEN) {`,
1282
- ` try {`,
1283
- ` window.__HMR_WS__.send(JSON.stringify({`,
1284
- ` type: 'hydration-error',`,
1285
- ` data: {`,
1286
- ` componentName: '${componentName}',`,
1287
- ` componentPath: componentPath,`,
1288
- ` error: error instanceof Error ? error.message : String(error),`,
1289
- ` timestamp: Date.now()`,
1290
- ` }`,
1291
- ` }));`,
1292
- ` } catch (err) {}`,
1293
- ` }`,
1294
- `}
1295
- `,
1296
- `// Track if we've already switched to client-only mode`,
1297
- `let hasSwitchedToClientOnly = false;`,
1298
- `let hydrationErrorDetected = false;
1299
- `,
1300
- `function handleHydrationFallback(error) {`,
1301
- ` if (hasSwitchedToClientOnly) return; // Already handled`,
1302
- ` hasSwitchedToClientOnly = true;`,
1303
- ` hydrationErrorDetected = true;
1304
- `,
1305
- ` logHydrationError(error, '${componentName}');
1306
- `,
1307
- ` // Fallback: client-only render (no hydration)`,
1308
- ` try {`,
1309
- ` // Unmount existing root if it exists`,
1310
- ` if (window.__REACT_ROOT__ && typeof window.__REACT_ROOT__.unmount === 'function') {`,
1311
- ` try {`,
1312
- ` window.__REACT_ROOT__.unmount();`,
1313
- ` } catch (e) {`,
1314
- ` // Ignore unmount errors`,
1315
- ` }`,
1316
- ` }
1317
- `,
1318
- ` // Render into the same root container when falling back to client-only`,
1319
- ` const root = createRoot(container);`,
1320
- ` root.render(${isDev2 ? `createElement(ErrorBoundary, null, createElement(${componentName}, mergedProps))` : `createElement(${componentName}, mergedProps)`});`,
1321
- ` window.__REACT_ROOT__ = root;`,
1322
- ` window.__HMR_CLIENT_ONLY_MODE__ = true;`,
1323
- ` } catch (fallbackError) {`,
1324
- ` window.location.reload();`,
1325
- ` }`,
1326
- `}
1327
- `,
1328
- `// HMR State Preservation: Check for preserved state and merge with initial props`,
1329
- `// This allows state to be preserved across HMR updates without modifying component files`,
1330
- `let preservedState = (typeof window !== 'undefined' && window.__HMR_PRESERVED_STATE__) ? window.__HMR_PRESERVED_STATE__ : {};
1331
- `,
1332
- `// Also check sessionStorage for state that survived a page reload (for React HMR)`,
1333
- `if (typeof window !== 'undefined' && typeof sessionStorage !== 'undefined') {`,
1334
- ` const hmrStateJson = sessionStorage.getItem('__REACT_HMR_STATE__');`,
1335
- ` if (hmrStateJson) {`,
1336
- ` try {`,
1337
- ` const hmrState = JSON.parse(hmrStateJson);`,
1338
- ` preservedState = { ...preservedState, ...hmrState };`,
1339
- ` sessionStorage.removeItem('__REACT_HMR_STATE__');`,
1340
- ` } catch (e) {}`,
1341
- ` }`,
1342
- `}
1343
- `,
1344
- `const mergedProps = { ...(window.__INITIAL_PROPS__ || {}), ...preservedState };`,
1345
- `// Clear preserved state after using it (so it doesn't persist across multiple updates)`,
1346
- `if (typeof window !== 'undefined') {`,
1347
- ` window.__HMR_PRESERVED_STATE__ = undefined;`,
1348
- `}
1349
- `,
1350
- `// Attempt hydration with error handling`,
1351
- `// Use document (not document.body) when the page renders <html><head><body>`,
1352
- `// to avoid "In HTML, <html> cannot be a child of <body>" hydration error`,
1353
- `const container = typeof document !== 'undefined' ? document : null;`,
1354
- `if (!container) {`,
1355
- ` throw new Error('React root container not found: document is null');`,
1356
- `}
1357
- `,
1358
- `// Guard: only hydrate on first load. During HMR re-imports, skip hydration`,
1359
- `// so React Fast Refresh can swap components in-place and preserve state.`,
1360
- `if (!window.__REACT_ROOT__) {`,
1361
- ` let root;`,
1362
- ` // After HMR, SSR is skipped to avoid stale content flash \u2014 render client-only`,
1363
- ` if (window.__SSR_DIRTY__) {`,
1364
- ` root = createRoot(container);`,
1365
- ` root.render(${isDev2 ? `createElement(ErrorBoundary, null, createElement(${componentName}, mergedProps))` : `createElement(${componentName}, mergedProps)`});`,
1366
- ` window.__REACT_ROOT__ = root;`,
1367
- ` } else {`,
1368
- ` try {`,
1369
- ` // Use onRecoverableError to catch hydration errors (React 19)`,
1370
- ` root = hydrateRoot(`,
1371
- ` container,`,
1372
- ` ${isDev2 ? `createElement(ErrorBoundary, null, createElement(${componentName}, mergedProps))` : `createElement(${componentName}, mergedProps)`},`,
1373
- ` {`,
1374
- ` onRecoverableError: (error) => {`,
1375
- ` // Check if this is a hydration error (isHydrationError filters out whitespace-only head mismatches)`,
1376
- ` if (isDev && isHydrationError(error)) {`,
1377
- ` // Real hydration error - handle it`,
1378
- ` handleHydrationFallback(error);`,
1379
- ` } else {`,
1380
- ` // Not a hydration error, or it's a whitespace-only mismatch that was filtered out`,
1381
- ` // Check if it's a whitespace-only head mismatch using the same logic as isHydrationError`,
1382
- ` const errorMessage = error instanceof Error ? error.message : String(error);`,
1383
- ` const errorString = String(error);`,
1384
- ` const fullMessage = errorMessage + ' ' + errorString;`,
1385
- ` const hydrationKeywords = ['hydration', 'Hydration', 'mismatch', 'Mismatch', 'did not match', 'server rendered HTML', 'server HTML', 'client HTML', 'Hydration failed'];`,
1386
- ` const isHydration = hydrationKeywords.some(keyword => fullMessage.includes(keyword));`,
1387
- ` if (isHydration) {`,
1388
- ` // Check if this is a head/link/stylesheet related mismatch`,
1389
- ` const isHeadRelated = fullMessage.includes('<head') || fullMessage.includes('</head>') || fullMessage.includes('head>') || fullMessage.includes('<link') || fullMessage.includes('link>') || fullMessage.includes('stylesheet') || fullMessage.includes('fonts.googleapis') || fullMessage.includes('rel="stylesheet"');`,
1390
- ` // Check if the mismatch involves only whitespace/newlines`,
1391
- ` const hasWhitespacePattern = /\\{\\s*["']\\\\n[^"']*["']\\s*\\}/.test(fullMessage) || /\\{\\s*["'][\\\\n\\\\r\\\\s]+["']\\s*\\}/.test(fullMessage) || /-\\s*\\{\\s*["'][\\\\n\\\\r\\\\s]+["']\\s*\\}/.test(fullMessage);`,
1392
- ` const isWhitespaceOnly = /^[\\s\\n\\r]*$/.test(errorString) || /^[\\s\\n\\r]*$/.test(errorMessage);`,
1393
- ` const hasNewlinePattern = fullMessage.includes('\\\\n') || fullMessage.includes('\\\\r') || fullMessage.includes('\\n') || fullMessage.includes('\\r');`,
1394
- ` // If it's head-related and involves whitespace/newlines, silently ignore it`,
1395
- ` if (isHeadRelated && (hasWhitespacePattern || isWhitespaceOnly || hasNewlinePattern)) {`,
1396
- ` // Already logged by isHydrationError, just return silently`,
1397
- ` return;`,
1398
- ` }`,
1399
- ` }`,
1400
- ` // Log other recoverable errors`,
1401
- ` console.error('React recoverable error:', error);`,
1402
- ` }`,
1403
- ` }`,
1404
- ` }`,
1405
- ` );`,
1406
- ` window.__REACT_ROOT__ = root;`,
1407
- ` } catch (error) {`,
1408
- ` // Catch synchronous errors (shouldn't happen with hydrateRoot, but safety net)`,
1409
- ` if (isDev && isHydrationError(error)) {`,
1410
- ` handleHydrationFallback(error);`,
1411
- ` } else {`,
1412
- ` throw error;`,
1413
- ` }`,
1414
- ` }`,
1415
- ` } // end else (normal hydration path)
1416
- `,
1417
- ` // Also listen for hydration errors via console.error (React logs them there)`,
1418
- ` if (isDev) {`,
1419
- ` const originalError = console.error;`,
1420
- ` console.error = function(...args) {`,
1421
- ` const errorMessage = args.map(arg => {`,
1422
- ` if (arg instanceof Error) return arg.message;`,
1423
- ` return String(arg);`,
1424
- ` }).join(' ');`,
1425
- ` `,
1426
- ` // Check if this is a hydration error`,
1427
- ` if (isHydrationError({ message: errorMessage }) && !hydrationErrorDetected) {`,
1428
- ` hydrationErrorDetected = true;`,
1429
- ` // Create a synthetic error for fallback`,
1430
- ` const syntheticError = new Error(errorMessage);`,
1431
- ` // Use setTimeout to ensure this happens after React's error handling`,
1432
- ` setTimeout(() => {`,
1433
- ` handleHydrationFallback(syntheticError);`,
1434
- ` }, 0);`,
1435
- ` }`,
1436
- ` `,
1437
- ` // Call original console.error`,
1438
- ` originalError.apply(console, args);`,
1439
- ` };`,
1440
- ` }`,
1441
- `}`,
1442
- ...isDev2 ? [
1443
- `
1444
- // Pre-warm: import the page module from the module server`,
1445
- `// immediately so the browser caches all /@src/ URLs.`,
1446
- `import('/@src/${relative2(process.cwd(), resolve6(reactPagesDirectory, `${componentName}.tsx`)).replace(/\\/g, "/")}').catch(() => {});`
1447
- ] : []
1448
- ].join(`
1449
- `);
1450
- const indexPath = join2(reactIndexesDirectory, `${componentName}.tsx`);
1451
- const hasher = new Bun.CryptoHasher("md5");
1452
- hasher.update(content);
1453
- const contentHash = hasher.digest("hex");
1454
- if (indexContentCache.get(indexPath) === contentHash && existsSync3(indexPath)) {
1455
- return;
1456
- }
1457
- indexContentCache.set(indexPath, contentHash);
1458
- await writeFile(indexPath, content);
1459
- });
1460
- await Promise.all(promises);
1461
- if (!isDev2) {
1462
- return;
1463
- }
1464
- const refreshPath = join2(reactIndexesDirectory, "_refresh.tsx");
1465
- if (!existsSync3(refreshPath)) {
1466
- await writeFile(refreshPath, `import '${refreshSetupPath}';
1467
- import 'react';
1468
- import 'react-dom/client';
1469
- `);
1470
- }
1471
- };
1472
- var init_generateReactIndexes = __esm(() => {
1473
- indexContentCache = new Map;
1474
- devClientDir = resolveDevClientDir();
1475
- errorOverlayPath = join2(devClientDir, "errorOverlay.ts").replace(/\\/g, "/");
1476
- hmrClientPath = join2(devClientDir, "hmrClient.ts").replace(/\\/g, "/");
1477
- refreshSetupPath = join2(devClientDir, "reactRefreshSetup.ts").replace(/\\/g, "/");
1478
- });
1479
-
1480
- // src/build/islandBindingCompat.ts
1481
- import { resolve as resolve7 } from "path";
1482
- var packageToFramework, compatFileNames, normalize = (value) => value.replace(/\\/g, "/"), isFrameworkPackage = (value) => (value in packageToFramework), resolveIslandCompatModule = (specifier, importer, frameworkDirs) => {
1483
- if (!isFrameworkPackage(specifier)) {
1484
- return null;
1485
- }
1486
- const framework = packageToFramework[specifier];
1487
- const frameworkDir = frameworkDirs[framework];
1488
- if (!frameworkDir) {
1489
- return null;
1490
- }
1491
- const normalizedImporter = normalize(importer);
1492
- const normalizedFrameworkDir = normalize(resolve7(frameworkDir));
1493
- if (!normalizedImporter.startsWith(normalizedFrameworkDir)) {
1494
- return null;
1495
- }
1496
- if (normalizedImporter.includes("/generated/absolute-") || normalizedImporter.includes("/generated/Island.") || normalizedImporter.includes("/generated/islands.")) {
1497
- return null;
1498
- }
1499
- return resolve7(frameworkDir, "generated", compatFileNames[framework]);
1500
- };
1501
- var init_islandBindingCompat = __esm(() => {
1502
- packageToFramework = {
1503
- "@absolutejs/absolute/angular": "angular",
1504
- "@absolutejs/absolute/react": "react",
1505
- "@absolutejs/absolute/svelte": "svelte",
1506
- "@absolutejs/absolute/vue": "vue"
1507
- };
1508
- compatFileNames = {
1509
- angular: "absolute-angular.ts",
1510
- react: "absolute-react.ts",
1511
- svelte: "absolute-svelte.ts",
1512
- vue: "absolute-vue.ts"
1513
- };
1514
- });
1515
-
1516
- // src/build/islandBindingPlugin.ts
1517
- var exports_islandBindingPlugin = {};
1518
- __export(exports_islandBindingPlugin, {
1519
- createIslandBindingPlugin: () => createIslandBindingPlugin
1520
- });
1521
- var createIslandBindingPlugin = (frameworkDirs) => ({
1522
- name: "absolute-island-binding-plugin",
1523
- setup(build2) {
1524
- build2.onResolve({
1525
- filter: /^@absolutejs\/absolute\/(react|vue|svelte|angular)$/
1526
- }, (args) => {
1527
- const redirected = resolveIslandCompatModule(args.path, args.importer, frameworkDirs);
1528
- if (!redirected) {
1529
- return;
1530
- }
1531
- return {
1532
- path: redirected
1533
- };
1534
- });
1535
- }
1536
- });
1537
- var init_islandBindingPlugin = __esm(() => {
1538
- init_islandBindingCompat();
1539
- });
1540
-
1541
- // src/build/wrapHTMLScript.ts
1542
- var wrapHTMLScriptWithHMR = (code, scriptId) => {
1543
- const escapedId = JSON.stringify(scriptId);
1544
- return `${code}
1545
-
1546
- // HMR acceptance - allows this script to be hot-reloaded
1547
- if (typeof import.meta !== "undefined" && import.meta.hot) {
1548
- import.meta.hot.accept();
1549
- console.log('[HMR] Script ready:', ${escapedId});
1550
- }
1551
- `;
1552
- };
1553
-
1554
- // src/build/htmlScriptHMRPlugin.ts
1555
- var scriptLoaders, toLoader = (ext) => {
1556
- for (const loader of scriptLoaders) {
1557
- if (loader === ext)
1558
- return loader;
1559
- }
1560
- return "ts";
1561
- }, createHTMLScriptHMRPlugin = (htmlDir, htmxDir) => ({
1562
- name: "html-script-hmr",
1563
- setup(build2) {
1564
- build2.onLoad({ filter: /\.(ts|js|tsx|jsx)$/ }, async (args) => {
1565
- const normalizedPath = args.path.replace(/\\/g, "/");
1566
- const isHtmlScript = htmlDir && normalizedPath.includes(htmlDir.replace(/\\/g, "/")) && normalizedPath.includes("/scripts/");
1567
- const isHtmxScript = htmxDir && normalizedPath.includes(htmxDir.replace(/\\/g, "/")) && normalizedPath.includes("/scripts/");
1568
- if (!isHtmlScript && !isHtmxScript) {
1569
- return;
1570
- }
1571
- const text = await Bun.file(args.path).text();
1572
- const wrapped = wrapHTMLScriptWithHMR(text, normalizedPath);
1573
- const ext = args.path.split(".").pop() ?? "ts";
1574
- const loader = toLoader(ext);
1575
- return {
1576
- contents: wrapped,
1577
- loader
1578
- };
1579
- });
1580
- }
1581
- });
1582
- var init_htmlScriptHMRPlugin = __esm(() => {
1583
- scriptLoaders = new Set(["ts", "js", "tsx", "jsx"]);
1584
- });
1585
-
1586
206
  // src/angular/injectorPatch.ts
1587
- import { existsSync as existsSync4, readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "fs";
1588
- import { dirname as dirname4, join as join3, resolve as resolve8 } from "path";
207
+ import { existsSync, readFileSync, writeFileSync } from "fs";
208
+ import { dirname, join, resolve } from "path";
1589
209
  var applyInjectorPatch = (chunkPath, content) => {
1590
210
  if (content.includes('Symbol.for("angular.currentInjector")')) {
1591
211
  return;
@@ -1619,18 +239,18 @@ var applyInjectorPatch = (chunkPath, content) => {
1619
239
  if (patched === content) {
1620
240
  return;
1621
241
  }
1622
- writeFileSync3(chunkPath, patched, "utf-8");
242
+ writeFileSync(chunkPath, patched, "utf-8");
1623
243
  }, resolveAngularCoreDir = () => {
1624
- const fromProject = resolve8(process.cwd(), "node_modules/@angular/core");
1625
- if (existsSync4(join3(fromProject, "package.json"))) {
244
+ const fromProject = resolve(process.cwd(), "node_modules/@angular/core");
245
+ if (existsSync(join(fromProject, "package.json"))) {
1626
246
  return fromProject;
1627
247
  }
1628
- return dirname4(__require.resolve("@angular/core/package.json"));
248
+ return dirname(__require.resolve("@angular/core/package.json"));
1629
249
  }, patchAngularInjectorSingleton = () => {
1630
250
  try {
1631
251
  const coreDir = resolveAngularCoreDir();
1632
- const chunkPath = join3(coreDir, "fesm2022", "_not_found-chunk.mjs");
1633
- const content = readFileSync2(chunkPath, "utf-8");
252
+ const chunkPath = join(coreDir, "fesm2022", "_not_found-chunk.mjs");
253
+ const content = readFileSync(chunkPath, "utf-8");
1634
254
  applyInjectorPatch(chunkPath, content);
1635
255
  } catch {}
1636
256
  };
@@ -1639,11 +259,11 @@ var init_injectorPatch = __esm(() => {
1639
259
  });
1640
260
 
1641
261
  // src/angular/resolveAngularPackage.ts
1642
- import { existsSync as existsSync5 } from "fs";
1643
- import { resolve as resolve9 } from "path";
262
+ import { existsSync as existsSync2 } from "fs";
263
+ import { resolve as resolve2 } from "path";
1644
264
  var resolveAngularPackage = (specifier) => {
1645
- const fromProject = resolve9(process.cwd(), "node_modules", specifier);
1646
- if (existsSync5(fromProject)) {
265
+ const fromProject = resolve2(process.cwd(), "node_modules", specifier);
266
+ if (existsSync2(fromProject)) {
1647
267
  return fromProject;
1648
268
  }
1649
269
  return specifier;
@@ -1737,6 +357,14 @@ var init_angularDeps = __esm(() => {
1737
357
  init_resolveAngularPackage();
1738
358
  });
1739
359
 
360
+ // src/utils/stringModifiers.ts
361
+ var normalizeSlug = (str) => str.trim().replace(/\s+/g, "-").replace(/[^A-Za-z0-9\-_]+/g, "").replace(/[-_]{2,}/g, "-"), toKebab = (str) => normalizeSlug(str).replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase(), toPascal = (str) => {
362
+ if (!str.includes("-") && !str.includes("_")) {
363
+ return str.charAt(0).toUpperCase() + str.slice(1);
364
+ }
365
+ return normalizeSlug(str).split(/[-_]/).filter(Boolean).map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1).toLowerCase()).join("");
366
+ }, toScreamingSnake = (str) => str.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toUpperCase();
367
+
1740
368
  // src/utils/registerClientScript.ts
1741
369
  var scriptRegistry, requestCounter = 0, getRequestId = () => `req_${Date.now()}_${++requestCounter}`, ssrContextGetter = null, getSsrContextId = () => ssrContextGetter?.() || Object.getOwnPropertyDescriptor(globalThis, "__absolutejs_requestId")?.value, registerClientScript = (script, requestId) => {
1742
370
  const id = requestId || getSsrContextId() || getRequestId();
@@ -3278,8 +1906,8 @@ class SourceMapGenerator {
3278
1906
  lines = [];
3279
1907
  lastCol0 = 0;
3280
1908
  hasMappings = false;
3281
- constructor(file2 = null) {
3282
- this.file = file2;
1909
+ constructor(file = null) {
1910
+ this.file = file;
3283
1911
  }
3284
1912
  addSource(url, content = null) {
3285
1913
  if (!this.sourcesContent.has(url)) {
@@ -5172,8 +3800,8 @@ class ParseLocation {
5172
3800
  offset;
5173
3801
  line;
5174
3802
  col;
5175
- constructor(file2, offset, line, col) {
5176
- this.file = file2;
3803
+ constructor(file, offset, line, col) {
3804
+ this.file = file;
5177
3805
  this.offset = offset;
5178
3806
  this.line = line;
5179
3807
  this.col = col;
@@ -5906,12 +4534,12 @@ class ShadowCss {
5906
4534
  let scopedSelector = "";
5907
4535
  let startIndex = 0;
5908
4536
  let res;
5909
- const sep2 = /( |>|\+|~(?!=))(?!([^)(]*(?:\([^)(]*(?:\([^)(]*(?:\([^)(]*\)[^)(]*)*\)[^)(]*)*\)[^)(]*)*\)))\s*/g;
4537
+ const sep = /( |>|\+|~(?!=))(?!([^)(]*(?:\([^)(]*(?:\([^)(]*(?:\([^)(]*\)[^)(]*)*\)[^)(]*)*\)[^)(]*)*\)))\s*/g;
5910
4538
  const hasHost = selector.includes(_polyfillHostNoCombinator);
5911
4539
  if (isParentSelector || this._shouldScopeIndicator) {
5912
4540
  this._shouldScopeIndicator = !hasHost;
5913
4541
  }
5914
- while ((res = sep2.exec(selector)) !== null) {
4542
+ while ((res = sep.exec(selector)) !== null) {
5915
4543
  const separator = res[1];
5916
4544
  const part2 = selector.slice(startIndex, res.index);
5917
4545
  if (part2.match(/__esc-ph-(\d+)__/) && selector[res.index + 1]?.match(/[a-fA-F\d]/)) {
@@ -5919,7 +4547,7 @@ class ShadowCss {
5919
4547
  }
5920
4548
  const scopedPart = _pseudoFunctionAwareScopeSelectorPart(part2);
5921
4549
  scopedSelector += `${scopedPart} ${separator} `;
5922
- startIndex = sep2.lastIndex;
4550
+ startIndex = sep.lastIndex;
5923
4551
  }
5924
4552
  const part = selector.substring(startIndex);
5925
4553
  scopedSelector += _pseudoFunctionAwareScopeSelectorPart(part);
@@ -22376,13 +21004,13 @@ function getTemplateExpression(template2, templateInfo) {
22376
21004
  return literal(templateInfo.content, null, null);
22377
21005
  }
22378
21006
  const contents = templateInfo.content;
22379
- const file2 = new ParseSourceFile(contents, templateInfo.sourceUrl);
22380
- const start = new ParseLocation(file2, 0, 0, 0);
22381
- const end = computeEndLocation(file2, contents);
21007
+ const file = new ParseSourceFile(contents, templateInfo.sourceUrl);
21008
+ const start = new ParseLocation(file, 0, 0, 0);
21009
+ const end = computeEndLocation(file, contents);
22382
21010
  const span = new ParseSourceSpan(start, end);
22383
21011
  return literal(contents, null, span);
22384
21012
  }
22385
- function computeEndLocation(file2, contents) {
21013
+ function computeEndLocation(file, contents) {
22386
21014
  const length = contents.length;
22387
21015
  let lineStart = 0;
22388
21016
  let lastLineStart = 0;
@@ -22395,7 +21023,7 @@ function computeEndLocation(file2, contents) {
22395
21023
  line++;
22396
21024
  }
22397
21025
  } while (lineStart !== -1);
22398
- return new ParseLocation(file2, length, line, length - lastLineStart);
21026
+ return new ParseLocation(file, length, line, length - lastLineStart);
22399
21027
  }
22400
21028
  function compileUsedDependenciesMetadata(meta) {
22401
21029
  const wrapType = meta.declarationListEmitMode !== 0 ? generateForwardRef : (expr) => expr;
@@ -30916,7 +29544,7 @@ If '${name}' is a directive input, make sure the directive is imported by the` +
30916
29544
  transUnits.push(new CR(6), transUnit);
30917
29545
  });
30918
29546
  const body = new Tag("body", {}, [...transUnits, new CR(4)]);
30919
- const file2 = new Tag("file", {
29547
+ const file = new Tag("file", {
30920
29548
  "source-language": locale || _DEFAULT_SOURCE_LANG$1,
30921
29549
  datatype: "plaintext",
30922
29550
  original: "ng2.template"
@@ -30924,7 +29552,7 @@ If '${name}' is a directive input, make sure the directive is imported by the` +
30924
29552
  const xliff = new Tag("xliff", {
30925
29553
  version: _VERSION$1,
30926
29554
  xmlns: _XMLNS$1
30927
- }, [new CR(2), file2, new CR]);
29555
+ }, [new CR(2), file, new CR]);
30928
29556
  return serialize$1([new Declaration({
30929
29557
  version: "1.0",
30930
29558
  encoding: "UTF-8"
@@ -30994,7 +29622,7 @@ ${errors.join(`
30994
29622
  unit.children.push(new CR(6), segment, new CR(4));
30995
29623
  units.push(new CR(4), unit);
30996
29624
  });
30997
- const file2 = new Tag("file", {
29625
+ const file = new Tag("file", {
30998
29626
  original: "ng.template",
30999
29627
  id: "ngi18n"
31000
29628
  }, [...units, new CR(2)]);
@@ -31002,7 +29630,7 @@ ${errors.join(`
31002
29630
  version: _VERSION,
31003
29631
  xmlns: _XMLNS,
31004
29632
  srcLang: locale || _DEFAULT_SOURCE_LANG
31005
- }, [new CR(2), file2, new CR]);
29633
+ }, [new CR(2), file, new CR]);
31006
29634
  return serialize$1([new Declaration({
31007
29635
  version: "1.0",
31008
29636
  encoding: "UTF-8"
@@ -31338,414 +29966,1789 @@ var ISLAND_TAG_RE, extractIslandAttribute = (attributeString, name) => {
31338
29966
  if (quotedMatch?.[1]) {
31339
29967
  return { expression: JSON.stringify(quotedMatch[1]), found: true };
31340
29968
  }
31341
- const attributeIndex = attributeString.search(new RegExp(`\\b${name}\\s*=\\s*\\{`));
31342
- if (attributeIndex >= 0) {
31343
- const braceStart = attributeString.indexOf("{", attributeIndex);
31344
- if (braceStart >= 0) {
31345
- let depth = 0;
31346
- for (let index = braceStart;index < attributeString.length; index += 1) {
31347
- const char = attributeString[index];
31348
- if (char === "{") {
31349
- depth += 1;
31350
- }
31351
- if (char === "}") {
31352
- depth -= 1;
31353
- if (depth === 0) {
31354
- return {
31355
- expression: attributeString.slice(braceStart + 1, index).trim(),
31356
- found: true
31357
- };
31358
- }
31359
- }
31360
- }
31361
- }
29969
+ const attributeIndex = attributeString.search(new RegExp(`\\b${name}\\s*=\\s*\\{`));
29970
+ if (attributeIndex >= 0) {
29971
+ const braceStart = attributeString.indexOf("{", attributeIndex);
29972
+ if (braceStart >= 0) {
29973
+ let depth = 0;
29974
+ for (let index = braceStart;index < attributeString.length; index += 1) {
29975
+ const char = attributeString[index];
29976
+ if (char === "{") {
29977
+ depth += 1;
29978
+ }
29979
+ if (char === "}") {
29980
+ depth -= 1;
29981
+ if (depth === 0) {
29982
+ return {
29983
+ expression: attributeString.slice(braceStart + 1, index).trim(),
29984
+ found: true
29985
+ };
29986
+ }
29987
+ }
29988
+ }
29989
+ }
29990
+ }
29991
+ return { expression: "", found: false };
29992
+ }, lowerSvelteIslandSyntax = (source, mode = "server") => {
29993
+ if (!source.includes("<Island")) {
29994
+ return { code: source, transformed: false };
29995
+ }
29996
+ let islandIndex = 0;
29997
+ const transformedMarkup = source.replace(ISLAND_TAG_RE, (fullMatch, attributeString) => {
29998
+ const framework = extractIslandAttribute(attributeString, "framework");
29999
+ const component = extractIslandAttribute(attributeString, "component");
30000
+ if (!framework.found || !component.found) {
30001
+ return fullMatch;
30002
+ }
30003
+ const hydrate = extractIslandAttribute(attributeString, "hydrate");
30004
+ const props = extractIslandAttribute(attributeString, "props");
30005
+ const slotId = `absolute-svelte-island-${islandIndex.toString(BASE_36_RADIX)}`;
30006
+ islandIndex += 1;
30007
+ const resolveExpression = `await __absoluteResolveIslandHtml(${JSON.stringify(slotId)}, { component: ${component.expression}, framework: ${framework.expression}, hydrate: ${hydrate.found ? hydrate.expression : JSON.stringify("load")}, props: ${props.found ? props.expression : "{}"} })`;
30008
+ return `<div data-absolute-island-slot="${slotId}" style="display: contents">{@html ${resolveExpression}}</div>`;
30009
+ });
30010
+ const importLine = 'import { resolveIslandHtml as __absoluteResolveIslandHtml } from "@absolutejs/absolute/svelte";';
30011
+ if (transformedMarkup.includes("<script")) {
30012
+ return {
30013
+ code: transformedMarkup.replace(/<script(\s[^>]*)?>/, (match) => `${match}
30014
+ ${importLine}
30015
+ `),
30016
+ transformed: true
30017
+ };
30018
+ }
30019
+ return {
30020
+ code: `<script lang="ts">
30021
+ ${importLine}
30022
+ </script>
30023
+ ${transformedMarkup}`,
30024
+ transformed: true
30025
+ };
30026
+ };
30027
+ var init_lowerIslandSyntax = __esm(() => {
30028
+ init_constants();
30029
+ ISLAND_TAG_RE = /<Island\b([\s\S]*?)\/>/g;
30030
+ });
30031
+
30032
+ // src/core/svelteServerModule.ts
30033
+ import { mkdir } from "fs/promises";
30034
+ import { dirname as dirname2, extname, join as join2, relative, resolve as resolve3 } from "path";
30035
+ var serverCacheRoot, compiledModuleCache, transpiler, ensureRelativeImportPath = (from, to) => {
30036
+ const importPath = relative(dirname2(from), to).replace(/\\/g, "/");
30037
+ return importPath.startsWith(".") ? importPath : `./${importPath}`;
30038
+ }, resolveRelativeModule = async (spec, from) => {
30039
+ if (!spec.startsWith(".")) {
30040
+ return null;
30041
+ }
30042
+ const basePath = resolve3(dirname2(from), spec);
30043
+ const candidates = [
30044
+ basePath,
30045
+ `${basePath}.ts`,
30046
+ `${basePath}.js`,
30047
+ `${basePath}.mjs`,
30048
+ `${basePath}.cjs`,
30049
+ `${basePath}.json`,
30050
+ join2(basePath, "index.ts"),
30051
+ join2(basePath, "index.js"),
30052
+ join2(basePath, "index.mjs"),
30053
+ join2(basePath, "index.cjs"),
30054
+ join2(basePath, "index.json")
30055
+ ];
30056
+ for (const candidate of candidates) {
30057
+ if (await Bun.file(candidate).exists() === true) {
30058
+ return candidate;
30059
+ }
30060
+ }
30061
+ return null;
30062
+ }, getCachedModulePath = (sourcePath) => {
30063
+ const relativeSourcePath = relative(process.cwd(), sourcePath).replace(/\\/g, "/");
30064
+ const normalizedSourcePath = relativeSourcePath.startsWith("..") ? sourcePath.replace(/[:\\/]/g, "_") : relativeSourcePath;
30065
+ return join2(serverCacheRoot, `${normalizedSourcePath}.server.js`);
30066
+ }, resolveSvelteImport = async (spec, from) => {
30067
+ if (spec.startsWith("/")) {
30068
+ return spec;
30069
+ }
30070
+ if (!spec.startsWith(".")) {
30071
+ return null;
30072
+ }
30073
+ const explicitPath = resolve3(dirname2(from), spec);
30074
+ if (extname(explicitPath) === ".svelte") {
30075
+ return explicitPath;
30076
+ }
30077
+ const candidate = `${explicitPath}.svelte`;
30078
+ if (await Bun.file(candidate).exists() === true) {
30079
+ return candidate;
30080
+ }
30081
+ return null;
30082
+ }, writeIfChanged = async (path, content) => {
30083
+ const targetFile = Bun.file(path);
30084
+ const exists = await targetFile.exists();
30085
+ if (exists) {
30086
+ const currentContent = await targetFile.text();
30087
+ if (currentContent === content) {
30088
+ return;
30089
+ }
30090
+ }
30091
+ await Bun.write(path, content);
30092
+ }, compileSvelteServerModule = async (sourcePath) => {
30093
+ const cachedModulePath = compiledModuleCache.get(sourcePath);
30094
+ if (cachedModulePath) {
30095
+ return cachedModulePath;
30096
+ }
30097
+ const source = await Bun.file(sourcePath).text();
30098
+ const { compile, preprocess } = await import("svelte/compiler");
30099
+ const loweredSource = lowerSvelteIslandSyntax(source, "server");
30100
+ const preprocessed = await preprocess(loweredSource.code, {});
30101
+ const transpiled = sourcePath.endsWith(".ts") || sourcePath.endsWith(".svelte.ts") ? transpiler.transformSync(preprocessed.code) : preprocessed.code;
30102
+ const childImportSpecs = Array.from(transpiled.matchAll(/from\s+['"]([^'"]+)['"]/g)).map((match) => match[1]).filter((value) => value !== undefined);
30103
+ const resolvedChildModules = await Promise.all(childImportSpecs.map((spec) => resolveSvelteImport(spec, sourcePath)));
30104
+ const resolvedModuleImports = await Promise.all(childImportSpecs.map((spec) => resolveRelativeModule(spec, sourcePath)));
30105
+ const childModulePaths = new Map;
30106
+ const rewrittenModulePaths = new Map;
30107
+ for (let index = 0;index < childImportSpecs.length; index += 1) {
30108
+ const spec = childImportSpecs[index];
30109
+ const resolvedChild = resolvedChildModules[index];
30110
+ if (!spec || !resolvedChild)
30111
+ continue;
30112
+ const compiledChildPath = await compileSvelteServerModule(resolvedChild);
30113
+ childModulePaths.set(spec, compiledChildPath);
30114
+ }
30115
+ for (let index = 0;index < childImportSpecs.length; index += 1) {
30116
+ const spec = childImportSpecs[index];
30117
+ const resolvedModuleImport = resolvedModuleImports[index];
30118
+ if (!spec || !resolvedModuleImport)
30119
+ continue;
30120
+ if (resolvedChildModules[index])
30121
+ continue;
30122
+ rewrittenModulePaths.set(spec, ensureRelativeImportPath(getCachedModulePath(sourcePath), resolvedModuleImport));
30123
+ }
30124
+ let compiledCode = compile(transpiled, {
30125
+ css: "injected",
30126
+ experimental: {
30127
+ async: loweredSource.transformed
30128
+ },
30129
+ filename: sourcePath,
30130
+ generate: "server"
30131
+ }).js.code;
30132
+ for (const [spec, compiledChildPath] of childModulePaths) {
30133
+ compiledCode = compiledCode.replaceAll(spec, ensureRelativeImportPath(getCachedModulePath(sourcePath), compiledChildPath));
30134
+ }
30135
+ for (const [spec, resolvedModuleImport] of rewrittenModulePaths) {
30136
+ compiledCode = compiledCode.replaceAll(spec, resolvedModuleImport);
30137
+ }
30138
+ const compiledModulePath = getCachedModulePath(sourcePath);
30139
+ await mkdir(dirname2(compiledModulePath), { recursive: true });
30140
+ await writeIfChanged(compiledModulePath, compiledCode);
30141
+ compiledModuleCache.set(sourcePath, compiledModulePath);
30142
+ return compiledModulePath;
30143
+ };
30144
+ var init_svelteServerModule = __esm(() => {
30145
+ init_lowerIslandSyntax();
30146
+ serverCacheRoot = join2(process.cwd(), ".absolutejs", "islands", "svelte");
30147
+ compiledModuleCache = new Map;
30148
+ transpiler = new Bun.Transpiler({
30149
+ loader: "ts",
30150
+ target: "browser"
30151
+ });
30152
+ });
30153
+
30154
+ // src/core/islandMarkupAttributes.ts
30155
+ var getIslandMarkerAttributes = (props, islandId) => ({
30156
+ "data-component": props.component,
30157
+ "data-framework": props.framework,
30158
+ "data-hydrate": props.hydrate ?? "load",
30159
+ "data-island": "true",
30160
+ ...islandId ? { "data-island-id": islandId } : {},
30161
+ "data-props": serializeIslandProps(props.props)
30162
+ }), escapeHtmlAttribute = (value) => value.replaceAll("&", "&amp;").replaceAll('"', "&quot;").replaceAll("<", "&lt;").replaceAll(">", "&gt;"), serializeIslandAttributes = (attributes) => Object.entries(attributes).map(([key, value]) => `${key}="${escapeHtmlAttribute(value)}"`).join(" ");
30163
+ var init_islandMarkupAttributes = __esm(() => {
30164
+ init_islands();
30165
+ });
30166
+
30167
+ // src/core/renderIslandMarkup.ts
30168
+ var islandSequence = 0, resolvedServerComponentCache, resolvedServerBuildComponentCache, nextIslandId = () => {
30169
+ islandSequence += 1;
30170
+ return `island-${islandSequence}`;
30171
+ }, isRecord2 = (value) => typeof value === "object" && value !== null, isReactServerIslandComponent = (value) => typeof value === "function", isSvelteServerIslandComponent = (value) => typeof value === "function", isVueServerIslandComponent = (value) => typeof value === "function" || isRecord2(value), isAngularServerIslandComponent = (value) => typeof value === "function", resolveBuildReferencePath = (source, registryPath) => source.startsWith("file://") ? new URL(source).pathname : source.startsWith(".") ? new URL(source, registryPath).pathname : source, loadServerBuildComponent = async (buildReferencePath) => {
30172
+ const cachedBuildComponent = resolvedServerBuildComponentCache.get(buildReferencePath);
30173
+ if (cachedBuildComponent) {
30174
+ return cachedBuildComponent;
30175
+ }
30176
+ const loadPromise = (async () => {
30177
+ const compiledModulePath = await compileSvelteServerModule(buildReferencePath);
30178
+ const loadedModule = await import(compiledModulePath);
30179
+ return "default" in loadedModule ? loadedModule.default : loadedModule;
30180
+ })();
30181
+ resolvedServerBuildComponentCache.set(buildReferencePath, loadPromise);
30182
+ return loadPromise;
30183
+ }, loadServerImportComponent = async (resolvedComponent) => {
30184
+ const resolvedModulePath = resolvedComponent.startsWith(".") ? new URL(resolvedComponent, import.meta.url).pathname : resolvedComponent;
30185
+ const importTarget = resolvedModulePath.endsWith(".svelte") ? await compileSvelteServerModule(resolvedModulePath) : resolvedModulePath;
30186
+ const loadedModule = await import(importTarget);
30187
+ return "default" in loadedModule ? loadedModule.default : loadedModule;
30188
+ }, resolveServerIslandComponent = async (component) => {
30189
+ const cachedResolvedComponent = resolvedServerComponentCache.get(component);
30190
+ if (cachedResolvedComponent) {
30191
+ return cachedResolvedComponent;
30192
+ }
30193
+ const resolutionPromise = (async () => {
30194
+ const buildReference = getIslandBuildReference(component);
30195
+ const buildReferencePath = buildReference?.source ? resolveBuildReferencePath(buildReference.source, import.meta.url) : null;
30196
+ if (buildReferencePath?.endsWith(".svelte")) {
30197
+ return loadServerBuildComponent(buildReferencePath);
30198
+ }
30199
+ const resolvedComponent = getIslandComponent(component);
30200
+ if (typeof resolvedComponent !== "string") {
30201
+ return resolvedComponent;
30202
+ }
30203
+ return loadServerImportComponent(resolvedComponent);
30204
+ })();
30205
+ resolvedServerComponentCache.set(component, resolutionPromise);
30206
+ return resolutionPromise;
30207
+ }, resolveReactServerIslandComponent = async (component) => {
30208
+ const resolvedComponent = await resolveServerIslandComponent(component);
30209
+ if (!isReactServerIslandComponent(resolvedComponent)) {
30210
+ throw new Error("Resolved React island is not a valid React component.");
30211
+ }
30212
+ return resolvedComponent;
30213
+ }, resolveSvelteServerIslandComponent = async (component) => {
30214
+ const resolvedComponent = await resolveServerIslandComponent(component);
30215
+ if (!isSvelteServerIslandComponent(resolvedComponent)) {
30216
+ throw new Error("Resolved Svelte island is not a valid Svelte component.");
30217
+ }
30218
+ return resolvedComponent;
30219
+ }, resolveVueServerIslandComponent = async (component) => {
30220
+ const resolvedComponent = await resolveServerIslandComponent(component);
30221
+ if (!isVueServerIslandComponent(resolvedComponent)) {
30222
+ throw new Error("Resolved Vue island is not a valid Vue component.");
30223
+ }
30224
+ return resolvedComponent;
30225
+ }, resolveAngularServerIslandComponent = async (component) => {
30226
+ const resolvedComponent = await resolveServerIslandComponent(component);
30227
+ if (!isAngularServerIslandComponent(resolvedComponent)) {
30228
+ throw new Error("Resolved Angular island is not a valid Angular component.");
30229
+ }
30230
+ return resolvedComponent;
30231
+ }, renderIslandResult = async (registry, props) => {
30232
+ const islandId = nextIslandId();
30233
+ const attributes = getIslandMarkerAttributes(props);
30234
+ if (props.framework === "react") {
30235
+ const entry = registry.react?.[props.component];
30236
+ if (!entry) {
30237
+ throw new Error(`Island component "${props.component}" is not registered for framework "react".`);
30238
+ }
30239
+ const component = await resolveReactServerIslandComponent(entry);
30240
+ const html = renderReactIslandToHtml(component, props.props);
30241
+ return { attributes, html };
30242
+ }
30243
+ if (props.framework === "svelte") {
30244
+ const entry = registry.svelte?.[props.component];
30245
+ if (!entry) {
30246
+ throw new Error(`Island component "${props.component}" is not registered for framework "svelte".`);
30247
+ }
30248
+ const component = await resolveSvelteServerIslandComponent(entry);
30249
+ const html = renderSvelteIslandToHtml(component, props.props);
30250
+ return { attributes, html };
30251
+ }
30252
+ if (props.framework === "vue") {
30253
+ const entry = registry.vue?.[props.component];
30254
+ if (!entry) {
30255
+ throw new Error(`Island component "${props.component}" is not registered for framework "vue".`);
30256
+ }
30257
+ const component = await resolveVueServerIslandComponent(entry);
30258
+ const html = await renderVueIslandToHtml(component, props.props);
30259
+ return { attributes, html };
30260
+ }
30261
+ if (props.framework === "angular") {
30262
+ const entry = registry.angular?.[props.component];
30263
+ if (!entry) {
30264
+ throw new Error(`Island component "${props.component}" is not registered for framework "angular".`);
30265
+ }
30266
+ const component = await resolveAngularServerIslandComponent(entry);
30267
+ const html = await renderAngularIslandToHtml(component, props.props, islandId);
30268
+ return {
30269
+ attributes: {
30270
+ ...getIslandMarkerAttributes(props, islandId)
30271
+ },
30272
+ html
30273
+ };
30274
+ }
30275
+ throw new Error(`Framework "${props.framework}" is not implemented in this prototype.`);
30276
+ }, renderIslandMarkup = async (registry, props) => {
30277
+ const result = await renderIslandResult(registry, props);
30278
+ return `<div ${serializeIslandAttributes(result.attributes)}>${result.html}</div>`;
30279
+ };
30280
+ var init_renderIslandMarkup = __esm(() => {
30281
+ init_islandSsr();
30282
+ init_svelteServerModule();
30283
+ init_islandMarkupAttributes();
30284
+ init_islands();
30285
+ resolvedServerComponentCache = new Map;
30286
+ resolvedServerBuildComponentCache = new Map;
30287
+ });
30288
+
30289
+ // src/build/islandEntries.ts
30290
+ import {
30291
+ mkdirSync,
30292
+ rmSync,
30293
+ writeFileSync as writeFileSync2
30294
+ } from "fs";
30295
+ import { dirname as dirname3, extname as extname2, join as join3, relative as relative2, resolve as resolve4 } from "path";
30296
+ var frameworks, isRecord3 = (value) => typeof value === "object" && value !== null, resolveRegistryExport = (mod) => {
30297
+ if (isRecord3(mod.islandRegistry))
30298
+ return mod.islandRegistry;
30299
+ if (isRecord3(mod.default))
30300
+ return mod.default;
30301
+ throw new Error("Island registry module must export `islandRegistry` or a default registry object.");
30302
+ }, normalizeImportPath = (wrapperPath, targetPath) => {
30303
+ const importPath = relative2(dirname3(wrapperPath), targetPath).replace(/\\/g, "/");
30304
+ return importPath.startsWith(".") ? importPath : `./${importPath}`;
30305
+ }, isIdentifier = (value) => /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(value), resolveIslandSourcePath = (registryPath, sourcePath) => {
30306
+ if (sourcePath.startsWith("file://")) {
30307
+ return new URL(sourcePath).pathname;
30308
+ }
30309
+ return resolve4(dirname3(registryPath), sourcePath);
30310
+ }, createRegistryImportCode = (wrapperPath, registryPath, hasNamedExport) => {
30311
+ const normalizedPath = normalizeImportPath(wrapperPath, registryPath);
30312
+ if (hasNamedExport) {
30313
+ return {
30314
+ importStatement: `import { islandRegistry as __absoluteIslandRegistry } from ${JSON.stringify(normalizedPath)};`,
30315
+ registryReference: "__absoluteIslandRegistry"
30316
+ };
30317
+ }
30318
+ return {
30319
+ importStatement: `import __absoluteIslandRegistry from ${JSON.stringify(normalizedPath)};`,
30320
+ registryReference: "__absoluteIslandRegistry"
30321
+ };
30322
+ }, createDirectEntrySource = (wrapperPath, importPath, exportName) => {
30323
+ const normalizedImportPath = normalizeImportPath(wrapperPath, importPath);
30324
+ if (!exportName || exportName === "default") {
30325
+ return `export { default } from ${JSON.stringify(normalizedImportPath)};
30326
+ `;
30327
+ }
30328
+ return `export { ${exportName} as default } from ${JSON.stringify(normalizedImportPath)};
30329
+ `;
30330
+ }, createRegistryEntrySource = (wrapperPath, registryPath, hasNamedExport, framework, component) => {
30331
+ const { importStatement, registryReference } = createRegistryImportCode(wrapperPath, registryPath, hasNamedExport);
30332
+ const frameworkAccess = isIdentifier(framework) ? `${registryReference}.${framework}` : `${registryReference}[${JSON.stringify(framework)}]`;
30333
+ const componentAccess = isIdentifier(component) ? `${frameworkAccess}.${component}` : `${frameworkAccess}[${JSON.stringify(component)}]`;
30334
+ return `${importStatement}
30335
+
30336
+ const component = ${componentAccess};
30337
+
30338
+ export default component;
30339
+ `;
30340
+ }, shouldUseCompiledClientPath = (framework, sourcePath) => {
30341
+ if (framework === "svelte") {
30342
+ return /\.svelte(?:\.(?:ts|js))?$/.test(sourcePath);
30343
+ }
30344
+ if (framework === "vue") {
30345
+ return extname2(sourcePath) === ".vue";
30346
+ }
30347
+ return false;
30348
+ }, loadIslandRegistryBuildInfo = async (registryPath) => {
30349
+ const resolvedRegistryPath = resolve4(registryPath);
30350
+ const registryModule = await import(resolvedRegistryPath);
30351
+ const registry = resolveRegistryExport(registryModule);
30352
+ const definitions = [];
30353
+ for (const framework of frameworks) {
30354
+ const frameworkRegistry = registry[framework];
30355
+ if (!isRecord3(frameworkRegistry))
30356
+ continue;
30357
+ for (const [component, value] of Object.entries(frameworkRegistry)) {
30358
+ definitions.push({
30359
+ buildReference: getIslandBuildReference(value),
30360
+ component,
30361
+ framework
30362
+ });
30363
+ }
30364
+ }
30365
+ return {
30366
+ definitions,
30367
+ hasNamedExport: isRecord3(registryModule.islandRegistry),
30368
+ registry,
30369
+ resolvedRegistryPath
30370
+ };
30371
+ }, collectIslandFrameworkSources = (buildInfo) => {
30372
+ const sources = {};
30373
+ for (const definition of buildInfo.definitions) {
30374
+ const buildReference = definition.buildReference;
30375
+ if (!buildReference)
30376
+ continue;
30377
+ const resolvedSourcePath = resolveIslandSourcePath(buildInfo.resolvedRegistryPath, buildReference.source);
30378
+ if (!shouldUseCompiledClientPath(definition.framework, resolvedSourcePath)) {
30379
+ continue;
30380
+ }
30381
+ const frameworkSources = sources[definition.framework] ?? [];
30382
+ if (!frameworkSources.includes(resolvedSourcePath)) {
30383
+ frameworkSources.push(resolvedSourcePath);
30384
+ }
30385
+ sources[definition.framework] = frameworkSources;
30386
+ }
30387
+ return sources;
30388
+ }, generateIslandEntryPoints = async ({
30389
+ buildInfo,
30390
+ buildPath,
30391
+ clientPathMaps = {}
30392
+ }) => {
30393
+ const generatedRoot = join3(buildPath, "_island_entries");
30394
+ rmSync(generatedRoot, { force: true, recursive: true });
30395
+ const entries = [];
30396
+ for (const definition of buildInfo.definitions) {
30397
+ const entryPath = join3(generatedRoot, "islands", definition.framework, `${definition.component}.ts`);
30398
+ const buildReference = definition.buildReference;
30399
+ const source = buildReference ? resolveIslandSourcePath(buildInfo.resolvedRegistryPath, buildReference.source) : null;
30400
+ const compiledSourcePath = source && shouldUseCompiledClientPath(definition.framework, source) ? clientPathMaps[definition.framework]?.get(source) : undefined;
30401
+ const entrySource = source && (compiledSourcePath || !shouldUseCompiledClientPath(definition.framework, source)) ? createDirectEntrySource(entryPath, compiledSourcePath ?? source, compiledSourcePath ? undefined : buildReference?.export) : createRegistryEntrySource(entryPath, buildInfo.resolvedRegistryPath, buildInfo.hasNamedExport, definition.framework, definition.component);
30402
+ mkdirSync(dirname3(entryPath), { recursive: true });
30403
+ writeFileSync2(entryPath, entrySource);
30404
+ entries.push({
30405
+ component: definition.component,
30406
+ entryPath,
30407
+ framework: definition.framework
30408
+ });
30409
+ }
30410
+ return {
30411
+ entries,
30412
+ generatedRoot
30413
+ };
30414
+ };
30415
+ var init_islandEntries = __esm(() => {
30416
+ init_islands();
30417
+ frameworks = [
30418
+ "react",
30419
+ "svelte",
30420
+ "vue",
30421
+ "angular"
30422
+ ];
30423
+ });
30424
+
30425
+ // src/core/currentIslandRegistry.ts
30426
+ var setCurrentIslandRegistry = (registry) => {
30427
+ globalThis.__absoluteIslandRegistry = registry;
30428
+ }, requireCurrentIslandRegistry = () => {
30429
+ const registry = globalThis.__absoluteIslandRegistry;
30430
+ if (!registry) {
30431
+ throw new Error("No island registry is active. Configure `islands.registry` in absolute.config.ts before rendering <Island />.");
30432
+ }
30433
+ return registry;
30434
+ };
30435
+
30436
+ // src/build/staticIslandPages.ts
30437
+ import { readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "fs";
30438
+ var ISLAND_TAG_RE_SOURCE = "<(?:absolute-island|island)\\b([^>]*?)(?:\\/\\>|>(?:[\\s\\S]*?)<\\/(?:absolute-island|island)>)", ATTRIBUTE_RE_SOURCE = `([A-Za-z_:][-A-Za-z0-9_:.]*)\\s*=\\s*(?:"([^"]*)"|'([^']*)')`, islandFrameworks, islandHydrationModes, isRecord4 = (value) => typeof value === "object" && value !== null, isIslandFramework = (value) => islandFrameworks.some((framework) => framework === value), isIslandHydrationMode = (value) => islandHydrationModes.some((mode) => mode === value), parseIslandAttributes = (attributeString) => {
30439
+ const attributeRe = new RegExp(ATTRIBUTE_RE_SOURCE, "g");
30440
+ const attributes = new Map;
30441
+ let match = attributeRe.exec(attributeString);
30442
+ while (match) {
30443
+ const key = match[1];
30444
+ const doubleQuotedValue = match[2];
30445
+ const singleQuotedValue = match[3];
30446
+ if (!key) {
30447
+ match = attributeRe.exec(attributeString);
30448
+ continue;
30449
+ }
30450
+ const value = doubleQuotedValue ?? singleQuotedValue ?? "";
30451
+ attributes.set(key, value);
30452
+ match = attributeRe.exec(attributeString);
30453
+ }
30454
+ return attributes;
30455
+ }, parseIslandTag = (attributeString) => {
30456
+ const attributes = parseIslandAttributes(attributeString);
30457
+ const framework = attributes.get("framework");
30458
+ const component = attributes.get("component");
30459
+ const hydrate = attributes.get("hydrate") ?? "load";
30460
+ const propsSource = attributes.get("props") ?? "{}";
30461
+ if (!framework || !component) {
30462
+ return null;
30463
+ }
30464
+ if (!isIslandFramework(framework)) {
30465
+ throw new Error(`Unsupported static island framework "${framework}".`);
30466
+ }
30467
+ if (!isIslandHydrationMode(hydrate)) {
30468
+ throw new Error(`Unsupported static island hydrate mode "${hydrate}".`);
30469
+ }
30470
+ let parsedProps;
30471
+ try {
30472
+ const candidate = JSON.parse(propsSource);
30473
+ parsedProps = isRecord4(candidate) ? candidate : {};
30474
+ } catch (error) {
30475
+ throw new Error(`Failed to parse static island props JSON for ${framework}:${component}: ${error instanceof Error ? error.message : String(error)}`);
30476
+ }
30477
+ return {
30478
+ component,
30479
+ framework,
30480
+ hydrate,
30481
+ props: parsedProps
30482
+ };
30483
+ }, transformStaticPageHtml = async (originalHtml, registry) => {
30484
+ const islandTagRe = new RegExp(ISLAND_TAG_RE_SOURCE, "gi");
30485
+ if (!islandTagRe.test(originalHtml)) {
30486
+ return originalHtml;
30487
+ }
30488
+ islandTagRe.lastIndex = 0;
30489
+ let transformedHtml = "";
30490
+ let lastIndex = 0;
30491
+ let match = islandTagRe.exec(originalHtml);
30492
+ while (match) {
30493
+ const fullMatch = match[0];
30494
+ const attributeString = match[1] ?? "";
30495
+ const matchIndex = match.index;
30496
+ transformedHtml += originalHtml.slice(lastIndex, matchIndex);
30497
+ const props = parseIslandTag(attributeString);
30498
+ transformedHtml += props ? await renderIslandMarkup(registry, props) : fullMatch;
30499
+ lastIndex = matchIndex + fullMatch.length;
30500
+ match = islandTagRe.exec(originalHtml);
30501
+ }
30502
+ transformedHtml += originalHtml.slice(lastIndex);
30503
+ return transformedHtml;
30504
+ }, transformStaticPage = async (pagePath, registry) => {
30505
+ const originalHtml = readFileSync2(pagePath, "utf-8");
30506
+ const transformedHtml = await transformStaticPageHtml(originalHtml, registry);
30507
+ if (transformedHtml !== originalHtml) {
30508
+ writeFileSync3(pagePath, transformedHtml);
30509
+ }
30510
+ }, transformStaticPagesWithIslands = async (registryPath, pagePaths) => {
30511
+ if (!registryPath || pagePaths.length === 0) {
30512
+ return;
30513
+ }
30514
+ const { registry } = await loadIslandRegistryBuildInfo(registryPath);
30515
+ await Promise.all(pagePaths.map((pagePath) => transformStaticPage(pagePath, registry)));
30516
+ }, transformCurrentStaticPageHtml = async (html) => transformStaticPageHtml(html, requireCurrentIslandRegistry());
30517
+ var init_staticIslandPages = __esm(() => {
30518
+ init_renderIslandMarkup();
30519
+ init_islandEntries();
30520
+ islandFrameworks = [
30521
+ "react",
30522
+ "svelte",
30523
+ "vue",
30524
+ "angular"
30525
+ ];
30526
+ islandHydrationModes = ["load", "idle", "visible", "none"];
30527
+ });
30528
+
30529
+ // src/core/islandPageContext.ts
30530
+ var BOOTSTRAP_MANIFEST_KEY = "BootstrapClient", ISLAND_MARKER = 'data-island="true"', MANIFEST_MARKER = "__ABSOLUTE_MANIFEST__", ISLAND_STATE_MARKER = "__ABS_ISLAND_STATE__", buildIslandsHeadMarkup = (manifest) => {
30531
+ const manifestScript = `<script>window.__ABSOLUTE_MANIFEST__ = ${JSON.stringify(manifest)}</script>`;
30532
+ const islandStateScript = "<script>window.__ABS_ISLAND_STATE__ = window.__ABS_ISLAND_STATE__ ?? {}</script>";
30533
+ const bootstrapPath = manifest[BOOTSTRAP_MANIFEST_KEY];
30534
+ const bootstrapScript = bootstrapPath ? `<script type="module" src="${bootstrapPath}"></script>` : "";
30535
+ return `${manifestScript}${islandStateScript}${bootstrapScript}`;
30536
+ }, injectHeadMarkup = (html, markup) => {
30537
+ const closingHeadIndex = html.indexOf("</head>");
30538
+ if (closingHeadIndex >= 0) {
30539
+ return `${html.slice(0, closingHeadIndex)}${markup}${html.slice(closingHeadIndex)}`;
30540
+ }
30541
+ const openingBodyIndex = html.indexOf("<body");
30542
+ if (openingBodyIndex >= 0) {
30543
+ const bodyStart = html.indexOf(">", openingBodyIndex);
30544
+ if (bodyStart >= 0) {
30545
+ return `${html.slice(0, openingBodyIndex)}<head>${markup}</head>${html.slice(openingBodyIndex)}`;
30546
+ }
30547
+ }
30548
+ return `<!DOCTYPE html><html><head>${markup}</head><body>${html}</body></html>`;
30549
+ }, setCurrentIslandManifest = (manifest) => {
30550
+ globalThis.__absoluteManifest = manifest;
30551
+ }, htmlContainsIslands = (html) => html.includes(ISLAND_MARKER), injectIslandPageContext = (html, options) => {
30552
+ const manifest = globalThis.__absoluteManifest;
30553
+ const hasIslands = options?.hasIslands ?? htmlContainsIslands(html);
30554
+ if (!manifest || !hasIslands) {
30555
+ return html;
30556
+ }
30557
+ if (html.includes(MANIFEST_MARKER) || html.includes(ISLAND_STATE_MARKER)) {
30558
+ return html;
30559
+ }
30560
+ return injectHeadMarkup(html, buildIslandsHeadMarkup(manifest));
30561
+ };
30562
+
30563
+ // src/utils/ssrErrorPage.ts
30564
+ var ssrErrorPage = (framework, error) => {
30565
+ const frameworkColors = {
30566
+ angular: "#dd0031",
30567
+ html: "#e34c26",
30568
+ htmx: "#1a365d",
30569
+ react: "#61dafb",
30570
+ svelte: "#ff3e00",
30571
+ vue: "#42b883"
30572
+ };
30573
+ const accent = frameworkColors[framework] ?? "#94a3b8";
30574
+ const label = framework.charAt(0).toUpperCase() + framework.slice(1);
30575
+ const message = error instanceof Error ? error.message : String(error);
30576
+ return `<!DOCTYPE html>
30577
+ <html>
30578
+ <head>
30579
+ <meta charset="utf-8">
30580
+ <meta name="viewport" content="width=device-width, initial-scale=1">
30581
+ <title>SSR Error - AbsoluteJS</title>
30582
+ <style>
30583
+ *{margin:0;padding:0;box-sizing:border-box}
30584
+ body{min-height:100vh;background:linear-gradient(135deg,rgba(15,23,42,0.98) 0%,rgba(30,41,59,0.98) 100%);color:#e2e8f0;font-family:"JetBrains Mono","Fira Code",ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:14px;line-height:1.6;display:flex;align-items:flex-start;justify-content:center;padding:32px}
30585
+ .card{max-width:720px;width:100%;background:rgba(30,41,59,0.6);border:1px solid rgba(71,85,105,0.5);border-radius:16px;box-shadow:0 25px 50px -12px rgba(0,0,0,0.5),0 0 0 1px rgba(255,255,255,0.05);overflow:hidden}
30586
+ .header{display:flex;align-items:center;justify-content:space-between;gap:16px;padding:20px 24px;background:rgba(15,23,42,0.5);border-bottom:1px solid rgba(71,85,105,0.4)}
30587
+ .brand{font-weight:700;font-size:20px;color:#fff;letter-spacing:-0.02em}
30588
+ .badge{padding:5px 10px;border-radius:8px;font-size:12px;font-weight:600;background:${accent};color:#fff;opacity:0.95;box-shadow:0 2px 4px rgba(0,0,0,0.2)}
30589
+ .kind{color:#94a3b8;font-size:13px;font-weight:500}
30590
+ .content{padding:24px}
30591
+ .label{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:0.08em;color:#94a3b8;margin-bottom:8px}
30592
+ .message{margin:0;padding:16px 20px;background:rgba(239,68,68,0.12);border:1px solid rgba(239,68,68,0.25);border-radius:10px;overflow-x:auto;white-space:pre-wrap;word-break:break-word;color:#fca5a5;font-size:13px;line-height:1.5}
30593
+ .hint{margin-top:20px;padding:12px 20px;background:rgba(71,85,105,0.3);border-radius:10px;border:1px solid rgba(71,85,105,0.4);color:#cbd5e1;font-size:13px}
30594
+ </style>
30595
+ </head>
30596
+ <body>
30597
+ <div class="card">
30598
+ <div class="header">
30599
+ <div style="display:flex;align-items:center;gap:12px">
30600
+ <span class="brand">AbsoluteJS</span>
30601
+ <span class="badge">${label}</span>
30602
+ </div>
30603
+ <span class="kind">Server Render Error</span>
30604
+ </div>
30605
+ <div class="content">
30606
+ <div class="label">What went wrong</div>
30607
+ <pre class="message">${message.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")}</pre>
30608
+ <div class="hint">A component threw during server-side rendering. Check the terminal for the full stack trace.</div>
30609
+ </div>
30610
+ </div>
30611
+ </body>
30612
+ </html>`;
30613
+ };
30614
+
30615
+ // src/utils/resolveConvention.ts
30616
+ import { basename } from "path";
30617
+ var CONVENTIONS_KEY = "__absoluteConventions", isConventionsMap = (value) => Boolean(value) && typeof value === "object", getMap = () => {
30618
+ const value = Reflect.get(globalThis, CONVENTIONS_KEY);
30619
+ if (isConventionsMap(value))
30620
+ return value;
30621
+ const empty = {};
30622
+ return empty;
30623
+ }, derivePageName = (pagePath) => {
30624
+ const base = basename(pagePath);
30625
+ const dotIndex = base.indexOf(".");
30626
+ const name = dotIndex > 0 ? base.slice(0, dotIndex) : base;
30627
+ return toPascal(name);
30628
+ }, resolveErrorConventionPath = (framework, pageName) => {
30629
+ const conventions2 = getMap()[framework];
30630
+ if (!conventions2)
30631
+ return;
30632
+ return conventions2.pages?.[pageName]?.error ?? conventions2.defaults?.error;
30633
+ }, resolveNotFoundConventionPath = (framework) => getMap()[framework]?.defaults?.notFound, setConventions = (map) => {
30634
+ Reflect.set(globalThis, CONVENTIONS_KEY, map);
30635
+ }, isDev = () => true, buildErrorProps = (error) => {
30636
+ const message = error instanceof Error ? error.message : String(error);
30637
+ const stack = isDev() && error instanceof Error ? error.stack : undefined;
30638
+ return { error: { message, stack } };
30639
+ }, renderReactError = async (conventionPath, errorProps) => {
30640
+ const { createElement: createElement2 } = await import("react");
30641
+ const { renderToReadableStream } = await import("react-dom/server");
30642
+ const mod = await import(conventionPath);
30643
+ const [firstKey] = Object.keys(mod);
30644
+ const ErrorComponent = mod.default ?? (firstKey ? mod[firstKey] : undefined);
30645
+ const element2 = createElement2(ErrorComponent, errorProps);
30646
+ const stream = await renderToReadableStream(element2);
30647
+ return new Response(stream, {
30648
+ headers: { "Content-Type": "text/html" },
30649
+ status: 500
30650
+ });
30651
+ }, renderSvelteError = async (conventionPath, errorProps) => {
30652
+ const { render } = await import("svelte/server");
30653
+ const mod = await import(conventionPath);
30654
+ const ErrorComponent = mod.default;
30655
+ const { head, body } = render(ErrorComponent, {
30656
+ props: errorProps
30657
+ });
30658
+ const html = `<!DOCTYPE html><html><head>${head}</head><body>${body}</body></html>`;
30659
+ return new Response(html, {
30660
+ headers: { "Content-Type": "text/html" },
30661
+ status: 500
30662
+ });
30663
+ }, unescapeVueStyles = (ssrBody) => {
30664
+ let styles = "";
30665
+ const body = ssrBody.replace(/<style>([\s\S]*?)<\/style>/g, (_, css) => {
30666
+ styles += `<style>${css.replace(/&quot;/g, '"').replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">")}</style>`;
30667
+ return "";
30668
+ });
30669
+ return { body, styles };
30670
+ }, renderVueError = async (conventionPath, errorProps) => {
30671
+ const { createSSRApp: createSSRApp2, h: h2 } = await import("vue");
30672
+ const { renderToString } = await import("vue/server-renderer");
30673
+ const mod = await import(conventionPath);
30674
+ const ErrorComponent = mod.default;
30675
+ const app = createSSRApp2({
30676
+ render: () => h2(ErrorComponent, errorProps)
30677
+ });
30678
+ const rawBody = await renderToString(app);
30679
+ const { styles, body } = unescapeVueStyles(rawBody);
30680
+ const html = `<!DOCTYPE html><html><head>${styles}</head><body><div id="root">${body}</div></body></html>`;
30681
+ return new Response(html, {
30682
+ headers: { "Content-Type": "text/html" },
30683
+ status: 500
30684
+ });
30685
+ }, renderAngularError = async (conventionPath, errorProps) => {
30686
+ const mod = await import(conventionPath);
30687
+ const renderError = mod.default ?? mod.renderError;
30688
+ if (typeof renderError !== "function")
30689
+ return null;
30690
+ const html = renderError(errorProps);
30691
+ return new Response(html, {
30692
+ headers: { "Content-Type": "text/html" },
30693
+ status: 500
30694
+ });
30695
+ }, logConventionRenderError = (framework, label, renderError) => {
30696
+ const message = renderError instanceof Error ? renderError.message : "";
30697
+ if (message.includes("Cannot find module") || message.includes("Cannot find package") || message.includes("not found in module")) {
30698
+ console.error(`[SSR] Convention ${label} page for ${framework} failed: missing framework package. Ensure the ${framework} runtime is installed (e.g. bun add ${framework === "react" ? "react react-dom" : framework}).`);
30699
+ return;
30700
+ }
30701
+ console.error(`[SSR] Failed to render ${framework} convention ${label} page:`, renderError);
30702
+ }, ERROR_RENDERERS, renderConventionError = async (framework, pageName, error) => {
30703
+ const conventionPath = resolveErrorConventionPath(framework, pageName);
30704
+ if (!conventionPath)
30705
+ return null;
30706
+ const errorProps = buildErrorProps(error);
30707
+ const renderer = ERROR_RENDERERS[framework];
30708
+ if (!renderer)
30709
+ return null;
30710
+ try {
30711
+ return await renderer(conventionPath, errorProps);
30712
+ } catch (renderError) {
30713
+ logConventionRenderError(framework, "error", renderError);
30714
+ }
30715
+ return null;
30716
+ }, renderReactNotFound = async (conventionPath) => {
30717
+ const { createElement: createElement2 } = await import("react");
30718
+ const { renderToReadableStream } = await import("react-dom/server");
30719
+ const mod = await import(conventionPath);
30720
+ const [nfKey] = Object.keys(mod);
30721
+ const NotFoundComponent = mod.default ?? (nfKey ? mod[nfKey] : undefined);
30722
+ const element2 = createElement2(NotFoundComponent);
30723
+ const stream = await renderToReadableStream(element2);
30724
+ return new Response(stream, {
30725
+ headers: { "Content-Type": "text/html" },
30726
+ status: 404
30727
+ });
30728
+ }, renderSvelteNotFound = async (conventionPath) => {
30729
+ const { render } = await import("svelte/server");
30730
+ const mod = await import(conventionPath);
30731
+ const NotFoundComponent = mod.default;
30732
+ const { head, body } = render(NotFoundComponent);
30733
+ const html = `<!DOCTYPE html><html><head>${head}</head><body>${body}</body></html>`;
30734
+ return new Response(html, {
30735
+ headers: { "Content-Type": "text/html" },
30736
+ status: 404
30737
+ });
30738
+ }, renderVueNotFound = async (conventionPath) => {
30739
+ const { createSSRApp: createSSRApp2, h: h2 } = await import("vue");
30740
+ const { renderToString } = await import("vue/server-renderer");
30741
+ const mod = await import(conventionPath);
30742
+ const NotFoundComponent = mod.default;
30743
+ const app = createSSRApp2({
30744
+ render: () => h2(NotFoundComponent)
30745
+ });
30746
+ const rawBody = await renderToString(app);
30747
+ const { styles, body } = unescapeVueStyles(rawBody);
30748
+ const html = `<!DOCTYPE html><html><head>${styles}</head><body><div id="root">${body}</div></body></html>`;
30749
+ return new Response(html, {
30750
+ headers: { "Content-Type": "text/html" },
30751
+ status: 404
30752
+ });
30753
+ }, renderAngularNotFound = async (conventionPath) => {
30754
+ const mod = await import(conventionPath);
30755
+ const renderNotFound = mod.default ?? mod.renderNotFound;
30756
+ if (typeof renderNotFound !== "function")
30757
+ return null;
30758
+ const html = renderNotFound();
30759
+ return new Response(html, {
30760
+ headers: { "Content-Type": "text/html" },
30761
+ status: 404
30762
+ });
30763
+ }, NOT_FOUND_RENDERERS, renderConventionNotFound = async (framework) => {
30764
+ const conventionPath = resolveNotFoundConventionPath(framework);
30765
+ if (!conventionPath)
30766
+ return null;
30767
+ const renderer = NOT_FOUND_RENDERERS[framework];
30768
+ if (!renderer)
30769
+ return null;
30770
+ try {
30771
+ return await renderer(conventionPath);
30772
+ } catch (renderError) {
30773
+ logConventionRenderError(framework, "not-found", renderError);
30774
+ }
30775
+ return null;
30776
+ }, NOT_FOUND_PRIORITY, renderFirstNotFound = async () => {
30777
+ for (const framework of NOT_FOUND_PRIORITY) {
30778
+ if (!getMap()[framework]?.defaults?.notFound)
30779
+ continue;
30780
+ const response = await renderConventionNotFound(framework);
30781
+ if (response)
30782
+ return response;
30783
+ }
30784
+ return null;
30785
+ };
30786
+ var init_resolveConvention = __esm(() => {
30787
+ ERROR_RENDERERS = {
30788
+ angular: renderAngularError,
30789
+ react: renderReactError,
30790
+ svelte: renderSvelteError,
30791
+ vue: renderVueError
30792
+ };
30793
+ NOT_FOUND_RENDERERS = {
30794
+ angular: renderAngularNotFound,
30795
+ react: renderReactNotFound,
30796
+ svelte: renderSvelteNotFound,
30797
+ vue: renderVueNotFound
30798
+ };
30799
+ NOT_FOUND_PRIORITY = [
30800
+ "react",
30801
+ "svelte",
30802
+ "vue",
30803
+ "angular"
30804
+ ];
30805
+ });
30806
+
30807
+ // src/react/pageHandler.ts
30808
+ var ssrDirty = false, buildDirtyResponse = (index, maybeProps) => {
30809
+ const propsScript = maybeProps ? `window.__INITIAL_PROPS__=${JSON.stringify(maybeProps)};` : "";
30810
+ const dirtyFlag = "window.__SSR_DIRTY__=true;";
30811
+ const refreshSetup = "window.__REFRESH_BUFFER__=[];" + "window.$RefreshReg$=function(t,i){window.__REFRESH_BUFFER__.push([t,i])};" + "window.$RefreshSig$=function(){return function(t){return t}};";
30812
+ const inlineScript = `${propsScript}${dirtyFlag}${refreshSetup}`;
30813
+ const html = `<!DOCTYPE html><html><head></head><body>` + `<script>${inlineScript}</script>` + `<script type="module" src="${index}"></script>` + `</body></html>`;
30814
+ return new Response(html, {
30815
+ headers: { "Content-Type": "text/html" }
30816
+ });
30817
+ }, handleReactPageRequest = async (PageComponent, index, ...props) => {
30818
+ const [maybeProps] = props;
30819
+ if (ssrDirty) {
30820
+ return buildDirtyResponse(index, maybeProps);
30821
+ }
30822
+ try {
30823
+ const { createElement: createElement2 } = await import("react");
30824
+ const { renderToReadableStream } = await import("react-dom/server");
30825
+ const element2 = maybeProps !== undefined ? createElement2(PageComponent, maybeProps) : createElement2(PageComponent);
30826
+ const propsScript = maybeProps ? `window.__INITIAL_PROPS__=${JSON.stringify(maybeProps)};` : "";
30827
+ const refreshSetup = "window.__REFRESH_BUFFER__=[];window.$RefreshReg$=function(t,i){window.__REFRESH_BUFFER__.push([t,i])};window.$RefreshSig$=function(){return function(t){return t}};";
30828
+ const stream = await renderToReadableStream(element2, {
30829
+ bootstrapModules: [index],
30830
+ bootstrapScriptContent: propsScript + refreshSetup || undefined,
30831
+ onError(error) {
30832
+ console.error("[SSR] React streaming error:", error);
30833
+ }
30834
+ });
30835
+ const html = injectIslandPageContext(await new Response(stream).text());
30836
+ return new Response(html, {
30837
+ headers: { "Content-Type": "text/html" }
30838
+ });
30839
+ } catch (error) {
30840
+ console.error("[SSR] React render error:", error);
30841
+ const pageName = PageComponent.name || PageComponent.displayName || "";
30842
+ const conventionResponse = await renderConventionError("react", pageName, error);
30843
+ if (conventionResponse)
30844
+ return conventionResponse;
30845
+ return new Response(ssrErrorPage("react", error), {
30846
+ headers: { "Content-Type": "text/html" },
30847
+ status: 500
30848
+ });
30849
+ }
30850
+ }, invalidateReactSsrCache = () => {
30851
+ ssrDirty = true;
30852
+ };
30853
+ var init_pageHandler = __esm(() => {
30854
+ init_resolveConvention();
30855
+ });
30856
+
30857
+ // src/build/scanEntryPoints.ts
30858
+ import { existsSync as existsSync3 } from "fs";
30859
+ var {Glob } = globalThis.Bun;
30860
+ var scanEntryPoints = async (dir, pattern) => {
30861
+ if (!existsSync3(dir))
30862
+ return [];
30863
+ const entryPaths = [];
30864
+ const glob = new Glob(pattern);
30865
+ for await (const file2 of glob.scan({ absolute: true, cwd: dir })) {
30866
+ entryPaths.push(file2);
30867
+ }
30868
+ return entryPaths;
30869
+ };
30870
+ var init_scanEntryPoints = () => {};
30871
+
30872
+ // src/islands/sourceMetadata.ts
30873
+ var islandFrameworks2, islandHydrationModes2, isIslandFramework2 = (value) => islandFrameworks2.some((framework) => framework === value), isIslandHydrate = (value) => islandHydrationModes2.some((hydrate) => hydrate === value), parseIslandTagAttributes = (attributeString) => {
30874
+ const frameworkMatch = attributeString.match(/\bframework\s*=\s*["']([^"']+)["']/);
30875
+ const componentMatch = attributeString.match(/\bcomponent\s*=\s*["']([^"']+)["']/);
30876
+ const hydrateMatch = attributeString.match(/\bhydrate\s*=\s*["']([^"']+)["']/);
30877
+ const framework = frameworkMatch?.[1];
30878
+ const component = componentMatch?.[1];
30879
+ if (!framework || !component) {
30880
+ return null;
30881
+ }
30882
+ if (!isIslandFramework2(framework)) {
30883
+ return null;
30884
+ }
30885
+ const hydrateCandidate = hydrateMatch?.[1];
30886
+ return {
30887
+ component,
30888
+ framework,
30889
+ hydrate: hydrateCandidate && isIslandHydrate(hydrateCandidate) ? hydrateCandidate : undefined
30890
+ };
30891
+ }, normalizeUsage = (usage) => `${usage.framework}:${usage.component}:${usage.hydrate ?? ""}`, addUsage = (usageMap, usage) => {
30892
+ if (!usage)
30893
+ return;
30894
+ usageMap.set(normalizeUsage(usage), usage);
30895
+ }, extractIslandUsagesFromSource = (source) => {
30896
+ const usageMap = new Map;
30897
+ const islandTagRegex = /<Island\b([\s\S]*?)(?:\/>|>(?:[\s\S]*?)<\/Island>)/g;
30898
+ let islandTagMatch = islandTagRegex.exec(source);
30899
+ while (islandTagMatch) {
30900
+ addUsage(usageMap, parseIslandTagAttributes(islandTagMatch[1] ?? ""));
30901
+ islandTagMatch = islandTagRegex.exec(source);
30902
+ }
30903
+ const staticRenderCallRegex = /renderIsland\s*\(\s*\{[\s\S]*?\bframework\s*:\s*['"]([^'"]+)['"][\s\S]*?\bcomponent\s*:\s*['"]([^'"]+)['"](?:[\s\S]*?\bhydrate\s*:\s*['"]([^'"]+)['"])?[\s\S]*?\}\s*\)/g;
30904
+ let renderMatch = staticRenderCallRegex.exec(source);
30905
+ while (renderMatch) {
30906
+ const framework = renderMatch[1];
30907
+ const component = renderMatch[2];
30908
+ const hydrate = renderMatch[3];
30909
+ if (!framework || !component || !isIslandFramework2(framework)) {
30910
+ renderMatch = staticRenderCallRegex.exec(source);
30911
+ continue;
30912
+ }
30913
+ addUsage(usageMap, {
30914
+ component,
30915
+ framework,
30916
+ hydrate: hydrate && isIslandHydrate(hydrate) ? hydrate : undefined
30917
+ });
30918
+ renderMatch = staticRenderCallRegex.exec(source);
30919
+ }
30920
+ return [...usageMap.values()];
30921
+ }, buildIslandMetadataExports = (source) => {
30922
+ const usages = extractIslandUsagesFromSource(source);
30923
+ const serialized = JSON.stringify(usages);
30924
+ return `
30925
+ export const __ABSOLUTE_PAGE_ISLANDS__ = ${serialized};
30926
+ export const __ABSOLUTE_PAGE_HAS_ISLANDS__ = ${usages.length > 0};
30927
+ `;
30928
+ };
30929
+ var init_sourceMetadata = __esm(() => {
30930
+ islandFrameworks2 = [
30931
+ "react",
30932
+ "svelte",
30933
+ "vue",
30934
+ "angular"
30935
+ ];
30936
+ islandHydrationModes2 = [
30937
+ "load",
30938
+ "idle",
30939
+ "visible",
30940
+ "none"
30941
+ ];
30942
+ });
30943
+
30944
+ // src/islands/pageMetadata.ts
30945
+ import { readFileSync as readFileSync3 } from "fs";
30946
+ import { dirname as dirname4, resolve as resolve7 } from "path";
30947
+ var pagePatterns, getPageDirs = (config) => [
30948
+ { dir: config.angularDirectory, framework: "angular" },
30949
+ { dir: config.reactDirectory, framework: "react" },
30950
+ { dir: config.svelteDirectory, framework: "svelte" },
30951
+ { dir: config.vueDirectory, framework: "vue" },
30952
+ { dir: config.htmlDirectory, framework: "html" },
30953
+ { dir: config.htmxDirectory, framework: "htmx" }
30954
+ ].filter((entry) => typeof entry.dir === "string" && entry.dir.length > 0), buildIslandSourceLookup = async (config) => {
30955
+ const registryPath = config.islands?.registry;
30956
+ if (!registryPath) {
30957
+ return new Map;
30958
+ }
30959
+ const buildInfo = await loadIslandRegistryBuildInfo(registryPath);
30960
+ const lookup = new Map;
30961
+ for (const definition of buildInfo.definitions) {
30962
+ const source = definition.buildReference?.source;
30963
+ if (!source)
30964
+ continue;
30965
+ const resolvedSource = source.startsWith("file://") ? new URL(source).pathname : resolve7(dirname4(buildInfo.resolvedRegistryPath), source);
30966
+ lookup.set(`${definition.framework}:${definition.component}`, resolve7(resolvedSource));
31362
30967
  }
31363
- return { expression: "", found: false };
31364
- }, lowerSvelteIslandSyntax = (source, mode = "server") => {
31365
- if (!source.includes("<Island")) {
31366
- return { code: source, transformed: false };
30968
+ return lookup;
30969
+ }, loadPageIslandMetadata = async (config) => {
30970
+ const pageMetadata = new Map;
30971
+ const islandSourceLookup = await buildIslandSourceLookup(config);
30972
+ for (const entry of getPageDirs(config)) {
30973
+ const pattern = pagePatterns[entry.framework];
30974
+ if (!pattern)
30975
+ continue;
30976
+ const files = await scanEntryPoints(resolve7(entry.dir), pattern);
30977
+ for (const filePath of files) {
30978
+ const source = readFileSync3(filePath, "utf-8");
30979
+ const islands = extractIslandUsagesFromSource(source);
30980
+ pageMetadata.set(resolve7(filePath), {
30981
+ islands: islands.map((usage) => {
30982
+ const sourcePath = islandSourceLookup.get(`${usage.framework}:${usage.component}`);
30983
+ return sourcePath ? {
30984
+ ...usage,
30985
+ source: sourcePath
30986
+ } : usage;
30987
+ }),
30988
+ pagePath: resolve7(filePath)
30989
+ });
30990
+ }
31367
30991
  }
31368
- let islandIndex = 0;
31369
- const transformedMarkup = source.replace(ISLAND_TAG_RE, (fullMatch, attributeString) => {
31370
- const framework = extractIslandAttribute(attributeString, "framework");
31371
- const component = extractIslandAttribute(attributeString, "component");
31372
- if (!framework.found || !component.found) {
31373
- return fullMatch;
30992
+ return pageMetadata;
30993
+ }, setCurrentPageIslandMetadata = (metadata2) => {
30994
+ globalThis.__absolutePageIslandMetadata = metadata2;
30995
+ }, getCurrentPageIslandMetadata = () => globalThis.__absolutePageIslandMetadata ?? new Map, getPagesUsingIslandSource = (sourcePath) => {
30996
+ const target = resolve7(sourcePath);
30997
+ const matches = [];
30998
+ for (const metadata2 of getCurrentPageIslandMetadata().values()) {
30999
+ const usesTarget = metadata2.islands.some((usage) => {
31000
+ const candidate = usage.source;
31001
+ return candidate ? resolve7(candidate) === target : false;
31002
+ });
31003
+ if (usesTarget) {
31004
+ matches.push(metadata2.pagePath);
31374
31005
  }
31375
- const hydrate = extractIslandAttribute(attributeString, "hydrate");
31376
- const props = extractIslandAttribute(attributeString, "props");
31377
- const slotId = `absolute-svelte-island-${islandIndex.toString(BASE_36_RADIX)}`;
31378
- islandIndex += 1;
31379
- const resolveExpression = `await __absoluteResolveIslandHtml(${JSON.stringify(slotId)}, { component: ${component.expression}, framework: ${framework.expression}, hydrate: ${hydrate.found ? hydrate.expression : JSON.stringify("load")}, props: ${props.found ? props.expression : "{}"} })`;
31380
- return `<div data-absolute-island-slot="${slotId}" style="display: contents">{@html ${resolveExpression}}</div>`;
31381
- });
31382
- const importLine = 'import { resolveIslandHtml as __absoluteResolveIslandHtml } from "@absolutejs/absolute/svelte";';
31383
- if (transformedMarkup.includes("<script")) {
31384
- return {
31385
- code: transformedMarkup.replace(/<script(\s[^>]*)?>/, (match) => `${match}
31386
- ${importLine}
31387
- `),
31388
- transformed: true
31389
- };
31390
31006
  }
31391
- return {
31392
- code: `<script lang="ts">
31393
- ${importLine}
31394
- </script>
31395
- ${transformedMarkup}`,
31396
- transformed: true
31397
- };
31007
+ return matches;
31398
31008
  };
31399
- var init_lowerIslandSyntax = __esm(() => {
31400
- init_constants();
31401
- ISLAND_TAG_RE = /<Island\b([\s\S]*?)\/>/g;
31009
+ var init_pageMetadata = __esm(() => {
31010
+ init_islandEntries();
31011
+ init_scanEntryPoints();
31012
+ init_sourceMetadata();
31013
+ pagePatterns = {
31014
+ angular: "pages/**/*.{ts,js}",
31015
+ html: "pages/**/*.html",
31016
+ htmx: "pages/**/*.html",
31017
+ react: "pages/**/*.{ts,tsx,js,jsx}",
31018
+ svelte: "pages/**/*.svelte",
31019
+ vue: "pages/**/*.vue"
31020
+ };
31402
31021
  });
31403
31022
 
31404
- // src/core/svelteServerModule.ts
31405
- import { mkdir } from "fs/promises";
31406
- import { dirname as dirname5, extname as extname3, join as join4, relative as relative3, resolve as resolve10 } from "path";
31407
- var serverCacheRoot, compiledModuleCache, transpiler, ensureRelativeImportPath = (from, to) => {
31408
- const importPath = relative3(dirname5(from), to).replace(/\\/g, "/");
31409
- return importPath.startsWith(".") ? importPath : `./${importPath}`;
31410
- }, resolveRelativeModule = async (spec, from) => {
31411
- if (!spec.startsWith(".")) {
31412
- return null;
31413
- }
31414
- const basePath = resolve10(dirname5(from), spec);
31415
- const candidates = [
31416
- basePath,
31417
- `${basePath}.ts`,
31418
- `${basePath}.js`,
31419
- `${basePath}.mjs`,
31420
- `${basePath}.cjs`,
31421
- `${basePath}.json`,
31422
- join4(basePath, "index.ts"),
31423
- join4(basePath, "index.js"),
31424
- join4(basePath, "index.mjs"),
31425
- join4(basePath, "index.cjs"),
31426
- join4(basePath, "index.json")
31427
- ];
31428
- for (const candidate of candidates) {
31429
- if (await Bun.file(candidate).exists() === true) {
31430
- return candidate;
31431
- }
31023
+ // src/build/generateIslandBindings.ts
31024
+ var exports_generateIslandBindings = {};
31025
+ __export(exports_generateIslandBindings, {
31026
+ generateIslandBindings: () => generateIslandBindings
31027
+ });
31028
+ import { existsSync as existsSync4, mkdirSync as mkdirSync2, rmSync as rmSync2, writeFileSync as writeFileSync4 } from "fs";
31029
+ import { dirname as dirname5, resolve as resolve8 } from "path";
31030
+ var ensureDir = (filePath) => {
31031
+ mkdirSync2(dirname5(filePath), { recursive: true });
31032
+ }, writeIfChanged2 = (filePath, content) => {
31033
+ ensureDir(filePath);
31034
+ writeFileSync4(filePath, content);
31035
+ }, removeIfExists = (filePath) => {
31036
+ if (existsSync4(filePath)) {
31037
+ rmSync2(filePath, { force: true });
31432
31038
  }
31433
- return null;
31434
- }, getCachedModulePath = (sourcePath) => {
31435
- const relativeSourcePath = relative3(process.cwd(), sourcePath).replace(/\\/g, "/");
31436
- const normalizedSourcePath = relativeSourcePath.startsWith("..") ? sourcePath.replace(/[:\\/]/g, "_") : relativeSourcePath;
31437
- return join4(serverCacheRoot, `${normalizedSourcePath}.server.js`);
31438
- }, resolveSvelteImport = async (spec, from) => {
31439
- if (spec.startsWith("/")) {
31440
- return spec;
31039
+ }, generateIslandBindings = (projectRoot, config) => {
31040
+ const registryPath = config.islands?.registry;
31041
+ if (!registryPath) {
31042
+ return;
31441
31043
  }
31442
- if (!spec.startsWith(".")) {
31443
- return null;
31044
+ const resolvedRegistryPath = resolve8(projectRoot, registryPath);
31045
+ removeIfExists(resolve8(dirname5(resolvedRegistryPath), "absolute-islands.d.ts"));
31046
+ if (config.reactDirectory) {
31047
+ const compatTarget = resolve8(config.reactDirectory, "generated", "absolute-react.ts");
31048
+ removeIfExists(resolve8(config.reactDirectory, "generated", "Island.tsx"));
31049
+ removeIfExists(resolve8(config.reactDirectory, "generated", "absolute-react.d.ts"));
31050
+ writeIfChanged2(compatTarget, `export * from "@absolutejs/absolute/react";
31051
+ `);
31444
31052
  }
31445
- const explicitPath = resolve10(dirname5(from), spec);
31446
- if (extname3(explicitPath) === ".svelte") {
31447
- return explicitPath;
31053
+ if (config.vueDirectory) {
31054
+ const compatTarget = resolve8(config.vueDirectory, "generated", "absolute-vue.ts");
31055
+ removeIfExists(resolve8(config.vueDirectory, "generated", "Island.ts"));
31056
+ removeIfExists(resolve8(config.vueDirectory, "generated", "absolute-vue.d.ts"));
31057
+ writeIfChanged2(compatTarget, `export * from "@absolutejs/absolute/vue";
31058
+ `);
31448
31059
  }
31449
- const candidate = `${explicitPath}.svelte`;
31450
- if (await Bun.file(candidate).exists() === true) {
31451
- return candidate;
31060
+ if (config.svelteDirectory) {
31061
+ const compatTarget = resolve8(config.svelteDirectory, "generated", "absolute-svelte.ts");
31062
+ removeIfExists(resolve8(config.svelteDirectory, "generated", "islands.ts"));
31063
+ removeIfExists(resolve8(config.svelteDirectory, "generated", "absolute-svelte.d.ts"));
31064
+ writeIfChanged2(compatTarget, `export * from "@absolutejs/absolute/svelte";
31065
+ `);
31452
31066
  }
31453
- return null;
31454
- }, writeIfChanged2 = async (path, content) => {
31455
- const targetFile = Bun.file(path);
31456
- const exists = await targetFile.exists();
31457
- if (exists) {
31458
- const currentContent = await targetFile.text();
31459
- if (currentContent === content) {
31460
- return;
31461
- }
31067
+ if (config.angularDirectory) {
31068
+ const compatTarget = resolve8(config.angularDirectory, "generated", "absolute-angular.ts");
31069
+ removeIfExists(resolve8(config.angularDirectory, "generated", "islands.ts"));
31070
+ removeIfExists(resolve8(config.angularDirectory, "generated", "absolute-angular.d.ts"));
31071
+ writeIfChanged2(compatTarget, `export * from "@absolutejs/absolute/angular";
31072
+ `);
31462
31073
  }
31463
- await Bun.write(path, content);
31464
- }, compileSvelteServerModule = async (sourcePath) => {
31465
- const cachedModulePath = compiledModuleCache.get(sourcePath);
31466
- if (cachedModulePath) {
31467
- return cachedModulePath;
31074
+ };
31075
+ var init_generateIslandBindings = () => {};
31076
+
31077
+ // src/utils/getDurationString.ts
31078
+ var getDurationString = (duration) => {
31079
+ let durationString;
31080
+ if (duration < MILLISECONDS_IN_A_SECOND) {
31081
+ durationString = `${duration.toFixed(TIME_PRECISION)}ms`;
31082
+ } else if (duration < MILLISECONDS_IN_A_MINUTE) {
31083
+ durationString = `${(duration / MILLISECONDS_IN_A_SECOND).toFixed(TIME_PRECISION)}s`;
31084
+ } else {
31085
+ durationString = `${(duration / MILLISECONDS_IN_A_MINUTE).toFixed(TIME_PRECISION)}m`;
31468
31086
  }
31469
- const source = await Bun.file(sourcePath).text();
31470
- const { compile, preprocess } = await import("svelte/compiler");
31471
- const loweredSource = lowerSvelteIslandSyntax(source, "server");
31472
- const preprocessed = await preprocess(loweredSource.code, {});
31473
- const transpiled = sourcePath.endsWith(".ts") || sourcePath.endsWith(".svelte.ts") ? transpiler.transformSync(preprocessed.code) : preprocessed.code;
31474
- const childImportSpecs = Array.from(transpiled.matchAll(/from\s+['"]([^'"]+)['"]/g)).map((match) => match[1]).filter((value) => value !== undefined);
31475
- const resolvedChildModules = await Promise.all(childImportSpecs.map((spec) => resolveSvelteImport(spec, sourcePath)));
31476
- const resolvedModuleImports = await Promise.all(childImportSpecs.map((spec) => resolveRelativeModule(spec, sourcePath)));
31477
- const childModulePaths = new Map;
31478
- const rewrittenModulePaths = new Map;
31479
- for (let index = 0;index < childImportSpecs.length; index += 1) {
31480
- const spec = childImportSpecs[index];
31481
- const resolvedChild = resolvedChildModules[index];
31482
- if (!spec || !resolvedChild)
31483
- continue;
31484
- const compiledChildPath = await compileSvelteServerModule(resolvedChild);
31485
- childModulePaths.set(spec, compiledChildPath);
31087
+ return durationString;
31088
+ };
31089
+ var init_getDurationString = __esm(() => {
31090
+ init_constants();
31091
+ });
31092
+
31093
+ // src/utils/startupBanner.ts
31094
+ var colors, MONTHS, formatTimestamp = () => {
31095
+ const now = new Date;
31096
+ const month = MONTHS[now.getMonth()];
31097
+ const day = now.getDate().toString().padStart(2, "0");
31098
+ let hours = now.getHours();
31099
+ const minutes = now.getMinutes().toString().padStart(2, "0");
31100
+ const seconds = now.getSeconds().toString().padStart(2, "0");
31101
+ const ampm = hours >= HOURS_IN_HALF_DAY ? "PM" : "AM";
31102
+ hours = hours % HOURS_IN_HALF_DAY || HOURS_IN_HALF_DAY;
31103
+ return `${month} ${day} ${hours}:${minutes}:${seconds} ${ampm}`;
31104
+ }, startupBanner = (options) => {
31105
+ const {
31106
+ version,
31107
+ duration,
31108
+ port,
31109
+ host,
31110
+ networkUrl,
31111
+ protocol = "http"
31112
+ } = options;
31113
+ const name = `${colors.cyan}${colors.bold}ABSOLUTEJS${colors.reset}`;
31114
+ const ver = `${colors.dim}v${version}${colors.reset}`;
31115
+ const time = `${colors.dim}ready in${colors.reset} ${colors.bold}${getDurationString(duration)}${colors.reset}`;
31116
+ console.log("");
31117
+ console.log(` ${name} ${ver} ${time}`);
31118
+ console.log("");
31119
+ console.log(` ${colors.green}\u279C${colors.reset} ${colors.bold}Local:${colors.reset} ${protocol}://${host === "0.0.0.0" ? "localhost" : host}:${port}/`);
31120
+ if (networkUrl) {
31121
+ console.log(` ${colors.green}\u279C${colors.reset} ${colors.bold}Network:${colors.reset} ${networkUrl}`);
31486
31122
  }
31487
- for (let index = 0;index < childImportSpecs.length; index += 1) {
31488
- const spec = childImportSpecs[index];
31489
- const resolvedModuleImport = resolvedModuleImports[index];
31490
- if (!spec || !resolvedModuleImport)
31491
- continue;
31492
- if (resolvedChildModules[index])
31493
- continue;
31494
- rewrittenModulePaths.set(spec, ensureRelativeImportPath(getCachedModulePath(sourcePath), resolvedModuleImport));
31123
+ console.log("");
31124
+ };
31125
+ var init_startupBanner = __esm(() => {
31126
+ init_constants();
31127
+ init_getDurationString();
31128
+ colors = {
31129
+ bold: "\x1B[1m",
31130
+ cyan: "\x1B[36m",
31131
+ dim: "\x1B[2m",
31132
+ green: "\x1B[32m",
31133
+ reset: "\x1B[0m"
31134
+ };
31135
+ MONTHS = [
31136
+ "Jan",
31137
+ "Feb",
31138
+ "Mar",
31139
+ "Apr",
31140
+ "May",
31141
+ "Jun",
31142
+ "Jul",
31143
+ "Aug",
31144
+ "Sep",
31145
+ "Oct",
31146
+ "Nov",
31147
+ "Dec"
31148
+ ];
31149
+ });
31150
+
31151
+ // src/utils/logger.ts
31152
+ var colors2, frameworkColors, formatPath = (filePath) => {
31153
+ const cwd = process.cwd();
31154
+ let relative3 = filePath.startsWith(cwd) ? filePath.slice(cwd.length + 1) : filePath;
31155
+ relative3 = relative3.replace(/\\/g, "/");
31156
+ if (!relative3.startsWith("/")) {
31157
+ relative3 = `/${relative3}`;
31495
31158
  }
31496
- let compiledCode = compile(transpiled, {
31497
- css: "injected",
31498
- experimental: {
31499
- async: loweredSource.transformed
31500
- },
31501
- filename: sourcePath,
31502
- generate: "server"
31503
- }).js.code;
31504
- for (const [spec, compiledChildPath] of childModulePaths) {
31505
- compiledCode = compiledCode.replaceAll(spec, ensureRelativeImportPath(getCachedModulePath(sourcePath), compiledChildPath));
31159
+ return relative3;
31160
+ }, getFrameworkColor = (framework) => frameworkColors[framework] || colors2.white, log = (action, options) => {
31161
+ const timestamp = `${colors2.dim}${formatTimestamp()}${colors2.reset}`;
31162
+ const tag = `${colors2.cyan}[hmr]${colors2.reset}`;
31163
+ let message = action;
31164
+ if (options?.path) {
31165
+ const pathColor = options.framework ? getFrameworkColor(options.framework) : colors2.white;
31166
+ message += ` ${pathColor}${formatPath(options.path)}${colors2.reset}`;
31506
31167
  }
31507
- for (const [spec, resolvedModuleImport] of rewrittenModulePaths) {
31508
- compiledCode = compiledCode.replaceAll(spec, resolvedModuleImport);
31168
+ if (options?.duration !== undefined) {
31169
+ message += ` ${colors2.dim}(${options.duration}ms)${colors2.reset}`;
31509
31170
  }
31510
- const compiledModulePath = getCachedModulePath(sourcePath);
31511
- await mkdir(dirname5(compiledModulePath), { recursive: true });
31512
- await writeIfChanged2(compiledModulePath, compiledCode);
31513
- compiledModuleCache.set(sourcePath, compiledModulePath);
31514
- return compiledModulePath;
31171
+ console.log(`${timestamp} ${tag} ${message}`);
31172
+ }, logCssUpdate = (path, framework, duration) => {
31173
+ log("css update", { duration, framework: framework ?? "css", path });
31174
+ }, logError = (message, error) => {
31175
+ const timestamp = `${colors2.dim}${formatTimestamp()}${colors2.reset}`;
31176
+ const tag = `${colors2.red}[hmr]${colors2.reset}`;
31177
+ const errorMsg = error instanceof Error ? error.message : error;
31178
+ const fullMessage = `${colors2.red}error${colors2.reset} ${message}${errorMsg ? `: ${errorMsg}` : ""}`;
31179
+ console.error(`${timestamp} ${tag} ${fullMessage}`);
31180
+ }, logHmrUpdate = (path, framework, duration) => {
31181
+ log("hmr update", { duration, framework, path });
31182
+ }, logScriptUpdate = (path, framework, duration) => {
31183
+ log("script update", { duration, framework, path });
31184
+ }, logServerReload = () => {
31185
+ log(`${colors2.cyan}server module reloaded${colors2.reset}`);
31186
+ }, logWarn = (message) => {
31187
+ const timestamp = `${colors2.dim}${formatTimestamp()}${colors2.reset}`;
31188
+ const tag = `${colors2.yellow}[hmr]${colors2.reset}`;
31189
+ console.warn(`${timestamp} ${tag} ${colors2.yellow}warning${colors2.reset} ${message}`);
31515
31190
  };
31516
- var init_svelteServerModule = __esm(() => {
31517
- init_lowerIslandSyntax();
31518
- serverCacheRoot = join4(process.cwd(), ".absolutejs", "islands", "svelte");
31519
- compiledModuleCache = new Map;
31520
- transpiler = new Bun.Transpiler({
31521
- loader: "ts",
31522
- target: "browser"
31523
- });
31191
+ var init_logger = __esm(() => {
31192
+ init_startupBanner();
31193
+ colors2 = {
31194
+ blue: "\x1B[34m",
31195
+ bold: "\x1B[1m",
31196
+ cyan: "\x1B[36m",
31197
+ dim: "\x1B[2m",
31198
+ green: "\x1B[32m",
31199
+ magenta: "\x1B[35m",
31200
+ red: "\x1B[31m",
31201
+ reset: "\x1B[0m",
31202
+ white: "\x1B[37m",
31203
+ yellow: "\x1B[33m"
31204
+ };
31205
+ frameworkColors = {
31206
+ angular: colors2.magenta,
31207
+ assets: colors2.dim,
31208
+ css: colors2.cyan,
31209
+ html: colors2.white,
31210
+ htmx: colors2.white,
31211
+ react: colors2.blue,
31212
+ svelte: colors2.yellow,
31213
+ vue: colors2.green
31214
+ };
31524
31215
  });
31525
31216
 
31526
- // src/core/islandMarkupAttributes.ts
31527
- var getIslandMarkerAttributes = (props, islandId) => ({
31528
- "data-component": props.component,
31529
- "data-framework": props.framework,
31530
- "data-hydrate": props.hydrate ?? "load",
31531
- "data-island": "true",
31532
- ...islandId ? { "data-island-id": islandId } : {},
31533
- "data-props": serializeIslandProps(props.props)
31534
- }), escapeHtmlAttribute = (value) => value.replaceAll("&", "&amp;").replaceAll('"', "&quot;").replaceAll("<", "&lt;").replaceAll(">", "&gt;"), serializeIslandAttributes = (attributes) => Object.entries(attributes).map(([key, value]) => `${key}="${escapeHtmlAttribute(value)}"`).join(" ");
31535
- var init_islandMarkupAttributes = __esm(() => {
31536
- init_islands();
31537
- });
31217
+ // src/utils/normalizePath.ts
31218
+ var normalizePath = (path) => path.replace(/\\/g, "/");
31538
31219
 
31539
- // src/core/renderIslandMarkup.ts
31540
- var islandSequence = 0, resolvedServerComponentCache, resolvedServerBuildComponentCache, nextIslandId = () => {
31541
- islandSequence += 1;
31542
- return `island-${islandSequence}`;
31543
- }, isRecord4 = (value) => typeof value === "object" && value !== null, isReactServerIslandComponent = (value) => typeof value === "function", isSvelteServerIslandComponent = (value) => typeof value === "function", isVueServerIslandComponent = (value) => typeof value === "function" || isRecord4(value), isAngularServerIslandComponent = (value) => typeof value === "function", resolveBuildReferencePath = (source, registryPath) => source.startsWith("file://") ? new URL(source).pathname : source.startsWith(".") ? new URL(source, registryPath).pathname : source, loadServerBuildComponent = async (buildReferencePath) => {
31544
- const cachedBuildComponent = resolvedServerBuildComponentCache.get(buildReferencePath);
31545
- if (cachedBuildComponent) {
31546
- return cachedBuildComponent;
31547
- }
31548
- const loadPromise = (async () => {
31549
- const compiledModulePath = await compileSvelteServerModule(buildReferencePath);
31550
- const loadedModule = await import(compiledModulePath);
31551
- return "default" in loadedModule ? loadedModule.default : loadedModule;
31552
- })();
31553
- resolvedServerBuildComponentCache.set(buildReferencePath, loadPromise);
31554
- return loadPromise;
31555
- }, loadServerImportComponent = async (resolvedComponent) => {
31556
- const resolvedModulePath = resolvedComponent.startsWith(".") ? new URL(resolvedComponent, import.meta.url).pathname : resolvedComponent;
31557
- const importTarget = resolvedModulePath.endsWith(".svelte") ? await compileSvelteServerModule(resolvedModulePath) : resolvedModulePath;
31558
- const loadedModule = await import(importTarget);
31559
- return "default" in loadedModule ? loadedModule.default : loadedModule;
31560
- }, resolveServerIslandComponent = async (component) => {
31561
- const cachedResolvedComponent = resolvedServerComponentCache.get(component);
31562
- if (cachedResolvedComponent) {
31563
- return cachedResolvedComponent;
31220
+ // src/build/generateManifest.ts
31221
+ var exports_generateManifest = {};
31222
+ __export(exports_generateManifest, {
31223
+ generateManifest: () => generateManifest
31224
+ });
31225
+ import { extname as extname3 } from "path";
31226
+ var getManifestKey = (folder, pascalName, isClientComponent, isReact, isVue, isSvelte, isAngular) => {
31227
+ if (folder === "indexes")
31228
+ return `${pascalName}Index`;
31229
+ if (isClientComponent)
31230
+ return `${pascalName}Client`;
31231
+ if (folder !== "pages")
31232
+ return pascalName;
31233
+ if (isReact)
31234
+ return `${pascalName}Page`;
31235
+ if (isVue || isSvelte || isAngular)
31236
+ return pascalName;
31237
+ return `${pascalName}Page`;
31238
+ }, getCssKey = (pascalName, segments) => {
31239
+ const isFromVue = segments.some((seg) => seg === "vue");
31240
+ if (isFromVue && segments.includes("css"))
31241
+ return `${pascalName}CompiledCSS`;
31242
+ const isFromReact = segments.some((seg) => seg === "react");
31243
+ const isFromSvelte = segments.some((seg) => seg === "svelte");
31244
+ const isFromAngular = segments.some((seg) => seg === "angular");
31245
+ if (isFromReact || isFromVue || isFromSvelte || isFromAngular)
31246
+ return `${pascalName}BundledCSS`;
31247
+ return `${pascalName}CSS`;
31248
+ }, generateManifest = (outputs, buildPath) => outputs.reduce((manifest, artifact) => {
31249
+ const normalizedArtifactPath = normalizePath(artifact.path);
31250
+ const normalizedBuildPath = normalizePath(buildPath);
31251
+ let relative3 = normalizedArtifactPath.startsWith(normalizedBuildPath) ? normalizedArtifactPath.slice(normalizedBuildPath.length) : normalizedArtifactPath;
31252
+ relative3 = relative3.replace(/^\/+/, "");
31253
+ const segments = relative3.split("/");
31254
+ const fileWithHash = segments.pop();
31255
+ if (!fileWithHash)
31256
+ return manifest;
31257
+ const [baseName] = fileWithHash.split(`.${artifact.hash}.`);
31258
+ if (!baseName)
31259
+ return manifest;
31260
+ const pascalName = toPascal(baseName);
31261
+ const ext = extname3(fileWithHash);
31262
+ const islandIndex = segments.findIndex((seg) => seg === "islands");
31263
+ if (ext === ".css") {
31264
+ const cssKey = getCssKey(pascalName, segments);
31265
+ if (manifest[cssKey] && manifest[cssKey] !== `/${relative3}`)
31266
+ logWarn(`Duplicate manifest key "${cssKey}" \u2014 "${manifest[cssKey]}" will be overwritten by "/${relative3}". Use unique page names across frameworks.`);
31267
+ manifest[cssKey] = `/${relative3}`;
31268
+ return manifest;
31564
31269
  }
31565
- const resolutionPromise = (async () => {
31566
- const buildReference = getIslandBuildReference(component);
31567
- const buildReferencePath = buildReference?.source ? resolveBuildReferencePath(buildReference.source, import.meta.url) : null;
31568
- if (buildReferencePath?.endsWith(".svelte")) {
31569
- return loadServerBuildComponent(buildReferencePath);
31570
- }
31571
- const resolvedComponent = getIslandComponent(component);
31572
- if (typeof resolvedComponent !== "string") {
31573
- return resolvedComponent;
31270
+ if (islandIndex > UNFOUND_INDEX) {
31271
+ const frameworkSegment = segments[islandIndex + 1];
31272
+ if (frameworkSegment === "react" || frameworkSegment === "svelte" || frameworkSegment === "vue" || frameworkSegment === "angular") {
31273
+ const manifestKey2 = getIslandManifestKey(frameworkSegment, pascalName);
31274
+ manifest[manifestKey2] = `/${relative3}`;
31275
+ return manifest;
31574
31276
  }
31575
- return loadServerImportComponent(resolvedComponent);
31576
- })();
31577
- resolvedServerComponentCache.set(component, resolutionPromise);
31578
- return resolutionPromise;
31579
- }, resolveReactServerIslandComponent = async (component) => {
31580
- const resolvedComponent = await resolveServerIslandComponent(component);
31581
- if (!isReactServerIslandComponent(resolvedComponent)) {
31582
- throw new Error("Resolved React island is not a valid React component.");
31583
31277
  }
31584
- return resolvedComponent;
31585
- }, resolveSvelteServerIslandComponent = async (component) => {
31586
- const resolvedComponent = await resolveServerIslandComponent(component);
31587
- if (!isSvelteServerIslandComponent(resolvedComponent)) {
31588
- throw new Error("Resolved Svelte island is not a valid Svelte component.");
31278
+ const idx = segments.findIndex((seg) => seg === "indexes" || seg === "pages" || seg === "client");
31279
+ const folder = idx > UNFOUND_INDEX ? segments[idx] : segments[0];
31280
+ const isReact = segments.some((seg) => seg === "react");
31281
+ const isVue = segments.some((seg) => seg === "vue");
31282
+ const isSvelte = segments.some((seg) => seg === "svelte");
31283
+ const isAngular = segments.some((seg) => seg === "angular");
31284
+ const isClientComponent = segments.includes("client");
31285
+ const manifestKey = getManifestKey(folder, pascalName, isClientComponent, isReact, isVue, isSvelte, isAngular);
31286
+ if (manifest[manifestKey] && manifest[manifestKey] !== `/${relative3}`) {
31287
+ logWarn(`Duplicate manifest key "${manifestKey}" \u2014 "${manifest[manifestKey]}" will be overwritten by "/${relative3}". Use unique page names across frameworks.`);
31589
31288
  }
31590
- return resolvedComponent;
31591
- }, resolveVueServerIslandComponent = async (component) => {
31592
- const resolvedComponent = await resolveServerIslandComponent(component);
31593
- if (!isVueServerIslandComponent(resolvedComponent)) {
31594
- throw new Error("Resolved Vue island is not a valid Vue component.");
31289
+ manifest[manifestKey] = `/${relative3}`;
31290
+ return manifest;
31291
+ }, {});
31292
+ var init_generateManifest = __esm(() => {
31293
+ init_constants();
31294
+ init_logger();
31295
+ });
31296
+
31297
+ // src/build/generateReactIndexes.ts
31298
+ var exports_generateReactIndexes = {};
31299
+ __export(exports_generateReactIndexes, {
31300
+ generateReactIndexFiles: () => generateReactIndexFiles
31301
+ });
31302
+ import { existsSync as existsSync5, mkdirSync as mkdirSync3 } from "fs";
31303
+ import { readdir, rm, writeFile } from "fs/promises";
31304
+ import { basename as basename2, join as join4, relative as relative3, resolve as resolve9, sep } from "path";
31305
+ var {Glob: Glob2 } = globalThis.Bun;
31306
+ var indexContentCache, resolveDevClientDir = () => {
31307
+ const projectRoot = process.cwd();
31308
+ const fromSource = resolve9(import.meta.dir, "../dev/client");
31309
+ if (existsSync5(fromSource) && fromSource.startsWith(projectRoot)) {
31310
+ return fromSource;
31595
31311
  }
31596
- return resolvedComponent;
31597
- }, resolveAngularServerIslandComponent = async (component) => {
31598
- const resolvedComponent = await resolveServerIslandComponent(component);
31599
- if (!isAngularServerIslandComponent(resolvedComponent)) {
31600
- throw new Error("Resolved Angular island is not a valid Angular component.");
31312
+ const fromNodeModules = resolve9(projectRoot, "node_modules/@absolutejs/absolute/dist/dev/client");
31313
+ if (existsSync5(fromNodeModules))
31314
+ return fromNodeModules;
31315
+ return resolve9(import.meta.dir, "./dev/client");
31316
+ }, devClientDir, errorOverlayPath, hmrClientPath, refreshSetupPath, generateReactIndexFiles = async (reactPagesDirectory, reactIndexesDirectory, isDev2 = false) => {
31317
+ if (!existsSync5(reactIndexesDirectory)) {
31318
+ mkdirSync3(reactIndexesDirectory, { recursive: true });
31601
31319
  }
31602
- return resolvedComponent;
31603
- }, renderIslandResult = async (registry, props) => {
31604
- const islandId = nextIslandId();
31605
- const attributes = getIslandMarkerAttributes(props);
31606
- if (props.framework === "react") {
31607
- const entry = registry.react?.[props.component];
31608
- if (!entry) {
31609
- throw new Error(`Island component "${props.component}" is not registered for framework "react".`);
31610
- }
31611
- const component = await resolveReactServerIslandComponent(entry);
31612
- const html = renderReactIslandToHtml(component, props.props);
31613
- return { attributes, html };
31320
+ const CONVENTION_RE = /^(?:(.+)\.)?(error|loading|not-found)\.[^.]+$/;
31321
+ const pagesGlob = new Glob2("*.*");
31322
+ const files = [];
31323
+ for await (const file2 of pagesGlob.scan({ cwd: reactPagesDirectory })) {
31324
+ if (CONVENTION_RE.test(file2))
31325
+ continue;
31326
+ files.push(file2);
31614
31327
  }
31615
- if (props.framework === "svelte") {
31616
- const entry = registry.svelte?.[props.component];
31617
- if (!entry) {
31618
- throw new Error(`Island component "${props.component}" is not registered for framework "svelte".`);
31619
- }
31620
- const component = await resolveSvelteServerIslandComponent(entry);
31621
- const html = renderSvelteIslandToHtml(component, props.props);
31622
- return { attributes, html };
31328
+ const currentPageNames = new Set(files.map((file2) => basename2(file2).split(".")[0]));
31329
+ const emptyStringArray = [];
31330
+ const existingIndexes = await readdir(reactIndexesDirectory).catch(() => emptyStringArray);
31331
+ const staleIndexes = existingIndexes.filter((indexFile) => {
31332
+ const indexName = indexFile.replace(/\.tsx$/, "");
31333
+ return indexName !== "_refresh" && !currentPageNames.has(indexName);
31334
+ });
31335
+ if (staleIndexes.length > 0) {
31336
+ await Promise.all(staleIndexes.map((indexFile) => {
31337
+ indexContentCache.delete(join4(reactIndexesDirectory, indexFile));
31338
+ return rm(join4(reactIndexesDirectory, indexFile), {
31339
+ force: true
31340
+ });
31341
+ }));
31623
31342
  }
31624
- if (props.framework === "vue") {
31625
- const entry = registry.vue?.[props.component];
31626
- if (!entry) {
31627
- throw new Error(`Island component "${props.component}" is not registered for framework "vue".`);
31343
+ const pagesRelPath = relative3(resolve9(reactIndexesDirectory), resolve9(reactPagesDirectory)).split(sep).join("/");
31344
+ const promises = files.map(async (file2) => {
31345
+ const fileName = basename2(file2);
31346
+ const [componentName] = fileName.split(".");
31347
+ const hmrPreamble = isDev2 ? [
31348
+ `window.__HMR_FRAMEWORK__ = "react";`,
31349
+ `window.__REACT_COMPONENT_KEY__ = "${componentName}Index";`,
31350
+ `import '${refreshSetupPath}';`,
31351
+ `import '${hmrClientPath}';`,
31352
+ `import { showErrorOverlay, hideErrorOverlay } from '${errorOverlayPath}';
31353
+ `
31354
+ ] : [];
31355
+ const reactImports = isDev2 ? [
31356
+ `import { hydrateRoot, createRoot } from 'react-dom/client';`,
31357
+ `import { createElement, Component } from 'react';`
31358
+ ] : [
31359
+ `import { hydrateRoot, createRoot } from 'react-dom/client';`,
31360
+ `import { createElement } from 'react';`
31361
+ ];
31362
+ const errorBoundaryDef = isDev2 ? [
31363
+ `
31364
+ // Dev-only Error Boundary to catch React render errors`,
31365
+ `class ErrorBoundary extends Component {`,
31366
+ ` constructor(props) {`,
31367
+ ` super(props);`,
31368
+ ` this.state = { hasError: false };`,
31369
+ ` window.__ERROR_BOUNDARY__ = this;`,
31370
+ ` }`,
31371
+ ` static getDerivedStateFromError() {`,
31372
+ ` return { hasError: true };`,
31373
+ ` }`,
31374
+ ` componentDidCatch(error) {`,
31375
+ ` showErrorOverlay({`,
31376
+ ` framework: 'react',`,
31377
+ ` kind: 'runtime',`,
31378
+ ` message: error && error.stack ? error.stack : String(error)`,
31379
+ ` });`,
31380
+ ` }`,
31381
+ ` componentDidUpdate(prevProps, prevState) {`,
31382
+ ` if (prevState.hasError && !this.state.hasError) {`,
31383
+ ` hideErrorOverlay();`,
31384
+ ` }`,
31385
+ ` }`,
31386
+ ` reset() {`,
31387
+ ` this.setState({ hasError: false });`,
31388
+ ` }`,
31389
+ ` render() {`,
31390
+ ` if (this.state.hasError) return null;`,
31391
+ ``,
31392
+ ` return this.props.children;`,
31393
+ ` }`,
31394
+ `}
31395
+ `
31396
+ ] : [];
31397
+ const content = [
31398
+ ...hmrPreamble,
31399
+ ...reactImports,
31400
+ `import type { ComponentType } from 'react'`,
31401
+ `import { ${componentName} } from '${pagesRelPath}/${componentName}';
31402
+ `,
31403
+ `type PropsOf<C> = C extends ComponentType<infer P> ? P : never;
31404
+ `,
31405
+ `declare global {`,
31406
+ ` interface Window {`,
31407
+ ` __INITIAL_PROPS__?: PropsOf<typeof ${componentName}>`,
31408
+ ` __REACT_ROOT__?: ReturnType<typeof hydrateRoot | typeof createRoot>`,
31409
+ ` __HMR_CLIENT_ONLY_MODE__?: boolean`,
31410
+ ` }`,
31411
+ `}
31412
+ `,
31413
+ ...errorBoundaryDef,
31414
+ `// Hydration with error handling and fallback`,
31415
+ `const isDev = ${isDev2};`,
31416
+ `const componentPath = '${pagesRelPath}/${componentName}';
31417
+ `,
31418
+ `function isHydrationError(error) {`,
31419
+ ` if (!error) return false;`,
31420
+ ` const errorMessage = error instanceof Error ? error.message : String(error);`,
31421
+ ` const errorString = String(error);`,
31422
+ ` const fullMessage = errorMessage + ' ' + errorString;`,
31423
+ ` const hydrationKeywords = ['hydration', 'Hydration', 'mismatch', 'Mismatch', 'did not match', 'server rendered HTML', 'server HTML', 'client HTML', 'Hydration failed'];`,
31424
+ ` const isHydration = hydrationKeywords.some(keyword => fullMessage.includes(keyword));`,
31425
+ ` `,
31426
+ ` // Ignore whitespace-only mismatches in <head> - these are harmless formatting differences`,
31427
+ ` // The error often shows: + <link...> vs - {"\\n "} which is just formatting`,
31428
+ ` if (isHydration) {`,
31429
+ ` // Check if this is a head/link/stylesheet related mismatch`,
31430
+ ` const isHeadRelated = fullMessage.includes('<head') || fullMessage.includes('</head>') || fullMessage.includes('head>') || fullMessage.includes('<link') || fullMessage.includes('link>') || fullMessage.includes('stylesheet') || fullMessage.includes('fonts.googleapis') || fullMessage.includes('rel="stylesheet"');`,
31431
+ ` `,
31432
+ ` // Check if the mismatch involves only whitespace/newlines`,
31433
+ ` // Pattern: looks for {"\\n"} or {"\\n "} or similar whitespace-only content`,
31434
+ ` // Also check for patterns like: - {"\\n "} or + <link...>`,
31435
+ ` const hasWhitespacePattern = /\\{\\s*["']\\\\n[^"']*["']\\s*\\}/.test(fullMessage) || /\\{\\s*["'][\\\\n\\\\r\\\\s]+["']\\s*\\}/.test(fullMessage) || /-\\s*\\{\\s*["'][\\\\n\\\\r\\\\s]+["']\\s*\\}/.test(fullMessage);`,
31436
+ ` const isWhitespaceOnly = /^[\\s\\n\\r]*$/.test(errorString) || /^[\\s\\n\\r]*$/.test(errorMessage);`,
31437
+ ` const hasNewlinePattern = fullMessage.includes('\\\\n') || fullMessage.includes('\\\\r') || fullMessage.includes('\\n') || fullMessage.includes('\\r');`,
31438
+ ` `,
31439
+ ` // If it's head-related and involves whitespace/newlines, ignore it`,
31440
+ ` if (isHeadRelated && (hasWhitespacePattern || isWhitespaceOnly || hasNewlinePattern)) {`,
31441
+ ` return false; // Don't treat whitespace-only head mismatches as errors`,
31442
+ ` }`,
31443
+ ` }`,
31444
+ ` return isHydration;`,
31445
+ `}
31446
+ `,
31447
+ `function logHydrationError(error, componentName) {`,
31448
+ ` if (!isDev) return;`,
31449
+ ` if (window.__HMR_WS__ && window.__HMR_WS__.readyState === WebSocket.OPEN) {`,
31450
+ ` try {`,
31451
+ ` window.__HMR_WS__.send(JSON.stringify({`,
31452
+ ` type: 'hydration-error',`,
31453
+ ` data: {`,
31454
+ ` componentName: '${componentName}',`,
31455
+ ` componentPath: componentPath,`,
31456
+ ` error: error instanceof Error ? error.message : String(error),`,
31457
+ ` timestamp: Date.now()`,
31458
+ ` }`,
31459
+ ` }));`,
31460
+ ` } catch (err) {}`,
31461
+ ` }`,
31462
+ `}
31463
+ `,
31464
+ `// Track if we've already switched to client-only mode`,
31465
+ `let hasSwitchedToClientOnly = false;`,
31466
+ `let hydrationErrorDetected = false;
31467
+ `,
31468
+ `function handleHydrationFallback(error) {`,
31469
+ ` if (hasSwitchedToClientOnly) return; // Already handled`,
31470
+ ` hasSwitchedToClientOnly = true;`,
31471
+ ` hydrationErrorDetected = true;
31472
+ `,
31473
+ ` logHydrationError(error, '${componentName}');
31474
+ `,
31475
+ ` // Fallback: client-only render (no hydration)`,
31476
+ ` try {`,
31477
+ ` // Unmount existing root if it exists`,
31478
+ ` if (window.__REACT_ROOT__ && typeof window.__REACT_ROOT__.unmount === 'function') {`,
31479
+ ` try {`,
31480
+ ` window.__REACT_ROOT__.unmount();`,
31481
+ ` } catch (e) {`,
31482
+ ` // Ignore unmount errors`,
31483
+ ` }`,
31484
+ ` }
31485
+ `,
31486
+ ` // Render into the same root container when falling back to client-only`,
31487
+ ` const root = createRoot(container);`,
31488
+ ` root.render(${isDev2 ? `createElement(ErrorBoundary, null, createElement(${componentName}, mergedProps))` : `createElement(${componentName}, mergedProps)`});`,
31489
+ ` window.__REACT_ROOT__ = root;`,
31490
+ ` window.__HMR_CLIENT_ONLY_MODE__ = true;`,
31491
+ ` } catch (fallbackError) {`,
31492
+ ` window.location.reload();`,
31493
+ ` }`,
31494
+ `}
31495
+ `,
31496
+ `// HMR State Preservation: Check for preserved state and merge with initial props`,
31497
+ `// This allows state to be preserved across HMR updates without modifying component files`,
31498
+ `let preservedState = (typeof window !== 'undefined' && window.__HMR_PRESERVED_STATE__) ? window.__HMR_PRESERVED_STATE__ : {};
31499
+ `,
31500
+ `// Also check sessionStorage for state that survived a page reload (for React HMR)`,
31501
+ `if (typeof window !== 'undefined' && typeof sessionStorage !== 'undefined') {`,
31502
+ ` const hmrStateJson = sessionStorage.getItem('__REACT_HMR_STATE__');`,
31503
+ ` if (hmrStateJson) {`,
31504
+ ` try {`,
31505
+ ` const hmrState = JSON.parse(hmrStateJson);`,
31506
+ ` preservedState = { ...preservedState, ...hmrState };`,
31507
+ ` sessionStorage.removeItem('__REACT_HMR_STATE__');`,
31508
+ ` } catch (e) {}`,
31509
+ ` }`,
31510
+ `}
31511
+ `,
31512
+ `const mergedProps = { ...(window.__INITIAL_PROPS__ || {}), ...preservedState };`,
31513
+ `// Clear preserved state after using it (so it doesn't persist across multiple updates)`,
31514
+ `if (typeof window !== 'undefined') {`,
31515
+ ` window.__HMR_PRESERVED_STATE__ = undefined;`,
31516
+ `}
31517
+ `,
31518
+ `// Attempt hydration with error handling`,
31519
+ `// Use document (not document.body) when the page renders <html><head><body>`,
31520
+ `// to avoid "In HTML, <html> cannot be a child of <body>" hydration error`,
31521
+ `const container = typeof document !== 'undefined' ? document : null;`,
31522
+ `if (!container) {`,
31523
+ ` throw new Error('React root container not found: document is null');`,
31524
+ `}
31525
+ `,
31526
+ `// Guard: only hydrate on first load. During HMR re-imports, skip hydration`,
31527
+ `// so React Fast Refresh can swap components in-place and preserve state.`,
31528
+ `if (!window.__REACT_ROOT__) {`,
31529
+ ` let root;`,
31530
+ ` // After HMR, SSR is skipped to avoid stale content flash \u2014 render client-only`,
31531
+ ` if (window.__SSR_DIRTY__) {`,
31532
+ ` root = createRoot(container);`,
31533
+ ` root.render(${isDev2 ? `createElement(ErrorBoundary, null, createElement(${componentName}, mergedProps))` : `createElement(${componentName}, mergedProps)`});`,
31534
+ ` window.__REACT_ROOT__ = root;`,
31535
+ ` } else {`,
31536
+ ` try {`,
31537
+ ` // Use onRecoverableError to catch hydration errors (React 19)`,
31538
+ ` root = hydrateRoot(`,
31539
+ ` container,`,
31540
+ ` ${isDev2 ? `createElement(ErrorBoundary, null, createElement(${componentName}, mergedProps))` : `createElement(${componentName}, mergedProps)`},`,
31541
+ ` {`,
31542
+ ` onRecoverableError: (error) => {`,
31543
+ ` // Check if this is a hydration error (isHydrationError filters out whitespace-only head mismatches)`,
31544
+ ` if (isDev && isHydrationError(error)) {`,
31545
+ ` // Real hydration error - handle it`,
31546
+ ` handleHydrationFallback(error);`,
31547
+ ` } else {`,
31548
+ ` // Not a hydration error, or it's a whitespace-only mismatch that was filtered out`,
31549
+ ` // Check if it's a whitespace-only head mismatch using the same logic as isHydrationError`,
31550
+ ` const errorMessage = error instanceof Error ? error.message : String(error);`,
31551
+ ` const errorString = String(error);`,
31552
+ ` const fullMessage = errorMessage + ' ' + errorString;`,
31553
+ ` const hydrationKeywords = ['hydration', 'Hydration', 'mismatch', 'Mismatch', 'did not match', 'server rendered HTML', 'server HTML', 'client HTML', 'Hydration failed'];`,
31554
+ ` const isHydration = hydrationKeywords.some(keyword => fullMessage.includes(keyword));`,
31555
+ ` if (isHydration) {`,
31556
+ ` // Check if this is a head/link/stylesheet related mismatch`,
31557
+ ` const isHeadRelated = fullMessage.includes('<head') || fullMessage.includes('</head>') || fullMessage.includes('head>') || fullMessage.includes('<link') || fullMessage.includes('link>') || fullMessage.includes('stylesheet') || fullMessage.includes('fonts.googleapis') || fullMessage.includes('rel="stylesheet"');`,
31558
+ ` // Check if the mismatch involves only whitespace/newlines`,
31559
+ ` const hasWhitespacePattern = /\\{\\s*["']\\\\n[^"']*["']\\s*\\}/.test(fullMessage) || /\\{\\s*["'][\\\\n\\\\r\\\\s]+["']\\s*\\}/.test(fullMessage) || /-\\s*\\{\\s*["'][\\\\n\\\\r\\\\s]+["']\\s*\\}/.test(fullMessage);`,
31560
+ ` const isWhitespaceOnly = /^[\\s\\n\\r]*$/.test(errorString) || /^[\\s\\n\\r]*$/.test(errorMessage);`,
31561
+ ` const hasNewlinePattern = fullMessage.includes('\\\\n') || fullMessage.includes('\\\\r') || fullMessage.includes('\\n') || fullMessage.includes('\\r');`,
31562
+ ` // If it's head-related and involves whitespace/newlines, silently ignore it`,
31563
+ ` if (isHeadRelated && (hasWhitespacePattern || isWhitespaceOnly || hasNewlinePattern)) {`,
31564
+ ` // Already logged by isHydrationError, just return silently`,
31565
+ ` return;`,
31566
+ ` }`,
31567
+ ` }`,
31568
+ ` // Log other recoverable errors`,
31569
+ ` console.error('React recoverable error:', error);`,
31570
+ ` }`,
31571
+ ` }`,
31572
+ ` }`,
31573
+ ` );`,
31574
+ ` window.__REACT_ROOT__ = root;`,
31575
+ ` } catch (error) {`,
31576
+ ` // Catch synchronous errors (shouldn't happen with hydrateRoot, but safety net)`,
31577
+ ` if (isDev && isHydrationError(error)) {`,
31578
+ ` handleHydrationFallback(error);`,
31579
+ ` } else {`,
31580
+ ` throw error;`,
31581
+ ` }`,
31582
+ ` }`,
31583
+ ` } // end else (normal hydration path)
31584
+ `,
31585
+ ` // Also listen for hydration errors via console.error (React logs them there)`,
31586
+ ` if (isDev) {`,
31587
+ ` const originalError = console.error;`,
31588
+ ` console.error = function(...args) {`,
31589
+ ` const errorMessage = args.map(arg => {`,
31590
+ ` if (arg instanceof Error) return arg.message;`,
31591
+ ` return String(arg);`,
31592
+ ` }).join(' ');`,
31593
+ ` `,
31594
+ ` // Check if this is a hydration error`,
31595
+ ` if (isHydrationError({ message: errorMessage }) && !hydrationErrorDetected) {`,
31596
+ ` hydrationErrorDetected = true;`,
31597
+ ` // Create a synthetic error for fallback`,
31598
+ ` const syntheticError = new Error(errorMessage);`,
31599
+ ` // Use setTimeout to ensure this happens after React's error handling`,
31600
+ ` setTimeout(() => {`,
31601
+ ` handleHydrationFallback(syntheticError);`,
31602
+ ` }, 0);`,
31603
+ ` }`,
31604
+ ` `,
31605
+ ` // Call original console.error`,
31606
+ ` originalError.apply(console, args);`,
31607
+ ` };`,
31608
+ ` }`,
31609
+ `}`,
31610
+ ...isDev2 ? [
31611
+ `
31612
+ // Pre-warm: import the page module from the module server`,
31613
+ `// immediately so the browser caches all /@src/ URLs.`,
31614
+ `import('/@src/${relative3(process.cwd(), resolve9(reactPagesDirectory, `${componentName}.tsx`)).replace(/\\/g, "/")}').catch(() => {});`
31615
+ ] : []
31616
+ ].join(`
31617
+ `);
31618
+ const indexPath = join4(reactIndexesDirectory, `${componentName}.tsx`);
31619
+ const hasher = new Bun.CryptoHasher("md5");
31620
+ hasher.update(content);
31621
+ const contentHash = hasher.digest("hex");
31622
+ if (indexContentCache.get(indexPath) === contentHash && existsSync5(indexPath)) {
31623
+ return;
31628
31624
  }
31629
- const component = await resolveVueServerIslandComponent(entry);
31630
- const html = await renderVueIslandToHtml(component, props.props);
31631
- return { attributes, html };
31625
+ indexContentCache.set(indexPath, contentHash);
31626
+ await writeFile(indexPath, content);
31627
+ });
31628
+ await Promise.all(promises);
31629
+ if (!isDev2) {
31630
+ return;
31632
31631
  }
31633
- if (props.framework === "angular") {
31634
- const entry = registry.angular?.[props.component];
31635
- if (!entry) {
31636
- throw new Error(`Island component "${props.component}" is not registered for framework "angular".`);
31637
- }
31638
- const component = await resolveAngularServerIslandComponent(entry);
31639
- const html = await renderAngularIslandToHtml(component, props.props, islandId);
31640
- return {
31641
- attributes: {
31642
- ...getIslandMarkerAttributes(props, islandId)
31643
- },
31644
- html
31645
- };
31632
+ const refreshPath = join4(reactIndexesDirectory, "_refresh.tsx");
31633
+ if (!existsSync5(refreshPath)) {
31634
+ await writeFile(refreshPath, `import '${refreshSetupPath}';
31635
+ import 'react';
31636
+ import 'react-dom/client';
31637
+ `);
31646
31638
  }
31647
- throw new Error(`Framework "${props.framework}" is not implemented in this prototype.`);
31648
- }, renderIslandMarkup = async (registry, props) => {
31649
- const result = await renderIslandResult(registry, props);
31650
- return `<div ${serializeIslandAttributes(result.attributes)}>${result.html}</div>`;
31651
31639
  };
31652
- var init_renderIslandMarkup = __esm(() => {
31653
- init_islandSsr();
31654
- init_svelteServerModule();
31655
- init_islandMarkupAttributes();
31656
- init_islands();
31657
- resolvedServerComponentCache = new Map;
31658
- resolvedServerBuildComponentCache = new Map;
31640
+ var init_generateReactIndexes = __esm(() => {
31641
+ indexContentCache = new Map;
31642
+ devClientDir = resolveDevClientDir();
31643
+ errorOverlayPath = join4(devClientDir, "errorOverlay.ts").replace(/\\/g, "/");
31644
+ hmrClientPath = join4(devClientDir, "hmrClient.ts").replace(/\\/g, "/");
31645
+ refreshSetupPath = join4(devClientDir, "reactRefreshSetup.ts").replace(/\\/g, "/");
31659
31646
  });
31660
31647
 
31661
- // src/build/staticIslandPages.ts
31662
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync4 } from "fs";
31663
- var ISLAND_TAG_RE_SOURCE = "<island\\b([^>]*?)(?:\\/\\>|>(?:[\\s\\S]*?)<\\/island>)", ATTRIBUTE_RE_SOURCE = `([A-Za-z_:][-A-Za-z0-9_:.]*)\\s*=\\s*(?:"([^"]*)"|'([^']*)')`, islandFrameworks2, islandHydrationModes2, isRecord5 = (value) => typeof value === "object" && value !== null, isIslandFramework2 = (value) => islandFrameworks2.some((framework) => framework === value), isIslandHydrationMode = (value) => islandHydrationModes2.some((mode) => mode === value), parseIslandAttributes = (attributeString) => {
31664
- const attributeRe = new RegExp(ATTRIBUTE_RE_SOURCE, "g");
31665
- const attributes = new Map;
31666
- let match = attributeRe.exec(attributeString);
31667
- while (match) {
31668
- const key = match[1];
31669
- const doubleQuotedValue = match[2];
31670
- const singleQuotedValue = match[3];
31671
- if (!key) {
31672
- match = attributeRe.exec(attributeString);
31673
- continue;
31674
- }
31675
- const value = doubleQuotedValue ?? singleQuotedValue ?? "";
31676
- attributes.set(key, value);
31677
- match = attributeRe.exec(attributeString);
31678
- }
31679
- return attributes;
31680
- }, parseIslandTag = (attributeString) => {
31681
- const attributes = parseIslandAttributes(attributeString);
31682
- const framework = attributes.get("framework");
31683
- const component = attributes.get("component");
31684
- const hydrate = attributes.get("hydrate") ?? "load";
31685
- const propsSource = attributes.get("props") ?? "{}";
31686
- if (!framework || !component) {
31648
+ // src/build/islandBindingCompat.ts
31649
+ import { resolve as resolve10 } from "path";
31650
+ var packageToFramework, compatFileNames, normalize = (value) => value.replace(/\\/g, "/"), isFrameworkPackage = (value) => (value in packageToFramework), resolveIslandCompatModule = (specifier, importer, frameworkDirs) => {
31651
+ if (!isFrameworkPackage(specifier)) {
31687
31652
  return null;
31688
31653
  }
31689
- if (!isIslandFramework2(framework)) {
31690
- throw new Error(`Unsupported static island framework "${framework}".`);
31654
+ const framework = packageToFramework[specifier];
31655
+ const frameworkDir = frameworkDirs[framework];
31656
+ if (!frameworkDir) {
31657
+ return null;
31691
31658
  }
31692
- if (!isIslandHydrationMode(hydrate)) {
31693
- throw new Error(`Unsupported static island hydrate mode "${hydrate}".`);
31659
+ const normalizedImporter = normalize(importer);
31660
+ const normalizedFrameworkDir = normalize(resolve10(frameworkDir));
31661
+ if (!normalizedImporter.startsWith(normalizedFrameworkDir)) {
31662
+ return null;
31694
31663
  }
31695
- let parsedProps;
31696
- try {
31697
- const candidate = JSON.parse(propsSource);
31698
- parsedProps = isRecord5(candidate) ? candidate : {};
31699
- } catch (error) {
31700
- throw new Error(`Failed to parse static island props JSON for ${framework}:${component}: ${error instanceof Error ? error.message : String(error)}`);
31664
+ if (normalizedImporter.includes("/generated/absolute-") || normalizedImporter.includes("/generated/Island.") || normalizedImporter.includes("/generated/islands.")) {
31665
+ return null;
31701
31666
  }
31702
- return {
31703
- component,
31704
- framework,
31705
- hydrate,
31706
- props: parsedProps
31667
+ return resolve10(frameworkDir, "generated", compatFileNames[framework]);
31668
+ };
31669
+ var init_islandBindingCompat = __esm(() => {
31670
+ packageToFramework = {
31671
+ "@absolutejs/absolute/angular": "angular",
31672
+ "@absolutejs/absolute/react": "react",
31673
+ "@absolutejs/absolute/svelte": "svelte",
31674
+ "@absolutejs/absolute/vue": "vue"
31707
31675
  };
31708
- }, transformStaticPage = async (pagePath, registry) => {
31709
- const originalHtml = readFileSync3(pagePath, "utf-8");
31710
- const islandTagRe = new RegExp(ISLAND_TAG_RE_SOURCE, "gi");
31711
- if (!islandTagRe.test(originalHtml)) {
31712
- return;
31713
- }
31714
- islandTagRe.lastIndex = 0;
31715
- let transformedHtml = "";
31716
- let lastIndex = 0;
31717
- let match = islandTagRe.exec(originalHtml);
31718
- while (match) {
31719
- const fullMatch = match[0];
31720
- const attributeString = match[1] ?? "";
31721
- const matchIndex = match.index;
31722
- transformedHtml += originalHtml.slice(lastIndex, matchIndex);
31723
- const props = parseIslandTag(attributeString);
31724
- transformedHtml += props ? await renderIslandMarkup(registry, props) : fullMatch;
31725
- lastIndex = matchIndex + fullMatch.length;
31726
- match = islandTagRe.exec(originalHtml);
31676
+ compatFileNames = {
31677
+ angular: "absolute-angular.ts",
31678
+ react: "absolute-react.ts",
31679
+ svelte: "absolute-svelte.ts",
31680
+ vue: "absolute-vue.ts"
31681
+ };
31682
+ });
31683
+
31684
+ // src/build/islandBindingPlugin.ts
31685
+ var exports_islandBindingPlugin = {};
31686
+ __export(exports_islandBindingPlugin, {
31687
+ createIslandBindingPlugin: () => createIslandBindingPlugin
31688
+ });
31689
+ var createIslandBindingPlugin = (frameworkDirs) => ({
31690
+ name: "absolute-island-binding-plugin",
31691
+ setup(build2) {
31692
+ build2.onResolve({
31693
+ filter: /^@absolutejs\/absolute\/(react|vue|svelte|angular)$/
31694
+ }, (args) => {
31695
+ const redirected = resolveIslandCompatModule(args.path, args.importer, frameworkDirs);
31696
+ if (!redirected) {
31697
+ return;
31698
+ }
31699
+ return {
31700
+ path: redirected
31701
+ };
31702
+ });
31727
31703
  }
31728
- transformedHtml += originalHtml.slice(lastIndex);
31729
- if (transformedHtml !== originalHtml) {
31730
- writeFileSync4(pagePath, transformedHtml);
31704
+ });
31705
+ var init_islandBindingPlugin = __esm(() => {
31706
+ init_islandBindingCompat();
31707
+ });
31708
+
31709
+ // src/build/wrapHTMLScript.ts
31710
+ var wrapHTMLScriptWithHMR = (code, scriptId) => {
31711
+ const escapedId = JSON.stringify(scriptId);
31712
+ return `${code}
31713
+
31714
+ // HMR acceptance - allows this script to be hot-reloaded
31715
+ if (typeof import.meta !== "undefined" && import.meta.hot) {
31716
+ import.meta.hot.accept();
31717
+ console.log('[HMR] Script ready:', ${escapedId});
31718
+ }
31719
+ `;
31720
+ };
31721
+
31722
+ // src/build/htmlScriptHMRPlugin.ts
31723
+ var scriptLoaders, toLoader = (ext) => {
31724
+ for (const loader of scriptLoaders) {
31725
+ if (loader === ext)
31726
+ return loader;
31731
31727
  }
31732
- }, transformStaticPagesWithIslands = async (registryPath, pagePaths) => {
31733
- if (!registryPath || pagePaths.length === 0) {
31734
- return;
31728
+ return "ts";
31729
+ }, createHTMLScriptHMRPlugin = (htmlDir, htmxDir) => ({
31730
+ name: "html-script-hmr",
31731
+ setup(build2) {
31732
+ build2.onLoad({ filter: /\.(ts|js|tsx|jsx)$/ }, async (args) => {
31733
+ const normalizedPath = args.path.replace(/\\/g, "/");
31734
+ const isHtmlScript = htmlDir && normalizedPath.includes(htmlDir.replace(/\\/g, "/")) && normalizedPath.includes("/scripts/");
31735
+ const isHtmxScript = htmxDir && normalizedPath.includes(htmxDir.replace(/\\/g, "/")) && normalizedPath.includes("/scripts/");
31736
+ if (!isHtmlScript && !isHtmxScript) {
31737
+ return;
31738
+ }
31739
+ const text2 = await Bun.file(args.path).text();
31740
+ const wrapped = wrapHTMLScriptWithHMR(text2, normalizedPath);
31741
+ const ext = args.path.split(".").pop() ?? "ts";
31742
+ const loader = toLoader(ext);
31743
+ return {
31744
+ contents: wrapped,
31745
+ loader
31746
+ };
31747
+ });
31735
31748
  }
31736
- const { registry } = await loadIslandRegistryBuildInfo(registryPath);
31737
- await Promise.all(pagePaths.map((pagePath) => transformStaticPage(pagePath, registry)));
31738
- };
31739
- var init_staticIslandPages = __esm(() => {
31740
- init_renderIslandMarkup();
31741
- init_islandEntries();
31742
- islandFrameworks2 = [
31743
- "react",
31744
- "svelte",
31745
- "vue",
31746
- "angular"
31747
- ];
31748
- islandHydrationModes2 = ["load", "idle", "visible", "none"];
31749
+ });
31750
+ var init_htmlScriptHMRPlugin = __esm(() => {
31751
+ scriptLoaders = new Set(["ts", "js", "tsx", "jsx"]);
31749
31752
  });
31750
31753
 
31751
31754
  // src/build/outputLogs.ts
@@ -208783,11 +208786,13 @@ var asset = (source, name) => {
208783
208786
  init_islands();
208784
208787
 
208785
208788
  // src/core/pageHandlers.ts
208789
+ init_staticIslandPages();
208786
208790
  init_pageHandler();
208787
208791
  var {file } = globalThis.Bun;
208788
208792
  var handleStaticPageRequest = async (pagePath) => {
208789
208793
  const html = await file(pagePath).text();
208790
- return new Response(injectIslandPageContext(html), {
208794
+ const transformedHtml = await transformCurrentStaticPageHtml(html);
208795
+ return new Response(injectIslandPageContext(transformedHtml), {
208791
208796
  headers: { "Content-Type": "text/html" }
208792
208797
  });
208793
208798
  };
@@ -208798,9 +208803,9 @@ import { existsSync as existsSync23, readdirSync as readdirSync2, readFileSync a
208798
208803
  import { basename as basename12, join as join22, relative as relative12, resolve as resolve34 } from "path";
208799
208804
 
208800
208805
  // src/utils/loadConfig.ts
208801
- import { resolve } from "path";
208806
+ import { resolve as resolve5 } from "path";
208802
208807
  var loadConfig = async (configPath) => {
208803
- const resolved = resolve(configPath ?? process.env.ABSOLUTE_CONFIG ?? "absolute.config.ts");
208808
+ const resolved = resolve5(configPath ?? process.env.ABSOLUTE_CONFIG ?? "absolute.config.ts");
208804
208809
  const mod = await import(resolved);
208805
208810
  const config = mod.default ?? mod.config;
208806
208811
  if (!config) {
@@ -208810,24 +208815,24 @@ Expected: export default defineConfig({ ... })`);
208810
208815
  return config;
208811
208816
  };
208812
208817
  // src/core/loadIslandRegistry.ts
208813
- import { resolve as resolve2 } from "path";
208814
- var isRecord2 = (value) => typeof value === "object" && value !== null;
208815
- var resolveRegistryExport = (mod) => {
208816
- if (isRecord2(mod.islandRegistry))
208818
+ import { resolve as resolve6 } from "path";
208819
+ var isRecord5 = (value) => typeof value === "object" && value !== null;
208820
+ var resolveRegistryExport2 = (mod) => {
208821
+ if (isRecord5(mod.islandRegistry))
208817
208822
  return mod.islandRegistry;
208818
- if (isRecord2(mod.default))
208823
+ if (isRecord5(mod.default))
208819
208824
  return mod.default;
208820
208825
  throw new Error("Island registry module must export `islandRegistry` or a default registry object.");
208821
208826
  };
208822
- var isRegistryModuleExport = (value) => isRecord2(value);
208823
- var isIslandRegistryInput = (value) => isRecord2(value);
208827
+ var isRegistryModuleExport = (value) => isRecord5(value);
208828
+ var isIslandRegistryInput = (value) => isRecord5(value);
208824
208829
  var loadIslandRegistry = async (registryPath) => {
208825
- const resolvedRegistryPath = resolve2(registryPath);
208830
+ const resolvedRegistryPath = resolve6(registryPath);
208826
208831
  const importedModule = await import(resolvedRegistryPath);
208827
208832
  if (!isRegistryModuleExport(importedModule)) {
208828
208833
  throw new Error("Island registry module must export an object namespace.");
208829
208834
  }
208830
- const registryExport = resolveRegistryExport(importedModule);
208835
+ const registryExport = resolveRegistryExport2(importedModule);
208831
208836
  if (!isIslandRegistryInput(registryExport)) {
208832
208837
  throw new Error("Resolved island registry export is not an object.");
208833
208838
  }
@@ -215445,5 +215450,5 @@ export {
215445
215450
  ANGULAR_INIT_TIMEOUT_MS
215446
215451
  };
215447
215452
 
215448
- //# debugId=F5D0915C24474B6D64756E2164756E21
215453
+ //# debugId=F4015D5A2C67198764756E2164756E21
215449
215454
  //# sourceMappingURL=index.js.map