@ng-prism/core 21.11.0 → 21.12.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/dist/app/header/prism-header-widget-host.component.d.ts +11 -0
  2. package/dist/app/header/prism-header-widget-host.component.d.ts.map +1 -0
  3. package/dist/app/header/prism-header-widget-host.component.js +57 -0
  4. package/dist/app/header/prism-header.component.d.ts +3 -0
  5. package/dist/app/header/prism-header.component.d.ts.map +1 -1
  6. package/dist/app/header/prism-header.component.js +76 -20
  7. package/dist/app/panels/a11y/a11y-header-badge.component.d.ts +16 -0
  8. package/dist/app/panels/a11y/a11y-header-badge.component.d.ts.map +1 -0
  9. package/dist/app/panels/a11y/a11y-header-badge.component.js +84 -0
  10. package/dist/app/panels/a11y/a11y-thresholds.d.ts +6 -0
  11. package/dist/app/panels/a11y/a11y-thresholds.d.ts.map +1 -0
  12. package/dist/app/panels/a11y/a11y-thresholds.js +13 -0
  13. package/dist/app/panels/a11y/a11y.types.d.ts +33 -0
  14. package/dist/app/panels/a11y/a11y.types.d.ts.map +1 -1
  15. package/dist/app/panels/builtin-header-widgets.d.ts +3 -0
  16. package/dist/app/panels/builtin-header-widgets.d.ts.map +1 -0
  17. package/dist/app/panels/builtin-header-widgets.js +9 -0
  18. package/dist/app/panels/panel-host/prism-panel-host.component.d.ts +16 -5
  19. package/dist/app/panels/panel-host/prism-panel-host.component.d.ts.map +1 -1
  20. package/dist/app/panels/panel-host/prism-panel-host.component.js +91 -44
  21. package/dist/app/provide-prism.d.ts.map +1 -1
  22. package/dist/app/provide-prism.js +11 -0
  23. package/dist/app/renderer/prism-renderer.component.d.ts +2 -0
  24. package/dist/app/renderer/prism-renderer.component.d.ts.map +1 -1
  25. package/dist/app/renderer/prism-renderer.component.js +18 -3
  26. package/dist/app/services/prism-panel.service.d.ts +5 -0
  27. package/dist/app/services/prism-panel.service.d.ts.map +1 -1
  28. package/dist/app/services/prism-panel.service.js +7 -3
  29. package/dist/app/services/prism-plugin.service.d.ts +4 -1
  30. package/dist/app/services/prism-plugin.service.d.ts.map +1 -1
  31. package/dist/app/services/prism-plugin.service.js +11 -0
  32. package/dist/app/variant-ribbon/prism-variant-ribbon.component.d.ts +4 -1
  33. package/dist/app/variant-ribbon/prism-variant-ribbon.component.d.ts.map +1 -1
  34. package/dist/app/variant-ribbon/prism-variant-ribbon.component.js +67 -36
  35. package/dist/builder/a11y/a11y-report-reader.d.ts +12 -0
  36. package/dist/builder/a11y/a11y-report-reader.d.ts.map +1 -0
  37. package/dist/builder/a11y/a11y-report-reader.js +49 -0
  38. package/dist/builder/manifest/runtime-manifest.generator.d.ts +1 -0
  39. package/dist/builder/manifest/runtime-manifest.generator.d.ts.map +1 -1
  40. package/dist/builder/manifest/runtime-manifest.generator.js +8 -1
  41. package/dist/builder/plugin-runner/plugin-runner.d.ts.map +1 -1
  42. package/dist/builder/plugin-runner/plugin-runner.js +3 -1
  43. package/dist/builder/shared/prism-pipeline.d.ts.map +1 -1
  44. package/dist/builder/shared/prism-pipeline.js +19 -1
  45. package/dist/cli/audit-a11y/aggregator.d.ts +10 -0
  46. package/dist/cli/audit-a11y/aggregator.d.ts.map +1 -0
  47. package/dist/cli/audit-a11y/aggregator.js +64 -0
  48. package/dist/cli/audit-a11y/auditor.d.ts +14 -0
  49. package/dist/cli/audit-a11y/auditor.d.ts.map +1 -0
  50. package/dist/cli/audit-a11y/auditor.js +104 -0
  51. package/dist/cli/audit-a11y/cli.d.ts +3 -0
  52. package/dist/cli/audit-a11y/cli.d.ts.map +1 -0
  53. package/dist/cli/audit-a11y/cli.js +143 -0
  54. package/dist/cli/audit-a11y/index.d.ts +4 -0
  55. package/dist/cli/audit-a11y/index.d.ts.map +1 -0
  56. package/dist/cli/audit-a11y/index.js +3 -0
  57. package/dist/cli/audit-a11y/static-server.d.ts +11 -0
  58. package/dist/cli/audit-a11y/static-server.d.ts.map +1 -0
  59. package/dist/cli/audit-a11y/static-server.js +61 -0
  60. package/dist/decorator/index.d.ts +1 -1
  61. package/dist/decorator/index.d.ts.map +1 -1
  62. package/dist/decorator/showcase.decorator.d.ts +1 -1
  63. package/dist/decorator/showcase.decorator.d.ts.map +1 -1
  64. package/dist/decorator/showcase.types.d.ts +20 -6
  65. package/dist/decorator/showcase.types.d.ts.map +1 -1
  66. package/dist/plugin/index.d.ts +2 -2
  67. package/dist/plugin/index.d.ts.map +1 -1
  68. package/dist/plugin/index.js +1 -1
  69. package/dist/plugin/plugin.types.d.ts +33 -0
  70. package/dist/plugin/plugin.types.d.ts.map +1 -1
  71. package/package.json +12 -3
@@ -1,40 +1,43 @@
1
- import { Component, inject, computed, ChangeDetectionStrategy, } from '@angular/core';
1
+ import { Component, inject, computed, effect, viewChild, viewChildren, ChangeDetectionStrategy, } from '@angular/core';
2
2
  import { PrismIconComponent } from '../icons/prism-icon.component.js';
3
3
  import { PrismNavigationService } from '../services/prism-navigation.service.js';
4
4
  import { PrismRendererService } from '../services/prism-renderer.service.js';
5
5
  import * as i0 from "@angular/core";
