@absolutejs/absolute 0.19.0-beta.844 → 0.19.0-beta.846
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/angular/components/core/streamingSlotRegistrar.js +1 -1
- package/dist/angular/components/core/streamingSlotRegistry.js +2 -2
- package/dist/angular/index.js +29 -21
- package/dist/angular/index.js.map +10 -9
- package/dist/angular/server.js +29 -21
- package/dist/angular/server.js.map +10 -9
- package/dist/build.js +984 -508
- package/dist/build.js.map +16 -13
- package/dist/cli/index.js +547 -286
- package/dist/client/index.js +16 -9
- package/dist/client/index.js.map +6 -5
- package/dist/dev/client/handlers/angular.ts +309 -19
- package/dist/dev/client/handlers/angularRuntime.ts +468 -0
- package/dist/dev/client/hmrToast.ts +150 -0
- package/dist/index.js +1031 -555
- package/dist/index.js.map +17 -14
- package/dist/islands/index.js +16 -9
- package/dist/islands/index.js.map +6 -5
- package/dist/react/index.js +16 -9
- package/dist/react/index.js.map +6 -5
- package/dist/src/build/rewriteImports.d.ts +6 -14
- package/dist/src/build/rewriteImportsPlugin.d.ts +48 -0
- package/dist/src/dev/angular/editTypeDetection.d.ts +8 -0
- package/dist/src/dev/pathUtils.d.ts +3 -0
- package/dist/src/utils/buildDirectoryLock.d.ts +26 -3
- package/dist/src/utils/loadConfig.d.ts +5 -0
- package/dist/src/utils/resolveDevPort.d.ts +21 -0
- package/dist/src/utils/runtimeMode.d.ts +3 -0
- package/dist/svelte/index.js +16 -9
- package/dist/svelte/index.js.map +6 -5
- package/dist/types/build.d.ts +15 -0
- package/dist/types/globals.d.ts +12 -0
- package/dist/vue/index.js +16 -9
- package/dist/vue/index.js.map +6 -5
- package/package.json +1 -1
|
@@ -32,22 +32,50 @@ import {
|
|
|
32
32
|
restoreScrollState
|
|
33
33
|
} from '../domState';
|
|
34
34
|
import { detectCurrentFramework, findIndexPath } from '../frameworkDetect';
|
|
35
|
+
import { showHmrToast } from '../hmrToast';
|
|
36
|
+
|
|
37
|
+
type AngularUpdateType =
|
|
38
|
+
| 'template'
|
|
39
|
+
| 'style-component'
|
|
40
|
+
| 'class-component'
|
|
41
|
+
| 'service-method-only'
|
|
42
|
+
| 'service-with-side-effects'
|
|
43
|
+
| 'route'
|
|
44
|
+
| 'reboot'
|
|
45
|
+
// Legacy types kept for back-compat with global CSS edits and pre-2.1
|
|
46
|
+
// builds. Treated as fast-path triggers (style → swap stylesheet,
|
|
47
|
+
// logic → fast-patch + reboot fallback).
|
|
48
|
+
| 'style'
|
|
49
|
+
| 'css-only'
|
|
50
|
+
| 'logic'
|
|
51
|
+
| 'full';
|
|
35
52
|
|
|
36
53
|
type HMRMessage = {
|
|
37
54
|
data: {
|
|
38
55
|
cssBaseName?: string;
|
|
39
56
|
cssUrl?: string;
|
|
57
|
+
editSourceFile?: string;
|
|
40
58
|
html?: string;
|
|
41
59
|
manifest?: Record<string, string>;
|
|
42
60
|
pageModuleUrl?: string;
|
|
61
|
+
reason?: string;
|
|
43
62
|
serverDuration?: number;
|
|
44
63
|
sourceFile?: string;
|
|
45
|
-
updateType?:
|
|
64
|
+
updateType?: AngularUpdateType;
|
|
46
65
|
};
|
|
47
66
|
};
|
|
48
67
|
|
|
49
68
|
type AngularHmrApi = {
|
|
50
69
|
applyUpdate: (id: string, newCtor: unknown) => boolean;
|
|
70
|
+
applyStyleUpdate?: (id: string, newCtor: unknown) => boolean;
|
|
71
|
+
applyTemplateUpdate?: (id: string, newCtor: unknown) => boolean;
|
|
72
|
+
applyServiceUpdate?: (id: string, newCtor: unknown) => boolean;
|
|
73
|
+
beginStyleUpdateBatch?: () => void;
|
|
74
|
+
endStyleUpdateBatch?: () => Array<{ id: string; ok: boolean }>;
|
|
75
|
+
beginTemplateUpdateBatch?: () => void;
|
|
76
|
+
endTemplateUpdateBatch?: () => Array<{ id: string; ok: boolean }>;
|
|
77
|
+
beginServiceUpdateBatch?: () => void;
|
|
78
|
+
endServiceUpdateBatch?: () => Array<{ id: string; ok: boolean }>;
|
|
51
79
|
getRegistry?: () => Map<string, unknown>;
|
|
52
80
|
refresh: () => void;
|
|
53
81
|
hasPageExportsChanged?: (sourceId: string) => boolean;
|
|
@@ -320,45 +348,307 @@ const handleFastUpdate = async (message: HMRMessage) => {
|
|
|
320
348
|
let activeMessage: Promise<void> | null = null;
|
|
321
349
|
let pendingMessage: HMRMessage | null = null;
|
|
322
350
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
351
|
+
/* Stub handlers for fast paths that aren't implemented yet. Each one
|
|
352
|
+
* returns false to signal "couldn't handle, fall through to reboot",
|
|
353
|
+
* letting Phase 2 ship piecewise — we land §2.1 (this dispatch table)
|
|
354
|
+
* first, then §2.4 / §2.3 / §2.2 fill in the stubs one at a time
|
|
355
|
+
* without further plumbing changes. */
|
|
356
|
+
/* Template HMR — re-imports the rebuilt page chunk under FAST_PATCH +
|
|
357
|
+
* TEMPLATE_UPDATE_MODE, then asks the runtime to swap the template
|
|
358
|
+
* subgraph of `ɵcmp` (template factory, slot counts, queries,
|
|
359
|
+
* dependencies, host bindings) on every re-registered component.
|
|
360
|
+
*
|
|
361
|
+
* Live instances keep their state, services, queries, and DI tokens —
|
|
362
|
+
* only the rendered output changes. After all components are patched,
|
|
363
|
+
* a single `refresh()` call walks the subtree, marks each patched
|
|
364
|
+
* component dirty, and ticks the app to repaint.
|
|
365
|
+
*
|
|
366
|
+
* Returns false on any partial failure so the orchestrator falls
|
|
367
|
+
* through to a coherent reboot. */
|
|
368
|
+
type TemplateUpdateWindow = FastPatchWindow & {
|
|
369
|
+
__ANGULAR_HMR_TEMPLATE_UPDATE_MODE__?: boolean;
|
|
370
|
+
};
|
|
329
371
|
|
|
330
|
-
|
|
372
|
+
const handleTemplateUpdate = async (message: HMRMessage): Promise<boolean> => {
|
|
373
|
+
const hmr = window.__ANGULAR_HMR__;
|
|
374
|
+
if (
|
|
375
|
+
!hmr ||
|
|
376
|
+
!hmr.applyTemplateUpdate ||
|
|
377
|
+
!hmr.beginTemplateUpdateBatch ||
|
|
378
|
+
!hmr.endTemplateUpdateBatch
|
|
379
|
+
) {
|
|
380
|
+
return false;
|
|
331
381
|
}
|
|
332
382
|
|
|
333
|
-
|
|
383
|
+
const indexPath = findIndexPath(
|
|
384
|
+
message.data.manifest,
|
|
385
|
+
message.data.sourceFile,
|
|
386
|
+
'angular'
|
|
387
|
+
);
|
|
388
|
+
if (!indexPath) return false;
|
|
389
|
+
|
|
390
|
+
const w = window as TemplateUpdateWindow;
|
|
391
|
+
w.__ANGULAR_HMR_FAST_PATCH__ = true;
|
|
392
|
+
w.__ANGULAR_HMR_TEMPLATE_UPDATE_MODE__ = true;
|
|
393
|
+
hmr.beginTemplateUpdateBatch();
|
|
394
|
+
|
|
395
|
+
const origWarn = suppressNg0912();
|
|
334
396
|
try {
|
|
335
|
-
|
|
336
|
-
|
|
397
|
+
await import(`${indexPath}?t=${Date.now()}`);
|
|
398
|
+
|
|
399
|
+
// Page-level routes/providers cannot ride a template update.
|
|
400
|
+
if (hmr.hasPageExportsChanged?.(message.data.sourceFile || '')) {
|
|
401
|
+
return false;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
const results = hmr.endTemplateUpdateBatch();
|
|
405
|
+
if (results.length === 0) return false;
|
|
406
|
+
if (!results.every((r) => r.ok)) return false;
|
|
407
|
+
|
|
408
|
+
console.warn = origWarn;
|
|
409
|
+
hmr.refresh();
|
|
410
|
+
|
|
411
|
+
return true;
|
|
337
412
|
} catch (err) {
|
|
413
|
+
console.warn = origWarn;
|
|
338
414
|
console.warn(
|
|
339
|
-
'[HMR] Angular
|
|
415
|
+
'[HMR] Angular template update failed, falling back:',
|
|
340
416
|
err
|
|
341
417
|
);
|
|
418
|
+
return false;
|
|
419
|
+
} finally {
|
|
420
|
+
delete w.__ANGULAR_HMR_FAST_PATCH__;
|
|
421
|
+
delete w.__ANGULAR_HMR_TEMPLATE_UPDATE_MODE__;
|
|
422
|
+
console.warn = origWarn;
|
|
423
|
+
}
|
|
424
|
+
};
|
|
425
|
+
|
|
426
|
+
/* Component-style HMR — re-imports the rebuilt page chunk under the
|
|
427
|
+
* combined `FAST_PATCH` and `STYLE_UPDATE_MODE` flags so:
|
|
428
|
+
* - the chunk's bootstrap section is skipped (FAST_PATCH)
|
|
429
|
+
* - every per-file auto-registration block routes its new ctor
|
|
430
|
+
* through `applyStyleUpdate` instead of a no-op (STYLE_UPDATE_MODE)
|
|
431
|
+
*
|
|
432
|
+
* The registration-block path is the only way to reach CHILD
|
|
433
|
+
* components — the page chunk's `export *` only re-exports the page's
|
|
434
|
+
* own module, so a top-level export walk would miss imported
|
|
435
|
+
* components like `LayoutComponent`. Each compiled .ts file emits a
|
|
436
|
+
* registration block for its own component classes, so the chunk
|
|
437
|
+
* covers the whole tree on re-evaluation.
|
|
438
|
+
*
|
|
439
|
+
* Returns true iff every component the chunk re-registered swapped
|
|
440
|
+
* its styles cleanly. Any failure (Shadow DOM, length change, missing
|
|
441
|
+
* live <style> tag) → reboot. The transactional check inside
|
|
442
|
+
* `applyStyleUpdate` means we never apply a partial update — either
|
|
443
|
+
* the page restyles coherently or we reboot. */
|
|
444
|
+
type StyleUpdateWindow = FastPatchWindow & {
|
|
445
|
+
__ANGULAR_HMR_STYLE_UPDATE_MODE__?: boolean;
|
|
446
|
+
};
|
|
447
|
+
|
|
448
|
+
const handleComponentStyleUpdate = async (
|
|
449
|
+
message: HMRMessage
|
|
450
|
+
): Promise<boolean> => {
|
|
451
|
+
const hmr = window.__ANGULAR_HMR__;
|
|
452
|
+
if (
|
|
453
|
+
!hmr ||
|
|
454
|
+
!hmr.applyStyleUpdate ||
|
|
455
|
+
!hmr.beginStyleUpdateBatch ||
|
|
456
|
+
!hmr.endStyleUpdateBatch
|
|
457
|
+
) {
|
|
458
|
+
return false;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
const indexPath = findIndexPath(
|
|
462
|
+
message.data.manifest,
|
|
463
|
+
message.data.sourceFile,
|
|
464
|
+
'angular'
|
|
465
|
+
);
|
|
466
|
+
if (!indexPath) return false;
|
|
467
|
+
|
|
468
|
+
const w = window as StyleUpdateWindow;
|
|
469
|
+
w.__ANGULAR_HMR_FAST_PATCH__ = true;
|
|
470
|
+
w.__ANGULAR_HMR_STYLE_UPDATE_MODE__ = true;
|
|
471
|
+
hmr.beginStyleUpdateBatch();
|
|
472
|
+
|
|
473
|
+
try {
|
|
474
|
+
await import(`${indexPath}?t=${Date.now()}`);
|
|
475
|
+
|
|
476
|
+
// Page-level routes/providers cannot ride a style update — they
|
|
477
|
+
// are read once during bootstrap. If they changed in this
|
|
478
|
+
// rebuild, reboot rather than risk a stale router/injector.
|
|
479
|
+
if (hmr.hasPageExportsChanged?.(message.data.sourceFile || '')) {
|
|
480
|
+
return false;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
const results = hmr.endStyleUpdateBatch();
|
|
484
|
+
if (results.length === 0) {
|
|
485
|
+
// Chunk re-evaluated but no component re-registered — likely
|
|
486
|
+
// the edited file isn't actually used by this page, or the
|
|
487
|
+
// chunk skipped its registration block. Fall back to reboot.
|
|
488
|
+
return false;
|
|
489
|
+
}
|
|
490
|
+
return results.every((r) => r.ok);
|
|
491
|
+
} catch (err) {
|
|
492
|
+
console.warn('[HMR] Angular style update failed, falling back:', err);
|
|
493
|
+
return false;
|
|
494
|
+
} finally {
|
|
495
|
+
delete w.__ANGULAR_HMR_FAST_PATCH__;
|
|
496
|
+
delete w.__ANGULAR_HMR_STYLE_UPDATE_MODE__;
|
|
497
|
+
}
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
/* Service HMR — re-imports the rebuilt page chunk under FAST_PATCH +
|
|
501
|
+
* SERVICE_UPDATE_MODE so the page's auto-registration block routes
|
|
502
|
+
* each new service ctor through `applyServiceUpdate`. The runtime
|
|
503
|
+
* does prototype method-swap (always) and best-effort field merge on
|
|
504
|
+
* the live singleton (when reachable via the root injector and
|
|
505
|
+
* donor-instantiable). Live components keep their references — they
|
|
506
|
+
* just call into the new method bodies on next invocation.
|
|
507
|
+
*
|
|
508
|
+
* This path only runs when the server-side classifier returned
|
|
509
|
+
* `service-method-only` — services with side-effecting constructors
|
|
510
|
+
* never get here, so the live singleton's existing subscriptions /
|
|
511
|
+
* timers / listeners stay intact and we don't double-register them. */
|
|
512
|
+
type ServiceUpdateWindow = FastPatchWindow & {
|
|
513
|
+
__ANGULAR_HMR_SERVICE_UPDATE_MODE__?: boolean;
|
|
514
|
+
};
|
|
515
|
+
|
|
516
|
+
const handleServiceMethodSwap = async (
|
|
517
|
+
message: HMRMessage
|
|
518
|
+
): Promise<boolean> => {
|
|
519
|
+
const hmr = window.__ANGULAR_HMR__;
|
|
520
|
+
if (
|
|
521
|
+
!hmr ||
|
|
522
|
+
!hmr.applyServiceUpdate ||
|
|
523
|
+
!hmr.beginServiceUpdateBatch ||
|
|
524
|
+
!hmr.endServiceUpdateBatch
|
|
525
|
+
) {
|
|
526
|
+
return false;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
const indexPath = findIndexPath(
|
|
530
|
+
message.data.manifest,
|
|
531
|
+
message.data.sourceFile,
|
|
532
|
+
'angular'
|
|
533
|
+
);
|
|
534
|
+
if (!indexPath) return false;
|
|
535
|
+
|
|
536
|
+
const w = window as ServiceUpdateWindow;
|
|
537
|
+
w.__ANGULAR_HMR_FAST_PATCH__ = true;
|
|
538
|
+
w.__ANGULAR_HMR_SERVICE_UPDATE_MODE__ = true;
|
|
539
|
+
hmr.beginServiceUpdateBatch();
|
|
540
|
+
|
|
541
|
+
try {
|
|
542
|
+
await import(`${indexPath}?t=${Date.now()}`);
|
|
543
|
+
|
|
544
|
+
// Page-level routes/providers cannot ride a service update.
|
|
545
|
+
if (hmr.hasPageExportsChanged?.(message.data.sourceFile || '')) {
|
|
546
|
+
return false;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
const results = hmr.endServiceUpdateBatch();
|
|
550
|
+
if (results.length === 0) return false;
|
|
551
|
+
if (!results.every((r) => r.ok)) return false;
|
|
552
|
+
|
|
553
|
+
// New method bodies might compute new values for observable-fed
|
|
554
|
+
// fields, so the existing component subtree should re-render.
|
|
555
|
+
// `refresh()` ticks the app + marks any pending fast-patch
|
|
556
|
+
// components dirty.
|
|
557
|
+
hmr.refresh();
|
|
558
|
+
|
|
559
|
+
return true;
|
|
560
|
+
} catch (err) {
|
|
561
|
+
console.warn('[HMR] Angular service update failed, falling back:', err);
|
|
562
|
+
return false;
|
|
563
|
+
} finally {
|
|
564
|
+
delete w.__ANGULAR_HMR_FAST_PATCH__;
|
|
565
|
+
delete w.__ANGULAR_HMR_SERVICE_UPDATE_MODE__;
|
|
566
|
+
}
|
|
567
|
+
};
|
|
568
|
+
|
|
569
|
+
const logRebootReason = (message: HMRMessage) => {
|
|
570
|
+
const reason = message.data.reason;
|
|
571
|
+
const updateType = message.data.updateType;
|
|
572
|
+
if (!reason && !updateType) return;
|
|
573
|
+
console.info(
|
|
574
|
+
`[HMR] Angular reboot — ${updateType ?? 'unknown'}: ${reason ?? '(no reason given)'}`
|
|
575
|
+
);
|
|
576
|
+
// Surface the same info as a brief on-page toast so the developer
|
|
577
|
+
// sees it without opening devtools. Toasts auto-dismiss after a
|
|
578
|
+
// few seconds.
|
|
579
|
+
showHmrToast({
|
|
580
|
+
editSourceFile: message.data.editSourceFile,
|
|
581
|
+
reason,
|
|
582
|
+
updateType
|
|
583
|
+
});
|
|
584
|
+
};
|
|
585
|
+
|
|
586
|
+
const processMessage = async (message: HMRMessage) => {
|
|
587
|
+
const updateType = message.data.updateType ?? 'logic';
|
|
588
|
+
|
|
589
|
+
switch (updateType) {
|
|
590
|
+
case 'template': {
|
|
591
|
+
const ok = await handleTemplateUpdate(message);
|
|
592
|
+
if (ok) return;
|
|
593
|
+
break;
|
|
594
|
+
}
|
|
595
|
+
case 'style-component': {
|
|
596
|
+
const ok = await handleComponentStyleUpdate(message);
|
|
597
|
+
if (ok) return;
|
|
598
|
+
break;
|
|
599
|
+
}
|
|
600
|
+
case 'service-method-only': {
|
|
601
|
+
const ok = await handleServiceMethodSwap(message);
|
|
602
|
+
if (ok) return;
|
|
603
|
+
break;
|
|
604
|
+
}
|
|
605
|
+
case 'class-component':
|
|
606
|
+
case 'logic': {
|
|
607
|
+
// Existing prototype-swap fast-patch. Falls through to reboot
|
|
608
|
+
// on any failure (provider change, page-export change, etc.).
|
|
609
|
+
try {
|
|
610
|
+
const patched = await handleFastUpdate(message);
|
|
611
|
+
if (patched) return;
|
|
612
|
+
} catch (err) {
|
|
613
|
+
console.warn(
|
|
614
|
+
'[HMR] Angular fast update threw, falling back to reboot:',
|
|
615
|
+
err
|
|
616
|
+
);
|
|
617
|
+
}
|
|
618
|
+
break;
|
|
619
|
+
}
|
|
620
|
+
case 'service-with-side-effects':
|
|
621
|
+
case 'route':
|
|
622
|
+
case 'reboot':
|
|
623
|
+
case 'full':
|
|
624
|
+
// Explicit reboot signals — skip the fast path entirely and
|
|
625
|
+
// log why so the developer can see the classification.
|
|
626
|
+
break;
|
|
627
|
+
default:
|
|
628
|
+
// Unknown update type — be conservative and reboot.
|
|
629
|
+
break;
|
|
342
630
|
}
|
|
343
631
|
|
|
344
|
-
//
|
|
345
|
-
//
|
|
346
|
-
// that didn't opt in is reset to its class-field defaults. The
|
|
347
|
-
// log emitted by `endHmrReboot`
|
|
348
|
-
|
|
632
|
+
// Falling through: full re-bootstrap. Components and services that
|
|
633
|
+
// opted into `preserveAcrossHmr(this)` keep their state; anything
|
|
634
|
+
// that didn't opt in is reset to its class-field defaults. The
|
|
635
|
+
// summary log emitted by `endHmrReboot` lists what was preserved.
|
|
636
|
+
logRebootReason(message);
|
|
349
637
|
await handleFullUpdate(message);
|
|
350
638
|
};
|
|
351
639
|
|
|
352
640
|
export const handleAngularUpdate = (message: HMRMessage) => {
|
|
353
641
|
if (detectCurrentFramework() !== 'angular') return;
|
|
354
642
|
|
|
355
|
-
const updateType = message.data.updateType
|
|
643
|
+
const updateType = message.data.updateType ?? 'logic';
|
|
356
644
|
|
|
357
645
|
if (
|
|
358
646
|
(updateType === 'style' || updateType === 'css-only') &&
|
|
359
647
|
message.data.cssUrl
|
|
360
648
|
) {
|
|
361
|
-
// CSS-only updates
|
|
649
|
+
// Global CSS-only updates: swap the stylesheet in place. These
|
|
650
|
+
// run outside the activeMessage queue because they can't conflict
|
|
651
|
+
// with an in-flight component update.
|
|
362
652
|
swapStylesheet(
|
|
363
653
|
message.data.cssUrl,
|
|
364
654
|
message.data.cssBaseName || '',
|