diff --git a/inputfiles/addedTypes.jsonc b/inputfiles/addedTypes.jsonc index fbc48d8dd..62a55b192 100644 --- a/inputfiles/addedTypes.jsonc +++ b/inputfiles/addedTypes.jsonc @@ -373,98 +373,6 @@ // Full support: https://developer.mozilla.org/en-US/docs/Web/API/Storage_Access_API#browser_compatibility "storage-access" ] - }, - "AutoFillBase": { - "name": "AutoFillBase", - "value": [ - // Off - "off", - // Automatic - "on", - "" - ] - }, - "AutoFillAddressKind": { - "name": "AutoFillAddressKind", - "value": [ - "shipping", - "billing" - ] - }, - "AutoFillNormalField": { - "name": "AutoFillNormalField", - "value": [ - "name", - "honorific-prefix", - "given-name", - "additional-name", - "family-name", - "honorific-suffix", - - "username", - "new-password", - "current-password", - // Supported in iOS Safari too even though WPT tests - // for Safari currently fail as of 2023-06. - "one-time-code", - - "organization", - "street-address", - "address-line1", - "address-line2", - "address-line3", - "address-level4", - "address-level3", - "address-level2", - "address-level1", - "country", - "country-name", - "postal-code", - - "cc-name", - "cc-given-name", - "cc-family-name", - "cc-number", - "cc-exp", - "cc-exp-month", - "cc-exp-year", - "cc-csc", - "cc-type", - "transaction-currency", - "transaction-amount", - - "bday-day", - "bday-month", - "bday-year" - ] - }, - "AutoFillContactKind": { - "name": "AutoFillContactKind", - "value": [ - "home", - "work", - "mobile" - ] - }, - "AutoFillContactField": { - "name": "AutoFillContactField", - "value": [ - "tel", - "tel-country-code", - "tel-national", - "tel-area-code", - "tel-local", - "tel-local-prefix", - "tel-local-suffix", - "tel-extension", - "email" - ] - }, - "AutoFillCredentialField": { - "name": "AutoFillCredentialField", - "value": [ - "webauthn" - ] } } }, diff --git a/inputfiles/patches/autocomplete.kdl b/inputfiles/patches/autocomplete.kdl new file mode 100644 index 000000000..a73f49aac --- /dev/null +++ b/inputfiles/patches/autocomplete.kdl @@ -0,0 +1,74 @@ +enum AutoFillBase { + // Off + off + // Automatic + on + "" +} +enum AutoFillAddressKind { + shipping + billing +} +enum AutoFillNormalField { + name + honorific-prefix + given-name + additional-name + family-name + honorific-suffix + + username + new-password + current-password + // Supported in iOS Safari too even though WPT tests + // for Safari currently fail as of 2023-06. + one-time-code + + organization + street-address + address-line1 + address-line2 + address-line3 + address-level4 + address-level3 + address-level2 + address-level1 + country + country-name + postal-code + + cc-name + cc-given-name + cc-family-name + cc-number + cc-exp + cc-exp-month + cc-exp-year + cc-csc + cc-type + transaction-currency + transaction-amount + + bday-day + bday-month + bday-year +} +enum AutoFillContactKind { + home + work + mobile +} +enum AutoFillContactField { + tel + tel-country-code + tel-national + tel-area-code + tel-local + tel-local-prefix + tel-local-suffix + tel-extension + email +} +enum AutoFillCredentialField { + webauthn +} diff --git a/package-lock.json b/package-lock.json index 1df705e51..a841533f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,6 +29,7 @@ "eslint-plugin-prettier": "^5.1.3", "globals": "^16.0.0", "jsonc-parser": "^3.2.1", + "kdljs": "^0.3.0", "node-fetch": "^3.3.2", "prettier": "^3.2.5", "print-diff": "^2.0.0", @@ -46,6 +47,50 @@ "node": ">=0.10.0" } }, + "node_modules/@chevrotain/cst-dts-gen": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-11.0.3.tgz", + "integrity": "sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/gast": "11.0.3", + "@chevrotain/types": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "node_modules/@chevrotain/gast": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-11.0.3.tgz", + "integrity": "sha512-+qNfcoNk70PyS/uxmj3li5NiECO+2YKZZQMbmjTqRI3Qchu8Hig/Q9vgkHpI3alNjr7M+a2St5pw5w5F6NL5/Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/types": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "node_modules/@chevrotain/regexp-to-ast": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/regexp-to-ast/-/regexp-to-ast-11.0.3.tgz", + "integrity": "sha512-1fMHaBZxLFvWI067AVbGJav1eRY7N8DDvYCTwGBiE/ytKBgP8azTdgyrKyWZ9Mfh09eHWb5PgTSO8wi7U824RA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@chevrotain/types": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-11.0.3.tgz", + "integrity": "sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@chevrotain/utils": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-11.0.3.tgz", + "integrity": "sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", @@ -1199,6 +1244,21 @@ "node": ">=4" } }, + "node_modules/chevrotain": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-11.0.3.tgz", + "integrity": "sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/cst-dts-gen": "11.0.3", + "@chevrotain/gast": "11.0.3", + "@chevrotain/regexp-to-ast": "11.0.3", + "@chevrotain/types": "11.0.3", + "@chevrotain/utils": "11.0.3", + "lodash-es": "4.17.21" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -2707,6 +2767,16 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/kdljs": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/kdljs/-/kdljs-0.3.0.tgz", + "integrity": "sha512-tNb5vWkcouQKp8Q3CJ0BhA4ThcnWdVcY0Z+zertOelM8ywHVOabu3FZdDKldQj9j/CZt17eb5+kq/1Gg9GDtXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chevrotain": "^11.0.3" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -2750,6 +2820,13 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.find": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.find/-/lodash.find-4.6.0.tgz", diff --git a/package.json b/package.json index 80809a5c4..57b831ffc 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,7 @@ "eslint-plugin-prettier": "^5.1.3", "globals": "^16.0.0", "jsonc-parser": "^3.2.1", + "kdljs": "^0.3.0", "node-fetch": "^3.3.2", "prettier": "^3.2.5", "print-diff": "^2.0.0", diff --git a/src/build.ts b/src/build.ts index 27a2509f5..533d4cc26 100644 --- a/src/build.ts +++ b/src/build.ts @@ -14,6 +14,7 @@ import { getInterfaceToEventMap } from "./build/webref/events.js"; import { getWebidls } from "./build/webref/idl.js"; import jsonc from "jsonc-parser"; import { generateDescriptions } from "./build/mdn-comments.js"; +import readPatches from "./build/patches.js"; function mergeNamesakes(filtered: Browser.WebIdl) { const targets = [ @@ -93,6 +94,7 @@ async function emitDom() { const overriddenItems = await readInputJSON("overridingTypes.jsonc"); const addedItems = await readInputJSON("addedTypes.jsonc"); + const patches = await readPatches(); const comments = await readInputJSON("comments.json"); const deprecatedInfo = await readInputJSON("deprecatedMessage.json"); const documentationFromMDN = await generateDescriptions(); @@ -230,6 +232,7 @@ async function emitDom() { webidl = mergeApiDescriptions(webidl, documentationFromMDN); webidl = merge(webidl, addedItems); webidl = merge(webidl, overriddenItems); + webidl = merge(webidl, patches); webidl = merge(webidl, comments); webidl = mergeDeprecatedMessage(webidl, deprecatedInfo); for (const name in webidl.interfaces!.interface) { diff --git a/src/build/patches.ts b/src/build/patches.ts new file mode 100644 index 000000000..b2875f6b2 --- /dev/null +++ b/src/build/patches.ts @@ -0,0 +1,74 @@ +import { parse } from "kdljs"; +import type { Enum } from "./types"; +import { readdir, readFile } from "fs/promises"; +import { merge } from "./helpers.js"; + +/** + * Converts patch files in KDL to match the [types](types.d.ts). + */ +function parseKDL(kdlText: string) { + const { output, errors } = parse(kdlText); + + if (errors.length) { + throw new Error("KDL parse errors", { cause: errors }); + } + + const nodes = output!; + const enums: Record = {}; + + for (const node of nodes) { + if (node.name === "enum") { + handleEnum(node, enums); + } + } + + return { enums: { enum: enums } }; +} + +/** + * Handles an enum node by extracting its name and values. + * Throws an error if the enum name is missing or if the values are not in the correct format. + * @param node The enum node to handle. + * @param enums The record of enums to update. + */ +function handleEnum(node: any, enums: Record) { + const name = node.values[0]; + if (typeof name !== "string") { + throw new Error("Missing enum name"); + } + const values: string[] = []; + + for (const child of node.children ?? []) { + values.push(child.name); + } + + enums[name] = { name, value: values }; +} + +/** + * Collect all file URLs in a directory. + */ +async function getAllFileURLs(folder: URL): Promise { + const entries = await readdir(folder, { withFileTypes: true }); + return entries.map((entry) => new URL(entry.name, folder)); +} + +/** + * Read and parse a single KDL file. + */ +export async function readPatch(fileUrl: URL): Promise { + const text = await readFile(fileUrl, "utf8"); + return parseKDL(text); +} + +/** + * Read, parse, and merge all KDL files under the input folder. + */ +export default async function readPatches(): Promise { + const patchDirectory = new URL("../../inputfiles/patches/", import.meta.url); + const fileUrls = await getAllFileURLs(patchDirectory); + + const parsedContents = await Promise.all(fileUrls.map(readPatch)); + + return parsedContents.reduce((acc, current) => merge(acc, current), {}); +}