6
+ const _c0 = ["tabsContainer"];
7
+ const _c1 = ["tabButton"];
6
8
  const _forTrack0 = ($index, $item) => $item.name;
7
- function PrismVariantRibbonComponent_Conditional_0_For_2_Template(rf, ctx) { if (rf & 1) {
9
+ function PrismVariantRibbonComponent_Conditional_0_For_4_Template(rf, ctx) { if (rf & 1) {
8
10
  const _r2 = i0.ɵɵgetCurrentView();
9
- i0.ɵɵelementStart(0, "button", 7);
10
- i0.ɵɵlistener("click", function PrismVariantRibbonComponent_Conditional_0_For_2_Template_button_click_0_listener() { const ɵ$index_4_r3 = i0.ɵɵrestoreView(_r2).$index; const ctx_r3 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r3.rendererService.selectVariant(ɵ$index_4_r3)); });
11
- i0.ɵɵelement(1, "span", 8);
12
- i0.ɵɵtext(2);
11
+ i0.ɵɵelementStart(0, "button", 10, 1);
12
+ i0.ɵɵlistener("click", function PrismVariantRibbonComponent_Conditional_0_For_4_Template_button_click_0_listener() { const ɵ$index_6_r3 = i0.ɵɵrestoreView(_r2).$index; const ctx_r3 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r3.rendererService.selectVariant(ɵ$index_6_r3)); });
13
+ i0.ɵɵelement(2, "span", 11);
14
+ i0.ɵɵtext(3);
13
15
  i0.ɵɵelementEnd();
14
16
  } if (rf & 2) {
15
17
  const v_r5 = ctx.$implicit;
16
- const ɵ$index_4_r3 = ctx.$index;
18
+ const ɵ$index_6_r3 = ctx.$index;
17
19
  const ctx_r3 = i0.ɵɵnextContext(2);
18
- i0.ɵɵstyleProp("--vc", ctx_r3.variantColor(v_r5.name, ɵ$index_4_r3));
19
- i0.ɵɵclassProp("v-tab--active", ctx_r3.rendererService.activeVariantIndex() === ɵ$index_4_r3);
20
- i0.ɵɵadvance(2);
20
+ i0.ɵɵstyleProp("--vc", ctx_r3.variantColor(v_r5.name, ɵ$index_6_r3));
21
+ i0.ɵɵclassProp("v-tab--active", ctx_r3.rendererService.activeVariantIndex() === ɵ$index_6_r3);
22
+ i0.ɵɵadvance(3);
21
23
  i0.ɵɵtextInterpolate1(" ", v_r5.name, " ");
22
24
  } }
23
25
  function PrismVariantRibbonComponent_Conditional_0_Template(rf, ctx) { if (rf & 1) {
24
26
  const _r1 = i0.ɵɵgetCurrentView();
25
- i0.ɵɵelementStart(0, "nav", 0);
26
- i0.ɵɵrepeaterCreate(1, PrismVariantRibbonComponent_Conditional_0_For_2_Template, 3, 5, "button", 1, _forTrack0);
27
- i0.ɵɵelementStart(3, "div", 2)(4, "button", 3);
28
- i0.ɵɵlistener("click", function PrismVariantRibbonComponent_Conditional_0_Template_button_click_4_listener() { i0.ɵɵrestoreView(_r1); const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.prev()); });
29
- i0.ɵɵelement(5, "prism-icon", 4);
27
+ i0.ɵɵelementStart(0, "nav", 2)(1, "div", 3, 0);
28
+ i0.ɵɵrepeaterCreate(3, PrismVariantRibbonComponent_Conditional_0_For_4_Template, 4, 5, "button", 4, _forTrack0);
29
+ i0.ɵɵelementEnd();
30
+ i0.ɵɵelementStart(5, "div", 5)(6, "button", 6);
31
+ i0.ɵɵlistener("click", function PrismVariantRibbonComponent_Conditional_0_Template_button_click_6_listener() { i0.ɵɵrestoreView(_r1); const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.prev()); });
32
+ i0.ɵɵelement(7, "prism-icon", 7);
30
33
  i0.ɵɵelementEnd();
31
- i0.ɵɵelementStart(6, "button", 5);
32
- i0.ɵɵlistener("click", function PrismVariantRibbonComponent_Conditional_0_Template_button_click_6_listener() { i0.ɵɵrestoreView(_r1); const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.next()); });
33
- i0.ɵɵelement(7, "prism-icon", 6);
34
+ i0.ɵɵelementStart(8, "button", 8);
35
+ i0.ɵɵlistener("click", function PrismVariantRibbonComponent_Conditional_0_Template_button_click_8_listener() { i0.ɵɵrestoreView(_r1); const ctx_r3 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r3.next()); });
36
+ i0.ɵɵelement(9, "prism-icon", 9);
34
37
  i0.ɵɵelementEnd()()();
