<commit_before>﻿module TS

open System
open System.Text.RegularExpressions
open Shared
open System.IO

// Global print target
let Pt = StringPrinter()

// When dump webworker types the dom types are ignored
let mutable ignoreDOMTypes = false

// Extended types used but not defined in the spec
let extendedTypes = 
    ["ArrayBuffer";"ArrayBufferView";"Int8Array";"Uint8Array";"Int16Array";"Uint16Array";"Int32Array";"Uint32Array";"Float32Array";"Float64Array"]

/// Get typescript type using object dom type, object name, and it's associated interface name
let rec DomTypeToTsType (objDomType: string) = 
    match objDomType.Trim('?') with
    | "AbortMode" -> "String"
    | "any" -> "any"
    | "bool" | "boolean" | "Boolean" -> "boolean"
    | "CanvasPixelArray" -> "number[]"
    | "Date" -> "Date"
    | "DOMHighResTimeStamp" -> "number"
    | "DOMString" -> "string"
    | "DOMTimeStamp" -> "number"
    | "EndOfStreamError" -> "number"
    | "EventListener" -> "EventListenerOrEventListenerObject"
    | "double" | "float" -> "number"
    | "Function" -> "Function"
    | "long" | "long long" | "signed long" | "signed long long" | "unsigned long" | "unsigned long long" -> "number"
    | "object" -> "any"
    | "Promise" -> "Promise"
    | "ReadyState" -> "number"
    | "sequence" -> "Array"
    | "short" | "signed short" | "unsigned short" -> "number"
    | "UnrestrictedDouble" -> "number"
    | "void" -> "void"
    | extendedType when List.contains extendedType extendedTypes -> extendedType
    | _ -> 
        if ignoreDOMTypes && Seq.contains objDomType ["Element"; "Window"; "Document"] then "any"
        else
            // Name of an interface / enum / dict. Just return itself
            if allInterfacesMap.ContainsKey objDomType ||
                allCallbackFuncs.ContainsKey objDomType ||
                allDictionariesMap.ContainsKey objDomType then
                objDomType
            elif allEnumsMap.ContainsKey objDomType then "string"
            // Deal with union type
            elif (objDomType.Contains(" or ")) then
                let allTypes = objDomType.Trim('(', ')').Split([|" or "|], StringSplitOptions.None)
                                |> Array.map DomTypeToTsType
                if Seq.contains "any" allTypes then "any" else String.concat " | " allTypes
            else 
                // Check if is array type, which looks like "sequence<DOMString>"
                let genericMatch = Regex.Match(objDomType, @"^(\w+)<(\w+)>$")
                if genericMatch.Success then
                    let tName = DomTypeToTsType (genericMatch.Groups.[1].Value)
                    match tName with
                    | "Promise" -> "any"
                    | _ ->
                        let paramName = DomTypeToTsType (genericMatch.Groups.[2].Value)
                        if tName = "Array" then paramName + "[]"
                        else tName + "<" + paramName + ">"
                elif objDomType.EndsWith("[]") then
                    let elementType = objDomType.Replace("[]", "").Trim() |> DomTypeToTsType
                    elementType + "[]"
                else "any"

let DumpConstants (i: Browser.Interface) =
    let dumpConstant (c: Browser.Constant) = Pt.printl "%s: %s;" c.Name (DomTypeToTsType c.Type)
    if i.Constants.IsSome then 
        Array.iter dumpConstant i.Constants.Value.Constants

/// Dump overloads for the createElement method
let DumpCreateElementOverloads (m: Browser.Method) =
    if  not (OptionCheckValue "createElement" m.Name) || 
        (DomTypeToTsType m.Type) <> "Element" || 
        m.Params.Length <> 1 ||
        (DomTypeToTsType m.Params.[0].Type) <> "string" then
        raise (Exception "createElement method signature does not match expected.")
    else
        for e in tagNameToEleName do
            if iNameToIDependList.ContainsKey e.Value && Seq.contains "HTMLElement" iNameToIDependList.[e.Value] then
                Pt.printl "createElement(tagName: \"%s\"): %s;" e.Key e.Value
        Pt.printl "createElement(tagName: string): HTMLElement;"

/// Dump overloads for the getElementsByTagName method
let DumpGetElementsByTagNameOverloads (m: Browser.Method) =
    if  not (OptionCheckValue "getElementsByTagName" m.Name) || 
        (DomTypeToTsType m.Type) <> "NodeList" || 
        m.Params.Length <> 1 ||
        (DomTypeToTsType m.Params.[0].Type) <> "string" then
        raise (Exception "getElementsByTagName method signature does not match expected.")
    else
        for e in tagNameToEleName do
            Pt.printl "getElementsByTagName(%s: \"%s\"): NodeListOf<%s>;" m.Params.[0].Name (e.Key.ToLower()) e.Value
        Pt.printl "getElementsByTagName(%s: string): NodeListOf<Element>;" m.Params.[0].Name

