accented 0.0.1-dev.1 → 0.0.1-dev.3

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.
@@ -0,0 +1,8 @@
1
+ type AccentedProps = {
2
+ outputToConsole: boolean;
3
+ initialDelay: number;
4
+ throttleDelay: number;
5
+ };
6
+ export default function accented(props?: Partial<AccentedProps>): void;
7
+ export {};
8
+ //# sourceMappingURL=accented.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accented.d.ts","sourceRoot":"","sources":["../src/accented.ts"],"names":[],"mappings":"AAKA,KAAK,aAAa,GAAG;IACnB,eAAe,EAAE,OAAO,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAA;CACtB,CAAC;AAQF,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,KAAK,GAAE,OAAO,CAAC,aAAa,CAAM,QA6ClE"}
@@ -0,0 +1,46 @@
1
+ import axe from 'axe-core';
2
+ import TaskQueue from './task-queue';
3
+ import DomUpdater from './dom-updater';
4
+ import issuesToElements from './utils/issuesToElements';
5
+ const defaultProps = {
6
+ outputToConsole: true,
7
+ initialDelay: 0,
8
+ throttleDelay: 1000
9
+ };
10
+ export default function accented(props = {}) {
11
+ const { outputToConsole, initialDelay, throttleDelay } = { ...defaultProps, ...props };
12
+ if (typeof window === 'undefined' || typeof document === 'undefined') {
13
+ console.warn('Accented: this script can only run in the browser, and it’s likely running on the server now. Exiting.');
14
+ console.trace();
15
+ return;
16
+ }
17
+ const domUpdater = new DomUpdater();
18
+ const taskQueue = new TaskQueue(async () => {
19
+ performance.mark('axe-start');
20
+ const result = await axe.run();
21
+ const axeMeasure = performance.measure('axe', 'axe-start');
22
+ const elements = issuesToElements(result.violations);
23
+ domUpdater.update(elements);
24
+ if (outputToConsole) {
25
+ console.log('Result:', result);
26
+ }
27
+ console.log('Axe run duration:', Math.round(axeMeasure.duration), 'ms');
28
+ }, { initialDelay, throttleDelay });
29
+ taskQueue.add(document);
30
+ const mutationObserver = new MutationObserver(mutationList => {
31
+ // TODO: filter out irrelevant mutations
32
+ for (const mutationRecord of mutationList) {
33
+ if (mutationRecord.type === 'attributes' && mutationRecord.attributeName === 'data-accented') {
34
+ continue;
35
+ }
36
+ taskQueue.add(mutationRecord.target);
37
+ }
38
+ });
39
+ // TODO: read more about the params and decide which ones we need.
40
+ mutationObserver.observe(document, {
41
+ subtree: true,
42
+ childList: true,
43
+ attributes: true
44
+ });
45
+ }
46
+ //# sourceMappingURL=accented.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accented.js","sourceRoot":"","sources":["../src/accented.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,UAAU,CAAC;AAC3B,OAAO,SAAS,MAAM,cAAc,CAAC;AACrC,OAAO,UAAU,MAAM,eAAe,CAAC;AACvC,OAAO,gBAAgB,MAAM,0BAA0B,CAAC;AAQxD,MAAM,YAAY,GAAkB;IAClC,eAAe,EAAE,IAAI;IACrB,YAAY,EAAE,CAAC;IACf,aAAa,EAAE,IAAI;CACpB,CAAC;AAEF,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,QAAgC,EAAE;IACjE,MAAM,EAAC,eAAe,EAAE,YAAY,EAAE,aAAa,EAAC,GAAG,EAAC,GAAG,YAAY,EAAE,GAAG,KAAK,EAAC,CAAC;IAEnF,IAAI,OAAO,MAAM,KAAK,WAAW,IAAI,OAAO,QAAQ,KAAK,WAAW,EAAE,CAAC;QACrE,OAAO,CAAC,IAAI,CAAC,wGAAwG,CAAC,CAAC;QACvH,OAAO,CAAC,KAAK,EAAE,CAAC;QAChB,OAAO;IACT,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,UAAU,EAAE,CAAC;IAEpC,MAAM,SAAS,GAAG,IAAI,SAAS,CAAO,KAAK,IAAI,EAAE;QAC/C,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAE9B,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,GAAG,EAAE,CAAC;QAE/B,MAAM,UAAU,GAAG,WAAW,CAAC,OAAO,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;QAE3D,MAAM,QAAQ,GAAG,gBAAgB,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QACrD,UAAU,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAE5B,IAAI,eAAe,EAAE,CAAC;YACpB,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QACjC,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,mBAAmB,EAAE,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC,CAAC;IAC1E,CAAC,EAAE,EAAE,YAAY,EAAE,aAAa,EAAE,CAAC,CAAC;IAEpC,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAExB,MAAM,gBAAgB,GAAG,IAAI,gBAAgB,CAAC,YAAY,CAAC,EAAE;QAC3D,wCAAwC;QACxC,KAAK,MAAM,cAAc,IAAI,YAAY,EAAE,CAAC;YAC1C,IAAI,cAAc,CAAC,IAAI,KAAK,YAAY,IAAI,cAAc,CAAC,aAAa,KAAK,eAAe,EAAE,CAAC;gBAC7F,SAAS;YACX,CAAC;YACD,SAAS,CAAC,GAAG,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;QACvC,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,kEAAkE;IAClE,gBAAgB,CAAC,OAAO,CAAC,QAAQ,EAAE;QACjC,OAAO,EAAE,IAAI;QACb,SAAS,EAAE,IAAI;QACf,UAAU,EAAE,IAAI;KACjB,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,7 @@
1
+ export default class DomUpdater {
2
+ #private;
3
+ elements: Array<Element>;
4
+ constructor();
5
+ update(newElements: Array<Element>): void;
6
+ }
7
+ //# sourceMappingURL=dom-updater.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dom-updater.d.ts","sourceRoot":"","sources":["../src/dom-updater.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,OAAO,OAAO,UAAU;;IAC7B,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,CAAM;;IAM9B,MAAM,CAAC,WAAW,EAAE,KAAK,CAAC,OAAO,CAAC;CAsBnC"}
@@ -0,0 +1,29 @@
1
+ const attrName = 'data-accented';
2
+ export default class DomUpdater {
3
+ elements = [];
4
+ constructor() {
5
+ this.#addStylesheetToDocument();
6
+ }
7
+ update(newElements) {
8
+ for (const element of this.elements) {
9
+ element.removeAttribute(attrName);
10
+ }
11
+ this.elements = [...newElements];
12
+ for (const element of this.elements) {
13
+ element.setAttribute(attrName, '');
14
+ }
15
+ }
16
+ #addStylesheetToDocument() {
17
+ // TODO: is this the preferred way of adding a stylesheet?
18
+ const styleElement = document.createElement('style');
19
+ // FIX: looks like a bad idea, the stylesheet gets multiple <br> tags
20
+ styleElement.innerText = `
21
+ [${attrName}]:not(:focus-visible) {
22
+ outline: 2px solid red !important;
23
+ outline-offset: -2px;
24
+ }
25
+ `;
26
+ document.head.appendChild(styleElement);
27
+ }
28
+ }
29
+ //# sourceMappingURL=dom-updater.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dom-updater.js","sourceRoot":"","sources":["../src/dom-updater.ts"],"names":[],"mappings":"AAAA,MAAM,QAAQ,GAAG,eAAe,CAAC;AAEjC,MAAM,CAAC,OAAO,OAAO,UAAU;IAC7B,QAAQ,GAAmB,EAAE,CAAC;IAE9B;QACE,IAAI,CAAC,wBAAwB,EAAE,CAAC;IAClC,CAAC;IAED,MAAM,CAAC,WAA2B;QAChC,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACpC,OAAO,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;QACpC,CAAC;QACD,IAAI,CAAC,QAAQ,GAAG,CAAC,GAAG,WAAW,CAAC,CAAC;QACjC,KAAK,MAAM,OAAO,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YACpC,OAAO,CAAC,YAAY,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;IAED,wBAAwB;QACtB,0DAA0D;QAC1D,MAAM,YAAY,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QACrD,qEAAqE;QACrE,YAAY,CAAC,SAAS,GAAG;SACpB,QAAQ;;;;KAIZ,CAAC;QACF,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC;IAC1C,CAAC;CACF"}
@@ -0,0 +1,11 @@
1
+ type TaskCallback = () => void;
2
+ export default class TaskQueue<T> {
3
+ #private;
4
+ constructor(asyncCallback: TaskCallback, { initialDelay, throttleDelay }?: {
5
+ initialDelay?: number;
6
+ throttleDelay?: number;
7
+ });
8
+ add(item: T): void;
9
+ }
10
+ export {};
11
+ //# sourceMappingURL=task-queue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-queue.d.ts","sourceRoot":"","sources":["../src/task-queue.ts"],"names":[],"mappings":"AAAA,KAAK,YAAY,GAAG,MAAM,IAAI,CAAC;AAE/B,MAAM,CAAC,OAAO,OAAO,SAAS,CAAC,CAAC;;gBAalB,aAAa,EAAE,YAAY,EAAE,EAAC,YAAY,EAAE,aAAa,EAAC,GAAE;QAAC,YAAY,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAM;IA+B5H,GAAG,CAAC,IAAI,EAAE,CAAC;CAIZ"}
@@ -0,0 +1,40 @@
1
+ export default class TaskQueue {
2
+ #items = new Set();
3
+ #runningOrScheduled = false;
4
+ #initialDelay;
5
+ #throttleDelay;
6
+ // I'm not sure why the editor needs NodeJS.Timeout here.
7
+ // tsconfig.json doesn't mention that the code needs to run in Node.
8
+ // Maybe it's somehow related to the fact that we're using the Node test runner.
9
+ #timeoutId = null;
10
+ #asyncCallback = null;
11
+ constructor(asyncCallback, { initialDelay, throttleDelay } = {}) {
12
+ this.#asyncCallback = asyncCallback;
13
+ this.#initialDelay = initialDelay ?? 0;
14
+ this.#throttleDelay = throttleDelay ?? 1000;
15
+ }
16
+ #scheduleRun() {
17
+ if (this.#items.size === 0) {
18
+ this.#runningOrScheduled = false;
19
+ }
20
+ if (this.#timeoutId !== null || this.#items.size === 0) {
21
+ return;
22
+ }
23
+ const delay = this.#runningOrScheduled ? this.#throttleDelay : this.#initialDelay;
24
+ this.#runningOrScheduled = true;
25
+ this.#timeoutId = setTimeout(() => this.#run(), delay);
26
+ }
27
+ async #run() {
28
+ this.#items.clear();
29
+ if (this.#asyncCallback) {
30
+ await this.#asyncCallback();
31
+ }
32
+ this.#timeoutId = null;
33
+ this.#scheduleRun();
34
+ }
35
+ add(item) {
36
+ this.#items.add(item);
37
+ this.#scheduleRun();
38
+ }
39
+ }
40
+ //# sourceMappingURL=task-queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-queue.js","sourceRoot":"","sources":["../src/task-queue.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,OAAO,OAAO,SAAS;IAC5B,MAAM,GAAG,IAAI,GAAG,EAAK,CAAC;IACtB,mBAAmB,GAAG,KAAK,CAAC;IAC5B,aAAa,CAAS;IACtB,cAAc,CAAS;IAEvB,yDAAyD;IACzD,oEAAoE;IACpE,gFAAgF;IAChF,UAAU,GAAmC,IAAI,CAAC;IAElD,cAAc,GAAwB,IAAI,CAAC;IAE3C,YAAY,aAA2B,EAAE,EAAC,YAAY,EAAE,aAAa,KAAqD,EAAE;QAC1H,IAAI,CAAC,cAAc,GAAG,aAAa,CAAC;QACpC,IAAI,CAAC,aAAa,GAAG,YAAY,IAAI,CAAC,CAAC;QACvC,IAAI,CAAC,cAAc,GAAG,aAAa,IAAI,IAAI,CAAC;IAC9C,CAAC;IAED,YAAY;QACV,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YAC3B,IAAI,CAAC,mBAAmB,GAAG,KAAK,CAAC;QACnC,CAAC;QACD,IAAI,IAAI,CAAC,UAAU,KAAK,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YACvD,OAAO;QACT,CAAC;QAED,MAAM,KAAK,GAAG,IAAI,CAAC,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC;QAClF,IAAI,CAAC,mBAAmB,GAAG,IAAI,CAAC;QAChC,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,KAAK,CAAC,CAAC;IACzD,CAAC;IAED,KAAK,CAAC,IAAI;QACR,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QAEpB,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACxB,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;QAC9B,CAAC;QAED,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;QAEvB,IAAI,CAAC,YAAY,EAAE,CAAC;IACtB,CAAC;IAED,GAAG,CAAC,IAAO;QACT,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACtB,IAAI,CAAC,YAAY,EAAE,CAAC;IACtB,CAAC;CACF"}
@@ -0,0 +1,3 @@
1
+ import type { AxeResults } from 'axe-core';
2
+ export default function issuesToElements(issues: typeof AxeResults.violations): Element[];
3
+ //# sourceMappingURL=issuesToElements.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"issuesToElements.d.ts","sourceRoot":"","sources":["../../src/utils/issuesToElements.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAE3C,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,MAAM,EAAE,OAAO,UAAU,CAAC,UAAU,aAgB5E"}
@@ -0,0 +1,16 @@
1
+ export default function issuesToElements(issues) {
2
+ const elements = new Set();
3
+ for (const issue of issues) {
4
+ for (const node of issue.nodes) {
5
+ // TODO: how to make this easier / more reliable?
6
+ if (typeof node.target[0] === 'string') {
7
+ const element = document.querySelector(node.target[0]);
8
+ if (element) {
9
+ elements.add(element);
10
+ }
11
+ }
12
+ }
13
+ }
14
+ return [...elements];
15
+ }
16
+ //# sourceMappingURL=issuesToElements.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"issuesToElements.js","sourceRoot":"","sources":["../../src/utils/issuesToElements.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,MAAoC;IAC3E,MAAM,QAAQ,GAAiB,IAAI,GAAG,EAAE,CAAC;IAEzC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YAC/B,iDAAiD;YACjD,IAAI,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,QAAQ,EAAE,CAAC;gBACvC,MAAM,OAAO,GAAG,QAAQ,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;gBACvD,IAAI,OAAO,EAAE,CAAC;oBACZ,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;gBACxB,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,CAAC,GAAG,QAAQ,CAAC,CAAC;AACvB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare const whatever = 1;
2
+ //# sourceMappingURL=whatever.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"whatever.d.ts","sourceRoot":"","sources":["../src/whatever.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,QAAQ,IAAI,CAAC"}
@@ -0,0 +1,2 @@
1
+ export const whatever = 1;
2
+ //# sourceMappingURL=whatever.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"whatever.js","sourceRoot":"","sources":["../src/whatever.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "accented",
3
- "version": "0.0.1-dev.1",
3
+ "version": "0.0.1-dev.3",
4
4
  "description": "Continuous accessibility testing and issue highlighting for web development",
