astro 4.4.14 → 4.5.0

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 (98) hide show
  1. package/components/Code.astro +15 -12
  2. package/dist/@types/astro.d.ts +95 -18
  3. package/dist/assets/utils/getAssetsPrefix.d.ts +2 -0
  4. package/dist/assets/utils/getAssetsPrefix.js +14 -0
  5. package/dist/assets/vite-plugin-assets.js +10 -3
  6. package/dist/cli/add/index.js +76 -28
  7. package/dist/cli/install-package.js +2 -2
  8. package/dist/content/types-generator.js +56 -7
  9. package/dist/content/vite-plugin-content-assets.js +11 -3
  10. package/dist/core/app/common.js +2 -0
  11. package/dist/core/app/index.js +10 -2
  12. package/dist/core/app/types.d.ts +7 -2
  13. package/dist/core/base-pipeline.d.ts +2 -1
  14. package/dist/core/base-pipeline.js +2 -1
  15. package/dist/core/build/generate.js +1 -0
  16. package/dist/core/build/internal.d.ts +6 -0
  17. package/dist/core/build/internal.js +1 -0
  18. package/dist/core/build/plugins/index.js +6 -1
  19. package/dist/core/build/plugins/plugin-analyzer.js +10 -98
  20. package/dist/core/build/plugins/plugin-css.js +27 -1
  21. package/dist/core/build/plugins/plugin-manifest.js +5 -2
  22. package/dist/core/build/plugins/plugin-scripts.d.ts +8 -0
  23. package/dist/core/build/plugins/plugin-scripts.js +34 -0
  24. package/dist/core/compile/compile.d.ts +1 -7
  25. package/dist/core/compile/compile.js +5 -4
  26. package/dist/core/compile/style.d.ts +4 -3
  27. package/dist/core/compile/style.js +5 -4
  28. package/dist/core/compile/types.d.ts +11 -0
  29. package/dist/core/config/schema.d.ts +177 -113
  30. package/dist/core/config/schema.js +42 -9
  31. package/dist/core/config/vite-load.js +1 -0
  32. package/dist/core/constants.d.ts +1 -0
  33. package/dist/core/constants.js +3 -1
  34. package/dist/core/create-vite.js +5 -3
  35. package/dist/core/dev/dev.js +1 -1
  36. package/dist/core/errors/dev/vite.js +1 -1
  37. package/dist/core/messages.js +2 -2
  38. package/dist/core/render/params-and-props.js +2 -1
  39. package/dist/core/render/ssr-element.d.ts +8 -8
  40. package/dist/core/render/ssr-element.js +4 -2
  41. package/dist/core/render-context.js +3 -1
  42. package/dist/core/routing/astro-designed-error-pages.d.ts +2 -0
  43. package/dist/core/routing/astro-designed-error-pages.js +21 -0
  44. package/dist/runtime/client/dev-toolbar/apps/audit/index.d.ts +8 -15
  45. package/dist/runtime/client/dev-toolbar/apps/audit/index.js +130 -249
  46. package/dist/runtime/client/dev-toolbar/apps/audit/{a11y.js → rules/a11y.js} +4 -2
  47. package/dist/runtime/client/dev-toolbar/apps/audit/rules/index.d.ts +35 -0
  48. package/dist/runtime/client/dev-toolbar/apps/audit/rules/index.js +40 -0
  49. package/dist/runtime/client/dev-toolbar/apps/audit/{perf.js → rules/perf.js} +2 -2
  50. package/dist/runtime/client/dev-toolbar/apps/audit/ui/audit-list-item.d.ts +7 -0
  51. package/dist/runtime/client/dev-toolbar/apps/audit/ui/audit-list-item.js +137 -0
  52. package/dist/runtime/client/dev-toolbar/apps/audit/ui/audit-list-window.d.ts +23 -0
  53. package/dist/runtime/client/dev-toolbar/apps/audit/ui/audit-list-window.js +384 -0
  54. package/dist/runtime/client/dev-toolbar/apps/audit/ui/audit-ui.d.ts +6 -0
  55. package/dist/runtime/client/dev-toolbar/apps/audit/ui/audit-ui.js +126 -0
  56. package/dist/runtime/client/dev-toolbar/apps/utils/window.d.ts +1 -1
  57. package/dist/runtime/client/dev-toolbar/apps/utils/window.js +3 -1
  58. package/dist/runtime/client/dev-toolbar/entrypoint.js +43 -15
  59. package/dist/runtime/client/dev-toolbar/settings.d.ts +3 -1
  60. package/dist/runtime/client/dev-toolbar/settings.js +8 -2
  61. package/dist/runtime/client/dev-toolbar/toolbar.d.ts +1 -0
  62. package/dist/runtime/client/dev-toolbar/toolbar.js +10 -8
  63. package/dist/runtime/client/dev-toolbar/ui-library/badge.d.ts +14 -4
  64. package/dist/runtime/client/dev-toolbar/ui-library/badge.js +72 -33
  65. package/dist/runtime/client/dev-toolbar/ui-library/button.d.ts +14 -4
  66. package/dist/runtime/client/dev-toolbar/ui-library/button.js +100 -47
  67. package/dist/runtime/client/dev-toolbar/ui-library/card.d.ts +9 -0
  68. package/dist/runtime/client/dev-toolbar/ui-library/card.js +57 -2
  69. package/dist/runtime/client/dev-toolbar/ui-library/highlight.d.ts +9 -0
  70. package/dist/runtime/client/dev-toolbar/ui-library/highlight.js +54 -2
  71. package/dist/runtime/client/dev-toolbar/ui-library/icons.d.ts +4 -0
  72. package/dist/runtime/client/dev-toolbar/ui-library/icons.js +5 -1
  73. package/dist/runtime/client/dev-toolbar/ui-library/toggle.d.ts +9 -0
  74. package/dist/runtime/client/dev-toolbar/ui-library/toggle.js +64 -5
  75. package/dist/runtime/compiler/index.d.ts +1 -1
  76. package/dist/runtime/compiler/index.js +2 -0
  77. package/dist/runtime/server/hydration.js +3 -2
  78. package/dist/runtime/server/index.d.ts +1 -1
  79. package/dist/runtime/server/index.js +2 -0
  80. package/dist/runtime/server/render/astro/factory.d.ts +1 -1
  81. package/dist/runtime/server/render/component.js +4 -5
  82. package/dist/runtime/server/render/index.d.ts +1 -0
  83. package/dist/runtime/server/render/index.js +2 -0
  84. package/dist/runtime/server/render/script.d.ts +6 -0
  85. package/dist/runtime/server/render/script.js +15 -0
  86. package/dist/transitions/router.js +12 -3
  87. package/dist/vite-plugin-astro/index.d.ts +2 -2
  88. package/dist/vite-plugin-astro/index.js +12 -1
  89. package/dist/vite-plugin-astro/types.d.ts +21 -1
  90. package/dist/vite-plugin-astro-server/pipeline.js +6 -2
  91. package/dist/vite-plugin-astro-server/plugin.js +6 -2
  92. package/dist/vite-plugin-astro-server/response.d.ts +6 -0
  93. package/dist/vite-plugin-astro-server/response.js +13 -0
  94. package/dist/vite-plugin-astro-server/route.js +18 -2
  95. package/package.json +8 -9
  96. package/tsconfigs/base.json +3 -1
  97. /package/dist/runtime/client/dev-toolbar/apps/audit/{a11y.d.ts → rules/a11y.d.ts} +0 -0
  98. /package/dist/runtime/client/dev-toolbar/apps/audit/{perf.d.ts → rules/perf.d.ts} +0 -0