35
38
  } if (rf & 2) {
36
39
  const ctx_r3 = i0.ɵɵnextContext();
37
- i0.ɵɵadvance();
40
+ i0.ɵɵadvance(3);
38
41
  i0.ɵɵrepeater(ctx_r3.variants());
39
42
  i0.ɵɵadvance(3);
40
43
  i0.ɵɵproperty("disabled", ctx_r3.rendererService.activeVariantIndex() === 0);
@@ -61,10 +64,31 @@ function variantColor(name, index) {
61
64
  export class PrismVariantRibbonComponent {
62
65
  navigationService = inject(PrismNavigationService);
63
66
  rendererService = inject(PrismRendererService);
67
+ tabsContainer = viewChild('tabsContainer', ...(ngDevMode ? [{ debugName: "tabsContainer" }] : []));
68
+ tabButtons = viewChildren('tabButton', ...(ngDevMode ? [{ debugName: "tabButtons" }] : []));
64
69
  variants = computed(() => {
65
70
  const comp = this.navigationService.activeComponent();
66
71
  return comp?.meta.showcaseConfig.variants ?? [];
67
72
  }, ...(ngDevMode ? [{ debugName: "variants" }] : []));
73
+ constructor() {
74
+ effect(() => {
75
+ const idx = this.rendererService.activeVariantIndex();
76
+ const container = this.tabsContainer()?.nativeElement;
77
+ const button = this.tabButtons()[idx]?.nativeElement;
78
+ if (!container || !button)
79
+ return;
80
+ const buttonLeft = button.offsetLeft;
81
+ const buttonRight = buttonLeft + button.offsetWidth;
82
+ const viewLeft = container.scrollLeft;
83
+ const viewRight = viewLeft + container.clientWidth;
84
+ if (buttonLeft < viewLeft) {
85
+ container.scrollLeft = buttonLeft;
86
+ }
87
+ else if (buttonRight > viewRight) {
88
+ container.scrollLeft = buttonRight - container.clientWidth;
89
+ }
90
+ });
91
+ }
68
92
  variantColor(name, index) {
69
93
  return variantColor(name, index);
70
94
  }
@@ -79,28 +103,35 @@ export class PrismVariantRibbonComponent {
79
103
  this.rendererService.selectVariant(idx + 1);
80
104
  }
81
105
  static ɵfac = function PrismVariantRibbonComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || PrismVariantRibbonComponent)(); };
82
- static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: PrismVariantRibbonComponent, selectors: [["prism-variant-ribbon"]], decls: 1, vars: 1, consts: [[1, "variant-ribbon"], [1, "v-tab", 3, "v-tab--active", "--vc"], [1, "variant-ribbon-right"], ["title", "Previous variant", "aria-label", "Previous variant", 1, "tool-btn", 3, "click", "disabled"], ["name", "chevron-right", 2, "transform", "rotate(180deg)", 3, "size"], ["title", "Next variant", "aria-label", "Next variant", 1, "tool-btn", 3, "click", "disabled"], ["name", "chevron-right", 3, "size"], [1, "v-tab", 3, "click"], [1, "v-dot"]], template: function PrismVariantRibbonComponent_Template(rf, ctx) { if (rf & 1) {
83
- i0.ɵɵconditionalCreate(0, PrismVariantRibbonComponent_Conditional_0_Template, 8, 4, "nav", 0);
106
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: PrismVariantRibbonComponent, selectors: [["prism-variant-ribbon"]], viewQuery: function PrismVariantRibbonComponent_Query(rf, ctx) { if (rf & 1) {
107
+ i0.ɵɵviewQuerySignal(ctx.tabsContainer, _c0, 5)(ctx.tabButtons, _c1, 5);
108
+ } if (rf & 2) {
109
+ i0.ɵɵqueryAdvance(2);
110
+ } }, decls: 1, vars: 1, consts: [["tabsContainer", ""], ["tabButton", ""], [1, "variant-ribbon"], [1, "variant-ribbon-tabs"], [1, "v-tab", 3, "v-tab--active", "--vc"], [1, "variant-ribbon-right"], ["title", "Previous variant", "aria-label", "Previous variant", 1, "tool-btn", 3, "click", "disabled"], ["name", "chevron-right", 2, "transform", "rotate(180deg)", 3, "size"], ["title", "Next variant", "aria-label", "Next variant", 1, "tool-btn", 3, "click", "disabled"], ["name", "chevron-right", 3, "size"], [1, "v-tab", 3, "click"], [1, "v-dot"]], template: function PrismVariantRibbonComponent_Template(rf, ctx) { if (rf & 1) {
111
+ i0.ɵɵconditionalCreate(0, PrismVariantRibbonComponent_Conditional_0_Template, 10, 4, "nav", 2);
84
112
  } if (rf & 2) {
85
113
  i0.ɵɵconditional(ctx.variants().length > 0 ? 0 : -1);
86
- } }, dependencies: [PrismIconComponent], styles: ["[_nghost-%COMP%] { display: block; flex-shrink: 0; }\n\n .variant-ribbon[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n padding: 0 28px;\n background: var(--prism-bg);\n border-bottom: 1px solid var(--prism-border);\n height: 42px;\n gap: 2px;\n overflow-x: auto;\n scrollbar-width: none;\n }\n .variant-ribbon[_ngcontent-%COMP%]::-webkit-scrollbar { display: none; }\n\n .v-tab[_ngcontent-%COMP%] {\n position: relative;\n height: 100%;\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 0 14px;\n font-size: 13px;\n font-weight: 500;\n color: var(--prism-text-muted);\n white-space: nowrap;\n transition: color var(--dur-fast);\n background: none;\n border: none;\n cursor: pointer;\n font-family: var(--font-sans);\n }\n .v-tab[_ngcontent-%COMP%]::after {\n content: '';\n position: absolute;\n left: 10px;\n right: 10px;\n bottom: 0;\n height: 2px;\n background: linear-gradient(90deg, var(--prism-primary-from), var(--prism-primary-to));\n opacity: 0;\n transition: opacity var(--dur-fast);\n border-radius: 1px 1px 0 0;\n }\n .v-tab[_ngcontent-%COMP%]:hover { color: var(--prism-text-2); }\n .v-tab--active[_ngcontent-%COMP%] { color: var(--prism-text); }\n .v-tab--active[_ngcontent-%COMP%]::after { opacity: 1; }\n\n .v-dot[_ngcontent-%COMP%] {\n width: 6px;\n height: 6px;\n border-radius: 2px;\n background: var(--vc, var(--prism-primary));\n opacity: 0.6;\n }\n .v-tab--active[_ngcontent-%COMP%] .v-dot[_ngcontent-%COMP%] {\n opacity: 1;\n box-shadow: 0 0 6px var(--vc);\n }\n\n .variant-ribbon-right[_ngcontent-%COMP%] {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 8px;\n padding-left: 12px;\n border-left: 1px solid var(--prism-border);\n height: 24px;\n }\n\n .tool-btn[_ngcontent-%COMP%] {\n height: 24px;\n width: 24px;\n display: grid;\n place-items: center;\n border-radius: var(--radius-sm);\n color: var(--prism-text-muted);\n background: none;\n border: none;\n cursor: pointer;\n transition: all var(--dur-fast);\n }\n .tool-btn[_ngcontent-%COMP%]:hover:not(:disabled) {\n color: var(--prism-text);\n background: color-mix(in srgb, var(--prism-primary) 8%, transparent);\n }\n .tool-btn[_ngcontent-%COMP%]:disabled {\n opacity: 0.3;\n cursor: default;\n }\n\n [_ngcontent-%COMP%]:focus-visible {\n outline: 2px solid var(--prism-primary);\n outline-offset: 2px;\n }"], changeDetection: 0 });
114
+ } }, dependencies: [PrismIconComponent], styles: ["[_nghost-%COMP%] { display: block; flex-shrink: 0; }\n\n .variant-ribbon[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n padding: 0 28px;\n background: var(--prism-bg);\n border-bottom: 1px solid var(--prism-border);\n height: 42px;\n }\n\n .variant-ribbon-tabs[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n display: flex;\n align-items: center;\n gap: 2px;\n height: 100%;\n overflow-x: auto;\n scrollbar-width: none;\n }\n .variant-ribbon-tabs[_ngcontent-%COMP%]::-webkit-scrollbar { display: none; }\n\n .v-tab[_ngcontent-%COMP%] {\n position: relative;\n height: 100%;\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 0 14px;\n font-size: 13px;\n font-weight: 500;\n color: var(--prism-text-muted);\n white-space: nowrap;\n transition: color var(--dur-fast);\n background: none;\n border: none;\n cursor: pointer;\n font-family: var(--font-sans);\n }\n .v-tab[_ngcontent-%COMP%]::after {\n content: '';\n position: absolute;\n left: 10px;\n right: 10px;\n bottom: 0;\n height: 2px;\n background: linear-gradient(90deg, var(--prism-primary-from), var(--prism-primary-to));\n opacity: 0;\n transition: opacity var(--dur-fast);\n border-radius: 1px 1px 0 0;\n }\n .v-tab[_ngcontent-%COMP%]:hover { color: var(--prism-text-2); }\n .v-tab--active[_ngcontent-%COMP%] { color: var(--prism-text); }\n .v-tab--active[_ngcontent-%COMP%]::after { opacity: 1; }\n\n .v-dot[_ngcontent-%COMP%] {\n width: 6px;\n height: 6px;\n border-radius: 2px;\n background: var(--vc, var(--prism-primary));\n opacity: 0.6;\n }\n .v-tab--active[_ngcontent-%COMP%] .v-dot[_ngcontent-%COMP%] {\n opacity: 1;\n box-shadow: 0 0 6px var(--vc);\n }\n\n .variant-ribbon-right[_ngcontent-%COMP%] {\n flex-shrink: 0;\n display: flex;\n align-items: center;\n gap: 8px;\n padding-left: 12px;\n margin-left: 12px;\n border-left: 1px solid var(--prism-border);\n height: 24px;\n }\n\n .tool-btn[_ngcontent-%COMP%] {\n height: 24px;\n width: 24px;\n display: grid;\n place-items: center;\n border-radius: var(--radius-sm);\n color: var(--prism-text-muted);\n background: none;\n border: none;\n cursor: pointer;\n transition: all var(--dur-fast);\n }\n .tool-btn[_ngcontent-%COMP%]:hover:not(:disabled) {\n color: var(--prism-text);\n background: color-mix(in srgb, var(--prism-primary) 8%, transparent);\n }\n .tool-btn[_ngcontent-%COMP%]:disabled {\n opacity: 0.3;\n cursor: default;\n }\n\n [_ngcontent-%COMP%]:focus-visible {\n outline: 2px solid var(--prism-primary);\n outline-offset: 2px;\n }"], changeDetection: 0 });
87
115
  }
