diff --git a/app/.prettierrc.js b/app/.prettierrc.js index 2a1f0b48a..2a60b0bdb 100644 --- a/app/.prettierrc.js +++ b/app/.prettierrc.js @@ -1,3 +1,4 @@ module.exports = { endOfLine: "auto", + trailingComma: "es5", }; diff --git a/docs/.prettierignore b/docs/.prettierignore index 2e03c0b08..5b489b610 100644 --- a/docs/.prettierignore +++ b/docs/.prettierignore @@ -3,3 +3,4 @@ build .docusaurus *.mustache hardware-metadata.json +src/hardware-metadata.d.ts diff --git a/docs/.prettierrc.js b/docs/.prettierrc.js index 2a1f0b48a..2a60b0bdb 100644 --- a/docs/.prettierrc.js +++ b/docs/.prettierrc.js @@ -1,3 +1,4 @@ module.exports = { endOfLine: "auto", + trailingComma: "es5", }; diff --git a/docs/eslint.config.mjs b/docs/eslint.config.mjs index d1bb7e50f..c44970759 100644 --- a/docs/eslint.config.mjs +++ b/docs/eslint.config.mjs @@ -1,16 +1,20 @@ import { defineConfig, globalIgnores } from "eslint/config"; import globals from "globals"; -import js from "@eslint/js"; +import jseslint from "@eslint/js"; +import tseslint from "typescript-eslint"; import pluginReact from "eslint-plugin-react"; import * as mdx from "eslint-plugin-mdx"; export default defineConfig([ - globalIgnores([".cache-loader/", ".docusaurus/", "build/"]), - { files: ["**/*.{js,jsx,md,mdx}"] }, - { - plugins: { js }, - extends: ["js/recommended"], - }, + globalIgnores([ + ".cache-loader/", + ".docusaurus/", + "build/", + "src/hardware-metadata.d.ts", + ]), + { files: ["**/*.{js,jsx,ts,tsx,md,mdx}"] }, + jseslint.configs.recommended, + tseslint.configs.strict, mdx.flat, pluginReact.configs.flat.recommended, pluginReact.configs.flat["jsx-runtime"], @@ -18,7 +22,10 @@ export default defineConfig([ languageOptions: { globals: { ...globals.browser, ...globals.commonjs, ...globals.node }, }, - rules: { "react/no-unescaped-entities": "off" }, + rules: { + "react/no-unescaped-entities": "off", + "@typescript-eslint/no-require-imports": "off", + }, settings: { react: { version: "detect", diff --git a/docs/package-lock.json b/docs/package-lock.json index 59f02c3a7..0b24eae7f 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -46,6 +46,7 @@ "prettier": "^3.4.2", "string-replace-loader": "^3.1.0", "typescript": "^5.7.2", + "typescript-eslint": "^8.30.1", "webpack": "^5.97.1" } }, @@ -5922,6 +5923,225 @@ "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", "license": "MIT" }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.30.1.tgz", + "integrity": "sha512-v+VWphxMjn+1t48/jO4t950D6KR8JaJuNXzi33Ve6P8sEmPr5k6CEXjdGwT6+LodVnEa91EQCtwjWNUCPweo+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.30.1", + "@typescript-eslint/type-utils": "8.30.1", + "@typescript-eslint/utils": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.30.1.tgz", + "integrity": "sha512-H+vqmWwT5xoNrXqWs/fesmssOW70gxFlgcMlYcBaWNPIEWDgLa4W9nkSPmhuOgLnXq9QYgkZ31fhDyLhleCsAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.30.1", + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/typescript-estree": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.30.1.tgz", + "integrity": "sha512-+C0B6ChFXZkuaNDl73FJxRYT0G7ufVPOSQkqkpM/U198wUwUFOtgo1k/QzFh1KjpBitaK7R1tgjVz6o9HmsRPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.30.1.tgz", + "integrity": "sha512-64uBF76bfQiJyHgZISC7vcNz3adqQKIccVoKubyQcOnNcdJBvYOILV1v22Qhsw3tw3VQu5ll8ND6hycgAR5fEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "8.30.1", + "@typescript-eslint/utils": "8.30.1", + "debug": "^4.3.4", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.30.1.tgz", + "integrity": "sha512-81KawPfkuulyWo5QdyG/LOKbspyyiW+p4vpn4bYO7DM/hZImlVnFwrpCTnmNMOt8CvLRr5ojI9nU1Ekpw4RcEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.30.1.tgz", + "integrity": "sha512-kQQnxymiUy9tTb1F2uep9W6aBiYODgq5EMSk6Nxh4Z+BDUoYUSa029ISs5zTzKBFnexQEh71KqwjKnRz58lusQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/visitor-keys": "8.30.1", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.30.1.tgz", + "integrity": "sha512-T/8q4R9En2tcEsWPQgB5BQ0XJVOtfARcUvOa8yJP3fh9M/mXraLxZrkCfGb6ChrO/V3W+Xbd04RacUEqk1CFEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.30.1", + "@typescript-eslint/types": "8.30.1", + "@typescript-eslint/typescript-estree": "8.30.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.30.1.tgz", + "integrity": "sha512-aEhgas7aJ6vZnNFC7K4/vMGDGyOiqWcYZPpIWrTKuTAlsvDNKy2GFDqh9smL+iq069ZvR0YzEeq0B8NJlLzjFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.30.1", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@ungap/structured-clone": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", @@ -11099,6 +11319,13 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, "node_modules/gray-matter": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", @@ -21084,6 +21311,19 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, "node_modules/ts-dedent": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", @@ -21265,6 +21505,29 @@ "node": ">=14.17" } }, + "node_modules/typescript-eslint": { + "version": "8.30.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.30.1.tgz", + "integrity": "sha512-D7lC0kcehVH7Mb26MRQi64LMyRJsj3dToJxM1+JVTl53DQSV5/7oUGWQLcKl1C1KnoVHxMMU2FNQMffr7F3Row==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.30.1", + "@typescript-eslint/parser": "8.30.1", + "@typescript-eslint/utils": "8.30.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, "node_modules/ufo": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.4.tgz", diff --git a/docs/package.json b/docs/package.json index 11ea5908d..590c3ab29 100644 --- a/docs/package.json +++ b/docs/package.json @@ -65,6 +65,7 @@ "prettier": "^3.4.2", "string-replace-loader": "^3.1.0", "typescript": "^5.7.2", + "typescript-eslint": "^8.30.1", "webpack": "^5.97.1" } } diff --git a/docs/src/components/columns.tsx b/docs/src/components/columns.tsx index f64bc9494..09172c2c5 100644 --- a/docs/src/components/columns.tsx +++ b/docs/src/components/columns.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode, CSSProperties } from "react"; +import { ReactNode, CSSProperties } from "react"; import clsx from "clsx"; interface ColumnsProps { diff --git a/docs/src/components/hardware-list.tsx b/docs/src/components/hardware-list.tsx index 86f9a9d05..b27c2356d 100644 --- a/docs/src/components/hardware-list.tsx +++ b/docs/src/components/hardware-list.tsx @@ -18,11 +18,11 @@ function itemHasMultiple(item: HardwareMetadata) { function itemIds(item: HardwareMetadata) { if (item.type == "board" || item.type == "shield") { - let nodes = (item.siblings ?? [item.id]) - .map((id) => {id}) - .reduce( + const nodes = (item.siblings ?? [item.id]) + .map((id) => {id}) + .reduce( (prev, curr, index) => [...prev, index > 0 ? ", " : "", curr], - [] as ElementOrString[] + [] ); return {nodes}; } else { @@ -81,7 +81,7 @@ function mapInterconnect({ } function HardwareList({ items }: HardwareListProps) { - let grouped = groupedMetadata(items); + const grouped = groupedMetadata(items); return ( <> diff --git a/docs/src/components/hardware-utils.ts b/docs/src/components/hardware-utils.ts index 76452dc35..f6454bfbd 100644 --- a/docs/src/components/hardware-utils.ts +++ b/docs/src/components/hardware-utils.ts @@ -21,7 +21,7 @@ function groupedBoard(agg: GroupedMetadata, board: Board) { agg.onboard.push(board); } else if (board.exposes) { board.exposes.forEach((element) => { - let ic = agg.interconnects[element] ?? { + const ic = agg.interconnects[element] ?? { boards: [], shields: [], }; @@ -37,12 +37,12 @@ function groupedBoard(agg: GroupedMetadata, board: Board) { function groupedShield(agg: GroupedMetadata, shield: Shield) { shield.requires.forEach((id) => { - let ic = agg.interconnects[id] ?? { boards: [], shields: [] }; + const ic = agg.interconnects[id] ?? { boards: [], shields: [] }; ic.shields.push(shield); agg.interconnects[id] = ic; }); shield.exposes?.forEach((id) => { - let ic = agg.interconnects[id] ?? { boards: [], shields: [] }; + const ic = agg.interconnects[id] ?? { boards: [], shields: [] }; ic.shields.push(shield); agg.interconnects[id] = ic; }); @@ -50,7 +50,7 @@ function groupedShield(agg: GroupedMetadata, shield: Shield) { } function groupedInterconnect(agg: GroupedMetadata, item: Interconnect) { - let ic = agg.interconnects[item.id] ?? { boards: [], shields: [] }; + const ic = agg.interconnects[item.id] ?? { boards: [], shields: [] }; ic.interconnect = item; agg.interconnects[item.id] = ic; diff --git a/docs/src/components/interconnect-tabs.tsx b/docs/src/components/interconnect-tabs.tsx index cfe764b3d..dac9777ef 100644 --- a/docs/src/components/interconnect-tabs.tsx +++ b/docs/src/components/interconnect-tabs.tsx @@ -2,16 +2,16 @@ import Tabs from "@theme/Tabs"; import TabItem from "@theme/TabItem"; import { HardwareMetadata, Interconnect } from "../hardware-metadata"; -import { groupedMetadata, InterconnectDetails } from "./hardware-utils"; +import { groupedMetadata } from "./hardware-utils"; interface InterconnectTabsProps { items: HardwareMetadata[]; - gpio: Boolean; + gpio: boolean; } -function mapInterconnect(interconnect: Interconnect, gpio: Boolean) { - let content = require(`@site/src/data/interconnects/${interconnect.id}/design_guideline.md`); - let imageUrl = require(`@site/docs/assets/interconnects/${interconnect.id}/pinout.png`); +function mapInterconnect(interconnect: Interconnect, gpio: boolean) { + const content = require(`@site/src/data/interconnects/${interconnect.id}/design_guideline.md`); + const imageUrl = require(`@site/docs/assets/interconnects/${interconnect.id}/pinout.png`); return ( @@ -53,7 +53,7 @@ function mapInterconnectValue(interconnect: Interconnect) { } function InterconnectTabs({ items, gpio }: InterconnectTabsProps) { - let grouped = Object.values(groupedMetadata(items).interconnects) + const grouped = Object.values(groupedMetadata(items).interconnects) .map((i) => i?.interconnect as Interconnect) .filter((i) => i?.design_guideline) .sort((a, b) => a.id.localeCompare(b.id)); diff --git a/docs/src/hardware-metadata-static-plugin/index.js b/docs/src/hardware-metadata-static-plugin/index.js index 7fb6ad83f..6d475bc0e 100644 --- a/docs/src/hardware-metadata-static-plugin/index.js +++ b/docs/src/hardware-metadata-static-plugin/index.js @@ -22,9 +22,9 @@ const METADATA_GLOB = path.posix.join(BASE_DIR, "app/boards/**/*.zmk.yml"); * @param {string} file */ async function readMetadata(file) { - /** @type HardwareMetadata[] */ - // @ts-ignore - const documents = yaml.loadAll(await fs.readFile(file, "utf-8")); + const documents = /** @type HardwareMetadata[] */ ( + yaml.loadAll(await fs.readFile(file, "utf-8")) + ); return documents.map( (metadata) => diff --git a/docs/src/keymap-upgrade/headers.ts b/docs/src/keymap-upgrade/headers.ts index 8aa1928fc..628ca19b2 100644 --- a/docs/src/keymap-upgrade/headers.ts +++ b/docs/src/keymap-upgrade/headers.ts @@ -1,4 +1,4 @@ -import type { SyntaxNode, Tree } from "web-tree-sitter"; +import type { Tree } from "web-tree-sitter"; import { Devicetree, findCapture } from "./parser"; import { getUpgradeEdits, MatchFunc, ReplaceFunc, TextEdit } from "./textedit"; diff --git a/docs/src/keymap-upgrade/keycodes.ts b/docs/src/keymap-upgrade/keycodes.ts index 5fc3e66ab..e1bc21009 100644 --- a/docs/src/keymap-upgrade/keycodes.ts +++ b/docs/src/keymap-upgrade/keycodes.ts @@ -142,7 +142,7 @@ function findBehaviorNodes(paramNode: SyntaxNode) { } // Walk forward from the behavior to collect all its parameters. - let nodes = [behavior]; + const nodes = [behavior]; let param = behavior.nextNamedSibling; while (param && param.type !== "reference") { nodes.push(param); diff --git a/docs/src/keymap-upgrade/parser.ts b/docs/src/keymap-upgrade/parser.ts index b496d1d5a..1fca0ae71 100644 --- a/docs/src/keymap-upgrade/parser.ts +++ b/docs/src/keymap-upgrade/parser.ts @@ -133,7 +133,9 @@ export function findDevicetreeProperty( `(property name: (identifier) @name (#eq? @name "${name}")) @prop` ); const matches = query.matches(node); - const props = matches.map(({ captures }) => findCapture("prop", captures)!); + const props = matches + .map(({ captures }) => findCapture("prop", captures)) + .filter((node) => node !== null); if (options?.recursive) { return props; diff --git a/docs/src/keymap-upgrade/textedit.ts b/docs/src/keymap-upgrade/textedit.ts index 263b2ab81..504accdf8 100644 --- a/docs/src/keymap-upgrade/textedit.ts +++ b/docs/src/keymap-upgrade/textedit.ts @@ -94,7 +94,7 @@ export function applyEdits(text: string, edits: TextEdit[]) { const chunks: TextChunk[] = []; let currentIndex = 0; - for (let edit of edits) { + for (const edit of edits) { if (edit.startIndex < currentIndex) { console.warn("discarding overlapping edit", edit); continue; diff --git a/docs/tsconfig.json b/docs/tsconfig.json index b8e14486b..8ecf75534 100644 --- a/docs/tsconfig.json +++ b/docs/tsconfig.json @@ -4,10 +4,12 @@ "compilerOptions": { "types": ["node", "@docusaurus/theme-classic"], "moduleResolution": "Bundler", + "module": "esnext", "esModuleInterop": true, "resolveJsonModule": true, "strict": true, "noEmit": true, + "jsx": "react-jsx", "target": "ES6", "lib": ["ES2022", "DOM", "DOM.Iterable"] }