5
- "main": "src/accented.js",
5
+ "type": "module",
6
+ "main": "dist/accented.js",
6
7
  "files": [
7
- "src"
8
+ "dist"
8
9
  ],
9
10
  "repository": {
10
11
  "type": "git",
@@ -26,6 +27,8 @@
26
27
  "axe-core": "^4.10.2"
27
28
  },
28
29
  "scripts": {
29
- "test": "echo \"Error: no test specified\" && exit 1"
30
+ "build": "tsc",
31
+ "watch": "tsc --watch",
32
+ "test": "node --test --import tsx ./**/*.test.ts"
30
33
  }
31
34
  }
package/src/accented.js DELETED
@@ -1,31 +0,0 @@
1
- import axe from 'axe-core';
2
- import TaskQueue from './task-queue.js';
3
-
4
- export default function accented() {
5
- const taskQueue = new TaskQueue(async () => {
6
- performance.mark('axe-start');
7
-
8
- const result = await axe.run();
9
-
10
- const axeMeasure = performance.measure('axe', 'axe-start');
11
-
12
- console.log('Result:', result);
13
- console.log('Axe run duration:', Math.round(axeMeasure.duration), 'ms');
14
- });
15
-
16
- taskQueue.add(document);
17
-
18
- const mutationObserver = new MutationObserver(mutationList => {
19
- // TODO: filter out irrelevant mutations
20
- for (const mutationRecord of mutationList) {
21
- taskQueue.add(mutationRecord.target);
22
- }
23
- });
24
-
25
- // TODO: read more about the params and decide which ones we need.
26
- mutationObserver.observe(document, {
27
- subtree: true,
28
- childList: true,
29
- attributes: true
30
- });
31
- }
package/src/task-queue.js DELETED
@@ -1,37 +0,0 @@
1
- // TODO: add generic typing
2
- export default class TaskQueue {
3
- items = new Set();
4
-
5
- idleCallbackId = null;
6
-
7
- asyncCallback = null;
8
-
9
- constructor(asyncCallback) {
10
- this.asyncCallback = asyncCallback;
11
- }
12
-
13
- // TODO: test all the asynchronicity
14
-
15
- #scheduleRun() {
16
- if (this.idleCallbackId !== null || this.items.size === 0) {
17
- return;
18
- }
19
-
20
- this.idleCallbackId = requestIdleCallback(() => this.#run());
21
- }
22
-
23
- async #run() {
24
- this.items.clear();
25
-
26
- await this.asyncCallback();
27
-
28
- this.idleCallbackId = null;
29
-
30
- this.#scheduleRun();
31
- }
32
-
33
- add(item) {
34
- this.items.add(item);
35
- this.#scheduleRun();
36
- }
37
- }