@@ -1,25 +1,15 @@
1
- import { finder } from "@medv/finder";
2
- import {
3
- attachTooltipToHighlight,
4
- createHighlight,
5
- getElementsPositionInDocument,
6
- positionHighlight
7
- } from "../utils/highlight.js";
8
- import { closeOnOutsideClick, createWindowElement } from "../utils/window.js";
9
- import { a11y } from "./a11y.js";
10
- import { perf } from "./perf.js";
1
+ import { settings } from "../../settings.js";
2
+ import { positionHighlight } from "../utils/highlight.js";
3
+ import { closeOnOutsideClick } from "../utils/window.js";
4
+ import { rulesCategories } from "./rules/index.js";
5
+ import { DevToolbarAuditListItem } from "./ui/audit-list-item.js";
6
+ import { DevToolbarAuditListWindow } from "./ui/audit-list-window.js";
7
+ import { createAuditUI } from "./ui/audit-ui.js";
11
8
  const icon = '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 1 20 16"><path fill="#fff" d="M.6 2A1.1 1.1 0 0 1 1.7.9h16.6a1.1 1.1 0 1 1 0 2.2H1.6A1.1 1.1 0 0 1 .8 2Zm1.1 7.1h6a1.1 1.1 0 0 0 0-2.2h-6a1.1 1.1 0 0 0 0 2.2ZM9.3 13H1.8a1.1 1.1 0 1 0 0 2.2h7.5a1.1 1.1 0 1 0 0-2.2Zm11.3 1.9a1.1 1.1 0 0 1-1.5 0l-1.7-1.7a4.1 4.1 0 1 1 1.6-1.6l1.6 1.7a1.1 1.1 0 0 1 0 1.6Zm-5.3-3.4a1.9 1.9 0 1 0 0-3.8 1.9 1.9 0 0 0 0 3.8Z"/></svg>';