/// Dump overloads for the createEvent method
let DumpCreateEventOverloads (m: Browser.Method) =
    if  not (OptionCheckValue "createEvent" m.Name) || 
        (DomTypeToTsType m.Type) <> "Event" || 
        m.Params.Length <> 1 ||
        (DomTypeToTsType m.Params.[0].Type) <> "string" then
        raise (Exception "createEvent method signature does not match expected.")
    else
        // Dump plurals. For example, "Events", "MutationEvents"
        let hasPlurals = ["Event"; "MutationEvent"; "MouseEvent"; "SVGZoomEvent"; "UIEvent"]
        for x in distinctETypeList do
            if List.contains x hasPlurals then
                Pt.printl "createEvent(eventInterface:\"%s\"): %s;" x x
                Pt.printl "createEvent(eventInterface:\"%ss\"): %s;" x x
            else
                Pt.printl "createEvent(eventInterface:\"%s\"): %s;" x x
        Pt.printl "createEvent(eventInterface: string): Event;"

/// Generate the parameters string for function signatures 
let ParamsToString (ps: Param list) =
    let paramToString (p: Param) =
        (if p.Variadic then "..." else "") + 
        (AdjustParamName p.Name) + 
        (if not p.Variadic && p.Optional then "?: " else ": ") +
        (DomTypeToTsType p.Type) +
        (if p.Variadic then "[]" else "")
    String.Join(", ", (List.map paramToString ps))

let DumpMethod flavor prefix (i:Browser.Interface) (m:Browser.Method)  = 
    // print comment
    if m.Name.IsSome then
        match GetCommentForMethod i.Name m.Name.Value with
        | Some comment -> Pt.printl "%s" comment
        | _ -> ()

    // Find if there are overriding signatures in the external json file<commit_msg>CR feedback<commit_after>﻿module TS

open System
open System.Text.RegularExpressions
open Shared
open System.IO

// Global print target
let Pt = StringPrinter()

// When dump webworker types the dom types are ignored
let mutable ignoreDOMTypes = false

// Extended types used but not defined in the spec
let extendedTypes = 
    ["ArrayBuffer";"ArrayBufferView";"Int8Array";"Uint8Array";"Int16Array";"Uint16Array";"Int32Array";"Uint32Array";"Float32Array";"Float64Array"]

/// Get typescript type using object dom type, object name, and it's associated interface name
let rec DomTypeToTsType (objDomType: string) = 
    match objDomType.Trim('?') with
    | "AbortMode" -> "String"
    | "any" -> "any"
    | "bool" | "boolean" | "Boolean" -> "boolean"
    | "CanvasPixelArray" -> "number[]"
    | "Date" -> "Date"
    | "DOMHighResTimeStamp" -> "number"
    | "DOMString" -> "string"
    | "DOMTimeStamp" -> "number"
    | "EndOfStreamError" -> "number"
    | "EventListener" -> "EventListenerOrEventListenerObject"
    | "double" | "float" -> "number"
    | "Function" -> "Function"
    | "long" | "long long" | "signed long" | "signed long long" | "unsigned long" | "unsigned long long" -> "number"
    | "object" -> "any"
    | "Promise" -> "Promise"
    | "ReadyState" -> "number"
    | "sequence" -> "Array"
    | "short" | "signed short" | "unsigned short" -> "number"
    | "UnrestrictedDouble" -> "number"
    | "void" -> "void"
    | extendedType when List.contains extendedType extendedTypes -> extendedType
    | _ -> 
        if ignoreDOMTypes && Seq.contains objDomType ["Element"; "Window"; "Document"] then "any"
        else
            // Name of an interface / enum / dict. Just return itself
            if allInterfacesMap.ContainsKey objDomType ||
                allCallbackFuncs.ContainsKey objDomType ||
                allDictionariesMap.ContainsKey objDomType then
                objDomType
            elif allEnumsMap.ContainsKey objDomType then "string"
            // Deal with union type
            elif (objDomType.Contains(" or ")) then
                let allTypes = objDomType.Trim('(', ')').Split([|" or "|], StringSplitOptions.None)
                                |> Array.map DomTypeToTsType
                if Seq.contains "any" allTypes then "any" else String.concat " | " allTypes
            else 
                // Check if is array type, which looks like "sequence<DOMString>"
                let genericMatch = Regex.Match(objDomType, @"^(\w+)<(\w+)>$")
                if genericMatch.Success then
                    let tName = DomTypeToTsType (genericMatch.Groups.[1].Value)
                    match tName with
                    | "Promise" -> "any"
                    | _ ->
                        let paramName = DomTypeToTsType (genericMatch.Groups.[2].Value)
                        if tName = "Array" then paramName + "[]"
                        else tName + "<" + paramName + ">"
                elif objDomType.EndsWith("[]") then
                    let elementType = objDomType.Replace("[]", "").Trim() |> DomTypeToTsType
                    elementType + "[]"
                else "any"

