@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.
- package/dist/app/header/prism-header-widget-host.component.d.ts +11 -0
- package/dist/app/header/prism-header-widget-host.component.d.ts.map +1 -0
- package/dist/app/header/prism-header-widget-host.component.js +57 -0
- package/dist/app/header/prism-header.component.d.ts +3 -0
- package/dist/app/header/prism-header.component.d.ts.map +1 -1
- package/dist/app/header/prism-header.component.js +76 -20
- package/dist/app/panels/a11y/a11y-header-badge.component.d.ts +16 -0
- package/dist/app/panels/a11y/a11y-header-badge.component.d.ts.map +1 -0
- package/dist/app/panels/a11y/a11y-header-badge.component.js +84 -0
- package/dist/app/panels/a11y/a11y-thresholds.d.ts +6 -0
- package/dist/app/panels/a11y/a11y-thresholds.d.ts.map +1 -0
- package/dist/app/panels/a11y/a11y-thresholds.js +13 -0
- package/dist/app/panels/a11y/a11y.types.d.ts +33 -0
- package/dist/app/panels/a11y/a11y.types.d.ts.map +1 -1
- package/dist/app/panels/builtin-header-widgets.d.ts +3 -0
- package/dist/app/panels/builtin-header-widgets.d.ts.map +1 -0
- package/dist/app/panels/builtin-header-widgets.js +9 -0
- package/dist/app/panels/panel-host/prism-panel-host.component.d.ts +16 -5
- package/dist/app/panels/panel-host/prism-panel-host.component.d.ts.map +1 -1
- package/dist/app/panels/panel-host/prism-panel-host.component.js +91 -44
- package/dist/app/provide-prism.d.ts.map +1 -1
- package/dist/app/provide-prism.js +11 -0
- package/dist/app/renderer/prism-renderer.component.d.ts +2 -0
- package/dist/app/renderer/prism-renderer.component.d.ts.map +1 -1
- package/dist/app/renderer/prism-renderer.component.js +18 -3
- package/dist/app/services/prism-panel.service.d.ts +5 -0
- package/dist/app/services/prism-panel.service.d.ts.map +1 -1
- package/dist/app/services/prism-panel.service.js +7 -3
- package/dist/app/services/prism-plugin.service.d.ts +4 -1
- package/dist/app/services/prism-plugin.service.d.ts.map +1 -1
- package/dist/app/services/prism-plugin.service.js +11 -0
- package/dist/app/variant-ribbon/prism-variant-ribbon.component.d.ts +4 -1
- package/dist/app/variant-ribbon/prism-variant-ribbon.component.d.ts.map +1 -1
- package/dist/app/variant-ribbon/prism-variant-ribbon.component.js +67 -36
- package/dist/builder/a11y/a11y-report-reader.d.ts +12 -0
- package/dist/builder/a11y/a11y-report-reader.d.ts.map +1 -0
- package/dist/builder/a11y/a11y-report-reader.js +49 -0
- package/dist/builder/manifest/runtime-manifest.generator.d.ts +1 -0
- package/dist/builder/manifest/runtime-manifest.generator.d.ts.map +1 -1
- package/dist/builder/manifest/runtime-manifest.generator.js +8 -1
- package/dist/builder/plugin-runner/plugin-runner.d.ts.map +1 -1
- package/dist/builder/plugin-runner/plugin-runner.js +3 -1
- package/dist/builder/shared/prism-pipeline.d.ts.map +1 -1
- package/dist/builder/shared/prism-pipeline.js +19 -1
- package/dist/cli/audit-a11y/aggregator.d.ts +10 -0
- package/dist/cli/audit-a11y/aggregator.d.ts.map +1 -0
- package/dist/cli/audit-a11y/aggregator.js +64 -0
- package/dist/cli/audit-a11y/auditor.d.ts +14 -0
- package/dist/cli/audit-a11y/auditor.d.ts.map +1 -0
- package/dist/cli/audit-a11y/auditor.js +104 -0
- package/dist/cli/audit-a11y/cli.d.ts +3 -0
- package/dist/cli/audit-a11y/cli.d.ts.map +1 -0
- package/dist/cli/audit-a11y/cli.js +143 -0
- package/dist/cli/audit-a11y/index.d.ts +4 -0
- package/dist/cli/audit-a11y/index.d.ts.map +1 -0
- package/dist/cli/audit-a11y/index.js +3 -0
- package/dist/cli/audit-a11y/static-server.d.ts +11 -0
- package/dist/cli/audit-a11y/static-server.d.ts.map +1 -0
- package/dist/cli/audit-a11y/static-server.js +61 -0
- package/dist/decorator/index.d.ts +1 -1
- package/dist/decorator/index.d.ts.map +1 -1
- package/dist/decorator/showcase.decorator.d.ts +1 -1
- package/dist/decorator/showcase.decorator.d.ts.map +1 -1
- package/dist/decorator/showcase.types.d.ts +20 -6
- package/dist/decorator/showcase.types.d.ts.map +1 -1
- package/dist/plugin/index.d.ts +2 -2
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +1 -1
- package/dist/plugin/plugin.types.d.ts +33 -0
- package/dist/plugin/plugin.types.d.ts.map +1 -1
- 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
|
|
9
|
+
function PrismVariantRibbonComponent_Conditional_0_For_4_Template(rf, ctx) { if (rf & 1) {
|
|
8
10
|
const _r2 = i0.ɵɵgetCurrentView();
|
|
9
|
-
i0.ɵɵelementStart(0, "button",
|
|
10
|
-
i0.ɵɵlistener("click", function
|
|
11
|
-
i0.ɵɵelement(
|
|
12
|
-
i0.ɵɵtext(
|
|
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 ɵ$
|
|
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, ɵ$
|
|
19
|
-
i0.ɵɵclassProp("v-tab--active", ctx_r3.rendererService.activeVariantIndex() === ɵ$
|
|
20
|
-
i0.ɵɵadvance(
|
|
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(
|
|
27
|
-
i0.ɵɵ
|
|
28
|
-
i0.ɵɵ
|
|
29
|
-
i0.ɵɵ
|
|
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(
|
|
32
|
-
i0.ɵɵlistener("click", function
|
|
33
|
-
i0.ɵɵelement(
|
|
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"]],
|
|
83
|
-
i0.ɵɵ
|
|
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
|
|
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
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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
|
|
133
|
-
}],
|
|
134
|
-
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PrismVariantRibbonComponent, { className: "PrismVariantRibbonComponent", filePath: "app/variant-ribbon/prism-variant-ribbon.component.ts", lineNumber:
|
|
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;
|
|
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,
|
|
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 =
|
|
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;
|
|
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
|
-
|
|
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 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../../src/cli/audit-a11y/cli.ts"],"names":[],"mappings":""}
|