12
- const rules = [...a11y, ...perf];
13
- const dynamicAuditRuleKeys = ["title", "message"];
14
- function resolveAuditRule(rule, element) {
15
- let resolved = { ...rule };
16
- for (const key of dynamicAuditRuleKeys) {
17
- const value = rule[key];
18
- if (typeof value === "string")
19
- continue;
20
- resolved[key] = value(element);
21
- }
22
- return resolved;
9
+ try {
10
+ customElements.define("astro-dev-toolbar-audit-window", DevToolbarAuditListWindow);
11
+ customElements.define("astro-dev-toolbar-audit-list-item", DevToolbarAuditListItem);
12
+ } catch (e) {
23
13
  }
24
14
  var audit_default = {
25
15
  id: "astro:audit",
@@ -27,201 +17,116 @@ var audit_default = {
27
17
  icon,
28
18
  async init(canvas, eventTarget) {
29
19
  let audits = [];
20
+ let auditWindow = document.createElement(
21
+ "astro-dev-toolbar-audit-window"
22
+ );
23
+ let hasCreatedUI = false;
24
+ canvas.appendChild(auditWindow);
30
25
  await lint();
31
- document.addEventListener("astro:after-swap", async () => lint());
32
- document.addEventListener("astro:page-load", async () => refreshLintPositions);
33
- closeOnOutsideClick(eventTarget);
34
- async function lint() {
35
- audits.forEach(({ highlightElement }) => {
36
- highlightElement.remove();
37
- });
38
- audits = [];
39
- canvas.getElementById("no-audit")?.remove();
40
- const selectorCache = /* @__PURE__ */ new Map();
41
- for (const rule of rules) {
42
- const elements = selectorCache.get(rule.selector) ?? document.querySelectorAll(rule.selector);
43
- let matches = [];
44
- if (typeof rule.match === "undefined") {
45
- matches = Array.from(elements);
26
+ let mutationDebounce;
27
+ const observer = new MutationObserver(() => {
28
+ if (mutationDebounce) {
29
+ clearTimeout(mutationDebounce);
30
+ }
31
+ mutationDebounce = setTimeout(() => {
32
+ settings.logger.verboseLog("Rerunning audit lints because the DOM has been updated.");
33
+ if ("requestIdleCallback" in window) {
34
+ window.requestIdleCallback(
35
+ async () => {
36
+ lint();
37
+ },
38
+ { timeout: 300 }
39
+ );
46
40
  } else {
47
- for (const element of elements) {
48
- if (await rule.match(element)) {
49
- matches.push(element);
50
- }
51
- }
52
- }
53
- for (const element of matches) {
54
- if (audits.some((audit) => audit.auditedElement === element))
55
- continue;
56
- await createAuditProblem(rule, element);
41
+ setTimeout(() => {
42
+ lint();
43
+ }, 150);
57
44
  }
45
+ }, 250);
46
+ });
47
+ setupObserver();
48
+ document.addEventListener("astro:before-preparation", () => {
49
+ observer.disconnect();
50
+ });
51
+ document.addEventListener("astro:after-swap", async () => {
52
+ lint();
53
+ });
54
+ document.addEventListener("astro:page-load", async () => {
55
+ refreshLintPositions();
56
+ setTimeout(() => {
57
+ setupObserver();
58
+ }, 100);
59
+ });
60
+ eventTarget.addEventListener("app-toggled", (event) => {
61
+ if (event.detail.state === true) {
62
+ createAuditsUI();
58
63
  }
59
- if (audits.length > 0) {
60
- eventTarget.dispatchEvent(
61
- new CustomEvent("toggle-notification", {
62
- detail: {
63
- state: true
64
- }
65
- })
66
- );
67
- const auditListWindow = createWindowElement(
68
- `
69
- <style>
70
- astro-dev-toolbar-window {
71
- left: initial;
72
- top: 8px;
73
- right: 8px;
74
- transform: none;
75
- width: 320px;
76
- max-height: 320px;
77
- padding: 0;
78
- overflow: hidden;
79
- }
80
-
81
- hr {
82
- margin: 0;
83
- }
84
-
85
- header {
86
- display: flex;
87
- justify-content: space-between;
88
- align-items: center;
89
- padding: 18px;
90
- }
91
-
92
- h1 {
93
- font-size: 22px;
94
- font-weight: 600;
95
- color: #fff;
96
- }
97
-
98
- ul, li {
99
- margin: 0;
100
- padding: 0;
101
- list-style: none;
102
- }
103
-
104
- h1, h2 {
105
- margin: 0;
106
- }
107
-
108
- h3 {
109
- margin: 0;
110
- margin-bottom: 8px;
111
- color: white;
112
- white-space: nowrap;
113
- }
114
-
115
- .audit-title {
116
- font-weight: bold;
117
- color: white;
118
- margin-right: 1ch;
119
- }
120
-
121
- #audit-list {
122
- display: flex;
123
- flex-direction: column;
124
- overflow: auto;
125
- }
126
- </style>
127
-
128
- <header>
129
- <h1>Audits</h1>
130
- <astro-dev-toolbar-badge size="large">${audits.length} problem${audits.length > 1 ? "s" : ""} found</astro-dev-toolbar-badge>
131
- </header>
132
- <hr />`
133
- );
134
- const auditListUl = document.createElement("ul");
135
- auditListUl.id = "audit-list";
136
- audits.forEach((audit, index) => {
137
- const resolvedRule = resolveAuditRule(audit.rule, audit.auditedElement);
138
- const card = document.createElement("astro-dev-toolbar-card");
139
- card.shadowRoot.innerHTML = `
140
- <style>
141
- :host>button {
142
- text-align: left;
143
- box-shadow: none !important;
144
- ${index + 1 < audits.length ? "border-radius: 0 !important;" : "border-radius: 0 0 8px 8px !important;"}
145
- }
146
-
147
- :host>button:hover {
148
- cursor: pointer;
149
- }
150
- </style>`;
151
- card.clickAction = () => {
152
- audit.highlightElement.scrollIntoView();
153
- audit.highlightElement.focus();
154
- };
155
- const h3 = document.createElement("h3");
156
- h3.innerText = finder(audit.auditedElement);
157
- card.appendChild(h3);
158
- const div = document.createElement("div");
159
- const title = document.createElement("span");
160
- title.classList.add("audit-title");
161
- title.innerHTML = resolvedRule.title;
162
- div.appendChild(title);
163
- card.appendChild(div);
164
- auditListUl.appendChild(card);
64
+ });
65
+ closeOnOutsideClick(eventTarget, () => {
66
+ const activeAudits = audits.filter((audit) => audit.card?.hasAttribute("active"));
67
+ if (activeAudits.length > 0) {
68
+ activeAudits.forEach((audit) => {
69
+ audit.card?.toggleAttribute("active", false);
165
70
  });
166
- auditListWindow.appendChild(auditListUl);
167
- canvas.append(auditListWindow);
168
- } else {
169
- eventTarget.dispatchEvent(
170
- new CustomEvent("toggle-notification", {
171
- detail: {
172
- state: false
173
- }
174
- })
175
- );
176
- const window2 = createWindowElement(
177
- `<style>
178
- header {
179
- display: flex;
180
- }
181
-
182
- h1 {
183
- display: flex;
184
- align-items: center;
185
- gap: 8px;
186
- font-weight: 600;
187
- color: #fff;
188
- margin: 0;
189
- font-size: 22px;
190
- }
191
-
192
- astro-dev-toolbar-icon {
193
- width: 1em;
194
- height: 1em;
195
- padding: 8px;
196
- display: block;
197
- background: green;
198
- border-radius: 9999px;
199
- }
200
- </style>
201
- <header>
202
- <h1><astro-dev-toolbar-icon icon="check-circle"></astro-dev-toolbar-icon>No accessibility or performance issues detected.</h1>
203
- </header>
204
- <p>
205
- Nice work! This app scans the page and highlights common accessibility and performance issues for you, like a missing "alt" attribute on an image, or a image not using performant attributes.
206
- </p>
207
- `
208
- );
209
- canvas.append(window2);
71
+ return true;
210
72
  }
211
- ["scroll", "resize"].forEach((event) => {
212
- window.addEventListener(event, refreshLintPositions);
213
- });
73
+ return false;
74
+ });
75
+ async function createAuditsUI() {
76
+ if (hasCreatedUI)
77
+ return;
78
+ const fragment = document.createDocumentFragment();
79
+ for (const audit of audits) {
80
+ const { card, highlight } = createAuditUI(audit, audits);
81
+ audit.card = card;
82
+ audit.highlight = highlight;
83
+ fragment.appendChild(highlight);
84
+ }
85
+ auditWindow.audits = audits;
86
+ canvas.appendChild(fragment);
87
+ hasCreatedUI = true;
214
88
  }
215
- function refreshLintPositions() {
216
- const noAuditBlock = canvas.getElementById("no-audit");
217
- if (noAuditBlock) {
218
- const devOverlayRect = document.querySelector("astro-dev-toolbar")?.shadowRoot.querySelector("#dev-toolbar-root")?.getBoundingClientRect();
219
- noAuditBlock.style.top = `${(devOverlayRect?.top ?? 0) - (devOverlayRect?.height ?? 0) - 16}px`;
89
+ async function lint() {
90
+ if (audits.length > 0) {
91
+ audits.forEach((audit) => {
92
+ audit.highlight?.remove();
93
+ audit.card?.remove();
94
+ });
95
+ audits = [];
96
+ hasCreatedUI = false;
220
97
  }
221
- audits.forEach(({ highlightElement, auditedElement }) => {
222
- const rect = auditedElement.getBoundingClientRect();
223
- positionHighlight(highlightElement, rect);
224
- });
98
+ const selectorCache = /* @__PURE__ */ new Map();
99
+ for (const ruleCategory of rulesCategories) {
100
+ for (const rule of ruleCategory.rules) {
101
+ const elements = selectorCache.get(rule.selector) ?? document.querySelectorAll(rule.selector);
102
+ let matches = [];
103
+ if (typeof rule.match === "undefined") {
104
+ matches = Array.from(elements);
105
+ } else {
106
+ for (const element of elements) {
107
+ try {
108
+ if (await rule.match(element)) {
109
+ matches.push(element);
110
+ }
111
+ } catch (e) {
112
+ settings.logger.error(`Error while running audit's match function: ${e}`);
113
+ }
114
+ }
115
+ }
116
+ for (const element of matches) {
117
+ if (audits.some((audit) => audit.auditedElement === element))
118
+ continue;
119
+ await createAuditProblem(rule, element);
120
+ }
121
+ }
122
+ }
123
+ eventTarget.dispatchEvent(
124
+ new CustomEvent("toggle-notification", {
125
+ detail: {
126
+ state: audits.length > 0
127
+ }
128
+ })
129
+ );
225
130
  }
226
131
  async function createAuditProblem(rule, originalElement) {
227
132
  const computedStyle = window.getComputedStyle(originalElement);
@@ -232,52 +137,28 @@ var audit_default = {
232
137
  if (originalElement.nodeName === "IMG" && !originalElement.complete) {
233
138
  return;
234
139
  }
235
- const rect = originalElement.getBoundingClientRect();
236
- const highlight = createHighlight(rect, "warning", { "data-audit-code": rule.code });
237
- const tooltip = buildAuditTooltip(rule, originalElement);
238
- const { isFixed } = getElementsPositionInDocument(originalElement);
239
- if (isFixed) {
240
- tooltip.style.position = highlight.style.position = "fixed";
241
- }
242
- attachTooltipToHighlight(highlight, tooltip, originalElement);
243
- canvas.append(highlight);
244
140
  audits.push({
245
- highlightElement: highlight,
246
141
  auditedElement: originalElement,
247
- rule
142
+ rule,
143
+ card: null,
144
+ highlight: null
248
145
  });
249
146
  }
250
- function buildAuditTooltip(rule, element) {
251
- const tooltip = document.createElement("astro-dev-toolbar-tooltip");
252
- const { title, message } = resolveAuditRule(rule, element);
253
- tooltip.sections = [
254
- {
255
- icon: "warning",
256
- title: escapeHtml(title)
257
- },
258
- {
259
- content: escapeHtml(message)
260
- }
261
- ];
262
- const elementFile = element.getAttribute("data-astro-source-file");
263
- const elementPosition = element.getAttribute("data-astro-source-loc");
264
- if (elementFile) {
265
- const elementFileWithPosition = elementFile + (elementPosition ? ":" + elementPosition : "");
266
- tooltip.sections.push({
267
- content: elementFileWithPosition.slice(
268
- window.__astro_dev_toolbar__.root.length - 1
269
- // We want to keep the final slash, so minus one.
270
- ),
271
- clickDescription: "Click to go to file",
272
- async clickAction() {
273
- await fetch("/__open-in-editor?file=" + encodeURIComponent(elementFileWithPosition));
274
- }
275
- });
276
- }
277
- return tooltip;
147
+ function refreshLintPositions() {
148
+ audits.forEach(({ highlight, auditedElement }) => {
149
+ const rect = auditedElement.getBoundingClientRect();
150
+ if (highlight)
151
+ positionHighlight(highlight, rect);
152
+ });
278
153
  }
279
- function escapeHtml(unsafe) {
280
- return unsafe.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
154
+ ["scroll", "resize"].forEach((event) => {
155
+ window.addEventListener(event, refreshLintPositions);
156
+ });
157
+ function setupObserver() {
158
+ observer.observe(document.body, {
159
+ childList: true,
160
+ subtree: true
161
+ });
281
162
  }
282
163
  }
283
164
  };
@@ -262,8 +262,8 @@ const a11y = [
262
262
  }
263
263
  },
264
264
  {
265
- code: "a11y-invalid-attribute",
266
- title: "Attributes important for accessibility should have a valid value",
265
+ code: "a11y-invalid-href",
266
+ title: "Invalid `href` attribute",
267
267
  message: "`href` should not be empty, `'#'`, or `javascript:`.",
268
268
  selector: 'a[href]:is([href=""], [href="#"], [href^="javascript:" i])'
269
269
  },
@@ -306,6 +306,7 @@ const a11y = [
306
306
  {
307
307
  code: "a11y-missing-attribute",
308
308
  title: "Required attributes missing.",
309
+ description: "Some HTML elements require additional attributes for accessibility. For example, an `img` element requires an `alt` attribute, this attribute is used to describe the content of the image for screen readers.",
309
310
  message: (element) => {
310
311
  const requiredAttributes = a11y_required_attributes[element.localName];
311
312
  const missingAttributes = requiredAttributes.filter(
@@ -448,6 +449,7 @@ const a11y = [
448
449
  {
449
450
  code: "a11y-no-noninteractive-tabindex",
450
451
  title: "Invalid `tabindex` on non-interactive element",
452
+ description: 'The `tabindex` attribute should only be used on interactive elements, as it can be confusing for keyboard-only users to navigate through non-interactive elements. If your element is only conditionally interactive, consider using `tabindex="-1"` to make it focusable only when it is actually interactive.',
451
453
  message: (element) => `${element.localName} elements should not have \`tabindex\` attribute`,
452
454
  selector: "[tabindex]",
453
455
  match(element) {
@@ -0,0 +1,35 @@
1
+ type DynamicString = string | ((element: Element) => string);
2
+ export interface AuditRule {
3
+ code: string;
4
+ title: DynamicString;
5
+ message: DynamicString;
6
+ description?: DynamicString;
7
+ }
8
+ export interface ResolvedAuditRule {
9
+ code: string;
10
+ title: string;
11
+ message: string;
12
+ description?: string;
13
+ }
14
+ export interface AuditRuleWithSelector extends AuditRule {
15
+ selector: string;
16
+ match?: (element: Element) => boolean | null | undefined | void | Promise<boolean> | Promise<void> | Promise<null> | Promise<undefined>;
17
+ }
18
+ export declare const rulesCategories: ({
19
+ code: string;
20
+ name: string;
21
+ icon: "person-arms-spread";
22
+ rules: AuditRuleWithSelector[];
23
+ } | {
24
+ code: string;
25
+ name: string;
26
+ icon: "gauge";
27
+ rules: AuditRuleWithSelector[];
28
+ })[];
29
+ export declare function resolveAuditRule(rule: AuditRule, element: Element): ResolvedAuditRule;
30
+ export declare function getAuditCategory(rule: AuditRule): 'perf' | 'a11y';
31
+ export declare const categoryLabel: {
32
+ perf: string;
33
+ a11y: string;
34
+ };
35
+ export {};
@@ -0,0 +1,40 @@
1
+ import { settings } from "../../../settings.js";
2
+ import { a11y } from "./a11y.js";
3
+ import { perf } from "./perf.js";
4
+ const rulesCategories = [
5
+ { code: "a11y", name: "Accessibility", icon: "person-arms-spread", rules: a11y },
6
+ { code: "perf", name: "Performance", icon: "gauge", rules: perf }
7
+ ];
8
+ const dynamicAuditRuleKeys = ["title", "message", "description"];
9
+ function resolveAuditRule(rule, element) {
10
+ let resolved = { ...rule };
11
+ for (const key of dynamicAuditRuleKeys) {
12
+ const value = rule[key];
13
+ if (typeof value === "string")
14
+ continue;
15
+ try {
16
+ if (!value) {
17
+ resolved[key] = "";
18
+ continue;
19
+ }
20
+ resolved[key] = value(element);
21
+ } catch (err) {
22
+ settings.logger.error(`Error resolving dynamic audit rule ${rule.code}'s ${key}: ${err}`);
23
+ resolved[key] = "Error resolving dynamic rule";
24
+ }
25
+ }
26
+ return resolved;
27
+ }
28
+ function getAuditCategory(rule) {
29
+ return rule.code.split("-")[0];
30
+ }
31
+ const categoryLabel = {
32
+ perf: "performance",
33
+ a11y: "accessibility"
34
+ };
35
+ export {
36
+ categoryLabel,
37
+ getAuditCategory,
38
+ resolveAuditRule,
39
+ rulesCategories
40
+ };
@@ -21,7 +21,7 @@ const perf = [
21
21
  },
22
22
  {
23
23
  code: "perf-use-loading-lazy",
24
- title: 'Use the loading="lazy" attribute',
24
+ title: "Unoptimized loading attribute",
25
25
  message: (element) => `This ${element.nodeName} tag is below the fold and could be lazy-loaded to improve performance.`,
26
26
  selector: 'img:not([loading]), img[loading="eager"], iframe:not([loading]), iframe[loading="eager"]',
27
27
  match(element) {
@@ -35,7 +35,7 @@ const perf = [
35
35
  },
36
36
  {
37
37
  code: "perf-use-loading-eager",
38
- title: 'Use the loading="eager" attribute',
38
+ title: "Unoptimized loading attribute",
39
39
  message: (element) => `This ${element.nodeName} tag is above the fold and could be eagerly-loaded to improve performance.`,
40
40
  selector: 'img[loading="lazy"], iframe[loading="lazy"]',
41
41
  match(element) {
@@ -0,0 +1,7 @@
1
+ export declare class DevToolbarAuditListItem extends HTMLElement {
2
+ clickAction?: () => void | (() => Promise<void>);
3
+ shadowRoot: ShadowRoot;
4
+ isManualFocus: boolean;
5
+ constructor();
6
+ connectedCallback(): void;
7
+ }
@@ -0,0 +1,137 @@
1
+ class DevToolbarAuditListItem extends HTMLElement {
2
+ clickAction;
3
+ shadowRoot;
4
+ isManualFocus;
5
+ constructor() {
6
+ super();
7
+ this.shadowRoot = this.attachShadow({ mode: "open" });
8
+ this.isManualFocus = false;
9
+ this.shadowRoot.innerHTML = `
10
+ <style>
11
+ :host>button, :host>div {
12
+ box-sizing: border-box;
13
+ padding: 16px;
14
+ background: transparent;
15
+ border: none;
16
+ border-bottom: 1px solid #1F2433;
17
+ text-decoration: none;
18
+ width: 100%;
19
+ height: 100%;
20
+ }
21
+
22
+ h1, h2, h3, h4, h5, h6 {
23
+ color: #fff;
24
+ font-weight: 600;
25
+ }
26
+
27
+ :host>button:hover, :host([hovered])>button {
28
+ background: #FFFFFF20;
29
+ }
30
+
31
+ svg {
32
+ display: block;
33
+ margin: 0 auto;
34
+ }
35
+
36
+ :host>button#astro-overlay-card {
37
+ text-align: left;
38
+ box-shadow: none;
39
+ display: flex;
40
+ align-items: center;
41
+ overflow: hidden;
42
+ gap: 8px;
43
+ }
44
+
45
+ :host(:not([active]))>button:hover {
46
+ cursor: pointer;
47
+ }
48
+
49
+ .extended-info {
50
+ display: none;
51
+ color: white;
52
+ font-size: 14px;
53
+ }
54
+
55
+ .extended-info hr {
56
+ border: 1px solid rgba(27, 30, 36, 1);
57
+ }
58
+
59
+ :host([active]) .extended-info {
60
+ display: block;
61
+ position: absolute;
62
+ height: 100%;
63
+ top: 98px;
64
+ height: calc(100% - 98px);
65
+ background: #0d0e12;
66
+ user-select: text;
67
+ overflow: auto;
68
+ border: none;
69
+ z-index: 1000000000;
70
+ flex-direction: column;
71
+ }
72
+
73
+ :host([active])>button#astro-overlay-card {
74
+ display: none;
75
+ }
76
+
77
+ .audit-title {
78
+ margin: 0;
79
+ margin-bottom: 4px;
80
+ }
81
+
82
+ .extended-info .audit-selector {
83
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
84
+ display: flex;
85
+ align-items: center;
86
+ border-bottom: 1px solid transparent;
87
+ user-select: none;
88
+ color: rgba(191, 193, 201, 1);
89
+ }
90
+
91
+ .extended-info .audit-selector:hover {
92
+ border-bottom: 1px solid rgba(255, 255, 255);
93
+ cursor: pointer;
94
+ color: #fff;
95
+ }
96
+
97
+ .audit-selector svg {
98
+ width: 16px;
99
+ height: 16px;
100
+ display: inline;
101
+ }
102
+
103
+ .extended-info .audit-description {
104
+ color: rgba(191, 193, 201, 1);
105
+ }
106
+
107
+ .reset-button {
108
+ text-align: left;
109
+ border: none;
110
+ margin: 0;
111
+ width: auto;
112
+ overflow: visible;
113
+ background: transparent;
114
+ font: inherit;
115
+ line-height: normal;
116
+ -webkit-font-smoothing: inherit;
117
+ -moz-osx-font-smoothing: inherit;
118
+ -webkit-appearance: none;
119
+ padding: 0;
120
+ color: white;
121
+ }
122
+ </style>
123
+
124
+ <button id="astro-overlay-card">
125
+ <slot />
126
+ </button>
127
+ `;
128
+ }
129
+ connectedCallback() {
130
+ if (this.clickAction) {
131
+ this.shadowRoot.getElementById("astro-overlay-card")?.addEventListener("click", this.clickAction);
132
+ }
133
+ }
134
+ }
135
+ export {
136
+ DevToolbarAuditListItem
137
+ };