88
116
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(PrismVariantRibbonComponent, [{
89
117
  type: Component,
90
118
  args: [{ selector: 'prism-variant-ribbon', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [PrismIconComponent], template: `
91
119
  @if (variants().length > 0) {
92
120
  <nav class="variant-ribbon">
93
- @for (v of variants(); track v.name; let i = $index) {
94
- <button
95
- class="v-tab"
96
- [class.v-tab--active]="rendererService.activeVariantIndex() === i"
97
- [style.--vc]="variantColor(v.name, i)"
98
- (click)="rendererService.selectVariant(i)"
99
- >
100
- <span class="v-dot"></span>
101
- {{ v.name }}
102
- </button>
103
- }
121
+ <div class="variant-ribbon-tabs" #tabsContainer>
122
+ @for (v of variants(); track v.name; let i = $index) {
123
+ <button
124
+ #tabButton
125
+ class="v-tab"
126
+ [class.v-tab--active]="rendererService.activeVariantIndex() === i"
127
+ [style.--vc]="variantColor(v.name, i)"
128
+ (click)="rendererService.selectVariant(i)"
129
+ >
130
+ <span class="v-dot"></span>
131
+ {{ v.name }}
132
+ </button>
133
+ }
134
+ </div>
104
135
  <div class="variant-ribbon-right">
105
136
  <button
106
137
  class="tool-btn"
@@ -129,6 +160,6 @@ export class PrismVariantRibbonComponent {
129
160
  </div>
130
161
  </nav>
131
162
  }
132
- `, styles: ["\n :host { display: block; flex-shrink: 0; }\n\n .variant-ribbon {\n display: flex;\n align-items: center;\n padding: 0 28px;\n background: var(--prism-bg);\n border-bottom: 1px solid var(--prism-border);\n height: 42px;\n gap: 2px;\n overflow-x: auto;\n scrollbar-width: none;\n }\n .variant-ribbon::-webkit-scrollbar { display: none; }\n\n .v-tab {\n position: relative;\n height: 100%;\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 0 14px;\n font-size: 13px;\n font-weight: 500;\n color: var(--prism-text-muted);\n white-space: nowrap;\n transition: color var(--dur-fast);\n background: none;\n border: none;\n cursor: pointer;\n font-family: var(--font-sans);\n }\n .v-tab::after {\n content: '';\n position: absolute;\n left: 10px;\n right: 10px;\n bottom: 0;\n height: 2px;\n background: linear-gradient(90deg, var(--prism-primary-from), var(--prism-primary-to));\n opacity: 0;\n transition: opacity var(--dur-fast);\n border-radius: 1px 1px 0 0;\n }\n .v-tab:hover { color: var(--prism-text-2); }\n .v-tab--active { color: var(--prism-text); }\n .v-tab--active::after { opacity: 1; }\n\n .v-dot {\n width: 6px;\n height: 6px;\n border-radius: 2px;\n background: var(--vc, var(--prism-primary));\n opacity: 0.6;\n }\n .v-tab--active .v-dot {\n opacity: 1;\n box-shadow: 0 0 6px var(--vc);\n }\n\n .variant-ribbon-right {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 8px;\n padding-left: 12px;\n border-left: 1px solid var(--prism-border);\n height: 24px;\n }\n\n .tool-btn {\n height: 24px;\n width: 24px;\n display: grid;\n place-items: center;\n border-radius: var(--radius-sm);\n color: var(--prism-text-muted);\n background: none;\n border: none;\n cursor: pointer;\n transition: all var(--dur-fast);\n }\n .tool-btn:hover:not(:disabled) {\n color: var(--prism-text);\n background: color-mix(in srgb, var(--prism-primary) 8%, transparent);\n }\n .tool-btn:disabled {\n opacity: 0.3;\n cursor: default;\n }\n\n :focus-visible {\n outline: 2px solid var(--prism-primary);\n outline-offset: 2px;\n }\n "] }]
133
- }], null, null); })();
134
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PrismVariantRibbonComponent, { className: "PrismVariantRibbonComponent", filePath: "app/variant-ribbon/prism-variant-ribbon.component.ts", lineNumber: 171 }); })();
163
+ `, styles: ["\n :host { display: block; flex-shrink: 0; }\n\n .variant-ribbon {\n display: flex;\n align-items: center;\n padding: 0 28px;\n background: var(--prism-bg);\n border-bottom: 1px solid var(--prism-border);\n height: 42px;\n }\n\n .variant-ribbon-tabs {\n flex: 1;\n min-width: 0;\n display: flex;\n align-items: center;\n gap: 2px;\n height: 100%;\n overflow-x: auto;\n scrollbar-width: none;\n }\n .variant-ribbon-tabs::-webkit-scrollbar { display: none; }\n\n .v-tab {\n position: relative;\n height: 100%;\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 0 14px;\n font-size: 13px;\n font-weight: 500;\n color: var(--prism-text-muted);\n white-space: nowrap;\n transition: color var(--dur-fast);\n background: none;\n border: none;\n cursor: pointer;\n font-family: var(--font-sans);\n }\n .v-tab::after {\n content: '';\n position: absolute;\n left: 10px;\n right: 10px;\n bottom: 0;\n height: 2px;\n background: linear-gradient(90deg, var(--prism-primary-from), var(--prism-primary-to));\n opacity: 0;\n transition: opacity var(--dur-fast);\n border-radius: 1px 1px 0 0;\n }\n .v-tab:hover { color: var(--prism-text-2); }\n .v-tab--active { color: var(--prism-text); }\n .v-tab--active::after { opacity: 1; }\n\n .v-dot {\n width: 6px;\n height: 6px;\n border-radius: 2px;\n background: var(--vc, var(--prism-primary));\n opacity: 0.6;\n }\n .v-tab--active .v-dot {\n opacity: 1;\n box-shadow: 0 0 6px var(--vc);\n }\n\n .variant-ribbon-right {\n flex-shrink: 0;\n display: flex;\n align-items: center;\n gap: 8px;\n padding-left: 12px;\n margin-left: 12px;\n border-left: 1px solid var(--prism-border);\n height: 24px;\n }\n\n .tool-btn {\n height: 24px;\n width: 24px;\n display: grid;\n place-items: center;\n border-radius: var(--radius-sm);\n color: var(--prism-text-muted);\n background: none;\n border: none;\n cursor: pointer;\n transition: all var(--dur-fast);\n }\n .tool-btn:hover:not(:disabled) {\n color: var(--prism-text);\n background: color-mix(in srgb, var(--prism-primary) 8%, transparent);\n }\n .tool-btn:disabled {\n opacity: 0.3;\n cursor: default;\n }\n\n :focus-visible {\n outline: 2px solid var(--prism-primary);\n outline-offset: 2px;\n }\n "] }]
164
+ }], () => [], { tabsContainer: [{ type: i0.ViewChild, args: ['tabsContainer', { isSignal: true }] }], tabButtons: [{ type: i0.ViewChildren, args: ['tabButton', { isSignal: true }] }] }); })();
165
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PrismVariantRibbonComponent, { className: "PrismVariantRibbonComponent", filePath: "app/variant-ribbon/prism-variant-ribbon.component.ts", lineNumber: 187 }); })();
@@ -0,0 +1,12 @@
1
+ import type { A11yManifestMeta, A11yReport, A11yThresholds } from '../../app/panels/a11y/a11y.types.js';
2
+ export declare const DEFAULT_A11Y_REPORT_PATH = "a11y-report.json";
3
+ export declare function loadA11yReport(reportPath: string): A11yReport | null;
4
+ export declare function readA11yMeta(reportPath: string, thresholdsInput?: Partial<A11yThresholds>): A11yManifestMeta | null;
5
+ export interface ThresholdViolation {
6
+ metric: 'score' | 'critical' | 'serious' | 'moderate';
7
+ actual: number;
8
+ threshold: number;
9
+ }
10
+ export declare function checkA11yThresholds(meta: A11yManifestMeta): ThresholdViolation[];
11
+ export declare function clearA11yReportCache(): void;
12
+ //# sourceMappingURL=a11y-report-reader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"a11y-report-reader.d.ts","sourceRoot":"","sources":["../../../src/builder/a11y/a11y-report-reader.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,gBAAgB,EAChB,UAAU,EACV,cAAc,EACf,MAAM,qCAAqC,CAAC;AAG7C,eAAO,MAAM,wBAAwB,qBAAqB,CAAC;AAI3D,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAapE;AAED,wBAAgB,YAAY,CAC1B,UAAU,EAAE,MAAM,EAClB,eAAe,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,GACxC,gBAAgB,GAAG,IAAI,CAQzB;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,OAAO,GAAG,UAAU,GAAG,SAAS,GAAG,UAAU,CAAC;IACtD,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,gBAAgB,GACrB,kBAAkB,EAAE,CAkBtB;AAED,wBAAgB,oBAAoB,IAAI,IAAI,CAE3C"}
@@ -0,0 +1,49 @@
1
+ import { readFileSync, statSync } from 'node:fs';
2
+ import { resolveA11yThresholds } from '../../app/panels/a11y/a11y-thresholds.js';
3
+ export const DEFAULT_A11Y_REPORT_PATH = 'a11y-report.json';
4
+ const cache = new Map();
5
+ export function loadA11yReport(reportPath) {
6
+ try {
7
+ const mtime = statSync(reportPath).mtimeMs;
8
+ const cached = cache.get(reportPath);
9
+ if (cached && cached.mtime === mtime)
10
+ return cached.data;
11
+ const raw = readFileSync(reportPath, 'utf-8');
12
+ const parsed = JSON.parse(raw);
13
+ cache.set(reportPath, { mtime, data: parsed });
14
+ return parsed;
15
+ }
16
+ catch {
17
+ return null;
18
+ }
19
+ }
20
+ export function readA11yMeta(reportPath, thresholdsInput) {
21
+ const report = loadA11yReport(reportPath);
22
+ if (!report?.total)
23
+ return null;
24
+ return {
25
+ total: report.total,
26
+ thresholds: resolveA11yThresholds(thresholdsInput),
27
+ };
28
+ }
29
+ export function checkA11yThresholds(meta) {
30
+ const violations = [];
31
+ const t = meta.thresholds;
32
+ const total = meta.total;
33
+ if (total.score < t.score) {
34
+ violations.push({ metric: 'score', actual: total.score, threshold: t.score });
35
+ }
36
+ if (total.critical > t.critical) {
37
+ violations.push({ metric: 'critical', actual: total.critical, threshold: t.critical });
38
+ }
39
+ if (total.serious > t.serious) {
40
+ violations.push({ metric: 'serious', actual: total.serious, threshold: t.serious });
41
+ }
42
+ if (total.moderate > t.moderate) {
43
+ violations.push({ metric: 'moderate', actual: total.moderate, threshold: t.moderate });
44
+ }
45
+ return violations;
46
+ }
47
+ export function clearA11yReportCache() {
48
+ cache.clear();
49
+ }
@@ -4,6 +4,7 @@ export interface RuntimeManifestOptions {
4
4
  components: ScannedComponent[];
5
5
  libraryImportPath: string;
6
6
  pages?: StyleguidePage[];
7
+ meta?: Record<string, unknown>;
7
8
  }
8
9
  export declare function generateRuntimeManifest(options: RuntimeManifestOptions): string;
9
10
  //# sourceMappingURL=runtime-manifest.generator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-manifest.generator.d.ts","sourceRoot":"","sources":["../../../src/builder/manifest/runtime-manifest.generator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAa,MAAM,8BAA8B,CAAC;AAChF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAIjE,MAAM,WAAW,sBAAsB;IACrC,UAAU,EAAE,gBAAgB,EAAE,CAAC;IAC/B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,KAAK,CAAC,EAAE,cAAc,EAAE,CAAC;CAC1B;AAyHD,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,sBAAsB,GAAG,MAAM,CAsE/E"}
1
+ {"version":3,"file":"runtime-manifest.generator.d.ts","sourceRoot":"","sources":["../../../src/builder/manifest/runtime-manifest.generator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAa,MAAM,8BAA8B,CAAC;AAChF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAIjE,MAAM,WAAW,sBAAsB;IACrC,UAAU,EAAE,gBAAgB,EAAE,CAAC;IAC/B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,KAAK,CAAC,EAAE,cAAc,EAAE,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAyHD,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,sBAAsB,GAAG,MAAM,CA8E/E"}
@@ -108,7 +108,7 @@ function groupComponentsByImportPath(components, fallbackImportPath) {
108
108
  return groups;
109
109
  }
110
110
  export function generateRuntimeManifest(options) {
111
- const { components, libraryImportPath, pages } = options;
111
+ const { components, libraryImportPath, pages, meta } = options;
112
112
  const directives = components.filter((c) => c.componentMeta.isDirective);
113
113
  const hasDirectives = directives.length > 0;
114
114
  const lines = [
@@ -162,6 +162,13 @@ export function generateRuntimeManifest(options) {
162
162
  if (pages && pages.length > 0) {
163
163
  lines.push(` pages: ${JSON.stringify(pages, null, 2).split('\n').map((l, i) => i === 0 ? l : ' ' + l).join('\n')},`);
164
164
  }
165
+ if (meta && Object.keys(meta).length > 0) {
166
+ const metaJson = JSON.stringify(meta, null, 2)
167
+ .split('\n')
168
+ .map((l, i) => (i === 0 ? l : ' ' + l))
169
+ .join('\n');
170
+ lines.push(` meta: ${metaJson},`);
171
+ }
165
172
  lines.push('};');
166
173
  lines.push('');
167
174
  return lines.join('\n');
@@ -1 +1 @@
1
- {"version":3,"file":"plugin-runner.d.ts","sourceRoot":"","sources":["../../../src/builder/plugin-runner/plugin-runner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAoB,MAAM,8BAA8B,CAAC;AAmBnG,wBAAsB,cAAc,CAClC,QAAQ,EAAE,aAAa,EACvB,OAAO,EAAE,aAAa,EAAE,GACvB,OAAO,CAAC,aAAa,CAAC,CAuDxB"}
1
+ {"version":3,"file":"plugin-runner.d.ts","sourceRoot":"","sources":["../../../src/builder/plugin-runner/plugin-runner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAoB,MAAM,8BAA8B,CAAC;AAmBnG,wBAAsB,cAAc,CAClC,QAAQ,EAAE,aAAa,EACvB,OAAO,EAAE,aAAa,EAAE,GACvB,OAAO,CAAC,aAAa,CAAC,CAyDxB"}
@@ -46,7 +46,9 @@ export async function runPluginHooks(manifest, plugins) {
46
46
  }
47
47
  pages[i] = current;
48
48
  }
49
- let result = { components, pages };
49
+ let result = manifest.meta
50
+ ? { components, pages, meta: manifest.meta }
51
+ : { components, pages };
50
52
  for (const plugin of plugins) {
51
53
  if (plugin.onManifestReady) {
52
54
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"prism-pipeline.d.ts","sourceRoot":"","sources":["../../../src/builder/shared/prism-pipeline.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAGhE,OAAO,EAAiB,KAAK,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAOpE,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,mGAAmG;IACnG,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7B,oGAAoG;IACpG,eAAe,EAAE,MAAM,GAAG,SAAS,CAAC;CACrC;AAED,wBAAgB,mBAAmB,IAAI,kBAAkB,CAExD;AAED,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,oBAAoB,EAC7B,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,kBAAkB,GACxB,OAAO,CAAC,mBAAmB,CAAC,CAoD9B"}
1
+ {"version":3,"file":"prism-pipeline.d.ts","sourceRoot":"","sources":["../../../src/builder/shared/prism-pipeline.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAGhE,OAAO,EAAiB,KAAK,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAYpE,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,mGAAmG;IACnG,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7B,oGAAoG;IACpG,eAAe,EAAE,MAAM,GAAG,SAAS,CAAC;CACrC;AAED,wBAAgB,mBAAmB,IAAI,kBAAkB,CAExD;AAED,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,oBAAoB,EAC7B,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,kBAAkB,GACxB,OAAO,CAAC,mBAAmB,CAAC,CA2E9B"}
@@ -6,6 +6,7 @@ import { discoverSecondaryEntryPoints } from '../scanner/entry-point-discovery.j
6
6
  import { loadConfig } from '../config-loader/config-loader.js';
7
7
  import { runPluginHooks } from '../plugin-runner/plugin-runner.js';
8
8
  import { generateRuntimeManifest } from '../manifest/runtime-manifest.generator.js';
9
+ import { DEFAULT_A11Y_REPORT_PATH, checkA11yThresholds, readA11yMeta, } from '../a11y/a11y-report-reader.js';
9
10
  export function createPipelineState() {
10
11
  return { scanner: undefined, lastEntrySetKey: undefined };
11
12
  }
@@ -20,12 +21,29 @@ export async function runPrismPipeline(options, context, state) {
20
21
  const scanResult = scanEntryPoints(workspaceRoot, options, state);
21
22
  const pages = config.pages ? [...config.pages] : [];
22
23
  context.reportStatus('Running plugin hooks...');
23
- const manifest = await runPluginHooks({ ...scanResult, pages }, config.plugins ?? []);
24
+ let manifest = await runPluginHooks({ ...scanResult, pages }, config.plugins ?? []);
25
+ const a11yReportPath = config.a11y?.reportPath ?? DEFAULT_A11Y_REPORT_PATH;
26
+ const a11yMeta = readA11yMeta(join(workspaceRoot, a11yReportPath), config.a11y?.thresholds);
27
+ if (a11yMeta) {
28
+ const violations = checkA11yThresholds(a11yMeta);
29
+ if (violations.length > 0) {
30
+ const summary = violations
31
+ .map((v) => `${v.metric}: ${v.actual} (threshold ${v.threshold})`)
32
+ .join(', ');
33
+ throw new Error(`ng-prism: a11y thresholds violated — ${summary}. ` +
34
+ `Update components or relax thresholds via config.a11y.thresholds.`);
35
+ }
36
+ manifest = {
37
+ ...manifest,
38
+ meta: { ...manifest.meta, a11y: a11yMeta },
39
+ };
40
+ }
24
41
  context.reportStatus('Generating runtime manifest...');
25
42
  const source = generateRuntimeManifest({
26
43
  components: manifest.components,
27
44
  libraryImportPath: options.libraryImportPath,
28
45
  pages: manifest.pages,
46
+ meta: manifest.meta,
29
47
  });
30
48
  const projectMeta = await context.getProjectMetadata(options.prismProject);
31
49
  const sourceRoot = projectMeta['sourceRoot'] ??
@@ -0,0 +1,10 @@
1
+ import type { A11yReport, A11yScoreResult } from '../../app/panels/a11y/a11y.types.js';
2
+ export interface RawAuditResult {
3
+ className: string;
4
+ variantName: string;
5
+ variantIndex: number;
6
+ /** Result from `calculateScore(axeResults)` in the running app. */
7
+ score: A11yScoreResult;
8
+ }
9
+ export declare function aggregateAuditResults(results: RawAuditResult[]): A11yReport;
10
+ //# sourceMappingURL=aggregator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aggregator.d.ts","sourceRoot":"","sources":["../../../src/cli/audit-a11y/aggregator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,eAAe,EAChB,MAAM,qCAAqC,CAAC;AAE7C,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,mEAAmE;IACnE,KAAK,EAAE,eAAe,CAAC;CACxB;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,UAAU,CAkD3E"}
@@ -0,0 +1,64 @@
1
+ export function aggregateAuditResults(results) {
2
+ if (results.length === 0) {
3
+ return {
4
+ total: {
5
+ score: 100,
6
+ violations: 0,
7
+ critical: 0,
8
+ serious: 0,
9
+ moderate: 0,
10
+ minor: 0,
11
+ passes: 0,
12
+ incomplete: 0,
13
+ auditedComponents: 0,
14
+ auditedVariants: 0,
15
+ },
16
+ components: {},
17
+ generatedAt: new Date().toISOString(),
18
+ };
19
+ }
20
+ const byComponent = new Map();
21
+ for (const r of results) {
22
+ const list = byComponent.get(r.className) ?? [];
23
+ list.push(r.score);
24
+ byComponent.set(r.className, list);
25
+ }
26
+ const componentBreakdown = {};
27
+ for (const [className, scores] of byComponent) {
28
+ componentBreakdown[className] = averageScore(scores);
29
+ }
30
+ const all = results.map((r) => r.score);
31
+ const total = averageScore(all);
32
+ return {
33
+ total: {
34
+ ...total,
35
+ // Sum (not average) of violation counts — they aggregate across the library.
36
+ violations: sum(all, (s) => s.violations),
37
+ critical: sum(all, (s) => s.critical),
38
+ serious: sum(all, (s) => s.serious),
39
+ moderate: sum(all, (s) => s.moderate),
40
+ minor: sum(all, (s) => s.minor),
41
+ auditedComponents: byComponent.size,
42
+ auditedVariants: results.length,
43
+ },
44
+ components: componentBreakdown,
45
+ generatedAt: new Date().toISOString(),
46
+ };
47
+ }
48
+ function averageScore(scores) {
49
+ const n = scores.length;
50
+ const sumF = (sel) => scores.reduce((acc, s) => acc + sel(s), 0);
51
+ return {
52
+ score: Math.round(sumF((s) => s.score) / n),
53
+ violations: Math.round(sumF((s) => s.violations) / n),
54
+ critical: Math.round(sumF((s) => s.critical) / n),
55
+ serious: Math.round(sumF((s) => s.serious) / n),
56
+ moderate: Math.round(sumF((s) => s.moderate) / n),
57
+ minor: Math.round(sumF((s) => s.minor) / n),
58
+ passes: Math.round(sumF((s) => s.passes) / n),
59
+ incomplete: Math.round(sumF((s) => s.incomplete) / n),
60
+ };
61
+ }
62
+ function sum(scores, sel) {
63
+ return scores.reduce((acc, s) => acc + sel(s), 0);
64
+ }
@@ -0,0 +1,14 @@
1
+ import type { RawAuditResult } from './aggregator.js';
2
+ export interface AuditOptions {
3
+ baseUrl: string;
4
+ /** Component class names to limit the audit to. Empty = all. */
5
+ include?: string[];
6
+ /** Component class names to skip. */
7
+ exclude?: string[];
8
+ /** Per-page navigation/render timeout in ms. Default 10_000. */
9
+ timeoutMs?: number;
10
+ /** Logger for progress output. */
11
+ log?: (msg: string) => void;
12
+ }
13
+ export declare function auditPrismApp(opts: AuditOptions): Promise<RawAuditResult[]>;
14
+ //# sourceMappingURL=auditor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auditor.d.ts","sourceRoot":"","sources":["../../../src/cli/audit-a11y/auditor.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAWtD,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,qCAAqC;IACrC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,gEAAgE;IAChE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kCAAkC;IAClC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7B;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAyGjF"}
@@ -0,0 +1,104 @@
1
+ import { createRequire } from 'node:module';
2
+ const require = createRequire(import.meta.url);
3
+ export async function auditPrismApp(opts) {
4
+ const log = opts.log ?? (() => undefined);
5
+ const timeoutMs = opts.timeoutMs ?? 10_000;
6
+ const playwright = await loadPlaywright();
7
+ const axeSource = loadAxeSource();
8
+ const browser = await playwright.chromium.launch({ headless: true });
9
+ try {
10
+ const context = await browser.newContext();
11
+ const page = await context.newPage();
12
+ await page.goto(opts.baseUrl, { waitUntil: 'networkidle', timeout: timeoutMs });
13
+ await page.waitForFunction(() => globalThis['__PRISM_MANIFEST__'] !== undefined, undefined, { timeout: timeoutMs });
14
+ const manifest = (await page.evaluate(() => globalThis['__PRISM_MANIFEST__']));
15
+ const targets = manifest.components.filter((c) => {
16
+ if (opts.include?.length && !opts.include.includes(c.className))
17
+ return false;
18
+ if (opts.exclude?.length && opts.exclude.includes(c.className))
19
+ return false;
20
+ return true;
21
+ });
22
+ const results = [];
23
+ for (const comp of targets) {
24
+ for (const variant of comp.variants) {
25
+ const url = new URL(opts.baseUrl);
26
+ url.searchParams.set('component', comp.className);
27
+ if (variant.index > 0) {
28
+ url.searchParams.set('variant', String(variant.index));
29
+ }
30
+ log(`Auditing ${comp.className} :: ${variant.name} (#${variant.index})`);
31
+ await page.goto(url.toString(), { waitUntil: 'load', timeout: timeoutMs });
32
+ await page.waitForFunction(([expected]) => {
33
+ const el = document.querySelector('.demo-wrap');
34
+ const key = el?.getAttribute('data-prism-rendered');
35
+ return typeof key === 'string' && key.startsWith(expected);
36
+ }, [`${comp.className}:`], { timeout: timeoutMs });
37
+ await page.addScriptTag({ content: axeSource });
38
+ const score = (await page.evaluate(async () => {
39
+ const target = document.querySelector('.demo-wrap');
40
+ const axe = globalThis['axe'];
41
+ if (!target) {
42
+ return {
43
+ score: 0,
44
+ violations: 0,
45
+ critical: 0,
46
+ serious: 0,
47
+ moderate: 0,
48
+ minor: 0,
49
+ passes: 0,
50
+ incomplete: 0,
51
+ };
52
+ }
53
+ const r = await axe.run(target);
54
+ const critical = r.violations.filter((v) => v.impact === 'critical').length;
55
+ const serious = r.violations.filter((v) => v.impact === 'serious').length;
56
+ const moderate = r.violations.filter((v) => v.impact === 'moderate').length;
57
+ const minor = r.violations.filter((v) => v.impact === 'minor').length;
58
+ const deductions = critical * 25 + serious * 10 + moderate * 5 + minor * 1;
59
+ return {
60
+ score: Math.max(0, 100 - deductions),
61
+ violations: r.violations.length,
62
+ critical,
63
+ serious,
64
+ moderate,
65
+ minor,
66
+ passes: r.passes.length,
67
+ incomplete: r.incomplete.length,
68
+ };
69
+ }));
70
+ results.push({
71
+ className: comp.className,
72
+ variantName: variant.name,
73
+ variantIndex: variant.index,
74
+ score,
75
+ });
76
+ }
77
+ }
78
+ return results;
79
+ }
80
+ finally {
81
+ await browser.close();
82
+ }
83
+ }
84
+ async function loadPlaywright() {
85
+ try {
86
+ return (await import('playwright'));
87
+ }
88
+ catch (err) {
89
+ throw new Error('ng-prism-audit-a11y: `playwright` is not installed. Run `npm install --save-dev playwright` ' +
90
+ 'and `npx playwright install chromium`. Original error: ' +
91
+ (err instanceof Error ? err.message : String(err)));
92
+ }
93
+ }
94
+ function loadAxeSource() {
95
+ try {
96
+ const path = require.resolve('axe-core/axe.min.js');
97
+ return require('node:fs').readFileSync(path, 'utf-8');
98
+ }
99
+ catch (err) {
100
+ throw new Error('ng-prism-audit-a11y: `axe-core` is not installed. Run `npm install --save-dev axe-core`. ' +
101
+ 'Original error: ' +
102
+ (err instanceof Error ? err.message : String(err)));
103
+ }
104
+ }
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../../src/cli/audit-a11y/cli.ts"],"names":[],"mappings":""}