let DumpConstants (i: Browser.Interface) =
    let dumpConstant (c: Browser.Constant) = Pt.printl "%s: %s;" c.Name (DomTypeToTsType c.Type)
    if i.Constants.IsSome then 
        Array.iter dumpConstant i.Constants.Value.Constants

/// Dump overloads for the createElement method
let DumpCreateElementOverloads (m: Browser.Method) =
    if  not (OptionCheckValue "createElement" m.Name) || 
        (DomTypeToTsType m.Type) <> "Element" || 
        m.Params.Length <> 1 ||
        (DomTypeToTsType m.Params.[0].Type) <> "string" then
        raise (Exception "createElement method signature does not match expected.")
    else
        for e in tagNameToEleName do
            if iNameToIDependList.ContainsKey e.Value && Seq.contains "HTMLElement" iNameToIDependList.[e.Value] then
                Pt.printl "createElement(tagName: \"%s\"): %s;" e.Key e.Value
        Pt.printl "createElement(tagName: string): HTMLElement;"

/// Dump overloads for the getElementsByTagName method
let DumpGetElementsByTagNameOverloads (m: Browser.Method) =
    if  not (OptionCheckValue "getElementsByTagName" m.Name) || 
        (DomTypeToTsType m.Type) <> "NodeList" || 
        m.Params.Length <> 1 ||
        (DomTypeToTsType m.Params.[0].Type) <> "string" then
        raise (Exception "getElementsByTagName method signature does not match expected.")
    else
        for e in tagNameToEleName do
            Pt.printl "getElementsByTagName(%s: \"%s\"): NodeListOf<%s>;" m.Params.[0].Name (e.Key.ToLower()) e.Value
        Pt.printl "getElementsByTagName(%s: string): NodeListOf<Element>;" m.Params.[0].Name

/// Dump overloads for the createEvent method
let DumpCreateEventOverloads (m: Browser.Method) =
    if  not (OptionCheckValue "createEvent" m.Name) || 
        (DomTypeToTsType m.Type) <> "Event" || 
        m.Params.Length <> 1 ||
        (DomTypeToTsType m.Params.[0].Type) <> "string" then
        raise (Exception "createEvent method signature does not match expected.")
    else
        // Dump plurals. For example, "Events", "MutationEvents"
        let hasPlurals = ["Event"; "MutationEvent"; "MouseEvent"; "SVGZoomEvent"; "UIEvent"]
        for x in distinctETypeList do
            Pt.printl "createEvent(eventInterface:\"%s\"): %s;" x x 
            if List.contains x hasPlurals then
                Pt.printl "createEvent(eventInterface:\"%ss\"): %s;" x x
        Pt.printl "createEvent(eventInterface: string): Event;"

/// Generate the parameters string for function signatures 
let ParamsToString (ps: Param list) =
    let paramToString (p: Param) =
        (if p.Variadic then "..." else "") + 
        (AdjustParamName p.Name) + 
        (if not p.Variadic && p.Optional then "?: " else ": ") +
        (DomTypeToTsType p.Type) +
        (if p.Variadic then "[]" else "")
    String.Join(", ", (List.map paramToString ps))

let DumpMethod flavor prefix (i:Browser.Interface) (m:Browser.Method)  = 
    // print comment
    if m.Name.IsSome then
        match GetCommentForMethod i.Name m.Name.Value with
        | Some comment -> Pt.printl "%s" comment
        | _ -> ()

    // Find if there are overriding signatures in the external json file
{'fraction_non_alphanumeric': 0.1296906892761815, 'fraction_numerical': 0.0037682524729156855, 'mean_word_length': 2.4958836443468715, 'pattern_counts': {'":': 2, '<': 31, '<?xml version=': 0, '>': 87, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2150809', 'n_tokens_mistral': 3890, 'n_tokens_neox': 3681, 'n_words': 1012}