From 4c2eab4a87878d144780fbd6a9d19db993127ebe Mon Sep 17 00:00:00 2001 From: nojaf Date: Sat, 9 Nov 2024 16:04:44 +0100 Subject: [PATCH] Emit global items --- src/DOMAPI/Window.res | 16 +- src/Global.js | 2 + src/Global.res | 302 ++++++++++++++++++ .../src/build/emitter.ts | 108 ++++++- 4 files changed, 401 insertions(+), 27 deletions(-) create mode 100644 src/Global.js create mode 100644 src/Global.res diff --git a/src/DOMAPI/Window.res b/src/DOMAPI/Window.res index 6508cb2..3de9301 100644 --- a/src/DOMAPI/Window.res +++ b/src/DOMAPI/Window.res @@ -28,15 +28,13 @@ external atob: (window, string) => string = "atob" [Read more on MDN](https://developer.mozilla.org/docs/Web/API/Window/setTimeout) */ @send -external setTimeout: (window, ~handler: string, ~timeout: int=?, ~arguments: JSON.t) => int = - "setTimeout" +external setTimeout: (window, ~handler: string, ~timeout: int=?) => int = "setTimeout" /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/Window/setTimeout) */ @send -external setTimeout2: (window, ~handler: unit => unit, ~timeout: int=?, ~arguments: JSON.t) => int = - "setTimeout" +external setTimeout2: (window, ~handler: unit => unit, ~timeout: int=?) => int = "setTimeout" /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/Window/clearTimeout) @@ -48,19 +46,13 @@ external clearTimeout: (window, int) => unit = "clearTimeout" [Read more on MDN](https://developer.mozilla.org/docs/Web/API/Window/setInterval) */ @send -external setInterval: (window, ~handler: string, ~timeout: int=?, ~arguments: JSON.t) => int = - "setInterval" +external setInterval: (window, ~handler: string, ~timeout: int=?) => int = "setInterval" /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/Window/setInterval) */ @send -external setInterval2: ( - window, - ~handler: unit => unit, - ~timeout: int=?, - ~arguments: JSON.t, -) => int = "setInterval" +external setInterval2: (window, ~handler: unit => unit, ~timeout: int=?) => int = "setInterval" /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/Window/clearInterval) diff --git a/src/Global.js b/src/Global.js new file mode 100644 index 0000000..d856702 --- /dev/null +++ b/src/Global.js @@ -0,0 +1,2 @@ +// Generated by ReScript, PLEASE EDIT WITH CARE +/* This output is empty. Its source's type definitions, externals and/or unused code got optimized away. */ diff --git a/src/Global.res b/src/Global.res new file mode 100644 index 0000000..e49be0f --- /dev/null +++ b/src/Global.res @@ -0,0 +1,302 @@ +open DOMAPI +open HistoryAPI +open VisualViewportAPI +open WebSpeechAPI +open IndexedDBAPI +open WebCryptoAPI +open PerformanceAPI +open ServiceWorkerAPI +open WebStorageAPI +open CanvasAPI +open FileAPI +open ChannelMessagingAPI +open FetchAPI +open EventAPI + +external window: window = "window" +external self: window = "self" +external document: document = "document" +external name: string = "name" +external location: location = "location" +external history: history = "history" +external customElements: customElementRegistry = "customElements" +external locationbar: barProp = "locationbar" +external menubar: barProp = "menubar" +external personalbar: barProp = "personalbar" +external scrollbars: barProp = "scrollbars" +external statusbar: barProp = "statusbar" +external toolbar: barProp = "toolbar" +external closed: bool = "closed" +external frames: window = "frames" +external length: int = "length" +external top: window = "top" +external opener: JSON.t = "opener" +external parent: window = "parent" +external frameElement: element = "frameElement" +external navigator: navigator = "navigator" +external screen: screen = "screen" +external visualViewport: visualViewport = "visualViewport" +external innerWidth: int = "innerWidth" +external innerHeight: int = "innerHeight" +external scrollX: float = "scrollX" +external scrollY: float = "scrollY" +external screenX: int = "screenX" +external screenLeft: int = "screenLeft" +external screenY: int = "screenY" +external screenTop: int = "screenTop" +external outerWidth: int = "outerWidth" +external outerHeight: int = "outerHeight" +external devicePixelRatio: float = "devicePixelRatio" +external speechSynthesis: speechSynthesis = "speechSynthesis" +external origin: string = "origin" +external isSecureContext: bool = "isSecureContext" +external crossOriginIsolated: bool = "crossOriginIsolated" +external indexedDB: idbFactory = "indexedDB" +external crypto: crypto = "crypto" +external performance: performance = "performance" +external caches: cacheStorage = "caches" +external sessionStorage: storage = "sessionStorage" +external localStorage: storage = "localStorage" + +external reportError: JSON.t => unit = "reportError" + +external btoa: string => string = "btoa" + +external atob: string => string = "atob" + +external setTimeout: (~handler: string, ~timeout: int=?) => int = "setTimeout" + +external setTimeout2: (~handler: unit => unit, ~timeout: int=?) => int = "setTimeout" + +external clearTimeout: int => unit = "clearTimeout" + +external setInterval: (~handler: string, ~timeout: int=?) => int = "setInterval" + +external setInterval2: (~handler: unit => unit, ~timeout: int=?) => int = "setInterval" + +external clearInterval: int => unit = "clearInterval" + +external queueMicrotask: voidFunction => unit = "queueMicrotask" + +external createImageBitmap: ( + ~image: htmlImageElement, + ~options: imageBitmapOptions=?, +) => Promise.t = "createImageBitmap" + +external createImageBitmap2: ( + ~image: svgImageElement, + ~options: imageBitmapOptions=?, +) => Promise.t = "createImageBitmap" + +external createImageBitmap3: ( + ~image: htmlVideoElement, + ~options: imageBitmapOptions=?, +) => Promise.t = "createImageBitmap" + +external createImageBitmap4: ( + ~image: htmlCanvasElement, + ~options: imageBitmapOptions=?, +) => Promise.t = "createImageBitmap" + +external createImageBitmap5: ( + ~image: imageBitmap, + ~options: imageBitmapOptions=?, +) => Promise.t = "createImageBitmap" + +external createImageBitmap6: ( + ~image: offscreenCanvas, + ~options: imageBitmapOptions=?, +) => Promise.t = "createImageBitmap" + +external createImageBitmap7: ( + ~image: videoFrame, + ~options: imageBitmapOptions=?, +) => Promise.t = "createImageBitmap" + +external createImageBitmap8: ( + ~image: blob, + ~options: imageBitmapOptions=?, +) => Promise.t = "createImageBitmap" + +external createImageBitmap9: ( + ~image: imageData, + ~options: imageBitmapOptions=?, +) => Promise.t = "createImageBitmap" + +external createImageBitmap10: ( + ~image: htmlImageElement, + ~sx: int, + ~sy: int, + ~sw: int, + ~sh: int, + ~options: imageBitmapOptions=?, +) => Promise.t = "createImageBitmap" + +external createImageBitmap11: ( + ~image: svgImageElement, + ~sx: int, + ~sy: int, + ~sw: int, + ~sh: int, + ~options: imageBitmapOptions=?, +) => Promise.t = "createImageBitmap" + +external createImageBitmap12: ( + ~image: htmlVideoElement, + ~sx: int, + ~sy: int, + ~sw: int, + ~sh: int, + ~options: imageBitmapOptions=?, +) => Promise.t = "createImageBitmap" + +external createImageBitmap13: ( + ~image: htmlCanvasElement, + ~sx: int, + ~sy: int, + ~sw: int, + ~sh: int, + ~options: imageBitmapOptions=?, +) => Promise.t = "createImageBitmap" + +external createImageBitmap14: ( + ~image: imageBitmap, + ~sx: int, + ~sy: int, + ~sw: int, + ~sh: int, + ~options: imageBitmapOptions=?, +) => Promise.t = "createImageBitmap" + +external createImageBitmap15: ( + ~image: offscreenCanvas, + ~sx: int, + ~sy: int, + ~sw: int, + ~sh: int, + ~options: imageBitmapOptions=?, +) => Promise.t = "createImageBitmap" + +external createImageBitmap16: ( + ~image: videoFrame, + ~sx: int, + ~sy: int, + ~sw: int, + ~sh: int, + ~options: imageBitmapOptions=?, +) => Promise.t = "createImageBitmap" + +external createImageBitmap17: ( + ~image: blob, + ~sx: int, + ~sy: int, + ~sw: int, + ~sh: int, + ~options: imageBitmapOptions=?, +) => Promise.t = "createImageBitmap" + +external createImageBitmap18: ( + ~image: imageData, + ~sx: int, + ~sy: int, + ~sw: int, + ~sh: int, + ~options: imageBitmapOptions=?, +) => Promise.t = "createImageBitmap" + +external structuredClone: ('t, ~options: structuredSerializeOptions=?) => 't = "structuredClone" + +external fetch: (~input: request, ~init: requestInit=?) => Promise.t = "fetch" + +external fetch2: (~input: string, ~init: requestInit=?) => Promise.t = "fetch" + +external requestAnimationFrame: frameRequestCallback => int = "requestAnimationFrame" + +external cancelAnimationFrame: int => unit = "cancelAnimationFrame" + +external addEventListener: ( + ~type_: string, + ~callback: eventListener<'event>, + ~options: addEventListenerOptions=?, +) => unit = "addEventListener" + +external addEventListener2: ( + ~type_: string, + ~callback: eventListener<'event>, + ~options: bool=?, +) => unit = "addEventListener" + +external removeEventListener: ( + ~type_: string, + ~callback: eventListener<'event>, + ~options: eventListenerOptions=?, +) => unit = "removeEventListener" + +external removeEventListener2: ( + ~type_: string, + ~callback: eventListener<'event>, + ~options: bool=?, +) => unit = "removeEventListener" + +external dispatchEvent: event => bool = "dispatchEvent" + +external close: unit => unit = "close" + +external stop: unit => unit = "stop" + +external focus: unit => unit = "focus" + +external open_: (~url: string=?, ~target: string=?, ~features: string=?) => window = "open" + +external alert: unit => unit = "alert" + +external alert2: string => unit = "alert" + +external confirm: (~message: string=?) => bool = "confirm" + +external prompt: (~message: string=?, ~default: string=?) => string = "prompt" + +external print: unit => unit = "print" + +external postMessage: ( + ~message: JSON.t, + ~targetOrigin: string, + ~transfer: array>=?, +) => unit = "postMessage" + +external postMessage2: (~message: JSON.t, ~options: windowPostMessageOptions=?) => unit = + "postMessage" + +external matchMedia: string => mediaQueryList = "matchMedia" + +external moveTo: (~x: int, ~y: int) => unit = "moveTo" + +external moveBy: (~x: int, ~y: int) => unit = "moveBy" + +external resizeTo: (~width: int, ~height: int) => unit = "resizeTo" + +external resizeBy: (~x: int, ~y: int) => unit = "resizeBy" + +external scroll: (~options: scrollToOptions=?) => unit = "scroll" + +external scroll2: (~x: float, ~y: float) => unit = "scroll" + +external scrollTo: (~options: scrollToOptions=?) => unit = "scrollTo" + +external scrollTo2: (~x: float, ~y: float) => unit = "scrollTo" + +external scrollBy: (~options: scrollToOptions=?) => unit = "scrollBy" + +external scrollBy2: (~x: float, ~y: float) => unit = "scrollBy" + +external getComputedStyle: (~elt: element, ~pseudoElt: string=?) => cssStyleDeclaration = + "getComputedStyle" + +external requestIdleCallback: ( + ~callback: idleRequestCallback, + ~options: idleRequestOptions=?, +) => int = "requestIdleCallback" + +external cancelIdleCallback: int => unit = "cancelIdleCallback" + +external getSelection: unit => selection = "getSelection" diff --git a/tools/TypeScript-DOM-lib-generator/src/build/emitter.ts b/tools/TypeScript-DOM-lib-generator/src/build/emitter.ts index 54bedc5..2525d54 100644 --- a/tools/TypeScript-DOM-lib-generator/src/build/emitter.ts +++ b/tools/TypeScript-DOM-lib-generator/src/build/emitter.ts @@ -4,7 +4,6 @@ import { promises as fs } from "fs"; import { execSync } from "child_process"; import { fileURLToPath } from "url"; import * as path from "path"; -import { isConstructorDeclaration } from "typescript"; /// Decide which members of a function to emit enum EmitScope { @@ -575,6 +574,9 @@ export async function emitRescriptBindings(webidl: Browser.WebIdl) { case "any": return "JSON.t"; + case "EventListenerOrEventListenerObject": + return "eventListener<'event>"; + default: // TODO: some types are a inline variant type // Example: "IDBValidKey | IDBKeyRange" @@ -757,6 +759,21 @@ export async function emitRescriptBindings(webidl: Browser.WebIdl) { // TODO: some interface seems to have an "implements" property. // These properties and methods should also be included. + function collectAllProperties(i: Browser.Interface): Browser.Property[] { + const allProperties: Browser.Property[] = Object.values( + i.properties?.property ?? {}, + ); + + for (const mixinName of i.implements ?? []) { + const mixin = allMixins.find((m) => m.name === mixinName); + if (mixin) { + allProperties.push(...Object.values(mixin.properties?.property ?? {})); + } + } + + return allProperties; + } + function emitInterfaceRecord( options: interfaceSettings, i: Browser.Interface, @@ -795,16 +812,7 @@ export async function emitRescriptBindings(webidl: Browser.WebIdl) { } } - const allProperties: Browser.Property[] = Object.values( - i.properties?.property ?? {}, - ); - - for (const mixinName of i.implements ?? []) { - const mixin = allMixins.find((m) => m.name === mixinName); - if (mixin) { - allProperties.push(...Object.values(mixin.properties?.property ?? {})); - } - } + const allProperties: Browser.Property[] = collectAllProperties(i); for (const property of allProperties) { // I'm curious to know which properties are overwritten in the extended interface @@ -1066,6 +1074,7 @@ export async function emitRescriptBindings(webidl: Browser.WebIdl) { printer.endLine(); } + // TODO: consider dealing with variadic parameters function mapSignatureParameters( signature: Browser.Signature, join: string = ", ", @@ -1075,7 +1084,10 @@ export async function emitRescriptBindings(webidl: Browser.WebIdl) { (signature.typeParameters || []).map((tp) => tp.name), ); - const parameters = signature.param || []; + // Avoids the additional parameters for setTimeout and setInterval + const parameters = (signature.param || []).filter( + (p) => !(p.variadic && p.type === "any"), + ); return parameters.length === 0 ? "" @@ -1207,7 +1219,8 @@ export async function emitRescriptBindings(webidl: Browser.WebIdl) { ...extendedMethods, ...methodEntries, ]; - return allMethods; + + return allMethods.filter((m) => !m.deprecated && !isInvalidMethod(m)); } // We try and detect which open statements are required for the functions of a nested module. @@ -1268,8 +1281,6 @@ export async function emitRescriptBindings(webidl: Browser.WebIdl) { const methodEntries = extractMethodEntries(i); for (const method of methodEntries) { - if (isInvalidMethod(method)) continue; - for (const dedupedMethod of dedupeMethod(method)) { const signature = dedupedMethod.signature[0]; verifyTypesFromSignature( @@ -1528,6 +1539,71 @@ export async function emitRescriptBindings(webidl: Browser.WebIdl) { } } + async function emitGlobalModule() { + printer.reset(); + const opens = [ + "DOMAPI", + "HistoryAPI", + "VisualViewportAPI", + "WebSpeechAPI", + "IndexedDBAPI", + "WebCryptoAPI", + "PerformanceAPI", + "ServiceWorkerAPI", + "WebStorageAPI", + "CanvasAPI", + "FileAPI", + "ChannelMessagingAPI", + "FetchAPI", + "EventAPI", + ]; + for (const o of opens) { + printer.printLine(`open ${o}`); + } + printer.endLine(); + + const windowInterface = allInterfaces.find((i) => i.name === "Window"); + if (!windowInterface) throw new Error("Window interface not found"); + + const allProperties: Browser.Property[] = + collectAllProperties(windowInterface); + for (const property of allProperties) { + if (property.name.startsWith("on") || property.deprecated) { + continue; + } + + const name = reservedRescriptWords.includes(property.name) + ? `${property.name}_` + : property.name; + + printer.printLine( + `external ${name}: ${transformPropertyValue(windowInterface, property)} = "${property.name}"`, + ); + } + + printer.endLine(); + + const allMethods: MethodWithSource[] = + extractMethodEntries(windowInterface); + + for (const method of allMethods) { + const dedupedMethods = dedupeMethod(method); + for (const [idx, dedupedMethod] of dedupedMethods.entries()) { + // We don't use emitMethod because do not want to include the @send annotation + const suffix = idx > 0 ? (idx + 1).toString() : ""; + const signature = dedupedMethod.signature[0]; + let ps = mapSignatureParameters(dedupedMethod.signature[0]); + printer.printLine( + `external ${mapMethodName(method, suffix)}: (${ps}) => ${mapMethodReturnType(signature)} = "${method.name}"`, + ); + printer.endLine(); + } + } + + const contents = printer.getResult(); + await fs.writeFile(path.join(outputFolder, `Global.res`), contents); + } + async function emit() { printer.reset(); // printer.printLine('@@warning("-30")'); @@ -2778,6 +2854,8 @@ export async function emitRescriptBindings(webidl: Browser.WebIdl) { } } + await emitGlobalModule(); + execSync("npx rescript format -all", { cwd: repoRoot, stdio: "inherit" }); execSync("npx rewatch", { cwd: repoRoot, stdio: "inherit" });